From: Lars Op den Kamp Date: Wed, 27 Jun 2012 08:30:58 +0000 (+0200) Subject: Merge branch 'master' into release X-Git-Tag: upstream/2.2.0~1^2~22 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=d968b075dd7f0a4a96175045dbf4e20b7aa32872;hp=b9761e8922a75fbff7887cf7fd5b6e4e3eba0709;p=deb_libcec.git Merge branch 'master' into release --- diff --git a/ChangeLog b/ChangeLog index 3d69d4f..b8b13b9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,62 @@ +libcec (1.7.2-1) unstable; urgency=low + + * changed/added: + * display a warning message if libCEC was not compiled with adapter + detection for the target platform + * panasonic: added a vendor command for panasonic that will enable routing + of some more buttons on the remote (guide, text, ...) + * standby command won't be forwarded more than once every 10 seconds + * added vendor ids for Akai, AOC, Benq, Daewoo, Grundig, Medion, Sharp and + Vizio + * cec-client: don't display debug output by default in 'cec-client -l' + + * interface changes: + * added a callback for clients that is called when a source is + (de)activated, so the client can respond to this action + * added cec_command::PushArray() + + * fixed: + * command forwarding. fixes player not going into standby mode + * missing virtual keyword in CCECCommandHandler:: + GetReplacementDeviceType() + * replace the handler (if needed) in GetHandler() + * reply with abort reason 'invalid operand' to vendor commands that we + don't understand + * changed all Handle...() commands to return a cec_abort_reason and send + the correct abort reason to the initiator as a response to directly + addressed messages + * changed 'couldn't change the ackmask: the connection is closed' into a + debug message + * don't send active source commands when the physical address is invalid or + unknown + * set the power status of a device to 'powered on' after it sends a stream + path change. can save an unneeded power state update request + * removed dupe code around delayed activate source commands. check for + delayed active source commands every 5 seconds instead of 15 seconds. + * panasonic: reset m_bPowerUpEventReceived in CVLCommandHandler when the + device lets us know it went into standby mode. fixes possibly failed + active source switches after it succeeded once + * panasonic: fixed delayed source switch for panasonic + * panasonic: mark the tv as powered up once it sends the audiomode request + * set the physical address of each device controlled by a CCECClient if + it's valid + * Windows could get stuck in a loop in case there an error occured while + reading registry entries + * ABI fixes (binary compat with v1.2 for Windows and v1.5.2 for others) + * replace the handler directly after getting the vendor id of a device when + registering a client + * copy the class member values of the old handler when creating a new + command handler, or a delayed activate source will get lost when the + handler is switched + * cec-client: wrong client version + * Makefile cleanups. don't link cec-client and cec-config against libudev + and liblockdev + * pin libcec to the same version as libcec1 + * LibCecSharp: update the local configuration after connecting + * LibCecSharp: better handling of callbacks + + -- Pulse-Eight Packaging Wed, 27 Jun 2012 02:06:00 +0100 + libcec (1.7.1-1) unstable; urgency=low * changed/added: diff --git a/configure.ac b/configure.ac index ba40f03..3098d00 100644 --- a/configure.ac +++ b/configure.ac @@ -1,15 +1,44 @@ -AC_INIT([libcec], 1:7:0) +AC_PREREQ(2.59) +AC_INIT([libcec], [1:7:0], [http://libcec.pulse-eight.com/]) AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) AC_PROG_CXX AC_PROG_LIBTOOL +# search for pthread, required by all targets +AC_SEARCH_LIBS([pthread_create],[pthread],, + AC_MSG_ERROR("required library 'pthread' is missing")) + +# search for dlopen, required by all targets +AC_SEARCH_LIBS([dlopen], [dl], + [test "$ac_cv_search_dlopen" = "none required" || LIBS_DL=$ac_cv_search_dlopen], + AC_MSG_ERROR("required library 'dl' is missing")) + + +# platform specific libs, required by all targets +case "${host}" in + *-*-linux*) + LIBS+=" -lrt" + ;; + *-apple-darwin*) + ;; + *-freebsd*) + ;; +esac + +libs_client=$LIBS + +# search for udev and lockdev, only required by libCEC has_libudev="yes" case "${host}" in *-*-linux*) - PKG_CHECK_MODULES([UDEV],[libudev],,[has_libudev="no"]; AC_MSG_WARN("library 'udev' is missing - adapter detection will not be available")) - AC_CHECK_HEADER(lockdev.h,, AC_MSG_ERROR("required library 'liblockdev' is missing")) - LIBS+=" -lrt -llockdev" + PKG_CHECK_MODULES([UDEV],[libudev],, + [has_libudev="no"]; AC_MSG_WARN("library 'udev' is missing - adapter detection will not be available")) + + AC_CHECK_HEADER(lockdev.h,, + AC_MSG_ERROR("required library 'liblockdev' is missing")) + + LIBS+=" -llockdev" ;; *-apple-darwin*) has_libudev="no"; @@ -20,23 +49,20 @@ case "${host}" in ;; esac +# mark udev as available if it was found, so we can include adapter autodetection code if test "x$has_libudev" != "xno"; then INCLUDES="$INCLUDES $UDEV_CFLAGS";LIBS="$LIBS $UDEV_LIBS" AC_DEFINE([HAVE_LIBUDEV],[1],["Define to 1 if libudev is installed"]) REQUIRES="udev" fi -AC_SEARCH_LIBS([pthread_create],[pthread],, AC_MSG_ERROR("required library 'pthread' is missing")) - -libs_pre_dl=$LIBS - AC_SEARCH_LIBS(dlopen, [dl], - [test "$ac_cv_search_dlopen" = "none required" || LIBS_DL=$ac_cv_search_dlopen], - AC_MSG_ERROR("required library 'dl' is missing")) - AC_SUBST([LIBS_DL]) -LIBS=$libs_pre_dl +LIBS_LIBCEC=$LIBS +LIBS=$libs_client CXXFLAGS="-fPIC -Wall -Wextra -Wno-missing-field-initializers $CXXFLAGS" -AC_SUBST(REQUIRES) +AC_SUBST([REQUIRES]) +AC_SUBST([LIBS]) +AC_SUBST([LIBS_LIBCEC]) AC_CONFIG_FILES([src/lib/libcec.pc]) AC_OUTPUT([Makefile src/lib/Makefile src/testclient/Makefile src/cec-config/Makefile]) diff --git a/debian/changelog b/debian/changelog index 3d69d4f..b8b13b9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,62 @@ +libcec (1.7.2-1) unstable; urgency=low + + * changed/added: + * display a warning message if libCEC was not compiled with adapter + detection for the target platform + * panasonic: added a vendor command for panasonic that will enable routing + of some more buttons on the remote (guide, text, ...) + * standby command won't be forwarded more than once every 10 seconds + * added vendor ids for Akai, AOC, Benq, Daewoo, Grundig, Medion, Sharp and + Vizio + * cec-client: don't display debug output by default in 'cec-client -l' + + * interface changes: + * added a callback for clients that is called when a source is + (de)activated, so the client can respond to this action + * added cec_command::PushArray() + + * fixed: + * command forwarding. fixes player not going into standby mode + * missing virtual keyword in CCECCommandHandler:: + GetReplacementDeviceType() + * replace the handler (if needed) in GetHandler() + * reply with abort reason 'invalid operand' to vendor commands that we + don't understand + * changed all Handle...() commands to return a cec_abort_reason and send + the correct abort reason to the initiator as a response to directly + addressed messages + * changed 'couldn't change the ackmask: the connection is closed' into a + debug message + * don't send active source commands when the physical address is invalid or + unknown + * set the power status of a device to 'powered on' after it sends a stream + path change. can save an unneeded power state update request + * removed dupe code around delayed activate source commands. check for + delayed active source commands every 5 seconds instead of 15 seconds. + * panasonic: reset m_bPowerUpEventReceived in CVLCommandHandler when the + device lets us know it went into standby mode. fixes possibly failed + active source switches after it succeeded once + * panasonic: fixed delayed source switch for panasonic + * panasonic: mark the tv as powered up once it sends the audiomode request + * set the physical address of each device controlled by a CCECClient if + it's valid + * Windows could get stuck in a loop in case there an error occured while + reading registry entries + * ABI fixes (binary compat with v1.2 for Windows and v1.5.2 for others) + * replace the handler directly after getting the vendor id of a device when + registering a client + * copy the class member values of the old handler when creating a new + command handler, or a delayed activate source will get lost when the + handler is switched + * cec-client: wrong client version + * Makefile cleanups. don't link cec-client and cec-config against libudev + and liblockdev + * pin libcec to the same version as libcec1 + * LibCecSharp: update the local configuration after connecting + * LibCecSharp: better handling of callbacks + + -- Pulse-Eight Packaging Wed, 27 Jun 2012 02:06:00 +0100 + libcec (1.7.1-1) unstable; urgency=low * changed/added: diff --git a/debian/control b/debian/control index e28796f..511fb0c 100644 --- a/debian/control +++ b/debian/control @@ -47,6 +47,6 @@ Description: USB CEC Adaptor communication Library (utility programs) Package: libcec Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libcec1 +Depends: ${shlibs:Depends}, ${misc:Depends}, libcec1 (= ${binary:Version}) Description: Transitional package libcec. diff --git a/debian/control.hardy b/debian/control.hardy index ed58c35..e92e1b2 100644 --- a/debian/control.hardy +++ b/debian/control.hardy @@ -46,6 +46,6 @@ Description: USB CEC Adaptor communication Library (utility programs) Package: libcec Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libcec1 +Depends: ${shlibs:Depends}, ${misc:Depends}, libcec1 (= ${binary:Version}) Description: Transitional package libcec. diff --git a/include/cec.h b/include/cec.h index 8b335c1..de7129d 100644 --- a/include/cec.h +++ b/include/cec.h @@ -36,7 +36,7 @@ #include "cectypes.h" -#define LIBCEC_VERSION_CURRENT CEC_SERVER_VERSION_1_7_0 +#define LIBCEC_VERSION_CURRENT CEC_SERVER_VERSION_1_7_1 namespace CEC { @@ -60,6 +60,9 @@ namespace CEC */ virtual void Close(void) = 0; +// XXX XBMC Eden for Windows has been built against 1.2.0 and Ubuntu against 1.5.2 +// we accidently broke the abi between these versions, and this will ensure the upgrade will still work +#if !defined(_WIN32) && !defined(_WIN64) /*! * @brief Set and enable the callback methods. If this method is not called, the GetNext...() methods will have to be used. * @param cbParam Parameter to pass to callback methods. @@ -67,6 +70,7 @@ namespace CEC * @return True when enabled, false otherwise. */ virtual bool EnableCallbacks(void *cbParam, ICECCallbacks *callbacks) = 0; +#endif /*! * @brief Try to find all connected CEC adapters. Only implemented on Linux and Windows at the moment. @@ -153,6 +157,9 @@ namespace CEC */ virtual bool SetPhysicalAddress(uint16_t iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS) = 0; +// XXX XBMC Eden for Windows has been built against 1.2.0 and Ubuntu against 1.5.2 +// we accidently broke the abi between these versions, and this will ensure the upgrade will still work +#if !defined(_WIN32) && !defined(_WIN64) /*! * @deprecated Use libcec_configuration instead. * @brief Enable physical address detection (if the connected adapter supports this). @@ -167,6 +174,7 @@ namespace CEC * @return True when changed, false otherwise. */ virtual bool SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort) = 0; +#endif /*! * @brief Power on the connected CEC capable devices. @@ -269,12 +277,16 @@ namespace CEC */ virtual cec_power_status GetDevicePowerStatus(cec_logical_address iLogicalAddress) = 0; +// XXX XBMC Eden for Windows has been built against 1.2.0 and Ubuntu against 1.5.2 +// we accidently broke the abi between these versions, and this will ensure the upgrade will still work +#if !defined(_WIN32) && !defined(_WIN64) /*! * @brief Get the physical address of the device with the given logical address. * @param iLogicalAddress The device to get the vendor id for. * @return The physical address or 0 if it wasn't found. */ virtual uint16_t GetDevicePhysicalAddress(cec_logical_address iLogicalAddress) = 0; +#endif /*! * @brief Sends a POLL message to a device. @@ -436,6 +448,40 @@ namespace CEC * @return True when the device was found, false otherwise */ virtual bool GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs = 10000) = 0; + +// XXX XBMC Eden for Windows has been built against 1.2.0 and Ubuntu against 1.5.2 +// we accidently broke the abi between these versions, and this will ensure the upgrade will still work +#if defined(_WIN32) || defined(_WIN64) + /*! + * @brief Set and enable the callback methods. If this method is not called, the GetNext...() methods will have to be used. + * @param cbParam Parameter to pass to callback methods. + * @param callbacks The callbacks to set. + * @return True when enabled, false otherwise. + */ + virtual bool EnableCallbacks(void *cbParam, ICECCallbacks *callbacks) = 0; + + /*! + * @deprecated Use libcec_configuration instead. + * @brief Enable physical address detection (if the connected adapter supports this). + * @return True when physical address detection was enabled, false otherwise. + */ + virtual bool EnablePhysicalAddressDetection(void) = 0; + + /*! + * @brief Changes the active HDMI port. + * @param iBaseDevice The device to which this libcec is connected. + * @param iPort The new port number. + * @return True when changed, false otherwise. + */ + virtual bool SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort) = 0; + + /*! + * @brief Get the physical address of the device with the given logical address. + * @param iLogicalAddress The device to get the vendor id for. + * @return The physical address or 0 if it wasn't found. + */ + virtual uint16_t GetDevicePhysicalAddress(cec_logical_address iLogicalAddress) = 0; +#endif }; }; @@ -443,10 +489,19 @@ namespace CEC * @brief Load the CEC adapter library. * @param strDeviceName How to present this device to other devices. * @param deviceTypes The device types to use on the CEC bus. - * @param iPhysicalAddress The physical address to assume on the bus. If set to 0, libCEC will try to autodetect the address, with the data provided via SetHDMIPort() * @return An instance of ICECAdapter or NULL on error. */ -extern "C" DECLSPEC void * CECInit(const char *strDeviceName, CEC::cec_device_type_list deviceTypes, uint16_t iPhysicalAddress = 0); +extern "C" DECLSPEC void * CECInit(const char *strDeviceName, CEC::cec_device_type_list deviceTypes); + +/*! + * @deprecated + */ +extern "C" DECLSPEC void * CECCreate(const char *strDeviceName, CEC::cec_logical_address iLogicalAddress = CEC::CECDEVICE_PLAYBACKDEVICE1, uint16_t iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS); + +/*! + * @brief Unload the CEC adapter library. + */ +extern "C" DECLSPEC void CECDestroy(CEC::ICECAdapter *instance); /*! * @brief Load the CEC adapter library. @@ -461,9 +516,4 @@ extern "C" DECLSPEC void * CECInitialise(CEC::libcec_configuration *configuratio */ extern "C" DECLSPEC bool CECStartBootloader(void); -/*! - * @brief Unload the CEC adapter library. - */ -extern "C" DECLSPEC void CECDestroy(CEC::ICECAdapter *instance); - #endif /* CECEXPORTS_H_ */ diff --git a/include/cectypes.h b/include/cectypes.h index b5ae1ec..47168a2 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -110,6 +110,9 @@ namespace CEC { #define CEC_SERIAL_DEFAULT_BAUDRATE 38400 #define CEC_CLEAR_INPUT_DEFAULT_WAIT 1000 +#define CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS 5000 +#define CEC_FORWARD_STANDBY_MIN_INTERVAL 10000 + #define CEC_MIN_LIB_VERSION 1 #define CEC_LIB_VERSION_MAJOR 1 #define CEC_LIB_VERSION_MAJOR_STR "1" @@ -673,6 +676,15 @@ typedef enum cec_vendor_id CEC_VENDOR_PHILIPS = 0x00903E, CEC_VENDOR_SONY = 0x080046, CEC_VENDOR_TOSHIBA = 0x000039, + CEC_VENDOR_AKAI = 0x0020C7, + CEC_VENDOR_AOC = 0x002467, + CEC_VENDOR_BENQ = 0x8065E9, + CEC_VENDOR_DAEWOO = 0x009053, + CEC_VENDOR_GRUNDIG = 0x00D0D5, + CEC_VENDOR_MEDION = 0x000CB8, + CEC_VENDOR_SHARP = 0x08001F, + CEC_VENDOR_VIZIO = 0x6B746D, + CEC_VENDOR_UNKNOWN = 0 } cec_vendor_id; @@ -790,10 +802,11 @@ typedef struct cec_command int32_t transmit_timeout; /**< the timeout to use in ms */ #ifdef __cplusplus - cec_command(void) - { - Clear(); - } + // @todo re-add in v2.0 (breaks ABI) + //cec_command(void) + //{ + // Clear(); + //} cec_command &operator =(const struct cec_command &command) { @@ -901,6 +914,12 @@ typedef struct cec_command return CEC_OPCODE_NONE; } + + void PushArray(size_t len, uint8_t *data) + { + for (size_t iPtr = 0; iPtr < len; iPtr++) + PushBack(data[iPtr]); + } #endif } cec_command; @@ -1116,6 +1135,7 @@ typedef int (CEC_CDECL* CBCecCommandType)(void *param, const cec_command &); typedef int (CEC_CDECL* CBCecConfigurationChangedType)(void *param, const libcec_configuration &); typedef int (CEC_CDECL* CBCecAlertType)(void *param, const libcec_alert, const libcec_parameter &); typedef int (CEC_CDECL* CBCecMenuStateChangedType)(void *param, const cec_menu_state); +typedef void (CEC_CDECL* CBCecSourceActivatedType)(void *param, const cec_logical_address, const uint8_t); typedef struct ICECCallbacks { @@ -1166,9 +1186,17 @@ typedef struct ICECCallbacks */ CBCecMenuStateChangedType CBCecMenuStateChanged; + /*! + * @brief Called when a source that's handled by this client is activated. + * @param logicalAddress The address that was just activated. + * @param bActivated 1 when activated, 0 when deactivated. + */ + CBCecSourceActivatedType CBCecSourceActivated; + #ifdef __cplusplus - ICECCallbacks(void) { Clear(); } - ~ICECCallbacks(void) { Clear(); }; + // @todo re-add in v2.0 (breaks ABI) + // ICECCallbacks(void) { Clear(); } + //~ICECCallbacks(void) { Clear(); }; void Clear(void) { @@ -1178,6 +1206,7 @@ typedef struct ICECCallbacks CBCecConfigurationChanged = NULL; CBCecAlert = NULL; CBCecMenuStateChanged = NULL; + CBCecSourceActivated = NULL; } #endif } ICECCallbacks; @@ -1193,7 +1222,8 @@ typedef enum cec_client_version CEC_CLIENT_VERSION_1_6_1 = 0x1601, CEC_CLIENT_VERSION_1_6_2 = 0x1602, CEC_CLIENT_VERSION_1_6_3 = 0x1603, - CEC_CLIENT_VERSION_1_7_0 = 0x1700 + CEC_CLIENT_VERSION_1_7_0 = 0x1700, + CEC_CLIENT_VERSION_1_7_1 = 0x1701 } cec_client_version; typedef enum cec_server_version @@ -1207,7 +1237,8 @@ typedef enum cec_server_version CEC_SERVER_VERSION_1_6_1 = 0x1601, CEC_SERVER_VERSION_1_6_2 = 0x1602, CEC_SERVER_VERSION_1_6_3 = 0x1603, - CEC_SERVER_VERSION_1_7_0 = 0x1700 + CEC_SERVER_VERSION_1_7_0 = 0x1700, + CEC_SERVER_VERSION_1_7_1 = 0x1701 } cec_server_version; typedef struct libcec_configuration @@ -1245,8 +1276,9 @@ typedef struct libcec_configuration uint8_t bMonitorOnly; /*!< won't allocate a CCECClient when starting the connection when set (same as monitor mode). added in 1.6.3 */ #ifdef __cplusplus - libcec_configuration(void) { Clear(); } - ~libcec_configuration(void) { Clear(); } + // @todo re-add in v2.0 (breaks ABI) + // libcec_configuration(void) { Clear(); } + //~libcec_configuration(void) { Clear(); } bool operator==(const libcec_configuration &other) const { @@ -1329,15 +1361,6 @@ typedef struct libcec_configuration #endif } libcec_configuration; -#ifdef UNUSED -#elif defined(__GNUC__) -#define UNUSED(x) UNUSED_ ## x __attribute__((unused)) -#elif defined(__LCLINT__) -#define UNUSED(x) /*@unused@*/ x -#else -#define UNUSED(x) x -#endif - #ifdef __cplusplus }; }; diff --git a/project/LibCecSharp.vcproj b/project/LibCecSharp.vcproj index 0f2c492..38be809 100644 --- a/project/LibCecSharp.vcproj +++ b/project/LibCecSharp.vcproj @@ -48,7 +48,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories=""$(SolutionDir)..\include";"$(SolutionDir)..\src\lib\platform\windows"" - PreprocessorDefinitions="_DEBUG" + PreprocessorDefinitions="_DEBUG;_CRT_SECURE_NO_WARNINGS" RuntimeLibrary="3" UsePrecompiledHeader="0" WarningLevel="3" @@ -122,7 +122,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories=""$(SolutionDir)..\include";"$(SolutionDir)..\src\lib\platform\windows"" - PreprocessorDefinitions="_DEBUG" + PreprocessorDefinitions="_DEBUG;_CRT_SECURE_NO_WARNINGS" RuntimeLibrary="3" UsePrecompiledHeader="0" WarningLevel="3" @@ -197,7 +197,7 @@ +#include "../lib/platform/threads/mutex.h" #include #include #include "../../include/cec.h" +#include #using @@ -253,14 +254,22 @@ namespace CecSharp public enum class CecVendorId { - Samsung = 0x00F0, - LG = 0xE091, - Panasonic = 0x8045, - Pioneer = 0xE036, - Onkyo = 0x09B0, - Yamaha = 0xA0DE, - Philips = 0x903E, + Samsung = 0x0000F0, + LG = 0x00E091, + Panasonic = 0x008045, + Pioneer = 0x00E036, + Onkyo = 0x0009B0, + Yamaha = 0x00A0DE, + Philips = 0x00903E, Sony = 0x080046, + Toshiba = 0x000039, + Akai = 0x0020C7, + Benq = 0x8065E9, + Daewoo = 0x009053, + Grundig = 0x00D0D5, + Medion = 0x000CB8, + Sharp = 0x08001F, + Vizio = 0x6B746D, Unknown = 0 }; @@ -356,9 +365,10 @@ namespace CecSharp Version1_5_3 = 0x1503, Version1_6_0 = 0x1600, Version1_6_1 = 0x1601, - Version1_6_2 = 0x1602, - Version1_6_3 = 0x1603, - Version1_7_0 = 0x1700 + Version1_6_2 = 0x1602, + Version1_6_3 = 0x1603, + Version1_7_0 = 0x1700, + Version1_7_1 = 0x1701 }; public enum class CecServerVersion @@ -370,9 +380,10 @@ namespace CecSharp Version1_5_3 = 0x1503, Version1_6_0 = 0x1600, Version1_6_1 = 0x1601, - Version1_6_2 = 0x1602, - Version1_6_3 = 0x1603, - Version1_7_0 = 0x1700 + Version1_6_2 = 0x1602, + Version1_6_3 = 0x1603, + Version1_7_0 = 0x1700, + Version1_7_1 = 0x1701 }; public ref class CecAdapter @@ -696,65 +707,126 @@ namespace CecSharp typedef int (__stdcall *CONFIGCB) (const CEC::libcec_configuration &config); typedef int (__stdcall *ALERTCB) (const CEC::libcec_alert, const CEC::libcec_parameter &data); typedef int (__stdcall *MENUCB) (const CEC::cec_menu_state newVal); + typedef void (__stdcall *ACTICB) (const CEC::cec_logical_address logicalAddress, const uint8_t bActivated); - static LOGCB g_logCB; - static KEYCB g_keyCB; - static COMMANDCB g_commandCB; - static CONFIGCB g_configCB; - static ALERTCB g_alertCB; - static MENUCB g_menuCB; - static CEC::ICECCallbacks g_cecCallbacks; + typedef struct + { + LOGCB logCB; + KEYCB keyCB; + COMMANDCB commandCB; + CONFIGCB configCB; + ALERTCB alertCB; + MENUCB menuCB; + ACTICB sourceActivatedCB; + } UnmanagedCecCallbacks; + + static PLATFORM::CMutex g_callbackMutex; + static std::vector g_unmanagedCallbacks; + static CEC::ICECCallbacks g_cecCallbacks; int CecLogMessageCB(void *cbParam, const CEC::cec_log_message &message) { - if (g_logCB) - return g_logCB(message); + if (cbParam) + { + size_t iPtr = (size_t)cbParam; + PLATFORM::CLockObject lock(g_callbackMutex); + if (iPtr >= 0 && iPtr < g_unmanagedCallbacks.size()) + return g_unmanagedCallbacks[iPtr].logCB(message); + } return 0; } int CecKeyPressCB(void *cbParam, const CEC::cec_keypress &key) { - if (g_keyCB) - return g_keyCB(key); + if (cbParam) + { + size_t iPtr = (size_t)cbParam; + PLATFORM::CLockObject lock(g_callbackMutex); + if (iPtr >= 0 && iPtr < g_unmanagedCallbacks.size()) + return g_unmanagedCallbacks[iPtr].keyCB(key); + } return 0; } int CecCommandCB(void *cbParam, const CEC::cec_command &command) { - if (g_commandCB) - return g_commandCB(command); + if (cbParam) + { + size_t iPtr = (size_t)cbParam; + PLATFORM::CLockObject lock(g_callbackMutex); + if (iPtr >= 0 && iPtr < g_unmanagedCallbacks.size()) + return g_unmanagedCallbacks[iPtr].commandCB(command); + } return 0; } int CecConfigCB(void *cbParam, const CEC::libcec_configuration &config) { - if (g_configCB) - return g_configCB(config); + if (cbParam) + { + size_t iPtr = (size_t)cbParam; + PLATFORM::CLockObject lock(g_callbackMutex); + if (iPtr >= 0 && iPtr < g_unmanagedCallbacks.size()) + return g_unmanagedCallbacks[iPtr].configCB(config); + } return 0; } int CecAlertCB(void *cbParam, const CEC::libcec_alert alert, const CEC::libcec_parameter &data) { - if (g_alertCB) - return g_alertCB(alert, data); + if (cbParam) + { + size_t iPtr = (size_t)cbParam; + PLATFORM::CLockObject lock(g_callbackMutex); + if (iPtr >= 0 && iPtr < g_unmanagedCallbacks.size()) + return g_unmanagedCallbacks[iPtr].alertCB(alert, data); + } return 0; } int CecMenuCB(void *cbParam, const CEC::cec_menu_state newVal) { - if (g_menuCB) - return g_menuCB(newVal); + if (cbParam) + { + size_t iPtr = (size_t)cbParam; + PLATFORM::CLockObject lock(g_callbackMutex); + if (iPtr >= 0 && iPtr < g_unmanagedCallbacks.size()) + return g_unmanagedCallbacks[iPtr].menuCB(newVal); + } return 0; } + void CecSourceActivatedCB(void *cbParam, const CEC::cec_logical_address logicalAddress, const uint8_t bActivated) + { + if (cbParam) + { + size_t iPtr = (size_t)cbParam; + PLATFORM::CLockObject lock(g_callbackMutex); + if (iPtr >= 0 && iPtr < g_unmanagedCallbacks.size()) + g_unmanagedCallbacks[iPtr].sourceActivatedCB(logicalAddress, bActivated); + } + } + #pragma managed // delegates for the unmanaged callback methods - public delegate int CecLogMessageManagedDelegate(const CEC::cec_log_message &); - public delegate int CecKeyPressManagedDelegate(const CEC::cec_keypress &); - public delegate int CecCommandManagedDelegate(const CEC::cec_command &); - public delegate int CecConfigManagedDelegate(const CEC::libcec_configuration &); - public delegate int CecAlertManagedDelegate(const CEC::libcec_alert, const CEC::libcec_parameter &); - public delegate int CecMenuManagedDelegate(const CEC::cec_menu_state newVal); + public delegate int CecLogMessageManagedDelegate(const CEC::cec_log_message &); + public delegate int CecKeyPressManagedDelegate(const CEC::cec_keypress &); + public delegate int CecCommandManagedDelegate(const CEC::cec_command &); + public delegate int CecConfigManagedDelegate(const CEC::libcec_configuration &); + public delegate int CecAlertManagedDelegate(const CEC::libcec_alert, const CEC::libcec_parameter &); + public delegate int CecMenuManagedDelegate(const CEC::cec_menu_state newVal); + public delegate void CecSourceActivatedManagedDelegate(const CEC::cec_logical_address logicalAddress, const uint8_t bActivated); + + void AssignCallbacks() + { + g_cecCallbacks.CBCecLogMessage = CecLogMessageCB; + g_cecCallbacks.CBCecKeyPress = CecKeyPressCB; + g_cecCallbacks.CBCecCommand = CecCommandCB; + g_cecCallbacks.CBCecConfigurationChanged = CecConfigCB; + g_cecCallbacks.CBCecAlert = CecAlertCB; + g_cecCallbacks.CBCecMenuStateChanged = CecMenuCB; + g_cecCallbacks.CBCecSourceActivated = CecSourceActivatedCB; + } // callback method interface public ref class CecCallbackMethods @@ -762,14 +834,22 @@ namespace CecSharp public: CecCallbackMethods(void) { + m_iCallbackPtr = -1; + AssignCallbacks(); m_bHasCallbacks = false; m_bDelegatesCreated = false; } - ~CecCallbackMethods(void) - { - DestroyDelegates(); - } + ~CecCallbackMethods(void) + { + DestroyDelegates(); + } + + size_t GetCallbackPtr(void) + { + PLATFORM::CLockObject lock(g_callbackMutex); + return m_iCallbackPtr; + } protected: !CecCallbackMethods(void) @@ -826,6 +906,10 @@ namespace CecSharp return 0; } + virtual void SourceActivated(CecLogicalAddress logicalAddress, bool bActivated) + { + } + protected: // managed callback methods int CecLogMessageManaged(const CEC::cec_log_message &message) @@ -895,6 +979,12 @@ namespace CecSharp return iReturn; } + void CecSourceActivatedManaged(const CEC::cec_logical_address logicalAddress, const uint8_t bActivated) + { + if (m_bHasCallbacks) + m_callbacks->SourceActivated((CecLogicalAddress)logicalAddress, bActivated == 1); + } + void DestroyDelegates() { m_bHasCallbacks = false; @@ -906,6 +996,7 @@ namespace CecSharp m_commandGCHandle.Free(); m_alertGCHandle.Free(); m_menuGCHandle.Free(); + m_sourceActivatedGCHandle.Free(); } } @@ -918,43 +1009,55 @@ namespace CecSharp msclr::interop::marshal_context ^ context = gcnew msclr::interop::marshal_context(); // create the delegate method for the log message callback - m_logMessageDelegate = gcnew CecLogMessageManagedDelegate(this, &CecCallbackMethods::CecLogMessageManaged); - m_logMessageGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_logMessageDelegate); - g_logCB = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_logMessageDelegate).ToPointer()); - g_cecCallbacks.CBCecLogMessage = CecLogMessageCB; + m_logMessageDelegate = gcnew CecLogMessageManagedDelegate(this, &CecCallbackMethods::CecLogMessageManaged); + m_logMessageGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_logMessageDelegate); + m_logMessageCallback = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_logMessageDelegate).ToPointer()); // create the delegate method for the keypress callback - m_keypressDelegate = gcnew CecKeyPressManagedDelegate(this, &CecCallbackMethods::CecKeyPressManaged); - m_keypressGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_keypressDelegate); - g_keyCB = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_keypressDelegate).ToPointer()); - g_cecCallbacks.CBCecKeyPress = CecKeyPressCB; + m_keypressDelegate = gcnew CecKeyPressManagedDelegate(this, &CecCallbackMethods::CecKeyPressManaged); + m_keypressGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_keypressDelegate); + m_keypressCallback = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_keypressDelegate).ToPointer()); // create the delegate method for the command callback - m_commandDelegate = gcnew CecCommandManagedDelegate(this, &CecCallbackMethods::CecCommandManaged); - m_commandGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_commandDelegate); - g_commandCB = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_commandDelegate).ToPointer()); - g_cecCallbacks.CBCecCommand = CecCommandCB; + m_commandDelegate = gcnew CecCommandManagedDelegate(this, &CecCallbackMethods::CecCommandManaged); + m_commandGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_commandDelegate); + m_commandCallback = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_commandDelegate).ToPointer()); // create the delegate method for the configuration change callback - m_configDelegate = gcnew CecConfigManagedDelegate(this, &CecCallbackMethods::CecConfigManaged); - m_configGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_configDelegate); - g_configCB = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_configDelegate).ToPointer()); - g_cecCallbacks.CBCecConfigurationChanged = CecConfigCB; + m_configDelegate = gcnew CecConfigManagedDelegate(this, &CecCallbackMethods::CecConfigManaged); + m_configGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_configDelegate); + m_configCallback = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_configDelegate).ToPointer()); // create the delegate method for the alert callback - m_alertDelegate = gcnew CecAlertManagedDelegate(this, &CecCallbackMethods::CecAlertManaged); - m_alertGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_alertDelegate); - g_alertCB = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_alertDelegate).ToPointer()); - g_cecCallbacks.CBCecAlert = CecAlertCB; + m_alertDelegate = gcnew CecAlertManagedDelegate(this, &CecCallbackMethods::CecAlertManaged); + m_alertGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_alertDelegate); + m_alertCallback = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_alertDelegate).ToPointer()); // create the delegate method for the menu callback - m_menuDelegate = gcnew CecMenuManagedDelegate(this, &CecCallbackMethods::CecMenuManaged); - m_menuGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_menuDelegate); - g_menuCB = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_menuDelegate).ToPointer()); - g_cecCallbacks.CBCecMenuStateChanged = CecMenuCB; + m_menuDelegate = gcnew CecMenuManagedDelegate(this, &CecCallbackMethods::CecMenuManaged); + m_menuGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_menuDelegate); + m_menuCallback = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_menuDelegate).ToPointer()); + + // create the delegate method for the source activated callback + m_sourceActivatedDelegate = gcnew CecSourceActivatedManagedDelegate(this, &CecCallbackMethods::CecSourceActivatedManaged); + m_sourceActivatedGCHandle = System::Runtime::InteropServices::GCHandle::Alloc(m_sourceActivatedDelegate); + m_sourceActivatedCallback = static_cast(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(m_sourceActivatedDelegate).ToPointer()); delete context; - m_bDelegatesCreated = true; + + UnmanagedCecCallbacks unmanagedCallbacks; + unmanagedCallbacks.logCB = m_logMessageCallback; + unmanagedCallbacks.keyCB = m_keypressCallback; + unmanagedCallbacks.commandCB = m_commandCallback; + unmanagedCallbacks.configCB = m_configCallback; + unmanagedCallbacks.alertCB = m_alertCallback; + unmanagedCallbacks.menuCB = m_menuCallback; + unmanagedCallbacks.sourceActivatedCB = m_sourceActivatedCallback; + + PLATFORM::CLockObject lock(g_callbackMutex); + g_unmanagedCallbacks.push_back(unmanagedCallbacks); + m_iCallbackPtr = g_unmanagedCallbacks.size() - 1; + m_bDelegatesCreated = true; } } @@ -976,14 +1079,19 @@ namespace CecSharp CecAlertManagedDelegate ^ m_alertDelegate; static System::Runtime::InteropServices::GCHandle m_alertGCHandle; - CONFIGCB m_alertCallback; + ALERTCB m_alertCallback; CecMenuManagedDelegate ^ m_menuDelegate; static System::Runtime::InteropServices::GCHandle m_menuGCHandle; MENUCB m_menuCallback; + CecSourceActivatedManagedDelegate ^ m_sourceActivatedDelegate; + static System::Runtime::InteropServices::GCHandle m_sourceActivatedGCHandle; + ACTICB m_sourceActivatedCallback; + CecCallbackMethods ^ m_callbacks; bool m_bHasCallbacks; bool m_bDelegatesCreated; + size_t m_iCallbackPtr; }; } diff --git a/src/LibCecSharp/LibCecSharp.cpp b/src/LibCecSharp/LibCecSharp.cpp index 7f0667a..cd0f6d0 100644 --- a/src/LibCecSharp/LibCecSharp.cpp +++ b/src/LibCecSharp/LibCecSharp.cpp @@ -82,6 +82,7 @@ namespace CecSharp ConvertConfiguration(context, config, libCecConfig); m_libCec = (ICECAdapter *) CECInitialise(&libCecConfig); + config->Update(libCecConfig); delete context; return m_libCec != NULL; @@ -135,11 +136,9 @@ namespace CecSharp } if (netConfig->ServerVersion >= CecServerVersion::Version1_6_3) - { - config.bMonitorOnly = netConfig->MonitorOnlyClient ? 1 : 0; - } + config.bMonitorOnly = netConfig->MonitorOnlyClient ? 1 : 0; - config.callbacks = &g_cecCallbacks; + config.callbacks = &g_cecCallbacks; } public: @@ -189,7 +188,7 @@ namespace CecSharp virtual bool EnableCallbacks(CecCallbackMethods ^ callbacks) override { if (m_libCec && CecCallbackMethods::EnableCallbacks(callbacks)) - return m_libCec->EnableCallbacks(NULL, &g_cecCallbacks); + return m_libCec->EnableCallbacks((void*)GetCallbackPtr(), &g_cecCallbacks); return false; } diff --git a/src/cec-config-gui/CecConfigGUI.cs b/src/cec-config-gui/CecConfigGUI.cs index 848ad60..1bd2f07 100644 --- a/src/cec-config-gui/CecConfigGUI.cs +++ b/src/cec-config-gui/CecConfigGUI.cs @@ -28,7 +28,7 @@ namespace CecConfigGui Config.DeviceTypes.Types[0] = CecDeviceType.RecordingDevice; Config.DeviceName = "CEC Config"; Config.GetSettingsFromROM = true; - Config.ClientVersion = CecClientVersion.Version1_5_1; + Config.ClientVersion = CecClientVersion.Version1_7_1; Callbacks = new CecCallbackWrapper(this); Config.SetCallbacks(Callbacks); LoadXMLConfiguration(ref Config); diff --git a/src/cec-config-gui/Properties/AssemblyInfo.cs b/src/cec-config-gui/Properties/AssemblyInfo.cs index 1a83d3e..56ca7ec 100644 --- a/src/cec-config-gui/Properties/AssemblyInfo.cs +++ b/src/cec-config-gui/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] +[assembly: AssemblyVersion("1.7.1.0")] +[assembly: AssemblyFileVersion("1.7.1.0")] diff --git a/src/cec-config/Makefile.am b/src/cec-config/Makefile.am index a6c6c39..da7b89b 100644 --- a/src/cec-config/Makefile.am +++ b/src/cec-config/Makefile.am @@ -2,4 +2,4 @@ bin_PROGRAMS = cec-config cec_config_SOURCES = cec-config.cpp cec_config_CPPFLAGS = -I@abs_top_srcdir@/include -cec_config_LDFLAGS = @LIBS_DL@ \ No newline at end of file +cec_config_LDFLAGS = @LIBS@ diff --git a/src/cec-config/cec-config.cpp b/src/cec-config/cec-config.cpp index 57931ee..7b203cb 100644 --- a/src/cec-config/cec-config.cpp +++ b/src/cec-config/cec-config.cpp @@ -315,6 +315,8 @@ bool PowerOnTV(uint64_t iTimeout = 60000) int main (int UNUSED(argc), char *UNUSED(argv[])) { + g_callbacks.Clear(); + g_config.Clear(); PrintToStdOut("=== USB-CEC Adapter Configuration ===\n"); if (!OpenConnection()) return 1; diff --git a/src/lib/CECClient.cpp b/src/lib/CECClient.cpp index 7cf542b..b0d7e18 100644 --- a/src/lib/CECClient.cpp +++ b/src/lib/CECClient.cpp @@ -50,8 +50,10 @@ CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration &con m_bInitialised(false), m_bRegistered(false), m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN), - m_buttontime(0) + m_buttontime(0), + m_iPreventForwardingPowerOffCommand(0) { + m_configuration.Clear(); // set the initial configuration SetConfiguration(configuration); } @@ -123,7 +125,7 @@ bool CCECClient::OnRegister(void) // make the primary device the active source if the option is set if (m_configuration.bActivateSource == 1) - GetPrimaryDevice()->ActivateSource(); + GetPrimaryDevice()->ActivateSource(500); return true; } @@ -137,7 +139,7 @@ bool CCECClient::SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_ iPort > CEC_MAX_HDMI_PORTNUMBER) return bReturn; - LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice); + LIB_CEC->AddLog(CEC_LOG_NOTICE, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice); // update the configuration { @@ -269,6 +271,7 @@ void CCECClient::SetSupportedDeviceTypes(void) if (!types.IsSet(type)) types.Add(type); } + m_processor->GetTV()->MarkHandlerReady(); // set the new type list m_configuration.deviceTypes = types; @@ -881,14 +884,28 @@ bool CCECClient::SetConfiguration(const libcec_configuration &configuration) void CCECClient::AddCommand(const cec_command &command) { - CLockObject lock(m_mutex); + // don't forward the standby opcode more than once every 10 seconds + if (command.opcode == CEC_OPCODE_STANDBY) + { + CLockObject lock(m_mutex); + if (m_iPreventForwardingPowerOffCommand != 0 && + m_iPreventForwardingPowerOffCommand > GetTimeMs()) + return; + else + m_iPreventForwardingPowerOffCommand = GetTimeMs() + CEC_FORWARD_STANDBY_MIN_INTERVAL; + } - LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s (%X) -> %s (%X): %s (%2X)", ToString(command.initiator), command.initiator, ToString(command.destination), command.destination, ToString(command.opcode), command.opcode); + if (command.destination == CECDEVICE_BROADCAST || GetLogicalAddresses().IsSet(command.destination)) + { + CLockObject lock(m_mutex); - if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand) - m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command); - else if (!m_commandBuffer.Push(command)) - LIB_CEC->AddLog(CEC_LOG_WARNING, "command buffer is full"); + LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s (%X) -> %s (%X): %s (%2X)", ToString(command.initiator), command.initiator, ToString(command.destination), command.destination, ToString(command.opcode), command.opcode); + + if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand) + m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command); + else if (!m_commandBuffer.Push(command)) + LIB_CEC->AddLog(CEC_LOG_WARNING, "command buffer is full"); + } } int CCECClient::MenuStateChanged(const cec_menu_state newState) @@ -905,6 +922,30 @@ int CCECClient::MenuStateChanged(const cec_menu_state newState) return 0; } +void CCECClient::SourceActivated(const cec_logical_address logicalAddress) +{ + CLockObject lock(m_mutex); + + LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source activated: %s (%x)", ToString(logicalAddress), logicalAddress); + + if (m_configuration.callbacks && + m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_7_1 && + m_configuration.callbacks->CBCecSourceActivated) + m_configuration.callbacks->CBCecSourceActivated(m_configuration.callbackParam, logicalAddress, 1); +} + +void CCECClient::SourceDeactivated(const cec_logical_address logicalAddress) +{ + CLockObject lock(m_mutex); + + LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source deactivated: %s (%x)", ToString(logicalAddress), logicalAddress); + + if (m_configuration.callbacks && + m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_7_1 && + m_configuration.callbacks->CBCecSourceActivated) + m_configuration.callbacks->CBCecSourceActivated(m_configuration.callbackParam, logicalAddress, 0); +} + void CCECClient::Alert(const libcec_alert type, const libcec_parameter ¶m) { CLockObject lock(m_mutex); @@ -1172,7 +1213,10 @@ cec_device_type_list CCECClient::GetDeviceTypes(void) bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress) { if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress)) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - not setting invalid physical address %04x", __FUNCTION__, iPhysicalAddress); return false; + } // reconfigure all devices cec_logical_address reactivateSource(CECDEVICE_UNKNOWN); diff --git a/src/lib/CECClient.h b/src/lib/CECClient.h index 87a4b7f..26944a7 100644 --- a/src/lib/CECClient.h +++ b/src/lib/CECClient.h @@ -182,6 +182,8 @@ namespace CEC virtual void AddKey(const cec_keypress &key); virtual void SetCurrentButton(const cec_user_control_code iButtonCode); virtual void CheckKeypressTimeout(void); + virtual void SourceActivated(const cec_logical_address logicalAddress); + virtual void SourceDeactivated(const cec_logical_address logicalAddress); protected: /*! @@ -305,6 +307,7 @@ namespace CEC PLATFORM::CMutex m_logMutex; /**< mutex that is held when sending a log message back to the client */ cec_user_control_code m_iCurrentButton; /**< the control code of the button that's currently held down (if any) */ int64_t m_buttontime; /**< the timestamp when the button was pressed (in seconds since epoch), or 0 if none was pressed. */ + int64_t m_iPreventForwardingPowerOffCommand; /**< prevent forwarding standby commands until this time */ PLATFORM::SyncedBuffer m_logBuffer; /**< @deprecated will be removed in v2.0. buffer for log messages */ PLATFORM::SyncedBuffer m_keyBuffer; /**< @deprecated will be removed in v2.0. buffer for keypresses */ PLATFORM::SyncedBuffer m_commandBuffer; /**< @deprecated will be removed in v2.0. buffer for commands */ diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp index ca323fc..28adee3 100644 --- a/src/lib/CECProcessor.cpp +++ b/src/lib/CECProcessor.cpp @@ -51,7 +51,7 @@ using namespace std; using namespace PLATFORM; #define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000 -#define ACTIVE_SOURCE_CHECK_TIMEOUT 15000 +#define ACTIVE_SOURCE_CHECK_INTERVAL 500 #define ToString(x) CCECTypeUtils::ToString(x) @@ -198,19 +198,6 @@ void CCECProcessor::ReplaceHandlers(void) it->second->ReplaceHandler(true); } -void CCECProcessor::CheckPendingActiveSource(void) -{ - if (!CECInitialised()) - return; - - // check each device - for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) - { - if (it->second->GetHandler()->ActiveSourcePending()) - it->second->ActivateSource(); - } -} - bool CCECProcessor::OnCommandReceived(const cec_command &command) { return m_inBuffer.Push(command); @@ -220,8 +207,8 @@ void *CCECProcessor::Process(void) { m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started"); - cec_command command; - CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_TIMEOUT); + cec_command command; command.Clear(); + CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_INTERVAL); // as long as we're not being stopped and the connection is open while (!IsStopped() && m_communication->IsOpen()) @@ -241,8 +228,9 @@ void *CCECProcessor::Process(void) // check whether we need to activate a source, if it failed before if (activeSourceCheck.TimeLeft() == 0) { - CheckPendingActiveSource(); - activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_TIMEOUT); + if (CECInitialised()) + TransmitPendingActiveSourceCommands(); + activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_INTERVAL); } } } @@ -413,6 +401,7 @@ bool CCECProcessor::Transmit(const cec_command &data) m_iLastTransmission = GetTimeMs(); // set the number of tries iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1; + initiator->MarkHandlerReady(); } // and try to send the command @@ -565,7 +554,9 @@ bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator) bool CCECProcessor::SetStreamPath(uint16_t iPhysicalAddress) { // stream path changes are sent by the TV - return GetTV()->GetHandler()->TransmitSetStreamPath(iPhysicalAddress); + bool bReturn = GetTV()->GetHandler()->TransmitSetStreamPath(iPhysicalAddress); + GetTV()->MarkHandlerReady(); + return bReturn; } bool CCECProcessor::CanPersistConfiguration(void) @@ -646,7 +637,9 @@ bool CCECProcessor::RegisterClient(CCECClient *client) } // ensure that we know the vendor id of the TV - GetTV()->GetVendorId(CECDEVICE_UNREGISTERED); + CCECBusDevice *tv = GetTV(); + tv->GetVendorId(CECDEVICE_UNREGISTERED); + tv->ReplaceHandler(false); // unregister the client first if it's already been marked as registered if (client->IsRegistered()) @@ -675,6 +668,10 @@ bool CCECProcessor::RegisterClient(CCECClient *client) m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) { + // set the physical address of the device at this LA + if (CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress)) + (*it)->SetPhysicalAddress(configuration.iPhysicalAddress); + // replace a previous client CLockObject lock(m_mutex); m_clients.erase((*it)->GetLogicalAddress()); @@ -684,7 +681,7 @@ bool CCECProcessor::RegisterClient(CCECClient *client) // get the settings from the rom if (configuration.bGetSettingsFromROM == 1) { - libcec_configuration config; + libcec_configuration config; config.Clear(); m_communication->GetConfiguration(config); CLockObject lock(m_mutex); @@ -729,6 +726,7 @@ bool CCECProcessor::RegisterClient(CCECClient *client) CCECCommandHandler *handler = GetTV()->GetHandler(); if (handler) handler->InitHandler(); + GetTV()->MarkHandlerReady(); } return bReturn; diff --git a/src/lib/CECProcessor.h b/src/lib/CECProcessor.h index 04d84f5..5f496d2 100644 --- a/src/lib/CECProcessor.h +++ b/src/lib/CECProcessor.h @@ -134,7 +134,6 @@ namespace CEC void SetCECInitialised(bool bSetTo = true); void ReplaceHandlers(void); - void CheckPendingActiveSource(void); bool PhysicalAddressInUse(uint16_t iPhysicalAddress); bool SetAckMask(uint16_t iMask); diff --git a/src/lib/CECTypeUtils.h b/src/lib/CECTypeUtils.h index 5e39f54..a62637b 100644 --- a/src/lib/CECTypeUtils.h +++ b/src/lib/CECTypeUtils.h @@ -494,6 +494,22 @@ namespace CEC return "Sony"; case CEC_VENDOR_TOSHIBA: return "Toshiba"; + case CEC_VENDOR_AKAI: + return "Akai"; + case CEC_VENDOR_AOC: + return "AOC"; + case CEC_VENDOR_BENQ: + return "Benq"; + case CEC_VENDOR_DAEWOO: + return "Daewoo"; + case CEC_VENDOR_GRUNDIG: + return "Grundig"; + case CEC_VENDOR_MEDION: + return "Medion"; + case CEC_VENDOR_SHARP: + return "Sharp"; + case CEC_VENDOR_VIZIO: + return "Vizio"; default: return "Unknown"; } @@ -523,6 +539,8 @@ namespace CEC return "1.6.3"; case CEC_CLIENT_VERSION_1_7_0: return "1.7.0"; + case CEC_CLIENT_VERSION_1_7_1: + return "1.7.1"; default: return "Unknown"; } @@ -552,9 +570,30 @@ namespace CEC return "1.6.3"; case CEC_SERVER_VERSION_1_7_0: return "1.7.0"; + case CEC_SERVER_VERSION_1_7_1: + return "1.7.1"; default: return "Unknown"; } } + + static const char *ToString(const cec_abort_reason reason) + { + switch(reason) + { + case CEC_ABORT_REASON_UNRECOGNIZED_OPCODE: + return "unrecognised opcode"; + case CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND: + return "not in correct mode to respond"; + case CEC_ABORT_REASON_CANNOT_PROVIDE_SOURCE: + return "cannot provide source"; + case CEC_ABORT_REASON_INVALID_OPERAND: + return "invalid operand"; + case CEC_ABORT_REASON_REFUSED: + return "refused"; + default: + return "unknown"; + } + } }; } diff --git a/src/lib/LibCEC.cpp b/src/lib/LibCEC.cpp index 56ece65..c0931c3 100644 --- a/src/lib/LibCEC.cpp +++ b/src/lib/LibCEC.cpp @@ -114,6 +114,12 @@ void CLibCEC::Close(void) int8_t CLibCEC::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) { + if (!CUSBCECAdapterDetection::CanAutodetect()) + { + AddLog(CEC_LOG_WARNING, "libCEC has not been compiled with adapter detection code for this target, so the path to the COM port has to be provided to libCEC"); + return 0; + } + return CUSBCECAdapterDetection::FindAdapters(deviceList, iBufSize, strDevicePath); } @@ -469,6 +475,13 @@ void CLibCEC::AddLog(const cec_log_level level, const char *strFormat, ...) (*it)->AddLog(message); } +void CLibCEC::AddCommand(const cec_command &command) +{ + // send the command to all clients + for (vector::iterator it = m_clients.begin(); it != m_clients.end(); it++) + (*it)->AddCommand(command); +} + void CLibCEC::Alert(const libcec_alert type, const libcec_parameter ¶m) { // send the alert to all clients @@ -543,14 +556,14 @@ void * CECInitialise(libcec_configuration *configuration) return static_cast< void* > (lib); } -void * CECInit(const char *strDeviceName, CEC::cec_device_type_list types, uint16_t iPhysicalAddress /* = 0 */) +void * CECInit(const char *strDeviceName, CEC::cec_device_type_list types) { - libcec_configuration configuration; + libcec_configuration configuration; configuration.Clear(); // client version < 1.5.0 snprintf(configuration.strDeviceName, 13, "%s", strDeviceName); configuration.deviceTypes = types; - configuration.iPhysicalAddress = iPhysicalAddress; + configuration.iPhysicalAddress = CEC_INVALID_PHYSICAL_ADDRESS; if (configuration.deviceTypes.IsEmpty()) configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE); @@ -558,6 +571,18 @@ void * CECInit(const char *strDeviceName, CEC::cec_device_type_list types, uint1 return CECInitialise(&configuration); } +void * CECCreate(const char *strDeviceName, CEC::cec_logical_address iLogicalAddress /* = CEC::CECDEVICE_PLAYBACKDEVICE1 */, uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS */) +{ + libcec_configuration configuration; configuration.Clear(); + + // client version < 1.5.0 + snprintf(configuration.strDeviceName, 13, "%s", strDeviceName); + configuration.iPhysicalAddress = iPhysicalAddress; + configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE); + + return CECInitialise(&configuration); +} + bool CECStartBootloader(void) { bool bReturn(false); @@ -593,7 +618,6 @@ bool CLibCEC::GetDeviceInformation(const char *strPort, libcec_configuration *co // no longer being used void CLibCEC::AddKey(const cec_keypress &UNUSED(key)) {} -void CLibCEC::AddCommand(const cec_command &UNUSED(command)) {} void CLibCEC::ConfigurationChanged(const libcec_configuration &UNUSED(config)) {} void CLibCEC::SetCurrentButton(cec_user_control_code UNUSED(iButtonCode)) {} CLibCEC *CLibCEC::GetInstance(void) { return NULL; } diff --git a/src/lib/LibCEC.h b/src/lib/LibCEC.h index 0ef3e83..6407e57 100644 --- a/src/lib/LibCEC.h +++ b/src/lib/LibCEC.h @@ -134,7 +134,7 @@ namespace CEC void AddLog(const cec_log_level level, const char *strFormat, ...); static void AddKey(void) {} //UNUSED static void AddKey(const cec_keypress &key); //UNUSED - static void AddCommand(const cec_command &command); //UNUSED + void AddCommand(const cec_command &command); static void ConfigurationChanged(const libcec_configuration &config); //UNUSED static void SetCurrentButton(cec_user_control_code iButtonCode); //UNUSED void CheckKeypressTimeout(void); diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 30637f2..3d34f83 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -35,5 +35,5 @@ libcec_la_SOURCES = CECProcessor.cpp \ platform/adl/adl-edid.cpp \ platform/nvidia/nv-edid.cpp -libcec_la_LDFLAGS = @LIBS@ -version-info @VERSION@ +libcec_la_LDFLAGS = @LIBS_LIBCEC@ -version-info @VERSION@ libcec_la_CPPFLAGS = -I@abs_top_srcdir@/include diff --git a/src/lib/adapter/USBCECAdapterCommunication.cpp b/src/lib/adapter/USBCECAdapterCommunication.cpp index 45be4a6..b3f0f7d 100644 --- a/src/lib/adapter/USBCECAdapterCommunication.cpp +++ b/src/lib/adapter/USBCECAdapterCommunication.cpp @@ -512,7 +512,7 @@ bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask) return true; } - LIB_CEC->AddLog(CEC_LOG_ERROR, "couldn't change the ackmask: the connection is closed"); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "couldn't change the ackmask: the connection is closed"); return false; } diff --git a/src/lib/adapter/USBCECAdapterDetection.cpp b/src/lib/adapter/USBCECAdapterDetection.cpp index 8775b4e..78b2760 100644 --- a/src/lib/adapter/USBCECAdapterDetection.cpp +++ b/src/lib/adapter/USBCECAdapterDetection.cpp @@ -115,6 +115,15 @@ bool FindComPort(CStdString &strLocation) } #endif +bool CUSBCECAdapterDetection::CanAutodetect(void) +{ +#if defined(__APPLE__) || defined(HAVE_LIBUDEV) || defined(__WINDOWS__) || defined(__FreeBSD__) + return true; +#else + return false; +#endif +} + uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) { uint8_t iFound(0); diff --git a/src/lib/adapter/USBCECAdapterDetection.h b/src/lib/adapter/USBCECAdapterDetection.h index 9be3a57..9b6c624 100644 --- a/src/lib/adapter/USBCECAdapterDetection.h +++ b/src/lib/adapter/USBCECAdapterDetection.h @@ -38,6 +38,7 @@ namespace CEC class CUSBCECAdapterDetection { public: - static uint8_t FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); + static uint8_t FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); + static bool CanAutodetect(void); }; }; diff --git a/src/lib/adapter/USBCECAdapterMessageQueue.h b/src/lib/adapter/USBCECAdapterMessageQueue.h index 4646fe4..44c930b 100644 --- a/src/lib/adapter/USBCECAdapterMessageQueue.h +++ b/src/lib/adapter/USBCECAdapterMessageQueue.h @@ -136,7 +136,11 @@ namespace CEC CCECAdapterMessageQueue(CUSBCECAdapterCommunication *com) : PLATFORM::CThread(), m_com(com), - m_iNextMessage(0) {} + m_iNextMessage(0) + { + m_currentCECFrame.Clear(); + } + virtual ~CCECAdapterMessageQueue(void); /*! diff --git a/src/lib/devices/CECBusDevice.cpp b/src/lib/devices/CECBusDevice.cpp index 8f966a2..91dd835 100644 --- a/src/lib/devices/CECBusDevice.cpp +++ b/src/lib/devices/CECBusDevice.cpp @@ -32,6 +32,7 @@ #include "CECBusDevice.h" #include "../CECProcessor.h" +#include "../CECClient.h" #include "../implementations/ANCommandHandler.h" #include "../implementations/CECCommandHandler.h" #include "../implementations/SLCommandHandler.h" @@ -71,7 +72,8 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi m_deviceStatus (CEC_DEVICE_STATUS_UNKNOWN), m_iHandlerUseCount (0), m_bAwaitingReceiveFailed(false), - m_bVendorIdRequested (false) + m_bVendorIdRequested (false), + m_waitForResponse (new CWaitForResponse) { m_handler = new CCECCommandHandler(this); @@ -86,6 +88,7 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi CCECBusDevice::~CCECBusDevice(void) { DELETE_AND_NULL(m_handler); + DELETE_AND_NULL(m_waitForResponse); } bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */) @@ -107,21 +110,27 @@ bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */) if (CCECCommandHandler::HasSpecificHandler(m_vendor)) { LIB_CEC->AddLog(CEC_LOG_DEBUG, "replacing the command handler for device '%s' (%x)", GetLogicalAddressName(), GetLogicalAddress()); + + int32_t iTransmitTimeout = m_handler->m_iTransmitTimeout; + int32_t iTransmitWait = m_handler->m_iTransmitWait; + int8_t iTransmitRetries = m_handler->m_iTransmitRetries; + int64_t iActiveSourcePending = m_handler->m_iActiveSourcePending; + DELETE_AND_NULL(m_handler); switch (m_vendor) { case CEC_VENDOR_SAMSUNG: - m_handler = new CANCommandHandler(this); + m_handler = new CANCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending); break; case CEC_VENDOR_LG: - m_handler = new CSLCommandHandler(this); + m_handler = new CSLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending); break; case CEC_VENDOR_PANASONIC: - m_handler = new CVLCommandHandler(this); + m_handler = new CVLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending); break; default: - m_handler = new CCECCommandHandler(this); + m_handler = new CCECCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending); break; } @@ -148,6 +157,13 @@ bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */) return true; } +CCECCommandHandler *CCECBusDevice::GetHandler(void) +{ + ReplaceHandler(false); + MarkBusy(); + return m_handler; +} + bool CCECBusDevice::HandleCommand(const cec_command &command) { bool bHandled(false); @@ -223,7 +239,7 @@ void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode) // signal threads that are waiting for a reponse MarkBusy(); - m_handler->SignalOpcode(cec_command::GetResponseOpcode(opcode)); + SignalOpcode(cec_command::GetResponseOpcode(opcode)); MarkReady(); } @@ -793,6 +809,7 @@ void CCECBusDevice::ResetDeviceStatus(void) m_iLastActive = 0; m_bVendorIdRequested = false; m_unsupportedFeatures.clear(); + m_waitForResponse->Clear(); if (m_deviceStatus != CEC_DEVICE_STATUS_UNKNOWN) LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'unknown'", GetLogicalAddressName(), m_iLogicalAddress); @@ -882,12 +899,21 @@ bool CCECBusDevice::TransmitMenuState(const cec_logical_address dest) return bReturn; } -bool CCECBusDevice::ActivateSource(void) +bool CCECBusDevice::ActivateSource(uint64_t iDelay /* = 0 */) { MarkAsActiveSource(); - LIB_CEC->AddLog(CEC_LOG_DEBUG, "activating source '%s'", ToString(m_iLogicalAddress)); MarkBusy(); - bool bReturn = m_handler->ActivateSource(); + bool bReturn(true); + if (iDelay == 0) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending active source message for '%s'", ToString(m_iLogicalAddress)); + bReturn = m_handler->ActivateSource(); + } + else + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "scheduling active source message for '%s'", ToString(m_iLogicalAddress)); + m_handler->ScheduleActivateSource(iDelay); + } MarkReady(); return bReturn; } @@ -909,38 +935,76 @@ bool CCECBusDevice::RequestActiveSource(bool bWaitForResponse /* = true */) void CCECBusDevice::MarkAsActiveSource(void) { - CLockObject lock(m_mutex); - if (!m_bActiveSource) - LIB_CEC->AddLog(CEC_LOG_DEBUG, "making %s (%x) the active source", GetLogicalAddressName(), m_iLogicalAddress); - else - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%x) was already marked as active source", GetLogicalAddressName(), m_iLogicalAddress); + bool bWasActivated(false); + + // set the power status to powered on + SetPowerStatus(CEC_POWER_STATUS_ON); + // mark this device as active source + { + CLockObject lock(m_mutex); + if (!m_bActiveSource) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "making %s (%x) the active source", GetLogicalAddressName(), m_iLogicalAddress); + bWasActivated = true; + } + else + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%x) was already marked as active source", GetLogicalAddressName(), m_iLogicalAddress); + + m_bActiveSource = true; + } + + // mark other devices as inactive sources CECDEVICEVEC devices; m_processor->GetDevices()->Get(devices); for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) if ((*it)->GetLogicalAddress() != m_iLogicalAddress) (*it)->MarkAsInactiveSource(); - m_bActiveSource = true; - SetPowerStatus(CEC_POWER_STATUS_ON); + if (bWasActivated) + { + CCECClient *client = GetClient(); + if (client) + client->SourceActivated(m_iLogicalAddress); + } } void CCECBusDevice::MarkAsInactiveSource(void) { + bool bWasDeactivated(false); { CLockObject lock(m_mutex); if (m_bActiveSource) + { LIB_CEC->AddLog(CEC_LOG_DEBUG, "marking %s (%X) as inactive source", GetLogicalAddressName(), m_iLogicalAddress); + bWasDeactivated = true; + } m_bActiveSource = false; } + + if (bWasDeactivated) + { + CCECClient *client = GetClient(); + if (client) + client->SourceDeactivated(m_iLogicalAddress); + } } bool CCECBusDevice::TransmitActiveSource(void) { bool bSendActiveSource(false); + uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS); { CLockObject lock(m_mutex); + if (!HasValidPhysicalAddress()) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X) has an invalid physical address (%04x), not sending active source commands", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress); + return false; + } + + iPhysicalAddress = m_iPhysicalAddress; + if (m_powerStatus != CEC_POWER_STATUS_ON && m_powerStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON) LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not powered on", GetLogicalAddressName(), m_iLogicalAddress); else if (m_bActiveSource) @@ -956,7 +1020,7 @@ bool CCECBusDevice::TransmitActiveSource(void) if (bSendActiveSource) { MarkBusy(); - bActiveSourceSent = m_handler->TransmitActiveSource(m_iLogicalAddress, m_iPhysicalAddress); + bActiveSourceSent = m_handler->TransmitActiveSource(m_iLogicalAddress, iPhysicalAddress); MarkReady(); } @@ -999,7 +1063,7 @@ bool CCECBusDevice::TransmitInactiveSource(void) bool CCECBusDevice::TransmitPendingActiveSourceCommands(void) { MarkBusy(); - bool bReturn = m_handler->TransmitPendingActiveSourceCommands(); + bool bReturn = m_handler->ActivateSource(true); MarkReady(); return bReturn; } @@ -1236,3 +1300,13 @@ CCECClient *CCECBusDevice::GetClient(void) { return m_processor->GetClient(m_iLogicalAddress); } + +void CCECBusDevice::SignalOpcode(cec_opcode opcode) +{ + m_waitForResponse->Received(opcode); +} + +bool CCECBusDevice::WaitForOpcode(cec_opcode opcode) +{ + return m_waitForResponse->Wait(opcode); +} diff --git a/src/lib/devices/CECBusDevice.h b/src/lib/devices/CECBusDevice.h index 0e11d42..89fb690 100644 --- a/src/lib/devices/CECBusDevice.h +++ b/src/lib/devices/CECBusDevice.h @@ -33,6 +33,7 @@ #include "../../../include/cectypes.h" #include +#include #include "../platform/threads/mutex.h" #include "../platform/util/StdString.h" @@ -47,6 +48,85 @@ namespace CEC class CCECTuner; class CCECTV; + class CResponse + { + public: + CResponse(cec_opcode opcode) : + m_opcode(opcode){} + ~CResponse(void) + { + Broadcast(); + } + + bool Wait(uint32_t iTimeout) + { + return m_event.Wait(iTimeout); + } + + void Broadcast(void) + { + m_event.Broadcast(); + } + + private: + cec_opcode m_opcode; + PLATFORM::CEvent m_event; + }; + + class CWaitForResponse + { + public: + CWaitForResponse(void) {} + ~CWaitForResponse(void) + { + Clear(); + } + + void Clear() + { + PLATFORM::CLockObject lock(m_mutex); + for (std::map::iterator it = m_waitingFor.begin(); it != m_waitingFor.end(); it++) + it->second->Broadcast(); + m_waitingFor.clear(); + } + + bool Wait(cec_opcode opcode, uint32_t iTimeout = CEC_DEFAULT_TRANSMIT_WAIT) + { + CResponse *response = GetEvent(opcode); + return response ? response->Wait(iTimeout) : false; + } + + void Received(cec_opcode opcode) + { + CResponse *response = GetEvent(opcode); + if (response) + response->Broadcast(); + } + + private: + CResponse *GetEvent(cec_opcode opcode) + { + CResponse *retVal(NULL); + { + PLATFORM::CLockObject lock(m_mutex); + std::map::iterator it = m_waitingFor.find(opcode); + if (it != m_waitingFor.end()) + { + retVal = it->second; + } + else + { + retVal = new CResponse(opcode); + m_waitingFor[opcode] = retVal; + } + return retVal; + } + } + + PLATFORM::CMutex m_mutex; + std::map m_waitingFor; + }; + class CCECBusDevice { friend class CCECProcessor; @@ -56,7 +136,19 @@ namespace CEC virtual ~CCECBusDevice(void); virtual bool ReplaceHandler(bool bActivateSource = true); - virtual CCECCommandHandler * GetHandler(void) const { return m_handler; }; + + // TODO use something smarter than this + /*! + * @brief Get the command handler for this device. Call MarkHandlerReady() when done with it. + * @return The current handler. + */ + virtual CCECCommandHandler * GetHandler(void); + + /*! + * @brief To be called after GetHandler(), when no longer using it. + */ + virtual void MarkHandlerReady(void) { MarkReady(); } + virtual CCECProcessor * GetProcessor(void) const { return m_processor; } virtual uint64_t GetLastActive(void) const { return m_iLastActive; } virtual cec_device_type GetType(void) const { return m_type; } @@ -124,7 +216,7 @@ namespace CEC virtual void SetMenuState(const cec_menu_state state); virtual bool TransmitMenuState(const cec_logical_address destination); - virtual bool ActivateSource(void); + virtual bool ActivateSource(uint64_t iDelay = 0); virtual bool IsActiveSource(void) const { return m_bActiveSource; } virtual bool RequestActiveSource(bool bWaitForResponse = true); virtual void MarkAsActiveSource(void); @@ -141,6 +233,8 @@ namespace CEC virtual bool TryLogicalAddress(void); CCECClient * GetClient(void); + void SignalOpcode(cec_opcode opcode); + bool WaitForOpcode(cec_opcode opcode); CCECAudioSystem * AsAudioSystem(void); static CCECAudioSystem * AsAudioSystem(CCECBusDevice *device); @@ -184,5 +278,6 @@ namespace CEC unsigned m_iHandlerUseCount; bool m_bAwaitingReceiveFailed; bool m_bVendorIdRequested; + CWaitForResponse *m_waitForResponse; }; }; diff --git a/src/lib/implementations/ANCommandHandler.cpp b/src/lib/implementations/ANCommandHandler.cpp index 7c2d059..8eccf88 100644 --- a/src/lib/implementations/ANCommandHandler.cpp +++ b/src/lib/implementations/ANCommandHandler.cpp @@ -41,68 +41,56 @@ using namespace CEC; #define LIB_CEC m_busDevice->GetProcessor()->GetLib() #define ToString(p) LIB_CEC->ToString(p) -CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice) : - CCECCommandHandler(busDevice) +CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */, + int32_t iTransmitWait /* = CEC_DEFAULT_TRANSMIT_WAIT */, + int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */, + int64_t iActiveSourcePending /* = 0 */) : + CCECCommandHandler(busDevice, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending) { m_vendorId = CEC_VENDOR_SAMSUNG; m_bOPTSendDeckStatusUpdateOnActiveSource = false; } -bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command) +int CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command) { - if (m_processor->CECInitialised() && command.parameters.size > 0) - { - CCECClient *client = m_processor->GetClient(command.destination); - - cec_keypress key; - key.duration = CEC_BUTTON_TIMEOUT; - key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN; + if (command.parameters.size == 0) + return CEC_ABORT_REASON_INVALID_OPERAND; - switch (command.parameters[0]) - { - case CEC_USER_CONTROL_CODE_AN_RETURN: - key.keycode = client && client->GetClientVersion() >= CEC_CLIENT_VERSION_1_5_0 ? - CEC_USER_CONTROL_CODE_AN_RETURN : - CEC_USER_CONTROL_CODE_EXIT; - break; - case CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST: - key.keycode = CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST; - break; - default: - break; - } + if (!m_processor->CECInitialised()) + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; - if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN && client) - client->AddKey(key); - } + CCECClient *client = m_processor->GetClient(command.destination); + if (!client) + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; - return true; -} + cec_keypress key; + key.duration = CEC_BUTTON_TIMEOUT; + key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN; -bool CANCommandHandler::HandleCommand(const cec_command &command) -{ - bool bHandled(false); - if (m_processor->IsHandledByLibCEC(command.destination)) + switch (command.parameters[0]) { - 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; - } + case CEC_USER_CONTROL_CODE_AN_RETURN: + key.keycode = client && client->GetClientVersion() >= CEC_CLIENT_VERSION_1_5_0 ? + CEC_USER_CONTROL_CODE_AN_RETURN : + CEC_USER_CONTROL_CODE_EXIT; + break; + case CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST: + key.keycode = CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST; + break; + default: + break; } - if (!bHandled) - bHandled = CCECCommandHandler::HandleCommand(command); + if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN && client) + client->AddKey(key); + + return COMMAND_HANDLED; +} - return bHandled; +int CANCommandHandler::HandleVendorRemoteButtonUp(const cec_command &command) +{ + return HandleUserControlRelease(command); } bool CANCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination) diff --git a/src/lib/implementations/ANCommandHandler.h b/src/lib/implementations/ANCommandHandler.h index 567684f..3b08cbd 100644 --- a/src/lib/implementations/ANCommandHandler.h +++ b/src/lib/implementations/ANCommandHandler.h @@ -38,13 +38,17 @@ namespace CEC class CANCommandHandler : public CCECCommandHandler { public: - CANCommandHandler(CCECBusDevice *busDevice); + CANCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout = CEC_DEFAULT_TRANSMIT_TIMEOUT, + int32_t iTransmitWait = CEC_DEFAULT_TRANSMIT_WAIT, + int8_t iTransmitRetries = CEC_DEFAULT_TRANSMIT_RETRIES, + int64_t iActiveSourcePending = 0); virtual ~CANCommandHandler(void) {}; - bool HandleCommand(const cec_command &command); + int HandleVendorRemoteButtonDown(const cec_command &command); + int HandleVendorRemoteButtonUp(const cec_command &command); protected: - bool HandleVendorRemoteButtonDown(const cec_command &command); bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); }; }; diff --git a/src/lib/implementations/CECCommandHandler.cpp b/src/lib/implementations/CECCommandHandler.cpp index a240c90..ffe78b8 100644 --- a/src/lib/implementations/CECCommandHandler.cpp +++ b/src/lib/implementations/CECCommandHandler.cpp @@ -47,346 +47,343 @@ using namespace PLATFORM; #define LIB_CEC m_busDevice->GetProcessor()->GetLib() #define ToString(p) CCECTypeUtils::ToString(p) -CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice) : +CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */, + int32_t iTransmitWait /* = CEC_DEFAULT_TRANSMIT_WAIT */, + int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */, + int64_t iActiveSourcePending /* = 0 */) : m_busDevice(busDevice), m_processor(m_busDevice->GetProcessor()), - m_iTransmitTimeout(CEC_DEFAULT_TRANSMIT_TIMEOUT), - m_iTransmitWait(CEC_DEFAULT_TRANSMIT_WAIT), - m_iTransmitRetries(CEC_DEFAULT_TRANSMIT_RETRIES), + m_iTransmitTimeout(iTransmitTimeout), + m_iTransmitWait(iTransmitWait), + m_iTransmitRetries(iTransmitRetries), m_bHandlerInited(false), m_bOPTSendDeckStatusUpdateOnActiveSource(false), m_vendorId(CEC_VENDOR_UNKNOWN), - m_waitForResponse(new CWaitForResponse), - m_bActiveSourcePending(false) + m_iActiveSourcePending(iActiveSourcePending) { } -CCECCommandHandler::~CCECCommandHandler(void) -{ - DELETE_AND_NULL(m_waitForResponse); -} - bool CCECCommandHandler::HandleCommand(const cec_command &command) { if (command.opcode_set == 0) return HandlePoll(command); - bool bHandled(true); + int iHandled(CEC_ABORT_REASON_UNRECOGNIZED_OPCODE); - CCECClient *client = m_busDevice->GetClient(); - if (client) - client->AddCommand(command); + LIB_CEC->AddCommand(command); switch(command.opcode) { case CEC_OPCODE_REPORT_POWER_STATUS: - HandleReportPowerStatus(command); + iHandled = HandleReportPowerStatus(command); break; case CEC_OPCODE_CEC_VERSION: - HandleDeviceCecVersion(command); + iHandled = HandleDeviceCecVersion(command); break; case CEC_OPCODE_SET_MENU_LANGUAGE: - HandleSetMenuLanguage(command); + iHandled = HandleSetMenuLanguage(command); break; case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: - if (m_processor->CECInitialised()) - HandleGivePhysicalAddress(command); + iHandled = HandleGivePhysicalAddress(command); break; case CEC_OPCODE_GET_MENU_LANGUAGE: - if (m_processor->CECInitialised()) - HandleGiveMenuLanguage(command); + iHandled = HandleGiveMenuLanguage(command); break; case CEC_OPCODE_GIVE_OSD_NAME: - if (m_processor->CECInitialised()) - HandleGiveOSDName(command); + iHandled = HandleGiveOSDName(command); break; case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID: - if (m_processor->CECInitialised()) - HandleGiveDeviceVendorId(command); + iHandled = HandleGiveDeviceVendorId(command); break; case CEC_OPCODE_DEVICE_VENDOR_ID: - HandleDeviceVendorId(command); + iHandled = HandleDeviceVendorId(command); break; case CEC_OPCODE_VENDOR_COMMAND_WITH_ID: - HandleDeviceVendorCommandWithId(command); + iHandled = HandleDeviceVendorCommandWithId(command); break; case CEC_OPCODE_GIVE_DECK_STATUS: - if (m_processor->CECInitialised()) - HandleGiveDeckStatus(command); + iHandled = HandleGiveDeckStatus(command); break; case CEC_OPCODE_DECK_CONTROL: - HandleDeckControl(command); + iHandled = HandleDeckControl(command); break; case CEC_OPCODE_MENU_REQUEST: - if (m_processor->CECInitialised()) - HandleMenuRequest(command); + iHandled = HandleMenuRequest(command); break; case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS: - if (m_processor->CECInitialised()) - HandleGiveDevicePowerStatus(command); + iHandled = HandleGiveDevicePowerStatus(command); break; case CEC_OPCODE_GET_CEC_VERSION: - if (m_processor->CECInitialised()) - HandleGetCecVersion(command); + iHandled = HandleGetCecVersion(command); break; case CEC_OPCODE_USER_CONTROL_PRESSED: - if (m_processor->CECInitialised()) - HandleUserControlPressed(command); + iHandled = HandleUserControlPressed(command); break; case CEC_OPCODE_USER_CONTROL_RELEASE: - if (m_processor->CECInitialised()) - HandleUserControlRelease(command); + iHandled = HandleUserControlRelease(command); break; case CEC_OPCODE_GIVE_AUDIO_STATUS: - if (m_processor->CECInitialised()) - HandleGiveAudioStatus(command); + iHandled = HandleGiveAudioStatus(command); break; case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS: - if (m_processor->CECInitialised()) - HandleGiveSystemAudioModeStatus(command); + iHandled = HandleGiveSystemAudioModeStatus(command); break; case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST: - if (m_processor->CECInitialised()) - HandleSystemAudioModeRequest(command); + iHandled = HandleSystemAudioModeRequest(command); break; case CEC_OPCODE_REPORT_AUDIO_STATUS: - HandleReportAudioStatus(command); + iHandled = HandleReportAudioStatus(command); break; case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS: - HandleSystemAudioModeStatus(command); + iHandled = HandleSystemAudioModeStatus(command); break; case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE: - HandleSetSystemAudioMode(command); + iHandled = HandleSetSystemAudioMode(command); break; case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: - if (m_processor->CECInitialised()) - HandleRequestActiveSource(command); + iHandled = HandleRequestActiveSource(command); break; case CEC_OPCODE_SET_STREAM_PATH: - HandleSetStreamPath(command); + iHandled = HandleSetStreamPath(command); break; case CEC_OPCODE_ROUTING_CHANGE: - HandleRoutingChange(command); + iHandled = HandleRoutingChange(command); break; case CEC_OPCODE_ROUTING_INFORMATION: - HandleRoutingInformation(command); + iHandled = HandleRoutingInformation(command); break; case CEC_OPCODE_STANDBY: - if (m_processor->CECInitialised()) - HandleStandby(command); + iHandled = HandleStandby(command); break; case CEC_OPCODE_ACTIVE_SOURCE: - HandleActiveSource(command); + iHandled = HandleActiveSource(command); break; case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS: - HandleReportPhysicalAddress(command); + iHandled = HandleReportPhysicalAddress(command); break; case CEC_OPCODE_SET_OSD_NAME: - HandleSetOSDName(command); + iHandled = HandleSetOSDName(command); break; case CEC_OPCODE_IMAGE_VIEW_ON: - HandleImageViewOn(command); + iHandled = HandleImageViewOn(command); break; case CEC_OPCODE_TEXT_VIEW_ON: - HandleTextViewOn(command); + iHandled = HandleTextViewOn(command); break; case CEC_OPCODE_FEATURE_ABORT: - HandleFeatureAbort(command); + iHandled = HandleFeatureAbort(command); break; case CEC_OPCODE_VENDOR_COMMAND: - HandleVendorCommand(command); + iHandled = HandleVendorCommand(command); + break; + case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN: + iHandled = HandleVendorRemoteButtonDown(command); + break; + case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP: + iHandled = HandleVendorRemoteButtonUp(command); break; case CEC_OPCODE_PLAY: // libCEC (currently) doesn't need to do anything with this, since player applications handle it // but it should not respond with a feature abort + iHandled = COMMAND_HANDLED; break; default: - bHandled = false; break; } - if (bHandled) - m_waitForResponse->Received((command.opcode == CEC_OPCODE_FEATURE_ABORT && command.parameters.size > 0) ? (cec_opcode)command.parameters[0] : command.opcode); + if (iHandled == COMMAND_HANDLED) + m_busDevice->SignalOpcode((command.opcode == CEC_OPCODE_FEATURE_ABORT && command.parameters.size > 0) ? (cec_opcode)command.parameters[0] : command.opcode); else - UnhandledCommand(command); + UnhandledCommand(command, (cec_abort_reason)iHandled); - return bHandled; + return iHandled == COMMAND_HANDLED; } -bool CCECCommandHandler::HandleActiveSource(const cec_command &command) +int CCECCommandHandler::HandleActiveSource(const cec_command &command) { if (command.parameters.size == 2) { uint16_t iAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]); CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iAddress); if (device) + { device->MarkAsActiveSource(); + return COMMAND_HANDLED; + } } - return true; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleDeckControl(const cec_command &command) +int CCECCommandHandler::HandleDeckControl(const cec_command &command) { CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination)); if (device && command.parameters.size > 0) { device->SetDeckControlMode((cec_deck_control_mode) command.parameters[0]); - return true; + return COMMAND_HANDLED; } - return false; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleDeviceCecVersion(const cec_command &command) +int 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 COMMAND_HANDLED; } - return true; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command) +int CCECCommandHandler::HandleDeviceVendorCommandWithId(const cec_command & UNUSED(command)) { - if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) - m_processor->TransmitAbort(command.destination, command.initiator, command.opcode, CEC_ABORT_REASON_REFUSED); - - return true; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleDeviceVendorId(const cec_command &command) +int CCECCommandHandler::HandleDeviceVendorId(const cec_command &command) { - return SetVendorId(command); + SetVendorId(command); + return COMMAND_HANDLED; } -bool CCECCommandHandler::HandleFeatureAbort(const cec_command &command) +int CCECCommandHandler::HandleFeatureAbort(const cec_command &command) { if (command.parameters.size == 2 && (command.parameters[1] == CEC_ABORT_REASON_UNRECOGNIZED_OPCODE || command.parameters[1] == CEC_ABORT_REASON_REFUSED)) m_processor->GetDevice(command.initiator)->SetUnsupportedFeature((cec_opcode)command.parameters[0]); - return true; + return COMMAND_HANDLED; } -bool CCECCommandHandler::HandleGetCecVersion(const cec_command &command) +int CCECCommandHandler::HandleGetCecVersion(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECBusDevice *device = GetDevice(command.destination); - if (device) - return device->TransmitCECVersion(command.initiator); + if (device && device->TransmitCECVersion(command.initiator)) + return COMMAND_HANDLED; + return CEC_ABORT_REASON_INVALID_OPERAND; } - return false; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CCECCommandHandler::HandleGiveAudioStatus(const cec_command &command) +int CCECCommandHandler::HandleGiveAudioStatus(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination)); - if (device) - return device->TransmitAudioStatus(command.initiator); + if (device && device->TransmitAudioStatus(command.initiator)) + return COMMAND_HANDLED; + return CEC_ABORT_REASON_INVALID_OPERAND; } - return false; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CCECCommandHandler::HandleGiveDeckStatus(const cec_command &command) +int CCECCommandHandler::HandleGiveDeckStatus(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination)); - if (device) - return device->TransmitDeckStatus(command.initiator); + if (device && device->TransmitDeckStatus(command.initiator)) + return COMMAND_HANDLED; + return CEC_ABORT_REASON_INVALID_OPERAND; } - return false; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CCECCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command) +int CCECCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECBusDevice *device = GetDevice(command.destination); - if (device) - return device->TransmitPowerState(command.initiator); + if (device && device->TransmitPowerState(command.initiator)) + return COMMAND_HANDLED; + return CEC_ABORT_REASON_INVALID_OPERAND; } - return false; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleGiveDeviceVendorId(const cec_command &command) +int CCECCommandHandler::HandleGiveDeviceVendorId(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECBusDevice *device = GetDevice(command.destination); - if (device) - return device->TransmitVendorID(command.initiator); + if (device && device->TransmitVendorID(command.initiator)) + return COMMAND_HANDLED; } - return false; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleGiveOSDName(const cec_command &command) +int CCECCommandHandler::HandleGiveOSDName(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECBusDevice *device = GetDevice(command.destination); - if (device) - return device->TransmitOSDName(command.initiator); + if (device && device->TransmitOSDName(command.initiator)) + return COMMAND_HANDLED; } - return false; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command) +int CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECBusDevice *device = GetDevice(command.destination); - if (device) - return device->TransmitPhysicalAddress(); + if (device && device->TransmitPhysicalAddress()) + return COMMAND_HANDLED; + return CEC_ABORT_REASON_INVALID_OPERAND; } - return false; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CCECCommandHandler::HandleGiveMenuLanguage(const cec_command &command) +int CCECCommandHandler::HandleGiveMenuLanguage(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECBusDevice *device = GetDevice(command.destination); - if (device) - return device->TransmitSetMenuLanguage(command.initiator); + if (device && device->TransmitSetMenuLanguage(command.initiator)) + return COMMAND_HANDLED; + return CEC_ABORT_REASON_INVALID_OPERAND; } - return false; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CCECCommandHandler::HandleGiveSystemAudioModeStatus(const cec_command &command) +int CCECCommandHandler::HandleGiveSystemAudioModeStatus(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination)); - if (device) - return device->TransmitSystemAudioModeStatus(command.initiator); + if (device && device->TransmitSystemAudioModeStatus(command.initiator)) + return COMMAND_HANDLED; + return CEC_ABORT_REASON_INVALID_OPERAND; } - return false; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CCECCommandHandler::HandleImageViewOn(const cec_command &command) +int CCECCommandHandler::HandleImageViewOn(const cec_command &command) { m_processor->GetDevice(command.initiator)->MarkAsActiveSource(); - return true; + return COMMAND_HANDLED; } -bool CCECCommandHandler::HandleMenuRequest(const cec_command &command) +int CCECCommandHandler::HandleMenuRequest(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { @@ -407,11 +404,13 @@ bool CCECCommandHandler::HandleMenuRequest(const cec_command &command) device->SetMenuState(CEC_MENU_STATE_DEACTIVATED); } } - return device->TransmitMenuState(command.initiator); + if (device->TransmitMenuState(command.initiator)) + return COMMAND_HANDLED; } + return CEC_ABORT_REASON_INVALID_OPERAND; } - return false; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } bool CCECCommandHandler::HandlePoll(const cec_command &command) @@ -420,7 +419,7 @@ bool CCECCommandHandler::HandlePoll(const cec_command &command) return true; } -bool CCECCommandHandler::HandleReportAudioStatus(const cec_command &command) +int CCECCommandHandler::HandleReportAudioStatus(const cec_command &command) { if (command.parameters.size == 1) { @@ -428,34 +427,38 @@ bool CCECCommandHandler::HandleReportAudioStatus(const cec_command &command) if (device) { device->SetAudioStatus(command.parameters[0]); - return true; + return COMMAND_HANDLED; } } - return false; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleReportPhysicalAddress(const cec_command &command) +int CCECCommandHandler::HandleReportPhysicalAddress(const cec_command &command) { if (command.parameters.size == 3) { uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]); SetPhysicalAddress(command.initiator, iNewAddress); + return COMMAND_HANDLED; } - return true; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleReportPowerStatus(const cec_command &command) +int 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 COMMAND_HANDLED; + } } - return true; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleRequestActiveSource(const cec_command &command) +int CCECCommandHandler::HandleRequestActiveSource(const cec_command &command) { if (m_processor->CECInitialised()) { @@ -465,13 +468,12 @@ bool CCECCommandHandler::HandleRequestActiveSource(const cec_command &command) vector devices; for (size_t iDevicePtr = 0; iDevicePtr < GetMyDevices(devices); iDevicePtr++) devices[iDevicePtr]->TransmitActiveSource(); - - return true; } - return false; + + return COMMAND_HANDLED; } -bool CCECCommandHandler::HandleRoutingChange(const cec_command &command) +int CCECCommandHandler::HandleRoutingChange(const cec_command &command) { if (command.parameters.size == 4) { @@ -480,25 +482,32 @@ bool CCECCommandHandler::HandleRoutingChange(const cec_command &command) CCECBusDevice *device = GetDevice(command.initiator); if (device) + { device->SetStreamPath(iNewAddress, iOldAddress); + return COMMAND_HANDLED; + } } - return true; + + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleRoutingInformation(const cec_command &command) +int CCECCommandHandler::HandleRoutingInformation(const cec_command &command) { if (command.parameters.size == 2) { uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]); CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress); if (device) + { device->MarkAsActiveSource(); + return COMMAND_HANDLED; + } } - return false; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleSetMenuLanguage(const cec_command &command) +int CCECCommandHandler::HandleSetMenuLanguage(const cec_command &command) { if (command.parameters.size == 3) { @@ -511,13 +520,14 @@ bool CCECCommandHandler::HandleSetMenuLanguage(const cec_command &command) language.language[iPtr] = command.parameters[iPtr]; language.language[3] = 0; device->SetMenuLanguage(language); - return true; + return COMMAND_HANDLED; } } - return false; + + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleSetOSDName(const cec_command &command) +int CCECCommandHandler::HandleSetOSDName(const cec_command &command) { if (command.parameters.size > 0) { @@ -532,28 +542,39 @@ bool CCECCommandHandler::HandleSetOSDName(const cec_command &command) CStdString strName(buf); device->SetOSDName(strName); - return true; + return COMMAND_HANDLED; } } - return false; + + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleSetStreamPath(const cec_command &command) +int CCECCommandHandler::HandleSetStreamPath(const cec_command &command) { - if (m_processor->CECInitialised() && command.parameters.size >= 2) + if (!m_processor->CECInitialised()) + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; + + if (command.parameters.size >= 2) { uint16_t iStreamAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]); - LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %i sets stream path to physical address %04x", command.initiator, iStreamAddress); + LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s (%x) sets stream path to physical address %04x", ToString(command.initiator), command.initiator, iStreamAddress); + + // a device will only change the stream path when it's powered on + m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON); /* one of the device handled by libCEC has been made active */ CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamAddress); if (device && device->IsHandledByLibCEC()) + { device->ActivateSource(); + return COMMAND_HANDLED; + } } - return false; + + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleSystemAudioModeRequest(const cec_command &command) +int CCECCommandHandler::HandleSystemAudioModeRequest(const cec_command &command) { if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) { @@ -568,28 +589,31 @@ bool CCECCommandHandler::HandleSystemAudioModeRequest(const cec_command &command CCECBusDevice *newActiveDevice = GetDeviceByPhysicalAddress(iNewAddress); if (newActiveDevice) newActiveDevice->MarkAsActiveSource(); - return device->TransmitSetSystemAudioMode(command.initiator); + if (device->TransmitSetSystemAudioMode(command.initiator)) + return COMMAND_HANDLED; } else { device->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_OFF); - return device->TransmitSetSystemAudioMode(command.initiator); + if (device->TransmitSetSystemAudioMode(command.initiator)) + return COMMAND_HANDLED; } } } - return false; + + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CCECCommandHandler::HandleStandby(const cec_command &command) +int CCECCommandHandler::HandleStandby(const cec_command &command) { CCECBusDevice *device = GetDevice(command.initiator); if (device) device->SetPowerStatus(CEC_POWER_STATUS_STANDBY); - return true; + return COMMAND_HANDLED; } -bool CCECCommandHandler::HandleSystemAudioModeStatus(const cec_command &command) +int CCECCommandHandler::HandleSystemAudioModeStatus(const cec_command &command) { if (command.parameters.size == 1) { @@ -597,14 +621,14 @@ bool CCECCommandHandler::HandleSystemAudioModeStatus(const cec_command &command) if (device) { device->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]); - return true; + return COMMAND_HANDLED; } } - return false; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleSetSystemAudioMode(const cec_command &command) +int CCECCommandHandler::HandleSetSystemAudioMode(const cec_command &command) { if (command.parameters.size == 1) { @@ -612,87 +636,92 @@ bool CCECCommandHandler::HandleSetSystemAudioMode(const cec_command &command) if (device) { device->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]); - return true; + return COMMAND_HANDLED; } } - return false; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CCECCommandHandler::HandleTextViewOn(const cec_command &command) +int CCECCommandHandler::HandleTextViewOn(const cec_command &command) { m_processor->GetDevice(command.initiator)->MarkAsActiveSource(); - return true; + return COMMAND_HANDLED; } -bool CCECCommandHandler::HandleUserControlPressed(const cec_command &command) +int CCECCommandHandler::HandleUserControlPressed(const cec_command &command) { - if (m_processor->CECInitialised() && - m_processor->IsHandledByLibCEC(command.destination) && - command.parameters.size > 0) - { - CCECBusDevice *device = GetDevice(command.destination); - if (!device) - return true; + if (!m_processor->CECInitialised() || + !m_processor->IsHandledByLibCEC(command.destination)) + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; - CCECClient *client = device->GetClient(); - if (client) - client->AddKey(); + if (command.parameters.size == 0) + return CEC_ABORT_REASON_INVALID_OPERAND; - if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX) - client->SetCurrentButton((cec_user_control_code) command.parameters[0]); + CCECBusDevice *device = GetDevice(command.destination); + if (!device) + return CEC_ABORT_REASON_INVALID_OPERAND; - if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER || - command.parameters[0] == CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION) - { - bool bPowerOn(true); - if (!device) - return true; + CCECClient *client = device->GetClient(); + if (client) + client->AddKey(); - // CEC_USER_CONTROL_CODE_POWER operates as a toggle - // assume CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION does not - if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER) - { - cec_power_status status = device->GetCurrentPowerStatus(); - bPowerOn = !(status == CEC_POWER_STATUS_ON || status == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); - } + if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX) + client->SetCurrentButton((cec_user_control_code) command.parameters[0]); - if (bPowerOn) - { - device->ActivateSource(); - } - else - { - device->MarkAsInactiveSource(); - device->TransmitInactiveSource(); - device->SetMenuState(CEC_MENU_STATE_DEACTIVATED); - } + if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER || + command.parameters[0] == CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION) + { + bool bPowerOn(true); + + // CEC_USER_CONTROL_CODE_POWER operates as a toggle + // assume CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION does not + if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER) + { + cec_power_status status = device->GetCurrentPowerStatus(); + bPowerOn = !(status == CEC_POWER_STATUS_ON || status == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); } - return true; + if (bPowerOn) + { + device->ActivateSource(); + } + else + { + device->MarkAsInactiveSource(); + device->TransmitInactiveSource(); + device->SetMenuState(CEC_MENU_STATE_DEACTIVATED); + } } - return false; + + return COMMAND_HANDLED; } -bool CCECCommandHandler::HandleUserControlRelease(const cec_command &command) +int CCECCommandHandler::HandleUserControlRelease(const cec_command &command) { + if (!m_processor->CECInitialised() || + !m_processor->IsHandledByLibCEC(command.destination)) + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; + CCECClient *client = m_processor->GetClient(command.destination); if (client) client->AddKey(); - return true; + + return COMMAND_HANDLED; } -bool CCECCommandHandler::HandleVendorCommand(const cec_command & UNUSED(command)) +int CCECCommandHandler::HandleVendorCommand(const cec_command & UNUSED(command)) { - return true; + return CEC_ABORT_REASON_INVALID_OPERAND; } -void CCECCommandHandler::UnhandledCommand(const cec_command &command) +void CCECCommandHandler::UnhandledCommand(const cec_command &command, const cec_abort_reason reason) { - LIB_CEC->AddLog(CEC_LOG_DEBUG, "unhandled command with opcode %02x from address %d", command.opcode, command.initiator); - - if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) - m_processor->TransmitAbort(m_busDevice->GetLogicalAddress(), command.initiator, command.opcode, CEC_ABORT_REASON_UNRECOGNIZED_OPCODE); + if (m_processor->IsHandledByLibCEC(command.destination)) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending abort with opcode %02x and reason '%s' to %s", command.opcode, ToString(reason), ToString(command.initiator)); + m_processor->TransmitAbort(command.destination, command.initiator, command.opcode, reason); + } } size_t CCECCommandHandler::GetMyDevices(vector &devices) const @@ -1046,7 +1075,7 @@ bool CCECCommandHandler::Transmit(cec_command &command, bool bSuppressWait /* = LIB_CEC->AddLog(CEC_LOG_DEBUG, "command transmitted"); if (bExpectResponse) { - bReturn = m_waitForResponse->Wait(expectedResponse); + bReturn = m_busDevice->WaitForOpcode(expectedResponse); LIB_CEC->AddLog(CEC_LOG_DEBUG, bReturn ? "expected response received (%X: %s)" : "expected response not received (%X: %s)", (int)expectedResponse, ToString(expectedResponse)); } } @@ -1056,50 +1085,71 @@ bool CCECCommandHandler::Transmit(cec_command &command, bool bSuppressWait /* = return bReturn; } -bool CCECCommandHandler::ActivateSource(void) +bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = false */) { if (m_busDevice->IsActiveSource() && - m_busDevice->IsHandledByLibCEC()) + m_busDevice->IsHandledByLibCEC()) { { CLockObject lock(m_mutex); - m_bActiveSourcePending = false; + // check if we need to send a delayed source switch + if (bTransmitDelayedCommandsOnly) + { + if (m_iActiveSourcePending == 0 || GetTimeMs() < m_iActiveSourcePending) + return false; + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command"); + } + + // clear previous pending active source command + m_iActiveSourcePending = 0; } + // update the power state and menu state m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON); - m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED); + m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED); // TODO: LG + + // power on the TV + bool bActiveSourceFailed = !m_busDevice->TransmitImageViewOn(); - bool bActiveSourceFailed = !m_busDevice->TransmitImageViewOn() || - !m_busDevice->TransmitActiveSource() || - !m_busDevice->TransmitMenuState(CECDEVICE_TV); + // check if we're allowed to switch sources + bool bSourceSwitchAllowed = SourceSwitchAllowed(); + if (!bSourceSwitchAllowed) + LIB_CEC->AddLog(CEC_LOG_DEBUG, "source switch is currently not allowed by command handler"); - if (!bActiveSourceFailed) + // switch sources (if allowed) + if (!bActiveSourceFailed && bSourceSwitchAllowed) { - CCECPlaybackDevice *playbackDevice = m_busDevice->AsPlaybackDevice(); - if (playbackDevice && SendDeckStatusUpdateOnActiveSource()) - bActiveSourceFailed = !playbackDevice->TransmitDeckStatus(CECDEVICE_TV); + bActiveSourceFailed = !m_busDevice->TransmitActiveSource() || + !m_busDevice->TransmitMenuState(CECDEVICE_TV); + + // update the deck status for playback devices + if (!bActiveSourceFailed) + { + CCECPlaybackDevice *playbackDevice = m_busDevice->AsPlaybackDevice(); + if (playbackDevice && SendDeckStatusUpdateOnActiveSource()) + bActiveSourceFailed = !playbackDevice->TransmitDeckStatus(CECDEVICE_TV); + } } - if (bActiveSourceFailed) + // retry later + if (bActiveSourceFailed || !bSourceSwitchAllowed) { LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to make '%s' the active source. will retry later", m_busDevice->GetLogicalAddressName()); CLockObject lock(m_mutex); - m_bActiveSourcePending = true; + m_iActiveSourcePending = GetTimeMs() + (int64_t)CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS; return false; } + // mark the handler as initialised + CLockObject lock(m_mutex); m_bHandlerInited = true; } return true; } -void CCECCommandHandler::SignalOpcode(cec_opcode opcode) -{ - m_waitForResponse->Received(opcode); -} - -bool CCECCommandHandler::ActiveSourcePending(void) +void CCECCommandHandler::ScheduleActivateSource(uint64_t iDelay) { CLockObject lock(m_mutex); - return m_bActiveSourcePending; + m_iActiveSourcePending = GetTimeMs() + iDelay; } diff --git a/src/lib/implementations/CECCommandHandler.h b/src/lib/implementations/CECCommandHandler.h index 3a9bd6f..bc42128 100644 --- a/src/lib/implementations/CECCommandHandler.h +++ b/src/lib/implementations/CECCommandHandler.h @@ -33,92 +33,27 @@ #include "../../../include/cectypes.h" #include -#include #include "../platform/threads/mutex.h" #include "../platform/util/StdString.h" namespace CEC { + #define COMMAND_HANDLED 0xFF + class CCECProcessor; class CCECBusDevice; - class CResponse - { - public: - CResponse(cec_opcode opcode) : - m_opcode(opcode){} - ~CResponse(void) - { - Broadcast(); - } - - bool Wait(uint32_t iTimeout) - { - return m_event.Wait(iTimeout); - } - - void Broadcast(void) - { - m_event.Broadcast(); - } - - private: - cec_opcode m_opcode; - PLATFORM::CEvent m_event; - }; - - class CWaitForResponse - { - public: - CWaitForResponse(void) {} - ~CWaitForResponse(void) - { - PLATFORM::CLockObject lock(m_mutex); - m_waitingFor.clear(); - } - - bool Wait(cec_opcode opcode, uint32_t iTimeout = CEC_DEFAULT_TRANSMIT_WAIT) - { - CResponse *response = GetEvent(opcode); - return response ? response->Wait(iTimeout) : false; - } - - void Received(cec_opcode opcode) - { - CResponse *response = GetEvent(opcode); - if (response) - response->Broadcast(); - } - - private: - CResponse *GetEvent(cec_opcode opcode) - { - CResponse *retVal(NULL); - { - PLATFORM::CLockObject lock(m_mutex); - std::map::iterator it = m_waitingFor.find(opcode); - if (it != m_waitingFor.end()) - { - retVal = it->second; - } - else - { - retVal = new CResponse(opcode); - m_waitingFor[opcode] = retVal; - } - return retVal; - } - } - - PLATFORM::CMutex m_mutex; - std::map m_waitingFor; - }; - class CCECCommandHandler { + friend class CCECBusDevice; + public: - CCECCommandHandler(CCECBusDevice *busDevice); - virtual ~CCECCommandHandler(void); + CCECCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout = CEC_DEFAULT_TRANSMIT_TIMEOUT, + int32_t iTransmitWait = CEC_DEFAULT_TRANSMIT_WAIT, + int8_t iTransmitRetries = CEC_DEFAULT_TRANSMIT_RETRIES, + int64_t iActiveSourcePending = 0); + virtual ~CCECCommandHandler(void) {}; virtual bool HandleCommand(const cec_command &command); virtual cec_vendor_id GetVendorId(void) { return m_vendorId; }; @@ -126,7 +61,7 @@ namespace CEC static bool HasSpecificHandler(cec_vendor_id vendorId) { return vendorId == CEC_VENDOR_LG || vendorId == CEC_VENDOR_SAMSUNG || vendorId == CEC_VENDOR_PANASONIC;} virtual bool InitHandler(void) { return true; } - virtual bool ActivateSource(void); + virtual bool ActivateSource(bool bTransmitDelayedCommandsOnly = false); virtual uint8_t GetTransmitRetries(void) const { return m_iTransmitRetries; } virtual bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); @@ -158,51 +93,51 @@ namespace CEC virtual bool TransmitKeyRelease(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWait = true); virtual bool TransmitSetStreamPath(uint16_t iStreamPath); virtual bool SendDeckStatusUpdateOnActiveSource(void) const { return m_bOPTSendDeckStatusUpdateOnActiveSource; }; - virtual bool TransmitPendingActiveSourceCommands(void) { return true; } - virtual void SignalOpcode(cec_opcode opcode); + virtual void ScheduleActivateSource(uint64_t iDelay); - virtual bool ActiveSourcePending(void); virtual bool SupportsDeviceType(const cec_device_type UNUSED(type)) const { return true; }; - cec_device_type GetReplacementDeviceType(const cec_device_type type) const { return type; } + virtual cec_device_type GetReplacementDeviceType(const cec_device_type type) const { return type; } protected: - virtual bool HandleActiveSource(const cec_command &command); - virtual bool HandleDeckControl(const cec_command &command); - virtual bool HandleDeviceCecVersion(const cec_command &command); - virtual bool HandleDeviceVendorCommandWithId(const cec_command &command); - virtual bool HandleDeviceVendorId(const cec_command &command); - virtual bool HandleFeatureAbort(const cec_command &command); - virtual bool HandleGetCecVersion(const cec_command &command); - virtual bool HandleGiveAudioStatus(const cec_command &command); - virtual bool HandleGiveDeckStatus(const cec_command &command); - virtual bool HandleGiveDevicePowerStatus(const cec_command &command); - virtual bool HandleGiveDeviceVendorId(const cec_command &command); - virtual bool HandleGiveOSDName(const cec_command &command); - virtual bool HandleGivePhysicalAddress(const cec_command &command); - virtual bool HandleGiveMenuLanguage(const cec_command &command); - virtual bool HandleGiveSystemAudioModeStatus(const cec_command &command); - virtual bool HandleImageViewOn(const cec_command &command); - virtual bool HandleMenuRequest(const cec_command &command); + virtual int HandleActiveSource(const cec_command &command); + virtual int HandleDeckControl(const cec_command &command); + virtual int HandleDeviceCecVersion(const cec_command &command); + virtual int HandleDeviceVendorCommandWithId(const cec_command &command); + virtual int HandleDeviceVendorId(const cec_command &command); + virtual int HandleFeatureAbort(const cec_command &command); + virtual int HandleGetCecVersion(const cec_command &command); + virtual int HandleGiveAudioStatus(const cec_command &command); + virtual int HandleGiveDeckStatus(const cec_command &command); + virtual int HandleGiveDevicePowerStatus(const cec_command &command); + virtual int HandleGiveDeviceVendorId(const cec_command &command); + virtual int HandleGiveOSDName(const cec_command &command); + virtual int HandleGivePhysicalAddress(const cec_command &command); + virtual int HandleGiveMenuLanguage(const cec_command &command); + virtual int HandleGiveSystemAudioModeStatus(const cec_command &command); + virtual int HandleImageViewOn(const cec_command &command); + virtual int HandleMenuRequest(const cec_command &command); virtual bool HandlePoll(const cec_command &command); - virtual bool HandleReportAudioStatus(const cec_command &command); - virtual bool HandleReportPhysicalAddress(const cec_command &command); - virtual bool HandleReportPowerStatus(const cec_command &command); - virtual bool HandleRequestActiveSource(const cec_command &command); - virtual bool HandleRoutingChange(const cec_command &command); - virtual bool HandleRoutingInformation(const cec_command &command); - virtual bool HandleSetMenuLanguage(const cec_command &command); - virtual bool HandleSetOSDName(const cec_command &command); - virtual bool HandleSetStreamPath(const cec_command &command); - virtual bool HandleSystemAudioModeRequest(const cec_command &command); - virtual bool HandleStandby(const cec_command &command); - virtual bool HandleSystemAudioModeStatus(const cec_command &command); - virtual bool HandleSetSystemAudioMode(const cec_command &command); - virtual bool HandleTextViewOn(const cec_command &command); - virtual bool HandleUserControlPressed(const cec_command &command); - virtual bool HandleUserControlRelease(const cec_command &command); - virtual bool HandleVendorCommand(const cec_command &command); - virtual void UnhandledCommand(const cec_command &command); + virtual int HandleReportAudioStatus(const cec_command &command); + virtual int HandleReportPhysicalAddress(const cec_command &command); + virtual int HandleReportPowerStatus(const cec_command &command); + virtual int HandleRequestActiveSource(const cec_command &command); + virtual int HandleRoutingChange(const cec_command &command); + virtual int HandleRoutingInformation(const cec_command &command); + virtual int HandleSetMenuLanguage(const cec_command &command); + virtual int HandleSetOSDName(const cec_command &command); + virtual int HandleSetStreamPath(const cec_command &command); + virtual int HandleSystemAudioModeRequest(const cec_command &command); + virtual int HandleStandby(const cec_command &command); + virtual int HandleSystemAudioModeStatus(const cec_command &command); + virtual int HandleSetSystemAudioMode(const cec_command &command); + virtual int HandleTextViewOn(const cec_command &command); + virtual int HandleUserControlPressed(const cec_command &command); + virtual int HandleUserControlRelease(const cec_command &command); + virtual int HandleVendorCommand(const cec_command &command); + virtual int HandleVendorRemoteButtonDown(const cec_command & UNUSED(command)) { return CEC_ABORT_REASON_REFUSED; } + virtual int HandleVendorRemoteButtonUp(const cec_command & UNUSED(command)) { return CEC_ABORT_REASON_REFUSED; } + virtual void UnhandledCommand(const cec_command &command, const cec_abort_reason reason); virtual size_t GetMyDevices(std::vector &devices) const; virtual CCECBusDevice *GetDevice(cec_logical_address iLogicalAddress) const; @@ -213,6 +148,8 @@ namespace CEC virtual bool Transmit(cec_command &command, bool bSuppressWait = false); + virtual bool SourceSwitchAllowed(void) { return true; } + CCECBusDevice * m_busDevice; CCECProcessor * m_processor; int32_t m_iTransmitTimeout; @@ -221,8 +158,7 @@ namespace CEC bool m_bHandlerInited; bool m_bOPTSendDeckStatusUpdateOnActiveSource; cec_vendor_id m_vendorId; - CWaitForResponse *m_waitForResponse; - bool m_bActiveSourcePending; + int64_t m_iActiveSourcePending; PLATFORM::CMutex m_mutex; }; }; diff --git a/src/lib/implementations/RLCommandHandler.cpp b/src/lib/implementations/RLCommandHandler.cpp index b913c32..5653137 100644 --- a/src/lib/implementations/RLCommandHandler.cpp +++ b/src/lib/implementations/RLCommandHandler.cpp @@ -38,8 +38,12 @@ using namespace CEC; using namespace PLATFORM; -CRLCommandHandler::CRLCommandHandler(CCECBusDevice *busDevice) : - CCECCommandHandler(busDevice) +CRLCommandHandler::CRLCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */, + int32_t iTransmitWait /* = CEC_DEFAULT_TRANSMIT_WAIT */, + int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */, + int64_t iActiveSourcePending /* = 0 */) : + CCECCommandHandler(busDevice, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending) { m_vendorId = CEC_VENDOR_TOSHIBA; } diff --git a/src/lib/implementations/RLCommandHandler.h b/src/lib/implementations/RLCommandHandler.h index 9766827..ec9edd9 100644 --- a/src/lib/implementations/RLCommandHandler.h +++ b/src/lib/implementations/RLCommandHandler.h @@ -39,7 +39,11 @@ namespace CEC class CRLCommandHandler : public CCECCommandHandler { public: - CRLCommandHandler(CCECBusDevice *busDevice); + CRLCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout = CEC_DEFAULT_TRANSMIT_TIMEOUT, + int32_t iTransmitWait = CEC_DEFAULT_TRANSMIT_WAIT, + int8_t iTransmitRetries = CEC_DEFAULT_TRANSMIT_RETRIES, + int64_t iActiveSourcePending = 0); virtual ~CRLCommandHandler(void) {}; bool InitHandler(void); diff --git a/src/lib/implementations/SLCommandHandler.cpp b/src/lib/implementations/SLCommandHandler.cpp index 6066d3b..222fc95 100644 --- a/src/lib/implementations/SLCommandHandler.cpp +++ b/src/lib/implementations/SLCommandHandler.cpp @@ -56,8 +56,12 @@ using namespace PLATFORM; #define LIB_CEC m_busDevice->GetProcessor()->GetLib() #define ToString(p) LIB_CEC->ToString(p) -CSLCommandHandler::CSLCommandHandler(CCECBusDevice *busDevice) : - CCECCommandHandler(busDevice), +CSLCommandHandler::CSLCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */, + int32_t iTransmitWait /* = CEC_DEFAULT_TRANSMIT_WAIT */, + int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */, + int64_t iActiveSourcePending /* = 0 */) : + CCECCommandHandler(busDevice, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending), m_bSLEnabled(false), m_bActiveSourceSent(false) { @@ -103,7 +107,7 @@ bool CSLCommandHandler::InitHandler(void) return true; } -bool CSLCommandHandler::HandleActiveSource(const cec_command &command) +int CSLCommandHandler::HandleActiveSource(const cec_command &command) { if (command.parameters.size == 2) { @@ -122,13 +126,15 @@ bool CSLCommandHandler::HandleActiveSource(const cec_command &command) } primary->TransmitPowerState(CECDEVICE_TV); } + + return COMMAND_HANDLED; } - return true; + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CSLCommandHandler::HandleDeviceVendorId(const cec_command &command) +int CSLCommandHandler::HandleDeviceVendorId(const cec_command &command) { SetVendorId(command); @@ -143,13 +149,15 @@ bool CSLCommandHandler::HandleDeviceVendorId(const cec_command &command) cec_command response; cec_command::Format(response, initiator, command.initiator, CEC_OPCODE_FEATURE_ABORT); - return Transmit(response); + Transmit(response); + return COMMAND_HANDLED; } } - return true; + + return CCECCommandHandler::HandleDeviceVendorId(command); } -bool CSLCommandHandler::HandleVendorCommand(const cec_command &command) +int CSLCommandHandler::HandleVendorCommand(const cec_command &command) { if (!m_processor->IsHandledByLibCEC(command.destination)) return true; @@ -158,28 +166,28 @@ bool CSLCommandHandler::HandleVendorCommand(const cec_command &command) command.parameters[0] == SL_COMMAND_UNKNOWN_01) { HandleVendorCommand01(command); - return true; + return COMMAND_HANDLED; } else if (command.parameters.size == 2 && command.parameters[0] == SL_COMMAND_POWER_ON) { HandleVendorCommandPowerOn(command); - return true; + return COMMAND_HANDLED; } else if (command.parameters.size == 2 && command.parameters[0] == SL_COMMAND_CONNECT_REQUEST) { HandleVendorCommandSLConnect(command); - return true; + return COMMAND_HANDLED; } else if (command.parameters.size == 1 && command.parameters[0] == SL_COMMAND_REQUEST_POWER_STATUS) { HandleVendorCommandPowerOnStatus(command); - return true; + return COMMAND_HANDLED; } - return false; + return CCECCommandHandler::HandleVendorCommand(command); } void CSLCommandHandler::HandleVendorCommand01(const cec_command &command) @@ -252,44 +260,41 @@ void CSLCommandHandler::TransmitVendorCommandSetDeviceMode(const cec_logical_add Transmit(response); } -bool CSLCommandHandler::HandleGiveDeckStatus(const cec_command &command) +int CSLCommandHandler::HandleGiveDeckStatus(const cec_command &command) { - if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination)) + if (!m_processor->CECInitialised() || + !m_processor->IsHandledByLibCEC(command.destination)) + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; + + CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination)); + if (!device || command.parameters.size == 0) + return CEC_ABORT_REASON_INVALID_OPERAND; + + device->SetDeckStatus(!device->IsActiveSource() ? CEC_DECK_INFO_OTHER_STATUS : CEC_DECK_INFO_OTHER_STATUS_LG); + if (command.parameters[0] == CEC_STATUS_REQUEST_ON) { - CCECBusDevice *device = GetDevice(command.destination); - if (device && (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)) - { - if (command.parameters.size > 0) - { - ((CCECPlaybackDevice *) device)->SetDeckStatus(!device->IsActiveSource() ? CEC_DECK_INFO_OTHER_STATUS : CEC_DECK_INFO_OTHER_STATUS_LG); - if (command.parameters[0] == CEC_STATUS_REQUEST_ON) - { - bool bReturn = ((CCECPlaybackDevice *) device)->TransmitDeckStatus(command.initiator); - if (!ActiveSourceSent()) - ActivateSource(); - return bReturn; - } - else if (command.parameters[0] == CEC_STATUS_REQUEST_ONCE) - { - return ((CCECPlaybackDevice *) device)->TransmitDeckStatus(command.initiator); - } - } - } - return CCECCommandHandler::HandleGiveDeckStatus(command); + device->TransmitDeckStatus(command.initiator); + if (!ActiveSourceSent()) + ActivateSource(); + return COMMAND_HANDLED; + } + else if (command.parameters[0] == CEC_STATUS_REQUEST_ONCE) + { + device->TransmitDeckStatus(command.initiator); + return COMMAND_HANDLED; } - return false; + return CCECCommandHandler::HandleGiveDeckStatus(command); } -bool CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command) +int CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command) { - bool bReturn(false); if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination) && command.initiator == CECDEVICE_TV) { CCECBusDevice *device = GetDevice(command.destination); if (device && device->GetCurrentPowerStatus() != CEC_POWER_STATUS_ON) { - bReturn = device->TransmitPowerState(command.initiator); + device->TransmitPowerState(command.initiator); device->SetPowerStatus(CEC_POWER_STATUS_ON); } else @@ -297,7 +302,7 @@ bool CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command) if (!ActiveSourceSent()) { device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); - bReturn = device->TransmitPowerState(command.initiator); + device->TransmitPowerState(command.initiator); ActivateSource(); } else if (m_resetPowerState.IsSet() && m_resetPowerState.TimeLeft() > 0) @@ -309,22 +314,24 @@ bool CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command) m_bActiveSourceSent = false; } device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); - bReturn = device->TransmitPowerState(command.initiator); + device->TransmitPowerState(command.initiator); device->SetPowerStatus(CEC_POWER_STATUS_ON); m_resetPowerState.Init(5000); } else { - bReturn = device->TransmitPowerState(command.initiator); + device->TransmitPowerState(command.initiator); m_resetPowerState.Init(5000); } } + + return COMMAND_HANDLED; } - return bReturn; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CSLCommandHandler::HandleRequestActiveSource(const cec_command &command) +int CSLCommandHandler::HandleRequestActiveSource(const cec_command &command) { if (m_processor->CECInitialised()) { @@ -332,12 +339,12 @@ bool CSLCommandHandler::HandleRequestActiveSource(const cec_command &command) LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %i requests active source, ignored", (uint8_t) command.initiator); else ActivateSource(); - return true; + return COMMAND_HANDLED; } - return false; + return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } -bool CSLCommandHandler::HandleFeatureAbort(const cec_command &command) +int CSLCommandHandler::HandleFeatureAbort(const cec_command &command) { if (command.parameters.size == 0 && m_processor->GetPrimaryDevice()->GetCurrentPowerStatus() == CEC_POWER_STATUS_ON && !SLInitialised() && command.initiator == CECDEVICE_TV) @@ -349,7 +356,7 @@ bool CSLCommandHandler::HandleFeatureAbort(const cec_command &command) return CCECCommandHandler::HandleFeatureAbort(command); } -bool CSLCommandHandler::HandleStandby(const cec_command &command) +int CSLCommandHandler::HandleStandby(const cec_command &command) { if (command.initiator == CECDEVICE_TV) { diff --git a/src/lib/implementations/SLCommandHandler.h b/src/lib/implementations/SLCommandHandler.h index 0eb6c86..8c6902d 100644 --- a/src/lib/implementations/SLCommandHandler.h +++ b/src/lib/implementations/SLCommandHandler.h @@ -39,15 +39,19 @@ namespace CEC class CSLCommandHandler : public CCECCommandHandler { public: - CSLCommandHandler(CCECBusDevice *busDevice); + CSLCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout = CEC_DEFAULT_TRANSMIT_TIMEOUT, + int32_t iTransmitWait = CEC_DEFAULT_TRANSMIT_WAIT, + int8_t iTransmitRetries = CEC_DEFAULT_TRANSMIT_RETRIES, + int64_t iActiveSourcePending = 0); virtual ~CSLCommandHandler(void) {}; bool InitHandler(void); protected: - bool HandleActiveSource(const cec_command &command); - bool HandleDeviceVendorId(const cec_command &command); - bool HandleVendorCommand(const cec_command &command); + int HandleActiveSource(const cec_command &command); + int HandleDeviceVendorId(const cec_command &command); + int HandleVendorCommand(const cec_command &command); void HandleVendorCommand01(const cec_command &command); void TransmitVendorCommand0205(const cec_logical_address iSource, const cec_logical_address iDestination); @@ -58,11 +62,11 @@ namespace CEC void HandleVendorCommandSLConnect(const cec_command &command); void TransmitVendorCommandSetDeviceMode(const cec_logical_address iSource, const cec_logical_address iDestination, const cec_device_type type); - bool HandleGiveDevicePowerStatus(const cec_command &command); - bool HandleGiveDeckStatus(const cec_command &command); - bool HandleRequestActiveSource(const cec_command &command); - bool HandleFeatureAbort(const cec_command &command); - bool HandleStandby(const cec_command &command); + int HandleGiveDevicePowerStatus(const cec_command &command); + int HandleGiveDeckStatus(const cec_command &command); + int HandleRequestActiveSource(const cec_command &command); + int HandleFeatureAbort(const cec_command &command); + int HandleStandby(const cec_command &command); bool TransmitMenuState(const cec_logical_address UNUSED(iInitiator), const cec_logical_address UNUSED(iDestination), cec_menu_state UNUSED(menuState)) { return true; } bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); diff --git a/src/lib/implementations/VLCommandHandler.cpp b/src/lib/implementations/VLCommandHandler.cpp index 3d96004..97d06a2 100644 --- a/src/lib/implementations/VLCommandHandler.cpp +++ b/src/lib/implementations/VLCommandHandler.cpp @@ -32,6 +32,8 @@ #include "VLCommandHandler.h" #include "../devices/CECBusDevice.h" +#include "../devices/CECPlaybackDevice.h" +#include "../devices/CECTV.h" #include "../CECProcessor.h" #include "../LibCEC.h" #include "../CECClient.h" @@ -39,6 +41,7 @@ #define VL_POWER_CHANGE 0x20 #define VL_POWERED_UP 0x00 #define VL_POWERED_DOWN 0x01 +#define VL_UNKNOWN1 0x06 using namespace CEC; using namespace PLATFORM; @@ -46,10 +49,16 @@ using namespace PLATFORM; #define LIB_CEC m_busDevice->GetProcessor()->GetLib() #define ToString(p) LIB_CEC->ToString(p) -CVLCommandHandler::CVLCommandHandler(CCECBusDevice *busDevice) : - CCECCommandHandler(busDevice), - m_bActiveSourcePending(false), - m_bPowerUpEventReceived(false) +// wait this amount of ms before trying to switch sources after receiving the message from the TV that it's powered on +#define SOURCE_SWITCH_DELAY_MS 1000 + +CVLCommandHandler::CVLCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */, + int32_t iTransmitWait /* = CEC_DEFAULT_TRANSMIT_WAIT */, + int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */, + int64_t iActiveSourcePending /* = 0 */) : + CCECCommandHandler(busDevice, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending), + m_iPowerUpEventReceived(0) { m_vendorId = CEC_VENDOR_PANASONIC; } @@ -71,92 +80,152 @@ bool CVLCommandHandler::InitHandler(void) if (primary->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) return m_processor->GetPrimaryClient()->ChangeDeviceType(CEC_DEVICE_TYPE_RECORDING_DEVICE, CEC_DEVICE_TYPE_PLAYBACK_DEVICE); + + m_processor->GetTV()->RequestPowerStatus(primary->GetLogicalAddress(), false); } return CCECCommandHandler::InitHandler(); } -bool CVLCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command) +int CVLCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command) { + if (command.parameters[0] != 0x00 || + command.parameters[1] != 0x80 || + command.parameters[2] != 0x45) + return CEC_ABORT_REASON_INVALID_OPERAND; + if (command.initiator == CECDEVICE_TV && + command.parameters.At(3) == VL_UNKNOWN1) + { + // set the power up event time + { + CLockObject lock(m_mutex); + if (m_iPowerUpEventReceived == 0) + m_iPowerUpEventReceived = GetTimeMs(); + } + // mark the TV as powered on + m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_ON); + } + else if (command.initiator == CECDEVICE_TV && command.destination == CECDEVICE_BROADCAST && command.parameters.At(3) == VL_POWER_CHANGE) { if (command.parameters.At(4) == VL_POWERED_UP) { - LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV powered up"); + // set the power up event time { CLockObject lock(m_mutex); - m_bPowerUpEventReceived = true; + if (m_iPowerUpEventReceived == 0) + m_iPowerUpEventReceived = GetTimeMs(); } - m_processor->TransmitPendingActiveSourceCommands(); + // mark the TV as powered on + m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_ON); } else if (command.parameters.At(4) == VL_POWERED_DOWN) - LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV powered down"); - else if (command.parameters.At(4) == VL_POWERED_DOWN) - LIB_CEC->AddLog(CEC_LOG_DEBUG, "unknown vendor command"); + { + // reset the power up event time + { + CLockObject lock(m_mutex); + m_iPowerUpEventReceived = 0; + } + // mark the TV as powered off + m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_STANDBY); + } + else + LIB_CEC->AddLog(CEC_LOG_DEBUG, "skipping unknown vendor command"); - return true; + return COMMAND_HANDLED; } return CCECCommandHandler::HandleDeviceVendorCommandWithId(command); } -bool CVLCommandHandler::TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress) +bool CVLCommandHandler::PowerUpEventReceived(void) { - bool bPowerUpEventReceived(false); - - CCECBusDevice *tv = m_processor->GetDevice(CECDEVICE_TV); - if (tv && tv->GetCurrentVendorId() == CEC_VENDOR_PANASONIC) - { - CVLCommandHandler *handler = static_cast(tv->GetHandler()); - bPowerUpEventReceived = handler ? handler->PowerUpEventReceived() : false; - } + bool bPowerUpEventReceived(true); - if (!bPowerUpEventReceived) + if (m_busDevice->GetLogicalAddress() != CECDEVICE_TV) { - CLockObject lock(m_mutex); - // wait until we received the event - m_bActiveSourcePending = true; - return true; + // get the status from the TV + CCECBusDevice *tv = m_processor->GetTV(); + if (tv && tv->GetCurrentVendorId() == CEC_VENDOR_PANASONIC) + { + CVLCommandHandler *handler = static_cast(tv->GetHandler()); + bPowerUpEventReceived = handler ? handler->PowerUpEventReceived() : false; + tv->MarkHandlerReady(); + } } else { - // transmit standard active source message - return CCECCommandHandler::TransmitActiveSource(iInitiator, iPhysicalAddress) && - TransmitMenuState(iInitiator, CECDEVICE_TV, CEC_MENU_STATE_ACTIVATED); + // get the current status + { + CLockObject lock(m_mutex); + bPowerUpEventReceived = m_iPowerUpEventReceived > 0 && + GetTimeMs() - m_iPowerUpEventReceived > SOURCE_SWITCH_DELAY_MS; + } + + // if we didn't receive the event, check if the TV is already marked as powered on + if (!bPowerUpEventReceived && m_busDevice->GetCurrentPowerStatus() == CEC_POWER_STATUS_ON) + { + CLockObject lock(m_mutex); + m_iPowerUpEventReceived = GetTimeMs(); + bPowerUpEventReceived = true; + } } + + return bPowerUpEventReceived; } -bool CVLCommandHandler::TransmitPendingActiveSourceCommands(void) +int CVLCommandHandler::HandleStandby(const cec_command &command) { - bool bTransmitCommand(false); + // reset the power up event time { CLockObject lock(m_mutex); - bTransmitCommand = m_bActiveSourcePending; - m_bActiveSourcePending = false; + m_iPowerUpEventReceived = 0; } - if (bTransmitCommand) + return CCECCommandHandler::HandleStandby(command); +} + +int CVLCommandHandler::HandleVendorCommand(const cec_command &command) +{ + // some vendor command voodoo that will enable more buttons on the remote + if (command.parameters.size == 3 && + command.parameters[0] == 0x10 && + command.parameters[1] == 0x01 && + command.parameters[2] == 0x05) { - LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command"); - return CCECCommandHandler::TransmitActiveSource(m_busDevice->GetLogicalAddress(), m_busDevice->GetCurrentPhysicalAddress()) && - TransmitMenuState(m_busDevice->GetLogicalAddress(), CECDEVICE_TV, CEC_MENU_STATE_ACTIVATED); + cec_command response; + cec_command::Format(response, command.destination, command.initiator, CEC_OPCODE_VENDOR_COMMAND); + uint8_t iResponseData[] = {0x10, 0x02, 0xFF, 0xFF, 0x00, 0x05, 0x05, 0x45, 0x55, 0x5c, 0x58, 0x32}; + response.PushArray(12, iResponseData); + + Transmit(response, true); + + return COMMAND_HANDLED; } - return true; + + return CEC_ABORT_REASON_INVALID_OPERAND; } -bool CVLCommandHandler::PowerUpEventReceived(void) +bool CVLCommandHandler::SourceSwitchAllowed(void) +{ + return PowerUpEventReceived(); +} + +int CVLCommandHandler::HandleSystemAudioModeRequest(const cec_command &command) { + if (command.initiator == CECDEVICE_TV) { - CLockObject lock(m_mutex); - if (m_bPowerUpEventReceived) - return true; + // set the power up event time + { + CLockObject lock(m_mutex); + if (m_iPowerUpEventReceived == 0) + m_iPowerUpEventReceived = GetTimeMs(); + } + // mark the TV as powered on + m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_ON); } - cec_power_status powerStatus = m_busDevice->GetCurrentPowerStatus(); - - CLockObject lock(m_mutex); - m_bPowerUpEventReceived = (powerStatus == CEC_POWER_STATUS_ON); - return m_bPowerUpEventReceived; + return CCECCommandHandler::HandleSystemAudioModeRequest(command); } diff --git a/src/lib/implementations/VLCommandHandler.h b/src/lib/implementations/VLCommandHandler.h index f2bfc7e..d1b25d2 100644 --- a/src/lib/implementations/VLCommandHandler.h +++ b/src/lib/implementations/VLCommandHandler.h @@ -38,22 +38,28 @@ namespace CEC class CVLCommandHandler : public CCECCommandHandler { public: - CVLCommandHandler(CCECBusDevice *busDevice); + CVLCommandHandler(CCECBusDevice *busDevice, + int32_t iTransmitTimeout = CEC_DEFAULT_TRANSMIT_TIMEOUT, + int32_t iTransmitWait = CEC_DEFAULT_TRANSMIT_WAIT, + int8_t iTransmitRetries = CEC_DEFAULT_TRANSMIT_RETRIES, + int64_t iActiveSourcePending = 0); virtual ~CVLCommandHandler(void) {}; bool InitHandler(void); - bool HandleDeviceVendorCommandWithId(const cec_command &command); - bool TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress); - bool TransmitPendingActiveSourceCommands(void); + int HandleDeviceVendorCommandWithId(const cec_command &command); + int HandleStandby(const cec_command &command); + int HandleSystemAudioModeRequest(const cec_command &command); + int HandleVendorCommand(const cec_command &command); bool PowerUpEventReceived(void); bool SupportsDeviceType(const cec_device_type type) const { return type != CEC_DEVICE_TYPE_RECORDING_DEVICE; }; cec_device_type GetReplacementDeviceType(const cec_device_type type) const { return type == CEC_DEVICE_TYPE_RECORDING_DEVICE ? CEC_DEVICE_TYPE_PLAYBACK_DEVICE : type; } + bool SourceSwitchAllowed(void); + private: PLATFORM::CMutex m_mutex; - bool m_bActiveSourcePending; - bool m_bPowerUpEventReceived; + uint64_t m_iPowerUpEventReceived; }; }; diff --git a/src/lib/platform/os.h b/src/lib/platform/os.h index 4913d7b..60e02d6 100644 --- a/src/lib/platform/os.h +++ b/src/lib/platform/os.h @@ -31,6 +31,16 @@ * http://www.pulse-eight.net/ */ + +#ifdef UNUSED +#elif defined(__GNUC__) +#define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +#define UNUSED(x) /*@unused@*/ x +#else +#define UNUSED(x) x +#endif + #if (defined(_WIN32) || defined(_WIN64)) #include "windows/os-types.h" #else diff --git a/src/lib/platform/threads/mutex.h b/src/lib/platform/threads/mutex.h index ed60ba8..6fc57d1 100644 --- a/src/lib/platform/threads/mutex.h +++ b/src/lib/platform/threads/mutex.h @@ -51,7 +51,7 @@ namespace PLATFORM private: inline PreventCopy(const PreventCopy &c) { *this = c; } - inline PreventCopy &operator=(const PreventCopy &c){ *this = c; return *this; } + inline PreventCopy &operator=(const PreventCopy & UNUSED(c)){ return *this; } }; template diff --git a/src/lib/platform/windows/os-edid.cpp b/src/lib/platform/windows/os-edid.cpp index f9a1270..2678392 100644 --- a/src/lib/platform/windows/os-edid.cpp +++ b/src/lib/platform/windows/os-edid.cpp @@ -40,6 +40,7 @@ using namespace PLATFORM; static GUID MONITOR_GUID = { 0x4D36E96E, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } }; +#define PA_MAX_REGENTRIES_TO_CHECK 1024 uint16_t GetPhysicalAddressFromDevice(IN HDEVINFO hDevHandle, IN PSP_DEVINFO_DATA deviceInfoData) { @@ -51,16 +52,16 @@ uint16_t GetPhysicalAddressFromDevice(IN HDEVINFO hDevHandle, IN PSP_DEVINFO_DAT CHAR regEntryName[128]; DWORD regEntryNameLength(128); DWORD type; - LONG retVal(0); + LONG retVal(ERROR_SUCCESS); - for (LONG ptr = 0; iPA == 0 && retVal != ERROR_NO_MORE_ITEMS; ptr++) + for (LONG ptr = 0; iPA == 0 && retVal == ERROR_SUCCESS && ptr < PA_MAX_REGENTRIES_TO_CHECK; ptr++) { BYTE regEntryData[1024]; DWORD regEntryDataSize = sizeof(regEntryData); retVal = RegEnumValue(hDevRegKey, ptr, ®EntryName[0], ®EntryNameLength, NULL, &type, regEntryData, ®EntryDataSize); - if (retVal == 0 && !strcmp(regEntryName,"EDID")) + if (retVal == ERROR_SUCCESS && !strcmp(regEntryName,"EDID")) iPA = CEDIDParser::GetPhysicalAddressFromEDID(regEntryData, regEntryDataSize); } RegCloseKey(hDevRegKey); diff --git a/src/testclient/Makefile.am b/src/testclient/Makefile.am index 726cf62..d613d6d 100644 --- a/src/testclient/Makefile.am +++ b/src/testclient/Makefile.am @@ -2,4 +2,4 @@ bin_PROGRAMS = cec-client cec_client_SOURCES = main.cpp cec_client_CPPFLAGS = -I@abs_top_srcdir@/include -cec_client_LDFLAGS = @LIBS_DL@ \ No newline at end of file +cec_client_LDFLAGS = @LIBS@ diff --git a/src/testclient/main.cpp b/src/testclient/main.cpp index 57f6197..fe3f893 100644 --- a/src/testclient/main.cpp +++ b/src/testclient/main.cpp @@ -45,11 +45,14 @@ using namespace CEC; using namespace std; using namespace PLATFORM; +#define CEC_CONFIG_VERSION CEC_CLIENT_VERSION_1_7_1; + #include ICECCallbacks g_callbacks; libcec_configuration g_config; -int g_cecLogLevel(CEC_LOG_ALL); +int g_cecLogLevel(-1); +int g_cecDefaultLogLevel(CEC_LOG_ALL); ofstream g_logOutput; bool g_bShortLog(false); CStdString g_strPort; @@ -1007,6 +1010,8 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) else if (!strcmp(argv[iArgPtr], "--list-devices") || !strcmp(argv[iArgPtr], "-l")) { + if (g_cecLogLevel == -1) + g_cecLogLevel = CEC_LOG_WARNING + CEC_LOG_ERROR; ICECAdapter *parser = LibCecInitialise(&g_config); if (parser) { @@ -1030,6 +1035,9 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) else if (!strcmp(argv[iArgPtr], "--help") || !strcmp(argv[iArgPtr], "-h")) { + if (g_cecLogLevel == -1) + g_cecLogLevel = CEC_LOG_WARNING + CEC_LOG_ERROR; + ShowHelpCommandLine(argv[0]); return 0; } @@ -1098,8 +1106,9 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) int main (int argc, char *argv[]) { g_config.Clear(); + g_callbacks.Clear(); snprintf(g_config.strDeviceName, 13, "CECTester"); - g_config.clientVersion = CEC_CLIENT_VERSION_1_6_3; + g_config.clientVersion = CEC_CONFIG_VERSION; g_config.bActivateSource = 0; g_callbacks.CBCecLogMessage = &CecLogMessage; g_callbacks.CBCecKeyPress = &CecKeyPress; @@ -1110,6 +1119,9 @@ int main (int argc, char *argv[]) if (!ProcessCommandLineArguments(argc, argv)) return 0; + if (g_cecLogLevel == -1) + g_cecLogLevel = g_cecDefaultLogLevel; + if (g_config.deviceTypes.IsEmpty()) { if (!g_bSingleCommand)