V4L/DVB: Teach drivers/media/IR/ir-raw-event.c to use durations
[cascardo/linux.git] / drivers / media / IR / ir-nec-decoder.c
index 16360eb..02682e6 100644 (file)
@@ -1,4 +1,4 @@
-/* ir-raw-event.c - handle IR Pulse/Space event
+/* ir-nec-decoder.c - handle NEC IR Pulse/Space protocol
  *
  * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
  *
  */
 
 #include <media/ir-core.h>
+#include <linux/bitrev.h>
 
-/* Start time: 4.5 ms  */
-#define MIN_START_TIME 3900000
-#define MAX_START_TIME 5100000
+#define NEC_NBITS              32
+#define NEC_UNIT               562500  /* ns */
+#define NEC_HEADER_PULSE       PULSE(16)
+#define NEC_HEADER_SPACE       SPACE(8)
+#define NEC_REPEAT_SPACE       SPACE(4)
+#define NEC_BIT_PULSE          PULSE(1)
+#define NEC_BIT_0_SPACE                SPACE(1)
+#define NEC_BIT_1_SPACE                SPACE(3)
 
-/* Pulse time: 560 us  */
-#define MIN_PULSE_TIME 460000
-#define MAX_PULSE_TIME 660000
+/* Used to register nec_decoder clients */
+static LIST_HEAD(decoder_list);
+static DEFINE_SPINLOCK(decoder_lock);
 
-/* Bit 1 space time: 2.25ms-560 us */
-#define MIN_BIT1_TIME  1490000
-#define MAX_BIT1_TIME  1890000
+enum nec_state {
+       STATE_INACTIVE,
+       STATE_HEADER_SPACE,
+       STATE_BIT_PULSE,
+       STATE_BIT_SPACE,
+       STATE_TRAILER_PULSE,
+       STATE_TRAILER_SPACE,
+};
 
-/* Bit 0 space time: 1.12ms-560 us */
-#define MIN_BIT0_TIME  360000
-#define MAX_BIT0_TIME  760000
+struct decoder_data {
+       struct list_head        list;
+       struct ir_input_dev     *ir_dev;
+       int                     enabled:1;
 
+       /* State machine control */
+       enum nec_state          state;
+       u32                     nec_bits;
+       unsigned                count;
+};
 
-/** Decode NEC pulsecode. This code can take up to 76.5 ms to run.
-       Unfortunately, using IRQ to decode pulse didn't work, since it uses
-       a pulse train of 38KHz. This means one pulse on each 52 us
-*/
 
-int ir_nec_decode(struct input_dev *input_dev,
-                 struct ir_raw_event *evs,
-                 int len)
+/**
+ * get_decoder_data()  - gets decoder data
+ * @input_dev: input device
+ *
+ * Returns the struct decoder_data that corresponds to a device
+ */
+static struct decoder_data *get_decoder_data(struct  ir_input_dev *ir_dev)
 {
-       int i, count = -1;
-       int ircode = 0, not_code = 0;
-#if 0
-       /* Needed only after porting the event code to the decoder */
-       struct ir_input_dev *ir = input_get_drvdata(input_dev);
-#endif
-
-       /* Be sure that the first event is an start one and is a pulse */
-       for (i = 0; i < len; i++) {
-               if (evs[i].type & (IR_START_EVENT | IR_PULSE))
+       struct decoder_data *data = NULL;
+
+       spin_lock(&decoder_lock);
+       list_for_each_entry(data, &decoder_list, list) {
+               if (data->ir_dev == ir_dev)
                        break;
        }
-       i++;    /* First event doesn't contain data */
+       spin_unlock(&decoder_lock);
+       return data;
+}
+
+static ssize_t store_enabled(struct device *d,
+                            struct device_attribute *mattr,
+                            const char *buf,
+                            size_t len)
+{
+       unsigned long value;
+       struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+       struct decoder_data *data = get_decoder_data(ir_dev);
+
+       if (!data)
+               return -EINVAL;
+
+       if (strict_strtoul(buf, 10, &value) || value > 1)
+               return -EINVAL;
+
+       data->enabled = value;
+
+       return len;
+}
+
+static ssize_t show_enabled(struct device *d,
+                            struct device_attribute *mattr, char *buf)
+{
+       struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+       struct decoder_data *data = get_decoder_data(ir_dev);
+
+       if (!data)
+               return -EINVAL;
+
+       if (data->enabled)
+               return sprintf(buf, "1\n");
+       else
+       return sprintf(buf, "0\n");
+}
+
+static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled);
+
+static struct attribute *decoder_attributes[] = {
+       &dev_attr_enabled.attr,
+       NULL
+};
 
