Merge branch 'master' into release
authorLars Op den Kamp <lars@opdenkamp.eu>
Mon, 16 Apr 2012 16:45:01 +0000 (18:45 +0200)
committerLars Op den Kamp <lars@opdenkamp.eu>
Mon, 16 Apr 2012 16:45:01 +0000 (18:45 +0200)
63 files changed:
ChangeLog
README
configure.ac
debian/changelog
driver/p8usb-cec.inf
include/cec.h
include/cecc.h
include/cectypes.h
project/cec-config.rc
project/cec-config.vcxproj
project/cec-config.vcxproj.filters
project/libcec.rc
project/libcec.vcxproj
project/libcec.vcxproj.filters
project/testclient.rc
project/testclient.vcxproj
project/testclient.vcxproj.filters
src/CecSharpTester/Properties/AssemblyInfo.cs
src/LibCecSharp/AssemblyInfo.cpp
src/LibCecSharp/CecSharpTypes.h
src/LibCecSharp/LibCecSharp.cpp
src/cec-config-gui/CecConfigGUI.cs
src/cec-config-gui/Properties/AssemblyInfo.cs
src/cec-config-gui/actions/ConnectToDevice.cs
src/cec-config-gui/actions/UpdateEvent.cs
src/cec-config/cec-config.cpp
src/lib/CECProcessor.cpp
src/lib/CECProcessor.h
src/lib/LibCEC.cpp
src/lib/LibCEC.h
src/lib/LibCECC.cpp
src/lib/Makefile.am
src/lib/adapter/AdapterCommunication.h
src/lib/adapter/USBCECAdapterCommands.cpp [new file with mode: 0644]
src/lib/adapter/USBCECAdapterCommands.h [new file with mode: 0644]
src/lib/adapter/USBCECAdapterCommunication.cpp
src/lib/adapter/USBCECAdapterCommunication.h
src/lib/adapter/USBCECAdapterDetection.cpp
src/lib/adapter/USBCECAdapterMessage.cpp [new file with mode: 0644]
src/lib/adapter/USBCECAdapterMessage.h
src/lib/adapter/USBCECAdapterMessageQueue.cpp [new file with mode: 0644]
src/lib/adapter/USBCECAdapterMessageQueue.h [new file with mode: 0644]
src/lib/devices/CECBusDevice.cpp
src/lib/devices/CECBusDevice.h
src/lib/implementations/ANCommandHandler.cpp
src/lib/implementations/ANCommandHandler.h
src/lib/implementations/CECCommandHandler.cpp
src/lib/implementations/CECCommandHandler.h
src/lib/implementations/SLCommandHandler.cpp
src/lib/platform/posix/os-threads.h
src/lib/platform/posix/os-types.h
src/lib/platform/posix/serialport.cpp
src/lib/platform/sockets/serialport.h
src/lib/platform/threads/threads.h
src/lib/platform/util/buffer.h
src/lib/platform/util/timeutils.h
src/lib/platform/windows/os-threads.cpp [new file with mode: 0644]
src/lib/platform/windows/os-threads.h
src/lib/platform/windows/serialport.cpp
src/testclient/main.cpp
support/create-driver-installer.cmd
support/create-installer.bat [moved from support/create-installer.cmd with 92% similarity]
support/p8-usbcec-driver-installer.exe [new file with mode: 0644]

index 15d5a73e25bc2ec09e8a77494194342f1c4d3b76..d3b160e802b425813b64e25a0c2d3b1c6913fde1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,135 @@
+libcec (1.6-1) unstable; urgency=medium
+
+  * changed/added:
+    * full firmware V2 support
+    * -o/--osdname argument for cec-client to set a custom osd name
+    * added the firmware version to cec-client's -l / --list-devices command.
+      bugzid: 631
+    * added power on command for Samsung AVR devices. bugzid: 361
+    * added buttoncode for 'channels list' on Samsung (0x96)
+    * don't check for the windows ddk when a prebuilt driver installer is
+      present
+    * respond to Get Menu Language. bugzid: 547. In the event that the menu
+      language is undefined in libcec (currently the default), the response
+      will be a feature abort.
+
+  * interface changes:
+    * added the firmware version to libcec_configuration (read-only). bumped
+      server version to 1.6.0. fixed 'unknown server version' message on
+      startup. bugzid: 631
+    * added a new setting to control whether to put the TV in standby when the
+      player is put in standby. added some missing bits (version numbers,
+      config) to LibCecSharp. bugzid: 558
+    * added an alert callback. bugzid: 462
+    * added bShutdownOnStandby to libcec_configuration. bugzid: 660. This
+      setting tells the client to shutdown when the TV switches off and is
+      complimentary to bPowerOffOnStandby, which tells the PC to suspend.
+      They are kept separate to maintain backwards compatability.
+
+  * fixed
+    * gcc 4.7 compilation
+    * poll doesn't have an opcode. bugzid: 591
+    * wait for MSGEND when data was received when opening the connection.
+      bugzid: 536
+    * mark the correct device as active source after a stream path change.
+      if the new address is not found, but the old address is, then mark the
+      old address as inactive. fixes TV switching back to the old active source
+      when it scans for devices. bugzid: 592
+    * ensure that the vendor ID is sent before trying to activate any SL device
+      bugzid: 574
+    * fixed possible crash when in CLibCEC::IsLibCECActiveSource() when libCEC
+      doesn't know which device is the active source. bugzid: 479
+    * correct handling CEC_USER_CONTROL_CODE_POWER. This ensures that the power
+      code always operates as a toggle, depending on the current state, and
+      that SetCurrentButton is always called for a valid user control code.
+      bugzid: 570
+    * frequency wasn't checked in GetTimeMs(), leading to incorrect wait times
+      on some windows systems
+    * refactored USB adapter communication. less locks, shorter locks, added
+      documentation, lots of clean ups and no more incoming messages that are
+      skipped
+    * the destructor of CSerialSocket didn't call Close()
+    * added guards in CSerialPort
+    * reset m_socket to INVALID_SERIAL_SOCKET_VALUE after closing the
+      connection
+    * always wait for thread exit in CThread's desctructor
+    * crash on exit after GetDeviceInformation()
+    * check whether the destination is valid before setting anything in
+      m_bWaitingForAck. fixes heap corruption and crash on exit. bugzid: 479
+    * bUseTVMenuLanguage from libcec_configuration wasn't copied in
+      SetConfiguration(), so this setting was reset to the default value
+      (enabled) every time. bugzid: 617
+    * request the vendor id of a device if needed when the device status is
+      changed into 'present'. bugzid: 361
+    * give priority to messages from the TV. removed the global lock in
+      CCECProcessor when sending. this is no longer needed. bugzid: 238
+    * add some bounds checking to the HDMI port number. bugzid: 508
+    * fixed usbser.sys copying in the driver .INF. bugzid: 503
+    * only update the physical address when it has actually changed. bugzid:
+      672
+    * moved the static variables in os-threads from the header to a separate
+      cpp file, or it could lead to problems when included multiple times
+    * don't send an active source command when the physical address couldn't be
+      set, or it might confuse other CEC devices
+    * serial socket timeouts. bugzid: 654
+    * fixed possible crash when trying to request a vendor id of a device when
+      the address of libCEC isn't known yet. bugzid: 654
+    * extra guard so no commands are transmitted without a valid initiator.
+      bugzid: 654
+    * moved the timed ping to a separate thread. bugzid: 654
+    * persist settings directly when they're changed, only persist settings
+      that actually changed, only instruct the device to persist the settings
+      in eeprom when something changed, and don't persist settings on exit.
+      bugzid: 715
+    * cec-config-gui: persist settings both in the eeprom and in the settings
+      xml file
+    * validate the input in CCECProcessor::IsActiveSource(). fixes potential
+      crash when the active source isn't known. bugzid: 671
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com>  Mon, 16 Apr 2012 18:03:00 +0100
+
+libcec (1.5-4) unstable; urgency=low
+
+  * changed/added:
+    * OS-X installation requirements and pointer
+    * full v2 firmware support:
+      * ping the adapter every 15 seconds. bugzid: 541
+      * added v2 msgcodes. bugzid: 543
+      * implemented the write methods for the v2 configuration. bugzid: 543
+      * included the logical addresses in the persisted configuration for v2.
+        bugzid: 543
+      * persist the configuration before closing the connection. only try to
+        persist the configuration when talking to a v2 firmware. bugzid: 543
+      * added GetSetting() to CUSBCECAdapterCommunication. bugzid: 543
+      * read the persisted settings from the ROM, and update it in
+        libcec_configuration if found. bugzid: 543
+      * cec-client: set bGetSettingsFromROM to 1 in cec-client. bugzid: 543
+      * cec-client: only read persisted EEPROM settings when -r or --rom is
+        provided as cmdline arg. bugzid: 543
+      * call SetControlledMode(false) as last command when closing the
+        connection in v2. bugzid: 542
+      * initial FreeBSD support: simply try to use ttyU*
+
+  * fixed:
+    * set controlled mode and retry to send the previous command if it failed
+      with MSGCODE_COMMAND_REJECTED
+    * m_iLineTimeout was never initialised and used. fixes MSGCODE_REJECTED
+      when transmitting a message and incorrect line timeouts being used
+    * mac configure compile bugzid: 157
+    * cec-config-gui: fixed application exit when the user clicked 'no' when
+      asked to reconnect. bugzid: 556
+    * only wait for multiple 'command accepted' response when sending a
+      transmit command, and only to 1 in other cases. bugzid: 543
+    * set the correct logical address mask before switching to autonomous mode.
+      bugzid: 543
+    * re-added SetLineTimeout(). bugzid: 543
+    * CUSBCECAdapterCommunication::SetControlledMode checked for the wrong sent
+      state
+    * os-x: don't add 0.5 before dividing in GetTimeMs()
+    * os-x: struct timespec now values weren't set correctly
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com>  Fri, 16 Mar 2012 10:12:00 +0100
+
 libcec (1.5-3) unstable; urgency=low
 
   * fixed:
diff --git a/README b/README
index c60a92110b70d14b70ac42ac79541f0faf8bd7ea..4b0621c43cf7e09a9ff3cd52b0d090fa08dc37e9 100644 (file)
--- a/README
+++ b/README
@@ -30,7 +30,7 @@ To compile libCEC on OS-X, you'll need the following dependencies:
 * autoconf 2.13 or later
 * automake 1.11 or later
 * pkg-config
-* xcode (TODO: version?)
+* xcode 3.2.6 or later
 
 To compile, execute the following commands (TODO: please verify):
 # autoreconf -vif
@@ -38,6 +38,8 @@ To compile, execute the following commands (TODO: please verify):
 # make
 # sudo make install
 
+# Note: You may need to copy pkg.m4 to your m4 sources directory
+
 ===============================================================================
                               === Windows ===
 ===============================================================================
index 23b1a4e40693ce0f2e76645e748199859a9f7b60..f8b8701be858a9fa009e3698083232941e0fdfed 100644 (file)
@@ -1,4 +1,4 @@
-AC_INIT([libcec], 1:5:0)
+AC_INIT([libcec], 1:6:0)
 AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
 
 AC_PROG_CXX
@@ -7,13 +7,16 @@ AC_PROG_LIBTOOL
 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")])
+    PKG_CHECK_MODULES([UDEV],[libudev],,[has_libudev="no"]; AC_MSG_WARN("library 'udev' is missing - adapter detection will not be available"))
     LIBS+=" -lrt"
     ;;
   *-apple-darwin*)
     has_libudev="no";
     LIBS+="-framework CoreVideo -framework IOKit"
     ;;
+  *-freebsd*)
+    has_libudev="no"
+    ;;
 esac
 
 if test "x$has_libudev" != "xno"; then
index 15d5a73e25bc2ec09e8a77494194342f1c4d3b76..d3b160e802b425813b64e25a0c2d3b1c6913fde1 100644 (file)
@@ -1,3 +1,135 @@
+libcec (1.6-1) unstable; urgency=medium
+
+  * changed/added:
+    * full firmware V2 support
+    * -o/--osdname argument for cec-client to set a custom osd name
+    * added the firmware version to cec-client's -l / --list-devices command.
+      bugzid: 631
+    * added power on command for Samsung AVR devices. bugzid: 361
+    * added buttoncode for 'channels list' on Samsung (0x96)
+    * don't check for the windows ddk when a prebuilt driver installer is
+      present
+    * respond to Get Menu Language. bugzid: 547. In the event that the menu
+      language is undefined in libcec (currently the default), the response
+      will be a feature abort.
+
+  * interface changes:
+    * added the firmware version to libcec_configuration (read-only). bumped
+      server version to 1.6.0. fixed 'unknown server version' message on
+      startup. bugzid: 631
+    * added a new setting to control whether to put the TV in standby when the
+      player is put in standby. added some missing bits (version numbers,
+      config) to LibCecSharp. bugzid: 558
+    * added an alert callback. bugzid: 462
+    * added bShutdownOnStandby to libcec_configuration. bugzid: 660. This
+      setting tells the client to shutdown when the TV switches off and is
+      complimentary to bPowerOffOnStandby, which tells the PC to suspend.
+      They are kept separate to maintain backwards compatability.
+
+  * fixed
+    * gcc 4.7 compilation
+    * poll doesn't have an opcode. bugzid: 591
+    * wait for MSGEND when data was received when opening the connection.
+      bugzid: 536
+    * mark the correct device as active source after a stream path change.
+      if the new address is not found, but the old address is, then mark the
+      old address as inactive. fixes TV switching back to the old active source
+      when it scans for devices. bugzid: 592
+    * ensure that the vendor ID is sent before trying to activate any SL device
+      bugzid: 574
+    * fixed possible crash when in CLibCEC::IsLibCECActiveSource() when libCEC
+      doesn't know which device is the active source. bugzid: 479
+    * correct handling CEC_USER_CONTROL_CODE_POWER. This ensures that the power
+      code always operates as a toggle, depending on the current state, and
+      that SetCurrentButton is always called for a valid user control code.
+      bugzid: 570
+    * frequency wasn't checked in GetTimeMs(), leading to incorrect wait times
+      on some windows systems
+    * refactored USB adapter communication. less locks, shorter locks, added
+      documentation, lots of clean ups and no more incoming messages that are
+      skipped
+    * the destructor of CSerialSocket didn't call Close()
+    * added guards in CSerialPort
+    * reset m_socket to INVALID_SERIAL_SOCKET_VALUE after closing the
+      connection
+    * always wait for thread exit in CThread's desctructor
+    * crash on exit after GetDeviceInformation()
+    * check whether the destination is valid before setting anything in
+      m_bWaitingForAck. fixes heap corruption and crash on exit. bugzid: 479
+    * bUseTVMenuLanguage from libcec_configuration wasn't copied in
+      SetConfiguration(), so this setting was reset to the default value
+      (enabled) every time. bugzid: 617
+    * request the vendor id of a device if needed when the device status is
+      changed into 'present'. bugzid: 361
+    * give priority to messages from the TV. removed the global lock in
+      CCECProcessor when sending. this is no longer needed. bugzid: 238
+    * add some bounds checking to the HDMI port number. bugzid: 508
+    * fixed usbser.sys copying in the driver .INF. bugzid: 503
+    * only update the physical address when it has actually changed. bugzid:
+      672
+    * moved the static variables in os-threads from the header to a separate
+      cpp file, or it could lead to problems when included multiple times
+    * don't send an active source command when the physical address couldn't be
+      set, or it might confuse other CEC devices
+    * serial socket timeouts. bugzid: 654
+    * fixed possible crash when trying to request a vendor id of a device when
+      the address of libCEC isn't known yet. bugzid: 654
+    * extra guard so no commands are transmitted without a valid initiator.
+      bugzid: 654
+    * moved the timed ping to a separate thread. bugzid: 654
+    * persist settings directly when they're changed, only persist settings
+      that actually changed, only instruct the device to persist the settings
+      in eeprom when something changed, and don't persist settings on exit.
+      bugzid: 715
+    * cec-config-gui: persist settings both in the eeprom and in the settings
+      xml file
+    * validate the input in CCECProcessor::IsActiveSource(). fixes potential
+      crash when the active source isn't known. bugzid: 671
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com>  Mon, 16 Apr 2012 18:03:00 +0100
+
+libcec (1.5-4) unstable; urgency=low
+
+  * changed/added:
+    * OS-X installation requirements and pointer
+    * full v2 firmware support:
+      * ping the adapter every 15 seconds. bugzid: 541
+      * added v2 msgcodes. bugzid: 543
+      * implemented the write methods for the v2 configuration. bugzid: 543
+      * included the logical addresses in the persisted configuration for v2.
+        bugzid: 543
+      * persist the configuration before closing the connection. only try to
+        persist the configuration when talking to a v2 firmware. bugzid: 543
+      * added GetSetting() to CUSBCECAdapterCommunication. bugzid: 543
+      * read the persisted settings from the ROM, and update it in
+        libcec_configuration if found. bugzid: 543
+      * cec-client: set bGetSettingsFromROM to 1 in cec-client. bugzid: 543
+      * cec-client: only read persisted EEPROM settings when -r or --rom is
+        provided as cmdline arg. bugzid: 543
+      * call SetControlledMode(false) as last command when closing the
+        connection in v2. bugzid: 542
+      * initial FreeBSD support: simply try to use ttyU*
+
+  * fixed:
+    * set controlled mode and retry to send the previous command if it failed
+      with MSGCODE_COMMAND_REJECTED
+    * m_iLineTimeout was never initialised and used. fixes MSGCODE_REJECTED
+      when transmitting a message and incorrect line timeouts being used
+    * mac configure compile bugzid: 157
+    * cec-config-gui: fixed application exit when the user clicked 'no' when
+      asked to reconnect. bugzid: 556
+    * only wait for multiple 'command accepted' response when sending a
+      transmit command, and only to 1 in other cases. bugzid: 543
+    * set the correct logical address mask before switching to autonomous mode.
+      bugzid: 543
+    * re-added SetLineTimeout(). bugzid: 543
+    * CUSBCECAdapterCommunication::SetControlledMode checked for the wrong sent
+      state
+    * os-x: don't add 0.5 before dividing in GetTimeMs()
+    * os-x: struct timespec now values weren't set correctly
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com>  Fri, 16 Mar 2012 10:12:00 +0100
+
 libcec (1.5-3) unstable; urgency=low
 
   * fixed:
index 1731e8dc26f40242834e06550fc9f86ef7abe5e6..9c8962622f3a78498a90b646522bbafecc800b36 100644 (file)
@@ -13,6 +13,7 @@ DriverVer=07/04/2011,1.0.0.0
 %MFGNAME%=DeviceList, NTamd64
 
 [DestinationDirs]
+FakeModemCopyFileSection=12
 DefaultDestDir=12
 
 
@@ -22,12 +23,9 @@ DefaultDestDir=12
 
 [DriverInstall.nt]
 include=mdmcpq.inf
-CopyFiles=DriverCopyFiles.nt
+CopyFiles=FakeModemCopyFileSection
 AddReg=DriverInstall.nt.AddReg
 
-[DriverCopyFiles.nt]
-usbser.sys,,,0x20
-
 [DriverInstall.nt.AddReg]
 HKR,,DevLoader,,*ntkern
 HKR,,NTMPDriver,,%DRIVERFILENAME%.sys
@@ -49,12 +47,9 @@ ServiceBinary=%12%\%DRIVERFILENAME%.sys
 
 [DriverInstall.NTamd64]
 include=mdmcpq.inf
-CopyFiles=DriverCopyFiles.NTamd64
+CopyFiles=FakeModemCopyFileSection
 AddReg=DriverInstall.NTamd64.AddReg
 
-[DriverCopyFiles.NTamd64]
-%DRIVERFILENAME%.sys,,,0x20
-
 [DriverInstall.NTamd64.AddReg]
 HKR,,DevLoader,,*ntkern
 HKR,,NTMPDriver,,%DRIVERFILENAME%.sys
index 0332088906e72a0a5c254ec42b023251aa02e00e..20e103b87806b966e403b22e7d06381c2f2b4592 100644 (file)
@@ -424,6 +424,15 @@ namespace CEC
      * @return true when libCEC is the active source on the bus, false otherwise.
      */
     virtual bool IsLibCECActiveSource(void) = 0;
+
+    /*!
+     * @brief Get information about the given device
+     * @param strPort The port to which the device is connected
+     * @param config The device configuration
+     * @param iTimeoutMs The timeout in milliseconds
+     * @return True when the device was found, false otherwise
+     */
+    virtual bool GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs = 10000) = 0;
   };
 };
 
index 7e3512f011edb340fe7fbfef48380d2dc6b9a800..779f211752755dd563090538bbc45d6b2942c2db 100644 (file)
@@ -296,6 +296,12 @@ extern DECLSPEC void cec_rescan_devices(void);
 
 extern DECLSPEC int cec_is_libcec_active_source(void);
 
+#ifdef __cplusplus
+extern DECLSPEC int cec_get_device_information(const char *strPort, CEC::libcec_configuration *config, uint32_t iTimeoutMs);
+#else
+extern DECLSPEC int cec_get_device_information(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs);
+#endif
+
 #ifdef __cplusplus
 };
 #endif
index 095416e445020446efb37413b2c4ae08cea30657..28c8d923b487d906d4cead98591f34c7443795a4 100644 (file)
@@ -79,7 +79,9 @@ namespace CEC {
 #define CEC_DEFAULT_SETTING_POWER_OFF_SHUTDOWN    1
 #define CEC_DEFAULT_SETTING_POWER_OFF_SCREENSAVER 1
 #define CEC_DEFAULT_SETTING_POWER_OFF_ON_STANDBY  1
+#define CEC_DEFAULT_SETTING_SHUTDOWN_ON_STANDBY   0
 #define CEC_DEFAULT_SETTING_SEND_INACTIVE_SOURCE  1
+#define CEC_DEFAULT_SETTING_POWER_OFF_DEVICES_STANDBY 1
 
 #define CEC_DEFAULT_TRANSMIT_RETRY_WAIT 500
 #define CEC_DEFAULT_TRANSMIT_TIMEOUT    1000
@@ -469,7 +471,8 @@ typedef enum cec_user_control_code
   CEC_USER_CONTROL_CODE_F5                          = 0x75,
   CEC_USER_CONTROL_CODE_DATA                        = 0x76,
   CEC_USER_CONTROL_CODE_AN_RETURN                   = 0x91,
-  CEC_USER_CONTROL_CODE_MAX                         = 0x91,
+  CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST            = 0x96,
+  CEC_USER_CONTROL_CODE_MAX                         = 0x96,
   CEC_USER_CONTROL_CODE_UNKNOWN
 } cec_user_control_code;
 
@@ -600,6 +603,21 @@ typedef enum cec_adapter_messagecode
   MSGCODE_START_BOOTLOADER,
   MSGCODE_SET_POWERSTATE,
   MSGCODE_SET_CONTROLLED,
+  MSGCODE_GET_AUTO_ENABLED,
+  MSGCODE_SET_AUTO_ENABLED,
+  MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS,
+  MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS,
+  MSGCODE_GET_LOGICAL_ADDRESS_MASK,
+  MSGCODE_SET_LOGICAL_ADDRESS_MASK,
+  MSGCODE_GET_PHYSICAL_ADDRESS,
+  MSGCODE_SET_PHYSICAL_ADDRESS,
+  MSGCODE_GET_DEVICE_TYPE,
+  MSGCODE_SET_DEVICE_TYPE,
+  MSGCODE_GET_HDMI_VERSION,
+  MSGCODE_SET_HDMI_VERSION,
+  MSGCODE_GET_OSD_NAME,
+  MSGCODE_SET_OSD_NAME,
+  MSGCODE_WRITE_EEPROM,
   MSGCODE_FRAME_EOM = 0x80,
   MSGCODE_FRAME_ACK = 0x40,
 } cec_adapter_messagecode;
@@ -818,7 +836,7 @@ typedef struct cec_device_type_list
     return bReturn;
   }
 
-  bool IsEmpty()
+  bool IsEmpty() const
   {
     bool bReturn(true);
     for (unsigned int iPtr = 0; bReturn && iPtr < 5; iPtr++)
@@ -906,12 +924,29 @@ typedef struct cec_logical_addresses
 #endif
 } cec_logical_addresses;
 
+typedef enum libcec_alert
+{
+  CEC_ALERT_SERVICE_DEVICE
+} libcec_alert;
+
+typedef enum libcec_parameter_type
+{
+  CEC_PARAMETER_TYPE_STRING
+} libcec_parameter_type;
+
+struct libcec_parameter
+{
+  libcec_parameter_type paramType;
+  void*                 paramData;
+};
+
 struct libcec_configuration;
 
 typedef int (CEC_CDECL* CBCecLogMessageType)(void *param, const cec_log_message &);
 typedef int (CEC_CDECL* CBCecKeyPressType)(void *param, const cec_keypress &);
 typedef int (CEC_CDECL* CBCecCommandType)(void *param, const cec_command &);
 typedef int (CEC_CDECL* CBCecConfigurationChangedType)(void *param, const libcec_configuration &);
+typedef int (CEC_CDECL* CBCecAlertType)(void *param, const libcec_alert, const libcec_parameter &);
 
 typedef struct ICECCallbacks
 {
@@ -942,6 +977,14 @@ typedef struct ICECCallbacks
    * @return 1 when ok, 0 otherwise
    */
   CBCecConfigurationChangedType CBCecConfigurationChanged;
+
+  /*!
+   * @Brief Transfer a libcec alert message from libCEC to the client
+   * @Param alert The alert type transfer.
+   * @Param data  Misc. additional information.
+   * @return 1 when ok, 0 otherwise
+   */
+  CBCecAlertType CBCecAlert;
 } ICECCallbacks;
 
 typedef enum cec_client_version
@@ -949,7 +992,9 @@ typedef enum cec_client_version
   CEC_CLIENT_VERSION_PRE_1_5 = 0,
   CEC_CLIENT_VERSION_1_5_0   = 0x1500,
   CEC_CLIENT_VERSION_1_5_1   = 0x1501,
-  CEC_CLIENT_VERSION_1_5_2   = 0x1502
+  CEC_CLIENT_VERSION_1_5_2   = 0x1502,
+  CEC_CLIENT_VERSION_1_5_3   = 0x1503,
+  CEC_CLIENT_VERSION_1_6_0   = 0x1600
 } cec_client_version;
 
 typedef enum cec_server_version
@@ -957,7 +1002,9 @@ typedef enum cec_server_version
   CEC_SERVER_VERSION_PRE_1_5 = 0,
   CEC_SERVER_VERSION_1_5_0   = 0x1500,
   CEC_SERVER_VERSION_1_5_1   = 0x1501,
-  CEC_SERVER_VERSION_1_5_2   = 0x1502
+  CEC_SERVER_VERSION_1_5_2   = 0x1502,
+  CEC_SERVER_VERSION_1_5_3   = 0x1503,
+  CEC_SERVER_VERSION_1_6_0   = 0x1600
 } cec_server_version;
 
 typedef struct libcec_configuration
@@ -980,12 +1027,17 @@ typedef struct libcec_configuration
   uint8_t               bUseTVMenuLanguage;   /*!< use the menu language of the TV in the player application */
   uint8_t               bActivateSource;      /*!< make libCEC the active source on the bus when starting the player application */
   uint8_t               bPowerOffScreensaver; /*!< put devices in standby mode when activating the screensaver */
-  uint8_t               bPowerOffOnStandby;   /*!< put this PC in standby mode when the TV is switched off */
+  uint8_t               bPowerOffOnStandby;   /*!< put this PC in standby mode when the TV is switched off. only used when bShutdownOnStandby = 0  */
   uint8_t               bSendInactiveSource;  /*!< send an 'inactive source' message when stopping the player. added in 1.5.1 */
 
   void *                callbackParam;        /*!< the object to pass along with a call of the callback methods. NULL to ignore */
   ICECCallbacks *       callbacks;            /*!< the callback methods to use. set this to NULL when not using callbacks */
 
