summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/amd64/conf/GENERIC21
-rw-r--r--sys/conf/files31
-rw-r--r--sys/conf/options6
-rw-r--r--sys/dev/pci/uhci_pci.c338
-rw-r--r--sys/dev/usb/hid.c469
-rw-r--r--sys/dev/usb/hid.h89
-rw-r--r--sys/dev/usb/ohcireg.h204
-rw-r--r--sys/dev/usb/ohcivar.h107
-rw-r--r--sys/dev/usb/queue.addendum.h29
-rw-r--r--sys/dev/usb/ugen.c183
-rw-r--r--sys/dev/usb/uhci.c2355
-rw-r--r--sys/dev/usb/uhcireg.h184
-rw-r--r--sys/dev/usb/uhcivar.h179
-rw-r--r--sys/dev/usb/uhid.c632
-rw-r--r--sys/dev/usb/uhub.c490
-rw-r--r--sys/dev/usb/ukbd.c651
-rw-r--r--sys/dev/usb/ulpt.c502
-rw-r--r--sys/dev/usb/ums.c812
-rw-r--r--sys/dev/usb/usb.c603
-rw-r--r--sys/dev/usb/usb.h387
-rw-r--r--sys/dev/usb/usb_if.m11
-rw-r--r--sys/dev/usb/usb_mem.h91
-rw-r--r--sys/dev/usb/usb_port.h79
-rw-r--r--sys/dev/usb/usb_quirks.c89
-rw-r--r--sys/dev/usb/usb_quirks.h49
-rw-r--r--sys/dev/usb/usb_subr.c880
-rw-r--r--sys/dev/usb/usbdevs.h121
-rw-r--r--sys/dev/usb/usbdevs_data.h276
-rw-r--r--sys/dev/usb/usbdi.c1112
-rw-r--r--sys/dev/usb/usbdi.h303
-rw-r--r--sys/dev/usb/usbdi_util.c416
-rw-r--r--sys/dev/usb/usbdi_util.h74
-rw-r--r--sys/dev/usb/usbdivar.h231
-rw-r--r--sys/dev/usb/usbhid.h133
-rw-r--r--sys/i386/conf/GENERIC21
-rw-r--r--usr.sbin/usbd/Makefile15
-rw-r--r--usr.sbin/usbd/usbd.880
-rw-r--r--usr.sbin/usbd/usbd.c183
-rw-r--r--usr.sbin/usbdevs/Makefile8
-rw-r--r--usr.sbin/usbdevs/usbdevs.867
-rw-r--r--usr.sbin/usbdevs/usbdevs.c212
41 files changed, 12720 insertions, 3 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 930349a..f2c58b6 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -11,7 +11,7 @@
# device lines is present in the ./LINT configuration file. If you are
# in doubt as to the purpose or necessity of a line, check first in LINT.
#
-# $Id: GENERIC,v 1.130 1998/11/03 22:01:21 des Exp $
+# $Id: GENERIC,v 1.131 1998/11/12 11:29:28 obrien Exp $
machine "i386"
cpu "I386_CPU"
@@ -179,3 +179,22 @@ options SYSVSHM
# option. The number of devices determines the maximum number of
# simultaneous BPF clients programs runnable.
#pseudo-device bpfilter 4 #Berkeley packet filter
+
+
+# USB support
+#controller uhci0
+#controller usb0
+#
+# for the moment we have to specify the priorities of the device
+# drivers explicitly by the ordering in the list below. This will
+# be changed in the future.
+#
+#device ums0
+#device ukbd0
+#device ulpt0
+#device uhub0
+#device hid0
+#device ugen0
+#
+#options USB_DEBUG
+#options USBVERBOSE
diff --git a/sys/conf/files b/sys/conf/files
index 2f95d46..7d18ce7 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -599,3 +599,34 @@ vm/vm_swap.c standard
vm/vm_unix.c standard
vm/vnode_pager.c standard
vm/vm_zone.c standard
+#
+# USB support
+dev/pci/uhci_pci.c optional uhci device-driver
+usb_if.o optional uhci device-driver \
+ dependency "usb_if.c" \
+ compile-with "${NORMAL_C}" \
+ no-implicit-rule local
+usb_if.c optional uhci device-driver \
+ dependency "$S/kern/makedevops.pl $S/dev/usb/usb_if.m" \
+ compile-with "perl5 $S/kern/makedevops.pl -c $S/dev/usb/usb_if.m" \
+ no-obj no-implicit-rule before-depend local \
+ clean "usb_if.c"
+usb_if.h optional uhci device-driver \
+ dependency "$S/kern/makedevops.pl $S/dev/usb/usb_if.m" \
+ compile-with "perl5 $S/kern/makedevops.pl -h $S/dev/usb/usb_if.m" \
+ no-obj no-implicit-rule before-depend \
+ clean "usb_if.h"
+dev/usb/uhci.c optional uhci device-driver
+dev/usb/usb.c optional usb device-driver
+dev/usb/usbdi.c optional usb device-driver
+dev/usb/usbdi_util.c optional usb device-driver
+#dev/usb/usb_mem.c optional usb device-driver
+dev/usb/usb_subr.c optional usb device-driver
+dev/usb/usb_quirks.c optional usb device-driver
+dev/usb/hid.c optional usb device-driver
+dev/usb/uhub.c optional uhub device-driver
+dev/usb/ukbd.c optional ukbd device-driver
+dev/usb/ulpt.c optional ulpt device-driver
+dev/usb/ums.c optional ums device-driver
+dev/usb/ugen.c optional ugen device-driver
+#dev/usb/uhid.c optional hid device-driver
diff --git a/sys/conf/options b/sys/conf/options
index 0094484..8cbd96f 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -1,4 +1,4 @@
-# $Id: options,v 1.107 1998/11/05 14:28:17 dg Exp $
+# $Id: options,v 1.108 1998/11/23 09:58:59 phk Exp $
#
# On the handling of kernel options
#
@@ -305,3 +305,7 @@ SIMOS opt_simos.h
# options for bus/device framework
BUS_DEBUG opt_bus.h
+
+# options for USB support
+USB_DEBUG opt_usb.h
+USBVERBOSE opt_usb.h
diff --git a/sys/dev/pci/uhci_pci.c b/sys/dev/pci/uhci_pci.c
new file mode 100644
index 0000000..71bc228
--- /dev/null
+++ b/sys/dev/pci/uhci_pci.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#if defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/device.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+
+#if defined(__NetBSD__)
+#include <machine/bus.h>
+#include <dev/pci/pcivar.h>
+#elif defined(__FreeBSD__)
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+
+#define PCI_CLASS_SERIALBUS 0x0c000000
+#define PCI_SUBCLASS_COMMUNICATIONS_SERIAL 0x00000000
+#define PCI_SUBCLASS_SERIALBUS_FIREWIRE 0x00000000
+#define PCI_SUBCLASS_SERIALBUS_ACCESS 0x00010000
+#define PCI_SUBCLASS_SERIALBUS_SSA 0x00020000
+#define PCI_SUBCLASS_SERIALBUS_USB 0x00030000
+#define PCI_SUBCLASS_SERIALBUS_FIBER 0x00040000
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/uhcireg.h>
+#include <dev/usb/uhcivar.h>
+
+#if defined(__NetBSD__)
+int uhci_pci_match __P((struct device *, struct cfdata *, void *));
+void uhci_pci_attach __P((struct device *, struct device *, void *));
+
+struct cfattach uhci_pci_ca = {
+ sizeof(uhci_softc_t), uhci_pci_match, uhci_pci_attach
+};
+
+#elif defined(__FreeBSD__)
+
+#define PCI_INTERFACE_MASK 0x0000ff00
+#define PCI_INTERFACE_SHIFT 8
+#define PCI_INTERFACE(d) (((d)>>8)&PCI_INTERFACE_MASK)
+#define PCI_SUBCLASS(d) ((d)&PCI_SUBCLASS_MASK)
+#define PCI_CLASS(d) ((d)&PCI_CLASS_MASK)
+
+#define PCI_VENDOR(d) ((d)&0xffff)
+#define PCI_DEVICE(d) (((d)>>8)&0xffff)
+
+#define PCI_UHCI_DEVICEID_PIIX3 0x70208086ul
+#define PCI_UHCI_DEVICEID_PIIX4 0x71128086ul
+#define PCI_UHCI_DEVICEID_PIIX4E 0x71128086ul /* no separate step */
+
+#define PCI_UHCI_BASE_REG 0x20
+
+static char *uhci_pci_probe __P((pcici_t, pcidi_t));
+static void uhci_pci_attach __P((pcici_t, int));
+
+u_long uhci_count = 0; /* global counter for nr. of devices found */
+
+static struct pci_device uhci_pci_device = {
+ "uhci",
+ uhci_pci_probe,
+ uhci_pci_attach,
+ &uhci_count,
+ NULL
+};
+
+DATA_SET(pcidevice_set, uhci_pci_device);
+#endif
+
+
+#if defined(__NetBSD__)
+int
+uhci_pci_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct pci_attach_args *pa = (struct pci_attach_args *) aux;
+
+ if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS &&
+ PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB &&
+ PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_UHCI)
+ return 1;
+
+ return 0;
+}
+
+#elif defined(__FreeBSD__)
+static char *
+uhci_pci_probe(pcici_t config_id, pcidi_t device_id)
+{
+ u_int32_t class;
+
+ if (device_id == PCI_UHCI_DEVICEID_PIIX3)
+ return ("Intel 82371SB USB Host Controller");
+ else if (device_id == PCI_UHCI_DEVICEID_PIIX4)
+ return ("Intel 82371AB/EB USB Host Controller");
+ else {
+ class = pci_conf_read(config_id, PCI_CLASS_REG);
+ if ( PCI_CLASS(class) == PCI_CLASS_SERIALBUS
+ && PCI_SUBCLASS(class) == PCI_SUBCLASS_SERIALBUS_USB
+ && PCI_INTERFACE(class) == PCI_INTERFACE_UHCI) {
+ return ("UHCI Host Controller");
+ }
+ }
+
+ return NULL; /* dunno... */
+}
+#endif
+
+
+#if defined(__NetBSD__)
+void
+uhci_pci_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ uhci_softc_t *sc = (uhci_softc_t *)self;
+ struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+ pci_chipset_tag_t pc = pa->pa_pc;
+ char const *intrstr;
+ pci_intr_handle_t ih;
+ pcireg_t csr;
+ char *typestr, *vendor;
+ char devinfo[256];
+ usbd_status r;
+
+ pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo);
+ printf(": %s (rev. 0x%02x)\n", devinfo, PCI_REVISION(pa->pa_class));
+
+ /* Map I/O registers */
+ if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, 0,
+ &sc->iot, &sc->ioh, NULL, NULL)) {
+ printf("%s: can't map i/o space\n", sc->sc_bus.bdev.dv_xname);
+ return;
+ }
+
+ sc->sc_dmatag = pa->pa_dmat;
+
+
+ /* Enable the device. */
+ csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
+ csr | PCI_COMMAND_MASTER_ENABLE);
+
+ /* Map and establish the interrupt. */
+ return EFAULT;
+
+ if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin,
+ pa->pa_intrline, &ih)) {
+ printf("%s: couldn't map interrupt\n",
+ sc->sc_bus.bdev.dv_xname);
+ return;
+ }
+ intrstr = pci_intr_string(pc, ih);
+ sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB, uhci_intr, sc);
+ if (sc->sc_ih == NULL) {
+ printf("%s: couldn't establish interrupt",
+ sc->sc_bus.bdev.dv_xname);
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
+ return;
+ }
+ printf("%s: interrupting at %s\n", sc->sc_bus.bdev.dv_xname, intrstr);
+
+ switch(pci_conf_read(pc, pa->pa_tag, PCI_USBREV) & PCI_USBREV_MASK) {
+ case PCI_USBREV_PRE_1_0:
+ typestr = "pre 1.0";
+ break;
+ case PCI_USBREV_1_0:
+ typestr = "1.0";
+ break;
+ default:
+ typestr = "unknown";
+ break;
+ }
+ printf("%s: USB version %s\n", sc->sc_bus.bdev.dv_xname, typestr);
+
+ /* Figure out vendor for root hub descriptor. */
+ vendor = pci_findvendor(pa->pa_id);
+ if (vendor)
+ strncpy(sc->sc_vendor, vendor, sizeof(sc->sc_vendor));
+ else
+ sprintf(sc->sc_vendor, "vendor 0x%04x", PCI_VENDOR(pa->pa_id));
+
+ r = uhci_init(sc);
+ if (r != USBD_NORMAL_COMPLETION) {
+ printf("%s: init failed, error=%d\n",
+ sc->sc_bus.bdev.dv_xname, r);
+ return;
+ }
+
+ /* Attach usb device. */
+ config_found((void *)sc, &sc->sc_bus, usbctlprint);
+}
+
+
+#elif defined(__FreeBSD__)
+
+static void
+uhci_pci_attach(config_id, unit)
+ pcici_t config_id;
+ int unit;
+{
+ int irq;
+ int id;
+ char *typestr;
+ char devinfo[256];
+ usbd_status r;
+ uhci_softc_t *sc = NULL;
+ int legsup;
+
+ sc = malloc(sizeof(uhci_softc_t), M_DEVBUF, M_NOWAIT);
+ if ( sc == NULL ) {
+ printf("usb%d: could not allocate memory", unit);
+ return;
+ }
+ memset(sc, 0, sizeof(uhci_softc_t));
+
+ sc->sc_iobase = pci_conf_read(config_id,PCI_UHCI_BASE_REG) & 0xffe0;
+ sc->sc_int = pci_conf_read(config_id,PCI_INTERRUPT_REG) & 0xff;
+ sc->unit = unit;
+
+ if ( !pci_map_int(config_id, (pci_inthand_t *)uhci_intr,
+ (void *) sc, &bio_imask)) {
+ printf("usb%d: Unable to map irq\n", unit);
+ return;
+ }
+
+ if (bootverbose) {
+ printf("usb%d: interrupting at %d\n", unit, sc->sc_int);
+ switch(pci_conf_read(config_id, PCI_USBREV) & PCI_USBREV_MASK) {
+ case PCI_USBREV_PRE_1_0:
+ typestr = "pre 1.0";
+ break;
+ case PCI_USBREV_1_0:
+ typestr = "1.0";
+ break;
+ default:
+ typestr = "unknown";
+ break;
+ }
+ printf("usb%d: USB version %s\n", unit, typestr);
+ }
+
+ /* Figure out vendor for root hub descriptor. */
+ id = pci_conf_read(config_id, PCI_ID_REG);
+ if (PCI_VENDOR(id) == 0x8086)
+ sprintf(sc->sc_vendor, "Intel");
+ else
+ sprintf(sc->sc_vendor, "Vendor 0x%04x", PCI_VENDOR(id));
+
+ r = uhci_init(sc);
+ if (r != USBD_NORMAL_COMPLETION) {
+ printf("usb%d: init failed, error=%d\n", unit, r);
+ return;
+ }
+
+
+ /* We add a child to the root bus. After PCI configuration
+ * has completed the root bus will start to probe and
+ * attach all the devices attached to it, including our new
+ * kid.
+ *
+ * FIXME Sometime in the future the UHCI controller itself will
+ * become a kid of PCI device and this device add will no longer
+ * be necessary.
+ *
+ * See README for an elaborate description of the bus
+ * structure in spe.
+ */
+ sc->sc_bus.bdev = device_add_child(root_bus, "usb", unit, sc);
+ if (!sc->sc_bus.bdev)
+ DEVICE_ERROR(sc->sc_bus.bdev,
+ ("unable to add USB device to root bus\n"));
+
+ id = pci_conf_read(config_id, PCI_ID_REG);
+ switch (id) {
+ case PCI_UHCI_DEVICEID_PIIX3:
+ device_set_desc(sc->sc_bus.bdev, "Intel 82371SB USB Host Controller");
+ break;
+ case PCI_UHCI_DEVICEID_PIIX4:
+ device_set_desc(sc->sc_bus.bdev, "Intel 82371AB/EB USB Host Controller");
+ break;
+ default:
+ device_set_desc(sc->sc_bus.bdev, "UHCI Host Controller");
+ }
+
+ return;
+}
+#endif
diff --git a/sys/dev/usb/hid.c b/sys/dev/usb/hid.c
new file mode 100644
index 0000000..519e6d6
--- /dev/null
+++ b/sys/dev/usb/hid.c
@@ -0,0 +1,469 @@
+/* $NetBSD: hid.c,v 1.2 1998/07/24 20:57:46 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/hid.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+static void hid_clear_local __P((struct hid_item *));
+
+#define MAXUSAGE 100
+struct hid_data {
+ u_char *start;
+ u_char *end;
+ u_char *p;
+ struct hid_item cur;
+ int32_t usages[MAXUSAGE];
+ int nu;
+ int minset;
+ int multi;
+ int multimax;
+ int kindset;
+};
+
+static void
+hid_clear_local(c)
+ struct hid_item *c;
+{
+ c->usage = 0;
+ c->usage_minimum = 0;
+ c->usage_maximum = 0;
+ c->designator_index = 0;
+ c->designator_minimum = 0;
+ c->designator_maximum = 0;
+ c->string_index = 0;
+ c->string_minimum = 0;
+ c->string_maximum = 0;
+ c->set_delimiter = 0;
+}
+
+struct hid_data *
+hid_start_parse(d, len, kindset)
+ void *d;
+ int len;
+ int kindset;
+{
+ struct hid_data *s;
+ u_char *p = d;
+
+ s = malloc(sizeof *s, M_TEMP, M_WAITOK);
+ memset(s, 0, sizeof *s);
+ s->start = s->p = p;
+ s->end = p + len;
+ s->kindset = kindset;
+ return (s);
+}
+
+void
+hid_end_parse(s)
+ struct hid_data *s;
+{
+ while (s->cur.next) {
+ struct hid_item *hi = s->cur.next->next;
+ free(s->cur.next, M_TEMP);
+ s->cur.next = hi;
+ }
+ free(s, M_TEMP);
+}
+
+int
+hid_get_item(s, h)
+ struct hid_data *s;
+ struct hid_item *h;
+{
+ struct hid_item *c = &s->cur;
+ int bTag, bType, bSize;
+ u_char *data;
+ int32_t dval;
+ u_char *p;
+ struct hid_item *hi;
+ int i;
+
+ top:
+ if (s->multimax) {
+ if (s->multi < s->multimax) {
+ c->usage = s->usages[min(s->multi, s->nu-1)];
+ s->multi++;
+ *h = *c;
+ c->loc.pos += c->loc.size;
+ h->next = 0;
+ return (1);
+ } else {
+ c->loc.count = s->multimax;
+ s->multimax = 0;
+ s->nu = 0;
+ hid_clear_local(c);
+ }
+ }
+ for (;;) {
+ p = s->p;
+ if (p >= s->end)
+ return (0);
+
+ bSize = *p++;
+ if (bSize == 0xfe) {
+ /* long item */
+ bSize = *p++;
+ bSize |= *p++ << 8;
+ bTag = *p++;
+ data = p;
+ p += bSize;
+ bType = 0xff; /* XXX what should it be */
+ } else {
+ /* short item */
+ bTag = bSize >> 4;
+ bType = (bSize >> 2) & 3;
+ bSize &= 3;
+ if (bSize == 3) bSize = 4;
+ data = p;
+ p += bSize;
+ }
+ s->p = p;
+ switch(bSize) {
+ case 0:
+ dval = 0;
+ break;
+ case 1:
+ dval = (int8_t)*data++;
+ break;
+ case 2:
+ dval = *data++;
+ dval |= *data++ << 8;
+ dval = (int16_t)dval;
+ break;
+ case 4:
+ dval = *data++;
+ dval |= *data++ << 8;
+ dval |= *data++ << 16;
+ dval |= *data++ << 24;
+ break;
+ default:
+ printf("BAD LENGTH %d\n", bSize);
+ continue;
+ }
+
+ switch (bType) {
+ case 0: /* Main */
+ switch (bTag) {
+ case 8: /* Input */
+ if (!(s->kindset & (1 << hid_input)))
+ continue;
+ c->kind = hid_input;
+ c->flags = dval;
+ ret:
+ if (c->flags & HIO_VARIABLE) {
+ s->multimax = c->loc.count;
+ s->multi = 0;
+ c->loc.count = 1;
+ if (s->minset) {
+ for (i = c->usage_minimum;
+ i <= c->usage_maximum;
+ i++) {
+ s->usages[s->nu] = i;
+ if (s->nu < MAXUSAGE-1)
+ s->nu++;
+ }
+ s->minset = 0;
+ }
+ goto top;
+ } else {
+ *h = *c;
+ h->next = 0;
+ c->loc.pos +=
+ c->loc.size * c->loc.count;
+ hid_clear_local(c);
+ s->minset = 0;
+ return (1);
+ }
+ case 9: /* Output */
+ if (!(s->kindset & (1 << hid_output)))
+ continue;
+ c->kind = hid_output;
+ c->flags = dval;
+ goto ret;
+ case 10: /* Collection */
+ c->kind = hid_collection;
+ c->collection = dval;
+ c->collevel++;
+ *h = *c;
+ hid_clear_local(c);
+ s->nu = 0;
+ return (1);
+ case 11: /* Feature */
+ if (!(s->kindset & (1 << hid_feature)))
+ continue;
+ c->kind = hid_feature;
+ c->flags = dval;
+ goto ret;
+ case 12: /* End collection */
+ c->kind = hid_endcollection;
+ c->collevel--;
+ *h = *c;
+ hid_clear_local(c);
+ s->nu = 0;
+ return (1);
+ default:
+ printf("Main bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ case 1: /* Global */
+ switch (bTag) {
+ case 0:
+ c->_usage_page = dval << 16;
+ break;
+ case 1:
+ c->logical_minimum = dval;
+ break;
+ case 2:
+ c->logical_maximum = dval;
+ break;
+ case 3:
+ c->physical_maximum = dval;
+ break;
+ case 4:
+ c->physical_maximum = dval;
+ break;
+ case 5:
+ c->unit_exponent = dval;
+ break;
+ case 6:
+ c->unit = dval;
+ break;
+ case 7:
+ c->loc.size = dval;
+ break;
+ case 8:
+ c->report_ID = dval;
+ break;
+ case 9:
+ c->loc.count = dval;
+ break;
+ case 10: /* Push */
+ hi = malloc(sizeof *hi, M_TEMP, M_WAITOK);
+ *hi = s->cur;
+ c->next = hi;
+ break;
+ case 11: /* Pop */
+ hi = c->next;
+ s->cur = *hi;
+ free(hi, M_TEMP);
+ break;
+ default:
+ printf("Global bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ case 2: /* Local */
+ switch (bTag) {
+ case 0:
+ if (bSize == 1)
+ dval = c->_usage_page | (dval&0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval&0xffff);
+ c->usage = dval;
+ if (s->nu < MAXUSAGE)
+ s->usages[s->nu++] = dval;
+ /* else XXX */
+ break;
+ case 1:
+ s->minset = 1;
+ if (bSize == 1)
+ dval = c->_usage_page | (dval&0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval&0xffff);
+ c->usage_minimum = dval;
+ break;
+ case 2:
+ if (bSize == 1)
+ dval = c->_usage_page | (dval&0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval&0xffff);
+ c->usage_maximum = dval;
+ break;
+ case 3:
+ c->designator_index = dval;
+ break;
+ case 4:
+ c->designator_minimum = dval;
+ break;
+ case 5:
+ c->designator_maximum = dval;
+ break;
+ case 7:
+ c->string_index = dval;
+ break;
+ case 8:
+ c->string_minimum = dval;
+ break;
+ case 9:
+ c->string_maximum = dval;
+ break;
+ case 10:
+ c->set_delimiter = dval;
+ break;
+ default:
+ printf("Local bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ default:
+ printf("default bType=%d\n", bType);
+ break;
+ }
+ }
+}
+
+int
+hid_report_size(buf, len, k, idp)
+ void *buf;
+ int len;
+ enum hid_kind k;
+ u_int8_t *idp;
+{
+ struct hid_data *d;
+ struct hid_item h;
+ int size, id;
+
+ id = 0;
+ for (d = hid_start_parse(buf, len, 1<<k); hid_get_item(d, &h); )
+ if (h.report_ID)
+ id = h.report_ID;
+ hid_end_parse(d);
+ size = h.loc.pos;
+ if (id) {
+ size += 8;
+ *idp = id; /* XXX wrong */
+ } else
+ *idp = 0;
+ return ((size + 7) / 8);
+}
+
+int
+hid_locate(desc, size, u, k, loc, flags)
+ void *desc;
+ int size;
+ u_int32_t u;
+ enum hid_kind k;
+ struct hid_location *loc;
+ u_int32_t *flags;
+{
+ struct hid_data *d;
+ struct hid_item h;
+
+ for (d = hid_start_parse(desc, size, 1<<k); hid_get_item(d, &h); ) {
+ if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) {
+ if (loc)
+ *loc = h.loc;
+ if (flags)
+ *flags = h.flags;
+ hid_end_parse(d);
+ return (1);
+ }
+ }
+ hid_end_parse(d);
+ loc->size = 0;
+ return (0);
+}
+
+u_long
+hid_get_data(buf, loc)
+ u_char *buf;
+ struct hid_location *loc;
+{
+ u_int hpos = loc->pos;
+ u_int hsize = loc->size;
+ u_int32_t data;
+ int i, s;
+
+ DPRINTFN(10, ("hid_get_data: loc %d/%d\n", hpos, hsize));
+
+ if (hsize == 0)
+ return (0);
+
+ data = 0;
+ s = hpos / 8;
+ for (i = hpos; i < hpos+hsize; i += 8)
+ data |= buf[i / 8] << ((i / 8 - s) * 8);
+ data >>= hpos % 8;
+ data &= (1 << hsize) - 1;
+ hsize = 32 - hsize;
+ /* Sign extend */
+ data = ((int32_t)data << hsize) >> hsize;
+ DPRINTFN(10, ("hid_get_data: loc %d/%d = %lu\n",
+ loc->pos, loc->size, (long)data));
+ return (data);
+}
+
+int
+hid_is_collection(desc, size, usage)
+ void *desc;
+ int size;
+ u_int32_t usage;
+{
+ struct hid_data *hd;
+ struct hid_item hi;
+ int r;
+
+ hd = hid_start_parse(desc, size, hid_input);
+ if (!hd)
+ return (0);
+
+ r = hid_get_item(hd, &hi) &&
+ hi.kind == hid_collection &&
+ hi.usage == usage;
+ hid_end_parse(hd);
+ return (r);
+}
diff --git a/sys/dev/usb/hid.h b/sys/dev/usb/hid.h
new file mode 100644
index 0000000..875a18eb
--- /dev/null
+++ b/sys/dev/usb/hid.h
@@ -0,0 +1,89 @@
+/* $NetBSD: hid.h,v 1.2 1998/07/24 20:57:46 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+enum hid_kind {
+ hid_input, hid_output, hid_feature, hid_collection, hid_endcollection
+};
+
+struct hid_location {
+ u_int32_t size;
+ u_int32_t count;
+ u_int32_t pos;
+};
+
+struct hid_item {
+ /* Global */
+ int32_t _usage_page;
+ int32_t logical_minimum;
+ int32_t logical_maximum;
+ int32_t physical_minimum;
+ int32_t physical_maximum;
+ int32_t unit_exponent;
+ int32_t unit;
+ int32_t report_ID;
+ /* Local */
+ int32_t usage;
+ int32_t usage_minimum;
+ int32_t usage_maximum;
+ int32_t designator_index;
+ int32_t designator_minimum;
+ int32_t designator_maximum;
+ int32_t string_index;
+ int32_t string_minimum;
+ int32_t string_maximum;
+ int32_t set_delimiter;
+ /* Misc */
+ int32_t collection;
+ int collevel;
+ enum hid_kind kind;
+ u_int32_t flags;
+ /* Location */
+ struct hid_location loc;
+ /* */
+ struct hid_item *next;
+};
+
+struct hid_data *hid_start_parse __P((void *d, int len, int kindset));
+void hid_end_parse __P((struct hid_data *s));
+int hid_get_item __P((struct hid_data *s, struct hid_item *h));
+int hid_report_size __P((void *buf, int len, enum hid_kind k, u_int8_t *id));
+int hid_locate __P((void *desc, int size, u_int32_t usage,
+ enum hid_kind kind, struct hid_location *loc,
+ u_int32_t *flags));
+u_long hid_get_data __P((u_char *buf, struct hid_location *loc));
+int hid_is_collection __P((void *desc, int size, u_int32_t usage));
diff --git a/sys/dev/usb/ohcireg.h b/sys/dev/usb/ohcireg.h
new file mode 100644
index 0000000..3f62ddf
--- /dev/null
+++ b/sys/dev/usb/ohcireg.h
@@ -0,0 +1,204 @@
+/* $NetBSD: ohcireg.h,v 1.2 1998/07/26 00:40:59 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _DEV_PCI_OHCIREG_H_
+#define _DEV_PCI_OHCIREG_H_
+
+/*** PCI config registers ***/
+
+#define PCI_CBMEM 0x10 /* configuration base memory */
+
+#define PCI_INTERFACE_OHCI 0x10
+
+/*** OHCI registers */
+
+#define OHCI_REVISION 0x00 /* OHCI revision # */
+#define OHCI_REV_LO(rev) ((rev)&0xf)
+#define OHCI_REV_HI(rev) (((rev)>>4)&0xf)
+#define OHCI_REV_LEGACY(rev) ((rev) & 0x100)
+
+#define OHCI_CONTROL 0x04
+#define OHCI_CBSR_MASK 0x00000003 /* Control/Bulk Service Ratio */
+#define OHCI_RATIO_1_1 0x00000000
+#define OHCI_RATIO_1_2 0x00000001
+#define OHCI_RATIO_1_3 0x00000002
+#define OHCI_RATIO_1_4 0x00000003
+#define OHCI_PLE 0x00000004 /* Periodic List Enable */
+#define OHCI_IE 0x00000008 /* Isochronous Enable */
+#define OHCI_CLE 0x00000010 /* Control List Enable */
+#define OHCI_BLE 0x00000020 /* Bulk List Enable */
+#define OHCI_HCFS_MASK 0x000000c0 /* HostControllerFunctionalState */
+#define OHCI_HCFS_RESET 0x00000000
+#define OHCI_HCFS_RESUME 0x00000040
+#define OHCI_HCFS_OPERATIONAL 0x00000080
+#define OHCI_HCFS_SUSPEND 0x000000c0
+#define OHCI_IR 0x00000100 /* Interrupt Routing */
+#define OHCI_RWC 0x00000200 /* Remote Wakeup Connected */
+#define OHCI_RWE 0x00000400 /* Remote Wakeup Enabled */
+#define OHCI_COMMAND_STATUS 0x08
+#define OHCI_HCR 0x00000001 /* Host Controller Reset */
+#define OHCI_CLF 0x00000002 /* Control List Filled */
+#define OHCI_BLF 0x00000004 /* Bulk List Filled */
+#define OHCI_OCR 0x00000008 /* Ownership Change Request */
+#define OHCI_SOC_MASK 0x00030000 /* Scheduling Overrun Count */
+#define OHCI_INTERRUPT_STATUS 0x0c
+#define OHCI_SO 0x00000001 /* Scheduling Overrun */
+#define OHCI_WDH 0x00000002 /* Writeback Done Head */
+#define OHCI_SF 0x00000004 /* Start of Frame */
+#define OHCI_RD 0x00000008 /* Resume Detected */
+#define OHCI_UE 0x00000010 /* Unrecoverable Error */
+#define OHCI_FNO 0x00000020 /* Frame Number Overflow */
+#define OHCI_RHSC 0x00000040 /* Root Hub Status Change */
+#define OHCI_OC 0x40000000 /* Ownership Change */
+#define OHCI_MIE 0x80000000 /* Master Interrupt Enable */
+#define OHCI_INTERRUPT_ENABLE 0x10
+#define OHCI_INTERRUPT_DISABLE 0x14
+#define OHCI_HCCA 0x18
+#define OHCI_PERIOD_CURRENT_ED 0x1c
+#define OHCI_CONTROL_HEAD_ED 0x20
+#define OHCI_CONTROL_CURRENT_ED 0x24
+#define OHCI_BULK_HEAD_ED 0x28
+#define OHCI_BULK_CURRENT_ED 0x2c
+#define OHCI_DONE_HEAD 0x30
+#define OHCI_FM_INTERVAL 0x34
+#define OHCI_GET_IVAL(s) ((s) & 0x3fff)
+#define OHCI_GET_FSMPS(s) (((s) >> 16) & 0x7fff)
+#define OHCI_FIT 0x80000000
+#define OHCI_FM_REMAINING 0x38
+#define OHCI_FM_NUMBER 0x3c
+#define OHCI_PERIODIC_START 0x40
+#define OHCI_LS_THRESHOLD 0x44
+#define OHCI_RH_DESCRIPTOR_A 0x48
+#define OHCI_GET_NDP(s) ((s) & 0xff)
+#define OHCI_PSM 0x0100 /* Power Switching Mode */
+#define OHCI_NPS 0x0200 /* No Power Switching */
+#define OHCI_GET_POTPGT(s) ((s) >> 24)
+#define OHCI_RH_DESCRIPTOR_B 0x4c
+#define OHCI_RH_STATUS 0x50
+#define OHCI_LPS 0x00000001 /* Local Power Status */
+#define OHCI_OCI 0x00000002 /* OverCurrent Indicator */
+#define OHCI_DRWE 0x00008000 /* Device Remote Wakeup Enable */
+#define OHCI_LPSC 0x00010000 /* Local Power Status Change */
+#define OHCI_CCIC 0x00020000 /* OverCurrent Indicator Change */
+#define OHCI_CRWE 0x80000000 /* Clear Remote Wakeup Enable */
+#define OHCI_RH_PORT_STATUS(n) (0x50 + (n)*4) /* 1 based indexing */
+
+#define OHCI_LES (OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE)
+#define OHCI_ALL_INTRS (OHCI_SO | OHCI_WDH | OHCI_SF | OHCI_RD | OHCI_UE | OHCI_FNO | OHCI_RHSC | OHCI_OC)
+#define OHCI_NORMAL_INTRS (OHCI_SO | OHCI_WDH | OHCI_RD | OHCI_UE | OHCI_RHSC)
+
+#define OHCI_FSMPS(i) (((i-210)*6/7) << 16)
+#define OHCI_PERIODIC(i) ((i)*9/10)
+
+typedef u_int32_t ohci_physaddr_t;
+
+#define OHCI_NO_INTRS 32
+struct ohci_hcca {
+ ohci_physaddr_t hcca_interrupt_table[OHCI_NO_INTRS];
+ u_int32_t hcca_frame_number;
+ ohci_physaddr_t hcca_done_head;
+#define OHCI_DONE_INTRS 1
+};
+#define OHCI_HCCA_SIZE 256
+#define OHCI_HCCA_ALIGN 256
+
+typedef struct {
+ u_int32_t ed_flags;
+#define OHCI_ED_GET_FA(s) ((s) & 0x7f)
+#define OHCI_ED_ADDRMASK 0x0000007f
+#define OHCI_ED_SET_FA(s) (s)
+#define OHCI_ED_GET_EN(s) (((s) >> 7) & 0xf)
+#define OHCI_ED_SET_EN(s) ((s) << 7)
+#define OHCI_ED_DIR_MASK 0x00001800
+#define OHCI_ED_DIR_TD 0x00000000
+#define OHCI_ED_DIR_OUT 0x00000800
+#define OHCI_ED_DIR_IN 0x00001000
+#define OHCI_ED_SPEED 0x00002000
+#define OHCI_ED_SKIP 0x00004000
+#define OHCI_ED_FORMAT_GEN 0x00000000
+#define OHCI_ED_FORMAT_ISO 0x00008000
+#define OHCI_ED_GET_MAXP(s) (((s) >> 16) & 0x07ff)
+#define OHCI_ED_SET_MAXP(s) ((s) << 16)
+ ohci_physaddr_t ed_tailp;
+#define OHCI_HALTED 0x00000002
+#define OHCI_TOGGLECARRY 0x00000001
+#define OHCI_TAILMASK 0xfffffffc
+ ohci_physaddr_t ed_headp;
+ ohci_physaddr_t ed_nexted;
+} ohci_ed_t;
+#define OHCI_ED_SIZE 16
+#define OHCI_ED_ALIGN 16
+
+typedef struct {
+ u_int32_t td_flags;
+#define OHCI_TD_R 0x00040000 /* Buffer Rounding */
+#define OHCI_TD_DP_MASK 0x00180000 /* Direction / PID */
+#define OHCI_TD_SETUP 0x00000000
+#define OHCI_TD_OUT 0x00080000
+#define OHCI_TD_IN 0x00100000
+#define OHCI_TD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */
+#define OHCI_TD_SET_DI(x) ((x) << 21)
+#define OHCI_TD_NOINTR 0x00e00000
+#define OHCI_TD_TOGGLE_CARRY 0x00000000
+#define OHCI_TD_TOGGLE_0 0x02000000
+#define OHCI_TD_TOGGLE_1 0x03000000
+#define OHCI_TD_GET_EC(x) (((x) >> 26) & 3) /* Error Count */
+#define OHCI_TD_GET_CC(x) ((x) >> 28) /* Condition Code */
+#define OHCI_TD_NOCC 0xf0000000
+ ohci_physaddr_t td_cbp; /* Current Buffer Pointer */
+ ohci_physaddr_t td_nexttd; /* Next TD */
+ ohci_physaddr_t td_be; /* Buffer End */
+} ohci_td_t;
+#define OHCI_TD_SIZE 16
+#define OHCI_TD_ALIGN 16
+
+#define OHCI_CC_NO_ERROR 0
+#define OHCI_CC_CRC 1
+#define OHCI_CC_BIT_STUFFING 2
+#define OHCI_CC_DATA_TOGGLE_MISMATCH 3
+#define OHCI_CC_STALL 4
+#define OHCI_CC_DEVICE_NOT_RESPONDING 5
+#define OHCI_CC_PID_CHECK_FAILURE 6
+#define OHCI_CC_UNEXPECTED_PID 7
+#define OHCI_CC_DATA_OVERRUN 8
+#define OHCI_CC_DATA_UNDERRUN 9
+#define OHCI_CC_BUFFER_OVERRUN 12
+#define OHCI_CC_BUFFER_UNDERRUN 13
+#define OHCI_CC_NOT_ACCESSED 15
+
+#endif /* _DEV_PCI_OHCIREG_H_ */
diff --git a/sys/dev/usb/ohcivar.h b/sys/dev/usb/ohcivar.h
new file mode 100644
index 0000000..dfafdb9
--- /dev/null
+++ b/sys/dev/usb/ohcivar.h
@@ -0,0 +1,107 @@
+/* $NetBSD: ohcivar.h,v 1.2 1998/07/24 21:09:07 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+typedef struct ohci_soft_ed {
+ ohci_ed_t *ed;
+ struct ohci_soft_ed *next;
+ ohci_physaddr_t physaddr;
+} ohci_soft_ed_t;
+#define OHCI_ED_CHUNK 256
+
+typedef struct ohci_soft_td {
+ ohci_td_t *td;
+ struct ohci_soft_td *nexttd; /* mirrors nexttd in TD */
+ struct ohci_soft_td *dnext; /* next in done list */
+ ohci_physaddr_t physaddr;
+ LIST_ENTRY(ohci_soft_td) hnext;
+ /*ohci_soft_ed_t *sed;*/
+ usbd_request_handle reqh;
+ u_int16_t len;
+} ohci_soft_td_t;
+#define OHCI_TD_CHUNK 256
+
+#define OHCI_NO_EDS (2*OHCI_NO_INTRS-1)
+
+#define OHCI_HASH_SIZE 128
+
+typedef struct ohci_softc {
+ struct usbd_bus sc_bus; /* base device */
+ void *sc_ih; /* interrupt vectoring */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+
+ bus_dma_tag_t sc_dmatag; /* DMA tag */
+ /* XXX should keep track of all DMA memory */
+
+ usb_dma_t sc_hccadma;
+ struct ohci_hcca *sc_hcca;
+ ohci_soft_ed_t *sc_eds[OHCI_NO_EDS];
+ u_int sc_bws[OHCI_NO_INTRS];
+
+ u_int32_t sc_eintrs;
+ ohci_soft_ed_t *sc_ctrl_head;
+ ohci_soft_ed_t *sc_bulk_head;
+
+ LIST_HEAD(, ohci_soft_td) sc_hash_tds[OHCI_HASH_SIZE];
+
+ int sc_noport;
+ u_int8_t sc_addr; /* device address */
+ u_int8_t sc_conf; /* device configuration */
+
+ ohci_soft_ed_t *sc_freeeds;
+ ohci_soft_td_t *sc_freetds;
+
+ usbd_request_handle sc_intrreqh;
+
+ int sc_intrs;
+ char sc_vendor[16];
+} ohci_softc_t;
+
+usbd_status ohci_init __P((ohci_softc_t *));
+int ohci_intr __P((void *));
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ohcidebug) printf x
+#define DPRINTFN(n,x) if (ohcidebug>(n)) printf x
+int ohcidebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
diff --git a/sys/dev/usb/queue.addendum.h b/sys/dev/usb/queue.addendum.h
new file mode 100644
index 0000000..9252342
--- /dev/null
+++ b/sys/dev/usb/queue.addendum.h
@@ -0,0 +1,29 @@
+/* These definitions are taken from the NetBSD /sys/sys/queue.h file
+ * The copyright as in /sys/sys/queue.h from FreeBSD applies (they are the same)
+ */
+
+/* This was called SIMPLEQ
+ */
+#ifndef STAILQ_HEAD_INITIALIZER
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+#endif
+
+/* This one was called SIMPLEQ_REMOVE_HEAD but removes not only the
+ * head element, but a whole queue of elements from the head.
+ */
+#ifndef STAILQ_REMOVE_HEAD_QUEUE
+#define STAILQ_REMOVE_HEAD_QUEUE(head, elm, field) do { \
+ if (((head)->stqh_first = (elm)->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (0)
+#endif
+
+
+/* This is called LIST and was called like that as well in the NetBSD version
+ */
+#ifndef LIST_HEAD_INITIALIZER
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+#endif
+
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c
new file mode 100644
index 0000000..6bcaa08
--- /dev/null
+++ b/sys/dev/usb/ugen.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/uio.h>
+#include <sys/conf.h>
+#include <sys/syslog.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct ugen_softc {
+ bdevice sc_dev;
+ usbd_device_handle sc_udev; /* device */
+ usbd_interface_handle sc_iface; /* interface */
+ int sc_ifaceno;
+ int sc_bulk;
+};
+
+#if defined(__NetBSD__)
+int ugen_match __P((struct device *, struct cfdata *, void *));
+void ugen_attach __P((struct device *, struct device *, void *));
+
+extern struct cfdriver ugen_cd;
+
+struct cfattach ugen_ca = {
+ sizeof(struct ugen_softc), ugen_match, ugen_attach
+};
+#elif defined(__FreeBSD__)
+static device_probe_t ugen_match;
+static device_attach_t ugen_attach;
+static device_detach_t ugen_detach;
+
+static devclass_t ugen_devclass;
+
+static device_method_t ugen_methods[] = {
+ DEVMETHOD(device_probe, ugen_match),
+ DEVMETHOD(device_attach, ugen_attach),
+ DEVMETHOD(device_detach, ugen_detach),
+ {0,0}
+};
+
+static driver_t ugen_driver = {
+ "ugen",
+ ugen_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct ugen_softc)
+};
+#endif
+
+
+#if defined(__NetBSD__)
+int
+ugen_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+ugen_match(device_t device)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(device);
+#endif
+ usb_interface_descriptor_t *id;
+
+ DPRINTFN(10,("ugen_match\n"));
+ if (uaa->usegeneric)
+ return UMATCH_GENERIC;
+ else
+ return UMATCH_NONE;
+}
+
+#if defined(__NetBSD__)
+void
+ugen_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct ugen_softc *sc = (struct ugen_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+ugen_attach(device_t self)
+{
+ struct ugen_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+#endif
+ usbd_device_handle dev = uaa->device;
+ usb_device_descriptor_t *udd = &dev->ddesc;
+ char devinfo[1024];
+
+ usbd_devinfo(dev, 0, devinfo);
+#if defined(__FreeBSD__)
+ device_set_desc(self, devinfo);
+ printf("%s%d", device_get_name(self), device_get_unit(self));
+#endif
+ printf(": %s (device class %d/%d)\n", devinfo,
+ udd->bDeviceClass, udd->bDeviceSubClass);
+ sc->sc_dev = self;
+
+ ATTACH_SUCCESS_RETURN;
+}
+
+#if defined(__FreeBSD__)
+static int
+ugen_detach(device_t self)
+{
+ /* we need to cast away the const returned by
+ * device_get_desc
+ */
+ char *devinfo = (char *) device_get_desc(self);
+
+ if (devinfo) {
+ device_set_desc(self, NULL);
+ free(devinfo, M_USB);
+ }
+
+ return 0;
+}
+
+DRIVER_MODULE(ugen, usb, ugen_driver, ugen_devclass, usb_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c
new file mode 100644
index 0000000..5065195
--- /dev/null
+++ b/sys/dev/usb/uhci.c
@@ -0,0 +1,2355 @@
+/* $NetBSD: uhci.c,v 1.10 1998/08/02 22:30:52 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * USB Universal Host Controller driver.
+ * Handles PIIX3 and PIIX4.
+ *
+ * Data sheets: ftp://download.intel.com/design/intarch/datashts/29055002.pdf
+ * ftp://download.intel.com/design/intarch/datashts/29056201.pdf
+ * UHCI spec: http://www.intel.com/design/usb/uhci11d.pdf
+ * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/select.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/uhcireg.h>
+#include <dev/usb/uhcivar.h>
+
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+#include "dev/usb/queue.addendum.h"
+
+#define delay(d) DELAY(d)
+
+static struct callout_handle uhci_timeout_handle
+ = CALLOUT_HANDLE_INITIALIZER(&uhci_timeout_handle);
+static struct callout_handle uhci_timo_handle
+ = CALLOUT_HANDLE_INITIALIZER(&uhci_timo_handle);
+#endif
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+struct uhci_pipe {
+ struct usbd_pipe pipe;
+ uhci_intr_info_t *iinfo;
+ int newtoggle;
+ /* Info needed for different pipe kinds. */
+ union {
+ /* Control pipe */
+ struct {
+ uhci_soft_qh_t *sqh;
+ usb_dma_t reqdma;
+ usb_dma_t datadma;
+ uhci_soft_td_t *setup, *stat, *xferend;
+ u_int length;
+ } ctl;
+ /* Interrupt pipe */
+ struct {
+ usb_dma_t datadma;
+ int npoll;
+ uhci_soft_qh_t **qhs;
+ } intr;
+ /* Bulk pipe */
+ struct {
+ uhci_soft_qh_t *sqh;
+ usb_dma_t datadma;
+ u_int length;
+ int isread;
+ } bulk;
+ } u;
+};
+
+/*
+ * The uhci_intr_info free list can be global since they contain
+ * no dma specific data. The other free lists do.
+ */
+LIST_HEAD(, uhci_intr_info) uhci_ii_free;
+
+void uhci_busreset __P((uhci_softc_t *));
+void uhci_run __P((uhci_softc_t *, int run));
+uhci_soft_td_t *uhci_alloc_std __P((uhci_softc_t *));
+void uhci_free_std __P((uhci_softc_t *, uhci_soft_td_t *));
+uhci_soft_qh_t *uhci_alloc_sqh __P((uhci_softc_t *));
+void uhci_free_sqh __P((uhci_softc_t *, uhci_soft_qh_t *));
+uhci_intr_info_t *uhci_alloc_intr_info __P((uhci_softc_t *));
+void uhci_free_intr_info __P((uhci_intr_info_t *ii));
+void uhci_enter_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *,
+ uhci_intr_info_t *));
+void uhci_exit_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *));
+
+void uhci_free_std_chain __P((uhci_softc_t *,
+ uhci_soft_td_t *, uhci_soft_td_t *));
+usbd_status uhci_alloc_std_chain __P((struct uhci_pipe *, uhci_softc_t *,
+ int, int, usb_dma_t *,
+ uhci_soft_td_t **,
+ uhci_soft_td_t **));
+void uhci_timo __P((void *));
+void uhci_waitintr __P((uhci_softc_t *, usbd_request_handle));
+void uhci_check_intr __P((uhci_softc_t *, uhci_intr_info_t *));
+void uhci_ii_done __P((uhci_intr_info_t *, int));
+void uhci_timeout __P((void *));
+void uhci_wakeup_ctrl __P((void *, int, int, void *, int));
+void uhci_lock_frames __P((uhci_softc_t *));
+void uhci_unlock_frames __P((uhci_softc_t *));
+void uhci_add_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *));
+void uhci_add_bulk __P((uhci_softc_t *, uhci_soft_qh_t *));
+void uhci_remove_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *));
+void uhci_remove_bulk __P((uhci_softc_t *, uhci_soft_qh_t *));
+int uhci_str __P((usb_string_descriptor_t *, int, char *));
+
+void uhci_device_close __P((struct uhci_pipe *));
+
+void uhci_wakeup_cb __P((usbd_request_handle reqh));
+
+usbd_status uhci_device_ctrl_transfer __P((usbd_request_handle));
+void uhci_device_ctrl_abort __P((usbd_request_handle));
+void uhci_device_ctrl_close __P((usbd_pipe_handle));
+usbd_status uhci_device_intr_transfer __P((usbd_request_handle));
+void uhci_device_intr_abort __P((usbd_request_handle));
+void uhci_device_intr_close __P((usbd_pipe_handle));
+usbd_status uhci_device_bulk_transfer __P((usbd_request_handle));
+void uhci_device_bulk_abort __P((usbd_request_handle));
+void uhci_device_bulk_close __P((usbd_pipe_handle));
+
+usbd_status uhci_root_ctrl_transfer __P((usbd_request_handle));
+void uhci_root_ctrl_abort __P((usbd_request_handle));
+void uhci_root_ctrl_close __P((usbd_pipe_handle));
+usbd_status uhci_root_intr_transfer __P((usbd_request_handle));
+void uhci_root_intr_abort __P((usbd_request_handle));
+void uhci_root_intr_close __P((usbd_pipe_handle));
+
+usbd_status uhci_open __P((usbd_pipe_handle));
+void uhci_poll __P((struct usbd_bus *));
+
+usbd_status uhci_device_request __P((usbd_request_handle reqh));
+void uhci_ctrl_done __P((uhci_intr_info_t *ii));
+void uhci_bulk_done __P((uhci_intr_info_t *ii));
+
+void uhci_add_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *));
+void uhci_remove_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *));
+usbd_status uhci_device_setintr __P((uhci_softc_t *sc,
+ struct uhci_pipe *pipe, int ival));
+void uhci_intr_done __P((uhci_intr_info_t *ii));
+
+#ifdef USB_DEBUG
+static void uhci_dumpregs __P((uhci_softc_t *));
+void uhci_dump_tds __P((uhci_soft_td_t *));
+void uhci_dump_qh __P((uhci_soft_qh_t *));
+void uhci_dump __P((void));
+void uhci_dump_td __P((uhci_soft_td_t *));
+#endif
+
+#if defined(__NetBSD__)
+#define UWRITE2(sc, r, x) bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x))
+#define UWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x))
+#define UREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r))
+#define UREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r))
+#elif defined(__FreeBSD__)
+#define UWRITE2(sc,r,x) outw((sc)->sc_iobase + (r), (x))
+#define UWRITE4(sc,r,x) outl((sc)->sc_iobase + (r), (x))
+#define UREAD2(sc,r) inw((sc)->sc_iobase + (r))
+#define UREAD4(sc,r) inl((sc)->sc_iobase + (r))
+#endif
+
+#define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd)
+#define UHCISTS(sc) UREAD2(sc, UHCI_STS)
+
+#define UHCI_RESET_TIMEOUT 100 /* reset timeout */
+#define UHCI_CTRL_TIMEOUT 500 /* control transaction timeout */
+#define UHCI_ISO_DELAY 50 /* delay of start of iso */
+
+#define UHCI_CURFRAME(sc) (UREAD2(sc, UHCI_FRNUM) & UHCI_FRNUM_MASK)
+
+#define UHCI_INTR_ENDPT 1
+
+struct usbd_methods uhci_root_ctrl_methods = {
+ uhci_root_ctrl_transfer,
+ uhci_root_ctrl_abort,
+ uhci_root_ctrl_close,
+ 0,
+};
+
+struct usbd_methods uhci_root_intr_methods = {
+ uhci_root_intr_transfer,
+ uhci_root_intr_abort,
+ uhci_root_intr_close,
+ 0,
+};
+
+struct usbd_methods uhci_device_ctrl_methods = {
+ uhci_device_ctrl_transfer,
+ uhci_device_ctrl_abort,
+ uhci_device_ctrl_close,
+ 0,
+};
+
+struct usbd_methods uhci_device_intr_methods = {
+ uhci_device_intr_transfer,
+ uhci_device_intr_abort,
+ uhci_device_intr_close,
+ 0,
+};
+
+struct usbd_methods uhci_device_bulk_methods = {
+ uhci_device_bulk_transfer,
+ uhci_device_bulk_abort,
+ uhci_device_bulk_close,
+ 0,
+};
+
+void
+uhci_busreset(sc)
+ uhci_softc_t *sc;
+{
+ UHCICMD(sc, UHCI_CMD_GRESET); /* global reset */
+ usbd_delay_ms(&sc->sc_bus, USB_RESET_DELAY); /* wait at least 10ms */
+ UHCICMD(sc, 0); /* do nothing */
+}
+
+usbd_status
+uhci_init(sc)
+ uhci_softc_t *sc;
+{
+ usbd_status r;
+ int i, j;
+ uhci_soft_qh_t *csqh, *bsqh, *sqh;
+ uhci_soft_td_t *std;
+ usb_dma_t dma;
+ static int uhci_global_init_done = 0;
+
+ DPRINTFN(1,("uhci_init: start\n"));
+
+ if (!uhci_global_init_done) {
+ uhci_global_init_done = 1;
+ LIST_INIT(&uhci_ii_free);
+ }
+
+#if defined(USB_DEBUG)
+ if (uhcidebug > 2)
+ uhci_dumpregs(sc);
+#endif
+
+ uhci_run(sc, 0); /* stop the controller */
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+
+ /* Allocate and initialize real frame array. */
+ r = usb_allocmem(sc->sc_dmatag,
+ UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t),
+ UHCI_FRAMELIST_ALIGN, &dma);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ sc->sc_pframes = KERNADDR(&dma);
+ UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */
+ UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&dma)); /* set frame list */
+
+ uhci_busreset(sc);
+
+ /* Allocate the dummy QH where bulk traffic will be queued. */
+ bsqh = uhci_alloc_sqh(sc);
+ if (!bsqh)
+ return (USBD_NOMEM);
+ bsqh->qh->qh_hlink = UHCI_PTR_T; /* end of QH chain */
+ bsqh->qh->qh_elink = UHCI_PTR_T;
+ sc->sc_bulk_start = sc->sc_bulk_end = bsqh;
+
+ /* Allocate the dummy QH where control traffic will be queued. */
+ csqh = uhci_alloc_sqh(sc);
+ if (!csqh)
+ return (USBD_NOMEM);
+ csqh->qh->hlink = bsqh;
+ csqh->qh->qh_hlink = bsqh->physaddr | UHCI_PTR_Q;
+ csqh->qh->qh_elink = UHCI_PTR_T;
+ sc->sc_ctl_start = sc->sc_ctl_end = csqh;
+
+ /*
+ * Make all (virtual) frame list pointers point to the interrupt
+ * queue heads and the interrupt queue heads at the control
+ * queue head and point the physical frame list to the virtual.
+ */
+ for(i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
+ std = uhci_alloc_std(sc);
+ sqh = uhci_alloc_sqh(sc);
+ if (!std || !sqh)
+ return (USBD_NOMEM);
+ std->td->link.sqh = sqh;
+ std->td->td_link = sqh->physaddr | UHCI_PTR_Q;
+ std->td->td_status = UHCI_TD_IOS; /* iso, inactive */
+ std->td->td_token = 0;
+ std->td->td_buffer = 0;
+ sqh->qh->hlink = csqh;
+ sqh->qh->qh_hlink = csqh->physaddr | UHCI_PTR_Q;
+ sqh->qh->elink = 0;
+ sqh->qh->qh_elink = UHCI_PTR_T;
+ sc->sc_vframes[i].htd = std;
+ sc->sc_vframes[i].etd = std;
+ sc->sc_vframes[i].hqh = sqh;
+ sc->sc_vframes[i].eqh = sqh;
+ for (j = i;
+ j < UHCI_FRAMELIST_COUNT;
+ j += UHCI_VFRAMELIST_COUNT)
+ sc->sc_pframes[j] = std->physaddr;
+ }
+
+ LIST_INIT(&sc->sc_intrhead);
+
+ /* Set up the bus struct. */
+ sc->sc_bus.open_pipe = uhci_open;
+ sc->sc_bus.pipe_size = sizeof(struct uhci_pipe);
+ sc->sc_bus.do_poll = uhci_poll;
+
+ DPRINTFN(1,("uhci_init: enabling\n"));
+ UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE |
+ UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */
+
+ uhci_run(sc, 1); /* and here we go... */
+ return (USBD_NORMAL_COMPLETION);
+}
+
+#ifdef USB_DEBUG
+static void
+uhci_dumpregs(sc)
+ uhci_softc_t *sc;
+{
+ DEVICE_MSG(sc->sc_bus.bdev,("regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, flbase=%08x, sof=%04x, portsc1=%04x, portsc2=%04x\n",
+ UREAD2(sc, UHCI_CMD),
+ UREAD2(sc, UHCI_STS),
+ UREAD2(sc, UHCI_INTR),
+ UREAD2(sc, UHCI_FRNUM),
+ UREAD2(sc, UHCI_FLBASEADDR),
+ UREAD2(sc, UHCI_SOF),
+ UREAD2(sc, UHCI_PORTSC1),
+ UREAD2(sc, UHCI_PORTSC2)));
+}
+
+int uhci_longtd = 1;
+
+void
+uhci_dump_td(p)
+ uhci_soft_td_t *p;
+{
+ printf("TD(%p) at %08lx = 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",
+ p, (long)p->physaddr,
+ (long)p->td->td_link,
+ (long)p->td->td_status,
+ (long)p->td->td_token,
+ (long)p->td->td_buffer);
+ if (uhci_longtd)
+#if defined(__NetBSD__)
+ printf(" %b %b,errcnt=%d,actlen=%d pid=%02x,addr=%d,endpt=%d,D=%d,maxlen=%d\n",
+ (long)p->td->td_link,
+ "\20\1T\2Q\3VF",
+ (long)p->td->td_status,
+ "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD",
+ UHCI_TD_GET_ERRCNT(p->td->td_status),
+ UHCI_TD_GET_ACTLEN(p->td->td_status),
+ UHCI_TD_GET_PID(p->td->td_token),
+ UHCI_TD_GET_DEVADDR(p->td->td_token),
+ UHCI_TD_GET_ENDPT(p->td->td_token),
+ UHCI_TD_GET_DT(p->td->td_token),
+ UHCI_TD_GET_MAXLEN(p->td->td_token));
+#elif defined(__FreeBSD__)
+ printf(" Link=0x%08lx,Status=0x%08lx\n errcnt=%d,actlen=%d,pid=%02x,addr=%d,endpt=%d,D=%d,maxlen=%d\n",
+ (long)p->td->td_link,
+ (long)p->td->td_status,
+ UHCI_TD_GET_ERRCNT(p->td->td_status),
+ UHCI_TD_GET_ACTLEN(p->td->td_status),
+ UHCI_TD_GET_PID(p->td->td_token),
+ UHCI_TD_GET_DEVADDR(p->td->td_token),
+ UHCI_TD_GET_ENDPT(p->td->td_token),
+ UHCI_TD_GET_DT(p->td->td_token),
+ UHCI_TD_GET_MAXLEN(p->td->td_token));
+#endif
+
+}
+
+void
+uhci_dump_qh(p)
+ uhci_soft_qh_t *p;
+{
+ printf("QH(%p) at %08x: hlink=%08x elink=%08x\n", p, (int)p->physaddr,
+ p->qh->qh_hlink, p->qh->qh_elink);
+}
+
+
+#if 0
+void
+uhci_dump()
+{
+ uhci_softc_t *sc = uhci;
+
+ uhci_dumpregs(sc);
+ printf("intrs=%d\n", sc->sc_intrs);
+ printf("framelist[i].link = %08x\n", sc->sc_framelist[0].link);
+ uhci_dump_qh(sc->sc_ctl_start->qh->hlink);
+}
+#endif
+
+void
+uhci_dump_tds(std)
+ uhci_soft_td_t *std;
+{
+ uhci_soft_td_t *p;
+
+ for(p = std; p; p = p->td->link.std)
+ uhci_dump_td(p);
+}
+#endif
+
+/*
+ * This routine is executed periodically and simulates interrupts
+ * from the root controller interrupt pipe for port status change.
+ */
+void
+uhci_timo(addr)
+ void *addr;
+{
+ usbd_request_handle reqh = addr;
+ usbd_pipe_handle pipe = reqh->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ int s;
+ u_char *p;
+
+ DPRINTFN(15, ("uhci_timo\n"));
+
+ p = KERNADDR(&upipe->u.intr.datadma);
+ p[0] = 0;
+ if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC))
+ p[0] |= 1<<1;
+ if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC))
+ p[0] |= 1<<2;
+ if (p[0] != 0) {
+ reqh->actlen = 1;
+ reqh->status = USBD_NORMAL_COMPLETION;
+ s = splusb();
+ reqh->xfercb(reqh);
+ splx(s);
+ }
+ if (reqh->pipe->intrreqh == reqh) {
+#if defined(__NetBSD__)
+ timeout(uhci_timo, reqh, sc->sc_ival);
+#elif defined(__FreeBSD__)
+ /* To avoid race conditions we first initialise the struct
+ * before we use it. The timeout might happen between the
+ * setting of the timeout and the setting of callout_handler
+ */
+ callout_handle_init(&reqh->callout_handler);
+ reqh->callout_handler = timeout(uhci_timo, reqh, sc->sc_ival);
+#endif
+ } else {
+ usb_freemem(sc->sc_dmatag, &upipe->u.intr.datadma);
+ }
+}
+
+
+void
+uhci_lock_frames(sc)
+ uhci_softc_t *sc;
+{
+ int s = splusb();
+ while (sc->sc_vflock) {
+ sc->sc_vflock |= UHCI_WANT_LOCK;
+ tsleep(&sc->sc_vflock, PRIBIO, "uhcqhl", 0);
+ }
+ sc->sc_vflock = UHCI_HAS_LOCK;
+ splx(s);
+}
+
+void
+uhci_unlock_frames(sc)
+ uhci_softc_t *sc;
+{
+ int s = splusb();
+ sc->sc_vflock &= ~UHCI_HAS_LOCK;
+ if (sc->sc_vflock & UHCI_WANT_LOCK)
+ wakeup(&sc->sc_vflock);
+ splx(s);
+}
+
+/*
+ * Allocate an interrupt information struct. A free list is kept
+ * for fast allocation.
+ */
+uhci_intr_info_t *
+uhci_alloc_intr_info(sc)
+ uhci_softc_t *sc;
+{
+ uhci_intr_info_t *ii;
+
+ ii = LIST_FIRST(&uhci_ii_free);
+ if (ii)
+ LIST_REMOVE(ii, list);
+ else {
+ ii = malloc(sizeof(uhci_intr_info_t), M_USBDEV, M_NOWAIT);
+ }
+ ii->sc = sc;
+ return ii;
+}
+
+void
+uhci_free_intr_info(ii)
+ uhci_intr_info_t *ii;
+{
+ LIST_INSERT_HEAD(&uhci_ii_free, ii, list); /* and put on free list */
+}
+
+/* Add control QH, called at splusb(). */
+void
+uhci_add_ctrl(sc, sqh)
+ uhci_softc_t *sc;
+ uhci_soft_qh_t *sqh;
+{
+ uhci_qh_t *eqh;
+
+ DPRINTFN(10, ("uhci_add_ctrl: sqh=%p\n", sqh));
+ eqh = sc->sc_ctl_end->qh;
+ sqh->qh->hlink = eqh->hlink;
+ sqh->qh->qh_hlink = eqh->qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q;
+ sc->sc_ctl_end = sqh;
+}
+
+/* Remove control QH, called at splusb(). */
+void
+uhci_remove_ctrl(sc, sqh)
+ uhci_softc_t *sc;
+ uhci_soft_qh_t *sqh;
+{
+ uhci_soft_qh_t *pqh;
+
+ DPRINTFN(10, ("uhci_remove_ctrl: sqh=%p\n", sqh));
+ for (pqh = sc->sc_ctl_start; pqh->qh->hlink != sqh; pqh=pqh->qh->hlink)
+#if defined(DIAGNOSTIC) || defined(USB_DEBUG)
+ if (pqh->qh->qh_hlink & UHCI_PTR_T) {
+ printf("uhci_remove_ctrl: QH not found\n");
+ return;
+ }
+#else
+ ;
+#endif
+ pqh->qh->hlink = sqh->qh->hlink;
+ pqh->qh->qh_hlink = sqh->qh->qh_hlink;
+ if (sc->sc_ctl_end == sqh)
+ sc->sc_ctl_end = pqh;
+}
+
+/* Add bulk QH, called at splusb(). */
+void
+uhci_add_bulk(sc, sqh)
+ uhci_softc_t *sc;
+ uhci_soft_qh_t *sqh;
+{
+ uhci_qh_t *eqh;
+
+ DPRINTFN(10, ("uhci_add_bulk: sqh=%p\n", sqh));
+ eqh = sc->sc_bulk_end->qh;
+ sqh->qh->hlink = eqh->hlink;
+ sqh->qh->qh_hlink = eqh->qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q;
+ sc->sc_bulk_end = sqh;
+}
+
+/* Remove bulk QH, called at splusb(). */
+void
+uhci_remove_bulk(sc, sqh)
+ uhci_softc_t *sc;
+ uhci_soft_qh_t *sqh;
+{
+ uhci_soft_qh_t *pqh;
+
+ DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh));
+ for (pqh = sc->sc_bulk_start; pqh->qh->hlink != sqh; pqh=pqh->qh->hlink)
+#if defined(DIAGNOSTIC) || defined(USB_DEBUG)
+ if (pqh->qh->qh_hlink & UHCI_PTR_T) {
+ printf("uhci_remove_bulk: QH not found\n");
+ return;
+ }
+#else
+ ;
+#endif
+ pqh->qh->hlink = sqh->qh->hlink;
+ pqh->qh->qh_hlink = sqh->qh->qh_hlink;
+ if (sc->sc_bulk_end == sqh)
+ sc->sc_bulk_end = pqh;
+}
+
+int
+uhci_intr(p)
+ void *p;
+{
+ uhci_softc_t *sc = p;
+ int status, ret;
+ uhci_intr_info_t *ii;
+
+ sc->sc_intrs++;
+#if defined(USB_DEBUG)
+ if (uhcidebug > 9)
+ uhci_dumpregs(sc);
+#endif
+ status = UREAD2(sc, UHCI_STS);
+ ret = 0;
+ if (status & UHCI_STS_USBINT) {
+ UWRITE2(sc, UHCI_STS, UHCI_STS_USBINT); /* acknowledge */
+ ret = 1;
+ }
+ if (status & UHCI_STS_USBEI) {
+ UWRITE2(sc, UHCI_STS, UHCI_STS_USBEI); /* acknowledge */
+ ret = 1;
+ }
+ if (status & UHCI_STS_RD) {
+ UWRITE2(sc, UHCI_STS, UHCI_STS_RD); /* acknowledge */
+ DEVICE_MSG(sc->sc_bus.bdev, ("resume detect\n"));
+ ret = 1;
+ }
+ if (status & UHCI_STS_HSE) {
+ UWRITE2(sc, UHCI_STS, UHCI_STS_HSE); /* acknowledge */
+ DEVICE_MSG(sc->sc_bus.bdev, ("Host System Error\n"));
+ ret = 1;
+ }
+ if (status & UHCI_STS_HCPE) {
+ UWRITE2(sc, UHCI_STS, UHCI_STS_HCPE); /* acknowledge */
+ DEVICE_MSG(sc->sc_bus.bdev, ("Host System Error\n"));
+ ret = 1;
+ }
+ if (status & UHCI_STS_HCH)
+ DEVICE_ERROR(sc->sc_bus.bdev, ("controller halted\n"));
+ if (!ret)
+ return 0;
+
+ /*
+ * Interrupts on UHCI really suck. When the host controller
+ * interrupts because a transfer is completed there is no
+ * way of knowing which transfer it was. You can scan down
+ * the TDs and QHs of the previous frame to limit the search,
+ * but that assumes that the interrupt was not delayed by more
+ * than 1 ms, which may not always be true (e.g. after debug
+ * output on a slow console).
+ * We scan all interrupt descriptors to see if any have
+ * completed.
+ */
+ for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list))
+ uhci_check_intr(sc, ii);
+
+ DPRINTFN(10, ("uhci_intr: exit\n"));
+ return 1;
+}
+
+/* Check for an interrupt. */
+void
+uhci_check_intr(sc, ii)
+ uhci_softc_t *sc;
+ uhci_intr_info_t *ii;
+{
+ struct uhci_pipe *upipe;
+ uhci_soft_td_t *std, *lstd;
+
+ DPRINTFN(15, ("uhci_check_intr: ii=%p\n", ii));
+#ifdef DIAGNOSTIC
+ if (!ii) {
+ printf("uhci_check_intr: no ii? %p\n", ii);
+ return;
+ }
+#endif
+ if (!ii->stdstart)
+ return;
+ lstd = ii->stdend;
+#ifdef DIAGNOSTIC
+ if (!lstd) {
+ printf("uhci_check_intr: std==0\n");
+ return;
+ }
+#endif
+ /* If the last TD is still active the whole transfer probably is. */
+ if (lstd->td->td_status & UHCI_TD_ACTIVE) {
+ DPRINTFN(15, ("uhci_check_intr: active ii=%p\n", ii));
+ for (std = ii->stdstart; std != lstd; std = std->td->link.std)
+ if (std->td->td_status & UHCI_TD_STALLED)
+ goto done;
+ DPRINTFN(15, ("uhci_check_intr: ii=%p still active\n", ii));
+ return;
+ }
+ done:
+ upipe = (struct uhci_pipe *)ii->reqh->pipe;
+ upipe->pipe.endpoint->toggle = upipe->newtoggle;
+ uhci_ii_done(ii, 0);
+#if defined(__NetBSD__)
+ untimeout(uhci_timeout, ii);
+#elif defined(__FreeBSD__)
+ untimeout(uhci_timeout, ii, ii->callout_handler);
+#endif
+}
+
+void
+uhci_ii_done(ii, timo)
+ uhci_intr_info_t *ii;
+ int timo;
+{
+ usbd_request_handle reqh = ii->reqh;
+ uhci_soft_td_t *std;
+ u_int32_t tst;
+ int len, status;
+
+ DPRINTFN(10, ("uhci_ii_done: ii=%p ready %d\n", ii, timo));
+
+#ifdef DIAGNOSTIC
+ {
+ int s = splhigh();
+ if (ii->isdone) {
+ printf("uhci_ii_done: is done!\n");
+ splx(s);
+ return;
+ }
+ ii->isdone = 1;
+ splx(s);
+ }
+#endif
+
+ /* The transfer is done, compute length and status. */
+ for (len = status = 0, std = ii->stdstart;
+ std != 0;
+ std = std->td->link.std) {
+ tst = std->td->td_status;
+ status |= tst;
+#ifdef USB_DEBUG
+ if ((tst & UHCI_TD_ERROR) && uhcidebug) {
+ printf("uhci_ii_done: intr error TD:\n");
+ uhci_dump_td(std);
+ }
+#endif
+ if (UHCI_TD_GET_PID(std->td->td_token) != UHCI_TD_PID_SETUP)
+ len += UHCI_TD_GET_ACTLEN(tst);
+ }
+ status &= UHCI_TD_ERROR;
+ /* NWH wrong func name also below, 'uhci_intr' 3 times
+ DPRINTFN(10, ("uhci_check_intr: len=%d, status=0x%x\n", len, status));
+ */
+ DPRINTFN(10, ("uhci_ii_done: len=%d\n", len));
+ if (status != 0) {
+#if defined(__NetBSD__)
+ DPRINTFN(-1+(status==UHCI_TD_STALLED),
+ ("uhci_ii_done: error, status 0x%b\n", (long)status,
+ "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27STALLED\30ACTIVE"));
+#elif defined(__FreeBSD__)
+ DPRINTFN(-1+(status==UHCI_TD_STALLED),
+ ("uhci_ii_done: error, status 0x%08lx\n", (long)status));
+#endif
+ if (status == UHCI_TD_STALLED)
+ reqh->status = USBD_STALLED;
+ else
+ reqh->status = USBD_IOERROR; /* more info XXX */
+ reqh->actlen = 0;
+ } else {
+ reqh->status = USBD_NORMAL_COMPLETION;
+ reqh->actlen = len;
+ }
+ if (timo) {
+ /* We got a timeout. Make sure transaction is not active. */
+ reqh->status = USBD_TIMEOUT;
+ for (std = ii->stdstart; std != 0; std = std->td->link.std)
+ std->td->td_status &= ~UHCI_TD_ACTIVE;
+ /* XXX should we wait 1 ms */
+ }
+ DPRINTFN(5, ("uhci_ii_done: calling handler ii=%p\n", ii));
+
+ switch (reqh->pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ uhci_ctrl_done(ii);
+ break;
+ case UE_ISOCHRONOUS:
+ printf("uhci_ii_done: ISO??\n");
+ break;
+ case UE_BULK:
+ uhci_bulk_done(ii);
+ break;
+ case UE_INTERRUPT:
+ uhci_intr_done(ii);
+ break;
+ }
+
+ /* And finally execute callback. */
+ reqh->xfercb(reqh);
+}
+
+void
+uhci_timeout(addr)
+ void *addr;
+{
+ uhci_intr_info_t *ii = addr;
+ int s;
+
+ DPRINTF(("uhci_timeout: ii=%p\n", ii));
+ s = splusb();
+ uhci_ii_done(ii, 1);
+ splx(s);
+}
+
+/*
+ * Wait here until controller claims to have an interrupt.
+ * Then call uhci_intr and return. Use timeout to avoid waiting
+ * too long.
+ */
+void
+uhci_waitintr(sc, reqh)
+ uhci_softc_t *sc;
+ usbd_request_handle reqh;
+{
+ int timo = reqh->timeout;
+ int usecs;
+ int hzs;
+
+ DPRINTFN(10,("uhci_waitintr: timout = %ds\n", timo));
+
+ reqh->status = USBD_IN_PROGRESS;
+ for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 100000) {
+ /* NWH replaced by usbd_delay_ms
+ delay(1000);
+ NWH and descreased frequency from 1ms to 100ms, see also usecs -=...
+ */
+ usbd_delay_ms(&(sc->sc_bus), 100);
+ /* NWH disabled
+ DPRINTFN(10,("uhci_waitintr: 0x%04x\n", UREAD2(sc, UHCI_STS)));
+ */
+ if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT) {
+ uhci_intr(sc);
+ if (reqh->status != USBD_IN_PROGRESS)
+ return;
+ }
+ }
+ reqh->status = USBD_TIMEOUT;
+ reqh->xfercb(reqh);
+}
+
+void
+uhci_poll(bus)
+ struct usbd_bus *bus;
+{
+ uhci_softc_t *sc = (uhci_softc_t *)bus;
+
+ if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT)
+ uhci_intr(sc);
+}
+
+#if 0
+void
+uhci_reset(p)
+ void *p;
+{
+ uhci_softc_t *sc = p;
+ int n;
+
+ UHCICMD(sc, UHCI_CMD_HCRESET);
+ /* The reset bit goes low when the controller is done. */
+ for (n = 0; n < UHCI_RESET_TIMEOUT &&
+ (UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET); n++)
+ delay(100);
+ if (n >= UHCI_RESET_TIMEOUT)
+ DEVICE_ERROR(sc->sc_bus.bdev, ("controller did not reset\n"));
+}
+#endif
+
+void
+uhci_run(sc, run)
+ uhci_softc_t *sc;
+ int run;
+{
+ int s, n, running;
+
+ run = run != 0;
+ s = splusb(); /* XXX really? */
+ running = !(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH);
+ if (run == running) {
+ splx(s);
+ return;
+ }
+ UWRITE2(sc, UHCI_CMD, run ? UHCI_CMD_RS : 0);
+ for(n = 0; n < 100; n++) {
+ running = !(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH);
+ /* return when we've entered the state we want */
+ if (run == running) {
+ splx(s);
+ return;
+ }
+ }
+ splx(s);
+ DEVICE_ERROR(sc->sc_bus.bdev, ("cannot %s\n", (run ? "start" : "stop")));
+}
+
+/*
+ * Memory management routines.
+ * uhci_alloc_std allocates TDs
+ * uhci_alloc_sqh allocates QHs
+ * These two routines do their own free list management,
+ * partly for speed, partly because allocating DMAable memory
+ * has page size granularaity so much memory would be wasted if
+ * only one TD/QH (32 bytes) was placed in each alloacted chunk.
+ */
+
+uhci_soft_td_t *
+uhci_alloc_std(sc)
+ uhci_softc_t *sc;
+{
+ uhci_soft_td_t *std;
+ usbd_status r;
+ int i;
+ usb_dma_t dma;
+
+ if (!sc->sc_freetds) {
+ DPRINTFN(2,("uhci_alloc_std: allocating chunk\n"));
+ std = malloc(sizeof(uhci_soft_td_t) * UHCI_TD_CHUNK,
+ M_USBDEV, M_NOWAIT);
+ if (!std)
+ return 0;
+ r = usb_allocmem(sc->sc_dmatag, UHCI_TD_SIZE * UHCI_TD_CHUNK,
+ UHCI_TD_ALIGN, &dma);
+ if (r != USBD_NORMAL_COMPLETION) {
+ free(std, M_USBDEV);
+ return 0;
+ }
+ for(i = 0; i < UHCI_TD_CHUNK; i++, std++) {
+ std->physaddr = DMAADDR(&dma) +
+ i * UHCI_TD_SIZE;
+ std->td = (uhci_td_t *)
+ ((char *)KERNADDR(&dma) + i * UHCI_TD_SIZE);
+ std->td->link.std = sc->sc_freetds;
+ sc->sc_freetds = std;
+ }
+ }
+ std = sc->sc_freetds;
+ sc->sc_freetds = std->td->link.std;
+ memset(std->td, 0, UHCI_TD_SIZE);
+ return std;
+}
+
+void
+uhci_free_std(sc, std)
+ uhci_softc_t *sc;
+ uhci_soft_td_t *std;
+{
+#ifdef DIAGNOSTIC
+#define TD_IS_FREE 0x12345678
+ if (std->td->td_token == TD_IS_FREE) {
+ printf("uhci_free_std: freeing free TD %p\n", std);
+ return;
+ }
+ std->td->td_token = TD_IS_FREE;
+#endif
+ std->td->link.std = sc->sc_freetds;
+ sc->sc_freetds = std;
+}
+
+uhci_soft_qh_t *
+uhci_alloc_sqh(sc)
+ uhci_softc_t *sc;
+{
+ uhci_soft_qh_t *sqh;
+ usbd_status r;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (!sc->sc_freeqhs) {
+ DPRINTFN(2, ("uhci_alloc_sqh: allocating chunk\n"));
+ sqh = malloc(sizeof(uhci_soft_qh_t) * UHCI_QH_CHUNK,
+ M_USBDEV, M_NOWAIT);
+ if (!sqh)
+ return 0;
+ r = usb_allocmem(sc->sc_dmatag, UHCI_QH_SIZE * UHCI_QH_CHUNK,
+ UHCI_QH_ALIGN, &dma);
+ if (r != USBD_NORMAL_COMPLETION) {
+ free(sqh, M_USBDEV);
+ return 0;
+ }
+ for(i = 0; i < UHCI_QH_CHUNK; i++, sqh++) {
+ offs = i * UHCI_QH_SIZE;
+ sqh->physaddr = DMAADDR(&dma) + offs;
+ sqh->qh = (uhci_qh_t *)
+ ((char *)KERNADDR(&dma) + offs);
+ sqh->qh->hlink = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+ }
+ }
+ sqh = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh->qh->hlink;
+ memset(sqh->qh, 0, UHCI_QH_SIZE);
+ return sqh;
+}
+
+void
+uhci_free_sqh(sc, sqh)
+ uhci_softc_t *sc;
+ uhci_soft_qh_t *sqh;
+{
+ sqh->qh->hlink = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+}
+
+/*
+ * Enter a list of transfers onto a control queue.
+ * Called at splusb()
+ */
+void
+uhci_enter_ctl_q(sc, sqh, ii)
+ uhci_softc_t *sc;
+ uhci_soft_qh_t *sqh;
+ uhci_intr_info_t *ii;
+{
+ DPRINTFN(5, ("uhci_enter_ctl_q: sqh=%p\n", sqh));
+
+}
+
+void
+uhci_free_std_chain(sc, std, stdend)
+ uhci_softc_t *sc;
+ uhci_soft_td_t *std;
+ uhci_soft_td_t *stdend;
+{
+ uhci_soft_td_t *p;
+
+ for (; std != stdend; std = p) {
+ p = std->td->link.std;
+ uhci_free_std(sc, std);
+ }
+}
+
+usbd_status
+uhci_alloc_std_chain(upipe, sc, len, rd, dma, sp, ep)
+ struct uhci_pipe *upipe;
+ uhci_softc_t *sc;
+ int len, rd;
+ usb_dma_t *dma;
+ uhci_soft_td_t **sp, **ep;
+{
+ uhci_soft_td_t *p, *lastp;
+ uhci_physaddr_t lastlink;
+ u_int32_t ls;
+ int i, ntd, l, tog, maxp;
+ int addr = upipe->pipe.device->address;
+ int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+
+ DPRINTFN(15, ("uhci_alloc_std_chain: len=%d\n", len));
+ if (len == 0) {
+ *sp = *ep = 0;
+ printf("uhci_alloc_std_chain: len=0\n");
+ return (USBD_NORMAL_COMPLETION);
+ }
+ ls = upipe->pipe.device->lowspeed ? UHCI_TD_LS : 0;
+ maxp = UGETW(upipe->pipe.endpoint->edesc->wMaxPacketSize);
+ if (maxp == 0) {
+ printf("uhci_alloc_std_chain: maxp=0\n");
+ return (USBD_INVAL);
+ }
+ ntd = (len + maxp - 1) / maxp;
+ tog = upipe->pipe.endpoint->toggle;
+ if (ntd % 2 == 0)
+ tog ^= 1;
+ upipe->newtoggle = tog ^ 1;
+ lastp = 0;
+ lastlink = UHCI_PTR_T;
+ ntd--;
+ for (i = ntd; i >= 0; i--) {
+ p = uhci_alloc_std(sc);
+ if (!p) {
+ uhci_free_std_chain(sc, lastp, 0);
+ return (USBD_NOMEM);
+ }
+ p->td->link.std = lastp;
+ p->td->td_link = lastlink;
+ lastp = p;
+ lastlink = p->physaddr;
+ p->td->td_status = UHCI_TD_SET_ERRCNT(2) | ls | UHCI_TD_ACTIVE;
+ if (i == ntd) {
+ /* last TD */
+ l = len % maxp;
+ if (l == 0) l = maxp;
+ *ep = p;
+ } else
+ l = maxp;
+ p->td->td_token =
+ rd ? UHCI_TD_IN (l, endpt, addr, tog) :
+ UHCI_TD_OUT(l, endpt, addr, tog);
+ p->td->td_buffer = DMAADDR(dma) + i * maxp;
+ tog ^= 1;
+ }
+ *sp = lastp;
+ /*upipe->pipe.endpoint->toggle = tog;*/
+ DPRINTFN(10, ("uhci_alloc_std_chain: oldtog=%d newtog=%d\n",
+ upipe->pipe.endpoint->toggle, upipe->newtoggle));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+uhci_device_bulk_transfer(reqh)
+ usbd_request_handle reqh;
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ uhci_intr_info_t *ii = upipe->iinfo;
+ uhci_soft_td_t *xfer, *xferend;
+ uhci_soft_qh_t *sqh;
+ usb_dma_t *dmap;
+ usbd_status r;
+ int len, isread;
+ int s;
+
+ DPRINTFN(3, ("uhci_device_bulk_transfer: reqh=%p buf=%p len=%d flags=%d\n",
+ reqh, reqh->buffer, reqh->length, reqh->flags));
+
+ if (reqh->isreq)
+ panic("uhci_device_bulk_transfer: a request\n");
+
+ len = reqh->length;
+ dmap = &upipe->u.bulk.datadma;
+ isread = reqh->pipe->endpoint->edesc->bEndpointAddress & UE_IN;
+ sqh = upipe->u.bulk.sqh;
+
+ upipe->u.bulk.isread = isread;
+ upipe->u.bulk.length = len;
+
+ r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto ret1;
+ r = uhci_alloc_std_chain(upipe, sc, len, isread,
+ dmap, &xfer, &xferend);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto ret2;
+ xferend->td->td_status |= UHCI_TD_IOC;
+
+ if (!isread && len != 0)
+ memcpy(KERNADDR(dmap), reqh->buffer, len);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ printf("uhci_device_bulk_transfer: xfer(1)\n");
+ uhci_dump_tds(xfer);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ ii->reqh = reqh;
+ ii->stdstart = xfer;
+ ii->stdend = xferend;
+#ifdef DIAGNOSTIC
+ ii->isdone = 0;
+#endif
+
+ sqh->qh->elink = xfer;
+ sqh->qh->qh_elink = xfer->physaddr;
+ sqh->intr_info = ii;
+
+ s = splusb();
+ uhci_add_bulk(sc, sqh);
+ LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list);
+
+ if (reqh->timeout && !sc->sc_bus.use_polling)
+ timeout(uhci_timeout, ii, MS_TO_TICKS(reqh->timeout));
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ printf("uhci_device_bulk_transfer: xfer(2)\n");
+ uhci_dump_tds(xfer);
+ }
+#endif
+
+ return (USBD_IN_PROGRESS);
+
+ ret2:
+ if (len != 0)
+ usb_freemem(sc->sc_dmatag, dmap);
+ ret1:
+ return (r);
+}
+
+/* Abort a device bulk request. */
+void
+uhci_device_bulk_abort(reqh)
+ usbd_request_handle reqh;
+{
+ /* XXX inactivate */
+ usbd_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is finished */
+ /* XXX call done */
+}
+
+/* Close a device bulk pipe. */
+void
+uhci_device_bulk_close(pipe)
+ usbd_pipe_handle pipe;
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+
+ uhci_free_sqh(sc, upipe->u.bulk.sqh);
+ uhci_free_intr_info(upipe->iinfo);
+ /* XXX free other resources */
+}
+
+usbd_status
+uhci_device_ctrl_transfer(reqh)
+ usbd_request_handle reqh;
+{
+ uhci_softc_t *sc = (uhci_softc_t *)reqh->pipe->device->bus;
+ usbd_status r;
+
+ if (!reqh->isreq)
+ panic("uhci_device_ctrl_transfer: not a request\n");
+
+ r = uhci_device_request(reqh);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+
+ if (sc->sc_bus.use_polling)
+ uhci_waitintr(sc, reqh);
+ return (USBD_IN_PROGRESS);
+}
+
+usbd_status
+uhci_device_intr_transfer(reqh)
+ usbd_request_handle reqh;
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ uhci_intr_info_t *ii = upipe->iinfo;
+ uhci_soft_td_t *xfer, *xferend;
+ uhci_soft_qh_t *sqh;
+ usb_dma_t *dmap;
+ usbd_status r;
+ int len, i;
+ int s;
+
+ DPRINTFN(3, ("uhci_device_intr_transfer: reqh=%p buf=%p len=%d flags=%d\n",
+ reqh, reqh->buffer, reqh->length, reqh->flags));
+
+ if (reqh->isreq)
+ panic("uhci_device_intr_transfer: a request\n");
+
+ len = reqh->length;
+ dmap = &upipe->u.intr.datadma;
+ if (len == 0)
+ return (USBD_INVAL); /* XXX should it be? */
+
+ r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto ret1;
+ r = uhci_alloc_std_chain(upipe, sc, len, 1, dmap, &xfer, &xferend);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto ret2;
+ xferend->td->td_status |= UHCI_TD_IOC;
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ printf("uhci_device_intr_transfer: xfer(1)\n");
+ uhci_dump_tds(xfer);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ s = splusb();
+ /* Set up interrupt info. */
+ ii->reqh = reqh;
+ ii->stdstart = xfer;
+ ii->stdend = xferend;
+#ifdef DIAGNOSTIC
+ ii->isdone = 0;
+#endif
+
+DPRINTFN(10,("uhci_device_intr_transfer: qhs[0]=%p\n", upipe->u.intr.qhs[0]));
+ for (i = 0; i < upipe->u.intr.npoll; i++) {
+ sqh = upipe->u.intr.qhs[i];
+ sqh->qh->elink = xfer;
+ sqh->qh->qh_elink = xfer->physaddr;
+ }
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ printf("uhci_device_intr_transfer: xfer(2)\n");
+ uhci_dump_tds(xfer);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ return (USBD_IN_PROGRESS);
+
+ ret2:
+ if (len != 0)
+ usb_freemem(sc->sc_dmatag, dmap);
+ ret1:
+ return (r);
+}
+
+/* Abort a device control request. */
+void
+uhci_device_ctrl_abort(reqh)
+ usbd_request_handle reqh;
+{
+ /* XXX inactivate */
+ usbd_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is finished */
+ /* XXX call done */
+}
+
+/* Close a device control pipe. */
+void
+uhci_device_ctrl_close(pipe)
+ usbd_pipe_handle pipe;
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+
+ uhci_free_intr_info(upipe->iinfo);
+ /* XXX free other resources */
+}
+
+/* Abort a device interrupt request. */
+void
+uhci_device_intr_abort(reqh)
+ usbd_request_handle reqh;
+{
+ struct uhci_pipe *upipe;
+
+ DPRINTFN(1, ("uhci_device_intr_abort: reqh=%p\n", reqh));
+ /* XXX inactivate */
+ usbd_delay_ms(reqh->pipe->device->bus, 2); /* make sure it is finished */
+ if (reqh->pipe->intrreqh == reqh) {
+ DPRINTF(("uhci_device_intr_abort: remove\n"));
+ reqh->pipe->intrreqh = 0;
+ upipe = (struct uhci_pipe *)reqh->pipe;
+ uhci_intr_done(upipe->u.intr.qhs[0]->intr_info);
+ }
+}
+
+/* Close a device interrupt pipe. */
+void
+uhci_device_intr_close(pipe)
+ usbd_pipe_handle pipe;
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+ int i, s, npoll;
+
+ upipe->iinfo->stdstart = 0; /* inactive */
+
+ /* Unlink descriptors from controller data structures. */
+ npoll = upipe->u.intr.npoll;
+ uhci_lock_frames(sc);
+ for (i = 0; i < npoll; i++)
+ uhci_remove_intr(sc, upipe->u.intr.qhs[i]->pos,
+ upipe->u.intr.qhs[i]);
+ uhci_unlock_frames(sc);
+
+ /*
+ * We now have to wait for any activity on the physical
+ * descriptors to stop.
+ */
+ usbd_delay_ms(&sc->sc_bus, 2);
+
+ for(i = 0; i < npoll; i++)
+ uhci_free_sqh(sc, upipe->u.intr.qhs[i]);
+ free(upipe->u.intr.qhs, M_USB);
+
+ s = splusb();
+ LIST_REMOVE(upipe->iinfo, list); /* remove from active list */
+ splx(s);
+ uhci_free_intr_info(upipe->iinfo);
+
+ /* XXX free other resources */
+}
+
+usbd_status
+uhci_device_request(reqh)
+ usbd_request_handle reqh;
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
+ usb_device_request_t *req = &reqh->request;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ int addr = dev->address;
+ int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+ uhci_intr_info_t *ii = upipe->iinfo;
+ uhci_soft_td_t *setup, *xfer, *stat, *next, *xferend;
+ uhci_soft_qh_t *sqh;
+ usb_dma_t *dmap;
+ int len;
+ u_int32_t ls;
+ usbd_status r;
+ int isread;
+ int s;
+
+ DPRINTFN(1,("uhci_device_control type=0x%02x, request=0x%02x, wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n",
+ req->bmRequestType, req->bRequest, UGETW(req->wValue),
+ UGETW(req->wIndex), UGETW(req->wLength),
+ addr, endpt));
+
+ ls = dev->lowspeed ? UHCI_TD_LS : 0;
+ isread = req->bmRequestType & UT_READ;
+ len = UGETW(req->wLength);
+
+ setup = upipe->u.ctl.setup;
+ stat = upipe->u.ctl.stat;
+ sqh = upipe->u.ctl.sqh;
+ dmap = &upipe->u.ctl.datadma;
+
+ /* Set up data transaction */
+ if (len != 0) {
+ r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto ret1;
+ upipe->pipe.endpoint->toggle = 1;
+ r = uhci_alloc_std_chain(upipe, sc, len, isread,
+ dmap, &xfer, &xferend);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto ret2;
+ next = xfer;
+ xferend->td->link.std = stat;
+ xferend->td->td_link = stat->physaddr;
+ } else {
+ xfer = 0;
+ next = stat;
+ }
+ upipe->u.ctl.length = len;
+ upipe->u.ctl.xferend = xferend;
+
+ memcpy(KERNADDR(&upipe->u.ctl.reqdma), req, sizeof *req);
+ if (!isread && len != 0)
+ memcpy(KERNADDR(dmap), reqh->buffer, len);
+
+ setup->td->link.std = next;
+ setup->td->td_link = next->physaddr;
+ setup->td->td_status = UHCI_TD_SET_ERRCNT(2) | ls | UHCI_TD_ACTIVE;
+ setup->td->td_token = UHCI_TD_SETUP(sizeof *req, endpt, addr);
+ setup->td->td_buffer = DMAADDR(&upipe->u.ctl.reqdma);
+
+ stat->td->link.std = 0;
+ stat->td->td_link = UHCI_PTR_T;
+ stat->td->td_status = UHCI_TD_SET_ERRCNT(2) | ls |
+ UHCI_TD_ACTIVE | UHCI_TD_IOC;
+ stat->td->td_token =
+ isread ? UHCI_TD_OUT(0, endpt, addr, 1) :
+ UHCI_TD_IN (0, endpt, addr, 1);
+ stat->td->td_buffer = 0;
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 20) {
+ printf("uhci_device_request: setup\n");
+ uhci_dump_td(setup);
+ printf("uhci_device_request: stat\n");
+ uhci_dump_td(stat);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ ii->reqh = reqh;
+ ii->stdstart = setup;
+ ii->stdend = stat;
+#ifdef DIAGNOSTIC
+ ii->isdone = 0;
+#endif
+
+ sqh->qh->elink = setup;
+ sqh->qh->qh_elink = setup->physaddr;
+ sqh->intr_info = ii;
+
+ s = splusb();
+ uhci_add_ctrl(sc, sqh);
+ LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list);
+#ifdef USB_DEBUG
+ if (uhcidebug > 12) {
+ uhci_soft_td_t *std;
+ uhci_soft_qh_t *xqh;
+ uhci_soft_qh_t *sxqh;
+ int maxqh = 0;
+ uhci_physaddr_t link;
+ printf("uhci_enter_ctl_q: follow from [0]\n");
+ for (std = sc->sc_vframes[0].htd, link = 0;
+ (link & UHCI_PTR_Q) == 0;
+ std = std->td->link.std) {
+ link = std->td->td_link;
+ uhci_dump_td(std);
+ }
+ for (sxqh = xqh = (uhci_soft_qh_t *)std;
+ xqh;
+ /* FIXME NWH seems to be a circular list ??
+ * checking for beginning of list end of list
+ * and printing a maximum of 5 QH's ...
+ xqh = xqh->qh->hlink)
+ */
+ xqh = (maxqh++ == 5 || xqh->qh->hlink==sxqh || xqh->qh->hlink==xqh? NULL : xqh->qh->hlink)) {
+ uhci_dump_qh(xqh);
+ uhci_dump_qh(sxqh);
+ }
+ printf("Enqueued QH:\n");
+ uhci_dump_qh(sqh);
+ uhci_dump_tds(sqh->qh->elink);
+ }
+#endif
+ if (reqh->timeout && !sc->sc_bus.use_polling)
+#if defined(__NetBSD__)
+ timeout(uhci_timeout, ii, MS_TO_TICKS(reqh->timeout));
+#elif defined(__FreeBSD__)
+ /* To avoid race conditions we first initialise the struct
+ * before we use it. The timeout may happen between the setting
+ * of the timeout and the setting of callout_handle
+ */
+ callout_handle_init(&ii->callout_handler);
+ ii->callout_handler = timeout(uhci_timeout, ii, MS_TO_TICKS(reqh->timeout));
+#endif
+ splx(s);
+
+ return (USBD_NORMAL_COMPLETION);
+
+ ret2:
+ if (len != 0)
+ usb_freemem(sc->sc_dmatag, dmap);
+ ret1:
+ return (r);
+}
+
+void
+uhci_intr_done(ii)
+ uhci_intr_info_t *ii;
+{
+ uhci_softc_t *sc = ii->sc;
+ usbd_request_handle reqh = ii->reqh;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
+ usb_dma_t *dma;
+ uhci_soft_qh_t *sqh;
+ int i, npoll;
+
+ DPRINTFN(5, ("uhci_intr_done: length=%d\n", reqh->actlen));
+
+ dma = &upipe->u.intr.datadma;
+ memcpy(reqh->buffer, KERNADDR(dma), reqh->actlen);
+ npoll = upipe->u.intr.npoll;
+ for(i = 0; i < npoll; i++) {
+ sqh = upipe->u.intr.qhs[i];
+ sqh->qh->elink = 0;
+ sqh->qh->qh_elink = UHCI_PTR_T;
+ }
+ uhci_free_std_chain(sc, ii->stdstart, 0);
+
+ /* XXX Wasteful. */
+ if (reqh->pipe->intrreqh == reqh) {
+ uhci_soft_td_t *xfer, *xferend;
+
+ /* This alloc cannot fail since we freed the chain above. */
+ uhci_alloc_std_chain(upipe, sc, reqh->length, 1, dma,
+ &xfer, &xferend);
+ xferend->td->td_status |= UHCI_TD_IOC;
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ printf("uhci_device_intr_done: xfer(1)\n");
+ uhci_dump_tds(xfer);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ ii->stdstart = xfer;
+ ii->stdend = xferend;
+#ifdef DIAGNOSTIC
+ ii->isdone = 0;
+#endif
+ for (i = 0; i < npoll; i++) {
+ sqh = upipe->u.intr.qhs[i];
+ sqh->qh->elink = xfer;
+ sqh->qh->qh_elink = xfer->physaddr;
+ }
+ } else {
+ usb_freemem(sc->sc_dmatag, dma);
+ ii->stdstart = 0; /* mark as inactive */
+ }
+}
+
+/* Deallocate request data structures */
+void
+uhci_ctrl_done(ii)
+ uhci_intr_info_t *ii;
+{
+ uhci_softc_t *sc = ii->sc;
+ usbd_request_handle reqh = ii->reqh;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
+ u_int len = upipe->u.ctl.length;
+ usb_dma_t *dma;
+ uhci_td_t *htd = ii->stdstart->td;
+
+#ifdef DIAGNOSTIC
+ if (!reqh->isreq)
+ panic("uhci_ctrl_done: not a request\n");
+#endif
+
+ LIST_REMOVE(ii, list); /* remove from active list */
+
+ uhci_remove_ctrl(sc, upipe->u.ctl.sqh);
+
+ if (len != 0) {
+ dma = &upipe->u.ctl.datadma;
+ if (reqh->request.bmRequestType & UT_READ)
+ memcpy(reqh->buffer, KERNADDR(dma), len);
+ uhci_free_std_chain(sc, htd->link.std, ii->stdend);
+ usb_freemem(sc->sc_dmatag, dma);
+ }
+ DPRINTFN(5, ("uhci_ctrl_done: length=%d\n", reqh->actlen));
+}
+
+/* Deallocate request data structures */
+void
+uhci_bulk_done(ii)
+ uhci_intr_info_t *ii;
+{
+ uhci_softc_t *sc = ii->sc;
+ usbd_request_handle reqh = ii->reqh;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)reqh->pipe;
+ u_int len = upipe->u.bulk.length;
+ usb_dma_t *dma;
+ uhci_td_t *htd = ii->stdstart->td;
+
+ LIST_REMOVE(ii, list); /* remove from active list */
+
+ uhci_remove_bulk(sc, upipe->u.bulk.sqh);
+
+ if (len != 0) {
+ dma = &upipe->u.bulk.datadma;
+ if (upipe->u.bulk.isread && len != 0)
+ memcpy(reqh->buffer, KERNADDR(dma), len);
+ uhci_free_std_chain(sc, htd->link.std, 0);
+ usb_freemem(sc->sc_dmatag, dma);
+ }
+ DPRINTFN(4, ("uhci_bulk_done: length=%d\n", reqh->actlen));
+ /* XXX compute new toggle */
+}
+
+/* Add interrupt QH, called with vflock. */
+void
+uhci_add_intr(sc, n, sqh)
+ uhci_softc_t *sc;
+ int n;
+ uhci_soft_qh_t *sqh;
+{
+ struct uhci_vframe *vf = &sc->sc_vframes[n];
+ uhci_qh_t *eqh;
+
+ DPRINTFN(4, ("uhci_add_intr: n=%d sqh=%p\n", n, sqh));
+ eqh = vf->eqh->qh;
+ sqh->qh->hlink = eqh->hlink;
+ sqh->qh->qh_hlink = eqh->qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh_hlink = sqh->physaddr | UHCI_PTR_Q;
+ vf->eqh = sqh;
+ vf->bandwidth++;
+}
+
+/* Remove interrupt QH, called with vflock. */
+void
+uhci_remove_intr(sc, n, sqh)
+ uhci_softc_t *sc;
+ int n;
+ uhci_soft_qh_t *sqh;
+{
+ struct uhci_vframe *vf = &sc->sc_vframes[n];
+ uhci_soft_qh_t *pqh;
+
+ DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", n, sqh));
+
+ for (pqh = vf->hqh; pqh->qh->hlink != sqh; pqh = pqh->qh->hlink)
+#if defined(DIAGNOSTIC) || defined(USB_DEBUG)
+ if (pqh->qh->qh_hlink & UHCI_PTR_T) {
+ printf("uhci_remove_intr: QH not found\n");
+ return;
+ }
+#else
+ ;
+#endif
+ pqh->qh->hlink = sqh->qh->hlink;
+ pqh->qh->qh_hlink = sqh->qh->qh_hlink;
+ if (vf->eqh == sqh)
+ vf->eqh = pqh;
+ vf->bandwidth--;
+}
+
+usbd_status
+uhci_device_setintr(sc, upipe, ival)
+ uhci_softc_t *sc;
+ struct uhci_pipe *upipe;
+ int ival;
+{
+ uhci_soft_qh_t *sqh;
+ int i, npoll, s;
+ u_int bestbw, bw, bestoffs, offs;
+
+ DPRINTFN(2, ("uhci_setintr: pipe=%p\n", upipe));
+ if (ival == 0) {
+ printf("uhci_setintr: 0 interval\n");
+ return (USBD_INVAL);
+ }
+
+ if (ival > UHCI_VFRAMELIST_COUNT)
+ ival = UHCI_VFRAMELIST_COUNT;
+ npoll = (UHCI_VFRAMELIST_COUNT + ival - 1) / ival;
+ DPRINTFN(2, ("uhci_setintr: ival=%d npoll=%d\n", ival, npoll));
+
+ upipe->u.intr.npoll = npoll;
+ upipe->u.intr.qhs =
+ malloc(npoll * sizeof(uhci_soft_qh_t *), M_USB, M_WAITOK);
+
+ /*
+ * Figure out which offset in the schedule that has most
+ * bandwidth left over.
+ */
+#define MOD(i) ((i) & (UHCI_VFRAMELIST_COUNT-1))
+ for (bestoffs = offs = 0, bestbw = ~0; offs < ival; offs++) {
+ for (bw = i = 0; i < npoll; i++)
+ bw += sc->sc_vframes[MOD(i * ival + offs)].bandwidth;
+ if (bw < bestbw) {
+ bestbw = bw;
+ bestoffs = offs;
+ }
+ }
+ DPRINTFN(1, ("uhci_setintr: bw=%d offs=%d\n", bestbw, bestoffs));
+
+ upipe->iinfo->stdstart = 0;
+ for(i = 0; i < npoll; i++) {
+ upipe->u.intr.qhs[i] = sqh = uhci_alloc_sqh(sc);
+ sqh->qh->elink = 0;
+ sqh->qh->qh_elink = UHCI_PTR_T;
+ sqh->pos = MOD(i * ival + bestoffs);
+ sqh->intr_info = upipe->iinfo;
+ }
+#undef MOD
+
+ s = splusb();
+ LIST_INSERT_HEAD(&sc->sc_intrhead, upipe->iinfo, list);
+ splx(s);
+
+ uhci_lock_frames(sc);
+ /* Enter QHs into the controller data structures. */
+ for(i = 0; i < npoll; i++)
+ uhci_add_intr(sc, upipe->u.intr.qhs[i]->pos,
+ upipe->u.intr.qhs[i]);
+ uhci_unlock_frames(sc);
+
+ DPRINTFN(5, ("uhci_setintr: returns %p\n", upipe));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Open a new pipe. */
+usbd_status
+uhci_open(pipe)
+ usbd_pipe_handle pipe;
+{
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
+ usbd_status r;
+
+ DPRINTFN(1, ("uhci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
+ pipe, pipe->device->address,
+ ed->bEndpointAddress, sc->sc_addr));
+ if (pipe->device->address == sc->sc_addr) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &uhci_root_ctrl_methods;
+ break;
+ case UE_IN | UHCI_INTR_ENDPT:
+ pipe->methods = &uhci_root_intr_methods;
+ break;
+ default:
+ return (USBD_INVAL);
+ }
+ } else {
+ upipe->iinfo = uhci_alloc_intr_info(sc);
+ if (upipe->iinfo == 0)
+ return (USBD_NOMEM);
+ switch (ed->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &uhci_device_ctrl_methods;
+ upipe->u.ctl.sqh = uhci_alloc_sqh(sc);
+ if (upipe->u.ctl.sqh == 0)
+ goto bad;
+ upipe->u.ctl.setup = uhci_alloc_std(sc);
+ if (upipe->u.ctl.setup == 0) {
+ uhci_free_sqh(sc, upipe->u.ctl.sqh);
+ goto bad;
+ }
+ upipe->u.ctl.stat = uhci_alloc_std(sc);
+ if (upipe->u.ctl.stat == 0) {
+ uhci_free_sqh(sc, upipe->u.ctl.sqh);
+ uhci_free_std(sc, upipe->u.ctl.setup);
+ goto bad;
+ }
+ r = usb_allocmem(sc->sc_dmatag,
+ sizeof(usb_device_request_t),
+ 0, &upipe->u.ctl.reqdma);
+ if (r != USBD_NORMAL_COMPLETION) {
+ uhci_free_sqh(sc, upipe->u.ctl.sqh);
+ uhci_free_std(sc, upipe->u.ctl.setup);
+ uhci_free_std(sc, upipe->u.ctl.stat);
+ goto bad;
+ }
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &uhci_device_intr_methods;
+ return (uhci_device_setintr(sc, upipe, ed->bInterval));
+ case UE_ISOCHRONOUS:
+ printf("uhci_open: iso not implemented\n");
+ return (USBD_XXX);
+ case UE_BULK:
+ pipe->methods = &uhci_device_bulk_methods;
+ upipe->u.bulk.sqh = uhci_alloc_sqh(sc);
+ if (upipe->u.bulk.sqh == 0)
+ goto bad;
+ break;
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ uhci_free_intr_info(upipe->iinfo);
+ return (USBD_NOMEM);
+}
+
+/*
+ * Data structures and routines to emulate the root hub.
+ */
+usb_device_descriptor_t uhci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x00, 0x01}, /* USB version */
+ UCLASS_HUB, /* class */
+ USUBCLASS_HUB, /* subclass */
+ 0, /* protocol */
+ 64, /* max packet */
+ {0},{0},{0x00,0x01}, /* device id */
+ 1,2,0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+usb_config_descriptor_t uhci_confd = {
+ USB_CONFIG_DESCRIPTOR_SIZE,
+ UDESC_CONFIG,
+ {USB_CONFIG_DESCRIPTOR_SIZE +
+ USB_INTERFACE_DESCRIPTOR_SIZE +
+ USB_ENDPOINT_DESCRIPTOR_SIZE},
+ 1,
+ 1,
+ 0,
+ UC_SELF_POWERED,
+ 0 /* max power */
+};
+
+usb_interface_descriptor_t uhci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0,
+ 0,
+ 1,
+ UCLASS_HUB,
+ USUBCLASS_HUB,
+ 0,
+ 0
+};
+
+usb_endpoint_descriptor_t uhci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_IN | UHCI_INTR_ENDPT,
+ UE_INTERRUPT,
+ {8},
+ 255
+};
+
+usb_hub_descriptor_t uhci_hubd_piix = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 2,
+ { UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0 },
+ 50, /* power on to power good */
+ 0,
+ { 0x00 }, /* both ports are removable */
+ { 0x00 }, /* no ports can power down individually */
+};
+
+int
+uhci_str(p, l, s)
+ usb_string_descriptor_t *p;
+ int l;
+ char *s;
+{
+ int i;
+
+ if (l == 0)
+ return (0);
+ p->bLength = 2 * strlen(s) + 2;
+ if (l == 1)
+ return (1);
+ p->bDescriptorType = UDESC_STRING;
+ l -= 2;
+ for (i = 0; s[i] && l > 1; i++, l -= 2)
+ USETW2(p->bString[i], 0, s[i]);
+ return (2*i+2);
+}
+
+/*
+ * Simulate a hardware hub by handling all the necessary requests.
+ */
+usbd_status
+uhci_root_ctrl_transfer(reqh)
+ usbd_request_handle reqh;
+{
+ uhci_softc_t *sc = (uhci_softc_t *)reqh->pipe->device->bus;
+ usb_device_request_t *req;
+ void *buf;
+ int port, x;
+ int len, value, index, status, change, l, totlen = 0;
+ usb_port_status_t ps;
+ usbd_status r;
+
+ if (!reqh->isreq)
+ panic("uhci_root_ctrl_transfer: not a request\n");
+ req = &reqh->request;
+ buf = reqh->buffer;
+
+ DPRINTFN(2,("uhci_root_ctrl_control type=0x%02x request=%02x\n",
+ req->bmRequestType, req->bRequest));
+
+ len = UGETW(req->wLength);
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+#define C(x,y) ((x) | ((y) << 8))
+ switch(C(req->bRequest, req->bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_STALL are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ if (len > 0) {
+ *(u_int8_t *)buf = sc->sc_conf;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ DPRINTFN(2,("uhci_root_ctrl_control wValue=0x%04x\n", value));
+ switch(value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ memcpy(buf, &uhci_devd, l);
+ break;
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &uhci_confd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &uhci_ifcd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &uhci_endpd, l);
+ break;
+ case UDESC_STRING:
+ if (len == 0)
+ break;
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ switch (value & 0xff) {
+ case 1: /* Vendor */
+ totlen = uhci_str(buf, len, sc->sc_vendor);
+ break;
+ case 2: /* Product */
+ totlen = uhci_str(buf, len, "UHCI root hub");
+ break;
+ }
+ break;
+ default:
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ if (len > 0) {
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED);
+ totlen = 2;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus, 0);
+ totlen = 2;
+ }
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ r = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(3, ("uhci_root_ctrl_control: UR_CLEAR_PORT_FEATURE port=%d feature=%d\n",
+ index, value));
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PR);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x | UHCI_PORTSC_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x | UHCI_PORTSC_POEDC);
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x | UHCI_PORTSC_OCIC);
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_isreset = 0;
+ r = USBD_NORMAL_COMPLETION;
+ goto ret;
+ case UHF_PORT_CONNECTION:
+ case UHF_PORT_OVER_CURRENT:
+ case UHF_PORT_POWER:
+ case UHF_PORT_LOW_SPEED:
+ case UHF_C_PORT_SUSPEND:
+ default:
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ if (len > 0) {
+ *(u_int8_t *)buf =
+ (UREAD2(sc, port) & UHCI_PORTSC_LS) >>
+ UHCI_PORTSC_LS_SHIFT;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if (value != 0) {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ l = min(len, USB_HUB_DESCRIPTOR_SIZE);
+ totlen = l;
+ memcpy(buf, &uhci_hubd_piix, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ if (len != 4) {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len);
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ x = UREAD2(sc, port);
+ status = change = 0;
+ if (x & UHCI_PORTSC_CCS )
+ status |= UPS_CURRENT_CONNECT_STATUS;
+ if (x & UHCI_PORTSC_CSC )
+ change |= UPS_C_CONNECT_STATUS;
+ if (x & UHCI_PORTSC_PE )
+ status |= UPS_PORT_ENABLED;
+ if (x & UHCI_PORTSC_POEDC)
+ change |= UPS_C_PORT_ENABLED;
+ if (x & UHCI_PORTSC_OCI )
+ status |= UPS_OVERCURRENT_INDICATOR;
+ if (x & UHCI_PORTSC_OCIC )
+ change |= UPS_C_OVERCURRENT_INDICATOR;
+ if (x & UHCI_PORTSC_SUSP )
+ status |= UPS_SUSPEND;
+ if (x & UHCI_PORTSC_LSDA )
+ status |= UPS_LOW_SPEED;
+ status |= UPS_PORT_POWER;
+ if (sc->sc_isreset)
+ change |= UPS_C_PORT_RESET;
+ USETW(ps.wPortStatus, status);
+ USETW(ps.wPortChange, change);
+ l = min(len, sizeof ps);
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ r = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x | UHCI_PORTSC_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x | UHCI_PORTSC_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x | UHCI_PORTSC_PR);
+ usbd_delay_ms(&sc->sc_bus, 10);
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PR);
+ delay(100);
+ x = UREAD2(sc, port);
+ UWRITE2(sc, port, x | UHCI_PORTSC_PE);
+ delay(100);
+ DPRINTFN(3,("uhci port %d reset, status = 0x%04x\n",
+ index, UREAD2(sc, port)));
+ sc->sc_isreset = 1;
+ break;
+ case UHF_C_PORT_CONNECTION:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_PORT_CONNECTION:
+ case UHF_PORT_OVER_CURRENT:
+ case UHF_PORT_POWER:
+ case UHF_PORT_LOW_SPEED:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_RESET:
+ default:
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ default:
+ r = USBD_IOERROR;
+ goto ret;
+ }
+ reqh->actlen = totlen;
+ r = USBD_NORMAL_COMPLETION;
+ ret:
+ reqh->status = r;
+ reqh->xfercb(reqh);
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root control request. */
+void
+uhci_root_ctrl_abort(reqh)
+ usbd_request_handle reqh;
+{
+ /* Nothing to do, all transfers are syncronous. */
+}
+
+/* Close the root pipe. */
+void
+uhci_root_ctrl_close(pipe)
+ usbd_pipe_handle pipe;
+{
+#if defined(__NetBSD__)
+ untimeout(uhci_timo, pipe->intrreqh);
+#elif defined(__FreeBSD__)
+ untimeout(uhci_timo, pipe->intrreqh, pipe->intrreqh->callout_handler);
+#endif
+ DPRINTF(("uhci_root_ctrl_close\n"));
+}
+
+/* Abort a root interrupt request. */
+void
+uhci_root_intr_abort(reqh)
+ usbd_request_handle reqh;
+{
+#if defined(__NetBSD__)
+ untimeout(uhci_timo, reqh);
+#elif defined(__FreeBSD__)
+ untimeout(uhci_timo, reqh, reqh->callout_handler);
+#endif
+}
+
+/* Start a transfer on the root interrupt pipe */
+usbd_status
+uhci_root_intr_transfer(reqh)
+ usbd_request_handle reqh;
+{
+ usbd_pipe_handle pipe = reqh->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ usb_dma_t *dmap;
+ usbd_status r;
+ int len;
+
+ DPRINTFN(3, ("uhci_root_intr_transfer: reqh=%p buf=%p len=%d flags=%d\n",
+ reqh, reqh->buffer, reqh->length, reqh->flags));
+
+ len = reqh->length;
+ dmap = &upipe->u.intr.datadma;
+ if (len == 0)
+ return (USBD_INVAL); /* XXX should it be? */
+
+ r = usb_allocmem(sc->sc_dmatag, len, 0, dmap);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+
+ sc->sc_ival = MS_TO_TICKS(reqh->pipe->endpoint->edesc->bInterval);
+#if defined(__NetBSD__)
+ timeout(uhci_timo, reqh, sc->sc_ival);
+#elif defined(__FreeBSD__)
+ /* To avoid race conditions we first initialise the struct
+ * before we use it. The timeout happen between the setting
+ * of the timeout and the setting of callout_handle
+ */
+ callout_handle_init(&reqh->callout_handler);
+ reqh->callout_handler = timeout(uhci_timo, reqh, sc->sc_ival);
+#endif
+ return (USBD_IN_PROGRESS);
+}
+
+/* Close the root interrupt pipe. */
+void
+uhci_root_intr_close(pipe)
+ usbd_pipe_handle pipe;
+{
+#if defined(__NetBSD__)
+ untimeout(uhci_timo, pipe->intrreqh);
+#elif defined(__FreeBSD__)
+ untimeout(uhci_timo, pipe->intrreqh, pipe->intrreqh->callout_handler);
+#endif
+ DPRINTF(("uhci_root_intr_close\n"));
+}
diff --git a/sys/dev/usb/uhcireg.h b/sys/dev/usb/uhcireg.h
new file mode 100644
index 0000000..30c8700
--- /dev/null
+++ b/sys/dev/usb/uhcireg.h
@@ -0,0 +1,184 @@
+/* $NetBSD: uhcireg.h,v 1.2 1998/07/26 00:40:59 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef _DEV_PCI_UHCIREG_H_
+#define _DEV_PCI_UHCIREG_H_
+
+/*** PCI config registers ***/
+
+#define PCI_USBREV 0x60 /* USB protocol revision */
+#define PCI_USBREV_MASK 0xff
+#define PCI_USBREV_PRE_1_0 0x00
+#define PCI_USBREV_1_0 0x10
+
+#define PCI_CBIO 0x20 /* configuration base IO */
+
+#define PCI_INTERFACE_UHCI 0x00
+
+/*** UHCI registers ***/
+
+#define UHCI_CMD 0x00
+#define UHCI_CMD_RS 0x0001
+#define UHCI_CMD_HCRESET 0x0002
+#define UHCI_CMD_GRESET 0x0004
+#define UHCI_CMD_EGSM 0x0008
+#define UHCI_CMD_FGR 0x0010
+#define UHCI_CMD_SWDBG 0x0020
+#define UHCI_CMD_CF 0x0040
+#define UHCI_CMD_MAXP 0x0080
+
+#define UHCI_STS 0x02
+#define UHCI_STS_USBINT 0x0001
+#define UHCI_STS_USBEI 0x0002
+#define UHCI_STS_RD 0x0004
+#define UHCI_STS_HSE 0x0008
+#define UHCI_STS_HCPE 0x0010
+#define UHCI_STS_HCH 0x0020
+
+#define UHCI_INTR 0x04
+#define UHCI_INTR_TOCRCIE 0x0001
+#define UHCI_INTR_RIE 0x0002
+#define UHCI_INTR_IOCE 0x0004
+#define UHCI_INTR_SPIE 0x0008
+
+#define UHCI_FRNUM 0x06
+#define UHCI_FRNUM_MASK 0x03ff
+
+
+#define UHCI_FLBASEADDR 0x08
+
+#define UHCI_SOF 0x0c
+#define UHCI_SOF_MASK 0x7f
+
+#define UHCI_PORTSC1 0x010
+#define UHCI_PORTSC2 0x012
+#define UHCI_PORTSC_CCS 0x0001
+#define UHCI_PORTSC_CSC 0x0002
+#define UHCI_PORTSC_PE 0x0004
+#define UHCI_PORTSC_POEDC 0x0008
+#define UHCI_PORTSC_LS 0x0030
+#define UHCI_PORTSC_LS_SHIFT 4
+#define UHCI_PORTSC_RD 0x0040
+#define UHCI_PORTSC_LSDA 0x0100
+#define UHCI_PORTSC_PR 0x0200
+#define UHCI_PORTSC_OCI 0x0400
+#define UHCI_PORTSC_OCIC 0x0800
+#define UHCI_PORTSC_SUSP 0x1000
+
+#define UHCI_FRAMELIST_COUNT 1024
+#define UHCI_FRAMELIST_ALIGN 4096
+
+#define UHCI_TD_ALIGN 16
+#define UHCI_QH_ALIGN 16
+
+typedef u_int32_t uhci_physaddr_t;
+#define UHCI_PTR_T 0x00000001
+#define UHCI_PTR_Q 0x00000002
+#define UHCI_PTR_VF 0x00000004
+
+typedef union {
+ struct uhci_soft_qh *sqh;
+ struct uhci_soft_td *std;
+} uhci_soft_td_qh_t;
+
+/*
+ * The Queue Heads and Transfer Descriptors and accessed
+ * by both the CPU and the USB controller which runs
+ * concurrently. This means that they have to be accessed
+ * with great care. As long as the data structures are
+ * not linked into the controller's frame list they cannot
+ * be accessed by it and anything goes. As soon as a
+ * TD is accessible by the controller it "owns" the td_status
+ * field; it will not be written by the CPU. Similarly
+ * the controller "owns" the qh_elink field.
+ */
+
+typedef struct {
+ uhci_physaddr_t td_link;
+ u_int32_t td_status;
+#define UHCI_TD_GET_ACTLEN(s) (((s) + 1) & 0x3ff)
+#define UHCI_TD_ZERO_ACTLEN(t) ((t) | 0x3ff)
+#define UHCI_TD_BITSTUFF 0x00020000
+#define UHCI_TD_CRCTO 0x00040000
+#define UHCI_TD_NAK 0x00080000
+#define UHCI_TD_BABBLE 0x00100000
+#define UHCI_TD_DBUFFER 0x00200000
+#define UHCI_TD_STALLED 0x00400000
+#define UHCI_TD_ACTIVE 0x00800000
+#define UHCI_TD_IOC 0x01000000
+#define UHCI_TD_IOS 0x02000000
+#define UHCI_TD_LS 0x04000000
+#define UHCI_TD_GET_ERRCNT(s) (((s) >> 27) & 3)
+#define UHCI_TD_SET_ERRCNT(n) ((n) << 27)
+#define UHCI_TD_SPD 0x20000000
+ u_int32_t td_token;
+#define UHCI_TD_PID_IN 0x00000069
+#define UHCI_TD_PID_OUT 0x000000e1
+#define UHCI_TD_PID_SETUP 0x0000002d
+#define UHCI_TD_GET_PID(s) ((s) & 0xff)
+#define UHCI_TD_SET_DEVADDR(a) ((a) << 8)
+#define UHCI_TD_GET_DEVADDR(s) (((s) >> 8) & 0x7f)
+#define UHCI_TD_SET_ENDPT(e) ((e) << 15)
+#define UHCI_TD_GET_ENDPT(s) (((s) >> 15) & 0xf)
+#define UHCI_TD_SET_DT(t) ((t) << 19)
+#define UHCI_TD_GET_DT(s) (((s) >> 19) & 1)
+#define UHCI_TD_SET_MAXLEN(l) (((l)-1) << 21)
+#define UHCI_TD_GET_MAXLEN(s) ((((s) >> 21) + 1) & 0x7ff)
+#define UHCI_TD_MAXLEN_MASK 0xffe00000
+ u_int32_t td_buffer;
+ uhci_soft_td_qh_t link; /* soft version of the td_link field */
+ /* padding to 32 bytes */
+} uhci_td_t;
+#define UHCI_TD_SIZE 32
+
+#define UHCI_TD_ERROR (UHCI_TD_BITSTUFF|UHCI_TD_CRCTO|UHCI_TD_BABBLE|UHCI_TD_DBUFFER|UHCI_TD_STALLED)
+
+#define UHCI_TD_SETUP(len, endp, dev) (UHCI_TD_SET_MAXLEN(len) | UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | UHCI_TD_PID_SETUP)
+#define UHCI_TD_OUT(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | UHCI_TD_PID_OUT | UHCI_TD_SET_DT(dt))
+#define UHCI_TD_IN(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | UHCI_TD_PID_IN | UHCI_TD_SET_DT(dt))
+
+typedef struct {
+ uhci_physaddr_t qh_hlink;
+ uhci_physaddr_t qh_elink;
+ struct uhci_soft_qh *hlink; /* soft version of qh_hlink */
+ struct uhci_soft_td *elink; /* soft version of qh_elink */
+ /* padding to 32 bytes */
+} uhci_qh_t;
+#define UHCI_QH_SIZE 32
+
+#endif /* _DEV_PCI_UHCIREG_H_ */
diff --git a/sys/dev/usb/uhcivar.h b/sys/dev/usb/uhcivar.h
new file mode 100644
index 0000000..b7102f3
--- /dev/null
+++ b/sys/dev/usb/uhcivar.h
@@ -0,0 +1,179 @@
+/* $NetBSD: uhcivar.h,v 1.3 1998/07/26 00:40:59 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * To avoid having 1024 TDs for each isochronous transfer we introduce
+ * a virtual frame list. Every UHCI_VFRAMELIST_COUNT entries in the real
+ * frame list points to a non-active TD. These, in turn, which form the
+ * starts of the virtual frame list. This also has the advantage that it
+ * simplifies linking in/out TD/QH in the schedule.
+ * Furthermore, initially each of the inactive TDs point to an inactive
+ * QH that forms the start of the interrupt traffic for that slot.
+ * Each of these QHs point to the same QH that is the start of control
+ * traffic.
+ *
+ * UHCI_VFRAMELIST_COUNT should be a power of 2 and <= UHCI_FRAMELIST_COUNT.
+ */
+#define UHCI_VFRAMELIST_COUNT 128
+
+typedef struct uhci_soft_qh uhci_soft_qh_t;
+typedef struct uhci_soft_td uhci_soft_td_t;
+
+/*
+ * An interrupt info struct contains the information needed to
+ * execute a requested routine when the controller generates an
+ * interrupt. Since we cannot know which transfer generated
+ * the interrupt all structs are linked together so they can be
+ * searched at interrupt time.
+ */
+typedef struct uhci_intr_info {
+#if defined(__FreeBSD__)
+ struct callout_handle callout_handler;
+#endif
+ struct uhci_softc *sc;
+ usbd_request_handle reqh;
+ uhci_soft_td_t *stdstart;
+ uhci_soft_td_t *stdend;
+ LIST_ENTRY(uhci_intr_info) list;
+#ifdef DIAGNOSTIC
+ int isdone;
+#endif
+} uhci_intr_info_t;
+
+/*
+ * Extra information that we need for a TD.
+ */
+struct uhci_soft_td {
+ uhci_td_t *td; /* The real TD */
+ uhci_physaddr_t physaddr; /* and its physical address. */
+};
+#define UHCI_TD_CHUNK 128 /*(PAGE_SIZE / UHCI_TD_SIZE)*/
+
+/*
+ * Extra information that we need for a QH.
+ */
+struct uhci_soft_qh {
+ uhci_qh_t *qh; /* The real QH */
+ uhci_physaddr_t physaddr; /* and its physical address. */
+ int pos; /* Timeslot position */
+ uhci_intr_info_t *intr_info; /* Who to call on completion. */
+};
+#define UHCI_QH_CHUNK 128 /*(PAGE_SIZE / UHCI_QH_SIZE)*/
+
+/* Only used for buffer free list. */
+struct uhci_buffer {
+ struct uhci_buffer *next;
+};
+#define UHCI_BUFFER_SIZE 64
+#define UHCI_BUFFER_CHUNK 64 /*(PAGE_SIZE / UHCI_BUFFER_SIZE)*/
+
+/*
+ * Information about an entry in the virtial frame list.
+ */
+struct uhci_vframe {
+ uhci_soft_td_t *htd; /* pointer to dummy TD */
+ uhci_soft_td_t *etd; /* pointer to last TD */
+ uhci_soft_qh_t *hqh; /* pointer to dummy QH */
+ uhci_soft_qh_t *eqh; /* pointer to last QH */
+ u_int bandwidth; /* max bandwidth used by this frame */
+};
+
+typedef struct uhci_softc {
+ struct usbd_bus sc_bus; /* base device */
+#if defined(__NetBSD__)
+ void *sc_ih; /* interrupt vectoring */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+
+ bus_dma_tag_t sc_dmatag; /* DMA tag */
+ /* XXX should keep track of all DMA memory */
+#elif defined(__FreeBSD__)
+ int sc_iobase;
+ int sc_int;
+ int unit;
+#endif
+
+ uhci_physaddr_t *sc_pframes;
+ struct uhci_vframe sc_vframes[UHCI_VFRAMELIST_COUNT];
+
+ uhci_soft_qh_t *sc_ctl_start; /* dummy QH for control */
+ uhci_soft_qh_t *sc_ctl_end; /* last control QH */
+ uhci_soft_qh_t *sc_bulk_start; /* dummy QH for bulk */
+ uhci_soft_qh_t *sc_bulk_end; /* last bulk transfer */
+
+ uhci_soft_td_t *sc_freetds;
+ uhci_soft_qh_t *sc_freeqhs;
+ struct uhci_buffer *sc_freebuffers;
+
+ u_int8_t sc_addr; /* device address */
+ u_int8_t sc_conf; /* device configuration */
+
+ char sc_isreset;
+
+ int sc_intrs;
+ LIST_HEAD(, uhci_intr_info) sc_intrhead;
+
+ /* Info for the root hub interrupt channel. */
+ int sc_ival;
+
+ char sc_vflock;
+#define UHCI_HAS_LOCK 1
+#define UHCI_WANT_LOCK 2
+
+#if defined(__NetBSD__)
+ usb_dma_t *sc_mallocs;
+#endif
+
+ char sc_vendor[16];
+} uhci_softc_t;
+
+usbd_status uhci_init __P((uhci_softc_t *));
+int uhci_intr __P((void *));
+#if 0
+void uhci_reset __P((void *));
+#endif
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uhcidebug) printf x
+#define DPRINTFN(n,x) if (uhcidebug>(n)) printf x
+extern int uhcidebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
diff --git a/sys/dev/usb/uhid.c b/sys/dev/usb/uhid.c
new file mode 100644
index 0000000..34debf6
--- /dev/null
+++ b/sys/dev/usb/uhid.c
@@ -0,0 +1,632 @@
+/* $NetBSD: uhid.c,v 1.3 1998/08/01 20:52:45 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#elif defined(__FreeBSD__)
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/hid.h>
+#include <dev/usb/usb_quirks.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uhiddebug) printf x
+#define DPRINTFN(n,x) if (uhiddebug>(n)) printf x
+int uhiddebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct uhid_softc {
+ bdevice sc_dev; /* base device */
+ usbd_interface_handle sc_iface; /* interface */
+ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
+ int sc_ep_addr;
+
+ int sc_isize;
+ int sc_osize;
+ int sc_fsize;
+ u_int8_t sc_iid;
+ u_int8_t sc_oid;
+ u_int8_t sc_fid;
+
+ char *sc_ibuf;
+ char *sc_obuf;
+
+ void *sc_repdesc;
+ int sc_repdesc_size;
+
+ struct clist sc_q;
+ struct selinfo sc_rsel;
+ u_char sc_state; /* driver state */
+#define UHID_OPEN 0x01 /* device is open */
+#define UHID_ASLP 0x02 /* waiting for mouse data */
+#define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */
+#define UHID_IMMED 0x08 /* return read data immediately */
+ int sc_disconnected; /* device is gone */
+};
+
+#define UHIDUNIT(dev) (minor(dev))
+#define UHID_CHUNK 128 /* chunk size for read */
+#define UHID_BSIZE 1020 /* buffer size */
+
+#if defined(__NetBSD__)
+int uhid_match __P((struct device *, struct cfdata *, void *));
+void uhid_attach __P((struct device *, struct device *, void *));
+#elif defined(__FreeBSD__)
+static device_probe_t uhid_match;
+static device_attach_t uhid_attach;
+#endif
+
+int uhidopen __P((dev_t, int, int, struct proc *));
+int uhidclose __P((dev_t, int, int, struct proc *p));
+int uhidread __P((dev_t, struct uio *uio, int));
+int uhidwrite __P((dev_t, struct uio *uio, int));
+int uhidioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
+int uhidpoll __P((dev_t, int, struct proc *));
+void uhid_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
+void uhid_disco __P((void *));
+
+#if defined(__NetBSD__)
+extern struct cfdriver uhid_cd;
+
+struct cfattach uhid_ca = {
+ sizeof(struct uhid_softc), uhid_match, uhid_attach
+};
+#elif defined(__FreeBSD__)
+
+static devclass_t uhid_devclass;
+
+static device_method_t uhid_methods[] = {
+ DEVMETHOD(device_probe, uhid_match),
+ DEVMETHOD(device_attach, uhid_attach),
+ {0,0}
+};
+
+static driver_t uhid_driver = {
+ "uhid",
+ uhid_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct uhid_softc)
+};
+#endif
+
+
+#if defined(__NetBSD__)
+int
+uhid_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+uhid_match(device_t device)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(device);
+#endif
+ usb_interface_descriptor_t *id;
+
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (!id || id->bInterfaceClass != UCLASS_HID)
+ return (UMATCH_NONE);
+ return (UMATCH_IFACECLASS_GENERIC);
+}
+
+#if defined(__NetBSD__)
+void
+uhid_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct uhid_softc *sc = (struct uhid_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+uhid_attach(device_t self)
+{
+ struct uhid_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+#endif
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int size;
+ void *desc;
+ usbd_status r;
+ char devinfo[1024];
+
+ sc->sc_disconnected = 1;
+ sc->sc_iface = iface;
+ id = usbd_get_interface_descriptor(iface);
+ usbd_devinfo(uaa->device, 0, devinfo);
+#if defined(__FreeBSD__)
+ usb_device_set_desc(self, devinfo);
+ printf("%s%d", device_get_name(self), device_get_unit(self));
+#endif
+ printf(": %s (interface class %d/%d)\n", devinfo,
+ id->bInterfaceClass, id->bInterfaceSubClass);
+ sc->sc_dev = self;
+
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (!ed) {
+ DEVICE_ERROR(sc->sc_dev, ("could not read endpoint descriptor\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ DPRINTFN(10,("uhid_attach: \
+bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
+ ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
+ ed->bEndpointAddress & UE_IN ? "in" : "out",
+ ed->bmAttributes & UE_XFERTYPE,
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
+ (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ DEVICE_ERROR(sc->sc_dev, ("unexpected endpoint\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_ep_addr = ed->bEndpointAddress;
+ sc->sc_disconnected = 0;
+
+ r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USB);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DEVICE_ERROR(sc->sc_dev, ("no report descriptor\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ (void)usbd_set_idle(iface, 0, 0);
+
+ sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
+ sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid);
+ sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid);
+
+ sc->sc_repdesc = desc;
+ sc->sc_repdesc_size = size;
+
+ ATTACH_SUCCESS_RETURN;
+}
+
+void
+uhid_disco(p)
+ void *p;
+{
+ struct uhid_softc *sc = p;
+
+ DPRINTF(("ums_hid: sc=%p\n", sc));
+ usbd_abort_pipe(sc->sc_intrpipe);
+ sc->sc_disconnected = 1;
+}
+
+void
+uhid_intr(reqh, addr, status)
+ usbd_request_handle reqh;
+ usbd_private_handle addr;
+ usbd_status status;
+{
+ struct uhid_softc *sc = addr;
+
+ DPRINTFN(5, ("uhid_intr: status=%d\n", status));
+ DPRINTFN(5, ("uhid_intr: data = %02x %02x %02x\n",
+ sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("uhid_intr: status=%d\n", status));
+ sc->sc_state |= UHID_NEEDCLEAR;
+ return;
+ }
+
+ (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
+
+ if (sc->sc_state & UHID_ASLP) {
+ sc->sc_state &= ~UHID_ASLP;
+ DPRINTFN(5, ("uhid_intr: waking %p\n", sc));
+ wakeup((caddr_t)sc);
+ }
+ selwakeup(&sc->sc_rsel);
+}
+
+int
+uhidopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ usbd_status r;
+#if defined(__NetBSD__)
+ struct uhid_softc *sc;
+ int unit = UHIDUNIT(dev);
+
+ if (unit >= uhid_cd.cd_ndevs)
+ return ENXIO;
+ sc = uhid_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+#endif
+ if (!sc)
+ return ENXIO;
+
+ DPRINTF(("uhidopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected));
+
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ if (sc->sc_state & UHID_OPEN)
+ return EBUSY;
+
+#if defined(__NetBSD__)
+ if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1)
+ return ENOMEM;
+#elif defined(__FreeBSD__)
+ clist_alloc_cblocks(&sc->sc_q, UHID_BSIZE, 0);
+#endif
+
+ sc->sc_state |= UHID_OPEN;
+ sc->sc_state &= ~UHID_IMMED;
+
+ sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_WAITOK);
+ sc->sc_obuf = malloc(sc->sc_osize, M_USB, M_WAITOK);
+
+ /* Set up interrupt pipe. */
+ r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intrpipe, sc, sc->sc_ibuf,
+ sc->sc_isize, uhid_intr);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("uhidopen: usbd_open_pipe_intr failed, error=%d\n",r));
+ sc->sc_state &= ~UHID_OPEN;
+ return (EIO);
+ }
+ usbd_set_disco(sc->sc_intrpipe, uhid_disco, sc);
+
+ return 0;
+}
+
+int
+uhidclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+#if defined(__NetBSD__)
+ struct uhid_softc *sc;
+ int unit = UHIDUNIT(dev);
+
+ if (unit >= uhid_cd.cd_ndevs)
+ return ENXIO;
+ sc = uhid_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+#endif
+
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ DPRINTF(("uhidclose: sc=%p\n", sc));
+
+ /* Disable interrupts. */
+ usbd_abort_pipe(sc->sc_intrpipe);
+ usbd_close_pipe(sc->sc_intrpipe);
+
+ sc->sc_state &= ~UHID_OPEN;
+
+#if defined(__NetBSD__)
+ clfree(&sc->sc_q);
+#elif defined(__FreeBSD__)
+ clist_free_cblocks(&sc->sc_q);
+#endif
+
+ free(sc->sc_ibuf, M_USB);
+ free(sc->sc_obuf, M_USB);
+
+ return 0;
+}
+
+int
+uhidread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int s;
+ int error = 0;
+ size_t length;
+ u_char buffer[UHID_CHUNK];
+ usbd_status r;
+#if defined(__NetBSD__)
+ struct uhid_softc *sc;
+ int unit = UHIDUNIT(dev);
+
+ if (unit >= uhid_cd.cd_ndevs)
+ return ENXIO;
+ sc = uhid_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+#endif
+
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ DPRINTFN(1, ("uhidread\n"));
+ if (sc->sc_state & UHID_IMMED) {
+ DPRINTFN(1, ("uhidread immed\n"));
+
+ r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
+ sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EIO);
+ return (uiomove(buffer, sc->sc_isize, uio));
+ }
+
+ s = spltty();
+ while (sc->sc_q.c_cc == 0) {
+ if (flag & IO_NDELAY) {
+ splx(s);
+ return EWOULDBLOCK;
+ }
+ sc->sc_state |= UHID_ASLP;
+ DPRINTFN(5, ("uhidread: sleep on %p\n", sc));
+ error = tsleep((caddr_t)sc, PZERO | PCATCH, "uhidrea", 0);
+ DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
+ if (error) {
+ sc->sc_state &= ~UHID_ASLP;
+ splx(s);
+ return (error);
+ }
+ if (sc->sc_state & UHID_NEEDCLEAR) {
+ DPRINTFN(-1,("uhidread: clearing stall\n"));
+ sc->sc_state &= ~UHID_NEEDCLEAR;
+ usbd_clear_endpoint_stall(sc->sc_intrpipe);
+ }
+ }
+ splx(s);
+
+ /* Transfer as many chunks as possible. */
+ while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
+ length = min(sc->sc_q.c_cc, uio->uio_resid);
+ if (length > sizeof(buffer))
+ length = sizeof(buffer);
+
+ /* Remove a small chunk from the input queue. */
+ (void) q_to_b(&sc->sc_q, buffer, length);
+ DPRINTFN(5, ("uhidread: got %d chars\n", length));
+
+ /* Copy the data to the user process. */
+ if ((error = uiomove(buffer, length, uio)) != 0)
+ break;
+ }
+
+ return (error);
+}
+
+int
+uhidwrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int error;
+ int size;
+ usbd_status r;
+#if defined(__NetBSD__)
+ struct uhid_softc *sc;
+ int unit = UHIDUNIT(dev);
+
+ if (unit >= uhid_cd.cd_ndevs)
+ return ENXIO;
+ sc = uhid_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+#endif
+
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ DPRINTFN(1, ("uhidwrite\n"));
+
+ size = sc->sc_osize;
+ error = 0;
+ while (uio->uio_resid > 0) {
+ if (uio->uio_resid != size)
+ return (0);
+ if ((error = uiomove(sc->sc_obuf, size, uio)) != 0)
+ break;
+ if (sc->sc_oid)
+ r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
+ sc->sc_obuf[0],
+ sc->sc_obuf+1, size-1);
+ else
+ r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
+ 0, sc->sc_obuf, size);
+ if (r != USBD_NORMAL_COMPLETION) {
+ error = EIO;
+ break;
+ }
+ }
+ return (error);
+}
+
+int
+uhidioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct usb_ctl_report_desc *rd;
+ struct usb_ctl_report *re;
+ int size, id;
+ usbd_status r;
+#if defined(__NetBSD__)
+ struct uhid_softc *sc;
+ int unit = UHIDUNIT(dev);
+
+ if (unit >= uhid_cd.cd_ndevs)
+ return ENXIO;
+ sc = uhid_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+#endif
+
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
+ switch (cmd) {
+ case FIONBIO:
+ /* All handled in the upper FS layer. */
+ break;
+
+ case USB_GET_REPORT_DESC:
+ rd = (struct usb_ctl_report_desc *)addr;
+ size = min(sc->sc_repdesc_size, sizeof rd->data);
+ rd->size = size;
+ memcpy(rd->data, sc->sc_repdesc, size);
+ break;
+
+ case USB_SET_IMMED:
+ if (*(int *)addr)
+ sc->sc_state |= UHID_IMMED;
+ else
+ sc->sc_state &= ~UHID_IMMED;
+ break;
+
+ case USB_GET_REPORT:
+ re = (struct usb_ctl_report *)addr;
+ switch (re->report) {
+ case UHID_INPUT_REPORT:
+ size = sc->sc_isize;
+ id = sc->sc_iid;
+ break;
+ case UHID_OUTPUT_REPORT:
+ size = sc->sc_osize;
+ id = sc->sc_oid;
+ break;
+ case UHID_FEATURE_REPORT:
+ size = sc->sc_fsize;
+ id = sc->sc_fid;
+ break;
+ default:
+ return (EINVAL);
+ }
+ r = usbd_get_report(sc->sc_iface, re->report, id,
+ re->data, size);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EIO);
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+uhidpoll(dev, events, p)
+ dev_t dev;
+ int events;
+ struct proc *p;
+{
+ int revents = 0;
+ int s;
+#if defined(__NetBSD__)
+ struct uhid_softc *sc;
+ int unit = UHIDUNIT(dev);
+
+ if (unit >= uhid_cd.cd_ndevs)
+ return ENXIO;
+ sc = uhid_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct uhid_softc *sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+#endif
+
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ s = spltty();
+ if (events & (POLLOUT | POLLWRNORM))
+ revents |= events & (POLLOUT | POLLWRNORM);
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (sc->sc_q.c_cc > 0)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(p, &sc->sc_rsel);
+ }
+
+ splx(s);
+ return (revents);
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(uhid, usb, uhid_driver, uhid_devclass, usb_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c
new file mode 100644
index 0000000..a2f759f
--- /dev/null
+++ b/sys/dev/usb/uhub.c
@@ -0,0 +1,490 @@
+/* $NetBSD: uhub.c,v 1.5 1998/08/02 22:30:52 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct uhub_softc {
+ bdevice sc_dev; /* base device */
+ usbd_device_handle sc_hub; /* USB device */
+ usbd_pipe_handle sc_ipipe; /* interrupt pipe */
+ u_int8_t sc_status[1]; /* XXX more ports */
+ u_char sc_running;
+};
+
+#if defined(__NetBSD__)
+int uhub_match __P((struct device *, struct cfdata *, void *));
+void uhub_attach __P((struct device *, struct device *, void *));
+#elif defined(__FreeBSD__)
+static device_probe_t uhub_match;
+static device_attach_t uhub_attach;
+#endif
+
+usbd_status uhub_init_port __P((int, struct usbd_port *, usbd_device_handle));
+void uhub_disconnect __P((struct usbd_port *up, int portno));
+usbd_status uhub_explore __P((usbd_device_handle hub));
+void uhub_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
+
+/*void uhub_disco __P((void *));*/
+
+#if defined(__NetBSD__)
+extern struct cfdriver uhub_cd;
+
+struct cfattach uhub_ca = {
+ sizeof(struct uhub_softc), uhub_match, uhub_attach
+};
+
+struct cfattach uhub_uhub_ca = {
+ sizeof(struct uhub_softc), uhub_match, uhub_attach
+};
+
+#elif defined(__FreeBSD__)
+static devclass_t uhub_devclass;
+
+static device_method_t uhub_methods[] = {
+ DEVMETHOD(device_probe, uhub_match),
+ DEVMETHOD(device_attach, uhub_attach),
+ {0,0}
+};
+
+static driver_t uhub_driver = {
+ "usb", /* this is silly, but necessary. The uhub
+ * implements a usb bus on top of a usb bus,
+ * but the problem is that name of the driver
+ * is used a the name of the device class it
+ * implements.
+ */
+ uhub_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct uhub_softc)
+};
+#endif
+
+#if defined(__NetBSD__)
+int
+uhub_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+uhub_match(device_t device)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(device);
+#endif
+ usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
+
+ DPRINTFN(1,("uhub_match, dd=%p\n", dd));
+ /*
+ * The subclass for hubs seems to be 0 for some and 1 for others,
+ * so we just ignore the subclass.
+ */
+ if (uaa->iface == 0 && dd->bDeviceClass == UCLASS_HUB)
+ return (UMATCH_DEVCLASS_DEVSUBCLASS);
+ return (UMATCH_NONE);
+}
+
+#if defined(__NetBSD__)
+void
+uhub_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct uhub_softc *sc = (struct uhub_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+uhub_attach(device_t self)
+{
+ struct uhub_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+#endif
+ usbd_device_handle dev = uaa->device;
+ char devinfo[1024];
+ usbd_status r;
+ struct usbd_hub *hub;
+ usb_device_request_t req;
+ usb_hub_descriptor_t hubdesc;
+ int port, nports;
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+
+ DPRINTFN(10,("uhub_attach\n"));
+ sc->sc_hub = dev;
+ usbd_devinfo(dev, 1, devinfo);
+#if defined(__FreeBSD__)
+ usb_device_set_desc(self, devinfo);
+ printf("%s%d", device_get_name(self), device_get_unit(self));
+#endif
+ printf(": %s\n", devinfo);
+ sc->sc_dev = self;
+
+ r = usbd_set_config_no(dev, 0, 1);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DEVICE_ERROR(sc->sc_dev, ("configuration failed, error=%d\n", r));
+ ATTACH_ERROR_RETURN;
+ }
+
+ if (dev->depth > USB_HUB_MAX_DEPTH) {
+ DEVICE_ERROR(sc->sc_dev, ("hub depth (%d) exceeded, hub ignored\n",
+ USB_HUB_MAX_DEPTH));
+ ATTACH_ERROR_RETURN;
+ }
+
+ /* Get hub descriptor. */
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
+ DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
+ /* XXX not correct for hubs with >7 ports */
+ r = usbd_do_request(dev, &req, &hubdesc);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DEVICE_ERROR(sc->sc_dev, ("getting hub descriptor failed, error=%d\n", r));
+ ATTACH_ERROR_RETURN;
+ }
+
+ /* XXX block should be moved down to avoid memory leaking (or an overdose of free()'s) */
+ nports = hubdesc.bNbrPorts;
+ hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
+ M_USB, M_NOWAIT);
+ if (hub == 0)
+ ATTACH_ERROR_RETURN;
+ dev->hub = hub;
+ dev->hub->hubdata = sc;
+ hub->explore = uhub_explore;
+ hub->hubdesc = hubdesc;
+ hub->nports = nports;
+
+ DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, parent->selfpowered=%d\n",
+ dev->self_powered, dev->powersrc->parent,
+ dev->powersrc->parent ?
+ dev->powersrc->parent->self_powered : 0));
+ if (!dev->self_powered && dev->powersrc->parent &&
+ !dev->powersrc->parent->self_powered) {
+ DEVICE_ERROR(sc->sc_dev, ("bus powered hub connected to bus powered hub, ignored\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ /* Set up interrupt pipe. */
+ r = usbd_device2interface_handle(dev, 0, &iface);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DEVICE_ERROR(sc->sc_dev, ("no interface handle\n"));
+ ATTACH_ERROR_RETURN;
+ }
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (ed == 0) {
+ DEVICE_ERROR(sc->sc_dev, ("no endpoint descriptor\n"));
+ ATTACH_ERROR_RETURN;
+ }
+ if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ DEVICE_ERROR(sc->sc_dev, ("bad interrupt endpoint\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK,
+ &sc->sc_ipipe, sc, sc->sc_status,
+ sizeof(sc->sc_status),
+ uhub_intr);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DEVICE_ERROR(sc->sc_dev, ("cannot open interrupt pipe\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ for (port = 1; port <= nports; port++) {
+ r = uhub_init_port(port, &hub->ports[port-1], dev);
+ if (r != USBD_NORMAL_COMPLETION)
+ DEVICE_ERROR(sc->sc_dev, ("init of port %d failed\n", port));
+ }
+ sc->sc_running = 1;
+
+ ATTACH_SUCCESS_RETURN;
+}
+
+#if defined(__NetBSD__)
+static int
+uhub_detach(self)
+ struct device *self;
+{
+ struct uhub_softc *sc = (struct uhub_softc *)self;
+#elif defined(__FreeBSD__)
+static int
+uhub_detach(device_t self)
+{
+ struct uhub_softc *sc = device_get_softc(self);
+#endif
+ int nports = sc->sc_hub->hub->hubdesc.bNbrPorts;
+ int port;
+
+ for (port = 1; port <= nports; port++) {
+ if (sc->sc_hub->hub->ports[port-1].device)
+ uhub_disconnect(&sc->sc_hub->hub->ports[port-1], port);
+ }
+
+ free(sc->sc_hub->hub, M_USB);
+
+ return 0;
+}
+
+usbd_status
+uhub_init_port(port, uport, dev)
+ int port;
+ struct usbd_port *uport;
+ usbd_device_handle dev;
+{
+ usbd_status r;
+ u_int16_t pstatus;
+
+ uport->device = 0;
+ uport->parent = dev;
+ r = usbd_get_port_status(dev, port, &uport->status);
+ if (r != USBD_NORMAL_COMPLETION)
+ return r;
+ pstatus = UGETW(uport->status.wPortStatus);
+ DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x change=0x%04x\n",
+ port, pstatus, UGETW(uport->status.wPortChange)));
+ if ((pstatus & UPS_PORT_POWER) == 0) {
+ /* Port lacks power, turn it on */
+ r = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ r = usbd_get_port_status(dev, port, &uport->status);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ DPRINTF(("usb_init_port: turn on port %d power status=0x%04x change=0x%04x\n",
+ port, UGETW(uport->status.wPortStatus),
+ UGETW(uport->status.wPortChange)));
+ /* Wait for stable power. */
+ usbd_delay_ms(dev->bus, dev->hub->hubdesc.bPwrOn2PwrGood *
+ UHD_PWRON_FACTOR);
+ }
+ if (dev->self_powered)
+ /* Self powered hub, give ports maximum current. */
+ uport->power = USB_MAX_POWER;
+ else
+ uport->power = USB_MIN_POWER;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+uhub_explore(dev)
+ usbd_device_handle dev;
+{
+ usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
+ struct uhub_softc *sc = dev->hub->hubdata;
+ struct usbd_port *up;
+ usbd_status r;
+ int port;
+ int change, status;
+
+ DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address));
+
+ if (!sc->sc_running)
+ return (USBD_NOT_STARTED);
+
+ /* Ignore hubs that are too deep. */
+ if (dev->depth > USB_HUB_MAX_DEPTH)
+ return (USBD_TOO_DEEP);
+
+ for(port = 1; port <= hd->bNbrPorts; port++) {
+ up = &dev->hub->ports[port-1];
+ r = usbd_get_port_status(dev, port, &up->status);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("uhub_explore: get port status failed, error=%d\n",
+ r));
+ continue;
+ }
+ status = UGETW(up->status.wPortStatus);
+ change = UGETW(up->status.wPortChange);
+ DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n",
+ port, status, change));
+ if (!(change & UPS_CURRENT_CONNECT_STATUS)) {
+ /* No status change, just do recursive explore. */
+ if (up->device && up->device->hub)
+ up->device->hub->explore(up->device);
+ continue;
+ }
+ DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
+ dev->address, port));
+ usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
+ usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
+ /*
+ * If there is already a device on the port the change status
+ * must mean that is has disconnected. Looking at the
+ * current connect status is not enough to figure this out
+ * since a new unit may have been connected before we handle
+ * the disconnect.
+ */
+ if (up->device) {
+ /* Disconnected */
+ DPRINTF(("uhub_explore: device %d disappeared on port %d\n",
+ up->device->address, port));
+ uhub_disconnect(up, port);
+ usbd_clear_port_feature(dev, port,
+ UHF_C_PORT_CONNECTION);
+ }
+ if (!(status & UPS_CURRENT_CONNECT_STATUS))
+ continue;
+
+ /* Connected */
+ /* Wait for maximum device power up time. */
+ usbd_delay_ms(dev->bus, USB_PORT_POWERUP_DELAY);
+ /* Reset port, which implies enabling it. */
+ if (usbd_reset_port(dev, port, &up->status) !=
+ USBD_NORMAL_COMPLETION)
+ continue;
+
+ /* Wait for power to settle in device. */
+ usbd_delay_ms(dev->bus, USB_POWER_SETTLE);
+
+ /* Get device info and set its address. */
+ r = usbd_new_device(&sc->sc_dev, dev->bus,
+ dev->depth + 1, status & UPS_LOW_SPEED,
+ port, up);
+ /* XXX retry a few times? */
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTFN(-1,("uhub_explore: usb_new_device failed, error=%d\n", r));
+ /* Avoid addressing problems by disabling. */
+ /* usbd_reset_port(dev, port, &up->status); */
+/* XXX
+ * What should we do. The device may or may not be at its
+ * assigned address. In any case we'd like to ignore it.
+ * Maybe the port should be disabled until the device is
+ * disconnected.
+ */
+ if (r == USBD_SET_ADDR_FAILED || 1) {/* XXX */
+ /* The unit refused to accept a new
+ * address, and since we cannot leave
+ * at 0 we have to disable the port
+ * instead. */
+ /*
+ DEVICE_ERROR(*parent, ("device problem, disable port %d\n",
+ port));
+ */
+ usbd_clear_port_feature(dev, port,
+ UHF_PORT_ENABLE);
+ }
+ } else {
+ if (up->device->hub)
+ up->device->hub->explore(up->device);
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+uhub_disconnect(up, portno)
+ struct usbd_port *up;
+ int portno;
+{
+ usbd_device_handle dev = up->device;
+ usbd_pipe_handle p, n;
+ usb_hub_descriptor_t *hd;
+ struct usbd_port *spi;
+ int i, port;
+
+ DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n",
+ up, dev, portno));
+
+ DEVICE_MSG(dev->bdev, ("device addr %d%s on hub addr %d, port %d disconnected\n",
+ dev->address, dev->hub ? " (hub)" : "", up->parent->address, portno));
+
+ for (i = 0; i < dev->cdesc->bNumInterface; i++) {
+ for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) {
+ n = LIST_NEXT(p, next);
+ if (p->disco)
+ p->disco(p->discoarg);
+ usbd_abort_pipe(p);
+ usbd_close_pipe(p);
+ }
+ }
+
+ /* clean up the kindergarten, get rid of the kids */
+ usbd_remove_device(dev, up);
+}
+
+void
+uhub_intr(reqh, addr, status)
+ usbd_request_handle reqh;
+ usbd_private_handle addr;
+ usbd_status status;
+{
+ struct uhub_softc *sc = addr;
+
+ DPRINTFN(5,("uhub_intr: sc=%p\n", sc));
+#if 0
+ if (status != USBD_NORMAL_COMPLETION)
+ usbd_clear_endpoint_stall(sc->sc_ipipe);
+ else
+#endif
+ usb_needs_explore(sc->sc_hub->bus);
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(uhub, usb, uhub_driver, uhub_devclass, usb_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/ukbd.c b/sys/dev/usb/ukbd.c
new file mode 100644
index 0000000..57501a9
--- /dev/null
+++ b/sys/dev/usb/ukbd.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#elif defined(__FreeBSD__)
+#include <sys/ioccom.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/clock.h>
+#endif
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+#include <dev/usb/hid.h>
+
+#if defined(__NetBSD__)
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wskbdvar.h>
+#include <dev/wscons/wsksymdef.h>
+#include <dev/wscons/wsksymvar.h>
+#include <dev/wscons/wskbdmap_mfii.h>
+
+#include "opt_pckbd_layout.h"
+#include "opt_wsdisplay_compat.h"
+#endif
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ukbddebug) printf x
+#define DPRINTFN(n,x) if (ukbddebug>(n)) printf x
+int ukbddebug = 0;
+#elif defined(__FreeBSD__)
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UPROTO_BOOT_KEYBOARD 1
+
+#define NKEYCODE 6
+
+#define NUM_LOCK 0x01
+#define CAPS_LOCK 0x02
+#define SCROLL_LOCK 0x04
+
+struct ukbd_data {
+ u_int8_t modifiers;
+#define MOD_CONTROL_L 0x01
+#define MOD_CONTROL_R 0x10
+#define MOD_SHIFT_L 0x02
+#define MOD_SHIFT_R 0x20
+#define MOD_ALT_L 0x04
+#define MOD_ALT_R 0x40
+#define MOD_WIN_L 0x08
+#define MOD_WIN_R 0x80
+ u_int8_t reserved;
+ u_int8_t keycode[NKEYCODE];
+};
+
+#define PRESS 0
+#define RELEASE 0x100
+
+#define NMOD 6
+static struct {
+ int mask, key;
+} ukbd_mods[NMOD] = {
+ { MOD_CONTROL_L, 29 },
+ { MOD_CONTROL_R, 58 },
+ { MOD_SHIFT_L, 42 },
+ { MOD_SHIFT_R, 54 },
+ { MOD_ALT_L, 56 },
+ { MOD_ALT_R, 184 },
+};
+
+#define NN 0 /* no translation */
+/* Translate USB keycodes to US keyboard AT scancodes. */
+static u_int8_t ukbd_trtab[256] = {
+ 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */
+ 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */
+ 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */
+ 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */
+ 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */
+ 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */
+ 27, 43, NN, 39, 40, 41, 51, 52, /* 30 - 37 */
+ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
+ 65, 66, 67, 68, 87, 88, 170, 70, /* 40 - 47 */
+ 127, 210, 199, 201, 211, 207, 209, 205, /* 48 - 4F */
+ 203, 208, 200, 69, 181, 55, 74, 78, /* 50 - 57 */
+ 156, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
+ 72, 73, 82, 83, NN, NN, NN, NN, /* 60 - 67 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
+ NN, NN, NN, NN, NN, NN, 221, NN, /* 70 - 77 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 78 - 7F */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 80 - 87 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 88 - 8F */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* E0 - E7 */
+ NN, NN, NN, 219, NN, NN, NN, 220, /* E8 - EF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
+};
+
+#define KEY_ERROR 0x01
+
+struct ukbd_softc {
+ bdevice sc_dev; /* base device */
+ usbd_interface_handle sc_iface; /* interface */
+ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
+ int sc_ep_addr;
+
+ struct ukbd_data sc_ndata;
+ struct ukbd_data sc_odata;
+
+ char sc_enabled;
+ char sc_disconnected; /* device is gone */
+
+ int sc_leds;
+#if defined(__NetBSD__)
+ struct device *sc_wskbddev;
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+ int sc_rawkbd;
+#endif
+#endif
+
+ int sc_polling;
+ int sc_pollchar;
+};
+
+#define UKBDUNIT(dev) (minor(dev))
+#define UKBD_CHUNK 128 /* chunk size for read */
+#define UKBD_BSIZE 1020 /* buffer size */
+
+#if defined(__NetBSD__)
+int ukbd_match __P((struct device *, struct cfdata *, void *));
+void ukbd_attach __P((struct device *, struct device *, void *));
+#elif defined(__FreeBSD__)
+static device_probe_t ukbd_match;
+static device_attach_t ukbd_attach;
+static device_detach_t ukbd_detach;
+#endif
+
+void ukbd_cngetc __P((void *, u_int *, int *));
+void ukbd_cnpollc __P((void *, int));
+
+#if defined(__NetBSD__)
+const struct wskbd_consops ukbd_consops = {
+ ukbd_cngetc,
+ ukbd_cnpollc,
+};
+#endif
+
+void ukbd_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
+void ukbd_disco __P((void *));
+
+int ukbd_enable __P((void *, int));
+void ukbd_set_leds __P((void *, int));
+#if defined(__NetBSD__)
+int ukbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
+
+const struct wskbd_accessops ukbd_accessops = {
+ ukbd_enable,
+ ukbd_set_leds,
+ ukbd_ioctl,
+};
+
+const struct wskbd_mapdata ukbd_keymapdata = {
+ pckbd_keydesctab,
+#ifdef PCKBD_LAYOUT
+ PCKBD_LAYOUT,
+#else
+ KB_US,
+#endif
+};
+#endif
+
+#if defined(__NetBSD__)
+extern struct cfdriver ukbd_cd;
+
+struct cfattach ukbd_ca = {
+ sizeof(struct ukbd_softc), ukbd_match, ukbd_attach
+};
+#elif defined(__FreeBSD__)
+static devclass_t ukbd_devclass;
+
+static device_method_t ukbd_methods[] = {
+ DEVMETHOD(device_probe, ukbd_match),
+ DEVMETHOD(device_attach, ukbd_attach),
+ DEVMETHOD(device_detach, ukbd_detach),
+ {0,0}
+};
+
+static driver_t ukbd_driver = {
+ "ukbd",
+ ukbd_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct ukbd_softc)
+};
+#endif
+
+#if defined(__NetBSD__)
+int
+ukbd_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct usb_attach_arg *uaa = (struct usb_attach_arg *)aux;
+#elif defined(__FreeBSD__)
+static int
+ukbd_match(device_t device)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(device);
+#endif
+ usb_interface_descriptor_t *id;
+
+ /* Check that this is a keyboard that speaks the boot protocol. */
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id->bInterfaceClass != UCLASS_HID ||
+ id->bInterfaceSubClass != USUBCLASS_BOOT ||
+ id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD)
+ return (UMATCH_NONE);
+ return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+}
+
+#if defined(__NetBSD__)
+void
+ukbd_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct ukbd_softc *sc = (struct ukbd_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+ukbd_attach(device_t self)
+{
+ struct ukbd_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+#endif
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status r;
+ char devinfo[1024];
+#if defined(__NetBSD__)
+ struct wskbddev_attach_args a;
+#else
+ int i;
+#endif
+
+ sc->sc_disconnected = 1;
+ sc->sc_iface = iface;
+ id = usbd_get_interface_descriptor(iface);
+ usbd_devinfo(uaa->device, 0, devinfo);
+#if defined(__FreeBSD__)
+ usb_device_set_desc(self, devinfo);
+ printf("%s%d", device_get_name(self), device_get_unit(self));
+#endif
+ printf(": %s (interface class %d/%d)\n", devinfo,
+ id->bInterfaceClass, id->bInterfaceSubClass);
+ sc->sc_dev = self;
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (!ed) {
+ DEVICE_ERROR(sc->sc_dev, ("could not read endpoint descriptor\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ DPRINTFN(10,("ukbd_attach: \
+bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
+ ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
+ ed->bEndpointAddress & UE_IN ? "in" : "out",
+ ed->bmAttributes & UE_XFERTYPE,
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
+ (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ DEVICE_ERROR(sc->sc_dev, ("unexpected endpoint\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) {
+ r = usbd_set_protocol(iface, 0);
+ DPRINTFN(5, ("ukbd_attach: protocol set\n"));
+ if (r != USBD_NORMAL_COMPLETION) {
+ DEVICE_ERROR(sc->sc_dev, ("set protocol failed\n"));
+ ATTACH_ERROR_RETURN;
+ }
+ }
+ /* Ignore if SETIDLE fails since it is not crucial. */
+ usbd_set_idle(iface, 0, 0);
+
+ sc->sc_ep_addr = ed->bEndpointAddress;
+ sc->sc_disconnected = 0;
+
+#if defined(__NetBSD__)
+ a.console = 0; /* XXX */
+
+ a.keymap = &ukbd_keymapdata;
+
+ a.accessops = &ukbd_accessops;
+ a.accesscookie = sc;
+
+ sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
+
+#elif defined(__FreeBSD__)
+ /* it's alive! IT'S ALIVE! */
+ ukbd_set_leds(sc, NUM_LOCK);
+ DELAY(15000);
+ ukbd_set_leds(sc, CAPS_LOCK);
+ DELAY(20000);
+ ukbd_set_leds(sc, SCROLL_LOCK);
+ DELAY(30000);
+ ukbd_set_leds(sc, CAPS_LOCK);
+ DELAY(50000);
+ ukbd_set_leds(sc, NUM_LOCK);
+
+ ukbd_enable(sc, 1);
+#endif
+
+ ATTACH_SUCCESS_RETURN;
+}
+
+
+#if defined(__FreeBSD__)
+int
+ukbd_detach(device_t self)
+{
+ struct ukbd_softc *sc = device_get_softc(self);
+ char *devinfo = (char *) device_get_desc(self);
+
+ if (sc->sc_enabled)
+ return ENXIO;
+
+ if (devinfo) {
+ device_set_desc(self, NULL);
+ free(devinfo, M_USB);
+ }
+
+ /* good bye, and thanks for all the fish */
+ ukbd_set_leds(sc, NUM_LOCK);
+ DELAY(50000);
+ ukbd_set_leds(sc, CAPS_LOCK);
+ DELAY(30000);
+ ukbd_set_leds(sc, SCROLL_LOCK);
+ DELAY(20000);
+ ukbd_set_leds(sc, CAPS_LOCK);
+ DELAY(15000);
+ ukbd_set_leds(sc, NUM_LOCK);
+
+ return 0;
+}
+#endif
+
+
+void
+ukbd_disco(p)
+ void *p;
+{
+ struct ukbd_softc *sc = p;
+
+ DPRINTF(("ukbd_disco: sc=%p\n", sc));
+ usbd_abort_pipe(sc->sc_intrpipe);
+ sc->sc_disconnected = 1;
+}
+
+int
+ukbd_enable(v, on)
+ void *v;
+ int on;
+{
+ struct ukbd_softc *sc = v;
+ usbd_status r;
+
+ if (on) {
+ /* Set up interrupt pipe. */
+ if (sc->sc_enabled)
+ return EBUSY;
+
+ sc->sc_enabled = 1;
+ r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intrpipe, sc, &sc->sc_ndata,
+ sizeof(sc->sc_ndata), ukbd_intr);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EIO);
+ usbd_set_disco(sc->sc_intrpipe, ukbd_disco, sc);
+ } else {
+ /* Disable interrupts. */
+ usbd_abort_pipe(sc->sc_intrpipe);
+ usbd_close_pipe(sc->sc_intrpipe);
+
+ sc->sc_enabled = 0;
+ }
+
+ return (0);
+}
+
+void
+ukbd_intr(reqh, addr, status)
+ usbd_request_handle reqh;
+ usbd_private_handle addr;
+ usbd_status status;
+{
+ struct ukbd_softc *sc = addr;
+ struct ukbd_data *ud = &sc->sc_ndata;
+ int mod, omod;
+ int ibuf[NMOD+2*NKEYCODE]; /* chars events */
+ int nkeys, i, j;
+ int key, c;
+#define ADDKEY(c) ibuf[nkeys++] = (c)
+
+ DPRINTFN(5, ("ukbd_intr: status=%d\n", status));
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ukbd_intr: status=%d\n", status));
+ usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
+ return;
+ }
+
+ DPRINTFN(5, (" mod=0x%02x key0=0x%02x key1=0x%02x\n",
+ ud->modifiers, ud->keycode[0], ud->keycode[1]));
+
+ if (ud->keycode[0] == KEY_ERROR)
+ return; /* ignore */
+ nkeys = 0;
+ mod = ud->modifiers;
+ omod = sc->sc_odata.modifiers;
+ if (mod != omod)
+ for (i = 0; i < NMOD; i++)
+ if (( mod & ukbd_mods[i].mask) !=
+ (omod & ukbd_mods[i].mask))
+ ADDKEY(ukbd_mods[i].key |
+ (mod & ukbd_mods[i].mask
+ ? PRESS : RELEASE));
+ if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) {
+ /* Check for released keys. */
+ for (i = 0; i < NKEYCODE; i++) {
+ key = sc->sc_odata.keycode[i];
+ if (key == 0)
+ continue;
+ for (j = 0; j < NKEYCODE; j++)
+ if (key == ud->keycode[j])
+ goto rfound;
+ c = ukbd_trtab[key];
+ if (c)
+ ADDKEY(c | RELEASE);
+ rfound:
+ ;
+ }
+
+ /* Check for pressed keys. */
+ for (i = 0; i < NKEYCODE; i++) {
+ key = ud->keycode[i];
+ if (key == 0)
+ continue;
+ for (j = 0; j < NKEYCODE; j++)
+ if (key == sc->sc_odata.keycode[j])
+ goto pfound;
+ c = ukbd_trtab[key];
+ DPRINTFN(2,("ukbd_intr: press key=0x%02x -> 0x%02x\n",
+ key, c));
+ if (c)
+ ADDKEY(c | PRESS);
+ pfound:
+ ;
+ }
+ }
+ sc->sc_odata = *ud;
+
+ if (sc->sc_polling) {
+ if (nkeys > 0)
+ sc->sc_pollchar = ibuf[0]; /* XXX lost keys? */
+ return;
+ }
+ for (i = 0; i < nkeys; i++) {
+ c = ibuf[i];
+#if defined(__NetBSD__)
+ wskbd_input(sc->sc_wskbddev,
+ c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN,
+ c & 0xff);
+#elif defined(__FreeBSD__)
+ printf("%c (%d) %s\n", ((c&0xff) < 32 || (c&0xff) > 126? '.':(c&0xff)), c,
+ (c&RELEASE? "released":"pressed"));
+ if (ud->modifiers)
+ printf("0x%04x\n", ud->modifiers);
+ for (i = 0; i < NKEYCODE; i++)
+ if (ud->keycode[i])
+ printf("%d ", ud->keycode[i]);
+ printf("\n");
+#endif
+ }
+}
+
+void
+ukbd_set_leds(v, leds)
+ void *v;
+ int leds;
+{
+ struct ukbd_softc *sc = v;
+ u_int8_t res = leds;
+
+ DPRINTF(("ukbd_set_leds: sc=%p leds=%d\n", sc, leds));
+
+ sc->sc_leds = leds;
+#if defined(__NetBSD__)
+ res = 0;
+ if (leds & WSKBD_LED_SCROLL)
+ res |= SCROLL_LOCK;
+ if (leds & WSKBD_LED_NUM)
+ res |= NUM_LOCK;
+ if (leds & WSKBD_LED_CAPS)
+ res |= CAPS_LOCK;
+#endif
+ usbd_set_report_async(sc->sc_iface, UHID_OUTPUT_REPORT, 0, &res, 1);
+}
+
+#if defined(__NetBSD__)
+int
+ukbd_ioctl(v, cmd, data, flag, p)
+ void *v;
+ u_long cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ struct ukbd_softc *sc = v;
+
+ switch (cmd) {
+ case WSKBDIO_GTYPE:
+ *(int *)data = WSKBD_TYPE_PC_XT;
+ return 0;
+ case WSKBDIO_SETLEDS:
+ ukbd_set_leds(v, *(int *)data);
+ return 0;
+ case WSKBDIO_GETLEDS:
+ *(int *)data = sc->sc_leds;
+ return (0);
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+ case WSKBDIO_SETMODE:
+ sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
+ return (0);
+#endif
+ }
+ return -1;
+}
+
+/* Console interface. */
+/* XXX does not work. */
+void
+ukbd_cngetc(v, type, data)
+ void *v;
+ u_int *type;
+ int *data;
+{
+ struct ukbd_softc *sc = v;
+ usbd_lock_token s;
+ int c;
+
+ DPRINTFN(1,("ukbd_cngetc: enter\n"));
+ s = usbd_lock();
+ sc->sc_polling = 1;
+ sc->sc_pollchar = -1;
+ while(sc->sc_pollchar == -1)
+ usbd_dopoll(sc->sc_iface);
+ sc->sc_polling = 0;
+ c = sc->sc_pollchar;
+ *type = c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
+ *data = c & 0xff;
+ usbd_unlock(s);
+ DPRINTFN(1,("ukbd_cngetc: return 0x%02x\n", c));
+}
+
+void
+ukbd_cnpollc(v, on)
+ void *v;
+ int on;
+{
+ struct ukbd_softc *sc = v;
+
+ DPRINTFN(1,("ukbd_cnpollc: sc=%p on=%d\n", v, on));
+
+ usbd_set_polling(sc->sc_iface, on);
+}
+#endif
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(ukbd, usb, ukbd_driver, ukbd_devclass, usb_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/ulpt.c b/sys/dev/usb/ulpt.c
new file mode 100644
index 0000000..29d20c6
--- /dev/null
+++ b/sys/dev/usb/ulpt.c
@@ -0,0 +1,502 @@
+/* $NetBSD: ulpt.c,v 1.2 1998/07/25 15:19:09 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#if defined(__NetBSD__)
+#include <sys/user.h>
+#endif
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#elif defined(__FreeBSD__)
+#include <sys/ioccom.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/uio.h>
+#include <sys/conf.h>
+#include <sys/syslog.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */
+#define STEP hz/4
+
+#define LPTPRI (PZERO+8)
+#define ULPT_BSIZE 1024
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ulptdebug) printf x
+#define DPRINTFN(n,x) if (ulptdebug>(n)) printf x
+int ulptdebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UR_GET_DEVICE_ID 0
+#define UR_GET_PORT_STATUS 1
+#define UR_SOFT_RESET 2
+
+#define LPS_NERR 0x08 /* printer no error */
+#define LPS_SELECT 0x10 /* printer selected */
+#define LPS_NOPAPER 0x20 /* printer out of paper */
+#define LPS_INVERT (LPS_SELECT|LPS_NERR)
+#define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER)
+
+struct ulpt_softc {
+ bdevice sc_dev;
+ usbd_device_handle sc_udev; /* device */
+ usbd_interface_handle sc_iface; /* interface */
+ int sc_ifaceno;
+ usbd_pipe_handle sc_bulkpipe; /* bulk pipe */
+ int sc_bulk;
+
+ u_char sc_state;
+#define ULPT_OPEN 0x01 /* device is open */
+#define ULPT_OBUSY 0x02 /* printer is busy doing output */
+#define ULPT_INIT 0x04 /* waiting to initialize for open */
+ u_char sc_flags;
+#define ULPT_NOPRIME 0x40 /* don't prime on open */
+ u_char sc_laststatus;
+};
+
+#if defined(__NetBSD__)
+int ulpt_match __P((struct device *, struct cfdata *, void *));
+void ulpt_attach __P((struct device *, struct device *, void *));
+#elif defined(__FreeBSD__)
+static device_probe_t ulpt_match;
+static device_attach_t ulpt_attach;
+#endif
+
+int ulptopen __P((dev_t, int, int, struct proc *));
+int ulptclose __P((dev_t, int, int, struct proc *p));
+int ulptwrite __P((dev_t, struct uio *uio, int));
+int ulptioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
+void ulpt_disco __P((void *));
+
+int ulpt_status __P((struct ulpt_softc *));
+void ulpt_reset __P((struct ulpt_softc *));
+int ulpt_statusmsg __P((u_char, struct ulpt_softc *));
+
+#define ULPTUNIT(s) (minor(s) & 0x1f)
+#define ULPTFLAGS(s) (minor(s) & 0xe0)
+
+#if defined(__NetBSD__)
+extern struct cfdriver ulpt_cd;
+
+struct cfattach ulpt_ca = {
+ sizeof(struct ulpt_softc), ulpt_match, ulpt_attach
+};
+#elif defined(__FreeBSD__)
+static devclass_t ulpt_devclass;
+
+static device_method_t ulpt_methods[] = {
+ DEVMETHOD(device_probe, ulpt_match),
+ DEVMETHOD(device_attach, ulpt_attach),
+ {0,0}
+};
+
+static driver_t ulpt_driver = {
+ "ulpt",
+ ulpt_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct ulpt_softc)
+};
+#endif
+
+
+#if defined(__NetBSD__)
+int
+ulpt_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+ulpt_match(device_t device)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(device);
+#endif
+ usb_interface_descriptor_t *id;
+
+ DPRINTFN(10,("ulpt_match\n"));
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id->bInterfaceClass == UCLASS_PRINTER &&
+ id->bInterfaceSubClass == USUBCLASS_PRINTER &&
+ (id->bInterfaceProtocol == UPROTO_PRINTER_UNI ||
+ id->bInterfaceProtocol == UPROTO_PRINTER_BI))
+ return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+ return (UMATCH_NONE);
+}
+
+#if defined(__NetBSD__)
+void
+ulpt_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct ulpt_softc *sc = (struct ulpt_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+ulpt_attach(device_t self)
+{
+ struct ulpt_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+#endif
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
+#if 0
+ usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
+ usb_device_request_t req;
+#endif
+ char devinfo[1024];
+ usb_endpoint_descriptor_t *ed;
+ usbd_status r;
+
+ DPRINTFN(10,("ulpt_attach: sc=%p\n", sc));
+ usbd_devinfo(dev, 0, devinfo);
+#if defined(__FreeBSD__)
+ usb_device_set_desc(self, devinfo);
+ printf("%s%d", device_get_name(self), device_get_unit(self));
+#endif
+ printf(": %s (interface class %d/%d)\n", devinfo,
+ id->bInterfaceClass, id->bInterfaceSubClass);
+ sc->sc_dev = self;
+
+ /* Figure out which endpoint is the bulk out endpoint. */
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (!ed)
+ goto nobulk;
+ if ((ed->bEndpointAddress & UE_IN) != UE_OUT ||
+ (ed->bmAttributes & UE_XFERTYPE) != UE_BULK) {
+ /* In case we are using a bidir protocol... */
+ ed = usbd_interface2endpoint_descriptor(iface, 1);
+ if (!ed)
+ goto nobulk;
+ if ((ed->bEndpointAddress & UE_IN) != UE_OUT ||
+ (ed->bmAttributes & UE_XFERTYPE) != UE_BULK)
+ goto nobulk;
+ }
+ sc->sc_bulk = ed->bEndpointAddress;
+ DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_bulk));
+
+ sc->sc_iface = iface;
+ r = usbd_interface2device_handle(iface, &sc->sc_udev);
+ if (r != USBD_NORMAL_COMPLETION)
+ ATTACH_ERROR_RETURN;
+ sc->sc_ifaceno = id->bInterfaceNumber;
+
+#if 0
+XXX needs a different way to read the id string since the length
+is unknown. usbd_do_request() returns error on a short transfer.
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_DEVICE_ID;
+ USETW(req.wValue, cd->bConfigurationValue);
+ USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting);
+ USETW(req.wLength, sizeof devinfo - 1);
+ r = usbd_do_request(dev, &req, devinfo);
+ if (r == USBD_NORMAL_COMPLETION) {
+ int len;
+ char *idstr;
+ len = (devinfo[0] << 8) | (devinfo[1] & 0xff);
+ /* devinfo now contains an IEEE-1284 device ID */
+ idstr = devinfo+2;
+ idstr[len] = 0;
+ DEVICE_ERROR(sc->sc_dev, ("device id <%s>\n", idstr));
+ } else {
+ printf("%s: \n", sc->sc_dev.dv_xname);
+ DEVICE_ERROR(sc->sc_dev, ("cannot get device id"));
+ }
+#endif
+
+ ATTACH_SUCCESS_RETURN;
+
+ nobulk:
+ DEVICE_ERROR(sc->sc_dev, ("could not find bulk endpoint\n"));
+ ATTACH_ERROR_RETURN;
+}
+
+int
+ulpt_status(sc)
+ struct ulpt_softc *sc;
+{
+ usb_device_request_t req;
+ usbd_status r;
+ u_char status;
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_PORT_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 1);
+ r = usbd_do_request(sc->sc_udev, &req, &status);
+ DPRINTFN(1, ("ulpt_status: status=0x%02x r=%d\n", status, r));
+ if (r == USBD_NORMAL_COMPLETION)
+ return (status);
+ else
+ return (0);
+}
+
+void
+ulpt_reset(sc)
+ struct ulpt_softc *sc;
+{
+ usb_device_request_t req;
+
+ DPRINTFN(1, ("ulpt_reset\n"));
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SOFT_RESET;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 0);
+ (void)usbd_do_request(sc->sc_udev, &req, 0);
+}
+
+/*
+ * Reset the printer, then wait until it's selected and not busy.
+ */
+int
+ulptopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ u_char flags = ULPTFLAGS(dev);
+ usbd_status r;
+ int spin, error;
+#if defined(__NetBSD__)
+ int unit = ULPTUNIT(dev);
+ struct ulpt_softc *sc;
+
+ if (unit >= ulpt_cd.cd_ndevs)
+ return ENXIO;
+ sc = ulpt_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct ulpt_softc *sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev));
+#endif
+
+ if (!sc || !sc->sc_iface)
+ return ENXIO;
+
+ if (sc->sc_state)
+ return EBUSY;
+
+ sc->sc_state = ULPT_INIT;
+ sc->sc_flags = flags;
+ DPRINTF(("ulptopen: flags=0x%x\n", (unsigned)flags));
+
+ if ((flags & ULPT_NOPRIME) == 0)
+ ulpt_reset(sc);
+
+ for (spin = 0; (ulpt_status(sc) & LPS_SELECT) == 0; spin += STEP) {
+ if (spin >= TIMEOUT) {
+ sc->sc_state = 0;
+ return EBUSY;
+ }
+
+ /* wait 1/4 second, give up if we get a signal */
+ error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "ulptop", STEP);
+ if (error != EWOULDBLOCK) {
+ sc->sc_state = 0;
+ return error;
+ }
+ }
+
+ r = usbd_open_pipe(sc->sc_iface, sc->sc_bulk, 0, &sc->sc_bulkpipe);
+ if (r != USBD_NORMAL_COMPLETION) {
+ sc->sc_state = 0;
+ return (EIO);
+ }
+
+ sc->sc_state = ULPT_OPEN;
+
+ DPRINTF(("ulptopen: done\n"));
+ return (0);
+}
+
+int
+ulpt_statusmsg(status, sc)
+ u_char status;
+ struct ulpt_softc *sc;
+{
+ u_char new;
+
+ status = (status ^ LPS_INVERT) & LPS_MASK;
+ new = status & ~sc->sc_laststatus;
+ sc->sc_laststatus = status;
+
+/* XXX should be tidied up into one block and a definition in usb_ports.h
+ */
+#if defined(__NetBSD__)
+ if (new & LPS_SELECT)
+ log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname);
+ else if (new & LPS_NOPAPER)
+ log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname);
+ else if (new & LPS_NERR)
+ log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname);
+#elif defined(__FreeBSD__)
+ if (new & LPS_SELECT)
+ log(LOG_NOTICE, "%s%d: offline\n",
+ device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev));
+ else if (new & LPS_NOPAPER)
+ log(LOG_NOTICE, "%s%d: out of paper\n",
+ device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev));
+ else if (new & LPS_NERR)
+ log(LOG_NOTICE, "%s%d: output error\n",
+ device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev));
+#endif
+
+ return status;
+}
+
+int
+ulptclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+#if defined(__NetBSD__)
+ int unit = ULPTUNIT(dev);
+ struct ulpt_softc *sc;
+
+ if (unit >= ulpt_cd.cd_ndevs)
+ return (ENXIO);
+ sc = ulpt_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct ulpt_softc *sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev));
+#endif
+
+ usbd_close_pipe(sc->sc_bulkpipe);
+
+ sc->sc_state = 0;
+
+ DPRINTF(("ulptclose: closed\n"));
+ return (0);
+}
+
+int
+ulptwrite(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ size_t n;
+ int error = 0;
+ char buf[ULPT_BSIZE];
+ usbd_request_handle reqh;
+ usbd_status r;
+#if defined(__NetBSD__)
+ int unit = ULPTUNIT(dev);
+ struct ulpt_softc *sc;
+
+ if (unit >= ulpt_cd.cd_ndevs)
+ return (ENXIO);
+ sc = ulpt_cd.cd_devs[unit];
+#elif defined(__FreeBSD__)
+ struct ulpt_softc *sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev));
+#endif
+
+ DPRINTF(("ulptwrite\n"));
+ reqh = usbd_alloc_request();
+ if (reqh == 0)
+ return (EIO);
+ while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) {
+ ulpt_statusmsg(ulpt_status(sc), sc);
+ uiomove(buf, n, uio);
+ /* XXX use callback to enable interrupt? */
+ r = usbd_setup_request(reqh, sc->sc_bulkpipe, 0, buf, n,
+ 0, USBD_NO_TIMEOUT, 0);
+ if (r != USBD_NORMAL_COMPLETION) {
+ error = EIO;
+ break;
+ }
+ DPRINTFN(1, ("ulptwrite: transfer %d bytes\n", n));
+ r = usbd_sync_transfer(reqh);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ulptwrite: error=%d\n", r));
+ usbd_clear_endpoint_stall(sc->sc_bulkpipe);
+ error = EIO;
+ break;
+ }
+ }
+ usbd_free_request(reqh);
+ return (error);
+}
+
+int
+ulptioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ int error = 0;
+
+ switch (cmd) {
+ default:
+ error = ENODEV;
+ }
+
+ return error;
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(ulpt, usb, ulpt_driver, ulpt_devclass, usb_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/ums.c b/sys/dev/usb/ums.c
new file mode 100644
index 0000000..80dfa16
--- /dev/null
+++ b/sys/dev/usb/ums.c
@@ -0,0 +1,812 @@
+/* $NetBSD: ums.c,v 1.8 1998/08/01 20:11:39 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#endif
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+#include <dev/usb/hid.h>
+
+#if defined(__NetBSD__)
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+#elif defined(__FreeBSD__)
+#include <machine/mouse.h>
+#endif
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (umsdebug) printf x
+#define DPRINTFN(n,x) if (umsdebug>(n)) printf x
+int umsdebug = 1;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UMSUNIT(s) (minor(s)&0x1f)
+
+#define PS2LBUTMASK x01
+#define PS2RBUTMASK x02
+#define PS2MBUTMASK x04
+#define PS2BUTMASK 0x0f
+
+#define QUEUE_BUFSIZE 240 /* MUST be dividable by 3 _and_ 4 */
+
+struct ums_softc {
+ bdevice sc_dev; /* base device */
+ usbd_interface_handle sc_iface; /* interface */
+ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
+ int sc_ep_addr;
+
+ u_char *sc_ibuf;
+ u_int8_t sc_iid;
+ int sc_isize;
+ struct hid_location sc_loc_x, sc_loc_y, sc_loc_z;
+ struct hid_location *sc_loc_btn;
+
+ int sc_enabled;
+ int sc_disconnected; /* device is gone */
+
+ int flags; /* device configuration */
+# define UMS_Z 0x01 /* z direction available */
+ int nbuttons;
+
+#if defined(__NetBSD__)
+ u_char sc_buttons; /* mouse button status */
+ struct device *sc_wsmousedev;
+#elif defined(__FreeBSD__)
+ u_char qbuf[QUEUE_BUFSIZE];
+ u_char dummy[100]; /* just for safety and for now */
+ int qcount, qhead, qtail;
+ mousehw_t hw;
+ mousemode_t mode;
+ mousestatus_t status;
+
+ int state;
+# define UMS_ASLEEP 0x01 /* readFromDevice is waiting */
+# define UMS_SELECT 0x02 /* select is waiting */
+ struct selinfo rsel; /* process waiting in select */
+#endif
+};
+
+#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
+#define MOUSE_FLAGS (HIO_RELATIVE)
+
+#if defined(__NetBSD__)
+int ums_match __P((struct device *, struct cfdata *, void *));
+void ums_attach __P((struct device *, struct device *, void *));
+#elif defined(__FreeBSD__)
+static device_probe_t ums_match;
+static device_attach_t ums_attach;
+static device_detach_t ums_detach;
+#endif
+
+void ums_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
+void ums_disco __P((void *));
+
+static int ums_enable __P((void *));
+static void ums_disable __P((void *));
+
+#if defined(__NetBSD__)
+static int ums_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
+#elif defined(__FreeBSD__)
+static d_open_t ums_open;
+static d_close_t ums_close;
+static d_read_t ums_read;
+static d_ioctl_t ums_ioctl;
+static d_poll_t ums_poll;
+
+#define UMS_CDEV_MAJOR 138 /* XXX NWH should be requested */
+
+static struct cdevsw ums_cdevsw = {
+ ums_open, ums_close, ums_read, nowrite,
+ ums_ioctl, nostop, nullreset, nodevtotty,
+ ums_poll, nommap,
+ NULL, "ums_", NULL, -1
+};
+#endif
+
+#if defined(__NetBSD__)
+const struct wsmouse_accessops ums_accessops = {
+ ums_enable,
+ ums_ioctl,
+ ums_disable,
+};
+#endif
+
+#if defined(__NetBSD__)
+extern struct cfdriver ums_cd;
+
+struct cfattach ums_ca = {
+ sizeof(struct ums_softc), ums_match, ums_attach
+};
+#elif defined(__FreeBSD__)
+static devclass_t ums_devclass;
+
+static device_method_t ums_methods[] = {
+ DEVMETHOD(device_probe, ums_match),
+ DEVMETHOD(device_attach, ums_attach),
+ DEVMETHOD(device_detach, ums_detach),
+ {0,0}
+};
+
+static driver_t ums_driver = {
+ "ums",
+ ums_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct ums_softc)
+};
+#endif
+
+
+#if defined(__NetBSD__)
+int
+ums_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+{
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+ums_match(device_t device)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(device);
+#endif
+ usb_interface_descriptor_t *id;
+ int size, ret;
+ void *desc;
+ usbd_status r;
+
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id->bInterfaceClass != UCLASS_HID)
+ return (UMATCH_NONE);
+
+ r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (UMATCH_NONE);
+
+ if (hid_is_collection(desc, size,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
+ ret = UMATCH_IFACECLASS;
+ else
+ ret = UMATCH_NONE;
+
+ free(desc, M_TEMP);
+ return (ret);
+}
+
+#if defined(__NetBSD__)
+void
+ums_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct ums_softc *sc = (struct ums_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+#elif defined(__FreeBSD__)
+static int
+ums_attach(device_t self)
+{
+ struct ums_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+#endif
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+#if defined(__NetBSD__)
+ struct wsmousedev_attach_args a;
+#endif
+ char devinfo[1024];
+ int size;
+ void *desc;
+ usbd_status r;
+ u_int32_t flags;
+ struct hid_location loc_btn;
+ int i;
+
+ sc->sc_disconnected = 1;
+ sc->sc_iface = iface;
+ id = usbd_get_interface_descriptor(iface);
+ usbd_devinfo(uaa->device, 0, devinfo);
+#if defined(__FreeBSD__)
+ usb_device_set_desc(self, devinfo);
+ printf("%s%d", device_get_name(self), device_get_unit(self));
+#endif
+ printf(": %s (interface class %d/%d)\n", devinfo,
+ id->bInterfaceClass, id->bInterfaceSubClass);
+ sc->sc_dev = self;
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (!ed) {
+ DEVICE_ERROR(sc->sc_dev, ("could not read endpoint descriptor\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
+ "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d "
+ "bInterval=%d\n",
+ ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
+ ed->bEndpointAddress & UE_IN ? "in" : "out",
+ ed->bmAttributes & UE_XFERTYPE,
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
+ (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ DEVICE_ERROR(sc->sc_dev, ("unexpected endpoint\n"));
+ ATTACH_ERROR_RETURN;
+ }
+
+ r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP);
+ if (r != USBD_NORMAL_COMPLETION)
+ ATTACH_ERROR_RETURN;
+
+ if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
+ hid_input, &sc->sc_loc_x, &flags)) {
+ DEVICE_ERROR(sc->sc_dev, ("mouse has no X report\n"));
+ ATTACH_ERROR_RETURN;
+ }
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS)
+ DEVICE_ERROR(sc->sc_dev, ("X report 0x%04x not supported\n",
+ flags));
+
+ if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
+ hid_input, &sc->sc_loc_y, &flags)) {
+ DEVICE_ERROR(sc->sc_dev, ("mouse has no Y report\n"));
+ ATTACH_ERROR_RETURN;
+ }
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS)
+ DEVICE_ERROR(sc->sc_dev, ("Y report 0x%04x not supported\n",
+ flags));
+
+#ifndef USBVERBOSE
+ if (bootverbose)
+#endif
+ {
+ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
+ hid_input, &sc->sc_loc_z, &flags))
+ DEVICE_MSG(sc->sc_dev, ("Device has Z axis\n"));
+ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_SLIDER),
+ hid_input, &sc->sc_loc_z, &flags))
+ DEVICE_MSG(sc->sc_dev, ("Device has Slider\n"));
+ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_DIAL),
+ hid_input, &sc->sc_loc_z, &flags))
+ DEVICE_MSG(sc->sc_dev, ("Device has Dial\n"));
+ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
+ hid_input, &sc->sc_loc_z, &flags))
+ DEVICE_MSG(sc->sc_dev, ("Device has Wheel\n"));
+ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_HAT_SWITCH),
+ hid_input, &sc->sc_loc_z, &flags))
+ DEVICE_MSG(sc->sc_dev, ("Device has Hat Switch\n"));
+ }
+
+ /* try to guess the Z activator: first check Z, then WHEEL */
+ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
+ hid_input, &sc->sc_loc_z, &flags) ||
+ hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
+ hid_input, &sc->sc_loc_z, &flags)) {
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
+ sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
+#if !defined(__FreeBSD__) /* FIXME */
+ /* IntelliMouse protocol is not properly implemented yet.
+ * Probably the wisest to use the sysmouse protocol
+ */
+ } else {
+ sc->flags |= UMS_Z;
+#endif
+ }
+ }
+
+ /* figure out the number of buttons, 7 is an arbitrary limit */
+ for (i = 1; i <= 7; i++)
+ if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
+ hid_input, &loc_btn, 0))
+ break;
+ sc->nbuttons = i - 1;
+ sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons, M_USBDEV, M_NOWAIT);
+ if (!sc->sc_loc_btn)
+ ATTACH_ERROR_RETURN;
+
+#ifndef USBVERBOSE
+ if (bootverbose)
+#endif
+ DEVICE_MSG(sc->sc_dev, ("%d buttons%s\n",
+ sc->nbuttons, (sc->flags & UMS_Z? " and Z dir.":"")));
+
+ for (i = 1; i <= sc->nbuttons; i++)
+ hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
+ hid_input, &sc->sc_loc_btn[i-1], 0);
+
+ sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
+ sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT);
+ if (!sc->sc_ibuf) {
+ free(sc->sc_loc_btn, M_USB);
+ ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_ep_addr = ed->bEndpointAddress;
+ sc->sc_disconnected = 0;
+ free(desc, M_TEMP);
+
+#ifdef USB_DEBUG
+ DPRINTF(("ums_attach: sc=%p\n", sc));
+ DPRINTF(("ums_attach: X\t%d/%d\n",
+ sc->sc_loc_x.pos, sc->sc_loc_x.size));
+ DPRINTF(("ums_attach: Y\t%d/%d\n",
+ sc->sc_loc_x.pos, sc->sc_loc_x.size));
+ if (sc->flags & UMS_Z)
+ DPRINTF(("ums_attach: Z\t%d/%d\n",
+ sc->sc_loc_z.pos, sc->sc_loc_z.size));
+ for (i = 1; i <= sc->nbuttons; i++) {
+ DPRINTF(("ums_attach: B%d\t%d/%d\n",
+ i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
+ }
+ DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
+#endif
+
+#if defined(__NetBSD__)
+ a.accessops = &ums_accessops;
+ a.accesscookie = sc;
+
+ sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
+#elif defined(__FreeBSD__)
+ sc->hw.buttons = 2; /* XXX hw&mode values are bogus */
+ sc->hw.iftype = MOUSE_IF_PS2;
+ sc->hw.type = MOUSE_MOUSE;
+ if (sc->flags & UMS_Z)
+ sc->hw.model = MOUSE_MODEL_INTELLI;
+ else
+ sc->hw.model = MOUSE_MODEL_GENERIC;
+ sc->hw.hwid = 0;
+ sc->mode.protocol = MOUSE_PROTO_PS2;
+ sc->mode.rate = -1;
+ sc->mode.resolution = MOUSE_RES_DEFAULT;
+ sc->mode.accelfactor = 1;
+ sc->mode.level = 0;
+ if (sc->flags & UMS_Z) {
+ sc->mode.packetsize = MOUSE_INTELLI_PACKETSIZE;
+ sc->mode.syncmask[0] = 0xc8;
+ } else {
+ sc->mode.packetsize = MOUSE_PS2_PACKETSIZE;
+ sc->mode.syncmask[0] = 0xc0;
+ }
+ sc->mode.syncmask[1] = 0;
+
+ sc->status.flags = 0;
+ sc->status.button = sc->status.obutton = 0;
+ sc->status.dx = sc->status.dy = sc->status.dz = 0;
+
+ sc->rsel.si_flags = 0;
+ sc->rsel.si_pid = 0;
+#endif
+
+ ATTACH_SUCCESS_RETURN;
+}
+
+
+#if defined(__FreeBSD__)
+static int
+ums_detach(device_t self)
+{
+ struct ums_softc *sc = device_get_softc(self);
+ char *devinfo = (char *) device_get_desc(self);
+
+ if (devinfo) {
+ device_set_desc(self, NULL);
+ free(devinfo, M_USB);
+ }
+ free(sc->sc_loc_btn, M_USB);
+ free(sc->sc_ibuf, M_USB);
+
+ return 0;
+}
+#endif
+
+void
+ums_disco(p)
+ void *p;
+{
+ struct ums_softc *sc = p;
+
+ DPRINTF(("ums_disco: sc=%p\n", sc));
+ usbd_abort_pipe(sc->sc_intrpipe);
+ sc->sc_disconnected = 1;
+}
+
+void
+ums_intr(reqh, addr, status)
+ usbd_request_handle reqh;
+ usbd_private_handle addr;
+ usbd_status status;
+{
+ struct ums_softc *sc = addr;
+ u_char *ibuf;
+ int dx, dy, dz;
+ u_char buttons = 0;
+ int i;
+
+ DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
+ DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n",
+ sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ums_intr: status=%d\n", status));
+ usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
+ return;
+ }
+
+ ibuf = sc->sc_ibuf;
+ if (sc->sc_iid) {
+ if (*ibuf++ != sc->sc_iid)
+ return;
+ }
+ dx = hid_get_data(ibuf, &sc->sc_loc_x);
+ dy = -hid_get_data(ibuf, &sc->sc_loc_y);
+ dz = hid_get_data(ibuf, &sc->sc_loc_z);
+ /* NWH Why are you modifying the button assignments here?
+ * That's the purpose of a high level mouse driver
+ */
+ for (i = 1; i <= sc->nbuttons; i++)
+ if (hid_get_data(ibuf, &sc->sc_loc_btn[i-1]))
+ buttons |= (1 << (i-1));
+
+#if defined(__NetBSD__)
+ if (dx || dy || buttons != sc->sc_buttons) {
+ DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
+ dx, dy, dz, buttons));
+ sc->sc_buttons = buttons;
+ if (sc->sc_wsmousedev)
+ wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz);
+#elif defined(__FreeBSD__)
+ if (dx || dy || buttons != sc->status.button) {
+ DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
+ dx, dy, dz, buttons));
+
+ sc->status.button = buttons;
+ sc->status.dx += dx;
+ sc->status.dy += dy;
+ sc->status.dz += dz;
+
+ /* Discard data in case of full buffer */
+ if (sc->qcount == sizeof(sc->qbuf)) {
+ DPRINTF(("Buffer full, discarded packet"));
+ return;
+ }
+
+ sc->qbuf[sc->qhead] = MOUSE_PS2_SYNC;
+ if (dx < 0)
+ sc->qbuf[sc->qhead] |= MOUSE_PS2_XNEG;
+ if (dx > 255 || dx < -255)
+ sc->qbuf[sc->qhead] |= MOUSE_PS2_XOVERFLOW;
+ if (dy < 0)
+ sc->qbuf[sc->qhead] |= MOUSE_PS2_YNEG;
+ if (dy > 255 || dy < -255)
+ sc->qbuf[sc->qhead] |= MOUSE_PS2_YOVERFLOW;
+ sc->qbuf[sc->qhead++] |= buttons;
+ sc->qbuf[sc->qhead++] = dx;
+ sc->qbuf[sc->qhead++] = dy;
+ sc->qcount += 3;
+ if (sc->flags & UMS_Z) {
+ sc->qbuf[sc->qhead++] = dz;
+ sc->qcount++;
+ }
+#ifdef USB_DEBUG
+ if (sc->qhead > sizeof(sc->qbuf))
+ DPRINTF(("Buffer overrun! %d %d\n", sc->qhead, sizeof(sc->qbuf)));
+#endif
+ if (sc->qhead >= sizeof(sc->qbuf)) /* wrap round at end of buffer */
+ sc->qhead = 0;
+
+ if (sc->state & UMS_ASLEEP) /* someone waiting for data */
+ wakeup(sc);
+ selwakeup(&sc->rsel); /* wake up any pending selects */
+ sc->state &= ~UMS_SELECT;
+#endif
+ }
+}
+
+
+static int
+ums_enable(v)
+ void *v;
+{
+ struct ums_softc *sc = v;
+
+ usbd_status r;
+
+ if (sc->sc_enabled)
+ return EBUSY;
+
+ sc->sc_enabled = 1;
+#if defined(__NetBSD__)
+ sc->sc_buttons = 0;
+#elif defined(__FreeBSD__)
+ sc->qcount = 0;
+ sc->qhead = sc->qtail = 0;
+#ifdef USB_DEBUG
+ if (sizeof(sc->qbuf) % 4 || sizeof(sc->qbuf) % 3) {
+ DPRINTF(("Buffer size not dividable by 3 or 4\n"));
+ return ENXIO;
+ }
+#endif
+ sc->status.flags = 0;
+ sc->status.button = sc->status.obutton = 0;
+ sc->status.dx = sc->status.dy = sc->status.dz = 0;
+#endif
+
+ /* Set up interrupt pipe. */
+ r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
+ USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc,
+ sc->sc_ibuf, sc->sc_isize, ums_intr);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
+ r));
+ sc->sc_enabled = 0;
+ return (EIO);
+ }
+ usbd_set_disco(sc->sc_intrpipe, ums_disco, sc);
+ return (0);
+}
+
+static void
+ums_disable(v)
+ void *v;
+{
+ struct ums_softc *sc = v;
+
+ /* Disable interrupts. */
+ usbd_abort_pipe(sc->sc_intrpipe);
+ usbd_close_pipe(sc->sc_intrpipe);
+
+ sc->sc_enabled = 0;
+
+#if defined(USBVERBOSE) && defined(__FreeBSD__)
+ if (sc->qcount != 0)
+ DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
+#endif
+}
+
+#if defined(__NetBSD__)
+static int
+ums_ioctl(v, cmd, data, flag, p)
+ void *v;
+ u_long cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+
+{
+ switch (cmd) {
+ case WSMOUSEIO_GTYPE:
+ *(u_int *)data = WSMOUSE_TYPE_PS2;
+ return (0);
+ }
+
+ return (-1); /* NWH XXX ??? */
+}
+
+#elif defined(__FreeBSD__)
+static int
+ums_open(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+
+ if (!sc) {
+ DPRINTF(("sc not found at open"));
+ return EINVAL;
+ }
+
+ return ums_enable(sc);
+}
+
+static int
+ums_close(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+
+ if (!sc) {
+ DPRINTF(("sc not found at close"));
+ return EINVAL;
+ }
+
+ if (sc->sc_enabled)
+ ums_disable(sc);
+ return 0;
+}
+
+static int
+ums_read(dev_t dev, struct uio *uio, int flag)
+{
+ struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ int s;
+ char buf[sizeof(sc->qbuf)];
+ int l = 0;
+ int error;
+
+ if (!sc || !sc->sc_enabled) {
+ DPRINTF(("sc not found at read"));
+ return EINVAL;
+ }
+
+ s = splusb();
+ while (sc->qcount == 0 ) {
+ /* NWH XXX non blocking I/O ??
+ if (non blocking I/O ) {
+ splx(s);
+ return EWOULDBLOCK;
+ } else {
+ */
+ sc->state |= UMS_ASLEEP;
+ error = tsleep(sc, PZERO | PCATCH, "umsrea", 0);
+ sc->state &= ~UMS_ASLEEP;
+ if (error) {
+ splx(s);
+ return error;
+ }
+ }
+
+ while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
+ l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
+ if (l > sizeof(buf))
+ l = sizeof(buf);
+ if (l > sizeof(sc->qbuf) - sc->qtail) /* transfer till end of buf */
+ l = sizeof(sc->qbuf) - sc->qtail;
+
+ splx(s);
+ uiomove(&sc->qbuf[sc->qtail], l, uio);
+ s = splusb();
+
+ if ( sc->qcount - l < 0 ) {
+ DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
+ sc->qcount = l;
+ }
+ sc->qcount -= l; /* remove the bytes from the buffer */
+ sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
+ }
+ splx(s);
+
+ return 0;
+}
+
+static int
+ums_poll(dev_t dev, int events, struct proc *p)
+{
+ struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ int revents = 0;
+ int s;
+
+ if (!sc) {
+ DPRINTF(("sc not found at poll"));
+ return 0; /* just to make sure */
+ }
+
+ s = splusb();
+ if (events & (POLLIN | POLLRDNORM))
+ if (sc->qcount) {
+ revents = events & (POLLIN | POLLRDNORM);
+ } else {
+ sc->state |= UMS_SELECT;
+ selrecord(p, &sc->rsel);
+ }
+ splx(s);
+
+ return revents;
+}
+
+int
+ums_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
+{
+ struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ int error = 0;
+ int s;
+
+ if (!sc) {
+ DPRINTF(("sc not found at ioctl"));
+ return ENOENT;
+ }
+
+ switch(cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->hw;
+ break;
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->mode;
+ break;
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->mode.level;
+ break;
+ case MOUSE_GETSTATUS: {
+ mousestatus_t *status = (mousestatus_t *) addr;
+
+ s = splusb();
+ *status = sc->status;
+ sc->status.obutton = sc->status.button;
+ sc->status.button = 0;
+ sc->status.dx = sc->status.dy = sc->status.dz = 0;
+ splx(s);
+
+ if (status->dx || status->dy || status->dz)
+ status->flags |= MOUSE_POSCHANGED;
+ if (status->button != status->obutton)
+ status->flags |= MOUSE_BUTTONSCHANGED;
+ break;
+ }
+ default:
+ error = ENOTTY;
+ }
+
+ return error;
+}
+#endif
+
+#if defined(__FreeBSD__)
+CDEV_DRIVER_MODULE(ums, usb, ums_driver, ums_devclass,
+ UMS_CDEV_MAJOR, ums_cdevsw, usb_driver_load, 0);
+#endif
+
diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c
new file mode 100644
index 0000000..3cea6ba
--- /dev/null
+++ b/sys/dev/usb/usb.c
@@ -0,0 +1,603 @@
+/* $NetBSD: usb.c,v 1.3 1998/08/01 18:16:20 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl
+ * More USB specs at http://www.usb.org/developers/index.shtml
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#else
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/uio.h>
+#include <sys/conf.h>
+#endif
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/select.h>
+
+#include <dev/usb/usb.h>
+
+#if defined(__FreeBSD__)
+MALLOC_DEFINE(M_USB, "USB", "USB");
+MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device");
+#endif
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_quirks.h>
+
+#if defined(__FreeBSD__)
+#include "usb_if.h"
+#endif
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+int usbdebug = 2;
+int uhcidebug = 2;
+int ohcidebug = 2;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define USBUNIT(dev) (minor(dev))
+
+struct usb_softc {
+ bdevice sc_dev; /* base device */
+ usbd_bus_handle sc_bus; /* USB controller */
+ struct usbd_port sc_port; /* dummy port for root hub */
+ char sc_running;
+ char sc_exploring;
+ struct selinfo sc_consel; /* waiting for connect change */
+};
+
+#if defined(__NetBSD__)
+int usb_match __P((struct device *, struct cfdata *, void *));
+void usb_attach __P((struct device *, struct device *, void *));
+
+int usbopen __P((dev_t, int, int, struct proc *));
+int usbclose __P((dev_t, int, int, struct proc *));
+int usbioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
+int usbpoll __P((dev_t, int, struct proc *));
+
+#else
+static device_probe_t usb_match;
+static device_attach_t usb_attach;
+static bus_print_child_t usb_print_child;
+
+d_open_t usbopen;
+d_close_t usbclose;
+d_ioctl_t usbioctl;
+int usbpoll __P((dev_t, int, struct proc *));
+
+struct cdevsw usb_cdevsw = {
+ usbopen, usbclose, noread, nowrite,
+ usbioctl, nullstop, nullreset, nodevtotty,
+ seltrue, nommap, nostrat,
+ "usb", NULL, -1
+};
+#endif
+
+usbd_status usb_discover __P((struct usb_softc *));
+
+#if defined(__NetBSD__)
+extern struct cfdriver usb_cd;
+
+struct cfattach usb_ca = {
+ sizeof(struct usb_softc), usb_match, usb_attach
+};
+#else
+static devclass_t usb_devclass = NULL;
+
+static device_method_t usb_methods[] = {
+ DEVMETHOD(device_probe, usb_match),
+ DEVMETHOD(device_attach, usb_attach),
+
+ DEVMETHOD(bus_print_child, usb_print_child),
+ {0, 0}
+};
+
+static driver_t usb_driver = {
+ "usb",
+ usb_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct usb_softc),
+};
+#endif
+
+#if defined(__NetBSD__)
+int
+usb_match(parent, match, aux)
+ struct device *parent;
+ struct cfdata *match;
+ void *aux;
+#else
+static int
+usb_match(device_t device)
+#endif
+{
+ DPRINTF(("usbd_match\n"));
+#if defined(__NetBSD__)
+ return (1);
+#else
+ return (0);
+#endif
+}
+
+#if defined(__NetBSD__)
+void
+usb_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct usb_softc *sc = (struct usb_softc *)self;
+#else
+static int
+usb_attach(device_t device)
+{
+ struct usb_softc *sc = device_get_softc(device);
+ void *aux = device_get_ivars(device);
+#endif
+ usbd_device_handle dev;
+ usbd_status r;
+
+#if defined(__NetBSD__)
+ printf("\n");
+#endif
+
+ DPRINTF(("usbd_attach\n"));
+ usbd_init();
+ sc->sc_bus = aux;
+ sc->sc_bus->usbctl = sc;
+ sc->sc_running = 1;
+ sc->sc_bus->use_polling = 1;
+ sc->sc_port.power = USB_MAX_POWER;
+#if defined(__FreeBSD__)
+ sc->sc_dev = device;
+#endif
+ r = usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, 0, 0, &sc->sc_port);
+
+ if (r == USBD_NORMAL_COMPLETION) {
+ dev = sc->sc_port.device;
+ if (!dev->hub) {
+ sc->sc_running = 0;
+ DEVICE_ERROR(sc->sc_dev, ("root device is not a hub\n"));
+ ATTACH_ERROR_RETURN;
+ }
+ sc->sc_bus->root_hub = dev;
+ dev->hub->explore(sc->sc_bus->root_hub);
+ } else {
+ DEVICE_ERROR(sc->sc_dev, ("root hub problem, error=%d\n", r));
+ sc->sc_running = 0;
+ }
+ sc->sc_bus->use_polling = 0;
+
+ ATTACH_SUCCESS_RETURN;
+}
+
+#if defined(__NetBSD__)
+int
+usbctlprint(aux, pnp)
+ void *aux;
+ const char *pnp;
+{
+ /* only "usb"es can attach to host controllers */
+ if (pnp)
+ printf("usb at %s", pnp);
+
+ return (UNCONF);
+}
+
+#else
+static void
+usb_print_child(device_t parent, device_t child)
+{
+ struct usb_softc *sc = device_get_softc(child);
+
+ printf(" at %s%d", device_get_name(parent), device_get_unit(parent));
+
+ /* How do we get to the usbd_device_handle???
+ usbd_device_handle dev = invalidadosch;
+
+ printf(" addr %d", dev->addr);
+
+ if (bootverbose) {
+ if (dev->lowspeed)
+ printf(", lowspeed");
+ if (dev->self_powered)
+ printf(", self powered");
+ else
+ printf(", %dmA", dev->power);
+ printf(", config %d", dev->config);
+ }
+ */
+}
+
+/* Reconfigure all the USB busses in the system
+ */
+
+int
+usb_driver_load(module_t mod, modeventtype_t what, void *arg)
+{
+ /* subroutine is there but inactive at the moment
+ * the reconfiguration process has not been thought through yet.
+ */
+ devclass_t ugen_devclass = devclass_find("ugen");
+ device_t *devlist;
+ int devcount;
+ int error;
+
+ switch (what) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ if (!usb_devclass)
+ return 0; /* just ignore call */
+
+ if (ugen_devclass) {
+ /* detach devices from generic driver if possible
+ */
+ error = devclass_get_devices(ugen_devclass, &devlist,
+ &devcount);
+ if (!error)
+ for (devcount--; devcount >= 0; devcount--)
+ (void) DEVICE_DETACH(devlist[devcount]);
+ }
+
+ error = devclass_get_devices(usb_devclass, &devlist, &devcount);
+ if (error)
+ return 0; /* XXX maybe transient, or error? */
+
+ for (devcount--; devcount >= 0; devcount--)
+ USB_RECONFIGURE(devlist[devcount]);
+
+ free(devlist, M_TEMP);
+ return 0;
+ }
+
+ return 0; /* nothing to do by us */
+}
+
+/* Set the description of the device including a malloc and copy
+ */
+void
+usb_device_set_desc(device_t device, char *devinfo)
+{
+ size_t l;
+ char *desc;
+
+ if ( devinfo ) {
+ l = strlen(devinfo);
+ desc = malloc(l+1, M_USB, M_NOWAIT);
+ if (desc)
+ memcpy(desc, devinfo, l+1);
+ } else
+ desc = NULL;
+
+ device_set_desc(device, desc);
+}
+#endif
+
+int
+usbopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+#if defined(__NetBSD__)
+ int unit = USBUNIT(dev);
+ struct usb_softc *sc;
+
+ if (unit >= usb_cd.cd_ndevs)
+ return (ENXIO);
+ sc = usb_cd.cd_devs[unit];
+#else
+ device_t device = devclass_get_device(usb_devclass, USBUNIT(dev));
+ struct usb_softc *sc = device_get_softc(device);
+#endif
+
+ if (sc == 0 || !sc->sc_running)
+ return (ENXIO);
+
+ return (0);
+}
+
+int
+usbclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ return (0);
+}
+
+int
+usbioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+#if defined(__NetBSD__)
+ int unit = USBUNIT(dev);
+ struct usb_softc *sc;
+
+ if (unit >= usb_cd.cd_ndevs)
+ return (ENXIO);
+ sc = usb_cd.cd_devs[unit];
+#else
+ device_t device = devclass_get_device(usb_devclass, USBUNIT(dev));
+ struct usb_softc *sc = device_get_softc(device);
+#endif
+
+ if (sc == 0 || !sc->sc_running)
+ return (ENXIO);
+ switch (cmd) {
+#ifdef USB_DEBUG
+ case USB_SETDEBUG:
+ usbdebug = uhcidebug = ohcidebug = *(int *)data;
+ break;
+#endif
+ case USB_DISCOVER:
+ usb_discover(sc);
+ break;
+ case USB_REQUEST:
+ {
+ struct usb_ctl_request *ur = (void *)data;
+ int len = UGETW(ur->request.wLength);
+ struct iovec iov;
+ struct uio uio;
+ void *ptr = 0;
+ int addr = ur->addr;
+ usbd_status r;
+ int error = 0;
+
+ if (len < 0 || len > 32768)
+ return EINVAL;
+ if (addr < 0 || addr >= USB_MAX_DEVICES ||
+ sc->sc_bus->devices[addr] == 0)
+ return EINVAL;
+ if (len != 0) {
+ iov.iov_base = (caddr_t)ur->data;
+ iov.iov_len = len;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_USERSPACE;
+ uio.uio_rw =
+ ur->request.bmRequestType & UT_READ ?
+ UIO_READ : UIO_WRITE;
+ uio.uio_procp = p;
+ ptr = malloc(len, M_TEMP, M_WAITOK);
+ if (uio.uio_rw == UIO_WRITE) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+ r = usbd_do_request(sc->sc_bus->devices[addr],
+ &ur->request, ptr);
+ if (r) {
+ error = EIO;
+ goto ret;
+ }
+ if (len != 0) {
+ if (uio.uio_rw == UIO_READ) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+ ret:
+ if (ptr)
+ free(ptr, M_TEMP);
+ return (error);
+ break;
+ }
+
+ case USB_DEVICEINFO:
+ {
+ struct usb_device_info *di = (void *)data;
+ int addr = di->addr;
+ usbd_device_handle dev;
+ struct usbd_port *p;
+ int i, r, s;
+
+ if (addr < 1 || addr >= USB_MAX_DEVICES)
+ return (EINVAL);
+ dev = sc->sc_bus->devices[addr];
+ if (dev == 0)
+ return (ENXIO);
+ di->config = dev->config;
+ usbd_devinfo_vp(dev, di->product, di->vendor);
+ usbd_printBCD(di->revision, UGETW(dev->ddesc.bcdDevice));
+ di->class = dev->ddesc.bDeviceClass;
+ di->power = dev->self_powered ? 0 : dev->power;
+ di->lowspeed = dev->lowspeed;
+ if (dev->hub) {
+ for (i = 0;
+ i < sizeof(di->ports) / sizeof(di->ports[0]) &&
+ i < dev->hub->hubdesc.bNbrPorts;
+ i++) {
+ p = &dev->hub->ports[i];
+ if (p->device)
+ r = p->device->address;
+ else {
+ s = UGETW(p->status.wPortStatus);
+ if (s & UPS_PORT_ENABLED)
+ r = USB_PORT_ENABLED;
+ else if (s & UPS_SUSPEND)
+ r = USB_PORT_SUSPENDED;
+ else if (s & UPS_PORT_POWER)
+ r = USB_PORT_POWERED;
+ else
+ r = USB_PORT_DISABLED;
+ }
+ di->ports[i] = r;
+ }
+ di->nports = dev->hub->hubdesc.bNbrPorts;
+ } else
+ di->nports = 0;
+ break;
+ }
+
+ case USB_DEVICESTATS:
+ *(struct usb_device_stats *)data = sc->sc_bus->stats;
+ break;
+
+ default:
+ return (ENXIO);
+ }
+ return (0);
+}
+
+int
+usbpoll(dev, events, p)
+ dev_t dev;
+ int events;
+ struct proc *p;
+{
+ int revents, s;
+#if defined(__NetBSD__)
+ int unit = USBUNIT(dev);
+ struct usb_softc *sc;
+
+ if (unit >= usb_cd.cd_ndevs)
+ return (ENXIO);
+ sc = usb_cd.cd_devs[unit];
+#else
+ device_t device = devclass_get_device(usb_devclass, USBUNIT(dev));
+ struct usb_softc *sc = device_get_softc(device);
+#endif
+
+ DPRINTFN(2, ("usbpoll: sc=%p events=0x%x\n", sc, events));
+ s = splusb();
+ revents = 0;
+ if (events & (POLLOUT | POLLWRNORM))
+ if (sc->sc_bus->needs_explore)
+ revents |= events & (POLLOUT | POLLWRNORM);
+ DPRINTFN(2, ("usbpoll: revents=0x%x\n", revents));
+ if (revents == 0) {
+ if (events & (POLLOUT | POLLWRNORM)) {
+ DPRINTFN(2, ("usbpoll: selrecord\n"));
+ selrecord(p, &sc->sc_consel);
+ }
+ }
+ splx(s);
+ return (revents);
+}
+
+#if defined(__NetBSD__)
+/* See remarks on this in usbdi.c
+ */
+int
+usb_bus_count()
+{
+ int i, n;
+
+ for (i = n = 0; i < usb_cd.cd_ndevs; i++)
+ if (usb_cd.cd_devs[i])
+ n++;
+ return (n);
+}
+
+usbd_status
+usb_get_bus_handle(n, h)
+ int n;
+ usbd_bus_handle *h;
+{
+ int i;
+
+ for (i = 0; i < usb_cd.cd_ndevs; i++)
+ if (usb_cd.cd_devs[i] && n-- == 0) {
+ *h = usb_cd.cd_devs[i];
+ return (USBD_NORMAL_COMPLETION);
+ }
+ return (USBD_INVAL);
+}
+#endif
+
+usbd_status
+usb_discover(sc)
+ struct usb_softc *sc;
+{
+ int s;
+
+ /* Explore device tree from the root */
+ /* We need mutual exclusion while traversing the device tree. */
+ s = splusb();
+ while (sc->sc_exploring)
+ tsleep(&sc->sc_exploring, PRIBIO, "usbdis", 0);
+ sc->sc_exploring = 1;
+ sc->sc_bus->needs_explore = 0;
+ splx(s);
+
+ sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
+
+ s = splusb();
+ sc->sc_exploring = 0;
+ wakeup(&sc->sc_exploring);
+ splx(s);
+ /* XXX should we start over if sc_needsexplore is set again? */
+ return (0);
+}
+
+void
+usb_needs_explore(bus)
+ usbd_bus_handle bus;
+{
+ bus->needs_explore = 1;
+ selwakeup(&bus->usbctl->sc_consel);
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(usb, root, usb_driver, usb_devclass, 0, 0);
+#endif
diff --git a/sys/dev/usb/usb.h b/sys/dev/usb/usb.h
new file mode 100644
index 0000000..28695a3
--- /dev/null
+++ b/sys/dev/usb/usb.h
@@ -0,0 +1,387 @@
+/* $NetBSD: usb.h,v 1.3 1998/07/25 15:22:11 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#include <sys/types.h>
+#if defined(__NetBSD__)
+#include <sys/ioctl.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <sys/malloc.h>
+
+MALLOC_DECLARE(M_USB);
+MALLOC_DECLARE(M_USBDEV);
+#endif
+
+#define USB_MAX_DEVICES 128
+#define USB_START_ADDR 0
+
+#define USB_CONTROL_ENDPOINT 0
+#define USB_MAX_ENDPOINTS 16
+
+/*
+ * The USB records contain some unaligned little-endian word
+ * components. The U[SG]ETW macros take care of both the alignment
+ * and endian problem and should always be used to access 16 bit
+ * values.
+ */
+typedef u_int8_t uByte;
+typedef u_int8_t uWord[2];
+#define UGETW(w) ((w)[0] | ((w)[1] << 8))
+#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8))
+#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h))
+/*
+ * On little-endian machines that can handle unanliged accesses
+ * (e.g. i386) these macros can be replaced by the following.
+ */
+#if 0
+#define UGETW(w) (*(u_int16_t *)(w))
+#define USETW(w,v) (*(u_int16_t *)(w) = (v))
+#endif
+
+typedef struct {
+ uByte bmRequestType;
+ uByte bRequest;
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+} usb_device_request_t;
+
+#define UT_WRITE 0x00
+#define UT_READ 0x80
+#define UT_STANDARD 0x00
+#define UT_CLASS 0x20
+#define UT_VENDOR 0x40
+#define UT_DEVICE 0x00
+#define UT_INTERFACE 0x01
+#define UT_ENDPOINT 0x02
+#define UT_OTHER 0x03
+
+#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE)
+#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE)
+#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT)
+#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE)
+#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE)
+#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT)
+#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE)
+#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE)
+#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER)
+#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE)
+#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE)
+#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER)
+
+/* Requests */
+#define UR_GET_STATUS 0x00
+#define UR_CLEAR_FEATURE 0x01
+#define UR_SET_FEATURE 0x03
+#define UR_SET_ADDRESS 0x05
+#define UR_GET_DESCRIPTOR 0x06
+#define UDESC_DEVICE 1
+#define UDESC_CONFIG 2
+#define UDESC_STRING 3
+#define UDESC_INTERFACE 4
+#define UDESC_ENDPOINT 5
+#define UR_SET_DESCRIPTOR 0x07
+#define UR_GET_CONFIG 0x08
+#define UR_SET_CONFIG 0x09
+#define UR_GET_INTERFACE 0x0a
+#define UR_SET_INTERFACE 0x0b
+#define UR_SYNCH_FRAME 0x0c
+
+/* Feature numbers */
+#define UF_ENDPOINT_STALL 0
+#define UF_DEVICE_REMOTE_WAKEUP 1
+
+#define USB_MAX_IPACKET 8 /* maximum size of the initial packet */
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+} usb_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize;
+ /* The fields below are not part of the initial descriptor. */
+ uWord idVendor;
+ uWord idProduct;
+ uWord bcdDevice;
+ uByte iManufacturer;
+ uByte iProduct;
+ uByte iSerialNumber;
+ uByte bNumConfigurations;
+} usb_device_descriptor_t;
+#define USB_DEVICE_DESCRIPTOR_SIZE 18
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumInterface;
+ uByte bConfigurationValue;
+ uByte iConfiguration;
+ uByte bmAttributes;
+#define UC_BUS_POWERED 0x80
+#define UC_SELF_POWERED 0x40
+#define UC_REMOTE_WAKEUP 0x20
+ uByte bMaxPower; /* max current in 2 mA units */
+#define UC_POWER_FACTOR 2
+} usb_config_descriptor_t;
+#define USB_CONFIG_DESCRIPTOR_SIZE 9
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bInterfaceNumber;
+ uByte bAlternateSetting;
+ uByte bNumEndpoints;
+ uByte bInterfaceClass;
+ uByte bInterfaceSubClass;
+ uByte bInterfaceProtocol;
+ uByte iInterface;
+} usb_interface_descriptor_t;
+#define USB_INTERFACE_DESCRIPTOR_SIZE 9
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bEndpointAddress;
+#define UE_IN 0x80
+#define UE_OUT 0x00
+#define UE_ADDR 0x0f
+#define UE_GET_IN(a) (((a) >> 7) & 1)
+ uByte bmAttributes;
+#define UE_CONTROL 0x00
+#define UE_ISOCHRONOUS 0x01
+#define UE_BULK 0x02
+#define UE_INTERRUPT 0x03
+#define UE_XFERTYPE 0x03
+ uWord wMaxPacketSize;
+ uByte bInterval;
+} usb_endpoint_descriptor_t;
+#define USB_ENDPOINT_DESCRIPTOR_SIZE 7
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bString[127];
+} usb_string_descriptor_t;
+#define USB_MAX_STRING_LEN 128
+
+/* Hub specific request */
+#define UR_GET_BUS_STATE 0x02
+
+/* Hub features */
+#define UHF_C_HUB_LOCAL_POWER 0
+#define UHF_C_HUB_OVER_CURRENT 1
+#define UHF_PORT_CONNECTION 0
+#define UHF_PORT_ENABLE 1
+#define UHF_PORT_SUSPEND 2
+#define UHF_PORT_OVER_CURRENT 3
+#define UHF_PORT_RESET 4
+#define UHF_PORT_POWER 8
+#define UHF_PORT_LOW_SPEED 9
+#define UHF_C_PORT_CONNECTION 16
+#define UHF_C_PORT_ENABLE 17
+#define UHF_C_PORT_SUSPEND 18
+#define UHF_C_PORT_OVER_CURRENT 19
+#define UHF_C_PORT_RESET 20
+
+typedef struct {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord bHubCharacteristics;
+#define UHD_PWR 0x03
+#define UHD_PWR_GANGED 0x00
+#define UHD_PWR_INDIVIDUAL 0x01
+#define UHD_PWR_NO_SWITCH 0x02
+#define UHD_COMPOUND 0x04
+#define UHD_OC 0x18
+#define UHD_OC_GLOBAL 0x00
+#define UHD_OC_INDIVIDUAL 0x08
+#define UHD_OC_NONE 0x10
+ uByte bPwrOn2PwrGood; /* delay in 2 ms units */
+#define UHD_PWRON_FACTOR 2
+ uByte bHubContrCurrent;
+ uByte DeviceRemovable[1];
+ /* this is only correct with 1-7 ports on the hub */
+ uByte PortPowerCtrlMask[3];
+} usb_hub_descriptor_t;
+#define USB_HUB_DESCRIPTOR_SIZE 9
+
+typedef struct {
+ uWord wStatus;
+/* Device status flags */
+#define UDS_SELF_POWERED 0x0001
+#define UDS_REMOTE_WAKEUP 0x0002
+} usb_status_t;
+
+typedef struct {
+ uWord wHubStatus;
+#define UHS_LOCAL_POWER 0x0001
+#define UHS_OVER_CURRENT 0x0002
+ uWord wHubChange;
+} usb_hub_status_t;
+
+typedef struct {
+ uWord wPortStatus;
+#define UPS_CURRENT_CONNECT_STATUS 0x0001
+#define UPS_PORT_ENABLED 0x0002
+#define UPS_SUSPEND 0x0004
+#define UPS_OVERCURRENT_INDICATOR 0x0008
+#define UPS_RESET 0x0010
+#define UPS_PORT_POWER 0x0100
+#define UPS_LOW_SPEED 0x0200
+ uWord wPortChange;
+#define UPS_C_CONNECT_STATUS 0x0001
+#define UPS_C_PORT_ENABLED 0x0002
+#define UPS_C_SUSPEND 0x0004
+#define UPS_C_OVERCURRENT_INDICATOR 0x0008
+#define UPS_C_PORT_RESET 0x0010
+} usb_port_status_t;
+
+#define UDESC_CS_DEVICE 0x21
+#define UDESC_CS_CONFIG 0x22
+#define UDESC_CS_STRING 0x23
+#define UDESC_CS_INTERFACE 0x24
+#define UDESC_CS_ENDPOINT 0x25
+
+#define UDESC_HUB 0x29
+
+#define UCLASS_UNSPEC 0
+#define UCLASS_AUDIO 1
+#define USUBCLASS_AUDIOCONTROL 1
+#define USUBCLASS_AUDIOSTREAM 2
+#define UCLASS_HID 3
+#define USUBCLASS_BOOT 1
+#define UCLASS_PRINTER 7
+#define USUBCLASS_PRINTER 1
+#define UPROTO_PRINTER_UNI 1
+#define UPROTO_PRINTER_BI 2
+#define UCLASS_HUB 9
+#define USUBCLASS_HUB 1
+
+#define USB_HUB_MAX_DEPTH 5
+
+#define USB_PORT_RESET_DELAY 10 /* ms */
+#define USB_PORT_POWERUP_DELAY 100 /* ms */
+#define USB_POWER_SETTLE 100 /* ms */
+
+#define USB_MIN_POWER 100 /* mA */
+#define USB_MAX_POWER 500 /* mA */
+
+
+#define USB_RESET_DELAY 100 /* ms XXX?*/
+#define USB_RESUME_DELAY 10 /* ms XXX?*/
+
+/*** ioctl() related stuff ***/
+
+struct usb_ctl_request {
+ int addr;
+ usb_device_request_t request;
+ void *data;
+};
+
+struct usb_all_desc {
+ u_char data[1024]; /* filled data size will vary */
+};
+
+struct usb_ctl_report_desc {
+ int size;
+ u_char data[1024]; /* filled data size will vary */
+};
+
+struct usb_device_info {
+ uByte addr; /* device address */
+ char product[USB_MAX_STRING_LEN];
+ char vendor[USB_MAX_STRING_LEN];
+ char revision[8];
+ uByte class;
+ uByte config;
+ uByte lowspeed;
+ int power; /* power consumption in mA, 0 if selfpowered */
+ int nports;
+ uByte ports[16]; /* hub only: addresses of devices on ports */
+#define USB_PORT_ENABLED 0xff
+#define USB_PORT_SUSPENDED 0xfe
+#define USB_PORT_POWERED 0xfd
+#define USB_PORT_DISABLED 0xfc
+};
+
+struct usb_ctl_report {
+ int report;
+ u_char data[1024]; /* filled data size will vary */
+};
+
+struct usb_device_stats {
+ u_long requests[4]; /* indexed by transfer type UE_* */
+};
+
+/* USB controller */
+#define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request)
+#define USB_SETDEBUG _IOW ('U', 2, int)
+#define USB_DISCOVER _IO ('U', 3)
+#define USB_DEVICEINFO _IOWR('U', 4, struct usb_device_info)
+#define USB_DEVICESTATS _IOR ('U', 5, struct usb_device_stats)
+
+/* Generic HID device */
+#define USB_GET_REPORT_DESC _IOR ('U', 21, struct usb_ctl_report_desc)
+#define USB_SET_IMMED _IOW ('U', 22, int)
+#define USB_GET_REPORT _IOWR('U', 23, struct usb_ctl_report)
+
+/* Generic USB device */
+#define USB_SET_CONFIG _IOW ('U', 100, int)
+#define USB_SET_INTERFACE _IOW ('U', 101, int)
+#define USB_GET_DEVICE_DESC _IOR ('U', 102, usb_device_descriptor_t)
+#define USB_GET_CONFIG_DESC _IOR ('U', 103, usb_config_descriptor_t)
+#define USB_GET_INTERFACE_DESC _IOR ('U', 104, usb_interface_descriptor_t)
+#define USB_GET_ALL_DESC _IOR ('U', 105, struct usb_all_desc)
+
+
+#endif /* _USB_H_ */
diff --git a/sys/dev/usb/usb_if.m b/sys/dev/usb/usb_if.m
new file mode 100644
index 0000000..f9cb673
--- /dev/null
+++ b/sys/dev/usb/usb_if.m
@@ -0,0 +1,11 @@
+# USB interface description
+#
+
+INTERFACE usb;
+
+# The device should start probing for new children again
+#
+METHOD int reconfigure {
+ device_t dev;
+};
+
diff --git a/sys/dev/usb/usb_mem.h b/sys/dev/usb/usb_mem.h
new file mode 100644
index 0000000..7391d18
--- /dev/null
+++ b/sys/dev/usb/usb_mem.h
@@ -0,0 +1,91 @@
+/* $NetBSD: usb_mem.h,v 1.1 1998/07/24 21:09:08 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if defined(__NetBSD__)
+typedef struct usb_block_dma {
+ bus_dma_tag_t tag;
+ bus_dmamap_t map;
+ caddr_t kaddr;
+ bus_dma_segment_t segs[1];
+ int nsegs;
+ size_t size;
+ size_t align;
+ int fullblock;
+ LIST_ENTRY(usb_block_dma) next;
+} usb_dma_block_t;
+
+typedef struct {
+ usb_dma_block_t *block;
+ u_int offs;
+} usb_dma_t;
+
+#define DMAADDR(dma) ((dma)->block->segs[0].ds_addr + (dma)->offs)
+#define KERNADDR(dma) ((void *)((dma)->block->kaddr + (dma)->offs))
+
+usbd_status usb_allocmem __P((bus_dma_tag_t, size_t, size_t, usb_dma_t *));
+void usb_freemem __P((bus_dma_tag_t, usb_dma_t *));
+
+#else
+
+/* FreeBSD does not have special functions for dma memory, so let's keep it
+ * simple for now.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/pmap.h> /* for vtophys */
+
+
+
+typedef void * usb_dma_t;
+
+#define usb_allocmem(t,s,a,p) (*(p) = malloc(s, M_USB, M_NOWAIT), (*(p) == NULL? USBD_NOMEM: USBD_NORMAL_COMPLETION))
+#define usb_freemem(t,p) (free(*(p), M_USB))
+
+#define DMAADDR(dma) (vtophys(*(dma)))
+#define KERNADDR(dma) ((void *) *(dma))
+#endif
+
diff --git a/sys/dev/usb/usb_port.h b/sys/dev/usb/usb_port.h
new file mode 100644
index 0000000..df66674
--- /dev/null
+++ b/sys/dev/usb/usb_port.h
@@ -0,0 +1,79 @@
+/* Macro's to cope with the differences between NetBSD and FreeBSD
+ */
+
+/*
+ * NetBSD
+ *
+ */
+
+#if defined(__NetBSD__)
+#include "opt_usbverbose.h"
+
+#define DEVICE_NAME(bdev) \
+ printf("%s: ", (bdev).dv_xname)
+#define DEVICE_MSG(bdev
+
+typedef struct device bdevice; /* base device */
+
+
+
+/*
+ * FreeBSD
+ *
+ */
+
+#elif defined(__FreeBSD__)
+#include "opt_usb.h"
+#define DEVICE_NAME(bdev) \
+ printf("%s%d: ", \
+ device_get_name(bdev), device_get_unit(bdev))
+
+/* XXX Change this when FreeBSD has memset
+ */
+#define memset(d, v, s) \
+ do{ \
+ if ((v) == 0) \
+ bzero((d), (s)); \
+ else \
+ panic("Non zero filler for memset, cannot handle!"); \
+ } while (0)
+
+/* XXX can't we put this somehow into a typedef? */
+#define bdevice device_t /* base device */
+
+#define USB_MODULE(name, driver, devclass) \
+ DRIVER_MODULE((name), "usb", (driver), (devclass), usb_driver_load, 0)
+#endif
+
+
+
+
+/*
+ * General
+ *
+ */
+
+#define DEVICE_MSG(bdev, s) (DEVICE_NAME(bdev), printf s)
+#define DEVICE_ERROR(bdev, s) DEVICE_MSG(bdev, s)
+
+
+/* Returns from attach for NetBSD vs. FreeBSD
+ */
+
+/* Error returns */
+#if defined(__NetBSD__)
+#define ATTACH_ERROR_RETURN return
+#define ATTACH_SUCCESS_RETURN return
+#elif defined(__FreeBSD__)
+#define ATTACH_ERROR_RETURN return ENXIO
+#define ATTACH_SUCCESS_RETURN return 0
+#endif
+
+
+/*
+ * The debugging subsystem
+ */
+
+/* XXX to be filled in
+ */
+
diff --git a/sys/dev/usb/usb_quirks.c b/sys/dev/usb/usb_quirks.c
new file mode 100644
index 0000000..e707f56
--- /dev/null
+++ b/sys/dev/usb/usb_quirks.c
@@ -0,0 +1,89 @@
+/* $NetBSD: usb_quirks.c,v 1.1 1998/07/12 19:52:00 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#endif
+#include <sys/select.h>
+
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+struct usbd_quirk_entry {
+ u_int16_t idVendor;
+ u_int16_t idProduct;
+ u_int16_t bcdDevice;
+ struct usbd_quirks quirks;
+} quirks[] = {
+ { USB_VENDOR_GENIUS, USB_PRODUCT_GENIUS_NICHE, 0x100, { UQ_NO_SET_PROTO}},
+ { USB_VENDOR_INSIDEOUT,USB_PRODUCT_INSIDEOUT_EDGEPORT4,
+ 0x094, { UQ_SWAP_UNICODE}},
+ { USB_VENDOR_UNIXTAR, USB_PRODUCT_UNIXTAR_UTUSB41, 0x100, { UQ_HUB_POWER }},
+ { 0, 0, 0, { 0 } }
+};
+
+struct usbd_quirks usbd_no_quirk = { 0 };
+
+struct usbd_quirks *
+usbd_find_quirk(d)
+ usb_device_descriptor_t *d;
+{
+ struct usbd_quirk_entry *t;
+
+ for (t = quirks; t->idVendor != 0; t++) {
+ if (t->idVendor == UGETW(d->idVendor) &&
+ t->idProduct == UGETW(d->idProduct) &&
+ t->bcdDevice == UGETW(d->bcdDevice))
+ break;
+ }
+#ifdef USB_DEBUG
+ { extern int usbdebug;
+ if (usbdebug && t->quirks.uq_flags)
+ printf("quirk %d/%d/%x: %d\n",
+ UGETW(d->idVendor), UGETW(d->idProduct),
+ UGETW(d->bcdDevice), t->quirks.uq_flags);
+ }
+#endif
+ return (&t->quirks);
+}
diff --git a/sys/dev/usb/usb_quirks.h b/sys/dev/usb/usb_quirks.h
new file mode 100644
index 0000000..6d9e822
--- /dev/null
+++ b/sys/dev/usb/usb_quirks.h
@@ -0,0 +1,49 @@
+/* $NetBSD: usb_quirks.h,v 1.1 1998/07/12 19:52:00 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+struct usbd_quirks {
+ u_int32_t uq_flags; /* Device problems: */
+#define UQ_NO_SET_PROTO 0x01 /* cannot handle SET PROTOCOL */
+#define UQ_SWAP_UNICODE 0x02 /* has some Unicode strings swapped. */
+#define UQ_HUB_POWER 0x04 /* does not respond correctly to get
+ device status; use get hub status. */
+};
+
+extern struct usbd_quirks usbd_no_quirk;
+
+struct usbd_quirks *usbd_find_quirk __P((usb_device_descriptor_t *));
diff --git a/sys/dev/usb/usb_subr.c b/sys/dev/usb/usb_subr.c
new file mode 100644
index 0000000..63fc7e4
--- /dev/null
+++ b/sys/dev/usb/usb_subr.c
@@ -0,0 +1,880 @@
+/* $NetBSD: usb_subr.c,v 1.7 1998/08/02 22:30:53 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+#include <sys/select.h>
+
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+#define delay(d) DELAY(d)
+#endif
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+static usbd_status usbd_set_config __P((usbd_device_handle, int));
+char *usbd_get_string __P((usbd_device_handle, int, char *));
+int usbd_getnewaddr __P((usbd_bus_handle bus));
+int usbd_print __P((void *aux, const char *pnp));
+#if defined(__NetBSD__)
+int usbd_submatch __P((struct device *, struct cfdata *cf, void *));
+#endif
+usb_interface_descriptor_t *usbd_find_idesc __P((usb_config_descriptor_t *cd,
+ int ino, int ano));
+usbd_status usbd_fill_iface_data __P((usbd_device_handle dev, int i, int a));
+void usbd_free_iface_data __P((usbd_device_handle dev, int ifcno));
+void usbd_kill_pipe __P((usbd_pipe_handle));
+static usbd_status usbd_probe_and_attach(bdevice *parent, usbd_device_handle dev);
+
+#ifdef USBVERBOSE
+typedef u_int16_t usb_vendor_id_t;
+typedef u_int16_t usb_product_id_t;
+
+/*
+ * Descriptions of of known vendors and devices ("products").
+ */
+struct usb_knowndev {
+ usb_vendor_id_t vendor;
+ usb_product_id_t product;
+ int flags;
+ char *vendorname, *productname;
+};
+#define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */
+
+#include <dev/usb/usbdevs_data.h>
+#endif /* USBVERBOSE */
+
+
+char *
+usbd_get_string(dev, si, buf)
+ usbd_device_handle dev;
+ int si;
+ char *buf;
+{
+ int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE;
+ usb_device_request_t req;
+ usb_string_descriptor_t us;
+ char *s;
+ int i, n;
+ u_int16_t c;
+ usbd_status r;
+ int lang; /* NWH */
+
+ if (si == 0)
+ return 0;
+
+ /* NWH added fetching of language
+ * See 9.6.5 (spec v1.0)
+ */
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_STRING, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 4); /* only first word in bString */
+ r = usbd_do_request(dev, &req, &us);
+ if (r != USBD_NORMAL_COMPLETION)
+ return 0;
+ lang = UGETW(us.bString[0]);
+ /* NWH end */
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_STRING, si);
+ USETW(req.wIndex, lang);
+ USETW(req.wLength, 1); /* only size byte first */
+ r = usbd_do_request(dev, &req, &us);
+ if (r != USBD_NORMAL_COMPLETION)
+ return 0;
+ USETW(req.wLength, us.bLength); /* the whole string */
+ r = usbd_do_request(dev, &req, &us);
+ if (r != USBD_NORMAL_COMPLETION)
+ return 0;
+ s = buf;
+ n = us.bLength / 2 - 1;
+ for (i = 0; i < n; i++) {
+ c = UGETW(us.bString[i]);
+ /* Convert from Unicode, handle buggy strings. */
+ if ((c & 0xff00) == 0)
+ *s++ = c;
+ else if ((c & 0x00ff) == 0 && swap)
+ *s++ = c >> 8;
+ else
+ *s++ = '?';
+ }
+ *s++ = 0;
+ return buf;
+}
+
+void
+usbd_devinfo_vp(dev, v, p)
+ usbd_device_handle dev;
+ char *v, *p;
+{
+ usb_device_descriptor_t *udd = &dev->ddesc;
+ char *vendor = 0, *product = 0;
+#ifdef USBVERBOSE
+ struct usb_knowndev *kdp;
+#endif
+
+ if (!dev) {
+ DPRINTF(("usbd_devinfo_vp: dev not set\n"));
+ return;
+ }
+ if (!v) {
+ DPRINTF(("usbd_devinfo_vp: v not set\n"));
+ return;
+ }
+ if (!p) {
+ DPRINTF(("usbd_devinfo_vp: p not set\n"));
+ return;
+ }
+
+ vendor = usbd_get_string(dev, udd->iManufacturer, v);
+ product = usbd_get_string(dev, udd->iProduct, p);
+#ifdef USBVERBOSE
+ if (!vendor) {
+ for(kdp = usb_knowndevs;
+ kdp->vendorname != NULL;
+ kdp++) {
+ if (kdp->vendor == UGETW(udd->idVendor) &&
+ (kdp->product == UGETW(udd->idProduct) ||
+ (kdp->flags & USB_KNOWNDEV_NOPROD) != 0))
+ break;
+ }
+ if (kdp->vendorname == NULL)
+ vendor = product = NULL;
+ else {
+ vendor = kdp->vendorname;
+ product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ?
+ kdp->productname : NULL;
+ }
+ }
+#endif
+ if (vendor)
+ strcpy(v, vendor);
+ else
+ sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor));
+ if (product)
+ strcpy(p, product);
+ else
+ sprintf(p, "product 0x%04x", UGETW(udd->idProduct));
+}
+
+int
+usbd_printBCD(cp, bcd)
+ char *cp;
+ int bcd;
+{
+ return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff));
+}
+
+void
+usbd_devinfo(dev, showclass, cp)
+ usbd_device_handle dev;
+ int showclass;
+ char *cp;
+{
+ usb_device_descriptor_t *udd = &dev->ddesc;
+ char vendor[USB_MAX_STRING_LEN];
+ char product[USB_MAX_STRING_LEN];
+ int bcdDevice, bcdUSB;
+
+ usbd_devinfo_vp(dev, vendor, product);
+ cp += sprintf(cp, "%s", vendor);
+ cp += sprintf(cp, " %s", product);
+ if (showclass)
+ cp += sprintf(cp, " (class %d/%d)",
+ udd->bDeviceClass, udd->bDeviceSubClass);
+ bcdUSB = UGETW(udd->bcdUSB);
+ bcdDevice = UGETW(udd->bcdDevice);
+ cp += sprintf(cp, " (rev ");
+ cp += usbd_printBCD(cp, bcdUSB);
+ *cp++ = '/';
+ cp += usbd_printBCD(cp, bcdDevice);
+ *cp++ = ')';
+ cp += sprintf(cp, " (addr %d)", dev->address);
+}
+
+/* Delay for a certain number of ms */
+void
+usbd_delay_ms(bus, ms)
+ usbd_bus_handle bus;
+ int ms;
+{
+ /* Wait at least two clock ticks so we know the time has passed. */
+ if (bus->use_polling)
+ delay((ms+1) * 1000);
+ else
+ tsleep(&ms, PRIBIO, "usbdly", (ms*hz+999)/1000 + 1);
+}
+
+usbd_status
+usbd_reset_port(dev, port, ps)
+ usbd_device_handle dev;
+ int port;
+ usb_port_status_t *ps;
+{
+ usb_device_request_t req;
+ usbd_status r;
+ int n;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, UHF_PORT_RESET);
+ USETW(req.wIndex, port);
+ USETW(req.wLength, 0);
+ r = usbd_do_request(dev, &req, 0);
+ DPRINTFN(1,("usbd_reset_port: port %d reset done, error=%d\n",
+ port, r));
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ n = 10;
+ do {
+ /* Wait for device to recover from reset. */
+ usbd_delay_ms(dev->bus, USB_PORT_RESET_DELAY);
+ r = usbd_get_port_status(dev, port, ps);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("usbd_reset_port: get status failed %d\n",r));
+ return (r);
+ }
+ } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0);
+ if (n == 0) {
+ printf("usbd_reset_port: timeout\n");
+ return (USBD_IOERROR);
+ }
+ r = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET);
+#ifdef USB_DEBUG
+ if (r != USBD_NORMAL_COMPLETION)
+ DPRINTF(("usbd_reset_port: clear port feature failed %d\n",r));
+#endif
+ return (r);
+}
+
+usb_interface_descriptor_t *
+usbd_find_idesc(cd, ino, ano)
+ usb_config_descriptor_t *cd;
+ int ino;
+ int ano;
+{
+ char *p = (char *)cd;
+ char *end = p + UGETW(cd->wTotalLength);
+ usb_interface_descriptor_t *d;
+
+ for (; p < end; p += d->bLength) {
+ d = (usb_interface_descriptor_t *)p;
+ if (p + d->bLength <= end &&
+ d->bDescriptorType == UDESC_INTERFACE &&
+ d->bInterfaceNumber == ino && d->bAlternateSetting == ano)
+ return (d);
+ }
+ return (0);
+}
+
+usbd_status
+usbd_fill_iface_data(dev, ino, ano)
+ usbd_device_handle dev;
+ int ino;
+ int ano;
+{
+ usbd_interface_handle ifc = &dev->ifaces[ino];
+ usb_endpoint_descriptor_t *ed;
+ char *p, *end;
+ int endpt, nendpt;
+ usbd_status r;
+
+ DPRINTFN(5,("usbd_fill_iface_data: ino=%d ano=%d\n", ino, ano));
+ ifc->device = dev;
+ ifc->state = USBD_INTERFACE_ACTIVE;
+ ifc->idesc = usbd_find_idesc(dev->cdesc, ino, ano);
+ if (ifc->idesc == 0)
+ return (USBD_INVAL);
+ nendpt = ifc->idesc->bNumEndpoints;
+ DPRINTFN(10,("usbd_fill_iface_data: found idesc n=%d\n", nendpt));
+ if (nendpt != 0) {
+ ifc->endpoints = malloc(nendpt * sizeof(struct usbd_endpoint),
+ M_USB, M_NOWAIT);
+ if (ifc->endpoints == 0)
+ return (USBD_NOMEM);
+ } else
+ ifc->endpoints = 0;
+ ifc->priv = 0;
+ p = (char *)ifc->idesc + ifc->idesc->bLength;
+ end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength);
+ for (endpt = 0; endpt < nendpt; endpt++) {
+ DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt));
+ for (; p < end; p += ed->bLength) {
+ ed = (usb_endpoint_descriptor_t *)p;
+ DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p len=%d type=%d\n",
+ p, end, ed->bLength, ed->bDescriptorType));
+ if (p + ed->bLength <= end &&
+ ed->bDescriptorType == UDESC_ENDPOINT)
+ goto found;
+ if (ed->bDescriptorType == UDESC_INTERFACE)
+ break;
+ }
+ r = USBD_INVAL;
+ goto bad;
+ found:
+ ifc->endpoints[endpt].edesc = ed;
+ ifc->endpoints[endpt].state = USBD_ENDPOINT_ACTIVE;
+ ifc->endpoints[endpt].refcnt = 0;
+ ifc->endpoints[endpt].toggle = 0;
+ }
+ LIST_INIT(&ifc->pipes);
+ return (USBD_NORMAL_COMPLETION);
+ bad:
+ free(ifc->endpoints, M_USB);
+ return (r);
+}
+
+void
+usbd_free_iface_data(dev, ifcno)
+ usbd_device_handle dev;
+ int ifcno;
+{
+ usbd_interface_handle ifc = &dev->ifaces[ifcno];
+ if (ifc->endpoints)
+ free(ifc->endpoints, M_USB);
+}
+
+static usbd_status
+usbd_set_config(dev, conf)
+ usbd_device_handle dev;
+ int conf;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_CONFIG;
+ USETW(req.wValue, conf);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_set_config_no(dev, no, msg)
+ usbd_device_handle dev;
+ int no;
+ int msg;
+{
+ usb_status_t ds;
+ usb_hub_status_t hs;
+ usb_config_descriptor_t cd, *cdp;
+ usbd_status r;
+ int ifcno, nifc, len, selfpowered, power;
+
+ DPRINTFN(5, ("usbd_set_config_no: dev=%p no=%d\n", dev, no));
+
+ /* XXX check that all interfaces are idle */
+ if (dev->config != 0) {
+ DPRINTF(("usbd_set_config_no: free old config\n"));
+ /* Free all configuration data structures. */
+ nifc = dev->cdesc->bNumInterface;
+ for (ifcno = 0; ifcno < nifc; ifcno++)
+ usbd_free_iface_data(dev, ifcno);
+ free(dev->ifaces, M_USB);
+ free(dev->cdesc, M_USB);
+ dev->ifaces = 0;
+ dev->cdesc = 0;
+ dev->config = 0;
+ dev->state = USBD_DEVICE_ADDRESSED;
+ }
+
+ /* Figure out what config number to use. */
+ r = usbd_get_config_desc(dev, no, &cd);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ len = UGETW(cd.wTotalLength);
+ cdp = malloc(len, M_USB, M_NOWAIT);
+ if (cdp == 0)
+ return (USBD_NOMEM);
+ r = usbd_get_desc(dev, UDESC_CONFIG, no, len, cdp);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto bad;
+ selfpowered = 0;
+ if (cdp->bmAttributes & UC_SELF_POWERED) {
+ /* May be self powered. */
+ if (cdp->bmAttributes & UC_BUS_POWERED) {
+ /* Must ask device. */
+ if (dev->quirks->uq_flags & UQ_HUB_POWER) {
+ /* Buggy hub, use hub descriptor. */
+ r = usbd_get_hub_status(dev, &hs);
+ if (r == USBD_NORMAL_COMPLETION &&
+ !(UGETW(hs.wHubStatus) & UHS_LOCAL_POWER))
+ selfpowered = 1;
+ } else {
+ r = usbd_get_device_status(dev, &ds);
+ if (r == USBD_NORMAL_COMPLETION &&
+ (UGETW(ds.wStatus) & UDS_SELF_POWERED))
+ selfpowered = 1;
+ }
+ DPRINTF(("usbd_set_config_no: status=0x%04x, error=%d\n",
+ UGETW(ds.wStatus), r));
+ } else
+ selfpowered = 1;
+ }
+ DPRINTF(("usbd_set_config_no: (addr %d) attr=0x%02x, selfpowered=%d, power=%d, powerquirk=%x\n",
+ dev->address, cdp->bmAttributes,
+ selfpowered, cdp->bMaxPower * 2,
+ dev->quirks->uq_flags & UQ_HUB_POWER));
+#ifdef USB_DEBUG
+ if (!dev->powersrc) {
+ printf("usbd_set_config_no: No power source?\n");
+ return (EIO);
+ }
+#endif
+ power = cdp->bMaxPower * 2;
+ if (power > dev->powersrc->power) {
+ /* XXX print nicer message. */
+ if (msg)
+ DEVICE_ERROR(dev->bus->bdev,
+ ("device addr %d (config %d) exceeds power budget, %d mA > %d mA\n",
+ dev->address,
+ cdp->bConfigurationValue,
+ power, dev->powersrc->power));
+ r = USBD_NO_POWER;
+ goto bad;
+ }
+ dev->power = power;
+ dev->self_powered = selfpowered;
+
+ r = usbd_set_config(dev, cdp->bConfigurationValue);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("usbd_set_config_no: setting config=%d failed, error=%d\n",
+ cdp->bConfigurationValue, r));
+ goto bad;
+ }
+ DPRINTF(("usbd_set_config_no: setting new config %d\n",
+ cdp->bConfigurationValue));
+ nifc = cdp->bNumInterface;
+ dev->ifaces = malloc(nifc * sizeof(struct usbd_interface),
+ M_USB, M_NOWAIT);
+ if (dev->ifaces == 0) {
+ r = USBD_NOMEM;
+ goto bad;
+ }
+ DPRINTFN(5,("usbd_set_config_no: dev=%p cdesc=%p\n", dev, cdp));
+ dev->cdesc = cdp;
+ dev->config = cdp->bConfigurationValue;
+ dev->state = USBD_DEVICE_CONFIGURED;
+ for (ifcno = 0; ifcno < nifc; ifcno++) {
+ r = usbd_fill_iface_data(dev, ifcno, 0);
+ if (r != USBD_NORMAL_COMPLETION) {
+ while (--ifcno >= 0)
+ usbd_free_iface_data(dev, ifcno);
+ goto bad;
+ }
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ free(cdp, M_USB);
+ return (r);
+}
+
+/* XXX add function for alternate settings */
+
+usbd_status
+usbd_setup_pipe(dev, iface, ep, pipe)
+ usbd_device_handle dev;
+ usbd_interface_handle iface;
+ struct usbd_endpoint *ep;
+ usbd_pipe_handle *pipe;
+{
+ usbd_pipe_handle p;
+ usbd_status r;
+
+ *pipe = NULL;
+
+ DPRINTFN(1,("usbd_setup_pipe: dev=%p iface=%p ep=%p pipe=%p\n",
+ dev, iface, ep, pipe));
+ p = malloc(dev->bus->pipe_size, M_USB, M_NOWAIT);
+ if (p == 0)
+ return (USBD_NOMEM);
+ p->device = dev;
+ p->iface = iface;
+ p->state = USBD_PIPE_ACTIVE;
+ p->endpoint = ep;
+ ep->refcnt++;
+ p->refcnt = 1;
+ p->intrreqh = 0;
+ p->running = 0;
+ SIMPLEQ_INIT(&p->queue);
+ r = dev->bus->open_pipe(p);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("usbd_setup_pipe: endpoint=%d failed, error=%d\n",
+ ep->edesc->bEndpointAddress, r));
+ free(p, M_USB);
+ return (r);
+ }
+ *pipe = p;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Abort the device control pipe. */
+void
+usbd_kill_pipe(pipe)
+ usbd_pipe_handle pipe;
+{
+ pipe->methods->close(pipe);
+ pipe->endpoint->refcnt--;
+ free(pipe, M_USB);
+}
+
+int
+usbd_getnewaddr(bus)
+ usbd_bus_handle bus;
+{
+ int addr;
+
+ for (addr = 1; addr < USB_MAX_DEVICES; addr++)
+ if (bus->devices[addr] == 0)
+ return (addr);
+ return (-1);
+}
+
+
+/* NWH separated out the probe and attach code
+ */
+static usbd_status
+usbd_probe_and_attach(parent, dev)
+ bdevice *parent;
+ usbd_device_handle dev;
+{
+ struct usb_attach_arg uaa;
+ usb_device_descriptor_t *dd = &dev->ddesc;
+ int r, found, i, confi;
+
+#if defined(__FreeBSD__)
+ dev->bdev = device_add_child(*parent, NULL, -1, &uaa);
+ if (!dev->bdev) {
+ DEVICE_ERROR(dev->bus->bdev, ("Device creation failed\n"));
+ return ENXIO;
+ }
+#endif
+
+ uaa.device = dev;
+ uaa.iface = 0;
+ uaa.usegeneric = 0;
+
+ /* First try with device specific drivers. */
+#if defined(__NetBSD__)
+ if (config_found_sm(parent, &uaa, usbd_print, usbd_submatch) != 0)
+#elif defined(__FreeBSD__)
+ if (device_probe_and_attach(dev->bdev) == 0)
+#endif
+ return (USBD_NORMAL_COMPLETION);
+
+ DPRINTF(("usbd_new_device: no device driver found\n"));
+
+ /* Next try with interface drivers. */
+ for (confi = 0; confi < dd->bNumConfigurations; confi++) {
+ r = usbd_set_config_no(dev, confi, 1);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DEVICE_ERROR(*parent, ("set config failed, r=%d\n", r));
+ return r;
+ }
+ for (found = i = 0; i < dev->cdesc->bNumInterface; i++) {
+ uaa.iface = &dev->ifaces[i];
+#if defined(__NetBSD__)
+ if (config_found_sm(parent, &uaa, usbd_print,
+ usbd_submatch))
+#elif defined(__FreeBSD__)
+ if (device_probe_and_attach(dev->bdev) == 0)
+#endif
+ found++;
+ }
+ if (found != 0)
+ return (USBD_NORMAL_COMPLETION);
+ }
+ /* No interfaces were attach in any of the configurations. */
+ if (dd->bNumConfigurations > 0)
+ usbd_set_config_no(dev, 0, 0);
+
+ DPRINTF(("usbd_new_device: no interface drivers found\n"));
+
+ /* Finally try the generic driver. */
+ uaa.iface = 0;
+ uaa.usegeneric = 1;
+#if defined(__NetBSD__)
+ if (config_found_sm(parent, &uaa, usbd_print, usbd_submatch) != 0)
+ return (USBD_NORMAL_COMPLETION);
+#elif defined(__FreeBSD__)
+ if (device_probe_and_attach(dev->bdev) == 0)
+ return (USBD_NORMAL_COMPLETION);
+#endif
+
+ /* generic attach failed, but leave the device as it is
+ * we just did not find any drivers, that's all. the device is
+ * fully operational and not harming anyone
+ */
+ DPRINTF(("usbd_new_device: generic attach failed\n"));
+ return USBD_NORMAL_COMPLETION;
+}
+
+
+
+/*
+ * Called when a new device has been put in the powered state,
+ * but not yet in the addressed state.
+ * Get initial descriptor, set the address, get full descriptor,
+ * and attach a driver.
+ */
+usbd_status
+usbd_new_device(parent, bus, depth, lowspeed, port, up)
+ bdevice *parent;
+ usbd_bus_handle bus;
+ int depth;
+ int lowspeed;
+ int port;
+ struct usbd_port *up;
+{
+ usbd_device_handle dev;
+ usb_device_descriptor_t *dd;
+ usbd_status r;
+ int addr;
+ int i;
+
+ DPRINTF(("usbd_new_device bus=%p depth=%d lowspeed=%d\n",
+ bus, depth, lowspeed));
+ addr = usbd_getnewaddr(bus);
+ if (addr < 0) {
+ DEVICE_ERROR(bus->bdev, ("No free USB addresses, new device ignored.\n"));
+ return (USBD_NO_ADDR);
+ }
+
+ dev = malloc(sizeof *dev, M_USB, M_NOWAIT);
+ if (dev == 0)
+ return (USBD_NOMEM);
+ memset(dev, 0, sizeof(*dev));
+
+ dev->bus = bus;
+
+ /* Set up default endpoint handle. */
+ dev->def_ep.edesc = &dev->def_ep_desc;
+ dev->def_ep.state = USBD_ENDPOINT_ACTIVE;
+ dev->def_ep.refcnt = 0;
+ dev->def_ep.toggle = 0; /* XXX */
+
+ /* Set up default endpoint descriptor. */
+ dev->def_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE;
+ dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT;
+ dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
+ dev->def_ep_desc.bmAttributes = UE_CONTROL;
+ USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET);
+ dev->def_ep_desc.bInterval = 0;
+
+ dev->state = USBD_DEVICE_DEFAULT;
+ dev->quirks = &usbd_no_quirk;
+ dev->address = USB_START_ADDR;
+ dev->ddesc.bMaxPacketSize = 0;
+ dev->lowspeed = lowspeed != 0;
+ dev->depth = depth;
+ dev->powersrc = up;
+
+#if defined(__FreeBSD__)
+ dev->bdev = NULL;
+#endif
+
+ /* Establish the the default pipe. */
+ r = usbd_setup_pipe(dev, 0, &dev->def_ep, &dev->default_pipe);
+ if (r != USBD_NORMAL_COMPLETION) {
+ usbd_remove_device(dev, up);
+ return r;
+ }
+
+ up->device = dev;
+ dd = &dev->ddesc;
+ /* Try a few times in case the device is slow (i.e. outside specs.) */
+ for (i = 0; i < 5; i++) {
+ /* Get the first 8 bytes of the device descriptor. */
+ r = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd);
+ if (r == USBD_NORMAL_COMPLETION)
+ break;
+ usbd_delay_ms(dev->bus, 200);
+ }
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc failed\n",
+ addr));
+ usbd_remove_device(dev, up);
+ return r;
+ }
+
+ DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, subclass=%d, protocol=%d, maxpacket=%d, ls=%d\n",
+ addr, UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass,
+ dd->bDeviceProtocol, dd->bMaxPacketSize, dev->lowspeed));
+
+ USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize);
+
+ /* Get the full device descriptor. */
+ r = usbd_get_device_desc(dev, dd);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTFN(-1, ("usbd_new_device: addr=%d, getting full desc failed\n", addr));
+ usbd_remove_device(dev, up);
+ return r;
+ }
+
+ /* Figure out what's wrong with this device. */
+ dev->quirks = usbd_find_quirk(dd);
+
+ /* Set the address */
+ r = usbd_set_address(dev, addr);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTFN(-1,("usbd_new_device: set address %d failed\n",addr));
+ usbd_remove_device(dev, up);
+ return USBD_SET_ADDR_FAILED;
+ }
+ dev->address = addr; /* New device address now */
+ dev->state = USBD_DEVICE_ADDRESSED;
+ bus->devices[addr] = dev;
+
+ /* Assume 100mA bus powered for now. Changed when configured. */
+ dev->power = USB_MIN_POWER;
+ dev->self_powered = 0;
+
+ DPRINTF(("usbd_new_device: new dev (addr %d), dev=%p, parent=%p\n",
+ addr, dev, parent));
+
+ r = usbd_probe_and_attach(parent, dev);
+ if (r) {
+ usbd_remove_device(dev, up);
+ return r;
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usbd_remove_device(dev, up)
+ usbd_device_handle dev;
+ struct usbd_port *up;
+{
+ DPRINTF(("usbd_remove_device: %p\n", dev));
+
+#if defined(__NetBSD__)
+ /* XXX bit of a hack, only for hubs the detach is called
+ * the code should register a detach function and use that one
+ * to detach a device porperly
+ */
+ if (dev->bdev && dev->hub)
+ uhub_detach(dev->hub->hubdata);
+#elif defined(__FreeBSD__)
+ if (dev->bdev)
+ device_delete_child(device_get_parent(dev->bdev), dev->bdev);
+#endif
+
+ if (dev->default_pipe)
+ usbd_kill_pipe(dev->default_pipe);
+ up->device = 0;
+ dev->bus->devices[dev->address] = 0;
+
+ free(dev, M_USB);
+}
+
+#if defined(__NetBSD__)
+int
+usbd_print(aux, pnp)
+ void *aux;
+ const char *pnp;
+{
+ struct usb_attach_arg *uaa = aux;
+ char devinfo[1024];
+
+ DPRINTFN(15, ("usbd_print dev=%p\n", uaa->device));
+ if (pnp) {
+ if (!uaa->usegeneric)
+ return (QUIET);
+ usbd_devinfo(uaa->device, 1, devinfo);
+ printf("%s at %s", devinfo, pnp);
+ }
+ if (uaa->port != 0)
+ printf(" port %d", uaa->port);
+ return (UNCONF);
+}
+
+int
+usbd_submatch(parent, cf, aux)
+ struct device *parent;
+ struct cfdata *cf;
+ void *aux;
+{
+ struct usb_attach_arg *uaa = aux;
+
+ if (uaa->port != 0 &&
+ cf->uhubcf_port != UHUB_UNK_PORT &&
+ cf->uhubcf_port != uaa->port)
+ return 0;
+ return ((*cf->cf_attach->ca_match)(parent, cf, aux));
+}
+
+#elif defined(__FreeBSD__)
+static void
+usbd_bus_print_child(device_t bus, device_t dev)
+{
+ /* FIXME print the device address and the configuration used
+ */
+}
+#endif
diff --git a/sys/dev/usb/usbdevs.h b/sys/dev/usb/usbdevs.h
new file mode 100644
index 0000000..94e2973
--- /dev/null
+++ b/sys/dev/usb/usbdevs.h
@@ -0,0 +1,121 @@
+/* $NetBSD: usbdevs.h,v 1.6 1998/10/05 02:31:13 mark Exp $ */
+
+/*
+ * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.
+ *
+ * generated from:
+ * NetBSD: usbdevs,v 1.5 1998/10/05 02:30:17 mark Exp
+ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * List of known USB vendors
+ */
+
+#define USB_VENDOR_NEC 0x0409 /* NEC */
+#define USB_VENDOR_KODAK 0x040a /* Eastman Kodak */
+#define USB_VENDOR_NANAO 0x0440 /* Nanao */
+#define USB_VENDOR_UNIXTAR 0x0451 /* Unixtar */
+#define USB_VENDOR_GENIUS 0x0458 /* Genius */
+#define USB_VENDOR_CHERRY 0x046a /* Cherry */
+#define USB_VENDOR_PHILIPS 0x0471 /* Philips */
+#define USB_VENDOR_CONNECTIX 0x0478 /* Connectix */
+#define USB_VENDOR_CYPRESS 0x04b4 /* Cypress Semicondutor */
+#define USB_VENDOR_EIZO 0x056d /* EIZO */
+#define USB_VENDOR_BELKIN 0x05ab /* Belkin */
+#define USB_VENDOR_EIZONANAO 0x05e7 /* EIZO Nanao */
+#define USB_VENDOR_CHIC 0x05fe /* Chic Technology */
+#define USB_VENDOR_PLX 0x10b5 /* PLX */
+#define USB_VENDOR_INSIDEOUT 0x1608 /* Inside Out Networks */
+#define USB_VENDOR_INTEL 0x8086 /* Intel */
+
+/*
+ * List of known products. Grouped by vendor.
+ */
+
+/* NEC products */
+#define USB_PRODUCT_NEC_HUB 0x55aa /* hub */
+
+/* Kodak products */
+#define USB_PRODUCT_KODAK_DC260 0x0110 /* Digital Science DC260 */
+
+/* Nanao products */
+#define USB_PRODUCT_NANAO_HUB 0x0000 /* hub */
+#define USB_PRODUCT_NANAO_MONITOR 0x0001 /* monitor */
+
+/* Unixtar products */
+#define USB_PRODUCT_UNIXTAR_UTUSB41 0x1446 /* UT-USB41 */
+
+/* Genius products */
+#define USB_PRODUCT_GENIUS_NICHE 0x0001 /* Niche mouse */
+#define USB_PRODUCT_GENIUS_FLIGHT2000 0x1004 /* Flight 2000 joystick */
+
+/* Cherry products */
+#define USB_PRODUCT_CHERRY_MY3000KBD 0x0001 /* My3000 keyboard */
+#define USB_PRODUCT_CHERRY_MY3000HUB 0x0003 /* My3000 hub */
+
+/* Philips products */
+#define USB_PRODUCT_PHILIPS_DSS 0x0101 /* DSS 350 Digital Speaker System */
+#define USB_PRODUCT_PHILIPS_HUB 0x0201 /* hub */
+
+/* Connectix products */
+#define USB_PRODUCT_CONNECTIX_QUICKCAM 0x0001 /* QuickCam */
+
+/* Cypress Semiconduuctor products */
+#define USB_PRODUCT_CYPRESS_MOUSE 0x0001 /* mouse */
+
+/* Belkin products */
+#define USB_PRODUCT_BELKIN_F5U002 0x0002 /* Parallel printer adapter */
+
+/* EIZO Nanao products */
+#define USB_PRODUCT_EIZO_HUB 0x0000 /* hub */
+#define USB_PRODUCT_EIZO_MONITOR 0x0001 /* monitor */
+#define USB_PRODUCT_EIZONANAO_HUB 0x0000 /* hub */
+#define USB_PRODUCT_EIZONANAO_MONITOR 0x0001 /* monitor */
+
+/* Chic Technology */
+#define USB_PRODUCT_CHIC_MOUSE1 0x0001 /* mouse */
+
+/* PLX products */
+#define USB_PRODUCT_PLX_TESTBOARD 0x9060 /* test board */
+
+/* Inside Out Networks products */
+#define USB_PRODUCT_INSIDEOUT_EDGEPORT4 0x0001 /* EdgePort/4 */
+
+/* Intel products */
+#define USB_PRODUCT_INTEL_TESTBOARD 0x9890 /* 82930 test board */
diff --git a/sys/dev/usb/usbdevs_data.h b/sys/dev/usb/usbdevs_data.h
new file mode 100644
index 0000000..d6b4133
--- /dev/null
+++ b/sys/dev/usb/usbdevs_data.h
@@ -0,0 +1,276 @@
+/* $NetBSD: usbdevs_data.h,v 1.6 1998/10/05 02:31:14 mark Exp $ */
+
+/*
+ * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.
+ *
+ * generated from:
+ * NetBSD: usbdevs,v 1.5 1998/10/05 02:30:17 mark Exp
+ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+struct usb_knowndev usb_knowndevs[] = {
+ {
+ USB_VENDOR_NEC, USB_PRODUCT_NEC_HUB,
+ 0,
+ "NEC",
+ "hub",
+ },
+ {
+ USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC260,
+ 0,
+ "Eastman Kodak",
+ "Digital Science DC260",
+ },
+ {
+ USB_VENDOR_NANAO, USB_PRODUCT_NANAO_HUB,
+ 0,
+ "Nanao",
+ "hub",
+ },
+ {
+ USB_VENDOR_NANAO, USB_PRODUCT_NANAO_MONITOR,
+ 0,
+ "Nanao",
+ "monitor",
+ },
+ {
+ USB_VENDOR_UNIXTAR, USB_PRODUCT_UNIXTAR_UTUSB41,
+ 0,
+ "Unixtar",
+ "UT-USB41",
+ },
+ {
+ USB_VENDOR_GENIUS, USB_PRODUCT_GENIUS_NICHE,
+ 0,
+ "Genius",
+ "Niche mouse",
+ },
+ {
+ USB_VENDOR_GENIUS, USB_PRODUCT_GENIUS_FLIGHT2000,
+ 0,
+ "Genius",
+ "Flight 2000 joystick",
+ },
+ {
+ USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_MY3000KBD,
+ 0,
+ "Cherry",
+ "My3000 keyboard",
+ },
+ {
+ USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_MY3000HUB,
+ 0,
+ "Cherry",
+ "My3000 hub",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DSS,
+ 0,
+ "Philips",
+ "DSS 350 Digital Speaker System",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_HUB,
+ 0,
+ "Philips",
+ "hub",
+ },
+ {
+ USB_VENDOR_CONNECTIX, USB_PRODUCT_CONNECTIX_QUICKCAM,
+ 0,
+ "Connectix",
+ "QuickCam",
+ },
+ {
+ USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_MOUSE,
+ 0,
+ "Cypress Semicondutor",
+ "mouse",
+ },
+ {
+ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U002,
+ 0,
+ "Belkin",
+ "Parallel printer adapter",
+ },
+ {
+ USB_VENDOR_EIZO, USB_PRODUCT_EIZO_HUB,
+ 0,
+ "EIZO",
+ "hub",
+ },
+ {
+ USB_VENDOR_EIZO, USB_PRODUCT_EIZO_MONITOR,
+ 0,
+ "EIZO",
+ "monitor",
+ },
+ {
+ USB_VENDOR_EIZONANAO, USB_PRODUCT_EIZONANAO_HUB,
+ 0,
+ "EIZO Nanao",
+ "hub",
+ },
+ {
+ USB_VENDOR_EIZONANAO, USB_PRODUCT_EIZONANAO_MONITOR,
+ 0,
+ "EIZO Nanao",
+ "monitor",
+ },
+ {
+ USB_VENDOR_CHIC, USB_PRODUCT_CHIC_MOUSE1,
+ 0,
+ "Chic Technology",
+ "mouse",
+ },
+ {
+ USB_VENDOR_PLX, USB_PRODUCT_PLX_TESTBOARD,
+ 0,
+ "PLX",
+ "test board",
+ },
+ {
+ USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4,
+ 0,
+ "Inside Out Networks",
+ "EdgePort/4",
+ },
+ {
+ USB_VENDOR_INTEL, USB_PRODUCT_INTEL_TESTBOARD,
+ 0,
+ "Intel",
+ "82930 test board",
+ },
+ {
+ USB_VENDOR_NEC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "NEC",
+ NULL,
+ },
+ {
+ USB_VENDOR_KODAK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Eastman Kodak",
+ NULL,
+ },
+ {
+ USB_VENDOR_NANAO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Nanao",
+ NULL,
+ },
+ {
+ USB_VENDOR_UNIXTAR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Unixtar",
+ NULL,
+ },
+ {
+ USB_VENDOR_GENIUS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Genius",
+ NULL,
+ },
+ {
+ USB_VENDOR_CHERRY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Cherry",
+ NULL,
+ },
+ {
+ USB_VENDOR_PHILIPS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Philips",
+ NULL,
+ },
+ {
+ USB_VENDOR_CONNECTIX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Connectix",
+ NULL,
+ },
+ {
+ USB_VENDOR_CYPRESS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Cypress Semicondutor",
+ NULL,
+ },
+ {
+ USB_VENDOR_EIZO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "EIZO",
+ NULL,
+ },
+ {
+ USB_VENDOR_BELKIN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Belkin",
+ NULL,
+ },
+ {
+ USB_VENDOR_EIZONANAO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "EIZO Nanao",
+ NULL,
+ },
+ {
+ USB_VENDOR_CHIC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Chic Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_PLX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "PLX",
+ NULL,
+ },
+ {
+ USB_VENDOR_INSIDEOUT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Inside Out Networks",
+ NULL,
+ },
+ {
+ USB_VENDOR_INTEL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Intel",
+ NULL,
+ },
+ { 0, 0, 0, NULL, NULL, }
+};
diff --git a/sys/dev/usb/usbdi.c b/sys/dev/usb/usbdi.c
new file mode 100644
index 0000000..cce795a
--- /dev/null
+++ b/sys/dev/usb/usbdi.c
@@ -0,0 +1,1112 @@
+/* $NetBSD: usbdi.c,v 1.9 1998/08/02 22:30:53 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#else
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#endif
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <sys/queue.h>
+#if defined(__FreeBSD__)
+#include "dev/usb/queue.addendum.h"
+#endif
+
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+static usbd_status usbd_ar_pipe __P((usbd_pipe_handle pipe));
+static usbd_status usbd_ar_iface __P((usbd_interface_handle iface));
+static void usbd_transfer_cb __P((usbd_request_handle reqh));
+static void usbd_sync_transfer_cb __P((usbd_request_handle reqh));
+static usbd_status usbd_do_transfer __P((usbd_request_handle reqh));
+static usbd_status usbd_start __P((usbd_pipe_handle pipe));
+void usbd_do_request_async_cb
+ __P((usbd_request_handle, usbd_private_handle, usbd_status));
+
+static SIMPLEQ_HEAD(, usbd_request) usbd_free_requests;
+
+#if defined(__FreeBSD__)
+#define USB_CDEV_MAJOR 79
+
+extern struct cdevsw usb_cdevsw;
+#endif
+
+usbd_status
+usbd_open_pipe(iface, address, flags, pipe)
+ usbd_interface_handle iface;
+ u_int8_t address;
+ u_int8_t flags;
+ usbd_pipe_handle *pipe;
+{
+ usbd_pipe_handle p;
+ struct usbd_endpoint *ep;
+ int i, r;
+
+ if (iface->state != USBD_INTERFACE_ACTIVE)
+ return (USBD_INTERFACE_NOT_ACTIVE);
+ for (i = 0; i < iface->idesc->bNumEndpoints; i++) {
+ ep = &iface->endpoints[i];
+ if (ep->edesc->bEndpointAddress == address)
+ goto found;
+ }
+ return (USBD_BAD_ADDRESS);
+ found:
+ if ((flags & USBD_EXCLUSIVE_USE) &&
+ ep->refcnt != 0)
+ return (USBD_IN_USE);
+ r = usbd_setup_pipe(iface->device, iface, ep, &p);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ LIST_INSERT_HEAD(&iface->pipes, p, next);
+ *pipe = p;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_open_pipe_intr(iface, address, flags, pipe, priv, buffer, length, cb)
+ usbd_interface_handle iface;
+ u_int8_t address;
+ u_int8_t flags;
+ usbd_pipe_handle *pipe;
+ usbd_private_handle priv;
+ void *buffer;
+ u_int32_t length;
+ usbd_callback cb;
+{
+ usbd_status r;
+ usbd_request_handle reqh;
+ usbd_pipe_handle ipipe;
+
+ reqh = usbd_alloc_request();
+ if (reqh == 0)
+ return (USBD_NOMEM);
+ r = usbd_open_pipe(iface, address, USBD_EXCLUSIVE_USE, &ipipe);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto bad1;
+ r = usbd_setup_request(reqh, ipipe, priv, buffer, length,
+ USBD_XFER_IN | flags, USBD_NO_TIMEOUT, cb);
+ if (r != USBD_NORMAL_COMPLETION)
+ goto bad2;
+ ipipe->intrreqh = reqh;
+ r = usbd_transfer(reqh);
+ *pipe = ipipe;
+ if (r != USBD_IN_PROGRESS)
+ goto bad3;
+ return (USBD_NORMAL_COMPLETION);
+
+ bad3:
+ ipipe->intrreqh = 0;
+ bad2:
+ usbd_close_pipe(ipipe);
+ bad1:
+ usbd_free_request(reqh);
+ return r;
+}
+
+usbd_status
+usbd_open_pipe_iso(iface, address, flags, pipe, priv, bufsize, nbuf, cb)
+ usbd_interface_handle iface;
+ u_int8_t address;
+ u_int8_t flags;
+ usbd_pipe_handle *pipe;
+ usbd_private_handle priv;
+ u_int32_t bufsize;
+ u_int32_t nbuf;
+ usbd_callback cb;
+{
+ usbd_status r;
+ usbd_pipe_handle p;
+
+ r = usbd_open_pipe(iface, address, USBD_EXCLUSIVE_USE, &p);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ if (!p->methods->isobuf) {
+ usbd_close_pipe(p);
+ return (USBD_INVAL);
+ }
+ r = p->methods->isobuf(p, bufsize, nbuf);
+ if (r != USBD_NORMAL_COMPLETION) {
+ usbd_close_pipe(p);
+ return (r);
+ }
+ *pipe = p;
+ return r;
+}
+
+usbd_status
+usbd_close_pipe(pipe)
+ usbd_pipe_handle pipe;
+{
+ if (pipe->iface->state != USBD_INTERFACE_ACTIVE)
+ return (USBD_INTERFACE_NOT_ACTIVE);
+ if (--pipe->refcnt != 0)
+ return (USBD_NORMAL_COMPLETION);
+ if (SIMPLEQ_FIRST(&pipe->queue) != 0)
+ return (USBD_PENDING_REQUESTS);
+ LIST_REMOVE(pipe, next);
+ pipe->endpoint->refcnt--;
+ pipe->methods->close(pipe);
+ if (pipe->intrreqh)
+ usbd_free_request(pipe->intrreqh);
+ free(pipe, M_USB);
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_transfer(reqh)
+ usbd_request_handle reqh;
+{
+ reqh->xfercb = usbd_transfer_cb;
+ return (usbd_do_transfer(reqh));
+}
+
+static usbd_status
+usbd_do_transfer(reqh)
+ usbd_request_handle reqh;
+{
+ usbd_pipe_handle pipe = reqh->pipe;
+ usbd_interface_handle iface = pipe->iface;
+ usbd_status r;
+ int s;
+
+ DPRINTFN(10,("usbd_do_transfer: reqh=%p\n", reqh));
+ reqh->done = 0;
+ s = splusb();
+ if (pipe->state == USBD_PIPE_IDLE ||
+ (iface && iface->state == USBD_INTERFACE_IDLE)) {
+ splx(s);
+ return (USBD_IS_IDLE);
+ }
+ SIMPLEQ_INSERT_TAIL(&pipe->queue, reqh, next);
+ if (pipe->state == USBD_PIPE_ACTIVE &&
+ (!iface || iface->state == USBD_INTERFACE_ACTIVE)) {
+ r = usbd_start(pipe);
+ } else
+ r = USBD_NOT_STARTED;
+ splx(s);
+ return (r);
+}
+
+static usbd_status
+usbd_start(pipe)
+ usbd_pipe_handle pipe;
+{
+ usbd_request_handle reqh;
+
+ DPRINTFN(5, ("usbd_start: pipe=%p, running=%d\n",
+ pipe, pipe->running));
+ if (pipe->running)
+ return (USBD_IN_PROGRESS);
+ reqh = SIMPLEQ_FIRST(&pipe->queue);
+ if (!reqh) {
+ /* XXX */
+ printf("usbd_start: pipe empty!\n");
+ pipe->running = 0;
+ return (USBD_XXX);
+ }
+ SIMPLEQ_REMOVE_HEAD(&pipe->queue, reqh, next);
+ pipe->running = 1;
+ pipe->curreqh = reqh;
+ return (pipe->methods->transfer(reqh));
+}
+
+
+usbd_request_handle
+usbd_alloc_request()
+{
+ usbd_request_handle reqh;
+
+ reqh = SIMPLEQ_FIRST(&usbd_free_requests);
+ if (reqh)
+ SIMPLEQ_REMOVE_HEAD(&usbd_free_requests, reqh, next);
+ else
+ reqh = malloc(sizeof(*reqh), M_USB, M_NOWAIT);
+ if (!reqh)
+ return (0);
+ memset(reqh, 0, sizeof *reqh);
+ return (reqh);
+}
+
+usbd_status
+usbd_free_request(reqh)
+ usbd_request_handle reqh;
+{
+ SIMPLEQ_INSERT_HEAD(&usbd_free_requests, reqh, next);
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_setup_request(reqh, pipe, priv, buffer, length, flags, timeout, callback)
+ usbd_request_handle reqh;
+ usbd_pipe_handle pipe;
+ usbd_private_handle priv;
+ void *buffer;
+ u_int32_t length;
+ u_int16_t flags;
+ u_int32_t timeout;
+ void (*callback) __P((usbd_request_handle,
+ usbd_private_handle,
+ usbd_status));
+{
+ reqh->pipe = pipe;
+ reqh->isreq = 0;
+ reqh->priv = priv;
+ reqh->buffer = buffer;
+ reqh->length = length;
+ reqh->actlen = 0;
+ reqh->flags = flags;
+ reqh->callback = callback;
+ reqh->status = USBD_NOT_STARTED;
+ reqh->retries = 1;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_setup_device_request(reqh, req)
+ usbd_request_handle reqh;
+ usb_device_request_t *req;
+{
+ reqh->isreq = 1;
+ reqh->request = *req;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_setup_default_request(reqh, dev, priv, timeout, req, buffer,
+ length, flags, callback)
+ usbd_request_handle reqh;
+ usbd_device_handle dev;
+ usbd_private_handle priv;
+ u_int32_t timeout;
+ usb_device_request_t *req;
+ void *buffer;
+ u_int32_t length;
+ u_int16_t flags;
+ void (*callback) __P((usbd_request_handle,
+ usbd_private_handle,
+ usbd_status));
+{
+ reqh->pipe = dev->default_pipe;
+ reqh->priv = priv;
+ reqh->buffer = buffer;
+ reqh->length = length;
+ reqh->actlen = 0;
+ reqh->flags = flags;
+ reqh->timeout = timeout;
+ reqh->status = USBD_NOT_STARTED;
+ reqh->callback = callback;
+ reqh->request = *req;
+ reqh->isreq = 1;
+ reqh->retries = 1;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_set_request_timeout(reqh, timeout)
+ usbd_request_handle reqh;
+ u_int32_t timeout;
+{
+ reqh->timeout = timeout;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_request_status(reqh, priv, buffer, count, status)
+ usbd_request_handle reqh;
+ usbd_private_handle *priv;
+ void **buffer;
+ u_int32_t *count;
+ usbd_status *status;
+{
+ *priv = reqh->priv;
+ *buffer = reqh->buffer;
+ *count = reqh->actlen;
+ *status = reqh->status;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_request_device_data(reqh, req)
+ usbd_request_handle reqh;
+ usb_device_request_t *req;
+{
+ if (!reqh->isreq)
+ return (USBD_INVAL);
+ *req = reqh->request;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+#if 0
+usb_descriptor_t *
+usbd_get_descriptor(iface, desc_type)
+ usbd_interface_handle *iface;
+ u_int8_t desc_type;
+XX
+#endif
+
+usb_config_descriptor_t *
+usbd_get_config_descriptor(dev)
+ usbd_device_handle dev;
+{
+ return (dev->cdesc);
+}
+
+usb_interface_descriptor_t *
+usbd_get_interface_descriptor(iface)
+ usbd_interface_handle iface;
+{
+ return (iface->idesc);
+}
+
+usb_device_descriptor_t *
+usbd_get_device_descriptor(dev)
+ usbd_device_handle dev;
+{
+ return (&dev->ddesc);
+}
+
+usb_endpoint_descriptor_t *
+usbd_interface2endpoint_descriptor(iface, index)
+ usbd_interface_handle iface;
+ u_int8_t index;
+{
+ if (index >= iface->idesc->bNumEndpoints)
+ return 0;
+ return (iface->endpoints[index].edesc);
+}
+
+usbd_status usbd_set_configuration(dev, conf)
+ usbd_device_handle dev;
+ u_int16_t conf;
+{
+ return usbd_set_config_no(dev, conf, 0);
+}
+
+usbd_status
+usbd_retry_request(reqh, retry_count)
+ usbd_request_handle reqh;
+ u_int32_t retry_count;
+{
+ usbd_status r;
+
+ r = usbd_set_pipe_state(reqh->pipe, USBD_PIPE_ACTIVE);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ reqh->retries = retry_count;
+ return (usbd_transfer(reqh));
+}
+
+usbd_status
+usbd_abort_pipe(pipe)
+ usbd_pipe_handle pipe;
+{
+ usbd_status r;
+ int s, st;
+
+ if (pipe->iface->state != USBD_INTERFACE_ACTIVE)
+ return (USBD_INTERFACE_NOT_ACTIVE);
+ s = splusb();
+ st = pipe->state;
+ r = usbd_ar_pipe(pipe);
+ pipe->state = st;
+ splx(s);
+ return (r);
+}
+
+usbd_status
+usbd_abort_interface(iface)
+ usbd_interface_handle iface;
+{
+ usbd_status r;
+ int s, st;
+
+ s = splusb();
+ st = iface->state;
+ r = usbd_ar_iface(iface);
+ iface->state = st;
+ splx(s);
+ return (r);
+}
+
+usbd_status
+usbd_reset_pipe(pipe)
+ usbd_pipe_handle pipe;
+{
+ usbd_status r;
+ int s;
+
+ if (pipe->iface->state != USBD_INTERFACE_ACTIVE)
+ return (USBD_INTERFACE_NOT_ACTIVE);
+ s = splusb();
+ r = usbd_ar_pipe(pipe);
+ /* XXX anything else */
+ pipe->state = USBD_PIPE_ACTIVE;
+ splx(s);
+ return (r);
+}
+
+usbd_status
+usbd_reset_interface(iface)
+ usbd_interface_handle iface;
+{
+ usbd_status r;
+ int s;
+
+ s = splusb();
+ r = usbd_ar_iface(iface);
+ /* XXX anything else */
+ iface->state = USBD_INTERFACE_ACTIVE;
+ splx(s);
+ return (r);
+}
+
+usbd_status
+usbd_clear_endpoint_stall(pipe)
+ usbd_pipe_handle pipe;
+{
+ usbd_device_handle dev = pipe->device;
+ usb_device_request_t req;
+ usbd_status r;
+
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_STALL);
+ USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress);
+ USETW(req.wLength, 0);
+ r = usbd_do_request(dev, &req, 0);
+#if 0
+XXX should we do this?
+ if (r == USBD_NORMAL_COMPLETION) {
+ pipe->state = USBD_PIPE_ACTIVE;
+ /* XXX activate pipe */
+ }
+#endif
+ return (r);
+}
+
+usbd_status
+usbd_clear_endpoint_stall_async(pipe)
+ usbd_pipe_handle pipe;
+{
+ usbd_device_handle dev = pipe->device;
+ usb_device_request_t req;
+ usbd_status r;
+
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_STALL);
+ USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress);
+ USETW(req.wLength, 0);
+ r = usbd_do_request_async(dev, &req, 0);
+ return (r);
+}
+
+usbd_status
+usbd_set_pipe_state(pipe, state)
+ usbd_pipe_handle pipe;
+ usbd_pipe_state state;
+{
+ int s;
+
+ if (pipe->iface->state != USBD_INTERFACE_ACTIVE)
+ return (USBD_INTERFACE_NOT_ACTIVE);
+ if (state != USBD_PIPE_ACTIVE &&
+ state != USBD_PIPE_STALLED &&
+ state != USBD_PIPE_IDLE)
+ return (USBD_INVAL);
+ pipe->state = state;
+ if (state == USBD_PIPE_ACTIVE) {
+ s = splusb();
+ usbd_start(pipe);
+ splx(s);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_pipe_state(pipe, state, endpoint_state, request_count)
+ usbd_pipe_handle pipe;
+ usbd_pipe_state *state;
+ u_int32_t *endpoint_state;
+ u_int32_t *request_count;
+{
+ int n;
+ usbd_request_handle r;
+
+ *state = pipe->state;
+ *endpoint_state = pipe->endpoint->state;
+ for (r = SIMPLEQ_FIRST(&pipe->queue), n = 0;
+ r != 0;
+ r = SIMPLEQ_NEXT(r, next), n++)
+ ;
+ *request_count = n;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_set_interface_state(iface, state)
+ usbd_interface_handle iface;
+ usbd_interface_state state;
+{
+ int ps;
+ usbd_pipe_handle p;
+
+ if (state == USBD_INTERFACE_ACTIVE)
+ ps = USBD_PIPE_ACTIVE;
+ else if (state == USBD_INTERFACE_STALLED)
+ ps = USBD_PIPE_STALLED;
+ else if (state == USBD_INTERFACE_IDLE)
+ ps = USBD_PIPE_IDLE;
+ else
+ return (USBD_INVAL);
+ iface->state = USBD_INTERFACE_ACTIVE; /* to allow setting the pipe */
+ for (p = LIST_FIRST(&iface->pipes); p != 0; p = LIST_NEXT(p, next))
+ usbd_set_pipe_state(p, ps);
+ iface->state = state;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_interface_state(iface, state)
+ usbd_interface_handle iface;
+ usbd_interface_state *state;
+{
+ *state = iface->state;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_device_state(dev, state)
+ usbd_device_handle dev;
+ usbd_device_state *state;
+{
+ *state = dev->state;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+#if 0
+usbd_status
+usbd_set_device_state(dev, state)
+ usbd_device_handle dev;
+ usbd_device_state state;
+X
+#endif
+
+usbd_status
+usbd_device_address(dev, address)
+ usbd_device_handle dev;
+ u_int8_t *address;
+{
+ *address = dev->address;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_endpoint_address(pipe, address)
+ usbd_pipe_handle pipe;
+ u_int8_t *address;
+{
+ *address = pipe->endpoint->edesc->bEndpointAddress;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_endpoint_count(iface, count)
+ usbd_interface_handle iface;
+ u_int8_t *count;
+{
+ *count = iface->idesc->bNumEndpoints;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_interface_count(dev, count)
+ usbd_device_handle dev;
+ u_int8_t *count;
+{
+ if (!dev->cdesc)
+ return (USBD_NOT_CONFIGURED);
+ *count = dev->cdesc->bNumInterface;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+#if defined(__NetBSD__)
+/* Do we need these?
+ * Alternative: let people probe every single device node they can
+ * find for valid devices instead of counting through n of them.
+ */
+u_int8_t
+usbd_bus_count()
+{
+ return (usb_bus_count());
+}
+
+usbd_status
+usbd_get_bus_handle(index, bus)
+ u_int8_t index;
+ usbd_bus_handle *bus;
+{
+ return (usb_get_bus_handle(index, bus));
+}
+#endif
+
+usbd_status
+usbd_get_root_hub(bus, dev)
+ usbd_bus_handle bus;
+ usbd_device_handle *dev;
+{
+ *dev = bus->root_hub;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_port_count(dev, nports)
+ usbd_device_handle dev;
+ u_int8_t *nports;
+{
+ if (dev->hub == 0)
+ return (USBD_INVAL);
+ *nports = dev->hub->hubdesc.bNbrPorts;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_hub2device_handle(dev, port, devp)
+ usbd_device_handle dev;
+ u_int8_t port;
+ usbd_device_handle *devp;
+{
+ if (dev->hub == 0 || port >= dev->hub->hubdesc.bNbrPorts ||
+ dev->hub->ports[port].device == 0)
+ return (USBD_INVAL);
+ *devp = dev->hub->ports[port].device;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_request2pipe_handle(reqh, pipe)
+ usbd_request_handle reqh;
+ usbd_pipe_handle *pipe;
+{
+ *pipe = reqh->pipe;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_pipe2interface_handle(pipe, iface)
+ usbd_pipe_handle pipe;
+ usbd_interface_handle *iface;
+{
+ *iface = pipe->iface;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_interface2device_handle(iface, dev)
+ usbd_interface_handle iface;
+ usbd_device_handle *dev;
+{
+ *dev = iface->device;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_device2bus_handle(dev, bus)
+ usbd_device_handle dev;
+ usbd_bus_handle *bus;
+{
+ *bus = dev->bus;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_device2interface_handle(dev, ifaceno, iface)
+ usbd_device_handle dev;
+ u_int8_t ifaceno;
+ usbd_interface_handle *iface;
+{
+ if (!dev->cdesc)
+ return (USBD_NOT_CONFIGURED);
+ if (ifaceno >= dev->cdesc->bNumInterface)
+ return (USBD_INVAL);
+ *iface = &dev->ifaces[ifaceno];
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_set_interface_private_handle(iface, priv)
+ usbd_interface_handle iface;
+ usbd_private_handle priv;
+{
+ iface->priv = priv;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_interface_private_handle(iface, priv)
+ usbd_interface_handle iface;
+ usbd_private_handle *priv;
+{
+ *priv = iface->priv;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_reference_pipe(pipe)
+ usbd_pipe_handle pipe;
+{
+ pipe->refcnt++;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_dereference_pipe(pipe)
+ usbd_pipe_handle pipe;
+{
+ pipe->refcnt--;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_lock_token
+usbd_lock()
+{
+ return (splusb());
+}
+
+void
+usbd_unlock(tok)
+ usbd_lock_token tok;
+{
+ splx(tok);
+}
+
+/* XXX need to check that the interface is idle */
+usbd_status
+usbd_set_interface(iface, aiface)
+ usbd_interface_handle iface;
+ int aiface;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_INTERFACE;
+ req.bRequest = UR_SET_INTERFACE;
+ USETW(req.wValue, aiface);
+ USETW(req.wIndex, iface->idesc->iInterface);
+ USETW(req.wLength, 0);
+ return usbd_do_request(iface->device, &req, 0);
+}
+
+/*** Internal routines ***/
+
+/* Dequeue all pipe operations, called at splusb(). */
+static usbd_status
+usbd_ar_pipe(pipe)
+ usbd_pipe_handle pipe;
+{
+ usbd_request_handle reqh;
+
+ if (pipe->curreqh != 0)
+ pipe->methods->abort(pipe->curreqh);
+
+ for (;;) {
+ reqh = SIMPLEQ_FIRST(&pipe->queue);
+ if (reqh == 0)
+ break;
+ SIMPLEQ_REMOVE_HEAD(&pipe->queue, reqh, next);
+ reqh->status = USBD_CANCELLED;
+ if (reqh->callback)
+ reqh->callback(reqh, reqh->priv, reqh->status);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Dequeue all interface operations, called at splusb(). */
+static usbd_status
+usbd_ar_iface(iface)
+ usbd_interface_handle iface;
+{
+ usbd_pipe_handle p;
+ usbd_status r, ret = USBD_NORMAL_COMPLETION;
+
+ for (p = LIST_FIRST(&iface->pipes); p != 0; p = LIST_NEXT(p, next)) {
+ r = usbd_ar_pipe(p);
+ if (r != USBD_NORMAL_COMPLETION)
+ ret = r;
+ }
+ return (ret);
+}
+
+static int usbd_global_init_done = 0;
+
+void
+usbd_init()
+{
+#if defined(__FreeBSD__)
+ dev_t dev;
+#endif
+
+ if (!usbd_global_init_done) {
+ usbd_global_init_done = 1;
+ SIMPLEQ_INIT(&usbd_free_requests);
+
+#if defined(__FreeBSD__)
+ dev = makedev(USB_CDEV_MAJOR, 0);
+ cdevsw_add(&dev, &usb_cdevsw, NULL);
+#endif
+ }
+}
+
+static void
+usbd_transfer_cb(reqh)
+ usbd_request_handle reqh;
+{
+ usbd_pipe_handle pipe = reqh->pipe;
+ usbd_request_handle nreqh;
+ usbd_status r;
+
+ /* Count completed transfers. */
+ ++pipe->device->bus->stats.requests
+ [pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE];
+
+ /* XXX check retry count */
+ reqh->done = 1;
+ if (reqh->status == USBD_NORMAL_COMPLETION &&
+ reqh->actlen < reqh->length &&
+ !(reqh->flags & USBD_SHORT_XFER_OK)) {
+ DPRINTFN(-1, ("usbd_transfer_cb: short xfer %d < %d\n",
+ reqh->actlen, reqh->length));
+ reqh->status = USBD_SHORT_XFER;
+ }
+ pipe->curreqh = 0;
+ if (reqh->callback)
+ reqh->callback(reqh, reqh->priv, reqh->status);
+
+ if (pipe->state != USBD_PIPE_ACTIVE) {
+ pipe->running = 0;
+ return;
+ }
+ nreqh = SIMPLEQ_FIRST(&pipe->queue);
+ DPRINTFN(5, ("usbd_transfer_cb: nreqh=%p\n", nreqh));
+ if (!nreqh)
+ pipe->running = 0;
+ else {
+ SIMPLEQ_REMOVE_HEAD(&pipe->queue, nreqh, next);
+ pipe->curreqh = reqh;
+ r = pipe->methods->transfer(nreqh);
+ if (r != USBD_IN_PROGRESS)
+ printf("usbd_transfer_cb: error=%d\n", r);
+ }
+}
+
+static void
+usbd_sync_transfer_cb(reqh)
+ usbd_request_handle reqh;
+{
+ usbd_transfer_cb(reqh);
+ if (!reqh->pipe->device->bus->use_polling)
+ wakeup(reqh);
+}
+
+/* Like usbd_transfer(), but waits for completion. */
+usbd_status
+usbd_sync_transfer(reqh)
+ usbd_request_handle reqh;
+{
+ usbd_status r;
+ int s;
+
+ reqh->xfercb = usbd_sync_transfer_cb;
+ r = usbd_do_transfer(reqh);
+ if (r != USBD_IN_PROGRESS)
+ return (r);
+ s = splusb();
+ if (!reqh->done) {
+ if (reqh->pipe->device->bus->use_polling)
+ panic("usbd_sync_transfer: not done\n");
+ tsleep(reqh, PRIBIO, "usbsyn", 0);
+ }
+ splx(s);
+ return (reqh->status);
+}
+
+usbd_status
+usbd_do_request(dev, req, data)
+ usbd_device_handle dev;
+ usb_device_request_t *req;
+ void *data;
+{
+ usbd_request_handle reqh;
+ usbd_status r;
+
+#ifdef DIAGNOSTIC
+ if (!curproc) {
+ printf("usbd_do_request: not in process context\n");
+ return (USBD_XXX);
+ }
+#endif
+
+ reqh = usbd_alloc_request();
+ if (reqh == 0)
+ return (USBD_NOMEM);
+ r = usbd_setup_default_request(
+ reqh, dev, 0, USBD_DEFAULT_TIMEOUT, req, data,
+ UGETW(req->wLength), 0, 0);
+ if (r != USBD_NORMAL_COMPLETION) {
+ usbd_free_request(reqh);
+ return (r);
+ }
+ r = usbd_sync_transfer(reqh);
+#if defined(USB_DEBUG) || defined(DIAGNOSTIC)
+ if (reqh->actlen > reqh->length)
+ printf("usbd_do_request: overrun addr=%d type=0x%02x req=0x%02x val=%d index=%d rlen=%d length=%d actlen=%d\n",
+ dev->address, reqh->request.bmRequestType,
+ reqh->request.bRequest, UGETW(reqh->request.wValue),
+ UGETW(reqh->request.wIndex),
+ UGETW(reqh->request.wLength),
+ reqh->length, reqh->actlen);
+#endif
+ usbd_free_request(reqh);
+ return (r);
+}
+
+void
+usbd_do_request_async_cb(reqh, priv, status)
+ usbd_request_handle reqh;
+ usbd_private_handle priv;
+ usbd_status status;
+{
+#if defined(USB_DEBUG) || defined(DIAGNOSTIC)
+ if (reqh->actlen > reqh->length)
+ printf("usbd_do_request: overrun addr=%d type=0x%02x req=0x%02x val=%d index=%d rlen=%d length=%d actlen=%d\n",
+ reqh->pipe->device->address,
+ reqh->request.bmRequestType,
+ reqh->request.bRequest, UGETW(reqh->request.wValue),
+ UGETW(reqh->request.wIndex),
+ UGETW(reqh->request.wLength),
+ reqh->length, reqh->actlen);
+#endif
+ usbd_free_request(reqh);
+}
+
+/*
+ * Execute a request without waiting for completion.
+ * Can be used from interrupt context.
+ */
+usbd_status
+usbd_do_request_async(dev, req, data)
+ usbd_device_handle dev;
+ usb_device_request_t *req;
+ void *data;
+{
+ usbd_request_handle reqh;
+ usbd_status r;
+
+ reqh = usbd_alloc_request();
+ if (reqh == 0)
+ return (USBD_NOMEM);
+ r = usbd_setup_default_request(
+ reqh, dev, 0, USBD_DEFAULT_TIMEOUT, req, data,
+ UGETW(req->wLength), 0, usbd_do_request_async_cb);
+ if (r != USBD_NORMAL_COMPLETION) {
+ usbd_free_request(reqh);
+ return (r);
+ }
+ r = usbd_transfer(reqh);
+ if (r != USBD_IN_PROGRESS)
+ return (r);
+ return (USBD_NORMAL_COMPLETION);
+}
+
+struct usbd_quirks *
+usbd_get_quirks(dev)
+ usbd_device_handle dev;
+{
+ return (dev->quirks);
+}
+
+void
+usbd_set_disco(p, hdl, data)
+ usbd_pipe_handle p;
+ void (*hdl) __P((void *));
+ void *data;
+{
+ p->disco = hdl;
+ p->discoarg = data;
+}
+
+/* XXX do periodic free() of free list */
+
+/*
+ * Called from keyboard driver when in polling mode.
+ */
+void
+usbd_dopoll(iface)
+ usbd_interface_handle iface;
+{
+ iface->device->bus->do_poll(iface->device->bus);
+}
+
+void
+usbd_set_polling(iface, on)
+ usbd_interface_handle iface;
+ int on;
+{
+ iface->device->bus->use_polling = on;
+}
diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h
new file mode 100644
index 0000000..491376a
--- /dev/null
+++ b/sys/dev/usb/usbdi.h
@@ -0,0 +1,303 @@
+/* $NetBSD: usbdi.h,v 1.6 1998/08/02 22:30:53 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+typedef struct usbd_bus *usbd_bus_handle;
+typedef struct usbd_device *usbd_device_handle;
+typedef struct usbd_interface *usbd_interface_handle;
+typedef struct usbd_pipe *usbd_pipe_handle;
+typedef struct usbd_request *usbd_request_handle;
+typedef void *usbd_private_handle;
+
+typedef enum {
+ USBD_ENDPOINT_ACTIVE,
+ USBD_ENDPOINT_STALLED,
+} usbd_endpoint_state;
+
+typedef enum {
+ USBD_PIPE_ACTIVE,
+ USBD_PIPE_STALLED,
+ USBD_PIPE_IDLE,
+} usbd_pipe_state;
+
+typedef enum {
+ USBD_INTERFACE_ACTIVE,
+ USBD_INTERFACE_STALLED,
+ USBD_INTERFACE_IDLE,
+} usbd_interface_state;
+
+typedef enum {
+ USBD_DEVICE_ATTACHED,
+ USBD_DEVICE_POWERED,
+ USBD_DEVICE_DEFAULT,
+ USBD_DEVICE_ADDRESSED,
+ USBD_DEVICE_CONFIGURED,
+ USBD_DEVICE_SUSPENDED,
+} usbd_device_state;
+
+typedef enum {
+ USBD_NORMAL_COMPLETION = 0,
+ USBD_IN_PROGRESS,
+ /* errors */
+ USBD_PENDING_REQUESTS,
+ USBD_NOT_STARTED,
+ USBD_INVAL,
+ USBD_IS_IDLE,
+ USBD_NOMEM,
+ USBD_CANCELLED,
+ USBD_BAD_ADDRESS,
+ USBD_IN_USE,
+ USBD_INTERFACE_NOT_ACTIVE,
+ USBD_NO_ADDR,
+ USBD_SET_ADDR_FAILED,
+ USBD_NO_POWER,
+ USBD_TOO_DEEP,
+ USBD_IOERROR,
+ USBD_NOT_CONFIGURED,
+ USBD_TIMEOUT,
+ USBD_SHORT_XFER,
+ USBD_STALLED,
+
+ USBD_XXX,
+} usbd_status;
+
+typedef int usbd_lock_token;
+
+typedef void (*usbd_callback) __P((usbd_request_handle, usbd_private_handle,
+ usbd_status));
+
+/* Open flags */
+#define USBD_EXCLUSIVE_USE 0x01
+
+/* Request flags */
+#define USBD_XFER_OUT 0x01
+#define USBD_XFER_IN 0x02
+#define USBD_SHORT_XFER_OK 0x04
+
+#define USBD_NO_TIMEOUT 0
+#define USBD_DEFAULT_TIMEOUT 5000 /* ms = 5 s */
+
+usbd_status usbd_open_pipe
+ __P((usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe));
+usbd_status usbd_close_pipe __P((usbd_pipe_handle pipe));
+usbd_status usbd_transfer __P((usbd_request_handle req));
+usbd_request_handle usbd_alloc_request __P((void));
+usbd_status usbd_free_request __P((usbd_request_handle reqh));
+usbd_status usbd_setup_request
+ __P((usbd_request_handle reqh, usbd_pipe_handle pipe,
+ usbd_private_handle priv, void *buffer,
+ u_int32_t length, u_int16_t flags, u_int32_t timeout,
+ usbd_callback));
+usbd_status usbd_setup_device_request
+ __P((usbd_request_handle reqh, usb_device_request_t *req));
+usbd_status usbd_setup_default_request
+ __P((usbd_request_handle reqh, usbd_device_handle dev,
+ usbd_private_handle priv, u_int32_t timeout,
+ usb_device_request_t *req, void *buffer,
+ u_int32_t length, u_int16_t flags, usbd_callback));
+usbd_status usbd_set_request_timeout
+ __P((usbd_request_handle reqh, u_int32_t timeout));
+usbd_status usbd_get_request_status
+ __P((usbd_request_handle reqh, usbd_private_handle *priv,
+ void **buffer, u_int32_t *count, usbd_status *status));
+usbd_status usbd_request_device_data
+ __P((usbd_request_handle reqh, usb_device_request_t *req));
+usb_descriptor_t *usbd_get_descriptor
+ __P((usbd_interface_handle *iface, u_int8_t desc_type));
+usb_endpoint_descriptor_t *usbd_interface2endpoint_descriptor
+ __P((usbd_interface_handle iface, u_int8_t address));
+usbd_status usbd_set_configuration
+ __P((usbd_device_handle dev, u_int16_t conf));
+usbd_status usbd_retry_request
+ __P((usbd_request_handle reqh, u_int32_t retry_count));
+usbd_status usbd_abort_pipe __P((usbd_pipe_handle pipe));
+usbd_status usbd_abort_interface __P((usbd_interface_handle iface));
+usbd_status usbd_reset_pipe __P((usbd_pipe_handle pipe));
+usbd_status usbd_reset_interface __P((usbd_interface_handle iface));
+usbd_status usbd_clear_endpoint_stall __P((usbd_pipe_handle pipe));
+usbd_status usbd_clear_endpoint_stall_async __P((usbd_pipe_handle pipe));
+usbd_status usbd_set_pipe_state
+ __P((usbd_pipe_handle pipe, usbd_pipe_state state));
+usbd_status usbd_get_pipe_state
+ __P((usbd_pipe_handle pipe, usbd_pipe_state *state,
+ u_int32_t *endpoint_state, u_int32_t *request_count));
+usbd_status usbd_set_interface_state
+ __P((usbd_interface_handle iface, usbd_interface_state state));
+usbd_status usbd_get_interface_state
+ __P((usbd_interface_handle iface, usbd_interface_state *state));
+usbd_status usbd_get_device_state
+ __P((usbd_device_handle dev, usbd_device_state *state));
+usbd_status usbd_set_device_state
+ __P((usbd_device_handle dev, usbd_device_state state));
+usbd_status usbd_device_address
+ __P((usbd_device_handle dev, u_int8_t *address));
+usbd_status usbd_endpoint_address
+ __P((usbd_pipe_handle dev, u_int8_t *address));
+usbd_status usbd_endpoint_count
+ __P((usbd_interface_handle dev, u_int8_t *count));
+usbd_status usbd_interface_count
+ __P((usbd_device_handle dev, u_int8_t *count));
+u_int8_t usbd_bus_count __P((void));
+usbd_status usbd_get_bus_handle __P((u_int8_t index, usbd_bus_handle *bus));
+usbd_status usbd_get_root_hub
+ __P((usbd_bus_handle bus, usbd_device_handle *dev));
+usbd_status usbd_port_count __P((usbd_device_handle hub, u_int8_t *nports));
+usbd_status usbd_hub2device_handle
+ __P((usbd_device_handle hub, u_int8_t port, usbd_device_handle *dev));
+usbd_status usbd_request2pipe_handle
+ __P((usbd_request_handle reqh, usbd_pipe_handle *pipe));
+usbd_status usbd_pipe2interface_handle
+ __P((usbd_pipe_handle pipe, usbd_interface_handle *iface));
+usbd_status usbd_interface2device_handle
+ __P((usbd_interface_handle iface, usbd_device_handle *dev));
+usbd_status usbd_device2bus_handle
+ __P((usbd_device_handle dev, usbd_bus_handle *bus));
+usbd_status usbd_device2interface_handle
+ __P((usbd_device_handle dev, u_int8_t ifaceno,
+ usbd_interface_handle *iface));
+usbd_status usbd_set_interface_private_handle
+ __P((usbd_interface_handle iface, usbd_private_handle priv));
+usbd_status usbd_get_interface_private_handle
+ __P((usbd_interface_handle iface, usbd_private_handle *priv));
+usbd_status usbd_reference_pipe __P((usbd_pipe_handle pipe));
+usbd_status usbd_dereference_pipe __P((usbd_pipe_handle pipe));
+usbd_lock_token usbd_lock __P((void));
+void usbd_unlock __P((usbd_lock_token tok));
+
+/* Non-standard */
+usbd_status usbd_sync_transfer __P((usbd_request_handle req));
+usbd_status usbd_open_pipe_intr
+ __P((usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe,
+ usbd_private_handle priv, void *buffer,
+ u_int32_t length, usbd_callback));
+usbd_status usbd_open_pipe_iso
+ __P((usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe,
+ usbd_private_handle priv, u_int32_t bufsize, u_int32_t nbuf,
+ usbd_callback));
+usbd_status usbd_do_request
+ __P((usbd_device_handle pipe, usb_device_request_t *req, void *data));
+usbd_status usbd_do_request_async
+ __P((usbd_device_handle pipe, usb_device_request_t *req, void *data));
+usb_interface_descriptor_t *usbd_get_interface_descriptor
+ __P((usbd_interface_handle iface));
+usb_config_descriptor_t *usbd_get_config_descriptor
+ __P((usbd_device_handle dev));
+usb_device_descriptor_t *usbd_get_device_descriptor
+ __P((usbd_device_handle dev));
+usbd_status usbd_set_interface __P((usbd_interface_handle, int));
+
+void usbd_dopoll __P((usbd_interface_handle));
+void usbd_set_polling __P((usbd_interface_handle iface, int on));
+
+/* NetBSD attachment information */
+
+/* Attach data */
+struct usb_attach_arg {
+ struct usbd_device *device;
+ struct usbd_interface *iface;
+ int usegeneric;
+};
+
+#if defined(__NetBSD__)
+/* Match codes. */
+/* First five codes is for a whole device. */
+#define UMATCH_VENDOR_PRODUCT_REV 14
+#define UMATCH_VENDOR_PRODUCT 13
+#define UMATCH_VENDOR_DEVCLASS_DEVPROTO 12
+#define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO 11
+#define UMATCH_DEVCLASS_DEVSUBCLASS 10
+/* Next six codes are for interfaces. */
+#define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE 9
+#define UMATCH_VENDOR_PRODUCT_CONF_IFACE 8
+#define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO 7
+#define UMATCH_VENDOR_IFACESUBCLASS 6
+#define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO 5
+#define UMATCH_IFACECLASS_IFACESUBCLASS 4
+#define UMATCH_IFACECLASS 3
+#define UMATCH_IFACECLASS_GENERIC 2
+/* Generic driver */
+#define UMATCH_GENERIC 1
+/* No match */
+#define UMATCH_NONE 0
+
+#elif defined(__FreeBSD__)
+/* FreeBSD needs values less than zero */
+/* for the moment disabled
+#define UMATCH_VENDOR_PRODUCT_REV -14
+#define UMATCH_VENDOR_PRODUCT -13
+#define UMATCH_VENDOR_DEVCLASS_DEVPROTO -12
+#define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO -11
+#define UMATCH_DEVCLASS_DEVSUBCLASS -10
+#define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE -9
+#define UMATCH_VENDOR_PRODUCT_CONF_IFACE -8
+#define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO -7
+#define UMATCH_VENDOR_IFACESUBCLASS -6
+#define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO -5
+#define UMATCH_IFACECLASS_IFACESUBCLASS -4
+#define UMATCH_IFACECLASS -3
+#define UMATCH_IFACECLASS_GENERIC -2
+#define UMATCH_GENERIC -1
+#define UMATCH_NONE ENXIO
+
+* For the moment we use Yes/No answers with appropriate
+* sorting in the config file
+*/
+#define UMATCH_VENDOR_PRODUCT_REV 0
+#define UMATCH_VENDOR_PRODUCT 0
+#define UMATCH_VENDOR_DEVCLASS_DEVPROTO 0
+#define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO 0
+#define UMATCH_DEVCLASS_DEVSUBCLASS 0
+#define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE 0
+#define UMATCH_VENDOR_PRODUCT_CONF_IFACE 0
+#define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO 0
+#define UMATCH_VENDOR_IFACESUBCLASS 0
+#define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO 0
+#define UMATCH_IFACECLASS_IFACESUBCLASS 0
+#define UMATCH_IFACECLASS 0
+#define UMATCH_IFACECLASS_GENERIC 0
+#define UMATCH_GENERIC 0
+#define UMATCH_NONE ENXIO
+
+
+#endif
+
+void usbd_devinfo __P((usbd_device_handle, int, char *));
+struct usbd_quirks *usbd_get_quirks __P((usbd_device_handle));
+void usbd_set_disco __P((usbd_pipe_handle, void (*)(void *), void *));
diff --git a/sys/dev/usb/usbdi_util.c b/sys/dev/usb/usbdi_util.c
new file mode 100644
index 0000000..a439655
--- /dev/null
+++ b/sys/dev/usb/usbdi_util.c
@@ -0,0 +1,416 @@
+/* $NetBSD: usbdi_util.c,v 1.4 1998/08/02 22:30:53 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <dev/usb/usb_port.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#endif
+#include <sys/proc.h>
+#include <sys/select.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+usbd_status
+usbd_get_desc(dev, type, index, len, desc)
+ usbd_device_handle dev;
+ int type, index;
+ int len;
+ void *desc;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, type, index);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+ return (usbd_do_request(dev, &req, desc));
+}
+
+usbd_status
+usbd_get_config_desc(dev, conf, d)
+ usbd_device_handle dev;
+ int conf;
+ usb_config_descriptor_t *d;
+{
+ DPRINTFN(3,("usbd_get_config_desc: conf=%d\n", conf));
+ return (usbd_get_desc(dev, UDESC_CONFIG,
+ conf, USB_CONFIG_DESCRIPTOR_SIZE, d));
+}
+
+usbd_status
+usbd_get_config_desc_full(dev, conf, d, size)
+ usbd_device_handle dev;
+ int conf;
+ void *d;
+ int size;
+{
+ DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf));
+ return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d));
+}
+
+usbd_status
+usbd_get_device_desc(dev, d)
+ usbd_device_handle dev;
+ usb_device_descriptor_t *d;
+{
+ DPRINTFN(3,("usbd_get_device_desc:\n"));
+ return (usbd_get_desc(dev, UDESC_DEVICE,
+ 0, USB_DEVICE_DESCRIPTOR_SIZE, d));
+}
+
+usbd_status
+usbd_get_device_status(dev, st)
+ usbd_device_handle dev;
+ usb_status_t *st;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(usb_status_t));
+ return (usbd_do_request(dev, &req, st));
+}
+
+usbd_status
+usbd_get_hub_status(dev, st)
+ usbd_device_handle dev;
+ usb_hub_status_t *st;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(usb_hub_status_t));
+ return (usbd_do_request(dev, &req, st));
+}
+
+usbd_status
+usbd_set_address(dev, addr)
+ usbd_device_handle dev;
+ int addr;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return usbd_do_request(dev, &req, 0);
+}
+
+usbd_status
+usbd_get_port_status(dev, port, ps)
+ usbd_device_handle dev;
+ int port;
+ usb_port_status_t *ps;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_CLASS_OTHER;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, port);
+ USETW(req.wLength, sizeof *ps);
+ return (usbd_do_request(dev, &req, ps));
+}
+
+usbd_status
+usbd_clear_port_feature(dev, port, sel)
+ usbd_device_handle dev;
+ int port, sel;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, port);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_set_port_feature(dev, port, sel)
+ usbd_device_handle dev;
+ int port, sel;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, port);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+
+usbd_status
+usbd_set_protocol(iface, report)
+ usbd_interface_handle iface;
+ int report;
+{
+ usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status r;
+
+ DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n",
+ iface, report, id->bInterfaceNumber));
+ r = usbd_interface2device_handle(iface, &dev);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ if (!id)
+ return (USBD_INVAL);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_PROTOCOL;
+ USETW(req.wValue, report);
+ USETW(req.wIndex, id->bInterfaceNumber);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_set_report(iface, type, id, data, len)
+ usbd_interface_handle iface;
+ int type;
+ int id;
+ void *data;
+ int len;
+{
+ usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status r;
+
+ DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
+ r = usbd_interface2device_handle(iface, &dev);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ if (!ifd)
+ return (USBD_INVAL);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, type, id);
+ USETW(req.wIndex, ifd->bInterfaceNumber);
+ USETW(req.wLength, len);
+ return (usbd_do_request(dev, &req, data));
+}
+
+usbd_status
+usbd_set_report_async(iface, type, id, data, len)
+ usbd_interface_handle iface;
+ int type;
+ int id;
+ void *data;
+ int len;
+{
+ usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status r;
+
+ DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len));
+ r = usbd_interface2device_handle(iface, &dev);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ if (!ifd)
+ return (USBD_INVAL);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, type, id);
+ USETW(req.wIndex, ifd->bInterfaceNumber);
+ USETW(req.wLength, len);
+ return (usbd_do_request_async(dev, &req, data));
+}
+
+usbd_status
+usbd_get_report(iface, type, id, data, len)
+ usbd_interface_handle iface;
+ int type;
+ int id;
+ void *data;
+ int len;
+{
+ usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status r;
+
+ DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
+ r = usbd_interface2device_handle(iface, &dev);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ if (!ifd)
+ return (USBD_INVAL);
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_REPORT;
+ USETW2(req.wValue, type, id);
+ USETW(req.wIndex, ifd->bInterfaceNumber);
+ USETW(req.wLength, len);
+ return (usbd_do_request(dev, &req, data));
+}
+
+usbd_status
+usbd_set_idle(iface, duration, id)
+ usbd_interface_handle iface;
+ int duration;
+ int id;
+{
+ usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status r;
+
+ DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id));
+ r = usbd_interface2device_handle(iface, &dev);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ if (!ifd)
+ return (USBD_INVAL);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_IDLE;
+ USETW2(req.wValue, duration, id);
+ USETW(req.wIndex, ifd->bInterfaceNumber);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_get_report_descriptor(dev, i, size, d)
+ usbd_device_handle dev;
+ int i;
+ int size;
+ void *d;
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_INTERFACE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_REPORT, 0);
+ USETW(req.wIndex, i);
+ USETW(req.wLength, size);
+ return (usbd_do_request(dev, &req, d));
+}
+
+usb_hid_descriptor_t *
+usbd_get_hid_descriptor(ifc)
+ usbd_interface_handle ifc;
+{
+ usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc);
+ usbd_device_handle dev;
+ usb_config_descriptor_t *cdesc;
+ usb_hid_descriptor_t *hd;
+ char *p, *end;
+ usbd_status r;
+
+ r = usbd_interface2device_handle(ifc, &dev);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (0);
+ cdesc = usbd_get_config_descriptor(dev);
+
+ p = (char *)idesc + idesc->bLength;
+ end = (char *)cdesc + UGETW(cdesc->wTotalLength);
+
+ for (; p < end; p += hd->bLength) {
+ hd = (usb_hid_descriptor_t *)p;
+ if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID)
+ return (hd);
+ if (hd->bDescriptorType == UDESC_INTERFACE)
+ break;
+ }
+ return (0);
+}
+
+usbd_status
+usbd_alloc_report_desc(ifc, descp, sizep, mem)
+ usbd_interface_handle ifc;
+ void **descp;
+ int *sizep;
+#if defined(__NetBSD__)
+ int mem;
+#elif defined(__FreeBSD__)
+ struct malloc_type *mem;
+#endif
+
+{
+ usb_hid_descriptor_t *hid;
+ usbd_device_handle dev;
+ usbd_status r;
+
+ r = usbd_interface2device_handle(ifc, &dev);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ hid = usbd_get_hid_descriptor(ifc);
+ if (!hid)
+ return (USBD_IOERROR);
+ *sizep = UGETW(hid->descrs[0].wDescriptorLength);
+ *descp = malloc(*sizep, mem, M_NOWAIT);
+ if (!*descp)
+ return (USBD_NOMEM);
+ r = usbd_get_report_descriptor(dev, 0, *sizep, *descp);
+ if (r != USBD_NORMAL_COMPLETION) {
+ free(*descp, mem);
+ return (r);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
diff --git a/sys/dev/usb/usbdi_util.h b/sys/dev/usb/usbdi_util.h
new file mode 100644
index 0000000..2c21f50
--- /dev/null
+++ b/sys/dev/usb/usbdi_util.h
@@ -0,0 +1,74 @@
+/* $NetBSD: usbdi_util.h,v 1.4 1998/08/02 22:30:53 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+usbd_status usbd_get_desc __P((usbd_device_handle dev, int type,
+ int index, int len, void *desc));
+usbd_status usbd_get_config_desc __P((usbd_device_handle, int,
+ usb_config_descriptor_t *));
+usbd_status usbd_get_config_desc_full __P((usbd_device_handle, int,
+ void *, int));
+usbd_status usbd_get_device_desc __P((usbd_device_handle dev,
+ usb_device_descriptor_t *d));
+usbd_status usbd_set_address __P((usbd_device_handle dev, int addr));
+usbd_status usbd_get_port_status __P((usbd_device_handle,
+ int, usb_port_status_t *));
+usbd_status usbd_set_port_feature __P((usbd_device_handle dev, int, int));
+usbd_status usbd_clear_port_feature __P((usbd_device_handle, int, int));
+usbd_status usbd_get_device_status __P((usbd_device_handle,usb_status_t*));
+usbd_status usbd_get_hub_status __P((usbd_device_handle dev,
+ usb_hub_status_t *st));
+usbd_status usbd_set_protocol __P((usbd_interface_handle dev, int report));
+usbd_status usbd_get_report_descriptor
+ __P((usbd_device_handle dev, int i, int size, void *d));
+struct usb_hid_descriptor *usbd_get_hid_descriptor
+ __P((usbd_interface_handle ifc));
+usbd_status usbd_set_report
+ __P((usbd_interface_handle iface,int type,int id,void *data,int len));
+usbd_status usbd_set_report_async
+ __P((usbd_interface_handle iface,int type,int id,void *data,int len));
+usbd_status usbd_get_report
+ __P((usbd_interface_handle iface,int type,int id,void *data,int len));
+usbd_status usbd_set_idle
+ __P((usbd_interface_handle iface, int duration, int id));
+#if defined(__NetBSD__)
+usbd_status usbd_alloc_report_desc
+ __P((usbd_interface_handle ifc, void **descp, int *sizep, int mem));
+#elif defined(__FreeBSD__)
+usbd_status usbd_alloc_report_desc
+ __P((usbd_interface_handle ifc, void **descp, int *sizep, struct malloc_type * mem));
+#endif
diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h
new file mode 100644
index 0000000..40e7947
--- /dev/null
+++ b/sys/dev/usb/usbdivar.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if defined(__FreeBSD__)
+/* conversiom from one type of queue to the other */
+#define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD_QUEUE
+#define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD
+#define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL
+#define SIMPLEQ_NEXT STAILQ_NEXT
+#define SIMPLEQ_FIRST STAILQ_FIRST
+#define SIMPLEQ_HEAD STAILQ_HEAD
+#define SIMPLEQ_INIT STAILQ_INIT
+#define SIMPLEQ_ENTRY STAILQ_ENTRY
+#endif
+
+struct usbd_request;
+struct usbd_pipe;
+
+struct usbd_endpoint {
+ usb_endpoint_descriptor_t *edesc;
+ usbd_endpoint_state state;
+ int refcnt;
+ int toggle; /* XXX */
+};
+
+typedef void (*usbd_xfercb)__P((usbd_request_handle req));
+
+struct usbd_methods {
+ usbd_status (*transfer)__P((usbd_request_handle reqh));
+ void (*abort)__P((usbd_request_handle reqh));
+ void (*close)__P((usbd_pipe_handle pipe));
+ usbd_status (*isobuf)__P((usbd_pipe_handle pipe,
+ u_int32_t bufsize,u_int32_t nbuf));
+};
+
+struct usbd_port {
+ usb_port_status_t status;
+ int power; /* mA of current on port */
+ struct usbd_device *device;
+ struct usbd_device *parent; /* The ports hub */
+};
+
+struct usbd_hub {
+ usbd_status (*explore)__P((usbd_device_handle hub));
+ void *hubdata;
+ usb_hub_descriptor_t hubdesc;
+ int nports;
+ struct usbd_port ports[1];
+};
+
+struct usb_softc;
+
+/*****/
+
+struct usbd_bus {
+ /* Filled by HC driver */
+ bdevice bdev; /* base device, host adapter */
+ usbd_status (*open_pipe)__P((struct usbd_pipe *pipe));
+ u_int32_t pipe_size; /* size of a pipe struct */
+ void (*do_poll)__P((struct usbd_bus *));
+ /* Filled by usb driver */
+ struct usbd_device *root_hub;
+ usbd_device_handle devices[USB_MAX_DEVICES];
+ char needs_explore;/* a hub a signalled a change */
+ char use_polling;
+ struct usb_softc *usbctl;
+ struct usb_device_stats stats;
+};
+
+struct usbd_device {
+ struct usbd_bus *bus;
+ usbd_device_state state;
+ struct usbd_pipe *default_pipe;
+ u_int8_t address;
+ u_int8_t depth;
+ u_int8_t lowspeed;
+ u_int16_t power;
+ u_int8_t self_powered;
+ int config;
+ struct usbd_port *powersrc;
+ struct usbd_endpoint def_ep; /* for pipe 0 */
+ usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */
+ struct usbd_interface *ifaces;
+ usb_device_descriptor_t ddesc;
+ usb_config_descriptor_t *cdesc; /* full config descr */
+ struct usbd_quirks *quirks;
+ struct usbd_hub *hub; /* only if this is a hub */
+#if defined(__FreeBSD__)
+ bdevice bdev; /* base device */
+#endif
+};
+
+struct usbd_interface {
+ struct usbd_device *device;
+ usbd_interface_state state;
+ usb_interface_descriptor_t *idesc;
+ struct usbd_endpoint *endpoints;
+ void *priv;
+ LIST_HEAD(, usbd_pipe) pipes;
+};
+
+struct usbd_pipe {
+ struct usbd_interface *iface;
+ struct usbd_device *device;
+ struct usbd_endpoint *endpoint;
+ usbd_pipe_state state;
+ int32_t refcnt;
+ char running;
+ SIMPLEQ_HEAD(, usbd_request) queue;
+ LIST_ENTRY(usbd_pipe) next;
+
+ void (*disco) __P((void *));
+ void *discoarg;
+
+ usbd_request_handle intrreqh; /* used for repeating requests */
+ usbd_request_handle curreqh; /* currently running request */
+
+ /* Filled by HC driver. */
+ struct usbd_methods *methods;
+};
+
+struct usbd_request {
+ struct usbd_pipe *pipe;
+ void *priv;
+ void *buffer;
+ u_int32_t length;
+ u_int32_t actlen;
+ u_int16_t flags;
+ u_int32_t timeout;
+ usbd_status status;
+ usbd_callback callback;
+ usbd_xfercb xfercb;
+ u_int32_t retries;
+ char done;
+
+ usb_device_request_t request;
+ u_int8_t isreq;
+
+ SIMPLEQ_ENTRY(usbd_request) next;
+
+ void *hcpriv; /* XXX private use by the HC driver */
+
+#if defined(__FreeBSD__)
+ struct callout_handle callout_handler;
+#endif
+};
+
+void usbd_init __P((void));
+
+/* Routines from usb_subr.c */
+int usbctlprint __P((void *, const char *));
+void usbd_delay_ms __P((usbd_bus_handle, int));
+void usbd_devinfo_vp __P((usbd_device_handle, char *, char *));
+usbd_status usbd_set_config_no __P((usbd_device_handle, int, int));
+usbd_status usbd_reset_port __P((usbd_device_handle dev,
+ int port, usb_port_status_t *ps));
+usbd_status usbd_setup_pipe __P((usbd_device_handle dev,
+ usbd_interface_handle iface,
+ struct usbd_endpoint *,
+ usbd_pipe_handle *pipe));
+usbd_status usbd_new_device __P((bdevice *parent,
+ usbd_bus_handle bus, int depth,
+ int lowspeed, int port,
+ struct usbd_port *));
+void usbd_remove_device __P((usbd_device_handle,
+ struct usbd_port *));
+int usbd_printBCD __P((char *cp, int bcd));
+
+/* Routines from usb.c */
+int usb_bus_count __P((void));
+usbd_status usb_get_bus_handle __P((int, usbd_bus_handle *));
+void usb_needs_explore __P((usbd_bus_handle));
+
+#if defined(__FreeBSD__)
+int usb_driver_load __P((module_t mod, modeventtype_t what,
+ void *arg));
+void usb_device_set_desc __P((device_t device, char *devinfo));
+#endif
+
+extern int usbd_use_polling;
+
+/* Locator stuff. */
+
+#if defined(__NetBSD__)
+/* NWH File not found anywhere in NetBSD sources... */
+#include "locators.h"
+#endif
+
+#define uhubcf_port cf_loc[UHUBCF_PORT]
+#define UHUB_UNK_PORT UHUBCF_PORT_DEFAULT /* wildcarded 'port' */
+
+/* Junk. */
+
+/* XXX */
+#define splusb splbio
+#define IPL_USB IPL_BIO
+/* XXX */
+
diff --git a/sys/dev/usb/usbhid.h b/sys/dev/usb/usbhid.h
new file mode 100644
index 0000000..c4a55df
--- /dev/null
+++ b/sys/dev/usb/usbhid.h
@@ -0,0 +1,133 @@
+/* $NetBSD: usbhid.h,v 1.1 1998/07/12 19:52:01 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson <augustss@carlstedt.se>
+ * Carlstedt Research & Technology
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+
+#ifndef _USBHID_H_
+#define _USBHID_H_
+
+#define UR_GET_HID_DESCRIPTOR 0x06
+#define UDESC_HID 0x21
+#define UDESC_REPORT 0x22
+#define UDESC_PHYSICAL 0x23
+#define UR_SET_HID_DESCRIPTOR 0x07
+#define UR_GET_REPORT 0x01
+#define UR_SET_REPORT 0x09
+#define UR_GET_IDLE 0x02
+#define UR_SET_IDLE 0x0a
+#define UR_GET_PROTOCOL 0x03
+#define UR_SET_PROTOCOL 0x0b
+
+typedef struct usb_hid_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdHID;
+ uByte bCountryCode;
+ uByte bNumDescriptors;
+ struct {
+ uByte bDescriptorType;
+ uWord wDescriptorLength;
+ } descrs[1];
+} usb_hid_descriptor_t;
+#define USB_HID_DESCRIPTOR_SIZE(n) (9+(n)*3)
+
+/* Usage pages */
+#define HUP_GENERIC_DESKTOP 0x0001
+#define HUP_SIMULATION 0x0002
+#define HUP_LEDS 0x0008
+#define HUP_BUTTON 0x0009
+
+/* Usages, generic desktop */
+#define HUG_POINTER 0x0001
+#define HUG_MOUSE 0x0002
+#define HUG_JOYSTICK 0x0004
+#define HUG_GAME_PAD 0x0005
+#define HUG_KEYBOARD 0x0006
+#define HUG_KEYPAD 0x0007
+#define HUG_X 0x0030
+#define HUG_Y 0x0031
+#define HUG_Z 0x0032
+#define HUG_RX 0x0033
+#define HUG_RY 0x0034
+#define HUG_RZ 0x0035
+#define HUG_SLIDER 0x0036
+#define HUG_DIAL 0x0037
+#define HUG_WHEEL 0x0038
+#define HUG_HAT_SWITCH 0x0039
+#define HUG_COUNTED_BUFFER 0x003a
+#define HUG_BYTE_COUNT 0x003b
+#define HUG_MOTION_WAKEUP 0x003c
+#define HUG_VX 0x0040
+#define HUG_VY 0x0041
+#define HUG_VZ 0x0042
+#define HUG_VBRX 0x0043
+#define HUG_VBRY 0x0044
+#define HUG_VBRZ 0x0045
+#define HUG_VNO 0x0046
+#define HUG_SYSTEM_CONTROL 0x0080
+#define HUG_SYSTEM_POWER_DOWN 0x0081
+#define HUG_SYSTEM_SLEEP 0x0082
+#define HUG_SYSTEM_WAKEUP 0x0083
+#define HUG_SYSTEM_CONTEXT_MENU 0x0084
+#define HUG_SYSTEM_MAIN_MENU 0x0085
+#define HUG_SYSTEM_APP_MENU 0x0086
+#define HUG_SYSTEM_MENU_HELP 0x0087
+#define HUG_SYSTEM_MENU_EXIT 0x0088
+#define HUG_SYSTEM_MENU_SELECT 0x0089
+#define HUG_SYSTEM_MENU_RIGHT 0x008a
+#define HUG_SYSTEM_MENU_LEFT 0x008b
+#define HUG_SYSTEM_MENU_UP 0x008c
+#define HUG_SYSTEM_MENU_DOWN 0x008d
+
+#define HID_USAGE2(p,u) (((p) << 16) | u)
+
+#define UHID_INPUT_REPORT 0x01
+#define UHID_OUTPUT_REPORT 0x02
+#define UHID_FEATURE_REPORT 0x03
+
+/* Bits in the input/output/feature items */
+#define HIO_CONST 0x001
+#define HIO_VARIABLE 0x002
+#define HIO_RELATIVE 0x004
+#define HIO_WRAP 0x008
+#define HIO_NONLINEAR 0x010
+#define HIO_NOPREF 0x020
+#define HIO_NULLSTATE 0x040
+#define HIO_VOLATILE 0x080
+#define HIO_BUFBYTES 0x100
+
+#endif /* _USBHID_H_ */
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 930349a..f2c58b6 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -11,7 +11,7 @@
# device lines is present in the ./LINT configuration file. If you are
# in doubt as to the purpose or necessity of a line, check first in LINT.
#
-# $Id: GENERIC,v 1.130 1998/11/03 22:01:21 des Exp $
+# $Id: GENERIC,v 1.131 1998/11/12 11:29:28 obrien Exp $
machine "i386"
cpu "I386_CPU"
@@ -179,3 +179,22 @@ options SYSVSHM
# option. The number of devices determines the maximum number of
# simultaneous BPF clients programs runnable.
#pseudo-device bpfilter 4 #Berkeley packet filter
+
+
+# USB support
+#controller uhci0
+#controller usb0
+#
+# for the moment we have to specify the priorities of the device
+# drivers explicitly by the ordering in the list below. This will
+# be changed in the future.
+#
+#device ums0
+#device ukbd0
+#device ulpt0
+#device uhub0
+#device hid0
+#device ugen0
+#
+#options USB_DEBUG
+#options USBVERBOSE
diff --git a/usr.sbin/usbd/Makefile b/usr.sbin/usbd/Makefile
new file mode 100644
index 0000000..0c74ed5
--- /dev/null
+++ b/usr.sbin/usbd/Makefile
@@ -0,0 +1,15 @@
+# FIXME have a look at all the other files and align it
+
+PROG= usbd
+MAN= usbd.8
+
+# for FreeBSD we need MAN8 instead of MAN
+MAN8= usbd.8
+
+# This hard coded path is not necessary as soon as we are in the
+# base FreeBSD system. The .h files will be in /usr/include by then.
+#
+CFLAGS += -I../../sys
+
+.include <bsd.prog.mk>
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/usbd/usbd.8 b/usr.sbin/usbd/usbd.8
new file mode 100644
index 0000000..47d745e
--- /dev/null
+++ b/usr.sbin/usbd/usbd.8
@@ -0,0 +1,80 @@
+.\" $NetBSD: usbd.8,v 1.2 1998/07/13 11:01:50 augustss Exp $
+.\" Copyright (c) 1998 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" Author: Lennart Augustsson
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd July 12, 1998
+.Dt USBD 8
+.Os
+.Sh NAME
+.Nm usbd
+.Nd supervise USB attach/detach
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f Ar device
+.Op Fl t Ar timeout
+.Op Fl v
+.Sh DESCRIPTION
+.Nm
+handles the USB device attachment and detachment.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Enable debugging to the standard output,
+and do not disassociate from the controlling terminal.
+.It Fl e
+Do one device tree exploration and then exit.
+.It Fl f Ar device
+Specify the pathname of a USB controller device file.
+The flag may be repeated to watch more than one USB controller.
+The default is
+.Pa /dev/usb0 ,
+.Pa /dev/usb1 ,
+.Pa /dev/usb2 ,
+and
+.Pa /dev/usb3 .
+.It Fl t Ar timeout
+Set the timeout interval (in seconds) before an exploration happens
+without being triggered by a connect or disconnect.
+A timeout of 0 means that there is no timeout. The default is 30.
+.It Fl v
+Be verbose.
+.El
+.Sh SEE ALSO
+.Xr usb 4
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Nx 1.4 .
diff --git a/usr.sbin/usbd/usbd.c b/usr.sbin/usbd/usbd.c
new file mode 100644
index 0000000..99a2d52
--- /dev/null
+++ b/usr.sbin/usbd/usbd.c
@@ -0,0 +1,183 @@
+/* $NetBSD: usbd.c,v 1.2 1998/07/23 18:39:53 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#if defined(__FreeBSD__)
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#endif
+#include <dev/usb/usb.h>
+
+#define USBDEV "/dev/usb"
+#define MAXUSBDEV 4
+
+extern char *__progname;
+
+void usage(void);
+
+void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s [-d] [-e] [-f dev] [-t timeout] [-v]\n",
+ __progname);
+ exit(1);
+}
+
+#define NDEVS 20 /* maximum number of usb controllers */
+
+/*
+ * Sometimes a device does not respond in time for interrupt
+ * driven explore to find it. Therefore we run an exploration
+ * at regular intervals to catch those.
+ */
+#define TIMEOUT 30
+
+int
+main(int argc, char **argv)
+{
+ int r, i;
+ char *devs[NDEVS];
+ int ndevs = 0;
+ int fds[NDEVS];
+ fd_set fdset;
+ int ch, verbose = 0;
+ int debug = 0;
+ int explore = 0;
+ int itimo = TIMEOUT;
+ int maxfd;
+ char buf[50];
+ struct timeval timo;
+ extern char *optarg;
+ extern int optind;
+
+ while ((ch = getopt(argc, argv, "def:t:v")) != -1) {
+ switch(ch) {
+ case 'd':
+ debug++;
+ break;
+ case 'e':
+ explore++;
+ break;
+ case 'f':
+ if (ndevs < NDEVS)
+ devs[ndevs++] = optarg;
+ break;
+ case 't':
+ itimo = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ maxfd = 0;
+ if (ndevs == 0) {
+ for (i = 0; i < MAXUSBDEV; i++) {
+ sprintf(buf, "%s%d", USBDEV, i);
+ fds[ndevs] = open(buf, O_RDWR);
+ if (fds[ndevs] >= 0) {
+ devs[ndevs] = strdup(buf);
+ if (verbose)
+ printf("%s: opening %s\n",
+ __progname, devs[ndevs]);
+ if (fds[ndevs] > maxfd)
+ maxfd = fds[ndevs];
+ ndevs++;
+ }
+ }
+ } else {
+ for (i = 0; i < ndevs; i++) {
+ fds[i] = open(devs[i], O_RDWR);
+ if (fds[i] < 0)
+ err(1, "%s", devs[i]);
+ else if (fds[i] > maxfd)
+ maxfd = fds[i];
+ }
+ }
+ if (ndevs == 0) {
+ if (verbose)
+ printf("%s: no USB controllers found\n", __progname);
+ exit(0);
+ }
+
+ if (explore) {
+ for (i = 0; i < ndevs; i++) {
+ r = ioctl(fds[i], USB_DISCOVER);
+ if (r < 0)
+ err(1, "USB_DISCOVER");
+ }
+ exit(0);
+ }
+
+ if (!debug)
+ daemon(0, 0);
+
+
+
+ FD_ZERO(&fdset);
+ for (;;) {
+ for (i = 0; i < ndevs; i++)
+ FD_SET(fds[i], &fdset);
+ timo.tv_usec = 0;
+ timo.tv_sec = itimo;
+ r = select(maxfd+1, &fdset, &fdset, 0, itimo ? &timo : 0);
+ if (r < 0)
+ warn("select failed\n");
+ for (i = 0; i < ndevs; i++)
+ if (r == 0 || FD_ISSET(fds[i], &fdset)) {
+ if (verbose)
+ printf("%s: doing %sdiscovery on %s\n",
+ __progname, r ? "" : "timeout ",
+ devs[i]);
+ if (ioctl(fds[i], USB_DISCOVER) < 0)
+ err(1, "USB_DISCOVER");
+ }
+ }
+}
diff --git a/usr.sbin/usbdevs/Makefile b/usr.sbin/usbdevs/Makefile
new file mode 100644
index 0000000..1f40a90
--- /dev/null
+++ b/usr.sbin/usbdevs/Makefile
@@ -0,0 +1,8 @@
+# $NetBSD: Makefile,v 1.2 1998/07/12 20:40:45 augustss Exp $
+
+PROG= usbdevs
+MAN8= usbdevs.8
+CFLAGS+=-I../..//sys
+
+.include <bsd.prog.mk>
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/usbdevs/usbdevs.8 b/usr.sbin/usbdevs/usbdevs.8
new file mode 100644
index 0000000..8f9f256
--- /dev/null
+++ b/usr.sbin/usbdevs/usbdevs.8
@@ -0,0 +1,67 @@
+.\" $NetBSD: usbdevs.8,v 1.3 1998/07/23 13:57:51 augustss Exp $
+.\" Copyright (c) 1998 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" Author: Lennart Augustsson
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd July 12, 1998
+.Dt USBDEVS 8
+.Os
+.Sh NAME
+.Nm usbdevs
+.Nd show USB devices connected to the system
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar addr
+.Op Fl f Ar dev
+.Op Fl v
+.Sh DESCRIPTION
+.Nm
+prints a listing of all USB devices connected to the system
+with some information about each device.
+The indentation of each line indicates its distance from the root.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a Ar addr
+only print information about the device at the given address.
+.It Fl f Ar dev
+only print information for the given USB controller.
+.It Fl v
+Be verbose.
+.El
+.Sh SEE ALSO
+.Xr usb 4
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Nx 1.4 .
diff --git a/usr.sbin/usbdevs/usbdevs.c b/usr.sbin/usbdevs/usbdevs.c
new file mode 100644
index 0000000..a12a994
--- /dev/null
+++ b/usr.sbin/usbdevs/usbdevs.c
@@ -0,0 +1,212 @@
+/* $NetBSD: usbdevs.c,v 1.4 1998/07/23 13:57:51 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Author: Lennart Augustsson
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <dev/usb/usb.h>
+#if defined(__FreeBSD__)
+#include <sys/ioctl.h>
+#endif
+
+#define USBDEV "/dev/usb"
+
+int verbose;
+
+void usage __P((void));
+void usbdev __P((int f, int a, int rec));
+void usbdump __P((int f));
+void dumpone __P((char *name, int f, int addr));
+int main __P((int, char **));
+
+extern char *__progname;
+
+void
+usage()
+{
+ fprintf(stderr, "Usage: %s [-a addr] [-f dev] [-v]\n", __progname);
+ exit(1);
+}
+
+char done[USB_MAX_DEVICES];
+int indent;
+
+void
+usbdev(f, a, rec)
+ int f;
+ int a;
+ int rec;
+{
+ struct usb_device_info di;
+ int e, p;
+
+ di.addr = a;
+ e = ioctl(f, USB_DEVICEINFO, &di);
+ if (e)
+ return;
+ done[a] = 1;
+ printf("addr %d: ", di.addr);
+ if (verbose) {
+ if (di.lowspeed)
+ printf("low speed, ");
+ if (di.power)
+ printf("power %d mA, ", di.power);
+ else
+ printf("self powered, ");
+ if (di.config)
+ printf("config %d, ", di.config);
+ else
+ printf("unconfigured, ");
+ }
+ printf("%s, %s", di.product, di.vendor);
+ if (verbose)
+ printf(", rev %s", di.revision);
+ printf("\n");
+ if (!rec)
+ return;
+ for (p = 0; p < di.nports; p++) {
+ int s = di.ports[p];
+ if (s >= USB_MAX_DEVICES) {
+ if (verbose) {
+ printf("%*sport %d %s\n", indent+1, "", p+1,
+ s == USB_PORT_ENABLED ? "enabled" :
+ s == USB_PORT_SUSPENDED ? "suspended" :
+ s == USB_PORT_POWERED ? "powered" :
+ s == USB_PORT_DISABLED ? "disabled" :
+ "???");
+
+ }
+ continue;
+ }
+ indent++;
+ printf("%*s", indent, "");
+ if (verbose)
+ printf("port %d ", p+1);
+ usbdev(f, di.ports[p], 1);
+ indent--;
+ }
+}
+
+void
+usbdump(f)
+ int f;
+{
+ int a;
+
+ for (a = 1; a < USB_MAX_DEVICES; a++) {
+ if (!done[a])
+ usbdev(f, a, 1);
+ }
+}
+
+void
+dumpone(name, f, addr)
+ char *name;
+ int f;
+ int addr;
+{
+ if (verbose)
+ printf("Controller %s:\n", name);
+ indent = 0;
+ memset(done, 0, sizeof done);
+ if (addr)
+ usbdev(f, addr, 0);
+ else
+ usbdump(f);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, i, f;
+ char buf[50];
+ extern int optind;
+ extern char *optarg;
+ char *dev = 0;
+ int addr = 0;
+ int ncont;
+
+ while ((ch = getopt(argc, argv, "a:f:v")) != -1) {
+ switch(ch) {
+ case 'a':
+ addr = atoi(optarg);
+ break;
+ case 'f':
+ dev = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (dev == 0) {
+ for (ncont = 0, i = 0; i < 10; i++) {
+ sprintf(buf, "%s%d", USBDEV, i);
+ f = open(buf, O_RDONLY);
+ if (f >= 0) {
+ ncont++;
+ dumpone(buf, f, addr);
+ close(f);
+ } else {
+ if (errno == EACCES)
+ warn("%s", buf);
+ }
+ }
+ if (verbose && ncont == 0)
+ printf("%s: no USB controllers found\n", __progname);
+ } else {
+ f = open(dev, O_RDONLY);
+ if (f >= 0)
+ dumpone(dev, f, addr);
+ else
+ err(1, "%s", dev);
+ }
+ exit(0);
+}
OpenPOWER on IntegriCloud