Import 2.3.18pre1
[davej-history.git] / drivers / char / pc_keyb.c
index c4089b2..b4b6069 100644 (file)
  * See keyboard.c for the whole history.
  *
  * Major cleanup by Martin Mares, May 1997
+ *
+ * Combined the keyboard and PS/2 mouse handling into one file,
+ * because they share the same hardware.
+ * Johan Myreen <jem@iki.fi> 1998-10-08.
+ *
+ * Code fixes to handle mouse ACKs properly.
+ * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
+ *
  */
 
 #include <linux/config.h>
 
+#include <linux/spinlock.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/kbd_ll.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/malloc.h>
 
 #include <asm/keyboard.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
 #include <asm/system.h>
 
 /* Some configuration switches are present in the include file... */
@@ -40,173+56,71 @@ unsigned char pckbd_sysrq_xlate[128] =
        "\r\000/";                                      /* 0x60 - 0x6f */
 #endif
 
-/*
- * In case we run on a non-x86 hardware we need to initialize both the keyboard
- * controller and the keyboard. On a x86, the BIOS will already have initialized
- * them.
- */
-
-#ifndef __i386__
-#define INIT_KBD
-#endif
-
-#ifdef INIT_KBD
-
-__initfunc(static int kbd_wait_for_input(void))
-{
-       int     n;
-       int     status, data;
-       unsigned long start = jiffies;
-
-       do {
-                status = inb(KBD_STATUS_REG);
-                /*
-                 * Wait for input data to become available.  This bit will
-                 * then be cleared by the following read of the DATA
-                 * register.
-                 */
-
-                if (!(status & KBD_STAT_OBF))
-                       continue;
-
-               data = inb(KBD_DATA_REG);
-
-                /*
-                 * Check to see if a timeout error has occurred.  This means
-                 * that transmission was started but did not complete in the
-                 * normal time cycle.  PERR is set when a parity error occurred
-                 * in the last transmission.
-                 */
-                if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) {
-                       continue;
-                }
-               return (data & 0xff);
-        } while (jiffies - start < KBD_INIT_TIMEOUT);
-        return -1;     /* timed-out if fell through to here... */
-}
-
-__initfunc(static void kbd_write(int address, int data))
-{
-       int status;
-
-       do {
-               status = inb(KBD_STATUS_REG);
-       } while (status & KBD_STAT_IBF);
-       outb(data, address);
-}
-
-__initfunc(static char *initialize_kbd2(void))
-{
-       /* Flush any pending input. */
-
-       while (kbd_wait_for_input() != -1)
-               ;
-
-       /*
-        * Test the keyboard interface.
-        * This seems to be the only way to get it going.
-        * If the test is successful a x55 is placed in the input buffer.
-        */
-
-       kbd_write(KBD_CNTL_REG, KBD_CCMD_SELF_TEST);
-       if (kbd_wait_for_input() != 0x55)
-               return "Keyboard failed self test";
-
-       /*
-        * Perform a keyboard interface test.  This causes the controller
-        * to test the keyboard clock and data lines.  The results of the
-        * test are placed in the input buffer.
-        */
-
-       kbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_TEST);
-       if (kbd_wait_for_input() != 0x00)
-               return "Keyboard interface failed self test";
-
-       /* Enable the keyboard by allowing the keyboard clock to run. */
-
-       kbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_ENABLE);
-
-       /*
-        * Reset keyboard. If the read times out
-        * then the assumption is that no keyboard is
-        * plugged into the machine.
-        * This defaults the keyboard to scan-code set 2.
-        */
-
-       kbd_write(KBD_DATA_REG, KBD_CMD_RESET);
-       if (kbd_wait_for_input() != KBD_REPLY_ACK)
-               return "Keyboard reset failed, no ACK";
-       if (kbd_wait_for_input() != KBD_REPLY_POR)
-               return "Keyboard reset failed, no POR";
-
-       /*
-        * Set keyboard controller mode. During this, the keyboard should be
-        * in the disabled state.
-        */
-
-       kbd_write(KBD_DATA_REG, KBD_CMD_DISABLE);
-       if (kbd_wait_for_input() != KBD_REPLY_ACK)
-               return "Disable keyboard: no ACK";
+static void kbd_write(int address, int data);
+static unsigned char handle_kbd_event(void);
 
