From: Lars Op den Kamp Date: Mon, 3 Oct 2011 22:54:29 +0000 (+0200) Subject: Merge branch 'master' into release X-Git-Tag: upstream/2.2.0~1^2~118^2~13 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=93f86187ea362ab62f9e0f93b77fb3bcc29166ab;hp=175d5c4f566f7e978aeb137f8721c5ec5fa90fd3;p=deb_libcec.git Merge branch 'master' into release * bumped interface version to 4 * timeout parameter removed from Close()/cec_close(). return type changed to void * added cec_destroy() method * removed timeout parameter from Transmit() * change the default argument of PowerOnDevices() to CECDEVICE_TV * removed PowerOffDevices(). use StandbyDevices() instead * removed obsolete methods from the interface * fixed bug: pthread_cond_wait was called without the mutex locked * fixed possible deadlock: don't send messages and wait for an ack with the mutex locked in CCECParser * created a separate reader thread and fixed the 'lock timeout' bug * testclient: use CECDEVICE_TV instead of the default argument (CECDEVICE_BROADCAST) for PowerOnDevices() and PowerOffDevices() --- diff --git a/ChangeLog b/ChangeLog index 3a893f2..10c85f1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +libcec (0.4-1) unstable; urgency=low + + * bumped interface version to 4 + * timeout parameter removed from Close()/cec_close(). return type changed to + void + * added cec_destroy() method + * removed timeout parameter from Transmit() + * change the default argument of PowerOnDevices() to CECDEVICE_TV + * removed PowerOffDevices(). use StandbyDevices() instead + * removed obsolete methods from the interface + * fixed bug: pthread_cond_wait was called without the mutex locked + * fixed possible deadlock: don't send messages and wait for an ack with the + mutex locked in CCECParser + * created a separate reader thread and fixed the 'lock timeout' bug + * testclient: use CECDEVICE_TV instead of the default argument + (CECDEVICE_BROADCAST) for PowerOnDevices() and PowerOffDevices() + + -- Pulse-Eight Packaging Tue, 04 Oct 2011 00:48:00 +0200 + libcec (0.3-1) unstable; urgency=low * added device detection support for Windows diff --git a/configure.ac b/configure.ac index 883d414..bd97334 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([libcec], 0:2:0) +AC_INIT([libcec], 0:4:0) AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) AC_PROG_CXX diff --git a/debian/changelog b/debian/changelog index 3a893f2..10c85f1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,22 @@ +libcec (0.4-1) unstable; urgency=low + + * bumped interface version to 4 + * timeout parameter removed from Close()/cec_close(). return type changed to + void + * added cec_destroy() method + * removed timeout parameter from Transmit() + * change the default argument of PowerOnDevices() to CECDEVICE_TV + * removed PowerOffDevices(). use StandbyDevices() instead + * removed obsolete methods from the interface + * fixed bug: pthread_cond_wait was called without the mutex locked + * fixed possible deadlock: don't send messages and wait for an ack with the + mutex locked in CCECParser + * created a separate reader thread and fixed the 'lock timeout' bug + * testclient: use CECDEVICE_TV instead of the default argument + (CECDEVICE_BROADCAST) for PowerOnDevices() and PowerOffDevices() + + -- Pulse-Eight Packaging Tue, 04 Oct 2011 00:48:00 +0200 + libcec (0.3-1) unstable; urgency=low * added device detection support for Windows diff --git a/include/CECExports.h b/include/CECExports.h index 0aef25c..9a5c095 100644 --- a/include/CECExports.h +++ b/include/CECExports.h @@ -55,9 +55,10 @@ extern "C" { namespace CEC { #endif - #define CEC_MIN_VERSION 2 - #define CEC_LIB_VERSION 2 + #define CEC_MIN_VERSION 4 + #define CEC_LIB_VERSION 4 #define CEC_SETTLE_DOWN_TIME 1000 + #define CEC_BUTTON_TIMEOUT 500 typedef std::vector cec_frame; @@ -253,11 +254,11 @@ namespace CEC { unsigned int duration; } cec_keypress; - typedef struct cec_device + typedef struct cec_adapter { std::string path; std::string comm; - } cec_device; + } cec_adapter; typedef struct cec_command { diff --git a/include/CECExportsC.h b/include/CECExportsC.h index 8d91fe2..61cabd2 100644 --- a/include/CECExportsC.h +++ b/include/CECExportsC.h @@ -45,13 +45,17 @@ extern "C" { * @param iPhysicalAddress The physical address of this device. 0x1000 by default. * @return True when initialised, false otherwise. */ - #ifdef __cplusplus extern DECLSPEC bool cec_init(const char *strDeviceName, CEC::cec_logical_address iLogicalAddress = CEC::CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS); #else extern DECLSPEC bool cec_init(const char *strDeviceName, cec_logical_address iLogicalAddress = CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS); #endif +/*! + * @brief Unload the CEC adapter library. + */ +extern DECLSPEC void cec_destroy(void); + /*! * @brief Open a connection to the CEC adapter. * @param strPort The path to the port. @@ -62,15 +66,26 @@ extern DECLSPEC bool cec_open(const char *strPort, int iTimeout); /*! * @brief Close the connection to the CEC adapter. - * @param iTimeout Timeout in ms */ -extern DECLSPEC bool cec_close(int iTimeout); +extern DECLSPEC void cec_close(void); + +/*! + * @brief Try to find all connected CEC adapters. Only implemented on Linux at the moment. + * @param deviceList The vector to store device descriptors in. + * @param strDevicePath Optional device path. Only adds device descriptors that match the given device path. + * @return The number of devices that were found, or -1 when an error occured. + */ +#ifdef __cplusplus +extern DECLSPEC int cec_find_adapters(std::vector &deviceList, const char *strDevicePath = NULL); +#else +extern DECLSPEC int cec_find_adapters(std::vector &deviceList, const char *strDevicePath = NULL); +#endif /*! * @brief Ping the CEC adapter. * @return True when the ping was succesful, false otherwise. */ -extern DECLSPEC bool cec_ping(void); +extern DECLSPEC bool cec_ping_adapters(void); /*! * @brief Start the bootloader of the CEC adapter. @@ -79,15 +94,14 @@ extern DECLSPEC bool cec_ping(void); extern DECLSPEC bool cec_start_bootloader(void); /*! - * @brief Power off connected CEC capable devices. - * @param address The logical address to power off. - * @return True when the command was sent succesfully, false otherwise. + * @return Get the minimal version of libcec that this version of libcec can interface with. */ -#ifdef __cplusplus -extern DECLSPEC bool cec_power_off_devices(CEC::cec_logical_address address = CEC::CECDEVICE_BROADCAST); -#else -extern DECLSPEC bool cec_power_off_devices(cec_logical_address address = CECDEVICE_BROADCAST); -#endif +extern DECLSPEC int cec_get_min_version(void); + +/*! + * @return Get the version of libcec. + */ +extern DECLSPEC int cec_get_lib_version(void); /*! * @brief Power on the connected CEC capable devices. @@ -95,9 +109,9 @@ extern DECLSPEC bool cec_power_off_devices(cec_logical_address address = CECDEVI * @return True when the command was sent succesfully, false otherwise. */ #ifdef __cplusplus -extern DECLSPEC bool cec_power_on_devices(CEC::cec_logical_address address = CEC::CECDEVICE_BROADCAST); +extern DECLSPEC bool cec_power_on_devices(CEC::cec_logical_address address = CEC::CECDEVICE_TV); #else -extern DECLSPEC bool cec_power_on_devices(cec_logical_address address = CECDEVICE_BROADCAST); +extern DECLSPEC bool cec_power_on_devices(cec_logical_address address = CECDEVICE_TV); #endif /*! @@ -160,13 +174,12 @@ extern DECLSPEC bool cec_get_next_command(cec_command *command); * @brief Transmit a frame on the CEC line. * @param data The frame to send. * @param bWaitForAck Wait for an ACK message for 1 second after this frame has been sent. - * @param iTimeout Timeout if the message could not be sent for this amount of ms. Does not influence the timeout of the wait for the ACK message. That timeout is specified by the CEC standard. * @return True when the data was sent and acked, false otherwise. */ #ifdef __cplusplus -extern DECLSPEC bool cec_transmit(const CEC::cec_frame &data, bool bWaitForAck = true, int64_t iTimeout = (int64_t) 5000); +extern DECLSPEC bool cec_transmit(const CEC::cec_frame &data, bool bWaitForAck = true); #else -extern DECLSPEC bool cec_transmit(const cec_frame &data, bool bWaitForAck = true, int64_t iTimeout = (int64_t) 5000); +extern DECLSPEC bool cec_transmit(const cec_frame &data, bool bWaitForAck = true); #endif /*! @@ -180,36 +193,6 @@ extern DECLSPEC bool cec_set_logical_address(CEC::cec_logical_address iLogicalAd extern DECLSPEC bool cec_set_logical_address(cec_logical_address myAddress, cec_logical_address targetAddress); #endif -/*! - * @deprecated Use cec_set_logical_address() instead. - * @brief Set the ack mask of the CEC adapter. - * @param iMask The cec adapter's ack mask. - * @return True when the ack mask was sent succesfully, false otherwise. - */ -extern DECLSPEC bool cec_set_ack_mask(uint16_t iMask); - -/*! - * @return Get the minimal version of libcec that this version of libcec can interface with. - */ -extern DECLSPEC int cec_get_min_version(void); - -/*! - * @return Get the version of libcec. - */ -extern DECLSPEC int cec_get_lib_version(void); - -/*! - * @brief Try to find all connected CEC adapters. Only implemented on Linux at the moment. - * @param deviceList The vector to store device descriptors in. - * @param strDevicePath Optional device path. Only adds device descriptors that match the given device path. - * @return The number of devices that were found, or -1 when an error occured. - */ -#ifdef __cplusplus -extern DECLSPEC int cec_find_devices(std::vector &deviceList, const char *strDevicePath = NULL); -#else -extern DECLSPEC int cec_find_devices(std::vector &deviceList, const char *strDevicePath = NULL); -#endif - #ifdef __cplusplus }; #endif diff --git a/include/CECExportsCpp.h b/include/CECExportsCpp.h index ac00190..54dbcbf 100644 --- a/include/CECExportsCpp.h +++ b/include/CECExportsCpp.h @@ -33,9 +33,11 @@ namespace CEC { - class ICECDevice + class ICECAdapter { public: + /*! @name Adapter methods */ + //@{ /*! * @see cec_open */ @@ -44,47 +46,33 @@ namespace CEC /*! * @see cec_close */ - virtual bool Close(int iTimeoutMs = 2000) = 0; + virtual void Close(void) = 0; /*! - * @see cec_find_devices + * @see cec_find_adapters */ - virtual int FindDevices(std::vector &deviceList, const char *strDevicePath = NULL) = 0; + virtual int FindAdapters(std::vector &deviceList, const char *strDevicePath = NULL) = 0; /*! - * @see cec_ping + * @see cec_ping_adapters */ - virtual bool Ping(void) = 0; + virtual bool PingAdapter(void) = 0; /*! * @see cec_start_bootloader */ virtual bool StartBootloader(void) = 0; + //@} /*! - * @see cec_power_off_devices - */ - virtual bool PowerOffDevices(cec_logical_address address = CECDEVICE_BROADCAST) = 0; - - /*! - * @see cec_power_on_devices - */ - virtual bool PowerOnDevices(cec_logical_address address = CECDEVICE_BROADCAST) = 0; - - /*! - * @see cec_standby_devices - */ - virtual bool StandbyDevices(cec_logical_address address = CECDEVICE_BROADCAST) = 0; - - /*! - * @see cec_set_active_view + * @see cec_get_min_version */ - virtual bool SetActiveView(void) = 0; + virtual int GetMinVersion(void) = 0; /*! - * @see cec_set_inactive_view + * @see cec_get_lib_version */ - virtual bool SetInactiveView(void) = 0; + virtual int GetLibVersion(void) = 0; /*! * @see cec_get_next_log_message @@ -104,7 +92,7 @@ namespace CEC /*! * @see cec_transmit */ - virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true, int64_t iTimeout = (int64_t) 5000) = 0; + virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true) = 0; /*! * @see cec_set_logical_address @@ -112,19 +100,25 @@ namespace CEC virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress) = 0; /*! - * @deprecated use SetLogicalAddress() instead + * @see cec_power_on_devices */ - virtual bool SetAckMask(uint16_t iMask) = 0; + virtual bool PowerOnDevices(cec_logical_address address = CECDEVICE_TV) = 0; /*! - * @see cec_get_min_version + * @see cec_standby_devices */ - virtual int GetMinVersion(void) = 0; + virtual bool StandbyDevices(cec_logical_address address = CECDEVICE_BROADCAST) = 0; /*! - * @see cec_get_lib_version + * @see cec_set_active_view */ - virtual int GetLibVersion(void) = 0; + virtual bool SetActiveView(void) = 0; + + /*! + * @see cec_set_inactive_view + */ + virtual bool SetInactiveView(void) = 0; + }; }; @@ -141,7 +135,7 @@ static int g_iLibCECInstanceCount = 0; /*! * @see cec_init */ -inline CEC::ICECDevice *LoadLibCec(const char *strName, CEC::cec_logical_address iLogicalAddress = CEC::CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS) +inline CEC::ICECAdapter *LoadLibCec(const char *strName, CEC::cec_logical_address iLogicalAddress = CEC::CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS) { typedef void* (__cdecl*_CreateLibCec)(const char *, uint8_t, uint8_t); _CreateLibCec CreateLibCec; @@ -155,14 +149,14 @@ inline CEC::ICECDevice *LoadLibCec(const char *strName, CEC::cec_logical_address CreateLibCec = (_CreateLibCec) (GetProcAddress(g_libCEC, "CECCreate")); if (!CreateLibCec) return NULL; - return static_cast< CEC::ICECDevice* > (CreateLibCec(strName, iLogicalAddress, iPhysicalAddress)); + return static_cast< CEC::ICECAdapter* > (CreateLibCec(strName, iLogicalAddress, iPhysicalAddress)); } /*! * @brief Unload the given libcec instance. * @param device The instance to unload. */ -inline void UnloadLibCec(CEC::ICECDevice *device) +inline void UnloadLibCec(CEC::ICECAdapter *device) { delete device; @@ -178,16 +172,16 @@ inline void UnloadLibCec(CEC::ICECDevice *device) /*! * @see cec_init */ -inline CEC::ICECDevice *LoadLibCec(const char *strName, CEC::cec_logical_address iLogicalAddress = CEC::CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS) +inline CEC::ICECAdapter *LoadLibCec(const char *strName, CEC::cec_logical_address iLogicalAddress = CEC::CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS) { - return (CEC::ICECDevice*) CECCreate(strName, iLogicalAddress, iPhysicalAddress); + return (CEC::ICECAdapter*) CECCreate(strName, iLogicalAddress, iPhysicalAddress); }; /*! * @brief Unload the given libcec instance. * @param device The instance to unload. */ -inline void UnloadLibCec(CEC::ICECDevice *device) +inline void UnloadLibCec(CEC::ICECAdapter *device) { delete device; }; diff --git a/include/CECTypes.h b/include/CECTypes.h index 3518d20..b9bd2ad 100644 --- a/include/CECTypes.h +++ b/include/CECTypes.h @@ -31,11 +31,6 @@ * http://www.pulse-eight.net/ */ -#include -#include -#include -#include "CECExports.h" - typedef enum { CEC_ABORT_REASON_UNRECOGNIZED_OPCODE = 0, diff --git a/project/libcec.vcxproj b/project/libcec.vcxproj index c4a3372..a9edbba 100644 --- a/project/libcec.vcxproj +++ b/project/libcec.vcxproj @@ -15,8 +15,10 @@ - - + + + + @@ -32,10 +34,12 @@ - - - - + + + + + + diff --git a/project/libcec.vcxproj.filters b/project/libcec.vcxproj.filters index f9f44b3..2bc976f 100644 --- a/project/libcec.vcxproj.filters +++ b/project/libcec.vcxproj.filters @@ -42,7 +42,6 @@ platform - exports @@ -64,7 +63,10 @@ exports - + + + + @@ -79,13 +81,15 @@ platform - platform - - - + + + + + + diff --git a/src/lib/AdapterCommunication.cpp b/src/lib/AdapterCommunication.cpp new file mode 100644 index 0000000..266842f --- /dev/null +++ b/src/lib/AdapterCommunication.cpp @@ -0,0 +1,352 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011 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 "AdapterCommunication.h" + +#include "LibCEC.h" +#include "libPlatform/serialport.h" +#include "util/StdString.h" + +using namespace std; +using namespace CEC; + +CAdapterCommunication::CAdapterCommunication(CLibCEC *controller) : + m_controller(controller), + m_inbuf(NULL), + m_iInbufSize(0), + m_iInbufUsed(0), + m_bStarted(false), + m_bStop(false) +{ + m_port = new CSerialPort; +} + +CAdapterCommunication::~CAdapterCommunication(void) +{ + m_port->Close(); + m_port = NULL; +} + +bool CAdapterCommunication::Open(const char *strPort, int iBaudRate /* = 38400 */, int iTimeoutMs /* = 10000 */) +{ + CLockObject lock(&m_commMutex); + if (m_bStarted) + return false; + + if (!m_port->Open(strPort, iBaudRate)) + { + CStdString strError; + strError.Format("error opening serial port '%s': %s", strPort, m_port->GetError().c_str()); + m_controller->AddLog(CEC_LOG_ERROR, strError); + return false; + } + + m_controller->AddLog(CEC_LOG_DEBUG, "connection opened"); + + //clear any input bytes + uint8_t buff[1024]; + m_port->Read(buff, sizeof(buff), 50); + + CCondition::Sleep(CEC_SETTLE_DOWN_TIME); + + m_bStop = false; + m_bStarted = true; + + if (CreateThread()) + { + m_controller->AddLog(CEC_LOG_DEBUG, "reader thread created"); + return true; + } + else + { + m_controller->AddLog(CEC_LOG_DEBUG, "could not create a reader thread"); + } + + return false; +} + +void CAdapterCommunication::Close(void) +{ + StopThread(); + m_port->Close(); +} + +void *CAdapterCommunication::Process(void) +{ + while (!m_bStop) + { + if (!ReadFromDevice(250)) + { + m_bStarted = false; + break; + } + + CCondition::Sleep(50); + } + + m_controller->AddLog(CEC_LOG_DEBUG, "reader thread terminated"); + + CLockObject lock(&m_commMutex); + m_bStarted = false; + return NULL; +} + +bool CAdapterCommunication::ReadFromDevice(int iTimeout) +{ + uint8_t buff[1024]; + CLockObject lock(&m_commMutex); + int iBytesRead = m_port->Read(buff, sizeof(buff), iTimeout); + lock.Leave(); + if (iBytesRead < 0) + { + CStdString strError; + strError.Format("error reading from serial port: %s", m_port->GetError().c_str()); + m_controller->AddLog(CEC_LOG_ERROR, strError); + return false; + } + else if (iBytesRead > 0) + AddData(buff, iBytesRead); + + return true; +} + +void CAdapterCommunication::AddData(uint8_t *data, int iLen) +{ + CLockObject lock(&m_bufferMutex); + if (iLen + m_iInbufUsed > m_iInbufSize) + { + m_iInbufSize = iLen + m_iInbufUsed; + m_inbuf = (uint8_t*)realloc(m_inbuf, m_iInbufSize); + } + + memcpy(m_inbuf + m_iInbufUsed, data, iLen); + m_iInbufUsed += iLen; + lock.Leave(); + m_condition.Signal(); +} + +bool CAdapterCommunication::Write(const cec_frame &data) +{ + CLockObject lock(&m_commMutex); + + if (m_port->Write(data) != data.size()) + { + CStdString strError; + strError.Format("error writing to serial port: %s", m_port->GetError().c_str()); + m_controller->AddLog(CEC_LOG_ERROR, strError); + return false; + } + + m_controller->AddLog(CEC_LOG_DEBUG, "command sent"); + + CCondition::Sleep((int) data.size() * 24 /*data*/ + 5 /*start bit (4.5 ms)*/ + 50 /* to be on the safe side */); + + return true; +} + +bool CAdapterCommunication::Read(cec_frame &msg, int iTimeout) +{ + CLockObject lock(&m_bufferMutex); + + if (m_iInbufUsed < 1) + m_condition.Wait(&m_bufferMutex, iTimeout); + + if (m_iInbufUsed < 1) + return false; + + //search for first start of message + int startpos = -1; + for (int i = 0; i < m_iInbufUsed; i++) + { + if (m_inbuf[i] == MSGSTART) + { + startpos = i; + break; + } + } + + if (startpos == -1) + return false; + + //move anything from the first start of message to the beginning of the buffer + if (startpos > 0) + { + memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos); + m_iInbufUsed -= startpos; + } + + if (m_iInbufUsed < 2) + return false; + + //look for end of message + startpos = -1; + int endpos = -1; + for (int i = 1; i < m_iInbufUsed; i++) + { + if (m_inbuf[i] == MSGEND) + { + endpos = i; + break; + } + else if (m_inbuf[i] == MSGSTART) + { + startpos = i; + break; + } + } + + if (startpos > 0) //we found a msgstart before msgend, this is not right, remove + { + m_controller->AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND"); + memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos); + m_iInbufUsed -= startpos; + return false; + } + + if (endpos > 0) //found a MSGEND + { + msg.clear(); + bool isesc = false; + for (int i = 1; i < endpos; i++) + { + if (isesc) + { + msg.push_back(m_inbuf[i] + (uint8_t)ESCOFFSET); + isesc = false; + } + else if (m_inbuf[i] == MSGESC) + { + isesc = true; + } + else + { + msg.push_back(m_inbuf[i]); + } + } + + if (endpos + 1 < m_iInbufUsed) + memmove(m_inbuf, m_inbuf + endpos + 1, m_iInbufUsed - endpos - 1); + + m_iInbufUsed -= endpos + 1; + + return true; + } + + return false; +} + +std::string CAdapterCommunication::GetError(void) const +{ + return m_port->GetError(); +} + +bool CAdapterCommunication::StartBootloader(void) +{ + if (!IsRunning()) + return false; + + m_controller->AddLog(CEC_LOG_DEBUG, "starting the bootloader"); + cec_frame output; + output.push_back(MSGSTART); + PushEscaped(output, MSGCODE_START_BOOTLOADER); + output.push_back(MSGEND); + + if (!Write(output)) + { + m_controller->AddLog(CEC_LOG_ERROR, "could not start the bootloader"); + return false; + } + m_controller->AddLog(CEC_LOG_DEBUG, "bootloader start command transmitted"); + return true; +} + +void CAdapterCommunication::PushEscaped(cec_frame &vec, uint8_t byte) +{ + if (byte >= MSGESC && byte != MSGSTART) + { + vec.push_back(MSGESC); + vec.push_back(byte - ESCOFFSET); + } + else + { + vec.push_back(byte); + } +} + +bool CAdapterCommunication::SetAckMask(uint16_t iMask) +{ + if (!IsRunning()) + return false; + + CStdString strLog; + strLog.Format("setting ackmask to %2x", iMask); + m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str()); + + cec_frame output; + + output.push_back(MSGSTART); + PushEscaped(output, MSGCODE_SET_ACK_MASK); + PushEscaped(output, iMask >> 8); + PushEscaped(output, (uint8_t)iMask); + output.push_back(MSGEND); + + if (!Write(output)) + { + m_controller->AddLog(CEC_LOG_ERROR, "could not set the ackmask"); + return false; + } + + return true; +} + +bool CAdapterCommunication::PingAdapter(void) +{ + if (!IsRunning()) + return false; + + m_controller->AddLog(CEC_LOG_DEBUG, "sending ping"); + cec_frame output; + output.push_back(MSGSTART); + PushEscaped(output, MSGCODE_PING); + output.push_back(MSGEND); + + if (!Write(output)) + { + m_controller->AddLog(CEC_LOG_ERROR, "could not send ping command"); + return false; + } + + m_controller->AddLog(CEC_LOG_DEBUG, "ping tranmitted"); + + // TODO check for pong + return true; +} diff --git a/src/lib/AdapterCommunication.h b/src/lib/AdapterCommunication.h new file mode 100644 index 0000000..f7dafa0 --- /dev/null +++ b/src/lib/AdapterCommunication.h @@ -0,0 +1,77 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011 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 "../../include/CECExports.h" +#include "util/threads.h" + +class CSerialPort; + +namespace CEC +{ + class CLibCEC; + + class CAdapterCommunication : CThread + { + public: + CAdapterCommunication(CLibCEC *controller); + virtual ~CAdapterCommunication(); + + bool Open(const char *strPort, int iBaudRate = 38400, int iTimeoutMs = 10000); + bool Read(cec_frame &msg, int iTimeout = 1000); + bool Write(const cec_frame &frame); + bool PingAdapter(void); + void Close(void); + bool IsOpen(void) const { return !m_bStop && m_bStarted; } + std::string GetError(void) const; + + void *Process(void); + + bool StartBootloader(void); + bool SetAckMask(uint16_t iMask); + static void PushEscaped(cec_frame &vec, uint8_t byte); + private: + void AddData(uint8_t *data, int iLen); + bool ReadFromDevice(int iTimeout); + + CSerialPort * m_port; + CLibCEC * m_controller; + uint8_t* m_inbuf; + int m_iInbufSize; + int m_iInbufUsed; + bool m_bStarted; + bool m_bStop; + CMutex m_commMutex; + CMutex m_bufferMutex; + CCondition m_condition; + }; +}; diff --git a/src/lib/CECDetect.cpp b/src/lib/AdapterDetection.cpp similarity index 97% rename from src/lib/CECDetect.cpp rename to src/lib/AdapterDetection.cpp index a8aa7e1..747ea16 100644 --- a/src/lib/CECDetect.cpp +++ b/src/lib/AdapterDetection.cpp @@ -30,10 +30,9 @@ * http://www.pulse-eight.net/ */ -#include "CECDetect.h" +#include "AdapterDetection.h" #include "libPlatform/os-dependent.h" #include "util/StdString.h" -#include #if !defined(__WINDOWS__) #include @@ -101,7 +100,7 @@ bool FindComPort(CStdString &strLocation) } #endif -int CCECDetect::FindDevices(vector &deviceList, const char *strDevicePath /* = NULL */) +int CAdapterDetection::FindAdapters(vector &deviceList, const char *strDevicePath /* = NULL */) { int iFound(0); @@ -146,7 +145,7 @@ int CCECDetect::FindDevices(vector &deviceList, const char *strDevic CStdString strComm(strPath); if (FindComPort(strComm)) { - cec_device foundDev; + cec_adapter foundDev; foundDev.path = strPath; foundDev.comm = strComm; deviceList.push_back(foundDev); @@ -244,7 +243,7 @@ int CCECDetect::FindDevices(vector &deviceList, const char *strDevic if (_tcslen(strPortName) > 3 && _tcsnicmp(strPortName, _T("COM"), 3) == 0 && _ttoi(&(strPortName[3])) > 0) { - cec_device foundDev; + cec_adapter foundDev; foundDev.path = devicedetailData->DevicePath; foundDev.comm = strPortName; deviceList.push_back(foundDev); diff --git a/src/lib/CECDetect.h b/src/lib/AdapterDetection.h similarity index 91% rename from src/lib/CECDetect.h rename to src/lib/AdapterDetection.h index b3cb7e0..d775bbc 100644 --- a/src/lib/CECDetect.h +++ b/src/lib/AdapterDetection.h @@ -35,9 +35,9 @@ namespace CEC { - class CCECDetect + class CAdapterDetection { public: - static int FindDevices(std::vector &deviceList, const char *strDevicePath = NULL); + static int FindAdapters(std::vector &deviceList, const char *strDevicePath = NULL); }; }; diff --git a/src/lib/CECParser.cpp b/src/lib/CECParser.cpp deleted file mode 100644 index dad7a0d..0000000 --- a/src/lib/CECParser.cpp +++ /dev/null @@ -1,952 +0,0 @@ -/* - * This file is part of the libCEC(R) library. - * - * libCEC(R) is Copyright (C) 2011 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 "CECParser.h" - -#include -#include -#include -#include -#include -#include "util/StdString.h" -#include "libPlatform/serialport.h" -#include "util/threads.h" -#include "util/timeutils.h" -#include "CECDetect.h" - -using namespace CEC; -using namespace std; - -#define CEC_MAX_RETRY 5 - -/*! - * ICECDevice implementation - */ -//@{ -CCECParser::CCECParser(const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, int iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS*/) : - m_inbuf(NULL), - m_iInbufSize(0), - m_iInbufUsed(0), - m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN), - m_physicaladdress(iPhysicalAddress), - m_iLogicalAddress(iLogicalAddress), - m_strDeviceName(strDeviceName), - m_bRunning(false) -{ - m_serialport = new CSerialPort; -} - -CCECParser::~CCECParser(void) -{ - Close(0); - m_serialport->Close(); - delete m_serialport; -} - -bool CCECParser::Open(const char *strPort, int iTimeoutMs /* = 10000 */) -{ - bool bReturn(false); - - if (!(bReturn = m_serialport->Open(strPort, 38400))) - { - CStdString strError; - strError.Format("error opening serial port '%s': %s", strPort, m_serialport->GetError().c_str()); - AddLog(CEC_LOG_ERROR, strError); - return bReturn; - } - - //clear any input bytes - uint8_t buff[1024]; - m_serialport->Read(buff, sizeof(buff), CEC_SETTLE_DOWN_TIME); - - if (bReturn) - bReturn = SetLogicalAddress(m_iLogicalAddress); - - if (!bReturn) - { - CStdString strError; - strError.Format("error opening serial port '%s': %s", strPort, m_serialport->GetError().c_str()); - AddLog(CEC_LOG_ERROR, strError); - return bReturn; - } - - if (bReturn) - { - m_bRunning = true; - if (pthread_create(&m_thread, NULL, (void *(*) (void *))&CCECParser::ThreadHandler, (void *)this) == 0) - pthread_detach(m_thread); - else - m_bRunning = false; - } - - return bReturn; -} - -bool CCECParser::Close(int iTimeoutMs /* = 2000 */) -{ - m_bRunning = false; - bool bExit(false); - if (iTimeoutMs > 0) - { - bExit = m_exitCondition.Wait(&m_mutex, iTimeoutMs); - m_mutex.Unlock(); - } - else - { - pthread_join(m_thread, NULL); - bExit = true; - } - - return bExit; -} - -void *CCECParser::ThreadHandler(CCECParser *parser) -{ - if (parser) - parser->Process(); - return 0; -} - -bool CCECParser::Process(void) -{ - int64_t now = GetTimeMs(); - while (m_bRunning) - { - { - CLockObject lock(&m_mutex, 1000); - if (lock.IsLocked()) - { - if (!ReadFromDevice(100)) - { - m_bRunning = false; - return false; - } - } - } - - //AddLog(CEC_LOG_DEBUG, "processing messages"); - ProcessMessages(); - now = GetTimeMs(); - CheckKeypressTimeout(now); - CCondition::Sleep(50); - } - - AddLog(CEC_LOG_DEBUG, "reader thread terminated"); - m_bRunning = false; - m_exitCondition.Signal(); - return true; -} - -bool CCECParser::Ping(void) -{ - if (!m_bRunning) - return false; - - AddLog(CEC_LOG_DEBUG, "sending ping"); - cec_frame output; - output.push_back(MSGSTART); - PushEscaped(output, MSGCODE_PING); - output.push_back(MSGEND); - - if (!TransmitFormatted(output, false, (int64_t) 5000)) - { - AddLog(CEC_LOG_ERROR, "could not send ping command"); - return false; - } - - AddLog(CEC_LOG_DEBUG, "ping tranmitted"); - - // TODO check for pong - return true; -} - -bool CCECParser::StartBootloader(void) -{ - if (!m_bRunning) - return false; - - AddLog(CEC_LOG_DEBUG, "starting the bootloader"); - cec_frame output; - output.push_back(MSGSTART); - PushEscaped(output, MSGCODE_START_BOOTLOADER); - output.push_back(MSGEND); - - if (!TransmitFormatted(output, false, (int64_t) 5000)) - { - AddLog(CEC_LOG_ERROR, "could not start the bootloader"); - return false; - } - - AddLog(CEC_LOG_DEBUG, "bootloader start command transmitted"); - return true; -} - -uint8_t CCECParser::GetSourceDestination(cec_logical_address destination /* = CECDEVICE_BROADCAST */) -{ - return ((uint8_t)m_iLogicalAddress << 4) + (uint8_t)destination; -} - -bool CCECParser::PowerOffDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) -{ - if (!m_bRunning) - return false; - - CStdString strLog; - strLog.Format("powering off devices with logical address %d", (int8_t)address); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); - cec_frame frame; - frame.push_back(GetSourceDestination(address)); - frame.push_back(CEC_OPCODE_STANDBY); - return Transmit(frame); -} - -bool CCECParser::PowerOnDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) -{ - if (!m_bRunning) - return false; - - CStdString strLog; - strLog.Format("powering on devices with logical address %d", (int8_t)address); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); - cec_frame frame; - frame.push_back(GetSourceDestination(address)); - frame.push_back(CEC_OPCODE_TEXT_VIEW_ON); - return Transmit(frame); -} - -bool CCECParser::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) -{ - if (!m_bRunning) - return false; - - CStdString strLog; - strLog.Format("putting all devices with logical address %d in standby mode", (int8_t)address); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); - cec_frame frame; - frame.push_back(GetSourceDestination(address)); - frame.push_back(CEC_OPCODE_STANDBY); - return Transmit(frame); -} - -bool CCECParser::SetActiveView(void) -{ - if (!m_bRunning) - return false; - - AddLog(CEC_LOG_DEBUG, "setting active view"); - cec_frame frame; - frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST)); - frame.push_back(CEC_OPCODE_ACTIVE_SOURCE); - frame.push_back((m_physicaladdress >> 8) & 0xFF); - frame.push_back(m_physicaladdress & 0xFF); - return Transmit(frame); -} - -bool CCECParser::SetInactiveView(void) -{ - if (!m_bRunning) - return false; - - AddLog(CEC_LOG_DEBUG, "setting inactive view"); - cec_frame frame; - frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST)); - frame.push_back(CEC_OPCODE_INACTIVE_SOURCE); - frame.push_back((m_physicaladdress >> 8) & 0xFF); - frame.push_back(m_physicaladdress & 0xFF); - return Transmit(frame); -} - -bool CCECParser::GetNextLogMessage(cec_log_message *message) -{ - return m_bRunning ? m_logBuffer.Pop(*message) : false; -} - -bool CCECParser::GetNextKeypress(cec_keypress *key) -{ - return m_bRunning ? m_keyBuffer.Pop(*key) : false; -} - -bool CCECParser::GetNextCommand(cec_command *command) -{ - return m_bRunning ? m_commandBuffer.Pop(*command) : false; -} -//@} - -void CCECParser::TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) -{ - AddLog(CEC_LOG_DEBUG, "transmitting abort message"); - cec_frame frame; - frame.push_back(GetSourceDestination(address)); - frame.push_back(CEC_OPCODE_FEATURE_ABORT); - frame.push_back(opcode); - frame.push_back(reason); - Transmit(frame); -} - -void CCECParser::ReportCECVersion(cec_logical_address address /* = CECDEVICE_TV */) -{ - cec_frame frame; - AddLog(CEC_LOG_NOTICE, "reporting CEC version as 1.3a"); - frame.push_back(GetSourceDestination(address)); - frame.push_back(CEC_OPCODE_CEC_VERSION); - frame.push_back(CEC_VERSION_1_3A); - Transmit(frame); -} - -void CCECParser::ReportPowerState(cec_logical_address address /*= CECDEVICE_TV */, bool bOn /* = true */) -{ - cec_frame frame; - if (bOn) - AddLog(CEC_LOG_NOTICE, "reporting \"On\" power status"); - else - AddLog(CEC_LOG_NOTICE, "reporting \"Off\" power status"); - - frame.push_back(GetSourceDestination(address)); - frame.push_back(CEC_OPCODE_REPORT_POWER_STATUS); - frame.push_back(bOn ? CEC_POWER_STATUS_ON : CEC_POWER_STATUS_STANDBY); - Transmit(frame); -} - -void CCECParser::ReportMenuState(cec_logical_address address /* = CECDEVICE_TV */, bool bActive /* = true */) -{ - cec_frame frame; - if (bActive) - AddLog(CEC_LOG_NOTICE, "reporting menu state as active"); - else - AddLog(CEC_LOG_NOTICE, "reporting menu state as inactive"); - - frame.push_back(GetSourceDestination(address)); - frame.push_back(CEC_OPCODE_MENU_STATUS); - frame.push_back(bActive ? CEC_MENU_STATE_ACTIVATED : CEC_MENU_STATE_DEACTIVATED); - Transmit(frame); -} - -void CCECParser::ReportVendorID(cec_logical_address address /* = CECDEVICE_TV */) -{ - AddLog(CEC_LOG_NOTICE, "vendor ID requested, feature abort"); - TransmitAbort(address, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID); -} - -void CCECParser::ReportOSDName(cec_logical_address address /* = CECDEVICE_TV */) -{ - cec_frame frame; - const char *osdname = m_strDeviceName.c_str(); - CStdString strLog; - strLog.Format("reporting OSD name as %s", osdname); - AddLog(CEC_LOG_NOTICE, strLog.c_str()); - frame.push_back(GetSourceDestination(address)); - frame.push_back(CEC_OPCODE_SET_OSD_NAME); - - for (unsigned int i = 0; i < strlen(osdname); i++) - frame.push_back(osdname[i]); - - Transmit(frame); -} - -void CCECParser::ReportPhysicalAddress(void) -{ - cec_frame frame; - CStdString strLog; - strLog.Format("reporting physical address as %04x", m_physicaladdress); - AddLog(CEC_LOG_NOTICE, strLog.c_str()); - frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST)); - frame.push_back(CEC_OPCODE_REPORT_PHYSICAL_ADDRESS); - frame.push_back((m_physicaladdress >> 8) & 0xFF); - frame.push_back(m_physicaladdress & 0xFF); - frame.push_back(CEC_DEVICE_TYPE_PLAYBACK_DEVICE); - Transmit(frame); -} - -void CCECParser::BroadcastActiveSource(void) -{ - cec_frame frame; - AddLog(CEC_LOG_NOTICE, "broadcasting active source"); - frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST)); - frame.push_back(CEC_OPCODE_ACTIVE_SOURCE); - frame.push_back((m_physicaladdress >> 8) & 0xFF); - frame.push_back(m_physicaladdress & 0xFF); - Transmit(frame); -} - -bool CCECParser::TransmitFormatted(const cec_frame &data, bool bWaitForAck /* = true */, int64_t iTimeout /* = 2000 */) -{ - CLockObject lock(&m_mutex, iTimeout); - if (!lock.IsLocked()) - { - AddLog(CEC_LOG_ERROR, "could not get a write lock"); - return false; - } - - if (m_serialport->Write(data) != data.size()) - { - CStdString strError; - strError.Format("error writing to serial port: %s", m_serialport->GetError().c_str()); - AddLog(CEC_LOG_ERROR, strError); - return false; - } - AddLog(CEC_LOG_DEBUG, "command sent"); - - CCondition::Sleep((int) data.size() * 24 /*data*/ + 5 /*start bit (4.5 ms)*/ + 50 /* to be on the safe side */); - if (bWaitForAck && !WaitForAck()) - { - AddLog(CEC_LOG_DEBUG, "did not receive ACK"); - return false; - } - - return true; -} - -bool CCECParser::Transmit(const cec_frame &data, bool bWaitForAck /* = true */, int64_t iTimeout /* = 5000 */) -{ - CStdString txStr = "transmit "; - for (unsigned int i = 0; i < data.size(); i++) - txStr.AppendFormat(" %02x", data[i]); - AddLog(CEC_LOG_DEBUG, txStr.c_str()); - - if (data.empty()) - { - AddLog(CEC_LOG_WARNING, "transmit buffer is empty"); - return false; - } - - cec_frame output; - - //set ack polarity to high when transmitting to the broadcast address - //set ack polarity low when transmitting to any other address - output.push_back(MSGSTART); - PushEscaped(output, MSGCODE_TRANSMIT_ACK_POLARITY); - - if ((data[0] & 0xF) == 0xF) - PushEscaped(output, CEC_TRUE); - else - PushEscaped(output, CEC_FALSE); - - output.push_back(MSGEND); - - for (unsigned int i = 0; i < data.size(); i++) - { - output.push_back(MSGSTART); - - if (i == data.size() - 1) - PushEscaped(output, MSGCODE_TRANSMIT_EOM); - else - PushEscaped(output, MSGCODE_TRANSMIT); - - PushEscaped(output, data[i]); - - output.push_back(MSGEND); - } - - return TransmitFormatted(output, bWaitForAck, iTimeout); -} - -bool CCECParser::WaitForAck(int64_t iTimeout /* = 1000 */) -{ - bool bGotAck(false); - bool bError(false); - - int64_t iNow = GetTimeMs(); - int64_t iTargetTime = iNow + iTimeout; - - while (!bGotAck && !bError && (iTimeout <= 0 || iNow < iTargetTime)) - { - if (!ReadFromDevice((int) iTimeout)) - { - AddLog(CEC_LOG_ERROR, "failed to read from device"); - return false; - } - - cec_frame msg; - while (!bGotAck && !bError && GetMessage(msg, false)) - { - uint8_t iCode = msg[0] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK); - - switch (iCode) - { - case MSGCODE_COMMAND_ACCEPTED: - AddLog(CEC_LOG_DEBUG, "MSGCODE_COMMAND_ACCEPTED"); - break; - case MSGCODE_TRANSMIT_SUCCEEDED: - AddLog(CEC_LOG_DEBUG, "MSGCODE_TRANSMIT_SUCCEEDED"); - // TODO - bGotAck = true; - break; - case MSGCODE_RECEIVE_FAILED: - AddLog(CEC_LOG_WARNING, "MSGCODE_RECEIVE_FAILED"); - bError = true; - break; - case MSGCODE_COMMAND_REJECTED: - AddLog(CEC_LOG_WARNING, "MSGCODE_COMMAND_REJECTED"); - bError = true; - break; - case MSGCODE_TRANSMIT_FAILED_LINE: - AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_LINE"); - bError = true; - break; - case MSGCODE_TRANSMIT_FAILED_ACK: - AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_ACK"); - bError = true; - break; - case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: - AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA"); - bError = true; - break; - case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: - AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE"); - bError = true; - break; - default: - m_frameBuffer.Push(msg); - bGotAck = (msg[0] & MSGCODE_FRAME_ACK) != 0; - break; - } - iNow = GetTimeMs(); - } - } - - return bGotAck && !bError; -} - -bool CCECParser::ReadFromDevice(int iTimeout) -{ - uint8_t buff[1024]; - int iBytesRead = m_serialport->Read(buff, sizeof(buff), iTimeout); - if (iBytesRead < 0) - { - CStdString strError; - strError.Format("error reading from serial port: %s", m_serialport->GetError().c_str()); - AddLog(CEC_LOG_ERROR, strError); - return false; - } - else if (iBytesRead > 0) - AddData(buff, iBytesRead); - - return true; -} - -void CCECParser::ProcessMessages(void) -{ - cec_frame msg; - while (m_bRunning && GetMessage(msg)) - ParseMessage(msg); -} - -bool CCECParser::GetMessage(cec_frame &msg, bool bFromBuffer /* = true */) -{ - if (bFromBuffer && m_frameBuffer.Pop(msg)) - return true; - - if (m_iInbufUsed < 1) - return false; - - //search for first start of message - int startpos = -1; - for (int i = 0; i < m_iInbufUsed; i++) - { - if (m_inbuf[i] == MSGSTART) - { - startpos = i; - break; - } - } - - if (startpos == -1) - return false; - - //move anything from the first start of message to the beginning of the buffer - if (startpos > 0) - { - memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos); - m_iInbufUsed -= startpos; - } - - if (m_iInbufUsed < 2) - return false; - - //look for end of message - startpos = -1; - int endpos = -1; - for (int i = 1; i < m_iInbufUsed; i++) - { - if (m_inbuf[i] == MSGEND) - { - endpos = i; - break; - } - else if (m_inbuf[i] == MSGSTART) - { - startpos = i; - break; - } - } - - if (startpos > 0) //we found a msgstart before msgend, this is not right, remove - { - AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND"); - memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos); - m_iInbufUsed -= startpos; - return false; - } - - if (endpos > 0) //found a MSGEND - { - msg.clear(); - bool isesc = false; - for (int i = 1; i < endpos; i++) - { - if (isesc) - { - msg.push_back(m_inbuf[i] + (uint8_t)ESCOFFSET); - isesc = false; - } - else if (m_inbuf[i] == MSGESC) - { - isesc = true; - } - else - { - msg.push_back(m_inbuf[i]); - } - } - - if (endpos + 1 < m_iInbufUsed) - memmove(m_inbuf, m_inbuf + endpos + 1, m_iInbufUsed - endpos - 1); - - m_iInbufUsed -= endpos + 1; - - return true; - } - - return false; -} - -void CCECParser::ParseMessage(cec_frame &msg) -{ - if (msg.empty()) - return; - - CStdString logStr; - uint8_t iCode = msg[0] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK); - bool bEom = (msg[0] & MSGCODE_FRAME_EOM) != 0; - bool bAck = (msg[0] & MSGCODE_FRAME_ACK) != 0; - - switch(iCode) - { - case MSGCODE_NOTHING: - AddLog(CEC_LOG_DEBUG, "MSGCODE_NOTHING"); - break; - case MSGCODE_TIMEOUT_ERROR: - case MSGCODE_HIGH_ERROR: - case MSGCODE_LOW_ERROR: - { - if (iCode == MSGCODE_TIMEOUT_ERROR) - logStr = "MSGCODE_TIMEOUT"; - else if (iCode == MSGCODE_HIGH_ERROR) - logStr = "MSGCODE_HIGH_ERROR"; - else - logStr = "MSGCODE_LOW_ERROR"; - - int iLine = (msg.size() >= 3) ? (msg[1] << 8) | (msg[2]) : 0; - uint32_t iTime = (msg.size() >= 7) ? (msg[3] << 24) | (msg[4] << 16) | (msg[5] << 8) | (msg[6]) : 0; - logStr.AppendFormat(" line:%i", iLine); - logStr.AppendFormat(" time:%u", iTime); - AddLog(CEC_LOG_WARNING, logStr.c_str()); - } - break; - case MSGCODE_FRAME_START: - { - logStr = "MSGCODE_FRAME_START"; - m_currentframe.clear(); - if (msg.size() >= 2) - { - int iInitiator = msg[1] >> 4; - int iDestination = msg[1] & 0xF; - logStr.AppendFormat(" initiator:%u destination:%u ack:%s %s", iInitiator, iDestination, bAck ? "high" : "low", bEom ? "eom" : ""); - - m_currentframe.push_back(msg[1]); - } - AddLog(CEC_LOG_DEBUG, logStr.c_str()); - } - break; - case MSGCODE_FRAME_DATA: - { - logStr = "MSGCODE_FRAME_DATA"; - if (msg.size() >= 2) - { - uint8_t iData = msg[1]; - logStr.AppendFormat(" %02x", iData); - m_currentframe.push_back(iData); - } - AddLog(CEC_LOG_DEBUG, logStr.c_str()); - } - if (bEom) - ParseCurrentFrame(); - break; - default: - break; - } -} - -void CCECParser::ParseCurrentFrame(void) -{ - uint8_t initiator = m_currentframe[0] >> 4; - uint8_t destination = m_currentframe[0] & 0xF; - - CStdString dataStr; - dataStr.Format("received frame: initiator: %u destination: %u", initiator, destination); - - if (m_currentframe.size() > 1) - { - dataStr += " data:"; - for (unsigned int i = 1; i < m_currentframe.size(); i++) - dataStr.AppendFormat(" %02x", m_currentframe[i]); - } - AddLog(CEC_LOG_DEBUG, dataStr.c_str()); - - if (m_currentframe.size() <= 1) - return; - - vector tx; - cec_opcode opCode = (cec_opcode) m_currentframe[1]; - if (destination == (uint16_t) m_iLogicalAddress) - { - switch(opCode) - { - case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: - ReportPhysicalAddress(); - SetActiveView(); - break; - case CEC_OPCODE_GIVE_OSD_NAME: - ReportOSDName((cec_logical_address)initiator); - break; - case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID: - ReportVendorID((cec_logical_address)initiator); - break; - case CEC_OPCODE_MENU_REQUEST: - ReportMenuState((cec_logical_address)initiator); - break; - case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS: - ReportPowerState((cec_logical_address)initiator); - break; - case CEC_OPCODE_GET_CEC_VERSION: - ReportCECVersion((cec_logical_address)initiator); - break; - case CEC_OPCODE_USER_CONTROL_PRESSED: - if (m_currentframe.size() > 2) - { - AddKey(); - - if (m_currentframe[2] <= CEC_USER_CONTROL_CODE_MAX) - { - m_iCurrentButton = (cec_user_control_code) m_currentframe[2]; - m_buttontime = GetTimeMs(); - } - } - break; - case CEC_OPCODE_USER_CONTROL_RELEASE: - AddKey(); - break; - default: - cec_frame params = m_currentframe; - params.erase(params.begin(), params.begin() + 2); - AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms); - break; - } - } - else if (destination == (uint8_t) CECDEVICE_BROADCAST) - { - if (opCode == CEC_OPCODE_REQUEST_ACTIVE_SOURCE) - { - BroadcastActiveSource(); - } - else if (opCode == CEC_OPCODE_SET_STREAM_PATH) - { - if (m_currentframe.size() >= 4) - { - int streamaddr = ((int)m_currentframe[2] << 8) | ((int)m_currentframe[3]); - CStdString strLog; - strLog.Format("%i requests stream path from physical address %04x", initiator, streamaddr); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); - if (streamaddr == m_physicaladdress) - BroadcastActiveSource(); - } - } - else - { - cec_frame params = m_currentframe; - params.erase(params.begin(), params.begin() + 2); - AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms); - } - } - else - { - CStdString strLog; - strLog.Format("ignoring frame: destination: %u != %u", destination, (uint16_t)m_iLogicalAddress); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); - } -} - -void CCECParser::AddData(uint8_t *data, int iLen) -{ - if (iLen + m_iInbufUsed > m_iInbufSize) - { - m_iInbufSize = iLen + m_iInbufUsed; - m_inbuf = (uint8_t*)realloc(m_inbuf, m_iInbufSize); - } - - memcpy(m_inbuf + m_iInbufUsed, data, iLen); - m_iInbufUsed += iLen; -} - -void CCECParser::PushEscaped(cec_frame &vec, uint8_t byte) -{ - if (byte >= MSGESC && byte != MSGSTART) - { - vec.push_back(MSGESC); - vec.push_back(byte - ESCOFFSET); - } - else - { - vec.push_back(byte); - } -} - -void CCECParser::CheckKeypressTimeout(int64_t now) -{ - if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN && now - m_buttontime > 500) - { - AddKey(); - m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN; - } -} - -bool CCECParser::SetLogicalAddress(cec_logical_address iLogicalAddress) -{ - CStdString strLog; - strLog.Format("setting logical address to %d", iLogicalAddress); - AddLog(CEC_LOG_NOTICE, strLog.c_str()); - - m_iLogicalAddress = iLogicalAddress; - return SetAckMask(0x1 << (uint8_t)m_iLogicalAddress); -} - -bool CCECParser::SetAckMask(uint16_t iMask) -{ - CStdString strLog; - strLog.Format("setting ackmask to %2x", iMask); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); - - cec_frame output; - - output.push_back(MSGSTART); - PushEscaped(output, MSGCODE_SET_ACK_MASK); - PushEscaped(output, iMask >> 8); - PushEscaped(output, (uint8_t)iMask); - output.push_back(MSGEND); - - if (m_serialport->Write(output) == -1) - { - strLog.Format("error writing to serial port: %s", m_serialport->GetError().c_str()); - AddLog(CEC_LOG_ERROR, strLog); - return false; - } - - return true; -} - -void CCECParser::AddLog(cec_log_level level, const string &strMessage) -{ - cec_log_message message; - message.level = level; - message.message.assign(strMessage.c_str()); - m_logBuffer.Push(message); -} - -void CCECParser::AddKey(void) -{ - if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN) - { - cec_keypress key; - key.duration = (unsigned int) (GetTimeMs() - m_buttontime); - key.keycode = m_iCurrentButton; - m_keyBuffer.Push(key); - m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN; - m_buttontime = 0; - } -} - -void CCECParser::AddCommand(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_frame *parameters) -{ - cec_command command; - command.source = source; - command.destination = destination; - command.opcode = opcode; - if (parameters) - command.parameters = *parameters; - if (m_commandBuffer.Push(command)) - { - CStdString strDebug; - strDebug.Format("stored command '%d' in the command buffer. buffer size = %d", opcode, m_commandBuffer.Size()); - AddLog(CEC_LOG_DEBUG, strDebug); - } - else - { - AddLog(CEC_LOG_WARNING, "command buffer is full"); - } -} - -int CCECParser::GetMinVersion(void) -{ - return CEC_MIN_VERSION; -} - -int CCECParser::GetLibVersion(void) -{ - return CEC_LIB_VERSION; -} - -int CCECParser::FindDevices(std::vector &deviceList, const char *strDevicePath /* = NULL */) -{ - CStdString strDebug; - if (strDevicePath) - strDebug.Format("trying to autodetect the com port for device path '%s'", strDevicePath); - else - strDebug.Format("trying to autodetect all CEC adapters"); - AddLog(CEC_LOG_DEBUG, strDebug); - - return CCECDetect::FindDevices(deviceList, strDevicePath); -} - -DECLSPEC void * CECCreate(const char *strDeviceName, CEC::cec_logical_address iLogicalAddress /*= CEC::CECDEVICE_PLAYBACKDEVICE1 */, int iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS */) -{ - return static_cast< void* > (new CCECParser(strDeviceName, iLogicalAddress, iPhysicalAddress)); -} diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp new file mode 100644 index 0000000..f3b89c3 --- /dev/null +++ b/src/lib/CECProcessor.cpp @@ -0,0 +1,555 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011 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 "CECProcessor.h" + +#include "AdapterCommunication.h" +#include "LibCEC.h" +#include "util/StdString.h" +#include "util/timeutils.h" + +using namespace CEC; +using namespace std; + +CCECProcessor::CCECProcessor(CLibCEC *controller, CAdapterCommunication *serComm, const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, int iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS*/) : + m_physicaladdress(iPhysicalAddress), + m_iLogicalAddress(iLogicalAddress), + m_strDeviceName(strDeviceName), + m_communication(serComm), + m_controller(controller) +{ +} + +CCECProcessor::~CCECProcessor(void) +{ + StopThread(); + m_communication = NULL; + m_controller = NULL; +} + +bool CCECProcessor::Start(void) +{ + if (!m_communication || !m_communication->IsOpen()) + return false; + + if (!SetLogicalAddress(m_iLogicalAddress)) + { + m_controller->AddLog(CEC_LOG_ERROR, "could not set the logical address"); + return false; + } + + if (CreateThread()) + return true; + else + m_controller->AddLog(CEC_LOG_ERROR, "could not create a processor thread"); + + return false; +} + +void *CCECProcessor::Process(void) +{ + m_controller->AddLog(CEC_LOG_DEBUG, "processor thread started"); + + while (!m_bStop) + { + bool bParseFrame(false); + { + CLockObject lock(&m_mutex); + cec_frame msg; + if (!m_bStop && m_communication->IsOpen() && m_communication->Read(msg, CEC_BUTTON_TIMEOUT)) + bParseFrame = ParseMessage(msg); + } + + if (bParseFrame) + ParseCurrentFrame(); + + m_controller->CheckKeypressTimeout(); + CCondition::Sleep(50); + } + + m_controller->AddLog(CEC_LOG_DEBUG, "processor thread terminated"); + return NULL; +} + +bool CCECProcessor::PowerOnDevices(cec_logical_address address /* = CECDEVICE_TV */) +{ + if (!IsRunning()) + return false; + + CStdString strLog; + strLog.Format("powering on devices with logical address %d", (int8_t)address); + m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str()); + cec_frame frame; + frame.push_back(GetSourceDestination(address)); + frame.push_back(CEC_OPCODE_TEXT_VIEW_ON); + return Transmit(frame); +} + +bool CCECProcessor::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +{ + if (!IsRunning()) + return false; + + CStdString strLog; + strLog.Format("putting all devices with logical address %d in standby mode", (int8_t)address); + m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str()); + cec_frame frame; + frame.push_back(GetSourceDestination(address)); + frame.push_back(CEC_OPCODE_STANDBY); + return Transmit(frame); +} + +bool CCECProcessor::SetActiveView(void) +{ + if (!IsRunning()) + return false; + + m_controller->AddLog(CEC_LOG_DEBUG, "setting active view"); + cec_frame frame; + frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST)); + frame.push_back(CEC_OPCODE_ACTIVE_SOURCE); + frame.push_back((m_physicaladdress >> 8) & 0xFF); + frame.push_back(m_physicaladdress & 0xFF); + return Transmit(frame); +} + +bool CCECProcessor::SetInactiveView(void) +{ + if (!IsRunning()) + return false; + + m_controller->AddLog(CEC_LOG_DEBUG, "setting inactive view"); + cec_frame frame; + frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST)); + frame.push_back(CEC_OPCODE_INACTIVE_SOURCE); + frame.push_back((m_physicaladdress >> 8) & 0xFF); + frame.push_back(m_physicaladdress & 0xFF); + return Transmit(frame); +} + +bool CCECProcessor::Transmit(const cec_frame &data, bool bWaitForAck /* = true */) +{ + CStdString txStr = "transmit "; + for (unsigned int i = 0; i < data.size(); i++) + txStr.AppendFormat(" %02x", data[i]); + m_controller->AddLog(CEC_LOG_DEBUG, txStr.c_str()); + + if (data.empty()) + { + m_controller->AddLog(CEC_LOG_WARNING, "transmit buffer is empty"); + return false; + } + + cec_frame output; + + //set ack polarity to high when transmitting to the broadcast address + //set ack polarity low when transmitting to any other address + output.push_back(MSGSTART); + CAdapterCommunication::PushEscaped(output, MSGCODE_TRANSMIT_ACK_POLARITY); + + if ((data[0] & 0xF) == 0xF) + CAdapterCommunication::PushEscaped(output, CEC_TRUE); + else + CAdapterCommunication::PushEscaped(output, CEC_FALSE); + + output.push_back(MSGEND); + + for (unsigned int i = 0; i < data.size(); i++) + { + output.push_back(MSGSTART); + + if (i == data.size() - 1) + CAdapterCommunication::PushEscaped(output, MSGCODE_TRANSMIT_EOM); + else + CAdapterCommunication::PushEscaped(output, MSGCODE_TRANSMIT); + + CAdapterCommunication::PushEscaped(output, data[i]); + + output.push_back(MSGEND); + } + + return TransmitFormatted(output, bWaitForAck); +} + +bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress) +{ + CStdString strLog; + strLog.Format("setting logical address to %d", iLogicalAddress); + m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str()); + + m_iLogicalAddress = iLogicalAddress; + return m_communication && m_communication->SetAckMask(0x1 << (uint8_t)m_iLogicalAddress); +} + +bool CCECProcessor::TransmitFormatted(const cec_frame &data, bool bWaitForAck /* = true */) +{ + CLockObject lock(&m_mutex); + if (!m_communication || !m_communication->Write(data)) + return false; + + if (bWaitForAck && !WaitForAck()) + { + m_controller->AddLog(CEC_LOG_DEBUG, "did not receive ACK"); + return false; + } + + return true; +} + +void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) +{ + m_controller->AddLog(CEC_LOG_DEBUG, "transmitting abort message"); + cec_frame frame; + frame.push_back(GetSourceDestination(address)); + frame.push_back(CEC_OPCODE_FEATURE_ABORT); + frame.push_back(opcode); + frame.push_back(reason); + Transmit(frame); +} + +void CCECProcessor::ReportCECVersion(cec_logical_address address /* = CECDEVICE_TV */) +{ + cec_frame frame; + m_controller->AddLog(CEC_LOG_NOTICE, "reporting CEC version as 1.3a"); + frame.push_back(GetSourceDestination(address)); + frame.push_back(CEC_OPCODE_CEC_VERSION); + frame.push_back(CEC_VERSION_1_3A); + Transmit(frame); +} + +void CCECProcessor::ReportPowerState(cec_logical_address address /*= CECDEVICE_TV */, bool bOn /* = true */) +{ + cec_frame frame; + if (bOn) + m_controller->AddLog(CEC_LOG_NOTICE, "reporting \"On\" power status"); + else + m_controller->AddLog(CEC_LOG_NOTICE, "reporting \"Off\" power status"); + + frame.push_back(GetSourceDestination(address)); + frame.push_back(CEC_OPCODE_REPORT_POWER_STATUS); + frame.push_back(bOn ? CEC_POWER_STATUS_ON : CEC_POWER_STATUS_STANDBY); + Transmit(frame); +} + +void CCECProcessor::ReportMenuState(cec_logical_address address /* = CECDEVICE_TV */, bool bActive /* = true */) +{ + cec_frame frame; + if (bActive) + m_controller->AddLog(CEC_LOG_NOTICE, "reporting menu state as active"); + else + m_controller->AddLog(CEC_LOG_NOTICE, "reporting menu state as inactive"); + + frame.push_back(GetSourceDestination(address)); + frame.push_back(CEC_OPCODE_MENU_STATUS); + frame.push_back(bActive ? CEC_MENU_STATE_ACTIVATED : CEC_MENU_STATE_DEACTIVATED); + Transmit(frame); +} + +void CCECProcessor::ReportVendorID(cec_logical_address address /* = CECDEVICE_TV */) +{ + m_controller->AddLog(CEC_LOG_NOTICE, "vendor ID requested, feature abort"); + TransmitAbort(address, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID); +} + +void CCECProcessor::ReportOSDName(cec_logical_address address /* = CECDEVICE_TV */) +{ + cec_frame frame; + const char *osdname = m_strDeviceName.c_str(); + CStdString strLog; + strLog.Format("reporting OSD name as %s", osdname); + m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str()); + frame.push_back(GetSourceDestination(address)); + frame.push_back(CEC_OPCODE_SET_OSD_NAME); + + for (unsigned int i = 0; i < strlen(osdname); i++) + frame.push_back(osdname[i]); + + Transmit(frame); +} + +void CCECProcessor::ReportPhysicalAddress(void) +{ + cec_frame frame; + CStdString strLog; + strLog.Format("reporting physical address as %04x", m_physicaladdress); + m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str()); + frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST)); + frame.push_back(CEC_OPCODE_REPORT_PHYSICAL_ADDRESS); + frame.push_back((m_physicaladdress >> 8) & 0xFF); + frame.push_back(m_physicaladdress & 0xFF); + frame.push_back(CEC_DEVICE_TYPE_PLAYBACK_DEVICE); + Transmit(frame); +} + +void CCECProcessor::BroadcastActiveSource(void) +{ + cec_frame frame; + m_controller->AddLog(CEC_LOG_NOTICE, "broadcasting active source"); + frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST)); + frame.push_back(CEC_OPCODE_ACTIVE_SOURCE); + frame.push_back((m_physicaladdress >> 8) & 0xFF); + frame.push_back(m_physicaladdress & 0xFF); + Transmit(frame); +} + +uint8_t CCECProcessor::GetSourceDestination(cec_logical_address destination /* = CECDEVICE_BROADCAST */) const +{ + return ((uint8_t)m_iLogicalAddress << 4) + (uint8_t)destination; +} + +bool CCECProcessor::WaitForAck(int iTimeout /* = 1000 */) +{ + bool bGotAck(false); + bool bError(false); + + int64_t iNow = GetTimeMs(); + int64_t iTargetTime = iNow + (int64_t) iTimeout; + + while (!bGotAck && !bError && (iTimeout <= 0 || iNow < iTargetTime)) + { + cec_frame msg; + while (!bGotAck && !bError && m_communication->Read(msg, iTimeout)) + { + uint8_t iCode = msg[0] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK); + + switch (iCode) + { + case MSGCODE_COMMAND_ACCEPTED: + m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_COMMAND_ACCEPTED"); + break; + case MSGCODE_TRANSMIT_SUCCEEDED: + m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_TRANSMIT_SUCCEEDED"); + // TODO + bGotAck = true; + break; + case MSGCODE_RECEIVE_FAILED: + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_RECEIVE_FAILED"); + bError = true; + break; + case MSGCODE_COMMAND_REJECTED: + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_COMMAND_REJECTED"); + bError = true; + break; + case MSGCODE_TRANSMIT_FAILED_LINE: + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_LINE"); + bError = true; + break; + case MSGCODE_TRANSMIT_FAILED_ACK: + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_ACK"); + bError = true; + break; + case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA"); + bError = true; + break; + case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE"); + bError = true; + break; + default: + m_frameBuffer.Push(msg); + bGotAck = (msg[0] & MSGCODE_FRAME_ACK) != 0; + break; + } + iNow = GetTimeMs(); + } + } + + return bGotAck && !bError; +} + +bool CCECProcessor::ParseMessage(cec_frame &msg) +{ + bool bReturn(false); + + if (msg.empty()) + return bReturn; + + CStdString logStr; + uint8_t iCode = msg[0] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK); + bool bEom = (msg[0] & MSGCODE_FRAME_EOM) != 0; + bool bAck = (msg[0] & MSGCODE_FRAME_ACK) != 0; + + switch(iCode) + { + case MSGCODE_NOTHING: + m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_NOTHING"); + break; + case MSGCODE_TIMEOUT_ERROR: + case MSGCODE_HIGH_ERROR: + case MSGCODE_LOW_ERROR: + { + if (iCode == MSGCODE_TIMEOUT_ERROR) + logStr = "MSGCODE_TIMEOUT"; + else if (iCode == MSGCODE_HIGH_ERROR) + logStr = "MSGCODE_HIGH_ERROR"; + else + logStr = "MSGCODE_LOW_ERROR"; + + int iLine = (msg.size() >= 3) ? (msg[1] << 8) | (msg[2]) : 0; + uint32_t iTime = (msg.size() >= 7) ? (msg[3] << 24) | (msg[4] << 16) | (msg[5] << 8) | (msg[6]) : 0; + logStr.AppendFormat(" line:%i", iLine); + logStr.AppendFormat(" time:%u", iTime); + m_controller->AddLog(CEC_LOG_WARNING, logStr.c_str()); + } + break; + case MSGCODE_FRAME_START: + { + logStr = "MSGCODE_FRAME_START"; + m_currentframe.clear(); + if (msg.size() >= 2) + { + int iInitiator = msg[1] >> 4; + int iDestination = msg[1] & 0xF; + logStr.AppendFormat(" initiator:%u destination:%u ack:%s %s", iInitiator, iDestination, bAck ? "high" : "low", bEom ? "eom" : ""); + + m_currentframe.push_back(msg[1]); + } + m_controller->AddLog(CEC_LOG_DEBUG, logStr.c_str()); + } + break; + case MSGCODE_FRAME_DATA: + { + logStr = "MSGCODE_FRAME_DATA"; + if (msg.size() >= 2) + { + uint8_t iData = msg[1]; + logStr.AppendFormat(" %02x", iData); + m_currentframe.push_back(iData); + } + m_controller->AddLog(CEC_LOG_DEBUG, logStr.c_str()); + } + if (bEom) + bReturn = true; + break; + default: + break; + } + + return bReturn; +} + +void CCECProcessor::ParseCurrentFrame(void) +{ + uint8_t initiator = m_currentframe[0] >> 4; + uint8_t destination = m_currentframe[0] & 0xF; + + CStdString dataStr; + dataStr.Format("received frame: initiator: %u destination: %u", initiator, destination); + + if (m_currentframe.size() > 1) + { + dataStr += " data:"; + for (unsigned int i = 1; i < m_currentframe.size(); i++) + dataStr.AppendFormat(" %02x", m_currentframe[i]); + } + m_controller->AddLog(CEC_LOG_DEBUG, dataStr.c_str()); + + if (m_currentframe.size() <= 1) + return; + + vector tx; + cec_opcode opCode = (cec_opcode) m_currentframe[1]; + if (destination == (uint16_t) m_iLogicalAddress) + { + switch(opCode) + { + case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: + ReportPhysicalAddress(); + SetActiveView(); + break; + case CEC_OPCODE_GIVE_OSD_NAME: + ReportOSDName((cec_logical_address)initiator); + break; + case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID: + ReportVendorID((cec_logical_address)initiator); + break; + case CEC_OPCODE_MENU_REQUEST: + ReportMenuState((cec_logical_address)initiator); + break; + case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS: + ReportPowerState((cec_logical_address)initiator); + break; + case CEC_OPCODE_GET_CEC_VERSION: + ReportCECVersion((cec_logical_address)initiator); + break; + case CEC_OPCODE_USER_CONTROL_PRESSED: + if (m_currentframe.size() > 2) + { + m_controller->AddKey(); + + if (m_currentframe[2] <= CEC_USER_CONTROL_CODE_MAX) + m_controller->SetCurrentButton((cec_user_control_code) m_currentframe[2]); + } + break; + case CEC_OPCODE_USER_CONTROL_RELEASE: + m_controller->AddKey(); + break; + default: + cec_frame params = m_currentframe; + params.erase(params.begin(), params.begin() + 2); + m_controller->AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms); + break; + } + } + else if (destination == (uint8_t) CECDEVICE_BROADCAST) + { + if (opCode == CEC_OPCODE_REQUEST_ACTIVE_SOURCE) + { + BroadcastActiveSource(); + } + else if (opCode == CEC_OPCODE_SET_STREAM_PATH) + { + if (m_currentframe.size() >= 4) + { + int streamaddr = ((int)m_currentframe[2] << 8) | ((int)m_currentframe[3]); + CStdString strLog; + strLog.Format("%i requests stream path from physical address %04x", initiator, streamaddr); + m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str()); + if (streamaddr == m_physicaladdress) + BroadcastActiveSource(); + } + } + else + { + cec_frame params = m_currentframe; + params.erase(params.begin(), params.begin() + 2); + m_controller->AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms); + } + } + else + { + CStdString strLog; + strLog.Format("ignoring frame: destination: %u != %u", destination, (uint16_t)m_iLogicalAddress); + m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str()); + } +} diff --git a/src/lib/CECParser.h b/src/lib/CECProcessor.h similarity index 55% rename from src/lib/CECParser.h rename to src/lib/CECProcessor.h index be95855..339653c 100644 --- a/src/lib/CECParser.h +++ b/src/lib/CECProcessor.h @@ -31,50 +31,35 @@ * http://www.pulse-eight.net/ */ -#include -#include #include "../../include/CECExports.h" #include "../../include/CECTypes.h" +#include "util/threads.h" #include "util/buffer.h" class CSerialPort; namespace CEC { - class CCECParser : public ICECDevice + class CLibCEC; + class CAdapterCommunication; + + class CCECProcessor : public CThread { public: - /*! - * ICECDevice implementation - */ - //@{ - CCECParser(const char *strDeviceName, cec_logical_address iLogicalAddress = CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS); - virtual ~CCECParser(void); + CCECProcessor(CLibCEC *controller, CAdapterCommunication *serComm, const char *strDeviceName, cec_logical_address iLogicalAddress = CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS); + virtual ~CCECProcessor(void); + + virtual bool Start(void); + void *Process(void); - virtual bool Open(const char *strPort, int iTimeout = 10000); - virtual bool Close(int iTimeoutMs = 2000); - virtual int FindDevices(std::vector &deviceList, const char *strDevicePath = NULL); - virtual bool Ping(void); - virtual bool StartBootloader(void); - virtual bool PowerOffDevices(cec_logical_address address = CECDEVICE_BROADCAST); - virtual bool PowerOnDevices(cec_logical_address address = CECDEVICE_BROADCAST); + virtual bool PowerOnDevices(cec_logical_address address = CECDEVICE_TV); virtual bool StandbyDevices(cec_logical_address address = CECDEVICE_BROADCAST); virtual bool SetActiveView(void); virtual bool SetInactiveView(void); - virtual bool GetNextLogMessage(cec_log_message *message); - virtual bool GetNextKeypress(cec_keypress *key); - virtual bool GetNextCommand(cec_command *command); - virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true, int64_t iTimeout = (int64_t) 5000); + virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true); virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress); - virtual bool SetAckMask(uint16_t iMask); - virtual int GetMinVersion(void); - virtual int GetLibVersion(void); - //@} - - static void *ThreadHandler(CCECParser *parser); - bool Process(void); protected: - virtual bool TransmitFormatted(const cec_frame &data, bool bWaitForAck = true, int64_t iTimeout = (int64_t) 2000); + virtual bool TransmitFormatted(const cec_frame &data, bool bWaitForAck = true); virtual void TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE); virtual void ReportCECVersion(cec_logical_address address = CECDEVICE_TV); virtual void ReportPowerState(cec_logical_address address = CECDEVICE_TV, bool bOn = true); @@ -83,41 +68,20 @@ namespace CEC virtual void ReportOSDName(cec_logical_address address = CECDEVICE_TV); virtual void ReportPhysicalAddress(void); virtual void BroadcastActiveSource(void); - virtual uint8_t GetSourceDestination(cec_logical_address destination = CECDEVICE_BROADCAST); + virtual uint8_t GetSourceDestination(cec_logical_address destination = CECDEVICE_BROADCAST) const; private: - void AddKey(void); - void AddCommand(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_frame *parameters); - void AddLog(cec_log_level level, const std::string &strMessage); - bool WaitForAck(int64_t iTimeout = (int64_t) 1000); - bool ReadFromDevice(int iTimeout); - void ProcessMessages(void); - bool GetMessage(cec_frame &msg, bool bFromBuffer = true); - void ParseMessage(cec_frame &msg); + bool WaitForAck(int iTimeout = 1000); + bool ParseMessage(cec_frame &msg); void ParseCurrentFrame(void); - void AddData(uint8_t* data, int len); - void PushEscaped(cec_frame &vec, uint8_t iByte); - - void CheckKeypressTimeout(int64_t now); - - uint8_t* m_inbuf; - int m_iInbufSize; - int m_iInbufUsed; - CSerialPort * m_serialport; cec_frame m_currentframe; - cec_user_control_code m_iCurrentButton; - int64_t m_buttontime; int m_physicaladdress; cec_logical_address m_iLogicalAddress; CecBuffer m_frameBuffer; - CecBuffer m_logBuffer; - CecBuffer m_keyBuffer; - CecBuffer m_commandBuffer; std::string m_strDeviceName; - pthread_t m_thread; CMutex m_mutex; - CCondition m_exitCondition; - bool m_bRunning; + CAdapterCommunication *m_communication; + CLibCEC *m_controller; }; }; diff --git a/src/lib/LibCEC.cpp b/src/lib/LibCEC.cpp new file mode 100644 index 0000000..23c4b18 --- /dev/null +++ b/src/lib/LibCEC.cpp @@ -0,0 +1,231 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011 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 "LibCEC.h" + +#include "AdapterCommunication.h" +#include "AdapterDetection.h" +#include "CECProcessor.h" +#include "util/StdString.h" +#include "util/timeutils.h" + +using namespace std; +using namespace CEC; + +CLibCEC::CLibCEC(const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, int iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS */) : + m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN), + m_buttontime(0) +{ + m_comm = new CAdapterCommunication(this); + m_cec = new CCECProcessor(this, m_comm, strDeviceName, iLogicalAddress, iPhysicalAddress); +} + +CLibCEC::~CLibCEC(void) +{ + delete m_cec; + m_cec = NULL; + + delete m_comm; + m_comm = NULL; +} + +bool CLibCEC::Open(const char *strPort, int iTimeoutMs /* = 10000 */) +{ + if (!m_comm) + return false; + + if (m_comm->IsOpen()) + { + AddLog(CEC_LOG_ERROR, "connection already open"); + return false; + } + + if (!m_comm->Open(strPort, 38400, iTimeoutMs)) + { + AddLog(CEC_LOG_ERROR, "could not open a connection"); + return false; + } + + if (!m_cec->Start()) + { + AddLog(CEC_LOG_ERROR, "could not start CEC communications"); + return false; + } + + return true; +} + +void CLibCEC::Close(void) +{ + if (m_cec) + m_cec->StopThread(); + if (m_comm) + m_comm->Close(); +} + +int CLibCEC::FindAdapters(std::vector &deviceList, const char *strDevicePath /* = NULL */) +{ + CStdString strDebug; + if (strDevicePath) + strDebug.Format("trying to autodetect the com port for device path '%s'", strDevicePath); + else + strDebug.Format("trying to autodetect all CEC adapters"); + AddLog(CEC_LOG_DEBUG, strDebug); + + return CAdapterDetection::FindAdapters(deviceList, strDevicePath); +} + +bool CLibCEC::PingAdapter(void) +{ + return m_comm ? m_comm->PingAdapter() : false; +} + +bool CLibCEC::StartBootloader(void) +{ + return m_comm ? m_comm->StartBootloader() : false; +} + +int CLibCEC::GetMinVersion(void) +{ + return CEC_MIN_VERSION; +} + +int CLibCEC::GetLibVersion(void) +{ + return CEC_LIB_VERSION; +} + +bool CLibCEC::GetNextLogMessage(cec_log_message *message) +{ + return m_logBuffer.Pop(*message); +} + +bool CLibCEC::GetNextKeypress(cec_keypress *key) +{ + return m_keyBuffer.Pop(*key); +} + +bool CLibCEC::GetNextCommand(cec_command *command) +{ + return m_commandBuffer.Pop(*command); +} + +bool CLibCEC::Transmit(const cec_frame &data, bool bWaitForAck /* = true */) +{ + return m_cec ? m_cec->Transmit(data, bWaitForAck) : false; +} + +bool CLibCEC::SetLogicalAddress(cec_logical_address iLogicalAddress) +{ + return m_cec ? m_cec->SetLogicalAddress(iLogicalAddress) : false; +} + +bool CLibCEC::PowerOnDevices(cec_logical_address address /* = CECDEVICE_TV */) +{ + return m_cec ? m_cec->PowerOnDevices(address) : false; +} + +bool CLibCEC::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +{ + return m_cec ? m_cec->StandbyDevices(address) : false; +} + +bool CLibCEC::SetActiveView(void) +{ + return m_cec ? m_cec->SetActiveView() : false; +} + +bool CLibCEC::SetInactiveView(void) +{ + return m_cec ? m_cec->SetInactiveView() : false; +} + +void CLibCEC::AddLog(cec_log_level level, const string &strMessage) +{ + cec_log_message message; + message.level = level; + message.message.assign(strMessage.c_str()); + m_logBuffer.Push(message); +} + +void CLibCEC::AddKey(void) +{ + if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN) + { + cec_keypress key; + key.duration = (unsigned int) (GetTimeMs() - m_buttontime); + key.keycode = m_iCurrentButton; + m_keyBuffer.Push(key); + m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN; + m_buttontime = 0; + } +} + +void CLibCEC::AddCommand(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_frame *parameters) +{ + cec_command command; + command.source = source; + command.destination = destination; + command.opcode = opcode; + if (parameters) + command.parameters = *parameters; + if (m_commandBuffer.Push(command)) + { + CStdString strDebug; + strDebug.Format("stored command '%d' in the command buffer. buffer size = %d", opcode, m_commandBuffer.Size()); + AddLog(CEC_LOG_DEBUG, strDebug); + } + else + { + AddLog(CEC_LOG_WARNING, "command buffer is full"); + } +} + +void CLibCEC::CheckKeypressTimeout(void) +{ + if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN && GetTimeMs() - m_buttontime > CEC_BUTTON_TIMEOUT) + { + AddKey(); + m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN; + } +} + +void CLibCEC::SetCurrentButton(cec_user_control_code iButtonCode) +{ + m_iCurrentButton = iButtonCode; + m_buttontime = GetTimeMs(); +} + +DECLSPEC void * CECCreate(const char *strDeviceName, CEC::cec_logical_address iLogicalAddress /*= CEC::CECDEVICE_PLAYBACKDEVICE1 */, int iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS */) +{ + return static_cast< void* > (new CLibCEC(strDeviceName, iLogicalAddress, iPhysicalAddress)); +} diff --git a/src/lib/LibCEC.h b/src/lib/LibCEC.h new file mode 100644 index 0000000..b863cb4 --- /dev/null +++ b/src/lib/LibCEC.h @@ -0,0 +1,90 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011 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 "../../include/CECExports.h" +#include "../../include/CECTypes.h" +#include "util/buffer.h" + +namespace CEC +{ + class CAdapterCommunication; + class CCECProcessor; + + class CLibCEC : public ICECAdapter + { + public: + /*! + * ICECAdapter implementation + */ + //@{ + CLibCEC(const char *strDeviceName, cec_logical_address iLogicalAddress = CECDEVICE_PLAYBACKDEVICE1, int iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS); + virtual ~CLibCEC(void); + + virtual bool Open(const char *strPort, int iTimeout = 10000); + virtual void Close(void); + virtual int FindAdapters(std::vector &deviceList, const char *strDevicePath = NULL); + virtual bool PingAdapter(void); + virtual bool StartBootloader(void); + + virtual int GetMinVersion(void); + virtual int GetLibVersion(void); + + virtual bool GetNextLogMessage(cec_log_message *message); + virtual bool GetNextKeypress(cec_keypress *key); + virtual bool GetNextCommand(cec_command *command); + + virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true); + virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress); + + virtual bool PowerOnDevices(cec_logical_address address = CECDEVICE_TV); + virtual bool StandbyDevices(cec_logical_address address = CECDEVICE_BROADCAST); + virtual bool SetActiveView(void); + virtual bool SetInactiveView(void); + //@} + + virtual void AddLog(cec_log_level level, const std::string &strMessage); + virtual void AddKey(void); + virtual void AddCommand(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_frame *parameters); + virtual void CheckKeypressTimeout(void); + virtual void SetCurrentButton(cec_user_control_code iButtonCode); + + protected: + cec_user_control_code m_iCurrentButton; + int64_t m_buttontime; + CCECProcessor *m_cec; + CAdapterCommunication *m_comm; + CecBuffer m_logBuffer; + CecBuffer m_keyBuffer; + CecBuffer m_commandBuffer; + }; +}; diff --git a/src/lib/CECParserC.cpp b/src/lib/LibCECC.cpp similarity index 79% rename from src/lib/CECParserC.cpp rename to src/lib/LibCECC.cpp index b21f1c0..7f2475b 100644 --- a/src/lib/CECParserC.cpp +++ b/src/lib/LibCECC.cpp @@ -30,7 +30,7 @@ * http://www.pulse-eight.net/ */ -#include "CECParser.h" +#include "LibCEC.h" using namespace CEC; using namespace std; @@ -39,79 +39,67 @@ using namespace std; * C interface implementation */ //@{ -ICECDevice *cec_parser; +ICECAdapter *cec_parser; bool cec_init(const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, int iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS */) { - cec_parser = (ICECDevice *) CECCreate(strDeviceName, iLogicalAddress, iPhysicalAddress); + cec_parser = (ICECAdapter *) CECCreate(strDeviceName, iLogicalAddress, iPhysicalAddress); return (cec_parser != NULL); } -bool cec_open(const char *strPort, int iTimeout) -{ - if (cec_parser) - return cec_parser->Open(strPort, iTimeout); - return false; -} - -bool cec_close(int iTimeout) +void cec_destroy(void) { - bool bReturn = false; - if (cec_parser) - bReturn = cec_parser->Close(iTimeout); - + cec_close(); delete cec_parser; cec_parser = NULL; - return bReturn; } -bool cec_ping(void) +bool cec_open(const char *strPort, int iTimeout) { if (cec_parser) - return cec_parser->Ping(); + return cec_parser->Open(strPort, iTimeout); return false; } -bool cec_start_bootloader(void) +void cec_close(void) { if (cec_parser) - return cec_parser->StartBootloader(); - return false; + cec_parser->Close(); } -bool cec_power_off_devices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +int cec_find_adapters(vector &deviceList, const char *strDevicePath /* = NULL */) { if (cec_parser) - return cec_parser->PowerOffDevices(address); - return false; + return cec_parser->FindAdapters(deviceList, strDevicePath); + return -1; } -bool cec_power_on_devices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +bool cec_ping_adapters(void) { if (cec_parser) - return cec_parser->PowerOnDevices(address); + return cec_parser->PingAdapter(); return false; } -bool cec_standby_devices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +bool cec_start_bootloader(void) { if (cec_parser) - return cec_parser->StandbyDevices(address); + return cec_parser->StartBootloader(); return false; } -bool cec_set_active_view(void) +int cec_get_min_version(void) { if (cec_parser) - return cec_parser->SetActiveView(); - return false; + return cec_parser->GetMinVersion(); + return -1; } -bool cec_set_inactive_view(void) +int cec_get_lib_version(void) { if (cec_parser) - return cec_parser->SetInactiveView(); - return false; + return cec_parser->GetLibVersion(); + return -1; } bool cec_get_next_log_message(cec_log_message *message) @@ -135,10 +123,10 @@ bool cec_get_next_command(cec_command *command) return false; } -bool cec_transmit(const CEC::cec_frame &data, bool bWaitForAck /* = true */, int64_t iTimeout /* = 2000 */) +bool cec_transmit(const CEC::cec_frame &data, bool bWaitForAck /* = true */) { if (cec_parser) - return cec_parser->Transmit(data, bWaitForAck, iTimeout); + return cec_parser->Transmit(data, bWaitForAck); return false; } @@ -149,32 +137,32 @@ bool cec_set_logical_address(cec_logical_address iLogicalAddress) return false; } -bool cec_set_ack_mask(uint16_t iMask) +bool cec_power_on_devices(cec_logical_address address /* = CECDEVICE_TV */) { if (cec_parser) - return cec_parser->SetAckMask(iMask); + return cec_parser->PowerOnDevices(address); return false; } -int cec_get_min_version(void) +bool cec_standby_devices(cec_logical_address address /* = CECDEVICE_BROADCAST */) { if (cec_parser) - return cec_parser->GetMinVersion(); - return -1; + return cec_parser->StandbyDevices(address); + return false; } -int cec_get_lib_version(void) +bool cec_set_active_view(void) { if (cec_parser) - return cec_parser->GetLibVersion(); - return -1; + return cec_parser->SetActiveView(); + return false; } -int cec_find_devices(vector &deviceList, const char *strDevicePath /* = NULL */) +bool cec_set_inactive_view(void) { if (cec_parser) - return cec_parser->FindDevices(deviceList, strDevicePath); - return -1; + return cec_parser->SetInactiveView(); + return false; } //@} diff --git a/src/lib/CECDll.cpp b/src/lib/LibCECDll.cpp similarity index 98% rename from src/lib/CECDll.cpp rename to src/lib/LibCECDll.cpp index ad90164..fb25d0e 100644 --- a/src/lib/CECDll.cpp +++ b/src/lib/LibCECDll.cpp @@ -31,7 +31,6 @@ */ #include "CECExports.h" -#include "CECParser.h" using namespace CEC; diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index ca89d5a..47664b8 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -8,16 +8,20 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libcec.pc -libcec_la_SOURCES = CECParser.cpp \ - CECParser.h \ - CECParserC.cpp \ - CECDetect.cpp \ - CECDetect.h \ +libcec_la_SOURCES = AdapterCommunication.cpp \ + AdapterCommunication.h \ + AdapterDetection.cpp \ + AdapterDetection.h \ + CECProcessor.cpp \ + CECProcessor.h \ + LibCEC.cpp \ + LibCEC.h \ + LibCECC.cpp \ ../../include/CECExports.h \ ../../include/CECExportsCpp.h \ ../../include/CECExportsC.h \ - util/misc.h \ util/misc.cpp \ + util/misc.h \ util/StdString.h \ util/threads.cpp \ util/threads.h \ diff --git a/src/lib/util/threads.cpp b/src/lib/util/threads.cpp index 3e40c2b..58aff36 100644 --- a/src/lib/util/threads.cpp +++ b/src/lib/util/threads.cpp @@ -36,62 +36,53 @@ CMutex::CMutex(void) { pthread_mutex_init(&m_mutex, NULL); - m_condition = new CCondition(); - m_bLocked = false; } CMutex::~CMutex(void) { - delete m_condition; pthread_mutex_destroy(&m_mutex); } -bool CMutex::TryLock(int64_t iTimeout) +bool CMutex::TryLock(void) { - m_bLocked = (pthread_mutex_trylock(&m_mutex) == 0); - if (!m_bLocked) - { - if (m_condition->Wait(this, iTimeout)) - m_bLocked = (pthread_mutex_trylock(&m_mutex) == 0); - } - - return m_bLocked; + return (pthread_mutex_trylock(&m_mutex) == 0); } bool CMutex::Lock(void) { - m_bLocked = (pthread_mutex_lock(&m_mutex) == 0); - return m_bLocked; + return (pthread_mutex_lock(&m_mutex) == 0); } void CMutex::Unlock(void) { pthread_mutex_unlock(&m_mutex); - m_bLocked = false; - m_condition->Signal(); } -CLockObject::CLockObject(CMutex *mutex, int64_t iTimeout /* = -1 */) : - m_mutex(mutex), - m_bLocked(false) +CLockObject::CLockObject(CMutex *mutex) : + m_mutex(mutex) { - if (iTimeout > 0) - m_bLocked = m_mutex->TryLock(iTimeout); - else - m_bLocked = m_mutex->Lock(); + m_mutex->Lock(); } CLockObject::~CLockObject(void) { - m_mutex->Unlock(); - m_bLocked = false; + Leave(); m_mutex = NULL; } +void CLockObject::Leave(void) +{ + m_mutex->Unlock(); +} + +void CLockObject::Lock(void) +{ + m_mutex->Lock(); +} + CCondition::CCondition(void) { pthread_cond_init(&m_cond, NULL); - m_bSignaled = false; } CCondition::~CCondition(void) @@ -107,6 +98,7 @@ void CCondition::Signal(void) bool CCondition::Wait(CMutex *mutex, int64_t iTimeout) { + bool bReturn(false); struct timespec abstime; struct timeval now; if (gettimeofday(&now, NULL) == 0) @@ -114,14 +106,9 @@ bool CCondition::Wait(CMutex *mutex, int64_t iTimeout) iTimeout += now.tv_usec / 1000; abstime.tv_sec = now.tv_sec + (time_t)(iTimeout / 1000); abstime.tv_nsec = (long)((iTimeout % (unsigned long)1000) * (unsigned long)1000000); - m_bSignaled = (pthread_cond_timedwait(&m_cond, &mutex->m_mutex, &abstime) == 0); - if (!m_bSignaled) - pthread_mutex_unlock(&mutex->m_mutex); + bReturn = (pthread_cond_timedwait(&m_cond, &mutex->m_mutex, &abstime) == 0); } - bool bReturn = m_bSignaled; - m_bSignaled = false; - return bReturn; } @@ -130,5 +117,46 @@ void CCondition::Sleep(int iTimeout) sched_yield(); CCondition w; CMutex m; + CLockObject lock(&m); w.Wait(&m, iTimeout); } + +CThread::CThread(void) : + m_bRunning(false), + m_bStop(false) +{ +} + +CThread::~CThread(void) +{ + m_bStop = true; + pthread_join(m_thread, NULL); +} + +bool CThread::CreateThread(void) +{ + bool bReturn(false); + + if (!m_bRunning && pthread_create(&m_thread, NULL, (void *(*) (void *))&CThread::ThreadHandler, (void *)this) == 0) + { + m_bRunning = true; + pthread_detach(m_thread); + bReturn = true; + } + + return bReturn; +} + +void *CThread::ThreadHandler(CThread *thread) +{ + if (thread) + return thread->Process(); + return NULL; +} + +void CThread::StopThread(bool bWaitForExit /* = true */) +{ + m_bStop = true; + if (bWaitForExit) + pthread_join(m_thread, NULL); +} diff --git a/src/lib/util/threads.h b/src/lib/util/threads.h index 41ad2b5..d1b6239 100644 --- a/src/lib/util/threads.h +++ b/src/lib/util/threads.h @@ -39,14 +39,13 @@ class CCondition { public: CCondition(void); - ~CCondition(void); + virtual ~CCondition(void); void Signal(void); bool Wait(CMutex *mutex, int64_t iTimeout); static void Sleep(int iTimeout); private: - bool m_bSignaled; pthread_cond_t m_cond; }; @@ -56,25 +55,43 @@ public: CMutex(void); virtual ~CMutex(void); - bool TryLock(int64_t iTimeout); + bool TryLock(void); bool Lock(void); void Unlock(void); - bool IsLocked(void) const { return m_bLocked; } pthread_mutex_t m_mutex; - CCondition *m_condition; - bool m_bLocked; }; class CLockObject { public: - CLockObject(CMutex *mutex, int64_t iTimeout = -1); + CLockObject(CMutex *mutex); ~CLockObject(void); bool IsLocked(void) const { return m_bLocked; } + void Leave(void); + void Lock(void); private: CMutex *m_mutex; bool m_bLocked; }; + +class CThread +{ +public: + CThread(void); + virtual ~CThread(void); + + virtual bool IsRunning(void) const { return m_bRunning; } + virtual bool CreateThread(void); + virtual void StopThread(bool bWaitForExit = true); + + static void *ThreadHandler(CThread *thread); + virtual void *Process(void) = 0; + +protected: + pthread_t m_thread; + bool m_bRunning; + bool m_bStop; +}; diff --git a/src/testclient/main.cpp b/src/testclient/main.cpp index 15a333d..90158ed 100644 --- a/src/testclient/main.cpp +++ b/src/testclient/main.cpp @@ -42,9 +42,9 @@ using namespace CEC; using namespace std; -#define CEC_TEST_CLIENT_VERSION 2 +#define CEC_TEST_CLIENT_VERSION 3 -void flush_log(ICECDevice *cecParser) +void flush_log(ICECAdapter *cecParser) { cec_log_message message; while (cecParser && cecParser->GetNextLogMessage(&message)) @@ -67,11 +67,11 @@ void flush_log(ICECDevice *cecParser) } } -void list_devices(ICECDevice *parser) +void list_devices(ICECAdapter *parser) { cout << "Found devices: "; - vector devices; - int iDevicesFound = parser->FindDevices(devices); + vector devices; + int iDevicesFound = parser->FindAdapters(devices); if (iDevicesFound <= 0) { #ifdef __WINDOWS__ @@ -114,9 +114,6 @@ void show_console_help(void) "tx {bytes} transfer bytes over the CEC line." << endl << "[tx 40 00 FF 11 22 33] sends bytes 0x40 0x00 0xFF 0x11 0x22 0x33" << endl << endl << - "am {ackmack} change the ackmask of the CEC adapter." << endl << - "[am 10] ackmask 0x10 (logical address 4)" << endl << - endl << "la {logical_address} change the logical address of the CEC adapter." << endl << "[la 4] logical address 4" << endl << endl << @@ -129,7 +126,7 @@ void show_console_help(void) int main (int argc, char *argv[]) { - ICECDevice *parser = LoadLibCec("CEC Tester"); + ICECAdapter *parser = LoadLibCec("CEC Tester"); if (!parser && parser->GetMinVersion() > CEC_TEST_CLIENT_VERSION) { cout << "Unable to create parser. Is libcec.dll present?" << endl; @@ -150,8 +147,8 @@ int main (int argc, char *argv[]) if (argc < 2) { cout << "no serial port given. trying autodetect: "; - vector devices; - int iDevicesFound = parser->FindDevices(devices); + vector devices; + int iDevicesFound = parser->FindAdapters(devices); if (iDevicesFound <= 0) { cout << "FAILED" << endl; @@ -192,7 +189,7 @@ int main (int argc, char *argv[]) cout << "cec device opened" << endl; usleep(CEC_SETTLE_DOWN_TIME); - parser->PowerOnDevices(); + parser->PowerOnDevices(CECDEVICE_TV); flush_log(parser); parser->SetActiveView(); @@ -223,15 +220,6 @@ int main (int argc, char *argv[]) parser->Transmit(bytes); } - else if (command == "am") - { - string strvalue; - int ackmask; - if (GetWord(input, strvalue) && HexStrToInt(strvalue, ackmask)) - { - parser->SetAckMask(ackmask); - } - } else if (command == "la") { string strvalue; @@ -242,7 +230,7 @@ int main (int argc, char *argv[]) } else if (command == "ping") { - parser->Ping(); + parser->PingAdapter(); } else if (command == "bl") { @@ -263,7 +251,8 @@ int main (int argc, char *argv[]) CCondition::Sleep(50); } - parser->PowerOffDevices(); + parser->StandbyDevices(CECDEVICE_BROADCAST); + parser->Close(); flush_log(parser); UnloadLibCec(parser); return 0;