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