diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-12-04 15:17:47 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-12-05 15:42:23 -0500 |
commit | e97f4677961f68e29bd906022ebf60a6df7f530a (patch) | |
tree | a7145fadf1b07dcc33d4b49fadd3e123b1751dd0 /drivers/media/common | |
parent | 92fda216b439932bf7511e6381bbe1d42ba98875 (diff) |
V4L/DVB (13542): ir-keytable: Allow dynamic table change
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/common')
-rw-r--r-- | drivers/media/common/ir-keytable.c | 207 |
1 files changed, 197 insertions, 10 deletions
diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c index b06f023ca760..26ce5bc2fdd5 100644 --- a/drivers/media/common/ir-keytable.c +++ b/drivers/media/common/ir-keytable.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <media/ir-common.h> | 8 | #include <media/ir-common.h> |
9 | 9 | ||
10 | #define IR_TAB_MIN_SIZE 32 | 10 | #define IR_TAB_MIN_SIZE 32 |
11 | #define IR_TAB_MAX_SIZE 1024 | ||
11 | 12 | ||
12 | /** | 13 | /** |
13 | * ir_seek_table() - returns the element order on the table | 14 | * ir_seek_table() - returns the element order on the table |
@@ -121,7 +122,154 @@ static int ir_getkeycode(struct input_dev *dev, | |||
121 | return 0; | 122 | return 0; |
122 | } | 123 | } |
123 | 124 | ||
124 | return -EINVAL; | 125 | /* |
126 | * Scancode not found and table can't be expanded | ||
127 | */ | ||
128 | if (elem < 0 && rc_tab->size == IR_TAB_MAX_SIZE) | ||
129 | return -EINVAL; | ||
130 | |||
131 | /* | ||
132 | * If is there extra space, returns KEY_RESERVED, | ||
133 | * otherwise, input core won't let ir_setkeycode to work | ||
134 | */ | ||
135 | *keycode = KEY_RESERVED; | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | |||
140 | /** | ||
141 | * ir_is_resize_needed() - Check if the table needs rezise | ||
142 | * @table: keycode table that may need to resize | ||
143 | * @n_elems: minimum number of entries to store keycodes | ||
144 | * | ||
145 | * Considering that kmalloc uses power of two storage areas, this | ||
146 | * routine detects if the real alloced size will change. If not, it | ||
147 | * just returns without doing nothing. Otherwise, it will extend or | ||
148 | * reduce the table size to meet the new needs. | ||
149 | * | ||
150 | * It returns 0 if no resize is needed, 1 otherwise. | ||
151 | */ | ||
152 | static int ir_is_resize_needed(struct ir_scancode_table *table, int n_elems) | ||
153 | { | ||
154 | int cur_size = ir_roundup_tablesize(table->size); | ||
155 | int new_size = ir_roundup_tablesize(n_elems); | ||
156 | |||
157 | if (cur_size == new_size) | ||
158 | return 0; | ||
159 | |||
160 | /* Resize is needed */ | ||
161 | return 1; | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * ir_delete_key() - remove a keycode from the table | ||
166 | * @rc_tab: keycode table | ||
167 | * @elem: element to be removed | ||
168 | * | ||
169 | */ | ||
170 | static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem) | ||
171 | { | ||
172 | unsigned long flags = 0; | ||
173 | int newsize = rc_tab->size - 1; | ||
174 | int resize = ir_is_resize_needed(rc_tab, newsize); | ||
175 | struct ir_scancode *oldkeymap = rc_tab->scan; | ||
176 | struct ir_scancode *newkeymap; | ||
177 | |||
178 | if (resize) { | ||
179 | newkeymap = kzalloc(ir_roundup_tablesize(newsize) * | ||
180 | sizeof(*newkeymap), GFP_ATOMIC); | ||
181 | |||
182 | /* There's no memory for resize. Keep the old table */ | ||
183 | if (!newkeymap) | ||
184 | resize = 0; | ||
185 | } | ||
186 | |||
187 | if (!resize) { | ||
188 | newkeymap = oldkeymap; | ||
189 | |||
190 | /* We'll modify the live table. Lock it */ | ||
191 | spin_lock_irqsave(&rc_tab->lock, flags); | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * Copy the elements before the one that will be deleted | ||
196 | * if (!resize), both oldkeymap and newkeymap points | ||
197 | * to the same place, so, there's no need to copy | ||
198 | */ | ||
199 | if (resize && elem > 0) | ||
200 | memcpy(newkeymap, oldkeymap, | ||
201 | elem * sizeof(*newkeymap)); | ||
202 | |||
203 | /* | ||
204 | * Copy the other elements overwriting the element to be removed | ||
205 | * This operation applies to both resize and non-resize case | ||
206 | */ | ||
207 | if (elem < newsize) | ||
208 | memcpy(&newkeymap[elem], &oldkeymap[elem + 1], | ||
209 | (newsize - elem) * sizeof(*newkeymap)); | ||
210 | |||
211 | if (resize) { | ||
212 | /* | ||
213 | * As the copy happened to a temporary table, only here | ||
214 | * it needs to lock while replacing the table pointers | ||
215 | * to use the new table | ||
216 | */ | ||
217 | spin_lock_irqsave(&rc_tab->lock, flags); | ||
218 | rc_tab->size = newsize; | ||
219 | rc_tab->scan = newkeymap; | ||
220 | spin_unlock_irqrestore(&rc_tab->lock, flags); | ||
221 | |||
222 | /* Frees the old keytable */ | ||
223 | kfree(oldkeymap); | ||
224 | } else { | ||
225 | rc_tab->size = newsize; | ||
226 | spin_unlock_irqrestore(&rc_tab->lock, flags); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * ir_insert_key() - insert a keycode at the table | ||
232 | * @rc_tab: keycode table | ||
233 | * @scancode: the desired scancode | ||
234 | * @keycode: the keycode to be retorned. | ||
235 | * | ||
236 | */ | ||
237 | static int ir_insert_key(struct ir_scancode_table *rc_tab, | ||
238 | int scancode, int keycode) | ||
239 | { | ||
240 | unsigned long flags; | ||
241 | int elem = rc_tab->size; | ||
242 | int newsize = rc_tab->size + 1; | ||
243 | int resize = ir_is_resize_needed(rc_tab, newsize); | ||
244 | struct ir_scancode *oldkeymap = rc_tab->scan; | ||
245 | struct ir_scancode *newkeymap; | ||
246 | |||
247 | if (resize) { | ||
248 | newkeymap = kzalloc(ir_roundup_tablesize(newsize) * | ||
249 | sizeof(*newkeymap), GFP_ATOMIC); | ||
250 | if (!newkeymap) | ||
251 | return -ENOMEM; | ||
252 | |||
253 | memcpy(newkeymap, oldkeymap, | ||
254 | rc_tab->size * sizeof(*newkeymap)); | ||
255 | } else | ||
256 | newkeymap = oldkeymap; | ||
257 | |||
258 | /* Stores the new code at the table */ | ||
259 | IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n", | ||
260 | rc_tab->size, scancode, keycode); | ||
261 | |||
262 | spin_lock_irqsave(&rc_tab->lock, flags); | ||
263 | rc_tab->size = newsize; | ||
264 | if (resize) { | ||
265 | rc_tab->scan = newkeymap; | ||
266 | kfree(oldkeymap); | ||
267 | } | ||
268 | newkeymap[elem].scancode = scancode; | ||
269 | newkeymap[elem].keycode = keycode; | ||
270 | spin_unlock_irqrestore(&rc_tab->lock, flags); | ||
271 | |||
272 | return 0; | ||
125 | } | 273 | } |
126 | 274 | ||
127 | /** | 275 | /** |
@@ -142,20 +290,59 @@ static int ir_setkeycode(struct input_dev *dev, | |||
142 | struct ir_scancode *keymap = rc_tab->scan; | 290 | struct ir_scancode *keymap = rc_tab->scan; |
143 | unsigned long flags; | 291 | unsigned long flags; |
144 | 292 | ||
145 | /* Search if it is replacing an existing keycode */ | 293 | /* |
294 | * Handle keycode table deletions | ||
295 | * | ||
296 | * If userspace is adding a KEY_UNKNOWN or KEY_RESERVED, | ||
297 | * deal as a trial to remove an existing scancode attribution | ||
298 | * if table become too big, reduce it to save space | ||
299 | */ | ||
300 | if (keycode == KEY_UNKNOWN || keycode == KEY_RESERVED) { | ||
301 | rc = ir_seek_table(rc_tab, scancode); | ||
302 | if (rc < 0) | ||
303 | return 0; | ||
304 | |||
305 | IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", rc, scancode); | ||
306 | clear_bit(keymap[rc].keycode, dev->keybit); | ||
307 | ir_delete_key(rc_tab, rc); | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * Handle keycode replacements | ||
314 | * | ||
315 | * If the scancode exists, just replace by the new value | ||
316 | */ | ||
146 | rc = ir_seek_table(rc_tab, scancode); | 317 | rc = ir_seek_table(rc_tab, scancode); |
147 | if (rc <0) | 318 | if (rc >= 0) { |
148 | return rc; | 319 | IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", |
320 | rc, scancode, keycode); | ||
149 | 321 | ||
150 | IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", | 322 | clear_bit(keymap[rc].keycode, dev->keybit); |
151 | rc, scancode, keycode); | ||
152 | 323 | ||
153 | clear_bit(keymap[rc].keycode, dev->keybit); | 324 | spin_lock_irqsave(&rc_tab->lock, flags); |
325 | keymap[rc].keycode = keycode; | ||
326 | spin_unlock_irqrestore(&rc_tab->lock, flags); | ||
154 | 327 | ||
155 | spin_lock_irqsave(&rc_tab->lock, flags); | 328 | set_bit(keycode, dev->keybit); |
156 | keymap[rc].keycode = keycode; | ||
157 | spin_unlock_irqrestore(&rc_tab->lock, flags); | ||
158 | 329 | ||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Handle new scancode inserts | ||
335 | * | ||
336 | * reallocate table if needed and insert a new keycode | ||
337 | */ | ||
338 | |||
339 | /* Avoid growing the table indefinitely */ | ||
340 | if (rc_tab->size + 1 > IR_TAB_MAX_SIZE) | ||
341 | return -EINVAL; | ||
342 | |||
343 | rc = ir_insert_key(rc_tab, scancode, keycode); | ||
344 | if (rc < 0) | ||
345 | return rc; | ||
159 | set_bit(keycode, dev->keybit); | 346 | set_bit(keycode, dev->keybit); |
160 | 347 | ||
161 | return 0; | 348 | return 0; |