• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4  */
5 
6 #include <sstream>
7 
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <sys/ioctl.h>
11 #include <sys/time.h>
12 #include <unistd.h>
13 
14 #include "cec-compliance.h"
15 #include "compiler.h"
16 
17 /* Short option list
18 
19    Please keep in alphabetical order.
20    That makes it easier to see which short options are still free.
21 
22    In general the lower case is used to set something and the upper
23    case is used to retrieve a setting. */
24 enum Option {
25 	OptSetAdapter = 'a',
26 	OptTestAdapter = 'A',
27 	OptColor = 'C',
28 	OptSetDevice = 'd',
29 	OptSetDriver = 'D',
30 	OptExpect = 'e',
31 	OptExitOnFail = 'E',
32 	OptTestFuzzing = 'F',
33 	OptHelp = 'h',
34 	OptInteractive = 'i',
35 	OptListTests = 'l',
36 	OptExpectWithNoWarnings = 'n',
37 	OptNoWarnings = 'N',
38 	OptRemote = 'r',
39 	OptReplyThreshold = 'R',
40 	OptSkipInfo = 's',
41 	OptShowTimestamp = 'S',
42 	OptTimeout = 't',
43 	OptTrace = 'T',
44 	OptVerbose = 'v',
45 	OptWallClock = 'w',
46 	OptExitOnWarn = 'W',
47 
48 	OptTestCore = 128,
49 	OptTestAudioRateControl,
50 	OptTestARCControl,
51 	OptTestCapDiscoveryControl,
52 	OptTestDeckControl,
53 	OptTestDeviceMenuControl,
54 	OptTestDeviceOSDTransfer,
55 	OptTestDynamicAutoLipsync,
56 	OptTestOSDDisplay,
57 	OptTestOneTouchPlay,
58 	OptTestOneTouchRecord,
59 	OptTestPowerStatus,
60 	OptTestRemoteControlPassthrough,
61 	OptTestRoutingControl,
62 	OptTestSystemAudioControl,
63 	OptTestSystemInformation,
64 	OptTestTimerProgramming,
65 	OptTestTunerControl,
66 	OptTestVendorSpecificCommands,
67 	OptTestStandbyResume,
68 
69 	OptSkipTestAudioRateControl,
70 	OptSkipTestARCControl,
71 	OptSkipTestCapDiscoveryControl,
72 	OptSkipTestDeckControl,
73 	OptSkipTestDeviceMenuControl,
74 	OptSkipTestDeviceOSDTransfer,
75 	OptSkipTestDynamicAutoLipsync,
76 	OptSkipTestOSDDisplay,
77 	OptSkipTestOneTouchPlay,
78 	OptSkipTestOneTouchRecord,
79 	OptSkipTestPowerStatus,
80 	OptSkipTestRemoteControlPassthrough,
81 	OptSkipTestRoutingControl,
82 	OptSkipTestSystemAudioControl,
83 	OptSkipTestSystemInformation,
84 	OptSkipTestTimerProgramming,
85 	OptSkipTestTunerControl,
86 	OptSkipTestVendorSpecificCommands,
87 	OptSkipTestStandbyResume,
88 
89 	OptVersion,
90 	OptLast = 256
91 };
92 
93 
94 static char options[OptLast];
95 
96 static int app_result;
97 static int tests_total, tests_ok;
98 
99 bool show_info;
100 bool show_colors;
101 bool show_warnings = true;
102 bool exit_on_fail;
103 bool exit_on_warn;
104 unsigned warnings;
105 unsigned reply_threshold = 1000;
106 time_t long_timeout = 60;
107 
108 static struct option long_options[] = {
109 	{"device", required_argument, nullptr, OptSetDevice},
110 	{"adapter", required_argument, nullptr, OptSetAdapter},
111 	{"driver", required_argument, nullptr, OptSetDriver},
112 	{"help", no_argument, nullptr, OptHelp},
113 	{"no-warnings", no_argument, nullptr, OptNoWarnings},
114 	{"exit-on-fail", no_argument, nullptr, OptExitOnFail},
115 	{"exit-on-warn", no_argument, nullptr, OptExitOnWarn},
116 	{"remote", optional_argument, nullptr, OptRemote},
117 	{"list-tests", no_argument, nullptr, OptListTests},
118 	{"expect", required_argument, nullptr, OptExpect},
119 	{"expect-with-no-warnings", required_argument, nullptr, OptExpectWithNoWarnings},
120 	{"timeout", required_argument, nullptr, OptTimeout},
121 	{"trace", no_argument, nullptr, OptTrace},
122 	{"verbose", no_argument, nullptr, OptVerbose},
123 	{"color", required_argument, nullptr, OptColor},
124 	{"skip-info", no_argument, nullptr, OptSkipInfo},
125 	{"wall-clock", no_argument, nullptr, OptWallClock},
126 	{"show-timestamp", no_argument, nullptr, OptShowTimestamp},
127 	{"interactive", no_argument, nullptr, OptInteractive},
128 	{"reply-threshold", required_argument, nullptr, OptReplyThreshold},
129 
130 	{"test-adapter", no_argument, nullptr, OptTestAdapter},
131 	{"test-fuzzing", no_argument, nullptr, OptTestFuzzing},
132 	{"test-core", no_argument, nullptr, OptTestCore},
133 	{"test-audio-rate-control", no_argument, nullptr, OptTestAudioRateControl},
134 	{"test-audio-return-channel-control", no_argument, nullptr, OptTestARCControl},
135 	{"test-capability-discovery-and-control", no_argument, nullptr, OptTestCapDiscoveryControl},
136 	{"test-deck-control", no_argument, nullptr, OptTestDeckControl},
137 	{"test-device-menu-control", no_argument, nullptr, OptTestDeviceMenuControl},
138 	{"test-device-osd-transfer", no_argument, nullptr, OptTestDeviceOSDTransfer},
139 	{"test-dynamic-auto-lipsync", no_argument, nullptr, OptTestDynamicAutoLipsync},
140 	{"test-osd-display", no_argument, nullptr, OptTestOSDDisplay},
141 	{"test-one-touch-play", no_argument, nullptr, OptTestOneTouchPlay},
142 	{"test-one-touch-record", no_argument, nullptr, OptTestOneTouchRecord},
143 	{"test-power-status", no_argument, nullptr, OptTestPowerStatus},
144 	{"test-remote-control-passthrough", no_argument, nullptr, OptTestRemoteControlPassthrough},
145 	{"test-routing-control", no_argument, nullptr, OptTestRoutingControl},
146 	{"test-system-audio-control", no_argument, nullptr, OptTestSystemAudioControl},
147 	{"test-system-information", no_argument, nullptr, OptTestSystemInformation},
148 	{"test-timer-programming", no_argument, nullptr, OptTestTimerProgramming},
149 	{"test-tuner-control", no_argument, nullptr, OptTestTunerControl},
150 	{"test-vendor-specific-commands", no_argument, nullptr, OptTestVendorSpecificCommands},
151 	{"test-standby-resume", no_argument, nullptr, OptTestStandbyResume},
152 
153 	{"skip-test-audio-rate-control", no_argument, nullptr, OptSkipTestAudioRateControl},
154 	{"skip-test-audio-return-channel-control", no_argument, nullptr, OptSkipTestARCControl},
155 	{"skip-test-capability-discovery-and-control", no_argument, nullptr, OptSkipTestCapDiscoveryControl},
156 	{"skip-test-deck-control", no_argument, nullptr, OptSkipTestDeckControl},
157 	{"skip-test-device-menu-control", no_argument, nullptr, OptSkipTestDeviceMenuControl},
158 	{"skip-test-device-osd-transfer", no_argument, nullptr, OptSkipTestDeviceOSDTransfer},
159 	{"skip-test-dynamic-auto-lipsync", no_argument, nullptr, OptSkipTestDynamicAutoLipsync},
160 	{"skip-test-osd-display", no_argument, nullptr, OptSkipTestOSDDisplay},
161 	{"skip-test-one-touch-play", no_argument, nullptr, OptSkipTestOneTouchPlay},
162 	{"skip-test-one-touch-record", no_argument, nullptr, OptSkipTestOneTouchRecord},
163 	{"skip-test-power-status", no_argument, nullptr, OptSkipTestPowerStatus},
164 	{"skip-test-remote-control-passthrough", no_argument, nullptr, OptSkipTestRemoteControlPassthrough},
165 	{"skip-test-routing-control", no_argument, nullptr, OptSkipTestRoutingControl},
166 	{"skip-test-system-audio-control", no_argument, nullptr, OptSkipTestSystemAudioControl},
167 	{"skip-test-system-information", no_argument, nullptr, OptSkipTestSystemInformation},
168 	{"skip-test-timer-programming", no_argument, nullptr, OptSkipTestTimerProgramming},
169 	{"skip-test-tuner-control", no_argument, nullptr, OptSkipTestTunerControl},
170 	{"skip-test-vendor-specific-commands", no_argument, nullptr, OptSkipTestVendorSpecificCommands},
171 	{"skip-test-standby-resume", no_argument, nullptr, OptSkipTestStandbyResume},
172 	{"version", no_argument, nullptr, OptVersion},
173 	{nullptr, 0, nullptr, 0}
174 };
175 
176 #define STR(x) #x
177 #define STRING(x) STR(x)
178 
usage()179 static void usage()
180 {
181 	printf("Usage:\n"
182 	       "  -d, --device <dev>   Use device <dev> instead of /dev/cec0\n"
183 	       "                       If <dev> starts with a digit, then /dev/cec<dev> is used.\n"
184 	       "  -D, --driver <driver>    Use a cec device with this driver name\n"
185 	       "  -a, --adapter <adapter>  Use a cec device with this adapter name\n"
186 	       "  -r, --remote [<la>]  As initiator test the remote logical address or all LAs if no LA was given\n"
187 	       "  -R, --reply-threshold <timeout>\n"
188 	       "                       Warn if replies take longer than this threshold (default 1000ms)\n"
189 	       "  -i, --interactive    Interactive mode when doing remote tests\n"
190 	       "  -t, --timeout <secs> Set the standby/resume timeout to <secs>. Default is 60s.\n"
191 	       "\n"
192 	       "  -A, --test-adapter                  Test the CEC adapter API\n"
193 	       "  -F, --test-fuzzing                  Test by fuzzing CEC messages\n"
194 	       "  --test-core                         Test the core functionality\n"
195 	       "\n"
196 	       "By changing --test to --skip-test in the following options you can skip tests\n"
197 	       "instead of enabling them.\n"
198 	       "\n"
199 	       "  --test-audio-rate-control           Test the Audio Rate Control feature\n"
200 	       "  --test-audio-return-channel-control Test the Audio Return Channel Control feature\n"
201 	       "  --test-capability-discovery-and-control Test the Capability Discovery and Control feature\n"
202 	       "  --test-deck-control                 Test the Deck Control feature\n"
203 	       "  --test-device-menu-control          Test the Device Menu Control feature\n"
204 	       "  --test-device-osd-transfer          Test the Device OSD Transfer feature\n"
205 	       "  --test-dynamic-auto-lipsync         Test the Dynamic Auto Lipsync feature\n"
206 	       "  --test-osd-display                  Test the OSD Display feature\n"
207 	       "  --test-one-touch-play               Test the One Touch Play feature\n"
208 	       "  --test-one-touch-record             Test the One Touch Record feature\n"
209 	       "  --test-power-status                 Test the Power Status feature\n"
210 	       "  --test-remote-control-passthrough   Test the Remote Control Passthrough feature\n"
211 	       "  --test-routing-control              Test the Routing Control feature\n"
212 	       "  --test-system-audio-control         Test the System Audio Control feature\n"
213 	       "  --test-system-information           Test the System Information feature\n"
214 	       "  --test-timer-programming            Test the Timer Programming feature\n"
215 	       "  --test-tuner-control                Test the Tuner Control feature\n"
216 	       "  --test-vendor-specific-commands     Test the Vendor Specific Commands feature\n"
217 	       "  --test-standby-resume               Test standby and resume functionality. This will activate\n"
218 	       "                                      testing of Standby, Give Device Power Status and One Touch Play.\n"
219 	       "\n"
220 	       "  -E, --exit-on-fail Exit on the first fail.\n"
221 	       "  -l, --list-tests   List all tests.\n"
222 	       "  -e, --expect <test>=<result>\n"
223 	       "                     Fail if the test gave a different result.\n"
224 	       "  -n, --expect-with-no-warnings <test>=<result>\n"
225 	       "                     Fail if the test gave a different result or if the test generated warnings.\n"
226 	       "  -h, --help         Display this help message\n"
227 	       "  -C, --color <when> Highlight OK/warn/fail/FAIL strings with colors\n"
228 	       "                     <when> can be set to always, never, or auto (the default)\n"
229 	       "  -N, --no-warnings  Turn off warning messages\n"
230 	       "  -s, --skip-info    Skip Driver Info output\n"
231 	       "  -T, --trace        Trace all called ioctls\n"
232 	       "  -v, --verbose      Turn on verbose reporting\n"
233 	       "  --version          Show version information\n"
234 	       "  -w, --wall-clock   Show timestamps as wall-clock time\n"
235 	       "  -S, --show-timestamp Show timestamp of the start of each test\n"
236 	       "  -W, --exit-on-warn Exit on the first warning.\n"
237 	       );
238 }
239 
safename(const char * name)240 std::string safename(const char *name)
241 {
242 	std::string s;
243 	bool not_alnum = false;
244 
245 	while (*name) {
246 		if (isalnum(*name)) {
247 			if (not_alnum && !s.empty())
248 				s += '-';
249 			s += tolower(*name);
250 			not_alnum = false;
251 		} else if (!not_alnum)
252 			not_alnum = true;
253 		name++;
254 	}
255 	return s;
256 }
257 
ts2s(__u64 ts)258 static std::string ts2s(__u64 ts)
259 {
260 	std::string s;
261 	struct timespec now;
262 	struct timeval tv;
263 	struct timeval sub;
264 	struct timeval res;
265 	__u64 diff;
266 	char buf[64];
267 	time_t t;
268 
269 	if (!options[OptWallClock]) {
270 		sprintf(buf, "%llu.%03llus", ts / 1000000000, (ts % 1000000000) / 1000000);
271 		return buf;
272 	}
273 	clock_gettime(CLOCK_MONOTONIC, &now);
274 	gettimeofday(&tv, nullptr);
275 	diff = now.tv_sec * 1000000000ULL + now.tv_nsec - ts;
276 	sub.tv_sec = diff / 1000000000ULL;
277 	sub.tv_usec = (diff % 1000000000ULL) / 1000;
278 	timersub(&tv, &sub, &res);
279 	t = res.tv_sec;
280 	s = ctime(&t);
281 	s = s.substr(0, s.length() - 6);
282 	sprintf(buf, "%03llu", (__u64)res.tv_usec / 1000);
283 	return s + "." + buf;
284 }
285 
current_ts()286 std::string current_ts()
287 {
288 	struct timespec ts;
289 
290 	clock_gettime(CLOCK_MONOTONIC, &ts);
291 	return ts2s(ts.tv_sec * 1000000000ULL + ts.tv_nsec);
292 }
293 
power_status2s(__u8 power_status)294 const char *power_status2s(__u8 power_status)
295 {
296 	switch (power_status) {
297 	case CEC_OP_POWER_STATUS_ON:
298 		return "On";
299 	case CEC_OP_POWER_STATUS_STANDBY:
300 		return "Standby";
301 	case CEC_OP_POWER_STATUS_TO_ON:
302 		return "In transition Standby to On";
303 	case CEC_OP_POWER_STATUS_TO_STANDBY:
304 		return "In transition On to Standby";
305 	default:
306 		return "Unknown";
307 	}
308 }
309 
bcast_system2s(__u8 bcast_system)310 const char *bcast_system2s(__u8 bcast_system)
311 {
312 	switch (bcast_system) {
313 	case CEC_OP_BCAST_SYSTEM_PAL_BG:
314 		return "PAL B/G";
315 	case CEC_OP_BCAST_SYSTEM_SECAM_LQ:
316 		return "SECAM L'";
317 	case CEC_OP_BCAST_SYSTEM_PAL_M:
318 		return "PAL M";
319 	case CEC_OP_BCAST_SYSTEM_NTSC_M:
320 		return "NTSC M";
321 	case CEC_OP_BCAST_SYSTEM_PAL_I:
322 		return "PAL I";
323 	case CEC_OP_BCAST_SYSTEM_SECAM_DK:
324 		return "SECAM DK";
325 	case CEC_OP_BCAST_SYSTEM_SECAM_BG:
326 		return "SECAM B/G";
327 	case CEC_OP_BCAST_SYSTEM_SECAM_L:
328 		return "SECAM L";
329 	case CEC_OP_BCAST_SYSTEM_PAL_DK:
330 		return "PAL DK";
331 	case 31:
332 		return "Other System";
333 	default:
334 		return "Future use";
335 	}
336 }
337 
dig_bcast_system2s(__u8 bcast_system)338 const char *dig_bcast_system2s(__u8 bcast_system)
339 {
340 	switch (bcast_system) {
341 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
342 		return "ARIB generic";
343 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
344 		return "ATSC generic";
345 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
346 		return "DVB generic";
347 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
348 		return "ARIB-BS";
349 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
350 		return "ARIB-CS";
351 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
352 		return "ARIB-T";
353 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
354 		return "ATSC Cable";
355 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
356 		return "ATSC Satellite";
357 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
358 		return "ATSC Terrestrial";
359 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
360 		return "DVB-C";
361 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
362 		return "DVB-S";
363 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
364 		return "DVB S2";
365 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
366 		return "DVB-T";
367 	default:
368 		return "Invalid";
369 	}
370 }
371 
opcode2s(const struct cec_msg * msg)372 std::string opcode2s(const struct cec_msg *msg)
373 {
374 	std::stringstream oss;
375 	__u8 opcode = msg->msg[1];
376 	const char *name;
377 
378 	if (msg->len == 1)
379 		return "MSG_POLL";
380 
381 	if (opcode == CEC_MSG_CDC_MESSAGE) {
382 		__u8 cdc_opcode = msg->msg[4];
383 		name = cec_cdc_opcode2s(cdc_opcode);
384 
385 		if (name)
386 			return name;
387 		oss << "CDC: 0x" << std::hex << static_cast<unsigned>(cdc_opcode);
388 		return oss.str();
389 	}
390 
391 	name = cec_opcode2s(opcode);
392 
393 	if (name)
394 		return name;
395 	oss << "0x" << std::hex << static_cast<unsigned>(opcode);
396 	return oss.str();
397 }
398 
cec_named_ioctl(struct node * node,const char * name,unsigned long int request,void * parm)399 int cec_named_ioctl(struct node *node, const char *name,
400 		    unsigned long int request, void *parm)
401 {
402 	int retval;
403 	int e;
404 	auto msg = static_cast<struct cec_msg *>(parm);
405 	__u8 opcode = 0;
406 	std::string opname;
407 
408 	if (request == CEC_TRANSMIT) {
409 		opcode = cec_msg_opcode(msg);
410 		opname = opcode2s(msg);
411 	}
412 
413 	retval = ioctl(node->fd, request, parm);
414 
415 	if (request == CEC_RECEIVE) {
416 		opcode = cec_msg_opcode(msg);
417 		opname = opcode2s(msg);
418 	}
419 
420 	e = retval == 0 ? 0 : errno;
421 	if (options[OptTrace] && (e || !show_info ||
422 			(request != CEC_TRANSMIT && request != CEC_RECEIVE))) {
423 		if (request == CEC_TRANSMIT)
424 			printf("\t\t%s: %s returned %d (%s)\n",
425 				opname.c_str(), name, retval, strerror(e));
426 		else
427 			printf("\t\t%s returned %d (%s)\n",
428 				name, retval, strerror(e));
429 	}
430 
431 	if (!retval && request == CEC_TRANSMIT &&
432 	    (msg->tx_status & CEC_TX_STATUS_OK) && ((msg->tx_status & CEC_TX_STATUS_MAX_RETRIES))) {
433 		/*
434 		 * Workaround this bug in the CEC framework. This bug was solved
435 		 * in kernel 4.18 but older versions still can produce this incorrect
436 		 * combination of TX flags. If this occurs, then this really means
437 		 * that the transmit went OK, but the wait for the reply was
438 		 * cancelled (e.g. due to the HPD doing down).
439 		 */
440 		msg->tx_status &= ~(CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_ERROR);
441 		if (msg->tx_error_cnt)
442 			msg->tx_error_cnt--;
443 		msg->rx_status = CEC_RX_STATUS_TIMEOUT;
444 		msg->rx_ts = msg->tx_ts;
445 		warn("Both OK and MAX_RETRIES were set in tx_status! Applied workaround.\n");
446 	}
447 
448 	if (!retval && show_info &&
449 	    (request == CEC_TRANSMIT || request == CEC_RECEIVE)) {
450 		printf("\t\t%s: Sequence: %u Length: %u\n",
451 		       opname.c_str(), msg->sequence, msg->len);
452 		if (msg->tx_ts || msg->rx_ts) {
453 			printf("\t\t\t");
454 			if (msg->tx_ts)
455 				printf("Tx Timestamp: %s ", ts2s(msg->tx_ts).c_str());
456 			if (msg->rx_ts)
457 				printf("Rx Timestamp: %s", ts2s(msg->rx_ts).c_str());
458 			printf("\n");
459 			if (msg->tx_ts && msg->rx_ts)
460 				printf("\t\t\tApproximate response time: %u ms\n",
461 				       response_time_ms(msg));
462 		}
463 		if ((msg->tx_status & ~CEC_TX_STATUS_OK) ||
464 		    (msg->rx_status & ~CEC_RX_STATUS_OK))
465 			printf("\t\t\tStatus: %s\n", cec_status2s(*msg).c_str());
466 		if (msg->tx_status & CEC_TX_STATUS_TIMEOUT)
467 			warn("CEC_TX_STATUS_TIMEOUT was set, should not happen.\n");
468 	}
469 
470 	if (!retval) {
471 		__u8 la = cec_msg_initiator(msg);
472 
473 		/*
474 		 * TODO: The logic here might need to be re-evaluated.
475 		 *
476 		 * Currently a message is registered as recognized if
477 		 *     - We receive a reply that is not Feature Abort with
478 		 *       [Unrecognized Opcode] or [Undetermined]
479 		 *     - We manually receive (CEC_RECEIVE) and get a Feature Abort
480 		 *       with reason different than [Unrecognized Opcode] or
481 		 *       [Undetermined]
482 		 */
483 		if (request == CEC_TRANSMIT && msg->timeout > 0 &&
484 		    cec_msg_initiator(msg) != CEC_LOG_ADDR_UNREGISTERED &&
485 		    cec_msg_destination(msg) != CEC_LOG_ADDR_BROADCAST &&
486 		    (msg->tx_status & CEC_TX_STATUS_OK) &&
487 		    (msg->rx_status & CEC_RX_STATUS_OK)) {
488 			if (cec_msg_status_is_abort(msg) &&
489 			    (abort_reason(msg) == CEC_OP_ABORT_UNRECOGNIZED_OP ||
490 			     abort_reason(msg) == CEC_OP_ABORT_UNDETERMINED))
491 				node->remote[la].unrecognized_op[opcode] = true;
492 			else
493 				node->remote[la].recognized_op[opcode] = true;
494 		}
495 
496 		if (request == CEC_RECEIVE &&
497 		    cec_msg_initiator(msg) != CEC_LOG_ADDR_UNREGISTERED &&
498 		    cec_msg_opcode(msg) == CEC_MSG_FEATURE_ABORT) {
499 			__u8 abort_msg = msg->msg[2];
500 
501 			if (abort_reason(msg) == CEC_OP_ABORT_UNRECOGNIZED_OP ||
502 			    abort_reason(msg) == CEC_OP_ABORT_UNDETERMINED)
503 				node->remote[la].unrecognized_op[abort_msg] = true;
504 			else
505 				node->remote[la].recognized_op[abort_msg] = true;
506 		}
507 	}
508 
509 	return retval == -1 ? e : (retval ? -1 : 0);
510 }
511 
result_name(int res,bool show_colors)512 const char *result_name(int res, bool show_colors)
513 {
514 	switch (res) {
515 	case OK_NOT_SUPPORTED:
516 		return show_colors ? COLOR_GREEN("OK") " (Not Supported)" : "OK (Not Supported)";
517 	case OK_PRESUMED:
518 		return show_colors ? COLOR_GREEN("OK") " (Presumed)" : "OK (Presumed)";
519 	case OK_REFUSED:
520 		return show_colors ? COLOR_GREEN("OK") " (Refused)" : "OK (Refused)";
521 	case OK_UNEXPECTED:
522 		return show_colors ? COLOR_GREEN("OK") " (Unexpected)" : "OK (Unexpected)";
523 	case OK_EXPECTED_FAIL:
524 		return show_colors ? COLOR_GREEN("OK") " (Expected Failure)" : "OK (Expected Failure)";
525 	case OK:
526 		return show_colors ? COLOR_GREEN("OK") : "OK";
527 	default:
528 		return show_colors ? COLOR_RED("FAIL") : "FAIL";
529 	}
530 }
531 
ok(int res)532 const char *ok(int res)
533 {
534 	const char *res_name = result_name(res, show_colors);
535 
536 	switch (res) {
537 	case OK_NOT_SUPPORTED:
538 	case OK_PRESUMED:
539 	case OK_REFUSED:
540 	case OK_UNEXPECTED:
541 	case OK_EXPECTED_FAIL:
542 	case OK:
543 		res = OK;
544 		break;
545 	default:
546 		break;
547 	}
548 	tests_total++;
549 	if (res)
550 		app_result = res;
551 	else
552 		tests_ok++;
553 	return res_name;
554 }
555 
check_0(const void * p,int len)556 int check_0(const void *p, int len)
557 {
558 	const __u8 *q = static_cast<const __u8 *>(p);
559 
560 	while (len--)
561 		if (*q++)
562 			return 1;
563 	return 0;
564 }
565 
566 #define TX_WAIT_FOR_HPD		10
567 #define TX_WAIT_FOR_HPD_RETURN	30
568 
wait_for_hpd(struct node * node,bool send_image_view_on)569 static bool wait_for_hpd(struct node *node, bool send_image_view_on)
570 {
571 	int fd = node->fd;
572 	int flags = fcntl(node->fd, F_GETFL);
573 	time_t t = time(nullptr);
574 
575 	fcntl(node->fd, F_SETFL, flags | O_NONBLOCK);
576 	for (;;) {
577 		struct timeval tv = { 1, 0 };
578 		fd_set ex_fds;
579 		int res;
580 
581 		FD_ZERO(&ex_fds);
582 		FD_SET(fd, &ex_fds);
583 		res = select(fd + 1, nullptr, nullptr, &ex_fds, &tv);
584 		if (res < 0) {
585 			fail("select failed with error %d\n", errno);
586 			return false;
587 		}
588 		if (FD_ISSET(fd, &ex_fds)) {
589 			struct cec_event ev;
590 
591 			res = doioctl(node, CEC_DQEVENT, &ev);
592 			if (!res && ev.event == CEC_EVENT_STATE_CHANGE &&
593 			    ev.state_change.log_addr_mask)
594 				break;
595 		}
596 
597 		if (send_image_view_on && time(nullptr) - t > TX_WAIT_FOR_HPD) {
598 			struct cec_msg image_view_on_msg;
599 
600 			// So the HPD is gone (possibly due to a standby), but
601 			// some TVs still have a working CEC bus, so send Image
602 			// View On to attempt to wake it up again.
603 			cec_msg_init(&image_view_on_msg, CEC_LOG_ADDR_UNREGISTERED,
604 				     CEC_LOG_ADDR_TV);
605 			cec_msg_image_view_on(&image_view_on_msg);
606 			doioctl(node, CEC_TRANSMIT, &image_view_on_msg);
607 			send_image_view_on = false;
608 		}
609 
610 		if (time(nullptr) - t > TX_WAIT_FOR_HPD + TX_WAIT_FOR_HPD_RETURN) {
611 			fail("timed out after %d s waiting for HPD to return\n",
612 			     TX_WAIT_FOR_HPD + TX_WAIT_FOR_HPD_RETURN);
613 			return false;
614 		}
615 	}
616 	fcntl(node->fd, F_SETFL, flags);
617 	return true;
618 }
619 
transmit_timeout(struct node * node,struct cec_msg * msg,unsigned timeout)620 bool transmit_timeout(struct node *node, struct cec_msg *msg, unsigned timeout)
621 {
622 	struct cec_msg original_msg = *msg;
623 	bool retried = false;
624 	int res;
625 
626 	msg->timeout = timeout;
627 retry:
628 	res = doioctl(node, CEC_TRANSMIT, msg);
629 	if (res == ENODEV) {
630 		printf("Device was disconnected.\n");
631 		std::exit(EXIT_FAILURE);
632 	}
633 	if (res == ENONET) {
634 		if (retried) {
635 			fail("HPD was lost twice, that can't be right\n");
636 			return false;
637 		}
638 		warn("HPD was lost, wait for it to come up again.\n");
639 
640 		if (!wait_for_hpd(node, !(node->caps & CEC_CAP_NEEDS_HPD) &&
641 				  cec_msg_destination(msg) == CEC_LOG_ADDR_TV))
642 			return false;
643 
644 		retried = true;
645 		goto retry;
646 	}
647 
648 	if (res || !(msg->tx_status & CEC_TX_STATUS_OK))
649 		return false;
650 
651 	if (((msg->rx_status & CEC_RX_STATUS_OK) || (msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT))
652 	    && response_time_ms(msg) > reply_threshold)
653 		warn("Waited %4ums for %s to msg %s.\n",
654 		     response_time_ms(msg),
655 		     (msg->rx_status & CEC_RX_STATUS_OK) ? "reply" : "Feature Abort",
656 		     opcode2s(&original_msg).c_str());
657 
658 	if (!cec_msg_status_is_abort(msg))
659 		return true;
660 
661 	if (cec_msg_is_broadcast(&original_msg)) {
662 		fail("Received Feature Abort in reply to broadcast message\n");
663 		return false;
664 	}
665 
666 	const char *reason;
667 
668 	switch (abort_reason(msg)) {
669 	case CEC_OP_ABORT_UNRECOGNIZED_OP:
670 	case CEC_OP_ABORT_UNDETERMINED:
671 		return true;
672 	case CEC_OP_ABORT_INVALID_OP:
673 		reason = "Invalid operand";
674 		break;
675 	case CEC_OP_ABORT_NO_SOURCE:
676 		reason = "Cannot provide source";
677 		break;
678 	case CEC_OP_ABORT_REFUSED:
679 		reason = "Refused";
680 		break;
681 	case CEC_OP_ABORT_INCORRECT_MODE:
682 		reason = "Incorrect mode";
683 		break;
684 	default:
685 		reason = "Unknown";
686 		fail_once("Unknown Feature Abort reason (0x%02x)\n", abort_reason(msg));
687 		break;
688 	}
689 	info("Opcode %s was replied to with Feature Abort [%s]\n",
690 	     opcode2s(&original_msg).c_str(), reason);
691 
692 	return true;
693 }
694 
util_receive(struct node * node,unsigned la,unsigned timeout,struct cec_msg * msg,__u8 sent_msg,__u8 reply1,__u8 reply2)695 int util_receive(struct node *node, unsigned la, unsigned timeout,
696 		 struct cec_msg *msg, __u8 sent_msg, __u8 reply1, __u8 reply2)
697 {
698 	unsigned ts_start = get_ts_ms();
699 
700 	while (get_ts_ms() - ts_start < timeout) {
701 		memset(msg, 0, sizeof(*msg));
702 		msg->timeout = 20;
703 		if (doioctl(node, CEC_RECEIVE, msg))
704 			continue;
705 		if (cec_msg_initiator(msg) != la)
706 			continue;
707 
708 		if (msg->msg[1] == CEC_MSG_FEATURE_ABORT) {
709 			__u8 reason, abort_msg;
710 
711 			cec_ops_feature_abort(msg, &abort_msg, &reason);
712 			if (abort_msg != sent_msg)
713 				continue;
714 			return 0;
715 		}
716 
717 		if (msg->msg[1] == reply1 || (reply2 && msg->msg[1] == reply2))
718 			return msg->msg[1];
719 	}
720 
721 	return -1;
722 }
723 
poll_remote_devs(struct node * node)724 static int poll_remote_devs(struct node *node)
725 {
726 	unsigned retries = 0;
727 
728 	node->remote_la_mask = 0;
729 	if (!(node->caps & CEC_CAP_TRANSMIT))
730 		return 0;
731 
732 	for (unsigned i = 0; i < 15; i++) {
733 		struct cec_msg msg;
734 
735 		cec_msg_init(&msg, node->log_addr[0], i);
736 		fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
737 
738 		if (msg.tx_status & CEC_TX_STATUS_OK) {
739 			node->remote_la_mask |= 1 << i;
740 			retries = 0;
741 		} else if (msg.tx_status & CEC_TX_STATUS_NACK) {
742 			retries = 0;
743 		} else {
744 			if (!(msg.tx_status & CEC_TX_STATUS_ARB_LOST))
745 				warn("retry poll due to unexpected status: %s\n",
746 				     cec_status2s(msg).c_str());
747 			retries++;
748 			fail_on_test(retries > 10);
749 			i--;
750 		}
751 	}
752 	return 0;
753 }
754 
topology_probe_device(struct node * node,unsigned i,unsigned la)755 static void topology_probe_device(struct node *node, unsigned i, unsigned la)
756 {
757 	struct cec_msg msg;
758 	bool unknown;
759 
760 	printf("\tSystem Information for device %d (%s) from device %d (%s):\n",
761 	       i, cec_la2s(i), la, cec_la2s(la));
762 
763 	cec_msg_init(&msg, la, i);
764 	cec_msg_get_cec_version(&msg, true);
765 	unknown = !transmit_timeout(node, &msg) || timed_out_or_abort(&msg);
766 	printf("\t\tCEC Version                : ");
767 	if (unknown) {
768 		printf("%s\n", cec_status2s(msg).c_str());
769 		node->remote[i].cec_version = CEC_OP_CEC_VERSION_1_4;
770 	}
771 	/* This needs to be kept in sync with newer CEC versions */
772 	else {
773 		node->remote[i].cec_version = msg.msg[2];
774 		if (msg.msg[2] < CEC_OP_CEC_VERSION_1_3A) {
775 			printf("< 1.3a (%x)\n", msg.msg[2]);
776 			warn("The reported CEC version is less than 1.3a. The device will be tested as a CEC 1.3a compliant device.\n");
777 		}
778 		else if (msg.msg[2] > CEC_OP_CEC_VERSION_2_0) {
779 			printf("> 2.0 (%x)\n", msg.msg[2]);
780 			warn("The reported CEC version is greater than 2.0. The device will be tested as a CEC 2.0 compliant device.\n");
781 		}
782 		else
783 			printf("%s\n", cec_version2s(msg.msg[2]));
784 	}
785 
786 	cec_msg_init(&msg, la, i);
787 	cec_msg_give_physical_addr(&msg, true);
788 	unknown = !transmit_timeout(node, &msg) || timed_out_or_abort(&msg);
789 	printf("\t\tPhysical Address           : ");
790 	if (unknown) {
791 		printf("%s\n", cec_status2s(msg).c_str());
792 		node->remote[i].phys_addr = CEC_PHYS_ADDR_INVALID;
793 	}
794 	else {
795 		node->remote[i].phys_addr = (msg.msg[2] << 8) | msg.msg[3];
796 		printf("%x.%x.%x.%x\n",
797 		       cec_phys_addr_exp(node->remote[i].phys_addr));
798 		node->remote[i].prim_type = msg.msg[4];
799 		printf("\t\tPrimary Device Type        : %s\n",
800 		       cec_prim_type2s(node->remote[i].prim_type));
801 	}
802 
803 	cec_msg_init(&msg, la, i);
804 	cec_msg_give_device_vendor_id(&msg, true);
805 	unknown = !transmit_timeout(node, &msg) || timed_out_or_abort(&msg);
806 	printf("\t\tVendor ID                  : ");
807 	if (unknown) {
808 		printf("%s\n", cec_status2s(msg).c_str());
809 		node->remote[i].vendor_id = CEC_VENDOR_ID_NONE;
810 	} else {
811 		__u32 vendor_id = (msg.msg[2] << 16) | (msg.msg[3] << 8) | msg.msg[4];
812 		node->remote[i].vendor_id = vendor_id;
813 
814 		const char *vendor = cec_vendor2s(vendor_id);
815 
816 		if (vendor)
817 			printf("0x%06x (%s)\n", node->remote[i].vendor_id, vendor);
818 		else
819 			printf("0x%06x, %u\n", vendor_id, vendor_id);
820 	}
821 
822 	cec_msg_init(&msg, la, i);
823 	cec_msg_give_osd_name(&msg, true);
824 	unknown = !transmit_timeout(node, &msg) || timed_out_or_abort(&msg);
825 	printf("\t\tOSD Name                   : ");
826 	if (unknown) {
827 		printf("%s\n", cec_status2s(msg).c_str());
828 	} else {
829 		cec_ops_set_osd_name(&msg, node->remote[i].osd_name);
830 		printf("'%s'\n", node->remote[i].osd_name);
831 	}
832 
833 	cec_msg_init(&msg, la, i);
834 	cec_msg_get_menu_language(&msg, true);
835 	if (transmit_timeout(node, &msg) && !timed_out_or_abort(&msg)) {
836 		cec_ops_set_menu_language(&msg, node->remote[i].language);
837 		printf("\t\tMenu Language              : %s\n",
838 		       node->remote[i].language);
839 	}
840 
841 	cec_msg_init(&msg, la, i);
842 	cec_msg_give_device_power_status(&msg, true);
843 	unknown = !transmit_timeout(node, &msg) || timed_out_or_abort(&msg);
844 	printf("\t\tPower Status               : ");
845 	if (unknown) {
846 		printf("%s\n", cec_status2s(msg).c_str());
847 	} else {
848 		__u8 pwr;
849 
850 		cec_ops_report_power_status(&msg, &pwr);
851 		if (pwr >= 4)
852 			printf("Invalid\n");
853 		else {
854 			node->remote[i].has_power_status = true;
855 			node->remote[i].in_standby = pwr != CEC_OP_POWER_STATUS_ON;
856 			printf("%s\n", power_status2s(pwr));
857 		}
858 	}
859 
860 	if (node->remote[i].cec_version < CEC_OP_CEC_VERSION_2_0)
861 		return;
862 	cec_msg_init(&msg, la, i);
863 	cec_msg_give_features(&msg, true);
864 	if (transmit_timeout(node, &msg) && !timed_out_or_abort(&msg)) {
865 		/* RC Profile and Device Features are assumed to be 1 byte. As of CEC 2.0 only
866 		   1 byte is used, but this might be extended in future versions. */
867 		__u8 cec_version, all_device_types;
868 		const __u8 *rc_profile = nullptr, *dev_features = nullptr;
869 
870 		cec_ops_report_features(&msg, &cec_version, &all_device_types,
871 					&rc_profile, &dev_features);
872 		if (rc_profile == nullptr || dev_features == nullptr)
873 			return;
874 		node->remote[i].rc_profile = *rc_profile;
875 		node->remote[i].dev_features = *dev_features;
876 		node->remote[i].all_device_types = all_device_types;
877 		node->remote[i].source_has_arc_rx =
878 			(*dev_features & CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX) != 0;
879 		node->remote[i].sink_has_arc_tx =
880 			(*dev_features & CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX) != 0;
881 		node->remote[i].has_aud_rate =
882 			(*dev_features & CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE) != 0;
883 		node->remote[i].has_deck_ctl =
884 			(*dev_features & CEC_OP_FEAT_DEV_HAS_DECK_CONTROL) != 0;
885 		node->remote[i].has_rec_tv =
886 			(*dev_features & CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN) != 0;
887 	}
888 }
889 
main(int argc,char ** argv)890 int main(int argc, char **argv)
891 {
892 	std::string device;
893 	const char *driver = nullptr;
894 	const char *adapter = nullptr;
895 	char short_options[26 * 2 * 2 + 1];
896 	int remote_la = -1;
897 	bool test_remote = false;
898 	unsigned test_tags = 0;
899 	int idx = 0;
900 	int fd = -1;
901 	int ch;
902 	int i;
903 	const char *env_media_apps_color = getenv("MEDIA_APPS_COLOR");
904 
905 	srandom(time(nullptr));
906 	if (!env_media_apps_color || !strcmp(env_media_apps_color, "auto"))
907 		show_colors = isatty(STDOUT_FILENO);
908 	else if (!strcmp(env_media_apps_color, "always"))
909 		show_colors = true;
910 	else if (!strcmp(env_media_apps_color, "never"))
911 		show_colors = false;
912 	else {
913 		fprintf(stderr,
914 			"cec-compliance: invalid value for MEDIA_APPS_COLOR environment variable\n");
915 	}
916 
917 	collectTests();
918 
919 	for (i = 0; long_options[i].name; i++) {
920 		if (!isalpha(long_options[i].val))
921 			continue;
922 		short_options[idx++] = long_options[i].val;
923 		if (long_options[i].has_arg == required_argument) {
924 			short_options[idx++] = ':';
925 		} else if (long_options[i].has_arg == optional_argument) {
926 			short_options[idx++] = ':';
927 			short_options[idx++] = ':';
928 		}
929 	}
930 	while (true) {
931 		int option_index = 0;
932 
933 		short_options[idx] = 0;
934 		ch = getopt_long(argc, argv, short_options,
935 				 long_options, &option_index);
936 		if (ch == -1)
937 			break;
938 
939 		options[ch] = 1;
940 		if (!option_index) {
941 			for (i = 0; long_options[i].val; i++) {
942 				if (long_options[i].val == ch) {
943 					option_index = i;
944 					break;
945 				}
946 			}
947 		}
948 		if (long_options[option_index].has_arg == optional_argument &&
949 		    !optarg && argv[optind] && argv[optind][0] != '-')
950 			optarg = argv[optind++];
951 
952 		switch (ch) {
953 		case OptHelp:
954 			usage();
955 			return 0;
956 		case OptSetDevice:
957 			device = optarg;
958 			if (device[0] >= '0' && device[0] <= '9' && device.length() <= 3) {
959 				static char newdev[20];
960 
961 				sprintf(newdev, "/dev/cec%s", optarg);
962 				device = newdev;
963 			}
964 			break;
965 		case OptSetDriver:
966 			driver = optarg;
967 			break;
968 		case OptSetAdapter:
969 			adapter = optarg;
970 			break;
971 		case OptReplyThreshold:
972 			reply_threshold = strtoul(optarg, nullptr, 0);
973 			break;
974 		case OptTimeout:
975 			long_timeout = strtoul(optarg, nullptr, 0);
976 			break;
977 		case OptColor:
978 			if (!strcmp(optarg, "always"))
979 				show_colors = true;
980 			else if (!strcmp(optarg, "never"))
981 				show_colors = false;
982 			else if (!strcmp(optarg, "auto"))
983 				show_colors = isatty(STDOUT_FILENO);
984 			else {
985 				usage();
986 				std::exit(EXIT_FAILURE);
987 			}
988 			break;
989 		case OptNoWarnings:
990 			show_warnings = false;
991 			break;
992 		case OptExitOnFail:
993 			exit_on_fail = true;
994 			break;
995 		case OptExitOnWarn:
996 			exit_on_warn = true;
997 			break;
998 		case OptExpect:
999 		case OptExpectWithNoWarnings:
1000 			if (setExpectedResult(optarg, ch == OptExpectWithNoWarnings)) {
1001 				fprintf(stderr, "invalid -e argument %s\n", optarg);
1002 				usage();
1003 				return 1;
1004 			}
1005 			break;
1006 		case OptRemote:
1007 			if (optarg) {
1008 				remote_la = strtoul(optarg, nullptr, 0);
1009 				if (remote_la < 0 || remote_la > 15) {
1010 					fprintf(stderr, "--test: invalid remote logical address\n");
1011 					usage();
1012 					return 1;
1013 				}
1014 			}
1015 			test_remote = true;
1016 			break;
1017 		case OptWallClock:
1018 			break;
1019 		case OptVerbose:
1020 			show_info = true;
1021 			break;
1022 		case OptVersion:
1023 			printf("cec-compliance %s%s\n",
1024 			       PACKAGE_VERSION, STRING(GIT_COMMIT_CNT));
1025 			if (strlen(STRING(GIT_SHA)))
1026 				printf("cec-compliance SHA: %s %s\n",
1027 				       STRING(GIT_SHA), STRING(GIT_COMMIT_DATE));
1028 			std::exit(EXIT_SUCCESS);
1029 		case ':':
1030 			fprintf(stderr, "Option '%s' requires a value\n",
1031 				argv[optind]);
1032 			usage();
1033 			return 1;
1034 		case '?':
1035 			if (argv[optind])
1036 				fprintf(stderr, "Unknown argument '%s'\n", argv[optind]);
1037 			usage();
1038 			return 1;
1039 		}
1040 	}
1041 	if (optind < argc) {
1042 		printf("unknown arguments: ");
1043 		while (optind < argc)
1044 			printf("%s ", argv[optind++]);
1045 		printf("\n");
1046 		usage();
1047 		return 1;
1048 	}
1049 
1050 	if (device.empty() && (driver || adapter)) {
1051 		device = cec_device_find(driver, adapter);
1052 		if (device.empty()) {
1053 			fprintf(stderr,
1054 				"Could not find a CEC device for the given driver/adapter combination\n");
1055 			std::exit(EXIT_FAILURE);
1056 		}
1057 	}
1058 	if (device.empty())
1059 		device = "/dev/cec0";
1060 
1061 	if ((fd = open(device.c_str(), O_RDWR)) < 0) {
1062 		fprintf(stderr, "Failed to open %s: %s\n", device.c_str(),
1063 			strerror(errno));
1064 		std::exit(EXIT_FAILURE);
1065 	}
1066 
1067 	struct node node = { };
1068 	struct cec_caps caps = { };
1069 
1070 	node.fd = fd;
1071 	node.device = device.c_str();
1072 	doioctl(&node, CEC_ADAP_G_CAPS, &caps);
1073 	node.caps = caps.capabilities;
1074 	node.available_log_addrs = caps.available_log_addrs;
1075 
1076 	if (options[OptTestAudioRateControl])
1077 		test_tags |= TAG_AUDIO_RATE_CONTROL;
1078 	if (options[OptTestARCControl])
1079 		test_tags |= TAG_ARC_CONTROL;
1080 	if (options[OptTestCapDiscoveryControl])
1081 		test_tags |= TAG_CAP_DISCOVERY_CONTROL;
1082 	if (options[OptTestDeckControl])
1083 		test_tags |= TAG_DECK_CONTROL;
1084 	if (options[OptTestDeviceMenuControl])
1085 		test_tags |= TAG_DEVICE_MENU_CONTROL;
1086 	if (options[OptTestDeviceOSDTransfer])
1087 		test_tags |= TAG_DEVICE_OSD_TRANSFER;
1088 	if (options[OptTestDynamicAutoLipsync])
1089 		test_tags |= TAG_DYNAMIC_AUTO_LIPSYNC;
1090 	if (options[OptTestOSDDisplay])
1091 		test_tags |= TAG_OSD_DISPLAY;
1092 	if (options[OptTestOneTouchPlay])
1093 		test_tags |= TAG_ONE_TOUCH_PLAY;
1094 	if (options[OptTestOneTouchRecord])
1095 		test_tags |= TAG_ONE_TOUCH_RECORD;
1096 	if (options[OptTestPowerStatus])
1097 		test_tags |= TAG_POWER_STATUS;
1098 	if (options[OptTestRemoteControlPassthrough])
1099 		test_tags |= TAG_REMOTE_CONTROL_PASSTHROUGH;
1100 	if (options[OptTestRoutingControl])
1101 		test_tags |= TAG_ROUTING_CONTROL;
1102 	if (options[OptTestSystemAudioControl])
1103 		test_tags |= TAG_SYSTEM_AUDIO_CONTROL;
1104 	if (options[OptTestSystemInformation])
1105 		test_tags |= TAG_SYSTEM_INFORMATION;
1106 	if (options[OptTestTimerProgramming])
1107 		test_tags |= TAG_TIMER_PROGRAMMING;
1108 	if (options[OptTestTunerControl])
1109 		test_tags |= TAG_TUNER_CONTROL;
1110 	if (options[OptTestVendorSpecificCommands])
1111 		test_tags |= TAG_VENDOR_SPECIFIC_COMMANDS;
1112 	/* When code is added to the Standby/Resume test for waking up
1113 	   other devices than TVs, the necessary tags should be added
1114 	   here (probably Routing Control and/or RC Passthrough) */
1115 	if (options[OptTestStandbyResume])
1116 		test_tags |= TAG_POWER_STATUS | TAG_STANDBY_RESUME;
1117 
1118 	if (!test_tags && !options[OptTestCore])
1119 		test_tags = TAG_ALL;
1120 
1121 	if (options[OptSkipTestAudioRateControl])
1122 		test_tags &= ~TAG_AUDIO_RATE_CONTROL;
1123 	if (options[OptSkipTestARCControl])
1124 		test_tags &= ~TAG_ARC_CONTROL;
1125 	if (options[OptSkipTestCapDiscoveryControl])
1126 		test_tags &= ~TAG_CAP_DISCOVERY_CONTROL;
1127 	if (options[OptSkipTestDeckControl])
1128 		test_tags &= ~TAG_DECK_CONTROL;
1129 	if (options[OptSkipTestDeviceMenuControl])
1130 		test_tags &= ~TAG_DEVICE_MENU_CONTROL;
1131 	if (options[OptSkipTestDeviceOSDTransfer])
1132 		test_tags &= ~TAG_DEVICE_OSD_TRANSFER;
1133 	if (options[OptSkipTestDynamicAutoLipsync])
1134 		test_tags &= ~TAG_DYNAMIC_AUTO_LIPSYNC;
1135 	if (options[OptSkipTestOSDDisplay])
1136 		test_tags &= ~TAG_OSD_DISPLAY;
1137 	if (options[OptSkipTestOneTouchPlay])
1138 		test_tags &= ~TAG_ONE_TOUCH_PLAY;
1139 	if (options[OptSkipTestOneTouchRecord])
1140 		test_tags &= ~TAG_ONE_TOUCH_RECORD;
1141 	if (options[OptSkipTestPowerStatus])
1142 		test_tags &= ~TAG_POWER_STATUS;
1143 	if (options[OptSkipTestRemoteControlPassthrough])
1144 		test_tags &= ~TAG_REMOTE_CONTROL_PASSTHROUGH;
1145 	if (options[OptSkipTestRoutingControl])
1146 		test_tags &= ~TAG_ROUTING_CONTROL;
1147 	if (options[OptSkipTestSystemAudioControl])
1148 		test_tags &= ~TAG_SYSTEM_AUDIO_CONTROL;
1149 	if (options[OptSkipTestSystemInformation])
1150 		test_tags &= ~TAG_SYSTEM_INFORMATION;
1151 	if (options[OptSkipTestTimerProgramming])
1152 		test_tags &= ~TAG_TIMER_PROGRAMMING;
1153 	if (options[OptSkipTestTunerControl])
1154 		test_tags &= ~TAG_TUNER_CONTROL;
1155 	if (options[OptSkipTestVendorSpecificCommands])
1156 		test_tags &= ~TAG_VENDOR_SPECIFIC_COMMANDS;
1157 	if (options[OptSkipTestStandbyResume])
1158 		test_tags &= ~(TAG_POWER_STATUS | TAG_STANDBY_RESUME);
1159 
1160 	if (options[OptInteractive])
1161 		test_tags |= TAG_INTERACTIVE;
1162 
1163 	if (strlen(STRING(GIT_SHA)))
1164 		printf("cec-compliance SHA                 : %s %s\n",
1165 		       STRING(GIT_SHA), STRING(GIT_COMMIT_DATE));
1166 
1167 	node.phys_addr = CEC_PHYS_ADDR_INVALID;
1168 	doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &node.phys_addr);
1169 
1170 	struct cec_log_addrs laddrs = { };
1171 	doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1172 
1173 	if (node.phys_addr == CEC_PHYS_ADDR_INVALID &&
1174 	    !(node.caps & (CEC_CAP_PHYS_ADDR | CEC_CAP_NEEDS_HPD)) &&
1175 	    laddrs.num_log_addrs) {
1176 		struct cec_msg msg;
1177 
1178 		/*
1179 		 * Special corner case: if PA is invalid, then you can still try
1180 		 * to wake up a TV.
1181 		 */
1182 		cec_msg_init(&msg, CEC_LOG_ADDR_UNREGISTERED, CEC_LOG_ADDR_TV);
1183 
1184 		cec_msg_image_view_on(&msg);
1185 		fail_on_test(doioctl(&node, CEC_TRANSMIT, &msg));
1186 		if (msg.tx_status & CEC_TX_STATUS_OK) {
1187 			time_t cnt = 0;
1188 
1189 			while (cnt++ <= long_timeout) {
1190 				fail_on_test(doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &node.phys_addr));
1191 				if (node.phys_addr != CEC_PHYS_ADDR_INVALID) {
1192 					doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1193 					break;
1194 				}
1195 				sleep(1);
1196 			}
1197 		}
1198 
1199 	}
1200 
1201 	if (options[OptSkipInfo]) {
1202 		printf("\n");
1203 	} else {
1204 		struct cec_connector_info conn_info = {};
1205 
1206 		doioctl(&node, CEC_ADAP_G_CONNECTOR_INFO, &conn_info);
1207 
1208 		cec_driver_info(caps, laddrs, node.phys_addr, conn_info);
1209 	}
1210 
1211 	if (options[OptListTests]) {
1212 		printf("\nAvailable Tests:\n\n");
1213 		listTests();
1214 		printf("\n");
1215 		printf("Possible test results:\n"
1216 		       "\t0 = OK                  Supported correctly by the device.\n"
1217 		       "\t1 = FAIL                Failed and was expected to be supported by this device.\n"
1218 		       "\t2 = OK (Presumed)       Presumably supported.  Manually check to confirm.\n"
1219 		       "\t3 = OK (Not Supported)  Not supported and not mandatory for the device.\n"
1220 		       "\t4 = OK (Refused)        Supported by the device, but was refused.\n"
1221 		       "\t5 = OK (Unexpected)     Supported correctly but is not expected to be supported for this device.\n");
1222 	}
1223 
1224 	bool missing_pa = node.phys_addr == CEC_PHYS_ADDR_INVALID && (node.caps & CEC_CAP_PHYS_ADDR);
1225 	bool missing_la = laddrs.num_log_addrs == 0 && (node.caps & CEC_CAP_LOG_ADDRS);
1226 
1227 	if (missing_la || missing_pa)
1228 		printf("\n");
1229 	if (missing_pa)
1230 		fprintf(stderr, "FAIL: missing physical address, use cec-ctl to configure this\n");
1231 	if (missing_la)
1232 		fprintf(stderr, "FAIL: missing logical address(es), use cec-ctl to configure this\n");
1233 	if (missing_la || missing_pa)
1234 		std::exit(EXIT_FAILURE);
1235 
1236 	if (!options[OptSkipInfo]) {
1237 		printf("\nCompliance test for %s device %s:\n\n",
1238 		       caps.driver, device.c_str());
1239 		printf("    The test results mean the following:\n"
1240 		       "        OK                    Supported correctly by the device.\n"
1241 		       "        OK (Not Supported)    Not supported and not mandatory for the device.\n"
1242 		       "        OK (Presumed)         Presumably supported.  Manually check to confirm.\n"
1243 		       "        OK (Unexpected)       Supported correctly but is not expected to be supported for this device.\n"
1244 		       "        OK (Refused)          Supported by the device, but was refused.\n"
1245 		       "        OK (Expected Failure) Failed but this was expected (see -e option).\n"
1246 		       "        FAIL                  Failed and was expected to be supported by this device.\n\n");
1247 	}
1248 
1249 	node.has_cec20 = laddrs.cec_version >= CEC_OP_CEC_VERSION_2_0;
1250 	node.num_log_addrs = laddrs.num_log_addrs;
1251 	memcpy(node.log_addr, laddrs.log_addr, laddrs.num_log_addrs);
1252 	node.adap_la_mask = laddrs.log_addr_mask;
1253 	node.current_time = time(nullptr);
1254 
1255 	printf("Find remote devices:\n");
1256 	printf("\tPolling: %s\n", ok(poll_remote_devs(&node)));
1257 
1258 	if (!node.remote_la_mask) {
1259 		printf("\nFAIL: No remote devices found, exiting.\n");
1260 		std::exit(EXIT_FAILURE);
1261 	}
1262 
1263 	if (options[OptTestAdapter])
1264 		testAdapter(node, laddrs, device.c_str());
1265 	printf("\n");
1266 
1267 	printf("Network topology:\n");
1268 	for (unsigned i = 0; i < 15; i++)
1269 		if (node.remote_la_mask & (1 << i))
1270 			topology_probe_device(&node, i, node.log_addr[0]);
1271 	printf("\n");
1272 
1273 	if (options[OptTestFuzzing] && remote_la >= 0)
1274 		std::exit(testFuzzing(node, laddrs.log_addr[0], remote_la));
1275 
1276 	unsigned remote_la_mask = node.remote_la_mask;
1277 
1278 	if (remote_la >= 0)
1279 		remote_la_mask = 1 << remote_la;
1280 
1281 	if (test_remote) {
1282 		for (unsigned i = 0; i < node.num_log_addrs; i++) {
1283 			unsigned from = node.log_addr[i];
1284 			node.prim_devtype = laddrs.primary_device_type[i];
1285 
1286 			for (unsigned to = 0; to <= 15; to++)
1287 				if (!(node.adap_la_mask & (1 << to)) &&
1288 				    (remote_la_mask & (1 << to)))
1289 					testRemote(&node, from, to, test_tags,
1290 						   options[OptInteractive], options[OptShowTimestamp]);
1291 		}
1292 	}
1293 
1294 	/* Final test report */
1295 
1296 	close(fd);
1297 
1298 	printf("Total for %s device %s: %d, Succeeded: %d, Failed: %d, Warnings: %d\n",
1299 	       caps.driver, device.c_str(),
1300 	       tests_total, tests_ok, tests_total - tests_ok, warnings);
1301 	std::exit(app_result);
1302 }
1303