From: Dmitry Torokhov > 3) They light up a led on the keyboard, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ atkbd tries to switch leds but one tasklet can not interrupt another so it deadlocks... You need to revert just the patch below, not entire bk-input --- 25-akpm/drivers/input/serio/i8042.c | 140 ++++++++++++------------------------ 1 files changed, 49 insertions(+), 91 deletions(-) diff -puN drivers/input/serio/i8042.c~revert-i8042-interrupt-handling drivers/input/serio/i8042.c --- 25/drivers/input/serio/i8042.c~revert-i8042-interrupt-handling 2004-05-13 17:09:59.987912912 -0700 +++ 25-akpm/drivers/input/serio/i8042.c 2004-05-13 17:10:47.710657952 -0700 @@ -2,7 +2,6 @@ * i8042 keyboard and mouse controller driver for Linux * * Copyright (c) 1999-2002 Vojtech Pavlik - * Copyright (c) 2004 Dmitry Torokhov */ /* @@ -75,14 +74,6 @@ struct i8042_values { 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_aux_port; static unsigned char i8042_initial_ctr; @@ -91,7 +82,7 @@ static unsigned char i8042_mux_open; static unsigned char i8042_mux_present; static unsigned char i8042_sysdev_initialized; static struct pm_dev *i8042_pm_dev; -static struct timer_list i8042_timer; +struct timer_list i8042_timer; /* * Shared IRQ's require a device pointer, but this driver doesn't support @@ -383,108 +374,76 @@ static char i8042_mux_short[4][16]; static char i8042_mux_phys[4][32]; /* - * i8042_handle_data() is the most important function in this driver - - * it processes data received by i8042_interrupt and sends it to the - * upper layers. + * i8042_interrupt() is the most important function in this driver - + * it handles the interrupts from the i8042, and sends incoming bytes + * to the upper layers. */ -static void i8042_handle_data(unsigned long notused) +static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + unsigned long flags; unsigned char str, data = 0; unsigned int dfl; + int ret; - /* - * 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) { - - str = i8042_buf.str[i8042_buf.read_pos]; - data = i8042_buf.data[i8042_buf.read_pos]; + mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); - i8042_buf.read_pos++; - i8042_buf.read_pos %= I8042_QUEUE_LEN; + spin_lock_irqsave(&i8042_lock, flags); + str = i8042_read_status(); + if (str & I8042_STR_OBF) + data = i8042_read_data(); + spin_unlock_irqrestore(&i8042_lock, flags); - dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | - ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); - - if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) { - - if (str & I8042_STR_MUXERR) { - switch (data) { - case 0xfd: - case 0xfe: dfl = SERIO_TIMEOUT; break; - case 0xff: dfl = SERIO_PARITY; break; - } - data = 0xfe; - } else dfl = 0; - - dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)", - data, (str >> 6), irq, - dfl & SERIO_PARITY ? ", bad parity" : "", - dfl & SERIO_TIMEOUT ? ", timeout" : ""); - - serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, NULL); - } else { - - 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); - } + if (~str & I8042_STR_OBF) { + if (irq) dbg("Interrupt %d, without any data", irq); + ret = 0; + goto out; } -} -DECLARE_TASKLET(i8042_tasklet, i8042_handle_data, 0); + dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | + ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); -/* - * i8042_interrupt() handles the interrupts from i8042 and schedules - * i8042_handle_data to process and pass received bytes to the upper - * layers. - */ + if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) { -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); - - while ((str = i8042_read_status()) & I8042_STR_OBF) { - - n_bytes++; + if (str & I8042_STR_MUXERR) { + switch (data) { + case 0xfd: + case 0xfe: dfl = SERIO_TIMEOUT; break; + case 0xff: dfl = SERIO_PARITY; break; + } + data = 0xfe; + } else dfl = 0; - i8042_buf.str[i8042_buf.write_pos] = str; - i8042_buf.data[i8042_buf.write_pos] = i8042_read_data(); + dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)", + data, (str >> 6), irq, + dfl & SERIO_PARITY ? ", bad parity" : "", + dfl & SERIO_TIMEOUT ? ", timeout" : ""); - i8042_buf.write_pos++; - i8042_buf.write_pos %= I8042_QUEUE_LEN; + serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, regs); - if (unlikely(i8042_buf.write_pos == i8042_buf.read_pos)) - printk(KERN_WARNING "i8042.c: ring buffer full\n"); + goto irq_ret; } - spin_unlock_irqrestore(&i8042_lock, flags); + 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 (unlikely(n_bytes == 0)) { - if (irq) dbg("Interrupt %d, without any data", irq); - return IRQ_NONE; + if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) { + serio_interrupt(&i8042_aux_port, data, dfl, regs); + goto irq_ret; } - tasklet_schedule(&i8042_tasklet); + if (!i8042_kbd_values.exists) + goto irq_ret; - return IRQ_HANDLED; -} + serio_interrupt(&i8042_kbd_port, data, dfl, regs); +irq_ret: + ret = 1; +out: + return IRQ_RETVAL(ret); +} /* * i8042_enable_mux_mode checks whether the controller has an active @@ -1060,7 +1019,6 @@ void __exit i8042_exit(void) serio_unregister_port(i8042_mux_port + i); del_timer_sync(&i8042_timer); - tasklet_kill(&i8042_tasklet); i8042_platform_exit(); } _