always call the SourceActivated() callback in CCECBusDevice::MarkAsActiveSource(...
[deb_libcec.git] / src / lib / devices / CECBusDevice.cpp
index 51ddfeca68af118baca2ed31bda586c1d0921cee..489ded2cec4f441d379922e76f5be7459795c452 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libCEC(R) library.
  *
- * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited.  All rights reserved.
+ * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited.  All rights reserved.
  * libCEC(R) is an original work, containing original code.
  *
  * libCEC(R) is a trademark of Pulse-Eight Limited.
  *     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 "../platform/util/timeutils.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/implementations/PHCommandHandler.h"
+#include "lib/implementations/RLCommandHandler.h"
+#include "lib/implementations/RHCommandHandler.h"
+#include "lib/implementations/AQCommandHandler.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"
+#include "CECRecordingDevice.h"
+#include "CECTuner.h"
+#include "CECTV.h"
+
+using namespace std;
 using namespace CEC;
 using namespace PLATFORM;
 
-#define ToString(p) m_processor->ToString(p)
+#define LIB_CEC     m_processor->GetLib()
+#define ToString(p) CCECTypeUtils::ToString(p)
 
-CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogicalAddress, uint16_t iPhysicalAddress) :
+CResponse::CResponse(cec_opcode opcode) :
+    m_opcode(opcode)
+{
+}
+
+CResponse::~CResponse(void)
+{
+  Broadcast();
+}
+
+bool CResponse::Wait(uint32_t iTimeout)
+{
+  return m_event.Wait(iTimeout);
+}
+
+void CResponse::Broadcast(void)
+{
+  m_event.Broadcast();
+}
+
+CWaitForResponse::CWaitForResponse(void)
+{
+}
+
+CWaitForResponse::~CWaitForResponse(void)
+{
+  Clear();
+}
+
+void CWaitForResponse::Clear()
+{
+  PLATFORM::CLockObject lock(m_mutex);
+  for (std::map<cec_opcode, CResponse*>::iterator it = m_waitingFor.begin(); it != m_waitingFor.end(); it++)
+  {
+    it->second->Broadcast();
+    delete it->second;
+  }
+  m_waitingFor.clear();
+}
+
+bool CWaitForResponse::Wait(cec_opcode opcode, uint32_t iTimeout)
+{
+  CResponse *response = GetEvent(opcode);
+  return response ? response->Wait(iTimeout) : false;
+}
+
+void CWaitForResponse::Received(cec_opcode opcode)
+{
+  CResponse *response = GetEvent(opcode);
+  if (response)
+    response->Broadcast();
+}
+
+CResponse* CWaitForResponse::GetEvent(cec_opcode opcode)
+{
+  CResponse *retVal(NULL);
+  {
+    PLATFORM::CLockObject lock(m_mutex);
+    std::map<cec_opcode, CResponse*>::iterator it = m_waitingFor.find(opcode);
+    if (it != m_waitingFor.end())
+    {
+      retVal = it->second;
+    }
+    else
+    {
+      retVal = new CResponse(opcode);
+      m_waitingFor[opcode] = retVal;
+    }
+    return retVal;
+  }
+}
+
+CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogicalAddress, uint16_t iPhysicalAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */) :
   m_type                  (CEC_DEVICE_TYPE_RESERVED),
   m_iPhysicalAddress      (iPhysicalAddress),
-  m_iStreamPath           (0),
+  m_iStreamPath           (CEC_INVALID_PHYSICAL_ADDRESS),
   m_iLogicalAddress       (iLogicalAddress),
   m_powerStatus           (CEC_POWER_STATUS_UNKNOWN),
   m_processor             (processor),
@@ -61,7 +150,9 @@ 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_bImageViewOnSent      (false)
 {
   m_handler = new CCECCommandHandler(this);
 
@@ -75,7 +166,94 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi
 
 CCECBusDevice::~CCECBusDevice(void)
 {
-  delete m_handler;
+  DELETE_AND_NULL(m_handler);
+  DELETE_AND_NULL(m_waitForResponse);
+}
+
+bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */)
+{
+  if (m_iLogicalAddress == CECDEVICE_BROADCAST)
+    return false;
+
+  bool bInitHandler(false);
+  {
+    CLockObject lock(m_mutex);
+    CLockObject handlerLock(m_handlerMutex);
+    if (m_iHandlerUseCount > 0)
+      return false;
+
+    MarkBusy();
+
+    if (m_vendor != m_handler->GetVendorId())
+    {
+      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, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
+          break;
+        case CEC_VENDOR_LG:
+          m_handler = new CSLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
+          break;
+        case CEC_VENDOR_PANASONIC:
+          m_handler = new CVLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
+          break;
+        case CEC_VENDOR_PHILIPS:
+          m_handler = new CPHCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
+          break;
+        case CEC_VENDOR_TOSHIBA:
+        case CEC_VENDOR_TOSHIBA2:
+          m_handler = new CRLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
+          break;
+        case CEC_VENDOR_ONKYO:
+          m_handler = new CRHCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
+          break;
+        case CEC_VENDOR_SHARP:
+          m_handler = new CAQCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
+          break;
+        default:
+          m_handler = new CCECCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
+          break;
+        }
+
+        m_handler->SetVendorId(m_vendor);
+        bInitHandler = true;
+      }
+    }
+  }
+
+  if (bInitHandler)
+  {
+    CCECBusDevice *primary = GetProcessor()->GetPrimaryDevice();
+    if (primary->GetLogicalAddress() != CECDEVICE_UNREGISTERED)
+    {
+      m_handler->InitHandler();
+
+      if (bActivateSource && IsHandledByLibCEC() && IsActiveSource())
+        m_handler->ActivateSource();
+    }
+  }
+
+  MarkReady();
+
+  return true;
+}
+
+CCECCommandHandler *CCECBusDevice::GetHandler(void)
+{
+  ReplaceHandler(false);
+  MarkBusy();
+  return m_handler;
 }
 
 bool CCECBusDevice::HandleCommand(const cec_command &command)
@@ -86,11 +264,6 @@ bool CCECBusDevice::HandleCommand(const cec_command &command)
   {
     CLockObject lock(m_mutex);
     m_iLastActive = GetTimeMs();
-
-    /* don't call GetStatus() here, just read the value with the mutex locked */
-    if (m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC && command.opcode_set == 1)
-      m_deviceStatus = CEC_DEVICE_STATUS_PRESENT;
-
     MarkBusy();
   }
 
@@ -98,13 +271,13 @@ bool CCECBusDevice::HandleCommand(const cec_command &command)
   bHandled = m_handler->HandleCommand(command);
 
   /* change status to present */
-  if (bHandled)
+  if (bHandled && GetLogicalAddress() != CECDEVICE_BROADCAST && command.opcode_set == 1)
   {
     CLockObject lock(m_mutex);
     if (m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
     {
       if (m_deviceStatus != CEC_DEVICE_STATUS_PRESENT)
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "device %s (%x) status changed to present after command %s", GetLogicalAddressName(), (uint8_t)GetLogicalAddress(), ToString(command.opcode));
+        LIB_CEC->AddLog(CEC_LOG_DEBUG, "device %s (%x) status changed to present after command %s", GetLogicalAddressName(), (uint8_t)GetLogicalAddress(), ToString(command.opcode));
       m_deviceStatus = CEC_DEVICE_STATUS_PRESENT;
     }
   }
@@ -113,44 +286,78 @@ bool CCECBusDevice::HandleCommand(const cec_command &command)
   return bHandled;
 }
 
-bool CCECBusDevice::PowerOn(void)
+const char* CCECBusDevice::GetLogicalAddressName(void) const
 {
-  bool bReturn(false);
-  GetVendorId(); // ensure that we got the vendor id, because the implementations vary per vendor
+  return ToString(m_iLogicalAddress);
+}
+
+bool CCECBusDevice::IsPresent(void)
+{
+  CLockObject lock(m_mutex);
+  return m_deviceStatus == CEC_DEVICE_STATUS_PRESENT;
+}
+
+bool CCECBusDevice::IsHandledByLibCEC(void)
+{
+  CLockObject lock(m_mutex);
+  return m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC;
+}
+
+void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode)
+{
+  // some commands should never be marked as unsupported
+  if (opcode == CEC_OPCODE_VENDOR_COMMAND ||
+      opcode == CEC_OPCODE_VENDOR_COMMAND_WITH_ID ||
+      opcode == CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN ||
+      opcode == CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP ||
+      opcode == CEC_OPCODE_ABORT ||
+      opcode == CEC_OPCODE_FEATURE_ABORT ||
+      opcode == CEC_OPCODE_NONE ||
+      opcode == CEC_OPCODE_USER_CONTROL_PRESSED ||
+      opcode == CEC_OPCODE_USER_CONTROL_RELEASE)
+    return;
 
-  MarkBusy();
-  cec_power_status currentStatus = GetPowerStatus(false);
-  if (currentStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON &&
-    currentStatus != CEC_POWER_STATUS_ON)
   {
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< powering on '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    if (m_handler->PowerOn(GetMyLogicalAddress(), m_iLogicalAddress))
+    CLockObject lock(m_mutex);
+    if (m_unsupportedFeatures.find(opcode) == m_unsupportedFeatures.end())
     {
-      SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
-      bReturn = true;
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "marking opcode '%s' as unsupported feature for device '%s'", ToString(opcode), GetLogicalAddressName());
+      m_unsupportedFeatures.insert(opcode);
     }
   }
-  else
-  {
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "'%s' (%X) is already '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(currentStatus));
-  }
 
+  // signal threads that are waiting for a reponse
+  MarkBusy();
+  SignalOpcode(cec_command::GetResponseOpcode(opcode));
+  MarkReady();
+}
+
+bool CCECBusDevice::IsUnsupportedFeature(cec_opcode opcode)
+{
+  CLockObject lock(m_mutex);
+  bool bUnsupported = (m_unsupportedFeatures.find(opcode) != m_unsupportedFeatures.end());
+  if (bUnsupported)
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "'%s' is marked as unsupported feature for device '%s'", ToString(opcode), GetLogicalAddressName());
+  return bUnsupported;
+}
+
+bool CCECBusDevice::TransmitKeypress(const cec_logical_address initiator, cec_user_control_code key, bool bWait /* = true */)
+{
+  MarkBusy();
+  bool bReturn = m_handler->TransmitKeypress(initiator, m_iLogicalAddress, key, bWait);
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::Standby(void)
+bool CCECBusDevice::TransmitKeyRelease(const cec_logical_address initiator, bool bWait /* = true */)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "<< putting '%s' (%X) in standby mode", GetLogicalAddressName(), m_iLogicalAddress);
   MarkBusy();
-  bool bReturn = m_handler->TransmitStandby(GetMyLogicalAddress(), m_iLogicalAddress);
+  bool bReturn = m_handler->TransmitKeyRelease(initiator, m_iLogicalAddress, bWait);
   MarkReady();
   return bReturn;
 }
 
-/** @name Getters */
-//@{
-cec_version CCECBusDevice::GetCecVersion(bool bUpdate /* = false */)
+cec_version CCECBusDevice::GetCecVersion(const cec_logical_address initiator, bool bUpdate /* = false */)
 {
   bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
   bool bRequestUpdate(false);
@@ -162,36 +369,53 @@ cec_version CCECBusDevice::GetCecVersion(bool bUpdate /* = false */)
 
   if (bRequestUpdate)
   {
-    CheckVendorIdRequested();
-    RequestCecVersion();
+    CheckVendorIdRequested(initiator);
+    RequestCecVersion(initiator);
   }
 
   CLockObject lock(m_mutex);
   return m_cecVersion;
 }
 
-bool CCECBusDevice::RequestCecVersion(bool bWaitForResponse /* = true */)
+void CCECBusDevice::SetCecVersion(const cec_version newVersion)
+{
+  CLockObject lock(m_mutex);
+  if (m_cecVersion != newVersion)
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): CEC version %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(newVersion));
+  m_cecVersion = newVersion;
+}
+
+bool CCECBusDevice::RequestCecVersion(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
-  if (!MyLogicalAddressContains(m_iLogicalAddress) &&
+  if (!IsHandledByLibCEC() &&
       !IsUnsupportedFeature(CEC_OPCODE_GET_CEC_VERSION))
   {
     MarkBusy();
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting CEC version of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-
-    bReturn = m_handler->TransmitRequestCecVersion(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting CEC version of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestCecVersion(initiator, m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
 }
 
-const char* CCECBusDevice::GetLogicalAddressName(void) const
+bool CCECBusDevice::TransmitCECVersion(const cec_logical_address destination, bool bIsReply)
 {
-  return ToString(m_iLogicalAddress);
+  cec_version version;
+  {
+    CLockObject lock(m_mutex);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): cec version %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, ToString(m_cecVersion));
+    version = m_cecVersion;
+  }
+
+  MarkBusy();
+  bool bReturn = m_handler->TransmitCECVersion(m_iLogicalAddress, destination, version, bIsReply);
+  MarkReady();
+  return bReturn;
 }
 
-cec_menu_language &CCECBusDevice::GetMenuLanguage(bool bUpdate /* = false */)
+cec_menu_language &CCECBusDevice::GetMenuLanguage(const cec_logical_address initiator, bool bUpdate /* = false */)
 {
   bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
   bool bRequestUpdate(false);
@@ -203,46 +427,102 @@ cec_menu_language &CCECBusDevice::GetMenuLanguage(bool bUpdate /* = false */)
 
   if (bRequestUpdate)
   {
-    CheckVendorIdRequested();
-    RequestMenuLanguage();
+    CheckVendorIdRequested(initiator);
+    RequestMenuLanguage(initiator);
   }
 
   CLockObject lock(m_mutex);
   return m_menuLanguage;
 }
 
-bool CCECBusDevice::RequestMenuLanguage(bool bWaitForResponse /* = true */)
+void CCECBusDevice::SetMenuLanguage(const char *strLanguage)
+{
+  if (!strLanguage)
+    return;
+
+  CLockObject lock(m_mutex);
+  if (strcmp(strLanguage, m_menuLanguage.language))
+  {
+    memcpy(m_menuLanguage.language, strLanguage, 3);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): menu language set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, m_menuLanguage.language);
+  }
+}
+
+void CCECBusDevice::SetMenuLanguage(const cec_menu_language &language)
+{
+  if (language.device == m_iLogicalAddress)
+    SetMenuLanguage(language.language);
+}
+
+bool CCECBusDevice::RequestMenuLanguage(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
-  if (!MyLogicalAddressContains(m_iLogicalAddress) &&
+  if (!IsHandledByLibCEC() &&
       !IsUnsupportedFeature(CEC_OPCODE_GET_MENU_LANGUAGE))
   {
     MarkBusy();
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting menu language of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestMenuLanguage(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting menu language of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestMenuLanguage(initiator, m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
 }
 
-cec_menu_state CCECBusDevice::GetMenuState(void)
+bool CCECBusDevice::TransmitSetMenuLanguage(const cec_logical_address destination, bool bIsReply)
 {
-  CLockObject lock(m_mutex);
-  return m_menuState;
+  bool bReturn(false);
+  cec_menu_language language;
+  {
+    CLockObject lock(m_mutex);
+    language = m_menuLanguage;
+  }
+
+  char lang[4];
+  {
+    CLockObject lock(m_mutex);
+    lang[0] = language.language[0];
+    lang[1] = language.language[1];
+    lang[2] = language.language[2];
+    lang[3] = (char)0;
+  }
+
+  MarkBusy();
+  if (lang[0] == '?' && lang[1] == '?' && lang[2] == '?')
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): menu language feature abort", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination);
+    m_processor->TransmitAbort(m_iLogicalAddress, destination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
+    bReturn = true;
+  }
+  else
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> broadcast (F): menu language '%s'", GetLogicalAddressName(), m_iLogicalAddress, lang);
+    bReturn = m_handler->TransmitSetMenuLanguage(m_iLogicalAddress, lang, bIsReply);
+  }
+  MarkReady();
+  return bReturn;
 }
 
-cec_logical_address CCECBusDevice::GetMyLogicalAddress(void) const
+bool CCECBusDevice::TransmitOSDString(const cec_logical_address destination, cec_display_control duration, const char *strMessage, bool bIsReply)
 {
-  return m_processor->GetLogicalAddress();
+  bool bReturn(false);
+  if (!m_processor->GetDevice(destination)->IsUnsupportedFeature(CEC_OPCODE_SET_OSD_STRING))
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %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, bIsReply);
+    MarkReady();
+  }
+  return bReturn;
 }
 
