Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f4f37c8e authored by Dmitry Torokhov's avatar Dmitry Torokhov
Browse files

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: default avatarDmitry Torokhov <dtor@mail.ru>
parent 554101e3
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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;
	}
+3 −3
Original line number Diff line number Diff line
@@ -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)))
+72 −6
Original line number Diff line number Diff line
@@ -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++) \
+3 −0
Original line number Diff line number Diff line
@@ -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;

/**