• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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