blob: 726e74b309012dff187c6ff33b0d9d1daa0aa543 [file] [log] [blame]
#!/usr/bin/perl
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
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;
}
my $cur_msg;
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 =~ /^int reply/;
$func_args =~ s/^int 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 =~ /_/ && !exists($msgs{$cec_msg})) {
$cec_msg =~ s/_[^_]*$//;
}
return unless ($cec_msg =~ /_/);
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;
$cur_msg = $msg;
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 ($cec_msg =~ /_HTNG_/ && !$htng_case) {
$htng_case = 1;
$cdc_case = 0;
$std_logswitch = $logswitch;
$logswitch = "";
}
if ($cdc_case) {
$cdcmsgtable .= "\t{ $cec_msg, \"$msg_name\" },\n";
} elsif ($htng_case) {
$htngmsgtable .= "\t{ $cec_msg, \"$msg_name\" },\n";
} else {
$msgtable .= "\t{ $cec_msg, \"$msg_name\" },\n";
}
if (@args == 0) {
$logswitch .= "\tcase $cec_msg:\n";
$logswitch .= "\t\tprintf(\"$msg_name (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(\"$msg_name (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 "vendor_id") {
$logswitch .= "\t\tlog_vendor_id(\"$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";
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 (cec_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 =~ /audio_out_delay/ || $name =~ /video_latency/) {
$switch .= "\t\t\t\t$name = parse_latency(value);\n";
} elsif ($name =~ /phys_addr/) {
$switch .= "\t\t\t\t$name = cec_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 .= ", ";
}
$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_arg_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 cec_arg arg_$name = {\n";
$arg_structs .= "\tCEC_ARG_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";
push @{$feature_usage{$feature}}, $msg;
}
while (<>) {
last if /\/\* Messages \*\//;
}
$comment = 0;
$has_also = 0;
$operand_name = "";
$feature = "";
while (<>) {
chomp;
last if /_CEC_UAPI_FUNCS_H/;
if (/^\/\*.*Feature \*\/$/) {
($feature) = /^\/\* (.*) Feature/;
}
elsif (/^\/\*.*General Protocol Messages \*\/$/) {
$feature = "Abort";
}
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;
if (!exists($feature_usage{$feature})) {
push @{$feature_usage{$feature}}, "";
}
}
} 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/;
}
elsif (/^\/\*.*General Protocol Messages \*\/$/) {
$feature = "Abort";
}
if (/\/\* broadcast \*\//) {
$usage_msg{$cur_msg} =~ s/"\)\\n"$/", bcast)\\n"/;
}
s/\/\*.*\*\///;
if ($comment) {
next unless /\*\//;
$comment = 0;
s/^.*\*\///;
}
if (/\/\*/) {
$comment = 1;
next;
}
next if /^\s*$/;
next if /cec_msg_reply_feature_abort/;
next if /cec_msg_htng_init/;
if (/^static (__)?inline(__)? void cec_msg.*\(.*\)/) {
s/static\s(__)?inline(__)?\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\s(__)?inline(__)?\svoid\s//;
$func =~ s/struct cec_msg \*msg, //;
$func =~ s/struct cec_msg \*msg//;
process_func($feature, $func);
$func = "";
}
}
$options .= "\tOptHelpAll,\n";
open(my $fh, '>', 'cec-parse-src-gen.h') or die "Could not open cec-parse-src-gen.h for writing";
print $fh "\n\n";
foreach (sort keys %feature_usage) {
$name = $_;
s/ /_/g;
s/([A-Z])/\l\1/g;
$usage_var = $_ . "_usage";
printf $fh "static const char *$usage_var =\n";
$usage = "";
foreach (@{$feature_usage{$name}}) {
$usage .= $usage_msg{$_};
}
foreach (@{$feature_also{$name}}) {
$usage .= $usage_msg{$_};
}
chop $usage;
$usage =~ s/" --vendor-remote-button-up/VENDOR_EXTRA\n\t" --vendor-remote-button-up/;
printf $fh "%s;\n\n", $usage;
s/_/-/g;
$opt = "OptHelp" . $name;
$opt =~ s/ //g;
$help .= "\tif (options[OptHelpAll] || options\[$opt\]) {\n";
$help .= "\t\tprintf(\"$name Feature:\\n\\n\");\n";
$help .= "\t\tprintf(\"\%s\\n\", $usage_var);\n\t}\n";
}
printf $fh "void cec_parse_usage_options(const char *options)\n{\n";
printf $fh "%s}\n\n", $help;
printf $fh "void cec_parse_msg_args(struct cec_msg &msg, int reply, const cec_msg_args *opt, int ch)\n{\n";
printf $fh "\tchar *value, *subs = optarg;\n\n";
printf $fh "\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 $fh "%s", $switch;
printf $fh "\t}\n};\n\n";
close $fh;
open(my $fh, '>', 'cec-parse-gen.h') or die "Could not open cec-parse-gen.h for writing";
foreach (sort keys %feature_usage) {
$name = $_;
s/ /-/g;
s/([A-Z])/\l\1/g;
$help_features .= sprintf("\t\" --help-%-28s Show help for the $name feature\\n\" \\\n", $_);
$opt = "OptHelp" . $name;
$opt =~ s/ //g;
$options .= "\t$opt,\n";
$long_opts .= "\t{ \"help-$_\", no_argument, 0, $opt }, \\\n";
}
print $fh "enum cec_parse_options {\n\tOptMessages = 255,\n";
printf $fh "%s\n\tOptLast = 512\n};\n\n", $options;
printf $fh "#define CEC_PARSE_LONG_OPTS \\\n%s\n\n", $long_opts;
printf $fh "#define CEC_PARSE_USAGE \\\n%s\n\n", $help_features;
close $fh;
open(my $fh, '>', 'cec-log-gen.h') or die "Could not open cec-log-gen.h for writing";
printf $fh "%s%s\n", $enums, $arg_structs;
printf $fh "static const struct cec_msg_args messages[] = {\n\t{\n";
printf $fh "%s\t}\n};\n\n", $messages;
print $fh <<'EOF';
void cec_log_msg(const struct cec_msg *msg)
{
if (msg->len == 1) {
printf("POLL\n");
goto status;
}
switch (msg->msg[1]) {
EOF
printf $fh "%s", $std_logswitch;
print $fh <<'EOF';
default:
log_unknown_msg(msg);
break;
}
break;
default:
log_unknown_msg(msg);
break;
}
status:
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", cec_status2s(*msg).c_str());
}
static void log_htng_msg(const struct cec_msg *msg)
{
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", cec_status2s(*msg).c_str());
if (msg->len < 6)
return;
switch (msg->msg[5]) {
EOF
printf $fh "%s", $logswitch;
print $fh <<'EOF';
default:
log_htng_unknown_msg(msg);
break;
}
}
EOF
close $fh;
open(my $fh, '>', 'cec-msgs-gen.h') or die "Could not open cec-msgs-gen.h for writing";
printf $fh "struct msgtable {\n";
printf $fh "\t__u8 opcode;\n";
printf $fh "\tconst char *name;\n";
printf $fh "};\n\n";
printf $fh "static const struct msgtable msgtable[] = {\n";
printf $fh "%s", $msgtable;
printf $fh "\t{ CEC_MSG_VENDOR_COMMAND, \"VENDOR_COMMAND\" },\n";
printf $fh "\t{ CEC_MSG_VENDOR_COMMAND_WITH_ID, \"VENDOR_COMMAND_WITH_ID\" },\n";
printf $fh "\t{ CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN, \"VENDOR_REMOTE_BUTTON_DOWN\" },\n";
printf $fh "\t{ CEC_MSG_CDC_MESSAGE, \"CDC_MESSAGE\" },\n";
printf $fh "};\n\n";
printf $fh "static const struct msgtable cdcmsgtable[] = {\n";
printf $fh "%s", $cdcmsgtable;
printf $fh "};\n\n";
printf $fh "static const struct msgtable htngmsgtable[] = {\n";
printf $fh "%s", $htngmsgtable;
printf $fh "};\n";
close $fh;