diff mbox

[RFC,6/6,media] rc: teach lirc how to send scancodes

Message ID 985a9b11e5e02eb43e16d27db23086528434be24.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 send mode has to be switched to LIRC_MODE_SCANCODE and then you can
send one scancode with a write. The encoding is the same as for receiving
scancodes.

FIXME: Currently only the nec encoder can encode IR.
FIXME: The "decoders" should be renamed (codec?)

Signed-off-by: Sean Young <sean@mess.org>
---
 .../DocBook/media/v4l/lirc_device_interface.xml    | 39 +++++++++--------
 drivers/media/rc/ir-lirc-codec.c                   | 49 +++++++++++++++------
 drivers/media/rc/ir-nec-decoder.c                  | 50 ++++++++++++++++++++++
 drivers/media/rc/rc-core-priv.h                    |  1 +
 drivers/media/rc/rc-ir-raw.c                       | 19 ++++++++
 include/media/lirc.h                               |  1 +
 include/media/rc-core.h                            |  3 +-
 7 files changed, 132 insertions(+), 30 deletions(-)

Comments

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

> The send mode has to be switched to LIRC_MODE_SCANCODE and then you can
> send one scancode with a write. The encoding is the same as for receiving
> scancodes.
> 
> FIXME: Currently only the nec encoder can encode IR.
> FIXME: The "decoders" should be renamed (codec?)

The FIXMEs should of course be addressed to merge this one ;)

The patch's idea makes sense to me, but I'll let to review it on a next
spin of this patch series.
--
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
David Härdeman May 20, 2015, 8:53 a.m. UTC | #2
On 2015-03-19 22:50, Sean Young wrote:
> The send mode has to be switched to LIRC_MODE_SCANCODE and then you can
> send one scancode with a write. The encoding is the same as for 
> receiving
> scancodes.

Why do the encoding in-kernel when it can be done in userspace?

I'd understand if it was hardware that accepted a scancode as input, but 
that doesn't seem to be the case?

> FIXME: Currently only the nec encoder can encode IR.
> FIXME: The "decoders" should be renamed (codec?)
> 
> Signed-off-by: Sean Young <sean@mess.org>
> ---
>  .../DocBook/media/v4l/lirc_device_interface.xml    | 39 
> +++++++++--------
>  drivers/media/rc/ir-lirc-codec.c                   | 49 
> +++++++++++++++------
>  drivers/media/rc/ir-nec-decoder.c                  | 50 
> ++++++++++++++++++++++
>  drivers/media/rc/rc-core-priv.h                    |  1 +
>  drivers/media/rc/rc-ir-raw.c                       | 19 ++++++++
>  include/media/lirc.h                               |  1 +
>  include/media/rc-core.h                            |  3 +-
>  7 files changed, 132 insertions(+), 30 deletions(-)
> 
> diff --git a/Documentation/DocBook/media/v4l/lirc_device_interface.xml
> b/Documentation/DocBook/media/v4l/lirc_device_interface.xml
> index f92b5a5..e7f8139 100644
> --- a/Documentation/DocBook/media/v4l/lirc_device_interface.xml
> +++ b/Documentation/DocBook/media/v4l/lirc_device_interface.xml
> @@ -37,8 +37,8 @@ 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.
> +the lowest 8 bits is the rc protocol (see enum rc_type in rc-map.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>
> 
> @@ -47,13 +47,24 @@ unsigned int is the scancode; there is no type in
> the highest 8 bits.
>  <section id="lirc_write">
>  <title>LIRC write fop</title>
> 
> -<para>The data written to the chardev is a pulse/space sequence of 
> integer
> -values. Pulses and spaces are only marked implicitly by their 
> position. The
> -data must start and end with a pulse, therefore, the data must always 
> include
> -an uneven number of samples. The write function must block until the 
> data has
> -been transmitted by the hardware. If more data is provided than the 
> hardware
> -can send, the driver returns EINVAL.</para>
> +<para>
> +This is for sending or "blasting" IR. The format depends on the send 
> mode,
> +this is either LIRC_MODE_PULSE or LIRC_MODE_SCANCODE. The mode can be 
> set
> +and get using <xref linkend="lirc_ioctl"/>. </para>
> +</para>
> +<para>In LIRC_MODE_PULSE, the data written to the chardev is a 
> pulse/space
> +sequence of integer values. Pulses and spaces are only marked 
> implicitly by
> +their position. The data must start and end with a pulse, therefore, 
> the
> +data must always include an uneven number of samples. The write 
> function
> +must block until the data has been transmitted by the hardware. If 
> more data
> +is provided than the hardware can send, the driver returns 
> EINVAL.</para>
> 
> +<para>In LIRC_MODE_SCANCODE, two 32 bit unsigneds must be written. The
> +first unsigned must have it highest 8 bits set to LIRC_MODE2_SCANCODE 
> and
> +the lowest 8 bit signify the rc protocol (see enum rc_type in 
> rc-map.h).
> +If the protocol supports repeats then that can be set using
> +LIRC_SCANCODE_REPEAT and the same for LIRC_SCANCODE_TOGGLE. The next 
> 32 bit
> +unsigned is the scancode.
>  </section>
> 
>  <section id="lirc_ioctl">
> @@ -81,9 +92,10 @@ on working with the default settings 
> initially.</para>
>      </listitem>
>    </varlistentry>
>    <varlistentry>
> -    <term>LIRC_GET_SEND_MODE</term>
> +    <term>LIRC_{G,S}ET_SEND_MODE</term>
>      <listitem>
> -      <para>Get supported transmit mode. Only LIRC_MODE_PULSE is
> supported by lircd.</para>
> +      <para>Get or set the send mode. This can either be 
> LIRC_MODE_PULSE or
> +      LIRC_MODE_SCANCODE. </para>
>      </listitem>
>    </varlistentry>
>    <varlistentry>
> @@ -155,13 +167,6 @@ on working with the default settings 
> initially.</para>
>      </listitem>
>    </varlistentry>
>    <varlistentry>
> -    <term>LIRC_SET_{SEND,REC}_MODE</term>
> -    <listitem>
> -      <para>Set send/receive mode. Largely obsolete for send, as only
> -      LIRC_MODE_PULSE is supported.</para>
> -    </listitem>
> -  </varlistentry>
> -  <varlistentry>
>      <term>LIRC_SET_{SEND,REC}_CARRIER</term>
>      <listitem>
>        <para>Set send/receive carrier (in Hz).</para>
> diff --git a/drivers/media/rc/ir-lirc-codec.c 
> b/drivers/media/rc/ir-lirc-codec.c
> index 594535e..14d9b41 100644
> --- a/drivers/media/rc/ir-lirc-codec.c
> +++ b/drivers/media/rc/ir-lirc-codec.c
> @@ -141,16 +141,39 @@ static ssize_t ir_lirc_transmit_ir(struct file
> *file, const char __user *buf,
>  	if (!dev || !dev->lirc)
>  		return -EFAULT;
> 
> -	if (n < sizeof(unsigned) || n % sizeof(unsigned))
> -		return -EINVAL;
> +	if (dev->send_mode == LIRC_MODE_SCANCODE) {
> +		unsigned c[2];
> 
> -	count = n / sizeof(unsigned);
> -	if (count > LIRCBUF_SIZE || count % 2 == 0)
> -		return -EINVAL;
> +		if (n != sizeof(unsigned) * 2)
> +			return -EINVAL;
> +
> +		ret = copy_from_user(c, buf, n);
> +		if (ret)
> +			return ret;
> +
> +		txbuf = kmalloc(GFP_KERNEL, sizeof(unsigned) * LIRCBUF_SIZE);
> +		if (!txbuf)
> +			return -ENOMEM;
> 
> -	txbuf = memdup_user(buf, n);
> -	if (IS_ERR(txbuf))
> -		return PTR_ERR(txbuf);
> +		ret = ir_raw_encode(c[0] & LIRC_SCANCODE_PROTOCOL_MASK, c[1],
> +				(c[0] & LIRC_SCANCODE_REPEAT) != 0,
> +				(c[0] & LIRC_SCANCODE_TOGGLE) != 0,
> +				txbuf, LIRCBUF_SIZE);
> +		if (ret < 0)
> +			goto out;
> +		count = ret;
> +	} else {
> +		if (n < sizeof(unsigned) || n % sizeof(unsigned))
> +			return -EINVAL;
> +
> +		count = n / sizeof(unsigned);
> +		if (count > LIRCBUF_SIZE || count % 2 == 0)
> +			return -EINVAL;
> +
> +		txbuf = memdup_user(buf, n);
> +		if (IS_ERR(txbuf))
> +			return PTR_ERR(txbuf);
> +	}
> 
>  	if (!dev->tx_ir) {
>  		ret = -ENOSYS;
> @@ -173,7 +196,7 @@ static ssize_t ir_lirc_transmit_ir(struct file
> *file, const char __user *buf,
>  	for (duration = i = 0; i < ret; i++)
>  		duration += txbuf[i];
> 
> -	ret *= sizeof(unsigned int);
> +	ret = n;
> 
>  	/*
>  	 * The lircd gap calculation expects the write function to
> @@ -216,16 +239,17 @@ static long ir_lirc_ioctl(struct file *filep,
> unsigned int cmd,
>  		if (!(dev->lirc->features & LIRC_CAN_SEND_MASK))
>  			return -ENOSYS;
> 
> -		val = LIRC_MODE_PULSE;
> +		val = dev->send_mode;
>  		break;
> 
>  	case LIRC_SET_SEND_MODE:
>  		if (!(dev->lirc->features & LIRC_CAN_SEND_MASK))
>  			return -ENOSYS;
> 
> -		if (val != LIRC_MODE_PULSE)
> +		if (val != LIRC_MODE_PULSE && val != LIRC_MODE_SCANCODE)
>  			return -EINVAL;
> 
> +		dev->send_mode = val;
>  		return 0;
> 
>  	case LIRC_GET_REC_MODE:
> @@ -386,13 +410,14 @@ int ir_lirc_register(struct rc_dev *dev)
>  	if (dev->driver_type == RC_DRIVER_IR_RAW)
>  		features |= LIRC_CAN_REC_MODE2;
>  	if (dev->tx_ir) {
> -		features |= LIRC_CAN_SEND_PULSE;
> +		features |= LIRC_CAN_SEND_PULSE | LIRC_CAN_SEND_SCANCODE;
>  		if (dev->s_tx_mask)
>  			features |= LIRC_CAN_SET_TRANSMITTER_MASK;
>  		if (dev->s_tx_carrier)
>  			features |= LIRC_CAN_SET_SEND_CARRIER;
>  		if (dev->s_tx_duty_cycle)
>  			features |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
> +		dev->send_mode = LIRC_MODE_PULSE;
>  	}
> 
>  	if (dev->s_rx_carrier_range)
> diff --git a/drivers/media/rc/ir-nec-decoder.c
> b/drivers/media/rc/ir-nec-decoder.c
> index 7b81fec..1232084 100644
> --- a/drivers/media/rc/ir-nec-decoder.c
> +++ b/drivers/media/rc/ir-nec-decoder.c
> @@ -200,9 +200,59 @@ static int ir_nec_decode(struct rc_dev *dev,
> struct ir_raw_event ev)
>  	return -EINVAL;
>  }
> 
> +static int ir_nec_encode(enum rc_type type, u32 scancode, bool repeat,
> +				bool toggle, unsigned *buf, unsigned count)
> +{
> +	unsigned i = 0, bits;
> +
> +	if (type != RC_TYPE_NEC)
> +		return -EINVAL;
> +
> +	if (count < 3)
> +		return -ENOSPC;
> +
> +	if (repeat) {
> +		buf[i++] = NEC_HEADER_PULSE;
> +		buf[i++] = NEC_REPEAT_SPACE;
> +		buf[i++] = STATE_TRAILER_PULSE;
> +		return 3;
> +	}
> +
> +	if (count < 3 + NEC_NBITS * 2)
> +		return -ENOSPC;
> +
> +	if (scancode < 0x10000) { /* normal NEC */
> +		u32 cmd, addr;
> +
> +		addr = (scancode >> 8) & 0xff;
> +		cmd = scancode & 0xff;
> +
> +		scancode = addr << 24 | (~addr & 0xff) << 16 |
> +						cmd << 8 | (~cmd & 0xff);
> +	} else if (scancode < 0x1000000) { /* extended NEC */
> +		u32 cmd = scancode & 0xff;
> +
> +		scancode = (scancode & 0xffff00) << 8 |
> +						(cmd << 8) | (~cmd & 0xff);
> +	}
> +
> +	buf[i++] = NEC_HEADER_PULSE;
> +	buf[i++] = NEC_HEADER_SPACE;
> +	for (bits = 0; bits < NEC_NBITS; bits++) {
> +		buf[i++] = NEC_BIT_PULSE;
> +		buf[i++] = (scancode & 0x80000000) ?
> +					NEC_BIT_1_SPACE : NEC_BIT_0_SPACE;
> +		scancode <<= 1;
> +	}
> +	buf[i++] = STATE_TRAILER_PULSE;
> +
> +	return i;
> +}
> +
>  static struct ir_raw_handler nec_handler = {
>  	.protocols	= RC_BIT_NEC,
>  	.decode		= ir_nec_decode,
> +	.encode		= ir_nec_encode,
>  };
> 
>  static int __init ir_nec_decode_init(void)
> diff --git a/drivers/media/rc/rc-core-priv.h 
> b/drivers/media/rc/rc-core-priv.h
> index f613306..82d9132 100644
> --- a/drivers/media/rc/rc-core-priv.h
> +++ b/drivers/media/rc/rc-core-priv.h
> @@ -25,6 +25,7 @@ struct ir_raw_handler {
> 
>  	u64 protocols; /* which are handled by this handler */
>  	int (*decode)(struct rc_dev *dev, struct ir_raw_event event);
> +	int (*encode)(enum rc_type protocol, u32 scancode, bool repeat, bool
> toggle, unsigned *buf, unsigned size);
> 
>  	/* These two should only be used by the mce kbd decoder */
>  	int (*raw_register)(struct rc_dev *dev);
> diff --git a/drivers/media/rc/rc-ir-raw.c 
> b/drivers/media/rc/rc-ir-raw.c
> index d298be7..d4e2144 100644
> --- a/drivers/media/rc/rc-ir-raw.c
> +++ b/drivers/media/rc/rc-ir-raw.c
> @@ -351,6 +351,25 @@ void ir_raw_handler_unregister(struct
> ir_raw_handler *ir_raw_handler)
>  }
>  EXPORT_SYMBOL(ir_raw_handler_unregister);
> 
> +int ir_raw_encode(enum rc_type type, u32 scancode, bool repeat, bool 
> toggle,
> +					unsigned *buf, unsigned size)
> +{
> +	struct ir_raw_handler *handler;
> +	u64 protocol = 1ull << type;
> +	int ret = -ENOSYS;
> +
> +	mutex_lock(&ir_raw_handler_lock);
> +	list_for_each_entry(handler, &ir_raw_handler_list, list) {
> +		if (handler->protocols & protocol && handler->encode) {
> +			ret = handler->encode(type, scancode, repeat, toggle,
> +								buf, size);
> +			break;
> +		}
> +	}
> +	mutex_unlock(&ir_raw_handler_lock);
> +	return ret;
> +}
> +
>  void ir_raw_init(void)
>  {
>  	/* Load the decoder modules */
> diff --git a/include/media/lirc.h b/include/media/lirc.h
> index a635fc9..3807ded 100644
> --- a/include/media/lirc.h
> +++ b/include/media/lirc.h
> @@ -60,6 +60,7 @@
>  #define LIRC_CAN_SEND_PULSE            LIRC_MODE2SEND(LIRC_MODE_PULSE)
>  #define LIRC_CAN_SEND_MODE2            LIRC_MODE2SEND(LIRC_MODE_MODE2)
>  #define LIRC_CAN_SEND_LIRCCODE         
> LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
> +#define LIRC_CAN_SEND_SCANCODE         
> LIRC_MODE2SEND(LIRC_MODE_SCANCODE)
> 
>  #define LIRC_CAN_SEND_MASK             0x0000003f
> 
> diff --git a/include/media/rc-core.h b/include/media/rc-core.h
> index a8cef8c..601a5ba 100644
> --- a/include/media/rc-core.h
> +++ b/include/media/rc-core.h
> @@ -163,7 +163,7 @@ struct rc_dev {
>  	u64				gap_duration;
>  	bool				gap;
>  	bool				send_timeout_reports;
> -	u32				rec_mode;
> +	u32				rec_mode, send_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);
> @@ -258,6 +258,7 @@ int ir_raw_event_store_edge(struct rc_dev *dev,
> enum raw_event_type type);
>  int ir_raw_event_store_with_filter(struct rc_dev *dev,
>  				struct ir_raw_event *ev);
>  void ir_raw_event_set_idle(struct rc_dev *dev, bool idle);
> +int ir_raw_encode(enum rc_type type, u32 scancode, bool repeat, bool
> toggle, unsigned *buf, unsigned size);
> 
>  static inline void ir_raw_event_reset(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
Mauro Carvalho Chehab May 20, 2015, 9:08 a.m. UTC | #3
Em Wed, 20 May 2015 10:53:59 +0200
David Härdeman <david@hardeman.nu> escreveu:

> On 2015-03-19 22:50, Sean Young wrote:
> > The send mode has to be switched to LIRC_MODE_SCANCODE and then you can
> > send one scancode with a write. The encoding is the same as for 
> > receiving
> > scancodes.
> 
> Why do the encoding in-kernel when it can be done in userspace?
> 
> I'd understand if it was hardware that accepted a scancode as input, but 
> that doesn't seem to be the case?

IMO, that makes the interface clearer. Also, the encoding code is needed
anyway, as it is needed to setup the wake up keycode on some hardware.
So, we already added encoder capabilities at some decoders:

0d830b2d1295 [media] rc: rc-core: Add support for encode_wakeup drivers
cf257e288ad3 [media] rc: ir-rc6-decoder: Add encode capability
a0466f15b465 [media] rc: ir-rc5-decoder: Add encode capability
1d971d927efa [media] rc: rc-ir-raw: Add Manchester encoder (phase encoder) helper
9869da5bacc5 [media] rc: rc-ir-raw: Add scancode encoder callback

> 
> > FIXME: Currently only the nec encoder can encode IR.

We actually need to be sure that the NEC encoder is doing the same
way as the RC5/RC6 encoders.

Regards,
Mauro
--
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
David Härdeman May 20, 2015, 9:18 a.m. UTC | #4
On 2015-05-20 11:08, Mauro Carvalho Chehab wrote:
> Em Wed, 20 May 2015 10:53:59 +0200
> David Härdeman <david@hardeman.nu> escreveu:
> 
>> On 2015-03-19 22:50, Sean Young wrote:
>> > The send mode has to be switched to LIRC_MODE_SCANCODE and then you can
>> > send one scancode with a write. The encoding is the same as for
>> > receiving
>> > scancodes.
>> 
>> Why do the encoding in-kernel when it can be done in userspace?
>> 
>> I'd understand if it was hardware that accepted a scancode as input, 
>> but
>> that doesn't seem to be the case?
> 
> IMO, that makes the interface clearer. Also, the encoding code is 
> needed
> anyway, as it is needed to setup the wake up keycode on some hardware.
> So, we already added encoder capabilities at some decoders:

I know.

But with the proposed API userspace would have to first try to send a 
scancode + protocol, then see what the error code was, and if it 
indicated that the kernel couldn't encode the scancode, userspace would 
anyway have to encode it itself and then try again with raw events?

I think that's a bad API (to put it mildly).

There's simply no need to encode the scancodes in kernel (even if the 
code happens to be present for a random and fluctuating set of 
protocols) and any well-written userspace would have to include code to 
encode to any protocol it wants to support (since it can't rely on the 
kernel supporting any given protocol)....what does that buy you except a 
hairy API and added complexity?

--
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 f92b5a5..e7f8139 100644
--- a/Documentation/DocBook/media/v4l/lirc_device_interface.xml
+++ b/Documentation/DocBook/media/v4l/lirc_device_interface.xml
@@ -37,8 +37,8 @@  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.
+the lowest 8 bits is the rc protocol (see enum rc_type in rc-map.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>
 
@@ -47,13 +47,24 @@  unsigned int is the scancode; there is no type in the highest 8 bits.
 <section id="lirc_write">
 <title>LIRC write fop</title>
 
-<para>The data written to the chardev is a pulse/space sequence of integer
-values. Pulses and spaces are only marked implicitly by their position. The
-data must start and end with a pulse, therefore, the data must always include
-an uneven number of samples. The write function must block until the data has
-been transmitted by the hardware. If more data is provided than the hardware
-can send, the driver returns EINVAL.</para>
+<para>
+This is for sending or "blasting" IR. The format depends on the send mode,
+this is either LIRC_MODE_PULSE or LIRC_MODE_SCANCODE. The mode can be set
+and get using <xref linkend="lirc_ioctl"/>. </para>
+</para>
+<para>In LIRC_MODE_PULSE, the data written to the chardev is a pulse/space
+sequence of integer values. Pulses and spaces are only marked implicitly by
+their position. The data must start and end with a pulse, therefore, the
+data must always include an uneven number of samples. The write function
+must block until the data has been transmitted by the hardware. If more data
+is provided than the hardware can send, the driver returns EINVAL.</para>
 
+<para>In LIRC_MODE_SCANCODE, two 32 bit unsigneds must be written. The
+first unsigned must have it highest 8 bits set to LIRC_MODE2_SCANCODE and
+the lowest 8 bit signify the rc protocol (see enum rc_type in rc-map.h).
+If the protocol supports repeats then that can be set using
+LIRC_SCANCODE_REPEAT and the same for LIRC_SCANCODE_TOGGLE. The next 32 bit
+unsigned is the scancode.
 </section>
 
 <section id="lirc_ioctl">
@@ -81,9 +92,10 @@  on working with the default settings initially.</para>
     </listitem>
   </varlistentry>
   <varlistentry>
-    <term>LIRC_GET_SEND_MODE</term>
+    <term>LIRC_{G,S}ET_SEND_MODE</term>
     <listitem>
-      <para>Get supported transmit mode. Only LIRC_MODE_PULSE is supported by lircd.</para>
+      <para>Get or set the send mode. This can either be LIRC_MODE_PULSE or
+      LIRC_MODE_SCANCODE. </para>
     </listitem>
   </varlistentry>
   <varlistentry>
@@ -155,13 +167,6 @@  on working with the default settings initially.</para>
     </listitem>
   </varlistentry>
   <varlistentry>
-    <term>LIRC_SET_{SEND,REC}_MODE</term>
-    <listitem>
-      <para>Set send/receive mode. Largely obsolete for send, as only
-      LIRC_MODE_PULSE is supported.</para>
-    </listitem>
-  </varlistentry>
-  <varlistentry>
     <term>LIRC_SET_{SEND,REC}_CARRIER</term>
     <listitem>
       <para>Set send/receive carrier (in Hz).</para>
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index 594535e..14d9b41 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -141,16 +141,39 @@  static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 	if (!dev || !dev->lirc)
 		return -EFAULT;
 
-	if (n < sizeof(unsigned) || n % sizeof(unsigned))
-		return -EINVAL;
+	if (dev->send_mode == LIRC_MODE_SCANCODE) {
+		unsigned c[2];
 
-	count = n / sizeof(unsigned);
-	if (count > LIRCBUF_SIZE || count % 2 == 0)
-		return -EINVAL;
+		if (n != sizeof(unsigned) * 2)
+			return -EINVAL;
+
+		ret = copy_from_user(c, buf, n);
+		if (ret)
+			return ret;
+
+		txbuf = kmalloc(GFP_KERNEL, sizeof(unsigned) * LIRCBUF_SIZE);
+		if (!txbuf)
+			return -ENOMEM;
 
-	txbuf = memdup_user(buf, n);
-	if (IS_ERR(txbuf))
-		return PTR_ERR(txbuf);
+		ret = ir_raw_encode(c[0] & LIRC_SCANCODE_PROTOCOL_MASK, c[1],
+				(c[0] & LIRC_SCANCODE_REPEAT) != 0,
+				(c[0] & LIRC_SCANCODE_TOGGLE) != 0,
+				txbuf, LIRCBUF_SIZE);
+		if (ret < 0)
+			goto out;
+		count = ret;
+	} else {
+		if (n < sizeof(unsigned) || n % sizeof(unsigned))
+			return -EINVAL;
+
+		count = n / sizeof(unsigned);
+		if (count > LIRCBUF_SIZE || count % 2 == 0)
+			return -EINVAL;
+
+		txbuf = memdup_user(buf, n);
+		if (IS_ERR(txbuf))
+			return PTR_ERR(txbuf);
+	}
 
 	if (!dev->tx_ir) {
 		ret = -ENOSYS;
@@ -173,7 +196,7 @@  static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 	for (duration = i = 0; i < ret; i++)
 		duration += txbuf[i];
 
-	ret *= sizeof(unsigned int);
+	ret = n;
 
 	/*
 	 * The lircd gap calculation expects the write function to
@@ -216,16 +239,17 @@  static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
 		if (!(dev->lirc->features & LIRC_CAN_SEND_MASK))
 			return -ENOSYS;
 
-		val = LIRC_MODE_PULSE;
+		val = dev->send_mode;
 		break;
 
 	case LIRC_SET_SEND_MODE:
 		if (!(dev->lirc->features & LIRC_CAN_SEND_MASK))
 			return -ENOSYS;
 
-		if (val != LIRC_MODE_PULSE)
+		if (val != LIRC_MODE_PULSE && val != LIRC_MODE_SCANCODE)
 			return -EINVAL;
 
+		dev->send_mode = val;
 		return 0;
 
 	case LIRC_GET_REC_MODE:
@@ -386,13 +410,14 @@  int ir_lirc_register(struct rc_dev *dev)
 	if (dev->driver_type == RC_DRIVER_IR_RAW)
 		features |= LIRC_CAN_REC_MODE2;
 	if (dev->tx_ir) {
-		features |= LIRC_CAN_SEND_PULSE;
+		features |= LIRC_CAN_SEND_PULSE | LIRC_CAN_SEND_SCANCODE;
 		if (dev->s_tx_mask)
 			features |= LIRC_CAN_SET_TRANSMITTER_MASK;
 		if (dev->s_tx_carrier)
 			features |= LIRC_CAN_SET_SEND_CARRIER;
 		if (dev->s_tx_duty_cycle)
 			features |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
+		dev->send_mode = LIRC_MODE_PULSE;
 	}
 
 	if (dev->s_rx_carrier_range)
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 7b81fec..1232084 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -200,9 +200,59 @@  static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
 	return -EINVAL;
 }
 
+static int ir_nec_encode(enum rc_type type, u32 scancode, bool repeat,
+				bool toggle, unsigned *buf, unsigned count)
+{
+	unsigned i = 0, bits;
+
+	if (type != RC_TYPE_NEC)
+		return -EINVAL;
+
+	if (count < 3)
+		return -ENOSPC;
+
+	if (repeat) {
+		buf[i++] = NEC_HEADER_PULSE;
+		buf[i++] = NEC_REPEAT_SPACE;
+		buf[i++] = STATE_TRAILER_PULSE;
+		return 3;
+	}
+
+	if (count < 3 + NEC_NBITS * 2)
+		return -ENOSPC;
+
+	if (scancode < 0x10000) { /* normal NEC */
+		u32 cmd, addr;
+
+		addr = (scancode >> 8) & 0xff;
+		cmd = scancode & 0xff;
+
+		scancode = addr << 24 | (~addr & 0xff) << 16 |
+						cmd << 8 | (~cmd & 0xff);
+	} else if (scancode < 0x1000000) { /* extended NEC */
+		u32 cmd = scancode & 0xff;
+
+		scancode = (scancode & 0xffff00) << 8 |
+						(cmd << 8) | (~cmd & 0xff);
+	}
+
+	buf[i++] = NEC_HEADER_PULSE;
+	buf[i++] = NEC_HEADER_SPACE;
+	for (bits = 0; bits < NEC_NBITS; bits++) {
+		buf[i++] = NEC_BIT_PULSE;
+		buf[i++] = (scancode & 0x80000000) ?
+					NEC_BIT_1_SPACE : NEC_BIT_0_SPACE;
+		scancode <<= 1;
+	}
+	buf[i++] = STATE_TRAILER_PULSE;
+
+	return i;
+}
+
 static struct ir_raw_handler nec_handler = {
 	.protocols	= RC_BIT_NEC,
 	.decode		= ir_nec_decode,
+	.encode		= ir_nec_encode,
 };
 
 static int __init ir_nec_decode_init(void)
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index f613306..82d9132 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -25,6 +25,7 @@  struct ir_raw_handler {
 
 	u64 protocols; /* which are handled by this handler */
 	int (*decode)(struct rc_dev *dev, struct ir_raw_event event);
+	int (*encode)(enum rc_type protocol, u32 scancode, bool repeat, bool toggle, unsigned *buf, unsigned size);
 
 	/* These two should only be used by the mce kbd decoder */
 	int (*raw_register)(struct rc_dev *dev);
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index d298be7..d4e2144 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -351,6 +351,25 @@  void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
 }
 EXPORT_SYMBOL(ir_raw_handler_unregister);
 
+int ir_raw_encode(enum rc_type type, u32 scancode, bool repeat, bool toggle,
+					unsigned *buf, unsigned size)
+{
+	struct ir_raw_handler *handler;
+	u64 protocol = 1ull << type;
+	int ret = -ENOSYS;
+
+	mutex_lock(&ir_raw_handler_lock);
+	list_for_each_entry(handler, &ir_raw_handler_list, list) {
+		if (handler->protocols & protocol && handler->encode) {
+			ret = handler->encode(type, scancode, repeat, toggle,
+								buf, size);
+			break;
+		}
+	}
+	mutex_unlock(&ir_raw_handler_lock);
+	return ret;
+}
+
 void ir_raw_init(void)
 {
 	/* Load the decoder modules */
diff --git a/include/media/lirc.h b/include/media/lirc.h
index a635fc9..3807ded 100644
--- a/include/media/lirc.h
+++ b/include/media/lirc.h
@@ -60,6 +60,7 @@ 
 #define LIRC_CAN_SEND_PULSE            LIRC_MODE2SEND(LIRC_MODE_PULSE)
 #define LIRC_CAN_SEND_MODE2            LIRC_MODE2SEND(LIRC_MODE_MODE2)
 #define LIRC_CAN_SEND_LIRCCODE         LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
+#define LIRC_CAN_SEND_SCANCODE         LIRC_MODE2SEND(LIRC_MODE_SCANCODE)
 
 #define LIRC_CAN_SEND_MASK             0x0000003f
 
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index a8cef8c..601a5ba 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -163,7 +163,7 @@  struct rc_dev {
 	u64				gap_duration;
 	bool				gap;
 	bool				send_timeout_reports;
-	u32				rec_mode;
+	u32				rec_mode, send_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);
@@ -258,6 +258,7 @@  int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type);
 int ir_raw_event_store_with_filter(struct rc_dev *dev,
 				struct ir_raw_event *ev);
 void ir_raw_event_set_idle(struct rc_dev *dev, bool idle);
+int ir_raw_encode(enum rc_type type, u32 scancode, bool repeat, bool toggle, unsigned *buf, unsigned size);
 
 static inline void ir_raw_event_reset(struct rc_dev *dev)
 {