1 /* $NetBSD: adb_ms.c,v 1.7 2007/04/16 00:22:55 macallan Exp $ */
4 * Copyright (C) 1998 Colin Wood
5 * Copyright (C) 2006, 2007 Michael Lorenz
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: adb_ms.c,v 1.7 2007/04/16 00:22:55 macallan Exp $");
37 #include <sys/param.h>
38 #include <sys/device.h>
39 #include <sys/fcntl.h>
41 #include <sys/select.h>
43 #include <sys/signalvar.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/sysctl.h>
48 #include <machine/autoconf.h>
50 #include <dev/wscons/wsconsio.h>
51 #include <dev/wscons/wsmousevar.h>
53 #include <machine/adbsys.h>
54 #include <dev/adb/adbvar.h>
59 #define DPRINTF printf
61 #define DPRINTF while (0) printf
65 * State info, per mouse instance.
69 struct adb_device
*sc_adbdev
;
70 struct adb_bus_accessops
*sc_ops
;
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 struct device
*sc_wsmousedev
;
80 /* helpers for trackpads */
83 * trackpad protocol variant. Known so far:
84 * 2 buttons - PowerBook 3400, single events on button 3 and 4 indicate
86 * 4 buttons - iBook G4, button 6 indicates finger down, button 4 is
95 uint8_t sc_buffer
[16];
98 /* EMP device classes */
99 #define MSCLASS_TABLET 0
100 #define MSCLASS_MOUSE 1
101 #define MSCLASS_TRACKBALL 2
102 #define MSCLASS_TRACKPAD 3
105 * Function declarations.
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 ms_processevent(adb_event_t *event, struct adbms_softc *);
111 static void init_trackpad(struct adbms_softc
*);
112 static void adbms_init_mouse(struct adbms_softc
*);
113 static void adbms_init_turbo(struct adbms_softc
*);
114 static void adbms_init_uspeed(struct adbms_softc
*);
115 static void adbms_process_event(struct adbms_softc
*, int, uint8_t *);
116 static int adbms_send_sync(struct adbms_softc
*, uint8_t, int, uint8_t *);
118 /* Driver definition. */
119 CFATTACH_DECL_NEW(adbms
, sizeof(struct adbms_softc
),
120 adbms_match
, adbms_attach
, NULL
, NULL
);
122 static int adbms_enable(void *);
123 static int adbms_ioctl(void *, u_long
, void *, int, struct lwp
*);
124 static void adbms_disable(void *);
127 * handle tapping the trackpad
128 * different pads report different button counts and use slightly different
131 static void adbms_mangle_2(struct adbms_softc
*, int);
132 static void adbms_mangle_4(struct adbms_softc
*, int);
133 static void adbms_handler(void *, int, uint8_t *);
134 static int adbms_wait(struct adbms_softc
*, int);
135 static int sysctl_adbms_tap(SYSCTLFN_ARGS
);
137 const struct wsmouse_accessops adbms_accessops
= {
144 adbms_match(device_t parent
, cfdata_t cf
, void *aux
)
146 struct adb_attach_args
*aaa
= aux
;
148 if (aaa
->dev
->original_addr
== ADBADDR_MS
)
155 adbms_attach(device_t parent
, device_t self
, void *aux
)
157 struct adbms_softc
*sc
= device_private(self
);
158 struct adb_attach_args
*aaa
= aux
;
159 struct wsmousedev_attach_args a
;
162 sc
->sc_ops
= aaa
->ops
;
163 sc
->sc_adbdev
= aaa
->dev
;
164 sc
->sc_adbdev
->cookie
= sc
;
165 sc
->sc_adbdev
->handler
= adbms_handler
;
166 sc
->sc_us
= ADBTALK(sc
->sc_adbdev
->current_addr
, 0);
167 printf(" addr %d: ", sc
->sc_adbdev
->current_addr
);
169 sc
->sc_class
= MSCLASS_MOUSE
;
180 /* print out the type of mouse we have */
181 switch (sc
->sc_adbdev
->handler_id
) {
183 printf("%d-button, %d dpi mouse\n", sc
->sc_buttons
,
188 printf("%d-button, %d dpi mouse\n", sc
->sc_buttons
,
192 printf("Mouse Systems A3 mouse, %d-button, %d dpi\n",
193 sc
->sc_buttons
, (int)(sc
->sc_res
));
196 printf("MicroSpeed mouse, default parameters\n");
199 printf("Contour mouse, default parameters\n");
202 printf("Kensington Turbo Mouse\n");
205 if (sc
->sc_devid
[0] == '\0') {
207 switch (sc
->sc_class
) {
209 printf("MouseMan (non-EMP) mouse");
211 case MSCLASS_TRACKBALL
:
212 printf("TrackMan (non-EMP) trackball");
215 printf("non-EMP relative positioning device");
221 switch (sc
->sc_class
) {
228 case MSCLASS_TRACKBALL
:
231 case MSCLASS_TRACKPAD
:
236 printf("unknown device");
239 printf(" <%s> %d-button, %d dpi\n", sc
->sc_devid
,
240 sc
->sc_buttons
, (int)(sc
->sc_res
));
244 printf("relative positioning device (mouse?) (%d)\n",
245 sc
->sc_adbdev
->handler_id
);
249 a
.accessops
= &adbms_accessops
;
251 sc
->sc_wsmousedev
= config_found(self
, &a
, wsmousedevprint
);
256 * Initialize extended mouse support -- probes devices as described
257 * in Inside Macintosh: Devices, Chapter 5 "ADB Manager".
259 * Extended Mouse Protocol is documented in TechNote HW1:
260 * "ADB - The Untold Story: Space Aliens Ate My Mouse"
262 * Supports: Extended Mouse Protocol, MicroSpeed Mouse Deluxe,
263 * Mouse Systems A^3 Mouse, Logitech non-EMP MouseMan
266 ems_init(struct adbms_softc
*sc
)
269 DPRINTF("ems_init %d\n", sc
->sc_adbdev
->handler_id
);
271 switch (sc
->sc_adbdev
->handler_id
) {
274 adbms_init_uspeed(sc
);
277 adbms_init_turbo(sc
);
281 adbms_init_mouse(sc
);
286 adbms_init_uspeed(struct adbms_softc
*sc
)
288 uint8_t cmd
, addr
, buffer
[4];
290 addr
= sc
->sc_adbdev
->current_addr
;
292 /* Found MicroSpeed Mouse Deluxe Mac or Contour Mouse */
293 cmd
= ADBLISTEN(addr
, 1);
296 * To setup the MicroSpeed or the Contour, it appears
297 * that we can send the following command to the mouse
298 * and then expect data back in the form:
299 * buffer[0] = 4 (bytes)
300 * buffer[1], buffer[2] as std. mouse
301 * buffer[3] = buffer[4] = 0xff when no buttons
302 * are down. When button N down, bit N is clear.
303 * buffer[4]'s locking mask enables a
304 * click to toggle the button down state--sort of
305 * like the "Easy Access" shift/control/etc. keys.
306 * buffer[3]'s alternative speed mask enables using
307 * different speed when the corr. button is down
309 buffer
[0] = 0x00; /* Alternative speed */
310 buffer
[1] = 0x00; /* speed = maximum */
311 buffer
[2] = 0x10; /* enable extended protocol,
312 * lower bits = alt. speed mask
315 buffer
[3] = 0x07; /* Locking mask = 0000b,
316 * enable buttons = 0111b
318 adbms_send_sync(sc
, cmd
, 4, buffer
);
325 adbms_init_turbo(struct adbms_softc
*sc
)
329 /* Found Kensington Turbo Mouse */
330 static u_char data1
[] =
331 { 0xe7, 0x8c, 0, 0, 0, 0xff, 0xff, 0x94 };
332 static u_char data2
[] =
333 { 0xa5, 0x14, 0, 0, 0x69, 0xff, 0xff, 0x27 };
335 addr
= sc
->sc_adbdev
->current_addr
;
337 adbms_send_sync(sc
, ADBFLUSH(addr
), 0, NULL
);
338 adbms_send_sync(sc
, ADBLISTEN(addr
, 2), 8, data1
);
339 adbms_send_sync(sc
, ADBFLUSH(addr
), 0, NULL
);
340 adbms_send_sync(sc
, ADBLISTEN(addr
, 2), 8, data2
);
344 adbms_init_mouse(struct adbms_softc
*sc
)
347 uint8_t cmd
, addr
, buffer
[16];
349 addr
= sc
->sc_adbdev
->current_addr
;
351 cmd
= ADBTALK(addr
, 3);
352 if (!adbms_send_sync(sc
, cmd
, 0, NULL
)) {
354 printf("adb: ems_init timed out\n");
359 /* Attempt to initialize Extended Mouse Protocol */
360 len
= sc
->sc_msg_len
;
361 memcpy(buffer
, sc
->sc_buffer
, len
);
362 DPRINTF("buffer: %02x %02x\n", buffer
[0], buffer
[1]);
363 buffer
[1] = 4; /* make handler ID 4 */
364 cmd
= ADBLISTEN(addr
, 3);
365 if (!adbms_send_sync(sc
, cmd
, len
, buffer
)) {
367 printf("adb: ems_init timed out\n");
373 * Check to see if successful, if not
374 * try to initialize it as other types
376 cmd
= ADBTALK(addr
, 3);
377 if (!adbms_send_sync(sc
, cmd
, 0, NULL
)) {
378 DPRINTF("timeout checking for EMP switch\n");
381 DPRINTF("new handler ID: %02x\n", sc
->sc_buffer
[1]);
382 if (sc
->sc_buffer
[1] == ADBMS_EXTENDED
) {
383 sc
->sc_adbdev
->handler_id
= ADBMS_EXTENDED
;
384 cmd
= ADBTALK(addr
, 1);
385 if(!adbms_send_sync(sc
, cmd
, 0, NULL
)) {
386 DPRINTF("adb: ems_init timed out\n");
390 len
= sc
->sc_msg_len
;
391 memcpy(buffer
, sc
->sc_buffer
, len
);
393 if (sc
->sc_msg_len
== 8) {
394 /* we have a true EMP device */
397 printf("EMP: %02x %02x %02x %02x %02x %02x %02x %02x\n",
398 buffer
[0], buffer
[1], buffer
[2], buffer
[3],
399 buffer
[4], buffer
[5], buffer
[6], buffer
[7]);
401 sc
->sc_class
= buffer
[6];
402 sc
->sc_buttons
= buffer
[7];
403 sc
->sc_res
= (int)*(short *)&buffer
[4];
404 memcpy(sc
->sc_devid
, &(buffer
[0]), 4);
405 } else if (buffer
[0] == 0x9a &&
406 ((buffer
[1] == 0x20) || (buffer
[1] == 0x21))) {
408 * Set up non-EMP Mouseman/Trackman to put
409 * button bits in 3rd byte instead of sending
410 * via pseudo keyboard device.
412 if (buffer
[1] == 0x21)
413 sc
->sc_class
= MSCLASS_TRACKBALL
;
415 sc
->sc_class
= MSCLASS_MOUSE
;
417 cmd
= ADBLISTEN(addr
, 1);
420 adbms_send_sync(sc
, cmd
, 2, buffer
);
422 cmd
= ADBLISTEN(addr
, 1);
425 adbms_send_sync(sc
, cmd
, 2, buffer
);
427 cmd
= ADBLISTEN(addr
, 1);
430 adbms_send_sync(sc
, cmd
, 2, buffer
);
432 cmd
= ADBLISTEN(addr
, 1);
435 adbms_send_sync(sc
, cmd
, 2, buffer
);
441 /* Attempt to initialize as an A3 mouse */
442 buffer
[1] = 0x03; /* make handler ID 3 */
443 cmd
= ADBLISTEN(addr
, 3);
444 if (!adbms_send_sync(sc
, cmd
, len
, buffer
)) {
446 printf("adb: ems_init timed out\n");
452 * Check to see if successful, if not
453 * try to initialize it as other types
455 cmd
= ADBTALK(addr
, 3);
456 if(adbms_send_sync(sc
, cmd
, 0, NULL
)) {
457 len
= sc
->sc_msg_len
;
458 memcpy(buffer
, sc
->sc_buffer
, len
);
459 if (buffer
[1] == ADBMS_MSA3
) {
460 sc
->sc_adbdev
->handler_id
= ADBMS_MSA3
;
461 /* Initialize as above */
462 cmd
= ADBLISTEN(addr
, 2);
465 /* Irrelevant, buffer has 0x77 */
468 * enable 3 button mode = 0111b,
471 adbms_send_sync(sc
, cmd
, 3, buffer
);
480 adbms_handler(void *cookie
, int len
, uint8_t *data
)
482 struct adbms_softc
*sc
= cookie
;
486 printf("%s: %02x - ", device_xname(sc
->sc_dev
), sc
->sc_us
);
487 for (i
= 0; i
< len
; i
++) {
488 printf(" %02x", data
[i
]);
493 memcpy(sc
->sc_buffer
, &data
[2], len
- 2);
494 sc
->sc_msg_len
= len
- 2;
495 if (data
[1] == sc
->sc_us
) {
496 /* make sense of the mouse message */
497 adbms_process_event(sc
, sc
->sc_msg_len
, sc
->sc_buffer
);
500 wakeup(&sc
->sc_event
);
502 DPRINTF("bogus message\n");
507 adbms_process_event(struct adbms_softc
*sc
, int len
, uint8_t *buffer
)
509 int buttons
= 0, mask
, dx
, dy
, i
;
512 if ((sc
->sc_adbdev
->handler_id
== ADBMS_EXTENDED
) && (sc
->sc_devid
[0] == 0)) {
513 /* massage the data to look like EMP data */
514 if ((buffer
[2] & 0x04) == 0x04)
518 if ((buffer
[2] & 0x02) == 0x02)
522 if ((buffer
[2] & 0x01) == 0x01)
528 switch (sc
->sc_adbdev
->handler_id
) {
531 /* MicroSpeed mouse and Contour mouse */
533 buttons
= (~buffer
[3]) & 0xff;
535 buttons
= (buffer
[1] & 0x80) ? 0 : 1;
538 /* Mouse Systems A3 mouse */
540 buttons
= (~buffer
[2]) & 0x07;
542 buttons
= (buffer
[0] & 0x80) ? 0 : 1;
545 /* Classic Mouse Protocol (up to 2 buttons) */
546 for (i
= 0; i
< 2; i
++, button_bit
<<= 1)
547 /* 0 when button down */
548 if (!(buffer
[i
] & 0x80))
549 buttons
|= button_bit
;
551 buttons
&= ~button_bit
;
552 /* Extended Protocol (up to 6 more buttons) */
553 for (mask
= 0x80; i
< len
;
554 i
+= (mask
== 0x80), button_bit
<<= 1) {
555 /* 0 when button down */
556 if (!(buffer
[i
] & mask
))
557 buttons
|= button_bit
;
559 buttons
&= ~button_bit
;
560 mask
= ((mask
>> 4) & 0xf)
561 | ((mask
& 0xf) << 4);
566 dx
= ((int)(buffer
[1] & 0x3f)) - ((buffer
[1] & 0x40) ? 64 : 0);
567 dy
= ((int)(buffer
[0] & 0x3f)) - ((buffer
[0] & 0x40) ? 64 : 0);
569 if (sc
->sc_class
== MSCLASS_TRACKPAD
) {
571 if (sc
->sc_tapping
== 1) {
573 /* finger is down - collect motion data */
577 DPRINTF("buttons: %02x\n", buttons
);
578 switch (sc
->sc_buttons
) {
580 buttons
|= ((buttons
& 2) >> 1);
581 adbms_mangle_2(sc
, buttons
);
584 adbms_mangle_4(sc
, buttons
);
588 /* filter the pseudo-buttons out */
592 if (sc
->sc_wsmousedev
)
593 wsmouse_input(sc
->sc_wsmousedev
, sc
->sc_mb
| buttons
,
595 WSMOUSE_INPUT_DELTA
);
597 aed_input(&new_event
);
602 adbms_mangle_2(struct adbms_softc
*sc
, int buttons
)
606 /* finger down on pad */
607 if (sc
->sc_down
== 0) {
616 if (((sc
->sc_x
* sc
->sc_x
+
617 sc
->sc_y
* sc
->sc_y
) < 20) &&
618 (sc
->sc_wsmousedev
)) {
620 * if there wasn't much movement between
621 * finger down and up again we assume
622 * someone tapped the pad and we just
623 * send a mouse button event
625 wsmouse_input(sc
->sc_wsmousedev
,
626 1, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA
);
634 adbms_mangle_4(struct adbms_softc
*sc
, int buttons
)
637 if (buttons
& 0x20) {
638 /* finger down on pad */
639 if (sc
->sc_down
== 0) {
645 if ((buttons
& 0x20) == 0) {
648 if (((sc
->sc_x
* sc
->sc_x
+
649 sc
->sc_y
* sc
->sc_y
) < 20) &&
650 (sc
->sc_wsmousedev
)) {
652 * if there wasn't much movement between
653 * finger down and up again we assume
654 * someone tapped the pad and we just
655 * send a mouse button event
657 wsmouse_input(sc
->sc_wsmousedev
,
658 1, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA
);
666 adbms_enable(void *v
)
672 adbms_ioctl(void *v
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
676 case WSMOUSEIO_GTYPE
:
677 *(u_int
*)data
= WSMOUSE_TYPE_ADB
;
681 return (EPASSTHROUGH
);
687 adbms_disable(void *v
)
692 init_trackpad(struct adbms_softc
*sc
)
694 struct sysctlnode
*me
= NULL
, *node
= NULL
;
697 uint8_t b2
[] = {0x99, 0x94, 0x19, 0xff, 0xb2, 0x8a, 0x1b, 0x50};
699 addr
= sc
->sc_adbdev
->current_addr
;
700 cmd
= ADBTALK(addr
, 1);
701 if (!adbms_send_sync(sc
, cmd
, 0, NULL
))
704 if (sc
->sc_msg_len
!= 8)
707 memcpy(buffer
, sc
->sc_buffer
, 8);
709 /* now whack the pad */
710 cmd
= ADBLISTEN(addr
, 1);
712 adbms_send_sync(sc
, cmd
, 8, buffer
);
715 cmd
= ADBLISTEN(addr
, 2);
716 adbms_send_sync(sc
, cmd
, 8, b2
);
719 cmd
= ADBLISTEN(addr
, 1);
721 adbms_send_sync(sc
, cmd
, 8, buffer
);
723 cmd
= ADBFLUSH(addr
);
724 adbms_send_sync(sc
, cmd
, 0, NULL
);
728 * setup a sysctl node to control wether tapping the pad should
729 * trigger mouse button events
734 ret
= sysctl_createv(NULL
, 0, NULL
, (const struct sysctlnode
**)&me
,
736 CTLTYPE_NODE
, device_xname(sc
->sc_dev
), NULL
,
738 CTL_MACHDEP
, CTL_CREATE
, CTL_EOL
);
740 ret
= sysctl_createv(NULL
, 0, NULL
, (const struct sysctlnode
**)&node
,
741 CTLFLAG_READWRITE
| CTLFLAG_OWNDESC
| CTLFLAG_IMMEDIATE
,
742 CTLTYPE_INT
, "tapping", "tapping the pad causes button events",
743 sysctl_adbms_tap
, 1, NULL
, 0,
744 CTL_MACHDEP
, me
->sysctl_num
, CTL_CREATE
, CTL_EOL
);
746 node
->sysctl_data
= sc
;
751 adbms_wait(struct adbms_softc
*sc
, int timeout
)
756 while (sc
->sc_msg_len
== -1) {
757 sc
->sc_ops
->poll(sc
->sc_ops
->cookie
);
760 while ((sc
->sc_msg_len
== -1) && (cnt
< timeout
)) {
761 tsleep(&sc
->sc_event
, 0, "adbkbdio", hz
);
765 return (sc
->sc_msg_len
> 0);
769 adbms_send_sync(struct adbms_softc
*sc
, uint8_t cmd
, int len
, uint8_t *msg
)
774 DPRINTF("send: %02x", cmd
);
775 for (i
= 0; i
< len
; i
++)
776 DPRINTF(" %02x", msg
[i
]);
778 sc
->sc_ops
->send(sc
->sc_ops
->cookie
, sc
->sc_poll
, cmd
, len
, msg
);
779 adbms_wait(sc
, 1000);
780 return (sc
->sc_msg_len
!= -1);
784 sysctl_adbms_tap(SYSCTLFN_ARGS
)
786 struct sysctlnode node
= *rnode
;
787 struct adbms_softc
*sc
= node
.sysctl_data
;
789 node
.sysctl_idata
= sc
->sc_tapping
;
793 /* we're asked to write */
794 node
.sysctl_data
= &sc
->sc_tapping
;
795 if (sysctl_lookup(SYSCTLFN_CALL(&node
)) == 0) {
797 sc
->sc_tapping
= (node
.sysctl_idata
== 0) ? 0 : 1;
803 node
.sysctl_size
= 4;
804 return (sysctl_lookup(SYSCTLFN_CALL(&node
)));
810 SYSCTL_SETUP(sysctl_ams_setup
, "sysctl ams subtree setup")
813 sysctl_createv(NULL
, 0, NULL
, NULL
,
815 CTLTYPE_NODE
, "machdep", NULL
,
817 CTL_MACHDEP
, CTL_EOL
);