Merge branch 'master' into release
authorLars Op den Kamp <lars@opdenkamp.eu>
Tue, 1 Nov 2011 22:08:52 +0000 (23:08 +0100)
committerLars Op den Kamp <lars@opdenkamp.eu>
Tue, 1 Nov 2011 22:09:35 +0000 (23:09 +0100)
  * interface changes:
    * added GetDevicePowerStatus()/cec_get_device_power_status()
    * added GetDeviceVendorId()/cec_get_device_vendor_id()
    * added GetDeviceMenuLanguage()/cec_get_device_menu_language()
    * added GetDeviceCecVersion()/cec_get_device_cec_version()
    * added SwitchMonitoring()/cec_switch_monitoring() to the interface. when
  monitoring is enabled, the device will only log the data it received,
  but will not respond to any message
    * removed timeout parameter in Transmit() and included the ack timeout in
  the cec_command struct
    * made the vendor id -> vendor name translation available
    * made CEC_LOG levels powers of 2
    * introduced CEC_LOG_TRAFFIC log level
  * fixed:
    * set the correct ackmask on startup
    * wait for ack while keeping a lock
    * wait for the processor thread to start before continueing on startup
    * wait for messages to be transmitted before continueing in
  CCECProcessor::Transmit()
    * only set the logical address once when it has changed
    * correct source for broadcast messages
    * win32: create Release type installer
  * changed:
    * make all reads and write in CAdapterCommunication go through buffers.
    * poll for a vendor ID of connected devices and switch to a non-standard
  CEC implementation if needed.
    * added vendor detection of Samsung and LG devices
    * handle samsung remote command 'return'
  * cec-client:
    * added -la and --logical-address to the command line params
    * added -d and --log-level params to cec-client
    * added -sf and --short-log-file, which only log the actual messages, not
  the level and timestamp
    * added -f and --log-file parameters to cec-client
    * added option to change the log level to cec-client

41 files changed:
.gitignore
ChangeLog
README
configure.ac
debian/changelog
include/cec.h
include/cecc.h
include/cecloader.h
include/cectypes.h
project/bin/7za.exe [new file with mode: 0644]
project/bin/libeay32.dll [new file with mode: 0644]
project/bin/libiconv2.dll [new file with mode: 0644]
project/bin/libintl3.dll [new file with mode: 0644]
project/bin/libssl32.dll [new file with mode: 0644]
project/bin/wget.exe [new file with mode: 0644]
project/create-installer.cmd
project/download-deps.cmd [new file with mode: 0644]
project/libCEC.nsi
project/libcec.vcxproj
project/libcec.vcxproj.filters
project/testclient.vcxproj
src/lib/AdapterCommunication.cpp
src/lib/AdapterCommunication.h
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/devices/CECBusDevice.cpp [new file with mode: 0644]
src/lib/devices/CECBusDevice.h [new file with mode: 0644]
src/lib/implementations/ANCommandHandler.cpp [new file with mode: 0644]
src/lib/implementations/ANCommandHandler.h [new file with mode: 0644]
src/lib/implementations/CECCommandHandler.cpp [new file with mode: 0644]
src/lib/implementations/CECCommandHandler.h [new file with mode: 0644]
src/lib/implementations/SLCommandHandler.cpp [new file with mode: 0644]
src/lib/implementations/SLCommandHandler.h [new file with mode: 0644]
src/lib/platform/linux/serialport.cpp
src/lib/platform/serialport.h
src/lib/platform/windows/serialport.cpp
src/testclient/main.cpp

index 3ea373cb2ef2d708c1593be9b45c8989442090bb..e196aa93c1abec77ca760b5e01a2aad495bc0892 100644 (file)
@@ -28,6 +28,9 @@ cec-client.pdb
 
 build
 
+include/boost
+
+project/boost-1_46_1-xbmc-win32
 project/Debug/
 project/ipch/
 project/libcec.sdf
index b6fd44c6338d1f52e5adb96811eed4a92d70eee9..8b4cdf5a65c6b9648f38eff7d1fa848fc68bbafc 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+libcec (0.8-1) unstable; urgency=low
+
+  * interface changes:
+    * added GetDevicePowerStatus()/cec_get_device_power_status()
+    * added GetDeviceVendorId()/cec_get_device_vendor_id()
+    * added GetDeviceMenuLanguage()/cec_get_device_menu_language()
+    * added GetDeviceCecVersion()/cec_get_device_cec_version()
+    * added SwitchMonitoring()/cec_switch_monitoring() to the interface. when
+         monitoring is enabled, the device will only log the data it received,
+         but will not respond to any message
+    * removed timeout parameter in Transmit() and included the ack timeout in
+         the cec_command struct
+    * made the vendor id -> vendor name translation available
+    * made CEC_LOG levels powers of 2
+    * introduced CEC_LOG_TRAFFIC log level
+  * fixed:
+    * set the correct ackmask on startup
+    * wait for ack while keeping a lock
+    * wait for the processor thread to start before continueing on startup
+    * wait for messages to be transmitted before continueing in
+         CCECProcessor::Transmit()
+    * only set the logical address once when it has changed
+    * correct source for broadcast messages
+    * win32: create Release type installer
+  * changed:
+    * make all reads and write in CAdapterCommunication go through buffers.
+    * poll for a vendor ID of connected devices and switch to a non-standard
+         CEC implementation if needed.
+    * added vendor detection of Samsung and LG devices
+    * handle samsung remote command 'return'
+  * cec-client:
+    * added -la and --logical-address to the command line params
+    * added -d and --log-level params to cec-client
+    * added -sf and --short-log-file, which only log the actual messages, not
+         the level and timestamp
+    * added -f and --log-file parameters to cec-client
+    * added option to change the log level to cec-client
+
+-- Pulse-Eight Packaging <packaging@pulse-eight.com>  Tue, 01 Nov 2011 22:58:00 +0200
+
 libcec (0.7-1) unstable; urgency=low
 
   * send a keypress with 0 duration when a key is pressed and with a duration
diff --git a/README b/README
index ebc1a4bfdc720e5ef58f86367a632b3085ea92c3..c5f108074afd835b7b4e7de570c1554458246490 100644 (file)
--- a/README
+++ b/README
@@ -1,20 +1,29 @@
-For Linux:
-autoreconf -vif
-./configure --prefix=/usr
-make
-sudo make install
+This library provides support for the Pulse-Eight USB-CEC adapter.
 
-For Windows:
-Open /project/libcec.sln with Visual C++ 2010 or Visual Studio 2010.
-Build the project.
-Copy libcec.dll and libpthread.dll to your desired destination.
+To install libCEC on Linux:
+# autoreconf -vif
+# ./configure --prefix=/usr
+# make
+# sudo make install
+
+To install libCEC on Windows:
+* go to /project and execute download-deps.cmd to download Boost.
+* open /project/libcec.sln with Visual C++ 2010 or Visual Studio 2010.
+* build the project.
+* copy libcec.dll and pthreadVC2.dll to your desired destination.
+
+To build an installer on Windows:
+* download and install the Windows DDK.
+* download and install NSIS.
+* go to /project and execute download-deps.cmd to download Boost.
+* go to /project and execute create-installer.cmd to create the installer.
+* the installer is stored as /project/libCEC-installer.exe
 
 Test the device:
-Run "cec-client -h" to display the options of the test client.
+* run "cec-client -h" to display the options of the test client.
 
 For developers:
-See /include/CECExports.h
-
+* see /include/cec.h for the C++ API and /include/cecc.h for the C version.
 
 If you wish to contribute to this project, you must first sign our contributors agreement
 Please see http://www.pulse-eight.net/contributors for more information
\ No newline at end of file
index fd1eb01a2e6e807ea1611dd3694aa2769d4bd540..3285c89a3856a6580683509657070d065cf89595 100644 (file)
@@ -1,4 +1,4 @@
-AC_INIT([libcec], 0:7:0)
+AC_INIT([libcec], 0:8:0)
 AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
 
 AC_PROG_CXX
@@ -23,6 +23,13 @@ libs_pre_dl=$LIBS
   AC_SUBST([LIBS_DL])
 LIBS=$libs_pre_dl
 
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADERS([boost/shared_ptr.hpp], [],
+    [AC_MSG_ERROR(You need the Boost libraries.)])
+AC_CHECK_HEADERS([boost/enable_shared_from_this.hpp], [],
+    [AC_MSG_ERROR(You need the Boost libraries.)])
+AC_LANG_POP([C++])
+
 CXXFLAGS="-fPIC -Wall -Wextra $CXXFLAGS"
 
 AC_SUBST(REQUIRES)
index b6fd44c6338d1f52e5adb96811eed4a92d70eee9..8b4cdf5a65c6b9648f38eff7d1fa848fc68bbafc 100644 (file)
@@ -1,3 +1,43 @@
+libcec (0.8-1) unstable; urgency=low
+
+  * interface changes:
+    * added GetDevicePowerStatus()/cec_get_device_power_status()
+    * added GetDeviceVendorId()/cec_get_device_vendor_id()
+    * added GetDeviceMenuLanguage()/cec_get_device_menu_language()
+    * added GetDeviceCecVersion()/cec_get_device_cec_version()
+    * added SwitchMonitoring()/cec_switch_monitoring() to the interface. when
+         monitoring is enabled, the device will only log the data it received,
+         but will not respond to any message
+    * removed timeout parameter in Transmit() and included the ack timeout in
+         the cec_command struct
+    * made the vendor id -> vendor name translation available
+    * made CEC_LOG levels powers of 2
+    * introduced CEC_LOG_TRAFFIC log level
+  * fixed:
+    * set the correct ackmask on startup
+    * wait for ack while keeping a lock
+    * wait for the processor thread to start before continueing on startup
+    * wait for messages to be transmitted before continueing in
+         CCECProcessor::Transmit()
+    * only set the logical address once when it has changed
+    * correct source for broadcast messages
+    * win32: create Release type installer
+  * changed:
+    * make all reads and write in CAdapterCommunication go through buffers.
+    * poll for a vendor ID of connected devices and switch to a non-standard
+         CEC implementation if needed.
+    * added vendor detection of Samsung and LG devices
+    * handle samsung remote command 'return'
+  * cec-client:
+    * added -la and --logical-address to the command line params
+    * added -d and --log-level params to cec-client
+    * added -sf and --short-log-file, which only log the actual messages, not
+         the level and timestamp
+    * added -f and --log-file parameters to cec-client
+    * added option to change the log level to cec-client
+
+-- Pulse-Eight Packaging <packaging@pulse-eight.com>  Tue, 01 Nov 2011 22:58:00 +0200
+
 libcec (0.7-1) unstable; urgency=low
 
   * send a keypress with 0 duration when a key is pressed and with a duration
index c23c35a125ae83b6eeed287af8bd8a94bf4a050e..1701df786c16fe7489e356cf76e3ed157ded3f32 100644 (file)
@@ -98,7 +98,7 @@ namespace CEC
     /*!
      * @see cec_transmit
      */
-    virtual bool Transmit(const cec_command &data, bool bWaitForAck = true) = 0;
+    virtual bool Transmit(const cec_command &data) = 0;
 
     /*!
      * @see cec_set_logical_address
@@ -134,6 +134,31 @@ namespace CEC
      * @see cec_set_osd_string
      */
     virtual bool SetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage) = 0;
+
+    /*!
+     * @see cec_switch_monitoring
+     */
+    virtual bool SwitchMonitoring(bool bEnable) = 0;
+
+    /*!
+     * @see cec_get_device_cec_version
+     */
+    virtual cec_version GetDeviceCecVersion(cec_logical_address iAddress) = 0;
+
+    /*!
+     * @see cec_get_device_menu_language
+     */
+    virtual bool GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language) = 0;
+
+    /*!
+     * @see cec_get_device_vendor_id
+     */
+    virtual uint64_t GetDeviceVendorId(cec_logical_address iAddress) = 0;
+
+    /*!
+     * @see cec_get_device_power_status
+     */
+    virtual cec_power_status GetDevicePowerStatus(cec_logical_address iAddress) = 0;
   };
 };
 
index c319c196da7ab33bcda88d576f4867a12d8ab0a8..bec392030e6cd67bce5443ed4f7aa3540efdcac7 100644 (file)
@@ -175,13 +175,12 @@ extern DECLSPEC int cec_get_next_command(cec_command *command);
 /*!
  * @brief Transmit a frame on the CEC line.
  * @param data The frame to send.
- * @param bWaitForAck Wait for an ACK message for 1 second after this frame has been sent.
  * @return True when the data was sent and acked, false otherwise.
  */
 #ifdef __cplusplus
-extern DECLSPEC int cec_transmit(const CEC::cec_command &data, int bWaitForAck = 1);
+extern DECLSPEC int cec_transmit(const CEC::cec_command &data);
 #else
-extern DECLSPEC int cec_transmit(const cec_command &data, int bWaitForAck = 1);
+extern DECLSPEC int cec_transmit(const cec_command &data);
 #endif
 
 /*!
@@ -203,8 +202,10 @@ extern DECLSPEC int cec_set_logical_address(cec_logical_address myAddress, cec_l
 extern DECLSPEC int cec_set_physical_address(uint16_t iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS);
 
 /*!
- * @brief Display a message on the TV.
- * @brief The message to display.
+ * @brief Display a message on the device with the given logical address.
+ * @param iLogicalAddres The device to display the message on.
+ * @param duration The duration of the message
+ * @param strMessage The message to display.
  * @return True when the command was sent, false otherwise.
  */
 #ifdef __cplusplus
@@ -213,6 +214,58 @@ extern DECLSPEC int cec_set_osd_string(CEC::cec_logical_address iLogicalAddress,
 extern DECLSPEC int cec_set_osd_string(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage);
 #endif
 
+/*!
+ * @brief Enable or disable monitoring mode.
+ * @param bEnable True to enable, false to disable.
+ * @return True when switched successfully, false otherwise.
+ */
+extern DECLSPEC int cec_switch_monitoring(int bEnable);
+
+/*!
+ * @brief Get the CEC version of the device with the given logical address
+ * @param iLogicalAddress The device to get the CEC version for.
+ * @return The version or CEC_VERSION_UNKNOWN when the version couldn't be fetched.
+ */
+#ifdef __cplusplus
+extern DECLSPEC CEC::cec_version cec_get_device_cec_version(CEC::cec_logical_address iLogicalAddress);
+#else
+extern DECLSPEC cec_version cec_get_device_cec_version(cec_logical_address iLogicalAddress);
+#endif
+
+/*!
+ * @brief Get the menu language of the device with the given logical address
+ * @param iLogicalAddress The device to get the menu language for.
+ * @param language The requested menu language.
+ * @return True when fetched succesfully, false otherwise.
+ */
+#ifdef __cplusplus
+extern DECLSPEC int cec_get_device_menu_language(CEC::cec_logical_address iLogicalAddress, CEC::cec_menu_language *language);
+#else
+extern DECLSPEC int cec_get_device_menu_language(cec_logical_address iLogicalAddress, cec_menu_language *language);
+#endif
+
+/*!
+ * @brief Get the vendor ID of the device with the given logical address.
+ * @param iLogicalAddress The device to get the vendor id for.
+ * @return The vendor ID or 0 if it wasn't found.
+ */
+#ifdef __cplusplus
+extern DECLSPEC uint64_t cec_get_device_vendor_id(CEC::cec_logical_address iLogicalAddress);
+#else
+extern DECLSPEC uint64_t cec_get_device_vendor_id(cec_logical_address iLogicalAddress);
+#endif
+
+/*!
+ * @brief Get the power status of the device with the given logical address.
+ * @param iLogicalAddress The device to get the power status for.
+ * @return The power status or CEC_POWER_STATUS_UNKNOWN if it wasn't found.
+ */
+#ifdef __cplusplus
+extern DECLSPEC CEC::cec_power_status cec_get_device_power_status(CEC::cec_logical_address iLogicalAddress);
+#else
+extern DECLSPEC cec_power_status cec_get_device_power_status(cec_logical_address iLogicalAddress);
+#endif
+
 #ifdef __cplusplus
 };
 #endif
index 7ae8b4984f9d9ae825dc1dfffecc97273e2d7e8c..242d4fe52d9fb0cbe12cbfeeca6fa8af828d7e7b 100644 (file)
@@ -84,9 +84,9 @@ CEC::ICECAdapter *LoadLibCec(const char *strName, CEC::cec_logical_address iLogi
     if (!g_libCEC)
     {
 #if defined(__APPLE__)
-      cout << "cannot find " << (strLib ? strLib : "libcec.dylib") << endl;
+      cout << "cannot find " << (strLib ? strLib : "libcec.dylib") << dlerror() << endl;
 #else
-      cout << "cannot find " << (strLib ? strLib : "libcec.so") << endl;
+      cout << "cannot find " << (strLib ? strLib : "libcec.so") << dlerror() << endl;
 #endif
       return NULL;
     }
index 004ea060170047888c636bf8d019791e7fd24f87..5f2edc2f59a604eb01bf9065945bb1be350be150 100644 (file)
@@ -97,13 +97,14 @@ typedef enum
   CEC_TRUE = 1
 } ECecBoolean;
 
-typedef enum
+typedef enum cec_version
 {
-  CEC_VERSION_1_2 = 0x01,
-  CEC_VERSION_1_2A = 0x02,
-  CEC_VERSION_1_3 = 0x03,
-  CEC_VERSION_1_3A = 0x04
-} ECecVersion;
+  CEC_VERSION_UNKNOWN = 0x00,
+  CEC_VERSION_1_2     = 0x01,
+  CEC_VERSION_1_2A    = 0x02,
+  CEC_VERSION_1_3     = 0x03,
+  CEC_VERSION_1_3A    = 0x04
+} cec_version;
 
 typedef enum
 {
@@ -197,13 +198,14 @@ typedef enum
   CEC_PLAY_MODE_SLOW_REVERSE_MAX_SPEED = 0x1B
 } ECecPlayMode;
 
-typedef enum
+typedef enum cec_power_status
 {
   CEC_POWER_STATUS_ON = 0x00,
   CEC_POWER_STATUS_STANDBY = 0x01,
   CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON = 0x02,
-  CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY = 0x03
-} ECecPowerStatus;
+  CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY = 0x03,
+  CEC_POWER_STATUS_UNKNOWN = 0x99
+} cec_power_status;
 
 typedef enum
 {
@@ -434,6 +436,11 @@ typedef enum cec_user_control_code
   CEC_USER_CONTROL_CODE_UNKNOWN
 } cec_user_control_code;
 
+typedef enum cec_an_user_control_code
+{
+  CEC_AN_USER_CONTROL_CODE_RETURN = 0x91
+} cec_an_user_control_code;
+
 typedef enum cec_logical_address
 {
   CECDEVICE_UNKNOWN = -1, //not a valid logical address
@@ -524,12 +531,20 @@ typedef enum cec_opcode
 
 typedef enum cec_log_level
 {
-  CEC_LOG_DEBUG = 0,
-  CEC_LOG_NOTICE,
-  CEC_LOG_WARNING,
-  CEC_LOG_ERROR
+  CEC_LOG_ERROR   = 1,
+  CEC_LOG_WARNING = 2,
+  CEC_LOG_NOTICE  = 4,
+  CEC_LOG_TRAFFIC = 8,
+  CEC_LOG_DEBUG   = 16,
+  CEC_LOG_ALL     = 31
 } cec_log_level;
 
+typedef struct cec_menu_language
+{
+  char                language[4];
+  cec_logical_address device;
+} cec_menu_language;
+
 typedef struct cec_log_message
 {
   char          message[1024];
@@ -584,6 +599,15 @@ typedef struct cec_datapacket
   uint8_t size;
 
 #ifdef __cplusplus
+  cec_datapacket &operator =(const struct cec_datapacket &packet)
+  {
+    clear();
+    for (uint8_t iPtr = 0; iPtr < packet.size; iPtr++)
+      push_back(packet[iPtr]);
+
+    return *this;
+  }
+
   bool    empty(void) const             { return size == 0; }
   bool    full(void) const              { return size == 100; }
   uint8_t operator[](uint8_t pos) const { return pos < size ? data[pos] : 0; }
@@ -618,26 +642,6 @@ typedef struct cec_datapacket
 
 } cec_datapacket;
 
-typedef struct cec_adapter_message
-{
-  cec_datapacket packet;
-
-#ifdef __cplusplus
-  bool                    empty(void) const             { return packet.empty(); }
-  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; }
-  void                    clear(void)                   { packet.clear(); }
-  void                    shift(uint8_t iShiftBy)       { packet.shift(iShiftBy); }
-  void                    push_back(uint8_t add)        { packet.push_back(add); }
-  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                    eom(void) const               { return packet.size >= 1 ? (packet.at(0) & MSGCODE_FRAME_EOM) != 0 : false; }
-  bool                    ack(void) const               { return packet.size >= 1 ? (packet.at(0) & MSGCODE_FRAME_ACK) != 0 : false; }
-  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; };
-#endif
-} cec_adapter_message;
-
 typedef struct cec_command
 {
   cec_logical_address initiator;
@@ -647,15 +651,30 @@ typedef struct cec_command
   cec_opcode          opcode;
   cec_datapacket      parameters;
   int8_t              opcode_set;
+  int32_t             transmit_timeout;
 
 #ifdef __cplusplus
+  cec_command &operator =(const struct cec_command &command)
+  {
+    initiator        = command.initiator;
+    destination      = command.destination;
+    ack              = command.ack;
+    eom              = command.eom;
+    opcode           = command.opcode;
+    opcode_set       = command.opcode_set;
+    transmit_timeout = command.transmit_timeout;
+    parameters       = command.parameters;
+
+    return *this;
+  }
+
   static void format(cec_command &command, cec_logical_address initiator, cec_logical_address destination, cec_opcode opcode)
   {
     command.clear();
-    command.initiator   = initiator;
-    command.destination = destination;
-    command.opcode      = opcode;
-    command.opcode_set  = 1;
+    command.initiator        = initiator;
+    command.destination      = destination;
+    command.opcode           = opcode;
+    command.opcode_set       = 1;
   }
 
   void push_back(uint8_t data)
@@ -676,12 +695,13 @@ typedef struct cec_command
 
   void clear(void)
   {
-    initiator   = CECDEVICE_UNKNOWN;
-    destination = CECDEVICE_UNKNOWN;
-    ack         = 0;
-    eom         = 0;
-    opcode_set  = 0;
-    opcode      = CEC_OPCODE_FEATURE_ABORT;
+    initiator        = CECDEVICE_UNKNOWN;
+    destination      = CECDEVICE_UNKNOWN;
+    ack              = 0;
+    eom              = 0;
+    opcode_set       = 0;
+    opcode           = CEC_OPCODE_FEATURE_ABORT;
+    transmit_timeout = 1000;
     parameters.clear();
   };
 #endif
@@ -689,9 +709,28 @@ typedef struct cec_command
 
 typedef enum cec_vendor_id
 {
-  CEC_VENDOR_SAMSUNG = 240,
+  CEC_VENDOR_SAMSUNG = 0x00F0,
+  CEC_VENDOR_LG      = 0xE091,
   CEC_VENDOR_UNKNOWN = 0
-} vendor_id;
+} cec_vendor_id;
+
+typedef struct cec_vendor
+{
+  const char *AsString(void) const
+  {
+    switch (vendor)
+    {
+    case CEC_VENDOR_SAMSUNG:
+      return "Samsung";
+    case CEC_VENDOR_LG:
+        return "LG";
+    default:
+      return "Unknown";
+    }
+  }
+
+  cec_vendor_id vendor;
+} cec_vendor;
 
 //default physical address 1.0.0.0, HDMI port 1
 #define CEC_DEFAULT_PHYSICAL_ADDRESS 0x1000
@@ -699,8 +738,8 @@ typedef enum cec_vendor_id
 #define MSGEND                       0xFE
 #define MSGESC                       0xFD
 #define ESCOFFSET                    3
-#define CEC_MIN_VERSION              6
-#define CEC_LIB_VERSION              7
+#define CEC_MIN_VERSION              8
+#define CEC_LIB_VERSION              8
 #define CEC_BUTTON_TIMEOUT           500
 
 #ifdef __cplusplus
diff --git a/project/bin/7za.exe b/project/bin/7za.exe
new file mode 100644 (file)
index 0000000..8836e99
Binary files /dev/null and b/project/bin/7za.exe differ
diff --git a/project/bin/libeay32.dll b/project/bin/libeay32.dll
new file mode 100644 (file)
index 0000000..8d31f86
Binary files /dev/null and b/project/bin/libeay32.dll differ
diff --git a/project/bin/libiconv2.dll b/project/bin/libiconv2.dll
new file mode 100644 (file)
index 0000000..544dd92
Binary files /dev/null and b/project/bin/libiconv2.dll differ
diff --git a/project/bin/libintl3.dll b/project/bin/libintl3.dll
new file mode 100644 (file)
index 0000000..ec11e6b
Binary files /dev/null and b/project/bin/libintl3.dll differ
diff --git a/project/bin/libssl32.dll b/project/bin/libssl32.dll
new file mode 100644 (file)
index 0000000..a30ff0e
Binary files /dev/null and b/project/bin/libssl32.dll differ
diff --git a/project/bin/wget.exe b/project/bin/wget.exe
new file mode 100644 (file)
index 0000000..54b372e
Binary files /dev/null and b/project/bin/wget.exe differ
index 951b92b7896a72d10688a33b00de47aff50effad..5187528a28c53886cc1e54b24e08394d84e7a394 100644 (file)
@@ -13,11 +13,11 @@ IF "%VS100COMNTOOLS%"=="" (
 
 rem Compile libCEC
 echo Cleaning libCEC
-%COMPILER% libcec.sln /clean Debug
+%COMPILER% libcec.sln /clean Release
 
 echo Compiling libCEC
-%COMPILER% libcec.sln /clean Debug
-%COMPILER% libcec.sln /build Debug
+%COMPILER% libcec.sln /clean Release
+%COMPILER% libcec.sln /build Release
 
 rem Copy driver installer
 echo Copying driver installer
diff --git a/project/download-deps.cmd b/project/download-deps.cmd
new file mode 100644 (file)
index 0000000..a5ad0c6
--- /dev/null
@@ -0,0 +1,13 @@
+@echo off
+
+echo Downloading boost
+bin\wget.exe "http://mirrors.xbmc.org/build-deps/win32/boost-1_46_1-xbmc-win32.7z"
+
+echo Extracting boost
+bin\7za.exe x -y "boost-1_46_1-xbmc-win32.7z"
+
+echo Copying boost
+xcopy boost-1_46_1-xbmc-win32\include\* "..\include\" /E /Q /I /Y
+
+echo Cleaning up
+del boost-1_46_1-xbmc-win32.7z
index 8fb714333b346ef35a46f9540973a503f5344b04..eaf10932e36d54215ddbf196183116a5d6561b98 100644 (file)
@@ -52,7 +52,6 @@ Section "libCEC" SecLibCEC
   File "..\COPYING"
   File "..\libcec.dll"
   File "..\libcec.lib"
-  File "..\libcec.pdb"
   File "..\pthreadVC2.dll"
   File "..\README"
 
@@ -61,7 +60,7 @@ Section "libCEC" SecLibCEC
   File "..\dpinst-x86.exe"
   File "..\OEM001.inf"
   SetOutPath "$INSTDIR\include"
-  File /r /x *.so "..\include\*.*"
+  File /r /x *.so "..\include\cec*.*"
 
   ;Store installation folder
   WriteRegStr HKCU "Software\libCEC" "" $INSTDIR
index ab7e00eef4cc4a506ff728dff13017404c04c1fe..78763cef8d308d903a56765d9294c2ab4e5e7509 100644 (file)
     <ClInclude Include="..\src\lib\AdapterCommunication.h" />
     <ClInclude Include="..\src\lib\AdapterDetection.h" />
     <ClInclude Include="..\src\lib\CECProcessor.h" />
+    <ClInclude Include="..\src\lib\devices\CECBusDevice.h" />
+    <ClInclude Include="..\src\lib\implementations\ANCommandHandler.h" />
+    <ClInclude Include="..\src\lib\implementations\CECCommandHandler.h" />
+    <ClInclude Include="..\src\lib\implementations\SLCommandHandler.h" />
     <ClInclude Include="..\src\lib\LibCEC.h" />
     <ClInclude Include="..\src\lib\platform\baudrate.h" />
     <ClInclude Include="..\src\lib\platform\os-dependent.h" />
     <ClCompile Include="..\src\lib\AdapterCommunication.cpp" />
     <ClCompile Include="..\src\lib\AdapterDetection.cpp" />
     <ClCompile Include="..\src\lib\CECProcessor.cpp" />
+    <ClCompile Include="..\src\lib\devices\CECBusDevice.cpp" />
+    <ClCompile Include="..\src\lib\implementations\ANCommandHandler.cpp" />
+    <ClCompile Include="..\src\lib\implementations\CECCommandHandler.cpp" />
+    <ClCompile Include="..\src\lib\implementations\SLCommandHandler.cpp" />
     <ClCompile Include="..\src\lib\LibCEC.cpp" />
     <ClCompile Include="..\src\lib\LibCECC.cpp" />
     <ClCompile Include="..\src\lib\LibCECDll.cpp" />
       <OutputFile>$(SolutionDir)..\libcec.dll</OutputFile>
       <AdditionalDependencies>%(AdditionalDependencies);setupapi.lib;$(SolutionDir)..\src\lib\platform\pthread_win32\pthreadVC2.lib</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>libcmtd</IgnoreSpecificDefaultLibraries>
-      <Version>7</Version>
+      <Version>8</Version>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
       <OutputFile>$(SolutionDir)..\libcec.dll</OutputFile>
       <AdditionalDependencies>%(AdditionalDependencies);setupapi.lib;$(SolutionDir)..\src\lib\platform\pthread_win32\pthreadVC2.lib</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>libcmt</IgnoreSpecificDefaultLibraries>
-      <Version>7</Version>
+      <Version>8</Version>
     </Link>
   </ItemDefinitionGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
index c677d84c1ef1045bda334fca4ff4bd5d89b30490..1468823729b49dfb086503d2862c1a56ea57dc5b 100644 (file)
     <Filter Include="exports">
       <UniqueIdentifier>{01b9c84a-dcfe-4bdc-b983-69e3e3929b0f}</UniqueIdentifier>
     </Filter>
+    <Filter Include="implementations">
+      <UniqueIdentifier>{03bd59df-ccac-4664-b61b-3151bb219efa}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="devices">
+      <UniqueIdentifier>{bfc43a58-636d-4c1a-b191-486cb8509c7c}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\lib\util\buffer.h">
     <ClInclude Include="..\include\cectypes.h">
       <Filter>exports</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\lib\implementations\ANCommandHandler.h">
+      <Filter>implementations</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\lib\implementations\CECCommandHandler.h">
+      <Filter>implementations</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\lib\implementations\SLCommandHandler.h">
+      <Filter>implementations</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\lib\devices\CECBusDevice.h">
+      <Filter>devices</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\src\lib\AdapterCommunication.cpp" />
     <ClCompile Include="..\src\lib\platform\windows\serialport.cpp">
       <Filter>platform</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\lib\implementations\ANCommandHandler.cpp">
+      <Filter>implementations</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\lib\implementations\CECCommandHandler.cpp">
+      <Filter>implementations</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\lib\implementations\SLCommandHandler.cpp">
+      <Filter>implementations</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\lib\devices\CECBusDevice.cpp">
+      <Filter>devices</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
index 01335de7d416aa7a8b1006bb9f430820635b21cc..68434c8dbbc4a4b756d68c5cbd81e0a1d70ec0db 100644 (file)
@@ -63,7 +63,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>$(ProjectDir)..\src\lib\platform\pthread_win32\pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>$(SolutionDir)..\cec-client.exe</OutputFile>
-      <Version>7</Version>
+      <Version>8</Version>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -85,7 +85,7 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalDependencies>$(ProjectDir)..\src\lib\platform\pthread_win32\pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <Version>7</Version>
+      <Version>8</Version>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
index f880d982e80175a7ab54590116528b2eca81bfce..2286a01e57080e8c12663e7b6c5a48d0d628f1c0 100644 (file)
 #include "LibCEC.h"
 #include "platform/serialport.h"
 #include "util/StdString.h"
+#include "platform/timeutils.h"
 
 using namespace std;
 using namespace CEC;
 
+CCECAdapterMessage::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
+  push_back(MSGSTART);
+  push_escaped(MSGCODE_TRANSMIT_ACK_POLARITY);
+  if (command.destination == CECDEVICE_BROADCAST)
+    push_escaped(CEC_TRUE);
+  else
+    push_escaped(CEC_FALSE);
+  push_back(MSGEND);
+
+  // add source and destination
+  push_back(MSGSTART);
+  push_escaped(MSGCODE_TRANSMIT);
+  push_back(((uint8_t)command.initiator << 4) + (uint8_t)command.destination);
+  push_back(MSGEND);
+
+  // add opcode
+  push_back(MSGSTART);
+  push_escaped(command.parameters.empty() ? (uint8_t)MSGCODE_TRANSMIT_EOM : (uint8_t)MSGCODE_TRANSMIT);
+  push_back((uint8_t) command.opcode);
+  push_back(MSGEND);
+
+  // add parameters
+  for (int8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
+  {
+    push_back(MSGSTART);
+
+    if (iPtr == command.parameters.size - 1)
+      push_escaped( MSGCODE_TRANSMIT_EOM);
+    else
+      push_escaped(MSGCODE_TRANSMIT);
+
+    push_escaped(command.parameters[iPtr]);
+
+    push_back(MSGEND);
+  }
+
+  // set timeout
+  transmit_timeout = command.transmit_timeout;
+}
+
+CCECAdapterMessage &CCECAdapterMessage::operator =(const CCECAdapterMessage &msg)
+{
+  packet = msg.packet;
+  state  = msg.state;
+  return *this;
+}
+
+CStdString CCECAdapterMessage::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;
+  }
+
+  return strMsg;
+}
+
+CStdString CCECAdapterMessage::ToString(void) const
+{
+  CStdString strMsg;
+  strMsg = MessageCodeAsString();
+
+  switch (message())
+  {
+  case MSGCODE_TIMEOUT_ERROR:
+  case MSGCODE_HIGH_ERROR:
+  case MSGCODE_LOW_ERROR:
+    {
+      int 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:%i", 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(), ack() ? "high" : "low", eom() ? "eom" : "");
+    break;
+  case MSGCODE_FRAME_DATA:
+    if (size() >= 2)
+      strMsg.AppendFormat(" %02x %s", at(1), eom() ? "eom" : "");
+    break;
+  default:
+    break;
+  }
+
+  return strMsg;
+}
+
+bool CCECAdapterMessage::is_error(void) const
+{
+  cec_adapter_messagecode code = message();
+  return (code == MSGCODE_TIMEOUT_ERROR ||
+    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);
+}
+
+void CCECAdapterMessage::push_escaped(int16_t byte)
+{
+  if (byte >= MSGESC && byte != MSGSTART)
+  {
+    push_back(MSGESC);
+    push_back((uint8_t) (byte - ESCOFFSET));
+  }
+  else
+    push_back((uint8_t) byte);
+}
+
 CAdapterCommunication::CAdapterCommunication(CLibCEC *controller) :
     m_port(NULL),
-    m_controller(controller),
-    m_inbuf(NULL),
-    m_iInbufSize(0),
-    m_iInbufUsed(0)
+    m_controller(controller)
 {
   m_port = new CSerialPort;
 }
@@ -58,14 +251,11 @@ CAdapterCommunication::~CAdapterCommunication(void)
     delete m_port;
     m_port = NULL;
   }
