diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2007-11-04 00:41:12 -0400 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-01-21 01:11:06 -0500 |
| commit | f4f37c8ec7d2491c8885c890ba74254b9adfbeee (patch) | |
| tree | ecd4cb10af7fd7aa4fb02fe2a106608e9d64c3fe | |
| parent | 554101e3e5f396b987c846332863a3fcdc87b1d6 (diff) | |
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>
| -rw-r--r-- | drivers/char/keyboard.c | 4 | ||||
| -rw-r--r-- | drivers/input/evdev.c | 6 | ||||
| -rw-r--r-- | drivers/input/input.c | 78 | ||||
| -rw-r--r-- | include/linux/input.h | 3 |
4 files changed, 80 insertions, 11 deletions
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index fc54d234507a..5218d0d0511e 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c | |||
| @@ -194,7 +194,7 @@ int getkeycode(unsigned int scancode) | |||
| 194 | int error = -ENODEV; | 194 | int error = -ENODEV; |
| 195 | 195 | ||
| 196 | list_for_each_entry(handle, &kbd_handler.h_list, h_node) { | 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 | if (!error) | 198 | if (!error) |
| 199 | return keycode; | 199 | return keycode; |
| 200 | } | 200 | } |
| @@ -208,7 +208,7 @@ int setkeycode(unsigned int scancode, unsigned int keycode) | |||
| 208 | int error = -ENODEV; | 208 | int error = -ENODEV; |
| 209 | 209 | ||
| 210 | list_for_each_entry(handle, &kbd_handler.h_list, h_node) { | 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 | if (!error) | 212 | if (!error) |
| 213 | break; | 213 | break; |
| 214 | } | 214 | } |
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index e5b4e9bfbdc5..0727b0a12557 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c | |||
| @@ -617,7 +617,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 617 | if (get_user(t, ip)) | 617 | if (get_user(t, ip)) |
| 618 | return -EFAULT; | 618 | return -EFAULT; |
| 619 | 619 | ||
| 620 | error = dev->getkeycode(dev, t, &v); | 620 | error = input_get_keycode(dev, t, &v); |
| 621 | if (error) | 621 | if (error) |
| 622 | return error; | 622 | return error; |
| 623 | 623 | ||
| @@ -630,7 +630,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 630 | if (get_user(t, ip) || get_user(v, ip + 1)) | 630 | if (get_user(t, ip) || get_user(v, ip + 1)) |
| 631 | return -EFAULT; | 631 | return -EFAULT; |
| 632 | 632 | ||
| 633 | return dev->setkeycode(dev, t, v); | 633 | return input_set_keycode(dev, t, v); |
| 634 | 634 | ||
| 635 | case EVIOCSFF: | 635 | case EVIOCSFF: |
| 636 | if (copy_from_user(&effect, p, sizeof(effect))) | 636 | if (copy_from_user(&effect, p, sizeof(effect))) |
| @@ -683,7 +683,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 683 | case EV_FF: bits = dev->ffbit; len = FF_MAX; break; | 683 | case EV_FF: bits = dev->ffbit; len = FF_MAX; break; |
| 684 | case EV_SW: bits = dev->swbit; len = SW_MAX; break; | 684 | case EV_SW: bits = dev->swbit; len = SW_MAX; break; |
| 685 | default: return -EINVAL; | 685 | default: return -EINVAL; |
| 686 | } | 686 | } |
| 687 | return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode); | 687 | return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode); |
| 688 | } | 688 | } |
| 689 | 689 | ||
diff --git a/drivers/input/input.c b/drivers/input/input.c index a0be978501ff..e1729e1dd9b2 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c | |||
| @@ -493,7 +493,7 @@ static void input_disconnect_device(struct input_dev *dev) | |||
| 493 | if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { | 493 | if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { |
| 494 | for (code = 0; code <= KEY_MAX; code++) { | 494 | for (code = 0; code <= KEY_MAX; code++) { |
| 495 | if (is_event_supported(code, dev->keybit, KEY_MAX) && | 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 | input_pass_event(dev, EV_KEY, code, 0); | 497 | input_pass_event(dev, EV_KEY, code, 0); |
| 498 | } | 498 | } |
| 499 | } | 499 | } |
| @@ -526,7 +526,7 @@ static int input_default_getkeycode(struct input_dev *dev, | |||
| 526 | if (!dev->keycodesize) | 526 | if (!dev->keycodesize) |
| 527 | return -EINVAL; | 527 | return -EINVAL; |
| 528 | 528 | ||
| 529 | if (scancode < 0 || scancode >= dev->keycodemax) | 529 | if (scancode >= dev->keycodemax) |
| 530 | return -EINVAL; | 530 | return -EINVAL; |
| 531 | 531 | ||
| 532 | *keycode = input_fetch_keycode(dev, scancode); | 532 | *keycode = input_fetch_keycode(dev, scancode); |
| @@ -540,10 +540,7 @@ static int input_default_setkeycode(struct input_dev *dev, | |||
| 540 | int old_keycode; | 540 | int old_keycode; |
| 541 | int i; | 541 | int i; |
| 542 | 542 | ||
| 543 | if (scancode < 0 || scancode >= dev->keycodemax) | 543 | if (scancode >= dev->keycodemax) |
| 544 | return -EINVAL; | ||
| 545 | |||
| 546 | if (keycode < 0 || keycode > KEY_MAX) | ||
| 547 | return -EINVAL; | 544 | return -EINVAL; |
| 548 | 545 | ||
| 549 | if (!dev->keycodesize) | 546 | if (!dev->keycodesize) |
| @@ -586,6 +583,75 @@ static int input_default_setkeycode(struct input_dev *dev, | |||
| 586 | return 0; | 583 | return 0; |
| 587 | } | 584 | } |
| 588 | 585 | ||
| 586 | /** | ||
| 587 | * input_get_keycode - retrieve keycode currently mapped to a given scancode | ||
| 588 | * @dev: input device which keymap is being queried | ||
| 589 | * @scancode: scancode (or its equivalent for device in question) for which | ||
| 590 | * keycode is needed | ||
| 591 | * @keycode: result | ||
| 592 | * | ||
| 593 | * This function should be called by anyone interested in retrieving current | ||
| 594 | * keymap. Presently keyboard and evdev handlers use it. | ||
| 595 | */ | ||
| 596 | int input_get_keycode(struct input_dev *dev, int scancode, int *keycode) | ||
| 597 | { | ||
| 598 | if (scancode < 0) | ||
| 599 | return -EINVAL; | ||
| 600 | |||
| 601 | return dev->getkeycode(dev, scancode, keycode); | ||
| 602 | } | ||
| 603 | EXPORT_SYMBOL(input_get_keycode); | ||
| 604 | |||
| 605 | /** | ||
| 606 | * input_get_keycode - assign new keycode to a given scancode | ||
| 607 | * @dev: input device which keymap is being updated | ||
| 608 | * @scancode: scancode (or its equivalent for device in question) | ||
| 609 | * @keycode: new keycode to be assigned to the scancode | ||
| 610 | * | ||
| 611 | * This function should be called by anyone needing to update current | ||
| 612 | * keymap. Presently keyboard and evdev handlers use it. | ||
| 613 | */ | ||
| 614 | int input_set_keycode(struct input_dev *dev, int scancode, int keycode) | ||
| 615 | { | ||
| 616 | unsigned long flags; | ||
| 617 | int old_keycode; | ||
| 618 | int retval; | ||
| 619 | |||
| 620 | if (scancode < 0) | ||
| 621 | return -EINVAL; | ||
| 622 | |||
| 623 | if (keycode < 0 || keycode > KEY_MAX) | ||
| 624 | return -EINVAL; | ||
| 625 | |||
| 626 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 627 | |||
| 628 | retval = dev->getkeycode(dev, scancode, &old_keycode); | ||
| 629 | if (retval) | ||
| 630 | goto out; | ||
| 631 | |||
| 632 | retval = dev->setkeycode(dev, scancode, keycode); | ||
| 633 | if (retval) | ||
| 634 | goto out; | ||
| 635 | |||
| 636 | /* | ||
| 637 | * Simulate keyup event if keycode is not present | ||
| 638 | * in the keymap anymore | ||
| 639 | */ | ||
| 640 | if (test_bit(EV_KEY, dev->evbit) && | ||
| 641 | !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && | ||
| 642 | __test_and_clear_bit(old_keycode, dev->key)) { | ||
| 643 | |||
| 644 | input_pass_event(dev, EV_KEY, old_keycode, 0); | ||
| 645 | if (dev->sync) | ||
| 646 | input_pass_event(dev, EV_SYN, SYN_REPORT, 1); | ||
| 647 | } | ||
| 648 | |||
| 649 | out: | ||
| 650 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 651 | |||
| 652 | return retval; | ||
| 653 | } | ||
| 654 | EXPORT_SYMBOL(input_set_keycode); | ||
| 589 | 655 | ||
| 590 | #define MATCH_BIT(bit, max) \ | 656 | #define MATCH_BIT(bit, max) \ |
| 591 | for (i = 0; i < BITS_TO_LONGS(max); i++) \ | 657 | for (i = 0; i < BITS_TO_LONGS(max); i++) \ |
diff --git a/include/linux/input.h b/include/linux/input.h index 2075d6da2a31..9a963fe97300 100644 --- a/include/linux/input.h +++ b/include/linux/input.h | |||
| @@ -1309,6 +1309,9 @@ static inline void input_set_abs_params(struct input_dev *dev, int axis, int min | |||
| 1309 | dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis); | 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 | extern struct class input_class; | 1315 | extern struct class input_class; |
| 1313 | 1316 | ||
| 1314 | /** | 1317 | /** |