+  cec_logical_addresses logicalAddresses;     /*!< the current logical addresses. read-only. added in 1.5.3 */
+  uint16_t              iFirmwareVersion;     /*!< the firmware version of the adapter. added in 1.6.0 */
+  uint8_t               bPowerOffDevicesOnStandby; /*!< put devices in standby when the PC/player is put in standby. added in 1.6.0 */
+  uint8_t               bShutdownOnStandby;   /*!< shutdown this PC when the TV is switched off. only used when bPowerOffOnStandby = 0. added in 1.6.0 */
+
 #ifdef __cplusplus
   void Clear(void)
   {
@@ -1012,7 +1064,11 @@ typedef struct libcec_configuration
     #endif
     bPowerOffScreensaver = CEC_DEFAULT_SETTING_POWER_OFF_SCREENSAVER;
     bPowerOffOnStandby   = CEC_DEFAULT_SETTING_POWER_OFF_ON_STANDBY;
+    bShutdownOnStandby   = CEC_DEFAULT_SETTING_SHUTDOWN_ON_STANDBY;
     bSendInactiveSource  = CEC_DEFAULT_SETTING_SEND_INACTIVE_SOURCE;
+    logicalAddresses.Clear();
+    iFirmwareVersion          = CEC_FW_VERSION_UNKNOWN;
+    bPowerOffDevicesOnStandby = CEC_DEFAULT_SETTING_POWER_OFF_DEVICES_STANDBY;
 
     callbackParam    = NULL;
     callbacks        = NULL;
index 180f4f050acf9882a287d1144d293a21bf783876..cc3fb1333f8b18d5d2ade9a6e46fad6e45f4aca7 100644 (file)
Binary files a/project/cec-config.rc and b/project/cec-config.rc differ
index 2243ff94368802b028f05b9089833c39bacdff38..6ca84e430d4c449f6d9a0feb78aab0984c847f26 100644 (file)
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\src\cec-config\cec-config.cpp" />
+    <ClCompile Include="..\src\lib\platform\windows\os-threads.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="cec-config.rc" />
index 0d3f64797f092fe5ccf5b3b2b03f7ddf883ff82e..2ee5127e2cdbc3def865c83e95a59740c54df0cd 100644 (file)
@@ -21,6 +21,9 @@
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\src\cec-config\cec-config.cpp" />
+    <ClCompile Include="..\src\lib\platform\windows\os-threads.cpp">
+      <Filter>platform</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="cec-config.rc" />
index 4de5b4fa94028e021d401b8d4da0d80115dfe513..c51d36b6dff587a1c3101fbd754523cb221e6536 100644 (file)
Binary files a/project/libcec.rc and b/project/libcec.rc differ
index 3c1e89b837222c5325caf766a1bdb2899582cf4d..aed370cb44fbe04e1eff5e0cdd524018cf7a26ca 100644 (file)
     <ClInclude Include="..\include\cecloader.h" />
     <ClInclude Include="..\include\cectypes.h" />
     <ClInclude Include="..\src\lib\adapter\AdapterCommunication.h" />
+    <ClInclude Include="..\src\lib\adapter\USBCECAdapterCommands.h" />
     <ClInclude Include="..\src\lib\adapter\USBCECAdapterCommunication.h" />
     <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\CECProcessor.h" />
     <ClInclude Include="..\src\lib\devices\CECAudioSystem.h" />
     <ClInclude Include="..\src\lib\devices\CECBusDevice.h" />
     <ClInclude Include="..\src\lib\platform\windows\stdint.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="..\src\lib\adapter\USBCECAdapterCommands.cpp" />
     <ClCompile Include="..\src\lib\adapter\USBCECAdapterCommunication.cpp" />
     <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\CECProcessor.cpp" />
     <ClCompile Include="..\src\lib\devices\CECAudioSystem.cpp" />
     <ClCompile Include="..\src\lib\devices\CECBusDevice.cpp" />
@@ -73,6 +78,7 @@
     <ClCompile Include="..\src\lib\LibCEC.cpp" />
     <ClCompile Include="..\src\lib\LibCECC.cpp" />
     <ClCompile Include="..\src\lib\LibCECDll.cpp" />
+    <ClCompile Include="..\src\lib\platform\windows\os-threads.cpp" />
     <ClCompile Include="..\src\lib\platform\windows\serialport.cpp" />
   </ItemGroup>
   <ItemGroup>
index 56f1a464a29d6120280b634ab8b04d097e78c28f..fdd9df3bf76398d27b6b680502dc402da9d0885a 100644 (file)
     <ClInclude Include="..\src\lib\implementations\RLCommandHandler.h">
       <Filter>implementations</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\lib\adapter\USBCECAdapterCommands.h">
+      <Filter>adapter</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\lib\adapter\USBCECAdapterMessageQueue.h">
+      <Filter>adapter</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\src\lib\CECProcessor.cpp" />
     <ClCompile Include="..\src\lib\devices\CECTV.cpp">
       <Filter>devices</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\lib\platform\windows\os-threads.cpp">
+      <Filter>platform\windows</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\lib\platform\windows\serialport.cpp">
       <Filter>platform\windows</Filter>
     </ClCompile>
     <ClCompile Include="..\src\lib\implementations\RLCommandHandler.cpp">
       <Filter>implementations</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\lib\adapter\USBCECAdapterCommands.cpp">
+      <Filter>adapter</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\lib\adapter\USBCECAdapterMessage.cpp">
+      <Filter>adapter</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\lib\adapter\USBCECAdapterMessageQueue.cpp">
+      <Filter>adapter</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="libcec.rc" />
index 15edfb5a5f2080c5845050e16f3992e72fee829d..d4bfa3b625bbc8a883001b6e3d17bfb326b0b5af 100644 (file)
Binary files a/project/testclient.rc and b/project/testclient.rc differ
index fb1957290a362c2ea309f2c9388f09baa73f1292..0862f27ccda27a72bbcdf1256d89afe7bafd0c9f 100644 (file)
     <ClInclude Include="..\include\cecloader.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="..\src\lib\platform\windows\os-threads.cpp" />
     <ClCompile Include="..\src\testclient\main.cpp" />
   </ItemGroup>
   <ItemGroup>
index 70acf3d3041ea6202fe042add81dbe2caf3622f8..0fd21fb85474a02309eeccc6f3f852b19d581ad8 100644 (file)
@@ -18,6 +18,9 @@
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\src\testclient\main.cpp" />
+    <ClCompile Include="..\src\lib\platform\windows\os-threads.cpp">
+      <Filter>platform</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="testclient.rc" />
index e12aca92a80ebcf6ba338bba6b5442d790634ddd..bc1156738cfa00eb79db2a2a6cbc630eda50b504 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.5.2.0")]
-[assembly: AssemblyFileVersion("1.5.2.0")]
+[assembly: AssemblyVersion("1.6.0.0")]
+[assembly: AssemblyFileVersion("1.6.0.0")]
index 5dfbef2d600b2f90fa096f30d5a2d513a24ef9f2..d70e9d0f4605ad924be62a01973569b633a39005 100644 (file)
@@ -13,7 +13,7 @@ using namespace System::Security::Permissions;
 [assembly:AssemblyTrademarkAttribute("")];
 [assembly:AssemblyCultureAttribute("")];
 
-[assembly:AssemblyVersionAttribute("1.5.2.0")];
+[assembly:AssemblyVersionAttribute("1.6.0.0")];
 
 [assembly:ComVisible(false)];
 [assembly:CLSCompliantAttribute(true)];
index 8cdaa89a0e9e797f2931fedb936534c3009a2bd2..cc888c79ad320646b1bb9139b3d6803736d62bdf 100644 (file)
@@ -83,6 +83,29 @@ namespace CecSharp
                Broadcast        = 15
        };
 
+       public enum class CecAlert
+       {
+               ServiceDevice = 1
+       };
+
+       public enum class CecParameterType
+       {
+               ParameterTypeString = 1
+       };
+
+       public ref class CecParameter
+       {
+       public:
+               CecParameter(CecParameterType type, System::String ^ strData)
+               {
+                       Type = type;
+                       Data = strData;
+               }
+
+               property CecParameterType Type;
+               property System::String ^ Data;
+       };
+
        public enum class CecPowerStatus
        {
                On                      = 0x00,
@@ -328,7 +351,9 @@ namespace CecSharp
                VersionPre1_5 = 0,
                Version1_5_0  = 0x1500,
     Version1_5_1  = 0x1501,
-    Version1_5_2  = 0x1502
+    Version1_5_2  = 0x1502,
+    Version1_5_3  = 0x1503,
+    Version1_6_0  = 0x1600
        };
 
   public enum class CecServerVersion
@@ -336,7 +361,9 @@ namespace CecSharp
                VersionPre1_5 = 0,
                Version1_5_0  = 0x1500,
     Version1_5_1  = 0x1501,
-    Version1_5_2  = 0x1502
+    Version1_5_2  = 0x1502,
+    Version1_5_3  = 0x1503,
+    Version1_6_0  = 0x1600
        };
 
        public ref class CecAdapter
@@ -552,6 +579,9 @@ namespace CecSharp
                        PowerOffScreensaver = CEC_DEFAULT_SETTING_POWER_OFF_SCREENSAVER == 1;
                        PowerOffOnStandby   = CEC_DEFAULT_SETTING_POWER_OFF_ON_STANDBY == 1;
       SendInactiveSource  = CEC_DEFAULT_SETTING_SEND_INACTIVE_SOURCE == 1;
+      LogicalAddresses    = gcnew CecLogicalAddresses();
+      FirmwareVersion     = 1;
+      PowerOffDevicesOnStandby = CEC_DEFAULT_SETTING_POWER_OFF_DEVICES_STANDBY == 1;
                }
 
                void SetCallbacks(CecCallbackMethods ^callbacks)
@@ -591,7 +621,23 @@ namespace CecSharp
 
                        PowerOffScreensaver = config.bPowerOffScreensaver == 1;
                        PowerOffOnStandby = config.bPowerOffOnStandby == 1;
-      SendInactiveSource = config.bSendInactiveSource == 1;
+
+      if (ServerVersion >= CecServerVersion::Version1_5_1)
+        SendInactiveSource = config.bSendInactiveSource == 1;
+
+      if (ServerVersion >= CecServerVersion::Version1_5_3)
+      {
+        LogicalAddresses->Clear();
+                         for (uint8_t iPtr = 0; iPtr <= 16; iPtr++)
+                                 if (config.logicalAddresses[iPtr])
+                                       LogicalAddresses->Set((CecLogicalAddress)iPtr);
+      }
+
+      if (ServerVersion >= CecServerVersion::Version1_6_0)
+      {
+        FirmwareVersion          = config.iFirmwareVersion;
+        PowerOffDevicesOnStandby = config.bPowerOffDevicesOnStandby == 1;
+      }
                }
 
                property System::String ^     DeviceName;
@@ -613,6 +659,9 @@ namespace CecSharp
                property bool                 PowerOffScreensaver;
                property bool                 PowerOffOnStandby;
     property bool                 SendInactiveSource;
+    property CecLogicalAddresses ^LogicalAddresses;
+    property uint16_t             FirmwareVersion;
+    property bool                 PowerOffDevicesOnStandby;
 
                property CecCallbackMethods ^ Callbacks;
        };
@@ -624,11 +673,13 @@ namespace CecSharp
        typedef int (__stdcall *KEYCB)    (const CEC::cec_keypress &key);
        typedef int (__stdcall *COMMANDCB)(const CEC::cec_command &command);
        typedef int (__stdcall *CONFIGCB) (const CEC::libcec_configuration &config);
+       typedef int (__stdcall *ALERTCB)  (const CEC::libcec_alert, const CEC::libcec_parameter &data);
 
        static LOGCB              g_logCB;
        static KEYCB              g_keyCB;
        static COMMANDCB          g_commandCB;
        static CONFIGCB           g_configCB;
+       static ALERTCB            g_alertCB;
        static CEC::ICECCallbacks g_cecCallbacks;
 
        int CecLogMessageCB(void *cbParam, const CEC::cec_log_message &message)
@@ -659,12 +710,20 @@ namespace CecSharp
                return 0;
        }
 
+       int CecAlertCB(void *cbParam, const CEC::libcec_alert alert, const CEC::libcec_parameter &data)
+       {
+               if (g_alertCB)
+                       return g_alertCB(alert, data);
+               return 0;
+       }
+
        #pragma managed
        // delegates for the unmanaged callback methods
        public delegate int CecLogMessageManagedDelegate(const CEC::cec_log_message &);
        public delegate int CecKeyPressManagedDelegate(const CEC::cec_keypress &);
        public delegate int CecCommandManagedDelegate(const CEC::cec_command &);
        public delegate int CecConfigManagedDelegate(const CEC::libcec_configuration &);
+       public delegate int CecAlertManagedDelegate(const CEC::libcec_alert, const CEC::libcec_parameter &);
 
        // callback method interface
        public ref class CecCallbackMethods
@@ -725,6 +784,12 @@ namespace CecSharp
                {
                        return 0;
                }
+
+               virtual int ReceiveAlert(CecAlert alert, CecParameter ^ data)
+               {
+                       return 0;
+               }
+
        protected:
                // managed callback methods
                int CecLogMessageManaged(const CEC::cec_log_message &message)
@@ -768,6 +833,22 @@ namespace CecSharp
                        return iReturn;
                }
 
+               int CecAlertManaged(const CEC::libcec_alert alert, const CEC::libcec_parameter &data)
+               {
+                       int iReturn(0);
+                       if (m_bHasCallbacks)
+                       {
+                               CecParameterType newType = (CecParameterType)data.paramType;
+                               if (newType == CecParameterType::ParameterTypeString)
+                               {
+                                       System::String ^ newData = gcnew System::String((const char *)data.paramData, 0, 128);
+                                       CecParameter ^ newParam = gcnew CecParameter(newType, newData);
+                                   iReturn = m_callbacks->ReceiveAlert((CecAlert)alert, newParam);
+                               }
+                       }
+                       return iReturn;
+               }
+
                void DestroyDelegates()
                {
       m_bHasCallbacks = false;
@@ -812,6 +893,12 @@ namespace CecSharp
         g_configCB                  = static_cast<CONFIGCB>(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_configDelegate).ToPointer());
         g_cecCallbacks.CBCecConfigurationChanged = CecConfigCB;
 
+        // create the delegate method for the alert callback
+        m_alertDelegate            = gcnew CecAlertManagedDelegate(this, &CecCallbackMethods::CecAlertManaged);
+        m_alertGCHandle            = System::Runtime::InteropServices::GCHandle::Alloc(m_alertDelegate);
+        g_alertCB                  = static_cast<ALERTCB>(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_alertDelegate).ToPointer());
+        g_cecCallbacks.CBCecAlert  = CecAlertCB;
+
         delete context;
         m_bDelegatesCreated = true;
       }
@@ -833,6 +920,10 @@ namespace CecSharp
                static System::Runtime::InteropServices::GCHandle m_configGCHandle;
                CONFIGCB                                          m_configCallback;
 
+               CecAlertManagedDelegate ^                         m_alertDelegate;
+               static System::Runtime::InteropServices::GCHandle m_alertGCHandle;
+               CONFIGCB                                          m_alertCallback;
+
                CecCallbackMethods ^ m_callbacks;
          bool                 m_bHasCallbacks;
     bool                 m_bDelegatesCreated;
index 76dd00624e7948d3fcbf9eedfcf7d73f156f1b86..2338671d577dfa53448185aacfd58abafc89a753 100644 (file)
@@ -117,7 +117,13 @@ namespace CecSharp
                        }
                        config.bPowerOffScreensaver = netConfig->PowerOffScreensaver ? 1 : 0;
                        config.bPowerOffOnStandby   = netConfig->PowerOffOnStandby ? 1 : 0;
-      config.bSendInactiveSource  = netConfig->SendInactiveSource ? 1 : 0;
+
+      if (netConfig->ServerVersion >= CecServerVersion::Version1_5_1)
+        config.bSendInactiveSource  = netConfig->SendInactiveSource ? 1 : 0;
+
+      if (netConfig->ServerVersion >= CecServerVersion::Version1_6_0)
+        config.bPowerOffDevicesOnStandby  = netConfig->PowerOffDevicesOnStandby ? 1 : 0;
+
                        config.callbacks            = &g_cecCallbacks;
                }
 
@@ -481,6 +487,31 @@ namespace CecSharp
                        return bReturn;
                }
 
+    bool IsLibCECActiveSource()
+    {
+      return m_libCec->IsLibCECActiveSource();
+    }
+
+    bool GetDeviceInformation(String ^ port, LibCECConfiguration ^configuration, uint32_t timeoutMs)
+    {
+      bool bReturn(false);
+      marshal_context ^ context = gcnew marshal_context();
+
+      libcec_configuration config;
+                       config.Clear();
+
+      const char* strPortC = port->Length > 0 ? context->marshal_as<const char*>(port) : NULL;
+
+      if (m_libCec->GetDeviceInformation(strPortC, &config, timeoutMs))
+                       {
+                               configuration->Update(config);
+        bReturn = true;
+                       }
+
+      delete context;
+      return bReturn;
+    }
+
                String ^ ToString(CecLogicalAddress iAddress)
                {
                        const char *retVal = m_libCec->ToString((cec_logical_address)iAddress);
index 94ce2ad5a016212f4897d41c74c4c4457fc13d2e..8fb066f2e9349915ad260c15d55306ea78792218 100644 (file)
@@ -321,6 +321,12 @@ namespace CecConfigGui
           }
           SetControlVisible(pProgress, false);
           break;
+        case UpdateEventType.ExitApplication:
+          ActiveProcess = null;
+          SetControlsEnabled(false);
+          SetControlVisible(pProgress, false);
+          Application.Exit();
+          break;
       }
     }
 
@@ -545,100 +551,93 @@ namespace CecConfigGui
       Config.WakeDevices = WakeDevices;
       Config.PowerOffDevices = PowerOffDevices;
 
-      if (!Lib.CanPersistConfiguration())
+      /* save settings in the eeprom */
+      Lib.PersistConfiguration(Config);
+
+      /* and in xml */
+      if (ActiveProcess == null)
       {
-        if (ActiveProcess == null)
-        {
-          SetControlsEnabled(false);
-          string xbmcDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\XBMC\userdata\peripheral_data";
-          string defaultDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+        SetControlsEnabled(false);
+        string xbmcDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\XBMC\userdata\peripheral_data";
+        string defaultDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
 
-          SaveFileDialog dialog = new SaveFileDialog()
+        SaveFileDialog dialog = new SaveFileDialog()
+        {
+          Title = "Where do you want to store the settings?",
+          InitialDirectory = Directory.Exists(xbmcDir) ? xbmcDir : defaultDir,
+          FileName = "usb_2548_1001.xml",
+          Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*",
+          FilterIndex = 1
+        };
+
+        if (dialog.ShowDialog() == DialogResult.OK)
+        {
+          FileStream fs = null;
+          string error = string.Empty;
+          try
+          {
+            fs = (FileStream)dialog.OpenFile();
+          }
+          catch (Exception ex)
           {
-            Title = "Where do you want to store the settings?",
-            InitialDirectory = Directory.Exists(xbmcDir) ? xbmcDir : defaultDir,
-            FileName = "usb_2548_1001.xml",
-            Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*",
-            FilterIndex = 1
-          };
-
-          if (dialog.ShowDialog() == DialogResult.OK)
+            error = ex.Message;
+          }
+          if (fs == null)
           {
-            FileStream fs = null;
-            string error = string.Empty;
-            try
-            {
-              fs = (FileStream)dialog.OpenFile();
-            }
-            catch (Exception ex)
-            {
-              error = ex.Message;
-            }
-            if (fs == null)
-            {
-              MessageBox.Show("Cannot open '" + dialog.FileName + "' for writing" + (error.Length > 0 ? ": " + error : string.Empty ), "Pulse-Eight USB-CEC Adapter", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            }
-            else
-            {
-              StreamWriter writer = new StreamWriter(fs);
-              StringBuilder output = new StringBuilder();
-              output.AppendLine("<settings>");
-              output.AppendLine("<setting id=\"cec_hdmi_port\" value=\"" + Config.HDMIPort + "\" />");
-              output.AppendLine("<setting id=\"connected_device\" value=\"" + (Config.BaseDevice == CecLogicalAddress.AudioSystem ? 5 : 0) + "\" />");
-              output.AppendLine("<setting id=\"cec_power_on_startup\" value=\"" + (Config.ActivateSource ? 1 : 0) + "\" />");
-              output.AppendLine("<setting id=\"cec_power_off_shutdown\" value=\"" + (Config.PowerOffDevices.IsSet(CecLogicalAddress.Broadcast) ? 1 : 0) + "\" />");
-              output.AppendLine("<setting id=\"cec_standby_screensaver\" value=\"" + (Config.PowerOffScreensaver ? 1 : 0) + "\" />");
-              output.AppendLine("<setting id=\"standby_pc_on_tv_standby\" value=\"" + (Config.PowerOffOnStandby ? 1 : 0) + "\" />");
-              output.AppendLine("<setting id=\"use_tv_menu_language\" value=\"" + (Config.UseTVMenuLanguage ? 1 : 0) + "\" />");
-              output.AppendLine("<setting id=\"enabled\" value=\"1\" />");
-              output.AppendLine("<setting id=\"port\" value=\"\" />");
-
-              // only supported by 1.5.0+ clients
-              output.AppendLine("<!-- the following lines are only supported by v1.5.0+ clients -->");
-              output.AppendLine("<setting id=\"activate_source\" value=\"" + (Config.ActivateSource ? 1 : 0) + "\" />");
-              output.AppendLine("<setting id=\"physical_address\" value=\"" + string.Format("{0,4:X}", cbOverrideAddress.Checked ? Config.PhysicalAddress : 0).Trim() + "\" />");
-              output.AppendLine("<setting id=\"device_type\" value=\"" + (int)Config.DeviceTypes.Types[0] + "\" />");
-              output.AppendLine("<setting id=\"tv_vendor\" value=\"" + string.Format("{0,6:X}", (int)Config.TvVendor).Trim() + "\" />");
-
-              output.Append("<setting id=\"wake_devices\" value=\"");
-              StringBuilder strWakeDevices = new StringBuilder();
-              foreach (CecLogicalAddress addr in Config.WakeDevices.Addresses)
-                if (addr != CecLogicalAddress.Unknown)
-                  strWakeDevices.Append(" " + (int)addr);
-              output.Append(strWakeDevices.ToString().Trim());
-              output.AppendLine("\" />");
-
-              output.Append("<setting id=\"standby_devices\" value=\"");
-              StringBuilder strSleepDevices = new StringBuilder();
-              foreach (CecLogicalAddress addr in Config.PowerOffDevices.Addresses)
-                if (addr != CecLogicalAddress.Unknown)
-                  strSleepDevices.Append(" " + (int)addr);
-              output.Append(strSleepDevices.ToString().Trim()); 
-              output.AppendLine("\" />");
-
-              // only supported by 1.5.1+ clients
-              output.AppendLine("<!-- the following lines are only supported by v1.5.1+ clients -->");
-              output.AppendLine("<setting id=\"send_inactive_source\" value=\"" + (Config.SendInactiveSource ? 1 : 0) + "\" />");
-
-              output.AppendLine("</settings>");
-              writer.Write(output.ToString());
-              writer.Close();
-              fs.Close();
-              fs.Dispose();
-              MessageBox.Show("Settings are stored.", "Pulse-Eight USB-CEC Adapter", MessageBoxButtons.OK, MessageBoxIcon.Information);
-            }
+            MessageBox.Show("Cannot open '" + dialog.FileName + "' for writing" + (error.Length > 0 ? ": " + error : string.Empty ), "Pulse-Eight USB-CEC Adapter", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          }
+          else
+          {
+            StreamWriter writer = new StreamWriter(fs);
+            StringBuilder output = new StringBuilder();
+            output.AppendLine("<settings>");
+            output.AppendLine("<setting id=\"cec_hdmi_port\" value=\"" + Config.HDMIPort + "\" />");
+            output.AppendLine("<setting id=\"connected_device\" value=\"" + (Config.BaseDevice == CecLogicalAddress.AudioSystem ? 5 : 0) + "\" />");
+            output.AppendLine("<setting id=\"cec_power_on_startup\" value=\"" + (Config.ActivateSource ? 1 : 0) + "\" />");
+            output.AppendLine("<setting id=\"cec_power_off_shutdown\" value=\"" + (Config.PowerOffDevices.IsSet(CecLogicalAddress.Broadcast) ? 1 : 0) + "\" />");
+            output.AppendLine("<setting id=\"cec_standby_screensaver\" value=\"" + (Config.PowerOffScreensaver ? 1 : 0) + "\" />");
+            output.AppendLine("<setting id=\"standby_pc_on_tv_standby\" value=\"" + (Config.PowerOffOnStandby ? 1 : 0) + "\" />");
+            output.AppendLine("<setting id=\"use_tv_menu_language\" value=\"" + (Config.UseTVMenuLanguage ? 1 : 0) + "\" />");
+            output.AppendLine("<setting id=\"enabled\" value=\"1\" />");
+            output.AppendLine("<setting id=\"port\" value=\"\" />");
+
+            // only supported by 1.5.0+ clients
+            output.AppendLine("<!-- the following lines are only supported by v1.5.0+ clients -->");
+            output.AppendLine("<setting id=\"activate_source\" value=\"" + (Config.ActivateSource ? 1 : 0) + "\" />");
+            output.AppendLine("<setting id=\"physical_address\" value=\"" + string.Format("{0,4:X}", cbOverrideAddress.Checked ? Config.PhysicalAddress : 0).Trim() + "\" />");
+            output.AppendLine("<setting id=\"device_type\" value=\"" + (int)Config.DeviceTypes.Types[0] + "\" />");
+            output.AppendLine("<setting id=\"tv_vendor\" value=\"" + string.Format("{0,6:X}", (int)Config.TvVendor).Trim() + "\" />");
+
+            output.Append("<setting id=\"wake_devices\" value=\"");
+            StringBuilder strWakeDevices = new StringBuilder();
+            foreach (CecLogicalAddress addr in Config.WakeDevices.Addresses)
+              if (addr != CecLogicalAddress.Unknown)
+                strWakeDevices.Append(" " + (int)addr);
+            output.Append(strWakeDevices.ToString().Trim());
+            output.AppendLine("\" />");
+
+            output.Append("<setting id=\"standby_devices\" value=\"");
+            StringBuilder strSleepDevices = new StringBuilder();
+            foreach (CecLogicalAddress addr in Config.PowerOffDevices.Addresses)
+              if (addr != CecLogicalAddress.Unknown)
+                strSleepDevices.Append(" " + (int)addr);
+            output.Append(strSleepDevices.ToString().Trim()); 
+            output.AppendLine("\" />");
+
+            // only supported by 1.5.1+ clients
+            output.AppendLine("<!-- the following lines are only supported by v1.5.1+ clients -->");
+            output.AppendLine("<setting id=\"send_inactive_source\" value=\"" + (Config.SendInactiveSource ? 1 : 0) + "\" />");
+
+            output.AppendLine("</settings>");
+            writer.Write(output.ToString());
+            writer.Close();
+            fs.Close();
+            fs.Dispose();
+            MessageBox.Show("Settings are stored.", "Pulse-Eight USB-CEC Adapter", MessageBoxButtons.OK, MessageBoxIcon.Information);
           }
-          SetControlsEnabled(true);
         }
+        SetControlsEnabled(true);
       }
-      else
-      {
-        if (!Lib.PersistConfiguration(Config))
-          MessageBox.Show("Could not persist the new settings.", "Pulse-Eight USB-CEC Adapter", MessageBoxButtons.OK, MessageBoxIcon.Error);
-        else
-          MessageBox.Show("Settings are stored.", "Pulse-Eight USB-CEC Adapter", MessageBoxButtons.OK, MessageBoxIcon.Information);
-      }
-      SetControlsEnabled(true);
     }
 
     private void bReloadConfig_Click(object sender, EventArgs e)
index a1206f99126633184aa7a86119ba51ec14a948ed..829c8473b63d44d104c8472facda462faad6d3ac 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.5.2.0")]
-[assembly: AssemblyFileVersion("1.5.2.0")]
+[assembly: AssemblyVersion("1.6.0.0")]
+[assembly: AssemblyFileVersion("1.6.0.0")]
index fedc31398e700c7721b9bb89f7f91ec77f51424a..8c198f4c60a20f86042aada349c0c0618ca81118 100644 (file)
@@ -22,7 +22,10 @@ namespace CecConfigGui.actions
       {
         DialogResult result = MessageBox.Show("Could not detect to any CEC adapter. Please check your configuration. Do you want to try again?", "Pulse-Eight USB-CEC Adapter", MessageBoxButtons.YesNo);
         if (result == DialogResult.No)
-          Application.Exit();
+        {
+          SendEvent(UpdateEventType.ExitApplication);
+          return;
+        }
         else
           adapters = Lib.FindAdapters(string.Empty);
       }
@@ -31,7 +34,10 @@ namespace CecConfigGui.actions
       {
         DialogResult result = MessageBox.Show("Could not connect to any CEC adapter. Please check your configuration. Do you want to try again?", "Pulse-Eight USB-CEC Adapter", MessageBoxButtons.YesNo);
         if (result == DialogResult.No)
-          Application.Exit();
+        {
+          SendEvent(UpdateEventType.ExitApplication);
+          return;
+        }
       }
 
       SendEvent(UpdateEventType.ProgressBar, 20);
index a7e2df6bbb2227e83aea53d9692f87dc1c517941..4a3e701a9099c91fbaa46fc24128593fa8f14231 100644 (file)
@@ -17,7 +17,8 @@ namespace CecConfigGui
     AVRVendorId,
     Configuration,
     MenuLanguage,
-    PollDevices
+    PollDevices,
+    ExitApplication
   }
 
   public class UpdateEvent : EventArgs
index 456369180c069a9a66122ef720314cf38249cab5..1e6c96fd9272e6763b7fd265294019710f433202 100644 (file)
@@ -145,6 +145,7 @@ void EnableCallbacks(ICECAdapter *adapter)
   g_callbacks.CBCecKeyPress   = &CecKeyPress;
   g_callbacks.CBCecCommand    = &CecCommand;
   g_callbacks.CBCecConfigurationChanged = NULL;
+  g_callbacks.CBCecAlert      = NULL;
   adapter->EnableCallbacks(NULL, &g_callbacks);
 }
 
@@ -228,18 +229,20 @@ bool OpenConnection(cec_device_type type = CEC_DEVICE_TYPE_RECORDING_DEVICE)
 
 int8_t FindPhysicalAddressPortNumber(void)
 {
-  PrintToStdOut("Enter the HDMI port number to which you connected your CEC adapter, followed by <enter>. Only port 1, 2, 3 or 4 are supported. Anything else will cancel this wizard.");
+  PrintToStdOut("Enter the HDMI port number to which you connected your CEC adapter, followed by <enter>. Valid ports are in the range 1-15. Anything else will cancel this wizard.");
   string input;
   getline(cin, input);
   cin.clear();
-  if (input.empty() || (input != "1" && input != "2" && input != "3" && input != "4"))
+  if (input.empty())
     return -1;
-  return (int8_t)atoi(input.c_str());
+
+  int hdmiport = atoi(input.c_str());
+  return (hdmiport < 1 || hdmiport > 15) ? -1 : (int8_t)hdmiport;
 }
 
 cec_logical_address FindPhysicalAddressBaseDevice(void)
 {
-  PrintToStdOut("Press 1 of 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);
index b455ac88d9ab0558e81f77108e703be494331f4b..c7d586e79cce42ab39e061a69b419ebbf786d3f6 100644 (file)
@@ -57,10 +57,9 @@ CCECProcessor::CCECProcessor(CLibCEC *controller, libcec_configuration *configur
     m_iRetryLineTimeout(3),
     m_iLastTransmission(0)
 {
-  m_logicalAddresses.Clear();
   CreateBusDevices();
   m_configuration.Clear();
-  m_configuration.serverVersion = configuration->serverVersion;
+  m_configuration.serverVersion = CEC_SERVER_VERSION_1_6_0;
   SetConfiguration(configuration);
 
   if (m_configuration.tvVendor != CEC_VENDOR_UNKNOWN)
@@ -78,7 +77,7 @@ CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, con
     m_iLastTransmission(0)
 {
   m_configuration.Clear();
-  m_configuration.serverVersion    = CEC_SERVER_VERSION_1_5_2;
+  m_configuration.serverVersion    = CEC_SERVER_VERSION_1_6_0;
 
   // client version < 1.5.0
   m_configuration.clientVersion    = (uint32_t)CEC_CLIENT_VERSION_PRE_1_5;
@@ -90,7 +89,6 @@ CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, con
 
   if (m_configuration.deviceTypes.IsEmpty())
     m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
-  m_logicalAddresses.Clear();
   CreateBusDevices();
 }
 
@@ -134,7 +132,10 @@ CCECProcessor::~CCECProcessor(void)
   Close();
 
   for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
+  {
     delete m_busDevices[iPtr];
+    m_busDevices[iPtr] = NULL;
+  }
 }
 
 void CCECProcessor::Close(void)
@@ -152,13 +153,12 @@ void CCECProcessor::Close(void)
 
   if (bClose && m_communication)
   {
-    m_communication->Close();
     delete m_communication;
     m_communication = NULL;
   }
 }
 
-bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs)
+bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */)
 {
   bool bReturn(false);
   Close();
@@ -174,18 +174,11 @@ bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint
     m_bConnectionOpened = (m_communication != NULL);
   }
 
-  /* check for an already opened connection */
-  if (m_communication->IsOpen())
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "connection already opened");
-    return bReturn;
-  }
-
   CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
 
   /* open a new connection */
   unsigned iConnectTry(0);
-  while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open(this, (timeout.TimeLeft() / CEC_CONNECT_TRIES))) == false)
+  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_communication->Close();
@@ -193,7 +186,24 @@ bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint
   }
 
   if (bReturn)
-    CLibCEC::AddLog(CEC_LOG_NOTICE, "connected to the CEC adapter. firmware version = %d, client version = %s", m_communication->GetFirmwareVersion(), ToString((cec_client_version)m_configuration.clientVersion));
+  {
+    m_configuration.iFirmwareVersion = m_communication->GetFirmwareVersion();
+    CLibCEC::AddLog(CEC_LOG_NOTICE, "connected to the CEC adapter. firmware version = %d, client version = %s", m_configuration.iFirmwareVersion, ToString((cec_client_version)m_configuration.clientVersion));
+  }
+
+  if (m_configuration.bGetSettingsFromROM == 1)
+  {
+    libcec_configuration config;
+    config.Clear();
+    m_communication->GetConfiguration(&config);
+
+    CLockObject lock(m_mutex);
+    if (!config.deviceTypes.IsEmpty())
+      m_configuration.deviceTypes = config.deviceTypes;
+    if (config.iPhysicalAddress > 0)
+      m_configuration.iPhysicalAddress = config.iPhysicalAddress;
+    snprintf(m_configuration.strDeviceName, 13, "%s", config.strDeviceName);
+  }
 
   return bReturn;
 }
@@ -215,8 +225,8 @@ bool CCECProcessor::Initialise(void)
   bool bReturn(false);
   {
     CLockObject lock(m_mutex);
-    if (!m_logicalAddresses.IsEmpty())
-      m_logicalAddresses.Clear();
+    if (!m_configuration.logicalAddresses.IsEmpty())
+      m_configuration.logicalAddresses.Clear();
 
     if (!FindLogicalAddresses())
     {
@@ -225,11 +235,11 @@ bool CCECProcessor::Initialise(void)
     }
 
     /* only set our OSD name for the primary device */
-    m_busDevices[m_logicalAddresses.primary]->m_strDeviceName = m_configuration.strDeviceName;
+    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_logicalAddresses.primary]->m_bActiveSource = true;
+      m_busDevices[m_configuration.logicalAddresses.primary]->m_bActiveSource = true;
   }
 
   /* get the vendor id from the TV, so we are using the correct handler */
@@ -238,15 +248,15 @@ bool CCECProcessor::Initialise(void)
   if (m_configuration.iPhysicalAddress != 0)
   {
     CLibCEC::AddLog(CEC_LOG_NOTICE, "setting the physical address to %4x", m_configuration.iPhysicalAddress);
-    m_busDevices[m_logicalAddresses.primary]->m_iPhysicalAddress = m_configuration.iPhysicalAddress;
-    if ((bReturn = m_busDevices[m_logicalAddresses.primary]->TransmitPhysicalAddress()) == false)
+    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 %4x", m_configuration.iPhysicalAddress);
   }
   else if (m_configuration.iPhysicalAddress == 0 && (bReturn = SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort, true)) == false)
     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);
 
-  if (m_configuration.bActivateSource == 1)
-    m_busDevices[m_logicalAddresses.primary]->ActivateSource();
+  if (bReturn && m_configuration.bActivateSource == 1)
+    m_busDevices[m_configuration.logicalAddresses.primary]->ActivateSource();
 
   SetInitialised(bReturn);
   if (bReturn)
@@ -289,7 +299,7 @@ bool CCECProcessor::TryLogicalAddress(cec_logical_address address)
 {
   if (m_busDevices[address]->TryLogicalAddress())
   {
-    m_logicalAddresses.Set(address);
+    m_configuration.logicalAddresses.Set(address);
     return true;
   }
 
@@ -335,7 +345,7 @@ bool CCECProcessor::ChangeDeviceType(cec_device_type from, cec_device_type to)
 
   CLockObject lock(m_mutex);
   CCECBusDevice *previousDevice = GetDeviceByType(from);
-  m_logicalAddresses.primary = CECDEVICE_UNKNOWN;
+  m_configuration.logicalAddresses.primary = CECDEVICE_UNKNOWN;
 
   for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
   {
@@ -407,7 +417,7 @@ bool CCECProcessor::ChangeDeviceType(cec_device_type from, cec_device_type to)
 bool CCECProcessor::FindLogicalAddresses(void)
 {
   bool bReturn(true);
-  m_logicalAddresses.Clear();
+  m_configuration.logicalAddresses.Clear();
 
   if (m_configuration.deviceTypes.IsEmpty())
   {
@@ -433,7 +443,7 @@ bool CCECProcessor::FindLogicalAddresses(void)
   }
 
   if (bReturn)
-    SetAckMask(m_logicalAddresses.AckMask());
+    SetAckMask(m_configuration.logicalAddresses.AckMask());
 
   return bReturn;
 }
@@ -448,23 +458,27 @@ void CCECProcessor::ReplaceHandlers(void)
 
 bool CCECProcessor::OnCommandReceived(const cec_command &command)
 {
-  ParseCommand(command);
-  return true;
+  return m_inBuffer.Push(command);
 }
 
 void *CCECProcessor::Process(void)
 {
   CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started");
 
+  cec_command command;
+  command.Clear();
+
   while (!IsStopped() && m_communication->IsOpen())
   {
+    if (m_inBuffer.Pop(command, 500))
+      ParseCommand(command);
+
     if (IsInitialised())
     {
       ReplaceHandlers();
 
       m_controller->CheckKeypressTimeout();
     }
-    Sleep(5);
   }
 
   return NULL;
@@ -477,13 +491,13 @@ bool CCECProcessor::SetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RE
   if (!IsRunning())
     return bReturn;
 
-  cec_logical_address addr = m_logicalAddresses.primary;
+  cec_logical_address addr = m_configuration.logicalAddresses.primary;
 
   if (type != CEC_DEVICE_TYPE_RESERVED)
   {
     for (uint8_t iPtr = 0; iPtr <= 11; iPtr++)
     {
-      if (m_logicalAddresses[iPtr] && m_busDevices[iPtr]->m_type == type)
+      if (m_configuration.logicalAddresses[iPtr] && m_busDevices[iPtr]->m_type == type)
       {
         addr = (cec_logical_address) iPtr;
         break;
@@ -502,7 +516,8 @@ bool CCECProcessor::SetActiveSource(uint16_t iStreamPath)
 {
   bool bReturn(false);
 
-  CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath);
+  // suppress polls when searching for a device
+  CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath, false, true);
   if (device)
   {
     device->SetActiveSource();
@@ -566,6 +581,12 @@ bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort,
 {
   bool bReturn(false);
 
+  // limit the HDMI port range to 1-15
+  if (iPort < 1)
+      iPort = 1;
+  if (iPort > 15)
+      iPort = 15;
+
   {
     CLockObject lock(m_mutex);
     m_configuration.baseDevice = iBaseDevice;
@@ -620,8 +641,8 @@ bool CCECProcessor::TransmitInactiveSource(void)
   if (!IsRunning())
     return false;
 
-  if (!m_logicalAddresses.IsEmpty() && m_busDevices[m_logicalAddresses.primary])
-    return m_busDevices[m_logicalAddresses.primary]->TransmitInactiveSource();
+  if (!m_configuration.logicalAddresses.IsEmpty() && m_busDevices[m_configuration.logicalAddresses.primary])
+    return m_busDevices[m_configuration.logicalAddresses.primary]->TransmitInactiveSource();
   return false;
 }
 
@@ -640,12 +661,12 @@ void CCECProcessor::LogOutput(const cec_command &data)
 bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress)
 {
   CLockObject lock(m_mutex);
-  if (m_logicalAddresses.primary != iLogicalAddress)
+  if (m_configuration.logicalAddresses.primary != iLogicalAddress)
   {
     CLibCEC::AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress);
-    m_logicalAddresses.primary = iLogicalAddress;
-    m_logicalAddresses.Set(iLogicalAddress);
-    return SetAckMask(m_logicalAddresses.AckMask());
+    m_configuration.logicalAddresses.primary = iLogicalAddress;
+    m_configuration.logicalAddresses.Set(iLogicalAddress);
+    return SetAckMask(m_configuration.logicalAddresses.AckMask());
   }
 
   return true;
@@ -655,12 +676,12 @@ bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = tru
 {
   for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
   {
-    if (m_logicalAddresses[iPtr])
+    if (m_configuration.logicalAddresses[iPtr])
       m_busDevices[iPtr]->SetMenuState(state);
   }
 
   if (bSendUpdate)
-    m_busDevices[m_logicalAddresses.primary]->TransmitMenuState(CECDEVICE_TV);
+    m_busDevices[m_configuration.logicalAddresses.primary]->TransmitMenuState(CECDEVICE_TV);
 
   return true;
 }
@@ -677,11 +698,11 @@ bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpda
     m_configuration.iPhysicalAddress = iPhysicalAddress;
     CLibCEC::AddLog(CEC_LOG_DEBUG, "setting physical address to '%4x'", iPhysicalAddress);
 
-    if (!m_logicalAddresses.IsEmpty())
+    if (!m_configuration.logicalAddresses.IsEmpty())
     {
       bool bWasActiveSource(false);
       for (uint8_t iPtr = 0; iPtr < 15; iPtr++)
-        if (m_logicalAddresses[iPtr])
+        if (m_configuration.logicalAddresses[iPtr])
         {
           bWasActiveSource |= m_busDevices[iPtr]->IsActiveSource();
           m_busDevices[iPtr]->SetInactiveSource();
@@ -703,7 +724,16 @@ bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpda
     SetActiveView();
 
   if (bReturn)
-    CLibCEC::ConfigurationChanged(m_configuration);
+  {
+    libcec_configuration config;
+    {
+      CLockObject lock(m_mutex);
+      config = m_configuration;
+    }
+
+    PersistConfiguration(&config);
+    CLibCEC::ConfigurationChanged(config);
+  }
 
   return bReturn;
 }
@@ -720,16 +750,16 @@ bool CCECProcessor::SwitchMonitoring(bool bEnable)
   if (bEnable)
     return SetAckMask(0);
   else
-    return SetAckMask(m_logicalAddresses.AckMask());
+    return SetAckMask(m_configuration.logicalAddresses.AckMask());
 }
 
 bool CCECProcessor::PollDevice(cec_logical_address iAddress)
 {
   if (iAddress != CECDEVICE_UNKNOWN && m_busDevices[iAddress])
   {
-    return m_logicalAddresses.primary == CECDEVICE_UNKNOWN ?
+    return m_configuration.logicalAddresses.primary == CECDEVICE_UNKNOWN ?
         m_busDevices[iAddress]->TransmitPoll(iAddress) :
-        m_busDevices[m_logicalAddresses.primary]->TransmitPoll(iAddress);
+        m_busDevices[m_configuration.logicalAddresses.primary]->TransmitPoll(iAddress);
   }
   return false;
 }
@@ -761,15 +791,15 @@ uint8_t CCECProcessor::MuteAudio(bool bSendRelease /* = true */)
   return status;
 }
 
-CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bRefresh /* = false */) const
+CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bRefresh /* = false */, bool bSuppressPoll /* = false */) const
 {
-  if (m_busDevices[m_logicalAddresses.primary]->GetPhysicalAddress(false) == iPhysicalAddress)
-    return m_busDevices[m_logicalAddresses.primary];
+  if (m_busDevices[m_configuration.logicalAddresses.primary]->GetPhysicalAddress(false) == iPhysicalAddress)
+    return m_busDevices[m_configuration.logicalAddresses.primary];
 
   CCECBusDevice *device = NULL;
   for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
   {
-    if (m_busDevices[iPtr]->GetPhysicalAddress(bRefresh) == iPhysicalAddress)
+    if (m_busDevices[iPtr]->GetPhysicalAddress(bRefresh, bSuppressPoll) == iPhysicalAddress)
     {
       device = m_busDevices[iPtr];
       break;
@@ -785,7 +815,7 @@ CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const
 
   for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
   {
-    if (m_busDevices[iPtr]->m_type == type && m_logicalAddresses[iPtr])
+    if (m_busDevices[iPtr]->m_type == type && m_configuration.logicalAddresses[iPtr])
     {
       device = m_busDevices[iPtr];
       break;
@@ -798,7 +828,7 @@ CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const
 CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) const
 {
   CCECBusDevice *device(NULL);
-  cec_logical_address primary = m_logicalAddresses.primary;
+  cec_logical_address primary = m_configuration.logicalAddresses.primary;
   if (primary != CECDEVICE_UNKNOWN)
     device = m_busDevices[primary];
   return device;
@@ -864,12 +894,14 @@ cec_logical_address CCECProcessor::GetActiveSource(void)
 
 bool CCECProcessor::IsActiveSource(cec_logical_address iAddress)
 {
-  return m_busDevices[iAddress]->IsActiveSource();
+  return iAddress > CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST ?
+    m_busDevices[iAddress]->IsActiveSource() :
+    false;
 }
 
 bool CCECProcessor::Transmit(const cec_command &data)
 {
-  if (m_logicalAddresses[(uint8_t)data.destination])
+  if (m_configuration.logicalAddresses[(uint8_t)data.destination])
   {
     CLibCEC::AddLog(CEC_LOG_WARNING, "not sending data to myself!");
     return false;
@@ -890,7 +922,7 @@ bool CCECProcessor::Transmit(const cec_command &data)
     iMaxTries = m_busDevices[data.initiator]->GetHandler()->GetTransmitRetries() + 1;
   }
 
-  return m_communication->Write(data, iMaxTries, m_iLineTimeout, m_iRetryLineTimeout)
+  return m_communication->Write(data, iMaxTries, m_iStandardLineTimeout, m_iRetryLineTimeout)
       == ADAPTER_MESSAGE_STATE_SENT_ACKED;
 }
 
@@ -900,7 +932,7 @@ void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode
 
   cec_command command;
   // TODO
-  cec_command::Format(command, m_logicalAddresses.primary, address, CEC_OPCODE_FEATURE_ABORT);
+  cec_command::Format(command, m_configuration.logicalAddresses.primary, address, CEC_OPCODE_FEATURE_ABORT);
   command.parameters.PushBack((uint8_t)opcode);
   command.parameters.PushBack((uint8_t)reason);
 
@@ -910,7 +942,9 @@ void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode
 void CCECProcessor::ParseCommand(const cec_command &command)
 {
   CStdString dataStr;
-  dataStr.Format(">> %1x%1x:%02x", command.initiator, command.destination, command.opcode);
+  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());
@@ -949,8 +983,8 @@ bool CCECProcessor::IsPresentDeviceType(cec_device_type type)
 
 uint16_t CCECProcessor::GetPhysicalAddress(void) const
 {
-  if (!m_logicalAddresses.IsEmpty() && m_busDevices[m_logicalAddresses.primary])
-    return m_busDevices[m_logicalAddresses.primary]->GetPhysicalAddress(false);
+  if (!m_configuration.logicalAddresses.IsEmpty() && m_busDevices[m_configuration.logicalAddresses.primary])
+    return m_busDevices[m_configuration.logicalAddresses.primary]->GetPhysicalAddress(false);
   return false;
 }
 
@@ -1376,6 +1410,10 @@ const char *CCECProcessor::ToString(const cec_client_version version)
     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";
   default:
     return "Unknown";
   }
@@ -1393,6 +1431,10 @@ const char *CCECProcessor::ToString(const cec_server_version version)
     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";
   default:
     return "Unknown";
   }
@@ -1451,7 +1493,7 @@ bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */)
     IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort);
     CTimeout timeout(10000);
     int iConnectTry(0);
-    while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(NULL, (timeout.TimeLeft() / CEC_CONNECT_TRIES)), true) == false)
+    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();
@@ -1477,7 +1519,8 @@ bool CCECProcessor::PingAdapter(void)
 
 void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination)
 {
-  m_busDevices[initiator]->HandlePoll(destination);
+  if (destination < CECDEVICE_BROADCAST)
+    m_busDevices[destination]->HandlePollFrom(initiator);
 }
 
 bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator)
@@ -1497,12 +1540,15 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
   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
 
   // 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]));
 
   bool bPhysicalAddressChanged(false);
 
@@ -1515,6 +1561,8 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
     {
       if (IsRunning())
         CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%4x'", __FUNCTION__, iPhysicalAddress);
+      else
+        CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using physical address '%x'", __FUNCTION__, iPhysicalAddress);
       bPhysicalAddressChanged = (m_configuration.iPhysicalAddress != iPhysicalAddress);
       m_configuration.iPhysicalAddress = iPhysicalAddress;
       m_configuration.iHDMIPort = 0;
@@ -1528,9 +1576,11 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
   {
     if (configuration->iPhysicalAddress != 0)
       bPhysicalAddressChanged = IsRunning() && m_configuration.iPhysicalAddress != configuration->iPhysicalAddress;
-    if (IsRunning())
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using physical address '%4x'", __FUNCTION__, configuration->iPhysicalAddress);
-    m_configuration.iPhysicalAddress = configuration->iPhysicalAddress;
+    if (bPhysicalAddressChanged)
+    {
+      CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - physical address '%x'", __FUNCTION__, configuration->iPhysicalAddress);
+      m_configuration.iPhysicalAddress = configuration->iPhysicalAddress;
+    }
   }
 
   bool bHdmiPortChanged(false);
@@ -1538,20 +1588,17 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
   {
     // base device
     bHdmiPortChanged = IsRunning() && m_configuration.baseDevice != configuration->baseDevice;
-    if (IsRunning())
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using base device '%x'", __FUNCTION__, (int)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;
-    if (IsRunning())
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using HDMI port '%d'", __FUNCTION__, configuration->iHDMIPort);
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using HDMI port '%d'", __FUNCTION__, configuration->iHDMIPort);
     m_configuration.iHDMIPort = configuration->iHDMIPort;
   }
   else
   {
-    if (IsRunning())
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
     m_configuration.baseDevice = CECDEVICE_UNKNOWN;
     m_configuration.iHDMIPort  = 0;
   }
@@ -1559,6 +1606,7 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
   bReinit = bPhysicalAddressChanged || bHdmiPortChanged || bDeviceTypeChanged;
 
   // 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))
   {
@@ -1568,6 +1616,7 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
   }
 
   // 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)
   {
     m_configuration.tvVendor= configuration->tvVendor;
@@ -1583,7 +1632,7 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
   }
 
   // just copy these
-  m_configuration.clientVersion        = configuration->clientVersion;
+  m_configuration.bUseTVMenuLanguage   = configuration->bUseTVMenuLanguage;
   m_configuration.bActivateSource      = configuration->bActivateSource;
   m_configuration.bGetSettingsFromROM  = configuration->bGetSettingsFromROM;
   m_configuration.powerOffDevices      = configuration->powerOffDevices;
@@ -1594,11 +1643,22 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
   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;
+  }
+
   // ensure that there is at least 1 device type set
   if (m_configuration.deviceTypes.IsEmpty())
     m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
 
-  if (bReinit)
+  // persist the configuration
+  if (IsRunning())
+    m_communication->PersistConfiguration(&m_configuration);
+
+  if (bReinit || m_configuration.logicalAddresses.IsEmpty())
   {
     if (bDeviceTypeChanged)
       return ChangeDeviceType(oldPrimaryType, m_configuration.deviceTypes[0]);
@@ -1607,7 +1667,7 @@ bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
     else
       return SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort);
   }
-  else if (m_configuration.bActivateSource == 1 && IsRunning() && !IsActiveSource(m_logicalAddresses.primary))
+  else if (m_configuration.bActivateSource == 1 && IsRunning() && !IsActiveSource(m_configuration.logicalAddresses.primary))
   {
     // activate the source if we're not already the active source
     SetActiveSource(m_configuration.deviceTypes.types[0]);
@@ -1641,6 +1701,18 @@ bool CCECProcessor::GetCurrentConfiguration(libcec_configuration *configuration)
   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;
+  }
+
   return true;
 }
 
@@ -1659,3 +1731,16 @@ void CCECProcessor::RescanActiveDevices(void)
   for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
     m_busDevices[iPtr]->GetStatus(true);
 }
+
+bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = 10000 */)
+{
+  if (!OpenConnection(strPort, 38400, iTimeoutMs, false))
+    return false;
+
+  config->iFirmwareVersion = m_communication->GetFirmwareVersion();
+  config->iPhysicalAddress = m_communication->GetPhysicalAddress();
+
+  delete m_communication;
+  m_communication = NULL;
+  return true;
+}
index 08bf8d00bf441fbfada9b51191b3cbc939d241fe..25b0c2aa10f75d627191807155d87bac85fd4847 100644 (file)
@@ -43,6 +43,58 @@ namespace CEC
   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 = 10000)
+    {
+      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 CCECProcessor : public PLATFORM::CThread, public IAdapterCommunicationCallback
   {
     public:
@@ -57,7 +109,7 @@ namespace CEC
       virtual bool                  OnCommandReceived(const cec_command &command);
 
       virtual bool                  IsMonitoring(void) const { return m_bMonitor; }
-      virtual CCECBusDevice *       GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bRefresh = false) const;
+      virtual CCECBusDevice *       GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bRefresh = false, bool bSuppressPoll = false) const;
       virtual CCECBusDevice *       GetDeviceByType(cec_device_type type) const;
       virtual CCECBusDevice *       GetPrimaryDevice(void) const;
       virtual cec_version           GetDeviceCecVersion(cec_logical_address iAddress);
@@ -71,11 +123,11 @@ namespace CEC
       virtual cec_osd_name          GetDeviceOSDName(cec_logical_address iAddress);
       virtual uint64_t              GetDeviceVendorId(cec_logical_address iAddress);
       virtual cec_power_status      GetDevicePowerStatus(cec_logical_address iAddress);
-      virtual cec_logical_address   GetLogicalAddress(void) const { return m_logicalAddresses.primary; }
-      virtual cec_logical_addresses GetLogicalAddresses(void) const { return m_logicalAddresses; }
+      virtual cec_logical_address   GetLogicalAddress(void) const { return m_configuration.logicalAddresses.primary; }
+      virtual cec_logical_addresses GetLogicalAddresses(void) const { return m_configuration.logicalAddresses; }
       virtual cec_logical_addresses GetActiveDevices(void);
       virtual uint16_t              GetDevicePhysicalAddress(cec_logical_address iAddress);
-      virtual bool                  HasLogicalAddress(cec_logical_address address) const { return m_logicalAddresses.IsSet(address); }
+      virtual bool                  HasLogicalAddress(cec_logical_address address) const { return m_configuration.logicalAddresses.IsSet(address); }
       virtual bool                  IsPresentDevice(cec_logical_address address);
       virtual bool                  IsPresentDeviceType(cec_device_type type);
       virtual uint16_t              GetPhysicalAddress(void) const;
@@ -142,11 +194,12 @@ namespace CEC
       virtual void HandlePoll(cec_logical_address initiator, cec_logical_address destination);
       virtual bool HandleReceiveFailed(cec_logical_address initiator);
 
+      virtual bool GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs = 10000);
+
       CCECBusDevice *  m_busDevices[16];
-      PLATFORM::CMutex m_transmitMutex;
 
   private:
-      bool OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs);
+      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);
@@ -164,17 +217,16 @@ namespace CEC
 
       bool                                m_bConnectionOpened;
       bool                                m_bInitialised;
-      cec_logical_addresses               m_logicalAddresses;
       PLATFORM::CMutex                    m_mutex;
       IAdapterCommunication *             m_communication;
       CLibCEC*                            m_controller;
       bool                                m_bMonitor;
       cec_keypress                        m_previousKey;
       PLATFORM::CThread *                 m_busScan;
-      uint8_t                             m_iLineTimeout;
       uint8_t                             m_iStandardLineTimeout;
       uint8_t                             m_iRetryLineTimeout;
       uint64_t                            m_iLastTransmission;
+      CCECInputBuffer                     m_inBuffer;
       libcec_configuration                m_configuration;
   };
 
index ab05822a230a1c7784dfe924f1426279578fa93e..3230e684d9779aaa21e068932b9fb2be0ec0d274 100644 (file)
@@ -60,7 +60,7 @@ CLibCEC::CLibCEC(libcec_configuration *configuration) :
     m_callbacks(configuration->callbacks),
     m_cbParam(configuration->callbackParam)
 {
-  configuration->serverVersion = CEC_SERVER_VERSION_1_5_2;
+  configuration->serverVersion = CEC_SERVER_VERSION_1_6_0;
   m_cec = new CCECProcessor(this, configuration);
 }
 
@@ -510,7 +510,7 @@ bool CECStartBootloader(void)
   {
     CUSBCECAdapterCommunication comm(NULL, deviceList[0].comm);
     CTimeout timeout(10000);
-    while (timeout.TimeLeft() > 0 && (bReturn = comm.Open(NULL, (timeout.TimeLeft() / CEC_CONNECT_TRIES)), true) == false)
+    while (timeout.TimeLeft() > 0 && (bReturn = comm.Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
     {
       comm.Close();
       CEvent::Sleep(500);
@@ -587,6 +587,11 @@ const char *CLibCEC::ToString(const cec_server_version version)
   return m_cec->ToString(version);
 }
 
+const char *CLibCEC::ToString(const cec_device_type type)
+{
+  return m_cec->ToString(type);
+}
+
 bool CLibCEC::GetCurrentConfiguration(libcec_configuration *configuration)
 {
   return m_cec->IsInitialised() && m_cec->GetCurrentConfiguration(configuration);
@@ -614,7 +619,102 @@ void CLibCEC::RescanActiveDevices(void)
 
 bool CLibCEC::IsLibCECActiveSource(void)
 {
-  return m_cec ?
-      m_cec->m_busDevices[m_cec->GetActiveSource()]->GetStatus(false) == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC :
-      false;
+  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;
+}
+
+cec_device_type CLibCEC::GetType(cec_logical_address address)
+{
+  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;
+  }
+}
+
+uint16_t CLibCEC::GetMaskForType(cec_logical_address address)
+{
+  return GetMaskForType(GetType(address));
+}
+
+uint16_t CLibCEC::GetMaskForType(cec_device_type type)
+{
+  switch (type)
+  {
+    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:
+    {
+      cec_logical_addresses addr;
+      addr.Clear();
+      addr.Set(CECDEVICE_TV);
+      return addr.AckMask();
+    }
+    default:
+      return 0;
+  }
+}
+
+bool CLibCEC::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = 10000 */)
+{
+  if (m_cec->IsRunning())
+    return false;
+  
+  return m_cec->GetDeviceInformation(strPort, config, iTimeoutMs);
 }
index 9241a2c3f8336ce602ee353367f4ca7f52eeb4e7..46973711bed2269cadba69e9c678515cfe973ea3 100644 (file)
@@ -121,6 +121,13 @@ namespace CEC
       const char *ToString(const cec_vendor_id vendor);
       const char *ToString(const cec_client_version version);
       const char *ToString(const cec_server_version version);
+      const char *ToString(const cec_device_type type);
+
+      static cec_device_type GetType(cec_logical_address address);
+      static uint16_t GetMaskForType(cec_logical_address address);
+      static uint16_t GetMaskForType(cec_device_type type);
+
+      virtual bool GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs = 10000);
     //@}
 
       static void AddLog(const cec_log_level level, const char *strFormat, ...);
index 29b08fd98ce429570f57c3e75b69ca69c3c36763..4a7e0be9b8260f8d0a491929be7a9db930f18922 100644 (file)
@@ -421,4 +421,9 @@ int cec_is_libcec_active_source(void)
   return cec_parser ? (cec_parser->IsLibCECActiveSource() ? 1 : 0) : -1;
 }
 
+int cec_get_device_information(const char *strPort, CEC::libcec_configuration *config, uint32_t iTimeoutMs)
+{
+  return cec_parser ? (cec_parser->GetDeviceInformation(strPort, config, iTimeoutMs) ? 1 : 0) : -1;
+}
+
 //@}
index 083775f6fbc63578cf9c07d3a140834e332c51f3..60d6d2d43069a24c9a6579c1cd0f4d5bf4ac7566 100644 (file)
@@ -11,8 +11,11 @@ pkgconfig_DATA = libcec.pc
 libcec_la_SOURCES = CECProcessor.cpp \
                     LibCEC.cpp \
                     LibCECC.cpp \
+                    adapter/USBCECAdapterCommands.cpp \
                     adapter/USBCECAdapterCommunication.cpp \
                     adapter/USBCECAdapterDetection.cpp \
+                    adapter/USBCECAdapterMessage.cpp \
+                    adapter/USBCECAdapterMessageQueue.cpp \
                     devices/CECAudioSystem.cpp \
                     devices/CECBusDevice.cpp \
                     devices/CECPlaybackDevice.cpp \
@@ -23,7 +26,7 @@ libcec_la_SOURCES = CECProcessor.cpp \
                     implementations/CECCommandHandler.cpp \
                     implementations/SLCommandHandler.cpp \
                     implementations/VLCommandHandler.cpp \
-                                       implementations/RLCommandHandler.cpp \
+                    implementations/RLCommandHandler.cpp \
                     platform/posix/serialport.cpp
 
 libcec_la_LDFLAGS = @LIBS@ -version-info @VERSION@
index d78b11d0164d37e4cf66f1c13b353aa2bc62717b..160a0fd3c30c320f9d262e6b2725c48b61fc0673 100644 (file)
  */
 
 #include "../platform/util/StdString.h"
