aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil-cisco@xs4all.nl>2019-10-02 17:15:57 +0200
committerHans Verkuil <hverkuil-cisco@xs4all.nl>2019-10-03 09:47:03 +0200
commit31a5041aa77b44f90fcdd4bde890516fa2fe03ad (patch)
treec5759eb8e7a125feef598b113aa954615f8699b7
parent5079294a7fc95a711e9daa2186d3b07db6e7c239 (diff)
utils/libcecutil: add CEC utility library
Add a new library containing CEC info, logging and parsing utility functions. Most of this code is generated from the cec headers. This commit adds the library and compiles it. It is not yet used anywhere, though. Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
-rw-r--r--configure.ac1
-rw-r--r--utils/Makefile.am1
-rw-r--r--utils/libcecutil/.gitignore4
-rw-r--r--utils/libcecutil/Makefile.am22
-rwxr-xr-xutils/libcecutil/cec-gen.pl547
-rw-r--r--utils/libcecutil/cec-info.cpp523
-rw-r--r--utils/libcecutil/cec-info.h35
-rw-r--r--utils/libcecutil/cec-log.cpp319
-rw-r--r--utils/libcecutil/cec-log.h43
-rw-r--r--utils/libcecutil/cec-parse.cpp230
-rw-r--r--utils/libcecutil/cec-parse.h16
11 files changed, 1741 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 583f2260..c84067ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -33,6 +33,7 @@ AC_CONFIG_FILES([Makefile
utils/v4l2-dbg/Makefile
utils/v4l2-sysfs-path/Makefile
utils/qv4l2/Makefile
+ utils/libcecutil/Makefile
utils/cec-ctl/Makefile
utils/cec-ctl/cec-ctl.1
utils/cec-compliance/Makefile
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 9c29926a..0e68a612 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -9,6 +9,7 @@ SUBDIRS = \
v4l2-ctl \
v4l2-dbg \
v4l2-sysfs-path \
+ libcecutil \
cec-ctl \
cec-compliance \
cec-follower \
diff --git a/utils/libcecutil/.gitignore b/utils/libcecutil/.gitignore
new file mode 100644
index 00000000..49a48b9a
--- /dev/null
+++ b/utils/libcecutil/.gitignore
@@ -0,0 +1,4 @@
+cec-log-gen.h
+cec-parse-gen.h
+cec-parse-src-gen.h
+cec-msgs-gen.h
diff --git a/utils/libcecutil/Makefile.am b/utils/libcecutil/Makefile.am
new file mode 100644
index 00000000..68ffbc49
--- /dev/null
+++ b/utils/libcecutil/Makefile.am
@@ -0,0 +1,22 @@
+noinst_LTLIBRARIES = libcecutil.la
+
+libcecutil_la_SOURCES = cec-info.cpp cec-log.cpp cec-parse.cpp cec-info.h cec-log.h cec-parse.h
+libcecutil_la_CPPFLAGS = -static -I$(top_srcdir)/utils/common
+libcecutil_la_LDFLAGS = -static
+
+CEC_GEN_PREREQS = $(top_srcdir)/include/linux/cec.h $(top_srcdir)/utils/common/cec-htng.h \
+ $(top_srcdir)/include/linux/cec-funcs.h $(top_srcdir)/utils/common/cec-htng-funcs.h
+
+cec-info.cpp: cec-msgs-gen.h
+
+cec-log.cpp: cec-log-gen.h
+
+cec-parse.cpp: cec-parse-gen.h cec-parse-src-gen.h
+
+cec-msgs-gen.h cec-log-gen.h cec-parse-src-gen.h cec-parse-gen.h: cec-gen.pl $(CEC_GEN_PREREQS)
+ cec-gen.pl $@ $(CEC_GEN_PREREQS) >$@
+
+clean-local:
+ -rm -vf cec-log-gen.h cec-parse-gen.h cec-parse-src-gen.h cec-table-gen.h
+
+EXTRA_DIST = TODO
diff --git a/utils/libcecutil/cec-gen.pl b/utils/libcecutil/cec-gen.pl
new file mode 100755
index 00000000..89286e42
--- /dev/null
+++ b/utils/libcecutil/cec-gen.pl
@@ -0,0 +1,547 @@
+#!/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;
+}
+
+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(\"$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";
+ 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;
+}
+
+$to_header = 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/;
+ }
+ 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";
+
+if ($to_header eq "cec-parse-src-gen.h") {
+ print "\n\n";
+ 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 = "";
+ 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 "%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 "void cec_parse_usage_options(const char *options)\n{\n";
+ printf "%s}\n\n", $help;
+ printf "void cec_parse_msg_args(struct cec_msg &msg, int reply, const cec_msg_args *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";
+ exit 0;
+}
+
+if ($to_header eq "cec-parse-gen.h") {
+ 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 "enum cec_parse_options {\n\tOptMessages = 255,\n";
+ printf "%s\n\tOptLast = 512\n};\n\n", $options;
+
+ printf "#define CEC_PARSE_LONG_OPTS \\\n%s\n\n", $long_opts;
+ printf "#define CEC_PARSE_USAGE \\\n%s\n\n", $help_features;
+ exit 0;
+}
+
+if ($to_header eq "cec-log-gen.h") {
+ printf "%s%s\n", $enums, $arg_structs;
+ printf "static const struct cec_msg_args messages[] = {\n\t{\n";
+ printf "%s\t}\n};\n\n", $messages;
+
+ print <<'EOF';
+void cec_log_msg(const struct cec_msg *msg)
+{
+ if (msg->len == 1) {
+ printf("CEC_MSG_POLL\n");
+ goto status;
+ }
+
+ switch (msg->msg[1]) {
+EOF
+ printf "%s", $std_logswitch;
+ print <<'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());
+}
+
+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 "%s", $logswitch;
+ print <<'EOF';
+ default:
+ log_htng_unknown_msg(msg);
+ break;
+ }
+}
+EOF
+ exit 0;
+}
+
+if ($to_header eq "cec-msgs-gen.h") {
+ printf "struct msgtable {\n";
+ printf "\t__u8 opcode;\n";
+ printf "\tconst char *name;\n";
+ printf "};\n\n";
+ printf "static const struct msgtable msgtable[] = {\n";
+ printf "%s", $msgtable;
+ printf "\t{ CEC_MSG_VENDOR_COMMAND, \"VENDOR_COMMAND\" },\n";
+ printf "\t{ CEC_MSG_VENDOR_COMMAND_WITH_ID, \"VENDOR_COMMAND_WITH_ID\" },\n";
+ printf "\t{ CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN, \"VENDOR_REMOTE_BUTTON_DOWN\" },\n";
+ printf "\t{ CEC_MSG_CDC_MESSAGE, \"CDC_MESSAGE\" },\n";
+ printf "};\n\n";
+ printf "static const struct msgtable cdcmsgtable[] = {\n";
+ printf "%s", $cdcmsgtable;
+ printf "};\n\n";
+ printf "static const struct msgtable htngmsgtable[] = {\n";
+ printf "%s", $htngmsgtable;
+ printf "};\n";
+ exit 0;
+}
diff --git a/utils/libcecutil/cec-info.cpp b/utils/libcecutil/cec-info.cpp
new file mode 100644
index 00000000..c8dd7fe2
--- /dev/null
+++ b/utils/libcecutil/cec-info.cpp
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: LGPL-2.1-only
+/*
+ * CEC common helper functions
+ *
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <cec-info.h>
+#include <cec-htng.h>
+
+#include "cec-msgs-gen.h"
+
+const char *cec_opcode2s(unsigned opcode)
+{
+ for (unsigned i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++)
+ if (msgtable[i].opcode == opcode)
+ return msgtable[i].name;
+ return NULL;
+}
+
+const char *cec_cdc_opcode2s(unsigned cdc_opcode)
+{
+ for (unsigned i = 0; i < sizeof(cdcmsgtable) / sizeof(cdcmsgtable[0]); i++)
+ if (cdcmsgtable[i].opcode == cdc_opcode)
+ return cdcmsgtable[i].name;
+ return NULL;
+}
+
+const char *cec_htng_opcode2s(unsigned htng_opcode)
+{
+ for (unsigned i = 0; i < sizeof(htngmsgtable) / sizeof(htngmsgtable[0]); i++)
+ if (htngmsgtable[i].opcode == htng_opcode)
+ return htngmsgtable[i].name;
+ return NULL;
+}
+
+static std::string caps2s(unsigned caps)
+{
+ std::string s;
+
+ if (caps & CEC_CAP_PHYS_ADDR)
+ s += "\t\tPhysical Address\n";
+ if (caps & CEC_CAP_LOG_ADDRS)
+ s += "\t\tLogical Addresses\n";
+ if (caps & CEC_CAP_TRANSMIT)
+ s += "\t\tTransmit\n";
+ if (caps & CEC_CAP_PASSTHROUGH)
+ s += "\t\tPassthrough\n";
+ if (caps & CEC_CAP_RC)
+ s += "\t\tRemote Control Support\n";
+ if (caps & CEC_CAP_MONITOR_ALL)
+ s += "\t\tMonitor All\n";
+ if (caps & CEC_CAP_NEEDS_HPD)
+ s += "\t\tNeeds HPD\n";
+ if (caps & CEC_CAP_MONITOR_PIN)
+ s += "\t\tMonitor Pin\n";
+ if (caps & CEC_CAP_CONNECTOR_INFO)
+ s += "\t\tConnector Info\n";
+ return s;
+}
+
+static std::string laflags2s(unsigned flags)
+{
+ std::string s;
+
+ if (!flags)
+ return s;
+
+ s = "(";
+ if (flags & CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK)
+ s += "Allow Fallback to Unregistered, ";
+ if (flags & CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU)
+ s += "Allow RC Passthrough, ";
+ if (flags & CEC_LOG_ADDRS_FL_CDC_ONLY)
+ s += "CDC-Only, ";
+ if (s.length())
+ s.erase(s.length() - 2, 2);
+ return s + ")";
+}
+
+const char *cec_version2s(unsigned version)
+{
+ switch (version) {
+ case CEC_OP_CEC_VERSION_1_3A:
+ return "1.3a";
+ case CEC_OP_CEC_VERSION_1_4:
+ return "1.4";
+ case CEC_OP_CEC_VERSION_2_0:
+ return "2.0";
+ default:
+ return "Unknown";
+ }
+}
+
+/*
+ * Most of these vendor IDs come from include/cectypes.h from libcec.
+ */
+const char *cec_vendor2s(unsigned vendor)
+{
+ switch (vendor) {
+ case 0x000039:
+ case 0x000ce7:
+ return "(Toshiba)";
+ case 0x0000f0:
+ return "(Samsung)";
+ case 0x0005cd:
+ return "(Denon)";
+ case 0x000678:
+ return "(Marantz)";
+ case 0x000982:
+ return "(Loewe)";
+ case 0x0009b0:
+ return "(Onkyo)";
+ case 0x000c03:
+ return "(HDMI)";
+ case 0x001582:
+ return "(Pulse-Eight)";
+ case 0x001950:
+ case 0x9c645e:
+ return "(Harman Kardon)";
+ case 0x001a11:
+ return "(Google)";
+ case 0x0020c7:
+ return "(Akai)";
+ case 0x002467:
+ return "(AOC)";
+ case 0x005060:
+ return "(Cisco)";
+ case 0x008045:
+ return "(Panasonic)";
+ case 0x00903e:
+ return "(Philips)";
+ case 0x009053:
+ return "(Daewoo)";
+ case 0x00a0de:
+ return "(Yamaha)";
+ case 0x00d0d5:
+ return "(Grundig)";
+ case 0x00d38d:
+ return "(Hospitality Profile)";
+ case 0x00e036:
+ return "(Pioneer)";
+ case 0x00e091:
+ return "(LG)";
+ case 0x08001f:
+ case 0x534850:
+ return "(Sharp)";
+ case 0x080046:
+ return "(Sony)";
+ case 0x18c086:
+ return "(Broadcom)";
+ case 0x5cad76:
+ return "(TCL)";
+ case 0x6b746d:
+ return "(Vizio)";
+ case 0x743a65:
+ return "(NEC)";
+ case 0x8065e9:
+ return "(Benq)";
+ default:
+ return "";
+ }
+}
+
+const char *cec_prim_type2s(unsigned type)
+{
+ switch (type) {
+ case CEC_OP_PRIM_DEVTYPE_TV:
+ return "TV";
+ case CEC_OP_PRIM_DEVTYPE_RECORD:
+ return "Record";
+ case CEC_OP_PRIM_DEVTYPE_TUNER:
+ return "Tuner";
+ case CEC_OP_PRIM_DEVTYPE_PLAYBACK:
+ return "Playback";
+ case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:
+ return "Audio System";
+ case CEC_OP_PRIM_DEVTYPE_SWITCH:
+ return "Switch";
+ case CEC_OP_PRIM_DEVTYPE_PROCESSOR:
+ return "Processor";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *cec_la_type2s(unsigned type)
+{
+ switch (type) {
+ case CEC_LOG_ADDR_TYPE_TV:
+ return "TV";
+ case CEC_LOG_ADDR_TYPE_RECORD:
+ return "Record";
+ case CEC_LOG_ADDR_TYPE_TUNER:
+ return "Tuner";
+ case CEC_LOG_ADDR_TYPE_PLAYBACK:
+ return "Playback";
+ case CEC_LOG_ADDR_TYPE_AUDIOSYSTEM:
+ return "Audio System";
+ case CEC_LOG_ADDR_TYPE_SPECIFIC:
+ return "Specific";
+ case CEC_LOG_ADDR_TYPE_UNREGISTERED:
+ return "Unregistered";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *cec_la2s(unsigned la)
+{
+ switch (la & 0xf) {
+ case 0:
+ return "TV";
+ case 1:
+ return "Recording Device 1";
+ case 2:
+ return "Recording Device 2";
+ case 3:
+ return "Tuner 1";
+ case 4:
+ return "Playback Device 1";
+ case 5:
+ return "Audio System";
+ case 6:
+ return "Tuner 2";
+ case 7:
+ return "Tuner 3";
+ case 8:
+ return "Playback Device 2";
+ case 9:
+ return "Recording Device 3";
+ case 10:
+ return "Tuner 4";
+ case 11:
+ return "Playback Device 3";
+ case 12:
+ return "Reserved 1";
+ case 13:
+ return "Reserved 2";
+ case 14:
+ return "Specific";
+ case 15:
+ default:
+ return "Unregistered";
+ }
+}
+
+std::string cec_all_dev_types2s(unsigned types)
+{
+ std::string s;
+
+ if (types & CEC_OP_ALL_DEVTYPE_TV)
+ s += "TV, ";
+ if (types & CEC_OP_ALL_DEVTYPE_RECORD)
+ s += "Record, ";
+ if (types & CEC_OP_ALL_DEVTYPE_TUNER)
+ s += "Tuner, ";
+ if (types & CEC_OP_ALL_DEVTYPE_PLAYBACK)
+ s += "Playback, ";
+ if (types & CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM)
+ s += "Audio System, ";
+ if (types & CEC_OP_ALL_DEVTYPE_SWITCH)
+ s += "Switch, ";
+ if (s.length())
+ return s.erase(s.length() - 2, 2);
+ return s;
+}
+
+std::string cec_rc_src_prof2s(unsigned prof, const std::string &prefix)
+{
+ std::string s;
+
+ prof &= 0x1f;
+ if (prof == 0)
+ return prefix + "\t\tNone\n";
+ if (prof & CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU)
+ s += prefix + "\t\tSource Has Device Root Menu\n";
+ if (prof & CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU)
+ s += prefix + "\t\tSource Has Device Setup Menu\n";
+ if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU)
+ s += prefix + "\t\tSource Has Contents Menu\n";
+ if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU)
+ s += prefix + "\t\tSource Has Media Top Menu\n";
+ if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU)
+ s += prefix + "\t\tSource Has Media Context-Sensitive Menu\n";
+ return s;
+}
+
+std::string cec_dev_feat2s(unsigned feat, const std::string &prefix)
+{
+ std::string s;
+
+ feat &= 0x7e;
+ if (feat == 0)
+ return prefix + "\t\tNone\n";
+ if (feat & CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN)
+ s += prefix + "\t\tTV Supports <Record TV Screen>\n";
+ if (feat & CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING)
+ s += prefix + "\t\tTV Supports <Set OSD String>\n";
+ if (feat & CEC_OP_FEAT_DEV_HAS_DECK_CONTROL)
+ s += prefix + "\t\tSupports Deck Control\n";
+ if (feat & CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE)
+ s += prefix + "\t\tSource Supports <Set Audio Rate>\n";
+ if (feat & CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX)
+ s += prefix + "\t\tSink Supports ARC Tx\n";
+ if (feat & CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX)
+ s += prefix + "\t\tSource Supports ARC Rx\n";
+ return s;
+}
+
+static std::string tx_status2s(const struct cec_msg &msg)
+{
+ std::string s;
+ char num[4];
+ unsigned stat = msg.tx_status;
+
+ if (stat)
+ s += "Tx";
+ if (stat & CEC_TX_STATUS_OK)
+ s += ", OK";
+ if (stat & CEC_TX_STATUS_ARB_LOST) {
+ sprintf(num, "%u", msg.tx_arb_lost_cnt);
+ s += ", Arbitration Lost";
+ if (msg.tx_arb_lost_cnt)
+ s += " (" + std::string(num) + ")";
+ }
+ if (stat & CEC_TX_STATUS_NACK) {
+ sprintf(num, "%u", msg.tx_nack_cnt);
+ s += ", Not Acknowledged";
+ if (msg.tx_nack_cnt)
+ s += " (" + std::string(num) + ")";
+ }
+ if (stat & CEC_TX_STATUS_LOW_DRIVE) {
+ sprintf(num, "%u", msg.tx_low_drive_cnt);
+ s += ", Low Drive";
+ if (msg.tx_low_drive_cnt)
+ s += " (" + std::string(num) + ")";
+ }
+ if (stat & CEC_TX_STATUS_ERROR) {
+ sprintf(num, "%u", msg.tx_error_cnt);
+ s += ", Error";
+ if (msg.tx_error_cnt)
+ s += " (" + std::string(num) + ")";
+ }
+ if (stat & CEC_TX_STATUS_ABORTED)
+ s += ", Aborted";
+ if (stat & CEC_TX_STATUS_TIMEOUT)
+ s += ", Timeout";
+ if (stat & CEC_TX_STATUS_MAX_RETRIES)
+ s += ", Max Retries";
+ return s;
+}
+
+static std::string rx_status2s(unsigned stat)
+{
+ std::string s;
+
+ if (stat)
+ s += "Rx";
+ if (stat & CEC_RX_STATUS_OK)
+ s += ", OK";
+ if (stat & CEC_RX_STATUS_TIMEOUT)
+ s += ", Timeout";
+ if (stat & CEC_RX_STATUS_FEATURE_ABORT)
+ s += ", Feature Abort";
+ if (stat & CEC_RX_STATUS_ABORTED)
+ s += ", Aborted";
+ return s;
+}
+
+std::string cec_status2s(const struct cec_msg &msg)
+{
+ std::string s;
+
+ if (msg.tx_status)
+ s = tx_status2s(msg);
+ if (msg.rx_status) {
+ if (!s.empty())
+ s += ", ";
+ s += rx_status2s(msg.rx_status);
+ }
+ return s;
+}
+
+void cec_driver_info(const struct cec_caps &caps,
+ const struct cec_log_addrs &laddrs, __u16 phys_addr,
+ const struct cec_connector_info &conn_info)
+{
+ printf("Driver Info:\n");
+ printf("\tDriver Name : %s\n", caps.driver);
+ printf("\tAdapter Name : %s\n", caps.name);
+ printf("\tCapabilities : 0x%08x\n", caps.capabilities);
+ printf("%s", caps2s(caps.capabilities).c_str());
+ printf("\tDriver version : %d.%d.%d\n",
+ caps.version >> 16,
+ (caps.version >> 8) & 0xff,
+ caps.version & 0xff);
+ printf("\tAvailable Logical Addresses: %u\n",
+ caps.available_log_addrs);
+ switch (conn_info.type) {
+ case CEC_CONNECTOR_TYPE_NO_CONNECTOR:
+ printf("\tConnector Info : None\n");
+ break;
+ case CEC_CONNECTOR_TYPE_DRM:
+ printf("\tDRM Connector Info : card %u, connector %u\n",
+ conn_info.drm.card_no, conn_info.drm.connector_id);
+ break;
+ default:
+ printf("\tConnector Info : Type %u\n",
+ conn_info.type);
+ break;
+ }
+
+ printf("\tPhysical Address : %x.%x.%x.%x\n",
+ cec_phys_addr_exp(phys_addr));
+ printf("\tLogical Address Mask : 0x%04x\n", laddrs.log_addr_mask);
+ printf("\tCEC Version : %s\n", cec_version2s(laddrs.cec_version));
+ if (laddrs.vendor_id != CEC_VENDOR_ID_NONE)
+ printf("\tVendor ID : 0x%06x %s\n",
+ laddrs.vendor_id, cec_vendor2s(laddrs.vendor_id));
+ printf("\tOSD Name : %s\n", laddrs.osd_name);
+ printf("\tLogical Addresses : %u %s\n",
+ laddrs.num_log_addrs, laflags2s(laddrs.flags).c_str());
+ for (unsigned i = 0; i < laddrs.num_log_addrs; i++) {
+ if (laddrs.log_addr[i] == CEC_LOG_ADDR_INVALID)
+ printf("\n\t Logical Address : Not Allocated\n");
+ else
+ printf("\n\t Logical Address : %d (%s)\n",
+ laddrs.log_addr[i], cec_la2s(laddrs.log_addr[i]));
+ printf("\t Primary Device Type : %s\n",
+ cec_prim_type2s(laddrs.primary_device_type[i]));
+ printf("\t Logical Address Type : %s\n",
+ cec_la_type2s(laddrs.log_addr_type[i]));
+ if (laddrs.cec_version < CEC_OP_CEC_VERSION_2_0)
+ continue;
+ printf("\t All Device Types : %s\n",
+ cec_all_dev_types2s(laddrs.all_device_types[i]).c_str());
+
+ bool is_dev_feat = false;
+ for (unsigned idx = 0; idx < sizeof(laddrs.features[0]); idx++) {
+ __u8 byte = laddrs.features[i][idx];
+
+ if (!is_dev_feat) {
+ if (byte & 0x40) {
+ printf("\t RC Source Profile :\n%s",
+ cec_rc_src_prof2s(byte, "").c_str());
+ } else {
+ const char *s = "Reserved";
+
+ switch (byte & 0xf) {
+ case 0:
+ s = "None";
+ break;
+ case 2:
+ s = "RC Profile 1";
+ break;
+ case 6:
+ s = "RC Profile 2";
+ break;
+ case 10:
+ s = "RC Profile 3";
+ break;
+ case 14:
+ s = "RC Profile 4";
+ break;
+ }
+ printf("\t RC TV Profile : %s\n", s);
+ }
+ } else {
+ printf("\t Device Features :\n%s",
+ cec_dev_feat2s(byte, "").c_str());
+ }
+ if (byte & CEC_OP_FEAT_EXT)
+ continue;
+ if (!is_dev_feat)
+ is_dev_feat = true;
+ else
+ break;
+ }
+ }
+}
+
+std::string cec_device_find(const char *driver, const char *adapter)
+{
+ DIR *dp;
+ struct dirent *ep;
+ std::string name;
+
+ dp = opendir("/dev");
+ if (dp == NULL) {
+ perror("Couldn't open the directory");
+ return name;
+ }
+ while ((ep = readdir(dp)))
+ if (!memcmp(ep->d_name, "cec", 3) && isdigit(ep->d_name[3])) {
+ std::string devname("/dev/");
+ struct cec_caps caps;
+ int fd;
+
+ devname += ep->d_name;
+ fd = open(devname.c_str(), O_RDWR);
+
+ if (fd < 0)
+ continue;
+ int err = ioctl(fd, CEC_ADAP_G_CAPS, &caps);
+ close(fd);
+ if (err)
+ continue;
+ if ((!driver || !strcmp(driver, caps.driver)) &&
+ (!adapter || !strcmp(adapter, caps.name))) {
+ name = devname;
+ break;
+ }
+ }
+ closedir(dp);
+ return name;
+}
diff --git a/utils/libcecutil/cec-info.h b/utils/libcecutil/cec-info.h
new file mode 100644
index 00000000..22e54f2a
--- /dev/null
+++ b/utils/libcecutil/cec-info.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * CEC common helper functions
+ *
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _CEC_INFO_H_
+#define _CEC_INFO_H_
+
+#include <linux/cec.h>
+
+#define cec_phys_addr_exp(pa) \
+ ((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf
+
+const char *cec_opcode2s(unsigned opcode);
+const char *cec_cdc_opcode2s(unsigned cdc_opcode);
+const char *cec_htng_opcode2s(unsigned htng_opcode);
+const char *cec_la2s(unsigned la);
+const char *cec_la_type2s(unsigned type);
+const char *cec_prim_type2s(unsigned type);
+const char *cec_version2s(unsigned version);
+const char *cec_vendor2s(unsigned vendor);
+std::string cec_all_dev_types2s(unsigned types);
+std::string cec_rc_src_prof2s(unsigned prof, const std::string &prefix);
+std::string cec_dev_feat2s(unsigned feat, const std::string &prefix);
+std::string cec_status2s(const struct cec_msg &msg);
+
+void cec_driver_info(const struct cec_caps &caps,
+ const struct cec_log_addrs &laddrs, __u16 phys_addr,
+ const struct cec_connector_info &conn_info);
+
+std::string cec_device_find(const char *driver, const char *adapter);
+
+#endif
diff --git a/utils/libcecutil/cec-log.cpp b/utils/libcecutil/cec-log.cpp
new file mode 100644
index 00000000..1e45f151
--- /dev/null
+++ b/utils/libcecutil/cec-log.cpp
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+/*
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <string>
+#include <linux/cec-funcs.h>
+#include "cec-htng-funcs.h"
+#include "cec-info.h"
+#include "cec-log.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;
+ }
+ }
+ /* fall through */
+ case CEC_ARG_TYPE_U8:
+ 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,
+ val >> 12, (val >> 8) & 0xf, (val >> 4) & 0xf, val & 0xf);
+ 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_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 NULL;
+ 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, (__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_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("CEC_MSG_VENDOR_COMMAND_WITH_ID (0x%02x):\n",
+ CEC_MSG_VENDOR_COMMAND_WITH_ID);
+ log_arg(&arg_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("CEC_MSG_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("CEC_MSG_VENDOR_COMMAND_WITH_ID (0x%02x):\n",
+ CEC_MSG_VENDOR_COMMAND_WITH_ID);
+ log_arg(&arg_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("CEC_MSG_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("CEC_MSG_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("CEC_MSG (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 NULL;
+}
diff --git a/utils/libcecutil/cec-log.h b/utils/libcecutil/cec-log.h
new file mode 100644
index 00000000..1fb74851
--- /dev/null
+++ b/utils/libcecutil/cec-log.h
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+/*
+ * Copyright 2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _CEC_LOG_H_
+#define _CEC_LOG_H_
+
+struct cec_arg_enum_values {
+ const char *type_name;
+ __u8 value;
+};
+
+enum cec_arg_type {
+ CEC_ARG_TYPE_U8,
+ CEC_ARG_TYPE_U16,
+ CEC_ARG_TYPE_U32,
+ CEC_ARG_TYPE_STRING,
+ CEC_ARG_TYPE_ENUM,
+};
+
+struct cec_arg {
+ enum cec_arg_type type;
+ __u8 num_enum_values;
+ const struct cec_arg_enum_values *values;
+};
+
+#define CEC_MAX_ARGS 16
+
+struct cec_msg_args {
+ __u8 msg;
+ __u8 num_args;
+ const char *arg_names[CEC_MAX_ARGS+1];
+ const struct cec_arg *args[CEC_MAX_ARGS];
+ const char *msg_name;
+};
+
+const struct cec_msg_args *cec_log_msg_args(unsigned int index);
+void cec_log_msg(const struct cec_msg *msg);
+void cec_log_htng_msg(const struct cec_msg *msg);
+const char *cec_log_ui_cmd_string(__u8 ui_cmd);
+
+#endif
diff --git a/utils/libcecutil/cec-parse.cpp b/utils/libcecutil/cec-parse.cpp
new file mode 100644
index 00000000..bb73fe57
--- /dev/null
+++ b/utils/libcecutil/cec-parse.cpp
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <stdarg.h>
+#include <ctime>
+#include <cerrno>
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <linux/cec-funcs.h>
+#include "cec-htng-funcs.h"
+#include "cec-log.h"
+#include "cec-parse.h"
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+static struct cec_op_digital_service_id *args2digital_service_id(__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)
+{
+ static struct cec_op_digital_service_id dsid;
+
+ dsid.service_id_method = service_id_method;
+ dsid.dig_bcast_system = dig_bcast_system;
+ if (service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
+ dsid.channel.channel_number_fmt = channel_number_fmt;
+ dsid.channel.major = major;
+ dsid.channel.minor = minor;
+ return &dsid;
+ }
+ switch (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:
+ dsid.atsc.transport_id = transport_id;
+ dsid.atsc.program_number = program_number;
+ break;
+ default:
+ dsid.dvb.transport_id = transport_id;
+ dsid.dvb.service_id = service_id;
+ dsid.dvb.orig_network_id = orig_network_id;
+ break;
+ }
+ return &dsid;
+}
+
+static struct cec_op_ui_command *args2ui_command(__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)
+{
+ static struct cec_op_ui_command ui_command;
+
+ ui_command.ui_cmd = ui_cmd;
+ ui_command.has_opt_arg = has_opt_arg;
+ if (!has_opt_arg)
+ return &ui_command;
+ switch (ui_cmd) {
+ case CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE:
+ ui_command.ui_broadcast_type = ui_bcast_type;
+ break;
+ case CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION:
+ ui_command.ui_sound_presentation_control = ui_snd_pres_ctl;
+ break;
+ case CEC_OP_UI_CMD_PLAY_FUNCTION:
+ ui_command.play_mode = play_mode;
+ break;
+ case CEC_OP_UI_CMD_TUNE_FUNCTION:
+ ui_command.channel_identifier.channel_number_fmt = channel_number_fmt;
+ ui_command.channel_identifier.major = major;
+ ui_command.channel_identifier.minor = minor;
+ break;
+ case CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION:
+ ui_command.ui_function_media = ui_function_media;
+ break;
+ case CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION:
+ ui_command.ui_function_select_av_input = ui_function_select_av_input;
+ break;
+ case CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION:
+ ui_command.ui_function_select_audio_input = ui_function_select_audio_input;
+ break;
+ default:
+ ui_command.has_opt_arg = false;
+ break;
+ }
+ return &ui_command;
+}
+
+static __u32 *args2short_descrs(__u32 descriptor1,
+ __u32 descriptor2,
+ __u32 descriptor3,
+ __u32 descriptor4)
+{
+ static __u32 descriptors[4];
+
+ descriptors[0] = descriptor1;
+ descriptors[1] = descriptor2;
+ descriptors[2] = descriptor3;
+ descriptors[3] = descriptor4;
+ return descriptors;
+}
+
+static __u8 *args2short_aud_fmt_ids(__u8 audio_format_id1,
+ __u8 audio_format_id2,
+ __u8 audio_format_id3,
+ __u8 audio_format_id4)
+{
+ static __u8 audio_format_ids[4];
+
+ audio_format_ids[0] = audio_format_id1;
+ audio_format_ids[1] = audio_format_id2;
+ audio_format_ids[2] = audio_format_id3;
+ audio_format_ids[3] = audio_format_id4;
+ return audio_format_ids;
+}
+
+static __u8 *args2short_aud_fmt_codes(__u8 audio_format_code1,
+ __u8 audio_format_code2,
+ __u8 audio_format_code3,
+ __u8 audio_format_code4)
+{
+ static __u8 audio_format_codes[4];
+
+ audio_format_codes[0] = audio_format_code1;
+ audio_format_codes[1] = audio_format_code2;
+ audio_format_codes[2] = audio_format_code3;
+ audio_format_codes[3] = audio_format_code4;
+ return audio_format_codes;
+}
+
+int cec_parse_subopt(char **subs, const char * const *subopts, char **value)
+{
+ int opt = getsubopt(subs, (char * const *)subopts, value);
+
+ if (opt == -1) {
+ fprintf(stderr, "Invalid suboptions specified\n");
+ return -1;
+ }
+ if (*value == NULL) {
+ fprintf(stderr, "No value given to suboption <%s>\n",
+ subopts[opt]);
+ return -1;
+ }
+ return opt;
+}
+
+static unsigned parse_enum(const char *value, const struct cec_arg *a)
+{
+ if (isdigit(*value))
+ return strtoul(value, NULL, 0);
+ for (int i = 0; i < a->num_enum_values; i++) {
+ if (!strcmp(value, a->values[i].type_name))
+ return a->values[i].value;
+ }
+ return 0;
+}
+
+unsigned cec_parse_phys_addr(const char *value)
+{
+ unsigned p1, p2, p3, p4;
+
+ if (!strchr(value, '.'))
+ return strtoul(value, NULL, 0);
+ if (sscanf(value, "%x.%x.%x.%x", &p1, &p2, &p3, &p4) != 4) {
+ fprintf(stderr, "Expected a physical address of the form x.x.x.x\n");
+ return 0;
+ }
+ if (p1 > 0xf || p2 > 0xf || p3 > 0xf || p4 > 0xf) {
+ fprintf(stderr, "Physical address components should never be larger than 0xf\n");
+ return 0;
+ }
+ return (p1 << 12) | (p2 << 8) | (p3 << 4) | p4;
+}
+
+static unsigned parse_latency(const char *value)
+{
+ char *end;
+ unsigned delay = strtoul(value, &end, 0);
+
+ if (!memcmp(end, "ms", 2))
+ delay = (delay / 2) + 1;
+ if (delay < 1)
+ delay = 1;
+ else if (delay > 251)
+ delay = 251;
+ return delay;
+}
+
+
+#define VENDOR_EXTRA \
+ " --vendor-command payload=<byte>[:<byte>]*\n" \
+ " Send VENDOR_COMMAND message (" xstr(CEC_MSG_VENDOR_COMMAND) ")\n" \
+ " --vendor-command-with-id vendor-id=<val>,cmd=<byte>[:<byte>]*\n" \
+ " Send VENDOR_COMMAND_WITH_ID message (" xstr(CEC_MSG_VENDOR_COMMAND_WITH_ID) ")\n" \
+ " --vendor-remote-button-down rc-code=<byte>[:<byte>]*\n" \
+ " Send VENDOR_REMOTE_BUTTON_DOWN message (" xstr(CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN) ")\n"
+
+#include "cec-parse-src-gen.h"
diff --git a/utils/libcecutil/cec-parse.h b/utils/libcecutil/cec-parse.h
new file mode 100644
index 00000000..3346d803
--- /dev/null
+++ b/utils/libcecutil/cec-parse.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+/*
+ * Copyright 2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _CEC_PARSE_H_
+#define _CEC_PARSE_H_
+
+#include "cec-parse-gen.h"
+
+void cec_parse_usage_options(const char *options);
+void cec_parse_msg_args(struct cec_msg &msg, int reply, const struct cec_msg_args *opt, int ch);
+int cec_parse_subopt(char **subs, const char * const *subopts, char **value);
+unsigned cec_parse_phys_addr(const char *value);
+
+#endif

Privacy Policy