--- linux/drivers/char/pc_keyb.c.orig	Wed Dec 13 10:21:43 2000
+++ linux/drivers/char/pc_keyb.c	Wed Dec 13 10:38:23 2000
@@ -13,6 +13,15 @@
  * Code fixes to handle mouse ACKs properly.
  * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
  *
+ * More work to handle mouse ACKs properly.  (Modified version of
+ * patch by Julian Bradfield <jcb@dcs.ed.ac.uk>.)
+ * Chris Hanson <cph@zurich.ai.mit.edu> 2000-12-11.
+ *
+ * Implement exclusive access mechanism for aux device.
+ * This permits grabbing the mouse away from the X server,
+ * which is needed by fancy mice that have configurable features.
+ * Chris Hanson <cph@zurich.ai.mit.edu> 2000-10-30.
+ *
  */
 
 #include <linux/config.h>
@@ -57,7 +66,9 @@
 static void kbd_write_command_w(int data);
 static void kbd_write_output_w(int data);
 #ifdef CONFIG_PSMOUSE
-static void aux_write_ack(int val);
+static void __aux_write_ack(int val);
+static void aux_kill_fasync(struct fasync_struct *fasync, int s);
+static int aux_release_ioctl(struct file *file);
 #endif
 
 spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
@@ -81,13 +92,30 @@
 
 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. */
+/* Used when we (as opposed to user programs using the aux device)
+   send commands to the mouse. */
 static unsigned char mouse_reply_expected = 0;
 
+/* Used to make sure we have received an ACK from the byte last
+   written to the mouse before writing another.  */
+static unsigned long mouse_ack_pending = 0;
+static struct wait_queue *mouse_ack_wait = NULL;
+/* How many jiffies to wait for the mouse to respond to a command with
+   an ACK.  25 msec is the maximum allowed delay for the hardware to
+   respond.  We round that up to the nearest jiffy.  */
+#define MOUSE_ACK_TIMEOUT ((25 * HZ + 999) / 1000)
+
 #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*/
+
+/* Support for exclusive access to the AUX device. */
+static struct file *aux_exclusive = 0;
+static struct wait_queue *aux_exclusive_wait = NULL;
+static unsigned long aux_last_write = 0;
+#define AUX_GRAB _IO('M', 1)
+#define AUX_RELEASE _IO('M', 2)
 #endif /* CONFIG_PSMOUSE */
 
 /*
@@ -392,6 +420,13 @@
 static inline void handle_mouse_event(unsigned char scancode)
 {
 #ifdef CONFIG_PSMOUSE
+	if (mouse_ack_pending) {
+		/* It needn't actually be an ack, it could be an echo;
+		   but every byte sent to the mouse results in a byte
+		   back. */
+		wake_up_interruptible(&mouse_ack_wait);
+		mouse_ack_pending = 0;
+	}
 	if (mouse_reply_expected) {
 		if (scancode == AUX_ACK) {
 			mouse_reply_expected--;
@@ -401,14 +436,7 @@
 	}
     else if(scancode == AUX_RECONNECT){
         queue->head = queue->tail = 0;  /* Flush input queue */
-        /* ping the mouse :) */
-	kb_wait();
-	kbd_write_command(KBD_CCMD_WRITE_MOUSE);
-	kb_wait();
-	kbd_write_output(AUX_ENABLE_DEV);
-	/* we expect an ACK in response. */
-	mouse_reply_expected++;
-	kb_wait();
+        __aux_write_ack(AUX_ENABLE_DEV);  /* ping the mouse :) */
         return;
     }
 
@@ -421,7 +449,7 @@
 		if (head != queue->tail) {
 			queue->head = head;
 			if (queue->fasync)
-				kill_fasync(queue->fasync, SIGIO);
+				aux_kill_fasync(queue->fasync, SIGIO);
 			wake_up_interruptible(&queue->proc_list);
 		}
 	}
