aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/common
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2009-12-04 15:17:47 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-12-05 15:42:23 -0500
commite97f4677961f68e29bd906022ebf60a6df7f530a (patch)
treea7145fadf1b07dcc33d4b49fadd3e123b1751dd0 /drivers/media/common
parent92fda216b439932bf7511e6381bbe1d42ba98875 (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.c207
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 */
152static 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 */
170static 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 */
237static 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;