1 /* $NetBSD: ams.c,v 1.18 2005/12/11 12:18:03 christos Exp $ */ 2 3 /* 4 * Copyright (C) 1998 Colin Wood 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 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Colin Wood. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: ams.c,v 1.18 2005/12/11 12:18:03 christos Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/device.h> 38 #include <sys/fcntl.h> 39 #include <sys/poll.h> 40 #include <sys/select.h> 41 #include <sys/proc.h> 42 #include <sys/signalvar.h> 43 #include <sys/systm.h> 44 45 #include <machine/autoconf.h> 46 47 #include <dev/wscons/wsconsio.h> 48 #include <dev/wscons/wsmousevar.h> 49 50 #include <macppc/dev/adbvar.h> 51 #include <macppc/dev/aedvar.h> 52 #include <macppc/dev/amsvar.h> 53 54 #include "aed.h" 55 56 /* 57 * Function declarations. 58 */ 59 static int amsmatch __P((struct device *, struct cfdata *, void *)); 60 static void amsattach __P((struct device *, struct device *, void *)); 61 static void ems_init __P((struct ams_softc *)); 62 static void ms_processevent __P((adb_event_t *event, struct ams_softc *)); 63 64 /* Driver definition. */ 65 CFATTACH_DECL(ams, sizeof(struct ams_softc), 66 amsmatch, amsattach, NULL, NULL); 67 68 int ams_enable __P((void *)); 69 int ams_ioctl __P((void *, u_long, caddr_t, int, struct lwp *)); 70 void ams_disable __P((void *)); 71 72 const struct wsmouse_accessops ams_accessops = { 73 ams_enable, 74 ams_ioctl, 75 ams_disable, 76 }; 77 78 static int 79 amsmatch(parent, cf, aux) 80 struct device *parent; 81 struct cfdata *cf; 82 void *aux; 83 { 84 struct adb_attach_args *aa_args = aux; 85 86 if (aa_args->origaddr == ADBADDR_MS) 87 return 1; 88 else 89 return 0; 90 } 91 92 static void 93 amsattach(parent, self, aux) 94 struct device *parent, *self; 95 void *aux; 96 { 97 ADBSetInfoBlock adbinfo; 98 struct ams_softc *sc = (struct ams_softc *)self; 99 struct adb_attach_args *aa_args = aux; 100 int error; 101 struct wsmousedev_attach_args a; 102 103 sc->origaddr = aa_args->origaddr; 104 sc->adbaddr = aa_args->adbaddr; 105 sc->handler_id = aa_args->handler_id; 106 107 sc->sc_class = MSCLASS_MOUSE; 108 sc->sc_buttons = 1; 109 sc->sc_res = 100; 110 sc->sc_devid[0] = 0; 111 sc->sc_devid[4] = 0; 112 113 adbinfo.siServiceRtPtr = (Ptr)ms_adbcomplete; 114 adbinfo.siDataAreaAddr = (caddr_t)sc; 115 116 ems_init(sc); 117 118 /* print out the type of mouse we have */ 119 switch (sc->handler_id) { 120 case ADBMS_100DPI: 121 printf("%d-button, %d dpi mouse\n", sc->sc_buttons, 122 (int)(sc->sc_res)); 123 break; 124 case ADBMS_200DPI: 125 sc->sc_res = 200; 126 printf("%d-button, %d dpi mouse\n", sc->sc_buttons, 127 (int)(sc->sc_res)); 128 break; 129 case ADBMS_MSA3: 130 printf("Mouse Systems A3 mouse, %d-button, %d dpi\n", 131 sc->sc_buttons, (int)(sc->sc_res)); 132 break; 133 case ADBMS_USPEED: 134 printf("MicroSpeed mouse, default parameters\n"); 135 break; 136 case ADBMS_UCONTOUR: 137 printf("Contour mouse, default parameters\n"); 138 break; 139 case ADBMS_TURBO: 140 printf("Kensington Turbo Mouse\n"); 141 break; 142 case ADBMS_EXTENDED: 143 if (sc->sc_devid[0] == '\0') { 144 printf("Logitech "); 145 switch (sc->sc_class) { 146 case MSCLASS_MOUSE: 147 printf("MouseMan (non-EMP) mouse"); 148 break; 149 case MSCLASS_TRACKBALL: 150 printf("TrackMan (non-EMP) trackball"); 151 break; 152 default: 153 printf("non-EMP relative positioning device"); 154 break; 155 } 156 printf("\n"); 157 } else { 158 printf("EMP "); 159 switch (sc->sc_class) { 160 case MSCLASS_TABLET: 161 printf("tablet"); 162 break; 163 case MSCLASS_MOUSE: 164 printf("mouse"); 165 break; 166 case MSCLASS_TRACKBALL: 167 printf("trackball"); 168 break; 169 case MSCLASS_TRACKPAD: 170 printf("trackpad"); 171 break; 172 default: 173 printf("unknown device"); 174 break; 175 } 176 printf(" <%s> %d-button, %d dpi\n", sc->sc_devid, 177 sc->sc_buttons, (int)(sc->sc_res)); 178 } 179 break; 180 default: 181 printf("relative positioning device (mouse?) (%d)\n", 182 sc->handler_id); 183 break; 184 } 185 error = SetADBInfo(&adbinfo, sc->adbaddr); 186 #ifdef ADB_DEBUG 187 if (adb_debug) 188 printf("ams: returned %d from SetADBInfo\n", error); 189 #endif 190 191 a.accessops = &ams_accessops; 192 a.accesscookie = sc; 193 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 194 } 195 196 197 /* 198 * Initialize extended mouse support -- probes devices as described 199 * in Inside Macintosh: Devices, Chapter 5 "ADB Manager". 200 * 201 * Extended Mouse Protocol is documented in TechNote HW1: 202 * "ADB - The Untold Story: Space Aliens Ate My Mouse" 203 * 204 * Supports: Extended Mouse Protocol, MicroSpeed Mouse Deluxe, 205 * Mouse Systems A^3 Mouse, Logitech non-EMP MouseMan 206 */ 207 void 208 ems_init(sc) 209 struct ams_softc *sc; 210 { 211 int adbaddr; 212 short cmd; 213 u_char buffer[9]; 214 215 adbaddr = sc->adbaddr; 216 if (sc->origaddr != ADBADDR_MS) 217 return; 218 if (sc->handler_id == ADBMS_USPEED || 219 sc->handler_id == ADBMS_UCONTOUR) { 220 /* Found MicroSpeed Mouse Deluxe Mac or Contour Mouse */ 221 cmd = ADBLISTEN(adbaddr, 1); 222 223 /* 224 * To setup the MicroSpeed or the Contour, it appears 225 * that we can send the following command to the mouse 226 * and then expect data back in the form: 227 * buffer[0] = 4 (bytes) 228 * buffer[1], buffer[2] as std. mouse 229 * buffer[3] = buffer[4] = 0xff when no buttons 230 * are down. When button N down, bit N is clear. 231 * buffer[4]'s locking mask enables a 232 * click to toggle the button down state--sort of 233 * like the "Easy Access" shift/control/etc. keys. 234 * buffer[3]'s alternative speed mask enables using 235 * different speed when the corr. button is down 236 */ 237 buffer[0] = 4; 238 buffer[1] = 0x00; /* Alternative speed */ 239 buffer[2] = 0x00; /* speed = maximum */ 240 buffer[3] = 0x10; /* enable extended protocol, 241 * lower bits = alt. speed mask 242 * = 0000b 243 */ 244 buffer[4] = 0x07; /* Locking mask = 0000b, 245 * enable buttons = 0111b 246 */ 247 adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd); 248 249 sc->sc_buttons = 3; 250 sc->sc_res = 200; 251 return; 252 } 253 if (sc->handler_id == ADBMS_TURBO) { 254 /* Found Kensington Turbo Mouse */ 255 static u_char data1[] = 256 { 8, 0xe7, 0x8c, 0, 0, 0, 0xff, 0xff, 0x94 }; 257 static u_char data2[] = 258 { 8, 0xa5, 0x14, 0, 0, 0x69, 0xff, 0xff, 0x27 }; 259 260 buffer[0] = 0; 261 adb_op_sync((Ptr)buffer, NULL, (Ptr)0, ADBFLUSH(adbaddr)); 262 263 adb_op_sync((Ptr)data1, NULL, (Ptr)0, ADBLISTEN(adbaddr, 2)); 264 265 buffer[0] = 0; 266 adb_op_sync((Ptr)buffer, NULL, (Ptr)0, ADBFLUSH(adbaddr)); 267 268 adb_op_sync((Ptr)data2, NULL, (Ptr)0, ADBLISTEN(adbaddr, 2)); 269 return; 270 } 271 if ((sc->handler_id == ADBMS_100DPI) || 272 (sc->handler_id == ADBMS_200DPI)) { 273 /* found a mouse */ 274 cmd = ADBTALK(adbaddr, 3); 275 if (adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd)) { 276 #ifdef ADB_DEBUG 277 if (adb_debug) 278 printf("adb: ems_init timed out\n"); 279 #endif 280 return; 281 } 282 283 /* Attempt to initialize Extended Mouse Protocol */ 284 buffer[2] = 4; /* make handler ID 4 */ 285 cmd = ADBLISTEN(adbaddr, 3); 286 if (adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd)) { 287 #ifdef ADB_DEBUG 288 if (adb_debug) 289 printf("adb: ems_init timed out\n"); 290 #endif 291 return; 292 } 293 294 /* 295 * Check to see if successful, if not 296 * try to initialize it as other types 297 */ 298 cmd = ADBTALK(adbaddr, 3); 299 if (adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd) == 0 && 300 buffer[2] == ADBMS_EXTENDED) { 301 sc->handler_id = ADBMS_EXTENDED; 302 cmd = ADBTALK(adbaddr, 1); 303 if (adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd)) { 304 #ifdef ADB_DEBUG 305 if (adb_debug) 306 printf("adb: ems_init timed out\n"); 307 #endif 308 } else if (buffer[0] == 8) { 309 /* we have a true EMP device */ 310 sc->sc_class = buffer[7]; 311 sc->sc_buttons = buffer[8]; 312 sc->sc_res = (int)*(short *)&buffer[5]; 313 memcpy(sc->sc_devid, &(buffer[1]), 4); 314 } else if (buffer[1] == 0x9a && 315 ((buffer[2] == 0x20) || (buffer[2] == 0x21))) { 316 /* 317 * Set up non-EMP Mouseman/Trackman to put 318 * button bits in 3rd byte instead of sending 319 * via pseudo keyboard device. 320 */ 321 cmd = ADBLISTEN(adbaddr, 1); 322 buffer[0]=2; 323 buffer[1]=0x00; 324 buffer[2]=0x81; 325 adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd); 326 327 cmd = ADBLISTEN(adbaddr, 1); 328 buffer[0]=2; 329 buffer[1]=0x01; 330 buffer[2]=0x81; 331 adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd); 332 333 cmd = ADBLISTEN(adbaddr, 1); 334 buffer[0]=2; 335 buffer[1]=0x02; 336 buffer[2]=0x81; 337 adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd); 338 339 cmd = ADBLISTEN(adbaddr, 1); 340 buffer[0]=2; 341 buffer[1]=0x03; 342 buffer[2]=0x38; 343 adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd); 344 345 sc->sc_buttons = 3; 346 sc->sc_res = 400; 347 if (buffer[2] == 0x21) 348 sc->sc_class = MSCLASS_TRACKBALL; 349 else 350 sc->sc_class = MSCLASS_MOUSE; 351 } else 352 /* unknown device? */; 353 } else { 354 /* Attempt to initialize as an A3 mouse */ 355 buffer[2] = 0x03; /* make handler ID 3 */ 356 cmd = ADBLISTEN(adbaddr, 3); 357 if (adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd)) { 358 #ifdef ADB_DEBUG 359 if (adb_debug) 360 printf("adb: ems_init timed out\n"); 361 #endif 362 return; 363 } 364 365 /* 366 * Check to see if successful, if not 367 * try to initialize it as other types 368 */ 369 cmd = ADBTALK(adbaddr, 3); 370 if (adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd) == 0 371 && buffer[2] == ADBMS_MSA3) { 372 sc->handler_id = ADBMS_MSA3; 373 /* Initialize as above */ 374 cmd = ADBLISTEN(adbaddr, 2); 375 /* listen 2 */ 376 buffer[0] = 3; 377 buffer[1] = 0x00; 378 /* Irrelevant, buffer has 0x77 */ 379 buffer[2] = 0x07; 380 /* 381 * enable 3 button mode = 0111b, 382 * speed = normal 383 */ 384 adb_op_sync((Ptr)buffer, NULL, (Ptr)0, cmd); 385 sc->sc_buttons = 3; 386 sc->sc_res = 300; 387 } else { 388 /* No special support for this mouse */ 389 } 390 } 391 } 392 } 393 394 /* 395 * Handle putting the mouse data received from the ADB into 396 * an ADB event record. 397 */ 398 void 399 ms_adbcomplete(buffer, data_area, adb_command) 400 caddr_t buffer; 401 caddr_t data_area; 402 int adb_command; 403 { 404 adb_event_t event; 405 struct ams_softc *sc; 406 int adbaddr; 407 #ifdef ADB_DEBUG 408 int i; 409 410 if (adb_debug) 411 printf("adb: transaction completion\n"); 412 #endif 413 414 adbaddr = ADB_CMDADDR(adb_command); 415 sc = (struct ams_softc *)data_area; 416 417 if ((sc->handler_id == ADBMS_EXTENDED) && (sc->sc_devid[0] == 0)) { 418 /* massage the data to look like EMP data */ 419 if ((buffer[3] & 0x04) == 0x04) 420 buffer[1] &= 0x7f; 421 else 422 buffer[1] |= 0x80; 423 if ((buffer[3] & 0x02) == 0x02) 424 buffer[2] &= 0x7f; 425 else 426 buffer[2] |= 0x80; 427 if ((buffer[3] & 0x01) == 0x01) 428 buffer[3] = 0x00; 429 else 430 buffer[3] = 0x80; 431 } 432 433 event.addr = adbaddr; 434 event.hand_id = sc->handler_id; 435 event.def_addr = sc->origaddr; 436 event.byte_count = buffer[0]; 437 memcpy(event.bytes, buffer + 1, event.byte_count); 438 439 #ifdef ADB_DEBUG 440 if (adb_debug) { 441 printf("ams: from %d at %d (org %d) %d:", event.addr, 442 event.hand_id, event.def_addr, buffer[0]); 443 for (i = 1; i <= buffer[0]; i++) 444 printf(" %x", buffer[i]); 445 printf("\n"); 446 } 447 #endif 448 449 microtime(&event.timestamp); 450 451 ms_processevent(&event, sc); 452 } 453 454 /* 455 * Given a mouse ADB event, record the button settings, calculate the 456 * x- and y-axis motion, and handoff the event to the appropriate subsystem. 457 */ 458 static void 459 ms_processevent(event, sc) 460 adb_event_t *event; 461 struct ams_softc *sc; 462 { 463 adb_event_t new_event; 464 int i, button_bit, max_byte, mask, buttons; 465 466 new_event = *event; 467 buttons = 0; 468 469 /* 470 * This should handle both plain ol' Apple mice and mice 471 * that claim to support the Extended Apple Mouse Protocol. 472 */ 473 max_byte = event->byte_count; 474 button_bit = 1; 475 switch (event->hand_id) { 476 case ADBMS_USPEED: 477 case ADBMS_UCONTOUR: 478 /* MicroSpeed mouse and Contour mouse */ 479 if (max_byte == 4) 480 buttons = (~event->bytes[2]) & 0xff; 481 else 482 buttons = (event->bytes[0] & 0x80) ? 0 : 1; 483 break; 484 case ADBMS_MSA3: 485 /* Mouse Systems A3 mouse */ 486 if (max_byte == 3) 487 buttons = (~event->bytes[2]) & 0x07; 488 else 489 buttons = (event->bytes[0] & 0x80) ? 0 : 1; 490 break; 491 default: 492 /* Classic Mouse Protocol (up to 2 buttons) */ 493 for (i = 0; i < 2; i++, button_bit <<= 1) 494 /* 0 when button down */ 495 if (!(event->bytes[i] & 0x80)) 496 buttons |= button_bit; 497 else 498 buttons &= ~button_bit; 499 /* Extended Protocol (up to 6 more buttons) */ 500 for (mask = 0x80; i < max_byte; 501 i += (mask == 0x80), button_bit <<= 1) { 502 /* 0 when button down */ 503 if (!(event->bytes[i] & mask)) 504 buttons |= button_bit; 505 else 506 buttons &= ~button_bit; 507 mask = ((mask >> 4) & 0xf) 508 | ((mask & 0xf) << 4); 509 } 510 break; 511 } 512 new_event.u.m.buttons = sc->sc_mb | buttons; 513 new_event.u.m.dx = ((signed int) (event->bytes[1] & 0x3f)) - 514 ((event->bytes[1] & 0x40) ? 64 : 0); 515 new_event.u.m.dy = ((signed int) (event->bytes[0] & 0x3f)) - 516 ((event->bytes[0] & 0x40) ? 64 : 0); 517 518 if (sc->sc_wsmousedev) 519 wsmouse_input(sc->sc_wsmousedev, new_event.u.m.buttons, 520 new_event.u.m.dx, -new_event.u.m.dy, 0, 521 WSMOUSE_INPUT_DELTA); 522 #if NAED > 0 523 aed_input(&new_event); 524 #endif 525 } 526 527 int 528 ams_enable(v) 529 void *v; 530 { 531 return 0; 532 } 533 534 int 535 ams_ioctl(v, cmd, data, flag, l) 536 void *v; 537 u_long cmd; 538 caddr_t data; 539 int flag; 540 struct lwp *l; 541 { 542 return EPASSTHROUGH; 543 } 544 545 void 546 ams_disable(v) 547 void *v; 548 { 549 } 550