-       kbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE);
-       kbd_write(KBD_DATA_REG, KBD_MODE_KBD_INT
-                             | KBD_MODE_SYS
-                             | KBD_MODE_DISABLE_MOUSE
-                             | KBD_MODE_KCC);
-
-       kbd_write(KBD_DATA_REG, KBD_CMD_ENABLE);
-       if (kbd_wait_for_input() != KBD_REPLY_ACK)
-               return "Enable keyboard: no ACK";
+spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
 
-       /*
-        * Finally, set the typematic rate to maximum.
-        */
+/* used only by send_data - set by keyboard_interrupt */
+static volatile unsigned char reply_expected = 0;
+static volatile unsigned char acknowledge = 0;
+static volatile unsigned char resend = 0;
 
-       kbd_write(KBD_DATA_REG, KBD_CMD_SET_RATE);
-       if (kbd_wait_for_input() != KBD_REPLY_ACK)
-               return "Set rate: no ACK";
-       kbd_write(KBD_DATA_REG, 0x00);
-       if (kbd_wait_for_input() != KBD_REPLY_ACK)
-               return "Set rate: no ACK";
 
-       return NULL;
-}
+#if defined CONFIG_PSMOUSE
+/*
+ *     PS/2 Auxiliary Device
+ */
 
-__initfunc(static void initialize_kbd(void))
-{
-       char *msg;
+static int __init psaux_init(void);
 
-       disable_irq(KEYBOARD_IRQ);
-       msg = initialize_kbd2();
-       enable_irq(KEYBOARD_IRQ);
+static struct aux_queue *queue;        /* Mouse data buffer. */
+static int aux_count = 0;
+/* used when we send commands to the mouse that expect an ACK. */
+static unsigned char mouse_reply_expected = 0;
 
-       if (msg)
-               printk(KERN_WARNING "initialize_kbd: %s\n", msg);
-}
+#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)
+#define AUX_INTS_ON  (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
 
-#endif /* INIT_KBD */
+#define MAX_RETRIES    60              /* some aux operations take long time*/
 
-unsigned char kbd_read_mask = KBD_STAT_OBF; /* Modified by psaux.c */
+#ifndef AUX_IRQ
+# define AUX_IRQ       12
+#endif
 
-/* used only by send_data - set by keyboard_interrupt */
-static volatile unsigned char reply_expected = 0;
-static volatile unsigned char acknowledge = 0;
-static volatile unsigned char resend = 0;
+#endif /* CONFIG_PSMOUSE */
 
 /*
- *     Wait for keyboard controller input buffer is empty.
+ * Wait for keyboard controller input buffer to drain.
+ *
+ * Don't use 'jiffies' so that we don't depend on
+ * interrupts..
+ *
+ * Quote from PS/2 System Reference Manual:
+ *
+ * "Address hex 0060 and address hex 0064 should be written only when
+ * the input-buffer-full bit and output-buffer-full bit in the
+ * Controller Status register are set 0."
  */
 
-static inline void kb_wait(void)
+static void kb_wait(void)
 {
-       unsigned long start = jiffies;
+       unsigned long timeout = KBC_TIMEOUT;
 
        do {
-               if (! (inb_p(KBD_STATUS_REG) & KBD_STAT_IBF))
+               /*
+                * "handle_kbd_event()" will handle any incoming events
+                * while we wait - keypresses or mouse movement.
+                */
+               unsigned char status = handle_kbd_event();
+
+               if (! (status & KBD_STAT_IBF))
                        return;
-       } while (jiffies - start < KBC_TIMEOUT);
+               mdelay(1);
+               timeout--;
+       } while (timeout);
 #ifdef KBD_REPORT_TIMEOUTS
-       printk(KERN_WARNING "Keyboard timed out\n");
+       printk(KERN_WARNING "Keyboard timed out[1]\n");
 #endif
 }
 
@@ -325,8+239,6 @@ static unsigned char e0_keys[128] = {
   0, 0, 0, 0, 0, 0, 0, 0                             /* 0x78-0x7f */
 };
 
-static unsigned int prev_scancode = 0;   /* remember E0, E1 */
-
 int pckbd_setkeycode(unsigned int scancode, unsigned int keycode)
 {
        if (scancode < SC_LIM || scancode > 255 || keycode > 127)
@@ -346,20+258,6 @@ int pckbd_getkeycode(unsigned int scancode)
            e0_keys[scancode - 128];
 }
 
-#if DISABLE_KBD_DURING_INTERRUPTS
-static inline void send_cmd(unsigned char c)
-{
-       kb_wait();
-       outb(c, KBD_CNTL_REG);
-}
-
-#define disable_keyboard()     do { send_cmd(KBD_CCMD_KBD_DISABLE); kb_wait(); } while (0)
-#define enable_keyboard()      send_cmd(KBD_CCMD_KBD_ENABLE)
-#else
-#define disable_keyboard()     /* nothing */
-#define enable_keyboard()      /* nothing */
-#endif
-
 static int do_acknowledge(unsigned char scancode)
 {
        if (reply_expected) {
@@ -383,41+281,28 @@ static int do_acknowledge(unsigned char scancode)
                       scancode);
 #endif
        }
-       if (scancode == 0) {
-#ifdef KBD_REPORT_ERR
-               printk(KERN_INFO "Keyboard buffer overflow\n");
-#endif
-               prev_scancode = 0;
-               return 0;
-       }
        return 1;
 }
 
-int pckbd_pretranslate(unsigned char scancode, char raw_mode)
+int pckbd_translate(unsigned char scancode, unsigned char *keycode,
+                   char raw_mode)
 {
-       if (scancode == 0xff) {
-               /* in scancode mode 1, my ESC key generates 0xff */
-               /* the calculator keys on a FOCUS 9000 generate 0xff */
-#ifndef KBD_IS_FOCUS_9000
-#ifdef KBD_REPORT_ERR
-               if (!raw_mode)
-                 printk(KERN_DEBUG "Keyboard error\n");
-#endif
-#endif
-               prev_scancode = 0;
-               return 0;
-       }
+       static int prev_scancode = 0;
 
+       /* special prefix scancodes.. */
        if (scancode == 0xe0 || scancode == 0xe1) {
                prev_scancode = scancode;
                return 0;
-       }
-       return 1;
-}
+       }
+
+       /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */
+       if (scancode == 0x00 || scancode == 0xff) {
+               prev_scancode = 0;
+               return 0;
+       }
+
+       scancode &= 0x7f;
 
