using namespace CEC;
using namespace PLATFORM;
+#define CEC_ADAPTER_PING_TIMEOUT 15000
+
+void *CUSBCECAdapterProcessor::Process(void)
+{
+ cec_command command;
+ while (!IsStopped())
+ {
+ if (m_inBuffer.Pop(command))
+ m_callback->OnCommandReceived(command);
+ Sleep(5);
+ }
+
+ return NULL;
+}
+
+void CUSBCECAdapterProcessor::AddCommand(cec_command command)
+{
+ m_inBuffer.Push(command);
+}
+
CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
m_port(NULL),
m_processor(processor),
+ m_bHasData(false),
m_iLineTimeout(0),
m_iFirmwareVersion(CEC_FW_VERSION_UNKNOWN),
m_lastInitiator(CECDEVICE_UNKNOWN),
m_bNextIsEscaped(false),
- m_bGotStart(false)
+ m_bGotStart(false),
+ m_messageProcessor(NULL),
+ m_bInitialised(false)
{
m_port = new PLATFORM::CSerialPort(strPort, iBaudRate);
}
while (iNow < iTarget && (bPinged = PingAdapter()) == false)
{
CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
- Sleep(500);
+ CEvent::Sleep(500);
iNow = GetTimeMs();
}
/* try to read the firmware version */
m_iFirmwareVersion = CEC_FW_VERSION_UNKNOWN;
unsigned iFwVersionTry(0);
- while (bPinged && iNow < iTarget && (m_iFirmwareVersion = GetFirmwareVersion()) == CEC_FW_VERSION_UNKNOWN)
+ while (bPinged && iNow < iTarget && (m_iFirmwareVersion = GetFirmwareVersion()) == CEC_FW_VERSION_UNKNOWN && iFwVersionTry < 3)
{
- CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond with a correct firmware version (try %d)", ++iFwVersionTry);
- Sleep(500);
+ CLibCEC::AddLog(CEC_LOG_WARNING, "the adapter did not respond with a correct firmware version (try %d)", ++iFwVersionTry);
+ CEvent::Sleep(500);
iNow = GetTimeMs();
}
+ if (m_iFirmwareVersion == CEC_FW_VERSION_UNKNOWN)
+ {
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "defaulting to firmware version 1");
+ m_iFirmwareVersion = 1;
+ }
+
if (m_iFirmwareVersion >= 2)
{
/* try to set controlled mode */
while (iNow < iTarget && (bControlled = SetControlledMode(true)) == false)
{
CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
- Sleep(500);
+ CEvent::Sleep(500);
iNow = GetTimeMs();
}
bReturn = bControlled;
else
bReturn = true;
+ {
+ CLockObject lock(m_mutex);
+ m_bInitialised = bReturn;
+ }
+
return bReturn;
}
-bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */)
+bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */)
{
uint64_t iNow = GetTimeMs();
uint64_t iTimeout = iNow + iTimeoutMs;
CLibCEC::AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
- //clear any input bytes
- uint8_t buff[1024];
- while (m_port->Read(buff, 1024, 100) > 0)
+ if (!bSkipChecks)
{
- CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it");
- Sleep(250);
+ //clear any input bytes
+ uint8_t buff[1024];
+ while (m_port->Read(buff, 1024, 100) > 0)
+ {
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it");
+ Sleep(250);
+ }
}
}
- if (CreateThread())
+ if (!bSkipChecks && !CheckAdapter())
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
+ return false;
+ }
+ else
{
- if (!CheckAdapter())
+ if (CreateThread())
{
- StopThread();
- CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
+ return true;
}
else
{
- CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
- return true;
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
}
}
- CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
return false;
}
void CUSBCECAdapterCommunication::Close(void)
{
- SetAckMask(0);
- CLockObject lock(m_mutex);
- m_rcvCondition.Broadcast();
StopThread();
}
void *CUSBCECAdapterCommunication::Process(void)
{
+ m_messageProcessor = new CUSBCECAdapterProcessor(m_callback);
+ m_messageProcessor->CreateThread();
+
cec_command command;
+ command.Clear();
bool bCommandReceived(false);
+ CTimeout pingTimeout(CEC_ADAPTER_PING_TIMEOUT);
while (!IsStopped())
{
{
CLockObject lock(m_mutex);
ReadFromDevice(50);
- bCommandReceived = m_callback && Read(command, 0);
+ bCommandReceived = m_callback && Read(command, 0) && m_bInitialised;
}
/* push the next command to the callback method if there is one */
if (!IsStopped() && bCommandReceived)
- m_callback->OnCommandReceived(command);
+ m_messageProcessor->AddCommand(command);
+
+ /* ping the adapter every 15 seconds */
+ if (pingTimeout.TimeLeft() == 0)
+ {
+ pingTimeout.Init(CEC_ADAPTER_PING_TIMEOUT);
+ PingAdapter();
+ }
if (!IsStopped())
{
}
}
+ /* stop the message processor */
+ m_messageProcessor->StopThread();
+ delete m_messageProcessor;
+
+ /* notify all threads that are waiting on messages to be sent */
CCECAdapterMessage *msg(NULL);
- if (m_outBuffer.Pop(msg))
- msg->condition.Broadcast();
+ while (m_outBuffer.Pop(msg))
+ msg->event.Broadcast();
+
+ /* set the ackmask to 0 before closing the connection */
+ SetAckMaskInternal(0, true);
+
+ if (m_iFirmwareVersion >= 2)
+ SetControlledMode(false);
if (m_port)
{
bool CUSBCECAdapterCommunication::Write(CCECAdapterMessage *data)
{
- CLockObject lock(data->mutex);
data->state = ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT;
m_outBuffer.Push(data);
- data->condition.Wait(data->mutex);
+ data->event.Wait(5000);
if ((data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT_ACKED) ||
(!data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT))
if (!m_inBuffer.Pop(buf))
{
- if (iTimeout == 0 || !m_rcvCondition.Wait(m_mutex, iTimeout))
+ if (iTimeout == 0 || !m_rcvCondition.Wait(m_mutex, m_bHasData, iTimeout))
return false;
m_inBuffer.Pop(buf);
+ m_bHasData = !m_inBuffer.IsEmpty();
}
if (buf)
bool CUSBCECAdapterCommunication::PingAdapter(void)
{
- bool bReturn(false);
- if (!IsRunning())
- return bReturn;
-
+ CLockObject lock(m_mutex);
CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping");
+
CCECAdapterMessage *output = new CCECAdapterMessage;
output->PushBack(MSGSTART);
output->PushBack(MSGEND);
output->isTransmission = false;
- if ((bReturn = Write(output)) == false)
- CLibCEC::AddLog(CEC_LOG_ERROR, "could not ping the adapter");
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
delete output;
+ if (!bWriteOk)
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not ping the adapter");
+ return false;
+ }
- return bReturn;
+ return true;
}
bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg)
if (msg.IsEmpty())
return bEom;
+ CLockObject adapterLock(m_mutex);
switch(msg.Message())
{
case MSGCODE_FRAME_START:
m_currentframe.PushBack(msg[1]);
m_currentframe.eom = msg.IsEOM();
}
- bEom = msg.IsEOM();
}
break;
default:
}
CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
- return bEom;
+ return msg.IsEOM();
}
uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
{
uint16_t iReturn(m_iFirmwareVersion);
- if (!IsRunning())
- return iReturn;
if (iReturn == CEC_FW_VERSION_UNKNOWN)
{
}
bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
+{
+ return SetAckMaskInternal(iMask, false);
+}
+
+bool CUSBCECAdapterCommunication::SetAckMaskInternal(uint16_t iMask, bool bWriteDirectly /* = false */)
{
bool bReturn(false);
CLibCEC::AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask);
output->PushBack(MSGEND);
output->isTransmission = false;
- if ((bReturn = Write(output)) == false)
+ if (bWriteDirectly)
+ SendMessageToAdapter(output);
+ else if ((bReturn = Write(output)) == false)
CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the ackmask");
delete output;
return bReturn;
}
+bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
+{
+ if (m_iFirmwareVersion < 2)
+ return false;
+
+ bool bReturn(true);
+ bReturn &= SetAutoEnabled(true);
+ bReturn &= SetDefaultLogicalAddress(configuration->logicalAddresses.primary);
+ bReturn &= SetLogicalAddressMask(configuration->logicalAddresses.AckMask());
+ bReturn &= SetPhysicalAddress(configuration->iPhysicalAddress);
+ bReturn &= SetCECVersion(CEC_VERSION_1_3A);
+ bReturn &= SetOSDName(configuration->strDeviceName);
+ if (bReturn)
+ bReturn = WriteEEPROM();
+ return bReturn;
+}
bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
{
- bool bReturn(false);
+ CLockObject lock(m_mutex);
CLibCEC::AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off");
CCECAdapterMessage *output = new CCECAdapterMessage;
output->PushBack(MSGEND);
output->isTransmission = false;
- if ((bReturn = Write(output)) == false)
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+ delete output;
+ if (!bWriteOk)
+ {
CLibCEC::AddLog(CEC_LOG_ERROR, "could not set controlled mode");
+ return false;
+ }
+
+ return true;
+}
+
+bool CUSBCECAdapterCommunication::SetAutoEnabled(bool enabled)
+{
+ CLockObject lock(m_mutex);
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "turning autonomous mode %s", enabled ? "on" : "off");
+
+ CCECAdapterMessage *output = new CCECAdapterMessage;
+
+ output->PushBack(MSGSTART);
+ output->PushEscaped(MSGCODE_SET_AUTO_ENABLED);
+ output->PushEscaped(enabled);
+ output->PushBack(MSGEND);
+ output->isTransmission = false;
+
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
delete output;
+ if (!bWriteOk)
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not set autonomous mode");
+ return false;
+ }
- return bReturn;
+ return true;
+}
+
+bool CUSBCECAdapterCommunication::SetDefaultLogicalAddress(cec_logical_address address)
+{
+ CLockObject lock(m_mutex);
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the default logical address to %1X", address);
+
+ CCECAdapterMessage *output = new CCECAdapterMessage;
+
+ output->PushBack(MSGSTART);
+ output->PushEscaped(MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS);
+ output->PushEscaped((uint8_t) address);
+ output->PushBack(MSGEND);
+ output->isTransmission = false;
+
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+ delete output;
+ if (!bWriteOk)
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the default logical address");
+ return false;
+ }
+
+ return true;
+}
+
+bool CUSBCECAdapterCommunication::SetLogicalAddressMask(uint16_t iMask)
+{
+ CLockObject lock(m_mutex);
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the logical address mask to %2X", iMask);
+
+ CCECAdapterMessage *output = new CCECAdapterMessage;
+
+ output->PushBack(MSGSTART);
+ output->PushEscaped(MSGCODE_SET_LOGICAL_ADDRESS_MASK);
+ output->PushEscaped(iMask >> 8);
+ output->PushEscaped((uint8_t)iMask);
+ output->PushBack(MSGEND);
+ output->isTransmission = false;
+
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+ delete output;
+ if (!bWriteOk)
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the logical address mask");
+ return false;
+ }
+
+ return true;
+}
+
+bool CUSBCECAdapterCommunication::SetPhysicalAddress(uint16_t iPhysicalAddress)
+{
+ CLockObject lock(m_mutex);
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the physical address to %2X", iPhysicalAddress);
+
+ CCECAdapterMessage *output = new CCECAdapterMessage;
+
+ output->PushBack(MSGSTART);
+ output->PushEscaped(MSGCODE_SET_PHYSICAL_ADDRESS);
+ output->PushEscaped(iPhysicalAddress >> 8);
+ output->PushEscaped((uint8_t)iPhysicalAddress);
+ output->PushBack(MSGEND);
+ output->isTransmission = false;
+
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+ delete output;
+ if (!bWriteOk)
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the physical address");
+ return false;
+ }
+
+ return true;
+}
+
+bool CUSBCECAdapterCommunication::SetCECVersion(cec_version version)
+{
+ CLockObject lock(m_mutex);
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the CEC version to %s", CLibCEC::GetInstance()->ToString(version));
+
+ CCECAdapterMessage *output = new CCECAdapterMessage;
+
+ output->PushBack(MSGSTART);
+ output->PushEscaped(MSGCODE_SET_HDMI_VERSION);
+ output->PushEscaped((uint8_t)version);
+ output->PushBack(MSGEND);
+ output->isTransmission = false;
+
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+ delete output;
+ if (!bWriteOk)
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the CEC version");
+ return false;
+ }
+
+ return true;
+}
+
+bool CUSBCECAdapterCommunication::SetOSDName(const char *strOSDName)
+{
+ CLockObject lock(m_mutex);
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the OSD name to %s", strOSDName);
+
+ CCECAdapterMessage *output = new CCECAdapterMessage;
+
+ output->PushBack(MSGSTART);
+ output->PushEscaped(MSGCODE_SET_OSD_NAME);
+ for (size_t iPtr = 0; iPtr < strlen(strOSDName); iPtr++)
+ output->PushEscaped(strOSDName[iPtr]);
+ output->PushBack(MSGEND);
+ output->isTransmission = false;
+
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+ delete output;
+ if (!bWriteOk)
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the OSD name");
+ return false;
+ }
+
+ return true;
+}
+
+bool CUSBCECAdapterCommunication::WriteEEPROM(void)
+{
+ CLockObject lock(m_mutex);
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "writing settings in the EEPROM");
+
+ CCECAdapterMessage *output = new CCECAdapterMessage;
+
+ output->PushBack(MSGSTART);
+ output->PushEscaped(MSGCODE_WRITE_EEPROM);
+ output->PushBack(MSGEND);
+ output->isTransmission = false;
+
+ SendMessageToAdapter(output);
+ bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+ delete output;
+ if (!bWriteOk)
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "could not write the settings in the EEPROM");
+ return false;
+ }
+
+ return true;
}
bool CUSBCECAdapterCommunication::IsOpen(void)
switch(msg.Message())
{
case MSGCODE_COMMAND_ACCEPTED:
- CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString());
if (iPacketsLeft > 0)
iPacketsLeft--;
if (!message.isTransmission && iPacketsLeft == 0)
bTransmitSucceeded = true;
+ CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - waiting for %d more", msg.ToString().c_str(), iPacketsLeft);
break;
case MSGCODE_TRANSMIT_SUCCEEDED:
CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString());
m_currentAdapterMessage.Clear();
m_bGotStart = false;
m_bNextIsEscaped = false;
- m_rcvCondition.Signal();
+ m_bHasData = true;
+ m_rcvCondition.Broadcast();
}
else if (m_bNextIsEscaped)
{
if (iBytesRead < 0 || iBytesRead > 256)
{
CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
+ StopThread(false);
return false;
}
else if (iBytesRead > 0)
void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg)
{
CLockObject adapterLock(m_mutex);
- CLockObject lock(msg->mutex);
+ if (!m_port->IsOpen())
+ {
+ CLibCEC::AddLog(CEC_LOG_ERROR, "error writing to serial port: the connection is closed");
+ msg->state = ADAPTER_MESSAGE_STATE_ERROR;
+ return;
+ }
+
if (msg->tries == 1)
SetLineTimeout(msg->lineTimeout);
else
CLibCEC::AddLog(CEC_LOG_DEBUG, "did not receive ack");
}
}
- msg->condition.Signal();
+ msg->event.Signal();
}
void CUSBCECAdapterCommunication::WriteNextCommand(void)
if (m_outBuffer.Pop(msg))
SendMessageToAdapter(msg);
}
+
+CStdString CUSBCECAdapterCommunication::GetPortName(void)
+{
+ CStdString strName;
+ strName = m_port->GetName();
+ return strName;
+}