From 3ead056c503f92e9c4352c41d51e613d4fa7b00e Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Thu, 2 Aug 2012 12:11:05 +0200 Subject: [PATCH] p8: match to the response provided by the firmware when checking responses (added in firmware v2 rev6). do delayed eeprom writes async, not in process, or it'll block processing of other input --- .../USBCECAdapterCommunication.cpp | 176 +++++++++++------- .../Pulse-Eight/USBCECAdapterCommunication.h | 31 ++- .../Pulse-Eight/USBCECAdapterMessage.cpp | 18 +- .../Pulse-Eight/USBCECAdapterMessage.h | 5 + .../Pulse-Eight/USBCECAdapterMessageQueue.cpp | 85 ++++++++- .../Pulse-Eight/USBCECAdapterMessageQueue.h | 5 + 6 files changed, 232 insertions(+), 88 deletions(-) diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index 053860e..b524dd8 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -55,8 +55,10 @@ using namespace PLATFORM; // firmware version 2 #define CEC_LATEST_ADAPTER_FW_VERSION 2 -// firmware date Thu Apr 26 20:14:49 2012 +0000 -#define CEC_LATEST_ADAPTER_FW_DATE 0x5009F0A3 +// firmware date Thu Aug 2 08:31:24 UTC 2012 +#define CEC_LATEST_ADAPTER_FW_DATE 0x501a4b0c + +#define CEC_FW_DATE_EXTENDED_RESPONSE 0x501a4b0c #define LIB_CEC m_callback->GetLib() @@ -67,10 +69,9 @@ CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCa m_lastPollDestination(CECDEVICE_UNKNOWN), m_bInitialised(false), m_pingThread(NULL), + m_eepromWriteThread(NULL), m_commands(NULL), - m_adapterMessageQueue(NULL), - m_iLastEepromWrite(0), - m_iScheduleEepromWrite(0) + m_adapterMessageQueue(NULL) { m_logicalAddresses.Clear(); for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++) @@ -171,17 +172,27 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONN } else if (bStartListening) { - /* start a ping thread, that will ping the adapter every 15 seconds - if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */ - m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT); - if (m_pingThread->CreateThread()) + /* start the eeprom write thread, that handles all eeprom writes async */ + m_eepromWriteThread = new CAdapterEepromWriteThread(this); + if (!m_eepromWriteThread->CreateThread()) { - bConnectionOpened = true; + bConnectionOpened = false; + LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create the eeprom write thread"); } else { - bConnectionOpened = false; - LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread"); + /* start a ping thread, that will ping the adapter every 15 seconds + if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */ + m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT); + if (m_pingThread->CreateThread()) + { + bConnectionOpened = true; + } + else + { + bConnectionOpened = false; + LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread"); + } } } @@ -210,6 +221,10 @@ void CUSBCECAdapterCommunication::Close(void) m_adapterMessageQueue->Clear(); + /* stop and delete the write thread */ + m_eepromWriteThread->Stop(); + DELETE_AND_NULL(m_eepromWriteThread); + /* stop and delete the ping thread */ DELETE_AND_NULL(m_pingThread); @@ -244,7 +259,6 @@ void *CUSBCECAdapterCommunication::Process(void) CCECAdapterMessage msg; LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started"); - bool bWriteEeprom(false); while (!IsStopped()) { /* read from the serial port */ @@ -257,31 +271,6 @@ void *CUSBCECAdapterCommunication::Process(void) break; } - // check if we need to do another eeprom write - { - CLockObject lock(m_mutex); - int64_t iNow = GetTimeMs(); - if (m_iScheduleEepromWrite > 0 && - m_iScheduleEepromWrite >= iNow) - { - m_iScheduleEepromWrite = 0; - m_iLastEepromWrite = iNow; - bWriteEeprom = true; - } - } - - if (bWriteEeprom) - { - LIB_CEC->AddLog(CEC_LOG_DEBUG, "updating the eeprom (scheduled)"); - bWriteEeprom = false; - if (!m_commands->WriteEEPROM()) - { - // failed, retry later - CLockObject lock(m_mutex); - m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY; - } - } - /* TODO sleep 5 ms so other threads can get a lock */ Sleep(5); } @@ -571,47 +560,35 @@ uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void) uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void) { - return IsOpen() ? m_commands->RequestBuildDate() : m_commands ? m_commands->GetPersistedBuildDate() : 0; + uint32_t iBuildDate(0); + if (m_commands) + iBuildDate = m_commands->GetPersistedBuildDate(); + if (iBuildDate == 0 && IsOpen()) + iBuildDate = m_commands->RequestBuildDate(); + + return iBuildDate; +} + +bool CUSBCECAdapterCommunication::ProvidesExtendedResponse(void) +{ + uint32_t iBuildDate(0); + if (m_commands) + iBuildDate = m_commands->GetPersistedBuildDate(); + + return iBuildDate >= CEC_FW_DATE_EXTENDED_RESPONSE; } bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void) { - return GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION && - GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE; + return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE && + GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION; } bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration) { - if (IsOpen()) - { - // returns true when something changed - if (m_commands->PersistConfiguration(configuration)) - { - { - CLockObject lock(m_mutex); - uint64_t iNow = GetTimeMs(); - if (iNow - m_iLastEepromWrite < CEC_ADAPTER_EEPROM_WRITE_INTERVAL) - { - // if there was more than 1 write within the last 30 seconds, schedule another one - if (m_iScheduleEepromWrite == 0) - m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL; - return true; - } - else - { - m_iLastEepromWrite = iNow; - } - } - - if (!m_commands->WriteEEPROM()) - { - // write failed, retry later - CLockObject lock(m_mutex); - m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY; - } - } - } - return IsOpen() ? m_commands->PersistConfiguration(configuration) : false; + return IsOpen() ? + m_commands->PersistConfiguration(configuration) && m_eepromWriteThread->Write() : + false; } bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration) @@ -700,7 +677,62 @@ void *CAdapterPingThread::Process(void) } } - Sleep(500); + Sleep(5); + } + return NULL; +} + +void CAdapterEepromWriteThread::Stop(void) +{ + StopThread(-1); + { + CLockObject lock(m_mutex); + if (m_iScheduleEepromWrite > 0) + m_com->LIB_CEC->AddLog(CEC_LOG_WARNING, "write thread stopped while a write was queued"); + m_condition.Signal(); + } + StopThread(); +} + +void *CAdapterEepromWriteThread::Process(void) +{ + while (!IsStopped()) + { + CLockObject lock(m_mutex); + if ((m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite < GetTimeMs()) || + m_condition.Wait(m_mutex, m_bWrite, 100)) + { + m_bWrite = false; + if (m_com->m_commands->WriteEEPROM()) + { + m_iLastEepromWrite = GetTimeMs(); + m_iScheduleEepromWrite = 0; + } + else + { + m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY; + } + } } return NULL; } + +bool CAdapterEepromWriteThread::Write(void) +{ + CLockObject lock(m_mutex); + if (m_iScheduleEepromWrite == 0) + { + int64_t iNow = GetTimeMs(); + if (m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL > iNow) + { + m_com->LIB_CEC->AddLog(CEC_LOG_DEBUG, "delaying eeprom write by %ld ms", m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL - iNow); + m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL; + } + else + { + m_bWrite = true; + m_condition.Signal(); + } + } + return true; +} diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.h b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.h index 5b065f3..1144d0e 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.h +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.h @@ -43,6 +43,7 @@ namespace CEC { class CCECProcessor; class CAdapterPingThread; + class CAdapterEepromWriteThread; class CUSBCECAdapterCommands; class CCECAdapterMessageQueue; class CCECAdapterMessage; @@ -51,6 +52,7 @@ namespace CEC { friend class CUSBCECAdapterCommands; friend class CCECAdapterMessageQueue; + friend class CAdapterEepromWriteThread; public: /*! @@ -86,6 +88,8 @@ namespace CEC bool SupportsSourceLogicalAddress(const cec_logical_address UNUSED(address)) { return true; } ///} + bool ProvidesExtendedResponse(void); + void *Process(void); private: @@ -169,11 +173,32 @@ namespace CEC bool m_bInitialised; /**< true when the connection is initialised, false otherwise */ bool m_bWaitingForAck[15]; /**< array in which we store from which devices we're expecting acks */ CAdapterPingThread * m_pingThread; /**< ping thread, that pings the adapter every 15 seconds */ + CAdapterEepromWriteThread * m_eepromWriteThread; /**< eeprom writes are done async */ CUSBCECAdapterCommands * m_commands; /**< commands that can be sent to the adapter */ CCECAdapterMessageQueue * m_adapterMessageQueue; /**< the incoming and outgoing message queue */ cec_logical_addresses m_logicalAddresses; /**< the logical address list that this instance is using */ - int64_t m_iLastEepromWrite; /**< last time that this instance did an eeprom write */ - int64_t m_iScheduleEepromWrite; /**< in case there were more than 2 changes within 30 seconds, do another write at this time */ + }; + + class CAdapterEepromWriteThread : public PLATFORM::CThread + { + public: + CAdapterEepromWriteThread(CUSBCECAdapterCommunication *com) : + m_com(com), + m_bWrite(false), + m_iLastEepromWrite(0), + m_iScheduleEepromWrite(0) {} + virtual ~CAdapterEepromWriteThread(void) {} + + bool Write(void); + void* Process(void); + void Stop(void); + private: + CUSBCECAdapterCommunication *m_com; + bool m_bWrite; + PLATFORM::CCondition m_condition; + PLATFORM::CMutex m_mutex; + int64_t m_iLastEepromWrite; /**< last time that this instance did an eeprom write */ + int64_t m_iScheduleEepromWrite; /**< in case there were more than 2 changes within 30 seconds, do another write at this time */ }; class CAdapterPingThread : public PLATFORM::CThread @@ -184,7 +209,7 @@ namespace CEC m_timeout(iTimeout){} virtual ~CAdapterPingThread(void) {} - virtual void* Process(void); + void* Process(void); private: CUSBCECAdapterCommunication *m_com; PLATFORM::CTimeout m_timeout; diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterMessage.cpp b/src/lib/adapter/Pulse-Eight/USBCECAdapterMessage.cpp index f213c1d..08e6c35 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterMessage.cpp +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterMessage.cpp @@ -126,9 +126,14 @@ std::string CCECAdapterMessage::ToString(void) const strMsg.AppendFormat(" %02x %s", At(2), IsEOM() ? "eom" : ""); break; default: - for (uint8_t iPtr = 2; iPtr < Size(); iPtr++) - if (At(iPtr) != MSGEND) - strMsg.AppendFormat(" %02x", At(iPtr)); + if (Size() >= 2 && (Message() == MSGCODE_COMMAND_ACCEPTED || Message() == MSGCODE_COMMAND_REJECTED)) + strMsg.AppendFormat(": %s", ToString((cec_adapter_messagecode)At(2))); + else + { + for (uint8_t iPtr = 2; iPtr < Size(); iPtr++) + if (At(iPtr) != MSGEND) + strMsg.AppendFormat(" %02x", At(iPtr)); + } break; } } @@ -329,6 +334,13 @@ cec_adapter_messagecode CCECAdapterMessage::Message(void) const MSGCODE_NOTHING; } +cec_adapter_messagecode CCECAdapterMessage::ResponseTo(void) const +{ + return packet.size >= 3 ? + (cec_adapter_messagecode) (packet.At(2) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) : + MSGCODE_NOTHING; +} + bool CCECAdapterMessage::IsTranmission(void) const { cec_adapter_messagecode msgCode = Message(); diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterMessage.h b/src/lib/adapter/Pulse-Eight/USBCECAdapterMessage.h index e4fedce..40ee1a4 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterMessage.h +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterMessage.h @@ -127,6 +127,11 @@ namespace CEC */ cec_adapter_messagecode Message(void) const; + /*! + * @return The messagecode (if provided) that this message is responding to + */ + cec_adapter_messagecode ResponseTo(void) const; + /*! * @return True when this message is a transmission, false otherwise. */ diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp b/src/lib/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp index c713eea..b05a20e 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp @@ -113,21 +113,76 @@ cec_adapter_messagecode CCECAdapterMessageQueueEntry::MessageCode(void) return m_message->Message(); } -bool CCECAdapterMessageQueueEntry::IsResponse(const CCECAdapterMessage &msg) +bool CCECAdapterMessageQueueEntry::IsResponseOld(const CCECAdapterMessage &msg) { cec_adapter_messagecode msgCode = msg.Message(); + return msgCode == MessageCode() || - (m_message->IsTranmission() && msgCode == MSGCODE_TIMEOUT_ERROR) || msgCode == MSGCODE_COMMAND_ACCEPTED || msgCode == MSGCODE_COMMAND_REJECTED || - (m_message->IsTranmission() && msgCode == MSGCODE_HIGH_ERROR) || - (m_message->IsTranmission() && msgCode == MSGCODE_LOW_ERROR) || - (m_message->IsTranmission() && msgCode == MSGCODE_RECEIVE_FAILED) || - (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_LINE) || - (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_ACK) || - (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA) || - (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE) || - (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_SUCCEEDED); + (m_message->IsTranmission() && (msgCode == MSGCODE_TIMEOUT_ERROR || + msgCode == MSGCODE_HIGH_ERROR || + msgCode == MSGCODE_LOW_ERROR || + msgCode == MSGCODE_RECEIVE_FAILED || + msgCode == MSGCODE_TRANSMIT_FAILED_LINE || + msgCode == MSGCODE_TRANSMIT_FAILED_ACK || + msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA || + msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE || + msgCode == MSGCODE_TRANSMIT_SUCCEEDED)); +} + +bool CCECAdapterMessageQueueEntry::IsResponse(const CCECAdapterMessage &msg) +{ + cec_adapter_messagecode thisMsgCode = m_message->Message(); + cec_adapter_messagecode msgCode = msg.Message(); + cec_adapter_messagecode msgResponse = msg.ResponseTo(); + + // msgcode matches, always a response + if (msgCode == MessageCode()) + return true; + + if (!ProvidesExtendedResponse()) + return IsResponseOld(msg); + + // response without a msgcode + if (msgResponse == MSGCODE_NOTHING) + { + m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_WARNING, "no response code received"); + return true; + } + + // commands that only repond with accepted/rejected + if (thisMsgCode == MSGCODE_PING || + thisMsgCode == MSGCODE_SET_ACK_MASK || + thisMsgCode == MSGCODE_SET_CONTROLLED || + thisMsgCode == MSGCODE_SET_AUTO_ENABLED || + thisMsgCode == MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS || + thisMsgCode == MSGCODE_SET_LOGICAL_ADDRESS_MASK || + thisMsgCode == MSGCODE_SET_PHYSICAL_ADDRESS || + thisMsgCode == MSGCODE_SET_DEVICE_TYPE || + thisMsgCode == MSGCODE_SET_HDMI_VERSION || + thisMsgCode == MSGCODE_SET_OSD_NAME || + thisMsgCode == MSGCODE_WRITE_EEPROM || + thisMsgCode == MSGCODE_TRANSMIT_IDLETIME) + return thisMsgCode == msgResponse; + + if (!m_message->IsTranmission()) + { + m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_WARNING, "FIXME! not a transmission"); + return false; + } + + return ((msgCode == MSGCODE_COMMAND_ACCEPTED || msgCode == MSGCODE_COMMAND_REJECTED) && + (msgResponse == MSGCODE_TRANSMIT_ACK_POLARITY || msgResponse == MSGCODE_TRANSMIT || msgResponse == MSGCODE_TRANSMIT_EOM)) || + msgCode == MSGCODE_TIMEOUT_ERROR || + msgCode == MSGCODE_HIGH_ERROR || + msgCode == MSGCODE_LOW_ERROR || + msgCode == MSGCODE_RECEIVE_FAILED || + msgCode == MSGCODE_TRANSMIT_FAILED_LINE || + msgCode == MSGCODE_TRANSMIT_FAILED_ACK || + msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA || + msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE || + msgCode == MSGCODE_TRANSMIT_SUCCEEDED; } const char *CCECAdapterMessageQueueEntry::ToString(void) const @@ -216,6 +271,11 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedResponse(const CCECAdapterMess return true; } +bool CCECAdapterMessageQueueEntry::ProvidesExtendedResponse(void) +{ + return m_queue && m_queue->ProvidesExtendedResponse(); +} + CCECAdapterMessageQueue::CCECAdapterMessageQueue(CUSBCECAdapterCommunication *com) : PLATFORM::CThread(), m_com(com), @@ -359,3 +419,8 @@ bool CCECAdapterMessageQueue::Write(CCECAdapterMessage *msg) return bReturn; } + +bool CCECAdapterMessageQueue::ProvidesExtendedResponse(void) +{ + return m_com && m_com->ProvidesExtendedResponse(); +} diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h b/src/lib/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h index 6e325a7..edd34be 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h @@ -82,6 +82,7 @@ namespace CEC * @return True when it's a response, false otherwise. */ bool IsResponse(const CCECAdapterMessage &msg); + bool IsResponseOld(const CCECAdapterMessage &msg); /*! * @return The command that was sent in human readable form. @@ -114,6 +115,8 @@ namespace CEC */ void Signal(void); + bool ProvidesExtendedResponse(void); + CCECAdapterMessageQueue * m_queue; CCECAdapterMessage * m_message; /**< the message that was sent */ uint8_t m_iPacketsLeft; /**< the amount of acks that we're waiting on */ @@ -162,6 +165,8 @@ namespace CEC */ bool Write(CCECAdapterMessage *msg); + bool ProvidesExtendedResponse(void); + virtual void *Process(void); private: -- 2.34.1