-
-  if (m_inbuf)
-    free(m_inbuf);
 }
 
 bool CAdapterCommunication::Open(const char *strPort, uint16_t iBaudRate /* = 38400 */, uint32_t iTimeoutMs /* = 10000 */)
 {
-  CLockObject lock(&m_commMutex);
+  CLockObject lock(&m_mutex);
   if (!m_port)
   {
     m_controller->AddLog(CEC_LOG_ERROR, "port is NULL");
@@ -89,11 +279,12 @@ bool CAdapterCommunication::Open(const char *strPort, uint16_t iBaudRate /* = 38
 
   //clear any input bytes
   uint8_t buff[1024];
-  m_port->Read(buff, sizeof(buff), 50);
+  m_port->Read(buff, sizeof(buff), 500);
 
   if (CreateThread())
   {
-    m_controller->AddLog(CEC_LOG_DEBUG, "communication thread created");
+    m_startCondition.Wait(&m_mutex);
+    m_controller->AddLog(CEC_LOG_DEBUG, "communication thread started");
     return true;
   }
   else
@@ -106,33 +297,25 @@ bool CAdapterCommunication::Open(const char *strPort, uint16_t iBaudRate /* = 38
 
 void CAdapterCommunication::Close(void)
 {
-  CLockObject lock(&m_commMutex);
-  StopThread();
-
-  if (m_inbuf)
-  {
-    free(m_inbuf);
-    m_inbuf = NULL;
-    m_iInbufSize = 0;
-    m_iInbufUsed = 0;
-  }
-
+  CLockObject lock(&m_mutex);
+  m_startCondition.Broadcast();
   m_rcvCondition.Broadcast();
+  StopThread();
 }
 
 void *CAdapterCommunication::Process(void)
 {
-  m_controller->AddLog(CEC_LOG_DEBUG, "communication thread started");
+  {
+    CLockObject lock(&m_mutex);
+    m_startCondition.Signal();
+  }
 
   while (!IsStopped())
   {
-    {
-      CLockObject lock(&m_commMutex);
-      ReadFromDevice(100);
-    }
-
-    if (!IsStopped())
-      Sleep(5);
+    ReadFromDevice(500);
+    Sleep(5);
+    WriteNextCommand();
+    Sleep(5);
   }
 
   return NULL;
@@ -161,128 +344,95 @@ bool CAdapterCommunication::ReadFromDevice(uint32_t iTimeout)
 
 void CAdapterCommunication::AddData(uint8_t *data, uint8_t iLen)
 {
-  CLockObject lock(&m_bufferMutex);
-  if (m_iInbufUsed + iLen > m_iInbufSize)
-  {
-    m_iInbufSize = m_iInbufUsed + iLen;
-    m_inbuf = (uint8_t*)realloc(m_inbuf, m_iInbufSize);
-  }
-
-  memcpy(m_inbuf + m_iInbufUsed, data, iLen);
-  m_iInbufUsed += iLen;
+  CLockObject lock(&m_mutex);
+  for (unsigned int iPtr = 0; iPtr < iLen; iPtr++)
+    m_inBuffer.Push(data[iPtr]);
 
   m_rcvCondition.Signal();
 }
 
-bool CAdapterCommunication::Write(const cec_adapter_message &data)
+void CAdapterCommunication::WriteNextCommand(void)
 {
-  CLockObject lock(&m_commMutex);
-  if (m_port->Write(data) != (int32_t) data.size())
+  CCECAdapterMessagePtr msg;
+  if (m_outBuffer.Pop(msg))
   {
-    CStdString strError;
-    strError.Format("error writing to serial port: %s", m_port->GetError().c_str());
-    m_controller->AddLog(CEC_LOG_ERROR, strError);
-    return false;
+    CLockObject lock(&msg->mutex);
+    if (m_port->Write(msg) != (int32_t) msg->size())
+    {
+      CStdString strError;
+      strError.Format("error writing to serial port: %s", m_port->GetError().c_str());
+      m_controller->AddLog(CEC_LOG_ERROR, strError);
+      msg->state = ADAPTER_MESSAGE_STATE_ERROR;
+    }
+    else
+    {
+      m_controller->AddLog(CEC_LOG_DEBUG, "command sent");
+      CCondition::Sleep((uint32_t) msg->size() * 24 /*data*/ + 5 /*start bit (4.5 ms)*/ + 10);
+      msg->state = ADAPTER_MESSAGE_STATE_SENT;
+    }
+    msg->condition.Signal();
   }
+}
 
-  m_controller->AddLog(CEC_LOG_DEBUG, "command sent");
-  CCondition::Sleep((uint32_t) data.size() * (uint32_t)24 /*data*/ + (uint32_t)5 /*start bit (4.5 ms)*/);
-
+bool CAdapterCommunication::Write(CCECAdapterMessagePtr data)
+{
+  data->state = ADAPTER_MESSAGE_STATE_WAITING;
+  m_outBuffer.Push(data);
   return true;
 }
 
-bool CAdapterCommunication::Read(cec_adapter_message &msg, uint32_t iTimeout)
+bool CAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout)
 {
-  CLockObject lock(&m_bufferMutex);
+  CLockObject lock(&m_mutex);
 
-  if (m_iInbufUsed < 1)
-  {
-    if (!m_rcvCondition.Wait(&m_bufferMutex, iTimeout))
-      return false;
-  }
-
-  if (m_iInbufUsed < 1 || IsStopped())
-    return false;
+  msg.clear();
+  uint64_t iNow = GetTimeMs();
+  uint64_t iTarget = iNow + iTimeout;
+  bool bGotFullMessage(false);
+  bool bNextIsEscaped(false);
+  bool bGotStart(false);
 
-  //search for first start of message
-  int16_t startpos = -1;
-  for (int16_t iPtr = 0; iPtr < m_iInbufUsed; iPtr++)
+  while(!bGotFullMessage && iNow < iTarget)
   {
-    if (m_inbuf[iPtr] == MSGSTART)
+    uint8_t buf = 0;
+    if (!m_inBuffer.Pop(buf))
     {
-      startpos = iPtr;
-      break;
+      if (!m_rcvCondition.Wait(&m_mutex, (uint32_t) (iTarget - iNow)))
+        return false;
     }
-  }
-
-  if (startpos == -1)
-    return false;
 
-  //move anything from the first start of message to the beginning of the buffer
-  if (startpos > 0)
-  {
-    memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos);
-    m_iInbufUsed -= startpos;
-  }
-
-  if (m_iInbufUsed < 2)
-    return false;
-
-  //look for end of message
-  startpos = -1;
-  int16_t endpos = -1;
-  for (int16_t iPtr = 1; iPtr < m_iInbufUsed; iPtr++)
-  {
-    if (m_inbuf[iPtr] == MSGEND)
+    if (!bGotStart)
     {
-      endpos = iPtr;
-      break;
+      if (buf == MSGSTART)
+        bGotStart = true;
+      continue;
     }
-    else if (m_inbuf[iPtr] == MSGSTART)
+    else if (buf == MSGSTART) //we found a msgstart before msgend, this is not right, remove
     {
-      startpos = iPtr;
-      break;
+      m_controller->AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND");
+      msg.clear();
+      bGotStart = true;
     }
-  }
-
-  if (startpos > 0) //we found a msgstart before msgend, this is not right, remove
-  {
-    m_controller->AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND");
-    memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos);
-    m_iInbufUsed -= startpos;
-    return false;
-  }
 
-  if (endpos > 0) //found a MSGEND
-  {
-    msg.clear();
-    bool isesc = false;
-    for (int16_t iPtr = 1; iPtr < endpos; iPtr++)
+    if (buf == MSGEND)
     {
-      if (isesc)
-      {
-        msg.push_back(m_inbuf[iPtr] + (uint8_t)ESCOFFSET);
-        isesc = false;
-      }
-      else if (m_inbuf[iPtr] == MSGESC)
-      {
-        isesc = true;
-      }
-      else
-      {
-        msg.push_back(m_inbuf[iPtr]);
-      }
+      bGotFullMessage = true;
     }
-
-    if (endpos + 1 < m_iInbufUsed)
-      memmove(m_inbuf, m_inbuf + endpos + 1, m_iInbufUsed - endpos - 1);
-
-    m_iInbufUsed -= endpos + 1;
-
-    return true;
+    else if (bNextIsEscaped)
+    {
+      msg.push_back(buf + (uint8_t)ESCOFFSET);
+      bNextIsEscaped = false;
+    }
+    else if (buf == MSGESC)
+      bNextIsEscaped = true;
+    else
+      msg.push_back(buf);
   }
 
-  return false;
+  if (bGotFullMessage)
+    msg.state = ADAPTER_MESSAGE_STATE_RECEIVED;
+
+  return bGotFullMessage;
 }
 
 std::string CAdapterCommunication::GetError(void) const
@@ -296,12 +446,11 @@ bool CAdapterCommunication::StartBootloader(void)
     return false;
 
   m_controller->AddLog(CEC_LOG_DEBUG, "starting the bootloader");
-  cec_adapter_message output;
-  output.clear();
+  CCECAdapterMessagePtr output(new CCECAdapterMessage);
 
-  output.push_back(MSGSTART);
-  PushEscaped(output, MSGCODE_START_BOOTLOADER);
-  output.push_back(MSGEND);
+  output->push_back(MSGSTART);
+  output->push_escaped(MSGCODE_START_BOOTLOADER);
+  output->push_back(MSGEND);
 
   if (!Write(output))
   {
@@ -312,58 +461,17 @@ bool CAdapterCommunication::StartBootloader(void)
   return true;
 }
 
-void CAdapterCommunication::PushEscaped(cec_adapter_message &vec, uint8_t byte)
-{
-  if (byte >= MSGESC && byte != MSGSTART)
-  {
-    vec.push_back(MSGESC);
-    vec.push_back(byte - ESCOFFSET);
-  }
-  else
-  {
-    vec.push_back(byte);
-  }
-}
-
-bool CAdapterCommunication::SetAckMask(uint16_t iMask)
-{
-  if (!IsRunning())
-    return false;
-
-  CStdString strLog;
-  strLog.Format("setting ackmask to %2x", iMask);
-  m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
-
-  cec_adapter_message output;
-  output.clear();
-
-  output.push_back(MSGSTART);
-  PushEscaped(output, MSGCODE_SET_ACK_MASK);
-  PushEscaped(output, iMask >> 8);
-  PushEscaped(output, (uint8_t)iMask);
-  output.push_back(MSGEND);
-
-  if (!Write(output))
-  {
-    m_controller->AddLog(CEC_LOG_ERROR, "could not set the ackmask");
-    return false;
-  }
-
-  return true;
-}
-
 bool CAdapterCommunication::PingAdapter(void)
 {
   if (!IsRunning())
     return false;
 
   m_controller->AddLog(CEC_LOG_DEBUG, "sending ping");
-  cec_adapter_message output;
-  output.clear();
+  CCECAdapterMessagePtr output(new CCECAdapterMessage);
 
-  output.push_back(MSGSTART);
-  PushEscaped(output, MSGCODE_PING);
-  output.push_back(MSGEND);
+  output->push_back(MSGSTART);
+  output->push_escaped(MSGCODE_PING);
+  output->push_back(MSGEND);
 
   if (!Write(output))
   {
@@ -379,48 +487,5 @@ bool CAdapterCommunication::PingAdapter(void)
 
 bool CAdapterCommunication::IsOpen(void) const
 {
-  return !IsStopped() && m_port->IsOpen();
-}
-
-void CAdapterCommunication::FormatAdapterMessage(const cec_command &command, cec_adapter_message &packet)
-{
-  packet.clear();
-
-  //set ack polarity to high when transmitting to the broadcast address
-  //set ack polarity low when transmitting to any other address
-  packet.push_back(MSGSTART);
-  PushEscaped(packet, MSGCODE_TRANSMIT_ACK_POLARITY);
-  if (command.destination == CECDEVICE_BROADCAST)
-    PushEscaped(packet, CEC_TRUE);
-  else
-    PushEscaped(packet, CEC_FALSE);
-  packet.push_back(MSGEND);
-
-  // add source and destination
-  packet.push_back(MSGSTART);
-  PushEscaped(packet, MSGCODE_TRANSMIT);
-  packet.push_back(((uint8_t)command.initiator << 4) + (uint8_t)command.destination);
-  packet.push_back(MSGEND);
-
-  // add opcode
-  packet.push_back(MSGSTART);
-  PushEscaped(packet, command.parameters.empty() ? (uint8_t)MSGCODE_TRANSMIT_EOM : (uint8_t)MSGCODE_TRANSMIT);
-  packet.push_back((uint8_t) command.opcode);
-  packet.push_back(MSGEND);
-
-  // add parameters
-  for (int8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
-  {
-    packet.push_back(MSGSTART);
-
-    if (iPtr == command.parameters.size - 1)
-      PushEscaped(packet, MSGCODE_TRANSMIT_EOM);
-    else
-      PushEscaped(packet, MSGCODE_TRANSMIT);
-
-    PushEscaped(packet, command.parameters[iPtr]);
-
-    packet.push_back(MSGEND);
-  }
+  return !IsStopped() && m_port->IsOpen() && IsRunning();
 }
-
index fcb2817a67b3b4d7dc431bdbdbfd4010827bad3a..ef5e0aa7c2934c55649dcef5915b65130da3a52f 100644 (file)
 
 #include <cectypes.h>
 #include "platform/threads.h"
+#include "util/buffer.h"
+#include "util/StdString.h"
 #include <string>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
 
 namespace CEC
 {
+  typedef enum cec_adapter_message_state
+  {
+    ADAPTER_MESSAGE_STATE_UNKNOWN = 0,
+    ADAPTER_MESSAGE_STATE_WAITING,
+    ADAPTER_MESSAGE_STATE_SENT,
+    ADAPTER_MESSAGE_STATE_RECEIVED,
+    ADAPTER_MESSAGE_STATE_ERROR
+  } cec_adapter_message_state;
+
+
+  class CCECAdapterMessage : public boost::enable_shared_from_this<CCECAdapterMessage>
+  {
+  public:
+    CCECAdapterMessage(void) { clear(); }
+    CCECAdapterMessage(const cec_command &command);
+    CCECAdapterMessage &operator =(const CCECAdapterMessage &msg);
+    CStdString ToString(void) const;
+    CStdString MessageCodeAsString(void) const;
+
+    bool                    empty(void) const             { return packet.empty(); }
+    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; }
+    void                    clear(void)                   { state = ADAPTER_MESSAGE_STATE_UNKNOWN; transmit_timeout = 0; packet.clear(); }
+    void                    shift(uint8_t iShiftBy)       { packet.shift(iShiftBy); }
+    void                    push_back(uint8_t add)        { packet.push_back(add); }
+    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                    eom(void) const               { return packet.size >= 1 ? (packet.at(0) & MSGCODE_FRAME_EOM) != 0 : false; }
+    bool                    ack(void) const               { return packet.size >= 1 ? (packet.at(0) & MSGCODE_FRAME_ACK) != 0 : false; }
+    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; };
+    bool                    is_error(void) const;
+    void                    push_escaped(int16_t byte);
+
+    cec_datapacket            packet;
+    cec_adapter_message_state state;
+    int32_t                   transmit_timeout;
+    CMutex                    mutex;
+    CCondition                condition;
+  };
+  typedef boost::shared_ptr<CCECAdapterMessage> CCECAdapterMessagePtr;
+
   class CSerialPort;
   class CLibCEC;
 
@@ -47,8 +93,8 @@ namespace CEC
     virtual ~CAdapterCommunication();
 
     bool Open(const char *strPort, uint16_t iBaudRate = 38400, uint32_t iTimeoutMs = 10000);
-    bool Read(cec_adapter_message &msg, uint32_t iTimeout = 1000);
-    bool Write(const cec_adapter_message &frame);
+    bool Read(CCECAdapterMessage &msg, uint32_t iTimeout = 1000);
+    bool Write(CCECAdapterMessagePtr data);
     bool PingAdapter(void);
     void Close(void);
     bool IsOpen(void) const;
@@ -57,21 +103,18 @@ namespace CEC
     void *Process(void);
 
     bool StartBootloader(void);
-    bool SetAckMask(uint16_t iMask);
-    static void PushEscaped(cec_adapter_message &vec, uint8_t byte);
-    static void FormatAdapterMessage(const cec_command &command, cec_adapter_message &packet);
 
   private:
+    void WriteNextCommand(void);
     void AddData(uint8_t *data, uint8_t iLen);
     bool ReadFromDevice(uint32_t iTimeout);
 
-    CSerialPort *        m_port;
-    CLibCEC *            m_controller;
-    uint8_t*             m_inbuf;
-    int16_t              m_iInbufSize;
-    int16_t              m_iInbufUsed;
-    CMutex               m_bufferMutex;
-    CMutex               m_commMutex;
-    CCondition           m_rcvCondition;
+    CSerialPort *                    m_port;
+    CLibCEC *                        m_controller;
+    CecBuffer<uint8_t>               m_inBuffer;
+    CecBuffer<CCECAdapterMessagePtr> m_outBuffer;
+    CMutex                           m_mutex;
+    CCondition                       m_rcvCondition;
+    CCondition                       m_startCondition;
   };
 };
index 88541c01fb315821f402f3f62af1b3cc1c90d0f0..0749e5375f00c0362d1dc9a93127f49b8cfe4976 100644 (file)
@@ -33,6 +33,7 @@
 #include "CECProcessor.h"
 
 #include "AdapterCommunication.h"
+#include "devices/CECBusDevice.h"
 #include "LibCEC.h"
 #include "util/StdString.h"
 #include "platform/timeutils.h"
@@ -41,41 +42,44 @@ using namespace CEC;
 using namespace std;
 
 CCECProcessor::CCECProcessor(CLibCEC *controller, CAdapterCommunication *serComm, const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS*/) :
-    m_iPhysicalAddress(iPhysicalAddress),
     m_iLogicalAddress(iLogicalAddress),
     m_strDeviceName(strDeviceName),
     m_communication(serComm),
-    m_controller(controller)
+    m_controller(controller),
+    m_bMonitor(false)
 {
-  for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
-    m_vendorIds[iPtr] = CEC_VENDOR_UNKNOWN;
-  for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
-    m_vendorClasses[iPtr] = (uint8_t) 0;
+  for (int iPtr = 0; iPtr < 16; iPtr++)
+    m_busDevices[iPtr] = new CCECBusDevice(this, (cec_logical_address) iPtr, iPtr == iLogicalAddress ? iPhysicalAddress : 0);
 }
 
 CCECProcessor::~CCECProcessor(void)
 {
+  m_startCondition.Broadcast();
   StopThread();
   m_communication = NULL;
   m_controller = NULL;
+  for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
+    delete m_busDevices[iPtr];
 }
 
 bool CCECProcessor::Start(void)
 {
+  CLockObject lock(&m_mutex);
   if (!m_communication || !m_communication->IsOpen())
   {
     m_controller->AddLog(CEC_LOG_ERROR, "connection is closed");
     return false;
   }
 
-  if (!SetLogicalAddress(m_iLogicalAddress))
-  {
-    m_controller->AddLog(CEC_LOG_ERROR, "could not set the logical address");
-    return false;
-  }
-
   if (CreateThread())
+  {
+    if (!m_startCondition.Wait(&m_mutex))
+    {
+      m_controller->AddLog(CEC_LOG_ERROR, "could not create a processor thread");
+      return false;
+    }
     return true;
+  }
   else
     m_controller->AddLog(CEC_LOG_ERROR, "could not create a processor thread");
 
@@ -84,25 +88,31 @@ bool CCECProcessor::Start(void)
 
 void *CCECProcessor::Process(void)
 {
-  m_controller->AddLog(CEC_LOG_DEBUG, "processor thread started");
+  cec_command           command;
+  CCECAdapterMessage    msg;
 
-  cec_command command;
-  cec_adapter_message msg;
+  SetAckMask(0x1 << (uint8_t)m_iLogicalAddress);
+
+  {
+    CLockObject lock(&m_mutex);
+    m_controller->AddLog(CEC_LOG_DEBUG, "processor thread started");
+    m_startCondition.Signal();
+  }
 
   while (!IsStopped())
   {
     bool bParseFrame(false);
-    bool bError(false);
-    bool bTransmitSucceeded(false);
     command.clear();
     msg.clear();
 
     {
       CLockObject lock(&m_mutex);
       if (m_communication->IsOpen() && m_communication->Read(msg, 50))
-        ParseMessage(msg, &bError, &bTransmitSucceeded, &bParseFrame);
+      {
+        m_controller->AddLog(msg.is_error() ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
+        bParseFrame = ParseMessage(msg) && !IsStopped();
+      }
 
-      bParseFrame &= !IsStopped();
       if (bParseFrame)
         command = m_currentframe;
     }
@@ -110,58 +120,27 @@ void *CCECProcessor::Process(void)
     if (bParseFrame)
       ParseCommand(command);
 
+    Sleep(5);
+
     m_controller->CheckKeypressTimeout();
 
-    if (!IsStopped())
-      Sleep(5);
+    for (unsigned int iDevicePtr = 0; iDevicePtr < 16; iDevicePtr++)
+      m_busDevices[iDevicePtr]->PollVendorId();
+
+    Sleep(5);
   }
 
   return NULL;
 }
 
-bool CCECProcessor::PowerOnDevices(cec_logical_address address /* = CECDEVICE_TV */)
-{
-  if (!IsRunning())
-    return false;
-
-  CStdString strLog;
-  strLog.Format("<< powering on device with logical address %d", (int8_t)address);
-  m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_IMAGE_VIEW_ON);
-
-  return Transmit(command);
-}
-
-bool CCECProcessor::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
-{
-  if (!IsRunning())
-    return false;
-
-  CStdString strLog;
-  strLog.Format("<< putting device with logical address %d in standby mode", (int8_t)address);
-  m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_STANDBY);
-
-  return Transmit(command);
-}
-
 bool CCECProcessor::SetActiveView(void)
 {
   if (!IsRunning())
     return false;
 
-  m_controller->AddLog(CEC_LOG_DEBUG, "<< setting active view");
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
-  command.parameters.push_back((m_iPhysicalAddress >> 8) & 0xFF);
-  command.parameters.push_back(m_iPhysicalAddress & 0xFF);
-
-  return Transmit(command);
+  if (m_iLogicalAddress != CECDEVICE_UNKNOWN && m_busDevices[m_iLogicalAddress])
+    return m_busDevices[m_iLogicalAddress]->BroadcastActiveView();
+  return false;
 }
 
 bool CCECProcessor::SetInactiveView(void)
@@ -169,102 +148,123 @@ bool CCECProcessor::SetInactiveView(void)
   if (!IsRunning())
     return false;
 
-  m_controller->AddLog(CEC_LOG_DEBUG, "<< setting inactive view");
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, CECDEVICE_BROADCAST, CEC_OPCODE_INACTIVE_SOURCE);
-  command.parameters.push_back((m_iPhysicalAddress >> 8) & 0xFF);
-  command.parameters.push_back(m_iPhysicalAddress & 0xFF);
-
-  return Transmit(command);
+  if (m_iLogicalAddress != CECDEVICE_UNKNOWN && m_busDevices[m_iLogicalAddress])
+    return m_busDevices[m_iLogicalAddress]->BroadcastInactiveView();
+  return false;
 }
 
 void CCECProcessor::LogOutput(const cec_command &data)
 {
-  CStdString txStr = "transmit ";
-  txStr.AppendFormat(" %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
-  txStr.AppendFormat(" %02x", (uint8_t)data.opcode);
+  CStdString strTx;
+  strTx.Format("<< %02x:%02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination, (uint8_t)data.opcode);
 
   for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
-    txStr.AppendFormat(" %02x", data.parameters[iPtr]);
-  m_controller->AddLog(CEC_LOG_DEBUG, txStr.c_str());
+    strTx.AppendFormat(":%02x", data.parameters[iPtr]);
+  m_controller->AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
 }
 
-bool CCECProcessor::Transmit(const cec_command &data, bool bWaitForAck /* = true */)
+bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress)
 {
-  LogOutput(data);
+  if (m_iLogicalAddress != iLogicalAddress)
+  {
+    CStdString strLog;
+    strLog.Format("<< setting logical address to %1x", iLogicalAddress);
+    m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
+    m_iLogicalAddress = iLogicalAddress;
+    return SetAckMask(0x1 << (uint8_t)m_iLogicalAddress);
+  }
 
-  cec_adapter_message output;
-  output.clear();
-  CAdapterCommunication::FormatAdapterMessage(data, output);
+  return true;
+}
 
-  return TransmitFormatted(output, bWaitForAck);
+bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress)
+{
+  if (m_iLogicalAddress != CECDEVICE_UNKNOWN && m_busDevices[m_iLogicalAddress])
+  {
+    m_busDevices[m_iLogicalAddress]->SetPhysicalAddress(iPhysicalAddress);
+    return m_busDevices[m_iLogicalAddress]->BroadcastActiveView();
+  }
+  return false;
 }
 
-bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress)
+bool CCECProcessor::SwitchMonitoring(bool bEnable)
 {
   CStdString strLog;
-  strLog.Format("<< setting logical address to %1x", iLogicalAddress);
+  strLog.Format("== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
   m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
 
-  m_iLogicalAddress = iLogicalAddress;
-  return m_communication && m_communication->SetAckMask(0x1 << (uint8_t)m_iLogicalAddress);
+  m_bMonitor = bEnable;
+  if (bEnable)
+    return SetAckMask(0);
+  else
+    return SetAckMask(0x1 << (uint8_t)m_iLogicalAddress);
 }
 
-bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress)
+cec_version CCECProcessor::GetDeviceCecVersion(cec_logical_address iAddress)
 {
-  CStdString strLog;
-  strLog.Format("<< setting physical address to %2x", iPhysicalAddress);
-  m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
+  return m_busDevices[iAddress]->GetCecVersion();
+}
 
-  m_iPhysicalAddress = iPhysicalAddress;
-  return SetActiveView();
+bool CCECProcessor::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language)
+{
+  if (m_busDevices[iAddress])
+  {
+    *language = m_busDevices[iAddress]->GetMenuLanguage();
+    return (strcmp(language->language, "???") != 0);
+  }
+  return false;
 }
 
-bool CCECProcessor::SetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage)
+uint64_t CCECProcessor::GetDeviceVendorId(cec_logical_address iAddress)
 {
-  CStdString strLog;
-  strLog.Format("<< display message '%s'", strMessage);
-  m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
+  if (m_busDevices[iAddress])
+    return m_busDevices[iAddress]->GetVendorId();
+  return false;
+}
 
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, iLogicalAddress, CEC_OPCODE_SET_OSD_STRING);
-  command.parameters.push_back((uint8_t)duration);
+cec_power_status CCECProcessor::GetDevicePowerStatus(cec_logical_address iAddress)
+{
+  if (m_busDevices[iAddress])
+    return m_busDevices[iAddress]->GetPowerStatus();
+  return CEC_POWER_STATUS_UNKNOWN;
+}
 
-  for (unsigned int iPtr = 0; iPtr < strlen(strMessage); iPtr++)
-    command.parameters.push_back(strMessage[iPtr]);
+bool CCECProcessor::Transmit(const cec_command &data)
+{
+  LogOutput(data);
 
-  return Transmit(command);
+  CCECAdapterMessagePtr output(new CCECAdapterMessage(data));
+  return Transmit(output);
 }
 
-bool CCECProcessor::TransmitFormatted(const cec_adapter_message &data, bool bWaitForAck /* = true */)
+bool CCECProcessor::Transmit(CCECAdapterMessagePtr output)
 {
+  bool bReturn(false);
   CLockObject lock(&m_mutex);
-  if (!m_communication || !m_communication->Write(data))
-    return false;
-
-  if (bWaitForAck)
   {
-    uint64_t now = GetTimeMs();
-    uint64_t target = now + 1000;
-    bool bError(false);
-    bool bGotAck(false);
-
-    while (!bGotAck && now < target)
+    CLockObject msgLock(&output->mutex);
+    if (!m_communication || !m_communication->Write(output))
+      return bReturn;
+    else
     {
-      bGotAck = WaitForAck(&bError, (uint32_t) (target - now));
-      now = GetTimeMs();
-
-      if (bError && now < target)
+      output->condition.Wait(&output->mutex, 1000);
+      if (output->state != ADAPTER_MESSAGE_STATE_SENT)
       {
-        m_controller->AddLog(CEC_LOG_ERROR, "retransmitting previous frame");
-        if (!m_communication->Write(data))
-          return false;
+        m_controller->AddLog(CEC_LOG_ERROR, "command was not sent");
+        return bReturn;
       }
     }
+
+    if (output->transmit_timeout > 0)
+    {
+      if ((bReturn = WaitForTransmitSucceeded(output->size(), output->transmit_timeout)) == false)
+        m_controller->AddLog(CEC_LOG_ERROR, "did not receive ack");
+    }
+    else
+      bReturn = true;
   }
 
-  return true;
+  return bReturn;
 }
 
 void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
@@ -279,105 +279,18 @@ void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode
   Transmit(command);
 }
 
-void CCECProcessor::ReportCECVersion(cec_logical_address address /* = CECDEVICE_TV */)
-{
-  m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting CEC version as 1.3a");
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_CEC_VERSION);
-  command.parameters.push_back(CEC_VERSION_1_3A);
-
-  Transmit(command);
-}
-
-void CCECProcessor::ReportPowerState(cec_logical_address address /*= CECDEVICE_TV */, bool bOn /* = true */)
-{
-  if (bOn)
-    m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting \"On\" power status");
-  else
-    m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting \"Off\" power status");
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_REPORT_POWER_STATUS);
-  command.parameters.push_back(bOn ? (uint8_t) CEC_POWER_STATUS_ON : (uint8_t) CEC_POWER_STATUS_STANDBY);
-
-  Transmit(command);
-}
-
-void CCECProcessor::ReportMenuState(cec_logical_address address /* = CECDEVICE_TV */, bool bActive /* = true */)
+bool CCECProcessor::WaitForTransmitSucceeded(uint8_t iLength, uint32_t iTimeout /* = 1000 */)
 {
-  if (bActive)
-    m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting menu state as active");
-  else
-    m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting menu state as inactive");
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_MENU_STATUS);
-  command.parameters.push_back(bActive ? (uint8_t) CEC_MENU_STATE_ACTIVATED : (uint8_t) CEC_MENU_STATE_DEACTIVATED);
-
-  Transmit(command);
-}
-
-void CCECProcessor::ReportVendorID(cec_logical_address address /* = CECDEVICE_TV */)
-{
-  m_controller->AddLog(CEC_LOG_NOTICE, "<< vendor ID requested, feature abort");
-  TransmitAbort(address, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
-}
-
-void CCECProcessor::ReportOSDName(cec_logical_address address /* = CECDEVICE_TV */)
-{
-  const char *osdname = m_strDeviceName.c_str();
-  CStdString strLog;
-  strLog.Format("<< reporting OSD name as %s", osdname);
-  m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_SET_OSD_NAME);
-  for (unsigned int iPtr = 0; iPtr < strlen(osdname); iPtr++)
-    command.parameters.push_back(osdname[iPtr]);
-
-  Transmit(command);
-}
-
-void CCECProcessor::ReportPhysicalAddress(void)
-{
-  CStdString strLog;
-  strLog.Format("<< reporting physical address as %04x", m_iPhysicalAddress);
-  m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, CECDEVICE_BROADCAST, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
-  command.parameters.push_back((uint8_t) ((m_iPhysicalAddress >> 8) & 0xFF));
-  command.parameters.push_back((uint8_t) (m_iPhysicalAddress & 0xFF));
-  command.parameters.push_back((uint8_t) (CEC_DEVICE_TYPE_PLAYBACK_DEVICE));
-
-  Transmit(command);
-}
-
-void CCECProcessor::BroadcastActiveSource(void)
-{
-  m_controller->AddLog(CEC_LOG_NOTICE, "<< broadcasting active source");
-
-  cec_command command;
-  cec_command::format(command, m_iLogicalAddress, CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
-  command.parameters.push_back((uint8_t) ((m_iPhysicalAddress >> 8) & 0xFF));
-  command.parameters.push_back((uint8_t) (m_iPhysicalAddress & 0xFF));
-
-  Transmit(command);
-}
-
-bool CCECProcessor::WaitForAck(bool *bError, uint32_t iTimeout /* = 1000 */)
-{
-  bool bTransmitSucceeded = false, bEom = false;
-  *bError = false;
+  bool bError(false);
+  bool bTransmitSucceeded(false);
+  uint8_t iPacketsLeft(iLength / 4);
 
   int64_t iNow = GetTimeMs();
   int64_t iTargetTime = iNow + (uint64_t) iTimeout;
 
-  while (!bTransmitSucceeded && !*bError && (iTimeout == 0 || iNow < iTargetTime))
+  while (!bTransmitSucceeded && !bError && (iTimeout == 0 || iNow < iTargetTime))
   {
-    cec_adapter_message msg;
-    msg.clear();
+    CCECAdapterMessage msg;
 
     if (!m_communication->Read(msg, iTimeout > 0 ? (int32_t)(iTargetTime - iNow) : 1000))
     {
@@ -385,265 +298,127 @@ bool CCECProcessor::WaitForAck(bool *bError, uint32_t iTimeout /* = 1000 */)
       continue;
     }
 
-    ParseMessage(msg, bError, &bTransmitSucceeded, &bEom, false);
-    iNow = GetTimeMs();
+    if ((bError = msg.is_error()) == false)
+    {
+      m_controller->AddLog(bError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
+
+      switch(msg.message())
+      {
+      case MSGCODE_COMMAND_ACCEPTED:
+        if (iPacketsLeft > 0)
+          iPacketsLeft--;
+        break;
+      case MSGCODE_TRANSMIT_SUCCEEDED:
+        bTransmitSucceeded = (iPacketsLeft == 0);
+        bError = !bTransmitSucceeded;
+        break;
+      default:
+        ParseMessage(msg);
+      }
+
+      iNow = GetTimeMs();
+    }
   }
 
-  return bTransmitSucceeded && !*bError;
+  return bTransmitSucceeded && !bError;
 }
 
-void CCECProcessor::ParseMessage(cec_adapter_message &msg, bool *bError, bool *bTransmitSucceeded, bool *bEom, bool bProcessMessages /* = true */)
+bool CCECProcessor::ParseMessage(const CCECAdapterMessage &msg)
 {
-  *bError = false;
-  *bTransmitSucceeded = false;
-  *bEom = false;
+  bool bEom = false;
 
   if (msg.empty())
-    return;
-
-  CStdString logStr;
+    return bEom;
 
   switch(msg.message())
   {
-  case MSGCODE_NOTHING:
-    m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_NOTHING");
-    break;
-  case MSGCODE_TIMEOUT_ERROR:
-  case MSGCODE_HIGH_ERROR:
-  case MSGCODE_LOW_ERROR:
-    {
-      if (msg.message() == MSGCODE_TIMEOUT_ERROR)
-        logStr = "MSGCODE_TIMEOUT";
-      else if (msg.message() == MSGCODE_HIGH_ERROR)
-        logStr = "MSGCODE_HIGH_ERROR";
-      else
-        logStr = "MSGCODE_LOW_ERROR";
-
-      int iLine      = (msg.size() >= 3) ? (msg[1] << 8) | (msg[2]) : 0;
-      uint32_t iTime = (msg.size() >= 7) ? (msg[3] << 24) | (msg[4] << 16) | (msg[5] << 8) | (msg[6]) : 0;
-      logStr.AppendFormat(" line:%i", iLine);
-      logStr.AppendFormat(" time:%u", iTime);
-      m_controller->AddLog(CEC_LOG_WARNING, logStr.c_str());
-      *bError = true;
-    }
-    break;
   case MSGCODE_FRAME_START:
     {
-      if (bProcessMessages)
+      m_currentframe.clear();
+      if (msg.size() >= 2)
       {
-        logStr = "MSGCODE_FRAME_START";
-        m_currentframe.clear();
-        if (msg.size() >= 2)
-        {
-          logStr.AppendFormat(" initiator:%u destination:%u ack:%s %s", msg.initiator(), msg.destination(), msg.ack() ? "high" : "low", msg.eom() ? "eom" : "");
-          m_currentframe.initiator   = msg.initiator();
-          m_currentframe.destination = msg.destination();
-          m_currentframe.ack         = msg.ack();
-          m_currentframe.eom         = msg.eom();
-        }
-        m_controller->AddLog(CEC_LOG_DEBUG, logStr.c_str());
-      }
-      else
-      {
-        m_frameBuffer.Push(msg);
+        m_currentframe.initiator   = msg.initiator();
+        m_currentframe.destination = msg.destination();
+        m_currentframe.ack         = msg.ack();
+        m_currentframe.eom         = msg.eom();
       }
     }
     break;
   case MSGCODE_FRAME_DATA:
     {
-      if (bProcessMessages)
-      {
-        logStr = "MSGCODE_FRAME_DATA";
-        if (msg.size() >= 2)
-        {
-          uint8_t iData = msg[1];
-          logStr.AppendFormat(" %02x", iData);
-          m_currentframe.push_back(iData);
-          m_currentframe.eom = msg.eom();
-        }
-        m_controller->AddLog(CEC_LOG_DEBUG, logStr.c_str());
-      }
-      else
+      if (msg.size() >= 2)
       {
-        m_frameBuffer.Push(msg);
+        m_currentframe.push_back(msg[1]);
+        m_currentframe.eom = msg.eom();
       }
-
-      *bEom = msg.eom();
+      bEom = msg.eom();
     }
     break;
-  case MSGCODE_COMMAND_ACCEPTED:
-    m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_COMMAND_ACCEPTED");
-    break;
-  case MSGCODE_TRANSMIT_SUCCEEDED:
-    m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_TRANSMIT_SUCCEEDED");
-    *bTransmitSucceeded = true;
-    break;
-  case MSGCODE_RECEIVE_FAILED:
-    m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_RECEIVE_FAILED");
-    *bError = true;
-    break;
-  case MSGCODE_COMMAND_REJECTED:
-    m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_COMMAND_REJECTED");
-    *bError = true;
-    break;
-  case MSGCODE_TRANSMIT_FAILED_LINE:
-    m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_LINE");
-    *bError = true;
-    break;
-  case MSGCODE_TRANSMIT_FAILED_ACK:
-    m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_ACK");
-    *bError = true;
-    break;
-  case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
-    m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA");
-    *bError = true;
-    break;
-  case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
-    m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE");
-    *bError = true;
-    break;
   default:
     break;
   }
+
+  return bEom;
 }
 
-void CCECProcessor::ParseVendorId(cec_logical_address device, const cec_datapacket &data)
+void CCECProcessor::ParseCommand(cec_command &command)
 {
-  if (data.size < 3)
-  {
-    m_controller->AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
-    return;
-  }
-
-  uint64_t iVendorId = ((uint64_t)data[0] << 3) +
-                       ((uint64_t)data[1] << 2) +
-                        (uint64_t)data[2];
-
-  m_vendorIds[(uint8_t)device]     = iVendorId;
-  m_vendorClasses[(uint8_t)device] = data.size >= 4 ? data[3] : 0;
+  CStdString dataStr;
+  dataStr.Format(">> %1x%1x:%02x", command.initiator, command.destination, command.opcode);
+  for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
+    dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]);
+  m_controller->AddLog(CEC_LOG_TRAFFIC, dataStr.c_str());
 
-  CStdString strLog;
-  strLog.Format("device %d: vendor = %s (%lld) class = %2x", (uint8_t)device, CECVendorIdToString(m_vendorIds[(uint8_t)device]), iVendorId, m_vendorClasses[(uint8_t)device]);
-  m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+  if (!m_bMonitor)
+    m_busDevices[(uint8_t)command.initiator]->HandleCommand(command);
 }
 
