cec: fix ACK handling after d297cbd4fc30bc93836532a9a31027b7e64150b3. bugzid: 591
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
index 09a3bb2029de81b4774f8c41294014c18398e29d..fb91d957c2244afb425fb56d3fe30c4aeaf9a3ca 100644 (file)
@@ -72,6 +72,8 @@ CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(CCECProcessor *processo
     m_messageProcessor(NULL),
     m_bInitialised(false)
 {
+  for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
+    m_bWaitingForAck[iPtr] = false;
   m_port = new CSerialPort(strPort, iBaudRate);
 }
 
@@ -181,9 +183,20 @@ bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32
     {
       //clear any input bytes
       uint8_t buff[1024];
-      while (m_port->Read(buff, 1024, 100) > 0)
+      ssize_t iBytesRead(0);
+      bool bGotMsgStart(false), bGotMsgEnd(false);
+      while ((iBytesRead = m_port->Read(buff, 1024, 100)) > 0 || (bGotMsgStart && !bGotMsgEnd))
       {
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it");
+        if (!bGotMsgStart)
+          CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it");
+        // if something was received, wait for MSGEND
+        for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
+        {
+          if (buff[iPtr] == MSGSTART)
+            bGotMsgStart = true;
+          else if (buff[iPtr] == MSGEND)
+            bGotMsgEnd = true;
+        }
         Sleep(250);
       }
     }
@@ -292,6 +305,11 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
   output->retryTimeout = iRetryLineTimeout;
   output->tries = 0;
 
+  {
+    CLockObject lock(m_mutex);
+    m_bWaitingForAck[data.destination] = true;
+  }
+
   bool bRetry(true);
   while (bRetry && ++output->tries < output->maxTries)
   {
@@ -415,8 +433,16 @@ bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg)
       }
       if (m_currentframe.ack == 0x1)
       {
-        m_lastInitiator = m_currentframe.initiator;
-        m_processor->HandlePoll(m_currentframe.initiator, m_currentframe.destination);
+        m_lastInitiator    = m_currentframe.initiator;
+        if (!m_bWaitingForAck[m_currentframe.destination])
+        {
+          m_currentframe.eom = 1;
+          bEom = true;
+        }
+        else
+        {
+          m_bWaitingForAck[m_currentframe.destination] = false;
+        }
       }
     }
     break;
@@ -441,7 +467,7 @@ bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg)
   }
 
   CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
-  return msg.IsEOM();
+  return msg.IsEOM() || bEom;
 }
 
 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
@@ -684,7 +710,7 @@ bool CUSBCECAdapterCommunication::GetSettingLogicalAddressMask(uint16_t &iMask)
 bool CUSBCECAdapterCommunication::SetSettingPhysicalAddress(uint16_t iPhysicalAddress)
 {
   CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the physical address to %2X", iPhysicalAddress);
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the physical address to %04X", iPhysicalAddress);
 
   CCECAdapterMessage params;
   params.PushEscaped(iPhysicalAddress >> 8);
@@ -794,8 +820,13 @@ bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message)
 
     if (msg.Message() == MSGCODE_FRAME_START && msg.IsACK())
     {
-      m_processor->HandlePoll(msg.Initiator(), msg.Destination());
-      m_lastInitiator = msg.Initiator();
+      if (m_bWaitingForAck[msg.Initiator()])
+        m_bWaitingForAck[msg.Initiator()] = false;
+      else
+      {
+        m_processor->HandlePoll(msg.Initiator(), msg.Destination());
+        m_lastInitiator = msg.Initiator();
+      }
       iNow = GetTimeMs();
       continue;
     }
@@ -967,7 +998,7 @@ CStdString CUSBCECAdapterCommunication::GetPortName(void)
   return strName;
 }
 
-bool CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bExpectAck /* = true */, bool bIsTransmission /* = false */, bool bSendDirectly /* = true */)
+bool CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bExpectAck /* = true */, bool bIsTransmission /* = false */, bool bSendDirectly /* = true */, bool bIsRetry /* = false */)
 {
   CLockObject lock(m_mutex);
 
@@ -990,6 +1021,13 @@ bool CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, C
   {
     CLibCEC::AddLog(CEC_LOG_ERROR, "'%s' failed", CCECAdapterMessage::ToString(msgCode));
     delete output;
+
+    if (!bIsRetry && output->reply == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED)
+    {
+      CLibCEC::AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
+      if (SetControlledMode(true))
+        return SendCommand(msgCode, params, bExpectAck, bIsTransmission, bSendDirectly, true);
+    }
     return false;
   }