Add bShutdownOnStandby to libcec_configuration. bugzid: 660
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
index 82c4252d27eb314d993c3178357cc18dfddc026e..e9d4d9333e399430a008b72ccf779bc3d3117f86 100644 (file)
@@ -42,44 +42,23 @@ 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_lastDestination(CECDEVICE_UNKNOWN),
     m_bNextIsEscaped(false),
     m_bGotStart(false),
-    m_messageProcessor(NULL),
-    m_bInitialised(false)
+    m_bInitialised(false),
+    m_pingThread(NULL)
 {
+  for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
+    m_bWaitingForAck[iPtr] = false;
   m_port = new CSerialPort(strPort, iBaudRate);
 }
 
-CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
-{
-  Close();
-}
-
 bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */)
 {
   bool bReturn(false);
@@ -136,7 +115,7 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */
   return bReturn;
 }
 
-bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */)
+bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
 {
   uint64_t iNow = GetTimeMs();
   uint64_t iTimeout = iNow + iTimeoutMs;
@@ -181,9 +160,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);
       }
     }
@@ -192,73 +182,63 @@ bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32
   if (!bSkipChecks && !CheckAdapter())
   {
     CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
+    delete m_port;
+    m_port = NULL;
     return false;
   }
-  else
+  else if (bStartListening)
   {
-    if (CreateThread())
+    m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT);
+    if (CreateThread() && m_pingThread->CreateThread())
     {
       CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
       return true;
     }
     else
     {
+      delete m_port;
+      m_port = NULL;
       CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
+      return false;
     }
   }
+  else
+  {
+    delete m_port;
+    m_port = NULL;
+  }
 
-  return false;
+  return true;
 }
 
 void CUSBCECAdapterCommunication::Close(void)
 {
-  StopThread();
+  if (m_pingThread)
+    m_pingThread->StopThread(0);
+  delete m_pingThread;
+  m_pingThread = NULL;
+  StopThread(0);
 }
 
 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) && m_bInitialised;
+      bCommandReceived = m_callback && Read(command, 50) && m_bInitialised;
     }
 
     /* push the next command to the callback method if there is one */
     if (!IsStopped() && bCommandReceived)
-      m_messageProcessor->AddCommand(command);
-
-    /* ping the adapter every 15 seconds */
-    if (pingTimeout.TimeLeft() == 0)
-    {
-      pingTimeout.Init(CEC_ADAPTER_PING_TIMEOUT);
-      PingAdapter();
-    }
+      m_callback->OnCommandReceived(command);
 
-    if (!IsStopped())
-    {
-      Sleep(5);
-      WriteNextCommand();
-    }
+    Sleep(5);
   }
 
-  /* stop the message processor */
-  m_messageProcessor->StopThread();
-  delete m_messageProcessor;
-
-  /* notify all threads that are waiting on messages to be sent */
-  CCECAdapterMessage *msg(NULL);
-  while (m_outBuffer.Pop(msg))
-    msg->event.Broadcast();
-
   /* set the ackmask to 0 before closing the connection */
   SetAckMaskInternal(0, true);
 
@@ -271,6 +251,7 @@ void *CUSBCECAdapterCommunication::Process(void)
     m_port = NULL;
   }
 
+  m_rcvCondition.Broadcast();
   return NULL;
 }
 
@@ -292,6 +273,12 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
   output->retryTimeout = iRetryLineTimeout;
   output->tries = 0;
 
+  if (data.destination < 15)
+  {
+    CLockObject lock(m_mutex);
+    m_bWaitingForAck[data.destination] = true;
+  }
+
   bool bRetry(true);
   while (bRetry && ++output->tries < output->maxTries)
   {
@@ -308,8 +295,7 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
 bool CUSBCECAdapterCommunication::Write(CCECAdapterMessage *data)
 {
   data->state = ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT;
-  m_outBuffer.Push(data);
-  data->event.Wait(5000);
+  SendMessageToAdapter(data);
 
   if ((data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT_ACKED) ||
       (!data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT))
@@ -339,9 +325,10 @@ bool CUSBCECAdapterCommunication::Read(cec_command &command, uint32_t iTimeout)
   return false;
 }
 
-bool CUSBCECAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout)
+bool CUSBCECAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout, size_t iLen)
 {
   CLockObject lock(m_mutex);
+  ReadFromDevice(iTimeout, iLen);
 
   msg.Clear();
   CCECAdapterMessage *buf(NULL);
@@ -385,7 +372,6 @@ bool CUSBCECAdapterCommunication::StartBootloader(void)
 
 bool CUSBCECAdapterCommunication::PingAdapter(void)
 {
-  CLockObject lock(m_mutex);
   CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping");
 
   CCECAdapterMessage params;
@@ -415,16 +401,22 @@ 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_lastDestination    = m_currentframe.destination;
+        if (m_currentframe.destination < 15)
+        {
+          if (!m_bWaitingForAck[m_currentframe.destination])
+            m_processor->HandlePoll(m_currentframe.initiator, m_currentframe.destination);
+          else
+            m_bWaitingForAck[m_currentframe.destination] = false;
+        }
       }
     }
     break;
   case MSGCODE_RECEIVE_FAILED:
     {
       m_currentframe.Clear();
-      if (m_lastInitiator != CECDEVICE_UNKNOWN)
-        bIsError = m_processor->HandleReceiveFailed(m_lastInitiator);
+      if (m_lastDestination != CECDEVICE_UNKNOWN)
+        bIsError = m_processor->HandleReceiveFailed(m_lastDestination);
     }
     break;
   case MSGCODE_FRAME_DATA:
@@ -466,15 +458,16 @@ uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
 
 bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
 {
-  m_iLineTimeout = iTimeout;
-  bool bReturn(m_iLineTimeout != iTimeout);
+  bool bReturn(true);
 
-  if (!bReturn)
+  if (m_iLineTimeout != iTimeout)
   {
     CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the line timeout to %d", iTimeout);
     CCECAdapterMessage params;
     params.PushEscaped(iTimeout);
     bReturn = SendCommand(MSGCODE_TRANSMIT_IDLETIME, params);
+    if (bReturn)
+      m_iLineTimeout = iTimeout;
   }
 
   return bReturn;
@@ -515,6 +508,7 @@ bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *con
 
 bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
 {
+  configuration->iFirmwareVersion = m_iFirmwareVersion;
   if (m_iFirmwareVersion < 2)
     return false;
 
@@ -684,7 +678,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);
@@ -784,9 +778,8 @@ bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message)
 
   while (!bTransmitSucceeded && !bError && iNow < iTargetTime)
   {
-    ReadFromDevice(50);
     CCECAdapterMessage msg;
-    if (!Read(msg, 0))
+    if (!Read(msg, 50))
     {
       iNow = GetTimeMs();
       continue;
@@ -794,15 +787,20 @@ 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 (msg.Initiator() < 15 && m_bWaitingForAck[msg.Initiator()])
+        m_bWaitingForAck[msg.Initiator()] = false;
+      else if (msg.Initiator() < 15)
+      {
+        m_processor->HandlePoll(msg.Initiator(), msg.Destination());
+        m_lastDestination = msg.Initiator();
+      }
       iNow = GetTimeMs();
       continue;
     }
 
     if (msg.Message() == MSGCODE_RECEIVE_FAILED &&
-        m_lastInitiator != CECDEVICE_UNKNOWN &&
-        m_processor->HandleReceiveFailed(m_lastInitiator))
+        m_lastDestination != CECDEVICE_UNKNOWN &&
+        m_processor->HandleReceiveFailed(m_lastDestination))
     {
       iNow = GetTimeMs();
       continue;
@@ -950,14 +948,6 @@ void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg)
         CLibCEC::AddLog(CEC_LOG_DEBUG, "did not receive ack");
     }
   }
-  msg->event.Signal();
-}
-
-void CUSBCECAdapterCommunication::WriteNextCommand(void)
-{
-  CCECAdapterMessage *msg(NULL);
-  if (m_outBuffer.Pop(msg))
-    SendMessageToAdapter(msg);
 }
 
 CStdString CUSBCECAdapterCommunication::GetPortName(void)
@@ -967,14 +957,14 @@ 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);
 
   CCECAdapterMessage *output = new CCECAdapterMessage;
 
   output->PushBack(MSGSTART);
-  output->PushEscaped(msgCode);
+  output->PushEscaped((uint8_t)msgCode);
   output->Append(params);
   output->PushBack(MSGEND);
   output->isTransmission = bIsTransmission;
@@ -986,14 +976,22 @@ bool CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, C
     Write(output);
 
   bool bWriteOk = output->state == (output->expectControllerAck ? ADAPTER_MESSAGE_STATE_SENT_ACKED : ADAPTER_MESSAGE_STATE_SENT);
+  cec_adapter_messagecode reply = output->reply;
+  delete output;
+
   if (!bWriteOk)
   {
     CLibCEC::AddLog(CEC_LOG_ERROR, "'%s' failed", CCECAdapterMessage::ToString(msgCode));
-    delete output;
+
+    if (!bIsRetry && 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;
   }
 
-  delete output;
   return true;
 }
 
@@ -1009,10 +1007,8 @@ cec_datapacket CUSBCECAdapterCommunication::GetSetting(cec_adapter_messagecode m
     return retVal;
   }
 
-  Sleep(250); // TODO ReadFromDevice() isn't waiting for the timeout to pass on win32
-  ReadFromDevice(CEC_DEFAULT_TRANSMIT_WAIT, iResponseLength + 3 /* start + msgcode + iResponseLength + end */);
   CCECAdapterMessage input;
-  if (Read(input, 0))
+  if (Read(input, CEC_DEFAULT_TRANSMIT_WAIT, iResponseLength + 3 /* start + msgcode + iResponseLength + end */))
   {
     if (input.Message() != msgCode)
       CLibCEC::AddLog(CEC_LOG_ERROR, "invalid response to %s received (%s)", CCECAdapterMessage::ToString(msgCode), CCECAdapterMessage::ToString(input.Message()));
@@ -1029,3 +1025,18 @@ cec_datapacket CUSBCECAdapterCommunication::GetSetting(cec_adapter_messagecode m
 
   return retVal;
 }
+
+void *CAdapterPingThread::Process(void)
+{
+  while (!IsStopped())
+  {
+    if (m_timeout.TimeLeft() == 0)
+    {
+      m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
+      m_com->PingAdapter();
+    }
+
+    Sleep(500);
+  }
+  return NULL;
+}