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