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