cec: don't replace a command handler when it's being used
authorLars Op den Kamp <lars@opdenkamp.eu>
Tue, 27 Dec 2011 19:15:08 +0000 (20:15 +0100)
committerLars Op den Kamp <lars@opdenkamp.eu>
Tue, 27 Dec 2011 19:15:08 +0000 (20:15 +0100)
src/lib/CECProcessor.cpp
src/lib/CECProcessor.h
src/lib/devices/CECBusDevice.cpp
src/lib/devices/CECBusDevice.h
src/lib/implementations/CECCommandHandler.cpp
src/lib/implementations/CECCommandHandler.h

index 27bca694646f05378cc4a085c4b08e4dcdf08acd..63205f460f416e31649ed94327721a67ff7225e5 100644 (file)
@@ -49,6 +49,7 @@ using namespace std;
 
 CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS*/) :
     m_bStarted(false),
+    m_bInitialised(false),
     m_iHDMIPort(CEC_DEFAULT_HDMI_PORT),
     m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE),
     m_lastInitiator(CECDEVICE_UNKNOWN),
@@ -69,6 +70,7 @@ CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, cec
 
 CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, const cec_device_type_list &types) :
     m_bStarted(false),
+    m_bInitialised(false),
     m_iHDMIPort(CEC_DEFAULT_HDMI_PORT),
     m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE),
     m_strDeviceName(strDeviceName),
@@ -176,8 +178,9 @@ bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = 38400 */,
   /* make the primary device the active source */
   if (bReturn)
   {
+    m_bInitialised = true;
     m_busDevices[m_logicalAddresses.primary]->m_bActiveSource = true;
-    bReturn = m_busDevices[CECDEVICE_TV]->GetHandler()->InitHandler();
+    bReturn = m_busDevices[CECDEVICE_TV]->InitHandler();
   }
 
   if (bReturn)
@@ -413,6 +416,7 @@ bool CCECProcessor::SetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true *
 bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */)
 {
   bool bReturn(false);
+  CLockObject lock(&m_mutex);
 
   m_iBaseDevice = iBaseDevice;
   m_iHDMIPort = iPort;
@@ -482,6 +486,7 @@ void CCECProcessor::LogOutput(const cec_command &data)
 
 bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress)
 {
+  CLockObject lock(&m_mutex);
   if (m_logicalAddresses.primary != iLogicalAddress)
   {
     CStdString strLog;
@@ -511,6 +516,7 @@ bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = tru
 
 bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress)
 {
+  CLockObject lock(&m_mutex);
   if (!m_logicalAddresses.IsEmpty())
   {
     for (uint8_t iPtr = 0; iPtr < 15; iPtr++)
@@ -604,7 +610,7 @@ CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const
 
   for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
   {
-    if (m_busDevices[iPtr]->m_type == type)
+    if (m_busDevices[iPtr]->m_type == type && m_logicalAddresses[iPtr])
     {
       device = m_busDevices[iPtr];
       break;
index e2705fd007c1853aa6e31a12789cf25983157f48..9cb1bf9fe9982a1bc936b8e5d9d0877991bd8320 100644 (file)
@@ -78,6 +78,7 @@ namespace CEC
       virtual bool                  IsStarted(void) const { return m_bStarted; }
       virtual cec_logical_address   GetActiveSource(void);
       virtual bool                  IsActiveSource(cec_logical_address iAddress);
+      virtual bool                  IsInitialised(void) const { return m_bInitialised; }
 
       virtual bool SetActiveView(void);
       virtual bool SetActiveSource(cec_device_type type = CEC_DEVICE_TYPE_RESERVED);
@@ -147,6 +148,7 @@ namespace CEC
       void ParseCommand(cec_command &command);
 
       bool                   m_bStarted;
+      bool                   m_bInitialised;
       uint8_t                m_iHDMIPort;
       cec_logical_address    m_iBaseDevice;
       cec_command            m_currentframe;
index 6279de948712b051e1dd4d2d7eaffa61a2a72380..1ea78d72fdf0d6418b9cd3d22f1903b479d770a1 100644 (file)
@@ -50,11 +50,13 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi
   m_powerStatus(CEC_POWER_STATUS_UNKNOWN),
   m_processor(processor),
   m_vendor(CEC_VENDOR_UNKNOWN),
+  m_bReplaceHandler(false),
   m_menuState(CEC_MENU_STATE_ACTIVATED),
   m_bActiveSource(false),
   m_iLastActive(0),
   m_cecVersion(CEC_VERSION_UNKNOWN),
-  m_deviceStatus(CEC_DEVICE_STATUS_UNKNOWN)
+  m_deviceStatus(CEC_DEVICE_STATUS_UNKNOWN),
+  m_handlerMutex(false)
 {
   m_handler = new CCECCommandHandler(this);
 
@@ -90,6 +92,7 @@ bool CCECBusDevice::HandleCommand(const cec_command &command)
   }
 
   /* handle the command */
+  ReplaceHandler(true);
   bHandled = m_handler->HandleCommand(command);
 
   /* change status to present */
@@ -117,7 +120,7 @@ bool CCECBusDevice::PowerOn(void)
   strLog.Format("<< powering on '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
   AddLog(CEC_LOG_DEBUG, strLog.c_str());
 
-  if (m_handler->TransmitPowerOn(GetMyLogicalAddress(), m_iLogicalAddress))
+  if (m_handler->TransmitImageViewOn(GetMyLogicalAddress(), m_iLogicalAddress))
   {
     {
       CLockObject lock(&m_mutex);
@@ -165,6 +168,7 @@ cec_version CCECBusDevice::GetCecVersion(bool bUpdate /* = false */)
 bool CCECBusDevice::RequestCecVersion(void)
 {
   bool bReturn(false);
+
   if (!MyLogicalAddressContains(m_iLogicalAddress))
   {
     CStdString strLog;
@@ -194,6 +198,7 @@ cec_menu_language &CCECBusDevice::GetMenuLanguage(bool bUpdate /* = false */)
 bool CCECBusDevice::RequestMenuLanguage(void)
 {
   bool bReturn(false);
+
   if (!MyLogicalAddressContains(m_iLogicalAddress) &&
       !IsUnsupportedFeature(CEC_OPCODE_GET_MENU_LANGUAGE))
   {
@@ -229,6 +234,7 @@ CStdString CCECBusDevice::GetOSDName(bool bUpdate /* = false */)
 bool CCECBusDevice::RequestOSDName(void)
 {
   bool bReturn(false);
+
   if (!MyLogicalAddressContains(m_iLogicalAddress) &&
       !IsUnsupportedFeature(CEC_OPCODE_GIVE_OSD_NAME))
   {
@@ -256,6 +262,7 @@ uint16_t CCECBusDevice::GetPhysicalAddress(bool bUpdate /* = false */)
 bool CCECBusDevice::RequestPhysicalAddress(void)
 {
   bool bReturn(false);
+
   if (!MyLogicalAddressContains(m_iLogicalAddress))
   {
     CStdString strLog;
@@ -279,6 +286,7 @@ cec_power_status CCECBusDevice::GetPowerStatus(bool bUpdate /* = false */)
 bool CCECBusDevice::RequestPowerStatus(void)
 {
   bool bReturn(false);
+
   if (!MyLogicalAddressContains(m_iLogicalAddress) &&
       !IsUnsupportedFeature(CEC_OPCODE_GIVE_DEVICE_POWER_STATUS))
   {
@@ -303,6 +311,7 @@ cec_vendor_id CCECBusDevice::GetVendorId(bool bUpdate /* = false */)
 bool CCECBusDevice::RequestVendorId(void)
 {
   bool bReturn(false);
+
   if (!MyLogicalAddressContains(m_iLogicalAddress))
   {
     CStdString strLog;
@@ -558,41 +567,51 @@ void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus)
   }
 }
 
-bool CCECBusDevice::SetVendorId(uint64_t iVendorId, bool bInitHandler /* = true */)
+bool CCECBusDevice::ReplaceHandler(bool bInitHandler /* = true */)
 {
-  bool bVendorChanged(false);
+  CLockObject lock(&m_mutex);
+  CLockObject handlerLock(&m_handlerMutex);
 
+  if (m_vendor != m_handler->GetVendorId())
   {
-    CLockObject lock(&m_mutex);
-    bVendorChanged = (m_vendor != (cec_vendor_id)iVendorId);
-    m_vendor = (cec_vendor_id)iVendorId;
+    if (m_handler->InUse())
+      return false;
 
-    if (bVendorChanged)
-      delete m_handler;
+    delete m_handler;
 
-    switch (iVendorId)
+    switch (m_vendor)
     {
     case CEC_VENDOR_SAMSUNG:
-      if (bVendorChanged)
-        m_handler = new CANCommandHandler(this);
+      m_handler = new CANCommandHandler(this);
       break;
     case CEC_VENDOR_LG:
-      if (bVendorChanged)
-        m_handler = new CSLCommandHandler(this);
+      m_handler = new CSLCommandHandler(this);
       break;
     case CEC_VENDOR_PANASONIC:
-      if (bVendorChanged)
-        m_handler = new CVLCommandHandler(this);
+      m_handler = new CVLCommandHandler(this);
       break;
     default:
-      if (bVendorChanged)
-        m_handler = new CCECCommandHandler(this);
+      m_handler = new CCECCommandHandler(this);
       break;
     }
+
+    if (bInitHandler && m_processor->GetLogicalAddresses().IsSet(m_iLogicalAddress) && m_processor->IsInitialised())
+      m_handler->InitHandler();
   }
 
-  if (bVendorChanged && bInitHandler && m_handler->GetVendorId() != CEC_VENDOR_UNKNOWN)
-    m_handler->InitHandler();
+  return true;
+}
+
+bool CCECBusDevice::SetVendorId(uint64_t iVendorId, bool bInitHandler /* = true */)
+{
+  bool bVendorChanged(false);
+
+  {
+    CLockObject lock(&m_mutex);
+    bVendorChanged = (m_vendor != (cec_vendor_id)iVendorId);
+    m_vendor = (cec_vendor_id)iVendorId;
+    ReplaceHandler(bInitHandler);
+  }
 
   CStdString strLog;
   strLog.Format("%s (%X): vendor = %s (%06x)", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_vendor), m_vendor);
@@ -631,7 +650,14 @@ bool CCECBusDevice::TransmitActiveSource(void)
     }
   }
 
-  return bSendActiveSource ? m_handler->TransmitActiveSource(m_iLogicalAddress, m_iPhysicalAddress) : false;
+  if (bSendActiveSource)
+  {
+    m_handler->TransmitActiveSource(m_iLogicalAddress, m_iPhysicalAddress);
+    m_handler->TransmitImageViewOn(m_iLogicalAddress, CECDEVICE_TV);
+    return true;
+  }
+
+  return false;
 }
 
 bool CCECBusDevice::TransmitCECVersion(cec_logical_address dest)
@@ -814,4 +840,12 @@ void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode)
 {
   m_unsupportedFeatures.insert(opcode);
 }
+
+bool CCECBusDevice::InitHandler(void)
+{
+  CLockObject lock(&m_mutex);
+  ReplaceHandler(false);
+  return m_handler->InitHandler();
+}
+
 //@}
index 0ca662afe471a6c5117321cfc909c3513e725500..30d8329c5c8d711f8f4335b9d86f6019d9e9f9c8 100644 (file)
@@ -80,6 +80,7 @@ namespace CEC
     virtual void SetInactiveSource(void);
     virtual void SetActiveSource(void);
     virtual bool TryLogicalAddress(void);
+    virtual bool InitHandler(void);
 
     virtual void SetDeviceStatus(const cec_bus_device_status newStatus);
     virtual void SetPhysicalAddress(uint16_t iNewAddress);
@@ -105,6 +106,8 @@ namespace CEC
     virtual bool TransmitKeyRelease(bool bWait = true);
 
   protected:
+    bool ReplaceHandler(bool bInitHandler = true);
+
     bool RequestCecVersion(void);
     bool RequestMenuLanguage(void);
     bool RequestPowerStatus(void);
@@ -124,6 +127,7 @@ namespace CEC
     CCECProcessor      *  m_processor;
     CCECCommandHandler *  m_handler;
     cec_vendor_id         m_vendor;
+    bool                  m_bReplaceHandler;
     cec_menu_state        m_menuState;
     bool                  m_bActiveSource;
     uint64_t              m_iLastActive;
@@ -131,5 +135,6 @@ namespace CEC
     cec_bus_device_status m_deviceStatus;
     std::set<cec_opcode>  m_unsupportedFeatures;
     CMutex                m_mutex;
+    CMutex                m_handlerMutex;
   };
 };
index 23fd550b068ea9a2a4ee15550faadb6dbb2faa6e..5da80a01405c8db98e54acbe1cfbb3c3070b845b 100644 (file)
@@ -45,12 +45,15 @@ CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice) :
     m_iTransmitTimeout(CEC_DEFAULT_TRANSMIT_TIMEOUT),
     m_iTransmitWait(CEC_DEFAULT_TRANSMIT_WAIT),
     m_iTransmitRetries(CEC_DEFAULT_TRANSMIT_RETRIES),
-    m_bHandlerInited(false)
+    m_bHandlerInited(false),
+    m_iUseCounter(0)
 {
 }
 
 CCECCommandHandler::~CCECCommandHandler(void)
 {
+  CLockObject lock(&m_processor->m_transmitMutex);
+  CLockObject receiveLock(&m_receiveMutex);
   m_condition.Broadcast();
 }
 
@@ -58,6 +61,7 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
 {
   bool bHandled(true), bHandlerChanged(false);
 
+  MarkBusy();
   CStdString strLog;
   strLog.Format(">> %s (%X) -> %s (%X): %s (%2X)", m_processor->ToString(command.initiator), command.initiator, m_processor->ToString(command.destination), command.destination, m_processor->ToString(command.opcode), command.opcode);
   m_busDevice->AddLog(CEC_LOG_NOTICE, strLog);
@@ -174,6 +178,7 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
     m_condition.Signal();
   }
 
+  MarkReady();
   return bHandled;
 }
 
@@ -704,7 +709,7 @@ bool CCECCommandHandler::HandleReceiveFailed(void)
   return true;
 }
 
-bool CCECCommandHandler::TransmitPowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
+bool CCECCommandHandler::TransmitImageViewOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
 {
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_IMAGE_VIEW_ON);
@@ -926,18 +931,23 @@ bool CCECCommandHandler::TransmitKeyRelease(const cec_logical_address iInitiator
 
 bool CCECCommandHandler::Transmit(cec_command &command, bool bExpectResponse /* = true */)
 {
+  bool bReturn(false);
   command.transmit_timeout = m_iTransmitTimeout;
 
-  CLockObject writeLock(&m_processor->m_transmitMutex);
-  CLockObject receiveLock(&m_receiveMutex);
-  if (m_processor->Transmit(command))
   {
-    if (bExpectResponse)
-      return m_condition.Wait(&m_receiveMutex, m_iTransmitWait);
-    return true;
+    CLockObject writeLock(&m_processor->m_transmitMutex);
+    CLockObject receiveLock(&m_receiveMutex);
+    ++m_iUseCounter;
+    if (m_processor->Transmit(command))
+    {
+      bReturn = bExpectResponse ?
+          m_condition.Wait(&m_receiveMutex, m_iTransmitWait) :
+          true;
+    }
+    --m_iUseCounter;
   }
 
-  return false;
+  return bReturn;
 }
 
 bool CCECCommandHandler::InitHandler(void)
@@ -957,3 +967,21 @@ bool CCECCommandHandler::InitHandler(void)
   }
   return true;
 }
+
+void CCECCommandHandler::MarkBusy(void)
+{
+  CLockObject receiveLock(&m_receiveMutex);
+  ++m_iUseCounter;
+}
+
+bool CCECCommandHandler::MarkReady(void)
+{
+  CLockObject receiveLock(&m_receiveMutex);
+  return m_iUseCounter > 0 ? (--m_iUseCounter == 0) : true;
+}
+
+bool CCECCommandHandler::InUse(void)
+{
+  CLockObject receiveLock(&m_receiveMutex);
+  return m_iUseCounter > 0;
+}
index 0469b31e0eaf6412360f6ced970c8f4cad619a4e..9ced8293cd2f3477bf9cdb914e8d57374890232e 100644 (file)
@@ -55,7 +55,7 @@ namespace CEC
     virtual bool InitHandler(void);
     virtual uint8_t GetTransmitRetries(void) const { return m_iTransmitRetries; }
 
-    virtual bool TransmitPowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination);
+    virtual bool TransmitImageViewOn(const cec_logical_address iInitiator, const cec_logical_address iDestination);
     virtual bool TransmitStandby(const cec_logical_address iInitiator, const cec_logical_address iDestination);
     virtual bool TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination);
     virtual bool TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination);
@@ -80,6 +80,10 @@ namespace CEC
     virtual bool TransmitKeypress(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_user_control_code key, bool bWait = true);
     virtual bool TransmitKeyRelease(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWait = true);
 
+    virtual void MarkBusy(void);
+    virtual bool MarkReady(void);
+    virtual bool InUse(void);
+
   protected:
     virtual bool HandleActiveSource(const cec_command &command);
     virtual bool HandleDeckControl(const cec_command &command);
@@ -131,6 +135,7 @@ namespace CEC
     int32_t        m_iTransmitWait;
     int8_t         m_iTransmitRetries;
     bool           m_bHandlerInited;
+    uint8_t        m_iUseCounter;
     CMutex         m_receiveMutex;
     CCondition     m_condition;
   };