All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities
@ 2015-08-18  8:36 Hans Verkuil
  2015-08-18  8:36 ` [PATCHv2 1/4] Makefile.am: copy cec headers with make sync-with-kernel Hans Verkuil
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Hans Verkuil @ 2015-08-18  8:36 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, m.szyprowski, linux-input, linux-samsung-soc, lars,
	kamil, linux

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

-- 
2.1.4


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCHv2 1/4] Makefile.am: copy cec headers with make sync-with-kernel
  2015-08-18  8:36 [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities Hans Verkuil
@ 2015-08-18  8:36 ` Hans Verkuil
  2015-08-18  8:36 ` [PATCHv2 2/4] sync-with-kernel Hans Verkuil
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2015-08-18  8:36 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, m.szyprowski, linux-input, linux-samsung-soc, lars,
	kamil, linux, Hans Verkuil

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
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCHv2 2/4] sync-with-kernel
  2015-08-18  8:36 [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities Hans Verkuil
  2015-08-18  8:36 ` [PATCHv2 1/4] Makefile.am: copy cec headers with make sync-with-kernel Hans Verkuil
@ 2015-08-18  8:36 ` Hans Verkuil
  2015-08-18  8:36 ` [PATCHv2 3/4] cec-compliance: add new CEC compliance utility Hans Verkuil
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2015-08-18  8:36 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, m.szyprowski, linux-input, linux-samsung-soc, lars,
	kamil, linux, Hans Verkuil

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
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCHv2 3/4] cec-compliance: add new CEC compliance utility
  2015-08-18  8:36 [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities Hans Verkuil
  2015-08-18  8:36 ` [PATCHv2 1/4] Makefile.am: copy cec headers with make sync-with-kernel Hans Verkuil
  2015-08-18  8:36 ` [PATCHv2 2/4] sync-with-kernel Hans Verkuil
@ 2015-08-18  8:36 ` Hans Verkuil
  2015-08-18  8:36 ` [PATCHv2 4/4] cec-ctl: CEC control utility Hans Verkuil
  2015-08-18  8:46   ` Hans Verkuil
  4 siblings, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2015-08-18  8:36 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, m.szyprowski, linux-input, linux-samsung-soc, lars,
	kamil, linux, Hans Verkuil

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
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCHv2 4/4] cec-ctl: CEC control utility
  2015-08-18  8:36 [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities Hans Verkuil
                   ` (2 preceding siblings ...)
  2015-08-18  8:36 ` [PATCHv2 3/4] cec-compliance: add new CEC compliance utility Hans Verkuil
@ 2015-08-18  8:36 ` Hans Verkuil
  2015-08-18  8:46   ` Hans Verkuil
  4 siblings, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2015-08-18  8:36 UTC (permalink / raw)
  To: linux-media
  Cc: dri-devel, m.szyprowski, linux-input, linux-samsung-soc, lars,
	kamil, linux, Hans Verkuil

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
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities
  2015-08-18  8:36 [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities Hans Verkuil
@ 2015-08-18  8:46   ` Hans Verkuil
  2015-08-18  8:36 ` [PATCHv2 2/4] sync-with-kernel Hans Verkuil
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2015-08-18  8:46 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: dri-devel, m.szyprowski, linux-input, linux-samsung-soc, lars,
	kamil, linux

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
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities
@ 2015-08-18  8:46   ` Hans Verkuil
  0 siblings, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2015-08-18  8:46 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: linux-samsung-soc, linux, kamil, dri-devel, lars, linux-input,
	m.szyprowski

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 mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-08-18  8:56 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-18  8:36 [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities Hans Verkuil
2015-08-18  8:36 ` [PATCHv2 1/4] Makefile.am: copy cec headers with make sync-with-kernel Hans Verkuil
2015-08-18  8:36 ` [PATCHv2 2/4] sync-with-kernel Hans Verkuil
2015-08-18  8:36 ` [PATCHv2 3/4] cec-compliance: add new CEC compliance utility Hans Verkuil
2015-08-18  8:36 ` [PATCHv2 4/4] cec-ctl: CEC control utility Hans Verkuil
2015-08-18  8:46 ` [PATCHv2 0/4] cec-ctl/compliance: new CEC utilities Hans Verkuil
2015-08-18  8:46   ` Hans Verkuil

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.