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>
This commit is contained in:
parent
7105d2ea73
commit
4e8d340daa
1 changed files with 26 additions and 8 deletions
|
@ -368,6 +368,25 @@ static void i8042_stop(struct serio *serio)
|
|||
port->serio = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* i8042_filter() filters out unwanted bytes from the input data stream.
|
||||
* It is called from i8042_interrupt and thus is running with interrupts
|
||||
* off and i8042_lock held.
|
||||
*/
|
||||
static bool i8042_filter(unsigned char data, unsigned char str)
|
||||
{
|
||||
if (unlikely(i8042_suppress_kbd_ack)) {
|
||||
if ((~str & I8042_STR_AUXDATA) &&
|
||||
(data == 0xfa || data == 0xfe)) {
|
||||
i8042_suppress_kbd_ack--;
|
||||
dbg("Extra keyboard ACK - filtered out\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* i8042_interrupt() is the most important function in this driver -
|
||||
* it handles the interrupts from the i8042, and sends incoming bytes
|
||||
|
@ -381,9 +400,11 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
|||
unsigned char str, data;
|
||||
unsigned int dfl;
|
||||
unsigned int port_no;
|
||||
bool filtered;
|
||||
int ret = 1;
|
||||
|
||||
spin_lock_irqsave(&i8042_lock, flags);
|
||||
|
||||
str = i8042_read_status();
|
||||
if (unlikely(~str & I8042_STR_OBF)) {
|
||||
spin_unlock_irqrestore(&i8042_lock, flags);
|
||||
|
@ -391,8 +412,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
|||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
data = i8042_read_data();
|
||||
spin_unlock_irqrestore(&i8042_lock, flags);
|
||||
|
||||
if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
|
||||
static unsigned long last_transmit;
|
||||
|
@ -447,14 +468,11 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
|||
dfl & SERIO_PARITY ? ", bad parity" : "",
|
||||
dfl & SERIO_TIMEOUT ? ", timeout" : "");
|
||||
|
||||
if (unlikely(i8042_suppress_kbd_ack))
|
||||
if (port_no == I8042_KBD_PORT_NO &&
|
||||
(data == 0xfa || data == 0xfe)) {
|
||||
i8042_suppress_kbd_ack--;
|
||||
goto out;
|
||||
}
|
||||
filtered = i8042_filter(data, str);
|
||||
|
||||
if (likely(port->exists))
|
||||
spin_unlock_irqrestore(&i8042_lock, flags);
|
||||
|
||||
if (likely(port->exists && !filtered))
|
||||
serio_interrupt(port->serio, data, dfl);
|
||||
|
||||
out:
|
||||
|
|
Loading…
Reference in a new issue