Merge branch 'master' into release
authorLars Op den Kamp <lars@opdenkamp.eu>
Tue, 15 May 2012 10:31:27 +0000 (12:31 +0200)
committerLars Op den Kamp <lars@opdenkamp.eu>
Tue, 15 May 2012 10:31:27 +0000 (12:31 +0200)
60 files changed:
ChangeLog
README
configure.ac
debian/changelog
debian/control
include/cec.h
include/cectypes.h
project/cec-config.rc
project/libcec.rc
project/libcec.vcxproj
project/libcec.vcxproj.filters
project/testclient.rc
src/CecSharpTester/Properties/AssemblyInfo.cs
src/LibCecSharp/AssemblyInfo.cpp
src/LibCecSharp/CecSharpTypes.h
src/LibCecSharp/LibCecSharp.cpp
src/cec-config-gui/Properties/AssemblyInfo.cs
src/cec-config/cec-config.cpp
src/lib/CECClient.cpp [new file with mode: 0644]
src/lib/CECClient.h [new file with mode: 0644]
src/lib/CECInputBuffer.h [new file with mode: 0644]
src/lib/CECProcessor.cpp
src/lib/CECProcessor.h
src/lib/LibCEC.cpp
src/lib/LibCEC.h
src/lib/Makefile.am
src/lib/adapter/AdapterCommunication.h
src/lib/adapter/USBCECAdapterCommands.cpp
src/lib/adapter/USBCECAdapterCommands.h
src/lib/adapter/USBCECAdapterCommunication.cpp
src/lib/adapter/USBCECAdapterCommunication.h
src/lib/adapter/USBCECAdapterMessage.cpp
src/lib/adapter/USBCECAdapterMessageQueue.cpp
src/lib/adapter/USBCECAdapterMessageQueue.h
src/lib/devices/CECAudioSystem.cpp
src/lib/devices/CECAudioSystem.h
src/lib/devices/CECBusDevice.cpp
src/lib/devices/CECBusDevice.h
src/lib/devices/CECDeviceMap.cpp [new file with mode: 0644]
src/lib/devices/CECDeviceMap.h [new file with mode: 0644]
src/lib/devices/CECPlaybackDevice.cpp
src/lib/devices/CECPlaybackDevice.h
src/lib/devices/CECRecordingDevice.cpp
src/lib/devices/CECRecordingDevice.h
src/lib/devices/CECTV.cpp
src/lib/devices/CECTV.h
src/lib/devices/CECTuner.cpp
src/lib/devices/CECTuner.h
src/lib/implementations/ANCommandHandler.cpp
src/lib/implementations/CECCommandHandler.cpp
src/lib/implementations/CECCommandHandler.h
src/lib/implementations/SLCommandHandler.cpp
src/lib/implementations/SLCommandHandler.h
src/lib/implementations/VLCommandHandler.cpp
src/lib/platform/posix/os-threads.h
src/lib/platform/posix/os-types.h
src/lib/platform/posix/serialport.cpp
src/lib/platform/threads/threads.h
src/lib/platform/windows/os-threads.h
src/testclient/main.cpp

index aad871a0cbf9acf313f713634f76cef3fce5f4a1..e964a490978c12c9732a795cb737b994076346d5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+libcec (1.6-4) unstable; urgency=low
+
+  * changed/added:
+    * serial port locking on linux
+    * cec-client: added -m/--monitor startup options, which will start a
+      monitor-only client. use 'mon 0' to switch to a normal client
+    * display an alert message when the firmware of the adapter can be upgraded
+    * added CEC 1.4 opcodes
+    * send a feature abort again for all unhandled commands
+    * refactored CLibCEC so a client registers itself within CCECProcessor,
+      which then allocates one or more logical addresses for it.
+
+  * interface changes:
+    * added a new setting, bMonitorOnly, which will start a monitor-only client
+
+  * fixed:
+    * ensure that we don't crash out when trying to stop a thread that hasn't
+      been started yet
+    * don't send a 'disconnected' alert when the close method is called
+    * use the correct source when transmitting an abort message as a reposonse
+      to a vendor command with id
+    * removed statics
+    * check the client version, not the server version in
+      libcec_configuration::operator==()
+    * cec-config: fix physical address detection
+    * LibCecSharp: fixed buffer overrun when copying libcec_configuration.
+      fixes crash when trying to save the configuration in the config gui.
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com>  Mon, 14 May 2012 22:39:00 +0100
+
 libcec (1.6-3) unstable; urgency=low
 
   * changed/added:
diff --git a/README b/README
index d90e7ff3cfdc1d68c24e71db5b9e94ff6f65c123..a0ced0aa5408766a4dde602faba5e46d81ca32aa 100644 (file)
--- a/README
+++ b/README
@@ -7,6 +7,7 @@ This library provides support for the Pulse-Eight USB-CEC adapter.
 libCEC needs the following dependencies in order to work correctly:
 * udev v151 or later
 * cdc-acm support compiled into the kernel or available as module
+* liblockdev 1.0 or later
 
 To compile libCEC on Linux, you'll need the following dependencies:
 * autoconf 2.13 or later
@@ -15,6 +16,7 @@ To compile libCEC on Linux, you'll need the following dependencies:
 * libtool
 * udev development headers v151 or later
 * gcc 4.2 or later
+* liblockdev 1.0 development headers
 
 To compile, execute the following commands:
 # autoreconf -vif
index f8b8701be858a9fa009e3698083232941e0fdfed..b8d6031a1871832ede4f21f90b1c06b3acff8e62 100644 (file)
@@ -8,7 +8,8 @@ has_libudev="yes"
 case "${host}" in
   *-*-linux*)
     PKG_CHECK_MODULES([UDEV],[libudev],,[has_libudev="no"]; AC_MSG_WARN("library 'udev' is missing - adapter detection will not be available"))
-    LIBS+=" -lrt"
+    AC_CHECK_HEADER(lockdev.h,, AC_MSG_ERROR("required library 'liblockdev' is missing"))
+    LIBS+=" -lrt -llockdev"
     ;;
   *-apple-darwin*)
     has_libudev="no";
index aad871a0cbf9acf313f713634f76cef3fce5f4a1..e964a490978c12c9732a795cb737b994076346d5 100644 (file)
@@ -1,3 +1,33 @@
+libcec (1.6-4) unstable; urgency=low
+
+  * changed/added:
+    * serial port locking on linux
+    * cec-client: added -m/--monitor startup options, which will start a
+      monitor-only client. use 'mon 0' to switch to a normal client
+    * display an alert message when the firmware of the adapter can be upgraded
+    * added CEC 1.4 opcodes
+    * send a feature abort again for all unhandled commands
+    * refactored CLibCEC so a client registers itself within CCECProcessor,
+      which then allocates one or more logical addresses for it.
+
+  * interface changes:
+    * added a new setting, bMonitorOnly, which will start a monitor-only client
+
+  * fixed:
+    * ensure that we don't crash out when trying to stop a thread that hasn't
+      been started yet
+    * don't send a 'disconnected' alert when the close method is called
+    * use the correct source when transmitting an abort message as a reposonse
+      to a vendor command with id
+    * removed statics
+    * check the client version, not the server version in
+      libcec_configuration::operator==()
+    * cec-config: fix physical address detection
+    * LibCecSharp: fixed buffer overrun when copying libcec_configuration.
+      fixes crash when trying to save the configuration in the config gui.
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com>  Mon, 14 May 2012 22:39:00 +0100
+
 libcec (1.6-3) unstable; urgency=low
 
   * changed/added:
index 570934984b4f57f395a546f2f01a66138760a0d2..fa8b7616060336c527e350d4a175e07af9a94c13 100644 (file)
@@ -2,13 +2,13 @@ Source: libcec
 Section: video
 Priority: extra
 Maintainer: Lars Op den Kamp <lars.opdenkamp@pulse-eight.com>
-Build-Depends: debhelper (>= 7), libudev-dev, mime-support, locales, gawk, autotools-dev, autoconf, automake, pkg-config
+Build-Depends: debhelper (>= 7), libudev-dev, mime-support, locales, gawk, autotools-dev, autoconf, automake, pkg-config, liblockdev1-dev
 Standards-Version: 3.8.3
 Homepage: http://www.pulse-eight.net/
 
 Package: libcec
 Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
+Depends: ${shlibs:Depends}, ${misc:Depends}, liblockdev1
 Description: Pulse-Eight CEC adapter control library
  With this library you can access your Pulse-Eight CEC adapter.
 
index fada217ef52007f514013d9b05c4a6e18f7180a2..abfde74b7e6a9807de2eb59c4eeb51eb7a7c4ded 100644 (file)
@@ -36,7 +36,7 @@
 
 #include "cectypes.h"
 
-#define LIBCEC_VERSION_CURRENT CEC_SERVER_VERSION_1_6_2
+#define LIBCEC_VERSION_CURRENT CEC_SERVER_VERSION_1_6_3
 
 namespace CEC
 {
index ca065c606051ade2507c39ab669db40004d4a308..77b9927d3dca42823779cd844c95339cee2bfb29 100644 (file)
@@ -584,6 +584,15 @@ typedef enum cec_opcode
   CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST     = 0x70,
   CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS      = 0x7E,
   CEC_OPCODE_SET_AUDIO_RATE                = 0x9A,
+
+  /* CEC 1.4 */
+  CEC_OPCODE_START_ARC                     = 0xC0,
+  CEC_OPCODE_REPORT_ARC_STARTED            = 0xC1,
+  CEC_OPCODE_REPORT_ARC_ENDED              = 0xC2,
+  CEC_OPCODE_REQUEST_ARC_START             = 0xC3,
+  CEC_OPCODE_REQUEST_ARC_END               = 0xC4,
+  CEC_OPCODE_END_ARC                       = 0xC5,
+  CEC_OPCODE_CDC                           = 0xF8,
   /* when this opcode is set, no opcode will be sent to the device. this is one of the reserved numbers */
   CEC_OPCODE_NONE                          = 0xFD
 } cec_opcode;
@@ -999,7 +1008,7 @@ typedef struct cec_logical_addresses
    */
   void Clear(void)
   {
-    primary = CECDEVICE_UNKNOWN;
+    primary = CECDEVICE_UNREGISTERED;
     for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
       addresses[iPtr] = 0;
   }
@@ -1009,7 +1018,7 @@ typedef struct cec_logical_addresses
    */
   bool IsEmpty(void) const
   {
-    return primary == CECDEVICE_UNKNOWN;
+    return primary == CECDEVICE_UNREGISTERED;
   }
 
   /*!
@@ -1031,7 +1040,7 @@ typedef struct cec_logical_addresses
    */
   void Set(cec_logical_address address)
   {
-    if (primary == CECDEVICE_UNKNOWN)
+    if (primary == CECDEVICE_UNREGISTERED)
       primary = address;
 
     addresses[(int) address] = 1;
@@ -1041,10 +1050,10 @@ typedef struct cec_logical_addresses
    * @brief Mark a logical address as 'unset'
    * @param address The logical address to remove from this list.
    */
-  void Unset(cec_logical_address address)
+  void Unset(const cec_logical_address address)
   {
     if (primary == address)
-      primary = CECDEVICE_UNKNOWN;
+      primary = CECDEVICE_UNREGISTERED;
 
     addresses[(int) address] = 0;
   }
@@ -1081,12 +1090,15 @@ typedef struct cec_logical_addresses
 typedef enum libcec_alert
 {
   CEC_ALERT_SERVICE_DEVICE,
-  CEC_ALERT_CONNECTION_LOST
+  CEC_ALERT_CONNECTION_LOST,
+  CEC_ALERT_PERMISSION_ERROR,
+  CEC_ALERT_PORT_BUSY
 } libcec_alert;
 
 typedef enum libcec_parameter_type
 {
-  CEC_PARAMETER_TYPE_STRING
+  CEC_PARAMETER_TYPE_STRING,
+  CEC_PARAMETER_TYPE_UNKOWN
 } libcec_parameter_type;
 
 struct libcec_parameter
@@ -1178,7 +1190,8 @@ typedef enum cec_client_version
   CEC_CLIENT_VERSION_1_5_3   = 0x1503,
   CEC_CLIENT_VERSION_1_6_0   = 0x1600,
   CEC_CLIENT_VERSION_1_6_1   = 0x1601,
-  CEC_CLIENT_VERSION_1_6_2   = 0x1602
+  CEC_CLIENT_VERSION_1_6_2   = 0x1602,
+  CEC_CLIENT_VERSION_1_6_3   = 0x1603
 } cec_client_version;
 
 typedef enum cec_server_version
@@ -1190,7 +1203,8 @@ typedef enum cec_server_version
   CEC_SERVER_VERSION_1_5_3   = 0x1503,
   CEC_SERVER_VERSION_1_6_0   = 0x1600,
   CEC_SERVER_VERSION_1_6_1   = 0x1601,
-  CEC_SERVER_VERSION_1_6_2   = 0x1602
+  CEC_SERVER_VERSION_1_6_2   = 0x1602,
+  CEC_SERVER_VERSION_1_6_3   = 0x1603
 } cec_server_version;
 
 typedef struct libcec_configuration
@@ -1225,6 +1239,7 @@ typedef struct libcec_configuration
   uint8_t               bShutdownOnStandby;   /*!< shutdown this PC when the TV is switched off. only used when bPowerOffOnStandby = 0. added in 1.6.0 */
   char                  strDeviceLanguage[3]; /*!< the menu language used by the client. 3 character ISO 639-2 country code. see http://http://www.loc.gov/standards/iso639-2/ added in 1.6.2 */
   uint32_t              iFirmwareBuildDate;   /*!< the build date of the firmware, in seconds since epoch. if not available, this value will be set to 0. added in 1.6.2 */
+  uint8_t               bMonitorOnly;         /*!< won't allocate a CCECClient when starting the connection when set (same as monitor mode). added in 1.6.3 */
 
 #ifdef __cplusplus
    libcec_configuration(void) { Clear(); }
@@ -1250,14 +1265,16 @@ typedef struct libcec_configuration
                  bPowerOffOnStandby   == other.bPowerOffOnStandby &&
                  bSendInactiveSource  == other.bSendInactiveSource &&
         /* libcec 1.5.3+ */
-        (other.serverVersion < CEC_SERVER_VERSION_1_5_3 || logicalAddresses == other.logicalAddresses) &&
+        (other.clientVersion < CEC_CLIENT_VERSION_1_5_3 || logicalAddresses == other.logicalAddresses) &&
         /* libcec 1.6.0+ */
-        (other.serverVersion < CEC_SERVER_VERSION_1_6_0 || iFirmwareVersion          == other.iFirmwareVersion) &&
-        (other.serverVersion < CEC_SERVER_VERSION_1_6_0 || bPowerOffDevicesOnStandby == other.bPowerOffDevicesOnStandby) &&
-        (other.serverVersion < CEC_SERVER_VERSION_1_6_0 || bShutdownOnStandby        == other.bShutdownOnStandby) &&
+        (other.clientVersion < CEC_CLIENT_VERSION_1_6_0 || iFirmwareVersion          == other.iFirmwareVersion) &&
+        (other.clientVersion < CEC_CLIENT_VERSION_1_6_0 || bPowerOffDevicesOnStandby == other.bPowerOffDevicesOnStandby) &&
+        (other.clientVersion < CEC_CLIENT_VERSION_1_6_0 || bShutdownOnStandby        == other.bShutdownOnStandby) &&
         /* libcec 1.6.2+ */
-        (other.serverVersion < CEC_SERVER_VERSION_1_6_2 || !strncmp(strDeviceLanguage, other.strDeviceLanguage, 3)) &&
-        (other.serverVersion < CEC_SERVER_VERSION_1_6_2 || iFirmwareBuildDate       == other.iFirmwareBuildDate));
+        (other.clientVersion < CEC_CLIENT_VERSION_1_6_2 || !strncmp(strDeviceLanguage, other.strDeviceLanguage, 3)) &&
+        (other.clientVersion < CEC_CLIENT_VERSION_1_6_2 || iFirmwareBuildDate        == other.iFirmwareBuildDate) &&
+        /* libcec 1.6.3+ */
+        (other.clientVersion < CEC_CLIENT_VERSION_1_6_3 || bMonitorOnly              == other.bMonitorOnly));
   }
 
   bool operator!=(const libcec_configuration &other) const
@@ -1288,6 +1305,7 @@ typedef struct libcec_configuration
     bPowerOffDevicesOnStandby =       CEC_DEFAULT_SETTING_POWER_OFF_DEVICES_STANDBY;
     memcpy(strDeviceLanguage,         CEC_DEFAULT_DEVICE_LANGUAGE, 3);
     iFirmwareBuildDate =              CEC_FW_BUILD_UNKNOWN;
+    bMonitorOnly =                    0;
 
     memset(strDeviceName, 0, 13);
     deviceTypes.clear();
index fd83b8b6408a1d4d0e1d7fed4dd20fbf644601be..5548b060a7a77045ce168f178ad6f3aa648a2416 100644 (file)
Binary files a/project/cec-config.rc and b/project/cec-config.rc differ
index 3ff74fe5c2457b84e5e2657ca14b0fc16d29cad0..74148fe458305d777749c7cf3353686d1ba0c9fe 100644 (file)
Binary files a/project/libcec.rc and b/project/libcec.rc differ
index aed370cb44fbe04e1eff5e0cdd524018cf7a26ca..a5beb9f418d79981a6a547bcbdc4aed91ae3f652 100644 (file)
     <ClInclude Include="..\src\lib\adapter\USBCECAdapterDetection.h" />
     <ClInclude Include="..\src\lib\adapter\USBCECAdapterMessage.h" />
     <ClInclude Include="..\src\lib\adapter\USBCECAdapterMessageQueue.h" />
+    <ClInclude Include="..\src\lib\CECClient.h" />
+    <ClInclude Include="..\src\lib\CECInputBuffer.h" />
     <ClInclude Include="..\src\lib\CECProcessor.h" />
     <ClInclude Include="..\src\lib\devices\CECAudioSystem.h" />
     <ClInclude Include="..\src\lib\devices\CECBusDevice.h" />
+    <ClInclude Include="..\src\lib\devices\CECDeviceMap.h" />
     <ClInclude Include="..\src\lib\devices\CECPlaybackDevice.h" />
     <ClInclude Include="..\src\lib\devices\CECRecordingDevice.h" />
     <ClInclude Include="..\src\lib\devices\CECTuner.h" />
     <ClCompile Include="..\src\lib\adapter\USBCECAdapterDetection.cpp" />
     <ClCompile Include="..\src\lib\adapter\USBCECAdapterMessage.cpp" />
     <ClCompile Include="..\src\lib\adapter\USBCECAdapterMessageQueue.cpp" />
+    <ClCompile Include="..\src\lib\CECClient.cpp" />
     <ClCompile Include="..\src\lib\CECProcessor.cpp" />
     <ClCompile Include="..\src\lib\devices\CECAudioSystem.cpp" />
     <ClCompile Include="..\src\lib\devices\CECBusDevice.cpp" />
+    <ClCompile Include="..\src\lib\devices\CECDeviceMap.cpp" />
     <ClCompile Include="..\src\lib\devices\CECPlaybackDevice.cpp" />
     <ClCompile Include="..\src\lib\devices\CECRecordingDevice.cpp" />
     <ClCompile Include="..\src\lib\devices\CECTuner.cpp" />
index fdd9df3bf76398d27b6b680502dc402da9d0885a..f1205a5e2bc7657be680bad501a4538a683e3522 100644 (file)
     <ClInclude Include="..\src\lib\adapter\USBCECAdapterMessageQueue.h">
       <Filter>adapter</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\lib\CECClient.h" />
+    <ClInclude Include="..\src\lib\CECInputBuffer.h" />
+    <ClInclude Include="..\src\lib\devices\CECDeviceMap.h">
+      <Filter>devices</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\src\lib\CECProcessor.cpp" />
     <ClCompile Include="..\src\lib\adapter\USBCECAdapterMessageQueue.cpp">
       <Filter>adapter</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\lib\CECClient.cpp" />
+    <ClCompile Include="..\src\lib\devices\CECDeviceMap.cpp">
+      <Filter>devices</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="libcec.rc" />
index bba03693151e5d56ee29e48f766d6bb4797229d0..a2f7026c06cc3e320a8c60d870bb6f30248776cf 100644 (file)
Binary files a/project/testclient.rc and b/project/testclient.rc differ
index a252ab27585e3e5064d00f5e3aea188e34ecdcd5..e66603c574716680f5525b2c8551fa2f590bd560 100644 (file)
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers 
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.6.2.0")]
-[assembly: AssemblyFileVersion("1.6.2.0")]
+[assembly: AssemblyVersion("1.6.3.0")]
+[assembly: AssemblyFileVersion("1.6.3.0")]
index 6296abd61747401f517ee99bd6de7497883b69fd..6c7d86d403ea3bc5da6460b04208f1a062c42c78 100644 (file)
@@ -13,7 +13,7 @@ using namespace System::Security::Permissions;
 [assembly:AssemblyTrademarkAttribute("")];
 [assembly:AssemblyCultureAttribute("")];
 
-[assembly:AssemblyVersionAttribute("1.6.2.0")];
+[assembly:AssemblyVersionAttribute("1.6.3.0")];
 
 [assembly:ComVisible(false)];
 [assembly:CLSCompliantAttribute(true)];
index 14630197f51a561227d005175519552b23ea502f..43aaff935ca0b75e8bfe0b8cc78523820cdf3831 100644 (file)
@@ -355,7 +355,8 @@ namespace CecSharp
                Version1_5_3  = 0x1503,
                Version1_6_0  = 0x1600,
                Version1_6_1  = 0x1601,
-    Version1_6_2  = 0x1602
+    Version1_6_2  = 0x1602,
+    Version1_6_3  = 0x1603
        };
 
        public enum class CecServerVersion
@@ -367,7 +368,8 @@ namespace CecSharp
                Version1_5_3  = 0x1503,
                Version1_6_0  = 0x1600,
                Version1_6_1  = 0x1601,
-    Version1_6_2  = 0x1602
+    Version1_6_2  = 0x1602,
+    Version1_6_3  = 0x1603
        };
 
        public ref class CecAdapter
@@ -649,6 +651,9 @@ namespace CecSharp
 
                        if (ServerVersion >= CecServerVersion::Version1_6_2)
                                DeviceLanguage = gcnew System::String(config.strDeviceLanguage);
+
+                       if (ServerVersion >= CecServerVersion::Version1_6_3)
+                         MonitorOnlyClient = config.bMonitorOnly == 1;
                }
 
                property System::String ^     DeviceName;
@@ -674,6 +679,7 @@ namespace CecSharp
                property uint16_t             FirmwareVersion;
                property bool                 PowerOffDevicesOnStandby;
                property bool                 ShutdownOnStandby;
+               property bool                 MonitorOnlyClient;
                property System::String ^     DeviceLanguage;
                property CecCallbackMethods ^ Callbacks;
        };
index 680ccfba6f2ffa51b549e145c202c1c78cb3a97f..97329b3e9cd773e8dd531528c3c815dc24588261 100644 (file)
@@ -91,7 +91,8 @@ namespace CecSharp
                {
                        config.Clear();
 
-                       _snprintf_s(config.strDeviceName, 13, context->marshal_as<const char*>(netConfig->DeviceName));
+                       const char *strDeviceName = context->marshal_as<const char*>(netConfig->DeviceName);
+                       memcpy_s(config.strDeviceName, 13, strDeviceName, 13);
                        for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
                                config.deviceTypes.types[iPtr] = (cec_device_type)netConfig->DeviceTypes->Types[iPtr];
 
@@ -128,7 +129,15 @@ namespace CecSharp
                        }
 
                        if (netConfig->ServerVersion >= CecServerVersion::Version1_6_2)
-                               _snprintf_s(config.strDeviceLanguage, 3, context->marshal_as<const char*>(netConfig->DeviceLanguage));
+                       {
+                               const char *strDeviceLanguage = context->marshal_as<const char*>(netConfig->DeviceLanguage);
+                               memcpy_s(config.strDeviceLanguage, 3, strDeviceLanguage, 3);
+                       }
+
+                       if (netConfig->ServerVersion >= CecServerVersion::Version1_6_3)
+                       {
+                         config.bMonitorOnly              = netConfig->MonitorOnlyClient ? 1 : 0;
+                       }
 
                        config.callbacks            = &g_cecCallbacks;
                }
index b669eb14ff97086f3441b27c1129146d7a3aeed8..8cc88a95658dbc2af5ceb2dcd368bae5d624eb3c 100644 (file)
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers 
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.6.2.0")]
-[assembly: AssemblyFileVersion("1.6.2.0")]
+[assembly: AssemblyVersion("1.6.3.0")]
+[assembly: AssemblyFileVersion("1.6.3.0")]
index c601afc3f9f573683e2dc2648ebac02dd55b998c..57931ee81dfb4dcbe59e54bdb8e23cfcc89a8768 100644 (file)
@@ -59,7 +59,6 @@ cec_user_control_code g_lastKey = CEC_USER_CONTROL_CODE_UNKNOWN;
 ICECCallbacks         g_callbacks;
 libcec_configuration  g_config;
 ICECAdapter *         g_parser;
-cec_logical_address   g_primaryAddress;
 
 inline void PrintToStdOut(const char *strFormat, ...)
 {
@@ -139,32 +138,6 @@ int CecCommand(void *UNUSED(cbParam), const cec_command &command)
   return 0;
 }
 
-void EnableCallbacks(ICECAdapter *adapter)
-{
-  g_callbacks.CBCecLogMessage = &CecLogMessage;
-  g_callbacks.CBCecKeyPress   = &CecKeyPress;
-  g_callbacks.CBCecCommand    = &CecCommand;
-  adapter->EnableCallbacks(NULL, &g_callbacks);
-}
-
-ICECAdapter *CreateParser(cec_device_type_list typeList)
-{
-  ICECAdapter *parser = LibCecInit("ButtonConfig", typeList);
-  if (!parser)
-  {
-  #ifdef __WINDOWS__
-    PrintToStdOut("Cannot load libcec.dll");
-  #else
-    PrintToStdOut("Cannot load libcec.so");
-  #endif
-    return NULL;
-  }
-
-  PrintToStdOut("CEC Parser created - libcec version %d.%d", parser->GetLibVersionMajor(), parser->GetLibVersionMinor());
-
-  return parser;
-}
-
 bool ProcessConsoleCommand(string &input)
 {
   if (!input.empty())
@@ -184,7 +157,7 @@ bool OpenConnection(cec_device_type type = CEC_DEVICE_TYPE_RECORDING_DEVICE)
   g_config.Clear();
   snprintf(g_config.strDeviceName, 13, "CEC-config");
   g_config.callbackParam      = NULL;
-  g_config.clientVersion      = (uint32_t)CEC_CLIENT_VERSION_1_5_0;
+  g_config.clientVersion      = (uint32_t)CEC_CLIENT_VERSION_1_6_2;
   g_callbacks.CBCecLogMessage = &CecLogMessage;
   g_callbacks.CBCecKeyPress   = &CecKeyPress;
   g_callbacks.CBCecCommand    = &CecCommand;
@@ -218,10 +191,9 @@ bool OpenConnection(cec_device_type type = CEC_DEVICE_TYPE_RECORDING_DEVICE)
     return false;
   }
 
-  cec_logical_addresses addr = g_parser->GetLogicalAddresses();
-  g_primaryAddress = addr.primary;
+  g_parser->GetCurrentConfiguration(&g_config);
+  PrintToStdOut("CEC Parser created - libCEC version %s", g_parser->ToString((cec_server_version)g_config.serverVersion));
 
-  PrintToStdOut("cec device opened. using logical address %X", g_primaryAddress);
   return true;
 }
 
@@ -240,7 +212,7 @@ int8_t FindPhysicalAddressPortNumber(void)
 
 cec_logical_address FindPhysicalAddressBaseDevice(void)
 {
-  PrintToStdOut("Press 1 if your CEC adapter is connected to your TV or\npress 2 if it's connected to an AVR, followed by <enter>. Anything else will cancel this wizard.");
+  PrintToStdOut("Press 1 if your CEC adapter is connected to your TV or\nPress 2 if it's connected to an AVR, followed by <enter>. Anything else will cancel this wizard.");
 
   string input;
   getline(cin, input);
@@ -279,7 +251,8 @@ uint16_t FindPhysicalAddress(void)
       PrintToStdOut("Failed. Please enter the address manually, or restart this wizard and use different settings.");
     else
     {
-      iAddress = g_parser->GetDevicePhysicalAddress(g_primaryAddress);
+      g_parser->GetCurrentConfiguration(&g_config);
+      iAddress = g_parser->GetDevicePhysicalAddress(g_config.logicalAddresses.primary);
       if (iAddress == 0 || iAddress == CEC_INVALID_PHYSICAL_ADDRESS)
         PrintToStdOut("Failed. Please enter the address manually, or restart this wizard and use different settings.");
     }
@@ -389,13 +362,22 @@ int main (int UNUSED(argc), char *UNUSED(argv[]))
     g_config.bActivateSource = (input == "y" || input == "Y") ? 1 : 0;
   }
 
+  {
+    PrintToStdOut("Do you want to power on the TV when starting the application (y/n)?");
+    string input;
+    getline(cin, input);
+    cin.clear();
+    if (input == "y" || input == "Y")
+      g_config.wakeDevices.Set(CECDEVICE_TV);
+  }
+
   {
     PrintToStdOut("Do you want to power off CEC devices when closing the application (y/n)?");
     string input;
     getline(cin, input);
     cin.clear();
     if (input == "y" || input == "Y")
-      g_config.powerOffDevices.Set(CECDEVICE_BROADCAST);
+      g_config.powerOffDevices.Set(CECDEVICE_TV);
   }
 
   {
@@ -428,44 +410,32 @@ int main (int UNUSED(argc), char *UNUSED(argv[]))
   PrintToStdOut("Physical address:                                        %4X", g_config.iPhysicalAddress);
   PrintToStdOut("Use the TV's language setting:                           %s", g_config.bUseTVMenuLanguage ? "yes" : "no");
   PrintToStdOut("Make the adapter the active source when starting XBMC:   %s", g_config.bActivateSource ? "yes" : "no");
+  PrintToStdOut("Power on the TV when starting XBMC:                      %s", g_config.wakeDevices.IsSet(CECDEVICE_BROADCAST) ? "yes" : "no");
   PrintToStdOut("Power off devices when stopping XBMC:                    %s", g_config.powerOffDevices.IsSet(CECDEVICE_BROADCAST) ? "yes" : "no");
   PrintToStdOut("Put devices in standby mode when activating screensaver: %s", g_config.bPowerOffScreensaver ? "yes" : "no");
   PrintToStdOut("Put this PC in standby mode when the TV is switched off: %s", g_config.bPowerOffOnStandby ? "yes" : "no");
-  PrintToStdOut("Seend an inactive source message when stopping XBMC:     %s\n\n", g_config.bSendInactiveSource ? "yes" : "no");
+  PrintToStdOut("Send an inactive source message when stopping XBMC:      %s\n\n", g_config.bSendInactiveSource ? "yes" : "no");
 
-  if (g_parser->CanPersistConfiguration())
-  {
-    PrintToStdOut("Do you want to store these settings in the adapter (y/n)?");
-    string input;
-    getline(cin, input);
-    cin.clear();
-    if (input == "y" || input == "Y")
-    {
-      PrintToStdOut("Storing settings ...");
-      if (g_parser->PersistConfiguration(&g_config))
-        PrintToStdOut("Settings stored.");
-      else
-        PrintToStdOut("The settings could not be stored");
-    }
-  }
+  PrintToStdOut("Storing settings ...");
+  if (g_parser->PersistConfiguration(&g_config))
+    PrintToStdOut("Settings stored.");
   else
-  {
-    PrintToStdOut("This adapter doesn't support settings persistence. Please set up these settings in your media player application.");
+    PrintToStdOut("The settings could not be stored");
 
-    ofstream configOutput;
-    configOutput.open("usb_2548_1001.xml");
-    if (configOutput.is_open())
-    {
-      CStdString strWakeDevices;
-      for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
-        if (g_config.wakeDevices[iPtr])
-          strWakeDevices.AppendFormat(" %d" + iPtr);
-      CStdString strStandbyDevices;
-      for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
-        if (g_config.powerOffDevices[iPtr])
-          strStandbyDevices.AppendFormat(" %d" + iPtr);
-
-      configOutput <<
+  ofstream configOutput;
+  configOutput.open("usb_2548_1001.xml");
+  if (configOutput.is_open())
+  {
+    CStdString strWakeDevices;
+    for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
+      if (g_config.wakeDevices[iPtr])
+        strWakeDevices.AppendFormat(" %d" + iPtr);
+    CStdString strStandbyDevices;
+    for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
+      if (g_config.powerOffDevices[iPtr])
+        strStandbyDevices.AppendFormat(" %d" + iPtr);
+
+    configOutput <<
         "<settings>\n" <<
           "\t<setting id=\"enabled\" value=\"1\" />\n" <<
           "\t<setting id=\"activate_source\" value=\"" << (int)g_config.bActivateSource << "\" />\n" <<
@@ -480,10 +450,9 @@ int main (int UNUSED(argc), char *UNUSED(argv[]))
           "\t<setting id=\"port\" value=\"\" />\n" <<
           "\t<setting id=\"send_inactive_source\" value=\"" << (int)g_config.bSendInactiveSource << "\" />\n" <<
         "</settings>";
-      configOutput.close();
+    configOutput.close();
 
-      PrintToStdOut("The configuration has been stored in 'usb_2548_1001.xml'. Copy this file to ~/.userdata/peripheral_data to use it in XBMC");
-    }
+    PrintToStdOut("The configuration has been stored in 'usb_2548_1001.xml'. Copy this file to ~/.userdata/peripheral_data to use it in XBMC");
   }
 
   g_parser->StandbyDevices();
diff --git a/src/lib/CECClient.cpp b/src/lib/CECClient.cpp
new file mode 100644 (file)
index 0000000..24f196f
--- /dev/null
@@ -0,0 +1,1292 @@
+/*
+ * 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 an original work, containing original code.
+ *
+ * libCEC(R) is a trademark of Pulse-Eight Limited.
+ *
+ * This program is dual-licensed; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Alternatively, you can license this library under a commercial license,
+ * please contact Pulse-Eight Licensing for more information.
+ *
+ * For more information contact:
+ * Pulse-Eight Licensing       <license@pulse-eight.com>
+ *     http://www.pulse-eight.com/
+ *     http://www.pulse-eight.net/
+ */
+
+#include "CECClient.h"
+#include "CECProcessor.h"
+#include "LibCEC.h"
+#include "devices/CECPlaybackDevice.h"
+#include "devices/CECAudioSystem.h"
+#include "devices/CECTV.h"
+
+using namespace CEC;
+using namespace PLATFORM;
+
+#define LIB_CEC     m_processor->GetLib()
+#define ToString(x) LIB_CEC->ToString(x)
+
+CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration &configuration) :
+    m_processor(processor),
+    m_bInitialised(false),
+    m_bRegistered(false),
+    m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
+    m_buttontime(0)
+{
+  // set the initial configuration
+  SetConfiguration(configuration);
+}
+
+CCECClient::~CCECClient(void)
+{
+  // unregister the client
+  if (m_processor && IsRegistered())
+    m_processor->UnregisterClient(this);
+}
+
+bool CCECClient::IsInitialised(void)
+{
+  CLockObject lock(m_mutex);
+  return m_bInitialised && m_processor;
+}
+
+void CCECClient::SetInitialised(bool bSetTo)
+{
+  CLockObject lock(m_mutex);
+  m_bInitialised = bSetTo;
+}
+
+bool CCECClient::IsRegistered(void)
+{
+  CLockObject lock(m_mutex);
+  return m_bRegistered && m_processor;
+}
+
+void CCECClient::SetRegistered(bool bSetTo)
+{
+  CLockObject lock(m_mutex);
+  m_bRegistered = bSetTo;
+}
+
+bool CCECClient::OnRegister(void)
+{
+  // return false if already initialised
+  if (IsInitialised())
+    return true;
+
+  // get all device we control
+  CECDEVICEVEC devices;
+  m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+
+  // return false when no devices were found
+  if (devices.empty())
+  {
+    LIB_CEC->AddLog(CEC_LOG_WARNING, "cannot find the primary device (logical address %x)", GetPrimaryLogicalAdddress());
+    return false;
+  }
+
+  // mark as initialised
+  SetInitialised(true);
+
+  // configure all devices
+  for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
+  {
+    // only set our OSD name for the primary device
+    if ((*it)->GetLogicalAddress() == GetPrimaryLogicalAdddress())
+      (*it)->SetOSDName(m_configuration.strDeviceName);
+
+    // set the default menu language for devices we control
+    (*it)->SetMenuLanguage(m_configuration.strDeviceLanguage);
+  }
+
+  // set the physical address
+  SetPhysicalAddress(m_configuration);
+
+  // ensure that we know the vendor id of the TV, so we are using the correct handler
+  m_processor->GetTV()->GetVendorId(GetPrimaryLogicalAdddress());
+
+  // make the primary device the active source if the option is set
+  if (m_configuration.bActivateSource == 1)
+    GetPrimaryDevice()->ActivateSource();
+
+  return true;
+}
+
+bool CCECClient::SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_t iPort, bool bForce /* = false */)
+{
+  bool bReturn(false);
+
+  // limit the HDMI port range to 1-15
+  if (iPort < CEC_MIN_HDMI_PORTNUMBER ||
+      iPort > CEC_MAX_HDMI_PORTNUMBER)
+    return bReturn;
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice);
+
+  // update the configuration
+  {
+    CLockObject lock(m_mutex);
+    m_configuration.baseDevice = iBaseDevice;
+    m_configuration.iHDMIPort  = iPort;
+  }
+
+  // don't continue if the connection isn't opened
+  if (!m_processor->CECInitialised() && !bForce)
+    return true;
+
+  // get the PA of the base device
+  uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
+  CCECBusDevice *baseDevice = m_processor->GetDevice(iBaseDevice);
+  if (baseDevice)
+    iPhysicalAddress = baseDevice->GetPhysicalAddress(GetPrimaryLogicalAdddress());
+
+  // add our port number
+  if (iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
+  {
+    if (iPhysicalAddress == 0)
+      iPhysicalAddress += 0x1000 * iPort;
+    else if (iPhysicalAddress % 0x1000 == 0)
+      iPhysicalAddress += 0x100 * iPort;
+    else if (iPhysicalAddress % 0x100 == 0)
+      iPhysicalAddress += 0x10 * iPort;
+    else if (iPhysicalAddress % 0x10 == 0)
+      iPhysicalAddress += iPort;
+
+    bReturn = true;
+  }
+
+  // set the default address when something went wrong
+  if (!bReturn)
+  {
+    LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the default value %04X", iPhysicalAddress, CEC_DEFAULT_PHYSICAL_ADDRESS);
+    iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
+  }
+
+  // and set the address
+  SetDevicePhysicalAddress(iPhysicalAddress);
+
+  ConfigurationChanged(m_configuration);
+
+  return bReturn;
+}
+
+void CCECClient::ResetPhysicalAddress(void)
+{
+  SetPhysicalAddress(m_configuration);
+}
+
+void CCECClient::SetPhysicalAddress(const libcec_configuration &configuration)
+{
+  // try to autodetect the address
+  bool bPASet(false);
+  if (m_processor->CECInitialised() && configuration.bAutodetectAddress == 1)
+    bPASet = AutodetectPhysicalAddress();
+
+  // try to use physical address setting
+  if (!bPASet && CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress))
+    bPASet = SetPhysicalAddress(configuration.iPhysicalAddress);
+
+  // use the base device + hdmi port settings
+  if (!bPASet)
+    bPASet = SetHDMIPort(configuration.baseDevice, configuration.iHDMIPort);
+
+  // reset to defaults if something went wrong
+  if (!bPASet)
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
+    m_configuration.baseDevice = CECDEVICE_UNKNOWN;
+    m_configuration.iHDMIPort  = CEC_HDMI_PORTNUMBER_NONE;
+  }
+}
+
+bool CCECClient::SetPhysicalAddress(const uint16_t iPhysicalAddress)
+{
+  // update the configuration
+  {
+    CLockObject lock(m_mutex);
+    if (m_configuration.iPhysicalAddress == iPhysicalAddress)
+    {
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "physical address unchanged (%04X)", iPhysicalAddress);
+      return true;
+    }
+    else
+    {
+      m_configuration.iPhysicalAddress = iPhysicalAddress;
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting physical address to '%04X'", iPhysicalAddress);
+    }
+  }
+
+  // persist the new configuration
+  m_processor->PersistConfiguration(m_configuration);
+
+  // set the physical address for each device
+  SetDevicePhysicalAddress(iPhysicalAddress);
+
+  // and send back the updated configuration
+  ConfigurationChanged(m_configuration);
+
+  return true;
+}
+
+bool CCECClient::AllocateLogicalAddresses(void)
+{
+  // reset all previous LAs that were set
+  m_configuration.logicalAddresses.Clear();
+
+  // display an error if no device types are set
+  if (m_configuration.deviceTypes.IsEmpty())
+  {
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "no device types given");
+    return false;
+  }
+
+  // check each entry of the list
+  for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
+  {
+    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
+      continue;
+
+    // find an LA for this type
+    cec_logical_address address(CECDEVICE_UNKNOWN);
+    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE)
+      address = AllocateLogicalAddressRecordingDevice();
+    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER)
+      address = AllocateLogicalAddressTuner();
+    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
+      address = AllocateLogicalAddressPlaybackDevice();
+    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
+      address = AllocateLogicalAddressAudioSystem();
+
+    // display an error if no LA could be allocated
+    if (address == CECDEVICE_UNKNOWN)
+    {
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - failed to allocate device '%d', type '%s'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]));
+      return false;
+    }
+
+    // display the registered LA
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - device '%d', type '%s', LA '%X'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]), address);
+    m_configuration.logicalAddresses.Set(address);
+  }
+
+  return true;
+}
+
+cec_logical_address CCECClient::AllocateLogicalAddressRecordingDevice(void)
+{
+  cec_logical_address retVal(CECDEVICE_UNKNOWN);
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'");
+  if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1))
+    retVal = CECDEVICE_RECORDINGDEVICE1;
+  else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2))
+    retVal = CECDEVICE_RECORDINGDEVICE2;
+  else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3))
+    retVal = CECDEVICE_RECORDINGDEVICE3;
+
+  return retVal;
+}
+
+cec_logical_address CCECClient::AllocateLogicalAddressTuner(void)
+{
+  cec_logical_address retVal(CECDEVICE_UNKNOWN);
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'");
+  if (m_processor->TryLogicalAddress(CECDEVICE_TUNER1))
+    retVal = CECDEVICE_TUNER1;
+  else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER2))
+    retVal = CECDEVICE_TUNER2;
+  else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER3))
+    retVal = CECDEVICE_TUNER3;
+  else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER4))
+    retVal = CECDEVICE_TUNER4;
+
+  return retVal;
+}
+
+cec_logical_address CCECClient::AllocateLogicalAddressPlaybackDevice(void)
+{
+  cec_logical_address retVal(CECDEVICE_UNKNOWN);
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'");
+  if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1))
+    retVal = CECDEVICE_PLAYBACKDEVICE1;
+  else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2))
+    retVal = CECDEVICE_PLAYBACKDEVICE2;
+  else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3))
+    retVal = CECDEVICE_PLAYBACKDEVICE3;
+
+  return retVal;
+}
+
+cec_logical_address CCECClient::AllocateLogicalAddressAudioSystem(void)
+{
+  cec_logical_address retVal(CECDEVICE_UNKNOWN);
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audiosystem'");
+  if (m_processor->TryLogicalAddress(CECDEVICE_AUDIOSYSTEM))
+    retVal = CECDEVICE_AUDIOSYSTEM;
+
+  return retVal;
+}
+
+CCECBusDevice *CCECClient::GetDeviceByType(const cec_device_type type) const
+{
+  // get all devices that match our logical addresses
+  CECDEVICEVEC devices;
+  m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+
+  // filter the type we need
+  CCECDeviceMap::FilterType(type, devices);
+
+  return devices.empty() ?
+      NULL :
+      *devices.begin();
+}
+
+bool CCECClient::ChangeDeviceType(const cec_device_type from, const cec_device_type to)
+{
+  LIB_CEC->AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to));
+
+  CLockObject lock(m_mutex);
+
+  // get the previous device that was allocated
+  CCECBusDevice *previousDevice = GetDeviceByType(from);
+  if (!previousDevice)
+    return false;
+
+  // change the type in the device type list
+  bool bChanged(false);
+  for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
+  {
+    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
+      continue;
+
+    if (m_configuration.deviceTypes.types[iPtr] == from)
+    {
+      bChanged = true;
+      m_configuration.deviceTypes.types[iPtr] = to;
+    }
+    else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged)
+    {
+      // ensure that dupes are removed
+      m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED;
+    }
+  }
+
+  // re-register the client to set the new ackmask
+  if (!m_processor->RegisterClient(this))
+    return false;
+
+  return true;
+}
+
+bool CCECClient::SetLogicalAddress(const cec_logical_address iLogicalAddress)
+{
+  CLockObject lock(m_mutex);
+  if (GetPrimaryLogicalAdddress() != iLogicalAddress)
+  {
+    {
+      CLockObject lock(m_mutex);
+      LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress);
+      m_configuration.logicalAddresses.primary = iLogicalAddress;
+      m_configuration.logicalAddresses.Set(iLogicalAddress);
+    }
+    return m_processor->RegisterClient(this);
+  }
+
+  return true;
+}
+
+bool CCECClient::Transmit(const cec_command &data)
+{
+  return m_processor ? m_processor->Transmit(data) : false;
+}
+
+bool CCECClient::SendPowerOnDevices(const cec_logical_address address /* = CECDEVICE_TV */)
+{
+  // if the broadcast address if set as destination and the client version >=1.5.0, read the wakeDevices setting
+  if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
+  {
+    CECDEVICEVEC devices;
+    m_processor->GetDevices()->GetWakeDevices(m_configuration, devices);
+    return m_processor->PowerOnDevices(GetPrimaryLogicalAdddress(), devices);
+  }
+
+  return m_processor->PowerOnDevice(GetPrimaryLogicalAdddress(), address);
+}
+
+bool CCECClient::SendStandbyDevices(const cec_logical_address address /* = CECDEVICE_BROADCAST */)
+{
+  // if the broadcast address if set as destination and the client version >=1.5.0, read the standbyDevices setting
+  if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
+  {
+    CECDEVICEVEC devices;
+    m_processor->GetDevices()->GetPowerOffDevices(m_configuration, devices);
+    return m_processor->StandbyDevices(GetPrimaryLogicalAdddress(), devices);
+  }
+
+  return m_processor->StandbyDevice(GetPrimaryLogicalAdddress(), address);
+}
+
+bool CCECClient::SendSetActiveSource(const cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
+{
+  // get the devices that are controlled by us
+  CECDEVICEVEC devices;
+  m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+
+  // filter out the device that matches the given type
+  if (type != CEC_DEVICE_TYPE_RESERVED)
+    CCECDeviceMap::FilterType(type, devices);
+
+  // no devices left, re-fetch the list of devices that are controlled by us
+  if (devices.empty())
+    m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+
+  if (!devices.empty())
+  {
+    // get the first device from the list
+    CCECBusDevice *device = *devices.begin();
+
+    // and activate it
+    if (!m_processor->CECInitialised())
+      device->MarkAsActiveSource();
+    else if (device->HasValidPhysicalAddress())
+      return device->ActivateSource();
+  }
+
+  return false;
+}
+
+CCECPlaybackDevice *CCECClient::GetPlaybackDevice(void)
+{
+  CCECPlaybackDevice *device(NULL);
+  CECDEVICEVEC devices;
+
+  // get the playback devices
+  m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+  CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE, devices);
+
+  // no matches, get the recording devices
+  if (devices.empty())
+  {
+    m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+    CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_RECORDING_DEVICE, devices);
+  }
+
+  // get the first device that matches, and cast it to CCECPlaybackDevice
+  if (!devices.empty())
+    device = (*devices.begin())->AsPlaybackDevice();
+
+  return device;
+}
+
+cec_logical_address CCECClient::GetPrimaryLogicalAdddress(void)
+{
+  CLockObject lock(m_mutex);
+  return m_configuration.logicalAddresses.primary;
+}
+
+CCECBusDevice *CCECClient::GetPrimaryDevice(void)
+{
+  return m_processor->GetDevice(GetPrimaryLogicalAdddress());
+}
+
+bool CCECClient::SendSetDeckControlMode(const cec_deck_control_mode mode, bool bSendUpdate /* = true */)
+{
+  // find a playback device that we control
+  CCECPlaybackDevice *device = GetPlaybackDevice();
+  if (device)
+  {
+    // and set the deck control mode if there is a match
+    device->SetDeckControlMode(mode);
+    if (bSendUpdate)
+      return device->TransmitDeckStatus(CECDEVICE_TV);
+    return true;
+  }
+
+  // no match
+  return false;
+}
+
+bool CCECClient::SendSetDeckInfo(const cec_deck_info info, bool bSendUpdate /* = true */)
+{
+  // find a playback device that we control
+  CCECPlaybackDevice *device = GetPlaybackDevice();
+  if (device)
+  {
+    // and set the deck status if there is a match
+    device->SetDeckStatus(info);
+    if (bSendUpdate)
+      return device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV);
+    return true;
+  }
+
+  // no match
+  return false;
+}
+
+bool CCECClient::SendSetMenuState(const cec_menu_state state, bool bSendUpdate /* = true */)
+{
+  CECDEVICEVEC devices;
+
+  // set the menu state for all devices that are controlled by us
+  m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+  for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
+  {
+    (*it)->SetMenuState(state);
+    if (bSendUpdate)
+      (*it)->TransmitMenuState(CECDEVICE_TV);
+  }
+
+  return true;
+}
+
+bool CCECClient::SendSetInactiveView(void)
+{
+  CECDEVICEVEC devices;
+
+  // mark all devices that are controlled by us as inactive source
+  m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+  for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
+  {
+    if ((*it)->IsActiveSource())
+    {
+      (*it)->MarkAsInactiveSource();
+      return (*it)->TransmitInactiveSource();
+    }
+  }
+
+  return true;
+}
+
+bool CCECClient::SendSetOSDString(const cec_logical_address iLogicalAddress, const cec_display_control duration, const char *strMessage)
+{
+  CCECBusDevice *primary = GetPrimaryDevice();
+  if (primary)
+    return primary->TransmitOSDString(iLogicalAddress, duration, strMessage);
+
+  return false;
+}
+
+cec_version CCECClient::GetDeviceCecVersion(const cec_logical_address iAddress)
+{
+  CCECBusDevice *device = m_processor->GetDevice(iAddress);
+  if (device)
+    return device->GetCecVersion(GetPrimaryLogicalAdddress());
+  return CEC_VERSION_UNKNOWN;
+}
+
+bool CCECClient::GetDeviceMenuLanguage(const cec_logical_address iAddress, cec_menu_language &language)
+{
+  CCECBusDevice *device = m_processor->GetDevice(iAddress);
+  if (device)
+  {
+    language = device->GetMenuLanguage(GetPrimaryLogicalAdddress());
+    return (strcmp(language.language, "???") != 0);
+  }
+  return false;
+}
+
+cec_osd_name CCECClient::GetDeviceOSDName(const cec_logical_address iAddress)
+{
+  cec_osd_name retVal;
+  retVal.device = iAddress;
+  retVal.name[0] = 0;
+
+  CCECBusDevice *device = m_processor->GetDevice(iAddress);
+  if (device)
+  {
+    CStdString strOSDName = device->GetOSDName(GetPrimaryLogicalAdddress());
+    snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str());
+    retVal.device = iAddress;
+  }
+
+  return retVal;
+}
+
+uint16_t CCECClient::GetDevicePhysicalAddress(const cec_logical_address iAddress)
+{
+  CCECBusDevice *device = m_processor->GetDevice(iAddress);
+  if (device)
+    return device->GetPhysicalAddress(GetPrimaryLogicalAdddress());
+  return CEC_INVALID_PHYSICAL_ADDRESS;
+}
+
+cec_power_status CCECClient::GetDevicePowerStatus(const cec_logical_address iAddress)
+{
+  CCECBusDevice *device = m_processor->GetDevice(iAddress);
+  if (device)
+    return device->GetPowerStatus(GetPrimaryLogicalAdddress());
+  return CEC_POWER_STATUS_UNKNOWN;
+}
+
+uint64_t CCECClient::GetDeviceVendorId(const cec_logical_address iAddress)
+{
+  CCECBusDevice *device = m_processor->GetDevice(iAddress);
+  if (device)
+    return device->GetVendorId(GetPrimaryLogicalAdddress());
+  return CEC_VENDOR_UNKNOWN;
+}
+
+uint8_t CCECClient::SendVolumeUp(bool bSendRelease /* = true */)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECAudioSystem *audio = m_processor->GetAudioSystem();
+
+  return device && audio && audio->IsPresent() ?
+      audio->VolumeUp(device->GetLogicalAddress(), bSendRelease) :
+      (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
+}
+
+uint8_t CCECClient::SendVolumeDown(bool bSendRelease /* = true */)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECAudioSystem *audio = m_processor->GetAudioSystem();
+
+  return device && audio && audio->IsPresent() ?
+      audio->VolumeDown(device->GetLogicalAddress(), bSendRelease) :
+      (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
+}
+
+uint8_t CCECClient::SendMuteAudio(void)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECAudioSystem *audio = m_processor->GetAudioSystem();
+
+  return device && audio && audio->IsPresent() ?
+      audio->MuteAudio(device->GetLogicalAddress()) :
+      (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
+}
+
+bool CCECClient::SendKeypress(const cec_logical_address iDestination, const cec_user_control_code key, bool bWait /* = true */)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECBusDevice *dest = m_processor->GetDevice(iDestination);
+
+  return device && dest ?
+      device->TransmitKeypress(GetPrimaryLogicalAdddress(), key, bWait) :
+      false;
+}
+
+bool CCECClient::SendKeyRelease(const cec_logical_address iDestination, bool bWait /* = true */)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECBusDevice *dest = m_processor->GetDevice(iDestination);
+
+  return device && dest ?
+      device->TransmitKeyRelease(GetPrimaryLogicalAdddress(), bWait) :
+      false;
+}
+
+bool CCECClient::GetCurrentConfiguration(libcec_configuration &configuration)
+{
+  CLockObject lock(m_mutex);
+
+  // client version 1.5.0
+  snprintf(configuration.strDeviceName, 13, "%s", m_configuration.strDeviceName);
+  configuration.deviceTypes          = m_configuration.deviceTypes;
+  configuration.bAutodetectAddress   = m_configuration.bAutodetectAddress;
+  configuration.iPhysicalAddress     = m_configuration.iPhysicalAddress;
+  configuration.baseDevice           = m_configuration.baseDevice;
+  configuration.iHDMIPort            = m_configuration.iHDMIPort;
+  configuration.clientVersion        = m_configuration.clientVersion;
+  configuration.serverVersion        = m_configuration.serverVersion;
+  configuration.tvVendor             = m_configuration.tvVendor;
+
+  configuration.bGetSettingsFromROM  = m_configuration.bGetSettingsFromROM;
+  configuration.bUseTVMenuLanguage   = m_configuration.bUseTVMenuLanguage;
+  configuration.bActivateSource      = m_configuration.bActivateSource;
+  configuration.wakeDevices          = m_configuration.wakeDevices;
+  configuration.powerOffDevices      = m_configuration.powerOffDevices;
+  configuration.bPowerOffScreensaver = m_configuration.bPowerOffScreensaver;
+  configuration.bPowerOffOnStandby   = m_configuration.bPowerOffOnStandby;
+
+  // client version 1.5.1
+  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1)
+    configuration.bSendInactiveSource = m_configuration.bSendInactiveSource;
+
+  // client version 1.5.3
+  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_3)
+    configuration.logicalAddresses    = m_configuration.logicalAddresses;
+
+  // client version 1.6.0
+  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0)
+  {
+    configuration.iFirmwareVersion          = m_configuration.iFirmwareVersion;
+    configuration.bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby;
+    configuration.bShutdownOnStandby        = m_configuration.bShutdownOnStandby;
+  }
+
+  // client version 1.6.2
+  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2)
+  {
+    memcpy(configuration.strDeviceLanguage, m_configuration.strDeviceLanguage, 3);
+    configuration.iFirmwareBuildDate      = m_configuration.iFirmwareBuildDate;
+  }
+
+  // client version 1.6.3
+  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3)
+  {
+    configuration.bMonitorOnly            = m_configuration.bMonitorOnly;
+  }
+
+  return true;
+}
+
+bool CCECClient::SetConfiguration(const libcec_configuration &configuration)
+{
+  bool bIsRunning(m_processor && m_processor->CECInitialised());
+  CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL;
+  uint16_t iPA = primary ? primary->GetCurrentPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
+
+  // update the callbacks
+  if (configuration.callbacks)
+    EnableCallbacks(configuration.callbackParam, configuration.callbacks);
+
+  // update the client version
+  SetClientVersion((cec_client_version)configuration.clientVersion);
+
+  // update the OSD name
+  CStdString strOSDName(configuration.strDeviceName);
+  SetOSDName(strOSDName);
+
+  // update the TV vendor override
+  SetTVVendorOverride((cec_vendor_id)configuration.tvVendor);
+
+  // just copy these
+  {
+    CLockObject lock(m_mutex);
+    m_configuration.bUseTVMenuLanguage   = configuration.bUseTVMenuLanguage;
+    m_configuration.bActivateSource      = configuration.bActivateSource;
+    m_configuration.bGetSettingsFromROM  = configuration.bGetSettingsFromROM;
+    m_configuration.wakeDevices          = configuration.wakeDevices;
+    m_configuration.powerOffDevices      = configuration.powerOffDevices;
+    m_configuration.bPowerOffScreensaver = configuration.bPowerOffScreensaver;
+    m_configuration.bPowerOffOnStandby   = configuration.bPowerOffOnStandby;
+
+    // client version 1.5.1
+    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1)
+      m_configuration.bSendInactiveSource = configuration.bSendInactiveSource;
+
+    // client version 1.6.0
+    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0)
+    {
+      m_configuration.bPowerOffDevicesOnStandby = configuration.bPowerOffDevicesOnStandby;
+      m_configuration.bShutdownOnStandby        = configuration.bShutdownOnStandby;
+    }
+
+    // client version 1.6.2
+    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2)
+    {
+      memcpy(m_configuration.strDeviceLanguage, configuration.strDeviceLanguage, 3);
+    }
+
+    // client version 1.6.3
+    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3)
+    {
+      m_configuration.bMonitorOnly = configuration.bMonitorOnly;
+    }
+
+    // ensure that there is at least 1 device type set
+    if (m_configuration.deviceTypes.IsEmpty())
+      m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
+  }
+
+  bool bNeedReinit(false);
+
+  // device types
+  if (SetDeviceTypes(configuration.deviceTypes))
+  {
+    // the device type changed. just copy the rest, and re-register
+    {
+      CLockObject lock(m_mutex);
+      m_configuration.iPhysicalAddress = configuration.iPhysicalAddress;
+      m_configuration.baseDevice       = configuration.baseDevice;
+      m_configuration.iHDMIPort        = configuration.iHDMIPort;
+      bNeedReinit = true;
+    }
+  }
+  else
+  {
+    // set the physical address
+    SetPhysicalAddress(configuration);
+  }
+
+  m_processor->PersistConfiguration(m_configuration);
+
+  if (!primary)
+    primary = GetPrimaryDevice();
+
+  if (bNeedReinit || !primary || primary->GetCurrentPhysicalAddress() != iPA)
+  {
+    // PA or device type changed
+    m_processor->RegisterClient(this);
+  }
+  else if (primary && configuration.bActivateSource == 1 && bIsRunning && !primary->IsActiveSource())
+  {
+    // activate the source if we're not already the active source
+    primary->ActivateSource();
+  }
+
+  return true;
+}
+
+void CCECClient::AddCommand(const cec_command &command)
+{
+  CLockObject lock(m_mutex);
+
+  LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s (%X) -> %s (%X): %s (%2X)", ToString(command.initiator), command.initiator, ToString(command.destination), command.destination, ToString(command.opcode), command.opcode);
+
+  if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
+    m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
+  else if (!m_commandBuffer.Push(command))
+    LIB_CEC->AddLog(CEC_LOG_WARNING, "command buffer is full");
+}
+
+int CCECClient::MenuStateChanged(const cec_menu_state newState)
+{
+  CLockObject lock(m_mutex);
+
+  LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
+
+  if (m_configuration.callbacks &&
+      m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
+      m_configuration.callbacks->CBCecMenuStateChanged)
+    return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
+
+  return 0;
+}
+
+void CCECClient::Alert(const libcec_alert type, const libcec_parameter &param)
+{
+  CLockObject lock(m_mutex);
+
+  if (m_configuration.callbacks &&
+      m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
+      m_configuration.callbacks->CBCecAlert)
+    m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
+}
+
+void CCECClient::AddLog(const cec_log_message &message)
+{
+  CLockObject lock(m_logMutex);
+  if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
+    m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
+  else
+    m_logBuffer.Push(message);
+}
+
+void CCECClient::AddKey(void)
+{
+  CLockObject lock(m_mutex);
+
+  if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
+  {
+    cec_keypress key;
+
+    key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
+    key.keycode = m_iCurrentButton;
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "key released: %1x", key.keycode);
+
+    if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
+      m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
+    else
+      m_keyBuffer.Push(key);
+    m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
+  }
+
+  m_buttontime = 0;
+}
+
+void CCECClient::AddKey(const cec_keypress &key)
+{
+  CLockObject lock(m_mutex);
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %1x", key.keycode);
+
+  if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
+    m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
+  else
+    m_keyBuffer.Push(key);
+
+  m_iCurrentButton = key.duration > 0 ? CEC_USER_CONTROL_CODE_UNKNOWN : key.keycode;
+  m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
+}
+
+void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode)
+{
+  // push a keypress to the buffer with 0 duration and another with the duration set when released
+  cec_keypress key;
+  key.duration = 0;
+  key.keycode = iButtonCode;
+
+  AddKey(key);
+}
+
+void CCECClient::CheckKeypressTimeout(void)
+{
+  if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN && GetTimeMs() - m_buttontime > CEC_BUTTON_TIMEOUT)
+  {
+    AddKey();
+    m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
+  }
+}
+
+void CCECClient::ConfigurationChanged(const libcec_configuration &config)
+{
+  CLockObject lock(m_mutex);
+
+  if (m_configuration.callbacks &&
+      m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
+      m_configuration.callbacks->CBCecConfigurationChanged &&
+      m_processor->CECInitialised())
+    m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
+}
+
+bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
+{
+  CLockObject lock(m_mutex);
+  m_configuration.callbackParam = cbParam;
+  m_configuration.callbacks     = callbacks;
+  return true;
+}
+
+bool CCECClient::PingAdapter(void)
+{
+  return m_processor ? m_processor->PingAdapter() : false;
+}
+
+bool CCECClient::GetNextLogMessage(cec_log_message *message)
+{
+  return (m_logBuffer.Pop(*message));
+}
+
+bool CCECClient::GetNextKeypress(cec_keypress *key)
+{
+  return m_keyBuffer.Pop(*key);
+}
+
+bool CCECClient::GetNextCommand(cec_command *command)
+{
+  return m_commandBuffer.Pop(*command);
+}
+
+CStdString CCECClient::GetConnectionInfo(void)
+{
+  CStdString strLog;
+  strLog.Format("libCEC version = %s, client version = %s, firmware version = %d", ToString((cec_server_version)m_configuration.serverVersion), ToString((cec_client_version)m_configuration.clientVersion), m_configuration.iFirmwareVersion);
+  if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
+  {
+    time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
+    strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
+    strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime
+    strLog.append(" +0000");
+  }
+
+  // log the addresses that are being used
+  if (!m_configuration.logicalAddresses.IsEmpty())
+  {
+    strLog.append(", logical address(es) = ");
+    CECDEVICEVEC devices;
+    m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+    for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
+      strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress());
+  }
+
+  if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
+    strLog.AppendFormat(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort);
+  else
+    strLog.AppendFormat(", physical address: %04x", m_configuration.iPhysicalAddress);
+
+  return strLog;
+}
+
+void CCECClient::SetTVVendorOverride(const cec_vendor_id id)
+{
+  {
+    CLockObject lock(m_mutex);
+    m_configuration.tvVendor = id;
+  }
+
+  if (id != CEC_VENDOR_UNKNOWN)
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString(id));
+
+    CCECBusDevice *tv = m_processor ? m_processor->GetTV() : NULL;
+    if (tv)
+      tv->SetVendorId((uint64_t)id);
+  }
+}
+
+cec_vendor_id CCECClient::GetTVVendorOverride(void)
+{
+  CLockObject lock(m_mutex);
+  return (cec_vendor_id)m_configuration.tvVendor;
+}
+
+void CCECClient::SetOSDName(const CStdString &strDeviceName)
+{
+  {
+    CLockObject lock(m_mutex);
+    snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName.c_str());
+  }
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str());
+
+  CCECBusDevice *primary = GetPrimaryDevice();
+  if (primary && !primary->GetCurrentOSDName().Equals(strDeviceName))
+  {
+    primary->SetOSDName(strDeviceName);
+    if (m_processor && m_processor->CECInitialised())
+      primary->TransmitOSDName(CECDEVICE_TV);
+  }
+}
+
+CStdString CCECClient::GetOSDName(void)
+{
+  CLockObject lock(m_mutex);
+  CStdString strOSDName(m_configuration.strDeviceName);
+  return strOSDName;
+}
+
+void CCECClient::SetWakeDevices(const cec_logical_addresses &addresses)
+{
+  CLockObject lock(m_mutex);
+  m_configuration.wakeDevices = addresses;
+}
+
+cec_logical_addresses CCECClient::GetWakeDevices(void)
+{
+  CLockObject lock(m_mutex);
+  return m_configuration.wakeDevices;
+}
+
+bool CCECClient::AutodetectPhysicalAddress(void)
+{
+  bool bPhysicalAutodetected(false);
+  uint16_t iPhysicalAddress = m_processor ? m_processor->GetDetectedPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
+
+  if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
+
+    CLockObject lock(m_mutex);
+    m_configuration.iPhysicalAddress = iPhysicalAddress;
+    m_configuration.iHDMIPort        = CEC_HDMI_PORTNUMBER_NONE;
+    m_configuration.baseDevice       = CECDEVICE_UNKNOWN;
+    bPhysicalAutodetected            = true;
+  }
+
+  SetDevicePhysicalAddress(iPhysicalAddress);
+
+  return bPhysicalAutodetected;
+}
+
+void CCECClient::SetClientVersion(const cec_client_version version)
+{
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString(version));
+
+  CLockObject lock(m_mutex);
+  m_configuration.clientVersion = (uint32_t)version;
+}
+
+cec_client_version CCECClient::GetClientVersion(void)
+{
+  CLockObject lock(m_mutex);
+  return (cec_client_version)m_configuration.clientVersion;
+}
+
+bool CCECClient::SetDeviceTypes(const cec_device_type_list &deviceTypes)
+{
+  bool bNeedReinit(false);
+
+  {
+    CLockObject lock(m_mutex);
+    bNeedReinit = m_processor && m_processor->CECInitialised() &&
+        (m_configuration.deviceTypes != deviceTypes);
+    m_configuration.deviceTypes = deviceTypes;
+  }
+
+  if (bNeedReinit)
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(deviceTypes[0]));
+
+  return bNeedReinit;
+}
+
+cec_device_type_list CCECClient::GetDeviceTypes(void)
+{
+  cec_device_type_list retVal;
+  CLockObject lock(m_mutex);
+  retVal = m_configuration.deviceTypes;
+  return retVal;
+}
+
+bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress)
+{
+  if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
+    return false;
+
+  // reconfigure all devices
+  cec_logical_address reactivateSource(CECDEVICE_UNKNOWN);
+  CECDEVICEVEC devices;
+  m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
+  for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
+  {
+    // if this device was the active source, reactivate it afterwards
+    if ((*it)->IsActiveSource())
+      reactivateSource = (*it)->GetLogicalAddress();
+
+    // mark the device as inactive source
+    if (IsInitialised())
+      (*it)->MarkAsInactiveSource();
+
+    // set the new physical address
+    (*it)->SetPhysicalAddress(iPhysicalAddress);
+
+    // and transmit it
+    if (IsInitialised())
+      (*it)->TransmitPhysicalAddress();
+  }
+
+  // reactivate the previous active source
+  if (reactivateSource != CECDEVICE_UNKNOWN &&
+      m_processor->CECInitialised() &&
+      IsInitialised())
+  {
+    CCECBusDevice *device = m_processor->GetDevice(reactivateSource);
+    if (device)
+      device->ActivateSource();
+  }
+
+  return true;
+}
+
+bool CCECClient::SwitchMonitoring(bool bEnable)
+{
+  LIB_CEC->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
+
+  if (m_processor)
+  {
+    if (bEnable)
+      return m_processor->UnregisterClient(this);
+    else
+    {
+      m_configuration.bMonitorOnly = false;
+      return m_processor->RegisterClient(this);
+    }
+  }
+
+  return false;
+}
+
+bool CCECClient::PollDevice(const cec_logical_address iAddress)
+{
+  // try to find the primary device
+  CCECBusDevice *primary = GetPrimaryDevice();
+  // poll the destination, with the primary as source
+  if (primary)
+    return primary->TransmitPoll(iAddress);
+
+  return m_processor ? m_processor->PollDevice(iAddress) : false;
+}
+
+cec_logical_addresses CCECClient::GetActiveDevices(void)
+{
+  CECDEVICEVEC activeDevices;
+  if (m_processor)
+    m_processor->GetDevices()->GetActive(activeDevices);
+  return CCECDeviceMap::ToLogicalAddresses(activeDevices);
+}
+
+bool CCECClient::IsActiveDevice(const cec_logical_address iAddress)
+{
+  cec_logical_addresses activeDevices = GetActiveDevices();
+  return activeDevices.IsSet(iAddress);
+}
+
+bool CCECClient::IsActiveDeviceType(const cec_device_type type)
+{
+  CECDEVICEVEC activeDevices;
+  if (m_processor)
+    m_processor->GetDevices()->GetActive(activeDevices);
+  CCECDeviceMap::FilterType(type, activeDevices);
+  return !activeDevices.empty();
+}
+
+cec_logical_address CCECClient::GetActiveSource(void)
+{
+  return m_processor ? m_processor->GetActiveSource() : CECDEVICE_UNKNOWN;
+}
+
+bool CCECClient::IsActiveSource(const cec_logical_address iAddress)
+{
+  return m_processor ? m_processor->IsActiveSource(iAddress) : false;
+}
+
+bool CCECClient::SetStreamPath(const cec_logical_address iAddress)
+{
+  uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress);
+  if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS)
+    return SetStreamPath(iPhysicalAddress);
+  return false;
+}
+
+bool CCECClient::SetStreamPath(const uint16_t iPhysicalAddress)
+{
+  return m_processor ? m_processor->SetStreamPath(iPhysicalAddress) : false;
+}
+
+cec_logical_addresses CCECClient::GetLogicalAddresses(void)
+{
+  cec_logical_addresses addresses;
+  CLockObject lock(m_mutex);
+  addresses = m_configuration.logicalAddresses;
+  return addresses;
+}
+
+bool CCECClient::CanPersistConfiguration(void)
+{
+  return m_processor ? m_processor->CanPersistConfiguration() : false;
+}
+
+bool CCECClient::PersistConfiguration(const libcec_configuration &configuration)
+{
+  return m_processor ? m_processor->PersistConfiguration(configuration) : false;
+}
+
+void CCECClient::RescanActiveDevices(void)
+{
+  if (m_processor)
+    m_processor->RescanActiveDevices();
+}
+
+bool CCECClient::IsLibCECActiveSource(void)
+{
+  bool bReturn(false);
+  if (m_processor)
+  {
+    cec_logical_address activeSource = m_processor->GetActiveSource();
+    CCECBusDevice *device = m_processor->GetDevice(activeSource);
+    if (device)
+      bReturn = device->IsHandledByLibCEC();
+  }
+  return bReturn;
+}
diff --git a/src/lib/CECClient.h b/src/lib/CECClient.h
new file mode 100644 (file)
index 0000000..3d812f0
--- /dev/null
@@ -0,0 +1,307 @@
+#pragma once
+/*
+ * 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 an original work, containing original code.
+ *
+ * libCEC(R) is a trademark of Pulse-Eight Limited.
+ *
+ * This program is dual-licensed; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Alternatively, you can license this library under a commercial license,
+ * please contact Pulse-Eight Licensing for more information.
+ *
+ * For more information contact:
+ * Pulse-Eight Licensing       <license@pulse-eight.com>
+ *     http://www.pulse-eight.com/
+ *     http://www.pulse-eight.net/
+ */
+
+#include "../../include/cectypes.h"
+#include "platform/threads/mutex.h"
+#include "platform/util/buffer.h"
+
+#include "devices/CECBusDevice.h"
+
+namespace CEC
+{
+  class CCECProcessor;
+
+  class CCECClient
+  {
+    friend class CCECProcessor;
+
+  public:
+    CCECClient(CCECProcessor *processor, const libcec_configuration &configuration);
+    virtual ~CCECClient(void);
+
+    /*!
+     * @return True when initialised and registered, false otherwise.
+     */
+    virtual bool IsInitialised(void);
+
+    /*!
+     * @return True when registered in the processor, false otherwise.
+     */
+    virtual bool IsRegistered(void);
+
+    /*!
+     * @return The primary logical address that this client is controlling.
+     */
+    virtual cec_logical_address GetPrimaryLogicalAdddress(void);
+
+    /*!
+     * @return The primary device that this client is controlling, or NULL if none.
+     */
+    virtual CCECBusDevice *GetPrimaryDevice(void);
+
+    /*!
+     * @return Get the playback device or recording device that this client is controlling, or NULL if none.
+     */
+    virtual CCECPlaybackDevice *GetPlaybackDevice(void);
+
+    /*!
+     * @brief Change one of the device types that this client is controlling into another.
+     * @param from The type to change.
+     * @param to The new value.
+     * @return True when changed, false otherwise.
+     */
+    virtual bool ChangeDeviceType(const cec_device_type from, const cec_device_type to);
+
+    /*!
+     * @brief Get a device that this client is controlling, given it's type.
+     * @param type The type of the device to get.
+     * @return The requested device, or NULL if not found.
+     */
+    virtual CCECBusDevice *GetDeviceByType(const cec_device_type type) const;
+
+    /*!
+     * @brief Reset the physical address from the configuration.
+     */
+    virtual void ResetPhysicalAddress(void);
+
+    /*!
+     * @return A string that describes this client.
+     */
+    virtual CStdString GetConnectionInfo(void);
+
+    /*!
+     * @return The current value of the TV vendor override setting.
+     */
+    virtual cec_vendor_id GetTVVendorOverride(void);
+
+    /*!
+     * @return The current value of the OSD name setting.
+     */
+    virtual CStdString GetOSDName(void);
+
+    /*!
+     * @return Get the current value of the wake device setting.
+     */
+    virtual cec_logical_addresses GetWakeDevices(void);
+
+    /*!
+     * @return The version of this client.
+     */
+    virtual cec_client_version GetClientVersion(void);
+
+    /*!
+     * @return The device types that this client is controlling.
+     */
+    virtual cec_device_type_list GetDeviceTypes(void);
+
+    // client-specific part of ICECAdapter
+    virtual bool                  EnableCallbacks(void *cbParam, ICECCallbacks *callbacks);
+    virtual bool                  PingAdapter(void);
+    virtual bool                  GetNextLogMessage(cec_log_message *message); /**< @deprecated will be removed in v2.0 */
+    virtual bool                  GetNextKeypress(cec_keypress *key);          /**< @deprecated will be removed in v2.0 */
+    virtual bool                  GetNextCommand(cec_command *command);        /**< @deprecated will be removed in v2.0 */
+    virtual bool                  Transmit(const cec_command &data);
+    virtual bool                  SetLogicalAddress(const cec_logical_address iLogicalAddress);
+    virtual bool                  SetPhysicalAddress(const uint16_t iPhysicalAddress);
+    virtual bool                  SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_t iPort, bool bForce = false);
+    virtual bool                  SendPowerOnDevices(const cec_logical_address address = CECDEVICE_TV);
+    virtual bool                  SendStandbyDevices(const cec_logical_address address = CECDEVICE_BROADCAST);
+    virtual bool                  SendSetActiveSource(const cec_device_type type = CEC_DEVICE_TYPE_RESERVED);
+    virtual bool                  SendSetDeckControlMode(const cec_deck_control_mode mode, bool bSendUpdate = true);
+    virtual bool                  SendSetDeckInfo(const cec_deck_info info, bool bSendUpdate = true);
+    virtual bool                  SendSetInactiveView(void);
+    virtual bool                  SendSetMenuState(const cec_menu_state state, bool bSendUpdate = true);
+    virtual bool                  SendSetOSDString(const cec_logical_address iLogicalAddress, const cec_display_control duration, const char *strMessage);
+    virtual bool                  SwitchMonitoring(bool bEnable);
+    virtual cec_version           GetDeviceCecVersion(const cec_logical_address iAddress);
+    virtual bool                  GetDeviceMenuLanguage(const cec_logical_address iAddress, cec_menu_language &language);
+    virtual uint64_t              GetDeviceVendorId(const cec_logical_address iAddress);
+    virtual cec_power_status      GetDevicePowerStatus(const cec_logical_address iAddress);
+    virtual uint16_t              GetDevicePhysicalAddress(const cec_logical_address iAddress);
+    virtual bool                  PollDevice(const cec_logical_address iAddress);
+    virtual cec_logical_addresses GetActiveDevices(void);
+    virtual bool                  IsActiveDevice(const cec_logical_address iAddress);
+    virtual bool                  IsActiveDeviceType(const cec_device_type type);
+    virtual uint8_t               SendVolumeUp(bool bSendRelease = true);
+    virtual uint8_t               SendVolumeDown(bool bSendRelease = true);
+    virtual uint8_t               SendMuteAudio(void);
+    virtual bool                  SendKeypress(const cec_logical_address iDestination, const cec_user_control_code key, bool bWait = true);
+    virtual bool                  SendKeyRelease(const cec_logical_address iDestination, bool bWait = true);
+    virtual cec_osd_name          GetDeviceOSDName(const cec_logical_address iAddress);
+    virtual cec_logical_address   GetActiveSource(void);
+    virtual bool                  IsActiveSource(const cec_logical_address iAddress);
+    virtual bool                  SetStreamPath(const cec_logical_address iAddress);
+    virtual bool                  SetStreamPath(const uint16_t iPhysicalAddress);
+    virtual cec_logical_addresses GetLogicalAddresses(void);
+    virtual void                  RescanActiveDevices(void);
+    virtual bool                  IsLibCECActiveSource(void);
+
+    // configuration
+    virtual bool                  GetCurrentConfiguration(libcec_configuration &configuration);
+    virtual bool                  SetConfiguration(const libcec_configuration &configuration);
+    virtual bool                  CanPersistConfiguration(void);
+    virtual bool                  PersistConfiguration(const libcec_configuration &configuration);
+    virtual void                  SetPhysicalAddress(const libcec_configuration &configuration);
+
+    // callbacks
+    virtual void                  AddCommand(const cec_command &command);
+    virtual int                   MenuStateChanged(const cec_menu_state newState);
+    virtual void                  Alert(const libcec_alert type, const libcec_parameter &param);
+    virtual void                  AddLog(const cec_log_message &message);
+    virtual void                  AddKey(void);
+    virtual void                  AddKey(const cec_keypress &key);
+    virtual void                  SetCurrentButton(const cec_user_control_code iButtonCode);
+    virtual void                  CheckKeypressTimeout(void);
+
+  protected:
+    /*!
+     * @brief Register this client in the processor
+     * @return True when registered, false otherwise.
+     */
+    virtual bool OnRegister(void);
+
+    /*!
+     * @brief Called by the processor when this client is unregistered
+     */
+    virtual void OnUnregister(void) { SetRegistered(false); SetInitialised(false); }
+
+    /*!
+     * @brief Set the registered state of this client.
+     * @param bSetTo The new value.
+     */
+    virtual void SetRegistered(bool bSetTo);
+
+    /*!
+     * @brief Set the initialised state of this client.
+     * @param bSetTo The new value
+     */
+    virtual void SetInitialised(bool bSetTo);
+
+    /*!
+     * @brief Change the TV vendor id override setting.
+     * @param id The new value.
+     */
+    virtual void SetTVVendorOverride(const cec_vendor_id id);
+
+    /*!
+     * @brief Change the OSD name of the primary device that this client is controlling.
+     * @param strDeviceName The new value.
+     */
+    virtual void SetOSDName(const CStdString &strDeviceName);
+
+    /*!
+     * @brief Change the value of the devices to wake.
+     * @param addresses The new value.
+     */
+    virtual void SetWakeDevices(const cec_logical_addresses &addresses);
+
+    /*!
+     * @brief Change the value of the client version setting.
+     * @param version The new version setting.
+     */
+    virtual void SetClientVersion(const cec_client_version version);
+
+    /*!
+     * @brief Change the device types that this client is controlling.
+     * @param deviceTypes The new types.
+     * @return True when the client needs to be re-registered to pick up the new setting, false otherwise.
+     */
+    virtual bool SetDeviceTypes(const cec_device_type_list &deviceTypes);
+
+    /*!
+     * @return A pointer to the current configuration of this client.
+     */
+    virtual libcec_configuration *GetConfiguration(void) { return &m_configuration; }
+
+    /*!
+     * @brief Called by the processor when registering this client to allocate the logical addresses.
+     * @return True when the addresses for all types were allocated, false otherwise.
+     */
+    virtual bool AllocateLogicalAddresses(void);
+
+    /*!
+     * @brief Try to allocate a logical address for a recording device controlled by this client.
+     * @return The logical address that was allocated, or CECDEVICE_UNKNOWN if none could be allocated.
+     */
+    virtual cec_logical_address AllocateLogicalAddressRecordingDevice(void);
+
+    /*!
+     * @brief Try to allocate a logical address for a tuner controlled by this client.
+     * @return The logical address that was allocated, or CECDEVICE_UNKNOWN if none could be allocated.
+     */
+    virtual cec_logical_address AllocateLogicalAddressTuner(void);
+
+    /*!
+     * @brief Try to allocate a logical address for a playback device controlled by this client.
+     * @return The logical address that was allocated, or CECDEVICE_UNKNOWN if none could be allocated.
+     */
+    virtual cec_logical_address AllocateLogicalAddressPlaybackDevice(void);
+
+    /*!
+     * @brief Try to allocate a logical address for an audiosystem controlled by this client.
+     * @return The logical address that was allocated, or CECDEVICE_UNKNOWN if none could be allocated.
+     */
+    virtual cec_logical_address AllocateLogicalAddressAudioSystem(void);
+
+    /*!
+     * @brief Change the physical address of the devices controlled by this client.
+     * @param iPhysicalAddress The new physical address.
+     * @return True when changed, false otherwise.
+     */
+    virtual bool SetDevicePhysicalAddress(const uint16_t iPhysicalAddress);
+
+    /*!
+     * @brief Called when the configuration changed and needs to be sent back to the client.
+     * @param config The new configuration.
+     */
+    virtual void ConfigurationChanged(const libcec_configuration &config);
+
+    /*!
+     * @brief Try to autodetect the physical address.
+     * @return True when autodetected (and set in m_configuration), false otherwise.
+     */
+    virtual bool AutodetectPhysicalAddress(void);
+
+    CCECProcessor *                         m_processor;      /**< a pointer to the processor */
+    libcec_configuration                    m_configuration;  /**< the configuration of this client */
+    bool                                    m_bInitialised;   /**< true when initialised, false otherwise */
+    bool                                    m_bRegistered;    /**< true when registered in the processor, false otherwise */
+    PLATFORM::CMutex                        m_mutex;          /**< mutex for changes to this instance */
+    PLATFORM::CMutex                        m_logMutex;       /**< mutex that is held when sending a log message back to the client */
+    cec_user_control_code                   m_iCurrentButton; /**< the control code of the button that's currently held down (if any) */
+    int64_t                                 m_buttontime;     /**< the timestamp when the button was pressed (in seconds since epoch), or 0 if none was pressed. */
+    PLATFORM::SyncedBuffer<cec_log_message> m_logBuffer;      /**< @deprecated will be removed in v2.0. buffer for log messages */
+    PLATFORM::SyncedBuffer<cec_keypress>    m_keyBuffer;      /**< @deprecated will be removed in v2.0. buffer for keypresses */
+    PLATFORM::SyncedBuffer<cec_command>     m_commandBuffer;  /**< @deprecated will be removed in v2.0. buffer for commands */
+  };
+}
diff --git a/src/lib/CECInputBuffer.h b/src/lib/CECInputBuffer.h
new file mode 100644 (file)
index 0000000..9daf582
--- /dev/null
@@ -0,0 +1,91 @@
+#pragma once
+/*
+ * 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 an original work, containing original code.
+ *
+ * libCEC(R) is a trademark of Pulse-Eight Limited.
+ *
+ * This program is dual-licensed; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Alternatively, you can license this library under a commercial license,
+ * please contact Pulse-Eight Licensing for more information.
+ *
+ * For more information contact:
+ * Pulse-Eight Licensing       <license@pulse-eight.com>
+ *     http://www.pulse-eight.com/
+ *     http://www.pulse-eight.net/
+ */
+
+#include "../../include/cectypes.h"
+#include "platform/threads/mutex.h"
+#include "platform/util/buffer.h"
+
+namespace CEC
+{
+  // a buffer that priotises the input from the TV.
+  // if we need more than this, we'll have to change it into a priority_queue
+  class CCECInputBuffer
+  {
+  public:
+    CCECInputBuffer(void) : m_bHasData(false) {}
+    virtual ~CCECInputBuffer(void)
+    {
+      m_condition.Broadcast();
+    }
+
+    bool Push(const cec_command &command)
+    {
+      bool bReturn(false);
+      PLATFORM::CLockObject lock(m_mutex);
+      if (command.initiator == CECDEVICE_TV)
+        bReturn = m_tvInBuffer.Push(command);
+      else
+        bReturn = m_inBuffer.Push(command);
+
+      m_bHasData |= bReturn;
+      if (m_bHasData)
+        m_condition.Signal();
+
+      return bReturn;
+    }
+
+    bool Pop(cec_command &command, uint16_t iTimeout)
+    {
+      bool bReturn(false);
+      PLATFORM::CLockObject lock(m_mutex);
+      if (m_tvInBuffer.IsEmpty() && m_inBuffer.IsEmpty() &&
+          !m_condition.Wait(m_mutex, m_bHasData, iTimeout))
+        return bReturn;
+
+      if (m_tvInBuffer.Pop(command))
+        bReturn = true;
+      else if (m_inBuffer.Pop(command))
+        bReturn = true;
+
+      m_bHasData = !m_tvInBuffer.IsEmpty() || !m_inBuffer.IsEmpty();
+      return bReturn;
+    }
+
+  private:
+    PLATFORM::CMutex                    m_mutex;
+    PLATFORM::CCondition<volatile bool> m_condition;
+    volatile bool                       m_bHasData;
+    PLATFORM::SyncedBuffer<cec_command> m_tvInBuffer;
+    PLATFORM::SyncedBuffer<cec_command> m_inBuffer;
+  };
+};
index 861a9ac6ca5ef380effa53cef73a8378ae330833..3bfa061548c0e4b74d4811e7296790a6849b2630 100644 (file)
@@ -41,6 +41,7 @@
 #include "devices/CECTV.h"
 #include "implementations/CECCommandHandler.h"
 #include "LibCEC.h"
+#include "CECClient.h"
 #include "platform/util/timeutils.h"
 
 using namespace CEC;
@@ -49,436 +50,160 @@ using namespace PLATFORM;
 
 #define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000
 
-CCECProcessor::CCECProcessor(CLibCEC *controller, libcec_configuration *configuration) :
-    m_bConnectionOpened(false),
+#define ToString(x) m_libcec->ToString(x)
+
+CCECProcessor::CCECProcessor(CLibCEC *libcec) :
     m_bInitialised(false),
     m_communication(NULL),
-    m_controller(controller),
-    m_bMonitor(false),
+    m_libcec(libcec),
     m_iStandardLineTimeout(3),
     m_iRetryLineTimeout(3),
     m_iLastTransmission(0)
 {
-  CreateBusDevices();
-  m_configuration.Clear();
-  m_configuration.serverVersion = LIBCEC_VERSION_CURRENT;
-  SetConfiguration(configuration);
-
-  if (m_configuration.tvVendor != CEC_VENDOR_UNKNOWN)
-    m_busDevices[CECDEVICE_TV]->ReplaceHandler(false);
+  m_busDevices = new CCECDeviceMap(this);
 }
 
-CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, const cec_device_type_list &types, uint16_t iPhysicalAddress) :
-    m_bConnectionOpened(false),
-    m_bInitialised(false),
-    m_communication(NULL),
-    m_controller(controller),
-    m_bMonitor(false),
-    m_iStandardLineTimeout(3),
-    m_iRetryLineTimeout(3),
-    m_iLastTransmission(0)
+CCECProcessor::~CCECProcessor(void)
 {
-  m_configuration.Clear();
-  m_configuration.serverVersion    = LIBCEC_VERSION_CURRENT;
-
-  // client version < 1.5.0
-  m_configuration.clientVersion    = (uint32_t)CEC_CLIENT_VERSION_PRE_1_5;
-  snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName);
-  m_configuration.deviceTypes      = types;
-  m_configuration.iPhysicalAddress = iPhysicalAddress;
-  m_configuration.baseDevice       = (cec_logical_address)CEC_DEFAULT_BASE_DEVICE;
-  m_configuration.iHDMIPort        = CEC_DEFAULT_HDMI_PORT;
-
-  if (m_configuration.deviceTypes.IsEmpty())
-    m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
-  CreateBusDevices();
+  Close();
+  delete m_busDevices;
 }
 
-void CCECProcessor::CreateBusDevices(void)
+bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
 {
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
+  CLockObject lock(m_mutex);
+  // open a connection
+  if (!OpenConnection(strPort, iBaudRate, iTimeoutMs))
+    return false;
+
+  // create the processor thread
+  if (!IsRunning())
   {
-    switch(iPtr)
+    if (!CreateThread())
     {
-    case CECDEVICE_AUDIOSYSTEM:
-      m_busDevices[iPtr] = new CCECAudioSystem(this, (cec_logical_address) iPtr);
-      break;
-    case CECDEVICE_PLAYBACKDEVICE1:
-    case CECDEVICE_PLAYBACKDEVICE2:
-    case CECDEVICE_PLAYBACKDEVICE3:
-      m_busDevices[iPtr] = new CCECPlaybackDevice(this, (cec_logical_address) iPtr);
-      break;
-    case CECDEVICE_RECORDINGDEVICE1:
-    case CECDEVICE_RECORDINGDEVICE2:
-    case CECDEVICE_RECORDINGDEVICE3:
-      m_busDevices[iPtr] = new CCECRecordingDevice(this, (cec_logical_address) iPtr);
-      break;
-    case CECDEVICE_TUNER1:
-    case CECDEVICE_TUNER2:
-    case CECDEVICE_TUNER3:
-    case CECDEVICE_TUNER4:
-      m_busDevices[iPtr] = new CCECTuner(this, (cec_logical_address) iPtr);
-      break;
-    case CECDEVICE_TV:
-      m_busDevices[iPtr] = new CCECTV(this, (cec_logical_address) iPtr);
-      break;
-    default:
-      m_busDevices[iPtr] = new CCECBusDevice(this, (cec_logical_address) iPtr);
-      break;
+      m_libcec->AddLog(CEC_LOG_ERROR, "could not create a processor thread");
+      return false;
     }
   }
-}
 
-CCECProcessor::~CCECProcessor(void)
-{
-  Close();
-
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
-  {
-    delete m_busDevices[iPtr];
-    m_busDevices[iPtr] = NULL;
-  }
+  return true;
 }
 
 void CCECProcessor::Close(void)
 {
-  StopThread(false);
-  SetInitialised(false);
+  // mark as uninitialised
+  SetCECInitialised(false);
+
+  // stop the processor
   StopThread();
 
-  bool bClose(false);
+  // close the connection
+  if (m_communication)
   {
-    CLockObject lock(m_mutex);
-    bClose = m_bConnectionOpened;
-    m_bConnectionOpened = false;
+    delete m_communication;
+    m_communication = NULL;
   }
+}
 
-  if (bClose && m_communication)
+void CCECProcessor::ResetMembers(void)
+{
+  // close the connection
+  if (m_communication)
   {
     delete m_communication;
     m_communication = NULL;
   }
+
+  // reset the other members to the initial state
+  m_iStandardLineTimeout = 3;
+  m_iRetryLineTimeout = 3;
+  m_iLastTransmission = 0;
+  m_busDevices->ResetDeviceStatus();
 }
 
 bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */)
 {
   bool bReturn(false);
+  CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
+
+  // ensure that a previous connection is closed
   Close();
 
+  // reset all member to the initial state
+  ResetMembers();
+
+  // check whether the Close() method deleted any previous connection
+  if (m_communication)
   {
-    CLockObject lock(m_mutex);
-    if (m_bConnectionOpened)
-    {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "connection already opened");
-      return false;
-    }
-    m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate);
-    m_bConnectionOpened = (m_communication != NULL);
+    m_libcec->AddLog(CEC_LOG_ERROR, "previous connection could not be closed");
+    return bReturn;
   }
 
-  CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
+  // create a new connection
+  m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate);
 
-  /* open a new connection */
+  // open a new connection
   unsigned iConnectTry(0);
   while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
+    m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
     m_communication->Close();
     CEvent::Sleep(CEC_DEFAULT_CONNECT_RETRY_WAIT);
   }
 
-  if (bReturn)
-  {
-    m_configuration.iFirmwareVersion = m_communication->GetFirmwareVersion();
-    m_configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
-    CStdString strLog;
-    strLog.Format("connected to the CEC adapter. libCEC version = %s, client version = %s, firmware version = %d", ToString((cec_server_version)m_configuration.serverVersion), ToString((cec_client_version)m_configuration.clientVersion), m_configuration.iFirmwareVersion);
-    if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
-    {
-      time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
-      strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
-      strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime
-      strLog.append(" +0000");
-    }
-    CLibCEC::AddLog(CEC_LOG_NOTICE, strLog);
-  }
+  m_libcec->AddLog(CEC_LOG_NOTICE, "connection opened");
 
-  if (m_configuration.bGetSettingsFromROM == 1)
-  {
-    libcec_configuration config;
-    config.Clear();
-    m_communication->GetConfiguration(&config);
+  // always start by setting the ackmask to 0, to clear previous values
+  SetAckMask(0);
 
-    CLockObject lock(m_mutex);
-    if (!config.deviceTypes.IsEmpty())
-      m_configuration.deviceTypes = config.deviceTypes;
-    if (IsValidPhysicalAddress(config.iPhysicalAddress))
-      m_configuration.iPhysicalAddress = config.iPhysicalAddress;
-    snprintf(m_configuration.strDeviceName, 13, "%s", config.strDeviceName);
-  }
+  // mark as initialised
+  SetCECInitialised(true);
 
   return bReturn;
 }
 
-bool CCECProcessor::IsInitialised(void)
+bool CCECProcessor::CECInitialised(void)
 {
   CLockObject lock(m_threadMutex);
   return m_bInitialised;
 }
 
-void CCECProcessor::SetInitialised(bool bSetTo /* = true */)
+void CCECProcessor::SetCECInitialised(bool bSetTo /* = true */)
 {
-  CLockObject lock(m_mutex);
-  m_bInitialised = bSetTo;
-}
-
-bool CCECProcessor::Initialise(void)
-{
-  bool bReturn(false);
   {
     CLockObject lock(m_mutex);
-    if (!m_configuration.logicalAddresses.IsEmpty())
-      m_configuration.logicalAddresses.Clear();
-
-    if (!FindLogicalAddresses())
-    {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "could not detect our logical addresses");
-      return bReturn;
-    }
-
-    /* only set our OSD name for the primary device */
-    m_busDevices[m_configuration.logicalAddresses.primary]->m_strDeviceName = m_configuration.strDeviceName;
-
-    /* make the primary device the active source if the option is set */
-    if (m_configuration.bActivateSource == 1)
-      m_busDevices[m_configuration.logicalAddresses.primary]->m_bActiveSource = true;
-
-    /* set the default menu language for devices we control */
-    cec_menu_language language;
-    language.device = m_configuration.logicalAddresses.primary;
-    memcpy(language.language, m_configuration.strDeviceLanguage, 3);
-    language.language[3] = 0;
-
-    for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-    {
-      if (m_configuration.logicalAddresses[iPtr])
-      {
-        language.device = (cec_logical_address) iPtr;
-        m_busDevices[iPtr]->SetMenuLanguage(language);
-      }
-    }
-  }
-
-  /* get the vendor id from the TV, so we are using the correct handler */
-  m_busDevices[CECDEVICE_TV]->GetVendorId();
-
-  if (IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
-  {
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "setting the physical address to %04X", m_configuration.iPhysicalAddress);
-    m_busDevices[m_configuration.logicalAddresses.primary]->m_iPhysicalAddress = m_configuration.iPhysicalAddress;
-    if ((bReturn = m_busDevices[m_configuration.logicalAddresses.primary]->TransmitPhysicalAddress()) == false)
-      CLibCEC::AddLog(CEC_LOG_ERROR, "unable to set the physical address to %04X", m_configuration.iPhysicalAddress);
-  }
-  else
-  {
-    if (!SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort, true))
-      CLibCEC::AddLog(CEC_LOG_ERROR, "unable to set HDMI port %d on %s (%x)", m_configuration.iHDMIPort, ToString(m_configuration.baseDevice), (uint8_t)m_configuration.baseDevice);
-    bReturn = true;
-  }
-
-  if (bReturn && m_configuration.bActivateSource == 1)
-    m_busDevices[m_configuration.logicalAddresses.primary]->ActivateSource();
-
-  SetInitialised(bReturn);
-  if (bReturn)
-    CLibCEC::ConfigurationChanged(m_configuration);
-
-  return bReturn;
-}
-
-bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
-{
-  bool bReturn(false);
-
-  {
-    CLockObject lock(m_mutex);
-    if (!OpenConnection(strPort, iBaudRate, iTimeoutMs))
-      return bReturn;
-
-    /* create the processor thread */
-    if (!CreateThread())
-    {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a processor thread");
-      return bReturn;
-    }
-  }
-
-  if ((bReturn = Initialise()) == false)
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a processor thread");
-    StopThread(true);
+    m_bInitialised = bSetTo;
   }
-  else
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started");
-  }
-
-  return bReturn;
+  if (!bSetTo)
+    UnregisterClients();
 }
 
 bool CCECProcessor::TryLogicalAddress(cec_logical_address address)
 {
-  if (m_busDevices[address]->TryLogicalAddress())
-  {
-    m_configuration.logicalAddresses.Set(address);
-    return true;
-  }
-
-  return false;
-}
-
-bool CCECProcessor::FindLogicalAddressRecordingDevice(void)
-{
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'");
-  return TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1) ||
-      TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2) ||
-      TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3);
-}
-
-bool CCECProcessor::FindLogicalAddressTuner(void)
-{
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'");
-  return TryLogicalAddress(CECDEVICE_TUNER1) ||
-      TryLogicalAddress(CECDEVICE_TUNER2) ||
-      TryLogicalAddress(CECDEVICE_TUNER3) ||
-      TryLogicalAddress(CECDEVICE_TUNER4);
-}
-
-bool CCECProcessor::FindLogicalAddressPlaybackDevice(void)
-{
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'");
-  return TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1) ||
-      TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2) ||
-      TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3);
-}
-
-bool CCECProcessor::FindLogicalAddressAudioSystem(void)
-{
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audio'");
-  return TryLogicalAddress(CECDEVICE_AUDIOSYSTEM);
-}
-
-bool CCECProcessor::ChangeDeviceType(cec_device_type from, cec_device_type to)
-{
-  bool bChanged(false);
-
-  CLibCEC::AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to));
-
-  CLockObject lock(m_mutex);
-  CCECBusDevice *previousDevice = GetDeviceByType(from);
-  m_configuration.logicalAddresses.primary = CECDEVICE_UNKNOWN;
-
-  for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
-  {
-    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
-      continue;
-
-    if (m_configuration.deviceTypes.types[iPtr] == from)
-    {
-      bChanged = true;
-      m_configuration.deviceTypes.types[iPtr] = to;
-    }
-    else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged)
-    {
-      m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED;
-    }
-  }
-
-  if (bChanged)
-  {
-    FindLogicalAddresses();
-
-    CCECBusDevice *newDevice = GetDeviceByType(to);
-    if (previousDevice && newDevice)
-    {
-      newDevice->SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
-      previousDevice->SetDeviceStatus(CEC_DEVICE_STATUS_NOT_PRESENT);
-
-      newDevice->SetCecVersion(previousDevice->GetCecVersion(false));
-      previousDevice->SetCecVersion(CEC_VERSION_UNKNOWN);
-
-      newDevice->SetMenuLanguage(previousDevice->GetMenuLanguage(false));
-
-      newDevice->SetMenuState(previousDevice->GetMenuState());
-      previousDevice->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
-
-      newDevice->SetOSDName(previousDevice->GetOSDName(false));
-      previousDevice->SetOSDName(ToString(previousDevice->GetLogicalAddress()));
-
-      newDevice->SetPhysicalAddress(previousDevice->GetPhysicalAddress());
-      previousDevice->SetPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
-
-      newDevice->SetPowerStatus(previousDevice->GetPowerStatus(false));
-      previousDevice->SetPowerStatus(CEC_POWER_STATUS_UNKNOWN);
-
-      newDevice->SetVendorId(previousDevice->GetVendorId(false));
-      previousDevice->SetVendorId(CEC_VENDOR_UNKNOWN);
-
-      if ((from == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || from == CEC_DEVICE_TYPE_RECORDING_DEVICE) &&
-          (to == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || to == CEC_DEVICE_TYPE_RECORDING_DEVICE))
-      {
-        ((CCECPlaybackDevice *) newDevice)->SetDeckControlMode(((CCECPlaybackDevice *) previousDevice)->GetDeckControlMode());
-        ((CCECPlaybackDevice *) previousDevice)->SetDeckControlMode(CEC_DECK_CONTROL_MODE_STOP);
-
-        ((CCECPlaybackDevice *) newDevice)->SetDeckStatus(((CCECPlaybackDevice *) previousDevice)->GetDeckStatus());
-        ((CCECPlaybackDevice *) previousDevice)->SetDeckStatus(CEC_DECK_INFO_STOP);
-      }
-    }
-  }
-
-  return true;
-}
-
-bool CCECProcessor::FindLogicalAddresses(void)
-{
-  bool bReturn(true);
-  m_configuration.logicalAddresses.Clear();
-
-  if (m_configuration.deviceTypes.IsEmpty())
+  // find the device
+  CCECBusDevice *device = m_busDevices->At(address);
+  if (device)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "no device types set");
-    return false;
-  }
+    // check if it's already marked as present or used
+    if (device->IsPresent() || device->IsHandledByLibCEC())
+      return false;
 
-  for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
-  {
-    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
-      continue;
-
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - device %d: type %d", __FUNCTION__, iPtr, m_configuration.deviceTypes.types[iPtr]);
-
-    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE)
-      bReturn &= FindLogicalAddressRecordingDevice();
-    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER)
-      bReturn &= FindLogicalAddressTuner();
-    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
-      bReturn &= FindLogicalAddressPlaybackDevice();
-    if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
-      bReturn &= FindLogicalAddressAudioSystem();
+    // poll the LA if not
+    SetAckMask(0);
+    return device->TryLogicalAddress();
   }
 
-  if (bReturn)
-    SetAckMask(m_configuration.logicalAddresses.AckMask());
-
-  return bReturn;
+  return false;
 }
 
 void CCECProcessor::ReplaceHandlers(void)
 {
-  if (!IsInitialised())
+  if (!CECInitialised())
     return;
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++)
-    m_busDevices[iPtr]->ReplaceHandler(m_bInitialised);
+
+  // check each device
+  for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
+    it->second->ReplaceHandler(true);
 }
 
 bool CCECProcessor::OnCommandReceived(const cec_command &command)
@@ -488,558 +213,257 @@ bool CCECProcessor::OnCommandReceived(const cec_command &command)
 
 void *CCECProcessor::Process(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started");
+  m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started");
 
   cec_command command;
-  command.Clear();
 
+  // as long as we're not being stopped and the connection is open
   while (!IsStopped() && m_communication->IsOpen())
   {
+    // wait for a new incoming command, and process it
     if (m_inBuffer.Pop(command, CEC_PROCESSOR_SIGNAL_WAIT_TIME))
-      ParseCommand(command);
+      ProcessCommand(command);
 
-    if (IsInitialised())
+    if (CECInitialised())
     {
-      ReplaceHandlers();
+      // check clients for keypress timeouts
+      m_libcec->CheckKeypressTimeout();
 
-      m_controller->CheckKeypressTimeout();
+      // check if we need to replace handlers
+      ReplaceHandlers();
     }
   }
 
   return NULL;
 }
 
-bool CCECProcessor::SetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
+bool CCECProcessor::ActivateSource(uint16_t iStreamPath)
 {
   bool bReturn(false);
 
-  if (!IsRunning())
-    return bReturn;
-
-  cec_logical_address addr = m_configuration.logicalAddresses.primary;
-
-  if (type != CEC_DEVICE_TYPE_RESERVED)
-  {
-    for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++)
-    {
-      if (m_configuration.logicalAddresses[iPtr] && m_busDevices[iPtr]->m_type == type)
-      {
-        addr = (cec_logical_address) iPtr;
-        break;
-      }
-    }
-  }
-
-  m_busDevices[addr]->SetActiveSource();
-  if (IsValidPhysicalAddress(m_busDevices[addr]->GetPhysicalAddress()))
-    bReturn = m_busDevices[addr]->ActivateSource();
-
-  return bReturn;
-}
-
-bool CCECProcessor::SetActiveSource(uint16_t iStreamPath)
-{
-  bool bReturn(false);
-
-  // suppress polls when searching for a device
+  // find the device with the given PA
   CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath);
+  // and make it the active source when found
   if (device)
-  {
-    device->SetActiveSource();
-    bReturn = true;
-  }
+    bReturn = device->ActivateSource();
   else
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath);
-  }
+    m_libcec->AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath);
 
   return bReturn;
 }
 
 void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout)
 {
-  CLockObject lock(m_mutex);
-  m_iStandardLineTimeout = iTimeout;
-}
-
-void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout)
-{
-  CLockObject lock(m_mutex);
-  m_iRetryLineTimeout = iTimeout;
-}
-
-bool CCECProcessor::SetActiveView(void)
-{
-  CLibCEC::AddLog(CEC_LOG_WARNING, "deprecated method %s called", __FUNCTION__);
-  return SetActiveSource(m_configuration.deviceTypes.IsEmpty() ? CEC_DEVICE_TYPE_RESERVED : m_configuration.deviceTypes[0]);
-}
-
-bool CCECProcessor::SetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate /* = true */)
-{
-  bool bReturn(false);
-
-  CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
-  if (device)
-  {
-    ((CCECPlaybackDevice *) device)->SetDeckControlMode(mode);
-    if (bSendUpdate)
-      ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV);
-    bReturn = true;
-  }
-
-  return bReturn;
-}
-
-bool CCECProcessor::SetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true */)
-{
-  bool bReturn(false);
-
-  CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
-  if (device)
-  {
-    ((CCECPlaybackDevice *) device)->SetDeckStatus(info);
-    if (bSendUpdate)
-      ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV);
-    bReturn = true;
-  }
-
-  return bReturn;
-}
-
-bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */)
-{
-  bool bReturn(false);
-
-  // limit the HDMI port range to 1-15
-  if (iPort < CEC_MIN_HDMI_PORTNUMBER ||
-      iPort > CEC_MAX_HDMI_PORTNUMBER)
-    return bReturn;
-
-  {
-    CLockObject lock(m_mutex);
-    m_configuration.baseDevice = iBaseDevice;
-    m_configuration.iHDMIPort  = iPort;
-  }
-
-  if (!IsRunning() && !bForce)
-    return true;
-
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice);
-
-  uint16_t iPhysicalAddress(0);
-  if (iBaseDevice > CECDEVICE_TV)
-    iPhysicalAddress = m_busDevices[iBaseDevice]->GetPhysicalAddress(false);
-
-  if (iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
-  {
-    if (iPhysicalAddress == 0)
-      iPhysicalAddress += 0x1000 * iPort;
-    else if (iPhysicalAddress % 0x1000 == 0)
-      iPhysicalAddress += 0x100 * iPort;
-    else if (iPhysicalAddress % 0x100 == 0)
-      iPhysicalAddress += 0x10 * iPort;
-    else if (iPhysicalAddress % 0x10 == 0)
-      iPhysicalAddress += iPort;
-
-    bReturn = true;
-  }
-
-  if (!bReturn)
-  {
-    CLibCEC::AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the default value %04X", iPhysicalAddress, CEC_DEFAULT_PHYSICAL_ADDRESS);
-    iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
-  }
-
-  SetPhysicalAddress(iPhysicalAddress);
-
-  return bReturn;
-}
-
-bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress)
-{
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-  {
-    if (m_busDevices[iPtr]->GetPhysicalAddress() == iPhysicalAddress)
-      return true;
-  }
-  return false;
-}
-
-bool CCECProcessor::TransmitInactiveSource(void)
-{
-  if (!IsRunning())
-    return false;
-
-  if (!m_configuration.logicalAddresses.IsEmpty() && m_busDevices[m_configuration.logicalAddresses.primary])
-    return m_busDevices[m_configuration.logicalAddresses.primary]->TransmitInactiveSource();
-  return false;
-}
-
-void CCECProcessor::LogOutput(const cec_command &data)
-{
-  CStdString strTx;
-  strTx.Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
-  if (data.opcode_set)
-      strTx.AppendFormat(":%02x", (uint8_t)data.opcode);
-
-  for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
-    strTx.AppendFormat(":%02x", data.parameters[iPtr]);
-  CLibCEC::AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
-}
-
-bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress)
-{
-  CLockObject lock(m_mutex);
-  if (m_configuration.logicalAddresses.primary != iLogicalAddress)
-  {
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress);
-    m_configuration.logicalAddresses.primary = iLogicalAddress;
-    m_configuration.logicalAddresses.Set(iLogicalAddress);
-    return SetAckMask(m_configuration.logicalAddresses.AckMask());
-  }
-
-  return true;
-}
-
-bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = true */)
-{
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-  {
-    if (m_configuration.logicalAddresses[iPtr])
-      m_busDevices[iPtr]->SetMenuState(state);
-  }
-
-  if (bSendUpdate)
-    m_busDevices[m_configuration.logicalAddresses.primary]->TransmitMenuState(CECDEVICE_TV);
-
-  return true;
-}
-
-bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpdate /* = true */)
-{
-  bool bSendActiveView(false);
-  bool bReturn(false);
-  cec_logical_addresses sendUpdatesTo;
-  sendUpdatesTo.Clear();
-
-  {
-    CLockObject lock(m_mutex);
-    m_configuration.iPhysicalAddress = iPhysicalAddress;
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "setting physical address to '%04X'", iPhysicalAddress);
-
-    if (!m_configuration.logicalAddresses.IsEmpty())
-    {
-      bool bWasActiveSource(false);
-      for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-        if (m_configuration.logicalAddresses[iPtr])
-        {
-          bWasActiveSource |= m_busDevices[iPtr]->IsActiveSource();
-          m_busDevices[iPtr]->SetInactiveSource();
-          m_busDevices[iPtr]->SetPhysicalAddress(iPhysicalAddress);
-          if (bSendUpdate)
-            sendUpdatesTo.Set((cec_logical_address)iPtr);
-        }
-
-      bSendActiveView = bWasActiveSource && bSendUpdate;
-      bReturn = true;
-    }
-  }
-
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-    if (sendUpdatesTo[iPtr])
-      m_busDevices[iPtr]->TransmitPhysicalAddress();
-
-  if (bSendActiveView)
-    SetActiveView();
-
-  if (bReturn)
-  {
-    libcec_configuration config;
-    {
-      CLockObject lock(m_mutex);
-      config = m_configuration;
-    }
-
-    PersistConfiguration(&config);
-    CLibCEC::ConfigurationChanged(config);
-  }
-
-  return bReturn;
-}
-
-bool CCECProcessor::SwitchMonitoring(bool bEnable)
-{
-  CLibCEC::AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
-
-  {
-    CLockObject lock(m_mutex);
-    m_bMonitor = bEnable;
-  }
-
-  if (bEnable)
-    return SetAckMask(0);
-  else
-    return SetAckMask(m_configuration.logicalAddresses.AckMask());
-}
-
-bool CCECProcessor::PollDevice(cec_logical_address iAddress)
-{
-  if (iAddress != CECDEVICE_UNKNOWN && m_busDevices[iAddress])
-  {
-    return m_configuration.logicalAddresses.primary == CECDEVICE_UNKNOWN ?
-        m_busDevices[iAddress]->TransmitPoll(iAddress) :
-        m_busDevices[m_configuration.logicalAddresses.primary]->TransmitPoll(iAddress);
-  }
-  return false;
-}
-
-uint8_t CCECProcessor::VolumeUp(bool bSendRelease /* = true */)
-{
-  uint8_t status(CEC_AUDIO_VOLUME_STATUS_UNKNOWN);
-  if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
-    status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeUp(bSendRelease);
-
-  return status;
-}
-
-uint8_t CCECProcessor::VolumeDown(bool bSendRelease /* = true */)
-{
-  uint8_t status(CEC_AUDIO_VOLUME_STATUS_UNKNOWN);
-  if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
-    status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeDown(bSendRelease);
-
-  return status;
-}
-
-uint8_t CCECProcessor::MuteAudio(bool bSendRelease /* = true */)
-{
-  uint8_t status(CEC_AUDIO_VOLUME_STATUS_UNKNOWN);
-  if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
-    status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->MuteAudio(bSendRelease);
-
-  return status;
-}
-
-CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */)
-{
-  CCECBusDevice *device(NULL);
-
-  // check each device until we found a match
-  for (uint8_t iPtr = CECDEVICE_TV; !device && iPtr < CECDEVICE_BROADCAST; iPtr++)
-  {
-    if (m_busDevices[iPtr]->GetPhysicalAddress(bSuppressUpdate) == iPhysicalAddress)
-      device = m_busDevices[iPtr];
-  }
-
-  return device;
-}
-
-CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const
-{
-  CCECBusDevice *device = NULL;
-
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-  {
-    if (m_busDevices[iPtr]->m_type == type && m_configuration.logicalAddresses[iPtr])
-    {
-      device = m_busDevices[iPtr];
-      break;
-    }
-  }
-
-  return device;
+  CLockObject lock(m_mutex);
+  m_iStandardLineTimeout = iTimeout;
 }
 
-CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) const
+uint8_t CCECProcessor::GetStandardLineTimeout(void)
 {
-  CCECBusDevice *device(NULL);
-  cec_logical_address primary = m_configuration.logicalAddresses.primary;
-  if (primary != CECDEVICE_UNKNOWN)
-    device = m_busDevices[primary];
-  return device;
+  CLockObject lock(m_mutex);
+  return m_iStandardLineTimeout;
 }
 
-cec_version CCECProcessor::GetDeviceCecVersion(cec_logical_address iAddress)
+void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout)
 {
-  return m_busDevices[iAddress]->GetCecVersion();
+  CLockObject lock(m_mutex);
+  m_iRetryLineTimeout = iTimeout;
 }
 
-cec_osd_name CCECProcessor::GetDeviceOSDName(cec_logical_address iAddress)
+uint8_t CCECProcessor::GetRetryLineTimeout(void)
 {
-  CStdString strOSDName = m_busDevices[iAddress]->GetOSDName();
-  cec_osd_name retVal;
-
-  snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str());
-  retVal.device = iAddress;
-
-  return retVal;
+  CLockObject lock(m_mutex);
+  return m_iRetryLineTimeout;
 }
 
-bool CCECProcessor::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language)
+bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress)
 {
-  if (m_busDevices[iAddress])
-  {
-    *language = m_busDevices[iAddress]->GetMenuLanguage();
-    return (strcmp(language->language, "???") != 0);
-  }
-  return false;
+  CCECBusDevice *device = GetDeviceByPhysicalAddress(iPhysicalAddress);
+  return device != NULL;
 }
 
-CStdString CCECProcessor::GetDeviceName(void) const
+void CCECProcessor::LogOutput(const cec_command &data)
 {
-  CStdString strName;
-  strName = m_configuration.strDeviceName;
-  return strName;
+  CStdString strTx;
+
+  // initiator and destination
+  strTx.Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
+
+  // append the opcode
+  if (data.opcode_set)
+      strTx.AppendFormat(":%02x", (uint8_t)data.opcode);
+
+  // append the parameters
+  for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
+    strTx.AppendFormat(":%02x", data.parameters[iPtr]);
+
+  // and log it
+  m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
 }
 
-uint64_t CCECProcessor::GetDeviceVendorId(cec_logical_address iAddress)
+bool CCECProcessor::PollDevice(cec_logical_address iAddress)
 {
-  if (m_busDevices[iAddress])
-    return m_busDevices[iAddress]->GetVendorId();
+  // try to find the primary device
+  CCECBusDevice *primary = GetPrimaryDevice();
+  // poll the destination, with the primary as source
+  if (primary)
+    return primary->TransmitPoll(iAddress);
+
+  // try to find the destination
+  CCECBusDevice *device = m_busDevices->At(iAddress);
+  // and poll the destination, with the same LA as source
+  if (device)
+    return device->TransmitPoll(iAddress);
+
   return false;
 }
 
-uint16_t CCECProcessor::GetDevicePhysicalAddress(cec_logical_address iAddress)
+CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */)
 {
-  if (m_busDevices[iAddress])
-    return m_busDevices[iAddress]->GetPhysicalAddress(false);
-  return false;
+  return m_busDevices ?
+      m_busDevices->GetDeviceByPhysicalAddress(iPhysicalAddress, bSuppressUpdate) :
+      NULL;
 }
 
-cec_power_status CCECProcessor::GetDevicePowerStatus(cec_logical_address iAddress)
+CCECBusDevice *CCECProcessor::GetDevice(cec_logical_address address) const
 {
-  if (m_busDevices[iAddress])
-    return m_busDevices[iAddress]->GetPowerStatus();
-  return CEC_POWER_STATUS_UNKNOWN;
+  return m_busDevices ?
+      m_busDevices->At(address) :
+      NULL;
 }
 
 cec_logical_address CCECProcessor::GetActiveSource(bool bRequestActiveSource /* = true */)
 {
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++)
-  {
-    if (m_busDevices[iPtr]->IsActiveSource())
-      return (cec_logical_address)iPtr;
-  }
+  // get the device that is marked as active source from the device map
+  CCECBusDevice *activeSource = m_busDevices->GetActiveSource();
+  if (activeSource)
+    return activeSource->GetLogicalAddress();
 
-  if (bRequestActiveSource && m_configuration.logicalAddresses.primary != CECDEVICE_UNKNOWN)
+  if (bRequestActiveSource)
   {
-    CCECBusDevice *primary = m_busDevices[m_configuration.logicalAddresses.primary];
+    // request the active source from the bus
+    CCECBusDevice *primary = GetPrimaryDevice();
     if (primary)
+    {
       primary->RequestActiveSource();
-
-    return GetActiveSource(false);
+      return GetActiveSource(false);
+    }
   }
 
+  // unknown or none
   return CECDEVICE_UNKNOWN;
 }
 
 bool CCECProcessor::IsActiveSource(cec_logical_address iAddress)
 {
-  return iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST ?
-    m_busDevices[iAddress]->IsActiveSource() :
-    false;
+  CCECBusDevice *device = m_busDevices->At(iAddress);
+  return device && device->IsActiveSource();
 }
 
 bool CCECProcessor::Transmit(const cec_command &data)
 {
-  if (m_configuration.logicalAddresses[(uint8_t)data.destination])
+  uint8_t iMaxTries(0);
+  bool bRetry(true);
+  uint8_t iTries(0);
+
+  // get the current timeout setting
+  uint8_t iLineTimeout(GetStandardLineTimeout());
+
+  // reset the state of this message to 'unknown'
+  cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN;
+
+  LogOutput(data);
+
+  // find the initiator device
+  CCECBusDevice *initiator = m_busDevices->At(data.initiator);
+  if (!initiator)
   {
-    CLibCEC::AddLog(CEC_LOG_WARNING, "not sending data to myself!");
+    m_libcec->AddLog(CEC_LOG_WARNING, "invalid initiator");
     return false;
   }
 
-  uint8_t iMaxTries(0);
+  // find the destination device, if it's not the broadcast address
+  if (data.destination != CECDEVICE_BROADCAST)
   {
-    CLockObject lock(m_mutex);
-    if (IsStopped())
-      return false;
-    LogOutput(data);
-    m_iLastTransmission = GetTimeMs();
-    if (!m_communication || !m_communication->IsOpen())
+    // check if the device is marked as handled by libCEC
+    CCECBusDevice *destination = m_busDevices->At(data.destination);
+    if (destination && destination->IsHandledByLibCEC())
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "cannot transmit command: connection closed");
+      // and reject the command if it's trying to send data to a device that is handled by libCEC
+      m_libcec->AddLog(CEC_LOG_WARNING, "not sending data to myself!");
       return false;
     }
-    iMaxTries = m_busDevices[data.initiator]->GetHandler()->GetTransmitRetries() + 1;
   }
 
-  bool bRetry(true);
-  uint8_t iTries(0);
-  uint8_t iLineTimeout = m_iStandardLineTimeout;
-  cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN;
+  {
+    CLockObject lock(m_mutex);
+    m_iLastTransmission = GetTimeMs();
+    // set the number of tries
+    iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1;
+  }
 
+  // and try to send the command
   while (bRetry && ++iTries < iMaxTries)
   {
-    if (m_busDevices[data.initiator]->IsUnsupportedFeature(data.opcode))
+    if (initiator->IsUnsupportedFeature(data.opcode))
       return false;
 
-    adapterState = m_communication->Write(data, bRetry, iLineTimeout);
+    adapterState = !IsStopped() && m_communication && m_communication->IsOpen() ?
+        m_communication->Write(data, bRetry, iLineTimeout) :
+        ADAPTER_MESSAGE_STATE_ERROR;
     iLineTimeout = m_iRetryLineTimeout;
   }
 
   return adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED;
 }
 
-void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
+void CCECProcessor::TransmitAbort(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "<< transmitting abort message");
+  m_libcec->AddLog(CEC_LOG_DEBUG, "<< transmitting abort message");
 
   cec_command command;
-  // TODO
-  cec_command::Format(command, m_configuration.logicalAddresses.primary, address, CEC_OPCODE_FEATURE_ABORT);
+  cec_command::Format(command, source, destination, CEC_OPCODE_FEATURE_ABORT);
   command.parameters.PushBack((uint8_t)opcode);
   command.parameters.PushBack((uint8_t)reason);
 
   Transmit(command);
 }
 
-void CCECProcessor::ParseCommand(const cec_command &command)
+void CCECProcessor::ProcessCommand(const cec_command &command)
 {
+  // log the command
   CStdString dataStr;
   dataStr.Format(">> %1x%1x", command.initiator, command.destination);
   if (command.opcode_set == 1)
     dataStr.AppendFormat(":%02x", command.opcode);
   for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
     dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]);
-  CLibCEC::AddLog(CEC_LOG_TRAFFIC, dataStr.c_str());
+  m_libcec->AddLog(CEC_LOG_TRAFFIC, dataStr.c_str());
 
-  if (!m_bMonitor && command.initiator >= CECDEVICE_TV && command.initiator <= CECDEVICE_BROADCAST)
-    m_busDevices[(uint8_t)command.initiator]->HandleCommand(command);
-}
+  // find the initiator
+  CCECBusDevice *device = m_busDevices->At(command.initiator);
 
-cec_logical_addresses CCECProcessor::GetActiveDevices(void)
-{
-  cec_logical_addresses addresses;
-  addresses.Clear();
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-  {
-    if (m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT)
-      addresses.Set((cec_logical_address) iPtr);
-  }
-  return addresses;
+  if (device)
+    device->HandleCommand(command);
 }
 
 bool CCECProcessor::IsPresentDevice(cec_logical_address address)
 {
-  return m_busDevices[address]->GetStatus() == CEC_DEVICE_STATUS_PRESENT;
+  CCECBusDevice *device = m_busDevices->At(address);
+  return device && device->GetStatus() == CEC_DEVICE_STATUS_PRESENT;
 }
 
 bool CCECProcessor::IsPresentDeviceType(cec_device_type type)
 {
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-  {
-    if (m_busDevices[iPtr]->GetType() == type && m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT)
-      return true;
-  }
-
-  return false;
+  CECDEVICEVEC devices;
+  m_busDevices->GetByType(type, devices);
+  CCECDeviceMap::FilterActive(devices);
+  return !devices.empty();
 }
 
-uint16_t CCECProcessor::GetPhysicalAddress(void) const
+uint16_t CCECProcessor::GetDetectedPhysicalAddress(void) const
 {
-  if (!m_configuration.logicalAddresses.IsEmpty() && m_busDevices[m_configuration.logicalAddresses.primary])
-    return m_busDevices[m_configuration.logicalAddresses.primary]->GetPhysicalAddress();
-  return false;
+  return m_communication ? m_communication->GetPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
 }
 
 bool CCECProcessor::SetAckMask(uint16_t iMask)
@@ -1047,755 +471,339 @@ bool CCECProcessor::SetAckMask(uint16_t iMask)
   return m_communication ? m_communication->SetAckMask(iMask) : false;
 }
 
-bool CCECProcessor::TransmitKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */)
+bool CCECProcessor::StandbyDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices)
 {
-  return m_busDevices[iDestination]->TransmitKeypress(key, bWait);
+  bool bReturn(true);
+  for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
+    bReturn &= (*it)->Standby(initiator);
+  return bReturn;
 }
 
-bool CCECProcessor::TransmitKeyRelease(cec_logical_address iDestination, bool bWait /* = true */)
+bool CCECProcessor::StandbyDevice(const cec_logical_address initiator, cec_logical_address address)
 {
-  return m_busDevices[iDestination]->TransmitKeyRelease(bWait);
+  CCECBusDevice *device = m_busDevices->At(address);
+  return device ? device->Standby(initiator) : false;
 }
 
-bool CCECProcessor::EnablePhysicalAddressDetection(void)
+bool CCECProcessor::PowerOnDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices)
 {
-  CLibCEC::AddLog(CEC_LOG_WARNING, "deprecated method %s called", __FUNCTION__);
-  uint16_t iPhysicalAddress = m_communication->GetPhysicalAddress();
-  if (IsValidPhysicalAddress(iPhysicalAddress))
-  {
-    m_configuration.bAutodetectAddress = 1;
-    m_configuration.iPhysicalAddress   = iPhysicalAddress;
-    m_configuration.baseDevice         = CECDEVICE_UNKNOWN;
-    m_configuration.iHDMIPort          = CEC_HDMI_PORTNUMBER_NONE;
-    return SetPhysicalAddress(iPhysicalAddress);
-  }
-  return false;
+  bool bReturn(true);
+  for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
+    bReturn &= (*it)->PowerOn(initiator);
+  return bReturn;
 }
 
-bool CCECProcessor::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
+bool CCECProcessor::PowerOnDevice(const cec_logical_address initiator, cec_logical_address address)
 {
-  if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
-  {
-    bool bReturn(true);
-    for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
-    {
-      if (m_configuration.powerOffDevices[iPtr])
-      {
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - putting '%s' in standby mode", __FUNCTION__, ToString((cec_logical_address)iPtr));
-        bReturn &= m_busDevices[iPtr]->Standby();
-      }
-    }
-    return bReturn;
-  }
-
-  return m_busDevices[address]->Standby();
+  CCECBusDevice *device = m_busDevices->At(address);
+  return device ? device->PowerOn(initiator) : false;
 }
 
-bool CCECProcessor::PowerOnDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
+bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */)
 {
-  if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
+  bool bReturn(false);
+  // open a connection if no connection has been opened
+  if (!m_communication && strPort)
   {
-    bool bReturn(true);
-    for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
+    IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort);
+    CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT);
+    int iConnectTry(0);
+    while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
+    {
+      m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
+      comm->Close();
+      Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
+    }
+    if (comm->IsOpen())
     {
-      if (m_configuration.wakeDevices[iPtr])
-      {
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - powering on '%s'", __FUNCTION__, ToString((cec_logical_address)iPtr));
-        bReturn &= m_busDevices[iPtr]->PowerOn();
-      }
+      bReturn = comm->StartBootloader();
+      delete comm;
     }
     return bReturn;
   }
-
-  return m_busDevices[address]->PowerOn();
-}
-
-const char *CCECProcessor::ToString(const cec_device_type type)
-{
-  switch (type)
+  else
   {
-  case CEC_DEVICE_TYPE_AUDIO_SYSTEM:
-    return "audio system";
-  case CEC_DEVICE_TYPE_PLAYBACK_DEVICE:
-    return "playback device";
-  case CEC_DEVICE_TYPE_RECORDING_DEVICE:
-      return "recording device";
-  case CEC_DEVICE_TYPE_RESERVED:
-      return "reserved";
-  case CEC_DEVICE_TYPE_TUNER:
-      return "tuner";
-  case CEC_DEVICE_TYPE_TV:
-      return "TV";
-  default:
-    return "unknown";
+    m_communication->StartBootloader();
+    Close();
+    bReturn = true;
   }
-}
 
-const char *CCECProcessor::ToString(const cec_menu_state state)
-{
-  switch (state)
-  {
-  case CEC_MENU_STATE_ACTIVATED:
-    return "activated";
-  case CEC_MENU_STATE_DEACTIVATED:
-    return "deactivated";
-  default:
-    return "unknown";
-  }
+  return bReturn;
 }
 
-const char *CCECProcessor::ToString(const cec_version version)
+bool CCECProcessor::PingAdapter(void)
 {
-  switch (version)
-  {
-  case CEC_VERSION_1_2:
-    return "1.2";
-  case CEC_VERSION_1_2A:
-    return "1.2a";
-  case CEC_VERSION_1_3:
-    return "1.3";
-  case CEC_VERSION_1_3A:
-    return "1.3a";
-  case CEC_VERSION_1_4:
-    return "1.4";
-  default:
-    return "unknown";
-  }
+  return m_communication->PingAdapter();
 }
 
-const char *CCECProcessor::ToString(const cec_power_status status)
+void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination)
 {
-  switch (status)
-  {
-  case CEC_POWER_STATUS_ON:
-    return "on";
-  case CEC_POWER_STATUS_STANDBY:
-    return "standby";
-  case CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY:
-    return "in transition from on to standby";
-  case CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON:
-    return "in transition from standby to on";
-  default:
-    return "unknown";
-  }
+  CCECBusDevice *device = m_busDevices->At(destination);
+  if (device)
+    device->HandlePollFrom(initiator);
 }
 
-const char *CCECProcessor::ToString(const cec_logical_address address)
+bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator)
 {
-  switch(address)
-  {
-  case CECDEVICE_AUDIOSYSTEM:
-    return "Audio";
-  case CECDEVICE_BROADCAST:
-    return "Broadcast";
-  case CECDEVICE_FREEUSE:
-    return "Free use";
-  case CECDEVICE_PLAYBACKDEVICE1:
-    return "Playback 1";
-  case CECDEVICE_PLAYBACKDEVICE2:
-    return "Playback 2";
-  case CECDEVICE_PLAYBACKDEVICE3:
-    return "Playback 3";
-  case CECDEVICE_RECORDINGDEVICE1:
-    return "Recorder 1";
-  case CECDEVICE_RECORDINGDEVICE2:
-    return "Recorder 2";
-  case CECDEVICE_RECORDINGDEVICE3:
-    return "Recorder 3";
-  case CECDEVICE_RESERVED1:
-    return "Reserved 1";
-  case CECDEVICE_RESERVED2:
-    return "Reserved 2";
-  case CECDEVICE_TUNER1:
-    return "Tuner 1";
-  case CECDEVICE_TUNER2:
-    return "Tuner 2";
-  case CECDEVICE_TUNER3:
-    return "Tuner 3";
-  case CECDEVICE_TUNER4:
-    return "Tuner 4";
-  case CECDEVICE_TV:
-    return "TV";
-  default:
-    return "unknown";
-  }
+  CCECBusDevice *device = m_busDevices->At(initiator);
+  return !device || !device->HandleReceiveFailed();
 }
 
-const char *CCECProcessor::ToString(const cec_deck_control_mode mode)
+bool CCECProcessor::SetStreamPath(uint16_t iPhysicalAddress)
 {
-  switch (mode)
-  {
-  case CEC_DECK_CONTROL_MODE_SKIP_FORWARD_WIND:
-    return "skip forward wind";
-  case CEC_DECK_CONTROL_MODE_EJECT:
-    return "eject";
-  case CEC_DECK_CONTROL_MODE_SKIP_REVERSE_REWIND:
-    return "reverse rewind";
-  case CEC_DECK_CONTROL_MODE_STOP:
-    return "stop";
-  default:
-    return "unknown";
-  }
+  // stream path changes are sent by the TV
+  return GetTV()->GetHandler()->TransmitSetStreamPath(iPhysicalAddress);
 }
 
-const char *CCECProcessor::ToString(const cec_deck_info status)
+bool CCECProcessor::CanPersistConfiguration(void)
 {
-  switch (status)
-  {
-  case CEC_DECK_INFO_PLAY:
-    return "play";
-  case CEC_DECK_INFO_RECORD:
-    return "record";
-  case CEC_DECK_INFO_PLAY_REVERSE:
-    return "play reverse";
-  case CEC_DECK_INFO_STILL:
-    return "still";
-  case CEC_DECK_INFO_SLOW:
-    return "slow";
-  case CEC_DECK_INFO_SLOW_REVERSE:
-    return "slow reverse";
-  case CEC_DECK_INFO_FAST_FORWARD:
-    return "fast forward";
-  case CEC_DECK_INFO_FAST_REVERSE:
-    return "fast reverse";
-  case CEC_DECK_INFO_NO_MEDIA:
-    return "no media";
-  case CEC_DECK_INFO_STOP:
-    return "stop";
-  case CEC_DECK_INFO_SKIP_FORWARD_WIND:
-    return "info skip forward wind";
-  case CEC_DECK_INFO_SKIP_REVERSE_REWIND:
-    return "info skip reverse rewind";
-  case CEC_DECK_INFO_INDEX_SEARCH_FORWARD:
-    return "info index search forward";
-  case CEC_DECK_INFO_INDEX_SEARCH_REVERSE:
-    return "info index search reverse";
-  case CEC_DECK_INFO_OTHER_STATUS:
-    return "other";
-  case CEC_DECK_INFO_OTHER_STATUS_LG:
-    return "LG other";
-  default:
-    return "unknown";
-  }
+  return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false;
 }
 
-const char *CCECProcessor::ToString(const cec_opcode opcode)
+bool CCECProcessor::PersistConfiguration(const libcec_configuration &configuration)
 {
-  switch (opcode)
-  {
-  case CEC_OPCODE_ACTIVE_SOURCE:
-    return "active source";
-  case CEC_OPCODE_IMAGE_VIEW_ON:
-    return "image view on";
-  case CEC_OPCODE_TEXT_VIEW_ON:
-    return "text view on";
-  case CEC_OPCODE_INACTIVE_SOURCE:
-    return "inactive source";
-  case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
-    return "request active source";
-  case CEC_OPCODE_ROUTING_CHANGE:
-    return "routing change";
-  case CEC_OPCODE_ROUTING_INFORMATION:
-    return "routing information";
-  case CEC_OPCODE_SET_STREAM_PATH:
-    return "set stream path";
-  case CEC_OPCODE_STANDBY:
-    return "standby";
-  case CEC_OPCODE_RECORD_OFF:
-    return "record off";
-  case CEC_OPCODE_RECORD_ON:
-    return "record on";
-  case CEC_OPCODE_RECORD_STATUS:
-    return "record status";
-  case CEC_OPCODE_RECORD_TV_SCREEN:
-    return "record tv screen";
-  case CEC_OPCODE_CLEAR_ANALOGUE_TIMER:
-    return "clear analogue timer";
-  case CEC_OPCODE_CLEAR_DIGITAL_TIMER:
-    return "clear digital timer";
-  case CEC_OPCODE_CLEAR_EXTERNAL_TIMER:
-    return "clear external timer";
-  case CEC_OPCODE_SET_ANALOGUE_TIMER:
-    return "set analogue timer";
-  case CEC_OPCODE_SET_DIGITAL_TIMER:
-    return "set digital timer";
-  case CEC_OPCODE_SET_EXTERNAL_TIMER:
-    return "set external timer";
-  case CEC_OPCODE_SET_TIMER_PROGRAM_TITLE:
-    return "set timer program title";
-  case CEC_OPCODE_TIMER_CLEARED_STATUS:
-    return "timer cleared status";
-  case CEC_OPCODE_TIMER_STATUS:
-    return "timer status";
-  case CEC_OPCODE_CEC_VERSION:
-    return "cec version";
-  case CEC_OPCODE_GET_CEC_VERSION:
-    return "get cec version";
-  case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
-    return "give physical address";
-  case CEC_OPCODE_GET_MENU_LANGUAGE:
-    return "get menu language";
-  case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
-    return "report physical address";
-  case CEC_OPCODE_SET_MENU_LANGUAGE:
-    return "set menu language";
-  case CEC_OPCODE_DECK_CONTROL:
-    return "deck control";
-  case CEC_OPCODE_DECK_STATUS:
-    return "deck status";
-  case CEC_OPCODE_GIVE_DECK_STATUS:
-    return "give deck status";
-  case CEC_OPCODE_PLAY:
-    return "play";
-  case CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS:
-    return "give tuner status";
-  case CEC_OPCODE_SELECT_ANALOGUE_SERVICE:
-    return "select analogue service";
-  case CEC_OPCODE_SELECT_DIGITAL_SERVICE:
-    return "set digital service";
-  case CEC_OPCODE_TUNER_DEVICE_STATUS:
-    return "tuner device status";
-  case CEC_OPCODE_TUNER_STEP_DECREMENT:
-    return "tuner step decrement";
-  case CEC_OPCODE_TUNER_STEP_INCREMENT:
-    return "tuner step increment";
-  case CEC_OPCODE_DEVICE_VENDOR_ID:
-    return "device vendor id";
-  case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
-    return "give device vendor id";
-  case CEC_OPCODE_VENDOR_COMMAND:
-    return "vendor command";
-  case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
-    return "vendor command with id";
-  case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:
-    return "vendor remote button down";
-  case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:
-    return "vendor remote button up";
-  case CEC_OPCODE_SET_OSD_STRING:
-    return "set osd string";
-  case CEC_OPCODE_GIVE_OSD_NAME:
-    return "give osd name";
-  case CEC_OPCODE_SET_OSD_NAME:
-    return "set osd name";
-  case CEC_OPCODE_MENU_REQUEST:
-    return "menu request";
-  case CEC_OPCODE_MENU_STATUS:
-    return "menu status";
-  case CEC_OPCODE_USER_CONTROL_PRESSED:
-    return "user control pressed";
-  case CEC_OPCODE_USER_CONTROL_RELEASE:
-    return "user control release";
-  case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
-    return "give device power status";
-  case CEC_OPCODE_REPORT_POWER_STATUS:
-    return "report power status";
-  case CEC_OPCODE_FEATURE_ABORT:
-    return "feature abort";
-  case CEC_OPCODE_ABORT:
-    return "abort";
-  case CEC_OPCODE_GIVE_AUDIO_STATUS:
-    return "give audio status";
-  case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
-    return "give audio mode status";
-  case CEC_OPCODE_REPORT_AUDIO_STATUS:
-    return "report audio status";
-  case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
-    return "set system audio mode";
-  case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
-    return "system audio mode request";
-  case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:
-    return "system audio mode status";
-  case CEC_OPCODE_SET_AUDIO_RATE:
-    return "set audio rate";
-  case CEC_OPCODE_NONE:
-    return "poll";
-  default:
-    return "UNKNOWN";
-  }
+  return m_communication ? m_communication->PersistConfiguration(configuration) : false;
 }
 
-const char *CCECProcessor::ToString(const cec_system_audio_status mode)
+void CCECProcessor::RescanActiveDevices(void)
 {
-  switch(mode)
-  {
-  case CEC_SYSTEM_AUDIO_STATUS_ON:
-    return "on";
-  case CEC_SYSTEM_AUDIO_STATUS_OFF:
-    return "off";
-  default:
-    return "unknown";
-  }
+  for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
+    it->second->GetStatus(true);
 }
 
-const char *CCECProcessor::ToString(const cec_audio_status UNUSED(status))
+bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
 {
-  // TODO this is a mask
-  return "TODO";
-}
+  if (!OpenConnection(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs, false))
+    return false;
 
-const char *CCECProcessor::ToString(const cec_vendor_id vendor)
-{
-  switch (vendor)
-  {
-  case CEC_VENDOR_SAMSUNG:
-    return "Samsung";
-  case CEC_VENDOR_LG:
-    return "LG";
-  case CEC_VENDOR_PANASONIC:
-    return "Panasonic";
-  case CEC_VENDOR_PIONEER:
-    return "Pioneer";
-  case CEC_VENDOR_ONKYO:
-    return "Onkyo";
-  case CEC_VENDOR_YAMAHA:
-    return "Yamaha";
-  case CEC_VENDOR_PHILIPS:
-    return "Philips";
-  case CEC_VENDOR_SONY:
-    return "Sony";
-  case CEC_VENDOR_TOSHIBA:
-    return "Toshiba";
-  default:
-    return "Unknown";
-  }
-}
+  config->iFirmwareVersion   = m_communication->GetFirmwareVersion();
+  config->iPhysicalAddress   = m_communication->GetPhysicalAddress();
+  config->iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
 
-const char *CCECProcessor::ToString(const cec_client_version version)
-{
-  switch (version)
-  {
-  case CEC_CLIENT_VERSION_PRE_1_5:
-    return "pre-1.5";
-  case CEC_CLIENT_VERSION_1_5_0:
-    return "1.5.0";
-  case CEC_CLIENT_VERSION_1_5_1:
-    return "1.5.1";
-  case CEC_CLIENT_VERSION_1_5_2:
-    return "1.5.2";
-  case CEC_CLIENT_VERSION_1_5_3:
-    return "1.5.3";
-  case CEC_CLIENT_VERSION_1_6_0:
-    return "1.6.0";
-  case CEC_CLIENT_VERSION_1_6_1:
-    return "1.6.1";
-  case CEC_CLIENT_VERSION_1_6_2:
-    return "1.6.2";
-  default:
-    return "Unknown";
-  }
+  return true;
 }
 
-const char *CCECProcessor::ToString(const cec_server_version version)
+bool CCECProcessor::TransmitPendingActiveSourceCommands(void)
 {
-  switch (version)
-  {
-  case CEC_SERVER_VERSION_PRE_1_5:
-    return "pre-1.5";
-  case CEC_SERVER_VERSION_1_5_0:
-    return "1.5.0";
-  case CEC_SERVER_VERSION_1_5_1:
-    return "1.5.1";
-  case CEC_SERVER_VERSION_1_5_2:
-    return "1.5.2";
-  case CEC_SERVER_VERSION_1_5_3:
-    return "1.5.3";
-  case CEC_SERVER_VERSION_1_6_0:
-    return "1.6.0";
-  case CEC_SERVER_VERSION_1_6_1:
-    return "1.6.1";
-  case CEC_SERVER_VERSION_1_6_2:
-    return "1.6.2";
-  default:
-    return "Unknown";
-  }
+  bool bReturn(true);
+  for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
+    bReturn &= it->second->TransmitPendingActiveSourceCommands();
+  return bReturn;
 }
 
-bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */)
+CCECTV *CCECProcessor::GetTV(void) const
 {
-  bool bReturn(false);
-  if (!m_communication && strPort)
-  {
-    IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort);
-    CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT);
-    int iConnectTry(0);
-    while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
-    {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
-      comm->Close();
-      Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
-    }
-    if (comm->IsOpen())
-    {
-      bReturn = comm->StartBootloader();
-      delete comm;
-    }
-    return bReturn;
-  }
-  else
-  {
-    m_communication->StartBootloader();
-    Close();
-    bReturn = true;
-  }
-
-  return bReturn;
+  return CCECBusDevice::AsTV(m_busDevices->At(CECDEVICE_TV));
 }
 
-bool CCECProcessor::PingAdapter(void)
+CCECAudioSystem *CCECProcessor::GetAudioSystem(void) const
 {
-  return m_communication->PingAdapter();
+  return CCECBusDevice::AsAudioSystem(m_busDevices->At(CECDEVICE_AUDIOSYSTEM));
 }
 
-void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination)
+CCECPlaybackDevice *CCECProcessor::GetPlaybackDevice(cec_logical_address address) const
 {
-  if (destination < CECDEVICE_BROADCAST)
-    m_busDevices[destination]->HandlePollFrom(initiator);
+  return CCECBusDevice::AsPlaybackDevice(m_busDevices->At(address));
 }
 
-bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator)
+CCECRecordingDevice *CCECProcessor::GetRecordingDevice(cec_logical_address address) const
 {
-  return !m_busDevices[initiator]->HandleReceiveFailed();
+  return CCECBusDevice::AsRecordingDevice(m_busDevices->At(address));
 }
 
-bool CCECProcessor::SetStreamPath(uint16_t iPhysicalAddress)
+CCECTuner *CCECProcessor::GetTuner(cec_logical_address address) const
 {
-  // stream path changes are sent by the TV
-  return m_busDevices[CECDEVICE_TV]->GetHandler()->TransmitSetStreamPath(iPhysicalAddress);
+  return CCECBusDevice::AsTuner(m_busDevices->At(address));
 }
 
-bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
+bool CCECProcessor::RegisterClient(CCECClient *client)
 {
-  bool bReinit(false);
-  CCECBusDevice *primary = IsRunning() ? GetPrimaryDevice() : NULL;
-  cec_device_type oldPrimaryType = primary ? primary->GetType() : CEC_DEVICE_TYPE_RECORDING_DEVICE;
-  m_configuration.clientVersion  = configuration->clientVersion;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString((cec_client_version)configuration->clientVersion));
-
-  // client version 1.5.0
+  if (!client)
+    return false;
 
-  // device types
-  bool bDeviceTypeChanged = IsRunning () && m_configuration.deviceTypes != configuration->deviceTypes;
-  m_configuration.deviceTypes = configuration->deviceTypes;
-  if (bDeviceTypeChanged)
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(configuration->deviceTypes[0]));
+  libcec_configuration &configuration = *client->GetConfiguration();
 
-  bool bPhysicalAddressChanged(false);
+  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3 && configuration.bMonitorOnly == 1)
+    return true;
 
-  // autodetect address
-  bool bPhysicalAutodetected(false);
-  if (IsRunning() && configuration->bAutodetectAddress == 1)
+  if (!CECInitialised())
   {
-    uint16_t iPhysicalAddress = m_communication->GetPhysicalAddress();
-    if (IsValidPhysicalAddress(iPhysicalAddress))
-    {
-      if (IsRunning())
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
-      else
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using physical address '%04X'", __FUNCTION__, iPhysicalAddress);
-      bPhysicalAddressChanged = (m_configuration.iPhysicalAddress != iPhysicalAddress);
-      m_configuration.iPhysicalAddress = iPhysicalAddress;
-      m_configuration.iHDMIPort        = CEC_HDMI_PORTNUMBER_NONE;
-      m_configuration.baseDevice       = CECDEVICE_UNKNOWN;
-      bPhysicalAutodetected            = true;
-    }
+    m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: CEC processor is not initialised");
+    return false;
   }
 
-  // physical address
-  if (!bPhysicalAutodetected)
-  {
-    uint16_t iPhysicalAddress(IsValidPhysicalAddress(configuration->iPhysicalAddress) ? configuration->iPhysicalAddress : CEC_PHYSICAL_ADDRESS_TV);
-    bPhysicalAddressChanged = IsRunning() && m_configuration.iPhysicalAddress != iPhysicalAddress;
-    if (bPhysicalAddressChanged)
-    {
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - physical address '%04X'", __FUNCTION__, iPhysicalAddress);
-      m_configuration.iPhysicalAddress = iPhysicalAddress;
-    }
-  }
+  // unregister the client first if it's already been marked as registered
+  if (client->IsRegistered())
+    UnregisterClient(client);
 
-  bool bHdmiPortChanged(false);
-  if (!bPhysicalAutodetected && !IsValidPhysicalAddress(configuration->iPhysicalAddress))
-  {
-    // base device
-    bHdmiPortChanged = IsRunning() && m_configuration.baseDevice != configuration->baseDevice;
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using base device '%x'", __FUNCTION__, (int)configuration->baseDevice);
-    m_configuration.baseDevice = configuration->baseDevice;
-
-    // hdmi port
-    bHdmiPortChanged |= IsRunning() && m_configuration.iHDMIPort != configuration->iHDMIPort;
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using HDMI port '%d'", __FUNCTION__, configuration->iHDMIPort);
-    m_configuration.iHDMIPort = configuration->iHDMIPort;
-  }
-  else
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
-    m_configuration.baseDevice = CECDEVICE_UNKNOWN;
-    m_configuration.iHDMIPort  = CEC_HDMI_PORTNUMBER_NONE;
-  }
+  // get the configuration from the client
+  m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion));
 
-  bReinit = bPhysicalAddressChanged || bHdmiPortChanged || bDeviceTypeChanged;
+  // mark as uninitialised and unregistered
+  client->SetRegistered(false);
+  client->SetInitialised(false);
 
-  // device name
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, configuration->strDeviceName);
-  snprintf(m_configuration.strDeviceName, 13, "%s", configuration->strDeviceName);
-  if (primary && !primary->GetOSDName().Equals(m_configuration.strDeviceName))
+  // get the current ackmask, so we can restore it if polling fails
+  uint16_t iPreviousMask(m_communication->GetAckMask());
+
+  // find logical addresses for this client
+  if (!client->AllocateLogicalAddresses())
   {
-    primary->SetOSDName(m_configuration.strDeviceName);
-    if (!bReinit && IsRunning())
-      primary->TransmitOSDName(CECDEVICE_TV);
+    m_libcec->AddLog(CEC_LOG_ERROR, "failed to register the new CEC client - cannot allocate the requested device types");
+    SetAckMask(iPreviousMask);
+    return false;
   }
 
-  // tv vendor id override
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString((cec_vendor_id)configuration->tvVendor));
-  if (m_configuration.tvVendor != configuration->tvVendor)
+  // register this client on the new addresses
+  CECDEVICEVEC devices;
+  m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses);
+  for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
   {
-    m_configuration.tvVendor= configuration->tvVendor;
-    m_busDevices[CECDEVICE_TV]->SetVendorId((uint64_t)m_configuration.tvVendor);
+    // replace a previous client
+    CLockObject lock(m_mutex);
+    m_clients.erase((*it)->GetLogicalAddress());
+    m_clients.insert(make_pair<cec_logical_address, CCECClient *>((*it)->GetLogicalAddress(), client));
   }
 
-  // wake CEC devices
-  if (m_configuration.wakeDevices != configuration->wakeDevices)
+  // get the settings from the rom
+  if (configuration.bGetSettingsFromROM == 1)
   {
-    m_configuration.wakeDevices = configuration->wakeDevices;
-    if (!bReinit && IsRunning())
-      PowerOnDevices();
-  }
+    libcec_configuration config;
+    m_communication->GetConfiguration(config);
 
-  // just copy these
-  m_configuration.bUseTVMenuLanguage   = configuration->bUseTVMenuLanguage;
-  m_configuration.bActivateSource      = configuration->bActivateSource;
-  m_configuration.bGetSettingsFromROM  = configuration->bGetSettingsFromROM;
-  m_configuration.powerOffDevices      = configuration->powerOffDevices;
-  m_configuration.bPowerOffScreensaver = configuration->bPowerOffScreensaver;
-  m_configuration.bPowerOffOnStandby   = configuration->bPowerOffOnStandby;
+    CLockObject lock(m_mutex);
+    if (!config.deviceTypes.IsEmpty())
+      configuration.deviceTypes = config.deviceTypes;
+    if (CLibCEC::IsValidPhysicalAddress(config.iPhysicalAddress))
+      configuration.iPhysicalAddress = config.iPhysicalAddress;
+    snprintf(configuration.strDeviceName, 13, "%s", config.strDeviceName);
+  }
 
-  // client version 1.5.1
-  if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1)
-    m_configuration.bSendInactiveSource = configuration->bSendInactiveSource;
+  // set the firmware version and build date
+  configuration.serverVersion      = LIBCEC_VERSION_CURRENT;
+  configuration.iFirmwareVersion   = m_communication->GetFirmwareVersion();
+  configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
 
-  // client version 1.6.0
-  if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0)
-  {
-    m_configuration.bPowerOffDevicesOnStandby = configuration->bPowerOffDevicesOnStandby;
-    m_configuration.bShutdownOnStandby        = configuration->bShutdownOnStandby;
-  }
+  // mark the client as registered
+  client->SetRegistered(true);
 
-  // client version 1.6.2
-  if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2)
-  {
-    memcpy(m_configuration.strDeviceLanguage, configuration->strDeviceLanguage, 3);
-  }
+  // set the new ack mask
+  bool bReturn = SetAckMask(GetLogicalAddresses().AckMask()) &&
+      // and initialise the client
+      client->OnRegister();
 
-  // ensure that there is at least 1 device type set
-  if (m_configuration.deviceTypes.IsEmpty())
-    m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
+  // log the new registration
+  CStdString strLog;
+  strLog.Format("%s: %s", bReturn ? "CEC client registered" : "failed to register the CEC client", client->GetConnectionInfo().c_str());
+  m_libcec->AddLog(bReturn ? CEC_LOG_NOTICE : CEC_LOG_ERROR, strLog);
 
-  bool bReturn(true);
-  if (bReinit || m_configuration.logicalAddresses.IsEmpty())
-  {
-    if (bDeviceTypeChanged)
-      bReturn = ChangeDeviceType(oldPrimaryType, m_configuration.deviceTypes[0]);
-    else if (IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
-      bReturn = SetPhysicalAddress(m_configuration.iPhysicalAddress);
-    else if (m_configuration.baseDevice != CECDEVICE_UNKNOWN && m_configuration.iHDMIPort != CEC_HDMI_PORTNUMBER_NONE)
-      bReturn = SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort);
-  }
-  else if (m_configuration.bActivateSource == 1 && IsRunning() && !IsActiveSource(m_configuration.logicalAddresses.primary))
+  // display a warning if the firmware can be upgraded
+  if (bReturn && !IsRunningLatestFirmware())
   {
-    // activate the source if we're not already the active source
-    SetActiveSource(m_configuration.deviceTypes.types[0]);
+    const char *strUpgradeMessage = "The firmware of this adapter can be upgraded. Please visit http://blog.pulse-eight.com/ for more information.";
+    m_libcec->AddLog(CEC_LOG_WARNING, strUpgradeMessage);
+    libcec_parameter param;
+    param.paramData = (void*)strUpgradeMessage; param.paramType = CEC_PARAMETER_TYPE_STRING;
+    client->Alert(CEC_ALERT_SERVICE_DEVICE, param);
   }
 
-  // persist the configuration
-  if (IsRunning())
-    m_communication->PersistConfiguration(&m_configuration);
-
   return bReturn;
 }
 
-bool CCECProcessor::GetCurrentConfiguration(libcec_configuration *configuration)
+bool CCECProcessor::UnregisterClient(CCECClient *client)
 {
-  // client version 1.5.0
-  snprintf(configuration->strDeviceName, 13, "%s", m_configuration.strDeviceName);
-  configuration->deviceTypes          = m_configuration.deviceTypes;
-  configuration->bAutodetectAddress   = m_configuration.bAutodetectAddress;
-  configuration->iPhysicalAddress     = m_configuration.iPhysicalAddress;
-  configuration->baseDevice           = m_configuration.baseDevice;
-  configuration->iHDMIPort            = m_configuration.iHDMIPort;
-  configuration->clientVersion        = m_configuration.clientVersion;
-  configuration->serverVersion        = m_configuration.serverVersion;
-  configuration->tvVendor             = m_configuration.tvVendor;
-
-  configuration->bGetSettingsFromROM  = m_configuration.bGetSettingsFromROM;
-  configuration->bUseTVMenuLanguage   = m_configuration.bUseTVMenuLanguage;
-  configuration->bActivateSource      = m_configuration.bActivateSource;
-  configuration->wakeDevices          = m_configuration.wakeDevices;
-  configuration->powerOffDevices      = m_configuration.powerOffDevices;
-  configuration->bPowerOffScreensaver = m_configuration.bPowerOffScreensaver;
-  configuration->bPowerOffOnStandby   = m_configuration.bPowerOffOnStandby;
-
-  // client version 1.5.1
-  if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1)
-    configuration->bSendInactiveSource = m_configuration.bSendInactiveSource;
-
-  // client version 1.5.3
-  if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_3)
-    configuration->logicalAddresses    = m_configuration.logicalAddresses;
-
-  // client version 1.6.0
-  if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0)
-  {
-    configuration->iFirmwareVersion          = m_configuration.iFirmwareVersion;
-    configuration->bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby;
-    configuration->bShutdownOnStandby        = m_configuration.bShutdownOnStandby;
-  }
+  if (!client)
+    return false;
+
+  if (client->IsRegistered())
+    m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str());
+
+  // notify the client that it will be unregistered
+  client->OnUnregister();
 
-  // client version 1.6.2
-  if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2)
   {
-    memcpy(configuration->strDeviceLanguage, m_configuration.strDeviceLanguage, 3);
-    configuration->iFirmwareBuildDate      = m_configuration.iFirmwareBuildDate;
+    CLockObject lock(m_mutex);
+    // find all devices that match the LA's of this client
+    CECDEVICEVEC devices;
+    m_busDevices->GetByLogicalAddresses(devices, client->GetConfiguration()->logicalAddresses);
+    for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
+    {
+      // find the client
+      map<cec_logical_address, CCECClient *>::iterator entry = m_clients.find((*it)->GetLogicalAddress());
+      // unregister the client
+      if (entry != m_clients.end())
+        m_clients.erase(entry);
+
+      // reset the device status
+      (*it)->ResetDeviceStatus();
+    }
   }
-  return true;
+
+  // set the new ackmask
+  return SetAckMask(GetLogicalAddresses().AckMask());
 }
 
-bool CCECProcessor::CanPersistConfiguration(void)
+void CCECProcessor::UnregisterClients(void)
 {
-  return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false;
+  m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering all CEC clients");
+
+  vector<CCECClient *> clients = m_libcec->GetClients();
+  for (vector<CCECClient *>::iterator client = clients.begin(); client != clients.end(); client++)
+    UnregisterClient(*client);
+
+  CLockObject lock(m_mutex);
+  m_clients.clear();
 }
 
-bool CCECProcessor::PersistConfiguration(libcec_configuration *configuration)
+CCECClient *CCECProcessor::GetClient(const cec_logical_address address)
 {
-  return m_communication ? m_communication->PersistConfiguration(configuration) : false;
+  CLockObject lock(m_mutex);
+  map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.find(address);
+  if (client != m_clients.end())
+    return client->second;
+  return NULL;
 }
 
-void CCECProcessor::RescanActiveDevices(void)
+CCECClient *CCECProcessor::GetPrimaryClient(void)
 {
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-    m_busDevices[iPtr]->GetStatus(true);
+  CLockObject lock(m_mutex);
+  map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.begin();
+  if (client != m_clients.end())
+    return client->second;
+  return NULL;
 }
 
-bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
+CCECBusDevice *CCECProcessor::GetPrimaryDevice(void)
 {
-  if (!OpenConnection(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs, false))
-    return false;
+  return m_busDevices->At(GetLogicalAddress());
+}
 
-  config->iFirmwareVersion   = m_communication->GetFirmwareVersion();
-  config->iPhysicalAddress   = m_communication->GetPhysicalAddress();
-  config->iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
+cec_logical_address CCECProcessor::GetLogicalAddress(void)
+{
+  cec_logical_addresses addresses = GetLogicalAddresses();
+  return addresses.primary;
+}
 
-  return true;
+cec_logical_addresses CCECProcessor::GetLogicalAddresses(void)
+{
+  CLockObject lock(m_mutex);
+  cec_logical_addresses addresses;
+  addresses.Clear();
+  for (map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.begin(); client != m_clients.end(); client++)
+    addresses.Set(client->first);
+
+  return addresses;
 }
 
-bool CCECProcessor::TransmitPendingActiveSourceCommands(void)
+bool CCECProcessor::IsHandledByLibCEC(const cec_logical_address address) const
 {
-  bool bReturn(true);
-  for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
-    bReturn &= m_busDevices[iPtr]->TransmitPendingActiveSourceCommands();
-  return bReturn;
+  CCECBusDevice *device = GetDevice(address);
+  return device && device->IsHandledByLibCEC();
 }
 
-bool CCECProcessor::IsValidPhysicalAddress(uint16_t iPhysicalAddress)
+bool CCECProcessor::IsRunningLatestFirmware(void)
 {
-  return iPhysicalAddress >= CEC_MIN_PHYSICAL_ADDRESS &&
-         iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS;
+  return m_communication && m_communication->IsOpen() ?
+      m_communication->IsRunningLatestFirmware() :
+      true;
 }
index 3986862fe719f3f2730c56bd5cb1840cd700d4af..5f496d2631558506045e645ecf450e4ba37c5995 100644 (file)
 
 #include <string>
 #include "../../include/cectypes.h"
+
 #include "platform/threads/threads.h"
 #include "platform/util/buffer.h"
+
 #include "adapter/AdapterCommunication.h"
+#include "devices/CECDeviceMap.h"
+#include "CECInputBuffer.h"
 
 namespace CEC
 {
   class CLibCEC;
   class IAdapterCommunication;
   class CCECBusDevice;
-
-  // a buffer that priotises the input from the TV.
-  // if we need more than this, we'll have to change it into a priority_queue
-  class CCECInputBuffer
-  {
-  public:
-    CCECInputBuffer(void) : m_bHasData(false) {}
-    virtual ~CCECInputBuffer(void)
-    {
-      m_condition.Broadcast();
-    }
-
-    bool Push(const cec_command &command)
-    {
-      bool bReturn(false);
-      PLATFORM::CLockObject lock(m_mutex);
-      if (command.initiator == CECDEVICE_TV)
-        bReturn = m_tvInBuffer.Push(command);
-      else
-        bReturn = m_inBuffer.Push(command);
-
-      m_bHasData |= bReturn;
-      if (m_bHasData)
-        m_condition.Signal();
-
-      return bReturn;
-    }
-
-    bool Pop(cec_command &command, uint16_t iTimeout)
-    {
-      bool bReturn(false);
-      PLATFORM::CLockObject lock(m_mutex);
-      if (m_tvInBuffer.IsEmpty() && m_inBuffer.IsEmpty() &&
-          !m_condition.Wait(m_mutex, m_bHasData, iTimeout))
-        return bReturn;
-
-      if (m_tvInBuffer.Pop(command))
-        bReturn = true;
-      else if (m_inBuffer.Pop(command))
-        bReturn = true;
-
-      m_bHasData = !m_tvInBuffer.IsEmpty() || !m_inBuffer.IsEmpty();
-      return bReturn;
-    }
-
-  private:
-    PLATFORM::CMutex                    m_mutex;
-    PLATFORM::CCondition<volatile bool> m_condition;
-    volatile bool                       m_bHasData;
-    PLATFORM::SyncedBuffer<cec_command> m_tvInBuffer;
-    PLATFORM::SyncedBuffer<cec_command> m_inBuffer;
-  };
+  class CCECAudioSystem;
+  class CCECPlaybackDevice;
+  class CCECRecordingDevice;
+  class CCECTuner;
+  class CCECTV;
+  class CCECClient;
 
   class CCECProcessor : public PLATFORM::CThread, public IAdapterCommunicationCallback
   {
     public:
-      CCECProcessor(CLibCEC *controller, const char *strDeviceName, const cec_device_type_list &types, uint16_t iPhysicalAddress);
-      CCECProcessor(CLibCEC *controller, libcec_configuration *configuration);
+      CCECProcessor(CLibCEC *libcec);
       virtual ~CCECProcessor(void);
 
       bool Start(const char *strPort, uint16_t iBaudRate = CEC_SERIAL_DEFAULT_BAUDRATE, uint32_t iTimeoutMs = CEC_DEFAULT_CONNECT_TIMEOUT);
       void *Process(void);
       void Close(void);
 
+      bool RegisterClient(CCECClient *client);
+      bool UnregisterClient(CCECClient *client);
+      void UnregisterClients(void);
+      CCECClient *GetPrimaryClient(void);
+      CCECClient *GetClient(const cec_logical_address address);
+
       bool                  OnCommandReceived(const cec_command &command);
 
-      bool                  IsMonitoring(void) const { return m_bMonitor; }
+      CCECBusDevice *       GetDevice(cec_logical_address address) const;
+      CCECAudioSystem *     GetAudioSystem(void) const;
+      CCECPlaybackDevice *  GetPlaybackDevice(cec_logical_address address) const;
+      CCECRecordingDevice * GetRecordingDevice(cec_logical_address address) const;
+      CCECTuner *           GetTuner(cec_logical_address address) const;
+      CCECTV *              GetTV(void) const;
+
       CCECBusDevice *       GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate = true);
-      CCECBusDevice *       GetDeviceByType(cec_device_type type) const;
-      CCECBusDevice *       GetPrimaryDevice(void) const;
-      cec_version           GetDeviceCecVersion(cec_logical_address iAddress);
-      bool                  GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language);
-      CStdString            GetDeviceName(void) const;
-      cec_osd_name          GetDeviceOSDName(cec_logical_address iAddress);
-      uint64_t              GetDeviceVendorId(cec_logical_address iAddress);
-      cec_power_status      GetDevicePowerStatus(cec_logical_address iAddress);
-      cec_logical_address   GetLogicalAddress(void) const { return m_configuration.logicalAddresses.primary; }
-      cec_logical_addresses GetLogicalAddresses(void) const { return m_configuration.logicalAddresses; }
-      cec_logical_addresses GetActiveDevices(void);
-      uint16_t              GetDevicePhysicalAddress(cec_logical_address iAddress);
-      bool                  HasLogicalAddress(cec_logical_address address) const { return m_configuration.logicalAddresses.IsSet(address); }
+      CCECBusDevice *       GetPrimaryDevice(void);
+      cec_logical_address   GetLogicalAddress(void);
+      cec_logical_addresses GetLogicalAddresses(void);
       bool                  IsPresentDevice(cec_logical_address address);
       bool                  IsPresentDeviceType(cec_device_type type);
-      uint16_t              GetPhysicalAddress(void) const;
+      uint16_t              GetDetectedPhysicalAddress(void) const;
       uint64_t              GetLastTransmission(void) const { return m_iLastTransmission; }
       cec_logical_address   GetActiveSource(bool bRequestActiveSource = true);
       bool                  IsActiveSource(cec_logical_address iAddress);
-      bool                  IsInitialised(void);
+      bool                  CECInitialised(void);
       bool                  SetStreamPath(uint16_t iPhysicalAddress);
-      cec_client_version    GetClientVersion(void) const { return (cec_client_version)m_configuration.clientVersion; };
-      bool                  StandbyDevices(cec_logical_address address = CECDEVICE_BROADCAST);
-      bool                  PowerOnDevices(cec_logical_address address = CECDEVICE_BROADCAST);
 
-      bool SetActiveView(void);
-      bool SetActiveSource(cec_device_type type = CEC_DEVICE_TYPE_RESERVED);
-      bool SetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate = true);
+      bool                  StandbyDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices);
+      bool                  StandbyDevice(const cec_logical_address initiator, cec_logical_address address);
+      bool                  PowerOnDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices);
+      bool                  PowerOnDevice(const cec_logical_address initiator, cec_logical_address address);
+
       bool SetDeckInfo(cec_deck_info info, bool bSendUpdate = true);
-      bool SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce = false);
-      bool TransmitInactiveSource(void);
-      bool SetLogicalAddress(cec_logical_address iLogicalAddress);
-      bool SetMenuState(cec_menu_state state, bool bSendUpdate = true);
-      bool SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpdate = true);
-      bool SetActiveSource(uint16_t iStreamPath);
-      bool SwitchMonitoring(bool bEnable);
+      bool ActivateSource(uint16_t iStreamPath);
       bool PollDevice(cec_logical_address iAddress);
-      uint8_t VolumeUp(bool bSendRelease = true);
-      uint8_t VolumeDown(bool bSendRelease = true);
-      uint8_t MuteAudio(bool bSendRelease = true);
-      bool TransmitKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait = true);
-      bool TransmitKeyRelease(cec_logical_address iDestination, bool bWait = true);
-      bool EnablePhysicalAddressDetection(void);
       void SetStandardLineTimeout(uint8_t iTimeout);
+      uint8_t GetStandardLineTimeout(void);
       void SetRetryLineTimeout(uint8_t iTimeout);
-      bool GetCurrentConfiguration(libcec_configuration *configuration);
-      bool SetConfiguration(const libcec_configuration *configuration);
+      uint8_t GetRetryLineTimeout(void);
       bool CanPersistConfiguration(void);
-      bool PersistConfiguration(libcec_configuration *configuration);
+      bool PersistConfiguration(const libcec_configuration &configuration);
       void RescanActiveDevices(void);
 
       bool SetLineTimeout(uint8_t iTimeout);
 
-      const char *ToString(const cec_device_type type);
-      const char *ToString(const cec_menu_state state);
-      const char *ToString(const cec_version version);
-      const char *ToString(const cec_power_status status);
-      const char *ToString(const cec_logical_address address);
-      const char *ToString(const cec_deck_control_mode mode);
-      const char *ToString(const cec_deck_info status);
-      const char *ToString(const cec_opcode opcode);
-      const char *ToString(const cec_system_audio_status mode);
-      const char *ToString(const cec_audio_status status);
-      const char *ToString(const cec_vendor_id vendor);
-      const char *ToString(const cec_client_version version);
-      const char *ToString(const cec_server_version version);
-
-      static bool IsValidPhysicalAddress(uint16_t iPhysicalAddress);
-
       bool Transmit(const cec_command &data);
-      void TransmitAbort(cec_logical_address address, cec_opcode opcode, cec_abort_reason reason = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE);
-
-      bool ChangeDeviceType(cec_device_type from, cec_device_type to);
-      bool FindLogicalAddresses(void);
-      bool SetAckMask(uint16_t iMask);
+      void TransmitAbort(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_abort_reason reason = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE);
 
       bool StartBootloader(const char *strPort = NULL);
       bool PingAdapter(void);
@@ -195,37 +121,36 @@ namespace CEC
 
       bool TransmitPendingActiveSourceCommands(void);
 
-      CCECBusDevice *  m_busDevices[16];
+      CCECDeviceMap *GetDevices(void) const { return m_busDevices; }
+      CLibCEC *GetLib(void) const { return m_libcec; }
+
+      bool IsHandledByLibCEC(const cec_logical_address address) const;
 
+      bool TryLogicalAddress(cec_logical_address address);
+
+      bool IsRunningLatestFirmware(void);
   private:
       bool OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening = true);
-      bool Initialise(void);
-      void SetInitialised(bool bSetTo = true);
-      void CreateBusDevices(void);
+      void SetCECInitialised(bool bSetTo = true);
 
       void ReplaceHandlers(void);
       bool PhysicalAddressInUse(uint16_t iPhysicalAddress);
-      bool TryLogicalAddress(cec_logical_address address);
-      bool FindLogicalAddressRecordingDevice(void);
-      bool FindLogicalAddressTuner(void);
-      bool FindLogicalAddressPlaybackDevice(void);
-      bool FindLogicalAddressAudioSystem(void);
+      bool SetAckMask(uint16_t iMask);
 
       void LogOutput(const cec_command &data);
-      void ParseCommand(const cec_command &command);
-
-      bool                                m_bConnectionOpened;
-      bool                                m_bInitialised;
-      PLATFORM::CMutex                    m_mutex;
-      IAdapterCommunication *             m_communication;
-      CLibCEC*                            m_controller;
-      bool                                m_bMonitor;
-      cec_keypress                        m_previousKey;
-      PLATFORM::CThread *                 m_busScan;
-      uint8_t                             m_iStandardLineTimeout;
-      uint8_t                             m_iRetryLineTimeout;
-      uint64_t                            m_iLastTransmission;
-      CCECInputBuffer                     m_inBuffer;
-      libcec_configuration                m_configuration;
+      void ProcessCommand(const cec_command &command);
+
+      void ResetMembers(void);
+
+      bool                                        m_bInitialised;
+      PLATFORM::CMutex                            m_mutex;
+      IAdapterCommunication *                     m_communication;
+      CLibCEC*                                    m_libcec;
+      uint8_t                                     m_iStandardLineTimeout;
+      uint8_t                                     m_iRetryLineTimeout;
+      uint64_t                                    m_iLastTransmission;
+      CCECInputBuffer                             m_inBuffer;
+      CCECDeviceMap *                             m_busDevices;
+      std::map<cec_logical_address, CCECClient *> m_clients;
   };
 };
index 88e93e0dcd07a2d3ae2c366c46d068219b8faa54..b64cdc709ad6ab22f7a7eb17dac3c2d264e24896 100644 (file)
 #include "adapter/USBCECAdapterDetection.h"
 #include "adapter/USBCECAdapterCommunication.h"
 #include "CECProcessor.h"
+#include "devices/CECAudioSystem.h"
 #include "devices/CECBusDevice.h"
+#include "devices/CECPlaybackDevice.h"
+#include "devices/CECTV.h"
 #include "platform/util/timeutils.h"
 #include "platform/util/StdString.h"
 
+#include "CECClient.h"
+
 using namespace std;
 using namespace CEC;
 using namespace PLATFORM;
 
-CLibCEC::CLibCEC(const char *strDeviceName, cec_device_type_list types, uint16_t iPhysicalAddress /* = 0 */) :
+CLibCEC::CLibCEC(const char *UNUSED(strDeviceName), cec_device_type_list UNUSED(types), uint16_t UNUSED(iPhysicalAddress) /* = 0 */) :
     m_iStartTime(GetTimeMs()),
-    m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
-    m_buttontime(0),
-    m_callbacks(NULL),
-    m_cbParam(NULL)
+    m_client(NULL)
 {
-  m_cec = new CCECProcessor(this, strDeviceName, types, iPhysicalAddress);
+  m_cec = new CCECProcessor(this);
 }
 
-CLibCEC::CLibCEC(libcec_configuration *configuration) :
+CLibCEC::CLibCEC(libcec_configuration *UNUSED(configuration)) :
     m_iStartTime(GetTimeMs()),
-    m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
-    m_buttontime(0),
-    m_callbacks(configuration->callbacks),
-    m_cbParam(configuration->callbackParam)
+    m_client(NULL)
 {
-  m_cec = new CCECProcessor(this, configuration);
+  m_cec = new CCECProcessor(this);
 }
 
 CLibCEC::~CLibCEC(void)
 {
+  // unregister all clients client
+  UnregisterClients();
+
+  // delete the adapter connection
   delete m_cec;
+  m_cec = NULL;
 }
 
 bool CLibCEC::Open(const char *strPort, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
 {
-  if (m_cec->IsRunning())
-  {
-    AddLog(CEC_LOG_ERROR, "connection already open");
+  if (!m_cec || !strPort)
     return false;
-  }
 
+  // open a new connection
   if (!m_cec->Start(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs))
   {
     AddLog(CEC_LOG_ERROR, "could not start CEC communications");
     return false;
   }
 
+  // register all clients
+  for (vector<CCECClient *>::iterator it = m_clients.begin(); it != m_clients.end(); it++)
+  {
+    if (!m_cec->RegisterClient(*it))
+    {
+      AddLog(CEC_LOG_ERROR, "failed to register a CEC client");
+      return false;
+    }
+  }
+
   return true;
 }
 
 void CLibCEC::Close(void)
 {
+  // unregister all clients
+  UnregisterClients();
+
+  // close the connection
   if (m_cec)
     m_cec->Close();
 }
 
-bool CLibCEC::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
+int8_t CLibCEC::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */)
 {
-  CLockObject lock(m_mutex);
-  if (m_cec)
-  {
-    m_cbParam   = cbParam;
-    m_callbacks = callbacks;
-  }
-  return false;
+  return CUSBCECAdapterDetection::FindAdapters(deviceList, iBufSize, strDevicePath);
 }
 
-int8_t CLibCEC::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */)
+bool CLibCEC::StartBootloader(void)
 {
-  CStdString strDebug;
-  if (strDevicePath)
-    strDebug.Format("trying to autodetect the com port for device path '%s'", strDevicePath);
-  else
-    strDebug.Format("trying to autodetect all CEC adapters");
-  AddLog(CEC_LOG_DEBUG, strDebug);
-
-  return CUSBCECAdapterDetection::FindAdapters(deviceList, iBufSize, strDevicePath);
+  return m_cec ? m_cec->StartBootloader() : false;
 }
 
 bool CLibCEC::PingAdapter(void)
 {
-  return m_cec ? m_cec->PingAdapter() : false;
+  return m_client ? m_client->PingAdapter() : false;
 }
 
-bool CLibCEC::StartBootloader(void)
+bool CLibCEC::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
 {
-  return m_cec ? m_cec->StartBootloader() : false;
+  return m_client ? m_client->EnableCallbacks(cbParam, callbacks) : false;
 }
 
-bool CLibCEC::GetNextLogMessage(cec_log_message *message)
+bool CLibCEC::GetCurrentConfiguration(libcec_configuration *configuration)
 {
-  return (m_logBuffer.Pop(*message));
+  return m_client ? m_client->GetCurrentConfiguration(*configuration) : false;
 }
 
-bool CLibCEC::GetNextKeypress(cec_keypress *key)
+bool CLibCEC::SetConfiguration(const libcec_configuration *configuration)
 {
-  return m_keyBuffer.Pop(*key);
+  return m_client ? m_client->SetConfiguration(*configuration) : false;
 }
 
-bool CLibCEC::GetNextCommand(cec_command *command)
+bool CLibCEC::CanPersistConfiguration(void)
+{
+  return m_client ? m_client->CanPersistConfiguration() : false;
+}
+
+bool CLibCEC::PersistConfiguration(libcec_configuration *configuration)
+{
+  return m_client ? m_client->PersistConfiguration(*configuration) : false;
+}
+
+void CLibCEC::RescanActiveDevices(void)
+{
+  if (m_client)
+    m_client->RescanActiveDevices();
+}
+
+bool CLibCEC::IsLibCECActiveSource(void)
 {
-  return m_commandBuffer.Pop(*command);
+  return m_client ? m_client->IsLibCECActiveSource() : false;
 }
 
 bool CLibCEC::Transmit(const cec_command &data)
 {
-  return m_cec ? m_cec->Transmit(data) : false;
+  return m_client ? m_client->Transmit(data) : false;
 }
 
 bool CLibCEC::SetLogicalAddress(cec_logical_address iLogicalAddress)
 {
-  return m_cec ? m_cec->SetLogicalAddress(iLogicalAddress) : false;
+  return m_client ? m_client->SetLogicalAddress(iLogicalAddress) : false;
 }
 
 bool CLibCEC::SetPhysicalAddress(uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS */)
 {
-  return m_cec ? m_cec->SetPhysicalAddress(iPhysicalAddress) : false;
+  return m_client ? m_client->SetPhysicalAddress(iPhysicalAddress) : false;
 }
 
 bool CLibCEC::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort /* = CEC_DEFAULT_HDMI_PORT */)
 {
-  return m_cec ? m_cec->SetHDMIPort(iBaseDevice, iPort) : false;
-}
-
-bool CLibCEC::EnablePhysicalAddressDetection(void)
-{
-  return m_cec ? m_cec->EnablePhysicalAddressDetection() : false;
+  return m_client ? m_client->SetHDMIPort(iBaseDevice, iPort) : false;
 }
 
 bool CLibCEC::PowerOnDevices(cec_logical_address address /* = CECDEVICE_TV */)
 {
-  return m_cec && address >= CECDEVICE_TV && address <= CECDEVICE_BROADCAST ? m_cec->PowerOnDevices(address) : false;
+  return m_client ? m_client->SendPowerOnDevices(address) : false;
 }
 
 bool CLibCEC::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
 {
-  return m_cec && address >= CECDEVICE_TV && address <= CECDEVICE_BROADCAST ? m_cec->StandbyDevices(address) : false;
+  return m_client ? m_client->SendStandbyDevices(address) : false;
 }
 
 bool CLibCEC::SetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
 {
-  return m_cec ? m_cec->SetActiveSource(type) : false;
-}
-
-bool CLibCEC::SetActiveView(void)
-{
-  return m_cec ? m_cec->SetActiveView() : false;
+  return m_client ? m_client->SendSetActiveSource(type) : false;
 }
 
 bool CLibCEC::SetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate /* = true */)
 {
-  return m_cec ? m_cec->SetDeckControlMode(mode, bSendUpdate) : false;
+  return m_client ? m_client->SendSetDeckControlMode(mode, bSendUpdate) : false;
 }
 
 bool CLibCEC::SetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true */)
 {
-  return m_cec ? m_cec->SetDeckInfo(info, bSendUpdate) : false;
+  return m_client ? m_client->SendSetDeckInfo(info, bSendUpdate) : false;
 }
 
 bool CLibCEC::SetInactiveView(void)
 {
-  return m_cec ? m_cec->TransmitInactiveSource() : false;
+  return m_client ? m_client->SendSetInactiveView() : false;
 }
 
 bool CLibCEC::SetMenuState(cec_menu_state state, bool bSendUpdate /* = true */)
 {
-  return m_cec ? m_cec->SetMenuState(state, bSendUpdate) : false;
+  return m_client ? m_client->SendSetMenuState(state, bSendUpdate) : false;
 }
 
 bool CLibCEC::SetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage)
 {
-  return m_cec && iLogicalAddress >= CECDEVICE_TV && iLogicalAddress <= CECDEVICE_BROADCAST ?
-      m_cec->m_busDevices[m_cec->GetLogicalAddress()]->TransmitOSDString(iLogicalAddress, duration, strMessage) :
-      false;
+  return m_client ? m_client->SendSetOSDString(iLogicalAddress, duration, strMessage) : false;
 }
 
 bool CLibCEC::SwitchMonitoring(bool bEnable)
 {
-  return m_cec ? m_cec->SwitchMonitoring(bEnable) : false;
+  return m_client ? m_client->SwitchMonitoring(bEnable) : false;
 }
 
 cec_version CLibCEC::GetDeviceCecVersion(cec_logical_address iAddress)
 {
-  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
-    return m_cec->GetDeviceCecVersion(iAddress);
-  return CEC_VERSION_UNKNOWN;
+  return m_client ? m_client->GetDeviceCecVersion(iAddress) : CEC_VERSION_UNKNOWN;
 }
 
 bool CLibCEC::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language)
 {
-  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
-    return m_cec->GetDeviceMenuLanguage(iAddress, language);
-  return false;
+  return m_client ? m_client->GetDeviceMenuLanguage(iAddress, *language) : false;
 }
 
 uint64_t CLibCEC::GetDeviceVendorId(cec_logical_address iAddress)
 {
-  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
-    return m_cec->GetDeviceVendorId(iAddress);
-  return 0;
+  return m_client ? m_client->GetDeviceVendorId(iAddress) : (uint64_t)CEC_VENDOR_UNKNOWN;
 }
 
 uint16_t CLibCEC::GetDevicePhysicalAddress(cec_logical_address iAddress)
 {
-  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
-    return m_cec->GetDevicePhysicalAddress(iAddress);
-  return 0;
-}
-
-cec_logical_address CLibCEC::GetActiveSource(void)
-{
-  return m_cec ? m_cec->GetActiveSource() : CECDEVICE_UNKNOWN;
-}
-
-bool CLibCEC::IsActiveSource(cec_logical_address iAddress)
-{
-  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
-    return m_cec->IsActiveSource(iAddress);
-  return false;
+  return m_client ? m_client->GetDevicePhysicalAddress(iAddress) : CEC_INVALID_PHYSICAL_ADDRESS;
 }
 
 cec_power_status CLibCEC::GetDevicePowerStatus(cec_logical_address iAddress)
 {
-  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
-    return m_cec->GetDevicePowerStatus(iAddress);
-  return CEC_POWER_STATUS_UNKNOWN;
+  return m_client ? m_client->GetDevicePowerStatus(iAddress) : CEC_POWER_STATUS_UNKNOWN;
 }
 
 bool CLibCEC::PollDevice(cec_logical_address iAddress)
 {
-  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
-    return m_cec->PollDevice(iAddress);
-  return false;
+  return m_client ? m_client->PollDevice(iAddress) : false;
 }
 
 cec_logical_addresses CLibCEC::GetActiveDevices(void)
 {
   cec_logical_addresses addresses;
   addresses.Clear();
-  if (m_cec)
-    addresses = m_cec->GetActiveDevices();
+  if (m_client)
+    addresses = m_client->GetActiveDevices();
   return addresses;
 }
 
 bool CLibCEC::IsActiveDevice(cec_logical_address iAddress)
 {
-  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
-    return m_cec->IsPresentDevice(iAddress);
-  return false;
+  return m_client ? m_client->IsActiveDevice(iAddress) : false;
 }
 
 bool CLibCEC::IsActiveDeviceType(cec_device_type type)
 {
-  if (m_cec && type >= CEC_DEVICE_TYPE_TV && type <= CEC_DEVICE_TYPE_AUDIO_SYSTEM)
-    return m_cec->IsPresentDeviceType(type);
-  return false;
+  return m_client ? m_client->IsActiveDeviceType(type) : false;
 }
 
 uint8_t CLibCEC::VolumeUp(bool bSendRelease /* = true */)
 {
-  if (m_cec)
-    return m_cec->VolumeUp(bSendRelease);
-  return 0;
+  return m_client ? m_client->SendVolumeUp(bSendRelease) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
 }
 
 uint8_t CLibCEC::VolumeDown(bool bSendRelease /* = true */)
 {
-  if (m_cec)
-    return m_cec->VolumeDown(bSendRelease);
-  return 0;
+  return m_client ? m_client->SendVolumeDown(bSendRelease) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
 }
 
-
-uint8_t CLibCEC::MuteAudio(bool bSendRelease /* = true */)
+uint8_t CLibCEC::MuteAudio(bool UNUSED(bSendRelease) /* = true */)
 {
-  if (m_cec)
-    return m_cec->MuteAudio(bSendRelease);
-  return 0;
+  return m_client ? m_client->SendMuteAudio() : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
 }
 
 bool CLibCEC::SendKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */)
 {
-  if (m_cec)
-    return m_cec->TransmitKeypress(iDestination, key, bWait);
-  return false;
+  return m_client ? m_client->SendKeypress(iDestination, key, bWait) : false;
 }
 
 bool CLibCEC::SendKeyRelease(cec_logical_address iDestination, bool bWait /* = true */)
 {
-  if (m_cec)
-    return m_cec->TransmitKeyRelease(iDestination, bWait);
-  return false;
+  return m_client ? m_client->SendKeyRelease(iDestination, bWait) : false;
 }
 
 cec_osd_name CLibCEC::GetDeviceOSDName(cec_logical_address iAddress)
 {
   cec_osd_name retVal;
-  retVal.device = iAddress;
-  retVal.name[0] = 0;
-
-  if (m_cec)
-    retVal = m_cec->GetDeviceOSDName(iAddress);
+  retVal.device = CECDEVICE_UNKNOWN;
+  memset(retVal.name, 0, 14);
 
+  if (m_client)
+    retVal = m_client->GetDeviceOSDName(iAddress);
   return retVal;
 }
 
-void CLibCEC::AddLog(const cec_log_level level, const char *strFormat, ...)
-{
-  CStdString strLog;
-
-  va_list argList;
-  va_start(argList, strFormat);
-  strLog.FormatV(strFormat, argList);
-  va_end(argList);
-
-  CLibCEC *instance = CLibCEC::GetInstance();
-  if (!instance)
-    return;
-  CLockObject lock(instance->m_logMutex);
-
-  cec_log_message message;
-  message.level = level;
-  message.time = GetTimeMs() - instance->m_iStartTime;
-  snprintf(message.message, sizeof(message.message), "%s", strLog.c_str());
-
-  if (instance->m_callbacks && instance->m_callbacks->CBCecLogMessage)
-    instance->m_callbacks->CBCecLogMessage(instance->m_cbParam, message);
-  else
-    instance->m_logBuffer.Push(message);
-}
-
-void CLibCEC::AddKey(const cec_keypress &key)
-{
-  CLibCEC *instance = CLibCEC::GetInstance();
-  if (!instance)
-    return;
-  CLockObject lock(instance->m_mutex);
-
-  AddLog(CEC_LOG_DEBUG, "key pressed: %1x", key.keycode);
-
-  if (instance->m_callbacks && instance->m_callbacks->CBCecKeyPress)
-    instance->m_callbacks->CBCecKeyPress(instance->m_cbParam, key);
-  else
-    instance->m_keyBuffer.Push(key);
-
-  instance->m_iCurrentButton = key.duration > 0 ? CEC_USER_CONTROL_CODE_UNKNOWN : key.keycode;
-  instance->m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
-}
-
-void CLibCEC::ConfigurationChanged(const libcec_configuration &config)
-{
-  CLibCEC *instance = CLibCEC::GetInstance();
-  CLockObject lock(instance->m_mutex);
-
-  if (instance->m_callbacks &&
-      config.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
-      instance->m_callbacks->CBCecConfigurationChanged &&
-      instance->m_cec->IsInitialised())
-    instance->m_callbacks->CBCecConfigurationChanged(instance->m_cbParam, config);
-}
-
-void CLibCEC::SetCurrentButton(cec_user_control_code iButtonCode)
-{
-  /* push keypress to the keybuffer with 0 duration.
-     push another press to the keybuffer with the duration set when the button is released */
-  cec_keypress key;
-  key.duration = 0;
-  key.keycode = iButtonCode;
-
-  AddKey(key);
-}
-
-void CLibCEC::AddKey(void)
-{
-  CLibCEC *instance = CLibCEC::GetInstance();
-  if (!instance)
-    return;
-  CLockObject lock(instance->m_mutex);
-
-  if (instance->m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
-  {
-    cec_keypress key;
-
-    key.duration = (unsigned int) (GetTimeMs() - instance->m_buttontime);
-    key.keycode = instance->m_iCurrentButton;
-    AddLog(CEC_LOG_DEBUG, "key released: %1x", key.keycode);
-
-    if (instance->m_callbacks && instance->m_callbacks->CBCecKeyPress)
-      instance->m_callbacks->CBCecKeyPress(instance->m_cbParam, key);
-    else
-      instance->m_keyBuffer.Push(key);
-    instance->m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
-  }
-  instance->m_buttontime = 0;
-}
-
-void CLibCEC::AddCommand(const cec_command &command)
-{
-  CLibCEC *instance = CLibCEC::GetInstance();
-  if (!instance)
-    return;
-  CLockObject lock(instance->m_mutex);
-
-  AddLog(CEC_LOG_NOTICE, ">> %s (%X) -> %s (%X): %s (%2X)", instance->m_cec->ToString(command.initiator), command.initiator, instance->m_cec->ToString(command.destination), command.destination, instance->m_cec->ToString(command.opcode), command.opcode);
-
-  if (instance->m_callbacks && instance->m_callbacks->CBCecCommand)
-    instance->m_callbacks->CBCecCommand(instance->m_cbParam, command);
-  else if (!instance->m_commandBuffer.Push(command))
-    AddLog(CEC_LOG_WARNING, "command buffer is full");
-}
-
-void CLibCEC::Alert(const libcec_alert type, const libcec_parameter &param)
-{
-  CLibCEC *instance = CLibCEC::GetInstance();
-  if (!instance)
-    return;
-  CLockObject lock(instance->m_mutex);
-
-  libcec_configuration config;
-  instance->GetCurrentConfiguration(&config);
-
-  if (instance->m_callbacks &&
-      config.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
-      instance->m_cec->IsInitialised() &&
-      instance->m_callbacks->CBCecAlert)
-    instance->m_callbacks->CBCecAlert(instance->m_cbParam, type, param);
-
-  if (type == CEC_ALERT_CONNECTION_LOST)
-    instance->Close();
-}
-
-void CLibCEC::CheckKeypressTimeout(void)
+cec_logical_address CLibCEC::GetActiveSource(void)
 {
-  if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN && GetTimeMs() - m_buttontime > CEC_BUTTON_TIMEOUT)
-  {
-    AddKey();
-    m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
-  }
+  return m_client ? m_client->GetActiveSource() : CECDEVICE_UNKNOWN;
 }
 
-int CLibCEC::MenuStateChanged(const cec_menu_state newState)
+bool CLibCEC::IsActiveSource(cec_logical_address iAddress)
 {
-  int iReturn(0);
-
-  CLibCEC *instance = CLibCEC::GetInstance();
-  if (!instance)
-    return iReturn;
-  CLockObject lock(instance->m_mutex);
-
-  AddLog(CEC_LOG_NOTICE, ">> %s: %s", instance->m_cec->ToString(CEC_OPCODE_MENU_REQUEST), instance->m_cec->ToString(newState));
-
-  libcec_configuration config;
-  instance->GetCurrentConfiguration(&config);
-
-  if (instance->m_callbacks &&
-      config.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
-      instance->m_callbacks->CBCecMenuStateChanged)
-    iReturn = instance->m_callbacks->CBCecMenuStateChanged(instance->m_cbParam, newState);
-
-  return iReturn;
+  return m_client ? m_client->IsActiveSource(iAddress) : false;
 }
-
 bool CLibCEC::SetStreamPath(cec_logical_address iAddress)
 {
-  uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress);
-  if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS)
-    return SetStreamPath(iPhysicalAddress);
-  return false;
+  return m_client ? m_client->SetStreamPath(iAddress) : false;
 }
 
 bool CLibCEC::SetStreamPath(uint16_t iPhysicalAddress)
 {
-  return m_cec->SetStreamPath(iPhysicalAddress);
+  return m_client ? m_client->SetStreamPath(iPhysicalAddress) : false;
 }
 
 cec_logical_addresses CLibCEC::GetLogicalAddresses(void)
 {
-  cec_logical_addresses addr = m_cec->GetLogicalAddresses();
-  return addr;
+  cec_logical_addresses addresses;
+  addresses.Clear();
+  if (m_client)
+    m_client->GetLogicalAddresses();
+  return addresses;
 }
 
-static CLibCEC *g_libCEC_instance(NULL);
-CLibCEC *CLibCEC::GetInstance(void)
+bool CLibCEC::GetNextLogMessage(cec_log_message *message)
 {
-  return g_libCEC_instance;
+  return m_client ? m_client->GetNextLogMessage(message) : false;
 }
 
-void CLibCEC::SetInstance(CLibCEC *instance)
+bool CLibCEC::GetNextKeypress(cec_keypress *key)
 {
-  if (g_libCEC_instance)
-    delete g_libCEC_instance;
-  g_libCEC_instance = instance;
+  return m_client ? m_client->GetNextKeypress(key) : false;
 }
 
-void * CECInit(const char *strDeviceName, CEC::cec_device_type_list types, uint16_t UNUSED(iPhysicalAddress) /* = 0 */)
+bool CLibCEC::GetNextCommand(cec_command *command)
 {
-  CLibCEC *lib = new CLibCEC(strDeviceName, types);
-  CLibCEC::SetInstance(lib);
-  return static_cast< void* > (lib);
+  return m_client ? m_client->GetNextCommand(command) : false;
 }
 
-void * CECInitialise(libcec_configuration *configuration)
+cec_device_type CLibCEC::GetType(cec_logical_address address)
 {
-  CLibCEC *lib = new CLibCEC(configuration);
-  CLibCEC::SetInstance(lib);
-  lib->GetCurrentConfiguration(configuration);
-  return static_cast< void* > (lib);
+  switch (address)
+  {
+    case CECDEVICE_AUDIOSYSTEM:
+      return CEC_DEVICE_TYPE_AUDIO_SYSTEM;
+    case CECDEVICE_PLAYBACKDEVICE1:
+    case CECDEVICE_PLAYBACKDEVICE2:
+    case CECDEVICE_PLAYBACKDEVICE3:
+      return CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
+    case CECDEVICE_RECORDINGDEVICE1:
+    case CECDEVICE_RECORDINGDEVICE2:
+    case CECDEVICE_RECORDINGDEVICE3:
+      return CEC_DEVICE_TYPE_RECORDING_DEVICE;
+    case CECDEVICE_TUNER1:
+    case CECDEVICE_TUNER2:
+    case CECDEVICE_TUNER3:
+    case CECDEVICE_TUNER4:
+      return CEC_DEVICE_TYPE_TUNER;
+    case CECDEVICE_TV:
+      return CEC_DEVICE_TYPE_TV;
+    default:
+      return CEC_DEVICE_TYPE_RESERVED;
+  }
 }
 
-bool CECStartBootloader(void)
+uint16_t CLibCEC::GetMaskForType(cec_logical_address address)
 {
-  bool bReturn(false);
-  cec_adapter deviceList[1];
-  if (CUSBCECAdapterDetection::FindAdapters(deviceList, 1) > 0)
+  return GetMaskForType(GetType(address));
+}
+
+uint16_t CLibCEC::GetMaskForType(cec_device_type type)
+{
+  switch (type)
   {
-    CUSBCECAdapterCommunication comm(NULL, deviceList[0].comm);
-    CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT);
-    while (timeout.TimeLeft() > 0 && (bReturn = comm.Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
+    case CEC_DEVICE_TYPE_AUDIO_SYSTEM:
     {
-      comm.Close();
-      CEvent::Sleep(500);
+      cec_logical_addresses addr;
+      addr.Clear();
+      addr.Set(CECDEVICE_AUDIOSYSTEM);
+      return addr.AckMask();
     }
-    if (comm.IsOpen())
-      bReturn = comm.StartBootloader();
+    case CEC_DEVICE_TYPE_PLAYBACK_DEVICE:
+    {
+      cec_logical_addresses addr;
+      addr.Clear();
+      addr.Set(CECDEVICE_PLAYBACKDEVICE1);
+      addr.Set(CECDEVICE_PLAYBACKDEVICE2);
+      addr.Set(CECDEVICE_PLAYBACKDEVICE3);
+      return addr.AckMask();
+    }
+    case CEC_DEVICE_TYPE_RECORDING_DEVICE:
+    {
+      cec_logical_addresses addr;
+      addr.Clear();
+      addr.Set(CECDEVICE_RECORDINGDEVICE1);
+      addr.Set(CECDEVICE_RECORDINGDEVICE2);
+      addr.Set(CECDEVICE_RECORDINGDEVICE3);
+      return addr.AckMask();
+    }
+    case CEC_DEVICE_TYPE_TUNER:
+    {
+      cec_logical_addresses addr;
+      addr.Clear();
+      addr.Set(CECDEVICE_TUNER1);
+      addr.Set(CECDEVICE_TUNER2);
+      addr.Set(CECDEVICE_TUNER3);
+      addr.Set(CECDEVICE_TUNER4);
+      return addr.AckMask();
+    }
+    case CEC_DEVICE_TYPE_TV:
+    {
+      cec_logical_addresses addr;
+      addr.Clear();
+      addr.Set(CECDEVICE_TV);
+      return addr.AckMask();
+    }
+    default:
+      return 0;
   }
+}
 
-  return bReturn;
+bool CLibCEC::IsValidPhysicalAddress(uint16_t iPhysicalAddress)
+{
+  return iPhysicalAddress >= CEC_MIN_PHYSICAL_ADDRESS &&
+         iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS;
 }
 
-void CECDestroy(CEC::ICECAdapter *UNUSED(instance))
+const char *CLibCEC::ToString(const cec_device_type type)
 {
-  CLibCEC::SetInstance(NULL);
+  switch (type)
+  {
+  case CEC_DEVICE_TYPE_AUDIO_SYSTEM:
+    return "audio system";
+  case CEC_DEVICE_TYPE_PLAYBACK_DEVICE:
+    return "playback device";
+  case CEC_DEVICE_TYPE_RECORDING_DEVICE:
+      return "recording device";
+  case CEC_DEVICE_TYPE_RESERVED:
+      return "reserved";
+  case CEC_DEVICE_TYPE_TUNER:
+      return "tuner";
+  case CEC_DEVICE_TYPE_TV:
+      return "TV";
+  default:
+    return "unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_menu_state state)
 {
-  return m_cec->ToString(state);
+  switch (state)
+  {
+  case CEC_MENU_STATE_ACTIVATED:
+    return "activated";
+  case CEC_MENU_STATE_DEACTIVATED:
+    return "deactivated";
+  default:
+    return "unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_version version)
 {
-  return m_cec->ToString(version);
+  switch (version)
+  {
+  case CEC_VERSION_1_2:
+    return "1.2";
+  case CEC_VERSION_1_2A:
+    return "1.2a";
+  case CEC_VERSION_1_3:
+    return "1.3";
+  case CEC_VERSION_1_3A:
+    return "1.3a";
+  case CEC_VERSION_1_4:
+    return "1.4";
+  default:
+    return "unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_power_status status)
 {
-  return m_cec->ToString(status);
+  switch (status)
+  {
+  case CEC_POWER_STATUS_ON:
+    return "on";
+  case CEC_POWER_STATUS_STANDBY:
+    return "standby";
+  case CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY:
+    return "in transition from on to standby";
+  case CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON:
+    return "in transition from standby to on";
+  default:
+    return "unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_logical_address address)
 {
-  return m_cec->ToString(address);
+  switch(address)
+  {
+  case CECDEVICE_AUDIOSYSTEM:
+    return "Audio";
+  case CECDEVICE_BROADCAST:
+    return "Broadcast";
+  case CECDEVICE_FREEUSE:
+    return "Free use";
+  case CECDEVICE_PLAYBACKDEVICE1:
+    return "Playback 1";
+  case CECDEVICE_PLAYBACKDEVICE2:
+    return "Playback 2";
+  case CECDEVICE_PLAYBACKDEVICE3:
+    return "Playback 3";
+  case CECDEVICE_RECORDINGDEVICE1:
+    return "Recorder 1";
+  case CECDEVICE_RECORDINGDEVICE2:
+    return "Recorder 2";
+  case CECDEVICE_RECORDINGDEVICE3:
+    return "Recorder 3";
+  case CECDEVICE_RESERVED1:
+    return "Reserved 1";
+  case CECDEVICE_RESERVED2:
+    return "Reserved 2";
+  case CECDEVICE_TUNER1:
+    return "Tuner 1";
+  case CECDEVICE_TUNER2:
+    return "Tuner 2";
+  case CECDEVICE_TUNER3:
+    return "Tuner 3";
+  case CECDEVICE_TUNER4:
+    return "Tuner 4";
+  case CECDEVICE_TV:
+    return "TV";
+  default:
+    return "unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_deck_control_mode mode)
 {
-  return m_cec->ToString(mode);
+  switch (mode)
+  {
+  case CEC_DECK_CONTROL_MODE_SKIP_FORWARD_WIND:
+    return "skip forward wind";
+  case CEC_DECK_CONTROL_MODE_EJECT:
+    return "eject";
+  case CEC_DECK_CONTROL_MODE_SKIP_REVERSE_REWIND:
+    return "reverse rewind";
+  case CEC_DECK_CONTROL_MODE_STOP:
+    return "stop";
+  default:
+    return "unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_deck_info status)
 {
-  return m_cec->ToString(status);
+  switch (status)
+  {
+  case CEC_DECK_INFO_PLAY:
+    return "play";
+  case CEC_DECK_INFO_RECORD:
+    return "record";
+  case CEC_DECK_INFO_PLAY_REVERSE:
+    return "play reverse";
+  case CEC_DECK_INFO_STILL:
+    return "still";
+  case CEC_DECK_INFO_SLOW:
+    return "slow";
+  case CEC_DECK_INFO_SLOW_REVERSE:
+    return "slow reverse";
+  case CEC_DECK_INFO_FAST_FORWARD:
+    return "fast forward";
+  case CEC_DECK_INFO_FAST_REVERSE:
+    return "fast reverse";
+  case CEC_DECK_INFO_NO_MEDIA:
+    return "no media";
+  case CEC_DECK_INFO_STOP:
+    return "stop";
+  case CEC_DECK_INFO_SKIP_FORWARD_WIND:
+    return "info skip forward wind";
+  case CEC_DECK_INFO_SKIP_REVERSE_REWIND:
+    return "info skip reverse rewind";
+  case CEC_DECK_INFO_INDEX_SEARCH_FORWARD:
+    return "info index search forward";
+  case CEC_DECK_INFO_INDEX_SEARCH_REVERSE:
+    return "info index search reverse";
+  case CEC_DECK_INFO_OTHER_STATUS:
+    return "other";
+  case CEC_DECK_INFO_OTHER_STATUS_LG:
+    return "LG other";
+  default:
+    return "unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_opcode opcode)
 {
-  return m_cec->ToString(opcode);
+  switch (opcode)
+  {
+  case CEC_OPCODE_ACTIVE_SOURCE:
+    return "active source";
+  case CEC_OPCODE_IMAGE_VIEW_ON:
+    return "image view on";
+  case CEC_OPCODE_TEXT_VIEW_ON:
+    return "text view on";
+  case CEC_OPCODE_INACTIVE_SOURCE:
+    return "inactive source";
+  case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
+    return "request active source";
+  case CEC_OPCODE_ROUTING_CHANGE:
+    return "routing change";
+  case CEC_OPCODE_ROUTING_INFORMATION:
+    return "routing information";
+  case CEC_OPCODE_SET_STREAM_PATH:
+    return "set stream path";
+  case CEC_OPCODE_STANDBY:
+    return "standby";
+  case CEC_OPCODE_RECORD_OFF:
+    return "record off";
+  case CEC_OPCODE_RECORD_ON:
+    return "record on";
+  case CEC_OPCODE_RECORD_STATUS:
+    return "record status";
+  case CEC_OPCODE_RECORD_TV_SCREEN:
+    return "record tv screen";
+  case CEC_OPCODE_CLEAR_ANALOGUE_TIMER:
+    return "clear analogue timer";
+  case CEC_OPCODE_CLEAR_DIGITAL_TIMER:
+    return "clear digital timer";
+  case CEC_OPCODE_CLEAR_EXTERNAL_TIMER:
+    return "clear external timer";
+  case CEC_OPCODE_SET_ANALOGUE_TIMER:
+    return "set analogue timer";
+  case CEC_OPCODE_SET_DIGITAL_TIMER:
+    return "set digital timer";
+  case CEC_OPCODE_SET_EXTERNAL_TIMER:
+    return "set external timer";
+  case CEC_OPCODE_SET_TIMER_PROGRAM_TITLE:
+    return "set timer program title";
+  case CEC_OPCODE_TIMER_CLEARED_STATUS:
+    return "timer cleared status";
+  case CEC_OPCODE_TIMER_STATUS:
+    return "timer status";
+  case CEC_OPCODE_CEC_VERSION:
+    return "cec version";
+  case CEC_OPCODE_GET_CEC_VERSION:
+    return "get cec version";
+  case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
+    return "give physical address";
+  case CEC_OPCODE_GET_MENU_LANGUAGE:
+    return "get menu language";
+  case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
+    return "report physical address";
+  case CEC_OPCODE_SET_MENU_LANGUAGE:
+    return "set menu language";
+  case CEC_OPCODE_DECK_CONTROL:
+    return "deck control";
+  case CEC_OPCODE_DECK_STATUS:
+    return "deck status";
+  case CEC_OPCODE_GIVE_DECK_STATUS:
+    return "give deck status";
+  case CEC_OPCODE_PLAY:
+    return "play";
+  case CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS:
+    return "give tuner status";
+  case CEC_OPCODE_SELECT_ANALOGUE_SERVICE:
+    return "select analogue service";
+  case CEC_OPCODE_SELECT_DIGITAL_SERVICE:
+    return "set digital service";
+  case CEC_OPCODE_TUNER_DEVICE_STATUS:
+    return "tuner device status";
+  case CEC_OPCODE_TUNER_STEP_DECREMENT:
+    return "tuner step decrement";
+  case CEC_OPCODE_TUNER_STEP_INCREMENT:
+    return "tuner step increment";
+  case CEC_OPCODE_DEVICE_VENDOR_ID:
+    return "device vendor id";
+  case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
+    return "give device vendor id";
+  case CEC_OPCODE_VENDOR_COMMAND:
+    return "vendor command";
+  case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
+    return "vendor command with id";
+  case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:
+    return "vendor remote button down";
+  case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:
+    return "vendor remote button up";
+  case CEC_OPCODE_SET_OSD_STRING:
+    return "set osd string";
+  case CEC_OPCODE_GIVE_OSD_NAME:
+    return "give osd name";
+  case CEC_OPCODE_SET_OSD_NAME:
+    return "set osd name";
+  case CEC_OPCODE_MENU_REQUEST:
+    return "menu request";
+  case CEC_OPCODE_MENU_STATUS:
+    return "menu status";
+  case CEC_OPCODE_USER_CONTROL_PRESSED:
+    return "user control pressed";
+  case CEC_OPCODE_USER_CONTROL_RELEASE:
+    return "user control release";
+  case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
+    return "give device power status";
+  case CEC_OPCODE_REPORT_POWER_STATUS:
+    return "report power status";
+  case CEC_OPCODE_FEATURE_ABORT:
+    return "feature abort";
+  case CEC_OPCODE_ABORT:
+    return "abort";
+  case CEC_OPCODE_GIVE_AUDIO_STATUS:
+    return "give audio status";
+  case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
+    return "give audio mode status";
+  case CEC_OPCODE_REPORT_AUDIO_STATUS:
+    return "report audio status";
+  case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
+    return "set system audio mode";
+  case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
+    return "system audio mode request";
+  case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:
+    return "system audio mode status";
+  case CEC_OPCODE_SET_AUDIO_RATE:
+    return "set audio rate";
+  case CEC_OPCODE_START_ARC:
+    return "start ARC";
+  case CEC_OPCODE_REPORT_ARC_STARTED:
+    return "report ARC started";
+  case CEC_OPCODE_REPORT_ARC_ENDED:
+    return "report ARC ended";
+  case CEC_OPCODE_REQUEST_ARC_START:
+    return "request ARC start";
+  case CEC_OPCODE_REQUEST_ARC_END:
+    return "request ARC end";
+  case CEC_OPCODE_END_ARC:
+    return "end ARC";
+  case CEC_OPCODE_CDC:
+    return "CDC";
+  case CEC_OPCODE_NONE:
+    return "poll";
+  default:
+    return "UNKNOWN";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_system_audio_status mode)
 {
-  return m_cec->ToString(mode);
+  switch(mode)
+  {
+  case CEC_SYSTEM_AUDIO_STATUS_ON:
+    return "on";
+  case CEC_SYSTEM_AUDIO_STATUS_OFF:
+    return "off";
+  default:
+    return "unknown";
+  }
 }
 
-const char *CLibCEC::ToString(const cec_audio_status status)
+const char *CLibCEC::ToString(const cec_audio_status UNUSED(status))
 {
-  return m_cec->ToString(status);
+  // TODO this is a mask
+  return "TODO";
 }
 
 const char *CLibCEC::ToString(const cec_vendor_id vendor)
 {
-  return m_cec->ToString(vendor);
+  switch (vendor)
+  {
+  case CEC_VENDOR_SAMSUNG:
+    return "Samsung";
+  case CEC_VENDOR_LG:
+    return "LG";
+  case CEC_VENDOR_PANASONIC:
+    return "Panasonic";
+  case CEC_VENDOR_PIONEER:
+    return "Pioneer";
+  case CEC_VENDOR_ONKYO:
+    return "Onkyo";
+  case CEC_VENDOR_YAMAHA:
+    return "Yamaha";
+  case CEC_VENDOR_PHILIPS:
+    return "Philips";
+  case CEC_VENDOR_SONY:
+    return "Sony";
+  case CEC_VENDOR_TOSHIBA:
+    return "Toshiba";
+  default:
+    return "Unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_client_version version)
 {
-  return m_cec->ToString(version);
+  switch (version)
+  {
+  case CEC_CLIENT_VERSION_PRE_1_5:
+    return "pre-1.5";
+  case CEC_CLIENT_VERSION_1_5_0:
+    return "1.5.0";
+  case CEC_CLIENT_VERSION_1_5_1:
+    return "1.5.1";
+  case CEC_CLIENT_VERSION_1_5_2:
+    return "1.5.2";
+  case CEC_CLIENT_VERSION_1_5_3:
+    return "1.5.3";
+  case CEC_CLIENT_VERSION_1_6_0:
+    return "1.6.0";
+  case CEC_CLIENT_VERSION_1_6_1:
+    return "1.6.1";
+  case CEC_CLIENT_VERSION_1_6_2:
+    return "1.6.2";
+  case CEC_CLIENT_VERSION_1_6_3:
+    return "1.6.3";
+  default:
+    return "Unknown";
+  }
 }
 
 const char *CLibCEC::ToString(const cec_server_version version)
 {
-  return m_cec->ToString(version);
+  switch (version)
+  {
+  case CEC_SERVER_VERSION_PRE_1_5:
+    return "pre-1.5";
+  case CEC_SERVER_VERSION_1_5_0:
+    return "1.5.0";
+  case CEC_SERVER_VERSION_1_5_1:
+    return "1.5.1";
+  case CEC_SERVER_VERSION_1_5_2:
+    return "1.5.2";
+  case CEC_SERVER_VERSION_1_5_3:
+    return "1.5.3";
+  case CEC_SERVER_VERSION_1_6_0:
+    return "1.6.0";
+  case CEC_SERVER_VERSION_1_6_1:
+    return "1.6.1";
+  case CEC_SERVER_VERSION_1_6_2:
+    return "1.6.2";
+  case CEC_SERVER_VERSION_1_6_3:
+    return "1.6.3";
+  default:
+    return "Unknown";
+  }
 }
 
-const char *CLibCEC::ToString(const cec_device_type type)
+void CLibCEC::CheckKeypressTimeout(void)
 {
-  return m_cec->ToString(type);
+  // check all clients
+  for (vector<CCECClient *>::iterator it = m_clients.begin(); it != m_clients.end(); it++)
+    (*it)->CheckKeypressTimeout();
 }
 
-bool CLibCEC::GetCurrentConfiguration(libcec_configuration *configuration)
+void CLibCEC::AddLog(const cec_log_level level, const char *strFormat, ...)
 {
-  return m_cec->GetCurrentConfiguration(configuration);
+  CStdString strLog;
+
+  // format the message
+  va_list argList;
+  va_start(argList, strFormat);
+  strLog.FormatV(strFormat, argList);
+  va_end(argList);
+
+  cec_log_message message;
+  message.level = level;
+  message.time = GetTimeMs() - m_iStartTime;
+  snprintf(message.message, sizeof(message.message), "%s", strLog.c_str());
+
+  // send the message to all clients
+  for (vector<CCECClient *>::iterator it = m_clients.begin(); it != m_clients.end(); it++)
+    (*it)->AddLog(message);
 }
 
-bool CLibCEC::SetConfiguration(const libcec_configuration *configuration)
+void CLibCEC::Alert(const libcec_alert type, const libcec_parameter &param)
 {
-  return m_cec->SetConfiguration(configuration);
+  // send the alert to all clients
+  for (vector<CCECClient *>::iterator it = m_clients.begin(); it != m_clients.end(); it++)
+    (*it)->Alert(type, param);
 }
 
-bool CLibCEC::CanPersistConfiguration(void)
+bool CLibCEC::SetActiveView(void)
 {
-  return m_cec->CanPersistConfiguration();
+  AddLog(CEC_LOG_WARNING, "deprecated method %s called", __FUNCTION__);
+  return SetActiveSource();
 }
 
-bool CLibCEC::PersistConfiguration(libcec_configuration *configuration)
+bool CLibCEC::EnablePhysicalAddressDetection(void)
 {
-  return m_cec->PersistConfiguration(configuration);
+  AddLog(CEC_LOG_WARNING, "deprecated method %s called", __FUNCTION__);
+  return true;
 }
 
-void CLibCEC::RescanActiveDevices(void)
+CCECClient *CLibCEC::RegisterClient(libcec_configuration &configuration)
 {
-  return m_cec->RescanActiveDevices();
+  if (!m_cec)
+    return NULL;
+
+  // create a new client instance
+  CCECClient *newClient = new CCECClient(m_cec, configuration);
+  if (!newClient)
+    return NULL;
+  m_clients.push_back(newClient);
+
+  // if the default client isn't set, set it
+  if (!m_client)
+    m_client = newClient;
+
+  // register the new client
+  if (m_cec->CECInitialised())
+    m_cec->RegisterClient(newClient);
+
+  return newClient;
 }
 
-bool CLibCEC::IsLibCECActiveSource(void)
+void CLibCEC::UnregisterClients(void)
 {
-  bool bReturn(false);
   if (m_cec)
-  {
-    cec_logical_address activeSource = m_cec->GetActiveSource();
-    if (activeSource != CECDEVICE_UNKNOWN)
-      bReturn = m_cec->m_busDevices[activeSource]->GetStatus(false) == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC;
-  }
-  return bReturn;
+    m_cec->UnregisterClients();
+
+  m_clients.clear();
+
+  delete m_client;
+  m_client = NULL;
 }
 
-cec_device_type CLibCEC::GetType(cec_logical_address address)
+void * CECInitialise(libcec_configuration *configuration)
 {
-  switch (address)
-  {
-    case CECDEVICE_AUDIOSYSTEM:
-      return CEC_DEVICE_TYPE_AUDIO_SYSTEM;
-    case CECDEVICE_PLAYBACKDEVICE1:
-    case CECDEVICE_PLAYBACKDEVICE2:
-    case CECDEVICE_PLAYBACKDEVICE3:
-      return CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
-    case CECDEVICE_RECORDINGDEVICE1:
-    case CECDEVICE_RECORDINGDEVICE2:
-    case CECDEVICE_RECORDINGDEVICE3:
-      return CEC_DEVICE_TYPE_RECORDING_DEVICE;
-    case CECDEVICE_TUNER1:
-    case CECDEVICE_TUNER2:
-    case CECDEVICE_TUNER3:
-    case CECDEVICE_TUNER4:
-      return CEC_DEVICE_TYPE_TUNER;
-    case CECDEVICE_TV:
-      return CEC_DEVICE_TYPE_TV;
-    default:
-      return CEC_DEVICE_TYPE_RESERVED;
-  }
+  if (!configuration)
+    return NULL;
+
+  // create a new libCEC instance
+  CLibCEC *lib = new CLibCEC(NULL);
+
+  // register a new client
+  CCECClient *client(NULL);
+  if (lib && configuration)
+    client = lib->RegisterClient(*configuration);
+
+  // update the current configuration
+  if (client)
+    client->GetCurrentConfiguration(*configuration);
+
+  // ensure that the correct server version is set
+  configuration->serverVersion = LIBCEC_VERSION_CURRENT;
+
+  return static_cast< void* > (lib);
 }
 
-uint16_t CLibCEC::GetMaskForType(cec_logical_address address)
+void * CECInit(const char *strDeviceName, CEC::cec_device_type_list types, uint16_t iPhysicalAddress /* = 0 */)
 {
-  return GetMaskForType(GetType(address));
+  libcec_configuration configuration;
+
+  // client version < 1.5.0
+  snprintf(configuration.strDeviceName, 13, "%s", strDeviceName);
+  configuration.deviceTypes      = types;
+  configuration.iPhysicalAddress = iPhysicalAddress;
+
+  if (configuration.deviceTypes.IsEmpty())
+    configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
+
+  return CECInitialise(&configuration);
 }
 
-uint16_t CLibCEC::GetMaskForType(cec_device_type type)
+bool CECStartBootloader(void)
 {
-  switch (type)
+  bool bReturn(false);
+  cec_adapter deviceList[1];
+  if (CUSBCECAdapterDetection::FindAdapters(deviceList, 1) > 0)
   {
-    case CEC_DEVICE_TYPE_AUDIO_SYSTEM:
-    {
-      cec_logical_addresses addr;
-      addr.Clear();
-      addr.Set(CECDEVICE_AUDIOSYSTEM);
-      return addr.AckMask();
-    }
-    case CEC_DEVICE_TYPE_PLAYBACK_DEVICE:
-    {
-      cec_logical_addresses addr;
-      addr.Clear();
-      addr.Set(CECDEVICE_PLAYBACKDEVICE1);
-      addr.Set(CECDEVICE_PLAYBACKDEVICE2);
-      addr.Set(CECDEVICE_PLAYBACKDEVICE3);
-      return addr.AckMask();
-    }
-    case CEC_DEVICE_TYPE_RECORDING_DEVICE:
-    {
-      cec_logical_addresses addr;
-      addr.Clear();
-      addr.Set(CECDEVICE_RECORDINGDEVICE1);
-      addr.Set(CECDEVICE_RECORDINGDEVICE2);
-      addr.Set(CECDEVICE_RECORDINGDEVICE3);
-      return addr.AckMask();
-    }
-    case CEC_DEVICE_TYPE_TUNER:
-    {
-      cec_logical_addresses addr;
-      addr.Clear();
-      addr.Set(CECDEVICE_TUNER1);
-      addr.Set(CECDEVICE_TUNER2);
-      addr.Set(CECDEVICE_TUNER3);
-      addr.Set(CECDEVICE_TUNER4);
-      return addr.AckMask();
-    }
-    case CEC_DEVICE_TYPE_TV:
+    CUSBCECAdapterCommunication comm(NULL, deviceList[0].comm);
+    CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT);
+    while (timeout.TimeLeft() > 0 && (bReturn = comm.Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
     {
-      cec_logical_addresses addr;
-      addr.Clear();
-      addr.Set(CECDEVICE_TV);
-      return addr.AckMask();
+      comm.Close();
+      CEvent::Sleep(500);
     }
-    default:
-      return 0;
+    if (comm.IsOpen())
+      bReturn = comm.StartBootloader();
   }
+
+  return bReturn;
+}
+
+void CECDestroy(CEC::ICECAdapter *instance)
+{
+  delete instance;
 }
 
 bool CLibCEC::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
 {
   if (m_cec->IsRunning())
     return false;
-  
+
   return m_cec->GetDeviceInformation(strPort, config, iTimeoutMs);
 }
+
+// no longer being used
+void CLibCEC::AddKey(const cec_keypress &UNUSED(key)) {}
+void CLibCEC::AddCommand(const cec_command &UNUSED(command)) {}
+void CLibCEC::ConfigurationChanged(const libcec_configuration &UNUSED(config)) {}
+void CLibCEC::SetCurrentButton(cec_user_control_code UNUSED(iButtonCode)) {}
+CLibCEC *CLibCEC::GetInstance(void) { return NULL; }
+void CLibCEC::SetInstance(CLibCEC *UNUSED(instance)) {}
index 563cec1ca1c0a0d7db8561280e2247a2a05c055d..0ef3e832d3d318218eda23c4beeccceefd40947e 100644 (file)
@@ -39,18 +39,19 @@ namespace CEC
 {
   class CAdapterCommunication;
   class CCECProcessor;
+  class CCECClient;
 
   class CLibCEC : public ICECAdapter
   {
     public:
-    /*!
-     * ICECAdapter implementation
-     */
-    //@{
       CLibCEC(const char *strDeviceName, cec_device_type_list types, uint16_t iPhysicalAddress = 0);
       CLibCEC(libcec_configuration *configuration);
       virtual ~CLibCEC(void);
 
+    /*!
+     * ICECAdapter implementation
+     */
+    //@{
       bool Open(const char *strPort, uint32_t iTimeout = CEC_DEFAULT_CONNECT_TIMEOUT);
       void Close(void);
       bool EnableCallbacks(void *cbParam, ICECCallbacks *callbacks);
@@ -130,30 +131,37 @@ namespace CEC
       bool GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs = CEC_DEFAULT_CONNECT_TIMEOUT);
     //@}
 
-      static void AddLog(const cec_log_level level, const char *strFormat, ...);
-      static void AddKey(void);
-      static void AddKey(const cec_keypress &key);
-      static void AddCommand(const cec_command &command);
-      static void ConfigurationChanged(const libcec_configuration &config);
-      static void SetCurrentButton(cec_user_control_code iButtonCode);
+      void AddLog(const cec_log_level level, const char *strFormat, ...);
+      static void AddKey(void) {}                                           //UNUSED
+      static void AddKey(const cec_keypress &key);                          //UNUSED
+      static void AddCommand(const cec_command &command);                   //UNUSED
+      static void ConfigurationChanged(const libcec_configuration &config); //UNUSED
+      static void SetCurrentButton(cec_user_control_code iButtonCode);      //UNUSED
       void CheckKeypressTimeout(void);
-      static int MenuStateChanged(const cec_menu_state newState);
-      static void Alert(const libcec_alert type, const libcec_parameter &param);
+      void Alert(const libcec_alert type, const libcec_parameter &param);
+      static CLibCEC *GetInstance(void);                                    //UNUSED
+      static void SetInstance(CLibCEC *instance);                           //UNUSED
 
-      static CLibCEC *GetInstance(void);
-      static void SetInstance(CLibCEC *instance);
+      static bool IsValidPhysicalAddress(uint16_t iPhysicalAddress);
+      CCECClient *RegisterClient(libcec_configuration &configuration);
+      void UnregisterClients(void);
+      std::vector<CCECClient *> GetClients(void) { return m_clients; };
+
+      CCECProcessor *           m_cec;
 
-      CCECProcessor *                         m_cec;
     protected:
       int64_t                                 m_iStartTime;
-      cec_user_control_code                   m_iCurrentButton;
-      int64_t                                 m_buttontime;
-      PLATFORM::SyncedBuffer<cec_log_message> m_logBuffer;
-      PLATFORM::SyncedBuffer<cec_keypress>    m_keyBuffer;
-      PLATFORM::SyncedBuffer<cec_command>     m_commandBuffer;
-      ICECCallbacks *                         m_callbacks;
-      void *                                  m_cbParam;
+      cec_user_control_code                   m_iCurrentButton;             //UNUSED
+      int64_t                                 m_buttontime;                 //UNUSED
+      PLATFORM::SyncedBuffer<cec_log_message> m_logBuffer;                  //UNUSED
+      PLATFORM::SyncedBuffer<cec_keypress>    m_keyBuffer;                  //UNUSED
+      PLATFORM::SyncedBuffer<cec_command>     m_commandBuffer;              //UNUSED
+      ICECCallbacks *                         m_callbacks;                  //UNUSED
+      void *                                  m_cbParam;                    //UNUSED
       PLATFORM::CMutex                        m_mutex;
-      PLATFORM::CMutex                        m_logMutex;
+      PLATFORM::CMutex                        m_logMutex;                   //UNUSED
+
+      CCECClient *              m_client;
+      std::vector<CCECClient *> m_clients;
   };
 };
index 60d6d2d43069a24c9a6579c1cd0f4d5bf4ac7566..19f5e6f7a2dc03fd300dccbea40c68f1c3b32c5f 100644 (file)
@@ -11,6 +11,7 @@ pkgconfig_DATA = libcec.pc
 libcec_la_SOURCES = CECProcessor.cpp \
                     LibCEC.cpp \
                     LibCECC.cpp \
+                    CECClient.cpp \
                     adapter/USBCECAdapterCommands.cpp \
                     adapter/USBCECAdapterCommunication.cpp \
                     adapter/USBCECAdapterDetection.cpp \
@@ -18,6 +19,7 @@ libcec_la_SOURCES = CECProcessor.cpp \
                     adapter/USBCECAdapterMessageQueue.cpp \
                     devices/CECAudioSystem.cpp \
                     devices/CECBusDevice.cpp \
+                    devices/CECDeviceMap.cpp \
                     devices/CECPlaybackDevice.cpp \
                     devices/CECRecordingDevice.cpp \
                     devices/CECTuner.cpp \
index 48883bb2f3a07646d2b8894f0ad10c5493c67fe7..11af5e7a3f14b4e7e5cc5e420a31b80cc1f6a3a8 100644 (file)
@@ -36,6 +36,8 @@
 
 namespace CEC
 {
+  class CLibCEC;
+
   class IAdapterCommunicationCallback
   {
   public:
@@ -62,6 +64,8 @@ namespace CEC
      * @return True when this is an error, false otherwise.
      */
     virtual bool HandleReceiveFailed(cec_logical_address initiator) = 0;
+
+    virtual CLibCEC *GetLib(void) const = 0;
   };
 
   class IAdapterCommunication
@@ -126,6 +130,7 @@ namespace CEC
      * @return True when set, false otherwise.
      */
     virtual bool SetAckMask(uint16_t iMask) = 0;
+    virtual uint16_t GetAckMask(void) = 0;
 
     /*!
      * @brief Check whether the CEC adapter responds
@@ -143,6 +148,11 @@ namespace CEC
      */
     virtual uint32_t GetFirmwareBuildDate(void) = 0;
 
+    /*!
+     * @return True when this adapter is using the latest firmware build, or when the latest firmware build for this adapter type is unknown. False otherwise.
+     */
+    virtual bool IsRunningLatestFirmware(void) = 0;
+
     /*!
      * @return True when the control mode has been set, false otherwise.
      */
@@ -153,14 +163,14 @@ namespace CEC
      * @brief The configuration to store.
      * @return True when the configuration was persisted, false otherwise.
      */
-    virtual bool PersistConfiguration(libcec_configuration *configuration) = 0;
+    virtual bool PersistConfiguration(const libcec_configuration &configuration) = 0;
 
     /*!
      * @brief Get the persisted configuration from the adapter (if supported)
      * @param configuration The updated configuration.
      * @return True when the configuration was updated, false otherwise.
      */
-    virtual bool GetConfiguration(libcec_configuration *configuration) = 0;
+    virtual bool GetConfiguration(libcec_configuration &configuration) = 0;
 
     /*!
      * @return The name of the port
@@ -172,7 +182,6 @@ namespace CEC
      */
     virtual uint16_t GetPhysicalAddress(void) = 0;
 
-  protected:
     IAdapterCommunicationCallback *m_callback;
   };
 };
index 0374dbee60b19f662f693a6bbf430d57a93f1f28..bdc24126f7a2b2099205d07b7a58ee2385a14e14 100644 (file)
@@ -37,6 +37,9 @@
 using namespace CEC;
 using namespace PLATFORM;
 
+#define LIB_CEC     m_comm->m_callback->GetLib()
+#define ToString(p) LIB_CEC->ToString(p)
+
 CUSBCECAdapterCommands::CUSBCECAdapterCommands(CUSBCECAdapterCommunication *comm) :
     m_comm(comm),
     m_bSettingsRetrieved(false),
@@ -73,20 +76,20 @@ uint16_t CUSBCECAdapterCommands::RequestFirmwareVersion(void)
 
   while (m_persistedConfiguration.iFirmwareVersion == CEC_FW_VERSION_UNKNOWN && iFwVersionTry++ < 3)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting the firmware version");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting the firmware version");
     cec_datapacket response = RequestSetting(MSGCODE_FIRMWARE_VERSION);
     if (response.size == 2)
       m_persistedConfiguration.iFirmwareVersion = (response[0] << 8 | response[1]);
     else
     {
-      CLibCEC::AddLog(CEC_LOG_WARNING, "the adapter did not respond with a correct firmware version (try %d)", iFwVersionTry);
+      LIB_CEC->AddLog(CEC_LOG_WARNING, "the adapter did not respond with a correct firmware version (try %d)", iFwVersionTry);
       CEvent::Sleep(500);
     }
   }
 
   if (m_persistedConfiguration.iFirmwareVersion == CEC_FW_VERSION_UNKNOWN)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "defaulting to firmware version 1");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "defaulting to firmware version 1");
     m_persistedConfiguration.iFirmwareVersion = 1;
   }
 
@@ -95,13 +98,13 @@ uint16_t CUSBCECAdapterCommands::RequestFirmwareVersion(void)
 
 bool CUSBCECAdapterCommands::RequestSettingAutoEnabled(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting autonomous mode setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting autonomous mode setting");
 
   cec_datapacket response = RequestSetting(MSGCODE_GET_AUTO_ENABLED);
   if (response.size == 1)
   {
     m_bSettingAutoEnabled = response[0] == 1;
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted autonomous mode setting: '%s'", m_bSettingAutoEnabled ? "enabled" : "disabled");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "using persisted autonomous mode setting: '%s'", m_bSettingAutoEnabled ? "enabled" : "disabled");
     return true;
   }
   return false;
@@ -109,13 +112,13 @@ bool CUSBCECAdapterCommands::RequestSettingAutoEnabled(void)
 
 bool CUSBCECAdapterCommands::RequestSettingCECVersion(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting CEC version setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting CEC version setting");
 
   cec_datapacket response = RequestSetting(MSGCODE_GET_HDMI_VERSION);
   if (response.size == 1)
   {
     m_settingCecVersion = (cec_version)response[0];
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted CEC version setting: '%s'", CLibCEC::GetInstance()->ToString(m_settingCecVersion));
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "using persisted CEC version setting: '%s'", ToString(m_settingCecVersion));
     return true;
   }
   return false;
@@ -125,7 +128,7 @@ uint32_t CUSBCECAdapterCommands::RequestBuildDate(void)
 {
   if (m_iBuildDate == CEC_FW_BUILD_UNKNOWN)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting firmware build date");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting firmware build date");
 
     cec_datapacket response = RequestSetting(MSGCODE_GET_BUILDDATE);
     if (response.size == 4)
@@ -136,13 +139,13 @@ uint32_t CUSBCECAdapterCommands::RequestBuildDate(void)
 
 bool CUSBCECAdapterCommands::RequestSettingDefaultLogicalAddress(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting default logical address setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting default logical address setting");
 
   cec_datapacket response = RequestSetting(MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS);
   if (response.size == 1)
   {
     m_persistedConfiguration.logicalAddresses.primary = (cec_logical_address)response[0];
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted logical address setting: '%s'", CLibCEC::GetInstance()->ToString(m_persistedConfiguration.logicalAddresses.primary));
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "using persisted logical address setting: '%s'", ToString(m_persistedConfiguration.logicalAddresses.primary));
     return true;
   }
   return false;
@@ -150,29 +153,29 @@ bool CUSBCECAdapterCommands::RequestSettingDefaultLogicalAddress(void)
 
 bool CUSBCECAdapterCommands::RequestSettingDeviceType(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting device type setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting device type setting");
   m_persistedConfiguration.deviceTypes.Clear();
 
   cec_datapacket response = RequestSetting(MSGCODE_GET_DEVICE_TYPE);
   if (response.size == 1)
   {
     m_persistedConfiguration.deviceTypes.Add((cec_device_type)response[0]);
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted device type setting: '%s'", CLibCEC::GetInstance()->ToString((cec_device_type)response[0]));
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "using persisted device type setting: '%s'", ToString((cec_device_type)response[0]));
     return true;
   }
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "no persisted device type setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "no persisted device type setting");
   return false;
 }
 
 bool CUSBCECAdapterCommands::RequestSettingLogicalAddressMask(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting logical address mask setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting logical address mask setting");
 
   cec_datapacket response = RequestSetting(MSGCODE_GET_LOGICAL_ADDRESS_MASK);
   if (response.size == 2)
   {
     m_iSettingLAMask = ((uint16_t)response[0] << 8) | ((uint16_t)response[1]);
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted logical address mask setting: '%x'", m_iSettingLAMask);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "using persisted logical address mask setting: '%x'", m_iSettingLAMask);
     return true;
   }
   return false;
@@ -180,13 +183,13 @@ bool CUSBCECAdapterCommands::RequestSettingLogicalAddressMask(void)
 
 bool CUSBCECAdapterCommands::RequestSettingOSDName(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting OSD name setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting OSD name setting");
 
   memset(m_persistedConfiguration.strDeviceName, 0, 13);
   cec_datapacket response = RequestSetting(MSGCODE_GET_OSD_NAME);
   if (response.size == 0)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "no persisted device name setting");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "no persisted device name setting");
     return false;
   }
 
@@ -196,22 +199,22 @@ bool CUSBCECAdapterCommands::RequestSettingOSDName(void)
   buf[response.size] = 0;
 
   snprintf(m_persistedConfiguration.strDeviceName, 13, "%s", buf);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted device name setting: '%s'", buf);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "using persisted device name setting: '%s'", buf);
   return true;
 }
 
 bool CUSBCECAdapterCommands::RequestSettingPhysicalAddress(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting physical address setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting physical address setting");
 
   cec_datapacket response = RequestSetting(MSGCODE_GET_PHYSICAL_ADDRESS);
   if (response.size == 2)
   {
     m_persistedConfiguration.iPhysicalAddress = ((uint16_t)response[0] << 8) | ((uint16_t)response[1]);
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted physical address setting: '%4x'", m_persistedConfiguration.iPhysicalAddress);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "using persisted physical address setting: '%4x'", m_persistedConfiguration.iPhysicalAddress);
     return true;
   }
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "no persisted physical address setting");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "no persisted physical address setting");
   return false;
 }
 
@@ -222,12 +225,12 @@ bool CUSBCECAdapterCommands::SetSettingAutoEnabled(bool enabled)
   /* check whether this value was changed */
   if (m_bSettingAutoEnabled == enabled)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "autonomous mode setting unchanged (%s)", enabled ? "on" : "off");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "autonomous mode setting unchanged (%s)", enabled ? "on" : "off");
     return bReturn;
   }
 
   m_bNeedsWrite = true;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "turning autonomous mode %s", enabled ? "on" : "off");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "turning autonomous mode %s", enabled ? "on" : "off");
 
   CCECAdapterMessage params;
   params.PushEscaped(enabled ? 1 : 0);
@@ -248,12 +251,12 @@ bool CUSBCECAdapterCommands::SetSettingDeviceType(cec_device_type type)
   /* check whether this value was changed */
   if (m_persistedConfiguration.deviceTypes.types[0] == type)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "device type setting unchanged (%X)", (uint8_t)type);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "device type setting unchanged (%X)", (uint8_t)type);
     return bReturn;
   }
 
   m_bNeedsWrite = true;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the device type to %X (previous: %X)", (uint8_t)type, (uint8_t)m_persistedConfiguration.deviceTypes.types[0]);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting the device type to %X (previous: %X)", (uint8_t)type, (uint8_t)m_persistedConfiguration.deviceTypes.types[0]);
 
   CCECAdapterMessage params;
   params.PushEscaped((uint8_t)type);
@@ -271,12 +274,12 @@ bool CUSBCECAdapterCommands::SetSettingDefaultLogicalAddress(cec_logical_address
   /* check whether this value was changed */
   if (m_persistedConfiguration.logicalAddresses.primary == address)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "logical address setting unchanged (%X)", (uint8_t)address);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address setting unchanged (%X)", (uint8_t)address);
     return bReturn;
   }
 
   m_bNeedsWrite = true;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the default logical address to %X (previous: %X)", (uint8_t)address, (uint8_t)m_persistedConfiguration.logicalAddresses.primary);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting the default logical address to %X (previous: %X)", (uint8_t)address, (uint8_t)m_persistedConfiguration.logicalAddresses.primary);
 
   CCECAdapterMessage params;
   params.PushEscaped((uint8_t)address);
@@ -297,12 +300,12 @@ bool CUSBCECAdapterCommands::SetSettingLogicalAddressMask(uint16_t iMask)
   /* check whether this value was changed */
   if (m_iSettingLAMask == iMask)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "logical address mask setting unchanged (%2X)", iMask);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address mask setting unchanged (%2X)", iMask);
     return bReturn;
   }
 
   m_bNeedsWrite = true;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the logical address mask to %2X (previous: %2X)", iMask, m_iSettingLAMask);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting the logical address mask to %2X (previous: %2X)", iMask, m_iSettingLAMask);
 
   CCECAdapterMessage params;
   params.PushEscaped(iMask >> 8);
@@ -324,12 +327,12 @@ bool CUSBCECAdapterCommands::SetSettingPhysicalAddress(uint16_t iPhysicalAddress
   /* check whether this value was changed */
   if (m_persistedConfiguration.iPhysicalAddress == iPhysicalAddress)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "physical address setting unchanged (%04X)", iPhysicalAddress);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "physical address setting unchanged (%04X)", iPhysicalAddress);
     return bReturn;
   }
 
   m_bNeedsWrite = true;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the physical address to %04X (previous: %04X)", iPhysicalAddress, m_persistedConfiguration.iPhysicalAddress);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting the physical address to %04X (previous: %04X)", iPhysicalAddress, m_persistedConfiguration.iPhysicalAddress);
 
   CCECAdapterMessage params;
   params.PushEscaped(iPhysicalAddress >> 8);
@@ -351,12 +354,12 @@ bool CUSBCECAdapterCommands::SetSettingCECVersion(cec_version version)
   /* check whether this value was changed */
   if (m_settingCecVersion == version)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "CEC version setting unchanged (%s)", CLibCEC::GetInstance()->ToString(version));
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "CEC version setting unchanged (%s)", ToString(version));
     return bReturn;
   }
 
   m_bNeedsWrite = true;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the CEC version to %s (previous: %s)", CLibCEC::GetInstance()->ToString(version), CLibCEC::GetInstance()->ToString(m_settingCecVersion));
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting the CEC version to %s (previous: %s)", ToString(version), ToString(m_settingCecVersion));
 
   CCECAdapterMessage params;
   params.PushEscaped((uint8_t)version);
@@ -377,11 +380,11 @@ bool CUSBCECAdapterCommands::SetSettingOSDName(const char *strOSDName)
   /* check whether this value was changed */
   if (!strcmp(m_persistedConfiguration.strDeviceName, strOSDName))
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "OSD name setting unchanged (%s)", strOSDName);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "OSD name setting unchanged (%s)", strOSDName);
     return bReturn;
   }
 
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the OSD name to %s (previous: %s)", strOSDName, m_persistedConfiguration.strDeviceName);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting the OSD name to %s (previous: %s)", strOSDName, m_persistedConfiguration.strDeviceName);
 
   CCECAdapterMessage params;
   for (size_t iPtr = 0; iPtr < strlen(strOSDName); iPtr++)
@@ -401,7 +404,7 @@ bool CUSBCECAdapterCommands::WriteEEPROM(void)
   if (!m_bNeedsWrite)
     return true;
 
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "writing settings in the EEPROM");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "writing settings in the EEPROM");
 
   CCECAdapterMessage params;
   CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_WRITE_EEPROM, params);
@@ -410,7 +413,7 @@ bool CUSBCECAdapterCommands::WriteEEPROM(void)
   return m_bNeedsWrite;
 }
 
-bool CUSBCECAdapterCommands::PersistConfiguration(libcec_configuration *configuration)
+bool CUSBCECAdapterCommands::PersistConfiguration(const libcec_configuration &configuration)
 {
   if (m_persistedConfiguration.iFirmwareVersion < 2)
     return false;
@@ -420,12 +423,12 @@ bool CUSBCECAdapterCommands::PersistConfiguration(libcec_configuration *configur
 
   bool bReturn(true);
   bReturn &= SetSettingAutoEnabled(true);
-  bReturn &= SetSettingDeviceType(CLibCEC::GetType(configuration->logicalAddresses.primary));
-  bReturn &= SetSettingDefaultLogicalAddress(configuration->logicalAddresses.primary);
-  bReturn &= SetSettingLogicalAddressMask(CLibCEC::GetMaskForType(configuration->logicalAddresses.primary));
-  bReturn &= SetSettingPhysicalAddress(configuration->iPhysicalAddress);
+  bReturn &= SetSettingDeviceType(CLibCEC::GetType(configuration.logicalAddresses.primary));
+  bReturn &= SetSettingDefaultLogicalAddress(configuration.logicalAddresses.primary);
+  bReturn &= SetSettingLogicalAddressMask(CLibCEC::GetMaskForType(configuration.logicalAddresses.primary));
+  bReturn &= SetSettingPhysicalAddress(configuration.iPhysicalAddress);
   bReturn &= SetSettingCECVersion(CEC_VERSION_1_3A);
-  bReturn &= SetSettingOSDName(configuration->strDeviceName);
+  bReturn &= SetSettingOSDName(configuration.strDeviceName);
   bReturn &= WriteEEPROM();
   return bReturn;
 }
@@ -434,7 +437,7 @@ bool CUSBCECAdapterCommands::RequestSettings(void)
 {
   if (m_persistedConfiguration.iFirmwareVersion < 2)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - firmware version %d does not have any eeprom settings", __FUNCTION__, m_persistedConfiguration.iFirmwareVersion);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - firmware version %d does not have any eeprom settings", __FUNCTION__, m_persistedConfiguration.iFirmwareVersion);
     // settings can only be persisted with firmware v2+
     return false;
   }
@@ -474,24 +477,24 @@ bool CUSBCECAdapterCommands::RequestSettings(void)
   return bReturn;
 }
 
-bool CUSBCECAdapterCommands::GetConfiguration(libcec_configuration *configuration)
+bool CUSBCECAdapterCommands::GetConfiguration(libcec_configuration &configuration)
 {
   // get the settings from the eeprom if needed
   if (!RequestSettings())
     return false;
 
   // copy the settings
-  configuration->iFirmwareVersion = m_persistedConfiguration.iFirmwareVersion;
-  configuration->deviceTypes      = m_persistedConfiguration.deviceTypes;
-  configuration->iPhysicalAddress = m_persistedConfiguration.iPhysicalAddress;
-  snprintf(configuration->strDeviceName, 13, "%s", m_persistedConfiguration.strDeviceName);
+  configuration.iFirmwareVersion = m_persistedConfiguration.iFirmwareVersion;
+  configuration.deviceTypes      = m_persistedConfiguration.deviceTypes;
+  configuration.iPhysicalAddress = m_persistedConfiguration.iPhysicalAddress;
+  snprintf(configuration.strDeviceName, 13, "%s", m_persistedConfiguration.strDeviceName);
 
   return true;
 }
 
 bool CUSBCECAdapterCommands::PingAdapter(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending ping");
 
   CCECAdapterMessage params;
   CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_PING, params);
@@ -502,7 +505,7 @@ bool CUSBCECAdapterCommands::PingAdapter(void)
 
 bool CUSBCECAdapterCommands::SetAckMask(uint16_t iMask)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask);
 
   CCECAdapterMessage params;
   params.PushEscaped(iMask >> 8);
@@ -515,7 +518,7 @@ bool CUSBCECAdapterCommands::SetAckMask(uint16_t iMask)
 
 bool CUSBCECAdapterCommands::StartBootloader(void)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "starting the bootloader");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "starting the bootloader");
 
   CCECAdapterMessage params;
   CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_START_BOOTLOADER, params);
@@ -526,7 +529,7 @@ bool CUSBCECAdapterCommands::StartBootloader(void)
 
 bool CUSBCECAdapterCommands::SetLineTimeout(uint8_t iTimeout)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the line timeout to %d", iTimeout);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting the line timeout to %d", iTimeout);
   CCECAdapterMessage params;
   params.PushEscaped(iTimeout);
   CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_TRANSMIT_IDLETIME, params);
@@ -537,7 +540,7 @@ bool CUSBCECAdapterCommands::SetLineTimeout(uint8_t iTimeout)
 
 bool CUSBCECAdapterCommands::SetControlledMode(bool controlled)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off");
 
   CCECAdapterMessage params;
   params.PushEscaped(controlled ? 1 : 0);
index 389aa5f680a74a44d19ccf125491f0f45a9b5ffd..d961240fc13c3ca282c53a5c41545792505f004c 100644 (file)
@@ -57,14 +57,14 @@ namespace CEC
      * @param configuration The configuration to persist.
      * @return True when persisted, false otherwise.
      */
-    bool PersistConfiguration(libcec_configuration *configuration);
+    bool PersistConfiguration(const libcec_configuration &configuration);
 
     /*!
      * @brief Get the persisted configuration from the EEPROM.
      * @param configuration The persisted configuration.
      * @return True when retrieved, false otherwise.
      */
-    bool GetConfiguration(libcec_configuration *configuration);
+    bool GetConfiguration(libcec_configuration &configuration);
 
     /*!
      * @brief Send a ping command to the adapter.
index 6a4a0b99b99171cce2621406afdd4309163025c1..801ebc509550fda47fe1b80dff0dce9cca77befc 100644 (file)
@@ -44,6 +44,13 @@ using namespace PLATFORM;
 
 #define CEC_ADAPTER_PING_TIMEOUT 15000
 
+// firmware version 2
+#define CEC_LATEST_ADAPTER_FW_VERSION 2
+// firmware date Thu Apr 26 20:14:49 2012 +0000
+#define CEC_LATEST_ADAPTER_FW_DATE    0x4F99ACB9
+
+#define LIB_CEC m_callback->GetLib()
+
 CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */) :
     IAdapterCommunication(callback),
     m_port(NULL),
@@ -52,7 +59,8 @@ CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCa
     m_bInitialised(false),
     m_pingThread(NULL),
     m_commands(NULL),
-    m_adapterMessageQueue(NULL)
+    m_adapterMessageQueue(NULL),
+    m_iAckMask(0xFFFF)
 {
   for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
     m_bWaitingForAck[iPtr] = false;
@@ -76,14 +84,14 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONN
     /* we need the port settings here */
     if (!m_port)
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "port is NULL");
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "port is NULL");
       return bConnectionOpened;
     }
 
     /* return true when the port is already open */
     if (IsOpen())
     {
-      CLibCEC::AddLog(CEC_LOG_WARNING, "port is already open");
+      LIB_CEC->AddLog(CEC_LOG_WARNING, "port is already open");
       return true;
     }
 
@@ -113,23 +121,38 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONN
     /* return false when we couldn't connect */
     if (!bConnectionOpened)
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, strError);
+      LIB_CEC->AddLog(CEC_LOG_ERROR, strError);
+
+      if (m_port->GetErrorNumber() == EACCES)
+      {
+        libcec_parameter param;
+        param.paramType = CEC_PARAMETER_TYPE_STRING;
+        param.paramData = (void*)"No permission to open the device";
+        LIB_CEC->Alert(CEC_ALERT_PERMISSION_ERROR, param);
+      }
+      else if (m_port->GetErrorNumber() == EBUSY)
+      {
+        libcec_parameter param;
+        param.paramType = CEC_PARAMETER_TYPE_STRING;
+        param.paramData = (void*)"The serial port is busy. Only one program can access the device directly.";
+        LIB_CEC->Alert(CEC_ALERT_PORT_BUSY, param);
+      }
       return false;
     }
 
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
     ClearInputBytes();
   }
 
   if (!CreateThread())
   {
     bConnectionOpened = false;
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a communication thread");
   }
   else if (!bSkipChecks && !CheckAdapter())
   {
     bConnectionOpened = false;
-    CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
   }
   else if (bStartListening)
   {
@@ -143,7 +166,7 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONN
     else
     {
       bConnectionOpened = false;
-      CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a ping thread");
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread");
     }
   }
 
@@ -161,9 +184,9 @@ void CUSBCECAdapterCommunication::Close(void)
   CLockObject lock(m_mutex);
 
   /* set the ackmask to 0 before closing the connection */
-  if (IsRunning() && m_port->IsOpen() && m_port->GetErrorNumber() == 0)
+  if (IsOpen() && m_port->GetErrorNumber() == 0)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__);
     SetAckMask(0);
     if (m_commands->GetFirmwareVersion() >= 2)
       SetControlledMode(false);
@@ -180,9 +203,6 @@ void CUSBCECAdapterCommunication::Close(void)
   /* close and delete the com port connection */
   if (m_port)
     m_port->Close();
-
-  libcec_parameter param;
-  CLibCEC::Alert(CEC_ALERT_CONNECTION_LOST, param);
 }
 
 cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout)
@@ -209,20 +229,26 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
 void *CUSBCECAdapterCommunication::Process(void)
 {
   CCECAdapterMessage msg;
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
 
   while (!IsStopped())
   {
     /* read from the serial port */
     if (!ReadFromDevice(50, 5))
+    {
+      libcec_parameter param;
+      param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
+      LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
+
       break;
+    }
 
     /* TODO sleep 5 ms so other threads can get a lock */
     Sleep(5);
   }
 
   m_adapterMessageQueue->Clear();
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread ended");
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
   return NULL;
 }
 
@@ -303,9 +329,9 @@ bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
 bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
 {
   CLockObject adapterLock(m_mutex);
-  if (!m_port->IsOpen())
+  if (!IsOpen())
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': the connection is closed", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str());
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': the connection is closed", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str());
     message->state = ADAPTER_MESSAGE_STATE_ERROR;
     return false;
   }
@@ -313,13 +339,14 @@ bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
   /* write the message */
   if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str(), m_port->GetError().c_str());
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str(), m_port->GetError().c_str());
     message->state = ADAPTER_MESSAGE_STATE_ERROR;
-    Close();
+    // this will trigger an alert in the reader thread
+    m_port->Close();
     return false;
   }
 
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
   message->state = ADAPTER_MESSAGE_STATE_SENT;
   return true;
 }
@@ -334,14 +361,14 @@ bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize
   /* read from the serial port */
   {
     CLockObject lock(m_mutex);
-    if (!m_port || !m_port->IsOpen())
+    if (!IsOpen())
       return false;
 
     iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
 
     if (m_port->GetErrorNumber())
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
       m_port->Close();
       return false;
     }
@@ -360,8 +387,7 @@ bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize
 
 CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
 {
-  if (!m_port || !m_port->IsOpen() ||
-      !m_adapterMessageQueue)
+  if (!IsOpen() || !m_adapterMessageQueue)
     return NULL;
 
   /* create the adapter message for this command */
@@ -374,8 +400,9 @@ CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_message
   /* write the command */
   if (!m_adapterMessageQueue->Write(output))
   {
+    // this will trigger an alert in the reader thread
     if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
-      Close();
+      m_port->Close();
     return output;
   }
   else
@@ -386,7 +413,7 @@ CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_message
       /* if the controller reported that the command was rejected, and we didn't send the command
          to set controlled mode, then the controller probably switched to auto mode. set controlled
          mode and retry */
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
       delete output;
       if (SetControlledMode(true))
         return SendCommand(msgCode, params, true);
@@ -406,7 +433,7 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFA
   unsigned iPingTry(0);
   while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
     CEvent::Sleep(500);
   }
 
@@ -418,7 +445,7 @@ bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFA
     bool bControlled(false);
     while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
       CEvent::Sleep(500);
     }
     bReturn = bControlled;
@@ -460,7 +487,7 @@ bool CUSBCECAdapterCommunication::StartBootloader(void)
 {
   if (m_port->IsOpen() && m_commands->StartBootloader())
   {
-    Close();
+    m_port->Close();
     return true;
   }
   return false;
@@ -468,32 +495,53 @@ bool CUSBCECAdapterCommunication::StartBootloader(void)
 
 bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
 {
-  return m_port->IsOpen() ? m_commands->SetAckMask(iMask) : false;
+  if (m_iAckMask == iMask)
+    return true;
+
+  if (IsOpen() && m_commands->SetAckMask(iMask))
+  {
+    m_iAckMask = iMask;
+    return true;
+  }
+
+  LIB_CEC->AddLog(CEC_LOG_ERROR, "couldn't change the ackmask: the connection is closed");
+  return false;
+}
+
+uint16_t CUSBCECAdapterCommunication::GetAckMask(void)
+{
+  return m_iAckMask;
 }
 
 bool CUSBCECAdapterCommunication::PingAdapter(void)
 {
-  return m_port->IsOpen() ? m_commands->PingAdapter() : false;
+  return IsOpen() ? m_commands->PingAdapter() : false;
 }
 
 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
 {
-  return m_commands->GetFirmwareVersion();
+  return IsOpen() ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
 }
 
 uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
 {
-  return m_commands->RequestBuildDate();
+  return IsOpen() ? m_commands->RequestBuildDate() : 0;
+}
+
+bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
+{
+  return GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION &&
+      GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE;
 }
 
-bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
+bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
 {
-  return m_port->IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
+  return IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
 }
 
-bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
+bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
 {
-  return m_port->IsOpen() ? m_commands->GetConfiguration(configuration) : false;
+  return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
 }
 
 CStdString CUSBCECAdapterCommunication::GetPortName(void)
@@ -503,7 +551,7 @@ CStdString CUSBCECAdapterCommunication::GetPortName(void)
 
 bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
 {
-  return m_port->IsOpen() ? m_commands->SetControlledMode(controlled) : false;
+  return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
 }
 
 void *CAdapterPingThread::Process(void)
@@ -535,7 +583,7 @@ void *CAdapterPingThread::Process(void)
       if (iFailedCounter == 3)
       {
         /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
-        CLibCEC::AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
+        m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
         m_com->StopThread(false);
         break;
       }
index a1171588bdd0777b9f1523b5acaf2f1c64c79178..7409e86d874b002b356a16a39cdea1b3931f2e70 100644 (file)
@@ -74,11 +74,13 @@ namespace CEC
 
     bool StartBootloader(void);
     bool SetAckMask(uint16_t iMask);
+    uint16_t GetAckMask(void);
     bool PingAdapter(void);
     uint16_t GetFirmwareVersion(void);
     uint32_t GetFirmwareBuildDate(void);
-    bool PersistConfiguration(libcec_configuration *configuration);
-    bool GetConfiguration(libcec_configuration *configuration);
+    bool IsRunningLatestFirmware(void);
+    bool PersistConfiguration(const libcec_configuration &configuration);
+    bool GetConfiguration(libcec_configuration &configuration);
     CStdString GetPortName(void);
     uint16_t GetPhysicalAddress(void) { return 0; }
     bool SetControlledMode(bool controlled);
@@ -164,6 +166,7 @@ namespace CEC
     CAdapterPingThread *                         m_pingThread;           /**< ping thread, that pings the adapter every 15 seconds */
     CUSBCECAdapterCommands *                     m_commands;             /**< commands that can be sent to the adapter */
     CCECAdapterMessageQueue *                    m_adapterMessageQueue;  /**< the incoming and outgoing message queue */
+    uint16_t                                     m_iAckMask;
   };
 
   class CAdapterPingThread : public PLATFORM::CThread
index aef61b2b924f0652129de7b7688f6a62f404d393..8ef5662fdf560e56ac33c4b35fe24d457a706427 100644 (file)
@@ -298,7 +298,7 @@ bool CCECAdapterMessage::PushReceivedByte(uint8_t byte)
   {
     if (HasStartMessage())
     {
-      CLibCEC::AddLog(CEC_LOG_WARNING, "received MSGSTART before MSGEND, removing previous buffer contents");
+      //TODO CLibCEC::AddLog(CEC_LOG_WARNING, "received MSGSTART before MSGEND, removing previous buffer contents");
       Clear();
     }
     PushBack(byte);
index a270a78235792a39a81a6fd746088c7155cea175..45b348c8752af10aed323f993fc118fd918a6c45 100644 (file)
@@ -41,7 +41,8 @@ using namespace std;
 
 #define MESSAGE_QUEUE_SIGNAL_WAIT_TIME 1000
 
-CCECAdapterMessageQueueEntry::CCECAdapterMessageQueueEntry(CCECAdapterMessage *message) :
+CCECAdapterMessageQueueEntry::CCECAdapterMessageQueueEntry(CCECAdapterMessageQueue *queue, CCECAdapterMessage *message) :
+    m_queue(queue),
     m_message(message),
     m_iPacketsLeft(message->IsTranmission() ? message->Size() / 4 : 1),
     m_bSucceeded(false),
@@ -150,7 +151,7 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedCommandAccepted(const CCECAdap
       strLog.Format("%s - command accepted", ToString());
       if (m_iPacketsLeft > 0)
         strLog.AppendFormat(" - waiting for %d more", m_iPacketsLeft);
-      CLibCEC::AddLog(CEC_LOG_DEBUG, strLog);
+      m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_DEBUG, strLog);
 
       /* no more packets left and not a transmission, so we're done */
       if (!m_message->IsTranmission() && m_iPacketsLeft == 0)
@@ -176,7 +177,7 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedTransmitSucceeded(const CCECAd
     if (m_iPacketsLeft == 0)
     {
       /* transmission succeeded, so we're done */
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - transmit succeeded", ToString());
+      m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_DEBUG, "%s - transmit succeeded", ToString());
       m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED;
       m_message->response = message.packet;
     }
@@ -184,7 +185,7 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedTransmitSucceeded(const CCECAd
     {
       /* error, we expected more acks
          since the messages are processed in order, this should not happen, so this is an error situation */
-      CLibCEC::AddLog(CEC_LOG_WARNING, "%s - received 'transmit succeeded' but not enough 'command accepted' messages (%d left)", ToString(), m_iPacketsLeft);
+      m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_WARNING, "%s - received 'transmit succeeded' but not enough 'command accepted' messages (%d left)", ToString(), m_iPacketsLeft);
       m_message->state = ADAPTER_MESSAGE_STATE_ERROR;
     }
   }
@@ -198,7 +199,7 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedResponse(const CCECAdapterMess
 {
   {
     CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - received response - %s", ToString(), message.ToString().c_str());
+    m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_DEBUG, "%s - received response - %s", ToString(), message.ToString().c_str());
     m_message->response = message.packet;
     if (m_message->IsTranmission())
       m_message->state = message.Message() == MSGCODE_TRANSMIT_SUCCEEDED ? ADAPTER_MESSAGE_STATE_SENT_ACKED : ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
@@ -263,7 +264,7 @@ void CCECAdapterMessageQueue::MessageReceived(const CCECAdapterMessage &msg)
   {
     /* the message wasn't handled */
     bool bIsError(m_com->HandlePoll(msg));
-    CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
+    m_com->m_callback->GetLib()->AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
 
     /* push this message to the current frame */
     if (!bIsError && msg.PushToCecCommand(m_currentCECFrame))
@@ -312,7 +313,7 @@ bool CCECAdapterMessageQueue::Write(CCECAdapterMessage *msg)
     m_com->SetLineTimeout(msg->lineTimeout);
   }
 
-  CCECAdapterMessageQueueEntry *entry = new CCECAdapterMessageQueueEntry(msg);
+  CCECAdapterMessageQueueEntry *entry = new CCECAdapterMessageQueueEntry(this, msg);
   uint64_t iEntryId(0);
   /* add to the wait for ack queue */
   if (msg->Message() != MSGCODE_START_BOOTLOADER)
@@ -330,7 +331,7 @@ bool CCECAdapterMessageQueue::Write(CCECAdapterMessage *msg)
   {
     if (!entry->Wait(msg->transmit_timeout <= 5 ? CEC_DEFAULT_TRANSMIT_WAIT : msg->transmit_timeout))
     {
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "command '%s' was not acked by the controller", CCECAdapterMessage::ToString(msg->Message()));
+      m_com->m_callback->GetLib()->AddLog(CEC_LOG_DEBUG, "command '%s' was not acked by the controller", CCECAdapterMessage::ToString(msg->Message()));
       msg->state = ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
       bReturn = false;
     }
index e476e26660382a9359d12fc822054d99114e9c10..4646fe497cdeb7a56883ad291ed877ad8fb050d1 100644 (file)
 namespace CEC
 {
   class CUSBCECAdapterCommunication;
+  class CCECAdapterMessageQueue;
 
   class CCECAdapterMessageQueueEntry
   {
   public:
-    CCECAdapterMessageQueueEntry(CCECAdapterMessage *message);
+    CCECAdapterMessageQueueEntry(CCECAdapterMessageQueue *queue, CCECAdapterMessage *message);
     virtual ~CCECAdapterMessageQueueEntry(void);
 
     /*!
@@ -112,6 +113,7 @@ namespace CEC
      */
     void Signal(void);
 
+    CCECAdapterMessageQueue *  m_queue;
     CCECAdapterMessage *       m_message;      /**< the message that was sent */
     uint8_t                    m_iPacketsLeft; /**< the amount of acks that we're waiting on */
     bool                       m_bSucceeded;   /**< true when the command received a response, false otherwise */
@@ -123,6 +125,7 @@ namespace CEC
   class CCECAdapterMessageQueue : public PLATFORM::CThread
   {
     friend class CUSBCECAdapterCommunication;
+    friend class CCECAdapterMessageQueueEntry;
 
   public:
     /*!
index 7e33597a7a974b0dbd0b778465a97540033f4f7d..d3c674c5265f57c321fd65db0f853d0a3f4f716c 100644 (file)
@@ -38,7 +38,8 @@
 using namespace CEC;
 using namespace PLATFORM;
 
-#define ToString(p) m_processor->ToString(p)
+#define LIB_CEC     m_processor->GetLib()
+#define ToString(p) LIB_CEC->ToString(p)
 
 CCECAudioSystem::CCECAudioSystem(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */) :
     CCECBusDevice(processor, address, iPhysicalAddress),
@@ -53,7 +54,7 @@ bool CCECAudioSystem::SetAudioStatus(uint8_t status)
   CLockObject lock(m_mutex);
   if (m_audioStatus != status)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): audio status changed from %2x to %2x", GetLogicalAddressName(), m_iLogicalAddress, m_audioStatus, status);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s (%X): audio status changed from %2x to %2x", GetLogicalAddressName(), m_iLogicalAddress, m_audioStatus, status);
     m_audioStatus = status;
     return true;
   }
@@ -66,7 +67,7 @@ bool CCECAudioSystem::SetSystemAudioModeStatus(const cec_system_audio_status mod
   CLockObject lock(m_mutex);
   if (m_systemAudioStatus != mode)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): system audio mode status changed from %s to %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_systemAudioStatus), ToString(mode));
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s (%X): system audio mode status changed from %s to %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_systemAudioStatus), ToString(mode));
     m_systemAudioStatus = mode;
     return true;
   }
@@ -79,7 +80,7 @@ bool CCECAudioSystem::TransmitAudioStatus(cec_logical_address dest)
   uint8_t state;
   {
     CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %x -> %x: audio status '%2x'", m_iLogicalAddress, dest, m_audioStatus);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %x -> %x: audio status '%2x'", m_iLogicalAddress, dest, m_audioStatus);
     state = m_audioStatus;
   }
 
@@ -91,7 +92,7 @@ bool CCECAudioSystem::TransmitSetSystemAudioMode(cec_logical_address dest)
   cec_system_audio_status state;
   {
     CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %x -> %x: set system audio mode '%2x'", m_iLogicalAddress, dest, m_audioStatus);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %x -> %x: set system audio mode '%2x'", m_iLogicalAddress, dest, m_audioStatus);
     state = m_systemAudioStatus;
   }
 
@@ -103,35 +104,37 @@ bool CCECAudioSystem::TransmitSystemAudioModeStatus(cec_logical_address dest)
   cec_system_audio_status state;
   {
     CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %x -> %x: system audio mode '%s'", m_iLogicalAddress, dest, ToString(m_systemAudioStatus));
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %x -> %x: system audio mode '%s'", m_iLogicalAddress, dest, ToString(m_systemAudioStatus));
     state = m_systemAudioStatus;
   }
 
   return m_handler->TransmitSystemAudioModeStatus(m_iLogicalAddress, dest, state);
 }
 
-uint8_t CCECAudioSystem::VolumeUp(bool bSendRelease /* = true */)
+uint8_t CCECAudioSystem::VolumeUp(const cec_logical_address source, bool bSendRelease /* = true */)
 {
-  if (TransmitKeypress(CEC_USER_CONTROL_CODE_VOLUME_UP) && bSendRelease)
-    TransmitKeyRelease();
+  TransmitKeypress(source, CEC_USER_CONTROL_CODE_VOLUME_UP);
+  if (bSendRelease)
+    TransmitKeyRelease(source);
 
   CLockObject lock(m_mutex);
   return m_audioStatus;
 }
 
-uint8_t CCECAudioSystem::VolumeDown(bool bSendRelease /* = true */)
+uint8_t CCECAudioSystem::VolumeDown(const cec_logical_address source, bool bSendRelease /* = true */)
 {
-  if (TransmitKeypress(CEC_USER_CONTROL_CODE_VOLUME_DOWN) && bSendRelease)
-    TransmitKeyRelease();
+  TransmitKeypress(source, CEC_USER_CONTROL_CODE_VOLUME_DOWN);
+  if (bSendRelease)
+    TransmitKeyRelease(source);
 
   CLockObject lock(m_mutex);
   return m_audioStatus;
 }
 
-uint8_t CCECAudioSystem::MuteAudio(bool bSendRelease /* = true */)
+uint8_t CCECAudioSystem::MuteAudio(const cec_logical_address source)
 {
-  if (TransmitKeypress(CEC_USER_CONTROL_CODE_MUTE) && bSendRelease)
-    TransmitKeyRelease();
+  TransmitKeypress(source, CEC_USER_CONTROL_CODE_MUTE);
+  TransmitKeyRelease(source);
 
   CLockObject lock(m_mutex);
   return m_audioStatus;
index a340ad88ce8541d302ab1ad1fdb9e6c8554493a8..e2e3cd219030f0aa2c85ffa587321f499062fa58 100644 (file)
@@ -47,9 +47,9 @@ namespace CEC
     bool TransmitSetSystemAudioMode(cec_logical_address dest);
     bool TransmitSystemAudioModeStatus(cec_logical_address dest);
 
-    uint8_t VolumeUp(bool bSendRelease = true);
-    uint8_t VolumeDown(bool bSendRelease = true);
-    uint8_t MuteAudio(bool bSendRelease = true);
+    uint8_t VolumeUp(const cec_logical_address source, bool bSendRelease = true);
+    uint8_t VolumeDown(const cec_logical_address source, bool bSendRelease = true);
+    uint8_t MuteAudio(const cec_logical_address source);
 
     bool TransmitActiveSource(void) { return false; }
 
index 7e63a67ff8808d6da91a3835a4a590cccd68d65b..b192391906607f0da79ea1cb377aa3aa375256b5 100644 (file)
 #include "../LibCEC.h"
 #include "../platform/util/timeutils.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) LIB_CEC->ToString(p)
 
 CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogicalAddress, uint16_t iPhysicalAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */) :
   m_type                  (CEC_DEVICE_TYPE_RESERVED),
@@ -78,6 +86,62 @@ CCECBusDevice::~CCECBusDevice(void)
   delete m_handler;
 }
 
+bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */)
+{
+  bool bInitHandler(false);
+  {
+    CTryLockObject lock(m_mutex);
+    if (!lock.IsLocked())
+      return false;
+
+    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());
+        delete m_handler;
+
+        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;
+        }
+
+        m_handler->SetVendorId(m_vendor);
+        bInitHandler = true;
+      }
+    }
+  }
+
+  if (bInitHandler)
+  {
+    m_handler->InitHandler();
+
+    if (bActivateSource && IsHandledByLibCEC() && IsActiveSource())
+      m_handler->ActivateSource();
+  }
+
+  MarkReady();
+
+  return true;
+}
+
 bool CCECBusDevice::HandleCommand(const cec_command &command)
 {
   bool bHandled(false);
@@ -98,13 +162,13 @@ bool CCECBusDevice::HandleCommand(const cec_command &command)
   bHandled = m_handler->HandleCommand(command);
 
   /* change status to present */
-  if (bHandled)
+  if (bHandled && GetLogicalAddress() != CECDEVICE_BROADCAST)
   {
     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 +177,76 @@ 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)
+    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();
+  m_handler->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,51 +258,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::RequestActiveSource(bool bWaitForResponse /* = true */)
+void CCECBusDevice::SetCecVersion(const cec_version newVersion)
 {
-  bool bReturn(false);
-
-  if (MyLogicalAddressContains(m_iLogicalAddress))
-  {
-    MarkBusy();
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting active source");
-
-    bReturn = m_handler->TransmitRequestActiveSource(GetMyLogicalAddress(), bWaitForResponse);
-    MarkReady();
-  }
-  return bReturn;
+  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(bool bWaitForResponse /* = true */)
+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_NOTICE, "<< 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)
 {
-  return ToString(m_iLogicalAddress);
+  cec_version version;
+  {
+    CLockObject lock(m_mutex);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %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);
+  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);
@@ -218,46 +316,101 @@ 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_NOTICE, "<< 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)
 {
-  CLockObject lock(m_mutex);
-  return m_menuState;
+  bool bReturn(false);
+  cec_menu_language language;
+  {
+    CLockObject lock(m_mutex);
+    language = m_menuLanguage;
+  }
+
+  char lang[3];
+  {
+    CLockObject lock(m_mutex);
+    lang[0] = language.language[0];
+    lang[1] = language.language[1];
+    lang[2] = language.language[2];
+  }
+
+  MarkBusy();
+  if (lang[0] == '?' && lang[1] == '?' && lang[2] == '?')
+  {
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %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_NOTICE, "<< %s (%X) -> broadcast (F): Menu language '%s'", GetLogicalAddressName(), m_iLogicalAddress, lang);
+    bReturn = m_handler->TransmitSetMenuLanguage(m_iLogicalAddress, lang);
+  }
+  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)
 {
-  return m_processor->GetLogicalAddress();
+  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);
+    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);
@@ -270,30 +423,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_NOTICE, "<< 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)
+{
+  CStdString strDeviceName;
+  {
+    CLockObject lock(m_mutex);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %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);
+  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)
   {
@@ -306,9 +496,9 @@ uint16_t CCECBusDevice::GetPhysicalAddress(bool bSuppressUpdate /* = true */)
 
     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");
     }
   }
 
@@ -316,21 +506,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_NOTICE, "<< 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(void)
+{
+  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_NOTICE, "<< %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);
+  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);
@@ -345,55 +572,108 @@ cec_power_status CCECBusDevice::GetPowerStatus(bool bUpdate /* = false */)
 
   if (bRequestUpdate)
   {
-    CheckVendorIdRequested();
-    RequestPowerStatus();
+    CheckVendorIdRequested(initiator);
+    RequestPowerStatus(initiator);
   }
 
   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;
+  }
+}
+
+bool CCECBusDevice::RequestPowerStatus(const cec_logical_address initiator, 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);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< requesting power status of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestPowerStatus(initiator, m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
 }
 
-cec_vendor_id CCECBusDevice::GetVendorId(bool bUpdate /* = false */)
+bool CCECBusDevice::TransmitPowerState(const cec_logical_address destination)
 {
-  bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
-  bool bRequestUpdate(false);
+  cec_power_status state;
   {
     CLockObject lock(m_mutex);
-    bRequestUpdate = (bIsPresent &&
-        (bUpdate || m_vendor == CEC_VENDOR_UNKNOWN));
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, ToString(m_powerStatus));
+    state = m_powerStatus;
   }
 
-  if (bRequestUpdate)
-    RequestVendorId();
-
+  MarkBusy();
+  bool bReturn = m_handler->TransmitPowerState(m_iLogicalAddress, destination, state);
+  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);
+  {
+    CLockObject lock(m_mutex);
+    bRequestUpdate = (bIsPresent &&
+        (bUpdate || m_vendor == CEC_VENDOR_UNKNOWN));
+  }
+
+  if (bRequestUpdate)
+    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_NOTICE, "<< requesting vendor ID of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestVendorId(initiator, m_iLogicalAddress, bWaitForResponse);
     MarkReady();
 
     if (bWaitForResponse)
@@ -402,75 +682,32 @@ 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 /* = true */)
 {
-  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_NOTICE, "<< %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_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);
+  }
+  MarkReady();
+  return bReturn;
 }
 
 cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bool bSuppressPoll /* = false */)
@@ -498,283 +735,200 @@ 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)
-{
-  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;
-  }
-}
-
-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;
-  }
-}
-
-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);
-  else
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s (%x) was already marked as 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    (CEC_INVALID_PHYSICAL_ADDRESS);
-  SetOSDName       (ToString(m_iLogicalAddress));
-  SetInactiveSource();
-  m_iLastActive = 0;
-  m_unsupportedFeatures.clear();
-}
-
 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);
       SetMenuState     (CEC_MENU_STATE_ACTIVATED);
       SetCecVersion    (CEC_VERSION_1_3A);
       SetStreamPath    (CEC_INVALID_PHYSICAL_ADDRESS);
-      SetInactiveSource();
+      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;
       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));
+        LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'not present'", GetLogicalAddressName(), m_iLogicalAddress);
         ResetDeviceStatus();
         m_deviceStatus = newStatus;
       }
       break;
+    default:
+      ResetDeviceStatus();
+      break;
     }
   }
-
-  if (newStatus == CEC_DEVICE_STATUS_PRESENT)
-    RequestVendorId(false);
 }
 
-void CCECBusDevice::SetPhysicalAddress(uint16_t iNewAddress)
+void CCECBusDevice::ResetDeviceStatus(void)
 {
   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();
+
+  m_iLastActive = 0;
+  m_bVendorIdRequested = false;
+  m_unsupportedFeatures.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 /* = CEC_INVALID_PHYSICAL_ADDRESS */)
+bool CCECBusDevice::TransmitPoll(const cec_logical_address dest)
 {
-  CLockObject lock(m_mutex);
-  if (iNewAddress != m_iStreamPath)
-  {
-    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;
 
-  CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
-  if (device)
+  CCECBusDevice *destDevice = m_processor->GetDevice(destination);
+  if (destDevice->m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
+    return bReturn;
+
+  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);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, bReturn ? ">> POLL sent" : ">> POLL not sent");
+
+  CLockObject lock(m_mutex);
+  if (bReturn)
   {
-    // 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();
+    m_iLastActive = GetTimeMs();
+    destDevice->m_deviceStatus = CEC_DEVICE_STATUS_PRESENT;
   }
   else
+    destDevice->m_deviceStatus = CEC_DEVICE_STATUS_NOT_PRESENT;
+
+  MarkReady();
+  return bReturn;
+}
+
+void CCECBusDevice::HandlePoll(const cec_logical_address destination)
+{
+  if (destination >= 0 && destination < CECDEVICE_BROADCAST)
   {
-    // try to find the device with the old address, and mark it as inactive when found
-    device = m_processor->GetDeviceByPhysicalAddress(iOldAddress);
+    CCECBusDevice *device = m_processor->GetDevice(destination);
     if (device)
-      device->SetInactiveSource();
+      device->HandlePollFrom(m_iLogicalAddress);
   }
 }
 
-void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus)
+void CCECBusDevice::HandlePollFrom(const cec_logical_address initiator)
 {
-  CLockObject lock(m_mutex);
-  if (m_powerStatus != powerStatus)
-  {
-    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;
-  }
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< POLL: %s (%x) -> %s (%x)", ToString(initiator), initiator, ToString(m_iLogicalAddress), m_iLogicalAddress);
+  m_bAwaitingReceiveFailed = true;
 }
 
-void CCECBusDevice::MarkBusy(void)
+bool CCECBusDevice::HandleReceiveFailed(void)
 {
-  CLockObject handlerLock(m_handlerMutex);
-  ++m_iHandlerUseCount;
+  bool bReturn = m_bAwaitingReceiveFailed;
+  m_bAwaitingReceiveFailed = false;
+  return bReturn;
 }
 
-void CCECBusDevice::MarkReady(void)
+cec_menu_state CCECBusDevice::GetMenuState(const cec_logical_address UNUSED(initiator))
 {
-  CLockObject handlerLock(m_handlerMutex);
-  if (m_iHandlerUseCount > 0)
-    --m_iHandlerUseCount;
+  CLockObject lock(m_mutex);
+  return m_menuState;
 }
 
-bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */)
+void CCECBusDevice::SetMenuState(const cec_menu_state state)
 {
-  bool bInitHandler(false);
+  CLockObject lock(m_mutex);
+  if (m_menuState != state)
   {
-    CTryLockObject lock(m_mutex);
-    if (!lock.IsLocked())
-      return false;
-
-    CLockObject handlerLock(m_handlerMutex);
-    if (m_iHandlerUseCount > 0)
-      return false;
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): menu state set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_menuState));
+    m_menuState = state;
+  }
+}
 
-    MarkBusy();
+bool CCECBusDevice::TransmitMenuState(const cec_logical_address dest)
+{
+  cec_menu_state menuState;
+  {
+    CLockObject lock(m_mutex);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): menu state '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_menuState));
+    menuState = m_menuState;
+  }
 
-    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;
+  MarkBusy();
+  bool bReturn = m_handler->TransmitMenuState(m_iLogicalAddress, dest, menuState);
+  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::ActivateSource(void)
+{
+  MarkAsActiveSource();
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "activating source '%s'", ToString(m_iLogicalAddress));
+  MarkBusy();
+  bool bReturn = m_handler->ActivateSource();
+  MarkReady();
+  return bReturn;
+}
 
-        m_handler->SetVendorId(m_vendor);
-        bInitHandler = true;
-      }
-    }
-  }
+bool CCECBusDevice::RequestActiveSource(bool bWaitForResponse /* = true */)
+{
+  bool bReturn(false);
 
-  if (bInitHandler)
+  if (IsHandledByLibCEC())
   {
-    m_handler->InitHandler();
+    MarkBusy();
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< requesting active source");
 
-    if (bActivateSource && m_processor->GetLogicalAddresses().IsSet(m_iLogicalAddress) && m_processor->IsInitialised() && IsActiveSource())
-      m_handler->ActivateSource();
+    bReturn = m_handler->TransmitRequestActiveSource(m_iLogicalAddress, bWaitForResponse);
+    MarkReady();
   }
+  return bReturn;
+}
 
-  MarkReady();
+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);
 
-  return true;
+  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);
 }
 
-bool CCECBusDevice::SetVendorId(uint64_t iVendorId)
+void CCECBusDevice::MarkAsInactiveSource(void)
 {
-  bool bVendorChanged(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);
+    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;
 }
-//@}
 
-/** @name Transmit methods */
-//@{
 bool CCECBusDevice::TransmitActiveSource(void)
 {
   bool bSendActiveSource(false);
@@ -782,14 +936,14 @@ bool CCECBusDevice::TransmitActiveSource(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);
     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);
   }
 
   if (bSendActiveSource)
@@ -803,28 +957,13 @@ bool CCECBusDevice::TransmitActiveSource(void)
   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;
-}
-
 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;
     }
   }
@@ -840,7 +979,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;
   }
 
@@ -850,275 +989,243 @@ 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->TransmitPendingActiveSourceCommands();
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitOSDName(cec_logical_address dest)
+void CCECBusDevice::SetStreamPath(uint16_t iNewAddress, uint16_t iOldAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */)
 {
-  CStdString strDeviceName;
+  CLockObject lock(m_mutex);
+  if (iNewAddress != m_iStreamPath)
   {
-    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;
+    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->TransmitOSDName(m_iLogicalAddress, dest, strDeviceName);
-  MarkReady();
-  return bReturn;
-}
+  if (!LIB_CEC->IsValidPhysicalAddress(iNewAddress))
+    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 *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
+  if (device)
   {
-    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();
+    // 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();
   }
-  return bReturn;
-}
-
-bool CCECBusDevice::TransmitPhysicalAddress(void)
-{
-  uint16_t iPhysicalAddress;
-  cec_device_type type;
+  else
   {
-    CLockObject lock(m_mutex);
-    if (m_iPhysicalAddress == CEC_INVALID_PHYSICAL_ADDRESS)
-      return false;
-
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): physical adddress %4x", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
-    iPhysicalAddress = m_iPhysicalAddress;
-    type = m_type;
+    // 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();
   }
-
-  MarkBusy();
-  bool bReturn = m_handler->TransmitPhysicalAddress(m_iLogicalAddress, iPhysicalAddress, type);
-  MarkReady();
-  return bReturn;
 }
 
-bool CCECBusDevice::TransmitSetMenuLanguage(cec_logical_address dest)
+bool CCECBusDevice::PowerOn(const cec_logical_address initiator)
 {
   bool bReturn(false);
-  cec_menu_language language = GetMenuLanguage();
-
-  char lang[3];
-  {
-    CLockObject lock(m_mutex);
-    lang[0] = language.language[0];
-    lang[1] = language.language[1];
-    lang[2] = language.language[2];
-  }
+  GetVendorId(initiator); // ensure that we got the vendor id, because the implementations vary per vendor
 
   MarkBusy();
-  if (lang[0] == '?' && lang[1] == '?' && lang[2] == '?')
+  cec_power_status currentStatus = GetPowerStatus(initiator, false);
+  if (currentStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON &&
+    currentStatus != CEC_POWER_STATUS_ON)
   {
-      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);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< powering on '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
+    if (m_handler->PowerOn(initiator, m_iLogicalAddress))
+    {
+      SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
       bReturn = 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);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "'%s' (%X) is already '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(currentStatus));
   }
+
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitPoll(cec_logical_address dest)
+bool CCECBusDevice::Standby(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
 
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< putting '%s' (%X) in standby mode", GetLogicalAddressName(), m_iLogicalAddress);
   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)
-  {
-    m_iLastActive = GetTimeMs();
-    destDevice->m_deviceStatus = CEC_DEVICE_STATUS_PRESENT;
-  }
-  else
-    destDevice->m_deviceStatus = CEC_DEVICE_STATUS_NOT_PRESENT;
-
+  bool bReturn = m_handler->TransmitStandby(initiator, m_iLogicalAddress);
   MarkReady();
   return bReturn;
 }
 
-bool CCECBusDevice::TransmitPowerState(cec_logical_address dest)
+bool CCECBusDevice::NeedsPoll(void)
 {
-  cec_power_status state;
+  bool bSendPoll(false);
+  cec_logical_address pollAddress(CECDEVICE_UNKNOWN);
+  switch (m_iLogicalAddress)
   {
-    CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_powerStatus));
-    state = m_powerStatus;
+  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();
-  bool bReturn = m_handler->TransmitPowerState(m_iLogicalAddress, dest, state);
-  MarkReady();
-  return bReturn;
+  if (!bSendPoll && pollAddress != CECDEVICE_UNKNOWN)
+  {
+    CCECBusDevice *device = m_processor->GetDevice(pollAddress);
+    if (device)
+    {
+      cec_bus_device_status status = device->GetStatus();
+      bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
+    }
+    else
+    {
+      bSendPoll = true;
+    }
+  }
+
+  return bSendPoll;
 }
 
-bool CCECBusDevice::TransmitVendorID(cec_logical_address dest, bool bSendAbort /* = true */)
+void CCECBusDevice::CheckVendorIdRequested(const cec_logical_address initiator)
 {
-  bool bReturn(false);
-  uint64_t iVendorId;
+  bool bRequestVendorId(false);
   {
     CLockObject lock(m_mutex);
-    iVendorId = (uint64_t)m_vendor;
+    bRequestVendorId = !m_bVendorIdRequested;
+    m_bVendorIdRequested = true;
   }
 
-  MarkBusy();
-  if (iVendorId == CEC_VENDOR_UNKNOWN)
-  {
-    if (bSendAbort)
-    {
-      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;
-    }
-  }
-  else
+  if (bRequestVendorId)
   {
-    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);
+    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_DEBUG, "'%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)
 {
-  // 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)
-    return;
+  return AsTuner(this);
+}
 
-  {
-    CLockObject lock(m_mutex);
-    if (m_unsupportedFeatures.find(opcode) == m_unsupportedFeatures.end())
-    {
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "marking opcode '%s' as unsupported feature for device '%s'", ToString(opcode), GetLogicalAddressName());
-      m_unsupportedFeatures.insert(opcode);
-    }
-  }
+CCECTV *CCECBusDevice::AsTV(void)
+{
+  return AsTV(this);
+}
 
-  // signal threads that are waiting for a reponse
-  MarkBusy();
-  m_handler->SignalOpcode(cec_command::GetResponseOpcode(opcode));
-  MarkReady();
+CCECAudioSystem *CCECBusDevice::AsAudioSystem(CCECBusDevice *device)
+{
+  if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
+    return static_cast<CCECAudioSystem *>(device);
+  return NULL;
 }
 
-bool CCECBusDevice::ActivateSource(void)
+CCECPlaybackDevice *CCECBusDevice::AsPlaybackDevice(CCECBusDevice *device)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "activating source '%s'", ToString(m_iLogicalAddress));
-  MarkBusy();
-  bool bReturn = m_handler->ActivateSource();
-  MarkReady();
-  return bReturn;
+  if (device &&
+      (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE ||
+       device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE))
+    return static_cast<CCECPlaybackDevice *>(device);
+  return NULL;
 }
 
-void CCECBusDevice::HandlePoll(cec_logical_address destination)
+CCECRecordingDevice *CCECBusDevice::AsRecordingDevice(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_RECORDING_DEVICE)
+    return static_cast<CCECRecordingDevice *>(device);
+  return NULL;
 }
 
-void CCECBusDevice::HandlePollFrom(cec_logical_address initiator)
+CCECTuner *CCECBusDevice::AsTuner(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_TUNER)
+    return static_cast<CCECTuner *>(device);
+  return NULL;
 }
 
-bool CCECBusDevice::HandleReceiveFailed(void)
+CCECTV *CCECBusDevice::AsTV(CCECBusDevice *device)
 {
-  bool bReturn = m_bAwaitingReceiveFailed;
-  m_bAwaitingReceiveFailed = false;
-  return bReturn;
+  if (device && device->GetType() == CEC_DEVICE_TYPE_TV)
+    return static_cast<CCECTV *>(device);
+  return NULL;
 }
 
-void CCECBusDevice::CheckVendorIdRequested(void)
+void CCECBusDevice::MarkBusy(void)
 {
-  bool bRequestVendorId(false);
-  {
-    CLockObject lock(m_mutex);
-    bRequestVendorId = !m_bVendorIdRequested;
-    m_bVendorIdRequested = true;
-  }
+  CLockObject handlerLock(m_handlerMutex);
+  ++m_iHandlerUseCount;
+}
 
-  if (bRequestVendorId)
+void CCECBusDevice::MarkReady(void)
+{
+  CLockObject handlerLock(m_handlerMutex);
+  if (m_iHandlerUseCount > 0)
+    --m_iHandlerUseCount;
+}
+
+bool CCECBusDevice::TryLogicalAddress(void)
+{
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "trying logical address '%s'", GetLogicalAddressName());
+
+  if (!TransmitPoll(m_iLogicalAddress))
   {
-    ReplaceHandler(false);
-    GetVendorId();
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "using logical address '%s'", GetLogicalAddressName());
+    SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
+
+    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);
 }
-
-//@}
index 95b1bad34155c40b07d10420fc834d4d8cafea3b..0e11d42725a5591f5670b60a5e02bf52501afa68 100644 (file)
 
 namespace CEC
 {
+  class CCECClient;
   class CCECProcessor;
   class CCECCommandHandler;
+  class CCECAudioSystem;
+  class CCECPlaybackDevice;
+  class CCECRecordingDevice;
+  class CCECTuner;
+  class CCECTV;
 
   class CCECBusDevice
   {
@@ -49,83 +55,109 @@ namespace CEC
     CCECBusDevice(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress = CEC_INVALID_PHYSICAL_ADDRESS);
     virtual ~CCECBusDevice(void);
 
-    virtual bool HandleCommand(const cec_command &command);
-    virtual bool PowerOn(void);
-    virtual bool Standby(void);
-
-    virtual cec_version           GetCecVersion(bool bUpdate = false);
-    virtual CCECCommandHandler *  GetHandler(void) const { return m_handler; };
-    virtual uint64_t              GetLastActive(void) const { return m_iLastActive; }
+    virtual bool                  ReplaceHandler(bool bActivateSource = true);
+    virtual CCECCommandHandler *  GetHandler(void) const        { return m_handler; };
+    virtual CCECProcessor *       GetProcessor(void) const      { return m_processor; }
+    virtual uint64_t              GetLastActive(void) const     { return m_iLastActive; }
+    virtual cec_device_type       GetType(void) const           { return m_type; }
     virtual cec_logical_address   GetLogicalAddress(void) const { return m_iLogicalAddress; }
     virtual const char*           GetLogicalAddressName(void) const;
-    virtual cec_menu_language &   GetMenuLanguage(bool bUpdate = false);
-    virtual cec_menu_state        GetMenuState(void);
-    virtual cec_logical_address   GetMyLogicalAddress(void) const;
-    virtual uint16_t              GetMyPhysicalAddress(void) const;
-    virtual CStdString            GetOSDName(bool bUpdate = false);
-    virtual uint16_t              GetPhysicalAddress(bool bSuppressUpdate = true);
-    virtual cec_power_status      GetPowerStatus(bool bUpdate = false);
-    virtual CCECProcessor *       GetProcessor(void) const { return m_processor; }
-    virtual cec_device_type       GetType(void) const { return m_type; }
-    virtual cec_vendor_id         GetVendorId(bool bUpdate = false);
-    virtual const char *          GetVendorName(bool bUpdate = false);
-    virtual bool                  MyLogicalAddressContains(cec_logical_address address) const;
-    virtual cec_bus_device_status GetStatus(bool bForcePoll = false, bool bSuppressPoll = false);
-    virtual bool                  IsActiveSource(void) const { return m_bActiveSource; }
+    virtual bool                  IsPresent(void);
+    virtual bool                  IsHandledByLibCEC(void);
+
+    virtual bool                  HandleCommand(const cec_command &command);
     virtual bool                  IsUnsupportedFeature(cec_opcode opcode);
     virtual void                  SetUnsupportedFeature(cec_opcode opcode);
-    virtual void                  HandlePoll(cec_logical_address destination);
-    virtual void                  HandlePollFrom(cec_logical_address initiator);
+
+    virtual bool                  TransmitKeypress(const cec_logical_address initiator, cec_user_control_code key, bool bWait = true);
+    virtual bool                  TransmitKeyRelease(const cec_logical_address initiator, bool bWait = true);
+
+    virtual cec_version           GetCecVersion(const cec_logical_address initiator, bool bUpdate = false);
+    virtual void                  SetCecVersion(const cec_version newVersion);
+    virtual bool                  RequestCecVersion(const cec_logical_address initiator, bool bWaitForResponse = true);
+    virtual bool                  TransmitCECVersion(const cec_logical_address destination);
+
+    virtual cec_menu_language &   GetMenuLanguage(const cec_logical_address initiator, bool bUpdate = false);
+    virtual void                  SetMenuLanguage(const char *strLanguage);
+    virtual void                  SetMenuLanguage(const cec_menu_language &menuLanguage);
+    virtual bool                  RequestMenuLanguage(const cec_logical_address initiator, bool bWaitForResponse = true);
+    virtual bool                  TransmitSetMenuLanguage(const cec_logical_address destination);
+
+    virtual bool                  TransmitOSDString(const cec_logical_address destination, cec_display_control duration, const char *strMessage);
+
+    virtual CStdString            GetCurrentOSDName(void);
+    virtual CStdString            GetOSDName(const cec_logical_address initiator, bool bUpdate = false);
+    virtual void                  SetOSDName(CStdString strName);
+    virtual bool                  RequestOSDName(const cec_logical_address source, bool bWaitForResponse = true);
+    virtual bool                  TransmitOSDName(const cec_logical_address destination);
+
+    virtual uint16_t              GetCurrentPhysicalAddress(void);
+    virtual bool                  HasValidPhysicalAddress(void);
+    virtual uint16_t              GetPhysicalAddress(const cec_logical_address initiator, bool bSuppressUpdate = false);
+    virtual bool                  SetPhysicalAddress(uint16_t iNewAddress);
+    virtual bool                  RequestPhysicalAddress(const cec_logical_address initiator, bool bWaitForResponse = true);
+    virtual bool                  TransmitPhysicalAddress(void);
+
+    virtual cec_power_status      GetCurrentPowerStatus(void);
+    virtual cec_power_status      GetPowerStatus(const cec_logical_address initiator, bool bUpdate = false);
+    virtual void                  SetPowerStatus(const cec_power_status powerStatus);
+    virtual bool                  RequestPowerStatus(const cec_logical_address initiator, bool bWaitForResponse = true);
+    virtual bool                  TransmitPowerState(const cec_logical_address destination);
+
+    virtual cec_vendor_id         GetCurrentVendorId(void);
+    virtual cec_vendor_id         GetVendorId(const cec_logical_address initiator, bool bUpdate = false);
+    virtual const char *          GetVendorName(const cec_logical_address initiator, bool bUpdate = false);
+    virtual bool                  SetVendorId(uint64_t iVendorId);
+    virtual bool                  RequestVendorId(const cec_logical_address initiator, bool bWaitForResponse = true);
+    virtual bool                  TransmitVendorID(const cec_logical_address destination, bool bSendAbort = true);
+
+    virtual cec_bus_device_status GetCurrentStatus(void) { return GetStatus(false, true); }
+    virtual cec_bus_device_status GetStatus(bool bForcePoll = false, bool bSuppressPoll = false);
+    virtual void                  SetDeviceStatus(const cec_bus_device_status newStatus);
+    virtual void                  ResetDeviceStatus(void);
+    virtual bool                  TransmitPoll(const cec_logical_address destination);
+    virtual void                  HandlePoll(const cec_logical_address destination);
+    virtual void                  HandlePollFrom(const cec_logical_address initiator);
     virtual bool                  HandleReceiveFailed(void);
 
-    virtual void SetInactiveSource(void);
-    virtual void SetActiveSource(void);
-    virtual bool TryLogicalAddress(void);
-    virtual bool ActivateSource(void);
-
-    virtual void SetDeviceStatus(const cec_bus_device_status newStatus);
-    virtual void SetPhysicalAddress(uint16_t iNewAddress);
-    virtual void SetStreamPath(uint16_t iNewAddress, uint16_t iOldAddress = CEC_INVALID_PHYSICAL_ADDRESS);
-    virtual void SetCecVersion(const cec_version newVersion);
-    virtual void SetMenuLanguage(const cec_menu_language &menuLanguage);
-    virtual void SetOSDName(CStdString strName);
-    virtual void SetMenuState(const cec_menu_state state);
-    virtual bool SetVendorId(uint64_t iVendorId);
-    virtual void SetPowerStatus(const cec_power_status powerStatus);
-
-    virtual bool TransmitActiveSource(void);
-    virtual bool TransmitCECVersion(cec_logical_address dest);
-    virtual bool TransmitImageViewOn(void);
-    virtual bool TransmitInactiveSource(void);
-    virtual bool TransmitMenuState(cec_logical_address dest);
-    virtual bool TransmitOSDName(cec_logical_address dest);
-    virtual bool TransmitOSDString(cec_logical_address dest, cec_display_control duration, const char *strMessage);
-    virtual bool TransmitPhysicalAddress(void);
-    virtual bool TransmitSetMenuLanguage(cec_logical_address dest);
-    virtual bool TransmitPowerState(cec_logical_address dest);
-    virtual bool TransmitPoll(cec_logical_address dest);
-    virtual bool TransmitVendorID(cec_logical_address dest, bool bSendAbort = true);
-    virtual bool TransmitKeypress(cec_user_control_code key, bool bWait = true);
-    virtual bool TransmitKeyRelease(bool bWait = true);
-
-    bool ReplaceHandler(bool bActivateSource = true);
-    virtual bool TransmitPendingActiveSourceCommands(void);
-
-    virtual bool RequestActiveSource(bool bWaitForResponse = true);
+    virtual cec_menu_state        GetMenuState(const cec_logical_address initiator);
+    virtual void                  SetMenuState(const cec_menu_state state);
+    virtual bool                  TransmitMenuState(const cec_logical_address destination);
+
+    virtual bool                  ActivateSource(void);
+    virtual bool                  IsActiveSource(void) const    { return m_bActiveSource; }
+    virtual bool                  RequestActiveSource(bool bWaitForResponse = true);
+    virtual void                  MarkAsActiveSource(void);
+    virtual void                  MarkAsInactiveSource(void);
+    virtual bool                  TransmitActiveSource(void);
+    virtual bool                  TransmitImageViewOn(void);
+    virtual bool                  TransmitInactiveSource(void);
+    virtual bool                  TransmitPendingActiveSourceCommands(void);
+    virtual void                  SetStreamPath(uint16_t iNewAddress, uint16_t iOldAddress = CEC_INVALID_PHYSICAL_ADDRESS);
+
+    virtual bool                  PowerOn(const cec_logical_address initiator);
+    virtual bool                  Standby(const cec_logical_address initiator);
+
+    virtual bool                  TryLogicalAddress(void);
+
+    CCECClient *                  GetClient(void);
+
+           CCECAudioSystem *      AsAudioSystem(void);
+    static CCECAudioSystem *      AsAudioSystem(CCECBusDevice *device);
+           CCECPlaybackDevice *   AsPlaybackDevice(void);
+    static CCECPlaybackDevice *   AsPlaybackDevice(CCECBusDevice *device);
+           CCECRecordingDevice *  AsRecordingDevice(void);
+    static CCECRecordingDevice *  AsRecordingDevice(CCECBusDevice *device);
+           CCECTuner *            AsTuner(void);
+    static CCECTuner *            AsTuner(CCECBusDevice *device);
+           CCECTV *               AsTV(void);
+    static CCECTV *               AsTV(CCECBusDevice *device);
 
   protected:
-    void ResetDeviceStatus(void);
-    void CheckVendorIdRequested(void);
+    void CheckVendorIdRequested(const cec_logical_address source);
     void MarkBusy(void);
     void MarkReady(void);
 
-    bool RequestCecVersion(bool bWaitForResponse = true);
-    bool RequestMenuLanguage(bool bWaitForResponse = true);
-    bool RequestPowerStatus(bool bWaitForResponse = true);
-    bool RequestVendorId(bool bWaitForResponse = true);
-    bool RequestPhysicalAddress(bool bWaitForResponse = true);
-    bool RequestOSDName(bool bWaitForResponse = true);
-
     bool NeedsPoll(void);
 
     cec_device_type       m_type;
diff --git a/src/lib/devices/CECDeviceMap.cpp b/src/lib/devices/CECDeviceMap.cpp
new file mode 100644 (file)
index 0000000..3c70603
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * 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 an original work, containing original code.
+ *
+ * libCEC(R) is a trademark of Pulse-Eight Limited.
+ *
+ * This program is dual-licensed; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Alternatively, you can license this library under a commercial license,
+ * please contact Pulse-Eight Licensing for more information.
+ *
+ * For more information contact:
+ * Pulse-Eight Licensing       <license@pulse-eight.com>
+ *     http://www.pulse-eight.com/
+ *     http://www.pulse-eight.net/
+ */
+
+#include "CECDeviceMap.h"
+#include "CECAudioSystem.h"
+#include "CECPlaybackDevice.h"
+#include "CECRecordingDevice.h"
+#include "CECTuner.h"
+#include "CECTV.h"
+#include "../CECProcessor.h"
+
+using namespace std;
+using namespace CEC;
+
+CCECDeviceMap::CCECDeviceMap(CCECProcessor *processor) :
+    m_processor(processor)
+{
+  for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
+  {
+    switch(iPtr)
+    {
+    case CECDEVICE_AUDIOSYSTEM:
+      m_busDevices.insert(make_pair<cec_logical_address, CCECBusDevice *>((cec_logical_address)iPtr, new CCECAudioSystem(processor, (cec_logical_address) iPtr)));
+      break;
+    case CECDEVICE_PLAYBACKDEVICE1:
+    case CECDEVICE_PLAYBACKDEVICE2:
+    case CECDEVICE_PLAYBACKDEVICE3:
+      m_busDevices.insert(make_pair<cec_logical_address, CCECBusDevice *>((cec_logical_address)iPtr, new CCECPlaybackDevice(processor, (cec_logical_address) iPtr)));
+      break;
+    case CECDEVICE_RECORDINGDEVICE1:
+    case CECDEVICE_RECORDINGDEVICE2:
+    case CECDEVICE_RECORDINGDEVICE3:
+      m_busDevices.insert(make_pair<cec_logical_address, CCECBusDevice *>((cec_logical_address)iPtr, new CCECRecordingDevice(processor, (cec_logical_address) iPtr)));
+      break;
+    case CECDEVICE_TUNER1:
+    case CECDEVICE_TUNER2:
+    case CECDEVICE_TUNER3:
+    case CECDEVICE_TUNER4:
+      m_busDevices.insert(make_pair<cec_logical_address, CCECBusDevice *>((cec_logical_address)iPtr, new CCECTuner(processor, (cec_logical_address) iPtr)));
+      break;
+    case CECDEVICE_TV:
+      m_busDevices.insert(make_pair<cec_logical_address, CCECBusDevice *>((cec_logical_address)iPtr, new CCECTV(processor, (cec_logical_address) iPtr)));
+      break;
+    default:
+      m_busDevices.insert(make_pair<cec_logical_address, CCECBusDevice *>((cec_logical_address)iPtr, new CCECBusDevice(processor, (cec_logical_address) iPtr)));
+      break;
+    }
+  }
+}
+CCECDeviceMap::~CCECDeviceMap(void)
+{
+  Clear();
+}
+
+CECDEVICEMAP::iterator CCECDeviceMap::Begin(void)
+{
+  return m_busDevices.begin();
+}
+
+CECDEVICEMAP::iterator CCECDeviceMap::End(void)
+{
+  return m_busDevices.end();
+}
+
+void CCECDeviceMap::ResetDeviceStatus(void)
+{
+  for (CECDEVICEMAP::iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+    it->second->ResetDeviceStatus();
+}
+
+CCECBusDevice *CCECDeviceMap::operator[] (cec_logical_address iAddress) const
+{
+  return At(iAddress);
+}
+
+CCECBusDevice *CCECDeviceMap::operator[] (uint8_t iAddress) const
+{
+  return At(iAddress);
+}
+
+CCECBusDevice *CCECDeviceMap::At(cec_logical_address iAddress) const
+{
+  return At((uint8_t) iAddress);
+}
+
+CCECBusDevice *CCECDeviceMap::At(uint8_t iAddress) const
+{
+  CECDEVICEMAP::const_iterator it = m_busDevices.find((cec_logical_address)iAddress);
+  if (it != m_busDevices.end())
+    return it->second;
+  return NULL;
+}
+
+void CCECDeviceMap::Clear(void)
+{
+  for (CECDEVICEMAP::iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+    delete it->second;
+  m_busDevices.clear();
+}
+
+CCECBusDevice *CCECDeviceMap::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */)
+{
+  CCECBusDevice *device(NULL);
+
+  // check each device until we found a match
+  for (CECDEVICEMAP::iterator it = m_busDevices.begin(); !device && it != m_busDevices.end(); it++)
+  {
+    if (it->second->GetPhysicalAddress(m_processor->GetLogicalAddress(), bSuppressUpdate) == iPhysicalAddress)
+      device = it->second;
+  }
+
+  return device;
+}
+
+void CCECDeviceMap::Get(CECDEVICEVEC &devices) const
+{
+  for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+    devices.push_back(it->second);
+}
+
+void CCECDeviceMap::GetByLogicalAddresses(CECDEVICEVEC &devices, const cec_logical_addresses &addresses)
+{
+  for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+  {
+    if (addresses.IsSet(it->first))
+      devices.push_back(it->second);
+  }
+}
+
+void CCECDeviceMap::GetByType(const cec_device_type type, CECDEVICEVEC &devices) const
+{
+  for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+    if (it->second->GetType() == type)
+      devices.push_back(it->second);
+}
+
+void CCECDeviceMap::GetLibCECControlled(CECDEVICEVEC &devices) const
+{
+  for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+    if (it->second->IsHandledByLibCEC())
+      devices.push_back(it->second);
+}
+
+void CCECDeviceMap::GetActive(CECDEVICEVEC &devices) const
+{
+  for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+  {
+    cec_bus_device_status status = it->second->GetStatus();
+    if (status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC ||
+        status == CEC_DEVICE_STATUS_PRESENT)
+      devices.push_back(it->second);
+  }
+}
+
+void CCECDeviceMap::GetPowerOffDevices(const libcec_configuration &configuration, CECDEVICEVEC &devices) const
+{
+  for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+  {
+    if (configuration.powerOffDevices[(uint8_t)it->first])
+      devices.push_back(it->second);
+  }
+}
+
+void CCECDeviceMap::GetWakeDevices(const libcec_configuration &configuration, CECDEVICEVEC &devices) const
+{
+  for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+  {
+    if (configuration.wakeDevices[(uint8_t)it->first])
+      devices.push_back(it->second);
+  }
+}
+
+CCECBusDevice *CCECDeviceMap::GetActiveSource(void) const
+{
+  for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++)
+  {
+    if (it->second->IsActiveSource())
+      return it->second;
+  }
+  return NULL;
+}
+
+void CCECDeviceMap::FilterLibCECControlled(CECDEVICEVEC &devices)
+{
+  CECDEVICEVEC newDevices;
+  for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
+  {
+    if ((*it)->IsHandledByLibCEC())
+      newDevices.push_back(*it);
+  }
+  devices = newDevices;
+}
+
+void CCECDeviceMap::FilterActive(CECDEVICEVEC &devices)
+{
+  CECDEVICEVEC newDevices;
+  for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
+  {
+    cec_bus_device_status status = (*it)->GetCurrentStatus();
+    if (status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC ||
+        status == CEC_DEVICE_STATUS_PRESENT)
+      newDevices.push_back(*it);
+  }
+  devices = newDevices;
+}
+
+void CCECDeviceMap::FilterTypes(const cec_device_type_list &types, CECDEVICEVEC &devices)
+{
+  cec_device_type_list t(types);//silly, but needed to retain abi
+  CECDEVICEVEC newDevices;
+  for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
+  {
+    if (t.IsSet((*it)->GetType()))
+      newDevices.push_back(*it);
+  }
+  devices = newDevices;
+}
+
+void CCECDeviceMap::FilterType(const cec_device_type type, CECDEVICEVEC &devices)
+{
+  CECDEVICEVEC newDevices;
+  for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
+  {
+    if ((*it)->GetType() == type)
+      newDevices.push_back(*it);
+  }
+  devices = newDevices;
+}
+
+cec_logical_addresses CCECDeviceMap::ToLogicalAddresses(const CECDEVICEVEC &devices)
+{
+  cec_logical_addresses addresses;
+  addresses.Clear();
+  for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
+    addresses.Set((*it)->GetLogicalAddress());
+  return addresses;
+}
diff --git a/src/lib/devices/CECDeviceMap.h b/src/lib/devices/CECDeviceMap.h
new file mode 100644 (file)
index 0000000..128f574
--- /dev/null
@@ -0,0 +1,83 @@
+#pragma once
+/*
+ * 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 an original work, containing original code.
+ *
+ * libCEC(R) is a trademark of Pulse-Eight Limited.
+ *
+ * This program is dual-licensed; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Alternatively, you can license this library under a commercial license,
+ * please contact Pulse-Eight Licensing for more information.
+ *
+ * For more information contact:
+ * Pulse-Eight Licensing       <license@pulse-eight.com>
+ *     http://www.pulse-eight.com/
+ *     http://www.pulse-eight.net/
+ */
+
+#include "../../../include/cectypes.h"
+#include <map>
+#include <vector>
+
+namespace CEC
+{
+  class CCECBusDevice;
+
+  typedef std::map<cec_logical_address, CCECBusDevice *> CECDEVICEMAP;
+  typedef std::vector<CCECBusDevice *>                   CECDEVICEVEC;
+
+  class CCECProcessor;
+
+  class CCECDeviceMap
+  {
+  public:
+    CCECDeviceMap(CCECProcessor *processor);
+    virtual ~CCECDeviceMap(void);
+    CECDEVICEMAP::iterator  Begin(void);
+    CECDEVICEMAP::iterator  End(void);
+    void                    ResetDeviceStatus(void);
+    CCECBusDevice *         operator[] (cec_logical_address iAddress) const;
+    CCECBusDevice *         operator[] (uint8_t iAddress) const;
+    CCECBusDevice *         At(cec_logical_address iAddress) const;
+    CCECBusDevice *         At(uint8_t iAddress) const;
+    CCECBusDevice *         GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate = true);
+
+    void Get(CECDEVICEVEC &devices) const;
+    void GetLibCECControlled(CECDEVICEVEC &devices) const;
+    void GetByLogicalAddresses(CECDEVICEVEC &devices, const cec_logical_addresses &addresses);
+    void GetActive(CECDEVICEVEC &devices) const;
+    void GetByType(const cec_device_type type, CECDEVICEVEC &devices) const;
+
+    void GetPowerOffDevices(const libcec_configuration &configuration, CECDEVICEVEC &devices) const;
+    void GetWakeDevices(const libcec_configuration &configuration, CECDEVICEVEC &devices) const;
+
+    CCECBusDevice *GetActiveSource(void) const;
+
+    static void FilterLibCECControlled(CECDEVICEVEC &devices);
+    static void FilterActive(CECDEVICEVEC &devices);
+    static void FilterTypes(const cec_device_type_list &types, CECDEVICEVEC &devices);
+    static void FilterType(const cec_device_type type, CECDEVICEVEC &devices);
+    static cec_logical_addresses ToLogicalAddresses(const CECDEVICEVEC &devices);
+  private:
+    void Clear(void);
+
+    CECDEVICEMAP   m_busDevices;
+    CCECProcessor *m_processor;
+  };
+}
index 522b88f49a572d3042794e1be1e4ac3b452bb2a7..d1ac742c0ec39d4673c7dd343ea327fdffb3a014 100644 (file)
@@ -38,7 +38,7 @@
 using namespace CEC;
 using namespace PLATFORM;
 
-#define ToString(p) m_processor->ToString(p)
+#define ToString(p) m_processor->GetLib()->ToString(p)
 
 CCECPlaybackDevice::CCECPlaybackDevice(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */) :
     CCECBusDevice(processor, address, iPhysicalAddress),
@@ -48,7 +48,7 @@ CCECPlaybackDevice::CCECPlaybackDevice(CCECProcessor *processor, cec_logical_add
   m_type = CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
 }
 
-cec_deck_info CCECPlaybackDevice::GetDeckStatus(void)
+cec_deck_info CCECPlaybackDevice::GetDeckStatus(const cec_logical_address UNUSED(initiator))
 {
   CLockObject lock(m_mutex);
   return m_deckStatus;
@@ -59,12 +59,12 @@ void CCECPlaybackDevice::SetDeckStatus(cec_deck_info deckStatus)
   CLockObject lock(m_mutex);
   if (m_deckStatus != deckStatus)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): deck status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_deckStatus), ToString(deckStatus));
+    m_processor->GetLib()->AddLog(CEC_LOG_DEBUG, ">> %s (%X): deck status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_deckStatus), ToString(deckStatus));
     m_deckStatus = deckStatus;
   }
 }
 
-cec_deck_control_mode CCECPlaybackDevice::GetDeckControlMode(void)
+cec_deck_control_mode CCECPlaybackDevice::GetDeckControlMode(const cec_logical_address UNUSED(initiator))
 {
   CLockObject lock(m_mutex);
   return m_deckControlMode;
@@ -75,7 +75,7 @@ void CCECPlaybackDevice::SetDeckControlMode(cec_deck_control_mode mode)
   CLockObject lock(m_mutex);
   if (m_deckControlMode != mode)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %s (%X): deck control mode changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_deckControlMode), ToString(mode));
+    m_processor->GetLib()->AddLog(CEC_LOG_DEBUG, ">> %s (%X): deck control mode changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_deckControlMode), ToString(mode));
     m_deckControlMode = mode;
   }
 }
@@ -85,9 +85,17 @@ bool CCECPlaybackDevice::TransmitDeckStatus(cec_logical_address dest)
   cec_deck_info state;
   {
     CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): deck status '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_deckStatus));
+    m_processor->GetLib()->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): deck status '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_deckStatus));
     state = m_deckStatus;
   }
 
   return m_handler->TransmitDeckStatus(m_iLogicalAddress, dest, state);
 }
+
+void CCECPlaybackDevice::ResetDeviceStatus(void)
+{
+  CLockObject lock(m_mutex);
+  m_deckStatus      = CEC_DECK_INFO_STOP;
+  m_deckControlMode = CEC_DECK_CONTROL_MODE_STOP;
+  CCECBusDevice::ResetDeviceStatus();
+}
index 414f8478517562b71366d592fdc5f60222827aaf..c21bf30f206a8ea107a19c0309692d2f108faed9 100644 (file)
@@ -41,14 +41,16 @@ namespace CEC
     CCECPlaybackDevice(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress = CEC_INVALID_PHYSICAL_ADDRESS);
     virtual ~CCECPlaybackDevice(void) {};
 
-    cec_deck_info GetDeckStatus(void);
-    cec_deck_control_mode GetDeckControlMode(void);
+    cec_deck_info GetDeckStatus(const cec_logical_address initiator);
+    cec_deck_control_mode GetDeckControlMode(const cec_logical_address initiator);
 
     void SetDeckStatus(cec_deck_info deckStatus);
     void SetDeckControlMode(cec_deck_control_mode mode);
 
     bool TransmitDeckStatus(cec_logical_address dest);
 
+    virtual void ResetDeviceStatus(void);
+
   protected:
     cec_deck_info         m_deckStatus;
     cec_deck_control_mode m_deckControlMode;
index c5f4535af4254b7b83028e63b011c5138bb5b154..2a82902b4d63286e598ad25bf1d2cb8d48bda4ea 100644 (file)
 #include "CECRecordingDevice.h"
 
 using namespace CEC;
+using namespace PLATFORM;
 
 CCECRecordingDevice::CCECRecordingDevice(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */) :
-    CCECBusDevice(processor, address, iPhysicalAddress),
-    m_playbackDevice(processor, address, iPhysicalAddress),
+    CCECPlaybackDevice(processor, address, iPhysicalAddress),
     m_tuner(processor, address, iPhysicalAddress)
 {
   m_type = CEC_DEVICE_TYPE_RECORDING_DEVICE;
 }
 
-cec_deck_info CCECRecordingDevice::GetDeckStatus(void)
+void CCECRecordingDevice::ResetDeviceStatus(void)
 {
-  return m_playbackDevice.GetDeckStatus();
-}
-
-cec_deck_control_mode CCECRecordingDevice::GetDeckControlMode(void)
-{
-  return m_playbackDevice.GetDeckControlMode();
-}
-
-void CCECRecordingDevice::SetDeckStatus(cec_deck_info deckStatus)
-{
-  m_playbackDevice.SetDeckStatus(deckStatus);
-}
-
-void CCECRecordingDevice::SetDeckControlMode(cec_deck_control_mode mode)
-{
-  m_playbackDevice.SetDeckControlMode(mode);
-}
-
-bool CCECRecordingDevice::TransmitDeckStatus(cec_logical_address dest)
-{
-  return m_playbackDevice.TransmitDeckStatus(dest);
+  CLockObject lock(m_mutex);
+  m_tuner.ResetDeviceStatus();
+  CCECPlaybackDevice::ResetDeviceStatus();
 }
index f64b95b357a88683691fc6a9dcaf8004effa4a5d..f37c37b1a02fd62b9f5222bf9b96fb2b849bbd3c 100644 (file)
 
 namespace CEC
 {
-  class CCECRecordingDevice : public CCECBusDevice
+  class CCECRecordingDevice : public CCECPlaybackDevice
   {
   public:
     CCECRecordingDevice(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress = CEC_INVALID_PHYSICAL_ADDRESS);
     virtual ~CCECRecordingDevice(void) {};
 
-    /* playback device methods */
-    cec_deck_info GetDeckStatus(void);
-    cec_deck_control_mode GetDeckControlMode(void);
-
-    void SetDeckStatus(cec_deck_info deckStatus);
-    void SetDeckControlMode(cec_deck_control_mode mode);
-
-    bool TransmitDeckStatus(cec_logical_address dest);
-
-    /* tuner methods */
-    //TODO
+    virtual void ResetDeviceStatus(void);
 
+    /* TODO: tuner methods */
   protected:
-    CCECPlaybackDevice m_playbackDevice;
     CCECTuner          m_tuner;
   };
 }
index 5c2e909f26296ed7f8972c02e43e6640480afc08..b26dbdc8451fa4f857d0178b3edb34458085c68c 100644 (file)
 #include "CECTV.h"
 
 using namespace CEC;
+using namespace PLATFORM;
 
 CCECTV::CCECTV(CCECProcessor *processor, cec_logical_address address) :
     CCECBusDevice(processor, address, CEC_PHYSICAL_ADDRESS_TV)
 {
   m_type = CEC_DEVICE_TYPE_TV;
 }
+
+void CCECTV::ResetDeviceStatus(void)
+{
+  CLockObject lock(m_mutex);
+  CCECBusDevice::ResetDeviceStatus();
+}
index e712e96b8818f9eedf1b3d6cf03705dfe7df254a..293f5e8004d3704705fa441874f055b969f9160c 100644 (file)
@@ -40,5 +40,7 @@ namespace CEC
   public:
     CCECTV(CCECProcessor *processor, cec_logical_address address);
     virtual ~CCECTV(void) {};
+
+    virtual void ResetDeviceStatus(void);
   };
 }
index feb35e4b5bd92834c3099efcbac1b92ee7637409..4721c1a5a35010be3396a04c573960e37a605694 100644 (file)
 #include "CECTuner.h"
 
 using namespace CEC;
+using namespace PLATFORM;
 
 CCECTuner::CCECTuner(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */) :
     CCECBusDevice(processor, address, iPhysicalAddress)
 {
-  m_type          = CEC_DEVICE_TYPE_TUNER;
+  m_type = CEC_DEVICE_TYPE_TUNER;
+}
+
+void CCECTuner::ResetDeviceStatus(void)
+{
+  CLockObject lock(m_mutex);
+  CCECBusDevice::ResetDeviceStatus();
 }
index ad65efdf65ae729a0740ac7b4eeecdc9435d7976..e9ce44c7809facd3190120c6bd53ea714106ad43 100644 (file)
@@ -40,5 +40,7 @@ namespace CEC
   public:
     CCECTuner(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress = CEC_INVALID_PHYSICAL_ADDRESS);
     virtual ~CCECTuner(void) {};
+
+    virtual void ResetDeviceStatus(void);
   };
 }
index 182f0acc76444d10769062f8413cac767b3f5cad..7c2d05906d7b4a33550edb0eb55cbfcdd129268f 100644 (file)
 #include "../devices/CECBusDevice.h"
 #include "../CECProcessor.h"
 #include "../LibCEC.h"
+#include "../CECClient.h"
 
 using namespace CEC;
 
+#define LIB_CEC     m_busDevice->GetProcessor()->GetLib()
+#define ToString(p) LIB_CEC->ToString(p)
+
 CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice) :
     CCECCommandHandler(busDevice)
 {
@@ -46,8 +50,10 @@ CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice) :
 
 bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command)
 {
-  if (m_processor->IsRunning() && command.parameters.size > 0)
+  if (m_processor->CECInitialised() && command.parameters.size > 0)
   {
+    CCECClient *client = m_processor->GetClient(command.destination);
+
     cec_keypress key;
     key.duration = CEC_BUTTON_TIMEOUT;
     key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
@@ -55,7 +61,7 @@ bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command)
     switch (command.parameters[0])
     {
     case CEC_USER_CONTROL_CODE_AN_RETURN:
-      key.keycode = m_processor->GetClientVersion() >= CEC_CLIENT_VERSION_1_5_0 ?
+      key.keycode = client && client->GetClientVersion() >= CEC_CLIENT_VERSION_1_5_0 ?
         CEC_USER_CONTROL_CODE_AN_RETURN :
         CEC_USER_CONTROL_CODE_EXIT;
       break;
@@ -66,8 +72,8 @@ bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command)
       break;
     }
 
-    if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN)
-      CLibCEC::AddKey(key);
+    if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN && client)
+      client->AddKey(key);
   }
 
   return true;
@@ -76,7 +82,7 @@ bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command)
 bool CANCommandHandler::HandleCommand(const cec_command &command)
 {
   bool bHandled(false);
-  if (m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->IsHandledByLibCEC(command.destination))
   {
     switch(command.opcode)
     {
index 449c589e27154805350ab8dee9cbb06204bd2f40..40d7ec37425a26ded517d2c96f3829c2a62ccb8b 100644 (file)
@@ -34,6 +34,7 @@
 #include "../devices/CECBusDevice.h"
 #include "../devices/CECAudioSystem.h"
 #include "../devices/CECPlaybackDevice.h"
+#include "../CECClient.h"
 #include "../CECProcessor.h"
 #include "../LibCEC.h"
 
@@ -41,6 +42,9 @@ using namespace CEC;
 using namespace std;
 using namespace PLATFORM;
 
+#define LIB_CEC     m_busDevice->GetProcessor()->GetLib()
+#define ToString(p) LIB_CEC->ToString(p)
+
 CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice) :
     m_busDevice(busDevice),
     m_processor(m_busDevice->GetProcessor()),
@@ -66,7 +70,9 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
 
   bool bHandled(true);
 
-  CLibCEC::AddCommand(command);
+  CCECClient *client = m_busDevice->GetClient();
+  if (client)
+    client->AddCommand(command);
 
   switch(command.opcode)
   {
@@ -80,19 +86,19 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
     HandleSetMenuLanguage(command);
     break;
   case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGivePhysicalAddress(command);
     break;
   case CEC_OPCODE_GET_MENU_LANGUAGE:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGiveMenuLanguage(command);
     break;
   case CEC_OPCODE_GIVE_OSD_NAME:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGiveOSDName(command);
     break;
   case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGiveDeviceVendorId(command);
     break;
   case CEC_OPCODE_DEVICE_VENDOR_ID:
@@ -102,42 +108,42 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
     HandleDeviceVendorCommandWithId(command);
     break;
   case CEC_OPCODE_GIVE_DECK_STATUS:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGiveDeckStatus(command);
     break;
   case CEC_OPCODE_DECK_CONTROL:
     HandleDeckControl(command);
     break;
   case CEC_OPCODE_MENU_REQUEST:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleMenuRequest(command);
     break;
   case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGiveDevicePowerStatus(command);
     break;
   case CEC_OPCODE_GET_CEC_VERSION:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGetCecVersion(command);
     break;
   case CEC_OPCODE_USER_CONTROL_PRESSED:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleUserControlPressed(command);
     break;
   case CEC_OPCODE_USER_CONTROL_RELEASE:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleUserControlRelease(command);
     break;
   case CEC_OPCODE_GIVE_AUDIO_STATUS:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGiveAudioStatus(command);
     break;
   case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleGiveSystemAudioModeStatus(command);
     break;
   case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleSystemAudioModeRequest(command);
     break;
   case CEC_OPCODE_REPORT_AUDIO_STATUS:
@@ -150,7 +156,7 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
     HandleSetSystemAudioMode(command);
     break;
   case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleRequestActiveSource(command);
     break;
   case CEC_OPCODE_SET_STREAM_PATH:
@@ -163,7 +169,7 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
     HandleRoutingInformation(command);
     break;
   case CEC_OPCODE_STANDBY:
-    if (m_processor->IsInitialised())
+    if (m_processor->CECInitialised())
       HandleStandby(command);
     break;
   case CEC_OPCODE_ACTIVE_SOURCE:
@@ -188,13 +194,14 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
     HandleVendorCommand(command);
     break;
   default:
-    UnhandledCommand(command);
     bHandled = false;
     break;
   }
 
   if (bHandled)
     m_waitForResponse->Received((command.opcode == CEC_OPCODE_FEATURE_ABORT && command.parameters.size > 0) ? (cec_opcode)command.parameters[0] : command.opcode);
+  else
+    UnhandledCommand(command);
 
   return bHandled;
 }
@@ -204,7 +211,9 @@ bool CCECCommandHandler::HandleActiveSource(const cec_command &command)
   if (command.parameters.size == 2)
   {
     uint16_t iAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
-    return m_processor->SetActiveSource(iAddress);
+    CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iAddress);
+    if (device)
+      device->MarkAsActiveSource();
   }
 
   return true;
@@ -212,10 +221,10 @@ bool CCECCommandHandler::HandleActiveSource(const cec_command &command)
 
 bool CCECCommandHandler::HandleDeckControl(const cec_command &command)
 {
-  CCECBusDevice *device = GetDevice(command.destination);
-  if (device && (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) && command.parameters.size > 0)
+  CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination));
+  if (device && command.parameters.size > 0)
   {
-    ((CCECPlaybackDevice *) device)->SetDeckControlMode((cec_deck_control_mode) command.parameters[0]);
+    device->SetDeckControlMode((cec_deck_control_mode) command.parameters[0]);
     return true;
   }
 
@@ -236,8 +245,8 @@ bool CCECCommandHandler::HandleDeviceCecVersion(const cec_command &command)
 
 bool CCECCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
-    m_processor->TransmitAbort(command.initiator, command.opcode, CEC_ABORT_REASON_REFUSED);
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
+    m_processor->TransmitAbort(command.destination, command.initiator, command.opcode, CEC_ABORT_REASON_REFUSED);
 
   return true;
 }
@@ -252,13 +261,13 @@ bool CCECCommandHandler::HandleFeatureAbort(const cec_command &command)
   if (command.parameters.size == 2 &&
         (command.parameters[1] == CEC_ABORT_REASON_UNRECOGNIZED_OPCODE ||
          command.parameters[1] == CEC_ABORT_REASON_REFUSED))
-    m_processor->m_busDevices[command.initiator]->SetUnsupportedFeature((cec_opcode)command.parameters[0]);
+    m_processor->GetDevice(command.initiator)->SetUnsupportedFeature((cec_opcode)command.parameters[0]);
   return true;
 }
 
 bool CCECCommandHandler::HandleGetCecVersion(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
     CCECBusDevice *device = GetDevice(command.destination);
     if (device)
@@ -270,11 +279,11 @@ bool CCECCommandHandler::HandleGetCecVersion(const cec_command &command)
 
 bool CCECCommandHandler::HandleGiveAudioStatus(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
-    CCECBusDevice *device = GetDevice(command.destination);
-    if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
-      return ((CCECAudioSystem *) device)->TransmitAudioStatus(command.initiator);
+    CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
+    if (device)
+      return device->TransmitAudioStatus(command.initiator);
   }
 
   return false;
@@ -282,11 +291,11 @@ bool CCECCommandHandler::HandleGiveAudioStatus(const cec_command &command)
 
 bool CCECCommandHandler::HandleGiveDeckStatus(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
-    CCECBusDevice *device = GetDevice(command.destination);
-    if (device && (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE))
-      return ((CCECPlaybackDevice *) device)->TransmitDeckStatus(command.initiator);
+    CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination));
+    if (device)
+      return device->TransmitDeckStatus(command.initiator);
   }
 
   return false;
@@ -294,7 +303,7 @@ bool CCECCommandHandler::HandleGiveDeckStatus(const cec_command &command)
 
 bool CCECCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
     CCECBusDevice *device = GetDevice(command.destination);
     if (device)
@@ -306,7 +315,7 @@ bool CCECCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
 
 bool CCECCommandHandler::HandleGiveDeviceVendorId(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
     CCECBusDevice *device = GetDevice(command.destination);
     if (device)
@@ -318,7 +327,7 @@ bool CCECCommandHandler::HandleGiveDeviceVendorId(const cec_command &command)
 
 bool CCECCommandHandler::HandleGiveOSDName(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
     CCECBusDevice *device = GetDevice(command.destination);
     if (device)
@@ -330,7 +339,7 @@ bool CCECCommandHandler::HandleGiveOSDName(const cec_command &command)
 
 bool CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
     CCECBusDevice *device = GetDevice(command.destination);
     if (device)
@@ -342,7 +351,7 @@ bool CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command)
 
 bool CCECCommandHandler::HandleGiveMenuLanguage(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
     CCECBusDevice *device = GetDevice(command.destination);
     if (device)
@@ -354,11 +363,11 @@ bool CCECCommandHandler::HandleGiveMenuLanguage(const cec_command &command)
 
 bool CCECCommandHandler::HandleGiveSystemAudioModeStatus(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
-    CCECBusDevice *device = GetDevice(command.destination);
-    if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
-      return ((CCECAudioSystem *) device)->TransmitSystemAudioModeStatus(command.initiator);
+    CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
+    if (device)
+      return device->TransmitSystemAudioModeStatus(command.initiator);
   }
 
   return false;
@@ -366,26 +375,30 @@ bool CCECCommandHandler::HandleGiveSystemAudioModeStatus(const cec_command &comm
 
 bool CCECCommandHandler::HandleImageViewOn(const cec_command &command)
 {
-  m_processor->m_busDevices[command.initiator]->SetActiveSource();
+  m_processor->GetDevice(command.initiator)->MarkAsActiveSource();
   return true;
 }
 
 bool CCECCommandHandler::HandleMenuRequest(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
     CCECBusDevice *device = GetDevice(command.destination);
     if (device)
     {
-      if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_ACTIVATE)
-      {
-        if (CLibCEC::MenuStateChanged(CEC_MENU_STATE_ACTIVATED) == 1)
-          device->SetMenuState(CEC_MENU_STATE_ACTIVATED);
-      }
-      else if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_DEACTIVATE)
+      CCECClient *client = device->GetClient();
+      if (client)
       {
-        if (CLibCEC::MenuStateChanged(CEC_MENU_STATE_DEACTIVATED) == 1)
-          device->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
+        if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_ACTIVATE)
+        {
+          if (client->MenuStateChanged(CEC_MENU_STATE_ACTIVATED) == 1)
+            device->SetMenuState(CEC_MENU_STATE_ACTIVATED);
+        }
+        else if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_DEACTIVATE)
+        {
+          if (client->MenuStateChanged(CEC_MENU_STATE_DEACTIVATED) == 1)
+            device->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
+        }
       }
       return device->TransmitMenuState(command.initiator);
     }
@@ -404,10 +417,10 @@ bool CCECCommandHandler::HandleReportAudioStatus(const cec_command &command)
 {
   if (command.parameters.size == 1)
   {
-    CCECBusDevice *device = GetDevice(command.initiator);
-    if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
+    CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
+    if (device)
     {
-      ((CCECAudioSystem *)device)->SetAudioStatus(command.parameters[0]);
+      device->SetAudioStatus(command.parameters[0]);
       return true;
     }
   }
@@ -437,10 +450,10 @@ bool CCECCommandHandler::HandleReportPowerStatus(const cec_command &command)
 
 bool CCECCommandHandler::HandleRequestActiveSource(const cec_command &command)
 {
-  if (m_processor->IsRunning())
+  if (m_processor->CECInitialised())
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %i requests active source", (uint8_t) command.initiator);
-    m_processor->m_busDevices[command.initiator]->SetPowerStatus(CEC_POWER_STATUS_ON);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %i requests active source", (uint8_t) command.initiator);
+    m_processor->GetDevice(command.initiator)->SetPowerStatus(CEC_POWER_STATUS_ON);
 
     vector<CCECBusDevice *> devices;
     for (size_t iDevicePtr = 0; iDevicePtr < GetMyDevices(devices); iDevicePtr++)
@@ -470,7 +483,9 @@ bool CCECCommandHandler::HandleRoutingInformation(const cec_command &command)
   if (command.parameters.size == 2)
   {
     uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
-    m_processor->SetActiveSource(iNewAddress);
+    CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
+    if (device)
+      device->MarkAsActiveSource();
   }
 
   return false;
@@ -518,47 +533,40 @@ bool CCECCommandHandler::HandleSetOSDName(const cec_command &command)
 
 bool CCECCommandHandler::HandleSetStreamPath(const cec_command &command)
 {
-  if (m_processor->IsRunning() && command.parameters.size >= 2)
+  if (m_processor->CECInitialised() && command.parameters.size >= 2)
   {
     uint16_t iStreamAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
-    CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %i sets stream path to physical address %04x", command.initiator, iStreamAddress);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %i sets stream path to physical address %04x", command.initiator, iStreamAddress);
 
     /* one of the device handled by libCEC has been made active */
     CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamAddress);
-    if (device && m_busDevice->MyLogicalAddressContains(device->GetLogicalAddress()))
-    {
-      device->SetActiveSource();
-      device->TransmitImageViewOn();
-      device->TransmitActiveSource();
-
-      device->SetMenuState(CEC_MENU_STATE_ACTIVATED);
-      device->TransmitMenuState(command.initiator);
-    }
+    if (device && device->IsHandledByLibCEC())
+      device->ActivateSource();
   }
   return false;
 }
 
 bool CCECCommandHandler::HandleSystemAudioModeRequest(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
-    CCECBusDevice *device = GetDevice(command.destination);
-    if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
+    CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
+    if (device)
     {
       if (command.parameters.size >= 2)
       {
         device->SetPowerStatus(CEC_POWER_STATUS_ON);
-        ((CCECAudioSystem *) device)->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_ON);
+        device->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_ON);
         uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
         CCECBusDevice *newActiveDevice = GetDeviceByPhysicalAddress(iNewAddress);
         if (newActiveDevice)
-          newActiveDevice->SetActiveSource();
-        return ((CCECAudioSystem *) device)->TransmitSetSystemAudioMode(command.initiator);
+          newActiveDevice->MarkAsActiveSource();
+        return device->TransmitSetSystemAudioMode(command.initiator);
       }
       else
       {
-        ((CCECAudioSystem *) device)->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_OFF);
-        return ((CCECAudioSystem *) device)->TransmitSetSystemAudioMode(command.initiator);
+        device->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_OFF);
+        return device->TransmitSetSystemAudioMode(command.initiator);
       }
     }
   }
@@ -578,10 +586,10 @@ bool CCECCommandHandler::HandleSystemAudioModeStatus(const cec_command &command)
 {
   if (command.parameters.size == 1)
   {
-    CCECBusDevice *device = GetDevice(command.initiator);
-    if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
+    CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
+    if (device)
     {
-      ((CCECAudioSystem *)device)->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
+      device->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
       return true;
     }
   }
@@ -593,10 +601,10 @@ bool CCECCommandHandler::HandleSetSystemAudioMode(const cec_command &command)
 {
   if (command.parameters.size == 1)
   {
-    CCECBusDevice *device = GetDevice(command.initiator);
-    if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
+    CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
+    if (device)
     {
-      ((CCECAudioSystem *)device)->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
+      device->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
       return true;
     }
   }
@@ -606,25 +614,31 @@ bool CCECCommandHandler::HandleSetSystemAudioMode(const cec_command &command)
 
 bool CCECCommandHandler::HandleTextViewOn(const cec_command &command)
 {
-  m_processor->m_busDevices[command.initiator]->SetActiveSource();
+  m_processor->GetDevice(command.initiator)->MarkAsActiveSource();
   return true;
 }
 
 bool CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
 {
-  if (m_processor->IsRunning() &&
-      m_busDevice->MyLogicalAddressContains(command.destination) &&
+  if (m_processor->CECInitialised() &&
+      m_processor->IsHandledByLibCEC(command.destination) &&
       command.parameters.size > 0)
   {
-    CLibCEC::AddKey();
+    CCECBusDevice *device = GetDevice(command.destination);
+    if (!device)
+      return true;
+
+    CCECClient *client = device->GetClient();
+    if (client)
+      client->AddKey();
+
     if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
-      CLibCEC::SetCurrentButton((cec_user_control_code) command.parameters[0]);
+      client->SetCurrentButton((cec_user_control_code) command.parameters[0]);
 
     if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER ||
         command.parameters[0] == CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION)
     {
       bool bPowerOn(true);
-      CCECBusDevice *device = GetDevice(command.destination);
       if (!device)
         return true;
 
@@ -632,23 +646,17 @@ bool CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
       // assume CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION does not
       if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER)
       {
-        cec_power_status status = device->GetPowerStatus();
+        cec_power_status status = device->GetCurrentPowerStatus();
         bPowerOn = !(status == CEC_POWER_STATUS_ON || status == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
       }
 
       if (bPowerOn)
       {
-        device->SetActiveSource();
-        device->TransmitImageViewOn();
-        device->TransmitActiveSource();
-
-        if (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE ||
-            device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)
-          ((CCECPlaybackDevice *)device)->TransmitDeckStatus(command.initiator);
+        device->ActivateSource();
       }
       else
       {
-        device->SetInactiveSource();
+        device->MarkAsInactiveSource();
         device->TransmitInactiveSource();
         device->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
       }
@@ -661,9 +669,9 @@ bool CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
 
 bool CCECCommandHandler::HandleUserControlRelease(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
-    CLibCEC::AddKey();
-
+  CCECClient *client = m_processor->GetClient(command.destination);
+  if (client)
+    client->AddKey();
   return true;
 }
 
@@ -674,7 +682,10 @@ bool CCECCommandHandler::HandleVendorCommand(const cec_command & UNUSED(command)
 
 void CCECCommandHandler::UnhandledCommand(const cec_command &command)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "unhandled command with opcode %02x from address %d", command.opcode, command.initiator);
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "unhandled command with opcode %02x from address %d", command.opcode, command.initiator);
+
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
+    m_processor->TransmitAbort(m_busDevice->GetLogicalAddress(), command.initiator, command.opcode, CEC_ABORT_REASON_UNRECOGNIZED_OPCODE);
 }
 
 size_t CCECCommandHandler::GetMyDevices(vector<CCECBusDevice *> &devices) const
@@ -696,12 +707,7 @@ size_t CCECCommandHandler::GetMyDevices(vector<CCECBusDevice *> &devices) const
 
 CCECBusDevice *CCECCommandHandler::GetDevice(cec_logical_address iLogicalAddress) const
 {
-  CCECBusDevice *device = NULL;
-
-  if (iLogicalAddress >= CECDEVICE_TV && iLogicalAddress <= CECDEVICE_BROADCAST)
-    device = m_processor->m_busDevices[iLogicalAddress];
-
-  return device;
+  return m_processor->GetDevice(iLogicalAddress);
 }
 
 CCECBusDevice *CCECCommandHandler::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress) const
@@ -709,17 +715,12 @@ CCECBusDevice *CCECCommandHandler::GetDeviceByPhysicalAddress(uint16_t iPhysical
   return m_processor->GetDeviceByPhysicalAddress(iPhysicalAddress);
 }
 
-CCECBusDevice *CCECCommandHandler::GetDeviceByType(cec_device_type type) const
-{
-  return m_processor->GetDeviceByType(type);
-}
-
 bool CCECCommandHandler::SetVendorId(const cec_command &command)
 {
   bool bChanged(false);
   if (command.parameters.size < 3)
   {
-    CLibCEC::AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
+    LIB_CEC->AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
     return bChanged;
   }
 
@@ -735,17 +736,26 @@ bool CCECCommandHandler::SetVendorId(const cec_command &command)
 
 void CCECCommandHandler::SetPhysicalAddress(cec_logical_address iAddress, uint16_t iNewAddress)
 {
-  if (!m_busDevice->MyLogicalAddressContains(iAddress))
+  if (!m_processor->IsHandledByLibCEC(iAddress))
   {
-    bool bOurAddress(m_processor->GetPhysicalAddress() == iNewAddress);
-    GetDevice(iAddress)->SetPhysicalAddress(iNewAddress);
-    if (bOurAddress)
+    CCECBusDevice *otherDevice = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
+    CCECClient *client = otherDevice ? otherDevice->GetClient() : NULL;
+
+    CCECBusDevice *device = m_processor->GetDevice(iAddress);
+    if (device)
+      device->SetPhysicalAddress(iNewAddress);
+    else
     {
-      /* another device reported the same physical address as ours
-       * since we don't have physical address detection yet, we'll just use the
-       * given address, increased by 0x100 for now */
-      m_processor->SetPhysicalAddress(iNewAddress + 0x100);
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "device with logical address %X not found", iAddress);
     }
+
+    /* another device reported the same physical address as ours */
+    if (client)
+      client->ResetPhysicalAddress();
+  }
+  else
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignore physical address report for device %s (%X) because it's marked as handled by libCEC", ToString(iAddress), iAddress);
   }
 }
 
@@ -1016,7 +1026,7 @@ bool CCECCommandHandler::Transmit(cec_command &command, bool bSuppressWait /* =
 
   if (command.initiator == CECDEVICE_UNKNOWN)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "not transmitting a command without a valid initiator");
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "not transmitting a command without a valid initiator");
     return bReturn;
   }
 
@@ -1026,11 +1036,11 @@ bool CCECCommandHandler::Transmit(cec_command &command, bool bSuppressWait /* =
     {
       if ((bReturn = m_processor->Transmit(command)) == true)
       {
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "command transmitted");
+        LIB_CEC->AddLog(CEC_LOG_DEBUG, "command transmitted");
         if (bExpectResponse)
         {
           bReturn = m_waitForResponse->Wait(expectedResponse);
-          CLibCEC::AddLog(CEC_LOG_DEBUG, bReturn ? "expected response received (%X: %s)" : "expected response not received (%X: %s)", (int)expectedResponse, m_processor->ToString(expectedResponse));
+          LIB_CEC->AddLog(CEC_LOG_DEBUG, bReturn ? "expected response received (%X: %s)" : "expected response not received (%X: %s)", (int)expectedResponse, ToString(expectedResponse));
         }
       }
     }
@@ -1042,7 +1052,7 @@ bool CCECCommandHandler::Transmit(cec_command &command, bool bSuppressWait /* =
 bool CCECCommandHandler::ActivateSource(void)
 {
   if (m_busDevice->IsActiveSource() &&
-    m_busDevice->GetStatus(false) == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
+    m_busDevice->IsHandledByLibCEC())
   {
     m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
     m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED);
@@ -1050,10 +1060,10 @@ bool CCECCommandHandler::ActivateSource(void)
     m_busDevice->TransmitImageViewOn();
     m_busDevice->TransmitActiveSource();
     m_busDevice->TransmitMenuState(CECDEVICE_TV);
-    if ((m_busDevice->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE ||
-      m_busDevice->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) &&
-      SendDeckStatusUpdateOnActiveSource())
-      ((CCECPlaybackDevice *)m_busDevice)->TransmitDeckStatus(CECDEVICE_TV);
+
+    CCECPlaybackDevice *playbackDevice = m_busDevice->AsPlaybackDevice();
+    if (playbackDevice && SendDeckStatusUpdateOnActiveSource())
+      playbackDevice->TransmitDeckStatus(CECDEVICE_TV);
     m_bHandlerInited = true;
   }
   return true;
index 24f62658b9009c419e07d057a923cc4aa5a6faaa..1bced84ff8fc469b17a004aa70891233913a1f78 100644 (file)
@@ -203,7 +203,6 @@ namespace CEC
     virtual size_t GetMyDevices(std::vector<CCECBusDevice *> &devices) const;
     virtual CCECBusDevice *GetDevice(cec_logical_address iLogicalAddress) const;
     virtual CCECBusDevice *GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress) const;
-    virtual CCECBusDevice *GetDeviceByType(cec_device_type type) const;
 
     virtual bool SetVendorId(const cec_command &command);
     virtual void SetPhysicalAddress(cec_logical_address iAddress, uint16_t iNewAddress);
index b9103ec5f59da75a037aee61f68a68c9986bde46..f39aef8997f48a504387518e5fccdbc747a24b53 100644 (file)
@@ -53,6 +53,9 @@ using namespace PLATFORM;
 #define SL_COMMAND_CONNECT_REQUEST      0x04
 #define SL_COMMAND_SET_DEVICE_MODE      0x05
 
+#define LIB_CEC     m_busDevice->GetProcessor()->GetLib()
+#define ToString(p) LIB_CEC->ToString(p)
+
 CSLCommandHandler::CSLCommandHandler(CCECBusDevice *busDevice) :
     CCECCommandHandler(busDevice),
     m_bSLEnabled(false),
@@ -99,37 +102,17 @@ bool CSLCommandHandler::InitHandler(void)
   return true;
 }
 
-bool CSLCommandHandler::ActivateSource(void)
-{
-  if (!m_processor->GetPrimaryDevice()->IsActiveSource())
-  {
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "not activating the source because we're not marked as active");
-    return true;
-  }
-
-  {
-    CLockObject lock(m_SLMutex);
-    m_bActiveSourceSent = true;
-  }
-
-  CCECBusDevice *primary = m_processor->GetPrimaryDevice();
-  primary->SetActiveSource();
-  primary->SetPowerStatus(CEC_POWER_STATUS_ON);
-  primary->TransmitPowerState(CECDEVICE_TV);
-  primary->TransmitImageViewOn();
-  primary->TransmitActiveSource();
-  return true;
-}
-
 bool CSLCommandHandler::HandleActiveSource(const cec_command &command)
 {
   if (command.parameters.size == 2)
   {
     uint16_t iAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
     CCECBusDevice *primary = m_processor->GetPrimaryDevice();
-    bool bSendPowerOffState(iAddress != primary->GetPhysicalAddress() && primary->IsActiveSource());
+    bool bSendPowerOffState(iAddress != primary->GetCurrentPhysicalAddress() && primary->IsActiveSource());
 
-    m_processor->SetActiveSource(iAddress);
+    CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iAddress);
+    if (device)
+      device->MarkAsActiveSource();
     if (bSendPowerOffState)
     {
       {
@@ -151,7 +134,7 @@ bool CSLCommandHandler::HandleDeviceVendorId(const cec_command &command)
   if (!SLInitialised() && command.initiator == CECDEVICE_TV)
   {
     cec_command response;
-    cec_command::Format(response, m_processor->GetLogicalAddress(), command.initiator, CEC_OPCODE_FEATURE_ABORT);
+    cec_command::Format(response, command.destination, command.initiator, CEC_OPCODE_FEATURE_ABORT);
     return Transmit(response);
   }
   return true;
@@ -159,7 +142,7 @@ bool CSLCommandHandler::HandleDeviceVendorId(const cec_command &command)
 
 bool CSLCommandHandler::HandleVendorCommand(const cec_command &command)
 {
-  if (!m_busDevice->MyLogicalAddressContains(command.destination))
+  if (!m_processor->IsHandledByLibCEC(command.destination))
     return true;
 
   if (command.parameters.size == 1 &&
@@ -215,7 +198,7 @@ void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command)
   if (device)
   {
     SetSLInitialised();
-    device->SetActiveSource();
+    device->MarkAsActiveSource();
     device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
     device->TransmitPowerState(command.initiator);
 
@@ -233,17 +216,20 @@ void CSLCommandHandler::HandleVendorCommandPowerOnStatus(const cec_command &comm
 {
   if (command.destination != CECDEVICE_BROADCAST)
   {
-    CCECBusDevice *device = m_processor->m_busDevices[m_processor->GetLogicalAddresses().primary];
-    device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
-    device->TransmitPowerState(command.initiator);
-    device->SetPowerStatus(CEC_POWER_STATUS_ON);
+    CCECBusDevice *device = m_processor->GetPrimaryDevice();
+    if (device)
+    {
+      device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
+      device->TransmitPowerState(command.initiator);
+      device->SetPowerStatus(CEC_POWER_STATUS_ON);
+    }
   }
 }
 
 void CSLCommandHandler::HandleVendorCommandSLConnect(const cec_command &command)
 {
   SetSLInitialised();
-  TransmitVendorCommandSetDeviceMode(m_processor->GetLogicalAddress(), command.initiator, CEC_DEVICE_TYPE_RECORDING_DEVICE);
+  TransmitVendorCommandSetDeviceMode(command.destination, command.initiator, CEC_DEVICE_TYPE_RECORDING_DEVICE);
 
   ActivateSource();
 }
@@ -259,7 +245,7 @@ void CSLCommandHandler::TransmitVendorCommandSetDeviceMode(const cec_logical_add
 
 bool CSLCommandHandler::HandleGiveDeckStatus(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
   {
     CCECBusDevice *device = GetDevice(command.destination);
     if (device && (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE))
@@ -289,10 +275,10 @@ bool CSLCommandHandler::HandleGiveDeckStatus(const cec_command &command)
 bool CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
 {
   bool bReturn(false);
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination) && command.initiator == CECDEVICE_TV)
+  if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination) && command.initiator == CECDEVICE_TV)
   {
     CCECBusDevice *device = GetDevice(command.destination);
-    if (device && device->GetPowerStatus(false) != CEC_POWER_STATUS_ON)
+    if (device && device->GetCurrentPowerStatus() != CEC_POWER_STATUS_ON)
     {
       bReturn = device->TransmitPowerState(command.initiator);
       device->SetPowerStatus(CEC_POWER_STATUS_ON);
@@ -308,7 +294,7 @@ bool CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
       else if (m_resetPowerState.IsSet() && m_resetPowerState.TimeLeft() > 0)
       {
         /* TODO assume that we've bugged out. the return button no longer works after this */
-        CLibCEC::AddLog(CEC_LOG_WARNING, "FIXME: LG seems to have bugged out. resetting to 'in transition standby to on'. the return button will not work");
+        LIB_CEC->AddLog(CEC_LOG_WARNING, "FIXME: LG seems to have bugged out. resetting to 'in transition standby to on'. the return button will not work");
         {
           CLockObject lock(m_SLMutex);
           m_bActiveSourceSent = false;
@@ -331,10 +317,10 @@ bool CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
 
 bool CSLCommandHandler::HandleRequestActiveSource(const cec_command &command)
 {
-  if (m_processor->IsRunning())
+  if (m_processor->CECInitialised())
   {
     if (ActiveSourceSent())
-      CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %i requests active source, ignored", (uint8_t) command.initiator);
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %i requests active source, ignored", (uint8_t) command.initiator);
     else
       ActivateSource();
     return true;
@@ -344,7 +330,7 @@ bool CSLCommandHandler::HandleRequestActiveSource(const cec_command &command)
 
 bool CSLCommandHandler::HandleFeatureAbort(const cec_command &command)
 {
-  if (command.parameters.size == 0 && m_processor->GetPrimaryDevice()->GetPowerStatus() == CEC_POWER_STATUS_ON && !SLInitialised() &&
+  if (command.parameters.size == 0 && m_processor->GetPrimaryDevice()->GetCurrentPowerStatus() == CEC_POWER_STATUS_ON && !SLInitialised() &&
       command.initiator == CECDEVICE_TV)
   {
     m_processor->GetPrimaryDevice()->TransmitPowerState(command.initiator);
@@ -367,7 +353,7 @@ bool CSLCommandHandler::HandleStandby(const cec_command &command)
 
 void CSLCommandHandler::ResetSLState(void)
 {
-  CLibCEC::AddLog(CEC_LOG_NOTICE, "resetting SL initialised state");
+  LIB_CEC->AddLog(CEC_LOG_NOTICE, "resetting SL initialised state");
   CLockObject lock(m_SLMutex);
   m_bSLEnabled = false;
   m_bActiveSourceSent = false;
@@ -376,7 +362,7 @@ void CSLCommandHandler::ResetSLState(void)
 
 void CSLCommandHandler::SetSLInitialised(void)
 {
-  CLibCEC::AddLog(CEC_LOG_NOTICE, "SL initialised");
+  LIB_CEC->AddLog(CEC_LOG_NOTICE, "SL initialised");
   CLockObject lock(m_SLMutex);
   m_bSLEnabled = true;
 }
index fefb8b387dea30d8bf49b477279b19ee08847e9e..0eb6c8656e25170519279e9035c0bce66930da89 100644 (file)
@@ -43,7 +43,6 @@ namespace CEC
     virtual ~CSLCommandHandler(void) {};
 
     bool InitHandler(void);
-    bool ActivateSource(void);
 
   protected:
     bool HandleActiveSource(const cec_command &command);
index e8cf7c8c4d01f0aede754d5c2c71f76d94185c50..59eb5747cb6cc9693415286f836337647c5e1aab 100644 (file)
@@ -34,6 +34,7 @@
 #include "../devices/CECBusDevice.h"
 #include "../CECProcessor.h"
 #include "../LibCEC.h"
+#include "../CECClient.h"
 
 #define VL_POWER_CHANGE 0x20
 #define VL_POWERED_UP   0x00
@@ -42,6 +43,9 @@
 using namespace CEC;
 using namespace PLATFORM;
 
+#define LIB_CEC     m_busDevice->GetProcessor()->GetLib()
+#define ToString(p) LIB_CEC->ToString(p)
+
 CVLCommandHandler::CVLCommandHandler(CCECBusDevice *busDevice) :
     CCECCommandHandler(busDevice),
     m_bActiveSourcePending(false),
@@ -65,7 +69,7 @@ bool CVLCommandHandler::InitHandler(void)
 {
   CCECBusDevice *primary = m_processor->GetPrimaryDevice();
   if (primary->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)
-    return m_processor->ChangeDeviceType(CEC_DEVICE_TYPE_RECORDING_DEVICE, CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
+    return m_processor->GetPrimaryClient()->ChangeDeviceType(CEC_DEVICE_TYPE_RECORDING_DEVICE, CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
 
   return CCECCommandHandler::InitHandler();
 }
@@ -78,7 +82,7 @@ bool CVLCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &comma
   {
     if (command.parameters.At(4) == VL_POWERED_UP)
     {
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "TV powered up");
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV powered up");
       {
         CLockObject lock(m_mutex);
         m_bPowerUpEventReceived = true;
@@ -86,9 +90,9 @@ bool CVLCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &comma
       m_processor->TransmitPendingActiveSourceCommands();
     }
     else if (command.parameters.At(4) == VL_POWERED_DOWN)
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "TV powered down");
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV powered down");
     else if (command.parameters.At(4) == VL_POWERED_DOWN)
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "unknown vendor command");
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "unknown vendor command");
 
     return true;
   }
@@ -100,8 +104,8 @@ bool CVLCommandHandler::TransmitActiveSource(const cec_logical_address iInitiato
 {
   bool bPowerUpEventReceived(false);
 
-  CCECBusDevice *tv = m_processor->m_busDevices[CECDEVICE_TV];
-  if (tv && tv->GetVendorId(false) == CEC_VENDOR_PANASONIC)
+  CCECBusDevice *tv = m_processor->GetDevice(CECDEVICE_TV);
+  if (tv && tv->GetCurrentVendorId() == CEC_VENDOR_PANASONIC)
   {
     CVLCommandHandler *handler = static_cast<CVLCommandHandler *>(tv->GetHandler());
     bPowerUpEventReceived = handler ? handler->PowerUpEventReceived() : false;
@@ -132,8 +136,8 @@ bool CVLCommandHandler::TransmitPendingActiveSourceCommands(void)
 
   if (bTransmitCommand)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
-    return CCECCommandHandler::TransmitActiveSource(m_busDevice->GetLogicalAddress(), m_busDevice->GetPhysicalAddress());
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
+    return CCECCommandHandler::TransmitActiveSource(m_busDevice->GetLogicalAddress(), m_busDevice->GetCurrentPhysicalAddress());
   }
   return true;
 }
@@ -146,7 +150,7 @@ bool CVLCommandHandler::PowerUpEventReceived(void)
       return true;
   }
 
-  cec_power_status powerStatus = m_busDevice->GetPowerStatus();
+  cec_power_status powerStatus = m_busDevice->GetCurrentPowerStatus();
 
   CLockObject lock(m_mutex);
   m_bPowerUpEventReceived = (powerStatus == CEC_POWER_STATUS_ON);
index 89f822b0dec300a89aee85d2f55e62871fac9080..e5f935cf64ac9d24ff0c7637b53725755671c417 100644 (file)
@@ -64,6 +64,7 @@ namespace PLATFORM
   }
 
   typedef pthread_t thread_t;
+  #define INVALID_THREAD_VALUE 0
 
   #define ThreadsCreate(thread, func, arg)         (pthread_create(&thread, NULL, (void *(*) (void *))func, (void *)arg) == 0)
   #define ThreadsWait(thread, retval)              (thread ? pthread_join(thread, retval) == 0 : true)
index b89b9cc2a57617f15ae69ddc5d9d65274cdd737c..a365635490f66d4a7b46e86211404abe4ac60de3 100644 (file)
 #include <semaphore.h>
 #include <stdint.h>
 
+extern "C" {
+#include <sys/types.h>
+}
+
 #define LIBTYPE
 #define DECLSPEC
 
index cefc21201ef03ee41d17feea11e5cdc3aac9e529..3764a1567f9673a26aedfaa0ba1192cfe4884145 100644 (file)
 #ifndef IUCLC
 #define IUCLC  0
 #endif
+#else
+#include <lockdev.h>
 #endif
+
 using namespace std;
 using namespace PLATFORM;
 
+inline bool RemoveLock(const char *strDeviceName)
+{
+  #if !defined(__APPLE__) && !defined(__FreeBSD__)
+  return dev_unlock(strDeviceName, 0) == 0;
+  #endif
+}
+
 void CSerialSocket::Close(void)
 {
   if (IsOpen())
+  {
     SocketClose(m_socket);
+    RemoveLock(m_strName.c_str());
+  }
 }
 
 void CSerialSocket::Shutdown(void)
 {
   if (IsOpen())
+  {
     SocketClose(m_socket);
+    RemoveLock(m_strName.c_str());
+  }
 }
 
 ssize_t CSerialSocket::Write(void* data, size_t len)
@@ -78,32 +94,48 @@ bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */)
 {
   iTimeoutMs = 0;
   if (IsOpen())
+  {
+    m_iError = EINVAL;
     return false;
+  }
 
   if (m_iDatabits != SERIAL_DATA_BITS_FIVE && m_iDatabits != SERIAL_DATA_BITS_SIX &&
       m_iDatabits != SERIAL_DATA_BITS_SEVEN && m_iDatabits != SERIAL_DATA_BITS_EIGHT)
   {
     m_strError = "Databits has to be between 5 and 8";
+    m_iError = EINVAL;
     return false;
   }
 
   if (m_iStopbits != SERIAL_STOP_BITS_ONE && m_iStopbits != SERIAL_STOP_BITS_TWO)
   {
     m_strError = "Stopbits has to be 1 or 2";
+    m_iError = EINVAL;
     return false;
   }
 
   if (m_iParity != SERIAL_PARITY_NONE && m_iParity != SERIAL_PARITY_EVEN && m_iParity != SERIAL_PARITY_ODD)
   {
     m_strError = "Parity has to be none, even or odd";
+    m_iError = EINVAL;
+    return false;
+  }
+
+  #if !defined(__APPLE__) && !defined(__FreeBSD__)
+  if (dev_lock(m_strName.c_str()) != 0)
+  {
+    m_strError = "Couldn't lock the serial port";
+    m_iError = EBUSY;
     return false;
   }
+  #endif
 
   m_socket = open(m_strName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
 
   if (m_socket == INVALID_SERIAL_SOCKET_VALUE)
   {
     m_strError = strerror(errno);
+    RemoveLock(m_strName.c_str());
     return false;
   }
 
@@ -150,6 +182,7 @@ bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */)
   if (tcsetattr(m_socket, TCSANOW, &m_options) != 0)
   {
     m_strError = strerror(errno);
+    RemoveLock(m_strName.c_str());
     return false;
   }
   
index e29578c728a1b5b0abf5288b5898615433fb6498..36477708d4750e6c2e035ca739d067f06cdff26a 100644 (file)
@@ -41,16 +41,15 @@ namespace PLATFORM
     CThread(void) :
         m_bStop(false),
         m_bRunning(false),
-        m_bStopped(false) {}
+        m_bStopped(false),
+        m_thread(INVALID_THREAD_VALUE) {}
 
     virtual ~CThread(void)
     {
       StopThread(0);
-      void *retVal;
-      ThreadsWait(m_thread, &retVal);
-      #if defined(__WINDOWS__)
-      (void *)retVal; //"unreferenced local variable" warning
-      #endif
+      void *retVal = NULL;
+      if (m_thread != INVALID_THREAD_VALUE)
+        ThreadsWait(m_thread, &retVal);
     }
 
     static void *ThreadHandler(CThread *thread)
index 3714c16711950919edd768708e5d2cee4fa48594..ff773f094cab69aca2374e9b5c30a2a0200db277 100644 (file)
@@ -34,6 +34,7 @@
 namespace PLATFORM
 {
   #define thread_t                                 HANDLE
+  #define INVALID_THREAD_VALUE                     INVALID_HANDLE
   #define ThreadsWait(thread, retVal)              (::WaitForSingleObject(thread, INFINITE) < 0)
   #define ThreadsCreate(thread, func, arg)         ((thread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, NULL)) == NULL ? false : true)
 
index 39b7ff47df5ca8e761aa3235adc7feffc9db02e9..34fd38730821fd78e7e2c2f6045ad29344d62bde 100644 (file)
@@ -249,6 +249,7 @@ void ShowHelpCommandLine(const char* strExec)
       "  -s --single-command         Execute a single command and exit. Does not power" << endl <<
       "                              on devices on startup and power them off on exit." << endl <<
       "  -o --osd-name {osd name}    Use a custom osd name." << endl <<
+      "  -m --monitor                Start a monitor-only client." << endl <<
       "  [COM PORT]                  The com port to connect to. If no COM" << endl <<
       "                              port is given, the client tries to connect to the" << endl <<
       "                              first device that is detected." << endl <<
@@ -1072,6 +1073,13 @@ bool ProcessCommandLineArguments(int argc, char *argv[])
         }
         ++iArgPtr;
       }
+      else if (!strcmp(argv[iArgPtr], "-m") ||
+               !strcmp(argv[iArgPtr], "--monitor"))
+      {
+        cout << "starting a monitor-only client. use 'mon 0' to switch to normal mode" << endl;
+        g_config.bMonitorOnly = 1;
+        ++iArgPtr;
+      }
       else
       {
         g_strPort = argv[iArgPtr++];
@@ -1086,7 +1094,7 @@ int main (int argc, char *argv[])
 {
   g_config.Clear();
   snprintf(g_config.strDeviceName, 13, "CECTester");
-  g_config.clientVersion       = CEC_CLIENT_VERSION_1_6_2;
+  g_config.clientVersion       = CEC_CLIENT_VERSION_1_6_3;
   g_config.bActivateSource     = 0;
   g_callbacks.CBCecLogMessage  = &CecLogMessage;
   g_callbacks.CBCecKeyPress    = &CecKeyPress;