1 // SPDX-License-Identifier: (LGPL-2.1-only OR BSD-3-Clause)
2 /*
3 * CEC common helper functions
4 *
5 * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6 */
7
8 #include <cctype>
9 #include <string>
10
11 #include <dirent.h>
12 #include <fcntl.h>
13 #include <sys/ioctl.h>
14 #include <unistd.h>
15
16 #include <cec-info.h>
17 #include <cec-htng.h>
18
19 #include "cec-msgs-gen.h"
20
cec_opcode2s(unsigned opcode)21 const char *cec_opcode2s(unsigned opcode)
22 {
23 for (const auto &i : msgtable)
24 if (i.opcode == opcode)
25 return i.name;
26 return nullptr;
27 }
28
cec_cdc_opcode2s(unsigned cdc_opcode)29 const char *cec_cdc_opcode2s(unsigned cdc_opcode)
30 {
31 for (const auto &i : cdcmsgtable)
32 if (i.opcode == cdc_opcode)
33 return i.name;
34 return nullptr;
35 }
36
cec_htng_opcode2s(unsigned htng_opcode)37 const char *cec_htng_opcode2s(unsigned htng_opcode)
38 {
39 for (const auto &i : htngmsgtable)
40 if (i.opcode == htng_opcode)
41 return i.name;
42 return nullptr;
43 }
44
caps2s(unsigned caps)45 static std::string caps2s(unsigned caps)
46 {
47 std::string s;
48
49 if (caps & CEC_CAP_PHYS_ADDR)
50 s += "\t\tPhysical Address\n";
51 if (caps & CEC_CAP_LOG_ADDRS)
52 s += "\t\tLogical Addresses\n";
53 if (caps & CEC_CAP_TRANSMIT)
54 s += "\t\tTransmit\n";
55 if (caps & CEC_CAP_PASSTHROUGH)
56 s += "\t\tPassthrough\n";
57 if (caps & CEC_CAP_RC)
58 s += "\t\tRemote Control Support\n";
59 if (caps & CEC_CAP_MONITOR_ALL)
60 s += "\t\tMonitor All\n";
61 if (caps & CEC_CAP_NEEDS_HPD)
62 s += "\t\tNeeds HPD\n";
63 if (caps & CEC_CAP_MONITOR_PIN)
64 s += "\t\tMonitor Pin\n";
65 if (caps & CEC_CAP_CONNECTOR_INFO)
66 s += "\t\tConnector Info\n";
67 return s;
68 }
69
laflags2s(unsigned flags)70 static std::string laflags2s(unsigned flags)
71 {
72 std::string s;
73
74 if (!flags)
75 return s;
76
77 s = "(";
78 if (flags & CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK)
79 s += "Allow Fallback to Unregistered, ";
80 if (flags & CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU)
81 s += "Allow RC Passthrough, ";
82 if (flags & CEC_LOG_ADDRS_FL_CDC_ONLY)
83 s += "CDC-Only, ";
84 if (s.length())
85 s.erase(s.length() - 2, 2);
86 return s + ")";
87 }
88
cec_version2s(unsigned version)89 const char *cec_version2s(unsigned version)
90 {
91 switch (version) {
92 case CEC_OP_CEC_VERSION_1_3A:
93 return "1.3a";
94 case CEC_OP_CEC_VERSION_1_4:
95 return "1.4";
96 case CEC_OP_CEC_VERSION_2_0:
97 return "2.0";
98 default:
99 return "Unknown";
100 }
101 }
102
103 /*
104 * Most of these vendor IDs come from include/cectypes.h from libcec.
105 */
cec_vendor2s(unsigned vendor)106 const char *cec_vendor2s(unsigned vendor)
107 {
108 switch (vendor) {
109 case 0x000039:
110 case 0x000ce7:
111 return "Toshiba";
112 case 0x0000f0:
113 return "Samsung";
114 case 0x0005cd:
115 return "Denon";
116 case 0x000678:
117 return "Marantz";
118 case 0x000982:
119 return "Loewe";
120 case 0x0009b0:
121 return "Onkyo";
122 case 0x000c03:
123 return "HDMI";
124 case 0x001582:
125 return "Pulse-Eight";
126 case 0x001950:
127 case 0x9c645e:
128 return "Harman Kardon";
129 case 0x001a11:
130 return "Google";
131 case 0x0020c7:
132 return "Akai";
133 case 0x002467:
134 return "AOC";
135 case 0x005060:
136 return "Cisco";
137 case 0x008045:
138 return "Panasonic";
139 case 0x00903e:
140 return "Philips";
141 case 0x009053:
142 return "Daewoo";
143 case 0x00a0de:
144 return "Yamaha";
145 case 0x00d0d5:
146 return "Grundig";
147 case 0x00d38d:
148 return "Hospitality Profile";
149 case 0x00e036:
150 return "Pioneer";
151 case 0x00e091:
152 return "LG";
153 case 0x08001f:
154 case 0x534850:
155 return "Sharp";
156 case 0x080046:
157 return "Sony";
158 case 0x0acd8f:
159 return "CDDC";
160 case 0x18c086:
161 return "Broadcom";
162 case 0x5cad76:
163 return "TCL";
164 case 0x6b746d:
165 return "Vizio";
166 case 0x743a65:
167 return "NEC";
168 case 0x8065e9:
169 return "Benq";
170 default:
171 return nullptr;
172 }
173 }
174
cec_prim_type2s(unsigned type)175 const char *cec_prim_type2s(unsigned type)
176 {
177 switch (type) {
178 case CEC_OP_PRIM_DEVTYPE_TV:
179 return "TV";
180 case CEC_OP_PRIM_DEVTYPE_RECORD:
181 return "Record";
182 case CEC_OP_PRIM_DEVTYPE_TUNER:
183 return "Tuner";
184 case CEC_OP_PRIM_DEVTYPE_PLAYBACK:
185 return "Playback";
186 case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:
187 return "Audio System";
188 case CEC_OP_PRIM_DEVTYPE_SWITCH:
189 return "Switch";
190 case CEC_OP_PRIM_DEVTYPE_PROCESSOR:
191 return "Processor";
192 default:
193 return "Unknown";
194 }
195 }
196
cec_la_type2s(unsigned type)197 const char *cec_la_type2s(unsigned type)
198 {
199 switch (type) {
200 case CEC_LOG_ADDR_TYPE_TV:
201 return "TV";
202 case CEC_LOG_ADDR_TYPE_RECORD:
203 return "Record";
204 case CEC_LOG_ADDR_TYPE_TUNER:
205 return "Tuner";
206 case CEC_LOG_ADDR_TYPE_PLAYBACK:
207 return "Playback";
208 case CEC_LOG_ADDR_TYPE_AUDIOSYSTEM:
209 return "Audio System";
210 case CEC_LOG_ADDR_TYPE_SPECIFIC:
211 return "Specific";
212 case CEC_LOG_ADDR_TYPE_UNREGISTERED:
213 return "Unregistered";
214 default:
215 return "Unknown";
216 }
217 }
218
cec_la2s(unsigned la)219 const char *cec_la2s(unsigned la)
220 {
221 switch (la & 0xf) {
222 case 0:
223 return "TV";
224 case 1:
225 return "Recording Device 1";
226 case 2:
227 return "Recording Device 2";
228 case 3:
229 return "Tuner 1";
230 case 4:
231 return "Playback Device 1";
232 case 5:
233 return "Audio System";
234 case 6:
235 return "Tuner 2";
236 case 7:
237 return "Tuner 3";
238 case 8:
239 return "Playback Device 2";
240 case 9:
241 return "Recording Device 3";
242 case 10:
243 return "Tuner 4";
244 case 11:
245 return "Playback Device 3";
246 case 12:
247 return "Backup 1";
248 case 13:
249 return "Backup 2";
250 case 14:
251 return "Specific";
252 case 15:
253 default:
254 return "Unregistered";
255 }
256 }
257
cec_all_dev_types2s(unsigned types)258 std::string cec_all_dev_types2s(unsigned types)
259 {
260 std::string s;
261
262 if (types & CEC_OP_ALL_DEVTYPE_TV)
263 s += "TV, ";
264 if (types & CEC_OP_ALL_DEVTYPE_RECORD)
265 s += "Record, ";
266 if (types & CEC_OP_ALL_DEVTYPE_TUNER)
267 s += "Tuner, ";
268 if (types & CEC_OP_ALL_DEVTYPE_PLAYBACK)
269 s += "Playback, ";
270 if (types & CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM)
271 s += "Audio System, ";
272 if (types & CEC_OP_ALL_DEVTYPE_SWITCH)
273 s += "Switch, ";
274 if (s.length())
275 return s.erase(s.length() - 2, 2);
276 return s;
277 }
278
cec_rc_src_prof2s(unsigned prof,const std::string & prefix)279 std::string cec_rc_src_prof2s(unsigned prof, const std::string &prefix)
280 {
281 std::string s;
282
283 prof &= 0x1f;
284 if (prof == 0)
285 return prefix + "\t\tNone\n";
286 if (prof & CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU)
287 s += prefix + "\t\tSource Has Device Root Menu\n";
288 if (prof & CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU)
289 s += prefix + "\t\tSource Has Device Setup Menu\n";
290 if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU)
291 s += prefix + "\t\tSource Has Contents Menu\n";
292 if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU)
293 s += prefix + "\t\tSource Has Media Top Menu\n";
294 if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU)
295 s += prefix + "\t\tSource Has Media Context-Sensitive Menu\n";
296 return s;
297 }
298
cec_dev_feat2s(unsigned feat,const std::string & prefix)299 std::string cec_dev_feat2s(unsigned feat, const std::string &prefix)
300 {
301 std::string s;
302
303 feat &= 0x7e;
304 if (feat == 0)
305 return prefix + "\t\tNone\n";
306 if (feat & CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN)
307 s += prefix + "\t\tTV Supports <Record TV Screen>\n";
308 if (feat & CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING)
309 s += prefix + "\t\tTV Supports <Set OSD String>\n";
310 if (feat & CEC_OP_FEAT_DEV_HAS_DECK_CONTROL)
311 s += prefix + "\t\tSupports Deck Control\n";
312 if (feat & CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE)
313 s += prefix + "\t\tSource Supports <Set Audio Rate>\n";
314 if (feat & CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX)
315 s += prefix + "\t\tSink Supports ARC Tx\n";
316 if (feat & CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX)
317 s += prefix + "\t\tSource Supports ARC Rx\n";
318 return s;
319 }
320
tx_status2s(const struct cec_msg & msg)321 static std::string tx_status2s(const struct cec_msg &msg)
322 {
323 std::string s;
324 char num[4];
325 unsigned stat = msg.tx_status;
326
327 if (stat)
328 s += "Tx";
329 if (stat & CEC_TX_STATUS_OK)
330 s += ", OK";
331 if (stat & CEC_TX_STATUS_ARB_LOST) {
332 sprintf(num, "%u", msg.tx_arb_lost_cnt);
333 s += ", Arbitration Lost";
334 if (msg.tx_arb_lost_cnt)
335 s += " (" + std::string(num) + ")";
336 }
337 if (stat & CEC_TX_STATUS_NACK) {
338 sprintf(num, "%u", msg.tx_nack_cnt);
339 s += ", Not Acknowledged";
340 if (msg.tx_nack_cnt)
341 s += " (" + std::string(num) + ")";
342 }
343 if (stat & CEC_TX_STATUS_LOW_DRIVE) {
344 sprintf(num, "%u", msg.tx_low_drive_cnt);
345 s += ", Low Drive";
346 if (msg.tx_low_drive_cnt)
347 s += " (" + std::string(num) + ")";
348 }
349 if (stat & CEC_TX_STATUS_ERROR) {
350 sprintf(num, "%u", msg.tx_error_cnt);
351 s += ", Error";
352 if (msg.tx_error_cnt)
353 s += " (" + std::string(num) + ")";
354 }
355 if (stat & CEC_TX_STATUS_ABORTED)
356 s += ", Aborted";
357 if (stat & CEC_TX_STATUS_TIMEOUT)
358 s += ", Timeout";
359 if (stat & CEC_TX_STATUS_MAX_RETRIES)
360 s += ", Max Retries";
361 return s;
362 }
363
rx_status2s(unsigned stat)364 static std::string rx_status2s(unsigned stat)
365 {
366 std::string s;
367
368 if (stat)
369 s += "Rx";
370 if (stat & CEC_RX_STATUS_OK)
371 s += ", OK";
372 if (stat & CEC_RX_STATUS_TIMEOUT)
373 s += ", Timeout";
374 if (stat & CEC_RX_STATUS_FEATURE_ABORT)
375 s += ", Feature Abort";
376 if (stat & CEC_RX_STATUS_ABORTED)
377 s += ", Aborted";
378 return s;
379 }
380
cec_status2s(const struct cec_msg & msg)381 std::string cec_status2s(const struct cec_msg &msg)
382 {
383 std::string s;
384
385 if (msg.tx_status)
386 s = tx_status2s(msg);
387 if (msg.rx_status) {
388 if (!s.empty())
389 s += ", ";
390 s += rx_status2s(msg.rx_status);
391 }
392 return s;
393 }
394
cec_driver_info(const struct cec_caps & caps,const struct cec_log_addrs & laddrs,__u16 phys_addr,const struct cec_connector_info & conn_info)395 void cec_driver_info(const struct cec_caps &caps,
396 const struct cec_log_addrs &laddrs, __u16 phys_addr,
397 const struct cec_connector_info &conn_info)
398 {
399 printf("Driver Info:\n");
400 printf("\tDriver Name : %s\n", caps.driver);
401 printf("\tAdapter Name : %s\n", caps.name);
402 printf("\tCapabilities : 0x%08x\n", caps.capabilities);
403 printf("%s", caps2s(caps.capabilities).c_str());
404 printf("\tDriver version : %d.%d.%d\n",
405 caps.version >> 16,
406 (caps.version >> 8) & 0xff,
407 caps.version & 0xff);
408 printf("\tAvailable Logical Addresses: %u\n",
409 caps.available_log_addrs);
410 switch (conn_info.type) {
411 case CEC_CONNECTOR_TYPE_NO_CONNECTOR:
412 printf("\tConnector Info : None\n");
413 break;
414 case CEC_CONNECTOR_TYPE_DRM:
415 printf("\tDRM Connector Info : card %u, connector %u\n",
416 conn_info.drm.card_no, conn_info.drm.connector_id);
417 break;
418 default:
419 printf("\tConnector Info : Type %u\n",
420 conn_info.type);
421 break;
422 }
423
424 printf("\tPhysical Address : %x.%x.%x.%x\n",
425 cec_phys_addr_exp(phys_addr));
426 printf("\tLogical Address Mask : 0x%04x\n", laddrs.log_addr_mask);
427 printf("\tCEC Version : %s\n", cec_version2s(laddrs.cec_version));
428 if (laddrs.vendor_id != CEC_VENDOR_ID_NONE) {
429 const char *vendor = cec_vendor2s(laddrs.vendor_id);
430
431 if (vendor)
432 printf("\tVendor ID : 0x%06x (%s)\n",
433 laddrs.vendor_id, vendor);
434 else
435 printf("\tVendor ID : 0x%06x, %u\n",
436 laddrs.vendor_id, laddrs.vendor_id);
437 }
438 printf("\tOSD Name : '%s'\n", laddrs.osd_name);
439 printf("\tLogical Addresses : %u %s\n",
440 laddrs.num_log_addrs, laflags2s(laddrs.flags).c_str());
441 for (unsigned i = 0; i < laddrs.num_log_addrs; i++) {
442 if (laddrs.log_addr[i] == CEC_LOG_ADDR_INVALID)
443 printf("\n\t Logical Address : Not Allocated\n");
444 else
445 printf("\n\t Logical Address : %d (%s)\n",
446 laddrs.log_addr[i], cec_la2s(laddrs.log_addr[i]));
447 printf("\t Primary Device Type : %s\n",
448 cec_prim_type2s(laddrs.primary_device_type[i]));
449 printf("\t Logical Address Type : %s\n",
450 cec_la_type2s(laddrs.log_addr_type[i]));
451 printf("\t All Device Types : %s\n",
452 cec_all_dev_types2s(laddrs.all_device_types[i]).c_str());
453
454 bool is_dev_feat = false;
455 for (__u8 byte : laddrs.features[i]) {
456 if (!is_dev_feat) {
457 if (byte & 0x40) {
458 printf("\t RC Source Profile :\n%s",
459 cec_rc_src_prof2s(byte, "").c_str());
460 } else {
461 const char *s = "Reserved";
462
463 switch (byte & 0xf) {
464 case 0:
465 s = "None";
466 break;
467 case 2:
468 s = "RC Profile 1";
469 break;
470 case 6:
471 s = "RC Profile 2";
472 break;
473 case 10:
474 s = "RC Profile 3";
475 break;
476 case 14:
477 s = "RC Profile 4";
478 break;
479 }
480 printf("\t RC TV Profile : %s\n", s);
481 }
482 } else {
483 printf("\t Device Features :\n%s",
484 cec_dev_feat2s(byte, "").c_str());
485 }
486 if (byte & CEC_OP_FEAT_EXT)
487 continue;
488 if (!is_dev_feat)
489 is_dev_feat = true;
490 else
491 break;
492 }
493 }
494 }
495
cec_device_find(const char * driver,const char * adapter)496 std::string cec_device_find(const char *driver, const char *adapter)
497 {
498 DIR *dp;
499 struct dirent *ep;
500 std::string name;
501
502 dp = opendir("/dev");
503 if (dp == nullptr) {
504 perror("Couldn't open the directory");
505 return name;
506 }
507 while ((ep = readdir(dp)))
508 if (!memcmp(ep->d_name, "cec", 3) && isdigit(ep->d_name[3])) {
509 std::string devname("/dev/");
510 struct cec_caps caps;
511 int fd;
512
513 devname += ep->d_name;
514 fd = open(devname.c_str(), O_RDWR);
515
516 if (fd < 0)
517 continue;
518 int err = ioctl(fd, CEC_ADAP_G_CAPS, &caps);
519 close(fd);
520 if (err)
521 continue;
522 if ((!driver || !strcmp(driver, caps.driver)) &&
523 (!adapter || !strcmp(adapter, caps.name))) {
524 name = devname;
525 break;
526 }
527 }
528 closedir(dp);
529 return name;
530 }
531