summaryrefslogtreecommitdiffstats
path: root/sys/dev/adb
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
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')
-rw-r--r--sys/dev/adb/adb.h78
-rw-r--r--sys/dev/adb/adb_bus.c384
-rw-r--r--sys/dev/adb/adb_hb_if.m58
-rw-r--r--sys/dev/adb/adb_if.m49
-rw-r--r--sys/dev/adb/adb_kbd.c692
-rw-r--r--sys/dev/adb/adb_mouse.c539
-rw-r--r--sys/dev/adb/adbvar.h56
7 files changed, 1856 insertions, 0 deletions
diff --git a/sys/dev/adb/adb.h b/sys/dev/adb/adb.h
new file mode 100644
index 0000000..8d68a87
--- /dev/null
+++ b/sys/dev/adb/adb.h
@@ -0,0 +1,78 @@
+/*-
+ * 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$
+ */
+
+#ifndef _POWERPC_ADB_H_
+#define _POWERPC_ADB_H_
+
+#include "adb_hb_if.h"
+#include "adb_if.h"
+
+enum {
+ ADB_COMMAND_FLUSH = 0,
+ ADB_COMMAND_LISTEN = 2,
+ ADB_COMMAND_TALK = 3,
+};
+
+enum {
+ ADB_DEVICE_DONGLE = 0x01,
+ ADB_DEVICE_KEYBOARD = 0x02,
+ ADB_DEVICE_MOUSE = 0x03,
+ ADB_DEVICE_TABLET = 0x04,
+ ADB_DEVICE_MODEM = 0x05,
+
+ ADB_DEVICE_MISC = 0x07
+};
+
+struct adb_devinfo {
+ uint8_t address;
+ uint8_t default_address;
+ uint8_t handler_id;
+
+ uint16_t register3;
+};
+
+/* Pass packets down through the bus manager */
+u_int adb_send_packet(device_t dev, u_char command, u_char reg, int len,
+ u_char *data);
+u_int adb_set_autopoll(device_t dev, u_char enable);
+
+/* Pass packets up from the interface */
+u_int adb_receive_raw_packet(device_t dev, u_char status, u_char command,
+ int len, u_char *data);
+
+uint8_t adb_get_device_type(device_t dev);
+uint8_t adb_get_device_handler(device_t dev);
+uint8_t adb_set_device_handler(device_t dev, uint8_t newhandler);
+
+uint8_t adb_read_register(device_t dev, u_char reg, size_t *len, void *data);
+
+/* Bits for implementing ADB host bus adapters */
+extern devclass_t adb_devclass;
+extern driver_t adb_driver;
+
+#endif
+
diff --git a/sys/dev/adb/adb_bus.c b/sys/dev/adb/adb_bus.c
new file mode 100644
index 0000000..cc31a9d
--- /dev/null
+++ b/sys/dev/adb/adb_bus.c
@@ -0,0 +1,384 @@
+/*-
+ * 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/kernel.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include "adb.h"
+#include "adbvar.h"
+
+static int adb_bus_probe(device_t dev);
+static int adb_bus_attach(device_t dev);
+static int adb_bus_detach(device_t dev);
+static void adb_probe_nomatch(device_t dev, device_t child);
+static int adb_print_child(device_t dev, device_t child);
+
+static int adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, uint8_t reg, int len, u_char *data);
+
+static char *adb_device_string[] = {
+ "HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc"
+};
+
+static device_method_t adb_bus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, adb_bus_probe),
+ DEVMETHOD(device_attach, adb_bus_attach),
+ DEVMETHOD(device_detach, adb_bus_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus Interface */
+ DEVMETHOD(bus_probe_nomatch, adb_probe_nomatch),
+ DEVMETHOD(bus_print_child, adb_print_child),
+
+ { 0, 0 },
+};
+
+driver_t adb_driver = {
+ "adb",
+ adb_bus_methods,
+ sizeof(struct adb_softc),
+};
+
+devclass_t adb_devclass;
+
+static int
+adb_bus_probe(device_t dev)
+{
+ device_set_desc(dev, "Apple Desktop Bus");
+ return (0);
+}
+
+static int
+adb_bus_attach(device_t dev)
+{
+ struct adb_softc *sc = device_get_softc(dev);
+ uint8_t i, next_free;
+ uint16_t r3;
+
+ sc->sc_dev = dev;
+ sc->parent = device_get_parent(dev);
+
+ sc->packet_reply = 0;
+ sc->autopoll_mask = 0;
+
+ mtx_init(&sc->sc_sync_mtx,"adbsyn",NULL,MTX_DEF | MTX_RECURSE);
+
+ /* Initialize devinfo */
+ for (i = 0; i < 16; i++) {
+ sc->devinfo[i].address = i;
+ sc->devinfo[i].default_address = 0;
+ }
+
+ /* Reset ADB bus */
+ adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL);
+ DELAY(1500);
+
+ /* Enumerate bus */
+ next_free = 8;
+
+ for (i = 1; i < 7; i++) {
+ int8_t first_relocated = -1;
+ int reply = 0;
+
+ do {
+ reply = adb_send_raw_packet_sync(dev,i,
+ ADB_COMMAND_TALK,3,0,NULL);
+
+ if (reply) {
+ /* If we got a response, relocate to next_free */
+ r3 = sc->devinfo[i].register3;
+ r3 &= 0xf000;
+ r3 |= ((uint16_t)(next_free) & 0x000f) << 8;
+ r3 |= 0x00fe;
+
+ adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3,
+ sizeof(uint16_t),(u_char *)(&r3));
+
+ adb_send_raw_packet_sync(dev,next_free,
+ ADB_COMMAND_TALK,3,0,NULL);
+
+ sc->devinfo[next_free].default_address = i;
+ if (first_relocated < 0)
+ first_relocated = next_free;
+
+ next_free++;
+ } else if (first_relocated > 0) {
+ /* Collisions removed, relocate first device back */
+
+ r3 = sc->devinfo[i].register3;
+ r3 &= 0xf000;
+ r3 |= ((uint16_t)(i) & 0x000f) << 8;
+
+ adb_send_raw_packet_sync(dev,first_relocated,
+ ADB_COMMAND_LISTEN,3,
+ sizeof(uint16_t),(u_char *)(&r3));
+ adb_send_raw_packet_sync(dev,i,
+ ADB_COMMAND_TALK,3,0,NULL);
+
+ sc->devinfo[i].default_address = i;
+ sc->devinfo[(int)(first_relocated)].default_address = 0;
+ break;
+ }
+ } while (reply);
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (sc->devinfo[i].default_address) {
+ sc->children[i] = device_add_child(dev, NULL, -1);
+ device_set_ivars(sc->children[i], &sc->devinfo[i]);
+ }
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int adb_bus_detach(device_t dev)
+{
+ struct adb_softc *sc = device_get_softc(dev);
+
+ mtx_destroy(&sc->sc_sync_mtx);
+
+ return (bus_generic_detach(dev));
+}
+
+
+static void
+adb_probe_nomatch(device_t dev, device_t child)
+{
+ struct adb_devinfo *dinfo;
+
+ if (bootverbose) {
+ dinfo = device_get_ivars(child);
+
+ device_printf(dev,"ADB %s at device %d (no driver attached)\n",
+ adb_device_string[dinfo->default_address],dinfo->address);
+ }
+}
+
+u_int
+adb_receive_raw_packet(device_t dev, u_char status, u_char command, int len,
+ u_char *data)
+{
+ struct adb_softc *sc = device_get_softc(dev);
+ u_char addr = command >> 4;
+
+ if (len > 0 && (command & 0x0f) == ((ADB_COMMAND_TALK << 2) | 3)) {
+ memcpy(&sc->devinfo[addr].register3,data,2);
+ sc->devinfo[addr].handler_id = data[1];
+ }
+
+ if (sc->sync_packet == command) {
+ memcpy(sc->syncreg,data,(len > 8) ? 8 : len);
+ atomic_store_rel_int(&sc->packet_reply,len + 1);
+ }
+
+ if (sc->children[addr] != NULL) {
+ ADB_RECEIVE_PACKET(sc->children[addr],status,
+ (command & 0x0f) >> 2,command & 0x03,len,data);
+ }
+
+ return (0);
+}
+
+static int
+adb_print_child(device_t dev, device_t child)
+{
+ struct adb_devinfo *dinfo;
+ int retval = 0;
+
+ dinfo = device_get_ivars(child);
+
+ retval += bus_print_child_header(dev,child);
+ printf(" at device %d",dinfo->address);
+ retval += bus_print_child_footer(dev, child);
+
+ return (retval);
+}
+
+u_int
+adb_send_packet(device_t dev, u_char command, u_char reg, int len, u_char *data)
+{
+ u_char command_byte = 0;
+ struct adb_devinfo *dinfo;
+ struct adb_softc *sc;
+
+ sc = device_get_softc(device_get_parent(dev));
+ dinfo = device_get_ivars(dev);
+
+ command_byte |= dinfo->address << 4;
+ command_byte |= command << 2;
+ command_byte |= reg;
+
+ ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
+
+ return (0);
+}
+
+u_int
+adb_set_autopoll(device_t dev, u_char enable)
+{
+ struct adb_devinfo *dinfo;
+ struct adb_softc *sc;
+ uint16_t mod = 0;
+
+ sc = device_get_softc(device_get_parent(dev));
+ dinfo = device_get_ivars(dev);
+
+ mod = enable << dinfo->address;
+ if (enable) {
+ sc->autopoll_mask |= mod;
+ } else {
+ mod = ~mod;
+ sc->autopoll_mask &= mod;
+ }
+
+ ADB_HB_SET_AUTOPOLL_MASK(sc->parent,sc->autopoll_mask);
+
+ return (0);
+}
+
+uint8_t
+adb_get_device_type(device_t dev)
+{
+ struct adb_devinfo *dinfo;
+
+ dinfo = device_get_ivars(dev);
+ return (dinfo->default_address);
+}
+
+uint8_t
+adb_get_device_handler(device_t dev)
+{
+ struct adb_devinfo *dinfo;
+
+ dinfo = device_get_ivars(dev);
+ return (dinfo->handler_id);
+}
+
+static int
+adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command,
+ uint8_t reg, int len, u_char *data)
+{
+ u_char command_byte = 0;
+ struct adb_softc *sc;
+ int result = -1;
+ int i = 0;
+
+ sc = device_get_softc(dev);
+
+ command_byte |= to << 4;
+ command_byte |= command << 2;
+ command_byte |= reg;
+
+ /* Wait if someone else has a synchronous request pending */
+ mtx_lock(&sc->sc_sync_mtx);
+
+ sc->packet_reply = 0;
+ sc->sync_packet = command_byte;
+
+ ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
+
+ while (!atomic_fetchadd_int(&sc->packet_reply,0)) {
+ /* Sometimes CUDA controllers hang up during cold boots.
+ Try poking them. */
+ if (i > 10)
+ ADB_HB_CONTROLLER_POLL(sc->parent);
+
+ DELAY(100);
+ i++;
+ }
+
+ result = sc->packet_reply - 1;
+
+ /* Clear packet sync */
+ sc->packet_reply = 0;
+ sc->sync_packet = 0xffff; /* We can't match a 16 bit value */
+
+ mtx_unlock(&sc->sc_sync_mtx);
+
+ return (result);
+}
+
+uint8_t
+adb_set_device_handler(device_t dev, uint8_t newhandler)
+{
+ struct adb_softc *sc;
+ struct adb_devinfo *dinfo;
+ uint16_t newr3;
+
+ dinfo = device_get_ivars(dev);
+ sc = device_get_softc(device_get_parent(dev));
+
+ newr3 = dinfo->register3 & 0xff00;
+ newr3 |= (uint16_t)(newhandler);
+
+ adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
+ ADB_COMMAND_LISTEN, 3, sizeof(uint16_t), (u_char *)(&newr3));
+ adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
+ ADB_COMMAND_TALK, 3, 0, NULL);
+
+ return (dinfo->handler_id);
+}
+
+uint8_t
+adb_read_register(device_t dev, u_char reg,
+ size_t *len, void *data)
+{
+ struct adb_softc *sc;
+ struct adb_devinfo *dinfo;
+ size_t orig_len;
+
+ dinfo = device_get_ivars(dev);
+ sc = device_get_softc(device_get_parent(dev));
+
+ orig_len = *len;
+
+ mtx_lock(&sc->sc_sync_mtx);
+
+ *len = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
+ ADB_COMMAND_TALK, reg, 0, NULL);
+
+ if (*len > 0)
+ memcpy(data,sc->syncreg,*len);
+
+ mtx_unlock(&sc->sc_sync_mtx);
+
+ return ((*len > 0) ? 0 : -1);
+}
+
diff --git a/sys/dev/adb/adb_hb_if.m b/sys/dev/adb/adb_hb_if.m
new file mode 100644
index 0000000..e27094d
--- /dev/null
+++ b/sys/dev/adb/adb_hb_if.m
@@ -0,0 +1,58 @@
+#-
+# 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 AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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/bus.h>
+
+#include <dev/adb/adb.h>
+
+INTERFACE adb_hb;
+
+#
+# Bus level operations. These are used by the ADB bus manager, and are
+# required to inplement an ADB interface.
+#
+
+METHOD u_int send_raw_packet {
+ device_t dev;
+ u_char command_byte;
+
+ int len;
+ u_char *data;
+ u_char poll;
+};
+
+METHOD u_int controller_poll {
+ device_t dev;
+};
+
+METHOD u_int set_autopoll_mask {
+ device_t dev;
+
+ uint16_t mask;
+};
+
diff --git a/sys/dev/adb/adb_if.m b/sys/dev/adb/adb_if.m
new file mode 100644
index 0000000..ef21fd8
--- /dev/null
+++ b/sys/dev/adb/adb_if.m
@@ -0,0 +1,49 @@
+#-
+# 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 AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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/bus.h>
+
+#include <dev/adb/adb.h>
+
+INTERFACE adb;
+
+#
+# Driver level operations
+#
+
+METHOD u_int receive_packet {
+ device_t dev;
+
+ u_char status;
+ u_char command;
+ u_char reg;
+
+ int len;
+ u_char *data;
+};
+
diff --git a/sys/dev/adb/adb_kbd.c b/sys/dev/adb/adb_kbd.c
new file mode 100644
index 0000000..db95cfe
--- /dev/null
+++ b/sys/dev/adb/adb_kbd.c
@@ -0,0 +1,692 @@
+/*-
+ * 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/kbio.h>
+#include <sys/condvar.h>
+#include <sys/callout.h>
+#include <sys/kernel.h>
+
+#include <machine/bus.h>
+
+#include "opt_kbd.h"
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/kbdtables.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include "adb.h"
+
+#define KBD_DRIVER_NAME "akbd"
+
+#define AKBD_EMULATE_ATKBD 1
+
+static int adb_kbd_probe(device_t dev);
+static int adb_kbd_attach(device_t dev);
+static int adb_kbd_detach(device_t dev);
+static void akbd_repeat(void *xsc);
+
+static u_int adb_kbd_receive_packet(device_t dev, u_char status,
+ u_char command, u_char reg, int len, u_char *data);
+
+struct adb_kbd_softc {
+ keyboard_t sc_kbd;
+
+ device_t sc_dev;
+ struct mtx sc_mutex;
+ struct cv sc_cv;
+
+ int sc_mode;
+ int sc_state;
+
+ int have_led_control;
+
+ uint8_t buffer[8];
+ volatile int buffers;
+
+ struct callout sc_repeater;
+ int sc_repeatstart;
+ int sc_repeatcontinue;
+ uint8_t last_press;
+};
+
+static device_method_t adb_kbd_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, adb_kbd_probe),
+ DEVMETHOD(device_attach, adb_kbd_attach),
+ DEVMETHOD(device_detach, adb_kbd_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_kbd_receive_packet),
+
+ { 0, 0 }
+};
+
+static driver_t adb_kbd_driver = {
+ "akbd",
+ adb_kbd_methods,
+ sizeof(struct adb_kbd_softc),
+};
+
+static devclass_t adb_kbd_devclass;
+
+DRIVER_MODULE(akbd, adb, adb_kbd_driver, adb_kbd_devclass, 0, 0);
+
+static const uint8_t adb_to_at_scancode_map[128] = { 30, 31, 32, 33, 35, 34,
+ 44, 45, 46, 47, 0, 48, 16, 17, 18, 19, 21, 20, 2, 3, 4, 5, 7, 6, 13,
+ 10, 8, 12, 9, 11, 27, 24, 22, 26, 23, 25, 28, 38, 36, 40, 37, 39, 43,
+ 51, 53, 49, 50, 52, 15, 57, 41, 14, 0, 1, 29, 0, 42, 58, 56, 97, 98,
+ 100, 95, 0, 0, 83, 0, 55, 0, 78, 0, 69, 0, 0, 0, 91, 89, 0, 74, 13, 0,
+ 0, 82, 79, 80, 81, 75, 76, 77, 71, 0, 72, 73, 0, 0, 0, 63, 64, 65, 61,
+ 66, 67, 0, 87, 0, 105, 0, 70, 0, 68, 0, 88, 0, 107, 102, 94, 96, 103,
+ 62, 99, 60, 101, 59, 54, 93, 90, 0, 0 };
+
+/* keyboard driver declaration */
+static int akbd_configure(int flags);
+static kbd_probe_t akbd_probe;
+static kbd_init_t akbd_init;
+static kbd_term_t akbd_term;
+static kbd_intr_t akbd_interrupt;
+static kbd_test_if_t akbd_test_if;
+static kbd_enable_t akbd_enable;
+static kbd_disable_t akbd_disable;
+static kbd_read_t akbd_read;
+static kbd_check_t akbd_check;
+static kbd_read_char_t akbd_read_char;
+static kbd_check_char_t akbd_check_char;
+static kbd_ioctl_t akbd_ioctl;
+static kbd_lock_t akbd_lock;
+static kbd_clear_state_t akbd_clear_state;
+static kbd_get_state_t akbd_get_state;
+static kbd_set_state_t akbd_set_state;
+static kbd_poll_mode_t akbd_poll;
+
+keyboard_switch_t akbdsw = {
+ akbd_probe,
+ akbd_init,
+ akbd_term,
+ akbd_interrupt,
+ akbd_test_if,
+ akbd_enable,
+ akbd_disable,
+ akbd_read,
+ akbd_check,
+ akbd_read_char,
+ akbd_check_char,
+ akbd_ioctl,
+ akbd_lock,
+ akbd_clear_state,
+ akbd_get_state,
+ akbd_set_state,
+ genkbd_get_fkeystr,
+ akbd_poll,
+ genkbd_diag,
+};
+
+KEYBOARD_DRIVER(akbd, akbdsw, akbd_configure);
+
+static int
+adb_kbd_probe(device_t dev)
+{
+ uint8_t type;
+
+ type = adb_get_device_type(dev);
+
+ if (type != ADB_DEVICE_KEYBOARD)
+ return (ENXIO);
+
+ switch(adb_get_device_handler(dev)) {
+ case 1:
+ device_set_desc(dev,"Apple Standard Keyboard");
+ break;
+ case 2:
+ device_set_desc(dev,"Apple Extended Keyboard");
+ break;
+ case 4:
+ device_set_desc(dev,"Apple ISO Keyboard");
+ break;
+ case 5:
+ device_set_desc(dev,"Apple Extended ISO Keyboard");
+ break;
+ case 8:
+ device_set_desc(dev,"Apple Keyboard II");
+ break;
+ case 9:
+ device_set_desc(dev,"Apple ISO Keyboard II");
+ break;
+ case 12:
+ device_set_desc(dev,"PowerBook Keyboard");
+ break;
+ case 13:
+ device_set_desc(dev,"PowerBook ISO Keyboard");
+ break;
+ case 24:
+ device_set_desc(dev,"PowerBook Extended Keyboard");
+ break;
+ case 27:
+ device_set_desc(dev,"Apple Design Keyboard");
+ break;
+ case 195:
+ device_set_desc(dev,"PowerBook G3 Keyboard");
+ break;
+ case 196:
+ device_set_desc(dev,"iBook Keyboard");
+ break;
+ default:
+ device_set_desc(dev,"ADB Keyboard");
+ break;
+ }
+
+ return (0);
+}
+
+static int
+ms_to_ticks(int ms)
+{
+ if (hz > 1000)
+ return ms*(hz/1000);
+
+ return ms/(1000/hz);
+}
+
+static int
+adb_kbd_attach(device_t dev)
+{
+ struct adb_kbd_softc *sc;
+ keyboard_switch_t *sw;
+
+ sw = kbd_get_switch(KBD_DRIVER_NAME);
+ if (sw == NULL) {
+ return ENXIO;
+ }
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_mode = K_RAW;
+ sc->sc_state = 0;
+ sc->have_led_control = 0;
+ sc->buffers = 0;
+
+ /* Try stepping forward to the extended keyboard protocol */
+ adb_set_device_handler(dev,3);
+
+ mtx_init(&sc->sc_mutex,KBD_DRIVER_NAME,MTX_DEF,0);
+ cv_init(&sc->sc_cv,KBD_DRIVER_NAME);
+ callout_init(&sc->sc_repeater, 0);
+
+#ifdef AKBD_EMULATE_ATKBD
+ kbd_init_struct(&sc->sc_kbd, KBD_DRIVER_NAME, KB_101, 0, 0, 0, 0);
+ kbd_set_maps(&sc->sc_kbd, &key_map, &accent_map, fkey_tab,
+ sizeof(fkey_tab) / sizeof(fkey_tab[0]));
+#else
+ #error ADB raw mode not implemented
+#endif
+
+ KBD_FOUND_DEVICE(&sc->sc_kbd);
+ KBD_PROBE_DONE(&sc->sc_kbd);
+ KBD_INIT_DONE(&sc->sc_kbd);
+ KBD_CONFIG_DONE(&sc->sc_kbd);
+
+ (*sw->enable)(&sc->sc_kbd);
+
+ kbd_register(&sc->sc_kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (kbd_attach(&sc->sc_kbd)) {
+ adb_kbd_detach(dev);
+ return ENXIO;
+ }
+#endif
+
+ adb_set_autopoll(dev,1);
+
+ /* Check (asynchronously) if we can read out the LED state from
+ this keyboard by reading the key state register */
+ adb_send_packet(dev,ADB_COMMAND_TALK,2,0,NULL);
+
+ return (0);
+}
+
+static int
+adb_kbd_detach(device_t dev)
+{
+ struct adb_kbd_softc *sc;
+ keyboard_t *kbd;
+
+ sc = device_get_softc(dev);
+
+ adb_set_autopoll(dev,0);
+ callout_stop(&sc->sc_repeater);
+
+ mtx_lock(&sc->sc_mutex);
+
+ kbd = kbd_get_keyboard(kbd_find_keyboard(KBD_DRIVER_NAME,
+ device_get_unit(dev)));
+
+ kbdd_disable(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ kbd_detach(kbd);
+#endif
+
+ kbdd_term(kbd);
+
+ mtx_unlock(&sc->sc_mutex);
+
+ mtx_destroy(&sc->sc_mutex);
+ cv_destroy(&sc->sc_cv);
+
+ return (0);
+}
+
+static u_int
+adb_kbd_receive_packet(device_t dev, u_char status,
+ u_char command, u_char reg, int len, u_char *data)
+{
+ struct adb_kbd_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (command != ADB_COMMAND_TALK)
+ return 0;
+
+ if (reg == 2 && len == 2) {
+ sc->have_led_control = 1;
+ return 0;
+ }
+
+ if (reg != 0 || len != 2)
+ return (0);
+
+ mtx_lock(&sc->sc_mutex);
+ if ((data[0] & 0x7f) == 57 && sc->buffers < 7) {
+ /* Fake the down/up cycle for caps lock */
+ sc->buffer[sc->buffers++] = data[0] & 0x7f;
+ sc->buffer[sc->buffers++] = (data[0] & 0x7f) | (1 << 7);
+ } else {
+ sc->buffer[sc->buffers++] = data[0];
+ }
+
+ if (sc->buffer[sc->buffers-1] < 0xff)
+ sc->last_press = sc->buffer[sc->buffers-1];
+
+ if ((data[1] & 0x7f) == 57 && sc->buffers < 7) {
+ /* Fake the down/up cycle for caps lock */
+ sc->buffer[sc->buffers++] = data[1] & 0x7f;
+ sc->buffer[sc->buffers++] = (data[1] & 0x7f) | (1 << 7);
+ } else {
+ sc->buffer[sc->buffers++] = data[1];
+ }
+
+ if (sc->buffer[sc->buffers-1] < 0xff)
+ sc->last_press = sc->buffer[sc->buffers-1];
+
+ /* Stop any existing key repeating */
+ callout_stop(&sc->sc_repeater);
+
+ /* Schedule a repeat callback on keydown */
+ if (!(sc->last_press & (1 << 7))) {
+ callout_reset(&sc->sc_repeater,
+ ms_to_ticks(sc->sc_kbd.kb_delay1), akbd_repeat, sc);
+ }
+ mtx_unlock(&sc->sc_mutex);
+
+ cv_broadcast(&sc->sc_cv);
+
+ if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) {
+ sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
+ KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
+ }
+
+ return (0);
+}
+
+static void
+akbd_repeat(void *xsc) {
+ struct adb_kbd_softc *sc = xsc;
+ int notify_kbd = 0;
+
+ /* Fake an up/down key repeat so long as we have the
+ free buffers */
+ mtx_lock(&sc->sc_mutex);
+ if (sc->buffers < 7) {
+ sc->buffer[sc->buffers++] = sc->last_press | (1 << 7);
+ sc->buffer[sc->buffers++] = sc->last_press;
+
+ notify_kbd = 1;
+ }
+ mtx_unlock(&sc->sc_mutex);
+
+ if (notify_kbd && KBD_IS_ACTIVE(&sc->sc_kbd)
+ && KBD_IS_BUSY(&sc->sc_kbd)) {
+ sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
+ KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
+ }
+
+ /* Reschedule the callout */
+ callout_reset(&sc->sc_repeater, ms_to_ticks(sc->sc_kbd.kb_delay2),
+ akbd_repeat, sc);
+}
+
+static int
+akbd_configure(int flags)
+{
+ return 0;
+}
+
+static int
+akbd_probe(int unit, void *arg, int flags)
+{
+ return 0;
+}
+
+static int
+akbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+ return 0;
+}
+
+static int
+akbd_term(keyboard_t *kbd)
+{
+ return 0;
+}
+
+static int
+akbd_interrupt(keyboard_t *kbd, void *arg)
+{
+ return 0;
+}
+
+static int
+akbd_test_if(keyboard_t *kbd)
+{
+ return 0;
+}
+
+static int
+akbd_enable(keyboard_t *kbd)
+{
+ KBD_ACTIVATE(kbd);
+ return (0);
+}
+
+static int
+akbd_disable(keyboard_t *kbd)
+{
+ struct adb_kbd_softc *sc;
+ sc = (struct adb_kbd_softc *)(kbd);
+
+ callout_stop(&sc->sc_repeater);
+ KBD_DEACTIVATE(kbd);
+ return (0);
+}
+
+static int
+akbd_read(keyboard_t *kbd, int wait)
+{
+ return (0);
+}
+
+static int
+akbd_check(keyboard_t *kbd)
+{
+ struct adb_kbd_softc *sc;
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (FALSE);
+
+ sc = (struct adb_kbd_softc *)(kbd);
+
+ mtx_lock(&sc->sc_mutex);
+ if (sc->buffers > 0) {
+ mtx_unlock(&sc->sc_mutex);
+ return (TRUE);
+ }
+ mtx_unlock(&sc->sc_mutex);
+
+ return (FALSE);
+}
+
+static u_int
+akbd_read_char(keyboard_t *kbd, int wait)
+{
+ struct adb_kbd_softc *sc;
+ uint8_t adb_code, final_scancode;
+ int i;
+
+ sc = (struct adb_kbd_softc *)(kbd);
+
+ mtx_lock(&sc->sc_mutex);
+ if (!sc->buffers && wait)
+ cv_wait(&sc->sc_cv,&sc->sc_mutex);
+
+ if (!sc->buffers) {
+ mtx_unlock(&sc->sc_mutex);
+ return (0);
+ }
+
+ adb_code = sc->buffer[0];
+
+ for (i = 1; i < sc->buffers; i++)
+ sc->buffer[i-1] = sc->buffer[i];
+
+ sc->buffers--;
+ mtx_unlock(&sc->sc_mutex);
+
+ #ifdef AKBD_EMULATE_ATKBD
+ final_scancode = adb_to_at_scancode_map[adb_code & 0x7f];
+ final_scancode |= adb_code & 0x80;
+ #else
+ final_scancode = adb_code;
+ #endif
+
+ return (final_scancode);
+}
+
+static int
+akbd_check_char(keyboard_t *kbd)
+{
+ if (!KBD_IS_ACTIVE(kbd))
+ return (FALSE);
+
+ return (akbd_check(kbd));
+}
+
+static int
+set_typematic(keyboard_t *kbd, int code)
+{
+ /* These numbers are in microseconds, so convert to ticks */
+
+ static int delays[] = { 250, 500, 750, 1000 };
+ static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504 };
+
+ if (code & ~0x7f)
+ return EINVAL;
+ kbd->kb_delay1 = delays[(code >> 5) & 3];
+ kbd->kb_delay2 = rates[code & 0x1f];
+ return 0;
+}
+
+static int akbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t data)
+{
+ struct adb_kbd_softc *sc;
+ uint16_t r2;
+ int error;
+
+ sc = (struct adb_kbd_softc *)(kbd);
+ error = 0;
+
+ switch (cmd) {
+ case KDGKBMODE:
+ *(int *)data = sc->sc_mode;
+ break;
+ case KDSKBMODE:
+ switch (*(int *)data) {
+ case K_XLATE:
+ if (sc->sc_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALLTHROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (sc->sc_mode != *(int *)data)
+ sc->sc_mode = *(int *)data;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ break;
+
+ case KDGETLED:
+ *(int *)data = KBD_LED_VAL(kbd);
+ break;
+
+ case KDSKBSTATE:
+ if (*(int *)data & ~LOCK_MASK) {
+ error = EINVAL;
+ break;
+ }
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= *(int *)data;
+
+ /* FALLTHROUGH */
+
+ case KDSETLED:
+ KBD_LED_VAL(kbd) = *(int *)data;
+
+ if (!sc->have_led_control)
+ break;
+
+ r2 = (~0 & 0x04) | 3;
+
+ if (*(int *)data & NLKED)
+ r2 &= ~1;
+ if (*(int *)data & CLKED)
+ r2 &= ~2;
+ if (*(int *)data & SLKED)
+ r2 &= ~4;
+
+ adb_send_packet(sc->sc_dev,ADB_COMMAND_LISTEN,2,
+ sizeof(uint16_t),(u_char *)&r2);
+
+ break;
+
+ case KDGKBSTATE:
+ *(int *)data = sc->sc_state & LOCK_MASK;
+ break;
+
+ case KDSETREPEAT:
+ if (!KBD_HAS_DEVICE(kbd))
+ return 0;
+ if (((int *)data)[1] < 0)
+ return EINVAL;
+ if (((int *)data)[0] < 0)
+ return EINVAL;
+ else if (((int *)data)[0] == 0) /* fastest possible value */
+ kbd->kb_delay1 = 200;
+ else
+ kbd->kb_delay1 = ((int *)data)[0];
+ kbd->kb_delay2 = ((int *)data)[1];
+
+ break;
+
+ case KDSETRAD:
+ error = set_typematic(kbd, *(int *)data);
+ break;
+
+ case PIO_KEYMAP:
+ case PIO_KEYMAPENT:
+ case PIO_DEADKEYMAP:
+ default:
+ return (genkbd_commonioctl(kbd, cmd, data));
+ }
+
+ return (error);
+}
+
+static int akbd_lock(keyboard_t *kbd, int lock)
+{
+ return (0);
+}
+
+static void akbd_clear_state(keyboard_t *kbd)
+{
+}
+
+static int akbd_get_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ return (0);
+}
+
+static int akbd_set_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ return (0);
+}
+
+static int akbd_poll(keyboard_t *kbd, int on)
+{
+ return (0);
+}
+
+static int
+akbd_modevent(module_t mod, int type, void *data)
+{
+ switch (type) {
+ case MOD_LOAD:
+ kbd_add_driver(&akbd_kbd_driver);
+ break;
+
+ case MOD_UNLOAD:
+ kbd_delete_driver(&akbd_kbd_driver);
+ break;
+
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ return (0);
+}
+
+DEV_MODULE(akbd, akbd_modevent, NULL);
+
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);
+}
+
diff --git a/sys/dev/adb/adbvar.h b/sys/dev/adb/adbvar.h
new file mode 100644
index 0000000..da0965d
--- /dev/null
+++ b/sys/dev/adb/adbvar.h
@@ -0,0 +1,56 @@
+/*-
+ * 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$
+ */
+
+#ifndef _POWERPC_ADBVAR_H_
+#define _POWERPC_ADBVAR_H_
+
+#include "adb.h"
+
+enum {
+ ADB_COMMAND_BUS_RESET = 0
+};
+
+
+struct adb_softc {
+ device_t sc_dev;
+
+ device_t parent;
+
+ struct mtx sc_sync_mtx;
+
+ volatile int sync_packet;
+ volatile int packet_reply;
+
+ uint16_t autopoll_mask;
+
+ uint8_t syncreg[8];
+
+ device_t children[16];
+ struct adb_devinfo devinfo[16];
+};
+
+#endif /* _POWERPC_ADBVAR_H_ */
OpenPOWER on IntegriCloud