LibCecSharp: fixed - set the primary LA in CecLogicalAddresses
[deb_libcec.git] / src / lib / devices / CECBusDevice.cpp
index 88d6b46c35cea9ddd00dc8e19df40faf18089acf..5b3e2cb42626fc1566bd215ea407236e76c11492 100644 (file)
  *     http://www.pulse-eight.net/
  */
 
+#include "env.h"
 #include "CECBusDevice.h"
-#include "../CECProcessor.h"
-#include "../implementations/ANCommandHandler.h"
-#include "../implementations/CECCommandHandler.h"
-#include "../implementations/SLCommandHandler.h"
-#include "../implementations/VLCommandHandler.h"
-#include "../LibCEC.h"
-#include "../CECTypeUtils.h"
-#include "../platform/util/timeutils.h"
-#include "../platform/util/util.h"
+
+#include "lib/CECProcessor.h"
+#include "lib/CECClient.h"
+#include "lib/implementations/ANCommandHandler.h"
+#include "lib/implementations/CECCommandHandler.h"
+#include "lib/implementations/SLCommandHandler.h"
+#include "lib/implementations/VLCommandHandler.h"
+#include "lib/LibCEC.h"
+#include "lib/CECTypeUtils.h"
+#include "lib/platform/util/timeutils.h"
+#include "lib/platform/util/util.h"
 
 #include "CECAudioSystem.h"
 #include "CECPlaybackDevice.h"
@@ -71,7 +74,8 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi
   m_deviceStatus          (CEC_DEVICE_STATUS_UNKNOWN),
   m_iHandlerUseCount      (0),
   m_bAwaitingReceiveFailed(false),
-  m_bVendorIdRequested    (false)
+  m_bVendorIdRequested    (false),
+  m_waitForResponse       (new CWaitForResponse)
 {
   m_handler = new CCECCommandHandler(this);
 
@@ -86,16 +90,14 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi
 CCECBusDevice::~CCECBusDevice(void)
 {
   DELETE_AND_NULL(m_handler);
+  DELETE_AND_NULL(m_waitForResponse);
 }
 
 bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */)
 {
   bool bInitHandler(false);
   {
-    CTryLockObject lock(m_mutex);
-    if (!lock.IsLocked())
-      return false;
-
+    CLockObject lock(m_mutex);
     CLockObject handlerLock(m_handlerMutex);
     if (m_iHandlerUseCount > 0)
       return false;
@@ -107,21 +109,27 @@ bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */)
       if (CCECCommandHandler::HasSpecificHandler(m_vendor))
       {
         LIB_CEC->AddLog(CEC_LOG_DEBUG, "replacing the command handler for device '%s' (%x)", GetLogicalAddressName(), GetLogicalAddress());
+
+        int32_t iTransmitTimeout     = m_handler->m_iTransmitTimeout;
+        int32_t iTransmitWait        = m_handler->m_iTransmitWait;
+        int8_t  iTransmitRetries     = m_handler->m_iTransmitRetries;
+        int64_t iActiveSourcePending = m_handler->m_iActiveSourcePending;
+
         DELETE_AND_NULL(m_handler);
 
         switch (m_vendor)
         {
         case CEC_VENDOR_SAMSUNG:
-          m_handler = new CANCommandHandler(this);
+          m_handler = new CANCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
           break;
         case CEC_VENDOR_LG:
-          m_handler = new CSLCommandHandler(this);
+          m_handler = new CSLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
           break;
         case CEC_VENDOR_PANASONIC:
-          m_handler = new CVLCommandHandler(this);
+          m_handler = new CVLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
           break;
         default:
-          m_handler = new CCECCommandHandler(this);
+          m_handler = new CCECCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
           break;
         }
 
@@ -230,7 +238,7 @@ void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode)
 
   // signal threads that are waiting for a reponse
   MarkBusy();
-  m_handler->SignalOpcode(cec_command::GetResponseOpcode(opcode));
+  SignalOpcode(cec_command::GetResponseOpcode(opcode));
   MarkReady();
 }
 
@@ -302,7 +310,7 @@ bool CCECBusDevice::RequestCecVersion(const cec_logical_address initiator, bool
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitCECVersion(const cec_logical_address destination)
+bool CCECBusDevice::TransmitCECVersion(const cec_logical_address destination, bool bIsReply)
 {
   cec_version version;
   {
@@ -312,7 +320,7 @@ bool CCECBusDevice::TransmitCECVersion(const cec_logical_address destination)
   }
 
   MarkBusy();
-  bool bReturn = m_handler->TransmitCECVersion(m_iLogicalAddress, destination, version);
+  bool bReturn = m_handler->TransmitCECVersion(m_iLogicalAddress, destination, version, bIsReply);
   MarkReady();
   return bReturn;
 }
@@ -371,7 +379,7 @@ bool CCECBusDevice::RequestMenuLanguage(const cec_logical_address initiator, boo
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitSetMenuLanguage(const cec_logical_address destination)
+bool CCECBusDevice::TransmitSetMenuLanguage(const cec_logical_address destination, bool bIsReply)
 {
   bool bReturn(false);
   cec_menu_language language;
@@ -398,20 +406,20 @@ bool CCECBusDevice::TransmitSetMenuLanguage(const cec_logical_address destinatio
   else
   {
     LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): Menu language '%s'", GetLogicalAddressName(), m_iLogicalAddress, lang);
-    bReturn = m_handler->TransmitSetMenuLanguage(m_iLogicalAddress, lang);
+    bReturn = m_handler->TransmitSetMenuLanguage(m_iLogicalAddress, lang, bIsReply);
   }
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitOSDString(const cec_logical_address destination, cec_display_control duration, const char *strMessage)
+bool CCECBusDevice::TransmitOSDString(const cec_logical_address destination, cec_display_control duration, const char *strMessage, bool bIsReply)
 {
   bool bReturn(false);
   if (!m_processor->GetDevice(destination)->IsUnsupportedFeature(CEC_OPCODE_SET_OSD_STRING))
   {
     LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): display OSD message '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, strMessage);
     MarkBusy();
-    bReturn = m_handler->TransmitOSDString(m_iLogicalAddress, destination, duration, strMessage);
+    bReturn = m_handler->TransmitOSDString(m_iLogicalAddress, destination, duration, strMessage, bIsReply);
     MarkReady();
   }
   return bReturn;
@@ -469,7 +477,7 @@ bool CCECBusDevice::RequestOSDName(const cec_logical_address initiator, bool bWa
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitOSDName(const cec_logical_address destination)
+bool CCECBusDevice::TransmitOSDName(const cec_logical_address destination, bool bIsReply)
 {
   CStdString strDeviceName;
   {
@@ -479,7 +487,7 @@ bool CCECBusDevice::TransmitOSDName(const cec_logical_address destination)
   }
 
   MarkBusy();
-  bool bReturn = m_handler->TransmitOSDName(m_iLogicalAddress, destination, strDeviceName);
+  bool bReturn = m_handler->TransmitOSDName(m_iLogicalAddress, destination, strDeviceName, bIsReply);
   MarkReady();
   return bReturn;
 }
@@ -544,7 +552,7 @@ bool CCECBusDevice::RequestPhysicalAddress(const cec_logical_address initiator,
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitPhysicalAddress(void)
+bool CCECBusDevice::TransmitPhysicalAddress(bool bIsReply)
 {
   uint16_t iPhysicalAddress;
   cec_device_type type;
@@ -559,7 +567,7 @@ bool CCECBusDevice::TransmitPhysicalAddress(void)
   }
 
   MarkBusy();
-  bool bReturn = m_handler->TransmitPhysicalAddress(m_iLogicalAddress, iPhysicalAddress, type);
+  bool bReturn = m_handler->TransmitPhysicalAddress(m_iLogicalAddress, iPhysicalAddress, type, bIsReply);
   MarkReady();
   return bReturn;
 }
@@ -619,7 +627,7 @@ bool CCECBusDevice::RequestPowerStatus(const cec_logical_address initiator, bool
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitPowerState(const cec_logical_address destination)
+bool CCECBusDevice::TransmitPowerState(const cec_logical_address destination, bool bIsReply)
 {
   cec_power_status state;
   {
@@ -629,7 +637,7 @@ bool CCECBusDevice::TransmitPowerState(const cec_logical_address destination)
   }
 
   MarkBusy();
-  bool bReturn = m_handler->TransmitPowerState(m_iLogicalAddress, destination, state);
+  bool bReturn = m_handler->TransmitPowerState(m_iLogicalAddress, destination, state, bIsReply);
   MarkReady();
   return bReturn;
 }
@@ -695,7 +703,7 @@ bool CCECBusDevice::RequestVendorId(const cec_logical_address initiator, bool bW
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitVendorID(const cec_logical_address destination, bool bSendAbort /* = true */)
+bool CCECBusDevice::TransmitVendorID(const cec_logical_address destination, bool bSendAbort, bool bIsReply)
 {
   bool bReturn(false);
   uint64_t iVendorId;
@@ -717,7 +725,7 @@ bool CCECBusDevice::TransmitVendorID(const cec_logical_address destination, bool
   else
   {
     LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): vendor id %s (%x)", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, ToString((cec_vendor_id)iVendorId), iVendorId);
-    bReturn = m_handler->TransmitVendorID(m_iLogicalAddress, iVendorId);
+    bReturn = m_handler->TransmitVendorID(m_iLogicalAddress, iVendorId, bIsReply);
   }
   MarkReady();
   return bReturn;
@@ -748,7 +756,7 @@ cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bo
   return status;
 }
 
-void CCECBusDevice::SetDeviceStatus(const cec_bus_device_status newStatus)
+void CCECBusDevice::SetDeviceStatus(const cec_bus_device_status newStatus, cec_version libCECSpecVersion /* = CEC_VERSION_1_4 */)
 {
   {
     CLockObject lock(m_mutex);
@@ -760,7 +768,7 @@ void CCECBusDevice::SetDeviceStatus(const cec_bus_device_status newStatus)
       SetPowerStatus   (CEC_POWER_STATUS_ON);
       SetVendorId      (CEC_VENDOR_UNKNOWN);
       SetMenuState     (CEC_MENU_STATE_ACTIVATED);
-      SetCecVersion    (CEC_VERSION_1_3A);
+      SetCecVersion    (libCECSpecVersion);
       SetStreamPath    (CEC_INVALID_PHYSICAL_ADDRESS);
       MarkAsInactiveSource();
       m_iLastActive   = 0;
@@ -800,13 +808,14 @@ void CCECBusDevice::ResetDeviceStatus(void)
   m_iLastActive = 0;
   m_bVendorIdRequested = false;
   m_unsupportedFeatures.clear();
+  m_waitForResponse->Clear();
 
   if (m_deviceStatus != CEC_DEVICE_STATUS_UNKNOWN)
     LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'unknown'", GetLogicalAddressName(), m_iLogicalAddress);
   m_deviceStatus = CEC_DEVICE_STATUS_UNKNOWN;
 }
 
-bool CCECBusDevice::TransmitPoll(const cec_logical_address dest)
+bool CCECBusDevice::TransmitPoll(const cec_logical_address dest, bool bIsReply)
 {
   bool bReturn(false);
   cec_logical_address destination(dest);
@@ -819,7 +828,7 @@ bool CCECBusDevice::TransmitPoll(const cec_logical_address dest)
 
   MarkBusy();
   LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): POLL", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest);
-  bReturn = m_handler->TransmitPoll(m_iLogicalAddress, destination);
+  bReturn = m_handler->TransmitPoll(m_iLogicalAddress, destination, bIsReply);
   LIB_CEC->AddLog(CEC_LOG_DEBUG, bReturn ? ">> POLL sent" : ">> POLL not sent");
 
   CLockObject lock(m_mutex);
@@ -874,7 +883,7 @@ void CCECBusDevice::SetMenuState(const cec_menu_state state)
   }
 }
 
-bool CCECBusDevice::TransmitMenuState(const cec_logical_address dest)
+bool CCECBusDevice::TransmitMenuState(const cec_logical_address dest, bool bIsReply)
 {
   cec_menu_state menuState;
   {
@@ -884,17 +893,26 @@ bool CCECBusDevice::TransmitMenuState(const cec_logical_address dest)
   }
 
   MarkBusy();
-  bool bReturn = m_handler->TransmitMenuState(m_iLogicalAddress, dest, menuState);
+  bool bReturn = m_handler->TransmitMenuState(m_iLogicalAddress, dest, menuState, bIsReply);
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::ActivateSource(void)
+bool CCECBusDevice::ActivateSource(uint64_t iDelay /* = 0 */)
 {
   MarkAsActiveSource();
-  LIB_CEC->AddLog(CEC_LOG_DEBUG, "activating source '%s'", ToString(m_iLogicalAddress));
   MarkBusy();
-  bool bReturn = m_handler->ActivateSource();
+  bool bReturn(true);
+  if (iDelay == 0)
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending active source message for '%s'", ToString(m_iLogicalAddress));
+    bReturn = m_handler->ActivateSource();
+  }
+  else
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "scheduling active source message for '%s'", ToString(m_iLogicalAddress));
+    m_handler->ScheduleActivateSource(iDelay);
+  }
   MarkReady();
   return bReturn;
 }
@@ -916,33 +934,62 @@ bool CCECBusDevice::RequestActiveSource(bool bWaitForResponse /* = true */)
 
 void CCECBusDevice::MarkAsActiveSource(void)
 {
-  CLockObject lock(m_mutex);
-  if (!m_bActiveSource)
-    LIB_CEC->AddLog(CEC_LOG_DEBUG, "making %s (%x) the active source", GetLogicalAddressName(), m_iLogicalAddress);
-  else
-    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%x) was already marked as active source", GetLogicalAddressName(), m_iLogicalAddress);
+  bool bWasActivated(false);
+
+  // set the power status to powered on
+  SetPowerStatus(CEC_POWER_STATUS_ON);
 
+  // mark this device as active source
+  {
+    CLockObject lock(m_mutex);
+    if (!m_bActiveSource)
+    {
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "making %s (%x) the active source", GetLogicalAddressName(), m_iLogicalAddress);
+      bWasActivated = true;
+    }
+    else
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%x) was already marked as active source", GetLogicalAddressName(), m_iLogicalAddress);
+
+    m_bActiveSource = true;
+  }
+
+  // mark other devices as inactive sources
   CECDEVICEVEC devices;
   m_processor->GetDevices()->Get(devices);
   for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
     if ((*it)->GetLogicalAddress() != m_iLogicalAddress)
       (*it)->MarkAsInactiveSource();
 
-  m_bActiveSource = true;
-  SetPowerStatus(CEC_POWER_STATUS_ON);
+  if (bWasActivated)
+  {
+    CCECClient *client = GetClient();
+    if (client)
+      client->SourceActivated(m_iLogicalAddress);
+  }
 }
 
 void CCECBusDevice::MarkAsInactiveSource(void)
 {
+  bool bWasDeactivated(false);
   {
     CLockObject lock(m_mutex);
     if (m_bActiveSource)
+    {
       LIB_CEC->AddLog(CEC_LOG_DEBUG, "marking %s (%X) as inactive source", GetLogicalAddressName(), m_iLogicalAddress);
+      bWasDeactivated = true;
+    }
     m_bActiveSource = false;
   }
+
+  if (bWasDeactivated)
+  {
+    CCECClient *client = GetClient();
+    if (client)
+      client->SourceDeactivated(m_iLogicalAddress);
+  }
 }
 
