Import 2.3.18pre1
[davej-history.git] / drivers / char / pc_keyb.c
index 47d5c20..b4b6069 100644 (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 <asm/spinlock.h>
+#include <linux/spinlock.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/tty.h>
 #include <asm/uaccess.h>
 #include <asm/irq.h>
 #include <asm/system.h>
-#include <asm/irq.h>
 
 /* Some configuration switches are present in the include file... */
 
@@ -74,16+76,18 @@ static int __init psaux_init(void);
 
 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;
 
 #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)
 
 #define MAX_RETRIES    60              /* some aux operations take long time*/
-#if defined(__alpha__) && !defined(CONFIG_PCI)
-# define AUX_IRQ       9               /* Jensen is odd indeed */
-#else
+
+#ifndef AUX_IRQ
 # define AUX_IRQ       12
 #endif
+
 #endif /* CONFIG_PSMOUSE */
 
 /*
@@ -99,7+103,7 @@ static int aux_count = 0;
  * Controller Status register are set 0."
  */
 
-static inline void kb_wait(void)
+static void kb_wait(void)
 {
        unsigned long timeout = KBC_TIMEOUT;
 
@@ -235,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)
@@ -279,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
@@ -400,6+389,33 @@ char pckbd_unexpected_up(unsigned char keycode)
            return 0200;
 }
 
+static inline void handle_mouse_event(unsigned char scancode)
+{
+#ifdef CONFIG_PSMOUSE
+       if (mouse_reply_expected) {
+               if (scancode == AUX_ACK) {
+                       mouse_reply_expected--;
+                       return;
+               }
+               mouse_reply_expected = 0;
+       }
+
+       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
+}
+
 /*
  * This reads the keyboard status port, and does the
  * appropriate action.
@@ -417,24+433,10 @@ static unsigned char handle_kbd_event(void)
                scancode = inb(KBD_DATA_REG);
 
                if (status & KBD_STAT_MOUSE_OBF) {
-#ifdef CONFIG_PSMOUSE
-                       /* Mouse data. */
-                       if (aux_count) {
-                               int head = queue->head;
-                               queue->buf[head] = scancode;
-                               add_mouse_randomness(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
+                       handle_mouse_event(scancode);
                } else {
                        if (do_acknowledge(scancode))
-                               handle_scancode(scancode);
+                               handle_scancode(scancode, !(scancode & 0x80));
                        mark_bh(KEYBOARD_BH);
                }
 
@@ -516,11+518,14 @@ void pckbd_leds(unsigned char leds)
 #endif
 
 /* for "kbd-reset" cmdline param */
-void __init kbd_reset_setup(char *str, int *ints)
+static int __init kbd_reset_setup(char *str)
 {
        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 */
 
@@ -688,9+693,6 @@ static char * __init initialize_kbd(void)
 
 void __init pckbd_init_hw(void)
 {
-       /* Get the keyboard controller registers (incomplete decode) */
-       request_region(0x60, 16, "keyboard");
-
        /* Flush any pending input. */
        kbd_clear_input();
 
@@ -716,9+718,7 @@ void __init pckbd_init_hw(void)
 static int __init detect_auxiliary_port(void)
 {
        unsigned long flags;
-       unsigned char status;
-       unsigned char val;
-       int loops = 5;
+       int loops = 10;
        int retval = 0;
 
        spin_lock_irqsave(&kbd_controller_lock, flags);
@@ -736,20+736,19 @@ static int __init detect_auxiliary_port(void)
        kb_wait();
        outb(0x5a, KBD_DATA_REG); /* 0x5a is a random dummy value. */
 
-       status = inb(KBD_STATUS_REG);
-       while (!(status & KBD_STAT_OBF) && loops--) {
-               mdelay(1);
-               status = inb(KBD_STATUS_REG);
-       }
+       do {
+               unsigned char status = inb(KBD_STATUS_REG);
 
-       if (status & KBD_STAT_OBF) {
-               val = inb(KBD_DATA_REG);
-               if (status & KBD_STAT_MOUSE_OBF) {
-                       printk(KERN_INFO "Detected PS/2 Mouse Port.\n");
-                       retval = 1;
+               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;
@@ -770,16+769,33 @@ static void aux_write_dev(int val)
        spin_unlock_irqrestore(&kbd_controller_lock, flags);
 }
 
-static unsigned int get_from_queue(void)
+/*
+ * 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 int result;
+       unsigned char result;
        unsigned long flags;
 
-       save_flags(flags);
-       cli();
+       spin_lock_irqsave(&kbd_controller_lock, flags);
        result = queue->buf[queue->tail];
        queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
-       restore_flags(flags);
+       spin_unlock_irqrestore(&kbd_controller_lock, flags);
        return result;
 }
 
@@ -811,7+827,7 @@ static int release_aux(struct inode * inode, struct file * file)
        if (--aux_count)
                return 0;
        kbd_write_cmd(AUX_INTS_OFF);                        /* Disable controller ints */
-       kbd_write(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG);
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_MOUSE_DISABLE);
        free_irq(AUX_IRQ, AUX_DEV);
        return 0;
 }
@@ -831,10+847,10 @@ static int open_aux(struct inode * inode, struct file * file)
                aux_count--;
                return -EBUSY;
        }
-       kbd_write(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable the
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_MOUSE_ENABLE); /* Enable the
                                                           auxiliary port on
                                                           controller. */
-       aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */
+       aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
        kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
 
        return 0;
@@ -847,7+863,7 @@ static int open_aux(struct inode * inode, struct file * file)
 static ssize_t read_aux(struct file * file, char * buffer,
                        size_t count, loff_t *ppos)
 {
-       struct wait_queue wait = { current, NULL };
+       DECLARE_WAITQUEUE(wait, current);
        ssize_t i = count;
        unsigned char c;
 
@@ -856,7+872,7 @@ static ssize_t read_aux(struct file * file, char * buffer,
                        return -EAGAIN;
                add_wait_queue(&queue->proc_list, &wait);
 repeat:
-               current->state = TASK_INTERRUPTIBLE;
+               set_current_state(TASK_INTERRUPTIBLE);
                if (queue_empty() && !signal_pending(current)) {
                        schedule();
                        goto repeat;
@@ -947,17+963,17 @@ static int __init psaux_init(void)
        queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
        memset(queue, 0, sizeof(*queue));
        queue->head = queue->tail = 0;
-       queue->proc_list = NULL;
+       init_waitqueue_head(&queue->proc_list);
 
 #ifdef INITIALIZE_MOUSE
-       kbd_write(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux. */
-       aux_write_dev(AUX_SET_SAMPLE);
-       aux_write_dev(100);                     /* 100 samples/sec */
-       aux_write_dev(AUX_SET_RES);
-       aux_write_dev(3);                       /* 8 counts per mm */
-       aux_write_dev(AUX_SET_SCALE21);         /* 2:1 scaling */
+       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_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable aux device. */
+       kbd_write(KBD_CNTL_REG, KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */
        kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */
 
        return 0;
close