cec: added callback methods to libCEC. enable them by calling EnableCallbacks(ICECCal...
[deb_libcec.git] / src / testclient / main.cpp
CommitLineData
abbca718
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
31 */
32
761dcc45 33#include <cec.h>
acec5f48 34
abbca718
LOK
35#include <cstdio>
36#include <fcntl.h>
37#include <iostream>
e6d2161b 38#include <fstream>
abbca718 39#include <string>
b9187cc6 40#include <sstream>
acec5f48
LOK
41#include "../lib/platform/threads.h"
42#include "../lib/util/StdString.h"
03ae897d 43#include "../lib/implementations/CECCommandHandler.h"
abbca718
LOK
44
45using namespace CEC;
46using namespace std;
47
d75be19b 48#define CEC_TEST_CLIENT_VERSION 1
abbca718 49
761dcc45 50#include <cecloader.h>
8bc45476 51
8354f747
LOK
52int g_cecLogLevel(CEC_LOG_ALL);
53ofstream g_logOutput;
54bool g_bShortLog(false);
55CStdString g_strPort;
aa52c2e0
LOK
56uint8_t g_iHDMIPort(CEC_DEFAULT_HDMI_PORT);
57cec_logical_address g_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE);
8354f747
LOK
58cec_device_type_list g_typeList;
59bool g_bSingleCommand(false);
60
b9187cc6 61
8bca69de 62inline bool HexStrToInt(const std::string& data, uint8_t& value)
b9187cc6 63{
8bca69de
LOK
64 int iTmp(0);
65 if (sscanf(data.c_str(), "%x", &iTmp) == 1)
66 {
67 if (iTmp > 256)
68 value = 255;
69 else if (iTmp < 0)
70 value = 0;
71 else
72 value = (uint8_t) iTmp;
b9187cc6 73
8bca69de
LOK
74 return true;
75 }
76
77 return false;
78}
b9187cc6
LOK
79
80//get the first word (separated by whitespace) from string data and place that in word
81//then remove that word from string data
82bool GetWord(string& data, string& word)
83{
84 stringstream datastream(data);
85 string end;
86
87 datastream >> word;
88 if (datastream.fail())
89 {
90 data.clear();
91 return false;
92 }
93
94 size_t pos = data.find(word) + word.length();
95
96 if (pos >= data.length())
97 {
98 data.clear();
99 return true;
100 }
101
102 data = data.substr(pos);
103
104 datastream.clear();
105 datastream.str(data);
106
107 datastream >> end;
108 if (datastream.fail())
109 data.clear();
110
111 return true;
112}
113
d5f66c28 114void FlushLog(ICECAdapter *cecParser)
abbca718
LOK
115{
116 cec_log_message message;
117 while (cecParser && cecParser->GetNextLogMessage(&message))
118 {
bdd433cb 119 if ((message.level & g_cecLogLevel) == message.level)
abbca718 120 {
e6d2161b 121 CStdString strLevel;
bdd433cb
LOK
122 switch (message.level)
123 {
124 case CEC_LOG_ERROR:
e6d2161b 125 strLevel = "ERROR: ";
bdd433cb
LOK
126 break;
127 case CEC_LOG_WARNING:
e6d2161b 128 strLevel = "WARNING: ";
bdd433cb
LOK
129 break;
130 case CEC_LOG_NOTICE:
e6d2161b 131 strLevel = "NOTICE: ";
bdd433cb
LOK
132 break;
133 case CEC_LOG_TRAFFIC:
e6d2161b 134 strLevel = "TRAFFIC: ";
bdd433cb
LOK
135 break;
136 case CEC_LOG_DEBUG:
e6d2161b 137 strLevel = "DEBUG: ";
bdd433cb
LOK
138 break;
139 default:
140 break;
141 }
40339852 142
8bc45476
LOK
143 CStdString strFullLog;
144 strFullLog.Format("%s[%16lld]\t%s", strLevel.c_str(), message.time, message.message);
145 cout << strFullLog.c_str() << endl;
e6d2161b
LOK
146
147 if (g_logOutput.is_open())
8bc45476
LOK
148 {
149 if (g_bShortLog)
150 g_logOutput << message.message << endl;
151 else
152 g_logOutput << strFullLog.c_str() << endl;
153 }
bdd433cb 154 }
abbca718
LOK
155 }
156}
157
d5f66c28 158void ListDevices(ICECAdapter *parser)
abbca718 159{
25701fa6
LOK
160 cec_adapter *devices = new cec_adapter[10];
161 uint8_t iDevicesFound = parser->FindAdapters(devices, 10, NULL);
abbca718
LOK
162 if (iDevicesFound <= 0)
163 {
25701fa6 164 cout << "Found devices: NONE" << endl;
abbca718
LOK
165 }
166 else
167 {
25701fa6
LOK
168 CStdString strLog;
169 strLog.Format("Found devices: %d", iDevicesFound);
170 cout << strLog.c_str() << endl;
171 for (unsigned int iDevicePtr = 0; iDevicePtr < iDevicesFound; iDevicePtr++)
abbca718
LOK
172 {
173 CStdString strDevice;
25701fa6 174 strDevice.Format("device: %d\npath: %s\ncom port: %s", iDevicePtr + 1, devices[iDevicePtr].path, devices[iDevicePtr].comm);
abbca718
LOK
175 cout << endl << strDevice.c_str() << endl;
176 }
177 }
178}
179
d5f66c28 180void ShowHelpCommandLine(const char* strExec)
abbca718
LOK
181{
182 cout << endl <<
183 strExec << " {-h|--help|-l|--list-devices|[COM PORT]}" << endl <<
184 endl <<
185 "parameters:" << endl <<
8bc45476
LOK
186 " -h --help Shows this help text" << endl <<
187 " -l --list-devices List all devices on this system" << endl <<
f8513317 188 " -t --type {p|r|t|a} The device type to use. More than one is possible." << endl <<
16b1e052 189 " -p --port {int} The HDMI port to use as active source." << endl <<
aa52c2e0
LOK
190 " -b --base {int} The logical address of the device to with this " << endl <<
191 " adapter is connected." << endl <<
8bc45476
LOK
192 " -f --log-file {file} Writes all libCEC log message to a file" << endl <<
193 " -sf --short-log-file {file} Writes all libCEC log message without timestamps" << endl <<
194 " and log levels to a file." << endl <<
606034b6 195 " -d --log-level {level} Sets the log level. See cectypes.h for values." << endl <<
5ad0ea13
LOK
196 " -s --single-command Execute a single command and exit. Does not power" << endl <<
197 " on devices on startup and power them off on exit." << endl <<
8bc45476
LOK
198 " [COM PORT] The com port to connect to. If no COM" << endl <<
199 " port is given, the client tries to connect to the" << endl <<
200 " first device that is detected." << endl <<
825ddb96 201 endl <<
8bc45476
LOK
202 "Type 'h' or 'help' and press enter after starting the client to display all " << endl <<
203 "available commands" << endl;
825ddb96
LOK
204}
205
d5f66c28 206ICECAdapter *CreateParser(cec_device_type_list typeList)
f8513317
LOK
207{
208 ICECAdapter *parser = LibCecInit("CECTester", typeList);
209 if (!parser || parser->GetMinLibVersion() > CEC_TEST_CLIENT_VERSION)
210 {
211 #ifdef __WINDOWS__
212 cout << "Cannot load libcec.dll" << endl;
213 #else
214 cout << "Cannot load libcec.so" << endl;
215 #endif
216 return NULL;
217 }
218
219 CStdString strLog;
220 strLog.Format("CEC Parser created - libcec version %d.%d", parser->GetLibVersionMajor(), parser->GetLibVersionMinor());
221 cout << strLog.c_str() << endl;
222
223 return parser;
224}
225
d5f66c28 226void ShowHelpConsole(void)
825ddb96
LOK
227{
228 cout << endl <<
229 "================================================================================" << endl <<
230 "Available commands:" << endl <<
231 endl <<
7e793bdd
LOK
232 "[tx] {bytes} transfer bytes over the CEC line." << endl <<
233 "[txn] {bytes} transfer bytes but don't wait for transmission ACK." << endl <<
234 "[on] {address} power on the device with the given logical address." << endl <<
235 "[standby] {address} put the device with the given address in standby mode." << endl <<
236 "[la] {logical address} change the logical address of the CEC adapter." << endl <<
d2f1c157 237 "[p] {device} {port} change the HDMI port number of the CEC adapter." << endl <<
7e793bdd 238 "[pa] {physical address} change the physical address of the CEC adapter." << endl <<
a91e1ab4 239 "[as] make the CEC adapter the active source." << endl <<
7e793bdd
LOK
240 "[osd] {addr} {string} set OSD message on the specified device." << endl <<
241 "[ver] {addr} get the CEC version of the specified device." << endl <<
242 "[ven] {addr} get the vendor ID of the specified device." << endl <<
243 "[lang] {addr} get the menu language of the specified device." << endl <<
244 "[pow] {addr} get the power status of the specified device." << endl <<
ed21be2a 245 "[name] {addr} get the OSD name of the specified device." << endl <<
7e793bdd
LOK
246 "[poll] {addr} poll the specified device." << endl <<
247 "[lad] lists active devices on the bus" << endl <<
248 "[ad] {addr} checks whether the specified device is active." << endl <<
249 "[at] {type} checks whether the specified device type is active." << endl <<
250 "[volup] send a volume up command to the amp if present" << endl <<
251 "[voldown] send a volume down command to the amp if present" << endl <<
252 "[mute] send a mute/unmute command to the amp if present" << endl <<
03ae897d 253 "[scan] scan the CEC bus and display device info" << endl <<
8b7e5ff6 254 "[mon] {1|0} enable or disable CEC bus monitoring." << endl <<
bdd433cb 255 "[log] {1 - 31} change the log level. see cectypes.h for values." << endl <<
825ddb96 256 "[ping] send a ping command to the CEC adapter." << endl <<
88f45c9b
LOK
257 "[bl] to let the adapter enter the bootloader, to upgrade" << endl <<
258 " the flash rom." << endl <<
259 "[r] reconnect to the CEC adapter." << endl <<
825ddb96 260 "[h] or [help] show this help." << endl <<
88f45c9b
LOK
261 "[q] or [quit] to quit the CEC test client and switch off all" << endl <<
262 " connected CEC devices." << endl <<
825ddb96 263 "================================================================================" << endl;
abbca718
LOK
264}
265
8354f747
LOK
266bool ProcessCommandTX(ICECAdapter *parser, const string &command, string &arguments)
267{
268 if (command == "tx" || command == "txn")
269 {
270 string strvalue;
271 uint8_t ivalue;
272 cec_command bytes;
273 bytes.Clear();
274
275 while (GetWord(arguments, strvalue) && HexStrToInt(strvalue, ivalue))
276 bytes.PushBack(ivalue);
277
278 if (command == "txn")
279 bytes.transmit_timeout = 0;
280
281 parser->Transmit(bytes);
282
283 return true;
284 }
285
286 return false;
287}
288
289bool ProcessCommandON(ICECAdapter *parser, const string &command, string &arguments)
290{
291 if (command == "on")
292 {
293 string strValue;
294 uint8_t iValue = 0;
295 if (GetWord(arguments, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
296 {
297 parser->PowerOnDevices((cec_logical_address) iValue);
298 return true;
299 }
300 else
301 {
302 cout << "invalid destination" << endl;
303 }
304 }
305
306 return false;
307}
308
309bool ProcessCommandSTANDBY(ICECAdapter *parser, const string &command, string &arguments)
310{
311 if (command == "standby")
312 {
313 string strValue;
314 uint8_t iValue = 0;
315 if (GetWord(arguments, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
316 {
317 parser->StandbyDevices((cec_logical_address) iValue);
318 return true;
319 }
320 else
321 {
322 cout << "invalid destination" << endl;
323 }
324 }
325
326 return false;
327}
328
329bool ProcessCommandPOLL(ICECAdapter *parser, const string &command, string &arguments)
330{
331 if (command == "poll")
332 {
333 string strValue;
334 uint8_t iValue = 0;
335 if (GetWord(arguments, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
336 {
337 if (parser->PollDevice((cec_logical_address) iValue))
338 cout << "POLL message sent" << endl;
339 else
340 cout << "POLL message not sent" << endl;
341 return true;
342 }
343 else
344 {
345 cout << "invalid destination" << endl;
346 }
347 }
348
349 return false;
350}
351
352bool ProcessCommandLA(ICECAdapter *parser, const string &command, string &arguments)
353{
354 if (command == "la")
355 {
356 string strvalue;
357 if (GetWord(arguments, strvalue))
358 {
359 parser->SetLogicalAddress((cec_logical_address) atoi(strvalue.c_str()));
360 return true;
361 }
362 }
363
364 return false;
365}
366
367bool ProcessCommandP(ICECAdapter *parser, const string &command, string &arguments)
368{
369 if (command == "p")
370 {
371 string strPort, strDevice;
372 if (GetWord(arguments, strDevice) && GetWord(arguments, strPort))
373 {
374 parser->SetHDMIPort((cec_logical_address)atoi(strDevice.c_str()), (uint8_t)atoi(strPort.c_str()));
375 return true;
376 }
377 }
378
379 return false;
380}
381
382bool ProcessCommandPA(ICECAdapter *parser, const string &command, string &arguments)
383{
384 if (command == "pa")
385 {
386 string strB1, strB2;
387 uint8_t iB1, iB2;
388 if (GetWord(arguments, strB1) && HexStrToInt(strB1, iB1) &&
389 GetWord(arguments, strB2) && HexStrToInt(strB2, iB2))
390 {
391 uint16_t iPhysicalAddress = ((uint16_t)iB1 << 8) + iB2;
392 parser->SetPhysicalAddress(iPhysicalAddress);
393 return true;
394 }
395 }
396
397 return false;
398}
399
400bool ProcessCommandOSD(ICECAdapter *parser, const string &command, string &arguments)
401{
402 if (command == "osd")
403 {
404 bool bFirstWord(false);
405 string strAddr, strMessage, strWord;
406 uint8_t iAddr;
407 if (GetWord(arguments, strAddr) && HexStrToInt(strAddr, iAddr) && iAddr < 0xF)
408 {
409 while (GetWord(arguments, strWord))
410 {
411 if (bFirstWord)
412 {
413 bFirstWord = false;
414 strMessage.append(" ");
415 }
416 strMessage.append(strWord);
417 }
418 parser->SetOSDString((cec_logical_address) iAddr, CEC_DISPLAY_CONTROL_DISPLAY_FOR_DEFAULT_TIME, strMessage.c_str());
419 return true;
420 }
421 }
422
423 return false;
424}
425
1de6617c 426bool ProcessCommandAS(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
a91e1ab4
LOK
427{
428 if (command == "as")
429 {
430 parser->SetActiveView();
431 return true;
432 }
433
434 return false;
435}
436
437
1de6617c 438bool ProcessCommandPING(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
8354f747
LOK
439{
440 if (command == "ping")
441 {
442 parser->PingAdapter();
443 return true;
444 }
445
446 return false;
447}
448
1de6617c 449bool ProcessCommandVOLUP(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
8354f747
LOK
450{
451 if (command == "volup")
452 {
453 CStdString strLog;
454 strLog.Format("volume up: %2X", parser->VolumeUp());
455 cout << strLog.c_str() << endl;
456 return true;
457 }
458
459 return false;
460}
461
1de6617c 462bool ProcessCommandVOLDOWN(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
8354f747
LOK
463{
464 if (command == "voldown")
465 {
466 CStdString strLog;
467 strLog.Format("volume up: %2X", parser->VolumeDown());
468 cout << strLog.c_str() << endl;
469 return true;
470 }
471
472 return false;
473}
474
1de6617c 475bool ProcessCommandMUTE(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
8354f747
LOK
476{
477 if (command == "mute")
478 {
479 CStdString strLog;
480 strLog.Format("mute: %2X", parser->MuteAudio());
481 cout << strLog.c_str() << endl;
482 return true;
483 }
484
485 return false;
486}
487
488bool ProcessCommandMON(ICECAdapter *parser, const string &command, string &arguments)
489{
490 if (command == "mon")
491 {
492 CStdString strEnable;
493 if (GetWord(arguments, strEnable) && (strEnable.Equals("0") || strEnable.Equals("1")))
494 {
495 parser->SwitchMonitoring(strEnable.Equals("1"));
496 return true;
497 }
498 }
499
500 return false;
501}
502
1de6617c 503bool ProcessCommandBL(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
8354f747
LOK
504{
505 if (command == "bl")
506 {
507 parser->StartBootloader();
508 return true;
509 }
510
511 return false;
512}
513
514bool ProcessCommandLANG(ICECAdapter *parser, const string &command, string &arguments)
515{
516 if (command == "lang")
517 {
518 CStdString strDev;
519 if (GetWord(arguments, strDev))
520 {
521 int iDev = atoi(strDev);
522 if (iDev >= 0 && iDev < 15)
523 {
524 CStdString strLog;
525 cec_menu_language language;
526 if (parser->GetDeviceMenuLanguage((cec_logical_address) iDev, &language))
527 strLog.Format("menu language '%s'", language.language);
528 else
529 strLog = "failed!";
530 cout << strLog.c_str() << endl;
531 return true;
532 }
533 }
534 }
535
536 return false;
537}
538
539bool ProcessCommandVEN(ICECAdapter *parser, const string &command, string &arguments)
540{
541 if (command == "ven")
542 {
543 CStdString strDev;
544 if (GetWord(arguments, strDev))
545 {
546 int iDev = atoi(strDev);
547 if (iDev >= 0 && iDev < 15)
548 {
549 uint64_t iVendor = parser->GetDeviceVendorId((cec_logical_address) iDev);
550 CStdString strLog;
551 strLog.Format("vendor id: %06x", iVendor);
552 cout << strLog.c_str() << endl;
553 return true;
554 }
555 }
556 }
557
558 return false;
559}
560
561bool ProcessCommandVER(ICECAdapter *parser, const string &command, string &arguments)
562{
563 if (command == "ver")
564 {
565 CStdString strDev;
566 if (GetWord(arguments, strDev))
567 {
568 int iDev = atoi(strDev);
569 if (iDev >= 0 && iDev < 15)
570 {
571 cec_version iVersion = parser->GetDeviceCecVersion((cec_logical_address) iDev);
572 cout << "CEC version " << parser->ToString(iVersion) << endl;
573 return true;
574 }
575 }
576 }
577
578 return false;
579}
580
581bool ProcessCommandPOW(ICECAdapter *parser, const string &command, string &arguments)
582{
583 if (command == "pow")
584 {
585 CStdString strDev;
586 if (GetWord(arguments, strDev))
587 {
588 int iDev = atoi(strDev);
589 if (iDev >= 0 && iDev < 15)
590 {
591 cec_power_status iPower = parser->GetDevicePowerStatus((cec_logical_address) iDev);
592 cout << "power status: " << parser->ToString(iPower) << endl;
593
594 return true;
595 }
596 }
597 }
598
599 return false;
600}
601
602bool ProcessCommandNAME(ICECAdapter *parser, const string &command, string &arguments)
603{
604 if (command == "name")
605 {
606 CStdString strDev;
607 if (GetWord(arguments, strDev))
608 {
609 int iDev = atoi(strDev);
610 if (iDev >= 0 && iDev < 15)
611 {
f71a1df9 612 cec_osd_name name = parser->GetDeviceOSDName((cec_logical_address)iDev);
8354f747
LOK
613 cout << "OSD name of device " << iDev << " is '" << name.name << "'" << endl;
614 }
615 return true;
616 }
617 }
618
619 return false;
620}
621
1de6617c 622bool ProcessCommandLAD(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
8354f747
LOK
623{
624 if (command == "lad")
625 {
626 cout << "listing active devices:" << endl;
627 cec_logical_addresses addresses = parser->GetActiveDevices();
90ea9066 628 for (uint8_t iPtr = 0; iPtr <= 11; iPtr++)
8354f747
LOK
629 if (addresses[iPtr])
630 cout << "logical address " << (int)iPtr << endl;
631 return true;
632 }
633
634 return false;
635}
636
637bool ProcessCommandAD(ICECAdapter *parser, const string &command, string &arguments)
638{
639 if (command == "ad")
640 {
641 CStdString strDev;
642 if (GetWord(arguments, strDev))
643 {
644 int iDev = atoi(strDev);
645 if (iDev >= 0 && iDev < 15)
646 cout << "logical address " << iDev << " is " << (parser->IsActiveDevice((cec_logical_address)iDev) ? "active" : "not active") << endl;
647 }
648 }
649
650 return false;
651}
652
653bool ProcessCommandAT(ICECAdapter *parser, const string &command, string &arguments)
654{
655 if (command == "at")
656 {
657 CStdString strType;
658 if (GetWord(arguments, strType))
659 {
660 cec_device_type type = CEC_DEVICE_TYPE_TV;
661 if (strType.Equals("a"))
662 type = CEC_DEVICE_TYPE_AUDIO_SYSTEM;
663 else if (strType.Equals("p"))
664 type = CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
665 else if (strType.Equals("r"))
666 type = CEC_DEVICE_TYPE_RECORDING_DEVICE;
667 else if (strType.Equals("t"))
668 type = CEC_DEVICE_TYPE_TUNER;
669 cout << "device " << type << " is " << (parser->IsActiveDeviceType(type) ? "active" : "not active") << endl;
670 return true;
671 }
672 }
673
674 return false;
675}
676
1de6617c 677bool ProcessCommandR(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
abbca718 678{
8354f747
LOK
679 if (command == "r")
680 {
681 cout << "closing the connection" << endl;
682 parser->Close();
683 FlushLog(parser);
684
685 cout << "opening a new connection" << endl;
686 parser->Open(g_strPort.c_str());
687 FlushLog(parser);
688
689 cout << "setting active source" << endl;
690 parser->SetActiveSource();
691 return true;
692 }
693
694 return false;
695}
696
1de6617c 697bool ProcessCommandH(ICECAdapter * UNUSED(parser), const string &command, string & UNUSED(arguments))
8354f747
LOK
698{
699 if (command == "h" || command == "help")
700 {
701 ShowHelpConsole();
702 return true;
703 }
704
705 return false;
706}
abbca718 707
1de6617c 708bool ProcessCommandLOG(ICECAdapter * UNUSED(parser), const string &command, string &arguments)
8354f747
LOK
709{
710 if (command == "log")
711 {
712 CStdString strLevel;
713 if (GetWord(arguments, strLevel))
714 {
715 int iNewLevel = atoi(strLevel);
716 if (iNewLevel >= CEC_LOG_ERROR && iNewLevel <= CEC_LOG_ALL)
717 {
718 g_cecLogLevel = iNewLevel;
719 cout << "log level changed to " << strLevel.c_str() << endl;
720 return true;
721 }
722 }
723 }
724
725 return false;
726}
727
1de6617c 728bool ProcessCommandSCAN(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
8354f747
LOK
729{
730 if (command == "scan")
731 {
732 cout << "CEC bus information" << endl;
733 cout << "===================" << endl;
734 cec_logical_addresses addresses = parser->GetActiveDevices();
735 for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
736 {
737 if (addresses[iPtr])
738 {
a98b2f2e 739 uint64_t iVendorId = parser->GetDeviceVendorId((cec_logical_address)iPtr);
b4b1b49b 740 bool bActive = parser->IsActiveSource((cec_logical_address)iPtr);
eab72c40 741 uint16_t iPhysicalAddress = parser->GetDevicePhysicalAddress((cec_logical_address)iPtr);
eab72c40
LOK
742 cec_version iCecVersion = parser->GetDeviceCecVersion((cec_logical_address)iPtr);
743 cec_power_status power = parser->GetDevicePowerStatus((cec_logical_address)iPtr);
f71a1df9 744 cec_osd_name osdName = parser->GetDeviceOSDName((cec_logical_address)iPtr);
eab72c40
LOK
745 CStdString strAddr;
746 strAddr.Format("%04x", iPhysicalAddress);
8354f747
LOK
747 cec_menu_language lang;
748 lang.device = CECDEVICE_UNKNOWN;
749 parser->GetDeviceMenuLanguage((cec_logical_address)iPtr, &lang);
750
751 cout << "device #" << (int)iPtr << ": " << parser->ToString((cec_logical_address)iPtr) << endl;
b4b1b49b
LOK
752 cout << "address: " << strAddr.c_str() << endl;
753 cout << "active source: " << (bActive ? "yes" : "no") << endl;
754 cout << "vendor: " << parser->ToString((cec_vendor_id)iVendorId) << endl;
755 cout << "osd string: " << osdName.name << endl;
756 cout << "CEC version: " << parser->ToString(iCecVersion) << endl;
757 cout << "power status: " << parser->ToString(power) << endl;
8354f747 758 if ((uint8_t)lang.device == iPtr)
b4b1b49b 759 cout << "language: " << lang.language << endl;
8354f747
LOK
760 cout << endl;
761 }
762 }
763 return true;
764 }
765
766 return false;
767}
768
769bool ProcessConsoleCommand(ICECAdapter *parser, string &input)
770{
771 if (!input.empty())
772 {
773 string command;
774 if (GetWord(input, command))
775 {
776 if (command == "q" || command == "quit")
777 return false;
778
779 ProcessCommandTX(parser, command, input) ||
780 ProcessCommandON(parser, command, input) ||
781 ProcessCommandSTANDBY(parser, command, input) ||
782 ProcessCommandPOLL(parser, command, input) ||
783 ProcessCommandLA(parser, command, input) ||
784 ProcessCommandP(parser, command, input) ||
785 ProcessCommandPA(parser, command, input) ||
a91e1ab4 786 ProcessCommandAS(parser, command, input) ||
8354f747
LOK
787 ProcessCommandOSD(parser, command, input) ||
788 ProcessCommandPING(parser, command, input) ||
789 ProcessCommandVOLUP(parser, command, input) ||
790 ProcessCommandVOLDOWN(parser, command, input) ||
791 ProcessCommandMUTE(parser, command, input) ||
792 ProcessCommandMON(parser, command, input) ||
793 ProcessCommandBL(parser, command, input) ||
794 ProcessCommandLANG(parser, command, input) ||
795 ProcessCommandVEN(parser, command, input) ||
796 ProcessCommandVER(parser, command, input) ||
797 ProcessCommandPOW(parser, command, input) ||
798 ProcessCommandNAME(parser, command, input) ||
799 ProcessCommandLAD(parser, command, input) ||
800 ProcessCommandAD(parser, command, input) ||
801 ProcessCommandAT(parser, command, input) ||
802 ProcessCommandR(parser, command, input) ||
803 ProcessCommandH(parser, command, input) ||
804 ProcessCommandLOG(parser, command, input) ||
805 ProcessCommandSCAN(parser, command, input);
806 }
807 }
808 return true;
809}
810
811bool ProcessCommandLineArguments(int argc, char *argv[])
812{
813 bool bReturn(true);
3d0dca16 814 int iArgPtr = 1;
d9c13c6d 815 while (iArgPtr < argc && bReturn)
e6d2161b 816 {
3d0dca16 817 if (argc >= iArgPtr + 1)
e6d2161b 818 {
3d0dca16
LOK
819 if (!strcmp(argv[iArgPtr], "-f") ||
820 !strcmp(argv[iArgPtr], "--log-file") ||
821 !strcmp(argv[iArgPtr], "-sf") ||
822 !strcmp(argv[iArgPtr], "--short-log-file"))
823 {
824 if (argc >= iArgPtr + 2)
825 {
826 g_logOutput.open(argv[iArgPtr + 1]);
827 g_bShortLog = (!strcmp(argv[iArgPtr], "-sf") || !strcmp(argv[iArgPtr], "--short-log-file"));
828 iArgPtr += 2;
829 }
830 else
831 {
606034b6
LOK
832 cout << "== skipped log-file parameter: no file given ==" << endl;
833 ++iArgPtr;
834 }
835 }
836 else if (!strcmp(argv[iArgPtr], "-d") ||
837 !strcmp(argv[iArgPtr], "--log-level"))
838 {
839 if (argc >= iArgPtr + 2)
840 {
841 int iNewLevel = atoi(argv[iArgPtr + 1]);
842 if (iNewLevel >= CEC_LOG_ERROR && iNewLevel <= CEC_LOG_ALL)
843 {
844 g_cecLogLevel = iNewLevel;
8354f747 845 if (!g_bSingleCommand)
5ad0ea13 846 cout << "log level set to " << argv[iArgPtr + 1] << endl;
606034b6
LOK
847 }
848 else
849 {
850 cout << "== skipped log-level parameter: invalid level '" << argv[iArgPtr + 1] << "' ==" << endl;
851 }
852 iArgPtr += 2;
853 }
854 else
855 {
856 cout << "== skipped log-level parameter: no level given ==" << endl;
3d0dca16
LOK
857 ++iArgPtr;
858 }
859 }
f8513317
LOK
860 else if (!strcmp(argv[iArgPtr], "-t") ||
861 !strcmp(argv[iArgPtr], "--type"))
d6cc5f60
LOK
862 {
863 if (argc >= iArgPtr + 2)
864 {
f8513317
LOK
865 if (!strcmp(argv[iArgPtr + 1], "p"))
866 {
8354f747 867 if (!g_bSingleCommand)
5ad0ea13 868 cout << "== using device type 'playback device'" << endl;
8354f747 869 g_typeList.add(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
f8513317
LOK
870 }
871 else if (!strcmp(argv[iArgPtr + 1], "r"))
d6cc5f60 872 {
8354f747 873 if (!g_bSingleCommand)
5ad0ea13 874 cout << "== using device type 'recording device'" << endl;
8354f747 875 g_typeList.add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
f8513317
LOK
876 }
877 else if (!strcmp(argv[iArgPtr + 1], "t"))
878 {
8354f747 879 if (!g_bSingleCommand)
5ad0ea13 880 cout << "== using device type 'tuner'" << endl;
8354f747 881 g_typeList.add(CEC_DEVICE_TYPE_TUNER);
f8513317
LOK
882 }
883 else if (!strcmp(argv[iArgPtr + 1], "a"))
884 {
8354f747 885 if (!g_bSingleCommand)
5ad0ea13 886 cout << "== using device type 'audio system'" << endl;
8354f747 887 g_typeList.add(CEC_DEVICE_TYPE_AUDIO_SYSTEM);
d6cc5f60
LOK
888 }
889 else
890 {
f8513317 891 cout << "== skipped invalid device type '" << argv[iArgPtr + 1] << "'" << endl;
d6cc5f60 892 }
d6cc5f60
LOK
893 ++iArgPtr;
894 }
f8513317 895 ++iArgPtr;
d6cc5f60 896 }
3d0dca16
LOK
897 else if (!strcmp(argv[iArgPtr], "--list-devices") ||
898 !strcmp(argv[iArgPtr], "-l"))
899 {
8354f747 900 ICECAdapter *parser = CreateParser(g_typeList);
f8513317
LOK
901 if (parser)
902 {
d5f66c28 903 ListDevices(parser);
f8513317 904 UnloadLibCec(parser);
d9c13c6d 905 parser = NULL;
f8513317 906 }
8354f747 907 bReturn = false;
3d0dca16 908 }
5ad0ea13
LOK
909 else if (!strcmp(argv[iArgPtr], "--single-command") ||
910 !strcmp(argv[iArgPtr], "-s"))
911 {
8354f747 912 g_bSingleCommand = true;
5ad0ea13
LOK
913 ++iArgPtr;
914 }
3d0dca16
LOK
915 else if (!strcmp(argv[iArgPtr], "--help") ||
916 !strcmp(argv[iArgPtr], "-h"))
917 {
d5f66c28 918 ShowHelpCommandLine(argv[0]);
3d0dca16
LOK
919 return 0;
920 }
aa52c2e0
LOK
921 else if (!strcmp(argv[iArgPtr], "-b") ||
922 !strcmp(argv[iArgPtr], "--base"))
923 {
924 if (argc >= iArgPtr + 2)
925 {
926 g_iBaseDevice = (cec_logical_address)atoi(argv[iArgPtr + 1]);
927 cout << "using base device '" << (int)g_iBaseDevice << "'" << endl;
928 ++iArgPtr;
929 }
930 ++iArgPtr;
931 }
45de9d9f 932 else if (!strcmp(argv[iArgPtr], "-p") ||
16b1e052 933 !strcmp(argv[iArgPtr], "--port"))
45de9d9f
LOK
934 {
935 if (argc >= iArgPtr + 2)
936 {
8354f747 937 g_iHDMIPort = (int8_t)atoi(argv[iArgPtr + 1]);
aa52c2e0 938 cout << "using HDMI port '" << (int)g_iHDMIPort << "'" << endl;
45de9d9f
LOK
939 ++iArgPtr;
940 }
941 ++iArgPtr;
942 }
3d0dca16
LOK
943 else
944 {
945 g_strPort = argv[iArgPtr++];
946 }
e6d2161b
LOK
947 }
948 }
949
8354f747
LOK
950 return bReturn;
951}
952
953int main (int argc, char *argv[])
954{
955 g_typeList.clear();
956
957 if (!ProcessCommandLineArguments(argc, argv))
958 return 0;
959
960 if (g_typeList.IsEmpty())
f8513317 961 {
8354f747 962 if (!g_bSingleCommand)
88e5de6f 963 cout << "No device type given. Using 'recording device'" << endl;
8354f747 964 g_typeList.add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
f8513317
LOK
965 }
966
8354f747 967 ICECAdapter *parser = LibCecInit("CECTester", g_typeList);
f8513317
LOK
968 if (!parser || parser->GetMinLibVersion() > CEC_TEST_CLIENT_VERSION)
969 {
970#ifdef __WINDOWS__
971 cout << "Cannot load libcec.dll" << endl;
972#else
973 cout << "Cannot load libcec.so" << endl;
974#endif
863c4e57
LOK
975
976 if (parser)
977 UnloadLibCec(parser);
978
f8513317
LOK
979 return 1;
980 }
f8513317 981
8354f747 982 if (!g_bSingleCommand)
5ad0ea13
LOK
983 {
984 CStdString strLog;
985 strLog.Format("CEC Parser created - libcec version %d.%d", parser->GetLibVersionMajor(), parser->GetLibVersionMinor());
986 cout << strLog.c_str() << endl;
987
988 //make stdin non-blocking
989 #ifndef __WINDOWS__
990 int flags = fcntl(0, F_GETFL, 0);
991 flags |= O_NONBLOCK;
992 fcntl(0, F_SETFL, flags);
993 #endif
994 }
f8513317 995
3d0dca16 996 if (g_strPort.IsEmpty())
abbca718 997 {
8354f747 998 if (!g_bSingleCommand)
5ad0ea13 999 cout << "no serial port given. trying autodetect: ";
25701fa6
LOK
1000 cec_adapter devices[10];
1001 uint8_t iDevicesFound = parser->FindAdapters(devices, 10, NULL);
abbca718
LOK
1002 if (iDevicesFound <= 0)
1003 {
8354f747 1004 if (g_bSingleCommand)
5ad0ea13 1005 cout << "autodetect ";
abbca718
LOK
1006 cout << "FAILED" << endl;
1007 UnloadLibCec(parser);
1008 return 1;
1009 }
1010 else
1011 {
8354f747 1012 if (!g_bSingleCommand)
5ad0ea13
LOK
1013 {
1014 cout << endl << " path: " << devices[0].path << endl <<
1015 " com port: " << devices[0].comm << endl << endl;
1016 }
3d0dca16 1017 g_strPort = devices[0].comm;
abbca718
LOK
1018 }
1019 }
e6d2161b 1020
aa52c2e0
LOK
1021 parser->SetHDMIPort(g_iBaseDevice, g_iHDMIPort);
1022 cout << "opening a connection to the CEC adapter..." << endl;
16b1e052 1023
3d0dca16 1024 if (!parser->Open(g_strPort.c_str()))
abbca718 1025 {
3d0dca16 1026 cout << "unable to open the device on port " << g_strPort << endl;
d5f66c28 1027 FlushLog(parser);
abbca718
LOK
1028 UnloadLibCec(parser);
1029 return 1;
1030 }
1031
8354f747 1032 if (!g_bSingleCommand)
5ad0ea13 1033 {
45b97de7 1034 FlushLog(parser);
5ad0ea13 1035 cout << "cec device opened" << endl;
abbca718 1036
5ad0ea13
LOK
1037 parser->PowerOnDevices(CECDEVICE_TV);
1038 FlushLog(parser);
abbca718 1039
5ad0ea13
LOK
1040 parser->SetActiveSource();
1041 FlushLog(parser);
1042
1043 cout << "waiting for input" << endl;
1044 }
abbca718
LOK
1045
1046 bool bContinue(true);
abbca718
LOK
1047 while (bContinue)
1048 {
d5f66c28 1049 FlushLog(parser);
abbca718 1050
b5b53c7d
LOK
1051 /* just ignore the command buffer and clear it */
1052 cec_command dummy;
1053 while (parser && parser->GetNextCommand(&dummy)) {}
1054
abbca718
LOK
1055 string input;
1056 getline(cin, input);
1057 cin.clear();
1058
8354f747 1059 if (ProcessConsoleCommand(parser, input) && !g_bSingleCommand)
abbca718 1060 {
8354f747 1061 if (!input.empty())
abbca718
LOK
1062 cout << "waiting for input" << endl;
1063 }
8354f747
LOK
1064 else
1065 bContinue = false;
5ad0ea13
LOK
1066
1067 if (bContinue)
1068 CCondition::Sleep(50);
abbca718
LOK
1069 }
1070
8354f747 1071 if (!g_bSingleCommand)
5ad0ea13
LOK
1072 parser->StandbyDevices(CECDEVICE_BROADCAST);
1073
5f39c4d8 1074 parser->Close();
d5f66c28 1075 FlushLog(parser);
abbca718 1076 UnloadLibCec(parser);
e6d2161b
LOK
1077
1078 if (g_logOutput.is_open())
1079 g_logOutput.close();
1080
abbca718
LOK
1081 return 0;
1082}