aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2009-12-02 00:54:35 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-12-02 00:57:48 -0500
commit66d2a5952eab875f1286e04f738ef029afdaf013 (patch)
tree6d30e807108ef7d2a56ec43271c45acc357df699 /drivers/char
parent6ee88d713fb75ab191515f66edffa4e866386565 (diff)
Input: keyboard - fix lack of locking when traversing handler->h_list
Keyboard handler should not attempt to traverse handler->h_list on its own, without any locking, otherwise it races with registering and unregistering of input handles which leads to crashes. Introduce input_handler_for_each_handle() helper that allows safely iterate over all handles attached to a particular handler and switch keyboard handler to use it. Reported-by: Jim Paradis <jparadis@redhat.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/keyboard.c202
1 files changed, 105 insertions, 97 deletions
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index ca090e57e161..ff8e9345f3c9 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -46,8 +46,6 @@
46 46
47extern void ctrl_alt_del(void); 47extern void ctrl_alt_del(void);
48 48
49#define to_handle_h(n) container_of(n, struct input_handle, h_node)
50
51/* 49/*
52 * Exported functions/variables 50 * Exported functions/variables
53 */ 51 */
@@ -191,78 +189,85 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
191 * etc.). So this means that scancodes for the extra function keys won't 189 * etc.). So this means that scancodes for the extra function keys won't
192 * be valid for the first event device, but will be for the second. 190 * be valid for the first event device, but will be for the second.
193 */ 191 */
192
193struct getset_keycode_data {
194 unsigned int scancode;
195 unsigned int keycode;
196 int error;
197};
198
199static int getkeycode_helper(struct input_handle *handle, void *data)
200{
201 struct getset_keycode_data *d = data;
202
203 d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode);
204
205 return d->error == 0; /* stop as soon as we successfully get one */
206}
207
194int getkeycode(unsigned int scancode) 208int getkeycode(unsigned int scancode)
195{ 209{
196 struct input_handle *handle; 210 struct getset_keycode_data d = { scancode, 0, -ENODEV };
197 int keycode;
198 int error = -ENODEV;
199 211
200 list_for_each_entry(handle, &kbd_handler.h_list, h_node) { 212 input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
201 error = input_get_keycode(handle->dev, scancode, &keycode);
202 if (!error)
203 return keycode;
204 }
205 213
206 return error; 214 return d.error ?: d.keycode;
215}
216
217static int setkeycode_helper(struct input_handle *handle, void *data)
218{
219 struct getset_keycode_data *d = data;
220
221 d->error = input_set_keycode(handle->dev, d->scancode, d->keycode);
222
223 return d->error == 0; /* stop as soon as we successfully set one */
207} 224}
208 225
209int setkeycode(unsigned int scancode, unsigned int keycode) 226int setkeycode(unsigned int scancode, unsigned int keycode)
210{ 227{
211 struct input_handle *handle; 228 struct getset_keycode_data d = { scancode, keycode, -ENODEV };
212 int error = -ENODEV;
213 229
214 list_for_each_entry(handle, &kbd_handler.h_list, h_node) { 230 input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
215 error = input_set_keycode(handle->dev, scancode, keycode);
216 if (!error)
217 break;
218 }
219 231
220 return error; 232 return d.error;
221} 233}
222 234
223/* 235/*
224 * Making beeps and bells. 236 * Making beeps and bells.
225 */ 237 */
226static void kd_nosound(unsigned long ignored) 238
239static int kd_sound_helper(struct input_handle *handle, void *data)
227{ 240{
228 struct input_handle *handle; 241 unsigned int *hz = data;
242 struct input_dev *dev = handle->dev;
229 243
230 list_for_each_entry(handle, &kbd_handler.h_list, h_node) { 244 if (test_bit(EV_SND, dev->evbit)) {
231 if (test_bit(EV_SND, handle->dev->evbit)) { 245 if (test_bit(SND_TONE, dev->sndbit))
232 if (test_bit(SND_TONE, handle->dev->sndbit)) 246 input_inject_event(handle, EV_SND, SND_TONE, *hz);
233 input_inject_event(handle, EV_SND, SND_TONE, 0); 247 if (test_bit(SND_BELL, handle->dev->sndbit))
234 if (test_bit(SND_BELL, handle->dev->sndbit)) 248 input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0);
235 input_inject_event(handle, EV_SND, SND_BELL, 0);
236 }
237 } 249 }
250
251 return 0;
252}
253
254static void kd_nosound(unsigned long ignored)
255{
256 static unsigned int zero;
257
258 input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper);
238} 259}
239 260
240static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0); 261static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0);
241 262
242void kd_mksound(unsigned int hz, unsigned int ticks) 263void kd_mksound(unsigned int hz, unsigned int ticks)
243{ 264{
244 struct list_head *node; 265 del_timer_sync(&kd_mksound_timer);
245 266
246 del_timer(&kd_mksound_timer); 267 input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper);
247 268
248 if (hz) { 269 if (hz && ticks)
249 list_for_each_prev(node, &kbd_handler.h_list) { 270 mod_timer(&kd_mksound_timer, jiffies + ticks);
250 struct input_handle *handle = to_handle_h(node);
251 if (test_bit(EV_SND, handle->dev->evbit)) {
252 if (test_bit(SND_TONE, handle->dev->sndbit)) {
253 input_inject_event(handle, EV_SND, SND_TONE, hz);
254 break;
255 }
256 if (test_bit(SND_BELL, handle->dev->sndbit)) {
257 input_inject_event(handle, EV_SND, SND_BELL, 1);
258 break;
259 }
260 }
261 }
262 if (ticks)
263 mod_timer(&kd_mksound_timer, jiffies + ticks);
264 } else
265 kd_nosound(0);
266} 271}
267EXPORT_SYMBOL(kd_mksound); 272EXPORT_SYMBOL(kd_mksound);
268 273
@@ -270,27 +275,34 @@ EXPORT_SYMBOL(kd_mksound);
270 * Setting the keyboard rate. 275 * Setting the keyboard rate.
271 */ 276 */
272 277
273int kbd_rate(struct kbd_repeat *rep) 278static int kbd_rate_helper(struct input_handle *handle, void *data)
274{ 279{
275 struct list_head *node; 280 struct input_dev *dev = handle->dev;
276 unsigned int d = 0; 281 struct kbd_repeat *rep = data;
277 unsigned int p = 0; 282
278 283 if (test_bit(EV_REP, dev->evbit)) {
279 list_for_each(node, &kbd_handler.h_list) { 284
280 struct input_handle *handle = to_handle_h(node); 285 if (rep[0].delay > 0)
281 struct input_dev *dev = handle->dev; 286 input_inject_event(handle,
282 287 EV_REP, REP_DELAY, rep[0].delay);
283 if (test_bit(EV_REP, dev->evbit)) { 288 if (rep[0].period > 0)
284 if (rep->delay > 0) 289 input_inject_event(handle,
285 input_inject_event(handle, EV_REP, REP_DELAY, rep->delay); 290 EV_REP, REP_PERIOD, rep[0].period);
286 if (rep->period > 0) 291
287 input_inject_event(handle, EV_REP, REP_PERIOD, rep->period); 292 rep[1].delay = dev->rep[REP_DELAY];
288 d = dev->rep[REP_DELAY]; 293 rep[1].period = dev->rep[REP_PERIOD];
289 p = dev->rep[REP_PERIOD];
290 }
291 } 294 }
292 rep->delay = d; 295
293 rep->period = p; 296 return 0;
297}
298
299int kbd_rate(struct kbd_repeat *rep)
300{
301 struct kbd_repeat data[2] = { *rep };
302
303 input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper);
304 *rep = data[1]; /* Copy currently used settings */
305
294 return 0; 306 return 0;
295} 307}
296 308
@@ -998,36 +1010,36 @@ static inline unsigned char getleds(void)
998 return leds; 1010 return leds;
999} 1011}
1000 1012
1013static int kbd_update_leds_helper(struct input_handle *handle, void *data)
1014{
1015 unsigned char leds = *(unsigned char *)data;
1016
1017 if (test_bit(EV_LED, handle->dev->evbit)) {
1018 input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
1019 input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
1020 input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
1021 input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
1022 }
1023
1024 return 0;
1025}
1026
1001/* 1027/*
1002 * This routine is the bottom half of the keyboard interrupt 1028 * This is the tasklet that updates LED state on all keyboards
1003 * routine, and runs with all interrupts enabled. It does 1029 * attached to the box. The reason we use tasklet is that we
1004 * console changing, led setting and copy_to_cooked, which can 1030 * need to handle the scenario when keyboard handler is not
1005 * take a reasonably long time. 1031 * registered yet but we already getting updates form VT to
1006 * 1032 * update led state.
1007 * Aside from timing (which isn't really that important for
1008 * keyboard interrupts as they happen often), using the software
1009 * interrupt routines for this thing allows us to easily mask
1010 * this when we don't want any of the above to happen.
1011 * This allows for easy and efficient race-condition prevention
1012 * for kbd_start => input_inject_event(dev, EV_LED, ...) => ...
1013 */ 1033 */
1014
1015static void kbd_bh(unsigned long dummy) 1034static void kbd_bh(unsigned long dummy)
1016{ 1035{
1017 struct list_head *node;
1018 unsigned char leds = getleds(); 1036 unsigned char leds = getleds();
1019 1037
1020 if (leds != ledstate) { 1038 if (leds != ledstate) {
1021 list_for_each(node, &kbd_handler.h_list) { 1039 input_handler_for_each_handle(&kbd_handler, &leds,
1022 struct input_handle *handle = to_handle_h(node); 1040 kbd_update_leds_helper);
1023 input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); 1041 ledstate = leds;
1024 input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
1025 input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
1026 input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
1027 }
1028 } 1042 }
1029
1030 ledstate = leds;
1031} 1043}
1032 1044
1033DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); 1045DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
@@ -1370,15 +1382,11 @@ static void kbd_disconnect(struct input_handle *handle)
1370 */ 1382 */
1371static void kbd_start(struct input_handle *handle) 1383static void kbd_start(struct input_handle *handle)
1372{ 1384{
1373 unsigned char leds = ledstate;
1374
1375 tasklet_disable(&keyboard_tasklet); 1385 tasklet_disable(&keyboard_tasklet);
1376 if (leds != 0xff) { 1386
1377 input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); 1387 if (ledstate != 0xff)
1378 input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); 1388 kbd_update_leds_helper(handle, &ledstate);
1379 input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); 1389
1380 input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
1381 }
1382 tasklet_enable(&keyboard_tasklet); 1390 tasklet_enable(&keyboard_tasklet);
1383} 1391}
1384 1392