-       if (i >= len)
+static struct attribute_group decoder_attribute_group = {
+       .name   = "nec_decoder",
+       .attrs  = decoder_attributes,
+};
+
+/**
+ * ir_nec_decode() - Decode one NEC pulse or space
+ * @input_dev: the struct input_dev descriptor of the device
+ * @duration:  duration in ns of pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_nec_decode(struct input_dev *input_dev, s64 duration)
+{
+       struct decoder_data *data;
+       struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+       int u;
+       u32 scancode;
+       u8 address, not_address, command, not_command;
+
+       data = get_decoder_data(ir_dev);
+       if (!data)
+               return -EINVAL;
+
+       if (!data->enabled)
+               return 0;
+
+       if (IS_RESET(duration)) {
+               data->state = STATE_INACTIVE;
+               return 0;
+       }
+
+       u = TO_UNITS(duration, NEC_UNIT);
+       if (DURATION(u) == 0)
+               goto out;
+
+       IR_dprintk(2, "NEC decode started at state %d (%i units, %ius)\n",
+                  data->state, u, TO_US(duration));
+
+       switch (data->state) {
+
+       case STATE_INACTIVE:
+               if (u == NEC_HEADER_PULSE) {
+                       data->count = 0;
+                       data->state = STATE_HEADER_SPACE;
+               }
                return 0;
 
-       /* First space should have 4.5 ms otherwise is not NEC protocol */
-       if ((evs[i].delta.tv_nsec < MIN_START_TIME) |
-           (evs[i].delta.tv_nsec > MAX_START_TIME) |
-           (evs[i].type != IR_SPACE))
-               goto err;
-
-       /*
-        * FIXME: need to implement the repeat sequence
-        */
-
-       count = 0;
-       for (i++; i < len; i++) {
-               int bit;
-
-               if ((evs[i].delta.tv_nsec < MIN_PULSE_TIME) |
-                   (evs[i].delta.tv_nsec > MAX_PULSE_TIME) |
-                   (evs[i].type != IR_PULSE))
-                       goto err;
-
-               if (++i >= len)
-                       goto err;
-               if (evs[i].type != IR_SPACE)
-                       goto err;
-
-               if ((evs[i].delta.tv_nsec > MIN_BIT1_TIME) &&
-                   (evs[i].delta.tv_nsec < MAX_BIT1_TIME))
-                       bit = 1;
-               else if ((evs[i].delta.tv_nsec > MIN_BIT0_TIME) &&
-                        (evs[i].delta.tv_nsec < MAX_BIT0_TIME))
-                       bit = 0;
-               else
-                       goto err;
-
-               if (bit) {
-                       int shift = count;
-                       /* Address first, then command */
-                       if (shift < 8) {
-                               shift += 8;
-                               ircode |= 1 << shift;
-                       } else if (shift < 16) {
-                               not_code |= 1 << shift;
-                       } else if (shift < 24) {
-                               shift -= 16;
-                               ircode |= 1 << shift;
-                       } else {
-                               shift -= 24;
-                               not_code |= 1 << shift;
-                       }
+       case STATE_HEADER_SPACE:
+               if (u == NEC_HEADER_SPACE) {
+                       data->state = STATE_BIT_PULSE;
+                       return 0;
+               } else if (u == NEC_REPEAT_SPACE) {
+                       ir_repeat(input_dev);
+                       IR_dprintk(1, "Repeat last key\n");
+                       data->state = STATE_TRAILER_PULSE;
+                       return 0;
+               }
+               break;
+
+       case STATE_BIT_PULSE:
+               if (u == NEC_BIT_PULSE) {
+                       data->state = STATE_BIT_SPACE;
+                       return 0;
+               }
+               break;
+
+       case STATE_BIT_SPACE:
+               if (u != NEC_BIT_0_SPACE && u != NEC_BIT_1_SPACE)
+                       break;
+
+               data->nec_bits <<= 1;
+               if (u == NEC_BIT_1_SPACE)
+                       data->nec_bits |= 1;
+               data->count++;
+
+               if (data->count != NEC_NBITS) {
+                       data->state = STATE_BIT_PULSE;
+                       return 0;
                }
-               if (++count == 32)
+
+               address     = bitrev8((data->nec_bits >> 24) & 0xff);
+               not_address = bitrev8((data->nec_bits >> 16) & 0xff);
+               command     = bitrev8((data->nec_bits >>  8) & 0xff);
+               not_command = bitrev8((data->nec_bits >>  0) & 0xff);
+
+               if ((command ^ not_command) != 0xff) {
+                       IR_dprintk(1, "NEC checksum error: received 0x%08x\n",
+                                  data->nec_bits);
                        break;
+               }
+
+               if ((address ^ not_address) != 0xff) {
+                       /* Extended NEC */
+                       scancode = address     << 16 |
+                                  not_address <<  8 |
+                                  command;
+                       IR_dprintk(1, "NEC (Ext) scancode 0x%06x\n", scancode);
+               } else {
+                       /* normal NEC */
+                       scancode = address << 8 | command;
+                       IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
+               }
+
+               ir_keydown(input_dev, scancode, 0);
+               data->state = STATE_TRAILER_PULSE;
+               return 0;
+
+       case STATE_TRAILER_PULSE:
+               if (u > 0) {
+                       data->state = STATE_TRAILER_SPACE;
+                       return 0;
+               }
+               break;
+
+       case STATE_TRAILER_SPACE:
+               if (u < 0) {
+                       data->state = STATE_INACTIVE;
+                       return 0;
+               }
+
+               break;
        }
 
-       /*
-        * Fixme: may need to accept Extended NEC protocol?
-        */
-       if ((ircode & ~not_code) != ircode) {
-               IR_dprintk(1, "NEC checksum error: code 0x%04x, not-code 0x%04x\n",
-                          ircode, not_code);
-               return -EINVAL;
+out:
+       IR_dprintk(1, "NEC decode failed at state %d (%i units, %ius)\n",
+                  data->state, u, TO_US(duration));
+       data->state = STATE_INACTIVE;
+       return -EINVAL;
+}
+
+static int ir_nec_register(struct input_dev *input_dev)
+{
+       struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+       struct decoder_data *data;
+       int rc;
+
+       rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+       if (rc < 0)
+               return rc;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+               return -ENOMEM;
        }
 
-       IR_dprintk(1, "NEC scancode 0x%04x\n", ircode);
+       data->ir_dev = ir_dev;
+       data->enabled = 1;
 
-       return ircode;
-err:
-       IR_dprintk(1, "NEC decoded failed at bit %d while decoding %luus time\n",
-                  count, (evs[i].delta.tv_nsec + 500) / 1000);
+       spin_lock(&decoder_lock);
+       list_add_tail(&data->list, &decoder_list);
+       spin_unlock(&decoder_lock);
 
-       return -EINVAL;
+       return 0;
+}
+
+static int ir_nec_unregister(struct input_dev *input_dev)
+{
+       struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+       static struct decoder_data *data;
+
+       data = get_decoder_data(ir_dev);
+       if (!data)
+               return 0;
+
+       sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+
+       spin_lock(&decoder_lock);
+       list_del(&data->list);
+       spin_unlock(&decoder_lock);
+
+       return 0;
 }
-EXPORT_SYMBOL_GPL(ir_nec_decode);
+
+static struct ir_raw_handler nec_handler = {
+       .decode         = ir_nec_decode,
+       .raw_register   = ir_nec_register,
+       .raw_unregister = ir_nec_unregister,
+};
+
+static int __init ir_nec_decode_init(void)
+{
+       ir_raw_handler_register(&nec_handler);
+
+       printk(KERN_INFO "IR NEC protocol handler initialized\n");
+       return 0;
+}
+
+static void __exit ir_nec_decode_exit(void)
+{
+       ir_raw_handler_unregister(&nec_handler);
+}
+
+module_init(ir_nec_decode_init);
+module_exit(ir_nec_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("NEC IR protocol decoder");