Commit 2e2e3b96 authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: sparse-keymap - implement safer freeing of the keymap

Allow calling sparse_keymap_free() before unregistering input device
whithout risk of racing with EVIOCGETKEYCODE and EVIOCSETKEYCODE.
This makes life of drivers writers easier.
Acked-by: default avatarYong Wang <yong.y.wang@intel.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 13bad37b
...@@ -659,7 +659,14 @@ static int input_default_setkeycode(struct input_dev *dev, ...@@ -659,7 +659,14 @@ static int input_default_setkeycode(struct input_dev *dev,
int input_get_keycode(struct input_dev *dev, int input_get_keycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode) unsigned int scancode, unsigned int *keycode)
{ {
return dev->getkeycode(dev, scancode, keycode); unsigned long flags;
int retval;
spin_lock_irqsave(&dev->event_lock, flags);
retval = dev->getkeycode(dev, scancode, keycode);
spin_unlock_irqrestore(&dev->event_lock, flags);
return retval;
} }
EXPORT_SYMBOL(input_get_keycode); EXPORT_SYMBOL(input_get_keycode);
......
...@@ -67,12 +67,14 @@ static int sparse_keymap_getkeycode(struct input_dev *dev, ...@@ -67,12 +67,14 @@ static int sparse_keymap_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int scancode,
unsigned int *keycode) unsigned int *keycode)
{ {
const struct key_entry *key = const struct key_entry *key;
sparse_keymap_entry_from_scancode(dev, scancode);
if (key && key->type == KE_KEY) { if (dev->keycode) {
*keycode = key->keycode; key = sparse_keymap_entry_from_scancode(dev, scancode);
return 0; if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
} }
return -EINVAL; return -EINVAL;
...@@ -85,17 +87,16 @@ static int sparse_keymap_setkeycode(struct input_dev *dev, ...@@ -85,17 +87,16 @@ static int sparse_keymap_setkeycode(struct input_dev *dev,
struct key_entry *key; struct key_entry *key;
int old_keycode; int old_keycode;
if (keycode < 0 || keycode > KEY_MAX) if (dev->keycode) {
return -EINVAL; key = sparse_keymap_entry_from_scancode(dev, scancode);
if (key && key->type == KE_KEY) {
key = sparse_keymap_entry_from_scancode(dev, scancode); old_keycode = key->keycode;
if (key && key->type == KE_KEY) { key->keycode = keycode;
old_keycode = key->keycode; set_bit(keycode, dev->keybit);
key->keycode = keycode; if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
set_bit(keycode, dev->keybit); clear_bit(old_keycode, dev->keybit);
if (!sparse_keymap_entry_from_keycode(dev, old_keycode)) return 0;
clear_bit(old_keycode, dev->keybit); }
return 0;
} }
return -EINVAL; return -EINVAL;
...@@ -175,14 +176,27 @@ EXPORT_SYMBOL(sparse_keymap_setup); ...@@ -175,14 +176,27 @@ EXPORT_SYMBOL(sparse_keymap_setup);
* *
* This function is used to free memory allocated by sparse keymap * This function is used to free memory allocated by sparse keymap
* in an input device that was set up by sparse_keymap_setup(). * in an input device that was set up by sparse_keymap_setup().
* NOTE: It is safe to cal this function while input device is
* still registered (however the drivers should care not to try to
* use freed keymap and thus have to shut off interrups/polling
* before freeing the keymap).
*/ */
void sparse_keymap_free(struct input_dev *dev) void sparse_keymap_free(struct input_dev *dev)
{ {
unsigned long flags;
/*
* Take event lock to prevent racing with input_get_keycode()
* and input_set_keycode() if we are called while input device
* is still registered.
*/
spin_lock_irqsave(&dev->event_lock, flags);
kfree(dev->keycode); kfree(dev->keycode);
dev->keycode = NULL; dev->keycode = NULL;
dev->keycodemax = 0; dev->keycodemax = 0;
dev->getkeycode = NULL;
dev->setkeycode = NULL; spin_unlock_irqrestore(&dev->event_lock, flags);
} }
EXPORT_SYMBOL(sparse_keymap_free); EXPORT_SYMBOL(sparse_keymap_free);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment