1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 /*
3  * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4  */
5 
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <inttypes.h>
11 #include <stdarg.h>
12 #include <string>
13 #include <linux/cec-funcs.h>
14 #include "cec-htng-funcs.h"
15 #include "cec-info.h"
16 #include "cec-log.h"
17 #include <sys/cdefs.h>
18 
19 static const struct cec_arg arg_u8 = {
20 	CEC_ARG_TYPE_U8,
21 };
22 
23 static const struct cec_arg arg_u16 = {
24 	CEC_ARG_TYPE_U16,
25 };
26 
27 static const struct cec_arg arg_u32 = {
28 	CEC_ARG_TYPE_U32,
29 };
30 
31 static const struct cec_arg arg_string = {
32 	CEC_ARG_TYPE_STRING,
33 };
34 
log_arg(const struct cec_arg * arg,const char * arg_name,uint32_t val)35 static void log_arg(const struct cec_arg *arg, const char *arg_name, uint32_t val)
36 {
37 	unsigned i;
38 
39 	switch (arg->type) {
40 	case CEC_ARG_TYPE_ENUM:
41 		for (i = 0; i < arg->num_enum_values; i++) {
42 			if (arg->values[i].value == val) {
43 				printf("\t%s: %s (0x%02x)\n", arg_name,
44 				       arg->values[i].type_name, val);
45 				return;
46 			}
47 		}
48 		;
49 	case CEC_ARG_TYPE_U8:
50 		if (!strcmp(arg_name, "video-latency") ||
51 		    !strcmp(arg_name, "audio-out-delay")) {
52 			printf("\t%s: %u (0x%02x, %d ms)\n", arg_name, val, val,
53 			       (val - 1) * 2);
54 		} else if (!strcmp(arg_name, "abort-msg")) {
55 			if (cec_opcode2s(val))
56 				printf("\t%s: %u (0x%02x, %s)\n",
57 				       arg_name, val, val, cec_opcode2s(val));
58 			else
59 				printf("\t%s: %u (0x%02x)\n", arg_name, val, val);
60 		} else {
61 			printf("\t%s: %u (0x%02x)\n", arg_name, val, val);
62 		}
63 		return;
64 	case CEC_ARG_TYPE_U16:
65 		if (strstr(arg_name, "phys-addr"))
66 			printf("\t%s: %x.%x.%x.%x\n", arg_name, cec_phys_addr_exp(val));
67 		else
68 			printf("\t%s: %u (0x%04x)\n", arg_name, val, val);
69 		return;
70 	case CEC_ARG_TYPE_U32:
71 		printf("\t%s: %u (0x%08x)\n", arg_name, val, val);
72 		return;
73 	default:
74 		break;
75 	}
76 	printf("\t%s: unknown type\n", arg_name);
77 }
78 
log_arg(const struct cec_arg * arg,const char * arg_name,const char * s)79 static void log_arg(const struct cec_arg *arg, const char *arg_name,
80 		    const char *s)
81 {
82 	switch (arg->type) {
83 	case CEC_ARG_TYPE_STRING:
84 		printf("\t%s: %s\n", arg_name, s);
85 		return;
86 	default:
87 		break;
88 	}
89 	printf("\t%s: unknown type\n", arg_name);
90 }
91 
92 static const struct cec_arg_enum_values type_rec_src_type[] = {
93 	{ "own", CEC_OP_RECORD_SRC_OWN },
94 	{ "digital", CEC_OP_RECORD_SRC_DIGITAL },
95 	{ "analog", CEC_OP_RECORD_SRC_ANALOG },
96 	{ "ext-plug", CEC_OP_RECORD_SRC_EXT_PLUG },
97 	{ "ext-phys-addr", CEC_OP_RECORD_SRC_EXT_PHYS_ADDR },
98 };
99 
100 static const struct cec_arg arg_rec_src_type = {
101 	CEC_ARG_TYPE_ENUM, 5, type_rec_src_type
102 };
103 
104 static void log_digital(const char *arg_name, const struct cec_op_digital_service_id *digital);
105 static void log_rec_src(const char *arg_name, const struct cec_op_record_src *rec_src);
106 static void log_tuner_dev_info(const char *arg_name, const struct cec_op_tuner_device_info *tuner_dev_info);
107 static void log_features(const struct cec_arg *arg, const char *arg_name, const uint8_t *p);
108 static void log_ui_command(const char *arg_name, const struct cec_op_ui_command *ui_cmd);
109 static void log_descriptors(const char *arg_name, unsigned num, const uint32_t *descriptors);
110 static void log_u8_array(const char *arg_name, unsigned num, const uint8_t *vals);
111 static void log_unknown_msg(const struct cec_msg *msg);
112 static void log_htng_unknown_msg(const struct cec_msg *msg);
113 
114 #include "cec-log-gen.h"
115 
cec_log_msg_args(unsigned int index)116 const struct cec_msg_args *cec_log_msg_args(unsigned int index)
117 {
118 	if (index >= sizeof(messages) / sizeof(messages[0]))
119 		return NULL;
120 	return &messages[index];
121 }
122 
log_digital(const char * arg_name,const struct cec_op_digital_service_id * digital)123 static void log_digital(const char *arg_name, const struct cec_op_digital_service_id *digital)
124 {
125 	log_arg(&arg_service_id_method, "service-id-method", digital->service_id_method);
126 	log_arg(&arg_dig_bcast_system, "dig-bcast-system", digital->dig_bcast_system);
127 	if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
128 		log_arg(&arg_channel_number_fmt, "channel-number-fmt", digital->channel.channel_number_fmt);
129 		log_arg(&arg_u16, "major", digital->channel.major);
130 		log_arg(&arg_u16, "minor", digital->channel.minor);
131 		return;
132 	}
133 
134 	switch (digital->dig_bcast_system) {
135 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
136 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
137 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
138 	case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
139 		log_arg(&arg_u16, "transport-id", digital->atsc.transport_id);
140 		log_arg(&arg_u16, "program-number", digital->atsc.program_number);
141 		break;
142 	default:
143 		log_arg(&arg_u16, "transport-id", digital->dvb.transport_id);
144 		log_arg(&arg_u16, "service-id", digital->dvb.service_id);
145 		log_arg(&arg_u16, "orig-network-id", digital->dvb.orig_network_id);
146 		break;
147 	}
148 }
149 
log_rec_src(const char * arg_name,const struct cec_op_record_src * rec_src)150 static void log_rec_src(const char *arg_name, const struct cec_op_record_src *rec_src)
151 {
152 	log_arg(&arg_rec_src_type, "rec-src-type", rec_src->type);
153 	switch (rec_src->type) {
154 	case CEC_OP_RECORD_SRC_OWN:
155 	default:
156 		break;
157 	case CEC_OP_RECORD_SRC_DIGITAL:
158 		log_digital(arg_name, &rec_src->digital);
159 		break;
160 	case CEC_OP_RECORD_SRC_ANALOG:
161 		log_arg(&arg_ana_bcast_type, "ana-bcast-type", rec_src->analog.ana_bcast_type);
162 		log_arg(&arg_u16, "ana-freq", rec_src->analog.ana_freq);
163 		log_arg(&arg_bcast_system, "bcast-system", rec_src->analog.bcast_system);
164 		break;
165 	case CEC_OP_RECORD_SRC_EXT_PLUG:
166 		log_arg(&arg_u8, "plug", rec_src->ext_plug.plug);
167 		break;
168 	case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
169 		log_arg(&arg_u16, "phys-addr", rec_src->ext_phys_addr.phys_addr);
170 		break;
171 	}
172 }
173 
log_tuner_dev_info(const char * arg_name,const struct cec_op_tuner_device_info * tuner_dev_info)174 static void log_tuner_dev_info(const char *arg_name, const struct cec_op_tuner_device_info *tuner_dev_info)
175 {
176 	log_arg(&arg_rec_flag, "rec-flag", tuner_dev_info->rec_flag);
177 	log_arg(&arg_tuner_display_info, "tuner-display-info", tuner_dev_info->tuner_display_info);
178 	if (tuner_dev_info->is_analog) {
179 		log_arg(&arg_ana_bcast_type, "ana-bcast-type", tuner_dev_info->analog.ana_bcast_type);
180 		log_arg(&arg_u16, "ana-freq", tuner_dev_info->analog.ana_freq);
181 		log_arg(&arg_bcast_system, "bcast-system", tuner_dev_info->analog.bcast_system);
182 	} else {
183 		log_digital(arg_name, &tuner_dev_info->digital);
184 	}
185 }
186 
log_features(const struct cec_arg * arg,const char * arg_name,const uint8_t * p)187 static void log_features(const struct cec_arg *arg,
188 			 const char *arg_name, const uint8_t *p)
189 {
190 	do {
191 		log_arg(arg, arg_name, static_cast<uint32_t>((*p) & ~CEC_OP_FEAT_EXT));
192 	} while ((*p++) & CEC_OP_FEAT_EXT);
193 }
194 
log_ui_command(const char * arg_name,const struct cec_op_ui_command * ui_cmd)195 static void log_ui_command(const char *arg_name,
196 			   const struct cec_op_ui_command *ui_cmd)
197 {
198 	log_arg(&arg_ui_cmd, arg_name, ui_cmd->ui_cmd);
199 	if (!ui_cmd->has_opt_arg)
200 		return;
201 	switch (ui_cmd->ui_cmd) {
202 	case CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE:
203 		log_arg(&arg_ui_bcast_type, "ui-broadcast-type",
204 			ui_cmd->ui_broadcast_type);
205 		break;
206 	case CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION:
207 		log_arg(&arg_ui_snd_pres_ctl, "ui-sound-presentation-control",
208 			ui_cmd->ui_sound_presentation_control);
209 		break;
210 	case CEC_OP_UI_CMD_PLAY_FUNCTION:
211 		log_arg(&arg_u8, "play-mode", ui_cmd->play_mode);
212 		break;
213 	case CEC_OP_UI_CMD_TUNE_FUNCTION:
214 		log_arg(&arg_channel_number_fmt, "channel-number-fmt",
215 			ui_cmd->channel_identifier.channel_number_fmt);
216 		log_arg(&arg_u16, "major", ui_cmd->channel_identifier.major);
217 		log_arg(&arg_u16, "minor", ui_cmd->channel_identifier.minor);
218 		break;
219 	case CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION:
220 		log_arg(&arg_u8, "ui-function-media", ui_cmd->ui_function_media);
221 		break;
222 	case CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION:
223 		log_arg(&arg_u8, "ui-function-select-av-input", ui_cmd->ui_function_select_av_input);
224 		break;
225 	case CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION:
226 		log_arg(&arg_u8, "ui-function-select-audio-input", ui_cmd->ui_function_select_audio_input);
227 		break;
228 	}
229 }
230 
log_descriptors(const char * arg_name,unsigned num,const uint32_t * descriptors)231 static void log_descriptors(const char *arg_name, unsigned num, const uint32_t *descriptors)
232 {
233 	for (unsigned i = 0; i < num; i++)
234 		log_arg(&arg_u32, arg_name, descriptors[i]);
235 }
236 
log_u8_array(const char * arg_name,unsigned num,const uint8_t * vals)237 static void log_u8_array(const char *arg_name, unsigned num, const uint8_t *vals)
238 {
239 	for (unsigned i = 0; i < num; i++)
240 		log_arg(&arg_u8, arg_name, vals[i]);
241 }
242 
log_htng_unknown_msg(const struct cec_msg * msg)243 static void log_htng_unknown_msg(const struct cec_msg *msg)
244 {
245 	uint32_t vendor_id;
246 	const uint8_t *bytes;
247 	uint8_t size;
248 	unsigned i;
249 
250 	cec_ops_vendor_command_with_id(msg, &vendor_id, &size, &bytes);
251 	printf("VENDOR_COMMAND_WITH_ID (0x%02x):\n",
252 	       CEC_MSG_VENDOR_COMMAND_WITH_ID);
253 	log_arg(&arg_vendor_id, "vendor-id", vendor_id);
254 	printf("\tvendor-specific-data:");
255 	for (i = 0; i < size; i++)
256 		printf(" 0x%02x", bytes[i]);
257 	printf("\n");
258 }
259 
log_unknown_msg(const struct cec_msg * msg)260 static void log_unknown_msg(const struct cec_msg *msg)
261 {
262 	uint32_t vendor_id;
263 	uint16_t phys_addr;
264 	const uint8_t *bytes;
265 	uint8_t size;
266 	unsigned i;
267 
268 	switch (msg->msg[1]) {
269 	case CEC_MSG_VENDOR_COMMAND:
270 		printf("VENDOR_COMMAND (0x%02x):\n",
271 		       CEC_MSG_VENDOR_COMMAND);
272 		cec_ops_vendor_command(msg, &size, &bytes);
273 		printf("\tvendor-specific-data:");
274 		for (i = 0; i < size; i++)
275 			printf(" 0x%02x", bytes[i]);
276 		printf("\n");
277 		break;
278 	case CEC_MSG_VENDOR_COMMAND_WITH_ID:
279 		cec_ops_vendor_command_with_id(msg, &vendor_id, &size, &bytes);
280 		switch (vendor_id) {
281 		case VENDOR_ID_HTNG:
282 			log_htng_msg(msg);
283 			break;
284 		default:
285 			printf("VENDOR_COMMAND_WITH_ID (0x%02x):\n",
286 			       CEC_MSG_VENDOR_COMMAND_WITH_ID);
287 			log_arg(&arg_vendor_id, "vendor-id", vendor_id);
288 			printf("\tvendor-specific-data:");
289 			for (i = 0; i < size; i++)
290 				printf(" 0x%02x", bytes[i]);
291 			printf("\n");
292 			break;
293 		}
294 		break;
295 	case CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN:
296 		printf("VENDOR_REMOTE_BUTTON_DOWN (0x%02x):\n",
297 		       CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN);
298 		cec_ops_vendor_remote_button_down(msg, &size, &bytes);
299 		printf("\tvendor-specific-rc-code:");
300 		for (i = 0; i < size; i++)
301 			printf(" 0x%02x", bytes[i]);
302 		printf("\n");
303 		break;
304 	case CEC_MSG_CDC_MESSAGE:
305 		phys_addr = (msg->msg[2] << 8) | msg->msg[3];
306 
307 		printf("CDC_MESSAGE (0x%02x): 0x%02x:\n",
308 		       CEC_MSG_CDC_MESSAGE, msg->msg[4]);
309 		log_arg(&arg_u16, "phys-addr", phys_addr);
310 		printf("\tpayload:");
311 		for (i = 5; i < msg->len; i++)
312 			printf(" 0x%02x", msg->msg[i]);
313 		printf("\n");
314 		break;
315 	default:
316 		printf("UNKNOWN (0x%02x)%s", msg->msg[1], msg->len > 2 ? ":\n\tpayload:" : "");
317 		for (i = 2; i < msg->len; i++)
318 			printf(" 0x%02x", msg->msg[i]);
319 		printf("\n");
320 		break;
321 	}
322 }
323 
cec_log_ui_cmd_string(uint8_t ui_cmd)324 const char *cec_log_ui_cmd_string(uint8_t ui_cmd)
325 {
326 	for (unsigned i = 0; i < arg_ui_cmd.num_enum_values; i++) {
327 		if (type_ui_cmd[i].value == ui_cmd)
328 			return type_ui_cmd[i].type_name;
329 	}
330 	return NULL;
331 }
332