diff options
| -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; |
