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>
This commit is contained in:
parent
554101e3e5
commit
f4f37c8ec7
4 changed files with 80 additions and 11 deletions
|
@ -194,7 +194,7 @@ int getkeycode(unsigned int scancode)
|
|||
int error = -ENODEV;
|
||||
|
||||
list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
|
||||
error = handle->dev->getkeycode(handle->dev, scancode, &keycode);
|
||||
error = input_get_keycode(handle->dev, scancode, &keycode);
|
||||
if (!error)
|
||||
return keycode;
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ int setkeycode(unsigned int scancode, unsigned int keycode)
|
|||
int error = -ENODEV;
|
||||
|
||||
list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
|
||||
error = handle->dev->setkeycode(handle->dev, scancode, keycode);
|
||||
error = input_set_keycode(handle->dev, scancode, keycode);
|
||||
if (!error)
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -617,7 +617,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
if (get_user(t, ip))
|
||||
return -EFAULT;
|
||||
|
||||
error = dev->getkeycode(dev, t, &v);
|
||||
error = input_get_keycode(dev, t, &v);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -630,7 +630,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
if (get_user(t, ip) || get_user(v, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
return dev->setkeycode(dev, t, v);
|
||||
return input_set_keycode(dev, t, v);
|
||||
|
||||
case EVIOCSFF:
|
||||
if (copy_from_user(&effect, p, sizeof(effect)))
|
||||
|
@ -683,7 +683,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
case EV_FF: bits = dev->ffbit; len = FF_MAX; break;
|
||||
case EV_SW: bits = dev->swbit; len = SW_MAX; break;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
}
|
||||
return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
|
||||
}
|
||||
|
||||
|
|
|
@ -493,7 +493,7 @@ static void input_disconnect_device(struct input_dev *dev)
|
|||
if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
|
||||
for (code = 0; code <= KEY_MAX; code++) {
|
||||
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
|
||||
test_bit(code, dev->key)) {
|
||||
__test_and_clear_bit(code, dev->key)) {
|
||||
input_pass_event(dev, EV_KEY, code, 0);
|
||||
}
|
||||
}
|
||||
|
@ -526,7 +526,7 @@ static int input_default_getkeycode(struct input_dev *dev,
|
|||
if (!dev->keycodesize)
|
||||
return -EINVAL;
|
||||
|
||||
if (scancode < 0 || scancode >= dev->keycodemax)
|
||||
if (scancode >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
*keycode = input_fetch_keycode(dev, scancode);
|
||||
|
@ -540,10 +540,7 @@ static int input_default_setkeycode(struct input_dev *dev,
|
|||
int old_keycode;
|
||||
int i;
|
||||
|
||||
if (scancode < 0 || scancode >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
if (keycode < 0 || keycode > KEY_MAX)
|
||||
if (scancode >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
if (!dev->keycodesize)
|
||||
|
@ -586,6 +583,75 @@ static int input_default_setkeycode(struct input_dev *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* input_get_keycode - retrieve keycode currently mapped to a given scancode
|
||||
* @dev: input device which keymap is being queried
|
||||
* @scancode: scancode (or its equivalent for device in question) for which
|
||||
* keycode is needed
|
||||
* @keycode: result
|
||||
*
|
||||
* This function should be called by anyone interested in retrieving current
|
||||
* keymap. Presently keyboard and evdev handlers use it.
|
||||
*/
|
||||
int input_get_keycode(struct input_dev *dev, int scancode, int *keycode)
|
||||
{
|
||||
if (scancode < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return dev->getkeycode(dev, scancode, keycode);
|
||||
}
|
||||
EXPORT_SYMBOL(input_get_keycode);
|
||||
|
||||
/**
|
||||
* input_get_keycode - assign new keycode to a given scancode
|
||||
* @dev: input device which keymap is being updated
|
||||
* @scancode: scancode (or its equivalent for device in question)
|
||||
* @keycode: new keycode to be assigned to the scancode
|
||||
*
|
||||
* This function should be called by anyone needing to update current
|
||||
* keymap. Presently keyboard and evdev handlers use it.
|
||||
*/
|
||||
int input_set_keycode(struct input_dev *dev, int scancode, int keycode)
|
||||
{
|
||||
unsigned long flags;
|
||||
int old_keycode;
|
||||
int retval;
|
||||
|
||||
if (scancode < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (keycode < 0 || keycode > KEY_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
retval = dev->getkeycode(dev, scancode, &old_keycode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
retval = dev->setkeycode(dev, scancode, keycode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Simulate keyup event if keycode is not present
|
||||
* in the keymap anymore
|
||||
*/
|
||||
if (test_bit(EV_KEY, dev->evbit) &&
|
||||
!is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
|
||||
__test_and_clear_bit(old_keycode, dev->key)) {
|
||||
|
||||
input_pass_event(dev, EV_KEY, old_keycode, 0);
|
||||
if (dev->sync)
|
||||
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(input_set_keycode);
|
||||
|
||||
#define MATCH_BIT(bit, max) \
|
||||
for (i = 0; i < BITS_TO_LONGS(max); i++) \
|
||||
|
|
|
@ -1309,6 +1309,9 @@ static inline void input_set_abs_params(struct input_dev *dev, int axis, int min
|
|||
dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
|
||||
}
|
||||
|
||||
int input_get_keycode(struct input_dev *dev, int scancode, int *keycode);
|
||||
int input_set_keycode(struct input_dev *dev, int scancode, int keycode);
|
||||
|
||||
extern struct class input_class;
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue