/*
* 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.
#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"
#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 0x4F99ACB9
+// 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()
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++)
}
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");
+ }
}
}
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);
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;
}
CCECAdapterMessage msg;
LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
- bool bWriteEeprom(false);
while (!IsStopped())
{
/* read from the serial port */
break;
}
- // check if we need to do another eeprom write
- {
- CLockObject lock(m_mutex);
- uint64_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();
m_lastPollDestination = msg.Destination();
if (msg.Destination() < CECDEVICE_BROADCAST)
{
+ CLockObject waitingLock(m_waitingMutex);
if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
{
if (m_callback)
/* mark as waiting for an ack from the destination */
if (dest < CECDEVICE_BROADCAST)
{
- CLockObject lock(m_mutex);
+ CLockObject waitingLock(m_waitingMutex);
m_bWaitingForAck[dest] = true;
}
}
{
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;
}
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;
}
}
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;
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)
/* 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;
+}