+#include "USBCECAdapterMessage.h"
 
 namespace CEC
 {
-  typedef enum cec_adapter_message_state
-  {
-    ADAPTER_MESSAGE_STATE_UNKNOWN = 0,          /**< the initial state */
-    ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT,   /**< waiting in the send queue of the adapter, or timed out */
-    ADAPTER_MESSAGE_STATE_SENT,                 /**< sent and waiting on an ACK */
-    ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED,       /**< sent, but failed to ACK */
-    ADAPTER_MESSAGE_STATE_SENT_ACKED,           /**< sent, and ACK received */
-    ADAPTER_MESSAGE_STATE_INCOMING,             /**< received from another device */
-    ADAPTER_MESSAGE_STATE_ERROR                 /**< an error occured */
-  } cec_adapter_message_state;
-
   class IAdapterCommunicationCallback
   {
   public:
@@ -58,22 +48,40 @@ namespace CEC
      * @return True when it was handled by this listener, false otherwise.
      */
     virtual bool OnCommandReceived(const cec_command &command) = 0;
+
+    /*!
+     * @brief Callback method for IAdapterCommunication, called when a poll was received.
+     * @param initiator The initiator that sent the poll.
+     * @param destination The destination of the poll message.
+     */
+    virtual void HandlePoll(cec_logical_address initiator, cec_logical_address destination) = 0;
+
+    /*!
+     * @brief Callback method for IAdapterCommunication, called when a receive failed message was received.
+     * @param initiator The initiator that sent the receive failed message.
+     * @return True when this is an error, false otherwise.
+     */
+    virtual bool HandleReceiveFailed(cec_logical_address initiator) = 0;
   };
 
   class IAdapterCommunication
   {
   public:
-    IAdapterCommunication(void) {}
+    /*!
+     * @param callback The callback struct. if set to NULL, the Read() method has to be used to read commands. if set, OnCommandReceived() will be called for each command that was received
+     */
+    IAdapterCommunication(IAdapterCommunicationCallback *callback) :
+      m_callback(callback) {}
     virtual ~IAdapterCommunication(void) {}
 
     /*!
      * @brief Open a connection to the CEC adapter
-     * @param cb The callback struct. if set to NULL, the Read() method has to be used to read commands. if set, OnCommandReceived() will be called for each command that was received
      * @param iTimeoutMs Connection timeout in ms
      * @param bSkipChecks Skips all initial checks of the adapter, and starts the reader/writer threads directly after connecting.
+     * @param bStartListening Start a listener thread when true. False to just open a connection, read the device info, and close the connection.
      * @return True when connected, false otherwise
      */
-    virtual bool Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs = 10000, bool bSkipChecks = false) = 0;
+    virtual bool Open(uint32_t iTimeoutMs = 10000, bool bSkipChecks = false, bool bStartListening = true) = 0;
 
     /*!
      * @brief Close an open connection
@@ -90,14 +98,6 @@ namespace CEC
      */
     virtual CStdString GetError(void) const = 0;
 
-    /*!
-     * @brief Reads one cec_command from the adapter
-     * @param command The command that will be read (output)
-     * @param iTimeout The read timeout
-     * @return True when a command has been read, false otherwise.
-     */
-    virtual bool Read(cec_command &command, uint32_t iTimeout) = 0;
-
     /*!
      * @brief Write a cec_command to the adapter
      * @param data The command to write
@@ -135,7 +135,7 @@ namespace CEC
     virtual bool PingAdapter(void) = 0;
 
     /*!
-     * @return The firmware version of this CEC adapter.
+     * @return The firmware version of this CEC adapter, or 0 if it's unknown.
      */
     virtual uint16_t GetFirmwareVersion(void) = 0;
 
@@ -151,6 +151,13 @@ namespace CEC
      */
     virtual bool PersistConfiguration(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;
+
     /*!
      * @return The name of the port
      */
@@ -160,5 +167,8 @@ namespace CEC
      * @return The physical address, if the adapter supports this. 0 otherwise.
      */
     virtual uint16_t GetPhysicalAddress(void) = 0;
+
+  protected:
+    IAdapterCommunicationCallback *m_callback;
   };
 };
diff --git a/src/lib/adapter/USBCECAdapterCommands.cpp b/src/lib/adapter/USBCECAdapterCommands.cpp
new file mode 100644 (file)
index 0000000..69a2632
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * 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 "USBCECAdapterCommands.h"
+#include "../LibCEC.h"
+#include "../CECProcessor.h"
+
+using namespace CEC;
+using namespace PLATFORM;
+
+CUSBCECAdapterCommands::CUSBCECAdapterCommands(CUSBCECAdapterCommunication *comm) :
+    m_comm(comm),
+    m_bSettingsRetrieved(false),
+    m_bSettingAutoEnabled(false),
+    m_settingCecVersion(CEC_VERSION_UNKNOWN),
+    m_iSettingLAMask(0),
+    m_bNeedsWrite(false)
+{
+  m_persistedConfiguration.Clear();
+}
+
+cec_datapacket CUSBCECAdapterCommands::RequestSetting(cec_adapter_messagecode msgCode)
+{
+  cec_datapacket retVal;
+  retVal.Clear();
+
+  CCECAdapterMessage params;
+  CCECAdapterMessage *message = m_comm->SendCommand(msgCode, params);
+  if (message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED)
+  {
+    retVal = message->response;
+    retVal.Shift(2); // shift out start and msgcode
+    retVal.size -= 1; // remove end
+  }
+  delete message;
+  return retVal;
+}
+
+uint16_t CUSBCECAdapterCommands::RequestFirmwareVersion(void)
+{
+  m_persistedConfiguration.iFirmwareVersion = CEC_FW_VERSION_UNKNOWN;
+  unsigned int iFwVersionTry(0);
+
+  while (m_persistedConfiguration.iFirmwareVersion == CEC_FW_VERSION_UNKNOWN && iFwVersionTry++ < 3)
+  {
+    CLibCEC::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);
+      CEvent::Sleep(500);
+    }
+  }
+
+  if (m_persistedConfiguration.iFirmwareVersion == CEC_FW_VERSION_UNKNOWN)
+  {
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "defaulting to firmware version 1");
+    m_persistedConfiguration.iFirmwareVersion = 1;
+  }
+
+  return m_persistedConfiguration.iFirmwareVersion;
+}
+
+bool CUSBCECAdapterCommands::RequestSettingAutoEnabled(void)
+{
+  CLibCEC::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;
+    return true;
+  }
+  return false;
+}
+
+bool CUSBCECAdapterCommands::RequestSettingCECVersion(void)
+{
+  CLibCEC::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];
+    return true;
+  }
+  return false;
+}
+
+bool CUSBCECAdapterCommands::RequestSettingDefaultLogicalAddress(void)
+{
+  CLibCEC::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];
+    return true;
+  }
+  return false;
+}
+
+bool CUSBCECAdapterCommands::RequestSettingDeviceType(void)
+{
+  CLibCEC::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]));
+    return true;
+  }
+  CLibCEC::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");
+
+  cec_datapacket response = RequestSetting(MSGCODE_GET_LOGICAL_ADDRESS_MASK);
+  if (response.size == 2)
+  {
+    m_iSettingLAMask = ((uint16_t)response[0] << 8) | ((uint16_t)response[1]);
+    return true;
+  }
+  return false;
+}
+
+bool CUSBCECAdapterCommands::RequestSettingOSDName(void)
+{
+  CLibCEC::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");
+    return false;
+  }
+
+  char buf[14];
+  for (uint8_t iPtr = 0; iPtr < response.size && iPtr < 13; iPtr++)
+    buf[iPtr] = (char)response[iPtr];
+  buf[response.size] = 0;
+
+  snprintf(m_persistedConfiguration.strDeviceName, 13, "%s", buf);
+  CLibCEC::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");
+
+  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);
+    return true;
+  }
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "no persisted physical address setting");
+  return false;
+}
+
+bool CUSBCECAdapterCommands::SetSettingAutoEnabled(bool enabled)
+{
+  bool bReturn(true);
+
+  /* check whether this value was changed */
+  if (m_bSettingAutoEnabled == enabled)
+  {
+    CLibCEC::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");
+
+  CCECAdapterMessage params;
+  params.PushEscaped(enabled ? 1 : 0);
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_AUTO_ENABLED, params);
+  bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+
+  if (bReturn)
+    m_bSettingAutoEnabled = enabled;
+
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetSettingDeviceType(cec_device_type type)
+{
+  bool bReturn(true);
+
+  /* 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);
+    return bReturn;
+  }
+
+  m_bNeedsWrite = true;
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the device type to %X", (uint8_t)type);
+
+  CCECAdapterMessage params;
+  params.PushEscaped((uint8_t)type);
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_DEVICE_TYPE, params);
+  bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetSettingDefaultLogicalAddress(cec_logical_address address)
+{
+  bool bReturn(true);
+
+  /* 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);
+    return bReturn;
+  }
+
+  m_bNeedsWrite = true;
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the default logical address to %X", (uint8_t)address);
+
+  CCECAdapterMessage params;
+  params.PushEscaped((uint8_t)address);
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS, params);
+  bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+
+  if (bReturn)
+    m_persistedConfiguration.logicalAddresses.primary = address;
+
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetSettingLogicalAddressMask(uint16_t iMask)
+{
+  bool bReturn(true);
+
+  /* check whether this value was changed */
+  if (m_iSettingLAMask == iMask)
+  {
+    CLibCEC::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", iMask);
+
+  CCECAdapterMessage params;
+  params.PushEscaped(iMask >> 8);
+  params.PushEscaped((uint8_t)iMask);
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_LOGICAL_ADDRESS_MASK, params);
+  bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+
+  if (bReturn)
+    m_iSettingLAMask = iMask;
+
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetSettingPhysicalAddress(uint16_t iPhysicalAddress)
+{
+  bool bReturn(true);
+
+  /* check whether this value was changed */
+  if (m_persistedConfiguration.iPhysicalAddress == iPhysicalAddress)
+  {
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "logical address mask setting unchanged (%04X)", iPhysicalAddress);
+    return bReturn;
+  }
+
+  m_bNeedsWrite = true;
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the physical address to %04X", iPhysicalAddress);
+
+  CCECAdapterMessage params;
+  params.PushEscaped(iPhysicalAddress >> 8);
+  params.PushEscaped((uint8_t)iPhysicalAddress);
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_PHYSICAL_ADDRESS, params);
+  bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+
+  if (bReturn)
+    m_persistedConfiguration.iPhysicalAddress = iPhysicalAddress;
+
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetSettingCECVersion(cec_version version)
+{
+  bool bReturn(true);
+
+  /* check whether this value was changed */
+  if (m_settingCecVersion == version)
+  {
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "CEC version setting unchanged (%s)", CLibCEC::GetInstance()->ToString(version));
+    return bReturn;
+  }
+
+  m_bNeedsWrite = true;
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the CEC version to %s", CLibCEC::GetInstance()->ToString(version));
+
+  CCECAdapterMessage params;
+  params.PushEscaped((uint8_t)version);
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_HDMI_VERSION, params);
+  bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+
+  if (bReturn)
+    m_settingCecVersion = version;
+
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetSettingOSDName(const char *strOSDName)
+{
+  bool bReturn(true);
+
+  /* check whether this value was changed */
+  if (!strcmp(m_persistedConfiguration.strDeviceName, strOSDName))
+  {
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "OSD name setting unchanged (%s)", strOSDName);
+    return bReturn;
+  }
+
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the OSD name to %s", strOSDName);
+
+  CCECAdapterMessage params;
+  for (size_t iPtr = 0; iPtr < strlen(strOSDName); iPtr++)
+    params.PushEscaped(strOSDName[iPtr]);
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_OSD_NAME, params);
+  bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+
+  if (bReturn)
+    snprintf(m_persistedConfiguration.strDeviceName, 13, "%s", strOSDName);
+
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::WriteEEPROM(void)
+{
+  if (!m_bNeedsWrite)
+    return true;
+
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "writing settings in the EEPROM");
+
+  CCECAdapterMessage params;
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_WRITE_EEPROM, params);
+  m_bNeedsWrite = !(message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED);
+  delete message;
+  return m_bNeedsWrite;
+}
+
+bool CUSBCECAdapterCommands::PersistConfiguration(libcec_configuration *configuration)
+{
+  if (m_persistedConfiguration.iFirmwareVersion < 2)
+    return false;
+
+  if (!RequestSettings())
+    return false;
+
+  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 &= SetSettingCECVersion(CEC_VERSION_1_3A);
+  bReturn &= SetSettingOSDName(configuration->strDeviceName);
+  bReturn &= WriteEEPROM();
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::RequestSettings(void)
+{
+  if (m_persistedConfiguration.iFirmwareVersion < 2)
+  {
+    // settings can only be persisted with firmware v2+
+    return false;
+  }
+
+  if (!m_bSettingsRetrieved)
+    return true;
+
+  bool bReturn(true);
+  bReturn &= RequestSettingAutoEnabled();
+  bReturn &= RequestSettingCECVersion();
+  bReturn &= RequestSettingDefaultLogicalAddress();
+  bReturn &= RequestSettingDeviceType();
+  bReturn &= RequestSettingLogicalAddressMask();
+  bReturn &= RequestSettingOSDName();
+  bReturn &= RequestSettingPhysicalAddress();
+
+  // don't read the following settings:
+  // - auto enabled (always enabled)
+  // - default logical address (autodetected)
+  // - logical address mask (autodetected)
+  // - CEC version (1.3a)
+
+  // TODO to be added to the firmware:
+  // - base device (4 bits)
+  // - HDMI port number (4 bits)
+  // - TV vendor id (12 bits)
+  // - wake devices (8 bits)
+  // - standby devices (8 bits)
+  // - use TV menu language (1 bit)
+  // - activate source (1 bit)
+  // - power off screensaver (1 bit)
+  // - power off on standby (1 bit)
+  // - send inactive source (1 bit)
+
+  m_bSettingsRetrieved = true;
+
+  return bReturn;
+}
+
+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);
+
+  return true;
+}
+
+bool CUSBCECAdapterCommands::PingAdapter(void)
+{
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping");
+
+  CCECAdapterMessage params;
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_PING, params);
+  bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetAckMask(uint16_t iMask)
+{
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask);
+
+  CCECAdapterMessage params;
+  params.PushEscaped(iMask >> 8);
+  params.PushEscaped((uint8_t)iMask);
+  CCECAdapterMessage *message  = m_comm->SendCommand(MSGCODE_SET_ACK_MASK, params);
+  bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::StartBootloader(void)
+{
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "starting the bootloader");
+
+  CCECAdapterMessage params;
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_START_BOOTLOADER, params);
+  bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetLineTimeout(uint8_t iTimeout)
+{
+  CLibCEC::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);
+  bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+  return bReturn;
+}
+
+bool CUSBCECAdapterCommands::SetControlledMode(bool controlled)
+{
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off");
+
+  CCECAdapterMessage params;
+  params.PushEscaped(controlled ? 1 : 0);
+  CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_CONTROLLED, params);
+  bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  delete message;
+  return bReturn;
+}
diff --git a/src/lib/adapter/USBCECAdapterCommands.h b/src/lib/adapter/USBCECAdapterCommands.h
new file mode 100644 (file)
index 0000000..b88651e
--- /dev/null
@@ -0,0 +1,222 @@
+#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 "USBCECAdapterCommunication.h"
+
+namespace CEC
+{
+  class CUSBCECAdapterCommands
+  {
+  public:
+    CUSBCECAdapterCommands(CUSBCECAdapterCommunication *comm);
+
+    /*!
+     * @brief Request the firmware version from the adapter.
+     * @return The firmware version, or 1 (default) if it couldn't be retrieved.
+     */
+    uint16_t RequestFirmwareVersion(void);
+
+    /*!
+     * @return The firmware version of the adapter, retrieved when the connection is opened.
+     */
+    uint16_t GetFirmwareVersion(void) const { return m_persistedConfiguration.iFirmwareVersion; };
+
+    /*!
+     * @brief Persist the current configuration in the EEPROM.
+     * @attention Not all settings are persisted at this time.
+     * @param configuration The configuration to persist.
+     * @return True when persisted, false otherwise.
+     */
+    bool PersistConfiguration(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);
+
+    /*!
+     * @brief Send a ping command to the adapter.
+     * @return True when acked by the adapter, false otherwise.
+     */
+    bool PingAdapter(void);
+
+    /*!
+     * @brief Change the ackmask of the adapter.
+     * @param iMask The new mask.
+     * @return True when the change was acked by the adapter, false otherwise.
+     */
+    bool SetAckMask(uint16_t iMask);
+
+    /*!
+     * @brief Put the adapter in bootloader mode.
+     * @attention The connection needs to be closed after this call, since the adapter will no longer be available.
+     * @return True when the command was sent, false otherwise.
+     */
+    bool StartBootloader(void);
+
+    /*!
+     * @brief Change the current CEC line timeout.
+     * @param iTimeout The new timeout.
+     * @return True when the change was acked by the adapter, false otherwise.
+     */
+    bool SetLineTimeout(uint8_t iTimeout);
+
+    /*!
+     * @brief Put the adapter in controlled or autonomous mode.
+     * @param controlled True to switch to controlled mode, false to switch to auto mode.
+     * @return True when acked by the controller, false otherwise.
+     */
+    bool SetControlledMode(bool controlled);
+
+  private:
+    /*!
+     * @brief Reads all settings from the eeprom.
+     * @return True when read, false otherwise.
+     */
+    bool RequestSettings(void);
+
+    /*!
+     * @brief Request a setting value from the adapter.
+     * @param msgCode The setting to retrieve.
+     * @return The response from the adapter.
+     */
+    cec_datapacket RequestSetting(cec_adapter_messagecode msgCode);
+
+    /*!
+     * @brief Change the value of the "auto enabled" setting.
+     * @param enabled The new value.
+     * @return True when set, false otherwise.
+     */
+    bool SetSettingAutoEnabled(bool enabled);
+
+    /*!
+     * @brief Request the value of the "auto enabled" setting from the adapter.
+     * @return True when retrieved, false otherwise.
+     */
+    bool RequestSettingAutoEnabled(void);
+
+    /*!
+     * @brief Change the value of the "device type" setting, used when the device is in autonomous mode.
+     * @param type The new value.
+     * @return True when set, false otherwise.
+     */
+    bool SetSettingDeviceType(cec_device_type type);
+
+    /*!
+     * @brief Request the value of the "device type" setting from the adapter.
+     * @return True when retrieved, false otherwise.
+     */
+    bool RequestSettingDeviceType(void);
+
+    /*!
+     * @brief Change the value of the "default logical address" setting, used when the device is in autonomous mode.
+     * @param address The new value.
+     * @return True when set, false otherwise.
+     */
+    bool SetSettingDefaultLogicalAddress(cec_logical_address address);
+
+    /*!
+     * @brief Request the value of the "default logical address" setting from the adapter.
+     * @return True when retrieved, false otherwise.
+     */
+    bool RequestSettingDefaultLogicalAddress(void);
+
+    /*!
+     * @brief Change the value of the "logical address mask" setting, used when the device is in autonomous mode.
+     * @param iMask The new value.
+     * @return True when set, false otherwise.
+     */
+    bool SetSettingLogicalAddressMask(uint16_t iMask);
+
+    /*!
+     * @brief Request the value of the "logical address mask" setting from the adapter.
+     * @return True when retrieved, false otherwise.
+     */
+    bool RequestSettingLogicalAddressMask(void);
+
+    /*!
+     * @brief Change the value of the "physical address" setting, used when the device is in autonomous mode.
+     * @param iPhysicalAddress The new value.
+     * @return True when set, false otherwise.
+     */
+    bool SetSettingPhysicalAddress(uint16_t iPhysicalAddress);
+
+    /*!
+     * @brief Request the value of the "physical address" setting from the adapter.
+     * @return True when retrieved, false otherwise.
+     */
+    bool RequestSettingPhysicalAddress(void);
+
+    /*!
+     * @brief Change the value of the "CEC version" setting, used when the device is in autonomous mode.
+     * @param version The new value.
+     * @return True when set, false otherwise.
+     */
+    bool SetSettingCECVersion(cec_version version);
+
+    /*!
+     * @brief Request the value of the "CEC version" setting from the adapter.
+     * @return True when retrieved, false otherwise.
+     */
+    bool RequestSettingCECVersion(void);
+
+    /*!
+     * @brief Change the value of the "OSD name" setting, used when the device is in autonomous mode.
+     * @param strOSDName The new value.
+     * @return True when set, false otherwise.
+     */
+    bool SetSettingOSDName(const char *strOSDName);
+
+    /*!
+     * @brief Request the value of the "OSD name" setting from the adapter.
+     * @return True when retrieved, false otherwise.
+     */
+    bool RequestSettingOSDName(void);
+
+    /*!
+     * @brief Persist the current settings in the EEPROM
+     * @return True when persisted, false otherwise.
+     */
+    bool WriteEEPROM(void);
+
+    CUSBCECAdapterCommunication *m_comm;                   /**< the communication handler */
+    bool                         m_bSettingsRetrieved;     /**< true when the settings were read from the eeprom, false otherwise */
+    bool                         m_bSettingAutoEnabled;    /**< the value of the auto-enabled setting */
+    cec_version                  m_settingCecVersion;      /**< the value of the cec version setting */
+    uint16_t                     m_iSettingLAMask;         /**< the value of the LA mask setting */
+    bool                         m_bNeedsWrite;            /**< true when we sent changed settings to the adapter that have not been persisted */
+    libcec_configuration         m_persistedConfiguration; /**< the configuration that is persisted in the eeprom */
+  };
+}
index e51a861f28579ebe6d5685c883032626e05add52..ef80ed049731abe60fe886f67dcd91018a40ac89 100644 (file)
@@ -31,6 +31,8 @@
  */
 
 #include "USBCECAdapterCommunication.h"
+#include "USBCECAdapterCommands.h"
+#include "USBCECAdapterMessageQueue.h"
 #include "../platform/sockets/serialport.h"
 #include "../platform/util/timeutils.h"
 #include "../LibCEC.h"
@@ -40,225 +42,136 @@ using namespace std;
 using namespace CEC;
 using namespace PLATFORM;
 
-void *CUSBCECAdapterProcessor::Process(void)
-{
-  cec_command command;
-  while (!IsStopped())
-  {
-    if (m_inBuffer.Pop(command))
-      m_callback->OnCommandReceived(command);
-    Sleep(5);
-  }
-
-  return NULL;
-}
-
-void CUSBCECAdapterProcessor::AddCommand(cec_command command)
-{
-  m_inBuffer.Push(command);
-}
+#define CEC_ADAPTER_PING_TIMEOUT 15000
 
-CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
+CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
+    IAdapterCommunication(callback),
     m_port(NULL),
-    m_processor(processor),
-    m_bHasData(false),
     m_iLineTimeout(0),
-    m_iFirmwareVersion(CEC_FW_VERSION_UNKNOWN),
-    m_lastInitiator(CECDEVICE_UNKNOWN),
-    m_bNextIsEscaped(false),
-    m_bGotStart(false),
-    m_messageProcessor(NULL),
-    m_bInitialised(false)
+    m_lastPollDestination(CECDEVICE_UNKNOWN),
+    m_bInitialised(false),
+    m_pingThread(NULL),
+    m_commands(NULL),
+    m_adapterMessageQueue(NULL)
 {
-  m_port = new PLATFORM::CSerialPort(strPort, iBaudRate);
+  for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
+    m_bWaitingForAck[iPtr] = false;
+  m_port = new CSerialPort(strPort, iBaudRate);
 }
 
 CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
 {
   Close();
+  delete m_commands;
+  delete m_adapterMessageQueue;
+  delete m_port;
 }
 
-bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */)
+bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
 {
-  bool bReturn(false);
-  uint64_t iNow = GetTimeMs();
-  uint64_t iTarget = iTimeoutMs > 0 ? iNow + iTimeoutMs : iNow + CEC_DEFAULT_TRANSMIT_WAIT;
-
-  /* try to ping the adapter */
-  bool bPinged(false);
-  unsigned iPingTry(0);
-  while (iNow < iTarget && (bPinged = PingAdapter()) == false)
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
-    CEvent::Sleep(500);
-    iNow = GetTimeMs();
-  }
-
-  /* try to read the firmware version */
-  m_iFirmwareVersion = CEC_FW_VERSION_UNKNOWN;
-  unsigned iFwVersionTry(0);
-  while (bPinged && iNow < iTarget && (m_iFirmwareVersion = GetFirmwareVersion()) == CEC_FW_VERSION_UNKNOWN && iFwVersionTry < 3)
-  {
-    CLibCEC::AddLog(CEC_LOG_WARNING, "the adapter did not respond with a correct firmware version (try %d)", ++iFwVersionTry);
-    CEvent::Sleep(500);
-    iNow = GetTimeMs();
-  }
-
-  if (m_iFirmwareVersion == CEC_FW_VERSION_UNKNOWN)
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "defaulting to firmware version 1");
-    m_iFirmwareVersion = 1;
-  }
-
-  if (m_iFirmwareVersion >= 2)
-  {
-    /* try to set controlled mode */
-    unsigned iControlledTry(0);
-    bool bControlled(false);
-    while (iNow < iTarget && (bControlled = SetControlledMode(true)) == false)
-    {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
-      CEvent::Sleep(500);
-      iNow = GetTimeMs();
-    }
-    bReturn = bControlled;
-  }
-  else
-    bReturn = true;
-
-  {
-    CLockObject lock(m_mutex);
-    m_bInitialised = bReturn;
-  }
-
-  return bReturn;
-}
-
-bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */)
-{
-  uint64_t iNow = GetTimeMs();
-  uint64_t iTimeout = iNow + iTimeoutMs;
-
+  bool bConnectionOpened(false);
   {
     CLockObject lock(m_mutex);
 
+    /* we need the port settings here */
     if (!m_port)
     {
       CLibCEC::AddLog(CEC_LOG_ERROR, "port is NULL");
-      return false;
+      return bConnectionOpened;
     }
 
+    /* return true when the port is already open */
     if (IsOpen())
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "port is already open");
+      CLibCEC::AddLog(CEC_LOG_WARNING, "port is already open");
       return true;
     }
 
-    m_callback = cb;
+    /* adapter commands */
+    if (!m_commands)
+      m_commands = new CUSBCECAdapterCommands(this);
+
+    if (!m_adapterMessageQueue)
+      m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
+
+    /* try to open the connection */
     CStdString strError;
-    bool bConnected(false);
-    while (!bConnected && iNow < iTimeout)
+    CTimeout timeout(iTimeoutMs);
+    while (!bConnectionOpened && timeout.TimeLeft() > 0)
     {
-      if ((bConnected = m_port->Open(iTimeout)) == false)
+      if ((bConnectionOpened = m_port->Open(timeout.TimeLeft())) == false)
       {
         strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str());
         Sleep(250);
-        iNow = GetTimeMs();
       }
+      /* and retry every 250ms until the timeout passed */
     }
 
-    if (!bConnected)
+    /* return false when we couldn't connect */
+    if (!bConnectionOpened)
     {
       CLibCEC::AddLog(CEC_LOG_ERROR, strError);
       return false;
     }
 
     CLibCEC::AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
-
-    if (!bSkipChecks)
-    {
-      //clear any input bytes
-      uint8_t buff[1024];
-      while (m_port->Read(buff, 1024, 100) > 0)
-      {
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it");
-        Sleep(250);
-      }
-    }
+    ClearInputBytes();
   }
 
-  if (!bSkipChecks && !CheckAdapter())
+  if (!CreateThread())
+  {
+    bConnectionOpened = false;
+    CLibCEC::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");
-    return false;
   }
-  else
+  else if (bStartListening)
   {
-    if (CreateThread())
+    /* start a ping thread, that will ping the adapter every 15 seconds
+       if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */
+    m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT);
+    if (m_pingThread->CreateThread())
     {
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
-      return true;
+      bConnectionOpened = true;
     }
     else
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
+      bConnectionOpened = false;
+      CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a ping thread");
     }
   }
 
-  return false;
-}
+  if (!bConnectionOpened || !bStartListening)
+    StopThread(0);
 
-void CUSBCECAdapterCommunication::Close(void)
-{
-  StopThread();
+  return bConnectionOpened;
 }
 
-void *CUSBCECAdapterCommunication::Process(void)
+void CUSBCECAdapterCommunication::Close(void)
 {
-  m_messageProcessor = new CUSBCECAdapterProcessor(m_callback);
-  m_messageProcessor->CreateThread();
-
-  cec_command command;
-  command.Clear();
-  bool bCommandReceived(false);
-  while (!IsStopped())
+  /* set the ackmask to 0 before closing the connection */
+  if (IsRunning())
   {
-    {
-      CLockObject lock(m_mutex);
-      ReadFromDevice(50);
-      bCommandReceived = m_callback && Read(command, 0) && m_bInitialised;
-    }
-
-    /* push the next command to the callback method if there is one */
-    if (!IsStopped() && bCommandReceived)
-      m_messageProcessor->AddCommand(command);
-
-    if (!IsStopped())
-    {
-      Sleep(5);
-      WriteNextCommand();
-    }
+    SetAckMask(0);
+    if (m_commands->GetFirmwareVersion() >= 2)
+      SetControlledMode(false);
   }
 
-  /* stop the message processor */
-  m_messageProcessor->StopThread();
-  delete m_messageProcessor;
-
-  /* notify all threads that are waiting on messages to be sent */
-  CCECAdapterMessage *msg(NULL);
-  while (m_outBuffer.Pop(msg))
-    msg->event.Broadcast();
+  /* stop and delete the ping thread */
+  if (m_pingThread)
+    m_pingThread->StopThread(0);
+  delete m_pingThread;
+  m_pingThread = NULL;
 
-  /* set the ackmask to 0 before closing the connection */
-  SetAckMaskInternal(0, true);
+  /* stop the reader thread */
+  StopThread(0);
 
+  /* close and delete the com port connection */
   if (m_port)
-  {
-    delete m_port;
-    m_port = NULL;
-  }
-
-  return NULL;
+    m_port->Close();
 }
 
 cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout /* = 3 */, uint8_t iRetryLineTimeout /* = 3 */)
@@ -267,22 +180,16 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
   if (!IsRunning())
     return retVal;
 
-  CCECAdapterMessage *output = new CCECAdapterMessage(data);
+  CCECAdapterMessage *output = new CCECAdapterMessage(data, iMaxTries, iLineTimeout, iRetryLineTimeout);
 
-  /* set the number of retries */
-  if (data.opcode == CEC_OPCODE_NONE) //TODO
-    output->maxTries = 1;
-  else if (data.initiator != CECDEVICE_BROADCAST)
-    output->maxTries = iMaxTries;
-
-  output->lineTimeout = iLineTimeout;
-  output->retryTimeout = iRetryLineTimeout;
-  output->tries = 0;
+  /* mark as waiting for an ack from the destination */
+  MarkAsWaiting(data.destination);
 
+  /* send the message */
   bool bRetry(true);
   while (bRetry && ++output->tries < output->maxTries)
   {
-    bRetry = (!Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
+    bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
     if (bRetry)
       Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
   }
@@ -292,488 +199,325 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
   return retVal;
 }
 
-bool CUSBCECAdapterCommunication::Write(CCECAdapterMessage *data)
+void *CUSBCECAdapterCommunication::Process(void)
 {
-  data->state = ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT;
-  m_outBuffer.Push(data);
-  data->event.Wait(5000);
+  CCECAdapterMessage msg;
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
 
-  if ((data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT_ACKED) ||
-      (!data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT))
+  while (!IsStopped())
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "command was not %s", data->state == ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED ? "acked" : "sent");
-    return false;
+    /* read from the serial port */
+    if (!ReadFromDevice(50, 5))
+      break;
+
+    /* TODO sleep 5 ms so other threads can get a lock */
+    Sleep(5);
   }
 
-  return true;
+  m_adapterMessageQueue->Clear();
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread ended");
+  return NULL;
 }
 
