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*
mouseattach(char * spec)58 mouseattach(char* spec)
59 {
60 return devattach('m', spec);
61 }
62
63 static int
mousewalk(Chan * c,char * name)64 mousewalk(Chan* c, char* name)
65 {
66 return devwalk(c, name, mousetab, nelem(mousetab), devgen);
67 }
68
69 static void
mousestat(Chan * c,char * db)70 mousestat(Chan* c, char* db)
71 {
72 devstat(c, db, mousetab, nelem(mousetab), devgen);
73 }
74
75 static Chan*
mouseopen(Chan * c,int omode)76 mouseopen(Chan* c, int omode)
77 {
78 return devopen(c, omode, mousetab, nelem(mousetab), devgen);
79 }
80
81 static void
mouseclose(Chan * c)82 mouseclose(Chan* c)
83 {
84 USED(c);
85 }
86
87 static long
mouseread(Chan * c,void * a,long n,vlong offset)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
mousewrite(Chan * c,void * a,long n,vlong)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
track(int b,int dx,int dy)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
setintellimouse(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
intellimousedetect(void)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
mouseprobe(char * buf,int len)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
mousestatus(char * buf,int len)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
logitechmouseputc(Queue * q,int c)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
m3mouseputc(Queue *,int c)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
serialmouse(int port,char * type,int setspeed)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
ps2mouseputc(int c,int shift)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
ps2mouse(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
busmouseintr(Ureg *,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
busmousedetect(void)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
busmouse(int irq)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
msbusmouseintr(Ureg *,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
msbusmousedetect(void)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
msbusmouse(int irq)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
mousectl(char * buf)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