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