Files
SDK_RK3288/system/displayd/Cecmanager.cpp
2016-05-23 20:37:01 +08:00

664 lines
16 KiB
C++
Executable File

#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <cutils/properties.h>
#include <cutils/log.h>
#include <linux/ioctl.h>
#include <linux/uinput.h>
#include <sys/prctl.h>
#include <poll.h>
#include "Cecmanager.h"
//#define LOG_TAG "Cecmanager"
HdmicecManage* HdmicecManage::m_pInstance = NULL;
#define HDMI_CEC_MAGIC 'N'
#define HDMI_IOCTL_CECSEND _IOW(HDMI_CEC_MAGIC, 0 ,struct cec_framedata)
#define HDMI_IOCTL_CECENAB _IOW(HDMI_CEC_MAGIC, 1, int)
#define HDMI_IOCTL_CECPHY _IOR(HDMI_CEC_MAGIC, 2, int)
#define HDMI_IOCTL_CECLOGIC _IOR(HDMI_CEC_MAGIC, 3, int)
#define HDMI_IOCTL_CECREAD _IOR(HDMI_CEC_MAGIC, 4, struct cec_framedata)
#define HDMI_IOCTL_CECSETLA _IOW(HDMI_CEC_MAGIC, 5, int)
#define HDMI_IOCTL_CECCLEARLA _IOW(HDMI_CEC_MAGIC, 6, int)
#define HDMI_IOCTL_CECWAKESTATE _IOR(HDMI_CEC_MAGIC, 7, int)
HdmicecManage::HdmicecManage() {
ALOGI("%s", __func__);
property_set("sys.hdmicec.version", HDMI_CEC_HAL_VERSION);
dev_fd = open(HDMI_DEV_PATH, O_RDWR);
if (dev_fd < 0) {
ALOGE("%s:not able to open dev cec", __func__);
return;
}
stat_fd = open(HDMI_STATE_PATH, O_RDONLY);
if (stat_fd < 0) {
ALOGE("%s:not able to open cec state", __func__);
return;
}
input_fd = open(INPUT_DEV_PATH, O_RDWR);
if (input_fd < 0) {
ALOGE("%s:not able to open input event0", __func__);
return;
}
mflag = true;
}
int HdmicecManage::isconnected() {
int hdmi_state;
int ret, fd;
char statebuf[2];
ALOGI("%s", __func__);
memset(statebuf, 0, sizeof(statebuf));
fd = open(HDMI_CONNECT_PATH, O_RDONLY);
if (fd < 0){
ALOGE("%s open error", __func__);
return -1;
}
ret = read(fd, statebuf, sizeof(statebuf));
close(fd);
if (ret < 0) {
ALOGE("%s read error", __func__);
return -1;
}
hdmi_state = atoi(statebuf);
ALOGI("hdmi_state = %d", hdmi_state);
if (hdmi_state == 1)
return HDMI_CONNECTED;
else
return HDMI_NOT_CONNECTED;
return HDMI_NOT_CONNECTED;
}
void HdmicecManage::clearlogicaddress() {
int ret;
ALOGI("%s", __func__);
ret = ioctl(dev_fd, HDMI_IOCTL_CECCLEARLA, NULL);
if (ret)
ALOGE("%s clear logic addresss error", __func__);
}
int HdmicecManage::sendmessage(cec_framedata cecframe) {
int ret, i, cecwakestate;
struct cec_framedata cecframe_temp;
ALOGI("%s", __func__);
ret = 0;
i = 30;
cecframe_temp = cecframe;
while(i--) {
ret = ioctl(dev_fd, HDMI_IOCTL_CECWAKESTATE, &cecwakestate);
if (cecwakestate != 0) {
ALOGI("cecwakestate = %d , i = %d", cecwakestate, i);
usleep(40*1000);
} else {
break;
}
if (i == 0) {
ALOGE("%s i = %d HDMI_RESULT_FAIL", __func__, i);
return -1;
}
}
if (cecframe.argcount > 15)
cecframe.argcount = 0;
ret = ioctl(dev_fd, HDMI_IOCTL_CECSEND, &cecframe);
if (ret)
return -1;
if (cecframe.returnval == CEC_SEND_SUCCESS) {
return CEC_SEND_SUCCESS;
} else if ((cecframe.returnval == CEC_SEND_NACK) ||
(cecframe.returnval == CEC_SEND_NACK)) {
ret = ioctl(dev_fd, HDMI_IOCTL_CECSEND, &cecframe_temp);
if (ret)
return -1;
}
return cecframe_temp.returnval;
}
int HdmicecManage::pinglogicaddress(char logic_addr) {
struct cec_framedata cecframe;
int ret;
ALOGI("%s", __func__);
memset(&cecframe, 0, sizeof(struct cec_framedata));
cecframe.srcdestaddr = logic_addr << 4 | logic_addr;
cecframe.argcount = 0;
ret = sendmessage(cecframe);
return ret;
}
int HdmicecManage::addlogicaddress(int logic_addr) {
int ret;
ALOGI("%s", __func__);
ret = ioctl(dev_fd, HDMI_IOCTL_CECSETLA, &logic_addr);
if (ret) {
ALOGE("%s add logic address error", __func__);
return -1;
}
return 0;
}
int HdmicecManage::getphysicaladdress() {
int ret;
int val;
ALOGI("%s", __func__);
val = 0;
ret = ioctl(dev_fd, HDMI_IOCTL_CECPHY, &val);
address_phy = val;
ALOGI("%s address_phy = 0x%x", __func__, val);
if (ret < 0 )
return -1;
else
return val;
}
int HdmicecManage::sendimageview() {
struct cec_framedata cecframe;
int ret;
ALOGI("%s", __func__);
memset(&cecframe, 0, sizeof(struct cec_framedata));
cecframe.opcode = CECOP_IMAGE_VIEW_ON;
cecframe.srcdestaddr = MAKE_SRCDEST(address_logic,
CEC_LOGADDR_TV);
cecframe.argcount = 0;
ret = sendmessage(cecframe);
return ret;
}
int HdmicecManage::sendactivesource() {
struct cec_framedata cecframe;
int ret;
ALOGI("%s", __func__);
memset(&cecframe, 0, sizeof(struct cec_framedata));
cecframe.opcode = CECOP_ACTIVE_SOURCE;
cecframe.srcdestaddr = MAKE_SRCDEST(address_logic,
CEC_LOGADDR_UNREGORBC);
cecframe.args[0] = (address_phy & 0xFF00) >> 8;
cecframe.args[1] = (address_phy & 0x00FF);
cecframe.argcount = 2;
ret = sendmessage(cecframe);
return ret;
}
void HdmicecManage::inputkey(int keycode) {
struct input_event event;
int ret;
memset(&event, 0, sizeof(event));
event.type = EV_KEY;
event.code = keycode;
event.value = 1;
ret = write(input_fd, &event, sizeof(event));
if (ret < 0) {
ALOGE("write /dev/input/event0 press error");
return;
}
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
ret = write(input_fd, &event, sizeof(event));
if (ret < 0) {
ALOGE("write /dev/input/event0 sysc error");
return;
}
memset(&event, 0, sizeof(event));
event.type = EV_KEY;
event.code = keycode;
event.value = 0;
ret = write(input_fd, &event, sizeof(event));
if (ret < 0) {
ALOGE("write /dev/input/event0 release error");
return;
}
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
ret = write(input_fd, &event, sizeof(event));
if (ret < 0) {
ALOGE("write /dev/input/event0 sync error");
return;
}
}
void HdmicecManage::uicontrol(int opcode) {
if (input_fd > 0){
switch (opcode) {
case scecmakesure:
inputkey(KEY_REPLY);
break;
case scecup:
inputkey(KEY_UP);
break;
case scecdown:
inputkey(KEY_DOWN);
break;
case scecleft:
inputkey(KEY_LEFT);
break;
case scecright:
inputkey(KEY_RIGHT);
break;
case scecback:
inputkey(KEY_BACK);
break;
case scecvendorback:
inputkey(KEY_BACK);
break;
}
}
}
void HdmicecManage::cecenumeration() {
char logicaddress[3] =
{CEC_LOGADDR_PLAYBACK1,
CEC_LOGADDR_PLAYBACK2,
CEC_LOGADDR_PLAYBACK3};
int i;
int trynum;
int rtvalue;
int availablecnt;
ALOGI("%s", __func__);
if ((dev_fd < 0) || (input_fd < 0) || (stat_fd < 0)) {
ALOGI("dev_fd = %d, input_fd = %d,stat_fd = %d",
dev_fd, input_fd, stat_fd);
return;
}
if (getphysicaladdress() > 0) {
for (i = 0; i < 3; i++) {
trynum = 0;
rtvalue = 0;
availablecnt = 0;
for(trynum = 0; trynum < 3; trynum ++) {
rtvalue = pinglogicaddress(logicaddress[i]);
if(rtvalue == 1) {
availablecnt ++;
ALOGI("availableCnt: %d\n", availablecnt);
}
usleep(5);
}
if (availablecnt > 1) {
address_logic = logicaddress[i];
ALOGI("Logic Address is 0x%x\n",
address_logic);
break;
}
}
if (i == 3)
address_logic = CEC_LOGADDR_UNREGORBC;
ALOGI("address_logic = %d", address_logic);
addlogicaddress(address_logic);
sendimageview();
sendactivesource();
}
}
bool HdmicecManage::validatececmessage(struct cec_framedata *pcpi)
{
char parametercount = 0;
bool countok = true;
/* Determine required parameter count */
ALOGI("%s", __func__);
switch (pcpi->opcode) {
case CECOP_IMAGE_VIEW_ON:
case CECOP_TEXT_VIEW_ON:
case CECOP_STANDBY:
case CECOP_GIVE_PHYSICAL_ADDRESS:
case CECOP_GIVE_DEVICE_POWER_STATUS:
case CECOP_GET_MENU_LANGUAGE:
case CECOP_GET_CEC_VERSION:
parametercount = 0;
break;
case CECOP_REPORT_POWER_STATUS: /* power status*/
case CECOP_CEC_VERSION: /* cec version*/
parametercount = 1;
break;
case CECOP_INACTIVE_SOURCE: /* physical address*/
case CECOP_FEATURE_ABORT:
case CECOP_ACTIVE_SOURCE: /* physical address*/
parametercount = 2;
break;
case CECOP_REPORT_PHYSICAL_ADDRESS:
case CECOP_DEVICE_VENDOR_ID: /* vendor id*/
parametercount = 3;
break;
case CECOP_SET_OSD_NAME: /* osd name (1-14 bytes)*/
case CECOP_SET_OSD_STRING:
parametercount = 1; /* must have a minimum of 1 operands*/
break;
case CECOP_ABORT:
break;
case CECOP_ARC_INITIATE:
break;
case CECOP_ARC_REPORT_INITIATED:
break;
case CECOP_ARC_REPORT_TERMINATED:
break;
case CECOP_ARC_REQUEST_INITIATION:
break;
case CECOP_ARC_REQUEST_TERMINATION:
break;
case CECOP_ARC_TERMINATE:
break;
default:
break;
}
/* Test for correct parameter count. */
if (pcpi->argcount < parametercount)
countok = false;
return countok;
}
int HdmicecManage::cecrxmsghandlerlast(struct cec_framedata *pcpi)
{
bool isdirectaddressed;
struct cec_framedata cecframe;
ALOGI("%s", __func__);
memset(&cecframe, 0, sizeof(struct cec_framedata));
isdirectaddressed = !((pcpi->srcdestaddr & 0x0F) ==
CEC_LOGADDR_UNREGORBC);
ALOGI("isdirectaddressed %d\n", (int)isdirectaddressed);
if (validatececmessage(pcpi)) {
/* If invalid message, ignore it, but treat it as handled */
if (isdirectaddressed) {
switch (pcpi->opcode) {
case CECOP_USER_CONTROL_PRESSED:
uicontrol(pcpi->args[0]);
break;
case CECOP_VENDOR_REMOTE_BUTTON_DOWN:
uicontrol(pcpi->args[0]);
break;
case CECOP_FEATURE_ABORT:
//CecHandleFeatureAbort(pcpi);
break;
case CECOP_GIVE_OSD_NAME:
cecframe.opcode = CECOP_SET_OSD_NAME;
cecframe.srcdestaddr =
MAKE_SRCDEST(address_logic,
CEC_LOGADDR_TV);
cecframe.args[0] = 'R';
cecframe.args[1] = 'K';
cecframe.args[2] = '-';
cecframe.args[3] = 'B';
cecframe.args[4] = 'O';
cecframe.args[5] = 'X';
cecframe.argcount = 6;
sendmessage(cecframe);
break;
case CECOP_VENDOR_COMMAND_WITH_ID:
if (pcpi->args[2] == 00) {
cecframe.opcode = CECOP_SET_OSD_NAME;
cecframe.srcdestaddr =
MAKE_SRCDEST(address_logic,
CEC_LOGADDR_TV);
cecframe.args[0] = '1';
cecframe.args[1] = '1';
cecframe.args[2] = '1';
cecframe.args[3] = '1';
cecframe.args[4] = '1';
cecframe.args[5] = '1';
cecframe.argcount = 6;
sendmessage(cecframe);
}
break;
case CECOP_IMAGE_VIEW_ON:
case CECOP_TEXT_VIEW_ON:
/* In our case, respond the same to both these messages*/
break;
case CECOP_GIVE_DEVICE_VENDOR_ID:
cecframe.opcode = CECOP_DEVICE_VENDOR_ID;
cecframe.srcdestaddr =
MAKE_SRCDEST(address_logic,
CEC_LOGADDR_UNREGORBC);
cecframe.args[0] = 0x1;
cecframe.args[1] = 0x2;
cecframe.args[2] = 0x3;
cecframe.argcount = 3;
sendmessage(cecframe);
break;
case CECOP_STANDBY: /* Direct and Broadcast*/
/* Setting this here will let the main task know */
/* (via SI_CecGetPowerState) and at the same time */
/* prevent us from broadcasting a STANDBY message */
/* of our own when the main task responds by */
/* calling SI_CecSetPowerState( STANDBY ); */
//cec_dev->powerstatus = CEC_POWERSTATUS_STANDBY;
ALOGI("CECOP_STANDBY");
inputkey(KEY_POWER);
break;
case CECOP_INACTIVE_SOURCE:
//CecHandleInactiveSource(pcpi);
break;
case CECOP_GIVE_PHYSICAL_ADDRESS:
cecframe.opcode = CECOP_REPORT_PHYSICAL_ADDRESS;
cecframe.srcdestaddr =
MAKE_SRCDEST(address_logic,
CEC_LOGADDR_UNREGORBC);
cecframe.args[0] = (address_phy & 0xFF00) >> 8;
cecframe.args[1] = (address_phy & 0x00FF);
cecframe.args[2] = address_logic;
cecframe.argcount = 3;
sendmessage(cecframe);
break;
case CECOP_GIVE_DEVICE_POWER_STATUS:
/* TV responds with power status. */
cecframe.opcode = CECOP_REPORT_POWER_STATUS;
cecframe.srcdestaddr =
MAKE_SRCDEST(address_logic,
(pcpi->srcdestaddr & 0xF0) >> 4);
//powerstatus = 0x00;
cecframe.args[0] = 0x00;
cecframe.argcount = 1;
sendmessage(cecframe);
break;
case CECOP_GET_MENU_LANGUAGE:
/* TV Responds with a Set Menu language command. */
cecframe.opcode = CECOP_SET_MENU_LANGUAGE;
cecframe.srcdestaddr =
MAKE_SRCDEST(address_logic,
CEC_LOGADDR_UNREGORBC);
cecframe.args[0] = 'e';
cecframe.args[1] = 'n';
cecframe.args[2] = 'g';
cecframe.argcount = 3;
sendmessage(cecframe);
break;
case CECOP_GET_CEC_VERSION:
/* TV responds to this request with it's CEC version support.*/
cecframe.srcdestaddr =
MAKE_SRCDEST(address_logic,
CEC_LOGADDR_TV);
cecframe.opcode = CECOP_CEC_VERSION;
cecframe.args[0] = 0x05; /* Report CEC1.4b*/
cecframe.argcount = 1;
sendmessage(cecframe);
break;
case CECOP_REPORT_POWER_STATUS:
/*Someone sent us their power state.
l_sourcePowerStatus = pCpi->args[0];
let NEW SOURCE task know about it.
if ( l_cecTaskState.task == SI_CECTASK_NEWSOURCE )
{
l_cecTaskState.cpiState = CPI_RESPONSE;
}*/
break;
/* Do not reply to directly addressed 'Broadcast' msgs. */
case CECOP_REQUEST_ACTIVE_SOURCE:
sendactivesource();
break;
case CECOP_ACTIVE_SOURCE:
case CECOP_REPORT_PHYSICAL_ADDRESS:
case CECOP_ROUTING_CHANGE:
case CECOP_ROUTING_INFORMATION:
case CECOP_SET_STREAM_PATH:
case CECOP_SET_MENU_LANGUAGE:
case CECOP_DEVICE_VENDOR_ID:
break;
case CECOP_ABORT:
break;
default:
/*CecSendFeatureAbort(pCpi, CECAR_UNRECOG_OPCODE);*/
break;
}
} else {
/* Respond to broadcast messages. */
switch (pcpi->opcode) {
case CECOP_STANDBY:
/* Setting this here will let the main task know */
/* (via SI_CecGetPowerState) and at the same time */
/* prevent us from broadcasting a STANDBY message */
/* of our own when the main task responds by */
/* calling SI_CecSetPowerState( STANDBY ); */
ALOGI("CECOP_STANDBY");
inputkey(KEY_POWER);
break;
case CECOP_ACTIVE_SOURCE:
/*CecHandleActiveSource( pCpi );*/
break;
/* Do not reply to 'Broadcast' msgs that we don't need.*/
case CECOP_REQUEST_ACTIVE_SOURCE:
sendactivesource();
break;
case CECOP_ROUTING_CHANGE:
case CECOP_ROUTING_INFORMATION:
case CECOP_SET_STREAM_PATH:
case CECOP_SET_MENU_LANGUAGE:
break;
}
}
}
return 0;
}
void* HdmicecManage::uevent_loop(void *param)
{
const int MAX_DATA = 64;
static char vdata[MAX_DATA];
char thread_name[64] = HDMI_CEC_UEVENT_THREAD_NAME;
struct pollfd pfd[2];
struct cec_framedata cecframe;
ALOGI("%s", __func__);
HdmicecManage *mHdmicecManage = HdmicecManage::GetInstance();
if (mHdmicecManage->dev_fd < 0) {
ALOGE("fd is NULL!");
delete mHdmicecManage;
return NULL;
}
prctl(PR_SET_NAME, (unsigned long) &thread_name, 0, 0, 0);
//setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
// Read once from the fds to clear the first notify
pread(mHdmicecManage->stat_fd, vdata , MAX_DATA, 0);
pfd[0].fd = mHdmicecManage->stat_fd;
if (pfd[0].fd >= 0)
pfd[0].events = POLLPRI | POLLERR;
do {
int err = poll(pfd, 1, -1);
if(err > 0) {
if (pfd[0].revents & POLLPRI) {
int len = pread(pfd[0].fd, vdata, MAX_DATA, 0);
if (len < 0) {
// If the read was just interrupted - it is not a
// fatal error. Just continue in this case
ALOGE ("%s: Unable to read cec state %s",
__FUNCTION__, strerror(errno));
continue;
}
const char *str = vdata;
int state = strtoull(str, NULL, 0);
ALOGD("cec state is %d", state);
memset(&cecframe, 0, sizeof(cecframe));
if (state == 0 || state == 1) {
ALOGD("%s sending hotplug: connected = %d ",
__FUNCTION__, state);
} else if (state == 2) {
int ret = ioctl(mHdmicecManage->dev_fd, HDMI_IOCTL_CECREAD, &cecframe);
if (ret < 0) {
ALOGE("%s hdmi cec read error", __FUNCTION__);
continue;
}
mHdmicecManage->cecrxmsghandlerlast(&cecframe);
}
}
} else {
ALOGE("%s: cec poll failed errno: %s", __FUNCTION__,
strerror(errno));
continue;
}
} while (mHdmicecManage->mflag);
return NULL;
}
void HdmicecManage::init_uevent_thread()
{
int ret;
ALOGI("Initializing UEVENT Thread");
ret = pthread_create(&uevent_thread, NULL, uevent_loop, NULL);
if (ret) {
ALOGE("%s: failed to create %s: %s", __FUNCTION__,
HDMI_CEC_UEVENT_THREAD_NAME, strerror(ret));
}
}
HdmicecManage::~HdmicecManage() {
if (dev_fd > 0)
close(dev_fd);
if (input_fd > 0)
close(input_fd);
if (stat_fd > 0)
close(stat_fd);
mflag = false;
pthread_join(uevent_thread, NULL);
}