-bool CUSBCECAdapterCommunication::Read(cec_command &command, uint32_t iTimeout)
+bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
 {
-  if (!IsRunning())
-    return false;
+  bool bIsError(msg.IsError());
+  cec_adapter_messagecode messageCode(msg.Message());
+  CLockObject lock(m_mutex);
 
-  CCECAdapterMessage msg;
-  if (Read(msg, iTimeout))
+  if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
   {
-    if (ParseMessage(msg))
+    m_lastPollDestination = msg.Destination();
+    if (msg.Destination() < CECDEVICE_BROADCAST)
     {
-      command = m_currentframe;
-      m_currentframe.Clear();
-      return true;
+      if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
+      {
+        if (m_callback)
+          m_callback->HandlePoll(msg.Initiator(), msg.Destination());
+      }
+      else
+        m_bWaitingForAck[msg.Destination()] = false;
     }
   }
-  return false;
-}
-
-bool CUSBCECAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout)
-{
-  CLockObject lock(m_mutex);
-
-  msg.Clear();
-  CCECAdapterMessage *buf(NULL);
-
-  if (!m_inBuffer.Pop(buf))
+  else if (messageCode == MSGCODE_RECEIVE_FAILED)
   {
-    if (iTimeout == 0 || !m_rcvCondition.Wait(m_mutex, m_bHasData, iTimeout))
-      return false;
-    m_inBuffer.Pop(buf);
-    m_bHasData = !m_inBuffer.IsEmpty();
+    /* hack to suppress warnings when an LG is polling */
+    if (m_lastPollDestination != CECDEVICE_UNKNOWN)
+      bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
   }
 
-  if (buf)
+  return bIsError;
+}
+
+void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
+{
+  /* mark as waiting for an ack from the destination */
+  if (dest < CECDEVICE_BROADCAST)
   {
-    msg.packet = buf->packet;
-    msg.state = ADAPTER_MESSAGE_STATE_INCOMING;
-    delete buf;
-    return true;
+    CLockObject lock(m_mutex);
+    m_bWaitingForAck[dest] = true;
   }
-  return false;
 }
 
-CStdString CUSBCECAdapterCommunication::GetError(void) const
+void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = 1000 */)
 {
-  CStdString strError;
-  strError = m_port->GetError();
-  return strError;
+  CTimeout timeout(iTimeout);
+  uint8_t buff[1024];
+  ssize_t iBytesRead(0);
+  bool bGotMsgEnd(true);
+
+  while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
+  {
+    bGotMsgEnd = false;
+    /* if something was received, wait for MSGEND */
+    for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
+      bGotMsgEnd = buff[iPtr] == MSGEND;
+  }
 }
 
-bool CUSBCECAdapterCommunication::StartBootloader(void)
+bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
 {
-  bool bReturn(false);
-  if (!IsRunning())
-    return bReturn;
+  bool bReturn(true);
+  bool bChanged(false);
 
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "starting the bootloader");
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_START_BOOTLOADER);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
-  output->expectControllerAck = false;
+  /* only send the command if the timeout changed */
+  {
+    CLockObject lock(m_mutex);
+    bChanged = (m_iLineTimeout != iTimeout);
+    m_iLineTimeout = iTimeout;
+  }
 
-  if ((bReturn = Write(output)) == false)
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not start the bootloader");
-  delete output;
+  if (bChanged)
+    bReturn = m_commands->SetLineTimeout(iTimeout);
 
   return bReturn;
 }
 
-bool CUSBCECAdapterCommunication::PingAdapter(void)
+bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping");
+  CLockObject adapterLock(m_mutex);
+  if (!m_port->IsOpen())
+  {
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to the serial port: the connection is closed", CCECAdapterMessage::ToString(message->Message()));
+    message->state = ADAPTER_MESSAGE_STATE_ERROR;
+    return false;
+  }
 
-  CCECAdapterMessage *output = new CCECAdapterMessage;
+  /* 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 the serial port: %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetError().c_str());
+    message->state = ADAPTER_MESSAGE_STATE_ERROR;
+    return false;
+  }
 
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_PING);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
+  message->state = ADAPTER_MESSAGE_STATE_SENT;
+  return true;
+}
 
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
+bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
+{
+  ssize_t iBytesRead(0);
+  uint8_t buff[256];
+  if (iSize > 256)
+    iSize = 256;
+
+  /* read from the serial port */
+  {
+    CLockObject lock(m_mutex);
+    if (!m_port)
+      return false;
+    iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
+  }
+
+  if (iBytesRead < 0 || iBytesRead > 256)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not ping the adapter");
+    CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
+    StopThread(false);
     return false;
   }
+  else if (iBytesRead > 0)
+  {
+    /* add the data to the current frame */
+    m_adapterMessageQueue->AddData(buff, iBytesRead);
+  }
 
   return true;
 }
 
-bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg)
+CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
 {
-  bool bEom(false);
-  bool bIsError(msg.IsError());
+  if (!m_port || !m_port->IsOpen() ||
+      !m_adapterMessageQueue)
+    return NULL;
 
-  if (msg.IsEmpty())
-    return bEom;
+  /* create the adapter message for this command */
+  CCECAdapterMessage *output = new CCECAdapterMessage;
+  output->PushBack(MSGSTART);
+  output->PushEscaped((uint8_t)msgCode);
+  output->Append(params);
+  output->PushBack(MSGEND);
 
-  CLockObject adapterLock(m_mutex);
-  switch(msg.Message())
+  /* write the command */
+  if (!m_adapterMessageQueue->Write(output))
   {
-  case MSGCODE_FRAME_START:
-    {
-      m_currentframe.Clear();
-      if (msg.Size() >= 2)
-      {
-        m_currentframe.initiator   = msg.Initiator();
-        m_currentframe.destination = msg.Destination();
-        m_currentframe.ack         = msg.IsACK();
-        m_currentframe.eom         = msg.IsEOM();
-      }
-      if (m_currentframe.ack == 0x1)
-      {
-        m_lastInitiator = m_currentframe.initiator;
-        m_processor->HandlePoll(m_currentframe.initiator, m_currentframe.destination);
-      }
-    }
-    break;
-  case MSGCODE_RECEIVE_FAILED:
+    // timed out
+    return output;
+  }
+  else
+  {
+    if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED)
     {
-      m_currentframe.Clear();
-      if (m_lastInitiator != CECDEVICE_UNKNOWN)
-        bIsError = m_processor->HandleReceiveFailed(m_lastInitiator);
+      /* 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");
+      delete output;
+      if (SetControlledMode(true))
+        return SendCommand(msgCode, params, true);
     }
-    break;
-  case MSGCODE_FRAME_DATA:
-    {
-      if (msg.Size() >= 2)
-      {
-        m_currentframe.PushBack(msg[1]);
-        m_currentframe.eom = msg.IsEOM();
-      }
-    }
-    break;
-  default:
-    break;
   }
 
-  CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
-  return msg.IsEOM();
+  return output;
 }
 
-uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
+bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */)
 {
-  uint16_t iReturn(m_iFirmwareVersion);
+  bool bReturn(false);
+  CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
 
-  if (iReturn == CEC_FW_VERSION_UNKNOWN)
+  /* try to ping the adapter */
+  bool bPinged(false);
+  unsigned iPingTry(0);
+  while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
   {
-    CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting the firmware version");
-    CCECAdapterMessage *output = new CCECAdapterMessage;
-
-    output->PushBack(MSGSTART);
-    output->PushEscaped(MSGCODE_FIRMWARE_VERSION);
-    output->PushBack(MSGEND);
-    output->isTransmission = false;
-    output->expectControllerAck = false;
-
-    SendMessageToAdapter(output);
-    bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT;
-    delete output;
-    if (!bWriteOk)
-    {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "could not request the firmware version");
-      return iReturn;
-    }
+    CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
+    CEvent::Sleep(500);
+  }
 
-    Sleep(250); // TODO ReadFromDevice() isn't waiting for the timeout to pass on win32
-    ReadFromDevice(CEC_DEFAULT_TRANSMIT_WAIT, 5 /* start + msgcode + 2 bytes for fw version + end */);
-    CCECAdapterMessage input;
-    if (Read(input, 0))
-    {
-      if (input.Message() != MSGCODE_FIRMWARE_VERSION || input.Size() != 3)
-        CLibCEC::AddLog(CEC_LOG_ERROR, "invalid firmware version (size = %d, message = %d)", input.Size(), input.Message());
-      else
-      {
-        m_iFirmwareVersion = (input[1] << 8 | input[2]);
-        iReturn = m_iFirmwareVersion;
-      }
-    }
-    else
+  /* try to read the firmware version */
+  if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
+  {
+    /* try to set controlled mode for v2+ firmwares */
+    unsigned iControlledTry(0);
+    bool bControlled(false);
+    while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "no firmware version received");
+      CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
+      CEvent::Sleep(500);
     }
+    bReturn = bControlled;
   }
+  else
+    bReturn = true;
 
-  return iReturn;
+  SetInitialised(bReturn);
+  return bReturn;
 }
 
-bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
+bool CUSBCECAdapterCommunication::IsOpen(void)
 {
-  m_iLineTimeout = iTimeout;
-  return true;
-  //TODO
-//  bool bReturn(m_iLineTimeout != iTimeout);
-//
-//  if (!bReturn)
-//  {
-//    CCECAdapterMessage *output = new CCECAdapterMessage;
-//
-//    output->PushBack(MSGSTART);
-//    output->PushEscaped(MSGCODE_TRANSMIT_IDLETIME);
-//    output->PushEscaped(iTimeout);
-//    output->PushBack(MSGEND);
-//    output->isTransmission = false;
-//
-//    if ((bReturn = Write(output)) == false)
-//      CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the idletime");
-//    delete output;
-//  }
-//
-//  return bReturn;
+  /* thread is not being stopped, the port is open and the thread is running */
+  return !IsStopped() && m_port->IsOpen() && IsRunning();
 }
 
-bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
+CStdString CUSBCECAdapterCommunication::GetError(void) const
 {
-  return SetAckMaskInternal(iMask, false);
+  return m_port->GetError();
 }
 
-bool CUSBCECAdapterCommunication::SetAckMaskInternal(uint16_t iMask, bool bWriteDirectly /* = false */)
+void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
 {
-  bool bReturn(false);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask);
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_ACK_MASK);
-  output->PushEscaped(iMask >> 8);
-  output->PushEscaped((uint8_t)iMask);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
-
-  if (bWriteDirectly)
-    SendMessageToAdapter(output);
-  else if ((bReturn = Write(output)) == false)
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the ackmask");
-  delete output;
-
-  return bReturn;
+  CLockObject lock(m_mutex);
+  m_bInitialised = bSetTo;
 }
 
-
-bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
+bool CUSBCECAdapterCommunication::IsInitialised(void)
 {
   CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off");
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_CONTROLLED);
-  output->PushEscaped(controlled);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
+  return m_bInitialised;
+}
 
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT;
-  delete output;
-  if (!bWriteOk)
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set controlled mode");
+bool CUSBCECAdapterCommunication::StartBootloader(void)
+{
+  if (!IsRunning())
     return false;
-  }
 
-  return true;
+  return m_commands->StartBootloader();
 }
 
-bool CUSBCECAdapterCommunication::IsOpen(void)
+bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
 {
-  return !IsStopped() && m_port->IsOpen() && IsRunning();
+  return m_commands->SetAckMask(iMask);
 }
 
-bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message)
+bool CUSBCECAdapterCommunication::PingAdapter(void)
 {
-  bool bError(false);
-  bool bTransmitSucceeded(false);
-  uint8_t iPacketsLeft(message.Size() / 4);
-
-  int64_t iNow = GetTimeMs();
-  int64_t iTargetTime = iNow + (message.transmit_timeout <= 5 ? CEC_DEFAULT_TRANSMIT_WAIT : message.transmit_timeout);
-
-  while (!bTransmitSucceeded && !bError && iNow < iTargetTime)
-  {
-    ReadFromDevice(50);
-    CCECAdapterMessage msg;
-    if (!Read(msg, 0))
-    {
-      iNow = GetTimeMs();
-      continue;
-    }
-
-    if (msg.Message() == MSGCODE_FRAME_START && msg.IsACK())
-    {
-      m_processor->HandlePoll(msg.Initiator(), msg.Destination());
-      m_lastInitiator = msg.Initiator();
-      iNow = GetTimeMs();
-      continue;
-    }
-
-    if (msg.Message() == MSGCODE_RECEIVE_FAILED &&
-        m_lastInitiator != CECDEVICE_UNKNOWN &&
-        m_processor->HandleReceiveFailed(m_lastInitiator))
-    {
-      iNow = GetTimeMs();
-      continue;
-    }
-
-    bError = msg.IsError();
-    if (bError)
-    {
-      message.reply = msg.Message();
-      CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString());
-    }
-    else
-    {
-      switch(msg.Message())
-      {
-      case MSGCODE_COMMAND_ACCEPTED:
-        if (iPacketsLeft > 0)
-          iPacketsLeft--;
-        if (!message.isTransmission && iPacketsLeft == 0)
-          bTransmitSucceeded = true;
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - waiting for %d more", msg.ToString().c_str(), iPacketsLeft);
-        break;
-      case MSGCODE_TRANSMIT_SUCCEEDED:
-        CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString());
-        bTransmitSucceeded = (iPacketsLeft == 0);
-        bError = !bTransmitSucceeded;
-        message.reply = MSGCODE_TRANSMIT_SUCCEEDED;
-        break;
-      default:
-        // ignore other data while waiting
-        break;
-      }
-
-      iNow = GetTimeMs();
-    }
-  }
-
-  message.state = bTransmitSucceeded && !bError ?
-      ADAPTER_MESSAGE_STATE_SENT_ACKED :
-      ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
+  return m_commands->PingAdapter();
+}
 
-  return bTransmitSucceeded && !bError;
+uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
+{
+  return m_commands->GetFirmwareVersion();
 }
 
-void CUSBCECAdapterCommunication::AddData(uint8_t *data, size_t iLen)
+bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
 {
-  CLockObject lock(m_mutex);
-  for (size_t iPtr = 0; iPtr < iLen; iPtr++)
-  {
-    if (!m_bGotStart)
-    {
-      if (data[iPtr] == MSGSTART)
-        m_bGotStart = true;
-    }
-    else if (data[iPtr] == MSGSTART) //we found a msgstart before msgend, this is not right, remove
-    {
-      if (m_currentAdapterMessage.Size() > 0)
-        CLibCEC::AddLog(CEC_LOG_WARNING, "received MSGSTART before MSGEND, removing previous buffer contents");
-      m_currentAdapterMessage.Clear();
-      m_bGotStart = true;
-    }
-    else if (data[iPtr] == MSGEND)
-    {
-      CCECAdapterMessage *newMessage = new CCECAdapterMessage;
-      newMessage->packet = m_currentAdapterMessage.packet;
-      m_inBuffer.Push(newMessage);
-      m_currentAdapterMessage.Clear();
-      m_bGotStart = false;
-      m_bNextIsEscaped = false;
-      m_bHasData = true;
-      m_rcvCondition.Broadcast();
-    }
-    else if (m_bNextIsEscaped)
-    {
-      m_currentAdapterMessage.PushBack(data[iPtr] + (uint8_t)ESCOFFSET);
-      m_bNextIsEscaped = false;
-    }
-    else if (data[iPtr] == MSGESC)
-    {
-      m_bNextIsEscaped = true;
-    }
-    else
-    {
-      m_currentAdapterMessage.PushBack(data[iPtr]);
-    }
-  }
+  return m_commands->PersistConfiguration(configuration);
 }
 
-bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
+bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
 {
-  ssize_t iBytesRead;
-  uint8_t buff[256];
-  if (!m_port)
-    return false;
-  if (iSize > 256)
-    iSize = 256;
+  return m_commands->GetConfiguration(configuration);
+}
 
-  CLockObject lock(m_mutex);
-  iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
-  if (iBytesRead < 0 || iBytesRead > 256)
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
-    StopThread(false);
-    return false;
-  }
-  else if (iBytesRead > 0)
-  {
-    AddData(buff, iBytesRead);
-  }
+CStdString CUSBCECAdapterCommunication::GetPortName(void)
+{
+  return m_port->GetName();
+}
 
-  return iBytesRead > 0;
+bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
+{
+  return m_commands->SetControlledMode(controlled);
 }
 
-void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg)
+void *CAdapterPingThread::Process(void)
 {
-  CLockObject adapterLock(m_mutex);
-  if (!m_port->IsOpen())
+  while (!IsStopped())
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "error writing to serial port: the connection is closed");
-    msg->state = ADAPTER_MESSAGE_STATE_ERROR;
-    return;
-  }
-
-  if (msg->tries == 1)
-    SetLineTimeout(msg->lineTimeout);
-  else
-    SetLineTimeout(msg->retryTimeout);
+    if (m_timeout.TimeLeft() == 0)
+    {
+      /* reinit the timeout */
+      m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
 
-  if (m_port->Write(msg->packet.data, msg->Size()) != (ssize_t) msg->Size())
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "error writing to serial port: %s", m_port->GetError().c_str());
-    msg->state = ADAPTER_MESSAGE_STATE_ERROR;
-  }
-  else
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "command sent");
-    msg->state = ADAPTER_MESSAGE_STATE_SENT;
+      /* send a ping to the adapter */
+      bool bPinged(false);
+      int iFailedCounter(0);
+      while (!bPinged && iFailedCounter < 3)
+      {
+        if (!m_com->PingAdapter())
+        {
+          /* sleep 1 second and retry */
+          Sleep(1000);
+          ++iFailedCounter;
+        }
+        else
+        {
+          bPinged = true;
+        }
+      }
 
-    if (msg->expectControllerAck)
-    {
-      if (!WaitForAck(*msg))
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "did not receive ack");
+      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->StopThread(false);
+        break;
+      }
     }
-  }
-  msg->event.Signal();
-}
-
-void CUSBCECAdapterCommunication::WriteNextCommand(void)
-{
-  CCECAdapterMessage *msg(NULL);
-  if (m_outBuffer.Pop(msg))
-    SendMessageToAdapter(msg);
-}
 
-CStdString CUSBCECAdapterCommunication::GetPortName(void)
-{
-  CStdString strName;
-  strName = m_port->GetName();
-  return strName;
+    Sleep(500);
+  }
+  return NULL;
 }
index c71453a4a4f0cf20a3827f0a4714a8d091e1160c..9d125a7b1080fd1de958f5827d8cd8bb4e2ab39b 100644 (file)
@@ -45,78 +45,137 @@ namespace PLATFORM
 namespace CEC
 {
   class CCECProcessor;
+  class CAdapterPingThread;
+  class CUSBCECAdapterCommands;
+  class CCECAdapterMessageQueue;
 
-  class CUSBCECAdapterProcessor: public PLATFORM::CThread
+  class CUSBCECAdapterCommunication : public IAdapterCommunication, public PLATFORM::CThread
   {
-  public:
-    CUSBCECAdapterProcessor(IAdapterCommunicationCallback *cb) :
-      m_callback(cb) {};
-    virtual ~CUSBCECAdapterProcessor(void)
-    {
-      StopThread();
-    }
-
-    void *Process(void);
-    void AddCommand(cec_command command);
-  private:
-    IAdapterCommunicationCallback *     m_callback;
-    PLATFORM::SyncedBuffer<cec_command> m_inBuffer;
-  };
+    friend class CUSBCECAdapterCommands;
+    friend class CCECAdapterMessageQueue;
 
-  class CUSBCECAdapterCommunication : public IAdapterCommunication, private PLATFORM::CThread
-  {
   public:
-    CUSBCECAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate = 38400);
-    virtual ~CUSBCECAdapterCommunication();
+    /*!
+     * @brief Create a new USB-CEC communication handler.
+     * @param callback The callback to use for incoming CEC commands.
+     * @param strPort The name of the com port to use.
+     * @param iBaudRate The baudrate to use on the com port connection.
+     */
+    CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate = 38400);
+    virtual ~CUSBCECAdapterCommunication(void);
 
-    virtual bool Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs = 10000, bool bSkipChecks = false);
-    virtual void Close(void);
-    virtual bool IsOpen(void);
-    virtual CStdString GetError(void) const;
-
-    bool Read(cec_command &command, uint32_t iTimeout);
+    /** @name IAdapterCommunication implementation */
+    ///{
+    bool Open(uint32_t iTimeoutMs = 10000, bool bSkipChecks = false, bool bStartListening = true);
+    void Close(void);
+    bool IsOpen(void);
+    CStdString GetError(void) const;
     cec_adapter_message_state Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout = 3, uint8_t iRetryLineTimeout = 3);
 
-    virtual bool SetLineTimeout(uint8_t iTimeout);
-    virtual bool StartBootloader(void);
-    virtual bool SetAckMask(uint16_t iMask);
-    virtual bool PingAdapter(void);
-    virtual uint16_t GetFirmwareVersion(void);
-    virtual bool SetControlledMode(bool controlled);
-    virtual bool PersistConfiguration(libcec_configuration * UNUSED(configuration)) { return false; } // TODO
-    virtual CStdString GetPortName(void);
-    virtual uint16_t GetPhysicalAddress(void) { return 0; }
+    bool StartBootloader(void);
+    bool SetAckMask(uint16_t iMask);
+    bool PingAdapter(void);
+    uint16_t GetFirmwareVersion(void);
+    bool PersistConfiguration(libcec_configuration *configuration);
+    bool GetConfiguration(libcec_configuration *configuration);
+    CStdString GetPortName(void);
+    uint16_t GetPhysicalAddress(void) { return 0; }
+    bool SetControlledMode(bool controlled);
+    ///}
 
     void *Process(void);
+
   private:
-    bool SetAckMaskInternal(uint16_t iMask, bool bWriteDirectly = false);
+    /*!
+     * @brief Clear all input bytes.
+     * @param iTimeout Timeout when anything was received.
+     */
+    void ClearInputBytes(uint32_t iTimeout = 1000);
+
+    /*!
+     * @brief Change the current CEC line timeout.
+     * @param iTimeout The new timeout.
+     * @return True when acked by the controller, false otherwise.
+     */
+    bool SetLineTimeout(uint8_t iTimeout);
+
+    /*!
+     * @brief Send a command to the controller and wait for an ack.
+     * @param msgCode The command to send.
+     * @param params The parameters to the command.
+     * @param bIsRetry True when this command is being retried, false otherwise.
+     * @return The message. Delete when done with it.
+     */
+    CCECAdapterMessage *SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry = false);
+
+    /*!
+     * @brief Change the "initialised" status.
+     * @param bSetTo The new value.
+     */
+    void SetInitialised(bool bSetTo = true);
+
+    /*!
+     * @return True when initialised, false otherwise.
+     */
+    bool IsInitialised(void);
 
+    /*!
+     * @brief Pings the adapter, checks the firmware version and sets controlled mode.
+     * @param iTimeoutMs The timeout after which this fails if no proper data was received.
+     * @return True when the checks passed, false otherwise.
+     */
     bool CheckAdapter(uint32_t iTimeoutMs = 10000);
-    bool Write(CCECAdapterMessage *data);
-    bool Read(CCECAdapterMessage &msg, uint32_t iTimeout = 1000);
-    bool ParseMessage(const CCECAdapterMessage &msg);
-    void SendMessageToAdapter(CCECAdapterMessage *msg);
-    void WriteNextCommand(void);
-    void AddData(uint8_t *data, size_t iLen);
+
+    /*!
+     * @brief Handle a poll message inside the adapter message (checks if one is present).
+     * @param msg The adapter message to parse.
+     * @return True when the message resulted in a CEC error, false otherwise.
+     */
+    bool HandlePoll(const CCECAdapterMessage &msg);
+
+    /*!
+     * @brief Read data from the device.
+     * @param iTimeout The read timeout to use.
+     * @param iSize The maximum read size.
+     * @return True when something was read, false otherwise.
+     */
     bool ReadFromDevice(uint32_t iTimeout, size_t iSize = 256);
-    bool WaitForAck(CCECAdapterMessage &message);
-
-    PLATFORM::ISocket *                          m_port;
-    CCECProcessor *                              m_processor;
-    PLATFORM::SyncedBuffer<CCECAdapterMessage *> m_inBuffer;
-    PLATFORM::SyncedBuffer<CCECAdapterMessage *> m_outBuffer;
-    PLATFORM::CMutex                             m_mutex;
-    PLATFORM::CCondition<volatile bool>          m_rcvCondition;
-    volatile bool                                m_bHasData;
-    uint8_t                                      m_iLineTimeout;
-    uint16_t                                     m_iFirmwareVersion;
-    cec_command                                  m_currentframe;
-    cec_logical_address                          m_lastInitiator;
-    CCECAdapterMessage                           m_currentAdapterMessage;
-    bool                                         m_bNextIsEscaped;
-    bool                                         m_bGotStart;
-    IAdapterCommunicationCallback *              m_callback;
-    CUSBCECAdapterProcessor *                    m_messageProcessor;
-    bool                                         m_bInitialised;
+
+    /*!
+     * @brief Writes a message to the serial port.
+     * @param message The message to write.
+     * @return True when written, false otherwise.
+     */
+    bool WriteToDevice(CCECAdapterMessage *message);
+
+    /*!
+     * @brief Called before sending a CEC command over the line, so we know we're expecting an ack.
+     * @param dest The destination of the CEC command.
+     */
+    void MarkAsWaiting(const cec_logical_address dest);
+
+    PLATFORM::ISocket *                          m_port;                 /**< the com port connection */
+    PLATFORM::CMutex                             m_mutex;                /**< mutex for changes in this class */
+    uint8_t                                      m_iLineTimeout;         /**< the current line timeout on the CEC line */
+    cec_logical_address                          m_lastPollDestination;  /**< the destination of the last poll message that was received */
+    bool                                         m_bInitialised;         /**< true when the connection is initialised, false otherwise */
+    bool                                         m_bWaitingForAck[15];   /**< array in which we store from which devices we're expecting acks */
+    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 */
+  };
+
+  class CAdapterPingThread : public PLATFORM::CThread
+  {
+  public:
+    CAdapterPingThread(CUSBCECAdapterCommunication *com, uint32_t iTimeout) :
+        m_com(com),
+        m_timeout(iTimeout){}
+    virtual ~CAdapterPingThread(void) {}
+
+    virtual void* Process(void);
+  private:
+    CUSBCECAdapterCommunication *m_com;
+    PLATFORM::CTimeout           m_timeout;
   };
 };
index 0d9f8b3d9c04b19738f560be47d1cb57ae43f95b..8775b4e55bbb2444c8b9a841e3e57f0e3748773f 100644 (file)
@@ -55,6 +55,9 @@ static GUID USB_RAW_GUID =  { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0x
 extern "C" {
 #include <libudev.h>
 }
+#elif defined(__FreeBSD__)
+#include <stdio.h>
+#include <unistd.h>
 #endif
 
 #define CEC_VID 0x2548
@@ -323,6 +326,19 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t i
 
     RegCloseKey(hDeviceKey);
   }
+#elif defined(__FreeBSD__)
+  char devicePath[PATH_MAX + 1];
+  int i;
+
+  for (i = 0; i < 8; ++i)
+  {
+    (void)snprintf(devicePath, sizeof(devicePath), "/dev/ttyU%d", i);
+    if (!access(devicePath, 0))
+    {
+      snprintf(deviceList[iFound  ].path, sizeof(deviceList[iFound].path), "%s", devicePath);
+      snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", devicePath);
+    }
+  }
 #endif
 
   iBufSize = 0; /* silence "unused" warning on linux/osx */