@@ -429,6 +457,7 @@
 }
 
 static unsigned char kbd_exists = 1;
+static unsigned char status_mask = 0;	/* At probe time we want all */
 
 /*
  * This reads the keyboard status port, and does the
@@ -446,18 +475,21 @@
 		unsigned char scancode;
 
 		scancode = kbd_read_input();
-#  ifdef CHECK_RECONNECT_SCANCODE
-    printk(KERN_INFO "-=db=-: kbd_read_input() : scancode == %d\n",scancode);
-#  endif
-		if (status & KBD_STAT_MOUSE_OBF) {
-			handle_mouse_event(scancode);
-		} else {
-			kbd_exists = 1;
-			if (do_acknowledge(scancode))
-				handle_scancode(scancode, !(scancode & 0x80));
-			mark_bh(KEYBOARD_BH);
+		
+		/* Check for errors. Shouldnt ever happen but it does on Compaq
+		   Presario 16[89]. */
+		   
+		if(!(status& status_mask))
+		{
+			if (status & KBD_STAT_MOUSE_OBF) {
+				handle_mouse_event(scancode);
+			} else {
+				kbd_exists = 1;
+				if (do_acknowledge(scancode))
+					handle_scancode(scancode, !(scancode & 0x80));
+				mark_bh(KEYBOARD_BH);
+			}
 		}
-
 		status = kbd_read_status();
 		
 		if(!work--)
@@ -741,7 +773,8 @@
 #if defined CONFIG_PSMOUSE
 	psaux_init();
 #endif
-
+	/* Switch keyboard processing to checking error bits */
+	status_mask = KBD_STAT_GTO|KBD_STAT_PERR;
 	/* Ok, finally allocate the IRQ, and off we go.. */
 	kbd_request_irq(keyboard_interrupt);
 }
@@ -790,38 +823,128 @@
 	return retval;
 }
 
+static void mouse_ack_timeout(unsigned long data)
+{
+	wake_up_interruptible(&mouse_ack_wait);
+}
+
 /*
  * Send a byte to the mouse.
  */
-static void aux_write_dev(int val)
+static int aux_write_dev(int val, int dont_block, int handle_ack)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&kbd_controller_lock, flags);
 	kb_wait();
+	/* If we haven't yet received the ACK from the previous write,
+	   we must wait for it.  */
+	if (mouse_ack_pending) {
+		unsigned long expires;
+		struct timer_list timer;
+		struct wait_queue wait = { current, NULL };
+
+		if (dont_block) {
+			spin_unlock_irqrestore(&kbd_controller_lock, flags);
+			return -EAGAIN;
+		}
+		expires = mouse_ack_pending + MOUSE_ACK_TIMEOUT;
+		if (jiffies >= expires)
+			goto timed_out;
+
+		add_wait_queue(&mouse_ack_wait, &wait);
+		init_timer (&timer);
+		timer.expires = expires;
+		timer.data = 0;
+		timer.function = mouse_ack_timeout;
+		add_timer(&timer);
+
+		do {
+			current->state = TASK_INTERRUPTIBLE;
+			spin_unlock_irqrestore(&kbd_controller_lock, flags);
+			schedule();
+			spin_lock_irqsave(&kbd_controller_lock, flags);
+		} while (mouse_ack_pending
+			 && jiffies < expires
+			 && !signal_pending(current));
+
+		del_timer(&timer);
+		remove_wait_queue(&mouse_ack_wait, &wait);
+
+		if (mouse_ack_pending && jiffies >= expires) {
+		timed_out:
+			printk(KERN_WARNING "mouse ack timeout\n");
+			mouse_ack_pending = 0;
+			spin_unlock_irqrestore(&kbd_controller_lock, flags);
+			return -EIO;
+		}
+		if (signal_pending(current)) {
+			spin_unlock_irqrestore(&kbd_controller_lock, flags);
+			return -ERESTARTSYS;
+		}
+	}
 	kbd_write_command(KBD_CCMD_WRITE_MOUSE);
 	kb_wait();
 	kbd_write_output(val);