-bool CCECBusDevice::TransmitActiveSource(void)
+bool CCECBusDevice::TransmitActiveSource(bool bIsReply)
 {
   bool bSendActiveSource(false);
   uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
@@ -972,7 +1019,7 @@ bool CCECBusDevice::TransmitActiveSource(void)
   if (bSendActiveSource)
   {
     MarkBusy();
-    bActiveSourceSent = m_handler->TransmitActiveSource(m_iLogicalAddress, iPhysicalAddress);
+    bActiveSourceSent = m_handler->TransmitActiveSource(m_iLogicalAddress, iPhysicalAddress, bIsReply);
     MarkReady();
   }
 
@@ -1231,14 +1278,14 @@ void CCECBusDevice::MarkReady(void)
     --m_iHandlerUseCount;
 }
 
-bool CCECBusDevice::TryLogicalAddress(void)
+bool CCECBusDevice::TryLogicalAddress(cec_version libCECSpecVersion /* = CEC_VERSION_1_4 */)
 {
   LIB_CEC->AddLog(CEC_LOG_DEBUG, "trying logical address '%s'", GetLogicalAddressName());
 
-  if (!TransmitPoll(m_iLogicalAddress))
+  if (!TransmitPoll(m_iLogicalAddress, false))
   {
     LIB_CEC->AddLog(CEC_LOG_NOTICE, "using logical address '%s'", GetLogicalAddressName());
-    SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
+    SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC, libCECSpecVersion);
 
     return true;
   }
@@ -1252,3 +1299,13 @@ CCECClient *CCECBusDevice::GetClient(void)
 {
   return m_processor->GetClient(m_iLogicalAddress);
 }
+
+void CCECBusDevice::SignalOpcode(cec_opcode opcode)
+{
+  m_waitForResponse->Received(opcode);
+}
+
+bool CCECBusDevice::WaitForOpcode(cec_opcode opcode)
+{
+  return m_waitForResponse->Wait(opcode);
+}