aboutsummaryrefslogtreecommitdiffstats
path: root/utils/cec-ctl/msg2ctl.pl
diff options
context:
space:
mode:
Diffstat (limited to 'utils/cec-ctl/msg2ctl.pl')
-rwxr-xr-xutils/cec-ctl/msg2ctl.pl464
1 files changed, 464 insertions, 0 deletions
diff --git a/utils/cec-ctl/msg2ctl.pl b/utils/cec-ctl/msg2ctl.pl
new file mode 100755
index 00000000..8bbd369f
--- /dev/null
+++ b/utils/cec-ctl/msg2ctl.pl
@@ -0,0 +1,464 @@
+#!/usr/bin/perl
+
+sub maxprefix {
+ my $p = shift(@_);
+ for (@_) {
+ chop $p until /^\Q$p/;
+ }
+ $p =~ s/_[^_]*$/_/;
+ $p = "CEC_OP_CEC_" if ($p =~ /CEC_OP_CEC_VERSION_/);
+ return $p;
+}
+
+sub process_func
+{
+ my $feature = shift;
+ my $func = shift;
+ my $func_args = $func;
+ $func =~ s/\(.*//;
+ my $msg = $func;
+ $msg =~ s/([a-z])/\U\1/g;
+ $func =~ s/cec_msg//;
+ my $opt = $func;
+ $opt =~ s/_([a-z])/\U\1/g;
+ $func_args =~ s/.*\((.*)\).*/\1/;
+ my $has_reply = $func_args =~ /^bool reply/;
+ $func_args =~ s/^bool reply,? ?//;
+ my $arg_names;
+ my $arg_ptrs;
+ my $name, $type, $size;
+ my $msg_dash_name, $msg_lc_name;
+ my @enum, $val;
+ my $usage;
+ my $has_digital = $func_args =~ /cec_op_digital_service_id/;
+ my $has_ui_command = $func_args =~ /cec_op_ui_command/;
+ my $has_short_aud_descr = $func_args =~ /num_descriptors/;
+
+ my @ops_args = split(/, */, $func_args);
+ if ($has_digital) {
+ $func_args =~ s/const struct cec_op_digital_service_id \*digital/__u8 service_id_method, __u8 dig_bcast_system, __u16 transport_id, __u16 service_id, __u16 orig_network_id, __u16 program_number, __u8 channel_number_fmt, __u16 major, __u16 minor/;
+ }
+ if ($has_ui_command) {
+ $func_args =~ s/const struct cec_op_ui_command \*ui_cmd/__u8 ui_cmd, __u8 has_opt_arg, __u8 play_mode, __u8 ui_function_media, __u8 ui_function_select_av_input, __u8 ui_function_select_audio_input, __u8 ui_bcast_type, __u8 ui_snd_pres_ctl, __u8 channel_number_fmt, __u16 major, __u16 minor/;
+ }
+ if ($has_short_aud_descr) {
+ $func_args =~ s/const __u32 \*descriptors/__u8 descriptor1, __u8 descriptor2, __u8 descriptor3, __u8 descriptor4/;
+ $func_args =~ s/const __u8 \*audio_format_id, const __u8 \*audio_format_code/__u8 audio_format_id1, __u8 audio_format_code1, __u8 audio_format_id2, __u8 audio_format_code2, __u8 audio_format_id3, __u8 audio_format_code3, __u8 audio_format_id4, __u8 audio_format_code4/;
+ }
+ my @args = split(/, */, $func_args);
+ my $has_struct = $func_args =~ /struct/;
+ return if ($func_args =~ /__u\d+\s*\*/);
+
+ my $cec_msg = $msg;
+ while ($cec_msg ne "" && !exists($msgs{$cec_msg})) {
+ $cec_msg =~ s/_[^_]*$//;
+ }
+ return if ($cec_msg eq "");
+
+ my $msg_name = $cec_msg;
+ $msg_name =~ s/CEC_MSG_//;
+ $msg_dash_name = $msg;
+ $msg_dash_name =~ s/CEC_MSG_//;
+ $msg_dash_name =~ s/([A-Z])/\l\1/g;
+ $msg_dash_name =~ s/_/-/g;
+ $msg_lc_name = $msg;
+ $msg_lc_name =~ s/([A-Z])/\l\1/g;
+
+ if ($cec_msg eq $msg) {
+ if ($cec_msg =~ /_CDC_/ && !$cdc_case) {
+ $cdc_case = 1;
+ $logswitch .= "\tcase CEC_MSG_CDC_MESSAGE:\n";
+ $logswitch .= "\tswitch (msg->msg[4]) {\n";
+ }
+ if (@args == 0) {
+ $logswitch .= "\tcase $cec_msg:\n";
+ $logswitch .= "\t\tprintf(\"$cec_msg (0x%02x):\\n\", $cec_msg);\n";
+ $logswitch .= "\t\tbreak;\n\n";
+ } else {
+ $logswitch .= "\tcase $cec_msg: {\n";
+ foreach (@ops_args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ if ($type =~ /struct .*\*/) {
+ $type =~ s/ \*//;
+ $type =~ s/const //;
+ }
+ if ($name eq "rc_profile" || $name eq "dev_features") {
+ $logswitch .= "\t\tconst __u8 *$name = NULL;\n";
+ } elsif ($type eq "const char *") {
+ $logswitch .= "\t\tchar $name\[16\];\n";
+ } elsif ($type eq "const __u32 *") {
+ $logswitch .= "\t\t__u32 $name\[4\];\n";
+ } elsif ($type eq "const __u8 *") {
+ $logswitch .= "\t\t__u8 $name\[4\];\n";
+ } elsif ($type =~ /struct/) {
+ $logswitch .= "\t\t$type $name = {};\n";
+ } else {
+ $logswitch .= "\t\t$type $name;\n";
+ }
+ }
+ if ($cdc_case) {
+ $logswitch .= "\t\t__u16 phys_addr;\n";
+ }
+ my $ops_lc_name = $msg_lc_name;
+ $ops_lc_name =~ s/^cec_msg/cec_ops/;
+ $logswitch .= "\n\t\t$ops_lc_name(msg";
+ if ($cdc_case) {
+ $logswitch .= ", &phys_addr";
+ }
+ foreach (@ops_args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ if ($type eq "const char *" ||
+ $type eq "const __u8 *" ||
+ $type eq "const __u32 *") {
+ $logswitch .= ", $name";
+ } else {
+ $logswitch .= ", &$name";
+ }
+ }
+ $logswitch .= ");\n";
+ $logswitch .= "\t\tprintf(\"$cec_msg (0x%02x):\\n\", $cec_msg);\n";
+ if ($cdc_case) {
+ $logswitch .= "\t\tlog_arg(&arg_phys_addr, \"phys-addr\", phys_addr);\n";
+ }
+ foreach (@ops_args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ my $dash_name = $name;
+ $dash_name =~ s/_/-/g;
+ if ($name eq "rc_profile" || $name eq "dev_features") {
+ $logswitch .= "\t\tlog_features(&arg_$name, \"$dash_name\", $name);\n";
+ } elsif ($name eq "digital") {
+ $logswitch .= "\t\tlog_digital(\"$dash_name\", &$name);\n";
+ } elsif ($name eq "ui_cmd") {
+ $logswitch .= "\t\tlog_ui_command(\"$dash_name\", &$name);\n";
+ } elsif ($name eq "rec_src") {
+ $logswitch .= "\t\tlog_rec_src(\"$dash_name\", &$name);\n";
+ } elsif ($name eq "tuner_dev_info") {
+ $logswitch .= "\t\tlog_tuner_dev_info(\"$dash_name\", &$name);\n";
+ } elsif ($name eq "descriptors") {
+ $logswitch .= "\t\tlog_descriptors(\"$dash_name\", num_descriptors, $name);\n";
+ } elsif ($name eq "audio_format_id" || $name eq "audio_format_code") {
+ $logswitch .= "\t\tlog_u8_array(\"$dash_name\", num_descriptors, $name);\n";
+ } else {
+ $logswitch .= "\t\tlog_arg(&arg_$name, \"$dash_name\", $name);\n";
+ }
+ }
+ $logswitch .= "\t\tbreak;\n\t}\n";
+ }
+ }
+ return if $has_struct;
+
+ $options .= "\tOpt$opt,\n";
+ $messages .= "\t\t$cec_msg,\n";
+ $messages .= "\t\tOpt$opt,\n" unless $is_log;
+ if (@args == 0) {
+ $messages .= "\t\t0, { }, { },\n";
+ $long_opts .= "\t{ \"$msg_dash_name\", no_argument, 0, Opt$opt }, \\\n";
+ $usage .= "\t\" --" . sprintf("%-30s", $msg_dash_name) . "Send $msg_name message (\" xstr($cec_msg) \")\\n\"\n";
+ $usage_msg{$msg} = $usage;
+ $switch .= "\tcase Opt$opt: {\n";
+ $switch .= "\t\t$msg_lc_name(&msg";
+ $switch .= ", reply" if $has_reply;
+ $switch .= ");\n\t\tbreak;\n\t}\n\n";
+ } else {
+ $long_opts .= "\t{ \"$msg_dash_name\", required_argument, 0, Opt$opt }, \\\n";
+ $usage .= "\t\" --$msg_dash_name";
+ my $prefix = "\t\" " . sprintf("%-30s", " ");
+ my $sep = "=";
+ foreach (@args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ $name =~ s/_/-/g;
+ $usage .= "$sep$name=<val>";
+ $sep = ",";
+ }
+ $usage .= "\\n\"\n";
+ foreach (@args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ @enum = @{$types{$name}};
+ next if !scalar(@enum);
+ $name =~ s/_/-/g;
+ $usage .= $prefix . "'$name' can have these values:\\n\"\n";
+ my $common_prefix = maxprefix(@enum);
+ foreach (@enum) {
+ my $e = $_;
+ s/^$common_prefix//;
+ s/([A-Z])/\l\1/g;
+ s/_/-/g;
+ $usage .= $prefix . " $_ (\" xstr($e) \")\\n\"\n";
+ }
+ }
+ $usage .= $prefix . "Send $msg_name message (\" xstr($cec_msg) \")\\n\"\n";
+ $usage_msg{$msg} = $usage;
+ $switch .= "\tcase Opt$opt: {\n";
+ foreach (@args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ if ($type =~ /char/) {
+ $switch .= "\t\tconst char *$name = \"\";\n";
+ } else {
+ $switch .= "\t\t$type $name = 0;\n";
+ }
+ }
+ $switch .= "\n\t\twhile (*subs != '\\0') {\n";
+ $switch .= "\t\t\tswitch (parse_subopt(&subs, opt->arg_names, &value)) {\n";
+ my $cnt = 0;
+ foreach (@args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ @enum = @{$types{$name}};
+ $switch .= "\t\t\tcase $cnt:\n";
+ if ($type =~ /char/) {
+ $switch .= "\t\t\t\t$name = value;\n";
+ } elsif (scalar(@enum) || $name eq "ui_cmd") {
+ $switch .= "\t\t\t\t$name = parse_enum(value, opt->args\[$cnt\]);\n";
+ } elsif ($name =~ /phys_addr/) {
+ $switch .= "\t\t\t\t$name = parse_phys_addr(value);\n";
+ } else {
+ $switch .= "\t\t\t\t$name = strtol(value, 0L, 0);\n";
+ }
+ $switch .= "\t\t\t\tbreak;\n";
+ $cnt++;
+ }
+ $switch .= "\t\t\tdefault:\n";
+ $switch .= "\t\t\t\texit(1);\n";
+ $switch .= "\t\t\t}\n\t\t}\n";
+ $switch .= "\t\t$msg_lc_name(&msg";
+ $switch .= ", reply" if $has_reply;
+ foreach (@args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ $switch .= ", $name";
+ }
+ $switch .= ");\n\t\tbreak;\n\t}\n\n";
+
+ foreach (@args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ if ($arg_names ne "") {
+ $arg_names .= ", ";
+ $arg_ptrs .= ", ";
+ }
+ if ($name eq "ui_cmd") {
+ $arg_ptrs .= "&arg_rc_$name";
+ } else {
+ $arg_ptrs .= "&arg_$name";
+ }
+ $name =~ s/_/-/g;
+ $arg_names .= '"' . $name . '"';
+ }
+ $size = $#args + 1;
+ $messages .= "\t\t$size, { $arg_names },\n";
+ $messages .= "\t\t{ $arg_ptrs },\n";
+ foreach (@args) {
+ ($type, $name) = /(.*?) ?([a-zA-Z_]\w+)$/;
+ @enum = @{$types{$name}};
+ $size = scalar(@enum);
+
+ if ($size && !defined($created_enum{$name})) {
+ $created_enum{$name} = 1;
+ $enums .= "static const struct cec_enum_values type_$name\[\] = {\n";
+ my $common_prefix = maxprefix(@enum);
+ foreach (@enum) {
+ $val = $_;
+ s/^$common_prefix//;
+ s/([A-Z])/\l\1/g;
+ s/_/-/g;
+ $enums .= "\t{ \"$_\", $val },\n";
+ }
+ $enums .= "};\n\n";
+ }
+ if (!defined($created_arg{$name})) {
+ $created_arg{$name} = 1;
+ if ($type eq "__u8" && $size) {
+ $arg_structs .= "static const struct arg arg_$name = {\n";
+ $arg_structs .= "\tCEC_TYPE_ENUM, $size, type_$name\n};\n\n";
+ } elsif ($type eq "__u8") {
+ $arg_structs .= "#define arg_$name arg_u8\n";
+ } elsif ($type eq "__u16") {
+ $arg_structs .= "#define arg_$name arg_u16\n";
+ } elsif ($type eq "__u32") {
+ $arg_structs .= "#define arg_$name arg_u32\n";
+ } elsif ($type eq "const char *") {
+ $arg_structs .= "#define arg_$name arg_string\n";
+ }
+ }
+ }
+ }
+ $messages .= "\t\t\"$msg_name\"\n";
+ $messages .= "\t}, {\n";
+ $feature_usage{$feature} .= $usage;
+}
+
+$is_log = shift;
+
+while (<>) {
+ last if /\/\* Messages \*\//;
+}
+
+$comment = 0;
+$has_also = 0;
+$operand_name = "";
+$feature = "";
+
+while (<>) {
+ chomp;
+ last if /_CEC_UAPI_FUNCS_H/;
+ if (/^\/\*.*Feature \*\/$/) {
+ ($feature) = /^\/\* (.*) Feature/;
+ $feature_usage{$feature} = "";
+ }
+ if ($operand_name ne "" && !/^#define/) {
+ @{$types{$operand_name}} = @ops;
+ undef @ops;
+ $operand_name = "";
+ }
+ if (/\/\*.*Operand \((.*)\)/) {
+ $operand_name = $1;
+ next;
+ }
+ s/\/\*.*\*\///;
+ if ($comment) {
+ if ($has_also) {
+ if (/CEC_MSG/) {
+ ($also_msg) = /(CEC_MSG\S+)/;
+ push @{$feature_also{$feature}}, $also_msg;
+ }
+ } elsif (/^ \* Has also:$/) {
+ $has_also = 1;
+ }
+ $has_also = 0 if (/\*\//);
+ next unless /\*\//;
+ $comment = 0;
+ s/^.*\*\///;
+ }
+ if (/\/\*/) {
+ $comment = 1;
+ $has_also = 0;
+ next;
+ }
+ next if /^\s*$/;
+ if (/^\#define/) {
+ ($name, $val) = /define (\S+)\s+(\S+)/;
+ if ($name =~ /^CEC_MSG/) {
+ $msgs{$name} = 1;
+ } elsif ($operand_name ne "" && $name =~ /^CEC_OP/) {
+ push @ops, $name;
+ }
+ next;
+ }
+}
+
+while (<>) {
+ chomp;
+ if (/^\/\*.*Feature \*\/$/) {
+ ($feature) = /^\/\* (.*) Feature/;
+ }
+ s/\/\*.*\*\///;
+ if ($comment) {
+ next unless /\*\//;
+ $comment = 0;
+ s/^.*\*\///;
+ }
+ if (/\/\*/) {
+ $comment = 1;
+ next;
+ }
+ next if /^\s*$/;
+ next if /cec_msg_reply_feature_abort/;
+ if (/^static inline void cec_msg.*\(.*\)/) {
+ s/static\sinline\svoid\s//;
+ s/struct cec_msg \*msg, //;
+ s/struct cec_msg \*msg//;
+ process_func($feature, $_);
+ next;
+ }
+ if (/^static inline void cec_msg/) {
+ $func = $_;
+ next;
+ }
+ if ($func ne "") {
+ $func .= $_;
+ next unless /\)$/;
+ $func =~ s/\s+/ /g;
+ $func =~ s/static\sinline\svoid\s//;
+ $func =~ s/struct cec_msg \*msg, //;
+ $func =~ s/struct cec_msg \*msg//;
+ process_func($feature, $func);
+ $func = "";
+ }
+}
+
+$options .= "\tOptHelpAll,\n";
+
+unless ($is_log) {
+ foreach (sort keys %feature_usage) {
+ $name = $_;
+ s/ /_/g;
+ s/([A-Z])/\l\1/g;
+ $usage_var = $_ . "_usage";
+ printf "static const char *$usage_var =\n";
+ $usage = $feature_usage{$name};
+ foreach (@{$feature_also{$name}}) {
+ $usage .= $usage_msg{$_};
+ }
+ chop $usage;
+ printf "%s;\n\n", $usage;
+ s/_/-/g;
+ $help_features .= sprintf("\t\" --help-%-28s Show messages for the $name feature\\n\" \\\n", $_);
+ $opt = "OptHelp" . $name;
+ $opt =~ s/ //g;
+ $help .= "\tif (options[OptHelpAll] || options\[$opt\]) {\n";
+ $help .= "\t\tprintf(\"$name Feature:\\n\");\n";
+ $help .= "\t\tprintf(\"\%s\\n\", $usage_var);\n\t}\n";
+ $options .= "\t$opt,\n";
+ $long_opts .= "\t{ \"help-$_\", no_argument, 0, $opt }, \\\n";
+ }
+
+ print "enum {\n\tOptMessages = 255,\n";
+ printf "%s\n\tOptLast = 512\n};\n\n", $options;
+
+ printf "#define CEC_LONG_OPTS \\\n%s\n\n", $long_opts;
+ printf "#define CEC_USAGE \\\n%s\n\n", $help_features;
+}
+
+printf "%s%s\n", $enums, $arg_structs;
+printf "static const struct message messages[] = {\n\t{\n";
+printf "%s\t}\n};\n\n", $messages;
+
+unless ($is_log) {
+ printf "static void usage_options(int ch)\n{\n";
+ printf "%s}\n\n", $help;
+ printf "static void parse_msg_args(struct cec_msg &msg, bool reply, const message *opt, int ch)\n{\n";
+ printf "\tchar *value, *subs = optarg;\n\n";
+ printf "\tswitch (ch) {\n";
+ $switch =~ s/(service_id_method, dig_bcast_system, transport_id, service_id, orig_network_id, program_number, channel_number_fmt, major, minor)/args2digital_service_id(\1)/g;
+ $switch =~ s/(ui_cmd, has_opt_arg, play_mode, ui_function_media, ui_function_select_av_input, ui_function_select_audio_input, ui_bcast_type, ui_snd_pres_ctl, channel_number_fmt, major, minor)/args2ui_command(\1)/g;
+ $switch =~ s/(descriptor1, descriptor2, descriptor3, descriptor4)/args2short_descrs(\1)/g;
+ $switch =~ s/(audio_format_id1, audio_format_code1, audio_format_id2, audio_format_code2, audio_format_id3, audio_format_code3, audio_format_id4, audio_format_code4)/args2short_aud_fmt_ids(audio_format_id1, audio_format_id2, audio_format_id3, audio_format_id4), args2short_aud_fmt_codes(audio_format_code1, audio_format_code2, audio_format_code3, audio_format_code4)/g;
+ printf "%s", $switch;
+ printf "\t}\n};\n\n";
+}
+
+print <<'EOF';
+void log_msg(const struct cec_msg *msg)
+{
+ if (msg->len == 1)
+ printf("CEC_MSG_POLL:\n");
+ if ((msg->tx_status && !(msg->tx_status & CEC_TX_STATUS_OK)) ||
+ (msg->rx_status && !(msg->rx_status & (CEC_RX_STATUS_OK | CEC_RX_STATUS_FEATURE_ABORT))))
+ printf("\t%s\n", status2s(*msg).c_str());
+
+ if (msg->len == 1)
+ return;
+
+ switch (msg->msg[1]) {
+EOF
+printf "%s", $logswitch;
+print <<'EOF';
+ default:
+ log_unknown_msg(msg);
+ break;
+ }
+ break;
+
+ default:
+ log_unknown_msg(msg);
+ break;
+ }
+}
+EOF

Privacy Policy