Commit a0b63e71 authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Vojtech Pavlik

Input: split i8042 interrupt handling into an IRQ handler and a tasklet

parent 47c6252f
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* i8042 keyboard and mouse controller driver for Linux * i8042 keyboard and mouse controller driver for Linux
* *
* Copyright (c) 1999-2002 Vojtech Pavlik * Copyright (c) 1999-2002 Vojtech Pavlik
* Copyright (c) 2004 Dmitry Torokhov
*/ */
/* /*
...@@ -74,6 +75,14 @@ struct i8042_values { ...@@ -74,6 +75,14 @@ struct i8042_values {
unsigned char *phys; unsigned char *phys;
}; };
#define I8042_QUEUE_LEN 64
struct {
unsigned char str[I8042_QUEUE_LEN];
unsigned char data[I8042_QUEUE_LEN];
unsigned int read_pos;
unsigned int write_pos;
} i8042_buf;
static struct serio i8042_kbd_port; static struct serio i8042_kbd_port;
static struct serio i8042_aux_port; static struct serio i8042_aux_port;
static unsigned char i8042_initial_ctr; static unsigned char i8042_initial_ctr;
...@@ -82,7 +91,7 @@ static unsigned char i8042_mux_open; ...@@ -82,7 +91,7 @@ static unsigned char i8042_mux_open;
static unsigned char i8042_mux_present; static unsigned char i8042_mux_present;
static unsigned char i8042_sysdev_initialized; static unsigned char i8042_sysdev_initialized;
static struct pm_dev *i8042_pm_dev; static struct pm_dev *i8042_pm_dev;
struct timer_list i8042_timer; static struct timer_list i8042_timer;
/* /*
* Shared IRQ's require a device pointer, but this driver doesn't support * Shared IRQ's require a device pointer, but this driver doesn't support
...@@ -374,77 +383,109 @@ static char i8042_mux_short[4][16]; ...@@ -374,77 +383,109 @@ static char i8042_mux_short[4][16];
static char i8042_mux_phys[4][32]; static char i8042_mux_phys[4][32];
/* /*
* i8042_interrupt() is the most important function in this driver - * i8042_handle_data() is the most important function in this driver -
* it handles the interrupts from the i8042, and sends incoming bytes * it processes data received by i8042_interrupt and sends it to the
* to the upper layers. * upper layers.
*/ */
static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) static void i8042_handle_data(unsigned long notused)
{ {
unsigned long flags;
unsigned char str, data = 0; unsigned char str, data = 0;
unsigned int dfl; unsigned int dfl;
int ret;
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); /*
* No locking it required on i8042_buf as the tasklet is guaranteed
* to be serialized and if write_pos changes while comparing it with
* read_pos another run will be scheduled by i8042_interrupt.
*/
while (i8042_buf.read_pos != i8042_buf.write_pos) {
spin_lock_irqsave(&i8042_lock, flags); str = i8042_buf.str[i8042_buf.read_pos];
str = i8042_read_status(); data = i8042_buf.data[i8042_buf.read_pos];
if (str & I8042_STR_OBF)
data = i8042_read_data();
spin_unlock_irqrestore(&i8042_lock, flags);
if (~str & I8042_STR_OBF) { i8042_buf.read_pos++;
if (irq) dbg("Interrupt %d, without any data", irq); i8042_buf.read_pos %= I8042_QUEUE_LEN;
ret = 0;
goto out;
}
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) { if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) {
if (str & I8042_STR_MUXERR) { if (str & I8042_STR_MUXERR) {
switch (data) { switch (data) {
case 0xfd: case 0xfd:
case 0xfe: dfl = SERIO_TIMEOUT; break; case 0xfe: dfl = SERIO_TIMEOUT; break;
case 0xff: dfl = SERIO_PARITY; break; case 0xff: dfl = SERIO_PARITY; break;
} }
data = 0xfe; data = 0xfe;
} else dfl = 0; } else dfl = 0;
dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)", dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)",
data, (str >> 6), irq, data, (str >> 6), irq,
dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : ""); dfl & SERIO_TIMEOUT ? ", timeout" : "");
serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, regs); serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, NULL);
} else {
goto irq_ret; dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA))
serio_interrupt(&i8042_aux_port, data, dfl, NULL);
else if (i8042_kbd_values.exists)
serio_interrupt(&i8042_kbd_port, data, dfl, NULL);
}
} }
}
DECLARE_TASKLET(i8042_tasklet, i8042_handle_data, 0);
/*
* i8042_interrupt() handles the interrupts from i8042 and schedules
* i8042_handle_data to process and pass received bytes to the upper
* layers.
*/
static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long flags;
unsigned char str;
unsigned int n_bytes = 0;
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
spin_lock_irqsave(&i8042_lock, flags);
dbg("%02x <- i8042 (interrupt, %s, %d%s%s)", while ((str = i8042_read_status()) & I8042_STR_OBF) {
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) { n_bytes++;
serio_interrupt(&i8042_aux_port, data, dfl, regs);
goto irq_ret; i8042_buf.str[i8042_buf.write_pos] = str;
i8042_buf.data[i8042_buf.write_pos] = i8042_read_data();
i8042_buf.write_pos++;
i8042_buf.write_pos %= I8042_QUEUE_LEN;
if (unlikely(i8042_buf.write_pos == i8042_buf.read_pos))
printk(KERN_WARNING "i8042.c: ring buffer full\n");
} }
if (!i8042_kbd_values.exists) spin_unlock_irqrestore(&i8042_lock, flags);
goto irq_ret;
serio_interrupt(&i8042_kbd_port, data, dfl, regs); if (unlikely(n_bytes == 0)) {
if (irq) dbg("Interrupt %d, without any data", irq);
return IRQ_NONE;
}
tasklet_schedule(&i8042_tasklet);
irq_ret: return IRQ_HANDLED;
ret = 1;
out:
return IRQ_RETVAL(ret);
} }
/* /*
* i8042_enable_mux_mode checks whether the controller has an active * i8042_enable_mux_mode checks whether the controller has an active
* multiplexor and puts the chip into Multiplexed (as opposed to * multiplexor and puts the chip into Multiplexed (as opposed to
...@@ -1020,6 +1061,8 @@ void __exit i8042_exit(void) ...@@ -1020,6 +1061,8 @@ void __exit i8042_exit(void)
if (i8042_mux_values[i].exists) if (i8042_mux_values[i].exists)
serio_unregister_port(i8042_mux_port + i); serio_unregister_port(i8042_mux_port + i);
tasklet_kill(&i8042_tasklet);
i8042_platform_exit(); i8042_platform_exit();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment