]>
Commit | Line | Data |
---|---|---|
995187be | 1 | /* ir-nec-decoder.c - handle NEC IR Pulse/Space protocol |
a3572c34 MCC |
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 | #include <media/ir-core.h> | |
16 | ||
9f154782 MCC |
17 | /* Start time: 4.5 ms + 560 us of the next pulse */ |
18 | #define MIN_START_TIME (3900000 + 560000) | |
19 | #define MAX_START_TIME (5100000 + 560000) | |
a3572c34 | 20 | |
9f154782 MCC |
21 | /* Bit 1 time: 2.25ms us */ |
22 | #define MIN_BIT1_TIME 2050000 | |
23 | #define MAX_BIT1_TIME 2450000 | |
a3572c34 | 24 | |
9f154782 MCC |
25 | /* Bit 0 time: 1.12ms us */ |
26 | #define MIN_BIT0_TIME 920000 | |
27 | #define MAX_BIT0_TIME 1320000 | |
a3572c34 | 28 | |
9f154782 MCC |
29 | /* Total IR code is 110 ms, including the 9 ms for the start pulse */ |
30 | #define MAX_NEC_TIME 4000000 | |
31 | ||
32 | /* Total IR code is 110 ms, including the 9 ms for the start pulse */ | |
33 | #define MIN_REPEAT_TIME 99000000 | |
34 | #define MAX_REPEAT_TIME 112000000 | |
35 | ||
36 | /* Repeat time: 2.25ms us */ | |
37 | #define MIN_REPEAT_START_TIME 2050000 | |
38 | #define MAX_REPEAT_START_TIME 3000000 | |
39 | ||
40 | #define REPEAT_TIME 240 /* ms */ | |
41 | ||
42 | /** is_repeat - Check if it is a NEC repeat event | |
43 | * @input_dev: the struct input_dev descriptor of the device | |
44 | * @pos: the position of the first event | |
45 | * @len: the length of the buffer | |
46 | */ | |
47 | static int is_repeat(struct ir_raw_event *evs, int len, int pos) | |
48 | { | |
49 | if ((evs[pos].delta.tv_nsec < MIN_REPEAT_START_TIME) || | |
50 | (evs[pos].delta.tv_nsec > MAX_REPEAT_START_TIME)) | |
51 | return 0; | |
52 | ||
53 | if (++pos >= len) | |
54 | return 0; | |
55 | ||
56 | if ((evs[pos].delta.tv_nsec < MIN_REPEAT_TIME) || | |
57 | (evs[pos].delta.tv_nsec > MAX_REPEAT_TIME)) | |
58 | return 0; | |
59 | ||
60 | return 1; | |
61 | } | |
a3572c34 | 62 | |
ada39630 MCC |
63 | /** |
64 | * __ir_nec_decode() - Decode one NEC pulsecode | |
65 | * @input_dev: the struct input_dev descriptor of the device | |
66 | * @evs: event array with type/duration of pulse/space | |
67 | * @len: length of the array | |
68 | * @pos: position to start seeking for a code | |
9f154782 MCC |
69 | * This function returns -EINVAL if no pulse got decoded, |
70 | * 0 if buffer is empty and 1 if one keycode were handled. | |
ada39630 MCC |
71 | */ |
72 | static int __ir_nec_decode(struct input_dev *input_dev, | |
73 | struct ir_raw_event *evs, | |
74 | int len, int *pos) | |
a3572c34 | 75 | { |
9f154782 | 76 | struct ir_input_dev *ir = input_get_drvdata(input_dev); |
ada39630 | 77 | int count = -1; |
a3572c34 | 78 | int ircode = 0, not_code = 0; |
a3572c34 MCC |
79 | |
80 | /* Be sure that the first event is an start one and is a pulse */ | |
ada39630 | 81 | for (; *pos < len; (*pos)++) { |
9f154782 MCC |
82 | /* Very long delays are considered as start events */ |
83 | if (evs[*pos].delta.tv_nsec > MAX_NEC_TIME) | |
a3572c34 | 84 | break; |
9f154782 MCC |
85 | if (evs[*pos].type & IR_START_EVENT) |
86 | break; | |
87 | IR_dprintk(1, "%luus: Spurious NEC %s\n", | |
88 | (evs[*pos].delta.tv_nsec + 500) / 1000, | |
89 | (evs[*pos].type & IR_SPACE) ? "space" : "pulse"); | |
a3572c34 | 90 | |
9f154782 | 91 | } |
ada39630 | 92 | if (*pos >= len) |
a3572c34 MCC |
93 | return 0; |
94 | ||
9f154782 MCC |
95 | (*pos)++; /* First event doesn't contain data */ |
96 | ||
97 | if (evs[*pos].type != IR_PULSE) | |
a3572c34 MCC |
98 | goto err; |
99 | ||
9f154782 MCC |
100 | /* Check if it is a NEC repeat event */ |
101 | if (is_repeat(evs, len, *pos)) { | |
102 | *pos += 2; | |
103 | if (ir->keypressed) { | |
104 | mod_timer(&ir->raw->timer_keyup, | |
105 | jiffies + msecs_to_jiffies(REPEAT_TIME)); | |
106 | IR_dprintk(1, "NEC repeat event\n"); | |
107 | return 1; | |
108 | } else { | |
109 | IR_dprintk(1, "missing NEC repeat event\n"); | |
110 | return 0; | |
111 | } | |
112 | } | |
113 | ||
114 | /* First space should have 4.5 ms otherwise is not NEC protocol */ | |
115 | if ((evs[*pos].delta.tv_nsec < MIN_START_TIME) || | |
116 | (evs[*pos].delta.tv_nsec > MAX_START_TIME)) | |
117 | goto err; | |
a3572c34 MCC |
118 | |
119 | count = 0; | |
ada39630 | 120 | for ((*pos)++; *pos < len; (*pos)++) { |
a3572c34 | 121 | int bit; |
ada39630 MCC |
122 | if ((evs[*pos].delta.tv_nsec > MIN_BIT1_TIME) && |
123 | (evs[*pos].delta.tv_nsec < MAX_BIT1_TIME)) | |
a3572c34 | 124 | bit = 1; |
ada39630 MCC |
125 | else if ((evs[*pos].delta.tv_nsec > MIN_BIT0_TIME) && |
126 | (evs[*pos].delta.tv_nsec < MAX_BIT0_TIME)) | |
a3572c34 MCC |
127 | bit = 0; |
128 | else | |
129 | goto err; | |
130 | ||
131 | if (bit) { | |
132 | int shift = count; | |
133 | /* Address first, then command */ | |
134 | if (shift < 8) { | |
135 | shift += 8; | |
136 | ircode |= 1 << shift; | |
137 | } else if (shift < 16) { | |
138 | not_code |= 1 << shift; | |
139 | } else if (shift < 24) { | |
140 | shift -= 16; | |
141 | ircode |= 1 << shift; | |
142 | } else { | |
143 | shift -= 24; | |
144 | not_code |= 1 << shift; | |
145 | } | |
146 | } | |
147 | if (++count == 32) | |
148 | break; | |
149 | } | |
995187be | 150 | (*pos)++; |
a3572c34 MCC |
151 | |
152 | /* | |
153 | * Fixme: may need to accept Extended NEC protocol? | |
154 | */ | |
155 | if ((ircode & ~not_code) != ircode) { | |
156 | IR_dprintk(1, "NEC checksum error: code 0x%04x, not-code 0x%04x\n", | |
157 | ircode, not_code); | |
158 | return -EINVAL; | |
159 | } | |
160 | ||
161 | IR_dprintk(1, "NEC scancode 0x%04x\n", ircode); | |
ada39630 | 162 | ir_keydown(input_dev, ircode); |
9f154782 MCC |
163 | mod_timer(&ir->raw->timer_keyup, |
164 | jiffies + msecs_to_jiffies(REPEAT_TIME)); | |
a3572c34 | 165 | |
9f154782 | 166 | return 1; |
a3572c34 | 167 | err: |
9f154782 MCC |
168 | IR_dprintk(1, "NEC decoded failed at bit %d (%s) while decoding %luus time\n", |
169 | count, | |
170 | (evs[*pos].type & IR_SPACE) ? "space" : "pulse", | |
171 | (evs[*pos].delta.tv_nsec + 500) / 1000); | |
a3572c34 MCC |
172 | |
173 | return -EINVAL; | |
174 | } | |
ada39630 MCC |
175 | |
176 | /** | |
177 | * __ir_nec_decode() - Decodes all NEC pulsecodes on a given array | |
178 | * @input_dev: the struct input_dev descriptor of the device | |
179 | * @evs: event array with type/duration of pulse/space | |
180 | * @len: length of the array | |
181 | * This function returns the number of decoded pulses or -EINVAL if no | |
182 | * pulse got decoded | |
183 | */ | |
995187be MCC |
184 | static int ir_nec_decode(struct input_dev *input_dev, |
185 | struct ir_raw_event *evs, | |
186 | int len) | |
ada39630 MCC |
187 | { |
188 | int pos = 0; | |
189 | int rc = 0; | |
190 | ||
191 | while (pos < len) { | |
9f154782 | 192 | if (__ir_nec_decode(input_dev, evs, len, &pos) > 0) |
ada39630 MCC |
193 | rc++; |
194 | } | |
195 | ||
196 | if (!rc) | |
197 | return -EINVAL; | |
198 | return rc; | |
199 | } | |
200 | ||
995187be MCC |
201 | static struct ir_raw_handler nec_handler = { |
202 | .decode = ir_nec_decode, | |
203 | }; | |
204 | ||
205 | static int __init ir_nec_decode_init(void) | |
206 | { | |
207 | ir_raw_handler_register(&nec_handler); | |
208 | ||
209 | printk(KERN_INFO "IR NEC protocol handler initialized\n"); | |
210 | return 0; | |
211 | } | |
212 | ||
213 | static void __exit ir_nec_decode_exit(void) | |
214 | { | |
215 | ir_raw_handler_unregister(&nec_handler); | |
216 | } | |
217 | ||
218 | module_init(ir_nec_decode_init); | |
219 | module_exit(ir_nec_decode_exit); | |
220 | ||
221 | MODULE_LICENSE("GPL"); | |
222 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); | |
223 | MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); | |
224 | MODULE_DESCRIPTION("NEC IR protocol decoder"); |