adb_ms.c revision 1.23 1 /* $NetBSD: adb_ms.c,v 1.23 2025/06/10 10:02:56 macallan Exp $ */
2
3 /*
4 * Copyright (C) 1998 Colin Wood
5 * Copyright (C) 2006, 2007 Michael Lorenz
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/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: adb_ms.c,v 1.23 2025/06/10 10:02:56 macallan Exp $");
36
37 #include <sys/param.h>
38 #include <sys/device.h>
39 #include <sys/fcntl.h>
40 #include <sys/poll.h>
41 #include <sys/select.h>
42 #include <sys/proc.h>
43 #include <sys/signalvar.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/sysctl.h>
47
48 #include <machine/autoconf.h>
49
50 #include <dev/wscons/wsconsio.h>
51 #include <dev/wscons/wsmousevar.h>
52
53 #include <machine/adbsys.h>
54 #include <dev/adb/adbvar.h>
55
56 #include "adbdebug.h"
57
58 #ifdef ADBMS_DEBUG
59 #define DPRINTF printf
60 #else
61 #define DPRINTF while (0) printf
62 #endif
63
64 /*
65 * State info, per mouse instance.
66 */
67 struct adbms_softc {
68 device_t sc_dev;
69 struct adb_device *sc_adbdev;
70 struct adb_bus_accessops *sc_ops;
71
72 /* Extended Mouse Protocol info, faked for non-EMP mice */
73 u_int8_t sc_class; /* mouse class (mouse, trackball) */
74 u_int8_t sc_buttons; /* number of buttons */
75 u_int32_t sc_res; /* mouse resolution (dpi) */
76 char sc_devid[5]; /* device indentifier */
77 uint8_t sc_us; /* cmd to watch for */
78 int sc_mb; /* current button state */
79 device_t sc_wsmousedev;
80 /* helpers for trackpads */
81 int sc_down;
82 /*
83 * trackpad protocol variant. Known so far:
84 * 2 buttons - PowerBook 3400, single events on button 3 and 4 indicate
85 * finger down and up
86 * 4 buttons - iBook G4, button 6 indicates finger down, button 4 is
87 * always down
88 */
89 int sc_x, sc_y;
90 int sc_tapping;
91 /* buffers */
92 int sc_poll;
93 int sc_msg_len;
94 int sc_event;
95 uint8_t sc_buffer[16];
96 };
97
98 /*
99 * Function declarations.
100 */
101 static int adbms_match(device_t, cfdata_t, void *);
102 static void adbms_attach(device_t, device_t, void *);
103 static void ems_init(struct adbms_softc *);
104 static void init_trackpad(struct adbms_softc *);
105 static void adbms_init_mouse(struct adbms_softc *);
106 static void adbms_init_turbo(struct adbms_softc *);
107 static void adbms_init_uspeed(struct adbms_softc *);
108 static void adbms_process_event(struct adbms_softc *, int, uint8_t *);
109 static int adbms_send_sync(struct adbms_softc *, uint8_t, int, uint8_t *);
110
111 /* Driver definition. */
112 CFATTACH_DECL_NEW(adbms, sizeof(struct adbms_softc),
113 adbms_match, adbms_attach, NULL, NULL);
114
115 static int adbms_enable(void *);
116 static int adbms_ioctl(void *, u_long, void *, int, struct lwp *);
117 static void adbms_disable(void *);
118
119 /*
120 * handle tapping the trackpad
121 * different pads report different button counts and use slightly different
122 * protocols
123 */
124 static void adbms_mangle_2(struct adbms_softc *, int);
125 static void adbms_mangle_4(struct adbms_softc *, int);
126 static void adbms_handler(void *, int, uint8_t *);
127 static int adbms_wait(struct adbms_softc *, int);
128 static int sysctl_adbms_tap(SYSCTLFN_ARGS);
129
130 const struct wsmouse_accessops adbms_accessops = {
131 adbms_enable,
132 adbms_ioctl,
133 adbms_disable,
134 };
135
136 static int
137 adbms_match(device_t parent, cfdata_t cf, void *aux)
138 {
139 struct adb_attach_args *aaa = aux;
140
141 if (aaa->dev->original_addr == ADBADDR_MS)
142 return 1;
143 else
144 return 0;
145 }
146
147 static void
148 adbms_attach(device_t parent, device_t self, void *aux)
149 {
150 struct adbms_softc *sc = device_private(self);
151 struct adb_attach_args *aaa = aux;
152 struct wsmousedev_attach_args a;
153
154 sc->sc_dev = self;
155 sc->sc_ops = aaa->ops;
156 sc->sc_adbdev = aaa->dev;
157 sc->sc_adbdev->cookie = sc;
158 sc->sc_adbdev->handler = adbms_handler;
159 sc->sc_us = ADBTALK(sc->sc_adbdev->current_addr, 0);
160 printf(" addr %d: ", sc->sc_adbdev->current_addr);
161
162 sc->sc_class = MSCLASS_MOUSE;
163 sc->sc_buttons = 1;
164 sc->sc_res = 100;
165 sc->sc_devid[0] = 0;
166 sc->sc_devid[4] = 0;
167 sc->sc_poll = 0;
168 sc->sc_msg_len = 0;
169 sc->sc_tapping = 1;
170
171 ems_init(sc);
172
173 /* print out the type of mouse we have */
174 switch (sc->sc_adbdev->handler_id) {
175 case ADBMS_100DPI:
176 printf("%d-button, %u dpi mouse\n", sc->sc_buttons,
177 sc->sc_res);
178 break;
179 case ADBMS_200DPI:
180 sc->sc_res = 200;
181 printf("%d-button, %u dpi mouse\n", sc->sc_buttons,
182 sc->sc_res);
183 break;
184 case ADBMS_MSA3:
185 printf("Mouse Systems A3 mouse, %d-button, %u dpi\n",
186 sc->sc_buttons, sc->sc_res);
187 break;
188 case ADBMS_USPEED:
189 printf("MicroSpeed mouse, default parameters\n");
190 break;
191 case ADBMS_UCONTOUR:
192 printf("Contour mouse, default parameters\n");
193 break;
194 case ADBMS_TURBO:
195 printf("Kensington Turbo Mouse\n");
196 break;
197 case ADBMS_EXTENDED:
198 if (sc->sc_devid[0] == '\0') {
199 printf("Logitech ");
200 switch (sc->sc_class) {
201 case MSCLASS_MOUSE:
202 printf("MouseMan (non-EMP) mouse");
203 break;
204 case MSCLASS_TRACKBALL:
205 printf("TrackMan (non-EMP) trackball");
206 break;
207 default:
208 printf("non-EMP relative positioning device");
209 break;
210 }
211 printf("\n");
212 } else {
213 printf("EMP ");
214 switch (sc->sc_class) {
215 case MSCLASS_TABLET:
216 printf("tablet");
217 break;
218 case MSCLASS_MOUSE:
219 printf("mouse");
220 break;
221 case MSCLASS_TRACKBALL:
222 printf("trackball");
223 break;
224 case MSCLASS_TRACKPAD:
225 printf("trackpad");
226 init_trackpad(sc);
227 break;
228 default:
229 printf("unknown device");
230 break;
231 }
232 printf(" <%s> %d-button, %u dpi\n", sc->sc_devid,
233 sc->sc_buttons, sc->sc_res);
234 }
235 break;
236 default:
237 printf("relative positioning device (mouse?) (%d)\n",
238 sc->sc_adbdev->handler_id);
239 break;
240 }
241
242 a.accessops = &adbms_accessops;
243 a.accesscookie = sc;
244 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE);
245 }
246
247 /*
248 * Initialize extended mouse support -- probes devices as described
249 * in Inside Macintosh: Devices, Chapter 5 "ADB Manager".
250 *
251 * Extended Mouse Protocol is documented in TechNote HW1:
252 * "ADB - The Untold Story: Space Aliens Ate My Mouse"
253 *
254 * Supports: Extended Mouse Protocol, MicroSpeed Mouse Deluxe,
255 * Mouse Systems A^3 Mouse, Logitech non-EMP MouseMan
256 */
257 void
258 ems_init(struct adbms_softc *sc)
259 {
260
261 DPRINTF("ems_init %x\n", sc->sc_adbdev->handler_id);
262
263 switch (sc->sc_adbdev->handler_id) {
264 case ADBMS_USPEED:
265 case ADBMS_UCONTOUR:
266 adbms_init_uspeed(sc);
267 return;
268 case ADBMS_TURBO:
269 adbms_init_turbo(sc);
270 return;
271 case ADBMS_100DPI:
272 case ADBMS_200DPI:
273 adbms_init_mouse(sc);
274 }
275 }
276
277 static void
278 adbms_init_uspeed(struct adbms_softc *sc)
279 {
280 uint8_t cmd, addr, buffer[4];
281
282 addr = sc->sc_adbdev->current_addr;
283
284
285 /* Found MicroSpeed Mouse Deluxe Mac or Contour Mouse */
286 cmd = ADBLISTEN(addr, 1);
287
288 /*
289 * To setup the MicroSpeed or the Contour, it appears
290 * that we can send the following command to the mouse
291 * and then expect data back in the form:
292 * buffer[0] = 4 (bytes)
293 * buffer[1], buffer[2] as std. mouse
294 * buffer[3] = buffer[4] = 0xff when no buttons
295 * are down. When button N down, bit N is clear.
296 * buffer[4]'s locking mask enables a
297 * click to toggle the button down state--sort of
298 * like the "Easy Access" shift/control/etc. keys.
299 * buffer[3]'s alternative speed mask enables using
300 * different speed when the corr. button is down
301 */
302 buffer[0] = 0x20; /* Alternative speed */
303 buffer[1] = 0x00; /* speed = maximum */
304 buffer[2] = 0x10; /* enable extended protocol,
305 * lower bits = alt. speed mask
306 * = 0000b
307 */
308 buffer[3] = 0x07; /* Locking mask = 0000b,
309 * enable buttons = 0111b
310 */
311 adbms_send_sync(sc, cmd, 4, buffer);
312
313 sc->sc_buttons = 3;
314
315 adbms_send_sync(sc, ADBTALK(addr, 1), 0, NULL);
316 #ifdef ADBMS_DEBUG
317 int i;
318 printf("reg *");
319 for (i = 0; i < sc->sc_msg_len; i++)
320 printf(" %02x", sc->sc_buffer[i]);
321 printf("\n");
322 #endif
323 sc->sc_res = 200;
324
325 if (sc->sc_msg_len == 0) {
326 DPRINTF("found ancient MacTrac, middle button can't be programmed\n");
327 sc->sc_res = 100;
328 }
329 }
330
331 static int
332 adbms_turbo_csum(uint8_t *d)
333 {
334 int i = 0, sum = 0;
335
336 for (i = 0; i < 7; i++)
337 sum ^= d[i];
338 return (sum ^ 0xff);
339 }
340
341 static void
342 adbms_init_turbo(struct adbms_softc *sc)
343 {
344 uint8_t addr;
345
346 /* Found Kensington Turbo Mouse */
347
348 /*
349 * byte 1 assigns what which button does
350 - 0x08 - button 1 - 1, button 2 - nothing
351 - 0x09 - both buttons - 1
352 - 0x0a - button 1 - 1, button 2 - toggle 1
353 - 0x0b - button 1 - 1, button 2 - nothing
354 - 0x0c - button 1 - 1, button 2 - 2
355 - 0x0e - button 1 - 1, button 2 - 3
356 - 0x0f - button 1 - 1, button 2 - toggle 3
357 - 0x10 - button 1 toggle 1, button 2 nothing
358 - 0x11 - button 1 - toggle 1, button 2 - 1
359 - 0x12 - both toggle 1
360 - 0x14 - button 1 toggle 1, button 2 - 2
361 - 0x21 - button 1 - 2, button 2 - 1
362 - 0x31 - button 1 - 3, button 2 - 1
363 * byte 4 programs a delay for button presses, apparently in 1/100 seconds
364 * byte 7 is a simple XOR checksum, writes will only stick if it's valid
365 as in, b[7] = (b[0] ^ b[1] ^ ... ^ b[6]) ^ 0xff
366 */
367
368 /* this seems to be the most reasonable default */
369 static u_char data[] =
370 { 0xa5, 0x0e, 0, 0, 1, 0xff, 0xff, 0/*0x55*/ };
371
372 addr = sc->sc_adbdev->current_addr;
373
374 #ifdef ADBMS_DEBUG
375 {
376 int i;
377 adbms_send_sync(sc, ADBTALK(addr, 2), 0, NULL);
378 printf("reg *");
379 for (i = 0; i < sc->sc_msg_len; i++)
380 printf(" %02x", sc->sc_buffer[i]);
381 printf("\n");
382 }
383 #endif
384
385 adbms_send_sync(sc, ADBFLUSH(addr), 0, NULL);
386 data[7] = adbms_turbo_csum(data);
387 adbms_send_sync(sc, ADBLISTEN(addr, 2), 8, data);
388
389
390 #ifdef ADBMS_DEBUG
391 int i, reg;
392 for (reg = 1; reg < 4; reg++) {
393 adbms_send_sync(sc, ADBTALK(addr, reg), 0, NULL);
394 printf("reg %d", reg);
395 for (i = 0; i < sc->sc_msg_len; i++)
396 printf(" %02x", sc->sc_buffer[i]);
397 printf("\n");
398 }
399 #endif
400 }
401
402 static void
403 adbms_init_mouse(struct adbms_softc *sc)
404 {
405 int len;
406 uint8_t cmd, addr, buffer[16];
407
408 addr = sc->sc_adbdev->current_addr;
409 /* found a mouse */
410 cmd = ADBTALK(addr, 3);
411 if (!adbms_send_sync(sc, cmd, 0, NULL)) {
412 #ifdef ADBMS_DEBUG
413 printf("adb: ems_init timed out\n");
414 #endif
415 return;
416 }
417
418 /* Attempt to initialize Extended Mouse Protocol */
419 len = sc->sc_msg_len;
420 memcpy(buffer, sc->sc_buffer, len);
421 DPRINTF("buffer: %02x %02x\n", buffer[0], buffer[1]);
422 buffer[1] = 4; /* make handler ID 4 */
423 cmd = ADBLISTEN(addr, 3);
424 if (!adbms_send_sync(sc, cmd, len, buffer)) {
425 #ifdef ADBMS_DEBUG
426 printf("adb: ems_init timed out\n");
427 #endif
428 return;
429 }
430
431 /*
432 * Check to see if successful, if not
433 * try to initialize it as other types
434 */
435 cmd = ADBTALK(addr, 3);
436 if (!adbms_send_sync(sc, cmd, 0, NULL)) {
437 DPRINTF("timeout checking for EMP switch\n");
438 return;
439 }
440 DPRINTF("new handler ID: %02x\n", sc->sc_buffer[1]);
441 if (sc->sc_buffer[1] == ADBMS_EXTENDED) {
442 sc->sc_adbdev->handler_id = ADBMS_EXTENDED;
443 cmd = ADBTALK(addr, 1);
444 if(!adbms_send_sync(sc, cmd, 0, NULL)) {
445 DPRINTF("adb: ems_init timed out\n");
446 return;
447 }
448
449 len = sc->sc_msg_len;
450 memcpy(buffer, sc->sc_buffer, len);
451
452 if (sc->sc_msg_len == 8) {
453 uint16_t res;
454 /* we have a true EMP device */
455 #ifdef ADB_PRINT_EMP
456
457 printf("EMP: %02x %02x %02x %02x %02x %02x %02x %02x\n",
458 buffer[0], buffer[1], buffer[2], buffer[3],
459 buffer[4], buffer[5], buffer[6], buffer[7]);
460 #endif
461 memcpy(sc->sc_devid, &buffer[0], 4);
462 memcpy(&res, &buffer[4], sizeof(res));
463 sc->sc_res = res;
464 sc->sc_class = buffer[6];
465 sc->sc_buttons = buffer[7];
466 } else if (buffer[0] == 0x9a &&
467 ((buffer[1] == 0x20) || (buffer[1] == 0x21))) {
468 /*
469 * Set up non-EMP Mouseman/Trackman to put
470 * button bits in 3rd byte instead of sending
471 * via pseudo keyboard device.
472 */
473 if (buffer[1] == 0x21)
474 sc->sc_class = MSCLASS_TRACKBALL;
475 else
476 sc->sc_class = MSCLASS_MOUSE;
477
478 cmd = ADBLISTEN(addr, 1);
479 buffer[0]=0x00;
480 buffer[1]=0x81;
481 adbms_send_sync(sc, cmd, 2, buffer);
482
483 cmd = ADBLISTEN(addr, 1);
484 buffer[0]=0x01;
485 buffer[1]=0x81;
486 adbms_send_sync(sc, cmd, 2, buffer);
487
488 cmd = ADBLISTEN(addr, 1);
489 buffer[0]=0x02;
490 buffer[1]=0x81;
491 adbms_send_sync(sc, cmd, 2, buffer);
492
493 cmd = ADBLISTEN(addr, 1);
494 buffer[0]=0x03;
495 buffer[1]=0x38;
496 adbms_send_sync(sc, cmd, 2, buffer);
497
498 sc->sc_buttons = 3;
499 sc->sc_res = 400;
500 }
501 } else {
502 /* Attempt to initialize as an A3 mouse */
503 buffer[1] = 0x03; /* make handler ID 3 */
504 cmd = ADBLISTEN(addr, 3);
505 if (!adbms_send_sync(sc, cmd, len, buffer)) {
506 #ifdef ADBMS_DEBUG
507 printf("adb: ems_init timed out\n");
508 #endif
509 return;
510 }
511
512 /*
513 * Check to see if successful, if not
514 * try to initialize it as other types
515 */
516 cmd = ADBTALK(addr, 3);
517 if(adbms_send_sync(sc, cmd, 0, NULL)) {
518 len = sc->sc_msg_len;
519 memcpy(buffer, sc->sc_buffer, len);
520 if (buffer[1] == ADBMS_MSA3) {
521 sc->sc_adbdev->handler_id = ADBMS_MSA3;
522 /* Initialize as above */
523 cmd = ADBLISTEN(addr, 2);
524 /* listen 2 */
525 buffer[0] = 0x00;
526 /* Irrelevant, buffer has 0x77 */
527 buffer[2] = 0x07;
528 /*
529 * enable 3 button mode = 0111b,
530 * speed = normal
531 */
532 adbms_send_sync(sc, cmd, 3, buffer);
533 sc->sc_buttons = 3;
534 sc->sc_res = 300;
535 }
536 }
537 }
538 }
539
540 static void
541 adbms_handler(void *cookie, int len, uint8_t *data)
542 {
543 struct adbms_softc *sc = cookie;
544
545 #ifdef ADBMS_DEBUG
546 int i;
547 printf("%s: %02x - ", device_xname(sc->sc_dev), sc->sc_us);
548 for (i = 0; i < len; i++) {
549 printf(" %02x", data[i]);
550 }
551 printf("\n");
552 #endif
553 if (len >= 2) {
554 memcpy(sc->sc_buffer, &data[2], len - 2);
555 sc->sc_msg_len = len - 2;
556 if ((data[1] == sc->sc_us) && (len > 2)) {
557 /* make sense of the mouse message */
558 adbms_process_event(sc, sc->sc_msg_len, sc->sc_buffer);
559 return;
560 }
561 wakeup(&sc->sc_event);
562 } else {
563 DPRINTF("bogus message\n");
564 }
565 }
566
567 static void
568 adbms_process_event(struct adbms_softc *sc, int len, uint8_t *buffer)
569 {
570 int buttons = 0, mask, dx, dy, i;
571 int button_bit = 1;
572
573 if ((sc->sc_adbdev->handler_id == ADBMS_EXTENDED) && (sc->sc_devid[0] == 0)) {
574 /* massage the data to look like EMP data */
575 if ((buffer[2] & 0x04) == 0x04)
576 buffer[0] &= 0x7f;
577 else
578 buffer[0] |= 0x80;
579 if ((buffer[2] & 0x02) == 0x02)
580 buffer[1] &= 0x7f;
581 else
582 buffer[1] |= 0x80;
583 if ((buffer[2] & 0x01) == 0x01)
584 buffer[2] = 0x00;
585 else
586 buffer[2] = 0x80;
587 }
588
589 switch (sc->sc_adbdev->handler_id) {
590 case ADBMS_USPEED:
591 case ADBMS_UCONTOUR:
592 /* MicroSpeed mouse and Contour mouse */
593 if (len == 4)
594 buttons = (~buffer[3]) & 0xff;
595 else {
596 /*
597 * deal with the strange way old MacTracs report
598 * button events:
599 * 0x8080 - all up
600 * 0x0000 - right button down
601 * ox0080 - left button down
602 * 0x8000 - both buttons down
603 * the middle button seems to do wome weird
604 * click lock thing
605 */
606 int bt = ((buffer[0] & 0x80) >> 6) |
607 ((buffer[1] & 0x80) >> 7);
608 int bttrans[] = {4, 1, 5, 0};
609 buttons = bttrans[bt];
610 DPRINTF("microspeed buttons %x\n", buttons);
611 }
612 break;
613 case ADBMS_MSA3:
614 /* Mouse Systems A3 mouse */
615 if (len == 3)
616 buttons = (~buffer[2]) & 0x07;
617 else
618 buttons = (buffer[0] & 0x80) ? 0 : 1;
619 break;
620 default:
621 /* Classic Mouse Protocol (up to 2 buttons) */
622 for (i = 0; i < 2; i++, button_bit <<= 1)
623 /* 0 when button down */
624 if (!(buffer[i] & 0x80))
625 buttons |= button_bit;
626 else
627 buttons &= ~button_bit;
628 /* Extended Protocol (up to 6 more buttons) */
629 for (mask = 0x80; i < len;
630 i += (mask == 0x80), button_bit <<= 1) {
631 /* 0 when button down */
632 if (!(buffer[i] & mask))
633 buttons |= button_bit;
634 else
635 buttons &= ~button_bit;
636 mask = ((mask >> 4) & 0xf)
637 | ((mask & 0xf) << 4);
638 }
639 break;
640 }
641
642 if ((sc->sc_adbdev->handler_id != ADBMS_EXTENDED) &&
643 (sc->sc_adbdev->handler_id != ADBMS_TURBO)) {
644 dx = ((int)(buffer[1] & 0x3f)) - ((buffer[1] & 0x40) ? 64 : 0);
645 dy = ((int)(buffer[0] & 0x3f)) - ((buffer[0] & 0x40) ? 64 : 0);
646 } else {
647 /* EMP crap, additional motion bits */
648 int shift = 7, ddx, ddy, sign, smask;
649
650 #ifdef ADBMS_DEBUG
651 printf("EMP packet:");
652 for (i = 0; i < len; i++)
653 printf(" %02x", buffer[i]);
654 printf("\n");
655 #endif
656 dx = (int)buffer[1] & 0x7f;
657 dy = (int)buffer[0] & 0x7f;
658 for (i = 2; i < len; i++) {
659 ddx = (buffer[i] & 0x07);
660 ddy = (buffer[i] & 0x70) >> 4;
661 dx |= (ddx << shift);
662 dy |= (ddy << shift);
663 shift += 3;
664 }
665 sign = 1 << (shift - 1);
666 smask = 0xffffffff << shift;
667 if (dx & sign)
668 dx |= smask;
669 if (dy & sign)
670 dy |= smask;
671 #ifdef ADBMS_DEBUG
672 printf("%d %d %08x %d\n", dx, dy, smask, shift);
673 #endif
674 }
675
676 if (sc->sc_class == MSCLASS_TRACKPAD) {
677
678 if (sc->sc_tapping == 1) {
679 if (sc->sc_down) {
680 /* finger is down - collect motion data */
681 sc->sc_x += dx;
682 sc->sc_y += dy;
683 }
684 DPRINTF("buttons: %02x\n", buttons);
685 switch (sc->sc_buttons) {
686 case 2:
687 buttons |= ((buttons & 2) >> 1);
688 adbms_mangle_2(sc, buttons);
689 break;
690 case 4:
691 adbms_mangle_4(sc, buttons);
692 break;
693 }
694 }
695 /* filter the pseudo-buttons out */
696 buttons &= 1;
697 }
698
699 if (sc->sc_wsmousedev)
700 wsmouse_input(sc->sc_wsmousedev, sc->sc_mb | buttons,
701 dx, -dy, 0, 0,
702 WSMOUSE_INPUT_DELTA);
703 #if NAED > 0
704 aed_input(&new_event);
705 #endif
706 }
707
708 static void
709 adbms_mangle_2(struct adbms_softc *sc, int buttons)
710 {
711
712 if (buttons & 4) {
713 /* finger down on pad */
714 if (sc->sc_down == 0) {
715 sc->sc_down = 1;
716 sc->sc_x = 0;
717 sc->sc_y = 0;
718 }
719 }
720 if (buttons & 8) {
721 /* finger up */
722 if (sc->sc_down) {
723 if (((sc->sc_x * sc->sc_x +
724 sc->sc_y * sc->sc_y) < 3) &&
725 (sc->sc_wsmousedev)) {
726 /*
727 * if there wasn't much movement between
728 * finger down and up again we assume
729 * someone tapped the pad and we just
730 * send a mouse button event
731 */
732 wsmouse_input(sc->sc_wsmousedev,
733 1, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
734 }
735 sc->sc_down = 0;
736 }
737 }
738 }
739
740 static void
741 adbms_mangle_4(struct adbms_softc *sc, int buttons)
742 {
743
744 if (buttons & 0x20) {
745 /* finger down on pad */
746 if (sc->sc_down == 0) {
747 sc->sc_down = 1;
748 sc->sc_x = 0;
749 sc->sc_y = 0;
750 }
751 }
752 if ((buttons & 0x20) == 0) {
753 /* finger up */
754 if (sc->sc_down) {
755 if (((sc->sc_x * sc->sc_x +
756 sc->sc_y * sc->sc_y) < 3) &&
757 (sc->sc_wsmousedev)) {
758 /*
759 * if there wasn't much movement between
760 * finger down and up again we assume
761 * someone tapped the pad and we just
762 * send a mouse button event
763 */
764 wsmouse_input(sc->sc_wsmousedev,
765 1, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
766 }
767 sc->sc_down = 0;
768 }
769 }
770 }
771
772 static int
773 adbms_enable(void *v)
774 {
775 return 0;
776 }
777
778 static int
779 adbms_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
780 {
781
782 switch (cmd) {
783 case WSMOUSEIO_GTYPE:
784 *(u_int *)data = WSMOUSE_TYPE_ADB;
785 break;
786
787 default:
788 return (EPASSTHROUGH);
789 }
790 return (0);
791 }
792
793 static void
794 adbms_disable(void *v)
795 {
796 }
797
798 static void
799 init_trackpad(struct adbms_softc *sc)
800 {
801 const struct sysctlnode *me = NULL, *node = NULL;
802 int cmd, addr, ret;
803 uint8_t buffer[16];
804 uint8_t b2[] = {0x99, 0x94, 0x19, 0xff, 0xb2, 0x8a, 0x1b, 0x50};
805
806 addr = sc->sc_adbdev->current_addr;
807 cmd = ADBTALK(addr, 1);
808 if (!adbms_send_sync(sc, cmd, 0, NULL))
809 return;
810
811 if (sc->sc_msg_len != 8)
812 return;
813
814 memcpy(buffer, sc->sc_buffer, 8);
815
816 /* now whack the pad */
817 cmd = ADBLISTEN(addr, 1);
818 buffer[6] = 0x0d;
819 adbms_send_sync(sc, cmd, 8, buffer);
820
821 delay(1000);
822 cmd = ADBLISTEN(addr, 2);
823 adbms_send_sync(sc, cmd, 8, b2);
824
825 delay(1000);
826 cmd = ADBLISTEN(addr, 1);
827 buffer[6] = 0x03;
828 adbms_send_sync(sc, cmd, 8, buffer);
829
830 cmd = ADBFLUSH(addr);
831 adbms_send_sync(sc, cmd, 0, NULL);
832 delay(1000);
833
834 /*
835 * setup a sysctl node to control whether tapping the pad should
836 * trigger mouse button events
837 */
838
839 sc->sc_tapping = 1;
840
841 ret = sysctl_createv(NULL, 0, NULL, &me,
842 CTLFLAG_READWRITE,
843 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
844 NULL, 0, NULL, 0,
845 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
846
847 ret = sysctl_createv(NULL, 0, NULL, &node,
848 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
849 CTLTYPE_INT, "tapping", "tapping the pad causes button events",
850 sysctl_adbms_tap, 1, (void *)sc, 0,
851 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
852
853 (void)ret;
854 }
855
856 static int
857 adbms_wait(struct adbms_softc *sc, int timeout)
858 {
859 int cnt = 0;
860
861 if (sc->sc_poll) {
862 while (sc->sc_msg_len == -1) {
863 sc->sc_ops->poll(sc->sc_ops->cookie);
864 }
865 } else {
866 while ((sc->sc_msg_len == -1) && (cnt < timeout)) {
867 tsleep(&sc->sc_event, 0, "adbmsio", hz);
868 cnt++;
869 }
870 }
871 return (sc->sc_msg_len > 0);
872 }
873
874 static int
875 adbms_send_sync(struct adbms_softc *sc, uint8_t cmd, int len, uint8_t *msg)
876 {
877 int i;
878
879 sc->sc_msg_len = -1;
880 DPRINTF("send: %02x", cmd);
881 for (i = 0; i < len; i++)
882 DPRINTF(" %02x", msg[i]);
883 DPRINTF("\n");
884 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, len, msg);
885 adbms_wait(sc, 3);
886 return (sc->sc_msg_len != -1);
887 }
888
889 static int
890 sysctl_adbms_tap(SYSCTLFN_ARGS)
891 {
892 struct sysctlnode node = *rnode;
893 struct adbms_softc *sc = node.sysctl_data;
894
895 node.sysctl_idata = sc->sc_tapping;
896
897 if (newp) {
898
899 /* we're asked to write */
900 node.sysctl_data = &sc->sc_tapping;
901 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
902
903 sc->sc_tapping = (*(int *)node.sysctl_data == 0) ? 0 : 1;
904 return 0;
905 }
906 return EINVAL;
907 } else {
908
909 node.sysctl_data = &sc->sc_tapping;
910 node.sysctl_size = 4;
911 return (sysctl_lookup(SYSCTLFN_CALL(&node)));
912 }
913
914 return 0;
915 }
916
917 SYSCTL_SETUP(sysctl_ams_setup, "sysctl ams subtree setup")
918 {
919
920 sysctl_createv(NULL, 0, NULL, NULL,
921 CTLFLAG_PERMANENT,
922 CTLTYPE_NODE, "machdep", NULL,
923 NULL, 0, NULL, 0,
924 CTL_MACHDEP, CTL_EOL);
925 }
926