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