From d591b4b84d85788c420b9a5ba3517dec6460a2b9 Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Sat, 7 Jan 2017 18:19:27 +0100 Subject: [PATCH 01/10] new RainShadow adapter --- docs/README.linux.md | 8 +- include/cectypes.h | 13 +- src/libcec/CMakeLists.txt | 3 +- src/libcec/adapter/AdapterFactory.cpp | 24 +- src/libcec/adapter/RainShadow/Rain.h | 38 ++ .../RainShadow/RainAdapterCommunication.cpp | 484 +++++++++++++++++ .../RainShadow/RainAdapterCommunication.h | 137 +++++ .../RainShadow/RainAdapterDetection.cpp | 507 ++++++++++++++++++ .../adapter/RainShadow/RainAdapterDetection.h | 56 ++ src/libcec/cmake/CheckPlatformSupport.cmake | 14 + src/libcec/cmake/DisplayPlatformSupport.cmake | 10 +- src/libcec/env.h.in | 3 + 12 files changed, 1290 insertions(+), 7 deletions(-) create mode 100644 src/libcec/adapter/RainShadow/Rain.h create mode 100644 src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp create mode 100644 src/libcec/adapter/RainShadow/RainAdapterCommunication.h create mode 100644 src/libcec/adapter/RainShadow/RainAdapterDetection.cpp create mode 100644 src/libcec/adapter/RainShadow/RainAdapterDetection.h diff --git a/docs/README.linux.md b/docs/README.linux.md index c59fb806..acd58ac3 100644 --- a/docs/README.linux.md +++ b/docs/README.linux.md @@ -45,6 +45,12 @@ Pass the argument `-DHAVE_AOCEC_API=1` to the cmake command in the compilation i cmake -DHAVE_AOCEC_API=1 .. ``` +### RainShadow +Pass the argument `-DHAVE_RAINSHADOW_API=1` to the cmake command in the compilation instructions: +``` +cmake -DHAVE_RAINSHADOW_API=1 .. +``` + ### TDA995x Pass the argument `-DHAVE_TDA995X_API=1` to the cmake command in the compilation instructions: ``` @@ -52,4 +58,4 @@ cmake -DHAVE_TDA995X_API=1 .. ``` ### Debian / Ubuntu .deb packaging -See [docs/README.debian.md](README.debian.md). \ No newline at end of file +See [docs/README.debian.md](README.debian.md). diff --git a/include/cectypes.h b/include/cectypes.h index 9c918427..af6aabe4 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -291,6 +291,16 @@ namespace CEC { */ #define CEC_AOCEC_VIRTUAL_COM "AOCEC" +/*! + * the path to use for the RainShadow HDMI CEC device + */ +#define CEC_RAINSHADOW_PATH "/dev/ttyACM0" + +/*! + * the name of the virtual COM port to use for the RainShadow' CEC wire + */ +#define CEC_RAINSHADOW_VIRTUAL_COM "RainShadow" + /*! * Mimimum client version */ @@ -861,7 +871,8 @@ typedef enum cec_adapter_type ADAPTERTYPE_RPI = 0x100, ADAPTERTYPE_TDA995x = 0x200, ADAPTERTYPE_EXYNOS = 0x300, - ADAPTERTYPE_AOCEC = 0x500 + ADAPTERTYPE_AOCEC = 0x500, + ADAPTERTYPE_RAINSHADOW = 0x700 } cec_adapter_type; /** force exporting through swig */ diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt index d3eefa34..01ecd5ba 100644 --- a/src/libcec/CMakeLists.txt +++ b/src/libcec/CMakeLists.txt @@ -68,7 +68,8 @@ set(CEC_SOURCES_IMPLEMENTATIONS implementations/ANCommandHandler.cpp # /platform/* set(CEC_SOURCES_PLATFORM platform/adl/adl-edid.cpp - platform/nvidia/nv-edid.cpp) + platform/nvidia/nv-edid.cpp + platform/drm/drm-edid.cpp) # headers set(CEC_EXT_HEADERS ${PROJECT_SOURCE_DIR}/../../include/cec.h diff --git a/src/libcec/adapter/AdapterFactory.cpp b/src/libcec/adapter/AdapterFactory.cpp index 91195ea0..a7ba2d3c 100644 --- a/src/libcec/adapter/AdapterFactory.cpp +++ b/src/libcec/adapter/AdapterFactory.cpp @@ -63,6 +63,11 @@ #include "AOCEC/AOCECAdapterCommunication.h" #endif +#if defined(HAVE_RAINSHADOW_API) +#include "RainShadow/RainAdapterDetection.h" +#include "RainShadow/RainAdapterCommunication.h" +#endif + using namespace CEC; int8_t CAdapterFactory::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) @@ -143,8 +148,19 @@ int8_t CAdapterFactory::DetectAdapters(cec_adapter_descriptor *deviceList, uint8 } #endif +#if defined(HAVE_RAINSHADOW_API) + if (!CRainAdapterDetection::CanAutodetect()) + { + if (m_lib) + m_lib->AddLog(CEC_LOG_WARNING, "libCEC has not been compiled with detection code for the RAINSHADOW USB-CEC Adapter, so the path to the COM port has to be provided to libCEC if this adapter is being used"); + } + else + iAdaptersFound += CRainAdapterDetection::FindAdapters(deviceList, iBufSize, strDevicePath); +#else + m_lib->AddLog(CEC_LOG_WARNING, "libCEC has not been compiled with support for the RainShadow USB-CEC Adapter"); +#endif -#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_AOCEC_API) +#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_AOCEC_API) && !defined(HAVE_RAINSHADOW_API) #error "libCEC doesn't have support for any type of adapter. please check your build system or configuration" #endif @@ -168,6 +184,10 @@ IAdapterCommunication *CAdapterFactory::GetInstance(const char *strPort, uint16_ return new CAOCECAdapterCommunication(m_lib->m_cec); #endif +#if defined(HAVE_RAINSHADOW_API) + return new CRainAdapterCommunication(m_lib->m_cec, strPort, iBaudRate); +#endif + #if defined(HAVE_RPI_API) if (!strcmp(strPort, CEC_RPI_VIRTUAL_COM)) return new CRPiCECAdapterCommunication(m_lib->m_cec); @@ -177,7 +197,7 @@ IAdapterCommunication *CAdapterFactory::GetInstance(const char *strPort, uint16_ return new CUSBCECAdapterCommunication(m_lib->m_cec, strPort, iBaudRate); #endif -#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_EXYNOS_API) && !defined(HAVE_AOCEC_API) +#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_EXYNOS_API) && !defined(HAVE_AOCEC_API) && !defined(HAVE_RAINSHADOW_API) return NULL; #endif } diff --git a/src/libcec/adapter/RainShadow/Rain.h b/src/libcec/adapter/RainShadow/Rain.h new file mode 100644 index 00000000..3263f95f --- /dev/null +++ b/src/libcec/adapter/RainShadow/Rain.h @@ -0,0 +1,38 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC RainShadow Code Copyright (C) 2017 Gerald Dachs + * based heavily on: + * libCEC Exynos Code Copyright (C) 2014 Valentin Manea + * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + + +#define CEC_MAX_FRAME_SIZE 16 diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp new file mode 100644 index 00000000..ce55a6a3 --- /dev/null +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp @@ -0,0 +1,484 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC RainShadow Code Copyright (C) 2017 Gerald Dachs + * based heavily on: + * libCEC Exynos Code Copyright (C) 2014 Valentin Manea + * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" +#include +#include + +#if defined(HAVE_RAINSHADOW_API) +#include "Rain.h" +#include "RainAdapterCommunication.h" +#include "CECTypeUtils.h" +#include "LibCEC.h" +#include "platform/sockets/serialport.h" +#include +#include +#include "platform/util/edid.h" +#include "platform/drm/drm-edid.h" + +using namespace CEC; +using namespace P8PLATFORM; + +#define LIB_CEC m_callback->GetLib() + +CRainAdapterCommunication::CRainAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint32_t iBaudRate /* = CEC_RAINSHADOW_SERIAL_DEFAULT_BAUDRATE */) : + IAdapterCommunication(callback), + m_port(NULL), + m_gotResponse(false), + m_bLogicalAddressChanged(false) + +{ + CLockObject lock(m_mutex); + m_logicalAddresses.Clear(); + m_port = new CSerialPort(strPort, iBaudRate); +} + +CRainAdapterCommunication::~CRainAdapterCommunication(void) +{ + Close(); + SAFE_DELETE(m_port); +} + +bool CRainAdapterCommunication::IsOpen(void) +{ + /* thread is not being stopped, the port is open and the thread is running */ + return !IsStopped() && m_port->IsOpen() && IsRunning(); +} + +bool CRainAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool UNUSED(bSkipChecks) /* = false */, bool bStartListening /* = true */) +{ + bool bConnectionOpened(false); + CLockObject lock(m_mutex); + + /* we need the port settings here */ + if (!m_port) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "port is NULL"); + return bConnectionOpened; + } + + /* return true when the port is already open */ + if (IsOpen()) + { + LIB_CEC->AddLog(CEC_LOG_WARNING, "port is already open"); + return true; + } + + /* try to open the connection */ + std::string strError; + CTimeout timeout(iTimeoutMs); + while (!bConnectionOpened && timeout.TimeLeft() > 0) + { + if ((bConnectionOpened = m_port->Open(timeout.TimeLeft())) == false) + { + strError = StringUtils::Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str()); + Sleep(250); + } + /* and retry every 250ms until the timeout passed */ + } + + /* return false when we couldn't connect */ + if (!bConnectionOpened) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, strError.c_str()); + + if (m_port->GetErrorNumber() == EACCES) + { + libcec_parameter param; + param.paramType = CEC_PARAMETER_TYPE_STRING; + param.paramData = (void*)"No permission to open the device"; + LIB_CEC->Alert(CEC_ALERT_PERMISSION_ERROR, param); + } + else if (m_port->GetErrorNumber() == EBUSY) + { + libcec_parameter param; + param.paramType = CEC_PARAMETER_TYPE_STRING; + param.paramData = (void*)"The serial port is busy. Only one program can access the device directly."; + LIB_CEC->Alert(CEC_ALERT_PORT_BUSY, param); + } + return false; + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting"); + + m_response[0] = '\0'; + m_gotResponse = true; + m_condition.Signal(); + + // always start by setting the ackmask to 0, to clear previous values + cec_logical_addresses addresses; addresses.Clear(); + SetLogicalAddresses(addresses); + + if (!CreateThread()) + { + bConnectionOpened = false; + LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a communication thread"); + } + + if (!bConnectionOpened || !bStartListening) + StopThread(0); + + return bConnectionOpened; +} + +void CRainAdapterCommunication::Close(void) +{ + /* stop the reader thread */ + StopThread(0); + + CLockObject lock(m_mutex); + + /* set the ackmask to 0 before closing the connection */ + if (IsOpen() && m_port->GetErrorNumber() == 0) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__); + cec_logical_addresses addresses; addresses.Clear(); + SetLogicalAddresses(addresses); + } + + /* close and delete the com port connection */ + if (m_port) + m_port->Close(); +} + +/** + * hex_to_bin - convert a hex digit to its real value + * @ch: ascii character represents hex digit + * + * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad + * input. + */ +int CRainAdapterCommunication::hex_to_bin(char ch) +{ + if ((ch >= '0') && (ch <= '9')) + return ch - '0'; + ch = tolower(ch); + if ((ch >= 'a') && (ch <= 'f')) + return ch - 'a' + 10; + return -1; +} + +/** + * hex2bin - convert an ascii hexadecimal string to its binary representation + * @dst: binary result + * @src: ascii hexadecimal string + * @count: result length + * + * Return 0 on success, -1 in case of bad input. + */ +int CRainAdapterCommunication::hex2bin(uint8_t *dst, const char *src, size_t count) +{ + while (count--) + { + int hi = hex_to_bin(*src++); + int lo = hex_to_bin(*src++); + + if ((hi < 0) || (lo < 0)) + return -1; + + *dst++ = (hi << 4) | lo; + } + return 0; +} + +std::string CRainAdapterCommunication::GetError(void) const +{ + return m_port->GetError(); +} + +cec_adapter_message_state CRainAdapterCommunication::Write( + const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), + bool UNUSED(bIsReply)) +{ + char buffer[DATA_SIZE]; + CLockObject lock(m_mutex); + + if (!IsOpen()) + return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; + + if (!data.opcode_set) + { + snprintf(buffer, sizeof(buffer), "!X%x~", data.destination); + } + else + { + char hex[4]; + + snprintf(buffer, sizeof(buffer), "!X%x %02x ", data.destination, + data.opcode); + for (int i = 0; i < data.parameters.size; i++) + { + snprintf(hex, sizeof(hex), "%02x ", data.parameters[i]); + strncat(buffer, hex, sizeof(buffer) - 1); + } + buffer[strlen(buffer) - 1] = '~'; + } + + if (m_port->Write(buffer, strlen(buffer)) == (ssize_t) strlen(buffer)) + { + m_condition.Wait(m_mutex, m_gotResponse); + m_gotResponse = false; + + if (!strncmp(m_response, "STA", 3) && m_response[strlen(m_response) - 1] == '1') + { + return ADAPTER_MESSAGE_STATE_SENT_ACKED; + } + } + return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; +} + +uint16_t CRainAdapterCommunication::GetFirmwareVersion(void) +{ + return 0; +} + +cec_vendor_id CRainAdapterCommunication::GetVendorId(void) +{ + return cec_vendor_id(CEC_VENDOR_UNKNOWN); +} + +std::string CRainAdapterCommunication::GetPortName(void) +{ + return m_port->GetName(); +} + +uint16_t CRainAdapterCommunication::GetPhysicalAddress(void) +{ + uint16_t iPA(0); + + // try to get the PA from ADL +#if defined(HAS_ADL_EDID_PARSER) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via ADL", __FUNCTION__); + CADLEdidParser adl; + iPA = adl.GetPhysicalAddress(); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - ADL returned physical address %04x", __FUNCTION__, iPA); + } +#endif + + // try to get the PA from the nvidia driver +#if defined(HAS_NVIDIA_EDID_PARSER) + if (iPA == 0) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via nvidia driver", __FUNCTION__); + CNVEdidParser nv; + iPA = nv.GetPhysicalAddress(); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - nvidia driver returned physical address %04x", __FUNCTION__, iPA); + } +#endif + +// try to get the PA from the intel driver +#if defined(HAVE_DRM_EDID_PARSER) + if (iPA == 0) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via drm files", __FUNCTION__); + CDRMEdidParser drm; + iPA = drm.GetPhysicalAddress(); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - drm files returned physical address %04x", __FUNCTION__, iPA); + } +#endif + + // try to get the PA from the OS + if (iPA == 0) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address from the OS", __FUNCTION__); + iPA = CEDIDParser::GetPhysicalAddress(); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - OS returned physical address %04x", __FUNCTION__, iPA); + } + + return iPA; +} + +cec_logical_addresses CRainAdapterCommunication::GetLogicalAddresses(void) +{ + CLockObject lock(m_mutex); + return m_logicalAddresses; +} + +bool CRainAdapterCommunication::InternalSetLogicalAddresses(const unsigned int log_addr) +{ + char command[DATA_SIZE]; + + CLockObject lock(m_mutex); + + if (!IsOpen()) + return false; + + snprintf(command, sizeof(command), "!A %x~", log_addr); + + if (m_port->Write(command, strlen(command)) != (ssize_t) strlen(command)) + { + return false; + } + + m_condition.Wait(m_mutex, m_gotResponse); + m_gotResponse = false; + + return !strncmp(m_response, "ADR", 3); +} + +bool CRainAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses) +{ + if (InternalSetLogicalAddresses(addresses.primary)) + { + m_logicalAddresses = addresses; + m_bLogicalAddressChanged = true; + + return true; + } + return false; +} + +void CRainAdapterCommunication::HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)) +{ + InternalSetLogicalAddresses(CECDEVICE_BROADCAST); +} + +void CRainAdapterCommunication::ProcessMessage(char *buffer) +{ + unsigned char msg[CEC_MAX_FRAME_SIZE]; + unsigned int len = 0; + int stat = -1; + + for (char *data = buffer + 3; *data; data++) + { + if (!isxdigit(*data)) + continue; + if (isxdigit(data[0]) && isxdigit(data[1])) + { + if (len == sizeof(msg)) + break; + if (hex2bin(msg + len, data, 1)) + continue; + len++; + data++; + continue; + } + if (!data[1]) + stat = hex_to_bin(data[0]); + break; + } + + if (len > 0 && (stat == 1 || stat == 2)) + { + cec_command cmd; + cec_logical_address initiator, destination; + + initiator = cec_logical_address(msg[0] >> 4); + destination = cec_logical_address(msg[0] & 0x0f); + + cec_command::Format(cmd, initiator, destination, + (len > 1) ? cec_opcode(msg[1]) : CEC_OPCODE_NONE); + + for (uint8_t i = 2; i < len; i++) + cmd.parameters.PushBack(msg[i]); + + if (!IsStopped()) + m_callback->OnCommandReceived(cmd); + } +} + +void *CRainAdapterCommunication::Process(void) +{ + char buf[DATA_SIZE]; + unsigned int idx = 0; + bool started = false; + unsigned char data; + + if (!IsOpen()) + return 0; + + while (!IsStopped()) + { + do + { + /* retry Read() if it was interrupted */ + m_port->Read(&data, sizeof(uint8_t), 50); + } while (m_port->GetErrorNumber() == EINTR); + + if (m_port->GetErrorNumber()) + { + libcec_parameter param; + param.paramType = CEC_PARAMETER_TYPE_STRING; + param.paramData = (void*)"No permission to open the device"; + LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param); + + break; + } + + if (!started && data != '?') + continue; + if (data == '\r') + { + buf[idx] = '\0'; + + if (!strncmp(buf, "REC", 3)) + ProcessMessage(buf); + else + { + strncpy(m_response, buf, sizeof(m_response)); + + m_gotResponse = true; + m_condition.Signal(); + } + idx = 0; + started = false; + continue; + } + else if (data == '?') + { + idx = 0; + started = true; + continue; + } + + if (data == '\n') + { + idx = 0; + started = false; + continue; + } + if (idx >= DATA_SIZE - 1) + { + idx = 0; + } + buf[idx++] = data; + continue; + } + + return 0; +} + +#endif // HAVE_RAINSHADOW_API diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h new file mode 100644 index 00000000..35542b47 --- /dev/null +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h @@ -0,0 +1,137 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC RainShadow Code Copyright (C) 2017 Gerald Dachs + * based heavily on: + * libCEC Exynos Code Copyright (C) 2014 Valentin Manea + * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ +#include "env.h" + + +#if defined(HAVE_RAINSHADOW_API) + +#include + +#include +#include +#include +#include "../AdapterCommunication.h" +//#include + +#define DATA_SIZE 256 +#define CEC_RAINSHADOW_SERIAL_DEFAULT_BAUDRATE 115200L + +namespace CEC +{ + class CRainAdapterCommunication : public IAdapterCommunication, public P8PLATFORM::CThread + { + public: + /*! + * @brief Create a new USB-CEC communication handler. + * @param callback The callback to use for incoming CEC commands. + * @param strPort The name of the com port to use. + * @param iBaudRate The baudrate to use on the com port connection. + */ + CRainAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint32_t iBaudRate = CEC_RAINSHADOW_SERIAL_DEFAULT_BAUDRATE); + virtual ~CRainAdapterCommunication(void); + + /** @name IAdapterCommunication implementation */ + ///{ + bool Open(uint32_t iTimeoutMs = CEC_DEFAULT_CONNECT_TIMEOUT, bool bSkipChecks = false, bool bStartListening = true); + void Close(void); + bool IsOpen(void); + std::string GetError(void) const; + cec_adapter_message_state Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool bIsReply); + + bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; } + bool StartBootloader(void) { return false; } + bool SetLogicalAddresses(const cec_logical_addresses &addresses); + cec_logical_addresses GetLogicalAddresses(void); + bool PingAdapter(void) { return IsInitialised(); } + uint16_t GetFirmwareVersion(void); + uint32_t GetFirmwareBuildDate(void) { return 0; } + bool IsRunningLatestFirmware(void) { return true; } + bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; } + bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; } + std::string GetPortName(void); + uint16_t GetPhysicalAddress(void); + bool SetControlledMode(bool UNUSED(controlled)) { return true; } + cec_vendor_id GetVendorId(void); + bool SupportsSourceLogicalAddress(const cec_logical_address address) { return address > CECDEVICE_TV && address <= CECDEVICE_BROADCAST; } + cec_adapter_type GetAdapterType(void) { return ADAPTERTYPE_RAINSHADOW; } + uint16_t GetAdapterVendorId(void) const { return 1; } + uint16_t GetAdapterProductId(void) const { return 1; } + void HandleLogicalAddressLost(cec_logical_address oldAddress); + void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + ///} + + void ProcessMessage(char *buffer); + + /** @name P8PLATFORM::CThread implementation */ + ///{ + void *Process(void); + ///} + + private: + bool IsInitialised(void) const { return 1; }; + + bool InternalSetLogicalAddresses(const unsigned int log_addr); + + /** + * hex_to_bin - convert a hex digit to its real value + * @ch: ascii character represents hex digit + * + * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad + * input. + */ + int hex_to_bin(char ch); + + /** + * hex2bin - convert an ascii hexadecimal string to its binary representation + * @dst: binary result + * @src: ascii hexadecimal string + * @count: result length + * + * Return 0 on success, -1 in case of bad input. + */ + int hex2bin(uint8_t *dst, const char *src, size_t count); + + P8PLATFORM::ISocket * m_port; /**< the com port connection */ + std::string m_strError; /**< current error message */ + char m_response[DATA_SIZE]; /**< current response from adapter */ + P8PLATFORM::CMutex m_mutex; + P8PLATFORM::CCondition m_condition; + bool m_gotResponse; + bool m_bLogicalAddressChanged; + cec_logical_addresses m_logicalAddresses; + }; +}; +#endif diff --git a/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp b/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp new file mode 100644 index 00000000..50d2c81e --- /dev/null +++ b/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp @@ -0,0 +1,507 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" +#include "RainAdapterDetection.h" + +#if defined(__APPLE__) +#include +#include +#include +#include +#include +#include +#include +#include +#elif defined(__WINDOWS__) +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "setupapi.lib") +#pragma comment(lib, "cfgmgr32.lib") +#include +#include +#include + +// the virtual COM port only shows up when requesting devices with the raw device guid! +static GUID USB_RAW_GUID = { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }; +static GUID USB_CDC_GUID = { 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } }; + +#elif defined(HAVE_LIBUDEV) +#include +#include +extern "C" { +#include +} +#elif defined(__FreeBSD__) +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +using namespace CEC; + +#if defined(HAVE_LIBUDEV) +static bool TranslateComPort(std::string& strString) +{ + std::string strTmp(strString); + std::reverse(strTmp.begin(), strTmp.end()); + const char* iSlash = strchr(strTmp.c_str(), '/'); + if (iSlash) + { + strTmp = StringUtils::Left(strTmp, iSlash - strTmp.c_str()); + std::reverse(strTmp.begin(), strTmp.end()); + strString = StringUtils::Format("%s/%s:1.0/tty", strString.c_str(), strTmp.c_str()); + return true; + } + + return false; +} + +static bool FindComPort(std::string& strLocation) +{ + std::string strPort = strLocation; + bool bReturn(!strPort.empty()); + std::string strConfigLocation(strLocation); + if (TranslateComPort(strConfigLocation)) + { + DIR *dir; + struct dirent *dirent; + if((dir = opendir(strConfigLocation.c_str())) == NULL) + return bReturn; + + while ((dirent = readdir(dir)) != NULL) + { + if(strcmp((char*)dirent->d_name, "." ) != 0 && strcmp((char*)dirent->d_name, ".." ) != 0) + { + strPort = StringUtils::Format("/dev/%s", dirent->d_name); + if (!strPort.empty()) + { + strLocation = strPort; + bReturn = true; + break; + } + } + } + closedir(dir); + } + + return bReturn; +} +#endif + +bool CRainAdapterDetection::CanAutodetect(void) +{ +#if defined(__APPLE__) || defined(HAVE_LIBUDEV) || defined(__WINDOWS__) || defined(__FreeBSD__) + return true; +#else + return false; +#endif +} + +#if defined(__WINDOWS__) +static bool GetComPortFromDevNode(DEVINST hDevInst, char* strPortName, unsigned int iSize) +{ + bool bReturn(false); + TCHAR strRegPortName[256]; + strRegPortName[0] = _T('\0'); + DWORD dwSize = sizeof(strRegPortName); + DWORD dwType = 0; + HKEY hDeviceKey; + + // open the device node key + if (CM_Open_DevNode_Key(hDevInst, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hDeviceKey, CM_REGISTRY_HARDWARE) != CR_SUCCESS) + { + printf("reg key not found\n"); + return bReturn; + } + + // locate the PortName entry. TODO this one doesn't seem to be available in universal + if ((RegQueryValueEx(hDeviceKey, _T("PortName"), NULL, &dwType, reinterpret_cast(strRegPortName), &dwSize) == ERROR_SUCCESS) && + (dwType == REG_SZ) && + _tcslen(strRegPortName) > 3 && + _tcsnicmp(strRegPortName, _T("COM"), 3) == 0 && + _ttoi(&(strRegPortName[3])) > 0) + { + // return the port name + snprintf(strPortName, iSize, "%s", strRegPortName); + bReturn = true; + } + + // TODO this one doesn't seem to be available in universal + RegCloseKey(hDeviceKey); + + return bReturn; +} + +static bool GetPidVidFromDeviceName(const std::string strDevName, int* vid, int* pid) +{ + size_t iPidPos = strDevName.find("PID_"); + size_t iVidPos = strDevName.find("VID_"); + if (iPidPos == std::string::npos || iVidPos == std::string::npos || (strDevName.find("&MI_") != std::string::npos && strDevName.find("&MI_00") == std::string::npos)) + return false; + + std::string strVendorId(strDevName.substr(iVidPos + 4, 4)); + std::string strProductId(strDevName.substr(iPidPos + 4, 4)); + + sscanf(strVendorId.c_str(), "%x", vid); + sscanf(strProductId.c_str(), "%x", pid); + + return true; +} +#endif + +uint8_t CRainAdapterDetection::FindAdaptersWindows(cec_adapter_descriptor* deviceList, uint8_t iBufSize, const char* strDevicePath /* = NULL */) +{ + uint8_t iFound(0); + +#if defined(__WINDOWS__) + ULONG len; + PCHAR buffer; + + CM_Get_Device_ID_List_Size(&len, 0, CM_GETIDLIST_FILTER_NONE); + buffer = (PCHAR)malloc(sizeof(CHAR) * len); + if (buffer) + { + CM_Get_Device_ID_List(0, buffer, len, CM_GETIDLIST_FILTER_NONE); + + for (CHAR* devId = buffer; *devId; devId += strlen(devId) + 1) + { + // check whether the path matches, if a path was given + if (strDevicePath && strcmp(strDevicePath, devId) != 0) + continue; + + // get the vid and pid + int iVendor, iProduct; + if (!GetPidVidFromDeviceName(devId, &iVendor, &iProduct)) + continue; + + // no match + if ((iVendor != RAIN_CEC_VID || iProduct != RAIN_CEC_PID) && (iVendor != RAIN_CEC_VID2 || iProduct != RAIN_CEC_PID2)) + continue; + + // locate the device node + DEVINST devInst = 0, childInst = 0; + if (CM_Locate_DevNode(&devInst, devId, 0) != CR_SUCCESS) + continue; + + // get the child node if this is a composite device + if (iProduct == RAIN_CEC_PID2) + { + if (CM_Get_Child(&childInst, devInst, 0) != CR_SUCCESS) + continue; + devInst = childInst; + } + + // get the com port + if (devInst != 0) + { + if (GetComPortFromDevNode(devInst, deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName))) + { + snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", devId); + deviceList[iFound].iVendorId = (uint16_t)iVendor; + deviceList[iFound].iProductId = (uint16_t)iProduct; + deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type + iFound++; + } + } + } + + free(buffer); + } +#else + (void)deviceList; + (void)iBufSize; + (void)strDevicePath; +#endif + + return iFound; +} + +uint8_t CRainAdapterDetection::FindAdaptersApple(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) +{ + uint8_t iFound(0); + +#if defined(__APPLE__) + kern_return_t kresult; + char bsdPath[MAXPATHLEN] = { 0 }; + io_iterator_t serialPortIterator; + + CFMutableDictionaryRef classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (classesToMatch) + { + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType)); + kresult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &serialPortIterator); + if (kresult == KERN_SUCCESS) + { + io_object_t serialService; + while ((serialService = IOIteratorNext(serialPortIterator))) + { + int iVendor = 0, iProduct = 0; + CFTypeRef bsdPathAsCFString; + + // fetch the device path. + bsdPathAsCFString = IORegistryEntryCreateCFProperty(serialService, + CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + if (bsdPathAsCFString) + { + // convert the path from a CFString to a C (NUL-terminated) string. + CFStringGetCString((CFStringRef)bsdPathAsCFString, bsdPath, MAXPATHLEN - 1, kCFStringEncodingUTF8); + CFRelease(bsdPathAsCFString); + + // now walk up the hierarchy until we find the entry with vendor/product IDs + io_registry_entry_t parent; + CFTypeRef vendorIdAsCFNumber = NULL; + CFTypeRef productIdAsCFNumber = NULL; + kern_return_t kresult = IORegistryEntryGetParentEntry(serialService, kIOServicePlane, &parent); + while (kresult == KERN_SUCCESS) + { + vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent, + kIOServicePlane, CFSTR(kUSBVendorID), kCFAllocatorDefault, 0); + productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent, + kIOServicePlane, CFSTR(kUSBProductID), kCFAllocatorDefault, 0); + if (vendorIdAsCFNumber && productIdAsCFNumber) + { + CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberIntType, &iVendor); + CFRelease(vendorIdAsCFNumber); + CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberIntType, &iProduct); + CFRelease(productIdAsCFNumber); + IOObjectRelease(parent); + break; + } + io_registry_entry_t oldparent = parent; + kresult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent); + IOObjectRelease(oldparent); + } + + if (strlen(bsdPath) && (iVendor == RAIN_CEC_VID && iProduct == RAIN_CEC_PID || iVendor == RAIN_CEC_VID2 && iProduct == RAIN_CEC_PID2)) + { + if (!strDevicePath || !strcmp(bsdPath, strDevicePath)) + { + // on darwin, the device path is the same as the comm path. + if (iFound == 0 || strcmp(deviceList[iFound - 1].strComName, bsdPath)) + { + snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", bsdPath); + snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", bsdPath); + deviceList[iFound].iVendorId = iVendor; + deviceList[iFound].iProductId = iProduct; + deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type + iFound++; + } + } + } + } + IOObjectRelease(serialService); + } + } + IOObjectRelease(serialPortIterator); + } +#else + (void)deviceList; + (void)iBufSize; + (void)strDevicePath; +#endif + return iFound; +} + +uint8_t CRainAdapterDetection::FindAdaptersUdev(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) +{ + uint8_t iFound(0); + +#if defined(HAVE_LIBUDEV) + struct udev *udev; + if (!(udev = udev_new())) + return -1; + + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + struct udev_device *dev, *pdev; + enumerate = udev_enumerate_new(udev); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + udev_list_entry_foreach(dev_list_entry, devices) + { + const char *strPath; + strPath = udev_list_entry_get_name(dev_list_entry); + + dev = udev_device_new_from_syspath(udev, strPath); + if (!dev) + continue; + + pdev = udev_device_get_parent(udev_device_get_parent(dev)); + if (!pdev || !udev_device_get_sysattr_value(pdev, "idVendor") || !udev_device_get_sysattr_value(pdev, "idProduct")) + { + udev_device_unref(dev); + continue; + } + + int iVendor, iProduct; + sscanf(udev_device_get_sysattr_value(pdev, "idVendor"), "%x", &iVendor); + sscanf(udev_device_get_sysattr_value(pdev, "idProduct"), "%x", &iProduct); + if ((iVendor == RAIN_CEC_VID && iProduct == RAIN_CEC_PID) || (iVendor == RAIN_CEC_VID2 && iProduct == RAIN_CEC_PID2)) + { + std::string strPath(udev_device_get_syspath(pdev)); + if (!strDevicePath || !strcmp(strPath.c_str(), strDevicePath)) + { + std::string strComm(strPath); + if (FindComPort(strComm) && (iFound == 0 || strcmp(deviceList[iFound - 1].strComName, strComm.c_str()))) + { + snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", strPath.c_str()); + snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", strComm.c_str()); + deviceList[iFound].iVendorId = iVendor; + deviceList[iFound].iProductId = iProduct; + deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type + iFound++; + } + } + } + udev_device_unref(dev); + + if (iFound >= iBufSize) + break; + } + + udev_enumerate_unref(enumerate); + udev_unref(udev); +#else + (void)deviceList; + (void)iBufSize; + (void)strDevicePath; +#endif + + return iFound; +} + +uint8_t CRainAdapterDetection::FindAdaptersFreeBSD(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) +{ + uint8_t iFound(0); + +#if defined(__FreeBSD__) + char devicePath[PATH_MAX + 1]; + char infos[512]; + char sysctlname[32]; + char ttyname[8]; + char *pos; + size_t infos_size = sizeof(infos); + int i; + + for (i = 0;; ++i) + { + unsigned int iVendor, iProduct; + memset(infos, 0, sizeof(infos)); + (void)snprintf(sysctlname, sizeof(sysctlname), + "dev.umodem.%d.%%pnpinfo", i); + if (sysctlbyname(sysctlname, infos, &infos_size, + NULL, 0) != 0) + break; + pos = strstr(infos, "vendor="); + if (pos == NULL) + continue; + sscanf(pos, "vendor=%x ", &iVendor); + + pos = strstr(infos, "product="); + if (pos == NULL) + continue; + sscanf(pos, "product=%x ", &iProduct); + + if ((iVendor != RAIN_CEC_VID || iProduct != RAIN_CEC_PID) && (iVendor != RAIN_CEC_VID2 || iProduct != RAIN_CEC_PID2)) + continue; + + pos = strstr(infos, "ttyname="); + if (pos == NULL) + continue; + sscanf(pos, "ttyname=%s ", ttyname); + + (void)snprintf(devicePath, sizeof(devicePath), + "/dev/tty%s", ttyname); + + if (strDevicePath) { + char currStrDevicePath[512]; + int port = 0; + int devaddr = 0; + memset(currStrDevicePath, 0, sizeof(currStrDevicePath)); + memset(infos, 0, sizeof(infos)); + (void)snprintf(sysctlname, sizeof(sysctlname), + "dev.umodem.%d.%%location", i); + if (sysctlbyname(sysctlname, infos, &infos_size, + NULL, 0) != 0) + break; + + pos = strstr(infos, "port="); + if (pos == NULL) + continue; + sscanf(pos, "port=%d ", &port); + + pos = strstr(infos, "devaddr="); + if (pos == NULL) + continue; + sscanf(pos, "devaddr=%d ", &devaddr); + + (void)snprintf(currStrDevicePath, sizeof(currStrDevicePath), + "/dev/ugen%d.%d", port, devaddr); + + if (strcmp(currStrDevicePath, strDevicePath) != 0) + continue; + } + snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", devicePath); + snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", devicePath); + deviceList[iFound].iVendorId = iVendor; + deviceList[iFound].iProductId = iProduct; + deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type + iFound++; + } +#else + (void)deviceList; + (void)iBufSize; + (void)strDevicePath; +#endif + + return iFound; +} + +uint8_t CRainAdapterDetection::FindAdapters(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) +{ + uint8_t iFound(0); + iFound = FindAdaptersApple(deviceList, iBufSize, strDevicePath); + if (iFound == 0) + iFound = FindAdaptersFreeBSD(deviceList, iBufSize, strDevicePath); + if (iFound == 0) + iFound = FindAdaptersUdev(deviceList, iBufSize, strDevicePath); + if (iFound == 0) + iFound = FindAdaptersWindows(deviceList, iBufSize, strDevicePath); + return iFound; +} diff --git a/src/libcec/adapter/RainShadow/RainAdapterDetection.h b/src/libcec/adapter/RainShadow/RainAdapterDetection.h new file mode 100644 index 00000000..403c9666 --- /dev/null +++ b/src/libcec/adapter/RainShadow/RainAdapterDetection.h @@ -0,0 +1,56 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" + +namespace CEC +{ +#define RAIN_CEC_VID 0x0483 // STMicroelectronics +#define RAIN_CEC_PID 0x5740 +#define RAIN_CEC_VID2 0x04D8 // RainShadow +#define RAIN_CEC_PID2 0xFF59 + + class CRainAdapterDetection + { + public: + static uint8_t FindAdapters(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); + static bool CanAutodetect(void); + + private: + static uint8_t FindAdaptersWindows(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); + static uint8_t FindAdaptersApple(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); + static uint8_t FindAdaptersUdev(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); + static uint8_t FindAdaptersFreeBSD(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); + }; +}; diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 532f2132..24f23ec5 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -10,6 +10,7 @@ # HAVE_TDA995X_API ON if TDA995X is supported # HAVE_EXYNOS_API ON if Exynos is supported # HAVE_AOCEC_API ON if AOCEC is supported +# HAVE_RAINSHADOW_API ON if RainShadow is supported # HAVE_P8_USB ON if Pulse-Eight devices are supported # HAVE_P8_USB_DETECT ON if Pulse-Eight devices can be auto-detected # HAVE_DRM_EDID_PARSER ON if DRM EDID parsing is supported @@ -30,6 +31,7 @@ SET(HAVE_RPI_API OFF CACHE BOOL "raspberry pi not supported") SET(HAVE_TDA995X_API OFF CACHE BOOL "tda995x not supported") SET(HAVE_EXYNOS_API OFF CACHE BOOL "exynos not supported") SET(HAVE_AOCEC_API OFF CACHE BOOL "aocec not supported") +SET(HAVE_RAINSHADOW_API OFF CACHE BOOL "RainShadow not supported") # Pulse-Eight devices are always supported set(HAVE_P8_USB ON CACHE BOOL "p8 usb-cec supported" FORCE) set(HAVE_P8_USB_DETECT OFF CACHE BOOL "p8 usb-cec detection not supported") @@ -148,6 +150,18 @@ else() else() set(HAVE_AOCEC_API 0) endif() + + # RainShadow + if (${HAVE_RAINSHADOW_API}) + set(LIB_INFO "${LIB_INFO}, RainShadow") + SET(HAVE_RAINSHADOW_API ON CACHE BOOL "RainShadow supported" FORCE) + set(CEC_SOURCES_ADAPTER_RAINSHADOW adapter/RainShadow/RainAdapterDetection.cpp + adapter/RainShadow/RainAdapterCommunication.cpp) + source_group("Source Files\\adapter\\RainShadow" FILES ${CEC_SOURCES_ADAPTER_RAINSHADOW}) + list(APPEND CEC_SOURCES ${CEC_SOURCES_ADAPTER_RAINSHADOW}) + else() + set(HAVE_RAINSHADOW_API 0) + endif() endif() # rt diff --git a/src/libcec/cmake/DisplayPlatformSupport.cmake b/src/libcec/cmake/DisplayPlatformSupport.cmake index 7ec10f5d..16665ddb 100644 --- a/src/libcec/cmake/DisplayPlatformSupport.cmake +++ b/src/libcec/cmake/DisplayPlatformSupport.cmake @@ -45,9 +45,15 @@ else() endif() if (HAVE_AOCEC_API) - message(STATUS "AOCEC support: yes") + message(STATUS "AOCEC support: yes") else() - message(STATUS "AOCEC support: no") + message(STATUS "AOCEC support: no") +endif() + +if (HAVE_RAINSHADOW_API) + message(STATUS "RainShadow support: yes") +else() + message(STATUS "RainShadow support: no") endif() if (HAVE_PYTHON) diff --git a/src/libcec/env.h.in b/src/libcec/env.h.in index fe6c83d3..d3e89eac 100644 --- a/src/libcec/env.h.in +++ b/src/libcec/env.h.in @@ -75,6 +75,9 @@ /* Define to 1 for AOCEC support */ #cmakedefine HAVE_AOCEC_API @HAVE_AOCEC_API@ +/* Define to 1 for RainShadow support */ +#cmakedefine HAVE_RAINSHADOW_API @HAVE_RAINSHADOW_API@ + /* Define to 1 for nVidia EDID parsing support (on selected models) */ #cmakedefine HAVE_NVIDIA_EDID_PARSER @HAVE_NVIDIA_EDID_PARSER@ From b011888308a5e084bbd9a62fabae3a5f91769370 Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Sat, 14 Jan 2017 00:40:21 +0100 Subject: [PATCH 02/10] set adapter phy addr and conf bits --- .../RainShadow/RainAdapterCommunication.cpp | 50 +++++++++++++++++++ .../RainShadow/RainAdapterCommunication.h | 14 ++++++ 2 files changed, 64 insertions(+) diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp index ce55a6a3..62e94e6e 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp @@ -150,6 +150,12 @@ bool CRainAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNEC if (!bConnectionOpened || !bStartListening) StopThread(0); + lock.Unlock(); + + SetAdapterPhysicalAddress(); + + SetAdapterConfigurationBits(); + return bConnectionOpened; } @@ -213,6 +219,50 @@ int CRainAdapterCommunication::hex2bin(uint8_t *dst, const char *src, size_t cou return 0; } +bool CRainAdapterCommunication::SetAdapterPhysicalAddress() +{ + char command[DATA_SIZE]; + + CLockObject lock(m_mutex); + + if (!IsOpen()) + return false; + + snprintf(command, sizeof(command), "!P %04x~", GetPhysicalAddress()); + + if (m_port->Write(command, strlen(command)) != (ssize_t) strlen(command)) + { + return false; + } + + m_condition.Wait(m_mutex, m_gotResponse); + m_gotResponse = false; + + return !strncmp(m_response, "PHY", 3); +} + +bool CRainAdapterCommunication::SetAdapterConfigurationBits() +{ + char command[DATA_SIZE]; + uint16_t adapterConfigurationBits = 5; // Higher level functions and Host wakeup + CLockObject lock(m_mutex); + + if (!IsOpen()) + return false; + + snprintf(command, sizeof(command), "!C %04x~", adapterConfigurationBits); + + if (m_port->Write(command, strlen(command)) != (ssize_t) strlen(command)) + { + return false; + } + + m_condition.Wait(m_mutex, m_gotResponse); + m_gotResponse = false; + + return !strncmp(m_response, "CFG", 3); +} + std::string CRainAdapterCommunication::GetError(void) const { return m_port->GetError(); diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h index 35542b47..0202be72 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h @@ -124,6 +124,20 @@ namespace CEC */ int hex2bin(uint8_t *dst, const char *src, size_t count); + /** + * SetAdapterPhysicalAddress - get the physical address from the system and report it to the adapter + * + * Return true on success, else false. + */ + bool SetAdapterPhysicalAddress(void); + + /** + * SetAdapterConfigrationBits - set the adapter configuration bits + * + * Return true on success, else false. + */ + bool SetAdapterConfigurationBits(void); + P8PLATFORM::ISocket * m_port; /**< the com port connection */ std::string m_strError; /**< current error message */ char m_response[DATA_SIZE]; /**< current response from adapter */ From f39b753d66f739e2f4654cff167f4dee9d58b53c Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Sat, 14 Jan 2017 22:35:31 +0100 Subject: [PATCH 03/10] usage of higher level api --- .../RainShadow/RainAdapterCommunication.cpp | 95 ++++++++++++++----- .../RainShadow/RainAdapterCommunication.h | 25 +++++ 2 files changed, 98 insertions(+), 22 deletions(-) diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp index 62e94e6e..1d1ca8c5 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp @@ -57,8 +57,8 @@ CRainAdapterCommunication::CRainAdapterCommunication(IAdapterCommunicationCallba IAdapterCommunication(callback), m_port(NULL), m_gotResponse(false), - m_bLogicalAddressChanged(false) - + m_bLogicalAddressChanged(false), + m_osdNameRequestState(OSD_NAME_REQUEST_NEEDED) { CLockObject lock(m_mutex); m_logicalAddresses.Clear(); @@ -152,7 +152,7 @@ bool CRainAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNEC lock.Unlock(); - SetAdapterPhysicalAddress(); + while(!SetAdapterPhysicalAddress()); SetAdapterConfigurationBits(); @@ -230,15 +230,7 @@ bool CRainAdapterCommunication::SetAdapterPhysicalAddress() snprintf(command, sizeof(command), "!P %04x~", GetPhysicalAddress()); - if (m_port->Write(command, strlen(command)) != (ssize_t) strlen(command)) - { - return false; - } - - m_condition.Wait(m_mutex, m_gotResponse); - m_gotResponse = false; - - return !strncmp(m_response, "PHY", 3); + return WriteAdapterCommand(command, "PHY"); } bool CRainAdapterCommunication::SetAdapterConfigurationBits() @@ -252,6 +244,37 @@ bool CRainAdapterCommunication::SetAdapterConfigurationBits() snprintf(command, sizeof(command), "!C %04x~", adapterConfigurationBits); + return WriteAdapterCommand(command, "CFG"); +} + +bool CRainAdapterCommunication::SetAdapterOsdName(const cec_datapacket &packet) +{ + char command[DATA_SIZE] = "!O"; + char *cmd_ptr = command + strlen(command); + + CLockObject lock(m_mutex); + + if (!IsOpen()) + return false; + + for (int i = 0; i < packet.size; ++i) + { + *cmd_ptr++ = packet.At(i); + } + + *cmd_ptr++ = '~'; + *cmd_ptr++ = '\0'; + + return WriteAdapterCommand(command, "OSD"); +} + +bool CRainAdapterCommunication::WriteAdapterCommand(char *command, + const char *response) +{ + bool ret; + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - Write command to adapter: %s", __FUNCTION__, command); + if (m_port->Write(command, strlen(command)) != (ssize_t) strlen(command)) { return false; @@ -260,7 +283,12 @@ bool CRainAdapterCommunication::SetAdapterConfigurationBits() m_condition.Wait(m_mutex, m_gotResponse); m_gotResponse = false; - return !strncmp(m_response, "CFG", 3); + ret = !strncmp(m_response, response, strlen(response)); + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - Response for command %s %s received", __FUNCTION__, command, ret ? "": "not "); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - Response was %s", __FUNCTION__, m_response); + + return ret; } std::string CRainAdapterCommunication::GetError(void) const @@ -282,6 +310,17 @@ cec_adapter_message_state CRainAdapterCommunication::Write( { snprintf(buffer, sizeof(buffer), "!X%x~", data.destination); } + else if (m_osdNameRequestState == OSD_NAME_REQUEST_SENT + && data.initiator == m_logicalAddresses.primary + && data.destination == CECDEVICE_BROADCAST + && data.opcode == CEC_OPCODE_SET_OSD_NAME) + { + SetAdapterOsdName(data.parameters); + + m_osdNameRequestState = OSD_NAME_REQUEST_DONE; + + return ADAPTER_MESSAGE_STATE_SENT_ACKED; + } else { char hex[4]; @@ -296,11 +335,15 @@ cec_adapter_message_state CRainAdapterCommunication::Write( buffer[strlen(buffer) - 1] = '~'; } + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - Write message to adapter: %s", __FUNCTION__, buffer); + if (m_port->Write(buffer, strlen(buffer)) == (ssize_t) strlen(buffer)) { m_condition.Wait(m_mutex, m_gotResponse); m_gotResponse = false; + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - got response %s for message %s", __FUNCTION__, m_response, buffer); + if (!strncmp(m_response, "STA", 3) && m_response[strlen(m_response) - 1] == '1') { return ADAPTER_MESSAGE_STATE_SENT_ACKED; @@ -388,15 +431,7 @@ bool CRainAdapterCommunication::InternalSetLogicalAddresses(const unsigned int l snprintf(command, sizeof(command), "!A %x~", log_addr); - if (m_port->Write(command, strlen(command)) != (ssize_t) strlen(command)) - { - return false; - } - - m_condition.Wait(m_mutex, m_gotResponse); - m_gotResponse = false; - - return !strncmp(m_response, "ADR", 3); + return WriteAdapterCommand(command, "ADR"); } bool CRainAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses) @@ -456,7 +491,9 @@ void CRainAdapterCommunication::ProcessMessage(char *buffer) cmd.parameters.PushBack(msg[i]); if (!IsStopped()) + { m_callback->OnCommandReceived(cmd); + } } } @@ -472,6 +509,20 @@ void *CRainAdapterCommunication::Process(void) while (!IsStopped()) { + if (m_osdNameRequestState == OSD_NAME_REQUEST_NEEDED + && m_logicalAddresses.primary != CECDEVICE_BROADCAST) + { + cec_command cmd; + + cec_command::Format(cmd, CECDEVICE_BROADCAST, m_logicalAddresses.primary, + CEC_OPCODE_GIVE_OSD_NAME); + + m_callback->OnCommandReceived(cmd); + m_osdNameRequestState = OSD_NAME_REQUEST_SENT; + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - osd name request sent", __FUNCTION__); + } + do { /* retry Read() if it was interrupted */ diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h index 0202be72..1c27df69 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h @@ -49,8 +49,15 @@ #define DATA_SIZE 256 #define CEC_RAINSHADOW_SERIAL_DEFAULT_BAUDRATE 115200L +typedef enum { + OSD_NAME_REQUEST_NEEDED, + OSD_NAME_REQUEST_SENT, + OSD_NAME_REQUEST_DONE, +} tOsdNameRequestState; + namespace CEC { + class CRainAdapterCommunication : public IAdapterCommunication, public P8PLATFORM::CThread { public: @@ -138,6 +145,23 @@ namespace CEC */ bool SetAdapterConfigurationBits(void); + /** + * SetAdapterOsdName - set the adapter OSD name + * @packet: packet that contains the OSD name + * + * Return true on success, else false. + */ + bool SetAdapterOsdName(const cec_datapacket &packet); + + /** + * WriteAdapterCommand - writes the adapter command and waits for the response + * @command: the command to write + * @response: the response to wait for + * + * Return true on success, else false. + */ + bool WriteAdapterCommand(char *command, const char *response); + P8PLATFORM::ISocket * m_port; /**< the com port connection */ std::string m_strError; /**< current error message */ char m_response[DATA_SIZE]; /**< current response from adapter */ @@ -146,6 +170,7 @@ namespace CEC bool m_gotResponse; bool m_bLogicalAddressChanged; cec_logical_addresses m_logicalAddresses; + tOsdNameRequestState m_osdNameRequestState; }; }; #endif From fb41cf620f0355036415a5b3a964fda0c50af007 Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Sun, 15 Jan 2017 12:59:40 +0100 Subject: [PATCH 04/10] don't try endless to send the physical address --- .../adapter/RainShadow/RainAdapterCommunication.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp index 1d1ca8c5..9d32cc5c 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp @@ -52,6 +52,7 @@ using namespace CEC; using namespace P8PLATFORM; #define LIB_CEC m_callback->GetLib() +#define MAX_TRY_COUNT 5 CRainAdapterCommunication::CRainAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint32_t iBaudRate /* = CEC_RAINSHADOW_SERIAL_DEFAULT_BAUDRATE */) : IAdapterCommunication(callback), @@ -152,7 +153,15 @@ bool CRainAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNEC lock.Unlock(); - while(!SetAdapterPhysicalAddress()); + for(int tryCount = 0; !SetAdapterPhysicalAddress(); ++tryCount) + { + if (tryCount > MAX_TRY_COUNT) + { + StopThread(0); + LIB_CEC->AddLog(CEC_LOG_ERROR, "got %d times the wrong response, no rainshadow adapter? Open failed"); + return false; + } + } SetAdapterConfigurationBits(); From e6cbc5f641b347df1ab674bc48c08236d52511a4 Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Sun, 15 Jan 2017 14:13:31 +0100 Subject: [PATCH 05/10] set the device type if available --- .../RainShadow/RainAdapterCommunication.cpp | 55 +++++++++++++++++-- .../RainShadow/RainAdapterCommunication.h | 7 +++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp index 9d32cc5c..a1d0a464 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp @@ -228,6 +228,39 @@ int CRainAdapterCommunication::hex2bin(uint8_t *dst, const char *src, size_t cou return 0; } +int8_t CRainAdapterCommunication::GetDeviceType(cec_logical_address) +{ + switch (m_logicalAddresses.primary) + { + case CECDEVICE_TV: + return (int8_t)CEC_DEVICE_TYPE_TV; + case CECDEVICE_RECORDINGDEVICE1: + case CECDEVICE_RECORDINGDEVICE2: + case CECDEVICE_RECORDINGDEVICE3: + return (int8_t)CEC_DEVICE_TYPE_RECORDING_DEVICE; + case CECDEVICE_TUNER1: + case CECDEVICE_TUNER2: + case CECDEVICE_TUNER3: + case CECDEVICE_TUNER4: + return (int8_t)CEC_DEVICE_TYPE_TUNER; + case CECDEVICE_PLAYBACKDEVICE1: + case CECDEVICE_PLAYBACKDEVICE2: + case CECDEVICE_PLAYBACKDEVICE3: + return (int8_t)CEC_DEVICE_TYPE_PLAYBACK_DEVICE; + case CECDEVICE_AUDIOSYSTEM: + return (int8_t)CEC_DEVICE_TYPE_AUDIO_SYSTEM; + case CECDEVICE_RESERVED1: + case CECDEVICE_RESERVED2: + return (int8_t)CEC_DEVICE_TYPE_RESERVED; + case CECDEVICE_FREEUSE: + return (int8_t)CEC_DEVICE_TYPE_TV; + default: + // do nothing + break; + } + return -1; +} + bool CRainAdapterCommunication::SetAdapterPhysicalAddress() { char command[DATA_SIZE]; @@ -237,7 +270,17 @@ bool CRainAdapterCommunication::SetAdapterPhysicalAddress() if (!IsOpen()) return false; - snprintf(command, sizeof(command), "!P %04x~", GetPhysicalAddress()); + int8_t type = GetDeviceType(m_logicalAddresses.primary); + + if (type >= 0) + { + snprintf(command, sizeof(command), "!P %04x %x~", GetPhysicalAddress(), + type); + } + else + { + snprintf(command, sizeof(command), "!P %04x~", GetPhysicalAddress()); + } return WriteAdapterCommand(command, "PHY"); } @@ -450,14 +493,18 @@ bool CRainAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses m_logicalAddresses = addresses; m_bLogicalAddressChanged = true; - return true; + return SetAdapterPhysicalAddress(); } return false; } -void CRainAdapterCommunication::HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)) +void CRainAdapterCommunication::HandleLogicalAddressLost( + cec_logical_address UNUSED(oldAddress)) { - InternalSetLogicalAddresses(CECDEVICE_BROADCAST); + if (InternalSetLogicalAddresses(CECDEVICE_BROADCAST)) + { + SetAdapterPhysicalAddress(); + } } void CRainAdapterCommunication::ProcessMessage(char *buffer) diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h index 1c27df69..46b8b574 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h @@ -162,6 +162,13 @@ namespace CEC */ bool WriteAdapterCommand(char *command, const char *response); + /** + * GetDeviceType - translates logical address to device type + * + * Return device type + */ + int8_t GetDeviceType(cec_logical_address); + P8PLATFORM::ISocket * m_port; /**< the com port connection */ std::string m_strError; /**< current error message */ char m_response[DATA_SIZE]; /**< current response from adapter */ From cf9c559a531deb5cdca0881e19ffe5cf75a70eff Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Fri, 20 Jan 2017 17:40:14 +0100 Subject: [PATCH 06/10] fixed adapterType --- src/libcec/adapter/RainShadow/RainAdapterDetection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp b/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp index 50d2c81e..615b2566 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp @@ -384,7 +384,7 @@ uint8_t CRainAdapterDetection::FindAdaptersUdev(cec_adapter_descriptor *deviceLi snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", strComm.c_str()); deviceList[iFound].iVendorId = iVendor; deviceList[iFound].iProductId = iProduct; - deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type + deviceList[iFound].adapterType = ADAPTERTYPE_RAINSHADOW; // will be overridden when not doing a "quick scan" by the actual type iFound++; } } From 6870c0cfe8d62169a54c1c6eed4174143d5f4546 Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Fri, 20 Jan 2017 20:47:29 +0100 Subject: [PATCH 07/10] allow rainshadow parallel to pulse8 --- src/libcec/adapter/AdapterFactory.cpp | 5 +++-- .../adapter/RainShadow/RainAdapterDetection.cpp | 15 +++++++++++++++ .../adapter/RainShadow/RainAdapterDetection.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libcec/adapter/AdapterFactory.cpp b/src/libcec/adapter/AdapterFactory.cpp index a7ba2d3c..fb43f980 100644 --- a/src/libcec/adapter/AdapterFactory.cpp +++ b/src/libcec/adapter/AdapterFactory.cpp @@ -155,7 +155,7 @@ int8_t CAdapterFactory::DetectAdapters(cec_adapter_descriptor *deviceList, uint8 m_lib->AddLog(CEC_LOG_WARNING, "libCEC has not been compiled with detection code for the RAINSHADOW USB-CEC Adapter, so the path to the COM port has to be provided to libCEC if this adapter is being used"); } else - iAdaptersFound += CRainAdapterDetection::FindAdapters(deviceList, iBufSize, strDevicePath); + iAdaptersFound += CRainAdapterDetection::FindAdapters(&deviceList[iAdaptersFound], iBufSize, strDevicePath); #else m_lib->AddLog(CEC_LOG_WARNING, "libCEC has not been compiled with support for the RainShadow USB-CEC Adapter"); #endif @@ -185,7 +185,8 @@ IAdapterCommunication *CAdapterFactory::GetInstance(const char *strPort, uint16_ #endif #if defined(HAVE_RAINSHADOW_API) - return new CRainAdapterCommunication(m_lib->m_cec, strPort, iBaudRate); + if (CRainAdapterDetection::isItMe(strPort)) + return new CRainAdapterCommunication(m_lib->m_cec, strPort, iBaudRate); #endif #if defined(HAVE_RPI_API) diff --git a/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp b/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp index 615b2566..56bbad78 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterDetection.cpp @@ -133,6 +133,21 @@ bool CRainAdapterDetection::CanAutodetect(void) #endif } +bool CRainAdapterDetection::isItMe(const char *strDevicePath) +{ + cec_adapter_descriptor deviceList[10]; + int iFound; + + iFound = FindAdapters(deviceList, sizeof(deviceList) / sizeof(deviceList[0]), NULL); + + for (int i = 0; i < iFound; ++i) + { + if (!strcmp(deviceList[i].strComName, strDevicePath)) + return true; + } + return false; +} + #if defined(__WINDOWS__) static bool GetComPortFromDevNode(DEVINST hDevInst, char* strPortName, unsigned int iSize) { diff --git a/src/libcec/adapter/RainShadow/RainAdapterDetection.h b/src/libcec/adapter/RainShadow/RainAdapterDetection.h index 403c9666..44c6469a 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterDetection.h +++ b/src/libcec/adapter/RainShadow/RainAdapterDetection.h @@ -46,6 +46,7 @@ namespace CEC public: static uint8_t FindAdapters(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); static bool CanAutodetect(void); + static bool isItMe(const char *strDevicePath); private: static uint8_t FindAdaptersWindows(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); From 21c3998f62b1871fd48f9ee3bcb664ead410b6dc Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Fri, 20 Jan 2017 20:59:43 +0100 Subject: [PATCH 08/10] removed settings of configuration bits --- .../RainShadow/RainAdapterCommunication.cpp | 16 ---------------- .../RainShadow/RainAdapterCommunication.h | 7 ------- 2 files changed, 23 deletions(-) diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp index a1d0a464..0fb9a4a9 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp @@ -163,8 +163,6 @@ bool CRainAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNEC } } - SetAdapterConfigurationBits(); - return bConnectionOpened; } @@ -285,20 +283,6 @@ bool CRainAdapterCommunication::SetAdapterPhysicalAddress() return WriteAdapterCommand(command, "PHY"); } -bool CRainAdapterCommunication::SetAdapterConfigurationBits() -{ - char command[DATA_SIZE]; - uint16_t adapterConfigurationBits = 5; // Higher level functions and Host wakeup - CLockObject lock(m_mutex); - - if (!IsOpen()) - return false; - - snprintf(command, sizeof(command), "!C %04x~", adapterConfigurationBits); - - return WriteAdapterCommand(command, "CFG"); -} - bool CRainAdapterCommunication::SetAdapterOsdName(const cec_datapacket &packet) { char command[DATA_SIZE] = "!O"; diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h index 46b8b574..e1a92deb 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h @@ -138,13 +138,6 @@ namespace CEC */ bool SetAdapterPhysicalAddress(void); - /** - * SetAdapterConfigrationBits - set the adapter configuration bits - * - * Return true on success, else false. - */ - bool SetAdapterConfigurationBits(void); - /** * SetAdapterOsdName - set the adapter OSD name * @packet: packet that contains the OSD name From e38ce305592c8e90572a93ae28d7d56ef9bdd3a6 Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Fri, 20 Jan 2017 23:56:40 +0100 Subject: [PATCH 09/10] removed setting of osd name --- .../RainShadow/RainAdapterCommunication.cpp | 59 +------------------ .../RainShadow/RainAdapterCommunication.h | 15 ----- 2 files changed, 1 insertion(+), 73 deletions(-) diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp index 0fb9a4a9..c23b0523 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp @@ -58,8 +58,7 @@ CRainAdapterCommunication::CRainAdapterCommunication(IAdapterCommunicationCallba IAdapterCommunication(callback), m_port(NULL), m_gotResponse(false), - m_bLogicalAddressChanged(false), - m_osdNameRequestState(OSD_NAME_REQUEST_NEEDED) + m_bLogicalAddressChanged(false) { CLockObject lock(m_mutex); m_logicalAddresses.Clear(); @@ -153,16 +152,6 @@ bool CRainAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNEC lock.Unlock(); - for(int tryCount = 0; !SetAdapterPhysicalAddress(); ++tryCount) - { - if (tryCount > MAX_TRY_COUNT) - { - StopThread(0); - LIB_CEC->AddLog(CEC_LOG_ERROR, "got %d times the wrong response, no rainshadow adapter? Open failed"); - return false; - } - } - return bConnectionOpened; } @@ -283,27 +272,6 @@ bool CRainAdapterCommunication::SetAdapterPhysicalAddress() return WriteAdapterCommand(command, "PHY"); } -bool CRainAdapterCommunication::SetAdapterOsdName(const cec_datapacket &packet) -{ - char command[DATA_SIZE] = "!O"; - char *cmd_ptr = command + strlen(command); - - CLockObject lock(m_mutex); - - if (!IsOpen()) - return false; - - for (int i = 0; i < packet.size; ++i) - { - *cmd_ptr++ = packet.At(i); - } - - *cmd_ptr++ = '~'; - *cmd_ptr++ = '\0'; - - return WriteAdapterCommand(command, "OSD"); -} - bool CRainAdapterCommunication::WriteAdapterCommand(char *command, const char *response) { @@ -346,17 +314,6 @@ cec_adapter_message_state CRainAdapterCommunication::Write( { snprintf(buffer, sizeof(buffer), "!X%x~", data.destination); } - else if (m_osdNameRequestState == OSD_NAME_REQUEST_SENT - && data.initiator == m_logicalAddresses.primary - && data.destination == CECDEVICE_BROADCAST - && data.opcode == CEC_OPCODE_SET_OSD_NAME) - { - SetAdapterOsdName(data.parameters); - - m_osdNameRequestState = OSD_NAME_REQUEST_DONE; - - return ADAPTER_MESSAGE_STATE_SENT_ACKED; - } else { char hex[4]; @@ -549,20 +506,6 @@ void *CRainAdapterCommunication::Process(void) while (!IsStopped()) { - if (m_osdNameRequestState == OSD_NAME_REQUEST_NEEDED - && m_logicalAddresses.primary != CECDEVICE_BROADCAST) - { - cec_command cmd; - - cec_command::Format(cmd, CECDEVICE_BROADCAST, m_logicalAddresses.primary, - CEC_OPCODE_GIVE_OSD_NAME); - - m_callback->OnCommandReceived(cmd); - m_osdNameRequestState = OSD_NAME_REQUEST_SENT; - - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - osd name request sent", __FUNCTION__); - } - do { /* retry Read() if it was interrupted */ diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h index e1a92deb..b3b174d9 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.h +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.h @@ -49,12 +49,6 @@ #define DATA_SIZE 256 #define CEC_RAINSHADOW_SERIAL_DEFAULT_BAUDRATE 115200L -typedef enum { - OSD_NAME_REQUEST_NEEDED, - OSD_NAME_REQUEST_SENT, - OSD_NAME_REQUEST_DONE, -} tOsdNameRequestState; - namespace CEC { @@ -138,14 +132,6 @@ namespace CEC */ bool SetAdapterPhysicalAddress(void); - /** - * SetAdapterOsdName - set the adapter OSD name - * @packet: packet that contains the OSD name - * - * Return true on success, else false. - */ - bool SetAdapterOsdName(const cec_datapacket &packet); - /** * WriteAdapterCommand - writes the adapter command and waits for the response * @command: the command to write @@ -170,7 +156,6 @@ namespace CEC bool m_gotResponse; bool m_bLogicalAddressChanged; cec_logical_addresses m_logicalAddresses; - tOsdNameRequestState m_osdNameRequestState; }; }; #endif From fa9cbbf03b10ff64d00a1027522bcbc4cd578c35 Mon Sep 17 00:00:00 2001 From: Gerald Dachs Date: Thu, 26 Jan 2017 08:15:38 +0100 Subject: [PATCH 10/10] bug fix --- src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp index c23b0523..a1b0eb72 100644 --- a/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp +++ b/src/libcec/adapter/RainShadow/RainAdapterCommunication.cpp @@ -279,13 +279,14 @@ bool CRainAdapterCommunication::WriteAdapterCommand(char *command, LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - Write command to adapter: %s", __FUNCTION__, command); + m_gotResponse = false; + if (m_port->Write(command, strlen(command)) != (ssize_t) strlen(command)) { return false; } m_condition.Wait(m_mutex, m_gotResponse); - m_gotResponse = false; ret = !strncmp(m_response, response, strlen(response)); @@ -330,10 +331,11 @@ cec_adapter_message_state CRainAdapterCommunication::Write( LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - Write message to adapter: %s", __FUNCTION__, buffer); + m_gotResponse = false; + if (m_port->Write(buffer, strlen(buffer)) == (ssize_t) strlen(buffer)) { m_condition.Wait(m_mutex, m_gotResponse); - m_gotResponse = false; LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - got response %s for message %s", __FUNCTION__, m_response, buffer);