[RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem - Kernel

This is a discussion on [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem - Kernel ; Third pass at implementing evdev support for IR. The goal of in-kernel IR is to integrate IR events into the evdev input event queue and maintain ordering of events from all input devices. New feature, raw mode. There are three ...

+ Reply to Thread
Results 1 to 10 of 10

Thread: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem

  1. [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem

    Third pass at implementing evdev support for IR. The goal of in-kernel IR is to integrate IR events into the evdev input event queue and maintain ordering of events from all input devices.

    New feature, raw mode. There are three sysfs attributes - ir_raw, ir_carrier, ir_xmitter. Read from ir_raw to get the raw timing data from the IR device. Set carrier and active xmitters and then copy raw data to ir_raw to send. These attributes may be better on a debug switch. You would use raw mode when decoding a new protocol. After you figure out the new protocol, write an in-kernel encoder/decoder for it.

    What should the IR API look like?

    How are IR events mapped into keyboard events? Should they be mapped? Map them in the kernel or in user space? The maps are tiny, less than 1K per remote. Sysfs can be used to load maps into the kernel driver. Make maps only for the common buttons and don't map unusual ones?

    How should multiple remotes be handled? Split them out into individual input devices, or group them onto a single IR device? I can implement either.

    The in-kernel code is tiny, about 20K including a driver.

    >From last post...

    Note that user space IR device drivers can use the existing support in evdev to inject events into the input queue.

    Send and receive are implemented. Received IR messages are decoded and sent to user space as input messages. Send is done via an IOCTL on the input device.

    Two drivers are supplied. mceusb2 implements send and receive support for the Microsoft USB IR dongle.

    The GPT driver implements receive only support for a GPT pin - GPT is a GPIO with a timer attached.

    Code is only lightly tested. Encoders and decoders have not been written for all protocols.
    Repeat is not handled for any protocol.
    I'm looking for help. There are 15 more existing LIRC drivers.

    ---

    Jon Smirl (4):
    Microsoft mceusb2 driver for in-kernel IR subsystem
    Example of PowerPC device tree support for GPT based IR
    GPT driver for in-kernel IR support.
    Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc.


    arch/powerpc/boot/dts/dspeak01.dts | 19 -
    drivers/input/Kconfig | 2
    drivers/input/Makefile | 3
    drivers/input/evdev.c | 55 +++
    drivers/input/input.c | 21 +
    drivers/input/ir-core.c | 659 ++++++++++++++++++++++++++++++++
    drivers/input/ir/Kconfig | 26 +
    drivers/input/ir/Makefile | 7
    drivers/input/ir/ir-gpt.c | 221 +++++++++++
    drivers/input/ir/ir-mceusb2.c | 741 ++++++++++++++++++++++++++++++++++++
    include/linux/input.h | 101 +++++
    include/linux/mod_devicetable.h | 3
    12 files changed, 1846 insertions(+), 12 deletions(-)
    create mode 100644 drivers/input/ir-core.c
    create mode 100644 drivers/input/ir/Kconfig
    create mode 100644 drivers/input/ir/Makefile
    create mode 100644 drivers/input/ir/ir-gpt.c
    create mode 100644 drivers/input/ir/ir-mceusb2.c

    --
    Jon Smirl
    jonsmirl@gmail.com
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  2. [RFC PATCH 3/4] Example of PowerPC device tree support for GPT based IR


    ---
    arch/powerpc/boot/dts/dspeak01.dts | 19 ++++++++-----------
    1 files changed, 8 insertions(+), 11 deletions(-)

    diff --git a/arch/powerpc/boot/dts/dspeak01.dts b/arch/powerpc/boot/dts/dspeak01.dts
    index 1d10f0a..cbb22a1 100644
    --- a/arch/powerpc/boot/dts/dspeak01.dts
    +++ b/arch/powerpc/boot/dts/dspeak01.dts
    @@ -131,16 +131,6 @@
    #gpio-cells = <2>;
    };

    - gpt7: timer@670 { /* General Purpose Timer in GPIO mode */
    - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio";
    - cell-index = <7>;
    - reg = <0x670 0x10>;
    - interrupts = <0x1 0x10 0x0>;
    - interrupt-parent = <&mpc5200_pic>;
    - gpio-controller;
    - #gpio-cells = <2>;
    - };
    -
    rtc@800 { // Real time clock
    compatible = "fsl,mpc5200b-rtc","fsl,mpc5200-rtc";
    device_type = "rtc";
    @@ -328,6 +318,14 @@
    reg = <0x8000 0x4000>;
    };

    + ir0: timer@670 { /* General Purpose Timer 6 in Input mode */
    + compatible = "gpt-ir";
    + cell-index = <7>;
    + reg = <0x670 0x10>;
    + interrupts = <0x1 0x10 0x0>;
    + interrupt-parent = <&mpc5200_pic>;
    + };
    +
    /* This is only an example device to show the usage of gpios. It maps all available
    * gpios to the "gpio-provider" device.
    */
    @@ -345,7 +343,6 @@
    &gpt4 0 0 /* timer4 61c x2-16 */
    &gpt5 0 0 /* timer5 44c x7-11 */
    &gpt6 0 0 /* timer6 60c x8-15 */
    - &gpt7 0 0 /* timer7 36a x17-9 */
    >;

    };
    };

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  3. [RFC PATCH 1/4] Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc.

    Received IR messages generate event in the input queue.
    IR messages are sent using an input IOCTL.

    Jon Smirl

    ---
    drivers/input/Kconfig | 2
    drivers/input/Makefile | 3
    drivers/input/evdev.c | 55 +++
    drivers/input/input.c | 21 +
    drivers/input/ir-core.c | 659 +++++++++++++++++++++++++++++++++++++++
    drivers/input/ir/Kconfig | 14 +
    drivers/input/ir/Makefile | 5
    include/linux/input.h | 101 ++++++
    include/linux/mod_devicetable.h | 3
    9 files changed, 862 insertions(+), 1 deletions(-)
    create mode 100644 drivers/input/ir-core.c
    create mode 100644 drivers/input/ir/Kconfig
    create mode 100644 drivers/input/ir/Makefile

    diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
    index 747633c..780d321 100644
    --- a/drivers/input/Kconfig
    +++ b/drivers/input/Kconfig
    @@ -172,6 +172,8 @@ source "drivers/input/touchscreen/Kconfig"

    source "drivers/input/misc/Kconfig"

    +source "drivers/input/ir/Kconfig"
    +
    endif

    menu "Hardware I/O ports"
    diff --git a/drivers/input/Makefile b/drivers/input/Makefile
    index 6a1049b..da47340 100644
    --- a/drivers/input/Makefile
    +++ b/drivers/input/Makefile
    @@ -5,7 +5,7 @@
    # Each configuration option enables a list of files.

    obj-$(CONFIG_INPUT) += input-core.o
    -input-core-objs := input.o ff-core.o
    +input-core-objs := input.o ff-core.o ir-core.o

    obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
    obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
    @@ -21,6 +21,7 @@ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/
    obj-$(CONFIG_INPUT_TABLET) += tablet/
    obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
    obj-$(CONFIG_INPUT_MISC) += misc/
    +obj-$(CONFIG_INPUT_IR) += ir/

    obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o

    diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
    index 3524bef..7a3f935 100644
    --- a/drivers/input/evdev.c
    +++ b/drivers/input/evdev.c
    @@ -329,6 +329,14 @@ struct ff_effect_compat {
    } u;
    };

    +struct ir_command_compat {
    + __u32 protocol;
    + __u32 device;
    + __u32 command;
    + __u32 transmitters;
    +};
    +
    +
    /* Note to the author of this code: did it ever occur to
    you why the ifdefs are needed? Think about it again. -AK */
    #ifdef CONFIG_X86_64
    @@ -433,6 +441,32 @@ static int evdev_ff_effect_from_user(const char __user *buffer, size_t size,
    return 0;
    }

    +static int evdev_ir_send_from_user(const char __user *buffer, size_t size,
    + struct ir_command *ir_command)
    +{
    + if (COMPAT_TEST) {
    + struct ir_command_compat *compat_ir_command;
    +
    + if (size != sizeof(struct ir_command_compat))
    + return -EINVAL;
    +
    + compat_ir_command = (struct ir_command_compat *)ir_command;
    +
    + if (copy_from_user(compat_ir_command, buffer,
    + sizeof(struct ir_command_compat)))
    + return -EFAULT;
    +
    + } else {
    + if (size != sizeof(struct ir_command))
    + return -EINVAL;
    +
    + if (copy_from_user(ir_command, buffer, sizeof(struct ir_command)))
    + return -EFAULT;
    + }
    +
    + return 0;
    +}
    +
    #else

    static inline size_t evdev_event_size(void)
    @@ -470,6 +504,18 @@ static int evdev_ff_effect_from_user(const char __user *buffer, size_t size,
    return 0;
    }

    +static int evdev_ir_send_from_user(const char __user *buffer, size_t size,
    + struct ir_command *ir_command)
    +{
    + if (size != sizeof(struct ir_command))
    + return -EINVAL;
    +
    + if (copy_from_user(ir_command, buffer, sizeof(struct ir_command)))
    + return -EFAULT;
    +
    + return 0;
    +}
    +
    #endif /* CONFIG_COMPAT */

    static ssize_t evdev_write(struct file *file, const char __user *buffer,
    @@ -696,6 +742,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
    struct input_dev *dev = evdev->handle.dev;
    struct input_absinfo abs;
    struct ff_effect effect;
    + struct ir_command ir_command;
    int __user *ip = (int __user *)p;
    int i, t, u, v;
    int error;
    @@ -860,6 +907,14 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,

    return 0;
    }
    +
    + if (_IOC_NR(cmd) == _IOC_NR(EVIOIRSEND)) {
    +
    + if (evdev_ir_send_from_user(p, _IOC_SIZE(cmd), &ir_command))
    + return -EFAULT;
    +
    + return input_ir_send(dev, &ir_command, file);
    + }
    }
    }
    return -EINVAL;
    diff --git a/drivers/input/input.c b/drivers/input/input.c
    index c13ced3..18f36d7 100644
    --- a/drivers/input/input.c
    +++ b/drivers/input/input.c
    @@ -240,6 +240,10 @@ static void input_handle_event(struct input_dev *dev,
    case EV_PWR:
    disposition = INPUT_PASS_TO_ALL;
    break;
    +
    + case EV_IR:
    + disposition = INPUT_PASS_TO_ALL;
    + break;
    }

    if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
    @@ -693,6 +697,7 @@ static const struct input_device_id *input_match_device(const struct input_devic
    MATCH_BIT(sndbit, SND_MAX);
    MATCH_BIT(ffbit, FF_MAX);
    MATCH_BIT(swbit, SW_MAX);
    + MATCH_BIT(irbit, IR_MAX);

    return id;
    }
    @@ -815,6 +820,8 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
    input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX);
    if (test_bit(EV_SW, dev->evbit))
    input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX);
    + if (test_bit(EV_IR, dev->evbit))
    + input_seq_print_bitmap(seq, "IR", dev->irbit, IR_MAX);

    seq_putc(seq, '\n');

    @@ -992,6 +999,8 @@ static int input_print_modalias(char *buf, int size, struct input_dev *id,
    'f', id->ffbit, 0, FF_MAX);
    len += input_print_modalias_bits(buf + len, size - len,
    'w', id->swbit, 0, SW_MAX);
    + len += input_print_modalias_bits(buf + len, size - len,
    + 'i', id->irbit, 0, IR_MAX);

    if (add_cr)
    len += snprintf(buf + len, max(size - len, 0), "\n");
    @@ -1093,6 +1102,7 @@ INPUT_DEV_CAP_ATTR(LED, led);
    INPUT_DEV_CAP_ATTR(SND, snd);
    INPUT_DEV_CAP_ATTR(FF, ff);
    INPUT_DEV_CAP_ATTR(SW, sw);
    +INPUT_DEV_CAP_ATTR(IR, ir);

    static struct attribute *input_dev_caps_attrs[] = {
    &dev_attr_ev.attr,
    @@ -1104,6 +1114,7 @@ static struct attribute *input_dev_caps_attrs[] = {
    &dev_attr_snd.attr,
    &dev_attr_ff.attr,
    &dev_attr_sw.attr,
    + &dev_attr_ir.attr,
    NULL
    };

    @@ -1221,6 +1232,8 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
    INPUT_ADD_HOTPLUG_BM_VAR("FF=", dev->ffbit, FF_MAX);
    if (test_bit(EV_SW, dev->evbit))
    INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX);
    + if (test_bit(EV_IR, dev->evbit))
    + INPUT_ADD_HOTPLUG_BM_VAR("IR=", dev->irbit, IR_MAX);

    INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev);

    @@ -1333,6 +1346,10 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
    __set_bit(code, dev->ffbit);
    break;

    + case EV_IR:
    + __set_bit(code, dev->irbit);
    + break;
    +
    case EV_PWR:
    /* do nothing */
    break;
    @@ -1396,6 +1413,10 @@ int input_register_device(struct input_dev *dev)
    if (error)
    return error;

    + error = input_ir_register(dev);
    + if (error)
    + return error;
    +
    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    printk(KERN_INFO "input: %s as %s\n",
    dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
    diff --git a/drivers/input/ir-core.c b/drivers/input/ir-core.c
    new file mode 100644
    index 0000000..8efcfa7
    --- /dev/null
    +++ b/drivers/input/ir-core.c
    @@ -0,0 +1,659 @@
    +/*
    + * Core routines for IR support
    + *
    + * Copyright (C) 2008 Jon Smirl
    + */
    +
    +#include
    +#include
    +#include
    +
    +#undef IR_PROTOCOL_DEBUG
    +#ifdef IR_PROTOCOL_DEBUG
    +#define PDEBUG( format, arg... ) \
    + printk(KERN_DEBUG format , ## arg);
    +#else
    +#define PDEBUG(format, arg...) \
    + ({ if (0) printk(KERN_DEBUG format , ## arg); 0; })
    +#endif
    +
    +static int encode_sony(struct ir_device *ir, struct ir_command *command)
    +{
    + /* Sony SIRC IR code */
    + /* http://www.sbprojects.com/knowledge/ir/sirc.htm */
    + int i, bit, dev, cmd, total;
    +
    + ir->send.count = 0;
    + switch (command->protocol) {
    + case IR_PROTOCOL_SONY_20:
    + dev = 10; cmd = 10; break;
    + case IR_PROTOCOL_SONY_15:
    + dev = 8; cmd = 7; break;
    + default:
    + case IR_PROTOCOL_SONY_12:
    + dev = 5; cmd = 7; break;
    + }
    + ir->send.buffer[ir->send.count++] = 2400;
    + ir->send.buffer[ir->send.count++] = 600;
    +
    + for (i = 0; i < cmd; i++) {
    + bit = command->command & 1;
    + command->command >>= 1;
    + ir->send.buffer[ir->send.count++] = (bit ? 1200 : 600);
    + ir->send.buffer[ir->send.count++] = 600;
    + }
    + for (i = 0; i < dev; i++) {
    + bit = command->device & 1;
    + command->device >>= 1;
    + ir->send.buffer[ir->send.count++] = (bit ? 1200 : 600);
    + ir->send.buffer[ir->send.count++] = 600;
    + }
    + total = 0;
    + for (i = 0; i < ir->send.count; i++)
    + total += ir->send.buffer[i];
    + ir->send.buffer[ir->send.count++] = 45000 - total;
    +
    + memcpy(&ir->send.buffer[ir->send.count], &ir->send.buffer[0], ir->send.count * sizeof ir->send.buffer[0]);
    + ir->send.count += ir->send.count;
    + memcpy(&ir->send.buffer[ir->send.count], &ir->send.buffer[0], ir->send.count * sizeof ir->send.buffer[0]);
    + ir->send.count += ir->send.count;
    +
    + return 0;
    +}
    +
    +static int decode_sony(struct input_dev *dev, struct ir_protocol *sony, unsigned int d, unsigned int bit)
    +{
    + /* Sony SIRC IR code */
    + /* http://www.sbprojects.com/knowledge/ir/sirc.htm */
    + /* based on a 600us cadence */
    + int ret = 0, delta = d;
    +
    + delta = (delta + 300) / 600;
    +
    + if ((bit == 0) && (delta > 22)) {
    + PDEBUG("SIRC state 1\n");
    + if ((sony->state == 26) || (sony->state == 32) || (sony->state == 42)) {
    + if (sony->good && (sony->good == sony->code)) {
    +
    + input_report_ir(dev, IR_PROTOCOL, (sony->state == 26) ? IR_PROTOCOL_SONY_12 :
    + (sony->state == 32) ? IR_PROTOCOL_SONY_15 : IR_PROTOCOL_SONY_20);
    +
    + if (sony->state == 26) {
    + input_report_ir(dev, IR_DEVICE, sony->code & 0x1F);
    + input_report_ir(dev, IR_COMMAND, sony->code >> 5);
    + } else {
    + input_report_ir(dev, IR_DEVICE, sony->code & 0xFF);
    + input_report_ir(dev, IR_COMMAND, sony->code >> 8);
    + }
    + input_sync(dev);
    +
    + sony->good = 0;
    + ret = 1;
    + } else {
    + PDEBUG("SIRC - Saving %d bit %05x\n", (sony->state - 2) / 2, sony->code);
    + sony->good = sony->code;
    + }
    + }
    + sony->state = 1;
    + sony->code = 0;
    + return ret;
    + }
    + if ((sony->state == 1) && (bit == 1) && (delta == 4)) {
    + sony->state = 2;
    + PDEBUG("SIRC state 2\n");
    + return 0;
    + }
    + if ((sony->state == 2) && (bit == 0) && (delta == 1)) {
    + sony->state = 3;
    + PDEBUG("SIRC state 3\n");
    + return 0;
    + }
    + if ((sony->state >= 3) && (sony->state & 1) && (bit == 1) && ((delta == 1) || (delta == 2))) {
    + sony->state++;
    + sony->code |= ((delta - 1) << ((sony->state - 4) / 2));
    + PDEBUG("SIRC state %d bit %d\n", sony->state, delta - 1);
    + return 0;
    + }
    + if ((sony->state >= 3) && !(sony->state & 1) && (bit == 0) && (delta == 1)) {
    + sony->state++;
    + PDEBUG("SIRC state %d\n", sony-> state);
    + return 0;
    + }
    + sony->state = 0;
    + return 0;
    +}
    +
    +
    +static int encode_jvc(struct ir_device *ir, struct ir_command *command)
    +{
    + /* JVC IR code */
    + /* http://www.sbprojects.com/knowledge/ir/jvc.htm */
    + int i, bit, total;
    +
    + ir->send.count = 0;
    +
    + ir->send.buffer[ir->send.count++] = 8400;
    + ir->send.buffer[ir->send.count++] = 4200;
    +
    + for (i = 0; i < 8; i++) {
    + bit = command->device & 1;
    + command->device >>= 1;
    + ir->send.buffer[ir->send.count++] = 525;
    + ir->send.buffer[ir->send.count++] = (bit ? 1575 : 525);
    + }
    + for (i = 0; i < 8; i++) {
    + bit = command->command & 1;
    + command->command >>= 1;
    + ir->send.buffer[ir->send.count++] = 525;
    + ir->send.buffer[ir->send.count++] = (bit ? 1575 : 525);
    + }
    + ir->send.buffer[ir->send.count++] = 525;
    +
    + total = 0;
    + for (i = 0; i < ir->send.count; i++)
    + total += ir->send.buffer[i];
    + ir->send.buffer[ir->send.count] = 55000 - total;
    +
    + return 0;
    +}
    +
    +static int decode_jvc(struct input_dev *dev, struct ir_protocol *jvc, unsigned int d, unsigned int bit)
    +{
    + /* JVC IR code */
    + /* http://www.sbprojects.com/knowledge/ir/jvc.htm */
    + /* based on a 525us cadence */
    + int ret = 0, delta = d;
    +
    + delta = (delta + 263) / 525;
    +
    + if ((bit == 0) && (delta > 22)) {
    + PDEBUG("JVC state 1\n");
    + jvc->state = 1;
    + jvc->code = 0;
    + return ret;
    + }
    + if ((jvc->state == 1) && (bit == 1) && (delta == 16)) {
    + jvc->state = 2;
    + PDEBUG("JVC state 2\n");
    + return 0;
    + }
    + if ((jvc->state == 2) && (bit == 0) && (delta == 8)) {
    + jvc->state = 3;
    + PDEBUG("JVC state 3\n");
    + return 0;
    + }
    + if ((jvc->state >= 3) && (jvc->state & 1) && (bit == 1) && (delta == 1)) {
    + jvc->state++;
    + PDEBUG("JVC state %d\n", jvc-> state);
    + return 0;
    + }
    + if ((jvc->state >= 3) && !(jvc->state & 1) && (bit == 0) && ((delta == 1) || (delta == 3))) {
    + if (delta == 3)
    + jvc->code |= 1 << ((jvc->state - 4) / 2);
    + jvc->state++;
    + PDEBUG("JVC state %d bit %d\n", jvc->state, delta - 1);
    + if (jvc->state == 34) {
    + jvc->state = 3;
    + if (jvc->good && (jvc->good == jvc->code)) {
    + input_report_ir(dev, IR_PROTOCOL, IR_PROTOCOL_JVC);
    + input_report_ir(dev, IR_DEVICE, jvc->code >> 8);
    + input_report_ir(dev, IR_COMMAND, jvc->code & 0xFF);
    + input_sync(dev);
    + jvc->good = 0;
    + ret = 1;
    + } else {
    + PDEBUG("JVC - Saving 16 bit %05x\n", jvc->code);
    + jvc->good = jvc->code;
    + }
    + jvc->code = 0;
    + }
    + return 0;
    + }
    + jvc->state = 0;
    + return 0;
    +}
    +
    +
    +static int encode_nec(struct ir_device *ir, struct ir_command *command)
    +{
    + /* NEC IR code */
    + /* http://www.sbprojects.com/knowledge/ir/nec.htm */
    + int i, bit, total;
    +
    + ir->send.count = 0;
    +
    + ir->send.buffer[ir->send.count++] = 9000;
    + ir->send.buffer[ir->send.count++] = 4500;
    +
    + for (i = 0; i < 8; i++) {
    + bit = command->device & 1;
    + command->device >>= 1;
    + ir->send.buffer[ir->send.count++] = 563;
    + ir->send.buffer[ir->send.count++] = (bit ? 1687 : 562);
    + }
    + for (i = 0; i < 8; i++) {
    + bit = command->command & 1;
    + command->command >>= 1;
    + ir->send.buffer[ir->send.count++] = 563;
    + ir->send.buffer[ir->send.count++] = (bit ? 1687 : 562);
    + }
    + ir->send.buffer[ir->send.count++] = 562;
    +
    + total = 0;
    + for (i = 0; i < ir->send.count; i++)
    + total += ir->send.buffer[i];
    + ir->send.buffer[ir->send.count] = 110000 - total;
    +
    + return 0;
    +}
    +
    +static int decode_nec(struct input_dev *dev, struct ir_protocol *nec, unsigned int d, unsigned int bit)
    +{
    + /* NEC IR code */
    + /* http://www.sbprojects.com/knowledge/ir/nec.htm */
    + /* based on a 560us cadence */
    + int delta = d;
    +
    + delta = (delta + 280) / 560;
    +
    + if ((bit == 0) && (delta > 22)) {
    + PDEBUG("nec state 1\n");
    + nec->state = 1;
    + nec->code = 0;
    + return 0;
    + }
    + if ((nec->state == 1) && (bit == 1) && (delta == 16)) {
    + nec->state = 2;
    + PDEBUG("nec state 2\n");
    + return 0;
    + }
    + if ((nec->state == 2) && (bit == 0) && (delta == 8)) {
    + nec->state = 3;
    + PDEBUG("nec state 3\n");
    + return 0;
    + }
    + if ((nec->state >= 3) && (nec->state & 1) && (bit == 1) && (delta == 1)) {
    + nec->state++;
    + PDEBUG("nec state %d\n", nec-> state);
    + if (nec->state == 68) {
    + input_report_ir(dev, IR_PROTOCOL, IR_PROTOCOL_NEC);
    + input_report_ir(dev, IR_DEVICE, nec->code >> 16);
    + input_report_ir(dev, IR_COMMAND, nec->code & 0xFFFF);
    + input_sync(dev);
    + return 1;
    + }
    + return 0;
    + }
    + if ((nec->state >= 3) && !(nec->state & 1) && (bit == 0) && ((delta == 1) || (delta == 3))) {
    + if (delta == 3)
    + nec->code |= 1 << ((nec->state - 4) / 2);
    + nec->state++;
    + PDEBUG("nec state %d bit %d\n", nec->state, delta - 1);
    + return 0;
    + }
    + nec->state = 0;
    + nec->code = 0;
    + return 0;
    +}
    +
    +
    +static int encode_rc5(struct ir_device *ir, struct ir_command *command)
    +{
    + /* Philips RC-5 IR code */
    + /* http://www.sbprojects.com/knowledge/ir/rc5.htm */
    + return 0;
    +}
    +
    +static int decode_rc5(struct input_dev *dev, struct ir_protocol *rc5, unsigned int d, unsigned int bit)
    +{
    + /* Philips RC-5 IR code */
    + /* http://www.sbprojects.com/knowledge/ir/rc5.htm */
    + /* based on a 889us cadence */
    + int delta = d;
    +
    + delta = (delta + 444) / 889;
    +
    + return 0;
    +}
    +
    +
    +static int encode_rc6(struct ir_device *ir, struct ir_command *command)
    +{
    + /* Philips RC-6 IR code */
    + /* http://www.sbprojects.com/knowledge/ir/rc6.htm */
    + int i, bit, last;
    +
    + ir->send.count = 0;
    +
    + ir->send.buffer[ir->send.count++] = 2666;
    + ir->send.buffer[ir->send.count++] = 889;
    +
    + ir->send.buffer[ir->send.count++] = 444;
    + ir->send.buffer[ir->send.count++] = 444;
    +
    + last = 1;
    + for (i = 0; i < 8; i++) {
    + bit = command->device & 1;
    + command->device >>= 1;
    +
    + if (last != bit)
    + ir->send.buffer[ir->send.count - 1] += 444;
    + else
    + ir->send.buffer[ir->send.count++] = 444;
    + ir->send.buffer[ir->send.count++] = 444;
    + last = bit;
    + }
    + for (i = 0; i < 8; i++) {
    + bit = command->command & 1;
    + command->command >>= 1;
    +
    + if (last != bit)
    + ir->send.buffer[ir->send.count - 1] += 444;
    + else
    + ir->send.buffer[ir->send.count++] = 444;
    + ir->send.buffer[ir->send.count++] = 444;
    + last = bit;
    + }
    + ir->send.buffer[ir->send.count] = 2666;
    +
    + return 0;
    +}
    +
    +static void decode_rc6_bit(struct input_dev *dev, struct ir_protocol *rc6, unsigned int bit)
    +{
    + /* bits come in one at a time */
    + /* when two are collected look for a symbol */
    + /* rc6->bits == 1 is a zero symbol */
    + /* rc6->bits == 2 is a one symbol */
    + rc6->count++;
    + rc6->bits <<= 1;
    + rc6->bits |= bit;
    + if (rc6->count == 2) {
    + if ((rc6->bits == 0) || (rc6->bits == 3)) {
    + rc6->mode = rc6->code;
    + rc6->code = 0;
    + } else {
    + rc6->code <<= 1;
    + if (rc6->bits == 2)
    + rc6->code |= 1;
    + }
    + rc6->count = 0;
    + if (rc6->state == 23) {
    + input_report_ir(dev, IR_PROTOCOL, IR_PROTOCOL_PHILIPS_RC6);
    + input_report_ir(dev, IR_DEVICE, rc6->code >> 8);
    + input_report_ir(dev, IR_COMMAND, rc6->code & 0xFF);
    + input_sync(dev);
    + rc6->state = 0;
    + } else
    + rc6->state++;
    + PDEBUG("rc6 state %d bit %d\n", rc6->state, rc6->bits == 2);
    + rc6->bits = 0;
    + }
    +}
    +
    +static int decode_rc6(struct input_dev *dev, struct ir_protocol *rc6, unsigned int d, unsigned int bit)
    +{
    + /* Philips RC-6 IR code */
    + /* http://www.sbprojects.com/knowledge/ir/rc6.htm */
    + /* based on a 444us cadence */
    +
    + int delta = d;
    +
    + delta = (delta + 222) / 444;
    +
    + if ((bit == 0) && (delta > 19)) {
    + rc6->count = 0;
    + rc6->bits = 0;
    + rc6->state = 1;
    + rc6->code = 0;
    + PDEBUG("rc6 state 1\n");
    + return 0;
    + }
    + if ((rc6->state == 1) && (bit == 1) && (delta == 6)) {
    + rc6->state = 2;
    + PDEBUG("rc6 state 2\n");
    + return 0;
    + }
    + if ((rc6->state == 2) && (bit == 0) && (delta == 2)) {
    + rc6->state = 3;
    + PDEBUG("rc6 state 3\n");
    + return 0;
    + }
    + if (rc6->state >= 3) {
    + if ((delta >= 1) || (delta <= 3)) {
    + while (delta-- >= 1)
    + decode_rc6_bit(dev, rc6, bit);
    + return 0;
    + }
    + }
    + rc6->state = 0;
    + rc6->code = 0;
    + return 0;
    +}
    +
    +static void record_raw(struct input_dev *dev, unsigned int delta, unsigned int bit)
    +{
    + int head = dev->ir->raw.head;
    + if (bit)
    + delta = -delta;
    +
    + head += 1;
    + if (head > sizeof dev->ir->raw.buffer)
    + head = 0;
    +
    + if (head != dev->ir->raw.tail) {
    + dev->ir->raw.buffer[dev->ir->raw.head] = delta;
    + dev->ir->raw.head = head;
    + }
    +}
    +
    +void input_ir_decode(struct input_dev *dev, unsigned int delta, unsigned int bit)
    +{
    + PDEBUG("IR bit %d %d\n", delta, bit);
    + record_raw(dev, delta, bit);
    + decode_sony(dev, &dev->ir->sony, delta, bit);
    + decode_jvc(dev, &dev->ir->jvc, delta, bit);
    + decode_nec(dev, &dev->ir->nec, delta, bit);
    + decode_rc5(dev, &dev->ir->rc5, delta, bit);
    + decode_rc6(dev, &dev->ir->rc6, delta, bit);
    +}
    +EXPORT_SYMBOL_GPL(input_ir_decode);
    +
    +static ssize_t ir_raw_show(struct device *dev,
    + struct device_attribute *attr, char *buf)
    +{
    + struct input_dev *input_dev = to_input_dev(dev);
    + unsigned int i = input_dev->ir->raw.tail;
    + unsigned int count = 0;
    +
    + printk("head %d tail %d\n", input_dev->ir->raw.head, input_dev->ir->raw.tail);
    + while (i != input_dev->ir->raw.head) {
    + count += snprintf(&buf[count], PAGE_SIZE, "%i\n", input_dev->ir->raw.buffer[i++]);
    + if (count >= PAGE_SIZE) {
    + input_dev->ir->raw.tail = i;
    + return PAGE_SIZE;
    + }
    + if (i > sizeof input_dev->ir->raw.buffer)
    + i = 0;
    + }
    + input_dev->ir->raw.tail = i;
    + return count;
    +}
    +
    +static ssize_t ir_raw_store(struct device *dev,
    + struct device_attribute *attr,
    + const char *buf,
    + size_t count)
    +{
    + struct ir_device *ir = to_input_dev(dev)->ir;
    + long delta;
    + int i = count;
    + int first = 0;
    +
    + if (!ir->xmit)
    + return count;
    + ir->send.count = 0;
    +
    + while (i > 0) {
    + i -= strict_strtoul(&buf[i], i, &delta);
    + while ((buf[i] != '\n') && (i > 0))
    + i--;
    + i--;
    + /* skip leading zeros */
    + if ((delta > 0) && !first)
    + continue;
    +
    + ir->send.buffer[ir->send.count++] = abs(delta);
    + }
    +
    + ir->xmit(ir->private, ir->send.buffer, ir->send.count, ir->raw.carrier, ir->raw.xmitter);
    +
    + return count;
    +}
    +
    +static ssize_t ir_carrier_show(struct device *dev,
    + struct device_attribute *attr, char *buf)
    +{
    + struct ir_device *ir = to_input_dev(dev)->ir;
    +
    + return sprintf(buf, "%i\n", ir->raw.carrier);
    +}
    +
    +static ssize_t ir_carrier_store(struct device *dev,
    + struct device_attribute *attr,
    + const char *buf,
    + size_t count)
    +{
    + struct ir_device *ir = to_input_dev(dev)->ir;
    +
    + ir->raw.carrier = simple_strtoul(buf, NULL, 0);
    + return count;
    +}
    +
    +static ssize_t ir_xmitter_show(struct device *dev,
    + struct device_attribute *attr, char *buf)
    +{
    + struct ir_device *ir = to_input_dev(dev)->ir;
    +
    + return sprintf(buf, "%i\n", ir->raw.xmitter);
    +}
    +
    +static ssize_t ir_xmitter_store(struct device *dev,
    + struct device_attribute *attr,
    + const char *buf,
    + size_t count)
    +{
    + struct ir_device *ir = to_input_dev(dev)->ir;
    +
    + ir->raw.xmitter = simple_strtoul(buf, NULL, 0);
    + return count;
    +}
    +
    +static DEVICE_ATTR(ir_raw, 0644, ir_raw_show, ir_raw_store);
    +static DEVICE_ATTR(ir_carrier, 0644, ir_carrier_show, ir_carrier_store);
    +static DEVICE_ATTR(ir_xmitter, 0644, ir_xmitter_show, ir_xmitter_store);
    +
    +int input_ir_register(struct input_dev *dev)
    +{
    + int rc;
    +
    + if (!dev->ir)
    + return 0;
    +
    + rc = device_create_file(&dev->dev, &dev_attr_ir_raw);
    + if (rc)
    + return rc;
    +
    + rc = device_create_file(&dev->dev, &dev_attr_ir_carrier);
    + if (rc)
    + return rc;
    +
    + rc = device_create_file(&dev->dev, &dev_attr_ir_xmitter);
    + return rc;
    +}
    +
    +int input_ir_create(struct input_dev *dev, void *private, send_func xmit)
    +{
    + dev->ir = kzalloc(sizeof(struct ir_device), GFP_KERNEL);
    + if (!dev->ir)
    + return -ENOMEM;
    +
    + dev->evbit[0] = BIT_MASK(EV_IR);
    + dev->ir->private = private;
    + dev->ir->xmit = xmit;
    +
    + return 0;
    +}
    +EXPORT_SYMBOL_GPL(input_ir_create);
    +
    +
    +void input_ir_destroy(struct input_dev *dev)
    +{
    + if (dev->ir) {
    + kfree(dev->ir);
    + dev->ir = NULL;
    + }
    +}
    +EXPORT_SYMBOL_GPL(input_ir_destroy);
    +
    +int input_ir_send(struct input_dev *dev, struct ir_command *ir_command, struct file *file)
    +{
    + unsigned freq, xmit = 0;
    + int ret;
    +
    + mutex_lock(&dev->ir->lock);
    +
    + switch (ir_command->protocol) {
    + case IR_PROTOCOL_PHILIPS_RC5:
    + freq = 36000;
    + encode_rc5(dev->ir, ir_command);
    + break;
    + case IR_PROTOCOL_PHILIPS_RC6:
    + freq = 36000;
    + encode_rc6(dev->ir, ir_command);
    + break;
    + case IR_PROTOCOL_PHILIPS_RCMM:
    + freq = 36000;
    + encode_rc5(dev->ir, ir_command);
    + break;
    + case IR_PROTOCOL_JVC:
    + freq = 38000;
    + encode_jvc(dev->ir, ir_command);
    + break;
    + case IR_PROTOCOL_NEC:
    + freq = 38000;
    + encode_nec(dev->ir, ir_command);
    + break;
    + case IR_PROTOCOL_NOKIA:
    + case IR_PROTOCOL_SHARP:
    + case IR_PROTOCOL_PHILIPS_RECS80:
    + freq = 38000;
    + break;
    + case IR_PROTOCOL_SONY_12:
    + case IR_PROTOCOL_SONY_15:
    + case IR_PROTOCOL_SONY_20:
    + encode_sony(dev->ir, ir_command);
    + freq = 40000;
    + break;
    + case IR_PROTOCOL_RCA:
    + freq = 56000;
    + break;
    + case IR_PROTOCOL_ITT:
    + freq = 0;
    + break;
    + default:
    + ret = -ENODEV;
    + goto exit;
    + }
    +
    + if (dev->ir && dev->ir->xmit)
    + ret = dev->ir->xmit(dev->ir->private, dev->ir->send.buffer, dev->ir->send.count, freq, xmit);
    + else
    + ret = -ENODEV;
    +
    +exit:
    + mutex_unlock(&dev->ir->lock);
    + return ret;
    +}
    +EXPORT_SYMBOL_GPL(input_ir_send);
    +
    diff --git a/drivers/input/ir/Kconfig b/drivers/input/ir/Kconfig
    new file mode 100644
    index 0000000..8afd2d6
    --- /dev/null
    +++ b/drivers/input/ir/Kconfig
    @@ -0,0 +1,14 @@
    +#
    +# LIRC driver(s) configuration
    +#
    +menuconfig INPUT_IR
    + bool "Infrared Remote (IR) receiver/transmitter drivers"
    + default n
    + help
    + Say Y here, and all supported Infrared Remote Control IR
    + receiver and transmitter drivers will be displayed. The receiver drivers
    + allow control of your Linux system via remote control.
    +
    +if INPUT_IR
    +
    +endif
    diff --git a/drivers/input/ir/Makefile b/drivers/input/ir/Makefile
    new file mode 100644
    index 0000000..08e6954
    --- /dev/null
    +++ b/drivers/input/ir/Makefile
    @@ -0,0 +1,5 @@
    +# Makefile for the ir drivers.
    +#
    +
    +# Each configuration option enables a list of files.
    +
    diff --git a/include/linux/input.h b/include/linux/input.h
    index a5802c9..2fbdf5a 100644
    --- a/include/linux/input.h
    +++ b/include/linux/input.h
    @@ -79,6 +79,8 @@ struct input_absinfo {
    #define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */
    #define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */

    +#define EVIOIRSEND _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ir_command)) /* send an IR command */
    +
    #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */

    /*
    @@ -97,6 +99,7 @@ struct input_absinfo {
    #define EV_FF 0x15
    #define EV_PWR 0x16
    #define EV_FF_STATUS 0x17
    +#define EV_IR 0x18
    #define EV_MAX 0x1f
    #define EV_CNT (EV_MAX+1)

    @@ -946,6 +949,55 @@ struct ff_effect {
    #define FF_MAX 0x7f
    #define FF_CNT (FF_MAX+1)

    +/*
    + * IR Support
    + */
    +
    +#define IR_PROTOCOL_JVC 1
    +#define IR_PROTOCOL_NEC 2
    +#define IR_PROTOCOL_NOKIA 3
    +#define IR_PROTOCOL_SHARP 4
    +#define IR_PROTOCOL_SONY_12 5
    +#define IR_PROTOCOL_SONY_15 6
    +#define IR_PROTOCOL_SONY_20 7
    +#define IR_PROTOCOL_PHILIPS_RC5 8
    +#define IR_PROTOCOL_PHILIPS_RC6 9
    +#define IR_PROTOCOL_PHILIPS_RCMM 10
    +#define IR_PROTOCOL_PHILIPS_RECS80 11
    +#define IR_PROTOCOL_RCA 12
    +#define IR_PROTOCOL_ITT 13
    +
    +#define IR_PROTOCOL 1
    +#define IR_DEVICE 2
    +#define IR_COMMAND 3
    +
    +#define IR_CAP_RECEIVE_BASEBAND 0
    +#define IR_CAP_RECEIVE_36K 1
    +#define IR_CAP_RECEIVE_38K 2
    +#define IR_CAP_RECEIVE_40K 3
    +#define IR_CAP_RECEIVE_56K 4
    +#define IR_CAP_SEND_BASEBAND 5
    +#define IR_CAP_SEND_36K 6
    +#define IR_CAP_SEND_38K 7
    +#define IR_CAP_SEND_40K 8
    +#define IR_CAP_SEND_56K 9
    +#define IR_CAP_XMITTER_1 10
    +#define IR_CAP_XMITTER_2 11
    +#define IR_CAP_XMITTER_3 12
    +#define IR_CAP_XMITTER_4 13
    +#define IR_CAP_RECEIVE_RAW 14
    +#define IR_CAP_SEND_RAW 15
    +#define IR_MAX 0x0f
    +#define IR_CNT IR_MAX + 1
    +
    +struct ir_command {
    + __u32 protocol;
    + __u32 device;
    + __u32 command;
    + __u32 transmitters;
    +};
    +
    +
    #ifdef __KERNEL__

    /*
    @@ -973,6 +1025,7 @@ struct ff_effect {
    * @sndbit: bitmap of sound effects supported by the device
    * @ffbit: bitmap of force feedback effects supported by the device
    * @swbit: bitmap of switches present on the device
    + * @irbit: bitmap of capabilies of the IR hardware
    * @keycodemax: size of keycode table
    * @keycodesize: size of elements in keycode table
    * @keycode: map of scancodes to keycodes for this device
    @@ -1045,6 +1098,7 @@ struct input_dev {
    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
    + unsigned long irbit[BITS_TO_LONGS(IR_CNT)];

    unsigned int keycodemax;
    unsigned int keycodesize;
    @@ -1053,6 +1107,7 @@ struct input_dev {
    int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);

    struct ff_device *ff;
    + struct ir_device *ir;

    unsigned int repeat_key;
    struct timer_list timer;
    @@ -1288,6 +1343,11 @@ static inline void input_report_switch(struct input_dev *dev, unsigned int code,
    input_event(dev, EV_SW, code, !!value);
    }

    +static inline void input_report_ir(struct input_dev *dev, unsigned int code, int value)
    +{
    + input_event(dev, EV_IR, code, value);
    +}
    +
    static inline void input_sync(struct input_dev *dev)
    {
    input_event(dev, EV_SYN, SYN_REPORT, 0);
    @@ -1366,5 +1426,46 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
    int input_ff_create_memless(struct input_dev *dev, void *data,
    int (*play_effect)(struct input_dev *, void *, struct ff_effect *));

    +/**
    + * struct ir_device - IR support structures
    + */
    +
    +struct ir_protocol {
    + unsigned int state, code, good, count, bits, mode;
    +};
    +
    +typedef int (*send_func)(void *private, unsigned int *buffer, unsigned int count,
    + unsigned int frequency, unsigned int xmitters);
    +
    +struct ir_device {
    + struct ir_protocol sony;
    + struct ir_protocol jvc;
    + struct ir_protocol nec;
    + struct ir_protocol rc5;
    + struct ir_protocol rc6;
    + struct mutex lock;
    + void *private;
    + send_func xmit;
    + struct {
    + unsigned int buffer[200];
    + unsigned int count;
    + } send;
    + struct {
    + int buffer[200];
    + unsigned int head;
    + unsigned int tail;
    + unsigned int carrier;
    + unsigned int xmitter;
    + } raw;
    +};
    +
    +int input_ir_create(struct input_dev *dev, void *private, send_func send);
    +void input_ir_destroy(struct input_dev *dev);
    +
    +void input_ir_decode(struct input_dev *dev, unsigned int delta, unsigned int bit);
    +int input_ir_send(struct input_dev *dev, struct ir_command *ir_command, struct file *file);
    +
    +int input_ir_register(struct input_dev *dev);
    +
    #endif
    #endif
    diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
    index c4db582..cb088ec 100644
    --- a/include/linux/mod_devicetable.h
    +++ b/include/linux/mod_devicetable.h
    @@ -282,6 +282,7 @@ struct pcmcia_device_id {
    #define INPUT_DEVICE_ID_SND_MAX 0x07
    #define INPUT_DEVICE_ID_FF_MAX 0x7f
    #define INPUT_DEVICE_ID_SW_MAX 0x0f
    +#define INPUT_DEVICE_ID_IR_MAX 0x0f

    #define INPUT_DEVICE_ID_MATCH_BUS 1
    #define INPUT_DEVICE_ID_MATCH_VENDOR 2
    @@ -297,6 +298,7 @@ struct pcmcia_device_id {
    #define INPUT_DEVICE_ID_MATCH_SNDBIT 0x0400
    #define INPUT_DEVICE_ID_MATCH_FFBIT 0x0800
    #define INPUT_DEVICE_ID_MATCH_SWBIT 0x1000
    +#define INPUT_DEVICE_ID_MATCH_IRBIT 0x2000

    struct input_device_id {

    @@ -316,6 +318,7 @@ struct input_device_id {
    kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
    + kernel_ulong_t irbit[INPUT_DEVICE_ID_IR_MAX / BITS_PER_LONG + 1];

    kernel_ulong_t driver_info;
    };

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  4. [RFC PATCH 2/4] GPT driver for in-kernel IR support.

    GPT is a GPIO pin that is cable able of measuring the lenght of pulses.
    GPTs are common on embedded systems
    ---
    drivers/input/ir/Kconfig | 6 +
    drivers/input/ir/Makefile | 1
    drivers/input/ir/ir-gpt.c | 221 +++++++++++++++++++++++++++++++++++++++++++++
    3 files changed, 228 insertions(+), 0 deletions(-)
    create mode 100644 drivers/input/ir/ir-gpt.c

    diff --git a/drivers/input/ir/Kconfig b/drivers/input/ir/Kconfig
    index 8afd2d6..b80ab31 100644
    --- a/drivers/input/ir/Kconfig
    +++ b/drivers/input/ir/Kconfig
    @@ -11,4 +11,10 @@ menuconfig INPUT_IR

    if INPUT_IR

    +config IR_GPT
    + tristate "GPT Based IR Receiver"
    + default m
    + help
    + Driver for GPT-based IR receiver found on Digispeaker
    +
    endif
    diff --git a/drivers/input/ir/Makefile b/drivers/input/ir/Makefile
    index 08e6954..7082f1d 100644
    --- a/drivers/input/ir/Makefile
    +++ b/drivers/input/ir/Makefile
    @@ -3,3 +3,4 @@

    # Each configuration option enables a list of files.

    +obj-$(CONFIG_IR_GPT) += ir-gpt.o
    diff --git a/drivers/input/ir/ir-gpt.c b/drivers/input/ir/ir-gpt.c
    new file mode 100644
    index 0000000..a26abe9
    --- /dev/null
    +++ b/drivers/input/ir/ir-gpt.c
    @@ -0,0 +1,221 @@
    +/*
    + * GPT timer based IR device
    + *
    + * Copyright (C) 2008 Jon Smirl
    + */
    +
    +#define DEBUG
    +
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +
    +#define MAX_SAMPLES 200
    +
    +struct ir_gpt {
    + int irq;
    + struct mpc52xx_gpt __iomem *regs;
    + spinlock_t lock;
    + struct work_struct queue;
    + int head, tail, previous;
    + unsigned int samples[MAX_SAMPLES];
    + struct input_dev *input;
    +};
    +
    +static void ir_event(struct work_struct *work)
    +{
    + unsigned long flags;
    + int delta, count;
    + unsigned int sample, wrap, bit;
    + struct ir_gpt *ir_gpt = container_of(work, struct ir_gpt, queue);
    +
    + while (1) {
    + spin_lock_irqsave(ir_gpt->lock, flags);
    + if (ir_gpt->tail == ir_gpt->head) {
    + spin_unlock_irqrestore(ir_gpt->lock, flags);
    + break;
    + }
    + sample = ir_gpt->samples[ir_gpt->tail];
    +
    + ir_gpt->tail++;
    + if (ir_gpt->tail >= MAX_SAMPLES)
    + ir_gpt->tail = 0;
    +
    + spin_unlock_irqrestore(ir_gpt->lock, flags);
    +
    + count = sample >> 16;
    + wrap = (sample >> 12) & 7;
    + bit = (sample >> 8) & 1;
    +
    + delta = count - ir_gpt->previous;
    + delta += wrap * 0x10000;
    +
    + ir_gpt->previous = count;
    +
    + input_ir_decode(ir_gpt->input, delta, bit);
    + }
    +}
    +
    +/*
    + * Interrupt handlers
    + */
    +static irqreturn_t dpeak_ir_irq(int irq, void *_ir)
    +{
    + unsigned long flags;
    + unsigned int sample, next;
    + struct ir_gpt *ir_gpt = _ir;
    +
    + sample = in_be32(&ir_gpt->regs->status);
    + out_be32(&ir_gpt->regs->status, 0xF);
    +
    + spin_lock_irqsave(ir_gpt->lock, flags);
    + ir_gpt->samples[ir_gpt->head] = sample;
    + next = ir_gpt->head + 1;
    + ir_gpt->head = (next >= MAX_SAMPLES ? 0 : next);
    + spin_unlock_irqrestore(ir_gpt->lock, flags);
    +
    + schedule_work(&ir_gpt->queue);
    +
    + return IRQ_HANDLED;
    +}
    +
    +
    +/* ---------------------------------------------------------------------
    + * OF platform bus binding code:
    + * - Probe/remove operations
    + * - OF device match table
    + */
    +static int __devinit ir_gpt_of_probe(struct of_device *op,
    + const struct of_device_id *match)
    +{
    + struct ir_gpt *ir_gpt;
    + struct resource res;
    + int ret, rc;
    +
    + dev_dbg(&op->dev, "ir_gpt_of_probe\n");
    +
    + /* Allocate and initialize the driver private data */
    + ir_gpt = kzalloc(sizeof *ir_gpt, GFP_KERNEL);
    + if (!ir_gpt)
    + return -ENOMEM;
    +
    + ir_gpt->input = input_allocate_device();
    + if (!ir_gpt->input) {
    + ret = -ENOMEM;
    + goto free_mem;
    + }
    + ret = input_ir_create(ir_gpt->input, ir_gpt, NULL);
    + if (ret)
    + goto free_input;
    +
    + ir_gpt->input->id.bustype = BUS_HOST;
    + ir_gpt->input->name = "GPT IR Receiver";
    +
    + ir_gpt->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_36K);
    + ir_gpt->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_38K);
    + ir_gpt->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_40K);
    + ir_gpt->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_RAW);
    +
    + ret = input_register_device(ir_gpt->input);
    + if (ret)
    + goto free_input;
    +
    + spin_lock_init(&ir_gpt->lock);
    + INIT_WORK (&ir_gpt->queue, ir_event);
    +
    + /* Fetch the registers and IRQ of the GPT */
    + if (of_address_to_resource(op->node, 0, &res)) {
    + dev_err(&op->dev, "Missing reg property\n");
    + ret = -ENODEV;
    + goto free_input;
    + }
    + ir_gpt->regs = ioremap(res.start, 1 + res.end - res.start);
    + if (!ir_gpt->regs) {
    + dev_err(&op->dev, "Could not map registers\n");
    + ret = -ENODEV;
    + goto free_input;
    + }
    + ir_gpt->irq = irq_of_parse_and_map(op->node, 0);
    + if (ir_gpt->irq == NO_IRQ) {
    + ret = -ENODEV;
    + goto free_input;
    + }
    + dev_dbg(&op->dev, "ir_gpt_of_probe irq=%d\n", ir_gpt->irq);
    +
    + rc = request_irq(ir_gpt->irq, &dpeak_ir_irq, IRQF_SHARED,
    + "gpt-ir", ir_gpt);
    + dev_dbg(&op->dev, "ir_gpt_of_probe request irq rc=%d\n", rc);
    +
    + /* set prescale to ? */
    + out_be32(&ir_gpt->regs->count, 0x00870000);
    +
    + /* Select input capture, enable the counter, and interrupt */
    + out_be32(&ir_gpt->regs->mode, 0x0);
    + out_be32(&ir_gpt->regs->mode, 0x00000501);
    +
    + /* Save what we've done so it can be found again later */
    + dev_set_drvdata(&op->dev, ir_gpt);
    +
    + printk("GPT IR Receiver driver\n");
    +
    + return 0;
    +
    +free_input:
    + input_ir_destroy(ir_gpt->input);
    + input_free_device(ir_gpt->input);
    +free_mem:
    + kfree(ir_gpt);
    + return ret;
    +}
    +
    +static int __devexit ir_gpt_of_remove(struct of_device *op)
    +{
    + struct ir_gpt *ir_gpt = dev_get_drvdata(&op->dev);
    +
    + dev_dbg(&op->dev, "ir_gpt_remove()\n");
    +
    + input_ir_destroy(ir_gpt->input);
    + input_free_device(ir_gpt->input);
    + kfree(ir_gpt);
    + dev_set_drvdata(&op->dev, NULL);
    +
    + return 0;
    +}
    +
    +/* Match table for of_platform binding */
    +static struct of_device_id ir_gpt_match[] __devinitdata = {
    + { .compatible = "gpt-ir", },
    + {}
    +};
    +MODULE_DEVICE_TABLE(of, ir_gpt_match);
    +
    +static struct of_platform_driver ir_gpt_driver = {
    + .match_table = ir_gpt_match,
    + .probe = ir_gpt_of_probe,
    + .remove = __devexit_p(ir_gpt_of_remove),
    + .driver = {
    + .name = "ir-gpt",
    + .owner = THIS_MODULE,
    + },
    +};
    +
    +/* ---------------------------------------------------------------------
    + * Module setup and teardown; simply register the of_platform driver
    + */
    +static int __init ir_gpt_init(void)
    +{
    + return of_register_platform_driver(&ir_gpt_driver);
    +}
    +module_init(ir_gpt_init);
    +
    +static void __exit ir_gpt_exit(void)
    +{
    + of_unregister_platform_driver(&ir_gpt_driver);
    +}
    +module_exit(ir_gpt_exit);

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  5. Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem

    Hi!

    > What should the IR API look like? How are IR events mapped into
    > keyboard events? Should they be mapped? Map them in the kernel or in
    > user space? The maps are tiny, less than 1K per remote. Sysfs can
    > be used to load maps into the kernel driver. Make maps only for the
    > common buttons and don't map unusual ones?


    Map them in kernel, in analogy with normal keyboards.

    > How should multiple remotes be handled? Split them out into
    > individual input devices, or group them onto a single IR device? I
    > can implement either.


    Individual input devices, I'd say... so that app can only listen for
    'its' remote.

    --
    (english) http://www.livejournal.com/~pavelmachek
    (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pav...rses/blog.html
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  6. Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem

    On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:
    > Hi!
    >
    > > What should the IR API look like? How are IR events mapped into
    > > keyboard events? Should they be mapped? Map them in the kernel or in
    > > user space? The maps are tiny, less than 1K per remote. Sysfs can
    > > be used to load maps into the kernel driver. Make maps only for the
    > > common buttons and don't map unusual ones?

    >
    > Map them in kernel, in analogy with normal keyboards.


    My thought was to basically follow what the ati_remote{,2} driver does.

    > > How should multiple remotes be handled? Split them out into
    > > individual input devices, or group them onto a single IR device? I
    > > can implement either.

    >
    > Individual input devices, I'd say... so that app can only listen for
    > 'its' remote.


    I don't quite get it. How can we tell there are multiple remotes to set
    up multiple input devices when the system comes up? All we can know
    about at driver init time is the receiver(s), no? Or would this be keyed
    off a config file? Or ______ ?


    --
    Jarod Wilson
    jarod@wilsonet.com

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  7. Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem

    On Fri, Oct 10, 2008 at 12:11 AM, Jarod Wilson wrote:
    > On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:
    >> Hi!
    >>
    >> > What should the IR API look like? How are IR events mapped into
    >> > keyboard events? Should they be mapped? Map them in the kernel or in
    >> > user space? The maps are tiny, less than 1K per remote. Sysfs can
    >> > be used to load maps into the kernel driver. Make maps only for the
    >> > common buttons and don't map unusual ones?

    >>
    >> Map them in kernel, in analogy with normal keyboards.

    >
    > My thought was to basically follow what the ati_remote{,2} driver does.
    >
    >> > How should multiple remotes be handled? Split them out into
    >> > individual input devices, or group them onto a single IR device? I
    >> > can implement either.

    >>
    >> Individual input devices, I'd say... so that app can only listen for
    >> 'its' remote.

    >
    > I don't quite get it. How can we tell there are multiple remotes to set
    > up multiple input devices when the system comes up? All we can know
    > about at driver init time is the receiver(s), no? Or would this be keyed
    > off a config file? Or ______ ?



    We could create a sysfs attribut named ir_config. For each config file
    you copy to it it creates a new input device. The config files have
    lists of map this protocol, device, command tuple to this key. When a
    remote button is pressed the raw codes are fed to the in-kernel
    protocol engine. That engine turns the raw codes into tuples. Tuples
    are matched against the configs that have been loaded until a hit is
    found. If no hit they get sent out the catch-all device.

    Most remotes send out unique codes, they have to or they would turn on
    unintended devices.


    >
    >
    > --
    > Jarod Wilson
    > jarod@wilsonet.com
    >
    >




    --
    Jon Smirl
    jonsmirl@gmail.com
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  8. Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem

    On Fri, 2008-10-10 at 01:04 -0400, Jon Smirl wrote:
    > On Fri, Oct 10, 2008 at 12:11 AM, Jarod Wilson wrote:
    > > On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:

    [...]
    > >> > How should multiple remotes be handled? Split them out into
    > >> > individual input devices, or group them onto a single IR device? I
    > >> > can implement either.
    > >>
    > >> Individual input devices, I'd say... so that app can only listen for
    > >> 'its' remote.

    > >
    > > I don't quite get it. How can we tell there are multiple remotes to set
    > > up multiple input devices when the system comes up? All we can know
    > > about at driver init time is the receiver(s), no? Or would this be keyed
    > > off a config file? Or ______ ?

    >
    >
    > We could create a sysfs attribut named ir_config. For each config file
    > you copy to it it creates a new input device. The config files have
    > lists of map this protocol, device, command tuple to this key. When a
    > remote button is pressed the raw codes are fed to the in-kernel
    > protocol engine. That engine turns the raw codes into tuples. Tuples
    > are matched against the configs that have been loaded until a hit is
    > found. If no hit they get sent out the catch-all device.


    Okay. I presume the catch-all device would simply note that it saw
    something, and not actually take any action beyond that.

    Also, a minor clarification: If its config file based, then its not so
    much one input device per remote, its one input device per config file,
    so one could set up two separate config files that are actually commands
    from the same remote, but have them listened to by different apps, or
    one could group together commands from multiple remotes in a single
    config so that multiple remotes would all control the same thing. Sounds
    good to me, provides for a reasonable amount of flexibility.

    Of course, this suggests a receiver wouldn't be able to do anything
    until the user provided a key mapping table of some sort. Would it make
    sense to provide a default IR-to-key mapping for each receiver type? A
    bit difficult to provide a sane default for something like a home-brew
    serial IR receiver, but you could certainly provide sane defaults for
    things like mceusb and imon (i.e., mappings for the remotes typically
    bundled with those receivers).

    > Most remotes send out unique codes, they have to or they would turn on
    > unintended devices.


    Yep, that's actually where the root of my concern was. I have at least a
    half dozen different remote-controlled devices in my AV cabinet near my
    myth box, and I obviously don't want anything but the codes I've set up
    for my myth box acted upon on the myth box.


    --
    Jarod Wilson
    jarod@wilsonet.com

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  9. Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem

    On Fri, Oct 10, 2008 at 9:42 AM, Jarod Wilson wrote:
    > On Fri, 2008-10-10 at 01:04 -0400, Jon Smirl wrote:
    >> On Fri, Oct 10, 2008 at 12:11 AM, Jarod Wilson wrote:
    >> > On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:

    > [...]
    >> >> > How should multiple remotes be handled? Split them out into
    >> >> > individual input devices, or group them onto a single IR device? I
    >> >> > can implement either.
    >> >>
    >> >> Individual input devices, I'd say... so that app can only listen for
    >> >> 'its' remote.
    >> >
    >> > I don't quite get it. How can we tell there are multiple remotes to set
    >> > up multiple input devices when the system comes up? All we can know
    >> > about at driver init time is the receiver(s), no? Or would this be keyed
    >> > off a config file? Or ______ ?

    >>
    >>
    >> We could create a sysfs attribut named ir_config. For each config file
    >> you copy to it it creates a new input device. The config files have
    >> lists of map this protocol, device, command tuple to this key. When a
    >> remote button is pressed the raw codes are fed to the in-kernel
    >> protocol engine. That engine turns the raw codes into tuples. Tuples
    >> are matched against the configs that have been loaded until a hit is
    >> found. If no hit they get sent out the catch-all device.

    >
    > Okay. I presume the catch-all device would simply note that it saw
    > something, and not actually take any action beyond that.


    The catch-all device is an device in /dev/input. It gets the input
    tuples in protocol/device/command form sent to it. I should probably
    send all IR input to this device, and flag the events that were
    translated and sent to another input device.

    >
    > Also, a minor clarification: If its config file based, then its not so
    > much one input device per remote, its one input device per config file,
    > so one could set up two separate config files that are actually commands
    > from the same remote, but have them listened to by different apps, or
    > one could group together commands from multiple remotes in a single
    > config so that multiple remotes would all control the same thing. Sounds
    > good to me, provides for a reasonable amount of flexibility.


    That is how it works.

    > Of course, this suggests a receiver wouldn't be able to do anything
    > until the user provided a key mapping table of some sort. Would it make
    > sense to provide a default IR-to-key mapping for each receiver type? A
    > bit difficult to provide a sane default for something like a home-brew
    > serial IR receiver, but you could certainly provide sane defaults for
    > things like mceusb and imon (i.e., mappings for the remotes typically
    > bundled with those receivers).


    Receivers can process the raw tuples using the catch-all device.

    You could write a little app like udev that listens to the catch-all
    device and then search the config files for untranslated tuples. When
    you match on an untranslated tuple, load that config file into the
    kernel.

    I'm taking the kids on their first trip to Disneyworld next week so I
    won't be able to work on this for a while.

    >
    >> Most remotes send out unique codes, they have to or they would turn on
    >> unintended devices.

    >
    > Yep, that's actually where the root of my concern was. I have at least a
    > half dozen different remote-controlled devices in my AV cabinet near my
    > myth box, and I obviously don't want anything but the codes I've set up
    > for my myth box acted upon on the myth box.
    >
    >
    > --
    > Jarod Wilson
    > jarod@wilsonet.com
    >
    >




    --
    Jon Smirl
    jonsmirl@gmail.com
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  10. Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem

    On Fri, 2008-10-10 at 10:08 -0400, Jon Smirl wrote:
    > On Fri, Oct 10, 2008 at 9:42 AM, Jarod Wilson wrote:
    > > On Fri, 2008-10-10 at 01:04 -0400, Jon Smirl wrote:
    > >> On Fri, Oct 10, 2008 at 12:11 AM, Jarod Wilson wrote:
    > >> > On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:

    > > [...]
    > >> >> > How should multiple remotes be handled? Split them out into
    > >> >> > individual input devices, or group them onto a single IR device? I
    > >> >> > can implement either.
    > >> >>
    > >> >> Individual input devices, I'd say... so that app can only listen for
    > >> >> 'its' remote.
    > >> >
    > >> > I don't quite get it. How can we tell there are multiple remotes to set
    > >> > up multiple input devices when the system comes up? All we can know
    > >> > about at driver init time is the receiver(s), no? Or would this be keyed
    > >> > off a config file? Or ______ ?
    > >>
    > >>
    > >> We could create a sysfs attribut named ir_config. For each config file
    > >> you copy to it it creates a new input device. The config files have
    > >> lists of map this protocol, device, command tuple to this key. When a
    > >> remote button is pressed the raw codes are fed to the in-kernel
    > >> protocol engine. That engine turns the raw codes into tuples. Tuples
    > >> are matched against the configs that have been loaded until a hit is
    > >> found. If no hit they get sent out the catch-all device.

    > >
    > > Okay. I presume the catch-all device would simply note that it saw
    > > something, and not actually take any action beyond that.

    >
    > The catch-all device is an device in /dev/input. It gets the input
    > tuples in protocol/device/command form sent to it. I should probably
    > send all IR input to this device, and flag the events that were
    > translated and sent to another input device.


    Sounds sane.

    > > Also, a minor clarification: If its config file based, then its not so
    > > much one input device per remote, its one input device per config file,
    > > so one could set up two separate config files that are actually commands
    > > from the same remote, but have them listened to by different apps, or
    > > one could group together commands from multiple remotes in a single
    > > config so that multiple remotes would all control the same thing. Sounds
    > > good to me, provides for a reasonable amount of flexibility.

    >
    > That is how it works.


    Cool. Still getting this all straight in my head...

    > > Of course, this suggests a receiver wouldn't be able to do anything
    > > until the user provided a key mapping table of some sort. Would it make
    > > sense to provide a default IR-to-key mapping for each receiver type? A
    > > bit difficult to provide a sane default for something like a home-brew
    > > serial IR receiver, but you could certainly provide sane defaults for
    > > things like mceusb and imon (i.e., mappings for the remotes typically
    > > bundled with those receivers).

    >
    > Receivers can process the raw tuples using the catch-all device.
    >
    > You could write a little app like udev that listens to the catch-all
    > device and then search the config files for untranslated tuples. When
    > you match on an untranslated tuple, load that config file into the
    > kernel.


    That's just the sort of thing I *don't* want though -- I can imagine my
    receiver picking up all the signals intended for my other AV devices and
    loading up a bunch of configs, thinking it should do something with
    these signals. Seems you'd want a training/pairing mode you could put
    the driver in to set this stuff up.

    > I'm taking the kids on their first trip to Disneyworld next week so I
    > won't be able to work on this for a while.


    Have fun! In the mean time, I've got a bit of porting to try out still,
    as well as some remaining review clean-ups...



    --
    Jarod Wilson
    jarod@wilsonet.com

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

+ Reply to Thread