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