diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-03-20 20:59:44 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-05-18 00:52:56 -0300 |
commit | a3572c34da8dacc78a629211a91cf34e9b408701 (patch) | |
tree | 281efd4d69b68bd4720668fd91cfcf16d1ed3089 | |
parent | 0210894956cf57d525d56341cc3e0f3d5d2db659 (diff) |
V4L/DVB: ir-core: Add logic to decode IR protocols at the IR core
Adds a method to pass IR raw pulse/code events into ir-core. This is
needed in order to support LIRC. It also helps to move common code
from the drivers into the core.
In order to allow testing, it implements a simple NEC protocol decoder
at ir-nec-decoder.c file. The logic is about the same used at saa7134
driver that handles Avermedia M135A and Encore FM53 boards.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/IR/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/IR/ir-nec-decoder.c | 131 | ||||
-rw-r--r-- | drivers/media/IR/ir-raw-event.c | 117 | ||||
-rw-r--r-- | drivers/media/video/saa7134/saa7134-input.c | 85 | ||||
-rw-r--r-- | include/media/ir-common.h | 3 | ||||
-rw-r--r-- | include/media/ir-core.h | 32 |
6 files changed, 365 insertions, 5 deletions
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile index 171890e7a41..18794c7b0ea 100644 --- a/drivers/media/IR/Makefile +++ b/drivers/media/IR/Makefile @@ -1,5 +1,5 @@ ir-common-objs := ir-functions.o ir-keymaps.o -ir-core-objs := ir-keytable.o ir-sysfs.o +ir-core-objs := ir-keytable.o ir-sysfs.o ir-raw-event.o ir-nec-decoder.o obj-$(CONFIG_IR_CORE) += ir-core.o obj-$(CONFIG_VIDEO_IR) += ir-common.o diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c new file mode 100644 index 00000000000..16360eb4055 --- /dev/null +++ b/drivers/media/IR/ir-nec-decoder.c @@ -0,0 +1,131 @@ +/* ir-raw-event.c - handle IR Pulse/Space event + * + * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <media/ir-core.h> + +/* Start time: 4.5 ms */ +#define MIN_START_TIME 3900000 +#define MAX_START_TIME 5100000 + +/* Pulse time: 560 us */ +#define MIN_PULSE_TIME 460000 +#define MAX_PULSE_TIME 660000 + +/* Bit 1 space time: 2.25ms-560 us */ +#define MIN_BIT1_TIME 1490000 +#define MAX_BIT1_TIME 1890000 + +/* Bit 0 space time: 1.12ms-560 us */ +#define MIN_BIT0_TIME 360000 +#define MAX_BIT0_TIME 760000 + + +/** 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) +{ + 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)) + break; + } + i++; /* First event doesn't contain data */ + + if (i >= len) + 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; + } + } + if (++count == 32) + 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; + } + + IR_dprintk(1, "NEC scancode 0x%04x\n", ircode); + + 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); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ir_nec_decode); diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c new file mode 100644 index 00000000000..9c71ac85892 --- /dev/null +++ b/drivers/media/IR/ir-raw-event.c @@ -0,0 +1,117 @@ +/* ir-raw-event.c - handle IR Pulse/Space event + * + * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <media/ir-core.h> + +/* Define the max number of bit transitions per IR keycode */ +#define MAX_IR_EVENT_SIZE 256 + +int ir_raw_event_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + int rc, size; + + ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL); + + size = sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE * 2; + size = roundup_pow_of_two(size); + + rc = kfifo_alloc(&ir->raw->kfifo, size, GFP_KERNEL); + + return rc; +} +EXPORT_SYMBOL_GPL(ir_raw_event_register); + +void ir_raw_event_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + + if (!ir->raw) + return; + + kfifo_free(&ir->raw->kfifo); + kfree(ir->raw); + ir->raw = NULL; +} +EXPORT_SYMBOL_GPL(ir_raw_event_unregister); + +int ir_raw_event_store(struct input_dev *input_dev, enum raw_event_type type) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + struct timespec ts; + struct ir_raw_event event; + int rc; + + if (!ir->raw) + return -EINVAL; + + event.type = type; + event.delta.tv_sec = 0; + event.delta.tv_nsec = 0; + + ktime_get_ts(&ts); + + if (timespec_equal(&ir->raw->last_event, &event.delta)) + event.type |= IR_START_EVENT; + else + event.delta = timespec_sub(ts, ir->raw->last_event); + + memcpy(&ir->raw->last_event, &ts, sizeof(ts)); + + if (event.delta.tv_sec) { + event.type |= IR_START_EVENT; + event.delta.tv_sec = 0; + event.delta.tv_nsec = 0; + } + + kfifo_in(&ir->raw->kfifo, &event, sizeof(event)); + + return rc; +} +EXPORT_SYMBOL_GPL(ir_raw_event_store); + +int ir_raw_event_handle(struct input_dev *input_dev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + int rc; + struct ir_raw_event *evs; + int len, i; + + /* + * Store the events into a temporary buffer. This allows calling more than + * one decoder to deal with the received data + */ + len = kfifo_len(&ir->raw->kfifo) / sizeof(*evs); + if (!len) + return 0; + evs = kmalloc(len * sizeof(*evs), GFP_ATOMIC); + + for (i = 0; i < len; i++) { + rc = kfifo_out(&ir->raw->kfifo, &evs[i], sizeof(*evs)); + if (rc != sizeof(*evs)) { + IR_dprintk(1, "overflow error: received %d instead of %zd\n", + rc, sizeof(*evs)); + return -EINVAL; + } + IR_dprintk(2, "event type %d, time before event: %07luus\n", + evs[i].type, (evs[i].delta.tv_nsec + 500) / 1000); + } + + rc = ir_nec_decode(input_dev, evs, len); + + kfree(evs); + + return rc; +} +EXPORT_SYMBOL_GPL(ir_raw_event_handle); diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 7e6f125e4e3..1a7cf9c8c68 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -68,6 +68,7 @@ MODULE_PARM_DESC(disable_other_ir, "disable full codes of " /* Helper functions for RC5 and NEC decoding at GPIO16 or GPIO18 */ static int saa7134_rc5_irq(struct saa7134_dev *dev); static int saa7134_nec_irq(struct saa7134_dev *dev); +static int saa7134_raw_decode_irq(struct saa7134_dev *dev); static void nec_task(unsigned long data); static void saa7134_nec_timer(unsigned long data); @@ -403,10 +404,12 @@ void saa7134_input_irq(struct saa7134_dev *dev) if (ir->nec_gpio) { saa7134_nec_irq(dev); - } else if (!ir->polling && !ir->rc5_gpio) { + } else if (!ir->polling && !ir->rc5_gpio && !ir->raw_decode) { build_key(dev); } else if (ir->rc5_gpio) { saa7134_rc5_irq(dev); + } else if (ir->raw_decode) { + saa7134_raw_decode_irq(dev); } } @@ -419,6 +422,23 @@ static void saa7134_input_timer(unsigned long data) mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); } +void ir_raw_decode_timer_end(unsigned long data) +{ + struct saa7134_dev *dev = (struct saa7134_dev *)data; + struct card_ir *ir = dev->remote; + int rc; + + /* + * FIXME: the IR key handling code should be called by the decoder, + * after implementing the repeat mode + */ + rc = ir_raw_event_handle(dev->remote->dev); + if (rc >= 0) { + ir_input_keydown(ir->dev, &ir->ir, rc); + ir_input_nokey(ir->dev, &ir->ir); + } +} + void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir) { if (ir->running) @@ -447,6 +467,11 @@ void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir) setup_timer(&ir->timer_keyup, saa7134_nec_timer, (unsigned long)dev); tasklet_init(&ir->tlet, nec_task, (unsigned long)dev); + } else if (ir->raw_decode) { + /* set timer_end for code completion */ + init_timer(&ir->timer_end); + ir->timer_end.function = ir_raw_decode_timer_end; + ir->timer_end.data = (unsigned long)dev; } } @@ -462,6 +487,9 @@ void saa7134_ir_stop(struct saa7134_dev *dev) del_timer_sync(&ir->timer_end); else if (ir->nec_gpio) tasklet_kill(&ir->tlet); + else if (ir->raw_decode) + del_timer_sync(&ir->timer_end); + ir->running = 0; } @@ -509,6 +537,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) int polling = 0; int rc5_gpio = 0; int nec_gpio = 0; + int raw_decode = 0; u64 ir_type = IR_TYPE_OTHER; int err; @@ -574,7 +603,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) ir_codes = &ir_codes_avermedia_m135a_rm_jx_table; mask_keydown = 0x0040000; mask_keycode = 0xffff; - nec_gpio = 1; + raw_decode = 1; break; case SAA7134_BOARD_AVERMEDIA_777: case SAA7134_BOARD_AVERMEDIA_A16AR: @@ -755,6 +784,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) ir->polling = polling; ir->rc5_gpio = rc5_gpio; ir->nec_gpio = nec_gpio; + ir->raw_decode = raw_decode; /* init input device */ snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)", @@ -762,7 +792,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); - if (ir_codes->ir_type != IR_TYPE_OTHER) { + if (ir_codes->ir_type != IR_TYPE_OTHER && !raw_decode) { ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; ir->props.priv = dev; ir->props.change_protocol = saa7134_ir_change_protocol; @@ -790,6 +820,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) err = ir_input_register(ir->dev, ir_codes, &ir->props, MODULE_NAME); if (err) goto err_out_stop; + if (ir_codes->ir_type != IR_TYPE_OTHER) { + err = ir_raw_event_register(ir->dev); + if (err) + goto err_out_stop; + } saa7134_ir_start(dev, ir); @@ -813,6 +848,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) return; saa7134_ir_stop(dev); + ir_raw_event_unregister(dev->remote->dev); ir_input_unregister(dev->remote->dev); kfree(dev->remote); dev->remote = NULL; @@ -919,6 +955,48 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) i2c_new_device(&dev->i2c_adap, &info); } +static int saa7134_raw_decode_irq(struct saa7134_dev *dev) +{ + struct card_ir *ir = dev->remote; + unsigned long timeout; + int count, pulse, oldpulse; + + /* Disable IR IRQ line */ + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); + + /* Generate initial event */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + pulse = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown; + ir_raw_event_store(dev->remote->dev, pulse? IR_PULSE : IR_SPACE); + +#if 1 + /* Wait up to 10 ms for event change */ + oldpulse = pulse; + for (count = 0; count < 1000; count++) { + udelay(10); + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + pulse = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) + & ir->mask_keydown; + if (pulse != oldpulse) + break; + } + + /* Store final event */ + ir_raw_event_store(dev->remote->dev, pulse? IR_PULSE : IR_SPACE); +#endif + /* Wait 15 ms before deciding to do something else */ + timeout = jiffies + jiffies_to_msecs(15); + mod_timer(&ir->timer_end, timeout); + + /* Enable IR IRQ line */ + saa_setl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); + + return 1; +} + static int saa7134_rc5_irq(struct saa7134_dev *dev) { struct card_ir *ir = dev->remote; @@ -961,7 +1039,6 @@ static int saa7134_rc5_irq(struct saa7134_dev *dev) return 1; } - /* On NEC protocol, One has 2.25 ms, and zero has 1.125 ms The first pulse (start) has 9 + 4.5 ms */ diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 41469b79ad1..87f2ec78deb 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -82,6 +82,9 @@ struct card_ir { /* NEC decoding */ u32 nec_gpio; struct tasklet_struct tlet; + + /* IR core raw decoding */ + u32 raw_decode; }; /* Routines from ir-functions.c */ diff --git a/include/media/ir-core.h b/include/media/ir-core.h index 1eae72d518e..369969d9077 100644 --- a/include/media/ir-core.h +++ b/include/media/ir-core.h @@ -16,6 +16,8 @@ #include <linux/input.h> #include <linux/spinlock.h> +#include <linux/kfifo.h> +#include <linux/time.h> extern int ir_core_debug; #define IR_dprintk(level, fmt, arg...) if (ir_core_debug >= level) \ @@ -27,6 +29,13 @@ extern int ir_core_debug; #define IR_TYPE_NEC (1 << 2) #define IR_TYPE_OTHER (((u64)1) << 63l) +enum raw_event_type { + IR_SPACE = (1 << 0), + IR_PULSE = (1 << 1), + IR_START_EVENT = (1 << 2), + IR_STOP_EVENT = (1 << 3), +}; + struct ir_scancode { u16 scancode; u32 keycode; @@ -46,6 +55,15 @@ struct ir_dev_props { int (*change_protocol)(void *priv, u64 ir_type); }; +struct ir_raw_event { + struct timespec delta; /* Time spent before event */ + enum raw_event_type type; /* event type */ +}; + +struct ir_raw_event_ctrl { + struct kfifo kfifo; /* fifo for the pulse/space events */ + struct timespec last_event; /* when last event occurred */ +}; struct ir_input_dev { struct device dev; /* device */ @@ -53,7 +71,9 @@ struct ir_input_dev { struct ir_scancode_table rc_tab; /* scan/key table */ unsigned long devno; /* device number */ const struct ir_dev_props *props; /* Device properties */ + struct ir_raw_event_ctrl *raw; /* for raw pulse/space events */ }; + #define to_ir_input_dev(_attr) container_of(_attr, struct ir_input_dev, attr) /* Routines from ir-keytable.c */ @@ -72,4 +92,16 @@ void ir_input_unregister(struct input_dev *input_dev); int ir_register_class(struct input_dev *input_dev); void ir_unregister_class(struct input_dev *input_dev); +/* Routines from ir-raw-event.c */ +int ir_raw_event_register(struct input_dev *input_dev); +void ir_raw_event_unregister(struct input_dev *input_dev); +int ir_raw_event_store(struct input_dev *input_dev, enum raw_event_type type); +int ir_raw_event_handle(struct input_dev *input_dev); + +/* from ir-nec-decoder.c */ +int ir_nec_decode(struct input_dev *input_dev, + struct ir_raw_event *evs, + int len); + + #endif |