From a3ecb31bc9dced1980edeb67a155b36c3b6af795 Mon Sep 17 00:00:00 2001 From: TangoCash Date: Tue, 17 Oct 2017 18:00:36 +0200 Subject: [PATCH] armbox: start implementing cec support --- libarmbox/video.cpp | 192 ++++++++++++++++++++++++++++++++++++++++++ libarmbox/video_lib.h | 27 +++++- 2 files changed, 216 insertions(+), 3 deletions(-) diff --git a/libarmbox/video.cpp b/libarmbox/video.cpp index a0412f1..0afd139 100644 --- a/libarmbox/video.cpp +++ b/libarmbox/video.cpp @@ -36,6 +36,7 @@ #include #include "video_lib.h" #include "lt_debug.h" +#include "linux-uapi-cec.h" #include @@ -160,6 +161,7 @@ cVideo::cVideo(int, void *, void *, unsigned int unit) } else devnum = unit; fd = -1; + standby_cec_activ = autoview_cec_activ = false; openDevice(); } @@ -519,6 +521,7 @@ void cVideo::Standby(unsigned int bOn) openDevice(); } video_standby = bOn; + SetCECState(video_standby); } int cVideo::getBlank(void) @@ -773,3 +776,192 @@ bool cVideo::GetScreenImage(unsigned char * &video, int &xres, int &yres, bool g return true; } + +bool cVideo::SetCECMode(VIDEO_HDMI_CEC_MODE _deviceType) +{ + physicalAddress[0] = 0x10; + physicalAddress[1] = 0x00; + logicalAddress = 1; + deviceType = 1; /* default: recorder */ + + hdmiFd = open("/dev/cec0", O_RDWR | O_CLOEXEC); + if (hdmiFd >= 0) + { + __u32 monitor = CEC_MODE_INITIATOR | CEC_MODE_FOLLOWER; + struct cec_caps caps = {}; + + if (ioctl(hdmiFd, CEC_ADAP_G_CAPS, &caps) < 0) + lt_info("%s: CEC get caps failed (%m)\n", __func__); + + if (caps.capabilities & CEC_CAP_LOG_ADDRS) + { + struct cec_log_addrs laddrs = {}; + + if (ioctl(hdmiFd, CEC_ADAP_S_LOG_ADDRS, &laddrs) < 0) + lt_info("%s: CEC reset log addr failed (%m)\n", __func__); + + memset(&laddrs, 0, sizeof(laddrs)); + + /* + * NOTE: cec_version, osd_name and deviceType should be made configurable, + * CEC_ADAP_S_LOG_ADDRS delayed till the desired values are available + * (saves us some startup speed as well, polling for a free logical address + * takes some time) + */ + laddrs.cec_version = CEC_OP_CEC_VERSION_2_0; + strcpy(laddrs.osd_name, "neutrino"); + laddrs.vendor_id = CEC_VENDOR_ID_NONE; + + switch (deviceType) + { + case CEC_LOG_ADDR_TV: + laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_TV; + laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_TV; + laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_TV; + break; + case CEC_LOG_ADDR_RECORD_1: + laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_RECORD; + laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_RECORD; + laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_RECORD; + break; + case CEC_LOG_ADDR_TUNER_1: + laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_TUNER; + laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_TUNER; + laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_TUNER; + break; + case CEC_LOG_ADDR_PLAYBACK_1: + laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_PLAYBACK; + laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_PLAYBACK; + laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_PLAYBACK; + break; + case CEC_LOG_ADDR_AUDIOSYSTEM: + laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM; + laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; + laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM; + break; + default: + laddrs.log_addr_type[laddrs.num_log_addrs] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + laddrs.all_device_types[laddrs.num_log_addrs] = CEC_OP_ALL_DEVTYPE_SWITCH; + laddrs.primary_device_type[laddrs.num_log_addrs] = CEC_OP_PRIM_DEVTYPE_SWITCH; + break; + } + laddrs.num_log_addrs++; + + if (ioctl(hdmiFd, CEC_ADAP_S_LOG_ADDRS, &laddrs) < 0) + lt_info("%s: CEC set log addr failed (%m)\n", __func__); + } + + if (ioctl(hdmiFd, CEC_S_MODE, &monitor) < 0) + lt_info("%s: CEC monitor failed (%m)\n", __func__); + + } + + getCECAddressInfo(); + +} + +void cVideo::getCECAddressInfo() +{ + if (hdmiFd >= 0) + { + bool hasdata = false; + struct addressinfo addressinfo; + + __u16 phys_addr; + struct cec_log_addrs laddrs = {}; + + ::ioctl(hdmiFd, CEC_ADAP_G_PHYS_ADDR, &phys_addr); + addressinfo.physical[0] = (phys_addr >> 8) & 0xff; + addressinfo.physical[1] = phys_addr & 0xff; + + ::ioctl(hdmiFd, CEC_ADAP_G_LOG_ADDRS, &laddrs); + addressinfo.logical = laddrs.log_addr[0]; + + switch (laddrs.log_addr_type[0]) + { + case CEC_LOG_ADDR_TYPE_TV: + addressinfo.type = CEC_LOG_ADDR_TV; + break; + case CEC_LOG_ADDR_TYPE_RECORD: + addressinfo.type = CEC_LOG_ADDR_RECORD_1; + break; + case CEC_LOG_ADDR_TYPE_TUNER: + addressinfo.type = CEC_LOG_ADDR_TUNER_1; + break; + case CEC_LOG_ADDR_TYPE_PLAYBACK: + addressinfo.type = CEC_LOG_ADDR_PLAYBACK_1; + break; + case CEC_LOG_ADDR_TYPE_AUDIOSYSTEM: + addressinfo.type = CEC_LOG_ADDR_AUDIOSYSTEM; + break; + case CEC_LOG_ADDR_TYPE_UNREGISTERED: + default: + addressinfo.type = CEC_LOG_ADDR_UNREGISTERED; + break; + } + hasdata = true; + + if (hasdata) + { + deviceType = addressinfo.type; + logicalAddress = addressinfo.logical; + if (!fixedAddress) + { + if (memcmp(physicalAddress, addressinfo.physical, sizeof(physicalAddress))) + { + lt_info("%s: detected physical address change: %02X%02X --> %02X%02X", __func__, physicalAddress[0], physicalAddress[1], addressinfo.physical[0], addressinfo.physical[1]); + memcpy(physicalAddress, addressinfo.physical, sizeof(physicalAddress)); + //reportPhysicalAddress(); + // addressChanged((physicalAddress[0] << 8) | physicalAddress[1]); + } + } + } + } +} + +void cVideo::sendCECMessage(struct cec_message &message) +{ + if (hdmiFd >= 0) + { + lt_info("[CEC] send message"); + for (int i = 0; i < message.length; i++) + { + lt_info(" %02X", message.data[i]); + } + lt_info("\n"); + struct cec_msg msg; + cec_msg_init(&msg, logicalAddress, message.address); + memcpy(&msg.msg[1], message.data, message.length); + msg.len = message.length + 1; + ioctl(hdmiFd, CEC_TRANSMIT, &msg); + } +} + +void cVideo::SetCECAutoStandby(bool state) +{ + standby_cec_activ = state; +} + +void cVideo::SetCECAutoView(bool state) +{ + autoview_cec_activ = state; +} + +void cVideo::SetCECState(bool state) +{ + struct cec_message message; + message.address = 0x0f; + message.length = 2; + + if ((standby_cec_activ) && state){ + message.data[0] = CEC_MSG_STANDBY; + message.data[1] = 1; + } + + if ((autoview_cec_activ) && !state){ + message.data[0] = CEC_MSG_IMAGE_VIEW_ON; + message.data[1] = 1; + } + + sendCECMessage(message); +} diff --git a/libarmbox/video_lib.h b/libarmbox/video_lib.h index b2e579b..2d2e787 100644 --- a/libarmbox/video_lib.h +++ b/libarmbox/video_lib.h @@ -135,6 +135,19 @@ typedef enum VIDEO_CONTROL_MAX = VIDEO_CONTROL_SHARPNESS } VIDEO_CONTROL; +struct cec_message +{ + unsigned char address; + unsigned char length; + unsigned char data[256]; +}__attribute__((packed)); +#define cec_rx_message cec_message +struct addressinfo +{ + unsigned char logical; + unsigned char physical[2]; + unsigned char type; +}; class cVideo { @@ -167,6 +180,11 @@ class cVideo /* used internally by dmx */ int64_t GetPTS(void); + unsigned char physicalAddress[2]; + bool fixedAddress,standby_cec_activ,autoview_cec_activ; + unsigned char deviceType, logicalAddress; + int hdmiFd; + public: /* constructor & destructor */ cVideo(int mode, void *, void *, unsigned int unit = 0); @@ -208,9 +226,12 @@ class cVideo int SetVideoSystem(int video_system, bool remember = true); int SetStreamType(VIDEO_FORMAT type); void SetSyncMode(AVSYNC_TYPE mode); - bool SetCECMode(VIDEO_HDMI_CEC_MODE) { return true; }; - void SetCECAutoView(bool) { return; }; - void SetCECAutoStandby(bool) { return; }; + bool SetCECMode(VIDEO_HDMI_CEC_MODE); + void SetCECAutoView(bool); + void SetCECAutoStandby(bool); + void getCECAddressInfo(); + void sendCECMessage(struct cec_message &message); + void SetCECState(bool state); void ShowPicture(const char * fname, const char *_destname = NULL); void StopPicture(); void Standby(unsigned int bOn);