-uint16_t CCECBusDevice::GetMyPhysicalAddress(void) const
+CStdString CCECBusDevice::GetCurrentOSDName(void)
 {
-  return m_processor->GetPhysicalAddress();
+  CLockObject lock(m_mutex);
+  return m_strDeviceName;
 }
 
-CStdString CCECBusDevice::GetOSDName(bool bUpdate /* = false */)
+CStdString CCECBusDevice::GetOSDName(const cec_logical_address initiator, bool bUpdate /* = false */)
 {
   bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
   bool bRequestUpdate(false);
@@ -255,30 +535,67 @@ CStdString CCECBusDevice::GetOSDName(bool bUpdate /* = false */)
 
   if (bRequestUpdate)
   {
-    CheckVendorIdRequested();
-    RequestOSDName();
+    CheckVendorIdRequested(initiator);
+    RequestOSDName(initiator);
   }
 
   CLockObject lock(m_mutex);
   return m_strDeviceName;
 }
 
-bool CCECBusDevice::RequestOSDName(bool bWaitForResponse /* = true */)
+void CCECBusDevice::SetOSDName(CStdString strName)
+{
+  CLockObject lock(m_mutex);
+  if (m_strDeviceName != strName)
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): osd name set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, strName.c_str());
+    m_strDeviceName = strName;
+  }
+}
+
+bool CCECBusDevice::RequestOSDName(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
-  if (!MyLogicalAddressContains(m_iLogicalAddress) &&
+  if (!IsHandledByLibCEC() &&
       !IsUnsupportedFeature(CEC_OPCODE_GIVE_OSD_NAME))
   {
     MarkBusy();
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting OSD name of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestOSDName(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting OSD name of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestOSDName(initiator, m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
 }
 
-uint16_t CCECBusDevice::GetPhysicalAddress(bool bSuppressUpdate /* = true */)
+bool CCECBusDevice::TransmitOSDName(const cec_logical_address destination, bool bIsReply)
+{
+  CStdString strDeviceName;
+  {
+    CLockObject lock(m_mutex);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): OSD name '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, m_strDeviceName.c_str());
+    strDeviceName = m_strDeviceName;
+  }
+
+  MarkBusy();
+  bool bReturn = m_handler->TransmitOSDName(m_iLogicalAddress, destination, strDeviceName, bIsReply);
+  MarkReady();
+  return bReturn;
+}
+
+bool CCECBusDevice::HasValidPhysicalAddress(void)
+{
+  CLockObject lock(m_mutex);
+  return CLibCEC::IsValidPhysicalAddress(m_iPhysicalAddress);
+}
+
+uint16_t CCECBusDevice::GetCurrentPhysicalAddress(void)
+{
+  CLockObject lock(m_mutex);
+  return m_iPhysicalAddress;
+}
+
+uint16_t CCECBusDevice::GetPhysicalAddress(const cec_logical_address initiator, bool bSuppressUpdate /* = false */)
 {
   if (!bSuppressUpdate)
   {
@@ -286,14 +603,14 @@ uint16_t CCECBusDevice::GetPhysicalAddress(bool bSuppressUpdate /* = true */)
     bool bRequestUpdate(false);
     {
       CLockObject lock(m_mutex);
-      bRequestUpdate = bIsPresent && m_iPhysicalAddress == 0xFFFF;
+      bRequestUpdate = bIsPresent && m_iPhysicalAddress == CEC_INVALID_PHYSICAL_ADDRESS;
     }
 
     if (bRequestUpdate)
     {
-      CheckVendorIdRequested();
-      if (!RequestPhysicalAddress())
-        CLibCEC::AddLog(CEC_LOG_ERROR, "failed to request the physical address");
+      CheckVendorIdRequested(initiator);
+      if (!RequestPhysicalAddress(initiator))
+        LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to request the physical address");
     }
   }
 
@@ -301,21 +618,58 @@ uint16_t CCECBusDevice::GetPhysicalAddress(bool bSuppressUpdate /* = true */)
   return m_iPhysicalAddress;
 }
 
-bool CCECBusDevice::RequestPhysicalAddress(bool bWaitForResponse /* = true */)
+bool CCECBusDevice::SetPhysicalAddress(uint16_t iNewAddress)
+{
+  CLockObject lock(m_mutex);
+  if (iNewAddress > 0 && m_iPhysicalAddress != iNewAddress)
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): physical address changed from %04x to %04x", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress, iNewAddress);
+    m_iPhysicalAddress = iNewAddress;
+  }
+  return true;
+}
+
+bool CCECBusDevice::RequestPhysicalAddress(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
-  if (!MyLogicalAddressContains(m_iLogicalAddress))
+  if (!IsHandledByLibCEC())
   {
     MarkBusy();
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting physical address of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestPhysicalAddress(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting physical address of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestPhysicalAddress(initiator, m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
 }
 
-cec_power_status CCECBusDevice::GetPowerStatus(bool bUpdate /* = false */)
+bool CCECBusDevice::TransmitPhysicalAddress(bool bIsReply)
+{
+  uint16_t iPhysicalAddress;
+  cec_device_type type;
+  {
+    CLockObject lock(m_mutex);
+    if (m_iPhysicalAddress == CEC_INVALID_PHYSICAL_ADDRESS)
+      return false;
+
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> broadcast (F): physical adddress %4x", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
+    iPhysicalAddress = m_iPhysicalAddress;
+    type = m_type;
+  }
+
+  MarkBusy();
+  bool bReturn = m_handler->TransmitPhysicalAddress(m_iLogicalAddress, iPhysicalAddress, type, bIsReply);
+  MarkReady();
+  return bReturn;
+}
+
+cec_power_status CCECBusDevice::GetCurrentPowerStatus(void)
+{
+  CLockObject lock(m_mutex);
+  return m_powerStatus;
+}
+
+cec_power_status CCECBusDevice::GetPowerStatus(const cec_logical_address initiator, bool bUpdate /* = false */)
 {
   bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
   bool bRequestUpdate(false);
@@ -330,30 +684,82 @@ cec_power_status CCECBusDevice::GetPowerStatus(bool bUpdate /* = false */)
 
   if (bRequestUpdate)
   {
-    CheckVendorIdRequested();
-    RequestPowerStatus();
+    CheckVendorIdRequested(initiator);
+    RequestPowerStatus(initiator, bUpdate);
   }
 
   CLockObject lock(m_mutex);
   return m_powerStatus;
 }
 
-bool CCECBusDevice::RequestPowerStatus(bool bWaitForResponse /* = true */)
+void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus)
+{
+  CLockObject lock(m_mutex);
+  if (m_powerStatus != powerStatus)
+  {
+    m_iLastPowerStateUpdate = GetTimeMs();
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): power status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_powerStatus), ToString(powerStatus));
+    m_powerStatus = powerStatus;
+  }
+}
+
+void CCECBusDevice::OnImageViewOnSent(bool bSentByLibCEC)
+{
+  CLockObject lock(m_mutex);
+  m_bImageViewOnSent = bSentByLibCEC;
+
+  if (m_powerStatus != CEC_POWER_STATUS_ON && m_powerStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON)
+  {
+    m_iLastPowerStateUpdate = GetTimeMs();
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): power status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_powerStatus), ToString(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON));
+    m_powerStatus = CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON;
+  }
+}
+
+bool CCECBusDevice::ImageViewOnSent(void)
+{
+  CLockObject lock(m_mutex);
+  return m_bImageViewOnSent;
+}
+
+bool CCECBusDevice::RequestPowerStatus(const cec_logical_address initiator, bool bUpdate, bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
-  if (!MyLogicalAddressContains(m_iLogicalAddress) &&
+  if (!IsHandledByLibCEC() &&
       !IsUnsupportedFeature(CEC_OPCODE_GIVE_DEVICE_POWER_STATUS))
   {
     MarkBusy();
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting power status of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestPowerStatus(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
+    bReturn = m_handler->TransmitRequestPowerStatus(initiator, m_iLogicalAddress, bUpdate, bWaitForResponse);
+    if (!bReturn)
+      SetPowerStatus(CEC_POWER_STATUS_UNKNOWN);
     MarkReady();
   }
   return bReturn;
 }
 
-cec_vendor_id CCECBusDevice::GetVendorId(bool bUpdate /* = false */)
+bool CCECBusDevice::TransmitPowerState(const cec_logical_address destination, bool bIsReply)
+{
+  cec_power_status state;
+  {
+    CLockObject lock(m_mutex);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, ToString(m_powerStatus));
+    state = m_powerStatus;
+  }
+
+  MarkBusy();
+  bool bReturn = m_handler->TransmitPowerState(m_iLogicalAddress, destination, state, bIsReply);
+  MarkReady();
+  return bReturn;
+}
+
+cec_vendor_id CCECBusDevice::GetCurrentVendorId(void)
+{
+  CLockObject lock(m_mutex);
+  return m_vendor;
+}
+
+cec_vendor_id CCECBusDevice::GetVendorId(const cec_logical_address initiator, bool bUpdate /* = false */)
 {
   bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
   bool bRequestUpdate(false);
@@ -364,21 +770,42 @@ cec_vendor_id CCECBusDevice::GetVendorId(bool bUpdate /* = false */)
   }
 
   if (bRequestUpdate)
-    RequestVendorId();
+    RequestVendorId(initiator);
 
   CLockObject lock(m_mutex);
   return m_vendor;
 }
 
-bool CCECBusDevice::RequestVendorId(bool bWaitForResponse /* = true */)
+const char *CCECBusDevice::GetVendorName(const cec_logical_address initiator, bool bUpdate /* = false */)
+{
+  return ToString(GetVendorId(initiator, bUpdate));
+}
+
+bool CCECBusDevice::SetVendorId(uint64_t iVendorId)
+{
+  bool bVendorChanged(false);
+
+  {
+    CLockObject lock(m_mutex);
+    bVendorChanged = (m_vendor != (cec_vendor_id)iVendorId);
+    m_vendor = (cec_vendor_id)iVendorId;
+  }
+
+  if (bVendorChanged)
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): vendor = %s (%06x)", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_vendor), m_vendor);
+
+  return bVendorChanged;
+}
+
+bool CCECBusDevice::RequestVendorId(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
-  if (!MyLogicalAddressContains(m_iLogicalAddress) && GetMyLogicalAddress() != CECDEVICE_UNKNOWN)
+  if (!IsHandledByLibCEC() && initiator != CECDEVICE_UNKNOWN)
   {
     MarkBusy();
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting vendor ID of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestVendorId(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting vendor ID of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestVendorId(initiator, m_iLogicalAddress, bWaitForResponse);
     MarkReady();
 
     if (bWaitForResponse)
@@ -387,79 +814,39 @@ bool CCECBusDevice::RequestVendorId(bool bWaitForResponse /* = true */)
   return bReturn;
 }
 
-const char *CCECBusDevice::GetVendorName(bool bUpdate /* = false */)
-{
-  return ToString(GetVendorId(bUpdate));
-}
-
-bool CCECBusDevice::MyLogicalAddressContains(cec_logical_address address) const
+bool CCECBusDevice::TransmitVendorID(const cec_logical_address destination, bool bSendAbort, bool bIsReply)
 {
-  return m_processor->HasLogicalAddress(address);
-}
+  bool bReturn(false);
+  uint64_t iVendorId;
+  {
+    CLockObject lock(m_mutex);
+    iVendorId = (uint64_t)m_vendor;
+  }
 
-bool CCECBusDevice::NeedsPoll(void)
-{
-  bool bSendPoll(false);
-  switch (m_iLogicalAddress)
+  MarkBusy();
+  if (iVendorId == CEC_VENDOR_UNKNOWN)
   {
-  case CECDEVICE_PLAYBACKDEVICE3:
-    {
-      cec_bus_device_status status = m_processor->m_busDevices[CECDEVICE_PLAYBACKDEVICE2]->GetStatus();
-      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
-    }
-    break;
-  case CECDEVICE_PLAYBACKDEVICE2:
-    {
-      cec_bus_device_status status = m_processor->m_busDevices[CECDEVICE_PLAYBACKDEVICE1]->GetStatus();
-      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
-    }
-    break;
-  case CECDEVICE_RECORDINGDEVICE3:
-    {
-      cec_bus_device_status status = m_processor->m_busDevices[CECDEVICE_RECORDINGDEVICE2]->GetStatus();
-      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
-    }
-    break;
-  case CECDEVICE_RECORDINGDEVICE2:
-    {
-      cec_bus_device_status status = m_processor->m_busDevices[CECDEVICE_RECORDINGDEVICE1]->GetStatus();
-      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
-    }
-    break;
-  case CECDEVICE_TUNER4:
-    {
-      cec_bus_device_status status = m_processor->m_busDevices[CECDEVICE_TUNER3]->GetStatus();
-      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
-    }
-    break;
-  case CECDEVICE_TUNER3:
-    {
-      cec_bus_device_status status = m_processor->m_busDevices[CECDEVICE_TUNER2]->GetStatus();
-      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
-    }
-    break;
-  case CECDEVICE_TUNER2:
+    if (bSendAbort)
     {
-      cec_bus_device_status status = m_processor->m_busDevices[CECDEVICE_TUNER1]->GetStatus();
-      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): vendor id feature abort", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination);
+      m_processor->TransmitAbort(m_iLogicalAddress, destination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
+      bReturn = true;
     }
-    break;
-  case CECDEVICE_AUDIOSYSTEM:
-  case CECDEVICE_PLAYBACKDEVICE1:
-  case CECDEVICE_RECORDINGDEVICE1:
-  case CECDEVICE_TUNER1:
-  case CECDEVICE_TV:
-    bSendPoll = true;
-    break;
-  default:
-    break;
   }
-
-  return bSendPoll;
+  else
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %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, destination, iVendorId, bIsReply);
+  }
+  MarkReady();
+  return bReturn;
 }
 
 cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bool bSuppressPoll /* = false */)
 {
+  if (m_iLogicalAddress == CECDEVICE_BROADCAST)
+    return CEC_DEVICE_STATUS_NOT_PRESENT;
+
   cec_bus_device_status status(CEC_DEVICE_STATUS_UNKNOWN);
   bool bNeedsPoll(false);
 
@@ -467,13 +854,19 @@ cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bo
     CLockObject lock(m_mutex);
     status = m_deviceStatus;
     bNeedsPoll = !bSuppressPoll &&
-        (bForcePoll || m_deviceStatus == CEC_DEVICE_STATUS_UNKNOWN);
+        m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC &&
+            // poll forced
+            (bForcePoll ||
+            // don't know the status
+            m_deviceStatus == CEC_DEVICE_STATUS_UNKNOWN ||
+            // always poll the TV if it's marked as not present
+            (m_deviceStatus == CEC_DEVICE_STATUS_NOT_PRESENT && m_iLogicalAddress == CECDEVICE_TV));
   }
 
   if (bNeedsPoll)
   {
     bool bPollAcked(false);
-    if (bNeedsPoll && NeedsPoll())
+    if (bNeedsPoll)
       bPollAcked = m_processor->PollDevice(m_iLogicalAddress);
 
     status = bPollAcked ? CEC_DEVICE_STATUS_PRESENT : CEC_DEVICE_STATUS_NOT_PRESENT;
@@ -483,331 +876,278 @@ cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bo
   return status;
 }
 
-//@}
-
-/** @name Setters */
-//@{
-void CCECBusDevice::SetCecVersion(const cec_version newVersion)
-{
-  if (m_cecVersion != newVersion)
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s (%X): CEC version %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(newVersion));
-  m_cecVersion = newVersion;
-}
-
-void CCECBusDevice::SetMenuLanguage(const cec_menu_language &language)
+void CCECBusDevice::SetDeviceStatus(const cec_bus_device_status newStatus, cec_version libCECSpecVersion /* = CEC_VERSION_1_4 */)
 {
-  CLockObject lock(m_mutex);
-  if (language.device == m_iLogicalAddress &&
-      strcmp(language.language, m_menuLanguage.language))
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): menu language set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, language.language);
-    m_menuLanguage = language;
-  }
-}
+  if (m_iLogicalAddress == CECDEVICE_UNREGISTERED)
+    return;
 
