cec: retry 'activate source' every 10 seconds if it failed
[deb_libcec.git] / src / lib / CECProcessor.cpp
index 0714d2a659b7f31c025d3ff15e6730465fdb10fb..fe40990553d2a4ea2dc278e70cda437262f2c666 100644 (file)
 #include "implementations/CECCommandHandler.h"
 #include "LibCEC.h"
 #include "CECClient.h"
+#include "CECTypeUtils.h"
 #include "platform/util/timeutils.h"
+#include "platform/util/util.h"
 
 using namespace CEC;
 using namespace std;
 using namespace PLATFORM;
 
 #define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000
+#define ACTIVE_SOURCE_CHECK_TIMEOUT    10000
 
-#define ToString(x) m_libcec->ToString(x)
+#define ToString(x) CCECTypeUtils::ToString(x)
 
 CCECProcessor::CCECProcessor(CLibCEC *libcec) :
     m_bInitialised(false),
     m_communication(NULL),
     m_libcec(libcec),
-    m_bMonitor(false),
-    m_iPreviousAckMask(0),
     m_iStandardLineTimeout(3),
     m_iRetryLineTimeout(3),
     m_iLastTransmission(0)
@@ -68,7 +69,7 @@ CCECProcessor::CCECProcessor(CLibCEC *libcec) :
 CCECProcessor::~CCECProcessor(void)
 {
   Close();
-  delete m_busDevices;
+  DELETE_AND_NULL(m_busDevices);
 }
 
 bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
@@ -81,18 +82,13 @@ bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIA
   // create the processor thread
   if (!IsRunning())
   {
-    if (CreateThread())
-      m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started");
-    else
+    if (!CreateThread())
     {
       m_libcec->AddLog(CEC_LOG_ERROR, "could not create a processor thread");
       return false;
     }
   }
 
-  // mark as initialised
-  SetCECInitialised(true);
-
   return true;
 }
 
@@ -105,25 +101,15 @@ void CCECProcessor::Close(void)
   StopThread();
 
   // close the connection
-  if (m_communication)
-  {
-    delete m_communication;
-    m_communication = NULL;
-  }
+  DELETE_AND_NULL(m_communication);
 }
 
 void CCECProcessor::ResetMembers(void)
 {
   // close the connection
-  if (m_communication)
-  {
-    delete m_communication;
-    m_communication = NULL;
-  }
+  DELETE_AND_NULL(m_communication);
 
   // reset the other members to the initial state
-  m_bMonitor = false;
-  m_iPreviousAckMask = 0;
   m_iStandardLineTimeout = 3;
   m_iRetryLineTimeout = 3;
   m_iLastTransmission = 0;
@@ -162,6 +148,12 @@ bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint
 
   m_libcec->AddLog(CEC_LOG_NOTICE, "connection opened");
 
+  // always start by setting the ackmask to 0, to clear previous values
+  SetAckMask(0);
+
+  // mark as initialised
+  SetCECInitialised(true);
+
   return bReturn;
 }
 
@@ -209,6 +201,19 @@ void CCECProcessor::ReplaceHandlers(void)
     it->second->ReplaceHandler(true);
 }
 
+void CCECProcessor::CheckPendingActiveSource(void)
+{
+  if (!CECInitialised())
+    return;
+
+  // check each device
+  for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
+  {
+    if (it->second->GetHandler()->ActiveSourcePending())
+      it->second->ActivateSource();
+  }
+}
+
 bool CCECProcessor::OnCommandReceived(const cec_command &command)
 {
   return m_inBuffer.Push(command);
@@ -219,6 +224,7 @@ void *CCECProcessor::Process(void)
   m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started");
 
   cec_command command;
+  CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_TIMEOUT);
 
   // as long as we're not being stopped and the connection is open
   while (!IsStopped() && m_communication->IsOpen())
@@ -234,6 +240,13 @@ void *CCECProcessor::Process(void)
 
       // check if we need to replace handlers
       ReplaceHandlers();
+
+      // check whether we need to activate a source, if it failed before
+      if (activeSourceCheck.TimeLeft() == 0)
+      {
+        CheckPendingActiveSource();
+        activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_TIMEOUT);
+      }
     }
   }
 
@@ -304,26 +317,6 @@ void CCECProcessor::LogOutput(const cec_command &data)
   m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
 }
 
-bool CCECProcessor::SwitchMonitoring(bool bEnable)
-{
-  m_libcec->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
-
-  {
-    CLockObject lock(m_mutex);
-    // switch to monitoring mode, which will stop processing of incoming messages
-    m_bMonitor = bEnable;
-    // and store the current ackmask
-    m_iPreviousAckMask = m_communication->GetAckMask();
-  }
-
-  // set the mask to 0 when enabling monitor mode
-  if (bEnable)
-    return SetAckMask(0);
-  // and restore the previous mask otherwise
-  else
-    return SetAckMask(m_iPreviousAckMask);
-}
-
 bool CCECProcessor::PollDevice(cec_logical_address iAddress)
 {
   // try to find the primary device
@@ -463,15 +456,11 @@ void CCECProcessor::ProcessCommand(const cec_command &command)
     dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]);
   m_libcec->AddLog(CEC_LOG_TRAFFIC, dataStr.c_str());
 
-  // if we're not in monitor mode
-  if (!m_bMonitor)
-  {
-    // find the initiator
-    CCECBusDevice *device = m_busDevices->At(command.initiator);
-    // and "handle" the command
-    if (device)
-      device->HandleCommand(command);
-  }
+  // find the initiator
+  CCECBusDevice *device = m_busDevices->At(command.initiator);
+
+  if (device)
+    device->HandleCommand(command);
 }
 
 bool CCECProcessor::IsPresentDevice(cec_logical_address address)
@@ -544,7 +533,7 @@ bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */)
     if (comm->IsOpen())
     {
       bReturn = comm->StartBootloader();
-      delete comm;
+      DELETE_AND_NULL(comm);
     }
     return bReturn;
   }
@@ -648,12 +637,22 @@ bool CCECProcessor::RegisterClient(CCECClient *client)
   if (!client)
     return false;
 
+  libcec_configuration &configuration = *client->GetConfiguration();
+
+  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3 && configuration.bMonitorOnly == 1)
+    return true;
+
+  if (!CECInitialised())
+  {
+    m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: CEC processor is not initialised");
+    return false;
+  }
+
   // unregister the client first if it's already been marked as registered
   if (client->IsRegistered())
     UnregisterClient(client);
 
   // get the configuration from the client
-  libcec_configuration &configuration = *client->GetConfiguration();
   m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion));
 
   // mark as uninitialised and unregistered
@@ -727,12 +726,13 @@ bool CCECProcessor::RegisterClient(CCECClient *client)
   return bReturn;
 }
 
-void CCECProcessor::UnregisterClient(CCECClient *client)
+bool CCECProcessor::UnregisterClient(CCECClient *client)
 {
   if (!client)
-    return;
+    return false;
 
-  m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str());
+  if (client->IsRegistered())
+    m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str());
 
   // notify the client that it will be unregistered
   client->OnUnregister();
@@ -756,7 +756,7 @@ void CCECProcessor::UnregisterClient(CCECClient *client)
   }
 
   // set the new ackmask
-  SetAckMask(GetLogicalAddresses().AckMask());
+  return SetAckMask(GetLogicalAddresses().AckMask());
 }
 
 void CCECProcessor::UnregisterClients(void)