diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2009-12-12 01:00:57 -0500 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2009-12-12 02:55:31 -0500 |
| commit | 4e8d340daac46cec8a0f8b3b0f228274fac913ba (patch) | |
| tree | f33f60499567f0ef54e4482956a74cad9106ccdb | |
| parent | 7105d2ea73e1391b681d0e1212c42f561c64d429 (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>
| -rw-r--r-- | drivers/input/serio/i8042.c | 34 |
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 | */ | ||
| 376 | static 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: |
