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