diff mbox

[RFC,5/6,media] lirc: pass IR scancodes to userspace via lirc bridge

Message ID 35de32b7b2061fee0e0b5fca124d09fec17c4e66.1426801061.git.sean@mess.org (mailing list archive)
State New, archived
Headers show

Commit Message

Sean Young March 19, 2015, 9:50 p.m. UTC
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@mess.org>
---
 .../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(-)

Comments

Mauro Carvalho Chehab May 14, 2015, 4:58 p.m. UTC | #1
Em Thu, 19 Mar 2015 21:50:16 +0000
Sean Young <sean@mess.org> escreveu:

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

What do you mean by "the bridge" in this context? The lirc devnode?

IMHO, if we enable LIRC to use LIRC_MODE_SCANCODE, the output via input
evdev should be suppressed, and only the LIRC interface should be doing
I/O. Yet, eventually it makes sense to provide a way to explicitly allow
or disable I/O via input/evdev interface when LIRC is in scanmode.

> 
> 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@mess.org>
> ---
>  .../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);
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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);