build
+include/boost
+
+project/boost-1_46_1-xbmc-win32
project/Debug/
project/ipch/
project/libcec.sdf
+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
-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
-AC_INIT([libcec], 0:7:0)
+AC_INIT([libcec], 0:8:0)
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
AC_PROG_CXX
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)
+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
/*!
* @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
* @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;
};
};
/*!
* @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
/*!
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
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
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;
}
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
{
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
{
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
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];
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; }
} 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;
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)
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
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
#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
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
--- /dev/null
+@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
File "..\COPYING"
File "..\libcec.dll"
File "..\libcec.lib"
- File "..\libcec.pdb"
File "..\pthreadVC2.dll"
File "..\README"
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
<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" />
<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
<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'">
<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>
#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;
}
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");
//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
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;
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
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))
{
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))
{
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();
}
-
#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;
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;
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;
};
};
#include "CECProcessor.h"
#include "AdapterCommunication.h"
+#include "devices/CECBusDevice.h"
#include "LibCEC.h"
#include "util/StdString.h"
#include "platform/timeutils.h"
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");
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;
}
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)
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 */)
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))
{
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;
}
#include <string>
#include <cectypes.h>
+#include "AdapterCommunication.h"
#include "platform/threads.h"
#include "util/buffer.h"
+#include "util/StdString.h"
class CSerialPort;
{
class CLibCEC;
class CAdapterCommunication;
+ class CCECBusDevice;
class CCECProcessor : public CThread
{
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;
};
};
#include "AdapterCommunication.h"
#include "AdapterDetection.h"
#include "CECProcessor.h"
+#include "devices/CECBusDevice.h"
#include "util/StdString.h"
#include "platform/timeutils.h"
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)
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)
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)
}
}
-void CLibCEC::AddCommand(cec_command &command)
+void CLibCEC::AddCommand(const cec_command &command)
{
if (m_commandBuffer.Push(command))
{
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);
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);
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;
}
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;
+}
+
//@}
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 \
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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;
+ };
+};
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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);
+ };
+};
--- /dev/null
+/*
+ * 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()];
+}
--- /dev/null
+#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;
+ };
+};
--- /dev/null
+/*
+ * 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)
+{
+}
--- /dev/null
+#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; };
+ };
+};
Close();
}
-int8_t CSerialPort::Write(const cec_adapter_message &data)
+int8_t CSerialPort::Write(CCECAdapterMessagePtr data)
{
fd_set port;
int32_t byteswritten = 0;
- while (byteswritten < (int32_t) data.size())
+ while (byteswritten < (int32_t) data->size())
{
FD_ZERO(&port);
FD_SET(m_fd, &port);
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);
#include <cectypes.h>
#include <string>
#include <stdint.h>
+#include "../AdapterCommunication.h"
#include "../platform/threads.h"
#ifndef __WINDOWS__
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; }
}
}
-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);
#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);
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;
+ }
+ }
}
}
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)
"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 <<
"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 <<
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];
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;
{
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();
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")
{
{
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;
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;
{
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;
parser->Close();
flush_log(parser);
UnloadLibCec(parser);
+
+ if (g_logOutput.is_open())
+ g_logOutput.close();
+
return 0;
}