-void CCECProcessor::ParseCommand(cec_command &command)
+uint16_t CCECProcessor::GetPhysicalAddress(void) const
 {
-  CStdString dataStr;
-  dataStr.Format(">> received frame: initiator: %u destination: %u", command.initiator, command.destination);
-  if (command.parameters.size > 1)
-  {
-    dataStr += " data:";
-    for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
-      dataStr.AppendFormat(" %02x", (unsigned int)command.parameters[iPtr]);
-  }
-  m_controller->AddLog(CEC_LOG_DEBUG, dataStr.c_str());
+  if (m_iLogicalAddress != CECDEVICE_UNKNOWN && m_busDevices[m_iLogicalAddress])
+    return m_busDevices[m_iLogicalAddress]->GetPhysicalAddress();
+  return false;
+}
 
-  if (command.destination == m_iLogicalAddress)
-  {
-    switch(command.opcode)
-    {
-    case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
-      ReportPhysicalAddress();
-      break;
-    case CEC_OPCODE_GIVE_OSD_NAME:
-      ReportOSDName(command.initiator);
-      break;
-    case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
-      ReportVendorID(command.initiator);
-      break;
-    case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
-      ParseVendorId(command.initiator, command.parameters);
-      TransmitAbort(command.initiator, CEC_OPCODE_VENDOR_COMMAND_WITH_ID);
-      break;
-    case CEC_OPCODE_GIVE_DECK_STATUS:
-      // need to support opcodes play and deck control before doing anything with this
-      TransmitAbort(command.initiator, CEC_OPCODE_GIVE_DECK_STATUS);
-      break;
-    case CEC_OPCODE_MENU_REQUEST:
-      if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_QUERY)
-        ReportMenuState(command.initiator);
-      break;
-    case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
-      ReportPowerState(command.initiator);
-      break;
-    case CEC_OPCODE_GET_CEC_VERSION:
-      ReportCECVersion(command.initiator);
-      break;
-    case CEC_OPCODE_USER_CONTROL_PRESSED:
-      if (command.parameters.size > 0)
-      {
-        m_controller->AddKey();
+void CCECProcessor::SetCurrentButton(cec_user_control_code iButtonCode)
+{
+  m_controller->SetCurrentButton(iButtonCode);
+}
 
-        if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
-        {
-          CStdString strLog;
-          strLog.Format("key pressed: %1x", command.parameters[0]);
-          m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+void CCECProcessor::AddCommand(const cec_command &command)
+{
+  m_controller->AddCommand(command);
+}
 
-          m_controller->SetCurrentButton((cec_user_control_code) command.parameters[0]);
-        }
-      }
-      break;
-    case CEC_OPCODE_USER_CONTROL_RELEASE:
-      m_controller->AddKey();
-      break;
-    default:
-      m_controller->AddCommand(command);
-      break;
-    }
-  }
-  else if (command.destination == CECDEVICE_BROADCAST)
-  {
-    CStdString strLog;
-    switch (command.opcode)
-    {
-    case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
-      strLog.Format(">> %i requests active source", (uint8_t) command.initiator);
-      m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
-      BroadcastActiveSource();
-      break;
-    case CEC_OPCODE_SET_STREAM_PATH:
-      if (command.parameters.size >= 2)
-      {
-        int streamaddr = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
-        strLog.Format(">> %i requests stream path from physical address %04x", command.initiator, streamaddr);
-        m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
-        if (streamaddr == m_iPhysicalAddress)
-          BroadcastActiveSource();
-      }
-      break;
-    case CEC_OPCODE_ROUTING_CHANGE:
-      if (command.parameters.size == 4)
-      {
-        uint16_t iOldAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
-        uint16_t iNewAddress = ((uint16_t)command.parameters[2] << 8) | ((uint16_t)command.parameters[3]);
-        strLog.Format(">> %i changed physical address from %04x to %04x", command.initiator, iOldAddress, iNewAddress);
-        m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+void CCECProcessor::AddKey(void)
+{
+  m_controller->AddKey();
+}
 
-        m_controller->AddCommand(command);
-      }
-      break;
-    default:
-      m_controller->AddCommand(command);
-      break;
-    }
-  }
-  else
-  {
-    CStdString strLog;
-    strLog.Format("ignoring frame: destination: %u != %u", command.destination, (uint8_t)m_iLogicalAddress);
-    m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
-  }
+void CCECProcessor::AddLog(cec_log_level level, const CStdString &strMessage)
+{
+  m_controller->AddLog(level, strMessage);
 }
 
-const char *CCECProcessor::CECVendorIdToString(const uint64_t iVendorId)
+bool CCECProcessor::SetAckMask(uint16_t iMask)
 {
-  switch (iVendorId)
+  CStdString strLog;
+  strLog.Format("setting ackmask to %2x", iMask);
+  m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+
+  CCECAdapterMessagePtr output(new CCECAdapterMessage);
+
+  output->push_back(MSGSTART);
+  output->push_escaped(MSGCODE_SET_ACK_MASK);
+  output->push_escaped(iMask >> 8);
+  output->push_escaped((uint8_t)iMask);
+  output->push_back(MSGEND);
+
+  if (!Transmit(output))
   {
-  case CEC_VENDOR_SAMSUNG:
-    return "Samsung";
-  default:
-    return "Unknown";
+    m_controller->AddLog(CEC_LOG_ERROR, "could not set the ackmask");
+    return false;
   }
+
+  return true;
 }
index dd0596e4e7d356e6675e4696b9851d73f6ba71a1..e2793a3bbcb75621d9b5af0516e61b28038f257f 100644 (file)
 
 #include <string>
 #include <cectypes.h>
+#include "AdapterCommunication.h"
 #include "platform/threads.h"
 #include "util/buffer.h"
+#include "util/StdString.h"
 
 class CSerialPort;
 
@@ -42,6 +44,7 @@ namespace CEC
 {
   class CLibCEC;
   class CAdapterCommunication;
+  class CCECBusDevice;
 
   class CCECProcessor : public CThread
   {
@@ -50,46 +53,47 @@ namespace CEC
       virtual ~CCECProcessor(void);
 
       virtual bool Start(void);
-      void *Process(void);
+      virtual void *Process(void);
+
+      virtual cec_version         GetDeviceCecVersion(cec_logical_address iAddress);
+      virtual bool                GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language);
+      virtual const std::string & GetDeviceName(void) { return m_strDeviceName; }
+      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_iLogicalAddress; }
+      virtual uint16_t            GetPhysicalAddress(void) const;
 
-      virtual bool PowerOnDevices(cec_logical_address address = CECDEVICE_TV);
-      virtual bool StandbyDevices(cec_logical_address address = CECDEVICE_BROADCAST);
       virtual bool SetActiveView(void);
       virtual bool SetInactiveView(void);
-      virtual bool Transmit(const cec_command &data, bool bWaitForAck = true);
       virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress);
       virtual bool SetPhysicalAddress(uint16_t iPhysicalAddress);
-      virtual bool SetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage);
-
-      static const char *CECVendorIdToString(const uint64_t iVendorId);
+      virtual bool SwitchMonitoring(bool bEnable);
 
-    protected:
-      virtual bool TransmitFormatted(const cec_adapter_message &data, bool bWaitForAck = true);
+      virtual bool Transmit(const cec_command &data);
+      virtual bool Transmit(CCECAdapterMessagePtr output);
       virtual void TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE);
-      virtual void ReportCECVersion(cec_logical_address address = CECDEVICE_TV);
-      virtual void ReportPowerState(cec_logical_address address = CECDEVICE_TV, bool bOn = true);
-      virtual void ReportMenuState(cec_logical_address address = CECDEVICE_TV, bool bActive = true);
-      virtual void ReportVendorID(cec_logical_address address = CECDEVICE_TV);
-      virtual void ReportOSDName(cec_logical_address address = CECDEVICE_TV);
-      virtual void ReportPhysicalAddress(void);
-      virtual void BroadcastActiveSource(void);
 
-    private:
+      virtual void SetCurrentButton(cec_user_control_code iButtonCode);
+      virtual void AddCommand(const cec_command &command);
+      virtual void AddKey(void);
+      virtual void AddLog(cec_log_level level, const CStdString &strMessage);
+
+      CCECBusDevice *m_busDevices[16];
+
+  private:
+      bool SetAckMask(uint16_t iMask);
       void LogOutput(const cec_command &data);
-      bool WaitForAck(bool *bError, uint32_t iTimeout = 1000);
-      void ParseMessage(cec_adapter_message &msg, bool *bError, bool *bTransmitSucceeded, bool *bEom, bool bProcessMessages = true);
+      bool WaitForTransmitSucceeded(uint8_t iLength, uint32_t iTimeout = 1000);
+      bool ParseMessage(const CCECAdapterMessage &msg);
       void ParseCommand(cec_command &command);
-      void ParseVendorId(cec_logical_address device, const cec_datapacket &data);
 
-      cec_command                    m_currentframe;
-      uint16_t                       m_iPhysicalAddress;
-      cec_logical_address            m_iLogicalAddress;
-      CecBuffer<cec_adapter_message> m_frameBuffer;
-      std::string                    m_strDeviceName;
-      CMutex                         m_mutex;
-      CAdapterCommunication         *m_communication;
-      CLibCEC                       *m_controller;
-      uint64_t                       m_vendorIds[16];
-      uint8_t                        m_vendorClasses[16];
+      cec_command            m_currentframe;
+      cec_logical_address    m_iLogicalAddress;
+      std::string            m_strDeviceName;
+      CMutex                 m_mutex;
+      CCondition             m_startCondition;
+      CAdapterCommunication *m_communication;
+      CLibCEC               *m_controller;
+      bool                   m_bMonitor;
   };
 };
index 7c0f7927956126f088a9bb5ec443f9c04ebe086b..ed60f43ff2f25f623e814f86b282c781c5f0e915 100644 (file)
@@ -35,6 +35,7 @@
 #include "AdapterCommunication.h"
 #include "AdapterDetection.h"
 #include "CECProcessor.h"
+#include "devices/CECBusDevice.h"
 #include "util/StdString.h"
 #include "platform/timeutils.h"
 
@@ -158,9 +159,9 @@ bool CLibCEC::GetNextCommand(cec_command *command)
   return m_commandBuffer.Pop(*command);
 }
 
-bool CLibCEC::Transmit(const cec_command &data, bool bWaitForAck /* = true */)
+bool CLibCEC::Transmit(const cec_command &data)
 {
-  return m_cec ? m_cec->Transmit(data, bWaitForAck) : false;
+  return m_cec ? m_cec->Transmit(data) : false;
 }
 
 bool CLibCEC::SetLogicalAddress(cec_logical_address iLogicalAddress)
@@ -175,12 +176,12 @@ bool CLibCEC::SetPhysicalAddress(uint16_t iPhysicalAddress)
 
 bool CLibCEC::PowerOnDevices(cec_logical_address address /* = CECDEVICE_TV */)
 {
-  return m_cec ? m_cec->PowerOnDevices(address) : false;
+  return m_cec && address >= CECDEVICE_TV && address <= CECDEVICE_BROADCAST ? m_cec->m_busDevices[(uint8_t)address]->PowerOn() : false;
 }
 
 bool CLibCEC::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
 {
-  return m_cec ? m_cec->StandbyDevices(address) : false;
+  return m_cec && address >= CECDEVICE_TV && address <= CECDEVICE_BROADCAST ? m_cec->m_busDevices[(uint8_t)address]->Standby() : false;
 }
 
 bool CLibCEC::SetActiveView(void)
@@ -195,7 +196,40 @@ bool CLibCEC::SetInactiveView(void)
 
 bool CLibCEC::SetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage)
 {
-  return m_cec ? m_cec->SetOSDString(iLogicalAddress, duration, strMessage) : false;
+  return m_cec && iLogicalAddress >= CECDEVICE_TV && iLogicalAddress <= CECDEVICE_BROADCAST ? m_cec->m_busDevices[(uint8_t)iLogicalAddress]->SetOSDString(duration, strMessage) : false;
+}
+
+bool CLibCEC::SwitchMonitoring(bool bEnable)
+{
+  return m_cec ? m_cec->SwitchMonitoring(bEnable) : false;
+}
+
+cec_version CLibCEC::GetDeviceCecVersion(cec_logical_address iAddress)
+{
+  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
+    return m_cec->GetDeviceCecVersion(iAddress);
+  return CEC_VERSION_UNKNOWN;
+}
+
+bool CLibCEC::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language)
+{
+  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
+    return m_cec->GetDeviceMenuLanguage(iAddress, language);
+  return false;
+}
+
+uint64_t CLibCEC::GetDeviceVendorId(cec_logical_address iAddress)
+{
+  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
+    return m_cec->GetDeviceVendorId(iAddress);
+  return 0;
+}
+
+cec_power_status CLibCEC::GetDevicePowerStatus(cec_logical_address iAddress)
+{
+  if (m_cec && iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST)
+    return m_cec->GetDevicePowerStatus(iAddress);
+  return CEC_POWER_STATUS_UNKNOWN;
 }
 
 void CLibCEC::AddLog(cec_log_level level, const string &strMessage)
@@ -224,7 +258,7 @@ void CLibCEC::AddKey(void)
   }
 }
 
-void CLibCEC::AddCommand(cec_command &command)
+void CLibCEC::AddCommand(const cec_command &command)
 {
   if (m_commandBuffer.Push(command))
   {
index 8ea0c98417dba829b9c763f349709df29836b1ec..391148dadbf110bfb93c993ff6e9b187bf3062f1 100644 (file)
@@ -63,7 +63,7 @@ namespace CEC
       virtual bool GetNextKeypress(cec_keypress *key);
       virtual bool GetNextCommand(cec_command *command);
 
-      virtual bool Transmit(const cec_command &data, bool bWaitForAck = true);
+      virtual bool Transmit(const cec_command &data);
       virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress = CECDEVICE_PLAYBACKDEVICE1);
       virtual bool SetPhysicalAddress(uint16_t iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS);
 
@@ -72,11 +72,16 @@ namespace CEC
       virtual bool SetActiveView(void);
       virtual bool SetInactiveView(void);
       virtual bool SetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage);
+      virtual bool SwitchMonitoring(bool bEnable);
+      virtual cec_version GetDeviceCecVersion(cec_logical_address iAddress);
+      virtual bool GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language);
+      virtual uint64_t GetDeviceVendorId(cec_logical_address iAddress);
+      virtual cec_power_status GetDevicePowerStatus(cec_logical_address iAddress);
     //@}
 
       virtual void AddLog(cec_log_level level, const std::string &strMessage);
       virtual void AddKey(void);
-      virtual void AddCommand(cec_command &command);
+      virtual void AddCommand(const cec_command &command);
       virtual void CheckKeypressTimeout(void);
       virtual void SetCurrentButton(cec_user_control_code iButtonCode);
 
index d062dff9357f39ccdd76b6156eee1663a3ac58f2..28a268b3879532f263c55095da3f3efae6553b18 100644 (file)
@@ -124,10 +124,10 @@ int cec_get_next_command(cec_command *command)
   return -1;
 }
 
-int cec_transmit(const CEC::cec_command &data, int bWaitForAck /* = true */)
+int cec_transmit(const CEC::cec_command &data)
 {
   if (cec_parser)
-    return cec_parser->Transmit(data, bWaitForAck == 1) ? 1 : 0;
+    return cec_parser->Transmit(data) ? 1 : 0;
   return -1;
 }
 
@@ -180,4 +180,39 @@ int cec_set_osd_string(cec_logical_address iLogicalAddress, cec_display_control
   return -1;
 }
 
+int cec_switch_monitoring(int bEnable)
+{
+  if (cec_parser)
+    return cec_parser->SwitchMonitoring(bEnable == 1) ? 1 : 0;
+  return -1;
+}
+
+cec_version cec_get_device_cec_version(cec_logical_address iLogicalAddress)
+{
+  if (cec_parser)
+    return cec_parser->GetDeviceCecVersion(iLogicalAddress);
+  return CEC_VERSION_UNKNOWN;
+}
+
+int cec_get_device_menu_language(cec_logical_address iLogicalAddress, cec_menu_language *language)
+{
+  if (cec_parser)
+    return cec_parser->GetDeviceMenuLanguage(iLogicalAddress, language) ? 1 : 0;
+  return -1;
+}
+
+uint64_t cec_get_device_vendor_id(cec_logical_address iLogicalAddress)
+{
+  if (cec_parser)
+    return cec_parser->GetDeviceVendorId(iLogicalAddress);
+  return 0;
+}
+
+cec_power_status cec_get_device_power_status(cec_logical_address iLogicalAddress)
+{
+  if (cec_parser)
+    return cec_parser->GetDevicePowerStatus(iLogicalAddress);
+  return CEC_POWER_STATUS_UNKNOWN;
+}
+
 //@}
index e3354e17115f71d68559c6b72eb42a1f599762b7..31d630de034c2e2693fba0bb0cfb852c5e330278 100644 (file)
@@ -18,6 +18,14 @@ libcec_la_SOURCES = AdapterCommunication.cpp \
                     LibCEC.h \
                     LibCECC.cpp \
                     util/StdString.h \
+                    devices/CECBusDevice.cpp \
+                    devices/CECBusDevice.h \
+                    implementations/ANCommandHandler.cpp \
+                    implementations/ANCommandHandler.h \
+                    implementations/CECCommandHandler.cpp \
+                    implementations/CECCommandHandler.h \
+                    implementations/SLCommandHandler.cpp \
+                    implementations/SLCommandHandler.h \
                     platform/timeutils.h \
                     platform/baudrate.h \
                     platform/os-dependent.h \
