The lirc interface only passes raw IR. Teach the lirc bridge how to pass scancodes (along with their IR information) to userspace. This introduces a new LIRC_MODE_SCANCODE mode where decoded IR is represented as two u32. The first one signifies LIRC_MODE_SCANCODE, the IR protocol, repeat and toggle bits, and the next u32 the scancode. This can be enabled with LIRC_MODE_MODE2 at the same time so that raw IR and scancodes will all be read. By default LIRC_MODE_MODE2 is only enabled for raw IR devices so that user space does not get confused by the new scancode messages. It can be enabled if LIRC_MODE_SCANCODE is set using LIRC_SET_REC_MODE ioctl. FIXME: The keycode is not passed via the bridge, but only via the input interface. Maybe this should be changed. With this change every rc device will have a lirc interface, including those which only produce scancodes in which case LIRC_MODE_SCANCODE will be enabled (else the lirc device will never produce anything). Signed-off-by: Sean Young <sean@xxxxxxxx> --- .../DocBook/media/v4l/lirc_device_interface.xml | 31 +++++++++------- drivers/media/rc/Kconfig | 4 +-- drivers/media/rc/ir-lirc-codec.c | 42 +++++++++++++++++++++- drivers/media/rc/rc-core-priv.h | 4 +++ drivers/media/rc/rc-main.c | 15 +++++--- include/media/lirc.h | 8 +++++ include/media/rc-core.h | 1 + 7 files changed, 86 insertions(+), 19 deletions(-) diff --git a/Documentation/DocBook/media/v4l/lirc_device_interface.xml b/Documentation/DocBook/media/v4l/lirc_device_interface.xml index 25926bd..f92b5a5 100644 --- a/Documentation/DocBook/media/v4l/lirc_device_interface.xml +++ b/Documentation/DocBook/media/v4l/lirc_device_interface.xml @@ -6,10 +6,10 @@ <title>Introduction</title> <para>The LIRC device interface is a bi-directional interface for -transporting raw IR data between userspace and kernelspace. Fundamentally, +transporting IR data between userspace and kernelspace. Fundamentally, it is just a chardev (/dev/lircX, for X = 0, 1, 2, ...), with a number of standard struct file_operations defined on it. With respect to -transporting raw IR data to and fro, the essential fops are read, write +transporting IR data to and fro, the essential fops are read, write and ioctl.</para> <para>Example dmesg output upon a driver registering w/LIRC:</para> @@ -29,14 +29,19 @@ and ioctl.</para> <section id="lirc_read"> <title>LIRC read fop</title> -<para>The lircd userspace daemon reads raw IR data from the LIRC chardev. The -exact format of the data depends on what modes a driver supports, and what -mode has been selected. lircd obtains supported modes and sets the active mode -via the ioctl interface, detailed at <xref linkend="lirc_ioctl"/>. The generally -preferred mode is LIRC_MODE_MODE2, in which packets containing an int value -describing an IR signal are read from the chardev.</para> +<para>The data read from the chardev is IR. The format depends on the rec mode; +this is either in LIRC_MODE_MODE2, LIRC_MODE_SCANCODE or both. In MODE2, data +is read as 32 bit unsigned values. The highest 8 bits signifies the type: +LIRC_MODE2_PULSE, LIRC_MODE2_SPACE, LIRC_MODE2_FREQUENCY, LIRC_MODE2_TIMEOUT. +The lower 24 bits signify the value either in Hertz or nanoseconds. +</para> +<para>If LIRC_MODE_SCANCODE is enabled then the type in the highest 8 bits +is LIRC_MODE2_SCANCODE. The 24 bit signifies a repeat, 23 bit toggle set and +the lowest 8 bits is the rc protocol (see rc_type in rc-core.h). The next full +unsigned int is the scancode; there is no type in the highest 8 bits. +</para> +<para>The mode can be set and get using <xref linkend="lirc_ioctl"/>. </para> -<para>See also <ulink url="http://www.lirc.org/html/technical.html">http://www.lirc.org/html/technical.html</ulink> for more info.</para> </section> <section id="lirc_write"> @@ -82,10 +87,12 @@ on working with the default settings initially.</para> </listitem> </varlistentry> <varlistentry> - <term>LIRC_GET_REC_MODE</term> + <term>LIRC_{G,S}ET_REC_MODE</term> <listitem> - <para>Get supported receive modes. Only LIRC_MODE_MODE2 and LIRC_MODE_LIRCCODE - are supported by lircd.</para> + <para>Get or set the receive mode. Devices that support raw IR will + support LIRC_MODE_MODE2; all devices support LIRC_MODE_SCANCODE. Note + that both modes can be enabled by ORing. That way, both raw IR and + decoded scancodes can be read simultaneously.</para> </listitem> </varlistentry> <varlistentry> diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index efdd6f7..247c22c 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -13,7 +13,7 @@ config LIRC ---help--- Enable this option to build the Linux Infrared Remote Control (LIRC) core device interface driver. The LIRC - interface passes raw IR to and from userspace, where the + interface passes IR to and from userspace, where the LIRC daemon handles protocol decoding for IR reception and encoding for IR transmitting (aka "blasting"). @@ -24,7 +24,7 @@ config IR_LIRC_CODEC default y ---help--- - Enable this option to pass raw IR to and from userspace via + Enable this option to pass IR to and from userspace via the LIRC interface. menuconfig RC_DECODERS diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index 475f6af..594535e 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -36,6 +36,8 @@ int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) if (!lirc || !lirc->rbuf) return -EINVAL; + if (!(dev->rec_mode & LIRC_MODE_MODE2)) + return 0; /* Packet start */ if (ev.reset) { @@ -101,6 +103,26 @@ int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) return 0; } +void ir_lirc_scancode(struct rc_dev *dev, enum rc_type proto, u32 scancode, + bool toggle, bool repeat) +{ + struct lirc_driver *lirc = dev->lirc; + int sample; + + if (!lirc || !lirc->rbuf || !(dev->rec_mode & LIRC_MODE_SCANCODE)) + return; + + sample = LIRC_MODE2_SCANCODE | proto; + if (toggle) + sample |= LIRC_SCANCODE_TOGGLE; + if (repeat) + sample |= LIRC_SCANCODE_REPEAT; + + lirc_buffer_write(lirc->rbuf, (unsigned char *) &sample); + lirc_buffer_write(lirc->rbuf, (unsigned char *) &scancode); + wake_up(&lirc->rbuf->wait_poll); +} + static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, size_t n, loff_t *ppos) { @@ -206,6 +228,20 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, return 0; + case LIRC_GET_REC_MODE: + val = dev->rec_mode; + break; + + case LIRC_SET_REC_MODE: + if (val == 0 || + val & ~(LIRC_MODE_MODE2 | LIRC_MODE_SCANCODE) || + (!(dev->lirc->features & LIRC_CAN_REC_MODE2) && + (val & LIRC_MODE_MODE2))) + return -EINVAL; + + dev->rec_mode = val; + return 0; + /* TX settings */ case LIRC_SET_TRANSMITTER_MASK: if (!dev->s_tx_mask) @@ -346,7 +382,9 @@ int ir_lirc_register(struct rc_dev *dev) if (rc) goto rbuf_init_failed; - features = LIRC_CAN_REC_MODE2; + features = LIRC_CAN_REC_SCANCODE; + if (dev->driver_type == RC_DRIVER_IR_RAW) + features |= LIRC_CAN_REC_MODE2; if (dev->tx_ir) { features |= LIRC_CAN_SEND_PULSE; if (dev->s_tx_mask) @@ -374,6 +412,8 @@ int ir_lirc_register(struct rc_dev *dev) dev->driver_name); drv->minor = -1; drv->features = features; + dev->rec_mode = features & LIRC_CAN_REC_MODE2 ? + LIRC_MODE_MODE2 : LIRC_MODE2_SCANCODE; drv->data = dev; drv->rbuf = rbuf; drv->set_use_inc = &ir_lirc_open; diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 732479d..f613306 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -223,11 +223,15 @@ static inline void load_xmp_decode(void) { } void ir_lirc_unregister(struct rc_dev *dev); int ir_lirc_register(struct rc_dev *dev); int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev); +void ir_lirc_scancode(struct rc_dev *dev, enum rc_type proto, u32 scancode, + bool toggle, bool repeat); #else static inline void ir_lirc_unregister(struct rc_dev *dev) {} static inline int ir_lirc_register(struct rc_dev *dev) { return 0; } static inline int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) { return 0; } +static inline void ir_lirc_scancode(struct rc_dev *dev, enum rc_type proto, + u32 scancode, bool toggle, bool repeat) {} #endif #ifdef CONFIG_LIRC diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index e717dc9..483038b 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -606,6 +606,10 @@ void rc_repeat(struct rc_dev *dev) spin_lock_irqsave(&dev->keylock, flags); + if (dev->lirc) + ir_lirc_scancode(dev, dev->last_protocol, dev->last_scancode, + false, true); + input_event(dev->input_dev, EV_MSC, MSC_SCAN, dev->last_scancode); input_sync(dev->input_dev); @@ -642,6 +646,9 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_type protocol, if (new_event && dev->keypressed) ir_do_keyup(dev, false); + if (dev->lirc) + ir_lirc_scancode(dev, protocol, scancode, toggle, false); + input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode); if (new_event && keycode != KEY_RESERVED) { @@ -1416,12 +1423,12 @@ int rc_register_device(struct rc_dev *dev) mutex_lock(&dev->lock); if (rc < 0) goto out_input; - - rc = ir_lirc_register(dev); - if (rc < 0) - goto out_raw; } + rc = ir_lirc_register(dev); + if (rc < 0) + goto out_raw; + if (dev->change_protocol) { u64 rc_type = (1ll << rc_map->rc_type); rc = dev->change_protocol(dev, &rc_type); diff --git a/include/media/lirc.h b/include/media/lirc.h index 7b845f8..a635fc9 100644 --- a/include/media/lirc.h +++ b/include/media/lirc.h @@ -16,6 +16,7 @@ #define LIRC_MODE2_PULSE 0x01000000 #define LIRC_MODE2_FREQUENCY 0x02000000 #define LIRC_MODE2_TIMEOUT 0x03000000 +#define LIRC_MODE2_SCANCODE 0x04000000 #define LIRC_VALUE_MASK 0x00FFFFFF #define LIRC_MODE2_MASK 0xFF000000 @@ -32,6 +33,11 @@ #define LIRC_IS_PULSE(val) (LIRC_MODE2(val) == LIRC_MODE2_PULSE) #define LIRC_IS_FREQUENCY(val) (LIRC_MODE2(val) == LIRC_MODE2_FREQUENCY) #define LIRC_IS_TIMEOUT(val) (LIRC_MODE2(val) == LIRC_MODE2_TIMEOUT) +#define LIRC_IS_SCANCODE(val) (LIRC_MODE2(val) == LIRC_MODE2_SCANCODE) + +#define LIRC_SCANCODE_TOGGLE 0x00800000 +#define LIRC_SCANCODE_REPEAT 0x00400000 +#define LIRC_SCANCODE_PROTOCOL_MASK 0x000000ff /* used heavily by lirc userspace */ #define lirc_t int @@ -46,6 +52,7 @@ #define LIRC_MODE_RAW 0x00000001 #define LIRC_MODE_PULSE 0x00000002 #define LIRC_MODE_MODE2 0x00000004 +#define LIRC_MODE_SCANCODE 0x00000008 #define LIRC_MODE_LIRCCODE 0x00000010 @@ -64,6 +71,7 @@ #define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE) #define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2) #define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE) +#define LIRC_CAN_REC_SCANCODE LIRC_MODE2REC(LIRC_MODE_SCANCODE) #define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK) diff --git a/include/media/rc-core.h b/include/media/rc-core.h index e3f217c..a8cef8c 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -163,6 +163,7 @@ struct rc_dev { u64 gap_duration; bool gap; bool send_timeout_reports; + u32 rec_mode; int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); int (*change_wakeup_protocol)(struct rc_dev *dev, u64 *rc_type); int (*open)(struct rc_dev *dev); -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html