cec-config: fix physical address detection
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
index f5bdc2f443dada92e3a4b0d57d87dc57b39d383c..3e3ba0845f7064dbc9076e15557f15db9ba93250 100644 (file)
@@ -44,7 +44,14 @@ using namespace PLATFORM;
 
 #define CEC_ADAPTER_PING_TIMEOUT 15000
 
-CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
+// firmware version 2
+#define CEC_LATEST_ADAPTER_FW_VERSION 2
+// firmware date Thu Apr 26 20:14:49 2012 +0000
+#define CEC_LATEST_ADAPTER_FW_DATE    0x4F99ACB9
+
+#define LIB_CEC m_callback->GetLib()
+
+CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */) :
     IAdapterCommunication(callback),
     m_port(NULL),
     m_iLineTimeout(0),
@@ -52,9 +59,10 @@ CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCa
     m_bInitialised(false),
     m_pingThread(NULL),
     m_commands(NULL),
-    m_adapterMessageQueue(NULL)
+    m_adapterMessageQueue(NULL),
+    m_iAckMask(0xFFFF)
 {
-  for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
+  for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
     m_bWaitingForAck[iPtr] = false;
   m_port = new CSerialPort(strPort, iBaudRate);
 }
@@ -67,7 +75,7 @@ CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
   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);
   {
@@ -76,14 +84,14 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool b
     /* we need the port settings here */
     if (!m_port)
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "port is NULL");
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "port is NULL");
       return bConnectionOpened;
     }
 
     /* return true when the port is already open */
     if (IsOpen())
     {
-      CLibCEC::AddLog(CEC_LOG_WARNING, "port is already open");
+      LIB_CEC->AddLog(CEC_LOG_WARNING, "port is already open");
       return true;
     }
 
@@ -92,7 +100,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;
@@ -110,23 +121,23 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool b
     /* return false when we couldn't connect */
     if (!bConnectionOpened)
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, strError);
+      LIB_CEC->AddLog(CEC_LOG_ERROR, strError);
       return false;
     }
 
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
     ClearInputBytes();
   }
 
   if (!CreateThread())
   {
     bConnectionOpened = false;
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a communication thread");
   }
   else if (!bSkipChecks && !CheckAdapter())
   {
     bConnectionOpened = false;
-    CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
   }
   else if (bStartListening)
   {
@@ -140,7 +151,7 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool b
     else
     {
       bConnectionOpened = false;
-      CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a ping thread");
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread");
     }
   }
 
@@ -152,23 +163,28 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool b
 
 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)
   {
+    LIB_CEC->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 */
   if (m_port)
     m_port->Close();
@@ -198,20 +214,26 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
 void *CUSBCECAdapterCommunication::Process(void)
 {
   CCECAdapterMessage msg;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
 
   while (!IsStopped())
   {
     /* read from the serial port */
     if (!ReadFromDevice(50, 5))
+    {
+      libcec_parameter param;
+      param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
+      LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
+
       break;
+    }
 
     /* TODO sleep 5 ms so other threads can get a lock */
     Sleep(5);
   }
 
   m_adapterMessageQueue->Clear();
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread ended");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
   return NULL;
 }
 
@@ -255,7 +277,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];
@@ -294,7 +316,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()));
+    LIB_CEC->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;
   }
@@ -302,12 +324,13 @@ 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());
+    LIB_CEC->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;
   }
 
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
   message->state = ADAPTER_MESSAGE_STATE_SENT;
   return true;
 }
@@ -322,17 +345,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())
+    {
+      LIB_CEC->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 */
@@ -358,17 +385,25 @@ CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_message
   /* write the command */
   if (!m_adapterMessageQueue->Write(output))
   {
-    // timed out
+    if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
+    {
+      libcec_parameter param;
+      param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
+      LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
+
+      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
          mode and retry */
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
       delete output;
       if (SetControlledMode(true))
         return SendCommand(msgCode, params, true);
@@ -378,7 +413,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);
@@ -388,7 +423,7 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */
   unsigned iPingTry(0);
   while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
     CEvent::Sleep(500);
   }
 
@@ -400,7 +435,7 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */
     bool bControlled(false);
     while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
       CEvent::Sleep(500);
     }
     bReturn = bControlled;
@@ -408,6 +443,9 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */
   else
     bReturn = true;
 
+  /* try to read the build date */
+  m_commands->RequestBuildDate();
+
   SetInitialised(bReturn);
   return bReturn;
 }
@@ -437,20 +475,36 @@ bool CUSBCECAdapterCommunication::IsInitialised(void)
 
 bool CUSBCECAdapterCommunication::StartBootloader(void)
 {
-  if (!IsRunning())
-    return false;
-
-  return m_commands->StartBootloader();
+  if (m_port->IsOpen() && m_commands->StartBootloader())
+  {
+    Close();
+    return true;
+  }
+  return false;
 }
 
 bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
 {
-  return m_commands->SetAckMask(iMask);
+  if (m_iAckMask == iMask)
+    return true;
+
+  if (m_port && m_port->IsOpen() && m_commands->SetAckMask(iMask))
+  {
+    m_iAckMask = iMask;
+    return true;
+  }
+
+  return false;
+}
+
+uint16_t CUSBCECAdapterCommunication::GetAckMask(void)
+{
+  return m_iAckMask;
 }
 
 bool CUSBCECAdapterCommunication::PingAdapter(void)
 {
-  return m_commands->PingAdapter();
+  return m_port->IsOpen() ? m_commands->PingAdapter() : false;
 }
 
 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
@@ -458,14 +512,25 @@ uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
   return m_commands->GetFirmwareVersion();
 }
 
-bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
+uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
+{
+  return m_commands->RequestBuildDate();
+}
+
+bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
+{
+  return GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION &&
+      GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE;
+}
+
+bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
 {
-  return m_commands->PersistConfiguration(configuration);
+  return m_port->IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
 }
 
-bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
+bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
 {
-  return m_commands->GetConfiguration(configuration);
+  return m_port->IsOpen() ? m_commands->GetConfiguration(configuration) : false;
 }
 
 CStdString CUSBCECAdapterCommunication::GetPortName(void)
@@ -475,7 +540,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)
@@ -494,8 +559,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
@@ -507,7 +572,7 @@ void *CAdapterPingThread::Process(void)
       if (iFailedCounter == 3)
       {
         /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
-        CLibCEC::AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
+        m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
         m_com->StopThread(false);
         break;
       }