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