diff options
Diffstat (limited to 'drivers/media/rc/ir-rc6-decoder.c')
-rw-r--r-- | drivers/media/rc/ir-rc6-decoder.c | 280 |
1 files changed, 280 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..755dafa3871b --- /dev/null +++ b/drivers/media/rc/ir-rc6-decoder.c | |||
@@ -0,0 +1,280 @@ | |||
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 "rc-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 | * @dev: the struct rc_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 rc_dev *dev, struct ir_raw_event ev) | ||
79 | { | ||
80 | struct rc6_dec *data = &dev->raw->rc6; | ||
81 | u32 scancode; | ||
82 | u8 toggle; | ||
83 | |||
84 | if (!(dev->raw->enabled_protocols & RC_TYPE_RC6)) | ||
85 | return 0; | ||
86 | |||
87 | if (!is_timing_event(ev)) { | ||
88 | if (ev.reset) | ||
89 | data->state = STATE_INACTIVE; | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
94 | goto out; | ||
95 | |||
96 | again: | ||
97 | IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n", | ||
98 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | ||
99 | |||
100 | if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
101 | return 0; | ||
102 | |||
103 | switch (data->state) { | ||
104 | |||
105 | case STATE_INACTIVE: | ||
106 | if (!ev.pulse) | ||
107 | break; | ||
108 | |||
109 | /* Note: larger margin on first pulse since each RC6_UNIT | ||
110 | is quite short and some hardware takes some time to | ||
111 | adjust to the signal */ | ||
112 | if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT)) | ||
113 | break; | ||
114 | |||
115 | data->state = STATE_PREFIX_SPACE; | ||
116 | data->count = 0; | ||
117 | return 0; | ||
118 | |||
119 | case STATE_PREFIX_SPACE: | ||
120 | if (ev.pulse) | ||
121 | break; | ||
122 | |||
123 | if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2)) | ||
124 | break; | ||
125 | |||
126 | data->state = STATE_HEADER_BIT_START; | ||
127 | return 0; | ||
128 | |||
129 | case STATE_HEADER_BIT_START: | ||
130 | if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) | ||
131 | break; | ||
132 | |||
133 | data->header <<= 1; | ||
134 | if (ev.pulse) | ||
135 | data->header |= 1; | ||
136 | data->count++; | ||
137 | data->state = STATE_HEADER_BIT_END; | ||
138 | return 0; | ||
139 | |||
140 | case STATE_HEADER_BIT_END: | ||
141 | if (!is_transition(&ev, &dev->raw->prev_ev)) | ||
142 | break; | ||
143 | |||
144 | if (data->count == RC6_HEADER_NBITS) | ||
145 | data->state = STATE_TOGGLE_START; | ||
146 | else | ||
147 | data->state = STATE_HEADER_BIT_START; | ||
148 | |||
149 | decrease_duration(&ev, RC6_BIT_END); | ||
150 | goto again; | ||
151 | |||
152 | case STATE_TOGGLE_START: | ||
153 | if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2)) | ||
154 | break; | ||
155 | |||
156 | data->toggle = ev.pulse; | ||
157 | data->state = STATE_TOGGLE_END; | ||
158 | return 0; | ||
159 | |||
160 | case STATE_TOGGLE_END: | ||
161 | if (!is_transition(&ev, &dev->raw->prev_ev) || | ||
162 | !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) | ||
163 | break; | ||
164 | |||
165 | if (!(data->header & RC6_STARTBIT_MASK)) { | ||
166 | IR_dprintk(1, "RC6 invalid start bit\n"); | ||
167 | break; | ||
168 | } | ||
169 | |||
170 | data->state = STATE_BODY_BIT_START; | ||
171 | decrease_duration(&ev, RC6_TOGGLE_END); | ||
172 | data->count = 0; | ||
173 | |||
174 | switch (rc6_mode(data)) { | ||
175 | case RC6_MODE_0: | ||
176 | data->wanted_bits = RC6_0_NBITS; | ||
177 | break; | ||
178 | case RC6_MODE_6A: | ||
179 | /* This might look weird, but we basically | ||
180 | check the value of the first body bit to | ||
181 | determine the number of bits in mode 6A */ | ||
182 | if ((!ev.pulse && !geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) || | ||
183 | geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) | ||
184 | data->wanted_bits = RC6_6A_LARGE_NBITS; | ||
185 | else | ||
186 | data->wanted_bits = RC6_6A_SMALL_NBITS; | ||
187 | break; | ||
188 | default: | ||
189 | IR_dprintk(1, "RC6 unknown mode\n"); | ||
190 | goto out; | ||
191 | } | ||
192 | goto again; | ||
193 | |||
194 | case STATE_BODY_BIT_START: | ||
195 | if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) | ||
196 | break; | ||
197 | |||
198 | data->body <<= 1; | ||
199 | if (ev.pulse) | ||
200 | data->body |= 1; | ||
201 | data->count++; | ||
202 | data->state = STATE_BODY_BIT_END; | ||
203 | return 0; | ||
204 | |||
205 | case STATE_BODY_BIT_END: | ||
206 | if (!is_transition(&ev, &dev->raw->prev_ev)) | ||
207 | break; | ||
208 | |||
209 | if (data->count == data->wanted_bits) | ||
210 | data->state = STATE_FINISHED; | ||
211 | else | ||
212 | data->state = STATE_BODY_BIT_START; | ||
213 | |||
214 | decrease_duration(&ev, RC6_BIT_END); | ||
215 | goto again; | ||
216 | |||
217 | case STATE_FINISHED: | ||
218 | if (ev.pulse) | ||
219 | break; | ||
220 | |||
221 | switch (rc6_mode(data)) { | ||
222 | case RC6_MODE_0: | ||
223 | scancode = data->body & 0xffff; | ||
224 | toggle = data->toggle; | ||
225 | IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", | ||
226 | scancode, toggle); | ||
227 | break; | ||
228 | case RC6_MODE_6A: | ||
229 | if (data->wanted_bits == RC6_6A_LARGE_NBITS) { | ||
230 | toggle = data->body & RC6_6A_MCE_TOGGLE_MASK ? 1 : 0; | ||
231 | scancode = data->body & ~RC6_6A_MCE_TOGGLE_MASK; | ||
232 | } else { | ||
233 | toggle = 0; | ||
234 | scancode = data->body & 0xffffff; | ||
235 | } | ||
236 | |||
237 | IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n", | ||
238 | scancode, toggle); | ||
239 | break; | ||
240 | default: | ||
241 | IR_dprintk(1, "RC6 unknown mode\n"); | ||
242 | goto out; | ||
243 | } | ||
244 | |||
245 | rc_keydown(dev, scancode, toggle); | ||
246 | data->state = STATE_INACTIVE; | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | out: | ||
251 | IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n", | ||
252 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | ||
253 | data->state = STATE_INACTIVE; | ||
254 | return -EINVAL; | ||
255 | } | ||
256 | |||
257 | static struct ir_raw_handler rc6_handler = { | ||
258 | .protocols = RC_TYPE_RC6, | ||
259 | .decode = ir_rc6_decode, | ||
260 | }; | ||
261 | |||
262 | static int __init ir_rc6_decode_init(void) | ||
263 | { | ||
264 | ir_raw_handler_register(&rc6_handler); | ||
265 | |||
266 | printk(KERN_INFO "IR RC6 protocol handler initialized\n"); | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static void __exit ir_rc6_decode_exit(void) | ||
271 | { | ||
272 | ir_raw_handler_unregister(&rc6_handler); | ||
273 | } | ||
274 | |||
275 | module_init(ir_rc6_decode_init); | ||
276 | module_exit(ir_rc6_decode_exit); | ||
277 | |||
278 | MODULE_LICENSE("GPL"); | ||
279 | MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); | ||
280 | MODULE_DESCRIPTION("RC6 IR protocol decoder"); | ||