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