diff --git a/src/lib/adapter/USBCECAdapterMessage.cpp b/src/lib/adapter/USBCECAdapterMessage.cpp
new file mode 100644 (file)
index 0000000..c23c21b
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * 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 "USBCECAdapterMessage.h"
+#include "../LibCEC.h"
+
+using namespace CEC;
+using namespace PLATFORM;
+
+CCECAdapterMessage::CCECAdapterMessage(void)
+{
+  Clear();
+}
+
+CCECAdapterMessage::CCECAdapterMessage(const cec_command &command, uint8_t iMaxTries /* = 1 */, uint8_t iLineTimeout /* = 3 */, uint8_t iRetryLineTimeout /* = 3 */)
+{
+  Clear();
+
+  //set ack polarity to high when transmitting to the broadcast address
+  //set ack polarity low when transmitting to any other address
+  PushBack(MSGSTART);
+  PushEscaped(MSGCODE_TRANSMIT_ACK_POLARITY);
+  if (command.destination == CECDEVICE_BROADCAST)
+    PushEscaped(CEC_TRUE);
+  else
+    PushEscaped(CEC_FALSE);
+  PushBack(MSGEND);
+
+  // add source and destination
+  PushBack(MSGSTART);
+  PushEscaped(command.opcode_set == 0 ? (uint8_t)MSGCODE_TRANSMIT_EOM : (uint8_t)MSGCODE_TRANSMIT);
+  PushBack(((uint8_t)command.initiator << 4) + (uint8_t)command.destination);
+  PushBack(MSGEND);
+
+  // add opcode
+  if (command.opcode_set == 1)
+  {
+    PushBack(MSGSTART);
+    PushEscaped(command.parameters.IsEmpty() ? (uint8_t)MSGCODE_TRANSMIT_EOM : (uint8_t)MSGCODE_TRANSMIT);
+    PushBack((uint8_t) command.opcode);
+    PushBack(MSGEND);
+
+    // add parameters
+    for (int8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
+    {
+      PushBack(MSGSTART);
+
+      if (iPtr == command.parameters.size - 1)
+        PushEscaped( MSGCODE_TRANSMIT_EOM);
+      else
+        PushEscaped(MSGCODE_TRANSMIT);
+
+      PushEscaped(command.parameters[iPtr]);
+
+      PushBack(MSGEND);
+    }
+  }
+
+  // set timeout
+  transmit_timeout = command.transmit_timeout;
+
+  /* set the number of retries */
+  if (command.opcode == CEC_OPCODE_NONE) //TODO
+    maxTries = 1;
+  else if (command.initiator != CECDEVICE_BROADCAST)
+    maxTries = iMaxTries;
+
+  lineTimeout = iLineTimeout;
+  retryTimeout = iRetryLineTimeout;
+  tries = 0;
+}
+
+CStdString CCECAdapterMessage::ToString(void) const
+{
+  CStdString strMsg;
+  if (Size() == 0)
+  {
+    strMsg = "empty message";
+  }
+  else
+  {
+    strMsg = ToString(Message());
+
+    switch (Message())
+    {
+    case MSGCODE_TIMEOUT_ERROR:
+    case MSGCODE_HIGH_ERROR:
+    case MSGCODE_LOW_ERROR:
+      {
+        uint32_t iLine = (Size() >= 4) ? (At(2) << 8) | At(3) : 0;
+        uint32_t iTime = (Size() >= 8) ? (At(4) << 24) | (At(5) << 16) | (At(6) << 8) | At(7) : 0;
+        strMsg.AppendFormat(" line:%u", iLine);
+        strMsg.AppendFormat(" time:%u", iTime);
+      }
+      break;
+    case MSGCODE_FRAME_START:
+      if (Size() >= 3)
+        strMsg.AppendFormat(" initiator:%1x destination:%1x ack:%s %s", Initiator(), Destination(), IsACK() ? "high" : "low", IsEOM() ? "eom" : "");
+      break;
+    case MSGCODE_FRAME_DATA:
+      if (Size() >= 3)
+        strMsg.AppendFormat(" %02x %s", At(2), IsEOM() ? "eom" : "");
+      break;
+    default:
+      break;
+    }
+  }
+
+  return strMsg;
+}
+
+const char *CCECAdapterMessage::ToString(cec_adapter_messagecode msgCode)
+{
+  switch (msgCode)
+  {
+  case MSGCODE_NOTHING:
+    return "NOTHING";
+  case MSGCODE_PING:
+    return "PING";
+  case MSGCODE_TIMEOUT_ERROR:
+    return "TIMEOUT";
+  case MSGCODE_HIGH_ERROR:
+    return "HIGH_ERROR";
+  case MSGCODE_LOW_ERROR:
+    return "LOW_ERROR";
+  case MSGCODE_FRAME_START:
+    return "FRAME_START";
+  case MSGCODE_FRAME_DATA:
+    return "FRAME_DATA";
+  case MSGCODE_RECEIVE_FAILED:
+    return "RECEIVE_FAILED";
+  case MSGCODE_COMMAND_ACCEPTED:
+    return "COMMAND_ACCEPTED";
+  case MSGCODE_COMMAND_REJECTED:
+    return "COMMAND_REJECTED";
+  case MSGCODE_SET_ACK_MASK:
+    return "SET_ACK_MASK";
+  case MSGCODE_TRANSMIT:
+    return "TRANSMIT";
+  case MSGCODE_TRANSMIT_EOM:
+    return "TRANSMIT_EOM";
+  case MSGCODE_TRANSMIT_IDLETIME:
+    return "TRANSMIT_IDLETIME";
+  case MSGCODE_TRANSMIT_ACK_POLARITY:
+    return "TRANSMIT_ACK_POLARITY";
+  case MSGCODE_TRANSMIT_LINE_TIMEOUT:
+    return "TRANSMIT_LINE_TIMEOUT";
+  case MSGCODE_TRANSMIT_SUCCEEDED:
+    return "TRANSMIT_SUCCEEDED";
+  case MSGCODE_TRANSMIT_FAILED_LINE:
+    return "TRANSMIT_FAILED_LINE";
+  case MSGCODE_TRANSMIT_FAILED_ACK:
+    return "TRANSMIT_FAILED_ACK";
+  case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
+    return "TRANSMIT_FAILED_TIMEOUT_DATA";
+  case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
+    return "TRANSMIT_FAILED_TIMEOUT_LINE";
+  case MSGCODE_FIRMWARE_VERSION:
+    return "FIRMWARE_VERSION";
+  case MSGCODE_START_BOOTLOADER:
+    return "START_BOOTLOADER";
+  case MSGCODE_FRAME_EOM:
+    return "FRAME_EOM";
+  case MSGCODE_FRAME_ACK:
+    return "FRAME_ACK";
+  case MSGCODE_SET_POWERSTATE:
+    return "SET_POWERSTATE";
+  case MSGCODE_SET_CONTROLLED:
+    return "SET_CONTROLLED";
+  case MSGCODE_GET_AUTO_ENABLED:
+    return "GET_AUTO_ENABLED";
+  case MSGCODE_SET_AUTO_ENABLED:
+    return "SET_AUTO_ENABLED";
+  case MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS:
+    return "GET_DEFAULT_LOGICAL_ADDRESS";
+  case MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS:
+    return "SET_DEFAULT_LOGICAL_ADDRESS";
+  case MSGCODE_GET_LOGICAL_ADDRESS_MASK:
+    return "GET_LOGICAL_ADDRESS_MASK";
+  case MSGCODE_SET_LOGICAL_ADDRESS_MASK:
+    return "SET_LOGICAL_ADDRESS_MASK";
+  case MSGCODE_GET_PHYSICAL_ADDRESS:
+    return "GET_PHYSICAL_ADDRESS";
+  case MSGCODE_SET_PHYSICAL_ADDRESS:
+    return "SET_PHYSICAL_ADDRESS";
+  case MSGCODE_GET_DEVICE_TYPE:
+    return "GET_DEVICE_TYPE";
+  case MSGCODE_SET_DEVICE_TYPE:
+    return "SET_DEVICE_TYPE";
+  case MSGCODE_GET_HDMI_VERSION:
+    return "GET_HDMI_VERSION";
+  case MSGCODE_SET_HDMI_VERSION:
+    return "SET_HDMI_VERSION";
+  case MSGCODE_GET_OSD_NAME:
+    return "GET_OSD_NAME";
+  case MSGCODE_SET_OSD_NAME:
+    return "SET_OSD_NAME";
+  case MSGCODE_WRITE_EEPROM:
+    return "WRITE_EEPROM";
+  }
+
+  return "unknown";
+}
+
+uint8_t CCECAdapterMessage::operator[](uint8_t pos) const
+{
+  return pos < packet.size ? packet[pos] : 0;
+}
+
+uint8_t CCECAdapterMessage::At(uint8_t pos) const
+{
+  return pos < packet.size ? packet[pos] : 0;
+}
+
+uint8_t CCECAdapterMessage::Size(void) const
+{
+  return packet.size;
+}
+
+bool CCECAdapterMessage::IsEmpty(void) const
+{
+  return packet.IsEmpty();
+}
+
+void CCECAdapterMessage::Clear(void)
+{
+  state               = ADAPTER_MESSAGE_STATE_UNKNOWN;
+  transmit_timeout    = CEC_DEFAULT_TRANSMIT_TIMEOUT;
+  response.Clear();
+  packet.Clear();
+  maxTries            = CEC_DEFAULT_TRANSMIT_RETRIES + 1;
+  tries               = 0;
+  lineTimeout         = 3;
+  retryTimeout        = 3;
+  bNextByteIsEscaped  = false;
+}
+
+void CCECAdapterMessage::Shift(uint8_t iShiftBy)
+{
+  packet.Shift(iShiftBy);
+}
+
+void CCECAdapterMessage::Append(CCECAdapterMessage &data)
+{
+  Append(data.packet);
+}
+
+void CCECAdapterMessage::Append(cec_datapacket &data)
+{
+  for (uint8_t iPtr = 0; iPtr < data.size; iPtr++)
+    PushBack(data[iPtr]);
+}
+
+void CCECAdapterMessage::PushBack(uint8_t byte)
+{
+  packet.PushBack(byte);
+}
+
+void CCECAdapterMessage::PushEscaped(uint8_t byte)
+{
+  if (byte >= MSGESC)
+  {
+    PushBack(MSGESC);
+    PushBack(byte - ESCOFFSET);
+  }
+  else
+  {
+    PushBack(byte);
+  }
+}
+
+bool CCECAdapterMessage::PushReceivedByte(uint8_t byte)
+{
+  if (byte == MSGSTART)
+  {
+    if (HasStartMessage())
+    {
+      CLibCEC::AddLog(CEC_LOG_WARNING, "received MSGSTART before MSGEND, removing previous buffer contents");
+      Clear();
+    }
+    PushBack(byte);
+  }
+  else
+  {
+    if (bNextByteIsEscaped)
+    {
+      PushBack(byte + (uint8_t)ESCOFFSET);
+      bNextByteIsEscaped = false;
+    }
+    else if (byte == MSGESC)
+      bNextByteIsEscaped = true;
+    else
+      PushBack(byte);
+  }
+
+  return byte == MSGEND;
+}
+
+cec_adapter_messagecode CCECAdapterMessage::Message(void) const
+{
+  return packet.size >= 2 ?
+      (cec_adapter_messagecode) (packet.At(1) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) :
+      MSGCODE_NOTHING;
+}
+
+bool CCECAdapterMessage::IsTranmission(void) const
+{
+  cec_adapter_messagecode msgCode = Message();
+  return msgCode == MSGCODE_FRAME_ACK ||
+      msgCode == MSGCODE_FRAME_DATA ||
+      msgCode == MSGCODE_FRAME_EOM ||
+      msgCode == MSGCODE_FRAME_START ||
+      msgCode == MSGCODE_HIGH_ERROR ||
+      msgCode == MSGCODE_LOW_ERROR ||
+      msgCode == MSGCODE_RECEIVE_FAILED ||
+      msgCode == MSGCODE_TRANSMIT_ACK_POLARITY ||
+      msgCode == MSGCODE_TRANSMIT_EOM ||
+      msgCode == MSGCODE_TRANSMIT_FAILED_ACK ||
+      msgCode == MSGCODE_TRANSMIT_FAILED_LINE ||
+      msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA ||
+      msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE ||
+      msgCode == MSGCODE_TRANSMIT_LINE_TIMEOUT ||
+      msgCode == MSGCODE_TRANSMIT_SUCCEEDED;
+}
+
+bool CCECAdapterMessage::IsEOM(void) const
+{
+  return packet.size >= 2 ?
+      (packet.At(1) & MSGCODE_FRAME_EOM) != 0 :
+      false;
+}
+
+bool CCECAdapterMessage::IsACK(void) const
+{
+  return packet.size >= 2 ?
+      (packet.At(1) & MSGCODE_FRAME_ACK) != 0 :
+      false;
+}
+
+bool CCECAdapterMessage::IsError(void) const
+{
+  cec_adapter_messagecode code = Message();
+  return (code == MSGCODE_HIGH_ERROR ||
+          code == MSGCODE_LOW_ERROR ||
+          code == MSGCODE_RECEIVE_FAILED ||
+          code == MSGCODE_COMMAND_REJECTED ||
+          code == MSGCODE_TRANSMIT_LINE_TIMEOUT ||
+          code == MSGCODE_TRANSMIT_FAILED_LINE ||
+          code == MSGCODE_TRANSMIT_FAILED_ACK ||
+          code == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA ||
+          code == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE);
+}
+
+bool CCECAdapterMessage::NeedsRetry(void) const
+{
+  return Reply() == MSGCODE_NOTHING ||
+         Reply() == MSGCODE_RECEIVE_FAILED ||
+         Reply() == MSGCODE_TIMEOUT_ERROR ||
+         Reply() == MSGCODE_TRANSMIT_FAILED_LINE ||
+         Reply() == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA ||
+         Reply() == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE ||
+         Reply() == MSGCODE_TRANSMIT_LINE_TIMEOUT;
+}
+
+cec_logical_address CCECAdapterMessage::Initiator(void) const
+{
+  return packet.size >= 3 ?
+      (cec_logical_address) (packet.At(2) >> 4) :
+      CECDEVICE_UNKNOWN;
+}
+
+cec_logical_address CCECAdapterMessage::Destination(void) const
+{
+  return packet.size >= 3 ?
+      (cec_logical_address) (packet.At(2) & 0xF) :
+      CECDEVICE_UNKNOWN;
+}
+
+bool CCECAdapterMessage::HasStartMessage(void) const
+{
+  return packet.size >= 1 && packet.At(0) == MSGSTART;
+}
+
+bool CCECAdapterMessage::PushToCecCommand(cec_command &command) const
+{
+  // empty message
+  if (IsEmpty())
+    return false;
+
+  cec_adapter_messagecode msgCode = Message();
+  if (msgCode == MSGCODE_FRAME_START)
+  {
+    command.Clear();
+    if (Size() >= 3)
+    {
+      command.initiator   = Initiator();
+      command.destination = Destination();
+      command.ack         = IsACK();
+      command.eom         = IsEOM();
+    }
+    return IsEOM() && !IsError();
+  }
+  else if (msgCode == MSGCODE_FRAME_DATA)
+  {
+    if (Size() >= 3)
+    {
+      command.PushBack(At(2));
+      command.eom = IsEOM();
+    }
+    return IsEOM() && !IsError();
+  }
+
+  return false;
+}
+
+cec_adapter_messagecode CCECAdapterMessage::Reply(void) const
+{
+  return response.size >= 2 ?
+      (cec_adapter_messagecode) (response.At(1) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) :
+      MSGCODE_NOTHING;
+}
index 06e4088eb81a54db93db5f2ea05932b2a61a5df4..2b122602d79b4b13e7716924f0096b57b3e03953 100644 (file)
  */
 
 #include "../platform/util/StdString.h"
+#include "../platform/util/buffer.h"
+#include "../platform/threads/mutex.h"
+#include "../../../include/cectypes.h"
 
 namespace CEC
 {
+  typedef enum cec_adapter_message_state
+  {
+    ADAPTER_MESSAGE_STATE_UNKNOWN = 0,        /**< the initial state */
+    ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT, /**< waiting in the send queue of the adapter, or timed out */
+    ADAPTER_MESSAGE_STATE_SENT,               /**< sent and waiting on an ACK */
+    ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED,     /**< sent, but failed to ACK */
+    ADAPTER_MESSAGE_STATE_SENT_ACKED,         /**< sent, and ACK received */
+    ADAPTER_MESSAGE_STATE_INCOMING,           /**< received from another device */
+    ADAPTER_MESSAGE_STATE_ERROR               /**< an error occured */
+  } cec_adapter_message_state;
+
   class CCECAdapterMessage
   {
   public:
-    CCECAdapterMessage(void) :
-        event(false)
-    {
-      Clear();
-    }
-
-    CCECAdapterMessage(const cec_command &command)
-    {
-      Clear();
-
-      //set ack polarity to high when transmitting to the broadcast address
-      //set ack polarity low when transmitting to any other address
-      PushBack(MSGSTART);
-      PushEscaped(MSGCODE_TRANSMIT_ACK_POLARITY);
-      if (command.destination == CECDEVICE_BROADCAST)
-        PushEscaped(CEC_TRUE);
-      else
-        PushEscaped(CEC_FALSE);
-      PushBack(MSGEND);
-
-      // add source and destination
-      PushBack(MSGSTART);
-      PushEscaped(command.opcode_set == 0 ? (uint8_t)MSGCODE_TRANSMIT_EOM : (uint8_t)MSGCODE_TRANSMIT);
-      PushBack(((uint8_t)command.initiator << 4) + (uint8_t)command.destination);
-      PushBack(MSGEND);
-
-      // add opcode
-      if (command.opcode_set == 1)
-      {
-        PushBack(MSGSTART);
-        PushEscaped(command.parameters.IsEmpty() ? (uint8_t)MSGCODE_TRANSMIT_EOM : (uint8_t)MSGCODE_TRANSMIT);
-        PushBack((uint8_t) command.opcode);
-        PushBack(MSGEND);
-
-        // add parameters
-        for (int8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
-        {
-          PushBack(MSGSTART);
-
-          if (iPtr == command.parameters.size - 1)
-            PushEscaped( MSGCODE_TRANSMIT_EOM);
-          else
-            PushEscaped(MSGCODE_TRANSMIT);
-
-          PushEscaped(command.parameters[iPtr]);
-
-          PushBack(MSGEND);
-        }
-      }
-
-      // set timeout
-      transmit_timeout = command.transmit_timeout;
-      //TODO
-    }
-
-    CCECAdapterMessage &operator=(const CCECAdapterMessage &msg)
-    {
-      packet = msg.packet;
-      state  = msg.state;
-      return *this;
-    }
-
-    CStdString ToString(void) const
-    {
-      CStdString strMsg;
-      if (Size() == 0)
-      {
-        strMsg = "empty message";
-      }
-      else
-      {
-        strMsg = MessageCodeAsString();
-
-        switch (Message())
-        {
-        case MSGCODE_TIMEOUT_ERROR:
-        case MSGCODE_HIGH_ERROR:
-        case MSGCODE_LOW_ERROR:
-          {
-            uint32_t iLine = (Size() >= 3) ? (At(1) << 8) | At(2) : 0;
-            uint32_t iTime = (Size() >= 7) ? (At(3) << 24) | (At(4) << 16) | (At(5) << 8) | At(6) : 0;
-            strMsg.AppendFormat(" line:%u", iLine);
-            strMsg.AppendFormat(" time:%u", iTime);
-          }
-          break;
-        case MSGCODE_FRAME_START:
-          if (Size() >= 2)
-            strMsg.AppendFormat(" initiator:%1x destination:%1x ack:%s %s", Initiator(), Destination(), IsACK() ? "high" : "low", IsEOM() ? "eom" : "");
-          break;
-        case MSGCODE_FRAME_DATA:
-          if (Size() >= 2)
-            strMsg.AppendFormat(" %02x %s", At(1), IsEOM() ? "eom" : "");
-          break;
-        default:
-          break;
-        }
-      }
-
-      return strMsg;
-    }
-
-    CStdString MessageCodeAsString(void) const
-    {
-      CStdString strMsg;
-      switch (Message())
-      {
-      case MSGCODE_NOTHING:
-        strMsg = "NOTHING";
-        break;
-      case MSGCODE_PING:
-        strMsg = "PING";
-        break;
-      case MSGCODE_TIMEOUT_ERROR:
-        strMsg = "TIMEOUT";
-        break;
-      case MSGCODE_HIGH_ERROR:
-        strMsg = "HIGH_ERROR";
-        break;
-      case MSGCODE_LOW_ERROR:
-        strMsg = "LOW_ERROR";
-        break;
-      case MSGCODE_FRAME_START:
-        strMsg = "FRAME_START";
-        break;
-      case MSGCODE_FRAME_DATA:
-        strMsg = "FRAME_DATA";
-        break;
-      case MSGCODE_RECEIVE_FAILED:
-        strMsg = "RECEIVE_FAILED";
-        break;
-      case MSGCODE_COMMAND_ACCEPTED:
-        strMsg = "COMMAND_ACCEPTED";
-        break;
-      case MSGCODE_COMMAND_REJECTED:
-        strMsg = "COMMAND_REJECTED";
-        break;
-      case MSGCODE_SET_ACK_MASK:
-        strMsg = "SET_ACK_MASK";
-        break;
-      case MSGCODE_TRANSMIT:
-        strMsg = "TRANSMIT";
-        break;
-      case MSGCODE_TRANSMIT_EOM:
-        strMsg = "TRANSMIT_EOM";
-        break;
-      case MSGCODE_TRANSMIT_IDLETIME:
-        strMsg = "TRANSMIT_IDLETIME";
-        break;
-      case MSGCODE_TRANSMIT_ACK_POLARITY:
-        strMsg = "TRANSMIT_ACK_POLARITY";
-        break;
-      case MSGCODE_TRANSMIT_LINE_TIMEOUT:
-        strMsg = "TRANSMIT_LINE_TIMEOUT";
-        break;
-      case MSGCODE_TRANSMIT_SUCCEEDED:
-        strMsg = "TRANSMIT_SUCCEEDED";
-        break;
-      case MSGCODE_TRANSMIT_FAILED_LINE:
-        strMsg = "TRANSMIT_FAILED_LINE";
-        break;
-      case MSGCODE_TRANSMIT_FAILED_ACK:
-        strMsg = "TRANSMIT_FAILED_ACK";
-        break;
-      case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
-        strMsg = "TRANSMIT_FAILED_TIMEOUT_DATA";
-        break;
-      case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
-        strMsg = "TRANSMIT_FAILED_TIMEOUT_LINE";
-        break;
-      case MSGCODE_FIRMWARE_VERSION:
-        strMsg = "FIRMWARE_VERSION";
-        break;
-      case MSGCODE_START_BOOTLOADER:
-        strMsg = "START_BOOTLOADER";
-        break;
-      case MSGCODE_FRAME_EOM:
-        strMsg = "FRAME_EOM";
-        break;
-      case MSGCODE_FRAME_ACK:
-        strMsg = "FRAME_ACK";
-        break;
-      case MSGCODE_SET_POWERSTATE:
-        strMsg = "SET_POWERSTATE";
-        break;
-      case MSGCODE_SET_CONTROLLED:
-        strMsg = "SET_CONTROLLED";
-        break;
-      }
-
-      return strMsg;
-    }
-
-    uint8_t operator[](uint8_t pos) const
-    {
-      return packet[pos];
-    }
-
-    uint8_t At(uint8_t pos) const
-    {
-      return packet[pos];
-    }
-
-    uint8_t Size(void) const
-    {
-      return packet.size;
-    }
-
-    bool IsEmpty(void) const
-    {
-      return packet.IsEmpty();
-    }
-
-    void Clear(void)
-    {
-      state               = ADAPTER_MESSAGE_STATE_UNKNOWN;
-      transmit_timeout    = CEC_DEFAULT_TRANSMIT_TIMEOUT;
-      packet.Clear();
-      maxTries            = CEC_DEFAULT_TRANSMIT_RETRIES + 1;
-      tries               = 0;
-      reply               = MSGCODE_NOTHING;
-      isTransmission      = true;
-      expectControllerAck = true;
-      lineTimeout         = 3;
-      retryTimeout        = 3;
-    }
-
-    void Shift(uint8_t iShiftBy)
-    {
-      packet.Shift(iShiftBy);
-    }
-
-    void PushBack(uint8_t add)
-    {
-      packet.PushBack(add);
-    }
-
-    void PushEscaped(uint8_t byte)
-    {
-      if (byte >= MSGESC)
-      {
-        PushBack(MSGESC);
-        PushBack(byte - ESCOFFSET);
-      }
-      else
-      {
-        PushBack(byte);
-      }
-    }
-
-    cec_adapter_messagecode Message(void) const
-    {
-      return packet.size >= 1 ?
-          (cec_adapter_messagecode) (packet.At(0) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) :
-          MSGCODE_NOTHING;
-    }
-
-    bool IsEOM(void) const
-    {
-      return packet.size >= 1 ?
-          (packet.At(0) & MSGCODE_FRAME_EOM) != 0 :
-          false;
-    }
-
-    bool IsACK(void) const
-    {
-      return packet.size >= 1 ?
-          (packet.At(0) & MSGCODE_FRAME_ACK) != 0 :
-          false;
-    }
-
-    bool IsError(void) const
-    {
-      cec_adapter_messagecode code = Message();
-      return (code == MSGCODE_HIGH_ERROR ||
-              code == MSGCODE_LOW_ERROR ||
-              code == MSGCODE_RECEIVE_FAILED ||
-              code == MSGCODE_COMMAND_REJECTED ||
-              code == MSGCODE_TRANSMIT_LINE_TIMEOUT ||
-              code == MSGCODE_TRANSMIT_FAILED_LINE ||
-              code == MSGCODE_TRANSMIT_FAILED_ACK ||
-              code == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA ||
-              code == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE);
-    }
-
-    bool NeedsRetry(void) const
-    {
-      return reply == MSGCODE_NOTHING ||
-             reply == MSGCODE_RECEIVE_FAILED ||
-             reply == MSGCODE_TIMEOUT_ERROR ||
-             reply == MSGCODE_TRANSMIT_FAILED_LINE ||
-             reply == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA ||
-             reply == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE ||
-             reply == MSGCODE_TRANSMIT_LINE_TIMEOUT;
-    }
-
-    cec_logical_address Initiator(void) const
-    {
-      return packet.size >= 2 ?
-          (cec_logical_address) (packet.At(1) >> 4) :
-          CECDEVICE_UNKNOWN;
-    }
-
-    cec_logical_address Destination(void) const
-    {
-      return packet.size >= 2 ?
-          (cec_logical_address) (packet.At(1) & 0xF) :
-          CECDEVICE_UNKNOWN;
-    }
-
-    uint8_t                               maxTries;
-    uint8_t                               tries;
-    cec_adapter_messagecode               reply;
-    cec_datapacket                        packet;
-    cec_adapter_message_state             state;
-    int32_t                               transmit_timeout;
-    bool                                  isTransmission;
-    bool                                  expectControllerAck;
-    uint8_t                               lineTimeout;
-    uint8_t                               retryTimeout;
-    PLATFORM::CEvent                      event;
+    /*!
+     * @brief Create an empty message.
+     */
+    CCECAdapterMessage(void);
+
+    /*!
+     * @brief Create a message with a command that is to be transmitted over the CEC line.
+     * @param command The command to transmit.
+     * @param iMaxTries The maximum number of tries.
+     * @param iLineTimeout The line timeout to use when sending this message the first time.
+     * @param iRetryLineTimeout The line timeout to use when retrying to send this message.
+     */
+    CCECAdapterMessage(const cec_command &command, uint8_t iMaxTries = 1, uint8_t iLineTimeout = 3, uint8_t iRetryLineTimeout = 3);
+
+    /*!
+     * @return the message as human readable string.
+     */
+    CStdString ToString(void) const;
+
+    /*!
+     * @brief Translate the messagecode into a human readable string.
+     * @param msgCode The messagecode to translate.
+     * @return The messagecode as string.
+     */
+    static const char *ToString(cec_adapter_messagecode msgCode);
+
+    /*!
+     * @brief Get the byte at the given position.
+     * @param pos The position to get.
+     * @return The requested byte, or 0 when it's out of range.
+     */
+    uint8_t At(uint8_t pos) const;
+    uint8_t operator[](uint8_t pos) const;
+
+    /*!
+     * @return The size of the packet in bytes.
+     */
+    uint8_t Size(void) const;
+
+    /*!
+     * @return True when empty, false otherwise.
+     */
+    bool IsEmpty(void) const;
+
+    /*!
+     * @brief Clear this message and reset everything to the initial values.
+     */
+    void Clear(void);
+
+    /*!
+     * @brief Shift the message by the given number of bytes.
+     * @param iShiftBy The number of bytes to shift.
+     */
+    void Shift(uint8_t iShiftBy);
+
+    /*!
+     * @brief Append the given message to this message.
+     * @param data The message to append.
+     */
+    void Append(CCECAdapterMessage &data);
+
+    /*!
+     * @brief Append the given datapacket to this message.
+     * @param data The packet to add.
+     */
+    void Append(cec_datapacket &data);
+
+    /*!
+     * @brief Adds a byte to this message. Does not escape the byte.
+     * @param byte The byte to add.
+     */
+    void PushBack(uint8_t byte);
+
+    /*!
+     * @brief Adds a byte to this message and escapes the byte if needed.
+     * @param byte The byte to add.
+     */
+    void PushEscaped(uint8_t byte);
+
+    /*!
+     * @brief Adds a byte to this message.
+     * @param byte The byte to add.
+     * @return True when a full message was received, false otherwise.
+     */
+    bool PushReceivedByte(uint8_t byte);
+
+    /*!
+     * @return The messagecode inside this adapter message, or MSGCODE_NOTHING if there is none.
+     */
+    cec_adapter_messagecode Message(void) const;
+
+    /*!
+     * @return True when this message is a transmission, false otherwise.
+     */
+    bool IsTranmission(void) const;
+
+    /*!
+     * @return True when the EOM bit is set, false otherwise.
+     */
+    bool IsEOM(void) const;
+
+    /*!
+     * @return True when the ACK bit is set, false otherwise.
+     */
+    bool IsACK(void) const;
+
+    /*!
+     * @return True when this message has been replied with an error code, false otherwise.
+     */
+    bool IsError(void) const;
+
+    /*!
+     * @return True when this message has been replied with an error code and needs to be retried, false otherwise.
+     */
+    bool NeedsRetry(void) const;
+
+    /*!
+     * @return The logical address of the initiator, or CECDEVICE_UNKNOWN if unknown.
+     */
+    cec_logical_address Initiator(void) const;
+
+    /*!
+     * @return The logical address of the destination, or CECDEVICE_UNKNOWN if unknown.
+     */
+    cec_logical_address Destination(void) const;
+
+    /*!
+     * @return True when this message contains a start message, false otherwise.
+     */
+    bool HasStartMessage(void) const;
+
+    /*!
+     * @brief Push this adapter message to the end of the given cec_command.
+     * @param command The command to push this message to.
+     * @return True when a full CEC message was received, false otherwise.
+     */
+    bool PushToCecCommand(cec_command &command) const;
+
+    /*!
+     * @return The response messagecode.
+     */
+    cec_adapter_messagecode Reply(void) const;
+
+    uint8_t                               maxTries;             /**< the maximum number of times to try to send this message */
+    uint8_t                               tries;                /**< the amount of times this message has been sent */
+    cec_datapacket                        response;             /**< the response to this message */
+    cec_datapacket                        packet;               /**< the actual data */
+    cec_adapter_message_state             state;                /**< the current state of this message */
+    int32_t                               transmit_timeout;     /**< the timeout to use when sending this message */
+    uint8_t                               lineTimeout;          /**< the default CEC line timeout to use when sending this message */
+    uint8_t                               retryTimeout;         /**< the CEC line timeout to use when retrying to send this message */
+
+  private:
+    bool                                  bNextByteIsEscaped;   /**< true when the next byte that is added will be escaped, false otherwise */
   };
 }
diff --git a/src/lib/adapter/USBCECAdapterMessageQueue.cpp b/src/lib/adapter/USBCECAdapterMessageQueue.cpp
new file mode 100644 (file)
index 0000000..9c66051
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * 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 "USBCECAdapterMessageQueue.h"
+#include "USBCECAdapterCommunication.h"
+#include "../platform/sockets/socket.h"
+#include "../LibCEC.h"
+
+using namespace CEC;
+using namespace PLATFORM;
+using namespace std;
+
+CCECAdapterMessageQueueEntry::CCECAdapterMessageQueueEntry(CCECAdapterMessage *message) :
+    m_message(message),
+    m_iPacketsLeft(message->IsTranmission() ? message->Size() / 4 : 1),
+    m_bSucceeded(false),
+    m_bWaiting(true) {}
+
+CCECAdapterMessageQueueEntry::~CCECAdapterMessageQueueEntry(void) { }
+
+void CCECAdapterMessageQueueEntry::Broadcast(void)
+{
+  CLockObject lock(m_mutex);
+  m_condition.Broadcast();
+}
+
+bool CCECAdapterMessageQueueEntry::MessageReceived(const CCECAdapterMessage &message)
+{
+  bool bHandled(false);
+
+  if (IsResponse(message))
+  {
+    switch (message.Message())
+    {
+    case MSGCODE_COMMAND_ACCEPTED:
+      bHandled = MessageReceivedCommandAccepted(message);
+      break;
+    case MSGCODE_TRANSMIT_SUCCEEDED:
+      bHandled = MessageReceivedTransmitSucceeded(message);
+      break;
+    default:
+      bHandled = MessageReceivedResponse(message);
+      break;
+    }
+  }
+
+  return bHandled;
+}
+
+void CCECAdapterMessageQueueEntry::Signal(void)
+{
+  CLockObject lock(m_mutex);
+  m_bSucceeded = true;
+  m_condition.Signal();
+}
+
+bool CCECAdapterMessageQueueEntry::Wait(uint32_t iTimeout)
+{
+  bool bReturn(false);
+  /* wait until we receive a signal when the tranmission succeeded */
+  {
+    CLockObject lock(m_mutex);
+    bReturn = m_bSucceeded ? true : m_condition.Wait(m_mutex, m_bSucceeded, iTimeout);
+    m_bWaiting = false;
+  }
+  return bReturn;
+}
+
+bool CCECAdapterMessageQueueEntry::IsWaiting(void)
+{
+  CLockObject lock(m_mutex);
+  return m_bWaiting;
+}
+
+cec_adapter_messagecode CCECAdapterMessageQueueEntry::MessageCode(void)
+{
+  return m_message->Message();
+}
+
+bool CCECAdapterMessageQueueEntry::IsResponse(const CCECAdapterMessage &msg)
+{
+  cec_adapter_messagecode msgCode = msg.Message();
+  return msgCode == MessageCode() ||
+         msgCode == MSGCODE_TIMEOUT_ERROR ||
+         msgCode == MSGCODE_COMMAND_ACCEPTED ||
+         msgCode == MSGCODE_COMMAND_REJECTED ||
+         (m_message->IsTranmission() && msgCode == MSGCODE_HIGH_ERROR) ||
+         (m_message->IsTranmission() && msgCode == MSGCODE_LOW_ERROR) ||
+         (m_message->IsTranmission() && msgCode == MSGCODE_RECEIVE_FAILED) ||
+         (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_LINE) ||
+         (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_ACK) ||
+         (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA) ||
+         (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE) ||
+         (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_SUCCEEDED);
+}
+
+const char *CCECAdapterMessageQueueEntry::ToString(void) const
+{
+  /* CEC transmissions got the 'set ack polarity' msgcode, which doesn't look nice */
+  if (m_message->IsTranmission())
+    return "CEC transmission";
+  else
+    return CCECAdapterMessage::ToString(m_message->Message());
+}
+
+bool CCECAdapterMessageQueueEntry::MessageReceivedCommandAccepted(const CCECAdapterMessage &message)
+{
+  bool bSendSignal(false);
+  bool bHandled(false);
+  {
+    CLockObject lock(m_mutex);
+    if (m_iPacketsLeft > 0)
+    {
+      /* decrease by 1 */
+      m_iPacketsLeft--;
+
+      /* log this message */
+      CStdString strLog;
+      strLog.Format("%s - command accepted", ToString());
+      if (m_iPacketsLeft > 0)
+        strLog.AppendFormat(" - waiting for %d more", m_iPacketsLeft);
+      CLibCEC::AddLog(CEC_LOG_DEBUG, strLog);
+
+      /* no more packets left and not a transmission, so we're done */
+      if (!m_message->IsTranmission() && m_iPacketsLeft == 0)
+      {
+        m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED;
+        m_message->response = message.packet;
+        bSendSignal = true;
+      }
+      bHandled = true;
+    }
+  }
+
+  if (bSendSignal)
+    Signal();
+
+  return bHandled;
+}
+
+bool CCECAdapterMessageQueueEntry::MessageReceivedTransmitSucceeded(const CCECAdapterMessage &message)
+{
+  {
+    CLockObject lock(m_mutex);
+    if (m_iPacketsLeft == 0)
+    {
+      /* transmission succeeded, so we're done */
+      CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - transmit succeeded", ToString());
+      m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED;
+      m_message->response = message.packet;
+    }
+    else
+    {
+      /* 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_message->state = ADAPTER_MESSAGE_STATE_ERROR;
+    }
+  }
+
+  Signal();
+
+  return true;
+}
+
+bool CCECAdapterMessageQueueEntry::MessageReceivedResponse(const CCECAdapterMessage &message)
+{
+  {
+    CLockObject lock(m_mutex);
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - received response", ToString());
+    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;
+    else
+      m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED;
+  }
+
+  Signal();
+
+  return true;
+}
+
+
+CCECAdapterMessageQueue::~CCECAdapterMessageQueue(void)
+{
+  Clear();
+}
+
+void CCECAdapterMessageQueue::Clear(void)
+{
+  CLockObject lock(m_mutex);
+  m_messages.clear();
+}
+
+void CCECAdapterMessageQueue::MessageReceived(const CCECAdapterMessage &msg)
+{
+  bool bHandled(false);
+  CLockObject lock(m_mutex);
+  /* send the received message to each entry in the queue until it is handled */
+  for (map<uint64_t, CCECAdapterMessageQueueEntry *>::iterator it = m_messages.begin(); !bHandled && it != m_messages.end(); it++)
+    bHandled = it->second->MessageReceived(msg);
+
+  if (!bHandled)
+  {
+    /* the message wasn't handled */
+    bool bIsError(m_com->HandlePoll(msg));
+    CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
+
+    /* push this message to the current frame */
+    if (!bIsError && msg.PushToCecCommand(m_currentCECFrame))
+    {
+      /* and push the current frame back over the callback method when a full command was received */
+      if (m_com->IsInitialised())
+        m_com->m_callback->OnCommandReceived(m_currentCECFrame);
+
+      /* clear the current frame */
+      m_currentCECFrame.Clear();
+    }
+  }
+}
+
+void CCECAdapterMessageQueue::AddData(uint8_t *data, size_t iLen)
+{
+  for (size_t iPtr = 0; iPtr < iLen; iPtr++)
+  {
+    bool bFullMessage(false);
+    {
+      CLockObject lock(m_mutex);
+      bFullMessage = m_incomingAdapterMessage.PushReceivedByte(data[iPtr]);
+    }
+
+    if (bFullMessage)
+    {
+      /* a full message was received */
+      CCECAdapterMessage newMessage;
+      newMessage.packet = m_incomingAdapterMessage.packet;
+      MessageReceived(newMessage);
+
+      /* clear the current message */
+      CLockObject lock(m_mutex);
+      m_incomingAdapterMessage.Clear();
+    }
+  }
+}
+
+bool CCECAdapterMessageQueue::Write(CCECAdapterMessage *msg)
+{
+  msg->state = ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT;
+
+  /* set the correct line timeout */
+  if (msg->IsTranmission())
+  {
+    if (msg->tries == 1)
+      m_com->SetLineTimeout(msg->lineTimeout);
+    else
+      m_com->SetLineTimeout(msg->retryTimeout);
+  }
+
+  CCECAdapterMessageQueueEntry *entry(NULL);
+  uint64_t iEntryId(0);
+  /* add to the wait for ack queue */
+  if (msg->Message() != MSGCODE_START_BOOTLOADER)
+  {
+    CLockObject lock(m_mutex);
+    entry = new CCECAdapterMessageQueueEntry(msg);
+    iEntryId = m_iNextMessage++;
+    m_messages.insert(make_pair(iEntryId, entry));
+  }
+
+  /* TODO write the message async */
+  if (!m_com->WriteToDevice(msg))
+  {
+    /* error! */
+    Clear();
+    return false;
+  }
+
+  bool bReturn(true);
+  if (entry)
+  {
+    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()));
+      msg->state = ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
+      bReturn = false;
+    }
+
+    CLockObject lock(m_mutex);
+    m_messages.erase(iEntryId);
+  }
+
+  return bReturn;
+}
diff --git a/src/lib/adapter/USBCECAdapterMessageQueue.h b/src/lib/adapter/USBCECAdapterMessageQueue.h
new file mode 100644 (file)
index 0000000..9c5be49
--- /dev/null
@@ -0,0 +1,171 @@
+#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 "USBCECAdapterMessage.h"
+#include <map>
+
+namespace CEC
+{
+  class CUSBCECAdapterCommunication;
+
+  class CCECAdapterMessageQueueEntry
+  {
+  public:
+    CCECAdapterMessageQueueEntry(CCECAdapterMessage *message);
+    virtual ~CCECAdapterMessageQueueEntry(void);
+
+    /*!
+     * @brief Signal waiting threads
+     */
+    void Broadcast(void);
+
+    /*!
+     * @brief Called when a message was received.
+     * @param message The message that was received.
+     * @return True when this message was handled by this entry, false otherwise.
+     */
+    bool MessageReceived(const CCECAdapterMessage &message);
+
+    /*!
+     * @brief Wait for a response to this command.
+     * @param iTimeout The timeout to use while waiting.
+     * @return True when a response was received before the timeout passed, false otherwise.
+     */
+    bool Wait(uint32_t iTimeout);
+
+    /*!
+     * @return True while a thread is waiting for a signal or isn't waiting yet, false otherwise.
+     */
+    bool IsWaiting(void);
+
+    /*!
+     * @return The msgcode of the command that was sent.
+     */
+    cec_adapter_messagecode MessageCode(void);
+
+    /*!
+     * @brief Check whether a message is a response to this command.
+     * @param msg The message to check.
+     * @return True when it's a response, false otherwise.
+     */
+    bool IsResponse(const CCECAdapterMessage &msg);
+
+    /*!
+     * @return The command that was sent in human readable form.
+     */
+    const char *ToString(void) const;
+
+  private:
+    /*!
+     * @brief Called when a 'command accepted' message was received.
+     * @param message The message that was received.
+     * @return True when the message was handled, false otherwise.
+     */
+    bool MessageReceivedCommandAccepted(const CCECAdapterMessage &message);
+
+    /*!
+     * @brief Called when a 'transmit succeeded' message was received.
+     * @param message The message that was received.
+     * @return True when the message was handled, false otherwise.
+     */
+    bool MessageReceivedTransmitSucceeded(const CCECAdapterMessage &message);
+
+    /*!
+     * @brief Called when a message that's not a 'command accepted' or 'transmit succeeded' message was received.
+     * @param message The message that was received.
+     * @return True when the message was handled, false otherwise.
+     */
+    bool MessageReceivedResponse(const CCECAdapterMessage &message);
+
+    /*!
+     * @brief Signals the waiting thread.
+     */
+    void Signal(void);
+
+    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 */
+    bool                       m_bWaiting;     /**< true while a thread is waiting or when it hasn't started waiting yet */
+    PLATFORM::CCondition<bool> m_condition;    /**< the condition to wait on */
+    PLATFORM::CMutex           m_mutex;        /**< mutex for changes to this class */
+  };
+
+  class CCECAdapterMessageQueue
+  {
+    friend class CUSBCECAdapterCommunication;
+
+  public:
+    /*!
+     * @brief Create a new message queue.
+     * @param com The communication handler callback to use.
+     * @param iQueueSize The outgoing message queue size.
+     */
+    CCECAdapterMessageQueue(CUSBCECAdapterCommunication *com) :
+      m_com(com),
+      m_iNextMessage(0) {}
+    virtual ~CCECAdapterMessageQueue(void);
+
+    /*!
+     * @brief Signal and delete everything in the queue
+     */
+    void Clear(void);
+
+    /*!
+     * @brief Called when a message was received from the adapter.
+     * @param msg The message that was received.
+     */
+    void MessageReceived(const CCECAdapterMessage &msg);
+
+    /*!
+     * @brief Adds received data to the current frame.
+     * @param data The data to add.
+     * @param iLen The length of the data to add.
+     */
+    void AddData(uint8_t *data, size_t iLen);
+
+    /*!
+     * @brief Transmit a command to the adapter and wait for a response.
+     * @param msg The command to send.
+     * @return True when written, false otherwise.
+     */
+    bool Write(CCECAdapterMessage *msg);
+
+  private:
+    CUSBCECAdapterCommunication *                          m_com;                    /**< the communication handler */
+    PLATFORM::CMutex                                       m_mutex;                  /**< mutex for changes to this class */
+    std::map<uint64_t, CCECAdapterMessageQueueEntry *>     m_messages;               /**< the outgoing message queue */
+    uint64_t                                               m_iNextMessage;           /**< the index of the next message */
+    CCECAdapterMessage                                     m_incomingAdapterMessage; /**< the current incoming message that's being assembled */
+    cec_command                                            m_currentCECFrame;        /**< the current incoming CEC command that's being assembled */
+  };
+}
index 845cb07d6426af2ab801fa7ba674a439c7628517..cba201b678c86424271236753d5fec67a9ae943c 100644 (file)
@@ -88,7 +88,7 @@ bool CCECBusDevice::HandleCommand(const cec_command &command)
     m_iLastActive = GetTimeMs();
 
     /* don't call GetStatus() here, just read the value with the mutex locked */
-    if (m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
+    if (m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC && command.opcode_set == 1)
       m_deviceStatus = CEC_DEVICE_STATUS_PRESENT;
 
     MarkBusy();
@@ -170,7 +170,7 @@ cec_version CCECBusDevice::GetCecVersion(bool bUpdate /* = false */)
   return m_cecVersion;
 }
 
-bool CCECBusDevice::RequestCecVersion(void)
+bool CCECBusDevice::RequestCecVersion(bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
@@ -180,7 +180,7 @@ bool CCECBusDevice::RequestCecVersion(void)
     MarkBusy();
     CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting CEC version of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
 
-    bReturn = m_handler->TransmitRequestCecVersion(GetMyLogicalAddress(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestCecVersion(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
@@ -211,7 +211,7 @@ cec_menu_language &CCECBusDevice::GetMenuLanguage(bool bUpdate /* = false */)
   return m_menuLanguage;
 }
 
-bool CCECBusDevice::RequestMenuLanguage(void)
+bool CCECBusDevice::RequestMenuLanguage(bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
@@ -220,7 +220,7 @@ bool CCECBusDevice::RequestMenuLanguage(void)
   {
     MarkBusy();
     CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting menu language of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestMenuLanguage(GetMyLogicalAddress(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestMenuLanguage(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
@@ -263,7 +263,7 @@ CStdString CCECBusDevice::GetOSDName(bool bUpdate /* = false */)
   return m_strDeviceName;
 }
 
-bool CCECBusDevice::RequestOSDName(void)
+bool CCECBusDevice::RequestOSDName(bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
@@ -272,15 +272,15 @@ bool CCECBusDevice::RequestOSDName(void)
   {
     MarkBusy();
     CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting OSD name of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestOSDName(GetMyLogicalAddress(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestOSDName(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
 }
 
-uint16_t CCECBusDevice::GetPhysicalAddress(bool bUpdate /* = false */)
+uint16_t CCECBusDevice::GetPhysicalAddress(bool bUpdate /* = false */, bool bSuppressPoll /* = false */)
 {
-  bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
+  bool bIsPresent(GetStatus(false, bSuppressPoll) == CEC_DEVICE_STATUS_PRESENT);
   bool bRequestUpdate(false);
   {
     CLockObject lock(m_mutex);
@@ -299,7 +299,7 @@ uint16_t CCECBusDevice::GetPhysicalAddress(bool bUpdate /* = false */)
   return m_iPhysicalAddress;
 }
 
-bool CCECBusDevice::RequestPhysicalAddress(void)
+bool CCECBusDevice::RequestPhysicalAddress(bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
@@ -307,7 +307,7 @@ bool CCECBusDevice::RequestPhysicalAddress(void)
   {
     MarkBusy();
     CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting physical address of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestPhysicalAddress(GetMyLogicalAddress(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestPhysicalAddress(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
@@ -336,7 +336,7 @@ cec_power_status CCECBusDevice::GetPowerStatus(bool bUpdate /* = false */)
   return m_powerStatus;
 }
 
-bool CCECBusDevice::RequestPowerStatus(void)
+bool CCECBusDevice::RequestPowerStatus(bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
@@ -345,7 +345,7 @@ bool CCECBusDevice::RequestPowerStatus(void)
   {
     MarkBusy();
     CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting power status of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestPowerStatus(GetMyLogicalAddress(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestPowerStatus(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
     MarkReady();
   }
   return bReturn;
@@ -368,18 +368,19 @@ cec_vendor_id CCECBusDevice::GetVendorId(bool bUpdate /* = false */)
   return m_vendor;
 }
 
-bool CCECBusDevice::RequestVendorId(void)
+bool CCECBusDevice::RequestVendorId(bool bWaitForResponse /* = true */)
 {
   bool bReturn(false);
 
-  if (!MyLogicalAddressContains(m_iLogicalAddress))
+  if (!MyLogicalAddressContains(m_iLogicalAddress) && GetMyLogicalAddress() != CECDEVICE_UNKNOWN)
   {
     MarkBusy();
     CLibCEC::AddLog(CEC_LOG_NOTICE, "<< requesting vendor ID of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
-    bReturn = m_handler->TransmitRequestVendorId(GetMyLogicalAddress(), m_iLogicalAddress);
+    bReturn = m_handler->TransmitRequestVendorId(GetMyLogicalAddress(), m_iLogicalAddress, bWaitForResponse);
     MarkReady();
 
-    ReplaceHandler(true);
+    if (bWaitForResponse)
+      ReplaceHandler(true);
   }
   return bReturn;
 }
@@ -455,7 +456,7 @@ bool CCECBusDevice::NeedsPoll(void)
   return bSendPoll;
 }
 
-cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */)
+cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bool bSuppressPoll /* = false */)
 {
   cec_bus_device_status status(CEC_DEVICE_STATUS_UNKNOWN);
   bool bNeedsPoll(false);
@@ -463,8 +464,8 @@ cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */)
   {
     CLockObject lock(m_mutex);
     status = m_deviceStatus;
-    bNeedsPoll = (m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC &&
-        (m_deviceStatus == CEC_DEVICE_STATUS_UNKNOWN || bForcePoll));
+    bNeedsPoll = !bSuppressPoll &&
+        (bForcePoll || m_deviceStatus == CEC_DEVICE_STATUS_UNKNOWN);
   }
 
   if (bNeedsPoll)
@@ -567,44 +568,49 @@ bool CCECBusDevice::TryLogicalAddress(void)
 
 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));
-    m_iStreamPath      = 0;
-    m_powerStatus      = CEC_POWER_STATUS_UNKNOWN;
-    m_vendor           = CEC_VENDOR_UNKNOWN;
-    m_menuState        = CEC_MENU_STATE_ACTIVATED;
-    m_bActiveSource    = false;
-    m_iLastActive      = 0;
-    m_cecVersion       = CEC_VERSION_UNKNOWN;
-    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));
-    m_iStreamPath      = 0;
-    m_powerStatus      = CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON;
-    m_vendor           = CEC_VENDOR_UNKNOWN;
-    m_menuState        = CEC_MENU_STATE_ACTIVATED;
-    m_bActiveSource    = false;
-    m_iLastActive      = 0;
-    m_cecVersion       = CEC_VERSION_1_3A;
-    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));
-    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));
-    m_deviceStatus = newStatus;
-    break;
+  {
+    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));
+      m_iStreamPath      = 0;
+      m_powerStatus      = CEC_POWER_STATUS_UNKNOWN;
+      m_vendor           = CEC_VENDOR_UNKNOWN;
+      m_menuState        = CEC_MENU_STATE_ACTIVATED;
+      m_bActiveSource    = false;
+      m_iLastActive      = 0;
+      m_cecVersion       = CEC_VERSION_UNKNOWN;
+      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));
+      m_iStreamPath      = 0;
+      m_powerStatus      = CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON;
+      m_vendor           = CEC_VENDOR_UNKNOWN;
+      m_menuState        = CEC_MENU_STATE_ACTIVATED;
+      m_bActiveSource    = false;
+      m_iLastActive      = 0;
+      m_cecVersion       = CEC_VERSION_1_3A;
+      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));
+      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));
+      m_deviceStatus = newStatus;
+      break;
+    }
   }
+
+  if (newStatus == CEC_DEVICE_STATUS_PRESENT)
+    RequestVendorId(false);
 }
 
 void CCECBusDevice::SetPhysicalAddress(uint16_t iNewAddress)
