2 * Copyright (C) 2008 Nathan Whitehorn
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/module.h>
34 #include <sys/mouse.h>
36 #include <sys/condvar.h>
37 #include <sys/selinfo.h>
38 #include <sys/sysctl.h>
40 #include <sys/fcntl.h>
41 #include <sys/kernel.h>
43 #include <machine/bus.h>
50 #define CDEV_GET_SOFTC(x) (x)->si_drv1
52 static int adb_mouse_probe(device_t dev);
53 static int adb_mouse_attach(device_t dev);
54 static int adb_mouse_detach(device_t dev);
55 static void adb_init_trackpad(device_t dev);
56 static int adb_tapping_sysctl(SYSCTL_HANDLER_ARGS);
58 static d_open_t ams_open;
59 static d_close_t ams_close;
60 static d_read_t ams_read;
61 static d_ioctl_t ams_ioctl;
62 static d_poll_t ams_poll;
64 static u_int adb_mouse_receive_packet(device_t dev, u_char status,
65 u_char command, u_char reg, int len, u_char *data);
67 struct adb_mouse_softc {
74 #define AMS_EXTENDED 0x1
75 #define AMS_TOUCHPAD 0x2
89 size_t packet_read_len;
95 static device_method_t adb_mouse_methods[] = {
96 /* Device interface */
97 DEVMETHOD(device_probe, adb_mouse_probe),
98 DEVMETHOD(device_attach, adb_mouse_attach),
99 DEVMETHOD(device_detach, adb_mouse_detach),
100 DEVMETHOD(device_shutdown, bus_generic_shutdown),
101 DEVMETHOD(device_suspend, bus_generic_suspend),
102 DEVMETHOD(device_resume, bus_generic_resume),
105 DEVMETHOD(adb_receive_packet, adb_mouse_receive_packet),
110 static driver_t adb_mouse_driver = {
113 sizeof(struct adb_mouse_softc),
116 static devclass_t adb_mouse_devclass;
118 DRIVER_MODULE(ams, adb, adb_mouse_driver, adb_mouse_devclass, 0, 0);
120 static struct cdevsw ams_cdevsw = {
121 .d_version = D_VERSION,
124 .d_close = ams_close,
126 .d_ioctl = ams_ioctl,
132 adb_mouse_probe(device_t dev)
136 type = adb_get_device_type(dev);
138 if (type != ADB_DEVICE_MOUSE)
141 device_set_desc(dev,"ADB Mouse");
146 adb_mouse_attach(device_t dev)
148 struct adb_mouse_softc *sc;
149 char *description = "Unknown Pointing Device";
154 sc = device_get_softc(dev);
157 mtx_init(&sc->sc_mtx,"ams",MTX_DEF,0);
158 cv_init(&sc->sc_cv,"ams");
163 sc->hw.iftype = MOUSE_IF_UNKNOWN;
164 sc->hw.type = MOUSE_UNKNOWN;
165 sc->hw.model = sc->hw.hwid = 0;
167 sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
169 sc->mode.resolution = 100;
170 sc->mode.accelfactor = 0;
172 sc->mode.packetsize = 5;
177 sc->last_buttons = 0;
178 sc->packet_read_len = 0;
180 /* Try to switch to extended protocol */
181 adb_set_device_handler(dev,4);
183 switch(adb_get_device_handler(dev)) {
185 sc->mode.resolution = 100;
188 sc->mode.resolution = 200;
191 r1_len = adb_read_register(dev,1,r1);
195 sc->flags |= AMS_EXTENDED;
196 memcpy(&sc->hw.hwid,r1,4);
197 sc->mode.resolution = (r1[4] << 8) | r1[5];
201 sc->hw.type = MOUSE_PAD;
202 description = "Tablet";
205 sc->hw.type = MOUSE_MOUSE;
206 description = "Mouse";
209 sc->hw.type = MOUSE_TRACKBALL;
210 description = "Trackball";
213 sc->flags |= AMS_TOUCHPAD;
214 sc->hw.type = MOUSE_PAD;
215 adb_init_trackpad(dev);
216 description = "Touchpad";
220 sc->hw.buttons = r1[7];
222 device_printf(dev,"%d-button %d-dpi %s\n",
223 sc->hw.buttons, sc->mode.resolution,description);
226 * Check for one of MacAlly's non-compliant 2-button mice.
227 * These claim to speak the extended mouse protocol, but
228 * instead speak the standard protocol and only when their
229 * handler is set to 0x42.
232 if (sc->hw.hwid == 0x4b4f4954) {
233 adb_set_device_handler(dev,0x42);
235 if (adb_get_device_handler(dev) == 0x42) {
236 device_printf(dev, "MacAlly 2-Button Mouse\n");
237 sc->flags &= ~AMS_EXTENDED;
244 sc->cdev = make_dev(&ams_cdevsw, device_get_unit(dev),
245 UID_ROOT, GID_OPERATOR, 0644, "ams%d",
246 device_get_unit(dev));
247 sc->cdev->si_drv1 = sc;
249 adb_set_autopoll(dev,1);
255 adb_mouse_detach(device_t dev)
257 struct adb_mouse_softc *sc;
259 adb_set_autopoll(dev,0);
261 sc = device_get_softc(dev);
262 destroy_dev(sc->cdev);
264 mtx_destroy(&sc->sc_mtx);
265 cv_destroy(&sc->sc_cv);
271 adb_init_trackpad(device_t dev)
273 struct adb_mouse_softc *sc;
274 struct sysctl_ctx_list *ctx;
275 struct sysctl_oid *tree;
281 sc = device_get_softc(dev);
283 r1_len = adb_read_register(dev, 1, r1);
285 /* An Extended Mouse register1 must return 8 bytes. */
293 adb_write_register(dev, 1, 8, r1);
295 r1_len = adb_read_register(dev, 1, r1);
299 device_printf(dev, "ADB Mouse = 0x%x "
300 "(non-Extended Mode)\n", r1[6]);
303 device_printf(dev, "ADB Mouse = 0x%x "
304 "(Extended Mode)\n", r1[6]);
306 /* Set ADB Extended Features to default values,
308 r2[0] = 0x19; /* Clicking: 0x19 disabled 0x99 enabled */
309 r2[1] = 0x94; /* Dragging: 0x14 disabled 0x94 enabled */
311 r2[3] = 0xff; /* DragLock: 0xff disabled 0xb2 enabled */
316 r2[7] = 0x57; /* 0x57 bits 3:0 for W mode */
318 adb_write_register(dev, 2, 8, r2);
326 ctx = device_get_sysctl_ctx(dev);
327 tree = device_get_sysctl_tree(dev);
328 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tapping",
329 CTLTYPE_INT | CTLFLAG_RW, sc, 0, adb_tapping_sysctl,
330 "I", "Tapping the pad causes button events");
335 adb_mouse_receive_packet(device_t dev, u_char status, u_char command,
336 u_char reg, int len, u_char *data)
338 struct adb_mouse_softc *sc;
341 int buttons, tmp_buttons;
343 sc = device_get_softc(dev);
345 if (command != ADB_COMMAND_TALK || reg != 0 || len < 2)
348 ydelta = data[0] & 0x7f;
349 xdelta = data[1] & 0x7f;
352 buttons |= !(data[0] & 0x80);
353 buttons |= !(data[1] & 0x80) << 1;
355 if (sc->flags & AMS_EXTENDED) {
356 for (i = 2; i < len && i < 5; i++) {
357 xdelta |= (data[i] & 0x07) << (3*i + 1);
358 ydelta |= (data[i] & 0x70) << (3*i - 3);
360 buttons |= !(data[i] & 0x08) << (2*i - 2);
361 buttons |= !(data[i] & 0x80) << (2*i - 1);
364 len = 2; /* Ignore extra data */
367 /* Do sign extension as necessary */
368 if (xdelta & (0x40 << 3*(len-2)))
369 xdelta |= 0xffffffc0 << 3*(len - 2);
370 if (ydelta & (0x40 << 3*(len-2)))
371 ydelta |= 0xffffffc0 << 3*(len - 2);
373 if ((sc->flags & AMS_TOUCHPAD) && (sc->sc_tapping == 1)) {
374 tmp_buttons = buttons;
375 if (buttons == 0x12) {
376 /* Map a double tap on button 3.
377 Keep the button state for the next sequence.
378 A double tap sequence is followed by a single tap
382 sc->button_buf = tmp_buttons;
383 } else if (buttons == 0x2) {
384 /* Map a single tap on button 2. But only if it is
385 not a successor from a double tap.
387 if (sc->button_buf != 0x3)
394 buttons = tmp_buttons;
398 * Some mice report high-numbered buttons on the wrong button number,
399 * so set the highest-numbered real button as pressed if there are
400 * mysterious high-numbered ones set.
402 * Don't do this for touchpads, because touchpads also trigger
403 * high button events when they are touched.
406 if (buttons & ~((1 << sc->hw.buttons) - 1)
407 && !(sc->flags & AMS_TOUCHPAD)) {
408 buttons |= 1 << (sc->hw.buttons - 1);
410 buttons &= (1 << sc->hw.buttons) - 1;
412 mtx_lock(&sc->sc_mtx);
414 /* Add in our new deltas, and take into account
415 Apple's opposite meaning for Y axis motion */
417 sc->xdelta += xdelta;
418 sc->ydelta -= ydelta;
420 sc->buttons = buttons;
422 mtx_unlock(&sc->sc_mtx);
424 cv_broadcast(&sc->sc_cv);
425 selwakeuppri(&sc->rsel, PZERO);
431 ams_open(struct cdev *dev, int flag, int fmt, struct thread *p)
433 struct adb_mouse_softc *sc;
435 sc = CDEV_GET_SOFTC(dev);
439 mtx_lock(&sc->sc_mtx);
440 sc->packet_read_len = 0;
444 mtx_unlock(&sc->sc_mtx);
450 ams_close(struct cdev *dev, int flag, int fmt, struct thread *p)
452 struct adb_mouse_softc *sc;
454 sc = CDEV_GET_SOFTC(dev);
456 cv_broadcast(&sc->sc_cv);
457 selwakeuppri(&sc->rsel, PZERO);
462 ams_poll(struct cdev *dev, int events, struct thread *p)
464 struct adb_mouse_softc *sc;
466 sc = CDEV_GET_SOFTC(dev);
470 if (events & (POLLIN | POLLRDNORM)) {
471 mtx_lock(&sc->sc_mtx);
473 if (sc->xdelta == 0 && sc->ydelta == 0 &&
474 sc->buttons == sc->last_buttons &&
475 sc->packet_read_len == 0) {
476 selrecord(p, &sc->rsel);
479 events &= (POLLIN | POLLRDNORM);
482 mtx_unlock(&sc->sc_mtx);
489 ams_read(struct cdev *dev, struct uio *uio, int flag)
491 struct adb_mouse_softc *sc;
496 sc = CDEV_GET_SOFTC(dev);
500 if (uio->uio_resid <= 0)
503 mtx_lock(&sc->sc_mtx);
505 if (!sc->packet_read_len) {
506 if (sc->xdelta == 0 && sc->ydelta == 0 &&
507 sc->buttons == sc->last_buttons) {
509 if (flag & O_NONBLOCK) {
510 mtx_unlock(&sc->sc_mtx);
515 /* Otherwise, block on new data */
516 error = cv_wait_sig(&sc->sc_cv, &sc->sc_mtx);
518 mtx_unlock(&sc->sc_mtx);
523 sc->packet[0] = 1 << 7;
524 sc->packet[0] |= (!(sc->buttons & 1)) << 2;
525 sc->packet[0] |= (!(sc->buttons & 4)) << 1;
526 sc->packet[0] |= (!(sc->buttons & 2));
528 if (sc->xdelta > 127) {
530 sc->packet[3] = sc->xdelta - 127;
531 } else if (sc->xdelta < -127) {
532 sc->packet[1] = -127;
533 sc->packet[3] = sc->xdelta + 127;
535 sc->packet[1] = sc->xdelta;
539 if (sc->ydelta > 127) {
541 sc->packet[4] = sc->ydelta - 127;
542 } else if (sc->ydelta < -127) {
543 sc->packet[2] = -127;
544 sc->packet[4] = sc->ydelta + 127;
546 sc->packet[2] = sc->ydelta;
554 sc->packet[7] = ~((uint8_t)(sc->buttons >> 3)) & 0x7f;
557 sc->last_buttons = sc->buttons;
561 sc->packet_read_len = sc->mode.packetsize;
564 len = (sc->packet_read_len > uio->uio_resid) ?
565 uio->uio_resid : sc->packet_read_len;
567 memcpy(outpacket,sc->packet +
568 (sc->mode.packetsize - sc->packet_read_len),len);
569 sc->packet_read_len -= len;
571 mtx_unlock(&sc->sc_mtx);
573 error = uiomove(outpacket,len,uio);
580 ams_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
583 struct adb_mouse_softc *sc;
586 sc = CDEV_GET_SOFTC(dev);
591 case MOUSE_GETHWINFO:
592 *(mousehw_t *)addr = sc->hw;
595 *(mousemode_t *)addr = sc->mode;
598 mode = *(mousemode_t *)addr;
599 addr = (caddr_t)&mode.level;
604 if (*(int *)addr == -1)
606 else if (*(int *)addr == 1) {
608 sc->mode.packetsize = 8;
610 } else if (*(int *)addr == 0) {
612 sc->mode.packetsize = 5;
618 *(int *)addr = sc->mode.level;
621 case MOUSE_GETSTATUS: {
622 mousestatus_t *status = (mousestatus_t *) addr;
624 mtx_lock(&sc->sc_mtx);
626 status->button = sc->buttons;
627 status->obutton = sc->last_buttons;
629 status->flags = status->button ^ status->obutton;
631 if (sc->xdelta != 0 || sc->ydelta)
632 status->flags |= MOUSE_POSCHANGED;
633 if (status->button != status->obutton)
634 status->flags |= MOUSE_BUTTONSCHANGED;
636 status->dx = sc->xdelta;
637 status->dy = sc->ydelta;
642 sc->last_buttons = sc->buttons;
644 mtx_unlock(&sc->sc_mtx);
655 adb_tapping_sysctl(SYSCTL_HANDLER_ARGS)
657 struct adb_mouse_softc *sc = arg1;
664 tapping = sc->sc_tapping;
666 error = sysctl_handle_int(oidp, &tapping, 0, req);
668 if (error || !req->newptr)
672 adb_read_register(dev, 2, r2);
673 r2[0] = 0x99; /* enable tapping. */
674 adb_write_register(dev, 2, 8, r2);
676 } else if (tapping == 0) {
677 adb_read_register(dev, 2, r2);
678 r2[0] = 0x19; /* disable tapping. */
679 adb_write_register(dev, 2, 8, r2);