-void CCECBusDevice::SetOSDName(CStdString strName)
-{
-  CLockObject lock(m_mutex);
-  if (m_strDeviceName != strName)
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): osd name set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, strName.c_str());
-    m_strDeviceName = strName;
-  }
-}
-
-void CCECBusDevice::SetMenuState(const cec_menu_state state)
-{
-  CLockObject lock(m_mutex);
-  if (m_menuState != state)
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): menu state set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_menuState));
-    m_menuState = state;
-  }
-}
-
-void CCECBusDevice::SetInactiveSource(void)
-{
-  {
-    CLockObject lock(m_mutex);
-    if (m_bActiveSource)
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "marking %s (%X) as inactive source", GetLogicalAddressName(), m_iLogicalAddress);
-    m_bActiveSource = false;
-  }
-
-  if (MyLogicalAddressContains(m_iLogicalAddress))
-    SetPowerStatus(CEC_POWER_STATUS_STANDBY);
-}
-
-void CCECBusDevice::SetActiveSource(void)
-{
-  CLockObject lock(m_mutex);
-  if (!m_bActiveSource)
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "making %s (%x) the active source", GetLogicalAddressName(), m_iLogicalAddress);
-
-  for (int iPtr = 0; iPtr < 16; iPtr++)
-    if (iPtr != m_iLogicalAddress)
-      m_processor->m_busDevices[iPtr]->SetInactiveSource();
-
-  m_bActiveSource = true;
-  SetPowerStatus(CEC_POWER_STATUS_ON);
-}
-
-bool CCECBusDevice::TryLogicalAddress(void)
-{
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "trying logical address '%s'", GetLogicalAddressName());
-
-  m_processor->SetAckMask(0);
-  if (!TransmitPoll(m_iLogicalAddress))
-  {
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "using logical address '%s'", GetLogicalAddressName());
-    SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
-
-    return true;
-  }
-
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "logical address '%s' already taken", GetLogicalAddressName());
-  SetDeviceStatus(CEC_DEVICE_STATUS_PRESENT);
-  return false;
-}
-
-void CCECBusDevice::ResetDeviceStatus(void)
-{
-  CLockObject lock(m_mutex);
-  SetPowerStatus   (CEC_POWER_STATUS_UNKNOWN);
-  SetVendorId      (CEC_VENDOR_UNKNOWN);
-  SetMenuState     (CEC_MENU_STATE_ACTIVATED);
-  SetCecVersion    (CEC_VERSION_UNKNOWN);
-  SetStreamPath    (0);
-  SetOSDName       (ToString(m_iLogicalAddress));
-  SetInactiveSource();
-  m_iLastActive = 0;
-}
-
-void CCECBusDevice::SetDeviceStatus(const cec_bus_device_status newStatus)
-{
   {
     CLockObject lock(m_mutex);
     switch (newStatus)
     {
-    case CEC_DEVICE_STATUS_UNKNOWN:
-      if (m_deviceStatus != newStatus)
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "device status of %s changed into 'unknown'", ToString(m_iLogicalAddress));
-      ResetDeviceStatus();
-      m_deviceStatus = newStatus;
-      break;
     case CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC:
       if (m_deviceStatus != newStatus)
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "device status of %s changed into 'handled by libCEC'", ToString(m_iLogicalAddress));
+        LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'handled by libCEC'", GetLogicalAddressName(), m_iLogicalAddress);
       SetPowerStatus   (CEC_POWER_STATUS_ON);
-      SetVendorId      (CEC_VENDOR_UNKNOWN);
+      SetVendorId      (CEC_VENDOR_PULSE_EIGHT);
       SetMenuState     (CEC_MENU_STATE_ACTIVATED);
-      SetCecVersion    (CEC_VERSION_1_3A);
-      SetStreamPath    (0);
-      SetInactiveSource();
+      SetCecVersion    (libCECSpecVersion);
+      SetStreamPath    (CEC_INVALID_PHYSICAL_ADDRESS);
+      MarkAsInactiveSource();
       m_iLastActive   = 0;
       m_deviceStatus  = newStatus;
       break;
     case CEC_DEVICE_STATUS_PRESENT:
       if (m_deviceStatus != newStatus)
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "device status of %s changed into 'present'", ToString(m_iLogicalAddress));
+        LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'present'", GetLogicalAddressName(), m_iLogicalAddress);
       m_deviceStatus = newStatus;
+      m_iLastActive = GetTimeMs();
       break;
     case CEC_DEVICE_STATUS_NOT_PRESENT:
       if (m_deviceStatus != newStatus)
       {
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "device status of %s changed into 'not present'", ToString(m_iLogicalAddress));
-        ResetDeviceStatus();
+        LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'not present'", GetLogicalAddressName(), m_iLogicalAddress);
+        ResetDeviceStatus(true);
         m_deviceStatus = newStatus;
       }
       break;
+    default:
+      ResetDeviceStatus();
+      break;
     }
   }
-
-  if (newStatus == CEC_DEVICE_STATUS_PRESENT)
-    RequestVendorId(false);
 }
 
-void CCECBusDevice::SetPhysicalAddress(uint16_t iNewAddress)
+void CCECBusDevice::ResetDeviceStatus(bool bClientUnregistered /* = false */)
 {
   CLockObject lock(m_mutex);
-  if (iNewAddress > 0 && m_iPhysicalAddress != iNewAddress)
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): physical address changed from %04x to %04x", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress, iNewAddress);
-    m_iPhysicalAddress = iNewAddress;
-  }
+  SetPowerStatus   (CEC_POWER_STATUS_UNKNOWN);
+  SetVendorId      (CEC_VENDOR_UNKNOWN);
+  SetMenuState     (CEC_MENU_STATE_ACTIVATED);
+  SetCecVersion    (CEC_VERSION_UNKNOWN);
+  SetStreamPath    (CEC_INVALID_PHYSICAL_ADDRESS);
+  SetOSDName       (ToString(m_iLogicalAddress));
+  MarkAsInactiveSource(bClientUnregistered);
+
+  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;
 }
 
-void CCECBusDevice::SetStreamPath(uint16_t iNewAddress, uint16_t iOldAddress /* = 0 */)
+bool CCECBusDevice::TransmitPoll(const cec_logical_address dest, bool bUpdateDeviceStatus)
 {
-  CLockObject lock(m_mutex);
-  if (iNewAddress > 0)
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): stream path changed from %04x to %04x", GetLogicalAddressName(), m_iLogicalAddress, iOldAddress == 0 ? m_iStreamPath : iOldAddress, iNewAddress);
-    m_iStreamPath = iNewAddress;
+  bool bReturn(false);
+  cec_logical_address destination(dest);
+  if (destination == CECDEVICE_UNKNOWN)
+    destination = m_iLogicalAddress;
 
-    // suppress polls when searching for a device
-    CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
-    if (device)
-    {
-      // if a device is found with the new physical address, mark it as active, which will automatically mark all other devices as inactive
-      device->SetActiveSource();
-    }
-    else
-    {
-      // try to find the device with the old address, and mark it as inactive when found
-      device = m_processor->GetDeviceByPhysicalAddress(iOldAddress);
-      if (device)
-        device->SetInactiveSource();
-    }
+  CCECBusDevice *destDevice = m_processor->GetDevice(destination);
+  if (destDevice->m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
+    return bReturn;
 
-    if (iNewAddress > 0)
-    {
-      lock.Unlock();
-      SetPowerStatus(CEC_POWER_STATUS_ON);
-    }
-  }
+  MarkBusy();
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): POLL", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest);
+  bReturn = m_handler->TransmitPoll(m_iLogicalAddress, destination, false);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, bReturn ? ">> POLL sent" : ">> POLL not sent");
+
+  if (bUpdateDeviceStatus)
+    destDevice->SetDeviceStatus(bReturn ? CEC_DEVICE_STATUS_PRESENT : CEC_DEVICE_STATUS_NOT_PRESENT);
+
+  MarkReady();
+  return bReturn;
 }
 
-void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus)
+void CCECBusDevice::HandlePoll(const cec_logical_address destination)
 {
-  CLockObject lock(m_mutex);
-  if (m_powerStatus != powerStatus)
+  if (destination >= 0 && destination < CECDEVICE_BROADCAST)
   {
-    m_iLastPowerStateUpdate = GetTimeMs();
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): power status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_powerStatus), ToString(powerStatus));
-    m_powerStatus = powerStatus;
+    CCECBusDevice *device = m_processor->GetDevice(destination);
+    if (device)
+      device->HandlePollFrom(m_iLogicalAddress);
   }
 }
 
