Commit | Line | Data |
---|---|---|
784a4931 DH |
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 | ||
f62de675 | 15 | #include "rc-core-priv.h" |
7a707b89 | 16 | #include <linux/module.h> |
784a4931 DH |
17 | |
18 | /* | |
19 | * This decoder currently supports: | |
20 | * RC6-0-16 (standard toggle bit in header) | |
db9bc660 | 21 | * RC6-6A-20 (no toggle bit) |
784a4931 DH |
22 | * RC6-6A-24 (no toggle bit) |
23 | * RC6-6A-32 (MCE version with toggle bit in body) | |
24 | */ | |
25 | ||
db9bc660 | 26 | #define RC6_UNIT 444444 /* nanosecs */ |
784a4931 DH |
27 | #define RC6_HEADER_NBITS 4 /* not including toggle bit */ |
28 | #define RC6_0_NBITS 16 | |
db9bc660 | 29 | #define RC6_6A_32_NBITS 32 |
30 | #define RC6_6A_NBITS 128 /* Variable 8..128 */ | |
e40b1127 DH |
31 | #define RC6_PREFIX_PULSE (6 * RC6_UNIT) |
32 | #define RC6_PREFIX_SPACE (2 * RC6_UNIT) | |
33 | #define RC6_BIT_START (1 * RC6_UNIT) | |
34 | #define RC6_BIT_END (1 * RC6_UNIT) | |
35 | #define RC6_TOGGLE_START (2 * RC6_UNIT) | |
36 | #define RC6_TOGGLE_END (2 * RC6_UNIT) | |
db9bc660 | 37 | #define RC6_SUFFIX_SPACE (6 * RC6_UNIT) |
784a4931 DH |
38 | #define RC6_MODE_MASK 0x07 /* for the header bits */ |
39 | #define RC6_STARTBIT_MASK 0x08 /* for the header bits */ | |
40 | #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ | |
db9bc660 | 41 | #define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */ |
42 | #define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */ | |
43 | #ifndef CHAR_BIT | |
44 | #define CHAR_BIT 8 /* Normally in <limits.h> */ | |
45 | #endif | |
784a4931 | 46 | |
784a4931 DH |
47 | enum rc6_mode { |
48 | RC6_MODE_0, | |
49 | RC6_MODE_6A, | |
50 | RC6_MODE_UNKNOWN, | |
51 | }; | |
52 | ||
53 | enum rc6_state { | |
54 | STATE_INACTIVE, | |
55 | STATE_PREFIX_SPACE, | |
56 | STATE_HEADER_BIT_START, | |
57 | STATE_HEADER_BIT_END, | |
58 | STATE_TOGGLE_START, | |
59 | STATE_TOGGLE_END, | |
60 | STATE_BODY_BIT_START, | |
61 | STATE_BODY_BIT_END, | |
62 | STATE_FINISHED, | |
63 | }; | |
64 | ||
c216369e | 65 | static enum rc6_mode rc6_mode(struct rc6_dec *data) |
784a4931 | 66 | { |
784a4931 DH |
67 | switch (data->header & RC6_MODE_MASK) { |
68 | case 0: | |
69 | return RC6_MODE_0; | |
70 | case 6: | |
71 | if (!data->toggle) | |
72 | return RC6_MODE_6A; | |
73 | /* fall through */ | |
74 | default: | |
75 | return RC6_MODE_UNKNOWN; | |
76 | } | |
77 | } | |
78 | ||
79 | /** | |
80 | * ir_rc6_decode() - Decode one RC6 pulse or space | |
d8b4b582 | 81 | * @dev: the struct rc_dev descriptor of the device |
e40b1127 | 82 | * @ev: the struct ir_raw_event descriptor of the pulse/space |
784a4931 DH |
83 | * |
84 | * This function returns -EINVAL if the pulse violates the state machine | |
85 | */ | |
d8b4b582 | 86 | static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) |
784a4931 | 87 | { |
d8b4b582 | 88 | struct rc6_dec *data = &dev->raw->rc6; |
784a4931 DH |
89 | u32 scancode; |
90 | u8 toggle; | |
784a4931 | 91 | |
52b66144 | 92 | if (!(dev->raw->enabled_protocols & RC_TYPE_RC6)) |
784a4931 DH |
93 | return 0; |
94 | ||
4651918a ML |
95 | if (!is_timing_event(ev)) { |
96 | if (ev.reset) | |
97 | data->state = STATE_INACTIVE; | |
784a4931 DH |
98 | return 0; |
99 | } | |
100 | ||
e40b1127 | 101 | if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) |
784a4931 DH |
102 | goto out; |
103 | ||
104 | again: | |
e40b1127 DH |
105 | IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n", |
106 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | |
784a4931 | 107 | |
e40b1127 | 108 | if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) |
784a4931 DH |
109 | return 0; |
110 | ||
111 | switch (data->state) { | |
112 | ||
113 | case STATE_INACTIVE: | |
e40b1127 DH |
114 | if (!ev.pulse) |
115 | break; | |
116 | ||
117 | /* Note: larger margin on first pulse since each RC6_UNIT | |
118 | is quite short and some hardware takes some time to | |
119 | adjust to the signal */ | |
120 | if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT)) | |
121 | break; | |
122 | ||
123 | data->state = STATE_PREFIX_SPACE; | |
124 | data->count = 0; | |
125 | return 0; | |
784a4931 DH |
126 | |
127 | case STATE_PREFIX_SPACE: | |
e40b1127 DH |
128 | if (ev.pulse) |
129 | break; | |
130 | ||
131 | if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2)) | |
132 | break; | |
133 | ||
134 | data->state = STATE_HEADER_BIT_START; | |
db9bc660 | 135 | data->header = 0; |
e40b1127 | 136 | return 0; |
784a4931 DH |
137 | |
138 | case STATE_HEADER_BIT_START: | |
e40b1127 DH |
139 | if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) |
140 | break; | |
141 | ||
142 | data->header <<= 1; | |
143 | if (ev.pulse) | |
144 | data->header |= 1; | |
145 | data->count++; | |
e40b1127 DH |
146 | data->state = STATE_HEADER_BIT_END; |
147 | return 0; | |
784a4931 DH |
148 | |
149 | case STATE_HEADER_BIT_END: | |
d8b4b582 | 150 | if (!is_transition(&ev, &dev->raw->prev_ev)) |
e40b1127 | 151 | break; |
784a4931 | 152 | |
e40b1127 DH |
153 | if (data->count == RC6_HEADER_NBITS) |
154 | data->state = STATE_TOGGLE_START; | |
155 | else | |
156 | data->state = STATE_HEADER_BIT_START; | |
157 | ||
158 | decrease_duration(&ev, RC6_BIT_END); | |
159 | goto again; | |
784a4931 DH |
160 | |
161 | case STATE_TOGGLE_START: | |
e40b1127 DH |
162 | if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2)) |
163 | break; | |
164 | ||
165 | data->toggle = ev.pulse; | |
e40b1127 DH |
166 | data->state = STATE_TOGGLE_END; |
167 | return 0; | |
784a4931 DH |
168 | |
169 | case STATE_TOGGLE_END: | |
d8b4b582 | 170 | if (!is_transition(&ev, &dev->raw->prev_ev) || |
e40b1127 DH |
171 | !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) |
172 | break; | |
784a4931 | 173 | |
e40b1127 DH |
174 | if (!(data->header & RC6_STARTBIT_MASK)) { |
175 | IR_dprintk(1, "RC6 invalid start bit\n"); | |
176 | break; | |
177 | } | |
784a4931 | 178 | |
e40b1127 | 179 | data->state = STATE_BODY_BIT_START; |
e40b1127 DH |
180 | decrease_duration(&ev, RC6_TOGGLE_END); |
181 | data->count = 0; | |
db9bc660 | 182 | data->body = 0; |
e40b1127 DH |
183 | |
184 | switch (rc6_mode(data)) { | |
185 | case RC6_MODE_0: | |
186 | data->wanted_bits = RC6_0_NBITS; | |
187 | break; | |
188 | case RC6_MODE_6A: | |
db9bc660 | 189 | data->wanted_bits = RC6_6A_NBITS; |
e40b1127 DH |
190 | break; |
191 | default: | |
192 | IR_dprintk(1, "RC6 unknown mode\n"); | |
193 | goto out; | |
784a4931 | 194 | } |
e40b1127 | 195 | goto again; |
784a4931 DH |
196 | |
197 | case STATE_BODY_BIT_START: | |
db9bc660 | 198 | if (eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) { |
199 | /* Discard LSB's that won't fit in data->body */ | |
200 | if (data->count++ < CHAR_BIT * sizeof data->body) { | |
201 | data->body <<= 1; | |
202 | if (ev.pulse) | |
203 | data->body |= 1; | |
204 | } | |
205 | data->state = STATE_BODY_BIT_END; | |
206 | return 0; | |
207 | } else if (RC6_MODE_6A == rc6_mode(data) && !ev.pulse && | |
208 | geq_margin(ev.duration, RC6_SUFFIX_SPACE, RC6_UNIT / 2)) { | |
209 | data->state = STATE_FINISHED; | |
210 | goto again; | |
211 | } | |
212 | break; | |
784a4931 DH |
213 | |
214 | case STATE_BODY_BIT_END: | |
d8b4b582 | 215 | if (!is_transition(&ev, &dev->raw->prev_ev)) |
e40b1127 | 216 | break; |
784a4931 | 217 | |
e40b1127 DH |
218 | if (data->count == data->wanted_bits) |
219 | data->state = STATE_FINISHED; | |
220 | else | |
221 | data->state = STATE_BODY_BIT_START; | |
222 | ||
223 | decrease_duration(&ev, RC6_BIT_END); | |
224 | goto again; | |
784a4931 DH |
225 | |
226 | case STATE_FINISHED: | |
e40b1127 DH |
227 | if (ev.pulse) |
228 | break; | |
229 | ||
784a4931 DH |
230 | switch (rc6_mode(data)) { |
231 | case RC6_MODE_0: | |
db9bc660 | 232 | scancode = data->body; |
784a4931 DH |
233 | toggle = data->toggle; |
234 | IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", | |
235 | scancode, toggle); | |
236 | break; | |
237 | case RC6_MODE_6A: | |
db9bc660 | 238 | if (data->count > CHAR_BIT * sizeof data->body) { |
239 | IR_dprintk(1, "RC6 too many (%u) data bits\n", | |
240 | data->count); | |
241 | goto out; | |
242 | } | |
243 | ||
244 | scancode = data->body; | |
245 | if (data->count == RC6_6A_32_NBITS && | |
246 | (scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { | |
247 | /* MCE RC */ | |
248 | toggle = (scancode & RC6_6A_MCE_TOGGLE_MASK) ? 1 : 0; | |
249 | scancode &= ~RC6_6A_MCE_TOGGLE_MASK; | |
784a4931 DH |
250 | } else { |
251 | toggle = 0; | |
784a4931 | 252 | } |
784a4931 DH |
253 | IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n", |
254 | scancode, toggle); | |
255 | break; | |
256 | default: | |
257 | IR_dprintk(1, "RC6 unknown mode\n"); | |
258 | goto out; | |
259 | } | |
260 | ||
ca86674b | 261 | rc_keydown(dev, scancode, toggle); |
784a4931 DH |
262 | data->state = STATE_INACTIVE; |
263 | return 0; | |
264 | } | |
265 | ||
266 | out: | |
e40b1127 DH |
267 | IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n", |
268 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | |
784a4931 DH |
269 | data->state = STATE_INACTIVE; |
270 | return -EINVAL; | |
271 | } | |
272 | ||
784a4931 | 273 | static struct ir_raw_handler rc6_handler = { |
52b66144 | 274 | .protocols = RC_TYPE_RC6, |
784a4931 | 275 | .decode = ir_rc6_decode, |
784a4931 DH |
276 | }; |
277 | ||
278 | static int __init ir_rc6_decode_init(void) | |
279 | { | |
280 | ir_raw_handler_register(&rc6_handler); | |
281 | ||
282 | printk(KERN_INFO "IR RC6 protocol handler initialized\n"); | |
283 | return 0; | |
284 | } | |
285 | ||
286 | static void __exit ir_rc6_decode_exit(void) | |
287 | { | |
288 | ir_raw_handler_unregister(&rc6_handler); | |
289 | } | |
290 | ||
291 | module_init(ir_rc6_decode_init); | |
292 | module_exit(ir_rc6_decode_exit); | |
293 | ||
294 | MODULE_LICENSE("GPL"); | |
295 | MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); | |
296 | MODULE_DESCRIPTION("RC6 IR protocol decoder"); |