]> git.neil.brown.name Git - history.git/commitdiff
This cset implements automatic detection of PS/2 mice and AT keyboards
authorVojtech Pavlik <vojtech@twilight.ucw.cz>
Thu, 11 Jul 2002 01:30:20 +0000 (03:30 +0200)
committerVojtech Pavlik <vojtech@twilight.ucw.cz>
Thu, 11 Jul 2002 01:30:20 +0000 (03:30 +0200)
even when they were not connected at boot time. This is done by
polling the i8042 chip when its interrupts are not enabled.

drivers/input/serio/i8042.c
drivers/input/serio/i8042.h
drivers/input/serio/serio.c
include/linux/serio.h

index a508b6a02619fd67db95970e53104193476d669a..b48a5600580d2227970c7886f711578d47e998b3 100644 (file)
@@ -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
index 569f81942f500c436cca6ad5b2ff1c32131e26d7..b5c56dcb4233e4d69671ab5b9a57c64390c96bf6 100644 (file)
  * 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.
 
 #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.
  */
index 9f54ec068b3a715e5bde9b92dc600ff6dbb97bf2..415adb66556808b5f2536fea34f019f53042cf40 100644 (file)
@@ -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;       
index 1e9de401018f3f02f08d3c696ec7f037da09aa4a..37deb0ca7836dda82f525fb4a24cae6c984f9a66 100644 (file)
@@ -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