--- linux-2.4.9/drivers/char/pc_keyb.c.orig	Tue Aug 14 18:49:50 2001
+++ linux-2.4.9/drivers/char/pc_keyb.c	Thu Aug 16 22:29:33 2001
@@ -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>
@@ -64,6 +73,8 @@
 static void aux_write_ack(int val);
 static void __aux_write_ack(int val);
 static int aux_reconnect = 0;
+static void aux_kill_fasync(struct fasync_struct **fasync, int sig, int band);
+static int aux_release_ioctl(struct file *file);
 #endif
 
 static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
@@ -87,13 +98,30 @@
  
 static struct aux_queue *queue;	/* Mouse data buffer. */
 static int aux_count;
-/* 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;
 
+/* 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;
+static wait_queue_head_t mouse_ack_wait;
+/* 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;
+static wait_queue_head_t aux_exclusive_wait;
+static unsigned long aux_last_write;
+#define AUX_GRAB _IO('M', 1)
+#define AUX_RELEASE _IO('M', 2)
 #endif /* CONFIG_PSMOUSE */
 
 /*
@@ -399,6 +427,13 @@
 {
 #ifdef CONFIG_PSMOUSE
 	static unsigned char prev_code;
+ 	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--;
@@ -423,7 +458,7 @@
 		head = (head + 1) & (AUX_BUF_SIZE-1);
 		if (head != queue->tail) {
 			queue->head = head;
-			kill_fasync(&queue->fasync, SIGIO, POLL_IN);
+			aux_kill_fasync(&queue->fasync, SIGIO, POLL_IN);
 			wake_up_interruptible(&queue->proc_list);
 		}
 	}
@@ -895,19 +930,75 @@
 	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;
+		DECLARE_WAITQUEUE(wait, current);
+
+		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;
 }
 
 /*
@@ -919,11 +1010,13 @@
 	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;
@@ -932,6 +1025,33 @@
 	__aux_write_ack(val);
 	spin_unlock_irqrestore(&kbd_controller_lock, flags);
 }
+#endif /* INITIALIZE_MOUSE */
+
+static void aux_kill_fasync(struct fasync_struct **fasync, int sig, int band)
+{
+	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, sig, band);
+}
 
 static unsigned char get_from_queue(void)
 {
@@ -971,6 +1091,8 @@
 {
 	lock_kernel();
 	fasync_aux(-1, file, 0);
+	if (aux_exclusive == file)
+		aux_release_ioctl(file);
 	if (--aux_count) {
 		unlock_kernel();
 		return 0;
@@ -1000,7 +1122,7 @@
 	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 */
 	
 	mdelay(2);			/* Ensure we follow the kbc access delay rules.. */
@@ -1011,6 +1133,33 @@
 }
 
 /*
+ * Implement exclusive access mechanism.
+ */
+
+#define AUX_ACCESS_ALLOWED(file) (!aux_exclusive || aux_exclusive == (file))
+
+static ssize_t aux_wait_for_access(struct file * file)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	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.
  */
 
@@ -1020,7 +1169,11 @@
 	DECLARE_WAITQUEUE(wait, current);
 	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;
@@ -1055,23 +1208,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;
 		}
 	}
 
@@ -1082,15 +1244,80 @@
 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)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	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 = {
 	read:		read_aux,
 	write:		write_aux,
 	poll:		aux_poll,
+	ioctl:		aux_ioctl,
 	open:		open_aux,
 	release:	release_aux,
 	fasync:		fasync_aux,
@@ -1115,6 +1342,8 @@
 	memset(queue, 0, sizeof(*queue));
 	queue->head = queue->tail = 0;
 	init_waitqueue_head(&queue->proc_list);
+	init_waitqueue_head(&mouse_ack_wait);
+ 	init_waitqueue_head(&aux_exclusive_wait);
 
 #ifdef INITIALIZE_MOUSE
 	kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */
@@ -1126,6 +1355,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;
 }