+	mouse_ack_pending = jiffies;
+	if (handle_ack)
+		/* We will deal with the ACK ourselves.  */
+		mouse_reply_expected++;
 	spin_unlock_irqrestore(&kbd_controller_lock, flags);
+	return 0;
 }
 
 /*
  * Send a byte to the mouse & handle returned ack
  */
-static void aux_write_ack(int val)
+static void __aux_write_ack(int val)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&kbd_controller_lock, flags);
 	kb_wait();
 	kbd_write_command(KBD_CCMD_WRITE_MOUSE);
 	kb_wait();
 	kbd_write_output(val);
-	/* we expect an ACK in response. */
+	mouse_ack_pending = jiffies;
+	/* We will deal with the ACK ourselves.  */
 	mouse_reply_expected++;
 	kb_wait();
+}
+
+#ifdef INITIALIZE_MOUSE
+static void aux_write_ack(int val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_controller_lock, flags);
+	__aux_write_ack(val);
 	spin_unlock_irqrestore(&kbd_controller_lock, flags);
 }
+#endif /* INITIALIZE_MOUSE */
+
+static void aux_kill_fasync(struct fasync_struct *fasync, int s)
+{
+	struct fasync_struct * fp;
+	struct fasync_struct fa;
+
+	fp = fasync;
+	/* If someone has grabbed the AUX device, send signal only to
+	   them and not to other processes.  We could do this directly
+	   if send_sigio was exported, but since it isn't we must
+	   synthesize a "struct fasync_struct" to pass to
+	   kill_fasync. */
+	if (aux_exclusive) {
+		while (1) {
+			if (!fp)
+				return;
+			if (fp->fa_file == aux_exclusive)
+				break;
+			fp = fp->fa_next;
+		}
+		fa = (*fp);
+		fa.fa_next = NULL;
+		fp = &fa;
+	}
+	kill_fasync(fp, s);
+}
 
 static unsigned char get_from_queue(void)
 {
@@ -860,6 +983,8 @@
 static int release_aux(struct inode * inode, struct file * file)
 {
 	fasync_aux(-1, file, 0);
+	if (aux_exclusive == file)
+		aux_release_ioctl(file);
 	if (--aux_count)
 		return 0;
 	kbd_write_cmd(AUX_INTS_OFF);			    /* Disable controller ints */
@@ -886,13 +1011,40 @@
 	kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE);	/* Enable the
 							   auxiliary port on
 							   controller. */
-	aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
+	aux_write_dev(AUX_ENABLE_DEV, 0, 1); /* Enable aux device */
 	kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
 
 	return 0;
 }
 
 /*
+ * Implement exclusive access mechanism.
+ */
+
+#define AUX_ACCESS_ALLOWED(file) (!aux_exclusive || aux_exclusive == (file))
+
+static ssize_t aux_wait_for_access(struct file * file)
+{
+	struct wait_queue wait = { current, NULL };
+
+	if (!AUX_ACCESS_ALLOWED(file)) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&aux_exclusive_wait, &wait);
+		do {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+		}
+		while (!AUX_ACCESS_ALLOWED(file)
+		       && !signal_pending(current));
+		remove_wait_queue(&aux_exclusive_wait, &wait);
+	}
+	if (!AUX_ACCESS_ALLOWED(file))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+/*
  * Put bytes from input queue to buffer.
  */
 
@@ -902,7 +1054,11 @@
 	struct wait_queue wait = { current, NULL };
 	ssize_t i = count;
 	unsigned char c;
+	ssize_t retval;
 
