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@xxxxxxxx> --- .../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) { -- 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