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: Yong Wang <yong.y.wang@intel.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
This commit is contained in:
@@ -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,13 +67,15 @@ 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 (dev->keycode) {
|
||||||
|
key = sparse_keymap_entry_from_scancode(dev, scancode);
|
||||||
if (key && key->type == KE_KEY) {
|
if (key && key->type == KE_KEY) {
|
||||||
*keycode = key->keycode;
|
*keycode = key->keycode;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@@ -85,9 +87,7 @@ 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);
|
key = sparse_keymap_entry_from_scancode(dev, scancode);
|
||||||
if (key && key->type == KE_KEY) {
|
if (key && key->type == KE_KEY) {
|
||||||
old_keycode = key->keycode;
|
old_keycode = key->keycode;
|
||||||
@@ -97,6 +97,7 @@ static int sparse_keymap_setkeycode(struct input_dev *dev,
|
|||||||
clear_bit(old_keycode, dev->keybit);
|
clear_bit(old_keycode, dev->keybit);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user