Commit 906f8f6a authored by Vojtech Pavlik's avatar Vojtech Pavlik

Merge bkbits:input into suse.cz:/home/vojtech/bk/input

parents a87191ad 29af30fb
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
* 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
*/ */
/* /*
...@@ -75,14 +74,6 @@ struct i8042_values { ...@@ -75,14 +74,6 @@ 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;
...@@ -91,7 +82,7 @@ static unsigned char i8042_mux_open; ...@@ -91,7 +82,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;
static struct timer_list i8042_timer; 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
...@@ -104,6 +95,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs); ...@@ -104,6 +95,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/* /*
* The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
* be ready for reading values from it / writing values to it. * be ready for reading values from it / writing values to it.
* Called always with i8042_lock held.
*/ */
static int i8042_wait_read(void) static int i8042_wait_read(void)
...@@ -383,108 +375,76 @@ static char i8042_mux_short[4][16]; ...@@ -383,108 +375,76 @@ static char i8042_mux_short[4][16];
static char i8042_mux_phys[4][32]; static char i8042_mux_phys[4][32];
/* /*
* i8042_handle_data() is the most important function in this driver - * i8042_interrupt() is the most important function in this driver -
* it processes data received by i8042_interrupt and sends it to the * it handles the interrupts from the i8042, and sends incoming bytes
* upper layers. * 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 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) {
str = i8042_buf.str[i8042_buf.read_pos];
data = i8042_buf.data[i8042_buf.read_pos];
i8042_buf.read_pos++;
i8042_buf.read_pos %= I8042_QUEUE_LEN;
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)", spin_lock_irqsave(&i8042_lock, flags);
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq, str = i8042_read_status();
dfl & SERIO_PARITY ? ", bad parity" : "", if (str & I8042_STR_OBF)
dfl & SERIO_TIMEOUT ? ", timeout" : ""); data = i8042_read_data();
spin_unlock_irqrestore(&i8042_lock, flags);
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) if (~str & I8042_STR_OBF) {
serio_interrupt(&i8042_aux_port, data, dfl, NULL); if (irq) dbg("Interrupt %d, without any data", irq);
else if (i8042_kbd_values.exists) ret = 0;
serio_interrupt(&i8042_kbd_port, data, dfl, NULL); 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);
/* if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) {
* 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);
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; dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)",
i8042_buf.data[i8042_buf.write_pos] = i8042_read_data(); data, (str >> 6), irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
i8042_buf.write_pos++; serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, regs);
i8042_buf.write_pos %= I8042_QUEUE_LEN;
if (unlikely(i8042_buf.write_pos == i8042_buf.read_pos)) goto irq_ret;
printk(KERN_WARNING "i8042.c: ring buffer full\n");
} }
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 (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {
if (irq) dbg("Interrupt %d, without any data", irq); serio_interrupt(&i8042_aux_port, data, dfl, regs);
return IRQ_NONE; 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 * i8042_enable_mux_mode checks whether the controller has an active
...@@ -718,6 +678,7 @@ static void i8042_timer_func(unsigned long data) ...@@ -718,6 +678,7 @@ static void i8042_timer_func(unsigned long data)
static int i8042_controller_init(void) static int i8042_controller_init(void)
{ {
unsigned long flags;
/* /*
* Test the i8042. We need to know if it thinks it's working correctly * Test the i8042. We need to know if it thinks it's working correctly
...@@ -764,12 +725,14 @@ static int i8042_controller_init(void) ...@@ -764,12 +725,14 @@ static int i8042_controller_init(void)
* Handle keylock. * Handle keylock.
*/ */
spin_lock_irqsave(&i8042_lock, flags);
if (~i8042_read_status() & I8042_STR_KEYLOCK) { if (~i8042_read_status() & I8042_STR_KEYLOCK) {
if (i8042_unlock) if (i8042_unlock)
i8042_ctr |= I8042_CTR_IGNKEYLOCK; i8042_ctr |= I8042_CTR_IGNKEYLOCK;
else else
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n"); printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
} }
spin_unlock_irqrestore(&i8042_lock, flags);
/* /*
* If the chip is configured into nontranslated mode by the BIOS, don't * If the chip is configured into nontranslated mode by the BIOS, don't
...@@ -1058,9 +1021,7 @@ void __exit i8042_exit(void) ...@@ -1058,9 +1021,7 @@ void __exit i8042_exit(void)
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
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);
del_timer_sync(&i8042_timer); del_timer_sync(&i8042_timer);
tasklet_kill(&i8042_tasklet);
i8042_platform_exit(); i8042_platform_exit();
} }
......
...@@ -67,12 +67,18 @@ struct serio_event { ...@@ -67,12 +67,18 @@ struct serio_event {
struct list_head node; struct list_head node;
}; };
static DECLARE_MUTEX(serio_sem);
spinlock_t serio_lock = SPIN_LOCK_UNLOCKED; /* protects serio_event_list and serio->dev */
static DECLARE_MUTEX(serio_sem); /* protects serio_list and serio_dev_list */
static LIST_HEAD(serio_list); static LIST_HEAD(serio_list);
static LIST_HEAD(serio_dev_list); static LIST_HEAD(serio_dev_list);
static LIST_HEAD(serio_event_list); static LIST_HEAD(serio_event_list);
static int serio_pid; static int serio_pid;
/*
* serio_find_dev() must be called with serio_sem down.
*/
static void serio_find_dev(struct serio *serio) static void serio_find_dev(struct serio *serio)
{ {
struct serio_dev *dev; struct serio_dev *dev;
...@@ -96,22 +102,42 @@ static DECLARE_COMPLETION(serio_exited); ...@@ -96,22 +102,42 @@ static DECLARE_COMPLETION(serio_exited);
static void serio_invalidate_pending_events(struct serio *serio) static void serio_invalidate_pending_events(struct serio *serio)
{ {
struct serio_event *event; struct serio_event *event;
unsigned long flags;
spin_lock_irqsave(&serio_lock, flags);
list_for_each_entry(event, &serio_event_list, node) list_for_each_entry(event, &serio_event_list, node)
if (event->serio == serio) if (event->serio == serio)
event->serio = NULL; event->serio = NULL;
spin_unlock_irqrestore(&serio_lock, flags);
} }
void serio_handle_events(void) void serio_handle_events(void)
{ {
struct list_head *node, *next; struct list_head *node;
struct serio_event *event; struct serio_event *event;
unsigned long flags;
while (1) {
spin_lock_irqsave(&serio_lock, flags);
if (list_empty(&serio_event_list)) {
spin_unlock_irqrestore(&serio_lock, flags);
break;
}
list_for_each_safe(node, next, &serio_event_list) { node = serio_event_list.next;
event = container_of(node, struct serio_event, node); event = container_of(node, struct serio_event, node);
list_del_init(node);
spin_unlock_irqrestore(&serio_lock, flags);
down(&serio_sem); down(&serio_sem);
if (event->serio == NULL)
if (event->serio == NULL) /*!!!*/
goto event_done; goto event_done;
switch (event->type) { switch (event->type) {
...@@ -139,7 +165,6 @@ void serio_handle_events(void) ...@@ -139,7 +165,6 @@ void serio_handle_events(void)
} }
event_done: event_done:
up(&serio_sem); up(&serio_sem);
list_del_init(node);
kfree(event); kfree(event);
} }
} }
...@@ -178,12 +203,18 @@ static void serio_queue_event(struct serio *serio, int event_type) ...@@ -178,12 +203,18 @@ static void serio_queue_event(struct serio *serio, int event_type)
void serio_rescan(struct serio *serio) void serio_rescan(struct serio *serio)
{ {
unsigned long flags;
spin_lock_irqsave(&serio_lock, flags);
serio_queue_event(serio, SERIO_RESCAN); serio_queue_event(serio, SERIO_RESCAN);
spin_unlock_irqrestore(&serio_lock, flags);
} }
void serio_reconnect(struct serio *serio) void serio_reconnect(struct serio *serio)
{ {
unsigned long flags;
spin_lock_irqsave(&serio_lock, flags);
serio_queue_event(serio, SERIO_RECONNECT); serio_queue_event(serio, SERIO_RECONNECT);
spin_unlock_irqrestore(&serio_lock, flags);
} }
irqreturn_t serio_interrupt(struct serio *serio, irqreturn_t serio_interrupt(struct serio *serio,
...@@ -191,17 +222,22 @@ irqreturn_t serio_interrupt(struct serio *serio, ...@@ -191,17 +222,22 @@ irqreturn_t serio_interrupt(struct serio *serio,
{ {
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
spin_lock_irq(&serio_lock);
if (serio->dev && serio->dev->interrupt) { if (serio->dev && serio->dev->interrupt) {
ret = serio->dev->interrupt(serio, data, flags, regs); ret = serio->dev->interrupt(serio, data, flags, regs);
} else { } else {
if (!flags) { if (!flags) {
if ((serio->type == SERIO_8042 || if ((serio->type != SERIO_8042 &&
serio->type == SERIO_8042_XL) && (data != 0xaa)) serio->type != SERIO_8042_XL) || (data == 0xaa)) {
return ret; serio_queue_event(serio, SERIO_RESCAN);
serio_rescan(serio); ret = IRQ_HANDLED;
ret = IRQ_HANDLED; }
} }
} }
spin_unlock_irq(&serio_lock);
return ret; return ret;
} }
...@@ -292,7 +328,11 @@ void serio_unregister_device(struct serio_dev *dev) ...@@ -292,7 +328,11 @@ void serio_unregister_device(struct serio_dev *dev)
/* called from serio_dev->connect/disconnect methods under serio_sem */ /* called from serio_dev->connect/disconnect methods under serio_sem */
int serio_open(struct serio *serio, struct serio_dev *dev) int serio_open(struct serio *serio, struct serio_dev *dev)
{ {
unsigned long flags;
spin_lock_irqsave(&serio_lock, flags);
serio->dev = dev; serio->dev = dev;
spin_unlock_irqrestore(&serio_lock, flags);
if (serio->open && serio->open(serio)) { if (serio->open && serio->open(serio)) {
serio->dev = NULL; serio->dev = NULL;
return -1; return -1;
...@@ -303,9 +343,13 @@ int serio_open(struct serio *serio, struct serio_dev *dev) ...@@ -303,9 +343,13 @@ int serio_open(struct serio *serio, struct serio_dev *dev)
/* called from serio_dev->connect/disconnect methods under serio_sem */ /* called from serio_dev->connect/disconnect methods under serio_sem */
void serio_close(struct serio *serio) void serio_close(struct serio *serio)
{ {
unsigned long flags;
if (serio->close) if (serio->close)
serio->close(serio); serio->close(serio);
spin_lock_irqsave(&serio_lock, flags);
serio->dev = NULL; serio->dev = NULL;
spin_unlock_irqrestore(&serio_lock, flags);
} }
static int __init serio_init(void) static int __init serio_init(void)
......
...@@ -36,7 +36,7 @@ struct serio { ...@@ -36,7 +36,7 @@ struct serio {
int (*open)(struct serio *); int (*open)(struct serio *);
void (*close)(struct serio *); void (*close)(struct serio *);
struct serio_dev *dev; struct serio_dev *dev; /* Accessed from interrupt, writes must be protected by serio_lock */
struct list_head node; struct list_head node;
}; };
......
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