diff --git a/src/lib/devices/CECBusDevice.cpp b/src/lib/devices/CECBusDevice.cpp
new file mode 100644 (file)
index 0000000..0678ce3
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * This file is part of the libCEC(R) library.
+ *
+ * libCEC(R) is Copyright (C) 2011 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 "CECBusDevice.h"
+#include "../CECProcessor.h"
+#include "../implementations/ANCommandHandler.h"
+#include "../implementations/CECCommandHandler.h"
+#include "../implementations/SLCommandHandler.h"
+#include "../platform/timeutils.h"
+
+using namespace CEC;
+
+CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogicalAddress, uint16_t iPhysicalAddress) :
+  m_iPhysicalAddress(iPhysicalAddress),
+  m_iLogicalAddress(iLogicalAddress),
+  m_powerStatus(CEC_POWER_STATUS_UNKNOWN),
+  m_processor(processor),
+  m_iVendorClass(CEC_VENDOR_UNKNOWN),
+  m_iLastActive(0),
+  m_cecVersion(CEC_VERSION_UNKNOWN)
+{
+  m_handler = new CCECCommandHandler(this);
+  for (unsigned int iPtr = 0; iPtr < 4; iPtr++)
+    m_menuLanguage.language[iPtr] = '?';
+  m_menuLanguage.language[3] = 0;
+  m_menuLanguage.device = iLogicalAddress;
+  m_vendor.vendor = CEC_VENDOR_UNKNOWN;
+}
+
+CCECBusDevice::~CCECBusDevice(void)
+{
+  m_condition.Broadcast();
+  delete m_handler;
+}
+
+cec_logical_address CCECBusDevice::GetMyLogicalAddress(void) const
+{
+  return m_processor->GetLogicalAddress();
+}
+
+uint16_t CCECBusDevice::GetMyPhysicalAddress(void) const
+{
+  return m_processor->GetPhysicalAddress();
+}
+
+void CCECBusDevice::AddLog(cec_log_level level, const CStdString &strMessage)
+{
+  m_processor->AddLog(level, strMessage);
+}
+
+void CCECBusDevice::SetMenuLanguage(const cec_menu_language &language)
+{
+  if (language.device == m_iLogicalAddress)
+  {
+    CStdString strLog;
+    strLog.Format("device %d menu language set to '%s'", m_iLogicalAddress, language.language);
+    m_processor->AddLog(CEC_LOG_DEBUG, strLog);
+    m_menuLanguage = language;
+  }
+}
+
+void CCECBusDevice::SetCecVersion(const cec_version newVersion)
+{
+  CStdString strLog;
+  m_cecVersion = newVersion;
+
+  switch (newVersion)
+  {
+  case CEC_VERSION_1_2:
+    strLog.Format("device %d reports CEC version 1.2", m_iLogicalAddress);
+    break;
+  case CEC_VERSION_1_2A:
+    strLog.Format("device %d reports CEC version 1.2a", m_iLogicalAddress);
+    break;
+  case CEC_VERSION_1_3:
+    strLog.Format("device %d reports CEC version 1.3", m_iLogicalAddress);
+    break;
+  case CEC_VERSION_1_3A:
+    strLog.Format("device %d reports CEC version 1.3a", m_iLogicalAddress);
+    break;
+  default:
+    strLog.Format("device %d reports an unknown CEC version", m_iLogicalAddress);
+    m_cecVersion = CEC_VERSION_UNKNOWN;
+    break;
+  }
+  AddLog(CEC_LOG_DEBUG, strLog);
+}
+
+void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus)
+{
+  if (m_powerStatus != powerStatus)
+  {
+    CStdString strLog;
+    strLog.Format("device %d power status changed from %2x to %2x", m_iLogicalAddress, m_powerStatus, powerStatus);
+    m_processor->AddLog(CEC_LOG_DEBUG, strLog);
+    m_powerStatus = powerStatus;
+  }
+}
+
+void CCECBusDevice::SetVendorId(const cec_datapacket &data)
+{
+  if (data.size < 3)
+  {
+    AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
+    return;
+  }
+
+  uint64_t iVendorId = ((uint64_t)data[0] << 3) +
+                       ((uint64_t)data[1] << 2) +
+                        (uint64_t)data[2];
+
+  SetVendorId(iVendorId, data.size >= 4 ? data[3] : 0);
+}
+
+void CCECBusDevice::SetVendorId(uint64_t iVendorId, uint8_t iVendorClass /* = 0 */)
+{
+  m_vendor.vendor = (cec_vendor_id)iVendorId;
+  m_iVendorClass = iVendorClass;
+
+  switch (iVendorId)
+  {
+  case CEC_VENDOR_SAMSUNG:
+    if (m_handler->GetVendorId() != CEC_VENDOR_SAMSUNG)
+    {
+      delete m_handler;
+      m_handler = new CANCommandHandler(this);
+    }
+    break;
+  case CEC_VENDOR_LG:
+    if (m_handler->GetVendorId() != CEC_VENDOR_LG)
+    {
+      delete m_handler;
+      m_handler = new CSLCommandHandler(this);
+    }
+    break;
+  default:
+    if (m_handler->GetVendorId() != CEC_VENDOR_UNKNOWN)
+    {
+      delete m_handler;
+      m_handler = new CCECCommandHandler(this);
+    }
+    break;
+  }
+
+  CStdString strLog;
+  strLog.Format("device %d: vendor = %s (%06x) class = %2x", m_iLogicalAddress, GetVendorName(), GetVendorId(), GetVendorClass());
+  m_processor->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+}
+
+bool CCECBusDevice::HandleCommand(const cec_command &command)
+{
+  CLockObject lock(&m_mutex);
+  m_iLastActive = GetTimeMs();
+  m_handler->HandleCommand(command);
+  m_condition.Signal();
+  return true;
+}
+
+const cec_vendor &CCECBusDevice::GetVendor(void)
+{
+  if (m_vendor.vendor == CEC_VENDOR_UNKNOWN)
+  {
+    AddLog(CEC_LOG_NOTICE, "<< requesting vendor ID");
+    cec_command command;
+    cec_command::format(command, GetMyLogicalAddress(), GetLogicalAddress(), CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
+    CLockObject lock(&m_mutex);
+
+    if (m_processor->Transmit(command))
+      m_condition.Wait(&m_mutex, 1000);
+  }
+
+  return m_vendor;
+}
+
+void CCECBusDevice::PollVendorId(void)
+{
+  CLockObject lock(&m_mutex);
+  if (m_iLastActive > 0 && m_iLogicalAddress != CECDEVICE_BROADCAST &&
+      m_vendor.vendor == CEC_VENDOR_UNKNOWN &&
+      GetTimeMs() - m_iLastActive > 5000)
+  {
+    m_iLastActive = GetTimeMs();
+
+    cec_command command;
+    cec_command::format(command, GetMyLogicalAddress(), GetLogicalAddress(), CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
+    if (m_processor->Transmit(command))
+      m_condition.Wait(&m_mutex, 1000);
+  }
+}
+
+void CCECBusDevice::SetPhysicalAddress(uint16_t iNewAddress, uint16_t iOldAddress /* = 0 */)
+{
+  if (iNewAddress > 0)
+  {
+    CStdString strLog;
+    strLog.Format(">> %i changed physical address from %04x to %04x", GetLogicalAddress(), m_iPhysicalAddress, iNewAddress);
+    AddLog(CEC_LOG_DEBUG, strLog.c_str());
+
+    m_iPhysicalAddress = iNewAddress;
+  }
+}
+
+bool CCECBusDevice::PowerOn(void)
+{
+  CStdString strLog;
+  strLog.Format("<< powering on device with logical address %d", (int8_t)m_iLogicalAddress);
+  AddLog(CEC_LOG_DEBUG, strLog.c_str());
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_IMAGE_VIEW_ON);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::Standby(void)
+{
+  CStdString strLog;
+  strLog.Format("<< putting device with logical address %d in standby mode", (int8_t)m_iLogicalAddress);
+  AddLog(CEC_LOG_DEBUG, strLog.c_str());
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_STANDBY);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::SetOSDString(cec_display_control duration, const char *strMessage)
+{
+  CStdString strLog;
+  strLog.Format("<< display message '%s'", strMessage);
+  AddLog(CEC_LOG_NOTICE, strLog.c_str());
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_SET_OSD_STRING);
+  command.parameters.push_back((uint8_t)duration);
+
+  for (unsigned int iPtr = 0; iPtr < strlen(strMessage); iPtr++)
+    command.parameters.push_back(strMessage[iPtr]);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::ReportCECVersion(void)
+{
+  AddLog(CEC_LOG_NOTICE, "<< reporting CEC version as 1.3a");
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_CEC_VERSION);
+  command.parameters.push_back(CEC_VERSION_1_3A);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::ReportDeckStatus(void)
+{
+  // need to support opcodes play and deck control before doing anything with this
+  AddLog(CEC_LOG_NOTICE, "<< deck status requested, feature abort");
+  m_processor->TransmitAbort(m_iLogicalAddress, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
+  return false;
+}
+
+bool CCECBusDevice::ReportMenuState(bool bActive /* = true */)
+{
+  if (bActive)
+    AddLog(CEC_LOG_NOTICE, "<< reporting menu state as active");
+  else
+    AddLog(CEC_LOG_NOTICE, "<< reporting menu state as inactive");
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_MENU_STATUS);
+  command.parameters.push_back(bActive ? (uint8_t) CEC_MENU_STATE_ACTIVATED : (uint8_t) CEC_MENU_STATE_DEACTIVATED);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::ReportOSDName(void)
+{
+  const char *osdname = m_processor->GetDeviceName().c_str();
+  CStdString strLog;
+  strLog.Format("<< reporting OSD name as %s", osdname);
+  AddLog(CEC_LOG_NOTICE, strLog.c_str());
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_SET_OSD_NAME);
+  for (unsigned int iPtr = 0; iPtr < strlen(osdname); iPtr++)
+    command.parameters.push_back(osdname[iPtr]);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::ReportPowerState(bool bOn /* = true */)
+{
+  if (bOn)
+    AddLog(CEC_LOG_NOTICE, "<< reporting \"On\" power status");
+  else
+    AddLog(CEC_LOG_NOTICE, "<< reporting \"Off\" power status");
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_REPORT_POWER_STATUS);
+  command.parameters.push_back(bOn ? (uint8_t) CEC_POWER_STATUS_ON : (uint8_t) CEC_POWER_STATUS_STANDBY);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::ReportVendorID(void)
+{
+  AddLog(CEC_LOG_NOTICE, "<< vendor ID requested, feature abort");
+  m_processor->TransmitAbort(m_iLogicalAddress, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
+  return false;
+}
+
+bool CCECBusDevice::BroadcastActiveView(void)
+{
+  AddLog(CEC_LOG_DEBUG, "<< setting active view");
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
+  command.parameters.push_back((m_iPhysicalAddress >> 8) & 0xFF);
+  command.parameters.push_back(m_iPhysicalAddress & 0xFF);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::BroadcastInactiveView(void)
+{
+  AddLog(CEC_LOG_DEBUG, "<< setting inactive view");
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_INACTIVE_SOURCE);
+  command.parameters.push_back((m_iPhysicalAddress >> 8) & 0xFF);
+  command.parameters.push_back(m_iPhysicalAddress & 0xFF);
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::BroadcastPhysicalAddress(void)
+{
+  CStdString strLog;
+  strLog.Format("<< reporting physical address as %04x", m_iPhysicalAddress);
+  AddLog(CEC_LOG_NOTICE, strLog.c_str());
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
+  command.parameters.push_back((uint8_t) ((m_iPhysicalAddress >> 8) & 0xFF));
+  command.parameters.push_back((uint8_t) (m_iPhysicalAddress & 0xFF));
+  command.parameters.push_back((uint8_t) (CEC_DEVICE_TYPE_PLAYBACK_DEVICE));
+
+  return m_processor->Transmit(command);
+}
+
+bool CCECBusDevice::BroadcastActiveSource(void)
+{
+  AddLog(CEC_LOG_NOTICE, "<< broadcasting active source");
+
+  cec_command command;
+  cec_command::format(command, GetMyLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
+  command.parameters.push_back((uint8_t) ((m_iPhysicalAddress >> 8) & 0xFF));
+  command.parameters.push_back((uint8_t) (m_iPhysicalAddress & 0xFF));
+
+  return m_processor->Transmit(command);
+}
+
+cec_version CCECBusDevice::GetCecVersion(bool bRefresh /* = true */)
+{
+  if (bRefresh || m_cecVersion == CEC_VERSION_UNKNOWN)
+  {
+    AddLog(CEC_LOG_NOTICE, "<< requesting CEC version");
+    cec_command command;
+    cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_GET_CEC_VERSION);
+    CLockObject lock(&m_mutex);
+    if (m_processor->Transmit(command))
+      m_condition.Wait(&m_mutex, 1000);
+  }
+
+  return m_cecVersion;
+}
+
+cec_menu_language &CCECBusDevice::GetMenuLanguage(bool bRefresh /* = true */)
+{
+  if (bRefresh || !strcmp(m_menuLanguage.language, "???"))
+  {
+    AddLog(CEC_LOG_NOTICE, "<< requesting menu language");
+    cec_command command;
+    cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_GET_MENU_LANGUAGE);
+    CLockObject lock(&m_mutex);
+    if (m_processor->Transmit(command))
+      m_condition.Wait(&m_mutex, 1000);
+  }
+
+  return m_menuLanguage;
+}
+
+cec_power_status CCECBusDevice::GetPowerStatus(bool bRefresh /* = true */)
+{
+  if (bRefresh || m_powerStatus == CEC_POWER_STATUS_UNKNOWN)
+  {
+    AddLog(CEC_LOG_NOTICE, "<< requesting power status");
+    cec_command command;
+    cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_GIVE_DEVICE_POWER_STATUS);
+    CLockObject lock(&m_mutex);
+    if (m_processor->Transmit(command))
+      m_condition.Wait(&m_mutex, 1000);
+  }
+
+  return m_powerStatus;
+}
diff --git a/src/lib/devices/CECBusDevice.h b/src/lib/devices/CECBusDevice.h
new file mode 100644 (file)
index 0000000..5971f58
--- /dev/null
@@ -0,0 +1,106 @@
+#pragma once
+/*
+ * This file is part of the libCEC(R) library.
+ *
+ * libCEC(R) is Copyright (C) 2011 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 <cectypes.h>
+#include "../platform/threads.h"
+#include "../util/StdString.h"
+
+namespace CEC
+{
+  class CCECProcessor;
+  class CCECCommandHandler;
+
+  class CCECBusDevice
+  {
+  public:
+    CCECBusDevice(CCECProcessor *processor, cec_logical_address address, uint16_t iPhysicalAddress = 0);
+    virtual ~CCECBusDevice(void);
+
+    virtual cec_logical_address GetMyLogicalAddress(void) const;
+    virtual uint16_t            GetMyPhysicalAddress(void) const;
+    virtual const char *        GetVendorName(void) { return GetVendor().AsString(); }
+    virtual cec_vendor_id       GetVendorId(void) { return GetVendor().vendor; };
+    virtual const cec_vendor &  GetVendor(void);
+    virtual uint8_t             GetVendorClass(void) const { return m_iVendorClass; }
+    virtual uint64_t            GetLastActive(void) const { return m_iLastActive; }
+    virtual cec_logical_address GetLogicalAddress(void) const { return m_iLogicalAddress; }
+    virtual uint16_t            GetPhysicalAddress(void) const { return m_iPhysicalAddress; }
+    virtual cec_version         GetCecVersion(bool bRefresh = true);
+    virtual cec_menu_language & GetMenuLanguage(bool bRefresh = true);
+    virtual cec_power_status    GetPowerStatus(bool bRefresh = true);
+
+    virtual bool PowerOn(void);
+    virtual bool Standby(void);
+    virtual bool SetOSDString(cec_display_control duration, const char *strMessage);
+    virtual void PollVendorId(void);
+
+    virtual void SetPhysicalAddress(uint16_t iNewAddress, uint16_t iOldAddress = 0);
+    virtual void SetCecVersion(const cec_version newVersion);
+    virtual void SetMenuLanguage(const cec_menu_language &menuLanguage);
+    virtual void SetVendorId(const cec_datapacket &data);
+    virtual void SetVendorId(uint64_t iVendorId, uint8_t iVendorClass = 0);
+    virtual void SetPowerStatus(const cec_power_status powerStatus);
+
+    virtual bool HandleCommand(const cec_command &command);
+
+    virtual void AddLog(cec_log_level level, const CStdString &strMessage);
+    virtual CCECProcessor *GetProcessor() const { return m_processor; }
+    virtual CCECCommandHandler *GetHandler(void) const { return m_handler; };
+
+    virtual bool ReportCECVersion(void);
+    virtual bool ReportDeckStatus(void);
+    virtual bool ReportMenuState(bool bActive = true);
+    virtual bool ReportOSDName(void);
+    virtual bool ReportPowerState(bool bOn = true);
+    virtual bool ReportVendorID(void);
+
+    virtual bool BroadcastActiveView(void);
+    virtual bool BroadcastInactiveView(void);
+    virtual bool BroadcastPhysicalAddress(void);
+    virtual bool BroadcastActiveSource(void);
+
+  protected:
+    uint16_t            m_iPhysicalAddress;
+    cec_logical_address m_iLogicalAddress;
+    cec_power_status    m_powerStatus;
+    cec_menu_language   m_menuLanguage;
+    CCECProcessor      *m_processor;
+    CCECCommandHandler *m_handler;
+    cec_vendor          m_vendor;
+    uint8_t             m_iVendorClass;
+    uint64_t            m_iLastActive;
+    cec_version         m_cecVersion;
+    CMutex              m_mutex;
+    CCondition          m_condition;
+  };
+};
diff --git a/src/lib/implementations/ANCommandHandler.cpp b/src/lib/implementations/ANCommandHandler.cpp
new file mode 100644 (file)
index 0000000..394cfa9
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the libCEC(R) library.
+ *
+ * libCEC(R) is Copyright (C) 2011 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 "ANCommandHandler.h"
+#include "../devices/CECBusDevice.h"
+#include "../CECProcessor.h"
+#include "../util/StdString.h"
+
+using namespace CEC;
+
+CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice) :
+    CCECCommandHandler(busDevice)
+{
+}
+
+bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command)
+{
+  if (command.parameters.size > 0)
+  {
+    m_busDevice->GetProcessor()->AddKey();
+
+    uint8_t iButton = 0;
+    switch (command.parameters[0])
+    {
+    case CEC_AN_USER_CONTROL_CODE_RETURN:
+      iButton = CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL;
+      break;
+    default:
+      break;
+    }
+
+    if (iButton > 0 && iButton <= CEC_USER_CONTROL_CODE_MAX)
+    {
+      CStdString strLog;
+      strLog.Format("key pressed: %1x", iButton);
+      m_busDevice->AddLog(CEC_LOG_DEBUG, strLog);
+
+      m_busDevice->GetProcessor()->SetCurrentButton((cec_user_control_code) command.parameters[0]);
+    }
+  }
+
+  return true;
+}
+
+bool CANCommandHandler::HandleCommand(const cec_command &command)
+{
+  bool bHandled(false);
+  if (command.destination == m_busDevice->GetMyLogicalAddress())
+  {
+    switch(command.opcode)
+    {
+    case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:
+      bHandled = true;
+      HandleVendorRemoteButtonDown(command);
+      break;
+    case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:
+      bHandled = true;
+      HandleUserControlRelease(command);
+      break;
+    default:
+      break;
+    }
+  }
+
+  if (!bHandled)
+    bHandled = CCECCommandHandler::HandleCommand(command);
+
+  return bHandled;
+}
diff --git a/src/lib/implementations/ANCommandHandler.h b/src/lib/implementations/ANCommandHandler.h
new file mode 100644 (file)
index 0000000..0207a63
--- /dev/null
@@ -0,0 +1,51 @@
+#pragma once
+/*
+ * This file is part of the libCEC(R) library.
+ *
+ * libCEC(R) is Copyright (C) 2011 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 "CECCommandHandler.h"
+
+namespace CEC
+{
+  class CANCommandHandler : public CCECCommandHandler
+  {
+  public:
+    CANCommandHandler(CCECBusDevice *busDevice);
+    virtual ~CANCommandHandler(void) {};
+
+    virtual bool HandleCommand(const cec_command &command);
+
+    virtual cec_vendor_id GetVendorId(void) { return CEC_VENDOR_SAMSUNG; };
+
+  protected:
+    virtual bool HandleVendorRemoteButtonDown(const cec_command &command);
+  };
+};
diff --git a/src/lib/implementations/CECCommandHandler.cpp b/src/lib/implementations/CECCommandHandler.cpp
new file mode 100644 (file)
index 0000000..ac00ebf
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * This file is part of the libCEC(R) library.
+ *
+ * libCEC(R) is Copyright (C) 2011 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 "CECCommandHandler.h"
+#include "../devices/CECBusDevice.h"
+#include "../CECProcessor.h"
+
+using namespace CEC;
+
+CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice)
+{
+  m_busDevice = busDevice;
+}
+
+bool CCECCommandHandler::HandleCommand(const cec_command &command)
+{
+  bool bHandled(true);
+
+  if (command.destination == m_busDevice->GetMyLogicalAddress())
+  {
+    switch(command.opcode)
+    {
+    case CEC_OPCODE_REPORT_POWER_STATUS:
+      HandleReportPowerStatus(command);
+      break;
+    case CEC_OPCODE_CEC_VERSION:
+      HandleDeviceCecVersion(command);
+      break;
+    case CEC_OPCODE_SET_MENU_LANGUAGE:
+      HandleSetMenuLanguage(command);
+      m_busDevice->GetProcessor()->AddCommand(command);
+      break;
+    case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
+      HandleGivePhysicalAddress(command);
+      break;
+    case CEC_OPCODE_GIVE_OSD_NAME:
+      HandleGiveOSDName(command);
+      break;
+    case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
+      HandleGiveDeviceVendorId(command);
+      break;
+    case CEC_OPCODE_DEVICE_VENDOR_ID:
+      HandleDeviceVendorId(command);
+      break;
+    case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
+      HandleDeviceVendorCommandWithId(command);
+      break;
+    case CEC_OPCODE_GIVE_DECK_STATUS:
+      HandleGiveDeckStatus(command);
+      break;
+    case CEC_OPCODE_MENU_REQUEST:
+      HandleMenuRequest(command);
+      break;
+    case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
+      HandleGiveDevicePowerStatus(command);
+      break;
+    case CEC_OPCODE_GET_CEC_VERSION:
+      HandleGetCecVersion(command);
+      break;
+    case CEC_OPCODE_USER_CONTROL_PRESSED:
+      HandleUserControlPressed(command);
+      break;
+    case CEC_OPCODE_USER_CONTROL_RELEASE:
+      HandleUserControlRelease(command);
+      break;
+    default:
+      UnhandledCommand(command);
+      m_busDevice->GetProcessor()->AddCommand(command);
+      bHandled = false;
+      break;
+    }
+  }
+  else if (command.destination == CECDEVICE_BROADCAST)
+  {
+    CStdString strLog;
+    switch (command.opcode)
+    {
+    case CEC_OPCODE_SET_MENU_LANGUAGE:
+      HandleSetMenuLanguage(command);
+      m_busDevice->GetProcessor()->AddCommand(command);
+      break;
+    case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
+      HandleRequestActiveSource(command);
+      break;
+    case CEC_OPCODE_SET_STREAM_PATH:
+      HandleSetStreamPath(command);
+      m_busDevice->GetProcessor()->AddCommand(command);
+      break;
+    case CEC_OPCODE_ROUTING_CHANGE:
+      HandleRoutingChange(command);
+      m_busDevice->GetProcessor()->AddCommand(command);
+      break;
+    case CEC_OPCODE_DEVICE_VENDOR_ID:
+      HandleDeviceVendorId(command);
+      break;
+    case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
+      HandleDeviceVendorCommandWithId(command);
+     break;
+    default:
+      UnhandledCommand(command);
+      m_busDevice->GetProcessor()->AddCommand(command);
+      bHandled = false;
+      break;
+    }
+  }
+  else
+  {
+    CStdString strLog;
+    strLog.Format("ignoring frame: destination: %u != %u", command.destination, (uint8_t)m_busDevice->GetMyLogicalAddress());
+    m_busDevice->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+    bHandled = false;
+  }
+
+  return bHandled;
+}
+
+bool CCECCommandHandler::HandleDeviceCecVersion(const cec_command &command)
+{
+  if (command.parameters.size == 1)
+  {
+    CCECBusDevice *device = GetDevice(command.initiator);
+    if (device)
+      device->SetCecVersion((cec_version) command.parameters[0]);
+  }
+
+  return true;
+}
+
+bool CCECCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command)
+{
+  CCECBusDevice *device = GetDevice(command.initiator);
+  if (device)
+    device->SetVendorId(command.parameters);
+
+  return true;
+}
+
+bool CCECCommandHandler::HandleDeviceVendorId(const cec_command &command)
+{
+  CCECBusDevice *device = GetDevice(command.initiator);
+  if (device)
+    device->SetVendorId(command.parameters);
+
+  return true;
+}
+
+bool CCECCommandHandler::HandleGetCecVersion(const cec_command &command)
+{
+  CCECBusDevice *device = GetDevice(command.initiator);
+  if (device)
+    return device->ReportCECVersion();
+
+  return false;
+}
+
+bool CCECCommandHandler::HandleGiveDeckStatus(const cec_command &command)
+{
+  CCECBusDevice *device = GetDevice(command.initiator);
+  if (device)
+    return device->ReportDeckStatus();
+
+  return false;
+}
+
+bool CCECCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
+{
+  CCECBusDevice *device = GetDevice(command.initiator);
+  if (device)
+    return device->ReportPowerState();
+
+  return false;
+}
+
+bool CCECCommandHandler::HandleGiveDeviceVendorId(const cec_command &command)
+{
+  CCECBusDevice *device = GetDevice(command.initiator);
+  if (device)
+    return device->ReportVendorID();
+
+  return false;
+}
+
+bool CCECCommandHandler::HandleGiveOSDName(const cec_command &command)
+{
+  CCECBusDevice *device = GetDevice(command.initiator);
+  if (device)
+    return device->ReportOSDName();
+
+  return false;
+}
+
+bool CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command)
+{
+  CCECBusDevice *device = GetThisDevice();
+  if (device)
+    return device->BroadcastPhysicalAddress();
+
+  return false;
+}
+
+bool CCECCommandHandler::HandleMenuRequest(const cec_command &command)
+{
+  if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_QUERY)
+  {
+    CCECBusDevice *device = GetDevice(command.initiator);
+    if (device)
+      return device->ReportMenuState();
+  }
+  return false;
+}
+
+bool CCECCommandHandler::HandleReportPowerStatus(const cec_command &command)
+{
+  if (command.parameters.size == 1)
+  {
+    CCECBusDevice *device = GetDevice(command.initiator);
+    if (device)
+      device->SetPowerStatus((cec_power_status) command.parameters[0]);
+  }
+  return true;
+}
+
+bool CCECCommandHandler::HandleRequestActiveSource(const cec_command &command)
+{
+  CStdString strLog;
+  strLog.Format(">> %i requests active source", (uint8_t) command.initiator);
+  m_busDevice->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+  CCECBusDevice *device = GetThisDevice();
+  if (device)
+    return device->BroadcastActiveSource();
+  return false;
+}
+
+bool CCECCommandHandler::HandleRoutingChange(const cec_command &command)
+{
+  if (command.parameters.size == 4)
+  {
+    uint16_t iOldAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
+    uint16_t iNewAddress = ((uint16_t)command.parameters[2] << 8) | ((uint16_t)command.parameters[3]);
+
+    CCECBusDevice *device = GetDevice(command.initiator);
+    if (device)
+      device->SetPhysicalAddress(iNewAddress, iOldAddress);
+  }
+  return true;
+}
+
+bool CCECCommandHandler::HandleSetMenuLanguage(const cec_command &command)
+{
+  if (command.parameters.size == 3)
+  {
+    CCECBusDevice *device = GetDevice(command.initiator);
+    if (device)
+    {
+      cec_menu_language language;
+      language.device = command.initiator;
+      for (uint8_t iPtr = 0; iPtr < 4; iPtr++)
+        language.language[iPtr] = command.parameters[iPtr];
+      language.language[3] = 0;
+      device->SetMenuLanguage(language);
+    }
+  }
+  return true;
+}
+
+bool CCECCommandHandler::HandleSetStreamPath(const cec_command &command)
+{
+  if (command.parameters.size >= 2)
+  {
+    int streamaddr = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
+    CStdString strLog;
+    strLog.Format(">> %i requests stream path from physical address %04x", command.initiator, streamaddr);
+    m_busDevice->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+    if (streamaddr == m_busDevice->GetMyPhysicalAddress())
+    {
+      CCECBusDevice *device = GetThisDevice();
+      if (device)
+        return device->BroadcastActiveSource();
+      return false;
+    }
+  }
+  return true;
+}
+
+bool CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
+{
+  if (command.parameters.size > 0)
+  {
+    m_busDevice->GetProcessor()->AddKey();
+
+    if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
+    {
+      CStdString strLog;
+      strLog.Format("key pressed: %1x", command.parameters[0]);
+      m_busDevice->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+
+      m_busDevice->GetProcessor()->SetCurrentButton((cec_user_control_code) command.parameters[0]);
+    }
+  }
+  return true;
+}
+
+bool CCECCommandHandler::HandleUserControlRelease(const cec_command &command)
+{
+  m_busDevice->GetProcessor()->AddKey();
+  return true;
+}
+
+void CCECCommandHandler::UnhandledCommand(const cec_command &command)
+{
+  CStdString strLog;
+  strLog.Format("unhandled command with opcode %02x from address %d", command.opcode, command.initiator);
+  m_busDevice->AddLog(CEC_LOG_DEBUG, strLog);
+}
+
+CCECBusDevice *CCECCommandHandler::GetDevice(cec_logical_address iLogicalAddress) const
+{
+  CCECBusDevice *device = NULL;
+
+  if (iLogicalAddress >= CECDEVICE_TV && iLogicalAddress <= CECDEVICE_BROADCAST)
+    device = m_busDevice->GetProcessor()->m_busDevices[iLogicalAddress];
+
+  return device;
+}
+
+CCECBusDevice *CCECCommandHandler::GetThisDevice(void) const
+{
+  return m_busDevice->GetProcessor()->m_busDevices[m_busDevice->GetMyLogicalAddress()];
+}
diff --git a/src/lib/implementations/CECCommandHandler.h b/src/lib/implementations/CECCommandHandler.h
new file mode 100644 (file)
index 0000000..0505c7c
--- /dev/null
@@ -0,0 +1,75 @@
+#pragma once
+/*
+ * This file is part of the libCEC(R) library.
+ *
+ * libCEC(R) is Copyright (C) 2011 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 <cectypes.h>
+
+namespace CEC
+{
+  class CCECBusDevice;
+
+  class CCECCommandHandler
+  {
+  public:
+    CCECCommandHandler(CCECBusDevice *busDevice);
+    virtual ~CCECCommandHandler(void) {};
+
+    virtual bool HandleCommand(const cec_command &command);
+    virtual cec_vendor_id GetVendorId(void) { return CEC_VENDOR_UNKNOWN; };
+
+  protected:
+    bool HandleDeviceCecVersion(const cec_command &command);
+    bool HandleDeviceVendorCommandWithId(const cec_command &command);
+    bool HandleDeviceVendorId(const cec_command &command);
+    bool HandleGetCecVersion(const cec_command &command);
+    bool HandleGiveDeckStatus(const cec_command &command);
+    bool HandleGiveDevicePowerStatus(const cec_command &command);
+    bool HandleGiveDeviceVendorId(const cec_command &command);
+    bool HandleGiveOSDName(const cec_command &command);
+    bool HandleGivePhysicalAddress(const cec_command &command);
+    bool HandleMenuRequest(const cec_command &command);
+    bool HandleReportPowerStatus(const cec_command &command);
+    bool HandleRequestActiveSource(const cec_command &command);
+    bool HandleRoutingChange(const cec_command &command);
+    bool HandleSetMenuLanguage(const cec_command &command);
+    bool HandleSetStreamPath(const cec_command &command);
+    bool HandleUserControlPressed(const cec_command &command);
+    bool HandleUserControlRelease(const cec_command &command);
+    void UnhandledCommand(const cec_command &command);
+
+    void SendToCommandBuffer(const cec_command &command);
+
+    CCECBusDevice *GetDevice(cec_logical_address iLogicalAddress) const;
+    CCECBusDevice *GetThisDevice(void) const;
+    CCECBusDevice *m_busDevice;
+  };
+};
diff --git a/src/lib/implementations/SLCommandHandler.cpp b/src/lib/implementations/SLCommandHandler.cpp
new file mode 100644 (file)
index 0000000..144cfc9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the libCEC(R) library.
+ *
+ * libCEC(R) is Copyright (C) 2011 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 "SLCommandHandler.h"
+
+using namespace CEC;
+
+CSLCommandHandler::CSLCommandHandler(CCECBusDevice *busDevice) :
+    CCECCommandHandler(busDevice)
+{
+}
diff --git a/src/lib/implementations/SLCommandHandler.h b/src/lib/implementations/SLCommandHandler.h
new file mode 100644 (file)
index 0000000..76a9970
--- /dev/null
@@ -0,0 +1,45 @@
+#pragma once
+/*
+ * This file is part of the libCEC(R) library.
+ *
+ * libCEC(R) is Copyright (C) 2011 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 "CECCommandHandler.h"
+
+namespace CEC
+{
+  class CSLCommandHandler : public CCECCommandHandler
+  {
+  public:
+    CSLCommandHandler(CCECBusDevice *busDevice);
+    virtual ~CSLCommandHandler(void) {};
+    virtual cec_vendor_id GetVendorId(void) { return CEC_VENDOR_LG; };
+  };
+};
index d323708214c8b602a9edc1dd02f0ead087a3b635..e362396e6cd471d41d4d562658fbc8adc5c54ec0 100644 (file)
@@ -46,7 +46,7 @@ CSerialPort::~CSerialPort()
   Close();
 }
 
-int8_t CSerialPort::Write(const cec_adapter_message &data)
+int8_t CSerialPort::Write(CCECAdapterMessagePtr data)
 {
   fd_set port;
 
@@ -59,7 +59,7 @@ int8_t CSerialPort::Write(const cec_adapter_message &data)
 
   int32_t byteswritten = 0;
 
-  while (byteswritten < (int32_t) data.size())
+  while (byteswritten < (int32_t) data->size())
   {
     FD_ZERO(&port);
     FD_SET(m_fd, &port);
@@ -70,7 +70,7 @@ int8_t CSerialPort::Write(const cec_adapter_message &data)
       return -1;
     }
 
-    returnv = write(m_fd, data.packet.data + byteswritten, data.size() - byteswritten);
+    returnv = write(m_fd, data->packet.data + byteswritten, data->size() - byteswritten);
     if (returnv == -1)
     {
       m_error = strerror(errno);
index 4d1be825c77d373a5eaacc2092c9f39b13b11dc9..4fb29541008441c8255207615dc9b02e0f07e6f0 100644 (file)
@@ -22,6 +22,7 @@
 #include <cectypes.h>
 #include <string>
 #include <stdint.h>
+#include "../AdapterCommunication.h"
 #include "../platform/threads.h"
 
 #ifndef __WINDOWS__
@@ -46,7 +47,7 @@ namespace CEC
       bool IsOpen();
       void Close();
 
-      int8_t Write(const cec_adapter_message &data);
+      int8_t Write(CCECAdapterMessagePtr data);
       int32_t Read(uint8_t* data, uint32_t len, uint64_t iTimeoutMs = 0);
 
       std::string GetError() { return m_error; }
index de2a9a394171f8c60367038cac90592953223afd..b028ff32e5e8810f5bb01bcb70cf8aa2810722db 100644 (file)
@@ -166,14 +166,14 @@ void CSerialPort::Close(void)
   }
 }
 
-int8_t CSerialPort::Write(const cec_adapter_message &data)
+int8_t CSerialPort::Write(CCECAdapterMessagePtr data)
 {
   CLockObject lock(&m_mutex);
   DWORD iBytesWritten = 0;
   if (!m_bIsOpen)
     return -1;
 
-  if (!WriteFile(m_handle, data.packet.data, data.size(), &iBytesWritten, NULL))
+  if (!WriteFile(m_handle, data->packet.data, data->size(), &iBytesWritten, NULL))
   {
     m_error = "Error while writing to COM port";
     FormatWindowsError(GetLastError(), m_error);
index 2939c6034f646e33aa4067b94205342ef7dd1909..d30d982053673bfa341b07688c00973d2f5425a1 100644 (file)
@@ -35,6 +35,7 @@
 #include <cstdio>
 #include <fcntl.h>
 #include <iostream>
+#include <fstream>
 #include <string>
 #include <sstream>
 #include "../lib/platform/threads.h"
 using namespace CEC;
 using namespace std;
 
-#define CEC_TEST_CLIENT_VERSION 7
+#define CEC_TEST_CLIENT_VERSION 8
 
 #include <cecloader.h>
 
+int        g_cecLogLevel = CEC_LOG_ALL;
+int        g_iLogicalAddress = CECDEVICE_PLAYBACKDEVICE1;
+ofstream   g_logOutput;
+bool       g_bShortLog = false;
+CStdString g_strPort;
+
 inline bool HexStrToInt(const std::string& data, uint8_t& value)
 {
   int iTmp(0);
@@ -104,25 +111,42 @@ void flush_log(ICECAdapter *cecParser)
   cec_log_message message;
   while (cecParser && cecParser->GetNextLogMessage(&message))
   {
-    switch (message.level)
+    if ((message.level & g_cecLogLevel) == message.level)
     {
-    case CEC_LOG_ERROR:
-      cout << "ERROR:   ";
-      break;
-    case CEC_LOG_WARNING:
-      cout << "WARNING: ";
-      break;
-    case CEC_LOG_NOTICE:
-      cout << "NOTICE:  ";
-      break;
-    case CEC_LOG_DEBUG:
-      cout << "DEBUG:   ";
-      break;
-    }
+      CStdString strLevel;
+      switch (message.level)
+      {
+      case CEC_LOG_ERROR:
+        strLevel = "ERROR:   ";
+        break;
+      case CEC_LOG_WARNING:
+        strLevel = "WARNING: ";
+        break;
+      case CEC_LOG_NOTICE:
+        strLevel = "NOTICE:  ";
+        break;
+      case CEC_LOG_TRAFFIC:
+        strLevel = "TRAFFIC: ";
+        break;
+      case CEC_LOG_DEBUG:
+        strLevel = "DEBUG:   ";
+        break;
+      default:
+        break;
+      }
 
-    CStdString strMessageTmp;
-    strMessageTmp.Format("[%16lld]\t%s", message.time, message.message);
-    cout << strMessageTmp.c_str() << endl;
+      CStdString strFullLog;
+      strFullLog.Format("%s[%16lld]\t%s", strLevel.c_str(), message.time, message.message);
+      cout << strFullLog.c_str() << endl;
+
+      if (g_logOutput.is_open())
+      {
+        if (g_bShortLog)
+          g_logOutput << message.message << endl;
+        else
+          g_logOutput << strFullLog.c_str() << endl;
+      }
+    }
   }
 }
 
@@ -154,11 +178,19 @@ void show_help(const char* strExec)
       strExec << " {-h|--help|-l|--list-devices|[COM PORT]}" << endl <<
       endl <<
       "parameters:" << endl <<
-      "\t-h --help            Shows this help text" << endl <<
-      "\t-l --list-devices    List all devices on this system" << endl <<
-      "\t[COM PORT]           The com port to connect to. If no COM port is given, the client tries to connect to the first device that is detected" << endl <<
+      "  -h --help                   Shows this help text" << endl <<
+      "  -l --list-devices           List all devices on this system" << endl <<
+      "  -la --logical-address {a}   The logical address to use." << endl <<
+      "  -f --log-file {file}        Writes all libCEC log message to a file" << 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 <<
+      "  [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 <<
       endl <<
-      "Type 'h' or 'help' and press enter after starting the client to display all available commands" << endl;
+      "Type 'h' or 'help' and press enter after starting the client to display all " << endl <<
+      "available commands" << endl;
 }
 
 void show_console_help(void)
@@ -168,7 +200,7 @@ void show_console_help(void)
   "Available commands:" << endl <<
   endl <<
   "tx {bytes}                transfer bytes over the CEC line." << endl <<
-  "txn {bytes}               transfer bytes and don't wait for an ACK reply." << endl <<
+  "txn {bytes}               transfer bytes but don't wait for transmission ACK." << endl <<
   "[tx 40 00 FF 11 22 33]    sends bytes 0x40 0x00 0xFF 0x11 0x22 0x33" << endl <<
   endl <<
   "on {address}              power on the device with the given logical address." << endl <<
@@ -186,6 +218,20 @@ void show_console_help(void)
   "osd {addr} {string}       set OSD message on the specified device." << endl <<
   "[osd 0 Test Message]      displays 'Test Message' on the TV" << endl <<
   endl <<
+  "ver {addr}                get the CEC version of the specified device." << endl <<
+  "[ver 0]                   get the CEC version of the TV" << endl <<
+  endl <<
+  "ven {addr}                get the vendor ID of the specified device." << endl <<
+  "[ven 0]                   get the vendor ID of the TV" << endl <<
+  endl <<
+  "lang {addr}               get the menu language of the specified device." << endl <<
+  "[lang 0]                  get the menu language of the TV" << endl <<
+  endl <<
+  "pow {addr}                get the power status of the specified device." << endl <<
+  "[pow 0]                   get the power status of the TV" << endl <<
+  endl <<
+  "[mon] {1|0}               enable or disable CEC bus monitoring." << endl <<
+  "[log] {1 - 31}            change the log level. see cectypes.h for values." << endl <<
   "[ping]                    send a ping command to the CEC adapter." << endl <<
   "[bl]                      to let the adapter enter the bootloader, to upgrade" << endl <<
   "                          the flash rom." << endl <<
@@ -219,8 +265,96 @@ int main (int argc, char *argv[])
   fcntl(0, F_SETFL, flags);
 #endif
 
-  string strPort;
-  if (argc < 2)
+  int iArgPtr = 1;
+  while (iArgPtr < argc)
+  {
+    if (argc >= iArgPtr + 1)
+    {
+      if (!strcmp(argv[iArgPtr], "-f") ||
+          !strcmp(argv[iArgPtr], "--log-file") ||
+          !strcmp(argv[iArgPtr], "-sf") ||
+          !strcmp(argv[iArgPtr], "--short-log-file"))
+      {
+        if (argc >= iArgPtr + 2)
+        {
+          g_logOutput.open(argv[iArgPtr + 1]);
+          g_bShortLog = (!strcmp(argv[iArgPtr], "-sf") || !strcmp(argv[iArgPtr], "--short-log-file"));
+          iArgPtr += 2;
+        }
+        else
+        {
+          cout << "== skipped log-file parameter: no file given ==" << endl;
+          ++iArgPtr;
+        }
+      }
+      else if (!strcmp(argv[iArgPtr], "-d") ||
+          !strcmp(argv[iArgPtr], "--log-level"))
+      {
+        if (argc >= iArgPtr + 2)
+        {
+          int iNewLevel = atoi(argv[iArgPtr + 1]);
+          if (iNewLevel >= CEC_LOG_ERROR && iNewLevel <= CEC_LOG_ALL)
+          {
+            g_cecLogLevel = iNewLevel;
+            cout << "log level set to " << argv[iArgPtr + 1] << endl;
+          }
+          else
+          {
+            cout << "== skipped log-level parameter: invalid level '" << argv[iArgPtr + 1] << "' ==" << endl;
+          }
+          iArgPtr += 2;
+        }
+        else
+        {
+          cout << "== skipped log-level parameter: no level given ==" << endl;
+          ++iArgPtr;
+        }
+      }
+      else if (!strcmp(argv[iArgPtr], "-la") ||
+               !strcmp(argv[iArgPtr], "--logical-address"))
+      {
+        if (argc >= iArgPtr + 2)
+        {
+          int iNewAddress = atoi(argv[iArgPtr + 1]);
+          if (iNewAddress >= 0 && iNewAddress <= 15)
+          {
+            g_iLogicalAddress = iNewAddress;
+            cout << "logical address set to " << argv[iArgPtr + 1] << endl;
+          }
+          else
+          {
+            cout << "== skipped logical-address parameter: invalid address '" << argv[iArgPtr + 1] << "' ==" << endl;
+          }
+          iArgPtr += 2;
+        }
+        else
+        {
+          cout << "== skipped logical-address parameter: no address given ==" << endl;
+          ++iArgPtr;
+        }
+      }
+      else if (!strcmp(argv[iArgPtr], "--list-devices") ||
+               !strcmp(argv[iArgPtr], "-l"))
+      {
+        list_devices(parser);
+        UnloadLibCec(parser);
+        return 0;
+      }
+      else if (!strcmp(argv[iArgPtr], "--help") ||
+               !strcmp(argv[iArgPtr], "-h"))
+      {
+        show_help(argv[0]);
+        UnloadLibCec(parser);
+        return 0;
+      }
+      else
+      {
+        g_strPort = argv[iArgPtr++];
+      }
+    }
+  }
+
+  if (g_strPort.IsEmpty())
   {
     cout << "no serial port given. trying autodetect: ";
     cec_adapter devices[10];
@@ -234,29 +368,16 @@ int main (int argc, char *argv[])
     else
     {
       cout << endl << " path:     " << devices[0].path << endl <<
-              " com port: " << devices[0].comm << endl << endl;
-      strPort = devices[0].comm;
+          " com port: " << devices[0].comm << endl << endl;
+      g_strPort = devices[0].comm;
     }
   }
-  else if (!strcmp(argv[1], "--list-devices") || !strcmp(argv[1], "-l"))
-  {
-    list_devices(parser);
-    UnloadLibCec(parser);
-    return 0;
-  }
-  else if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))
-  {
-    show_help(argv[0]);
-    return 0;
-  }
-  else
-  {
-    strPort = argv[1];
-  }
 
-  if (!parser->Open(strPort.c_str()))
+  parser->SetLogicalAddress((cec_logical_address) g_iLogicalAddress);
+
+  if (!parser->Open(g_strPort.c_str()))
   {
-    cout << "unable to open the device on port " << strPort << endl;
+    cout << "unable to open the device on port " << g_strPort << endl;
     flush_log(parser);
     UnloadLibCec(parser);
     return 1;
@@ -276,6 +397,10 @@ int main (int argc, char *argv[])
   {
     flush_log(parser);
 
+    /* just ignore the command buffer and clear it */
+    cec_command dummy;
+    while (parser && parser->GetNextCommand(&dummy)) {}
+
     string input;
     getline(cin, input);
     cin.clear();
@@ -295,7 +420,10 @@ int main (int argc, char *argv[])
           while (GetWord(input, strvalue) && HexStrToInt(strvalue, ivalue))
             bytes.push_back(ivalue);
 
-          parser->Transmit(bytes, command == "tx");
+          if (command == "txn")
+            bytes.transmit_timeout = 0;
+
+          parser->Transmit(bytes);
         }
         else if (command == "on")
         {
@@ -365,10 +493,111 @@ int main (int argc, char *argv[])
         {
           parser->PingAdapter();
         }
+        else if (command == "mon")
+        {
+          CStdString strEnable;
+          if (GetWord(input, strEnable) && (strEnable.Equals("0") || strEnable.Equals("1")))
+          {
+            parser->SwitchMonitoring(strEnable.Equals("1"));
+          }
+        }
         else if (command == "bl")
         {
           parser->StartBootloader();
         }
+        else if (command == "lang")
+        {
+          CStdString strDev;
+          if (GetWord(input, strDev))
+          {
+            int iDev = atoi(strDev);
+            if (iDev >= 0 && iDev < 15)
+            {
+              CStdString strLog;
+              cec_menu_language language;
+              if (parser->GetDeviceMenuLanguage((cec_logical_address) iDev, &language))
+                strLog.Format("menu language '%s'", language.language);
+              else
+                strLog = "failed!";
+              cout << strLog.c_str() << endl;
+            }
+          }
+        }
+        else if (command == "ven")
+        {
+          CStdString strDev;
+          if (GetWord(input, strDev))
+          {
+            int iDev = atoi(strDev);
+            if (iDev >= 0 && iDev < 15)
+            {
+              uint64_t iVendor = parser->GetDeviceVendorId((cec_logical_address) iDev);
+              CStdString strLog;
+              strLog.Format("vendor id: %06x", iVendor);
+              cout << strLog.c_str() << endl;
+            }
+          }
+        }
+        else if (command == "ver")
+        {
+          CStdString strDev;
+          if (GetWord(input, strDev))
+          {
+            int iDev = atoi(strDev);
+            if (iDev >= 0 && iDev < 15)
+            {
+              cec_version iVersion = parser->GetDeviceCecVersion((cec_logical_address) iDev);
+              switch (iVersion)
+              {
+              case CEC_VERSION_1_2:
+                cout << "CEC version 1.2" << endl;
+                break;
+              case CEC_VERSION_1_2A:
+                cout << "CEC version 1.2a" << endl;
+                break;
+              case CEC_VERSION_1_3:
+                cout << "CEC version 1.3" << endl;
+                break;
+              case CEC_VERSION_1_3A:
+                cout << "CEC version 1.3a" << endl;
+                break;
+              default:
+                cout << "unknown CEC version" << endl;
+                break;
+              }
+            }
+          }
+        }
+        else if (command == "pow")
+        {
+          CStdString strDev;
+          if (GetWord(input, strDev))
+          {
+            int iDev = atoi(strDev);
+            if (iDev >= 0 && iDev < 15)
+            {
+              cec_power_status iPower = parser->GetDevicePowerStatus((cec_logical_address) iDev);
+              switch (iPower)
+              {
+              case CEC_POWER_STATUS_ON:
+                cout << "powered on" << endl;
+                break;
+              case CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY:
+                cout << "on -> standby" << endl;
+                break;
+              case CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON:
+                cout << "standby -> on" << endl;
+                break;
+              case CEC_POWER_STATUS_STANDBY:
+                cout << "standby" << endl;
+                break;
+              default:
+                cout << "unknown power status" << endl;
+                break;
+              }
+            }
+          }
+        }
         else if (command == "r")
         {
           cout << "closing the connection" << endl;
@@ -376,7 +605,7 @@ int main (int argc, char *argv[])
           flush_log(parser);
 
           cout << "opening a new connection" << endl;
-          parser->Open(strPort.c_str());
+          parser->Open(g_strPort.c_str());
           flush_log(parser);
 
           cout << "setting active view" << endl;
@@ -390,6 +619,19 @@ int main (int argc, char *argv[])
         {
           bContinue = false;
         }
+        else if (command == "log")
+        {
+          CStdString strLevel;
+          if (GetWord(input, strLevel))
+          {
+            int iNewLevel = atoi(strLevel);
+            if (iNewLevel >= CEC_LOG_ERROR && iNewLevel <= CEC_LOG_ALL)
+            {
+              g_cecLogLevel = iNewLevel;
+              cout << "log level changed to " << strLevel.c_str() << endl;
+            }
+          }
+        }
       }
       if (bContinue)
         cout << "waiting for input" << endl;
@@ -401,5 +643,9 @@ int main (int argc, char *argv[])
   parser->Close();
   flush_log(parser);
   UnloadLibCec(parser);
+
+  if (g_logOutput.is_open())
+    g_logOutput.close();
+
   return 0;
 }