From 2abe74ebbd27d8c30060b3eebb363e10d3fbfd80 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 4 Oct 2011 00:22:27 +0200 Subject: [PATCH] cec: split up CEC processing and the public interface. removed obsolete methods from the interface. set minimal required interface version to 3 before releasing. clean ups, cosmetics. renamed some more files to be more descriptive --- include/CECExports.h | 7 +- include/CECExportsC.h | 57 +-- include/CECExportsCpp.h | 66 ++- include/CECTypes.h | 5 - src/lib/AdapterCommunication.cpp | 110 +++- src/lib/AdapterCommunication.h | 15 +- .../{CECDetect.cpp => AdapterDetection.cpp} | 9 +- src/lib/{CECDetect.h => AdapterDetection.h} | 4 +- src/lib/{CECParser.cpp => CECProcessor.cpp} | 477 +++++------------- src/lib/{CECParser.h => CECProcessor.h} | 52 +- src/lib/LibCEC.cpp | 231 +++++++++ src/lib/LibCEC.h | 90 ++++ src/lib/{CECParserC.cpp => LibCECC.cpp} | 68 +-- src/lib/{CECDll.cpp => LibCECDll.cpp} | 1 - src/lib/Makefile.am | 16 +- src/testclient/main.cpp | 30 +- 16 files changed, 672 insertions(+), 566 deletions(-) rename src/lib/{CECDetect.cpp => AdapterDetection.cpp} (97%) rename src/lib/{CECDetect.h => AdapterDetection.h} (91%) rename src/lib/{CECParser.cpp => CECProcessor.cpp} (54%) rename src/lib/{CECParser.h => CECProcessor.h} (63%) create mode 100644 src/lib/LibCEC.cpp create mode 100644 src/lib/LibCEC.h rename src/lib/{CECParserC.cpp => LibCECC.cpp} (84%) rename src/lib/{CECDll.cpp => LibCECDll.cpp} (98%) diff --git a/include/CECExports.h b/include/CECExports.h index b5a3b5d..bedd0e0 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_MIN_VERSION 3 #define CEC_LIB_VERSION 3 #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 d03bfe0..61cabd2 100644 --- a/include/CECExportsC.h +++ b/include/CECExportsC.h @@ -69,11 +69,23 @@ extern DECLSPEC bool cec_open(const char *strPort, 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. @@ -82,13 +94,14 @@ extern DECLSPEC bool cec_ping(void); extern DECLSPEC bool cec_start_bootloader(void); /*! - * @depcrecated Use cec_standby_devices() instead + * @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. */ -#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_lib_version(void); /*! * @brief Power on the connected CEC capable devices. @@ -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 635d505..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 */ @@ -47,44 +49,30 @@ namespace CEC 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; + //@} /*! - * @depcrecated Use StandbyDevices() instead - */ - 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 @@ -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/src/lib/AdapterCommunication.cpp b/src/lib/AdapterCommunication.cpp index 1ee7f88..266842f 100644 --- a/src/lib/AdapterCommunication.cpp +++ b/src/lib/AdapterCommunication.cpp @@ -31,15 +31,16 @@ */ #include "AdapterCommunication.h" -#include "CECParser.h" + +#include "LibCEC.h" #include "libPlatform/serialport.h" #include "util/StdString.h" using namespace std; using namespace CEC; -CAdapterCommunication::CAdapterCommunication(CCECParser *parser) : - m_parser(parser), +CAdapterCommunication::CAdapterCommunication(CLibCEC *controller) : + m_controller(controller), m_inbuf(NULL), m_iInbufSize(0), m_iInbufUsed(0), @@ -65,11 +66,11 @@ bool CAdapterCommunication::Open(const char *strPort, int iBaudRate /* = 38400 * { CStdString strError; strError.Format("error opening serial port '%s': %s", strPort, m_port->GetError().c_str()); - m_parser->AddLog(CEC_LOG_ERROR, strError); + m_controller->AddLog(CEC_LOG_ERROR, strError); return false; } - m_parser->AddLog(CEC_LOG_DEBUG, "connection opened"); + m_controller->AddLog(CEC_LOG_DEBUG, "connection opened"); //clear any input bytes uint8_t buff[1024]; @@ -82,12 +83,12 @@ bool CAdapterCommunication::Open(const char *strPort, int iBaudRate /* = 38400 * if (CreateThread()) { - m_parser->AddLog(CEC_LOG_DEBUG, "reader thread created"); + m_controller->AddLog(CEC_LOG_DEBUG, "reader thread created"); return true; } else { - m_parser->AddLog(CEC_LOG_DEBUG, "could not create a reader thread"); + m_controller->AddLog(CEC_LOG_DEBUG, "could not create a reader thread"); } return false; @@ -112,7 +113,7 @@ void *CAdapterCommunication::Process(void) CCondition::Sleep(50); } - m_parser->AddLog(CEC_LOG_DEBUG, "reader thread terminated"); + m_controller->AddLog(CEC_LOG_DEBUG, "reader thread terminated"); CLockObject lock(&m_commMutex); m_bStarted = false; @@ -129,7 +130,7 @@ bool CAdapterCommunication::ReadFromDevice(int iTimeout) { CStdString strError; strError.Format("error reading from serial port: %s", m_port->GetError().c_str()); - m_parser->AddLog(CEC_LOG_ERROR, strError); + m_controller->AddLog(CEC_LOG_ERROR, strError); return false; } else if (iBytesRead > 0) @@ -161,11 +162,14 @@ bool CAdapterCommunication::Write(const cec_frame &data) { CStdString strError; strError.Format("error writing to serial port: %s", m_port->GetError().c_str()); - m_parser->AddLog(CEC_LOG_ERROR, strError); + m_controller->AddLog(CEC_LOG_ERROR, strError); return false; } - m_parser->AddLog(CEC_LOG_DEBUG, "command sent"); + 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; } @@ -222,7 +226,7 @@ bool CAdapterCommunication::Read(cec_frame &msg, int iTimeout) if (startpos > 0) //we found a msgstart before msgend, this is not right, remove { - m_parser->AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND"); + m_controller->AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND"); memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos); m_iInbufUsed -= startpos; return false; @@ -264,3 +268,85 @@ 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 index 270d96b..f7dafa0 100644 --- a/src/lib/AdapterCommunication.h +++ b/src/lib/AdapterCommunication.h @@ -31,39 +31,40 @@ * http://www.pulse-eight.net/ */ -#include -#include #include "../../include/CECExports.h" -#include "../../include/CECTypes.h" -#include "util/buffer.h" #include "util/threads.h" class CSerialPort; namespace CEC { - class CCECParser; + class CLibCEC; class CAdapterCommunication : CThread { public: - CAdapterCommunication(CCECParser *parser); + 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; - CCECParser * m_parser; + CLibCEC * m_controller; uint8_t* m_inbuf; int m_iInbufSize; int m_iInbufUsed; 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/CECProcessor.cpp similarity index 54% rename from src/lib/CECParser.cpp rename to src/lib/CECProcessor.cpp index a86da1a..f3b89c3 100644 --- a/src/lib/CECParser.cpp +++ b/src/lib/CECProcessor.cpp @@ -30,87 +30,55 @@ * http://www.pulse-eight.net/ */ -#include "CECParser.h" +#include "CECProcessor.h" -#include -#include -#include -#include -#include +#include "AdapterCommunication.h" +#include "LibCEC.h" #include "util/StdString.h" -#include "libPlatform/serialport.h" -#include "util/threads.h" #include "util/timeutils.h" -#include "CECDetect.h" -#include "AdapterCommunication.h" using namespace CEC; using namespace std; -#define CEC_MAX_RETRY 5 -#define CEC_BUTTON_TIMEOUT 500 - -/*! - * ICECDevice implementation - */ -//@{ -CCECParser::CCECParser(const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, int iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS*/) : - m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN), +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_strDeviceName(strDeviceName), + m_communication(serComm), + m_controller(controller) { - m_communication = new CAdapterCommunication(this); } -CCECParser::~CCECParser(void) +CCECProcessor::~CCECProcessor(void) { - Close(); - m_communication->Close(); - delete m_communication; + StopThread(); + m_communication = NULL; + m_controller = NULL; } -bool CCECParser::Open(const char *strPort, int iTimeoutMs /* = 10000 */) +bool CCECProcessor::Start(void) { - if (!m_communication) - return false; - - if (m_communication->IsOpen()) - { - AddLog(CEC_LOG_ERROR, "connection already open"); + if (!m_communication || !m_communication->IsOpen()) return false; - } - - if (!m_communication->Open(strPort, 38400, iTimeoutMs)) - { - AddLog(CEC_LOG_ERROR, "could not open a connection"); - return false; - } if (!SetLogicalAddress(m_iLogicalAddress)) { - AddLog(CEC_LOG_ERROR, "could not set the logical address"); + m_controller->AddLog(CEC_LOG_ERROR, "could not set the logical address"); return false; } if (CreateThread()) return true; else - AddLog(CEC_LOG_ERROR, "could not create a processor thread"); + m_controller->AddLog(CEC_LOG_ERROR, "could not create a processor thread"); return false; } -void CCECParser::Close(void) +void *CCECProcessor::Process(void) { - StopThread(); -} - -void *CCECParser::Process(void) -{ - AddLog(CEC_LOG_DEBUG, "processor thread started"); + m_controller->AddLog(CEC_LOG_DEBUG, "processor thread started"); - int64_t now = GetTimeMs(); while (!m_bStop) { bool bParseFrame(false); @@ -124,103 +92,48 @@ void *CCECParser::Process(void) if (bParseFrame) ParseCurrentFrame(); - now = GetTimeMs(); - CheckKeypressTimeout(now); + m_controller->CheckKeypressTimeout(); CCondition::Sleep(50); } - AddLog(CEC_LOG_DEBUG, "processor thread terminated"); + m_controller->AddLog(CEC_LOG_DEBUG, "processor thread terminated"); return NULL; } -bool CCECParser::Ping(void) -{ - if (!IsRunning()) - 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)) - { - 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 (!IsRunning()) - 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)) - { - 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 */) -{ - return StandbyDevices(address); -} - -bool CCECParser::PowerOnDevices(cec_logical_address address /* = CECDEVICE_TV */) +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); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); + 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 CCECParser::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +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); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); + 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 CCECParser::SetActiveView(void) +bool CCECProcessor::SetActiveView(void) { if (!IsRunning()) return false; - AddLog(CEC_LOG_DEBUG, "setting active view"); + 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); @@ -229,12 +142,12 @@ bool CCECParser::SetActiveView(void) return Transmit(frame); } -bool CCECParser::SetInactiveView(void) +bool CCECProcessor::SetInactiveView(void) { if (!IsRunning()) return false; - AddLog(CEC_LOG_DEBUG, "setting inactive view"); + 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); @@ -243,25 +156,78 @@ bool CCECParser::SetInactiveView(void) return Transmit(frame); } -bool CCECParser::GetNextLogMessage(cec_log_message *message) +bool CCECProcessor::Transmit(const cec_frame &data, bool bWaitForAck /* = true */) { - return m_logBuffer.Pop(*message); + 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 CCECParser::GetNextKeypress(cec_keypress *key) +bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress) { - return IsRunning() ? m_keyBuffer.Pop(*key) : false; + 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 CCECParser::GetNextCommand(cec_command *command) +bool CCECProcessor::TransmitFormatted(const cec_frame &data, bool bWaitForAck /* = true */) { - return IsRunning() ? m_commandBuffer.Pop(*command) : false; + 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 CCECParser::TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) +void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) { - AddLog(CEC_LOG_DEBUG, "transmitting abort message"); + m_controller->AddLog(CEC_LOG_DEBUG, "transmitting abort message"); cec_frame frame; frame.push_back(GetSourceDestination(address)); frame.push_back(CEC_OPCODE_FEATURE_ABORT); @@ -270,23 +236,23 @@ void CCECParser::TransmitAbort(cec_logical_address address, cec_opcode opcode, E Transmit(frame); } -void CCECParser::ReportCECVersion(cec_logical_address address /* = CECDEVICE_TV */) +void CCECProcessor::ReportCECVersion(cec_logical_address address /* = CECDEVICE_TV */) { cec_frame frame; - AddLog(CEC_LOG_NOTICE, "reporting CEC version as 1.3a"); + 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 CCECParser::ReportPowerState(cec_logical_address address /*= CECDEVICE_TV */, bool bOn /* = true */) +void CCECProcessor::ReportPowerState(cec_logical_address address /*= CECDEVICE_TV */, bool bOn /* = true */) { cec_frame frame; if (bOn) - AddLog(CEC_LOG_NOTICE, "reporting \"On\" power status"); + m_controller->AddLog(CEC_LOG_NOTICE, "reporting \"On\" power status"); else - AddLog(CEC_LOG_NOTICE, "reporting \"Off\" power status"); + m_controller->AddLog(CEC_LOG_NOTICE, "reporting \"Off\" power status"); frame.push_back(GetSourceDestination(address)); frame.push_back(CEC_OPCODE_REPORT_POWER_STATUS); @@ -294,13 +260,13 @@ void CCECParser::ReportPowerState(cec_logical_address address /*= CECDEVICE_TV * Transmit(frame); } -void CCECParser::ReportMenuState(cec_logical_address address /* = CECDEVICE_TV */, bool bActive /* = true */) +void CCECProcessor::ReportMenuState(cec_logical_address address /* = CECDEVICE_TV */, bool bActive /* = true */) { cec_frame frame; if (bActive) - AddLog(CEC_LOG_NOTICE, "reporting menu state as active"); + m_controller->AddLog(CEC_LOG_NOTICE, "reporting menu state as active"); else - AddLog(CEC_LOG_NOTICE, "reporting menu state as inactive"); + m_controller->AddLog(CEC_LOG_NOTICE, "reporting menu state as inactive"); frame.push_back(GetSourceDestination(address)); frame.push_back(CEC_OPCODE_MENU_STATUS); @@ -308,19 +274,19 @@ void CCECParser::ReportMenuState(cec_logical_address address /* = CECDEVICE_TV * Transmit(frame); } -void CCECParser::ReportVendorID(cec_logical_address address /* = CECDEVICE_TV */) +void CCECProcessor::ReportVendorID(cec_logical_address address /* = CECDEVICE_TV */) { - AddLog(CEC_LOG_NOTICE, "vendor ID requested, feature abort"); + m_controller->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 */) +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); - AddLog(CEC_LOG_NOTICE, strLog.c_str()); + m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str()); frame.push_back(GetSourceDestination(address)); frame.push_back(CEC_OPCODE_SET_OSD_NAME); @@ -330,12 +296,12 @@ void CCECParser::ReportOSDName(cec_logical_address address /* = CECDEVICE_TV */) Transmit(frame); } -void CCECParser::ReportPhysicalAddress(void) +void CCECProcessor::ReportPhysicalAddress(void) { cec_frame frame; CStdString strLog; strLog.Format("reporting physical address as %04x", m_physicaladdress); - AddLog(CEC_LOG_NOTICE, strLog.c_str()); + 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); @@ -344,10 +310,10 @@ void CCECParser::ReportPhysicalAddress(void) Transmit(frame); } -void CCECParser::BroadcastActiveSource(void) +void CCECProcessor::BroadcastActiveSource(void) { cec_frame frame; - AddLog(CEC_LOG_NOTICE, "broadcasting active source"); + 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); @@ -355,69 +321,12 @@ void CCECParser::BroadcastActiveSource(void) Transmit(frame); } -bool CCECParser::TransmitFormatted(const cec_frame &data, bool bWaitForAck /* = true */) -{ - CLockObject lock(&m_mutex); - if (!m_communication || !m_communication->Write(data)) - { - return false; - } - - 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 */) +uint8_t CCECProcessor::GetSourceDestination(cec_logical_address destination /* = CECDEVICE_BROADCAST */) const { - 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); + return ((uint8_t)m_iLogicalAddress << 4) + (uint8_t)destination; } -bool CCECParser::WaitForAck(int iTimeout /* = 1000 */) +bool CCECProcessor::WaitForAck(int iTimeout /* = 1000 */) { bool bGotAck(false); bool bError(false); @@ -435,35 +344,35 @@ bool CCECParser::WaitForAck(int iTimeout /* = 1000 */) switch (iCode) { case MSGCODE_COMMAND_ACCEPTED: - AddLog(CEC_LOG_DEBUG, "MSGCODE_COMMAND_ACCEPTED"); + m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_COMMAND_ACCEPTED"); break; case MSGCODE_TRANSMIT_SUCCEEDED: - AddLog(CEC_LOG_DEBUG, "MSGCODE_TRANSMIT_SUCCEEDED"); + m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_TRANSMIT_SUCCEEDED"); // TODO bGotAck = true; break; case MSGCODE_RECEIVE_FAILED: - AddLog(CEC_LOG_WARNING, "MSGCODE_RECEIVE_FAILED"); + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_RECEIVE_FAILED"); bError = true; break; case MSGCODE_COMMAND_REJECTED: - AddLog(CEC_LOG_WARNING, "MSGCODE_COMMAND_REJECTED"); + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_COMMAND_REJECTED"); bError = true; break; case MSGCODE_TRANSMIT_FAILED_LINE: - AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_LINE"); + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_LINE"); bError = true; break; case MSGCODE_TRANSMIT_FAILED_ACK: - AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_ACK"); + m_controller->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"); + m_controller->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"); + m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE"); bError = true; break; default: @@ -478,7 +387,7 @@ bool CCECParser::WaitForAck(int iTimeout /* = 1000 */) return bGotAck && !bError; } -bool CCECParser::ParseMessage(cec_frame &msg) +bool CCECProcessor::ParseMessage(cec_frame &msg) { bool bReturn(false); @@ -493,7 +402,7 @@ bool CCECParser::ParseMessage(cec_frame &msg) switch(iCode) { case MSGCODE_NOTHING: - AddLog(CEC_LOG_DEBUG, "MSGCODE_NOTHING"); + m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_NOTHING"); break; case MSGCODE_TIMEOUT_ERROR: case MSGCODE_HIGH_ERROR: @@ -510,7 +419,7 @@ bool CCECParser::ParseMessage(cec_frame &msg) 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()); + m_controller->AddLog(CEC_LOG_WARNING, logStr.c_str()); } break; case MSGCODE_FRAME_START: @@ -525,7 +434,7 @@ bool CCECParser::ParseMessage(cec_frame &msg) m_currentframe.push_back(msg[1]); } - AddLog(CEC_LOG_DEBUG, logStr.c_str()); + m_controller->AddLog(CEC_LOG_DEBUG, logStr.c_str()); } break; case MSGCODE_FRAME_DATA: @@ -537,7 +446,7 @@ bool CCECParser::ParseMessage(cec_frame &msg) logStr.AppendFormat(" %02x", iData); m_currentframe.push_back(iData); } - AddLog(CEC_LOG_DEBUG, logStr.c_str()); + m_controller->AddLog(CEC_LOG_DEBUG, logStr.c_str()); } if (bEom) bReturn = true; @@ -549,7 +458,7 @@ bool CCECParser::ParseMessage(cec_frame &msg) return bReturn; } -void CCECParser::ParseCurrentFrame(void) +void CCECProcessor::ParseCurrentFrame(void) { uint8_t initiator = m_currentframe[0] >> 4; uint8_t destination = m_currentframe[0] & 0xF; @@ -563,7 +472,7 @@ void CCECParser::ParseCurrentFrame(void) for (unsigned int i = 1; i < m_currentframe.size(); i++) dataStr.AppendFormat(" %02x", m_currentframe[i]); } - AddLog(CEC_LOG_DEBUG, dataStr.c_str()); + m_controller->AddLog(CEC_LOG_DEBUG, dataStr.c_str()); if (m_currentframe.size() <= 1) return; @@ -596,22 +505,19 @@ void CCECParser::ParseCurrentFrame(void) case CEC_OPCODE_USER_CONTROL_PRESSED: if (m_currentframe.size() > 2) { - AddKey(); + m_controller->AddKey(); if (m_currentframe[2] <= CEC_USER_CONTROL_CODE_MAX) - { - m_iCurrentButton = (cec_user_control_code) m_currentframe[2]; - m_buttontime = GetTimeMs(); - } + m_controller->SetCurrentButton((cec_user_control_code) m_currentframe[2]); } break; case CEC_OPCODE_USER_CONTROL_RELEASE: - AddKey(); + m_controller->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); + m_controller->AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms); break; } } @@ -628,7 +534,7 @@ void CCECParser::ParseCurrentFrame(void) 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()); + m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str()); if (streamaddr == m_physicaladdress) BroadcastActiveSource(); } @@ -637,136 +543,13 @@ void CCECParser::ParseCurrentFrame(void) { cec_frame params = m_currentframe; params.erase(params.begin(), params.begin() + 2); - AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms); + 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); - AddLog(CEC_LOG_DEBUG, strLog.c_str()); + m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str()); } } - -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 > CEC_BUTTON_TIMEOUT) - { - 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_communication->Write(output)) - { - AddLog(CEC_LOG_ERROR, "could not set the ackmask"); - 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/CECParser.h b/src/lib/CECProcessor.h similarity index 63% rename from src/lib/CECParser.h rename to src/lib/CECProcessor.h index 27187c7..339653c 100644 --- a/src/lib/CECParser.h +++ b/src/lib/CECProcessor.h @@ -31,8 +31,6 @@ * http://www.pulse-eight.net/ */ -#include -#include #include "../../include/CECExports.h" #include "../../include/CECTypes.h" #include "util/threads.h" @@ -42,40 +40,24 @@ class CSerialPort; namespace CEC { + class CLibCEC; class CAdapterCommunication; - class CCECParser : public ICECDevice, public CThread + 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 void Close(void); - 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_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); virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress); - virtual bool SetAckMask(uint16_t iMask); - virtual int GetMinVersion(void); - virtual int GetLibVersion(void); - //@} - - void *Process(void); - void AddLog(cec_log_level level, const std::string &strMessage); protected: 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); @@ -86,34 +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); bool WaitForAck(int iTimeout = 1000); - bool ReadFromDevice(int iTimeout); - void ProcessMessages(void); - bool GetMessage(cec_frame &msg, bool bFromBuffer = true); 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); - 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; CMutex m_mutex; - CAdapterCommunication *m_communication; + 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 84% rename from src/lib/CECParserC.cpp rename to src/lib/LibCECC.cpp index 5d7bf61..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,11 +39,11 @@ 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); } @@ -67,53 +67,39 @@ void cec_close(void) cec_parser->Close(); } -bool cec_ping(void) +int cec_find_adapters(vector &deviceList, const char *strDevicePath /* = NULL */) { if (cec_parser) - return cec_parser->Ping(); - return false; -} - -bool cec_start_bootloader(void) -{ - if (cec_parser) - return cec_parser->StartBootloader(); - return false; -} - -bool cec_power_off_devices(cec_logical_address address /* = CECDEVICE_BROADCAST */) -{ - 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) @@ -151,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 0d015ad..47664b8 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -8,18 +8,20 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libcec.pc -libcec_la_SOURCES = CECParser.cpp \ - CECParser.h \ - CECParserC.cpp \ - CECDetect.cpp \ - CECDetect.h \ - AdapterCommunication.cpp \ +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/testclient/main.cpp b/src/testclient/main.cpp index 458c734..90158ed 100644 --- a/src/testclient/main.cpp +++ b/src/testclient/main.cpp @@ -44,7 +44,7 @@ using namespace std; #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; @@ -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,7 @@ int main (int argc, char *argv[]) CCondition::Sleep(50); } - parser->PowerOffDevices(CECDEVICE_BROADCAST); + parser->StandbyDevices(CECDEVICE_BROADCAST); parser->Close(); flush_log(parser); UnloadLibCec(parser); -- 2.34.1