Files
2026-08-24 10:03:24 +08:00

415 lines
13 KiB
C++

/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* @author Tellen Yu
* @version 1.0
* @date 2016/09/06
* @par function description:
* - 1 process HDCP TX authenticate
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <chrono>
#include <cutils/properties.h>
#include "HDCPTxAuth.h"
#include "mode_util.h"
#include <DebugHelper.h>
#define DISPLAY_HDMI_HDCP_KEY "/sys/class/amhdmitx/amhdmitx0/hdcp_lstore"
#define DISPLAY_HDMI_HDCP_CONF "/sys/class/amhdmitx/amhdmitx0/hdcp_ctrl"
#define DISPLAY_HDMI_HDCP_POWER "/sys/class/amhdmitx/amhdmitx0/hdcp_pwr"
#define DISPLAY_HDMI_VIDEO_MUTE "/sys/class/amhdmitx/amhdmitx0/vid_mute"
#define DISPLAY_HDMI_AVMUTE_SYSFS "/sys/devices/virtual/amhdmitx/amhdmitx0/avmute"
#define DISPLAY_HDMI_HDCP_AUTH "/sys/module/aml_media/parameters/hdmi_authenticated"
#define DISPLAY_HDMI_HDCP_VER "/sys/class/amhdmitx/amhdmitx0/hdcp_ver"
#define HDMI_TX_AUTH_FAIL "0"
#define HDMI_TX_AUTH_SUCCESS "1"
#define DISPLAY_HDMI_HDCP14_STOP "stop14" //stop HDCP1.4 authenticate
#define DISPLAY_HDMI_HDCP22_STOP "stop22" //stop HDCP2.2 authenticate
#define DISPLAY_HDMI_HDCP_14 "1"
#define DISPLAY_HDMI_HDCP_22 "2"
HDCPTxAuth::HDCPTxAuth() :
mRepeaterRxVer(REPEATER_RX_VERSION_NONE),
mMute(false),
mBootAnimFinished(false),
pthreadIdHdcpTx(0),
mFallbackDefault(false) {
mExitHdcpTxThread = false;
int ret;/* initialize an attribute to default value */
ret = pthread_mutex_init(&pthreadTxMutex, NULL);
if (ret != 0) {
SYS_LOGE("pthreadTxMutex init failed\n");
}
if (sem_init(&pthreadTxSem, 0, 0) < 0) {
SYS_LOGE("HDCPTxAuth, sem_init failed\n");
exit(0);
}
}
HDCPTxAuth::~HDCPTxAuth() {
sem_destroy(&pthreadTxSem);
pthread_mutex_destroy(&pthreadTxMutex);
}
void HDCPTxAuth::setBootAnimFinished(bool finished) {
mBootAnimFinished = finished;
}
void HDCPTxAuth::setRepeaterRxVersion(int ver) {
mRepeaterRxVer = ver;
}
void HDCPTxAuth::setHDCPCallback(HDCPTxAuthCallback *cb) {
pmHDCPTxAuthCallback = cb;
}
//start HDCP TX authenticate
int HDCPTxAuth::start() {
int ret;
pthread_t thread_id;
if (pthread_mutex_trylock(&pthreadTxMutex) != 0) {
SYS_LOGE("hdcp_tx create thread, Mutex is deadlock\n");
return -1;
}
mExitHdcpTxThread = false;
ret = pthread_create(&thread_id, NULL, authThread, this);
if (ret != 0) SYS_LOGE("hdcp_tx, thread create failed\n");
ret = sem_wait(&pthreadTxSem);
if (ret < 0) SYS_LOGE("hdcp_tx, sem_wait failed\n");
pthreadIdHdcpTx = thread_id;
pthread_mutex_unlock(&pthreadTxMutex);
SYS_LOGI("hdcp_tx, create hdcp thread id = %lu done\n", thread_id);
return 0;
}
//stop HDCP TX authenticate
int HDCPTxAuth::stop() {
void *threadResult;
int ret = -1;
stopVerAll();
#ifndef RECOVERY_MODE
mFallbackDefault = false;
#endif
if (0 != pthreadIdHdcpTx) {
mExitHdcpTxThread = true;
mCv.notify_all();
if (pthread_mutex_trylock(&pthreadTxMutex) != 0) {
SYS_LOGE("hdcp_tx exit thread, Mutex is deadlock\n");
return ret;
}
if (0 != pthread_join(pthreadIdHdcpTx, &threadResult)) {
pthread_mutex_unlock(&pthreadTxMutex);
SYS_LOGE("hdcp_tx exit thread failed\n");
return ret;
}
pthread_mutex_unlock(&pthreadTxMutex);
SYS_LOGI("hdcp_tx pthread exit id = %lu, %s done\n", pthreadIdHdcpTx, (char *)threadResult);
pthreadIdHdcpTx = 0;
ret = 0;
}
return ret;
}
//Define to force authentication regardless of keys presence
//#define HDCP_AUTHENTICATION_NO_KEYS
void HDCPTxAuth::mute(bool mute __unused) {
#if !defined(HDCP_AUTHENTICATION_NO_KEYS)
char hdcpTxKey[MESON_MODE_LEN] = {0};
meson_mode_read_sys(DISPLAY_HDMI_HDCP_KEY, hdcpTxKey, false, sizeof(hdcpTxKey));
if ((strlen(hdcpTxKey) == 0) || !(strcmp(hdcpTxKey, "00")))
return;
#endif
#ifdef HDCP_AUTHENTICATION
if (mute != mMute) {
//meson_mode_write_sys(DISPLAY_HDMI_AUDIO_MUTE, mute ? "1" : "0");
meson_mode_write_sys(DISPLAY_HDMI_VIDEO_MUTE, mute ? "1" : "0");
mMute = mute;
SYS_LOGI("hdcp_tx mute %s\n", mute ? "on" : "off");
}
#endif
}
void* HDCPTxAuth::authThread(void* data) {
set_thread_debug_info("HdcpTxThread");
bool hdcp22 = false;
bool hdcp14 = false;
HDCPTxAuth *pThiz = (HDCPTxAuth*)data;
SYS_LOGI("hdcp_tx thread loop entry\n");
sem_post(&pThiz->pthreadTxSem);
//while (!pThiz->mBootAnimFinished) {
// usleep(50*1000);//sleep 50ms
//}
int count = 0;
while (!pThiz->mExitHdcpTxThread) {
char hdcpTxKey[MESON_MODE_LEN] = {0};
//actually, every product need provision HDCP key, if can not read it, we will always pend on
meson_mode_read_sys(DISPLAY_HDMI_HDCP_KEY, hdcpTxKey, false, sizeof(hdcpTxKey));
//we already read the HDCP key now, if we support 2.2, we must support 1.4
if (((strstr(hdcpTxKey, (char *)"22") != NULL) && (strstr(hdcpTxKey, (char *)"14") != NULL)) ||
// only have HDCP 1.4 key
(!strcmp(hdcpTxKey, "14"))
//wait 10s tee_hdcp update key
|| (count > 10))
break;
count++;
std::unique_lock<std::mutex> autolock(pThiz->mMutex);
pThiz->mCv.wait_for(autolock, std::chrono::milliseconds(1000));
}
if (pThiz->mExitHdcpTxThread)
return NULL;
if (pThiz->authInit(&hdcp22, &hdcp14)) {
if (!pThiz->authLoop(hdcp22, hdcp14)) {
SYS_LOGE("HDCP authenticate fail\n");
pThiz->AuthResult(false);
}
}
return NULL;
}
bool HDCPTxAuth::authInit(bool *pHdcp22, bool *pHdcp14) {
bool useHdcp22 = false;
bool useHdcp14 = false;
#ifdef HDCP_AUTHENTICATION
char hdcpRxVer[MESON_MODE_LEN] = {0};
char hdcpTxKey[MESON_MODE_LEN] = {0};
//in general, MBOX is TX device, need to detect its TX keys.
// TV is RX device, need to detect its RX keys.
//HDCP TX: get current MBOX[TX] device contains which TX keys. Values:[14/22, 00 is no key]
meson_mode_read_sys(DISPLAY_HDMI_HDCP_KEY, hdcpTxKey, false, sizeof(hdcpTxKey));
SYS_LOGI("hdcp_tx key:%s\n", hdcpTxKey);
if ((strlen(hdcpTxKey) == 0) || !(strcmp(hdcpTxKey, "00"))) {
AuthResult(false);
return false;
}
//HDCP RX: get current TV[RX] device contains which RX key. Values:[14/22, 00 is no key]
//Values is the highest key. if value is 22, means the devices supports 22 and 14.
meson_mode_read_sys(DISPLAY_HDMI_HDCP_VER, hdcpRxVer, false, sizeof(hdcpRxVer));
SYS_LOGI("hdcp_tx remote version:%s\n", hdcpRxVer);
if ((strlen(hdcpRxVer) == 0) || !(strcmp(hdcpRxVer, "00"))) {
AuthResult(false);
return false;
}
//stop hdcp_tx
stopVerAll();
if (REPEATER_RX_VERSION_22 == mRepeaterRxVer) {
SYS_LOGI("hdcp_tx 2.2 supported for RxSupportHdcp2.2Auth\n");
useHdcp22 = true;
} else if (/*(strstr(cap, (char *)"2160p") != NULL) && */(strstr(hdcpRxVer, (char *)"22") != NULL) &&
(strstr(hdcpTxKey, (char *)"22") != NULL)) {
if ((!access(HDCP_TX_FW_PATH_1, 0)) || (!access(HDCP_TX_FW_PATH_2, 0))) {
SYS_LOGI("hdcp_tx 2.2 supported\n");
useHdcp22 = true;
} else {
SYS_LOGE("!!!hdcp_tx 2.2 key is burned in This device, But %s and %s is not exist\n", HDCP_TX_FW_PATH_1, HDCP_TX_FW_PATH_2);
}
}
if (REPEATER_RX_VERSION_14 == mRepeaterRxVer) {
SYS_LOGI("hdcp_tx 1.4 supported for RxSupportHdcp1.4Auth\n");
useHdcp14 = true;
} else if (!useHdcp22 && (strstr(hdcpRxVer, (char *)"14") != NULL) &&
(strstr(hdcpTxKey, (char *)"14") != NULL)) {
useHdcp14 = true;
SYS_LOGI("hdcp_tx 1.4 supported\n");
}
if (!useHdcp22 && !useHdcp14) {
//do not support hdcp1.4 and hdcp2.2
SYS_LOGE("device do not support hdcp1.4 or hdcp2.2\n");
AuthResult(false);
return false;
}
//start hdcp_tx
if (useHdcp22) {
startVer22();
}
else if (useHdcp14) {
startVer14();
}
#endif
*pHdcp22 = useHdcp22;
*pHdcp14 = useHdcp14;
return true;
}
bool HDCPTxAuth::authLoop(bool useHdcp22, bool useHdcp14) {
SYS_LOGI("hdcp_tx begin to authenticate hdcp22:%d, hdcp14:%d\n", useHdcp22, useHdcp14);
bool success = false;
#ifdef HDCP_AUTHENTICATION
int count = 0;
while (!mExitHdcpTxThread) {
std::unique_lock<std::mutex> autolock(mMutex);
mCv.wait_for(autolock, std::chrono::milliseconds(200));
char auth[MESON_MODE_LEN] = {0};
meson_mode_read_sys(DISPLAY_HDMI_HDCP_AUTH, auth, false, sizeof(auth));
if (strstr(auth, (char *)"1")) {//Authenticate is OK
success = true;
AuthResult(true);
break;
}
count++;
if (count > 40) { //max 200msx40 = 8s it will authenticate completely
if (useHdcp22) {
SYS_LOGE("hdcp_tx 2.2 authenticate fail for 8s timeout, change to hdcp_tx 1.4 authenticate\n");
count = 0;
useHdcp22 = false;
useHdcp14 = true;
stopVerAll();
//if support hdcp22, must support hdcp14
startVer14();
continue;
}
else if (useHdcp14) {
SYS_LOGE("hdcp_tx 1.4 authenticate fail, 8s timeout\n");
startVer14();
}
break;
}
}
#endif
SYS_LOGI("hdcp_tx authenticate success: %d\n", success?1:0);
return success;
}
void HDCPTxAuth::startVer22() {
//start hdcp_tx 2.2
SYS_LOGI("start hdcp_tx 2.2\n");
meson_mode_write_sys(DISPLAY_HDMI_HDCP_MODE, DISPLAY_HDMI_HDCP_22);
usleep(50*1000);
property_set("ctl.start", "hdcp_tx22");
}
void HDCPTxAuth::startVer14() {
//start hdcp_tx 1.4
//SYS_LOGI("hdcp_tx 1.4 start\n");
meson_mode_write_sys(DISPLAY_HDMI_HDCP_MODE, DISPLAY_HDMI_HDCP_14);
}
void HDCPTxAuth::stopVerAll() {
char hdcpRxVer[MESON_MODE_LEN] = {0};
//stop hdcp_tx 2.2 & 1.4
SYS_LOGI("hdcp_tx 2.2 & 1.4 stop hdcp pwr\n");
meson_mode_write_sys(DISPLAY_HDMI_HDCP_POWER, "1");
usleep(20000);
property_set("ctl.stop", "hdcp_tx22");
usleep(20000);
meson_mode_read_sys(DISPLAY_HDMI_HDCP_POWER, hdcpRxVer, false, sizeof(hdcpRxVer));
meson_mode_write_sys(DISPLAY_HDMI_HDCP_CONF, DISPLAY_HDMI_HDCP14_STOP);
meson_mode_write_sys(DISPLAY_HDMI_HDCP_CONF, DISPLAY_HDMI_HDCP22_STOP);
usleep(2000);
}
void HDCPTxAuth::AuthResult(bool result) {
char fail_case[PROPERTY_VALUE_MAX] = {0};
property_get(HDCP_TX_AUTH_FAIL, fail_case, "4");
if (result) {
meson_mode_write_sys(DISPLAY_HDMI_AVMUTE_SYSFS, "-1");
if (!strcmp(fail_case, "1") || !strcmp(fail_case, "2")) {
meson_mode_write_sys(DISPLAY_HDMI_VIDEO_MUTE, "0");
meson_mode_write_sys(DISPLAY_HDMI_AUDIO_MUTE, "0");
#ifndef LINUX_COMPILE
meson_mode_write_sys(DISPLAY_MEDIA_VIDEO_MUTE, "0");
#else
meson_mode_ioctl(IOCTL_AMVIDEO, AMSTREAM_IOC_SET_VIDEO_MUTE, false);
#endif
} else if (!strcmp(fail_case, "3") && mFallbackDefault) {
#ifndef RECOVERY_MODE
SYS_LOGD("send hdcp auth success event.\n");
if (pmHDCPTxAuthCallback)
pmHDCPTxAuthCallback->onHdcpTxAuthEvent(HDMI_TX_AUTH_SUCCESS);
mFallbackDefault = false;
#endif
}
} else {
if (!strcmp(fail_case, "1")) {
meson_mode_write_sys(DISPLAY_HDMI_VIDEO_MUTE, "1");
meson_mode_write_sys(DISPLAY_HDMI_AUDIO_MUTE, "1");
} else if (!strcmp(fail_case, "2")) {
meson_mode_write_sys(DISPLAY_HDMI_AUDIO_MUTE, "1");
#ifndef LINUX_COMPILE
meson_mode_write_sys(DISPLAY_MEDIA_VIDEO_MUTE, "1");
#else
meson_mode_ioctl(IOCTL_AMVIDEO, AMSTREAM_IOC_SET_VIDEO_MUTE, true);
#endif
} else if (!strcmp(fail_case, "3") && !mFallbackDefault) {
#ifndef RECOVERY_MODE
SYS_LOGD("send hdcp auth fail event.\n");
if (pmHDCPTxAuthCallback)
pmHDCPTxAuthCallback->onHdcpTxAuthEvent(HDMI_TX_AUTH_FAIL);
mFallbackDefault = true;
#endif
}
}
}
void HDCPTxAuth::isAuthSuccess(int *status) {
int ret = pthread_mutex_trylock(&pthreadTxMutex);
if (ret != 0) {
SYS_LOGE("try lock pthreadTxMutex return status: %d\n",ret);
*status = -1;
}
char auth[MESON_MODE_LEN] = {0};
meson_mode_read_sys(DISPLAY_HDMI_HDCP_AUTH, auth, false, sizeof(auth));
if (strstr(auth, (char *)"1")) {//Authenticate is OK
*status = 1;
}
pthread_mutex_unlock(&pthreadTxMutex);
SYS_LOGI("HDCPTx Auth status is: %d",*status);
}