Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: Add proper locking when changing device's keymap

Take dev->event_lock to make sure that we don't race with input_event() and
also force key up event when removing a key from keymap table.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

+80 -11
+2 -2
drivers/char/keyboard.c
··· 194 194 int error = -ENODEV; 195 195 196 196 list_for_each_entry(handle, &kbd_handler.h_list, h_node) { 197 - error = handle->dev->getkeycode(handle->dev, scancode, &keycode); 197 + error = input_get_keycode(handle->dev, scancode, &keycode); 198 198 if (!error) 199 199 return keycode; 200 200 } ··· 208 208 int error = -ENODEV; 209 209 210 210 list_for_each_entry(handle, &kbd_handler.h_list, h_node) { 211 - error = handle->dev->setkeycode(handle->dev, scancode, keycode); 211 + error = input_set_keycode(handle->dev, scancode, keycode); 212 212 if (!error) 213 213 break; 214 214 }
+3 -3
drivers/input/evdev.c
··· 617 617 if (get_user(t, ip)) 618 618 return -EFAULT; 619 619 620 - error = dev->getkeycode(dev, t, &v); 620 + error = input_get_keycode(dev, t, &v); 621 621 if (error) 622 622 return error; 623 623 ··· 630 630 if (get_user(t, ip) || get_user(v, ip + 1)) 631 631 return -EFAULT; 632 632 633 - return dev->setkeycode(dev, t, v); 633 + return input_set_keycode(dev, t, v); 634 634 635 635 case EVIOCSFF: 636 636 if (copy_from_user(&effect, p, sizeof(effect))) ··· 683 683 case EV_FF: bits = dev->ffbit; len = FF_MAX; break; 684 684 case EV_SW: bits = dev->swbit; len = SW_MAX; break; 685 685 default: return -EINVAL; 686 - } 686 + } 687 687 return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode); 688 688 } 689 689
+72 -6
drivers/input/input.c
··· 493 493 if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { 494 494 for (code = 0; code <= KEY_MAX; code++) { 495 495 if (is_event_supported(code, dev->keybit, KEY_MAX) && 496 - test_bit(code, dev->key)) { 496 + __test_and_clear_bit(code, dev->key)) { 497 497 input_pass_event(dev, EV_KEY, code, 0); 498 498 } 499 499 } ··· 526 526 if (!dev->keycodesize) 527 527 return -EINVAL; 528 528 529 - if (scancode < 0 || scancode >= dev->keycodemax) 529 + if (scancode >= dev->keycodemax) 530 530 return -EINVAL; 531 531 532 532 *keycode = input_fetch_keycode(dev, scancode); ··· 540 540 int old_keycode; 541 541 int i; 542 542 543 - if (scancode < 0 || scancode >= dev->keycodemax) 544 - return -EINVAL; 545 - 546 - if (keycode < 0 || keycode > KEY_MAX) 543 + if (scancode >= dev->keycodemax) 547 544 return -EINVAL; 548 545 549 546 if (!dev->keycodesize) ··· 583 586 return 0; 584 587 } 585 588 589 + /** 590 + * input_get_keycode - retrieve keycode currently mapped to a given scancode 591 + * @dev: input device which keymap is being queried 592 + * @scancode: scancode (or its equivalent for device in question) for which 593 + * keycode is needed 594 + * @keycode: result 595 + * 596 + * This function should be called by anyone interested in retrieving current 597 + * keymap. Presently keyboard and evdev handlers use it. 598 + */ 599 + int input_get_keycode(struct input_dev *dev, int scancode, int *keycode) 600 + { 601 + if (scancode < 0) 602 + return -EINVAL; 603 + 604 + return dev->getkeycode(dev, scancode, keycode); 605 + } 606 + EXPORT_SYMBOL(input_get_keycode); 607 + 608 + /** 609 + * input_get_keycode - assign new keycode to a given scancode 610 + * @dev: input device which keymap is being updated 611 + * @scancode: scancode (or its equivalent for device in question) 612 + * @keycode: new keycode to be assigned to the scancode 613 + * 614 + * This function should be called by anyone needing to update current 615 + * keymap. Presently keyboard and evdev handlers use it. 616 + */ 617 + int input_set_keycode(struct input_dev *dev, int scancode, int keycode) 618 + { 619 + unsigned long flags; 620 + int old_keycode; 621 + int retval; 622 + 623 + if (scancode < 0) 624 + return -EINVAL; 625 + 626 + if (keycode < 0 || keycode > KEY_MAX) 627 + return -EINVAL; 628 + 629 + spin_lock_irqsave(&dev->event_lock, flags); 630 + 631 + retval = dev->getkeycode(dev, scancode, &old_keycode); 632 + if (retval) 633 + goto out; 634 + 635 + retval = dev->setkeycode(dev, scancode, keycode); 636 + if (retval) 637 + goto out; 638 + 639 + /* 640 + * Simulate keyup event if keycode is not present 641 + * in the keymap anymore 642 + */ 643 + if (test_bit(EV_KEY, dev->evbit) && 644 + !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && 645 + __test_and_clear_bit(old_keycode, dev->key)) { 646 + 647 + input_pass_event(dev, EV_KEY, old_keycode, 0); 648 + if (dev->sync) 649 + input_pass_event(dev, EV_SYN, SYN_REPORT, 1); 650 + } 651 + 652 + out: 653 + spin_unlock_irqrestore(&dev->event_lock, flags); 654 + 655 + return retval; 656 + } 657 + EXPORT_SYMBOL(input_set_keycode); 586 658 587 659 #define MATCH_BIT(bit, max) \ 588 660 for (i = 0; i < BITS_TO_LONGS(max); i++) \
+3
include/linux/input.h
··· 1309 1309 dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis); 1310 1310 } 1311 1311 1312 + int input_get_keycode(struct input_dev *dev, int scancode, int *keycode); 1313 + int input_set_keycode(struct input_dev *dev, int scancode, int keycode); 1314 + 1312 1315 extern struct class input_class; 1313 1316 1314 1317 /**