From: Lars Op den Kamp Date: Sun, 8 Jan 2012 22:58:41 +0000 (-0800) Subject: Merge pull request #16 from finson/65894f58514a9ab61ae20e45cfde06c9d45600e4 X-Git-Tag: upstream/2.2.0~1^2~41^2~21 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=2ffc8cddcb866da58a4a45923823ad9fb1c296eb;hp=65894f58514a9ab61ae20e45cfde06c9d45600e4;p=deb_libcec.git Merge pull request #16 from finson/65894f58514a9ab61ae20e45cfde06c9d45600e4 Resolve difference between method name in LibCECC.cpp and cecc.h. --- diff --git a/ChangeLog b/ChangeLog index c24e400..7bd4547 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,61 @@ +libcec (1.3-3) unstable; urgency=low + + * changed/added: + * place in libudev include in an extern C block. fixes compilations on + older libudev versions (e.g. on Hardy). closes #2. credits @fbuenemann + * added pkg-config to the dependencies list. issue #15 + * updated README. closes #14 + * added a script that tests some basic functions of the CEC adapter: + /support/cec-test-device.sh + * fixed: + * don't make libCEC the active source when changing the physical address. + don't send active source messages on startup, when not the active source + fixes unwanted device power ups + * replace the command handler directly after receiving a changed vendor + id. change the primary type from recording device to playback device + for panasonic TVs + * don't send a deck status update when sending an active source message + for panasonic TVs + * only switch handlers once when using the generic handler + * don't switch handlers when not needed + * hold a lock in CCECProcessor::SetHDMIPort() + * don't send deck status updates when sending an active source message by + default + + -- Pulse-Eight Packaging Thu, 3 Jan 2012 22:48:00 +0100 + +libcec (1.3-2) unstable; urgency=low + + * changed/added: + * copy libcec.dll to the XBMC installation dir when XBMC is found + * disable background polling. let the client request this info when needed + * update the power status of a device when it's set to + CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON + * wait for the correct response when requesting something, not just any + response + * don't keep trying the same command/request after receiving a feature + abort message + * interface changes: + * change the previously unused boolean parameter in volume control methods + to bSendRelease, and only send a key release when it's true. default to + true + * fixed: + * don't send the power up/down keypress to listeners when in the initial + device state (powered off). fixes unexpected shutdown in XBMC when + connecting to the CEC adapter. + * send a 'menu state activated' command when starting up. bugzid: 113 + * don't wait for a response when not needed + * don't hold a lock while waiting for a response. fixes failed libCEC + inits and slow responses + * don't replace a command handler when it's being used. fixes possible + crash on startup + * don't try to do anything before the processor thread has started + * don't transmit active source messages when the physical address is + still 0xFFFF + * don't init the default handler before the physical address is known + + -- Pulse-Eight Packaging Thu, 29 Dec 2011 03:05:00 +0100 + libcec (1.3-1) unstable; urgency=low * changed/added: diff --git a/README b/README index 5ef90e3..230054a 100644 --- a/README +++ b/README @@ -1,15 +1,53 @@ This library provides support for the Pulse-Eight USB-CEC adapter. -To install libCEC on Linux: +=============================================================================== + === Linux === +=============================================================================== + +libCEC needs the following dependencies in order to work correctly: +* udev v151 or later +* cdc-acm support compiled into the kernel or available as module + +To compile libCEC on Linux, you'll need the following dependencies: +* autoconf 2.13 or later +* automake 1.11 or later +* pkg-config +* udev development headers v151 or later +* gcc 4.2 or later + +To compile, execute the following commands: +# autoreconf -vif +# ./configure --prefix=/usr +# make +# sudo make install + +=============================================================================== + === OS-X === +=============================================================================== + +To compile libCEC on OS-X, you'll need the following dependencies: +* autoconf 2.13 or later +* automake 1.11 or later +* pkg-config +* xcode (TODO: version?) + +To compile, execute the following commands (TODO: please verify): # autoreconf -vif # ./configure --prefix=/usr # make # sudo make install -To install libCEC on Windows: +=============================================================================== + === Windows === +=============================================================================== + +To compile libCEC on Windows, you'll need Visual C++ 2010 or Visual Studio 2010 +The installer needs the Windows DDK (Driver Development Kit) and Nullsoft's +NSIS. + +To compile libCEC, follow these instructions: * open /project/libcec.sln with Visual C++ 2010 or Visual Studio 2010. * build the project. -* copy libcec.dll and pthreadVC2.dll to your desired destination. To build an installer on Windows: * download and install the Windows DDK. @@ -17,17 +55,37 @@ To build an installer on Windows: * go to /project and execute create-installer.cmd to create the installer. * the installer is stored as /project/libCEC-installer.exe -Test the device: -* run "cec-client -h" to display the options of the test client. +=============================================================================== + === Debugging / Testing === +=============================================================================== + +We provide a test client, named cec-client, to debug the device. +To check whether the device can be detected, execute the following command: +* cec-client -l + +"cec-client -h" shows a list of commands and options that are available. -For developers: -* see /include/cec.h for the C++ API and /include/cecc.h for the C version. -* see src/testclient/main.cpp for an example +=============================================================================== + === Developers === +=============================================================================== -For .NET developers: -* build project/libcec.sln first +We provide a C, C++ and .NET CLR interface to the adapter. + +C++ developers: +* the API can be found in /include/cec.h +* an example implementation can be found in /src/testclient/main.cpp + +C developers: +* the API can be found in /include/cecc.h + +.NET developers: * add a reference to LibCecSharp.dll -* see src\CecSharpTester\CecSharpClient.cs for an example +* an example can be found in \src\CecSharpTester\CecSharpClient.cs + +=============================================================================== + === Developers Agreement === +=============================================================================== -If you wish to contribute to this project, you must first sign our contributors agreement -Please see http://www.pulse-eight.net/contributors for more information \ No newline at end of file +If you wish to contribute to this project, you must first sign our contributors +agreement. Please see http://www.pulse-eight.net/contributors for more +information. diff --git a/debian/changelog b/debian/changelog index c24e400..7bd4547 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,61 @@ +libcec (1.3-3) unstable; urgency=low + + * changed/added: + * place in libudev include in an extern C block. fixes compilations on + older libudev versions (e.g. on Hardy). closes #2. credits @fbuenemann + * added pkg-config to the dependencies list. issue #15 + * updated README. closes #14 + * added a script that tests some basic functions of the CEC adapter: + /support/cec-test-device.sh + * fixed: + * don't make libCEC the active source when changing the physical address. + don't send active source messages on startup, when not the active source + fixes unwanted device power ups + * replace the command handler directly after receiving a changed vendor + id. change the primary type from recording device to playback device + for panasonic TVs + * don't send a deck status update when sending an active source message + for panasonic TVs + * only switch handlers once when using the generic handler + * don't switch handlers when not needed + * hold a lock in CCECProcessor::SetHDMIPort() + * don't send deck status updates when sending an active source message by + default + + -- Pulse-Eight Packaging Thu, 3 Jan 2012 22:48:00 +0100 + +libcec (1.3-2) unstable; urgency=low + + * changed/added: + * copy libcec.dll to the XBMC installation dir when XBMC is found + * disable background polling. let the client request this info when needed + * update the power status of a device when it's set to + CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON + * wait for the correct response when requesting something, not just any + response + * don't keep trying the same command/request after receiving a feature + abort message + * interface changes: + * change the previously unused boolean parameter in volume control methods + to bSendRelease, and only send a key release when it's true. default to + true + * fixed: + * don't send the power up/down keypress to listeners when in the initial + device state (powered off). fixes unexpected shutdown in XBMC when + connecting to the CEC adapter. + * send a 'menu state activated' command when starting up. bugzid: 113 + * don't wait for a response when not needed + * don't hold a lock while waiting for a response. fixes failed libCEC + inits and slow responses + * don't replace a command handler when it's being used. fixes possible + crash on startup + * don't try to do anything before the processor thread has started + * don't transmit active source messages when the physical address is + still 0xFFFF + * don't init the default handler before the physical address is known + + -- Pulse-Eight Packaging Thu, 29 Dec 2011 03:05:00 +0100 + libcec (1.3-1) unstable; urgency=low * changed/added: diff --git a/project/libcec.rc b/project/libcec.rc index 1fe06c5..e18b9a3 100644 Binary files a/project/libcec.rc and b/project/libcec.rc differ diff --git a/project/testclient.rc b/project/testclient.rc index c63d3c9..339801b 100644 Binary files a/project/testclient.rc and b/project/testclient.rc differ diff --git a/src/CecSharpTester/AssemblyInfo.cs b/src/CecSharpTester/AssemblyInfo.cs index 058bbb9..73ffc39 100644 --- a/src/CecSharpTester/AssemblyInfo.cs +++ b/src/CecSharpTester/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Pulse-Eight Ltd.")] [assembly: AssemblyProduct("CecSharpClient")] -[assembly: AssemblyCopyright("Copyright © Pulse-Eight Ltd. 2011")] +[assembly: AssemblyCopyright("Copyright (c) Pulse-Eight Ltd. 2012)] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -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.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.3.2.0")] +[assembly: AssemblyFileVersion("1.3.2.0")] diff --git a/src/LibCecSharp/AssemblyInfo.cpp b/src/LibCecSharp/AssemblyInfo.cpp index 459ae9f..0624b82 100644 --- a/src/LibCecSharp/AssemblyInfo.cpp +++ b/src/LibCecSharp/AssemblyInfo.cpp @@ -16,7 +16,7 @@ using namespace System::Security::Permissions; [assembly:AssemblyConfigurationAttribute("")]; [assembly:AssemblyCompanyAttribute("Pulse-Eight Ltd.")]; [assembly:AssemblyProductAttribute("LibCecSharp")]; -[assembly:AssemblyCopyrightAttribute("Copyright (c) Pulse-Eight Ltd. 2011")]; +[assembly:AssemblyCopyrightAttribute("Copyright (c) Pulse-Eight Ltd. 2012")]; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")]; @@ -31,7 +31,7 @@ using namespace System::Security::Permissions; // You can specify all the value or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly:AssemblyVersionAttribute("1.0.*")]; +[assembly:AssemblyVersionAttribute("1.3.2.0")]; [assembly:ComVisible(false)]; diff --git a/src/lib/AdapterDetection.cpp b/src/lib/AdapterDetection.cpp index b304e43..de1da7c 100644 --- a/src/lib/AdapterDetection.cpp +++ b/src/lib/AdapterDetection.cpp @@ -50,8 +50,10 @@ static GUID USB_RAW_GUID = { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }; #elif defined(HAVE_LIBUDEV) #include -#include #include +extern "C" { +#include +} #endif #define CEC_VID 0x2548 diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp index 27bca69..5071ec6 100644 --- a/src/lib/CECProcessor.cpp +++ b/src/lib/CECProcessor.cpp @@ -49,6 +49,7 @@ using namespace std; CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS*/) : m_bStarted(false), + m_bInitialised(false), m_iHDMIPort(CEC_DEFAULT_HDMI_PORT), m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE), m_lastInitiator(CECDEVICE_UNKNOWN), @@ -69,6 +70,7 @@ CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, cec CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, const cec_device_type_list &types) : m_bStarted(false), + m_bInitialised(false), m_iHDMIPort(CEC_DEFAULT_HDMI_PORT), m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE), m_strDeviceName(strDeviceName), @@ -168,20 +170,15 @@ bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = 38400 */, m_busDevices[m_logicalAddresses.primary]->m_strDeviceName = m_strDeviceName; /* get the vendor id from the TV, so we are using the correct handler */ - m_busDevices[CECDEVICE_TV]->GetVendorId(); + m_busDevices[CECDEVICE_TV]->RequestVendorId(); + ReplaceHandlers(); bReturn = SetHDMIPort(m_iBaseDevice, m_iHDMIPort, true); } - /* make the primary device the active source */ - if (bReturn) - { - m_busDevices[m_logicalAddresses.primary]->m_bActiveSource = true; - bReturn = m_busDevices[CECDEVICE_TV]->GetHandler()->InitHandler(); - } - if (bReturn) { + m_bInitialised = true; m_controller->AddLog(CEC_LOG_DEBUG, "processor thread started"); } else @@ -235,6 +232,85 @@ bool CCECProcessor::FindLogicalAddressAudioSystem(void) return TryLogicalAddress(CECDEVICE_AUDIOSYSTEM); } +bool CCECProcessor::ChangeDeviceType(cec_device_type from, cec_device_type to) +{ + bool bChanged(false); + + CStdString strLog; + strLog.Format("changing device type '%s' into '%s'", ToString(from), ToString(to)); + AddLog(CEC_LOG_NOTICE, strLog); + + CLockObject lock(&m_mutex); + CCECBusDevice *previousDevice = GetDeviceByType(from); + m_logicalAddresses.primary = CECDEVICE_UNKNOWN; + + for (unsigned int iPtr = 0; iPtr < 5; iPtr++) + { + if (m_types.types[iPtr] == CEC_DEVICE_TYPE_RESERVED) + continue; + + if (m_types.types[iPtr] == from) + { + bChanged = true; + m_types.types[iPtr] = to; + } + else if (m_types.types[iPtr] == to && bChanged) + { + m_types.types[iPtr] = CEC_DEVICE_TYPE_RESERVED; + } + } + + if (bChanged) + { + FindLogicalAddresses(); + + CCECBusDevice *newDevice = GetDeviceByType(to); + if (previousDevice && newDevice) + { + newDevice->SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC); + previousDevice->SetDeviceStatus(CEC_DEVICE_STATUS_UNKNOWN); + + newDevice->SetCecVersion(previousDevice->GetCecVersion(false)); + previousDevice->SetCecVersion(CEC_VERSION_UNKNOWN); + + newDevice->SetMenuLanguage(previousDevice->GetMenuLanguage(false)); + cec_menu_language lang; + lang.device = previousDevice->GetLogicalAddress(); + for (unsigned int iPtr = 0; iPtr < 4; iPtr++) + lang.language[iPtr] = '?'; + lang.language[3] = 0; + previousDevice->SetMenuLanguage(lang); + + newDevice->SetMenuState(previousDevice->GetMenuState()); + previousDevice->SetMenuState(CEC_MENU_STATE_DEACTIVATED); + + newDevice->SetOSDName(previousDevice->GetOSDName(false)); + previousDevice->SetOSDName(ToString(previousDevice->GetLogicalAddress())); + + newDevice->SetPhysicalAddress(previousDevice->GetPhysicalAddress(false)); + previousDevice->SetPhysicalAddress(0xFFFF); + + newDevice->SetPowerStatus(previousDevice->GetPowerStatus(false)); + previousDevice->SetPowerStatus(CEC_POWER_STATUS_UNKNOWN); + + newDevice->SetVendorId(previousDevice->GetVendorId(false)); + previousDevice->SetVendorId(CEC_VENDOR_UNKNOWN); + + if ((from == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || from == CEC_DEVICE_TYPE_RECORDING_DEVICE) && + (to == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || to == CEC_DEVICE_TYPE_RECORDING_DEVICE)) + { + ((CCECPlaybackDevice *) newDevice)->SetDeckControlMode(((CCECPlaybackDevice *) previousDevice)->GetDeckControlMode()); + ((CCECPlaybackDevice *) previousDevice)->SetDeckControlMode(CEC_DECK_CONTROL_MODE_STOP); + + ((CCECPlaybackDevice *) newDevice)->SetDeckStatus(((CCECPlaybackDevice *) previousDevice)->GetDeckStatus()); + ((CCECPlaybackDevice *) previousDevice)->SetDeckStatus(CEC_DECK_INFO_STOP); + } + } + } + + return true; +} + bool CCECProcessor::FindLogicalAddresses(void) { bool bReturn(true); @@ -265,6 +341,12 @@ bool CCECProcessor::FindLogicalAddresses(void) return bReturn; } +void CCECProcessor::ReplaceHandlers(void) +{ + for (uint8_t iPtr = 0; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++) + m_busDevices[iPtr]->ReplaceHandler(m_bInitialised); +} + void *CCECProcessor::Process(void) { bool bParseFrame(false); @@ -280,6 +362,7 @@ void *CCECProcessor::Process(void) while (!IsStopped()) { + ReplaceHandlers(); command.Clear(); msg.clear(); @@ -338,7 +421,8 @@ bool CCECProcessor::SetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RE bReturn = m_busDevices[addr]->TransmitActiveSource(); if (bReturn && (m_busDevices[addr]->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || - m_busDevices[addr]->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)) + m_busDevices[addr]->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) && + m_busDevices[addr]->GetHandler()->SendDeckStatusUpdateOnActiveSource()) { bReturn = ((CCECPlaybackDevice *)m_busDevices[addr])->TransmitDeckStatus(CECDEVICE_TV); } @@ -413,6 +497,7 @@ bool CCECProcessor::SetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true * bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */) { bool bReturn(false); + CLockObject lock(&m_mutex); m_iBaseDevice = iBaseDevice; m_iHDMIPort = iPort; @@ -425,7 +510,11 @@ bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, uint16_t iPhysicalAddress(0); if (iBaseDevice > CECDEVICE_TV) + { + lock.Leave(); iPhysicalAddress = m_busDevices[iBaseDevice]->GetPhysicalAddress(); + lock.Lock(); + } if (iPhysicalAddress < 0xffff) { @@ -482,6 +571,7 @@ void CCECProcessor::LogOutput(const cec_command &data) bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress) { + CLockObject lock(&m_mutex); if (m_logicalAddresses.primary != iLogicalAddress) { CStdString strLog; @@ -509,18 +599,23 @@ bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = tru return true; } -bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress) +bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpdate /* = true */) { + bool bWasActiveSource(false); + CLockObject lock(&m_mutex); if (!m_logicalAddresses.IsEmpty()) { for (uint8_t iPtr = 0; iPtr < 15; iPtr++) if (m_logicalAddresses[iPtr]) { + bWasActiveSource |= m_busDevices[iPtr]->IsActiveSource(); m_busDevices[iPtr]->SetInactiveSource(); m_busDevices[iPtr]->SetPhysicalAddress(iPhysicalAddress); - m_busDevices[iPtr]->TransmitPhysicalAddress(); + if (bSendUpdate) + m_busDevices[iPtr]->TransmitPhysicalAddress(); } - return SetActiveView(); + + return bWasActiveSource && bSendUpdate ? SetActiveView() : true; } return false; } @@ -602,9 +697,9 @@ CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const { CCECBusDevice *device = NULL; - for (unsigned int iPtr = 0; iPtr < 16; iPtr++) + for (uint8_t iPtr = 0; iPtr < 16; iPtr++) { - if (m_busDevices[iPtr]->m_type == type) + if (m_busDevices[iPtr]->m_type == type && m_logicalAddresses[iPtr]) { device = m_busDevices[iPtr]; break; @@ -989,6 +1084,27 @@ bool CCECProcessor::TransmitKeyRelease(cec_logical_address iDestination, bool bW return m_busDevices[iDestination]->TransmitKeyRelease(bWait); } +const char *CCECProcessor::ToString(const cec_device_type type) +{ + switch (type) + { + case CEC_DEVICE_TYPE_AUDIO_SYSTEM: + return "audio system"; + case CEC_DEVICE_TYPE_PLAYBACK_DEVICE: + return "playback device"; + case CEC_DEVICE_TYPE_RECORDING_DEVICE: + return "recording device"; + case CEC_DEVICE_TYPE_RESERVED: + return "reserved"; + case CEC_DEVICE_TYPE_TUNER: + return "tuner"; + case CEC_DEVICE_TYPE_TV: + return "TV"; + default: + return "unknown"; + } +} + const char *CCECProcessor::ToString(const cec_menu_state state) { switch (state) diff --git a/src/lib/CECProcessor.h b/src/lib/CECProcessor.h index e2705fd..8c007b8 100644 --- a/src/lib/CECProcessor.h +++ b/src/lib/CECProcessor.h @@ -78,6 +78,7 @@ namespace CEC virtual bool IsStarted(void) const { return m_bStarted; } virtual cec_logical_address GetActiveSource(void); virtual bool IsActiveSource(cec_logical_address iAddress); + virtual bool IsInitialised(void) const { return m_bInitialised; } virtual bool SetActiveView(void); virtual bool SetActiveSource(cec_device_type type = CEC_DEVICE_TYPE_RESERVED); @@ -87,7 +88,7 @@ namespace CEC virtual bool TransmitInactiveSource(void); virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress); virtual bool SetMenuState(cec_menu_state state, bool bSendUpdate = true); - virtual bool SetPhysicalAddress(uint16_t iPhysicalAddress); + virtual bool SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpdate = true); virtual bool SetActiveSource(uint16_t iStreamPath); virtual bool SwitchMonitoring(bool bEnable); virtual bool PollDevice(cec_logical_address iAddress); @@ -102,6 +103,7 @@ namespace CEC bool SetLineTimeout(uint8_t iTimeout); + const char *ToString(const cec_device_type type); const char *ToString(const cec_menu_state state); const char *ToString(const cec_version version); const char *ToString(const cec_power_status status); @@ -123,6 +125,7 @@ namespace CEC virtual void AddKey(void); virtual void AddLog(cec_log_level level, const CStdString &strMessage); + virtual bool ChangeDeviceType(cec_device_type from, cec_device_type to); virtual bool FindLogicalAddresses(void); virtual bool SetAckMask(uint16_t iMask); @@ -133,6 +136,7 @@ namespace CEC CMutex m_transmitMutex; private: + void ReplaceHandlers(void); void ScanCECBus(void); bool PhysicalAddressInUse(uint16_t iPhysicalAddress); bool TryLogicalAddress(cec_logical_address address); @@ -147,6 +151,7 @@ namespace CEC void ParseCommand(cec_command &command); bool m_bStarted; + bool m_bInitialised; uint8_t m_iHDMIPort; cec_logical_address m_iBaseDevice; cec_command m_currentframe; diff --git a/src/lib/devices/CECBusDevice.cpp b/src/lib/devices/CECBusDevice.cpp index 6279de9..9e9145c 100644 --- a/src/lib/devices/CECBusDevice.cpp +++ b/src/lib/devices/CECBusDevice.cpp @@ -50,11 +50,13 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi m_powerStatus(CEC_POWER_STATUS_UNKNOWN), m_processor(processor), m_vendor(CEC_VENDOR_UNKNOWN), + m_bReplaceHandler(false), m_menuState(CEC_MENU_STATE_ACTIVATED), m_bActiveSource(false), m_iLastActive(0), m_cecVersion(CEC_VERSION_UNKNOWN), - m_deviceStatus(CEC_DEVICE_STATUS_UNKNOWN) + m_deviceStatus(CEC_DEVICE_STATUS_UNKNOWN), + m_handlerMutex(false) { m_handler = new CCECCommandHandler(this); @@ -117,7 +119,7 @@ bool CCECBusDevice::PowerOn(void) strLog.Format("<< powering on '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress); AddLog(CEC_LOG_DEBUG, strLog.c_str()); - if (m_handler->TransmitPowerOn(GetMyLogicalAddress(), m_iLogicalAddress)) + if (m_handler->TransmitImageViewOn(GetMyLogicalAddress(), m_iLogicalAddress)) { { CLockObject lock(&m_mutex); @@ -154,24 +156,33 @@ bool CCECBusDevice::Standby(void) //@{ cec_version CCECBusDevice::GetCecVersion(bool bUpdate /* = false */) { - CLockObject lock(&m_mutex); - if (GetStatus() == CEC_DEVICE_STATUS_PRESENT && - (bUpdate || m_cecVersion == CEC_VERSION_UNKNOWN)) + bool bRequestUpdate(false); + { + CLockObject lock(&m_mutex); + bRequestUpdate = (GetStatus() == CEC_DEVICE_STATUS_PRESENT && + (bUpdate || m_cecVersion == CEC_VERSION_UNKNOWN)); + } + + if (bRequestUpdate) RequestCecVersion(); + CLockObject lock(&m_mutex); return m_cecVersion; } bool CCECBusDevice::RequestCecVersion(void) { bool bReturn(false); + if (!MyLogicalAddressContains(m_iLogicalAddress)) { + m_handler->MarkBusy(); CStdString strLog; strLog.Format("<< requesting CEC version of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress); AddLog(CEC_LOG_NOTICE, strLog); bReturn = m_handler->TransmitRequestCecVersion(GetMyLogicalAddress(), m_iLogicalAddress); + m_handler->MarkReady(); } return bReturn; } @@ -183,28 +194,43 @@ const char* CCECBusDevice::GetLogicalAddressName(void) const cec_menu_language &CCECBusDevice::GetMenuLanguage(bool bUpdate /* = false */) { - CLockObject lock(&m_mutex); - if (GetStatus() == CEC_DEVICE_STATUS_PRESENT && - (bUpdate || !strcmp(m_menuLanguage.language, "???"))) + bool bRequestUpdate(false); + { + CLockObject lock(&m_mutex); + bRequestUpdate = (GetStatus() == CEC_DEVICE_STATUS_PRESENT && + (bUpdate || !strcmp(m_menuLanguage.language, "???"))); + } + + if (bRequestUpdate) RequestMenuLanguage(); + CLockObject lock(&m_mutex); return m_menuLanguage; } bool CCECBusDevice::RequestMenuLanguage(void) { bool bReturn(false); + if (!MyLogicalAddressContains(m_iLogicalAddress) && !IsUnsupportedFeature(CEC_OPCODE_GET_MENU_LANGUAGE)) { + m_handler->MarkBusy(); CStdString strLog; strLog.Format("<< requesting menu language of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress); AddLog(CEC_LOG_NOTICE, strLog); bReturn = m_handler->TransmitRequestMenuLanguage(GetMyLogicalAddress(), m_iLogicalAddress); + m_handler->MarkReady(); } return bReturn; } +cec_menu_state CCECBusDevice::GetMenuState(void) +{ + CLockObject lock(&m_mutex); + return m_menuState; +} + cec_logical_address CCECBusDevice::GetMyLogicalAddress(void) const { return m_processor->GetLogicalAddress(); @@ -217,98 +243,134 @@ uint16_t CCECBusDevice::GetMyPhysicalAddress(void) const CStdString CCECBusDevice::GetOSDName(bool bUpdate /* = false */) { - CLockObject lock(&m_mutex); - if (GetStatus() == CEC_DEVICE_STATUS_PRESENT && - (bUpdate || m_strDeviceName.Equals(ToString(m_iLogicalAddress))) && - m_type != CEC_DEVICE_TYPE_TV) + bool bRequestUpdate(false); + { + CLockObject lock(&m_mutex); + bRequestUpdate = (GetStatus() == CEC_DEVICE_STATUS_PRESENT && + (bUpdate || m_strDeviceName.Equals(ToString(m_iLogicalAddress))) && + m_type != CEC_DEVICE_TYPE_TV); + } + + if (bRequestUpdate) RequestOSDName(); + CLockObject lock(&m_mutex); return m_strDeviceName; } bool CCECBusDevice::RequestOSDName(void) { bool bReturn(false); + if (!MyLogicalAddressContains(m_iLogicalAddress) && !IsUnsupportedFeature(CEC_OPCODE_GIVE_OSD_NAME)) { + m_handler->MarkBusy(); CStdString strLog; strLog.Format("<< requesting OSD name of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress); AddLog(CEC_LOG_NOTICE, strLog); bReturn = m_handler->TransmitRequestOSDName(GetMyLogicalAddress(), m_iLogicalAddress); + m_handler->MarkReady(); } return bReturn; } uint16_t CCECBusDevice::GetPhysicalAddress(bool bUpdate /* = false */) { - CLockObject lock(&m_mutex); - if (GetStatus() == CEC_DEVICE_STATUS_PRESENT && - (m_iPhysicalAddress == 0xFFFF || bUpdate)) + bool bRequestUpdate(false); { - if (!RequestPhysicalAddress()) - AddLog(CEC_LOG_ERROR, "failed to request the physical address"); + CLockObject lock(&m_mutex); + bRequestUpdate = (GetStatus() == CEC_DEVICE_STATUS_PRESENT && + (m_iPhysicalAddress == 0xFFFF || bUpdate)); } + if (bRequestUpdate && !RequestPhysicalAddress()) + AddLog(CEC_LOG_ERROR, "failed to request the physical address (1)"); + + CLockObject lock(&m_mutex); return m_iPhysicalAddress; } bool CCECBusDevice::RequestPhysicalAddress(void) { bool bReturn(false); + if (!MyLogicalAddressContains(m_iLogicalAddress)) { + m_handler->MarkBusy(); CStdString strLog; strLog.Format("<< requesting physical address of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress); AddLog(CEC_LOG_NOTICE, strLog); bReturn = m_handler->TransmitRequestPhysicalAddress(GetMyLogicalAddress(), m_iLogicalAddress); + m_handler->MarkReady(); } return bReturn; } cec_power_status CCECBusDevice::GetPowerStatus(bool bUpdate /* = false */) { - CLockObject lock(&m_mutex); - if (GetStatus() == CEC_DEVICE_STATUS_PRESENT && - (bUpdate || m_powerStatus == CEC_POWER_STATUS_UNKNOWN)) + bool bRequestUpdate(false); + { + CLockObject lock(&m_mutex); + bRequestUpdate = (GetStatus() == CEC_DEVICE_STATUS_PRESENT && + (bUpdate || m_powerStatus == CEC_POWER_STATUS_UNKNOWN || + m_powerStatus == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON)); + } + + if (bRequestUpdate) RequestPowerStatus(); + CLockObject lock(&m_mutex); return m_powerStatus; } bool CCECBusDevice::RequestPowerStatus(void) { bool bReturn(false); + if (!MyLogicalAddressContains(m_iLogicalAddress) && !IsUnsupportedFeature(CEC_OPCODE_GIVE_DEVICE_POWER_STATUS)) { + m_handler->MarkBusy(); CStdString strLog; strLog.Format("<< requesting power status of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress); AddLog(CEC_LOG_NOTICE, strLog); bReturn = m_handler->TransmitRequestPowerStatus(GetMyLogicalAddress(), m_iLogicalAddress); + m_handler->MarkReady(); } return bReturn; } cec_vendor_id CCECBusDevice::GetVendorId(bool bUpdate /* = false */) { - CLockObject lock(&m_mutex); - if (GetStatus() == CEC_DEVICE_STATUS_PRESENT && - (bUpdate || m_vendor == CEC_VENDOR_UNKNOWN)) + bool bRequestUpdate(false); + { + CLockObject lock(&m_mutex); + bRequestUpdate = (GetStatus() == CEC_DEVICE_STATUS_PRESENT && + (bUpdate || m_vendor == CEC_VENDOR_UNKNOWN)); + } + + if (bRequestUpdate) RequestVendorId(); + CLockObject lock(&m_mutex); return m_vendor; } bool CCECBusDevice::RequestVendorId(void) { bool bReturn(false); + if (!MyLogicalAddressContains(m_iLogicalAddress)) { + m_handler->MarkBusy(); CStdString strLog; strLog.Format("<< requesting vendor ID of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress); AddLog(CEC_LOG_NOTICE, strLog); bReturn = m_handler->TransmitRequestVendorId(GetMyLogicalAddress(), m_iLogicalAddress); + m_handler->MarkReady(); + + ReplaceHandler(true); } return bReturn; } @@ -451,6 +513,12 @@ void CCECBusDevice::SetInactiveSource(void) void CCECBusDevice::SetActiveSource(void) { CLockObject lock(&m_mutex); + if (!m_bActiveSource) + { + CStdString strLog; + strLog.Format("making %s (%x) the active source", GetLogicalAddressName(), m_iLogicalAddress); + AddLog(CEC_LOG_DEBUG, strLog); + } for (int iPtr = 0; iPtr < 16; iPtr++) if (iPtr != m_iLogicalAddress) @@ -558,41 +626,63 @@ void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus) } } -bool CCECBusDevice::SetVendorId(uint64_t iVendorId, bool bInitHandler /* = true */) +bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */) { - bool bVendorChanged(false); + CLockObject lock(&m_mutex); + CLockObject handlerLock(&m_handlerMutex); + if (m_vendor != m_handler->GetVendorId()) { - CLockObject lock(&m_mutex); - bVendorChanged = (m_vendor != (cec_vendor_id)iVendorId); - m_vendor = (cec_vendor_id)iVendorId; + if (CCECCommandHandler::HasSpecificHandler(m_vendor)) + { + CStdString strLog; + if (m_handler->InUse()) + { + strLog.Format("handler for device '%s' (%x) is being used. not replacing the command handler", GetLogicalAddressName(), GetLogicalAddress()); + m_processor->AddLog(CEC_LOG_DEBUG, strLog); + return false; + } - if (bVendorChanged) + strLog.Format("replacing the command handler for device '%s' (%x)", GetLogicalAddressName(), GetLogicalAddress()); + m_processor->AddLog(CEC_LOG_DEBUG, strLog); delete m_handler; - switch (iVendorId) - { - case CEC_VENDOR_SAMSUNG: - if (bVendorChanged) + switch (m_vendor) + { + case CEC_VENDOR_SAMSUNG: m_handler = new CANCommandHandler(this); - break; - case CEC_VENDOR_LG: - if (bVendorChanged) + break; + case CEC_VENDOR_LG: m_handler = new CSLCommandHandler(this); - break; - case CEC_VENDOR_PANASONIC: - if (bVendorChanged) + break; + case CEC_VENDOR_PANASONIC: m_handler = new CVLCommandHandler(this); - break; - default: - if (bVendorChanged) + break; + default: m_handler = new CCECCommandHandler(this); - break; + break; + } + + m_handler->SetVendorId(m_vendor); + m_handler->InitHandler(); + + if (bActivateSource && m_processor->GetLogicalAddresses().IsSet(m_iLogicalAddress) && m_processor->IsInitialised() && IsActiveSource()) + m_handler->ActivateSource(); } } - if (bVendorChanged && bInitHandler && m_handler->GetVendorId() != CEC_VENDOR_UNKNOWN) - m_handler->InitHandler(); + return true; +} + +bool CCECBusDevice::SetVendorId(uint64_t iVendorId) +{ + bool bVendorChanged(false); + + { + CLockObject lock(&m_mutex); + bVendorChanged = (m_vendor != (cec_vendor_id)iVendorId); + m_vendor = (cec_vendor_id)iVendorId; + } CStdString strLog; strLog.Format("%s (%X): vendor = %s (%06x)", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_vendor), m_vendor); @@ -631,7 +721,14 @@ bool CCECBusDevice::TransmitActiveSource(void) } } - return bSendActiveSource ? m_handler->TransmitActiveSource(m_iLogicalAddress, m_iPhysicalAddress) : false; + if (bSendActiveSource) + { + m_handler->TransmitImageViewOn(m_iLogicalAddress, CECDEVICE_TV); + m_handler->TransmitActiveSource(m_iLogicalAddress, m_iPhysicalAddress); + return true; + } + + return false; } bool CCECBusDevice::TransmitCECVersion(cec_logical_address dest) @@ -814,4 +911,11 @@ void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode) { m_unsupportedFeatures.insert(opcode); } + +bool CCECBusDevice::ActivateSource(void) +{ + CLockObject lock(&m_mutex); + return m_handler->ActivateSource(); +} + //@} diff --git a/src/lib/devices/CECBusDevice.h b/src/lib/devices/CECBusDevice.h index 0ca662a..6157e0a 100644 --- a/src/lib/devices/CECBusDevice.h +++ b/src/lib/devices/CECBusDevice.h @@ -62,6 +62,7 @@ namespace CEC virtual cec_logical_address GetLogicalAddress(void) const { return m_iLogicalAddress; } virtual const char* GetLogicalAddressName(void) const; virtual cec_menu_language & GetMenuLanguage(bool bUpdate = false); + virtual cec_menu_state GetMenuState(void); virtual cec_logical_address GetMyLogicalAddress(void) const; virtual uint16_t GetMyPhysicalAddress(void) const; virtual CStdString GetOSDName(bool bUpdate = false); @@ -80,6 +81,7 @@ namespace CEC virtual void SetInactiveSource(void); virtual void SetActiveSource(void); virtual bool TryLogicalAddress(void); + virtual bool ActivateSource(void); virtual void SetDeviceStatus(const cec_bus_device_status newStatus); virtual void SetPhysicalAddress(uint16_t iNewAddress); @@ -88,7 +90,7 @@ namespace CEC virtual void SetMenuLanguage(const cec_menu_language &menuLanguage); virtual void SetOSDName(CStdString strName); virtual void SetMenuState(const cec_menu_state state); - virtual bool SetVendorId(uint64_t iVendorId, bool bInitHandler = true); + virtual bool SetVendorId(uint64_t iVendorId); virtual void SetPowerStatus(const cec_power_status powerStatus); virtual bool TransmitActiveSource(void); @@ -105,6 +107,8 @@ namespace CEC virtual bool TransmitKeyRelease(bool bWait = true); protected: + bool ReplaceHandler(bool bInitHandler = true); + bool RequestCecVersion(void); bool RequestMenuLanguage(void); bool RequestPowerStatus(void); @@ -124,6 +128,7 @@ namespace CEC CCECProcessor * m_processor; CCECCommandHandler * m_handler; cec_vendor_id m_vendor; + bool m_bReplaceHandler; cec_menu_state m_menuState; bool m_bActiveSource; uint64_t m_iLastActive; @@ -131,5 +136,6 @@ namespace CEC cec_bus_device_status m_deviceStatus; std::set m_unsupportedFeatures; CMutex m_mutex; + CMutex m_handlerMutex; }; }; diff --git a/src/lib/implementations/ANCommandHandler.cpp b/src/lib/implementations/ANCommandHandler.cpp index e9674ba..248a959 100644 --- a/src/lib/implementations/ANCommandHandler.cpp +++ b/src/lib/implementations/ANCommandHandler.cpp @@ -40,6 +40,8 @@ using namespace CEC; CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice) : CCECCommandHandler(busDevice) { + m_vendorId = CEC_VENDOR_SAMSUNG; + m_bOPTSendDeckStatusUpdateOnActiveSource = false; } bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command) diff --git a/src/lib/implementations/ANCommandHandler.h b/src/lib/implementations/ANCommandHandler.h index 0207a63..8966c1a 100644 --- a/src/lib/implementations/ANCommandHandler.h +++ b/src/lib/implementations/ANCommandHandler.h @@ -42,9 +42,6 @@ namespace CEC virtual ~CANCommandHandler(void) {}; virtual bool HandleCommand(const cec_command &command); - - virtual cec_vendor_id GetVendorId(void) { return CEC_VENDOR_SAMSUNG; }; - protected: virtual bool HandleVendorRemoteButtonDown(const cec_command &command); }; diff --git a/src/lib/implementations/CECCommandHandler.cpp b/src/lib/implementations/CECCommandHandler.cpp index 23fd550..40b4f07 100644 --- a/src/lib/implementations/CECCommandHandler.cpp +++ b/src/lib/implementations/CECCommandHandler.cpp @@ -45,19 +45,26 @@ CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice) : m_iTransmitTimeout(CEC_DEFAULT_TRANSMIT_TIMEOUT), m_iTransmitWait(CEC_DEFAULT_TRANSMIT_WAIT), m_iTransmitRetries(CEC_DEFAULT_TRANSMIT_RETRIES), - m_bHandlerInited(false) + m_bHandlerInited(false), + m_iUseCounter(0), + m_expectedResponse(CEC_OPCODE_NONE), + m_bOPTSendDeckStatusUpdateOnActiveSource(false), + m_vendorId(CEC_VENDOR_UNKNOWN) { } CCECCommandHandler::~CCECCommandHandler(void) { + CLockObject lock(&m_processor->m_transmitMutex); + CLockObject receiveLock(&m_receiveMutex); m_condition.Broadcast(); } bool CCECCommandHandler::HandleCommand(const cec_command &command) { - bool bHandled(true), bHandlerChanged(false); + bool bHandled(true); + MarkBusy(); CStdString strLog; strLog.Format(">> %s (%X) -> %s (%X): %s (%2X)", m_processor->ToString(command.initiator), command.initiator, m_processor->ToString(command.destination), command.destination, m_processor->ToString(command.opcode), command.opcode); m_busDevice->AddLog(CEC_LOG_NOTICE, strLog); @@ -76,49 +83,61 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command) HandleSetMenuLanguage(command); break; case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: - HandleGivePhysicalAddress(command); + if (m_processor->IsInitialised()) + HandleGivePhysicalAddress(command); break; case CEC_OPCODE_GIVE_OSD_NAME: - HandleGiveOSDName(command); + if (m_processor->IsInitialised()) + HandleGiveOSDName(command); break; case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID: - HandleGiveDeviceVendorId(command); + if (m_processor->IsInitialised()) + HandleGiveDeviceVendorId(command); break; case CEC_OPCODE_DEVICE_VENDOR_ID: - bHandlerChanged = HandleDeviceVendorId(command); + HandleDeviceVendorId(command); break; case CEC_OPCODE_VENDOR_COMMAND_WITH_ID: HandleDeviceVendorCommandWithId(command); break; case CEC_OPCODE_GIVE_DECK_STATUS: - HandleGiveDeckStatus(command); + if (m_processor->IsInitialised()) + HandleGiveDeckStatus(command); break; case CEC_OPCODE_DECK_CONTROL: HandleDeckControl(command); break; case CEC_OPCODE_MENU_REQUEST: - HandleMenuRequest(command); + if (m_processor->IsInitialised()) + HandleMenuRequest(command); break; case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS: - HandleGiveDevicePowerStatus(command); + if (m_processor->IsInitialised()) + HandleGiveDevicePowerStatus(command); break; case CEC_OPCODE_GET_CEC_VERSION: - HandleGetCecVersion(command); + if (m_processor->IsInitialised()) + HandleGetCecVersion(command); break; case CEC_OPCODE_USER_CONTROL_PRESSED: - HandleUserControlPressed(command); + if (m_processor->IsInitialised()) + HandleUserControlPressed(command); break; case CEC_OPCODE_USER_CONTROL_RELEASE: - HandleUserControlRelease(command); + if (m_processor->IsInitialised()) + HandleUserControlRelease(command); break; case CEC_OPCODE_GIVE_AUDIO_STATUS: - HandleGiveAudioStatus(command); + if (m_processor->IsInitialised()) + HandleGiveAudioStatus(command); break; case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS: - HandleGiveSystemAudioModeStatus(command); + if (m_processor->IsInitialised()) + HandleGiveSystemAudioModeStatus(command); break; case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST: - HandleSystemAudioModeRequest(command); + if (m_processor->IsInitialised()) + HandleSystemAudioModeRequest(command); break; case CEC_OPCODE_REPORT_AUDIO_STATUS: HandleReportAudioStatus(command); @@ -130,7 +149,8 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command) HandleSetSystemAudioMode(command); break; case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: - HandleRequestActiveSource(command); + if (m_processor->IsInitialised()) + HandleRequestActiveSource(command); break; case CEC_OPCODE_SET_STREAM_PATH: HandleSetStreamPath(command); @@ -142,7 +162,8 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command) HandleRoutingInformation(command); break; case CEC_OPCODE_STANDBY: - HandleStandby(command); + if (m_processor->IsInitialised()) + HandleStandby(command); break; case CEC_OPCODE_ACTIVE_SOURCE: HandleActiveSource(command); @@ -162,18 +183,24 @@ bool CCECCommandHandler::HandleCommand(const cec_command &command) case CEC_OPCODE_FEATURE_ABORT: HandleFeatureAbort(command); break; + case CEC_OPCODE_VENDOR_COMMAND: + HandleVendorCommand(command); + break; default: UnhandledCommand(command); bHandled = false; break; } - if (bHandled && !bHandlerChanged) + if (bHandled) { CLockObject lock(&m_receiveMutex); - m_condition.Signal(); + if (m_expectedResponse == CEC_OPCODE_NONE || + m_expectedResponse == command.opcode) + m_condition.Signal(); } + MarkReady(); return bHandled; } @@ -704,12 +731,12 @@ bool CCECCommandHandler::HandleReceiveFailed(void) return true; } -bool CCECCommandHandler::TransmitPowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination) +bool CCECCommandHandler::TransmitImageViewOn(const cec_logical_address iInitiator, const cec_logical_address iDestination) { cec_command command; cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_IMAGE_VIEW_ON); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitStandby(const cec_logical_address iInitiator, const cec_logical_address iDestination) @@ -717,7 +744,7 @@ bool CCECCommandHandler::TransmitStandby(const cec_logical_address iInitiator, c cec_command command; cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_STANDBY); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination) @@ -725,7 +752,7 @@ bool CCECCommandHandler::TransmitRequestCecVersion(const cec_logical_address iIn cec_command command; cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_CEC_VERSION); - return Transmit(command); + return Transmit(command, true, CEC_OPCODE_CEC_VERSION); } bool CCECCommandHandler::TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination) @@ -733,7 +760,7 @@ bool CCECCommandHandler::TransmitRequestMenuLanguage(const cec_logical_address i cec_command command; cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_MENU_LANGUAGE); - return Transmit(command); + return Transmit(command, true, CEC_OPCODE_SET_MENU_LANGUAGE); } bool CCECCommandHandler::TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination) @@ -741,7 +768,7 @@ bool CCECCommandHandler::TransmitRequestOSDName(const cec_logical_address iIniti cec_command command; cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_OSD_NAME); - return Transmit(command); + return Transmit(command, true, CEC_OPCODE_SET_OSD_NAME); } bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination) @@ -749,7 +776,7 @@ bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_addres cec_command command; cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_PHYSICAL_ADDRESS); - return Transmit(command); + return Transmit(command, true, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS); } bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination) @@ -757,7 +784,7 @@ bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iI cec_command command; cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_POWER_STATUS); - return Transmit(command); + return Transmit(command, true, CEC_OPCODE_REPORT_POWER_STATUS); } bool CCECCommandHandler::TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination) @@ -765,7 +792,7 @@ bool CCECCommandHandler::TransmitRequestVendorId(const cec_logical_address iInit cec_command command; cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID); - return Transmit(command); + return Transmit(command, true, CEC_OPCODE_DEVICE_VENDOR_ID); } bool CCECCommandHandler::TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress) @@ -775,7 +802,7 @@ bool CCECCommandHandler::TransmitActiveSource(const cec_logical_address iInitiat command.parameters.PushBack((uint8_t) ((iPhysicalAddress >> 8) & 0xFF)); command.parameters.PushBack((uint8_t) (iPhysicalAddress & 0xFF)); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitCECVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_version cecVersion) @@ -784,7 +811,7 @@ bool CCECCommandHandler::TransmitCECVersion(const cec_logical_address iInitiator cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_CEC_VERSION); command.parameters.PushBack((uint8_t)cecVersion); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitInactiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress) @@ -794,7 +821,7 @@ bool CCECCommandHandler::TransmitInactiveSource(const cec_logical_address iIniti command.parameters.PushBack((iPhysicalAddress >> 8) & 0xFF); command.parameters.PushBack(iPhysicalAddress & 0xFF); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitMenuState(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_menu_state menuState) @@ -803,7 +830,7 @@ bool CCECCommandHandler::TransmitMenuState(const cec_logical_address iInitiator, cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_MENU_STATUS); command.parameters.PushBack((uint8_t)menuState); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, CStdString strDeviceName) @@ -813,7 +840,7 @@ bool CCECCommandHandler::TransmitOSDName(const cec_logical_address iInitiator, c for (unsigned int iPtr = 0; iPtr < strDeviceName.length(); iPtr++) command.parameters.PushBack(strDeviceName.at(iPtr)); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitOSDString(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_display_control duration, const char *strMessage) @@ -828,7 +855,7 @@ bool CCECCommandHandler::TransmitOSDString(const cec_logical_address iInitiator, for (unsigned int iPtr = 0; iPtr < iLen; iPtr++) command.parameters.PushBack(strMessage[iPtr]); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitPhysicalAddress(const cec_logical_address iInitiator, uint16_t iPhysicalAddress, cec_device_type type) @@ -839,7 +866,7 @@ bool CCECCommandHandler::TransmitPhysicalAddress(const cec_logical_address iInit command.parameters.PushBack((uint8_t) (iPhysicalAddress & 0xFF)); command.parameters.PushBack((uint8_t) (type)); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitPoll(const cec_logical_address iInitiator, const cec_logical_address iDestination) @@ -856,7 +883,7 @@ bool CCECCommandHandler::TransmitPowerState(const cec_logical_address iInitiator cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_REPORT_POWER_STATUS); command.parameters.PushBack((uint8_t) state); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitVendorID(const cec_logical_address iInitiator, uint64_t iVendorId) @@ -868,7 +895,7 @@ bool CCECCommandHandler::TransmitVendorID(const cec_logical_address iInitiator, command.parameters.PushBack((uint8_t) (((uint64_t)iVendorId >> 8) & 0xFF)); command.parameters.PushBack((uint8_t) ((uint64_t)iVendorId & 0xFF)); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitAudioStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, uint8_t state) @@ -877,7 +904,7 @@ bool CCECCommandHandler::TransmitAudioStatus(const cec_logical_address iInitiato cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_REPORT_AUDIO_STATUS); command.parameters.PushBack(state); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitSetSystemAudioMode(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_system_audio_status state) @@ -886,7 +913,7 @@ bool CCECCommandHandler::TransmitSetSystemAudioMode(const cec_logical_address iI cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_SYSTEM_AUDIO_MODE); command.parameters.PushBack((uint8_t)state); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitSystemAudioModeStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_system_audio_status state) @@ -895,7 +922,7 @@ bool CCECCommandHandler::TransmitSystemAudioModeStatus(const cec_logical_address cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS); command.parameters.PushBack((uint8_t)state); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitDeckStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_deck_info state) @@ -904,7 +931,7 @@ bool CCECCommandHandler::TransmitDeckStatus(const cec_logical_address iInitiator cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_DECK_STATUS); command.PushBack((uint8_t)state); - return Transmit(command); + return Transmit(command, false); } bool CCECCommandHandler::TransmitKeypress(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */) @@ -924,23 +951,36 @@ bool CCECCommandHandler::TransmitKeyRelease(const cec_logical_address iInitiator return Transmit(command, bWait); } -bool CCECCommandHandler::Transmit(cec_command &command, bool bExpectResponse /* = true */) +bool CCECCommandHandler::Transmit(cec_command &command, bool bExpectResponse /* = true */, cec_opcode expectedResponse /* = CEC_OPCODE_NONE */) { + bool bReturn(false); + MarkBusy(); command.transmit_timeout = m_iTransmitTimeout; - CLockObject writeLock(&m_processor->m_transmitMutex); - CLockObject receiveLock(&m_receiveMutex); - if (m_processor->Transmit(command)) { - if (bExpectResponse) - return m_condition.Wait(&m_receiveMutex, m_iTransmitWait); - return true; + uint8_t iTries(0), iMaxTries(command.opcode == CEC_OPCODE_NONE ? 1 : m_iTransmitRetries + 1); + CLockObject writeLock(&m_processor->m_transmitMutex); + CLockObject receiveLock(&m_receiveMutex); + ++m_iUseCounter; + while (!bReturn && ++iTries <= iMaxTries) + { + m_expectedResponse = expectedResponse; + if (m_processor->Transmit(command)) + { + m_processor->AddLog(CEC_LOG_DEBUG, "command transmitted"); + bReturn = bExpectResponse ? + m_condition.Wait(&m_receiveMutex, m_iTransmitWait) : + true; + } + } + --m_iUseCounter; } - return false; + MarkReady(); + return bReturn; } -bool CCECCommandHandler::InitHandler(void) +bool CCECCommandHandler::ActivateSource(void) { if (m_busDevice->GetLogicalAddress() == CECDEVICE_TV) { @@ -957,3 +997,21 @@ bool CCECCommandHandler::InitHandler(void) } return true; } + +void CCECCommandHandler::MarkBusy(void) +{ + CLockObject receiveLock(&m_receiveMutex); + ++m_iUseCounter; +} + +bool CCECCommandHandler::MarkReady(void) +{ + CLockObject receiveLock(&m_receiveMutex); + return m_iUseCounter > 0 ? (--m_iUseCounter == 0) : true; +} + +bool CCECCommandHandler::InUse(void) +{ + CLockObject receiveLock(&m_receiveMutex); + return m_iUseCounter > 0; +} diff --git a/src/lib/implementations/CECCommandHandler.h b/src/lib/implementations/CECCommandHandler.h index 0469b31..0e3602d 100644 --- a/src/lib/implementations/CECCommandHandler.h +++ b/src/lib/implementations/CECCommandHandler.h @@ -48,14 +48,17 @@ namespace CEC virtual ~CCECCommandHandler(void); virtual bool HandleCommand(const cec_command &command); - virtual cec_vendor_id GetVendorId(void) { return CEC_VENDOR_UNKNOWN; }; + virtual cec_vendor_id GetVendorId(void) { return m_vendorId; }; + virtual void SetVendorId(cec_vendor_id vendorId) { m_vendorId = vendorId; } virtual void HandlePoll(const cec_logical_address iInitiator, const cec_logical_address iDestination); virtual bool HandleReceiveFailed(void); + static bool HasSpecificHandler(cec_vendor_id vendorId) { return vendorId == CEC_VENDOR_LG || vendorId == CEC_VENDOR_SAMSUNG || vendorId == CEC_VENDOR_PANASONIC;} - virtual bool InitHandler(void); + virtual bool InitHandler(void) { return true; } + virtual bool ActivateSource(void); virtual uint8_t GetTransmitRetries(void) const { return m_iTransmitRetries; } - virtual bool TransmitPowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); + virtual bool TransmitImageViewOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); virtual bool TransmitStandby(const cec_logical_address iInitiator, const cec_logical_address iDestination); virtual bool TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination); virtual bool TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination); @@ -80,6 +83,12 @@ namespace CEC virtual bool TransmitKeypress(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_user_control_code key, bool bWait = true); virtual bool TransmitKeyRelease(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWait = true); + virtual void MarkBusy(void); + virtual bool MarkReady(void); + virtual bool InUse(void); + + virtual bool SendDeckStatusUpdateOnActiveSource(void) const { return m_bOPTSendDeckStatusUpdateOnActiveSource; }; + protected: virtual bool HandleActiveSource(const cec_command &command); virtual bool HandleDeckControl(const cec_command &command); @@ -113,6 +122,7 @@ namespace CEC 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) { return true; } virtual void UnhandledCommand(const cec_command &command); virtual unsigned int GetMyDevices(std::vector &devices) const; @@ -123,7 +133,7 @@ namespace CEC virtual bool SetVendorId(const cec_command &command); virtual void SetPhysicalAddress(cec_logical_address iAddress, uint16_t iNewAddress); - virtual bool Transmit(cec_command &command, bool bExpectResponse = true); + virtual bool Transmit(cec_command &command, bool bExpectResponse = true, cec_opcode expectedResponse = CEC_OPCODE_NONE); CCECBusDevice *m_busDevice; CCECProcessor *m_processor; @@ -131,6 +141,10 @@ namespace CEC int32_t m_iTransmitWait; int8_t m_iTransmitRetries; bool m_bHandlerInited; + uint8_t m_iUseCounter; + cec_opcode m_expectedResponse; + bool m_bOPTSendDeckStatusUpdateOnActiveSource; + cec_vendor_id m_vendorId; CMutex m_receiveMutex; CCondition m_condition; }; diff --git a/src/lib/implementations/SLCommandHandler.cpp b/src/lib/implementations/SLCommandHandler.cpp index f86a644..7a357d3 100644 --- a/src/lib/implementations/SLCommandHandler.cpp +++ b/src/lib/implementations/SLCommandHandler.cpp @@ -35,6 +35,7 @@ #include "../devices/CECPlaybackDevice.h" #include "../CECProcessor.h" #include "../platform/timeutils.h" +#include "../platform/threads.h" using namespace CEC; @@ -51,10 +52,103 @@ CSLCommandHandler::CSLCommandHandler(CCECBusDevice *busDevice) : CCECCommandHandler(busDevice), m_bAwaitingReceiveFailed(false), m_bSLEnabled(false), - m_bVendorIdSent(false) + m_bPowerStateReset(false) { - /* TODO set to powered off until we fixed the connect on start loop issue */ - m_processor->GetPrimaryDevice()->m_powerStatus = CEC_POWER_STATUS_STANDBY; + m_vendorId = CEC_VENDOR_LG; + CCECBusDevice *primary = m_processor->GetPrimaryDevice(); + + /* imitate LG devices */ + if (m_busDevice->GetLogicalAddress() != primary->GetLogicalAddress()) + primary->SetVendorId(CEC_VENDOR_LG); + SetLGDeckStatus(); + + /* LG TVs don't always reply to CEC version requests, so just set it to 1.3a */ + if (m_busDevice->GetLogicalAddress() == CECDEVICE_TV) + m_busDevice->SetCecVersion(CEC_VERSION_1_3A); + + /* LG devices always return "korean" as language */ + cec_menu_language lang; + lang.device = m_busDevice->GetLogicalAddress(); + snprintf(lang.language, 4, "eng"); + m_busDevice->SetMenuLanguage(lang); +} + + +void CSLCommandHandler::HandlePoll(const cec_logical_address iInitiator, const cec_logical_address iDestination) +{ + CCECCommandHandler::HandlePoll(iInitiator, iDestination); + m_bAwaitingReceiveFailed = true; +} + +bool CSLCommandHandler::HandleReceiveFailed(void) +{ + if (m_bAwaitingReceiveFailed) + { + m_bAwaitingReceiveFailed = false; + return false; + } + + return true; +} + +bool CSLCommandHandler::InitHandler(void) +{ + if (m_bHandlerInited) + return true; + m_bHandlerInited = true; + + /* reply with LGs vendor id */ + CCECBusDevice *primary = m_processor->GetPrimaryDevice(); + if (m_busDevice->GetLogicalAddress() != primary->GetLogicalAddress()) + primary->TransmitVendorID(CECDEVICE_TV, false); + + primary->SetPowerStatus(CEC_POWER_STATUS_STANDBY); + return true; +} + +bool CSLCommandHandler::ActivateSource(void) +{ + CCECBusDevice *primary = m_processor->GetPrimaryDevice(); + primary->SetActiveSource(); + primary->TransmitActiveSource(); + return true; +} + +bool CSLCommandHandler::HandleActiveSource(const cec_command &command) +{ + if (command.parameters.size == 2) + { + uint16_t iAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]); + if (iAddress != m_busDevice->GetPhysicalAddress(false)) + m_bSLEnabled = false; + return m_processor->SetActiveSource(iAddress); + } + + return true; +} + +bool CSLCommandHandler::HandleFeatureAbort(const cec_command &command) +{ + CCECBusDevice *primary = m_processor->GetPrimaryDevice(); + if (primary->GetPowerStatus(false) == CEC_POWER_STATUS_ON && !m_bPowerStateReset && !m_bSLEnabled) + { + m_bPowerStateReset = true; + primary->SetPowerStatus(CEC_POWER_STATUS_STANDBY); + } + + return CCECCommandHandler::HandleFeatureAbort(command); +} + +bool CSLCommandHandler::HandleGivePhysicalAddress(const cec_command &command) +{ + if (m_processor->IsStarted() && m_busDevice->MyLogicalAddressContains(command.destination)) + { + CCECBusDevice *device = GetDevice(command.destination); + if (device) + return device->TransmitPhysicalAddress(); + } + + return false; } bool CSLCommandHandler::HandleVendorCommand(const cec_command &command) @@ -87,23 +181,6 @@ bool CSLCommandHandler::HandleVendorCommand(const cec_command &command) return false; } -bool CSLCommandHandler::HandleGiveDeckStatus(const cec_command &command) -{ - if (command.parameters.size == 1) - { - if (command.parameters[0] == CEC_STATUS_REQUEST_ONCE || - command.parameters[0] == CEC_STATUS_REQUEST_ON) - { - TransmitDeckStatus(command.initiator); - } - else - { - CCECCommandHandler::HandleGiveDeckStatus(command); - } - } - return true; -} - void CSLCommandHandler::HandleVendorCommand01(const cec_command &command) { TransmitVendorCommand0205(command.destination, command.initiator); @@ -116,17 +193,7 @@ void CSLCommandHandler::TransmitVendorCommand0205(const cec_logical_address iSou response.PushBack(SL_COMMAND_UNKNOWN_02); response.PushBack(SL_COMMAND_UNKNOWN_03); - Transmit(response); -} - -void CSLCommandHandler::TransmitVendorCommand05(const cec_logical_address iSource, const cec_logical_address iDestination) -{ - m_bSLEnabled = true; - cec_command response; - cec_command::Format(response, iSource, iDestination, CEC_OPCODE_VENDOR_COMMAND); - response.PushBack(SL_COMMAND_CONNECT_ACCEPT); - response.PushBack((uint8_t)iSource); - Transmit(response); + Transmit(response, false); } void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command) @@ -135,22 +202,16 @@ void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command) if (device) { m_bSLEnabled = true; - device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); + + device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); //XXX device->TransmitPowerState(command.initiator); - device->TransmitVendorID(command.initiator); - TransmitPowerOn(device->GetLogicalAddress(), command.initiator); - } -} + device->SetPowerStatus(CEC_POWER_STATUS_ON); -void CSLCommandHandler::HandleVendorCommandSLConnect(const cec_command &command) -{ - m_bSLEnabled = true; - m_processor->m_busDevices[command.initiator]->SetActiveSource(); - m_processor->m_busDevices[command.destination]->TransmitActiveSource(); - TransmitVendorCommand05(command.destination, command.initiator); - TransmitDeckStatus(command.initiator); + SetLGDeckStatus(); + device->SetActiveSource(); + TransmitImageViewOn(device->GetLogicalAddress(), command.initiator); + } } - void CSLCommandHandler::HandleVendorCommandPowerOnStatus(const cec_command &command) { if (command.destination != CECDEVICE_BROADCAST) @@ -162,159 +223,35 @@ void CSLCommandHandler::HandleVendorCommandPowerOnStatus(const cec_command &comm } } -void CSLCommandHandler::TransmitDeckStatus(const cec_logical_address iDestination) +void CSLCommandHandler::HandleVendorCommandSLConnect(const cec_command &command) { - /* set deck status for the playback device */ + m_bSLEnabled = true; + SetLGDeckStatus(); + CCECBusDevice *primary = m_processor->GetPrimaryDevice(); - if (primary->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || primary->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) - { - ((CCECPlaybackDevice *)primary)->SetDeckStatus(CEC_DECK_INFO_OTHER_STATUS_LG); - ((CCECPlaybackDevice *)primary)->TransmitDeckStatus(iDestination); - } + + primary->SetActiveSource(); + TransmitImageViewOn(primary->GetLogicalAddress(), command.initiator); + TransmitVendorCommand05(primary->GetLogicalAddress(), command.initiator); } -bool CSLCommandHandler::TransmitLGVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination) +void CSLCommandHandler::TransmitVendorCommand05(const cec_logical_address iSource, const cec_logical_address iDestination) { cec_command response; - cec_command::Format(response, iInitiator, iDestination, CEC_OPCODE_DEVICE_VENDOR_ID); - response.parameters.PushBack((uint8_t) (((uint64_t)CEC_VENDOR_LG >> 16) & 0xFF)); - response.parameters.PushBack((uint8_t) (((uint64_t)CEC_VENDOR_LG >> 8) & 0xFF)); - response.parameters.PushBack((uint8_t) ((uint64_t)CEC_VENDOR_LG & 0xFF)); - - Transmit(response); - return true; + cec_command::Format(response, iSource, iDestination, CEC_OPCODE_VENDOR_COMMAND); + response.PushBack(SL_COMMAND_CONNECT_ACCEPT); + response.PushBack((uint8_t)iSource); + Transmit(response, false); } -bool CSLCommandHandler::HandleGiveDeviceVendorId(const cec_command &command) +void CSLCommandHandler::SetLGDeckStatus(void) { - /* imitate LG devices */ - CCECBusDevice *device = GetDevice(command.destination); + /* LG TVs only route keypresses when the deck status is set to 0x20 */ + CCECBusDevice *device = m_processor->GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE); if (device) - device->SetVendorId(CEC_VENDOR_LG); - - return CCECCommandHandler::HandleGiveDeviceVendorId(command); -} - -bool CSLCommandHandler::HandleCommand(const cec_command &command) -{ - bool bHandled(false); - - if (m_processor->IsStarted() && (m_busDevice->MyLogicalAddressContains(command.destination) || - command.destination == CECDEVICE_BROADCAST)) - { - switch(command.opcode) - { - case CEC_OPCODE_VENDOR_COMMAND: - bHandled = HandleVendorCommand(command); - break; - case CEC_OPCODE_FEATURE_ABORT: - { - if (!m_bVendorIdSent) - { - m_bVendorIdSent = true; - TransmitLGVendorId(m_processor->GetLogicalAddresses().primary, CECDEVICE_BROADCAST); - } - } - bHandled = true; - default: - break; - } - } - - if (!bHandled) - bHandled = CCECCommandHandler::HandleCommand(command); - - return bHandled; -} - -void CSLCommandHandler::HandlePoll(const cec_logical_address iInitiator, const cec_logical_address iDestination) -{ - CCECCommandHandler::HandlePoll(iInitiator, iDestination); - m_bAwaitingReceiveFailed = true; -} - -bool CSLCommandHandler::HandleReceiveFailed(void) -{ - if (m_bAwaitingReceiveFailed) - { - m_bAwaitingReceiveFailed = false; - return false; - } - - return true; -} - -bool CSLCommandHandler::InitHandler(void) -{ - if (m_bHandlerInited) - return true; - m_bHandlerInited = true; + ((CCECPlaybackDevice *)device)->SetDeckStatus(CEC_DECK_INFO_OTHER_STATUS_LG); - m_processor->SetStandardLineTimeout(3); - m_processor->SetRetryLineTimeout(3); - - /* increase the number of retries because the tv is keeping the bus busy at times */ - m_iTransmitWait = 2000; - m_iTransmitRetries = 4; - m_iTransmitTimeout = 500; - - CCECBusDevice *primary = m_processor->GetPrimaryDevice(); - if (m_busDevice->GetLogicalAddress() != primary->GetLogicalAddress()) - { - primary->SetVendorId(CEC_VENDOR_LG, false); - primary->TransmitVendorID(CECDEVICE_TV, false); - } - - if (m_busDevice->GetLogicalAddress() == CECDEVICE_TV) - { - /* LG TVs don't always reply to CEC version requests, so just set it to 1.3a */ - m_busDevice->SetCecVersion(CEC_VERSION_1_3A); - } - - /* LG devices always return "korean" as language */ - cec_menu_language lang; - lang.device = m_busDevice->GetLogicalAddress(); - snprintf(lang.language, 4, "eng"); - m_busDevice->SetMenuLanguage(lang); - - if (m_busDevice->GetLogicalAddress() == CECDEVICE_TV) - { - m_processor->SetActiveSource(); - - /* LG TVs only route keypresses when the deck status is set to 0x20 */ - cec_logical_addresses addr = m_processor->GetLogicalAddresses(); - for (uint8_t iPtr = 0; iPtr < 15; iPtr++) - { - CCECBusDevice *device = m_processor->m_busDevices[iPtr]; - - if (addr[iPtr]) - { - if (device && (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || - device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)) - { - ((CCECPlaybackDevice *)device)->SetDeckStatus(CEC_DECK_INFO_OTHER_STATUS_LG); - ((CCECPlaybackDevice *)device)->TransmitDeckStatus(CECDEVICE_TV); - } - } - } - } - - return true; -} - -bool CSLCommandHandler::TransmitPowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination) -{ - if (iDestination != CECDEVICE_BROADCAST && - iDestination != CECDEVICE_TV && - m_processor->m_busDevices[iDestination]->GetVendorId(false) == CEC_VENDOR_LG) - { - cec_command command; - cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_VENDOR_COMMAND); - command.parameters.PushBack((uint8_t)SL_COMMAND_POWER_ON); - command.parameters.PushBack(0x00); - return Transmit(command); - } - - return CCECCommandHandler::TransmitPowerOn(iInitiator, iDestination); + device = m_processor->GetDeviceByType(CEC_DEVICE_TYPE_RECORDING_DEVICE); + if (device) + ((CCECPlaybackDevice *)device)->SetDeckStatus(CEC_DECK_INFO_OTHER_STATUS_LG); } - diff --git a/src/lib/implementations/SLCommandHandler.h b/src/lib/implementations/SLCommandHandler.h index 3a5a779..e398dc8 100644 --- a/src/lib/implementations/SLCommandHandler.h +++ b/src/lib/implementations/SLCommandHandler.h @@ -40,30 +40,32 @@ namespace CEC public: CSLCommandHandler(CCECBusDevice *busDevice); virtual ~CSLCommandHandler(void) {}; - virtual cec_vendor_id GetVendorId(void) { return CEC_VENDOR_LG; }; - virtual bool HandleCommand(const cec_command &command); virtual void HandlePoll(const cec_logical_address iInitiator, const cec_logical_address iDestination); virtual bool HandleReceiveFailed(void); + virtual bool InitHandler(void); - virtual bool TransmitLGVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination); - virtual bool TransmitPowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); + virtual bool ActivateSource(void); protected: + virtual bool HandleActiveSource(const cec_command &command); + virtual bool HandleFeatureAbort(const cec_command &command); + virtual bool HandleGivePhysicalAddress(const cec_command &command); + virtual bool HandleVendorCommand(const cec_command &command); + virtual void HandleVendorCommand01(const cec_command &command); + virtual void TransmitVendorCommand0205(const cec_logical_address iSource, const cec_logical_address iDestination); + virtual void HandleVendorCommandPowerOn(const cec_command &command); - virtual void HandleVendorCommandSLConnect(const cec_command &command); virtual void HandleVendorCommandPowerOnStatus(const cec_command &command); - virtual void TransmitVendorCommand0205(const cec_logical_address iSource, const cec_logical_address iDestination); + virtual void HandleVendorCommandSLConnect(const cec_command &command); virtual void TransmitVendorCommand05(const cec_logical_address iSource, const cec_logical_address iDestination); - virtual void TransmitDeckStatus(const cec_logical_address iDestination); - virtual bool HandleGiveDeviceVendorId(const cec_command &command); - virtual bool HandleVendorCommand(const cec_command &command); - virtual bool HandleGiveDeckStatus(const cec_command &command); + + virtual void SetLGDeckStatus(void); bool m_bAwaitingReceiveFailed; bool m_bSLEnabled; - bool m_bVendorIdSent; + bool m_bPowerStateReset; }; }; diff --git a/src/lib/implementations/VLCommandHandler.cpp b/src/lib/implementations/VLCommandHandler.cpp index fbbfa29..f0d59db 100644 --- a/src/lib/implementations/VLCommandHandler.cpp +++ b/src/lib/implementations/VLCommandHandler.cpp @@ -32,11 +32,22 @@ #include "VLCommandHandler.h" #include "../devices/CECBusDevice.h" -#include "../util/StdString.h" +#include "../CECProcessor.h" using namespace CEC; CVLCommandHandler::CVLCommandHandler(CCECBusDevice *busDevice) : CCECCommandHandler(busDevice) { + m_vendorId = CEC_VENDOR_PANASONIC; +} + +bool CVLCommandHandler::InitHandler(void) +{ + CCECBusDevice *primary = m_processor->GetPrimaryDevice(); + if (primary->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) + { + return m_processor->ChangeDeviceType(CEC_DEVICE_TYPE_RECORDING_DEVICE, CEC_DEVICE_TYPE_PLAYBACK_DEVICE); + } + return CCECCommandHandler::InitHandler(); } diff --git a/src/lib/implementations/VLCommandHandler.h b/src/lib/implementations/VLCommandHandler.h index 3959ad7..cff1da4 100644 --- a/src/lib/implementations/VLCommandHandler.h +++ b/src/lib/implementations/VLCommandHandler.h @@ -40,6 +40,6 @@ namespace CEC public: CVLCommandHandler(CCECBusDevice *busDevice); virtual ~CVLCommandHandler(void) {}; - virtual cec_vendor_id GetVendorId(void) { return CEC_VENDOR_PANASONIC; }; + virtual bool InitHandler(void); }; }; diff --git a/src/lib/platform/threads.cpp b/src/lib/platform/threads.cpp index 9ea0008..d168e90 100644 --- a/src/lib/platform/threads.cpp +++ b/src/lib/platform/threads.cpp @@ -35,9 +35,9 @@ using namespace CEC; -CMutex::CMutex(void) +CMutex::CMutex(bool bRecursive /* = true */) { - pthread_mutex_init(&m_mutex, GetMutexAttribute()); + pthread_mutex_init(&m_mutex, bRecursive ? GetMutexAttribute() : NULL); } CMutex::~CMutex(void) diff --git a/src/lib/platform/threads.h b/src/lib/platform/threads.h index 6b0b870..a258081 100644 --- a/src/lib/platform/threads.h +++ b/src/lib/platform/threads.h @@ -56,7 +56,7 @@ namespace CEC class CMutex { public: - CMutex(void); + CMutex(bool bRecursive = true); virtual ~CMutex(void); bool TryLock(void); diff --git a/support/cec-test-device.sh b/support/cec-test-device.sh new file mode 100755 index 0000000..d00b483 --- /dev/null +++ b/support/cec-test-device.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +## Tests whether the USB-CEC adapter can be accessed correctly +## Copyright (C) 2011 Pulse-Eight Ltd. + +check_lsusb() +{ + echo -n " * searching USB device: " + adapters_found=`lsusb | grep 2548:1001 | wc -l` + if [ "x${adapters_found}" = "x0" ]; then + echo "NOT FOUND" + return 1 + else + echo "ok" + return 0 + fi +} + +check_acm_module() +{ + echo -n " * checking for CDC-ACM support: " + acm_found=`grep cdc_acm /proc/modules | wc -l` + if [ "x${acm_found}" = "x0" ]; then + echo "NOT LOADED" + return 1 + else + echo "ok" + return 0 + fi +} + +check_acm_file() +{ + echo -n " * checking for CDC-ACM node: " + acm_found=`ls /dev/ttyACM* | wc -l` + if [ "x${acm_found}" = "x0" ]; then + echo "NOT FOUND" + return 1 + else + echo "ok" + return 0 + fi +} + +check_cec_client_dev() +{ + echo -n " * checking cec-client: " + cec_client=`cec-client -l | grep 'Found devices' | awk '{print \$3}'` + if [ -z "${cec_client}" ]; then + echo "ERROR" + elif [ "x${cec_client}" = "xNONE" ]; then + echo "NO DEVICES FOUND" + else + echo "ok" + fi +} + +check_poll_tv() +{ + echo -n " * trying to poll the TV: " + cec_client=`echo 'poll 0' | cec-client -t p -p 1 -d 1 -s | tail -n1 | grep 'POLL'` + if [ -z "${cec_client}" ]; then + echo "ERROR" + elif [ "x${cec_client}" = "xPOLL message sent" ]; then + echo "ok" + else + echo "COULD NOT POLL THE TV" + fi +} + +check_tv_vendor() +{ + echo -n " * vendor id of the TV: " + cec_client=`echo 'ven 0' | cec-client -t p -p 1 -d 1 -s | tail -n1 | grep 'vendor' | awk '{print \$3}'` + if [ -z "${cec_client}" ]; then + echo "ERROR" + else + echo "${cec_client}" + fi +} + +check_tv_power() +{ + echo -n " * power status of the TV: " + cec_client=`echo 'pow 0' | cec-client -t p -p 1 -d 1 -s | tail -n1 | grep 'power' | awk '{print \$3}'` + if [ -z "${cec_client}" ]; then + echo "ERROR" + else + echo "${cec_client}" + fi +} + +check_tv_lang() +{ + echo -n " * language of the TV: " + cec_client=`echo 'lang 0' | cec-client -t p -p 1 -d 1 -s | tail -n1 | grep 'language' | awk '{print \$3}'` + if [ -z "${cec_client}" ]; then + echo "ERROR" + else + echo "${cec_client}" + fi +} + +send_power_off() +{ + echo -n " * powering off the TV: " + cec_client=`echo 'standby 0' | cec-client -t p -p 1 -d 1 -s | tail -n1` + echo "ok" +} + +echo "Pulse-Eight USB-CEC Adapter tester v0.1" +echo "" + +check_lsusb +check_acm_module +check_acm_file +check_cec_client_dev +check_poll_tv +check_tv_vendor +check_tv_power +check_tv_lang +send_power_off +