This patch series adds two new utilities to the v4l-utils git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/). It assumes that the new CEC framework available in the kernel:
http://www.mail-archive.com/linux-media@vger.kernel.org/msg90085.html
The first patch adds the new cec headers to the 'sync-with-kernel' target, the second syncs with the kernel and adds the new cec headers to v4l-utils, the third adds the compliance utility and the last adds the cec-ctl utility.
The cec-compliance utility is by no means 100% coverage, in particular the event API and non-blocking ioctls are untested. But it is a starting point, and a complex protocol like CEC really needs a compliance tool.
The cec-ctl utility has almost full CEC message coverage: all generated from the cec headers, so this is easy to keep up to date.
Regards,
Hans
Changes since v1:
- Added CEC message logging/monitoring to cec-ctl. - Add support to clear the logical addresses.
Hans Verkuil (4): Makefile.am: copy cec headers with make sync-with-kernel sync-with-kernel cec-compliance: add new CEC compliance utility cec-ctl: CEC control utility
Makefile.am | 4 + configure.ac | 2 + contrib/freebsd/include/linux/input.h | 29 + contrib/freebsd/include/linux/v4l2-controls.h | 4 + include/linux/cec-funcs.h | 1771 +++++++++++++++++++++++++ include/linux/cec.h | 781 +++++++++++ include/linux/v4l2-controls.h | 4 + utils/Makefile.am | 2 + utils/cec-compliance/Makefile.am | 3 + utils/cec-compliance/cec-compliance.cpp | 926 +++++++++++++ utils/cec-compliance/cec-compliance.h | 87 ++ utils/cec-ctl/Makefile.am | 8 + utils/cec-ctl/cec-ctl.cpp | 1296 ++++++++++++++++++ utils/cec-ctl/msg2ctl.pl | 430 ++++++ utils/keytable/parse.h | 18 + utils/keytable/rc_keymaps/lme2510 | 132 +- utils/keytable/rc_maps.cfg | 1 + 17 files changed, 5432 insertions(+), 66 deletions(-) create mode 100644 include/linux/cec-funcs.h create mode 100644 include/linux/cec.h create mode 100644 utils/cec-compliance/Makefile.am create mode 100644 utils/cec-compliance/cec-compliance.cpp create mode 100644 utils/cec-compliance/cec-compliance.h create mode 100644 utils/cec-ctl/Makefile.am create mode 100644 utils/cec-ctl/cec-ctl.cpp create mode 100644 utils/cec-ctl/msg2ctl.pl
Copy the new cec headers.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- Makefile.am | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/Makefile.am b/Makefile.am index 1a61592..b8c450d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,6 +21,8 @@ sync-with-kernel: ! -f $(KERNEL_DIR)/usr/include/linux/v4l2-common.h -o \ ! -f $(KERNEL_DIR)/usr/include/linux/v4l2-subdev.h -o \ ! -f $(KERNEL_DIR)/usr/include/linux/v4l2-mediabus.h -o \ + ! -f $(KERNEL_DIR)/usr/include/linux/cec.h -o \ + ! -f $(KERNEL_DIR)/usr/include/linux/cec-funcs.h -o \ ! -f $(KERNEL_DIR)/usr/include/linux/ivtv.h -o \ ! -f $(KERNEL_DIR)/usr/include/linux/dvb/frontend.h -o \ ! -f $(KERNEL_DIR)/usr/include/linux/dvb/dmx.h -o \ @@ -38,6 +40,8 @@ sync-with-kernel: cp -a $(KERNEL_DIR)/usr/include/linux/v4l2-mediabus.h $(top_srcdir)/include/linux cp -a $(KERNEL_DIR)/usr/include/linux/media-bus-format.h $(top_srcdir)/include/linux cp -a $(KERNEL_DIR)/usr/include/linux/media.h $(top_srcdir)/include/linux + cp -a $(KERNEL_DIR)/usr/include/linux/cec.h $(top_srcdir)/include/linux + cp -a $(KERNEL_DIR)/usr/include/linux/cec-funcs.h $(top_srcdir)/include/linux cp -a $(KERNEL_DIR)/usr/include/linux/ivtv.h $(top_srcdir)/include/linux cp -a $(KERNEL_DIR)/usr/include/linux/dvb/frontend.h $(top_srcdir)/include/linux/dvb cp -a $(KERNEL_DIR)/usr/include/linux/dvb/dmx.h $(top_srcdir)/include/linux/dvb
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- contrib/freebsd/include/linux/input.h | 29 + contrib/freebsd/include/linux/v4l2-controls.h | 4 + include/linux/cec-funcs.h | 1771 +++++++++++++++++++++++++ include/linux/cec.h | 781 +++++++++++ include/linux/v4l2-controls.h | 4 + utils/keytable/parse.h | 18 + utils/keytable/rc_keymaps/lme2510 | 132 +- utils/keytable/rc_maps.cfg | 1 + 8 files changed, 2674 insertions(+), 66 deletions(-) create mode 100644 include/linux/cec-funcs.h create mode 100644 include/linux/cec.h
diff --git a/contrib/freebsd/include/linux/input.h b/contrib/freebsd/include/linux/input.h index 19345e1..1bc7241 100644 --- a/contrib/freebsd/include/linux/input.h +++ b/contrib/freebsd/include/linux/input.h @@ -786,6 +786,34 @@ struct input_keymap_entry { #define KEY_KBDINPUTASSIST_ACCEPT 0x264 #define KEY_KBDINPUTASSIST_CANCEL 0x265
+#define KEY_RIGHT_UP 0x266 +#define KEY_RIGHT_DOWN 0x267 +#define KEY_LEFT_UP 0x268 +#define KEY_LEFT_DOWN 0x269 +#define KEY_ROOT_MENU 0x26a /* Show Device's Root Menu */ +#define KEY_MEDIA_TOP_MENU 0x26b /* Show Top Menu of the Media (e.g. DVD) */ +#define KEY_NUMERIC_11 0x26c +#define KEY_NUMERIC_12 0x26d +/* + * Toggle Audio Description: refers to an audio service that helps blind and + * visually impaired consumers understand the action in a program. Note: in + * some countries this is referred to as "Video Description". + */ +#define KEY_AUDIO_DESC 0x26e +#define KEY_3D_MODE 0x26f +#define KEY_NEXT_FAVORITE 0x270 +#define KEY_STOP_RECORD 0x271 +#define KEY_PAUSE_RECORD 0x272 +#define KEY_VOD 0x273 /* Video on Demand */ +#define KEY_UNMUTE 0x274 +#define KEY_FASTREVERSE 0x275 +#define KEY_SLOWREVERSE 0x276 +/* + * Control a data application associated with the currently viewed channel, + * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.) + */ +#define KEY_DATA 0x275 + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 @@ -1006,6 +1034,7 @@ struct input_keymap_entry { #define BUS_GSC 0x1A #define BUS_ATARI 0x1B #define BUS_SPI 0x1C +#define BUS_CEC 0x1D
/* * MT_TOOL types diff --git a/contrib/freebsd/include/linux/v4l2-controls.h b/contrib/freebsd/include/linux/v4l2-controls.h index 9f6e108..d448c53 100644 --- a/contrib/freebsd/include/linux/v4l2-controls.h +++ b/contrib/freebsd/include/linux/v4l2-controls.h @@ -174,6 +174,10 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_ADV7180_BASE (V4L2_CID_USER_BASE + 0x1070)
+/* The base for the tc358743 driver controls. + * We reserve 16 controls for this driver. */ +#define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ diff --git a/include/linux/cec-funcs.h b/include/linux/cec-funcs.h new file mode 100644 index 0000000..45fb7bd --- /dev/null +++ b/include/linux/cec-funcs.h @@ -0,0 +1,1771 @@ +#ifndef _CEC_FUNCS_H +#define _CEC_FUNCS_H + +#include <linux/cec.h> + +/* One Touch Play Feature */ +static __inline__ void cec_msg_active_source(struct cec_msg *msg, __u16 phys_addr) +{ + msg->len = 4; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_ACTIVE_SOURCE; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; +} + +static __inline__ void cec_ops_active_source(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static __inline__ void cec_msg_image_view_on(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_IMAGE_VIEW_ON; +} + +static __inline__ void cec_msg_text_view_on(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_TEXT_VIEW_ON; +} + + +/* Routing Control Feature */ +static __inline__ void cec_msg_inactive_source(struct cec_msg *msg, + __u16 phys_addr) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_INACTIVE_SOURCE; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; +} + +static __inline__ void cec_ops_inactive_source(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static __inline__ void cec_msg_request_active_source(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_REQUEST_ACTIVE_SOURCE; + msg->reply = reply ? CEC_MSG_ACTIVE_SOURCE : 0; +} + +static __inline__ void cec_msg_routing_information(struct cec_msg *msg, + __u16 phys_addr) +{ + msg->len = 4; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_ROUTING_INFORMATION; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; +} + +static __inline__ void cec_ops_routing_information(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static __inline__ void cec_msg_routing_change(struct cec_msg *msg, + bool reply, + __u16 orig_phys_addr, + __u16 new_phys_addr) +{ + msg->len = 6; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_ROUTING_CHANGE; + msg->msg[2] = orig_phys_addr >> 8; + msg->msg[3] = orig_phys_addr & 0xff; + msg->msg[4] = new_phys_addr >> 8; + msg->msg[5] = new_phys_addr & 0xff; + msg->reply = reply ? CEC_MSG_ROUTING_INFORMATION : 0; +} + +static __inline__ void cec_ops_routing_change(const struct cec_msg *msg, + __u16 *orig_phys_addr, + __u16 *new_phys_addr) +{ + *orig_phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *new_phys_addr = (msg->msg[4] << 8) | msg->msg[5]; +} + +static __inline__ void cec_msg_set_stream_path(struct cec_msg *msg, __u16 phys_addr) +{ + msg->len = 4; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_SET_STREAM_PATH; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; +} + +static __inline__ void cec_ops_set_stream_path(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + + +/* Standby Feature */ +static __inline__ void cec_msg_standby(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_STANDBY; +} + + +/* One Touch Record Feature */ +static __inline__ void cec_msg_record_off(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_RECORD_OFF; +} + +struct cec_op_arib_data { + __u16 transport_id; + __u16 service_id; + __u16 orig_network_id; +}; + +struct cec_op_atsc_data { + __u16 transport_id; + __u16 program_number; +}; + +struct cec_op_dvb_data { + __u16 transport_id; + __u16 service_id; + __u16 orig_network_id; +}; + +struct cec_op_channel_data { + __u8 channel_number_fmt; + __u16 major; + __u16 minor; +}; + +struct cec_op_digital_service_id { + __u8 service_id_method; + __u8 dig_bcast_system; + union { + struct cec_op_arib_data arib; + struct cec_op_atsc_data atsc; + struct cec_op_dvb_data dvb; + struct cec_op_channel_data channel; + }; +}; + +struct cec_op_record_src { + __u8 type; + union { + struct cec_op_digital_service_id digital; + struct { + __u8 ana_bcast_type; + __u16 ana_freq; + __u8 bcast_system; + } analog; + struct { + __u8 plug; + } ext_plug; + struct { + __u16 phys_addr; + } ext_phys_addr; + }; +}; + +static __inline__ void cec_set_digital_service_id(__u8 *msg, + const struct cec_op_digital_service_id *digital) +{ + *msg++ = (digital->service_id_method << 7) | digital->dig_bcast_system; + if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) { + *msg++ = (digital->channel.channel_number_fmt << 2) | + (digital->channel.major >> 8); + *msg++ = digital->channel.major && 0xff; + *msg++ = digital->channel.minor >> 8; + *msg++ = digital->channel.minor & 0xff; + *msg++ = 0; + *msg++ = 0; + 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: + *msg++ = digital->atsc.transport_id >> 8; + *msg++ = digital->atsc.transport_id & 0xff; + *msg++ = digital->atsc.program_number >> 8; + *msg++ = digital->atsc.program_number & 0xff; + *msg++ = 0; + *msg++ = 0; + break; + default: + *msg++ = digital->dvb.transport_id >> 8; + *msg++ = digital->dvb.transport_id & 0xff; + *msg++ = digital->dvb.service_id >> 8; + *msg++ = digital->dvb.service_id & 0xff; + *msg++ = digital->dvb.orig_network_id >> 8; + *msg++ = digital->dvb.orig_network_id & 0xff; + break; + } +} + +static __inline__ void cec_get_digital_service_id(const __u8 *msg, + struct cec_op_digital_service_id *digital) +{ + digital->service_id_method = msg[0] >> 7; + digital->dig_bcast_system = msg[0] & 0x7f; + if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) { + digital->channel.channel_number_fmt = msg[1] >> 2; + digital->channel.major = ((msg[1] & 3) << 6) | msg[2]; + digital->channel.minor = (msg[3] << 8) | msg[4]; + return; + } + digital->dvb.transport_id = (msg[1] << 8) | msg[2]; + digital->dvb.service_id = (msg[3] << 8) | msg[4]; + digital->dvb.orig_network_id = (msg[5] << 8) | msg[6]; +} + +static __inline__ void cec_msg_record_on_own(struct cec_msg *msg) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_RECORD_ON; + msg->msg[2] = CEC_OP_RECORD_SRC_OWN; +} + +static __inline__ void cec_msg_record_on_digital(struct cec_msg *msg, + const struct cec_op_digital_service_id *digital) +{ + msg->len = 10; + msg->msg[1] = CEC_MSG_RECORD_ON; + msg->msg[2] = CEC_OP_RECORD_SRC_DIGITAL; + cec_set_digital_service_id(msg->msg + 3, digital); +} + +static __inline__ void cec_msg_record_on_analog(struct cec_msg *msg, + __u8 ana_bcast_type, + __u16 ana_freq, + __u8 bcast_system) +{ + msg->len = 7; + msg->msg[1] = CEC_MSG_RECORD_ON; + msg->msg[2] = CEC_OP_RECORD_SRC_ANALOG; + msg->msg[3] = ana_bcast_type; + msg->msg[4] = ana_freq >> 8; + msg->msg[5] = ana_freq & 0xff; + msg->msg[6] = bcast_system; +} + +static __inline__ void cec_msg_record_on_plug(struct cec_msg *msg, + __u8 plug) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_RECORD_ON; + msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PLUG; + msg->msg[3] = plug; +} + +static __inline__ void cec_msg_record_on_phys_addr(struct cec_msg *msg, + __u16 phys_addr) +{ + msg->len = 5; + msg->msg[1] = CEC_MSG_RECORD_ON; + msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PHYS_ADDR; + msg->msg[3] = phys_addr >> 8; + msg->msg[4] = phys_addr & 0xff; +} + +static __inline__ void cec_msg_record_on(struct cec_msg *msg, + const struct cec_op_record_src *rec_src) +{ + switch (rec_src->type) { + case CEC_OP_RECORD_SRC_OWN: + cec_msg_record_on_own(msg); + break; + case CEC_OP_RECORD_SRC_DIGITAL: + cec_msg_record_on_digital(msg, &rec_src->digital); + break; + case CEC_OP_RECORD_SRC_ANALOG: + cec_msg_record_on_analog(msg, + rec_src->analog.ana_bcast_type, + rec_src->analog.ana_freq, + rec_src->analog.bcast_system); + break; + case CEC_OP_RECORD_SRC_EXT_PLUG: + cec_msg_record_on_plug(msg, rec_src->ext_plug.plug); + break; + case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR: + cec_msg_record_on_phys_addr(msg, + rec_src->ext_phys_addr.phys_addr); + break; + } +} + +static __inline__ void cec_ops_record_on(const struct cec_msg *msg, + struct cec_op_record_src *rec_src) +{ + rec_src->type = msg->msg[2]; + switch (rec_src->type) { + case CEC_OP_RECORD_SRC_OWN: + break; + case CEC_OP_RECORD_SRC_DIGITAL: + cec_get_digital_service_id(msg->msg + 3, &rec_src->digital); + break; + case CEC_OP_RECORD_SRC_ANALOG: + rec_src->analog.ana_bcast_type = msg->msg[3]; + rec_src->analog.ana_freq = + (msg->msg[4] << 8) | msg->msg[5]; + rec_src->analog.bcast_system = msg->msg[6]; + break; + case CEC_OP_RECORD_SRC_EXT_PLUG: + rec_src->ext_plug.plug = msg->msg[3]; + break; + case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR: + rec_src->ext_phys_addr.phys_addr = + (msg->msg[3] << 8) | msg->msg[4]; + break; + } +} + +static __inline__ void cec_msg_record_status(struct cec_msg *msg, __u8 rec_status) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_RECORD_STATUS; + msg->msg[2] = rec_status; +} + +static __inline__ void cec_ops_record_status(const struct cec_msg *msg, + __u8 *rec_status) +{ + *rec_status = msg->msg[2]; +} + +static __inline__ void cec_msg_record_tv_screen(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_RECORD_TV_SCREEN; + msg->reply = reply ? CEC_MSG_RECORD_ON : 0; +} + + +/* Timer Programming Feature */ +static __inline__ void cec_msg_timer_status(struct cec_msg *msg, + __u8 timer_overlap_warning, + __u8 media_info, + __u8 prog_info, + __u8 prog_error, + __u8 duration_hr, + __u8 duration_min) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_TIMER_STATUS; + msg->msg[2] = (timer_overlap_warning << 7) | + (media_info << 5) | + (prog_info ? 0x10 : 0) | + (prog_info ? prog_info : prog_error); + if (prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE || + prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE || + prog_error == CEC_OP_PROG_ERROR_DUPLICATE) { + msg->len += 2; + msg->msg[3] = ((duration_hr / 10) << 4) | (duration_hr % 10); + msg->msg[4] = ((duration_min / 10) << 4) | (duration_min % 10); + } +} + +static __inline__ void cec_ops_timer_status(struct cec_msg *msg, + __u8 *timer_overlap_warning, + __u8 *media_info, + __u8 *prog_info, + __u8 *prog_error, + __u8 *duration_hr, + __u8 *duration_min) +{ + *timer_overlap_warning = msg->msg[2] >> 7; + *media_info = (msg->msg[2] >> 5) & 3; + if (msg->msg[2] & 0x10) { + *prog_info = msg->msg[2] & 0xf; + *prog_error = 0; + } else { + *prog_info = 0; + *prog_error = msg->msg[2] & 0xf; + } + if (*prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE || + *prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE || + *prog_error == CEC_OP_PROG_ERROR_DUPLICATE) { + *duration_hr = (msg->msg[3] >> 4) * 10 + (msg->msg[3] & 0xf); + *duration_min = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); + } else { + *duration_hr = *duration_min = 0; + } +} + +static __inline__ void cec_msg_timer_cleared_status(struct cec_msg *msg, + __u8 timer_cleared_status) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_TIMER_CLEARED_STATUS; + msg->msg[2] = timer_cleared_status; +} + +static __inline__ void cec_ops_timer_cleared_status(struct cec_msg *msg, + __u8 *timer_cleared_status) +{ + *timer_cleared_status = msg->msg[2]; +} + +static __inline__ void cec_msg_clear_analogue_timer(struct cec_msg *msg, + bool reply, + __u8 day, + __u8 month, + __u8 start_hr, + __u8 start_min, + __u8 duration_hr, + __u8 duration_min, + __u8 recording_seq, + __u8 ana_bcast_type, + __u16 ana_freq, + __u8 bcast_system) +{ + msg->len = 13; + msg->msg[1] = CEC_MSG_CLEAR_ANALOGUE_TIMER; + msg->msg[2] = day; + msg->msg[3] = month; + /* Hours and minutes are in BCD format */ + msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); + msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); + msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); + msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); + msg->msg[8] = recording_seq; + msg->msg[9] = ana_bcast_type; + msg->msg[10] = ana_freq >> 8; + msg->msg[11] = ana_freq & 0xff; + msg->msg[12] = bcast_system; + msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0; +} + +static __inline__ void cec_ops_clear_analogue_timer(struct cec_msg *msg, + __u8 *day, + __u8 *month, + __u8 *start_hr, + __u8 *start_min, + __u8 *duration_hr, + __u8 *duration_min, + __u8 *recording_seq, + __u8 *ana_bcast_type, + __u16 *ana_freq, + __u8 *bcast_system) +{ + *day = msg->msg[2]; + *month = msg->msg[3]; + /* Hours and minutes are in BCD format */ + *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); + *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); + *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); + *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); + *recording_seq = msg->msg[8]; + *ana_bcast_type = msg->msg[9]; + *ana_freq = (msg->msg[10] << 8) | msg->msg[11]; + *bcast_system = msg->msg[12]; +} + +static __inline__ void cec_msg_clear_digital_timer(struct cec_msg *msg, + bool reply, + __u8 day, + __u8 month, + __u8 start_hr, + __u8 start_min, + __u8 duration_hr, + __u8 duration_min, + __u8 recording_seq, + const struct cec_op_digital_service_id *digital) +{ + msg->len = 16; + msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0; + msg->msg[1] = CEC_MSG_CLEAR_DIGITAL_TIMER; + msg->msg[2] = day; + msg->msg[3] = month; + /* Hours and minutes are in BCD format */ + msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); + msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); + msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); + msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); + msg->msg[8] = recording_seq; + cec_set_digital_service_id(msg->msg + 9, digital); +} + +static __inline__ void cec_ops_clear_digital_timer(struct cec_msg *msg, + __u8 *day, + __u8 *month, + __u8 *start_hr, + __u8 *start_min, + __u8 *duration_hr, + __u8 *duration_min, + __u8 *recording_seq, + struct cec_op_digital_service_id *digital) +{ + *day = msg->msg[2]; + *month = msg->msg[3]; + /* Hours and minutes are in BCD format */ + *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); + *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); + *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); + *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); + *recording_seq = msg->msg[8]; + cec_get_digital_service_id(msg->msg + 9, digital); +} + +static __inline__ void cec_msg_clear_ext_timer(struct cec_msg *msg, + bool reply, + __u8 day, + __u8 month, + __u8 start_hr, + __u8 start_min, + __u8 duration_hr, + __u8 duration_min, + __u8 recording_seq, + __u8 ext_src_spec, + __u8 plug, + __u16 phys_addr) +{ + msg->len = 13; + msg->msg[1] = CEC_MSG_CLEAR_EXT_TIMER; + msg->msg[2] = day; + msg->msg[3] = month; + /* Hours and minutes are in BCD format */ + msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); + msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); + msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); + msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); + msg->msg[8] = recording_seq; + msg->msg[9] = ext_src_spec; + msg->msg[10] = plug; + msg->msg[11] = phys_addr >> 8; + msg->msg[12] = phys_addr & 0xff; + msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0; +} + +static __inline__ void cec_ops_clear_ext_timer(struct cec_msg *msg, + __u8 *day, + __u8 *month, + __u8 *start_hr, + __u8 *start_min, + __u8 *duration_hr, + __u8 *duration_min, + __u8 *recording_seq, + __u8 *ext_src_spec, + __u8 *plug, + __u16 *phys_addr) +{ + *day = msg->msg[2]; + *month = msg->msg[3]; + /* Hours and minutes are in BCD format */ + *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); + *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); + *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); + *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); + *recording_seq = msg->msg[8]; + *ext_src_spec = msg->msg[9]; + *plug = msg->msg[10]; + *phys_addr = (msg->msg[11] << 8) | msg->msg[12]; +} + +static __inline__ void cec_msg_set_analogue_timer(struct cec_msg *msg, + bool reply, + __u8 day, + __u8 month, + __u8 start_hr, + __u8 start_min, + __u8 duration_hr, + __u8 duration_min, + __u8 recording_seq, + __u8 ana_bcast_type, + __u16 ana_freq, + __u8 bcast_system) +{ + msg->len = 13; + msg->msg[1] = CEC_MSG_SET_ANALOGUE_TIMER; + msg->msg[2] = day; + msg->msg[3] = month; + /* Hours and minutes are in BCD format */ + msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); + msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); + msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); + msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); + msg->msg[8] = recording_seq; + msg->msg[9] = ana_bcast_type; + msg->msg[10] = ana_freq >> 8; + msg->msg[11] = ana_freq & 0xff; + msg->msg[12] = bcast_system; + msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0; +} + +static __inline__ void cec_ops_set_analogue_timer(struct cec_msg *msg, + __u8 *day, + __u8 *month, + __u8 *start_hr, + __u8 *start_min, + __u8 *duration_hr, + __u8 *duration_min, + __u8 *recording_seq, + __u8 *ana_bcast_type, + __u16 *ana_freq, + __u8 *bcast_system) +{ + *day = msg->msg[2]; + *month = msg->msg[3]; + /* Hours and minutes are in BCD format */ + *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); + *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); + *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); + *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); + *recording_seq = msg->msg[8]; + *ana_bcast_type = msg->msg[9]; + *ana_freq = (msg->msg[10] << 8) | msg->msg[11]; + *bcast_system = msg->msg[12]; +} + +static __inline__ void cec_msg_set_digital_timer(struct cec_msg *msg, + bool reply, + __u8 day, + __u8 month, + __u8 start_hr, + __u8 start_min, + __u8 duration_hr, + __u8 duration_min, + __u8 recording_seq, + const struct cec_op_digital_service_id *digital) +{ + msg->len = 16; + msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0; + msg->msg[1] = CEC_MSG_SET_DIGITAL_TIMER; + msg->msg[2] = day; + msg->msg[3] = month; + /* Hours and minutes are in BCD format */ + msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); + msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); + msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); + msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); + msg->msg[8] = recording_seq; + cec_set_digital_service_id(msg->msg + 9, digital); +} + +static __inline__ void cec_ops_set_digital_timer(struct cec_msg *msg, + __u8 *day, + __u8 *month, + __u8 *start_hr, + __u8 *start_min, + __u8 *duration_hr, + __u8 *duration_min, + __u8 *recording_seq, + struct cec_op_digital_service_id *digital) +{ + *day = msg->msg[2]; + *month = msg->msg[3]; + /* Hours and minutes are in BCD format */ + *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); + *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); + *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); + *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); + *recording_seq = msg->msg[8]; + cec_get_digital_service_id(msg->msg + 9, digital); +} + +static __inline__ void cec_msg_set_ext_timer(struct cec_msg *msg, + bool reply, + __u8 day, + __u8 month, + __u8 start_hr, + __u8 start_min, + __u8 duration_hr, + __u8 duration_min, + __u8 recording_seq, + __u8 ext_src_spec, + __u8 plug, + __u16 phys_addr) +{ + msg->len = 13; + msg->msg[1] = CEC_MSG_SET_EXT_TIMER; + msg->msg[2] = day; + msg->msg[3] = month; + /* Hours and minutes are in BCD format */ + msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10); + msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10); + msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10); + msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10); + msg->msg[8] = recording_seq; + msg->msg[9] = ext_src_spec; + msg->msg[10] = plug; + msg->msg[11] = phys_addr >> 8; + msg->msg[12] = phys_addr & 0xff; + msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0; +} + +static __inline__ void cec_ops_set_ext_timer(struct cec_msg *msg, + __u8 *day, + __u8 *month, + __u8 *start_hr, + __u8 *start_min, + __u8 *duration_hr, + __u8 *duration_min, + __u8 *recording_seq, + __u8 *ext_src_spec, + __u8 *plug, + __u16 *phys_addr) +{ + *day = msg->msg[2]; + *month = msg->msg[3]; + /* Hours and minutes are in BCD format */ + *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf); + *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf); + *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf); + *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf); + *recording_seq = msg->msg[8]; + *ext_src_spec = msg->msg[9]; + *plug = msg->msg[10]; + *phys_addr = (msg->msg[11] << 8) | msg->msg[12]; +} + +static __inline__ void cec_msg_set_timer_program_title(struct cec_msg *msg, + const char *prog_title) +{ + unsigned len = strlen(prog_title); + + if (len > 14) + len = 14; + msg->len = 2 + len; + msg->msg[1] = CEC_MSG_SET_TIMER_PROGRAM_TITLE; + memcpy(msg->msg + 2, prog_title, len); +} + +static __inline__ void cec_ops_set_timer_program_title(const struct cec_msg *msg, + char *prog_title) +{ + unsigned len = msg->len - 2; + + if (len > 14) + len = 14; + memcpy(prog_title, msg->msg + 2, len); + prog_title[len] = '\0'; +} + +/* System Information Feature */ +static __inline__ void cec_msg_cec_version(struct cec_msg *msg, __u8 cec_version) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_CEC_VERSION; + msg->msg[2] = cec_version; +} + +static __inline__ void cec_ops_cec_version(const struct cec_msg *msg, + __u8 *cec_version) +{ + *cec_version = msg->msg[2]; +} + +static __inline__ void cec_msg_get_cec_version(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GET_CEC_VERSION; + msg->reply = reply ? CEC_MSG_CEC_VERSION : 0; +} + +static __inline__ void cec_msg_report_physical_addr(struct cec_msg *msg, + __u16 phys_addr, __u8 prim_devtype) +{ + msg->len = 5; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_REPORT_PHYSICAL_ADDR; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; + msg->msg[4] = prim_devtype; +} + +static __inline__ void cec_ops_report_physical_addr(const struct cec_msg *msg, + __u16 *phys_addr, __u8 *prim_devtype) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *prim_devtype = msg->msg[4]; +} + +static __inline__ void cec_msg_give_physical_addr(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GIVE_PHYSICAL_ADDR; + msg->reply = reply ? CEC_MSG_REPORT_PHYSICAL_ADDR : 0; +} + +static __inline__ void cec_msg_set_menu_language(struct cec_msg *msg, + const char *language) +{ + msg->len = 5; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_SET_MENU_LANGUAGE; + memcpy(msg->msg + 2, language, 3); +} + +static __inline__ void cec_ops_set_menu_language(struct cec_msg *msg, + char *language) +{ + memcpy(language, msg->msg + 2, 3); +} + +static __inline__ void cec_msg_get_menu_language(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GET_MENU_LANGUAGE; + msg->reply = reply ? CEC_MSG_SET_MENU_LANGUAGE : 0; +} + +/* + * Assumes a single RC Profile byte and a single Device Features byte, + * i.e. no extended features are supported by this helper function. + */ +static __inline__ void cec_msg_report_features(struct cec_msg *msg, + __u8 cec_version, __u8 all_device_types, + __u8 rc_profile, __u8 dev_features) +{ + msg->len = 6; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_REPORT_FEATURES; + msg->msg[2] = cec_version; + msg->msg[3] = all_device_types; + msg->msg[4] = rc_profile; + msg->msg[5] = dev_features; +} + +static __inline__ void cec_ops_report_features(const struct cec_msg *msg, + __u8 *cec_version, __u8 *all_device_types, + const __u8 **rc_profile, const __u8 **dev_features) +{ + const __u8 *p = &msg->msg[4]; + + *cec_version = msg->msg[2]; + *all_device_types = msg->msg[3]; + *rc_profile = p; + while (p < &msg->msg[14] && (*p & CEC_OP_FEAT_EXT)) + p++; + if (!(*p & CEC_OP_FEAT_EXT)) { + *dev_features = p + 1; + while (p < &msg->msg[15] && (*p & CEC_OP_FEAT_EXT)) + p++; + } + if (*p & CEC_OP_FEAT_EXT) + *rc_profile = *dev_features = NULL; +} + +static __inline__ void cec_msg_give_features(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GIVE_FEATURES; + msg->reply = reply ? CEC_MSG_REPORT_FEATURES : 0; +} + +/* Deck Control Feature */ +static __inline__ void cec_msg_deck_control(struct cec_msg *msg, + __u8 deck_control_mode) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_DECK_CONTROL; + msg->msg[2] = deck_control_mode; +} + +static __inline__ void cec_ops_deck_control(struct cec_msg *msg, + __u8 *deck_control_mode) +{ + *deck_control_mode = msg->msg[2]; +} + +static __inline__ void cec_msg_deck_status(struct cec_msg *msg, + __u8 deck_info) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_DECK_STATUS; + msg->msg[2] = deck_info; +} + +static __inline__ void cec_ops_deck_status(struct cec_msg *msg, + __u8 *deck_info) +{ + *deck_info = msg->msg[2]; +} + +static __inline__ void cec_msg_give_deck_status(struct cec_msg *msg, + bool reply, + __u8 status_req) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_GIVE_DECK_STATUS; + msg->msg[2] = status_req; + msg->reply = reply ? CEC_MSG_DECK_STATUS : 0; +} + +static __inline__ void cec_ops_give_deck_status(struct cec_msg *msg, + __u8 *status_req) +{ + *status_req = msg->msg[2]; +} + +static __inline__ void cec_msg_play(struct cec_msg *msg, + __u8 play_mode) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_PLAY; + msg->msg[2] = play_mode; +} + +static __inline__ void cec_ops_play(struct cec_msg *msg, + __u8 *play_mode) +{ + *play_mode = msg->msg[2]; +} + + +/* Tuner Control Feature */ +struct cec_op_tuner_device_info { + __u8 rec_flag; + __u8 tuner_display_info; + bool is_analog; + union { + struct cec_op_digital_service_id digital; + struct { + __u8 ana_bcast_type; + __u16 ana_freq; + __u8 bcast_system; + } analog; + }; +}; + +static __inline__ void cec_msg_tuner_device_status_analog(struct cec_msg *msg, + __u8 rec_flag, + __u8 tuner_display_info, + __u8 ana_bcast_type, + __u16 ana_freq, + __u8 bcast_system) +{ + msg->len = 7; + msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS; + msg->msg[2] = (rec_flag << 7) | tuner_display_info; + msg->msg[3] = ana_bcast_type; + msg->msg[4] = ana_freq >> 8; + msg->msg[5] = ana_freq & 0xff; + msg->msg[6] = bcast_system; +} + +static __inline__ void cec_msg_tuner_device_status_digital(struct cec_msg *msg, + __u8 rec_flag, __u8 tuner_display_info, + const struct cec_op_digital_service_id *digital) +{ + msg->len = 10; + msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS; + msg->msg[2] = (rec_flag << 7) | tuner_display_info; + cec_set_digital_service_id(msg->msg + 3, digital); +} + +static __inline__ void cec_msg_tuner_device_status(struct cec_msg *msg, + const struct cec_op_tuner_device_info *tuner_dev_info) +{ + if (tuner_dev_info->is_analog) + cec_msg_tuner_device_status_analog(msg, + tuner_dev_info->rec_flag, + tuner_dev_info->tuner_display_info, + tuner_dev_info->analog.ana_bcast_type, + tuner_dev_info->analog.ana_freq, + tuner_dev_info->analog.bcast_system); + else + cec_msg_tuner_device_status_digital(msg, + tuner_dev_info->rec_flag, + tuner_dev_info->tuner_display_info, + &tuner_dev_info->digital); +} + +static __inline__ void cec_ops_tuner_device_status(struct cec_msg *msg, + struct cec_op_tuner_device_info *tuner_dev_info) +{ + tuner_dev_info->is_analog = msg->len < 10; + tuner_dev_info->rec_flag = msg->msg[2] >> 7; + tuner_dev_info->tuner_display_info = msg->msg[2] & 0x7f; + if (tuner_dev_info->is_analog) { + tuner_dev_info->analog.ana_bcast_type = msg->msg[3]; + tuner_dev_info->analog.ana_freq = (msg->msg[4] << 8) | msg->msg[5]; + tuner_dev_info->analog.bcast_system = msg->msg[6]; + return; + } + cec_get_digital_service_id(msg->msg + 3, &tuner_dev_info->digital); +} + +static __inline__ void cec_msg_give_tuner_device_status(struct cec_msg *msg, + bool reply, + __u8 status_req) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_GIVE_TUNER_DEVICE_STATUS; + msg->msg[2] = status_req; + msg->reply = reply ? CEC_MSG_TUNER_DEVICE_STATUS : 0; +} + +static __inline__ void cec_ops_give_tuner_device_status(struct cec_msg *msg, + __u8 *status_req) +{ + *status_req = msg->msg[2]; +} + +static __inline__ void cec_msg_select_analogue_service(struct cec_msg *msg, + __u8 ana_bcast_type, + __u16 ana_freq, + __u8 bcast_system) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_SELECT_ANALOGUE_SERVICE; + msg->msg[2] = ana_bcast_type; + msg->msg[3] = ana_freq >> 8; + msg->msg[4] = ana_freq & 0xff; + msg->msg[5] = bcast_system; +} + +static __inline__ void cec_ops_select_analogue_service(struct cec_msg *msg, + __u8 *ana_bcast_type, + __u16 *ana_freq, + __u8 *bcast_system) +{ + *ana_bcast_type = msg->msg[2]; + *ana_freq = (msg->msg[3] << 8) | msg->msg[4]; + *bcast_system = msg->msg[5]; +} + +static __inline__ void cec_msg_select_digital_service(struct cec_msg *msg, + const struct cec_op_digital_service_id *digital) +{ + msg->len = 9; + msg->msg[1] = CEC_MSG_SELECT_DIGITAL_SERVICE; + cec_set_digital_service_id(msg->msg + 2, digital); +} + +static __inline__ void cec_ops_select_digital_service(struct cec_msg *msg, + struct cec_op_digital_service_id *digital) +{ + cec_get_digital_service_id(msg->msg + 2, digital); +} + +static __inline__ void cec_msg_tuner_step_decrement(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_TUNER_STEP_DECREMENT; +} + +static __inline__ void cec_msg_tuner_step_increment(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_TUNER_STEP_INCREMENT; +} + + +/* Vendor Specific Commands Feature */ +static __inline__ void cec_msg_device_vendor_id(struct cec_msg *msg, __u32 vendor_id) +{ + msg->len = 5; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_DEVICE_VENDOR_ID; + msg->msg[2] = vendor_id >> 16; + msg->msg[3] = (vendor_id >> 8) & 0xff; + msg->msg[4] = vendor_id & 0xff; +} + +static __inline__ void cec_ops_device_vendor_id(const struct cec_msg *msg, + __u32 *vendor_id) +{ + *vendor_id = (msg->msg[2] << 16) | (msg->msg[3] << 8) | msg->msg[4]; +} + +static __inline__ void cec_msg_give_device_vendor_id(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GIVE_DEVICE_VENDOR_ID; + msg->reply = reply ? CEC_MSG_DEVICE_VENDOR_ID : 0; +} + +static __inline__ void cec_msg_vendor_remote_button_up(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_VENDOR_REMOTE_BUTTON_UP; +} + + +/* OSD Display Feature */ +static __inline__ void cec_msg_set_osd_string(struct cec_msg *msg, + __u8 disp_ctl, + const char *osd) +{ + unsigned len = strlen(osd); + + if (len > 13) + len = 13; + msg->len = 3 + len; + msg->msg[1] = CEC_MSG_SET_OSD_STRING; + msg->msg[2] = disp_ctl; + memcpy(msg->msg + 3, osd, len); +} + +static __inline__ void cec_ops_set_osd_string(const struct cec_msg *msg, + __u8 *disp_ctl, + char *osd) +{ + unsigned len = msg->len - 3; + + *disp_ctl = msg->msg[2]; + if (len > 13) + len = 13; + memcpy(osd, msg->msg + 3, len); + osd[len] = '\0'; +} + + +/* Device OSD Transfer Feature */ +static __inline__ void cec_msg_set_osd_name(struct cec_msg *msg, const char *name) +{ + unsigned len = strlen(name); + + if (len > 14) + len = 14; + msg->len = 2 + len; + msg->msg[1] = CEC_MSG_SET_OSD_NAME; + memcpy(msg->msg + 2, name, len); +} + +static __inline__ void cec_ops_set_osd_name(const struct cec_msg *msg, + char *name) +{ + unsigned len = msg->len - 2; + + if (len > 14) + len = 14; + memcpy(name, msg->msg + 2, len); + name[len] = '\0'; +} + +static __inline__ void cec_msg_give_osd_name(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GIVE_OSD_NAME; + msg->reply = reply ? CEC_MSG_SET_OSD_NAME : 0; +} + + +/* Device Menu Control Feature */ +static __inline__ void cec_msg_menu_status(struct cec_msg *msg, + __u8 menu_state) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_MENU_STATUS; + msg->msg[2] = menu_state; +} + +static __inline__ void cec_ops_menu_status(struct cec_msg *msg, + __u8 *menu_state) +{ + *menu_state = msg->msg[2]; +} + +static __inline__ void cec_msg_menu_request(struct cec_msg *msg, + bool reply, + __u8 menu_req) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_MENU_REQUEST; + msg->msg[2] = menu_req; + msg->reply = reply ? CEC_MSG_MENU_STATUS : 0; +} + +static __inline__ void cec_ops_menu_request(struct cec_msg *msg, + __u8 *menu_req) +{ + *menu_req = msg->msg[2]; +} + +static __inline__ void cec_msg_user_control_pressed(struct cec_msg *msg, + __u8 ui_cmd) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_USER_CONTROL_PRESSED; + msg->msg[2] = ui_cmd; +} + +static __inline__ void cec_ops_user_control_pressed(struct cec_msg *msg, + __u8 *ui_cmd) +{ + *ui_cmd = msg->msg[2]; +} + +static __inline__ void cec_msg_user_control_released(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_USER_CONTROL_RELEASED; +} + +/* Remote Control Passthrough Feature */ + +/* Power Status Feature */ +static __inline__ void cec_msg_report_power_status(struct cec_msg *msg, + __u8 pwr_state) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_REPORT_POWER_STATUS; + msg->msg[2] = pwr_state; +} + +static __inline__ void cec_ops_report_power_status(const struct cec_msg *msg, + __u8 *pwr_state) +{ + *pwr_state = msg->msg[2]; +} + +static __inline__ void cec_msg_give_device_power_status(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GIVE_DEVICE_POWER_STATUS; + msg->reply = reply ? CEC_MSG_REPORT_POWER_STATUS : 0; +} + +/* General Protocol Messages */ +static __inline__ void cec_msg_feature_abort(struct cec_msg *msg, + __u8 abort_msg, __u8 reason) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_FEATURE_ABORT; + msg->msg[2] = abort_msg; + msg->msg[3] = reason; +} + +static __inline__ void cec_ops_feature_abort(const struct cec_msg *msg, + __u8 *abort_msg, __u8 *reason) +{ + *abort_msg = msg->msg[2]; + *reason = msg->msg[3]; +} + +/* This changes the current message into an abort message */ +static __inline__ void cec_msg_reply_abort(struct cec_msg *msg, __u8 reason) +{ + cec_msg_set_reply_to(msg, msg); + msg->len = 4; + msg->msg[2] = msg->msg[1]; + msg->msg[3] = reason; + msg->msg[1] = CEC_MSG_FEATURE_ABORT; +} + +static __inline__ void cec_msg_abort(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_ABORT; +} + + +/* System Audio Control Feature */ +static __inline__ void cec_msg_report_audio_status(struct cec_msg *msg, + __u8 aud_mute_status, + __u8 aud_vol_status) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_REPORT_AUDIO_STATUS; + msg->msg[2] = (aud_mute_status << 7) | (aud_vol_status & 0x7f); +} + +static __inline__ void cec_ops_report_audio_status(const struct cec_msg *msg, + __u8 *aud_mute_status, + __u8 *aud_vol_status) +{ + *aud_mute_status = msg->msg[2] >> 7; + *aud_vol_status = msg->msg[2] & 0x7f; +} + +static __inline__ void cec_msg_give_audio_status(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GIVE_AUDIO_STATUS; + msg->reply = reply ? CEC_MSG_REPORT_AUDIO_STATUS : 0; +} + +static __inline__ void cec_msg_set_system_audio_mode(struct cec_msg *msg, + __u8 sys_aud_status) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_SET_SYSTEM_AUDIO_MODE; + msg->msg[2] = sys_aud_status; +} + +static __inline__ void cec_ops_set_system_audio_mode(const struct cec_msg *msg, + __u8 *sys_aud_status) +{ + *sys_aud_status = msg->msg[2]; +} + +static __inline__ void cec_msg_system_audio_mode_request(struct cec_msg *msg, + bool reply, + __u16 phys_addr) +{ + msg->len = phys_addr == 0xffff ? 2 : 4; + msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; + msg->reply = reply ? CEC_MSG_SET_SYSTEM_AUDIO_MODE : 0; + +} + +static __inline__ void cec_ops_system_audio_mode_request(const struct cec_msg *msg, + __u16 *phys_addr) +{ + if (msg->len < 4) + *phys_addr = 0xffff; + else + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static __inline__ void cec_msg_system_audio_mode_status(struct cec_msg *msg, + __u8 sys_aud_status) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_STATUS; + msg->msg[2] = sys_aud_status; +} + +static __inline__ void cec_ops_system_audio_mode_status(const struct cec_msg *msg, + __u8 *sys_aud_status) +{ + *sys_aud_status = msg->msg[2]; +} + +static __inline__ void cec_msg_give_system_audio_mode_status(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS; + msg->reply = reply ? CEC_MSG_SYSTEM_AUDIO_MODE_STATUS : 0; +} + +static __inline__ void cec_msg_report_short_audio_descriptor(struct cec_msg *msg, + __u8 num_descriptors, + const __u32 *descriptors) +{ + unsigned i; + + msg->len = 2 + num_descriptors * 3; + msg->msg[1] = CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR; + for (i = 0; i < num_descriptors; i++) { + msg->msg[2 + i * 3] = (descriptors[i] >> 16) & 0xff; + msg->msg[3 + i * 3] = (descriptors[i] >> 8) & 0xff; + msg->msg[4 + i * 3] = descriptors[i] & 0xff; + } +} + +static __inline__ void cec_ops_report_short_audio_descriptor(const struct cec_msg *msg, + __u8 *num_descriptors, + __u32 *descriptors) +{ + unsigned i; + + *num_descriptors = (msg->len - 2) / 3; + for (i = 0; i < *num_descriptors; i++) + descriptors[i] = (msg->msg[2 + i * 3] << 16) | + (msg->msg[3 + i * 3] << 8) | + msg->msg[4 + i * 3]; +} + +static __inline__ void cec_msg_request_short_audio_descriptor(struct cec_msg *msg, + __u8 audio_format_id, + __u8 audio_format_code) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR; + msg->msg[2] = (audio_format_id << 6) | (audio_format_code & 0x3f); +} + +static __inline__ void cec_ops_request_short_audio_descriptor(const struct cec_msg *msg, + __u8 *audio_format_id, + __u8 *audio_format_code) +{ + *audio_format_id = msg->msg[2] >> 6; + *audio_format_code = msg->msg[2] & 0x3f; +} + + +/* Audio Rate Control Feature */ +static __inline__ void cec_msg_set_audio_rate(struct cec_msg *msg, + __u8 audio_rate) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_SET_AUDIO_RATE; + msg->msg[2] = audio_rate; +} + +static __inline__ void cec_ops_set_audio_rate(const struct cec_msg *msg, + __u8 *audio_rate) +{ + *audio_rate = msg->msg[2]; +} + + +/* Audio Return Channel Control Feature */ +static __inline__ void cec_msg_report_arc_initiated(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_REPORT_ARC_INITIATED; +} + +static __inline__ void cec_msg_initiate_arc(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_INITIATE_ARC; + msg->reply = reply ? CEC_MSG_REPORT_ARC_INITIATED : 0; +} + +static __inline__ void cec_msg_request_arc_initiation(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_REQUEST_ARC_INITIATION; + msg->reply = reply ? CEC_MSG_INITIATE_ARC : 0; +} + +static __inline__ void cec_msg_report_arc_terminated(struct cec_msg *msg) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_REPORT_ARC_TERMINATED; +} + +static __inline__ void cec_msg_terminate_arc(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_TERMINATE_ARC; + msg->reply = reply ? CEC_MSG_REPORT_ARC_TERMINATED : 0; +} + +static __inline__ void cec_msg_request_arc_termination(struct cec_msg *msg, + bool reply) +{ + msg->len = 2; + msg->msg[1] = CEC_MSG_REQUEST_ARC_TERMINATION; + msg->reply = reply ? CEC_MSG_TERMINATE_ARC : 0; +} + + +/* Dynamic Audio Lipsync Feature */ +/* Only for CEC 2.0 and up */ +static __inline__ void cec_msg_report_current_latency(struct cec_msg *msg, + __u16 phys_addr, + __u8 video_latency, + __u8 low_latency_mode, + __u8 audio_out_compensated, + __u8 audio_out_delay) +{ + msg->len = 7; + msg->msg[1] = CEC_MSG_REPORT_CURRENT_LATENCY; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; + msg->msg[4] = video_latency; + msg->msg[5] = (low_latency_mode << 2) | audio_out_compensated; + msg->msg[6] = audio_out_delay; +} + +static __inline__ void cec_ops_report_current_latency(const struct cec_msg *msg, + __u16 *phys_addr, + __u8 *video_latency, + __u8 *low_latency_mode, + __u8 *audio_out_compensated, + __u8 *audio_out_delay) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *video_latency = msg->msg[4]; + *low_latency_mode = (msg->msg[5] >> 2) & 1; + *audio_out_compensated = msg->msg[5] & 3; + *audio_out_delay = msg->msg[6]; +} + +static __inline__ void cec_msg_request_current_latency(struct cec_msg *msg, + bool reply, + __u16 phys_addr) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REQUEST_CURRENT_LATENCY; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; + msg->reply = reply ? CEC_MSG_REPORT_CURRENT_LATENCY : 0; +} + +static __inline__ void cec_ops_request_current_latency(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + + +/* Capability Discovery and Control Feature */ +static __inline__ void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg, + __u16 phys_addr1, + __u16 phys_addr2) +{ + msg->len = 9; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE; + msg->msg[5] = phys_addr1 >> 8; + msg->msg[6] = phys_addr1 & 0xff; + msg->msg[7] = phys_addr2 >> 8; + msg->msg[8] = phys_addr2 & 0xff; +} + +static __inline__ void cec_ops_cdc_hec_inquire_state(const struct cec_msg *msg, + __u16 *phys_addr, + __u16 *phys_addr1, + __u16 *phys_addr2) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6]; + *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8]; +} + +static __inline__ void cec_msg_cdc_hec_report_state(struct cec_msg *msg, + __u16 target_phys_addr, + __u8 hec_func_state, + __u8 host_func_state, + __u8 enc_func_state, + __u8 cdc_errcode, + __u8 has_field, + __u16 hec_field) +{ + msg->len = has_field ? 10 : 8; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HEC_REPORT_STATE; + msg->msg[5] = target_phys_addr >> 8; + msg->msg[6] = target_phys_addr & 0xff; + msg->msg[7] = (hec_func_state << 6) | + (host_func_state << 4) | + (enc_func_state << 2) | + cdc_errcode; + if (has_field) { + msg->msg[8] = hec_field >> 8; + msg->msg[9] = hec_field & 0xff; + } +} + +static __inline__ void cec_ops_cdc_hec_report_state(const struct cec_msg *msg, + __u16 *phys_addr, + __u16 *target_phys_addr, + __u8 *hec_func_state, + __u8 *host_func_state, + __u8 *enc_func_state, + __u8 *cdc_errcode, + __u8 *has_field, + __u16 *hec_field) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *target_phys_addr = (msg->msg[5] << 8) | msg->msg[6]; + *hec_func_state = msg->msg[7] >> 6; + *host_func_state = (msg->msg[7] >> 4) & 3; + *enc_func_state = (msg->msg[7] >> 4) & 3; + *cdc_errcode = msg->msg[7] & 3; + *has_field = msg->len >= 10; + *hec_field = *has_field ? ((msg->msg[8] << 8) | msg->msg[9]) : 0; +} + +static __inline__ void cec_msg_cdc_hec_set_state(struct cec_msg *msg, + __u16 phys_addr1, + __u16 phys_addr2, + __u8 hec_set_state, + __u16 phys_addr3, + __u16 phys_addr4, + __u16 phys_addr5) +{ + msg->len = 10; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE; + msg->msg[5] = phys_addr1 >> 8; + msg->msg[6] = phys_addr1 & 0xff; + msg->msg[7] = phys_addr2 >> 8; + msg->msg[8] = phys_addr2 & 0xff; + msg->msg[9] = hec_set_state; + if (phys_addr3 != CEC_PHYS_ADDR_INVALID) { + msg->msg[msg->len++] = phys_addr3 >> 8; + msg->msg[msg->len++] = phys_addr3 & 0xff; + if (phys_addr4 != CEC_PHYS_ADDR_INVALID) { + msg->msg[msg->len++] = phys_addr4 >> 8; + msg->msg[msg->len++] = phys_addr4 & 0xff; + if (phys_addr5 != CEC_PHYS_ADDR_INVALID) { + msg->msg[msg->len++] = phys_addr5 >> 8; + msg->msg[msg->len++] = phys_addr5 & 0xff; + } + } + } +} + +static __inline__ void cec_ops_cdc_hec_set_state(const struct cec_msg *msg, + __u16 *phys_addr, + __u16 *phys_addr1, + __u16 *phys_addr2, + __u8 *hec_set_state, + __u16 *phys_addr3, + __u16 *phys_addr4, + __u16 *phys_addr5) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6]; + *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8]; + *hec_set_state = msg->msg[9]; + *phys_addr3 = *phys_addr4 = *phys_addr5 = CEC_PHYS_ADDR_INVALID; + if (msg->len >= 12) + *phys_addr3 = (msg->msg[10] << 8) | msg->msg[11]; + if (msg->len >= 14) + *phys_addr4 = (msg->msg[12] << 8) | msg->msg[13]; + if (msg->len >= 16) + *phys_addr5 = (msg->msg[14] << 8) | msg->msg[15]; +} + +static __inline__ void cec_msg_cdc_hec_set_state_adjacent(struct cec_msg *msg, + __u16 phys_addr1, + __u8 hec_set_state) +{ + msg->len = 8; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HEC_SET_STATE_ADJACENT; + msg->msg[5] = phys_addr1 >> 8; + msg->msg[6] = phys_addr1 & 0xff; + msg->msg[7] = hec_set_state; +} + +static __inline__ void cec_ops_cdc_hec_set_state_adjacent(const struct cec_msg *msg, + __u16 *phys_addr, + __u16 *phys_addr1, + __u8 *hec_set_state) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6]; + *hec_set_state = msg->msg[7]; +} + +static __inline__ void cec_msg_cdc_hec_request_deactivation(struct cec_msg *msg, + __u16 phys_addr1, + __u16 phys_addr2, + __u16 phys_addr3) +{ + msg->len = 11; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION; + msg->msg[5] = phys_addr1 >> 8; + msg->msg[6] = phys_addr1 & 0xff; + msg->msg[7] = phys_addr2 >> 8; + msg->msg[8] = phys_addr2 & 0xff; + msg->msg[9] = phys_addr3 >> 8; + msg->msg[10] = phys_addr3 & 0xff; +} + +static __inline__ void cec_ops_cdc_hec_request_deactivation(const struct cec_msg *msg, + __u16 *phys_addr, + __u16 *phys_addr1, + __u16 *phys_addr2, + __u16 *phys_addr3) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6]; + *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8]; + *phys_addr3 = (msg->msg[9] << 8) | msg->msg[10]; +} + +static __inline__ void cec_msg_cdc_hec_notify_alive(struct cec_msg *msg) +{ + msg->len = 5; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HEC_NOTIFY_ALIVE; +} + +static __inline__ void cec_ops_cdc_hec_notify_alive(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static __inline__ void cec_msg_cdc_hec_discover(struct cec_msg *msg) +{ + msg->len = 5; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HEC_DISCOVER; +} + +static __inline__ void cec_ops_cdc_hec_discover(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static __inline__ void cec_msg_cdc_hpd_set_state(struct cec_msg *msg, + __u8 input_port, + __u8 hpd_state) +{ + msg->len = 6; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HPD_SET_STATE; + msg->msg[5] = (input_port << 4) | hpd_state; +} + +static __inline__ void cec_ops_cdc_hpd_set_state(const struct cec_msg *msg, + __u16 *phys_addr, + __u8 *input_port, + __u8 *hpd_state) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *input_port = msg->msg[5] >> 4; + *hpd_state = msg->msg[5] & 0xf; +} + +static __inline__ void cec_msg_cdc_hpd_report_state(struct cec_msg *msg, + __u8 hpd_state, + __u8 hpd_error) +{ + msg->len = 6; + msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[1] = CEC_MSG_CDC_MESSAGE; + /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ + msg->msg[4] = CEC_MSG_CDC_HPD_REPORT_STATE; + msg->msg[5] = (hpd_state << 4) | hpd_error; +} + +static __inline__ void cec_ops_cdc_hpd_report_state(const struct cec_msg *msg, + __u16 *phys_addr, + __u8 *hpd_state, + __u8 *hpd_error) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; + *hpd_state = msg->msg[5] >> 4; + *hpd_error = msg->msg[5] & 0xf; +} + +#endif diff --git a/include/linux/cec.h b/include/linux/cec.h new file mode 100644 index 0000000..e02c21d --- /dev/null +++ b/include/linux/cec.h @@ -0,0 +1,781 @@ +#ifndef _CEC_H +#define _CEC_H + +#include <linux/types.h> + +struct cec_msg { + __u64 ts; + __u32 len; + __u32 status; + /* + * timeout (in ms) is used to timeout CEC_RECEIVE. + * Set to 0 if you want to wait forever. + */ + __u32 timeout; + /* + * The framework assigns a sequence number to messages that are sent. + * This can be used to track replies to previously sent messages. + */ + __u32 sequence; + __u8 msg[16]; + /* + * If non-zero, then wait for a reply with this opcode. + * If there was an error when sending the msg or FeatureAbort + * was returned, then reply is set to 0. + * If reply is non-zero upon return, then len/msg are set to + * the received message. + * If reply is zero upon return and status has the + * CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to the + * received feature abort message. + * If reply is zero upon return and status has the + * CEC_TX_STATUS_REPLY_TIMEOUT + * bit set, then no reply was seen at all. + * This field is ignored with CEC_RECEIVE. + * If reply is non-zero for CEC_TRANSMIT and the message is a broadcast, + * then -EINVAL is returned. + * if reply is non-zero, then timeout is set to 1000 (the required + * maximum response time). + */ + __u8 reply; + __u8 reserved[35]; +}; + +static __inline__ __u8 cec_msg_initiator(const struct cec_msg *msg) +{ + return msg->msg[0] >> 4; +} + +static __inline__ __u8 cec_msg_destination(const struct cec_msg *msg) +{ + return msg->msg[0] & 0xf; +} + +static __inline__ bool cec_msg_is_broadcast(const struct cec_msg *msg) +{ + return (msg->msg[0] & 0xf) == 0xf; +} + +static __inline__ void cec_msg_init(struct cec_msg *msg, + __u8 initiator, __u8 destination) +{ + memset(msg, 0, sizeof(*msg)); + msg->msg[0] = (initiator << 4) | destination; + msg->len = 1; +} + +/* + * Set the msg destination to the orig initiator and the msg initiator to the + * orig destination. Note that msg and orig may be the same pointer, in which + * case the change is done in place. + */ +static __inline__ void cec_msg_set_reply_to(struct cec_msg *msg, struct cec_msg *orig) +{ + /* The destination becomes the initiator and vice versa */ + msg->msg[0] = (cec_msg_destination(orig) << 4) | cec_msg_initiator(orig); +} + +/* cec status field */ +#define CEC_TX_STATUS_OK (0) +#define CEC_TX_STATUS_ARB_LOST (1 << 0) +#define CEC_TX_STATUS_RETRY_TIMEOUT (1 << 1) +#define CEC_TX_STATUS_FEATURE_ABORT (1 << 2) +#define CEC_TX_STATUS_REPLY_TIMEOUT (1 << 3) +#define CEC_RX_STATUS_READY (0) + +#define CEC_LOG_ADDR_INVALID 0xff +#define CEC_PHYS_ADDR_INVALID 0xffff + +/* The maximum number of logical addresses one device can be assigned to. + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */ +#define CEC_MAX_LOG_ADDRS 4 + +/* The logical address types that the CEC device wants to claim */ +#define CEC_LOG_ADDR_TYPE_TV 0 +#define CEC_LOG_ADDR_TYPE_RECORD 1 +#define CEC_LOG_ADDR_TYPE_TUNER 2 +#define CEC_LOG_ADDR_TYPE_PLAYBACK 3 +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM 4 +#define CEC_LOG_ADDR_TYPE_SPECIFIC 5 +#define CEC_LOG_ADDR_TYPE_UNREGISTERED 6 +/* Switches should use UNREGISTERED. + * Processors should use SPECIFIC. */ + +/* + * Use this if there is no vendor ID (CEC_G_VENDOR_ID) or if the vendor ID + * should be disabled (CEC_S_VENDOR_ID) + */ +#define CEC_VENDOR_ID_NONE 0xffffffff + +/* The CEC adapter state */ +#define CEC_ADAP_DISABLED 0 +#define CEC_ADAP_ENABLED 1 + +/* The passthrough mode state */ +#define CEC_PASSTHROUGH_DISABLED 0 +#define CEC_PASSTHROUGH_ENABLED 1 + +/* The monitor state */ +#define CEC_MONITOR_DISABLED 0 +#define CEC_MONITOR_ENABLED 1 + +/* Userspace has to configure the adapter state (enable/disable) */ +#define CEC_CAP_STATE (1 << 0) +/* Userspace has to configure the physical address */ +#define CEC_CAP_PHYS_ADDR (1 << 1) +/* Userspace has to configure the logical addresses */ +#define CEC_CAP_LOG_ADDRS (1 << 2) +/* Userspace can transmit and receive messages */ +#define CEC_CAP_IO (1 << 3) +/* Userspace has to configure the vendor id */ +#define CEC_CAP_VENDOR_ID (1 << 4) +/* + * Passthrough all messages instead of processing them. + * Note: ARC and CDC messages are always processed. + */ +#define CEC_CAP_PASSTHROUGH (1 << 5) +/* Supports remote control */ +#define CEC_CAP_RC (1 << 6) +/* Supports ARC */ +#define CEC_CAP_ARC (1 << 7) +/* Supports CDC HPD */ +#define CEC_CAP_CDC_HPD (1 << 8) +/* Is a source */ +#define CEC_CAP_IS_SOURCE (1 << 9) + +struct cec_caps { + __u32 available_log_addrs; + __u32 capabilities; + __u8 ninputs; + __u8 reserved[39]; +}; + +struct cec_log_addrs { + __u8 cec_version; + __u8 num_log_addrs; + __u8 primary_device_type[CEC_MAX_LOG_ADDRS]; + __u8 log_addr_type[CEC_MAX_LOG_ADDRS]; + __u8 log_addr[CEC_MAX_LOG_ADDRS]; + char osd_name[15]; + + /* CEC 2.0 */ + __u8 all_device_types[CEC_MAX_LOG_ADDRS]; + __u8 features[CEC_MAX_LOG_ADDRS][12]; + + __u8 reserved[63]; +}; + +/* Events */ + +/* Event that occurs when the adapter state changes */ +#define CEC_EVENT_STATE_CHANGE 1 +/* Event that occurs when inputs are connected/disconnected */ +#define CEC_EVENT_INPUTS_CHANGE 2 +/* This event is sent when messages are lost because the application + * didn't empty the message queue in time */ +#define CEC_EVENT_LOST_MSGS 3 + +#define CEC_EVENT_STATE_DISABLED 0 +#define CEC_EVENT_STATE_UNCONFIGURED 1 +#define CEC_EVENT_STATE_CONFIGURING 2 +#define CEC_EVENT_STATE_CONFIGURED 3 + +struct cec_event_state_change { + /* current CEC adapter state */ + __u8 state; +}; + +struct cec_event_inputs_change { + /* bit 0 == input port 0, bit 15 == input port 15 */ + /* currently connected input ports */ + __u16 connected_inputs; + /* input ports that changed state since last event */ + __u16 changed_inputs; +}; + +struct cec_event { + __u64 ts; + __u32 event; + __u32 reserved[7]; + union { + struct cec_event_state_change state_change; + struct cec_event_inputs_change inputs_change; + __u32 raw[16]; + }; +}; + +/* ioctls */ + +/* Adapter capabilities */ +#define CEC_ADAP_G_CAPS _IOWR('a', 0, struct cec_caps) + +/* + Configure the CEC adapter. It sets the device type and which + logical types it will try to claim. It will return which + logical addresses it could actually claim. + An error is returned if the adapter is disabled or if there + is no physical address assigned. + */ + +#define CEC_ADAP_G_LOG_ADDRS _IOR ('a', 1, struct cec_log_addrs) +#define CEC_ADAP_S_LOG_ADDRS _IOWR('a', 2, struct cec_log_addrs) + +/* + Enable/disable the adapter. The Set state ioctl may not + be available if that is handled internally. + */ +#define CEC_ADAP_G_STATE _IOR ('a', 3, __u32) +#define CEC_ADAP_S_STATE _IOW ('a', 4, __u32) + +/* + phys_addr is either 0 (if this is the CEC root device) + or a valid physical address obtained from the sink's EDID + as read by this CEC device (if this is a source device) + or a physical address obtained and modified from a sink + EDID and used for a sink CEC device. + If nothing is connected, then phys_addr is 0xffff. + See HDMI 1.4b, section 8.7 (Physical Address). + + The Set ioctl may not be available if that is handled + internally. + */ +#define CEC_ADAP_G_PHYS_ADDR _IOR ('a', 5, __u16) +#define CEC_ADAP_S_PHYS_ADDR _IOW ('a', 6, __u16) + +/* + Read and set the vendor ID of the CEC adapter. + */ +#define CEC_ADAP_G_VENDOR_ID _IOR ('a', 7, __u32) +#define CEC_ADAP_S_VENDOR_ID _IOW ('a', 8, __u32) + +/* + Read and set the monitor state of the filehandle. + */ +#define CEC_G_MONITOR _IOR ('a', 9, __u32) +#define CEC_S_MONITOR _IOW ('a', 10, __u32) + +/* + Claim message handling and set passthrough mode, + release message handling and get passthrough mode for + this filehandle. + */ +#define CEC_CLAIM _IOW ('a', 11, __u32) +#define CEC_RELEASE _IO ('a', 12) +#define CEC_G_PASSTHROUGH _IOR ('a', 13, __u32) + +/* Transmit/receive a CEC command */ +#define CEC_TRANSMIT _IOWR('a', 14, struct cec_msg) +#define CEC_RECEIVE _IOWR('a', 15, struct cec_msg) + +/* Dequeue CEC events */ +#define CEC_DQEVENT _IOWR('a', 16, struct cec_event) + +/* Commands */ + +/* One Touch Play Feature */ +#define CEC_MSG_ACTIVE_SOURCE 0x82 +#define CEC_MSG_IMAGE_VIEW_ON 0x04 +#define CEC_MSG_TEXT_VIEW_ON 0x0d + + +/* Routing Control Feature */ + +/* + * Has also: + * CEC_MSG_ACTIVE_SOURCE + */ + +#define CEC_MSG_INACTIVE_SOURCE 0x9d +#define CEC_MSG_REQUEST_ACTIVE_SOURCE 0x85 +#define CEC_MSG_ROUTING_CHANGE 0x80 +#define CEC_MSG_ROUTING_INFORMATION 0x81 +#define CEC_MSG_SET_STREAM_PATH 0x86 + + +/* Standby Feature */ +#define CEC_MSG_STANDBY 0x36 + + +/* One Touch Record Feature */ +#define CEC_MSG_RECORD_OFF 0x0b +#define CEC_MSG_RECORD_ON 0x09 +/* Record Source Type Operand (rec_src_type) */ +#define CEC_OP_RECORD_SRC_OWN 1 +#define CEC_OP_RECORD_SRC_DIGITAL 2 +#define CEC_OP_RECORD_SRC_ANALOG 3 +#define CEC_OP_RECORD_SRC_EXT_PLUG 4 +#define CEC_OP_RECORD_SRC_EXT_PHYS_ADDR 5 +/* Service Identification Method Operand (service_id_method) */ +#define CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID 0 +#define CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL 1 +/* Digital Service Broadcast System Operand (dig_bcast_system) */ +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN 0x00 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN 0x01 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN 0x02 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS 0x08 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS 0x09 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T 0x0a +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE 0x10 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT 0x11 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T 0x12 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C 0x18 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S 0x19 +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2 0x1a +#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T 0x1b +/* Analogue Broadcast Type Operand (ana_bcast_type) */ +#define CEC_OP_ANA_BCAST_TYPE_CABLE 0 +#define CEC_OP_ANA_BCAST_TYPE_SATELLITE 1 +#define CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL 2 +/* Broadcast System Operand (bcast_system) */ +#define CEC_OP_BCAST_SYSTEM_PAL_BG 0x00 +#define CEC_OP_BCAST_SYSTEM_SECAM_LQ 0x01 /* SECAM L' */ +#define CEC_OP_BCAST_SYSTEM_PAL_M 0x02 +#define CEC_OP_BCAST_SYSTEM_NTSC_M 0x03 +#define CEC_OP_BCAST_SYSTEM_PAL_I 0x04 +#define CEC_OP_BCAST_SYSTEM_SECAM_DK 0x05 +#define CEC_OP_BCAST_SYSTEM_SECAM_BG 0x06 +#define CEC_OP_BCAST_SYSTEM_SECAM_L 0x07 +#define CEC_OP_BCAST_SYSTEM_PAL_DK 0x08 +#define CEC_OP_BCAST_SYSTEM_OTHER 0x1f +/* Channel Number Format Operand (channel_number_fmt) */ +#define CEC_OP_CHANNEL_NUMBER_FMT_1_PART 0x01 +#define CEC_OP_CHANNEL_NUMBER_FMT_2_PART 0x02 + +#define CEC_MSG_RECORD_STATUS 0x0a +/* Record Status Operand (rec_status) */ +#define CEC_OP_RECORD_STATUS_CUR_SRC 0x01 +#define CEC_OP_RECORD_STATUS_DIG_SERVICE 0x02 +#define CEC_OP_RECORD_STATUS_ANA_SERVICE 0x03 +#define CEC_OP_RECORD_STATUS_EXT_INPUT 0x04 +#define CEC_OP_RECORD_STATUS_NO_DIG_SERVICE 0x05 +#define CEC_OP_RECORD_STATUS_NO_ANA_SERVICE 0x06 +#define CEC_OP_RECORD_STATUS_NO_SERVICE 0x07 +#define CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG 0x09 +#define CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR 0x0a +#define CEC_OP_RECORD_STATUS_UNSUP_CA 0x0b +#define CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS 0x0c +#define CEC_OP_RECORD_STATUS_CANT_COPY_SRC 0x0d +#define CEC_OP_RECORD_STATUS_NO_MORE_COPIES 0x0e +#define CEC_OP_RECORD_STATUS_NO_MEDIA 0x10 +#define CEC_OP_RECORD_STATUS_PLAYING 0x11 +#define CEC_OP_RECORD_STATUS_ALREADY_RECORDING 0x12 +#define CEC_OP_RECORD_STATUS_MEDIA_PROT 0x13 +#define CEC_OP_RECORD_STATUS_NO_SIGNAL 0x14 +#define CEC_OP_RECORD_STATUS_MEDIA_PROBLEM 0x15 +#define CEC_OP_RECORD_STATUS_NO_SPACE 0x16 +#define CEC_OP_RECORD_STATUS_PARENTAL_LOCK 0x17 +#define CEC_OP_RECORD_STATUS_TERMINATED_OK 0x1a +#define CEC_OP_RECORD_STATUS_ALREADY_TERM 0x1b +#define CEC_OP_RECORD_STATUS_OTHER 0x1f + +#define CEC_MSG_RECORD_TV_SCREEN 0x0f + + +/* Timer Programming Feature */ +#define CEC_MSG_CLEAR_ANALOGUE_TIMER 0x33 +/* Recording Sequence Operand (recording_seq) */ +#define CEC_OP_REC_SEQ_SUNDAY 0x01 +#define CEC_OP_REC_SEQ_MONDAY 0x02 +#define CEC_OP_REC_SEQ_TUESDAY 0x04 +#define CEC_OP_REC_SEQ_WEDNESDAY 0x08 +#define CEC_OP_REC_SEQ_THURSDAY 0x10 +#define CEC_OP_REC_SEQ_FRIDAY 0x20 +#define CEC_OP_REC_SEQ_SATERDAY 0x40 +#define CEC_OP_REC_SEQ_ONCE_ONLY 0x00 + +#define CEC_MSG_CLEAR_DIGITAL_TIMER 0x99 + +#define CEC_MSG_CLEAR_EXT_TIMER 0xa1 +/* External Source Specifier Operand (ext_src_spec) */ +#define CEC_OP_EXT_SRC_PLUG 0x04 +#define CEC_OP_EXT_SRC_PHYS_ADDR 0x05 + +#define CEC_MSG_SET_ANALOGUE_TIMER 0x34 +#define CEC_MSG_SET_DIGITAL_TIMER 0x97 +#define CEC_MSG_SET_EXT_TIMER 0xa2 + +#define CEC_MSG_SET_TIMER_PROGRAM_TITLE 0x67 +#define CEC_MSG_TIMER_CLEARED_STATUS 0x43 +/* Timer Cleared Status Data Operand (timer_cleared_status) */ +#define CEC_OP_TIMER_CLR_STAT_RECORDING 0x00 +#define CEC_OP_TIMER_CLR_STAT_NO_MATCHING 0x01 +#define CEC_OP_TIMER_CLR_STAT_NO_INFO 0x02 +#define CEC_OP_TIMER_CLR_STAT_CLEARED 0x80 + +#define CEC_MSG_TIMER_STATUS 0x35 +/* Timer Overlap Warning Operand (timer_overlap_warning) */ +#define CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP 0 +#define CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP 1 +/* Media Info Operand (media_info) */ +#define CEC_OP_MEDIA_INFO_UNPROT_MEDIA 0 +#define CEC_OP_MEDIA_INFO_PROT_MEDIA 1 +#define CEC_OP_MEDIA_INFO_NO_MEDIA 2 +/* Programmed Indicator Operand (prog_indicator) */ +#define CEC_OP_PROG_IND_NOT_PROGRAMMED 0 +#define CEC_OP_PROG_IND_PROGRAMMED 1 +/* Programmed Info Operand (prog_info) */ +#define CEC_OP_PROG_INFO_ENOUGH_SPACE 0x08 +#define CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE 0x09 +#define CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE 0x0b +#define CEC_OP_PROG_INFO_NONE_AVAILABLE 0x0a +/* Not Programmed Error Info Operand (prog_error) */ +#define CEC_OP_PROG_ERROR_NO_FREE_TIMER 0x01 +#define CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE 0x02 +#define CEC_OP_PROG_ERROR_REC_SEQ_ERROR 0x03 +#define CEC_OP_PROG_ERROR_INV_EXT_PLUG 0x04 +#define CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR 0x05 +#define CEC_OP_PROG_ERROR_CA_UNSUPP 0x06 +#define CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS 0x07 +#define CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP 0x08 +#define CEC_OP_PROG_ERROR_PARENTAL_LOCK 0x09 +#define CEC_OP_PROG_ERROR_CLOCK_FAILURE 0x0a +#define CEC_OP_PROG_ERROR_DUPLICATE 0x0e + + +/* System Information Feature */ +#define CEC_MSG_CEC_VERSION 0x9e +/* CEC Version Operand (cec_version) */ +#define CEC_OP_CEC_VERSION_1_3A 4 +#define CEC_OP_CEC_VERSION_1_4 5 +#define CEC_OP_CEC_VERSION_2_0 6 + +#define CEC_MSG_GET_CEC_VERSION 0x9f +#define CEC_MSG_GIVE_PHYSICAL_ADDR 0x83 +#define CEC_MSG_GET_MENU_LANGUAGE 0x91 +#define CEC_MSG_REPORT_PHYSICAL_ADDR 0x84 +/* Primary Device Type Operand (prim_devtype) */ +#define CEC_OP_PRIM_DEVTYPE_TV 0 +#define CEC_OP_PRIM_DEVTYPE_RECORD 1 +#define CEC_OP_PRIM_DEVTYPE_TUNER 3 +#define CEC_OP_PRIM_DEVTYPE_PLAYBACK 4 +#define CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM 5 +#define CEC_OP_PRIM_DEVTYPE_SWITCH 6 +#define CEC_OP_PRIM_DEVTYPE_PROCESSOR 7 + +#define CEC_MSG_SET_MENU_LANGUAGE 0x32 +#define CEC_MSG_REPORT_FEATURES 0xa6 /* HDMI 2.0 */ +/* All Device Types Operand (all_device_types) */ +#define CEC_OP_ALL_DEVTYPE_TV 0x80 +#define CEC_OP_ALL_DEVTYPE_RECORD 0x40 +#define CEC_OP_ALL_DEVTYPE_TUNER 0x20 +#define CEC_OP_ALL_DEVTYPE_PLAYBACK 0x10 +#define CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM 0x08 +#define CEC_OP_ALL_DEVTYPE_SWITCH 0x04 +/* And if you wondering what happened to PROCESSOR devices: those should + * be mapped to a SWITCH. */ + +/* Valid for RC Profile and Device Feature operands */ +#define CEC_OP_FEAT_EXT 0x80 /* Extension bit */ +/* RC Profile Operand (rc_profile) */ +#define CEC_OP_FEAT_RC_TV_PROFILE_NONE 0x00 +#define CEC_OP_FEAT_RC_TV_PROFILE_1 0x02 +#define CEC_OP_FEAT_RC_TV_PROFILE_2 0x06 +#define CEC_OP_FEAT_RC_TV_PROFILE_3 0x0a +#define CEC_OP_FEAT_RC_TV_PROFILE_4 0x0e +#define CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU 0x50 +#define CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU 0x48 +#define CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU 0x44 +#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU 0x42 +#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU 0x41 +/* Device Feature Operand (dev_features) */ +#define CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN 0x40 +#define CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING 0x20 +#define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL 0x10 +#define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE 0x08 +#define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX 0x04 +#define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX 0x02 + +#define CEC_MSG_GIVE_FEATURES 0xa5 /* HDMI 2.0 */ + + +/* Deck Control Feature */ +#define CEC_MSG_DECK_CONTROL 0x42 +/* Deck Control Mode Operand (deck_control_mode) */ +#define CEC_OP_DECK_CTL_MODE_SKIP_FWD 1 +#define CEC_OP_DECK_CTL_MODE_SKIP_REV 2 +#define CEC_OP_DECK_CTL_MODE_STOP 3 +#define CEC_OP_DECK_CTL_MODE_EJECT 4 + +#define CEC_MSG_DECK_STATUS 0x1b +/* Deck Info Operand (deck_info) */ +#define CEC_OP_DECK_INFO_PLAY 0x11 +#define CEC_OP_DECK_INFO_RECORD 0x12 +#define CEC_OP_DECK_INFO_PLAY_REV 0x13 +#define CEC_OP_DECK_INFO_STILL 0x14 +#define CEC_OP_DECK_INFO_SLOW 0x15 +#define CEC_OP_DECK_INFO_SLOW_REV 0x16 +#define CEC_OP_DECK_INFO_FAST_FWD 0x17 +#define CEC_OP_DECK_INFO_FAST_REV 0x18 +#define CEC_OP_DECK_INFO_NO_MEDIA 0x19 +#define CEC_OP_DECK_INFO_STOP 0x1a +#define CEC_OP_DECK_INFO_SKIP_FWD 0x1b +#define CEC_OP_DECK_INFO_SKIP_REV 0x1c +#define CEC_OP_DECK_INFO_INDEX_SEARCH_FWD 0x1d +#define CEC_OP_DECK_INFO_INDEX_SEARCH_REV 0x1e +#define CEC_OP_DECK_INFO_OTHER 0x1f + +#define CEC_MSG_GIVE_DECK_STATUS 0x1a +/* Status Request Operand (status_req) */ +#define CEC_OP_STATUS_REQ_ON 1 +#define CEC_OP_STATUS_REQ_OFF 2 +#define CEC_OP_STATUS_REQ_ONCE 3 + +#define CEC_MSG_PLAY 0x41 +/* Play Mode Operand (play_mode) */ +#define CEC_OP_PLAY_MODE_PLAY_FWD 0x24 +#define CEC_OP_PLAY_MODE_PLAY_REV 0x20 +#define CEC_OP_PLAY_MODE_PLAY_STILL 0x25 +#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN 0x05 +#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED 0x06 +#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX 0x07 +#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN 0x09 +#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED 0x0a +#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX 0x0b +#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN 0x15 +#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED 0x16 +#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX 0x17 +#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN 0x19 +#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED 0x1a +#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX 0x1b + + +/* Tuner Control Feature */ +#define CEC_MSG_GIVE_TUNER_DEVICE_STATUS 0x08 +#define CEC_MSG_SELECT_ANALOGUE_SERVICE 0x92 +#define CEC_MSG_SELECT_DIGITAL_SERVICE 0x93 +#define CEC_MSG_TUNER_DEVICE_STATUS 0x07 +/* Recording Flag Operand (rec_flag) */ +#define CEC_OP_REC_FLAG_USED 0 +#define CEC_OP_REC_FLAG_NOT_USED 1 +/* Tuner Display Info Operand (tuner_display_info) */ +#define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL 0 +#define CEC_OP_TUNER_DISPLAY_INFO_NONE 1 +#define CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE 2 + +#define CEC_MSG_TUNER_STEP_DECREMENT 0x06 +#define CEC_MSG_TUNER_STEP_INCREMENT 0x05 + + +/* Vendor Specific Commands Feature */ + +/* + * Has also: + * CEC_MSG_CEC_VERSION + * CEC_MSG_GET_CEC_VERSION + */ +#define CEC_MSG_DEVICE_VENDOR_ID 0x87 +#define CEC_MSG_GIVE_DEVICE_VENDOR_ID 0x8c +#define CEC_MSG_VENDOR_COMMAND 0x89 +#define CEC_MSG_VENDOR_COMMAND_WITH_ID 0xa0 +#define CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN 0x8a +#define CEC_MSG_VENDOR_REMOTE_BUTTON_UP 0x8b + + +/* OSD Display Feature */ +#define CEC_MSG_SET_OSD_STRING 0x64 +/* Display Control Operand (disp_ctl) */ +#define CEC_OP_DISP_CTL_DEFAULT 0x00 +#define CEC_OP_DISP_CTL_UNTIL_CLEARED 0x40 +#define CEC_OP_DISP_CTL_CLEAR 0x80 + + +/* Device OSD Transfer Feature */ +#define CEC_MSG_GIVE_OSD_NAME 0x46 +#define CEC_MSG_SET_OSD_NAME 0x47 + + +/* Device Menu Control Feature */ +#define CEC_MSG_MENU_REQUEST 0x8d +/* Menu Request Type Operand (menu_req) */ +#define CEC_OP_MENU_REQUEST_ACTIVATE 0x00 +#define CEC_OP_MENU_REQUEST_DEACTIVATE 0x01 +#define CEC_OP_MENU_REQUEST_QUERY 0x02 + +#define CEC_MSG_MENU_STATUS 0x8e +/* Menu State Operand (menu_state) */ +#define CEC_OP_MENU_STATE_ACTIVATED 0x00 +#define CEC_OP_MENU_STATE_DEACTIVATED 0x01 + +#define CEC_MSG_USER_CONTROL_PRESSED 0x44 +/* UI Broadcast Type Operand (ui_bcast_type) */ +#define CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL 0x00 +#define CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA 0x01 +#define CEC_OP_UI_BCAST_TYPE_ANALOGUE 0x10 +#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_T 0x20 +#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE 0x30 +#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT 0x40 +#define CEC_OP_UI_BCAST_TYPE_DIGITAL 0x50 +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_T 0x60 +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE 0x70 +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT 0x80 +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT 0x90 +#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2 0x91 +#define CEC_OP_UI_BCAST_TYPE_IP 0xa0 +/* UI Sound Presentation Control Operand (ui_snd_pres_ctl) */ +#define CEC_OP_UI_SND_PRES_CTL_DUAL_MONO 0x10 +#define CEC_OP_UI_SND_PRES_CTL_KARAOKE 0x20 +#define CEC_OP_UI_SND_PRES_CTL_DOWNMIX 0x80 +#define CEC_OP_UI_SND_PRES_CTL_REVERB 0x90 +#define CEC_OP_UI_SND_PRES_CTL_EQUALIZER 0xa0 +#define CEC_OP_UI_SND_PRES_CTL_BASS_UP 0xb1 +#define CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL 0xb2 +#define CEC_OP_UI_SND_PRES_CTL_BASS_DOWN 0xb3 +#define CEC_OP_UI_SND_PRES_CTL_TREBLE_UP 0xc1 +#define CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL 0xc2 +#define CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN 0xc3 + +#define CEC_MSG_USER_CONTROL_RELEASED 0x45 + + +/* Remote Control Passthrough Feature */ + +/* + * Has also: + * CEC_MSG_USER_CONTROL_PRESSED + * CEC_MSG_USER_CONTROL_RELEASED + */ + + +/* Power Status Feature */ +#define CEC_MSG_GIVE_DEVICE_POWER_STATUS 0x8f +#define CEC_MSG_REPORT_POWER_STATUS 0x90 +/* Power Status Operand (pwr_state) */ +#define CEC_OP_POWER_STATUS_ON 0 +#define CEC_OP_POWER_STATUS_STANDBY 1 +#define CEC_OP_POWER_STATUS_TO_ON 2 +#define CEC_OP_POWER_STATUS_TO_STANDBY 3 + + +/* General Protocol Messages */ +#define CEC_MSG_FEATURE_ABORT 0x00 +/* Abort Reason Operand (reason) */ +#define CEC_OP_ABORT_UNRECOGNIZED_OP 0 +#define CEC_OP_ABORT_INCORRECT_MODE 1 +#define CEC_OP_ABORT_NO_SOURCE 2 +#define CEC_OP_ABORT_INVALID_OP 3 +#define CEC_OP_ABORT_REFUSED 4 +#define CEC_OP_ABORT_UNDETERMINED 5 + +#define CEC_MSG_ABORT 0xff + + +/* System Audio Control Feature */ + +/* + * Has also: + * CEC_MSG_USER_CONTROL_PRESSED + * CEC_MSG_USER_CONTROL_RELEASED + */ +#define CEC_MSG_GIVE_AUDIO_STATUS 0x71 +#define CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS 0x7d +#define CEC_MSG_REPORT_AUDIO_STATUS 0x7a +/* Audio Mute Status Operand (aud_mute_status) */ +#define CEC_OP_AUD_MUTE_STATUS_OFF 0 +#define CEC_OP_AUD_MUTE_STATUS_ON 1 + +#define CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR 0xa3 +#define CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR 0xa4 +#define CEC_MSG_SET_SYSTEM_AUDIO_MODE 0x72 +/* System Audio Status Operand (sys_aud_status) */ +#define CEC_OP_SYS_AUD_STATUS_OFF 0 +#define CEC_OP_SYS_AUD_STATUS_ON 1 + +#define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST 0x70 +#define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS 0x7e +/* Audio Format ID Operand (audio_format_id) */ +#define CEC_OP_AUD_FMT_ID_CEA861 0 +#define CEC_OP_AUD_FMT_ID_CEA861_CXT 1 + + +/* Audio Rate Control Feature */ +#define CEC_MSG_SET_AUDIO_RATE 0x9a +/* Audio Rate Operand (audio_rate) */ +#define CEC_OP_AUD_RATE_OFF 0 +#define CEC_OP_AUD_RATE_WIDE_STD 1 +#define CEC_OP_AUD_RATE_WIDE_FAST 2 +#define CEC_OP_AUD_RATE_WIDE_SLOW 3 +#define CEC_OP_AUD_RATE_NARROW_STD 4 +#define CEC_OP_AUD_RATE_NARROW_FAST 5 +#define CEC_OP_AUD_RATE_NARROW_SLOW 6 + + +/* Audio Return Channel Control Feature */ +#define CEC_MSG_INITIATE_ARC 0xc0 +#define CEC_MSG_REPORT_ARC_INITIATED 0xc1 +#define CEC_MSG_REPORT_ARC_TERMINATED 0xc2 +#define CEC_MSG_REQUEST_ARC_INITIATION 0xc3 +#define CEC_MSG_REQUEST_ARC_TERMINATION 0xc4 +#define CEC_MSG_TERMINATE_ARC 0xc5 + + +/* Dynamic Audio Lipsync Feature */ +/* Only for CEC 2.0 and up */ +#define CEC_MSG_REQUEST_CURRENT_LATENCY 0xa7 +#define CEC_MSG_REPORT_CURRENT_LATENCY 0xa8 +/* Low Latency Mode Operand (low_latency_mode) */ +#define CEC_OP_LOW_LATENCY_MODE_OFF 0 +#define CEC_OP_LOW_LATENCY_MODE_ON 1 +/* Audio Output Compensated Operand (audio_out_compensated) */ +#define CEC_OP_AUD_OUT_COMPENSATED_NA 0 +#define CEC_OP_AUD_OUT_COMPENSATED_DELAY 1 +#define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY 2 +#define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY 3 + + +/* Capability Discovery and Control Feature */ +#define CEC_MSG_CDC_MESSAGE 0xf8 +/* Ethernet-over-HDMI: nobody ever does this... */ +#define CEC_MSG_CDC_HEC_INQUIRE_STATE 0x00 +#define CEC_MSG_CDC_HEC_REPORT_STATE 0x01 +/* HEC Functionality State Operand (hec_func_state) */ +#define CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED 0 +#define CEC_OP_HEC_FUNC_STATE_INACTIVE 1 +#define CEC_OP_HEC_FUNC_STATE_ACTIVE 2 +#define CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD 3 +/* Host Functionality State Operand (host_func_state) */ +#define CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED 0 +#define CEC_OP_HOST_FUNC_STATE_INACTIVE 1 +#define CEC_OP_HOST_FUNC_STATE_ACTIVE 2 +/* ENC Functionality State Operand (enc_func_state) */ +#define CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED 0 +#define CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE 1 +#define CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE 2 +/* CDC Error Code Operand (cdc_errcode) */ +#define CEC_OP_CDC_ERROR_CODE_NONE 0 +#define CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED 1 +#define CEC_OP_CDC_ERROR_CODE_WRONG_STATE 2 +#define CEC_OP_CDC_ERROR_CODE_OTHER 3 +/* HEC Support Operand (hec_support) */ +#define CEC_OP_HEC_SUPPORT_NO 0 +#define CEC_OP_HEC_SUPPORT_YES 1 +/* HEC Activation Operand (hec_activation) */ +#define CEC_OP_HEC_ACTIVATION_ON 0 +#define CEC_OP_HEC_ACTIVATION_OFF 1 + +#define CEC_MSG_CDC_HEC_SET_STATE_ADJACENT 0x02 +#define CEC_MSG_CDC_HEC_SET_STATE 0x03 +/* HEC Set State Operand (hec_set_state) */ +#define CEC_OP_HEC_SET_STATE_DEACTIVATE 0 +#define CEC_OP_HEC_SET_STATE_ACTIVATE 1 + +#define CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION 0x04 +#define CEC_MSG_CDC_HEC_NOTIFY_ALIVE 0x05 +#define CEC_MSG_CDC_HEC_DISCOVER 0x06 +/* Hotplug Detect messages */ +#define CEC_MSG_CDC_HPD_SET_STATE 0x10 +/* HPD State Operand (hpd_state) */ +#define CEC_OP_HPD_STATE_CP_EDID_DISABLE 0 +#define CEC_OP_HPD_STATE_CP_EDID_ENABLE 1 +#define CEC_OP_HPD_STATE_CP_EDID_DISABLE_ENABLE 2 +#define CEC_OP_HPD_STATE_EDID_DISABLE 3 +#define CEC_OP_HPD_STATE_EDID_ENABLE 4 +#define CEC_OP_HPD_STATE_EDID_DISABLE_ENABLE 5 +#define CEC_MSG_CDC_HPD_REPORT_STATE 0x11 +/* HPD Error Code Operand (hpd_error) */ +#define CEC_OP_HPD_ERROR_NONE 0 +#define CEC_OP_HPD_ERROR_INITIATOR_NOT_CAPABLE 1 +#define CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE 2 +#define CEC_OP_HPD_ERROR_OTHER 3 +#define CEC_OP_HPD_ERROR_NONE_NO_VIDEO 4 + +#endif diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h index 9f6e108..d448c53 100644 --- a/include/linux/v4l2-controls.h +++ b/include/linux/v4l2-controls.h @@ -174,6 +174,10 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_ADV7180_BASE (V4L2_CID_USER_BASE + 0x1070)
+/* The base for the tc358743 driver controls. + * We reserve 16 controls for this driver. */ +#define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ diff --git a/utils/keytable/parse.h b/utils/keytable/parse.h index 6ada6e5..67eb1a6 100644 --- a/utils/keytable/parse.h +++ b/utils/keytable/parse.h @@ -518,6 +518,24 @@ struct parse_event key_events[] = { {"KEY_KBDINPUTASSIST_NEXTGROUP", 0x263}, {"KEY_KBDINPUTASSIST_ACCEPT", 0x264}, {"KEY_KBDINPUTASSIST_CANCEL", 0x265}, + {"KEY_RIGHT_UP", 0x266}, + {"KEY_RIGHT_DOWN", 0x267}, + {"KEY_LEFT_UP", 0x268}, + {"KEY_LEFT_DOWN", 0x269}, + {"KEY_ROOT_MENU", 0x26a}, + {"KEY_MEDIA_TOP_MENU", 0x26b}, + {"KEY_NUMERIC_11", 0x26c}, + {"KEY_NUMERIC_12", 0x26d}, + {"KEY_AUDIO_DESC", 0x26e}, + {"KEY_3D_MODE", 0x26f}, + {"KEY_NEXT_FAVORITE", 0x270}, + {"KEY_STOP_RECORD", 0x271}, + {"KEY_PAUSE_RECORD", 0x272}, + {"KEY_VOD", 0x273}, + {"KEY_UNMUTE", 0x274}, + {"KEY_FASTREVERSE", 0x275}, + {"KEY_SLOWREVERSE", 0x276}, + {"KEY_DATA", 0x275}, {"BTN_TRIGGER_HAPPY", 0x2c0}, {"BTN_TRIGGER_HAPPY1", 0x2c0}, {"BTN_TRIGGER_HAPPY2", 0x2c1}, diff --git a/utils/keytable/rc_keymaps/lme2510 b/utils/keytable/rc_keymaps/lme2510 index 6d7b18e..1da95f8 100644 --- a/utils/keytable/rc_keymaps/lme2510 +++ b/utils/keytable/rc_keymaps/lme2510 @@ -1,67 +1,67 @@ # table lme2510, type: NEC -0x10ed45 KEY_0 -0x10ed5f KEY_1 -0x10ed50 KEY_2 -0x10ed5d KEY_3 -0x10ed41 KEY_4 -0x10ed0a KEY_5 -0x10ed42 KEY_6 -0x10ed47 KEY_7 -0x10ed49 KEY_8 -0x10ed05 KEY_9 -0x10ed43 KEY_POWER -0x10ed46 KEY_SUBTITLE -0x10ed06 KEY_PAUSE -0x10ed03 KEY_MEDIA_REPEAT -0x10ed02 KEY_PAUSE -0x10ed5e KEY_VOLUMEUP -0x10ed5c KEY_VOLUMEDOWN -0x10ed09 KEY_CHANNELUP -0x10ed1a KEY_CHANNELDOWN -0x10ed1e KEY_PLAY -0x10ed1b KEY_ZOOM -0x10ed59 KEY_MUTE -0x10ed5a KEY_TV -0x10ed18 KEY_RECORD -0x10ed07 KEY_EPG -0x10ed01 KEY_STOP -0xbf15 KEY_0 -0xbf08 KEY_1 -0xbf09 KEY_2 -0xbf0a KEY_3 -0xbf0c KEY_4 -0xbf0d KEY_5 -0xbf0e KEY_6 -0xbf10 KEY_7 -0xbf11 KEY_8 -0xbf12 KEY_9 -0xbf00 KEY_POWER -0xbf04 KEY_MEDIA_REPEAT -0xbf1a KEY_PAUSE -0xbf02 KEY_VOLUMEUP -0xbf06 KEY_VOLUMEDOWN -0xbf01 KEY_CHANNELUP -0xbf05 KEY_CHANNELDOWN -0xbf14 KEY_ZOOM -0xbf18 KEY_RECORD -0xbf16 KEY_STOP -0x1c KEY_0 -0x07 KEY_1 -0x15 KEY_2 -0x09 KEY_3 -0x16 KEY_4 -0x19 KEY_5 -0x0d KEY_6 -0x0c KEY_7 -0x18 KEY_8 -0x5e KEY_9 -0x45 KEY_POWER -0x44 KEY_MEDIA_REPEAT -0x4a KEY_PAUSE -0x47 KEY_VOLUMEUP -0x43 KEY_VOLUMEDOWN -0x46 KEY_CHANNELUP -0x40 KEY_CHANNELDOWN -0x08 KEY_ZOOM -0x42 KEY_RECORD -0x5a KEY_STOP +0xef12ba45 KEY_0 +0xef12a05f KEY_1 +0xef12af50 KEY_2 +0xef12a25d KEY_3 +0xef12be41 KEY_4 +0xef12f50a KEY_5 +0xef12bd42 KEY_6 +0xef12b847 KEY_7 +0xef12b649 KEY_8 +0xef12fa05 KEY_9 +0xef12bc43 KEY_POWER +0xef12b946 KEY_SUBTITLE +0xef12f906 KEY_PAUSE +0xef12fc03 KEY_MEDIA_REPEAT +0xef12fd02 KEY_PAUSE +0xef12a15e KEY_VOLUMEUP +0xef12a35c KEY_VOLUMEDOWN +0xef12f609 KEY_CHANNELUP +0xef12e51a KEY_CHANNELDOWN +0xef12e11e KEY_PLAY +0xef12e41b KEY_ZOOM +0xef12a659 KEY_MUTE +0xef12a55a KEY_TV +0xef12e718 KEY_RECORD +0xef12f807 KEY_EPG +0xef12fe01 KEY_STOP +0xff40ea15 KEY_0 +0xff40f708 KEY_1 +0xff40f609 KEY_2 +0xff40f50a KEY_3 +0xff40f30c KEY_4 +0xff40f20d KEY_5 +0xff40f10e KEY_6 +0xff40ef10 KEY_7 +0xff40ee11 KEY_8 +0xff40ed12 KEY_9 +0xff40ff00 KEY_POWER +0xff40fb04 KEY_MEDIA_REPEAT +0xff40e51a KEY_PAUSE +0xff40fd02 KEY_VOLUMEUP +0xff40f906 KEY_VOLUMEDOWN +0xff40fe01 KEY_CHANNELUP +0xff40fa05 KEY_CHANNELDOWN +0xff40eb14 KEY_ZOOM +0xff40e718 KEY_RECORD +0xff40e916 KEY_STOP +0xff00e31c KEY_0 +0xff00f807 KEY_1 +0xff00ea15 KEY_2 +0xff00f609 KEY_3 +0xff00e916 KEY_4 +0xff00e619 KEY_5 +0xff00f20d KEY_6 +0xff00f30c KEY_7 +0xff00e718 KEY_8 +0xff00a15e KEY_9 +0xff00ba45 KEY_POWER +0xff00bb44 KEY_MEDIA_REPEAT +0xff00b54a KEY_PAUSE +0xff00b847 KEY_VOLUMEUP +0xff00bc43 KEY_VOLUMEDOWN +0xff00b946 KEY_CHANNELUP +0xff00bf40 KEY_CHANNELDOWN +0xff00f708 KEY_ZOOM +0xff00bd42 KEY_RECORD +0xff00a55a KEY_STOP diff --git a/utils/keytable/rc_maps.cfg b/utils/keytable/rc_maps.cfg index e69fd6a..3bd7197 100644 --- a/utils/keytable/rc_maps.cfg +++ b/utils/keytable/rc_maps.cfg @@ -50,6 +50,7 @@ * rc-behold-columbus behold_columbus * rc-behold behold * rc-budget-ci-old budget_ci_old +* rc-cec cec * rc-cinergy-1400 cinergy_1400 * rc-cinergy cinergy * rc-delock-61959 delock_61959
This utility will attempt to test whether the CEC protocol was implemented correctly.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- configure.ac | 1 + utils/Makefile.am | 1 + utils/cec-compliance/Makefile.am | 3 + utils/cec-compliance/cec-compliance.cpp | 926 ++++++++++++++++++++++++++++++++ utils/cec-compliance/cec-compliance.h | 87 +++ 5 files changed, 1018 insertions(+) create mode 100644 utils/cec-compliance/Makefile.am create mode 100644 utils/cec-compliance/cec-compliance.cpp create mode 100644 utils/cec-compliance/cec-compliance.h
diff --git a/configure.ac b/configure.ac index d4e312c..12c2eb9 100644 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,7 @@ AC_CONFIG_FILES([Makefile utils/keytable/Makefile utils/media-ctl/Makefile utils/rds/Makefile + utils/cec-compliance/Makefile utils/v4l2-compliance/Makefile utils/v4l2-ctl/Makefile utils/v4l2-dbg/Makefile diff --git a/utils/Makefile.am b/utils/Makefile.am index 31b2979..c78e97b 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -5,6 +5,7 @@ SUBDIRS = \ decode_tm6000 \ keytable \ media-ctl \ + cec-compliance \ v4l2-compliance \ v4l2-ctl \ v4l2-dbg \ diff --git a/utils/cec-compliance/Makefile.am b/utils/cec-compliance/Makefile.am new file mode 100644 index 0000000..da4c0ef --- /dev/null +++ b/utils/cec-compliance/Makefile.am @@ -0,0 +1,3 @@ +bin_PROGRAMS = cec-compliance + +cec_compliance_SOURCES = cec-compliance.cpp diff --git a/utils/cec-compliance/cec-compliance.cpp b/utils/cec-compliance/cec-compliance.cpp new file mode 100644 index 0000000..59db8ab --- /dev/null +++ b/utils/cec-compliance/cec-compliance.cpp @@ -0,0 +1,926 @@ +/* + Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + Author: Hans Verkuil hans.verkuil@cisco.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + */ + +#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 <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <config.h> + +#include "cec-compliance.h" + +/* Short option list + + Please keep in alphabetical order. + That makes it easier to see which short options are still free. + + In general the lower case is used to set something and the upper + case is used to retrieve a setting. */ +enum Option { + OptPhysAddr = 'a', + OptSetDevice = 'd', + OptHelp = 'h', + OptNoWarnings = 'n', + OptTrace = 'T', + OptVerbose = 'v', + OptVendorID = 'V', + + OptTV, + OptRecord, + OptTuner, + OptPlayback, + OptAudio, + OptProcessor, + OptSwitch, + OptCDCOnly, + OptUnregistered, + OptLast = 256 +}; + +static char options[OptLast]; + +static int app_result; +static int tests_total, tests_ok; + +bool show_info; +bool show_warnings = true; +unsigned warnings; + +static struct option long_options[] = { + {"device", required_argument, 0, OptSetDevice}, + {"help", no_argument, 0, OptHelp}, + {"no-warnings", no_argument, 0, OptNoWarnings}, + {"trace", no_argument, 0, OptTrace}, + {"verbose", no_argument, 0, OptVerbose}, + {"phys-addr", required_argument, 0, OptPhysAddr}, + {"vendor-id", required_argument, 0, OptVendorID}, + + {"tv", no_argument, 0, OptTV}, + {"record", no_argument, 0, OptRecord}, + {"tuner", no_argument, 0, OptTuner}, + {"playback", no_argument, 0, OptPlayback}, + {"audio", no_argument, 0, OptAudio}, + {"processor", no_argument, 0, OptProcessor}, + {"switch", no_argument, 0, OptSwitch}, + {"cdc-only", no_argument, 0, OptCDCOnly}, + {"unregistered", no_argument, 0, OptUnregistered}, + {0, 0, 0, 0} +}; + +static void usage(void) +{ + printf("Usage:\n" + " -d, --device=<dev> Use device <dev> instead of /dev/cec0\n" + " If <dev> starts with a digit, then /dev/cec<dev> is used.\n" + " -h, --help Display this help message\n" + " -n, --no-warnings Turn off warning messages.\n" + " -T, --trace Trace all called ioctls.\n" + " -v, --verbose Turn on verbose reporting.\n" + " -a, --phys-addr=<addr>\n" + " Use this physical address.\n" + " -V, --vendor-id=<id>\n" + " Use this vendor ID.\n" + " --tv This is a TV\n" + " --record This is a recording device\n" + " --tuner This is a tuner device\n" + " --playback This is a playback device\n" + " --audio This is an audio system device\n" + " --processor This is a processor device\n" + " --switch This is a pure CEC switch\n" + " --cdc-only This is a CDC-only device\n" + " --unregistered This is an unregistered device\n" + ); +} + +static std::string caps2s(unsigned caps) +{ + std::string s; + + if (caps & CEC_CAP_STATE) + s += "\t\tState\n"; + 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_IO) + s += "\t\tI/O\n"; + if (caps & CEC_CAP_VENDOR_ID) + s += "\t\tVendor ID\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_ARC) + s += "\t\tAudio Return Channel\n"; + if (caps & CEC_CAP_CDC_HPD) + s += "\t\tCapability Discovery and Control HPD\n"; + if (caps & CEC_CAP_IS_SOURCE) + s += "\t\tIs Source\n"; + return s; +} + +static const char *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"; + } +} + +static const char *power_status2s(unsigned status) +{ + switch (status) { + case CEC_OP_POWER_STATUS_ON: + return "On"; + case CEC_OP_POWER_STATUS_STANDBY: + return "Standby"; + case CEC_OP_POWER_STATUS_TO_ON: + return "In Transition Standby to On"; + case CEC_OP_POWER_STATUS_TO_STANDBY: + return "In Transition On to Standby"; + default: + return "Unknown"; + } +} + +static const char *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"; + } +} + +static const char *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"; + } +} + +static const char *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 "Playback 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"; + } +} + +static std::string status2s(unsigned stat) +{ + std::string s; + + if (stat & CEC_TX_STATUS_ARB_LOST) + s += "ArbitrationLost "; + if (stat & CEC_TX_STATUS_REPLY_TIMEOUT) + s += "ReplyTimeout "; + if (stat & CEC_TX_STATUS_RETRY_TIMEOUT) + s += "RetryTimeout "; + if (stat & CEC_TX_STATUS_FEATURE_ABORT) + s += "FeatureAbort "; + return s; +} + +static std::string 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, "; + return s.erase(s.length() - 2, 2); +} + +static std::string rc_src_prof2s(unsigned prof) +{ + std::string s; + + prof &= 0x1f; + if (prof == 0) + return "\t\tNone\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU) + s += "\t\tSource Has Device Root Menu\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU) + s += "\t\tSource Has Device Setup Menu\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU) + s += "\t\tSource Has Contents Menu\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU) + s += "\t\tSource Has Media Top Menu\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU) + s += "\t\tSource Has Media Context-Sensitive Menu\n"; + return s; +} + +static std::string dev_feat2s(unsigned feat) +{ + std::string s; + + feat &= 0x3e; + if (feat == 0) + return "\t\tNone\n"; + if (feat & CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN) + s += "\t\tTV Supports <Record TV Screen>\n"; + if (feat & CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING) + s += "\t\tTV Supports <Set OSD String>\n"; + if (feat & CEC_OP_FEAT_DEV_HAS_DECK_CONTROL) + s += "\t\tSupports Deck Control\n"; + if (feat & CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE) + s += "\t\tSource Supports <Set Audio Rate>\n"; + if (feat & CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX) + s += "\t\tSink Supports ARC Tx\n"; + if (feat & CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX) + s += "\t\tSource Supports ARC Rx\n"; + return s; +} + +int cec_named_ioctl(int fd, const char *name, + unsigned long int request, void *parm) +{ + int retval = ioctl(fd, request, parm); + int e; + + e = retval == 0 ? 0 : errno; + if (options[OptTrace]) + printf("\t\t%s returned %d (%s)\n", + name, retval, strerror(e)); + + if (retval < 0) + app_result = -1; + + return retval == -1 ? e : (retval ? -1 : 0); +} + +const char *ok(int res) +{ + static char buf[100]; + + if (res == ENOTTY) { + strcpy(buf, "OK (Not Supported)"); + res = 0; + } else { + strcpy(buf, "OK"); + } + tests_total++; + if (res) { + app_result = res; + sprintf(buf, "FAIL"); + } else { + tests_ok++; + } + return buf; +} + +int check_0(const void *p, int len) +{ + const __u8 *q = (const __u8 *)p; + + while (len--) + if (*q++) + return 1; + return 0; +} + +static int testCap(struct node *node) +{ + struct cec_caps caps; + + memset(&caps, 0xff, sizeof(caps)); + // Must always be there + fail_on_test(doioctl(node, CEC_ADAP_G_CAPS, &caps)); + fail_on_test(check_0(caps.reserved, sizeof(caps.reserved))); + fail_on_test(caps.available_log_addrs == 0 || + caps.available_log_addrs > CEC_MAX_LOG_ADDRS); + fail_on_test((caps.capabilities & CEC_CAP_PASSTHROUGH) && + !(caps.capabilities & CEC_CAP_IO)); + return 0; +} + +static int testAdapPhysAddr(struct node *node, __u16 set_phys_addr) +{ + __u16 pa = 0xefff; + + fail_on_test(doioctl(node, CEC_ADAP_G_PHYS_ADDR, &pa)); + fail_on_test(pa == 0xefff); + if (node->caps & CEC_CAP_PHYS_ADDR) { + fail_on_test(doioctl(node, CEC_ADAP_S_PHYS_ADDR, &set_phys_addr)); + fail_on_test(doioctl(node, CEC_ADAP_G_PHYS_ADDR, &pa)); + fail_on_test(pa != set_phys_addr); + } else { + fail_on_test(doioctl(node, CEC_ADAP_S_PHYS_ADDR, &pa) != ENOTTY); + } + return 0; +} + +static int testVendorID(struct node *node, __u32 set_vendor_id) +{ + __u32 vendor_id = 0xeeeeeeee; + + fail_on_test(doioctl(node, CEC_ADAP_G_VENDOR_ID, &vendor_id)); + fail_on_test(vendor_id == 0xeeeeeeee); + fail_on_test(vendor_id != CEC_VENDOR_ID_NONE && + (vendor_id & 0xff000000)); + if (node->caps & CEC_CAP_VENDOR_ID) { + vendor_id = 0xeeeeeeee; + fail_on_test(doioctl(node, CEC_ADAP_S_VENDOR_ID, &vendor_id) != EINVAL); + fail_on_test(doioctl(node, CEC_ADAP_S_VENDOR_ID, &set_vendor_id)); + fail_on_test(doioctl(node, CEC_ADAP_G_VENDOR_ID, &vendor_id)); + fail_on_test(vendor_id != set_vendor_id); + } else { + fail_on_test(doioctl(node, CEC_ADAP_S_VENDOR_ID, &vendor_id) != ENOTTY); + } + return 0; +} + +static int testAdapState(struct node *node) +{ + __u32 state = 0xffffffff; + + fail_on_test(doioctl(node, CEC_ADAP_G_STATE, &state)); + fail_on_test(state > CEC_ADAP_ENABLED); + if (node->caps & CEC_CAP_STATE) { + state = CEC_ADAP_DISABLED; + fail_on_test(doioctl(node, CEC_ADAP_S_STATE, &state)); + fail_on_test(doioctl(node, CEC_ADAP_G_STATE, &state)); + fail_on_test(state != CEC_ADAP_DISABLED); + state = CEC_ADAP_ENABLED; + fail_on_test(doioctl(node, CEC_ADAP_S_STATE, &state)); + fail_on_test(doioctl(node, CEC_ADAP_G_STATE, &state)); + fail_on_test(state != CEC_ADAP_ENABLED); + + /* + * Do this again, thus guaranteeing that there is always + * a disabled -> enabled and an enabled -> disabled state + * transition tested. + */ + state = CEC_ADAP_DISABLED; + fail_on_test(doioctl(node, CEC_ADAP_S_STATE, &state)); + fail_on_test(doioctl(node, CEC_ADAP_G_STATE, &state)); + fail_on_test(state != CEC_ADAP_DISABLED); + state = CEC_ADAP_ENABLED; + fail_on_test(doioctl(node, CEC_ADAP_S_STATE, &state)); + fail_on_test(doioctl(node, CEC_ADAP_G_STATE, &state)); + fail_on_test(state != CEC_ADAP_ENABLED); + } else { + fail_on_test(doioctl(node, CEC_ADAP_S_STATE, &state) != ENOTTY); + } + return 0; +} + +static int testAdapLogAddrs(struct node *node, unsigned flags, + const char *osd_name) +{ + struct cec_log_addrs laddrs; + + memset(&laddrs, 0xff, sizeof(laddrs)); + fail_on_test(doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs)); + fail_on_test(check_0(laddrs.reserved, sizeof(laddrs.reserved))); + fail_on_test(laddrs.cec_version != CEC_OP_CEC_VERSION_1_4 && + laddrs.cec_version != CEC_OP_CEC_VERSION_2_0); + fail_on_test(laddrs.num_log_addrs > CEC_MAX_LOG_ADDRS); + if (node->caps & CEC_CAP_LOG_ADDRS) { + memset(&laddrs, 0, sizeof(laddrs)); + fail_on_test(doioctl(node, CEC_ADAP_S_LOG_ADDRS, &laddrs)); + fail_on_test(laddrs.num_log_addrs != 0); + fail_on_test(doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs)); + fail_on_test(laddrs.num_log_addrs != 0); + + memset(&laddrs, 0, sizeof(laddrs)); + laddrs.cec_version = CEC_OP_CEC_VERSION_2_0; + strcpy(laddrs.osd_name, osd_name); + for (unsigned i = 0; i < 8; i++) { + unsigned la_type; + unsigned all_dev_type; + + if (!(flags & (1 << i))) + continue; + fail_on_test(laddrs.num_log_addrs == node->available_log_addrs); + switch (i) { + case CEC_OP_PRIM_DEVTYPE_TV: + la_type = CEC_LOG_ADDR_TYPE_TV; + all_dev_type = CEC_OP_ALL_DEVTYPE_TV; + break; + case CEC_OP_PRIM_DEVTYPE_RECORD: + la_type = CEC_LOG_ADDR_TYPE_RECORD; + all_dev_type = CEC_OP_ALL_DEVTYPE_RECORD; + break; + case CEC_OP_PRIM_DEVTYPE_TUNER: + la_type = CEC_LOG_ADDR_TYPE_TUNER; + all_dev_type = CEC_OP_ALL_DEVTYPE_TUNER; + break; + case CEC_OP_PRIM_DEVTYPE_PLAYBACK: + la_type = CEC_LOG_ADDR_TYPE_PLAYBACK; + all_dev_type = CEC_OP_ALL_DEVTYPE_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: + la_type = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM; + all_dev_type = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; + break; + case CEC_OP_PRIM_DEVTYPE_PROCESSOR: + la_type = CEC_LOG_ADDR_TYPE_SPECIFIC; + all_dev_type = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + case CEC_OP_PRIM_DEVTYPE_SWITCH: + default: + la_type = CEC_LOG_ADDR_TYPE_UNREGISTERED; + all_dev_type = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + } + laddrs.log_addr_type[laddrs.num_log_addrs] = la_type; + laddrs.all_device_types[laddrs.num_log_addrs] = all_dev_type; + laddrs.primary_device_type[laddrs.num_log_addrs++] = i; + } + + fail_on_test(doioctl(node, CEC_ADAP_S_LOG_ADDRS, &laddrs)); + fail_on_test(laddrs.num_log_addrs == 0 || + laddrs.num_log_addrs > CEC_MAX_LOG_ADDRS); + node->num_log_addrs = laddrs.num_log_addrs; + memcpy(node->log_addr, laddrs.log_addr, laddrs.num_log_addrs); + fail_on_test(doioctl(node, CEC_ADAP_S_LOG_ADDRS, &laddrs) != EBUSY); + } else { + node->num_log_addrs = laddrs.num_log_addrs; + memcpy(node->log_addr, laddrs.log_addr, laddrs.num_log_addrs); + fail_on_test(doioctl(node, CEC_ADAP_S_LOG_ADDRS, &laddrs) != ENOTTY); + } + return 0; +} + +static int testTopologyDevice(struct node *node, unsigned i, unsigned la) +{ + struct cec_msg msg = { }; + char osd_name[15]; + + printf("\tSystem Information for device %d (%s) from device %d (%s):\n", + i, la2s(i), la, la2s(la)); + + cec_msg_init(&msg, la, i); + cec_msg_get_cec_version(&msg, true); + fail_on_test(doioctl(node, CEC_TRANSMIT, &msg)); + printf("\t\tCEC Version : "); + if (msg.status) + warn("%s\n", status2s(msg.status).c_str()); + else + printf("%s\n", version2s(msg.msg[2])); + + cec_msg_init(&msg, la, i); + cec_msg_give_physical_addr(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + printf("\t\tPhysical Address : "); + if (msg.status) { + warn("\n%s\n", status2s(msg.status).c_str()); + } else { + __u16 phys_addr = (msg.msg[2] << 8) | msg.msg[3]; + + printf("%x.%x.%x.%x\n", + phys_addr >> 12, (phys_addr >> 8) & 0xf, + (phys_addr >> 4) & 0xf, phys_addr & 0xf); + printf("\t\tPrimary Device Type : %s\n", + prim_type2s(msg.msg[4])); + } + + cec_msg_init(&msg, la, i); + cec_msg_give_device_vendor_id(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + printf("\t\tVendor ID : "); + if (msg.status) + warn("\n%s\n", status2s(msg.status).c_str()); + else + printf("0x%02x%02x%02x\n", + msg.msg[2], msg.msg[3], msg.msg[4]); + + cec_msg_init(&msg, la, i); + cec_msg_give_device_power_status(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + printf("\t\tPower Status : "); + if (msg.status) + warn("\n%s\n", status2s(msg.status).c_str()); + else + printf("%s\n", power_status2s(msg.msg[2])); + + cec_msg_init(&msg, la, i); + cec_msg_give_osd_name(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + cec_ops_set_osd_name(&msg, osd_name); + printf("\t\tOSD Name : "); + if (msg.status) + warn("\n%s\n", status2s(msg.status).c_str()); + else + printf("%s\n", osd_name); + return 0; +} + +static int testTopology(struct node *node) +{ + struct cec_msg msg = { }; + struct cec_log_addrs laddrs = { }; + + fail_on_test(doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs)); + + if (!(node->caps & CEC_CAP_IO)) { + cec_msg_init(&msg, 0xf, 0); + fail_on_test(doioctl(node, CEC_TRANSMIT, &msg) != ENOTTY); + return -ENOTTY; + } + + for (unsigned i = 0; i < 15; i++) { + int ret; + + cec_msg_init(&msg, 0xf, i); + ret = doioctl(node, CEC_TRANSMIT, &msg); + + switch (msg.status) { + case CEC_TX_STATUS_OK: + fail_on_test(testTopologyDevice(node, i, laddrs.log_addr[0])); + break; + case CEC_TX_STATUS_ARB_LOST: + warn("tx arbitration lost for addr %d\n", i); + break; + case CEC_TX_STATUS_RETRY_TIMEOUT: + break; + default: + return fail("ret ? %d\n", ret); + } + } + return 0; +} + +static int testARC(struct node *node) +{ + struct cec_msg msg = { }; + struct cec_log_addrs laddrs = { }; + unsigned la; + unsigned i; + + fail_on_test(doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs)); + la = laddrs.log_addr[0]; + + for (i = 0; i < 15; i++) { + if (i == la) + continue; + cec_msg_init(&msg, la, i); + cec_msg_initiate_arc(&msg, true); + fail_on_test(doioctl(node, CEC_TRANSMIT, &msg)); + if (msg.status == 0) { + cec_msg_init(&msg, la, i); + cec_msg_terminate_arc(&msg, true); + msg.reply = CEC_MSG_REPORT_ARC_TERMINATED; + fail_on_test(doioctl(node, CEC_TRANSMIT, &msg)); + fail_on_test(msg.status); + printf("logical address %d supports ARC\n", i); + } else { + printf("logical address %d doesn't support ARC\n", i); + } + } + return 0; +} + +int main(int argc, char **argv) +{ + const char *device = "/dev/cec0"; /* -d device */ + char short_options[26 * 2 * 2 + 1]; + __u32 vendor_id; + __u32 set_vendor_id = 0x000c03; /* HDMI LLC vendor ID */ + __u16 phys_addr; + __u16 set_phys_addr = 0; + int idx = 0; + int fd = -1; + int ch; + int i; + + for (i = 0; long_options[i].name; i++) { + if (!isalpha(long_options[i].val)) + continue; + short_options[idx++] = long_options[i].val; + if (long_options[i].has_arg == required_argument) + short_options[idx++] = ':'; + } + while (1) { + int option_index = 0; + + short_options[idx] = 0; + ch = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (ch == -1) + break; + + options[(int)ch] = 1; + switch (ch) { + case OptHelp: + usage(); + return 0; + case OptSetDevice: + device = optarg; + if (device[0] >= '0' && device[0] <= '9' && strlen(device) <= 3) { + static char newdev[20]; + + sprintf(newdev, "/dev/cec%s", device); + device = newdev; + } + break; + case OptNoWarnings: + show_warnings = false; + break; + case OptVerbose: + show_info = true; + break; + case OptPhysAddr: + set_phys_addr = strtoul(optarg, NULL, 0); + break; + case OptVendorID: + set_vendor_id = strtoul(optarg, NULL, 0) & 0x00ffffff; + break; + case OptSwitch: + if (options[OptCDCOnly] || options[OptUnregistered]) { + fprintf(stderr, "--switch cannot be combined with --cdc-only or --unregistered.\n"); + usage(); + return 1; + } + break; + case OptCDCOnly: + if (options[OptSwitch] || options[OptUnregistered]) { + fprintf(stderr, "--cdc-only cannot be combined with --switch or --unregistered.\n"); + usage(); + return 1; + } + break; + case OptUnregistered: + if (options[OptCDCOnly] || options[OptSwitch]) { + fprintf(stderr, "--unregistered cannot be combined with --cdc-only or --switch.\n"); + usage(); + return 1; + } + break; + case ':': + fprintf(stderr, "Option '%s' requires a value\n", + argv[optind]); + usage(); + return 1; + case '?': + if (argv[optind]) + fprintf(stderr, "Unknown argument '%s'\n", argv[optind]); + usage(); + return 1; + } + } + if (optind < argc) { + printf("unknown arguments: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(); + return 1; + } + + if ((fd = open(device, O_RDWR)) < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + + struct node node; + struct cec_caps caps = { }; + + node.fd = fd; + node.device = device; + doioctl(&node, CEC_ADAP_G_CAPS, &caps); + node.caps = caps.capabilities; + node.available_log_addrs = caps.available_log_addrs; + + unsigned flags = 0; + const char *osd_name; + + if (options[OptTV]) + osd_name = "TV"; + else if (options[OptRecord]) + osd_name = "Record"; + else if (options[OptPlayback]) + osd_name = "Playback"; + else if (options[OptTuner]) + osd_name = "Tuner"; + else if (options[OptAudio]) + osd_name = "Audio System"; + else if (options[OptProcessor]) + osd_name = "Processor"; + else if (options[OptSwitch] || options[OptCDCOnly] || options[OptUnregistered]) + osd_name = ""; + else + osd_name = "TV"; + + if (options[OptTV]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_TV; + if (options[OptRecord]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_RECORD; + if (options[OptTuner]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_TUNER; + if (options[OptPlayback]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_PLAYBACK; + if (options[OptAudio]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM; + if (options[OptProcessor]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_PROCESSOR; + if (options[OptSwitch] || options[OptCDCOnly] || options[OptUnregistered]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_SWITCH; + if (flags == 0) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_TV; + + printf("Driver Info:\n"); + printf("\tCapabilities : 0x%08x\n", caps.capabilities); + printf("%s", caps2s(caps.capabilities).c_str()); + printf("\tInputs : %u\n", caps.ninputs); + printf("\tAvailable Logical Addresses: %u\n", + caps.available_log_addrs); + + doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &phys_addr); + printf("\tPhysical Address : %x.%x.%x.%x\n", + phys_addr >> 12, (phys_addr >> 8) & 0xf, + (phys_addr >> 4) & 0xf, phys_addr & 0xf); + if (!options[OptPhysAddr] && phys_addr == CEC_PHYS_ADDR_INVALID && + (node.caps & CEC_CAP_PHYS_ADDR)) + warn("Perhaps you should use option --phys-addr?\n"); + + doioctl(&node, CEC_ADAP_G_VENDOR_ID, &vendor_id); + if (vendor_id != CEC_VENDOR_ID_NONE) + printf("\tVendor ID : 0x%06x\n", vendor_id); + + __u32 adap_state; + doioctl(&node, CEC_ADAP_G_STATE, &adap_state); + printf("\tAdapter State : %s\n", adap_state ? "Enabled" : "Disabled"); + + struct cec_log_addrs laddrs = { }; + doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs); + printf("\tCEC Version : %s\n", version2s(laddrs.cec_version)); + printf("\tLogical Addresses : %u\n", laddrs.num_log_addrs); + for (unsigned i = 0; i < laddrs.num_log_addrs; i++) { + printf("\t Logical Address : %d\n", + laddrs.log_addr[i]); + printf("\t Primary Device Type : %s\n", + prim_type2s(laddrs.primary_device_type[i])); + printf("\t Logical Address Type : %s\n", + 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", + 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\n", + 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\n", + dev_feat2s(byte).c_str()); + } + if (byte & CEC_OP_FEAT_EXT) + continue; + if (!is_dev_feat) + is_dev_feat = true; + else + break; + } + } + + printf("\nCompliance test for device %s:\n\n", device); + + /* Required ioctls */ + + printf("Required ioctls:\n"); + printf("\ttest CEC_ADAP_G_CAPS: %s\n", ok(testCap(&node))); + if (options[OptPhysAddr] || phys_addr == CEC_PHYS_ADDR_INVALID) + phys_addr = set_phys_addr; + printf("\ttest CEC_ADAP_G/S_PHYS_ADDR: %s\n", ok(testAdapPhysAddr(&node, phys_addr))); + if (options[OptVendorID] || vendor_id == CEC_VENDOR_ID_NONE) + vendor_id = set_vendor_id; + printf("\ttest CEC_ADAP_G/S_VENDOR_ID: %s\n", ok(testVendorID(&node, vendor_id))); + printf("\ttest CEC_ADAP_G/S_STATE: %s\n", ok(testAdapState(&node))); + printf("\ttest CEC_ADAP_G/S_LOG_ADDRS: %s\n", ok(testAdapLogAddrs(&node, flags, osd_name))); + printf("\ttest CEC topology discovery: %s\n", ok(testTopology(&node))); + printf("\ttest CEC ARC: %s\n", ok(testARC(&node))); + printf("\n"); + + /* Final test report */ + + close(fd); + printf("Total: %d, Succeeded: %d, Failed: %d, Warnings: %d\n", + tests_total, tests_ok, tests_total - tests_ok, warnings); + exit(app_result); +} diff --git a/utils/cec-compliance/cec-compliance.h b/utils/cec-compliance/cec-compliance.h new file mode 100644 index 0000000..cd02c24 --- /dev/null +++ b/utils/cec-compliance/cec-compliance.h @@ -0,0 +1,87 @@ +/* + CEC API compliance test tool. + + Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + Author: Hans Verkuil hans.verkuil@cisco.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + */ + +#ifndef _CEC_COMPLIANCE_H_ +#define _CEC_COMPLIANCE_H_ + +#include <stdarg.h> +#include <cerrno> +#include <string> +#include <linux/cec-funcs.h> + +extern bool show_info; +extern bool show_warnings; +extern unsigned warnings; + +struct node { + int fd; + const char *device; + unsigned caps; + unsigned available_log_addrs; + unsigned num_log_addrs; + __u8 log_addr[CEC_MAX_LOG_ADDRS]; +}; + +#define info(fmt, args...) \ + do { \ + if (show_info) \ + printf("\t\tinfo: " fmt, ##args); \ + } while (0) + +#define warn(fmt, args...) \ + do { \ + warnings++; \ + if (show_warnings) \ + printf("\t\twarn: %s(%d): " fmt, __FILE__, __LINE__, ##args); \ + } while (0) + +#define warn_once(fmt, args...) \ + do { \ + static bool show; \ + \ + if (!show) { \ + show = true; \ + warnings++; \ + if (show_warnings) \ + printf("\t\twarn: %s(%d): " fmt, \ + __FILE__, __LINE__, ##args); \ + } \ + } while (0) + +#define fail(fmt, args...) \ +({ \ + printf("\t\tfail: %s(%d): " fmt, __FILE__, __LINE__, ##args); \ + 1; \ +}) + +#define fail_on_test(test) \ + do { \ + if (test) \ + return fail("%s\n", #test); \ + } while (0) + +int cec_named_ioctl(int fd, const char *name, + unsigned long int request, void *parm); + +#define doioctl(n, r, p) cec_named_ioctl((n)->fd, #r, r, p) + +const char *ok(int res); + +// CEC core tests +int testCore(struct node *node); + +#endif
Generic CEC utility that can be used to send/receive/monitor CEC messages.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- configure.ac | 1 + utils/Makefile.am | 1 + utils/cec-ctl/Makefile.am | 8 + utils/cec-ctl/cec-ctl.cpp | 1296 +++++++++++++++++++++++++++++++++++++++++++++ utils/cec-ctl/msg2ctl.pl | 430 +++++++++++++++ 5 files changed, 1736 insertions(+) create mode 100644 utils/cec-ctl/Makefile.am create mode 100644 utils/cec-ctl/cec-ctl.cpp create mode 100644 utils/cec-ctl/msg2ctl.pl
diff --git a/configure.ac b/configure.ac index 12c2eb9..72d59bd 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,7 @@ AC_CONFIG_FILES([Makefile utils/media-ctl/Makefile utils/rds/Makefile utils/cec-compliance/Makefile + utils/cec-ctl/Makefile utils/v4l2-compliance/Makefile utils/v4l2-ctl/Makefile utils/v4l2-dbg/Makefile diff --git a/utils/Makefile.am b/utils/Makefile.am index c78e97b..617abf1 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -6,6 +6,7 @@ SUBDIRS = \ keytable \ media-ctl \ cec-compliance \ + cec-ctl \ v4l2-compliance \ v4l2-ctl \ v4l2-dbg \ diff --git a/utils/cec-ctl/Makefile.am b/utils/cec-ctl/Makefile.am new file mode 100644 index 0000000..378d7db --- /dev/null +++ b/utils/cec-ctl/Makefile.am @@ -0,0 +1,8 @@ +bin_PROGRAMS = cec-ctl + +cec_ctl_SOURCES = cec-ctl.cpp + +cec-ctl.cpp: cec-ctl-gen.h + +cec-ctl-gen.h: msg2ctl.pl ../../include/linux/cec.h ../../include/linux/cec-funcs.h + msg2ctl.pl ../../include/linux/cec.h ../../include/linux/cec-funcs.h >cec-ctl-gen.h diff --git a/utils/cec-ctl/cec-ctl.cpp b/utils/cec-ctl/cec-ctl.cpp new file mode 100644 index 0000000..6a1edb5 --- /dev/null +++ b/utils/cec-ctl/cec-ctl.cpp @@ -0,0 +1,1296 @@ +/* + Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + Author: Hans Verkuil hans.verkuil@cisco.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + */ + +#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 <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <stdarg.h> +#include <cerrno> +#include <string> +#include <vector> +#include <linux/cec-funcs.h> +#include <config.h> + +#define CEC_MAX_ARGS 16 + +#define xstr(s) str(s) +#define str(s) #s + +struct cec_enum_values { + const char *type_name; + __u8 value; +}; + +enum cec_types { + CEC_TYPE_U8, + CEC_TYPE_U16, + CEC_TYPE_U32, + CEC_TYPE_STRING, + CEC_TYPE_ENUM, +}; + +struct arg { + enum cec_types type; + __u8 num_enum_values; + const struct cec_enum_values *values; +}; + +static const struct arg arg_u8 = { + CEC_TYPE_U8, +}; + +static const struct arg arg_u16 = { + CEC_TYPE_U16, +}; + +static const struct arg arg_u32 = { + CEC_TYPE_U32, +}; + +static const struct arg arg_string = { + CEC_TYPE_STRING, +}; + +static const struct cec_enum_values type_ui_cmd[] = { + { "Select", 0x00 }, + { "Up", 0x01 }, + { "Down", 0x02 }, + { "Left", 0x03 }, + { "Right", 0x04 }, + { "Right-Up", 0x05 }, + { "Right-Down", 0x06 }, + { "Left-Up", 0x07 }, + { "Left-Down", 0x08 }, + { "Device Root Menu", 0x09 }, + { "Device Setup Menu", 0x0a }, + { "Contents Menu", 0x0b }, + { "Favorite Menu", 0x0c }, + { "Back", 0x0d }, + { "Media Top Menu", 0x10 }, + { "Media Context-sensitive Menu", 0x11 }, + { "Number Entry Mode", 0x1d }, + { "Number 11", 0x1e }, + { "Number 12", 0x1f }, + { "Number 0 or Number 10", 0x20 }, + { "Number 1", 0x21 }, + { "Number 2", 0x22 }, + { "Number 3", 0x23 }, + { "Number 4", 0x24 }, + { "Number 5", 0x25 }, + { "Number 6", 0x26 }, + { "Number 7", 0x27 }, + { "Number 8", 0x28 }, + { "Number 9", 0x29 }, + { "Dot", 0x2a }, + { "Enter", 0x2b }, + { "Clear", 0x2c }, + { "Next Favorite", 0x2f }, + { "Channel Up", 0x30 }, + { "Channel Down", 0x31 }, + { "Previous Channel", 0x32 }, + { "Sound Select", 0x33 }, + { "Input Select", 0x34 }, + { "Display Information", 0x35 }, + { "Help", 0x36 }, + { "Page Up", 0x37 }, + { "Page Down", 0x38 }, + { "Power", 0x40 }, + { "Volume Up", 0x41 }, + { "Volume Down", 0x42 }, + { "Mute", 0x43 }, + { "Play", 0x44 }, + { "Stop", 0x45 }, + { "Pause", 0x46 }, + { "Record", 0x47 }, + { "Rewind", 0x48 }, + { "Fast forward", 0x49 }, + { "Eject", 0x4a }, + { "Skip Forward", 0x4b }, + { "Skip Backward", 0x4c }, + { "Stop-Record", 0x4d }, + { "Pause-Record", 0x4e }, + { "Angle", 0x50 }, + { "Sub picture", 0x51 }, + { "Video on Demand", 0x52 }, + { "Electronic Program Guide", 0x53 }, + { "Timer Programming", 0x54 }, + { "Initial Configuration", 0x55 }, + { "Select Broadcast Type", 0x56 }, + { "Select Sound Presentation", 0x57 }, + { "Audio Description", 0x58 }, + { "Internet", 0x59 }, + { "3D Mode", 0x5a }, + { "Play Function", 0x60 }, + { "Pause-Play Function", 0x61 }, + { "Record Function", 0x62 }, + { "Pause-Record Function", 0x63 }, + { "Stop Function", 0x64 }, + { "Mute Function", 0x65 }, + { "Restore Volume Function", 0x66 }, + { "Tune Function", 0x67 }, + { "Select Media Function", 0x68 }, + { "Select A/V Input Function", 0x69 }, + { "Select Audio Input Function", 0x6a }, + { "Power Toggle Function", 0x6b }, + { "Power Off Function", 0x6c }, + { "Power On Function", 0x6d }, + { "F1 (Blue)", 0x71 }, + { "F2 (Red)", 0x72 }, + { "F3 (Green)", 0x73 }, + { "F4 (Yellow)", 0x74 }, + { "F5", 0x75 }, + { "Data", 0x76 }, +}; + +static const struct arg arg_rc_ui_cmd = { + CEC_TYPE_ENUM, sizeof(type_ui_cmd) / sizeof(type_ui_cmd[0]), type_ui_cmd +}; + +struct message { + __u8 msg; + unsigned option; + __u8 num_args; + const char *arg_names[CEC_MAX_ARGS+1]; + const struct arg *args[CEC_MAX_ARGS]; + const char *msg_name; +}; + +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 int 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 arg *a) +{ + if (isdigit(*value)) + return strtoul(optarg, 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; +} + +static char options[512]; + +static std::string status2s(unsigned stat) +{ + std::string s; + + if (stat & CEC_TX_STATUS_ARB_LOST) + s += "ArbitrationLost "; + if (stat & CEC_TX_STATUS_REPLY_TIMEOUT) + s += "ReplyTimeout "; + if (stat & CEC_TX_STATUS_RETRY_TIMEOUT) + s += "RetryTimeout "; + if (stat & CEC_TX_STATUS_FEATURE_ABORT) + s += "FeatureAbort "; + return s; +} + +static void log_arg(const struct arg *arg, const char *arg_name, __u32 val) +{ + unsigned i; + + switch (arg->type) { + case CEC_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_TYPE_U8: + printf("\t%s: %u (0x%02x)\n", arg_name, val, val); + return; + case CEC_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_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 arg *arg, const char *arg_name, + const char *s) +{ + switch (arg->type) { + case CEC_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_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 arg arg_rec_src_type = { + CEC_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 arg *arg, const char *arg_name, const __u8 *p); + +#include "cec-ctl-gen.h" + +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 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); +} + +/* Short option list + + Please keep in alphabetical order. + That makes it easier to see which short options are still free. + + In general the lower case is used to set something and the upper + case is used to retrieve a setting. */ +enum Option { + OptPhysAddr = 'a', + OptClear = 'C', + OptSetDevice = 'd', + OptAdapDisable = 'D', + OptAdapEnable = 'E', + OptFrom = 'f', + OptHelp = 'h', + OptMonitor = 'm', + OptNoReply = 'n', + OptShowTopology = 'S', + OptTo = 't', + OptTrace = 'T', + OptVerbose = 'v', + OptVendorID = 'V', + + OptTV = 128, + OptRecord, + OptTuner, + OptPlayback, + OptAudio, + OptProcessor, + OptSwitch, + OptCDCOnly, + OptUnregistered, + OptCECVersion1_4, + CEC_FEATURE_OPTIONS +}; + +struct node { + int fd; + const char *device; + unsigned caps; + unsigned available_log_addrs; + unsigned num_log_addrs; + __u8 log_addr[CEC_MAX_LOG_ADDRS]; +}; + +#define doioctl(n, r, p) cec_named_ioctl((n)->fd, #r, r, p) + +bool show_info; + +typedef std::vector<cec_msg> msg_vec; + +static const struct message *opt2message[OptLast - OptMessages]; + +static void init_messages() +{ + for (unsigned i = 0; messages[i].msg_name; i++) + opt2message[messages[i].option - OptMessages] = &messages[i]; +} + +static struct option long_options[] = { + { "device", required_argument, 0, OptSetDevice }, + { "help", no_argument, 0, OptHelp }, + { "trace", no_argument, 0, OptTrace }, + { "verbose", no_argument, 0, OptVerbose }, + { "phys-addr", required_argument, 0, OptPhysAddr }, + { "vendor-id", required_argument, 0, OptVendorID }, + { "cec-version-1.4", no_argument, 0, OptCECVersion1_4 }, + { "enable", no_argument, 0, OptAdapEnable }, + { "disable", no_argument, 0, OptAdapDisable }, + { "clear", no_argument, 0, OptClear }, + { "monitor", no_argument, 0, OptMonitor }, + { "no-reply", no_argument, 0, OptNoReply }, + { "to", required_argument, 0, OptTo }, + { "from", required_argument, 0, OptFrom }, + { "show-topology", no_argument, 0, OptShowTopology }, + + { "tv", no_argument, 0, OptTV }, + { "record", no_argument, 0, OptRecord }, + { "tuner", no_argument, 0, OptTuner }, + { "playback", no_argument, 0, OptPlayback }, + { "audio", no_argument, 0, OptAudio }, + { "processor", no_argument, 0, OptProcessor }, + { "switch", no_argument, 0, OptSwitch }, + { "cdc-only", no_argument, 0, OptCDCOnly }, + { "unregistered", no_argument, 0, OptUnregistered }, + { "help-all", no_argument, 0, OptHelpAll }, + + CEC_LONG_OPTS + + { 0, 0, 0, 0 } +}; + +static void usage(void) +{ + printf("Usage:\n" + " -d, --device=<dev> Use device <dev> instead of /dev/cec0\n" + " If <dev> starts with a digit, then /dev/cec<dev> is used.\n" + " -h, --help Display this help message\n" + " -T, --trace Trace all called ioctls.\n" + " -v, --verbose Turn on verbose reporting.\n" + " -a, --phys-addr=<addr>\n" + " Use this physical address.\n" + " -D, --disable Disable CEC adapter.\n" + " -E, --enable Enable CEC adapter.\n" + " -C, --clear Clear all logical addresses.\n" + " -m, --monitor Monitor CEC traffic.\n" + " -V, --vendor-id=<id>\n" + " Use this vendor ID.\n" + " -n, --no-reply Don't wait for a reply.\n" + " -S, --show-topology Show the CEC topology.\n" + " -t, --to=<la> Send message to the given logical address.\n" + " -f, --from=<la> Send message from the given logical address.\n" + " By default use the first assigned logical address.\n" + " --cec-version-1.4 Use CEC Version 1.4 instead of 2.0\n" + " --tv This is a TV\n" + " --record This is a recording device\n" + " --tuner This is a tuner device\n" + " --playback This is a playback device\n" + " --audio This is an audio system device\n" + " --processor This is a processor device\n" + " --switch This is a pure CEC switch\n" + " --cdc-only This is a CDC-only device\n" + " --unregistered This is an unregistered device\n" + "\n" + " --help-all Show all messages\n" + CEC_USAGE + ); +} + +static std::string caps2s(unsigned caps) +{ + std::string s; + + if (caps & CEC_CAP_STATE) + s += "\t\tState\n"; + 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_IO) + s += "\t\tI/O\n"; + if (caps & CEC_CAP_VENDOR_ID) + s += "\t\tVendor ID\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_ARC) + s += "\t\tAudio Return Channel\n"; + if (caps & CEC_CAP_CDC_HPD) + s += "\t\tCapability Discovery and Control HPD\n"; + if (caps & CEC_CAP_IS_SOURCE) + s += "\t\tIs Source\n"; + return s; +} + +static const char *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"; + } +} + +static const char *power_status2s(unsigned status) +{ + switch (status) { + case CEC_OP_POWER_STATUS_ON: + return "On"; + case CEC_OP_POWER_STATUS_STANDBY: + return "Standby"; + case CEC_OP_POWER_STATUS_TO_ON: + return "In Transition Standby to On"; + case CEC_OP_POWER_STATUS_TO_STANDBY: + return "In Transition On to Standby"; + default: + return "Unknown"; + } +} + +static const char *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"; + } +} + +static const char *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"; + } +} + +static const char *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 "Playback 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"; + } +} + +static std::string 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; +} + +static std::string rc_src_prof2s(unsigned prof) +{ + std::string s; + + prof &= 0x1f; + if (prof == 0) + return "\t\tNone\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU) + s += "\t\tSource Has Device Root Menu\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU) + s += "\t\tSource Has Device Setup Menu\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU) + s += "\t\tSource Has Contents Menu\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU) + s += "\t\tSource Has Media Top Menu\n"; + if (prof & CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU) + s += "\t\tSource Has Media Context-Sensitive Menu\n"; + return s; +} + +static std::string dev_feat2s(unsigned feat) +{ + std::string s; + + feat &= 0x3e; + if (feat == 0) + return "\t\tNone\n"; + if (feat & CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN) + s += "\t\tTV Supports <Record TV Screen>\n"; + if (feat & CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING) + s += "\t\tTV Supports <Set OSD String>\n"; + if (feat & CEC_OP_FEAT_DEV_HAS_DECK_CONTROL) + s += "\t\tSupports Deck Control\n"; + if (feat & CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE) + s += "\t\tSource Supports <Set Audio Rate>\n"; + if (feat & CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX) + s += "\t\tSink Supports ARC Tx\n"; + if (feat & CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX) + s += "\t\tSource Supports ARC Rx\n"; + return s; +} + +static const char *event_state2s(__u8 state) +{ + switch (state) { + case CEC_EVENT_STATE_DISABLED: + return "Disabled"; + case CEC_EVENT_STATE_UNCONFIGURED: + return "Unconfigured"; + case CEC_EVENT_STATE_CONFIGURING: + return "Configuring"; + case CEC_EVENT_STATE_CONFIGURED: + return "Configured"; + default: + return "Unknown"; + } +} + +int cec_named_ioctl(int fd, const char *name, + unsigned long int request, void *parm) +{ + int retval = ioctl(fd, request, parm); + int e; + + e = retval == 0 ? 0 : errno; + if (options[OptTrace]) + printf("\t\t%s returned %d (%s)\n", + name, retval, strerror(e)); + + return retval == -1 ? e : (retval ? -1 : 0); +} + +static void log_event(struct cec_event &ev) +{ + switch (ev.event) { + case CEC_EVENT_STATE_CHANGE: + printf("Event: State Change: %s\n", + event_state2s(ev.state_change.state)); + break; + case CEC_EVENT_INPUTS_CHANGE: + printf("Event: Inputs Change: Connected: 0x%04x Changed: 0x%04x\n", + ev.inputs_change.connected_inputs, + ev.inputs_change.changed_inputs); + break; + case CEC_EVENT_LOST_MSGS: + printf("Event: Lost Messages\n"); + break; + default: + printf("Event: Unknown (0x%x)\n", ev.event); + break; + } + printf("\tTimestamp: %llu.%09llus\n", ev.ts / 1000000000, ev.ts % 1000000000); +} + +static int showTopologyDevice(struct node *node, unsigned i, unsigned la) +{ + struct cec_msg msg; + char osd_name[15]; + + printf("\tSystem Information for device %d (%s) from device %d (%s):\n", + i, la2s(i), la, la2s(la)); + + cec_msg_init(&msg, la, i); + cec_msg_get_cec_version(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + printf("\t\tCEC Version : %s\n", + msg.status ? status2s(msg.status).c_str() : version2s(msg.msg[2])); + + cec_msg_init(&msg, la, i); + cec_msg_give_physical_addr(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + printf("\t\tPhysical Address : "); + if (msg.status) { + printf("%s\n", status2s(msg.status).c_str()); + } else { + __u16 phys_addr = (msg.msg[2] << 8) | msg.msg[3]; + + printf("%x.%x.%x.%x\n", + phys_addr >> 12, (phys_addr >> 8) & 0xf, + (phys_addr >> 4) & 0xf, phys_addr & 0xf); + printf("\t\tPrimary Device Type : %s\n", + prim_type2s(msg.msg[4])); + } + + cec_msg_init(&msg, la, i); + cec_msg_give_device_vendor_id(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + printf("\t\tVendor ID : "); + if (msg.status) + printf("%s\n", status2s(msg.status).c_str()); + else + printf("0x%02x%02x%02x\n", + msg.msg[2], msg.msg[3], msg.msg[4]); + + cec_msg_init(&msg, la, i); + cec_msg_give_device_power_status(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + printf("\t\tPower Status : %s\n", + msg.status ? status2s(msg.status).c_str() : power_status2s(msg.msg[2])); + + cec_msg_init(&msg, la, i); + cec_msg_give_osd_name(&msg, true); + doioctl(node, CEC_TRANSMIT, &msg); + cec_ops_set_osd_name(&msg, osd_name); + printf("\t\tOSD Name : %s\n", + msg.status ? status2s(msg.status).c_str() : osd_name); + return 0; +} + +static int showTopology(struct node *node) +{ + struct cec_msg msg = { }; + struct cec_log_addrs laddrs = { }; + + if (!(node->caps & CEC_CAP_IO)) + return -ENOTTY; + + doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs); + + for (unsigned i = 0; i < 15; i++) { + int ret; + + cec_msg_init(&msg, 0xf, i); + ret = doioctl(node, CEC_TRANSMIT, &msg); + + switch (msg.status) { + case CEC_TX_STATUS_OK: + showTopologyDevice(node, i, laddrs.log_addr[0]); + break; + case CEC_TX_STATUS_ARB_LOST: + if (show_info) + printf("\t\ttx arbitration lost for addr %d\n", i); + break; + case CEC_TX_STATUS_RETRY_TIMEOUT: + break; + default: + if (show_info) + printf("\t\tunknown status %d\n", ret); + break; + } + } + return 0; +} + +int main(int argc, char **argv) +{ + const char *device = "/dev/cec0"; /* -d device */ + const message *opt; + msg_vec msgs; + char short_options[26 * 2 * 2 + 1]; + __u32 vendor_id; + __u32 adap_state; + __u16 phys_addr; + __u8 from = 0, to = 0; + bool reply = true; + int idx = 0; + int fd = -1; + int ch; + int i; + + init_messages(); + + for (i = 0; long_options[i].name; i++) { + if (!isalpha(long_options[i].val)) + continue; + short_options[idx++] = long_options[i].val; + if (long_options[i].has_arg == required_argument) + short_options[idx++] = ':'; + } + while (1) { + int option_index = 0; + struct cec_msg msg; + + short_options[idx] = 0; + ch = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (ch == -1) + break; + + if (ch > OptMessages) + cec_msg_init(&msg, 0, 0); + options[(int)ch] = 1; + + switch (ch) { + case OptHelp: + usage(); + return 0; + case OptSetDevice: + device = optarg; + if (device[0] >= '0' && device[0] <= '9' && strlen(device) <= 3) { + static char newdev[20]; + + sprintf(newdev, "/dev/cec%s", device); + device = newdev; + } + break; + case OptVerbose: + show_info = true; + break; + case OptFrom: + from = strtoul(optarg, NULL, 0) & 0xf; + break; + case OptTo: + to = strtoul(optarg, NULL, 0) & 0xf; + break; + case OptNoReply: + reply = false; + break; + case OptPhysAddr: + phys_addr = strtoul(optarg, NULL, 0); + break; + case OptVendorID: + vendor_id = strtoul(optarg, NULL, 0) & 0x00ffffff; + break; + case OptAdapDisable: + adap_state = CEC_ADAP_DISABLED; + break; + case OptAdapEnable: + adap_state = CEC_ADAP_ENABLED; + break; + case OptSwitch: + if (options[OptCDCOnly] || options[OptUnregistered]) { + fprintf(stderr, "--switch cannot be combined with --cdc-only or --unregistered.\n"); + usage(); + return 1; + } + break; + case OptCDCOnly: + if (options[OptSwitch] || options[OptUnregistered]) { + fprintf(stderr, "--cdc-only cannot be combined with --switch or --unregistered.\n"); + usage(); + return 1; + } + break; + case OptUnregistered: + if (options[OptCDCOnly] || options[OptSwitch]) { + fprintf(stderr, "--unregistered cannot be combined with --cdc-only or --switch.\n"); + usage(); + return 1; + } + break; + case ':': + fprintf(stderr, "Option '%s' requires a value\n", + argv[optind]); + usage(); + return 1; + case '?': + if (argv[optind]) + fprintf(stderr, "Unknown argument '%s'\n", argv[optind]); + usage(); + return 1; + default: + if (ch >= OptHelpAll) { + usage_options(ch); + exit(0); + } + if (ch < OptMessages) + break; + opt = opt2message[ch - OptMessages]; + parse_msg_args(msg, reply, opt, ch); + msgs.push_back(msg); + break; + } + } + if (optind < argc) { + printf("unknown arguments: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(); + return 1; + } + + if ((fd = open(device, O_RDWR)) < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + + struct node node; + struct cec_caps caps = { }; + + node.fd = fd; + node.device = device; + doioctl(&node, CEC_ADAP_G_CAPS, &caps); + node.caps = caps.capabilities; + node.available_log_addrs = caps.available_log_addrs; + + unsigned flags = 0; + const char *osd_name; + + if (options[OptTV]) + osd_name = "TV"; + else if (options[OptRecord]) + osd_name = "Record"; + else if (options[OptPlayback]) + osd_name = "Playback"; + else if (options[OptTuner]) + osd_name = "Tuner"; + else if (options[OptAudio]) + osd_name = "Audio System"; + else if (options[OptProcessor]) + osd_name = "Processor"; + else if (options[OptSwitch] || options[OptCDCOnly] || options[OptUnregistered]) + osd_name = ""; + else + osd_name = "TV"; + + if (options[OptTV]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_TV; + if (options[OptRecord]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_RECORD; + if (options[OptTuner]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_TUNER; + if (options[OptPlayback]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_PLAYBACK; + if (options[OptAudio]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM; + if (options[OptProcessor]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_PROCESSOR; + if (options[OptSwitch] || options[OptCDCOnly] || options[OptUnregistered]) + flags |= 1 << CEC_OP_PRIM_DEVTYPE_SWITCH; + + printf("Driver Info:\n"); + printf("\tCapabilities : 0x%08x\n", caps.capabilities); + printf("%s", caps2s(caps.capabilities).c_str()); + printf("\tInputs : %u\n", caps.ninputs); + printf("\tAvailable Logical Addresses: %u\n", + caps.available_log_addrs); + + if ((node.caps & CEC_CAP_LOG_ADDRS) && options[OptClear]) { + struct cec_log_addrs laddrs = { }; + + doioctl(&node, CEC_ADAP_S_LOG_ADDRS, &laddrs); + } + + if ((node.caps & CEC_CAP_PHYS_ADDR) && options[OptPhysAddr]) + doioctl(&node, CEC_ADAP_S_PHYS_ADDR, &phys_addr); + doioctl(&node, CEC_ADAP_G_PHYS_ADDR, &phys_addr); + printf("\tPhysical Address : %x.%x.%x.%x\n", + phys_addr >> 12, (phys_addr >> 8) & 0xf, + (phys_addr >> 4) & 0xf, phys_addr & 0xf); + if (!options[OptPhysAddr] && phys_addr == 0xffff && + (node.caps & CEC_CAP_PHYS_ADDR)) + printf("Perhaps you should use option --phys-addr?\n"); + + if (node.caps & CEC_CAP_VENDOR_ID) { + if (!options[OptVendorID]) { + doioctl(&node, CEC_ADAP_G_VENDOR_ID, &vendor_id); + if (vendor_id == CEC_VENDOR_ID_NONE) + vendor_id = 0x000c03; /* HDMI LLC vendor ID */ + } + doioctl(&node, CEC_ADAP_S_VENDOR_ID, &vendor_id); + } + doioctl(&node, CEC_ADAP_G_VENDOR_ID, &vendor_id); + if (vendor_id != CEC_VENDOR_ID_NONE) + printf("\tVendor ID : 0x%06x\n", vendor_id); + + if ((node.caps & CEC_CAP_STATE) && + (options[OptAdapEnable] || options[OptAdapDisable])) + doioctl(&node, CEC_ADAP_S_STATE, &adap_state); + doioctl(&node, CEC_ADAP_G_STATE, &adap_state); + printf("\tAdapter State : %s\n", adap_state ? "Enabled" : "Disabled"); + if (adap_state == CEC_ADAP_DISABLED) + return 0; + + if ((node.caps & CEC_CAP_LOG_ADDRS) && flags) { + struct cec_log_addrs laddrs; + + memset(&laddrs, 0, sizeof(laddrs)); + doioctl(&node, CEC_ADAP_S_LOG_ADDRS, &laddrs); + memset(&laddrs, 0, sizeof(laddrs)); + + laddrs.cec_version = options[OptCECVersion1_4] ? + CEC_OP_CEC_VERSION_1_4 : CEC_OP_CEC_VERSION_2_0; + strcpy(laddrs.osd_name, osd_name); + + for (unsigned i = 0; i < 8; i++) { + unsigned la_type; + unsigned all_dev_type; + + if (!(flags & (1 << i))) + continue; + if (laddrs.num_log_addrs == node.available_log_addrs) { + fprintf(stderr, "Attempt to define too many logical addresses\n"); + exit(-1); + } + switch (i) { + case CEC_OP_PRIM_DEVTYPE_TV: + la_type = CEC_LOG_ADDR_TYPE_TV; + all_dev_type = CEC_OP_ALL_DEVTYPE_TV; + break; + case CEC_OP_PRIM_DEVTYPE_RECORD: + la_type = CEC_LOG_ADDR_TYPE_RECORD; + all_dev_type = CEC_OP_ALL_DEVTYPE_RECORD; + break; + case CEC_OP_PRIM_DEVTYPE_TUNER: + la_type = CEC_LOG_ADDR_TYPE_TUNER; + all_dev_type = CEC_OP_ALL_DEVTYPE_TUNER; + break; + case CEC_OP_PRIM_DEVTYPE_PLAYBACK: + la_type = CEC_LOG_ADDR_TYPE_PLAYBACK; + all_dev_type = CEC_OP_ALL_DEVTYPE_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: + la_type = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM; + all_dev_type = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; + break; + case CEC_OP_PRIM_DEVTYPE_PROCESSOR: + la_type = CEC_LOG_ADDR_TYPE_SPECIFIC; + all_dev_type = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + case CEC_OP_PRIM_DEVTYPE_SWITCH: + default: + la_type = CEC_LOG_ADDR_TYPE_UNREGISTERED; + all_dev_type = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + } + laddrs.log_addr_type[laddrs.num_log_addrs] = la_type; + laddrs.all_device_types[laddrs.num_log_addrs] = all_dev_type; + laddrs.primary_device_type[laddrs.num_log_addrs++] = i; + } + + doioctl(&node, CEC_ADAP_S_LOG_ADDRS, &laddrs); + } + + struct cec_log_addrs laddrs = { }; + doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs); + node.num_log_addrs = laddrs.num_log_addrs; + printf("\tCEC Version : %s\n", version2s(laddrs.cec_version)); + printf("\tLogical Addresses : %u\n", laddrs.num_log_addrs); + for (unsigned i = 0; i < laddrs.num_log_addrs; i++) { + printf("\n\t Logical Address : %d (%s)\n", + laddrs.log_addr[i], la2s(laddrs.log_addr[i])); + printf("\t Primary Device Type : %s\n", + prim_type2s(laddrs.primary_device_type[i])); + printf("\t Logical Address Type : %s\n", + 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", + 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\n", + 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", + dev_feat2s(byte).c_str()); + } + if (byte & CEC_OP_FEAT_EXT) + continue; + if (!is_dev_feat) + is_dev_feat = true; + else + break; + } + } + if (node.num_log_addrs == 0) { + if (options[OptMonitor]) + goto monitor; + return 0; + } + printf("\n"); + + if (!options[OptFrom]) + from = laddrs.log_addr[0]; + + if (options[OptShowTopology]) + showTopology(&node); + + for (msg_vec::iterator iter = msgs.begin(); iter != msgs.end(); ++iter) { + if (!options[OptTo]) { + fprintf(stderr, "attempting to send message without --to\n"); + exit(1); + } + struct cec_msg msg = *iter; + bool has_reply = msg.reply; + + printf("\nTransmit from %s to %s (%d to %d):\n", + la2s(from), to == 0xf ? "all" : la2s(to), from, to); + msg.msg[0] |= (from << 4) | to; + log_msg(msg); + if (doioctl(&node, CEC_TRANSMIT, &msg)) + continue; + if (has_reply) { + printf("Received from %s (%d):\n", la2s(cec_msg_initiator(&msg)), + cec_msg_initiator(&msg)); + log_msg(msg); + } else { + printf("\t%s\n", status2s(msg.status).c_str()); + } + printf("\tTimestamp: %llu.%09llus\n", msg.ts / 1000000000, msg.ts % 1000000000); + } + +monitor: + if (options[OptMonitor]) { + __u32 monitor = CEC_MONITOR_ENABLED; + fd_set rd_fds; + fd_set ex_fds; + int fd = node.fd; + + printf("\n"); + doioctl(&node, CEC_S_MONITOR, &monitor); + + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + while (1) { + int res; + + FD_ZERO(&rd_fds); + FD_ZERO(&ex_fds); + FD_SET(fd, &rd_fds); + FD_SET(fd, &ex_fds); + res = select(fd + 1, &rd_fds, NULL, &ex_fds, NULL); + if (res <= 0) + break; + if (FD_ISSET(fd, &rd_fds)) { + struct cec_msg msg = { }; + __u8 from, to; + + if (doioctl(&node, CEC_RECEIVE, &msg)) + continue; + + from = cec_msg_initiator(&msg); + to = cec_msg_destination(&msg); + printf("\nReceived from %s to %s (%d to %d):\n", + la2s(from), to == 0xf ? "all" : la2s(to), from, to); + log_msg(msg); + printf("\tTimestamp: %llu.%09llus\n", msg.ts / 1000000000, msg.ts % 1000000000); + } + if (FD_ISSET(fd, &ex_fds)) { + struct cec_event ev; + + if (doioctl(&node, CEC_DQEVENT, &ev)) + continue; + log_event(ev); + } + } + } + + close(fd); + return 0; +} diff --git a/utils/cec-ctl/msg2ctl.pl b/utils/cec-ctl/msg2ctl.pl new file mode 100644 index 0000000..af42905 --- /dev/null +++ b/utils/cec-ctl/msg2ctl.pl @@ -0,0 +1,430 @@ +#!/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 @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/; + } + 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:\n");\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"; + } 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 *") { + $logswitch .= ", $name"; + } else { + $logswitch .= ", &$name"; + } + } + $logswitch .= ");\n"; + $logswitch .= "\t\tprintf("$cec_msg:\n");\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 "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 "ui_cmd") { + $logswitch .= "\t\tlog_arg(&arg_rc_$name, "$dash_name", $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"; + 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"; + $cnt++; + if ($type =~ /char/) { + $switch .= "\t\t\t\t$name = value;\n"; + } elsif (scalar(@enum)) { + $switch .= "\t\t\t\t$name = parse_enum(value, opt->args[1]);\n"; + } else { + $switch .= "\t\t\t\t$name = strtol(value, 0L, 0);\n"; + } + $switch .= "\t\t\t\tbreak;\n"; + } + $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_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; +} + +while (<>) { + last if //* Commands *//; +} + +$comment = 0; +$has_also = 0; +$operand_name = ""; +$feature = ""; + +while (<>) { + chomp; + last if /_CEC_FUNCS_H/; + if (/^/*.*Feature */$/) { + ($feature) = /^/* (.*) 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_abort/; + 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"; + +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 .= $feature_usage{$_}; + } + 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_OPT_FEATURES \\n%s\n\n", $opt_features; +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; +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(service_id_method, dig_bcast_system, transport_id, service_id, orig_network_id, program_number, channel_number_fmt, major, minor)/g; +printf "%s", $switch; +printf "\t}\n};\n\n"; + +print <<'EOF'; +static void log_msg(struct cec_msg &msg) +{ + if (msg.len == 1) + printf("CEC_MSG_POLL:\n"); + if (msg.status && msg.status != CEC_TX_STATUS_FEATURE_ABORT) { + printf("\t%s\n", status2s(msg.status).c_str()); + return; + } + if (msg.len == 1) + return; + + switch (msg.msg[1]) { +EOF +printf "%s", $logswitch; +print <<'EOF'; + default: { + __u16 phys_addr = (msg.msg[2] << 8) | msg.msg[3]; + + printf("CDC MSG 0x%02x, %d bytes payload\n", msg.msg[4], msg.len - 5); + log_arg(&arg_u16, "phys-addr", phys_addr); + break; + } + } + break; + + default: + printf("MSG %02x, %d bytes payload\n", msg.msg[1], msg.len - 2); + break; + } +} +EOF
Oops, this cover letter is incomplete, here is the full cover letter:
This patch series adds two new utilities to the v4l-utils git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/). It assumes that the new CEC framework (v8) is available in the kernel:
http://www.mail-archive.com/linux-media@vger.kernel.org/msg91285.html
The first patch adds the new cec headers to the 'sync-with-kernel' target, the second syncs with the kernel and adds the new cec headers to v4l-utils, the third adds the compliance utility and the last adds the cec-ctl utility.
The cec-compliance utility is by no means 100% coverage, in particular the event API and non-blocking ioctls are untested. But it is a starting point, and a complex protocol like CEC really needs a compliance tool.
The cec-ctl utility has almost full CEC message coverage: all generated from the cec headers, so this is easy to keep up to date.
Regards,
Hans
Changes since v1:
- cec-ctl: added CEC message logging/monitoring. - cec-ctl: add support to clear the logical addresses. - cec-ctl: log events.
On 08/18/15 10:36, Hans Verkuil wrote:
Hans Verkuil (4): Makefile.am: copy cec headers with make sync-with-kernel sync-with-kernel cec-compliance: add new CEC compliance utility cec-ctl: CEC control utility
Makefile.am | 4 + configure.ac | 2 + contrib/freebsd/include/linux/input.h | 29 + contrib/freebsd/include/linux/v4l2-controls.h | 4 + include/linux/cec-funcs.h | 1771 +++++++++++++++++++++++++ include/linux/cec.h | 781 +++++++++++ include/linux/v4l2-controls.h | 4 + utils/Makefile.am | 2 + utils/cec-compliance/Makefile.am | 3 + utils/cec-compliance/cec-compliance.cpp | 926 +++++++++++++ utils/cec-compliance/cec-compliance.h | 87 ++ utils/cec-ctl/Makefile.am | 8 + utils/cec-ctl/cec-ctl.cpp | 1296 ++++++++++++++++++ utils/cec-ctl/msg2ctl.pl | 430 ++++++ utils/keytable/parse.h | 18 + utils/keytable/rc_keymaps/lme2510 | 132 +- utils/keytable/rc_maps.cfg | 1 + 17 files changed, 5432 insertions(+), 66 deletions(-) create mode 100644 include/linux/cec-funcs.h create mode 100644 include/linux/cec.h create mode 100644 utils/cec-compliance/Makefile.am create mode 100644 utils/cec-compliance/cec-compliance.cpp create mode 100644 utils/cec-compliance/cec-compliance.h create mode 100644 utils/cec-ctl/Makefile.am create mode 100644 utils/cec-ctl/cec-ctl.cpp create mode 100644 utils/cec-ctl/msg2ctl.pl
dri-devel@lists.freedesktop.org