X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Flib%2Fadapter%2FPulse-Eight%2FUSBCECAdapterCommunication.cpp;h=2e9c790a7342cce914c28eb29c4e8ba031c33712;hb=f7539eaf1ed0a488c0a93998c9b178d435014c51;hp=053860efd209fe4ede64969c69ddd2fb53564d8f;hpb=1dcf634481e61910daa99fdd3bbafcc55b429477;p=deb_libcec.git diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index 053860e..2e9c790 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -1,7 +1,7 @@ /* * 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 Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. * libCEC(R) is an original work, containing original code. * * libCEC(R) is a trademark of Pulse-Eight Limited. @@ -36,6 +36,7 @@ #include "USBCECAdapterCommands.h" #include "USBCECAdapterMessageQueue.h" #include "USBCECAdapterMessage.h" +#include "USBCECAdapterDetection.h" #include "lib/platform/sockets/serialport.h" #include "lib/platform/util/timeutils.h" #include "lib/platform/util/util.h" @@ -53,10 +54,13 @@ using namespace PLATFORM; #define CEC_ADAPTER_EEPROM_WRITE_INTERVAL 30000 #define CEC_ADAPTER_EEPROM_WRITE_RETRY 5000 -// 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 version 3 +#define CEC_LATEST_ADAPTER_FW_VERSION 3 +// firmware date Thu Nov 15 11:09:45 2012 +#define CEC_LATEST_ADAPTER_FW_DATE 0x50a4cd79 + +#define CEC_FW_DATE_EXTENDED_RESPONSE 0x501a4b0c +#define CEC_FW_DATE_DESCRIPTOR2 0x5045dbf5 #define LIB_CEC m_callback->GetLib() @@ -67,10 +71,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 +174,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 +223,11 @@ void CUSBCECAdapterCommunication::Close(void) m_adapterMessageQueue->Clear(); + /* stop and delete the write thread */ + if (m_eepromWriteThread) + m_eepromWriteThread->Stop(); + DELETE_AND_NULL(m_eepromWriteThread); + /* stop and delete the ping thread */ DELETE_AND_NULL(m_pingThread); @@ -218,24 +236,33 @@ void CUSBCECAdapterCommunication::Close(void) m_port->Close(); } -cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool UNUSED(bIsReply)) +cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool bIsReply) { cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN); if (!IsRunning()) return retVal; CCECAdapterMessage *output = new CCECAdapterMessage(data, iLineTimeout); + output->bFireAndForget = bIsReply; /* mark as waiting for an ack from the destination */ MarkAsWaiting(data.destination); /* send the message */ - bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0; - if (bRetry) - Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT); - retVal = output->state; + if (bIsReply) + { + retVal = m_adapterMessageQueue->Write(output) ? + ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT : ADAPTER_MESSAGE_STATE_ERROR; + } + else + { + bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0; + if (bRetry) + Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT); + retVal = output->state; - delete output; + delete output; + } return retVal; } @@ -244,7 +271,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,33 +283,9 @@ 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); + if (!IsStopped()) + Sleep(5); } m_adapterMessageQueue->Clear(); @@ -380,12 +382,13 @@ bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message) { LIB_CEC->AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str(), m_port->GetError().c_str()); message->state = ADAPTER_MESSAGE_STATE_ERROR; - // this will trigger an alert in the reader thread - m_port->Close(); + // let the higher level close the port return false; } +#ifdef CEC_DEBUGGING LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message())); +#endif message->state = ADAPTER_MESSAGE_STATE_SENT; return true; } @@ -403,12 +406,16 @@ bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize if (!IsOpen()) return false; - iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout); + do { + /* retry Read() if it was interrupted */ + iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout); + } while(m_port->GetErrorNumber() == EINTR); + if (m_port->GetErrorNumber()) { LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str()); - m_port->Close(); + // let the higher level close the port return false; } } @@ -492,8 +499,14 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFA else bReturn = true; - /* try to read the build date */ - m_commands->RequestBuildDate(); + if (m_commands->GetFirmwareVersion() >= 2) + { + /* try to read the build date */ + m_commands->RequestBuildDate(); + + /* try to read the adapter type */ + m_commands->RequestAdapterType(); + } SetInitialised(bReturn); return bReturn; @@ -571,47 +584,66 @@ 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; +} + +cec_adapter_type CUSBCECAdapterCommunication::GetAdapterType(void) +{ + cec_adapter_type type(ADAPTERTYPE_UNKNOWN); + if (m_commands) + type = (cec_adapter_type)m_commands->GetPersistedAdapterType(); + if (type == ADAPTERTYPE_UNKNOWN && IsOpen()) + type = (cec_adapter_type)m_commands->RequestAdapterType(); + + return type; +} + +bool CUSBCECAdapterCommunication::ProvidesExtendedResponse(void) +{ + uint32_t iBuildDate(0); + if (m_commands) + iBuildDate = m_commands->GetPersistedBuildDate(); + + return iBuildDate >= CEC_FW_DATE_EXTENDED_RESPONSE; +} + +uint16_t CUSBCECAdapterCommunication::GetAdapterVendorId(void) const +{ + return CEC_VID; +} + +uint16_t CUSBCECAdapterCommunication::GetAdapterProductId(void) const +{ + uint32_t iBuildDate(0); + if (m_commands) + iBuildDate = m_commands->GetPersistedBuildDate(); + + return iBuildDate >= CEC_FW_DATE_DESCRIPTOR2 ? CEC_PID2 : CEC_PID; +} + +void CUSBCECAdapterCommunication::SetActiveSource(bool bSetTo, bool bClientUnregistered) +{ + if (m_commands) + m_commands->SetActiveSource(bSetTo, bClientUnregistered); } 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) @@ -696,11 +728,74 @@ void *CAdapterPingThread::Process(void) /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */ m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection."); m_com->StopThread(false); + + libcec_parameter param; + param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN; + m_com->LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param); + break; } } - 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_bWrite = true; + 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)) + { + if (IsStopped()) + break; + 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; +}