xref: /inferno-os/os/pc/devmouse.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 #include	"../port/error.h"
8 
9 /*
10  * TODO
11  * - shift key should modify right button with non-serial mice
12  * + intellimouse implementation
13  * - acceleration for all mouse types
14  * + spurious interrupt 7 after probing for ps2 mouse for the first time...?
15  * - test with ms busmouse
16  * - test with logitech serial mouse
17  */
18 
19 /*
20  *  mouse types
21  */
22 enum
23 {
24 	Mouseother,
25 	Mouseserial,
26 	MousePS2,
27 	Mousebus,
28 	Mouseintelli,
29 	Mousemsbus,
30 };
31 
32 static int mousetype;
33 static int mouseswap;
34 static int mouseport;		/* port for serial mice, irq for bus mice */
35 static int mousesubtype;
36 static int accelerated;
37 static QLock mouselock;
38 
39 static int msbusmousedetect(void);
40 static int busmousedetect(void);
41 static void mousectl(char *buf);
42 static void mouseprobe(char *buf, int len);
43 static void mousestatus(char *buf, int len);
44 
45 enum{
46 	Qdir,
47 	Qmousectl,
48 	Qmouseprobe,
49 };
50 
51 static
52 Dirtab mousetab[]={
53 	"mousectl",		{Qmousectl, 0},	0,	0600,
54 	"mouseprobe",	{Qmouseprobe, 0}, 0, 0400,
55 };
56 
57 static Chan*
58 mouseattach(char* spec)
59 {
60 	return devattach('m', spec);
61 }
62 
63 static int
64 mousewalk(Chan* c, char* name)
65 {
66 	return devwalk(c, name, mousetab, nelem(mousetab), devgen);
67 }
68 
69 static void
70 mousestat(Chan* c, char* db)
71 {
72 	devstat(c, db, mousetab, nelem(mousetab), devgen);
73 }
74 
75 static Chan*
76 mouseopen(Chan* c, int omode)
77 {
78 	return devopen(c, omode, mousetab, nelem(mousetab), devgen);
79 }
80 
81 static void
82 mouseclose(Chan* c)
83 {
84 	USED(c);
85 }
86 
87 static long
88 mouseread(Chan* c, void* a, long n, vlong offset)
89 {
90 	char buf[64];
91 	USED(offset);
92 
93 	switch(c->qid.path & ~CHDIR){
94 	case Qdir:
95 		return devdirread(c, a, n, mousetab, nelem(mousetab), devgen);
96 	case Qmousectl:
97 		qlock(&mouselock);
98 		mousestatus(buf, sizeof(buf));
99 		qunlock(&mouselock);
100 		n = readstr(offset, a, n, buf);
101 		break;
102 	case Qmouseprobe:
103 		if (mousetype)
104 			error(Emouseset);
105 		mouseprobe(buf, sizeof(buf));
106 		n = readstr(offset, a, n, buf);
107 		break;
108 	default:
109 		n=0;
110 		break;
111 	}
112 	return n;
113 }
114 
115 static long
116 mousewrite(Chan* c, void *a, long n, vlong)
117 {
118 	char buf[64];
119 	if ((c->qid.path & ~CHDIR) != Qmousectl)
120 		error(Ebadusefd);
121 	if (n >= sizeof(buf))
122 		n = sizeof(buf) - 1;
123 	strncpy(buf, a, n);
124 	buf[n] = 0;
125 
126 	qlock(&mouselock);
127 	if (waserror()) {
128 		qunlock(&mouselock);
129 		nexterror();
130 	}
131 	mousectl(buf);
132 	poperror();
133 	qunlock(&mouselock);
134 	return n;
135 }
136 
137 static void
138 track(int b, int dx, int dy)
139 {
140 	static uchar map[8] = {0,4,2,6,1,5,3,7};
141 	if (mouseswap)
142 		b = map[b&7];
143 	mousetrack(b, dx, dy);
144 }
145 
146 static void
147 setintellimouse(void)
148 {
149 	i8042auxcmd(0xF3);	/* set sample */
150 	i8042auxcmd(0xC8);
151 	i8042auxcmd(0xF3);	/* set sample */
152 	i8042auxcmd(0x64);
153 	i8042auxcmd(0xF3);	/* set sample */
154 	i8042auxcmd(0x50);
155 }
156 
157 /*
158  * check for an Intellimouse.
159  * this is only used when we know there's an 8042 aux device
160  */
161 static int
162 intellimousedetect(void)
163 {
164 	int id;
165 	setintellimouse();
166 	/* check whether the mouse is now in extended mode */
167 	id = i8042auxcmdval(0xf2);		/* identify device */
168 	if (id != 3) {
169 		/*
170 		 * set back to standard sample rate (100 per sec)
171 		 */
172 		i8042auxcmd(0xf3);
173 		i8042auxcmd(0x64);
174 		return 0;
175 	}
176 	return 1;
177 }
178 
179 static void
180 mouseprobe(char *buf, int len)
181 {
182 	USED(len);
183 	/*
184 	 * bus mice are easiest, so probe them first
185 	 */
186 	if (busmousedetect())
187 		sprint(buf, "bus\n");
188 	else if (msbusmousedetect())
189 		sprint(buf, "msbus\n");
190 	else if (i8042auxdetect()) {
191 		if (intellimousedetect())
192 			sprint(buf, "ps2intellimouse\n");
193 		else
194 			sprint(buf, "ps2\n");
195 	}
196 	else
197 		*buf = 0;
198 }
199 
200 
201 static void
202 mousestatus(char *buf, int len)
203 {
204 	char *s;
205 	USED(len);
206 	s = buf;
207 	switch (mousetype) {
208 	case Mouseserial:
209 		if (mousesubtype)
210 			s += sprint(s, "serial %d %c\n", mouseport, mousesubtype);
211 		else
212 			s += sprint(s, "serial %d\n", mouseport);
213 		break;
214 	case MousePS2:
215 		s += sprint(s, "ps2\n");
216 		break;
217 	case Mousebus:
218 		s += sprint(s, "bus %d\n", mouseport);
219 		break;
220 	case Mouseintelli:
221 		s += sprint(s, "intelli\n");
222 		break;
223 	case Mousemsbus:
224 		s += sprint(s, "msbus %d\n", mouseport);
225 		break;
226 	default:
227 	case Mouseother:
228 		s += sprint(s, "unknown\n");
229 		break;
230 	}
231 	if (accelerated)
232 		s += sprint(s, "accelerated\n");
233 	if (mouseswap)
234 		sprint(s, "swap\n");
235 }
236 
237 /*
238  *  Logitech 5 byte packed binary mouse format, 8 bit bytes
239  *
240  *  shift & right button is the same as middle button (for 2 button mice)
241  */
242 static int
243 logitechmouseputc(Queue *q, int c)
244 {
245 	static short msg[5];
246 	static int nb;
247 	static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 5, 3, 7};
248 	int dx, dy, newbuttons;
249 	int mouseshifted;
250 
251 	USED(q);
252 	if((c&0xF0) == 0x80)
253 		nb=0;
254 	msg[nb] = c;
255 	if(c & 0x80)
256 		msg[nb] |= ~0xFF;	/* sign extend */
257 	if(++nb == 5){
258 		mouseshifted = 0;	/* XXX should be from keyboard shift key */
259 		newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)];
260 		dx = msg[1]+msg[3];
261 		dy = -(msg[2]+msg[4]);
262 		track(newbuttons, dx, dy);
263 		nb = 0;
264 	}
265 	return 0;
266 }
267 
268 /*
269  *  microsoft 3 button, 7 bit bytes
270  *
271  *	byte 0 -	1  L  R Y7 Y6 X7 X6
272  *	byte 1 -	0 X5 X4 X3 X2 X1 X0
273  *	byte 2 -	0 Y5 Y4 Y3 Y2 Y1 Y0
274  *	byte 3 -	0  M  x  x  x  x  x	(optional)
275  *
276  *  shift & right button is the same as middle button (for 2 button mice)
277  */
278 static int
279 m3mouseputc(Queue*, int c)
280 {
281 	static uchar msg[3];
282 	static int nb;
283 	static int middle;
284 	static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 5 };
285 	short x;
286 	int dx, dy, buttons;
287 
288 	/*
289 	 *  check bit 6 for consistency
290 	 */
291 	if(nb==0){
292 		if((c&0x40) == 0){
293 			/* an extra byte gets sent for the middle button */
294 			if(c & 0x1c)
295 				return 0;
296 			middle = (c&0x20) ? 2 : 0;
297 			buttons = (mouse.b & ~2) | middle;
298 			track(buttons, 0, 0);
299 			return 0;
300 		}
301 	}
302 	msg[nb] = c&0x3f;
303 	if(++nb == 3){
304 		nb = 0;
305 		buttons = middle | b[(msg[0]>>4)&3];
306 		x = (msg[0]&0x3)<<14;
307 		dx = (x>>8) | msg[1];
308 		x = (msg[0]&0xc)<<12;
309 		dy = (x>>8) | msg[2];
310 		track(buttons, dx, dy);
311 	}
312 	return 0;
313 }
314 
315 static void
316 serialmouse(int port, char *type, int setspeed)
317 {
318 	int (*putc)(Queue *, int) = 0;
319 	char pn[KNAMELEN];
320 
321 	if(mousetype)
322 		error(Emouseset);
323 
324 	if(port >= 2 || port < 0)
325 		error(Ebadarg);
326 
327 	if (type == 0)
328 		putc = logitechmouseputc;
329 	else if (*type == 'M')
330 		putc = m3mouseputc;
331 	else
332 		error(Ebadarg);
333 	snprint(pn, sizeof(pn), "%d", port);
334 	i8250mouse(pn, putc, setspeed);
335 	mousetype = Mouseserial;
336 	mouseport = port;
337 	mousesubtype = (type && *type == 'M') ? 'M' : 0;
338 }
339 
340 /*
341  *  ps/2 mouse message is three bytes
342  *
343  *	byte 0 -	0 0 SDY SDX 1 M R L
344  *	byte 1 -	DX
345  *	byte 2 -	DY
346  *
347  *  shift & left button is the same as middle button
348  */
349 static void
350 ps2mouseputc(int c, int shift)
351 {
352 	static short msg[3];
353 	static int nb;
354 	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
355 	int buttons, dx, dy;
356 
357 	/*
358 	 *  check byte 0 for consistency
359 	 */
360 	if(nb==0 && (c&0xc8)!=0x08)
361 		return;
362 
363 	msg[nb] = c;
364 	if(++nb == 3){
365 		nb = 0;
366 		if(msg[0] & 0x10)
367 			msg[1] |= 0xFF00;
368 		if(msg[0] & 0x20)
369 			msg[2] |= 0xFF00;
370 
371 		buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
372 		dx = msg[1];
373 		dy = -msg[2];
374 		track(buttons, dx, dy);
375 	}
376 	return;
377 }
378 
379 /*
380  *  set up a ps2 mouse
381  */
382 static void
383 ps2mouse(void)
384 {
385 	if(mousetype)
386 		error(Emouseset);
387 
388 	i8042auxenable(ps2mouseputc);
389 	/* make mouse streaming, enabled */
390 	i8042auxcmd(0xEA);
391 	i8042auxcmd(0xF4);
392 
393 	mousetype = MousePS2;
394 }
395 
396 /* logitech bus mouse ports and commands */
397 enum {
398 	/* ports */
399 	BMdatap	= 0x23c,
400 	BMsigp	= 0x23d,
401 	BMctlp	= 0x23e,
402 	BMintrp	= 0x23e,
403 	BMconfigp	= 0x23f,
404 
405 	/* commands */
406 	BMintron = 0x0,
407 	BMintroff = 0x10,
408 	BMrxlo	= 0x80,
409 	BMrxhi	= 0xa0,
410 	BMrylo	= 0xc0,
411 	BMryhi	= 0xe0,
412 
413 	BMconfig	= 0x91,
414 	BMdefault	= 0x90,
415 
416 	BMsigval	= 0xa5
417 };
418 
419 static void
420 busmouseintr(Ureg *, void *)
421 {
422 	char dx, dy;
423 	uchar b;
424 	static uchar oldb;
425 	static Lock intrlock;
426 	ilock(&intrlock);
427 	outb(BMintrp, BMintroff);
428 	outb(BMctlp, BMrxlo);
429 	dx = inb(BMdatap) & 0xf;
430 	outb(BMctlp, BMrxhi);
431 	dx |= (inb(BMdatap) & 0xf) << 4;
432 	outb(BMctlp, BMrylo);
433 	dy = inb(BMdatap) & 0xf;
434 	outb(BMctlp, BMryhi);
435 	b = inb(BMdatap);
436 	dy |= (b & 0xf) << 4;
437 	b = ~(b >> 5) & 7;
438 	if (dx || dy || b != oldb) {
439 		oldb = b;
440 		track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy);
441 	}
442 	iunlock(&intrlock);
443 	outb(BMintrp, BMintron);
444 }
445 
446 static int
447 busmousedetect(void)
448 {
449 	outb(BMconfigp, BMconfig);
450 	outb(BMsigp, BMsigval);
451 	delay(2);
452 	if (inb(BMsigp) != BMsigval)
453 		return 0;
454 	outb(BMconfigp, BMdefault);
455 	return 1;
456 }
457 
458 /*
459  * set up a logitech bus mouse
460  */
461 static void
462 busmouse(int irq)
463 {
464 	if (mousetype)
465 		error(Emouseset);
466 	if (!busmousedetect())
467 		error(Enodev);
468 
469 	intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, busmouseintr, 0, BUSUNKNOWN);
470 	outb(BMintrp, BMintron);
471 	mousetype = Mousebus;
472 	mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC;
473 }
474 
475 /* microsoft bus mouse ports and commands */
476 enum {
477 	MBMdatap=	0x23d,
478 	MBMsigp=	0x23e,
479 	MBMctlp=	0x23c,
480 	MBMconfigp=	0x23f,
481 
482 	MBMintron=	0x11,
483 	MBMintroff=	0x10,
484 	MBMrbuttons= 0x00,
485 	MBMrx=		0x01,
486 	MBMry=		0x02,
487 	MBMstart=	0x80,
488 	MBMcmd=		0x07,
489 };
490 
491 static void
492 msbusmouseintr(Ureg *, void *)
493 {
494 	char dx, dy;
495 	uchar b;
496 	static uchar oldb;
497 	static Lock intrlock;
498 	ilock(&intrlock);
499 	outb(MBMctlp, MBMcmd);
500 	outb(MBMdatap, inb(MBMdatap)|0x20);
501 
502 	outb(MBMctlp, MBMrx);
503 	dx = inb(MBMdatap);
504 
505 	outb(MBMctlp, MBMry);
506 	dy = inb(MBMdatap);
507 
508 	outb(MBMctlp, MBMrbuttons);
509 	b = inb(MBMdatap) & 0x7;
510 
511 	outb(MBMctlp, MBMcmd);
512 	outb(MBMdatap, inb(MBMdatap)&0xdf);
513 
514 	if (dx != 0 || dy != 0 || b != oldb) {
515 		oldb = b;
516 		/* XXX this is almost certainly wrong */
517 		track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy);
518 	}
519 	iunlock(&intrlock);
520 }
521 
522 static int
523 msbusmousedetect(void)
524 {
525 	if (inb(MBMsigp) == 0xde) {
526 		int v, i;
527 		delay(1);
528 		v = inb(MBMsigp);
529 		delay(1);
530 		for (i = 0; i < 4; i++) {
531 			if (inb(MBMsigp) != 0xde)
532 				break;
533 			delay(1);
534 			if (inb(MBMsigp) != v)
535 				break;
536 			delay(1);
537 		}
538 		if (i == 4) {
539 			outb(MBMctlp, MBMcmd);
540 			return 1;
541 		}
542 	}
543 	return 0;
544 }
545 
546 static void
547 msbusmouse(int irq)
548 {
549 	if (mousetype)
550 		error(Emouseset);
551 	if (!msbusmousedetect())
552 		error(Enodev);
553 	mousetype = Mousemsbus;
554 	mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC;
555 	intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, msbusmouseintr, 0, BUSUNKNOWN);
556 	outb(MBMdatap, MBMintron);
557 }
558 
559 static void
560 mousectl(char *buf)
561 {
562 	int nf, x;
563 	char *field[10];
564 	nf = getfields(buf, field, 10, 1, " \t\n");
565 	if (nf < 1)
566 		return;
567 	if(strncmp(field[0], "serial", 6) == 0){
568 		switch(nf){
569 		/* the difference between these two cases is intriguing - wrtp */
570 		case 1:
571 			serialmouse(atoi(field[0]+6), 0, 1);
572 			break;
573 		case 2:
574 			serialmouse(atoi(field[1]), 0, 0);
575 			break;
576 		case 3:
577 		default:
578 			serialmouse(atoi(field[1]), field[2], 0);
579 			break;
580 		}
581 	} else if(strcmp(field[0], "ps2") == 0){
582 		ps2mouse();
583 	} else if (strcmp(field[0], "ps2intellimouse") == 0) {
584 		ps2mouse();
585 		setintellimouse();
586 	} else if (strncmp(field[0], "bus", 3) == 0 || strncmp(field[0], "msbus", 5) == 0) {
587 		int irq, isms;
588 
589 		isms = (field[0][0] == 'm');
590 		if (nf == 1)
591 			irq = atoi(field[0] + (isms ? 5 : 3));
592 		else
593 			irq = atoi(field[1]);
594 		if (irq < 1)
595 			irq = -1;
596 		if (isms)
597 			msbusmouse(irq);
598 		else
599 			busmouse(irq);
600 	} else if(strcmp(field[0], "accelerated") == 0){
601 		switch(mousetype){
602 		case MousePS2:
603 			x = splhi();
604 			i8042auxcmd(0xE7);
605 			splx(x);
606 			accelerated = 1;
607 			break;
608 		}
609 	} else if(strcmp(field[0], "linear") == 0){
610 		switch(mousetype){
611 		case MousePS2:
612 			x = splhi();
613 			i8042auxcmd(0xE6);
614 			splx(x);
615 			accelerated = 0;
616 			break;
617 		}
618 	} else if(strcmp(field[0], "res") == 0){
619 		int n,m;
620 		switch(nf){
621 		default:
622 			n = 0x02;
623 			m = 0x23;
624 			break;
625 		case 2:
626 			n = atoi(field[1])&0x3;
627 			m = 0x7;
628 			break;
629 		case 3:
630 			n = atoi(field[1])&0x3;
631 			m = atoi(field[2])&0x7;
632 			break;
633 		}
634 
635 		switch(mousetype){
636 		case MousePS2:
637 			x = splhi();
638 			i8042auxcmd(0xE8);
639 			i8042auxcmd(n);
640 			i8042auxcmd(0x5A);
641 			i8042auxcmd(0x30|m);
642 			i8042auxcmd(0x5A);
643 			i8042auxcmd(0x20|(m>>1));
644 			splx(x);
645 			break;
646 		}
647 	} else if(strcmp(field[0], "swap") == 0)
648 		mouseswap ^= 1;
649 }
650 
651 Dev mousedevtab = {					/* defaults in dev.c */
652 	'm',
653 	"mouse",
654 
655 	devreset,					/* devreset */
656 	devinit,					/* devinit */
657 	mouseattach,
658 	devdetach,
659 	devclone,					/* devclone */
660 	mousewalk,
661 	mousestat,
662 	mouseopen,
663 	devcreate,					/* devcreate */
664 	mouseclose,
665 	mouseread,
666 	devbread,					/* devbread */
667 	mousewrite,
668 	devbwrite,					/* devbwrite */
669 	devremove,					/* devremove */
670 	devwstat,					/* devwstat */
671 };
672 
673