1 /* $NetBSD: pms.c,v 1.2 2004/03/18 21:05:19 bjh21 Exp $ */ 2 3 /*- 4 * Copyright (c) 1994 Charles M. Hannum. 5 * Copyright (c) 1992, 1993 Erik Forsberg. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 17 * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 22 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: pms.c,v 1.2 2004/03/18 21:05:19 bjh21 Exp $"); 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/device.h> 32 #include <sys/ioctl.h> 33 #include <sys/kernel.h> 34 #include <sys/kthread.h> 35 36 #include <machine/bus.h> 37 38 #include <dev/pckbport/pckbportvar.h> 39 40 #include <dev/pckbport/pmsreg.h> 41 42 #include <dev/wscons/wsconsio.h> 43 #include <dev/wscons/wsmousevar.h> 44 45 #ifdef PMSDEBUG 46 int pmsdebug = 1; 47 #define DPRINTF(x) if (pmsdebug) printf x 48 #else 49 #define DPRINTF(x) 50 #endif 51 52 enum pms_type { PMS_UNKNOWN, PMS_STANDARD, PMS_SCROLL3, PMS_SCROLL5 }; 53 54 struct pms_protocol { 55 int rates[3]; 56 int response; 57 const char *name; 58 } pms_protocols[] = { 59 { { 0, 0, 0 }, 0, "unknown protocol" }, 60 { { 0, 0, 0 }, 0, "no scroll wheel (3 buttons)" }, 61 { { 200, 100, 80 }, 3, "scroll wheel (3 buttons)" }, 62 { { 200, 200, 80 }, 4, "scroll wheel (5 buttons)" } 63 }; 64 65 enum pms_type tries[] = { 66 PMS_SCROLL5, PMS_SCROLL3, PMS_STANDARD, PMS_UNKNOWN 67 }; 68 69 struct pms_softc { /* driver status information */ 70 struct device sc_dev; 71 72 pckbport_tag_t sc_kbctag; 73 int sc_kbcslot; 74 75 int sc_enabled; /* input enabled? */ 76 #ifndef PMS_DISABLE_POWERHOOK 77 void *sc_powerhook; /* cookie from power hook */ 78 int sc_suspended; /* suspended? */ 79 #endif /* !PMS_DISABLE_POWERHOOK */ 80 int inputstate; /* number of bytes received for this packet */ 81 u_int buttons; /* mouse button status */ 82 enum pms_type protocol; 83 unsigned char packet[4]; 84 struct timeval last, current; 85 86 struct device *sc_wsmousedev; 87 struct proc *sc_event_thread; 88 }; 89 90 int pmsprobe(struct device *, struct cfdata *, void *); 91 void pmsattach(struct device *, struct device *, void *); 92 void pmsinput(void *, int); 93 94 CFATTACH_DECL(pms, sizeof(struct pms_softc), 95 pmsprobe, pmsattach, NULL, NULL); 96 97 static int pms_protocol(pckbport_tag_t, pckbport_slot_t); 98 static void do_enable(struct pms_softc *); 99 static void do_disable(struct pms_softc *); 100 static void pms_reset_thread(void*); 101 static void pms_spawn_reset_thread(void*); 102 int pms_enable(void *); 103 int pms_ioctl(void *, u_long, caddr_t, int, struct proc *); 104 void pms_disable(void *); 105 #ifndef PMS_DISABLE_POWERHOOK 106 void pms_power(int, void *); 107 #endif /* !PMS_DISABLE_POWERHOOK */ 108 109 const struct wsmouse_accessops pms_accessops = { 110 pms_enable, 111 pms_ioctl, 112 pms_disable, 113 }; 114 115 static int 116 pms_protocol(pckbport_tag_t tag, pckbport_slot_t slot) 117 { 118 u_char cmd[2], resp[1]; 119 int i, j, res; 120 struct pms_protocol *p; 121 122 for (j = 0; j < sizeof(tries) / sizeof(tries[0]); ++j) { 123 p = &pms_protocols[tries[j]]; 124 if (!p->rates[0]) 125 break; 126 cmd[0] = PMS_SET_SAMPLE; 127 for (i = 0; i < 3; i++) { 128 cmd[1] = p->rates[i]; 129 res = pckbport_enqueue_cmd(tag, slot, cmd, 2, 0, 1, 0); 130 if (res) 131 return PMS_STANDARD; 132 } 133 134 cmd[0] = PMS_SEND_DEV_ID; 135 res = pckbport_enqueue_cmd(tag, slot, cmd, 1, 1, 1, resp); 136 if (res) 137 return PMS_UNKNOWN; 138 if (resp[0] == p->response) { 139 DPRINTF(("pms_protocol: found mouse protocol %d\n", 140 tries[j])); 141 return tries[j]; 142 } 143 } 144 DPRINTF(("pms_protocol: standard PS/2 protocol (no scroll wheel)\n")); 145 return PMS_STANDARD; 146 } 147 148 int 149 pmsprobe(struct device *parent, struct cfdata *match, void *aux) 150 { 151 struct pckbport_attach_args *pa = aux; 152 u_char cmd[1], resp[2]; 153 int res; 154 155 if (pa->pa_slot != PCKBPORT_AUX_SLOT) 156 return 0; 157 158 /* Flush any garbage. */ 159 pckbport_flush(pa->pa_tag, pa->pa_slot); 160 161 /* reset the device */ 162 cmd[0] = PMS_RESET; 163 res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); 164 if (res) { 165 #ifdef DEBUG 166 printf("pmsprobe: reset error %d\n", res); 167 #endif 168 return 0; 169 } 170 if (resp[0] != PMS_RSTDONE) { 171 printf("pmsprobe: reset response 0x%x\n", resp[0]); 172 return 0; 173 } 174 175 /* get type number (0 = mouse) */ 176 if (resp[1] != 0) { 177 #ifdef DEBUG 178 printf("pmsprobe: type 0x%x\n", resp[1]); 179 #endif 180 return 0; 181 } 182 183 return 10; 184 } 185 186 void 187 pmsattach(struct device *parent, struct device *self, void *aux) 188 { 189 struct pms_softc *sc = (void *)self; 190 struct pckbport_attach_args *pa = aux; 191 struct wsmousedev_attach_args a; 192 u_char cmd[1], resp[2]; 193 int res; 194 195 sc->sc_kbctag = pa->pa_tag; 196 sc->sc_kbcslot = pa->pa_slot; 197 198 printf("\n"); 199 200 /* Flush any garbage. */ 201 pckbport_flush(pa->pa_tag, pa->pa_slot); 202 203 /* reset the device */ 204 cmd[0] = PMS_RESET; 205 res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1); 206 #ifdef DEBUG 207 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) { 208 printf("pmsattach: reset error\n"); 209 return; 210 } 211 #endif 212 sc->inputstate = 0; 213 sc->buttons = 0; 214 sc->protocol = PMS_UNKNOWN; 215 216 pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot, 217 pmsinput, sc, sc->sc_dev.dv_xname); 218 219 a.accessops = &pms_accessops; 220 a.accesscookie = sc; 221 222 /* 223 * Attach the wsmouse, saving a handle to it. 224 * Note that we don't need to check this pointer against NULL 225 * here or in pmsintr, because if this fails pms_enable() will 226 * never be called, so pmsinput() will never be called. 227 */ 228 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 229 230 /* no interrupts until enabled */ 231 cmd[0] = PMS_DEV_DISABLE; 232 res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 0, 0, 0); 233 if (res) 234 printf("pmsattach: disable error\n"); 235 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0); 236 237 kthread_create(pms_spawn_reset_thread, sc); 238 239 #ifndef PMS_DISABLE_POWERHOOK 240 sc->sc_powerhook = powerhook_establish(pms_power, sc); 241 sc->sc_suspended = 0; 242 #endif /* !PMS_DISABLE_POWERHOOK */ 243 } 244 245 static void 246 do_enable(struct pms_softc *sc) 247 { 248 u_char cmd[1]; 249 int res; 250 251 sc->inputstate = 0; 252 sc->buttons = 0; 253 254 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1); 255 256 cmd[0] = PMS_DEV_ENABLE; 257 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, 1, 0, 1, 0); 258 if (res) 259 printf("pms_enable: command error %d\n", res); 260 261 if (sc->protocol == PMS_UNKNOWN) 262 sc->protocol = pms_protocol(sc->sc_kbctag, sc->sc_kbcslot); 263 DPRINTF(("pms_enable: using %s protocol\n", 264 pms_protocols[sc->protocol].name)); 265 #if 0 266 { 267 u_char scmd[2]; 268 269 scmd[0] = PMS_SET_RES; 270 scmd[1] = 3; /* 8 counts/mm */ 271 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, 272 2, 0, 1, 0); 273 if (res) 274 printf("pms_enable: setup error1 (%d)\n", res); 275 276 scmd[0] = PMS_SET_SCALE21; 277 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, 278 1, 0, 1, 0); 279 if (res) 280 printf("pms_enable: setup error2 (%d)\n", res); 281 282 scmd[0] = PMS_SET_SAMPLE; 283 scmd[1] = 100; /* 100 samples/sec */ 284 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd, 285 2, 0, 1, 0); 286 if (res) 287 printf("pms_enable: setup error3 (%d)\n", res); 288 } 289 #endif 290 } 291 292 static void 293 do_disable(struct pms_softc *sc) 294 { 295 u_char cmd[1]; 296 int res; 297 298 cmd[0] = PMS_DEV_DISABLE; 299 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, 1, 0, 1, 0); 300 if (res) 301 printf("pms_disable: command error\n"); 302 303 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0); 304 } 305 306 int 307 pms_enable(void *v) 308 { 309 struct pms_softc *sc = v; 310 int s; 311 312 if (sc->sc_enabled) 313 return EBUSY; 314 315 do_enable(sc); 316 317 s = spltty(); 318 sc->sc_enabled = 1; 319 splx(s); 320 321 return 0; 322 } 323 324 void 325 pms_disable(void *v) 326 { 327 struct pms_softc *sc = v; 328 int s; 329 330 do_disable(sc); 331 332 s = spltty(); 333 sc->sc_enabled = 0; 334 splx(s); 335 } 336 337 #ifndef PMS_DISABLE_POWERHOOK 338 void 339 pms_power(int why, void *v) 340 { 341 struct pms_softc *sc = v; 342 343 switch (why) { 344 case PWR_STANDBY: 345 break; 346 case PWR_SUSPEND: 347 if (sc->sc_enabled) { 348 do_disable(sc); 349 sc->sc_suspended = 1; 350 } 351 break; 352 case PWR_RESUME: 353 if (sc->sc_enabled && sc->sc_suspended) { 354 sc->protocol = PMS_UNKNOWN; /* recheck protocol & init mouse */ 355 sc->sc_suspended = 0; 356 do_enable(sc); /* only if we were suspended */ 357 } 358 case PWR_SOFTSUSPEND: 359 case PWR_SOFTSTANDBY: 360 case PWR_SOFTRESUME: 361 break; 362 } 363 } 364 #endif /* !PMS_DISABLE_POWERHOOK */ 365 366 int 367 pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 368 { 369 struct pms_softc *sc = v; 370 u_char kbcmd[2]; 371 int i; 372 373 switch (cmd) { 374 case WSMOUSEIO_GTYPE: 375 *(u_int *)data = WSMOUSE_TYPE_PS2; 376 break; 377 378 case WSMOUSEIO_SRES: 379 i = (*(u_int *)data - 12) / 25; 380 381 if (i < 0) 382 i = 0; 383 384 if (i > 3) 385 i = 3; 386 387 kbcmd[0] = PMS_SET_RES; 388 kbcmd[1] = i; 389 i = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, kbcmd, 390 2, 0, 1, 0); 391 392 if (i) 393 printf("pms_ioctl: SET_RES command error\n"); 394 break; 395 396 default: 397 return EPASSTHROUGH; 398 } 399 return 0; 400 } 401 402 static void 403 pms_spawn_reset_thread(void *arg) 404 { 405 struct pms_softc *sc = arg; 406 407 kthread_create1(pms_reset_thread, sc, &sc->sc_event_thread, 408 sc->sc_dev.dv_xname); 409 } 410 411 static void 412 pms_reset_thread(void *arg) 413 { 414 struct pms_softc *sc = arg; 415 u_char cmd[1], resp[2]; 416 int res; 417 int save_protocol; 418 419 for (;;) { 420 tsleep(&sc->sc_enabled, PWAIT, "pmsreset", 0); 421 #ifdef PMSDEBUG 422 if (pmsdebug) 423 #endif 424 #if defined(PMSDEBUG) || defined(DIAGNOSTIC) 425 printf("%s: resetting mouse interface\n", 426 sc->sc_dev.dv_xname); 427 #endif 428 save_protocol = sc->protocol; 429 pms_disable(sc); 430 cmd[0] = PMS_RESET; 431 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, 1, 432 2, 1, resp); 433 if (res) 434 DPRINTF(("%s: reset error %d\n", sc->sc_dev.dv_xname, 435 res)); 436 sc->protocol = PMS_UNKNOWN; 437 pms_enable(sc); 438 if (sc->protocol != save_protocol) { 439 #if defined(PMSDEBUG) || defined(DIAGNOSTIC) 440 printf("%s: protocol change, sleeping and retrying\n", 441 sc->sc_dev.dv_xname); 442 #endif 443 pms_disable(sc); 444 cmd[0] = PMS_RESET; 445 res = pckbport_enqueue_cmd(sc->sc_kbctag, 446 sc->sc_kbcslot, cmd, 1, 2, 1, resp); 447 if (res) 448 DPRINTF(("%s: reset error %d\n", 449 sc->sc_dev.dv_xname, res)); 450 tsleep(pms_reset_thread, PWAIT, "pmsreset", hz); 451 cmd[0] = PMS_RESET; 452 res = pckbport_enqueue_cmd(sc->sc_kbctag, 453 sc->sc_kbcslot, cmd, 1, 2, 1, resp); 454 if (res) 455 DPRINTF(("%s: reset error %d\n", 456 sc->sc_dev.dv_xname, res)); 457 sc->protocol = PMS_UNKNOWN; /* reprobe protocol */ 458 pms_enable(sc); 459 #if defined(PMSDEBUG) || defined(DIAGNOSTIC) 460 if (sc->protocol != save_protocol) { 461 printf("%s: protocol changed.\n", 462 sc->sc_dev.dv_xname); 463 } 464 #endif 465 } 466 } 467 } 468 469 /* Masks for the first byte of a packet */ 470 #define PMS_LBUTMASK 0x01 471 #define PMS_RBUTMASK 0x02 472 #define PMS_MBUTMASK 0x04 473 #define PMS_4BUTMASK 0x10 474 #define PMS_5BUTMASK 0x20 475 476 void 477 pmsinput(void *vsc, int data) 478 { 479 struct pms_softc *sc = vsc; 480 u_int changed; 481 int dx, dy, dz = 0; 482 int newbuttons = 0; 483 int s; 484 485 if (!sc->sc_enabled) { 486 /* Interrupts are not expected. Discard the byte. */ 487 return; 488 } 489 490 s = splclock(); 491 sc->current = mono_time; 492 splx(s); 493 494 if (sc->inputstate > 0) { 495 struct timeval diff; 496 497 timersub(&sc->current, &sc->last, &diff); 498 /* 499 * Empirically, the delay should be about 1700us on a standard 500 * PS/2 port. I have seen delays as large as 4500us (rarely) 501 * in regular use. When using a confused mouse, I generally 502 * see delays at least as large as 30,000us. -seebs 503 * 504 * The thinkpad trackball returns at 22-23ms. So we use 505 * >= 40ms. In the future, I'll implement adaptable timeout 506 * by increasing the timeout if the mouse reset happens 507 * too frequently -christos 508 */ 509 if (diff.tv_sec > 0 || diff.tv_usec >= 40000) { 510 DPRINTF(("pms_input: unusual delay (%ld.%06ld s), " 511 "scheduling reset\n", 512 (long)diff.tv_sec, (long)diff.tv_usec)); 513 sc->inputstate = 0; 514 sc->sc_enabled = 0; 515 wakeup(&sc->sc_enabled); 516 return; 517 } 518 } 519 sc->last = sc->current; 520 521 if (sc->inputstate == 0) { 522 /* 523 * Some devices (seen on trackballs anytime, and on 524 * some mice shortly after reset) output garbage bytes 525 * between packets. Just ignore them. 526 */ 527 if ((data & 0xc0) != 0) 528 return; /* not in sync yet, discard input */ 529 } 530 531 sc->packet[sc->inputstate++] = data & 0xff; 532 switch (sc->inputstate) { 533 case 0: 534 /* no useful processing can be done yet */ 535 break; 536 537 case 1: 538 /* 539 * Why should we test for bit 0x8 and insist on it here? 540 * The old (psm.c and psm_intelli.c) drivers didn't do 541 * it, and there are devices where it does harm (that's 542 * why it is not used if using PMS_STANDARD protocol). 543 * Anyway, it does not to cause any harm to accept packets 544 * without this bit. 545 */ 546 #if 0 547 if (sc->protocol == PMS_STANDARD) 548 break; 549 if (!(sc->packet[0] & 0x8)) { 550 DPRINTF(("pmsinput: 0x8 not set in first byte " 551 "[0x%02x], resetting\n", sc->packet[0])); 552 sc->inputstate = 0; 553 sc->sc_enabled = 0; 554 wakeup(&sc->sc_enabled); 555 return; 556 } 557 #endif 558 break; 559 560 case 2: 561 break; 562 563 case 4: 564 /* Case 4 is a superset of case 3. This is *not* an accident. */ 565 if (sc->protocol == PMS_SCROLL3) { 566 dz = sc->packet[3]; 567 if (dz >= 128) 568 dz -= 256; 569 if (dz == -128) 570 dz = -127; 571 } else if (sc->protocol == PMS_SCROLL5) { 572 dz = sc->packet[3] & 0xf; 573 if (dz >= 8) 574 dz -= 16; 575 if (sc->packet[3] & PMS_4BUTMASK) 576 newbuttons |= 0x8; 577 if (sc->packet[3] & PMS_5BUTMASK) 578 newbuttons |= 0x10; 579 } else { 580 DPRINTF(("pmsinput: why am I looking at this byte?\n")); 581 dz = 0; 582 } 583 /* FALLTHROUGH */ 584 case 3: 585 /* 586 * This is only an endpoint for scroll protocols with 4 587 * bytes, or the standard protocol with 3. 588 */ 589 if (sc->protocol != PMS_STANDARD && sc->inputstate == 3) 590 break; 591 592 newbuttons |= ((sc->packet[0] & PMS_LBUTMASK) ? 0x1 : 0) | 593 ((sc->packet[0] & PMS_MBUTMASK) ? 0x2 : 0) | 594 ((sc->packet[0] & PMS_RBUTMASK) ? 0x4 : 0); 595 596 dx = sc->packet[1]; 597 if (dx >= 128) 598 dx -= 256; 599 if (dx == -128) 600 dx = -127; 601 602 dy = sc->packet[2]; 603 if (dy >= 128) 604 dy -= 256; 605 if (dy == -128) 606 dy = -127; 607 608 sc->inputstate = 0; 609 changed = (sc->buttons ^ newbuttons); 610 sc->buttons = newbuttons; 611 612 #ifdef PMSDEBUG 613 if (sc->protocol == PMS_STANDARD) { 614 DPRINTF(("pms: packet: 0x%02x%02x%02x\n", 615 sc->packet[0], sc->packet[1], sc->packet[2])); 616 } else { 617 DPRINTF(("pms: packet: 0x%02x%02x%02x%02x\n", 618 sc->packet[0], sc->packet[1], sc->packet[2], 619 sc->packet[3])); 620 } 621 #endif 622 if (dx || dy || dz || changed) { 623 #ifdef PMSDEBUG 624 DPRINTF(("pms: x %+03d y %+03d z %+03d " 625 "buttons 0x%02x\n", dx, dy, dz, sc->buttons)); 626 #endif 627 wsmouse_input(sc->sc_wsmousedev, 628 sc->buttons, dx, dy, dz, 629 WSMOUSE_INPUT_DELTA); 630 } 631 memset(sc->packet, 0, 4); 632 break; 633 634 /* If we get here, we have problems. */ 635 default: 636 printf("pmsinput: very confused. resetting.\n"); 637 sc->inputstate = 0; 638 sc->sc_enabled = 0; 639 wakeup(&sc->sc_enabled); 640 return; 641 } 642 } 643