Commit 40a302c2 authored by Vojtech Pavlik's avatar Vojtech Pavlik

This cset implements automatic detection of PS/2 mice and AT keyboards

even when they were not connected at boot time. This is done by
polling the i8042 chip when its interrupts are not enabled.
parent 15984745
...@@ -70,6 +70,7 @@ static struct serio i8042_kbd_port; ...@@ -70,6 +70,7 @@ 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;
static unsigned char i8042_ctr; static unsigned char i8042_ctr;
struct timer_list i8042_timer;
#ifdef I8042_DEBUG_IO #ifdef I8042_DEBUG_IO
static unsigned long i8042_start; static unsigned long i8042_start;
...@@ -243,7 +244,7 @@ static int i8042_aux_write(struct serio *port, unsigned char c) ...@@ -243,7 +244,7 @@ static int i8042_aux_write(struct serio *port, unsigned char c)
* mode tend to trash their CTR when doing the AUX_SEND command. * mode tend to trash their CTR when doing the AUX_SEND command.
*/ */
retval += i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR); retval |= i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR);
/* /*
* Make sure the interrupt happens and the character is received even * Make sure the interrupt happens and the character is received even
...@@ -251,7 +252,7 @@ static int i8042_aux_write(struct serio *port, unsigned char c) ...@@ -251,7 +252,7 @@ static int i8042_aux_write(struct serio *port, unsigned char c)
* characters later. * characters later.
*/ */
i8042_interrupt(0, port, NULL); i8042_interrupt(0, NULL, NULL);
return retval; return retval;
} }
...@@ -274,23 +275,16 @@ static int i8042_open(struct serio *port) ...@@ -274,23 +275,16 @@ static int i8042_open(struct serio *port)
} }
/* /*
* Enable the device and its interrupt. * Enable the interrupt.
*/ */
i8042_ctr |= values->irqen; i8042_ctr |= values->irqen;
i8042_ctr &= ~values->disable;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while opening %s.\n", values->name); printk(KERN_ERR "i8042.c: Can't write CTR while opening %s.\n", values->name);
return -1; return -1;
} }
/*
* Flush buffers
*/
i8042_flush();
return 0; return 0;
} }
...@@ -304,11 +298,10 @@ static void i8042_close(struct serio *port) ...@@ -304,11 +298,10 @@ static void i8042_close(struct serio *port)
struct i8042_values *values = port->driver; struct i8042_values *values = port->driver;
/* /*
* Disable the device and its interrupt. * Disable the interrupt.
*/ */
i8042_ctr &= ~values->irqen; i8042_ctr &= ~values->irqen;
i8042_ctr |= values->disable;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while closing %s.\n", values->name); printk(KERN_ERR "i8042.c: Can't write CTR while closing %s.\n", values->name);
...@@ -374,26 +367,29 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -374,26 +367,29 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{ {
unsigned long flags; unsigned long flags;
unsigned char str, data; unsigned char str, data;
unsigned int dfl;
spin_lock_irqsave(&i8042_lock, flags); spin_lock_irqsave(&i8042_lock, flags);
while ((str = inb(I8042_STATUS_REG)) & I8042_STR_OBF) { while ((str = inb(I8042_STATUS_REG)) & I8042_STR_OBF) {
data = inb(I8042_DATA_REG); data = inb(I8042_DATA_REG);
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
#ifdef I8042_DEBUG_IO #ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt-%s) [%d]\n", printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt-%s, %d) [%d]\n",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", (int) (jiffies - i8042_start)); data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq, (int) (jiffies - i8042_start));
#endif #endif
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) { if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {
serio_interrupt(&i8042_aux_port, data, 0); serio_interrupt(&i8042_aux_port, data, dfl);
} else { } else {
if (i8042_kbd_values.exists) { if (i8042_kbd_values.exists) {
if (!i8042_direct) { if (!i8042_direct) {
if (data > 0x7f) { if (data > 0x7f) {
if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) { if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) {
serio_interrupt(&i8042_kbd_port, 0xf0, 0); serio_interrupt(&i8042_kbd_port, 0xf0, dfl);
data = i8042_unxlate_table[data & 0x7f]; data = i8042_unxlate_table[data & 0x7f];
} }
} else { } else {
...@@ -401,7 +397,7 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -401,7 +397,7 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
data = i8042_unxlate_table[data]; data = i8042_unxlate_table[data];
} }
} }
serio_interrupt(&i8042_kbd_port, data, 0); serio_interrupt(&i8042_kbd_port, data, dfl);
} }
} }
} }
...@@ -524,6 +520,8 @@ static int __init i8042_controller_init(void) ...@@ -524,6 +520,8 @@ static int __init i8042_controller_init(void)
void i8042_controller_cleanup(void) void i8042_controller_cleanup(void)
{ {
i8042_flush();
/* /*
* Reset the controller. * Reset the controller.
*/ */
...@@ -565,6 +563,19 @@ static int __init i8042_check_aux(struct i8042_values *values, struct serio *por ...@@ -565,6 +563,19 @@ static int __init i8042_check_aux(struct i8042_values *values, struct serio *por
{ {
unsigned char param; unsigned char param;
/*
* Check if AUX irq is available. If it isn't, then there is no point
* in trying to detect AUX presence.
*/
if (request_irq(values->irq, i8042_interrupt, 0, "i8042", NULL))
return -1;
free_irq(values->irq, NULL);
/*
* Get rid of bytes in the queue.
*/
i8042_flush(); i8042_flush();
/* /*
...@@ -637,6 +648,32 @@ static int __init i8042_port_register(struct i8042_values *values, struct serio ...@@ -637,6 +648,32 @@ static int __init i8042_port_register(struct i8042_values *values, struct serio
return 0; return 0;
} }
static void i8042_timer_func(unsigned long data)
{
i8042_interrupt(0, NULL, NULL);
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
}
static void __init i8042_start_polling(void)
{
i8042_ctr &= ~I8042_CTR_KBDDIS;
if (i8042_aux_values.exists)
i8042_ctr &= ~I8042_CTR_AUXDIS;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_WARNING "i8042.c: Can't write CTR while starting polling.\n");
return;
}
i8042_timer.function = i8042_timer_func;
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
}
static void __exit i8042_stop_polling(void)
{
del_timer(&i8042_timer);
}
/* /*
* Module init and cleanup functions. * Module init and cleanup functions.
*/ */
...@@ -677,6 +714,15 @@ int __init i8042_init(void) ...@@ -677,6 +714,15 @@ int __init i8042_init(void)
i8042_start = jiffies; i8042_start = jiffies;
#endif #endif
/*
* On ix86 platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on ix86 boxes.
*/
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
if (!request_region(I8042_DATA_REG, 16, "i8042"))
return -EBUSY;
#endif
if (i8042_controller_init()) if (i8042_controller_init())
return -ENODEV; return -ENODEV;
...@@ -685,20 +731,18 @@ int __init i8042_init(void) ...@@ -685,20 +731,18 @@ int __init i8042_init(void)
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port)) if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port))
i8042_port_register(&i8042_aux_values, &i8042_aux_port); i8042_port_register(&i8042_aux_values, &i8042_aux_port);
/* i8042_start_polling();
* On ix86 platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on ix86 boxes.
*/
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
request_region(I8042_DATA_REG, 16, "i8042");
#endif
register_reboot_notifier(&i8042_notifier); register_reboot_notifier(&i8042_notifier);
return 0; return 0;
} }
void __exit i8042_exit(void) void __exit i8042_exit(void)
{ {
unregister_reboot_notifier(&i8042_notifier); unregister_reboot_notifier(&i8042_notifier);
i8042_stop_polling();
if (i8042_kbd_values.exists) if (i8042_kbd_values.exists)
serio_unregister_port(&i8042_kbd_port); serio_unregister_port(&i8042_kbd_port);
...@@ -707,6 +751,7 @@ void __exit i8042_exit(void) ...@@ -707,6 +751,7 @@ void __exit i8042_exit(void)
serio_unregister_port(&i8042_aux_port); serio_unregister_port(&i8042_aux_port);
i8042_controller_cleanup(); i8042_controller_cleanup();
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) #if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
release_region(I8042_DATA_REG, 16); release_region(I8042_DATA_REG, 16);
#endif #endif
......
...@@ -27,12 +27,6 @@ ...@@ -27,12 +27,6 @@
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/ */
/*
* If you want to reset your i8042 upon boot, define this.
*/
#undef I8042_RESET
/* /*
* If you want to trace all the i/o the i8042 module does for * If you want to trace all the i/o the i8042 module does for
* debugging purposes, define this. * debugging purposes, define this.
...@@ -62,6 +56,15 @@ ...@@ -62,6 +56,15 @@
#define I8042_CTL_TIMEOUT 10000 #define I8042_CTL_TIMEOUT 10000
/*
* When the device isn't opened and it's interrupts aren't used, we poll it at
* regular intervals to see if any characters arrived. If yes, we can start
* probing for any mouse / keyboard connected. This is the period of the
* polling.
*/
#define I8042_POLL_PERIOD HZ/20
/* /*
* Register numbers. * Register numbers.
*/ */
......
...@@ -111,6 +111,14 @@ void serio_rescan(struct serio *serio) ...@@ -111,6 +111,14 @@ void serio_rescan(struct serio *serio)
wake_up(&serio_wait); wake_up(&serio_wait);
} }
void serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
if (serio->dev && serio->dev->interrupt)
serio->dev->interrupt(serio, data, flags);
else
serio_rescan(serio);
}
void serio_register_port(struct serio *serio) void serio_register_port(struct serio *serio)
{ {
serio->next = serio_list; serio->next = serio_list;
......
...@@ -76,6 +76,7 @@ struct serio_dev { ...@@ -76,6 +76,7 @@ struct serio_dev {
int serio_open(struct serio *serio, struct serio_dev *dev); int serio_open(struct serio *serio, struct serio_dev *dev);
void serio_close(struct serio *serio); void serio_close(struct serio *serio);
void serio_rescan(struct serio *serio); void serio_rescan(struct serio *serio);
void serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags);
void serio_register_port(struct serio *serio); void serio_register_port(struct serio *serio);
void serio_unregister_port(struct serio *serio); void serio_unregister_port(struct serio *serio);
...@@ -93,12 +94,6 @@ static __inline__ void serio_dev_write_wakeup(struct serio *serio) ...@@ -93,12 +94,6 @@ static __inline__ void serio_dev_write_wakeup(struct serio *serio)
serio->dev->write_wakeup(serio); serio->dev->write_wakeup(serio);
} }
static __inline__ void serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
{
if (serio->dev && serio->dev->interrupt)
serio->dev->interrupt(serio, data, flags);
}
#define SERIO_TIMEOUT 1 #define SERIO_TIMEOUT 1
#define SERIO_PARITY 2 #define SERIO_PARITY 2
......
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