Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e8d340d authored by Dmitry Torokhov's avatar Dmitry Torokhov
Browse files

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: default avatarDmitry Torokhov <dtor@mail.ru>
parent 7105d2ea
Loading
Loading
Loading
Loading
+26 −8
Original line number Diff line number Diff line
@@ -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);

	spin_unlock_irqrestore(&i8042_lock, flags);

	if (likely(port->exists))
	if (likely(port->exists && !filtered))
		serio_interrupt(port->serio, data, dfl);

 out: