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;
static struct serio i8042_aux_port;
static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr;
struct timer_list i8042_timer;
#ifdef I8042_DEBUG_IO
static unsigned long i8042_start;
......@@ -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.
*/
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
......@@ -251,7 +252,7 @@ static int i8042_aux_write(struct serio *port, unsigned char c)
* characters later.
*/
i8042_interrupt(0, port, NULL);
i8042_interrupt(0, NULL, NULL);
return retval;
}
......@@ -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->disable;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while opening %s.\n", values->name);
return -1;
}
/*
* Flush buffers
*/
i8042_flush();
return 0;
}
......@@ -304,11 +298,10 @@ static void i8042_close(struct serio *port)
struct i8042_values *values = port->driver;
/*
* Disable the device and its interrupt.
* Disable the interrupt.
*/
i8042_ctr &= ~values->irqen;
i8042_ctr |= values->disable;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
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)
{
unsigned long flags;
unsigned char str, data;
unsigned int dfl;
spin_lock_irqsave(&i8042_lock, flags);
while ((str = inb(I8042_STATUS_REG)) & I8042_STR_OBF) {
data = inb(I8042_DATA_REG);
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt-%s) [%d]\n",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", (int) (jiffies - i8042_start));
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt-%s, %d) [%d]\n",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq, (int) (jiffies - i8042_start));
#endif
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {
serio_interrupt(&i8042_aux_port, data, 0);
serio_interrupt(&i8042_aux_port, data, dfl);
} else {
if (i8042_kbd_values.exists) {
if (!i8042_direct) {
if (data > 0x7f) {
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];
}
} else {
......@@ -401,7 +397,7 @@ static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
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)
void i8042_controller_cleanup(void)
{
i8042_flush();
/*
* Reset the controller.
*/
......@@ -565,6 +563,19 @@ static int __init i8042_check_aux(struct i8042_values *values, struct serio *por
{
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();
/*
......@@ -637,6 +648,32 @@ static int __init i8042_port_register(struct i8042_values *values, struct serio
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.
*/
......@@ -677,6 +714,15 @@ int __init i8042_init(void)
i8042_start = jiffies;
#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())
return -ENODEV;
......@@ -685,20 +731,18 @@ int __init i8042_init(void)
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port))
i8042_port_register(&i8042_aux_values, &i8042_aux_port);
/*
* 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
i8042_start_polling();
register_reboot_notifier(&i8042_notifier);
return 0;
}
void __exit i8042_exit(void)
{
unregister_reboot_notifier(&i8042_notifier);
i8042_stop_polling();
if (i8042_kbd_values.exists)
serio_unregister_port(&i8042_kbd_port);
......@@ -707,6 +751,7 @@ void __exit i8042_exit(void)
serio_unregister_port(&i8042_aux_port);
i8042_controller_cleanup();
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
release_region(I8042_DATA_REG, 16);
#endif
......
......@@ -27,12 +27,6 @@
* 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
* debugging purposes, define this.
......@@ -62,6 +56,15 @@
#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.
*/
......
......@@ -111,6 +111,14 @@ void serio_rescan(struct serio *serio)
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)
{
serio->next = serio_list;
......
......@@ -76,6 +76,7 @@ struct serio_dev {
int serio_open(struct serio *serio, struct serio_dev *dev);
void serio_close(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_unregister_port(struct serio *serio);
......@@ -93,12 +94,6 @@ static __inline__ void serio_dev_write_wakeup(struct serio *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_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