summaryrefslogtreecommitdiffstats
path: root/sys/dev/adb/adb_mouse.c
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2008-10-26 19:37:38 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2008-10-26 19:37:38 +0000
commit00912a1e641a0914eadb327588513cb1f9f31e52 (patch)
tree9b34ca3de2b230caffc837de31424d61363e74af /sys/dev/adb/adb_mouse.c
parent1a0ebdcac70cbfc992fdacea6e46c5f77b60378e (diff)
downloadFreeBSD-src-00912a1e641a0914eadb327588513cb1f9f31e52.zip
FreeBSD-src-00912a1e641a0914eadb327588513cb1f9f31e52.tar.gz
Add ADB support. This provides support for the external ADB bus on the PowerMac
G3 as well as the internal ADB keyboard and mice in PowerBooks and iBooks. This also brings in Mac GPIO support, for which we should eventually have a better interface. Obtained from: NetBSD (CUDA and PMU drivers)
Diffstat (limited to 'sys/dev/adb/adb_mouse.c')
-rw-r--r--sys/dev/adb/adb_mouse.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/sys/dev/adb/adb_mouse.c b/sys/dev/adb/adb_mouse.c
new file mode 100644
index 0000000..8b9c7d1
--- /dev/null
+++ b/sys/dev/adb/adb_mouse.c
@@ -0,0 +1,539 @@
+/*-
+ * Copyright (C) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/mouse.h>
+#include <sys/poll.h>
+#include <sys/condvar.h>
+#include <sys/selinfo.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include "adb.h"
+
+#define CDEV_GET_SOFTC(x) devclass_get_softc(adb_mouse_devclass, minor(x) & 0x1f)
+
+static int adb_mouse_probe(device_t dev);
+static int adb_mouse_attach(device_t dev);
+static int adb_mouse_detach(device_t dev);
+
+static d_open_t ams_open;
+static d_close_t ams_close;
+static d_read_t ams_read;
+static d_ioctl_t ams_ioctl;
+static d_poll_t ams_poll;
+
+static u_int adb_mouse_receive_packet(device_t dev, u_char status,
+ u_char command, u_char reg, int len, u_char *data);
+
+struct adb_mouse_softc {
+ device_t sc_dev;
+
+ struct mtx sc_mtx;
+ struct cv sc_cv;
+
+ int extended;
+ uint16_t dpi;
+
+ mousehw_t hw;
+ mousemode_t mode;
+ u_char id[4];
+
+ int buttons;
+ int last_buttons;
+ int xdelta, ydelta;
+
+ int8_t packet[8];
+ size_t packet_read_len;
+
+ struct cdev *cdev;
+ struct selinfo rsel;
+};
+
+static device_method_t adb_mouse_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, adb_mouse_probe),
+ DEVMETHOD(device_attach, adb_mouse_attach),
+ DEVMETHOD(device_detach, adb_mouse_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* ADB interface */
+ DEVMETHOD(adb_receive_packet, adb_mouse_receive_packet),
+
+ { 0, 0 }
+};
+
+static driver_t adb_mouse_driver = {
+ "ams",
+ adb_mouse_methods,
+ sizeof(struct adb_mouse_softc),
+};
+
+static devclass_t adb_mouse_devclass;
+
+DRIVER_MODULE(ams, adb, adb_mouse_driver, adb_mouse_devclass, 0, 0);
+
+static struct cdevsw ams_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = 0,
+ .d_open = ams_open,
+ .d_close = ams_close,
+ .d_read = ams_read,
+ .d_ioctl = ams_ioctl,
+ .d_poll = ams_poll,
+ .d_name = "ams",
+};
+
+static int
+adb_mouse_probe(device_t dev)
+{
+ uint8_t type;
+
+ type = adb_get_device_type(dev);
+
+ if (type != ADB_DEVICE_MOUSE)
+ return (ENXIO);
+
+ device_set_desc(dev,"ADB Mouse");
+ return (0);
+}
+
+static int
+adb_mouse_attach(device_t dev)
+{
+ struct adb_mouse_softc *sc;
+ char *description = "Unknown Pointing Device";
+
+ size_t r1_len;
+ u_char r1[8];
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ mtx_init(&sc->sc_mtx,"ams",MTX_DEF,0);
+ cv_init(&sc->sc_cv,"ams");
+
+ sc->extended = 0;
+
+ sc->hw.buttons = 2;
+ sc->hw.iftype = MOUSE_IF_UNKNOWN;
+ sc->hw.type = MOUSE_UNKNOWN;
+ sc->hw.model = sc->hw.hwid = 0;
+
+ sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->mode.rate = -1;
+ sc->mode.resolution = 100;
+ sc->mode.accelfactor = 0;
+ sc->mode.level = 0;
+ sc->mode.packetsize = 5;
+
+ sc->buttons = 0;
+ sc->last_buttons = 0;
+ sc->packet_read_len = 0;
+
+ /* Try to switch to extended protocol */
+ adb_set_device_handler(dev,4);
+
+ switch(adb_get_device_handler(dev)) {
+ case 1:
+ sc->mode.resolution = 100;
+ break;
+ case 2:
+ sc->mode.resolution = 200;
+ break;
+ case 4:
+ adb_read_register(dev,1,&r1_len,r1);
+ if (r1_len < 8)
+ break;
+
+ sc->extended = 1;
+ memcpy(&sc->hw.hwid,r1,4);
+ sc->mode.resolution = (r1[4] << 8) | r1[5];
+
+ switch (r1[6]) {
+ case 0:
+ sc->hw.type = MOUSE_PAD;
+ description = "Tablet";
+ break;
+ case 1:
+ sc->hw.type = MOUSE_MOUSE;
+ description = "Mouse";
+ break;
+ case 2:
+ sc->hw.type = MOUSE_TRACKBALL;
+ description = "Trackball";
+ break;
+ }
+
+ sc->hw.buttons = r1[7];
+
+ device_printf(dev,"%d-button %d-dpi %s\n",
+ sc->hw.buttons, sc->mode.resolution,description);
+
+ /*
+ * Check for one of MacAlly's non-compliant 2-button mice.
+ * These claim to speak the extended mouse protocol, but
+ * instead speak the standard protocol and only when their
+ * handler is set to 0x42.
+ */
+
+ if (sc->hw.hwid == 0x4b4f4954) {
+ adb_set_device_handler(dev,0x42);
+
+ if (adb_get_device_handler(dev) == 0x42) {
+ device_printf(dev, "MacAlly 2-Button Mouse\n");
+ sc->extended = 0;
+ }
+ }
+
+ break;
+ }
+
+ sc->cdev = make_dev(&ams_cdevsw, device_get_unit(dev),
+ UID_ROOT, GID_OPERATOR, 0644, "ams%d",
+ device_get_unit(dev));
+
+ adb_set_autopoll(dev,1);
+
+ return (0);
+}
+
+static int
+adb_mouse_detach(device_t dev)
+{
+ struct adb_mouse_softc *sc;
+
+ adb_set_autopoll(dev,0);
+
+ sc = device_get_softc(dev);
+ destroy_dev(sc->cdev);
+
+ mtx_destroy(&sc->sc_mtx);
+ cv_destroy(&sc->sc_cv);
+
+ return (0);
+}
+
+static u_int
+adb_mouse_receive_packet(device_t dev, u_char status, u_char command,
+ u_char reg, int len, u_char *data)
+{
+ struct adb_mouse_softc *sc;
+ int i = 0;
+ int xdelta, ydelta;
+ int buttons;
+
+ sc = device_get_softc(dev);
+
+ if (command != ADB_COMMAND_TALK || reg != 0 || len < 2)
+ return (0);
+
+ ydelta = data[0] & 0x7f;
+ xdelta = data[1] & 0x7f;
+
+ buttons = 0;
+ buttons |= !(data[0] & 0x80);
+ buttons |= !(data[1] & 0x80) << 1;
+
+ if (sc->extended) {
+ for (i = 2; i < len && i < 5; i++) {
+ xdelta |= (data[i] & 0x07) << (3*i + 1);
+ ydelta |= (data[i] & 0x70) << (3*i - 3);
+
+ buttons |= !(data[i] & 0x08) << (2*i - 2);
+ buttons |= !(data[i] & 0x80) << (2*i - 1);
+ }
+ } else {
+ len = 2; /* Ignore extra data */
+ }
+
+ /* Do sign extension as necessary */
+ if (xdelta & (0x40 << 3*(len-2)))
+ xdelta |= 0xffffffc0 << 3*(len - 2);
+ if (ydelta & (0x40 << 3*(len-2)))
+ ydelta |= 0xffffffc0 << 3*(len - 2);
+
+ /*
+ * Some mice report high-numbered buttons on the wrong button number,
+ * so set the highest-numbered real button as pressed if there are
+ * mysterious high-numbered ones set.
+ */
+
+ if (buttons & ~((1 << sc->hw.buttons) - 1)) {
+ buttons |= 1 << (sc->hw.buttons - 1);
+ buttons &= (1 << sc->hw.buttons) - 1;
+ }
+
+ mtx_lock(&sc->sc_mtx);
+
+ /* Add in our new deltas, and take into account
+ Apple's opposite meaning for Y axis motion */
+
+ sc->xdelta += xdelta;
+ sc->ydelta -= ydelta;
+
+ sc->buttons = buttons;
+
+ mtx_unlock(&sc->sc_mtx);
+
+ cv_broadcast(&sc->sc_cv);
+ selwakeuppri(&sc->rsel, PZERO);
+
+ return (0);
+}
+
+static int
+ams_open(struct cdev *dev, int flag, int fmt, struct thread *p)
+{
+ struct adb_mouse_softc *sc;
+
+ sc = CDEV_GET_SOFTC(dev);
+ if (sc == NULL)
+ return (ENXIO);
+
+ mtx_lock(&sc->sc_mtx);
+ sc->packet_read_len = 0;
+ sc->xdelta = 0;
+ sc->ydelta = 0;
+ sc->buttons = 0;
+ mtx_unlock(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+ams_close(struct cdev *dev, int flag, int fmt, struct thread *p)
+{
+ struct adb_mouse_softc *sc;
+
+ sc = CDEV_GET_SOFTC(dev);
+
+ cv_broadcast(&sc->sc_cv);
+ selwakeuppri(&sc->rsel, PZERO);
+ return (0);
+}
+
+static int
+ams_poll(struct cdev *dev, int events, struct thread *p)
+{
+ struct adb_mouse_softc *sc;
+
+ sc = CDEV_GET_SOFTC(dev);
+ if (sc == NULL)
+ return (EIO);
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ mtx_lock(&sc->sc_mtx);
+
+ if (sc->xdelta == 0 && sc->ydelta == 0 &&
+ sc->buttons == sc->last_buttons) {
+ selrecord(p, &sc->rsel);
+ events = 0;
+ } else {
+ events &= (POLLIN | POLLRDNORM);
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+ }
+
+ return events;
+}
+
+static int
+ams_read(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct adb_mouse_softc *sc;
+ size_t len;
+ int8_t outpacket[8];
+
+ sc = CDEV_GET_SOFTC(dev);
+ if (sc == NULL)
+ return (EIO);
+
+ if (uio->uio_resid <= 0)
+ return (0);
+
+ mtx_lock(&sc->sc_mtx);
+
+ if (!sc->packet_read_len) {
+ if (sc->xdelta == 0 && sc->ydelta == 0 &&
+ sc->buttons == sc->last_buttons) {
+
+ if (flag & O_NONBLOCK) {
+ mtx_unlock(&sc->sc_mtx);
+ return EWOULDBLOCK;
+ }
+
+
+ /* Otherwise, block on new data */
+ cv_wait(&sc->sc_cv,&sc->sc_mtx);
+ }
+
+ sc->packet[0] = 1 << 7;
+ sc->packet[0] |= (!(sc->buttons & 1)) << 2;
+ sc->packet[0] |= (!(sc->buttons & 4)) << 1;
+ sc->packet[0] |= (!(sc->buttons & 2));
+
+ if (sc->xdelta > 127) {
+ sc->packet[1] = 127;
+ sc->packet[3] = sc->xdelta - 127;
+ } else if (sc->xdelta < -127) {
+ sc->packet[1] = -127;
+ sc->packet[3] = sc->xdelta + 127;
+ } else {
+ sc->packet[1] = sc->xdelta;
+ sc->packet[3] = 0;
+ }
+
+ if (sc->ydelta > 127) {
+ sc->packet[2] = 127;
+ sc->packet[4] = sc->ydelta - 127;
+ } else if (sc->ydelta < -127) {
+ sc->packet[2] = -127;
+ sc->packet[4] = sc->ydelta + 127;
+ } else {
+ sc->packet[2] = sc->ydelta;
+ sc->packet[4] = 0;
+ }
+
+ /* No Z movement */
+ sc->packet[5] = 0;
+ sc->packet[6] = 0;
+
+ sc->packet[7] = ~((uint8_t)(sc->buttons >> 3)) & 0x7f;
+
+
+ sc->last_buttons = sc->buttons;
+ sc->xdelta = 0;
+ sc->ydelta = 0;
+
+ sc->packet_read_len = sc->mode.packetsize;
+ }
+
+ len = (sc->packet_read_len > uio->uio_resid) ?
+ uio->uio_resid : sc->packet_read_len;
+
+ memcpy(outpacket,sc->packet +
+ (sc->mode.packetsize - sc->packet_read_len),len);
+ sc->packet_read_len -= len;
+
+ mtx_unlock(&sc->sc_mtx);
+
+ uiomove(outpacket,len,uio);
+
+ return (0);
+}
+
+
+static int
+ams_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
+ struct thread *p)
+{
+ struct adb_mouse_softc *sc;
+ mousemode_t mode;
+
+ sc = CDEV_GET_SOFTC(dev);
+ if (sc == NULL)
+ return (EIO);
+
+ switch (cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->hw;
+ break;
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->mode;
+ break;
+ case MOUSE_SETMODE:
+ mode = *(mousemode_t *)addr;
+ addr = (caddr_t)&mode.level;
+
+ /* Fallthrough */
+
+ case MOUSE_SETLEVEL:
+ if (*(int *)addr == -1)
+ break;
+ else if (*(int *)addr == 1) {
+ sc->mode.level = 1;
+ sc->mode.packetsize = 8;
+ break;
+ } else if (*(int *)addr == 0) {
+ sc->mode.level = 0;
+ sc->mode.packetsize = 5;
+ break;
+ }
+
+ return EINVAL;
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->mode.level;
+ break;
+
+ case MOUSE_GETSTATUS: {
+ mousestatus_t *status = (mousestatus_t *) addr;
+
+ mtx_lock(&sc->sc_mtx);
+
+ status->button = sc->buttons;
+ status->obutton = sc->last_buttons;
+
+ status->flags = status->button ^ status->obutton;
+
+ if (sc->xdelta != 0 || sc->ydelta)
+ status->flags |= MOUSE_POSCHANGED;
+ if (status->button != status->obutton)
+ status->flags |= MOUSE_BUTTONSCHANGED;
+
+ status->dx = sc->xdelta;
+ status->dy = sc->ydelta;
+ status->dz = 0;
+
+ sc->xdelta = 0;
+ sc->ydelta = 0;
+ sc->last_buttons = sc->buttons;
+
+ mtx_unlock(&sc->sc_mtx);
+
+ break; }
+ default:
+ return ENOTTY;
+ }
+
+ return (0);
+}
+
OpenPOWER on IntegriCloud