File: kernel-2.2.17-psaux-exclusive.patch

package info (click to toggle)
tpconfig 3.1.3-9
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 564 kB
  • ctags: 185
  • sloc: ansic: 1,720; sh: 455; makefile: 82
file content (404 lines) | stat: -rw-r--r-- 11,060 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
--- linux/drivers/char/pc_keyb.c.orig	Mon Sep  4 13:39:17 2000
+++ linux/drivers/char/pc_keyb.c	Sun Nov  5 15:15:58 2000
@@ -13,6 +13,14 @@
  * Code fixes to handle mouse ACKs properly.
  * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
  *
+ * Code fixes to *really* handle mouse ACKs properly.
+ * Julian Bradfield <jcb@dcs.ed.ac.uk> 1999-04-30.
+ *
+ * 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>
@@ -58,6 +66,8 @@
 static void kbd_write_output_w(int data);
 #ifdef CONFIG_PSMOUSE
 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 +91,25 @@
 
 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 the user programs using the aux device)
+   send commands to the mouse that expect an ACK. */
 static unsigned char mouse_reply_expected = 0;
+/* used to make sure we read acks from the mouse before we write
+   another byte */
+static unsigned char mouse_ack_pending = 0;
+#define MOUSE_ACK_TIMEOUT 5    /* actually, 1 seems to be enough usually */ 
 
 #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 +414,10 @@
 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. */
+		mouse_ack_pending = 0;
 	if (mouse_reply_expected) {
 		if (scancode == AUX_ACK) {
 			mouse_reply_expected--;
@@ -399,19 +425,18 @@
 		}
 		mouse_reply_expected = 0;
 	}
-    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();
-        return;
-    }
-
+	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();
+		return;
+	}
 	add_mouse_randomness(scancode);
 	if (aux_count) {
 		int head = queue->head;
@@ -421,7 +446,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);
 		}
 	}
@@ -440,31 +465,33 @@
 	unsigned char status = kbd_read_status();
 	unsigned int work = 10000;
 
-	while (status & KBD_STAT_OBF) {
+	while ((--work > 0) && (status & KBD_STAT_OBF)) {
 		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 {
-			if (do_acknowledge(scancode))
-				handle_scancode(scancode, !(scancode & 0x80));
-			mark_bh(KEYBOARD_BH);
-		}
 
-		status = kbd_read_status();
-		
-		if(!work--)
+		/* Error bytes must be ignored to make the 
+		   Synaptics touchpads compaq use work */
+#if 1
+		/* Ignore error bytes */
+		if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR)))
+#endif
 		{
-			printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n",
-				status);
-			break;
+			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 = kbd_read_status();
 	}
 
+	if (!work)
+		printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n", status);
+
 	return status;
 }
 
@@ -791,12 +818,23 @@
 static void aux_write_dev(int val)
 {
 	unsigned long flags;
+	int loop = 0;
 
 	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 to arrive; otherwise we
+	   lose it. (At least, I think this is what is happening.)  */
+	while (mouse_ack_pending && loop++ < MOUSE_ACK_TIMEOUT ) {
+		mdelay(1);
+		handle_kbd_event();
+	}
+	if (mouse_ack_pending)
+		printk(KERN_WARNING "mouse ack timeout\n");
 	kbd_write_command(KBD_CCMD_WRITE_MOUSE);
 	kb_wait();
 	kbd_write_output(val);
+	mouse_ack_pending = 1;
 	spin_unlock_irqrestore(&kbd_controller_lock, flags);
 }
 
@@ -806,18 +844,52 @@
 static void aux_write_ack(int val)
 {
 	unsigned long flags;
+	int loop = 0;
 
 	spin_lock_irqsave(&kbd_controller_lock, flags);
 	kb_wait();
+	while (mouse_ack_pending && loop++ < MOUSE_ACK_TIMEOUT) {
+		mdelay(1);
+		handle_kbd_event();
+	}
+	if (mouse_ack_pending)
+		printk(KERN_WARNING "mouse ack timeout\n");
 	kbd_write_command(KBD_CCMD_WRITE_MOUSE);
 	kb_wait();
 	kbd_write_output(val);
-	/* we expect an ACK in response. */
+	mouse_ack_pending = 1;
+	/* we will deal with the ACK ourselves. */
 	mouse_reply_expected++;
 	kb_wait();
 	spin_unlock_irqrestore(&kbd_controller_lock, flags);
 }
 
+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)
 {
 	unsigned char result;
@@ -855,6 +927,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 */
@@ -888,6 +962,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)
+{
+	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.
  */
 
@@ -897,7 +998,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;
@@ -932,8 +1037,11 @@
 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;
 
@@ -949,6 +1057,7 @@
 		if (written) {
 			retval = written;
 			file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+			aux_last_write = jiffies;
 		}
 	}
 
@@ -958,18 +1067,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 */
@@ -1006,6 +1179,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;
 }