aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/serio/i8042.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2009-12-12 01:00:57 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-12-12 02:55:31 -0500
commit4e8d340daac46cec8a0f8b3b0f228274fac913ba (patch)
treef33f60499567f0ef54e4482956a74cad9106ccdb /drivers/input/serio/i8042.c
parent7105d2ea73e1391b681d0e1212c42f561c64d429 (diff)
Input: i8042 - fix locking in interrupt routine
We need to protect not only i8042 status and data register from concurrent access from IRQ 1 and 12 but the rest of the shared state as well, so let's move release of i8042_lock in i8042_interrupt() a little bit further down. Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/serio/i8042.c')
-rw-r--r--drivers/input/serio/i8042.c34
1 files changed, 26 insertions, 8 deletions
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 1df02d25aca5..634da68f7f35 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -369,6 +369,25 @@ static void i8042_stop(struct serio *serio)
369} 369}
370 370
371/* 371/*
372 * i8042_filter() filters out unwanted bytes from the input data stream.
373 * It is called from i8042_interrupt and thus is running with interrupts
374 * off and i8042_lock held.
375 */
376static bool i8042_filter(unsigned char data, unsigned char str)
377{
378 if (unlikely(i8042_suppress_kbd_ack)) {
379 if ((~str & I8042_STR_AUXDATA) &&
380 (data == 0xfa || data == 0xfe)) {
381 i8042_suppress_kbd_ack--;
382 dbg("Extra keyboard ACK - filtered out\n");
383 return true;
384 }
385 }
386
387 return false;
388}
389
390/*
372 * i8042_interrupt() is the most important function in this driver - 391 * i8042_interrupt() is the most important function in this driver -
373 * it handles the interrupts from the i8042, and sends incoming bytes 392 * it handles the interrupts from the i8042, and sends incoming bytes
374 * to the upper layers. 393 * to the upper layers.
@@ -381,9 +400,11 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
381 unsigned char str, data; 400 unsigned char str, data;
382 unsigned int dfl; 401 unsigned int dfl;
383 unsigned int port_no; 402 unsigned int port_no;
403 bool filtered;
384 int ret = 1; 404 int ret = 1;
385 405
386 spin_lock_irqsave(&i8042_lock, flags); 406 spin_lock_irqsave(&i8042_lock, flags);
407
387 str = i8042_read_status(); 408 str = i8042_read_status();
388 if (unlikely(~str & I8042_STR_OBF)) { 409 if (unlikely(~str & I8042_STR_OBF)) {
389 spin_unlock_irqrestore(&i8042_lock, flags); 410 spin_unlock_irqrestore(&i8042_lock, flags);
@@ -391,8 +412,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
391 ret = 0; 412 ret = 0;
392 goto out; 413 goto out;
393 } 414 }
415
394 data = i8042_read_data(); 416 data = i8042_read_data();
395 spin_unlock_irqrestore(&i8042_lock, flags);
396 417
397 if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { 418 if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
398 static unsigned long last_transmit; 419 static unsigned long last_transmit;
@@ -447,14 +468,11 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
447 dfl & SERIO_PARITY ? ", bad parity" : "", 468 dfl & SERIO_PARITY ? ", bad parity" : "",
448 dfl & SERIO_TIMEOUT ? ", timeout" : ""); 469 dfl & SERIO_TIMEOUT ? ", timeout" : "");
449 470
450 if (unlikely(i8042_suppress_kbd_ack)) 471 filtered = i8042_filter(data, str);
451 if (port_no == I8042_KBD_PORT_NO && 472
452 (data == 0xfa || data == 0xfe)) { 473 spin_unlock_irqrestore(&i8042_lock, flags);
453 i8042_suppress_kbd_ack--;
454 goto out;
455 }
456 474
457 if (likely(port->exists)) 475 if (likely(port->exists && !filtered))
458 serio_interrupt(port->serio, data, dfl); 476 serio_interrupt(port->serio, data, dfl);
459 477
460 out: 478 out: