cec: return false when SetHDMIPort() is called with invalid arguments
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
index 5e4a1740bfc0d285c37a69dcf0503eefdcd6218e..1d3df13c907541d4863a902644f395bec06cfae1 100644 (file)
@@ -44,7 +44,7 @@ using namespace PLATFORM;
 
 #define CEC_ADAPTER_PING_TIMEOUT 15000
 
-CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
+CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */) :
     IAdapterCommunication(callback),
     m_port(NULL),
     m_iLineTimeout(0),
@@ -64,9 +64,10 @@ CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
   Close();
   delete m_commands;
   delete m_adapterMessageQueue;
+  delete m_port;
 }
 
-bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
+bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
 {
   bool bConnectionOpened(false);
   {
@@ -91,7 +92,10 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool b
       m_commands = new CUSBCECAdapterCommands(this);
 
     if (!m_adapterMessageQueue)
+    {
       m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
+      m_adapterMessageQueue->CreateThread();
+    }
 
     /* try to open the connection */
     CStdString strError;
@@ -145,58 +149,57 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool b
 
   if (!bConnectionOpened || !bStartListening)
     StopThread(0);
-  if (!bConnectionOpened)
-  {
-    delete m_port;
-    m_port = NULL;
-  }
 
   return bConnectionOpened;
 }
 
 void CUSBCECAdapterCommunication::Close(void)
 {
+  /* stop the reader thread */
+  StopThread(0);
+
+  CLockObject lock(m_mutex);
+
   /* set the ackmask to 0 before closing the connection */
-  if (IsRunning())
+  if (IsRunning() && m_port->IsOpen() && m_port->GetErrorNumber() == 0)
   {
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__);
     SetAckMask(0);
     if (m_commands->GetFirmwareVersion() >= 2)
       SetControlledMode(false);
   }
 
+  m_adapterMessageQueue->Clear();
+
   /* stop and delete the ping thread */
   if (m_pingThread)
     m_pingThread->StopThread(0);
   delete m_pingThread;
   m_pingThread = NULL;
 
-  /* stop the reader thread */
-  StopThread(0);
-
   /* close and delete the com port connection */
-  delete m_port;
-  m_port = NULL;
+  if (m_port)
+    m_port->Close();
+
+  libcec_parameter param;
+  CLibCEC::Alert(CEC_ALERT_CONNECTION_LOST, param);
 }
 
-cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout /* = 3 */, uint8_t iRetryLineTimeout /* = 3 */)
+cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout)
 {
   cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN);
   if (!IsRunning())
     return retVal;
 
-  CCECAdapterMessage *output = new CCECAdapterMessage(data, iMaxTries, iLineTimeout, iRetryLineTimeout);
+  CCECAdapterMessage *output = new CCECAdapterMessage(data, iLineTimeout);
 
   /* mark as waiting for an ack from the destination */
   MarkAsWaiting(data.destination);
 
   /* send the message */
-  bool bRetry(true);
-  while (bRetry && ++output->tries < output->maxTries)
-  {
-    bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
-    if (bRetry)
-      Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
-  }
+  bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
+  if (bRetry)
+    Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
   retVal = output->state;
 
   delete output;
@@ -263,7 +266,7 @@ void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
   }
 }
 
-void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = 1000 */)
+void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
 {
   CTimeout timeout(iTimeout);
   uint8_t buff[1024];
@@ -302,7 +305,7 @@ bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
   CLockObject adapterLock(m_mutex);
   if (!m_port->IsOpen())
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to the serial port: the connection is closed", CCECAdapterMessage::ToString(message->Message()));
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': the connection is closed", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str());
     message->state = ADAPTER_MESSAGE_STATE_ERROR;
     return false;
   }
@@ -310,8 +313,9 @@ bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
   /* write the message */
   if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to the serial port: %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetError().c_str());
+    CLibCEC::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;
+    Close();
     return false;
   }
 
@@ -330,17 +334,21 @@ bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize
   /* read from the serial port */
   {
     CLockObject lock(m_mutex);
-    if (!m_port)
+    if (!m_port || !m_port->IsOpen())
       return false;
+
     iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
+
+    if (m_port->GetErrorNumber())
+    {
+      CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
+      m_port->Close();
+      return false;
+    }
   }
 
   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)
   {
     /* add the data to the current frame */
@@ -366,12 +374,14 @@ CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_message
   /* write the command */
   if (!m_adapterMessageQueue->Write(output))
   {
-    // timed out
+    if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
+      Close();
     return output;
   }
   else
   {
-    if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED)
+    if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
+        msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
     {
       /* if the controller reported that the command was rejected, and we didn't send the command
          to set controlled mode, then the controller probably switched to auto mode. set controlled
@@ -386,7 +396,7 @@ CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_message
   return output;
 }
 
-bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */)
+bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
 {
   bool bReturn(false);
   CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
@@ -416,6 +426,9 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */
   else
     bReturn = true;
 
+  /* try to read the build date */
+  m_commands->RequestBuildDate();
+
   SetInitialised(bReturn);
   return bReturn;
 }
@@ -445,20 +458,17 @@ bool CUSBCECAdapterCommunication::IsInitialised(void)
 
 bool CUSBCECAdapterCommunication::StartBootloader(void)
 {
-  if (!IsRunning())
-    return false;
-
-  return m_commands->StartBootloader();
+  return m_port->IsOpen() ? m_commands->StartBootloader() : false;
 }
 
 bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
 {
-  return m_commands->SetAckMask(iMask);
+  return m_port->IsOpen() ? m_commands->SetAckMask(iMask) : false;
 }
 
 bool CUSBCECAdapterCommunication::PingAdapter(void)
 {
-  return m_commands->PingAdapter();
+  return m_port->IsOpen() ? m_commands->PingAdapter() : false;
 }
 
 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
@@ -466,14 +476,19 @@ uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
   return m_commands->GetFirmwareVersion();
 }
 
+uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
+{
+  return m_commands->RequestBuildDate();
+}
+
 bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
 {
-  return m_commands->PersistConfiguration(configuration);
+  return m_port->IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
 }
 
 bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
 {
-  return m_commands->GetConfiguration(configuration);
+  return m_port->IsOpen() ? m_commands->GetConfiguration(configuration) : false;
 }
 
 CStdString CUSBCECAdapterCommunication::GetPortName(void)
@@ -483,7 +498,7 @@ CStdString CUSBCECAdapterCommunication::GetPortName(void)
 
 bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
 {
-  return m_commands->SetControlledMode(controlled);
+  return m_port->IsOpen() ? m_commands->SetControlledMode(controlled) : false;
 }
 
 void *CAdapterPingThread::Process(void)
@@ -502,8 +517,8 @@ void *CAdapterPingThread::Process(void)
       {
         if (!m_com->PingAdapter())
         {
-          /* sleep 1 second and retry */
-          Sleep(1000);
+          /* sleep and retry */
+          Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
           ++iFailedCounter;
         }
         else