+	retval = aux_wait_for_access(file);
+	if (retval < 0)
+		return retval;
 	if (queue_empty()) {
 		if (file->f_flags & O_NONBLOCK)
 			return -EAGAIN;
@@ -937,23 +1093,32 @@
 static ssize_t write_aux(struct file * file, const char * buffer,
 			 size_t count, loff_t *ppos)
 {
-	ssize_t retval = 0;
-
+	ssize_t retval;
+ 
+	retval = aux_wait_for_access(file);
+	if (retval < 0)
+		return retval;
 	if (count) {
 		ssize_t written = 0;
 
+		retval = -EIO;
 		if (count > 32)
 			count = 32; /* Limit to 32 bytes. */
 		do {
 			char c;
+			int write_result;
 			get_user(c, buffer++);
-			aux_write_dev(c);
+			write_result = aux_write_dev(c, file->f_flags & O_NONBLOCK, 0);
+			if (write_result) {
+				retval = write_result;
+				break;
+			}
 			written++;
 		} while (--count);
-		retval = -EIO;
 		if (written) {
 			retval = written;
 			file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+			aux_last_write = jiffies;
 		}
 	}
 
@@ -963,18 +1128,82 @@
 static unsigned int aux_poll(struct file *file, poll_table * wait)
 {
 	poll_wait(file, &queue->proc_list, wait);
-	if (!queue_empty())
+	if (AUX_ACCESS_ALLOWED(file) && !queue_empty())
 		return POLLIN | POLLRDNORM;
 	return 0;
 }
 
+/* Wait this long after last write to mouse before allowing AUX_GRAB
+   to happen.  This ensures that any outstanding mouse command is
+   completed.  The ACK from the command is supposed to arrive in 25
+   msec, and each subsequent status bytes are supposed to arrive
+   within 20 msec, so a command with 5 status bytes (I don't know any
+   this long) might take 125 msec.  Fudge this up a bit to account for
+   additional delay introduced by bus locking.  */
+#define AUX_GRAB_MIN_TIME (aux_last_write + (((200 * HZ) + 500) / 1000))
+#define AUX_GRAB_ALLOWED (aux_exclusive == 0 && (jiffies >= AUX_GRAB_MIN_TIME))
+
+static void aux_grab_timeout(unsigned long data)
+{
+	wake_up_interruptible(&aux_exclusive_wait);
+}
+
+static int aux_grab_ioctl(struct file *file)
+{
+	struct wait_queue wait = { current, NULL };
+	struct timer_list timer;
+
+	if (aux_exclusive == file)
+		return -EINVAL;
+	if (!AUX_GRAB_ALLOWED) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		init_timer (&timer);
+		timer.expires = AUX_GRAB_MIN_TIME;
+		timer.data = 0;
+		timer.function = aux_grab_timeout;
+		add_wait_queue(&aux_exclusive_wait, &wait);
+		add_timer(&timer);
+		do {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+		}
+		while (!AUX_GRAB_ALLOWED && !signal_pending(current));
+		del_timer(&timer);
+		remove_wait_queue(&aux_exclusive_wait, &wait);
+	}
+	if (!AUX_GRAB_ALLOWED)
+		return -ERESTARTSYS;
+	aux_exclusive = file;
+	return 0;
+}
+
+static int aux_release_ioctl(struct file *file)
+{
+	if (aux_exclusive != file)
+		return -ENOENT;
+	aux_exclusive = 0;
+	wake_up_interruptible(&aux_exclusive_wait);
+	return 0;
+}
+
+static int aux_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case AUX_GRAB: return aux_grab_ioctl(file);
+	case AUX_RELEASE: return aux_release_ioctl(file);
+	default: return -EINVAL;
+	}
+}
+
 struct file_operations psaux_fops = {
 	NULL,		/* seek */
 	read_aux,
 	write_aux,
 	NULL, 		/* readdir */
 	aux_poll,
-	NULL, 		/* ioctl */
+	aux_ioctl,
 	NULL,		/* mmap */
 	open_aux,
 	NULL,		/* flush */
@@ -1011,6 +1240,8 @@
 #endif /* INITIALIZE_MOUSE */
 	kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */
 	kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */
+
+	aux_last_write = jiffies;
 
 	return 0;
 }