@@ -625,6 +631,21 @@ void CCECBusDevice::SetStreamPath(uint16_t iNewAddress, uint16_t iOldAddress /*
     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;
 
+    // suppress polls when searching for a device
+    CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress, false, true);
+    if (device)
+    {
+      // if a device is found with the new physical address, mark it as active, which will automatically mark all other devices as inactive
+      device->SetActiveSource();
+    }
+    else
+    {
+      // try to find the device with the old address, and mark it as inactive when found
+      device = m_processor->GetDeviceByPhysicalAddress(iOldAddress, false, true);
+      if (device)
+        device->SetInactiveSource();
+    }
+
     if (iNewAddress > 0)
     {
       lock.Unlock();
@@ -869,6 +890,35 @@ bool CCECBusDevice::TransmitPhysicalAddress(void)
   return bReturn;
 }
 
+bool CCECBusDevice::TransmitSetMenuLanguage(cec_logical_address dest)
+{
+  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];
+  }
+
+  MarkBusy();
+  if (lang[0] == '?' && lang[1] == '?' && lang[2] == '?')
+  {
+      CLibCEC::AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> %s (%X): Menu language feature abort", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest);
+      m_processor->TransmitAbort(dest, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
+      bReturn = true;
+  }
+  else
+  {
+      CLibCEC::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;
+}
+
 bool CCECBusDevice::TransmitPoll(cec_logical_address dest)
 {
   bool bReturn(false);
@@ -984,9 +1034,19 @@ bool CCECBusDevice::ActivateSource(void)
   return bReturn;
 }
 
-void CCECBusDevice::HandlePoll(cec_logical_address iDestination)
+void CCECBusDevice::HandlePoll(cec_logical_address destination)
+{
+  if (destination >= 0 && destination < CECDEVICE_BROADCAST)
+  {
+    CCECBusDevice *device = m_processor->m_busDevices[destination];
+    if (device)
+      device->HandlePollFrom(m_iLogicalAddress);
+  }
+}
+
+void CCECBusDevice::HandlePollFrom(cec_logical_address initiator)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "<< POLL: %s (%x) -> %s (%x)", ToString(m_iLogicalAddress), m_iLogicalAddress, ToString(iDestination), iDestination);
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "<< POLL: %s (%x) -> %s (%x)", ToString(initiator), initiator, ToString(m_iLogicalAddress), m_iLogicalAddress);
   m_bAwaitingReceiveFailed = true;
 }
 
index 30ac2dc788df51c50251eaae0c8a4488799ccc03..2bd97302f1e047fbc9a2060c0284b768291acb24 100644 (file)
@@ -63,18 +63,19 @@ namespace CEC
     virtual cec_logical_address   GetMyLogicalAddress(void) const;
     virtual uint16_t              GetMyPhysicalAddress(void) const;
     virtual CStdString            GetOSDName(bool bUpdate = false);
-    virtual uint16_t              GetPhysicalAddress(bool bUpdate = false);
+    virtual uint16_t              GetPhysicalAddress(bool bUpdate = false, bool bSuppressPoll = false);
     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);
+    virtual cec_bus_device_status GetStatus(bool bForcePoll = false, bool bSuppressPoll = false);
     virtual bool                  IsActiveSource(void) const { return m_bActiveSource; }
     virtual bool                  IsUnsupportedFeature(cec_opcode opcode) const;
     virtual void                  SetUnsupportedFeature(cec_opcode opcode);
-    virtual void                  HandlePoll(cec_logical_address initiator);
+    virtual void                  HandlePoll(cec_logical_address destination);
+    virtual void                  HandlePollFrom(cec_logical_address initiator);
     virtual bool                  HandleReceiveFailed(void);
 
     virtual void SetInactiveSource(void);
@@ -100,6 +101,7 @@ namespace CEC
     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);
@@ -113,12 +115,12 @@ namespace CEC
     void MarkBusy(void);
     void MarkReady(void);
 
-    bool RequestCecVersion(void);
-    bool RequestMenuLanguage(void);
-    bool RequestPowerStatus(void);
-    bool RequestVendorId(void);
-    bool RequestPhysicalAddress(void);
-    bool RequestOSDName(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);
 
index f8a8ade3c421aa12d00f781b03978eb1706dc9ba..182f0acc76444d10769062f8413cac767b3f5cad 100644 (file)
@@ -59,6 +59,9 @@ bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command)
         CEC_USER_CONTROL_CODE_AN_RETURN :
         CEC_USER_CONTROL_CODE_EXIT;
       break;
+    case CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST:
+      key.keycode = CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST;
+      break;
     default:
       break;
     }
@@ -95,3 +98,15 @@ bool CANCommandHandler::HandleCommand(const cec_command &command)
 
   return bHandled;
 }
+
+bool CANCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
+{
+  if (iDestination == CECDEVICE_AUDIOSYSTEM)
+  {
+    /* Samsung AVR devices need to be woken up with key CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION */
+    return TransmitKeypress(iInitiator, iDestination, CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION) &&
+        TransmitKeyRelease(iInitiator, iDestination);
+  }
+
+  return CCECCommandHandler::PowerOn(iInitiator, iDestination);
+}
index 23b9de930b9434984def115a0f920ccd12220b7b..d9437d13a1e6529c364bf5f932e05bc32cfc44bd 100644 (file)
@@ -44,5 +44,6 @@ namespace CEC
     virtual bool HandleCommand(const cec_command &command);
   protected:
     virtual bool HandleVendorRemoteButtonDown(const cec_command &command);
+    virtual bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination);
   };
 };
index a9b4c40c57f2c7f483b9e1036ee60357a233fc88..eed60878d8231244346e80756ef45f39081f9d5d 100644 (file)
@@ -61,6 +61,9 @@ CCECCommandHandler::~CCECCommandHandler(void)
 
 bool CCECCommandHandler::HandleCommand(const cec_command &command)
 {
+  if (command.opcode_set == 0)
+    return HandlePoll(command);
+
   bool bHandled(true);
 
   CLibCEC::AddCommand(command);
@@ -80,6 +83,10 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command)
     if (m_processor->IsInitialised())
       HandleGivePhysicalAddress(command);
     break;
+  case CEC_OPCODE_GET_MENU_LANGUAGE:
+    if (m_processor->IsInitialised())
+      HandleGiveMenuLanguage(command);
+    break;
   case CEC_OPCODE_GIVE_OSD_NAME:
     if (m_processor->IsInitialised())
       HandleGiveOSDName(command);
@@ -338,6 +345,18 @@ bool CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command)
   return false;
 }
 
+bool CCECCommandHandler::HandleGiveMenuLanguage(const cec_command &command)
+{
+  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
+  {
+    CCECBusDevice *device = GetDevice(command.destination);
+    if (device)
+      return device->TransmitSetMenuLanguage(command.initiator);
+  }
+
+  return false;
+}
+
 bool CCECCommandHandler::HandleGiveSystemAudioModeStatus(const cec_command &command)
 {
   if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
@@ -371,6 +390,12 @@ bool CCECCommandHandler::HandleMenuRequest(const cec_command &command)
   return false;
 }
 
+bool CCECCommandHandler::HandlePoll(const cec_command &command)
+{
+  m_busDevice->HandlePoll(command.destination);
+  return true;
+}
+
 bool CCECCommandHandler::HandleReportAudioStatus(const cec_command &command)
 {
   if (command.parameters.size == 1)
@@ -582,37 +607,49 @@ bool CCECCommandHandler::HandleTextViewOn(const cec_command &command)
 
 bool CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
 {
-  if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination) && command.parameters.size > 0)
+  if (m_processor->IsRunning() &&
+      m_busDevice->MyLogicalAddressContains(command.destination) &&
+      command.parameters.size > 0)
   {
     CLibCEC::AddKey();
-
     if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
+      CLibCEC::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)
     {
-      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;
+
+      // CEC_USER_CONTROL_CODE_POWER operates as a toggle
+      // assume CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION does not
+      if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER)
       {
-        CCECBusDevice *device = GetDevice(command.destination);
-        if (device)
-        {
-          device->SetPowerStatus(CEC_POWER_STATUS_ON);
-          if (device->MyLogicalAddressContains(device->GetLogicalAddress()))
-          {
-            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);
-          }
-        }
+        cec_power_status status = device->GetPowerStatus();
+        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);
       }
       else
       {
-        CLibCEC::SetCurrentButton((cec_user_control_code) command.parameters[0]);
+        device->SetInactiveSource();
+        device->TransmitInactiveSource();
+        device->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
       }
-      return true;
     }
+
+    return true;
   }
   return false;
 }
@@ -732,52 +769,52 @@ bool CCECCommandHandler::TransmitStandby(const cec_logical_address iInitiator, c
   return Transmit(command, false);
 }
 
-bool CCECCommandHandler::TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination)
+bool CCECCommandHandler::TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
 {
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_CEC_VERSION);
 
-  return Transmit(command, true, CEC_OPCODE_CEC_VERSION);
+  return Transmit(command, bWaitForResponse, CEC_OPCODE_CEC_VERSION);
 }
 
-bool CCECCommandHandler::TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination)
+bool CCECCommandHandler::TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
 {
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_MENU_LANGUAGE);
 
-  return Transmit(command, true, CEC_OPCODE_SET_MENU_LANGUAGE);
+  return Transmit(command, bWaitForResponse, CEC_OPCODE_SET_MENU_LANGUAGE);
 }
 
-bool CCECCommandHandler::TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination)
+bool CCECCommandHandler::TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
 {
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_OSD_NAME);
 
-  return Transmit(command, true, CEC_OPCODE_SET_OSD_NAME);
+  return Transmit(command, bWaitForResponse, CEC_OPCODE_SET_OSD_NAME);
 }
 
-bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination)
+bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
 {
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_PHYSICAL_ADDRESS);
 
-  return Transmit(command, true, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
+  return Transmit(command, bWaitForResponse, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
 }
 
-bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination)
+bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
 {
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_POWER_STATUS);
 
-  return Transmit(command, true, CEC_OPCODE_REPORT_POWER_STATUS);
+  return Transmit(command, bWaitForResponse, CEC_OPCODE_REPORT_POWER_STATUS);
 }
 
-bool CCECCommandHandler::TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination)
+bool CCECCommandHandler::TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
 {
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
 
-  return Transmit(command, true, CEC_OPCODE_DEVICE_VENDOR_ID);
+  return Transmit(command, bWaitForResponse, CEC_OPCODE_DEVICE_VENDOR_ID);
 }
 
 bool CCECCommandHandler::TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress)
@@ -854,6 +891,17 @@ bool CCECCommandHandler::TransmitPhysicalAddress(const cec_logical_address iInit
   return Transmit(command, false);
 }
 
+bool CCECCommandHandler::TransmitSetMenuLanguage(const cec_logical_address iInitiator, const char lang[3])
+{
+  cec_command command;
+  command.Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_SET_MENU_LANGUAGE);
+  command.parameters.PushBack((uint8_t) lang[0]);
+  command.parameters.PushBack((uint8_t) lang[1]);
+  command.parameters.PushBack((uint8_t) lang[2]);
+
+  return Transmit(command, false);
+}
+
 bool CCECCommandHandler::TransmitPoll(const cec_logical_address iInitiator, const cec_logical_address iDestination)
 {
   cec_command command;
@@ -951,9 +999,14 @@ bool CCECCommandHandler::Transmit(cec_command &command, bool bExpectResponse /*
   bool bReturn(false);
   command.transmit_timeout = m_iTransmitTimeout;
 
+  if (command.initiator == CECDEVICE_UNKNOWN)
+  {
+    CLibCEC::AddLog(CEC_LOG_ERROR, "not transmitting a command without a valid initiator");
+    return bReturn;
+  }
+
   {
     uint8_t iTries(0), iMaxTries(command.opcode == CEC_OPCODE_NONE ? 1 : m_iTransmitRetries + 1);
-    CLockObject writeLock(m_processor->m_transmitMutex);
     while (!bReturn && ++iTries <= iMaxTries)
     {
       if ((bReturn = m_processor->Transmit(command)) == true)
index 4cfb81fbd1510d932777b4e9a10c17554ce3aa3a..93892709ff0fa01534b5d4c7c24b518d6f0387bc 100644 (file)
@@ -132,12 +132,12 @@ namespace CEC
     virtual bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination);
     virtual bool TransmitImageViewOn(const cec_logical_address iInitiator, const cec_logical_address iDestination);
     virtual bool TransmitStandby(const cec_logical_address iInitiator, const cec_logical_address iDestination);
-    virtual bool TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination);
-    virtual bool TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination);
-    virtual bool TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination);
-    virtual bool TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination);
-    virtual bool TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination);
-    virtual bool TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination);
+    virtual bool TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true);
+    virtual bool TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true);
+    virtual bool TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true);
+    virtual bool TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true);
+    virtual bool TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true);
+    virtual bool TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true);
     virtual bool TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress);
     virtual bool TransmitCECVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_version cecVersion);
     virtual bool TransmitInactiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress);
@@ -145,6 +145,7 @@ namespace CEC
     virtual bool TransmitOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, CStdString strDeviceName);
     virtual bool TransmitOSDString(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_display_control duration, const char *strMessage);
     virtual bool TransmitPhysicalAddress(const cec_logical_address iInitiator, uint16_t iPhysicalAddress, cec_device_type type);
+    virtual bool TransmitSetMenuLanguage(const cec_logical_address iInitiator, const char lang[3]);
     virtual bool TransmitPoll(const cec_logical_address iInitiator, const cec_logical_address iDestination);
     virtual bool TransmitPowerState(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_power_status state);
     virtual bool TransmitVendorID(const cec_logical_address iInitiator, uint64_t iVendorId);
@@ -171,9 +172,11 @@ namespace CEC
     virtual bool HandleGiveDeviceVendorId(const cec_command &command);
     virtual bool HandleGiveOSDName(const cec_command &command);
     virtual bool HandleGivePhysicalAddress(const cec_command &command);
+    virtual bool HandleGiveMenuLanguage(const cec_command &command);
     virtual bool HandleGiveSystemAudioModeStatus(const cec_command &command);
     virtual bool HandleImageViewOn(const cec_command &command);
     virtual bool HandleMenuRequest(const cec_command &command);
+    virtual bool HandlePoll(const cec_command &command);
     virtual bool HandleReportAudioStatus(const cec_command &command);
     virtual bool HandleReportPhysicalAddress(const cec_command &command);
     virtual bool HandleReportPowerStatus(const cec_command &command);
index 692a5fd824b51f722e1f89b728660bfa65c8eb9b..957d296a8a8733dfbf6ec8bf3931ffab67a92ddb 100644 (file)
@@ -152,7 +152,7 @@ bool CSLCommandHandler::HandleDeviceVendorId(const cec_command &command)
   {
     cec_command response;
     cec_command::Format(response, m_processor->GetLogicalAddress(), command.initiator, CEC_OPCODE_FEATURE_ABORT);
-    return Transmit(response);
+    return Transmit(response, false);
   }
   return true;
 }
@@ -411,10 +411,14 @@ bool CSLCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_
   {
     /* LG devices only allow themselves to be woken up by the TV with a vendor command */
     cec_command command;
+
+    if (!m_bSLEnabled)
+      TransmitVendorID(CECDEVICE_TV, CEC_VENDOR_LG);
+
     cec_command::Format(command, CECDEVICE_TV, iDestination, CEC_OPCODE_VENDOR_COMMAND);
     command.PushBack(SL_COMMAND_POWER_ON);
     command.PushBack(0);
-    return Transmit(command);
+    return Transmit(command, false);
   }
 
   return CCECCommandHandler::PowerOn(iInitiator, iDestination);
index fc28fc9017235fcb4804797dbf217cd2da8897c3..8b56731adfa2918f2a290e71dd4acc62ce2b7709 100644 (file)
@@ -52,8 +52,8 @@ namespace PLATFORM
     #ifdef __APPLE__
     struct timeval tv;
     gettimeofday(&tv, NULL);
-    now.tv_sec  = tv.tv_sec + 0;
-    now.tv_nsec = 0;
+    now.tv_sec  = tv.tv_sec;
+    now.tv_nsec = tv.tv_usec * 1000;
     #else
     clock_gettime(CLOCK_REALTIME, &now);
     #endif
index 631e96508e963a9cfb1b88184e463fc80527d837..b89b9cc2a57617f15ae69ddc5d9d65274cdd737c 100644 (file)
  */
 
 #define _FILE_OFFSET_BITS 64
+#include <unistd.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/time.h>
-#ifndef __APPLE__
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
 #include <sys/prctl.h>
 #endif
 #include <pthread.h>
index 5557064555be90200cc2eeb7873522f6c1de9359..cefc21201ef03ee41d17feea11e5cdc3aac9e529 100644 (file)
@@ -37,7 +37,7 @@
 #include "../util/baudrate.h"
 #include "../posix/os-socket.h"
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__FreeBSD__)
 #ifndef XCASE
 #define XCASE  0
 #endif
@@ -53,29 +53,32 @@ using namespace PLATFORM;
 
 void CSerialSocket::Close(void)
 {
-  SocketClose(m_socket);
+  if (IsOpen())
+    SocketClose(m_socket);
 }
 
 void CSerialSocket::Shutdown(void)
 {
-  SocketClose(m_socket);
+  if (IsOpen())
+    SocketClose(m_socket);
 }
 
 ssize_t CSerialSocket::Write(void* data, size_t len)
 {
-  return SocketWrite(m_socket, &m_iError, data, len);
+  return IsOpen() ? SocketWrite(m_socket, &m_iError, data, len) : -1;
 }
 
 ssize_t CSerialSocket::Read(void* data, size_t len, uint64_t iTimeoutMs /* = 0 */)
 {
-  return SocketRead(m_socket, &m_iError, data, len, iTimeoutMs);
+  return IsOpen() ? SocketRead(m_socket, &m_iError, data, len, iTimeoutMs) : -1;
 }
 
 //setting all this stuff up is a pain in the ass
 bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */)
 {
   iTimeoutMs = 0;
-  CLockObject lock(m_mutex);
+  if (IsOpen())
+    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)
index c74cc05d38ec967dfef8923af15ff466158beb09..eed05a001761f6add5dc1a0de1475ba5bb9dc240 100644 (file)
@@ -71,13 +71,16 @@ namespace PLATFORM
     public:
       CSerialSocket(const CStdString &strName, uint32_t iBaudrate, SerialDataBits iDatabits = SERIAL_DATA_BITS_EIGHT, SerialStopBits iStopbits = SERIAL_STOP_BITS_ONE, SerialParity iParity = SERIAL_PARITY_NONE) :
           CCommonSocket<serial_socket_t>(INVALID_SERIAL_SOCKET_VALUE, strName),
+          #ifdef __WINDOWS__
+          m_iCurrentReadTimeout(MAXDWORD),
+          #endif
           m_bIsOpen(false),
           m_iBaudrate(iBaudrate),
           m_iDatabits(iDatabits),
           m_iStopbits(iStopbits),
           m_iParity(iParity) {}
 
-      virtual ~CSerialSocket(void) {}
+      virtual ~CSerialSocket(void) { Close(); }
 
       virtual bool Open(uint64_t iTimeoutMs = 0);
       virtual void Close(void);
@@ -96,6 +99,9 @@ namespace PLATFORM
     protected:
   #ifndef __WINDOWS__
       struct termios  m_options;
+  #else
+      bool SetTimeouts(serial_socket_t socket, int* iError, DWORD iTimeoutMs);
+      DWORD           m_iCurrentReadTimeout;
   #endif
 
       bool            m_bIsOpen;
index c730da8256743c8132f7ef69279a365aa892854d..97774b3b1ee28289952bf8af4321d891398925e8 100644 (file)
@@ -45,7 +45,7 @@ namespace PLATFORM
 
     virtual ~CThread(void)
     {
-      StopThread();
+      StopThread(0);
     }
 
     static void *ThreadHandler(CThread *thread)
index 56ffd6450225384359ce1cb4a159d784d3fb624e..8777661df1c4e6942f937905126b868c2d2cce1f 100644 (file)
@@ -90,6 +90,18 @@ namespace PLATFORM
         return bReturn;
       }
 
+      bool Peek(_BType &entry)
+      {
+        bool bReturn(false);
+        CLockObject lock(m_mutex);
+        if (!m_buffer.empty())
+        {
+          entry = m_buffer.front();
+          bReturn = true;
+        }
+        return bReturn;
+      }
+
     private:
       size_t             m_maxSize;
       std::queue<_BType> m_buffer;
index b886f88a1cd58511b5cf03d9088af3456f6d54ac..5f2d27aeec3f46914e0a5b3388df50a2278f28df 100644 (file)
@@ -78,17 +78,14 @@ namespace PLATFORM
   inline int64_t GetTimeMs()
   {
   #if defined(__APPLE__)
-    return (int64_t) (CVGetCurrentHostTime() * 1000 / CVGetHostClockFrequency());
+    return (int64_t) (CVGetCurrentHostTime() / (int64_t)(CVGetHostClockFrequency() * 0.001));
   #elif defined(__WINDOWS__)
-    time_t rawtime;
-    time(&rawtime);
-
     LARGE_INTEGER tickPerSecond;
     LARGE_INTEGER tick;
     if (QueryPerformanceFrequency(&tickPerSecond))
     {
       QueryPerformanceCounter(&tick);
-      return (int64_t) (tick.QuadPart / 1000.);
+      return (int64_t) (tick.QuadPart / (tickPerSecond.QuadPart / 1000.));
     }
     return -1;
   #else
diff --git a/src/lib/platform/windows/os-threads.cpp b/src/lib/platform/windows/os-threads.cpp
new file mode 100644 (file)
index 0000000..7c06d41
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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 "../os.h"
+#include "os-threads.h"
+using namespace PLATFORM;
+
+static ConditionArg                     g_InitializeConditionVariable;
+static ConditionArg                     g_WakeConditionVariable;
+static ConditionArg                     g_WakeAllConditionVariable;
+static ConditionMutexArg                g_SleepConditionVariableCS;
+
+// check whether vista+ conditions are available at runtime
+static bool CheckVistaConditionFunctions(void)
+{
+  static int iHasVistaConditionFunctions(-1);
+  if (iHasVistaConditionFunctions == -1)
+  {
+    HMODULE handle = GetModuleHandle("Kernel32");
+    if (handle == NULL)
+    {
+      iHasVistaConditionFunctions = 0;
+    }
+    else
+    {
+      g_InitializeConditionVariable = (ConditionArg)     GetProcAddress(handle,"InitializeConditionVariable");
+      g_WakeConditionVariable       = (ConditionArg)     GetProcAddress(handle,"WakeConditionVariable");
+      g_WakeAllConditionVariable    = (ConditionArg)     GetProcAddress(handle,"WakeAllConditionVariable");
+      g_SleepConditionVariableCS    = (ConditionMutexArg)GetProcAddress(handle,"SleepConditionVariableCS");
+
+      // 1 when everything is resolved, 0 otherwise
+      iHasVistaConditionFunctions = g_InitializeConditionVariable &&
+                                    g_WakeConditionVariable &&
+                                    g_WakeAllConditionVariable &&
+                                    g_SleepConditionVariableCS ? 1 : 0;
+    }
+  }
+  return iHasVistaConditionFunctions == 1;
+}
+
+CConditionImpl::CConditionImpl(void)
+{
+  m_bOnVista = CheckVistaConditionFunctions();
+  if (m_bOnVista)
+    (*g_InitializeConditionVariable)(m_conditionVista = new CONDITION_VARIABLE);
+  else
+    m_conditionPreVista = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+CConditionImpl::~CConditionImpl(void)
+{
+  if (m_bOnVista)
+    delete m_conditionVista;
+  else
+    ::CloseHandle(m_conditionPreVista);
+}
+
+void CConditionImpl::Signal(void)
+{
+  if (m_bOnVista)
+    (*g_WakeConditionVariable)(m_conditionVista);
+  else
+    ::SetEvent(m_conditionPreVista);
+}
+
+void CConditionImpl::Broadcast(void)
+{
+  if (m_bOnVista)
+    (*g_WakeAllConditionVariable)(m_conditionVista);
+  else
+    ::SetEvent(m_conditionPreVista);
+}
+
+bool CConditionImpl::Wait(mutex_t &mutex)
+{
+  if (m_bOnVista)
+  {
+    return ((*g_SleepConditionVariableCS)(m_conditionVista, mutex, INFINITE) ? true : false);
+  }
+  else
+  {
+    ::ResetEvent(m_conditionPreVista);
+    MutexUnlock(mutex);
+    DWORD iWaitReturn = ::WaitForSingleObject(m_conditionPreVista, 1000);
+    MutexLock(mutex);
+    return (iWaitReturn == 0);
+  }
+}
+
+bool CConditionImpl::Wait(mutex_t &mutex, uint32_t iTimeoutMs)
+{
+  if (iTimeoutMs == 0)
+    return Wait(mutex);
+
+  if (m_bOnVista)
+  {
+    return ((*g_SleepConditionVariableCS)(m_conditionVista, mutex, iTimeoutMs) ? true : false);
+  }
+  else
+  {
+    ::ResetEvent(m_conditionPreVista);
+    MutexUnlock(mutex);
+    DWORD iWaitReturn = ::WaitForSingleObject(m_conditionPreVista, iTimeoutMs);
+    MutexLock(mutex);
+    return (iWaitReturn == 0);
+  }
+}
index e082691e24582b1afb6208488d8aa8118ecf8680..3714c16711950919edd768708e5d2cee4fa48594 100644 (file)
@@ -47,109 +47,16 @@ namespace PLATFORM
   // windows vista+ conditions
   typedef VOID (WINAPI *ConditionArg)     (CONDITION_VARIABLE*);
   typedef BOOL (WINAPI *ConditionMutexArg)(CONDITION_VARIABLE*, CRITICAL_SECTION*, DWORD);
-  static ConditionArg                     g_InitializeConditionVariable;
-  static ConditionArg                     g_WakeConditionVariable;
-  static ConditionArg                     g_WakeAllConditionVariable;
-  static ConditionMutexArg                g_SleepConditionVariableCS;
-
-  // check whether vista+ conditions are available at runtime  
-  static bool CheckVistaConditionFunctions(void)
-  {
-    static int iHasVistaConditionFunctions(-1);
-    if (iHasVistaConditionFunctions == -1)
-    {
-      HMODULE handle = GetModuleHandle("Kernel32");
-      if (handle == NULL)
-      {
-        iHasVistaConditionFunctions = 0;
-      }
-      else
-      {
-        g_InitializeConditionVariable = (ConditionArg)     GetProcAddress(handle,"InitializeConditionVariable");
-        g_WakeConditionVariable       = (ConditionArg)     GetProcAddress(handle,"WakeConditionVariable");
-        g_WakeAllConditionVariable    = (ConditionArg)     GetProcAddress(handle,"WakeAllConditionVariable");
-        g_SleepConditionVariableCS    = (ConditionMutexArg)GetProcAddress(handle,"SleepConditionVariableCS");
-
-        // 1 when everything is resolved, 0 otherwise
-        iHasVistaConditionFunctions = g_InitializeConditionVariable &&
-                                      g_WakeConditionVariable &&
-                                      g_WakeAllConditionVariable &&
-                                      g_SleepConditionVariableCS ? 1 : 0;
-      }
-    }
-    return iHasVistaConditionFunctions == 1;
-  }
 
   class CConditionImpl
   {
   public:
-    CConditionImpl(void)
-    {
-      m_bOnVista = CheckVistaConditionFunctions();
-      if (m_bOnVista)
-        (*g_InitializeConditionVariable)(m_conditionVista = new CONDITION_VARIABLE);
-      else
-        m_conditionPreVista = ::CreateEvent(NULL, TRUE, FALSE, NULL);
-    }
-
-    virtual ~CConditionImpl(void)
-    {
-      if (m_bOnVista)
-        delete m_conditionVista;
-      else
-        ::CloseHandle(m_conditionPreVista);
-    }
-
-    void Signal(void)
-    {
-      if (m_bOnVista)
-        (*g_WakeConditionVariable)(m_conditionVista);
-      else
-        ::SetEvent(m_conditionPreVista);
-    }
-
-    void Broadcast(void)
-    {
-      if (m_bOnVista)
-        (*g_WakeAllConditionVariable)(m_conditionVista);
-      else
-        ::SetEvent(m_conditionPreVista);
-    }
-
-    bool Wait(mutex_t &mutex)
-    {
-      if (m_bOnVista)
-      {
-        return ((*g_SleepConditionVariableCS)(m_conditionVista, mutex, INFINITE) ? true : false);
-      }
-      else
-      {
-        ::ResetEvent(m_conditionPreVista);
-        MutexUnlock(mutex);
-        DWORD iWaitReturn = ::WaitForSingleObject(m_conditionPreVista, 1000);
-        MutexLock(mutex);
-        return (iWaitReturn == 0);
-      }
-    }
-
-    bool Wait(mutex_t &mutex, uint32_t iTimeoutMs)
-    {
-      if (iTimeoutMs == 0)
-        return Wait(mutex);
-
-      if (m_bOnVista)
-      {
-        return ((*g_SleepConditionVariableCS)(m_conditionVista, mutex, iTimeoutMs) ? true : false);
-      }
-      else
-      {
-        ::ResetEvent(m_conditionPreVista);
-        MutexUnlock(mutex);
-        DWORD iWaitReturn = ::WaitForSingleObject(m_conditionPreVista, iTimeoutMs);
-        MutexLock(mutex);
-        return (iWaitReturn == 0);
-      }
-    }
+    CConditionImpl(void);
+    virtual ~CConditionImpl(void);
+    void Signal(void);
+    void Broadcast(void);
+    bool Wait(mutex_t &mutex);
+    bool Wait(mutex_t &mutex, uint32_t iTimeoutMs);
 
     bool                m_bOnVista;
     CONDITION_VARIABLE *m_conditionVista;
index 680b1f0426174729827e1b85c1ca4e75e136922f..96e743f80e0759fba864a6fa9ad171128a0bd850 100644 (file)
@@ -48,28 +48,25 @@ void FormatWindowsError(int iErrorCode, CStdString &strMessage)
   }
 }
 
-bool SetTimeouts(serial_socket_t socket, int* iError, bool bBlocking)
+bool CSerialSocket::SetTimeouts(serial_socket_t socket, int* iError, DWORD iTimeoutMs)
 {
   if (socket == INVALID_HANDLE_VALUE)
          return false;
 
-  COMMTIMEOUTS cto;
-  if (!GetCommTimeouts(socket, &cto))
-  {
-    *iError = GetLastError();
-    return false;
-  }
+  if (iTimeoutMs == m_iCurrentReadTimeout)
+    return true;
 
-  if (bBlocking)
+  COMMTIMEOUTS cto;
+  if (iTimeoutMs == 0)
   {
-    cto.ReadIntervalTimeout         = 0;
+    cto.ReadIntervalTimeout         = MAXDWORD;
     cto.ReadTotalTimeoutConstant    = 0;
     cto.ReadTotalTimeoutMultiplier  = 0;
   }
   else
   {
-    cto.ReadIntervalTimeout         = MAXDWORD;
-    cto.ReadTotalTimeoutConstant    = 0;
+    cto.ReadIntervalTimeout         = 0;
+    cto.ReadTotalTimeoutConstant    = iTimeoutMs;
     cto.ReadTotalTimeoutMultiplier  = 0;
   }
 
@@ -78,33 +75,50 @@ bool SetTimeouts(serial_socket_t socket, int* iError, bool bBlocking)
     *iError = GetLastError();
     return false;
   }
+  else
+  {
+    m_iCurrentReadTimeout = iTimeoutMs;
+  }
 
   return true;
 }
 
 void CSerialSocket::Close(void)
 {
-  SerialSocketClose(m_socket);
+  if (IsOpen())
+    SerialSocketClose(m_socket);
+  m_socket = INVALID_SERIAL_SOCKET_VALUE;
 }
 
 void CSerialSocket::Shutdown(void)
 {
-  SerialSocketClose(m_socket);
+  if (IsOpen())
+    SerialSocketClose(m_socket);
+  m_socket = INVALID_SERIAL_SOCKET_VALUE;
 }
 
 ssize_t CSerialSocket::Write(void* data, size_t len)
 {
-  return SerialSocketWrite(m_socket, &m_iError, data, len);
+  return IsOpen() ? SerialSocketWrite(m_socket, &m_iError, data, len) : -1;
 }
 
 ssize_t CSerialSocket::Read(void* data, size_t len, uint64_t iTimeoutMs /* = 0 */)
 {
-  return SerialSocketRead(m_socket, &m_iError, data, len, iTimeoutMs);
+  DWORD dwTimeoutMs((DWORD)iTimeoutMs);
+  if (iTimeoutMs != (uint64_t)iTimeoutMs)
+    dwTimeoutMs = MAXDWORD;
+
+  return IsOpen() && SetTimeouts(m_socket, &m_iError, dwTimeoutMs) ?
+    SerialSocketRead(m_socket, &m_iError, data, len, iTimeoutMs) :
+    -1;
 }
 
 bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */)
 {
   iTimeoutMs = 0;
+  if (IsOpen())
+    return false;
+
   CStdString strComPath = "\\\\.\\" + m_strName;
   CLockObject lock(m_mutex);
   m_socket = CreateFile(strComPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
@@ -146,7 +160,7 @@ bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */)
     return false;
   }
 
-  if (!SetTimeouts(m_socket, &m_iError, false))
+  if (!SetTimeouts(m_socket, &m_iError, 0))
   {
     m_strError = "unable to set timeouts";
     FormatWindowsError(GetLastError(), m_strError);
index 2b2c54dc772fc4e99b12d9cf4244efa3ea7986fc..c47017d328b661d978cc359c767dbb84fbff92c1 100644 (file)
@@ -181,6 +181,7 @@ void EnableCallbacks(ICECAdapter *adapter)
   g_callbacks.CBCecKeyPress   = &CecKeyPress;
   g_callbacks.CBCecCommand    = &CecCommand;
   g_callbacks.CBCecConfigurationChanged = NULL;
+  g_callbacks.CBCecAlert      = NULL;
   adapter->EnableCallbacks(NULL, &g_callbacks);
 }
 
@@ -194,9 +195,22 @@ void ListDevices(ICECAdapter *parser)
   }
   else
   {
-    PrintToStdOut("Found devices: %d\n", iDevicesFound);
+    CStdString strDeviceInfo;
+    strDeviceInfo.Format("Found devices: %d\n\n", iDevicesFound);
+
     for (int8_t iDevicePtr = 0; iDevicePtr < iDevicesFound; iDevicePtr++)
-      PrintToStdOut("device:        %d\npath:          %s\ncom port:      %s\n", iDevicePtr + 1, devices[iDevicePtr].path, devices[iDevicePtr].comm);
+    {
+      strDeviceInfo.AppendFormat("device:             %d\ncom port:           %s\n", iDevicePtr + 1, devices[iDevicePtr].comm);
+      libcec_configuration config;
+      config.Clear();
+
+      if (!parser->GetDeviceInformation(devices[iDevicePtr].comm, &config))
+        PrintToStdOut("WARNING: unable to open the device on port %s", devices[iDevicePtr].comm);
+      else
+        strDeviceInfo.AppendFormat("firmware version:   %d\n", config.iFirmwareVersion);
+      strDeviceInfo.append("\n");
+    }
+    PrintToStdOut(strDeviceInfo.c_str());
   }
 }
 
@@ -214,11 +228,13 @@ void ShowHelpCommandLine(const char* strExec)
       "  -b --base {int}             The logical address of the device to with this " << endl <<
       "                              adapter is connected." << endl <<
       "  -f --log-file {file}        Writes all libCEC log message to a file" << endl <<
+      "  -r --rom                    Read persisted settings from the EEPROM" << endl <<
       "  -sf --short-log-file {file} Writes all libCEC log message without timestamps" << endl <<
       "                              and log levels to a file." << endl <<
       "  -d --log-level {level}      Sets the log level. See cectypes.h for values." << endl <<
       "  -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 <<
       "  [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 <<
@@ -1008,12 +1024,35 @@ bool ProcessCommandLineArguments(int argc, char *argv[])
       {
         if (argc >= iArgPtr + 2)
         {
-          g_config.iHDMIPort = (int8_t)atoi(argv[iArgPtr + 1]);
+          uint8_t hdmiport = (int8_t)atoi(argv[iArgPtr + 1]);
+          if (hdmiport < 1)
+              hdmiport = 1;
+          if (hdmiport > 15)
+              hdmiport = 15;
+          g_config.iHDMIPort = hdmiport;
           cout << "using HDMI port '" << (int)g_config.iHDMIPort << "'" << endl;
           ++iArgPtr;
         }
         ++iArgPtr;
       }
+      else if (!strcmp(argv[iArgPtr], "-r") ||
+               !strcmp(argv[iArgPtr], "--rom"))
+      {
+        cout << "using settings from EEPROM" << endl;
+        g_config.bGetSettingsFromROM = 1;
+        ++iArgPtr;
+      }
+      else if (!strcmp(argv[iArgPtr], "-o") ||
+               !strcmp(argv[iArgPtr], "--osd-name"))
+      {
+        if (argc >= iArgPtr + 2)
+        {
+          snprintf(g_config.strDeviceName, 13, "%s", argv[iArgPtr + 1]);
+          cout << "using osd name " << g_config.strDeviceName << endl;
+          ++iArgPtr;
+        }
+        ++iArgPtr;
+      }
       else
       {
         g_strPort = argv[iArgPtr++];
@@ -1028,12 +1067,12 @@ int main (int argc, char *argv[])
 {
   g_config.Clear();
   snprintf(g_config.strDeviceName, 13, "CECTester");
-  g_config.callbackParam      = NULL;
-  g_config.clientVersion      = CEC_CLIENT_VERSION_1_5_0;
-  g_callbacks.CBCecLogMessage = &CecLogMessage;
-  g_callbacks.CBCecKeyPress   = &CecKeyPress;
-  g_callbacks.CBCecCommand    = &CecCommand;
-  g_config.callbacks          = &g_callbacks;
+  g_config.callbackParam       = NULL;
+  g_config.clientVersion       = CEC_CLIENT_VERSION_1_6_0;
+  g_callbacks.CBCecLogMessage  = &CecLogMessage;
+  g_callbacks.CBCecKeyPress    = &CecKeyPress;
+  g_callbacks.CBCecCommand     = &CecCommand;
+  g_config.callbacks           = &g_callbacks;
 
   if (!ProcessCommandLineArguments(argc, argv))
     return 0;
index 86d17cf8fae16864a44983a8bcca462dfd621244..153d9535caa2ca054cb2b2666f9ee78439c2cb3f 100644 (file)
@@ -26,6 +26,7 @@ IF EXIST "..\support\private\create-cat.cmd" (
 
 :CREATEINSTALLER
 echo. Creating the installer
+cd ..\project
 %NSIS% /V1 /X"SetCompressor /FINAL lzma" "p8-usbcec-driver.nsi"
 
 IF NOT EXIST "..\build\p8-usbcec-driver-installer.exe" GOTO :ERRORCREATINGINSTALLER
similarity index 92%
rename from support/create-installer.cmd
rename to support/create-installer.bat
index 8c37a8f93a8317d4559782fb3bd64441c578c12c..73b7a0f1341a5c9fc803a7f6aa20bd109a739503 100644 (file)
@@ -1,5 +1,7 @@
 @echo off
 
+set EXITCODE=1
+
 rem Check for NSIS
 IF EXIST "%ProgramFiles%\NSIS\makensis.exe" (
   set NSIS="%ProgramFiles%\NSIS\makensis.exe"
@@ -7,10 +9,6 @@ IF EXIST "%ProgramFiles%\NSIS\makensis.exe" (
   set NSIS="%ProgramFiles(x86)%\NSIS\makensis.exe"
 ) ELSE GOTO NONSIS
 
-rem Check for the Windows DDK
-IF NOT EXIST "C:\WinDDK\7600.16385.1" GOTO NODDK
-set DDK="C:\WinDDK\7600.16385.1"
-
 rem Check for VC10
 IF "%VS100COMNTOOLS%"=="" (
   set COMPILER10="%ProgramFiles%\Microsoft Visual Studio 10.0\Common7\IDE\VCExpress.exe"
@@ -26,6 +24,10 @@ mkdir ..\build
 IF EXIST "..\support\p8-usbcec-driver-installer.exe" (
   copy "..\support\p8-usbcec-driver-installer.exe" "..\build\."
 ) ELSE (
+  rem Check for the Windows DDK
+  IF NOT EXIST "C:\WinDDK\7600.16385.1" GOTO NODDK
+  set DDK="C:\WinDDK\7600.16385.1"
+
   call create-driver-installer.cmd
 )
 
@@ -127,8 +129,14 @@ IF EXIST "..\support\private\sign-binary.cmd" (
   CALL ..\support\private\sign-binary.cmd ..\build\libCEC-installer.exe
 )
 
-echo. The installer can be found here: ..\build\libCEC-installer.exe
+IF "%1%"=="" (
+  echo. The installer can be found here: ..\build\libCEC-installer.exe
+) ELSE (
+  move ..\build\libCEC-installer.exe ..\build\libCEC-%1%-installer.exe
+  echo. The installer can be found here: ..\build\libCEC-%1%-installer.exe
+)
 
+set EXITCODE=0
 GOTO EXIT
 
 :NOSDK10
@@ -155,4 +163,6 @@ del /q /f ..\build\*.lib
 del /q /f ..\build\*.exp
 del /s /f /q ..\build\x64
 rmdir ..\build\x64
-cd ..\support
\ No newline at end of file
+cd ..\support
+
+exit %EXITCODE%
diff --git a/support/p8-usbcec-driver-installer.exe b/support/p8-usbcec-driver-installer.exe
new file mode 100644 (file)
index 0000000..13596e7
Binary files /dev/null and b/support/p8-usbcec-driver-installer.exe differ