From 7bb4ed43f15a0fa2be17d2c3f580b181ac7430a7 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 1 Feb 2012 00:05:40 +0100 Subject: [PATCH] cec: receive and send full cec_commands, not CCECAdapterMessages in CAdapterCommunication. extract an IAdapterCommunication interface. fixed potentially missed data in CAdapterCommunication --- include/cectypes.h | 1 - project/libcec.vcxproj | 9 +- project/libcec.vcxproj.filters | 31 +- src/lib/CECProcessor.cpp | 124 ++------ src/lib/CECProcessor.h | 14 +- src/lib/LibCEC.cpp | 5 +- src/lib/Makefile.am | 4 +- src/lib/adapter/AdapterCommunication.h | 114 ++++--- ...ion.cpp => USBCECAdapterCommunication.cpp} | 290 ++++++++++++------ src/lib/adapter/USBCECAdapterCommunication.h | 94 ++++++ ...tection.cpp => USBCECAdapterDetection.cpp} | 4 +- ...erDetection.h => USBCECAdapterDetection.h} | 2 +- ...dapterMessage.h => USBCECAdapterMessage.h} | 16 +- 13 files changed, 424 insertions(+), 284 deletions(-) rename src/lib/adapter/{AdapterCommunication.cpp => USBCECAdapterCommunication.cpp} (62%) create mode 100644 src/lib/adapter/USBCECAdapterCommunication.h rename src/lib/adapter/{AdapterDetection.cpp => USBCECAdapterDetection.cpp} (98%) rename src/lib/adapter/{AdapterDetection.h => USBCECAdapterDetection.h} (97%) rename src/lib/adapter/{AdapterMessage.h => USBCECAdapterMessage.h} (96%) diff --git a/include/cectypes.h b/include/cectypes.h index 36057eb..ddbd79f 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -874,7 +874,6 @@ typedef struct cec_logical_addresses #endif } cec_logical_addresses; - typedef int (CEC_CDECL* CBCecLogMessageType)(void *param, const CEC::cec_log_message &); typedef int (CEC_CDECL* CBCecKeyPressType)(void *param, const cec_keypress &key); typedef int (CEC_CDECL* CBCecCommandType)(void *param, const cec_command &command); diff --git a/project/libcec.vcxproj b/project/libcec.vcxproj index d667d14..0ca4cca 100644 --- a/project/libcec.vcxproj +++ b/project/libcec.vcxproj @@ -24,8 +24,9 @@ - - + + + @@ -54,8 +55,8 @@ - - + + diff --git a/project/libcec.vcxproj.filters b/project/libcec.vcxproj.filters index 3786504..a85f501 100644 --- a/project/libcec.vcxproj.filters +++ b/project/libcec.vcxproj.filters @@ -74,15 +74,6 @@ devices - - adapter - - - adapter - - - adapter - platform @@ -125,6 +116,18 @@ platform\windows + + adapter + + + adapter + + + adapter + + + adapter + @@ -161,14 +164,14 @@ devices - - adapter + + platform\windows - + adapter - - platform\windows + + adapter diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp index 6faf72c..57704bd 100644 --- a/src/lib/CECProcessor.cpp +++ b/src/lib/CECProcessor.cpp @@ -32,7 +32,7 @@ #include "CECProcessor.h" -#include "adapter/AdapterMessage.h" +#include "adapter/USBCECAdapterCommunication.h" #include "devices/CECBusDevice.h" #include "devices/CECAudioSystem.h" #include "devices/CECPlaybackDevice.h" @@ -52,7 +52,6 @@ CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, cec m_bInitialised(false), m_iHDMIPort(CEC_DEFAULT_HDMI_PORT), m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE), - m_lastInitiator(CECDEVICE_UNKNOWN), m_strDeviceName(strDeviceName), m_communication(NULL), m_controller(controller), @@ -139,7 +138,7 @@ bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint return bReturn; } - m_communication = new CAdapterCommunication(this, strPort, iBaudRate); + m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate); /* check for an already opened connection */ if (m_communication->IsOpen()) @@ -420,17 +419,13 @@ void *CCECProcessor::Process(void) { ReplaceHandlers(); command.Clear(); - msg.Clear(); { CLockObject lock(m_mutex); if (m_commandBuffer.Pop(command)) bParseFrame = true; - else if (m_communication->IsOpen() && m_communication->Read(msg, 50)) - { - if ((bParseFrame = (ParseMessage(msg) && !IsStopped())) == true) - command = m_currentframe; - } + else if (m_communication->IsOpen() && m_communication->Read(command, 50)) + bParseFrame = true; } if (bParseFrame) @@ -856,53 +851,23 @@ bool CCECProcessor::IsActiveSource(cec_logical_address iAddress) bool CCECProcessor::Transmit(const cec_command &data) { - bool bReturn(false); - LogOutput(data); - - CCECAdapterMessage *output = new CCECAdapterMessage(data); - - /* set the number of retries */ - if (data.opcode == CEC_OPCODE_NONE) - output->maxTries = 1; - else if (data.initiator != CECDEVICE_BROADCAST) - output->maxTries = m_busDevices[data.initiator]->GetHandler()->GetTransmitRetries() + 1; - - bReturn = Transmit(output); - - /* set to "not present" on failed ack */ - if (output->state == ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED && - output->Destination() != CECDEVICE_BROADCAST) - m_busDevices[output->Destination()]->SetDeviceStatus(CEC_DEVICE_STATUS_NOT_PRESENT); - - delete output; - return bReturn; -} - -bool CCECProcessor::Transmit(CCECAdapterMessage *output) -{ - bool bReturn(false); - CLockObject lock(m_mutex); + cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN); { - if (!m_communication) - return bReturn; - + CLockObject lock(m_mutex); + LogOutput(data); m_iLastTransmission = GetTimeMs(); - m_communication->SetLineTimeout(m_iStandardLineTimeout); - output->tries = 0; - - do - { - if (output->tries > 0) - m_communication->SetLineTimeout(m_iRetryLineTimeout); - bReturn = m_communication->Write(output); - if (!bReturn) - Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT); - }while (!bReturn && output->transmit_timeout > 0 && output->NeedsRetry() && ++output->tries < output->maxTries); + if (!m_communication) + return false; + uint8_t iMaxTries = m_busDevices[data.initiator]->GetHandler()->GetTransmitRetries() + 1; + retVal = m_communication->Write(data, iMaxTries, m_iLineTimeout, m_iRetryLineTimeout); } - m_communication->SetLineTimeout(m_iStandardLineTimeout); + /* set to "not present" on failed ack */ + if (retVal == ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED && + data.destination != CECDEVICE_BROADCAST) + m_busDevices[data.destination]->SetDeviceStatus(CEC_DEVICE_STATUS_NOT_PRESENT); - return bReturn; + return retVal == ADAPTER_MESSAGE_STATE_SENT_ACKED; } void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) @@ -918,57 +883,6 @@ void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode Transmit(command); } -bool CCECProcessor::ParseMessage(const CCECAdapterMessage &msg) -{ - bool bEom(false); - bool bIsError(msg.IsError()); - - if (msg.IsEmpty()) - return bEom; - - switch(msg.Message()) - { - case MSGCODE_FRAME_START: - { - m_currentframe.Clear(); - if (msg.Size() >= 2) - { - m_currentframe.initiator = msg.Initiator(); - m_currentframe.destination = msg.Destination(); - m_currentframe.ack = msg.IsACK(); - m_currentframe.eom = msg.IsEOM(); - } - if (m_currentframe.ack == 0x1) - { - m_lastInitiator = m_currentframe.initiator; - m_busDevices[m_lastInitiator]->GetHandler()->HandlePoll(m_currentframe.initiator, m_currentframe.destination); - } - } - break; - case MSGCODE_RECEIVE_FAILED: - { - if (m_lastInitiator != CECDEVICE_UNKNOWN) - bIsError = m_busDevices[m_lastInitiator]->GetHandler()->HandleReceiveFailed(); - } - break; - case MSGCODE_FRAME_DATA: - { - if (msg.Size() >= 2) - { - m_currentframe.PushBack(msg[1]); - m_currentframe.eom = msg.IsEOM(); - } - bEom = msg.IsEOM(); - } - break; - default: - break; - } - - CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString()); - return bEom; -} - void CCECProcessor::ParseCommand(cec_command &command) { CStdString dataStr; @@ -1433,11 +1347,9 @@ bool CCECProcessor::PingAdapter(void) void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination) { m_busDevices[initiator]->GetHandler()->HandlePoll(initiator, destination); - m_lastInitiator = initiator; } -bool CCECProcessor::HandleReceiveFailed(void) +bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator) { - return m_lastInitiator != CECDEVICE_UNKNOWN && - !m_busDevices[m_lastInitiator]->GetHandler()->HandleReceiveFailed(); + return !m_busDevices[initiator]->GetHandler()->HandleReceiveFailed(); } diff --git a/src/lib/CECProcessor.h b/src/lib/CECProcessor.h index a78c41f..133a70b 100644 --- a/src/lib/CECProcessor.h +++ b/src/lib/CECProcessor.h @@ -33,15 +33,13 @@ #include #include -#include "adapter/AdapterCommunication.h" +#include "platform/threads/threads.h" #include "platform/util/buffer.h" -class CSerialPort; - namespace CEC { class CLibCEC; - class CAdapterCommunication; + class IAdapterCommunication; class CCECBusDevice; class CCECProcessor : public PLATFORM::CThread @@ -114,7 +112,6 @@ namespace CEC const char *ToString(const cec_vendor_id vendor); virtual bool Transmit(const cec_command &data); - virtual bool Transmit(CCECAdapterMessage *output); virtual void TransmitAbort(cec_logical_address address, cec_opcode opcode, cec_abort_reason reason = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE); virtual bool ChangeDeviceType(cec_device_type from, cec_device_type to); @@ -124,7 +121,7 @@ namespace CEC virtual bool StartBootloader(void); virtual bool PingAdapter(void); virtual void HandlePoll(cec_logical_address initiator, cec_logical_address destination); - virtual bool HandleReceiveFailed(void); + virtual bool HandleReceiveFailed(cec_logical_address initiator); CCECBusDevice * m_busDevices[16]; PLATFORM::CMutex m_transmitMutex; @@ -144,21 +141,18 @@ namespace CEC bool FindLogicalAddressAudioSystem(void); void LogOutput(const cec_command &data); - bool ParseMessage(const CCECAdapterMessage &msg); void ParseCommand(cec_command &command); bool m_bStarted; bool m_bInitialised; uint8_t m_iHDMIPort; cec_logical_address m_iBaseDevice; - cec_command m_currentframe; cec_logical_addresses m_logicalAddresses; - cec_logical_address m_lastInitiator; std::string m_strDeviceName; cec_device_type_list m_types; PLATFORM::CMutex m_mutex; PLATFORM::CCondition m_startCondition; - CAdapterCommunication* m_communication; + IAdapterCommunication * m_communication; CLibCEC* m_controller; bool m_bMonitor; PLATFORM::SyncedBuffer m_commandBuffer; diff --git a/src/lib/LibCEC.cpp b/src/lib/LibCEC.cpp index 0048fb2..2562cfd 100644 --- a/src/lib/LibCEC.cpp +++ b/src/lib/LibCEC.cpp @@ -32,8 +32,7 @@ #include "LibCEC.h" -#include "adapter/AdapterCommunication.h" -#include "adapter/AdapterDetection.h" +#include "adapter/USBCECAdapterDetection.h" #include "CECProcessor.h" #include "devices/CECBusDevice.h" #include "platform/util/timeutils.h" @@ -112,7 +111,7 @@ int8_t CLibCEC::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const ch strDebug.Format("trying to autodetect all CEC adapters"); AddLog(CEC_LOG_DEBUG, strDebug); - return CAdapterDetection::FindAdapters(deviceList, iBufSize, strDevicePath); + return CUSBCECAdapterDetection::FindAdapters(deviceList, iBufSize, strDevicePath); } bool CLibCEC::PingAdapter(void) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index cd20766..5df7c60 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -11,8 +11,8 @@ pkgconfig_DATA = libcec.pc libcec_la_SOURCES = CECProcessor.cpp \ LibCEC.cpp \ LibCECC.cpp \ - adapter/AdapterCommunication.cpp \ - adapter/AdapterDetection.cpp \ + adapter/USBCECAdapterCommunication.cpp \ + adapter/USBCECAdapterDetection.cpp \ devices/CECAudioSystem.cpp \ devices/CECBusDevice.cpp \ devices/CECPlaybackDevice.cpp \ diff --git a/src/lib/adapter/AdapterCommunication.h b/src/lib/adapter/AdapterCommunication.h index 54f30ce..cec8501 100644 --- a/src/lib/adapter/AdapterCommunication.h +++ b/src/lib/adapter/AdapterCommunication.h @@ -31,57 +31,93 @@ * http://www.pulse-eight.net/ */ -#include -#include "../platform/threads/threads.h" -#include "../platform/util/buffer.h" #include "../platform/util/StdString.h" -namespace PLATFORM -{ - class ISocket; -} - namespace CEC { - class CCECProcessor; - class CCECAdapterMessage; + typedef enum cec_adapter_message_state + { + ADAPTER_MESSAGE_STATE_UNKNOWN = 0, /**< the initial state */ + ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT, /**< waiting in the send queue of the adapter, or timed out */ + ADAPTER_MESSAGE_STATE_SENT, /**< sent and waiting on an ACK */ + ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED, /**< sent, but failed to ACK */ + ADAPTER_MESSAGE_STATE_SENT_ACKED, /**< sent, and ACK received */ + ADAPTER_MESSAGE_STATE_INCOMING, /**< received from another device */ + ADAPTER_MESSAGE_STATE_ERROR /**< an error occured */ + } cec_adapter_message_state; - class CAdapterCommunication : private PLATFORM::CThread + class IAdapterCommunication { public: - CAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate = 38400); - virtual ~CAdapterCommunication(); + /*! + * @brief Open a connection to the CEC adapter + * @param iTimeoutMs Connection timeout in ms + * @return True when connected, false otherwise + */ + virtual bool Open(uint32_t iTimeoutMs = 10000) = 0; + + /*! + * @brief Close an open connection + */ + virtual void Close(void) = 0; + + /*! + * @return True when the connection is open, false otherwise + */ + virtual bool IsOpen(void) = 0; + + /*! + * @return The last error message, or an empty string if there was none + */ + virtual CStdString GetError(void) const = 0; + + /*! + * @brief Reads one cec_command from the adapter + * @param command The command that will be read (output) + * @param iTimeout The read timeout + * @return True when a command has been read, false otherwise. + */ + virtual bool Read(cec_command &command, uint32_t iTimeout) = 0; - bool Open(uint32_t iTimeoutMs = 10000); - bool Read(CCECAdapterMessage &msg, uint32_t iTimeout = 1000); - bool Write(CCECAdapterMessage *data); - void Close(void); - bool IsOpen(void); - CStdString GetError(void) const; + /*! + * @brief Write a cec_command to the adapter + * @param data The command to write + * @param iMaxTries The maximum number of tries + * @param iLineTimeout The line timeout for the first try + * @param iRetryLineTimeout The line timeout for each next try + * @return The last state of the transmitted command + */ + virtual cec_adapter_message_state Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout = 3, uint8_t iRetryLineTimeout = 3) = 0; - void *Process(void); + /*! + * @brief Change the current line timeout on the CEC bus + * @param iTimeout The new timeout + * @return True when set, false otherwise + */ + virtual bool SetLineTimeout(uint8_t iTimeout) = 0; - bool SetLineTimeout(uint8_t iTimeout); - bool StartBootloader(void); - bool SetAckMask(uint16_t iMask); - bool PingAdapter(void); - uint16_t GetFirmwareVersion(void); + /*! + * @brief Put the device in bootloader mode (which will disrupt CEC communication when it succeeds) + * @return True when the bootloader command has been sent, false otherwise. + */ + virtual bool StartBootloader(void) = 0; - bool WaitForAck(CCECAdapterMessage &message); + /*! + * @brief Change the ACK-mask of the device, the mask for logical addresses to which the CEC device should ACK + * @param iMask The new mask + * @return True when set, false otherwise. + */ + virtual bool SetAckMask(uint16_t iMask) = 0; - private: - void SendMessageToAdapter(CCECAdapterMessage *msg); - void WriteNextCommand(void); - void AddData(uint8_t *data, size_t iLen); - bool ReadFromDevice(uint32_t iTimeout); + /*! + * @brief Check whether the CEC adapter responds + * @return True when the ping was sent and acked, false otherwise. + */ + virtual bool PingAdapter(void) = 0; - PLATFORM::ISocket * m_port; - CCECProcessor * m_processor; - PLATFORM::SyncedBuffer m_inBuffer; - PLATFORM::SyncedBuffer m_outBuffer; - PLATFORM::CMutex m_mutex; - PLATFORM::CCondition m_rcvCondition; - uint8_t m_iLineTimeout; - uint16_t m_iFirmwareVersion; + /*! + * @return The firmware version of this CEC adapter. + */ + virtual uint16_t GetFirmwareVersion(void) = 0; }; }; diff --git a/src/lib/adapter/AdapterCommunication.cpp b/src/lib/adapter/USBCECAdapterCommunication.cpp similarity index 62% rename from src/lib/adapter/AdapterCommunication.cpp rename to src/lib/adapter/USBCECAdapterCommunication.cpp index c0f9ea1..0134751 100644 --- a/src/lib/adapter/AdapterCommunication.cpp +++ b/src/lib/adapter/USBCECAdapterCommunication.cpp @@ -30,28 +30,29 @@ * http://www.pulse-eight.net/ */ -#include "AdapterCommunication.h" - -#include "AdapterMessage.h" -#include "../CECProcessor.h" +#include "USBCECAdapterCommunication.h" #include "../platform/sockets/serialport.h" #include "../platform/util/timeutils.h" #include "../LibCEC.h" +#include "../CECProcessor.h" using namespace std; using namespace CEC; using namespace PLATFORM; -CAdapterCommunication::CAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate /* = 38400 */) : +CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate /* = 38400 */) : m_port(NULL), m_processor(processor), m_iLineTimeout(0), - m_iFirmwareVersion(CEC_FW_VERSION_UNKNOWN) + m_iFirmwareVersion(CEC_FW_VERSION_UNKNOWN), + m_lastInitiator(CECDEVICE_UNKNOWN), + m_bNextIsEscaped(false), + m_bGotStart(false) { m_port = new PLATFORM::CSerialPort(strPort, iBaudRate); } -CAdapterCommunication::~CAdapterCommunication(void) +CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void) { Close(); @@ -62,7 +63,7 @@ CAdapterCommunication::~CAdapterCommunication(void) } } -bool CAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */) +bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */) { uint64_t iNow = GetTimeMs(); uint64_t iTimeout = iNow + iTimeoutMs; @@ -122,14 +123,14 @@ bool CAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */) return false; } -void CAdapterCommunication::Close(void) +void CUSBCECAdapterCommunication::Close(void) { CLockObject lock(m_mutex); m_rcvCondition.Broadcast(); StopThread(); } -void *CAdapterCommunication::Process(void) +void *CUSBCECAdapterCommunication::Process(void) { while (!IsStopped()) { @@ -145,7 +146,36 @@ void *CAdapterCommunication::Process(void) return NULL; } -bool CAdapterCommunication::Write(CCECAdapterMessage *data) +cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout /* = 3 */, uint8_t iRetryLineTimeout /* = 3 */) +{ + cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN); + + CCECAdapterMessage *output = new CCECAdapterMessage(data); + + /* set the number of retries */ + if (data.opcode == CEC_OPCODE_NONE) //TODO + output->maxTries = 1; + else if (data.initiator != CECDEVICE_BROADCAST) + output->maxTries = iMaxTries; + + output->lineTimeout = iLineTimeout; + output->retryTimeout = iRetryLineTimeout; + output->tries = 0; + + bool bRetry(true); + while (bRetry && ++output->tries < output->maxTries) + { + bRetry = (!Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0; + if (bRetry) + Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT); + } + retVal = output->state; + + delete output; + return retVal; +} + +bool CUSBCECAdapterCommunication::Write(CCECAdapterMessage *data) { bool bReturn(false); @@ -180,69 +210,53 @@ bool CAdapterCommunication::Write(CCECAdapterMessage *data) return bReturn; } -bool CAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout) +bool CUSBCECAdapterCommunication::Read(cec_command &command, uint32_t iTimeout) { - CLockObject lock(m_mutex); - - msg.Clear(); - uint64_t iNow = GetTimeMs(); - uint64_t iTarget = iNow + iTimeout; - bool bGotFullMessage(false); - bool bNextIsEscaped(false); - bool bGotStart(false); - - while(!bGotFullMessage && iNow < iTarget) + CCECAdapterMessage msg; + if (Read(msg, iTimeout)) { - uint8_t buf = 0; - if (!m_inBuffer.Pop(buf)) + if (ParseMessage(msg)) { - if (!m_rcvCondition.Wait(m_mutex, (uint32_t) (iTarget - iNow))) - return false; + command = m_currentframe; + m_currentframe.Clear(); + return true; } + } + return false; +} - if (!bGotStart) - { - if (buf == MSGSTART) - bGotStart = true; - continue; - } - else if (buf == MSGSTART) //we found a msgstart before msgend, this is not right, remove - { - if (msg.Size() > 0) - CLibCEC::AddLog(CEC_LOG_WARNING, "received MSGSTART before MSGEND, removing previous buffer contents"); - msg.Clear(); - bGotStart = true; - } +bool CUSBCECAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout) +{ + CLockObject lock(m_mutex); - if (buf == MSGEND) - { - bGotFullMessage = true; - } - else if (bNextIsEscaped) - { - msg.PushBack(buf + (uint8_t)ESCOFFSET); - bNextIsEscaped = false; - } - else if (buf == MSGESC) - bNextIsEscaped = true; - else - msg.PushBack(buf); - } + msg.Clear(); + CCECAdapterMessage *buf(NULL); - if (bGotFullMessage) - msg.state = ADAPTER_MESSAGE_STATE_INCOMING; + if (!m_inBuffer.Pop(buf)) + { + if (!m_rcvCondition.Wait(m_mutex, iTimeout)) + return false; + m_inBuffer.Pop(buf); + } - return bGotFullMessage; + if (buf) + { + msg.packet = buf->packet; + msg.state = msg.state = ADAPTER_MESSAGE_STATE_INCOMING; + delete buf; + return true; + } + return false; } -CStdString CAdapterCommunication::GetError(void) const +CStdString CUSBCECAdapterCommunication::GetError(void) const { CStdString strError; strError = m_port->GetError(); return strError; } -bool CAdapterCommunication::StartBootloader(void) +bool CUSBCECAdapterCommunication::StartBootloader(void) { bool bReturn(false); if (!IsRunning()) @@ -264,7 +278,7 @@ bool CAdapterCommunication::StartBootloader(void) return bReturn; } -bool CAdapterCommunication::PingAdapter(void) +bool CUSBCECAdapterCommunication::PingAdapter(void) { bool bReturn(false); if (!IsRunning()) @@ -285,7 +299,59 @@ bool CAdapterCommunication::PingAdapter(void) return bReturn; } -uint16_t CAdapterCommunication::GetFirmwareVersion(void) +bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg) +{ + bool bEom(false); + bool bIsError(msg.IsError()); + + if (msg.IsEmpty()) + return bEom; + + switch(msg.Message()) + { + case MSGCODE_FRAME_START: + { + m_currentframe.Clear(); + if (msg.Size() >= 2) + { + m_currentframe.initiator = msg.Initiator(); + m_currentframe.destination = msg.Destination(); + m_currentframe.ack = msg.IsACK(); + m_currentframe.eom = msg.IsEOM(); + } + if (m_currentframe.ack == 0x1) + { + m_lastInitiator = m_currentframe.initiator; + m_processor->HandlePoll(m_currentframe.initiator, m_currentframe.destination); + } + } + break; + case MSGCODE_RECEIVE_FAILED: + { + m_currentframe.Clear(); + if (m_lastInitiator != CECDEVICE_UNKNOWN) + bIsError = m_processor->HandleReceiveFailed(m_lastInitiator); + } + break; + case MSGCODE_FRAME_DATA: + { + if (msg.Size() >= 2) + { + m_currentframe.PushBack(msg[1]); + m_currentframe.eom = msg.IsEOM(); + } + bEom = msg.IsEOM(); + } + break; + default: + break; + } + + CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString()); + return bEom; +} + +uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void) { uint16_t iReturn(m_iFirmwareVersion); if (!IsRunning()) @@ -318,29 +384,32 @@ uint16_t CAdapterCommunication::GetFirmwareVersion(void) return iReturn; } -bool CAdapterCommunication::SetLineTimeout(uint8_t iTimeout) +bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout) { - bool bReturn(m_iLineTimeout != iTimeout); - - if (!bReturn) - { - CCECAdapterMessage *output = new CCECAdapterMessage; - - output->PushBack(MSGSTART); - output->PushEscaped(MSGCODE_TRANSMIT_IDLETIME); - output->PushEscaped(iTimeout); - output->PushBack(MSGEND); - output->isTransmission = false; - - if ((bReturn = Write(output)) == false) - CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the idletime"); - delete output; - } - - return bReturn; + m_iLineTimeout = iTimeout; + return true; + //TODO +// bool bReturn(m_iLineTimeout != iTimeout); +// +// if (!bReturn) +// { +// CCECAdapterMessage *output = new CCECAdapterMessage; +// +// output->PushBack(MSGSTART); +// output->PushEscaped(MSGCODE_TRANSMIT_IDLETIME); +// output->PushEscaped(iTimeout); +// output->PushBack(MSGEND); +// output->isTransmission = false; +// +// if ((bReturn = Write(output)) == false) +// CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the idletime"); +// delete output; +// } +// +// return bReturn; } -bool CAdapterCommunication::SetAckMask(uint16_t iMask) +bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask) { bool bReturn(false); CStdString strLog; @@ -363,12 +432,12 @@ bool CAdapterCommunication::SetAckMask(uint16_t iMask) return bReturn; } -bool CAdapterCommunication::IsOpen(void) +bool CUSBCECAdapterCommunication::IsOpen(void) { return !IsStopped() && m_port->IsOpen() && IsRunning(); } -bool CAdapterCommunication::WaitForAck(CCECAdapterMessage &message) +bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message) { bool bError(false); bool bTransmitSucceeded(false); @@ -393,12 +462,14 @@ bool CAdapterCommunication::WaitForAck(CCECAdapterMessage &message) if (msg.Message() == MSGCODE_FRAME_START && msg.IsACK()) { m_processor->HandlePoll(msg.Initiator(), msg.Destination()); + m_lastInitiator = msg.Initiator(); iNow = GetTimeMs(); continue; } if (msg.Message() == MSGCODE_RECEIVE_FAILED && - m_processor->HandleReceiveFailed()) + m_lastInitiator != CECDEVICE_UNKNOWN && + m_processor->HandleReceiveFailed(m_lastInitiator)) { iNow = GetTimeMs(); continue; @@ -439,16 +510,50 @@ bool CAdapterCommunication::WaitForAck(CCECAdapterMessage &message) return bTransmitSucceeded && !bError; } -void CAdapterCommunication::AddData(uint8_t *data, size_t iLen) +void CUSBCECAdapterCommunication::AddData(uint8_t *data, size_t iLen) { CLockObject lock(m_mutex); for (size_t iPtr = 0; iPtr < iLen; iPtr++) - m_inBuffer.Push(data[iPtr]); - - m_rcvCondition.Signal(); + { + if (!m_bGotStart) + { + if (data[iPtr] == MSGSTART) + m_bGotStart = true; + } + else if (data[iPtr] == MSGSTART) //we found a msgstart before msgend, this is not right, remove + { + if (m_currentAdapterMessage.Size() > 0) + CLibCEC::AddLog(CEC_LOG_WARNING, "received MSGSTART before MSGEND, removing previous buffer contents"); + m_currentAdapterMessage.Clear(); + m_bGotStart = true; + } + else if (data[iPtr] == MSGEND) + { + CCECAdapterMessage *newMessage = new CCECAdapterMessage; + newMessage->packet = m_currentAdapterMessage.packet; + m_inBuffer.Push(newMessage); + m_currentAdapterMessage.Clear(); + m_bGotStart = false; + m_bNextIsEscaped = false; + m_rcvCondition.Signal(); + } + else if (m_bNextIsEscaped) + { + m_currentAdapterMessage.PushBack(data[iPtr] + (uint8_t)ESCOFFSET); + m_bNextIsEscaped = false; + } + else if (data[iPtr] == MSGESC) + { + m_bNextIsEscaped = true; + } + else + { + m_currentAdapterMessage.PushBack(data[iPtr]); + } + } } -bool CAdapterCommunication::ReadFromDevice(uint32_t iTimeout) +bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout) { ssize_t iBytesRead; uint8_t buff[256]; @@ -470,10 +575,15 @@ bool CAdapterCommunication::ReadFromDevice(uint32_t iTimeout) return iBytesRead > 0; } -void CAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg) +void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg) { CLockObject adapterLock(m_mutex); CLockObject lock(msg->mutex); + if (msg->tries == 1) + SetLineTimeout(msg->lineTimeout); + else + SetLineTimeout(msg->retryTimeout); + if (m_port->Write(msg->packet.data, msg->Size()) != (ssize_t) msg->Size()) { CStdString strError; @@ -489,7 +599,7 @@ void CAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg) msg->condition.Signal(); } -void CAdapterCommunication::WriteNextCommand(void) +void CUSBCECAdapterCommunication::WriteNextCommand(void) { CCECAdapterMessage *msg(NULL); if (m_outBuffer.Pop(msg)) diff --git a/src/lib/adapter/USBCECAdapterCommunication.h b/src/lib/adapter/USBCECAdapterCommunication.h new file mode 100644 index 0000000..7aa0350 --- /dev/null +++ b/src/lib/adapter/USBCECAdapterCommunication.h @@ -0,0 +1,94 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2012 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 "../platform/threads/threads.h" +#include "../platform/util/buffer.h" +#include "AdapterCommunication.h" +#include "USBCECAdapterMessage.h" + +namespace PLATFORM +{ + class ISocket; +} + +namespace CEC +{ + class CCECProcessor; + + class CUSBCECAdapterCommunication : public IAdapterCommunication, private PLATFORM::CThread + { + public: + CUSBCECAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate = 38400); + virtual ~CUSBCECAdapterCommunication(); + + virtual bool Open(uint32_t iTimeoutMs = 10000); + virtual void Close(void); + virtual bool IsOpen(void); + virtual CStdString GetError(void) const; + + bool Read(cec_command &command, uint32_t iTimeout); + cec_adapter_message_state Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout = 3, uint8_t iRetryLineTimeout = 3); + + virtual bool SetLineTimeout(uint8_t iTimeout); + virtual bool StartBootloader(void); + virtual bool SetAckMask(uint16_t iMask); + virtual bool PingAdapter(void); + virtual uint16_t GetFirmwareVersion(void); + + void *Process(void); + private: + bool Write(CCECAdapterMessage *data); + bool Read(CCECAdapterMessage &msg, uint32_t iTimeout = 1000); + bool ParseMessage(const CCECAdapterMessage &msg); + void SendMessageToAdapter(CCECAdapterMessage *msg); + void WriteNextCommand(void); + void AddData(uint8_t *data, size_t iLen); + bool ReadFromDevice(uint32_t iTimeout); + bool WaitForAck(CCECAdapterMessage &message); + + PLATFORM::ISocket * m_port; + CCECProcessor * m_processor; + PLATFORM::SyncedBuffer m_inBuffer; + PLATFORM::SyncedBuffer m_outBuffer; + PLATFORM::CMutex m_mutex; + PLATFORM::CCondition m_rcvCondition; + uint8_t m_iLineTimeout; + uint16_t m_iFirmwareVersion; + cec_command m_currentframe; + cec_logical_address m_lastInitiator; + CCECAdapterMessage m_currentAdapterMessage; + bool m_bNextIsEscaped; + bool m_bGotStart; + }; +}; diff --git a/src/lib/adapter/AdapterDetection.cpp b/src/lib/adapter/USBCECAdapterDetection.cpp similarity index 98% rename from src/lib/adapter/AdapterDetection.cpp rename to src/lib/adapter/USBCECAdapterDetection.cpp index 10b0749..0d9f8b3 100644 --- a/src/lib/adapter/AdapterDetection.cpp +++ b/src/lib/adapter/USBCECAdapterDetection.cpp @@ -30,7 +30,7 @@ * http://www.pulse-eight.net/ */ -#include "AdapterDetection.h" +#include "USBCECAdapterDetection.h" #include "../platform/util/StdString.h" #if defined(__APPLE__) @@ -112,7 +112,7 @@ bool FindComPort(CStdString &strLocation) } #endif -uint8_t CAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) +uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) { uint8_t iFound(0); diff --git a/src/lib/adapter/AdapterDetection.h b/src/lib/adapter/USBCECAdapterDetection.h similarity index 97% rename from src/lib/adapter/AdapterDetection.h rename to src/lib/adapter/USBCECAdapterDetection.h index 6478156..45b641b 100644 --- a/src/lib/adapter/AdapterDetection.h +++ b/src/lib/adapter/USBCECAdapterDetection.h @@ -35,7 +35,7 @@ namespace CEC { - class CAdapterDetection + class CUSBCECAdapterDetection { public: static uint8_t FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); diff --git a/src/lib/adapter/AdapterMessage.h b/src/lib/adapter/USBCECAdapterMessage.h similarity index 96% rename from src/lib/adapter/AdapterMessage.h rename to src/lib/adapter/USBCECAdapterMessage.h index 2ba59a1..169c115 100644 --- a/src/lib/adapter/AdapterMessage.h +++ b/src/lib/adapter/USBCECAdapterMessage.h @@ -35,18 +35,6 @@ namespace CEC { - typedef enum cec_adapter_message_state - { - ADAPTER_MESSAGE_STATE_UNKNOWN = 0, - ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT, - ADAPTER_MESSAGE_STATE_SENT, - ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED, - ADAPTER_MESSAGE_STATE_SENT_ACKED, - ADAPTER_MESSAGE_STATE_INCOMING, - ADAPTER_MESSAGE_STATE_ERROR - } cec_adapter_message_state; - - class CCECAdapterMessage { public: @@ -265,6 +253,8 @@ namespace CEC reply = MSGCODE_NOTHING; isTransmission = true; expectControllerAck = true; + lineTimeout = 3; + retryTimeout = 3; } void Shift(uint8_t iShiftBy) @@ -358,6 +348,8 @@ namespace CEC int32_t transmit_timeout; bool isTransmission; bool expectControllerAck; + uint8_t lineTimeout; + uint8_t retryTimeout; PLATFORM::CMutex mutex; PLATFORM::CCondition condition; }; -- 2.34.1