-void CCECBusDevice::MarkBusy(void)
+void CCECBusDevice::HandlePollFrom(const cec_logical_address initiator)
 {
-  CLockObject handlerLock(m_handlerMutex);
-  ++m_iHandlerUseCount;
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< POLL: %s (%x) -> %s (%x)", ToString(initiator), initiator, ToString(m_iLogicalAddress), m_iLogicalAddress);
+  m_bAwaitingReceiveFailed = true;
 }
 
-void CCECBusDevice::MarkReady(void)
+bool CCECBusDevice::HandleReceiveFailed(void)
 {
-  CLockObject handlerLock(m_handlerMutex);
-  if (m_iHandlerUseCount > 0)
-    --m_iHandlerUseCount;
+  bool bReturn = m_bAwaitingReceiveFailed;
+  m_bAwaitingReceiveFailed = false;
+  return bReturn;
 }
 
-bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */)
+cec_menu_state CCECBusDevice::GetMenuState(const cec_logical_address UNUSED(initiator))
 {
-  bool bInitHandler(false);
+  CLockObject lock(m_mutex);
+  return m_menuState;
+}
+
+void CCECBusDevice::SetMenuState(const cec_menu_state state)
+{
+  CLockObject lock(m_mutex);
+  if (m_menuState != state)
   {
-    CTryLockObject lock(m_mutex);
-    if (!lock.IsLocked())
-      return false;
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): menu state set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_menuState));
+    m_menuState = state;
+  }
+}
 
-    CLockObject handlerLock(m_handlerMutex);
-    if (m_iHandlerUseCount > 0)
-      return false;
+bool CCECBusDevice::TransmitMenuState(const cec_logical_address dest, bool bIsReply)
+{
+  cec_menu_state menuState;
+  {
+    CLockObject lock(m_mutex);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): menu state '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_menuState));
+    menuState = m_menuState;
+  }
 
-    MarkBusy();
+  MarkBusy();
+  bool bReturn = m_handler->TransmitMenuState(m_iLogicalAddress, dest, menuState, bIsReply);
+  MarkReady();
+  return bReturn;
+}
 
-    if (m_vendor != m_handler->GetVendorId())
-    {
-      if (CCECCommandHandler::HasSpecificHandler(m_vendor))
-      {
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "replacing the command handler for device '%s' (%x)", GetLogicalAddressName(), GetLogicalAddress());
-        delete m_handler;
+bool CCECBusDevice::ActivateSource(uint64_t iDelay /* = 0 */)
+{
+  MarkAsActiveSource();
+  MarkBusy();
+  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;
+}
 
-        switch (m_vendor)
-        {
-        case CEC_VENDOR_SAMSUNG:
-          m_handler = new CANCommandHandler(this);
-          break;
-        case CEC_VENDOR_LG:
-          m_handler = new CSLCommandHandler(this);
-          break;
-        case CEC_VENDOR_PANASONIC:
-          m_handler = new CVLCommandHandler(this);
-          break;
-        default:
-          m_handler = new CCECCommandHandler(this);
-          break;
-        }
+bool CCECBusDevice::RequestActiveSource(bool bWaitForResponse /* = true */)
+{
+  bool bReturn(false);
 
-        m_handler->SetVendorId(m_vendor);
-        bInitHandler = true;
-      }
-    }
+  if (IsHandledByLibCEC())
+  {
+    MarkBusy();
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting active source");
+
+    bReturn = m_handler->TransmitRequestActiveSource(m_iLogicalAddress, bWaitForResponse);
+    MarkReady();
   }
+  return bReturn;
+}
 
-  if (bInitHandler)
+void CCECBusDevice::MarkAsActiveSource(void)
+{
+  bool bWasActivated(false);
+
+  // set the power status to powered on
+  SetPowerStatus(CEC_POWER_STATUS_ON);
+
+  // mark this device as active source
   {
-    m_handler->InitHandler();
+    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);
 
-    if (bActivateSource && m_processor->GetLogicalAddresses().IsSet(m_iLogicalAddress) && m_processor->IsInitialised() && IsActiveSource())
-      m_handler->ActivateSource();
+    m_bActiveSource = true;
   }
 
-  MarkReady();
+  CCECBusDevice* tv = m_processor->GetDevice(CECDEVICE_TV);
+  if (tv)
+    tv->OnImageViewOnSent(false);
 
-  return 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();
+
+  if (bWasActivated && IsHandledByLibCEC())
+    m_processor->SetActiveSource(true, false);
+
+  CCECClient *client = GetClient();
+  if (client)
+    client->SourceActivated(m_iLogicalAddress);
 }
 
-bool CCECBusDevice::SetVendorId(uint64_t iVendorId)
+void CCECBusDevice::MarkAsInactiveSource(bool bClientUnregistered /* = false */)
 {
-  bool bVendorChanged(false);
-
+  bool bWasDeactivated(false);
   {
     CLockObject lock(m_mutex);
-    bVendorChanged = (m_vendor != (cec_vendor_id)iVendorId);
-    m_vendor = (cec_vendor_id)iVendorId;
+    if (m_bActiveSource)
+    {
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "marking %s (%X) as inactive source", GetLogicalAddressName(), m_iLogicalAddress);
+      bWasDeactivated = true;
+    }
+    m_bActiveSource = false;
   }
 
-  if (bVendorChanged)
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s (%X): vendor = %s (%06x)", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_vendor), m_vendor);
-
-  return bVendorChanged;
+  if (bWasDeactivated)
+  {
+    if (IsHandledByLibCEC())
+      m_processor->SetActiveSource(false, bClientUnregistered);
+    CCECClient *client = GetClient();
+    if (client)
+      client->SourceDeactivated(m_iLogicalAddress);
+  }
 }
-//@}
 
-/** @name Transmit methods */
-//@{
-bool CCECBusDevice::TransmitActiveSource(void)
+bool CCECBusDevice::TransmitActiveSource(bool bIsReply)
 {
   bool bSendActiveSource(false);
+  uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
 
   {
     CLockObject lock(m_mutex);
+    if (!HasValidPhysicalAddress())
+    {
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X) has an invalid physical address (%04x), not sending active source commands", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
+      return false;
+    }
+
+    iPhysicalAddress = m_iPhysicalAddress;
+
     if (m_powerStatus != CEC_POWER_STATUS_ON && m_powerStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON)
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not powered on", GetLogicalAddressName(), m_iLogicalAddress);
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not powered on", GetLogicalAddressName(), m_iLogicalAddress);
     else if (m_bActiveSource)
     {
-      CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): active source (%4x)", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
+      LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): active source (%4x)", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
       bSendActiveSource = true;
     }
     else
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not the active source", GetLogicalAddressName(), m_iLogicalAddress);
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not the active source", GetLogicalAddressName(), m_iLogicalAddress);
   }
 
+  bool bActiveSourceSent(false);
   if (bSendActiveSource)
   {
     MarkBusy();
-    m_handler->TransmitActiveSource(m_iLogicalAddress, m_iPhysicalAddress);
+    bActiveSourceSent = m_handler->TransmitActiveSource(m_iLogicalAddress, iPhysicalAddress, bIsReply);
     MarkReady();
-    return true;
-  }
-
-  return false;
-}
-
-bool CCECBusDevice::TransmitCECVersion(cec_logical_address dest)
-{
-  cec_version version;
-  {
-    CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): cec version %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_cecVersion));
-    version = m_cecVersion;
   }
 
-  MarkBusy();
-  bool bReturn = m_handler->TransmitCECVersion(m_iLogicalAddress, dest, version);
-  MarkReady();
-  return bReturn;
+  return bActiveSourceSent;
 }
 
 bool CCECBusDevice::TransmitImageViewOn(void)
@@ -816,15 +1156,33 @@ bool CCECBusDevice::TransmitImageViewOn(void)
     CLockObject lock(m_mutex);
     if (m_powerStatus != CEC_POWER_STATUS_ON && m_powerStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON)
     {
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not powered on", GetLogicalAddressName(), m_iLogicalAddress);
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not powered on", GetLogicalAddressName(), m_iLogicalAddress);
       return false;
     }
   }
 
+  CCECBusDevice* tv = m_processor->GetDevice(CECDEVICE_TV);
+  if (!tv)
+  {
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - couldn't get TV instance", __FUNCTION__);
+    return false;
+  }
+
+  if (tv->ImageViewOnSent())
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - 'image view on' already sent", __FUNCTION__);
+    return true;
+  }
+
+  bool bImageViewOnSent(false);
   MarkBusy();
-  m_handler->TransmitImageViewOn(m_iLogicalAddress, CECDEVICE_TV);
+  bImageViewOnSent = m_handler->TransmitImageViewOn(m_iLogicalAddress, CECDEVICE_TV);
   MarkReady();
-  return true;
+
+  if (bImageViewOnSent)
+    tv->OnImageViewOnSent(true);
+
+  return bImageViewOnSent;
 }
 
 bool CCECBusDevice::TransmitInactiveSource(void)
@@ -832,7 +1190,7 @@ bool CCECBusDevice::TransmitInactiveSource(void)
   uint16_t iPhysicalAddress;
   {
     CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): inactive source", GetLogicalAddressName(), m_iLogicalAddress);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): inactive source", GetLogicalAddressName(), m_iLogicalAddress);
     iPhysicalAddress = m_iPhysicalAddress;
   }
 
@@ -842,261 +1200,273 @@ bool CCECBusDevice::TransmitInactiveSource(void)
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitMenuState(cec_logical_address dest)
+bool CCECBusDevice::TransmitPendingActiveSourceCommands(void)
 {
-  cec_menu_state menuState;
-  {
-    CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): menu state '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_menuState));
-    menuState = m_menuState;
-  }
-
   MarkBusy();
-  bool bReturn = m_handler->TransmitMenuState(m_iLogicalAddress, dest, menuState);
+  bool bReturn = m_handler->ActivateSource(true);
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitOSDName(cec_logical_address dest)
+void CCECBusDevice::SetActiveRoute(uint16_t iRoute)
 {
-  CStdString strDeviceName;
-  {
-    CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): OSD name '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, m_strDeviceName.c_str());
-    strDeviceName = m_strDeviceName;
-  }
+  SetPowerStatus(CEC_POWER_STATUS_ON);
 
-  MarkBusy();
-  bool bReturn = m_handler->TransmitOSDName(m_iLogicalAddress, dest, strDeviceName);
-  MarkReady();
-  return bReturn;
-}
+  CCECDeviceMap* map = m_processor->GetDevices();
+  if (!map)
+    return;
 
-bool CCECBusDevice::TransmitOSDString(cec_logical_address dest, cec_display_control duration, const char *strMessage)
-{
-  bool bReturn(false);
-  if (!m_processor->m_busDevices[dest]->IsUnsupportedFeature(CEC_OPCODE_SET_OSD_STRING))
+  CCECBusDevice* newRoute = m_processor->GetDeviceByPhysicalAddress(iRoute, true);
+  if (newRoute && newRoute->IsHandledByLibCEC())
   {
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): display OSD message '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, strMessage);
-    MarkBusy();
-    bReturn = m_handler->TransmitOSDString(m_iLogicalAddress, dest, duration, strMessage);
-    MarkReady();
+    // we were made the active source, send notification
+    newRoute->ActivateSource();
   }
-  return bReturn;
 }
 
-bool CCECBusDevice::TransmitPhysicalAddress(void)
+void CCECBusDevice::SetStreamPath(uint16_t iNewAddress, uint16_t iOldAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */)
 {
-  uint16_t iPhysicalAddress;
-  cec_device_type type;
-  {
-    CLockObject lock(m_mutex);
-    if (m_iPhysicalAddress == 0xffff)
-      return false;
+  if (iNewAddress != CEC_INVALID_PHYSICAL_ADDRESS)
+    SetPowerStatus(CEC_POWER_STATUS_ON);
 
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): physical adddress %4x", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
-    iPhysicalAddress = m_iPhysicalAddress;
-    type = m_type;
+  CLockObject lock(m_mutex);
+  if (iNewAddress != m_iStreamPath)
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): stream path changed from %04x to %04x", GetLogicalAddressName(), m_iLogicalAddress, iOldAddress == 0 ? m_iStreamPath : iOldAddress, iNewAddress);
+    m_iStreamPath = iNewAddress;
   }
 
-  MarkBusy();
-  bool bReturn = m_handler->TransmitPhysicalAddress(m_iLogicalAddress, iPhysicalAddress, type);
-  MarkReady();
-  return bReturn;
-}
+  if (!LIB_CEC->IsValidPhysicalAddress(iNewAddress))
+    return;
 
-bool CCECBusDevice::TransmitSetMenuLanguage(cec_logical_address dest)
-{
-  bool bReturn(false);
-  cec_menu_language language = GetMenuLanguage();
-
-  char lang[3];
+  CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
+  if (device)
   {
-    CLockObject lock(m_mutex);
-    lang[0] = language.language[0];
-    lang[1] = language.language[1];
-    lang[2] = language.language[2];
-  }
+    // if a device is found with the new physical address, mark it as active, which will automatically mark all other devices as inactive
+    device->MarkAsActiveSource();
 
-  MarkBusy();
-  if (lang[0] == '?' && lang[1] == '?' && lang[2] == '?')
-  {
-      CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): Menu language feature abort", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest);
-      m_processor->TransmitAbort(dest, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
-      bReturn = true;
+    // respond with an active source message if this device is handled by libCEC
+    if (device->IsHandledByLibCEC())
+      device->TransmitActiveSource(true);
   }
   else
   {
-      CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): Menu language '%s'", GetLogicalAddressName(), m_iLogicalAddress, lang);
-      bReturn = m_handler->TransmitSetMenuLanguage(m_iLogicalAddress, lang);
+    // try to find the device with the old address, and mark it as inactive when found
+    device = m_processor->GetDeviceByPhysicalAddress(iOldAddress);
+    if (device)
+      device->MarkAsInactiveSource();
   }
-  MarkReady();
-  return bReturn;
 }
 
-bool CCECBusDevice::TransmitPoll(cec_logical_address dest)
+bool CCECBusDevice::PowerOn(const cec_logical_address initiator)
 {
   bool bReturn(false);
-  if (dest == CECDEVICE_UNKNOWN)
-    dest = m_iLogicalAddress;
-
-  CCECBusDevice *destDevice = m_processor->m_busDevices[dest];
-  if (destDevice->m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
-    return bReturn;
+  GetVendorId(initiator); // ensure that we got the vendor id, because the implementations vary per vendor
 
   MarkBusy();
-  CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): POLL", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest);
-  bReturn = m_handler->TransmitPoll(m_iLogicalAddress, dest);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, bReturn ? ">> POLL sent" : ">> POLL not sent");
-
-  CLockObject lock(m_mutex);
-  if (bReturn)
+  cec_power_status currentStatus;
+  if (m_iLogicalAddress == CECDEVICE_TV ||
+      ((currentStatus = GetPowerStatus(initiator, false)) != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON &&
+        currentStatus != CEC_POWER_STATUS_ON))
   {
-    m_iLastActive = GetTimeMs();
-    destDevice->m_deviceStatus = CEC_DEVICE_STATUS_PRESENT;
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< powering on '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    bReturn = m_handler->PowerOn(initiator, m_iLogicalAddress);
   }
   else
-    destDevice->m_deviceStatus = CEC_DEVICE_STATUS_NOT_PRESENT;
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "'%s' (%X) is already '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(currentStatus));
+  }
 
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitPowerState(cec_logical_address dest)
+bool CCECBusDevice::Standby(const cec_logical_address initiator)
 {
-  cec_power_status state;
-  {
-    CLockObject lock(m_mutex);
-    if (!IsActiveSource())
-    {
-      CLibCEC::AddLog(CEC_LOG_NOTICE, "power state requested of %s (%X), but we are not the active source. setting power state to standby", GetLogicalAddressName(), m_iLogicalAddress);
-      SetPowerStatus(CEC_POWER_STATUS_STANDBY);
-    }
-
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_powerStatus));
-    state = m_powerStatus;
-  }
+  GetVendorId(initiator); // ensure that we got the vendor id, because the implementations vary per vendor
 
+  LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< putting '%s' (%X) in standby mode", GetLogicalAddressName(), m_iLogicalAddress);
   MarkBusy();
