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 <cstring>
7 
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <inttypes.h>
12 #include <getopt.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/time.h>
16 #include <dirent.h>
17 #include <fcntl.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <sys/ioctl.h>
21 #include <stdarg.h>
22 #include <ctime>
23 #include <cerrno>
24 #include <string>
25 #include <vector>
26 #include <map>
27 #include <algorithm>
28 #include <linux/cec-funcs.h>
29 #include "cec-htng-funcs.h"
30 #include "cec-log.h"
31 #include "cec-parse.h"
32 
33 #ifdef __ANDROID__
34 #include <android-config.h>
35 #else
36 #include <config.h>
37 #endif
38 
39 #include "cec-ctl.h"
40 #include <sys/cdefs.h>
41 
42 static struct timespec start_monotonic;
43 static struct timeval start_timeofday;
44 static bool ignore_la[16];
45 
46 #define POLL_FAKE_OPCODE 256
47 static unsigned short ignore_opcode[257];
48 
49 static char options[512];
50 
51 #define VENDOR_EXTRA \
52 	"  --vendor-command payload=<byte>[:<byte>]*\n" \
53 	"                                  Send VENDOR_COMMAND message (" xstr(CEC_MSG_VENDOR_COMMAND) ")\n" \
54 	"  --vendor-command-with-id vendor-id=<val>,cmd=<byte>[:<byte>]*\n" \
55 	"                                  Send VENDOR_COMMAND_WITH_ID message (" xstr(CEC_MSG_VENDOR_COMMAND_WITH_ID) ")\n" \
56 	"  --vendor-remote-button-down rc-code=<byte>[:<byte>]*\n" \
57 	"                                  Send VENDOR_REMOTE_BUTTON_DOWN message (" xstr(CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN) ")\n"
58 
59 /* Short option list
60 
61    Please keep in alphabetical order.
62    That makes it easier to see which short options are still free.
63 
64    In general the lower case is used to set something and the upper
65    case is used to retrieve a setting. */
66 enum Option {
67 	OptSetAdapter = 'a',
68 	OptClear = 'C',
69 	OptSetDevice = 'd',
70 	OptSetDriver = 'D',
71 	OptPhysAddrFromEDID = 'e',
72 	OptPhysAddrFromEDIDPoll = 'E',
73 	OptFrom = 'f',
74 	OptHelp = 'h',
75 	OptLogicalAddress = 'l',
76 	OptLogicalAddresses = 'L',
77 	OptMonitor = 'm',
78 	OptMonitorAll = 'M',
79 	OptToggleNoReply = 'n',
80 	OptNonBlocking = 'N',
81 	OptOsdName = 'o',
82 	OptPhysAddr = 'p',
83 	OptPoll = 'P',
84 	OptShowRaw = 'r',
85 	OptSkipInfo = 's',
86 	OptShowTopology = 'S',
87 	OptTo = 't',
88 	OptTrace = 'T',
89 	OptVerbose = 'v',
90 	OptVendorID = 'V',
91 	OptWallClock = 'w',
92 	OptWaitForMsgs = 'W',
93 
94 	OptTV = 128,
95 	OptRecord,
96 	OptTuner,
97 	OptPlayback,
98 	OptAudio,
99 	OptProcessor,
100 	OptSwitch,
101 	OptCDCOnly,
102 	OptUnregistered,
103 	OptCECVersion1_4,
104 	OptAllowUnregFallback,
105 	OptNoRC,
106 	OptReplyToFollowers,
107 	OptRawMsg,
108 	OptListDevices,
109 	OptTimeout,
110 	OptMonitorTime,
111 	OptMonitorPin,
112 	OptIgnore,
113 	OptStorePin,
114 	OptAnalyzePin,
115 	OptRcTVProfile1,
116 	OptRcTVProfile2,
117 	OptRcTVProfile3,
118 	OptRcTVProfile4,
119 	OptRcSrcDevRoot,
120 	OptRcSrcDevSetup,
121 	OptRcSrcContents,
122 	OptRcSrcMediaTop,
123 	OptRcSrcMediaContext,
124 	OptFeatRecordTVScreen,
125 	OptFeatSetOSDString,
126 	OptFeatDeckControl,
127 	OptFeatSetAudioRate,
128 	OptFeatSinkHasARCTx,
129 	OptFeatSourceHasARCRx,
130 	OptStressTestPowerCycle,
131 	OptTestPowerCycle,
132 	OptVendorCommand = 508,
133 	OptVendorCommandWithID,
134 	OptVendorRemoteButtonDown,
135 	OptCustomCommand,
136 };
137 
138 struct node {
139 	int fd;
140 	const char *device;
141 	unsigned caps;
142 	unsigned available_log_addrs;
143 	unsigned num_log_addrs;
144 	uint16_t log_addr_mask;
145 	uint16_t phys_addr;
146 	uint8_t log_addr[CEC_MAX_LOG_ADDRS];
147 };
148 
149 #define doioctl(n, r, p) cec_named_ioctl((n)->fd, #r, r, p)
150 
151 bool verbose;
152 
153 typedef std::vector<cec_msg> msg_vec;
154 
155 static struct option long_options[] = {
156 	{ "device", required_argument, 0, OptSetDevice },
157 	{ "adapter", required_argument, 0, OptSetAdapter },
158 	{ "driver", required_argument, 0, OptSetDriver },
159 	{ "help", no_argument, 0, OptHelp },
160 	{ "trace", no_argument, 0, OptTrace },
161 	{ "verbose", no_argument, 0, OptVerbose },
162 	{ "wall-clock", no_argument, 0, OptWallClock },
163 	{ "osd-name", required_argument, 0, OptOsdName },
164 	{ "phys-addr-from-edid-poll", required_argument, 0, OptPhysAddrFromEDIDPoll },
165 	{ "phys-addr-from-edid", required_argument, 0, OptPhysAddrFromEDID },
166 	{ "phys-addr", required_argument, 0, OptPhysAddr },
167 	{ "vendor-id", required_argument, 0, OptVendorID },
168 	{ "cec-version-1.4", no_argument, 0, OptCECVersion1_4 },
169 	{ "allow-unreg-fallback", no_argument, 0, OptAllowUnregFallback },
170 	{ "no-rc-passthrough", no_argument, 0, OptNoRC },
171 	{ "reply-to-followers", no_argument, 0, OptReplyToFollowers },
172 	{ "raw-msg", no_argument, 0, OptRawMsg },
173 	{ "timeout", required_argument, 0, OptTimeout },
174 	{ "clear", no_argument, 0, OptClear },
175 	{ "wait-for-msgs", no_argument, 0, OptWaitForMsgs },
176 	{ "monitor", no_argument, 0, OptMonitor },
177 	{ "monitor-all", no_argument, 0, OptMonitorAll },
178 	{ "monitor-pin", no_argument, 0, OptMonitorPin },
179 	{ "monitor-time", required_argument, 0, OptMonitorTime },
180 	{ "ignore", required_argument, 0, OptIgnore },
181 	{ "store-pin", required_argument, 0, OptStorePin },
182 	{ "analyze-pin", required_argument, 0, OptAnalyzePin },
183 	{ "no-reply", no_argument, 0, OptToggleNoReply },
184 	{ "non-blocking", no_argument, 0, OptNonBlocking },
185 	{ "logical-address", no_argument, 0, OptLogicalAddress },
186 	{ "logical-addresses", no_argument, 0, OptLogicalAddresses },
187 	{ "to", required_argument, 0, OptTo },
188 	{ "from", required_argument, 0, OptFrom },
189 	{ "skip-info", no_argument, 0, OptSkipInfo },
190 	{ "show-raw", no_argument, 0, OptShowRaw },
191 	{ "show-topology", no_argument, 0, OptShowTopology },
192 	{ "list-devices", no_argument, 0, OptListDevices },
193 	{ "poll", no_argument, 0, OptPoll },
194 	{ "rc-tv-profile-1", no_argument, 0, OptRcTVProfile1 },
195 	{ "rc-tv-profile-2", no_argument, 0, OptRcTVProfile2 },
196 	{ "rc-tv-profile-3", no_argument, 0, OptRcTVProfile3 },
197 	{ "rc-tv-profile-4", no_argument, 0, OptRcTVProfile4 },
198 	{ "rc-src-dev-root", no_argument, 0, OptRcSrcDevRoot },
199 	{ "rc-src-dev-setup", no_argument, 0, OptRcSrcDevSetup },
200 	{ "rc-src-contents", no_argument, 0, OptRcSrcContents },
201 	{ "rc-src-media-top", no_argument, 0, OptRcSrcMediaTop },
202 	{ "rc-src-media-context", no_argument, 0, OptRcSrcMediaContext },
203 	{ "feat-record-tv-screen", no_argument, 0, OptFeatRecordTVScreen },
204 	{ "feat-set-osd-string", no_argument, 0, OptFeatSetOSDString },
205 	{ "feat-deck-control", no_argument, 0, OptFeatDeckControl },
206 	{ "feat-set-audio-rate", no_argument, 0, OptFeatSetAudioRate },
207 	{ "feat-sink-has-arc-tx", no_argument, 0, OptFeatSinkHasARCTx },
208 	{ "feat-source-has-arc-rx", no_argument, 0, OptFeatSourceHasARCRx },
209 
210 	{ "tv", no_argument, 0, OptTV },
211 	{ "record", no_argument, 0, OptRecord },
212 	{ "tuner", no_argument, 0, OptTuner },
213 	{ "playback", no_argument, 0, OptPlayback },
214 	{ "audio", no_argument, 0, OptAudio },
215 	{ "processor", no_argument, 0, OptProcessor },
216 	{ "switch", no_argument, 0, OptSwitch },
217 	{ "cdc-only", no_argument, 0, OptCDCOnly },
218 	{ "unregistered", no_argument, 0, OptUnregistered },
219 	{ "help-all", no_argument, 0, OptHelpAll },
220 
221 	CEC_PARSE_LONG_OPTS
222 
223 	{ "vendor-remote-button-down", required_argument, 0, OptVendorRemoteButtonDown }, \
224 	{ "vendor-command-with-id", required_argument, 0, OptVendorCommandWithID }, \
225 	{ "vendor-command", required_argument, 0, OptVendorCommand }, \
226 	{ "custom-command", required_argument, 0, OptCustomCommand }, \
227 
228 	{ "test-power-cycle", optional_argument, 0, OptTestPowerCycle }, \
229 	{ "stress-test-power-cycle", required_argument, 0, OptStressTestPowerCycle }, \
230 
231 	{ 0, 0, 0, 0 }
232 };
233 
usage()234 static void usage()
235 {
236 	printf("Usage:\n"
237 	       "  -d, --device <dev>       Use device <dev> instead of /dev/cec0\n"
238 	       "                           If <dev> starts with a digit, then /dev/cec<dev> is used.\n"
239 	       "  -D, --driver <driver>    Use a cec device with this driver name\n"
240 	       "  -a, --adapter <adapter>  Use a cec device with this adapter name\n"
241 	       "  -p, --phys-addr <addr>   Use this physical address\n"
242 	       "  -e, --phys-addr-from-edid <path>\n"
243 	       "                           Set physical address from this EDID file\n"
244 	       "  -E, --phys-addr-from-edid-poll <path>\n"
245 	       "                           Continuously poll the EDID file for changes, and update the\n"
246 	       "                           physical address whenever there is a change\n"
247 	       "  -o, --osd-name <name>    Use this OSD name\n"
248 	       "  -V, --vendor-id <id>     Use this vendor ID\n"
249 	       "  -l, --logical-address    Show first configured logical address\n"
250 	       "  -L, --logical-addresses  Show all configured logical addresses\n"
251 	       "  -C, --clear              Clear all logical addresses\n"
252 	       "  -n, --no-reply           Toggle 'don't wait for a reply'\n"
253 	       "  -N, --non-blocking       Transmit messages in non-blocking mode\n"
254 	       "  -t, --to <la>            Send message to the given logical address\n"
255 	       "  -f, --from <la>          Send message from the given logical address\n"
256 	       "                           By default use the first assigned logical address\n"
257 	       "  -r, --show-raw           Show the raw CEC message (hex values)\n"
258 	       "  -s, --skip-info          Skip Driver Info output\n"
259 	       "  -S, --show-topology      Show the CEC topology\n"
260 	       "  -P, --poll               Send poll message\n"
261 	       "  -h, --help               Display this help message\n"
262 	       "  --help-all               Show all help messages\n"
263 	       "  -T, --trace              Trace all called ioctls\n"
264 	       "  -v, --verbose            Turn on verbose reporting\n"
265 	       "  -w, --wall-clock         Show timestamps as wall-clock time (implies -v)\n"
266 	       "  -W, --wait-for-msgs      Wait for messages and events for up to --monitor-time secs.\n"
267 	       "  --cec-version-1.4        Use CEC Version 1.4 instead of 2.0\n"
268 	       "  --allow-unreg-fallback   Allow fallback to Unregistered\n"
269 	       "  --no-rc-passthrough      Disable the RC passthrough\n"
270 	       "  --reply-to-followers     The reply will be sent to followers as well\n"
271 	       "  --raw-msg                Transmit the message without validating it (must be root)\n"
272 	       "  --timeout <ms>           Set the reply timeout in milliseconds (default is 1000 ms)\n"
273 	       "  --list-devices           List all cec devices\n"
274 	       "\n"
275 	       "  --tv                     This is a TV\n"
276 	       "  --record                 This is a recording and playback device\n"
277 	       "  --tuner                  This is a tuner device\n"
278 	       "  --playback               This is a playback device\n"
279 	       "  --audio                  This is an audio system device\n"
280 	       "  --processor              This is a processor device\n"
281 	       "  --switch                 This is a pure CEC switch\n"
282 	       "  --cdc-only               This is a CDC-only device\n"
283 	       "  --unregistered           This is an unregistered device\n"
284 	       "\n"
285 	       "  --feat-record-tv-screen  Signal the Record TV Screen feature\n"
286 	       "  --feat-set-osd-string    Signal the Set OSD String feature\n"
287 	       "  --feat-deck-control      Signal the Deck Control feature\n"
288 	       "  --feat-set-audio-rate    Signal the Set Audio Rate feature\n"
289 	       "  --feat-sink-has-arc-tx   Signal the sink ARC Tx feature\n"
290 	       "  --feat-source-has-arc-rx Signal the source ARC Rx feature\n"
291 	       "\n"
292 	       "  --rc-tv-profile-1        Signal RC TV Profile 1\n"
293 	       "  --rc-tv-profile-2        Signal RC TV Profile 2\n"
294 	       "  --rc-tv-profile-3        Signal RC TV Profile 3\n"
295 	       "  --rc-tv-profile-4        Signal RC TV Profile 4\n"
296 	       "\n"
297 	       "  --rc-src-dev-root        Signal that the RC source has a Dev Root Menu\n"
298 	       "  --rc-src-dev-setup       Signal that the RC source has a Dev Setup Menu\n"
299 	       "  --rc-src-contents        Signal that the RC source has a Contents Menu\n"
300 	       "  --rc-src-media-top       Signal that the RC source has a Media Top Menu\n"
301 	       "  --rc-src-media-context   Signal that the RC source has a Media Context Menu\n"
302 	       "\n"
303 	       "  -m, --monitor            Monitor CEC traffic\n"
304 	       "  -M, --monitor-all        Monitor all CEC traffic\n"
305 	       "  --monitor-pin            Monitor low-level CEC pin\n"
306 	       "  --monitor-time <secs>    Monitor for <secs> seconds (default is forever)\n"
307 	       "  --ignore <la>,<opcode>   Ignore messages from logical address <la> and opcode\n"
308 	       "                           <opcode> when monitoring. 'all' can be used for <la>\n"
309 	       "                           or <opcode> to match all logical addresses or opcodes.\n"
310 	       "                           To ignore poll messages use 'poll' as <opcode>.\n"
311 	       "  --store-pin <to>         Store the low-level CEC pin changes to the file <to>.\n"
312 	       "                           Use - for stdout.\n"
313 	       "  --analyze-pin <from>     Analyze the low-level CEC pin changes from the file <from>.\n"
314 	       "                           Use - for stdin.\n"
315 	       "  --test-power-cycle [polls=<n>][,sleep=<secs>]\n"
316 	       "                           Test power cycle behavior of the display. It polls up to\n"
317 	       "                           <n> times (default 15), waiting for a state change. If\n"
318 	       "                           that fails it waits <secs> seconds (default 10) before\n"
319 	       "                           retrying this.\n"
320 	       "  --stress-test-power-cycle cnt=<count>[,polls=<n>][,max-sleep=<maxsecs>][,min-sleep=<minsecs>][,seed=<seed>][,repeats=<reps>]\n"
321 	       "                            [,sleep-before-on=<secs1>][,sleep-before-off=<secs2>]\n"
322 	       "                           Power cycle display <count> times. If 0, then never stop.\n"
323 	       "                           It polls up to <n> times (default 30), waiting for a state change.\n"
324 	       "                           If <maxsecs> is non-zero (0 is the default), then sleep for\n"
325 	       "                           a random number of seconds between <minsecs> (0 is the default) and <maxsecs>\n"
326 	       "                           before each <Standby> or <Image View On> message.\n"
327 	       "                           If <seed> is specified, then set the randomizer seed to\n"
328 	       "                           that value instead of using the current time as seed.\n"
329 	       "                           If <reps> is specified, then repeat the <Image View On> and\n"
330 	       "                           <Standby> up to <reps> times. Note: should not be needed!\n"
331 	       "                           If <secs1> is specified, then sleep for <secs1> seconds\n"
332 	       "                           before transmitting <Image View On>.\n"
333 	       "                           If <secs2> is specified, then sleep for <secs2> seconds\n"
334 	       "                           before transmitting <Standby>.\n"
335 	       "\n"
336 	       CEC_PARSE_USAGE
337 	       "\n"
338 	       "  --custom-command cmd=<byte>[,payload=<byte>[:<byte>]*]\n"
339 	       "                                      Send custom message\n"
340 	       );
341 }
342 
power_status2s(uint8_t power_status)343 static const char *power_status2s(uint8_t power_status)
344 {
345 	switch (power_status) {
346 	case CEC_OP_POWER_STATUS_ON:
347 		return "On";
348 	case CEC_OP_POWER_STATUS_STANDBY:
349 		return "Standby";
350 	case CEC_OP_POWER_STATUS_TO_ON:
351 		return "In transition Standby to On";
352 	case CEC_OP_POWER_STATUS_TO_STANDBY:
353 		return "In transition On to Standby";
354 	default:
355 		return "Unknown";
356 	}
357 }
358 
ts2s(uint64_t ts)359 std::string ts2s(uint64_t ts)
360 {
361 	std::string s;
362 	struct timeval sub;
363 	struct timeval res;
364 	uint64_t diff;
365 	char buf[64];
366 	time_t t;
367 
368 	if (!options[OptWallClock]) {
369 		sprintf(buf, "%llu.%03llus", ts / 1000000000, (ts % 1000000000) / 1000000);
370 		return buf;
371 	}
372 	diff = ts - start_monotonic.tv_sec * 1000000000ULL - start_monotonic.tv_nsec;
373 	sub.tv_sec = diff / 1000000000ULL;
374 	sub.tv_usec = (diff % 1000000000ULL) / 1000;
375 	timeradd(&start_timeofday, &sub, &res);
376 	t = res.tv_sec;
377 	s = ctime(&t);
378 	s = s.substr(0, s.length() - 6);
379 	sprintf(buf, "%03lu", res.tv_usec / 1000);
380 	return s + "." + buf;
381 }
382 
ts2s(double ts)383 std::string ts2s(double ts)
384 {
385 	if (!options[OptWallClock]) {
386 		char buf[64];
387 
388 		sprintf(buf, "%10.06f", ts);
389 		return buf;
390 	}
391 	return ts2s(static_cast<uint64_t>(ts * 1000000000.0));
392 }
393 
current_ts()394 static uint64_t current_ts()
395 {
396 	struct timespec ts;
397 
398 	clock_gettime(CLOCK_MONOTONIC, &ts);
399 	return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
400 }
401 
cec_named_ioctl(int fd,const char * name,unsigned long int request,void * parm)402 int cec_named_ioctl(int fd, const char *name,
403 		    unsigned long int request, void *parm)
404 {
405 	int retval = ioctl(fd, request, parm);
406 	int e;
407 
408 	e = retval == 0 ? 0 : errno;
409 	if (options[OptTrace])
410 		printf("\t\t%s returned %d (%s)\n",
411 			name, retval, strerror(e));
412 
413 	return retval == -1 ? e : (retval ? -1 : 0);
414 }
415 
print_bytes(const uint8_t * bytes,unsigned len)416 static void print_bytes(const uint8_t *bytes, unsigned len)
417 {
418 	for (unsigned i = 0; i < len; i++)
419 		printf(" 0x%02x", bytes[i]);
420 	printf(" (");
421 	for (unsigned i = 0; i < len; i++)
422 		if (bytes[i] >= 32 && bytes[i] <= 127)
423 		    printf("%c", bytes[i]);
424 		else
425 		    printf(" ");
426 	printf(")");
427 }
428 
log_raw_msg(const struct cec_msg * msg)429 static void log_raw_msg(const struct cec_msg *msg)
430 {
431 	printf("\tRaw:");
432 	print_bytes(msg->msg, msg->len);
433 	printf("\n");
434 }
435 
event2s(uint32_t event)436 static const char *event2s(uint32_t event)
437 {
438 	switch (event) {
439 	case CEC_EVENT_STATE_CHANGE:
440 		return "State Change";
441 	case CEC_EVENT_LOST_MSGS:
442 		return "Lost Messages";
443 	case CEC_EVENT_PIN_CEC_LOW:
444 		return "CEC Pin Low";
445 	case CEC_EVENT_PIN_CEC_HIGH:
446 		return "CEC Pin High";
447 	case CEC_EVENT_PIN_HPD_LOW:
448 		return "HPD Pin Low";
449 	case CEC_EVENT_PIN_HPD_HIGH:
450 		return "HPD Pin High";
451 	case CEC_EVENT_PIN_5V_LOW:
452 		return "5V Pin Low";
453 	case CEC_EVENT_PIN_5V_HIGH:
454 		return "5V Pin High";
455 	default:
456 		return "Unknown";
457 	}
458 }
459 
log_event(struct cec_event & ev,bool show)460 static void log_event(struct cec_event &ev, bool show)
461 {
462 	bool is_high = ev.event == CEC_EVENT_PIN_CEC_HIGH;
463 	uint16_t pa;
464 
465 	if (ev.event != CEC_EVENT_PIN_CEC_LOW && ev.event != CEC_EVENT_PIN_CEC_HIGH &&
466 	    ev.event != CEC_EVENT_PIN_HPD_LOW && ev.event != CEC_EVENT_PIN_HPD_HIGH &&
467 	    ev.event != CEC_EVENT_PIN_5V_LOW && ev.event != CEC_EVENT_PIN_5V_HIGH)
468 		printf("\n");
469 	if ((ev.flags & CEC_EVENT_FL_DROPPED_EVENTS) && show)
470 		printf("(warn: %s events were lost)\n", event2s(ev.event));
471 	if ((ev.flags & CEC_EVENT_FL_INITIAL_STATE) && show)
472 		printf("Initial ");
473 	switch (ev.event) {
474 	case CEC_EVENT_STATE_CHANGE:
475 		pa = ev.state_change.phys_addr;
476 		if (show)
477 			printf("Event: State Change: PA: %x.%x.%x.%x, LA mask: 0x%04x, Conn Info: %s\n",
478 			       cec_phys_addr_exp(pa),
479 			       ev.state_change.log_addr_mask,
480 			       ev.state_change.have_conn_info ? "yes" : "no");
481 		break;
482 	case CEC_EVENT_LOST_MSGS:
483 		if (show)
484 			printf("Event: Lost %d messages\n",
485 			       ev.lost_msgs.lost_msgs);
486 		break;
487 	case CEC_EVENT_PIN_CEC_LOW:
488 	case CEC_EVENT_PIN_CEC_HIGH:
489 		if ((ev.flags & CEC_EVENT_FL_INITIAL_STATE) && show)
490 			printf("Event: CEC Pin %s\n", is_high ? "High" : "Low");
491 
492 		log_event_pin(is_high, ev.ts, show);
493 		return;
494 	case CEC_EVENT_PIN_HPD_LOW:
495 	case CEC_EVENT_PIN_HPD_HIGH:
496 		if (show)
497 			printf("Event: HPD Pin %s\n",
498 			       ev.event == CEC_EVENT_PIN_HPD_HIGH ? "High" : "Low");
499 		break;
500 	case CEC_EVENT_PIN_5V_LOW:
501 	case CEC_EVENT_PIN_5V_HIGH:
502 		if (show)
503 			printf("Event: 5V Pin %s\n",
504 			       ev.event == CEC_EVENT_PIN_5V_HIGH ? "High" : "Low");
505 		break;
506 	default:
507 		if (show)
508 			printf("Event: Unknown (0x%x)\n", ev.event);
509 		break;
510 	}
511 	if (verbose && show)
512 		printf("\tTimestamp: %s\n", ts2s(ev.ts).c_str());
513 }
514 
515 /*
516  * Bits 23-8 contain the physical address, bits 0-3 the logical address
517  * (equal to the index).
518  */
519 static uint32_t phys_addrs[16];
520 
showTopologyDevice(struct node * node,unsigned i,unsigned la)521 static int showTopologyDevice(struct node *node, unsigned i, unsigned la)
522 {
523 	struct cec_msg msg;
524 	char osd_name[15];
525 
526 	printf("\tSystem Information for device %d (%s) from device %d (%s):\n",
527 	       i, cec_la2s(i), la & 0xf, cec_la2s(la));
528 
529 	cec_msg_init(&msg, la, i);
530 	cec_msg_get_cec_version(&msg, true);
531 	doioctl(node, CEC_TRANSMIT, &msg);
532 	printf("\t\tCEC Version                : %s\n",
533 	       (!cec_msg_status_is_ok(&msg)) ? cec_status2s(msg).c_str() : cec_version2s(msg.msg[2]));
534 
535 	cec_msg_init(&msg, la, i);
536 	cec_msg_give_physical_addr(&msg, true);
537 	doioctl(node, CEC_TRANSMIT, &msg);
538 	printf("\t\tPhysical Address           : ");
539 	if (!cec_msg_status_is_ok(&msg)) {
540 		printf("%s\n", cec_status2s(msg).c_str());
541 	} else {
542 		uint16_t phys_addr = (msg.msg[2] << 8) | msg.msg[3];
543 
544 		printf("%x.%x.%x.%x\n", cec_phys_addr_exp(phys_addr));
545 		printf("\t\tPrimary Device Type        : %s\n",
546 		       cec_prim_type2s(msg.msg[4]));
547 		phys_addrs[i] = (phys_addr << 8) | i;
548 	}
549 
550 	cec_msg_init(&msg, la, i);
551 	cec_msg_give_device_vendor_id(&msg, true);
552 	doioctl(node, CEC_TRANSMIT, &msg);
553 	printf("\t\tVendor ID                  : ");
554 	if (!cec_msg_status_is_ok(&msg))
555 		printf("%s\n", cec_status2s(msg).c_str());
556 	else
557 		printf("0x%02x%02x%02x %s\n",
558 		       msg.msg[2], msg.msg[3], msg.msg[4],
559 		       cec_vendor2s(msg.msg[2] << 16 | msg.msg[3] << 8 | msg.msg[4]));
560 
561 	cec_msg_init(&msg, la, i);
562 	cec_msg_give_osd_name(&msg, true);
563 	doioctl(node, CEC_TRANSMIT, &msg);
564 	cec_ops_set_osd_name(&msg, osd_name);
565 	printf("\t\tOSD Name                   : ");
566 	if (cec_msg_status_is_ok(&msg))
567 		printf("'%s'\n", osd_name);
568 	else
569 		printf("%s\n", cec_status2s(msg).c_str());
570 
571 	cec_msg_init(&msg, la, i);
572 	cec_msg_get_menu_language(&msg, true);
573 	doioctl(node, CEC_TRANSMIT, &msg);
574 	if (cec_msg_status_is_ok(&msg)) {
575 		char language[4];
576 
577 		cec_ops_set_menu_language(&msg, language);
578 		language[3] = 0;
579 		printf("\t\tMenu Language              : %s\n", language);
580 	}
581 
582 	cec_msg_init(&msg, la, i);
583 	cec_msg_give_device_power_status(&msg, true);
584 	doioctl(node, CEC_TRANSMIT, &msg);
585 	if (cec_msg_status_is_ok(&msg)) {
586 		uint8_t pwr;
587 
588 		cec_ops_report_power_status(&msg, &pwr);
589 		printf("\t\tPower Status               : %s\n",
590 		       power_status2s(pwr));
591 	}
592 
593 	cec_msg_init(&msg, la, i);
594 	cec_msg_give_features(&msg, true);
595 	doioctl(node, CEC_TRANSMIT, &msg);
596 	if (cec_msg_status_is_ok(&msg)) {
597 		uint8_t vers, all_dev_types;
598 		const uint8_t *rc, *feat;
599 
600 		cec_ops_report_features(&msg, &vers, &all_dev_types, &rc, &feat);
601 
602 		printf("\t\tFeatures                   :\n");
603 		printf("\t\t    CEC Version            : %s\n", cec_version2s(vers));
604 		printf("\t\t    All Device Types       : %s\n",
605 		       cec_all_dev_types2s(all_dev_types).c_str());
606 		while (rc) {
607 			if (*rc & 0x40) {
608 				printf("\t\t    RC Source Profile      :\n%s",
609 				       cec_rc_src_prof2s(*rc, "\t").c_str());
610 			} else {
611 				const char *s = "Reserved";
612 
613 				switch (*rc & 0xf) {
614 				case 0:
615 					s = "None";
616 					break;
617 				case 2:
618 					s = "RC Profile 1";
619 					break;
620 				case 6:
621 					s = "RC Profile 2";
622 					break;
623 				case 10:
624 					s = "RC Profile 3";
625 					break;
626 				case 14:
627 					s = "RC Profile 4";
628 					break;
629 				}
630 				printf("\t\t    RC TV Profile          : %s\n", s);
631 			}
632 			if (!(*rc++ & CEC_OP_FEAT_EXT))
633 				break;
634 		}
635 
636 		while (feat) {
637 			printf("\t\t    Device Features        :\n%s",
638 				       cec_dev_feat2s(*feat, "\t").c_str());
639 			if (!(*feat++ & CEC_OP_FEAT_EXT))
640 				break;
641 		}
642 	}
643 	return 0;
644 }
645 
calc_mask(uint16_t pa)646 static uint16_t calc_mask(uint16_t pa)
647 {
648 	if (pa & 0xf)
649 		return 0xffff;
650 	if (pa & 0xff)
651 		return 0xfff0;
652 	if (pa & 0xfff)
653 		return 0xff00;
654 	if (pa & 0xffff)
655 		return 0xf000;
656 	return 0;
657 }
658 
showTopology(struct node * node)659 static int showTopology(struct node *node)
660 {
661 	struct cec_msg msg = { };
662 	struct cec_log_addrs laddrs = { };
663 
664 	if (!(node->caps & CEC_CAP_TRANSMIT))
665 		return -ENOTTY;
666 
667 	doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
668 
669 	if (!laddrs.num_log_addrs)
670 		return 0;
671 
672 	for (unsigned i = 0; i < 15; i++) {
673 		int ret;
674 
675 		cec_msg_init(&msg, laddrs.log_addr[0], i);
676 		ret = doioctl(node, CEC_TRANSMIT, &msg);
677 
678 		if (ret)
679 			continue;
680 
681 		if (msg.tx_status & CEC_TX_STATUS_OK)
682 			showTopologyDevice(node, i, laddrs.log_addr[0]);
683 		else if (verbose && !(msg.tx_status & CEC_TX_STATUS_MAX_RETRIES))
684 			printf("\t\t%s for addr %d\n", cec_status2s(msg).c_str(), i);
685 	}
686 
687 	uint32_t pas[16];
688 
689 	memcpy(pas, phys_addrs, sizeof(pas));
690 	std::sort(pas, pas + 16);
691 	unsigned level = 0;
692 	unsigned last_pa_mask = 0;
693 
694 	if ((pas[0] >> 8) == 0xffff)
695 		return 0;
696 
697 	printf("\n\tTopology:\n\n");
698 	for (unsigned i = 0; i < 16; i++) {
699 		uint16_t pa = pas[i] >> 8;
700 		uint8_t la = pas[i] & 0xf;
701 
702 		if (pa == 0xffff)
703 			break;
704 
705 		uint16_t pa_mask = calc_mask(pa);
706 
707 		while (last_pa_mask < pa_mask) {
708 			last_pa_mask = (last_pa_mask >> 4) | 0xf000;
709 			level++;
710 		}
711 		while (last_pa_mask > pa_mask) {
712 			last_pa_mask <<= 4;
713 			level--;
714 		}
715 		printf("\t");
716 		for (unsigned j = 0; j < level; j++)
717 			printf("    ");
718 		printf("%x.%x.%x.%x: %s\n", cec_phys_addr_exp(pa),
719 		       cec_la2s(la));
720 	}
721 	return 0;
722 }
723 
response_time_ms(const struct cec_msg & msg)724 static inline unsigned response_time_ms(const struct cec_msg &msg)
725 {
726 	unsigned ms = (msg.rx_ts - msg.tx_ts) / 1000000;
727 
728 	// Compensate for the time it took (approx.) to receive the
729 	// message.
730 	if (ms >= msg.len * 24)
731 		return ms - msg.len * 24;
732 	return 0;
733 }
734 
generate_eob_event(uint64_t ts,FILE * fstore)735 static void generate_eob_event(uint64_t ts, FILE *fstore)
736 {
737 	if (!eob_ts || eob_ts_max >= ts)
738 		return;
739 
740 	struct cec_event ev_eob = {
741 		eob_ts,
742 		CEC_EVENT_PIN_CEC_HIGH
743 	};
744 
745 	if (fstore) {
746 		fprintf(fstore, "%llu.%09llu %d\n",
747 			ev_eob.ts / 1000000000, ev_eob.ts % 1000000000,
748 			ev_eob.event - CEC_EVENT_PIN_CEC_LOW);
749 		fflush(fstore);
750 	}
751 	log_event(ev_eob, fstore != stdout);
752 }
753 
show_msg(const cec_msg & msg)754 static void show_msg(const cec_msg &msg)
755 {
756 	uint8_t from = cec_msg_initiator(&msg);
757 	uint8_t to = cec_msg_destination(&msg);
758 
759 	if (ignore_la[from])
760 		return;
761 	if ((msg.len == 1 && (ignore_opcode[POLL_FAKE_OPCODE] & (1 << from))) ||
762 	    (msg.len > 1 && (ignore_opcode[msg.msg[1]] & (1 << from))))
763 		return;
764 
765 	bool transmitted = msg.tx_status != 0;
766 	printf("%s %s to %s (%d to %d): ",
767 	       transmitted ? "Transmitted by" : "Received from",
768 	       cec_la2s(from), to == 0xf ? "all" : cec_la2s(to), from, to);
769 	cec_log_msg(&msg);
770 	if (options[OptShowRaw])
771 		log_raw_msg(&msg);
772 	std::string status;
773 	if ((msg.tx_status & ~CEC_TX_STATUS_OK) ||
774 	    (msg.rx_status & ~CEC_RX_STATUS_OK))
775 		status = std::string(" ") + cec_status2s(msg);
776 	if (verbose && transmitted)
777 		printf("\tSequence: %u Tx Timestamp: %s%s\n",
778 		       msg.sequence, ts2s(msg.tx_ts).c_str(),
779 		       status.c_str());
780 	else if (verbose && !transmitted)
781 		printf("\tSequence: %u Rx Timestamp: %s%s\n",
782 		       msg.sequence, ts2s(msg.rx_ts).c_str(),
783 		       status.c_str());
784 }
785 
wait_for_msgs(struct node & node,uint32_t monitor_time)786 static void wait_for_msgs(struct node &node, uint32_t monitor_time)
787 {
788 	fd_set rd_fds;
789 	fd_set ex_fds;
790 	int fd = node.fd;
791 	time_t t;
792 
793 	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
794 	t = time(NULL) + monitor_time;
795 
796 	while (!monitor_time || time(NULL) < t) {
797 		struct timeval tv = { 1, 0 };
798 		int res;
799 
800 		fflush(stdout);
801 		FD_ZERO(&rd_fds);
802 		FD_ZERO(&ex_fds);
803 		FD_SET(fd, &rd_fds);
804 		FD_SET(fd, &ex_fds);
805 		res = select(fd + 1, &rd_fds, NULL, &ex_fds, &tv);
806 		if (res < 0)
807 			break;
808 		if (FD_ISSET(fd, &ex_fds)) {
809 			struct cec_event ev;
810 
811 			if (doioctl(&node, CEC_DQEVENT, &ev))
812 				continue;
813 			log_event(ev, true);
814 		}
815 		if (FD_ISSET(fd, &rd_fds)) {
816 			struct cec_msg msg = { };
817 
818 			res = doioctl(&node, CEC_RECEIVE, &msg);
819 			if (res == ENODEV) {
820 				fprintf(stderr, "Device was disconnected.\n");
821 				break;
822 			}
823 			if (!res)
824 				show_msg(msg);
825 		}
826 	}
827 }
828 
829 #define MONITOR_FL_DROPPED_EVENTS     (1 << 16)
830 
monitor(struct node & node,uint32_t monitor_time,const char * store_pin)831 static void monitor(struct node &node, uint32_t monitor_time, const char *store_pin)
832 {
833 	uint32_t monitor = CEC_MODE_MONITOR;
834 	fd_set rd_fds;
835 	fd_set ex_fds;
836 	int fd = node.fd;
837 	FILE *fstore = NULL;
838 	time_t t;
839 
840 	if (options[OptMonitorAll])
841 		monitor = CEC_MODE_MONITOR_ALL;
842 	else if (options[OptMonitorPin] || options[OptStorePin])
843 		monitor = CEC_MODE_MONITOR_PIN;
844 
845 	if (!(node.caps & CEC_CAP_MONITOR_ALL) && monitor == CEC_MODE_MONITOR_ALL) {
846 		fprintf(stderr, "Monitor All mode is not supported, falling back to regular monitoring\n");
847 		monitor = CEC_MODE_MONITOR;
848 	}
849 	if (!(node.caps & CEC_CAP_MONITOR_PIN) && monitor == CEC_MODE_MONITOR_PIN) {
850 		fprintf(stderr, "Monitor Pin mode is not supported\n");
851 		usage();
852 		std::exit(EXIT_FAILURE);
853 	}
854 
855 	if (doioctl(&node, CEC_S_MODE, &monitor)) {
856 		fprintf(stderr, "Selecting monitor mode failed, you may have to run this as root.\n");
857 		return;
858 	}
859 
860 	if (monitor == CEC_MODE_MONITOR_PIN) {
861 		struct cec_log_addrs laddrs = { };
862 
863 		doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
864 		if (laddrs.log_addr_mask && !options[OptSkipInfo]) {
865 			fprintf(stderr, "note: this CEC adapter is configured. This may cause inaccurate event\n");
866 			fprintf(stderr, "      timestamps. It is recommended to unconfigure the adapter (cec-ctl -C)\n");
867 		}
868 	}
869 
870 	if (store_pin) {
871 		if (!strcmp(store_pin, "-"))
872 			fstore = stdout;
873 		else
874 			fstore = fopen(store_pin, "w+");
875 		if (fstore == NULL) {
876 			fprintf(stderr, "Failed to open %s: %s\n", store_pin,
877 				strerror(errno));
878 			std::exit(EXIT_FAILURE);
879 		}
880 		fprintf(fstore, "# cec-ctl --store-pin\n");
881 		fprintf(fstore, "# version 1\n");
882 		fprintf(fstore, "# start_monotonic %lu.%09lu\n",
883 			start_monotonic.tv_sec, start_monotonic.tv_nsec);
884 		fprintf(fstore, "# start_timeofday %lu.%06lu\n",
885 			start_timeofday.tv_sec, start_timeofday.tv_usec);
886 		fprintf(fstore, "# log_addr_mask 0x%04x\n", node.log_addr_mask);
887 		fprintf(fstore, "# phys_addr %x.%x.%x.%x\n",
888 			cec_phys_addr_exp(node.phys_addr));
889 	}
890 
891 	if (fstore != stdout)
892 		printf("\n");
893 
894 	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
895 	t = time(NULL) + monitor_time;
896 
897 	while (!monitor_time || time(NULL) < t) {
898 		struct timeval tv = { 1, 0 };
899 		bool pin_event = false;
900 		int res;
901 
902 		fflush(stdout);
903 		FD_ZERO(&rd_fds);
904 		FD_ZERO(&ex_fds);
905 		FD_SET(fd, &rd_fds);
906 		FD_SET(fd, &ex_fds);
907 		res = select(fd + 1, &rd_fds, NULL, &ex_fds, &tv);
908 		if (res < 0)
909 			break;
910 		if (FD_ISSET(fd, &rd_fds)) {
911 			struct cec_msg msg = { };
912 
913 			res = doioctl(&node, CEC_RECEIVE, &msg);
914 			if (res == ENODEV) {
915 				fprintf(stderr, "Device was disconnected.\n");
916 				break;
917 			}
918 			if (!res && fstore != stdout)
919 				show_msg(msg);
920 		}
921 		if (FD_ISSET(fd, &ex_fds)) {
922 			struct cec_event ev;
923 
924 			if (doioctl(&node, CEC_DQEVENT, &ev))
925 				continue;
926 			if (ev.event == CEC_EVENT_PIN_CEC_LOW ||
927 			    ev.event == CEC_EVENT_PIN_CEC_HIGH ||
928 			    ev.event == CEC_EVENT_PIN_HPD_LOW ||
929 			    ev.event == CEC_EVENT_PIN_HPD_HIGH ||
930 			    ev.event == CEC_EVENT_PIN_5V_LOW ||
931 			    ev.event == CEC_EVENT_PIN_5V_HIGH)
932 				pin_event = true;
933 			generate_eob_event(ev.ts, fstore);
934 			if (pin_event && fstore) {
935 				unsigned int v = ev.event - CEC_EVENT_PIN_CEC_LOW;
936 
937 				if (ev.flags & CEC_EVENT_FL_DROPPED_EVENTS)
938 					v |= MONITOR_FL_DROPPED_EVENTS;
939 				fprintf(fstore, "%llu.%09llu %d\n",
940 					ev.ts / 1000000000, ev.ts % 1000000000, v);
941 				fflush(fstore);
942 			}
943 			if (!pin_event || options[OptMonitorPin])
944 				log_event(ev, fstore != stdout);
945 		}
946 		if (!res && eob_ts) {
947 			struct timespec ts;
948 			uint64_t ts64;
949 
950 			clock_gettime(CLOCK_MONOTONIC, &ts);
951 			ts64 = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
952 			generate_eob_event(ts64, fstore);
953 		}
954 	}
955 	if (fstore && fstore != stdout)
956 		fclose(fstore);
957 }
958 
analyze(const char * analyze_pin)959 static void analyze(const char *analyze_pin)
960 {
961 	FILE *fanalyze;
962 	struct cec_event ev = { };
963 	unsigned long tv_sec, tv_nsec, tv_usec;
964 	unsigned version;
965 	unsigned log_addr_mask;
966 	unsigned pa1, pa2, pa3, pa4;
967 	unsigned line = 1;
968 	char s[100];
969 
970 	if (!strcmp(analyze_pin, "-"))
971 		fanalyze = stdin;
972 	else
973 		fanalyze = fopen(analyze_pin, "r");
974 	if (fanalyze == NULL) {
975 		fprintf(stderr, "Failed to open %s: %s\n", analyze_pin,
976 			strerror(errno));
977 		std::exit(EXIT_FAILURE);
978 	}
979 	if (!fgets(s, sizeof(s), fanalyze) ||
980 	    strcmp(s, "# cec-ctl --store-pin\n"))
981 		goto err;
982 	line++;
983 	if (!fgets(s, sizeof(s), fanalyze) ||
984 	    sscanf(s, "# version %u\n", &version) != 1 ||
985 	    version != 1)
986 		goto err;
987 	line++;
988 	if (!fgets(s, sizeof(s), fanalyze) ||
989 	    sscanf(s, "# start_monotonic %lu.%09lu\n", &tv_sec, &tv_nsec) != 2 ||
990 	    tv_nsec >= 1000000000)
991 		goto err;
992 	start_monotonic.tv_sec = tv_sec;
993 	start_monotonic.tv_nsec = tv_nsec;
994 	line++;
995 	if (!fgets(s, sizeof(s), fanalyze) ||
996 	    sscanf(s, "# start_timeofday %lu.%06lu\n", &tv_sec, &tv_usec) != 2 ||
997 	    tv_usec >= 1000000)
998 		goto err;
999 	start_timeofday.tv_sec = tv_sec;
1000 	start_timeofday.tv_usec = tv_usec;
1001 	line++;
1002 	if (!fgets(s, sizeof(s), fanalyze) ||
1003 	    sscanf(s, "# log_addr_mask 0x%04x\n", &log_addr_mask) != 1)
1004 		goto err;
1005 	line++;
1006 	if (!fgets(s, sizeof(s), fanalyze) ||
1007 	    sscanf(s, "# phys_addr %x.%x.%x.%x\n", &pa1, &pa2, &pa3, &pa4) != 4)
1008 		goto err;
1009 	line++;
1010 
1011 	printf("Physical Address:     %x.%x.%x.%x\n", pa1, pa2, pa3, pa4);
1012 	printf("Logical Address Mask: 0x%04x\n\n", log_addr_mask);
1013 
1014 	while (fgets(s, sizeof(s), fanalyze)) {
1015 		unsigned event;
1016 
1017 		if (s[0] == '#' || s[0] == '\n') {
1018 			line++;
1019 			continue;
1020 		}
1021 		if (sscanf(s, "%lu.%09lu %d\n", &tv_sec, &tv_nsec, &event) != 3 ||
1022 		    (event & ~MONITOR_FL_DROPPED_EVENTS) > 5) {
1023 			fprintf(stderr, "malformed data at line %d\n", line);
1024 			break;
1025 		}
1026 		ev.ts = tv_sec * 1000000000ULL + tv_nsec;
1027 		ev.flags = 0;
1028 		if (event & MONITOR_FL_DROPPED_EVENTS) {
1029 			event &= ~MONITOR_FL_DROPPED_EVENTS;
1030 			ev.flags = CEC_EVENT_FL_DROPPED_EVENTS;
1031 		}
1032 		ev.event = event + CEC_EVENT_PIN_CEC_LOW;
1033 		log_event(ev, true);
1034 		line++;
1035 	}
1036 
1037 	if (eob_ts) {
1038 		ev.event = CEC_EVENT_PIN_CEC_HIGH;
1039 		ev.ts = eob_ts;
1040 		log_event(ev, true);
1041 	}
1042 
1043 	if (fanalyze != stdin)
1044 		fclose(fanalyze);
1045 	return;
1046 
1047 err:
1048 	fprintf(stderr, "Not a pin store file: malformed data at line %d\n", line);
1049 	std::exit(EXIT_FAILURE);
1050 }
1051 
wait_for_pwr_state(struct node & node,unsigned from,unsigned & hpd_is_low_cnt,bool on)1052 static bool wait_for_pwr_state(struct node &node, unsigned from,
1053 			       unsigned &hpd_is_low_cnt, bool on)
1054 {
1055 	struct cec_msg msg;
1056 	uint8_t pwr;
1057 	int ret;
1058 
1059 	cec_msg_init(&msg, from, CEC_LOG_ADDR_TV);
1060 	cec_msg_give_device_power_status(&msg, true);
1061 	ret = doioctl(&node, CEC_TRANSMIT, &msg);
1062 	if (ret == EHOSTDOWN) {
1063 		printf("X");
1064 		fflush(stdout);
1065 		hpd_is_low_cnt++;
1066 		return hpd_is_low_cnt <= 2 ? false : !on;
1067 	}
1068 	hpd_is_low_cnt = 0;
1069 	if (ret) {
1070 		fprintf(stderr, "Give Device Power Status Transmit failed: %s\n",
1071 			strerror(ret));
1072 		std::exit(EXIT_FAILURE);
1073 	}
1074 	if ((msg.rx_status & CEC_RX_STATUS_OK) &&
1075 	    (msg.rx_status & CEC_RX_STATUS_FEATURE_ABORT)) {
1076 		printf("A");
1077 		fflush(stdout);
1078 		return false;
1079 	}
1080 	if (!(msg.rx_status & CEC_RX_STATUS_OK)) {
1081 		if (msg.tx_status & CEC_TX_STATUS_OK)
1082 			printf("T");
1083 		else
1084 			printf("N");
1085 		fflush(stdout);
1086 		return false;
1087 	}
1088 
1089 	cec_ops_report_power_status(&msg, &pwr);
1090 	switch (pwr) {
1091 	case CEC_OP_POWER_STATUS_ON:
1092 		printf("+");
1093 		break;
1094 	case CEC_OP_POWER_STATUS_STANDBY:
1095 		printf("-");
1096 		break;
1097 	case CEC_OP_POWER_STATUS_TO_ON:
1098 		printf("%c", on ? '/' : '|');
1099 		break;
1100 	case CEC_OP_POWER_STATUS_TO_STANDBY:
1101 		printf("%c", on ? '|' : '\\');
1102 		break;
1103 	default:
1104 		printf(" %d ", pwr);
1105 		break;
1106 	}
1107 	fflush(stdout);
1108 	return pwr == (on ? CEC_OP_POWER_STATUS_ON : CEC_OP_POWER_STATUS_STANDBY);
1109 }
1110 
wait_for_power_on(struct node & node,unsigned from)1111 static bool wait_for_power_on(struct node &node, unsigned from)
1112 {
1113 	unsigned hpd_is_low_cnt = 0;
1114 	return wait_for_pwr_state(node, from, hpd_is_low_cnt, true);
1115 }
1116 
wait_for_power_off(struct node & node,unsigned from,unsigned & hpd_is_low_cnt)1117 static bool wait_for_power_off(struct node &node, unsigned from, unsigned &hpd_is_low_cnt)
1118 {
1119 	return wait_for_pwr_state(node, from, hpd_is_low_cnt, false);
1120 }
1121 
transmit_msg_retry(struct node & node,struct cec_msg & msg)1122 static int transmit_msg_retry(struct node &node, struct cec_msg &msg)
1123 {
1124 	bool from_unreg = cec_msg_initiator(&msg) == CEC_LOG_ADDR_UNREGISTERED;
1125 	unsigned cnt = 0;
1126 	bool repeat;
1127 	int ret;
1128 
1129 	// Can happen if wakeup_la == 15 and CEC just started configuring
1130 	do {
1131 		ret = doioctl(&node, CEC_TRANSMIT, &msg);
1132 		repeat = ret == EHOSTDOWN || (ret == EINVAL && from_unreg);
1133 		if (repeat)
1134 			usleep(100000);
1135 	} while (repeat && cnt++ < 10);
1136 	return ret;
1137 }
1138 
init_power_cycle_test(struct node & node,unsigned repeats,unsigned max_tries)1139 static int init_power_cycle_test(struct node &node, unsigned repeats, unsigned max_tries)
1140 {
1141 	struct cec_msg msg;
1142 	unsigned from;
1143 	unsigned tries;
1144 	uint16_t pa;
1145 	int ret;
1146 
1147 	printf("Legend:\n\n"
1148 	       "X   No LA claimed (HPD is likely pulled low)\n"
1149 	       "N   Give Device Power Status was Nacked\n"
1150 	       "T   Time out waiting for Report Power Status reply\n"
1151 	       "A   Feature Abort of Give Device Power Status \n"
1152 	       "+   Reported On\n"
1153 	       "-   Reported In Standby\n"
1154 	       "/   Reported Transitioning to On\n"
1155 	       "\\   Reported Transitioning to Standby\n"
1156 	       "|   Reported Transitioning to On when 'to Standby' was expected or vice versa\n\n");
1157 
1158 	struct cec_log_addrs laddrs = { };
1159 	doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1160 	if (laddrs.log_addr[0] != CEC_LOG_ADDR_INVALID) {
1161 		from = laddrs.log_addr[0];
1162 	} else {
1163 		switch (laddrs.log_addr_type[0]) {
1164 		case CEC_LOG_ADDR_TYPE_TV:
1165 			fprintf(stderr, "A TV can't run the power cycle test.\n");
1166 			std::exit(EXIT_FAILURE);
1167 		case CEC_LOG_ADDR_TYPE_RECORD:
1168 			from = CEC_LOG_ADDR_RECORD_1;
1169 			break;
1170 		case CEC_LOG_ADDR_TYPE_TUNER:
1171 			from = CEC_LOG_ADDR_TUNER_1;
1172 			break;
1173 		case CEC_LOG_ADDR_TYPE_PLAYBACK:
1174 			from = CEC_LOG_ADDR_PLAYBACK_1;
1175 			break;
1176 		case CEC_LOG_ADDR_TYPE_AUDIOSYSTEM:
1177 			from = CEC_LOG_ADDR_AUDIOSYSTEM;
1178 			break;
1179 		case CEC_LOG_ADDR_TYPE_SPECIFIC:
1180 			from = CEC_LOG_ADDR_SPECIFIC;
1181 			break;
1182 		case CEC_LOG_ADDR_TYPE_UNREGISTERED:
1183 		default:
1184 			from = CEC_LOG_ADDR_UNREGISTERED;
1185 			break;
1186 		}
1187 	}
1188 
1189 	if (laddrs.log_addr[0] == CEC_LOG_ADDR_INVALID) {
1190 		printf("No Logical Addresses claimed, assume TV is already in Standby\n\n");
1191 	} else {
1192 		doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &pa);
1193 		for (unsigned repeat = 0; repeat <= repeats; repeat++) {
1194 			/*
1195 			 * Some displays only accept Standby from the Active Source.
1196 			 * So make us the Active Source before sending Standby.
1197 			 */
1198 			printf("%s: ", ts2s(current_ts()).c_str());
1199 			printf("Transmit Active Source to TV: ");
1200 			fflush(stdout);
1201 			cec_msg_init(&msg, from, CEC_LOG_ADDR_TV);
1202 			cec_msg_active_source(&msg, pa);
1203 			ret = transmit_msg_retry(node, msg);
1204 			if (ret) {
1205 				printf("FAIL: %s\n", strerror(ret));
1206 				std::exit(EXIT_FAILURE);
1207 			}
1208 			printf("OK\n");
1209 			printf("%s: ", ts2s(current_ts()).c_str());
1210 			printf("Transmit Standby to TV: ");
1211 			fflush(stdout);
1212 			cec_msg_init(&msg, from, CEC_LOG_ADDR_TV);
1213 			cec_msg_standby(&msg);
1214 
1215 			tries = 0;
1216 			unsigned hpd_is_low_cnt = 0;
1217 			for (;;) {
1218 				doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1219 				if (laddrs.log_addr[0] == CEC_LOG_ADDR_INVALID)
1220 					break;
1221 
1222 				ret = transmit_msg_retry(node, msg);
1223 				if (ret) {
1224 					printf("FAIL: %s\n", strerror(ret));
1225 					std::exit(EXIT_FAILURE);
1226 				}
1227 
1228 				if (wait_for_power_off(node, from, hpd_is_low_cnt))
1229 					break;
1230 				if (++tries > max_tries) {
1231 					if (repeat == repeats) {
1232 						printf(" FAIL: never went into standby\n");
1233 						std::exit(EXIT_FAILURE);
1234 					}
1235 					break;
1236 				}
1237 				sleep(1);
1238 			}
1239 			if (tries <= max_tries)
1240 				break;
1241 
1242 			printf(" WARN: never went into standby during attempt %u\n", repeat + 1);
1243 		}
1244 		printf(" OK\n");
1245 		printf("%s: ", ts2s(current_ts()).c_str());
1246 		printf("TV is in Standby\n");
1247 		sleep(5);
1248 	}
1249 	doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &pa);
1250 	printf("%s: ", ts2s(current_ts()).c_str());
1251 	printf("Physical Address: %x.%x.%x.%x\n\n",
1252 	       cec_phys_addr_exp(pa));
1253 	return from;
1254 }
1255 
test_power_cycle(struct node & node,unsigned int max_tries,unsigned int retry_sleep)1256 static void test_power_cycle(struct node &node, unsigned int max_tries,
1257 			     unsigned int retry_sleep)
1258 {
1259 	struct cec_log_addrs laddrs = { };
1260 	struct cec_msg msg;
1261 	unsigned failures = 0;
1262 	unsigned from;
1263 	unsigned tries;
1264 	unsigned secs;
1265 	uint16_t pa, prev_pa;
1266 	uint16_t display_pa = CEC_PHYS_ADDR_INVALID;
1267 	uint8_t wakeup_la;
1268 	int ret;
1269 
1270 	from = init_power_cycle_test(node, 2, max_tries);
1271 
1272 	doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1273 	if (laddrs.log_addr[0] != CEC_LOG_ADDR_INVALID)
1274 		wakeup_la = from = laddrs.log_addr[0];
1275 	else
1276 		wakeup_la = CEC_LOG_ADDR_UNREGISTERED;
1277 
1278 	bool hpd_is_low = wakeup_la == CEC_LOG_ADDR_UNREGISTERED;
1279 
1280 	printf("The Hotplug Detect pin %s when in Standby\n\n",
1281 	       hpd_is_low ? "is pulled low" : "remains high");
1282 
1283 	for (unsigned iter = 0; iter <= 2 * 12; iter++) {
1284 		unsigned i = iter / 2;
1285 
1286 		/*
1287 		 * For sleep values 0-5 run the test twice,
1288 		 * after that run it only once.
1289 		 */
1290 		if (i > 5 && (iter & 1))
1291 			continue;
1292 
1293 		printf("%s: ", ts2s(current_ts()).c_str());
1294 		printf("Wake up TV using Image View On from LA %s: ", cec_la2s(wakeup_la));
1295 		fflush(stdout);
1296 		cec_msg_init(&msg, wakeup_la, CEC_LOG_ADDR_TV);
1297 		cec_msg_image_view_on(&msg);
1298 		ret = transmit_msg_retry(node, msg);
1299 		if (ret) {
1300 			printf("FAIL: %s\n", strerror(ret));
1301 			std::exit(EXIT_FAILURE);
1302 		}
1303 		tries = 0;
1304 		for (;;) {
1305 			doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1306 			if (laddrs.log_addr[0] != CEC_LOG_ADDR_INVALID)
1307 				wakeup_la = from = laddrs.log_addr[0];
1308 			if (wait_for_power_on(node, from))
1309 				break;
1310 			if (++tries > max_tries)
1311 				break;
1312 			sleep(1);
1313 		}
1314 
1315 		if (tries > max_tries) {
1316 			wakeup_la = from;
1317 			printf("\nFAIL: never woke up, sleep %u secs, then retry\n",
1318 			       retry_sleep);
1319 			failures++;
1320 			fflush(stdout);
1321 			sleep(retry_sleep);
1322 			printf("%s: ", ts2s(current_ts()).c_str());
1323 			printf("Wake up TV using Image View On from LA %s: ", cec_la2s(wakeup_la));
1324 			fflush(stdout);
1325 			cec_msg_init(&msg, wakeup_la, CEC_LOG_ADDR_TV);
1326 			cec_msg_image_view_on(&msg);
1327 			ret = doioctl(&node, CEC_TRANSMIT, &msg);
1328 			if (ret == EHOSTDOWN) {
1329 				printf("(EHOSTDOWN) ");
1330 			} else if (ret == EINVAL && wakeup_la == CEC_LOG_ADDR_UNREGISTERED) {
1331 				printf("(EINVAL) ");
1332 			} else if (ret) {
1333 				printf("FAIL: %s\n", strerror(ret));
1334 				std::exit(EXIT_FAILURE);
1335 			}
1336 			tries = 0;
1337 			for (;;) {
1338 				doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1339 				if (laddrs.log_addr[0] != CEC_LOG_ADDR_INVALID)
1340 					wakeup_la = from = laddrs.log_addr[0];
1341 				if (wait_for_power_on(node, from))
1342 					break;
1343 				if (++tries > max_tries) {
1344 					printf("\nFAIL: never woke up\n");
1345 					failures++;
1346 					break;
1347 				}
1348 				sleep(1);
1349 			}
1350 		}
1351 		printf(" %d second%s\n", tries, tries == 1 ? "" : "s");
1352 
1353 		doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1354 		if (laddrs.log_addr[0] == CEC_LOG_ADDR_INVALID) {
1355 			printf("FAIL: invalid logical address\n");
1356 			std::exit(EXIT_FAILURE);
1357 		}
1358 		from = laddrs.log_addr[0];
1359 		doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &pa);
1360 		printf("%s: ", ts2s(current_ts()).c_str());
1361 		printf("Physical Address: %x.%x.%x.%x LA: %s\n",
1362 		       cec_phys_addr_exp(pa), cec_la2s(from));
1363 		if (pa == CEC_PHYS_ADDR_INVALID || !pa) {
1364 			printf("FAIL: invalid physical address\n");
1365 			std::exit(EXIT_FAILURE);
1366 		}
1367 		prev_pa = pa;
1368 		if (display_pa == CEC_PHYS_ADDR_INVALID)
1369 			display_pa = pa;
1370 		if (pa != display_pa) {
1371 			printf("FAIL: physical address changed from %x.%x.%x.%x to %x.%x.%x.%x\n",
1372 			       cec_phys_addr_exp(display_pa), cec_phys_addr_exp(pa));
1373 			std::exit(EXIT_FAILURE);
1374 		}
1375 		secs = i <= 10 ? i : 10 + 10 * (i - 10);
1376 		printf("%s: ", ts2s(current_ts()).c_str());
1377 		printf("Sleep %u second%s\n", secs, secs == 1 ? "" : "s");
1378 		sleep(secs);
1379 		doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &pa);
1380 		if (pa != prev_pa) {
1381 			printf("\tFAIL: PA is now %x.%x.%x.%x\n\n",
1382 			       cec_phys_addr_exp(pa));
1383 			std::exit(EXIT_FAILURE);
1384 		}
1385 
1386 		printf("\n%s: ", ts2s(current_ts()).c_str());
1387 		printf("Put TV in standby from LA %s: ", cec_la2s(from));
1388 		fflush(stdout);
1389 		/*
1390 		 * Some displays only accept Standby from the Active Source.
1391 		 * So make us the Active Source before sending Standby.
1392 		 */
1393 		cec_msg_init(&msg, from, CEC_LOG_ADDR_TV);
1394 		cec_msg_active_source(&msg, pa);
1395 		ret = transmit_msg_retry(node, msg);
1396 		if (ret) {
1397 			printf("FAIL: Active Source Transmit failed: %s\n", strerror(ret));
1398 			std::exit(EXIT_FAILURE);
1399 		}
1400 		cec_msg_init(&msg, from, CEC_LOG_ADDR_TV);
1401 		cec_msg_standby(&msg);
1402 		ret = transmit_msg_retry(node, msg);
1403 		if (ret) {
1404 			printf("FAIL: %s\n", strerror(ret));
1405 			std::exit(EXIT_FAILURE);
1406 		}
1407 
1408 		tries = 0;
1409 		bool first_standby = true;
1410 		unsigned hpd_is_low_cnt = 0;
1411 		for (;;) {
1412 			doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1413 			if (laddrs.log_addr[0] == CEC_LOG_ADDR_INVALID)
1414 				break;
1415 			if (!hpd_is_low)
1416 				hpd_is_low_cnt = 0;
1417 			if (wait_for_power_off(node, from, hpd_is_low_cnt))
1418 				break;
1419 			if (++tries > max_tries) {
1420 				if (first_standby) {
1421 					printf("\nFAIL: never went into standby, sleep %u secs, then retry\n",
1422 					       retry_sleep);
1423 					failures++;
1424 					fflush(stdout);
1425 					sleep(retry_sleep);
1426 					printf("%s: ", ts2s(current_ts()).c_str());
1427 					printf("Put TV in standby from LA %s: ", cec_la2s(from));
1428 					fflush(stdout);
1429 					first_standby = false;
1430 					tries = 0;
1431 					cec_msg_init(&msg, from, CEC_LOG_ADDR_TV);
1432 					cec_msg_standby(&msg);
1433 					ret = doioctl(&node, CEC_TRANSMIT, &msg);
1434 					if (!ret)
1435 						continue;
1436 					printf("FAIL: %s\n", strerror(ret));
1437 					std::exit(EXIT_FAILURE);
1438 				}
1439 				printf("\nFAIL: never went into standby\n");
1440 				failures++;
1441 				break;
1442 			}
1443 			sleep(1);
1444 		}
1445 		printf(" %d second%s\n", tries, tries == 1 ? "" : "s");
1446 		doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &pa);
1447 		printf("%s: ", ts2s(current_ts()).c_str());
1448 		printf("Physical Address: %x.%x.%x.%x\n",
1449 		       cec_phys_addr_exp(pa));
1450 		prev_pa = pa;
1451 		printf("%s: ", ts2s(current_ts()).c_str());
1452 		printf("Sleep %d second%s\n", secs, secs == 1 ? "" : "s");
1453 		sleep(secs);
1454 		doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &pa);
1455 		if (pa != prev_pa) {
1456 			printf("%s: ", ts2s(current_ts()).c_str());
1457 			printf("Physical Address: %x.%x.%x.%x\n",
1458 			       cec_phys_addr_exp(pa));
1459 		}
1460 		if (pa != CEC_PHYS_ADDR_INVALID && pa != display_pa) {
1461 			printf("FAIL: physical address changed from %x.%x.%x.%x to %x.%x.%x.%x\n",
1462 			       cec_phys_addr_exp(display_pa), cec_phys_addr_exp(pa));
1463 			std::exit(EXIT_FAILURE);
1464 		}
1465 		printf("\n");
1466 	}
1467 	if (failures)
1468 		printf("Test had %u failure%s\n", failures, failures == 1 ? "" : "s");
1469 }
1470 
stress_test_power_cycle(struct node & node,unsigned cnt,unsigned min_sleep,unsigned max_sleep,unsigned max_tries,bool has_seed,unsigned seed,unsigned repeats,double sleep_before_on,double sleep_before_off)1471 static void stress_test_power_cycle(struct node &node, unsigned cnt,
1472 				    unsigned min_sleep, unsigned max_sleep, unsigned max_tries,
1473 				    bool has_seed, unsigned seed, unsigned repeats,
1474 				    double sleep_before_on, double sleep_before_off)
1475 {
1476 	struct cec_log_addrs laddrs = { };
1477 	struct cec_msg msg;
1478 	unsigned tries = 0;
1479 	unsigned iter = 0;
1480 	unsigned min_usleep = 1000000 * (max_sleep ? min_sleep : 0);
1481 	unsigned mod_usleep = 0;
1482 	unsigned wakeup_la;
1483 	uint16_t pa, prev_pa;
1484 	uint16_t display_pa = CEC_PHYS_ADDR_INVALID;
1485 	int ret;
1486 
1487 	if (max_sleep)
1488 		mod_usleep = 1000000 * (max_sleep - min_sleep) + 1;
1489 
1490 	if (!has_seed)
1491 		seed = time(NULL);
1492 
1493 	if (mod_usleep)
1494 		printf("Randomizer seed: %u\n\n", seed);
1495 
1496 	unsigned from = init_power_cycle_test(node, repeats, max_tries);
1497 
1498 	doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1499 	if (laddrs.log_addr[0] != CEC_LOG_ADDR_INVALID)
1500 		wakeup_la = from = laddrs.log_addr[0];
1501 	else
1502 		wakeup_la = CEC_LOG_ADDR_UNREGISTERED;
1503 
1504 	bool hpd_is_low = wakeup_la == CEC_LOG_ADDR_UNREGISTERED;
1505 
1506 	printf("The Hotplug Detect pin %s when in Standby\n\n",
1507 	       hpd_is_low ? "is pulled low" : "remains high");
1508 
1509 	srandom(seed);
1510 
1511 	for (;;) {
1512 		unsigned usecs1 = mod_usleep ? random() % mod_usleep : static_cast<unsigned>(sleep_before_on * 1000000);
1513 		unsigned usecs2 = mod_usleep ? random() % mod_usleep : static_cast<unsigned>(sleep_before_off * 1000000);
1514 
1515 		usecs1 += min_usleep;
1516 		usecs2 += min_usleep;
1517 
1518 		iter++;
1519 
1520 		if (usecs1)
1521 			printf("%s: Sleep %.2fs\n", ts2s(current_ts()).c_str(),
1522 			       usecs1 / 1000000.0);
1523 		fflush(stdout);
1524 		usleep(usecs1);
1525 		for (unsigned repeat = 0; repeat <= repeats; repeat++) {
1526 			printf("%s: ", ts2s(current_ts()).c_str());
1527 			printf("Transmit Image View On from LA %s (iteration %u): ", cec_la2s(wakeup_la), iter);
1528 
1529 			tries = 0;
1530 			cec_msg_init(&msg, wakeup_la, CEC_LOG_ADDR_TV);
1531 			cec_msg_image_view_on(&msg);
1532 			ret = transmit_msg_retry(node, msg);
1533 			if (ret == EINVAL && wakeup_la == CEC_LOG_ADDR_UNREGISTERED) {
1534 				// Can happen if wakeup_la == 15 and CEC just started configuring
1535 				printf("(EINVAL) ");
1536 			} else if (ret) {
1537 				printf("FAIL: %s\n", strerror(ret));
1538 				std::exit(EXIT_FAILURE);
1539 			}
1540 
1541 			for (;;) {
1542 				doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1543 				if (laddrs.log_addr[0] != CEC_LOG_ADDR_INVALID)
1544 					wakeup_la = from = laddrs.log_addr[0];
1545 				if (wait_for_power_on(node, from))
1546 					break;
1547 				if (++tries > max_tries) {
1548 					if (repeat == repeats) {
1549 						printf("\nFAIL: never woke up\n");
1550 						std::exit(EXIT_FAILURE);
1551 					}
1552 					break;
1553 				}
1554 				sleep(1);
1555 			}
1556 			if (tries <= max_tries)
1557 				break;
1558 			printf("\nWARN: never woke up during attempt %u\n", repeat + 1);
1559 		}
1560 		printf(" %d second%s\n", tries, tries == 1 ? "" : "s");
1561 		doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1562 		if (laddrs.log_addr[0] == CEC_LOG_ADDR_INVALID) {
1563 			printf("FAIL: invalid logical address\n");
1564 			std::exit(EXIT_FAILURE);
1565 		}
1566 		from = laddrs.log_addr[0];
1567 		doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &pa);
1568 		prev_pa = pa;
1569 		printf("%s: ", ts2s(current_ts()).c_str());
1570 		printf("Physical Address: %x.%x.%x.%x LA: %s\n",
1571 		       cec_phys_addr_exp(pa), cec_la2s(from));
1572 		if (pa == CEC_PHYS_ADDR_INVALID || !pa) {
1573 			printf("FAIL: invalid physical address\n");
1574 			std::exit(EXIT_FAILURE);
1575 		}
1576 		if (display_pa == CEC_PHYS_ADDR_INVALID)
1577 			display_pa = pa;
1578 		if (pa != display_pa) {
1579 			printf("FAIL: physical address changed from %x.%x.%x.%x to %x.%x.%x.%x\n",
1580 			       cec_phys_addr_exp(display_pa), cec_phys_addr_exp(pa));
1581 			std::exit(EXIT_FAILURE);
1582 		}
1583 
1584 		if (cnt && iter == cnt)
1585 			break;
1586 
1587 		if (usecs2)
1588 			printf("%s: Sleep %.2fs\n", ts2s(current_ts()).c_str(),
1589 			       usecs2 / 1000000.0);
1590 		fflush(stdout);
1591 		usleep(usecs2);
1592 		for (unsigned repeat = 0; repeat <= repeats; repeat++) {
1593 			printf("%s: ", ts2s(current_ts()).c_str());
1594 			printf("Transmit Standby (iteration %u): ", iter);
1595 			doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &pa);
1596 			if (pa != prev_pa) {
1597 				printf("\tFAIL: PA is now %x.%x.%x.%x\n\n",
1598 				       cec_phys_addr_exp(pa));
1599 				std::exit(EXIT_FAILURE);
1600 			}
1601 			if (pa != CEC_PHYS_ADDR_INVALID && pa != display_pa) {
1602 				printf("FAIL: physical address changed from %x.%x.%x.%x to %x.%x.%x.%x\n",
1603 				       cec_phys_addr_exp(display_pa), cec_phys_addr_exp(pa));
1604 				std::exit(EXIT_FAILURE);
1605 			}
1606 
1607 			cec_msg_init(&msg, from, CEC_LOG_ADDR_TV);
1608 			/*
1609 			 * Some displays only accept Standby from the Active Source.
1610 			 * So make us the Active Source before sending Standby.
1611 			 */
1612 			cec_msg_active_source(&msg, pa);
1613 			ret = transmit_msg_retry(node, msg);
1614 			if (ret) {
1615 				printf("FAIL: Active Source Transmit failed: %s\n", strerror(ret));
1616 				std::exit(EXIT_FAILURE);
1617 			}
1618 			cec_msg_standby(&msg);
1619 			ret = transmit_msg_retry(node, msg);
1620 			if (ret) {
1621 				printf("FAIL: %s\n", strerror(ret));
1622 				std::exit(EXIT_FAILURE);
1623 			}
1624 
1625 			tries = 0;
1626 			unsigned hpd_is_low_cnt = 0;
1627 			for (;;) {
1628 				doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1629 				if (laddrs.log_addr[0] == CEC_LOG_ADDR_INVALID)
1630 					break;
1631 				if (!hpd_is_low)
1632 					hpd_is_low_cnt = 0;
1633 				if (wait_for_power_off(node, from, hpd_is_low_cnt))
1634 					break;
1635 				if (++tries > max_tries) {
1636 					if (repeat == repeats) {
1637 						printf("\nFAIL: never went into standby\n");
1638 						std::exit(EXIT_FAILURE);
1639 					}
1640 					break;
1641 				}
1642 				sleep(1);
1643 			}
1644 			if (tries <= max_tries)
1645 				break;
1646 			printf("\nWARN: never went into standby during attempt %u\n", repeat + 1);
1647 		}
1648 		printf(" %d second%s\n", tries, tries == 1 ? "" : "s");
1649 	}
1650 }
1651 
calc_node_val(const char * s)1652 static int calc_node_val(const char *s)
1653 {
1654 	s = std::strrchr(s, '/') + 1;
1655 
1656 	if (!memcmp(s, "cec", 3))
1657 		return atol(s + 3);
1658 	return 0;
1659 }
1660 
sort_on_device_name(const std::string & s1,const std::string & s2)1661 static bool sort_on_device_name(const std::string &s1, const std::string &s2)
1662 {
1663 	int n1 = calc_node_val(s1.c_str());
1664 	int n2 = calc_node_val(s2.c_str());
1665 
1666 	return n1 < n2;
1667 }
1668 
cec_get_edid_spa_location(const uint8_t * edid,unsigned int size)1669 static unsigned int cec_get_edid_spa_location(const uint8_t *edid,
1670 					      unsigned int size)
1671 {
1672 	unsigned int blocks = size / 128;
1673 	unsigned int block;
1674 	uint8_t d;
1675 
1676 	/* Sanity check: at least 2 blocks and a multiple of the block size */
1677 	if (blocks < 2 || size % 128)
1678 		return 0;
1679 
1680 	/*
1681 	 * If there are fewer extension blocks than the size, then update
1682 	 * 'blocks'. It is allowed to have more extension blocks than the size,
1683 	 * since some hardware can only read e.g. 256 bytes of the EDID, even
1684 	 * though more blocks are present. The first CEA-861 extension block
1685 	 * should normally be in block 1 anyway.
1686 	 */
1687 	if (edid[0x7e] + 1U < blocks)
1688 		blocks = edid[0x7e] + 1;
1689 
1690 	for (block = 1; block < blocks; block++) {
1691 		unsigned int offset = block * 128;
1692 
1693 		/* Skip any non-CEA-861 extension blocks */
1694 		if (edid[offset] != 0x02 || edid[offset + 1] != 0x03)
1695 			continue;
1696 
1697 		/* search Vendor Specific Data Block (tag 3) */
1698 		d = edid[offset + 2] & 0x7f;
1699 		/* Check if there are Data Blocks */
1700 		if (d <= 4)
1701 			continue;
1702 		if (d > 4) {
1703 			unsigned int i = offset + 4;
1704 			unsigned int end = offset + d;
1705 
1706 			/* Note: 'end' is always < 'size' */
1707 			do {
1708 				uint8_t tag = edid[i] >> 5;
1709 				uint8_t len = edid[i] & 0x1f;
1710 
1711 				if (tag == 3 && len >= 5 && i + len <= end &&
1712 				    edid[i + 1] == 0x03 &&
1713 				    edid[i + 2] == 0x0c &&
1714 				    edid[i + 3] == 0x00)
1715 					return i + 4;
1716 				i += len + 1;
1717 			} while (i < end);
1718 		}
1719 	}
1720 	return 0;
1721 }
1722 
parse_phys_addr_from_edid(const char * edid_path)1723 static uint16_t parse_phys_addr_from_edid(const char *edid_path)
1724 {
1725 	FILE *f = fopen(edid_path, "r");
1726 	uint16_t pa = CEC_PHYS_ADDR_INVALID;
1727 	uint8_t edid[256];
1728 
1729 	if (f == NULL)
1730 		return pa;
1731 	if (fread(edid, sizeof(edid), 1, f) == 1) {
1732 		unsigned int loc = cec_get_edid_spa_location(edid, sizeof(edid));
1733 
1734 		if (loc)
1735 			pa = (edid[loc] << 8) | edid[loc + 1];
1736 	}
1737 	fclose(f);
1738 	return pa;
1739 }
1740 
1741 typedef std::vector<std::string> dev_vec;
1742 typedef std::map<std::string, std::string> dev_map;
1743 
list_devices()1744 static void list_devices()
1745 {
1746 	DIR *dp;
1747 	struct dirent *ep;
1748 	dev_vec files;
1749 	dev_map links;
1750 	dev_map cards;
1751 	struct cec_caps caps;
1752 
1753 	dp = opendir("/dev");
1754 	if (dp == NULL) {
1755 		perror ("Couldn't open the directory");
1756 		return;
1757 	}
1758 	while ((ep = readdir(dp)))
1759 		if (!memcmp(ep->d_name, "cec", 3) && isdigit(ep->d_name[3]))
1760 			files.push_back(std::string("/dev/") + ep->d_name);
1761 	closedir(dp);
1762 
1763 	/* Find device nodes which are links to other device nodes */
1764 	for (dev_vec::iterator iter = files.begin();
1765 			iter != files.end(); ) {
1766 		char link[64+1];
1767 		int link_len;
1768 		std::string target;
1769 
1770 		link_len = readlink(iter->c_str(), link, 64);
1771 		if (link_len < 0) {	/* Not a link or error */
1772 			iter++;
1773 			continue;
1774 		}
1775 		link[link_len] = '\0';
1776 
1777 		/* Only remove from files list if target itself is in list */
1778 		if (link[0] != '/')	/* Relative link */
1779 			target = std::string("/dev/");
1780 		target += link;
1781 		if (find(files.begin(), files.end(), target) == files.end()) {
1782 			iter++;
1783 			continue;
1784 		}
1785 
1786 		/* Move the device node from files to links */
1787 		if (links[target].empty())
1788 			links[target] = *iter;
1789 		else
1790 			links[target] += ", " + *iter;
1791 		iter = files.erase(iter);
1792 	}
1793 
1794 	std::sort(files.begin(), files.end(), sort_on_device_name);
1795 
1796 	for (dev_vec::iterator iter = files.begin();
1797 			iter != files.end(); ++iter) {
1798 		int fd = open(iter->c_str(), O_RDWR);
1799 		std::string cec_info;
1800 
1801 		if (fd < 0)
1802 			continue;
1803 		int err = ioctl(fd, CEC_ADAP_G_CAPS, &caps);
1804 		close(fd);
1805 		if (err)
1806 			continue;
1807 		cec_info = std::string(caps.driver) + " (" + caps.name + ")";
1808 		if (cards[cec_info].empty())
1809 			cards[cec_info] += cec_info + ":\n";
1810 		cards[cec_info] += "\t" + (*iter);
1811 		if (!(links[*iter].empty()))
1812 			cards[cec_info] += " <- " + links[*iter];
1813 		cards[cec_info] += "\n";
1814 	}
1815 	for (dev_map::iterator iter = cards.begin();
1816 			iter != cards.end(); ++iter) {
1817 		printf("%s\n", iter->second.c_str());
1818 	}
1819 }
1820 
main(int argc,char ** argv)1821 int main(int argc, char **argv)
1822 {
1823 	std::string device;
1824 	const char *driver = NULL;
1825 	const char *adapter = NULL;
1826 	const struct cec_msg_args *opt;
1827 	msg_vec msgs;
1828 	char short_options[26 * 2 * 2 + 1];
1829 	uint32_t timeout = 1000;
1830 	uint32_t monitor_time = 0;
1831 	uint32_t vendor_id = 0x000c03; /* HDMI LLC vendor ID */
1832 	unsigned int stress_test_pwr_cycle_cnt = 0;
1833 	unsigned int stress_test_pwr_cycle_min_sleep = 0;
1834 	unsigned int stress_test_pwr_cycle_max_sleep = 0;
1835 	unsigned int stress_test_pwr_cycle_polls = 30;
1836 	bool stress_test_pwr_cycle_has_seed = false;
1837 	unsigned int stress_test_pwr_cycle_seed = 0;
1838 	unsigned int stress_test_pwr_cycle_repeats = 0;
1839 	double stress_test_pwr_cycle_sleep_before_on = 0;
1840 	double stress_test_pwr_cycle_sleep_before_off = 0;
1841 	unsigned int test_pwr_cycle_polls = 15;
1842 	unsigned int test_pwr_cycle_sleep = 10;
1843 	bool warn_if_unconfigured = false;
1844 	uint16_t phys_addr;
1845 	uint8_t from = 0, to = 0, first_to = 0xff;
1846 	uint8_t dev_features = 0;
1847 	uint8_t rc_tv = 0;
1848 	uint8_t rc_src = 0;
1849 	const char *osd_name = "";
1850 	const char *edid_path = NULL;
1851 	const char *store_pin = NULL;
1852 	const char *analyze_pin = NULL;
1853 	bool reply = true;
1854 	int idx = 0;
1855 	int fd = -1;
1856 	int ch;
1857 	int i;
1858 
1859 	memset(phys_addrs, 0xff, sizeof(phys_addrs));
1860 
1861 	for (i = 0; long_options[i].name; i++) {
1862 		if (!isalpha(long_options[i].val))
1863 			continue;
1864 		short_options[idx++] = long_options[i].val;
1865 		if (long_options[i].has_arg == required_argument)
1866 			short_options[idx++] = ':';
1867 	}
1868 	while (true) {
1869 		int option_index = 0;
1870 		struct cec_msg msg;
1871 
1872 		short_options[idx] = 0;
1873 		ch = getopt_long(argc, argv, short_options,
1874 				 long_options, &option_index);
1875 		if (ch == -1)
1876 			break;
1877 
1878 		cec_msg_init(&msg, 0, 0);
1879 		msg.msg[0] = options[OptTo] ? to : 0xf0;
1880 		options[ch] = 1;
1881 
1882 		switch (ch) {
1883 		case OptHelp:
1884 			usage();
1885 			return 0;
1886 		case OptSetDevice:
1887 			device = optarg;
1888 			if (device[0] >= '0' && device[0] <= '9' && device.length() <= 3) {
1889 				static char newdev[20];
1890 
1891 				sprintf(newdev, "/dev/cec%s", optarg);
1892 				device = newdev;
1893 			}
1894 			break;
1895 		case OptSetDriver:
1896 			driver = optarg;
1897 			break;
1898 		case OptSetAdapter:
1899 			adapter = optarg;
1900 			break;
1901 		case OptVerbose:
1902 			verbose = true;
1903 			break;
1904 		case OptFrom:
1905 			from = strtoul(optarg, NULL, 0) & 0xf;
1906 			break;
1907 		case OptTo:
1908 			to = strtoul(optarg, NULL, 0) & 0xf;
1909 			if (first_to == 0xff)
1910 				first_to = to;
1911 			break;
1912 		case OptTimeout:
1913 			timeout = strtoul(optarg, NULL, 0);
1914 			break;
1915 		case OptMonitorTime:
1916 			monitor_time = strtoul(optarg, NULL, 0);
1917 			break;
1918 		case OptIgnore: {
1919 			bool all_la = !strncmp(optarg, "all", 3);
1920 			bool all_opcodes = true;
1921 			const char *sep = std::strchr(optarg, ',');
1922 			unsigned la_mask = 0xffff, opcode, la = 0;
1923 
1924 			if (sep)
1925 				all_opcodes = !strncmp(sep + 1, "all", 3);
1926 			if (!all_la) {
1927 				la = strtoul(optarg, NULL, 0);
1928 
1929 				if (la > 15) {
1930 					fprintf(stderr, "invalid logical address (> 15)\n");
1931 					usage();
1932 					return 1;
1933 				}
1934 				la_mask = 1 << la;
1935 			}
1936 			if (!all_opcodes) {
1937 				if (!strncmp(sep + 1, "poll", 4)) {
1938 					opcode = POLL_FAKE_OPCODE;
1939 				} else {
1940 					opcode = strtoul(sep + 1, NULL, 0);
1941 					if (opcode > 255) {
1942 						fprintf(stderr, "invalid opcode (> 255)\n");
1943 						usage();
1944 						return 1;
1945 					}
1946 				}
1947 				ignore_opcode[opcode] |= la_mask;
1948 				break;
1949 			}
1950 			if (all_la && all_opcodes) {
1951 				fprintf(stderr, "all,all is invalid\n");
1952 				usage();
1953 				return 1;
1954 			}
1955 			ignore_la[la] = true;
1956 			break;
1957 		}
1958 		case OptStorePin:
1959 			store_pin = optarg;
1960 			break;
1961 		case OptAnalyzePin:
1962 			analyze_pin = optarg;
1963 			break;
1964 		case OptToggleNoReply:
1965 			reply = !reply;
1966 			break;
1967 		case OptPhysAddr:
1968 			phys_addr = cec_parse_phys_addr(optarg);
1969 			break;
1970 		case OptPhysAddrFromEDIDPoll:
1971 			edid_path = optarg;
1972 			;
1973 		case OptPhysAddrFromEDID:
1974 			phys_addr = parse_phys_addr_from_edid(optarg);
1975 			break;
1976 		case OptOsdName:
1977 			osd_name = optarg;
1978 			break;
1979 		case OptVendorID:
1980 			vendor_id = strtoul(optarg, NULL, 0) & 0x00ffffff;
1981 			break;
1982 		case OptRcTVProfile1:
1983 			rc_tv = CEC_OP_FEAT_RC_TV_PROFILE_1;
1984 			break;
1985 		case OptRcTVProfile2:
1986 			rc_tv = CEC_OP_FEAT_RC_TV_PROFILE_2;
1987 			break;
1988 		case OptRcTVProfile3:
1989 			rc_tv = CEC_OP_FEAT_RC_TV_PROFILE_3;
1990 			break;
1991 		case OptRcTVProfile4:
1992 			rc_tv = CEC_OP_FEAT_RC_TV_PROFILE_4;
1993 			break;
1994 		case OptRcSrcDevRoot:
1995 			rc_src |= CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU;
1996 			break;
1997 		case OptRcSrcDevSetup:
1998 			rc_src |= CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU;
1999 			break;
2000 		case OptRcSrcContents:
2001 			rc_src |= CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU;
2002 			break;
2003 		case OptRcSrcMediaTop:
2004 			rc_src |= CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU;
2005 			break;
2006 		case OptRcSrcMediaContext:
2007 			rc_src |= CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU;
2008 			break;
2009 		case OptFeatRecordTVScreen:
2010 			dev_features |= CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN;
2011 			break;
2012 		case OptFeatSetOSDString:
2013 			dev_features |= CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING;
2014 			break;
2015 		case OptFeatDeckControl:
2016 			dev_features |= CEC_OP_FEAT_DEV_HAS_DECK_CONTROL;
2017 			break;
2018 		case OptFeatSetAudioRate:
2019 			dev_features |= CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE;
2020 			break;
2021 		case OptFeatSinkHasARCTx:
2022 			dev_features |= CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX;
2023 			break;
2024 		case OptFeatSourceHasARCRx:
2025 			dev_features |= CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX;
2026 			break;
2027 		case OptPlayback:
2028 		case OptRecord:
2029 			if (options[OptPlayback] && options[OptRecord]) {
2030 				fprintf(stderr, "--playback and --record cannot be combined.\n\n");
2031 				usage();
2032 				return 1;
2033 			}
2034 			break;
2035 		case OptSwitch:
2036 		case OptCDCOnly:
2037 		case OptUnregistered:
2038 			if (options[OptCDCOnly] + options[OptUnregistered] + options[OptSwitch] > 1) {
2039 				fprintf(stderr, "--switch, --cdc-only and --unregistered cannot be combined.\n\n");
2040 				usage();
2041 				return 1;
2042 			}
2043 			break;
2044 		case ':':
2045 			fprintf(stderr, "Option '%s' requires a value\n\n",
2046 				argv[optind]);
2047 			usage();
2048 			return 1;
2049 		case '?':
2050 			if (argv[optind])
2051 				fprintf(stderr, "Unknown argument '%s'\n\n", argv[optind]);
2052 			usage();
2053 			return 1;
2054 		case OptVendorCommand: {
2055 			static const char *arg_names[] = {
2056 				"payload",
2057 				NULL
2058 			};
2059 			char *value, *endptr, *subs = optarg;
2060 			uint8_t size = 0;
2061 			uint8_t bytes[14];
2062 
2063 			while (*subs != '\0') {
2064 				switch (cec_parse_subopt(&subs, arg_names, &value)) {
2065 				case 0:
2066 					while (size < sizeof(bytes)) {
2067 						bytes[size++] = strtol(value, &endptr, 0L);
2068 						if (endptr == value) {
2069 							size--;
2070 							break;
2071 						}
2072 						value = std::strchr(value, ':');
2073 						if (value == NULL)
2074 							break;
2075 						value++;
2076 					}
2077 					break;
2078 				default:
2079 					std::exit(EXIT_FAILURE);
2080 				}
2081 			}
2082 			if (size) {
2083 				cec_msg_vendor_command(&msg, size, bytes);
2084 				msgs.push_back(msg);
2085 			}
2086 			break;
2087 		}
2088 		case OptCustomCommand: {
2089 			static const char *arg_names[] = {
2090 				"cmd",
2091 				"payload",
2092 				NULL
2093 			};
2094 			char *value, *endptr, *subs = optarg;
2095 			bool have_cmd = false;
2096 			uint8_t cmd = 0;
2097 			uint8_t size = 0;
2098 			uint8_t bytes[14];
2099 
2100 			while (*subs != '\0') {
2101 				switch (cec_parse_subopt(&subs, arg_names, &value)) {
2102 				case 0:
2103 					cmd = strtol(value, &endptr, 0L);
2104 					have_cmd = true;
2105 					break;
2106 				case 1:
2107 					while (size < sizeof(bytes)) {
2108 						bytes[size++] = strtol(value, &endptr, 0L);
2109 						if (endptr == value) {
2110 							size--;
2111 							break;
2112 						}
2113 						value = std::strchr(value, ':');
2114 						if (value == NULL)
2115 							break;
2116 						value++;
2117 					}
2118 					break;
2119 				default:
2120 					std::exit(EXIT_FAILURE);
2121 				}
2122 			}
2123 			if (have_cmd) {
2124 				msg.len = 2 + size;
2125 				msg.msg[1] = cmd;
2126 				memcpy(msg.msg + 2, bytes, size);
2127 				msgs.push_back(msg);
2128 			}
2129 			break;
2130 		}
2131 		case OptVendorCommandWithID: {
2132 			static const char *arg_names[] = {
2133 				"vendor-id",
2134 				"cmd",
2135 				NULL
2136 			};
2137 			char *value, *endptr, *subs = optarg;
2138 			uint32_t vendor_id = 0;
2139 			uint8_t size = 0;
2140 			uint8_t bytes[11];
2141 
2142 			while (*subs != '\0') {
2143 				switch (cec_parse_subopt(&subs, arg_names, &value)) {
2144 				case 0:
2145 					vendor_id = strtol(value, 0L, 0);
2146 					break;
2147 				case 1:
2148 					while (size < sizeof(bytes)) {
2149 						bytes[size++] = strtol(value, &endptr, 0L);
2150 						if (endptr == value) {
2151 							size--;
2152 							break;
2153 						}
2154 						value = std::strchr(value, ':');
2155 						if (value == NULL)
2156 							break;
2157 						value++;
2158 					}
2159 					break;
2160 				default:
2161 					std::exit(EXIT_FAILURE);
2162 				}
2163 			}
2164 			if (size) {
2165 				cec_msg_vendor_command_with_id(&msg, vendor_id, size, bytes);
2166 				msgs.push_back(msg);
2167 			}
2168 			break;
2169 		}
2170 		case OptVendorRemoteButtonDown: {
2171 			static const char *arg_names[] = {
2172 				"rc-code",
2173 				NULL
2174 			};
2175 			char *value, *endptr, *subs = optarg;
2176 			uint8_t size = 0;
2177 			uint8_t bytes[14];
2178 
2179 			while (*subs != '\0') {
2180 				switch (cec_parse_subopt(&subs, arg_names, &value)) {
2181 				case 0:
2182 					while (size < sizeof(bytes)) {
2183 						bytes[size++] = strtol(value, &endptr, 0L);
2184 						if (endptr == value) {
2185 							size--;
2186 							break;
2187 						}
2188 						value = std::strchr(value, ':');
2189 						if (value == NULL)
2190 							break;
2191 						value++;
2192 					}
2193 					break;
2194 				default:
2195 					std::exit(EXIT_FAILURE);
2196 				}
2197 			}
2198 			if (size) {
2199 				cec_msg_vendor_remote_button_down(&msg, size, bytes);
2200 				msgs.push_back(msg);
2201 			}
2202 			break;
2203 		}
2204 		case OptPoll:
2205 			msgs.push_back(msg);
2206 			break;
2207 
2208 		case OptListDevices:
2209 			list_devices();
2210 			break;
2211 
2212 		case OptTestPowerCycle: {
2213 			static const char *arg_names[] = {
2214 				"polls",
2215 				"sleep",
2216 				NULL
2217 			};
2218 			char *value, *subs = optarg;
2219 
2220 			warn_if_unconfigured = true;
2221 			if (!optarg)
2222 				break;
2223 
2224 			while (*subs != '\0') {
2225 				switch (cec_parse_subopt(&subs, arg_names, &value)) {
2226 				case 0:
2227 					test_pwr_cycle_polls = strtoul(value, 0L, 0);
2228 					break;
2229 				case 1:
2230 					test_pwr_cycle_sleep = strtoul(value, 0L, 0);
2231 					break;
2232 				default:
2233 					std::exit(EXIT_FAILURE);
2234 				}
2235 			}
2236 			break;
2237 		}
2238 
2239 		case OptStressTestPowerCycle: {
2240 			static const char *arg_names[] = {
2241 				"cnt",
2242 				"min-sleep",
2243 				"max-sleep",
2244 				"seed",
2245 				"repeats",
2246 				"sleep-before-on",
2247 				"sleep-before-off",
2248 				"polls",
2249 				NULL
2250 			};
2251 			char *value, *subs = optarg;
2252 
2253 			while (*subs != '\0') {
2254 				switch (cec_parse_subopt(&subs, arg_names, &value)) {
2255 				case 0:
2256 					stress_test_pwr_cycle_cnt = strtoul(value, 0L, 0);
2257 					break;
2258 				case 1:
2259 					stress_test_pwr_cycle_min_sleep = strtoul(value, 0L, 0);
2260 					break;
2261 				case 2:
2262 					stress_test_pwr_cycle_max_sleep = strtoul(value, 0L, 0);
2263 					break;
2264 				case 3:
2265 					stress_test_pwr_cycle_has_seed = true;
2266 					stress_test_pwr_cycle_seed = strtoul(value, 0L, 0);
2267 					break;
2268 				case 4:
2269 					stress_test_pwr_cycle_repeats = strtoul(value, 0L, 0);
2270 					break;
2271 				case 5:
2272 					stress_test_pwr_cycle_sleep_before_on = strtod(value, NULL);
2273 					break;
2274 				case 6:
2275 					stress_test_pwr_cycle_sleep_before_off = strtod(value, NULL);
2276 					break;
2277 				case 7:
2278 					stress_test_pwr_cycle_polls = strtoul(value, 0L, 0);
2279 					break;
2280 				default:
2281 					std::exit(EXIT_FAILURE);
2282 				}
2283 			}
2284 			if (stress_test_pwr_cycle_min_sleep > stress_test_pwr_cycle_max_sleep) {
2285 				fprintf(stderr, "min-sleep > max-sleep\n");
2286 				std::exit(EXIT_FAILURE);
2287 			}
2288 			warn_if_unconfigured = true;
2289 			break;
2290 		}
2291 
2292 		default:
2293 			if (ch >= OptHelpAll) {
2294 				cec_parse_usage_options(options);
2295 				std::exit(EXIT_SUCCESS);
2296 			}
2297 			if (ch <= OptMessages)
2298 				break;
2299 			opt = cec_log_msg_args(ch - OptMessages - 1);
2300 			cec_parse_msg_args(msg, reply, opt, ch);
2301 			msgs.push_back(msg);
2302 			break;
2303 		}
2304 	}
2305 	if (optind < argc) {
2306 		fprintf(stderr, "unknown arguments: ");
2307 		while (optind < argc)
2308 			fprintf(stderr, "%s ", argv[optind++]);
2309 		fprintf(stderr, "\n");
2310 		usage();
2311 		return 1;
2312 	}
2313 
2314 	if (!msgs.empty())
2315 		warn_if_unconfigured = true;
2316 
2317 	if (store_pin && analyze_pin) {
2318 		fprintf(stderr, "--store-pin and --analyze-pin options cannot be combined.\n\n");
2319 		usage();
2320 		return 1;
2321 	}
2322 
2323 	if (analyze_pin && options[OptSetDevice]) {
2324 		fprintf(stderr, "--device and --analyze-pin options cannot be combined.\n\n");
2325 		usage();
2326 		return 1;
2327 	}
2328 
2329 	if (analyze_pin) {
2330 		analyze(analyze_pin);
2331 		return 0;
2332 	}
2333 
2334 	if (options[OptWallClock] && !options[OptMonitorPin])
2335 		verbose = true;
2336 
2337 	if (store_pin && !strcmp(store_pin, "-"))
2338 		options[OptSkipInfo] = 1;
2339 
2340 	if (rc_tv && rc_src) {
2341 		fprintf(stderr, "--rc-tv- and --rc-src- options cannot be combined.\n\n");
2342 		usage();
2343 		return 1;
2344 	}
2345 
2346 	if (rc_tv && !options[OptTV]) {
2347 		fprintf(stderr, "--rc-tv- can only be used in combination with --tv.\n\n");
2348 		usage();
2349 		return 1;
2350 	}
2351 
2352 	if (rc_src && options[OptTV]) {
2353 		fprintf(stderr, "--rc-src- can't be used in combination with --tv.\n\n");
2354 		usage();
2355 		return 1;
2356 	}
2357 
2358 	if ((dev_features & (CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX |
2359 			     CEC_OP_FEAT_DEV_HAS_DECK_CONTROL |
2360 			     CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE)) && options[OptTV]) {
2361 		fprintf(stderr, "--feat-deck-control, --feat-set-audio-rate and --feat-source-has-arc-rx cannot be used in combination with --tv.\n\n");
2362 		usage();
2363 		return 1;
2364 	}
2365 
2366 	if ((dev_features & (CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN |
2367 			     CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING)) && !options[OptTV]) {
2368 		fprintf(stderr, "--feat-set-osd-string and --feat-record-tv-screen can only be used in combination with --tv.\n\n");
2369 		usage();
2370 		return 1;
2371 	}
2372 
2373 	if (device.empty() && (driver || adapter)) {
2374 		device = cec_device_find(driver, adapter);
2375 		if (device.empty()) {
2376 			fprintf(stderr,
2377 				"Could not find a CEC device for the given driver/adapter combination\n");
2378 			std::exit(EXIT_FAILURE);
2379 		}
2380 	}
2381 	if (device.empty())
2382 		device = "/dev/cec0";
2383 
2384 	clock_gettime(CLOCK_MONOTONIC, &start_monotonic);
2385 	gettimeofday(&start_timeofday, NULL);
2386 
2387 	if ((fd = open(device.c_str(), O_RDWR)) < 0) {
2388 		fprintf(stderr, "Failed to open %s: %s\n", device.c_str(),
2389 			strerror(errno));
2390 		std::exit(EXIT_FAILURE);
2391 	}
2392 
2393 	struct node node;
2394 	struct cec_caps caps = { };
2395 
2396 	node.fd = fd;
2397 	node.device = device.c_str();
2398 	doioctl(&node, CEC_ADAP_G_CAPS, &caps);
2399 	node.caps = caps.capabilities;
2400 	node.available_log_addrs = caps.available_log_addrs;
2401 
2402 	bool set_phys_addr = options[OptPhysAddr] || options[OptPhysAddrFromEDID] ||
2403 			     options[OptPhysAddrFromEDIDPoll];
2404 
2405 	if (set_phys_addr && !(node.caps & CEC_CAP_PHYS_ADDR))
2406 		fprintf(stderr, "The CEC adapter doesn't allow setting the physical address manually, ignore this option.\n\n");
2407 
2408 	unsigned flags = 0;
2409 
2410 	if (options[OptOsdName])
2411 		;
2412 	else if (options[OptTV])
2413 		osd_name = "TV";
2414 	else if (options[OptRecord])
2415 		osd_name = "Record";
2416 	else if (options[OptPlayback])
2417 		osd_name = "Playback";
2418 	else if (options[OptTuner])
2419 		osd_name = "Tuner";
2420 	else if (options[OptAudio])
2421 		osd_name = "Audio System";
2422 	else if (options[OptProcessor])
2423 		osd_name = "Processor";
2424 	else if (options[OptSwitch] || options[OptCDCOnly] || options[OptUnregistered])
2425 		osd_name = "";
2426 	else
2427 		osd_name = "TV";
2428 
2429 	if (options[OptTV])
2430 		flags |= 1 << CEC_OP_PRIM_DEVTYPE_TV;
2431 	if (options[OptRecord])
2432 		flags |= 1 << CEC_OP_PRIM_DEVTYPE_RECORD;
2433 	if (options[OptTuner])
2434 		flags |= 1 << CEC_OP_PRIM_DEVTYPE_TUNER;
2435 	if (options[OptPlayback])
2436 		flags |= 1 << CEC_OP_PRIM_DEVTYPE_PLAYBACK;
2437 	if (options[OptAudio])
2438 		flags |= 1 << CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
2439 	if (options[OptProcessor])
2440 		flags |= 1 << CEC_OP_PRIM_DEVTYPE_PROCESSOR;
2441 	if (options[OptSwitch] || options[OptCDCOnly] || options[OptUnregistered])
2442 		flags |= 1 << CEC_OP_PRIM_DEVTYPE_SWITCH;
2443 
2444 	if (flags == 0 && (rc_tv || rc_src || dev_features)) {
2445 		fprintf(stderr, "The --feat- and --rc- options can only be used in combination with selecting the device type.\n\n");
2446 		usage();
2447 		return 1;
2448 	}
2449 
2450 	bool set_log_addrs = (node.caps & CEC_CAP_LOG_ADDRS) && flags;
2451 	bool clear_log_addrs = (node.caps & CEC_CAP_LOG_ADDRS) && options[OptClear];
2452 
2453 	// When setting both PA and LA it is best to clear the LAs first.
2454 	if (clear_log_addrs || (set_phys_addr && set_log_addrs)) {
2455 		struct cec_log_addrs laddrs = { };
2456 
2457 		doioctl(&node, CEC_ADAP_S_LOG_ADDRS, &laddrs);
2458 	}
2459 
2460 	if (set_phys_addr)
2461 		doioctl(&node, CEC_ADAP_S_PHYS_ADDR, &phys_addr);
2462 
2463 	doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &phys_addr);
2464 
2465 	if (set_log_addrs) {
2466 		struct cec_log_addrs laddrs = {};
2467 		uint8_t all_dev_types = 0;
2468 		uint8_t prim_type = 0xff;
2469 
2470 		doioctl(&node, CEC_ADAP_S_LOG_ADDRS, &laddrs);
2471 		memset(&laddrs, 0, sizeof(laddrs));
2472 
2473 		laddrs.cec_version = options[OptCECVersion1_4] ?
2474 			CEC_OP_CEC_VERSION_1_4 : CEC_OP_CEC_VERSION_2_0;
2475 		strncpy(laddrs.osd_name, osd_name, sizeof(laddrs.osd_name));
2476 		laddrs.osd_name[sizeof(laddrs.osd_name) - 1] = 0;
2477 		laddrs.vendor_id = vendor_id;
2478 		if (options[OptAllowUnregFallback])
2479 			laddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
2480 		if (!options[OptNoRC] && flags != (1 << CEC_OP_PRIM_DEVTYPE_SWITCH))
2481 			laddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
2482 		if (options[OptCDCOnly])
2483 			laddrs.flags |= CEC_LOG_ADDRS_FL_CDC_ONLY;
2484 
2485 		for (unsigned i = 0; i < 8; i++) {
2486 			unsigned la_type;
2487 
2488 			if (!(flags & (1 << i)))
2489 				continue;
2490 			if (prim_type == 0xff)
2491 				prim_type = i;
2492 			if (laddrs.num_log_addrs == node.available_log_addrs) {
2493 				fprintf(stderr, "Attempt to define too many logical addresses\n");
2494 				std::exit(EXIT_FAILURE);
2495 			}
2496 			switch (i) {
2497 			case CEC_OP_PRIM_DEVTYPE_TV:
2498 				la_type = CEC_LOG_ADDR_TYPE_TV;
2499 				all_dev_types |= CEC_OP_ALL_DEVTYPE_TV;
2500 				prim_type = i;
2501 				break;
2502 			case CEC_OP_PRIM_DEVTYPE_RECORD:
2503 				la_type = CEC_LOG_ADDR_TYPE_RECORD;
2504 				all_dev_types |= CEC_OP_ALL_DEVTYPE_RECORD;
2505 				break;
2506 			case CEC_OP_PRIM_DEVTYPE_TUNER:
2507 				la_type = CEC_LOG_ADDR_TYPE_TUNER;
2508 				all_dev_types |= CEC_OP_ALL_DEVTYPE_TUNER;
2509 				break;
2510 			case CEC_OP_PRIM_DEVTYPE_PLAYBACK:
2511 				la_type = CEC_LOG_ADDR_TYPE_PLAYBACK;
2512 				all_dev_types |= CEC_OP_ALL_DEVTYPE_PLAYBACK;
2513 				break;
2514 			case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:
2515 				la_type = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
2516 				all_dev_types |= CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
2517 				if (prim_type != CEC_OP_PRIM_DEVTYPE_TV)
2518 					prim_type = i;
2519 				break;
2520 			case CEC_OP_PRIM_DEVTYPE_PROCESSOR:
2521 				la_type = CEC_LOG_ADDR_TYPE_SPECIFIC;
2522 				all_dev_types |= CEC_OP_ALL_DEVTYPE_SWITCH;
2523 				break;
2524 			case CEC_OP_PRIM_DEVTYPE_SWITCH:
2525 			default:
2526 				la_type = CEC_LOG_ADDR_TYPE_UNREGISTERED;
2527 				all_dev_types |= CEC_OP_ALL_DEVTYPE_SWITCH;
2528 				break;
2529 			}
2530 			laddrs.log_addr_type[laddrs.num_log_addrs++] = la_type;
2531 		}
2532 		for (unsigned i = 0; i < laddrs.num_log_addrs; i++) {
2533 			laddrs.primary_device_type[i] = prim_type;
2534 			laddrs.all_device_types[i] = all_dev_types;
2535 			laddrs.features[i][0] = rc_tv | rc_src;
2536 			laddrs.features[i][1] = dev_features;
2537 		}
2538 
2539 		doioctl(&node, CEC_ADAP_S_LOG_ADDRS, &laddrs);
2540 	}
2541 
2542 	struct cec_log_addrs laddrs = { };
2543 	doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
2544 	node.num_log_addrs = laddrs.num_log_addrs;
2545 	node.log_addr_mask = laddrs.log_addr_mask;
2546 	node.phys_addr = phys_addr;
2547 
2548 	for (i = 0; i < laddrs.num_log_addrs; i++) {
2549 		uint8_t la = laddrs.log_addr[i];
2550 
2551 		if (la != CEC_LOG_ADDR_INVALID)
2552 			phys_addrs[la] = (phys_addr << 8) | la;
2553 	}
2554 
2555 	if (!options[OptSkipInfo]) {
2556 		struct cec_connector_info conn_info = {};
2557 
2558 		doioctl(&node, CEC_ADAP_G_CONNECTOR_INFO, &conn_info);
2559 
2560 		cec_driver_info(caps, laddrs, phys_addr, conn_info);
2561 	}
2562 
2563 	if (node.num_log_addrs == 0) {
2564 		if (options[OptMonitor] || options[OptMonitorAll] ||
2565 		    options[OptMonitorPin] || options[OptStorePin] ||
2566 		    options[OptPhysAddrFromEDIDPoll])
2567 			goto skip_la;
2568 		if (warn_if_unconfigured)
2569 			fprintf(stderr, "\nAdapter is unconfigured, please configure it first.\n");
2570 		return 0;
2571 	}
2572 	if (!options[OptSkipInfo])
2573 		printf("\n");
2574 
2575 	if (!options[OptFrom])
2576 		from = laddrs.log_addr[0] & 0xf;
2577 
2578 	if (options[OptShowTopology])
2579 		showTopology(&node);
2580 
2581 	if (options[OptLogicalAddress])
2582 		printf("%d\n", laddrs.log_addr[0] & 0xf);
2583 	if (options[OptLogicalAddresses]) {
2584 		for (i = 0; i < laddrs.num_log_addrs; i++)
2585 			printf("%d ", laddrs.log_addr[i] & 0xf);
2586 		printf("\n");
2587 	}
2588 
2589 	if (options[OptWaitForMsgs]) {
2590 		uint32_t monitor = CEC_MODE_INITIATOR | CEC_MODE_FOLLOWER;
2591 
2592 		if (doioctl(&node, CEC_S_MODE, &monitor)) {
2593 			fprintf(stderr, "Selecting follower mode failed.\n");
2594 			return 1;
2595 		}
2596 	}
2597 	if (options[OptNonBlocking])
2598 		fcntl(node.fd, F_SETFL, fcntl(node.fd, F_GETFL) | O_NONBLOCK);
2599 
2600 	for (msg_vec::iterator iter = msgs.begin(); iter != msgs.end(); ++iter) {
2601 		struct cec_msg msg = *iter;
2602 
2603 		fflush(stdout);
2604 		if (!cec_msg_is_broadcast(&msg) && !options[OptTo]) {
2605 			fprintf(stderr, "attempting to send message without --to\n");
2606 			std::exit(EXIT_FAILURE);
2607 		}
2608 		if (msg.msg[0] == 0xf0)
2609 			msg.msg[0] = first_to;
2610 		msg.msg[0] &= 0x0f;
2611 		msg.msg[0] |= from << 4;
2612 		to = msg.msg[0] & 0xf;
2613 		printf("\nTransmit from %s to %s (%d to %d):\n",
2614 		       cec_la2s(from), to == 0xf ? "all" : cec_la2s(to), from, to);
2615 		msg.flags = options[OptReplyToFollowers] ? CEC_MSG_FL_REPLY_TO_FOLLOWERS : 0;
2616 		msg.flags |= options[OptRawMsg] ? CEC_MSG_FL_RAW : 0;
2617 		msg.timeout = msg.reply ? timeout : 0;
2618 		cec_log_msg(&msg);
2619 		if (doioctl(&node, CEC_TRANSMIT, &msg))
2620 			continue;
2621 		if (msg.rx_status & (CEC_RX_STATUS_OK | CEC_RX_STATUS_FEATURE_ABORT)) {
2622 			printf("    Received from %s (%d):\n    ", cec_la2s(cec_msg_initiator(&msg)),
2623 			       cec_msg_initiator(&msg));
2624 			cec_log_msg(&msg);
2625 			if (options[OptShowRaw])
2626 				log_raw_msg(&msg);
2627 		}
2628 		printf("\tSequence: %u Tx Timestamp: %s",
2629 			msg.sequence, ts2s(msg.tx_ts).c_str());
2630 		if (msg.rx_ts)
2631 			printf(" Rx Timestamp: %s\n\tApproximate response time: %u ms",
2632 				ts2s(msg.rx_ts).c_str(),
2633 				response_time_ms(msg));
2634 		printf("\n");
2635 		if (!cec_msg_status_is_ok(&msg) || verbose)
2636 			printf("\t%s\n", cec_status2s(msg).c_str());
2637 	}
2638 	fflush(stdout);
2639 
2640 	if (options[OptNonBlocking])
2641 		fcntl(node.fd, F_SETFL, fcntl(node.fd, F_GETFL) & ~O_NONBLOCK);
2642 
2643 	if (options[OptTestPowerCycle])
2644 		test_power_cycle(node, test_pwr_cycle_polls, test_pwr_cycle_sleep);
2645 	if (options[OptStressTestPowerCycle])
2646 		stress_test_power_cycle(node, stress_test_pwr_cycle_cnt,
2647 					stress_test_pwr_cycle_min_sleep,
2648 					stress_test_pwr_cycle_max_sleep,
2649 					stress_test_pwr_cycle_polls,
2650 					stress_test_pwr_cycle_has_seed,
2651 					stress_test_pwr_cycle_seed,
2652 					stress_test_pwr_cycle_repeats,
2653 					stress_test_pwr_cycle_sleep_before_on,
2654 					stress_test_pwr_cycle_sleep_before_off);
2655 
2656 skip_la:
2657 	if (options[OptPhysAddrFromEDIDPoll]) {
2658 		bool has_edid;
2659 		char dummy;
2660 		int fd;
2661 
2662 		fd = open(edid_path, O_RDONLY);
2663 		if (fd < 0)
2664 			std::exit(EXIT_FAILURE);
2665 		lseek(fd, 0, SEEK_SET);
2666 		has_edid = read(fd, &dummy, 1) > 0;
2667 
2668 		if (!has_edid)
2669 			phys_addr = CEC_PHYS_ADDR_INVALID;
2670 		else
2671 			phys_addr = parse_phys_addr_from_edid(edid_path);
2672 		doioctl(&node, CEC_ADAP_S_PHYS_ADDR, &phys_addr);
2673 		printf("Physical Address: %x.%x.%x.%x\n", cec_phys_addr_exp(phys_addr));
2674 
2675 		for (;;) {
2676 			bool edid;
2677 
2678 			/* Poll every 100 ms */
2679 			usleep(100000);
2680 			lseek(fd, 0, SEEK_SET);
2681 			edid = read(fd, &dummy, 1) > 0;
2682 			if (has_edid != edid) {
2683 				has_edid = edid;
2684 				if (!edid)
2685 					phys_addr = CEC_PHYS_ADDR_INVALID;
2686 				else
2687 					phys_addr = parse_phys_addr_from_edid(edid_path);
2688 				doioctl(&node, CEC_ADAP_S_PHYS_ADDR, &phys_addr);
2689 				printf("Physical Address: %x.%x.%x.%x\n",
2690 				       cec_phys_addr_exp(phys_addr));
2691 			}
2692 		}
2693 	}
2694 
2695 	if (options[OptMonitor] || options[OptMonitorAll] ||
2696 	    options[OptMonitorPin] || options[OptStorePin])
2697 		monitor(node, monitor_time, store_pin);
2698 	else if (options[OptWaitForMsgs])
2699 		wait_for_msgs(node, monitor_time);
2700 	fflush(stdout);
2701 	close(fd);
2702 	return 0;
2703 }
2704