1 /*- 2 * Copyright (C) 2008 Nathan Whitehorn 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28 #include <sys/cdefs.h> 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/module.h> 32 #include <sys/bus.h> 33 #include <sys/conf.h> 34 #include <sys/mouse.h> 35 #include <sys/poll.h> 36 #include <sys/condvar.h> 37 #include <sys/selinfo.h> 38 #include <sys/sysctl.h> 39 #include <sys/uio.h> 40 #include <sys/fcntl.h> 41 #include <sys/kernel.h> 42 43 #include <machine/bus.h> 44 45 #include <vm/vm.h> 46 #include <vm/pmap.h> 47 48 #include "adb.h" 49 50 #define CDEV_GET_SOFTC(x) (x)->si_drv1 51 52 static int adb_mouse_probe(device_t dev); 53 static int adb_mouse_attach(device_t dev); 54 static int adb_mouse_detach(device_t dev); 55 static void adb_init_trackpad(device_t dev); 56 static int adb_tapping_sysctl(SYSCTL_HANDLER_ARGS); 57 58 static d_open_t ams_open; 59 static d_close_t ams_close; 60 static d_read_t ams_read; 61 static d_ioctl_t ams_ioctl; 62 static d_poll_t ams_poll; 63 64 static u_int adb_mouse_receive_packet(device_t dev, u_char status, 65 u_char command, u_char reg, int len, u_char *data); 66 67 struct adb_mouse_softc { 68 device_t sc_dev; 69 70 struct mtx sc_mtx; 71 struct cv sc_cv; 72 73 int flags; 74 #define AMS_EXTENDED 0x1 75 #define AMS_TOUCHPAD 0x2 76 uint16_t dpi; 77 78 mousehw_t hw; 79 mousemode_t mode; 80 u_char id[4]; 81 82 int buttons; 83 u_int sc_tapping; 84 int button_buf; 85 int last_buttons; 86 int xdelta, ydelta; 87 88 int8_t packet[8]; 89 size_t packet_read_len; 90 91 struct cdev *cdev; 92 struct selinfo rsel; 93 }; 94 95 static device_method_t adb_mouse_methods[] = { 96 /* Device interface */ 97 DEVMETHOD(device_probe, adb_mouse_probe), 98 DEVMETHOD(device_attach, adb_mouse_attach), 99 DEVMETHOD(device_detach, adb_mouse_detach), 100 DEVMETHOD(device_shutdown, bus_generic_shutdown), 101 DEVMETHOD(device_suspend, bus_generic_suspend), 102 DEVMETHOD(device_resume, bus_generic_resume), 103 104 /* ADB interface */ 105 DEVMETHOD(adb_receive_packet, adb_mouse_receive_packet), 106 107 { 0, 0 } 108 }; 109 110 static driver_t adb_mouse_driver = { 111 "ams", 112 adb_mouse_methods, 113 sizeof(struct adb_mouse_softc), 114 }; 115 116 static devclass_t adb_mouse_devclass; 117 118 DRIVER_MODULE(ams, adb, adb_mouse_driver, adb_mouse_devclass, 0, 0); 119 120 static struct cdevsw ams_cdevsw = { 121 .d_version = D_VERSION, 122 .d_flags = 0, 123 .d_open = ams_open, 124 .d_close = ams_close, 125 .d_read = ams_read, 126 .d_ioctl = ams_ioctl, 127 .d_poll = ams_poll, 128 .d_name = "ams", 129 }; 130 131 static int 132 adb_mouse_probe(device_t dev) 133 { 134 uint8_t type; 135 136 type = adb_get_device_type(dev); 137 138 if (type != ADB_DEVICE_MOUSE) 139 return (ENXIO); 140 141 device_set_desc(dev,"ADB Mouse"); 142 return (0); 143 } 144 145 static int 146 adb_mouse_attach(device_t dev) 147 { 148 struct adb_mouse_softc *sc; 149 char *description = "Unknown Pointing Device"; 150 151 size_t r1_len; 152 u_char r1[8]; 153 154 sc = device_get_softc(dev); 155 sc->sc_dev = dev; 156 157 mtx_init(&sc->sc_mtx, "ams", NULL, MTX_DEF); 158 cv_init(&sc->sc_cv,"ams"); 159 160 sc->flags = 0; 161 162 sc->hw.buttons = 2; 163 sc->hw.iftype = MOUSE_IF_UNKNOWN; 164 sc->hw.type = MOUSE_UNKNOWN; 165 sc->hw.model = sc->hw.hwid = 0; 166 167 sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; 168 sc->mode.rate = -1; 169 sc->mode.resolution = 100; 170 sc->mode.accelfactor = 0; 171 sc->mode.level = 0; 172 sc->mode.packetsize = 5; 173 174 sc->buttons = 0; 175 sc->sc_tapping = 0; 176 sc->button_buf = 0; 177 sc->last_buttons = 0; 178 sc->packet_read_len = 0; 179 180 /* Try to switch to extended protocol */ 181 adb_set_device_handler(dev,4); 182 183 switch(adb_get_device_handler(dev)) { 184 case 1: 185 sc->mode.resolution = 100; 186 break; 187 case 2: 188 sc->mode.resolution = 200; 189 break; 190 case 4: 191 r1_len = adb_read_register(dev,1,r1); 192 if (r1_len < 8) 193 break; 194 195 sc->flags |= AMS_EXTENDED; 196 memcpy(&sc->hw.hwid,r1,4); 197 sc->mode.resolution = (r1[4] << 8) | r1[5]; 198 199 switch (r1[6]) { 200 case 0: 201 sc->hw.type = MOUSE_PAD; 202 description = "Tablet"; 203 break; 204 case 1: 205 sc->hw.type = MOUSE_MOUSE; 206 description = "Mouse"; 207 break; 208 case 2: 209 sc->hw.type = MOUSE_TRACKBALL; 210 description = "Trackball"; 211 break; 212 case 3: 213 sc->flags |= AMS_TOUCHPAD; 214 sc->hw.type = MOUSE_PAD; 215 adb_init_trackpad(dev); 216 description = "Touchpad"; 217 break; 218 } 219 220 sc->hw.buttons = r1[7]; 221 222 device_printf(dev,"%d-button %d-dpi %s\n", 223 sc->hw.buttons, sc->mode.resolution,description); 224 225 /* 226 * Check for one of MacAlly's non-compliant 2-button mice. 227 * These claim to speak the extended mouse protocol, but 228 * instead speak the standard protocol and only when their 229 * handler is set to 0x42. 230 */ 231 232 if (sc->hw.hwid == 0x4b4f4954) { 233 adb_set_device_handler(dev,0x42); 234 235 if (adb_get_device_handler(dev) == 0x42) { 236 device_printf(dev, "MacAlly 2-Button Mouse\n"); 237 sc->flags &= ~AMS_EXTENDED; 238 } 239 } 240 241 break; 242 } 243 244 sc->cdev = make_dev(&ams_cdevsw, device_get_unit(dev), 245 UID_ROOT, GID_OPERATOR, 0644, "ams%d", 246 device_get_unit(dev)); 247 sc->cdev->si_drv1 = sc; 248 249 adb_set_autopoll(dev,1); 250 251 return (0); 252 } 253 254 static int 255 adb_mouse_detach(device_t dev) 256 { 257 struct adb_mouse_softc *sc; 258 259 adb_set_autopoll(dev,0); 260 261 sc = device_get_softc(dev); 262 destroy_dev(sc->cdev); 263 264 mtx_destroy(&sc->sc_mtx); 265 cv_destroy(&sc->sc_cv); 266 267 return (0); 268 } 269 270 static void 271 adb_init_trackpad(device_t dev) 272 { 273 struct adb_mouse_softc *sc; 274 struct sysctl_ctx_list *ctx; 275 struct sysctl_oid *tree; 276 277 size_t r1_len; 278 u_char r1[8]; 279 u_char r2[8]; 280 281 sc = device_get_softc(dev); 282 283 r1_len = adb_read_register(dev, 1, r1); 284 285 /* An Extended Mouse register1 must return 8 bytes. */ 286 if (r1_len != 8) 287 return; 288 289 if((r1[6] != 0x0d)) 290 { 291 r1[6] = 0x0d; 292 293 adb_write_register(dev, 1, 8, r1); 294 295 r1_len = adb_read_register(dev, 1, r1); 296 297 if (r1[6] != 0x0d) 298 { 299 device_printf(dev, "ADB Mouse = 0x%x " 300 "(non-Extended Mode)\n", r1[6]); 301 return; 302 } else { 303 device_printf(dev, "ADB Mouse = 0x%x " 304 "(Extended Mode)\n", r1[6]); 305 306 /* Set ADB Extended Features to default values, 307 enabled. */ 308 r2[0] = 0x19; /* Clicking: 0x19 disabled 0x99 enabled */ 309 r2[1] = 0x94; /* Dragging: 0x14 disabled 0x94 enabled */ 310 r2[2] = 0x19; 311 r2[3] = 0xff; /* DragLock: 0xff disabled 0xb2 enabled */ 312 r2[4] = 0xb2; 313 r2[5] = 0x8a; 314 r2[6] = 0x1b; 315 316 r2[7] = 0x57; /* 0x57 bits 3:0 for W mode */ 317 318 adb_write_register(dev, 2, 8, r2); 319 320 } 321 } 322 323 /* 324 * Set up sysctl 325 */ 326 ctx = device_get_sysctl_ctx(dev); 327 tree = device_get_sysctl_tree(dev); 328 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tapping", 329 CTLTYPE_INT | CTLFLAG_RW, sc, 0, adb_tapping_sysctl, 330 "I", "Tapping the pad causes button events"); 331 return; 332 } 333 334 static u_int 335 adb_mouse_receive_packet(device_t dev, u_char status, u_char command, 336 u_char reg, int len, u_char *data) 337 { 338 struct adb_mouse_softc *sc; 339 int i = 0; 340 int xdelta, ydelta; 341 int buttons, tmp_buttons; 342 343 sc = device_get_softc(dev); 344 345 if (command != ADB_COMMAND_TALK || reg != 0 || len < 2) 346 return (0); 347 348 ydelta = data[0] & 0x7f; 349 xdelta = data[1] & 0x7f; 350 351 buttons = 0; 352 buttons |= !(data[0] & 0x80); 353 buttons |= !(data[1] & 0x80) << 1; 354 355 if (sc->flags & AMS_EXTENDED) { 356 for (i = 2; i < len && i < 5; i++) { 357 xdelta |= (data[i] & 0x07) << (3*i + 1); 358 ydelta |= (data[i] & 0x70) << (3*i - 3); 359 360 buttons |= !(data[i] & 0x08) << (2*i - 2); 361 buttons |= !(data[i] & 0x80) << (2*i - 1); 362 } 363 } else { 364 len = 2; /* Ignore extra data */ 365 } 366 367 /* Do sign extension as necessary */ 368 if (xdelta & (0x40 << 3*(len-2))) 369 xdelta |= 0xffffffc0 << 3*(len - 2); 370 if (ydelta & (0x40 << 3*(len-2))) 371 ydelta |= 0xffffffc0 << 3*(len - 2); 372 373 if ((sc->flags & AMS_TOUCHPAD) && (sc->sc_tapping == 1)) { 374 tmp_buttons = buttons; 375 if (buttons == 0x12) { 376 /* Map a double tap on button 3. 377 Keep the button state for the next sequence. 378 A double tap sequence is followed by a single tap 379 sequence. 380 */ 381 tmp_buttons = 0x3; 382 sc->button_buf = tmp_buttons; 383 } else if (buttons == 0x2) { 384 /* Map a single tap on button 2. But only if it is 385 not a successor from a double tap. 386 */ 387 if (sc->button_buf != 0x3) 388 tmp_buttons = 0x2; 389 else 390 tmp_buttons = 0; 391 392 sc->button_buf = 0; 393 } 394 buttons = tmp_buttons; 395 } 396 397 /* 398 * Some mice report high-numbered buttons on the wrong button number, 399 * so set the highest-numbered real button as pressed if there are 400 * mysterious high-numbered ones set. 401 * 402 * Don't do this for touchpads, because touchpads also trigger 403 * high button events when they are touched. 404 */ 405 406 if (buttons & ~((1 << sc->hw.buttons) - 1) 407 && !(sc->flags & AMS_TOUCHPAD)) { 408 buttons |= 1 << (sc->hw.buttons - 1); 409 } 410 buttons &= (1 << sc->hw.buttons) - 1; 411 412 mtx_lock(&sc->sc_mtx); 413 414 /* Add in our new deltas, and take into account 415 Apple's opposite meaning for Y axis motion */ 416 417 sc->xdelta += xdelta; 418 sc->ydelta -= ydelta; 419 420 sc->buttons = buttons; 421 422 mtx_unlock(&sc->sc_mtx); 423 424 cv_broadcast(&sc->sc_cv); 425 selwakeuppri(&sc->rsel, PZERO); 426 427 return (0); 428 } 429 430 static int 431 ams_open(struct cdev *dev, int flag, int fmt, struct thread *p) 432 { 433 struct adb_mouse_softc *sc; 434 435 sc = CDEV_GET_SOFTC(dev); 436 if (sc == NULL) 437 return (ENXIO); 438 439 mtx_lock(&sc->sc_mtx); 440 sc->packet_read_len = 0; 441 sc->xdelta = 0; 442 sc->ydelta = 0; 443 sc->buttons = 0; 444 mtx_unlock(&sc->sc_mtx); 445 446 return (0); 447 } 448 449 static int 450 ams_close(struct cdev *dev, int flag, int fmt, struct thread *p) 451 { 452 struct adb_mouse_softc *sc; 453 454 sc = CDEV_GET_SOFTC(dev); 455 456 cv_broadcast(&sc->sc_cv); 457 selwakeuppri(&sc->rsel, PZERO); 458 return (0); 459 } 460 461 static int 462 ams_poll(struct cdev *dev, int events, struct thread *p) 463 { 464 struct adb_mouse_softc *sc; 465 466 sc = CDEV_GET_SOFTC(dev); 467 if (sc == NULL) 468 return (EIO); 469 470 if (events & (POLLIN | POLLRDNORM)) { 471 mtx_lock(&sc->sc_mtx); 472 473 if (sc->xdelta == 0 && sc->ydelta == 0 && 474 sc->buttons == sc->last_buttons && 475 sc->packet_read_len == 0) { 476 selrecord(p, &sc->rsel); 477 events = 0; 478 } else { 479 events &= (POLLIN | POLLRDNORM); 480 } 481 482 mtx_unlock(&sc->sc_mtx); 483 } 484 485 return events; 486 } 487 488 static int 489 ams_read(struct cdev *dev, struct uio *uio, int flag) 490 { 491 struct adb_mouse_softc *sc; 492 size_t len; 493 int8_t outpacket[8]; 494 int error; 495 496 sc = CDEV_GET_SOFTC(dev); 497 if (sc == NULL) 498 return (EIO); 499 500 if (uio->uio_resid <= 0) 501 return (0); 502 503 mtx_lock(&sc->sc_mtx); 504 505 if (!sc->packet_read_len) { 506 if (sc->xdelta == 0 && sc->ydelta == 0 && 507 sc->buttons == sc->last_buttons) { 508 509 if (flag & O_NONBLOCK) { 510 mtx_unlock(&sc->sc_mtx); 511 return EWOULDBLOCK; 512 } 513 514 515 /* Otherwise, block on new data */ 516 error = cv_wait_sig(&sc->sc_cv, &sc->sc_mtx); 517 if (error) { 518 mtx_unlock(&sc->sc_mtx); 519 return (error); 520 } 521 } 522 523 sc->packet[0] = 1 << 7; 524 sc->packet[0] |= (!(sc->buttons & 1)) << 2; 525 sc->packet[0] |= (!(sc->buttons & 4)) << 1; 526 sc->packet[0] |= (!(sc->buttons & 2)); 527 528 if (sc->xdelta > 127) { 529 sc->packet[1] = 127; 530 sc->packet[3] = sc->xdelta - 127; 531 } else if (sc->xdelta < -127) { 532 sc->packet[1] = -127; 533 sc->packet[3] = sc->xdelta + 127; 534 } else { 535 sc->packet[1] = sc->xdelta; 536 sc->packet[3] = 0; 537 } 538 539 if (sc->ydelta > 127) { 540 sc->packet[2] = 127; 541 sc->packet[4] = sc->ydelta - 127; 542 } else if (sc->ydelta < -127) { 543 sc->packet[2] = -127; 544 sc->packet[4] = sc->ydelta + 127; 545 } else { 546 sc->packet[2] = sc->ydelta; 547 sc->packet[4] = 0; 548 } 549 550 /* No Z movement */ 551 sc->packet[5] = 0; 552 sc->packet[6] = 0; 553 554 sc->packet[7] = ~((uint8_t)(sc->buttons >> 3)) & 0x7f; 555 556 557 sc->last_buttons = sc->buttons; 558 sc->xdelta = 0; 559 sc->ydelta = 0; 560 561 sc->packet_read_len = sc->mode.packetsize; 562 } 563 564 len = (sc->packet_read_len > uio->uio_resid) ? 565 uio->uio_resid : sc->packet_read_len; 566 567 memcpy(outpacket,sc->packet + 568 (sc->mode.packetsize - sc->packet_read_len),len); 569 sc->packet_read_len -= len; 570 571 mtx_unlock(&sc->sc_mtx); 572 573 error = uiomove(outpacket,len,uio); 574 575 return (error); 576 } 577 578 579 static int 580 ams_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, 581 struct thread *p) 582 { 583 struct adb_mouse_softc *sc; 584 mousemode_t mode; 585 586 sc = CDEV_GET_SOFTC(dev); 587 if (sc == NULL) 588 return (EIO); 589 590 switch (cmd) { 591 case MOUSE_GETHWINFO: 592 *(mousehw_t *)addr = sc->hw; 593 break; 594 case MOUSE_GETMODE: 595 *(mousemode_t *)addr = sc->mode; 596 break; 597 case MOUSE_SETMODE: 598 mode = *(mousemode_t *)addr; 599 addr = (caddr_t)&mode.level; 600 601 /* Fallthrough */ 602 603 case MOUSE_SETLEVEL: 604 if (*(int *)addr == -1) 605 break; 606 else if (*(int *)addr == 1) { 607 sc->mode.level = 1; 608 sc->mode.packetsize = 8; 609 break; 610 } else if (*(int *)addr == 0) { 611 sc->mode.level = 0; 612 sc->mode.packetsize = 5; 613 break; 614 } 615 616 return EINVAL; 617 case MOUSE_GETLEVEL: 618 *(int *)addr = sc->mode.level; 619 break; 620 621 case MOUSE_GETSTATUS: { 622 mousestatus_t *status = (mousestatus_t *) addr; 623 624 mtx_lock(&sc->sc_mtx); 625 626 status->button = sc->buttons; 627 status->obutton = sc->last_buttons; 628 629 status->flags = status->button ^ status->obutton; 630 631 if (sc->xdelta != 0 || sc->ydelta) 632 status->flags |= MOUSE_POSCHANGED; 633 if (status->button != status->obutton) 634 status->flags |= MOUSE_BUTTONSCHANGED; 635 636 status->dx = sc->xdelta; 637 status->dy = sc->ydelta; 638 status->dz = 0; 639 640 sc->xdelta = 0; 641 sc->ydelta = 0; 642 sc->last_buttons = sc->buttons; 643 644 mtx_unlock(&sc->sc_mtx); 645 646 break; } 647 default: 648 return ENOTTY; 649 } 650 651 return (0); 652 } 653 654 static int 655 adb_tapping_sysctl(SYSCTL_HANDLER_ARGS) 656 { 657 struct adb_mouse_softc *sc = arg1; 658 device_t dev; 659 int error; 660 u_char r2[8]; 661 u_int tapping; 662 663 dev = sc->sc_dev; 664 tapping = sc->sc_tapping; 665 666 error = sysctl_handle_int(oidp, &tapping, 0, req); 667 668 if (error || !req->newptr) 669 return (error); 670 671 if (tapping == 1) { 672 adb_read_register(dev, 2, r2); 673 r2[0] = 0x99; /* enable tapping. */ 674 adb_write_register(dev, 2, 8, r2); 675 sc->sc_tapping = 1; 676 } else if (tapping == 0) { 677 adb_read_register(dev, 2, r2); 678 r2[0] = 0x19; /* disable tapping. */ 679 adb_write_register(dev, 2, 8, r2); 680 sc->sc_tapping = 0; 681 } 682 else 683 return (EINVAL); 684 685 return (0); 686 } 687