adb_mouse.c revision 184299
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: head/sys/dev/adb/adb_mouse.c 184299 2008-10-26 19:37:38Z nwhitehorn $ 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/uio.h> 39#include <sys/fcntl.h> 40#include <sys/kernel.h> 41 42#include <machine/bus.h> 43 44#include <vm/vm.h> 45#include <vm/pmap.h> 46 47#include "adb.h" 48 49#define CDEV_GET_SOFTC(x) devclass_get_softc(adb_mouse_devclass, minor(x) & 0x1f) 50 51static int adb_mouse_probe(device_t dev); 52static int adb_mouse_attach(device_t dev); 53static int adb_mouse_detach(device_t dev); 54 55static d_open_t ams_open; 56static d_close_t ams_close; 57static d_read_t ams_read; 58static d_ioctl_t ams_ioctl; 59static d_poll_t ams_poll; 60 61static u_int adb_mouse_receive_packet(device_t dev, u_char status, 62 u_char command, u_char reg, int len, u_char *data); 63 64struct adb_mouse_softc { 65 device_t sc_dev; 66 67 struct mtx sc_mtx; 68 struct cv sc_cv; 69 70 int extended; 71 uint16_t dpi; 72 73 mousehw_t hw; 74 mousemode_t mode; 75 u_char id[4]; 76 77 int buttons; 78 int last_buttons; 79 int xdelta, ydelta; 80 81 int8_t packet[8]; 82 size_t packet_read_len; 83 84 struct cdev *cdev; 85 struct selinfo rsel; 86}; 87 88static device_method_t adb_mouse_methods[] = { 89 /* Device interface */ 90 DEVMETHOD(device_probe, adb_mouse_probe), 91 DEVMETHOD(device_attach, adb_mouse_attach), 92 DEVMETHOD(device_detach, adb_mouse_detach), 93 DEVMETHOD(device_shutdown, bus_generic_shutdown), 94 DEVMETHOD(device_suspend, bus_generic_suspend), 95 DEVMETHOD(device_resume, bus_generic_resume), 96 97 /* ADB interface */ 98 DEVMETHOD(adb_receive_packet, adb_mouse_receive_packet), 99 100 { 0, 0 } 101}; 102 103static driver_t adb_mouse_driver = { 104 "ams", 105 adb_mouse_methods, 106 sizeof(struct adb_mouse_softc), 107}; 108 109static devclass_t adb_mouse_devclass; 110 111DRIVER_MODULE(ams, adb, adb_mouse_driver, adb_mouse_devclass, 0, 0); 112 113static struct cdevsw ams_cdevsw = { 114 .d_version = D_VERSION, 115 .d_flags = 0, 116 .d_open = ams_open, 117 .d_close = ams_close, 118 .d_read = ams_read, 119 .d_ioctl = ams_ioctl, 120 .d_poll = ams_poll, 121 .d_name = "ams", 122}; 123 124static int 125adb_mouse_probe(device_t dev) 126{ 127 uint8_t type; 128 129 type = adb_get_device_type(dev); 130 131 if (type != ADB_DEVICE_MOUSE) 132 return (ENXIO); 133 134 device_set_desc(dev,"ADB Mouse"); 135 return (0); 136} 137 138static int 139adb_mouse_attach(device_t dev) 140{ 141 struct adb_mouse_softc *sc; 142 char *description = "Unknown Pointing Device"; 143 144 size_t r1_len; 145 u_char r1[8]; 146 147 sc = device_get_softc(dev); 148 sc->sc_dev = dev; 149 150 mtx_init(&sc->sc_mtx,"ams",MTX_DEF,0); 151 cv_init(&sc->sc_cv,"ams"); 152 153 sc->extended = 0; 154 155 sc->hw.buttons = 2; 156 sc->hw.iftype = MOUSE_IF_UNKNOWN; 157 sc->hw.type = MOUSE_UNKNOWN; 158 sc->hw.model = sc->hw.hwid = 0; 159 160 sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; 161 sc->mode.rate = -1; 162 sc->mode.resolution = 100; 163 sc->mode.accelfactor = 0; 164 sc->mode.level = 0; 165 sc->mode.packetsize = 5; 166 167 sc->buttons = 0; 168 sc->last_buttons = 0; 169 sc->packet_read_len = 0; 170 171 /* Try to switch to extended protocol */ 172 adb_set_device_handler(dev,4); 173 174 switch(adb_get_device_handler(dev)) { 175 case 1: 176 sc->mode.resolution = 100; 177 break; 178 case 2: 179 sc->mode.resolution = 200; 180 break; 181 case 4: 182 adb_read_register(dev,1,&r1_len,r1); 183 if (r1_len < 8) 184 break; 185 186 sc->extended = 1; 187 memcpy(&sc->hw.hwid,r1,4); 188 sc->mode.resolution = (r1[4] << 8) | r1[5]; 189 190 switch (r1[6]) { 191 case 0: 192 sc->hw.type = MOUSE_PAD; 193 description = "Tablet"; 194 break; 195 case 1: 196 sc->hw.type = MOUSE_MOUSE; 197 description = "Mouse"; 198 break; 199 case 2: 200 sc->hw.type = MOUSE_TRACKBALL; 201 description = "Trackball"; 202 break; 203 } 204 205 sc->hw.buttons = r1[7]; 206 207 device_printf(dev,"%d-button %d-dpi %s\n", 208 sc->hw.buttons, sc->mode.resolution,description); 209 210 /* 211 * Check for one of MacAlly's non-compliant 2-button mice. 212 * These claim to speak the extended mouse protocol, but 213 * instead speak the standard protocol and only when their 214 * handler is set to 0x42. 215 */ 216 217 if (sc->hw.hwid == 0x4b4f4954) { 218 adb_set_device_handler(dev,0x42); 219 220 if (adb_get_device_handler(dev) == 0x42) { 221 device_printf(dev, "MacAlly 2-Button Mouse\n"); 222 sc->extended = 0; 223 } 224 } 225 226 break; 227 } 228 229 sc->cdev = make_dev(&ams_cdevsw, device_get_unit(dev), 230 UID_ROOT, GID_OPERATOR, 0644, "ams%d", 231 device_get_unit(dev)); 232 233 adb_set_autopoll(dev,1); 234 235 return (0); 236} 237 238static int 239adb_mouse_detach(device_t dev) 240{ 241 struct adb_mouse_softc *sc; 242 243 adb_set_autopoll(dev,0); 244 245 sc = device_get_softc(dev); 246 destroy_dev(sc->cdev); 247 248 mtx_destroy(&sc->sc_mtx); 249 cv_destroy(&sc->sc_cv); 250 251 return (0); 252} 253 254static u_int 255adb_mouse_receive_packet(device_t dev, u_char status, u_char command, 256 u_char reg, int len, u_char *data) 257{ 258 struct adb_mouse_softc *sc; 259 int i = 0; 260 int xdelta, ydelta; 261 int buttons; 262 263 sc = device_get_softc(dev); 264 265 if (command != ADB_COMMAND_TALK || reg != 0 || len < 2) 266 return (0); 267 268 ydelta = data[0] & 0x7f; 269 xdelta = data[1] & 0x7f; 270 271 buttons = 0; 272 buttons |= !(data[0] & 0x80); 273 buttons |= !(data[1] & 0x80) << 1; 274 275 if (sc->extended) { 276 for (i = 2; i < len && i < 5; i++) { 277 xdelta |= (data[i] & 0x07) << (3*i + 1); 278 ydelta |= (data[i] & 0x70) << (3*i - 3); 279 280 buttons |= !(data[i] & 0x08) << (2*i - 2); 281 buttons |= !(data[i] & 0x80) << (2*i - 1); 282 } 283 } else { 284 len = 2; /* Ignore extra data */ 285 } 286 287 /* Do sign extension as necessary */ 288 if (xdelta & (0x40 << 3*(len-2))) 289 xdelta |= 0xffffffc0 << 3*(len - 2); 290 if (ydelta & (0x40 << 3*(len-2))) 291 ydelta |= 0xffffffc0 << 3*(len - 2); 292 293 /* 294 * Some mice report high-numbered buttons on the wrong button number, 295 * so set the highest-numbered real button as pressed if there are 296 * mysterious high-numbered ones set. 297 */ 298 299 if (buttons & ~((1 << sc->hw.buttons) - 1)) { 300 buttons |= 1 << (sc->hw.buttons - 1); 301 buttons &= (1 << sc->hw.buttons) - 1; 302 } 303 304 mtx_lock(&sc->sc_mtx); 305 306 /* Add in our new deltas, and take into account 307 Apple's opposite meaning for Y axis motion */ 308 309 sc->xdelta += xdelta; 310 sc->ydelta -= ydelta; 311 312 sc->buttons = buttons; 313 314 mtx_unlock(&sc->sc_mtx); 315 316 cv_broadcast(&sc->sc_cv); 317 selwakeuppri(&sc->rsel, PZERO); 318 319 return (0); 320} 321 322static int 323ams_open(struct cdev *dev, int flag, int fmt, struct thread *p) 324{ 325 struct adb_mouse_softc *sc; 326 327 sc = CDEV_GET_SOFTC(dev); 328 if (sc == NULL) 329 return (ENXIO); 330 331 mtx_lock(&sc->sc_mtx); 332 sc->packet_read_len = 0; 333 sc->xdelta = 0; 334 sc->ydelta = 0; 335 sc->buttons = 0; 336 mtx_unlock(&sc->sc_mtx); 337 338 return (0); 339} 340 341static int 342ams_close(struct cdev *dev, int flag, int fmt, struct thread *p) 343{ 344 struct adb_mouse_softc *sc; 345 346 sc = CDEV_GET_SOFTC(dev); 347 348 cv_broadcast(&sc->sc_cv); 349 selwakeuppri(&sc->rsel, PZERO); 350 return (0); 351} 352 353static int 354ams_poll(struct cdev *dev, int events, struct thread *p) 355{ 356 struct adb_mouse_softc *sc; 357 358 sc = CDEV_GET_SOFTC(dev); 359 if (sc == NULL) 360 return (EIO); 361 362 if (events & (POLLIN | POLLRDNORM)) { 363 mtx_lock(&sc->sc_mtx); 364 365 if (sc->xdelta == 0 && sc->ydelta == 0 && 366 sc->buttons == sc->last_buttons) { 367 selrecord(p, &sc->rsel); 368 events = 0; 369 } else { 370 events &= (POLLIN | POLLRDNORM); 371 } 372 373 mtx_unlock(&sc->sc_mtx); 374 } 375 376 return events; 377} 378 379static int 380ams_read(struct cdev *dev, struct uio *uio, int flag) 381{ 382 struct adb_mouse_softc *sc; 383 size_t len; 384 int8_t outpacket[8]; 385 386 sc = CDEV_GET_SOFTC(dev); 387 if (sc == NULL) 388 return (EIO); 389 390 if (uio->uio_resid <= 0) 391 return (0); 392 393 mtx_lock(&sc->sc_mtx); 394 395 if (!sc->packet_read_len) { 396 if (sc->xdelta == 0 && sc->ydelta == 0 && 397 sc->buttons == sc->last_buttons) { 398 399 if (flag & O_NONBLOCK) { 400 mtx_unlock(&sc->sc_mtx); 401 return EWOULDBLOCK; 402 } 403 404 405 /* Otherwise, block on new data */ 406 cv_wait(&sc->sc_cv,&sc->sc_mtx); 407 } 408 409 sc->packet[0] = 1 << 7; 410 sc->packet[0] |= (!(sc->buttons & 1)) << 2; 411 sc->packet[0] |= (!(sc->buttons & 4)) << 1; 412 sc->packet[0] |= (!(sc->buttons & 2)); 413 414 if (sc->xdelta > 127) { 415 sc->packet[1] = 127; 416 sc->packet[3] = sc->xdelta - 127; 417 } else if (sc->xdelta < -127) { 418 sc->packet[1] = -127; 419 sc->packet[3] = sc->xdelta + 127; 420 } else { 421 sc->packet[1] = sc->xdelta; 422 sc->packet[3] = 0; 423 } 424 425 if (sc->ydelta > 127) { 426 sc->packet[2] = 127; 427 sc->packet[4] = sc->ydelta - 127; 428 } else if (sc->ydelta < -127) { 429 sc->packet[2] = -127; 430 sc->packet[4] = sc->ydelta + 127; 431 } else { 432 sc->packet[2] = sc->ydelta; 433 sc->packet[4] = 0; 434 } 435 436 /* No Z movement */ 437 sc->packet[5] = 0; 438 sc->packet[6] = 0; 439 440 sc->packet[7] = ~((uint8_t)(sc->buttons >> 3)) & 0x7f; 441 442 443 sc->last_buttons = sc->buttons; 444 sc->xdelta = 0; 445 sc->ydelta = 0; 446 447 sc->packet_read_len = sc->mode.packetsize; 448 } 449 450 len = (sc->packet_read_len > uio->uio_resid) ? 451 uio->uio_resid : sc->packet_read_len; 452 453 memcpy(outpacket,sc->packet + 454 (sc->mode.packetsize - sc->packet_read_len),len); 455 sc->packet_read_len -= len; 456 457 mtx_unlock(&sc->sc_mtx); 458 459 uiomove(outpacket,len,uio); 460 461 return (0); 462} 463 464 465static int 466ams_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, 467 struct thread *p) 468{ 469 struct adb_mouse_softc *sc; 470 mousemode_t mode; 471 472 sc = CDEV_GET_SOFTC(dev); 473 if (sc == NULL) 474 return (EIO); 475 476 switch (cmd) { 477 case MOUSE_GETHWINFO: 478 *(mousehw_t *)addr = sc->hw; 479 break; 480 case MOUSE_GETMODE: 481 *(mousemode_t *)addr = sc->mode; 482 break; 483 case MOUSE_SETMODE: 484 mode = *(mousemode_t *)addr; 485 addr = (caddr_t)&mode.level; 486 487 /* Fallthrough */ 488 489 case MOUSE_SETLEVEL: 490 if (*(int *)addr == -1) 491 break; 492 else if (*(int *)addr == 1) { 493 sc->mode.level = 1; 494 sc->mode.packetsize = 8; 495 break; 496 } else if (*(int *)addr == 0) { 497 sc->mode.level = 0; 498 sc->mode.packetsize = 5; 499 break; 500 } 501 502 return EINVAL; 503 case MOUSE_GETLEVEL: 504 *(int *)addr = sc->mode.level; 505 break; 506 507 case MOUSE_GETSTATUS: { 508 mousestatus_t *status = (mousestatus_t *) addr; 509 510 mtx_lock(&sc->sc_mtx); 511 512 status->button = sc->buttons; 513 status->obutton = sc->last_buttons; 514 515 status->flags = status->button ^ status->obutton; 516 517 if (sc->xdelta != 0 || sc->ydelta) 518 status->flags |= MOUSE_POSCHANGED; 519 if (status->button != status->obutton) 520 status->flags |= MOUSE_BUTTONSCHANGED; 521 522 status->dx = sc->xdelta; 523 status->dy = sc->ydelta; 524 status->dz = 0; 525 526 sc->xdelta = 0; 527 sc->ydelta = 0; 528 sc->last_buttons = sc->buttons; 529 530 mtx_unlock(&sc->sc_mtx); 531 532 break; } 533 default: 534 return ENOTTY; 535 } 536 537 return (0); 538} 539 540