diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-03-22 01:56:15 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-03-22 02:00:51 -0400 |
commit | 2e2e3b96d98d5c17e9c09bc6088df3e182a71814 (patch) | |
tree | 0ed49ca3ee60a9deb465d8aa4894365bd8d3030d /drivers/input/sparse-keymap.c | |
parent | 13bad37b04c779d98983307a27f97e9caa36f9b1 (diff) |
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>
Diffstat (limited to 'drivers/input/sparse-keymap.c')
-rw-r--r-- | drivers/input/sparse-keymap.c | 50 |
1 files changed, 32 insertions, 18 deletions
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index f64e004935a9..2434ac5d43fe 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c | |||
@@ -67,12 +67,14 @@ static int sparse_keymap_getkeycode(struct input_dev *dev, | |||
67 | unsigned int scancode, | 67 | unsigned int scancode, |
68 | unsigned int *keycode) | 68 | unsigned int *keycode) |
69 | { | 69 | { |
70 | const struct key_entry *key = | 70 | const struct key_entry *key; |
71 | sparse_keymap_entry_from_scancode(dev, scancode); | ||
72 | 71 | ||
73 | if (key && key->type == KE_KEY) { | 72 | if (dev->keycode) { |
74 | *keycode = key->keycode; | 73 | key = sparse_keymap_entry_from_scancode(dev, scancode); |
75 | return 0; | 74 | if (key && key->type == KE_KEY) { |
75 | *keycode = key->keycode; | ||
76 | return 0; | ||
77 | } | ||
76 | } | 78 | } |
77 | 79 | ||
78 | return -EINVAL; | 80 | return -EINVAL; |
@@ -85,17 +87,16 @@ static int sparse_keymap_setkeycode(struct input_dev *dev, | |||
85 | struct key_entry *key; | 87 | struct key_entry *key; |
86 | int old_keycode; | 88 | int old_keycode; |
87 | 89 | ||
88 | if (keycode < 0 || keycode > KEY_MAX) | 90 | if (dev->keycode) { |
89 | return -EINVAL; | 91 | key = sparse_keymap_entry_from_scancode(dev, scancode); |
90 | 92 | if (key && key->type == KE_KEY) { | |
91 | key = sparse_keymap_entry_from_scancode(dev, scancode); | 93 | old_keycode = key->keycode; |
92 | if (key && key->type == KE_KEY) { | 94 | key->keycode = keycode; |
93 | old_keycode = key->keycode; | 95 | set_bit(keycode, dev->keybit); |
94 | key->keycode = keycode; | 96 | if (!sparse_keymap_entry_from_keycode(dev, old_keycode)) |
95 | set_bit(keycode, dev->keybit); | 97 | clear_bit(old_keycode, dev->keybit); |
96 | if (!sparse_keymap_entry_from_keycode(dev, old_keycode)) | 98 | return 0; |
97 | clear_bit(old_keycode, dev->keybit); | 99 | } |
98 | return 0; | ||
99 | } | 100 | } |
100 | 101 | ||
101 | return -EINVAL; | 102 | return -EINVAL; |
@@ -175,14 +176,27 @@ EXPORT_SYMBOL(sparse_keymap_setup); | |||
175 | * | 176 | * |
176 | * This function is used to free memory allocated by sparse keymap | 177 | * This function is used to free memory allocated by sparse keymap |
177 | * in an input device that was set up by sparse_keymap_setup(). | 178 | * in an input device that was set up by sparse_keymap_setup(). |
179 | * NOTE: It is safe to cal this function while input device is | ||
180 | * still registered (however the drivers should care not to try to | ||
181 | * use freed keymap and thus have to shut off interrups/polling | ||
182 | * before freeing the keymap). | ||
178 | */ | 183 | */ |
179 | void sparse_keymap_free(struct input_dev *dev) | 184 | void sparse_keymap_free(struct input_dev *dev) |
180 | { | 185 | { |
186 | unsigned long flags; | ||
187 | |||
188 | /* | ||
189 | * Take event lock to prevent racing with input_get_keycode() | ||
190 | * and input_set_keycode() if we are called while input device | ||
191 | * is still registered. | ||
192 | */ | ||
193 | spin_lock_irqsave(&dev->event_lock, flags); | ||
194 | |||
181 | kfree(dev->keycode); | 195 | kfree(dev->keycode); |
182 | dev->keycode = NULL; | 196 | dev->keycode = NULL; |
183 | dev->keycodemax = 0; | 197 | dev->keycodemax = 0; |
184 | dev->getkeycode = NULL; | 198 | |
185 | dev->setkeycode = NULL; | 199 | spin_unlock_irqrestore(&dev->event_lock, flags); |
186 | } | 200 | } |
187 | EXPORT_SYMBOL(sparse_keymap_free); | 201 | EXPORT_SYMBOL(sparse_keymap_free); |
188 | 202 | ||