/* * Copyright@ Samsung Electronics Co. LTD * * 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. */ #include #include #include #include #include #include #include #include /* drv. header */ #include "cec.h" #include "libcec.h" #define CEC_DEBUG 0 /** * @def CEC_DEVICE_NAME * Defines simbolic name of the CEC device. */ #define CEC_DEVICE_NAME "/dev/CEC" static struct { enum CECDeviceType devtype; unsigned char laddr; } laddresses[] = { { CEC_DEVICE_RECODER, 1 }, { CEC_DEVICE_RECODER, 2 }, { CEC_DEVICE_TUNER, 3 }, { CEC_DEVICE_PLAYER, 4 }, { CEC_DEVICE_AUDIO, 5 }, { CEC_DEVICE_TUNER, 6 }, { CEC_DEVICE_TUNER, 7 }, { CEC_DEVICE_PLAYER, 8 }, { CEC_DEVICE_RECODER, 9 }, { CEC_DEVICE_TUNER, 10 }, { CEC_DEVICE_PLAYER, 11 }, }; static int CECSetLogicalAddr(unsigned int laddr); #ifdef CEC_DEBUG inline static void CECPrintFrame(unsigned char *buffer, unsigned int size); #endif static int fd = -1; /** * Open device driver and assign CEC file descriptor. * * @return If success to assign CEC file descriptor, return 1; otherwise, return 0. */ int CECOpen() { int res = 1; if (fd != -1) CECClose(); if ((fd = open(CEC_DEVICE_NAME, O_RDWR)) < 0) { LOGE("Can't open %s!\n", CEC_DEVICE_NAME); res = 0; } return res; } /** * Close CEC file descriptor. * * @return If success to close CEC file descriptor, return 1; otherwise, return 0. */ int CECClose() { int res = 1; if (fd != -1) { if (close(fd) != 0) { LOGE("close() failed!\n"); res = 0; } fd = -1; } return res; } /** * Allocate logical address. * * @param paddr [in] CEC device physical address. * @param devtype [in] CEC device type. * * @return new logical address, or 0 if an arror occured. */ int CECAllocLogicalAddress(int paddr, enum CECDeviceType devtype) { unsigned char laddr = CEC_LADDR_UNREGISTERED; int i = 0; if (fd == -1) { LOGE("open device first!\n"); return 0; } if (CECSetLogicalAddr(laddr) < 0) { LOGE("CECSetLogicalAddr() failed!\n"); return 0; } if (paddr == CEC_NOT_VALID_PHYSICAL_ADDRESS) return CEC_LADDR_UNREGISTERED; /* send "Polling Message" */ while (i < sizeof(laddresses)/sizeof(laddresses[0])) { if (laddresses[i].devtype == devtype) { unsigned char _laddr = laddresses[i].laddr; unsigned char message = ((_laddr << 4) | _laddr); if (CECSendMessage(&message, 1) != 1) { laddr = _laddr; break; } } i++; } if (laddr == CEC_LADDR_UNREGISTERED) { LOGE("All LA addresses in use!!!\n"); return CEC_LADDR_UNREGISTERED; } if (CECSetLogicalAddr(laddr) < 0) { LOGE("CECSetLogicalAddr() failed!\n"); return 0; } /* broadcast "Report Physical Address" */ unsigned char buffer[5]; buffer[0] = (laddr << 4) | CEC_MSG_BROADCAST; buffer[1] = CEC_OPCODE_REPORT_PHYSICAL_ADDRESS; buffer[2] = (paddr >> 8) & 0xFF; buffer[3] = paddr & 0xFF; buffer[4] = devtype; if (CECSendMessage(buffer, 5) != 5) { LOGE("CECSendMessage() failed!\n"); return 0; } return laddr; } /** * Send CEC message. * * @param *buffer [in] pointer to buffer address where message located. * @param size [in] message size. * * @return number of bytes written, or 0 if an arror occured. */ int CECSendMessage(unsigned char *buffer, int size) { if (fd == -1) { LOGE("open device first!\n"); return 0; } if (size > CEC_MAX_FRAME_SIZE) { LOGE("size should not exceed %d\n", CEC_MAX_FRAME_SIZE); return 0; } #if CEC_DEBUG LOGI("CECSendMessage() : "); CECPrintFrame(buffer, size); #endif return write(fd, buffer, size); } /** * Receive CEC message. * * @param *buffer [in] pointer to buffer address where message will be stored. * @param size [in] buffer size. * @param timeout [in] timeout in microseconds. * * @return number of bytes received, or 0 if an arror occured. */ int CECReceiveMessage(unsigned char *buffer, int size, long timeout) { int bytes = 0; fd_set rfds; struct timeval tv; int retval; if (fd == -1) { LOGE("open device first!\n"); return 0; } tv.tv_sec = 0; tv.tv_usec = timeout; FD_ZERO(&rfds); FD_SET(fd, &rfds); retval = select(fd + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { return 0; } else if (retval) { bytes = read(fd, buffer, size); #if CEC_DEBUG LOGI("CECReceiveMessage() : size(%d)", bytes); if(bytes > 0) CECPrintFrame(buffer, bytes); #endif } return bytes; } /** * Set CEC logical address. * * @return 1 if success, otherwise, return 0. */ int CECSetLogicalAddr(unsigned int laddr) { if (ioctl(fd, CEC_IOC_SETLADDR, &laddr)) { LOGE("ioctl(CEC_IOC_SETLA) failed!\n"); return 0; } return 1; } #if CEC_DEBUG /** * Print CEC frame. */ void CECPrintFrame(unsigned char *buffer, unsigned int size) { if (size > 0) { int i; LOGI("fsize: %d ", size); LOGI("frame: "); for (i = 0; i < size; i++) LOGI("0x%02x ", buffer[i]); LOGI("\n"); } } #endif /** * Check CEC message. * * @param opcode [in] pointer to buffer address where message will be stored. * @param lsrc [in] buffer size. * * @return 1 if message should be ignored, otherwise, return 0. */ //TODO: not finished int CECIgnoreMessage(unsigned char opcode, unsigned char lsrc) { int retval = 0; /* if a message coming from address 15 (unregistered) */ if (lsrc == CEC_LADDR_UNREGISTERED) { switch (opcode) { case CEC_OPCODE_DECK_CONTROL: case CEC_OPCODE_PLAY: retval = 1; default: break; } } return retval; } /** * Check CEC message. * * @param opcode [in] pointer to buffer address where message will be stored. * @param size [in] message size. * * @return 0 if message should be ignored, otherwise, return 1. */ //TODO: not finished int CECCheckMessageSize(unsigned char opcode, int size) { int retval = 1; switch (opcode) { case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: if (size != 1) retval = 0; break; case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE: if (size != 2) retval = 0; break; case CEC_OPCODE_PLAY: case CEC_OPCODE_DECK_CONTROL: case CEC_OPCODE_SET_MENU_LANGUAGE: case CEC_OPCODE_ACTIVE_SOURCE: case CEC_OPCODE_ROUTING_INFORMATION: case CEC_OPCODE_SET_STREAM_PATH: if (size != 3) retval = 0; break; case CEC_OPCODE_FEATURE_ABORT: case CEC_OPCODE_DEVICE_VENDOR_ID: case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS: if (size != 4) retval = 0; break; case CEC_OPCODE_ROUTING_CHANGE: if (size != 5) retval = 0; break; /* CDC - 1.4 */ case 0xf8: if (!(size > 5 && size <= 16)) retval = 0; break; default: break; } return retval; } /** * Check CEC message. * * @param opcode [in] pointer to buffer address where message will be stored. * @param broadcast [in] broadcast/direct message. * * @return 0 if message should be ignored, otherwise, return 1. */ //TODO: not finished int CECCheckMessageMode(unsigned char opcode, int broadcast) { int retval = 1; switch (opcode) { case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: case CEC_OPCODE_SET_MENU_LANGUAGE: case CEC_OPCODE_ACTIVE_SOURCE: if (!broadcast) retval = 0; break; case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: case CEC_OPCODE_DECK_CONTROL: case CEC_OPCODE_PLAY: case CEC_OPCODE_FEATURE_ABORT: case CEC_OPCODE_ABORT: if (broadcast) retval = 0; break; default: break; } return retval; }