-  bool bReturn = m_handler->TransmitPowerState(m_iLogicalAddress, dest, state);
+  bool bReturn = m_handler->TransmitStandby(initiator, m_iLogicalAddress);
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitVendorID(cec_logical_address dest, bool bSendAbort /* = true */)
+bool CCECBusDevice::NeedsPoll(void)
 {
-  bool bReturn(false);
-  uint64_t iVendorId;
+  bool bSendPoll(false);
+  cec_logical_address pollAddress(CECDEVICE_UNKNOWN);
+  switch (m_iLogicalAddress)
   {
-    CLockObject lock(m_mutex);
-    iVendorId = (uint64_t)m_vendor;
+  case CECDEVICE_PLAYBACKDEVICE3:
+    pollAddress = CECDEVICE_PLAYBACKDEVICE2;
+    break;
+  case CECDEVICE_PLAYBACKDEVICE2:
+    pollAddress = CECDEVICE_PLAYBACKDEVICE1;
+    break;
+  case CECDEVICE_RECORDINGDEVICE3:
+    pollAddress = CECDEVICE_RECORDINGDEVICE2;
+    break;
+  case CECDEVICE_RECORDINGDEVICE2:
+    pollAddress = CECDEVICE_RECORDINGDEVICE1;
+    break;
+  case CECDEVICE_TUNER4:
+    pollAddress = CECDEVICE_TUNER3;
+    break;
+  case CECDEVICE_TUNER3:
+    pollAddress = CECDEVICE_TUNER2;
+    break;
+  case CECDEVICE_TUNER2:
+    pollAddress = CECDEVICE_TUNER1;
+    break;
+  case CECDEVICE_AUDIOSYSTEM:
+  case CECDEVICE_PLAYBACKDEVICE1:
+  case CECDEVICE_RECORDINGDEVICE1:
+  case CECDEVICE_TUNER1:
+  case CECDEVICE_TV:
+    bSendPoll = true;
+    break;
+  default:
+    break;
   }
 
-  MarkBusy();
-  if (iVendorId == CEC_VENDOR_UNKNOWN)
+  if (!bSendPoll && pollAddress != CECDEVICE_UNKNOWN)
   {
-    if (bSendAbort)
+    CCECBusDevice *device = m_processor->GetDevice(pollAddress);
+    if (device)
     {
-      CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): vendor id feature abort", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest);
-      m_processor->TransmitAbort(dest, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
-      bReturn = true;
+      cec_bus_device_status status = device->GetStatus();
+      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
+    }
+    else
+    {
+      bSendPoll = true;
     }
   }
-  else
+
+  return bSendPoll;
+}
+
+void CCECBusDevice::CheckVendorIdRequested(const cec_logical_address initiator)
+{
+  bool bRequestVendorId(false);
   {
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): vendor id %s (%x)", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString((cec_vendor_id)iVendorId), iVendorId);
-    bReturn = m_handler->TransmitVendorID(m_iLogicalAddress, iVendorId);
+    CLockObject lock(m_mutex);
+    bRequestVendorId = !m_bVendorIdRequested;
+    m_bVendorIdRequested = true;
+  }
+
+  if (bRequestVendorId)
+  {
+    ReplaceHandler(false);
+    GetVendorId(initiator);
   }
-  MarkReady();
-  return bReturn;
 }
+//@}
 
-bool CCECBusDevice::TransmitKeypress(cec_user_control_code key, bool bWait /* = true */)
+CCECAudioSystem *CCECBusDevice::AsAudioSystem(void)
 {
-  MarkBusy();
-  bool bReturn = m_handler->TransmitKeypress(m_processor->GetLogicalAddress(), m_iLogicalAddress, key, bWait);
-  MarkReady();
-  return bReturn;
+  return AsAudioSystem(this);
 }
 
-bool CCECBusDevice::TransmitKeyRelease(bool bWait /* = true */)
+CCECPlaybackDevice *CCECBusDevice::AsPlaybackDevice(void)
 {
-  MarkBusy();
-  bool bReturn = m_handler->TransmitKeyRelease(m_processor->GetLogicalAddress(), m_iLogicalAddress, bWait);
-  MarkReady();
-  return bReturn;
+  return AsPlaybackDevice(this);
 }
 
-bool CCECBusDevice::IsUnsupportedFeature(cec_opcode opcode)
+CCECRecordingDevice *CCECBusDevice::AsRecordingDevice(void)
 {
-  CLockObject lock(m_mutex);
-  bool bUnsupported = (m_unsupportedFeatures.find(opcode) != m_unsupportedFeatures.end());
-  if (bUnsupported)
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "'%s' is marked as unsupported feature for device '%s'", ToString(opcode), GetLogicalAddressName());
-  return bUnsupported;
+  return AsRecordingDevice(this);
 }
 
-void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode)
+CCECTuner *CCECBusDevice::AsTuner(void)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "marking opcode '%s' as unsupported feature for device '%s'", ToString(opcode), GetLogicalAddressName());
-  m_unsupportedFeatures.insert(opcode);
+  return AsTuner(this);
 }
 
-bool CCECBusDevice::ActivateSource(void)
+CCECTV *CCECBusDevice::AsTV(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "activating source '%s'", ToString(m_iLogicalAddress));
-  MarkBusy();
-  bool bReturn = m_handler->ActivateSource();
-  MarkReady();
-  return bReturn;
+  return AsTV(this);
 }
 
-void CCECBusDevice::HandlePoll(cec_logical_address destination)
+CCECAudioSystem *CCECBusDevice::AsAudioSystem(CCECBusDevice *device)
 {
-  if (destination >= 0 && destination < CECDEVICE_BROADCAST)
-  {
-    CCECBusDevice *device = m_processor->m_busDevices[destination];
-    if (device)
-      device->HandlePollFrom(m_iLogicalAddress);
-  }
+  if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
+    return static_cast<CCECAudioSystem *>(device);
+  return NULL;
 }
 
-void CCECBusDevice::HandlePollFrom(cec_logical_address initiator)
+CCECPlaybackDevice *CCECBusDevice::AsPlaybackDevice(CCECBusDevice *device)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "<< POLL: %s (%x) -> %s (%x)", ToString(initiator), initiator, ToString(m_iLogicalAddress), m_iLogicalAddress);
-  m_bAwaitingReceiveFailed = true;
+  if (device &&
+      (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE ||
+       device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE))
+    return static_cast<CCECPlaybackDevice *>(device);
+  return NULL;
 }
 
-bool CCECBusDevice::HandleReceiveFailed(void)
+CCECRecordingDevice *CCECBusDevice::AsRecordingDevice(CCECBusDevice *device)
 {
-  bool bReturn = m_bAwaitingReceiveFailed;
-  m_bAwaitingReceiveFailed = false;
-  return bReturn;
+  if (device && device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)
+    return static_cast<CCECRecordingDevice *>(device);
+  return NULL;
 }
 
-void CCECBusDevice::CheckVendorIdRequested(void)
+CCECTuner *CCECBusDevice::AsTuner(CCECBusDevice *device)
 {
-  bool bRequestVendorId(false);
-  {
-    CLockObject lock(m_mutex);
-    bRequestVendorId = !m_bVendorIdRequested;
-    m_bVendorIdRequested = true;
-  }
+  if (device && device->GetType() == CEC_DEVICE_TYPE_TUNER)
+    return static_cast<CCECTuner *>(device);
+  return NULL;
+}
 
-  if (bRequestVendorId)
+CCECTV *CCECBusDevice::AsTV(CCECBusDevice *device)
+{
+  if (device && device->GetType() == CEC_DEVICE_TYPE_TV)
+    return static_cast<CCECTV *>(device);
+  return NULL;
+}
+
+void CCECBusDevice::MarkBusy(void)
+{
+  CLockObject handlerLock(m_handlerMutex);
+  ++m_iHandlerUseCount;
+}
+
+void CCECBusDevice::MarkReady(void)
+{
+  CLockObject handlerLock(m_handlerMutex);
+  if (m_iHandlerUseCount > 0)
+    --m_iHandlerUseCount;
+}
+
+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, false))
   {
-    ReplaceHandler(false);
-    GetVendorId();
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "using logical address '%s'", GetLogicalAddressName());
+    SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC, libCECSpecVersion);
+
+    return true;
   }
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address '%s' already taken", GetLogicalAddressName());
+  SetDeviceStatus(CEC_DEVICE_STATUS_PRESENT);
+  return false;
 }
 
-bool CCECBusDevice::TransmitPendingActiveSourceCommands(void)
+CCECClient *CCECBusDevice::GetClient(void)
 {
-  MarkBusy();
-  bool bReturn = m_handler->TransmitPendingActiveSourceCommands();
-  MarkReady();
-  return bReturn;
+  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);
+}