From: Mauro Carvalho Chehab <mchehab@redhat.com>
To: linux-media@vger.kernel.org,
Linux Media Mailing List <linux-media@vger.kernel.org>
Subject: [PATCH 18/26] V4L/DVB: ir-nec-decoder: Reimplement the entire decoder
Date: Tue, 6 Apr 2010 15:18:01 -0300 [thread overview]
Message-ID: <20100406151801.552299b9@pedra> (raw)
In-Reply-To: <cover.1270577768.git.mchehab@redhat.com>
Thanks to Andy Walls <awalls@md.metrocast.net> for pointing me his
code, that gave me some ideas to better implement it.
After some work with saa7134 bits, I found a way to catch both IRQ
edge pulses. By enabling it, the NEC decoder can now take both
pulse and spaces into account, making it more precise.
Instead of the old strategy of handling the events all at once,
this code implements a state machine. Due to that, it handles
individual pulse or space events, validating them against the
protocol, producing a much more reliable decoding.
With the new implementation, the protocol trailer bits are properly
handled, making possible for the repeat key to work.
Also, the code is now capable of handling both NEC and NEC extended
IR devices. With NEC, it produces a 16 bits code, while with NEC
extended, a 24 bits code is returned.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c
index 0b50060..33b260f 100644
--- a/drivers/media/IR/ir-nec-decoder.c
+++ b/drivers/media/IR/ir-nec-decoder.c
@@ -14,6 +14,14 @@
#include <media/ir-core.h>
+#define NEC_UNIT 559979 /* ns */
+#define NEC_HEADER_MARK (16 * NEC_UNIT)
+#define NEC_HEADER_SPACE (8 * NEC_UNIT)
+#define NEC_REPEAT_SPACE (4 * NEC_UNIT)
+#define NEC_MARK (NEC_UNIT)
+#define NEC_0_SYMBOL (NEC_UNIT)
+#define NEC_1_SYMBOL (3 * NEC_UNIT)
+
/* Start time: 4.5 ms + 560 us of the next pulse */
#define MIN_START_TIME (3900000 + 560000)
#define MAX_START_TIME (5100000 + 560000)
@@ -43,10 +51,32 @@
static LIST_HEAD(decoder_list);
static spinlock_t decoder_lock;
+enum nec_state {
+ STATE_INACTIVE,
+ STATE_HEADER_MARK,
+ STATE_HEADER_SPACE,
+ STATE_MARK,
+ STATE_SPACE,
+ STATE_TRAILER_MARK,
+ STATE_TRAILER_SPACE,
+};
+
+struct nec_code {
+ u8 address;
+ u8 not_address;
+ u8 command;
+ u8 not_command;
+};
+
struct decoder_data {
struct list_head list;
struct ir_input_dev *ir_dev;
int enabled:1;
+
+ /* State machine control */
+ enum nec_state state;
+ struct nec_code nec_code;
+ unsigned count;
};
@@ -118,139 +148,173 @@ static struct attribute_group decoder_attribute_group = {
};
-/** is_repeat - Check if it is a NEC repeat event
- * @input_dev: the struct input_dev descriptor of the device
- * @pos: the position of the first event
- * @len: the length of the buffer
- */
-static int is_repeat(struct ir_raw_event *evs, int len, int pos)
-{
- if ((evs[pos].delta.tv_nsec < MIN_REPEAT_START_TIME) ||
- (evs[pos].delta.tv_nsec > MAX_REPEAT_START_TIME))
- return 0;
-
- if (++pos >= len)
- return 0;
-
- if ((evs[pos].delta.tv_nsec < MIN_REPEAT_TIME) ||
- (evs[pos].delta.tv_nsec > MAX_REPEAT_TIME))
- return 0;
-
- return 1;
-}
-
/**
- * __ir_nec_decode() - Decode one NEC pulsecode
+ * handle_event() - Decode one NEC pulse or space
* @input_dev: the struct input_dev descriptor of the device
- * @evs: event array with type/duration of pulse/space
- * @len: length of the array
- * @pos: position to start seeking for a code
- * This function returns -EINVAL if no pulse got decoded,
- * 0 if buffer is empty and 1 if one keycode were handled.
+ * @ev: event array with type/duration of pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
*/
-static int __ir_nec_decode(struct input_dev *input_dev,
- struct ir_raw_event *evs,
- int len, int *pos)
+static int handle_event(struct input_dev *input_dev,
+ struct ir_raw_event *ev)
{
- struct ir_input_dev *ir = input_get_drvdata(input_dev);
- int count = -1;
- int ircode = 0, not_code = 0;
-
- /* Be sure that the first event is an start one and is a pulse */
- for (; *pos < len; (*pos)++) {
- /* Very long delays are considered as start events */
- if (evs[*pos].delta.tv_nsec > MAX_NEC_TIME)
- break;
- if (evs[*pos].type & IR_START_EVENT)
- break;
- IR_dprintk(1, "%luus: Spurious NEC %s\n",
- (evs[*pos].delta.tv_nsec + 500) / 1000,
- (evs[*pos].type & IR_SPACE) ? "space" : "pulse");
-
- }
- if (*pos >= len)
- return 0;
+ struct decoder_data *data;
+ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+ int bit, last_bit;
+
+ data = get_decoder_data(ir_dev);
+ if (!data)
+ return -EINVAL;
+
+ /* Except for the initial event, what matters is the previous bit */
+ bit = (ev->type & IR_PULSE) ? 1 : 0;
+
+ last_bit = !bit;
+
+ /* Discards spurious space last_bits when inactive */
- (*pos)++; /* First event doesn't contain data */
+ /* Very long delays are considered as start events */
+ if (ev->delta.tv_nsec > NEC_HEADER_MARK + NEC_HEADER_SPACE - NEC_UNIT / 2)
+ data->state = STATE_INACTIVE;
- if (evs[*pos].type != IR_PULSE)
- goto err;
+ if (ev->type & IR_START_EVENT)
+ data->state = STATE_INACTIVE;
+
+ switch (data->state) {
+ case STATE_INACTIVE:
+ if (!bit) /* PULSE marks the start event */
+ return 0;
+
+ data->count = 0;
+ data->state = STATE_HEADER_MARK;
+ memset (&data->nec_code, 0, sizeof(data->nec_code));
+ return 0;
+ case STATE_HEADER_MARK:
+ if (!last_bit)
+ goto err;
+ if (ev->delta.tv_nsec < NEC_HEADER_MARK - 6 * NEC_UNIT)
+ goto err;
+ data->state = STATE_HEADER_SPACE;
+ return 0;
+ case STATE_HEADER_SPACE:
+ if (last_bit)
+ goto err;
+ if (ev->delta.tv_nsec >= NEC_HEADER_SPACE - NEC_UNIT / 2) {
+ data->state = STATE_MARK;
+ return 0;
+ }
- /* Check if it is a NEC repeat event */
- if (is_repeat(evs, len, *pos)) {
- *pos += 2;
- if (ir->keypressed) {
+ if (ev->delta.tv_nsec >= NEC_REPEAT_SPACE - NEC_UNIT / 2) {
ir_repeat(input_dev);
- IR_dprintk(1, "NEC repeat event\n");
- return 1;
- } else {
- IR_dprintk(1, "missing NEC repeat event\n");
+ IR_dprintk(1, "Repeat last key\n");
+ data->state = STATE_TRAILER_MARK;
return 0;
}
- }
-
- /* First space should have 4.5 ms otherwise is not NEC protocol */
- if ((evs[*pos].delta.tv_nsec < MIN_START_TIME) ||
- (evs[*pos].delta.tv_nsec > MAX_START_TIME))
goto err;
+ case STATE_MARK:
+ if (!last_bit)
+ goto err;
+ if ((ev->delta.tv_nsec > NEC_MARK + NEC_UNIT / 2) ||
+ (ev->delta.tv_nsec < NEC_MARK - NEC_UNIT / 2))
+ goto err;
+ data->state = STATE_SPACE;
+ return 0;
+ case STATE_SPACE:
+ if (last_bit)
+ goto err;
- count = 0;
- for ((*pos)++; *pos < len; (*pos)++) {
- int bit;
- if ((evs[*pos].delta.tv_nsec > MIN_BIT1_TIME) &&
- (evs[*pos].delta.tv_nsec < MAX_BIT1_TIME))
- bit = 1;
- else if ((evs[*pos].delta.tv_nsec > MIN_BIT0_TIME) &&
- (evs[*pos].delta.tv_nsec < MAX_BIT0_TIME))
+ if ((ev->delta.tv_nsec >= NEC_0_SYMBOL - NEC_UNIT / 2) &&
+ (ev->delta.tv_nsec < NEC_0_SYMBOL + NEC_UNIT / 2))
bit = 0;
- else
- goto err;
+ else if ((ev->delta.tv_nsec >= NEC_1_SYMBOL - NEC_UNIT / 2) &&
+ (ev->delta.tv_nsec < NEC_1_SYMBOL + NEC_UNIT / 2))
+ bit = 1;
+ else {
+ IR_dprintk(1, "Decode failed at %d-th bit (%s) @%luus\n",
+ data->count,
+ last_bit ? "pulse" : "space",
+ (ev->delta.tv_nsec + 500) / 1000);
+ goto err2;
+ }
+
+ /* Ok, we've got a valid bit. proccess it */
if (bit) {
- int shift = count;
- /* Address first, then command */
+ int shift = data->count;
+
+ /*
+ * NEC transmit bytes on this temporal order:
+ * address | not address | command | not command
+ */
if (shift < 8) {
- shift += 8;
- ircode |= 1 << shift;
+ data->nec_code.address |= 1 << shift;
} else if (shift < 16) {
- not_code |= 1 << shift;
+ data->nec_code.not_address |= 1 << (shift - 8);
} else if (shift < 24) {
- shift -= 16;
- ircode |= 1 << shift;
+ data->nec_code.command |= 1 << (shift - 16);
} else {
- shift -= 24;
- not_code |= 1 << shift;
+ data->nec_code.not_command |= 1 << (shift - 24);
}
}
- if (++count == 32)
- break;
- }
- (*pos)++;
+ if (++data->count == 32) {
+ u32 scancode;
+ /*
+ * Fixme: may need to accept Extended NEC protocol?
+ */
+ if ((data->nec_code.command ^ data->nec_code.not_command) != 0xff)
+ goto checksum_err;
- /*
- * 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;
- }
+ if ((data->nec_code.address ^ data->nec_code.not_address) != 0xff) {
+ /* Extended NEC */
+ scancode = data->nec_code.address << 16 |
+ data->nec_code.not_address << 8 |
+ data->nec_code.command;
+ IR_dprintk(1, "NEC scancode 0x%06x\n", scancode);
+ } else {
+ /* normal NEC */
+ scancode = data->nec_code.address << 8 |
+ data->nec_code.command;
+ IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
+ }
+ ir_keydown(input_dev, scancode, 0);
- IR_dprintk(1, "NEC scancode 0x%04x\n", ircode);
- ir_keydown(input_dev, ircode, 0);
+ data->state = STATE_TRAILER_MARK;
+ } else
+ data->state = STATE_MARK;
+ return 0;
+ case STATE_TRAILER_MARK:
+ if (!last_bit)
+ goto err;
+ data->state = STATE_TRAILER_SPACE;
+ return 0;
+ case STATE_TRAILER_SPACE:
+ if (last_bit)
+ goto err;
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
- return 1;
err:
- IR_dprintk(1, "NEC decoded failed at bit %d (%s) while decoding %luus time\n",
- count,
- (evs[*pos].type & IR_SPACE) ? "space" : "pulse",
- (evs[*pos].delta.tv_nsec + 500) / 1000);
+ IR_dprintk(1, "NEC decoded failed at state %d (%s) @ %luus\n",
+ data->state,
+ bit ? "pulse" : "space",
+ (ev->delta.tv_nsec + 500) / 1000);
+err2:
+ data->state = STATE_INACTIVE;
+ return -EINVAL;
+checksum_err:
+ data->state = STATE_INACTIVE;
+ IR_dprintk(1, "NEC checksum error: received 0x%02x%02x%02x%02x\n",
+ data->nec_code.address,
+ data->nec_code.not_address,
+ data->nec_code.command,
+ data->nec_code.not_command);
return -EINVAL;
}
/**
- * __ir_nec_decode() - Decodes all NEC pulsecodes on a given array
+ * ir_nec_decode() - Decodes all NEC pulsecodes on a given array
* @input_dev: the struct input_dev descriptor of the device
* @evs: event array with type/duration of pulse/space
* @len: length of the array
@@ -269,10 +333,9 @@ static int ir_nec_decode(struct input_dev *input_dev,
if (!data || !data->enabled)
return 0;
- while (pos < len) {
- if (__ir_nec_decode(input_dev, evs, len, &pos) > 0)
- rc++;
- }
+ for (pos = 0; pos < len; pos++)
+ handle_event(input_dev, &evs[pos]);
+
return rc;
}
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index fd3225c..5dac6b7 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -658,7 +658,8 @@ int saa7134_input_init1(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_AVERMEDIA_M135A:
ir_codes = RC_MAP_AVERMEDIA_M135A_RM_JX;
- mask_keydown = 0x0040000;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
mask_keycode = 0xffff;
raw_decode = 1;
break;
@@ -1014,13 +1015,13 @@ static int saa7134_raw_decode_irq(struct saa7134_dev *dev)
{
struct card_ir *ir = dev->remote;
unsigned long timeout;
- int pulse;
+ int space;
/* 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);
+ space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown;
+ ir_raw_event_store(dev->remote->dev, space ? IR_SPACE : IR_PULSE);
/*
--
1.6.6.1
next prev parent reply other threads:[~2010-04-06 18:18 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <cover.1270577768.git.mchehab@redhat.com>
2010-04-06 18:18 ` [PATCH 26/26] V4L/DVB: ir-rc5-decoder: fix state machine Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 24/26] V4L/DVB: ir-core: Add support for badly-implemented hardware decoders Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 25/26] V4L/DVB: re-add enable/disable check to the IR decoders Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 23/26] V4L/DVB: ir-core: move rc map code to rc-map.h Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 22/26] V4L-DVB: ir-core: remove the ancillary buffer Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 21/26] V4L/DVB: cx88: don't handle IR on Pixelview too fast Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 19/26] ir-nec-decoder: Cleanups Mauro Carvalho Chehab
2010-04-06 18:18 ` Mauro Carvalho Chehab [this message]
2010-04-06 18:18 ` [PATCH 20/26] V4L-DVB: ir-rc5-decoder: Add a decoder for RC-5 IR protocol Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 13/26] V4L/DVB: saa7134: Add support for both positive and negative edge IRQ Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 15/26] V4L/DVB: ir-core: re-add some debug functions for keytable changes Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 10/26] V4L/DVB: ir-core: Make use of the new IR keymap modules Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 11/26] V4L/DVB: ir-common: remove keymap tables from the module Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 17/26] V4L/DVB: Convert drivers/media/dvb/ttpci/budget-ci.c to use ir-core Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 14/26] V4L/DVB: drivers/media/IR - improve keytable code Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 12/26] V4L/DVB: saa7134: Fix IRQ2 bit names for the register map Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 07/26] V4L/DVB: ir-core: Add support for RC map code register Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 05/26] V4L/DVB: ir-common: Use macros to define the keytables Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 06/26] V4L/DVB: ir-common: move IR tables from ir-keymaps.c to a separate file Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 09/26] V4L/DVB: ir: prepare IR code for a parameter change at register function Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 04/26] V4L/DVB: rename all *_rc_keys to ir_codes_*_nec_table Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 08/26] V4L/DVB: Break Remote Controller keymaps into modules Mauro Carvalho Chehab
2010-04-10 12:27 ` Andy Walls
2010-04-10 16:06 ` Mauro Carvalho Chehab
2010-04-10 17:26 ` Andy Walls
2010-04-06 18:18 ` [PATCH 01/26] V4L/DVB: ir-common: Use a function to declare an IR table Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 03/26] V4L/DVB: IR: use IR_KEYTABLE where an IR table is needed Mauro Carvalho Chehab
2010-04-06 18:18 ` [PATCH 02/26] V4L/DVB: ir-common: re-order keytables by name and remove duplicates Mauro Carvalho Chehab
2010-04-06 19:13 ` [PATCH 16/26] V4L/DVB: ir-core: improve keyup/keydown logic Mauro Carvalho Chehab
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100406151801.552299b9@pedra \
--to=mchehab@redhat.com \
--cc=linux-media@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox