diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-11-09 21:00:14 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-12-29 05:16:36 -0500 |
commit | 32cf86f6d16367db5a10039c1dd938a2427d697c (patch) | |
tree | 21e6716b64d80d280da2561efd873430ca833f08 /drivers/media/rc/ir-rc6-decoder.c | |
parent | 3ffea4988be3f3fa65f2104ba31eff2b5e0e82a0 (diff) |
[media] rename drivers/media/IR to drives/media/rc
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/rc/ir-rc6-decoder.c')
-rw-r--r-- | drivers/media/rc/ir-rc6-decoder.c | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c new file mode 100644 index 000000000000..d25da91f44ff --- /dev/null +++ b/drivers/media/rc/ir-rc6-decoder.c | |||
@@ -0,0 +1,281 @@ | |||
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 | enum rc6_mode { | ||
40 | RC6_MODE_0, | ||
41 | RC6_MODE_6A, | ||
42 | RC6_MODE_UNKNOWN, | ||
43 | }; | ||
44 | |||
45 | enum rc6_state { | ||
46 | STATE_INACTIVE, | ||
47 | STATE_PREFIX_SPACE, | ||
48 | STATE_HEADER_BIT_START, | ||
49 | STATE_HEADER_BIT_END, | ||
50 | STATE_TOGGLE_START, | ||
51 | STATE_TOGGLE_END, | ||
52 | STATE_BODY_BIT_START, | ||
53 | STATE_BODY_BIT_END, | ||
54 | STATE_FINISHED, | ||
55 | }; | ||
56 | |||
57 | static enum rc6_mode rc6_mode(struct rc6_dec *data) | ||
58 | { | ||
59 | switch (data->header & RC6_MODE_MASK) { | ||
60 | case 0: | ||
61 | return RC6_MODE_0; | ||
62 | case 6: | ||
63 | if (!data->toggle) | ||
64 | return RC6_MODE_6A; | ||
65 | /* fall through */ | ||
66 | default: | ||
67 | return RC6_MODE_UNKNOWN; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * ir_rc6_decode() - Decode one RC6 pulse or space | ||
73 | * @input_dev: the struct input_dev descriptor of the device | ||
74 | * @ev: the struct ir_raw_event descriptor of the pulse/space | ||
75 | * | ||
76 | * This function returns -EINVAL if the pulse violates the state machine | ||
77 | */ | ||
78 | static int ir_rc6_decode(struct input_dev *input_dev, struct ir_raw_event ev) | ||
79 | { | ||
80 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | ||
81 | struct rc6_dec *data = &ir_dev->raw->rc6; | ||
82 | u32 scancode; | ||
83 | u8 toggle; | ||
84 | |||
85 | if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC6)) | ||
86 | return 0; | ||
87 | |||
88 | if (!is_timing_event(ev)) { | ||
89 | if (ev.reset) | ||
90 | data->state = STATE_INACTIVE; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
95 | goto out; | ||
96 | |||
97 | again: | ||
98 | IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n", | ||
99 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | ||
100 | |||
101 | if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
102 | return 0; | ||
103 | |||
104 | switch (data->state) { | ||
105 | |||
106 | case STATE_INACTIVE: | ||
107 | if (!ev.pulse) | ||
108 | break; | ||
109 | |||
110 | /* Note: larger margin on first pulse since each RC6_UNIT | ||
111 | is quite short and some hardware takes some time to | ||
112 | adjust to the signal */ | ||
113 | if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT)) | ||
114 | break; | ||
115 | |||
116 | data->state = STATE_PREFIX_SPACE; | ||
117 | data->count = 0; | ||
118 | return 0; | ||
119 | |||
120 | case STATE_PREFIX_SPACE: | ||
121 | if (ev.pulse) | ||
122 | break; | ||
123 | |||
124 | if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2)) | ||
125 | break; | ||
126 | |||
127 | data->state = STATE_HEADER_BIT_START; | ||
128 | return 0; | ||
129 | |||
130 | case STATE_HEADER_BIT_START: | ||
131 | if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) | ||
132 | break; | ||
133 | |||
134 | data->header <<= 1; | ||
135 | if (ev.pulse) | ||
136 | data->header |= 1; | ||
137 | data->count++; | ||
138 | data->state = STATE_HEADER_BIT_END; | ||
139 | return 0; | ||
140 | |||
141 | case STATE_HEADER_BIT_END: | ||
142 | if (!is_transition(&ev, &ir_dev->raw->prev_ev)) | ||
143 | break; | ||
144 | |||
145 | if (data->count == RC6_HEADER_NBITS) | ||
146 | data->state = STATE_TOGGLE_START; | ||
147 | else | ||
148 | data->state = STATE_HEADER_BIT_START; | ||
149 | |||
150 | decrease_duration(&ev, RC6_BIT_END); | ||
151 | goto again; | ||
152 | |||
153 | case STATE_TOGGLE_START: | ||
154 | if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2)) | ||
155 | break; | ||
156 | |||
157 | data->toggle = ev.pulse; | ||
158 | data->state = STATE_TOGGLE_END; | ||
159 | return 0; | ||
160 | |||
161 | case STATE_TOGGLE_END: | ||
162 | if (!is_transition(&ev, &ir_dev->raw->prev_ev) || | ||
163 | !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) | ||
164 | break; | ||
165 | |||
166 | if (!(data->header & RC6_STARTBIT_MASK)) { | ||
167 | IR_dprintk(1, "RC6 invalid start bit\n"); | ||
168 | break; | ||
169 | } | ||
170 | |||
171 | data->state = STATE_BODY_BIT_START; | ||
172 | decrease_duration(&ev, RC6_TOGGLE_END); | ||
173 | data->count = 0; | ||
174 | |||
175 | switch (rc6_mode(data)) { | ||
176 | case RC6_MODE_0: | ||
177 | data->wanted_bits = RC6_0_NBITS; | ||
178 | break; | ||
179 | case RC6_MODE_6A: | ||
180 | /* This might look weird, but we basically | ||
181 | check the value of the first body bit to | ||
182 | determine the number of bits in mode 6A */ | ||
183 | if ((!ev.pulse && !geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) || | ||
184 | geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
185 | data->wanted_bits = RC6_6A_LARGE_NBITS; | ||
186 | else | ||
187 | data->wanted_bits = RC6_6A_SMALL_NBITS; | ||
188 | break; | ||
189 | default: | ||
190 | IR_dprintk(1, "RC6 unknown mode\n"); | ||
191 | goto out; | ||
192 | } | ||
193 | goto again; | ||
194 | |||
195 | case STATE_BODY_BIT_START: | ||
196 | if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) | ||
197 | break; | ||
198 | |||
199 | data->body <<= 1; | ||
200 | if (ev.pulse) | ||
201 | data->body |= 1; | ||
202 | data->count++; | ||
203 | data->state = STATE_BODY_BIT_END; | ||
204 | return 0; | ||
205 | |||
206 | case STATE_BODY_BIT_END: | ||
207 | if (!is_transition(&ev, &ir_dev->raw->prev_ev)) | ||
208 | break; | ||
209 | |||
210 | if (data->count == data->wanted_bits) | ||
211 | data->state = STATE_FINISHED; | ||
212 | else | ||
213 | data->state = STATE_BODY_BIT_START; | ||
214 | |||
215 | decrease_duration(&ev, RC6_BIT_END); | ||
216 | goto again; | ||
217 | |||
218 | case STATE_FINISHED: | ||
219 | if (ev.pulse) | ||
220 | break; | ||
221 | |||
222 | switch (rc6_mode(data)) { | ||
223 | case RC6_MODE_0: | ||
224 | scancode = data->body & 0xffff; | ||
225 | toggle = data->toggle; | ||
226 | IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", | ||
227 | scancode, toggle); | ||
228 | break; | ||
229 | case RC6_MODE_6A: | ||
230 | if (data->wanted_bits == RC6_6A_LARGE_NBITS) { | ||
231 | toggle = data->body & RC6_6A_MCE_TOGGLE_MASK ? 1 : 0; | ||
232 | scancode = data->body & ~RC6_6A_MCE_TOGGLE_MASK; | ||
233 | } else { | ||
234 | toggle = 0; | ||
235 | scancode = data->body & 0xffffff; | ||
236 | } | ||
237 | |||
238 | IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n", | ||
239 | scancode, toggle); | ||
240 | break; | ||
241 | default: | ||
242 | IR_dprintk(1, "RC6 unknown mode\n"); | ||
243 | goto out; | ||
244 | } | ||
245 | |||
246 | ir_keydown(input_dev, scancode, toggle); | ||
247 | data->state = STATE_INACTIVE; | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | out: | ||
252 | IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n", | ||
253 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | ||
254 | data->state = STATE_INACTIVE; | ||
255 | return -EINVAL; | ||
256 | } | ||
257 | |||
258 | static struct ir_raw_handler rc6_handler = { | ||
259 | .protocols = IR_TYPE_RC6, | ||
260 | .decode = ir_rc6_decode, | ||
261 | }; | ||
262 | |||
263 | static int __init ir_rc6_decode_init(void) | ||
264 | { | ||
265 | ir_raw_handler_register(&rc6_handler); | ||
266 | |||
267 | printk(KERN_INFO "IR RC6 protocol handler initialized\n"); | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static void __exit ir_rc6_decode_exit(void) | ||
272 | { | ||
273 | ir_raw_handler_unregister(&rc6_handler); | ||
274 | } | ||
275 | |||
276 | module_init(ir_rc6_decode_init); | ||
277 | module_exit(ir_rc6_decode_exit); | ||
278 | |||
279 | MODULE_LICENSE("GPL"); | ||
280 | MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); | ||
281 | MODULE_DESCRIPTION("RC6 IR protocol decoder"); | ||