aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2010-04-04 09:27:20 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-05-19 11:56:56 -0400
commitdb1423a6c79f66db2b1846614c13bde9c2db7ad2 (patch)
treee5d785694f2ee18deabb59267b19e160b0a96b50 /drivers
parent67780d6a2347d03b640f22295f8df7f00fbc829f (diff)
V4L-DVB: ir-rc5-decoder: Add a decoder for RC-5 IR protocol
This decoder is also based on a state machine, just like the NEC protocol decoder. It is pedantic in the sense that accepts only 14 bits. As there are some variants that outputs less bits, it needs to be improved to also handle those. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/IR/Kconfig9
-rw-r--r--drivers/media/IR/Makefile1
-rw-r--r--drivers/media/IR/ir-raw-event.c1
-rw-r--r--drivers/media/IR/ir-rc5-decoder.c371
4 files changed, 382 insertions, 0 deletions
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
index 0c557b8cc932..ba81bda3513d 100644
--- a/drivers/media/IR/Kconfig
+++ b/drivers/media/IR/Kconfig
@@ -18,3 +18,12 @@ config IR_NEC_DECODER
18 ---help--- 18 ---help---
19 Enable this option if you have IR with NEC protocol, and 19 Enable this option if you have IR with NEC protocol, and
20 if the IR is decoded in software 20 if the IR is decoded in software
21
22config IR_RC5_DECODER
23 tristate "Enable IR raw decoder for RC-5 protocol"
24 depends on IR_CORE
25 default y
26
27 ---help---
28 Enable this option if you have IR with RC-5 protocol, and
29 if the IR is decoded in software
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
index 0e3f912054ca..62e12d5c443a 100644
--- a/drivers/media/IR/Makefile
+++ b/drivers/media/IR/Makefile
@@ -6,3 +6,4 @@ obj-y += keymaps/
6obj-$(CONFIG_IR_CORE) += ir-core.o 6obj-$(CONFIG_IR_CORE) += ir-core.o
7obj-$(CONFIG_VIDEO_IR) += ir-common.o 7obj-$(CONFIG_VIDEO_IR) += ir-common.o
8obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o 8obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
9obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c
index 59f20541b03e..617e437e2beb 100644
--- a/drivers/media/IR/ir-raw-event.c
+++ b/drivers/media/IR/ir-raw-event.c
@@ -200,6 +200,7 @@ static void init_decoders(struct work_struct *work)
200 /* Load the decoder modules */ 200 /* Load the decoder modules */
201 201
202 load_nec_decode(); 202 load_nec_decode();
203 load_rc5_decode();
203 204
204 /* If needed, we may later add some init code. In this case, 205 /* If needed, we may later add some init code. In this case,
205 it is needed to change the CONFIG_MODULE test at ir-core.h 206 it is needed to change the CONFIG_MODULE test at ir-core.h
diff --git a/drivers/media/IR/ir-rc5-decoder.c b/drivers/media/IR/ir-rc5-decoder.c
new file mode 100644
index 000000000000..4b7eafecd842
--- /dev/null
+++ b/drivers/media/IR/ir-rc5-decoder.c
@@ -0,0 +1,371 @@
1/* ir-rc5-decoder.c - handle RC-5 IR Pulse/Space protocol
2 *
3 * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15/*
16 * This code only handles 14 bits RC-5 protocols. There are other variants
17 * that use a different number of bits. This is currently unsupported
18 */
19
20#include <media/ir-core.h>
21
22#define RC5_NBITS 14
23#define RC5_HALFBIT 888888 /* ns */
24#define RC5_BIT (RC5_HALFBIT * 2)
25#define RC5_DURATION (RC5_BIT * RC5_NBITS)
26
27#define is_rc5_halfbit(nsec) ((ev->delta.tv_nsec >= RC5_HALFBIT / 2) && \
28 (ev->delta.tv_nsec < RC5_HALFBIT + RC5_HALFBIT / 2))
29
30#define n_half(nsec) ((ev->delta.tv_nsec + RC5_HALFBIT / 2) / RC5_HALFBIT)
31
32/* Used to register rc5_decoder clients */
33static LIST_HEAD(decoder_list);
34static spinlock_t decoder_lock;
35
36enum rc5_state {
37 STATE_INACTIVE,
38 STATE_START2_SPACE,
39 STATE_START2_MARK,
40 STATE_MARKSPACE,
41 STATE_TRAILER_MARK,
42};
43
44static char *st_name[] = {
45 "Inactive",
46 "start2 sapce",
47 "start2 mark",
48 "mark",
49 "space",
50 "trailer"
51};
52
53struct rc5_code {
54 u8 address;
55 u8 command;
56};
57
58struct decoder_data {
59 struct list_head list;
60 struct ir_input_dev *ir_dev;
61 int enabled:1;
62
63 /* State machine control */
64 enum rc5_state state;
65 struct rc5_code rc5_code;
66 unsigned n_half;
67 unsigned count;
68};
69
70
71/**
72 * get_decoder_data() - gets decoder data
73 * @input_dev: input device
74 *
75 * Returns the struct decoder_data that corresponds to a device
76 */
77
78static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev)
79{
80 struct decoder_data *data = NULL;
81
82 spin_lock(&decoder_lock);
83 list_for_each_entry(data, &decoder_list, list) {
84 if (data->ir_dev == ir_dev)
85 break;
86 }
87 spin_unlock(&decoder_lock);
88 return data;
89}
90
91static ssize_t store_enabled(struct device *d,
92 struct device_attribute *mattr,
93 const char *buf,
94 size_t len)
95{
96 unsigned long value;
97 struct ir_input_dev *ir_dev = dev_get_drvdata(d);
98 struct decoder_data *data = get_decoder_data(ir_dev);
99
100 if (!data)
101 return -EINVAL;
102
103 if (strict_strtoul(buf, 10, &value) || value > 1)
104 return -EINVAL;
105
106 data->enabled = value;
107
108 return len;
109}
110
111static ssize_t show_enabled(struct device *d,
112 struct device_attribute *mattr, char *buf)
113{
114 struct ir_input_dev *ir_dev = dev_get_drvdata(d);
115 struct decoder_data *data = get_decoder_data(ir_dev);
116
117 if (!data)
118 return -EINVAL;
119
120 if (data->enabled)
121 return sprintf(buf, "1\n");
122 else
123 return sprintf(buf, "0\n");
124}
125
126static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled);
127
128static struct attribute *decoder_attributes[] = {
129 &dev_attr_enabled.attr,
130 NULL
131};
132
133static struct attribute_group decoder_attribute_group = {
134 .name = "rc5_decoder",
135 .attrs = decoder_attributes,
136};
137
138/**
139 * handle_event() - Decode one RC-5 pulse or space
140 * @input_dev: the struct input_dev descriptor of the device
141 * @ev: event array with type/duration of pulse/space
142 *
143 * This function returns -EINVAL if the pulse violates the state machine
144 */
145static int handle_event(struct input_dev *input_dev,
146 struct ir_raw_event *ev)
147{
148 struct decoder_data *data;
149 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
150 int bit, last_bit, n_half;
151
152 data = get_decoder_data(ir_dev);
153 if (!data)
154 return -EINVAL;
155
156 /* Except for the initial event, what matters is the previous bit */
157 bit = (ev->type & IR_PULSE) ? 1 : 0;
158
159 last_bit = !bit;
160
161 /* Discards spurious space last_bits when inactive */
162
163 /* Very long delays are considered as start events */
164 if (ev->delta.tv_nsec > RC5_DURATION + RC5_HALFBIT / 2)
165 data->state = STATE_INACTIVE;
166
167 if (ev->type & IR_START_EVENT)
168 data->state = STATE_INACTIVE;
169
170 switch (data->state) {
171 case STATE_INACTIVE:
172IR_dprintk(1, "currently inative. Received bit (%s) @%luus\n",
173 last_bit ? "pulse" : "space",
174 (ev->delta.tv_nsec + 500) / 1000);
175
176 /* Discards the initial start space */
177 if (bit)
178 return 0;
179 data->count = 0;
180 data->n_half = 0;
181 memset (&data->rc5_code, 0, sizeof(data->rc5_code));
182
183 data->state = STATE_START2_SPACE;
184 return 0;
185 case STATE_START2_SPACE:
186 if (last_bit)
187 goto err;
188 if (!is_rc5_halfbit(ev->delta.tv_nsec))
189 goto err;
190 data->state = STATE_START2_MARK;
191 return 0;
192 case STATE_START2_MARK:
193 if (!last_bit)
194 goto err;
195
196 if (!is_rc5_halfbit(ev->delta.tv_nsec))
197 goto err;
198
199 data->state = STATE_MARKSPACE;
200 return 0;
201 case STATE_MARKSPACE:
202 n_half = n_half(ev->delta.tv_nsec);
203 if (n_half < 1 || n_half > 3) {
204 IR_dprintk(1, "Decode failed at %d-th bit (%s) @%luus\n",
205 data->count,
206 last_bit ? "pulse" : "space",
207 (ev->delta.tv_nsec + 500) / 1000);
208printk("%d halves\n", n_half);
209 goto err2;
210 }
211 data->n_half += n_half;
212
213 if (!last_bit)
214 return 0;
215
216 /* Got one complete mark/space cycle */
217
218 bit = ((data->count + 1) * 2)/ data->n_half;
219
220printk("%d halves, %d bits\n", n_half, bit);
221
222#if 1 /* SANITY check - while testing the decoder */
223 if (bit > 1) {
224 IR_dprintk(1, "Decoder HAS failed at %d-th bit (%s) @%luus\n",
225 data->count,
226 last_bit ? "pulse" : "space",
227 (ev->delta.tv_nsec + 500) / 1000);
228
229 goto err2;
230 }
231#endif
232 /* Ok, we've got a valid bit. proccess it */
233 if (bit) {
234 int shift = data->count;
235
236 /*
237 * RC-5 transmit bytes on this temporal order:
238 * address | not address | command | not command
239 */
240 if (shift < 8) {
241 data->rc5_code.address |= 1 << shift;
242 } else {
243 data->rc5_code.command |= 1 << (shift - 8);
244 }
245 }
246 IR_dprintk(1, "RC-5: bit #%d: %d (%d)\n",
247 data->count, bit, data->n_half);
248 if (++data->count >= RC5_NBITS) {
249 u32 scancode;
250 scancode = data->rc5_code.address << 8 |
251 data->rc5_code.command;
252 IR_dprintk(1, "RC-5 scancode 0x%04x\n", scancode);
253
254 ir_keydown(input_dev, scancode, 0);
255
256 data->state = STATE_TRAILER_MARK;
257 }
258 return 0;
259 case STATE_TRAILER_MARK:
260 if (!last_bit)
261 goto err;
262 data->state = STATE_INACTIVE;
263 return 0;
264 }
265
266err:
267 IR_dprintk(1, "RC-5 decoded failed at state %s (%s) @ %luus\n",
268 st_name[data->state],
269 bit ? "pulse" : "space",
270 (ev->delta.tv_nsec + 500) / 1000);
271err2:
272 data->state = STATE_INACTIVE;
273 return -EINVAL;
274}
275
276/**
277 * ir_rc5_decode() - Decodes all RC-5 pulsecodes on a given array
278 * @input_dev: the struct input_dev descriptor of the device
279 * @evs: event array with type/duration of pulse/space
280 * @len: length of the array
281 * This function returns the number of decoded pulses
282 */
283static int ir_rc5_decode(struct input_dev *input_dev,
284 struct ir_raw_event *evs,
285 int len)
286{
287 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
288 struct decoder_data *data;
289 int pos = 0;
290 int rc = 0;
291
292 data = get_decoder_data(ir_dev);
293 if (!data || !data->enabled)
294 return 0;
295
296 for (pos = 0; pos < len; pos++)
297 handle_event(input_dev, &evs[pos]);
298
299 return rc;
300}
301
302static int ir_rc5_register(struct input_dev *input_dev)
303{
304 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
305 struct decoder_data *data;
306 int rc;
307
308 rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group);
309 if (rc < 0)
310 return rc;
311
312 data = kzalloc(sizeof(*data), GFP_KERNEL);
313 if (!data) {
314 sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
315 return -ENOMEM;
316 }
317
318 data->ir_dev = ir_dev;
319 data->enabled = 1;
320
321 spin_lock(&decoder_lock);
322 list_add_tail(&data->list, &decoder_list);
323 spin_unlock(&decoder_lock);
324
325 return 0;
326}
327
328static int ir_rc5_unregister(struct input_dev *input_dev)
329{
330 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
331 static struct decoder_data *data;
332
333 data = get_decoder_data(ir_dev);
334 if (!data)
335 return 0;
336
337 sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
338
339 spin_lock(&decoder_lock);
340 list_del(&data->list);
341 spin_unlock(&decoder_lock);
342
343 return 0;
344}
345
346static struct ir_raw_handler rc5_handler = {
347 .decode = ir_rc5_decode,
348 .raw_register = ir_rc5_register,
349 .raw_unregister = ir_rc5_unregister,
350};
351
352static int __init ir_rc5_decode_init(void)
353{
354 ir_raw_handler_register(&rc5_handler);
355
356 printk(KERN_INFO "IR RC-5 protocol handler initialized\n");
357 return 0;
358}
359
360static void __exit ir_rc5_decode_exit(void)
361{
362 ir_raw_handler_unregister(&rc5_handler);
363}
364
365module_init(ir_rc5_decode_init);
366module_exit(ir_rc5_decode_exit);
367
368MODULE_LICENSE("GPL");
369MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
370MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
371MODULE_DESCRIPTION("RC-5 IR protocol decoder");