From: Lars Op den Kamp Date: Fri, 13 Dec 2013 18:58:56 +0000 (+0100) Subject: Release 2.1.4: merge remote-tracking branch 'origin/master' into release X-Git-Tag: upstream/2.2.0~1^2~3 X-Git-Url: https://git.piment-noir.org/?p=deb_libcec.git;a=commitdiff_plain;h=25dd1b2e713206ab5c365c041aebeb8bf8869ca4;hp=54be21e43ea5b0268858e09f0252bb3909ef6132 Release 2.1.4: merge remote-tracking branch 'origin/master' into release --- diff --git a/ChangeLog b/ChangeLog index 0a17f5e..2ec6ebb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,60 @@ +libcec (2.1.4-1) unstable; urgency=low + + * changed / added: + * set the vendor id of devices handled by libCEC to our vendor id + * added protection against standby without a notification from XBMC and + clock changes. issue #186 + * update system audio mode when audio system devices are activated + * added vendor id and name 'Harman/Kardon' + * added vendor id and name 'Marantz' + * cec-tray: support for away mode + * cec-tray: support for screensaver events + * fixed: + * fixed potential crash when SendCommand returns NULL + * fixed crash when CCECProcessor::Transmit was called after + CCECProcessor::Close + * always call the SourceActivated() callback in + CCECBusDevice::MarkAsActiveSource(), even when we were already active, + and let clients handle dupes. this prevented the screensaver from + being cancelled in XBMC + * send our vendor id when the tv sends it's vendor id + * transmit our physical address when the TV sends it's physical address + * potential deadlock + * leak on exit in CWaitForResponse::Clear() + * also transmit an active source message as response to a set stream path + command when we were already marked as active. bugzid: 3116 + * only send an active source message when receiving a routing change to an + address handled by libCEC. issue #56 + * HandleVendorRemoteButtonUp -> HandleUserControlRelease for all vendors + * silence warning for xx:a0:00:09:b0:00:80:20. bugzid: 2559 + * fixed comboKeyTimeout=0. closes #168 + * Panasonic: doesn't request the active source status when powered up, but + it does send a vendor command. send an active source message when we're + marked as active source after receiving this command. issue #192 + * possible crash when the tv does not respond to polls and the client isn't + registered yet. don't poll the tv in monitoring mode. closes #163 + * Panasonic: standby command + * Panasonic: silence 'unmapped code' warning. bugzid: 2375 + * LG: clean up and attempt to fix issue #176 + * LG: newer firmwares don't send a second power status request after it's + been requested once and replied with 'standby -> on' like previous + versions. send an active source message in an attempt to fix this. issue: + #176 + * Sharp: check whether the 'auto power on' option is disabled and tell the + user how to enable it + * Samsung: respond to vendor command 0x23 sent by samsung. attempt to fix + bugzid: 2164 + * cec-client: try to reconnect when the connection was lost + * cec-tray: crash on load (MissingManifestResourceException) + * cec-tray: crash or deadlock on exit + * cec-tray: crash when the connection to the adapter was lost + * cec-tray: display TV not responding warning on top + * windows: build libCEC and cec-client against toolset v100 (VS2010) + * FreeBSD: proper the adapter detection. credits @fneufneu + * suppress mangling warning. credits @warped-rudi. closes #190 + + -- Pulse-Eight Packaging Sat, 13 Dec 2013 16:02:00 +0100 + libcec (2.1.3-1) unstable; urgency=low * changed / added: diff --git a/configure.ac b/configure.ac index 9b2ac34..772ca7f 100644 --- a/configure.ac +++ b/configure.ac @@ -342,7 +342,7 @@ fi LIBS_LIBCEC="$LIBS" LIBS="$libs_client" -CXXFLAGS="$CXXFLAGS -fPIC -Wall -Wextra -Wno-missing-field-initializers" +CXXFLAGS="$CXXFLAGS -fPIC -Wall -Wextra -Wno-missing-field-initializers -Wno-psabi" if test "x$use_debug" = "xyes"; then CXXFLAGS="$CXXFLAGS -g" diff --git a/debian/changelog b/debian/changelog index 0a17f5e..2ec6ebb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,60 @@ +libcec (2.1.4-1) unstable; urgency=low + + * changed / added: + * set the vendor id of devices handled by libCEC to our vendor id + * added protection against standby without a notification from XBMC and + clock changes. issue #186 + * update system audio mode when audio system devices are activated + * added vendor id and name 'Harman/Kardon' + * added vendor id and name 'Marantz' + * cec-tray: support for away mode + * cec-tray: support for screensaver events + * fixed: + * fixed potential crash when SendCommand returns NULL + * fixed crash when CCECProcessor::Transmit was called after + CCECProcessor::Close + * always call the SourceActivated() callback in + CCECBusDevice::MarkAsActiveSource(), even when we were already active, + and let clients handle dupes. this prevented the screensaver from + being cancelled in XBMC + * send our vendor id when the tv sends it's vendor id + * transmit our physical address when the TV sends it's physical address + * potential deadlock + * leak on exit in CWaitForResponse::Clear() + * also transmit an active source message as response to a set stream path + command when we were already marked as active. bugzid: 3116 + * only send an active source message when receiving a routing change to an + address handled by libCEC. issue #56 + * HandleVendorRemoteButtonUp -> HandleUserControlRelease for all vendors + * silence warning for xx:a0:00:09:b0:00:80:20. bugzid: 2559 + * fixed comboKeyTimeout=0. closes #168 + * Panasonic: doesn't request the active source status when powered up, but + it does send a vendor command. send an active source message when we're + marked as active source after receiving this command. issue #192 + * possible crash when the tv does not respond to polls and the client isn't + registered yet. don't poll the tv in monitoring mode. closes #163 + * Panasonic: standby command + * Panasonic: silence 'unmapped code' warning. bugzid: 2375 + * LG: clean up and attempt to fix issue #176 + * LG: newer firmwares don't send a second power status request after it's + been requested once and replied with 'standby -> on' like previous + versions. send an active source message in an attempt to fix this. issue: + #176 + * Sharp: check whether the 'auto power on' option is disabled and tell the + user how to enable it + * Samsung: respond to vendor command 0x23 sent by samsung. attempt to fix + bugzid: 2164 + * cec-client: try to reconnect when the connection was lost + * cec-tray: crash on load (MissingManifestResourceException) + * cec-tray: crash or deadlock on exit + * cec-tray: crash when the connection to the adapter was lost + * cec-tray: display TV not responding warning on top + * windows: build libCEC and cec-client against toolset v100 (VS2010) + * FreeBSD: proper the adapter detection. credits @fneufneu + * suppress mangling warning. credits @warped-rudi. closes #190 + + -- Pulse-Eight Packaging Sat, 13 Dec 2013 16:02:00 +0100 + libcec (2.1.3-1) unstable; urgency=low * changed / added: diff --git a/include/cectypes.h b/include/cectypes.h index 01adebe..9f86253 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -825,28 +825,31 @@ typedef enum cec_bus_device_status typedef enum cec_vendor_id { - CEC_VENDOR_TOSHIBA = 0x000039, - CEC_VENDOR_SAMSUNG = 0x0000F0, - CEC_VENDOR_DENON = 0x0005CD, - CEC_VENDOR_LOEWE = 0x000982, - CEC_VENDOR_ONKYO = 0x0009B0, - CEC_VENDOR_MEDION = 0x000CB8, - CEC_VENDOR_TOSHIBA2 = 0x000CE7, - CEC_VENDOR_AKAI = 0x0020C7, - CEC_VENDOR_AOC = 0x002467, - CEC_VENDOR_PANASONIC = 0x008045, - CEC_VENDOR_PHILIPS = 0x00903E, - CEC_VENDOR_DAEWOO = 0x009053, - CEC_VENDOR_YAMAHA = 0x00A0DE, - CEC_VENDOR_GRUNDIG = 0x00D0D5, - CEC_VENDOR_PIONEER = 0x00E036, - CEC_VENDOR_LG = 0x00E091, - CEC_VENDOR_SHARP = 0x08001F, - CEC_VENDOR_SONY = 0x080046, - CEC_VENDOR_BROADCOM = 0x18C086, - CEC_VENDOR_VIZIO = 0x6B746D, - CEC_VENDOR_BENQ = 0x8065E9, - CEC_VENDOR_UNKNOWN = 0 + CEC_VENDOR_TOSHIBA = 0x000039, + CEC_VENDOR_SAMSUNG = 0x0000F0, + CEC_VENDOR_DENON = 0x0005CD, + CEC_VENDOR_MARANTZ = 0x000678, + CEC_VENDOR_LOEWE = 0x000982, + CEC_VENDOR_ONKYO = 0x0009B0, + CEC_VENDOR_MEDION = 0x000CB8, + CEC_VENDOR_TOSHIBA2 = 0x000CE7, + CEC_VENDOR_PULSE_EIGHT = 0x001582, + CEC_VENDOR_AKAI = 0x0020C7, + CEC_VENDOR_AOC = 0x002467, + CEC_VENDOR_PANASONIC = 0x008045, + CEC_VENDOR_PHILIPS = 0x00903E, + CEC_VENDOR_DAEWOO = 0x009053, + CEC_VENDOR_YAMAHA = 0x00A0DE, + CEC_VENDOR_GRUNDIG = 0x00D0D5, + CEC_VENDOR_PIONEER = 0x00E036, + CEC_VENDOR_LG = 0x00E091, + CEC_VENDOR_SHARP = 0x08001F, + CEC_VENDOR_SONY = 0x080046, + CEC_VENDOR_BROADCOM = 0x18C086, + CEC_VENDOR_VIZIO = 0x6B746D, + CEC_VENDOR_BENQ = 0x8065E9, + CEC_VENDOR_HARMAN_KARDON = 0x9C645E, + CEC_VENDOR_UNKNOWN = 0 } cec_vendor_id; typedef enum cec_adapter_type @@ -1420,7 +1423,8 @@ typedef enum cec_client_version CEC_CLIENT_VERSION_2_1_1 = 0x2101, CEC_CLIENT_VERSION_2_1_2 = 0x2102, CEC_CLIENT_VERSION_2_1_3 = 0x2103, - CEC_CLIENT_VERSION_CURRENT = 0x2103 + CEC_CLIENT_VERSION_2_1_4 = 0x2104, + CEC_CLIENT_VERSION_CURRENT = 0x2104 } cec_client_version; typedef enum cec_server_version @@ -1452,7 +1456,8 @@ typedef enum cec_server_version CEC_SERVER_VERSION_2_1_1 = 0x2101, CEC_SERVER_VERSION_2_1_2 = 0x2102, CEC_SERVER_VERSION_2_1_3 = 0x2103, - CEC_SERVER_VERSION_CURRENT = 0x2103 + CEC_SERVER_VERSION_2_1_4 = 0x2104, + CEC_SERVER_VERSION_CURRENT = 0x2104 } cec_server_version; struct libcec_configuration diff --git a/project/LibCecSharp/LibCecSharp.rc b/project/LibCecSharp/LibCecSharp.rc index 81ae7b4..514fef2 100644 Binary files a/project/LibCecSharp/LibCecSharp.rc and b/project/LibCecSharp/LibCecSharp.rc differ diff --git a/project/libcec/libcec.rc b/project/libcec/libcec.rc index dd5aea5..a302d99 100644 Binary files a/project/libcec/libcec.rc and b/project/libcec/libcec.rc differ diff --git a/project/libcec/libcec.vcxproj b/project/libcec/libcec.vcxproj index b8db0b1..dc8daf8 100644 --- a/project/libcec/libcec.vcxproj +++ b/project/libcec/libcec.vcxproj @@ -47,6 +47,8 @@ + + @@ -93,6 +95,8 @@ + + @@ -116,25 +120,25 @@ DynamicLibrary true MultiByte - v110 + v100 DynamicLibrary true MultiByte - v110 + v100 DynamicLibrary false MultiByte - v110 + v100 DynamicLibrary false MultiByte - v110 + v100 diff --git a/project/libcec/libcec.vcxproj.filters b/project/libcec/libcec.vcxproj.filters index b9e41ed..903f44d 100644 --- a/project/libcec/libcec.vcxproj.filters +++ b/project/libcec/libcec.vcxproj.filters @@ -68,6 +68,12 @@ implementations + + implementations + + + implementations + devices @@ -201,6 +207,12 @@ implementations + + implementations + + + implementations + devices diff --git a/project/testclient/resource.h b/project/testclient/resource.h index 84edc7f..95a6c1a 100644 Binary files a/project/testclient/resource.h and b/project/testclient/resource.h differ diff --git a/project/testclient/testclient.rc b/project/testclient/testclient.rc index 5be5437..5990a9c 100644 Binary files a/project/testclient/testclient.rc and b/project/testclient/testclient.rc differ diff --git a/project/testclient/testclient.vcxproj b/project/testclient/testclient.vcxproj index 9e910ac..07b5487 100644 --- a/project/testclient/testclient.vcxproj +++ b/project/testclient/testclient.vcxproj @@ -28,25 +28,25 @@ Application true MultiByte - v110 + v100 Application true MultiByte - v110 + v100 Application false MultiByte - v110 + v100 Application false MultiByte - v110 + v100 diff --git a/src/CecSharpTester/Properties/AssemblyInfo.cs b/src/CecSharpTester/Properties/AssemblyInfo.cs index de041e6..cb4952f 100644 --- a/src/CecSharpTester/Properties/AssemblyInfo.cs +++ b/src/CecSharpTester/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("2.1.3.0")] -[assembly: AssemblyFileVersion("2.1.3.0")] +[assembly: AssemblyVersion("2.1.4.0")] +[assembly: AssemblyFileVersion("2.1.4.0")] diff --git a/src/LibCecSharp/AssemblyInfo.cpp b/src/LibCecSharp/AssemblyInfo.cpp index 50e65ec..19f1b43 100644 --- a/src/LibCecSharp/AssemblyInfo.cpp +++ b/src/LibCecSharp/AssemblyInfo.cpp @@ -13,7 +13,7 @@ using namespace System::Security::Permissions; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")]; -[assembly:AssemblyVersionAttribute("2.1.3.0")]; +[assembly:AssemblyVersionAttribute("2.1.4.0")]; [assembly:ComVisible(false)]; [assembly:CLSCompliantAttribute(true)]; diff --git a/src/LibCecSharp/CecSharpTypes.h b/src/LibCecSharp/CecSharpTypes.h index 89c9d3b..2de94bf 100644 --- a/src/LibCecSharp/CecSharpTypes.h +++ b/src/LibCecSharp/CecSharpTypes.h @@ -784,27 +784,31 @@ namespace CecSharp /// public enum class CecVendorId { - Toshiba = 0x000039, - Samsung = 0x0000F0, - Denon = 0x0005CD, - Loewe = 0x000982, - Onkyo = 0x0009B0, - Medion = 0x000CB8, - Akai = 0x0020C7, - AOC = 0x002467, - Panasonic = 0x008045, - Philips = 0x00903E, - Daewoo = 0x009053, - Yamaha = 0x00A0DE, - Grundig = 0x00D0D5, - Pioneer = 0x00E036, - LG = 0x00E091, - Sharp = 0x08001F, - Sony = 0x080046, - Broadcom = 0x18C086, - Vizio = 0x6B746D, - Benq = 0x8065E9, - Unknown = 0 + Toshiba = 0x000039, + Samsung = 0x0000F0, + Denon = 0x0005CD, + Marantz = 0x000678, + Loewe = 0x000982, + Onkyo = 0x0009B0, + Medion = 0x000CB8, + Toshiba2 = 0x000CE7, + PulseEight = 0x001582, + Akai = 0x0020C7, + AOC = 0x002467, + Panasonic = 0x008045, + Philips = 0x00903E, + Daewoo = 0x009053, + Yamaha = 0x00A0DE, + Grundig = 0x00D0D5, + Pioneer = 0x00E036, + LG = 0x00E091, + Sharp = 0x08001F, + Sony = 0x080046, + Broadcom = 0x18C086, + Vizio = 0x6B746D, + Benq = 0x8065E9, + HarmanKardon = 0x9C645E, + Unknown = 0 }; /// @@ -1225,9 +1229,13 @@ namespace CecSharp /// Version2_1_3 = 0x2103, /// + /// v2.1.4 + /// + Version2_1_4 = 0x2104, + /// /// The current version /// - CurrentVersion = 0x2103 + CurrentVersion = 0x2104 }; /// @@ -1343,10 +1351,14 @@ namespace CecSharp /// v2.1.3 /// Version2_1_3 = 0x2103, + /// + /// v2.1.4 + /// + Version2_1_4 = 0x2104, /// /// The current version /// - CurrentVersion = 0x2103 + CurrentVersion = 0x2104 }; /// @@ -2401,7 +2413,7 @@ namespace CecSharp CecParameterType newType = (CecParameterType)data.paramType; if (newType == CecParameterType::ParameterTypeString) { - System::String ^ newData = gcnew System::String((const char *)data.paramData, 0, 128); + System::String ^ newData = gcnew System::String(data.paramData ? (const char *)data.paramData : "", 0, 128); CecParameter ^ newParam = gcnew CecParameter(newType, newData); iReturn = m_callbacks->ReceiveAlert((CecAlert)alert, newParam); } diff --git a/src/LibCecTray/LibCECTray.csproj b/src/LibCecTray/LibCECTray.csproj index bfa1d9f..86cc6de 100644 --- a/src/LibCecTray/LibCECTray.csproj +++ b/src/LibCecTray/LibCECTray.csproj @@ -78,6 +78,7 @@ + diff --git a/src/LibCecTray/Properties/AssemblyInfo.cs b/src/LibCecTray/Properties/AssemblyInfo.cs index edb5400..8d6e8ed 100644 --- a/src/LibCecTray/Properties/AssemblyInfo.cs +++ b/src/LibCecTray/Properties/AssemblyInfo.cs @@ -31,5 +31,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("2.1.3.0")] -[assembly: AssemblyFileVersion("2.1.3.0")] +[assembly: AssemblyVersion("2.1.4.0")] +[assembly: AssemblyFileVersion("2.1.4.0")] diff --git a/src/LibCecTray/controller/Actions.cs b/src/LibCecTray/controller/Actions.cs index 13dc920..355f94e 100644 --- a/src/LibCecTray/controller/Actions.cs +++ b/src/LibCecTray/controller/Actions.cs @@ -94,7 +94,7 @@ namespace LibCECTray.controller _controller.CheckActiveDevices(); break; case UpdateEventType.ProcessCompleted: - if (!(_activeProcess is GetCurrentPhysicalAddress)) + if (!(_activeProcess is GetCurrentPhysicalAddress) && !SuppressUpdates) { _activeProcess = new GetCurrentPhysicalAddress(_controller.Lib); _activeProcess.EventHandler += ProcessEventHandler; @@ -123,6 +123,7 @@ namespace LibCECTray.controller break; case UpdateEventType.ExitApplication: + SuppressUpdates = true; _activeProcess = null; Application.Exit(); break; @@ -207,7 +208,7 @@ namespace LibCECTray.controller /// Activate the source at the given logical address. /// /// The logical address of the device to activate - public void ActivateSource(CecLogicalAddress address) + public void SetStreamPath(CecLogicalAddress address) { if (SuppressUpdates || _activeProcess != null) return; @@ -217,6 +218,16 @@ namespace LibCECTray.controller (new Thread(_activeProcess.Run)).Start(); } + public void ActivateSource() + { + if (SuppressUpdates || _activeProcess != null) return; + + _controller.SetControlsEnabled(false); + _activeProcess = new ActivateSource(_controller.Lib); + _activeProcess.EventHandler += ProcessEventHandler; + (new Thread(_activeProcess.Run)).Start(); + } + /// /// Send a standby command to the device at the given logical address /// diff --git a/src/LibCecTray/controller/actions/ActivateSource.cs b/src/LibCecTray/controller/actions/ActivateSource.cs new file mode 100644 index 0000000..44d2d50 --- /dev/null +++ b/src/LibCecTray/controller/actions/ActivateSource.cs @@ -0,0 +1,56 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +using CecSharp; +using LibCECTray.Properties; + +namespace LibCECTray.controller.actions +{ + class ActivateSource : UpdateProcess + { + public ActivateSource(LibCecSharp lib) + { + _lib = lib; + } + + public override void Process() + { + SendEvent(UpdateEventType.StatusText, Resources.action_activating_source); + SendEvent(UpdateEventType.ProgressBar, 50); + + var bResult = _lib.SetActiveSource(CecDeviceType.Reserved); + SendEvent(UpdateEventType.ProgressBar, 100); + } + + private readonly LibCecSharp _lib; + } +} diff --git a/src/LibCecTray/controller/actions/ConnectToDevice.cs b/src/LibCecTray/controller/actions/ConnectToDevice.cs index 6a832c5..e35906a 100644 --- a/src/LibCecTray/controller/actions/ConnectToDevice.cs +++ b/src/LibCecTray/controller/actions/ConnectToDevice.cs @@ -74,9 +74,12 @@ namespace LibCECTray.controller.actions SendEvent(UpdateEventType.StatusText, Resources.action_sending_power_on); _lib.PowerOnDevices(CecLogicalAddress.Broadcast); - SendEvent(UpdateEventType.StatusText, Resources.action_detecting_tv_vendor); - SendEvent(UpdateEventType.ProgressBar, 30); - SendEvent(UpdateEventType.TVVendorId, (int)_lib.GetDeviceVendorId(CecLogicalAddress.Tv)); + if (_lib.IsActiveDevice(CecLogicalAddress.Tv)) + { + SendEvent(UpdateEventType.StatusText, Resources.action_detecting_tv_vendor); + SendEvent(UpdateEventType.ProgressBar, 30); + SendEvent(UpdateEventType.TVVendorId, (int)_lib.GetDeviceVendorId(CecLogicalAddress.Tv)); + } SendEvent(UpdateEventType.ProgressBar, 50); SendEvent(UpdateEventType.StatusText, Resources.action_detecting_avr); @@ -91,7 +94,7 @@ namespace LibCECTray.controller.actions SendEvent(UpdateEventType.AVRVendorId, (int)_lib.GetDeviceVendorId(CecLogicalAddress.AudioSystem)); } - if (!_lib.GetDevicePowerStatus(CecLogicalAddress.Tv).Equals(CecPowerStatus.On)) + if (_lib.IsActiveDevice(CecLogicalAddress.Tv)&& !_lib.GetDevicePowerStatus(CecLogicalAddress.Tv).Equals(CecPowerStatus.On)) { SendEvent(UpdateEventType.ProgressBar, 70); SendEvent(UpdateEventType.StatusText, Resources.action_activating_source); @@ -110,7 +113,7 @@ namespace LibCECTray.controller.actions if (!_lib.IsActiveDevice(CecLogicalAddress.Tv)) { - MessageBox.Show(Resources.alert_tv_poll_failed, Resources.cec_alert, MessageBoxButtons.OK, MessageBoxIcon.Warning); + MessageBox.Show(Resources.alert_tv_poll_failed, Resources.cec_alert, MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); } SendEvent(UpdateEventType.ProgressBar, 100); diff --git a/src/LibCecTray/ui/CECTray.cs b/src/LibCecTray/ui/CECTray.cs index 6a04d62..0b8d17c 100644 --- a/src/LibCecTray/ui/CECTray.cs +++ b/src/LibCecTray/ui/CECTray.cs @@ -40,22 +40,11 @@ using LibCECTray.controller.applications; using LibCECTray.settings; using Microsoft.Win32; using System.Security.Permissions; +using System.Runtime.InteropServices; +using System.Threading; namespace LibCECTray.ui { - /// - /// The tab pages in this application - /// - internal enum ConfigTab - { - Configuration, - KeyConfiguration, - Tester, - Log, - WMC, - XBMC - } - /// /// Main LibCecTray GUI /// @@ -65,6 +54,13 @@ namespace LibCECTray.ui { Text = Resources.app_name; InitializeComponent(); + + _sstimer.Interval = 5000; + _sstimer.Tick += ScreensaverActiveCheck; + _sstimer.Enabled = false; + + _lastScreensaverActivated = DateTime.Now; + VisibleChanged += delegate { if (!Visible) @@ -72,28 +68,133 @@ namespace LibCECTray.ui else OnShow(); }; - SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(OnPowerModeChanged); + SystemEvents.SessionEnding += new SessionEndingEventHandler(OnSessionEnding); } public void OnSessionEnding(object sender, SessionEndingEventArgs e) { + Controller.CECActions.SuppressUpdates = true; Controller.Close(); } - public void OnPowerModeChanged(Object sender, PowerModeChangedEventArgs e) + #region Power state change window messages + private const int WM_POWERBROADCAST = 0x0218; + private const int WM_SYSCOMMAND = 0x0112; + + private const int PBT_APMSUSPEND = 0x0004; + private const int PBT_APMRESUMESUSPEND = 0x0007; + private const int PBT_APMRESUMECRITICAL = 0x0006; + private const int PBT_APMRESUMEAUTOMATIC = 0x0012; + private const int PBT_POWERSETTINGCHANGE = 0x8013; + + private static Guid GUID_SYSTEM_AWAYMODE = new Guid("98a7f580-01f7-48aa-9c0f-44352c29e5c0"); + + private const int SC_SCREENSAVE = 0xF140; + private const int SPI_GETSCREENSAVERRUNNING = 0x0072; + + [DllImport("user32.dll", SetLastError = true)] + static extern bool SystemParametersInfo(int action, int param, ref int retval, int updini); + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + internal struct POWERBROADCAST_SETTING + { + public Guid PowerSetting; + public uint DataLength; + public byte Data; + } + #endregion + + /// + /// Check for power state changes, and pass up when it's something we don't care about + /// + /// The incoming window message + protected override void WndProc(ref Message msg) { - switch (e.Mode) + if (msg.Msg == WM_SYSCOMMAND && (msg.WParam.ToInt32() & 0xfff0) == SC_SCREENSAVE) { - case PowerModes.Resume: - Controller.Initialise(); - break; - case PowerModes.Suspend: - Controller.Close(); - break; - case PowerModes.StatusChange: - break; + // there's no event for screensaver exit + if (!_sstimer.Enabled) + { + // guard against screensaver failing, and resulting in power up and down spam to the tv + TimeSpan diff = DateTime.Now - _lastScreensaverActivated; + if (diff.TotalSeconds > 60) + { + _sstimer.Enabled = true; + _lastScreensaverActivated = DateTime.Now; + Controller.CECActions.SendStandby(CecLogicalAddress.Broadcast); + } + } } + else if (msg.Msg == WM_POWERBROADCAST) + { + switch (msg.WParam.ToInt32()) + { + case PBT_APMSUSPEND: + OnSleep(); + return; + + case PBT_APMRESUMESUSPEND: + case PBT_APMRESUMECRITICAL: + case PBT_APMRESUMEAUTOMATIC: + OnWake(); + return; + + case PBT_POWERSETTINGCHANGE: + { + POWERBROADCAST_SETTING pwr = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(msg.LParam, typeof(POWERBROADCAST_SETTING)); + if (pwr.PowerSetting == GUID_SYSTEM_AWAYMODE && pwr.DataLength == Marshal.SizeOf(typeof(Int32))) + { + switch (pwr.Data) + { + case 0: + // do _not_ wake the pc when away mode is deactivated + //OnWake(); + //return; + case 1: + Controller.CECActions.SendStandby(CecLogicalAddress.Broadcast); + return; + default: + break; + } + } + } + break; + default: + break; + } + } + + // pass up when not handled + base.WndProc(ref msg); + } + + private void ScreensaverActiveCheck(object sender, EventArgs e) + { + if (!IsScreensaverActive()) + { + _sstimer.Enabled = false; + Controller.CECActions.ActivateSource(); + } + } + + private bool IsScreensaverActive() + { + int active = 1; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref active, 0); + return active == 1; + } + + private void OnWake() + { + Controller.Initialise(); + } + + private void OnSleep() + { + Controller.CECActions.SuppressUpdates = true; + AsyncDisconnect dc = new AsyncDisconnect(Controller); + (new Thread(dc.Process)).Start(); } public override sealed string Text @@ -112,7 +213,7 @@ namespace LibCECTray.ui Hide(); if (disposing) { - Controller.Close(); + OnSleep(); } if (disposing && (components != null)) { @@ -208,7 +309,7 @@ namespace LibCECTray.ui private void BActivateSourceClick(object sender, EventArgs e) { - Controller.CECActions.ActivateSource(GetTargetDevice()); + Controller.CECActions.SetStreamPath(GetTargetDevice()); } private void CbCommandDestinationSelectedIndexChanged(object sender, EventArgs e) @@ -521,6 +622,9 @@ namespace LibCECTray.ui { get { return GetSelectedTabName(tabPanel, tabPanel.TabPages); } } + + private System.Windows.Forms.Timer _sstimer = new System.Windows.Forms.Timer(); + private DateTime _lastScreensaverActivated; #endregion private void AddNewApplicationToolStripMenuItemClick(object sender, EventArgs e) @@ -529,4 +633,32 @@ namespace LibCECTray.ui Controller.DisplayDialog(appConfig, false); } } + + /// + /// The tab pages in this application + /// + internal enum ConfigTab + { + Configuration, + KeyConfiguration, + Tester, + Log, + WMC, + XBMC + } + + class AsyncDisconnect + { + public AsyncDisconnect(CECController controller) + { + _controller = controller; + } + + public void Process() + { + _controller.Close(); + } + + private CECController _controller; + } } diff --git a/src/LibCecTray/ui/DeviceInformation.cs b/src/LibCecTray/ui/DeviceInformation.cs index 581a417..1feed3b 100644 --- a/src/LibCecTray/ui/DeviceInformation.cs +++ b/src/LibCecTray/ui/DeviceInformation.cs @@ -72,7 +72,7 @@ namespace LibCECTray.ui { SetControlVisible(lInactiveSource, false); SetControlVisible(lActiveSource, true); - _controller.CECActions.ActivateSource(Address); + _controller.CECActions.SetStreamPath(Address); } private void LStandbyLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) diff --git a/src/lib/CECClient.cpp b/src/lib/CECClient.cpp index 4db7fcd..85df46d 100644 --- a/src/lib/CECClient.cpp +++ b/src/lib/CECClient.cpp @@ -973,10 +973,10 @@ void CCECClient::AddKey(bool bSendComboKey /* = false */) { key.duration = (unsigned int) (GetTimeMs() - m_buttontime); - cec_user_control_code comboKey(m_configuration.comboKey); - uint32_t iTimeoutMs(m_configuration.iComboKeyTimeoutMs); - - if (key.duration > iTimeoutMs || m_iCurrentButton != comboKey || bSendComboKey) + if (key.duration > m_configuration.iComboKeyTimeoutMs || + m_configuration.iComboKeyTimeoutMs == 0 || + m_iCurrentButton != m_configuration.comboKey || + bSendComboKey) { key.keycode = m_iCurrentButton; @@ -1009,7 +1009,7 @@ void CCECClient::AddKey(const cec_keypress &key) { CLockObject lock(m_mutex); - if (m_iCurrentButton == comboKey && key.duration == 0) + if (m_configuration.iComboKeyTimeoutMs > 0 && m_iCurrentButton == comboKey && key.duration == 0) { // stop + ok -> exit if (key.keycode == CEC_USER_CONTROL_CODE_SELECT) @@ -1070,7 +1070,7 @@ void CCECClient::CheckKeypressTimeout(void) m_configuration.iComboKeyTimeoutMs : CEC_DEFAULT_COMBO_TIMEOUT_MS); if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN && - ((m_iCurrentButton == comboKey && iNow - m_buttontime > iTimeoutMs) || + ((m_iCurrentButton == comboKey && iTimeoutMs > 0 && iNow - m_buttontime > iTimeoutMs) || (m_iCurrentButton != comboKey && iNow - m_buttontime > CEC_BUTTON_TIMEOUT))) { key.duration = (unsigned int) (iNow - m_buttontime); diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp index a44b81e..557b76d 100644 --- a/src/lib/CECProcessor.cpp +++ b/src/lib/CECProcessor.cpp @@ -57,6 +57,34 @@ using namespace PLATFORM; #define ToString(x) CCECTypeUtils::ToString(x) +CCECStandbyProtection::CCECStandbyProtection(CCECProcessor* processor) : + m_processor(processor) {} +CCECStandbyProtection::~CCECStandbyProtection(void) {} + +void* CCECStandbyProtection::Process(void) +{ + int64_t last = GetTimeMs(); + int64_t next; + while (!IsStopped()) + { + PLATFORM::CEvent::Sleep(1000); + + next = GetTimeMs(); + + // reset the connection if the clock changed + if (next < last || next - last > 10000) + { + libcec_parameter param; + param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN; + m_processor->GetLib()->Alert(CEC_ALERT_CONNECTION_LOST, param); + break; + } + + last = next; + } + return NULL; +} + CCECProcessor::CCECProcessor(CLibCEC *libcec) : m_bInitialised(false), m_communication(NULL), @@ -66,7 +94,8 @@ CCECProcessor::CCECProcessor(CLibCEC *libcec) : m_iLastTransmission(0), m_bMonitor(true), m_addrAllocator(NULL), - m_bStallCommunication(false) + m_bStallCommunication(false), + m_connCheck(NULL) { m_busDevices = new CCECDeviceMap(this); } @@ -105,11 +134,13 @@ void CCECProcessor::Close(void) SetCECInitialised(false); // stop the processor + DELETE_AND_NULL(m_connCheck); StopThread(-1); m_inBuffer.Broadcast(); StopThread(); // close the connection + CLockObject lock(m_mutex); DELETE_AND_NULL(m_communication); } @@ -215,6 +246,10 @@ void *CCECProcessor::Process(void) { m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started"); + if (!m_connCheck) + m_connCheck = new CCECStandbyProtection(this); + m_connCheck->CreateThread(); + cec_command command; command.Clear(); CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_INTERVAL); CTimeout tvPresentCheck(TV_PRESENT_CHECK_INTERVAL); @@ -245,12 +280,17 @@ void *CCECProcessor::Process(void) // check whether the TV is present and responding if (tvPresentCheck.TimeLeft() == 0) { - if (!m_busDevices->At(CECDEVICE_TV)->IsPresent()) + CCECClient *primary = GetPrimaryClient(); + // only check whether the tv responds to polls when a client is connected and not in monitoring mode + if (primary && primary->GetConfiguration()->bMonitorOnly != 1) { - libcec_parameter param; - param.paramType = CEC_PARAMETER_TYPE_STRING; - param.paramData = (void*)"TV does not respond to CEC polls"; - GetPrimaryClient()->Alert(CEC_ALERT_TV_POLL_FAILED, param); + if (!m_busDevices->At(CECDEVICE_TV)->IsPresent()) + { + libcec_parameter param; + param.paramType = CEC_PARAMETER_TYPE_STRING; + param.paramData = (void*)"TV does not respond to CEC polls"; + primary->Alert(CEC_ALERT_TV_POLL_FAILED, param); + } } tvPresentCheck.Init(TV_PRESENT_CHECK_INTERVAL); } @@ -400,6 +440,10 @@ bool CCECProcessor::Transmit(const cec_command &data, bool bIsReply) // reset the state of this message to 'unknown' cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN; + CLockObject lock(m_mutex); + if (!m_communication) + return false; + if (!m_communication->SupportsSourceLogicalAddress(transmitData.initiator)) { if (transmitData.initiator == CECDEVICE_UNREGISTERED && m_communication->SupportsSourceLogicalAddress(CECDEVICE_FREEUSE)) @@ -438,15 +482,14 @@ bool CCECProcessor::Transmit(const cec_command &data, bool bIsReply) } // wait until we finished allocating a new LA if it got lost + lock.Unlock(); while (m_bStallCommunication) Sleep(5); + lock.Lock(); - { - CLockObject lock(m_mutex); - m_iLastTransmission = GetTimeMs(); - // set the number of tries - iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1; - initiator->MarkHandlerReady(); - } + m_iLastTransmission = GetTimeMs(); + // set the number of tries + iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1; + initiator->MarkHandlerReady(); // and try to send the command while (bRetry && ++iTries < iMaxTries) @@ -854,7 +897,7 @@ bool CCECProcessor::RegisterClient(CCECClient *client) client->GetPrimaryDevice()->TransmitOSDName(CECDEVICE_TV, false); // request the power status of the TV - tv->RequestPowerStatus(sourceAddress, true); + tv->RequestPowerStatus(sourceAddress, true, true); return bReturn; } diff --git a/src/lib/CECProcessor.h b/src/lib/CECProcessor.h index 268be64..44f1fc1 100644 --- a/src/lib/CECProcessor.h +++ b/src/lib/CECProcessor.h @@ -52,6 +52,7 @@ namespace CEC class CCECTV; class CCECClient; class CCECProcessor; + class CCECStandbyProtection; class CCECAllocateLogicalAddress : public PLATFORM::CThread { @@ -177,5 +178,17 @@ namespace CEC bool m_bMonitor; CCECAllocateLogicalAddress* m_addrAllocator; bool m_bStallCommunication; + CCECStandbyProtection* m_connCheck; + }; + + class CCECStandbyProtection : public PLATFORM::CThread + { + public: + CCECStandbyProtection(CCECProcessor* processor); + virtual ~CCECStandbyProtection(void); + void* Process(void); + + private: + CCECProcessor* m_processor; }; }; diff --git a/src/lib/CECTypeUtils.h b/src/lib/CECTypeUtils.h index 506974b..dc4e5f1 100644 --- a/src/lib/CECTypeUtils.h +++ b/src/lib/CECTypeUtils.h @@ -517,6 +517,12 @@ namespace CEC return "Loewe"; case CEC_VENDOR_DENON: return "Denon"; + case CEC_VENDOR_MARANTZ: + return "Marantz"; + case CEC_VENDOR_HARMAN_KARDON: + return "Harman/Kardon"; + case CEC_VENDOR_PULSE_EIGHT: + return "Pulse Eight"; default: return "Unknown"; } @@ -580,6 +586,8 @@ namespace CEC return "2.1.2"; case CEC_CLIENT_VERSION_2_1_3: return "2.1.3"; + case CEC_CLIENT_VERSION_2_1_4: + return "2.1.4"; default: return "Unknown"; } @@ -643,6 +651,8 @@ namespace CEC return "2.1.2"; case CEC_SERVER_VERSION_2_1_3: return "2.1.3"; + case CEC_SERVER_VERSION_2_1_4: + return "2.1.4"; default: return "Unknown"; } diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 35166f2..9117d8e 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -26,7 +26,9 @@ libcec_la_SOURCES = CECProcessor.cpp \ implementations/SLCommandHandler.cpp \ implementations/VLCommandHandler.cpp \ implementations/RLCommandHandler.cpp \ - implementations/PHCommandHandler.cpp + implementations/PHCommandHandler.cpp \ + implementations/RHCommandHandler.cpp \ + implementations/AQCommandHandler.cpp ## server sockets, currently unused ##libcec_la_SOURCES += platform/posix/serversocket.cpp diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index f70c8ef..8505033 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -38,6 +38,7 @@ #include "lib/LibCEC.h" #include "lib/CECProcessor.h" #include "lib/CECTypeUtils.h" +#include "lib/platform/util/util.h" #include using namespace CEC; @@ -67,14 +68,14 @@ cec_datapacket CUSBCECAdapterCommands::RequestSetting(cec_adapter_messagecode ms CCECAdapterMessage params; CCECAdapterMessage *message = m_comm->SendCommand(msgCode, params); - if (message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED) + if (message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED) { retVal = message->response; retVal.Shift(2); // shift out start and msgcode retVal.size -= 1; // remove end } - delete message; + DELETE_AND_NULL(message); return retVal; } @@ -281,8 +282,8 @@ bool CUSBCECAdapterCommands::SetSettingAutoEnabled(bool enabled) CCECAdapterMessage params; params.PushEscaped(enabled ? 1 : 0); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_AUTO_ENABLED, params); - bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) { @@ -310,8 +311,8 @@ bool CUSBCECAdapterCommands::SetSettingDeviceType(cec_device_type type) CCECAdapterMessage params; params.PushEscaped((uint8_t)type); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_DEVICE_TYPE, params); - bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) { @@ -339,8 +340,8 @@ bool CUSBCECAdapterCommands::SetSettingDefaultLogicalAddress(cec_logical_address CCECAdapterMessage params; params.PushEscaped((uint8_t)address); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS, params); - bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) { @@ -369,8 +370,8 @@ bool CUSBCECAdapterCommands::SetSettingLogicalAddressMask(uint16_t iMask) params.PushEscaped(iMask >> 8); params.PushEscaped((uint8_t)iMask); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_LOGICAL_ADDRESS_MASK, params); - bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) { @@ -399,8 +400,8 @@ bool CUSBCECAdapterCommands::SetSettingPhysicalAddress(uint16_t iPhysicalAddress params.PushEscaped(iPhysicalAddress >> 8); params.PushEscaped((uint8_t)iPhysicalAddress); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_PHYSICAL_ADDRESS, params); - bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) { @@ -428,8 +429,8 @@ bool CUSBCECAdapterCommands::SetSettingCECVersion(cec_version version) CCECAdapterMessage params; params.PushEscaped((uint8_t)version); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_HDMI_VERSION, params); - bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) { @@ -454,8 +455,8 @@ bool CUSBCECAdapterCommands::SetSettingOSDName(const char *strOSDName) for (size_t iPtr = 0; iPtr < strlen(strOSDName); iPtr++) params.PushEscaped(strOSDName[iPtr]); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_OSD_NAME, params); - bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) snprintf(m_persistedConfiguration.strDeviceName, 13, "%s", strOSDName); @@ -475,8 +476,8 @@ bool CUSBCECAdapterCommands::WriteEEPROM(void) CCECAdapterMessage params; CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_WRITE_EEPROM, params); - bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bool bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) { @@ -574,8 +575,8 @@ bool CUSBCECAdapterCommands::PingAdapter(void) CCECAdapterMessage params; CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_PING, params); - bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bool bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); return bReturn; } @@ -587,8 +588,8 @@ bool CUSBCECAdapterCommands::SetAckMask(uint16_t iMask) params.PushEscaped(iMask >> 8); params.PushEscaped((uint8_t)iMask); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_ACK_MASK, params); - bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bool bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); return bReturn; } @@ -601,7 +602,7 @@ void CUSBCECAdapterCommands::SetActiveSource(bool bSetTo, bool bClientUnregister CCECAdapterMessage params; params.PushEscaped(bSetTo ? 1 : 0); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_ACTIVE_SOURCE, params); - delete message; + DELETE_AND_NULL(message); } } @@ -611,8 +612,8 @@ bool CUSBCECAdapterCommands::StartBootloader(void) CCECAdapterMessage params; CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_START_BOOTLOADER, params); - bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bool bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); return bReturn; } @@ -622,8 +623,8 @@ bool CUSBCECAdapterCommands::SetLineTimeout(uint8_t iTimeout) CCECAdapterMessage params; params.PushEscaped(iTimeout); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_TRANSMIT_IDLETIME, params); - bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bool bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); return bReturn; } @@ -640,8 +641,8 @@ bool CUSBCECAdapterCommands::SetControlledMode(bool controlled) CCECAdapterMessage params; params.PushEscaped(controlled ? 1 : 0); CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_CONTROLLED, params); - bool bReturn = message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; - delete message; + bool bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + DELETE_AND_NULL(message); if (bReturn) { diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index 07255a7..0373595 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -304,6 +304,7 @@ bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg) m_lastPollDestination = msg.Destination(); if (msg.Destination() < CECDEVICE_BROADCAST) { + CLockObject waitingLock(m_waitingMutex); if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM()) { if (m_callback) @@ -328,7 +329,7 @@ void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest) /* mark as waiting for an ack from the destination */ if (dest < CECDEVICE_BROADCAST) { - CLockObject lock(m_mutex); + CLockObject waitingLock(m_waitingMutex); m_bWaitingForAck[dest] = true; } } @@ -382,8 +383,7 @@ bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message) { LIB_CEC->AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str(), m_port->GetError().c_str()); message->state = ADAPTER_MESSAGE_STATE_ERROR; - // this will trigger an alert in the reader thread - m_port->Close(); + // let the higher level close the port return false; } @@ -416,7 +416,7 @@ bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize if (m_port->GetErrorNumber()) { LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str()); - m_port->Close(); + // let the higher level close the port return false; } } @@ -729,6 +729,11 @@ void *CAdapterPingThread::Process(void) /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */ m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection."); m_com->StopThread(false); + + libcec_parameter param; + param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN; + m_com->LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param); + break; } } diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.h b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.h index ba5d7ca..bac9437 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.h +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterCommunication.h @@ -182,6 +182,7 @@ namespace CEC CUSBCECAdapterCommands * m_commands; /**< commands that can be sent to the adapter */ CCECAdapterMessageQueue * m_adapterMessageQueue; /**< the incoming and outgoing message queue */ cec_logical_addresses m_logicalAddresses; /**< the logical address list that this instance is using */ + PLATFORM::CMutex m_waitingMutex; }; class CAdapterEepromWriteThread : public PLATFORM::CThread diff --git a/src/lib/adapter/Pulse-Eight/USBCECAdapterDetection.cpp b/src/lib/adapter/Pulse-Eight/USBCECAdapterDetection.cpp index 35ba386..58bf4a5 100644 --- a/src/lib/adapter/Pulse-Eight/USBCECAdapterDetection.cpp +++ b/src/lib/adapter/Pulse-Eight/USBCECAdapterDetection.cpp @@ -61,6 +61,8 @@ extern "C" { #include } #elif defined(__FreeBSD__) +#include +#include #include #include #endif @@ -434,22 +436,77 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter_descriptor *deviceList } #elif defined(__FreeBSD__) char devicePath[PATH_MAX + 1]; + char infos[512]; + char sysctlname[32]; + char ttyname[8]; + char *pos; + size_t infos_size = sizeof(infos); int i; - for (i = 0; i < 8; ++i) + for (i = 0; ; ++i) { - (void)snprintf(devicePath, sizeof(devicePath), "/dev/ttyU%d", i); - if (strDevicePath && strcmp(devicePath, strDevicePath) != 0) + unsigned int iVendor, iProduct; + memset(infos, 0, sizeof(infos)); + (void)snprintf(sysctlname, sizeof(sysctlname), + "dev.umodem.%d.%%pnpinfo", i); + if (sysctlbyname(sysctlname, infos, &infos_size, + NULL, 0) != 0) + break; + pos = strstr(infos, "vendor="); + if (pos == NULL) continue; - if (!access(devicePath, 0)) - { - snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", devicePath); - snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", devicePath); - deviceList[iFound].iVendorId = CEC_VID; - deviceList[iFound].iProductId = CEC_VID; - deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type - iFound++; + sscanf(pos, "vendor=%x ", &iVendor); + + pos = strstr(infos, "product="); + if (pos == NULL) + continue; + sscanf(pos, "product=%x ", &iProduct); + + if (iVendor != CEC_VID || (iProduct != CEC_PID && iProduct != CEC_PID2)) + continue; + + pos = strstr(infos, "ttyname="); + if (pos == NULL) + continue; + sscanf(pos, "ttyname=%s ", ttyname); + + (void)snprintf(devicePath, sizeof(devicePath), + "/dev/tty%s", ttyname); + + if (strDevicePath) { + char currStrDevicePath[512]; + int port = 0; + int devaddr = 0; + memset(currStrDevicePath, 0, sizeof(currStrDevicePath)); + memset(infos, 0, sizeof(infos)); + (void)snprintf(sysctlname, sizeof(sysctlname), + "dev.umodem.%d.%%location", i); + if (sysctlbyname(sysctlname, infos, &infos_size, + NULL, 0) != 0) + break; + + pos = strstr(infos, "port="); + if (pos == NULL) + continue; + sscanf(pos, "port=%d ", &port); + + pos = strstr(infos, "devaddr="); + if (pos == NULL) + continue; + sscanf(pos, "devaddr=%d ", &devaddr); + + (void)snprintf(currStrDevicePath, sizeof(currStrDevicePath), + "/dev/ugen%d.%d", port, devaddr); + + if (strcmp(currStrDevicePath, strDevicePath) != 0) + continue; } + snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", devicePath); + snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", devicePath); + deviceList[iFound].iVendorId = iVendor; + deviceList[iFound].iProductId = iProduct; + deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type + iFound++; } #else //silence "unused" warnings diff --git a/src/lib/devices/CECBusDevice.cpp b/src/lib/devices/CECBusDevice.cpp index a2cfbee..489ded2 100644 --- a/src/lib/devices/CECBusDevice.cpp +++ b/src/lib/devices/CECBusDevice.cpp @@ -41,6 +41,8 @@ #include "lib/implementations/VLCommandHandler.h" #include "lib/implementations/PHCommandHandler.h" #include "lib/implementations/RLCommandHandler.h" +#include "lib/implementations/RHCommandHandler.h" +#include "lib/implementations/AQCommandHandler.h" #include "lib/LibCEC.h" #include "lib/CECTypeUtils.h" #include "lib/platform/util/timeutils.h" @@ -59,6 +61,78 @@ using namespace PLATFORM; #define LIB_CEC m_processor->GetLib() #define ToString(p) CCECTypeUtils::ToString(p) +CResponse::CResponse(cec_opcode opcode) : + m_opcode(opcode) +{ +} + +CResponse::~CResponse(void) +{ + Broadcast(); +} + +bool CResponse::Wait(uint32_t iTimeout) +{ + return m_event.Wait(iTimeout); +} + +void CResponse::Broadcast(void) +{ + m_event.Broadcast(); +} + +CWaitForResponse::CWaitForResponse(void) +{ +} + +CWaitForResponse::~CWaitForResponse(void) +{ + Clear(); +} + +void CWaitForResponse::Clear() +{ + PLATFORM::CLockObject lock(m_mutex); + for (std::map::iterator it = m_waitingFor.begin(); it != m_waitingFor.end(); it++) + { + it->second->Broadcast(); + delete it->second; + } + m_waitingFor.clear(); +} + +bool CWaitForResponse::Wait(cec_opcode opcode, uint32_t iTimeout) +{ + CResponse *response = GetEvent(opcode); + return response ? response->Wait(iTimeout) : false; +} + +void CWaitForResponse::Received(cec_opcode opcode) +{ + CResponse *response = GetEvent(opcode); + if (response) + response->Broadcast(); +} + +CResponse* CWaitForResponse::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; + } +} + CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogicalAddress, uint16_t iPhysicalAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */) : m_type (CEC_DEVICE_TYPE_RESERVED), m_iPhysicalAddress (iPhysicalAddress), @@ -141,6 +215,12 @@ bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */) case CEC_VENDOR_TOSHIBA2: m_handler = new CRLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending); break; + case CEC_VENDOR_ONKYO: + m_handler = new CRHCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending); + break; + case CEC_VENDOR_SHARP: + m_handler = new CAQCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending); + break; default: m_handler = new CCECCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending); break; @@ -605,7 +685,7 @@ cec_power_status CCECBusDevice::GetPowerStatus(const cec_logical_address initiat if (bRequestUpdate) { CheckVendorIdRequested(initiator); - RequestPowerStatus(initiator); + RequestPowerStatus(initiator, bUpdate); } CLockObject lock(m_mutex); @@ -642,7 +722,7 @@ bool CCECBusDevice::ImageViewOnSent(void) return m_bImageViewOnSent; } -bool CCECBusDevice::RequestPowerStatus(const cec_logical_address initiator, bool bWaitForResponse /* = true */) +bool CCECBusDevice::RequestPowerStatus(const cec_logical_address initiator, bool bUpdate, bool bWaitForResponse /* = true */) { bool bReturn(false); @@ -650,7 +730,7 @@ bool CCECBusDevice::RequestPowerStatus(const cec_logical_address initiator, bool !IsUnsupportedFeature(CEC_OPCODE_GIVE_DEVICE_POWER_STATUS)) { MarkBusy(); - bReturn = m_handler->TransmitRequestPowerStatus(initiator, m_iLogicalAddress, bWaitForResponse); + bReturn = m_handler->TransmitRequestPowerStatus(initiator, m_iLogicalAddress, bUpdate, bWaitForResponse); if (!bReturn) SetPowerStatus(CEC_POWER_STATUS_UNKNOWN); MarkReady(); @@ -809,7 +889,7 @@ void CCECBusDevice::SetDeviceStatus(const cec_bus_device_status newStatus, cec_v if (m_deviceStatus != newStatus) LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'handled by libCEC'", GetLogicalAddressName(), m_iLogicalAddress); SetPowerStatus (CEC_POWER_STATUS_ON); - SetVendorId (CEC_VENDOR_UNKNOWN); + SetVendorId (CEC_VENDOR_PULSE_EIGHT); SetMenuState (CEC_MENU_STATE_ACTIVATED); SetCecVersion (libCECSpecVersion); SetStreamPath (CEC_INVALID_PHYSICAL_ADDRESS); @@ -1002,14 +1082,12 @@ void CCECBusDevice::MarkAsActiveSource(void) if ((*it)->GetLogicalAddress() != m_iLogicalAddress) (*it)->MarkAsInactiveSource(); - if (bWasActivated) - { - if (IsHandledByLibCEC()) - m_processor->SetActiveSource(true, false); - CCECClient *client = GetClient(); - if (client) - client->SourceActivated(m_iLogicalAddress); - } + if (bWasActivated && IsHandledByLibCEC()) + m_processor->SetActiveSource(true, false); + + CCECClient *client = GetClient(); + if (client) + client->SourceActivated(m_iLogicalAddress); } void CCECBusDevice::MarkAsInactiveSource(bool bClientUnregistered /* = false */) @@ -1139,14 +1217,10 @@ void CCECBusDevice::SetActiveRoute(uint16_t iRoute) return; CCECBusDevice* newRoute = m_processor->GetDeviceByPhysicalAddress(iRoute, true); - if (newRoute) + if (newRoute && newRoute->IsHandledByLibCEC()) { // we were made the active source, send notification - if (newRoute->IsHandledByLibCEC()) - newRoute->ActivateSource(); - // another device was made active - else - newRoute->MarkAsActiveSource(); + newRoute->ActivateSource(); } } diff --git a/src/lib/devices/CECBusDevice.h b/src/lib/devices/CECBusDevice.h index e159aea..d227c03 100644 --- a/src/lib/devices/CECBusDevice.h +++ b/src/lib/devices/CECBusDevice.h @@ -50,22 +50,11 @@ namespace CEC 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(); - } + CResponse(cec_opcode opcode); + ~CResponse(void); + + bool Wait(uint32_t iTimeout); + void Broadcast(void); private: cec_opcode m_opcode; @@ -75,52 +64,15 @@ namespace CEC 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(); - } + CWaitForResponse(void); + ~CWaitForResponse(void); + + void Clear(); + bool Wait(cec_opcode opcode, uint32_t iTimeout = CEC_DEFAULT_TRANSMIT_WAIT); + void Received(cec_opcode opcode); 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; - } - } + CResponse *GetEvent(cec_opcode opcode); PLATFORM::CMutex m_mutex; std::map m_waitingFor; @@ -194,7 +146,7 @@ namespace CEC virtual void SetPowerStatus(const cec_power_status powerStatus); virtual void OnImageViewOnSent(bool bSentByLibCEC); virtual bool ImageViewOnSent(void); - virtual bool RequestPowerStatus(const cec_logical_address initiator, bool bWaitForResponse = true); + virtual bool RequestPowerStatus(const cec_logical_address initiator, bool bUpdate, bool bWaitForResponse = true); virtual bool TransmitPowerState(const cec_logical_address destination, bool bIsReply); virtual cec_vendor_id GetCurrentVendorId(void); diff --git a/src/lib/implementations/ANCommandHandler.cpp b/src/lib/implementations/ANCommandHandler.cpp index 16831a9..7e0e9b9 100644 --- a/src/lib/implementations/ANCommandHandler.cpp +++ b/src/lib/implementations/ANCommandHandler.cpp @@ -76,11 +76,6 @@ int CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command) return COMMAND_HANDLED; } -int CANCommandHandler::HandleVendorRemoteButtonUp(const cec_command &command) -{ - return HandleUserControlRelease(command); -} - bool CANCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination) { if (iDestination == CECDEVICE_AUDIOSYSTEM) @@ -92,3 +87,32 @@ bool CANCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_ return CCECCommandHandler::PowerOn(iInitiator, iDestination); } + +int CANCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command) +{ + if (!m_processor->IsHandledByLibCEC(command.destination)) + return CEC_ABORT_REASON_INVALID_OPERAND; + + // samsung's vendor id + if (command.parameters[0] == 0x00 && command.parameters[1] == 0x00 && command.parameters[2] == 0xf0) + { + // unknown vendor command sent to devices + if (command.parameters[3] == 0x23) + { + cec_command response; + cec_command::Format(response, command.destination, command.initiator, CEC_OPCODE_VENDOR_COMMAND_WITH_ID); + + // samsung vendor id + response.parameters.PushBack(0x00); response.parameters.PushBack(0x00); response.parameters.PushBack(0xf0); + + // XXX see bugzid 2164. reply sent back by audio systems, we might have to send something different + response.parameters.PushBack(0x24); + response.parameters.PushBack(0x00); + response.parameters.PushBack(0x80); + + Transmit(response, false, true); + return COMMAND_HANDLED; + } + } + return CEC_ABORT_REASON_INVALID_OPERAND; +} diff --git a/src/lib/implementations/ANCommandHandler.h b/src/lib/implementations/ANCommandHandler.h index bc4b921..595170a 100644 --- a/src/lib/implementations/ANCommandHandler.h +++ b/src/lib/implementations/ANCommandHandler.h @@ -46,7 +46,7 @@ namespace CEC virtual ~CANCommandHandler(void) {}; int HandleVendorRemoteButtonDown(const cec_command &command); - int HandleVendorRemoteButtonUp(const cec_command &command); + int HandleDeviceVendorCommandWithId(const cec_command &command); protected: bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); diff --git a/src/lib/implementations/AQCommandHandler.cpp b/src/lib/implementations/AQCommandHandler.cpp new file mode 100644 index 0000000..93bba24 --- /dev/null +++ b/src/lib/implementations/AQCommandHandler.cpp @@ -0,0 +1,89 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" +#include "AQCommandHandler.h" + +#include "lib/devices/CECBusDevice.h" +#include "lib/CECProcessor.h" +#include "lib/LibCEC.h" +#include "lib/CECClient.h" + +using namespace CEC; +using namespace PLATFORM; + +#define LIB_CEC m_busDevice->GetProcessor()->GetLib() +#define ToString(p) LIB_CEC->ToString(p) + +CAQCommandHandler::CAQCommandHandler(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_powerOnCheck(NULL) +{ + m_vendorId = CEC_VENDOR_SHARP; +} + +CAQCommandHandler::~CAQCommandHandler(void) +{ + delete m_powerOnCheck; +} + +bool CAQCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination) +{ + bool bCheck(false); + bool bRetval(false); + if (m_busDevice->GetCurrentPowerStatus() != CEC_POWER_STATUS_ON && (!m_powerOnCheck || !m_powerOnCheck->IsRunning())) + bCheck = true; + bRetval = CCECCommandHandler::PowerOn(iInitiator, iDestination); + + if (bRetval && bCheck) + { + if (!m_powerOnCheck) + m_powerOnCheck = new CAQPowerStatusCheck(this, iInitiator, iDestination); + if (m_powerOnCheck) + m_powerOnCheck->CreateThread(); + } + + return bRetval; +} + +void* CAQPowerStatusCheck::Process(void) +{ + // sleep for 2 seconds and query the power status + Sleep(2000); + if (m_handler->m_busDevice->GetProcessor()->GetDevice(m_iDestination)->GetPowerStatus(m_iInitiator, true) == CEC_POWER_STATUS_STANDBY) + m_handler->m_busDevice->GetProcessor()->GetLib()->AddLog(CEC_LOG_WARNING, "AQUOS LINK 'auto power on' is disabled, which prevents the TV from being powered on. To correct this, press the menu button on your remote, go to 'link operation' -> 'AQUOS LINK setup' -> 'Auto power on' and set it to 'On'"); + return NULL; +} diff --git a/src/lib/implementations/AQCommandHandler.h b/src/lib/implementations/AQCommandHandler.h new file mode 100644 index 0000000..6833b41 --- /dev/null +++ b/src/lib/implementations/AQCommandHandler.h @@ -0,0 +1,74 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "CECCommandHandler.h" +#include "lib/platform/threads/threads.h" + +namespace CEC +{ + class CAQPowerStatusCheck; + + class CAQCommandHandler : public CCECCommandHandler + { + friend class CAQPowerStatusCheck; + public: + CAQCommandHandler(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 ~CAQCommandHandler(void); + + protected: + virtual bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); + + private: + CAQPowerStatusCheck* m_powerOnCheck; + }; + + class CAQPowerStatusCheck : public PLATFORM::CThread + { + public: + CAQPowerStatusCheck(CAQCommandHandler* handler, cec_logical_address iInitiator, cec_logical_address iDestination) : + m_handler(handler), + m_iInitiator(iInitiator), + m_iDestination(iDestination) {} + virtual ~CAQPowerStatusCheck(void) {} + + private: + void* Process(void); + CAQCommandHandler* m_handler; + cec_logical_address m_iInitiator; + cec_logical_address m_iDestination; + }; +}; diff --git a/src/lib/implementations/CECCommandHandler.cpp b/src/lib/implementations/CECCommandHandler.cpp index 2859d5a..29d1ffb 100644 --- a/src/lib/implementations/CECCommandHandler.cpp +++ b/src/lib/implementations/CECCommandHandler.cpp @@ -257,6 +257,14 @@ int CCECCommandHandler::HandleDeviceVendorCommandWithId(const cec_command & UNUS int CCECCommandHandler::HandleDeviceVendorId(const cec_command &command) { SetVendorId(command); + + if (command.initiator == CECDEVICE_TV) + { + CCECBusDevice* primary = m_processor->GetPrimaryDevice(); + if (primary) + primary->TransmitVendorID(CECDEVICE_BROADCAST, false, false); + } + return COMMAND_HANDLED; } @@ -455,6 +463,13 @@ int CCECCommandHandler::HandleReportPhysicalAddress(const cec_command &command) { uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]); SetPhysicalAddress(command.initiator, iNewAddress); + + if (command.initiator == CECDEVICE_TV) + { + CCECBusDevice* primary = m_processor->GetPrimaryDevice(); + if (primary) + primary->TransmitPhysicalAddress(false); + } return COMMAND_HANDLED; } return CEC_ABORT_REASON_INVALID_OPERAND; @@ -580,7 +595,10 @@ int CCECCommandHandler::HandleSetStreamPath(const cec_command &command) if (device->IsHandledByLibCEC() && !device->IsActiveSource()) device->ActivateSource(); else + { device->MarkAsActiveSource(); + device->TransmitActiveSource(true); + } return COMMAND_HANDLED; } } @@ -910,12 +928,12 @@ bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_addres return Transmit(command, !bWaitForResponse, false); } -bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */) +bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bUpdate, bool bWaitForResponse /* = true */) { if (iDestination == CECDEVICE_TV) { int64_t now(GetTimeMs()); - if (now - m_iPowerStatusRequested < REQUEST_POWER_STATUS_TIMEOUT) + if (!bUpdate && now - m_iPowerStatusRequested < REQUEST_POWER_STATUS_TIMEOUT) return true; m_iPowerStatusRequested = now; } @@ -1207,7 +1225,7 @@ bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = f bool bTvPresent = (tv && tv->GetStatus() == CEC_DEVICE_STATUS_PRESENT); bool bActiveSourceFailed(false); if (bTvPresent) - bActiveSourceFailed = !m_busDevice->TransmitImageViewOn(); + bActiveSourceFailed = !tv->PowerOn(m_busDevice->GetLogicalAddress()); else LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV not present, not sending 'image view on'"); @@ -1230,6 +1248,14 @@ bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = f if (playbackDevice && SendDeckStatusUpdateOnActiveSource()) bActiveSourceFailed = !playbackDevice->TransmitDeckStatus(CECDEVICE_TV, false); } + + // update system audio mode for audiosystem devices + if (bTvPresent && !bActiveSourceFailed) + { + CCECAudioSystem* audioDevice = m_busDevice->AsAudioSystem(); + if (audioDevice) + bActiveSourceFailed = !audioDevice->TransmitSetSystemAudioMode(CECDEVICE_TV, false); + } } // retry later diff --git a/src/lib/implementations/CECCommandHandler.h b/src/lib/implementations/CECCommandHandler.h index 9902a08..aeace02 100644 --- a/src/lib/implementations/CECCommandHandler.h +++ b/src/lib/implementations/CECCommandHandler.h @@ -58,7 +58,7 @@ namespace CEC virtual bool HandleCommand(const cec_command &command); virtual cec_vendor_id GetVendorId(void) { return m_vendorId; }; virtual void SetVendorId(cec_vendor_id vendorId) { m_vendorId = vendorId; } - static bool HasSpecificHandler(cec_vendor_id vendorId) { return vendorId == CEC_VENDOR_LG || vendorId == CEC_VENDOR_SAMSUNG || vendorId == CEC_VENDOR_PANASONIC || vendorId == CEC_VENDOR_PHILIPS;} + static bool HasSpecificHandler(cec_vendor_id vendorId) { return vendorId == CEC_VENDOR_LG || vendorId == CEC_VENDOR_SAMSUNG || vendorId == CEC_VENDOR_PANASONIC || vendorId == CEC_VENDOR_PHILIPS || vendorId == CEC_VENDOR_SHARP;} virtual bool InitHandler(void) { return true; } virtual bool ActivateSource(bool bTransmitDelayedCommandsOnly = false); @@ -73,7 +73,7 @@ namespace CEC virtual bool TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true); virtual bool TransmitRequestAudioStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true); virtual bool TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true); - virtual bool TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true); + virtual bool TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bUpdate, bool bWaitForResponse = true); virtual bool TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse = true); virtual bool TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress, bool bIsReply); virtual bool TransmitCECVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_version cecVersion, bool bIsReply); @@ -138,7 +138,7 @@ namespace CEC virtual int HandleUserControlRelease(const cec_command &command); virtual int HandleVendorCommand(const cec_command &command); virtual int HandleVendorRemoteButtonDown(const cec_command& command); - virtual int HandleVendorRemoteButtonUp(const cec_command & UNUSED(command)) { return COMMAND_HANDLED; } + virtual int HandleVendorRemoteButtonUp(const cec_command& command) { return HandleUserControlRelease(command); } virtual void UnhandledCommand(const cec_command &command, const cec_abort_reason reason); virtual void RequestEmailFromCustomer(const cec_command& command); diff --git a/src/lib/implementations/PHCommandHandler.cpp b/src/lib/implementations/PHCommandHandler.cpp index d7e4848..edb084c 100644 --- a/src/lib/implementations/PHCommandHandler.cpp +++ b/src/lib/implementations/PHCommandHandler.cpp @@ -152,9 +152,8 @@ int CPHCommandHandler::HandleDeviceVendorId(const cec_command& command) return CCECCommandHandler::HandleDeviceVendorId(command); } -bool CPHCommandHandler::TransmitVendorID(const cec_logical_address iInitiator, const cec_logical_address iDestination, uint64_t UNUSED(iVendorId), bool UNUSED(bIsReply)) +bool CPHCommandHandler::TransmitVendorID(const cec_logical_address iInitiator, const cec_logical_address iDestination, uint64_t UNUSED(iVendorId), bool bIsReply) { - LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): vendor id feature abort", ToString(iInitiator), iInitiator, ToString(iDestination), iDestination); - m_processor->TransmitAbort(iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID); - return true; + // XXX hack around the hack in CPHCommandHandler::InitHandler + return CCECCommandHandler::TransmitVendorID(iInitiator, iDestination, CEC_VENDOR_PULSE_EIGHT, bIsReply); } diff --git a/src/lib/implementations/PHCommandHandler.h b/src/lib/implementations/PHCommandHandler.h index f6f3fcc..5e30047 100644 --- a/src/lib/implementations/PHCommandHandler.h +++ b/src/lib/implementations/PHCommandHandler.h @@ -71,7 +71,6 @@ namespace CEC virtual int HandleUserControlRelease(const cec_command& command); virtual bool TransmitVendorID(const cec_logical_address iInitiator, const cec_logical_address iDestination, uint64_t iVendorId, bool bIsReply); virtual int HandleDeviceVendorId(const cec_command& command); - virtual int HandleVendorRemoteButtonUp(const cec_command & command) { return HandleUserControlRelease(command); } uint8_t m_iLastKeyCode; CImageViewOnCheck* m_imageViewOnCheck; }; diff --git a/src/lib/implementations/RHCommandHandler.cpp b/src/lib/implementations/RHCommandHandler.cpp new file mode 100644 index 0000000..66c9065 --- /dev/null +++ b/src/lib/implementations/RHCommandHandler.cpp @@ -0,0 +1,65 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" +#include "RHCommandHandler.h" + +#include "lib/devices/CECBusDevice.h" +#include "lib/CECProcessor.h" +#include "lib/LibCEC.h" +#include "lib/CECClient.h" + +using namespace CEC; + +#define LIB_CEC m_busDevice->GetProcessor()->GetLib() +#define ToString(p) LIB_CEC->ToString(p) + +CRHCommandHandler::CRHCommandHandler(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_ONKYO; +} + +int CRHCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command) +{ + // onkyo's vendor id + if (command.parameters[0] == 0x00 && command.parameters[1] == 0x09 && command.parameters[2] == 0xb0) + { + // ignore unknown vendor commands sent by onkyo devices, bugzid: 2559 + } + return CEC_ABORT_REASON_INVALID_OPERAND; + +} diff --git a/src/lib/implementations/RHCommandHandler.h b/src/lib/implementations/RHCommandHandler.h new file mode 100644 index 0000000..1992262 --- /dev/null +++ b/src/lib/implementations/RHCommandHandler.h @@ -0,0 +1,51 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is an original work, containing original code. + * + * libCEC(R) is a trademark of Pulse-Eight Limited. + * + * This program is dual-licensed; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Alternatively, you can license this library under a commercial license, + * please contact Pulse-Eight Licensing for more information. + * + * For more information contact: + * Pulse-Eight Licensing + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "CECCommandHandler.h" + +namespace CEC +{ + class CRHCommandHandler : public CCECCommandHandler + { + public: + CRHCommandHandler(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 ~CRHCommandHandler(void) {}; + + protected: + int HandleDeviceVendorCommandWithId(const cec_command &command); + }; +}; diff --git a/src/lib/implementations/SLCommandHandler.cpp b/src/lib/implementations/SLCommandHandler.cpp index ec12b02..ff47e1e 100644 --- a/src/lib/implementations/SLCommandHandler.cpp +++ b/src/lib/implementations/SLCommandHandler.cpp @@ -42,19 +42,18 @@ using namespace CEC; using namespace PLATFORM; -#define SL_COMMAND_UNKNOWN_01 0x01 -#define SL_COMMAND_UNKNOWN_02 0x02 - #define SL_COMMAND_TYPE_HDDRECORDER_DISC 0x01 #define SL_COMMAND_TYPE_VCR 0x02 #define SL_COMMAND_TYPE_DVDPLAYER 0x03 #define SL_COMMAND_TYPE_HDDRECORDER_DISC2 0x04 #define SL_COMMAND_TYPE_HDDRECORDER 0x05 -#define SL_COMMAND_REQUEST_POWER_STATUS 0xa0 +#define SL_COMMAND_INIT 0x01 +#define SL_COMMAND_ACK_INIT 0x02 #define SL_COMMAND_POWER_ON 0x03 #define SL_COMMAND_CONNECT_REQUEST 0x04 #define SL_COMMAND_SET_DEVICE_MODE 0x05 +#define SL_COMMAND_REQUEST_POWER_STATUS 0xa0 #define LIB_CEC m_busDevice->GetProcessor()->GetLib() #define ToString(p) LIB_CEC->ToString(p) @@ -65,8 +64,7 @@ CSLCommandHandler::CSLCommandHandler(CCECBusDevice *busDevice, int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */, int64_t iActiveSourcePending /* = 0 */) : CCECCommandHandler(busDevice, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending), - m_bSLEnabled(false), - m_bActiveSourceSent(false) + m_bSLEnabled(false) { m_vendorId = CEC_VENDOR_LG; @@ -98,74 +96,20 @@ bool CSLCommandHandler::InitHandler(void) primary->SetVendorId(CEC_VENDOR_LG); primary->ReplaceHandler(false); } - - if (m_busDevice->GetLogicalAddress() == CECDEVICE_TV) - { - /* start as 'in transition standby->on' */ - primary->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); - primary->TransmitPowerState(CECDEVICE_TV, false); - - /* send the vendor id */ - primary->TransmitVendorID(CECDEVICE_BROADCAST, false, false); - } } return true; } -int 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]); - CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iAddress); - if (device) - device->MarkAsActiveSource(); - - { - CLockObject lock(m_SLMutex); - m_bActiveSourceSent = false; - } - - return COMMAND_HANDLED; - } - - return CEC_ABORT_REASON_INVALID_OPERAND; - -} - -int CSLCommandHandler::HandleDeviceVendorId(const cec_command &command) -{ - SetVendorId(command); - - if (!SLInitialised() && command.initiator == CECDEVICE_TV) - { - CCECBusDevice *destination = m_processor->GetDevice(command.destination); - if (destination && (destination->GetLogicalAddress() == CECDEVICE_BROADCAST || destination->IsHandledByLibCEC())) - { - cec_logical_address initiator = destination->GetLogicalAddress(); - if (initiator == CECDEVICE_BROADCAST) - initiator = m_processor->GetPrimaryDevice()->GetLogicalAddress(); - - cec_command response; - cec_command::Format(response, initiator, command.initiator, CEC_OPCODE_FEATURE_ABORT); - Transmit(response, false, true); - return COMMAND_HANDLED; - } - } - - return CCECCommandHandler::HandleDeviceVendorId(command); -} - int CSLCommandHandler::HandleVendorCommand(const cec_command &command) { if (!m_processor->IsHandledByLibCEC(command.destination)) return true; if (command.parameters.size == 1 && - command.parameters[0] == SL_COMMAND_UNKNOWN_01) + command.parameters[0] == SL_COMMAND_INIT) { - HandleVendorCommand01(command); + HandleVendorCommandSLInit(command); return COMMAND_HANDLED; } else if (command.parameters.size == 2 && @@ -190,21 +134,26 @@ int CSLCommandHandler::HandleVendorCommand(const cec_command &command) return CCECCommandHandler::HandleVendorCommand(command); } -void CSLCommandHandler::HandleVendorCommand01(const cec_command &command) +void CSLCommandHandler::HandleVendorCommandSLInit(const cec_command &command) { - m_processor->GetPrimaryDevice()->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); - TransmitVendorCommand0205(command.destination, command.initiator); - CCECBusDevice* dev = m_processor->GetDevice(command.destination); - if (dev && dev->IsHandledByLibCEC() && dev->IsActiveSource()) - dev->TransmitActiveSource(false); + if (dev && dev->IsHandledByLibCEC()) + { + if (!dev->IsActiveSource()) + { + dev->SetPowerStatus(CEC_POWER_STATUS_STANDBY); + dev->TransmitPowerState(command.initiator, true); + } + + TransmitVendorCommandSLAckInit(command.destination, command.initiator); + } } -void CSLCommandHandler::TransmitVendorCommand0205(const cec_logical_address iSource, const cec_logical_address iDestination) +void CSLCommandHandler::TransmitVendorCommandSLAckInit(const cec_logical_address iSource, const cec_logical_address iDestination) { cec_command response; cec_command::Format(response, iSource, iDestination, CEC_OPCODE_VENDOR_COMMAND); - response.PushBack(SL_COMMAND_UNKNOWN_02); + response.PushBack(SL_COMMAND_ACK_INIT); response.PushBack(SL_COMMAND_TYPE_HDDRECORDER); Transmit(response, false, true); @@ -228,10 +177,9 @@ void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command) device->SetPowerStatus(CEC_POWER_STATUS_ON); device->TransmitPowerState(command.initiator, false); device->TransmitPhysicalAddress(false); - { - CLockObject lock(m_SLMutex); - m_bActiveSourceSent = false; - } + + if (device->IsActiveSource()) + ActivateSource(); } } void CSLCommandHandler::HandleVendorCommandPowerOnStatus(const cec_command &command) @@ -253,7 +201,15 @@ void CSLCommandHandler::HandleVendorCommandSLConnect(const cec_command &command) SetSLInitialised(); TransmitVendorCommandSetDeviceMode(command.destination, command.initiator, CEC_DEVICE_TYPE_RECORDING_DEVICE); - ActivateSource(); + + if (m_processor->IsActiveSource(command.destination) && m_processor->IsHandledByLibCEC(command.destination)) + { + CCECBusDevice* dev = m_processor->GetDevice(command.destination); + CCECPlaybackDevice* pb = dev->AsPlaybackDevice(); + if (pb) + pb->TransmitDeckStatus(command.initiator, true); + dev->TransmitPowerState(command.initiator, true); + } } void CSLCommandHandler::TransmitVendorCommandSetDeviceMode(const cec_logical_address iSource, const cec_logical_address iDestination, const cec_device_type type) @@ -279,8 +235,7 @@ int CSLCommandHandler::HandleGiveDeckStatus(const cec_command &command) if (command.parameters[0] == CEC_STATUS_REQUEST_ON) { device->TransmitDeckStatus(command.initiator, true); - if (!ActiveSourceSent()) - ActivateSource(); + ActivateSource(); return COMMAND_HANDLED; } else if (command.parameters[0] == CEC_STATUS_REQUEST_ONCE) @@ -304,20 +259,10 @@ int CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command) } else { - if (!ActiveSourceSent()) - { - device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); - device->TransmitPowerState(command.initiator, true); - ActivateSource(); - } - else if (m_resetPowerState.IsSet() && m_resetPowerState.TimeLeft() > 0) + if (m_resetPowerState.IsSet() && m_resetPowerState.TimeLeft() > 0) { /* TODO assume that we've bugged out. the return button no longer works after this */ LIB_CEC->AddLog(CEC_LOG_WARNING, "FIXME: LG seems to have bugged out. resetting to 'in transition standby to on'. the return button will not work"); - { - CLockObject lock(m_SLMutex); - m_bActiveSourceSent = false; - } device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); device->TransmitPowerState(command.initiator, true); device->SetPowerStatus(CEC_POWER_STATUS_ON); @@ -340,22 +285,25 @@ int CSLCommandHandler::HandleRequestActiveSource(const cec_command &command) { if (m_processor->CECInitialised()) { - if (ActiveSourceSent()) - LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %i requests active source, ignored", (uint8_t) command.initiator); - else - ActivateSource(); - return COMMAND_HANDLED; + if (!SLInitialised()) + TransmitVendorCommandSLAckInit(m_processor->GetPrimaryDevice()->GetLogicalAddress(), command.initiator); + CCECCommandHandler::HandleRequestActiveSource(command); } return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND; } int CSLCommandHandler::HandleFeatureAbort(const cec_command &command) { - if (command.parameters.size == 0 && m_processor->GetPrimaryDevice()->GetCurrentPowerStatus() == CEC_POWER_STATUS_ON && !SLInitialised() && + CCECBusDevice* primary = m_processor->GetPrimaryDevice(); + if (command.parameters.size == 0 && primary->GetLogicalAddress() != CECDEVICE_UNKNOWN && + primary->GetCurrentPowerStatus() == CEC_POWER_STATUS_ON && !SLInitialised() && command.initiator == CECDEVICE_TV) { - m_processor->GetPrimaryDevice()->TransmitPowerState(command.initiator, false); - m_processor->GetPrimaryDevice()->TransmitVendorID(CECDEVICE_BROADCAST, false, false); + if (!SLInitialised() && m_processor->IsActiveSource(command.destination)) + { + TransmitVendorCommandSLAckInit(command.destination, command.initiator); + return COMMAND_HANDLED; + } } return CCECCommandHandler::HandleFeatureAbort(command); @@ -363,11 +311,7 @@ int CSLCommandHandler::HandleFeatureAbort(const cec_command &command) int CSLCommandHandler::HandleStandby(const cec_command &command) { - if (command.initiator == CECDEVICE_TV) - { - CLockObject lock(m_SLMutex); - m_bActiveSourceSent = false; - } + ResetSLState(); return CCECCommandHandler::HandleStandby(command); } @@ -377,7 +321,6 @@ void CSLCommandHandler::ResetSLState(void) LIB_CEC->AddLog(CEC_LOG_NOTICE, "resetting SL initialised state"); CLockObject lock(m_SLMutex); m_bSLEnabled = false; - m_bActiveSourceSent = false; m_processor->GetPrimaryDevice()->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); } @@ -394,12 +337,6 @@ bool CSLCommandHandler::SLInitialised(void) return m_bSLEnabled; } -bool CSLCommandHandler::ActiveSourceSent(void) -{ - CLockObject lock(m_SLMutex); - return m_bActiveSourceSent; -} - bool CSLCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination) { if (iDestination != CECDEVICE_TV) @@ -419,9 +356,73 @@ bool CSLCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_ return CCECCommandHandler::PowerOn(iInitiator, iDestination); } -void CSLCommandHandler::VendorPreActivateSourceHook(void) +bool CSLCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = false */) { - CCECPlaybackDevice *device = m_busDevice->AsPlaybackDevice(); - if (device) - device->SetDeckStatus(!device->IsActiveSource() ? CEC_DECK_INFO_OTHER_STATUS : CEC_DECK_INFO_OTHER_STATUS_LG); + if (m_busDevice->IsActiveSource() && + m_busDevice->IsHandledByLibCEC()) + { + { + CLockObject lock(m_mutex); + // check if we need to send a delayed source switch + if (bTransmitDelayedCommandsOnly) + { + if (m_iActiveSourcePending == 0 || GetTimeMs() < m_iActiveSourcePending) + return false; + +#ifdef CEC_DEBUGGING + LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command"); +#endif + } + } + + CCECPlaybackDevice *device = m_busDevice->AsPlaybackDevice(); + if (device) + device->SetDeckStatus(!device->IsActiveSource() ? CEC_DECK_INFO_OTHER_STATUS : CEC_DECK_INFO_OTHER_STATUS_LG); + + // power on the TV + CCECBusDevice* tv = m_processor->GetDevice(CECDEVICE_TV); + bool bTvPresent = (tv && tv->GetStatus() == CEC_DEVICE_STATUS_PRESENT); + bool bActiveSourceFailed(false); + if (bTvPresent) + { + bActiveSourceFailed = !device->TransmitImageViewOn(); + } + else + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV not present, not sending 'image view on'"); + } + + // 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"); + + // switch sources (if allowed) + if (!bActiveSourceFailed && bSourceSwitchAllowed) + { + bActiveSourceFailed = !m_busDevice->TransmitActiveSource(false); + } + + // retry later + if (bActiveSourceFailed || !bSourceSwitchAllowed) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to make '%s' the active source. will retry later", m_busDevice->GetLogicalAddressName()); + int64_t now(GetTimeMs()); + CLockObject lock(m_mutex); + if (m_iActiveSourcePending == 0 || m_iActiveSourcePending < now) + m_iActiveSourcePending = now + (int64_t)CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS; + return false; + } + else + { + CLockObject lock(m_mutex); + // clear previous pending active source command + m_iActiveSourcePending = 0; + } + + // mark the handler as initialised + CLockObject lock(m_mutex); + m_bHandlerInited = true; + } + return true; } diff --git a/src/lib/implementations/SLCommandHandler.h b/src/lib/implementations/SLCommandHandler.h index ac8cb5c..166bac0 100644 --- a/src/lib/implementations/SLCommandHandler.h +++ b/src/lib/implementations/SLCommandHandler.h @@ -46,14 +46,13 @@ namespace CEC virtual ~CSLCommandHandler(void) {}; bool InitHandler(void); + bool ActivateSource(bool bTransmitDelayedCommandsOnly = false); protected: - 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); + void HandleVendorCommandSLInit(const cec_command &command); + void TransmitVendorCommandSLAckInit(const cec_logical_address iSource, const cec_logical_address iDestination); void HandleVendorCommandPowerOn(const cec_command &command); void HandleVendorCommandPowerOnStatus(const cec_command &command); @@ -68,17 +67,12 @@ namespace CEC 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), bool UNUSED(bIsReply)) { return true; } bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination); - int HandleVendorRemoteButtonUp(const cec_command& command) { return HandleUserControlRelease(command); } void ResetSLState(void); bool SLInitialised(void); void SetSLInitialised(void); - bool ActiveSourceSent(void); - - void VendorPreActivateSourceHook(void); bool m_bSLEnabled; - bool m_bActiveSourceSent; PLATFORM::CTimeout m_resetPowerState; PLATFORM::CMutex m_SLMutex; }; diff --git a/src/lib/implementations/VLCommandHandler.cpp b/src/lib/implementations/VLCommandHandler.cpp index 44feabc..0e74a06 100644 --- a/src/lib/implementations/VLCommandHandler.cpp +++ b/src/lib/implementations/VLCommandHandler.cpp @@ -106,23 +106,28 @@ int CVLCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &comman command.parameters[2] != 0x45) return CEC_ABORT_REASON_INVALID_OPERAND; - // XXX this is also sent when the TV is powered off -#if 0 if (command.initiator == CECDEVICE_TV && command.parameters.At(3) == VL_UNKNOWN1) { - // set the power up event time + if (command.parameters.size >= 5 && command.parameters.At(4) == 0x05) { - CLockObject lock(m_mutex); - if (m_iPowerUpEventReceived == 0) - m_iPowerUpEventReceived = GetTimeMs(); + // 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); + + CCECBusDevice* dev = m_processor->GetPrimaryDevice(); + if (dev && dev->IsActiveSource()) + dev->TransmitActiveSource(false); + + return COMMAND_HANDLED; } - // mark the TV as powered on - m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_ON); } - else -#endif - if (command.initiator == CECDEVICE_TV && + else if (command.initiator == CECDEVICE_TV && command.destination == CECDEVICE_BROADCAST && command.parameters.At(3) == VL_POWER_CHANGE) { @@ -248,10 +253,15 @@ int CVLCommandHandler::HandleVendorCommand(const cec_command &command) if (command.parameters.size == 3 && command.parameters[0] == 0x10 && command.parameters[1] == 0x01 && - command.parameters[2] == 0x05 && m_processor->IsHandledByLibCEC(command.destination)) { + // XXX i've seen 0x05 and 0x03 as third param. these probably indicate different types of TVs/capabilities + // when we feature abort this, then the TV will try the same thing with a vendor command with id SendVendorCommandCapabilities(m_processor->GetLogicalAddress(), command.initiator); + + CCECBusDevice* dev = m_processor->GetDevice(command.destination); + if (dev && dev->IsActiveSource()) + dev->ActivateSource(500); return COMMAND_HANDLED; } @@ -261,7 +271,7 @@ int CVLCommandHandler::HandleVendorCommand(const cec_command &command) bool CVLCommandHandler::SourceSwitchAllowed(void) { if (!PowerUpEventReceived()) - TransmitRequestPowerStatus(m_processor->GetPrimaryDevice()->GetLogicalAddress(), CECDEVICE_TV, false); + TransmitRequestPowerStatus(m_processor->GetPrimaryDevice()->GetLogicalAddress(), CECDEVICE_TV, false, false); return PowerUpEventReceived(); } diff --git a/src/testclient/main.cpp b/src/testclient/main.cpp index b6c2caf..03ca91d 100644 --- a/src/testclient/main.cpp +++ b/src/testclient/main.cpp @@ -43,6 +43,7 @@ #include "../lib/platform/os.h" #include "../lib/implementations/CECCommandHandler.h" #include "../lib/platform/util/StdString.h" +#include "../lib/platform/threads/threads.h" using namespace CEC; using namespace std; @@ -52,6 +53,8 @@ using namespace PLATFORM; #include "../../include/cecloader.h" +static void PrintToStdOut(const char *strFormat, ...); + ICECCallbacks g_callbacks; libcec_configuration g_config; int g_cecLogLevel(-1); @@ -63,8 +66,38 @@ bool g_bSingleCommand(false); bool g_bExit(false); bool g_bHardExit(false); CMutex g_outputMutex; +ICECAdapter* g_parser; + +class CReconnect : public PLATFORM::CThread +{ +public: + static CReconnect& Get(void) + { + static CReconnect _instance; + return _instance; + } + + virtual ~CReconnect(void) {} + + void* Process(void) + { + if (g_parser) + { + g_parser->Close(); + if (!g_parser->Open(g_strPort.c_str())) + { + PrintToStdOut("Failed to reconnect\n"); + g_bExit = true; + } + } + return NULL; + } + +private: + CReconnect(void) {} +}; -inline void PrintToStdOut(const char *strFormat, ...) +static void PrintToStdOut(const char *strFormat, ...) { CStdString strLog; @@ -186,8 +219,11 @@ int CecAlert(void *UNUSED(cbParam), const libcec_alert type, const libcec_parame switch (type) { case CEC_ALERT_CONNECTION_LOST: - PrintToStdOut("Connection lost - exiting\n"); - g_bExit = true; + if (!CReconnect::Get().IsRunning()) + { + PrintToStdOut("Connection lost - trying to reconnect\n"); + CReconnect::Get().CreateThread(false); + } break; default: break; @@ -1190,8 +1226,8 @@ int main (int argc, char *argv[]) g_config.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE); } - ICECAdapter *parser = LibCecInitialise(&g_config); - if (!parser) + g_parser = LibCecInitialise(&g_config); + if (!g_parser) { #ifdef __WINDOWS__ cout << "Cannot load libcec.dll" << endl; @@ -1199,19 +1235,19 @@ int main (int argc, char *argv[]) cout << "Cannot load libcec.so" << endl; #endif - if (parser) - UnloadLibCec(parser); + if (g_parser) + UnloadLibCec(g_parser); return 1; } // init video on targets that need this - parser->InitVideoStandalone(); + g_parser->InitVideoStandalone(); if (!g_bSingleCommand) { CStdString strLog; - strLog.Format("CEC Parser created - libCEC version %s", parser->ToString((cec_server_version)g_config.serverVersion)); + strLog.Format("CEC Parser created - libCEC version %s", g_parser->ToString((cec_server_version)g_config.serverVersion)); cout << strLog.c_str() << endl; //make stdin non-blocking @@ -1227,13 +1263,13 @@ int main (int argc, char *argv[]) if (!g_bSingleCommand) cout << "no serial port given. trying autodetect: "; cec_adapter devices[10]; - uint8_t iDevicesFound = parser->FindAdapters(devices, 10, NULL); + uint8_t iDevicesFound = g_parser->FindAdapters(devices, 10, NULL); if (iDevicesFound <= 0) { if (g_bSingleCommand) cout << "autodetect "; cout << "FAILED" << endl; - UnloadLibCec(parser); + UnloadLibCec(g_parser); return 1; } else @@ -1249,10 +1285,10 @@ int main (int argc, char *argv[]) PrintToStdOut("opening a connection to the CEC adapter..."); - if (!parser->Open(g_strPort.c_str())) + if (!g_parser->Open(g_strPort.c_str())) { PrintToStdOut("unable to open the device on port %s", g_strPort.c_str()); - UnloadLibCec(parser); + UnloadLibCec(g_parser); return 1; } @@ -1265,7 +1301,7 @@ int main (int argc, char *argv[]) getline(cin, input); cin.clear(); - if (ProcessConsoleCommand(parser, input) && !g_bSingleCommand && !g_bExit && !g_bHardExit) + if (ProcessConsoleCommand(g_parser, input) && !g_bSingleCommand && !g_bExit && !g_bHardExit) { if (!input.empty()) PrintToStdOut("waiting for input"); @@ -1277,8 +1313,8 @@ int main (int argc, char *argv[]) CEvent::Sleep(50); } - parser->Close(); - UnloadLibCec(parser); + g_parser->Close(); + UnloadLibCec(g_parser); if (g_logOutput.is_open()) g_logOutput.close();