cec: only wait for multiple 'command accepted' response when sending a transmit comma...
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
index 2fe62fedcc134cab6cfb6ce3894ba16f93cd3735..20f480b09b97310728c0f1322a9757b57bcfc9a9 100644 (file)
@@ -40,6 +40,8 @@ using namespace std;
 using namespace CEC;
 using namespace PLATFORM;
 
+#define CEC_ADAPTER_PING_TIMEOUT 15000
+
 void *CUSBCECAdapterProcessor::Process(void)
 {
   cec_command command;
@@ -97,13 +99,19 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */
   /* 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);
+    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 */
@@ -215,6 +223,7 @@ void *CUSBCECAdapterCommunication::Process(void)
   cec_command command;
   command.Clear();
   bool bCommandReceived(false);
+  CTimeout pingTimeout(CEC_ADAPTER_PING_TIMEOUT);
   while (!IsStopped())
   {
     {
@@ -227,6 +236,13 @@ void *CUSBCECAdapterCommunication::Process(void)
     if (!IsStopped() && bCommandReceived)
       m_messageProcessor->AddCommand(command);
 
+    /* ping the adapter every 15 seconds */
+    if (pingTimeout.TimeLeft() == 0)
+    {
+      pingTimeout.Init(CEC_ADAPTER_PING_TIMEOUT);
+      PingAdapter();
+    }
+
     if (!IsStopped())
     {
       Sleep(5);
@@ -246,6 +262,9 @@ void *CUSBCECAdapterCommunication::Process(void)
   /* set the ackmask to 0 before closing the connection */
   SetAckMaskInternal(0, true);
 
+  if (m_iFirmwareVersion >= 2)
+    SetControlledMode(false);
+
   if (m_port)
   {
     delete m_port;
@@ -550,6 +569,23 @@ bool CUSBCECAdapterCommunication::SetAckMaskInternal(uint16_t iMask, bool bWrite
   return bReturn;
 }
 
+bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
+{
+  if (m_iFirmwareVersion < 2)
+    return false;
+
+  bool bReturn(true);
+  bReturn &= SetAutoEnabled(true);
+  bReturn &= SetDeviceType(CLibCEC::GetType(configuration->logicalAddresses.primary));
+  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)
 {
@@ -565,7 +601,7 @@ bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
   output->isTransmission = false;
 
   SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT;
+  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
   delete output;
   if (!bWriteOk)
   {
@@ -576,6 +612,208 @@ bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
   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 true;
+}
+
+bool CUSBCECAdapterCommunication::SetDeviceType(cec_device_type type)
+{
+  CLockObject lock(m_mutex);
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the device type to %1X", (uint8_t)type);
+
+  CCECAdapterMessage *output = new CCECAdapterMessage;
+
+  output->PushBack(MSGSTART);
+  output->PushEscaped(MSGCODE_SET_DEVICE_TYPE);
+  output->PushEscaped((uint8_t)type);
+  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 device type");
+    return false;
+  }
+
+  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)
 {
   return !IsStopped() && m_port->IsOpen() && IsRunning();
@@ -585,7 +823,7 @@ bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message)
 {
   bool bError(false);
   bool bTransmitSucceeded(false);
-  uint8_t iPacketsLeft(message.Size() / 4);
+  uint8_t iPacketsLeft(message.isTransmission ? message.Size() / 4 : 1);
 
   int64_t iNow = GetTimeMs();
   int64_t iTargetTime = iNow + (message.transmit_timeout <= 5 ? CEC_DEFAULT_TRANSMIT_WAIT : message.transmit_timeout);