+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 <packaging@pulse-eight.com> Tue, 04 Oct 2011 00:48:00 +0200
+
libcec (0.3-1) unstable; urgency=low
* added device detection support for Windows
-AC_INIT([libcec], 0:2:0)
+AC_INIT([libcec], 0:4:0)
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
AC_PROG_CXX
+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 <packaging@pulse-eight.com> Tue, 04 Oct 2011 00:48:00 +0200
+
libcec (0.3-1) unstable; urgency=low
* added device detection support for Windows
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<uint8_t> cec_frame;
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
{
* @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.
/*!
* @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<CEC::cec_adapter> &deviceList, const char *strDevicePath = NULL);
+#else
+extern DECLSPEC int cec_find_adapters(std::vector<cec_adapter> &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.
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.
* @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
/*!
* @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
/*!
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<CEC::cec_device> &deviceList, const char *strDevicePath = NULL);
-#else
-extern DECLSPEC int cec_find_devices(std::vector<cec_device> &deviceList, const char *strDevicePath = NULL);
-#endif
-
#ifdef __cplusplus
};
#endif
namespace CEC
{
- class ICECDevice
+ class ICECAdapter
{
public:
+ /*! @name Adapter methods */
+ //@{
/*!
* @see cec_open
*/
/*!
* @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<cec_device> &deviceList, const char *strDevicePath = NULL) = 0;
+ virtual int FindAdapters(std::vector<cec_adapter> &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
/*!
* @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
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;
+
};
};
/*!
* @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;
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;
/*!
* @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;
};
* http://www.pulse-eight.net/
*/
-#include <stdint.h>
-#include <vector>
-#include <string>
-#include "CECExports.h"
-
typedef enum
{
CEC_ABORT_REASON_UNRECOGNIZED_OPCODE = 0,
<ClInclude Include="..\include\CECExportsC.h" />
<ClInclude Include="..\include\CECExportsCpp.h" />
<ClInclude Include="..\include\CECTypes.h" />
- <ClInclude Include="..\src\lib\CECDetect.h" />
- <ClInclude Include="..\src\lib\CECParser.h" />
+ <ClInclude Include="..\src\lib\AdapterCommunication.h" />
+ <ClInclude Include="..\src\lib\AdapterDetection.h" />
+ <ClInclude Include="..\src\lib\CECProcessor.h" />
+ <ClInclude Include="..\src\lib\LibCEC.h" />
<ClInclude Include="..\src\lib\libPlatform\baudrate.h" />
<ClInclude Include="..\src\lib\libPlatform\os-dependent.h" />
<ClInclude Include="..\src\lib\libPlatform\pthread_win32\pthread.h" />
<ClInclude Include="..\src\lib\util\timeutils.h" />
</ItemGroup>
<ItemGroup>
- <ClCompile Include="..\src\lib\CECDetect.cpp" />
- <ClCompile Include="..\src\lib\CECDll.cpp" />
- <ClCompile Include="..\src\lib\CECParser.cpp" />
- <ClCompile Include="..\src\lib\CECParserC.cpp" />
+ <ClCompile Include="..\src\lib\AdapterCommunication.cpp" />
+ <ClCompile Include="..\src\lib\AdapterDetection.cpp" />
+ <ClCompile Include="..\src\lib\CECProcessor.cpp" />
+ <ClCompile Include="..\src\lib\LibCEC.cpp" />
+ <ClCompile Include="..\src\lib\LibCECC.cpp" />
+ <ClCompile Include="..\src\lib\LibCECDll.cpp" />
<ClCompile Include="..\src\lib\libPlatform\windows\dlfcn-win32.cpp" />
<ClCompile Include="..\src\lib\libPlatform\windows\os_windows.cpp" />
<ClCompile Include="..\src\lib\libPlatform\windows\serialport.cpp" />
<ClInclude Include="..\src\lib\libPlatform\pthread_win32\semaphore.h">
<Filter>platform</Filter>
</ClInclude>
- <ClInclude Include="..\src\lib\CECParser.h" />
<ClInclude Include="..\include\CECExports.h">
<Filter>exports</Filter>
</ClInclude>
<ClInclude Include="..\include\CECExportsC.h">
<Filter>exports</Filter>
</ClInclude>
- <ClInclude Include="..\src\lib\CECDetect.h" />
+ <ClInclude Include="..\src\lib\AdapterCommunication.h" />
+ <ClInclude Include="..\src\lib\AdapterDetection.h" />
+ <ClInclude Include="..\src\lib\CECProcessor.h" />
+ <ClInclude Include="..\src\lib\LibCEC.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\lib\util\misc.cpp">
<ClCompile Include="..\src\lib\libPlatform\windows\os_windows.cpp">
<Filter>platform</Filter>
</ClCompile>
- <ClCompile Include="..\src\lib\CECParser.cpp" />
<ClCompile Include="..\src\lib\libPlatform\windows\serialport.cpp">
<Filter>platform</Filter>
</ClCompile>
- <ClCompile Include="..\src\lib\CECDll.cpp" />
- <ClCompile Include="..\src\lib\CECParserC.cpp" />
- <ClCompile Include="..\src\lib\CECDetect.cpp" />
+ <ClCompile Include="..\src\lib\AdapterCommunication.cpp" />
+ <ClCompile Include="..\src\lib\AdapterDetection.cpp" />
+ <ClCompile Include="..\src\lib\CECProcessor.cpp" />
+ <ClCompile Include="..\src\lib\LibCEC.cpp" />
+ <ClCompile Include="..\src\lib\LibCECC.cpp" />
+ <ClCompile Include="..\src\lib\LibCECDll.cpp" />
</ItemGroup>
<ItemGroup>
<Library Include="..\src\lib\libPlatform\pthread_win32\pthreadVC2.lib">
--- /dev/null
+/*
+ * 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 <license@pulse-eight.com>
+ * 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;
+}
--- /dev/null
+#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 <license@pulse-eight.com>
+ * 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;
+ };
+};
* http://www.pulse-eight.net/
*/
-#include "CECDetect.h"
+#include "AdapterDetection.h"
#include "libPlatform/os-dependent.h"
#include "util/StdString.h"
-#include <string.h>
#if !defined(__WINDOWS__)
#include <dirent.h>
}
#endif
-int CCECDetect::FindDevices(vector<cec_device> &deviceList, const char *strDevicePath /* = NULL */)
+int CAdapterDetection::FindAdapters(vector<cec_adapter> &deviceList, const char *strDevicePath /* = NULL */)
{
int iFound(0);
CStdString strComm(strPath);
if (FindComPort(strComm))
{
- cec_device foundDev;
+ cec_adapter foundDev;
foundDev.path = strPath;
foundDev.comm = strComm;
deviceList.push_back(foundDev);
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);
namespace CEC
{
- class CCECDetect
+ class CAdapterDetection
{
public:
- static int FindDevices(std::vector<cec_device> &deviceList, const char *strDevicePath = NULL);
+ static int FindAdapters(std::vector<cec_adapter> &deviceList, const char *strDevicePath = NULL);
};
};
+++ /dev/null
-/*
- * 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 <license@pulse-eight.com>
- * http://www.pulse-eight.com/
- * http://www.pulse-eight.net/
- */
-
-#include "CECParser.h"
-
-#include <algorithm>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <sys/stat.h>
-#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<uint8_t> 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<cec_device> &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));
-}
--- /dev/null
+/*
+ * 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 <license@pulse-eight.com>
+ * 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<uint8_t> 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());
+ }
+}
* http://www.pulse-eight.net/
*/
-#include <queue>
-#include <stdio.h>
#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<cec_device> &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);
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<cec_frame> m_frameBuffer;
- CecBuffer<cec_log_message> m_logBuffer;
- CecBuffer<cec_keypress> m_keyBuffer;
- CecBuffer<cec_command> 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;
};
};
--- /dev/null
+/*
+ * 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 <license@pulse-eight.com>
+ * 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<cec_adapter> &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));
+}
--- /dev/null
+#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 <license@pulse-eight.com>
+ * 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<cec_adapter> &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<cec_log_message> m_logBuffer;
+ CecBuffer<cec_keypress> m_keyBuffer;
+ CecBuffer<cec_command> m_commandBuffer;
+ };
+};
* http://www.pulse-eight.net/
*/
-#include "CECParser.h"
+#include "LibCEC.h"
using namespace CEC;
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<cec_adapter> &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)
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;
}
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<cec_device> &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;
}
//@}
*/
#include "CECExports.h"
-#include "CECParser.h"
using namespace CEC;
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 \
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)
bool CCondition::Wait(CMutex *mutex, int64_t iTimeout)
{
+ bool bReturn(false);
struct timespec abstime;
struct timeval now;
if (gettimeofday(&now, NULL) == 0)
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;
}
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);
+}
{
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;
};
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;
+};
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))
}
}
-void list_devices(ICECDevice *parser)
+void list_devices(ICECAdapter *parser)
{
cout << "Found devices: ";
- vector<cec_device> devices;
- int iDevicesFound = parser->FindDevices(devices);
+ vector<cec_adapter> devices;
+ int iDevicesFound = parser->FindAdapters(devices);
if (iDevicesFound <= 0)
{
#ifdef __WINDOWS__
"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 <<
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;
if (argc < 2)
{
cout << "no serial port given. trying autodetect: ";
- vector<cec_device> devices;
- int iDevicesFound = parser->FindDevices(devices);
+ vector<cec_adapter> devices;
+ int iDevicesFound = parser->FindAdapters(devices);
if (iDevicesFound <= 0)
{
cout << "FAILED" << endl;
cout << "cec device opened" << endl;
usleep(CEC_SETTLE_DOWN_TIME);
- parser->PowerOnDevices();
+ parser->PowerOnDevices(CECDEVICE_TV);
flush_log(parser);
parser->SetActiveView();
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;
}
else if (command == "ping")
{
- parser->Ping();
+ parser->PingAdapter();
}
else if (command == "bl")
{
CCondition::Sleep(50);
}
- parser->PowerOffDevices();
+ parser->StandbyDevices(CECDEVICE_BROADCAST);
+ parser->Close();
flush_log(parser);
UnloadLibCec(parser);
return 0;