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