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