From f57cba645f0f22c109355ef55c78cf328adc2d93 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Thu, 9 Feb 2012 14:07:26 +0100 Subject: [PATCH] cec: added cec-config, a libCEC configuration wizard. WIP. currently only detects the physical address. --- Makefile.am | 2 +- project/cec-config.rc | Bin 0 -> 3232 bytes project/cec-config.vcxproj | 183 ++++++++++++++ project/cec-config.vcxproj.filters | 28 +++ project/libcec.sln | 13 + src/cec-config/Makefile.am | 5 + src/cec-config/cec-config.cpp | 372 +++++++++++++++++++++++++++++ 7 files changed, 602 insertions(+), 1 deletion(-) create mode 100644 project/cec-config.rc create mode 100644 project/cec-config.vcxproj create mode 100644 project/cec-config.vcxproj.filters create mode 100644 src/cec-config/Makefile.am create mode 100644 src/cec-config/cec-config.cpp diff --git a/Makefile.am b/Makefile.am index c68f68b..471d2bd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = src/lib src/testclient \ No newline at end of file +SUBDIRS = src/lib src/testclient src/cec-config \ No newline at end of file diff --git a/project/cec-config.rc b/project/cec-config.rc new file mode 100644 index 0000000000000000000000000000000000000000..37783a24eb812f097bcfeda768938560fa6216f5 GIT binary patch literal 3232 zcmds)+in^$5QgWvQs2QMH?11N5_kbf7KB0rDhIVvBt!|DA`yr9#~YG za)Bz#Yh#b+@Xx{K_pg$a#K>ov$wXGNlr=q&4QoTz*0Pnc%=vXCpJ+xNWGV~#iuDcS zJ!>mQJ-1~xVQ%=P+-C9w&4s*|cStGOQRipu1e~1mHBytV^rSBXxt5l6WW@It=^eQd zpEY+{IhE&XUULQ8$sjC z7iJo7w^773nCt1}j`~p7^|WEu!k>PI#^B3ysnRQSFQV9ZAiMo-wD8L;3aml$>6JQ`)X9y_cYOF_b>hLBEi< zqm5DO%QrIHkM^DHc)y3nb+AcNIWGCpUq_z~cq$?VByVhuSx}Q2czcdLEj%9J_jGpT zb932{=IViU*sAiKS(;OPCNDePt{aE=PxD_fD4_RJ9;t|9bo@zNjrlcY{oL+$sh*)v ziqOxn>4&YtZNB%^(0o1eB{?*%;h(I;VMhE(;s9jpQbb>$Zm8s%M}gJJpv{stf0I7x z%yfE`v93z5(D&h;Z>M@&@1PZ<-cngi^)8xVNz#G~)}HBqgJ&E12P;8+_=NZLZVNr+ ztk~T#@&U;oLGTj0%CurMyJW3MKc>$+kxsJ+3l{^5Oft@ng-I8AOCocNjhh|hj~_#6 zd2f%T_;z$LwY!q82tyED{ZoZW*MUCVv<9q+Sbak!vr_B4XDdS4#)`X5LS17` + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1} + Win32Proj + cec-config + cec-config + + + + Application + true + MultiByte + + + Application + true + MultiByte + + + Application + false + MultiByte + + + Application + false + MultiByte + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\build\ + cec-config + + + $(SolutionDir)..\build\ + cec-config.x64 + + + $(SolutionDir)..\build\ + cec-config + + + $(SolutionDir)..\build\ + cec-config.x64 + + + + + + Level4 + Disabled + _USE_32BIT_TIME_T;_DEBUG;_CRT_SECURE_NO_WARNINGS;_WINSOCKAPI_;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions) + true + + + $(SolutiontDir)..\include;%(AdditionalIncludeDirectories) + + + Console + true + + + + + + + + + Level4 + Disabled + _WIN64;_DEBUG;_CRT_SECURE_NO_WARNINGS;_WINSOCKAPI_;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions) + true + + + $(SolutiontDir)..\include;%(AdditionalIncludeDirectories) + + + Console + true + + + + + + + + + Level4 + + + Full + false + true + _USE_32BIT_TIME_T;_CRT_SECURE_NO_WARNINGS;_WINSOCKAPI_;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions) + true + + + $(SolutionDir)..\include;%(AdditionalIncludeDirectories) + Speed + + + Console + false + true + true + + + + + + + Level4 + + + Full + true + _WIN64;_CRT_SECURE_NO_WARNINGS;_WINSOCKAPI_;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions) + true + + + $(SolutionDir)..\include;%(AdditionalIncludeDirectories) + Speed + + + Console + false + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/project/cec-config.vcxproj.filters b/project/cec-config.vcxproj.filters new file mode 100644 index 0000000..0d3f647 --- /dev/null +++ b/project/cec-config.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + {cc0a01c1-e1e7-4af8-9656-fa9463140613} + + + {f08c0a80-22d9-4bdd-90de-b9cf5fa88f99} + + + + + exports + + + exports + + + platform + + + + + + + + + \ No newline at end of file diff --git a/project/libcec.sln b/project/libcec.sln index 83d6d89..dbb86c9 100644 --- a/project/libcec.sln +++ b/project/libcec.sln @@ -8,6 +8,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testclient", "testclient.vc {C04B0FB1-667D-4F1C-BDAE-A07CDFFAAAA0} = {C04B0FB1-667D-4F1C-BDAE-A07CDFFAAAA0} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cec-config", "cec-config.vcxproj", "{00EE7081-9EEE-485C-B7CE-699A7BCA40C1}" + ProjectSection(ProjectDependencies) = postProject + {C04B0FB1-667D-4F1C-BDAE-A07CDFFAAAA0} = {C04B0FB1-667D-4F1C-BDAE-A07CDFFAAAA0} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -32,6 +37,14 @@ Global {F01222BF-6B3D-43BD-B254-434031CB9887}.Release|Win32.Build.0 = Release|Win32 {F01222BF-6B3D-43BD-B254-434031CB9887}.Release|x64.ActiveCfg = Release|x64 {F01222BF-6B3D-43BD-B254-434031CB9887}.Release|x64.Build.0 = Release|x64 + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1}.Debug|Win32.ActiveCfg = Debug|Win32 + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1}.Debug|Win32.Build.0 = Debug|Win32 + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1}.Debug|x64.ActiveCfg = Debug|x64 + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1}.Debug|x64.Build.0 = Debug|x64 + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1}.Release|Win32.ActiveCfg = Release|Win32 + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1}.Release|Win32.Build.0 = Release|Win32 + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1}.Release|x64.ActiveCfg = Release|x64 + {00EE7081-9EEE-485C-B7CE-699A7BCA40C1}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/cec-config/Makefile.am b/src/cec-config/Makefile.am new file mode 100644 index 0000000..33650c5 --- /dev/null +++ b/src/cec-config/Makefile.am @@ -0,0 +1,5 @@ +bin_PROGRAMS = cec-config +cec_client_SOURCES = cec-config.cpp + +cec_client_CPPFLAGS = -I@abs_top_srcdir@/include +cec_client_LDFLAGS = @LIBS_DL@ \ No newline at end of file diff --git a/src/cec-config/cec-config.cpp b/src/cec-config/cec-config.cpp new file mode 100644 index 0000000..c36bb6a --- /dev/null +++ b/src/cec-config/cec-config.cpp @@ -0,0 +1,372 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC(R) is Copyright (C) 2011-2012 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 "../../include/cec.h" + +#include +#include +#include +#include +#include +#include +#include "../lib/platform/threads/mutex.h" +#include "../lib/platform/util/timeutils.h" +#include "../lib/implementations/CECCommandHandler.h" + +using namespace CEC; +using namespace std; +using namespace PLATFORM; + +#include + +CMutex g_outputMutex; + +CMutex g_responseMutex; +CCondition g_responseCondtion; +cec_opcode g_lastCommand = CEC_OPCODE_NONE; + +CMutex g_keyMutex; +CCondition g_keyCondtion; +cec_user_control_code g_lastKey = CEC_USER_CONTROL_CODE_UNKNOWN; + +ICECCallbacks g_callbacks; +ICECAdapter * g_parser; +cec_logical_address g_primaryAddress; + +inline void PrintToStdOut(const char *strFormat, ...) +{ + CStdString strLog; + + va_list argList; + va_start(argList, strFormat); + strLog.FormatV(strFormat, argList); + va_end(argList); + + CLockObject lock(g_outputMutex); + cout << strLog << endl; +} + +//get the first word (separated by whitespace) from string data and place that in word +//then remove that word from string data +bool GetWord(string& data, string& word) +{ + stringstream datastream(data); + string end; + + datastream >> word; + if (datastream.fail()) + { + data.clear(); + return false; + } + + size_t pos = data.find(word) + word.length(); + + if (pos >= data.length()) + { + data.clear(); + return true; + } + + data = data.substr(pos); + + datastream.clear(); + datastream.str(data); + + datastream >> end; + if (datastream.fail()) + data.clear(); + + return true; +} + +int CecLogMessage(void *UNUSED(cbParam), const cec_log_message &message) +{ + switch (message.level) + { + case CEC_LOG_ERROR: + PrintToStdOut("ERROR:\t%s", message.message); + break; + case CEC_LOG_WARNING: + PrintToStdOut("ERROR:\t%s", message.message); + break; + default: + break; + } + + return 0; +} + +int CecKeyPress(void *UNUSED(cbParam), const cec_keypress &key) +{ + CLockObject lock(g_keyMutex); + g_lastKey = key.keycode; + g_keyCondtion.Signal(); + return 0; +} + +int CecCommand(void *UNUSED(cbParam), const cec_command &command) +{ + CLockObject lock(g_responseMutex); + g_lastCommand = command.opcode; + g_responseCondtion.Signal(); + return 0; +} + +void EnableCallbacks(ICECAdapter *adapter) +{ + g_callbacks.CBCecLogMessage = &CecLogMessage; + g_callbacks.CBCecKeyPress = &CecKeyPress; + g_callbacks.CBCecCommand = &CecCommand; + adapter->EnableCallbacks(NULL, &g_callbacks); +} + +ICECAdapter *CreateParser(cec_device_type_list typeList) +{ + ICECAdapter *parser = LibCecInit("ButtonConfig", typeList); + if (!parser) + { + #ifdef __WINDOWS__ + PrintToStdOut("Cannot load libcec.dll"); + #else + PrintToStdOut("Cannot load libcec.so"); + #endif + return NULL; + } + + PrintToStdOut("CEC Parser created - libcec version %d.%d", parser->GetLibVersionMajor(), parser->GetLibVersionMinor()); + + return parser; +} + +bool ProcessConsoleCommand(string &input) +{ + if (!input.empty()) + { + string command; + if (GetWord(input, command)) + { + if (command == "q" || command == "quit") + return false; + } + } + return true; +} + +bool OpenConnection(cec_device_type type = CEC_DEVICE_TYPE_RECORDING_DEVICE) +{ + cec_device_type_list types; + types.clear(); + types.add(type); + + g_parser = CreateParser(types); + if (!g_parser) + return false; + + CStdString strPort; + cec_adapter devices[10]; + uint8_t iDevicesFound = g_parser->FindAdapters(devices, 10, NULL); + if (iDevicesFound <= 0) + { + PrintToStdOut("autodetect FAILED"); + UnloadLibCec(g_parser); + return false; + } + else + { + strPort = devices[0].comm; + } + + EnableCallbacks(g_parser); + + // start with HDMI1 on the TV + g_parser->SetHDMIPort(CECDEVICE_TV, 1); + PrintToStdOut("opening a connection to the CEC adapter..."); + + if (!g_parser->Open(strPort.c_str())) + { + PrintToStdOut("unable to open the device on port %s", strPort.c_str()); + UnloadLibCec(g_parser); + return false; + } + + cec_logical_addresses addr = g_parser->GetLogicalAddresses(); + g_primaryAddress = addr.primary; + + PrintToStdOut("cec device opened. using logical address %X", g_primaryAddress); + return true; +} + +int8_t FindPhysicalAddressPortNumber(void) +{ + PrintToStdOut("Enter the HDMI port number to which you connected your CEC adapter, followed by . Only port 1, 2, 3 or 4 are supported. Anything else will cancel this wizard."); + string input; + getline(cin, input); + cin.clear(); + if (input.empty() || (input != "1" && input != "2" && input != "3" && input != "4")) + return -1; + return (int8_t)atoi(input.c_str()); +} + +cec_logical_address FindPhysicalAddressBaseDevice(void) +{ + PrintToStdOut("Press 1 of your CEC adapter is connected to your TV or\npress 2 if it's connected to an AVR, followed by . Anything else will cancel this wizard."); + + string input; + getline(cin, input); + cin.clear(); + if (input.empty() || (input != "1" && input != "2")) + { + PrintToStdOut("Exiting..."); + return CECDEVICE_UNKNOWN; + } + return (input == "2") ? + CECDEVICE_AUDIOSYSTEM : + CECDEVICE_TV; +} + +uint16_t FindPhysicalAddress(void) +{ + PrintToStdOut("=== Physical Address Configuration ===\n"); + uint16_t iAddress(0xFFFF); + + PrintToStdOut("Do you want to let libCEC try to autodetect the address (y/n)?"); + string input; + getline(cin, input); + cin.clear(); + if (input == "y" || input == "Y") + { + cec_logical_address baseDevice = FindPhysicalAddressBaseDevice(); + if (baseDevice == CECDEVICE_UNKNOWN) + return iAddress; + + int8_t iPortNumber = FindPhysicalAddressPortNumber(); + if (iPortNumber == -1) + return iAddress; + + PrintToStdOut("Trying to detect the physical address..."); + if (!g_parser->SetHDMIPort(baseDevice, iPortNumber)) + PrintToStdOut("Failed. Please enter the address manually, or restart this wizard and use different settings."); + else + { + iAddress = g_parser->GetDevicePhysicalAddress(g_primaryAddress); + if (iAddress == 0 || iAddress == 0xFFFF) + PrintToStdOut("Failed. Please enter the address manually, or restart this wizard and use different settings."); + } + } + + if (iAddress == 0 || iAddress == 0xFFFF) + { + PrintToStdOut("Please enter the physical address (0000 - FFFF), followed by ."); + getline(cin, input); + cin.clear(); + + int iAddressTmp; + if (sscanf(input.c_str(), "%x", &iAddressTmp) == 1) + { + if (iAddressTmp < 0 || iAddressTmp > 0xFFFF) + iAddressTmp = 0xFFFF; + iAddress = (uint16_t)iAddressTmp; + } + } + + return iAddress; +} + +bool PowerOnTV(uint64_t iTimeout = 60000, unsigned iTries = 2) +{ + cec_power_status currentTvPower(CEC_POWER_STATUS_UNKNOWN); + uint64_t iNow = GetTimeMs(); + uint64_t iTarget = iNow + iTimeout; + unsigned iTry(0); + + while (currentTvPower != CEC_POWER_STATUS_ON && iTarget > iNow && iTry < iTries) + { + currentTvPower = g_parser->GetDevicePowerStatus(CECDEVICE_TV); + if (currentTvPower != CEC_POWER_STATUS_ON) + { + PrintToStdOut("Sending 'power on' command to the TV"); + g_parser->PowerOnDevices(CECDEVICE_TV); + while (iTarget > iNow) + { + CLockObject lock(g_responseMutex); + g_responseCondtion.Wait(g_responseMutex, (uint32_t)((iTarget - iNow)/iTries)); + if (g_lastCommand == CEC_OPCODE_REQUEST_ACTIVE_SOURCE) + break; + iNow = GetTimeMs(); + } + } + } + + currentTvPower = g_parser->GetDevicePowerStatus(CECDEVICE_TV); + + if (currentTvPower != CEC_POWER_STATUS_ON) + PrintToStdOut("Failed to power on the TV, or the TV does not respond properly"); + + return currentTvPower == CEC_POWER_STATUS_ON; +} + +int main (int argc, char *argv[]) +{ + PrintToStdOut("=== USB-CEC Adapter Configuration ===\n"); + if (!OpenConnection()) + return 1; + + if (!PowerOnTV()) + return 1; + + bool bAddressOk(false); + uint16_t iAddress(0xFFF); + while (!bAddressOk) + { + iAddress = FindPhysicalAddress(); + + PrintToStdOut("Physical address: %4X", iAddress); + PrintToStdOut("Is this correct (y/n)?"); + string input; + getline(cin, input); + cin.clear(); + bAddressOk = (input == "y" || input == "Y"); + } + + PrintToStdOut("=== USB-CEC Adapter Configuration Summary ===\n"); + PrintToStdOut("Physical address: %4X", iAddress); + + g_parser->StandbyDevices(); + g_parser->Close(); + UnloadLibCec(g_parser); + + PrintToStdOut("Press enter to close this wizard."); + string input; + getline(cin, input); + return 0; +} -- 2.34.1