| // SPDX-License-Identifier: (LGPL-2.1-only OR BSD-3-Clause) |
| /* |
| * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
| */ |
| |
| #include <string> |
| |
| #include <unistd.h> |
| |
| #include <linux/cec-funcs.h> |
| |
| #include "cec-htng-funcs.h" |
| #include "cec-info.h" |
| #include "cec-log.h" |
| #include "compiler.h" |
| |
| static const struct cec_arg arg_u8 = { |
| CEC_ARG_TYPE_U8, |
| }; |
| |
| static const struct cec_arg arg_u16 = { |
| CEC_ARG_TYPE_U16, |
| }; |
| |
| static const struct cec_arg arg_u32 = { |
| CEC_ARG_TYPE_U32, |
| }; |
| |
| static const struct cec_arg arg_string = { |
| CEC_ARG_TYPE_STRING, |
| }; |
| |
| static void log_arg(const struct cec_arg *arg, const char *arg_name, __u32 val) |
| { |
| unsigned i; |
| |
| switch (arg->type) { |
| case CEC_ARG_TYPE_ENUM: |
| for (i = 0; i < arg->num_enum_values; i++) { |
| if (arg->values[i].value == val) { |
| printf("\t%s: %s (0x%02x)\n", arg_name, |
| arg->values[i].type_name, val); |
| return; |
| } |
| } |
| fallthrough; |
| case CEC_ARG_TYPE_U8: |
| if (!strcmp(arg_name, "video-latency") || |
| !strcmp(arg_name, "audio-out-delay")) { |
| printf("\t%s: %u (0x%02x, %d ms)\n", arg_name, val, val, |
| (val - 1) * 2); |
| } else if (!strcmp(arg_name, "abort-msg")) { |
| if (cec_opcode2s(val)) |
| printf("\t%s: %u (0x%02x, %s)\n", |
| arg_name, val, val, cec_opcode2s(val)); |
| else |
| printf("\t%s: %u (0x%02x)\n", arg_name, val, val); |
| } else { |
| printf("\t%s: %u (0x%02x)\n", arg_name, val, val); |
| } |
| return; |
| case CEC_ARG_TYPE_U16: |
| if (strstr(arg_name, "phys-addr")) |
| printf("\t%s: %x.%x.%x.%x\n", arg_name, cec_phys_addr_exp(val)); |
| else |
| printf("\t%s: %u (0x%04x)\n", arg_name, val, val); |
| return; |
| case CEC_ARG_TYPE_U32: |
| printf("\t%s: %u (0x%08x)\n", arg_name, val, val); |
| return; |
| default: |
| break; |
| } |
| printf("\t%s: unknown type\n", arg_name); |
| } |
| |
| static void log_arg(const struct cec_arg *arg, const char *arg_name, |
| const char *s) |
| { |
| switch (arg->type) { |
| case CEC_ARG_TYPE_STRING: |
| printf("\t%s: %s\n", arg_name, s); |
| return; |
| default: |
| break; |
| } |
| printf("\t%s: unknown type\n", arg_name); |
| } |
| |
| static const struct cec_arg_enum_values type_rec_src_type[] = { |
| { "own", CEC_OP_RECORD_SRC_OWN }, |
| { "digital", CEC_OP_RECORD_SRC_DIGITAL }, |
| { "analog", CEC_OP_RECORD_SRC_ANALOG }, |
| { "ext-plug", CEC_OP_RECORD_SRC_EXT_PLUG }, |
| { "ext-phys-addr", CEC_OP_RECORD_SRC_EXT_PHYS_ADDR }, |
| }; |
| |
| static const struct cec_arg arg_rec_src_type = { |
| CEC_ARG_TYPE_ENUM, 5, type_rec_src_type |
| }; |
| |
| static void log_digital(const char *arg_name, const struct cec_op_digital_service_id *digital); |
| static void log_rec_src(const char *arg_name, const struct cec_op_record_src *rec_src); |
| static void log_tuner_dev_info(const char *arg_name, const struct cec_op_tuner_device_info *tuner_dev_info); |
| static void log_features(const struct cec_arg *arg, const char *arg_name, const __u8 *p); |
| static void log_ui_command(const char *arg_name, const struct cec_op_ui_command *ui_cmd); |
| static void log_vendor_id(const char *arg_name, __u32 vendor_id); |
| static void log_descriptors(const char *arg_name, unsigned num, const __u32 *descriptors); |
| static void log_u8_array(const char *arg_name, unsigned num, const __u8 *vals); |
| static void log_unknown_msg(const struct cec_msg *msg); |
| static void log_htng_unknown_msg(const struct cec_msg *msg); |
| |
| #include "cec-log-gen.h" |
| |
| const struct cec_msg_args *cec_log_msg_args(unsigned int index) |
| { |
| if (index >= sizeof(messages) / sizeof(messages[0])) |
| return nullptr; |
| return &messages[index]; |
| } |
| |
| static void log_digital(const char *arg_name, const struct cec_op_digital_service_id *digital) |
| { |
| log_arg(&arg_service_id_method, "service-id-method", digital->service_id_method); |
| log_arg(&arg_dig_bcast_system, "dig-bcast-system", digital->dig_bcast_system); |
| if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) { |
| log_arg(&arg_channel_number_fmt, "channel-number-fmt", digital->channel.channel_number_fmt); |
| log_arg(&arg_u16, "major", digital->channel.major); |
| log_arg(&arg_u16, "minor", digital->channel.minor); |
| return; |
| } |
| |
| switch (digital->dig_bcast_system) { |
| case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN: |
| case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE: |
| case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT: |
| case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T: |
| log_arg(&arg_u16, "transport-id", digital->atsc.transport_id); |
| log_arg(&arg_u16, "program-number", digital->atsc.program_number); |
| break; |
| default: |
| log_arg(&arg_u16, "transport-id", digital->dvb.transport_id); |
| log_arg(&arg_u16, "service-id", digital->dvb.service_id); |
| log_arg(&arg_u16, "orig-network-id", digital->dvb.orig_network_id); |
| break; |
| } |
| } |
| |
| static void log_rec_src(const char *arg_name, const struct cec_op_record_src *rec_src) |
| { |
| log_arg(&arg_rec_src_type, "rec-src-type", rec_src->type); |
| switch (rec_src->type) { |
| case CEC_OP_RECORD_SRC_OWN: |
| default: |
| break; |
| case CEC_OP_RECORD_SRC_DIGITAL: |
| log_digital(arg_name, &rec_src->digital); |
| break; |
| case CEC_OP_RECORD_SRC_ANALOG: |
| log_arg(&arg_ana_bcast_type, "ana-bcast-type", rec_src->analog.ana_bcast_type); |
| log_arg(&arg_u16, "ana-freq", rec_src->analog.ana_freq); |
| log_arg(&arg_bcast_system, "bcast-system", rec_src->analog.bcast_system); |
| break; |
| case CEC_OP_RECORD_SRC_EXT_PLUG: |
| log_arg(&arg_u8, "plug", rec_src->ext_plug.plug); |
| break; |
| case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR: |
| log_arg(&arg_u16, "phys-addr", rec_src->ext_phys_addr.phys_addr); |
| break; |
| } |
| } |
| |
| static void log_tuner_dev_info(const char *arg_name, const struct cec_op_tuner_device_info *tuner_dev_info) |
| { |
| log_arg(&arg_rec_flag, "rec-flag", tuner_dev_info->rec_flag); |
| log_arg(&arg_tuner_display_info, "tuner-display-info", tuner_dev_info->tuner_display_info); |
| if (tuner_dev_info->is_analog) { |
| log_arg(&arg_ana_bcast_type, "ana-bcast-type", tuner_dev_info->analog.ana_bcast_type); |
| log_arg(&arg_u16, "ana-freq", tuner_dev_info->analog.ana_freq); |
| log_arg(&arg_bcast_system, "bcast-system", tuner_dev_info->analog.bcast_system); |
| } else { |
| log_digital(arg_name, &tuner_dev_info->digital); |
| } |
| } |
| |
| static void log_features(const struct cec_arg *arg, |
| const char *arg_name, const __u8 *p) |
| { |
| do { |
| log_arg(arg, arg_name, static_cast<__u32>((*p) & ~CEC_OP_FEAT_EXT)); |
| } while ((*p++) & CEC_OP_FEAT_EXT); |
| } |
| |
| static void log_ui_command(const char *arg_name, |
| const struct cec_op_ui_command *ui_cmd) |
| { |
| log_arg(&arg_ui_cmd, arg_name, ui_cmd->ui_cmd); |
| if (!ui_cmd->has_opt_arg) |
| return; |
| switch (ui_cmd->ui_cmd) { |
| case CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE: |
| log_arg(&arg_ui_bcast_type, "ui-broadcast-type", |
| ui_cmd->ui_broadcast_type); |
| break; |
| case CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION: |
| log_arg(&arg_ui_snd_pres_ctl, "ui-sound-presentation-control", |
| ui_cmd->ui_sound_presentation_control); |
| break; |
| case CEC_OP_UI_CMD_PLAY_FUNCTION: |
| log_arg(&arg_u8, "play-mode", ui_cmd->play_mode); |
| break; |
| case CEC_OP_UI_CMD_TUNE_FUNCTION: |
| log_arg(&arg_channel_number_fmt, "channel-number-fmt", |
| ui_cmd->channel_identifier.channel_number_fmt); |
| log_arg(&arg_u16, "major", ui_cmd->channel_identifier.major); |
| log_arg(&arg_u16, "minor", ui_cmd->channel_identifier.minor); |
| break; |
| case CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION: |
| log_arg(&arg_u8, "ui-function-media", ui_cmd->ui_function_media); |
| break; |
| case CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION: |
| log_arg(&arg_u8, "ui-function-select-av-input", ui_cmd->ui_function_select_av_input); |
| break; |
| case CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION: |
| log_arg(&arg_u8, "ui-function-select-audio-input", ui_cmd->ui_function_select_audio_input); |
| break; |
| } |
| } |
| |
| static void log_vendor_id(const char *arg_name, __u32 vendor_id) |
| { |
| const char *vendor = cec_vendor2s(vendor_id); |
| |
| if (vendor) |
| printf("\t%s: 0x%06x (%s)\n", arg_name, vendor_id, vendor); |
| else |
| printf("\t%s: 0x%06x, %u\n", arg_name, vendor_id, vendor_id); |
| } |
| |
| static void log_descriptors(const char *arg_name, unsigned num, const __u32 *descriptors) |
| { |
| for (unsigned i = 0; i < num; i++) |
| log_arg(&arg_u32, arg_name, descriptors[i]); |
| } |
| |
| static void log_u8_array(const char *arg_name, unsigned num, const __u8 *vals) |
| { |
| for (unsigned i = 0; i < num; i++) |
| log_arg(&arg_u8, arg_name, vals[i]); |
| } |
| |
| static void log_htng_unknown_msg(const struct cec_msg *msg) |
| { |
| __u32 vendor_id; |
| const __u8 *bytes; |
| __u8 size; |
| unsigned i; |
| |
| cec_ops_vendor_command_with_id(msg, &vendor_id, &size, &bytes); |
| printf("VENDOR_COMMAND_WITH_ID (0x%02x):\n", |
| CEC_MSG_VENDOR_COMMAND_WITH_ID); |
| log_vendor_id("vendor-id", vendor_id); |
| printf("\tvendor-specific-data:"); |
| for (i = 0; i < size; i++) |
| printf(" 0x%02x", bytes[i]); |
| printf("\n"); |
| } |
| |
| static void log_unknown_msg(const struct cec_msg *msg) |
| { |
| __u32 vendor_id; |
| __u16 phys_addr; |
| const __u8 *bytes; |
| __u8 size; |
| unsigned i; |
| |
| switch (msg->msg[1]) { |
| case CEC_MSG_VENDOR_COMMAND: |
| printf("VENDOR_COMMAND (0x%02x):\n", |
| CEC_MSG_VENDOR_COMMAND); |
| cec_ops_vendor_command(msg, &size, &bytes); |
| printf("\tvendor-specific-data:"); |
| for (i = 0; i < size; i++) |
| printf(" 0x%02x", bytes[i]); |
| printf("\n"); |
| break; |
| case CEC_MSG_VENDOR_COMMAND_WITH_ID: |
| cec_ops_vendor_command_with_id(msg, &vendor_id, &size, &bytes); |
| switch (vendor_id) { |
| case VENDOR_ID_HTNG: |
| log_htng_msg(msg); |
| break; |
| default: |
| printf("VENDOR_COMMAND_WITH_ID (0x%02x):\n", |
| CEC_MSG_VENDOR_COMMAND_WITH_ID); |
| log_vendor_id("vendor-id", vendor_id); |
| printf("\tvendor-specific-data:"); |
| for (i = 0; i < size; i++) |
| printf(" 0x%02x", bytes[i]); |
| printf("\n"); |
| break; |
| } |
| break; |
| case CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN: |
| printf("VENDOR_REMOTE_BUTTON_DOWN (0x%02x):\n", |
| CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN); |
| cec_ops_vendor_remote_button_down(msg, &size, &bytes); |
| printf("\tvendor-specific-rc-code:"); |
| for (i = 0; i < size; i++) |
| printf(" 0x%02x", bytes[i]); |
| printf("\n"); |
| break; |
| case CEC_MSG_CDC_MESSAGE: |
| phys_addr = (msg->msg[2] << 8) | msg->msg[3]; |
| |
| printf("CDC_MESSAGE (0x%02x): 0x%02x:\n", |
| CEC_MSG_CDC_MESSAGE, msg->msg[4]); |
| log_arg(&arg_u16, "phys-addr", phys_addr); |
| printf("\tpayload:"); |
| for (i = 5; i < msg->len; i++) |
| printf(" 0x%02x", msg->msg[i]); |
| printf("\n"); |
| break; |
| default: |
| printf("UNKNOWN (0x%02x)%s", msg->msg[1], msg->len > 2 ? ":\n\tpayload:" : ""); |
| for (i = 2; i < msg->len; i++) |
| printf(" 0x%02x", msg->msg[i]); |
| printf("\n"); |
| break; |
| } |
| } |
| |
| const char *cec_log_ui_cmd_string(__u8 ui_cmd) |
| { |
| for (unsigned i = 0; i < arg_ui_cmd.num_enum_values; i++) { |
| if (type_ui_cmd[i].value == ui_cmd) |
| return type_ui_cmd[i].type_name; |
| } |
| return nullptr; |
| } |