diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-20 12:37:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-20 12:37:45 -0400 |
commit | fb091be08d1acf184e8801dfdcace6e0cb19b1fe (patch) | |
tree | cbd0c4200fd8628d592167589ca790e36fc4ae26 /drivers/media/IR/ir-rc6-decoder.c | |
parent | bd7fc2f2d807fdb254f7efc542f8eec3f23e289e (diff) | |
parent | e8d0416796d43a950ec7b65629e53419b2e22453 (diff) |
Merge branch 'v4l_for_2.6.35' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'v4l_for_2.6.35' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (534 commits)
V4L/DVB (13554a): v4l: Use the video_drvdata function in drivers
V4L/DVB: vivi and mem2mem_testdev need slab.h to build
V4L/DVB: tm6000: bugfix image position
V4L/DVB: IR/imon: remove dead IMON_KEY_RELEASE_OFFSET
V4L/DVB: tm6000: README - add vbi
V4L/DVB: Fix unlock logic at medusa_video_init
V4L/DVB: fix dvb frontend lockup
V4L/DVB: s2255drv: remove dead code
V4L/DVB: s2255drv: return if vdev not found
V4L/DVB: ov511: cleanup: remove unneeded null check
V4L/DVB: media/mem2mem: dereferencing free memory
V4L/DVB: media/IR: Add missing include file to rc-map.c
V4L/DVB: dvb/stv6110x: cleanup error handling
V4L/DVB: ngene: Add lgdt3303 and mt2131 deps to Kconfig
V4L/DVB: ngene: start separating out DVB functions into separate file
V4L/DVB: ngene: split out card specific code into a separate file
V4L/DVB: ngene: split out i2c code into a separate file
V4L/DVB: ngene: add initial support for digital side of Avermedia m780
V4L/DVB: ngene: properly support boards where channel 0 isn't a TS input
V4L-DVB: ngene: make sure that tuner headers are included
...
Diffstat (limited to 'drivers/media/IR/ir-rc6-decoder.c')
-rw-r--r-- | drivers/media/IR/ir-rc6-decoder.c | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/drivers/media/IR/ir-rc6-decoder.c b/drivers/media/IR/ir-rc6-decoder.c new file mode 100644 index 000000000000..2bf479f4f1bc --- /dev/null +++ b/drivers/media/IR/ir-rc6-decoder.c | |||
@@ -0,0 +1,419 @@ | |||
1 | /* ir-rc6-decoder.c - A decoder for the RC6 IR protocol | ||
2 | * | ||
3 | * Copyright (C) 2010 by David Härdeman <david@hardeman.nu> | ||
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 | #include "ir-core-priv.h" | ||
16 | |||
17 | /* | ||
18 | * This decoder currently supports: | ||
19 | * RC6-0-16 (standard toggle bit in header) | ||
20 | * RC6-6A-24 (no toggle bit) | ||
21 | * RC6-6A-32 (MCE version with toggle bit in body) | ||
22 | */ | ||
23 | |||
24 | #define RC6_UNIT 444444 /* us */ | ||
25 | #define RC6_HEADER_NBITS 4 /* not including toggle bit */ | ||
26 | #define RC6_0_NBITS 16 | ||
27 | #define RC6_6A_SMALL_NBITS 24 | ||
28 | #define RC6_6A_LARGE_NBITS 32 | ||
29 | #define RC6_PREFIX_PULSE (6 * RC6_UNIT) | ||
30 | #define RC6_PREFIX_SPACE (2 * RC6_UNIT) | ||
31 | #define RC6_BIT_START (1 * RC6_UNIT) | ||
32 | #define RC6_BIT_END (1 * RC6_UNIT) | ||
33 | #define RC6_TOGGLE_START (2 * RC6_UNIT) | ||
34 | #define RC6_TOGGLE_END (2 * RC6_UNIT) | ||
35 | #define RC6_MODE_MASK 0x07 /* for the header bits */ | ||
36 | #define RC6_STARTBIT_MASK 0x08 /* for the header bits */ | ||
37 | #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ | ||
38 | |||
39 | /* Used to register rc6_decoder clients */ | ||
40 | static LIST_HEAD(decoder_list); | ||
41 | static DEFINE_SPINLOCK(decoder_lock); | ||
42 | |||
43 | enum rc6_mode { | ||
44 | RC6_MODE_0, | ||
45 | RC6_MODE_6A, | ||
46 | RC6_MODE_UNKNOWN, | ||
47 | }; | ||
48 | |||
49 | enum rc6_state { | ||
50 | STATE_INACTIVE, | ||
51 | STATE_PREFIX_SPACE, | ||
52 | STATE_HEADER_BIT_START, | ||
53 | STATE_HEADER_BIT_END, | ||
54 | STATE_TOGGLE_START, | ||
55 | STATE_TOGGLE_END, | ||
56 | STATE_BODY_BIT_START, | ||
57 | STATE_BODY_BIT_END, | ||
58 | STATE_FINISHED, | ||
59 | }; | ||
60 | |||
61 | struct decoder_data { | ||
62 | struct list_head list; | ||
63 | struct ir_input_dev *ir_dev; | ||
64 | int enabled:1; | ||
65 | |||
66 | /* State machine control */ | ||
67 | enum rc6_state state; | ||
68 | u8 header; | ||
69 | u32 body; | ||
70 | struct ir_raw_event prev_ev; | ||
71 | bool toggle; | ||
72 | unsigned count; | ||
73 | unsigned wanted_bits; | ||
74 | }; | ||
75 | |||
76 | |||
77 | /** | ||
78 | * get_decoder_data() - gets decoder data | ||
79 | * @input_dev: input device | ||
80 | * | ||
81 | * Returns the struct decoder_data that corresponds to a device | ||
82 | */ | ||
83 | static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) | ||
84 | { | ||
85 | struct decoder_data *data = NULL; | ||
86 | |||
87 | spin_lock(&decoder_lock); | ||
88 | list_for_each_entry(data, &decoder_list, list) { | ||
89 | if (data->ir_dev == ir_dev) | ||
90 | break; | ||
91 | } | ||
92 | spin_unlock(&decoder_lock); | ||
93 | return data; | ||
94 | } | ||
95 | |||
96 | static ssize_t store_enabled(struct device *d, | ||
97 | struct device_attribute *mattr, | ||
98 | const char *buf, | ||
99 | size_t len) | ||
100 | { | ||
101 | unsigned long value; | ||
102 | struct ir_input_dev *ir_dev = dev_get_drvdata(d); | ||
103 | struct decoder_data *data = get_decoder_data(ir_dev); | ||
104 | |||
105 | if (!data) | ||
106 | return -EINVAL; | ||
107 | |||
108 | if (strict_strtoul(buf, 10, &value) || value > 1) | ||
109 | return -EINVAL; | ||
110 | |||
111 | data->enabled = value; | ||
112 | |||
113 | return len; | ||
114 | } | ||
115 | |||
116 | static ssize_t show_enabled(struct device *d, | ||
117 | struct device_attribute *mattr, char *buf) | ||
118 | { | ||
119 | struct ir_input_dev *ir_dev = dev_get_drvdata(d); | ||
120 | struct decoder_data *data = get_decoder_data(ir_dev); | ||
121 | |||
122 | if (!data) | ||
123 | return -EINVAL; | ||
124 | |||
125 | if (data->enabled) | ||
126 | return sprintf(buf, "1\n"); | ||
127 | else | ||
128 | return sprintf(buf, "0\n"); | ||
129 | } | ||
130 | |||
131 | static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); | ||
132 | |||
133 | static struct attribute *decoder_attributes[] = { | ||
134 | &dev_attr_enabled.attr, | ||
135 | NULL | ||
136 | }; | ||
137 | |||
138 | static struct attribute_group decoder_attribute_group = { | ||
139 | .name = "rc6_decoder", | ||
140 | .attrs = decoder_attributes, | ||
141 | }; | ||
142 | |||
143 | static enum rc6_mode rc6_mode(struct decoder_data *data) { | ||
144 | switch (data->header & RC6_MODE_MASK) { | ||
145 | case 0: | ||
146 | return RC6_MODE_0; | ||
147 | case 6: | ||
148 | if (!data->toggle) | ||
149 | return RC6_MODE_6A; | ||
150 | /* fall through */ | ||
151 | default: | ||
152 | return RC6_MODE_UNKNOWN; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * ir_rc6_decode() - Decode one RC6 pulse or space | ||
158 | * @input_dev: the struct input_dev descriptor of the device | ||
159 | * @ev: the struct ir_raw_event descriptor of the pulse/space | ||
160 | * | ||
161 | * This function returns -EINVAL if the pulse violates the state machine | ||
162 | */ | ||
163 | static int ir_rc6_decode(struct input_dev *input_dev, struct ir_raw_event ev) | ||
164 | { | ||
165 | struct decoder_data *data; | ||
166 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | ||
167 | u32 scancode; | ||
168 | u8 toggle; | ||
169 | |||
170 | data = get_decoder_data(ir_dev); | ||
171 | if (!data) | ||
172 | return -EINVAL; | ||
173 | |||
174 | if (!data->enabled) | ||
175 | return 0; | ||
176 | |||
177 | if (IS_RESET(ev)) { | ||
178 | data->state = STATE_INACTIVE; | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
183 | goto out; | ||
184 | |||
185 | again: | ||
186 | IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n", | ||
187 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | ||
188 | |||
189 | if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
190 | return 0; | ||
191 | |||
192 | switch (data->state) { | ||
193 | |||
194 | case STATE_INACTIVE: | ||
195 | if (!ev.pulse) | ||
196 | break; | ||
197 | |||
198 | /* Note: larger margin on first pulse since each RC6_UNIT | ||
199 | is quite short and some hardware takes some time to | ||
200 | adjust to the signal */ | ||
201 | if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT)) | ||
202 | break; | ||
203 | |||
204 | data->state = STATE_PREFIX_SPACE; | ||
205 | data->count = 0; | ||
206 | return 0; | ||
207 | |||
208 | case STATE_PREFIX_SPACE: | ||
209 | if (ev.pulse) | ||
210 | break; | ||
211 | |||
212 | if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2)) | ||
213 | break; | ||
214 | |||
215 | data->state = STATE_HEADER_BIT_START; | ||
216 | return 0; | ||
217 | |||
218 | case STATE_HEADER_BIT_START: | ||
219 | if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) | ||
220 | break; | ||
221 | |||
222 | data->header <<= 1; | ||
223 | if (ev.pulse) | ||
224 | data->header |= 1; | ||
225 | data->count++; | ||
226 | data->prev_ev = ev; | ||
227 | data->state = STATE_HEADER_BIT_END; | ||
228 | return 0; | ||
229 | |||
230 | case STATE_HEADER_BIT_END: | ||
231 | if (!is_transition(&ev, &data->prev_ev)) | ||
232 | break; | ||
233 | |||
234 | if (data->count == RC6_HEADER_NBITS) | ||
235 | data->state = STATE_TOGGLE_START; | ||
236 | else | ||
237 | data->state = STATE_HEADER_BIT_START; | ||
238 | |||
239 | decrease_duration(&ev, RC6_BIT_END); | ||
240 | goto again; | ||
241 | |||
242 | case STATE_TOGGLE_START: | ||
243 | if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2)) | ||
244 | break; | ||
245 | |||
246 | data->toggle = ev.pulse; | ||
247 | data->prev_ev = ev; | ||
248 | data->state = STATE_TOGGLE_END; | ||
249 | return 0; | ||
250 | |||
251 | case STATE_TOGGLE_END: | ||
252 | if (!is_transition(&ev, &data->prev_ev) || | ||
253 | !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) | ||
254 | break; | ||
255 | |||
256 | if (!(data->header & RC6_STARTBIT_MASK)) { | ||
257 | IR_dprintk(1, "RC6 invalid start bit\n"); | ||
258 | break; | ||
259 | } | ||
260 | |||
261 | data->state = STATE_BODY_BIT_START; | ||
262 | data->prev_ev = ev; | ||
263 | decrease_duration(&ev, RC6_TOGGLE_END); | ||
264 | data->count = 0; | ||
265 | |||
266 | switch (rc6_mode(data)) { | ||
267 | case RC6_MODE_0: | ||
268 | data->wanted_bits = RC6_0_NBITS; | ||
269 | break; | ||
270 | case RC6_MODE_6A: | ||
271 | /* This might look weird, but we basically | ||
272 | check the value of the first body bit to | ||
273 | determine the number of bits in mode 6A */ | ||
274 | if ((!ev.pulse && !geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) || | ||
275 | geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
276 | data->wanted_bits = RC6_6A_LARGE_NBITS; | ||
277 | else | ||
278 | data->wanted_bits = RC6_6A_SMALL_NBITS; | ||
279 | break; | ||
280 | default: | ||
281 | IR_dprintk(1, "RC6 unknown mode\n"); | ||
282 | goto out; | ||
283 | } | ||
284 | goto again; | ||
285 | |||
286 | case STATE_BODY_BIT_START: | ||
287 | if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) | ||
288 | break; | ||
289 | |||
290 | data->body <<= 1; | ||
291 | if (ev.pulse) | ||
292 | data->body |= 1; | ||
293 | data->count++; | ||
294 | data->prev_ev = ev; | ||
295 | |||
296 | data->state = STATE_BODY_BIT_END; | ||
297 | return 0; | ||
298 | |||
299 | case STATE_BODY_BIT_END: | ||
300 | if (!is_transition(&ev, &data->prev_ev)) | ||
301 | break; | ||
302 | |||
303 | if (data->count == data->wanted_bits) | ||
304 | data->state = STATE_FINISHED; | ||
305 | else | ||
306 | data->state = STATE_BODY_BIT_START; | ||
307 | |||
308 | decrease_duration(&ev, RC6_BIT_END); | ||
309 | goto again; | ||
310 | |||
311 | case STATE_FINISHED: | ||
312 | if (ev.pulse) | ||
313 | break; | ||
314 | |||
315 | switch (rc6_mode(data)) { | ||
316 | case RC6_MODE_0: | ||
317 | scancode = data->body & 0xffff; | ||
318 | toggle = data->toggle; | ||
319 | IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", | ||
320 | scancode, toggle); | ||
321 | break; | ||
322 | case RC6_MODE_6A: | ||
323 | if (data->wanted_bits == RC6_6A_LARGE_NBITS) { | ||
324 | toggle = data->body & RC6_6A_MCE_TOGGLE_MASK ? 1 : 0; | ||
325 | scancode = data->body & ~RC6_6A_MCE_TOGGLE_MASK; | ||
326 | } else { | ||
327 | toggle = 0; | ||
328 | scancode = data->body & 0xffffff; | ||
329 | } | ||
330 | |||
331 | IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n", | ||
332 | scancode, toggle); | ||
333 | break; | ||
334 | default: | ||
335 | IR_dprintk(1, "RC6 unknown mode\n"); | ||
336 | goto out; | ||
337 | } | ||
338 | |||
339 | ir_keydown(input_dev, scancode, toggle); | ||
340 | data->state = STATE_INACTIVE; | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | out: | ||
345 | IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n", | ||
346 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | ||
347 | data->state = STATE_INACTIVE; | ||
348 | return -EINVAL; | ||
349 | } | ||
350 | |||
351 | static int ir_rc6_register(struct input_dev *input_dev) | ||
352 | { | ||
353 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | ||
354 | struct decoder_data *data; | ||
355 | int rc; | ||
356 | |||
357 | rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); | ||
358 | if (rc < 0) | ||
359 | return rc; | ||
360 | |||
361 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
362 | if (!data) { | ||
363 | sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); | ||
364 | return -ENOMEM; | ||
365 | } | ||
366 | |||
367 | data->ir_dev = ir_dev; | ||
368 | data->enabled = 1; | ||
369 | |||
370 | spin_lock(&decoder_lock); | ||
371 | list_add_tail(&data->list, &decoder_list); | ||
372 | spin_unlock(&decoder_lock); | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static int ir_rc6_unregister(struct input_dev *input_dev) | ||
378 | { | ||
379 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | ||
380 | static struct decoder_data *data; | ||
381 | |||
382 | data = get_decoder_data(ir_dev); | ||
383 | if (!data) | ||
384 | return 0; | ||
385 | |||
386 | sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); | ||
387 | |||
388 | spin_lock(&decoder_lock); | ||
389 | list_del(&data->list); | ||
390 | spin_unlock(&decoder_lock); | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static struct ir_raw_handler rc6_handler = { | ||
396 | .decode = ir_rc6_decode, | ||
397 | .raw_register = ir_rc6_register, | ||
398 | .raw_unregister = ir_rc6_unregister, | ||
399 | }; | ||
400 | |||
401 | static int __init ir_rc6_decode_init(void) | ||
402 | { | ||
403 | ir_raw_handler_register(&rc6_handler); | ||
404 | |||
405 | printk(KERN_INFO "IR RC6 protocol handler initialized\n"); | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static void __exit ir_rc6_decode_exit(void) | ||
410 | { | ||
411 | ir_raw_handler_unregister(&rc6_handler); | ||
412 | } | ||
413 | |||
414 | module_init(ir_rc6_decode_init); | ||
415 | module_exit(ir_rc6_decode_exit); | ||
416 | |||
417 | MODULE_LICENSE("GPL"); | ||
418 | MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); | ||
419 | MODULE_DESCRIPTION("RC6 IR protocol decoder"); | ||