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