-int pckbd_translate(unsigned char scancode, unsigned char *keycode,
-                   char raw_mode)
-{
        if (prev_scancode) {
          /*
           * usually it will be 0xe0, but a Pause key generates
@@ -504,59+389,106 @@ char pckbd_unexpected_up(unsigned char keycode)
            return 0200;
 }
 
-static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static inline void handle_mouse_event(unsigned char scancode)
 {
-       unsigned char status;
+#ifdef CONFIG_PSMOUSE
+       if (mouse_reply_expected) {
+               if (scancode == AUX_ACK) {
+                       mouse_reply_expected--;
+                       return;
+               }
+               mouse_reply_expected = 0;
+       }
 
-       kbd_pt_regs = regs;
-       disable_keyboard();
+       add_mouse_randomness(scancode);
+       if (aux_count) {
+               int head = queue->head;
+
+               queue->buf[head] = scancode;
+               head = (head + 1) & (AUX_BUF_SIZE-1);
+               if (head != queue->tail) {
+                       queue->head = head;
+                       if (queue->fasync)
+                               kill_fasync(queue->fasync, SIGIO);
+                       wake_up_interruptible(&queue->proc_list);
+               }
+       }
+#endif
+}
 
-       status = inb_p(KBD_STATUS_REG);
-       do {
-               unsigned char scancode;
+/*
+ * This reads the keyboard status port, and does the
+ * appropriate action.
+ *
+ * It requires that we hold the keyboard controller
+ * spinlock.
+ */
+static unsigned char handle_kbd_event(void)
+{
+       unsigned char status = inb(KBD_STATUS_REG);
 
-               /* mouse data? */
-               if (status & kbd_read_mask & KBD_STAT_MOUSE_OBF)
-                       break;
+       while (status & KBD_STAT_OBF) {
+               unsigned char scancode;
 
                scancode = inb(KBD_DATA_REG);
-               if ((status & KBD_STAT_OBF) && do_acknowledge(scancode))
-                       handle_scancode(scancode);
+
+               if (status & KBD_STAT_MOUSE_OBF) {
+                       handle_mouse_event(scancode);
+               } else {
+                       if (do_acknowledge(scancode))
+                               handle_scancode(scancode, !(scancode & 0x80));
+                       mark_bh(KEYBOARD_BH);
+               }
 
                status = inb(KBD_STATUS_REG);
-       } while (status & KBD_STAT_OBF);
+       }
+
+       return status;
+}
+
+
+static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned long flags;
+
+       kbd_pt_regs = regs;
 
-       mark_bh(KEYBOARD_BH);
-       enable_keyboard();
+       spin_lock_irqsave(&kbd_controller_lock, flags);
+       handle_kbd_event();
+       spin_unlock_irqrestore(&kbd_controller_lock, flags);
 }
 
 /*
  * send_data sends a character to the keyboard and waits
  * for an acknowledge, possibly retrying if asked to. Returns
  * the success status.
+ *
+ * Don't use 'jiffies', so that we don't depend on interrupts
  */
 static int send_data(unsigned char data)
 {
        int retries = 3;
-       unsigned long start;
 
        do {
-               kb_wait();
-               acknowledge = 0;
+               unsigned long timeout = KBD_TIMEOUT;
+
+               acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */
                resend = 0;
                reply_expected = 1;
-               outb_p(data, KBD_DATA_REG);
-               start = jiffies;
-               do {
+               kbd_write(KBD_DATA_REG, data);
+               for (;;) {
                        if (acknowledge)
                                return 1;
-                       if (jiffies - start >= KBD_TIMEOUT) {
+                       if (resend)
+                               break;
+                       mdelay(1);
+                       if (!--timeout) {
 #ifdef KBD_REPORT_TIMEOUTS
-                               printk(KERN_WARNING "Keyboard timeout\n");
+                               printk(KERN_WARNING "Keyboard timeout[2]\n");
 #endif
                                return 0;
                        }
-               } while (!resend);
+               }
        } while (retries-- > 0);
 #ifdef KBD_REPORT_TIMEOUTS
        printk(KERN_WARNING "keyboard: Too many NACKs -- noisy kbd cable?\n");
@@ -570,11+502,481 @@ void pckbd_leds(unsigned char leds)
            send_data(KBD_CMD_ENABLE);  /* re-enable kbd if any errors */
 }
 
-__initfunc(void pckbd_init_hw(void))
+/*
+ * In case we run on a non-x86 hardware we need to initialize both the
+ * keyboard controller and the keyboard.  On a x86, the BIOS will
+ * already have initialized them.
+ *
+ * Some x86 BIOSes do not correctly initialize the keyboard, so the
+ * "kbd-reset" command line options can be given to force a reset.
+ * [Ranger]
+ */
+#ifdef __i386__
+ int kbd_startup_reset __initdata = 0;
+#else
+ int kbd_startup_reset __initdata = 1;
+#endif
+
+/* for "kbd-reset" cmdline param */
+static int __init kbd_reset_setup(char *str)
 {
-       request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL);
-       request_region(0x60, 16, "keyboard");
-#ifdef INIT_KBD
-       initialize_kbd();
+       kbd_startup_reset = 1;
+       return 1;
+}
+
+__setup("kbd-reset", kbd_reset_setup);
+
+#define KBD_NO_DATA    (-1)    /* No data */
+#define KBD_BAD_DATA   (-2)    /* Parity or other error */
+
+static int __init kbd_read_input(void)
+{
+       int retval = KBD_NO_DATA;
+       unsigned char status;
+
+       status = inb(KBD_STATUS_REG);
+       if (status & KBD_STAT_OBF) {
+               unsigned char data = inb(KBD_DATA_REG);
+
+               retval = data;
+               if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
+                       retval = KBD_BAD_DATA;
+       }
+       return retval;
+}
+
+static void __init kbd_clear_input(void)
+{
+       int maxread = 100;      /* Random number */
+
+       do {
+               if (kbd_read_input() == KBD_NO_DATA)
+                       break;
+       } while (--maxread);
+}
+
+static int __init kbd_wait_for_input(void)
+{
+       long timeout = KBD_INIT_TIMEOUT;
+
+       do {
+               int retval = kbd_read_input();
+               if (retval >= 0)
+                       return retval;
+               mdelay(1);
+       } while (--timeout);
+       return -1;
+}
+
+static void kbd_write(int address, int data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&kbd_controller_lock, flags);
+       kb_wait();
+       outb(data, address);
+       spin_unlock_irqrestore(&kbd_controller_lock, flags);
+}
+
+#if defined CONFIG_PSMOUSE
+static void kbd_write_cmd(int cmd)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&kbd_controller_lock, flags);
+       kb_wait();
+       outb(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG);
+       kb_wait();
+       outb(cmd, KBD_DATA_REG);
+       spin_unlock_irqrestore(&kbd_controller_lock, flags);
+}
+#endif /* CONFIG_PSMOUSE */
+
+static char * __init initialize_kbd(void)
+{
+       int status;
+
+       /*
+        * Test the keyboard interface.
+        * This seems to be the only way to get it going.
+        * If the test is successful a x55 is placed in the input buffer.
+        */
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_SELF_TEST);
+       if (kbd_wait_for_input() != 0x55)
+               return "Keyboard failed self test";
+
+       /*
+        * Perform a keyboard interface test.  This causes the controller
+        * to test the keyboard clock and data lines.  The results of the
+        * test are placed in the input buffer.
+        */
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_TEST);
+       if (kbd_wait_for_input() != 0x00)
+               return "Keyboard interface failed self test";
+
+       /*
+        * Enable the keyboard by allowing the keyboard clock to run.
+        */
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_ENABLE);
+
+       /*
+        * Reset keyboard. If the read times out
+        * then the assumption is that no keyboard is
+        * plugged into the machine.
+        * This defaults the keyboard to scan-code set 2.
+        *
+        * Set up to try again if the keyboard asks for RESEND.
+        */
+       do {
+               kbd_write(KBD_DATA_REG, KBD_CMD_RESET);
+               status = kbd_wait_for_input();
+               if (status == KBD_REPLY_ACK)
+                       break;
+               if (status != KBD_REPLY_RESEND)
+                       return "Keyboard reset failed, no ACK";
+       } while (1);
+
+       if (kbd_wait_for_input() != KBD_REPLY_POR)
+               return "Keyboard reset failed, no POR";
+
+       /*
+        * Set keyboard controller mode. During this, the keyboard should be
+        * in the disabled state.
+        *
+        * Set up to try again if the keyboard asks for RESEND.
+        */
+       do {
+               kbd_write(KBD_DATA_REG, KBD_CMD_DISABLE);
+               status = kbd_wait_for_input();
+               if (status == KBD_REPLY_ACK)
+                       break;
+               if (status != KBD_REPLY_RESEND)
+                       return "Disable keyboard: no ACK";
+       } while (1);
+
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE);
+       kbd_write(KBD_DATA_REG, KBD_MODE_KBD_INT
+                             | KBD_MODE_SYS
+                             | KBD_MODE_DISABLE_MOUSE
+                             | KBD_MODE_KCC);
+
+       /* ibm powerpc portables need this to use scan-code set 1 -- Cort */
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_READ_MODE);
+       if (!(kbd_wait_for_input() & KBD_MODE_KCC)) {
+               /*
+                * If the controller does not support conversion,
+                * Set the keyboard to scan-code set 1.
+                */
+               kbd_write(KBD_DATA_REG, 0xF0);
+               kbd_wait_for_input();
+               kbd_write(KBD_DATA_REG, 0x01);
+               kbd_wait_for_input();
+       }
+
+       
+       kbd_write(KBD_DATA_REG, KBD_CMD_ENABLE);
+       if (kbd_wait_for_input() != KBD_REPLY_ACK)
+               return "Enable keyboard: no ACK";
+
+       /*
+        * Finally, set the typematic rate to maximum.
+        */
+       kbd_write(KBD_DATA_REG, KBD_CMD_SET_RATE);
+       if (kbd_wait_for_input() != KBD_REPLY_ACK)
+               return "Set rate: no ACK";
+       kbd_write(KBD_DATA_REG, 0x00);
+       if (kbd_wait_for_input() != KBD_REPLY_ACK)
+               return "Set rate: no ACK";
+
+       return NULL;
+}
+
+void __init pckbd_init_hw(void)
+{
+       /* Flush any pending input. */
+       kbd_clear_input();
+
+       if (kbd_startup_reset) {
+               char *msg = initialize_kbd();
+               if (msg)
+                       printk(KERN_WARNING "initialize_kbd: %s\n", msg);
+       }
+
+#if defined CONFIG_PSMOUSE
+       psaux_init();
 #endif
+
+       /* Ok, finally allocate the IRQ, and off we go.. */
+       request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL);
 }
+
+#if defined CONFIG_PSMOUSE
+
+/*
+ * Check if this is a dual port controller.
+ */
+static int __init detect_auxiliary_port(void)
+{
+       unsigned long flags;
+       int loops = 10;
+       int retval = 0;
+
+       spin_lock_irqsave(&kbd_controller_lock, flags);
+
+       /* Put the value 0x5A in the output buffer using the "Write
+        * Auxiliary Device Output Buffer" command (0xD3). Poll the
+        * Status Register for a while to see if the value really
+        * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF
+        * bit is also set to 1 in the Status Register, we assume this
+        * controller has an Auxiliary Port (a.k.a. Mouse Port).
+        */
+       kb_wait();
+       outb(KBD_CCMD_WRITE_AUX_OBUF, KBD_CNTL_REG);
+
+       kb_wait();
+       outb(0x5a, KBD_DATA_REG); /* 0x5a is a random dummy value. */
+
+       do {
+               unsigned char status = inb(KBD_STATUS_REG);
+
+               if (status & KBD_STAT_OBF) {
+                       (void) inb(KBD_DATA_REG);
+                       if (status & KBD_STAT_MOUSE_OBF) {
+                               printk(KERN_INFO "Detected PS/2 Mouse Port.\n");
+                               retval = 1;
+                       }
+                       break;
+               }
+               mdelay(1);
+       } while (--loops);
+       spin_unlock_irqrestore(&kbd_controller_lock, flags);
+
+       return retval;
+}
+
+/*
+ * Send a byte to the mouse.
+ */
+static void aux_write_dev(int val)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&kbd_controller_lock, flags);
+       kb_wait();
+       outb(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG);
+       kb_wait();
+       outb(val, KBD_DATA_REG);
+       spin_unlock_irqrestore(&kbd_controller_lock, flags);
+}
+
+/*
+ * Send a byte to the mouse & handle returned ack
+ */
+static void aux_write_ack(int val)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&kbd_controller_lock, flags);
+       kb_wait();
+       outb(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG);
+       kb_wait();
+       outb(val, KBD_DATA_REG);
+       /* we expect an ACK in response. */
+       mouse_reply_expected++;
+       kb_wait();
+       spin_unlock_irqrestore(&kbd_controller_lock, flags);
+}
+
+static unsigned char get_from_queue(void)
+{
+       unsigned char result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&kbd_controller_lock, flags);
+       result = queue->buf[queue->tail];
+       queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
+       spin_unlock_irqrestore(&kbd_controller_lock, flags);
+       return result;
+}
+
+
+static inline int queue_empty(void)
+{
+       return queue->head == queue->tail;
+}
+
+static int fasync_aux(int fd, struct file *filp, int on)
+{
+       int retval;
+
+       retval = fasync_helper(fd, filp, on, &queue->fasync);
+       if (retval < 0)
+               return retval;
+       return 0;
+}
+
+
+/*
+ * Random magic cookie for the aux device
+ */
+#define AUX_DEV ((void *)queue)
+
+static int release_aux(struct inode * inode, struct file * file)
+{
+       fasync_aux(-1, file, 0);
+       if (--aux_count)
+               return 0;
+       kbd_write_cmd(AUX_INTS_OFF);                        /* Disable controller ints */
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_MOUSE_DISABLE);
+       free_irq(AUX_IRQ, AUX_DEV);
+       return 0;
+}
+
+/*
+ * Install interrupt handler.
+ * Enable auxiliary device.
+ */
+
+static int open_aux(struct inode * inode, struct file * file)
+{
+       if (aux_count++) {
+               return 0;
+       }
+       queue->head = queue->tail = 0;          /* Flush input queue */
+       if (request_irq(AUX_IRQ, keyboard_interrupt, SA_SHIRQ, "PS/2 Mouse", AUX_DEV)) {
+               aux_count--;
+               return -EBUSY;
+       }
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_MOUSE_ENABLE); /* Enable the
+                                                          auxiliary port on
+                                                          controller. */
+       aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
+       kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
+
+       return 0;
+}
+
+/*
+ * Put bytes from input queue to buffer.
+ */
+
+static ssize_t read_aux(struct file * file, char * buffer,
+                       size_t count, loff_t *ppos)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       ssize_t i = count;
+       unsigned char c;
+
+       if (queue_empty()) {
+               if (file->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               add_wait_queue(&queue->proc_list, &wait);
+repeat:
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (queue_empty() && !signal_pending(current)) {
+                       schedule();
+                       goto repeat;
+               }
+               current->state = TASK_RUNNING;
+               remove_wait_queue(&queue->proc_list, &wait);
+       }
+       while (i > 0 && !queue_empty()) {
+               c = get_from_queue();
+               put_user(c, buffer++);
+               i--;
+       }
+       if (count-i) {
+               file->f_dentry->d_inode->i_atime = CURRENT_TIME;
+               return count-i;
+       }
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+       return 0;
+}
+
+/*
+ * Write to the aux device.
+ */
+
+static ssize_t write_aux(struct file * file, const char * buffer,
+                        size_t count, loff_t *ppos)
+{
+       ssize_t retval = 0;
+
+       if (count) {
+               ssize_t written = 0;
+
+               if (count > 32)
+                       count = 32; /* Limit to 32 bytes. */
+               do {
+                       char c;
+                       get_user(c, buffer++);
+                       aux_write_dev(c);
+                       written++;
+               } while (--count);
+               retval = -EIO;
+               if (written) {
+                       retval = written;
+                       file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+               }
+       }
+
+       return retval;
+}
+
+static unsigned int aux_poll(struct file *file, poll_table * wait)
+{
+       poll_wait(file, &queue->proc_list, wait);
+       if (!queue_empty())
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
+
+struct file_operations psaux_fops = {
+       NULL,           /* seek */
+       read_aux,
+       write_aux,
+       NULL,           /* readdir */
+       aux_poll,
+       NULL,           /* ioctl */
+       NULL,           /* mmap */
+       open_aux,
+       NULL,           /* flush */
+       release_aux,
+       NULL,
+       fasync_aux,
+};
+
+/*
+ * Initialize driver.
+ */
+static struct miscdevice psaux_mouse = {
+       PSMOUSE_MINOR, "psaux", &psaux_fops
+};
+
+static int __init psaux_init(void)
+{
+       if (!detect_auxiliary_port())
+               return -EIO;
+
+       misc_register(&psaux_mouse);
+       queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
+       memset(queue, 0, sizeof(*queue));
+       queue->head = queue->tail = 0;
+       init_waitqueue_head(&queue->proc_list);
+
+#ifdef INITIALIZE_MOUSE
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */
+       aux_write_ack(AUX_SET_SAMPLE);
+       aux_write_ack(100);                     /* 100 samples/sec */
+       aux_write_ack(AUX_SET_RES);
+       aux_write_ack(3);                       /* 8 counts per mm */
+       aux_write_ack(AUX_SET_SCALE21);         /* 2:1 scaling */
+#endif /* INITIALIZE_MOUSE */
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */
+       kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */
+
+       return 0;
+}
+
+#endif /* CONFIG_PSMOUSE */
close