Skip to content

Commit

Permalink
Input: sparse-keymap - implement safer freeing of the keymap
Browse files Browse the repository at this point in the history
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: Yong Wang <[email protected]>
Signed-off-by: Dmitry Torokhov <[email protected]>
  • Loading branch information
dtor committed Mar 22, 2010
1 parent 13bad37 commit 2e2e3b9
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 19 deletions.
9 changes: 8 additions & 1 deletion drivers/input/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,14 @@ static int input_default_setkeycode(struct input_dev *dev,
int input_get_keycode(struct input_dev *dev,
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);

Expand Down
50 changes: 32 additions & 18 deletions drivers/input/sparse-keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,14 @@ static int sparse_keymap_getkeycode(struct input_dev *dev,
unsigned int scancode,
unsigned int *keycode)
{
const struct key_entry *key =
sparse_keymap_entry_from_scancode(dev, scancode);
const struct key_entry *key;

if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
if (dev->keycode) {
key = sparse_keymap_entry_from_scancode(dev, scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
}

return -EINVAL;
Expand All @@ -85,17 +87,16 @@ static int sparse_keymap_setkeycode(struct input_dev *dev,
struct key_entry *key;
int old_keycode;

if (keycode < 0 || keycode > KEY_MAX)
return -EINVAL;

key = sparse_keymap_entry_from_scancode(dev, scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
if (dev->keycode) {
key = sparse_keymap_entry_from_scancode(dev, scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
}

return -EINVAL;
Expand Down Expand Up @@ -175,14 +176,27 @@ EXPORT_SYMBOL(sparse_keymap_setup);
*
* This function is used to free memory allocated by sparse keymap
* 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)
{
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);
dev->keycode = NULL;
dev->keycodemax = 0;
dev->getkeycode = NULL;
dev->setkeycode = NULL;

spin_unlock_irqrestore(&dev->event_lock, flags);
}
EXPORT_SYMBOL(sparse_keymap_free);

Expand Down

0 comments on commit 2e2e3b9

Please sign in to comment.