mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 19:56:18 +00:00
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;
|
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 -
|
* i8042_interrupt() is the most important function in this driver -
|
||||||
* it handles the interrupts from the i8042, and sends incoming bytes
|
* 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 char str, data;
|
||||||
unsigned int dfl;
|
unsigned int dfl;
|
||||||
unsigned int port_no;
|
unsigned int port_no;
|
||||||
|
bool filtered;
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
|
|
||||||
spin_lock_irqsave(&i8042_lock, flags);
|
spin_lock_irqsave(&i8042_lock, flags);
|
||||||
|
|
||||||
str = i8042_read_status();
|
str = i8042_read_status();
|
||||||
if (unlikely(~str & I8042_STR_OBF)) {
|
if (unlikely(~str & I8042_STR_OBF)) {
|
||||||
spin_unlock_irqrestore(&i8042_lock, flags);
|
spin_unlock_irqrestore(&i8042_lock, flags);
|
||||||
|
@ -391,8 +412,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = i8042_read_data();
|
data = i8042_read_data();
|
||||||
spin_unlock_irqrestore(&i8042_lock, flags);
|
|
||||||
|
|
||||||
if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
|
if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
|
||||||
static unsigned long last_transmit;
|
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_PARITY ? ", bad parity" : "",
|
||||||
dfl & SERIO_TIMEOUT ? ", timeout" : "");
|
dfl & SERIO_TIMEOUT ? ", timeout" : "");
|
||||||
|
|
||||||
if (unlikely(i8042_suppress_kbd_ack))
|
filtered = i8042_filter(data, str);
|
||||||
if (port_no == I8042_KBD_PORT_NO &&
|
|
||||||
(data == 0xfa || data == 0xfe)) {
|
|
||||||
i8042_suppress_kbd_ack--;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (likely(port->exists))
|
spin_unlock_irqrestore(&i8042_lock, flags);
|
||||||
|
|
||||||
|
if (likely(port->exists && !filtered))
|
||||||
serio_interrupt(port->serio, data, dfl);
|
serio_interrupt(port->serio, data, dfl);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
Loading…
Reference in a new issue