summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/FILES62
-rw-r--r--sys/dev/usb/Makefile.usbdevs16
-rw-r--r--sys/dev/usb/devlist2h.awk236
-rw-r--r--sys/dev/usb/dsbr100io.h49
-rw-r--r--sys/dev/usb/ehci.c2856
-rw-r--r--sys/dev/usb/ehci_pci.c347
-rw-r--r--sys/dev/usb/ehcireg.h293
-rw-r--r--sys/dev/usb/ehcivar.h153
-rw-r--r--sys/dev/usb/hid.c455
-rw-r--r--sys/dev/usb/hid.h91
-rw-r--r--sys/dev/usb/if_aue.c1556
-rw-r--r--sys/dev/usb/if_auereg.h265
-rw-r--r--sys/dev/usb/if_axe.c1179
-rw-r--r--sys/dev/usb/if_axereg.h180
-rw-r--r--sys/dev/usb/if_cue.c1204
-rw-r--r--sys/dev/usb/if_cuereg.h194
-rw-r--r--sys/dev/usb/if_kue.c1138
-rw-r--r--sys/dev/usb/if_kuereg.h186
-rw-r--r--sys/dev/usb/if_rue.c1497
-rw-r--r--sys/dev/usb/if_ruereg.h250
-rw-r--r--sys/dev/usb/if_udav.c2044
-rw-r--r--sys/dev/usb/if_udavreg.h234
-rw-r--r--sys/dev/usb/kue_fw.h685
-rw-r--r--sys/dev/usb/ohci.c3573
-rw-r--r--sys/dev/usb/ohci_pci.c370
-rw-r--r--sys/dev/usb/ohcireg.h249
-rw-r--r--sys/dev/usb/ohcivar.h170
-rw-r--r--sys/dev/usb/rio500_usb.h58
-rw-r--r--sys/dev/usb/ubsa.c771
-rw-r--r--sys/dev/usb/ubser.c1044
-rw-r--r--sys/dev/usb/ubser.h41
-rw-r--r--sys/dev/usb/ucom.c1164
-rw-r--r--sys/dev/usb/ucomvar.h182
-rw-r--r--sys/dev/usb/udbp.c843
-rw-r--r--sys/dev/usb/udbp.h80
-rw-r--r--sys/dev/usb/ufm.c474
-rw-r--r--sys/dev/usb/uftdi.c639
-rw-r--r--sys/dev/usb/uftdireg.h348
-rw-r--r--sys/dev/usb/ugen.c1471
-rw-r--r--sys/dev/usb/ugraphire_rdesc.h92
-rw-r--r--sys/dev/usb/uhci.c3538
-rw-r--r--sys/dev/usb/uhci_pci.c416
-rw-r--r--sys/dev/usb/uhcireg.h193
-rw-r--r--sys/dev/usb/uhcivar.h205
-rw-r--r--sys/dev/usb/uhid.c755
-rw-r--r--sys/dev/usb/uhub.c644
-rw-r--r--sys/dev/usb/ukbd.c1486
-rw-r--r--sys/dev/usb/ulpt.c883
-rw-r--r--sys/dev/usb/umass.c3072
-rw-r--r--sys/dev/usb/umct.c516
-rw-r--r--sys/dev/usb/umodem.c822
-rw-r--r--sys/dev/usb/ums.c828
-rw-r--r--sys/dev/usb/uplcom.c847
-rw-r--r--sys/dev/usb/urio.c688
-rw-r--r--sys/dev/usb/usb.c925
-rw-r--r--sys/dev/usb/usb.h693
-rw-r--r--sys/dev/usb/usb_ethersubr.c144
-rw-r--r--sys/dev/usb/usb_ethersubr.h47
-rw-r--r--sys/dev/usb/usb_if.m42
-rw-r--r--sys/dev/usb/usb_mem.c308
-rw-r--r--sys/dev/usb/usb_mem.h66
-rw-r--r--sys/dev/usb/usb_port.h520
-rw-r--r--sys/dev/usb/usb_quirks.c136
-rw-r--r--sys/dev/usb/usb_quirks.h61
-rw-r--r--sys/dev/usb/usb_subr.c1425
-rw-r--r--sys/dev/usb/usbcdc.h170
-rw-r--r--sys/dev/usb/usbdevs1347
-rw-r--r--sys/dev/usb/usbdevs.h1354
-rw-r--r--sys/dev/usb/usbdevs_data.h5335
-rw-r--r--sys/dev/usb/usbdi.c1153
-rw-r--r--sys/dev/usb/usbdi.h281
-rw-r--r--sys/dev/usb/usbdi_util.c508
-rw-r--r--sys/dev/usb/usbdi_util.h89
-rw-r--r--sys/dev/usb/usbdivar.h314
-rw-r--r--sys/dev/usb/usbhid.h184
-rw-r--r--sys/dev/usb/uscanner.c700
-rw-r--r--sys/dev/usb/uvisor.c588
-rw-r--r--sys/dev/usb/uvscom.c947
78 files changed, 58969 insertions, 0 deletions
diff --git a/sys/dev/usb/FILES b/sys/dev/usb/FILES
new file mode 100644
index 0000000..09db78c
--- /dev/null
+++ b/sys/dev/usb/FILES
@@ -0,0 +1,62 @@
+$FreeBSD$
+
+A small roadmap of the USB files:
+
+FILES this file
+Makefile to install .h files
+Makefile.usbdevs to run devlist2h.awk
+TODO just a list of things to do
+devlist2h.awk script to generate usbdevs*.h
+dsbr100io.h API for ufm.c
+ehci.c Host controller driver for EHCI
+ehcireg.h Hardware definitions for EHCI
+ehcivar.h API for ehci.c
+files.usb config include file
+hid.c subroutines to parse and access HID data
+hid.h API for hid.c
+if_aue.c USB Pegasus Ethernet driver
+if_auereg.h and definitions for it
+if_cue.c USB CATC Ethernet driver
+if_cuereg.h and definitions for it
+if_kue.c USB Kawasaki Ethernet driver
+if_kuereg.h and definitions for it
+if_upl.c USB Prolofic host-to-host driver
+ohci.c Host controller driver for OHCI
+ohcireg.h Hardware definitions for OHCI
+ohcivar.h API for ohci.c
+uaudio.c USB audio class driver
+uaudioreg.h and definitions for it
+ufm.c USB fm radio driver
+[Merged] ugen.c generic driver that can handle access to any USB device
+uhci.c Host controller driver for UHCI
+uhcireg.h Hardware definitions for UHCI
+uhcivar.h API for uhci.c
+uhid.c USB HID class driver
+uhub.c USB hub driver
+ukbd.c USB keyboard driver
+ukbdmap.c wscons key mapping for ukbd
+ukbdvar.h API for ukbd.c
+ulpt.c USB printer class driver
+umass.c USB mass storage driver
+umodem.c USB modem (CDC ACM) driver
+ums.c USB mouse driver
+urio.c USB Diamond Rio500 driver
+usb.c usb (bus) device driver
+usb.h general USB defines
+usb_mem.c memory allocation for DMAable memory
+usb_mem.h API for usb_mem.c
+usb_port.h compatibility defines for different OSs
+usb_quirks.c table of non-conforming USB devices and their problems
+usb_quirks.h API for usb_quirks.c
+usb_subr.c various subroutines used by USB code
+usbcdc.h USB CDC class definitions
+usbdevs data base of known device
+usbdevs.h generated from usbdevs
+usbdevs_data.h generated from usbdevs
+usbdi.c implementation of the USBDI API, which all drivers use
+usbdi.h API for usbdi.c
+usbdi_util.c utilities built on top of usbdi.h
+usbdi_util.h API for usbdi_util.c
+usbdivar.h internal defines and structures for usbdi.c
+uscanner.c minimal USB scanner driver
+usbhid.h USB HID class definitions
diff --git a/sys/dev/usb/Makefile.usbdevs b/sys/dev/usb/Makefile.usbdevs
new file mode 100644
index 0000000..319d066
--- /dev/null
+++ b/sys/dev/usb/Makefile.usbdevs
@@ -0,0 +1,16 @@
+# The files usbdevs.h and usbdevs_data.h are generated from usbdevs
+#
+# $FreeBSD$
+
+AWK= awk
+UNAME= uname
+RM= rm
+
+# The targets are always remade.
+
+.PHONY= all
+
+all: usbdevs devlist2h.awk
+ /bin/rm -f usbdevs.h usbdevs_data.h
+ ${AWK} -v type=USB -v os=`${UNAME} -s` -f devlist2h.awk usbdevs
+
diff --git a/sys/dev/usb/devlist2h.awk b/sys/dev/usb/devlist2h.awk
new file mode 100644
index 0000000..7b3e0df
--- /dev/null
+++ b/sys/dev/usb/devlist2h.awk
@@ -0,0 +1,236 @@
+#! /usr/bin/awk -f
+# $NetBSD: usb/devlist2h.awk,v 1.9 2001/01/18 20:28:22 jdolecek Exp $
+# $FreeBSD$
+#
+# Copyright (c) 1995, 1996 Christopher G. Demetriou
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Christopher G. Demetriou.
+# 4. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR 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.
+#
+BEGIN {
+ nproducts = nvendors = 0
+ dfile="usbdevs_data.h"
+ hfile="usbdevs.h"
+}
+NR == 1 {
+ VERSION = $0
+ gsub("\\$", "", VERSION)
+
+ if (os == "NetBSD")
+ printf("/*\t\$NetBSD\$\t*/\n\n") > dfile
+ else if (os == "FreeBSD")
+ printf("/* \$FreeBSD\$ */\n\n") > dfile
+ else if (os == "OpenBSD")
+ printf("/*\t\$OpenBSD\$\t*/\n\n") > dfile
+ else
+ printf("/* ??? */\n\n") > dfile
+ printf("/*\n") > dfile
+ printf(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n") \
+ > dfile
+ printf(" *\n") > dfile
+ printf(" * generated from:\n") > dfile
+ printf(" *\t%s\n", VERSION) > dfile
+ printf(" */\n") > dfile
+
+ if (os == "NetBSD")
+ printf("/*\t\$NetBSD\$\t*/\n\n") > hfile
+ else if (os == "FreeBSD")
+ printf("/* \$FreeBSD\$ */\n\n") > hfile
+ else if (os == "OpenBSD")
+ printf("/*\t\$OpenBSD\$\t*/\n\n") > hfile
+ else
+ printf("/* ??? */\n\n") > hfile
+ printf("/*\n") > hfile
+ printf(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n") \
+ > hfile
+ printf(" *\n") > hfile
+ printf(" * generated from:\n") > hfile
+ printf(" *\t%s\n", VERSION) > hfile
+ printf(" */\n") > hfile
+
+ next
+}
+$1 == "vendor" {
+ nvendors++
+
+ vendorindex[$2] = nvendors; # record index for this name, for later.
+ vendors[nvendors, 1] = $2; # name
+ vendors[nvendors, 2] = $3; # id
+ printf("#define\tUSB_VENDOR_%s\t%s\t", vendors[nvendors, 1],
+ vendors[nvendors, 2]) > hfile
+
+ i = 3; f = 4;
+
+ # comments
+ ocomment = oparen = 0
+ if (f <= NF) {
+ printf("\t/* ") > hfile
+ ocomment = 1;
+ }
+ while (f <= NF) {
+ if ($f == "#") {
+ printf("(") > hfile
+ oparen = 1
+ f++
+ continue
+ }
+ if (oparen) {
+ printf("%s", $f) > hfile
+ if (f < NF)
+ printf(" ") > hfile
+ f++
+ continue
+ }
+ vendors[nvendors, i] = $f
+ printf("%s", vendors[nvendors, i]) > hfile
+ if (f < NF)
+ printf(" ") > hfile
+ i++; f++;
+ }
+ if (oparen)
+ printf(")") > hfile
+ if (ocomment)
+ printf(" */") > hfile
+ printf("\n") > hfile
+
+ next
+}
+$1 == "product" {
+ nproducts++
+
+ products[nproducts, 1] = $2; # vendor name
+ products[nproducts, 2] = $3; # product id
+ products[nproducts, 3] = $4; # id
+ printf("#define\tUSB_PRODUCT_%s_%s\t%s\t", products[nproducts, 1],
+ products[nproducts, 2], products[nproducts, 3]) > hfile
+
+ i=4; f = 5;
+
+ # comments
+ ocomment = oparen = 0
+ if (f <= NF) {
+ printf("\t/* ") > hfile
+ ocomment = 1;
+ }
+ while (f <= NF) {
+ if ($f == "#") {
+ printf("(") > hfile
+ oparen = 1
+ f++
+ continue
+ }
+ if (oparen) {
+ printf("%s", $f) > hfile
+ if (f < NF)
+ printf(" ") > hfile
+ f++
+ continue
+ }
+ products[nproducts, i] = $f
+ printf("%s", products[nproducts, i]) > hfile
+ if (f < NF)
+ printf(" ") > hfile
+ i++; f++;
+ }
+ if (oparen)
+ printf(")") > hfile
+ if (ocomment)
+ printf(" */") > hfile
+ printf("\n") > hfile
+
+ next
+}
+{
+ if ($0 == "")
+ blanklines++
+ print $0 > hfile
+ if (blanklines < 2)
+ print $0 > dfile
+}
+END {
+ # print out the match tables
+
+ printf("\n") > dfile
+
+ printf("const struct usb_knowndev usb_knowndevs[] = {\n") > dfile
+ for (i = 1; i <= nproducts; i++) {
+ printf("\t{\n") > dfile
+ printf("\t USB_VENDOR_%s, USB_PRODUCT_%s_%s,\n",
+ products[i, 1], products[i, 1], products[i, 2]) \
+ > dfile
+ printf("\t ") > dfile
+ printf("0") > dfile
+ printf(",\n") > dfile
+
+ vendi = vendorindex[products[i, 1]];
+ printf("\t \"") > dfile
+ j = 3;
+ needspace = 0;
+ while (vendors[vendi, j] != "") {
+ if (needspace)
+ printf(" ") > dfile
+ printf("%s", vendors[vendi, j]) > dfile
+ needspace = 1
+ j++
+ }
+ printf("\",\n") > dfile
+
+ printf("\t \"") > dfile
+ j = 4;
+ needspace = 0;
+ while (products[i, j] != "") {
+ if (needspace)
+ printf(" ") > dfile
+ printf("%s", products[i, j]) > dfile
+ needspace = 1
+ j++
+ }
+ printf("\",\n") > dfile
+ printf("\t},\n") > dfile
+ }
+ for (i = 1; i <= nvendors; i++) {
+ printf("\t{\n") > dfile
+ printf("\t USB_VENDOR_%s, 0,\n", vendors[i, 1]) \
+ > dfile
+ printf("\t USB_KNOWNDEV_NOPROD,\n") \
+ > dfile
+ printf("\t \"") > dfile
+ j = 3;
+ needspace = 0;
+ while (vendors[i, j] != "") {
+ if (needspace)
+ printf(" ") > dfile
+ printf("%s", vendors[i, j]) > dfile
+ needspace = 1
+ j++
+ }
+ printf("\",\n") > dfile
+ printf("\t NULL,\n") > dfile
+ printf("\t},\n") > dfile
+ }
+ printf("\t{ 0, 0, 0, NULL, NULL, }\n") > dfile
+ printf("};\n") > dfile
+}
diff --git a/sys/dev/usb/dsbr100io.h b/sys/dev/usb/dsbr100io.h
new file mode 100644
index 0000000..aa53ead
--- /dev/null
+++ b/sys/dev/usb/dsbr100io.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2001 M. Warner Losh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+/* $FreeBSD$ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/ioccom.h>
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#define FM_SET_FREQ _IOWR('U', 200, int)
+#define FM_GET_FREQ _IOWR('U', 201, int)
+#define FM_START _IOWR('U', 202, int)
+#define FM_STOP _IOWR('U', 203, int)
+#define FM_GET_STAT _IOWR('U', 204, int)
+#else
+#define FM_SET_FREQ 0x1
+#define FM_GET_FREQ 0x2
+#define FM_START 0x3
+#define FM_STOP 0x4
+#define FM_GET_STAT 0x5
+#endif
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c
new file mode 100644
index 0000000..11dc97a
--- /dev/null
+++ b/sys/dev/usb/ehci.c
@@ -0,0 +1,2856 @@
+/* $NetBSD: ehci.c,v 1.46 2003/03/09 19:51:13 augustss Exp $ */
+
+/* Also ported from NetBSD:
+ * $NetBSD: ehci.c,v 1.50 2003/10/18 04:50:35 simonb Exp $
+ * $NetBSD: ehci.c,v 1.54 2004/01/17 13:15:05 jdolecek Exp $
+ * up to
+ * $NetBSD: ehci.c,v 1.64 2004/06/23 06:45:56 mycroft Exp $
+ */
+
+/*
+ * TODO
+ * hold off explorations by companion controllers until ehci has started.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) and by Charles M. Hannum.
+ *
+ * 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 Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
+ *
+ * The EHCI 1.0 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/docs/usb_20.zip
+ *
+ */
+
+/*
+ * TODO:
+ * 1) hold off explorations by companion controllers until ehci has started.
+ *
+ * 2) The EHCI driver lacks support for interrupt isochronous transfers, so
+ * devices using them don't work.
+ * Interrupt transfers are not difficult, it's just not done.
+ *
+ * 3) The meaty part to implement is the support for USB 2.0 hubs.
+ * They are quite compolicated since the need to be able to do
+ * "transaction translation", i.e., converting to/from USB 2 and USB 1.
+ * So the hub driver needs to handle and schedule these things, to
+ * assign place in frame where different devices get to go. See chapter
+ * on hubs in USB 2.0 for details.
+ *
+ * 4) command failures are not recovered correctly
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#include <sys/select.h>
+#elif defined(__FreeBSD__)
+#include <sys/endian.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/bus_pio.h>
+#include <machine/bus_memio.h>
+#include <sys/lockmgr.h>
+#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__)
+#include <machine/cpu.h>
+#endif
+#endif
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/endian.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/ehcireg.h>
+#include <dev/usb/ehcivar.h>
+
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+
+#define delay(d) DELAY(d)
+#endif
+
+#ifdef USB_DEBUG
+#define EHCI_DEBUG USB_DEBUG
+#define DPRINTF(x) if (ehcidebug) logprintf x
+#define DPRINTFN(n,x) if (ehcidebug>(n)) logprintf x
+int ehcidebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci");
+SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW,
+ &ehcidebug, 0, "ehci debug level");
+#ifndef __NetBSD__
+#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f))
+#endif
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct ehci_pipe {
+ struct usbd_pipe pipe;
+ int nexttoggle;
+
+ ehci_soft_qh_t *sqh;
+ union {
+ ehci_soft_qtd_t *qtd;
+ /* ehci_soft_itd_t *itd; */
+ } tail;
+ union {
+ /* Control pipe */
+ struct {
+ usb_dma_t reqdma;
+ u_int length;
+ /*ehci_soft_qtd_t *setup, *data, *stat;*/
+ } ctl;
+ /* Interrupt pipe */
+ /* XXX */
+ /* Bulk pipe */
+ struct {
+ u_int length;
+ } bulk;
+ /* Iso pipe */
+ /* XXX */
+ } u;
+};
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+Static void ehci_shutdown(void *);
+Static void ehci_power(int, void *);
+#endif
+
+Static usbd_status ehci_open(usbd_pipe_handle);
+Static void ehci_poll(struct usbd_bus *);
+Static void ehci_softintr(void *);
+Static int ehci_intr1(ehci_softc_t *);
+Static void ehci_waitintr(ehci_softc_t *, usbd_xfer_handle);
+Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *);
+Static void ehci_idone(struct ehci_xfer *);
+Static void ehci_timeout(void *);
+Static void ehci_timeout_task(void *);
+
+Static usbd_status ehci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t);
+Static void ehci_freem(struct usbd_bus *, usb_dma_t *);
+
+Static usbd_xfer_handle ehci_allocx(struct usbd_bus *);
+Static void ehci_freex(struct usbd_bus *, usbd_xfer_handle);
+
+Static usbd_status ehci_root_ctrl_transfer(usbd_xfer_handle);
+Static usbd_status ehci_root_ctrl_start(usbd_xfer_handle);
+Static void ehci_root_ctrl_abort(usbd_xfer_handle);
+Static void ehci_root_ctrl_close(usbd_pipe_handle);
+Static void ehci_root_ctrl_done(usbd_xfer_handle);
+
+Static usbd_status ehci_root_intr_transfer(usbd_xfer_handle);
+Static usbd_status ehci_root_intr_start(usbd_xfer_handle);
+Static void ehci_root_intr_abort(usbd_xfer_handle);
+Static void ehci_root_intr_close(usbd_pipe_handle);
+Static void ehci_root_intr_done(usbd_xfer_handle);
+
+Static usbd_status ehci_device_ctrl_transfer(usbd_xfer_handle);
+Static usbd_status ehci_device_ctrl_start(usbd_xfer_handle);
+Static void ehci_device_ctrl_abort(usbd_xfer_handle);
+Static void ehci_device_ctrl_close(usbd_pipe_handle);
+Static void ehci_device_ctrl_done(usbd_xfer_handle);
+
+Static usbd_status ehci_device_bulk_transfer(usbd_xfer_handle);
+Static usbd_status ehci_device_bulk_start(usbd_xfer_handle);
+Static void ehci_device_bulk_abort(usbd_xfer_handle);
+Static void ehci_device_bulk_close(usbd_pipe_handle);
+Static void ehci_device_bulk_done(usbd_xfer_handle);
+
+Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle);
+Static usbd_status ehci_device_intr_start(usbd_xfer_handle);
+Static void ehci_device_intr_abort(usbd_xfer_handle);
+Static void ehci_device_intr_close(usbd_pipe_handle);
+Static void ehci_device_intr_done(usbd_xfer_handle);
+
+Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle);
+Static usbd_status ehci_device_isoc_start(usbd_xfer_handle);
+Static void ehci_device_isoc_abort(usbd_xfer_handle);
+Static void ehci_device_isoc_close(usbd_pipe_handle);
+Static void ehci_device_isoc_done(usbd_xfer_handle);
+
+Static void ehci_device_clear_toggle(usbd_pipe_handle pipe);
+Static void ehci_noop(usbd_pipe_handle pipe);
+
+Static int ehci_str(usb_string_descriptor_t *, int, char *);
+Static void ehci_pcd(ehci_softc_t *, usbd_xfer_handle);
+Static void ehci_pcd_able(ehci_softc_t *, int);
+Static void ehci_pcd_enable(void *);
+Static void ehci_disown(ehci_softc_t *, int, int);
+
+Static ehci_soft_qh_t *ehci_alloc_sqh(ehci_softc_t *);
+Static void ehci_free_sqh(ehci_softc_t *, ehci_soft_qh_t *);
+
+Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *);
+Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *);
+Static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *,
+ ehci_softc_t *, int, int, usbd_xfer_handle,
+ ehci_soft_qtd_t **, ehci_soft_qtd_t **);
+Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *,
+ ehci_soft_qtd_t *);
+
+Static usbd_status ehci_device_request(usbd_xfer_handle xfer);
+
+Static void ehci_add_qh(ehci_soft_qh_t *, ehci_soft_qh_t *);
+Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *,
+ ehci_soft_qh_t *);
+Static void ehci_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_t *);
+Static void ehci_sync_hc(ehci_softc_t *);
+
+Static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *);
+Static void ehci_abort_xfer(usbd_xfer_handle, usbd_status);
+
+#ifdef EHCI_DEBUG
+Static void ehci_dump_regs(ehci_softc_t *);
+void ehci_dump(void);
+Static ehci_softc_t *theehci;
+Static void ehci_dump_link(ehci_link_t, int);
+Static void ehci_dump_sqtds(ehci_soft_qtd_t *);
+Static void ehci_dump_sqtd(ehci_soft_qtd_t *);
+Static void ehci_dump_qtd(ehci_qtd_t *);
+Static void ehci_dump_sqh(ehci_soft_qh_t *);
+#ifdef DIAGNOSTIC
+Static void ehci_dump_exfer(struct ehci_xfer *);
+#endif
+#endif
+
+#define EHCI_NULL htole32(EHCI_LINK_TERMINATE)
+
+#define EHCI_INTR_ENDPT 1
+
+#define ehci_add_intr_list(sc, ex) \
+ LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ex), inext);
+#define ehci_del_intr_list(ex) \
+ do { \
+ LIST_REMOVE((ex), inext); \
+ (ex)->inext.le_prev = NULL; \
+ } while (0)
+#define ehci_active_intr_list(ex) ((ex)->inext.le_prev != NULL)
+
+Static struct usbd_bus_methods ehci_bus_methods = {
+ ehci_open,
+ ehci_softintr,
+ ehci_poll,
+ ehci_allocm,
+ ehci_freem,
+ ehci_allocx,
+ ehci_freex,
+};
+
+Static struct usbd_pipe_methods ehci_root_ctrl_methods = {
+ ehci_root_ctrl_transfer,
+ ehci_root_ctrl_start,
+ ehci_root_ctrl_abort,
+ ehci_root_ctrl_close,
+ ehci_noop,
+ ehci_root_ctrl_done,
+};
+
+Static struct usbd_pipe_methods ehci_root_intr_methods = {
+ ehci_root_intr_transfer,
+ ehci_root_intr_start,
+ ehci_root_intr_abort,
+ ehci_root_intr_close,
+ ehci_noop,
+ ehci_root_intr_done,
+};
+
+Static struct usbd_pipe_methods ehci_device_ctrl_methods = {
+ ehci_device_ctrl_transfer,
+ ehci_device_ctrl_start,
+ ehci_device_ctrl_abort,
+ ehci_device_ctrl_close,
+ ehci_noop,
+ ehci_device_ctrl_done,
+};
+
+Static struct usbd_pipe_methods ehci_device_intr_methods = {
+ ehci_device_intr_transfer,
+ ehci_device_intr_start,
+ ehci_device_intr_abort,
+ ehci_device_intr_close,
+ ehci_device_clear_toggle,
+ ehci_device_intr_done,
+};
+
+Static struct usbd_pipe_methods ehci_device_bulk_methods = {
+ ehci_device_bulk_transfer,
+ ehci_device_bulk_start,
+ ehci_device_bulk_abort,
+ ehci_device_bulk_close,
+ ehci_device_clear_toggle,
+ ehci_device_bulk_done,
+};
+
+Static struct usbd_pipe_methods ehci_device_isoc_methods = {
+ ehci_device_isoc_transfer,
+ ehci_device_isoc_start,
+ ehci_device_isoc_abort,
+ ehci_device_isoc_close,
+ ehci_noop,
+ ehci_device_isoc_done,
+};
+
+usbd_status
+ehci_init(ehci_softc_t *sc)
+{
+ u_int32_t version, sparams, cparams, hcr;
+ u_int i;
+ usbd_status err;
+ ehci_soft_qh_t *sqh;
+
+ DPRINTF(("ehci_init: start\n"));
+#ifdef EHCI_DEBUG
+ theehci = sc;
+#endif
+
+ sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH);
+
+ version = EREAD2(sc, EHCI_HCIVERSION);
+ printf("%s: EHCI version %x.%x\n", USBDEVNAME(sc->sc_bus.bdev),
+ version >> 8, version & 0xff);
+
+ sparams = EREAD4(sc, EHCI_HCSPARAMS);
+ DPRINTF(("ehci_init: sparams=0x%x\n", sparams));
+ sc->sc_npcomp = EHCI_HCS_N_PCC(sparams);
+ if (EHCI_HCS_N_CC(sparams) != sc->sc_ncomp) {
+ printf("%s: wrong number of companions (%d != %d)\n",
+ USBDEVNAME(sc->sc_bus.bdev),
+ EHCI_HCS_N_CC(sparams), sc->sc_ncomp);
+ return (USBD_IOERROR);
+ }
+ if (sc->sc_ncomp > 0) {
+ printf("%s: companion controller%s, %d port%s each:",
+ USBDEVNAME(sc->sc_bus.bdev), sc->sc_ncomp!=1 ? "s" : "",
+ EHCI_HCS_N_PCC(sparams),
+ EHCI_HCS_N_PCC(sparams)!=1 ? "s" : "");
+ for (i = 0; i < sc->sc_ncomp; i++)
+ printf(" %s", USBDEVNAME(sc->sc_comps[i]->bdev));
+ printf("\n");
+ }
+ sc->sc_noport = EHCI_HCS_N_PORTS(sparams);
+ cparams = EREAD4(sc, EHCI_HCCPARAMS);
+ DPRINTF(("ehci_init: cparams=0x%x\n", cparams));
+
+ if (EHCI_HCC_64BIT(cparams)) {
+ /* MUST clear segment register if 64 bit capable. */
+ EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
+ }
+
+ sc->sc_bus.usbrev = USBREV_2_0;
+
+ /* Reset the controller */
+ DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev)));
+ EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */
+ usb_delay_ms(&sc->sc_bus, 1);
+ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
+ for (i = 0; i < 100; i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET;
+ if (!hcr)
+ break;
+ }
+ if (hcr) {
+ printf("%s: reset timeout\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ return (USBD_IOERROR);
+ }
+
+ /* frame list size at default, read back what we got and use that */
+ switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) {
+ case 0: sc->sc_flsize = 1024*4; break;
+ case 1: sc->sc_flsize = 512*4; break;
+ case 2: sc->sc_flsize = 256*4; break;
+ case 3: return (USBD_IOERROR);
+ }
+ err = usb_allocmem(&sc->sc_bus, sc->sc_flsize,
+ EHCI_FLALIGN_ALIGN, &sc->sc_fldma);
+ if (err)
+ return (err);
+ DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize));
+
+ /* Set up the bus struct. */
+ sc->sc_bus.methods = &ehci_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct ehci_pipe);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sc->sc_powerhook = powerhook_establish(ehci_power, sc);
+ sc->sc_shutdownhook = shutdownhook_establish(ehci_shutdown, sc);
+#endif
+
+ sc->sc_eintrs = EHCI_NORMAL_INTRS;
+
+ /* Allocate dummy QH that starts the async list. */
+ sqh = ehci_alloc_sqh(sc);
+ if (sqh == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ /* Fill the QH */
+ sqh->qh.qh_endp =
+ htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL);
+ sqh->qh.qh_link =
+ htole32(sqh->physaddr | EHCI_LINK_QH);
+ sqh->qh.qh_curqtd = EHCI_NULL;
+ sqh->next = NULL;
+ /* Fill the overlay qTD */
+ sqh->qh.qh_qtd.qtd_next = EHCI_NULL;
+ sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
+ sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED);
+ sqh->sqtd = NULL;
+#ifdef EHCI_DEBUG
+ if (ehcidebug) {
+ ehci_dump_sqh(sqh);
+ }
+#endif
+
+ /* Point to async list */
+ sc->sc_async_head = sqh;
+ EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH);
+
+ usb_callout_init(sc->sc_tmo_pcd);
+
+ lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0);
+
+ /* Enable interrupts */
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+ /* Turn on controller */
+ EOWRITE4(sc, EHCI_USBCMD,
+ EHCI_CMD_ITC_8 | /* 8 microframes */
+ (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) |
+ EHCI_CMD_ASE |
+ /* EHCI_CMD_PSE | */
+ EHCI_CMD_RS);
+
+ /* Take over port ownership */
+ EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF);
+
+ for (i = 0; i < 100; i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+ if (!hcr)
+ break;
+ }
+ if (hcr) {
+ printf("%s: run timeout\n", USBDEVNAME(sc->sc_bus.bdev));
+ return (USBD_IOERROR);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+
+#if 0
+ bad2:
+ ehci_free_sqh(sc, sc->sc_async_head);
+#endif
+ bad1:
+ usb_freemem(&sc->sc_bus, &sc->sc_fldma);
+ return (err);
+}
+
+int
+ehci_intr(void *v)
+{
+ ehci_softc_t *sc = v;
+
+ if (sc == NULL || sc->sc_dying)
+ return (0);
+
+ /* If we get an interrupt while polling, then just ignore it. */
+ if (sc->sc_bus.use_polling) {
+#ifdef DIAGNOSTIC
+ printf("ehci_intr: ignored interrupt while polling\n");
+#endif
+ return (0);
+ }
+
+ return (ehci_intr1(sc));
+}
+
+Static int
+ehci_intr1(ehci_softc_t *sc)
+{
+ u_int32_t intrs, eintrs;
+
+ DPRINTFN(20,("ehci_intr1: enter\n"));
+
+ /* In case the interrupt occurs before initialization has completed. */
+ if (sc == NULL) {
+#ifdef DIAGNOSTIC
+ printf("ehci_intr: sc == NULL\n");
+#endif
+ return (0);
+ }
+
+ intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
+
+ if (!intrs)
+ return (0);
+
+ EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */
+ eintrs = intrs & sc->sc_eintrs;
+ DPRINTFN(7, ("ehci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n",
+ sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS),
+ (u_int)eintrs));
+ if (!eintrs)
+ return (0);
+
+ sc->sc_bus.intr_context++;
+ sc->sc_bus.no_intrs++;
+ if (eintrs & EHCI_STS_IAA) {
+ DPRINTF(("ehci_intr1: door bell\n"));
+ wakeup(&sc->sc_async_head);
+ eintrs &= ~EHCI_STS_IAA;
+ }
+ if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) {
+ DPRINTFN(5,("ehci_intr1: %s %s\n",
+ eintrs & EHCI_STS_INT ? "INT" : "",
+ eintrs & EHCI_STS_ERRINT ? "ERRINT" : ""));
+ usb_schedsoftintr(&sc->sc_bus);
+ eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT);
+ }
+ if (eintrs & EHCI_STS_HSE) {
+ printf("%s: unrecoverable error, controller halted\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ /* XXX what else */
+ }
+ if (eintrs & EHCI_STS_PCD) {
+ ehci_pcd(sc, sc->sc_intrxfer);
+ /*
+ * Disable PCD interrupt for now, because it will be
+ * on until the port has been reset.
+ */
+ ehci_pcd_able(sc, 0);
+ /* Do not allow RHSC interrupts > 1 per second */
+ usb_callout(sc->sc_tmo_pcd, hz, ehci_pcd_enable, sc);
+ eintrs &= ~EHCI_STS_PCD;
+ }
+
+ sc->sc_bus.intr_context--;
+
+ if (eintrs != 0) {
+ /* Block unprocessed interrupts. */
+ sc->sc_eintrs &= ~eintrs;
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+ printf("%s: blocking intrs 0x%x\n",
+ USBDEVNAME(sc->sc_bus.bdev), eintrs);
+ }
+
+ return (1);
+}
+
+void
+ehci_pcd_able(ehci_softc_t *sc, int on)
+{
+ DPRINTFN(4, ("ehci_pcd_able: on=%d\n", on));
+ if (on)
+ sc->sc_eintrs |= EHCI_STS_PCD;
+ else
+ sc->sc_eintrs &= ~EHCI_STS_PCD;
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+}
+
+void
+ehci_pcd_enable(void *v_sc)
+{
+ ehci_softc_t *sc = v_sc;
+
+ ehci_pcd_able(sc, 1);
+}
+
+void
+ehci_pcd(ehci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe;
+ u_char *p;
+ int i, m;
+
+ if (xfer == NULL) {
+ /* Just ignore the change. */
+ return;
+ }
+
+ pipe = xfer->pipe;
+
+ p = KERNADDR(&xfer->dmabuf, 0);
+ m = min(sc->sc_noport, xfer->length * 8 - 1);
+ memset(p, 0, xfer->length);
+ for (i = 1; i <= m; i++) {
+ /* Pick out CHANGE bits from the status reg. */
+ if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR)
+ p[i/8] |= 1 << (i%8);
+ }
+ DPRINTF(("ehci_pcd: change=0x%02x\n", *p));
+ xfer->actlen = xfer->length;
+ xfer->status = USBD_NORMAL_COMPLETION;
+
+ usb_transfer_complete(xfer);
+}
+
+void
+ehci_softintr(void *v)
+{
+ ehci_softc_t *sc = v;
+ struct ehci_xfer *ex, *nextex;
+
+ DPRINTFN(10,("%s: ehci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev),
+ sc->sc_bus.intr_context));
+
+ sc->sc_bus.intr_context++;
+
+ /*
+ * The only explanation I can think of for why EHCI is as brain dead
+ * as UHCI interrupt-wise is that Intel was involved in both.
+ * An interrupt just tells us that something is done, we have no
+ * clue what, so we need to scan through all active transfers. :-(
+ */
+ for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = nextex) {
+ nextex = LIST_NEXT(ex, inext);
+ ehci_check_intr(sc, ex);
+ }
+
+#ifdef USB_USE_SOFTINTR
+ if (sc->sc_softwake) {
+ sc->sc_softwake = 0;
+ wakeup(&sc->sc_softwake);
+ }
+#endif /* USB_USE_SOFTINTR */
+
+ sc->sc_bus.intr_context--;
+}
+
+/* Check for an interrupt. */
+void
+ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+ ehci_soft_qtd_t *sqtd, *lsqtd;
+ u_int32_t status;
+
+ DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex));
+
+ if (ex->sqtdstart == NULL) {
+ printf("ehci_check_intr: sqtdstart=NULL\n");
+ return;
+ }
+ lsqtd = ex->sqtdend;
+#ifdef DIAGNOSTIC
+ if (lsqtd == NULL) {
+ printf("ehci_check_intr: sqtd==0\n");
+ return;
+ }
+#endif
+ /*
+ * If the last TD is still active we need to check whether there
+ * is a an error somewhere in the middle, or whether there was a
+ * short packet (SPD and not ACTIVE).
+ */
+ if (le32toh(lsqtd->qtd.qtd_status) & EHCI_QTD_ACTIVE) {
+ DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex));
+ for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) {
+ status = le32toh(sqtd->qtd.qtd_status);
+ /* If there's an active QTD the xfer isn't done. */
+ if (status & EHCI_QTD_ACTIVE)
+ break;
+ /* Any kind of error makes the xfer done. */
+ if (status & EHCI_QTD_HALTED)
+ goto done;
+ /* We want short packets, and it is short: it's done */
+ if (EHCI_QTD_GET_BYTES(status) != 0)
+ goto done;
+ }
+ DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n",
+ ex, ex->sqtdstart));
+ return;
+ }
+ done:
+ DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex));
+ usb_uncallout(ex->xfer.timeout_handle, ehci_timeout, ex);
+ ehci_idone(ex);
+}
+
+void
+ehci_idone(struct ehci_xfer *ex)
+{
+ usbd_xfer_handle xfer = &ex->xfer;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ ehci_soft_qtd_t *sqtd;
+ u_int32_t status = 0, nstatus;
+ int actlen;
+
+ DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex));
+#ifdef DIAGNOSTIC
+ {
+ int s = splhigh();
+ if (ex->isdone) {
+ splx(s);
+#ifdef EHCI_DEBUG
+ printf("ehci_idone: ex is done!\n ");
+ ehci_dump_exfer(ex);
+#else
+ printf("ehci_idone: ex=%p is done!\n", ex);
+#endif
+ return;
+ }
+ ex->isdone = 1;
+ splx(s);
+ }
+#endif
+
+ if (xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer));
+ return;
+ }
+
+#ifdef EHCI_DEBUG
+ DPRINTFN(/*10*/2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer, epipe));
+ if (ehcidebug > 10)
+ ehci_dump_sqtds(ex->sqtdstart);
+#endif
+
+ /* The transfer is done, compute actual length and status. */
+ actlen = 0;
+ for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) {
+ nstatus = le32toh(sqtd->qtd.qtd_status);
+ if (nstatus & EHCI_QTD_ACTIVE)
+ break;
+
+ status = nstatus;
+ /* halt is ok if descriptor is last, and complete */
+ if (sqtd->qtd.qtd_next == EHCI_NULL &&
+ EHCI_QTD_GET_BYTES(status) == 0)
+ status &= ~EHCI_QTD_HALTED;
+ if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP)
+ actlen += sqtd->len - EHCI_QTD_GET_BYTES(status);
+ }
+
+ /* If there are left over TDs we need to update the toggle. */
+ if (sqtd != NULL) {
+ printf("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus);
+#if 0
+ ehci_dump_sqh(epipe->sqh);
+ ehci_dump_sqtds(ex->sqtdstart);
+#endif
+ epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus);
+ }
+
+ status &= EHCI_QTD_STATERRS;
+ DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n",
+ xfer->length, actlen, status));
+ xfer->actlen = actlen;
+ if (status != 0) {
+#ifdef EHCI_DEBUG
+ char sbuf[128];
+
+ bitmask_snprintf((u_int32_t)status,
+ "\20\7HALTED\6BUFERR\5BABBLE\4XACTERR"
+ "\3MISSED", sbuf, sizeof(sbuf));
+
+ DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2,
+ ("ehci_idone: error, addr=%d, endpt=0x%02x, "
+ "status 0x%s\n",
+ xfer->pipe->device->address,
+ xfer->pipe->endpoint->edesc->bEndpointAddress,
+ sbuf));
+ if (ehcidebug > 2) {
+ ehci_dump_sqh(epipe->sqh);
+ ehci_dump_sqtds(ex->sqtdstart);
+ }
+#endif
+ if (status == EHCI_QTD_HALTED)
+ xfer->status = USBD_STALLED;
+ else
+ xfer->status = USBD_IOERROR; /* more info XXX */
+ } else {
+ xfer->status = USBD_NORMAL_COMPLETION;
+ }
+
+ usb_transfer_complete(xfer);
+ DPRINTFN(/*12*/2, ("ehci_idone: ex=%p done\n", ex));
+}
+
+/*
+ * Wait here until controller claims to have an interrupt.
+ * Then call ehci_intr and return. Use timeout to avoid waiting
+ * too long.
+ */
+void
+ehci_waitintr(ehci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ int timo = xfer->timeout;
+ int usecs;
+ u_int32_t intrs;
+
+ xfer->status = USBD_IN_PROGRESS;
+ for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ if (sc->sc_dying)
+ break;
+ intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)) &
+ sc->sc_eintrs;
+ DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs));
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 15)
+ ehci_dump_regs(sc);
+#endif
+ if (intrs) {
+ ehci_intr1(sc);
+ if (xfer->status != USBD_IN_PROGRESS)
+ return;
+ }
+ }
+
+ /* Timeout */
+ DPRINTF(("ehci_waitintr: timeout\n"));
+ xfer->status = USBD_TIMEOUT;
+ usb_transfer_complete(xfer);
+ /* XXX should free TD */
+}
+
+void
+ehci_poll(struct usbd_bus *bus)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)bus;
+#ifdef EHCI_DEBUG
+ static int last;
+ int new;
+ new = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
+ if (new != last) {
+ DPRINTFN(10,("ehci_poll: intrs=0x%04x\n", new));
+ last = new;
+ }
+#endif
+
+ if (EOREAD4(sc, EHCI_USBSTS) & sc->sc_eintrs)
+ ehci_intr1(sc);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+ehci_detach(struct ehci_softc *sc, int flags)
+{
+ int rv = 0;
+
+ if (sc->sc_child != NULL)
+ rv = config_detach(sc->sc_child, flags);
+
+ if (rv != 0)
+ return (rv);
+
+ usb_uncallout(sc->sc_tmo_pcd, ehci_pcd_enable, sc);
+
+ if (sc->sc_powerhook != NULL)
+ powerhook_disestablish(sc->sc_powerhook);
+ if (sc->sc_shutdownhook != NULL)
+ shutdownhook_disestablish(sc->sc_shutdownhook);
+
+ usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
+
+ /* XXX free other data structures XXX */
+
+ return (rv);
+}
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+ehci_activate(device_ptr_t self, enum devact act)
+{
+ struct ehci_softc *sc = (struct ehci_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ if (sc->sc_child != NULL)
+ rv = config_deactivate(sc->sc_child);
+ sc->sc_dying = 1;
+ break;
+ }
+ return (rv);
+}
+#endif
+
+/*
+ * Handle suspend/resume.
+ *
+ * We need to switch to polling mode here, because this routine is
+ * called from an intterupt context. This is all right since we
+ * are almost suspended anyway.
+ */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+void
+ehci_power(int why, void *v)
+{
+ ehci_softc_t *sc = v;
+ //u_int32_t ctl;
+ int s;
+
+#ifdef EHCI_DEBUG
+ DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why));
+ ehci_dump_regs(sc);
+#endif
+
+ s = splhardusb();
+ switch (why) {
+ case PWR_SUSPEND:
+ case PWR_STANDBY:
+ sc->sc_bus.use_polling++;
+#if 0
+OOO
+ ctl = OREAD4(sc, EHCI_CONTROL) & ~EHCI_HCFS_MASK;
+ if (sc->sc_control == 0) {
+ /*
+ * Preserve register values, in case that APM BIOS
+ * does not recover them.
+ */
+ sc->sc_control = ctl;
+ sc->sc_intre = OREAD4(sc, EHCI_INTERRUPT_ENABLE);
+ }
+ ctl |= EHCI_HCFS_SUSPEND;
+ OWRITE4(sc, EHCI_CONTROL, ctl);
+#endif
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+ sc->sc_bus.use_polling--;
+ break;
+ case PWR_RESUME:
+ sc->sc_bus.use_polling++;
+#if 0
+OOO
+ /* Some broken BIOSes do not recover these values */
+ OWRITE4(sc, EHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0));
+ OWRITE4(sc, EHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr);
+ OWRITE4(sc, EHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr);
+ if (sc->sc_intre)
+ OWRITE4(sc, EHCI_INTERRUPT_ENABLE,
+ sc->sc_intre & (EHCI_ALL_INTRS | EHCI_MIE));
+ if (sc->sc_control)
+ ctl = sc->sc_control;
+ else
+ ctl = OREAD4(sc, EHCI_CONTROL);
+ ctl |= EHCI_HCFS_RESUME;
+ OWRITE4(sc, EHCI_CONTROL, ctl);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY);
+ ctl = (ctl & ~EHCI_HCFS_MASK) | EHCI_HCFS_OPERATIONAL;
+ OWRITE4(sc, EHCI_CONTROL, ctl);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY);
+ sc->sc_control = sc->sc_intre = 0;
+#endif
+ sc->sc_bus.use_polling--;
+ break;
+ case PWR_SOFTSUSPEND:
+ case PWR_SOFTSTANDBY:
+ case PWR_SOFTRESUME:
+ break;
+ }
+ splx(s);
+}
+#endif
+
+/*
+ * Shut down the controller when the system is going down.
+ */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+void
+ehci_shutdown(void *v)
+{
+ ehci_softc_t *sc = v;
+
+ DPRINTF(("ehci_shutdown: stopping the HC\n"));
+ EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */
+ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
+}
+#endif
+
+usbd_status
+ehci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
+{
+ usbd_status err;
+
+ err = usb_allocmem(bus, size, 0, dma);
+#ifdef EHCI_DEBUG
+ if (err)
+ printf("ehci_allocm: usb_allocmem()=%d\n", err);
+#endif
+ return (err);
+}
+
+void
+ehci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ usb_freemem(bus, dma);
+}
+
+usbd_xfer_handle
+ehci_allocx(struct usbd_bus *bus)
+{
+ struct ehci_softc *sc = (struct ehci_softc *)bus;
+ usbd_xfer_handle xfer;
+
+ xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers);
+ if (xfer != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_FREE) {
+ printf("uhci_allocx: xfer=%p not free, 0x%08x\n", xfer,
+ xfer->busy_free);
+ }
+#endif
+ } else {
+ xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT);
+ }
+ if (xfer != NULL) {
+ memset(xfer, 0, sizeof (struct ehci_xfer));
+#ifdef DIAGNOSTIC
+ EXFER(xfer)->isdone = 1;
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
+ return (xfer);
+}
+
+void
+ehci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
+{
+ struct ehci_softc *sc = (struct ehci_softc *)bus;
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer,
+ xfer->busy_free);
+ return;
+ }
+ xfer->busy_free = XFER_FREE;
+ if (!EXFER(xfer)->isdone) {
+ printf("ehci_freex: !isdone\n");
+ return;
+ }
+#endif
+ SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+Static void
+ehci_device_clear_toggle(usbd_pipe_handle pipe)
+{
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+
+ DPRINTF(("ehci_device_clear_toggle: epipe=%p status=0x%x\n",
+ epipe, epipe->sqh->qh.qh_qtd.qtd_status));
+#ifdef USB_DEBUG
+ if (ehcidebug)
+ usbd_dump_pipe(pipe);
+#endif
+ epipe->nexttoggle = 0;
+}
+
+Static void
+ehci_noop(usbd_pipe_handle pipe)
+{
+}
+
+#ifdef EHCI_DEBUG
+void
+ehci_dump_regs(ehci_softc_t *sc)
+{
+ int i;
+ printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n",
+ EOREAD4(sc, EHCI_USBCMD),
+ EOREAD4(sc, EHCI_USBSTS),
+ EOREAD4(sc, EHCI_USBINTR));
+ printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n",
+ EOREAD4(sc, EHCI_FRINDEX),
+ EOREAD4(sc, EHCI_CTRLDSSEGMENT),
+ EOREAD4(sc, EHCI_PERIODICLISTBASE),
+ EOREAD4(sc, EHCI_ASYNCLISTADDR));
+ for (i = 1; i <= sc->sc_noport; i++)
+ printf("port %d status=0x%08x\n", i,
+ EOREAD4(sc, EHCI_PORTSC(i)));
+}
+
+/*
+ * Unused function - this is meant to be called from a kernel
+ * debugger.
+ */
+void
+ehci_dump()
+{
+ ehci_dump_regs(theehci);
+}
+
+void
+ehci_dump_link(ehci_link_t link, int type)
+{
+ link = le32toh(link);
+ printf("0x%08x", link);
+ if (link & EHCI_LINK_TERMINATE)
+ printf("<T>");
+ else {
+ printf("<");
+ if (type) {
+ switch (EHCI_LINK_TYPE(link)) {
+ case EHCI_LINK_ITD: printf("ITD"); break;
+ case EHCI_LINK_QH: printf("QH"); break;
+ case EHCI_LINK_SITD: printf("SITD"); break;
+ case EHCI_LINK_FSTN: printf("FSTN"); break;
+ }
+ }
+ printf(">");
+ }
+}
+
+void
+ehci_dump_sqtds(ehci_soft_qtd_t *sqtd)
+{
+ int i;
+ u_int32_t stop;
+
+ stop = 0;
+ for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) {
+ ehci_dump_sqtd(sqtd);
+ stop = sqtd->qtd.qtd_next & EHCI_LINK_TERMINATE;
+ }
+ if (sqtd)
+ printf("dump aborted, too many TDs\n");
+}
+
+void
+ehci_dump_sqtd(ehci_soft_qtd_t *sqtd)
+{
+ printf("QTD(%p) at 0x%08x:\n", sqtd, sqtd->physaddr);
+ ehci_dump_qtd(&sqtd->qtd);
+}
+
+void
+ehci_dump_qtd(ehci_qtd_t *qtd)
+{
+ u_int32_t s;
+ char sbuf[128];
+
+ printf(" next="); ehci_dump_link(qtd->qtd_next, 0);
+ printf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0);
+ printf("\n");
+ s = le32toh(qtd->qtd_status);
+ bitmask_snprintf(EHCI_QTD_GET_STATUS(s),
+ "\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE\4XACTERR"
+ "\3MISSED\2SPLIT\1PING", sbuf, sizeof(sbuf));
+ printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n",
+ s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s),
+ EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s));
+ printf(" cerr=%d pid=%d stat=0x%s\n", EHCI_QTD_GET_CERR(s),
+ EHCI_QTD_GET_PID(s), sbuf);
+ for (s = 0; s < 5; s++)
+ printf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s]));
+}
+
+void
+ehci_dump_sqh(ehci_soft_qh_t *sqh)
+{
+ ehci_qh_t *qh = &sqh->qh;
+ u_int32_t endp, endphub;
+
+ printf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr);
+ printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n");
+ endp = le32toh(qh->qh_endp);
+ printf(" endp=0x%08x\n", endp);
+ printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n",
+ EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp),
+ EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp),
+ EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp));
+ printf(" mpl=0x%x ctl=%d nrl=%d\n",
+ EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp),
+ EHCI_QH_GET_NRL(endp));
+ endphub = le32toh(qh->qh_endphub);
+ printf(" endphub=0x%08x\n", endphub);
+ printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n",
+ EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub),
+ EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub),
+ EHCI_QH_GET_MULT(endphub));
+ printf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); printf("\n");
+ printf("Overlay qTD:\n");
+ ehci_dump_qtd(&qh->qh_qtd);
+}
+
+#ifdef DIAGNOSTIC
+Static void
+ehci_dump_exfer(struct ehci_xfer *ex)
+{
+ printf("ehci_dump_exfer: ex=%p\n", ex);
+}
+#endif
+#endif
+
+usbd_status
+ehci_open(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
+ u_int8_t addr = dev->address;
+ u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+ ehci_soft_qh_t *sqh;
+ usbd_status err;
+ int s;
+ int speed, naks;
+
+ DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
+ pipe, addr, ed->bEndpointAddress, sc->sc_addr));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ epipe->nexttoggle = 0;
+
+ if (addr == sc->sc_addr) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &ehci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | EHCI_INTR_ENDPT:
+ pipe->methods = &ehci_root_intr_methods;
+ break;
+ default:
+ return (USBD_INVAL);
+ }
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ /* XXX All this stuff is only valid for async. */
+ switch (dev->speed) {
+ case USB_SPEED_LOW: speed = EHCI_QH_SPEED_LOW; break;
+ case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break;
+ case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break;
+ default: panic("ehci_open: bad device speed %d", dev->speed);
+ }
+ naks = 8; /* XXX */
+ sqh = ehci_alloc_sqh(sc);
+ if (sqh == NULL)
+ goto bad0;
+ /* qh_link filled when the QH is added */
+ sqh->qh.qh_endp = htole32(
+ EHCI_QH_SET_ADDR(addr) |
+ EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
+ EHCI_QH_SET_EPS(speed) |
+ EHCI_QH_DTC |
+ EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
+ (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
+ EHCI_QH_CTL : 0) |
+ EHCI_QH_SET_NRL(naks)
+ );
+ sqh->qh.qh_endphub = htole32(
+ EHCI_QH_SET_MULT(1)
+ /* XXX TT stuff */
+ /* XXX interrupt mask */
+ );
+ sqh->qh.qh_curqtd = EHCI_NULL;
+ /* Fill the overlay qTD */
+ sqh->qh.qh_qtd.qtd_next = EHCI_NULL;
+ sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
+ sqh->qh.qh_qtd.qtd_status = htole32(0);
+
+ epipe->sqh = sqh;
+
+ switch (xfertype) {
+ case UE_CONTROL:
+ err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t),
+ 0, &epipe->u.ctl.reqdma);
+#ifdef EHCI_DEBUG
+ if (err)
+ printf("ehci_open: usb_allocmem()=%d\n", err);
+#endif
+ if (err)
+ goto bad1;
+ pipe->methods = &ehci_device_ctrl_methods;
+ s = splusb();
+ ehci_add_qh(sqh, sc->sc_async_head);
+ splx(s);
+ break;
+ case UE_BULK:
+ pipe->methods = &ehci_device_bulk_methods;
+ s = splusb();
+ ehci_add_qh(sqh, sc->sc_async_head);
+ splx(s);
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &ehci_device_intr_methods;
+ return (USBD_INVAL);
+ case UE_ISOCHRONOUS:
+ pipe->methods = &ehci_device_isoc_methods;
+ return (USBD_INVAL);
+ default:
+ return (USBD_INVAL);
+ }
+ return (USBD_NORMAL_COMPLETION);
+
+ bad1:
+ ehci_free_sqh(sc, sqh);
+ bad0:
+ return (USBD_NOMEM);
+}
+
+/*
+ * Add an ED to the schedule. Called at splusb().
+ */
+void
+ehci_add_qh(ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
+{
+ SPLUSBCHECK;
+
+ sqh->next = head->next;
+ sqh->qh.qh_link = head->qh.qh_link;
+ head->next = sqh;
+ head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH);
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 5) {
+ printf("ehci_add_qh:\n");
+ ehci_dump_sqh(sqh);
+ }
+#endif
+}
+
+/*
+ * Remove an ED from the schedule. Called at splusb().
+ */
+void
+ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
+{
+ ehci_soft_qh_t *p;
+
+ SPLUSBCHECK;
+ /* XXX */
+ for (p = head; p != NULL && p->next != sqh; p = p->next)
+ ;
+ if (p == NULL)
+ panic("ehci_rem_qh: ED not found");
+ p->next = sqh->next;
+ p->qh.qh_link = sqh->qh.qh_link;
+
+ ehci_sync_hc(sc);
+}
+
+void
+ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd)
+{
+ /* Halt while we are messing. */
+ sqh->qh.qh_qtd.qtd_status |= htole32(EHCI_QTD_HALTED);
+ sqh->qh.qh_curqtd = 0;
+ sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr);
+ sqh->sqtd = sqtd;
+ /* Clear halt */
+ sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_HALTED);
+}
+
+/*
+ * Ensure that the HC has released all references to the QH. We do this
+ * by asking for a Async Advance Doorbell interrupt and then we wait for
+ * the interrupt.
+ * To make this easier we first obtain exclusive use of the doorbell.
+ */
+void
+ehci_sync_hc(ehci_softc_t *sc)
+{
+ int s, error;
+
+ if (sc->sc_dying) {
+ DPRINTFN(2,("ehci_sync_hc: dying\n"));
+ return;
+ }
+ DPRINTFN(2,("ehci_sync_hc: enter\n"));
+ /* get doorbell */
+ lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL, NULL);
+ s = splhardusb();
+ /* ask for doorbell */
+ EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD);
+ DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n",
+ EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS)));
+ error = tsleep(&sc->sc_async_head, PZERO, "ehcidi", hz); /* bell wait */
+ DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n",
+ EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS)));
+ splx(s);
+ /* release doorbell */
+ lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL, NULL);
+#ifdef DIAGNOSTIC
+ if (error)
+ printf("ehci_sync_hc: tsleep() = %d\n", error);
+#endif
+ DPRINTFN(2,("ehci_sync_hc: exit\n"));
+}
+
+/***********/
+
+/*
+ * Data structures and routines to emulate the root hub.
+ */
+Static usb_device_descriptor_t ehci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_HSHUBSTT, /* protocol */
+ 64, /* max packet */
+ {0},{0},{0x00,0x01}, /* device id */
+ 1,2,0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+Static usb_device_qualifier_t ehci_odevd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE_QUALIFIER, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ 1, /* # of configurations */
+ 0
+};
+
+Static usb_config_descriptor_t ehci_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 */
+};
+
+Static usb_interface_descriptor_t ehci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0,
+ 0,
+ 1,
+ UICLASS_HUB,
+ UISUBCLASS_HUB,
+ UIPROTO_HSHUBSTT,
+ 0
+};
+
+Static usb_endpoint_descriptor_t ehci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_IN | EHCI_INTR_ENDPT,
+ UE_INTERRUPT,
+ {8, 0}, /* max packet */
+ 255
+};
+
+Static usb_hub_descriptor_t ehci_hubd = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 0,
+ {0,0},
+ 0,
+ 0,
+ {0},
+};
+
+Static int
+ehci_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.
+ */
+Static usbd_status
+ehci_root_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+Static usbd_status
+ehci_root_ctrl_start(usbd_xfer_handle xfer)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ usb_device_request_t *req;
+ void *buf = NULL;
+ int port, i;
+ int s, len, value, index, l, totlen = 0;
+ usb_port_status_t ps;
+ usb_hub_descriptor_t hubd;
+ usbd_status err;
+ u_int32_t v;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ /* XXX panic */
+ return (USBD_INVAL);
+#endif
+ req = &xfer->request;
+
+ DPRINTFN(4,("ehci_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);
+
+ if (len != 0)
+ buf = KERNADDR(&xfer->dmabuf, 0);
+
+#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_HALT 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(8,("ehci_root_ctrl_control wValue=0x%04x\n", value));
+ switch(value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ USETW(ehci_devd.idVendor, sc->sc_id_vendor);
+ memcpy(buf, &ehci_devd, l);
+ break;
+ /*
+ * We can't really operate at another speed, but the spec says
+ * we need this descriptor.
+ */
+ case UDESC_DEVICE_QUALIFIER:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ memcpy(buf, &ehci_odevd, l);
+ break;
+ /*
+ * We can't really operate at another speed, but the spec says
+ * we need this descriptor.
+ */
+ case UDESC_OTHER_SPEED_CONFIGURATION:
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &ehci_confd, l);
+ ((usb_config_descriptor_t *)buf)->bDescriptorType =
+ value >> 8;
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &ehci_ifcd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &ehci_endpd, l);
+ break;
+ case UDESC_STRING:
+ if (len == 0)
+ break;
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ switch (value & 0xff) {
+ case 1: /* Vendor */
+ totlen = ehci_str(buf, len, sc->sc_vendor);
+ break;
+ case 2: /* Product */
+ totlen = ehci_str(buf, len, "EHCI root hub");
+ break;
+ }
+ break;
+ default:
+ err = 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) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ err = 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):
+ err = 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(8, ("ehci_root_ctrl_control: UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value));
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR;
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ EOWRITE4(sc, port, v &~ EHCI_PS_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ EOWRITE4(sc, port, v &~ EHCI_PS_SUSP);
+ break;
+ case UHF_PORT_POWER:
+ EOWRITE4(sc, port, v &~ EHCI_PS_PP);
+ break;
+ case UHF_PORT_TEST:
+ DPRINTFN(2,("ehci_root_ctrl_transfer: clear port test "
+ "%d\n", index));
+ break;
+ case UHF_PORT_INDICATOR:
+ DPRINTFN(2,("ehci_root_ctrl_transfer: clear port ind "
+ "%d\n", index));
+ EOWRITE4(sc, port, v &~ EHCI_PS_PIC);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ EOWRITE4(sc, port, v | EHCI_PS_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ EOWRITE4(sc, port, v | EHCI_PS_PEC);
+ break;
+ case UHF_C_PORT_SUSPEND:
+ /* how? */
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ EOWRITE4(sc, port, v | EHCI_PS_OCC);
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_isreset = 0;
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+#if 0
+ switch(value) {
+ case UHF_C_PORT_CONNECTION:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* Enable RHSC interrupt if condition is cleared. */
+ if ((OREAD4(sc, port) >> 16) == 0)
+ ehci_pcd_able(sc, 1);
+ break;
+ default:
+ break;
+ }
+#endif
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ hubd = ehci_hubd;
+ hubd.bNbrPorts = sc->sc_noport;
+ v = EOREAD4(sc, EHCI_HCSPARAMS);
+ USETW(hubd.wHubCharacteristics,
+ EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH |
+ EHCI_HCS_P_INCICATOR(EREAD4(sc, EHCI_HCSPARAMS))
+ ? UHD_PORT_IND : 0);
+ hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */
+ for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8)
+ hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */
+ hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i;
+ l = min(len, hubd.bDescLength);
+ totlen = l;
+ memcpy(buf, &hubd, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len); /* ? XXX */
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ DPRINTFN(8,("ehci_root_ctrl_transfer: get port status i=%d\n",
+ index));
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = EOREAD4(sc, EHCI_PORTSC(index));
+ DPRINTFN(8,("ehci_root_ctrl_transfer: port status=0x%04x\n",
+ v));
+ i = UPS_HIGH_SPEED;
+ if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS;
+ if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED;
+ if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND;
+ if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR;
+ if (v & EHCI_PS_PR) i |= UPS_RESET;
+ if (v & EHCI_PS_PP) i |= UPS_PORT_POWER;
+ USETW(ps.wPortStatus, i);
+ i = 0;
+ if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS;
+ if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED;
+ if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR;
+ if (sc->sc_isreset) i |= UPS_C_PORT_RESET;
+ USETW(ps.wPortChange, i);
+ l = min(len, sizeof ps);
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ err = 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 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR;
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ EOWRITE4(sc, port, v | EHCI_PS_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ EOWRITE4(sc, port, v | EHCI_PS_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ DPRINTFN(5,("ehci_root_ctrl_transfer: reset port %d\n",
+ index));
+ if (EHCI_PS_IS_LOWSPEED(v)) {
+ /* Low speed device, give up ownership. */
+ ehci_disown(sc, index, 1);
+ break;
+ }
+ /* Start reset sequence. */
+ v &= ~ (EHCI_PS_PE | EHCI_PS_PR);
+ EOWRITE4(sc, port, v | EHCI_PS_PR);
+ /* Wait for reset to complete. */
+ usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY);
+ if (sc->sc_dying) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ /* Terminate reset sequence. */
+ EOWRITE4(sc, port, v);
+ /* Wait for HC to complete reset. */
+ usb_delay_ms(&sc->sc_bus, EHCI_PORT_RESET_COMPLETE);
+ if (sc->sc_dying) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = EOREAD4(sc, port);
+ DPRINTF(("ehci after reset, status=0x%08x\n", v));
+ if (v & EHCI_PS_PR) {
+ printf("%s: port reset timeout\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ return (USBD_TIMEOUT);
+ }
+ if (!(v & EHCI_PS_PE)) {
+ /* Not a high speed device, give up ownership.*/
+ ehci_disown(sc, index, 0);
+ break;
+ }
+ sc->sc_isreset = 1;
+ DPRINTF(("ehci port %d reset, status = 0x%08x\n",
+ index, v));
+ break;
+ case UHF_PORT_POWER:
+ DPRINTFN(2,("ehci_root_ctrl_transfer: set port power "
+ "%d\n", index));
+ EOWRITE4(sc, port, v | EHCI_PS_PP);
+ break;
+ case UHF_PORT_TEST:
+ DPRINTFN(2,("ehci_root_ctrl_transfer: set port test "
+ "%d\n", index));
+ break;
+ case UHF_PORT_INDICATOR:
+ DPRINTFN(2,("ehci_root_ctrl_transfer: set port ind "
+ "%d\n", index));
+ EOWRITE4(sc, port, v | EHCI_PS_PIC);
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER):
+ case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER):
+ case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER):
+ case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER):
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->actlen = totlen;
+ err = USBD_NORMAL_COMPLETION;
+ ret:
+ xfer->status = err;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ return (USBD_IN_PROGRESS);
+}
+
+void
+ehci_disown(ehci_softc_t *sc, int index, int lowspeed)
+{
+ int port;
+ u_int32_t v;
+
+ DPRINTF(("ehci_disown: index=%d lowspeed=%d\n", index, lowspeed));
+#ifdef DIAGNOSTIC
+ if (sc->sc_npcomp != 0) {
+ int i = (index-1) / sc->sc_npcomp;
+ if (i >= sc->sc_ncomp)
+ printf("%s: strange port\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ else
+ printf("%s: handing over %s speed device on "
+ "port %d to %s\n",
+ USBDEVNAME(sc->sc_bus.bdev),
+ lowspeed ? "low" : "full",
+ index, USBDEVNAME(sc->sc_comps[i]->bdev));
+ } else {
+ printf("%s: npcomp == 0\n", USBDEVNAME(sc->sc_bus.bdev));
+ }
+#endif
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR;
+ EOWRITE4(sc, port, v | EHCI_PS_PO);
+}
+
+/* Abort a root control request. */
+Static void
+ehci_root_ctrl_abort(usbd_xfer_handle xfer)
+{
+ /* Nothing to do, all transfers are synchronous. */
+}
+
+/* Close the root pipe. */
+Static void
+ehci_root_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("ehci_root_ctrl_close\n"));
+ /* Nothing to do. */
+}
+
+void
+ehci_root_intr_done(usbd_xfer_handle xfer)
+{
+ xfer->hcpriv = NULL;
+}
+
+Static usbd_status
+ehci_root_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+Static usbd_status
+ehci_root_intr_start(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ sc->sc_intrxfer = xfer;
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root interrupt request. */
+Static void
+ehci_root_intr_abort(usbd_xfer_handle xfer)
+{
+ int s;
+
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("ehci_root_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ xfer->status = USBD_CANCELLED;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+}
+
+/* Close the root pipe. */
+Static void
+ehci_root_intr_close(usbd_pipe_handle pipe)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ehci_root_intr_close\n"));
+
+ sc->sc_intrxfer = NULL;
+}
+
+void
+ehci_root_ctrl_done(usbd_xfer_handle xfer)
+{
+ xfer->hcpriv = NULL;
+}
+
+/************************/
+
+ehci_soft_qh_t *
+ehci_alloc_sqh(ehci_softc_t *sc)
+{
+ ehci_soft_qh_t *sqh;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeqhs == NULL) {
+ DPRINTFN(2, ("ehci_alloc_sqh: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK,
+ EHCI_PAGE_SIZE, &dma);
+#ifdef EHCI_DEBUG
+ if (err)
+ printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err);
+#endif
+ if (err)
+ return (NULL);
+ for(i = 0; i < EHCI_SQH_CHUNK; i++) {
+ offs = i * EHCI_SQH_SIZE;
+ sqh = KERNADDR(&dma, offs);
+ sqh->physaddr = DMAADDR(&dma, offs);
+ sqh->next = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+ }
+ }
+ sqh = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh->next;
+ memset(&sqh->qh, 0, sizeof(ehci_qh_t));
+ sqh->next = NULL;
+ return (sqh);
+}
+
+void
+ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh)
+{
+ sqh->next = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+}
+
+ehci_soft_qtd_t *
+ehci_alloc_sqtd(ehci_softc_t *sc)
+{
+ ehci_soft_qtd_t *sqtd;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+ int s;
+
+ if (sc->sc_freeqtds == NULL) {
+ DPRINTFN(2, ("ehci_alloc_sqtd: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK,
+ EHCI_PAGE_SIZE, &dma);
+#ifdef EHCI_DEBUG
+ if (err)
+ printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err);
+#endif
+ if (err)
+ return (NULL);
+ s = splusb();
+ for(i = 0; i < EHCI_SQTD_CHUNK; i++) {
+ offs = i * EHCI_SQTD_SIZE;
+ sqtd = KERNADDR(&dma, offs);
+ sqtd->physaddr = DMAADDR(&dma, offs);
+ sqtd->nextqtd = sc->sc_freeqtds;
+ sc->sc_freeqtds = sqtd;
+ }
+ splx(s);
+ }
+
+ s = splusb();
+ sqtd = sc->sc_freeqtds;
+ sc->sc_freeqtds = sqtd->nextqtd;
+ memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t));
+ sqtd->nextqtd = NULL;
+ sqtd->xfer = NULL;
+ splx(s);
+
+ return (sqtd);
+}
+
+void
+ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
+{
+ int s;
+
+ s = splusb();
+ sqtd->nextqtd = sc->sc_freeqtds;
+ sc->sc_freeqtds = sqtd;
+ splx(s);
+}
+
+usbd_status
+ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
+ int alen, int rd, usbd_xfer_handle xfer,
+ ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep)
+{
+ ehci_soft_qtd_t *next, *cur;
+ ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys;
+ u_int32_t qtdstatus;
+ int len, curlen, mps, offset;
+ int i, tog;
+ usb_dma_t *dma = &xfer->dmabuf;
+
+ DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen));
+
+ offset = 0;
+ len = alen;
+ dataphys = DMAADDR(dma, 0);
+ dataphyslastpage = EHCI_PAGE(DMAADDR(dma, len - 1));
+#if 0
+printf("status=%08x toggle=%d\n", epipe->sqh->qh.qh_qtd.qtd_status,
+ epipe->nexttoggle);
+#endif
+ qtdstatus = htole32(
+ EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) |
+ EHCI_QTD_SET_CERR(3)
+ /* IOC set below */
+ /* BYTES set below */
+ );
+ mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
+ tog = epipe->nexttoggle;
+ qtdstatus |= EHCI_QTD_SET_TOGGLE(tog);
+
+ cur = ehci_alloc_sqtd(sc);
+ *sp = cur;
+ if (cur == NULL)
+ goto nomem;
+ for (;;) {
+ dataphyspage = EHCI_PAGE(dataphys);
+ /* The EHCI hardware can handle at most 5 pages. */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ if (dataphyslastpage - dataphyspage <
+ EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE) {
+ /* we can handle it in this QTD */
+ curlen = len;
+#elif defined(__FreeBSD__)
+ /* XXX This is pretty broken: Because we do not allocate
+ * a contiguous buffer (contiguous in physical pages) we
+ * can only transfer one page in one go.
+ * So check whether the start and end of the buffer are on
+ * the same page.
+ */
+ if (dataphyspage == dataphyslastpage) {
+ curlen = len;
+#endif
+ } else {
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* must use multiple TDs, fill as much as possible. */
+ curlen = EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE -
+ EHCI_PAGE_OFFSET(dataphys);
+#ifdef DIAGNOSTIC
+ if (curlen > len) {
+ printf("ehci_alloc_sqtd_chain: curlen=0x%x "
+ "len=0x%x offs=0x%x\n", curlen, len,
+ EHCI_PAGE_OFFSET(dataphys));
+ printf("lastpage=0x%x page=0x%x phys=0x%x\n",
+ dataphyslastpage, dataphyspage,
+ dataphys);
+ curlen = len;
+ }
+#endif
+#elif defined(__FreeBSD__)
+ /* See comment above (XXX) */
+ curlen = EHCI_PAGE_SIZE -
+ EHCI_PAGE_MASK(dataphys);
+#endif
+ /* the length must be a multiple of the max size */
+ curlen -= curlen % mps;
+ DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, "
+ "curlen=%d\n", curlen));
+#ifdef DIAGNOSTIC
+ if (curlen == 0)
+ panic("ehci_alloc_std: curlen == 0");
+#endif
+ }
+ DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x "
+ "dataphyslastpage=0x%08x len=%d curlen=%d\n",
+ dataphys, dataphyslastpage,
+ len, curlen));
+ len -= curlen;
+
+ if (len != 0) {
+ next = ehci_alloc_sqtd(sc);
+ if (next == NULL)
+ goto nomem;
+ nextphys = next->physaddr;
+ } else {
+ next = NULL;
+ nextphys = EHCI_NULL;
+ }
+
+ for (i = 0; i * EHCI_PAGE_SIZE < curlen; i++) {
+ ehci_physaddr_t a = dataphys + i * EHCI_PAGE_SIZE;
+ if (i != 0) /* use offset only in first buffer */
+ a = EHCI_PAGE(a);
+ cur->qtd.qtd_buffer[i] = htole32(a);
+ cur->qtd.qtd_buffer_hi[i] = 0;
+#ifdef DIAGNOSTIC
+ if (i >= EHCI_QTD_NBUFFERS) {
+ printf("ehci_alloc_sqtd_chain: i=%d\n", i);
+ goto nomem;
+ }
+#endif
+ }
+ cur->nextqtd = next;
+ cur->qtd.qtd_next = cur->qtd.qtd_altnext = htole32(nextphys);
+ cur->qtd.qtd_status =
+ qtdstatus | htole32(EHCI_QTD_SET_BYTES(curlen));
+ cur->xfer = xfer;
+ cur->len = curlen;
+ DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n",
+ dataphys, dataphys + curlen));
+ /* adjust the toggle based on the number of packets in this
+ qtd */
+ if (((curlen + mps - 1) / mps) & 1) {
+ tog ^= 1;
+ qtdstatus ^= EHCI_QTD_TOGGLE_MASK;
+ }
+ if (len == 0)
+ break;
+ DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n"));
+ offset += curlen;
+ dataphys = DMAADDR(dma, offset);
+ cur = next;
+ }
+ cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC);
+ *ep = cur;
+ epipe->nexttoggle = tog;
+
+ DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n",
+ *sp, *ep));
+
+ return (USBD_NORMAL_COMPLETION);
+
+ nomem:
+ /* XXX free chain */
+ DPRINTFN(-1,("ehci_alloc_sqtd_chain: no memory\n"));
+ return (USBD_NOMEM);
+}
+
+Static void
+ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd,
+ ehci_soft_qtd_t *sqtdend)
+{
+ ehci_soft_qtd_t *p;
+ int i;
+
+ DPRINTFN(10,("ehci_free_sqtd_chain: sqtd=%p sqtdend=%p\n",
+ sqtd, sqtdend));
+
+ for (i = 0; sqtd != sqtdend; sqtd = p, i++) {
+ p = sqtd->nextqtd;
+ ehci_free_sqtd(sc, sqtd);
+ }
+}
+
+/****************/
+
+/*
+ * Close a reqular pipe.
+ * Assumes that there are no pending transactions.
+ */
+void
+ehci_close_pipe(usbd_pipe_handle pipe, ehci_soft_qh_t *head)
+{
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+ ehci_soft_qh_t *sqh = epipe->sqh;
+ int s;
+
+ s = splusb();
+ ehci_rem_qh(sc, sqh, head);
+ splx(s);
+ ehci_free_sqh(sc, epipe->sqh);
+}
+
+/*
+ * Abort a device request.
+ * If this routine is called at splusb() it guarantees that the request
+ * will be removed from the hardware scheduling and that the callback
+ * for it will be called with USBD_CANCELLED status.
+ * It's impossible to guarantee that the requested transfer will not
+ * have happened since the hardware runs concurrently.
+ * If the transaction has already happened we rely on the ordinary
+ * interrupt processing to process it.
+ * XXX This is most probably wrong.
+ */
+void
+ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+#define exfer EXFER(xfer)
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus;
+ ehci_soft_qh_t *sqh = epipe->sqh;
+ ehci_soft_qtd_t *sqtd;
+ ehci_physaddr_t cur;
+ u_int32_t qhstatus;
+ int s;
+ int hit;
+
+ DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));
+
+ if (sc->sc_dying) {
+ /* If we're dying, just do the software part. */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer);
+ usb_transfer_complete(xfer);
+ splx(s);
+ return;
+ }
+
+ if (xfer->device->bus->intr_context || !curproc)
+ panic("ehci_abort_xfer: not in process context");
+
+ /*
+ * Step 1: Make interrupt routine and hardware ignore xfer.
+ */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer);
+ qhstatus = sqh->qh.qh_qtd.qtd_status;
+ sqh->qh.qh_qtd.qtd_status = qhstatus | htole32(EHCI_QTD_HALTED);
+ for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) {
+ sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED);
+ if (sqtd == exfer->sqtdend)
+ break;
+ }
+ splx(s);
+
+ /*
+ * Step 2: Wait until we know hardware has finished any possible
+ * use of the xfer. Also make sure the soft interrupt routine
+ * has run.
+ */
+ ehci_sync_hc(sc);
+ s = splusb();
+#ifdef USB_USE_SOFTINTR
+ sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+ usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+ tsleep(&sc->sc_softwake, PZERO, "ehciab", 0);
+#endif /* USB_USE_SOFTINTR */
+ splx(s);
+
+ /*
+ * Step 3: Remove any vestiges of the xfer from the hardware.
+ * The complication here is that the hardware may have executed
+ * beyond the xfer we're trying to abort. So as we're scanning
+ * the TDs of this xfer we check if the hardware points to
+ * any of them.
+ */
+ s = splusb(); /* XXX why? */
+ cur = EHCI_LINK_ADDR(le32toh(sqh->qh.qh_curqtd));
+ hit = 0;
+ for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) {
+ hit |= cur == sqtd->physaddr;
+ if (sqtd == exfer->sqtdend)
+ break;
+ }
+ sqtd = sqtd->nextqtd;
+ /* Zap curqtd register if hardware pointed inside the xfer. */
+ if (hit && sqtd != NULL) {
+ DPRINTFN(1,("ehci_abort_xfer: cur=0x%08x\n", sqtd->physaddr));
+ sqh->qh.qh_curqtd = htole32(sqtd->physaddr); /* unlink qTDs */
+ sqh->qh.qh_qtd.qtd_status = qhstatus;
+ } else {
+ DPRINTFN(1,("ehci_abort_xfer: no hit\n"));
+ }
+
+ /*
+ * Step 4: Execute callback.
+ */
+#ifdef DIAGNOSTIC
+ exfer->isdone = 1;
+#endif
+ usb_transfer_complete(xfer);
+
+ splx(s);
+#undef exfer
+}
+
+void
+ehci_timeout(void *addr)
+{
+ struct ehci_xfer *exfer = addr;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)exfer->xfer.pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus;
+
+ DPRINTF(("ehci_timeout: exfer=%p\n", exfer));
+#ifdef USB_DEBUG
+ if (ehcidebug > 1)
+ usbd_dump_pipe(exfer->xfer.pipe);
+#endif
+
+ if (sc->sc_dying) {
+ ehci_abort_xfer(&exfer->xfer, USBD_TIMEOUT);
+ return;
+ }
+
+ /* Execute the abort in a process context. */
+ usb_init_task(&exfer->abort_task, ehci_timeout_task, addr);
+ usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task);
+}
+
+void
+ehci_timeout_task(void *addr)
+{
+ usbd_xfer_handle xfer = addr;
+ int s;
+
+ DPRINTF(("ehci_timeout_task: xfer=%p\n", xfer));
+
+ s = splusb();
+ ehci_abort_xfer(xfer, USBD_TIMEOUT);
+ splx(s);
+}
+
+/************************/
+
+Static usbd_status
+ehci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+Static usbd_status
+ehci_device_ctrl_start(usbd_xfer_handle xfer)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ /* XXX panic */
+ printf("ehci_device_ctrl_transfer: not a request\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ err = ehci_device_request(xfer);
+ if (err)
+ return (err);
+
+ if (sc->sc_bus.use_polling)
+ ehci_waitintr(sc, xfer);
+ return (USBD_IN_PROGRESS);
+}
+
+void
+ehci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ struct ehci_xfer *ex = EXFER(xfer);
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ /*struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;*/
+
+ DPRINTFN(10,("ehci_ctrl_done: xfer=%p\n", xfer));
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ panic("ehci_ctrl_done: not a request");
+ }
+#endif
+
+ if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
+ ehci_del_intr_list(ex); /* remove from active list */
+ ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL);
+ }
+
+ DPRINTFN(5, ("ehci_ctrl_done: length=%d\n", xfer->actlen));
+}
+
+/* Abort a device control request. */
+Static void
+ehci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ehci_device_ctrl_abort: xfer=%p\n", xfer));
+ ehci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device control pipe. */
+Static void
+ehci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+ /*struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;*/
+
+ DPRINTF(("ehci_device_ctrl_close: pipe=%p\n", pipe));
+ ehci_close_pipe(pipe, sc->sc_async_head);
+}
+
+usbd_status
+ehci_device_request(usbd_xfer_handle xfer)
+{
+#define exfer EXFER(xfer)
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ usb_device_request_t *req = &xfer->request;
+ usbd_device_handle dev = epipe->pipe.device;
+ ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
+ int addr = dev->address;
+ ehci_soft_qtd_t *setup, *stat, *next;
+ ehci_soft_qh_t *sqh;
+ int isread;
+ int len;
+ usbd_status err;
+ int s;
+
+ isread = req->bmRequestType & UT_READ;
+ len = UGETW(req->wLength);
+
+ DPRINTFN(3,("ehci_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), len, addr,
+ epipe->pipe.endpoint->edesc->bEndpointAddress));
+
+ setup = ehci_alloc_sqtd(sc);
+ if (setup == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ stat = ehci_alloc_sqtd(sc);
+ if (stat == NULL) {
+ err = USBD_NOMEM;
+ goto bad2;
+ }
+
+ sqh = epipe->sqh;
+ epipe->u.ctl.length = len;
+
+ /* Update device address and length since they may have changed
+ during the setup of the control pipe in usbd_new_device(). */
+ /* XXX This only needs to be done once, but it's too early in open. */
+ /* XXXX Should not touch ED here! */
+ sqh->qh.qh_endp =
+ (sqh->qh.qh_endp & htole32(~(EHCI_QH_ADDRMASK | EHCI_QH_MPLMASK))) |
+ htole32(
+ EHCI_QH_SET_ADDR(addr) |
+ EHCI_QH_SET_MPL(UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize))
+ );
+
+ /* Set up data transaction */
+ if (len != 0) {
+ ehci_soft_qtd_t *end;
+
+ /* Start toggle at 1. */
+ epipe->nexttoggle = 1;
+ err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
+ &next, &end);
+ if (err)
+ goto bad3;
+ end->nextqtd = stat;
+ end->qtd.qtd_next =
+ end->qtd.qtd_altnext = htole32(stat->physaddr);
+ } else {
+ next = stat;
+ }
+
+ memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req);
+
+ /* Clear toggle */
+ setup->qtd.qtd_status = htole32(
+ EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
+ EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(0) |
+ EHCI_QTD_SET_BYTES(sizeof *req)
+ );
+ setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0));
+ setup->qtd.qtd_buffer_hi[0] = 0;
+ setup->nextqtd = next;
+ setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr);
+ setup->xfer = xfer;
+ setup->len = sizeof *req;
+
+ stat->qtd.qtd_status = htole32(
+ EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) |
+ EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(1) |
+ EHCI_QTD_IOC
+ );
+ stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */
+ stat->qtd.qtd_buffer_hi[0] = 0; /* XXX not needed? */
+ stat->nextqtd = NULL;
+ stat->qtd.qtd_next = stat->qtd.qtd_altnext = EHCI_NULL;
+ stat->xfer = xfer;
+ stat->len = 0;
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 5) {
+ DPRINTF(("ehci_device_request:\n"));
+ ehci_dump_sqh(sqh);
+ ehci_dump_sqtds(setup);
+ }
+#endif
+
+ exfer->sqtdstart = setup;
+ exfer->sqtdend = stat;
+#ifdef DIAGNOSTIC
+ if (!exfer->isdone) {
+ printf("ehci_device_request: not done, exfer=%p\n", exfer);
+ }
+ exfer->isdone = 0;
+#endif
+
+ /* Insert qTD in QH list. */
+ s = splusb();
+ ehci_set_qh_qtd(sqh, setup);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ehci_timeout, xfer);
+ }
+ ehci_add_intr_list(sc, exfer);
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 10) {
+ DPRINTF(("ehci_device_request: status=%x\n",
+ EOREAD4(sc, EHCI_USBSTS)));
+ delay(10000);
+ ehci_dump_regs(sc);
+ ehci_dump_sqh(sc->sc_async_head);
+ ehci_dump_sqh(sqh);
+ ehci_dump_sqtds(setup);
+ }
+#endif
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad3:
+ ehci_free_sqtd(sc, stat);
+ bad2:
+ ehci_free_sqtd(sc, setup);
+ bad1:
+ DPRINTFN(-1,("ehci_device_request: no memory\n"));
+ xfer->status = err;
+ usb_transfer_complete(xfer);
+ return (err);
+#undef exfer
+}
+
+/************************/
+
+Static usbd_status
+ehci_device_bulk_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+ehci_device_bulk_start(usbd_xfer_handle xfer)
+{
+#define exfer EXFER(xfer)
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ usbd_device_handle dev = epipe->pipe.device;
+ ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
+ ehci_soft_qtd_t *data, *dataend;
+ ehci_soft_qh_t *sqh;
+ usbd_status err;
+ int len, isread, endpt;
+ int s;
+
+ DPRINTFN(2, ("ehci_device_bulk_transfer: xfer=%p len=%d flags=%d\n",
+ xfer, xfer->length, xfer->flags));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("ehci_device_bulk_transfer: a request");
+#endif
+
+ len = xfer->length;
+ endpt = epipe->pipe.endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sqh = epipe->sqh;
+
+ epipe->u.bulk.length = len;
+
+ err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
+ &dataend);
+ if (err) {
+ DPRINTFN(-1,("ehci_device_bulk_transfer: no memory\n"));
+ xfer->status = err;
+ usb_transfer_complete(xfer);
+ return (err);
+ }
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 5) {
+ DPRINTF(("ehci_device_bulk_transfer: data(1)\n"));
+ ehci_dump_sqh(sqh);
+ ehci_dump_sqtds(data);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ exfer->sqtdstart = data;
+ exfer->sqtdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!exfer->isdone) {
+ printf("ehci_device_bulk_transfer: not done, ex=%p\n", exfer);
+ }
+ exfer->isdone = 0;
+#endif
+
+ s = splusb();
+ ehci_set_qh_qtd(sqh, data);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ehci_timeout, xfer);
+ }
+ ehci_add_intr_list(sc, exfer);
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 10) {
+ DPRINTF(("ehci_device_bulk_transfer: data(2)\n"));
+ delay(10000);
+ DPRINTF(("ehci_device_bulk_transfer: data(3)\n"));
+ ehci_dump_regs(sc);
+#if 0
+ printf("async_head:\n");
+ ehci_dump_sqh(sc->sc_async_head);
+#endif
+ printf("sqh:\n");
+ ehci_dump_sqh(sqh);
+ ehci_dump_sqtds(data);
+ }
+#endif
+
+ if (sc->sc_bus.use_polling)
+ ehci_waitintr(sc, xfer);
+
+ return (USBD_IN_PROGRESS);
+#undef exfer
+}
+
+Static void
+ehci_device_bulk_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ehci_device_bulk_abort: xfer=%p\n", xfer));
+ ehci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/*
+ * Close a device bulk pipe.
+ */
+Static void
+ehci_device_bulk_close(usbd_pipe_handle pipe)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ehci_device_bulk_close: pipe=%p\n", pipe));
+ ehci_close_pipe(pipe, sc->sc_async_head);
+}
+
+void
+ehci_device_bulk_done(usbd_xfer_handle xfer)
+{
+ struct ehci_xfer *ex = EXFER(xfer);
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ /*struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;*/
+
+ DPRINTFN(10,("ehci_bulk_done: xfer=%p, actlen=%d\n",
+ xfer, xfer->actlen));
+
+ if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
+ ehci_del_intr_list(ex); /* remove from active list */
+ ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL);
+ }
+
+ DPRINTFN(5, ("ehci_bulk_done: length=%d\n", xfer->actlen));
+}
+
+/************************/
+
+Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; }
+Static usbd_status ehci_device_intr_start(usbd_xfer_handle xfer) { return USBD_IOERROR; }
+Static void ehci_device_intr_abort(usbd_xfer_handle xfer) { }
+Static void ehci_device_intr_close(usbd_pipe_handle pipe) { }
+Static void ehci_device_intr_done(usbd_xfer_handle xfer) { }
+
+/************************/
+
+Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; }
+Static usbd_status ehci_device_isoc_start(usbd_xfer_handle xfer) { return USBD_IOERROR; }
+Static void ehci_device_isoc_abort(usbd_xfer_handle xfer) { }
+Static void ehci_device_isoc_close(usbd_pipe_handle pipe) { }
+Static void ehci_device_isoc_done(usbd_xfer_handle xfer) { }
diff --git a/sys/dev/usb/ehci_pci.c b/sys/dev/usb/ehci_pci.c
new file mode 100644
index 0000000..c2da667
--- /dev/null
+++ b/sys/dev/usb/ehci_pci.c
@@ -0,0 +1,347 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) at
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
+ *
+ * The EHCI 1.0 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/docs/usb_20.zip
+ */
+
+/* The low level controller code for EHCI has been split into
+ * PCI probes and EHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+#include <sys/lockmgr.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.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/ehcireg.h>
+#include <dev/usb/ehcivar.h>
+
+#define PCI_EHCI_VENDORID_ACERLABS 0x10b9
+#define PCI_EHCI_VENDORID_AMD 0x1022
+#define PCI_EHCI_VENDORID_APPLE 0x106b
+#define PCI_EHCI_VENDORID_CMDTECH 0x1095
+#define PCI_EHCI_VENDORID_INTEL 0x8086
+#define PCI_EHCI_VENDORID_NEC 0x1033
+#define PCI_EHCI_VENDORID_OPTI 0x1045
+#define PCI_EHCI_VENDORID_SIS 0x1039
+#define PCI_EHCI_VENDORID_NVIDIA 0x12D2
+#define PCI_EHCI_VENDORID_NVIDIA2 0x10DE
+
+#define PCI_EHCI_DEVICEID_NEC 0x00e01033
+static const char *ehci_device_nec = "NEC uPD 720100 USB 2.0 controller";
+
+static const char *ehci_device_generic = "EHCI (generic) USB 2.0 controller";
+
+#define PCI_EHCI_BASE_REG 0x10
+
+
+static int ehci_pci_attach(device_t self);
+static int ehci_pci_detach(device_t self);
+
+static const char *
+ehci_pci_match(device_t self)
+{
+ u_int32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case PCI_EHCI_DEVICEID_NEC:
+ return (ehci_device_nec);
+ default:
+ if (pci_get_class(self) == PCIC_SERIALBUS
+ && pci_get_subclass(self) == PCIS_SERIALBUS_USB
+ && pci_get_progif(self) == PCI_INTERFACE_EHCI) {
+ return (ehci_device_generic);
+ }
+ }
+
+ return NULL; /* dunno */
+}
+
+static int
+ehci_pci_probe(device_t self)
+{
+ const char *desc = ehci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return 0;
+ } else {
+ return ENXIO;
+ }
+}
+
+static int
+ehci_pci_attach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ device_t parent;
+ device_t *neighbors;
+ device_t *nbus;
+ struct usbd_bus *bsc;
+ int err;
+ int rid;
+ int ncomp;
+ int count, buscount;
+ int slot, function;
+ int res;
+ int i;
+
+ switch(pci_read_config(self, PCI_USBREV, 1) & PCI_USBREV_MASK) {
+ case PCI_USBREV_PRE_1_0:
+ case PCI_USBREV_1_0:
+ case PCI_USBREV_1_1:
+ sc->sc_bus.usbrev = USBREV_UNKNOWN;
+ printf("pre-2.0 USB rev\n");
+ return ENXIO;
+ case PCI_USBREV_2_0:
+ sc->sc_bus.usbrev = USBREV_2_0;
+ break;
+ default:
+ sc->sc_bus.usbrev = USBREV_UNKNOWN;
+ break;
+ }
+
+ pci_enable_busmaster(self);
+
+ rid = PCI_CBMEM;
+ sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->io_res) {
+ device_printf(self, "Could not map memory\n");
+ return ENXIO;
+ }
+ sc->iot = rman_get_bustag(sc->io_res);
+ sc->ioh = rman_get_bushandle(sc->io_res);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ ehci_pci_detach(self);
+ return ENXIO;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ ehci_pci_detach(self);
+ return ENOMEM;
+ }
+ device_set_ivars(sc->sc_bus.bdev, sc);
+
+ /* ehci_pci_match will never return NULL if ehci_pci_probe succeeded */
+ device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_ACERLABS:
+ sprintf(sc->sc_vendor, "AcerLabs");
+ break;
+ case PCI_EHCI_VENDORID_AMD:
+ sprintf(sc->sc_vendor, "AMD");
+ break;
+ case PCI_EHCI_VENDORID_APPLE:
+ sprintf(sc->sc_vendor, "Apple");
+ break;
+ case PCI_EHCI_VENDORID_CMDTECH:
+ sprintf(sc->sc_vendor, "CMDTECH");
+ break;
+ case PCI_EHCI_VENDORID_INTEL:
+ sprintf(sc->sc_vendor, "Intel");
+ break;
+ case PCI_EHCI_VENDORID_NEC:
+ sprintf(sc->sc_vendor, "NEC");
+ break;
+ case PCI_EHCI_VENDORID_OPTI:
+ sprintf(sc->sc_vendor, "OPTi");
+ break;
+ case PCI_EHCI_VENDORID_SIS:
+ sprintf(sc->sc_vendor, "SiS");
+ break;
+ case PCI_EHCI_VENDORID_NVIDIA:
+ case PCI_EHCI_VENDORID_NVIDIA2:
+ sprintf(sc->sc_vendor, "nVidia");
+ break;
+ default:
+ if (bootverbose)
+ device_printf(self, "(New EHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO,
+ (driver_intr_t *) ehci_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->ih = NULL;
+ ehci_pci_detach(self);
+ return ENXIO;
+ }
+
+ /*
+ * Find companion controllers. According to the spec they always
+ * have lower function numbers so they should be enumerated already.
+ */
+ parent = device_get_parent(self);
+ res = device_get_children(parent, &neighbors, &count);
+ if (res != 0) {
+ device_printf(self, "Error finding companion busses\n");
+ ehci_pci_detach(self);
+ return ENXIO;
+ }
+ ncomp = 0;
+ slot = pci_get_slot(self);
+ function = pci_get_function(self);
+ for (i = 0; i < count; i++) {
+ if (pci_get_slot(neighbors[i]) == slot && \
+ pci_get_function(neighbors[i]) < function) {
+ res = device_get_children(neighbors[i],
+ &nbus, &buscount);
+ if (res != 0 || buscount != 1)
+ continue;
+ bsc = device_get_softc(nbus[0]);
+ printf("ehci_pci_attach: companion %s\n",
+ USBDEVNAME(bsc->bdev));
+ sc->sc_comps[ncomp++] = bsc;
+ if (ncomp >= EHCI_COMPANION_MAX)
+ break;
+ }
+ }
+ sc->sc_ncomp = ncomp;
+
+ err = ehci_init(sc);
+ if (!err)
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+#if 0 /* TODO */
+ ehci_pci_detach(self);
+#endif
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ehci_pci_detach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+
+ /*
+ * XXX this code is not yet fit to be used as detach for the EHCI
+ * controller
+ */
+
+ /*
+ * disable interrupts that might have been switched on in ehci_init
+ */
+ if (sc->iot && sc->ioh)
+ bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0);
+
+ if (sc->irq_res && sc->ih) {
+ int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->ih = NULL;
+ }
+ if (sc->sc_bus.bdev) {
+ device_delete_child(self, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+ if (sc->irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res);
+ sc->irq_res = NULL;
+ }
+ if (sc->io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->io_res);
+ sc->io_res = NULL;
+ sc->iot = 0;
+ sc->ioh = 0;
+ }
+ return 0;
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_pci_probe),
+ DEVMETHOD(device_attach, ehci_pci_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(ehci_softc_t),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, pci, ehci_driver, ehci_devclass, 0, 0);
+DRIVER_MODULE(ehci, cardbus, ehci_driver, ehci_devclass, 0, 0);
diff --git a/sys/dev/usb/ehcireg.h b/sys/dev/usb/ehcireg.h
new file mode 100644
index 0000000..ec5d893
--- /dev/null
+++ b/sys/dev/usb/ehcireg.h
@@ -0,0 +1,293 @@
+/* $NetBSD: ehcireg.h,v 1.17 2004/06/23 06:45:56 mycroft Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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.
+ */
+
+/*
+ * The EHCI 0.96 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r096.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/data/usb_20.zip
+ */
+
+#ifndef _DEV_PCI_EHCIREG_H_
+#define _DEV_PCI_EHCIREG_H_
+
+/*** PCI config registers ***/
+
+#define PCI_CBMEM 0x10 /* configuration base MEM */
+
+#define PCI_INTERFACE_EHCI 0x20
+
+#define PCI_USBREV 0x60 /* RO USB protocol revision */
+#define PCI_USBREV_MASK 0xff
+#define PCI_USBREV_PRE_1_0 0x00
+#define PCI_USBREV_1_0 0x10
+#define PCI_USBREV_1_1 0x11
+#define PCI_USBREV_2_0 0x20
+
+#define PCI_EHCI_FLADJ 0x61 /*RW Frame len adj, SOF=59488+6*fladj */
+
+#define PCI_EHCI_PORTWAKECAP 0x62 /* RW Port wake caps (opt) */
+
+/* Regs ar EECP + offset */
+#define PCI_EHCI_USBLEGSUP 0x00
+#define PCI_EHCI_USBLEGCTLSTS 0x04
+
+/*** EHCI capability registers ***/
+
+#define EHCI_CAPLENGTH 0x00 /*RO Capability register length field */
+/* reserved 0x01 */
+#define EHCI_HCIVERSION 0x02 /* RO Interface version number */
+
+#define EHCI_HCSPARAMS 0x04 /* RO Structural parameters */
+#define EHCI_HCS_DEBUGPORT(x) (((x) >> 20) & 0xf)
+#define EHCI_HCS_P_INCICATOR(x) ((x) & 0x10000)
+#define EHCI_HCS_N_CC(x) (((x) >> 12) & 0xf) /* # of companion ctlrs */
+#define EHCI_HCS_N_PCC(x) (((x) >> 8) & 0xf) /* # of ports per comp. */
+#define EHCI_HCS_PPC(x) ((x) & 0x10) /* port power control */
+#define EHCI_HCS_N_PORTS(x) ((x) & 0xf) /* # of ports */
+
+#define EHCI_HCCPARAMS 0x08 /* RO Capability parameters */
+#define EHCI_HCC_EECP(x) (((x) >> 8) & 0xff) /* extended ports caps */
+#define EHCI_HCC_IST(x) (((x) >> 4) & 0xf) /* isoc sched threshold */
+#define EHCI_HCC_ASPC(x) ((x) & 0x4) /* async sched park cap */
+#define EHCI_HCC_PFLF(x) ((x) & 0x2) /* prog frame list flag */
+#define EHCI_HCC_64BIT(x) ((x) & 0x1) /* 64 bit address cap */
+
+#define EHCI_HCSP_PORTROUTE 0x0c /*RO Companion port route description */
+
+/* EHCI operational registers. Offset given by EHCI_CAPLENGTH register */
+#define EHCI_USBCMD 0x00 /* RO, RW, WO Command register */
+#define EHCI_CMD_ITC_M 0x00ff0000 /* RW interrupt threshold ctrl */
+#define EHCI_CMD_ITC_1 0x00010000
+#define EHCI_CMD_ITC_2 0x00020000
+#define EHCI_CMD_ITC_4 0x00040000
+#define EHCI_CMD_ITC_8 0x00080000
+#define EHCI_CMD_ITC_16 0x00100000
+#define EHCI_CMD_ITC_32 0x00200000
+#define EHCI_CMD_ITC_64 0x00400000
+#define EHCI_CMD_ASPME 0x00000800 /* RW/RO async park enable */
+#define EHCI_CMD_ASPMC 0x00000300 /* RW/RO async park count */
+#define EHCI_CMD_LHCR 0x00000080 /* RW light host ctrl reset */
+#define EHCI_CMD_IAAD 0x00000040 /* RW intr on async adv door bell */
+#define EHCI_CMD_ASE 0x00000020 /* RW async sched enable */
+#define EHCI_CMD_PSE 0x00000010 /* RW periodic sched enable */
+#define EHCI_CMD_FLS_M 0x0000000c /* RW/RO frame list size */
+#define EHCI_CMD_FLS(x) (((x) >> 2) & 3) /* RW/RO frame list size */
+#define EHCI_CMD_HCRESET 0x00000002 /* RW reset */
+#define EHCI_CMD_RS 0x00000001 /* RW run/stop */
+
+#define EHCI_USBSTS 0x04 /* RO, RW, RWC Status register */
+#define EHCI_STS_ASS 0x00008000 /* RO async sched status */
+#define EHCI_STS_PSS 0x00004000 /* RO periodic sched status */
+#define EHCI_STS_REC 0x00002000 /* RO reclamation */
+#define EHCI_STS_HCH 0x00001000 /* RO host controller halted */
+#define EHCI_STS_IAA 0x00000020 /* RWC interrupt on async adv */
+#define EHCI_STS_HSE 0x00000010 /* RWC host system error */
+#define EHCI_STS_FLR 0x00000008 /* RWC frame list rollover */
+#define EHCI_STS_PCD 0x00000004 /* RWC port change detect */
+#define EHCI_STS_ERRINT 0x00000002 /* RWC error interrupt */
+#define EHCI_STS_INT 0x00000001 /* RWC interrupt */
+#define EHCI_STS_INTRS(x) ((x) & 0x3f)
+
+#define EHCI_NORMAL_INTRS (EHCI_STS_IAA | EHCI_STS_HSE | EHCI_STS_PCD | EHCI_STS_ERRINT | EHCI_STS_INT)
+
+#define EHCI_USBINTR 0x08 /* RW Interrupt register */
+#define EHCI_INTR_IAAE 0x00000020 /* interrupt on async advance ena */
+#define EHCI_INTR_HSEE 0x00000010 /* host system error ena */
+#define EHCI_INTR_FLRE 0x00000008 /* frame list rollover ena */
+#define EHCI_INTR_PCIE 0x00000004 /* port change ena */
+#define EHCI_INTR_UEIE 0x00000002 /* USB error intr ena */
+#define EHCI_INTR_UIE 0x00000001 /* USB intr ena */
+
+#define EHCI_FRINDEX 0x0c /* RW Frame Index register */
+
+#define EHCI_CTRLDSSEGMENT 0x10 /* RW Control Data Structure Segment */
+
+#define EHCI_PERIODICLISTBASE 0x14 /* RW Periodic List Base */
+#define EHCI_ASYNCLISTADDR 0x18 /* RW Async List Base */
+
+#define EHCI_CONFIGFLAG 0x40 /* RW Configure Flag register */
+#define EHCI_CONF_CF 0x00000001 /* RW configure flag */
+
+#define EHCI_PORTSC(n) (0x40+4*(n)) /* RO, RW, RWC Port Status reg */
+#define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current ena */
+#define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect ena */
+#define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect ena */
+#define EHCI_PS_PTC 0x000f0000 /* RW port test control */
+#define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */
+#define EHCI_PS_PO 0x00002000 /* RW port owner */
+#define EHCI_PS_PP 0x00001000 /* RW,RO port power */
+#define EHCI_PS_LS 0x00000c00 /* RO line status */
+#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400)
+#define EHCI_PS_PR 0x00000100 /* RW port reset */
+#define EHCI_PS_SUSP 0x00000080 /* RW suspend */
+#define EHCI_PS_FPR 0x00000040 /* RW force port resume */
+#define EHCI_PS_OCC 0x00000020 /* RWC over current change */
+#define EHCI_PS_OCA 0x00000010 /* RO over current active */
+#define EHCI_PS_PEC 0x00000008 /* RWC port enable change */
+#define EHCI_PS_PE 0x00000004 /* RW port enable */
+#define EHCI_PS_CSC 0x00000002 /* RWC connect status change */
+#define EHCI_PS_CS 0x00000001 /* RO connect status */
+#define EHCI_PS_CLEAR (EHCI_PS_OCC|EHCI_PS_PEC|EHCI_PS_CSC)
+
+#define EHCI_PORT_RESET_COMPLETE 2 /* ms */
+
+#define EHCI_FLALIGN_ALIGN 0x1000
+
+/* No data structure may cross a page boundary. */
+#define EHCI_PAGE_SIZE 0x1000
+#define EHCI_PAGE(x) ((x) &~ 0xfff)
+#define EHCI_PAGE_OFFSET(x) ((x) & 0xfff)
+#if defined(__FreeBSD__)
+#define EHCI_PAGE_MASK(x) ((x) & 0xfff)
+#endif
+
+typedef u_int32_t ehci_link_t;
+#define EHCI_LINK_TERMINATE 0x00000001
+#define EHCI_LINK_TYPE(x) ((x) & 0x00000006)
+#define EHCI_LINK_ITD 0x0
+#define EHCI_LINK_QH 0x2
+#define EHCI_LINK_SITD 0x4
+#define EHCI_LINK_FSTN 0x6
+#define EHCI_LINK_ADDR(x) ((x) &~ 0x1f)
+
+typedef u_int32_t ehci_physaddr_t;
+
+/* Isochronous Transfer Descriptor */
+typedef struct {
+ ehci_link_t itd_next;
+ /* XXX many more */
+} ehci_itd_t;
+#define EHCI_ITD_ALIGN 32
+
+/* Split Transaction Isochronous Transfer Descriptor */
+typedef struct {
+ ehci_link_t sitd_next;
+ /* XXX many more */
+} ehci_sitd_t;
+#define EHCI_SITD_ALIGN 32
+
+/* Queue Element Transfer Descriptor */
+#define EHCI_QTD_NBUFFERS 5
+typedef struct {
+ ehci_link_t qtd_next;
+ ehci_link_t qtd_altnext;
+ u_int32_t qtd_status;
+#define EHCI_QTD_GET_STATUS(x) (((x) >> 0) & 0xff)
+#define EHCI_QTD_ACTIVE 0x80
+#define EHCI_QTD_HALTED 0x40
+#define EHCI_QTD_BUFERR 0x20
+#define EHCI_QTD_BABBLE 0x10
+#define EHCI_QTD_XACTERR 0x08
+#define EHCI_QTD_MISSEDMICRO 0x04
+#define EHCI_QTD_SPLITXSTATE 0x02
+#define EHCI_QTD_PINGSTATE 0x01
+#define EHCI_QTD_STATERRS 0x7c
+#define EHCI_QTD_GET_PID(x) (((x) >> 8) & 0x3)
+#define EHCI_QTD_SET_PID(x) ((x) << 8)
+#define EHCI_QTD_PID_OUT 0x0
+#define EHCI_QTD_PID_IN 0x1
+#define EHCI_QTD_PID_SETUP 0x2
+#define EHCI_QTD_GET_CERR(x) (((x) >> 10) & 0x3)
+#define EHCI_QTD_SET_CERR(x) ((x) << 10)
+#define EHCI_QTD_GET_C_PAGE(x) (((x) >> 12) & 0x7)
+#define EHCI_QTD_SET_C_PAGE(x) ((x) << 12)
+#define EHCI_QTD_GET_IOC(x) (((x) >> 15) & 0x1)
+#define EHCI_QTD_IOC 0x00008000
+#define EHCI_QTD_GET_BYTES(x) (((x) >> 16) & 0x7fff)
+#define EHCI_QTD_SET_BYTES(x) ((x) << 16)
+#define EHCI_QTD_GET_TOGGLE(x) (((x) >> 31) & 0x1)
+#define EHCI_QTD_SET_TOGGLE(x) ((x) << 31)
+#define EHCI_QTD_TOGGLE_MASK 0x80000000
+ ehci_physaddr_t qtd_buffer[EHCI_QTD_NBUFFERS];
+ ehci_physaddr_t qtd_buffer_hi[EHCI_QTD_NBUFFERS];
+} ehci_qtd_t;
+#define EHCI_QTD_ALIGN 32
+
+/* Queue Head */
+typedef struct {
+ ehci_link_t qh_link;
+ u_int32_t qh_endp;
+#define EHCI_QH_GET_ADDR(x) (((x) >> 0) & 0x7f) /* endpoint addr */
+#define EHCI_QH_SET_ADDR(x) (x)
+#define EHCI_QH_ADDRMASK 0x0000007f
+#define EHCI_QH_GET_INACT(x) (((x) >> 7) & 0x01) /* inactivate on next */
+#define EHCI_QH_INACT 0x00000080
+#define EHCI_QH_GET_ENDPT(x) (((x) >> 8) & 0x0f) /* endpoint no */
+#define EHCI_QH_SET_ENDPT(x) ((x) << 8)
+#define EHCI_QH_GET_EPS(x) (((x) >> 12) & 0x03) /* endpoint speed */
+#define EHCI_QH_SET_EPS(x) ((x) << 12)
+#define EHCI_QH_SPEED_FULL 0x0
+#define EHCI_QH_SPEED_LOW 0x1
+#define EHCI_QH_SPEED_HIGH 0x2
+#define EHCI_QH_GET_DTC(x) (((x) >> 14) & 0x01) /* data toggle control */
+#define EHCI_QH_DTC 0x00004000
+#define EHCI_QH_GET_HRECL(x) (((x) >> 15) & 0x01) /* head of reclamation */
+#define EHCI_QH_HRECL 0x00008000
+#define EHCI_QH_GET_MPL(x) (((x) >> 16) & 0x7ff) /* max packet len */
+#define EHCI_QH_SET_MPL(x) ((x) << 16)
+#define EHCI_QH_MPLMASK 0x07ff0000
+#define EHCI_QH_GET_CTL(x) (((x) >> 27) & 0x01) /* control endpoint */
+#define EHCI_QH_CTL 0x08000000
+#define EHCI_QH_GET_NRL(x) (((x) >> 28) & 0x0f) /* NAK reload */
+#define EHCI_QH_SET_NRL(x) ((x) << 28)
+ u_int32_t qh_endphub;
+#define EHCI_QH_GET_SMASK(x) (((x) >> 0) & 0xff) /* intr sched mask */
+#define EHCI_QH_SET_SMASK(x) ((x) << 0)
+#define EHCI_QH_GET_CMASK(x) (((x) >> 8) & 0xff) /* split completion mask */
+#define EHCI_QH_SET_CMASK(x) ((x) << 8)
+#define EHCI_QH_GET_HUBA(x) (((x) >> 16) & 0x7f) /* hub address */
+#define EHCI_QH_SET_HUBA(x) ((x) << 16)
+#define EHCI_QH_GET_PORT(x) (((x) >> 23) & 0x7f) /* hub port */
+#define EHCI_QH_SET_PORT(x) ((x) << 23)
+#define EHCI_QH_GET_MULT(x) (((x) >> 30) & 0x03) /* pipe multiplier */
+#define EHCI_QH_SET_MULT(x) ((x) << 30)
+ ehci_link_t qh_curqtd;
+ ehci_qtd_t qh_qtd;
+} ehci_qh_t;
+#define EHCI_QH_ALIGN 32
+
+/* Periodic Frame Span Traversal Node */
+typedef struct {
+ ehci_link_t fstn_link;
+ ehci_link_t fstn_back;
+} ehci_fstn_t;
+#define EHCI_FSTN_ALIGN 32
+
+#endif /* _DEV_PCI_EHCIREG_H_ */
diff --git a/sys/dev/usb/ehcivar.h b/sys/dev/usb/ehcivar.h
new file mode 100644
index 0000000..c743d93
--- /dev/null
+++ b/sys/dev/usb/ehcivar.h
@@ -0,0 +1,153 @@
+/* $NetBSD: ehcivar.h,v 1.12 2001/12/31 12:16:57 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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 ehci_soft_qtd {
+ ehci_qtd_t qtd;
+ struct ehci_soft_qtd *nextqtd; /* mirrors nextqtd in TD */
+ ehci_physaddr_t physaddr;
+ usbd_xfer_handle xfer;
+ LIST_ENTRY(ehci_soft_qtd) hnext;
+ u_int16_t len;
+} ehci_soft_qtd_t;
+#define EHCI_SQTD_SIZE ((sizeof (struct ehci_soft_qtd) + EHCI_QTD_ALIGN - 1) / EHCI_QTD_ALIGN * EHCI_QTD_ALIGN)
+#define EHCI_SQTD_CHUNK (EHCI_PAGE_SIZE / EHCI_SQTD_SIZE)
+
+typedef struct ehci_soft_qh {
+ ehci_qh_t qh;
+ struct ehci_soft_qh *next;
+ struct ehci_soft_qtd *sqtd;
+ ehci_physaddr_t physaddr;
+} ehci_soft_qh_t;
+#define EHCI_SQH_SIZE ((sizeof (struct ehci_soft_qh) + EHCI_QH_ALIGN - 1) / EHCI_QH_ALIGN * EHCI_QH_ALIGN)
+#define EHCI_SQH_CHUNK (EHCI_PAGE_SIZE / EHCI_SQH_SIZE)
+
+struct ehci_xfer {
+ struct usbd_xfer xfer;
+ struct usb_task abort_task;
+ LIST_ENTRY(ehci_xfer) inext; /* list of active xfers */
+ ehci_soft_qtd_t *sqtdstart;
+ ehci_soft_qtd_t *sqtdend;
+#ifdef DIAGNOSTIC
+ int isdone;
+#endif
+};
+#define EXFER(xfer) ((struct ehci_xfer *)(xfer))
+
+
+#define EHCI_HASH_SIZE 128
+#define EHCI_COMPANION_MAX 8
+
+typedef struct ehci_softc {
+ struct usbd_bus sc_bus; /* base device */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t sc_size;
+#if defined(__FreeBSD__)
+ void *ih;
+
+ struct resource *io_res;
+ struct resource *irq_res;
+#endif
+ u_int sc_offs; /* offset to operational regs */
+
+ char sc_vendor[16]; /* vendor string for root hub */
+ int sc_id_vendor; /* vendor ID for root hub */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ void *sc_powerhook; /* cookie from power hook */
+ void *sc_shutdownhook; /* cookie from shutdown hook */
+#endif
+
+ u_int sc_ncomp;
+ u_int sc_npcomp;
+ struct usbd_bus *sc_comps[EHCI_COMPANION_MAX];
+
+ usb_dma_t sc_fldma;
+ u_int sc_flsize;
+
+ LIST_HEAD(, ehci_xfer) sc_intrhead;
+
+ ehci_soft_qh_t *sc_freeqhs;
+ ehci_soft_qtd_t *sc_freeqtds;
+
+ int sc_noport;
+ u_int8_t sc_addr; /* device address */
+ u_int8_t sc_conf; /* device configuration */
+ usbd_xfer_handle sc_intrxfer;
+ char sc_isreset;
+#ifdef USB_USE_SOFTINTR
+ char sc_softwake;
+#endif /* USB_USE_SOFTINTR */
+
+ u_int32_t sc_eintrs;
+ ehci_soft_qh_t *sc_async_head;
+
+ SIMPLEQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */
+
+ struct lock sc_doorbell_lock;
+
+ usb_callout_t sc_tmo_pcd;
+
+ device_ptr_t sc_child; /* /dev/usb# device */
+
+ char sc_dying;
+} ehci_softc_t;
+
+#define EREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a))
+#define EREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (a))
+#define EREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (a))
+#define EWRITE1(sc, a, x) bus_space_write_1((sc)->iot, (sc)->ioh, (a), (x))
+#define EWRITE2(sc, a, x) bus_space_write_2((sc)->iot, (sc)->ioh, (a), (x))
+#define EWRITE4(sc, a, x) bus_space_write_4((sc)->iot, (sc)->ioh, (a), (x))
+#define EOREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a))
+#define EOREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a))
+#define EOREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a))
+#define EOWRITE1(sc, a, x) bus_space_write_1((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x))
+#define EOWRITE2(sc, a, x) bus_space_write_2((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x))
+#define EOWRITE4(sc, a, x) bus_space_write_4((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x))
+
+usbd_status ehci_init(ehci_softc_t *);
+int ehci_intr(void *);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int ehci_detach(ehci_softc_t *, int);
+int ehci_activate(device_ptr_t, enum devact);
+#endif
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
diff --git a/sys/dev/usb/hid.c b/sys/dev/usb/hid.c
new file mode 100644
index 0000000..f083a17
--- /dev/null
+++ b/sys/dev/usb/hid.c
@@ -0,0 +1,455 @@
+/* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#if defined(__NetBSD__)
+#include <sys/kernel.h>
+#endif
+#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) logprintf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+Static void hid_clear_local(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(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(void *d, int len, int kindset)
+{
+ struct hid_data *s;
+
+ s = malloc(sizeof *s, M_TEMP, M_WAITOK|M_ZERO);
+ s->start = s->p = d;
+ s->end = (char *)d + len;
+ s->kindset = kindset;
+ return (s);
+}
+
+void
+hid_end_parse(struct hid_data *s)
+{
+
+ while (s->cur.next != NULL) {
+ 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(struct hid_data *s, struct hid_item *h)
+{
+ struct hid_item *c = &s->cur;
+ unsigned int bTag, bType, bSize;
+ u_int32_t oldpos;
+ u_char *data;
+ int32_t dval;
+ u_char *p;
+ struct hid_item *hi;
+ int i;
+
+ top:
+ if (s->multimax != 0) {
+ 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;
+ oldpos = c->loc.pos;
+ s->cur = *hi;
+ c->loc.pos = oldpos;
+ 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(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 != 0)
+ id = h.report_ID;
+ hid_end_parse(d);
+ size = h.loc.pos;
+ if (id != 0) {
+ size += 8;
+ *idp = id; /* XXX wrong */
+ } else
+ *idp = 0;
+ return ((size + 7) / 8);
+}
+
+int
+hid_locate(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 != NULL)
+ *loc = h.loc;
+ if (flags != NULL)
+ *flags = h.flags;
+ hid_end_parse(d);
+ return (1);
+ }
+ }
+ hid_end_parse(d);
+ loc->size = 0;
+ return (0);
+}
+
+u_long
+hid_get_data(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(void *desc, int size, u_int32_t usage)
+{
+ struct hid_data *hd;
+ struct hid_item hi;
+ int err;
+
+ hd = hid_start_parse(desc, size, hid_input);
+ if (hd == NULL)
+ return (0);
+
+ err = hid_get_item(hd, &hi) &&
+ hi.kind == hid_collection &&
+ hi.usage == usage;
+ hid_end_parse(hd);
+ return (err);
+}
diff --git a/sys/dev/usb/hid.h b/sys/dev/usb/hid.h
new file mode 100644
index 0000000..590e7cb
--- /dev/null
+++ b/sys/dev/usb/hid.h
@@ -0,0 +1,91 @@
+/* $NetBSD: hid.h,v 1.6 2000/06/01 14:28:57 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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(void *d, int len, int kindset);
+void hid_end_parse(struct hid_data *s);
+int hid_get_item(struct hid_data *s, struct hid_item *h);
+int hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t *id);
+int hid_locate(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(u_char *buf, struct hid_location *loc);
+int hid_is_collection(void *desc, int size, u_int32_t usage);
diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
new file mode 100644
index 0000000..22f2b7a
--- /dev/null
+++ b/sys/dev/usb/if_aue.c
@@ -0,0 +1,1556 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver.
+ * Datasheet is available from http://www.admtek.com.tw.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet
+ * support: the control endpoint for reading/writing registers, burst
+ * read endpoint for packet reception, burst write for packet transmission
+ * and one for "interrupts." The chip uses the same RX filter scheme
+ * as the other ADMtek ethernet parts: one perfect filter entry for the
+ * the station address and a 64-bit multicast hash table. The chip supports
+ * both MII and HomePNA attachments.
+ *
+ * Since the maximum data transfer speed of USB is supposed to be 12Mbps,
+ * you're never really going to get 100Mbps speeds from this device. I
+ * think the idea is to allow the device to connect to 10 or 100Mbps
+ * networks, not necessarily to provide 100Mbps performance. Also, since
+ * the controller uses an external PHY chip, it's possible that board
+ * designers might simply choose a 10Mbps PHY.
+ *
+ * Registers are accessed using usbd_do_request(). Packet transfers are
+ * done using usbd_transfer() and friends.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+#if __FreeBSD_version < 500000
+#include <machine/clock.h>
+#endif
+
+#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_ethersubr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/if_auereg.h>
+
+MODULE_DEPEND(aue, usb, 1, 1, 1);
+MODULE_DEPEND(aue, ether, 1, 1, 1);
+MODULE_DEPEND(aue, miibus, 1, 1, 1);
+
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+/*
+ * Various supported device vendors/products.
+ */
+struct aue_type {
+ struct usb_devno aue_dev;
+ u_int16_t aue_flags;
+#define LSYS 0x0001 /* use Linksys reset */
+#define PNA 0x0002 /* has Home PNA */
+#define PII 0x0004 /* Pegasus II chip */
+};
+
+Static const struct aue_type aue_devs[] = {
+ {{ USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1}, PNA|PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000}, LSYS },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4}, PNA },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5}, PNA },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9}, PNA },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10}, 0 },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA}, 0 },
+ {{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC}, 0 },
+ {{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001}, PII },
+ {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS}, PNA },
+ {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII}, PII },
+ {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2}, PII },
+ {{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN}, PII },
+ {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100}, 0 },
+ {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100}, PNA },
+ {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100}, 0 },
+ {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100}, PII },
+ {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX}, 0 },
+ {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS},PII },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4}, LSYS|PII },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1}, LSYS },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX}, LSYS },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA}, PNA },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3}, LSYS|PII },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2}, LSYS|PII },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650}, LSYS },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0}, 0 },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1}, LSYS },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2}, 0 },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3}, LSYS },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX}, PII },
+ {{ USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET}, 0 },
+ {{ USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100}, PII },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_HN210E}, PII },
+ {{ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX}, 0 },
+ {{ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS}, PII },
+ {{ USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX}, 0 },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1}, LSYS|PII },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T}, LSYS },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX}, LSYS },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1}, LSYS|PNA },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA}, LSYS },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2}, LSYS|PII },
+ {{ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110}, PII },
+ {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1}, 0 },
+ {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5}, 0 },
+ {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5}, PII },
+ {{ USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM}, PII },
+ {{ USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC},PII },
+ {{ USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB}, 0 },
+ {{ USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB}, PII },
+ {{ USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100}, 0 },
+};
+#define aue_lookup(v, p) ((const struct aue_type *)usb_lookup(aue_devs, v, p))
+
+Static int aue_match(device_ptr_t);
+Static int aue_attach(device_ptr_t);
+Static int aue_detach(device_ptr_t);
+
+Static void aue_reset_pegasus_II(struct aue_softc *sc);
+Static int aue_tx_list_init(struct aue_softc *);
+Static int aue_rx_list_init(struct aue_softc *);
+Static int aue_newbuf(struct aue_softc *, struct aue_chain *, struct mbuf *);
+Static int aue_encap(struct aue_softc *, struct mbuf *, int);
+#ifdef AUE_INTR_PIPE
+Static void aue_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+#endif
+Static void aue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void aue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void aue_tick(void *);
+Static void aue_rxstart(struct ifnet *);
+Static void aue_start(struct ifnet *);
+Static int aue_ioctl(struct ifnet *, u_long, caddr_t);
+Static void aue_init(void *);
+Static void aue_stop(struct aue_softc *);
+Static void aue_watchdog(struct ifnet *);
+Static void aue_shutdown(device_ptr_t);
+Static int aue_ifmedia_upd(struct ifnet *);
+Static void aue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+Static void aue_eeprom_getword(struct aue_softc *, int, u_int16_t *);
+Static void aue_read_eeprom(struct aue_softc *, caddr_t, int, int, int);
+Static int aue_miibus_readreg(device_ptr_t, int, int);
+Static int aue_miibus_writereg(device_ptr_t, int, int, int);
+Static void aue_miibus_statchg(device_ptr_t);
+
+Static void aue_setmulti(struct aue_softc *);
+Static void aue_reset(struct aue_softc *);
+
+Static int aue_csr_read_1(struct aue_softc *, int);
+Static int aue_csr_write_1(struct aue_softc *, int, int);
+Static int aue_csr_read_2(struct aue_softc *, int);
+Static int aue_csr_write_2(struct aue_softc *, int, int);
+
+Static device_method_t aue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aue_match),
+ DEVMETHOD(device_attach, aue_attach),
+ DEVMETHOD(device_detach, aue_detach),
+ DEVMETHOD(device_shutdown, aue_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, aue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, aue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, aue_miibus_statchg),
+
+ { 0, 0 }
+};
+
+Static driver_t aue_driver = {
+ "aue",
+ aue_methods,
+ sizeof(struct aue_softc)
+};
+
+Static devclass_t aue_devclass;
+
+DRIVER_MODULE(aue, uhub, aue_driver, aue_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0);
+
+#define AUE_SETBIT(sc, reg, x) \
+ aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x))
+
+#define AUE_CLRBIT(sc, reg, x) \
+ aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x))
+
+Static int
+aue_csr_read_1(struct aue_softc *sc, int reg)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_int8_t val = 0;
+
+ if (sc->aue_dying)
+ return (0);
+
+ AUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = usbd_do_request(sc->aue_udev, &req, &val);
+
+ AUE_UNLOCK(sc);
+
+ if (err) {
+ return (0);
+ }
+
+ return (val);
+}
+
+Static int
+aue_csr_read_2(struct aue_softc *sc, int reg)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_int16_t val = 0;
+
+ if (sc->aue_dying)
+ return (0);
+
+ AUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = usbd_do_request(sc->aue_udev, &req, &val);
+
+ AUE_UNLOCK(sc);
+
+ if (err) {
+ return (0);
+ }
+
+ return (val);
+}
+
+Static int
+aue_csr_write_1(struct aue_softc *sc, int reg, int val)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->aue_dying)
+ return (0);
+
+ AUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = usbd_do_request(sc->aue_udev, &req, &val);
+
+ AUE_UNLOCK(sc);
+
+ if (err) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+Static int
+aue_csr_write_2(struct aue_softc *sc, int reg, int val)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->aue_dying)
+ return (0);
+
+ AUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = usbd_do_request(sc->aue_udev, &req, &val);
+
+ AUE_UNLOCK(sc);
+
+ if (err) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+Static void
+aue_eeprom_getword(struct aue_softc *sc, int addr, u_int16_t *dest)
+{
+ int i;
+ u_int16_t word = 0;
+
+ aue_csr_write_1(sc, AUE_EE_REG, addr);
+ aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ);
+
+ for (i = 0; i < AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE)
+ break;
+ }
+
+ if (i == AUE_TIMEOUT) {
+ printf("aue%d: EEPROM read timed out\n",
+ sc->aue_unit);
+ }
+
+ word = aue_csr_read_2(sc, AUE_EE_DATA);
+ *dest = word;
+
+ return;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+Static void
+aue_read_eeprom(struct aue_softc *sc, caddr_t dest, int off, int cnt, int swap)
+{
+ int i;
+ u_int16_t word = 0, *ptr;
+
+ for (i = 0; i < cnt; i++) {
+ aue_eeprom_getword(sc, off + i, &word);
+ ptr = (u_int16_t *)(dest + (i * 2));
+ if (swap)
+ *ptr = ntohs(word);
+ else
+ *ptr = word;
+ }
+
+ return;
+}
+
+Static int
+aue_miibus_readreg(device_ptr_t dev, int phy, int reg)
+{
+ struct aue_softc *sc = USBGETSOFTC(dev);
+ int i;
+ u_int16_t val = 0;
+
+ /*
+ * The Am79C901 HomePNA PHY actually contains
+ * two transceivers: a 1Mbps HomePNA PHY and a
+ * 10Mbps full/half duplex ethernet PHY with
+ * NWAY autoneg. However in the ADMtek adapter,
+ * only the 1Mbps PHY is actually connected to
+ * anything, so we ignore the 10Mbps one. It
+ * happens to be configured for MII address 3,
+ * so we filter that out.
+ */
+ if (sc->aue_vendor == USB_VENDOR_ADMTEK &&
+ sc->aue_product == USB_PRODUCT_ADMTEK_PEGASUS) {
+ if (phy == 3)
+ return (0);
+#ifdef notdef
+ if (phy != 1)
+ return (0);
+#endif
+ }
+
+ aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
+ aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
+
+ for (i = 0; i < AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
+ break;
+ }
+
+ if (i == AUE_TIMEOUT) {
+ printf("aue%d: MII read timed out\n", sc->aue_unit);
+ }
+
+ val = aue_csr_read_2(sc, AUE_PHY_DATA);
+
+ return (val);
+}
+
+Static int
+aue_miibus_writereg(device_ptr_t dev, int phy, int reg, int data)
+{
+ struct aue_softc *sc = USBGETSOFTC(dev);
+ int i;
+
+ if (phy == 3)
+ return (0);
+
+ aue_csr_write_2(sc, AUE_PHY_DATA, data);
+ aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
+ aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE);
+
+ for (i = 0; i < AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
+ break;
+ }
+
+ if (i == AUE_TIMEOUT) {
+ printf("aue%d: MII read timed out\n",
+ sc->aue_unit);
+ }
+
+ return(0);
+}
+
+Static void
+aue_miibus_statchg(device_ptr_t dev)
+{
+ struct aue_softc *sc = USBGETSOFTC(dev);
+ struct mii_data *mii = GET_MII(sc);
+
+ AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) {
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
+ } else {
+ AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
+ }
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
+ else
+ AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
+
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
+
+ /*
+ * Set the LED modes on the LinkSys adapter.
+ * This turns on the 'dual link LED' bin in the auxmode
+ * register of the Broadcom PHY.
+ */
+ if (sc->aue_flags & LSYS) {
+ u_int16_t auxmode;
+ auxmode = aue_miibus_readreg(dev, 0, 0x1b);
+ aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04);
+ }
+
+ return;
+}
+
+#define AUE_BITS 6
+
+Static void
+aue_setmulti(struct aue_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ u_int32_t h = 0, i;
+
+ ifp = &sc->arpcom.ac_if;
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+ return;
+ }
+
+ AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+
+ /* first, zot all the existing hash bits */
+ for (i = 0; i < 8; i++)
+ aue_csr_write_1(sc, AUE_MAR0 + i, 0);
+
+ /* now program new ones */
+#if __FreeBSD_version >= 500000
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#else
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#endif
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_le(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1);
+ AUE_SETBIT(sc, AUE_MAR + (h >> 3), 1 << (h & 0x7));
+ }
+
+ return;
+}
+
+Static void
+aue_reset_pegasus_II(struct aue_softc *sc)
+{
+ /* Magic constants taken from Linux driver. */
+ aue_csr_write_1(sc, AUE_REG_1D, 0);
+ aue_csr_write_1(sc, AUE_REG_7B, 2);
+#if 0
+ if ((sc->aue_flags & HAS_HOME_PNA) && mii_mode)
+ aue_csr_write_1(sc, AUE_REG_81, 6);
+ else
+#endif
+ aue_csr_write_1(sc, AUE_REG_81, 2);
+}
+
+Static void
+aue_reset(struct aue_softc *sc)
+{
+ int i;
+
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
+
+ for (i = 0; i < AUE_TIMEOUT; i++) {
+ if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ printf("aue%d: reset failed\n", sc->aue_unit);
+
+ /*
+ * The PHY(s) attached to the Pegasus chip may be held
+ * in reset until we flip on the GPIO outputs. Make sure
+ * to set the GPIO pins high so that the PHY(s) will
+ * be enabled.
+ *
+ * Note: We force all of the GPIO pins low first, *then*
+ * enable the ones we want.
+ */
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0);
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1);
+
+ if (sc->aue_flags & LSYS) {
+ /* Grrr. LinkSys has to be different from everyone else. */
+ aue_csr_write_1(sc, AUE_GPIO0,
+ AUE_GPIO_SEL0 | AUE_GPIO_SEL1);
+ aue_csr_write_1(sc, AUE_GPIO0,
+ AUE_GPIO_SEL0 | AUE_GPIO_SEL1 | AUE_GPIO_OUT0);
+ }
+
+ if (sc->aue_flags & PII)
+ aue_reset_pegasus_II(sc);
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(10000);
+
+ return;
+}
+
+/*
+ * Probe for a Pegasus chip.
+ */
+USB_MATCH(aue)
+{
+ USB_MATCH_START(aue, uaa);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ return (aue_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+USB_ATTACH(aue)
+{
+ USB_ATTACH_START(aue, sc, uaa);
+ char devinfo[1024];
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usbd_interface_handle iface;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ bzero(sc, sizeof(struct aue_softc));
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+
+ sc->aue_udev = uaa->device;
+ sc->aue_unit = device_get_unit(self);
+
+ if (usbd_set_config_no(sc->aue_udev, AUE_CONFIG_NO, 0)) {
+ printf("aue%d: getting interface handle failed\n",
+ sc->aue_unit);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ err = usbd_device2interface_handle(uaa->device, AUE_IFACE_IDX, &iface);
+ if (err) {
+ printf("aue%d: getting interface handle failed\n",
+ sc->aue_unit);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->aue_iface = iface;
+ sc->aue_flags = aue_lookup(uaa->vendor, uaa->product)->aue_flags;
+
+ sc->aue_product = uaa->product;
+ sc->aue_vendor = uaa->vendor;
+
+ id = usbd_get_interface_descriptor(sc->aue_iface);
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ device_set_desc_copy(self, devinfo);
+ printf("%s: %s\n", USBDEVNAME(self), devinfo);
+
+ /* Find endpoints. */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ printf("aue%d: couldn't get ep %d\n",
+ sc->aue_unit, i);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->aue_ed[AUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->aue_ed[AUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->aue_ed[AUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+#if __FreeBSD_version >= 500000
+ mtx_init(&sc->aue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+#endif
+ AUE_LOCK(sc);
+
+ /* Reset the adapter. */
+ aue_reset(sc);
+
+ /*
+ * Get station address from the EEPROM.
+ */
+ aue_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0);
+
+ bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ if_initname(ifp, "aue", sc->aue_unit);
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = aue_ioctl;
+ ifp->if_start = aue_start;
+ ifp->if_watchdog = aue_watchdog;
+ ifp->if_init = aue_init;
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ /*
+ * Do MII setup.
+ * NOTE: Doing this causes child devices to be attached to us,
+ * which we would normally disconnect at in the detach routine
+ * using device_delete_child(). However the USB code is set up
+ * such that when this driver is removed, all children devices
+ * are removed as well. In effect, the USB code ends up detaching
+ * all of our children for us, so we don't have to do is ourselves
+ * in aue_detach(). It's important to point this out since if
+ * we *do* try to detach the child devices ourselves, we will
+ * end up getting the children deleted twice, which will crash
+ * the system.
+ */
+ if (mii_phy_probe(self, &sc->aue_miibus,
+ aue_ifmedia_upd, aue_ifmedia_sts)) {
+ printf("aue%d: MII without any PHY!\n", sc->aue_unit);
+ AUE_UNLOCK(sc);
+#if __FreeBSD_version >= 500000
+ mtx_destroy(&sc->aue_mtx);
+#endif
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->aue_qdat.ifp = ifp;
+ sc->aue_qdat.if_rxstart = aue_rxstart;
+
+ /*
+ * Call MI attach routine.
+ */
+#if __FreeBSD_version >= 500000
+ ether_ifattach(ifp, eaddr);
+#else
+ ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
+#endif
+ callout_handle_init(&sc->aue_stat_ch);
+ usb_register_netisr();
+ sc->aue_dying = 0;
+
+ AUE_UNLOCK(sc);
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+Static int
+aue_detach(device_ptr_t dev)
+{
+ struct aue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ AUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ sc->aue_dying = 1;
+ untimeout(aue_tick, sc, sc->aue_stat_ch);
+#if __FreeBSD_version >= 500000
+ ether_ifdetach(ifp);
+#else
+ ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
+#endif
+
+ if (sc->aue_ep[AUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_TX]);
+ if (sc->aue_ep[AUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_RX]);
+#ifdef AUE_INTR_PIPE
+ if (sc->aue_ep[AUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_INTR]);
+#endif
+
+ AUE_UNLOCK(sc);
+#if __FreeBSD_version >= 500000
+ mtx_destroy(&sc->aue_mtx);
+#endif
+
+ return (0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+Static int
+aue_newbuf(struct aue_softc *sc, struct aue_chain *c, struct mbuf *m)
+{
+ struct mbuf *m_new = NULL;
+
+ if (m == NULL) {
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL) {
+ printf("aue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->aue_unit);
+ return (ENOBUFS);
+ }
+
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ printf("aue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->aue_unit);
+ m_freem(m_new);
+ return (ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ m_adj(m_new, ETHER_ALIGN);
+ c->aue_mbuf = m_new;
+
+ return (0);
+}
+
+Static int
+aue_rx_list_init(struct aue_softc *sc)
+{
+ struct aue_cdata *cd;
+ struct aue_chain *c;
+ int i;
+
+ cd = &sc->aue_cdata;
+ for (i = 0; i < AUE_RX_LIST_CNT; i++) {
+ c = &cd->aue_rx_chain[i];
+ c->aue_sc = sc;
+ c->aue_idx = i;
+ if (aue_newbuf(sc, c, NULL) == ENOBUFS)
+ return (ENOBUFS);
+ if (c->aue_xfer == NULL) {
+ c->aue_xfer = usbd_alloc_xfer(sc->aue_udev);
+ if (c->aue_xfer == NULL)
+ return (ENOBUFS);
+ }
+ }
+
+ return (0);
+}
+
+Static int
+aue_tx_list_init(struct aue_softc *sc)
+{
+ struct aue_cdata *cd;
+ struct aue_chain *c;
+ int i;
+
+ cd = &sc->aue_cdata;
+ for (i = 0; i < AUE_TX_LIST_CNT; i++) {
+ c = &cd->aue_tx_chain[i];
+ c->aue_sc = sc;
+ c->aue_idx = i;
+ c->aue_mbuf = NULL;
+ if (c->aue_xfer == NULL) {
+ c->aue_xfer = usbd_alloc_xfer(sc->aue_udev);
+ if (c->aue_xfer == NULL)
+ return (ENOBUFS);
+ }
+ c->aue_buf = malloc(AUE_BUFSZ, M_USBDEV, M_NOWAIT);
+ if (c->aue_buf == NULL)
+ return (ENOBUFS);
+ }
+
+ return (0);
+}
+
+#ifdef AUE_INTR_PIPE
+Static void
+aue_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct aue_softc *sc = priv;
+ struct ifnet *ifp;
+ struct aue_intrpkt *p;
+
+ AUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+ printf("aue%d: usb error on intr: %s\n", sc->aue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->aue_ep[AUE_ENDPT_RX]);
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, (void **)&p, NULL, NULL);
+
+ if (p->aue_txstat0)
+ ifp->if_oerrors++;
+
+ if (p->aue_txstat0 & (AUE_TXSTAT0_LATECOLL & AUE_TXSTAT0_EXCESSCOLL))
+ ifp->if_collisions++;
+
+ AUE_UNLOCK(sc);
+ return;
+}
+#endif
+
+Static void
+aue_rxstart(struct ifnet *ifp)
+{
+ struct aue_softc *sc;
+ struct aue_chain *c;
+
+ sc = ifp->if_softc;
+ AUE_LOCK(sc);
+ c = &sc->aue_cdata.aue_rx_chain[sc->aue_cdata.aue_rx_prod];
+
+ if (aue_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->aue_xfer, sc->aue_ep[AUE_ENDPT_RX],
+ c, mtod(c->aue_mbuf, char *), AUE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, aue_rxeof);
+ usbd_transfer(c->aue_xfer);
+
+ AUE_UNLOCK(sc);
+ return;
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+Static void
+aue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct aue_chain *c = priv;
+ struct aue_softc *sc = c->aue_sc;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ struct aue_rxpkt r;
+
+ if (sc->aue_dying)
+ return;
+ AUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->aue_rx_notice))
+ printf("aue%d: usb error on rx: %s\n", sc->aue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->aue_ep[AUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ if (total_len <= 4 + ETHER_CRC_LEN) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ m = c->aue_mbuf;
+ bcopy(mtod(m, char *) + total_len - 4, (char *)&r, sizeof(r));
+
+ /* Turn off all the non-error bits in the rx status word. */
+ r.aue_rxstat &= AUE_RXSTAT_MASK;
+
+ if (r.aue_rxstat) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ /* No errors; receive the packet. */
+ total_len -= (4 + ETHER_CRC_LEN);
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (struct ifnet *)&sc->aue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+ AUE_UNLOCK(sc);
+ return;
+done:
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(xfer, sc->aue_ep[AUE_ENDPT_RX],
+ c, mtod(c->aue_mbuf, char *), AUE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, aue_rxeof);
+ usbd_transfer(xfer);
+
+ AUE_UNLOCK(sc);
+ return;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+Static void
+aue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct aue_chain *c = priv;
+ struct aue_softc *sc = c->aue_sc;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ AUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+ printf("aue%d: usb error on tx: %s\n", sc->aue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->aue_ep[AUE_ENDPT_TX]);
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ usbd_get_xfer_status(c->aue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->aue_mbuf != NULL) {
+ c->aue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->aue_mbuf);
+ c->aue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ AUE_UNLOCK(sc);
+
+ return;
+}
+
+Static void
+aue_tick(void *xsc)
+{
+ struct aue_softc *sc = xsc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+
+ if (sc == NULL)
+ return;
+
+ AUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ mii = GET_MII(sc);
+ if (mii == NULL) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ mii_tick(mii);
+ if (!sc->aue_link && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->aue_link++;
+ if (ifp->if_snd.ifq_head != NULL)
+ aue_start(ifp);
+ }
+
+ sc->aue_stat_ch = timeout(aue_tick, sc, hz);
+
+ AUE_UNLOCK(sc);
+
+ return;
+}
+
+Static int
+aue_encap(struct aue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct aue_chain *c;
+ usbd_status err;
+
+ c = &sc->aue_cdata.aue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer, leaving two
+ * bytes at the beginning to hold the frame length.
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->aue_buf + 2);
+ c->aue_mbuf = m;
+
+ total_len = m->m_pkthdr.len + 2;
+
+ /*
+ * The ADMtek documentation says that the packet length is
+ * supposed to be specified in the first two bytes of the
+ * transfer, however it actually seems to ignore this info
+ * and base the frame size on the bulk transfer length.
+ */
+ c->aue_buf[0] = (u_int8_t)m->m_pkthdr.len;
+ c->aue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8);
+
+ usbd_setup_xfer(c->aue_xfer, sc->aue_ep[AUE_ENDPT_TX],
+ c, c->aue_buf, total_len, USBD_FORCE_SHORT_XFER,
+ 10000, aue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->aue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ aue_stop(sc);
+ return (EIO);
+ }
+
+ sc->aue_cdata.aue_tx_cnt++;
+
+ return (0);
+}
+
+Static void
+aue_start(struct ifnet *ifp)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mbuf *m_head = NULL;
+
+ AUE_LOCK(sc);
+
+ if (!sc->aue_link) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ if (ifp->if_flags & IFF_OACTIVE) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ if (aue_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+ AUE_UNLOCK(sc);
+
+ return;
+}
+
+Static void
+aue_init(void *xsc)
+{
+ struct aue_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mii_data *mii = GET_MII(sc);
+ struct aue_chain *c;
+ usbd_status err;
+ int i;
+
+ AUE_LOCK(sc);
+
+ if (ifp->if_flags & IFF_RUNNING) {
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ aue_reset(sc);
+
+ /* Set MAC address */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ aue_csr_write_1(sc, AUE_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+ else
+ AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+
+ /* Init TX ring. */
+ if (aue_tx_list_init(sc) == ENOBUFS) {
+ printf("aue%d: tx list init failed\n", sc->aue_unit);
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (aue_rx_list_init(sc) == ENOBUFS) {
+ printf("aue%d: rx list init failed\n", sc->aue_unit);
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef AUE_INTR_PIPE
+ sc->aue_cdata.aue_ibuf = malloc(AUE_INTR_PKTLEN, M_USBDEV, M_NOWAIT);
+#endif
+
+ /* Load the multicast filter. */
+ aue_setmulti(sc);
+
+ /* Enable RX and TX */
+ aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
+
+ mii_mediachg(mii);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->aue_iface, sc->aue_ed[AUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->aue_ep[AUE_ENDPT_RX]);
+ if (err) {
+ printf("aue%d: open rx pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ AUE_UNLOCK(sc);
+ return;
+ }
+ err = usbd_open_pipe(sc->aue_iface, sc->aue_ed[AUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->aue_ep[AUE_ENDPT_TX]);
+ if (err) {
+ printf("aue%d: open tx pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef AUE_INTR_PIPE
+ err = usbd_open_pipe_intr(sc->aue_iface, sc->aue_ed[AUE_ENDPT_INTR],
+ USBD_SHORT_XFER_OK, &sc->aue_ep[AUE_ENDPT_INTR], sc,
+ sc->aue_cdata.aue_ibuf, AUE_INTR_PKTLEN, aue_intr,
+ AUE_INTR_INTERVAL);
+ if (err) {
+ printf("aue%d: open intr pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ AUE_UNLOCK(sc);
+ return;
+ }
+#endif
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < AUE_RX_LIST_CNT; i++) {
+ c = &sc->aue_cdata.aue_rx_chain[i];
+ usbd_setup_xfer(c->aue_xfer, sc->aue_ep[AUE_ENDPT_RX],
+ c, mtod(c->aue_mbuf, char *), AUE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, aue_rxeof);
+ usbd_transfer(c->aue_xfer);
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ sc->aue_stat_ch = timeout(aue_tick, sc, hz);
+
+ AUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Set media options.
+ */
+Static int
+aue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ sc->aue_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+Static void
+aue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ return;
+}
+
+Static int
+aue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ int error = 0;
+
+ AUE_LOCK(sc);
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->aue_if_flags & IFF_PROMISC)) {
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+ } else if (ifp->if_flags & IFF_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->aue_if_flags & IFF_PROMISC) {
+ AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+ } else if (!(ifp->if_flags & IFF_RUNNING))
+ aue_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ aue_stop(sc);
+ }
+ sc->aue_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ aue_setmulti(sc);
+ error = 0;
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ AUE_UNLOCK(sc);
+
+ return (error);
+}
+
+Static void
+aue_watchdog(struct ifnet *ifp)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct aue_chain *c;
+ usbd_status stat;
+
+ AUE_LOCK(sc);
+
+ ifp->if_oerrors++;
+ printf("aue%d: watchdog timeout\n", sc->aue_unit);
+
+ c = &sc->aue_cdata.aue_tx_chain[0];
+ usbd_get_xfer_status(c->aue_xfer, NULL, NULL, NULL, &stat);
+ aue_txeof(c->aue_xfer, c, stat);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ aue_start(ifp);
+ AUE_UNLOCK(sc);
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+Static void
+aue_stop(struct aue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+ int i;
+
+ AUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ aue_csr_write_1(sc, AUE_CTL0, 0);
+ aue_csr_write_1(sc, AUE_CTL1, 0);
+ aue_reset(sc);
+ untimeout(aue_tick, sc, sc->aue_stat_ch);
+
+ /* Stop transfers. */
+ if (sc->aue_ep[AUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_RX]);
+ if (err) {
+ printf("aue%d: abort rx pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->aue_ep[AUE_ENDPT_RX]);
+ if (err) {
+ printf("aue%d: close rx pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ }
+ sc->aue_ep[AUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->aue_ep[AUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_TX]);
+ if (err) {
+ printf("aue%d: abort tx pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->aue_ep[AUE_ENDPT_TX]);
+ if (err) {
+ printf("aue%d: close tx pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ }
+ sc->aue_ep[AUE_ENDPT_TX] = NULL;
+ }
+
+#ifdef AUE_INTR_PIPE
+ if (sc->aue_ep[AUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_INTR]);
+ if (err) {
+ printf("aue%d: abort intr pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->aue_ep[AUE_ENDPT_INTR]);
+ if (err) {
+ printf("aue%d: close intr pipe failed: %s\n",
+ sc->aue_unit, usbd_errstr(err));
+ }
+ sc->aue_ep[AUE_ENDPT_INTR] = NULL;
+ }
+#endif
+
+ /* Free RX resources. */
+ for (i = 0; i < AUE_RX_LIST_CNT; i++) {
+ if (sc->aue_cdata.aue_rx_chain[i].aue_buf != NULL) {
+ free(sc->aue_cdata.aue_rx_chain[i].aue_buf, M_USBDEV);
+ sc->aue_cdata.aue_rx_chain[i].aue_buf = NULL;
+ }
+ if (sc->aue_cdata.aue_rx_chain[i].aue_mbuf != NULL) {
+ m_freem(sc->aue_cdata.aue_rx_chain[i].aue_mbuf);
+ sc->aue_cdata.aue_rx_chain[i].aue_mbuf = NULL;
+ }
+ if (sc->aue_cdata.aue_rx_chain[i].aue_xfer != NULL) {
+ usbd_free_xfer(sc->aue_cdata.aue_rx_chain[i].aue_xfer);
+ sc->aue_cdata.aue_rx_chain[i].aue_xfer = NULL;
+ }
+ }
+
+ /* Free TX resources. */
+ for (i = 0; i < AUE_TX_LIST_CNT; i++) {
+ if (sc->aue_cdata.aue_tx_chain[i].aue_buf != NULL) {
+ free(sc->aue_cdata.aue_tx_chain[i].aue_buf, M_USBDEV);
+ sc->aue_cdata.aue_tx_chain[i].aue_buf = NULL;
+ }
+ if (sc->aue_cdata.aue_tx_chain[i].aue_mbuf != NULL) {
+ m_freem(sc->aue_cdata.aue_tx_chain[i].aue_mbuf);
+ sc->aue_cdata.aue_tx_chain[i].aue_mbuf = NULL;
+ }
+ if (sc->aue_cdata.aue_tx_chain[i].aue_xfer != NULL) {
+ usbd_free_xfer(sc->aue_cdata.aue_tx_chain[i].aue_xfer);
+ sc->aue_cdata.aue_tx_chain[i].aue_xfer = NULL;
+ }
+ }
+
+#ifdef AUE_INTR_PIPE
+ free(sc->aue_cdata.aue_ibuf, M_USBDEV);
+ sc->aue_cdata.aue_ibuf = NULL;
+#endif
+
+ sc->aue_link = 0;
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ AUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+Static void
+aue_shutdown(device_ptr_t dev)
+{
+ struct aue_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->aue_dying++;
+ AUE_LOCK(sc);
+ aue_reset(sc);
+ aue_stop(sc);
+ AUE_UNLOCK(sc);
+
+ return;
+}
diff --git a/sys/dev/usb/if_auereg.h b/sys/dev/usb/if_auereg.h
new file mode 100644
index 0000000..478cece
--- /dev/null
+++ b/sys/dev/usb/if_auereg.h
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Register definitions for ADMtek Pegasus AN986 USB to Ethernet
+ * chip. The Pegasus uses a total of four USB endpoints: the control
+ * endpoint (0), a bulk read endpoint for receiving packets (1),
+ * a bulk write endpoint for sending packets (2) and an interrupt
+ * endpoint for passing RX and TX status (3). Endpoint 0 is used
+ * to read and write the ethernet module's registers. All registers
+ * are 8 bits wide.
+ *
+ * Packet transfer is done in 64 byte chunks. The last chunk in a
+ * transfer is denoted by having a length less that 64 bytes. For
+ * the RX case, the data includes an optional RX status word.
+ */
+
+#define AUE_UR_READREG 0xF0
+#define AUE_UR_WRITEREG 0xF1
+
+#define AUE_CONFIG_NO 1
+#define AUE_IFACE_IDX 0
+
+/*
+ * Note that while the ADMtek technically has four
+ * endpoints, the control endpoint (endpoint 0) is
+ * regarded as special by the USB code and drivers
+ * don't have direct access to it. (We access it
+ * using usbd_do_request() when reading/writing
+ * registers.) Consequently, our endpoint indexes
+ * don't match those in the ADMtek Pegasus manual:
+ * we consider the RX data endpoint to be index 0
+ * and work up from there.
+ */
+#define AUE_ENDPT_RX 0x0
+#define AUE_ENDPT_TX 0x1
+#define AUE_ENDPT_INTR 0x2
+#define AUE_ENDPT_MAX 0x3
+
+#define AUE_INTR_PKTLEN 0x8
+
+#define AUE_CTL0 0x00
+#define AUE_CTL1 0x01
+#define AUE_CTL2 0x02
+#define AUE_MAR0 0x08
+#define AUE_MAR1 0x09
+#define AUE_MAR2 0x0A
+#define AUE_MAR3 0x0B
+#define AUE_MAR4 0x0C
+#define AUE_MAR5 0x0D
+#define AUE_MAR6 0x0E
+#define AUE_MAR7 0x0F
+#define AUE_MAR AUE_MAR0
+#define AUE_PAR0 0x10
+#define AUE_PAR1 0x11
+#define AUE_PAR2 0x12
+#define AUE_PAR3 0x13
+#define AUE_PAR4 0x14
+#define AUE_PAR5 0x15
+#define AUE_PAR AUE_PAR0
+#define AUE_PAUSE0 0x18
+#define AUE_PAUSE1 0x19
+#define AUE_PAUSE AUE_PAUSE0
+#define AUE_RX_FLOWCTL_CNT 0x1A
+#define AUE_RX_FLOWCTL_FIFO 0x1B
+#define AUE_REG_1D 0x1D
+#define AUE_EE_REG 0x20
+#define AUE_EE_DATA0 0x21
+#define AUE_EE_DATA1 0x22
+#define AUE_EE_DATA AUE_EE_DATA0
+#define AUE_EE_CTL 0x23
+#define AUE_PHY_ADDR 0x25
+#define AUE_PHY_DATA0 0x26
+#define AUE_PHY_DATA1 0x27
+#define AUE_PHY_DATA AUE_PHY_DATA0
+#define AUE_PHY_CTL 0x28
+#define AUE_USB_STS 0x2A
+#define AUE_TXSTAT0 0x2B
+#define AUE_TXSTAT1 0x2C
+#define AUE_TXSTAT AUE_TXSTAT0
+#define AUE_RXSTAT 0x2D
+#define AUE_PKTLOST0 0x2E
+#define AUE_PKTLOST1 0x2F
+#define AUE_PKTLOST AUE_PKTLOST0
+
+#define AUE_REG_7B 0x7B
+#define AUE_GPIO0 0x7E
+#define AUE_GPIO1 0x7F
+#define AUE_REG_81 0x81
+
+#define AUE_CTL0_INCLUDE_RXCRC 0x01
+#define AUE_CTL0_ALLMULTI 0x02
+#define AUE_CTL0_STOP_BACKOFF 0x04
+#define AUE_CTL0_RXSTAT_APPEND 0x08
+#define AUE_CTL0_WAKEON_ENB 0x10
+#define AUE_CTL0_RXPAUSE_ENB 0x20
+#define AUE_CTL0_RX_ENB 0x40
+#define AUE_CTL0_TX_ENB 0x80
+
+#define AUE_CTL1_HOMELAN 0x04
+#define AUE_CTL1_RESETMAC 0x08
+#define AUE_CTL1_SPEEDSEL 0x10 /* 0 = 10mbps, 1 = 100mbps */
+#define AUE_CTL1_DUPLEX 0x20 /* 0 = half, 1 = full */
+#define AUE_CTL1_DELAYHOME 0x40
+
+#define AUE_CTL2_EP3_CLR 0x01 /* reading EP3 clrs status regs */
+#define AUE_CTL2_RX_BADFRAMES 0x02
+#define AUE_CTL2_RX_PROMISC 0x04
+#define AUE_CTL2_LOOPBACK 0x08
+#define AUE_CTL2_EEPROMWR_ENB 0x10
+#define AUE_CTL2_EEPROM_LOAD 0x20
+
+#define AUE_EECTL_WRITE 0x01
+#define AUE_EECTL_READ 0x02
+#define AUE_EECTL_DONE 0x04
+
+#define AUE_PHYCTL_PHYREG 0x1F
+#define AUE_PHYCTL_WRITE 0x20
+#define AUE_PHYCTL_READ 0x40
+#define AUE_PHYCTL_DONE 0x80
+
+#define AUE_USBSTS_SUSPEND 0x01
+#define AUE_USBSTS_RESUME 0x02
+
+#define AUE_TXSTAT0_JABTIMO 0x04
+#define AUE_TXSTAT0_CARLOSS 0x08
+#define AUE_TXSTAT0_NOCARRIER 0x10
+#define AUE_TXSTAT0_LATECOLL 0x20
+#define AUE_TXSTAT0_EXCESSCOLL 0x40
+#define AUE_TXSTAT0_UNDERRUN 0x80
+
+#define AUE_TXSTAT1_PKTCNT 0x0F
+#define AUE_TXSTAT1_FIFO_EMPTY 0x40
+#define AUE_TXSTAT1_FIFO_FULL 0x80
+
+#define AUE_RXSTAT_OVERRUN 0x01
+#define AUE_RXSTAT_PAUSE 0x02
+
+#define AUE_GPIO_IN0 0x01
+#define AUE_GPIO_OUT0 0x02
+#define AUE_GPIO_SEL0 0x04
+#define AUE_GPIO_IN1 0x08
+#define AUE_GPIO_OUT1 0x10
+#define AUE_GPIO_SEL1 0x20
+
+struct aue_intrpkt {
+ u_int8_t aue_txstat0;
+ u_int8_t aue_txstat1;
+ u_int8_t aue_rxstat;
+ u_int8_t aue_rxlostpkt0;
+ u_int8_t aue_rxlostpkt1;
+ u_int8_t aue_wakeupstat;
+ u_int8_t aue_rsvd;
+};
+
+struct aue_rxpkt {
+ u_int16_t aue_pktlen;
+ u_int8_t aue_rxstat;
+};
+
+#define AUE_RXSTAT_MCAST 0x01
+#define AUE_RXSTAT_GIANT 0x02
+#define AUE_RXSTAT_RUNT 0x04
+#define AUE_RXSTAT_CRCERR 0x08
+#define AUE_RXSTAT_DRIBBLE 0x10
+#define AUE_RXSTAT_MASK 0x1E
+
+#define AUE_TX_LIST_CNT 1
+#define AUE_RX_LIST_CNT 1
+
+struct aue_softc;
+
+struct aue_chain {
+ struct aue_softc *aue_sc;
+ usbd_xfer_handle aue_xfer;
+ char *aue_buf;
+ struct mbuf *aue_mbuf;
+ int aue_idx;
+};
+
+struct aue_cdata {
+ struct aue_chain aue_tx_chain[AUE_TX_LIST_CNT];
+ struct aue_chain aue_rx_chain[AUE_RX_LIST_CNT];
+ struct aue_intrpkt *aue_ibuf;
+ int aue_tx_prod;
+ int aue_tx_cons;
+ int aue_tx_cnt;
+ int aue_rx_prod;
+};
+
+#define AUE_INC(x, y) (x) = (x + 1) % y
+
+struct aue_softc {
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->aue_miibus))
+#elif defined(__NetBSD__)
+#define GET_MII(sc) (&(sc)->aue_mii)
+#elif defined(__OpenBSD__)
+#define GET_MII(sc) (&(sc)->aue_mii)
+#endif
+ struct arpcom arpcom;
+ device_t aue_miibus;
+ usbd_device_handle aue_udev;
+ usbd_interface_handle aue_iface;
+ u_int16_t aue_vendor;
+ u_int16_t aue_product;
+ int aue_ed[AUE_ENDPT_MAX];
+ usbd_pipe_handle aue_ep[AUE_ENDPT_MAX];
+ int aue_unit;
+ u_int8_t aue_link;
+ int aue_if_flags;
+ struct aue_cdata aue_cdata;
+ struct callout_handle aue_stat_ch;
+#if __FreeBSD_version >= 500000
+ struct mtx aue_mtx;
+#endif
+ u_int16_t aue_flags;
+ char aue_dying;
+ struct timeval aue_rx_notice;
+ struct usb_qdat aue_qdat;
+};
+
+#if 0
+#define AUE_LOCK(_sc) mtx_lock(&(_sc)->aue_mtx)
+#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->aue_mtx)
+#else
+#define AUE_LOCK(_sc)
+#define AUE_UNLOCK(_sc)
+#endif
+
+#define AUE_TIMEOUT 1000
+#define AUE_BUFSZ 1536
+#define AUE_MIN_FRAMELEN 60
+#define AUE_INTR_INTERVAL 100 /* ms */
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
new file mode 100644
index 0000000..440f468
--- /dev/null
+++ b/sys/dev/usb/if_axe.c
@@ -0,0 +1,1179 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000-2003
+ * Bill Paul <wpaul@windriver.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ASIX Electronics AX88172 USB 2.0 ethernet driver. Used in the
+ * LinkSys USB200M and various other adapters.
+ *
+ * Manuals available from:
+ * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF
+ * Note: you need the manual for the AX88170 chip (USB 1.x ethernet
+ * controller) to find the definitions for the RX control register.
+ * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF
+ *
+ * Written by Bill Paul <wpaul@windriver.com>
+ * Senior Engineer
+ * Wind River Systems
+ */
+
+/*
+ * The AX88172 provides USB ethernet supports at 10 and 100Mbps.
+ * It uses an external PHY (reference designs use a RealTek chip),
+ * and has a 64-bit multicast hash filter. There is some information
+ * missing from the manual which one needs to know in order to make
+ * the chip function:
+ *
+ * - You must set bit 7 in the RX control register, otherwise the
+ * chip won't receive any packets.
+ * - You must initialize all 3 IPG registers, or you won't be able
+ * to send any packets.
+ *
+ * Note that this device appears to only support loading the station
+ * address via autload from the EEPROM (i.e. there's no way to manaully
+ * set it).
+ *
+ * (Adam Weinberger wanted me to name this driver if_gir.c.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.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_ethersubr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+#include <dev/usb/if_axereg.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+Static struct axe_type axe_devs[] = {
+ { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172 },
+ { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100 },
+ { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX },
+ { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120 },
+ { 0, 0 }
+};
+
+Static int axe_match(device_ptr_t);
+Static int axe_attach(device_ptr_t);
+Static int axe_detach(device_ptr_t);
+
+Static int axe_tx_list_init(struct axe_softc *);
+Static int axe_rx_list_init(struct axe_softc *);
+Static int axe_newbuf(struct axe_softc *, struct axe_chain *, struct mbuf *);
+Static int axe_encap(struct axe_softc *, struct mbuf *, int);
+Static void axe_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void axe_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void axe_tick(void *);
+Static void axe_rxstart(struct ifnet *);
+Static void axe_start(struct ifnet *);
+Static int axe_ioctl(struct ifnet *, u_long, caddr_t);
+Static void axe_init(void *);
+Static void axe_stop(struct axe_softc *);
+Static void axe_watchdog(struct ifnet *);
+Static void axe_shutdown(device_ptr_t);
+Static int axe_miibus_readreg(device_ptr_t, int, int);
+Static int axe_miibus_writereg(device_ptr_t, int, int, int);
+Static void axe_miibus_statchg(device_ptr_t);
+Static int axe_cmd(struct axe_softc *, int, int, int, void *);
+Static int axe_ifmedia_upd(struct ifnet *);
+Static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+Static void axe_setmulti(struct axe_softc *);
+
+Static device_method_t axe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, axe_match),
+ DEVMETHOD(device_attach, axe_attach),
+ DEVMETHOD(device_detach, axe_detach),
+ DEVMETHOD(device_shutdown, axe_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, axe_miibus_readreg),
+ DEVMETHOD(miibus_writereg, axe_miibus_writereg),
+ DEVMETHOD(miibus_statchg, axe_miibus_statchg),
+
+ { 0, 0 }
+};
+
+Static driver_t axe_driver = {
+ "axe",
+ axe_methods,
+ sizeof(struct axe_softc)
+};
+
+Static devclass_t axe_devclass;
+
+DRIVER_MODULE(axe, uhub, axe_driver, axe_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(axe, usb, 1, 1, 1);
+MODULE_DEPEND(axe, miibus, 1, 1, 1);
+
+Static int
+axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->axe_dying)
+ return(0);
+
+ if (AXE_CMD_DIR(cmd))
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AXE_CMD_CMD(cmd);
+ USETW(req.wValue, val);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, AXE_CMD_LEN(cmd));
+
+ err = usbd_do_request(sc->axe_udev, &req, buf);
+
+ if (err)
+ return(-1);
+
+ return(0);
+}
+
+Static int
+axe_miibus_readreg(device_ptr_t dev, int phy, int reg)
+{
+ struct axe_softc *sc = USBGETSOFTC(dev);
+ usbd_status err;
+ u_int16_t val;
+
+ if (sc->axe_dying)
+ return(0);
+
+#ifdef notdef
+ /*
+ * The chip tells us the MII address of any supported
+ * PHYs attached to the chip, so only read from those.
+ */
+
+ if (sc->axe_phyaddrs[0] != AXE_NOPHY && phy != sc->axe_phyaddrs[0])
+ return (0);
+
+ if (sc->axe_phyaddrs[1] != AXE_NOPHY && phy != sc->axe_phyaddrs[1])
+ return (0);
+#endif
+ if (sc->axe_phyaddrs[0] != 0xFF && sc->axe_phyaddrs[0] != phy)
+ return (0);
+
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ err = axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, (void *)&val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+ AXE_UNLOCK(sc);
+
+ if (err) {
+ printf("axe%d: read PHY failed\n", sc->axe_unit);
+ return(-1);
+ }
+
+ if (val)
+ sc->axe_phyaddrs[0] = phy;
+
+ return (val);
+}
+
+Static int
+axe_miibus_writereg(device_ptr_t dev, int phy, int reg, int val)
+{
+ struct axe_softc *sc = USBGETSOFTC(dev);
+ usbd_status err;
+
+ if (sc->axe_dying)
+ return(0);
+
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ err = axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, (void *)&val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+ AXE_UNLOCK(sc);
+
+ if (err) {
+ printf("axe%d: write PHY failed\n", sc->axe_unit);
+ return(-1);
+ }
+
+ return (0);
+}
+
+Static void
+axe_miibus_statchg(device_ptr_t dev)
+{
+#ifdef notdef
+ struct axe_softc *sc = USBGETSOFTC(dev);
+ struct mii_data *mii = GET_MII(sc);
+#endif
+ /* doesn't seem to be necessary */
+
+ return;
+}
+
+/*
+ * Set media options.
+ */
+Static int
+axe_ifmedia_upd(struct ifnet *ifp)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ sc->axe_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+Static void
+axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ return;
+}
+
+Static void
+axe_setmulti(struct axe_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ u_int32_t h = 0;
+ u_int16_t rxmode;
+ u_int8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ ifp = &sc->arpcom.ac_if;
+
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, (void *)&rxmode);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxmode |= AXE_RXCMD_ALLMULTI;
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+ AXE_UNLOCK(sc);
+ return;
+ } else
+ rxmode &= ~AXE_RXCMD_ALLMULTI;
+
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ hashtbl[h / 8] |= 1 << (h % 8);
+ }
+
+ axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+Static void
+axe_reset(struct axe_softc *sc)
+{
+ if (sc->axe_dying)
+ return;
+
+ if (usbd_set_config_no(sc->axe_udev, AXE_CONFIG_NO, 1) ||
+ usbd_device2interface_handle(sc->axe_udev, AXE_IFACE_IDX,
+ &sc->axe_iface)) {
+ printf("axe%d: getting interface handle failed\n",
+ sc->axe_unit);
+ }
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(1000);
+ return;
+}
+
+/*
+ * Probe for a AX88172 chip.
+ */
+USB_MATCH(axe)
+{
+ USB_MATCH_START(axe, uaa);
+ struct axe_type *t;
+
+ if (!uaa->iface)
+ return(UMATCH_NONE);
+
+ t = axe_devs;
+ while(t->axe_vid) {
+ if (uaa->vendor == t->axe_vid &&
+ uaa->product == t->axe_did) {
+ return(UMATCH_VENDOR_PRODUCT);
+ }
+ t++;
+ }
+
+ return(UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+USB_ATTACH(axe)
+{
+ USB_ATTACH_START(axe, sc, uaa);
+ char devinfo[1024];
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ bzero(sc, sizeof(struct axe_softc));
+ sc->axe_udev = uaa->device;
+ sc->axe_dev = self;
+ sc->axe_unit = device_get_unit(self);
+
+ if (usbd_set_config_no(sc->axe_udev, AXE_CONFIG_NO, 1)) {
+ printf("axe%d: getting interface handle failed\n",
+ sc->axe_unit);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ if (usbd_device2interface_handle(uaa->device,
+ AXE_IFACE_IDX, &sc->axe_iface)) {
+ printf("axe%d: getting interface handle failed\n",
+ sc->axe_unit);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ id = usbd_get_interface_descriptor(sc->axe_iface);
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ device_set_desc_copy(self, devinfo);
+ printf("%s: %s\n", USBDEVNAME(self), devinfo);
+
+ /* Find endpoints. */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->axe_iface, i);
+ if (!ed) {
+ printf("axe%d: couldn't get ep %d\n",
+ sc->axe_unit, i);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->axe_ed[AXE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->axe_ed[AXE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->axe_ed[AXE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+ mtx_init(&sc->axe_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ AXE_LOCK(sc);
+
+ /*
+ * Get station address.
+ */
+ axe_cmd(sc, AXE_CMD_READ_NODEID, 0, 0, &eaddr);
+
+ /*
+ * Load IPG values and PHY indexes.
+ */
+ axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, (void *)&sc->axe_ipgs);
+ axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, (void *)&sc->axe_phyaddrs);
+
+ /*
+ * Work around broken adapters that appear to lie about
+ * their PHY addresses.
+ */
+ sc->axe_phyaddrs[0] = sc->axe_phyaddrs[1] = 0xFF;
+
+ bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ if_initname(ifp, "axe", sc->axe_unit);
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = axe_ioctl;
+ ifp->if_start = axe_start;
+ ifp->if_watchdog = axe_watchdog;
+ ifp->if_init = axe_init;
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ sc->axe_qdat.ifp = ifp;
+ sc->axe_qdat.if_rxstart = axe_rxstart;
+
+ if (mii_phy_probe(self, &sc->axe_miibus,
+ axe_ifmedia_upd, axe_ifmedia_sts)) {
+ printf("axe%d: MII without any PHY!\n", sc->axe_unit);
+ AXE_UNLOCK(sc);
+ mtx_destroy(&sc->axe_mtx);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /*
+ * Call MI attach routine.
+ */
+
+ ether_ifattach(ifp, eaddr);
+ callout_handle_init(&sc->axe_stat_ch);
+ usb_register_netisr();
+
+ sc->axe_dying = 0;
+
+ AXE_UNLOCK(sc);
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+Static int
+axe_detach(device_ptr_t dev)
+{
+ struct axe_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ AXE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ sc->axe_dying = 1;
+ untimeout(axe_tick, sc, sc->axe_stat_ch);
+ ether_ifdetach(ifp);
+
+ if (sc->axe_ep[AXE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]);
+ if (sc->axe_ep[AXE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]);
+ if (sc->axe_ep[AXE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
+
+ AXE_UNLOCK(sc);
+ mtx_destroy(&sc->axe_mtx);
+
+ return(0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+Static int
+axe_newbuf(struct axe_softc *sc, struct axe_chain *c, struct mbuf *m)
+{
+ struct mbuf *m_new = NULL;
+
+ if (m == NULL) {
+ m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m_new == NULL) {
+ printf("axe%d: no memory for rx list "
+ "-- packet dropped!\n", sc->axe_unit);
+ return(ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ m_adj(m_new, ETHER_ALIGN);
+ c->axe_mbuf = m_new;
+
+ return(0);
+}
+
+Static int
+axe_rx_list_init(struct axe_softc *sc)
+{
+ struct axe_cdata *cd;
+ struct axe_chain *c;
+ int i;
+
+ cd = &sc->axe_cdata;
+ for (i = 0; i < AXE_RX_LIST_CNT; i++) {
+ c = &cd->axe_rx_chain[i];
+ c->axe_sc = sc;
+ c->axe_idx = i;
+ if (axe_newbuf(sc, c, NULL) == ENOBUFS)
+ return(ENOBUFS);
+ if (c->axe_xfer == NULL) {
+ c->axe_xfer = usbd_alloc_xfer(sc->axe_udev);
+ if (c->axe_xfer == NULL)
+ return(ENOBUFS);
+ }
+ }
+
+ return(0);
+}
+
+Static int
+axe_tx_list_init(struct axe_softc *sc)
+{
+ struct axe_cdata *cd;
+ struct axe_chain *c;
+ int i;
+
+ cd = &sc->axe_cdata;
+ for (i = 0; i < AXE_TX_LIST_CNT; i++) {
+ c = &cd->axe_tx_chain[i];
+ c->axe_sc = sc;
+ c->axe_idx = i;
+ c->axe_mbuf = NULL;
+ if (c->axe_xfer == NULL) {
+ c->axe_xfer = usbd_alloc_xfer(sc->axe_udev);
+ if (c->axe_xfer == NULL)
+ return(ENOBUFS);
+ }
+ c->axe_buf = malloc(AXE_BUFSZ, M_USBDEV, M_NOWAIT);
+ if (c->axe_buf == NULL)
+ return(ENOBUFS);
+ }
+
+ return(0);
+}
+
+Static void
+axe_rxstart(struct ifnet *ifp)
+{
+ struct axe_softc *sc;
+ struct axe_chain *c;
+
+ sc = ifp->if_softc;
+ AXE_LOCK(sc);
+ c = &sc->axe_cdata.axe_rx_chain[sc->axe_cdata.axe_rx_prod];
+
+ if (axe_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->axe_xfer, sc->axe_ep[AXE_ENDPT_RX],
+ c, mtod(c->axe_mbuf, char *), AXE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, axe_rxeof);
+ usbd_transfer(c->axe_xfer);
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+Static void
+axe_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct axe_softc *sc;
+ struct axe_chain *c;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+
+ c = priv;
+ sc = c->axe_sc;
+ AXE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->axe_rx_notice))
+ printf("axe%d: usb error on rx: %s\n", sc->axe_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->axe_ep[AXE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ m = c->axe_mbuf;
+
+ if (total_len < sizeof(struct ether_header)) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (struct ifnet *)&sc->axe_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+ AXE_UNLOCK(sc);
+
+ return;
+done:
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->axe_xfer, sc->axe_ep[AXE_ENDPT_RX],
+ c, mtod(c->axe_mbuf, char *), AXE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, axe_rxeof);
+ usbd_transfer(c->axe_xfer);
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+Static void
+axe_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct axe_softc *sc;
+ struct axe_chain *c;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ c = priv;
+ sc = c->axe_sc;
+ AXE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+ printf("axe%d: usb error on tx: %s\n", sc->axe_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->axe_ep[AXE_ENDPT_TX]);
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ usbd_get_xfer_status(c->axe_xfer, NULL, NULL, NULL, &err);
+
+ if (c->axe_mbuf != NULL) {
+ c->axe_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->axe_mbuf);
+ c->axe_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+Static void
+axe_tick(void *xsc)
+{
+ struct axe_softc *sc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+
+ sc = xsc;
+
+ if (sc == NULL)
+ return;
+
+ AXE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ mii = GET_MII(sc);
+ if (mii == NULL) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ mii_tick(mii);
+ if (!sc->axe_link && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->axe_link++;
+ if (ifp->if_snd.ifq_head != NULL)
+ axe_start(ifp);
+ }
+
+ sc->axe_stat_ch = timeout(axe_tick, sc, hz);
+
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+Static int
+axe_encap(struct axe_softc *sc, struct mbuf *m, int idx)
+{
+ struct axe_chain *c;
+ usbd_status err;
+
+ c = &sc->axe_cdata.axe_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer, leaving two
+ * bytes at the beginning to hold the frame length.
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->axe_buf);
+ c->axe_mbuf = m;
+
+ usbd_setup_xfer(c->axe_xfer, sc->axe_ep[AXE_ENDPT_TX],
+ c, c->axe_buf, m->m_pkthdr.len, 0, 10000, axe_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->axe_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ axe_stop(sc);
+ return(EIO);
+ }
+
+ sc->axe_cdata.axe_tx_cnt++;
+
+ return(0);
+}
+
+Static void
+axe_start(struct ifnet *ifp)
+{
+ struct axe_softc *sc;
+ struct mbuf *m_head = NULL;
+
+ sc = ifp->if_softc;
+ AXE_LOCK(sc);
+
+ if (!sc->axe_link) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ if (ifp->if_flags & IFF_OACTIVE) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ if (axe_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+Static void
+axe_init(void *xsc)
+{
+ struct axe_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct axe_chain *c;
+ usbd_status err;
+ int i;
+ int rxmode;
+
+ if (ifp->if_flags & IFF_RUNNING)
+ return;
+
+ AXE_LOCK(sc);
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+
+ axe_reset(sc);
+
+#ifdef notdef
+ /* Set MAC address */
+ axe_mac(sc, sc->arpcom.ac_enaddr, 1);
+#endif
+
+ /* Enable RX logic. */
+
+ /* Init TX ring. */
+ if (axe_tx_list_init(sc) == ENOBUFS) {
+ printf("axe%d: tx list init failed\n", sc->axe_unit);
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (axe_rx_list_init(sc) == ENOBUFS) {
+ printf("axe%d: rx list init failed\n", sc->axe_unit);
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ /* Set transmitter IPG values */
+ axe_cmd(sc, AXE_CMD_WRITE_IPG0, 0, sc->axe_ipgs[0], NULL);
+ axe_cmd(sc, AXE_CMD_WRITE_IPG1, 0, sc->axe_ipgs[1], NULL);
+ axe_cmd(sc, AXE_CMD_WRITE_IPG2, 0, sc->axe_ipgs[2], NULL);
+
+ /* Enable receiver, set RX mode */
+ rxmode = AXE_RXCMD_UNICAST|AXE_RXCMD_MULTICAST|AXE_RXCMD_ENABLE;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ rxmode |= AXE_RXCMD_PROMISC;
+
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxmode |= AXE_RXCMD_BROADCAST;
+
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+
+ /* Load the multicast filter. */
+ axe_setmulti(sc);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->axe_iface, sc->axe_ed[AXE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->axe_ep[AXE_ENDPT_RX]);
+ if (err) {
+ printf("axe%d: open rx pipe failed: %s\n",
+ sc->axe_unit, usbd_errstr(err));
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ err = usbd_open_pipe(sc->axe_iface, sc->axe_ed[AXE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->axe_ep[AXE_ENDPT_TX]);
+ if (err) {
+ printf("axe%d: open tx pipe failed: %s\n",
+ sc->axe_unit, usbd_errstr(err));
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < AXE_RX_LIST_CNT; i++) {
+ c = &sc->axe_cdata.axe_rx_chain[i];
+ usbd_setup_xfer(c->axe_xfer, sc->axe_ep[AXE_ENDPT_RX],
+ c, mtod(c->axe_mbuf, char *), AXE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axe_rxeof);
+ usbd_transfer(c->axe_xfer);
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ AXE_UNLOCK(sc);
+
+ sc->axe_stat_ch = timeout(axe_tick, sc, hz);
+
+ return;
+}
+
+Static int
+axe_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ u_int16_t rxmode;
+ int error = 0;
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->axe_if_flags & IFF_PROMISC)) {
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_RXCTL_READ,
+ 0, 0, (void *)&rxmode);
+ rxmode |= AXE_RXCMD_PROMISC;
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE,
+ 0, rxmode, NULL);
+ AXE_UNLOCK(sc);
+ axe_setmulti(sc);
+ } else if (ifp->if_flags & IFF_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->axe_if_flags & IFF_PROMISC) {
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_RXCTL_READ,
+ 0, 0, (void *)&rxmode);
+ rxmode &= ~AXE_RXCMD_PROMISC;
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE,
+ 0, rxmode, NULL);
+ AXE_UNLOCK(sc);
+ axe_setmulti(sc);
+ } else if (!(ifp->if_flags & IFF_RUNNING))
+ axe_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ axe_stop(sc);
+ }
+ sc->axe_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ axe_setmulti(sc);
+ error = 0;
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ break;
+
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ AXE_UNLOCK(sc);
+
+ return(error);
+}
+
+Static void
+axe_watchdog(struct ifnet *ifp)
+{
+ struct axe_softc *sc;
+ struct axe_chain *c;
+ usbd_status stat;
+
+ sc = ifp->if_softc;
+ AXE_LOCK(sc);
+
+ ifp->if_oerrors++;
+ printf("axe%d: watchdog timeout\n", sc->axe_unit);
+
+ c = &sc->axe_cdata.axe_tx_chain[0];
+ usbd_get_xfer_status(c->axe_xfer, NULL, NULL, NULL, &stat);
+ axe_txeof(c->axe_xfer, c, stat);
+
+ AXE_UNLOCK(sc);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ axe_start(ifp);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+Static void
+axe_stop(struct axe_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+ int i;
+
+ AXE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ untimeout(axe_tick, sc, sc->axe_stat_ch);
+
+ /* Stop transfers. */
+ if (sc->axe_ep[AXE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]);
+ if (err) {
+ printf("axe%d: abort rx pipe failed: %s\n",
+ sc->axe_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_RX]);
+ if (err) {
+ printf("axe%d: close rx pipe failed: %s\n",
+ sc->axe_unit, usbd_errstr(err));
+ }
+ sc->axe_ep[AXE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->axe_ep[AXE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]);
+ if (err) {
+ printf("axe%d: abort tx pipe failed: %s\n",
+ sc->axe_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_TX]);
+ if (err) {
+ printf("axe%d: close tx pipe failed: %s\n",
+ sc->axe_unit, usbd_errstr(err));
+ }
+ sc->axe_ep[AXE_ENDPT_TX] = NULL;
+ }
+
+ if (sc->axe_ep[AXE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
+ if (err) {
+ printf("axe%d: abort intr pipe failed: %s\n",
+ sc->axe_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
+ if (err) {
+ printf("axe%d: close intr pipe failed: %s\n",
+ sc->axe_unit, usbd_errstr(err));
+ }
+ sc->axe_ep[AXE_ENDPT_INTR] = NULL;
+ }
+
+ axe_reset(sc);
+
+ /* Free RX resources. */
+ for (i = 0; i < AXE_RX_LIST_CNT; i++) {
+ if (sc->axe_cdata.axe_rx_chain[i].axe_buf != NULL) {
+ free(sc->axe_cdata.axe_rx_chain[i].axe_buf, M_USBDEV);
+ sc->axe_cdata.axe_rx_chain[i].axe_buf = NULL;
+ }
+ if (sc->axe_cdata.axe_rx_chain[i].axe_mbuf != NULL) {
+ m_freem(sc->axe_cdata.axe_rx_chain[i].axe_mbuf);
+ sc->axe_cdata.axe_rx_chain[i].axe_mbuf = NULL;
+ }
+ if (sc->axe_cdata.axe_rx_chain[i].axe_xfer != NULL) {
+ usbd_free_xfer(sc->axe_cdata.axe_rx_chain[i].axe_xfer);
+ sc->axe_cdata.axe_rx_chain[i].axe_xfer = NULL;
+ }
+ }
+
+ /* Free TX resources. */
+ for (i = 0; i < AXE_TX_LIST_CNT; i++) {
+ if (sc->axe_cdata.axe_tx_chain[i].axe_buf != NULL) {
+ free(sc->axe_cdata.axe_tx_chain[i].axe_buf, M_USBDEV);
+ sc->axe_cdata.axe_tx_chain[i].axe_buf = NULL;
+ }
+ if (sc->axe_cdata.axe_tx_chain[i].axe_mbuf != NULL) {
+ m_freem(sc->axe_cdata.axe_tx_chain[i].axe_mbuf);
+ sc->axe_cdata.axe_tx_chain[i].axe_mbuf = NULL;
+ }
+ if (sc->axe_cdata.axe_tx_chain[i].axe_xfer != NULL) {
+ usbd_free_xfer(sc->axe_cdata.axe_tx_chain[i].axe_xfer);
+ sc->axe_cdata.axe_tx_chain[i].axe_xfer = NULL;
+ }
+ }
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ sc->axe_link = 0;
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+Static void
+axe_shutdown(device_ptr_t dev)
+{
+ struct axe_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ axe_stop(sc);
+
+ return;
+}
diff --git a/sys/dev/usb/if_axereg.h b/sys/dev/usb/if_axereg.h
new file mode 100644
index 0000000..a9938f4
--- /dev/null
+++ b/sys/dev/usb/if_axereg.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000-2003
+ * Bill Paul <wpaul@windriver.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for the ASIX Electronics AX88172 to ethernet controller.
+ */
+
+
+/*
+ * Vendor specific commands
+ * ASIX conveniently doesn't document the 'set NODEID' command in their
+ * datasheet (thanks a lot guys).
+ * To make handling these commands easier, I added some extra data
+ * which is decided by the axe_cmd() routine. Commands are encoded
+ * in 16 bites, with the format: LDCC. L and D are both nibbles in
+ * the high byte. L represents the data length (0 to 15) and D
+ * represents the direction (0 for vendor read, 1 for vendor write).
+ * CC is the command byte, as specified in the manual.
+ */
+
+#define AXE_CMD_DIR(x) (((x) & 0x0F00) >> 8)
+#define AXE_CMD_LEN(x) (((x) & 0xF000) >> 12)
+#define AXE_CMD_CMD(x) ((x) & 0x00FF)
+
+#define AXE_CMD_READ_RXTX_SRAM 0x2002
+#define AXE_CMD_WRITE_RX_SRAM 0x0103
+#define AXE_CMD_WRITE_TX_SRAM 0x0104
+#define AXE_CMD_MII_OPMODE_SW 0x0106
+#define AXE_CMD_MII_READ_REG 0x2007
+#define AXE_CMD_MII_WRITE_REG 0x2108
+#define AXE_CMD_MII_READ_OPMODE 0x1009
+#define AXE_CMD_MII_OPMODE_HW 0x010A
+#define AXE_CMD_SROM_READ 0x200B
+#define AXE_CMD_SROM_WRITE 0x010C
+#define AXE_CMD_SROM_WR_ENABLE 0x010D
+#define AXE_CMD_SROM_WR_DISABLE 0x010E
+#define AXE_CMD_RXCTL_READ 0x200F
+#define AXE_CMD_RXCTL_WRITE 0x0110
+#define AXE_CMD_READ_IPG012 0x3011
+#define AXE_CMD_WRITE_IPG0 0x0112
+#define AXE_CMD_WRITE_IPG1 0x0113
+#define AXE_CMD_WRITE_IPG2 0x0114
+#define AXE_CMD_READ_MCAST 0x8015
+#define AXE_CMD_WRITE_MCAST 0x8116
+#define AXE_CMD_READ_NODEID 0x6017
+#define AXE_CMD_WRITE_NODEID 0x6118
+#define AXE_CMD_READ_PHYID 0x2019
+#define AXE_CMD_READ_MEDIA 0x101A
+#define AXE_CMD_WRITE_MEDIA 0x011B
+#define AXE_CMD_READ_MONITOR_MODE 0x101C
+#define AXE_CMD_WRITE_MONITOR_MODE 0x011D
+#define AXE_CMD_READ_GPIO 0x101E
+#define AXE_CMD_WRITE_GPIO 0x011F
+
+#define AXE_RXCMD_PROMISC 0x0001
+#define AXE_RXCMD_ALLMULTI 0x0002
+#define AXE_RXCMD_UNICAST 0x0004
+#define AXE_RXCMD_BROADCAST 0x0008
+#define AXE_RXCMD_MULTICAST 0x0010
+#define AXE_RXCMD_ENABLE 0x0080
+
+#define AXE_NOPHY 0xE0
+
+#define AXE_TIMEOUT 1000
+#define AXE_BUFSZ 1536
+#define AXE_MIN_FRAMELEN 60
+#define AXE_RX_FRAMES 1
+#define AXE_TX_FRAMES 1
+
+#define AXE_RX_LIST_CNT 1
+#define AXE_TX_LIST_CNT 1
+
+#define AXE_CTL_READ 0x01
+#define AXE_CTL_WRITE 0x02
+
+#define AXE_CONFIG_NO 1
+#define AXE_IFACE_IDX 0
+
+/*
+ * The interrupt endpoint is currently unused
+ * by the ASIX part.
+ */
+#define AXE_ENDPT_RX 0x0
+#define AXE_ENDPT_TX 0x1
+#define AXE_ENDPT_INTR 0x2
+#define AXE_ENDPT_MAX 0x3
+
+struct axe_type {
+ u_int16_t axe_vid;
+ u_int16_t axe_did;
+};
+
+struct axe_softc;
+
+struct axe_chain {
+ struct axe_softc *axe_sc;
+ usbd_xfer_handle axe_xfer;
+ char *axe_buf;
+ struct mbuf *axe_mbuf;
+ int axe_accum;
+ int axe_idx;
+};
+
+struct axe_cdata {
+ struct axe_chain axe_tx_chain[AXE_TX_LIST_CNT];
+ struct axe_chain axe_rx_chain[AXE_RX_LIST_CNT];
+ int axe_tx_prod;
+ int axe_tx_cons;
+ int axe_tx_cnt;
+ int axe_rx_prod;
+};
+
+#define AXE_INC(x, y) (x) = (x + 1) % y
+
+struct axe_softc {
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->axe_miibus))
+#elif defined(__NetBSD__)
+#define GET_MII(sc) (&(sc)->axe_mii)
+#elif defined(__OpenBSD__)
+#define GET_MII(sc) (&(sc)->axe_mii)
+#endif
+ struct arpcom arpcom;
+ device_t axe_miibus;
+ device_t axe_dev;
+ usbd_device_handle axe_udev;
+ usbd_interface_handle axe_iface;
+ int axe_ed[AXE_ENDPT_MAX];
+ usbd_pipe_handle axe_ep[AXE_ENDPT_MAX];
+ int axe_unit;
+ int axe_if_flags;
+ struct axe_cdata axe_cdata;
+ struct callout_handle axe_stat_ch;
+ struct mtx axe_mtx;
+ char axe_dying;
+ int axe_link;
+ unsigned char axe_ipgs[3];
+ unsigned char axe_phyaddrs[2];
+ struct timeval axe_rx_notice;
+ struct usb_qdat axe_qdat;
+};
+
+#if 0
+#define AXE_LOCK(_sc) mtx_lock(&(_sc)->axe_mtx)
+#define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->axe_mtx)
+#else
+#define AXE_LOCK(_sc)
+#define AXE_UNLOCK(_sc)
+#endif
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
new file mode 100644
index 0000000..d7f5f6b
--- /dev/null
+++ b/sys/dev/usb/if_cue.c
@@ -0,0 +1,1204 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate
+ * adapters and others.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The
+ * RX filter uses a 512-bit multicast hash table, single perfect entry
+ * for the station address, and promiscuous mode. Unlike the ADMtek
+ * and KLSI chips, the CATC ASIC supports read and write combining
+ * mode where multiple packets can be transfered using a single bulk
+ * transaction, which helps performance a great deal.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+#if __FreeBSD_version < 500000
+#include <machine/clock.h>
+#endif
+
+#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_ethersubr.h>
+
+#include <dev/usb/if_cuereg.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+Static struct cue_type cue_devs[] = {
+ { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE },
+ { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2 },
+ { USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK },
+ { 0, 0 }
+};
+
+Static int cue_match(device_ptr_t);
+Static int cue_attach(device_ptr_t);
+Static int cue_detach(device_ptr_t);
+
+Static int cue_tx_list_init(struct cue_softc *);
+Static int cue_rx_list_init(struct cue_softc *);
+Static int cue_newbuf(struct cue_softc *, struct cue_chain *, struct mbuf *);
+Static int cue_encap(struct cue_softc *, struct mbuf *, int);
+Static void cue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void cue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void cue_tick(void *);
+Static void cue_rxstart(struct ifnet *);
+Static void cue_start(struct ifnet *);
+Static int cue_ioctl(struct ifnet *, u_long, caddr_t);
+Static void cue_init(void *);
+Static void cue_stop(struct cue_softc *);
+Static void cue_watchdog(struct ifnet *);
+Static void cue_shutdown(device_ptr_t);
+
+Static void cue_setmulti(struct cue_softc *);
+Static uint32_t cue_mchash(const uint8_t *);
+Static void cue_reset(struct cue_softc *);
+
+Static int cue_csr_read_1(struct cue_softc *, int);
+Static int cue_csr_write_1(struct cue_softc *, int, int);
+Static int cue_csr_read_2(struct cue_softc *, int);
+#ifdef notdef
+Static int cue_csr_write_2(struct cue_softc *, int, int);
+#endif
+Static int cue_mem(struct cue_softc *, int, int, void *, int);
+Static int cue_getmac(struct cue_softc *, void *);
+
+Static device_method_t cue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cue_match),
+ DEVMETHOD(device_attach, cue_attach),
+ DEVMETHOD(device_detach, cue_detach),
+ DEVMETHOD(device_shutdown, cue_shutdown),
+
+ { 0, 0 }
+};
+
+Static driver_t cue_driver = {
+ "cue",
+ cue_methods,
+ sizeof(struct cue_softc)
+};
+
+Static devclass_t cue_devclass;
+
+DRIVER_MODULE(cue, uhub, cue_driver, cue_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(cue, usb, 1, 1, 1);
+MODULE_DEPEND(cue, ether, 1, 1, 1);
+
+#define CUE_SETBIT(sc, reg, x) \
+ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x))
+
+#define CUE_CLRBIT(sc, reg, x) \
+ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x))
+
+Static int
+cue_csr_read_1(struct cue_softc *sc, int reg)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_int8_t val = 0;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = usbd_do_request(sc->cue_udev, &req, &val);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(0);
+
+ return(val);
+}
+
+Static int
+cue_csr_read_2(struct cue_softc *sc, int reg)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_int16_t val = 0;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = usbd_do_request(sc->cue_udev, &req, &val);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(0);
+
+ return(val);
+}
+
+Static int
+cue_csr_write_1(struct cue_softc *sc, int reg, int val)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->cue_udev, &req, NULL);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(-1);
+
+ return(0);
+}
+
+#ifdef notdef
+Static int
+cue_csr_write_2(struct cue_softc *sc, int reg, int val)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->cue_udev, &req, NULL);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(-1);
+
+ return(0);
+}
+#endif
+
+Static int
+cue_mem(struct cue_softc *sc, int cmd, int addr, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ if (cmd == CUE_CMD_READSRAM)
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = cmd;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ err = usbd_do_request(sc->cue_udev, &req, buf);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(-1);
+
+ return(0);
+}
+
+Static int
+cue_getmac(struct cue_softc *sc, void *buf)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_GET_MACADDR;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, ETHER_ADDR_LEN);
+
+ err = usbd_do_request(sc->cue_udev, &req, buf);
+
+ CUE_UNLOCK(sc);
+
+ if (err) {
+ printf("cue%d: read MAC address failed\n", sc->cue_unit);
+ return(-1);
+ }
+
+ return(0);
+}
+
+#define CUE_BITS 9
+
+Static uint32_t
+cue_mchash(const uint8_t *addr)
+{
+ uint32_t crc;
+
+ /* Compute CRC for the address value. */
+ crc = ether_crc32_le(addr, ETHER_ADDR_LEN);
+
+ return (crc & ((1 << CUE_BITS) - 1));
+}
+
+Static void
+cue_setmulti(struct cue_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ u_int32_t h = 0, i;
+
+ ifp = &sc->arpcom.ac_if;
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ for (i = 0; i < CUE_MCAST_TABLE_LEN; i++)
+ sc->cue_mctab[i] = 0xFF;
+ cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
+ &sc->cue_mctab, CUE_MCAST_TABLE_LEN);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ for (i = 0; i < CUE_MCAST_TABLE_LEN; i++)
+ sc->cue_mctab[i] = 0;
+
+ /* now program new ones */
+#if __FreeBSD_version >= 500000
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#else
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#endif
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ sc->cue_mctab[h >> 3] |= 1 << (h & 0x7);
+ }
+
+ /*
+ * Also include the broadcast address in the filter
+ * so we can receive broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST) {
+#if __FreeBSD_version >= 500000
+ h = cue_mchash(ifp->if_broadcastaddr);
+#else
+ h = cue_mchash(etherbroadcastaddr);
+#endif
+ sc->cue_mctab[h >> 3] |= 1 << (h & 0x7);
+ }
+
+ cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
+ &sc->cue_mctab, CUE_MCAST_TABLE_LEN);
+
+ return;
+}
+
+Static void
+cue_reset(struct cue_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_RESET;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(sc->cue_udev, &req, NULL);
+ if (err)
+ printf("cue%d: reset failed\n", sc->cue_unit);
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(1000);
+ return;
+}
+
+/*
+ * Probe for a Pegasus chip.
+ */
+USB_MATCH(cue)
+{
+ USB_MATCH_START(cue, uaa);
+ struct cue_type *t;
+
+ if (!uaa->iface)
+ return(UMATCH_NONE);
+
+ t = cue_devs;
+ while(t->cue_vid) {
+ if (uaa->vendor == t->cue_vid &&
+ uaa->product == t->cue_did) {
+ return(UMATCH_VENDOR_PRODUCT);
+ }
+ t++;
+ }
+
+ return(UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+USB_ATTACH(cue)
+{
+ USB_ATTACH_START(cue, sc, uaa);
+ char devinfo[1024];
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ bzero(sc, sizeof(struct cue_softc));
+ sc->cue_iface = uaa->iface;
+ sc->cue_udev = uaa->device;
+ sc->cue_unit = device_get_unit(self);
+
+ if (usbd_set_config_no(sc->cue_udev, CUE_CONFIG_NO, 0)) {
+ printf("cue%d: getting interface handle failed\n",
+ sc->cue_unit);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ device_set_desc_copy(self, devinfo);
+ printf("%s: %s\n", USBDEVNAME(self), devinfo);
+
+ /* Find endpoints. */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(uaa->iface, i);
+ if (!ed) {
+ printf("cue%d: couldn't get ep %d\n",
+ sc->cue_unit, i);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->cue_ed[CUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->cue_ed[CUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->cue_ed[CUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+#if __FreeBSD_version >= 500000
+ mtx_init(&sc->cue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+#endif
+ CUE_LOCK(sc);
+
+#ifdef notdef
+ /* Reset the adapter. */
+ cue_reset(sc);
+#endif
+ /*
+ * Get station address.
+ */
+ cue_getmac(sc, &eaddr);
+
+ bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ if_initname(ifp, "cue", sc->cue_unit);
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = cue_ioctl;
+ ifp->if_start = cue_start;
+ ifp->if_watchdog = cue_watchdog;
+ ifp->if_init = cue_init;
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ sc->cue_qdat.ifp = ifp;
+ sc->cue_qdat.if_rxstart = cue_rxstart;
+
+ /*
+ * Call MI attach routine.
+ */
+#if __FreeBSD_version >= 500000
+ ether_ifattach(ifp, eaddr);
+#else
+ ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
+#endif
+ callout_handle_init(&sc->cue_stat_ch);
+ usb_register_netisr();
+ sc->cue_dying = 0;
+
+ CUE_UNLOCK(sc);
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+Static int
+cue_detach(device_ptr_t dev)
+{
+ struct cue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ CUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ sc->cue_dying = 1;
+ untimeout(cue_tick, sc, sc->cue_stat_ch);
+#if __FreeBSD_version >= 500000
+ ether_ifdetach(ifp);
+#else
+ ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
+#endif
+
+ if (sc->cue_ep[CUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_TX]);
+ if (sc->cue_ep[CUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_RX]);
+ if (sc->cue_ep[CUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_INTR]);
+
+ CUE_UNLOCK(sc);
+#if __FreeBSD_version >= 500000
+ mtx_destroy(&sc->cue_mtx);
+#endif
+
+ return(0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+Static int
+cue_newbuf(struct cue_softc *sc, struct cue_chain *c, struct mbuf *m)
+{
+ struct mbuf *m_new = NULL;
+
+ if (m == NULL) {
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL) {
+ printf("cue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->cue_unit);
+ return(ENOBUFS);
+ }
+
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ printf("cue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->cue_unit);
+ m_freem(m_new);
+ return(ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ m_adj(m_new, ETHER_ALIGN);
+ c->cue_mbuf = m_new;
+
+ return(0);
+}
+
+Static int
+cue_rx_list_init(struct cue_softc *sc)
+{
+ struct cue_cdata *cd;
+ struct cue_chain *c;
+ int i;
+
+ cd = &sc->cue_cdata;
+ for (i = 0; i < CUE_RX_LIST_CNT; i++) {
+ c = &cd->cue_rx_chain[i];
+ c->cue_sc = sc;
+ c->cue_idx = i;
+ if (cue_newbuf(sc, c, NULL) == ENOBUFS)
+ return(ENOBUFS);
+ if (c->cue_xfer == NULL) {
+ c->cue_xfer = usbd_alloc_xfer(sc->cue_udev);
+ if (c->cue_xfer == NULL)
+ return(ENOBUFS);
+ }
+ }
+
+ return(0);
+}
+
+Static int
+cue_tx_list_init(struct cue_softc *sc)
+{
+ struct cue_cdata *cd;
+ struct cue_chain *c;
+ int i;
+
+ cd = &sc->cue_cdata;
+ for (i = 0; i < CUE_TX_LIST_CNT; i++) {
+ c = &cd->cue_tx_chain[i];
+ c->cue_sc = sc;
+ c->cue_idx = i;
+ c->cue_mbuf = NULL;
+ if (c->cue_xfer == NULL) {
+ c->cue_xfer = usbd_alloc_xfer(sc->cue_udev);
+ if (c->cue_xfer == NULL)
+ return(ENOBUFS);
+ }
+ c->cue_buf = malloc(CUE_BUFSZ, M_USBDEV, M_NOWAIT);
+ if (c->cue_buf == NULL)
+ return(ENOBUFS);
+ }
+
+ return(0);
+}
+
+Static void
+cue_rxstart(struct ifnet *ifp)
+{
+ struct cue_softc *sc;
+ struct cue_chain *c;
+
+ sc = ifp->if_softc;
+ CUE_LOCK(sc);
+ c = &sc->cue_cdata.cue_rx_chain[sc->cue_cdata.cue_rx_prod];
+
+ if (cue_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->cue_xfer, sc->cue_ep[CUE_ENDPT_RX],
+ c, mtod(c->cue_mbuf, char *), CUE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, cue_rxeof);
+ usbd_transfer(c->cue_xfer);
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+Static void
+cue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct cue_softc *sc;
+ struct cue_chain *c;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ u_int16_t len;
+
+ c = priv;
+ sc = c->cue_sc;
+ CUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->cue_rx_notice))
+ printf("cue%d: usb error on rx: %s\n", sc->cue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->cue_ep[CUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ m = c->cue_mbuf;
+ len = *mtod(m, u_int16_t *);
+
+ /* No errors; receive the packet. */
+ total_len = len;
+
+ if (len < sizeof(struct ether_header)) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ ifp->if_ipackets++;
+ m_adj(m, sizeof(u_int16_t));
+ m->m_pkthdr.rcvif = (struct ifnet *)&sc->cue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+ CUE_UNLOCK(sc);
+
+ return;
+done:
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->cue_xfer, sc->cue_ep[CUE_ENDPT_RX],
+ c, mtod(c->cue_mbuf, char *), CUE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, cue_rxeof);
+ usbd_transfer(c->cue_xfer);
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+Static void
+cue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct cue_softc *sc;
+ struct cue_chain *c;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ c = priv;
+ sc = c->cue_sc;
+ CUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+ printf("cue%d: usb error on tx: %s\n", sc->cue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->cue_ep[CUE_ENDPT_TX]);
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ usbd_get_xfer_status(c->cue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->cue_mbuf != NULL) {
+ c->cue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->cue_mbuf);
+ c->cue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+Static void
+cue_tick(void *xsc)
+{
+ struct cue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = xsc;
+
+ if (sc == NULL)
+ return;
+
+ CUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_SINGLECOLL);
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_MULTICOLL);
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_EXCESSCOLL);
+
+ if (cue_csr_read_2(sc, CUE_RX_FRAMEERR))
+ ifp->if_ierrors++;
+
+ sc->cue_stat_ch = timeout(cue_tick, sc, hz);
+
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+Static int
+cue_encap(struct cue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct cue_chain *c;
+ usbd_status err;
+
+ c = &sc->cue_cdata.cue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer, leaving two
+ * bytes at the beginning to hold the frame length.
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->cue_buf + 2);
+ c->cue_mbuf = m;
+
+ total_len = m->m_pkthdr.len + 2;
+
+ /* The first two bytes are the frame length */
+ c->cue_buf[0] = (u_int8_t)m->m_pkthdr.len;
+ c->cue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8);
+
+ usbd_setup_xfer(c->cue_xfer, sc->cue_ep[CUE_ENDPT_TX],
+ c, c->cue_buf, total_len, 0, 10000, cue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->cue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ cue_stop(sc);
+ return(EIO);
+ }
+
+ sc->cue_cdata.cue_tx_cnt++;
+
+ return(0);
+}
+
+Static void
+cue_start(struct ifnet *ifp)
+{
+ struct cue_softc *sc;
+ struct mbuf *m_head = NULL;
+
+ sc = ifp->if_softc;
+ CUE_LOCK(sc);
+
+ if (ifp->if_flags & IFF_OACTIVE) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ if (cue_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+Static void
+cue_init(void *xsc)
+{
+ struct cue_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct cue_chain *c;
+ usbd_status err;
+ int i;
+
+ if (ifp->if_flags & IFF_RUNNING)
+ return;
+
+ CUE_LOCK(sc);
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+#ifdef foo
+ cue_reset(sc);
+#endif
+
+ /* Set MAC address */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ cue_csr_write_1(sc, CUE_PAR0 - i, sc->arpcom.ac_enaddr[i]);
+
+ /* Enable RX logic. */
+ cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON|CUE_ETHCTL_MCAST_ON);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC) {
+ CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ } else {
+ CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ }
+
+ /* Init TX ring. */
+ if (cue_tx_list_init(sc) == ENOBUFS) {
+ printf("cue%d: tx list init failed\n", sc->cue_unit);
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (cue_rx_list_init(sc) == ENOBUFS) {
+ printf("cue%d: rx list init failed\n", sc->cue_unit);
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Load the multicast filter. */
+ cue_setmulti(sc);
+
+ /*
+ * Set the number of RX and TX buffers that we want
+ * to reserve inside the ASIC.
+ */
+ cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES);
+ cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES);
+
+ /* Set advanced operation modes. */
+ cue_csr_write_1(sc, CUE_ADVANCED_OPMODES,
+ CUE_AOP_EMBED_RXLEN|0x01); /* 1 wait state */
+
+ /* Program the LED operation. */
+ cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->cue_ep[CUE_ENDPT_RX]);
+ if (err) {
+ printf("cue%d: open rx pipe failed: %s\n",
+ sc->cue_unit, usbd_errstr(err));
+ CUE_UNLOCK(sc);
+ return;
+ }
+ err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->cue_ep[CUE_ENDPT_TX]);
+ if (err) {
+ printf("cue%d: open tx pipe failed: %s\n",
+ sc->cue_unit, usbd_errstr(err));
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < CUE_RX_LIST_CNT; i++) {
+ c = &sc->cue_cdata.cue_rx_chain[i];
+ usbd_setup_xfer(c->cue_xfer, sc->cue_ep[CUE_ENDPT_RX],
+ c, mtod(c->cue_mbuf, char *), CUE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cue_rxeof);
+ usbd_transfer(c->cue_xfer);
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ CUE_UNLOCK(sc);
+
+ sc->cue_stat_ch = timeout(cue_tick, sc, hz);
+
+ return;
+}
+
+Static int
+cue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct cue_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ CUE_LOCK(sc);
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->cue_if_flags & IFF_PROMISC)) {
+ CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ cue_setmulti(sc);
+ } else if (ifp->if_flags & IFF_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->cue_if_flags & IFF_PROMISC) {
+ CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ cue_setmulti(sc);
+ } else if (!(ifp->if_flags & IFF_RUNNING))
+ cue_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ cue_stop(sc);
+ }
+ sc->cue_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ cue_setmulti(sc);
+ error = 0;
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ CUE_UNLOCK(sc);
+
+ return(error);
+}
+
+Static void
+cue_watchdog(struct ifnet *ifp)
+{
+ struct cue_softc *sc;
+ struct cue_chain *c;
+ usbd_status stat;
+
+ sc = ifp->if_softc;
+ CUE_LOCK(sc);
+
+ ifp->if_oerrors++;
+ printf("cue%d: watchdog timeout\n", sc->cue_unit);
+
+ c = &sc->cue_cdata.cue_tx_chain[0];
+ usbd_get_xfer_status(c->cue_xfer, NULL, NULL, NULL, &stat);
+ cue_txeof(c->cue_xfer, c, stat);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ cue_start(ifp);
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+Static void
+cue_stop(struct cue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+ int i;
+
+ CUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ cue_csr_write_1(sc, CUE_ETHCTL, 0);
+ cue_reset(sc);
+ untimeout(cue_tick, sc, sc->cue_stat_ch);
+
+ /* Stop transfers. */
+ if (sc->cue_ep[CUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_RX]);
+ if (err) {
+ printf("cue%d: abort rx pipe failed: %s\n",
+ sc->cue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_RX]);
+ if (err) {
+ printf("cue%d: close rx pipe failed: %s\n",
+ sc->cue_unit, usbd_errstr(err));
+ }
+ sc->cue_ep[CUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->cue_ep[CUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_TX]);
+ if (err) {
+ printf("cue%d: abort tx pipe failed: %s\n",
+ sc->cue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_TX]);
+ if (err) {
+ printf("cue%d: close tx pipe failed: %s\n",
+ sc->cue_unit, usbd_errstr(err));
+ }
+ sc->cue_ep[CUE_ENDPT_TX] = NULL;
+ }
+
+ if (sc->cue_ep[CUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_INTR]);
+ if (err) {
+ printf("cue%d: abort intr pipe failed: %s\n",
+ sc->cue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_INTR]);
+ if (err) {
+ printf("cue%d: close intr pipe failed: %s\n",
+ sc->cue_unit, usbd_errstr(err));
+ }
+ sc->cue_ep[CUE_ENDPT_INTR] = NULL;
+ }
+
+ /* Free RX resources. */
+ for (i = 0; i < CUE_RX_LIST_CNT; i++) {
+ if (sc->cue_cdata.cue_rx_chain[i].cue_buf != NULL) {
+ free(sc->cue_cdata.cue_rx_chain[i].cue_buf, M_USBDEV);
+ sc->cue_cdata.cue_rx_chain[i].cue_buf = NULL;
+ }
+ if (sc->cue_cdata.cue_rx_chain[i].cue_mbuf != NULL) {
+ m_freem(sc->cue_cdata.cue_rx_chain[i].cue_mbuf);
+ sc->cue_cdata.cue_rx_chain[i].cue_mbuf = NULL;
+ }
+ if (sc->cue_cdata.cue_rx_chain[i].cue_xfer != NULL) {
+ usbd_free_xfer(sc->cue_cdata.cue_rx_chain[i].cue_xfer);
+ sc->cue_cdata.cue_rx_chain[i].cue_xfer = NULL;
+ }
+ }
+
+ /* Free TX resources. */
+ for (i = 0; i < CUE_TX_LIST_CNT; i++) {
+ if (sc->cue_cdata.cue_tx_chain[i].cue_buf != NULL) {
+ free(sc->cue_cdata.cue_tx_chain[i].cue_buf, M_USBDEV);
+ sc->cue_cdata.cue_tx_chain[i].cue_buf = NULL;
+ }
+ if (sc->cue_cdata.cue_tx_chain[i].cue_mbuf != NULL) {
+ m_freem(sc->cue_cdata.cue_tx_chain[i].cue_mbuf);
+ sc->cue_cdata.cue_tx_chain[i].cue_mbuf = NULL;
+ }
+ if (sc->cue_cdata.cue_tx_chain[i].cue_xfer != NULL) {
+ usbd_free_xfer(sc->cue_cdata.cue_tx_chain[i].cue_xfer);
+ sc->cue_cdata.cue_tx_chain[i].cue_xfer = NULL;
+ }
+ }
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+Static void
+cue_shutdown(device_ptr_t dev)
+{
+ struct cue_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ CUE_LOCK(sc);
+ cue_reset(sc);
+ cue_stop(sc);
+ CUE_UNLOCK(sc);
+
+ return;
+}
diff --git a/sys/dev/usb/if_cuereg.h b/sys/dev/usb/if_cuereg.h
new file mode 100644
index 0000000..beaf1d5
--- /dev/null
+++ b/sys/dev/usb/if_cuereg.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for the CATC Netmate II USB to ethernet controller.
+ */
+
+
+/*
+ * Vendor specific control commands.
+ */
+#define CUE_CMD_RESET 0xF4
+#define CUE_CMD_GET_MACADDR 0xF2
+#define CUE_CMD_WRITEREG 0xFA
+#define CUE_CMD_READREG 0xFB
+#define CUE_CMD_READSRAM 0xF1
+#define CUE_CMD_WRITESRAM 0xFC
+
+/*
+ * Internal registers
+ */
+#define CUE_TX_BUFCNT 0x20
+#define CUE_RX_BUFCNT 0x21
+#define CUE_ADVANCED_OPMODES 0x22
+#define CUE_TX_BUFPKTS 0x23
+#define CUE_RX_BUFPKTS 0x24
+#define CUE_RX_MAXCHAIN 0x25
+
+#define CUE_ETHCTL 0x60
+#define CUE_ETHSTS 0x61
+#define CUE_PAR5 0x62
+#define CUE_PAR4 0x63
+#define CUE_PAR3 0x64
+#define CUE_PAR2 0x65
+#define CUE_PAR1 0x66
+#define CUE_PAR0 0x67
+
+/* Error counters, all 16 bits wide. */
+#define CUE_TX_SINGLECOLL 0x69
+#define CUE_TX_MULTICOLL 0x6B
+#define CUE_TX_EXCESSCOLL 0x6D
+#define CUE_RX_FRAMEERR 0x6F
+
+#define CUE_LEDCTL 0x81
+
+/* Advenced operating mode register */
+#define CUE_AOP_SRAMWAITS 0x03
+#define CUE_AOP_EMBED_RXLEN 0x08
+#define CUE_AOP_RXCOMBINE 0x10
+#define CUE_AOP_TXCOMBINE 0x20
+#define CUE_AOP_EVEN_PKT_READS 0x40
+#define CUE_AOP_LOOPBK 0x80
+
+/* Ethernet control register */
+#define CUE_ETHCTL_RX_ON 0x01
+#define CUE_ETHCTL_LINK_POLARITY 0x02
+#define CUE_ETHCTL_LINK_FORCE_OK 0x04
+#define CUE_ETHCTL_MCAST_ON 0x08
+#define CUE_ETHCTL_PROMISC 0x10
+
+/* Ethernet status register */
+#define CUE_ETHSTS_NO_CARRIER 0x01
+#define CUE_ETHSTS_LATECOLL 0x02
+#define CUE_ETHSTS_EXCESSCOLL 0x04
+#define CUE_ETHSTS_TXBUF_AVAIL 0x08
+#define CUE_ETHSTS_BAD_POLARITY 0x10
+#define CUE_ETHSTS_LINK_OK 0x20
+
+/* LED control register */
+#define CUE_LEDCTL_BLINK_1X 0x00
+#define CUE_LEDCTL_BLINK_2X 0x01
+#define CUE_LEDCTL_BLINK_QUARTER_ON 0x02
+#define CUE_LEDCTL_BLINK_QUARTER_OFF 0x03
+#define CUE_LEDCTL_OFF 0x04
+#define CUE_LEDCTL_FOLLOW_LINK 0x08
+
+/*
+ * Address in ASIC's internal SRAM where the
+ * multicast hash table lives. The table is 64 bytes long,
+ * giving us a 512-bit table. We have to set the bit that
+ * corresponds to the broadcast address in order to enable
+ * reception of broadcast frames.
+ */
+#define CUE_MCAST_TABLE_ADDR 0xFA80
+#define CUE_MCAST_TABLE_LEN 64
+
+#define CUE_TIMEOUT 1000
+#define CUE_BUFSZ 1536
+#define CUE_MIN_FRAMELEN 60
+#define CUE_RX_FRAMES 1
+#define CUE_TX_FRAMES 1
+
+#define CUE_RX_LIST_CNT 1
+#define CUE_TX_LIST_CNT 1
+
+#define CUE_CTL_READ 0x01
+#define CUE_CTL_WRITE 0x02
+
+#define CUE_CONFIG_NO 1
+
+/*
+ * The interrupt endpoint is currently unused
+ * by the KLSI part.
+ */
+#define CUE_ENDPT_RX 0x0
+#define CUE_ENDPT_TX 0x1
+#define CUE_ENDPT_INTR 0x2
+#define CUE_ENDPT_MAX 0x3
+
+struct cue_type {
+ u_int16_t cue_vid;
+ u_int16_t cue_did;
+};
+
+struct cue_softc;
+
+struct cue_chain {
+ struct cue_softc *cue_sc;
+ usbd_xfer_handle cue_xfer;
+ char *cue_buf;
+ struct mbuf *cue_mbuf;
+ int cue_accum;
+ int cue_idx;
+};
+
+struct cue_cdata {
+ struct cue_chain cue_tx_chain[CUE_TX_LIST_CNT];
+ struct cue_chain cue_rx_chain[CUE_RX_LIST_CNT];
+ int cue_tx_prod;
+ int cue_tx_cons;
+ int cue_tx_cnt;
+ int cue_rx_prod;
+};
+
+#define CUE_INC(x, y) (x) = (x + 1) % y
+
+struct cue_softc {
+ struct arpcom arpcom;
+ usbd_device_handle cue_udev;
+ usbd_interface_handle cue_iface;
+ int cue_ed[CUE_ENDPT_MAX];
+ usbd_pipe_handle cue_ep[CUE_ENDPT_MAX];
+ int cue_unit;
+ u_int8_t cue_mctab[CUE_MCAST_TABLE_LEN];
+ int cue_if_flags;
+ u_int16_t cue_rxfilt;
+ struct cue_cdata cue_cdata;
+ struct callout_handle cue_stat_ch;
+#if __FreeBSD_version >= 500000
+ struct mtx cue_mtx;
+#endif
+ char cue_dying;
+ struct timeval cue_rx_notice;
+ struct usb_qdat cue_qdat;
+};
+
+#if 0
+#define CUE_LOCK(_sc) mtx_lock(&(_sc)->cue_mtx)
+#define CUE_UNLOCK(_sc) mtx_unlock(&(_sc)->cue_mtx)
+#else
+#define CUE_LOCK(_sc)
+#define CUE_UNLOCK(_sc)
+#endif
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
new file mode 100644
index 0000000..5038ac4
--- /dev/null
+++ b/sys/dev/usb/if_kue.c
@@ -0,0 +1,1138 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The KLSI USB to ethernet adapter chip contains an USB serial interface,
+ * ethernet MAC and embedded microcontroller (called the QT Engine).
+ * The chip must have firmware loaded into it before it will operate.
+ * Packets are passed between the chip and host via bulk transfers.
+ * There is an interrupt endpoint mentioned in the software spec, however
+ * it's currently unused. This device is 10Mbps half-duplex only, hence
+ * there is no media selection logic. The MAC supports a 128 entry
+ * multicast filter, though the exact size of the filter can depend
+ * on the firmware. Curiously, while the software spec describes various
+ * ethernet statistics counters, my sample adapter and firmware combination
+ * claims not to support any statistics counters at all.
+ *
+ * Note that once we load the firmware in the device, we have to be
+ * careful not to load it again: if you restart your computer but
+ * leave the adapter attached to the USB controller, it may remain
+ * powered on and retain its firmware. In this case, we don't need
+ * to load the firmware a second time.
+ *
+ * Special thanks to Rob Furr for providing an ADS Technologies
+ * adapter for development and testing. No monkeys were harmed during
+ * the development of this driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+#if __FreeBSD_version < 500000
+#include <machine/clock.h>
+#endif
+
+#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_ethersubr.h>
+
+#include <dev/usb/if_kuereg.h>
+#include <dev/usb/kue_fw.h>
+
+MODULE_DEPEND(kue, usb, 1, 1, 1);
+MODULE_DEPEND(kue, ether, 1, 1, 1);
+
+/*
+ * Various supported device vendors/products.
+ */
+Static struct kue_type kue_devs[] = {
+ { USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101 },
+ { USB_VENDOR_KLSI, USB_PRODUCT_AOX_USB101 },
+ { USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT },
+ { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T },
+ { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101 },
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET },
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2 },
+ { USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45 },
+ { USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250 },
+ { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T },
+ { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C },
+ { USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB },
+ { USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T },
+ { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT },
+ { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN },
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3 },
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT },
+ { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450 },
+ { 0, 0 }
+};
+
+Static int kue_match(device_ptr_t);
+Static int kue_attach(device_ptr_t);
+Static int kue_detach(device_ptr_t);
+Static void kue_shutdown(device_ptr_t);
+Static int kue_tx_list_init(struct kue_softc *);
+Static int kue_rx_list_init(struct kue_softc *);
+Static int kue_newbuf(struct kue_softc *, struct kue_chain *, struct mbuf *);
+Static int kue_encap(struct kue_softc *, struct mbuf *, int);
+Static void kue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void kue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void kue_start(struct ifnet *);
+Static void kue_rxstart(struct ifnet *);
+Static int kue_ioctl(struct ifnet *, u_long, caddr_t);
+Static void kue_init(void *);
+Static void kue_stop(struct kue_softc *);
+Static void kue_watchdog(struct ifnet *);
+
+Static void kue_setmulti(struct kue_softc *);
+Static void kue_reset(struct kue_softc *);
+
+Static usbd_status kue_do_request(usbd_device_handle,
+ usb_device_request_t *, void *);
+Static usbd_status kue_ctl(struct kue_softc *, int, u_int8_t,
+ u_int16_t, char *, int);
+Static usbd_status kue_setword(struct kue_softc *, u_int8_t, u_int16_t);
+Static int kue_load_fw(struct kue_softc *);
+
+Static device_method_t kue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, kue_match),
+ DEVMETHOD(device_attach, kue_attach),
+ DEVMETHOD(device_detach, kue_detach),
+ DEVMETHOD(device_shutdown, kue_shutdown),
+
+ { 0, 0 }
+};
+
+Static driver_t kue_driver = {
+ "kue",
+ kue_methods,
+ sizeof(struct kue_softc)
+};
+
+Static devclass_t kue_devclass;
+
+DRIVER_MODULE(kue, uhub, kue_driver, kue_devclass, usbd_driver_load, 0);
+
+/*
+ * We have a custom do_request function which is almost like the
+ * regular do_request function, except it has a much longer timeout.
+ * Why? Because we need to make requests over the control endpoint
+ * to download the firmware to the device, which can take longer
+ * than the default timeout.
+ */
+Static usbd_status
+kue_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data)
+{
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ xfer = usbd_alloc_xfer(dev);
+ usbd_setup_default_xfer(xfer, dev, 0, 500000, req,
+ data, UGETW(req->wLength), USBD_SHORT_XFER_OK, 0);
+ err = usbd_sync_transfer(xfer);
+ usbd_free_xfer(xfer);
+ return(err);
+}
+
+Static usbd_status
+kue_setword(struct kue_softc *sc, u_int8_t breq, u_int16_t word)
+{
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->kue_dying)
+ return(USBD_NORMAL_COMPLETION);
+
+ dev = sc->kue_udev;
+
+ KUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+
+ req.bRequest = breq;
+ USETW(req.wValue, word);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = kue_do_request(dev, &req, NULL);
+
+ KUE_UNLOCK(sc);
+
+ return(err);
+}
+
+Static usbd_status
+kue_ctl(struct kue_softc *sc, int rw, u_int8_t breq, u_int16_t val,
+ char *data, int len)
+{
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status err;
+
+ dev = sc->kue_udev;
+
+ if (sc->kue_dying)
+ return(USBD_NORMAL_COMPLETION);
+
+ KUE_LOCK(sc);
+
+ if (rw == KUE_CTL_WRITE)
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+
+ req.bRequest = breq;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ err = kue_do_request(dev, &req, data);
+
+ KUE_UNLOCK(sc);
+
+ return(err);
+}
+
+Static int
+kue_load_fw(struct kue_softc *sc)
+{
+ usbd_status err;
+ usb_device_descriptor_t *dd;
+ int hwrev;
+
+ dd = &sc->kue_udev->ddesc;
+ hwrev = UGETW(dd->bcdDevice);
+
+ /*
+ * First, check if we even need to load the firmware.
+ * If the device was still attached when the system was
+ * rebooted, it may already have firmware loaded in it.
+ * If this is the case, we don't need to do it again.
+ * And in fact, if we try to load it again, we'll hang,
+ * so we have to avoid this condition if we don't want
+ * to look stupid.
+ *
+ * We can test this quickly by checking the bcdRevision
+ * code. The NIC will return a different revision code if
+ * it's probed while the firmware is still loaded and
+ * running.
+ */
+ if (hwrev == 0x0202)
+ return(0);
+
+ /* Load code segment */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_code_seg, sizeof(kue_code_seg));
+ if (err) {
+ printf("kue%d: failed to load code segment: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ return(ENXIO);
+ }
+
+ /* Load fixup segment */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_fix_seg, sizeof(kue_fix_seg));
+ if (err) {
+ printf("kue%d: failed to load fixup segment: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ return(ENXIO);
+ }
+
+ /* Send trigger command. */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_trig_seg, sizeof(kue_trig_seg));
+ if (err) {
+ printf("kue%d: failed to load trigger segment: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ return(ENXIO);
+ }
+
+ return(0);
+}
+
+Static void
+kue_setmulti(struct kue_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ int i = 0;
+
+ ifp = &sc->arpcom.ac_if;
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI;
+ sc->kue_rxfilt &= ~KUE_RXFILT_MULTICAST;
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);
+ return;
+ }
+
+ sc->kue_rxfilt &= ~KUE_RXFILT_ALLMULTI;
+
+#if __FreeBSD_version >= 500000
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#else
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#endif
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ /*
+ * If there are too many addresses for the
+ * internal filter, switch over to allmulti mode.
+ */
+ if (i == KUE_MCFILTCNT(sc))
+ break;
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ KUE_MCFILT(sc, i), ETHER_ADDR_LEN);
+ i++;
+ }
+
+ if (i == KUE_MCFILTCNT(sc))
+ sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI;
+ else {
+ sc->kue_rxfilt |= KUE_RXFILT_MULTICAST;
+ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS,
+ i, sc->kue_mcfilters, i * ETHER_ADDR_LEN);
+ }
+
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);
+
+ return;
+}
+
+/*
+ * Issue a SET_CONFIGURATION command to reset the MAC. This should be
+ * done after the firmware is loaded into the adapter in order to
+ * bring it into proper operation.
+ */
+Static void
+kue_reset(struct kue_softc *sc)
+{
+ if (usbd_set_config_no(sc->kue_udev, KUE_CONFIG_NO, 0) ||
+ usbd_device2interface_handle(sc->kue_udev, KUE_IFACE_IDX,
+ &sc->kue_iface)) {
+ printf("kue%d: getting interface handle failed\n",
+ sc->kue_unit);
+ }
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(1000);
+ return;
+}
+
+/*
+ * Probe for a KLSI chip.
+ */
+USB_MATCH(kue)
+{
+ USB_MATCH_START(kue, uaa);
+ struct kue_type *t;
+
+ if (!uaa->iface)
+ return(UMATCH_NONE);
+
+ t = kue_devs;
+ while(t->kue_vid) {
+ if (uaa->vendor == t->kue_vid &&
+ uaa->product == t->kue_did) {
+ return(UMATCH_VENDOR_PRODUCT);
+ }
+ t++;
+ }
+
+ return(UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do
+ * setup and ethernet/BPF attach.
+ */
+USB_ATTACH(kue)
+{
+ USB_ATTACH_START(kue, sc, uaa);
+ char devinfo[1024];
+ struct ifnet *ifp;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ bzero(sc, sizeof(struct kue_softc));
+ sc->kue_iface = uaa->iface;
+ sc->kue_udev = uaa->device;
+ sc->kue_unit = device_get_unit(self);
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ device_set_desc_copy(self, devinfo);
+ printf("%s: %s\n", USBDEVNAME(self), devinfo);
+
+ /* Find endpoints. */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(uaa->iface, i);
+ if (!ed) {
+ printf("kue%d: couldn't get ep %d\n",
+ sc->kue_unit, i);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->kue_ed[KUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->kue_ed[KUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->kue_ed[KUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+#if __FreeBSD_version >= 500000
+ mtx_init(&sc->kue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+#endif
+ KUE_LOCK(sc);
+
+ /* Load the firmware into the NIC. */
+ if (kue_load_fw(sc)) {
+ KUE_UNLOCK(sc);
+#if __FreeBSD_version >= 500000
+ mtx_destroy(&sc->kue_mtx);
+#endif
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Reset the adapter. */
+ kue_reset(sc);
+
+ /* Read ethernet descriptor */
+ err = kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR,
+ 0, (char *)&sc->kue_desc, sizeof(sc->kue_desc));
+
+ sc->kue_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
+ M_USBDEV, M_NOWAIT);
+
+ bcopy(sc->kue_desc.kue_macaddr,
+ (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ if_initname(ifp, "kue", sc->kue_unit);
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = kue_ioctl;
+ ifp->if_start = kue_start;
+ ifp->if_watchdog = kue_watchdog;
+ ifp->if_init = kue_init;
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ sc->kue_qdat.ifp = ifp;
+ sc->kue_qdat.if_rxstart = kue_rxstart;
+
+ /*
+ * Call MI attach routine.
+ */
+#if __FreeBSD_version >= 500000
+ ether_ifattach(ifp, sc->kue_desc.kue_macaddr);
+#else
+ ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
+#endif
+ usb_register_netisr();
+ sc->kue_dying = 0;
+
+ KUE_UNLOCK(sc);
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+Static int
+kue_detach(device_ptr_t dev)
+{
+ struct kue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ KUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ sc->kue_dying = 1;
+
+ if (ifp != NULL)
+#if __FreeBSD_version >= 500000
+ ether_ifdetach(ifp);
+#else
+ ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
+#endif
+
+ if (sc->kue_ep[KUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_TX]);
+ if (sc->kue_ep[KUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_RX]);
+ if (sc->kue_ep[KUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_INTR]);
+
+ if (sc->kue_mcfilters != NULL)
+ free(sc->kue_mcfilters, M_USBDEV);
+
+ KUE_UNLOCK(sc);
+#if __FreeBSD_version >= 500000
+ mtx_destroy(&sc->kue_mtx);
+#endif
+
+ return(0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+Static int
+kue_newbuf(struct kue_softc *sc, struct kue_chain *c, struct mbuf *m)
+{
+ struct mbuf *m_new = NULL;
+
+ if (m == NULL) {
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL) {
+ printf("kue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->kue_unit);
+ return(ENOBUFS);
+ }
+
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ printf("kue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->kue_unit);
+ m_freem(m_new);
+ return(ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ c->kue_mbuf = m_new;
+
+ return(0);
+}
+
+Static int
+kue_rx_list_init(struct kue_softc *sc)
+{
+ struct kue_cdata *cd;
+ struct kue_chain *c;
+ int i;
+
+ cd = &sc->kue_cdata;
+ for (i = 0; i < KUE_RX_LIST_CNT; i++) {
+ c = &cd->kue_rx_chain[i];
+ c->kue_sc = sc;
+ c->kue_idx = i;
+ if (kue_newbuf(sc, c, NULL) == ENOBUFS)
+ return(ENOBUFS);
+ if (c->kue_xfer == NULL) {
+ c->kue_xfer = usbd_alloc_xfer(sc->kue_udev);
+ if (c->kue_xfer == NULL)
+ return(ENOBUFS);
+ }
+ }
+
+ return(0);
+}
+
+Static int
+kue_tx_list_init(struct kue_softc *sc)
+{
+ struct kue_cdata *cd;
+ struct kue_chain *c;
+ int i;
+
+ cd = &sc->kue_cdata;
+ for (i = 0; i < KUE_TX_LIST_CNT; i++) {
+ c = &cd->kue_tx_chain[i];
+ c->kue_sc = sc;
+ c->kue_idx = i;
+ c->kue_mbuf = NULL;
+ if (c->kue_xfer == NULL) {
+ c->kue_xfer = usbd_alloc_xfer(sc->kue_udev);
+ if (c->kue_xfer == NULL)
+ return(ENOBUFS);
+ }
+ c->kue_buf = malloc(KUE_BUFSZ, M_USBDEV, M_NOWAIT);
+ if (c->kue_buf == NULL)
+ return(ENOBUFS);
+ }
+
+ return(0);
+}
+
+Static void
+kue_rxstart(struct ifnet *ifp)
+{
+ struct kue_softc *sc;
+ struct kue_chain *c;
+
+ sc = ifp->if_softc;
+ KUE_LOCK(sc);
+ c = &sc->kue_cdata.kue_rx_chain[sc->kue_cdata.kue_rx_prod];
+
+ if (kue_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->kue_xfer, sc->kue_ep[KUE_ENDPT_RX],
+ c, mtod(c->kue_mbuf, char *), KUE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, kue_rxeof);
+ usbd_transfer(c->kue_xfer);
+
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+Static void kue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct kue_softc *sc;
+ struct kue_chain *c;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ u_int16_t len;
+
+ c = priv;
+ sc = c->kue_sc;
+ KUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->kue_rx_notice))
+ printf("kue%d: usb error on rx: %s\n", sc->kue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->kue_ep[KUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+ m = c->kue_mbuf;
+ if (total_len <= 1)
+ goto done;
+
+ len = *mtod(m, u_int16_t *);
+ m_adj(m, sizeof(u_int16_t));
+
+ /* No errors; receive the packet. */
+ total_len = len;
+
+ if (len < sizeof(struct ether_header)) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (struct ifnet *)&sc->kue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+ KUE_UNLOCK(sc);
+
+ return;
+done:
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->kue_xfer, sc->kue_ep[KUE_ENDPT_RX],
+ c, mtod(c->kue_mbuf, char *), KUE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, kue_rxeof);
+ usbd_transfer(c->kue_xfer);
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+Static void
+kue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct kue_softc *sc;
+ struct kue_chain *c;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ c = priv;
+ sc = c->kue_sc;
+ KUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+ printf("kue%d: usb error on tx: %s\n", sc->kue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->kue_ep[KUE_ENDPT_TX]);
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ usbd_get_xfer_status(c->kue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->kue_mbuf != NULL) {
+ c->kue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->kue_mbuf);
+ c->kue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+Static int
+kue_encap(struct kue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct kue_chain *c;
+ usbd_status err;
+
+ c = &sc->kue_cdata.kue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer, leaving two
+ * bytes at the beginning to hold the frame length.
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->kue_buf + 2);
+ c->kue_mbuf = m;
+
+ total_len = m->m_pkthdr.len + 2;
+ total_len += 64 - (total_len % 64);
+
+ /* Frame length is specified in the first 2 bytes of the buffer. */
+ c->kue_buf[0] = (u_int8_t)m->m_pkthdr.len;
+ c->kue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8);
+
+ usbd_setup_xfer(c->kue_xfer, sc->kue_ep[KUE_ENDPT_TX],
+ c, c->kue_buf, total_len, 0, 10000, kue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->kue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ kue_stop(sc);
+ return(EIO);
+ }
+
+ sc->kue_cdata.kue_tx_cnt++;
+
+ return(0);
+}
+
+Static void
+kue_start(struct ifnet *ifp)
+{
+ struct kue_softc *sc;
+ struct mbuf *m_head = NULL;
+
+ sc = ifp->if_softc;
+ KUE_LOCK(sc);
+
+ if (ifp->if_flags & IFF_OACTIVE) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ if (kue_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+Static void
+kue_init(void *xsc)
+{
+ struct kue_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct kue_chain *c;
+ usbd_status err;
+ int i;
+
+ KUE_LOCK(sc);
+
+ if (ifp->if_flags & IFF_RUNNING) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Set MAC address */
+ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC,
+ 0, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ sc->kue_rxfilt = KUE_RXFILT_UNICAST|KUE_RXFILT_BROADCAST;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ sc->kue_rxfilt |= KUE_RXFILT_PROMISC;
+
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);
+
+ /* I'm not sure how to tune these. */
+#ifdef notdef
+ /*
+ * Leave this one alone for now; setting it
+ * wrong causes lockups on some machines/controllers.
+ */
+ kue_setword(sc, KUE_CMD_SET_SOFS, 1);
+#endif
+ kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64);
+
+ /* Init TX ring. */
+ if (kue_tx_list_init(sc) == ENOBUFS) {
+ printf("kue%d: tx list init failed\n", sc->kue_unit);
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (kue_rx_list_init(sc) == ENOBUFS) {
+ printf("kue%d: rx list init failed\n", sc->kue_unit);
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Load the multicast filter. */
+ kue_setmulti(sc);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->kue_iface, sc->kue_ed[KUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->kue_ep[KUE_ENDPT_RX]);
+ if (err) {
+ printf("kue%d: open rx pipe failed: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ err = usbd_open_pipe(sc->kue_iface, sc->kue_ed[KUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->kue_ep[KUE_ENDPT_TX]);
+ if (err) {
+ printf("kue%d: open tx pipe failed: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < KUE_RX_LIST_CNT; i++) {
+ c = &sc->kue_cdata.kue_rx_chain[i];
+ usbd_setup_xfer(c->kue_xfer, sc->kue_ep[KUE_ENDPT_RX],
+ c, mtod(c->kue_mbuf, char *), KUE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, kue_rxeof);
+ usbd_transfer(c->kue_xfer);
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+Static int
+kue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct kue_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ KUE_LOCK(sc);
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->kue_if_flags & IFF_PROMISC)) {
+ sc->kue_rxfilt |= KUE_RXFILT_PROMISC;
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER,
+ sc->kue_rxfilt);
+ } else if (ifp->if_flags & IFF_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->kue_if_flags & IFF_PROMISC) {
+ sc->kue_rxfilt &= ~KUE_RXFILT_PROMISC;
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER,
+ sc->kue_rxfilt);
+ } else if (!(ifp->if_flags & IFF_RUNNING))
+ kue_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ kue_stop(sc);
+ }
+ sc->kue_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ kue_setmulti(sc);
+ error = 0;
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ KUE_UNLOCK(sc);
+
+ return(error);
+}
+
+Static void
+kue_watchdog(struct ifnet *ifp)
+{
+ struct kue_softc *sc;
+ struct kue_chain *c;
+ usbd_status stat;
+
+ sc = ifp->if_softc;
+ KUE_LOCK(sc);
+ ifp->if_oerrors++;
+ printf("kue%d: watchdog timeout\n", sc->kue_unit);
+
+ c = &sc->kue_cdata.kue_tx_chain[0];
+ usbd_get_xfer_status(c->kue_xfer, NULL, NULL, NULL, &stat);
+ kue_txeof(c->kue_xfer, c, stat);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ kue_start(ifp);
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+Static void
+kue_stop(struct kue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+ int i;
+
+ KUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ /* Stop transfers. */
+ if (sc->kue_ep[KUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_RX]);
+ if (err) {
+ printf("kue%d: abort rx pipe failed: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_RX]);
+ if (err) {
+ printf("kue%d: close rx pipe failed: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ }
+ sc->kue_ep[KUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->kue_ep[KUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_TX]);
+ if (err) {
+ printf("kue%d: abort tx pipe failed: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_TX]);
+ if (err) {
+ printf("kue%d: close tx pipe failed: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ }
+ sc->kue_ep[KUE_ENDPT_TX] = NULL;
+ }
+
+ if (sc->kue_ep[KUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_INTR]);
+ if (err) {
+ printf("kue%d: abort intr pipe failed: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_INTR]);
+ if (err) {
+ printf("kue%d: close intr pipe failed: %s\n",
+ sc->kue_unit, usbd_errstr(err));
+ }
+ sc->kue_ep[KUE_ENDPT_INTR] = NULL;
+ }
+
+ /* Free RX resources. */
+ for (i = 0; i < KUE_RX_LIST_CNT; i++) {
+ if (sc->kue_cdata.kue_rx_chain[i].kue_buf != NULL) {
+ free(sc->kue_cdata.kue_rx_chain[i].kue_buf, M_USBDEV);
+ sc->kue_cdata.kue_rx_chain[i].kue_buf = NULL;
+ }
+ if (sc->kue_cdata.kue_rx_chain[i].kue_mbuf != NULL) {
+ m_freem(sc->kue_cdata.kue_rx_chain[i].kue_mbuf);
+ sc->kue_cdata.kue_rx_chain[i].kue_mbuf = NULL;
+ }
+ if (sc->kue_cdata.kue_rx_chain[i].kue_xfer != NULL) {
+ usbd_free_xfer(sc->kue_cdata.kue_rx_chain[i].kue_xfer);
+ sc->kue_cdata.kue_rx_chain[i].kue_xfer = NULL;
+ }
+ }
+
+ /* Free TX resources. */
+ for (i = 0; i < KUE_TX_LIST_CNT; i++) {
+ if (sc->kue_cdata.kue_tx_chain[i].kue_buf != NULL) {
+ free(sc->kue_cdata.kue_tx_chain[i].kue_buf, M_USBDEV);
+ sc->kue_cdata.kue_tx_chain[i].kue_buf = NULL;
+ }
+ if (sc->kue_cdata.kue_tx_chain[i].kue_mbuf != NULL) {
+ m_freem(sc->kue_cdata.kue_tx_chain[i].kue_mbuf);
+ sc->kue_cdata.kue_tx_chain[i].kue_mbuf = NULL;
+ }
+ if (sc->kue_cdata.kue_tx_chain[i].kue_xfer != NULL) {
+ usbd_free_xfer(sc->kue_cdata.kue_tx_chain[i].kue_xfer);
+ sc->kue_cdata.kue_tx_chain[i].kue_xfer = NULL;
+ }
+ }
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+Static void
+kue_shutdown(device_ptr_t dev)
+{
+ struct kue_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ kue_stop(sc);
+
+ return;
+}
diff --git a/sys/dev/usb/if_kuereg.h b/sys/dev/usb/if_kuereg.h
new file mode 100644
index 0000000..4fa8295
--- /dev/null
+++ b/sys/dev/usb/if_kuereg.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for the KLSI KL5KUSB101B USB to ethernet controller.
+ * The KLSI part is controlled via vendor control requests, the structure
+ * of which depend a bit on the firmware running on the internal
+ * microcontroller. The one exception is the 'send scan data' command,
+ * which is used to load the firmware.
+ */
+
+#define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00
+#define KUE_CMD_SET_MCAST_FILTERS 0x01
+#define KUE_CMD_SET_PKT_FILTER 0x02
+#define KUE_CMD_GET_ETHERSTATS 0x03
+#define KUE_CMD_GET_GPIO 0x04
+#define KUE_CMD_SET_GPIO 0x05
+#define KUE_CMD_SET_MAC 0x06
+#define KUE_CMD_GET_MAC 0x07
+#define KUE_CMD_SET_URB_SIZE 0x08
+#define KUE_CMD_SET_SOFS 0x09
+#define KUE_CMD_SET_EVEN_PKTS 0x0A
+#define KUE_CMD_SEND_SCAN 0xFF
+
+struct kue_ether_desc {
+ u_int8_t kue_len;
+ u_int8_t kue_rsvd0;
+ u_int8_t kue_rsvd1;
+ u_int8_t kue_macaddr[ETHER_ADDR_LEN];
+ u_int8_t kue_etherstats[4];
+ u_int8_t kue_maxseg[2];
+ u_int8_t kue_mcastfilt[2];
+ u_int8_t kue_rsvd2;
+};
+
+#define KUE_ETHERSTATS(x) \
+ (*(u_int32_t *)&(x)->kue_desc.kue_etherstats)
+#define KUE_MAXSEG(x) \
+ (*(u_int16_t *)&(x)->kue_desc.kue_maxseg)
+#define KUE_MCFILTCNT(x) \
+ ((*(u_int16_t *)&(x)->kue_desc.kue_mcastfilt) & 0x7FFF)
+#define KUE_MCFILT(x, y) \
+ (char *)&(sc->kue_mcfilters[y * ETHER_ADDR_LEN])
+
+#define KUE_STAT_TX_OK 0x00000001
+#define KUE_STAT_RX_OK 0x00000002
+#define KUE_STAT_TX_ERR 0x00000004
+#define KUE_STAT_RX_ERR 0x00000008
+#define KUE_STAT_RX_NOBUF 0x00000010
+#define KUE_STAT_TX_UCAST_BYTES 0x00000020
+#define KUE_STAT_TX_UCAST_FRAMES 0x00000040
+#define KUE_STAT_TX_MCAST_BYTES 0x00000080
+#define KUE_STAT_TX_MCAST_FRAMES 0x00000100
+#define KUE_STAT_TX_BCAST_BYTES 0x00000200
+#define KUE_STAT_TX_BCAST_FRAMES 0x00000400
+#define KUE_STAT_RX_UCAST_BYTES 0x00000800
+#define KUE_STAT_RX_UCAST_FRAMES 0x00001000
+#define KUE_STAT_RX_MCAST_BYTES 0x00002000
+#define KUE_STAT_RX_MCAST_FRAMES 0x00004000
+#define KUE_STAT_RX_BCAST_BYTES 0x00008000
+#define KUE_STAT_RX_BCAST_FRAMES 0x00010000
+#define KUE_STAT_RX_CRCERR 0x00020000
+#define KUE_STAT_TX_QUEUE_LENGTH 0x00040000
+#define KUE_STAT_RX_ALIGNERR 0x00080000
+#define KUE_STAT_TX_SINGLECOLL 0x00100000
+#define KUE_STAT_TX_MULTICOLL 0x00200000
+#define KUE_STAT_TX_DEFERRED 0x00400000
+#define KUE_STAT_TX_MAXCOLLS 0x00800000
+#define KUE_STAT_RX_OVERRUN 0x01000000
+#define KUE_STAT_TX_UNDERRUN 0x02000000
+#define KUE_STAT_TX_SQE_ERR 0x04000000
+#define KUE_STAT_TX_CARRLOSS 0x08000000
+#define KUE_STAT_RX_LATECOLL 0x10000000
+
+#define KUE_RXFILT_PROMISC 0x0001
+#define KUE_RXFILT_ALLMULTI 0x0002
+#define KUE_RXFILT_UNICAST 0x0004
+#define KUE_RXFILT_BROADCAST 0x0008
+#define KUE_RXFILT_MULTICAST 0x0010
+
+#define KUE_TIMEOUT 1000
+#define KUE_BUFSZ 1536
+#define KUE_MIN_FRAMELEN 60
+
+#define KUE_RX_LIST_CNT 1
+#define KUE_TX_LIST_CNT 1
+
+#define KUE_CTL_READ 0x01
+#define KUE_CTL_WRITE 0x02
+
+#define KUE_CONFIG_NO 1
+#define KUE_IFACE_IDX 0
+
+/*
+ * The interrupt endpoint is currently unused
+ * by the KLSI part.
+ */
+#define KUE_ENDPT_RX 0x0
+#define KUE_ENDPT_TX 0x1
+#define KUE_ENDPT_INTR 0x2
+#define KUE_ENDPT_MAX 0x3
+
+struct kue_type {
+ u_int16_t kue_vid;
+ u_int16_t kue_did;
+};
+
+struct kue_softc;
+
+struct kue_chain {
+ struct kue_softc *kue_sc;
+ usbd_xfer_handle kue_xfer;
+ char *kue_buf;
+ struct mbuf *kue_mbuf;
+ int kue_idx;
+};
+
+struct kue_cdata {
+ struct kue_chain kue_tx_chain[KUE_TX_LIST_CNT];
+ struct kue_chain kue_rx_chain[KUE_RX_LIST_CNT];
+ int kue_tx_prod;
+ int kue_tx_cons;
+ int kue_tx_cnt;
+ int kue_rx_prod;
+};
+
+#define KUE_INC(x, y) (x) = (x + 1) % y
+
+struct kue_softc {
+ struct arpcom arpcom;
+ usbd_device_handle kue_udev;
+ usbd_interface_handle kue_iface;
+ struct kue_ether_desc kue_desc;
+ int kue_ed[KUE_ENDPT_MAX];
+ usbd_pipe_handle kue_ep[KUE_ENDPT_MAX];
+ int kue_unit;
+ int kue_if_flags;
+ u_int16_t kue_rxfilt;
+ u_int8_t *kue_mcfilters;
+ struct kue_cdata kue_cdata;
+#if __FreeBSD_version >= 500000
+ struct mtx kue_mtx;
+#endif
+ char kue_dying;
+ struct timeval kue_rx_notice;
+ struct usb_qdat kue_qdat;
+};
+
+#if 0
+#define KUE_LOCK(_sc) mtx_lock(&(_sc)->kue_mtx)
+#define KUE_UNLOCK(_sc) mtx_unlock(&(_sc)->kue_mtx)
+#else
+#define KUE_LOCK(_sc)
+#define KUE_UNLOCK(_sc)
+#endif
diff --git a/sys/dev/usb/if_rue.c b/sys/dev/usb/if_rue.c
new file mode 100644
index 0000000..cc1f496
--- /dev/null
+++ b/sys/dev/usb/if_rue.c
@@ -0,0 +1,1497 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RealTek RTL8150 USB to fast ethernet controller driver.
+ * Datasheet is available from
+ * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+#if __FreeBSD_version < 500000
+#include <machine/clock.h>
+#endif
+
+#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_ethersubr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/if_ruereg.h>
+
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+#ifdef USB_DEBUG
+Static int ruedebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue");
+SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RW,
+ &ruedebug, 0, "rue debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (ruedebug > (n)) \
+ logprintf x; \
+ } while (0);
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+/*
+ * Various supported device vendors/products.
+ */
+
+Static struct rue_type rue_devs[] = {
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX },
+ { USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100 },
+ { 0, 0 }
+};
+
+Static int rue_match(device_ptr_t);
+Static int rue_attach(device_ptr_t);
+Static int rue_detach(device_ptr_t);
+
+Static int rue_tx_list_init(struct rue_softc *);
+Static int rue_rx_list_init(struct rue_softc *);
+Static int rue_newbuf(struct rue_softc *, struct rue_chain *, struct mbuf *);
+Static int rue_encap(struct rue_softc *, struct mbuf *, int);
+#ifdef RUE_INTR_PIPE
+Static void rue_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+#endif
+Static void rue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void rue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void rue_tick(void *);
+Static void rue_rxstart(struct ifnet *);
+Static void rue_start(struct ifnet *);
+Static int rue_ioctl(struct ifnet *, u_long, caddr_t);
+Static void rue_init(void *);
+Static void rue_stop(struct rue_softc *);
+Static void rue_watchdog(struct ifnet *);
+Static void rue_shutdown(device_ptr_t);
+Static int rue_ifmedia_upd(struct ifnet *);
+Static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+Static int rue_miibus_readreg(device_ptr_t, int, int);
+Static int rue_miibus_writereg(device_ptr_t, int, int, int);
+Static void rue_miibus_statchg(device_ptr_t);
+
+Static void rue_setmulti(struct rue_softc *);
+Static void rue_reset(struct rue_softc *);
+
+Static int rue_read_mem(struct rue_softc *, u_int16_t, void *, u_int16_t);
+Static int rue_write_mem(struct rue_softc *, u_int16_t, void *, u_int16_t);
+Static int rue_csr_read_1(struct rue_softc *, int);
+Static int rue_csr_write_1(struct rue_softc *, int, u_int8_t);
+Static int rue_csr_read_2(struct rue_softc *, int);
+Static int rue_csr_write_2(struct rue_softc *, int, u_int16_t);
+Static int rue_csr_write_4(struct rue_softc *, int, u_int32_t);
+
+Static device_method_t rue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rue_match),
+ DEVMETHOD(device_attach, rue_attach),
+ DEVMETHOD(device_detach, rue_detach),
+ DEVMETHOD(device_shutdown, rue_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, rue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, rue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, rue_miibus_statchg),
+
+ { 0, 0 }
+};
+
+Static driver_t rue_driver = {
+ "rue",
+ rue_methods,
+ sizeof(struct rue_softc)
+};
+
+Static devclass_t rue_devclass;
+
+DRIVER_MODULE(rue, uhub, rue_driver, rue_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(rue, usb, 1, 1, 1);
+MODULE_DEPEND(rue, ether, 1, 1, 1);
+MODULE_DEPEND(rue, miibus, 1, 1, 1);
+
+#define RUE_SETBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x))
+
+#define RUE_CLRBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x))
+
+#define RUE_SETBIT_2(sc, reg, x) \
+ rue_csr_write_2(sc, reg, rue_csr_read_2(sc, reg) | (x))
+
+#define RUE_CLRBIT_2(sc, reg, x) \
+ rue_csr_write_2(sc, reg, rue_csr_read_2(sc, reg) & ~(x))
+
+Static int
+rue_read_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->rue_dying)
+ return (0);
+
+ RUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ err = usbd_do_request(sc->rue_udev, &req, buf);
+
+ RUE_UNLOCK(sc);
+
+ if (err) {
+ printf("rue%d: control pipe read failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ return (-1);
+ }
+
+ return (0);
+}
+
+Static int
+rue_write_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->rue_dying)
+ return (0);
+
+ RUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ err = usbd_do_request(sc->rue_udev, &req, buf);
+
+ RUE_UNLOCK(sc);
+
+ if (err) {
+ printf("rue%d: control pipe write failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ return (-1);
+ }
+
+ return (0);
+}
+
+Static int
+rue_csr_read_1(struct rue_softc *sc, int reg)
+{
+ int err;
+ u_int8_t val = 0;
+
+ err = rue_read_mem(sc, reg, &val, 1);
+
+ if (err)
+ return (0);
+
+ return (val);
+}
+
+Static int
+rue_csr_read_2(struct rue_softc *sc, int reg)
+{
+ int err;
+ u_int16_t val = 0;
+ uWord w;
+
+ USETW(w, val);
+ err = rue_read_mem(sc, reg, &w, 2);
+ val = UGETW(w);
+
+ if (err)
+ return (0);
+
+ return (val);
+}
+
+Static int
+rue_csr_write_1(struct rue_softc *sc, int reg, u_int8_t val)
+{
+ int err;
+
+ err = rue_write_mem(sc, reg, &val, 1);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+Static int
+rue_csr_write_2(struct rue_softc *sc, int reg, u_int16_t val)
+{
+ int err;
+ uWord w;
+
+ USETW(w, val);
+ err = rue_write_mem(sc, reg, &w, 2);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+Static int
+rue_csr_write_4(struct rue_softc *sc, int reg, u_int32_t val)
+{
+ int err;
+ uDWord dw;
+
+ USETDW(dw, val);
+ err = rue_write_mem(sc, reg, &dw, 4);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+Static int
+rue_miibus_readreg(device_ptr_t dev, int phy, int reg)
+{
+ struct rue_softc *sc = USBGETSOFTC(dev);
+ int rval;
+ int ruereg;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ return (0);
+ break;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rval = rue_csr_read_1(sc, reg);
+ return (rval);
+ }
+ printf("rue%d: bad phy register\n", sc->rue_unit);
+ return (0);
+ }
+
+ rval = rue_csr_read_2(sc, ruereg);
+
+ return (rval);
+}
+
+Static int
+rue_miibus_writereg(device_ptr_t dev, int phy, int reg, int data)
+{
+ struct rue_softc *sc = USBGETSOFTC(dev);
+ int ruereg;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ return (0);
+ break;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rue_csr_write_1(sc, reg, data);
+ return (0);
+ }
+ printf("rue%d: bad phy register\n", sc->rue_unit);
+ return (0);
+ }
+ rue_csr_write_2(sc, ruereg, data);
+
+ return (0);
+}
+
+Static void
+rue_miibus_statchg(device_ptr_t dev)
+{
+ struct rue_softc *sc = USBGETSOFTC(dev);
+ struct mii_data *mii = GET_MII(sc);
+ int bmcr;
+
+ RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+
+ bmcr = rue_csr_read_2(sc, RUE_BMCR);
+
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+ bmcr |= RUE_BMCR_SPD_SET;
+ else
+ bmcr &= ~RUE_BMCR_SPD_SET;
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ bmcr |= RUE_BMCR_DUPLEX;
+ else
+ bmcr &= ~RUE_BMCR_DUPLEX;
+
+ rue_csr_write_2(sc, RUE_BMCR, bmcr);
+
+ RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+}
+
+/*
+ * Program the 64-bit multicast hash filter.
+ */
+
+Static void
+rue_setmulti(struct rue_softc *sc)
+{
+ struct ifnet *ifp;
+ int h = 0;
+ u_int32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+ u_int32_t rxcfg;
+ int mcnt = 0;
+
+ ifp = &sc->arpcom.ac_if;
+
+ rxcfg = rue_csr_read_2(sc, RUE_RCR);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP);
+ rxcfg &= ~RUE_RCR_AM;
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF);
+ rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ rue_csr_write_4(sc, RUE_MAR0, 0);
+ rue_csr_write_4(sc, RUE_MAR4, 0);
+
+ /* now program new ones */
+#if __FreeBSD_version >= 500000
+ TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link)
+#else
+ LIST_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link)
+#endif
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ mcnt++;
+ }
+
+ if (mcnt)
+ rxcfg |= RUE_RCR_AM;
+ else
+ rxcfg &= ~RUE_RCR_AM;
+
+ rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP);
+
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, hashes[0]);
+ rue_csr_write_4(sc, RUE_MAR4, hashes[1]);
+}
+
+Static void
+rue_reset(struct rue_softc *sc)
+{
+ int i;
+
+ rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST);
+
+ for (i = 0; i < RUE_TIMEOUT; i++) {
+ DELAY(500);
+ if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST))
+ break;
+ }
+ if (i == RUE_TIMEOUT)
+ printf("rue%d: reset never completed!\n", sc->rue_unit);
+
+ DELAY(10000);
+}
+
+/*
+ * Probe for a RTL8150 chip.
+ */
+
+USB_MATCH(rue)
+{
+ USB_MATCH_START(rue, uaa);
+ struct rue_type *t;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+
+ t = rue_devs;
+ while (t->rue_vid) {
+ if (uaa->vendor == t->rue_vid &&
+ uaa->product == t->rue_did) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ t++;
+ }
+
+ return (UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+
+USB_ATTACH(rue)
+{
+ USB_ATTACH_START(rue, sc, uaa);
+ char *devinfo;
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usbd_interface_handle iface;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+ struct rue_type *t;
+
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+
+ bzero(sc, sizeof (struct rue_softc));
+ usbd_devinfo(uaa->device, 0, devinfo);
+
+ sc->rue_udev = uaa->device;
+ sc->rue_unit = device_get_unit(self);
+
+ if (usbd_set_config_no(sc->rue_udev, RUE_CONFIG_NO, 0)) {
+ printf("rue%d: getting interface handle failed\n",
+ sc->rue_unit);
+ goto error;
+ }
+
+ err = usbd_device2interface_handle(uaa->device, RUE_IFACE_IDX, &iface);
+ if (err) {
+ printf("rue%d: getting interface handle failed\n",
+ sc->rue_unit);
+ goto error;
+ }
+
+ sc->rue_iface = iface;
+
+ t = rue_devs;
+ while (t->rue_vid) {
+ if (uaa->vendor == t->rue_vid &&
+ uaa->product == t->rue_did) {
+ sc->rue_info = t;
+ break;
+ }
+ t++;
+ }
+
+ id = usbd_get_interface_descriptor(sc->rue_iface);
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ device_set_desc_copy(self, devinfo);
+ printf("%s: %s\n", USBDEVNAME(self), devinfo);
+
+ /* Find endpoints */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ printf("rue%d: couldn't get ep %d\n", sc->rue_unit, i);
+ goto error;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->rue_ed[RUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->rue_ed[RUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->rue_ed[RUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+#if __FreeBSD_version >= 500000
+ mtx_init(&sc->rue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+#endif
+ RUE_LOCK(sc);
+
+ /* Reset the adapter */
+ rue_reset(sc);
+
+ /* Get station address from the EEPROM */
+ err = rue_read_mem(sc, RUE_EEPROM_IDR0,
+ (caddr_t)&eaddr, ETHER_ADDR_LEN);
+ if (err) {
+ printf("rue%d: couldn't get station address\n", sc->rue_unit);
+ goto error1;
+ }
+
+ bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ if_initname(ifp, "rue", sc->rue_unit);
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = rue_ioctl;
+ ifp->if_start = rue_start;
+ ifp->if_watchdog = rue_watchdog;
+ ifp->if_init = rue_init;
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ /* MII setup */
+ if (mii_phy_probe(self, &sc->rue_miibus,
+ rue_ifmedia_upd, rue_ifmedia_sts)) {
+ printf("rue%d: MII without any PHY!\n", sc->rue_unit);
+ goto error1;
+ }
+
+ sc->rue_qdat.ifp = ifp;
+ sc->rue_qdat.if_rxstart = rue_rxstart;
+
+ /* Call MI attach routine */
+#if __FreeBSD_version >= 500000
+ ether_ifattach(ifp, eaddr);
+#else
+ ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
+#endif
+ callout_handle_init(&sc->rue_stat_ch);
+ usb_register_netisr();
+ sc->rue_dying = 0;
+
+ RUE_UNLOCK(sc);
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_SUCCESS_RETURN;
+
+ error1:
+ RUE_UNLOCK(sc);
+#if __FreeBSD_version >= 500000
+ mtx_destroy(&sc->rue_mtx);
+#endif
+ error:
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+Static int
+rue_detach(device_ptr_t dev)
+{
+ struct rue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ RUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ sc->rue_dying = 1;
+ untimeout(rue_tick, sc, sc->rue_stat_ch);
+#if __FreeBSD_version >= 500000
+ ether_ifdetach(ifp);
+#else
+ ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
+#endif
+
+ if (sc->rue_ep[RUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (sc->rue_ep[RUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+#ifdef RUE_INTR_PIPE
+ if (sc->rue_ep[RUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+#endif
+
+ RUE_UNLOCK(sc);
+#if __FreeBSD_version >= 500000
+ mtx_destroy(&sc->rue_mtx);
+#endif
+
+ return (0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+
+Static int
+rue_newbuf(struct rue_softc *sc, struct rue_chain *c, struct mbuf *m)
+{
+ struct mbuf *m_new = NULL;
+
+ if (m == NULL) {
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL) {
+ printf("rue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->rue_unit);
+ return (ENOBUFS);
+ }
+
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ printf("rue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->rue_unit);
+ m_freem(m_new);
+ return (ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ m_adj(m_new, ETHER_ALIGN);
+ c->rue_mbuf = m_new;
+
+ return (0);
+}
+
+Static int
+rue_rx_list_init(struct rue_softc *sc)
+{
+ struct rue_cdata *cd;
+ struct rue_chain *c;
+ int i;
+
+ cd = &sc->rue_cdata;
+ for (i = 0; i < RUE_RX_LIST_CNT; i++) {
+ c = &cd->rue_rx_chain[i];
+ c->rue_sc = sc;
+ c->rue_idx = i;
+ if (rue_newbuf(sc, c, NULL) == ENOBUFS)
+ return (ENOBUFS);
+ if (c->rue_xfer == NULL) {
+ c->rue_xfer = usbd_alloc_xfer(sc->rue_udev);
+ if (c->rue_xfer == NULL)
+ return (ENOBUFS);
+ }
+ }
+
+ return (0);
+}
+
+Static int
+rue_tx_list_init(struct rue_softc *sc)
+{
+ struct rue_cdata *cd;
+ struct rue_chain *c;
+ int i;
+
+ cd = &sc->rue_cdata;
+ for (i = 0; i < RUE_TX_LIST_CNT; i++) {
+ c = &cd->rue_tx_chain[i];
+ c->rue_sc = sc;
+ c->rue_idx = i;
+ c->rue_mbuf = NULL;
+ if (c->rue_xfer == NULL) {
+ c->rue_xfer = usbd_alloc_xfer(sc->rue_udev);
+ if (c->rue_xfer == NULL)
+ return (ENOBUFS);
+ }
+ c->rue_buf = malloc(RUE_BUFSZ, M_USBDEV, M_NOWAIT);
+ if (c->rue_buf == NULL)
+ return (ENOBUFS);
+ }
+
+ return (0);
+}
+
+#ifdef RUE_INTR_PIPE
+Static void
+rue_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rue_softc *sc = priv;
+ struct ifnet *ifp;
+ struct rue_intrpkt *p;
+
+ RUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ printf("rue%d: usb error on intr: %s\n", sc->rue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_INTR]);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, (void **)&p, NULL, NULL);
+
+ ifp->if_ierrors += p->rue_rxlost_cnt;
+ ifp->if_ierrors += p->rue_crcerr_cnt;
+ ifp->if_collisions += p->rue_col_cnt;
+
+ RUE_UNLOCK(sc);
+}
+#endif
+
+Static void
+rue_rxstart(struct ifnet *ifp)
+{
+ struct rue_softc *sc;
+ struct rue_chain *c;
+
+ sc = ifp->if_softc;
+ RUE_LOCK(sc);
+ c = &sc->rue_cdata.rue_rx_chain[sc->rue_cdata.rue_rx_prod];
+
+ if (rue_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->rue_xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->rue_mbuf, char *), RUE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(c->rue_xfer);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+
+Static void
+rue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rue_chain *c = priv;
+ struct rue_softc *sc = c->rue_sc;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ struct rue_rxpkt r;
+
+ if (sc->rue_dying)
+ return;
+ RUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->rue_rx_notice))
+ printf("rue%d: usb error on rx: %s\n", sc->rue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ if (total_len <= ETHER_CRC_LEN) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ m = c->rue_mbuf;
+ bcopy(mtod(m, char *) + total_len - 4, (char *)&r, sizeof (r));
+
+ /* Check recieve packet was valid or not */
+ if ((r.rue_rxstat & RUE_RXSTAT_VALID) == 0) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ /* No errors; receive the packet. */
+ total_len -= ETHER_CRC_LEN;
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (struct ifnet *)&sc->rue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+
+ RUE_UNLOCK(sc);
+ return;
+
+ done:
+ /* Setup new transfer. */
+ usbd_setup_xfer(xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->rue_mbuf, char *), RUE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(xfer);
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+Static void
+rue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rue_chain *c = priv;
+ struct rue_softc *sc = c->rue_sc;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ RUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ printf("rue%d: usb error on tx: %s\n", sc->rue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_TX]);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ usbd_get_xfer_status(c->rue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->rue_mbuf != NULL) {
+ c->rue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->rue_mbuf);
+ c->rue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ RUE_UNLOCK(sc);
+}
+
+Static void
+rue_tick(void *xsc)
+{
+ struct rue_softc *sc = xsc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+
+ if (sc == NULL)
+ return;
+
+ RUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ mii = GET_MII(sc);
+ if (mii == NULL) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ mii_tick(mii);
+ if (!sc->rue_link && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->rue_link++;
+ if (ifp->if_snd.ifq_head != NULL)
+ rue_start(ifp);
+ }
+
+ sc->rue_stat_ch = timeout(rue_tick, sc, hz);
+
+ RUE_UNLOCK(sc);
+}
+
+Static int
+rue_encap(struct rue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct rue_chain *c;
+ usbd_status err;
+
+ c = &sc->rue_cdata.rue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->rue_buf);
+ c->rue_mbuf = m;
+
+ total_len = m->m_pkthdr.len;
+
+ /*
+ * This is an undocumented behavior.
+ * RTL8150 chip doesn't send frame length smaller than
+ * RUE_MIN_FRAMELEN (60) byte packet.
+ */
+ if (total_len < RUE_MIN_FRAMELEN)
+ total_len = RUE_MIN_FRAMELEN;
+
+ usbd_setup_xfer(c->rue_xfer, sc->rue_ep[RUE_ENDPT_TX],
+ c, c->rue_buf, total_len, USBD_FORCE_SHORT_XFER,
+ 10000, rue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->rue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ rue_stop(sc);
+ return (EIO);
+ }
+
+ sc->rue_cdata.rue_tx_cnt++;
+
+ return (0);
+}
+
+Static void
+rue_start(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mbuf *m_head = NULL;
+
+ RUE_LOCK(sc);
+
+ if (!sc->rue_link) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (ifp->if_flags & IFF_OACTIVE) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (rue_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+
+ RUE_UNLOCK(sc);
+}
+
+Static void
+rue_init(void *xsc)
+{
+ struct rue_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mii_data *mii = GET_MII(sc);
+ struct rue_chain *c;
+ usbd_status err;
+ int i;
+ int rxcfg;
+
+ RUE_LOCK(sc);
+
+ if (ifp->if_flags & IFF_RUNNING) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ rue_reset(sc);
+
+ /* Set MAC address */
+ rue_write_mem(sc, RUE_IDR0, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ /* Init TX ring. */
+ if (rue_tx_list_init(sc) == ENOBUFS) {
+ printf("rue%d: tx list init failed\n", sc->rue_unit);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (rue_rx_list_init(sc) == ENOBUFS) {
+ printf("rue%d: rx list init failed\n", sc->rue_unit);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef RUE_INTR_PIPE
+ sc->rue_cdata.rue_ibuf = malloc(RUE_INTR_PKTLEN, M_USBDEV, M_NOWAIT);
+#endif
+
+ /*
+ * Set the initial TX and RX configuration.
+ */
+ rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG);
+
+ rxcfg = RUE_RCR_CONFIG;
+
+ /* Set capture broadcast bit to capture broadcast frames. */
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxcfg |= RUE_RCR_AB;
+ else
+ rxcfg &= ~RUE_RCR_AB;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ rxcfg |= RUE_RCR_AAP;
+ else
+ rxcfg &= ~RUE_RCR_AAP;
+
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+
+ /* Load the multicast filter. */
+ rue_setmulti(sc);
+
+ /* Enable RX and TX */
+ rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN));
+
+ mii_mediachg(mii);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->rue_iface, sc->rue_ed[RUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ printf("rue%d: open rx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+ err = usbd_open_pipe(sc->rue_iface, sc->rue_ed[RUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ printf("rue%d: open tx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef RUE_INTR_PIPE
+ err = usbd_open_pipe_intr(sc->rue_iface, sc->rue_ed[RUE_ENDPT_INTR],
+ USBD_SHORT_XFER_OK,
+ &sc->rue_ep[RUE_ENDPT_INTR], sc,
+ sc->rue_cdata.rue_ibuf, RUE_INTR_PKTLEN,
+ rue_intr, RUE_INTR_INTERVAL);
+ if (err) {
+ printf("rue%d: open intr pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+#endif
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < RUE_RX_LIST_CNT; i++) {
+ c = &sc->rue_cdata.rue_rx_chain[i];
+ usbd_setup_xfer(c->rue_xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->rue_mbuf, char *), RUE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(c->rue_xfer);
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ sc->rue_stat_ch = timeout(rue_tick, sc, hz);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Set media options.
+ */
+
+Static int
+rue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ sc->rue_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ LIST_FOREACH (miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+
+Static void
+rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+Static int
+rue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ int error = 0;
+
+ RUE_LOCK(sc);
+
+ switch (command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->rue_if_flags & IFF_PROMISC)) {
+ RUE_SETBIT_2(sc, RUE_RCR,
+ (RUE_RCR_AAM | RUE_RCR_AAP));
+ rue_setmulti(sc);
+ } else if (ifp->if_flags & IFF_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->rue_if_flags & IFF_PROMISC) {
+ RUE_CLRBIT_2(sc, RUE_RCR,
+ (RUE_RCR_AAM | RUE_RCR_AAP));
+ rue_setmulti(sc);
+ } else if (!(ifp->if_flags & IFF_RUNNING))
+ rue_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ rue_stop(sc);
+ }
+ sc->rue_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ rue_setmulti(sc);
+ error = 0;
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ RUE_UNLOCK(sc);
+
+ return (error);
+}
+
+Static void
+rue_watchdog(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct rue_chain *c;
+ usbd_status stat;
+
+ RUE_LOCK(sc);
+
+ ifp->if_oerrors++;
+ printf("rue%d: watchdog timeout\n", sc->rue_unit);
+
+ c = &sc->rue_cdata.rue_tx_chain[0];
+ usbd_get_xfer_status(c->rue_xfer, NULL, NULL, NULL, &stat);
+ rue_txeof(c->rue_xfer, c, stat);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ rue_start(ifp);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+
+Static void
+rue_stop(struct rue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+ int i;
+
+ RUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ rue_csr_write_1(sc, RUE_CR, 0x00);
+ rue_reset(sc);
+
+ untimeout(rue_tick, sc, sc->rue_stat_ch);
+
+ /* Stop transfers. */
+ if (sc->rue_ep[RUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ printf("rue%d: abort rx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ printf("rue%d: close rx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->rue_ep[RUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ printf("rue%d: abort tx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ printf("rue%d: close tx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_TX] = NULL;
+ }
+
+#ifdef RUE_INTR_PIPE
+ if (sc->rue_ep[RUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+ if (err) {
+ printf("rue%d: abort intr pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+ if (err) {
+ printf("rue%d: close intr pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_INTR] = NULL;
+ }
+#endif
+
+ /* Free RX resources. */
+ for (i = 0; i < RUE_RX_LIST_CNT; i++) {
+ if (sc->rue_cdata.rue_rx_chain[i].rue_buf != NULL) {
+ free(sc->rue_cdata.rue_rx_chain[i].rue_buf, M_USBDEV);
+ sc->rue_cdata.rue_rx_chain[i].rue_buf = NULL;
+ }
+ if (sc->rue_cdata.rue_rx_chain[i].rue_mbuf != NULL) {
+ m_freem(sc->rue_cdata.rue_rx_chain[i].rue_mbuf);
+ sc->rue_cdata.rue_rx_chain[i].rue_mbuf = NULL;
+ }
+ if (sc->rue_cdata.rue_rx_chain[i].rue_xfer != NULL) {
+ usbd_free_xfer(sc->rue_cdata.rue_rx_chain[i].rue_xfer);
+ sc->rue_cdata.rue_rx_chain[i].rue_xfer = NULL;
+ }
+ }
+
+ /* Free TX resources. */
+ for (i = 0; i < RUE_TX_LIST_CNT; i++) {
+ if (sc->rue_cdata.rue_tx_chain[i].rue_buf != NULL) {
+ free(sc->rue_cdata.rue_tx_chain[i].rue_buf, M_USBDEV);
+ sc->rue_cdata.rue_tx_chain[i].rue_buf = NULL;
+ }
+ if (sc->rue_cdata.rue_tx_chain[i].rue_mbuf != NULL) {
+ m_freem(sc->rue_cdata.rue_tx_chain[i].rue_mbuf);
+ sc->rue_cdata.rue_tx_chain[i].rue_mbuf = NULL;
+ }
+ if (sc->rue_cdata.rue_tx_chain[i].rue_xfer != NULL) {
+ usbd_free_xfer(sc->rue_cdata.rue_tx_chain[i].rue_xfer);
+ sc->rue_cdata.rue_tx_chain[i].rue_xfer = NULL;
+ }
+ }
+
+#ifdef RUE_INTR_PIPE
+ free(sc->rue_cdata.rue_ibuf, M_USBDEV);
+ sc->rue_cdata.rue_ibuf = NULL;
+#endif
+
+ sc->rue_link = 0;
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+
+Static void
+rue_shutdown(device_ptr_t dev)
+{
+ struct rue_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->rue_dying++;
+ RUE_LOCK(sc);
+ rue_reset(sc);
+ rue_stop(sc);
+ RUE_UNLOCK(sc);
+}
diff --git a/sys/dev/usb/if_ruereg.h b/sys/dev/usb/if_ruereg.h
new file mode 100644
index 0000000..f381828
--- /dev/null
+++ b/sys/dev/usb/if_ruereg.h
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IF_RUEREG_H_
+#define _IF_RUEREG_H_
+
+#define RUE_INTR_PIPE 1 /* Use INTR PIPE */
+
+#define RUE_CONFIG_NO 1
+#define RUE_IFACE_IDX 0
+
+#define RUE_ENDPT_RX 0x0
+#define RUE_ENDPT_TX 0x1
+#define RUE_ENDPT_INTR 0x2
+#define RUE_ENDPT_MAX 0x3
+
+#define RUE_INTR_PKTLEN 0x8
+
+#define RUE_TIMEOUT 1000
+#define ETHER_ALIGN 2
+#define RUE_BUFSZ 1536
+#define RUE_MIN_FRAMELEN 60
+#define RUE_INTR_INTERVAL 100 /* ms */
+
+/*
+ * Registers
+ */
+
+#define RUE_IDR0 0x0120
+#define RUE_IDR1 0x0121
+#define RUE_IDR2 0x0122
+#define RUE_IDR3 0x0123
+#define RUE_IDR4 0x0124
+#define RUE_IDR5 0x0125
+
+#define RUE_MAR0 0x0126
+#define RUE_MAR1 0x0127
+#define RUE_MAR2 0x0128
+#define RUE_MAR3 0x0129
+#define RUE_MAR4 0x012A
+#define RUE_MAR5 0x012B
+#define RUE_MAR6 0x012C
+#define RUE_MAR7 0x012D
+
+#define RUE_CR 0x012E /* B, R/W */
+#define RUE_CR_SOFT_RST 0x10
+#define RUE_CR_RE 0x08
+#define RUE_CR_TE 0x04
+#define RUE_CR_EP3CLREN 0x02
+
+#define RUE_TCR 0x012F /* B, R/W */
+#define RUE_TCR_TXRR1 0x80
+#define RUE_TCR_TXRR0 0x40
+#define RUE_TCR_IFG1 0x10
+#define RUE_TCR_IFG0 0x08
+#define RUE_TCR_NOCRC 0x01
+#define RUE_TCR_CONFIG (RUE_TCR_TXRR1|RUE_TCR_TXRR0|RUE_TCR_IFG1|RUE_TCR_IFG0)
+
+#define RUE_RCR 0x0130 /* W, R/W */
+#define RUE_RCR_TAIL 0x80
+#define RUE_RCR_AER 0x40
+#define RUE_RCR_AR 0x20
+#define RUE_RCR_AM 0x10
+#define RUE_RCR_AB 0x08
+#define RUE_RCR_AD 0x04
+#define RUE_RCR_AAM 0x02
+#define RUE_RCR_AAP 0x01
+#define RUE_RCR_CONFIG (RUE_RCR_TAIL|RUE_RCR_AD)
+
+#define RUE_TSR 0x0132
+#define RUE_RSR 0x0133
+#define RUE_CON0 0x0135
+#define RUE_CON1 0x0136
+#define RUE_MSR 0x0137
+#define RUE_PHYADD 0x0138
+#define RUE_PHYDAT 0x0139
+
+#define RUE_PHYCNT 0x013B /* B, R/W */
+#define RUE_PHYCNT_PHYOWN 0x40
+#define RUE_PHYCNT_RWCR 0x20
+
+#define RUE_GPPC 0x013D
+#define RUE_WAKECNT 0x013E
+
+#define RUE_BMCR 0x0140
+#define RUE_BMCR_SPD_SET 0x2000
+#define RUE_BMCR_DUPLEX 0x0100
+
+#define RUE_BMSR 0x0142
+
+#define RUE_ANAR 0x0144 /* W, R/W */
+#define RUE_ANAR_PAUSE 0x0400
+
+#define RUE_ANLP 0x0146 /* W, R/O */
+#define RUE_ANLP_PAUSE 0x0400
+
+#define RUE_AER 0x0148
+
+#define RUE_NWAYT 0x014A
+#define RUE_CSCR 0x014C
+
+#define RUE_CRC0 0x014E
+#define RUE_CRC1 0x0150
+#define RUE_CRC2 0x0152
+#define RUE_CRC3 0x0154
+#define RUE_CRC4 0x0156
+
+#define RUE_BYTEMASK0 0x0158
+#define RUE_BYTEMASK1 0x0160
+#define RUE_BYTEMASK2 0x0168
+#define RUE_BYTEMASK3 0x0170
+#define RUE_BYTEMASK4 0x0178
+
+#define RUE_PHY1 0x0180
+#define RUE_PHY2 0x0184
+
+#define RUE_TW1 0x0186
+
+#define RUE_REG_MIN 0x0120
+#define RUE_REG_MAX 0x0189
+
+/*
+ * EEPROM address declarations
+ */
+
+#define RUE_EEPROM_BASE 0x1200
+
+#define RUE_EEPROM_IDR0 (RUE_EEPROM_BASE + 0x02)
+#define RUE_EEPROM_IDR1 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR2 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR3 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR4 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR5 (RUE_EEPROM_BASE + 0x03)
+
+#define RUE_EEPROM_INTERVAL (RUE_EEPROM_BASE + 0x17)
+
+struct rue_intrpkt {
+ u_int8_t rue_tsr;
+ u_int8_t rue_rsr;
+ u_int8_t rue_gep_msr;
+ u_int8_t rue_waksr;
+ u_int8_t rue_txok_cnt;
+ u_int8_t rue_rxlost_cnt;
+ u_int8_t rue_crcerr_cnt;
+ u_int8_t rue_col_cnt;
+};
+
+struct rue_rxpkt {
+ u_int16_t rue_pktlen : 12;
+ u_int16_t rue_rxstat : 4;
+};
+
+#define RUE_RXSTAT_VALID 0x01
+#define RUE_RXSTAT_RUNT 0x02
+#define RUE_RXSTAT_PMATCH 0x04
+#define RUE_RXSTAT_MCAST 0x08
+
+#define RUE_RXSTAT_MASK RUE_RXSTAT_VALID
+
+struct rue_type {
+ u_int16_t rue_vid;
+ u_int16_t rue_did;
+};
+
+#define RUE_TX_LIST_CNT 1
+#define RUE_RX_LIST_CNT 1
+
+struct rue_softc;
+
+struct rue_chain {
+ struct rue_softc *rue_sc;
+ usbd_xfer_handle rue_xfer;
+ char *rue_buf;
+ struct mbuf *rue_mbuf;
+ int rue_idx;
+};
+
+struct rue_cdata {
+ struct rue_chain rue_tx_chain[RUE_TX_LIST_CNT];
+ struct rue_chain rue_rx_chain[RUE_RX_LIST_CNT];
+ struct rue_intrpkt *rue_ibuf;
+ int rue_tx_prod;
+ int rue_tx_cons;
+ int rue_tx_cnt;
+ int rue_rx_prod;
+};
+
+struct rue_softc {
+ struct arpcom arpcom;
+ device_t rue_miibus;
+ usbd_device_handle rue_udev;
+ usbd_interface_handle rue_iface;
+ struct rue_type *rue_info;
+ int rue_ed[RUE_ENDPT_MAX];
+ usbd_pipe_handle rue_ep[RUE_ENDPT_MAX];
+ int rue_unit;
+ u_int8_t rue_link;
+ int rue_if_flags;
+ struct rue_cdata rue_cdata;
+ struct callout_handle rue_stat_ch;
+#if __FreeBSD_version >= 500000
+ struct mtx rue_mtx;
+#endif
+ char rue_dying;
+ struct timeval rue_rx_notice;
+ struct usb_qdat rue_qdat;
+};
+
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->rue_miibus))
+#elif defined(__NetBSD__)
+#define GET_MII(sc) (&(sc)->rue_mii)
+#elif defined(__OpenBSD__)
+#define GET_MII(sc) (&(sc)->rue_mii)
+#endif
+
+#if 0
+#define RUE_LOCK(_sc) mtx_lock(&(_sc)->rue_mtx)
+#define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->rue_mtx)
+#else
+#define RUE_LOCK(_sc)
+#define RUE_UNLOCK(_sc)
+#endif
+
+#endif /* _IF_RUEREG_H_ */
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
new file mode 100644
index 0000000..ffd60c5
--- /dev/null
+++ b/sys/dev/usb/if_udav.c
@@ -0,0 +1,2044 @@
+/* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */
+/* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 2003
+ * Shingo WATANABE <nabe@nabechan.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY)
+ * The spec can be found at the following url.
+ * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-F01-062202s.pdf
+ */
+
+/*
+ * TODO:
+ * Interrupt Endpoint support
+ * External PHYs
+ * powerhook() support?
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+#if defined(__NetBSD__)
+#include "opt_ns.h"
+#endif
+#if defined(__NetBSD__)
+#include "bpfilter.h"
+#endif
+#if defined(__FreeBSD__)
+#define NBPFILTER 1
+#endif
+#if defined(__NetBSD__)
+#include "rnd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#if defined(__FreeBSD__)
+#include <sys/types.h>
+#include <sys/lockmgr.h>
+#include <sys/sockio.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#endif
+
+#if NRND > 0
+#include <sys/rnd.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#if defined(__NetBSD__)
+#ifndef BPF_MTAP
+#define BPF_MTAP(_ifp, _m) do { \
+ if ((_ifp)->if_bpf)) { \
+ bpf_mtap((_ifp)->if_bpf, (_m)) ; \
+ } \
+} while (0)
+#endif
+#endif
+
+#if defined(__NetBSD__)
+#include <net/if_ether.h>
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_inarp.h>
+#endif /* INET */
+#elif defined(__FreeBSD__) /* defined(__NetBSD__) */
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif /* defined(__FreeBSD__) */
+
+#if defined(__NetBSD__)
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+#endif /* defined (__NetBSD__) */
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+#if __FreeBSD_version < 500000
+#include <machine/clock.h>
+#endif
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/usb/if_udavreg.h>
+
+#if defined(__FreeBSD__)
+MODULE_DEPEND(udav, usb, 1, 1, 1);
+MODULE_DEPEND(udav, ether, 1, 1, 1);
+MODULE_DEPEND(udav, miibus, 1, 1, 1);
+#endif
+
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+#if !defined(__FreeBSD__)
+/* Function declarations */
+USB_DECLARE_DRIVER(udav);
+#endif
+
+#if defined(__FreeBSD__)
+Static int udav_match(device_ptr_t);
+Static int udav_attach(device_ptr_t);
+Static int udav_detach(device_ptr_t);
+Static void udav_shutdown(device_ptr_t);
+#endif
+
+Static int udav_openpipes(struct udav_softc *);
+Static int udav_rx_list_init(struct udav_softc *);
+Static int udav_tx_list_init(struct udav_softc *);
+Static int udav_newbuf(struct udav_softc *, struct udav_chain *, struct mbuf *);
+Static void udav_start(struct ifnet *);
+Static int udav_send(struct udav_softc *, struct mbuf *, int);
+Static void udav_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+#if defined(__FreeBSD__)
+Static void udav_rxstart(struct ifnet *ifp);
+#endif
+Static void udav_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void udav_tick(void *);
+Static void udav_tick_task(void *);
+Static int udav_ioctl(struct ifnet *, u_long, caddr_t);
+Static void udav_stop_task(struct udav_softc *);
+Static void udav_stop(struct ifnet *, int);
+Static void udav_watchdog(struct ifnet *);
+Static int udav_ifmedia_change(struct ifnet *);
+Static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *);
+Static void udav_lock_mii(struct udav_softc *);
+Static void udav_unlock_mii(struct udav_softc *);
+Static int udav_miibus_readreg(device_ptr_t, int, int);
+Static void udav_miibus_writereg(device_ptr_t, int, int, int);
+Static void udav_miibus_statchg(device_ptr_t);
+#if defined(__NetBSD__)
+Static int udav_init(struct ifnet *);
+#elif defined(__FreeBSD__)
+Static void udav_init(void *);
+#endif
+Static void udav_setmulti(struct udav_softc *);
+Static void udav_reset(struct udav_softc *);
+
+Static int udav_csr_read(struct udav_softc *, int, void *, int);
+Static int udav_csr_write(struct udav_softc *, int, void *, int);
+Static int udav_csr_read1(struct udav_softc *, int);
+Static int udav_csr_write1(struct udav_softc *, int, unsigned char);
+
+#if 0
+Static int udav_mem_read(struct udav_softc *, int, void *, int);
+Static int udav_mem_write(struct udav_softc *, int, void *, int);
+Static int udav_mem_write1(struct udav_softc *, int, unsigned char);
+#endif
+
+#if defined(__FreeBSD__)
+Static device_method_t udav_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, udav_match),
+ DEVMETHOD(device_attach, udav_attach),
+ DEVMETHOD(device_detach, udav_detach),
+ DEVMETHOD(device_shutdown, udav_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, udav_miibus_readreg),
+ DEVMETHOD(miibus_writereg, udav_miibus_writereg),
+ DEVMETHOD(miibus_statchg, udav_miibus_statchg),
+
+ { 0, 0 }
+};
+
+Static driver_t udav_driver = {
+ "udav",
+ udav_methods,
+ sizeof(struct udav_softc)
+};
+
+Static devclass_t udav_devclass;
+
+DRIVER_MODULE(udav, uhub, udav_driver, udav_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0);
+
+#endif /* defined(__FreeBSD__) */
+
+/* Macros */
+#ifdef UDAV_DEBUG
+#define DPRINTF(x) if (udavdebug) logprintf x
+#define DPRINTFN(n,x) if (udavdebug >= (n)) logprintf x
+int udavdebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define delay(d) DELAY(d)
+
+#define UDAV_SETBIT(sc, reg, x) \
+ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x))
+
+#define UDAV_CLRBIT(sc, reg, x) \
+ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x))
+
+static const struct udav_type {
+ struct usb_devno udav_dev;
+ u_int16_t udav_flags;
+#define UDAV_EXT_PHY 0x0001
+} udav_devs [] = {
+ /* Corega USB-TXC */
+ {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC }, 0},
+#if 0
+ /* DAVICOM DM9601 Generic? */
+ /* XXX: The following ids was obtained from the data sheet. */
+ {{ 0x0a46, 0x9601 }, 0},
+#endif
+};
+#define udav_lookup(v, p) ((const struct udav_type *)usb_lookup(udav_devs, v, p))
+
+
+/* Probe */
+USB_MATCH(udav)
+{
+ USB_MATCH_START(udav, uaa);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ return (udav_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+/* Attach */
+USB_ATTACH(udav)
+{
+ USB_ATTACH_START(udav, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ char devinfo[1024];
+ const char *devname ;
+ struct ifnet *ifp;
+#if defined(__NetBSD__)
+ struct mii_data *mii;
+#endif
+ u_char eaddr[ETHER_ADDR_LEN];
+ int i;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ bzero(sc, sizeof(struct udav_softc));
+
+ usbd_devinfo(dev, 0, devinfo);
+ USB_ATTACH_SETUP;
+ devname = USBDEVNAME(sc->sc_dev);
+ printf("%s: %s\n", devname, devinfo);
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_no(dev, UDAV_CONFIG_NO, 1);
+ if (err) {
+ printf("%s: setting config no failed\n", devname);
+ goto bad;
+ }
+
+ usb_init_task(&sc->sc_tick_task, udav_tick_task, sc);
+ lockinit(&sc->sc_mii_lock, PZERO, "udavmii", 0, 0);
+ usb_init_task(&sc->sc_stop_task, (void (*)(void *)) udav_stop_task, sc);
+
+ /* get control interface */
+ err = usbd_device2interface_handle(dev, UDAV_IFACE_INDEX, &iface);
+ if (err) {
+ printf("%s: failed to get interface, err=%s\n", devname,
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ sc->sc_udev = dev;
+ sc->sc_ctl_iface = iface;
+ sc->sc_flags = udav_lookup(uaa->vendor, uaa->product)->udav_flags;
+
+ /* get interface descriptor */
+ id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
+
+ /* find endpoints */
+ sc->sc_bulkin_no = sc->sc_bulkout_no = sc->sc_intrin_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
+ if (ed == NULL) {
+ printf("%s: couldn't get endpoint %d\n", devname, i);
+ goto bad;
+ }
+ if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK &&
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ sc->sc_bulkin_no = ed->bEndpointAddress; /* RX */
+ else if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK &&
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
+ sc->sc_bulkout_no = ed->bEndpointAddress; /* TX */
+ else if ((ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT &&
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ sc->sc_intrin_no = ed->bEndpointAddress; /* Status */
+ }
+
+ if (sc->sc_bulkin_no == -1 || sc->sc_bulkout_no == -1 ||
+ sc->sc_intrin_no == -1) {
+ printf("%s: missing endpoint\n", devname);
+ goto bad;
+ }
+
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+ mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+#endif
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ /* reset the adapter */
+ udav_reset(sc);
+
+ /* Get Ethernet Address */
+ err = udav_csr_read(sc, UDAV_PAR, (void *)eaddr, ETHER_ADDR_LEN);
+ if (err) {
+ printf("%s: read MAC address failed\n", devname);
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+ goto bad;
+ }
+
+ /* Print Ethernet Address */
+ printf("%s: Ethernet address %s\n", devname, ether_sprintf(eaddr));
+
+#if defined(__FreeBSD__)
+ bcopy(eaddr, (char *)&sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
+#endif
+
+ /* initialize interface infomation */
+ ifp = GET_IFP(sc);
+ ifp->if_softc = sc;
+ ifp->if_mtu = ETHERMTU;
+#if defined(__NetBSD__)
+ strncpy(ifp->if_xname, devname, IFNAMSIZ);
+#elif defined(__FreeBSD__)
+ if_initname(ifp, "udav", device_get_unit(self));
+#endif
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_start = udav_start;
+ ifp->if_ioctl = udav_ioctl;
+ ifp->if_watchdog = udav_watchdog;
+ ifp->if_init = udav_init;
+#if defined(__NetBSD__)
+ ifp->if_stop = udav_stop;
+#endif
+#if defined(__FreeBSD__)
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+#endif
+#if defined(__NetBSD__)
+ IFQ_SET_READY(&ifp->if_snd);
+#endif
+
+
+#if defined(__NetBSD__)
+ /*
+ * Do ifmedia setup.
+ */
+ mii = &sc->sc_mii;
+ mii->mii_ifp = ifp;
+ mii->mii_readreg = udav_miibus_readreg;
+ mii->mii_writereg = udav_miibus_writereg;
+ mii->mii_statchg = udav_miibus_statchg;
+ mii->mii_flags = MIIF_AUTOTSLEEP;
+ ifmedia_init(&mii->mii_media, 0,
+ udav_ifmedia_change, udav_ifmedia_status);
+ mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+ if (LIST_FIRST(&mii->mii_phys) == NULL) {
+ ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
+ ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
+ } else
+ ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
+
+ /* attach the interface */
+ if_attach(ifp);
+ Ether_ifattach(ifp, eaddr);
+#elif defined(__FreeBSD__)
+ if (mii_phy_probe(self, &sc->sc_miibus,
+ udav_ifmedia_change, udav_ifmedia_status)) {
+ printf("%s: MII without any PHY!\n", USBDEVNAME(sc->sc_dev));
+ UDAV_UNLOCK(sc);
+ mtx_destroy(&sc->sc_mtx);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_qdat.ifp = ifp;
+ sc->sc_qdat.if_rxstart = udav_rxstart;
+
+ /*
+ * Call MI attach routine.
+ */
+
+ ether_ifattach(ifp, eaddr);
+#endif
+
+#if NRND > 0
+ rnd_attach_source(&sc->rnd_source, devname, RND_TYPE_NET, 0);
+#endif
+
+ usb_callout_init(sc->sc_stat_ch);
+#if defined(__FreeBSD__)
+ usb_register_netisr();
+#endif
+ sc->sc_attached = 1;
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, USBDEV(sc->sc_dev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+
+ bad:
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+}
+
+/* detach */
+USB_DETACH(udav)
+{
+ USB_DETACH_START(udav, sc);
+ struct ifnet *ifp = GET_IFP(sc);
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ /* Detached before attached finished */
+ if (!sc->sc_attached)
+ return (0);
+
+ UDAV_LOCK(sc);
+
+ usb_uncallout(sc->sc_stat_ch, udav_tick, sc);
+
+ /* Remove any pending tasks */
+ usb_rem_task(sc->sc_udev, &sc->sc_tick_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_stop_task);
+
+#if defined(__NetBSD__)
+ s = splusb();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ if (ifp->if_flags & IFF_RUNNING)
+ udav_stop(GET_IFP(sc), 1);
+
+#if NRND > 0
+ rnd_detach_source(&sc->rnd_source);
+#endif
+#if defined(__NetBSD__)
+ mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
+ ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
+#endif
+ ether_ifdetach(ifp);
+#if defined(__NetBSD__)
+ if_detach(ifp);
+#endif
+
+#ifdef DIAGNOSTIC
+ if (sc->sc_pipe_tx != NULL)
+ printf("%s: detach has active tx endpoint.\n",
+ USBDEVNAME(sc->sc_dev));
+ if (sc->sc_pipe_rx != NULL)
+ printf("%s: detach has active rx endpoint.\n",
+ USBDEVNAME(sc->sc_dev));
+ if (sc->sc_pipe_intr != NULL)
+ printf("%s: detach has active intr endpoint.\n",
+ USBDEVNAME(sc->sc_dev));
+#endif
+ sc->sc_attached = 0;
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+
+#if defined(__FreeBSD__)
+ mtx_destroy(&sc->sc_mtx);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+ return (0);
+}
+
+#if 0
+/* read memory */
+Static int
+udav_mem_read(struct udav_softc *sc, int offset, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xffff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_READ;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, buf);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ if (err) {
+ DPRINTF(("%s: %s: read failed. off=%04x, err=%d\n",
+ USBDEVNAME(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+/* write memory */
+Static int
+udav_mem_write(struct udav_softc *sc, int offset, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xffff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_WRITE;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, buf);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ if (err) {
+ DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
+ USBDEVNAME(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+/* write memory */
+Static int
+udav_mem_write1(struct udav_softc *sc, int offset, unsigned char ch)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xffff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_WRITE1;
+ USETW(req.wValue, ch);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, 0x0000);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ if (err) {
+ DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
+ USBDEVNAME(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+#endif
+
+/* read register(s) */
+Static int
+udav_csr_read(struct udav_softc *sc, int offset, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_READ;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, buf);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ if (err) {
+ DPRINTF(("%s: %s: read failed. off=%04x, err=%d\n",
+ USBDEVNAME(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+/* write register(s) */
+Static int
+udav_csr_write(struct udav_softc *sc, int offset, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_WRITE;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, buf);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ if (err) {
+ DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
+ USBDEVNAME(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+Static int
+udav_csr_read1(struct udav_softc *sc, int offset)
+{
+ u_int8_t val = 0;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ return (udav_csr_read(sc, offset, &val, 1) ? 0 : val);
+}
+
+/* write a register */
+Static int
+udav_csr_write1(struct udav_softc *sc, int offset, unsigned char ch)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_WRITE1;
+ USETW(req.wValue, ch);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, 0x0000);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ if (err) {
+ DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
+ USBDEVNAME(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+#if defined(__NetBSD__)
+Static int
+udav_init(struct ifnet *ifp)
+#elif defined(__FreeBSD__)
+Static void
+udav_init(void *xsc)
+#endif
+{
+#if defined(__NetBSD__)
+ struct udav_softc *sc = ifp->if_softc;
+#elif defined(__FreeBSD__)
+ struct udav_softc *sc = (struct udav_softc *)xsc;
+ struct ifnet *ifp = GET_IFP(sc);
+#endif
+ struct mii_data *mii = GET_MII(sc);
+ u_char *eaddr;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+#if defined(__NetBSD__)
+ return (EIO);
+#elif defined(__FreeBSD__)
+ return ;
+#endif
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ /* Cancel pending I/O and free all TX/RX buffers */
+ udav_stop(ifp, 1);
+
+#if defined(__NetBSD__)
+ eaddr = LLADDR(ifp->if_sadl);
+#elif defined(__FreeBSD__)
+ eaddr = sc->sc_ac.ac_enaddr ;
+#endif
+ udav_csr_write(sc, UDAV_PAR, eaddr, ETHER_ADDR_LEN);
+
+ /* Initialize network control register */
+ /* Disable loopback */
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1);
+
+ /* Initialize RX control register */
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC);
+
+ /* If we want promiscuous mode, accept all physical frames. */
+ if (ifp->if_flags & IFF_PROMISC)
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ else
+ UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+
+ /* Initialize transmit ring */
+ if (udav_tx_list_init(sc) == ENOBUFS) {
+ printf("%s: tx list init failed\n", USBDEVNAME(sc->sc_dev));
+#if defined(__NetBSD__)
+ splx(s);
+ return (EIO);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+ return ;
+#endif
+
+ }
+
+ /* Initialize receive ring */
+ if (udav_rx_list_init(sc) == ENOBUFS) {
+ printf("%s: rx list init failed\n", USBDEVNAME(sc->sc_dev));
+#if defined(__NetBSD__)
+ splx(s);
+ return (EIO);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+ return ;
+#endif
+ }
+
+ /* Load the multicast filter */
+ udav_setmulti(sc);
+
+ /* Enable RX */
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN);
+
+ /* clear POWER_DOWN state of internal PHY */
+ UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0);
+ UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0);
+
+ mii_mediachg(mii);
+
+ if (sc->sc_pipe_tx == NULL || sc->sc_pipe_rx == NULL) {
+ if (udav_openpipes(sc)) {
+#if defined(__NetBSD__)
+ splx(s);
+ return (EIO);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+ return ;
+#endif
+ }
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+
+ usb_callout(sc->sc_stat_ch, hz, udav_tick, sc);
+
+#if defined(__NetBSD__)
+ return (0);
+#elif defined(__FreeBSD__)
+ return ;
+#endif
+}
+
+Static void
+udav_reset(struct udav_softc *sc)
+{
+ int i;
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ /* Select PHY */
+#if 1
+ /*
+ * XXX: force select internal phy.
+ * external phy routines are not tested.
+ */
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+#else
+ if (sc->sc_flags & UDAV_EXT_PHY) {
+ UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+ } else {
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+ }
+#endif
+
+ UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST);
+
+ for (i = 0; i < UDAV_TX_TIMEOUT; i++) {
+ if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST))
+ break;
+ delay(10); /* XXX */
+ }
+ delay(10000); /* XXX */
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+udav_activate(device_ptr_t self, enum devact act)
+{
+ struct udav_softc *sc = (struct udav_softc *)self;
+
+ DPRINTF(("%s: %s: enter, act=%d\n", USBDEVNAME(sc->sc_dev),
+ __func__, act));
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ if_deactivate(&sc->sc_ec.ec_if);
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+#endif
+
+#define UDAV_BITS 6
+
+#define UDAV_CALCHASH(addr) \
+ (ether_crc32_le((addr), ETHER_ADDR_LEN) & ((1 << UDAV_BITS) - 1))
+
+Static void
+udav_setmulti(struct udav_softc *sc)
+{
+ struct ifnet *ifp;
+#if defined(__NetBSD__)
+ struct ether_multi *enm;
+ struct ether_multistep step;
+#elif defined(__FreeBSD__)
+ struct ifmultiaddr *ifma;
+#endif
+ u_int8_t hashes[8];
+ int h = 0;
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ ifp = GET_IFP(sc);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ return;
+ } else if (ifp->if_flags & IFF_ALLMULTI) {
+#if defined(__NetBSD__)
+ allmulti:
+#endif
+ ifp->if_flags |= IFF_ALLMULTI;
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
+ UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_PRMSC);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ memset(hashes, 0x00, sizeof(hashes));
+ hashes[7] |= 0x80; /* broadcast address */
+ udav_csr_write(sc, UDAV_MAR, hashes, sizeof(hashes));
+
+ /* now program new ones */
+#if defined(__NetBSD__)
+ ETHER_FIRST_MULTI(step, &sc->sc_ec, enm);
+ while (enm != NULL) {
+ if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
+ ETHER_ADDR_LEN) != 0)
+ goto allmulti;
+
+ h = UDAV_CALCHASH(enm->enm_addrlo);
+ hashes[h>>3] |= 1 << (h & 0x7);
+ ETHER_NEXT_MULTI(step, enm);
+ }
+#elif defined(__FreeBSD__)
+#if __FreeBSD_version >= 500000
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#else
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#endif
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = UDAV_CALCHASH(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr));
+ hashes[h>>3] |= 1 << (h & 0x7);
+ }
+#endif
+
+ /* disable all multicast */
+ ifp->if_flags &= ~IFF_ALLMULTI;
+ UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
+
+ /* write hash value to the register */
+ udav_csr_write(sc, UDAV_MAR, hashes, sizeof(hashes));
+}
+
+Static int
+udav_openpipes(struct udav_softc *sc)
+{
+ struct udav_chain *c;
+ usbd_status err;
+ int i;
+ int error = 0;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ sc->sc_refcnt++;
+
+ /* Open RX pipe */
+ err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkin_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_pipe_rx);
+ if (err) {
+ printf("%s: open rx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ error = EIO;
+ goto done;
+ }
+
+ /* Open TX pipe */
+ err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkout_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_pipe_tx);
+ if (err) {
+ printf("%s: open tx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ error = EIO;
+ goto done;
+ }
+
+#if 0
+ /* XXX: interrupt endpoint is not yet supported */
+ /* Open Interrupt pipe */
+ err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_intrin_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_pipe_intr, sc,
+ &sc->sc_cdata.udav_ibuf, UDAV_INTR_PKGLEN,
+ udav_intr, UDAV_INTR_INTERVAL);
+ if (err) {
+ printf("%s: open intr pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ error = EIO;
+ goto done;
+ }
+#endif
+
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < UDAV_RX_LIST_CNT; i++) {
+ c = &sc->sc_cdata.udav_rx_chain[i];
+ usbd_setup_xfer(c->udav_xfer, sc->sc_pipe_rx,
+ c, c->udav_buf, UDAV_BUFSZ,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, udav_rxeof);
+ (void)usbd_transfer(c->udav_xfer);
+ DPRINTF(("%s: %s: start read\n", USBDEVNAME(sc->sc_dev),
+ __func__));
+ }
+
+ done:
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+ return (error);
+}
+
+Static int
+udav_newbuf(struct udav_softc *sc, struct udav_chain *c, struct mbuf *m)
+{
+ struct mbuf *m_new = NULL;
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (m == NULL) {
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL) {
+ printf("%s: no memory for rx list "
+ "-- packet dropped!\n", USBDEVNAME(sc->sc_dev));
+ return (ENOBUFS);
+ }
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ printf("%s: no memory for rx list "
+ "-- packet dropped!\n", USBDEVNAME(sc->sc_dev));
+ m_freem(m_new);
+ return (ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ m_adj(m_new, ETHER_ALIGN);
+ c->udav_mbuf = m_new;
+
+ return (0);
+}
+
+
+Static int
+udav_rx_list_init(struct udav_softc *sc)
+{
+ struct udav_cdata *cd;
+ struct udav_chain *c;
+ int i;
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ cd = &sc->sc_cdata;
+ for (i = 0; i < UDAV_RX_LIST_CNT; i++) {
+ c = &cd->udav_rx_chain[i];
+ c->udav_sc = sc;
+ c->udav_idx = i;
+ if (udav_newbuf(sc, c, NULL) == ENOBUFS)
+ return (ENOBUFS);
+ if (c->udav_xfer == NULL) {
+ c->udav_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (c->udav_xfer == NULL)
+ return (ENOBUFS);
+ c->udav_buf = usbd_alloc_buffer(c->udav_xfer, UDAV_BUFSZ);
+ if (c->udav_buf == NULL) {
+ usbd_free_xfer(c->udav_xfer);
+ return (ENOBUFS);
+ }
+ }
+ }
+
+ return (0);
+}
+
+Static int
+udav_tx_list_init(struct udav_softc *sc)
+{
+ struct udav_cdata *cd;
+ struct udav_chain *c;
+ int i;
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ cd = &sc->sc_cdata;
+ for (i = 0; i < UDAV_TX_LIST_CNT; i++) {
+ c = &cd->udav_tx_chain[i];
+ c->udav_sc = sc;
+ c->udav_idx = i;
+ c->udav_mbuf = NULL;
+ if (c->udav_xfer == NULL) {
+ c->udav_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (c->udav_xfer == NULL)
+ return (ENOBUFS);
+ c->udav_buf = usbd_alloc_buffer(c->udav_xfer, UDAV_BUFSZ);
+ if (c->udav_buf == NULL) {
+ usbd_free_xfer(c->udav_xfer);
+ return (ENOBUFS);
+ }
+ }
+ }
+
+ return (0);
+}
+
+Static void
+udav_start(struct ifnet *ifp)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mbuf *m_head = NULL;
+
+ DPRINTF(("%s: %s: enter, link=%d\n", USBDEVNAME(sc->sc_dev),
+ __func__, sc->sc_link));
+
+ if (sc->sc_dying)
+ return;
+
+ if (!sc->sc_link)
+ return;
+
+ if (ifp->if_flags & IFF_OACTIVE)
+ return;
+#if defined(__NetBSD__)
+ IFQ_POLL(&ifp->if_snd, m_head);
+#elif defined(__FreeBSD__)
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+#endif
+ if (m_head == NULL)
+ return;
+
+ if (udav_send(sc, m_head, 0)) {
+#if defined(__FreeBSD__)
+ IF_PREPEND(&ifp->if_snd, m_head);
+#endif
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ }
+
+#if defined(__NetBSD__)
+ IFQ_DEQUEUE(&ifp->if_snd, m_head);
+#endif
+
+#if NBPFILTER > 0
+ BPF_MTAP(ifp, m_head);
+#endif
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /* Set a timeout in case the chip goes out to lunch. */
+ ifp->if_timer = 5;
+}
+
+Static int
+udav_send(struct udav_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct udav_chain *c;
+ usbd_status err;
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__func__));
+
+ c = &sc->sc_cdata.udav_tx_chain[idx];
+
+ /* Copy the mbuf data into a contiguous buffer */
+ /* first 2 bytes are packet length */
+ m_copydata(m, 0, m->m_pkthdr.len, c->udav_buf + 2);
+ c->udav_mbuf = m;
+ total_len = m->m_pkthdr.len;
+ if (total_len < UDAV_MIN_FRAME_LEN) {
+ memset(c->udav_buf + 2 + total_len, 0,
+ UDAV_MIN_FRAME_LEN - total_len);
+ total_len = UDAV_MIN_FRAME_LEN;
+ }
+
+ /* Frame length is specified in the first 2bytes of the buffer */
+ c->udav_buf[0] = (u_int8_t)total_len;
+ c->udav_buf[1] = (u_int8_t)(total_len >> 8);
+ total_len += 2;
+
+ usbd_setup_xfer(c->udav_xfer, sc->sc_pipe_tx, c, c->udav_buf, total_len,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
+ UDAV_TX_TIMEOUT, udav_txeof);
+
+ /* Transmit */
+ sc->sc_refcnt++;
+ err = usbd_transfer(c->udav_xfer);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ if (err != USBD_IN_PROGRESS) {
+ printf("%s: udav_send error=%s\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(err));
+ /* Stop the interface */
+ usb_add_task(sc->sc_udev, &sc->sc_stop_task);
+ return (EIO);
+ }
+
+ DPRINTF(("%s: %s: send %d bytes\n", USBDEVNAME(sc->sc_dev),
+ __func__, total_len));
+
+ sc->sc_cdata.udav_tx_cnt++;
+
+ return (0);
+}
+
+Static void
+udav_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct udav_chain *c = priv;
+ struct udav_softc *sc = c->udav_sc;
+ struct ifnet *ifp = GET_IFP(sc);
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ if (sc->sc_dying)
+ return;
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+ return;
+ }
+ ifp->if_oerrors++;
+ printf("%s: usb error on tx: %s\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(status));
+ if (status == USBD_STALLED) {
+ sc->sc_refcnt++;
+ usbd_clear_endpoint_stall(sc->sc_pipe_tx);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ }
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+ return;
+ }
+
+ ifp->if_opackets++;
+
+ m_freem(c->udav_mbuf);
+ c->udav_mbuf = NULL;
+
+#if defined(__NetBSD__)
+ if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
+#elif defined(__FreeBSD__)
+ if ( ifp->if_snd.ifq_head != NULL )
+#endif
+ udav_start(ifp);
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+}
+
+Static void
+udav_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct udav_chain *c = priv;
+ struct udav_softc *sc = c->udav_sc;
+ struct ifnet *ifp = GET_IFP(sc);
+ struct mbuf *m;
+ u_int32_t total_len;
+ u_int8_t *pktstat;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),__func__));
+
+ if (sc->sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+ sc->sc_rx_errs++;
+ if (usbd_ratecheck(&sc->sc_rx_notice)) {
+ printf("%s: %u usb errors on rx: %s\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_rx_errs,
+ usbd_errstr(status));
+ sc->sc_rx_errs = 0;
+ }
+ if (status == USBD_STALLED) {
+ sc->sc_refcnt++;
+ usbd_clear_endpoint_stall(sc->sc_pipe_rx);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ }
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ /* copy data to mbuf */
+ m = c->udav_mbuf;
+ memcpy(mtod(m, char *), c->udav_buf, total_len);
+
+ /* first byte in received data */
+ pktstat = mtod(m, u_int8_t *);
+ m_adj(m, sizeof(u_int8_t));
+ DPRINTF(("%s: RX Status: 0x%02x\n", USBDEVNAME(sc->sc_dev), *pktstat));
+
+ total_len = UGETW(mtod(m, u_int8_t *));
+ m_adj(m, sizeof(u_int16_t));
+
+ if (*pktstat & UDAV_RSR_LCS) {
+ ifp->if_collisions++;
+ goto done;
+ }
+
+ if (total_len < sizeof(struct ether_header) ||
+ *pktstat & UDAV_RSR_ERR) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ ifp->if_ipackets++;
+ total_len -= ETHER_CRC_LEN;
+
+ m->m_pkthdr.len = m->m_len = total_len;
+#if defined(__NetBSD__)
+ m->m_pkthdr.rcvif = ifp;
+#elif defined(__FreeBSD__)
+ m->m_pkthdr.rcvif = (struct ifnet *)&sc->sc_qdat;
+#endif
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+#if defined(__NetBSD__)
+ if (udav_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ goto done1;
+ }
+#endif
+
+#if NBPFILTER > 0
+ BPF_MTAP(ifp, m);
+#endif
+
+ DPRINTF(("%s: %s: deliver %d\n", USBDEVNAME(sc->sc_dev),
+ __func__, m->m_len));
+#if defined(__NetBSD__)
+ IF_INPUT(ifp, m);
+#endif
+#if defined(__FreeBSD__)
+ usb_ether_input(m);
+ UDAV_UNLOCK(sc);
+ return ;
+#endif
+
+#if defined(__NetBSD__)
+ done1:
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+ done:
+ /* Setup new transfer */
+ usbd_setup_xfer(xfer, sc->sc_pipe_rx, c, c->udav_buf, UDAV_BUFSZ,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, udav_rxeof);
+ sc->sc_refcnt++;
+ usbd_transfer(xfer);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+ DPRINTF(("%s: %s: start rx\n", USBDEVNAME(sc->sc_dev), __func__));
+}
+
+#if 0
+Static void udav_intr()
+{
+}
+#endif
+
+Static int
+udav_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+#if defined(__NetBSD__)
+ int s;
+#endif
+ int error = 0;
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ switch (cmd) {
+#if defined(__FreeBSD__)
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING &&
+ ifp->if_flags & IFF_PROMISC) {
+ UDAV_SETBIT(sc, UDAV_RCR,
+ UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ } else if (ifp->if_flags & IFF_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC)) {
+ if (ifp->if_flags & IFF_ALLMULTI)
+ UDAV_CLRBIT(sc, UDAV_RCR,
+ UDAV_RCR_PRMSC);
+ else
+ UDAV_CLRBIT(sc, UDAV_RCR,
+ UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ } else if (!(ifp->if_flags & IFF_RUNNING))
+ udav_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ udav_stop(ifp, 1);
+ }
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ udav_setmulti(sc);
+ error = 0;
+ break;
+#endif
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
+ break;
+
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+#if defined(__NetBSD__)
+ if (error == ENETRESET) {
+ udav_setmulti(sc);
+ error = 0;
+ }
+#endif
+ break;
+ }
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+
+ return (error);
+}
+
+Static void
+udav_watchdog(struct ifnet *ifp)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct udav_chain *c;
+ usbd_status stat;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ ifp->if_oerrors++;
+ printf("%s: watchdog timeout\n", USBDEVNAME(sc->sc_dev));
+
+#if defined(__NetBSD__)
+ s = splusb();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc)
+#endif
+ c = &sc->sc_cdata.udav_tx_chain[0];
+ usbd_get_xfer_status(c->udav_xfer, NULL, NULL, NULL, &stat);
+ udav_txeof(c->udav_xfer, c, stat);
+
+#if defined(__NetBSD__)
+ if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
+#elif defined(__FreeBSD__)
+ if ( ifp->if_snd.ifq_head != NULL )
+#endif
+ udav_start(ifp);
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+}
+
+Static void
+udav_stop_task(struct udav_softc *sc)
+{
+ udav_stop(GET_IFP(sc), 1);
+}
+
+/* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
+Static void
+udav_stop(struct ifnet *ifp, int disable)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ usbd_status err;
+ int i;
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ ifp->if_timer = 0;
+
+ udav_reset(sc);
+
+ usb_uncallout(sc->sc_stat_ch, udav_tick, sc);
+
+ /* Stop transfers */
+ /* RX endpoint */
+ if (sc->sc_pipe_rx != NULL) {
+ err = usbd_abort_pipe(sc->sc_pipe_rx);
+ if (err)
+ printf("%s: abort rx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_pipe_rx);
+ if (err)
+ printf("%s: close rx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ sc->sc_pipe_rx = NULL;
+ }
+
+ /* TX endpoint */
+ if (sc->sc_pipe_tx != NULL) {
+ err = usbd_abort_pipe(sc->sc_pipe_tx);
+ if (err)
+ printf("%s: abort tx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_pipe_tx);
+ if (err)
+ printf("%s: close tx pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ sc->sc_pipe_tx = NULL;
+ }
+
+#if 0
+ /* XXX: Interrupt endpoint is not yet supported!! */
+ /* Interrupt endpoint */
+ if (sc->sc_pipe_intr != NULL) {
+ err = usbd_abort_pipe(sc->sc_pipe_intr);
+ if (err)
+ printf("%s: abort intr pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_pipe_intr);
+ if (err)
+ printf("%s: close intr pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ sc->sc_pipe_intr = NULL;
+ }
+#endif
+
+ /* Free RX resources. */
+ for (i = 0; i < UDAV_RX_LIST_CNT; i++) {
+ if (sc->sc_cdata.udav_rx_chain[i].udav_mbuf != NULL) {
+ m_freem(sc->sc_cdata.udav_rx_chain[i].udav_mbuf);
+ sc->sc_cdata.udav_rx_chain[i].udav_mbuf = NULL;
+ }
+ if (sc->sc_cdata.udav_rx_chain[i].udav_xfer != NULL) {
+ usbd_free_xfer(sc->sc_cdata.udav_rx_chain[i].udav_xfer);
+ sc->sc_cdata.udav_rx_chain[i].udav_xfer = NULL;
+ }
+ }
+
+ /* Free TX resources. */
+ for (i = 0; i < UDAV_TX_LIST_CNT; i++) {
+ if (sc->sc_cdata.udav_tx_chain[i].udav_mbuf != NULL) {
+ m_freem(sc->sc_cdata.udav_tx_chain[i].udav_mbuf);
+ sc->sc_cdata.udav_tx_chain[i].udav_mbuf = NULL;
+ }
+ if (sc->sc_cdata.udav_tx_chain[i].udav_xfer != NULL) {
+ usbd_free_xfer(sc->sc_cdata.udav_tx_chain[i].udav_xfer);
+ sc->sc_cdata.udav_tx_chain[i].udav_xfer = NULL;
+ }
+ }
+
+ sc->sc_link = 0;
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+}
+
+/* Set media options */
+Static int
+udav_ifmedia_change(struct ifnet *ifp)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ sc->sc_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
+ miisc = LIST_NEXT(miisc, mii_list))
+ mii_phy_reset(miisc);
+ }
+
+ return (mii_mediachg(mii));
+}
+
+/* Report current media status. */
+Static void
+udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+ ifmr->ifm_active = IFM_ETHER | IFM_NONE;
+ ifmr->ifm_status = 0;
+ return;
+ }
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+Static void
+udav_tick(void *xsc)
+{
+ struct udav_softc *sc = xsc;
+
+ if (sc == NULL)
+ return;
+
+ DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),
+ __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ /* Perform periodic stuff in process context */
+ usb_add_task(sc->sc_udev, &sc->sc_tick_task);
+}
+
+Static void
+udav_tick_task(void *xsc)
+{
+ struct udav_softc *sc = xsc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ if (sc == NULL)
+ return;
+
+ DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),
+ __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ ifp = GET_IFP(sc);
+ mii = GET_MII(sc);
+
+ if (mii == NULL)
+ return;
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ mii_tick(mii);
+ if (!sc->sc_link) {
+ mii_pollstat(mii);
+ if (mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ DPRINTF(("%s: %s: got link\n",
+ USBDEVNAME(sc->sc_dev), __func__));
+ sc->sc_link++;
+#if defined(__NetBSD__)
+ if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
+#elif defined(__FreeBSD__)
+ if ( ifp->if_snd.ifq_head != NULL )
+#endif
+ udav_start(ifp);
+ }
+ }
+
+ usb_callout(sc->sc_stat_ch, hz, udav_tick, sc);
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+}
+
+/* Get exclusive access to the MII registers */
+Static void
+udav_lock_mii(struct udav_softc *sc)
+{
+ DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),
+ __func__));
+
+ sc->sc_refcnt++;
+#if defined(__NetBSD__)
+ lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL);
+#elif defined(__FreeBSD__)
+ lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL, NULL);
+#endif
+}
+
+Static void
+udav_unlock_mii(struct udav_softc *sc)
+{
+ DPRINTFN(0xff, ("%s: %s: enter\n", USBDEVNAME(sc->sc_dev),
+ __func__));
+
+#if defined(__NetBSD__)
+ lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL);
+#elif defined(__FreeBSD__)
+ lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL, NULL);
+#endif
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+}
+
+Static int
+udav_miibus_readreg(device_ptr_t dev, int phy, int reg)
+{
+ struct udav_softc *sc;
+ u_int8_t val[2];
+ u_int16_t data16;
+
+ if (dev == NULL)
+ return (0);
+
+ sc = USBGETSOFTC(dev);
+
+ DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x\n",
+ USBDEVNAME(sc->sc_dev), __func__, phy, reg));
+
+ if (sc->sc_dying) {
+#ifdef DIAGNOSTIC
+ printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev),
+ __func__);
+#endif
+ return (0);
+ }
+
+ /* XXX: one PHY only for the internal PHY */
+ if (phy != 0) {
+ DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n",
+ USBDEVNAME(sc->sc_dev), __func__, phy));
+ return (0);
+ }
+
+ udav_lock_mii(sc);
+
+ /* select internal PHY and set PHY register address */
+ udav_csr_write1(sc, UDAV_EPAR,
+ UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
+
+ /* select PHY operation and start read command */
+ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR);
+
+ /* XXX: should be wait? */
+
+ /* end read command */
+ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR);
+
+ /* retrieve the result from data registers */
+ udav_csr_read(sc, UDAV_EPDRL, val, 2);
+
+ udav_unlock_mii(sc);
+
+ data16 = val[0] | (val[1] << 8);
+
+ DPRINTFN(0xff, ("%s: %s: phy=%d reg=0x%04x => 0x%04x\n",
+ USBDEVNAME(sc->sc_dev), __func__, phy, reg, data16));
+
+ return (data16);
+}
+
+Static void
+udav_miibus_writereg(device_ptr_t dev, int phy, int reg, int data)
+{
+ struct udav_softc *sc;
+ u_int8_t val[2];
+
+ if (dev == NULL)
+ return;
+
+ sc = USBGETSOFTC(dev);
+
+ DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n",
+ USBDEVNAME(sc->sc_dev), __func__, phy, reg, data));
+
+ if (sc->sc_dying) {
+#ifdef DIAGNOSTIC
+ printf("%s: %s: dying\n", USBDEVNAME(sc->sc_dev),
+ __func__);
+#endif
+ return;
+ }
+
+ /* XXX: one PHY only for the internal PHY */
+ if (phy != 0) {
+ DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n",
+ USBDEVNAME(sc->sc_dev), __func__, phy));
+ return;
+ }
+
+ udav_lock_mii(sc);
+
+ /* select internal PHY and set PHY register address */
+ udav_csr_write1(sc, UDAV_EPAR,
+ UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
+
+ /* put the value to the data registers */
+ val[0] = data & 0xff;
+ val[1] = (data >> 8) & 0xff;
+ udav_csr_write(sc, UDAV_EPDRL, val, 2);
+
+ /* select PHY operation and start write command */
+ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW);
+
+ /* XXX: should be wait? */
+
+ /* end write command */
+ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW);
+
+ udav_unlock_mii(sc);
+
+ return;
+}
+
+Static void
+udav_miibus_statchg(device_ptr_t dev)
+{
+#ifdef UDAV_DEBUG
+ struct udav_softc *sc;
+
+ if (dev == NULL)
+ return;
+
+ sc = USBGETSOFTC(dev);
+ DPRINTF(("%s: %s: enter\n", USBDEVNAME(sc->sc_dev), __func__));
+#endif
+ /* Nothing to do */
+}
+
+#if defined(__FreeBSD__)
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+Static void
+udav_shutdown(device_ptr_t dev)
+{
+ struct udav_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ udav_stop_task(sc);
+
+ return;
+}
+
+Static void
+udav_rxstart(struct ifnet *ifp)
+{
+ struct udav_softc *sc;
+ struct udav_chain *c;
+
+ sc = ifp->if_softc;
+ UDAV_LOCK(sc);
+ c = &sc->sc_cdata.udav_rx_chain[sc->sc_cdata.udav_rx_prod];
+
+ if (udav_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ UDAV_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->udav_xfer, sc->sc_pipe_rx,
+ c, c->udav_buf, UDAV_BUFSZ,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, udav_rxeof);
+ usbd_transfer(c->udav_xfer);
+
+ UDAV_UNLOCK(sc);
+ return;
+}
+#endif
diff --git a/sys/dev/usb/if_udavreg.h b/sys/dev/usb/if_udavreg.h
new file mode 100644
index 0000000..7680422
--- /dev/null
+++ b/sys/dev/usb/if_udavreg.h
@@ -0,0 +1,234 @@
+/* $NetBSD: if_udavreg.h,v 1.2 2003/09/04 15:17:39 tsutsui Exp $ */
+/* $nabe: if_udavreg.h,v 1.2 2003/08/21 16:26:40 nabe Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 2003
+ * Shingo WATANABE <nabe@nabechan.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define UDAV_IFACE_INDEX 0
+#define UDAV_CONFIG_NO 1
+
+#define UDAV_TX_LIST_CNT 1
+#define UDAV_RX_LIST_CNT 1
+
+#define UDAV_TX_TIMEOUT 1000
+#define UDAV_TIMEOUT 10000
+
+#define ETHER_ALIGN 2
+
+
+/* Packet length */
+#define UDAV_MAX_MTU 1536 /* XXX: max frame size is unknown */
+#define UDAV_MIN_FRAME_LEN 60
+#define UDAV_BUFSZ UDAV_MAX_MTU
+
+/* Request */
+#define UDAV_REQ_REG_READ 0x00 /* Read from register(s) */
+#define UDAV_REQ_REG_WRITE 0x01 /* Write to register(s) */
+#define UDAV_REQ_REG_WRITE1 0x03 /* Write to a register */
+
+#define UDAV_REQ_MEM_READ 0x02 /* Read from memory */
+#define UDAV_REQ_MEM_WRITE 0x05 /* Write to memory */
+#define UDAV_REQ_MEM_WRITE1 0x07 /* Write a byte to memory */
+
+/* Registers */
+#define UDAV_NCR 0x00 /* Network Control Register */
+#define UDAV_NCR_EXT_PHY (1<<7) /* Select External PHY */
+#define UDAV_NCR_WAKEEN (1<<6) /* Wakeup Event Enable */
+#define UDAV_NCR_FCOL (1<<4) /* Force Collision Mode */
+#define UDAV_NCR_FDX (1<<3) /* Full-Duplex Mode (RO on Int. PHY) */
+#define UDAV_NCR_LBK1 (1<<2) /* Lookback Mode */
+#define UDAV_NCR_LBK0 (1<<1) /* Lookback Mode */
+#define UDAV_NCR_RST (1<<0) /* Software reset */
+
+#define UDAV_RCR 0x05 /* RX Control Register */
+#define UDAV_RCR_WTDIS (1<<6) /* Watchdog Timer Disable */
+#define UDAV_RCR_DIS_LONG (1<<5) /* Discard Long Packet(over 1522Byte) */
+#define UDAV_RCR_DIS_CRC (1<<4) /* Discard CRC Error Packet */
+#define UDAV_RCR_ALL (1<<3) /* Pass All Multicast */
+#define UDAV_RCR_RUNT (1<<2) /* Pass Runt Packet */
+#define UDAV_RCR_PRMSC (1<<1) /* Promiscuous Mode */
+#define UDAV_RCR_RXEN (1<<0) /* RX Enable */
+
+#define UDAV_RSR 0x06 /* RX Status Register */
+#define UDAV_RSR_RF (1<<7) /* Runt Frame */
+#define UDAV_RSR_MF (1<<6) /* Multicast Frame */
+#define UDAV_RSR_LCS (1<<5) /* Late Collision Seen */
+#define UDAV_RSR_RWTO (1<<4) /* Receive Watchdog Time-Out */
+#define UDAV_RSR_PLE (1<<3) /* Physical Layer Error */
+#define UDAV_RSR_AE (1<<2) /* Alignment Error */
+#define UDAV_RSR_CE (1<<1) /* CRC Error */
+#define UDAV_RSR_FOE (1<<0) /* FIFO Overflow Error */
+#define UDAV_RSR_ERR (UDAV_RSR_RF | UDAV_RSR_LCS | UDAV_RSR_RWTO |\
+ UDAV_RSR_PLE | UDAV_RSR_AE | UDAV_RSR_CE |\
+ UDAV_RSR_FOE)
+
+#define UDAV_EPCR 0x0b /* EEPROM & PHY Control Register */
+#define UDAV_EPCR_REEP (1<<5) /* Reload EEPROM */
+#define UDAV_EPCR_WEP (1<<4) /* Write EEPROM enable */
+#define UDAV_EPCR_EPOS (1<<3) /* EEPROM or PHY Operation Select */
+#define UDAV_EPCR_ERPRR (1<<2) /* EEPROM/PHY Register Read Command */
+#define UDAV_EPCR_ERPRW (1<<1) /* EEPROM/PHY Register Write Command */
+#define UDAV_EPCR_ERRE (1<<0) /* EEPROM/PHY Access Status */
+
+#define UDAV_EPAR 0x0c /* EEPROM & PHY Control Register */
+#define UDAV_EPAR_PHY_ADR1 (1<<7) /* PHY Address bit 1 */
+#define UDAV_EPAR_PHY_ADR0 (1<<6) /* PHY Address bit 0 */
+#define UDAV_EPAR_EROA (1<<0) /* EEPROM Word/PHY Register Address */
+#define UDAV_EPAR_EROA_MASK (0x1f) /* [5:0] */
+
+#define UDAV_EPDRL 0x0d /* EEPROM & PHY Data Register */
+#define UDAV_EPDRH 0x0e /* EEPROM & PHY Data Register */
+
+#define UDAV_PAR0 0x10 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR1 0x11 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR2 0x12 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR3 0x13 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR4 0x14 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR5 0x15 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR UDAV_PAR0
+
+#define UDAV_MAR0 0x16 /* Multicast Register */
+#define UDAV_MAR1 0x17 /* Multicast Register */
+#define UDAV_MAR2 0x18 /* Multicast Register */
+#define UDAV_MAR3 0x19 /* Multicast Register */
+#define UDAV_MAR4 0x1a /* Multicast Register */
+#define UDAV_MAR5 0x1b /* Multicast Register */
+#define UDAV_MAR6 0x1c /* Multicast Register */
+#define UDAV_MAR7 0x1d /* Multicast Register */
+#define UDAV_MAR UDAV_MAR0
+
+#define UDAV_GPCR 0x1e /* General purpose control register */
+#define UDAV_GPCR_GEP_CNTL6 (1<<6) /* General purpose control 6 */
+#define UDAV_GPCR_GEP_CNTL5 (1<<5) /* General purpose control 5 */
+#define UDAV_GPCR_GEP_CNTL4 (1<<4) /* General purpose control 4 */
+#define UDAV_GPCR_GEP_CNTL3 (1<<3) /* General purpose control 3 */
+#define UDAV_GPCR_GEP_CNTL2 (1<<2) /* General purpose control 2 */
+#define UDAV_GPCR_GEP_CNTL1 (1<<1) /* General purpose control 1 */
+#define UDAV_GPCR_GEP_CNTL0 (1<<0) /* General purpose control 0 */
+
+#define UDAV_GPR 0x1f /* General purpose register */
+#define UDAV_GPR_GEPIO6 (1<<6) /* General purpose 6 */
+#define UDAV_GPR_GEPIO5 (1<<5) /* General purpose 5 */
+#define UDAV_GPR_GEPIO4 (1<<4) /* General purpose 4 */
+#define UDAV_GPR_GEPIO3 (1<<3) /* General purpose 3 */
+#define UDAV_GPR_GEPIO2 (1<<2) /* General purpose 2 */
+#define UDAV_GPR_GEPIO1 (1<<1) /* General purpose 1 */
+#define UDAV_GPR_GEPIO0 (1<<0) /* General purpose 0 */
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#define GET_IFP(sc) (&(sc)->sc_ac.ac_if)
+#elif defined(__NetBSD__)
+#define GET_IFP(sc) (&(sc)->sc_ec.ec_if)
+#endif
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->sc_miibus))
+#else
+#define GET_MII(sc) (&(sc)->sc_mii)
+#endif
+
+#if defined(__FreeBSD__)
+#if 0
+#define UDAV_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define UDAV_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#else
+#define UDAV_LOCK(_sc)
+#define UDAV_UNLOCK(_sc)
+#endif
+#endif
+
+struct udav_chain {
+ struct udav_softc *udav_sc;
+ usbd_xfer_handle udav_xfer;
+ char *udav_buf;
+ struct mbuf *udav_mbuf;
+ int udav_idx;
+};
+
+struct udav_cdata {
+ struct udav_chain udav_tx_chain[UDAV_TX_LIST_CNT];
+ struct udav_chain udav_rx_chain[UDAV_TX_LIST_CNT];
+#if 0
+ /* XXX: Intrrupt Endpoint is not yet supported! */
+ struct udav_intrpkg udav_ibuf;
+#endif
+ int udav_tx_prod;
+ int udav_tx_cons;
+ int udav_tx_cnt;
+ int udav_rx_prod;
+};
+
+struct udav_softc {
+#if defined(__FreeBSD__)
+ struct arpcom sc_ac ; /* struct ifnet must be top of softc */
+#endif
+ USBBASEDEVICE sc_dev; /* base device */
+ usbd_device_handle sc_udev;
+
+ /* USB */
+ usbd_interface_handle sc_ctl_iface;
+ /* int sc_ctl_iface_no; */
+ int sc_bulkin_no; /* bulk in endpoint */
+ int sc_bulkout_no; /* bulk out endpoint */
+ int sc_intrin_no; /* intr in endpoint */
+ usbd_pipe_handle sc_pipe_rx;
+ usbd_pipe_handle sc_pipe_tx;
+ usbd_pipe_handle sc_pipe_intr;
+ usb_callout_t sc_stat_ch;
+ u_int sc_rx_errs;
+ /* u_int sc_intr_errs; */
+ struct timeval sc_rx_notice;
+
+ /* Ethernet */
+
+#if defined(__FreeBSD__)
+ device_t sc_miibus ;
+ struct mtx sc_mtx ;
+ struct usb_qdat sc_qdat;
+#elif defined(__NetBSD__)
+ struct ethercom sc_ec; /* ethernet common */
+ struct mii_data sc_mii;
+#endif
+ struct lock sc_mii_lock;
+ int sc_link;
+#define sc_media udav_mii.mii_media
+#if NRND > 0
+ rndsource_element_t rnd_source;
+#endif
+ struct udav_cdata sc_cdata;
+
+ int sc_attached;
+ int sc_dying;
+ int sc_refcnt;
+
+ struct usb_task sc_tick_task;
+ struct usb_task sc_stop_task;
+
+ u_int16_t sc_flags;
+};
diff --git a/sys/dev/usb/kue_fw.h b/sys/dev/usb/kue_fw.h
new file mode 100644
index 0000000..8d8c1e1
--- /dev/null
+++ b/sys/dev/usb/kue_fw.h
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This file contains the firmware needed to make the KLSI chip work,
+ * along with a few constants related to the QT Engine microcontroller
+ * embedded in the KLSI part.
+ *
+ * Firmware is loaded using the vendor-specific 'send scan data'
+ * command (0xFF). The basic operation is that we must load the
+ * firmware, then issue some trigger commands to fix it up and start
+ * it running. There are three transfers: load the binary code,
+ * load the 'fixup' (data segment?), then issue a command to
+ * start the code firmware running. The data itself is prefixed by
+ * a 16-bit signature word, a 16-bit length value, a type byte
+ * and an interrupt (command) byte. The code segment is of type
+ * 0x02 (replacement interrupt vector data) and the fixup segment
+ * is of type 0x03 (replacement interrupt fixup data). The interrupt
+ * code is 0x64 (load new code). The length word is the total length
+ * of the segment minus 7. I precomputed the values and stuck them
+ * into the appropriate locations within the segments to save some
+ * work in the driver.
+ */
+
+/* QT controller data block types. */
+/* Write data into specific memory location. */
+#define KUE_QTBTYPE_WRITE_DATA 0x00
+/* Write data into interrupt vector location */
+#define KUE_QTBTYPE_WRITE_INTVEC 0x01
+/* Replace interrupt vector with this data */
+#define KUE_QTBTYPE_REPL_INTVEC 0x02
+/* Fixup interrupt vector code with this data */
+#define KUE_QTBTYPE_FIXUP_INTVEC 0x03
+/* Force jump to location */
+#define KUE_QTBTYPE_JUMP 0x04
+/* Force call to location */
+#define KUE_QTBTYPE_CALL 0x05
+/* Force interrupt call */
+#define KUE_QTBTYPE_CALLINTR 0x06
+/*
+ * Cause data to be written using the specified QT engine
+ * interrupt, from starting location in memory for a specified
+ * number of bytes.
+ */
+#define KUE_QTBTYPE_WRITE_WITH_INTR 0x07
+/* Cause data from stream to be written using specified QT interrupt. */
+#define KUE_QTBTYPE_WRITE_STR_WITH_INTR 0x08
+/* Cause data to be written to config locations. */
+/* Addresses assume 0xc000 offset. */
+#define KUE_QTBTYPE_WRITE_CONFIG 0x09
+
+#define KUE_QTINTR_LOAD_CODE 0x64
+#define KUE_QTINTR_TRIGGER_CODE 0x3B
+#define KUE_QTINTR_LOAD_CODE_HIGH 0x9C
+
+/* Firmware code segment */
+Static unsigned char kue_code_seg[] =
+{
+ /******************************************/
+ /* NOTE: B6/C3 is data header signature */
+ /* 0xAA/0xBB is data length = total */
+ /* bytes - 7, 0xCC is type, 0xDD is */
+ /* interrupt to use. */
+ /******************************************/
+ 0xB6, 0xC3, 0xf7, 0x0e, 0x02, 0x64,
+ 0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00,
+ 0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f,
+ 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09,
+ 0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08,
+ 0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0,
+ 0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08,
+ 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1,
+ 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+ 0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00,
+ 0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf,
+ 0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09,
+ 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0,
+ 0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08,
+ 0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08,
+ 0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57,
+ 0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08,
+ 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00,
+ 0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09,
+ 0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59,
+ 0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00,
+ 0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1,
+ 0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03,
+ 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1,
+ 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08,
+ 0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04,
+ 0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09,
+ 0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00,
+ 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57,
+ 0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08,
+ 0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08,
+ 0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00,
+ 0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00,
+ 0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf,
+ 0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17,
+ 0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08,
+ 0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08,
+ 0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0,
+ 0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37,
+ 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60,
+ 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9,
+ 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17,
+ 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00,
+ 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00,
+ 0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00,
+ 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04,
+ 0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf,
+ 0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf,
+ 0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02,
+ 0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf,
+ 0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57,
+ 0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57,
+ 0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09,
+ 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02,
+ 0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01,
+ 0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02,
+ 0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1,
+ 0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77,
+ 0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02,
+ 0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77,
+ 0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07,
+ 0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00,
+ 0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77,
+ 0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda,
+ 0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0,
+ 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf,
+ 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8,
+ 0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77,
+ 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50,
+ 0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00,
+ 0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02,
+ 0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0,
+ 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00,
+ 0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e,
+ 0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0,
+ 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+ 0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07,
+ 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e,
+ 0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08,
+ 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07,
+ 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08,
+ 0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09,
+ 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07,
+ 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00,
+ 0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf,
+ 0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08,
+ 0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05,
+ 0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08,
+ 0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67,
+ 0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0,
+ 0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00,
+ 0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08,
+ 0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80,
+ 0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08,
+ 0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+ 0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf,
+ 0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0,
+ 0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00,
+ 0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00,
+ 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17,
+ 0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07,
+ 0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+ 0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03,
+ 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0,
+ 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07,
+ 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00,
+ 0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08,
+ 0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08,
+ 0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02,
+ 0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08,
+ 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00,
+ 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00,
+ 0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00,
+ 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00,
+ 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+ 0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09,
+ 0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52,
+ 0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08,
+ 0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17,
+ 0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05,
+ 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04,
+ 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62,
+ 0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02,
+ 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17,
+ 0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77,
+ 0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9,
+ 0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1,
+ 0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27,
+ 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27,
+ 0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00,
+ 0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff,
+ 0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17,
+ 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07,
+ 0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1,
+ 0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59,
+ 0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf,
+ 0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08,
+ 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf,
+ 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07,
+ 0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77,
+ 0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00,
+ 0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf,
+ 0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00,
+ 0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00,
+ 0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0,
+ 0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77,
+ 0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08,
+ 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf,
+ 0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1,
+ 0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1,
+ 0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf,
+ 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
+ 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09,
+ 0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53,
+ 0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05,
+ 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8,
+ 0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf,
+ 0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30,
+ 0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf,
+ 0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30,
+ 0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf,
+ 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09,
+ 0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09,
+ 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0,
+ 0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01,
+ 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07,
+ 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0,
+ 0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37,
+ 0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08,
+ 0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+ 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+ 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08,
+ 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08,
+ 0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0,
+ 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00,
+ 0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02,
+ 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00,
+ 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00,
+ 0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0,
+ 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+ 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04,
+ 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09,
+ 0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00,
+ 0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02,
+ 0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05,
+ 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57,
+ 0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf,
+ 0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0,
+ 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09,
+ 0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+ 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+ 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+ 0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00,
+ 0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf,
+ 0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17,
+ 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57,
+ 0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77,
+ 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+ 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00,
+ 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17,
+ 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1,
+ 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02,
+ 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77,
+ 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+ 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00,
+ 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf,
+ 0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57,
+ 0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8,
+ 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+ 0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08,
+ 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09,
+ 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77,
+ 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57,
+ 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19,
+ 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17,
+ 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf,
+ 0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57,
+ 0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b,
+ 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05,
+ 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09,
+ 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04,
+ 0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1,
+ 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf,
+ 0x68, 0x04, 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80,
+ 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e,
+ 0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00,
+ 0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09,
+ 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00,
+ 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07,
+ 0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13,
+ 0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08,
+ 0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09,
+ 0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09,
+ 0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b,
+ 0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00,
+ 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00,
+ 0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf,
+ 0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90,
+ 0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00,
+ 0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b,
+ 0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00,
+ 0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07,
+ 0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00,
+ 0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00,
+ 0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf,
+ 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0,
+ 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+ 0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf,
+ 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08,
+ 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf,
+ 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94,
+ 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09,
+ 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf,
+ 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00,
+ 0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00,
+ 0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00,
+ 0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00,
+ 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0,
+ 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0,
+ 0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a,
+ 0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a,
+ 0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b,
+ 0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02,
+ 0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00,
+ 0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57,
+ 0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37,
+ 0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2,
+ 0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00,
+ 0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57,
+ 0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b,
+ 0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09,
+ 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07,
+ 0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08,
+ 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08,
+ 0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0,
+ 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00,
+ 0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08,
+ 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf,
+ 0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09,
+ 0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08,
+ 0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c,
+ 0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf,
+ 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2,
+ 0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08,
+ 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07,
+ 0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb,
+ 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf,
+ 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57,
+ 0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07,
+ 0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00,
+ 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08,
+ 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07,
+ 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf,
+ 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00,
+ 0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf,
+ 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1,
+ 0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf,
+ 0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf,
+ 0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07,
+ 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00,
+ 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00,
+ 0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02,
+ 0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08,
+ 0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80,
+ 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf,
+ 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08,
+ 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf,
+ 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf,
+ 0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf,
+ 0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07,
+ 0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1,
+ 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00,
+ 0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde,
+ 0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2,
+ 0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08,
+ 0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf,
+ 0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08,
+ 0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde,
+ 0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08,
+ 0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57,
+ 0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0,
+ 0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67,
+ 0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00,
+ 0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0,
+ 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+ 0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf,
+ 0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08,
+ 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0,
+ 0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00,
+ 0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08,
+ 0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00,
+ 0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07,
+ 0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00,
+ 0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09,
+ 0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07,
+ 0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1,
+ 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf,
+ 0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07,
+ 0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08,
+ 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00,
+ 0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00,
+ 0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09,
+ 0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02,
+ 0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda,
+ 0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08,
+ 0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08,
+ 0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02,
+ 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1,
+ 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0,
+ 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08,
+ 0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08,
+ 0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08,
+ 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08,
+ 0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07,
+ 0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+ 0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+ 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+ 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+ 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00,
+ 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00,
+ 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+ 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+ 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07,
+ 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09,
+ 0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00,
+ 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0,
+ 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07,
+ 0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf,
+ 0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf,
+ 0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00,
+ 0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d,
+ 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1,
+ 0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87,
+ 0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08,
+ 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0,
+ 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87,
+ 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7,
+ 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0,
+ 0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0,
+ 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09,
+ 0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2,
+ 0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf,
+ 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02,
+ 0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08,
+ 0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77,
+ 0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf,
+ 0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07,
+ 0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e,
+ 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0,
+ 0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77,
+ 0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57,
+ 0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb,
+ 0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00,
+ 0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb,
+ 0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09,
+ 0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00,
+ 0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08,
+ 0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09,
+ 0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02,
+ 0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05,
+ 0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1,
+ 0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02,
+ 0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00,
+ 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04,
+ 0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00,
+ 0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf,
+ 0, 0
+};
+
+/* Firmware fixup (data?) segment */
+Static unsigned char kue_fix_seg[] =
+{
+ /******************************************/
+ /* NOTE: B6/C3 is data header signature */
+ /* 0xAA/0xBB is data length = total */
+ /* bytes - 7, 0xCC is type, 0xDD is */
+ /* interrupt to use. */
+ /******************************************/
+ 0xB6, 0xC3, 0xc9, 0x02, 0x03, 0x64,
+ 0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00,
+ 0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00,
+ 0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00,
+ 0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00,
+ 0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00,
+ 0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00,
+ 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00,
+ 0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00,
+ 0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00,
+ 0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00,
+ 0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01,
+ 0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01,
+ 0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01,
+ 0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01,
+ 0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01,
+ 0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01,
+ 0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02,
+ 0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02,
+ 0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02,
+ 0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02,
+ 0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03,
+ 0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03,
+ 0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03,
+ 0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03,
+ 0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03,
+ 0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03,
+ 0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03,
+ 0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04,
+ 0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04,
+ 0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04,
+ 0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04,
+ 0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04,
+ 0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05,
+ 0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05,
+ 0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05,
+ 0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05,
+ 0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06,
+ 0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06,
+ 0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06,
+ 0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06,
+ 0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06,
+ 0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06,
+ 0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06,
+ 0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07,
+ 0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07,
+ 0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08,
+ 0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08,
+ 0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08,
+ 0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08,
+ 0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09,
+ 0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09,
+ 0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09,
+ 0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09,
+ 0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09,
+ 0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09,
+ 0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09,
+ 0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09,
+ 0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a,
+ 0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a,
+ 0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a,
+ 0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a,
+ 0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a,
+ 0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a,
+ 0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a,
+ 0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a,
+ 0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a,
+ 0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a,
+ 0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b,
+ 0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b,
+ 0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b,
+ 0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b,
+ 0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b,
+ 0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b,
+ 0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c,
+ 0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c,
+ 0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c,
+ 0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c,
+ 0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c,
+ 0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c,
+ 0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c,
+ 0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c,
+ 0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d,
+ 0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d,
+ 0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d,
+ 0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d,
+ 0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d,
+ 0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e,
+ 0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e,
+ 0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e,
+ 0, 0
+};
+
+/* Fixup command. */
+#define KUE_TRIGCMD_OFFSET 5
+Static unsigned char kue_trig_seg[] = {
+0xb6, 0xc3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00
+};
diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c
new file mode 100644
index 0000000..a8dfe3f
--- /dev/null
+++ b/sys/dev/usb/ohci.c
@@ -0,0 +1,3573 @@
+/* $NetBSD: ohci.c,v 1.138 2003/02/08 03:32:50 ichiro Exp $ */
+
+/* Also, already ported:
+ * $NetBSD: ohci.c,v 1.140 2003/05/13 04:42:00 gson Exp $
+ * $NetBSD: ohci.c,v 1.141 2003/09/10 20:08:29 mycroft Exp $
+ * $NetBSD: ohci.c,v 1.142 2003/10/11 03:04:26 toshii Exp $
+ * $NetBSD: ohci.c,v 1.143 2003/10/18 04:50:35 simonb Exp $
+ * $NetBSD: ohci.c,v 1.144 2003/11/23 19:18:06 augustss Exp $
+ * $NetBSD: ohci.c,v 1.145 2003/11/23 19:20:25 augustss Exp $
+ * $NetBSD: ohci.c,v 1.146 2003/12/29 08:17:10 toshii Exp $
+ * $NetBSD: ohci.c,v 1.147 2004/06/22 07:20:35 mycroft Exp $
+ * $NetBSD: ohci.c,v 1.148 2004/06/22 18:27:46 mycroft Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 Open Host Controller driver.
+ *
+ * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#include <sys/select.h>
+#elif defined(__FreeBSD__)
+#include <sys/endian.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/bus_pio.h>
+#include <machine/bus_memio.h>
+#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__)
+#include <machine/cpu.h>
+#endif
+#endif
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/endian.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/ohcireg.h>
+#include <dev/usb/ohcivar.h>
+
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+
+#define delay(d) DELAY(d)
+#endif
+
+#if defined(__OpenBSD__)
+struct cfdriver ohci_cd = {
+ NULL, "ohci", DV_DULL
+};
+#endif
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ohcidebug) logprintf x
+#define DPRINTFN(n,x) if (ohcidebug>(n)) logprintf x
+int ohcidebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci");
+SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RW,
+ &ohcidebug, 0, "ohci debug level");
+#ifndef __NetBSD__
+#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f))
+#endif
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+/*
+ * The OHCI controller is little endian, so on big endian machines
+ * the data strored in memory needs to be swapped.
+ */
+#if defined(__OpenBSD__)
+#if BYTE_ORDER == BIG_ENDIAN
+#define htole32(x) (bswap32(x))
+#define le32toh(x) (bswap32(x))
+#else
+#define htole32(x) (x)
+#define le32toh(x) (x)
+#endif
+#endif
+
+struct ohci_pipe;
+
+Static ohci_soft_ed_t *ohci_alloc_sed(ohci_softc_t *);
+Static void ohci_free_sed(ohci_softc_t *, ohci_soft_ed_t *);
+
+Static ohci_soft_td_t *ohci_alloc_std(ohci_softc_t *);
+Static void ohci_free_std(ohci_softc_t *, ohci_soft_td_t *);
+
+Static ohci_soft_itd_t *ohci_alloc_sitd(ohci_softc_t *);
+Static void ohci_free_sitd(ohci_softc_t *,ohci_soft_itd_t *);
+
+#if 0
+Static void ohci_free_std_chain(ohci_softc_t *, ohci_soft_td_t *,
+ ohci_soft_td_t *);
+#endif
+Static usbd_status ohci_alloc_std_chain(struct ohci_pipe *,
+ ohci_softc_t *, int, int, usbd_xfer_handle,
+ ohci_soft_td_t *, ohci_soft_td_t **);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+Static void ohci_shutdown(void *v);
+Static void ohci_power(int, void *);
+#endif
+Static usbd_status ohci_open(usbd_pipe_handle);
+Static void ohci_poll(struct usbd_bus *);
+Static void ohci_softintr(void *);
+Static void ohci_waitintr(ohci_softc_t *, usbd_xfer_handle);
+Static void ohci_add_done(ohci_softc_t *, ohci_physaddr_t);
+Static void ohci_rhsc(ohci_softc_t *, usbd_xfer_handle);
+
+Static usbd_status ohci_device_request(usbd_xfer_handle xfer);
+Static void ohci_add_ed(ohci_soft_ed_t *, ohci_soft_ed_t *);
+Static void ohci_rem_ed(ohci_soft_ed_t *, ohci_soft_ed_t *);
+Static void ohci_hash_add_td(ohci_softc_t *, ohci_soft_td_t *);
+Static void ohci_hash_rem_td(ohci_softc_t *, ohci_soft_td_t *);
+Static ohci_soft_td_t *ohci_hash_find_td(ohci_softc_t *, ohci_physaddr_t);
+Static void ohci_hash_add_itd(ohci_softc_t *, ohci_soft_itd_t *);
+Static void ohci_hash_rem_itd(ohci_softc_t *, ohci_soft_itd_t *);
+Static ohci_soft_itd_t *ohci_hash_find_itd(ohci_softc_t *, ohci_physaddr_t);
+
+Static usbd_status ohci_setup_isoc(usbd_pipe_handle pipe);
+Static void ohci_device_isoc_enter(usbd_xfer_handle);
+
+Static usbd_status ohci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t);
+Static void ohci_freem(struct usbd_bus *, usb_dma_t *);
+
+Static usbd_xfer_handle ohci_allocx(struct usbd_bus *);
+Static void ohci_freex(struct usbd_bus *, usbd_xfer_handle);
+
+Static usbd_status ohci_root_ctrl_transfer(usbd_xfer_handle);
+Static usbd_status ohci_root_ctrl_start(usbd_xfer_handle);
+Static void ohci_root_ctrl_abort(usbd_xfer_handle);
+Static void ohci_root_ctrl_close(usbd_pipe_handle);
+Static void ohci_root_ctrl_done(usbd_xfer_handle);
+
+Static usbd_status ohci_root_intr_transfer(usbd_xfer_handle);
+Static usbd_status ohci_root_intr_start(usbd_xfer_handle);
+Static void ohci_root_intr_abort(usbd_xfer_handle);
+Static void ohci_root_intr_close(usbd_pipe_handle);
+Static void ohci_root_intr_done(usbd_xfer_handle);
+
+Static usbd_status ohci_device_ctrl_transfer(usbd_xfer_handle);
+Static usbd_status ohci_device_ctrl_start(usbd_xfer_handle);
+Static void ohci_device_ctrl_abort(usbd_xfer_handle);
+Static void ohci_device_ctrl_close(usbd_pipe_handle);
+Static void ohci_device_ctrl_done(usbd_xfer_handle);
+
+Static usbd_status ohci_device_bulk_transfer(usbd_xfer_handle);
+Static usbd_status ohci_device_bulk_start(usbd_xfer_handle);
+Static void ohci_device_bulk_abort(usbd_xfer_handle);
+Static void ohci_device_bulk_close(usbd_pipe_handle);
+Static void ohci_device_bulk_done(usbd_xfer_handle);
+
+Static usbd_status ohci_device_intr_transfer(usbd_xfer_handle);
+Static usbd_status ohci_device_intr_start(usbd_xfer_handle);
+Static void ohci_device_intr_abort(usbd_xfer_handle);
+Static void ohci_device_intr_close(usbd_pipe_handle);
+Static void ohci_device_intr_done(usbd_xfer_handle);
+
+Static usbd_status ohci_device_isoc_transfer(usbd_xfer_handle);
+Static usbd_status ohci_device_isoc_start(usbd_xfer_handle);
+Static void ohci_device_isoc_abort(usbd_xfer_handle);
+Static void ohci_device_isoc_close(usbd_pipe_handle);
+Static void ohci_device_isoc_done(usbd_xfer_handle);
+
+Static usbd_status ohci_device_setintr(ohci_softc_t *sc,
+ struct ohci_pipe *pipe, int ival);
+
+Static int ohci_str(usb_string_descriptor_t *, int, const char *);
+
+Static void ohci_timeout(void *);
+Static void ohci_timeout_task(void *);
+Static void ohci_rhsc_able(ohci_softc_t *, int);
+Static void ohci_rhsc_enable(void *);
+
+Static void ohci_close_pipe(usbd_pipe_handle, ohci_soft_ed_t *);
+Static void ohci_abort_xfer(usbd_xfer_handle, usbd_status);
+
+Static void ohci_device_clear_toggle(usbd_pipe_handle pipe);
+Static void ohci_noop(usbd_pipe_handle pipe);
+
+Static usbd_status ohci_controller_init(ohci_softc_t *sc);
+
+#ifdef USB_DEBUG
+Static void ohci_dumpregs(ohci_softc_t *);
+Static void ohci_dump_tds(ohci_soft_td_t *);
+Static void ohci_dump_td(ohci_soft_td_t *);
+Static void ohci_dump_ed(ohci_soft_ed_t *);
+Static void ohci_dump_itd(ohci_soft_itd_t *);
+Static void ohci_dump_itds(ohci_soft_itd_t *);
+#endif
+
+#define OBARR(sc) bus_space_barrier((sc)->iot, (sc)->ioh, 0, (sc)->sc_size, \
+ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
+#define OWRITE1(sc, r, x) \
+ do { OBARR(sc); bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)); } while (0)
+#define OWRITE2(sc, r, x) \
+ do { OBARR(sc); bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)); } while (0)
+#define OWRITE4(sc, r, x) \
+ do { OBARR(sc); bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)); } while (0)
+#define OREAD1(sc, r) (OBARR(sc), bus_space_read_1((sc)->iot, (sc)->ioh, (r)))
+#define OREAD2(sc, r) (OBARR(sc), bus_space_read_2((sc)->iot, (sc)->ioh, (r)))
+#define OREAD4(sc, r) (OBARR(sc), bus_space_read_4((sc)->iot, (sc)->ioh, (r)))
+
+/* Reverse the bits in a value 0 .. 31 */
+Static u_int8_t revbits[OHCI_NO_INTRS] =
+ { 0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c,
+ 0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e,
+ 0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d,
+ 0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f };
+
+struct ohci_pipe {
+ struct usbd_pipe pipe;
+ ohci_soft_ed_t *sed;
+ u_int32_t aborting;
+ union {
+ ohci_soft_td_t *td;
+ ohci_soft_itd_t *itd;
+ } tail;
+ /* Info needed for different pipe kinds. */
+ union {
+ /* Control pipe */
+ struct {
+ usb_dma_t reqdma;
+ u_int length;
+ ohci_soft_td_t *setup, *data, *stat;
+ } ctl;
+ /* Interrupt pipe */
+ struct {
+ int nslots;
+ int pos;
+ } intr;
+ /* Bulk pipe */
+ struct {
+ u_int length;
+ int isread;
+ } bulk;
+ /* Iso pipe */
+ struct iso {
+ int next, inuse;
+ } iso;
+ } u;
+};
+
+#define OHCI_INTR_ENDPT 1
+
+Static struct usbd_bus_methods ohci_bus_methods = {
+ ohci_open,
+ ohci_softintr,
+ ohci_poll,
+ ohci_allocm,
+ ohci_freem,
+ ohci_allocx,
+ ohci_freex,
+};
+
+Static struct usbd_pipe_methods ohci_root_ctrl_methods = {
+ ohci_root_ctrl_transfer,
+ ohci_root_ctrl_start,
+ ohci_root_ctrl_abort,
+ ohci_root_ctrl_close,
+ ohci_noop,
+ ohci_root_ctrl_done,
+};
+
+Static struct usbd_pipe_methods ohci_root_intr_methods = {
+ ohci_root_intr_transfer,
+ ohci_root_intr_start,
+ ohci_root_intr_abort,
+ ohci_root_intr_close,
+ ohci_noop,
+ ohci_root_intr_done,
+};
+
+Static struct usbd_pipe_methods ohci_device_ctrl_methods = {
+ ohci_device_ctrl_transfer,
+ ohci_device_ctrl_start,
+ ohci_device_ctrl_abort,
+ ohci_device_ctrl_close,
+ ohci_noop,
+ ohci_device_ctrl_done,
+};
+
+Static struct usbd_pipe_methods ohci_device_intr_methods = {
+ ohci_device_intr_transfer,
+ ohci_device_intr_start,
+ ohci_device_intr_abort,
+ ohci_device_intr_close,
+ ohci_device_clear_toggle,
+ ohci_device_intr_done,
+};
+
+Static struct usbd_pipe_methods ohci_device_bulk_methods = {
+ ohci_device_bulk_transfer,
+ ohci_device_bulk_start,
+ ohci_device_bulk_abort,
+ ohci_device_bulk_close,
+ ohci_device_clear_toggle,
+ ohci_device_bulk_done,
+};
+
+Static struct usbd_pipe_methods ohci_device_isoc_methods = {
+ ohci_device_isoc_transfer,
+ ohci_device_isoc_start,
+ ohci_device_isoc_abort,
+ ohci_device_isoc_close,
+ ohci_noop,
+ ohci_device_isoc_done,
+};
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+ohci_activate(device_ptr_t self, enum devact act)
+{
+ struct ohci_softc *sc = (struct ohci_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ if (sc->sc_child != NULL)
+ rv = config_deactivate(sc->sc_child);
+ sc->sc_dying = 1;
+ break;
+ }
+ return (rv);
+}
+
+int
+ohci_detach(struct ohci_softc *sc, int flags)
+{
+ int rv = 0;
+
+ if (sc->sc_child != NULL)
+ rv = config_detach(sc->sc_child, flags);
+
+ if (rv != 0)
+ return (rv);
+
+ usb_uncallout(sc->sc_tmo_rhsc, ohci_rhsc_enable, sc);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ powerhook_disestablish(sc->sc_powerhook);
+ shutdownhook_disestablish(sc->sc_shutdownhook);
+#endif
+
+ usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
+
+ /* free data structures XXX */
+
+ return (rv);
+}
+#endif
+
+ohci_soft_ed_t *
+ohci_alloc_sed(ohci_softc_t *sc)
+{
+ ohci_soft_ed_t *sed;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeeds == NULL) {
+ DPRINTFN(2, ("ohci_alloc_sed: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, OHCI_SED_SIZE * OHCI_SED_CHUNK,
+ OHCI_ED_ALIGN, &dma);
+ if (err)
+ return (NULL);
+ for(i = 0; i < OHCI_SED_CHUNK; i++) {
+ offs = i * OHCI_SED_SIZE;
+ sed = KERNADDR(&dma, offs);
+ sed->physaddr = DMAADDR(&dma, offs);
+ sed->next = sc->sc_freeeds;
+ sc->sc_freeeds = sed;
+ }
+ }
+ sed = sc->sc_freeeds;
+ sc->sc_freeeds = sed->next;
+ memset(&sed->ed, 0, sizeof(ohci_ed_t));
+ sed->next = 0;
+ return (sed);
+}
+
+void
+ohci_free_sed(ohci_softc_t *sc, ohci_soft_ed_t *sed)
+{
+ sed->next = sc->sc_freeeds;
+ sc->sc_freeeds = sed;
+}
+
+ohci_soft_td_t *
+ohci_alloc_std(ohci_softc_t *sc)
+{
+ ohci_soft_td_t *std;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+ int s;
+
+ if (sc->sc_freetds == NULL) {
+ DPRINTFN(2, ("ohci_alloc_std: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, OHCI_STD_SIZE * OHCI_STD_CHUNK,
+ OHCI_TD_ALIGN, &dma);
+ if (err)
+ return (NULL);
+ s = splusb();
+ for(i = 0; i < OHCI_STD_CHUNK; i++) {
+ offs = i * OHCI_STD_SIZE;
+ std = KERNADDR(&dma, offs);
+ std->physaddr = DMAADDR(&dma, offs);
+ std->nexttd = sc->sc_freetds;
+ sc->sc_freetds = std;
+ }
+ splx(s);
+ }
+
+ s = splusb();
+ std = sc->sc_freetds;
+ sc->sc_freetds = std->nexttd;
+ memset(&std->td, 0, sizeof(ohci_td_t));
+ std->nexttd = NULL;
+ std->xfer = NULL;
+ ohci_hash_add_td(sc, std);
+ splx(s);
+
+ return (std);
+}
+
+void
+ohci_free_std(ohci_softc_t *sc, ohci_soft_td_t *std)
+{
+ int s;
+
+ s = splusb();
+ ohci_hash_rem_td(sc, std);
+ std->nexttd = sc->sc_freetds;
+ sc->sc_freetds = std;
+ splx(s);
+}
+
+usbd_status
+ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
+ int alen, int rd, usbd_xfer_handle xfer,
+ ohci_soft_td_t *sp, ohci_soft_td_t **ep)
+{
+ ohci_soft_td_t *next, *cur;
+ ohci_physaddr_t dataphys;
+ u_int32_t tdflags;
+ int offset = 0;
+ int len, curlen;
+ usb_dma_t *dma = &xfer->dmabuf;
+ u_int16_t flags = xfer->flags;
+
+ DPRINTFN(alen < 4096,("ohci_alloc_std_chain: start len=%d\n", alen));
+
+ len = alen;
+ cur = sp;
+
+ tdflags = htole32(
+ (rd ? OHCI_TD_IN : OHCI_TD_OUT) |
+ (flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0) |
+ OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_NOINTR);
+
+ for (;;) {
+ next = ohci_alloc_std(sc);
+ if (next == NULL)
+ goto nomem;
+
+ dataphys = DMAADDR(dma, offset);
+
+ /*
+ * The OHCI hardware can handle at most one 4k crossing.
+ * XXX - currently we only allocate contigous buffers, but
+ * the OHCI spec says: If during the data transfer the buffer
+ * address contained in the HC's working copy of
+ * CurrentBufferPointer crosses a 4K boundary, the upper 20
+ * bits of Buffer End are copied to the working value of
+ * CurrentBufferPointer causing the next buffer address to
+ * be the 0th byte in the same 4K page that contains the
+ * last byte of the buffer (the 4K boundary crossing may
+ * occur within a data packet transfer.)
+ *
+ * If/when dma has multiple segments, this will need to
+ * properly handle fragmenting TD's.
+ *
+ * We can describe the above using maxsegsz = 4k and nsegs = 2
+ * in the future.
+ */
+ if (OHCI_PAGE(dataphys) == OHCI_PAGE(DMAADDR(dma, offset +
+ len - 1)) || len - (OHCI_PAGE_SIZE -
+ OHCI_PAGE_OFFSET(dataphys)) <= OHCI_PAGE_SIZE) {
+ /* we can handle it in this TD */
+ curlen = len;
+ } else {
+ /* XXX The calculation below is wrong and could
+ * result in a packet that is not a multiple of the
+ * MaxPacketSize in the case where the buffer does not
+ * start on an appropriate address (like for example in
+ * the case of an mbuf cluster). You'll get an early
+ * short packet.
+ */
+ /* must use multiple TDs, fill as much as possible. */
+ curlen = 2 * OHCI_PAGE_SIZE -
+ OHCI_PAGE_OFFSET(dataphys);
+ /* the length must be a multiple of the max size */
+ curlen -= curlen %
+ UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize);
+#ifdef DIAGNOSTIC
+ if (curlen == 0)
+ panic("ohci_alloc_std: curlen == 0");
+#endif
+ }
+ DPRINTFN(4,("ohci_alloc_std_chain: dataphys=0x%08x "
+ "len=%d curlen=%d\n",
+ dataphys, len, curlen));
+ len -= curlen;
+
+ cur->td.td_flags = tdflags;
+ cur->td.td_cbp = htole32(dataphys);
+ cur->nexttd = next;
+ cur->td.td_nexttd = htole32(next->physaddr);
+ cur->td.td_be = htole32(DMAADDR(dma, offset + curlen - 1));
+ cur->len = curlen;
+ cur->flags = OHCI_ADD_LEN;
+ cur->xfer = xfer;
+ DPRINTFN(10,("ohci_alloc_std_chain: cbp=0x%08x be=0x%08x\n",
+ dataphys, dataphys + curlen - 1));
+ if (len == 0)
+ break;
+ if (len < 0)
+ panic("Length went negative: %d curlen %d dma %p offset %08x", len, curlen, dma, (int)0);
+
+ DPRINTFN(10,("ohci_alloc_std_chain: extend chain\n"));
+ offset += curlen;
+ cur = next;
+ }
+ if ((flags & USBD_FORCE_SHORT_XFER) &&
+ alen % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize) == 0) {
+ /* Force a 0 length transfer at the end. */
+
+ cur = next;
+
+ next = ohci_alloc_std(sc);
+ if (next == NULL)
+ goto nomem;
+
+ cur->td.td_flags = tdflags;
+ cur->td.td_cbp = 0; /* indicate 0 length packet */
+ cur->nexttd = next;
+ cur->td.td_nexttd = htole32(next->physaddr);
+ cur->td.td_be = ~0;
+ cur->len = 0;
+ cur->flags = 0;
+ cur->xfer = xfer;
+ DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n"));
+ }
+ *ep = cur;
+
+ return (USBD_NORMAL_COMPLETION);
+
+ nomem:
+ /* XXX free chain */
+ return (USBD_NOMEM);
+}
+
+#if 0
+Static void
+ohci_free_std_chain(ohci_softc_t *sc, ohci_soft_td_t *std,
+ ohci_soft_td_t *stdend)
+{
+ ohci_soft_td_t *p;
+
+ for (; std != stdend; std = p) {
+ p = std->nexttd;
+ ohci_free_std(sc, std);
+ }
+}
+#endif
+
+ohci_soft_itd_t *
+ohci_alloc_sitd(ohci_softc_t *sc)
+{
+ ohci_soft_itd_t *sitd;
+ usbd_status err;
+ int i, s, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeitds == NULL) {
+ DPRINTFN(2, ("ohci_alloc_sitd: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, OHCI_SITD_SIZE * OHCI_SITD_CHUNK,
+ OHCI_ITD_ALIGN, &dma);
+ if (err)
+ return (NULL);
+ s = splusb();
+ for(i = 0; i < OHCI_SITD_CHUNK; i++) {
+ offs = i * OHCI_SITD_SIZE;
+ sitd = KERNADDR(&dma, offs);
+ sitd->physaddr = DMAADDR(&dma, offs);
+ sitd->nextitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd;
+ }
+ splx(s);
+ }
+
+ s = splusb();
+ sitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd->nextitd;
+ memset(&sitd->itd, 0, sizeof(ohci_itd_t));
+ sitd->nextitd = NULL;
+ sitd->xfer = NULL;
+ ohci_hash_add_itd(sc, sitd);
+ splx(s);
+
+#ifdef DIAGNOSTIC
+ sitd->isdone = 0;
+#endif
+
+ return (sitd);
+}
+
+void
+ohci_free_sitd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
+{
+ int s;
+
+ DPRINTFN(10,("ohci_free_sitd: sitd=%p\n", sitd));
+
+#ifdef DIAGNOSTIC
+ if (!sitd->isdone) {
+ panic("ohci_free_sitd: sitd=%p not done", sitd);
+ return;
+ }
+ /* Warn double free */
+ sitd->isdone = 0;
+#endif
+
+ s = splusb();
+ ohci_hash_rem_itd(sc, sitd);
+ sitd->nextitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd;
+ splx(s);
+}
+
+usbd_status
+ohci_init(ohci_softc_t *sc)
+{
+ ohci_soft_ed_t *sed, *psed;
+ usbd_status err;
+ int i;
+ u_int32_t rev;
+
+ DPRINTF(("ohci_init: start\n"));
+#if defined(__OpenBSD__)
+ printf(",");
+#else
+ printf("%s:", USBDEVNAME(sc->sc_bus.bdev));
+#endif
+ rev = OREAD4(sc, OHCI_REVISION);
+ printf(" OHCI version %d.%d%s\n", OHCI_REV_HI(rev), OHCI_REV_LO(rev),
+ OHCI_REV_LEGACY(rev) ? ", legacy support" : "");
+
+ if (OHCI_REV_HI(rev) != 1 || OHCI_REV_LO(rev) != 0) {
+ printf("%s: unsupported OHCI revision\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ sc->sc_bus.usbrev = USBREV_UNKNOWN;
+ return (USBD_INVAL);
+ }
+ sc->sc_bus.usbrev = USBREV_1_0;
+
+ for (i = 0; i < OHCI_HASH_SIZE; i++)
+ LIST_INIT(&sc->sc_hash_tds[i]);
+ for (i = 0; i < OHCI_HASH_SIZE; i++)
+ LIST_INIT(&sc->sc_hash_itds[i]);
+
+ SIMPLEQ_INIT(&sc->sc_free_xfers);
+
+ /* XXX determine alignment by R/W */
+ /* Allocate the HCCA area. */
+ err = usb_allocmem(&sc->sc_bus, OHCI_HCCA_SIZE,
+ OHCI_HCCA_ALIGN, &sc->sc_hccadma);
+ if (err)
+ return (err);
+ sc->sc_hcca = KERNADDR(&sc->sc_hccadma, 0);
+ memset(sc->sc_hcca, 0, OHCI_HCCA_SIZE);
+
+ sc->sc_eintrs = OHCI_NORMAL_INTRS;
+
+ /* Allocate dummy ED that starts the control list. */
+ sc->sc_ctrl_head = ohci_alloc_sed(sc);
+ if (sc->sc_ctrl_head == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ sc->sc_ctrl_head->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+
+ /* Allocate dummy ED that starts the bulk list. */
+ sc->sc_bulk_head = ohci_alloc_sed(sc);
+ if (sc->sc_bulk_head == NULL) {
+ err = USBD_NOMEM;
+ goto bad2;
+ }
+ sc->sc_bulk_head->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+
+ /* Allocate dummy ED that starts the isochronous list. */
+ sc->sc_isoc_head = ohci_alloc_sed(sc);
+ if (sc->sc_isoc_head == NULL) {
+ err = USBD_NOMEM;
+ goto bad3;
+ }
+ sc->sc_isoc_head->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+
+ /* Allocate all the dummy EDs that make up the interrupt tree. */
+ for (i = 0; i < OHCI_NO_EDS; i++) {
+ sed = ohci_alloc_sed(sc);
+ if (sed == NULL) {
+ while (--i >= 0)
+ ohci_free_sed(sc, sc->sc_eds[i]);
+ err = USBD_NOMEM;
+ goto bad4;
+ }
+ /* All ED fields are set to 0. */
+ sc->sc_eds[i] = sed;
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+ if (i != 0)
+ psed = sc->sc_eds[(i-1) / 2];
+ else
+ psed= sc->sc_isoc_head;
+ sed->next = psed;
+ sed->ed.ed_nexted = htole32(psed->physaddr);
+ }
+ /*
+ * Fill HCCA interrupt table. The bit reversal is to get
+ * the tree set up properly to spread the interrupts.
+ */
+ for (i = 0; i < OHCI_NO_INTRS; i++)
+ sc->sc_hcca->hcca_interrupt_table[revbits[i]] =
+ htole32(sc->sc_eds[OHCI_NO_EDS-OHCI_NO_INTRS+i]->physaddr);
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 15) {
+ for (i = 0; i < OHCI_NO_EDS; i++) {
+ printf("ed#%d ", i);
+ ohci_dump_ed(sc->sc_eds[i]);
+ }
+ printf("iso ");
+ ohci_dump_ed(sc->sc_isoc_head);
+ }
+#endif
+
+ err = ohci_controller_init(sc);
+ if (err != USBD_NORMAL_COMPLETION)
+ goto bad5;
+
+ /* Set up the bus struct. */
+ sc->sc_bus.methods = &ohci_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct ohci_pipe);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sc->sc_control = sc->sc_intre = 0;
+ sc->sc_powerhook = powerhook_establish(ohci_power, sc);
+ sc->sc_shutdownhook = shutdownhook_establish(ohci_shutdown, sc);
+#endif
+
+ usb_callout_init(sc->sc_tmo_rhsc);
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad5:
+ for (i = 0; i < OHCI_NO_EDS; i++)
+ ohci_free_sed(sc, sc->sc_eds[i]);
+ bad4:
+ ohci_free_sed(sc, sc->sc_isoc_head);
+ bad3:
+ ohci_free_sed(sc, sc->sc_bulk_head);
+ bad2:
+ ohci_free_sed(sc, sc->sc_ctrl_head);
+ bad1:
+ usb_freemem(&sc->sc_bus, &sc->sc_hccadma);
+ return (err);
+}
+
+Static usbd_status
+ohci_controller_init(ohci_softc_t *sc)
+{
+ int i;
+ u_int32_t s, ctl, ival, hcr, fm, per, desca;
+
+ /* Determine in what context we are running. */
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ if (ctl & OHCI_IR) {
+ /* SMM active, request change */
+ DPRINTF(("ohci_init: SMM active, request owner change\n"));
+ s = OREAD4(sc, OHCI_COMMAND_STATUS);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, s | OHCI_OCR);
+ for (i = 0; i < 100 && (ctl & OHCI_IR); i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ }
+ if ((ctl & OHCI_IR) == 0) {
+ printf("%s: SMM does not respond, resetting\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+ goto reset;
+ }
+#if 0
+/* Don't bother trying to reuse the BIOS init, we'll reset it anyway. */
+ } else if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_RESET) {
+ /* BIOS started controller. */
+ DPRINTF(("ohci_init: BIOS active\n"));
+ if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_OPERATIONAL) {
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_OPERATIONAL);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY);
+ }
+#endif
+ } else {
+ DPRINTF(("ohci_init: cold started\n"));
+ reset:
+ /* Controller was cold started. */
+ usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY);
+ }
+
+ /*
+ * This reset should not be necessary according to the OHCI spec, but
+ * without it some controllers do not start.
+ */
+ DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev)));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+ usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY);
+
+ /* We now own the host controller and the bus has been reset. */
+ ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL));
+
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */
+ /* Nominal time for a reset is 10 us. */
+ for (i = 0; i < 10; i++) {
+ delay(10);
+ hcr = OREAD4(sc, OHCI_COMMAND_STATUS) & OHCI_HCR;
+ if (!hcr)
+ break;
+ }
+ if (hcr) {
+ printf("%s: reset timeout\n", USBDEVNAME(sc->sc_bus.bdev));
+ return (USBD_IOERROR);
+ }
+#ifdef USB_DEBUG
+ if (ohcidebug > 15)
+ ohci_dumpregs(sc);
+#endif
+
+ /* The controller is now in SUSPEND state, we have 2ms to finish. */
+
+ /* Set up HC registers. */
+ OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0));
+ OWRITE4(sc, OHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr);
+ OWRITE4(sc, OHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr);
+ /* disable all interrupts and then switch on all desired interrupts */
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE);
+ /* switch on desired functional features */
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR);
+ ctl |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE |
+ OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL;
+ /* And finally start it! */
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+
+ /*
+ * The controller is now OPERATIONAL. Set a some final
+ * registers that should be set earlier, but that the
+ * controller ignores when in the SUSPEND state.
+ */
+ fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT;
+ fm |= OHCI_FSMPS(ival) | ival;
+ OWRITE4(sc, OHCI_FM_INTERVAL, fm);
+ per = OHCI_PERIODIC(ival); /* 90% periodic */
+ OWRITE4(sc, OHCI_PERIODIC_START, per);
+
+ /* Fiddle the No OverCurrent Protection bit to avoid chip bug. */
+ desca = OREAD4(sc, OHCI_RH_DESCRIPTOR_A);
+ OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca | OHCI_NOCP);
+ OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); /* Enable port power */
+ usb_delay_ms(&sc->sc_bus, OHCI_ENABLE_POWER_DELAY);
+ OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca);
+
+ /*
+ * The AMD756 requires a delay before re-reading the register,
+ * otherwise it will occasionally report 0 ports.
+ */
+ sc->sc_noport = 0;
+ for (i = 0; i < 10 && sc->sc_noport == 0; i++) {
+ usb_delay_ms(&sc->sc_bus, OHCI_READ_DESC_DELAY);
+ sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A));
+ }
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5)
+ ohci_dumpregs(sc);
+#endif
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+ohci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
+{
+ return (usb_allocmem(bus, size, 0, dma));
+}
+
+void
+ohci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ usb_freemem(bus, dma);
+}
+
+usbd_xfer_handle
+ohci_allocx(struct usbd_bus *bus)
+{
+ struct ohci_softc *sc = (struct ohci_softc *)bus;
+ usbd_xfer_handle xfer;
+
+ xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers);
+ if (xfer != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_FREE) {
+ printf("ohci_allocx: xfer=%p not free, 0x%08x\n", xfer,
+ xfer->busy_free);
+ }
+#endif
+ } else {
+ xfer = malloc(sizeof(struct ohci_xfer), M_USB, M_NOWAIT);
+ }
+ if (xfer != NULL) {
+ memset(xfer, 0, sizeof (struct ohci_xfer));
+#ifdef DIAGNOSTIC
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
+ return (xfer);
+}
+
+void
+ohci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
+{
+ struct ohci_softc *sc = (struct ohci_softc *)bus;
+ struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer;
+ ohci_soft_itd_t *sitd;
+
+ if (oxfer->ohci_xfer_flags & OHCI_ISOC_DIRTY) {
+ for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer;
+ sitd = sitd->nextitd)
+ ohci_free_sitd(sc, sitd);
+ }
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("ohci_freex: xfer=%p not busy, 0x%08x\n", xfer,
+ xfer->busy_free);
+ return;
+ }
+ xfer->busy_free = XFER_FREE;
+#endif
+ SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+/*
+ * Shut down the controller when the system is going down.
+ */
+void
+ohci_shutdown(void *v)
+{
+ ohci_softc_t *sc = v;
+
+ DPRINTF(("ohci_shutdown: stopping the HC\n"));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+}
+
+/*
+ * Handle suspend/resume.
+ *
+ * We need to switch to polling mode here, because this routine is
+ * called from an intterupt context. This is all right since we
+ * are almost suspended anyway.
+ */
+void
+ohci_power(int why, void *v)
+{
+ ohci_softc_t *sc = v;
+ u_int32_t ctl;
+ int s;
+
+#ifdef USB_DEBUG
+ DPRINTF(("ohci_power: sc=%p, why=%d\n", sc, why));
+ ohci_dumpregs(sc);
+#endif
+
+ s = splhardusb();
+ if (why != PWR_RESUME) {
+ sc->sc_bus.use_polling++;
+ ctl = OREAD4(sc, OHCI_CONTROL) & ~OHCI_HCFS_MASK;
+ if (sc->sc_control == 0) {
+ /*
+ * Preserve register values, in case that APM BIOS
+ * does not recover them.
+ */
+ sc->sc_control = ctl;
+ sc->sc_intre = OREAD4(sc, OHCI_INTERRUPT_ENABLE);
+ }
+ ctl |= OHCI_HCFS_SUSPEND;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+ sc->sc_bus.use_polling--;
+ } else {
+ sc->sc_bus.use_polling++;
+
+ /* Some broken BIOSes never initialize Controller chip */
+ ohci_controller_init(sc);
+
+ if (sc->sc_intre)
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE,
+ sc->sc_intre & (OHCI_ALL_INTRS | OHCI_MIE));
+ if (sc->sc_control)
+ ctl = sc->sc_control;
+ else
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ ctl |= OHCI_HCFS_RESUME;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY);
+ ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY);
+ sc->sc_control = sc->sc_intre = 0;
+ sc->sc_bus.use_polling--;
+ }
+ splx(s);
+}
+
+#ifdef USB_DEBUG
+void
+ohci_dumpregs(ohci_softc_t *sc)
+{
+ DPRINTF(("ohci_dumpregs: rev=0x%08x control=0x%08x command=0x%08x\n",
+ OREAD4(sc, OHCI_REVISION),
+ OREAD4(sc, OHCI_CONTROL),
+ OREAD4(sc, OHCI_COMMAND_STATUS)));
+ DPRINTF((" intrstat=0x%08x intre=0x%08x intrd=0x%08x\n",
+ OREAD4(sc, OHCI_INTERRUPT_STATUS),
+ OREAD4(sc, OHCI_INTERRUPT_ENABLE),
+ OREAD4(sc, OHCI_INTERRUPT_DISABLE)));
+ DPRINTF((" hcca=0x%08x percur=0x%08x ctrlhd=0x%08x\n",
+ OREAD4(sc, OHCI_HCCA),
+ OREAD4(sc, OHCI_PERIOD_CURRENT_ED),
+ OREAD4(sc, OHCI_CONTROL_HEAD_ED)));
+ DPRINTF((" ctrlcur=0x%08x bulkhd=0x%08x bulkcur=0x%08x\n",
+ OREAD4(sc, OHCI_CONTROL_CURRENT_ED),
+ OREAD4(sc, OHCI_BULK_HEAD_ED),
+ OREAD4(sc, OHCI_BULK_CURRENT_ED)));
+ DPRINTF((" done=0x%08x fmival=0x%08x fmrem=0x%08x\n",
+ OREAD4(sc, OHCI_DONE_HEAD),
+ OREAD4(sc, OHCI_FM_INTERVAL),
+ OREAD4(sc, OHCI_FM_REMAINING)));
+ DPRINTF((" fmnum=0x%08x perst=0x%08x lsthrs=0x%08x\n",
+ OREAD4(sc, OHCI_FM_NUMBER),
+ OREAD4(sc, OHCI_PERIODIC_START),
+ OREAD4(sc, OHCI_LS_THRESHOLD)));
+ DPRINTF((" desca=0x%08x descb=0x%08x stat=0x%08x\n",
+ OREAD4(sc, OHCI_RH_DESCRIPTOR_A),
+ OREAD4(sc, OHCI_RH_DESCRIPTOR_B),
+ OREAD4(sc, OHCI_RH_STATUS)));
+ DPRINTF((" port1=0x%08x port2=0x%08x\n",
+ OREAD4(sc, OHCI_RH_PORT_STATUS(1)),
+ OREAD4(sc, OHCI_RH_PORT_STATUS(2))));
+ DPRINTF((" HCCA: frame_number=0x%04x done_head=0x%08x\n",
+ le32toh(sc->sc_hcca->hcca_frame_number),
+ le32toh(sc->sc_hcca->hcca_done_head)));
+}
+#endif
+
+Static int ohci_intr1(ohci_softc_t *);
+
+int
+ohci_intr(void *p)
+{
+ ohci_softc_t *sc = p;
+
+ if (sc == NULL || sc->sc_dying)
+ return (0);
+
+ /* If we get an interrupt while polling, then just ignore it. */
+ if (sc->sc_bus.use_polling) {
+#ifdef DIAGNOSTIC
+ printf("ohci_intr: ignored interrupt while polling\n");
+#endif
+ return (0);
+ }
+
+ return (ohci_intr1(sc));
+}
+
+Static int
+ohci_intr1(ohci_softc_t *sc)
+{
+ u_int32_t intrs, eintrs;
+ ohci_physaddr_t done;
+
+ DPRINTFN(14,("ohci_intr1: enter\n"));
+
+ /* In case the interrupt occurs before initialization has completed. */
+ if (sc == NULL || sc->sc_hcca == NULL) {
+#ifdef DIAGNOSTIC
+ printf("ohci_intr: sc->sc_hcca == NULL\n");
+#endif
+ return (0);
+ }
+
+ intrs = 0;
+ done = le32toh(sc->sc_hcca->hcca_done_head);
+
+ /* The LSb of done is used to inform the HC Driver that an interrupt
+ * condition exists for both the Done list and for another event
+ * recorded in HcInterruptStatus. On an interrupt from the HC, the HC
+ * Driver checks the HccaDoneHead Value. If this value is 0, then the
+ * interrupt was caused by other than the HccaDoneHead update and the
+ * HcInterruptStatus register needs to be accessed to determine that
+ * exact interrupt cause. If HccaDoneHead is nonzero, then a Done list
+ * update interrupt is indicated and if the LSb of done is nonzero,
+ * then an additional interrupt event is indicated and
+ * HcInterruptStatus should be checked to determine its cause.
+ */
+ if (done != 0) {
+ if (done & ~OHCI_DONE_INTRS)
+ intrs = OHCI_WDH;
+ if (done & OHCI_DONE_INTRS) {
+ intrs |= OREAD4(sc, OHCI_INTERRUPT_STATUS);
+ done &= ~OHCI_DONE_INTRS;
+ }
+ sc->sc_hcca->hcca_done_head = 0;
+ } else
+ intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS) & ~OHCI_WDH;
+
+ if (intrs == 0) /* nothing to be done (PCI shared interrupt) */
+ return (0);
+
+ intrs &= ~OHCI_MIE;
+ OWRITE4(sc, OHCI_INTERRUPT_STATUS, intrs); /* Acknowledge */
+ eintrs = intrs & sc->sc_eintrs;
+ if (!eintrs)
+ return (0);
+
+ sc->sc_bus.intr_context++;
+ sc->sc_bus.no_intrs++;
+ DPRINTFN(7, ("ohci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n",
+ sc, (u_int)intrs, OREAD4(sc, OHCI_INTERRUPT_STATUS),
+ (u_int)eintrs));
+
+ if (eintrs & OHCI_SO) {
+ sc->sc_overrun_cnt++;
+ if (usbd_ratecheck(&sc->sc_overrun_ntc)) {
+ printf("%s: %u scheduling overruns\n",
+ USBDEVNAME(sc->sc_bus.bdev), sc->sc_overrun_cnt);
+ sc->sc_overrun_cnt = 0;
+ }
+ /* XXX do what */
+ eintrs &= ~OHCI_SO;
+ }
+ if (eintrs & OHCI_WDH) {
+ ohci_add_done(sc, done &~ OHCI_DONE_INTRS);
+ usb_schedsoftintr(&sc->sc_bus);
+ eintrs &= ~OHCI_WDH;
+ }
+ if (eintrs & OHCI_RD) {
+ printf("%s: resume detect\n", USBDEVNAME(sc->sc_bus.bdev));
+ /* XXX process resume detect */
+ }
+ if (eintrs & OHCI_UE) {
+ printf("%s: unrecoverable error, controller halted\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+ /* XXX what else */
+ }
+ if (eintrs & OHCI_RHSC) {
+ ohci_rhsc(sc, sc->sc_intrxfer);
+ /*
+ * Disable RHSC interrupt for now, because it will be
+ * on until the port has been reset.
+ */
+ ohci_rhsc_able(sc, 0);
+ /* Do not allow RHSC interrupts > 1 per second */
+ usb_callout(sc->sc_tmo_rhsc, hz, ohci_rhsc_enable, sc);
+ eintrs &= ~OHCI_RHSC;
+ }
+
+ sc->sc_bus.intr_context--;
+
+ if (eintrs != 0) {
+ /* Block unprocessed interrupts. XXX */
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, eintrs);
+ sc->sc_eintrs &= ~eintrs;
+ printf("%s: blocking intrs 0x%x\n",
+ USBDEVNAME(sc->sc_bus.bdev), eintrs);
+ }
+
+ return (1);
+}
+
+void
+ohci_rhsc_able(ohci_softc_t *sc, int on)
+{
+ DPRINTFN(4, ("ohci_rhsc_able: on=%d\n", on));
+ if (on) {
+ sc->sc_eintrs |= OHCI_RHSC;
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC);
+ } else {
+ sc->sc_eintrs &= ~OHCI_RHSC;
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC);
+ }
+}
+
+void
+ohci_rhsc_enable(void *v_sc)
+{
+ ohci_softc_t *sc = v_sc;
+ int s;
+
+ s = splhardusb();
+ ohci_rhsc_able(sc, 1);
+ splx(s);
+}
+
+#ifdef USB_DEBUG
+char *ohci_cc_strs[] = {
+ "NO_ERROR",
+ "CRC",
+ "BIT_STUFFING",
+ "DATA_TOGGLE_MISMATCH",
+ "STALL",
+ "DEVICE_NOT_RESPONDING",
+ "PID_CHECK_FAILURE",
+ "UNEXPECTED_PID",
+ "DATA_OVERRUN",
+ "DATA_UNDERRUN",
+ "BUFFER_OVERRUN",
+ "BUFFER_UNDERRUN",
+ "reserved",
+ "reserved",
+ "NOT_ACCESSED",
+ "NOT_ACCESSED"
+};
+#endif
+
+void
+ohci_add_done(ohci_softc_t *sc, ohci_physaddr_t done)
+{
+ ohci_soft_itd_t *sitd, *sidone, **ip;
+ ohci_soft_td_t *std, *sdone, **p;
+
+ /* Reverse the done list. */
+ for (sdone = NULL, sidone = NULL; done != 0; ) {
+ std = ohci_hash_find_td(sc, done);
+ if (std != NULL) {
+ std->dnext = sdone;
+ done = le32toh(std->td.td_nexttd);
+ sdone = std;
+ DPRINTFN(10,("add TD %p\n", std));
+ continue;
+ }
+ sitd = ohci_hash_find_itd(sc, done);
+ if (sitd != NULL) {
+ sitd->dnext = sidone;
+ done = le32toh(sitd->itd.itd_nextitd);
+ sidone = sitd;
+ DPRINTFN(5,("add ITD %p\n", sitd));
+ continue;
+ }
+ panic("ohci_add_done: addr 0x%08lx not found", (u_long)done);
+ }
+
+ /* sdone & sidone now hold the done lists. */
+ /* Put them on the already processed lists. */
+ for (p = &sc->sc_sdone; *p != NULL; p = &(*p)->dnext)
+ ;
+ *p = sdone;
+ for (ip = &sc->sc_sidone; *ip != NULL; ip = &(*ip)->dnext)
+ ;
+ *ip = sidone;
+}
+
+void
+ohci_softintr(void *v)
+{
+ ohci_softc_t *sc = v;
+ ohci_soft_itd_t *sitd, *sidone, *sitdnext;
+ ohci_soft_td_t *std, *sdone, *stdnext;
+ usbd_xfer_handle xfer;
+ struct ohci_pipe *opipe;
+ int len, cc, s;
+
+ DPRINTFN(10,("ohci_softintr: enter\n"));
+
+ sc->sc_bus.intr_context++;
+
+ s = splhardusb();
+ sdone = sc->sc_sdone;
+ sc->sc_sdone = NULL;
+ sidone = sc->sc_sidone;
+ sc->sc_sidone = NULL;
+ splx(s);
+
+ DPRINTFN(10,("ohci_softintr: sdone=%p sidone=%p\n", sdone, sidone));
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 10) {
+ DPRINTF(("ohci_process_done: TD done:\n"));
+ ohci_dump_tds(sdone);
+ }
+#endif
+
+ for (std = sdone; std; std = stdnext) {
+ xfer = std->xfer;
+ stdnext = std->dnext;
+ DPRINTFN(10, ("ohci_process_done: std=%p xfer=%p hcpriv=%p\n",
+ std, xfer, (xfer ? xfer->hcpriv : NULL)));
+ if (xfer == NULL || (std->flags & OHCI_TD_HANDLED)) {
+ /*
+ * xfer == NULL: There seems to be no xfer associated
+ * with this TD. It is tailp that happened to end up on
+ * the done queue.
+ * flags & OHCI_TD_HANDLED: The TD has already been
+ * handled by process_done and should not be done again.
+ * Shouldn't happen, but some chips are broken(?).
+ */
+ continue;
+ }
+ if (xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("ohci_process_done: cancel/timeout %p\n",
+ xfer));
+ /* Handled by abort routine. */
+ continue;
+ }
+ usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
+
+ len = std->len;
+ if (std->td.td_cbp != 0)
+ len -= le32toh(std->td.td_be) -
+ le32toh(std->td.td_cbp) + 1;
+ DPRINTFN(10, ("ohci_process_done: len=%d, flags=0x%x\n", len,
+ std->flags));
+ if (std->flags & OHCI_ADD_LEN)
+ xfer->actlen += len;
+
+ cc = OHCI_TD_GET_CC(le32toh(std->td.td_flags));
+ if (cc == OHCI_CC_NO_ERROR) {
+ if (std->flags & OHCI_CALL_DONE) {
+ xfer->status = USBD_NORMAL_COMPLETION;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ }
+ ohci_free_std(sc, std);
+ } else {
+ /*
+ * Endpoint is halted. First unlink all the TDs
+ * belonging to the failed transfer, and then restart
+ * the endpoint.
+ */
+ ohci_soft_td_t *p, *n;
+ opipe = (struct ohci_pipe *)xfer->pipe;
+
+ DPRINTFN(15,("ohci_process_done: error cc=%d (%s)\n",
+ OHCI_TD_GET_CC(le32toh(std->td.td_flags)),
+ ohci_cc_strs[OHCI_TD_GET_CC(le32toh(std->td.td_flags))]));
+
+
+ /* Mark all the TDs in the done queue for the current
+ * xfer as handled
+ */
+ for (p = stdnext; p; p = p->dnext) {
+ if (p->xfer == xfer)
+ p->flags |= OHCI_TD_HANDLED;
+ }
+
+ /* remove TDs */
+ for (p = std; p->xfer == xfer; p = n) {
+ n = p->nexttd;
+ ohci_free_std(sc, p);
+ }
+
+ /* clear halt */
+ opipe->sed->ed.ed_headp = htole32(p->physaddr);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+
+ if (cc == OHCI_CC_STALL)
+ xfer->status = USBD_STALLED;
+ else
+ xfer->status = USBD_IOERROR;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ }
+ }
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 10) {
+ DPRINTF(("ohci_softintr: ITD done:\n"));
+ ohci_dump_itds(sidone);
+ }
+#endif
+
+ for (sitd = sidone; sitd != NULL; sitd = sitdnext) {
+ xfer = sitd->xfer;
+ sitdnext = sitd->dnext;
+ sitd->flags |= OHCI_ITD_INTFIN;
+ DPRINTFN(1, ("ohci_process_done: sitd=%p xfer=%p hcpriv=%p\n",
+ sitd, xfer, xfer ? xfer->hcpriv : 0));
+ if (xfer == NULL)
+ continue;
+ if (xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("ohci_process_done: cancel/timeout %p\n",
+ xfer));
+ /* Handled by abort routine. */
+ continue;
+ }
+ if (xfer->pipe)
+ if (xfer->pipe->aborting)
+ continue; /*Ignore.*/
+#ifdef DIAGNOSTIC
+ if (sitd->isdone)
+ printf("ohci_softintr: sitd=%p is done\n", sitd);
+ sitd->isdone = 1;
+#endif
+ opipe = (struct ohci_pipe *)xfer->pipe;
+ if (opipe->aborting)
+ continue;
+
+ cc = OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags));
+ if (cc == OHCI_CC_NO_ERROR) {
+ /* XXX compute length for input */
+ if (sitd->flags & OHCI_CALL_DONE) {
+ opipe->u.iso.inuse -= xfer->nframes;
+ /* XXX update frlengths with actual length */
+ /* XXX xfer->actlen = actlen; */
+ xfer->status = USBD_NORMAL_COMPLETION;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ }
+ } else {
+ /* XXX Do more */
+ xfer->status = USBD_IOERROR;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ }
+ }
+
+#ifdef USB_USE_SOFTINTR
+ if (sc->sc_softwake) {
+ sc->sc_softwake = 0;
+ wakeup(&sc->sc_softwake);
+ }
+#endif /* USB_USE_SOFTINTR */
+
+ sc->sc_bus.intr_context--;
+ DPRINTFN(10,("ohci_softintr: done:\n"));
+}
+
+void
+ohci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ DPRINTFN(10,("ohci_device_ctrl_done: xfer=%p\n", xfer));
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ panic("ohci_device_ctrl_done: not a request");
+ }
+#endif
+ xfer->hcpriv = NULL;
+}
+
+void
+ohci_device_intr_done(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ ohci_soft_td_t *data, *tail;
+
+
+ DPRINTFN(10,("ohci_device_intr_done: xfer=%p, actlen=%d\n",
+ xfer, xfer->actlen));
+
+ xfer->hcpriv = NULL;
+
+ if (xfer->pipe->repeat) {
+ data = opipe->tail.td;
+ tail = ohci_alloc_std(sc); /* XXX should reuse TD */
+ if (tail == NULL) {
+ xfer->status = USBD_NOMEM;
+ return;
+ }
+ tail->xfer = NULL;
+
+ data->td.td_flags = htole32(
+ OHCI_TD_IN | OHCI_TD_NOCC |
+ OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY);
+ if (xfer->flags & USBD_SHORT_XFER_OK)
+ data->td.td_flags |= htole32(OHCI_TD_R);
+ data->td.td_cbp = htole32(DMAADDR(&xfer->dmabuf, 0));
+ data->nexttd = tail;
+ data->td.td_nexttd = htole32(tail->physaddr);
+ data->td.td_be = htole32(le32toh(data->td.td_cbp) +
+ xfer->length - 1);
+ data->len = xfer->length;
+ data->xfer = xfer;
+ data->flags = OHCI_CALL_DONE | OHCI_ADD_LEN;
+ xfer->hcpriv = data;
+ xfer->actlen = 0;
+
+ sed->ed.ed_tailp = htole32(tail->physaddr);
+ opipe->tail.td = tail;
+ }
+}
+
+void
+ohci_device_bulk_done(usbd_xfer_handle xfer)
+{
+ DPRINTFN(10,("ohci_device_bulk_done: xfer=%p, actlen=%d\n",
+ xfer, xfer->actlen));
+
+ xfer->hcpriv = NULL;
+}
+
+void
+ohci_rhsc(ohci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe;
+ u_char *p;
+ int i, m;
+ int hstatus;
+
+ hstatus = OREAD4(sc, OHCI_RH_STATUS);
+ DPRINTF(("ohci_rhsc: sc=%p xfer=%p hstatus=0x%08x\n",
+ sc, xfer, hstatus));
+
+ if (xfer == NULL) {
+ /* Just ignore the change. */
+ return;
+ }
+
+ pipe = xfer->pipe;
+
+ p = KERNADDR(&xfer->dmabuf, 0);
+ m = min(sc->sc_noport, xfer->length * 8 - 1);
+ memset(p, 0, xfer->length);
+ for (i = 1; i <= m; i++) {
+ /* Pick out CHANGE bits from the status reg. */
+ if (OREAD4(sc, OHCI_RH_PORT_STATUS(i)) >> 16)
+ p[i/8] |= 1 << (i%8);
+ }
+ DPRINTF(("ohci_rhsc: change=0x%02x\n", *p));
+ xfer->actlen = xfer->length;
+ xfer->status = USBD_NORMAL_COMPLETION;
+
+ usb_transfer_complete(xfer);
+}
+
+void
+ohci_root_intr_done(usbd_xfer_handle xfer)
+{
+ xfer->hcpriv = NULL;
+}
+
+void
+ohci_root_ctrl_done(usbd_xfer_handle xfer)
+{
+ xfer->hcpriv = NULL;
+}
+
+/*
+ * Wait here until controller claims to have an interrupt.
+ * Then call ohci_intr and return. Use timeout to avoid waiting
+ * too long.
+ */
+void
+ohci_waitintr(ohci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ int timo = xfer->timeout;
+ int usecs;
+ u_int32_t intrs;
+
+ xfer->status = USBD_IN_PROGRESS;
+ for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ if (sc->sc_dying)
+ break;
+ intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS) & sc->sc_eintrs;
+ DPRINTFN(15,("ohci_waitintr: 0x%04x\n", intrs));
+#ifdef USB_DEBUG
+ if (ohcidebug > 15)
+ ohci_dumpregs(sc);
+#endif
+ if (intrs) {
+ ohci_intr1(sc);
+ if (xfer->status != USBD_IN_PROGRESS)
+ return;
+ }
+ }
+
+ /* Timeout */
+ DPRINTF(("ohci_waitintr: timeout\n"));
+ xfer->status = USBD_TIMEOUT;
+ usb_transfer_complete(xfer);
+ /* XXX should free TD */
+}
+
+void
+ohci_poll(struct usbd_bus *bus)
+{
+ ohci_softc_t *sc = (ohci_softc_t *)bus;
+#ifdef USB_DEBUG
+ static int last;
+ int new;
+ new = OREAD4(sc, OHCI_INTERRUPT_STATUS);
+ if (new != last) {
+ DPRINTFN(10,("ohci_poll: intrs=0x%04x\n", new));
+ last = new;
+ }
+#endif
+
+ if (OREAD4(sc, OHCI_INTERRUPT_STATUS) & sc->sc_eintrs)
+ ohci_intr1(sc);
+}
+
+usbd_status
+ohci_device_request(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ usb_device_request_t *req = &xfer->request;
+ usbd_device_handle dev = opipe->pipe.device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ int addr = dev->address;
+ ohci_soft_td_t *setup, *stat, *next, *tail;
+ ohci_soft_ed_t *sed;
+ int isread;
+ int len;
+ usbd_status err;
+ int s;
+
+ isread = req->bmRequestType & UT_READ;
+ len = UGETW(req->wLength);
+
+ DPRINTFN(3,("ohci_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), len, addr,
+ opipe->pipe.endpoint->edesc->bEndpointAddress));
+
+ setup = opipe->tail.td;
+ stat = ohci_alloc_std(sc);
+ if (stat == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ tail = ohci_alloc_std(sc);
+ if (tail == NULL) {
+ err = USBD_NOMEM;
+ goto bad2;
+ }
+ tail->xfer = NULL;
+
+ sed = opipe->sed;
+ opipe->u.ctl.length = len;
+
+ /* Update device address and length since they may have changed
+ during the setup of the control pipe in usbd_new_device(). */
+ /* XXX This only needs to be done once, but it's too early in open. */
+ /* XXXX Should not touch ED here! */
+ sed->ed.ed_flags = htole32(
+ (le32toh(sed->ed.ed_flags) & ~(OHCI_ED_ADDRMASK | OHCI_ED_MAXPMASK)) |
+ OHCI_ED_SET_FA(addr) |
+ OHCI_ED_SET_MAXP(UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize)));
+
+ next = stat;
+
+ /* Set up data transaction */
+ if (len != 0) {
+ ohci_soft_td_t *std = stat;
+
+ err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer,
+ std, &stat);
+ stat = stat->nexttd; /* point at free TD */
+ if (err)
+ goto bad3;
+ /* Start toggle at 1 and then use the carried toggle. */
+ std->td.td_flags &= htole32(~OHCI_TD_TOGGLE_MASK);
+ std->td.td_flags |= htole32(OHCI_TD_TOGGLE_1);
+ }
+
+ memcpy(KERNADDR(&opipe->u.ctl.reqdma, 0), req, sizeof *req);
+
+ setup->td.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC |
+ OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR);
+ setup->td.td_cbp = htole32(DMAADDR(&opipe->u.ctl.reqdma, 0));
+ setup->nexttd = next;
+ setup->td.td_nexttd = htole32(next->physaddr);
+ setup->td.td_be = htole32(le32toh(setup->td.td_cbp) + sizeof *req - 1);
+ setup->len = 0;
+ setup->xfer = xfer;
+ setup->flags = 0;
+ xfer->hcpriv = setup;
+
+ stat->td.td_flags = htole32(
+ (isread ? OHCI_TD_OUT : OHCI_TD_IN) |
+ OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1));
+ stat->td.td_cbp = 0;
+ stat->nexttd = tail;
+ stat->td.td_nexttd = htole32(tail->physaddr);
+ stat->td.td_be = 0;
+ stat->flags = OHCI_CALL_DONE;
+ stat->len = 0;
+ stat->xfer = xfer;
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ DPRINTF(("ohci_device_request:\n"));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(setup);
+ }
+#endif
+
+ /* Insert ED in schedule */
+ s = splusb();
+ sed->ed.ed_tailp = htole32(tail->physaddr);
+ opipe->tail.td = tail;
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ohci_timeout, xfer);
+ }
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 20) {
+ delay(10000);
+ DPRINTF(("ohci_device_request: status=%x\n",
+ OREAD4(sc, OHCI_COMMAND_STATUS)));
+ ohci_dumpregs(sc);
+ printf("ctrl head:\n");
+ ohci_dump_ed(sc->sc_ctrl_head);
+ printf("sed:\n");
+ ohci_dump_ed(sed);
+ ohci_dump_tds(setup);
+ }
+#endif
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad3:
+ ohci_free_std(sc, tail);
+ bad2:
+ ohci_free_std(sc, stat);
+ bad1:
+ return (err);
+}
+
+/*
+ * Add an ED to the schedule. Called at splusb().
+ */
+void
+ohci_add_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head)
+{
+ DPRINTFN(8,("ohci_add_ed: sed=%p head=%p\n", sed, head));
+
+ SPLUSBCHECK;
+ sed->next = head->next;
+ sed->ed.ed_nexted = head->ed.ed_nexted;
+ head->next = sed;
+ head->ed.ed_nexted = htole32(sed->physaddr);
+}
+
+/*
+ * Remove an ED from the schedule. Called at splusb().
+ */
+void
+ohci_rem_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head)
+{
+ ohci_soft_ed_t *p;
+
+ SPLUSBCHECK;
+
+ /* XXX */
+ for (p = head; p != NULL && p->next != sed; p = p->next)
+ ;
+ if (p == NULL)
+ panic("ohci_rem_ed: ED not found");
+ p->next = sed->next;
+ p->ed.ed_nexted = sed->ed.ed_nexted;
+}
+
+/*
+ * When a transfer is completed the TD is added to the done queue by
+ * the host controller. This queue is the processed by software.
+ * Unfortunately the queue contains the physical address of the TD
+ * and we have no simple way to translate this back to a kernel address.
+ * To make the translation possible (and fast) we use a hash table of
+ * TDs currently in the schedule. The physical address is used as the
+ * hash value.
+ */
+
+#define HASH(a) (((a) >> 4) % OHCI_HASH_SIZE)
+/* Called at splusb() */
+void
+ohci_hash_add_td(ohci_softc_t *sc, ohci_soft_td_t *std)
+{
+ int h = HASH(std->physaddr);
+
+ SPLUSBCHECK;
+
+ LIST_INSERT_HEAD(&sc->sc_hash_tds[h], std, hnext);
+}
+
+/* Called at splusb() */
+void
+ohci_hash_rem_td(ohci_softc_t *sc, ohci_soft_td_t *std)
+{
+ SPLUSBCHECK;
+
+ LIST_REMOVE(std, hnext);
+}
+
+ohci_soft_td_t *
+ohci_hash_find_td(ohci_softc_t *sc, ohci_physaddr_t a)
+{
+ int h = HASH(a);
+ ohci_soft_td_t *std;
+
+ /* if these are present they should be masked out at an earlier
+ * stage.
+ */
+ KASSERT((a&~OHCI_HEADMASK) == 0, ("%s: 0x%b has lower bits set\n",
+ USBDEVNAME(sc->sc_bus.bdev),
+ (int) a, "\20\1HALT\2TOGGLE"));
+
+ for (std = LIST_FIRST(&sc->sc_hash_tds[h]);
+ std != NULL;
+ std = LIST_NEXT(std, hnext))
+ if (std->physaddr == a)
+ return (std);
+
+ DPRINTF(("%s: ohci_hash_find_td: addr 0x%08lx not found\n",
+ USBDEVNAME(sc->sc_bus.bdev), (u_long) a));
+ return (NULL);
+}
+
+/* Called at splusb() */
+void
+ohci_hash_add_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
+{
+ int h = HASH(sitd->physaddr);
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10,("ohci_hash_add_itd: sitd=%p physaddr=0x%08lx\n",
+ sitd, (u_long)sitd->physaddr));
+
+ LIST_INSERT_HEAD(&sc->sc_hash_itds[h], sitd, hnext);
+}
+
+/* Called at splusb() */
+void
+ohci_hash_rem_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
+{
+ SPLUSBCHECK;
+
+ DPRINTFN(10,("ohci_hash_rem_itd: sitd=%p physaddr=0x%08lx\n",
+ sitd, (u_long)sitd->physaddr));
+
+ LIST_REMOVE(sitd, hnext);
+}
+
+ohci_soft_itd_t *
+ohci_hash_find_itd(ohci_softc_t *sc, ohci_physaddr_t a)
+{
+ int h = HASH(a);
+ ohci_soft_itd_t *sitd;
+
+ for (sitd = LIST_FIRST(&sc->sc_hash_itds[h]);
+ sitd != NULL;
+ sitd = LIST_NEXT(sitd, hnext))
+ if (sitd->physaddr == a)
+ return (sitd);
+ return (NULL);
+}
+
+void
+ohci_timeout(void *addr)
+{
+ struct ohci_xfer *oxfer = addr;
+ struct ohci_pipe *opipe = (struct ohci_pipe *)oxfer->xfer.pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+
+ DPRINTF(("ohci_timeout: oxfer=%p\n", oxfer));
+
+ if (sc->sc_dying) {
+ ohci_abort_xfer(&oxfer->xfer, USBD_TIMEOUT);
+ return;
+ }
+
+ /* Execute the abort in a process context. */
+ usb_init_task(&oxfer->abort_task, ohci_timeout_task, addr);
+ usb_add_task(oxfer->xfer.pipe->device, &oxfer->abort_task);
+}
+
+void
+ohci_timeout_task(void *addr)
+{
+ usbd_xfer_handle xfer = addr;
+ int s;
+
+ DPRINTF(("ohci_timeout_task: xfer=%p\n", xfer));
+
+ s = splusb();
+ ohci_abort_xfer(xfer, USBD_TIMEOUT);
+ splx(s);
+}
+
+#ifdef USB_DEBUG
+void
+ohci_dump_tds(ohci_soft_td_t *std)
+{
+ for (; std; std = std->nexttd)
+ ohci_dump_td(std);
+}
+
+void
+ohci_dump_td(ohci_soft_td_t *std)
+{
+ char sbuf[128];
+
+ bitmask_snprintf((u_int32_t)le32toh(std->td.td_flags),
+ "\20\23R\24OUT\25IN\31TOG1\32SETTOGGLE",
+ sbuf, sizeof(sbuf));
+
+ printf("TD(%p) at %08lx: %s delay=%d ec=%d cc=%d\ncbp=0x%08lx "
+ "nexttd=0x%08lx be=0x%08lx\n",
+ std, (u_long)std->physaddr, sbuf,
+ OHCI_TD_GET_DI(le32toh(std->td.td_flags)),
+ OHCI_TD_GET_EC(le32toh(std->td.td_flags)),
+ OHCI_TD_GET_CC(le32toh(std->td.td_flags)),
+ (u_long)le32toh(std->td.td_cbp),
+ (u_long)le32toh(std->td.td_nexttd),
+ (u_long)le32toh(std->td.td_be));
+}
+
+void
+ohci_dump_itd(ohci_soft_itd_t *sitd)
+{
+ int i;
+
+ printf("ITD(%p) at %08lx: sf=%d di=%d fc=%d cc=%d\n"
+ "bp0=0x%08lx next=0x%08lx be=0x%08lx\n",
+ sitd, (u_long)sitd->physaddr,
+ OHCI_ITD_GET_SF(le32toh(sitd->itd.itd_flags)),
+ OHCI_ITD_GET_DI(le32toh(sitd->itd.itd_flags)),
+ OHCI_ITD_GET_FC(le32toh(sitd->itd.itd_flags)),
+ OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags)),
+ (u_long)le32toh(sitd->itd.itd_bp0),
+ (u_long)le32toh(sitd->itd.itd_nextitd),
+ (u_long)le32toh(sitd->itd.itd_be));
+ for (i = 0; i < OHCI_ITD_NOFFSET; i++)
+ printf("offs[%d]=0x%04x ", i,
+ (u_int)le16toh(sitd->itd.itd_offset[i]));
+ printf("\n");
+}
+
+void
+ohci_dump_itds(ohci_soft_itd_t *sitd)
+{
+ for (; sitd; sitd = sitd->nextitd)
+ ohci_dump_itd(sitd);
+}
+
+void
+ohci_dump_ed(ohci_soft_ed_t *sed)
+{
+ char sbuf[128], sbuf2[128];
+
+ bitmask_snprintf((u_int32_t)le32toh(sed->ed.ed_flags),
+ "\20\14OUT\15IN\16LOWSPEED\17SKIP\20ISO",
+ sbuf, sizeof(sbuf));
+ bitmask_snprintf((u_int32_t)le32toh(sed->ed.ed_headp),
+ "\20\1HALT\2CARRY", sbuf2, sizeof(sbuf2));
+
+ printf("ED(%p) at 0x%08lx: addr=%d endpt=%d maxp=%d flags=%s\ntailp=0x%08lx "
+ "headflags=%s headp=0x%08lx nexted=0x%08lx\n",
+ sed, (u_long)sed->physaddr,
+ OHCI_ED_GET_FA(le32toh(sed->ed.ed_flags)),
+ OHCI_ED_GET_EN(le32toh(sed->ed.ed_flags)),
+ OHCI_ED_GET_MAXP(le32toh(sed->ed.ed_flags)), sbuf,
+ (u_long)le32toh(sed->ed.ed_tailp), sbuf2,
+ (u_long)le32toh(sed->ed.ed_headp),
+ (u_long)le32toh(sed->ed.ed_nexted));
+}
+#endif
+
+usbd_status
+ohci_open(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ u_int8_t addr = dev->address;
+ u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
+ ohci_soft_ed_t *sed;
+ ohci_soft_td_t *std;
+ ohci_soft_itd_t *sitd;
+ ohci_physaddr_t tdphys;
+ u_int32_t fmt;
+ usbd_status err;
+ int s;
+ int ival;
+
+ DPRINTFN(1, ("ohci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
+ pipe, addr, ed->bEndpointAddress, sc->sc_addr));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ std = NULL;
+ sed = NULL;
+
+ if (addr == sc->sc_addr) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &ohci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | OHCI_INTR_ENDPT:
+ pipe->methods = &ohci_root_intr_methods;
+ break;
+ default:
+ return (USBD_INVAL);
+ }
+ } else {
+ sed = ohci_alloc_sed(sc);
+ if (sed == NULL)
+ goto bad0;
+ opipe->sed = sed;
+ if (xfertype == UE_ISOCHRONOUS) {
+ sitd = ohci_alloc_sitd(sc);
+ if (sitd == NULL)
+ goto bad1;
+ opipe->tail.itd = sitd;
+ opipe->aborting = 0;
+ tdphys = sitd->physaddr;
+ fmt = OHCI_ED_FORMAT_ISO;
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ fmt |= OHCI_ED_DIR_IN;
+ else
+ fmt |= OHCI_ED_DIR_OUT;
+ } else {
+ std = ohci_alloc_std(sc);
+ if (std == NULL)
+ goto bad1;
+ opipe->tail.td = std;
+ tdphys = std->physaddr;
+ fmt = OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD;
+ }
+ sed->ed.ed_flags = htole32(
+ OHCI_ED_SET_FA(addr) |
+ OHCI_ED_SET_EN(UE_GET_ADDR(ed->bEndpointAddress)) |
+ (dev->speed == USB_SPEED_LOW ? OHCI_ED_SPEED : 0) |
+ fmt |
+ OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize)));
+ sed->ed.ed_headp = sed->ed.ed_tailp = htole32(tdphys);
+
+ switch (xfertype) {
+ case UE_CONTROL:
+ pipe->methods = &ohci_device_ctrl_methods;
+ err = usb_allocmem(&sc->sc_bus,
+ sizeof(usb_device_request_t),
+ 0, &opipe->u.ctl.reqdma);
+ if (err)
+ goto bad;
+ s = splusb();
+ ohci_add_ed(sed, sc->sc_ctrl_head);
+ splx(s);
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &ohci_device_intr_methods;
+ ival = pipe->interval;
+ if (ival == USBD_DEFAULT_INTERVAL)
+ ival = ed->bInterval;
+ return (ohci_device_setintr(sc, opipe, ival));
+ case UE_ISOCHRONOUS:
+ pipe->methods = &ohci_device_isoc_methods;
+ return (ohci_setup_isoc(pipe));
+ case UE_BULK:
+ pipe->methods = &ohci_device_bulk_methods;
+ s = splusb();
+ ohci_add_ed(sed, sc->sc_bulk_head);
+ splx(s);
+ break;
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ if (std != NULL)
+ ohci_free_std(sc, std);
+ bad1:
+ if (sed != NULL)
+ ohci_free_sed(sc, sed);
+ bad0:
+ return (USBD_NOMEM);
+
+}
+
+/*
+ * Close a reqular pipe.
+ * Assumes that there are no pending transactions.
+ */
+void
+ohci_close_pipe(usbd_pipe_handle pipe, ohci_soft_ed_t *head)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ int s;
+
+ s = splusb();
+#ifdef DIAGNOSTIC
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+ if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
+ (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK)) {
+ ohci_soft_td_t *std;
+ std = ohci_hash_find_td(sc, le32toh(sed->ed.ed_headp));
+ printf("ohci_close_pipe: pipe not empty sed=%p hd=0x%x "
+ "tl=0x%x pipe=%p, std=%p\n", sed,
+ (int)le32toh(sed->ed.ed_headp),
+ (int)le32toh(sed->ed.ed_tailp),
+ pipe, std);
+#ifdef USB_DEBUG
+ usbd_dump_pipe(&opipe->pipe);
+#endif
+#ifdef USB_DEBUG
+ ohci_dump_ed(sed);
+ if (std)
+ ohci_dump_td(std);
+#endif
+ usb_delay_ms(&sc->sc_bus, 2);
+ if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
+ (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK))
+ printf("ohci_close_pipe: pipe still not empty\n");
+ }
+#endif
+ ohci_rem_ed(sed, head);
+ /* Make sure the host controller is not touching this ED */
+ usb_delay_ms(&sc->sc_bus, 1);
+ splx(s);
+ ohci_free_sed(sc, opipe->sed);
+}
+
+/*
+ * Abort a device request.
+ * If this routine is called at splusb() it guarantees that the request
+ * will be removed from the hardware scheduling and that the callback
+ * for it will be called with USBD_CANCELLED status.
+ * It's impossible to guarantee that the requested transfer will not
+ * have happened since the hardware runs concurrently.
+ * If the transaction has already happened we rely on the ordinary
+ * interrupt processing to process it.
+ */
+void
+ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ ohci_soft_td_t *p, *n;
+ ohci_physaddr_t headp;
+ int s, hit;
+
+ DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p sed=%p\n", xfer, opipe,sed));
+
+ if (sc->sc_dying) {
+ /* If we're dying, just do the software part. */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
+ usb_transfer_complete(xfer);
+ splx(s);
+ }
+
+ if (xfer->device->bus->intr_context || !curproc)
+ panic("ohci_abort_xfer: not in process context");
+
+ /*
+ * Step 1: Make interrupt routine and hardware ignore xfer.
+ */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
+ splx(s);
+ DPRINTFN(1,("ohci_abort_xfer: stop ed=%p\n", sed));
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */
+
+ /*
+ * Step 2: Wait until we know hardware has finished any possible
+ * use of the xfer. Also make sure the soft interrupt routine
+ * has run.
+ */
+ usb_delay_ms(opipe->pipe.device->bus, 20); /* Hardware finishes in 1ms */
+ s = splusb();
+#ifdef USB_USE_SOFTINTR
+ sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+ usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+ tsleep(&sc->sc_softwake, PZERO, "ohciab", 0);
+#endif /* USB_USE_SOFTINTR */
+ splx(s);
+
+ /*
+ * Step 3: Remove any vestiges of the xfer from the hardware.
+ * The complication here is that the hardware may have executed
+ * beyond the xfer we're trying to abort. So as we're scanning
+ * the TDs of this xfer we check if the hardware points to
+ * any of them.
+ */
+ s = splusb(); /* XXX why? */
+ p = xfer->hcpriv;
+#ifdef DIAGNOSTIC
+ if (p == NULL) {
+ splx(s);
+ printf("ohci_abort_xfer: hcpriv is NULL\n");
+ return;
+ }
+#endif
+#ifdef USB_DEBUG
+ if (ohcidebug > 1) {
+ DPRINTF(("ohci_abort_xfer: sed=\n"));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(p);
+ }
+#endif
+ headp = le32toh(sed->ed.ed_headp) & OHCI_HEADMASK;
+ hit = 0;
+ for (; p->xfer == xfer; p = n) {
+ hit |= headp == p->physaddr;
+ n = p->nexttd;
+ ohci_free_std(sc, p);
+ }
+ /* Zap headp register if hardware pointed inside the xfer. */
+ if (hit) {
+ DPRINTFN(1,("ohci_abort_xfer: set hd=0x08%x, tl=0x%08x\n",
+ (int)p->physaddr, (int)le32toh(sed->ed.ed_tailp)));
+ sed->ed.ed_headp = htole32(p->physaddr); /* unlink TDs */
+ } else {
+ DPRINTFN(1,("ohci_abort_xfer: no hit\n"));
+ }
+
+ /*
+ * Step 4: Turn on hardware again.
+ */
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */
+
+ /*
+ * Step 5: Execute callback.
+ */
+ usb_transfer_complete(xfer);
+
+ splx(s);
+}
+
+/*
+ * Data structures and routines to emulate the root hub.
+ */
+Static usb_device_descriptor_t ohci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x00, 0x01}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ {0},{0},{0x00,0x01}, /* device id */
+ 1,2,0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+Static usb_config_descriptor_t ohci_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 */
+};
+
+Static usb_interface_descriptor_t ohci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0,
+ 0,
+ 1,
+ UICLASS_HUB,
+ UISUBCLASS_HUB,
+ UIPROTO_FSHUB,
+ 0
+};
+
+Static usb_endpoint_descriptor_t ohci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_IN | OHCI_INTR_ENDPT,
+ UE_INTERRUPT,
+ {8, 0}, /* max packet */
+ 255
+};
+
+Static usb_hub_descriptor_t ohci_hubd = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 0,
+ {0,0},
+ 0,
+ 0,
+ {0},
+};
+
+Static int
+ohci_str(usb_string_descriptor_t *p, int l, const 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.
+ */
+Static usbd_status
+ohci_root_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+Static usbd_status
+ohci_root_ctrl_start(usbd_xfer_handle xfer)
+{
+ ohci_softc_t *sc = (ohci_softc_t *)xfer->pipe->device->bus;
+ usb_device_request_t *req;
+ void *buf = NULL;
+ int port, i;
+ int s, len, value, index, l, totlen = 0;
+ usb_port_status_t ps;
+ usb_hub_descriptor_t hubd;
+ usbd_status err;
+ u_int32_t v;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ /* XXX panic */
+ return (USBD_INVAL);
+#endif
+ req = &xfer->request;
+
+ DPRINTFN(4,("ohci_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);
+
+ if (len != 0)
+ buf = KERNADDR(&xfer->dmabuf, 0);
+
+#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_HALT 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(8,("ohci_root_ctrl_control wValue=0x%04x\n", value));
+ switch(value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ USETW(ohci_devd.idVendor, sc->sc_id_vendor);
+ memcpy(buf, &ohci_devd, l);
+ break;
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &ohci_confd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &ohci_ifcd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &ohci_endpd, l);
+ break;
+ case UDESC_STRING:
+ if (len == 0)
+ break;
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ switch (value & 0xff) {
+ case 1: /* Vendor */
+ totlen = ohci_str(buf, len, sc->sc_vendor);
+ break;
+ case 2: /* Product */
+ totlen = ohci_str(buf, len, "OHCI root hub");
+ break;
+ }
+ break;
+ default:
+ err = 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) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ err = 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):
+ err = 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(8, ("ohci_root_ctrl_control: UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value));
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = OHCI_RH_PORT_STATUS(index);
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_CURRENT_CONNECT_STATUS);
+ break;
+ case UHF_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_OVERCURRENT_INDICATOR);
+ break;
+ case UHF_PORT_POWER:
+ /* Yes, writing to the LOW_SPEED bit clears power. */
+ OWRITE4(sc, port, UPS_LOW_SPEED);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ OWRITE4(sc, port, UPS_C_CONNECT_STATUS << 16);
+ break;
+ case UHF_C_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_C_PORT_ENABLED << 16);
+ break;
+ case UHF_C_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_C_SUSPEND << 16);
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ OWRITE4(sc, port, UPS_C_OVERCURRENT_INDICATOR << 16);
+ break;
+ case UHF_C_PORT_RESET:
+ OWRITE4(sc, port, UPS_C_PORT_RESET << 16);
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ switch(value) {
+ case UHF_C_PORT_CONNECTION:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* Enable RHSC interrupt if condition is cleared. */
+ if ((OREAD4(sc, port) >> 16) == 0)
+ ohci_rhsc_able(sc, 1);
+ break;
+ default:
+ break;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = OREAD4(sc, OHCI_RH_DESCRIPTOR_A);
+ hubd = ohci_hubd;
+ hubd.bNbrPorts = sc->sc_noport;
+ USETW(hubd.wHubCharacteristics,
+ (v & OHCI_NPS ? UHD_PWR_NO_SWITCH :
+ v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL)
+ /* XXX overcurrent */
+ );
+ hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v);
+ v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B);
+ for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8)
+ hubd.DeviceRemovable[i++] = (u_int8_t)v;
+ hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i;
+ l = min(len, hubd.bDescLength);
+ totlen = l;
+ memcpy(buf, &hubd, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len); /* ? XXX */
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ DPRINTFN(8,("ohci_root_ctrl_transfer: get port status i=%d\n",
+ index));
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = OREAD4(sc, OHCI_RH_PORT_STATUS(index));
+ DPRINTFN(8,("ohci_root_ctrl_transfer: port status=0x%04x\n",
+ v));
+ USETW(ps.wPortStatus, v);
+ USETW(ps.wPortChange, v >> 16);
+ l = min(len, sizeof ps);
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ err = 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 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = OHCI_RH_PORT_STATUS(index);
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_PORT_ENABLED);
+ break;
+ case UHF_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_SUSPEND);
+ break;
+ case UHF_PORT_RESET:
+ DPRINTFN(5,("ohci_root_ctrl_transfer: reset port %d\n",
+ index));
+ OWRITE4(sc, port, UPS_RESET);
+ for (i = 0; i < 5; i++) {
+ usb_delay_ms(&sc->sc_bus,
+ USB_PORT_ROOT_RESET_DELAY);
+ if (sc->sc_dying) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if ((OREAD4(sc, port) & UPS_RESET) == 0)
+ break;
+ }
+ DPRINTFN(8,("ohci port %d reset, status = 0x%04x\n",
+ index, OREAD4(sc, port)));
+ break;
+ case UHF_PORT_POWER:
+ DPRINTFN(2,("ohci_root_ctrl_transfer: set port power "
+ "%d\n", index));
+ OWRITE4(sc, port, UPS_PORT_POWER);
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->actlen = totlen;
+ err = USBD_NORMAL_COMPLETION;
+ ret:
+ xfer->status = err;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root control request. */
+Static void
+ohci_root_ctrl_abort(usbd_xfer_handle xfer)
+{
+ /* Nothing to do, all transfers are synchronous. */
+}
+
+/* Close the root pipe. */
+Static void
+ohci_root_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("ohci_root_ctrl_close\n"));
+ /* Nothing to do. */
+}
+
+Static usbd_status
+ohci_root_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+Static usbd_status
+ohci_root_intr_start(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ sc->sc_intrxfer = xfer;
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root interrupt request. */
+Static void
+ohci_root_intr_abort(usbd_xfer_handle xfer)
+{
+ int s;
+
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("ohci_root_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ xfer->status = USBD_CANCELLED;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+}
+
+/* Close the root pipe. */
+Static void
+ohci_root_intr_close(usbd_pipe_handle pipe)
+{
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ohci_root_intr_close\n"));
+
+ sc->sc_intrxfer = NULL;
+}
+
+/************************/
+
+Static usbd_status
+ohci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+Static usbd_status
+ohci_device_ctrl_start(usbd_xfer_handle xfer)
+{
+ ohci_softc_t *sc = (ohci_softc_t *)xfer->pipe->device->bus;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ /* XXX panic */
+ printf("ohci_device_ctrl_transfer: not a request\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ err = ohci_device_request(xfer);
+ if (err)
+ return (err);
+
+ if (sc->sc_bus.use_polling)
+ ohci_waitintr(sc, xfer);
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a device control request. */
+Static void
+ohci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ohci_device_ctrl_abort: xfer=%p\n", xfer));
+ ohci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device control pipe. */
+Static void
+ohci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ohci_device_ctrl_close: pipe=%p\n", pipe));
+ ohci_close_pipe(pipe, sc->sc_ctrl_head);
+ ohci_free_std(sc, opipe->tail.td);
+}
+
+/************************/
+
+Static void
+ohci_device_clear_toggle(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+
+ opipe->sed->ed.ed_headp &= htole32(~OHCI_TOGGLECARRY);
+}
+
+Static void
+ohci_noop(usbd_pipe_handle pipe)
+{
+}
+
+Static usbd_status
+ohci_device_bulk_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+Static usbd_status
+ohci_device_bulk_start(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ usbd_device_handle dev = opipe->pipe.device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ int addr = dev->address;
+ ohci_soft_td_t *data, *tail, *tdp;
+ ohci_soft_ed_t *sed;
+ int s, len, isread, endpt;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST) {
+ /* XXX panic */
+ printf("ohci_device_bulk_start: a request\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ len = xfer->length;
+ endpt = xfer->pipe->endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sed = opipe->sed;
+
+ DPRINTFN(4,("ohci_device_bulk_start: xfer=%p len=%d isread=%d "
+ "flags=%d endpt=%d\n", xfer, len, isread, xfer->flags,
+ endpt));
+
+ opipe->u.bulk.isread = isread;
+ opipe->u.bulk.length = len;
+
+ /* Update device address */
+ sed->ed.ed_flags = htole32(
+ (le32toh(sed->ed.ed_flags) & ~OHCI_ED_ADDRMASK) |
+ OHCI_ED_SET_FA(addr));
+
+ /* Allocate a chain of new TDs (including a new tail). */
+ data = opipe->tail.td;
+ err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer,
+ data, &tail);
+ /* We want interrupt at the end of the transfer. */
+ tail->td.td_flags &= htole32(~OHCI_TD_INTR_MASK);
+ tail->td.td_flags |= htole32(OHCI_TD_SET_DI(1));
+ tail->flags |= OHCI_CALL_DONE;
+ tail = tail->nexttd; /* point at sentinel */
+ if (err)
+ return (err);
+
+ tail->xfer = NULL;
+ xfer->hcpriv = data;
+
+ DPRINTFN(4,("ohci_device_bulk_start: ed_flags=0x%08x td_flags=0x%08x "
+ "td_cbp=0x%08x td_be=0x%08x\n",
+ (int)le32toh(sed->ed.ed_flags),
+ (int)le32toh(data->td.td_flags),
+ (int)le32toh(data->td.td_cbp),
+ (int)le32toh(data->td.td_be)));
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ ohci_dump_ed(sed);
+ ohci_dump_tds(data);
+ }
+#endif
+
+ /* Insert ED in schedule */
+ s = splusb();
+ for (tdp = data; tdp != tail; tdp = tdp->nexttd) {
+ tdp->xfer = xfer;
+ }
+ sed->ed.ed_tailp = htole32(tail->physaddr);
+ opipe->tail.td = tail;
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ohci_timeout, xfer);
+ }
+
+#if 0
+/* This goes wrong if we are too slow. */
+ if (ohcidebug > 10) {
+ delay(10000);
+ DPRINTF(("ohci_device_intr_transfer: status=%x\n",
+ OREAD4(sc, OHCI_COMMAND_STATUS)));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(data);
+ }
+#endif
+
+ splx(s);
+
+ return (USBD_IN_PROGRESS);
+}
+
+Static void
+ohci_device_bulk_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ohci_device_bulk_abort: xfer=%p\n", xfer));
+ ohci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/*
+ * Close a device bulk pipe.
+ */
+Static void
+ohci_device_bulk_close(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ohci_device_bulk_close: pipe=%p\n", pipe));
+ ohci_close_pipe(pipe, sc->sc_bulk_head);
+ ohci_free_std(sc, opipe->tail.td);
+}
+
+/************************/
+
+Static usbd_status
+ohci_device_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+Static usbd_status
+ohci_device_intr_start(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ usbd_device_handle dev = opipe->pipe.device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ ohci_soft_td_t *data, *tail;
+ int len;
+ int s;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ DPRINTFN(3, ("ohci_device_intr_transfer: xfer=%p len=%d "
+ "flags=%d priv=%p\n",
+ xfer, xfer->length, xfer->flags, xfer->priv));
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("ohci_device_intr_transfer: a request");
+#endif
+
+ len = xfer->length;
+
+ data = opipe->tail.td;
+ tail = ohci_alloc_std(sc);
+ if (tail == NULL)
+ return (USBD_NOMEM);
+ tail->xfer = NULL;
+
+ data->td.td_flags = htole32(
+ OHCI_TD_IN | OHCI_TD_NOCC |
+ OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY);
+ if (xfer->flags & USBD_SHORT_XFER_OK)
+ data->td.td_flags |= htole32(OHCI_TD_R);
+ data->td.td_cbp = htole32(DMAADDR(&xfer->dmabuf, 0));
+ data->nexttd = tail;
+ data->td.td_nexttd = htole32(tail->physaddr);
+ data->td.td_be = htole32(le32toh(data->td.td_cbp) + len - 1);
+ data->len = len;
+ data->xfer = xfer;
+ data->flags = OHCI_CALL_DONE | OHCI_ADD_LEN;
+ xfer->hcpriv = data;
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ DPRINTF(("ohci_device_intr_transfer:\n"));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(data);
+ }
+#endif
+
+ /* Insert ED in schedule */
+ s = splusb();
+ sed->ed.ed_tailp = htole32(tail->physaddr);
+ opipe->tail.td = tail;
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP);
+
+#if 0
+/*
+ * This goes horribly wrong, printing thousands of descriptors,
+ * because false references are followed due to the fact that the
+ * TD is gone.
+ */
+ if (ohcidebug > 5) {
+ usb_delay_ms(&sc->sc_bus, 5);
+ DPRINTF(("ohci_device_intr_transfer: status=%x\n",
+ OREAD4(sc, OHCI_COMMAND_STATUS)));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(data);
+ }
+#endif
+ splx(s);
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a device control request. */
+Static void
+ohci_device_intr_abort(usbd_xfer_handle xfer)
+{
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("ohci_device_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ ohci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device interrupt pipe. */
+Static void
+ohci_device_intr_close(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+ int nslots = opipe->u.intr.nslots;
+ int pos = opipe->u.intr.pos;
+ int j;
+ ohci_soft_ed_t *p, *sed = opipe->sed;
+ int s;
+
+ DPRINTFN(1,("ohci_device_intr_close: pipe=%p nslots=%d pos=%d\n",
+ pipe, nslots, pos));
+ s = splusb();
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+ if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
+ (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK))
+ usb_delay_ms(&sc->sc_bus, 2);
+#ifdef DIAGNOSTIC
+ if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
+ (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK))
+ panic("%s: Intr pipe %p still has TDs queued",
+ USBDEVNAME(sc->sc_bus.bdev), pipe);
+#endif
+
+ for (p = sc->sc_eds[pos]; p && p->next != sed; p = p->next)
+ ;
+#ifdef DIAGNOSTIC
+ if (p == NULL)
+ panic("ohci_device_intr_close: ED not found");
+#endif
+ p->next = sed->next;
+ p->ed.ed_nexted = sed->ed.ed_nexted;
+ splx(s);
+
+ for (j = 0; j < nslots; j++)
+ --sc->sc_bws[(pos * nslots + j) % OHCI_NO_INTRS];
+
+ ohci_free_std(sc, opipe->tail.td);
+ ohci_free_sed(sc, opipe->sed);
+}
+
+Static usbd_status
+ohci_device_setintr(ohci_softc_t *sc, struct ohci_pipe *opipe, int ival)
+{
+ int i, j, s, best;
+ u_int npoll, slow, shigh, nslots;
+ u_int bestbw, bw;
+ ohci_soft_ed_t *hsed, *sed = opipe->sed;
+
+ DPRINTFN(2, ("ohci_setintr: pipe=%p\n", opipe));
+ if (ival == 0) {
+ printf("ohci_setintr: 0 interval\n");
+ return (USBD_INVAL);
+ }
+
+ npoll = OHCI_NO_INTRS;
+ while (npoll > ival)
+ npoll /= 2;
+ DPRINTFN(2, ("ohci_setintr: ival=%d npoll=%d\n", ival, npoll));
+
+ /*
+ * We now know which level in the tree the ED must go into.
+ * Figure out which slot has most bandwidth left over.
+ * Slots to examine:
+ * npoll
+ * 1 0
+ * 2 1 2
+ * 4 3 4 5 6
+ * 8 7 8 9 10 11 12 13 14
+ * N (N-1) .. (N-1+N-1)
+ */
+ slow = npoll-1;
+ shigh = slow + npoll;
+ nslots = OHCI_NO_INTRS / npoll;
+ for (best = i = slow, bestbw = ~0; i < shigh; i++) {
+ bw = 0;
+ for (j = 0; j < nslots; j++)
+ bw += sc->sc_bws[(i * nslots + j) % OHCI_NO_INTRS];
+ if (bw < bestbw) {
+ best = i;
+ bestbw = bw;
+ }
+ }
+ DPRINTFN(2, ("ohci_setintr: best=%d(%d..%d) bestbw=%d\n",
+ best, slow, shigh, bestbw));
+
+ s = splusb();
+ hsed = sc->sc_eds[best];
+ sed->next = hsed->next;
+ sed->ed.ed_nexted = hsed->ed.ed_nexted;
+ hsed->next = sed;
+ hsed->ed.ed_nexted = htole32(sed->physaddr);
+ splx(s);
+
+ for (j = 0; j < nslots; j++)
+ ++sc->sc_bws[(best * nslots + j) % OHCI_NO_INTRS];
+ opipe->u.intr.nslots = nslots;
+ opipe->u.intr.pos = best;
+
+ DPRINTFN(5, ("ohci_setintr: returns %p\n", opipe));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/***********************/
+
+usbd_status
+ohci_device_isoc_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ DPRINTFN(5,("ohci_device_isoc_transfer: xfer=%p\n", xfer));
+
+ /* Put it on our queue, */
+ err = usb_insert_transfer(xfer);
+
+ /* bail out on error, */
+ if (err && err != USBD_IN_PROGRESS)
+ return (err);
+
+ /* XXX should check inuse here */
+
+ /* insert into schedule, */
+ ohci_device_isoc_enter(xfer);
+
+ /* and start if the pipe wasn't running */
+ if (!err)
+ ohci_device_isoc_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
+
+ return (err);
+}
+
+void
+ohci_device_isoc_enter(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ usbd_device_handle dev = opipe->pipe.device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ struct iso *iso = &opipe->u.iso;
+ struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer;
+ ohci_soft_itd_t *sitd, *nsitd;
+ ohci_physaddr_t buf, offs, noffs, bp0, tdphys;
+ int i, ncur, nframes;
+ int s;
+
+ DPRINTFN(1,("ohci_device_isoc_enter: used=%d next=%d xfer=%p "
+ "nframes=%d\n",
+ iso->inuse, iso->next, xfer, xfer->nframes));
+
+ if (sc->sc_dying)
+ return;
+
+ if (iso->next == -1) {
+ /* Not in use yet, schedule it a few frames ahead. */
+ iso->next = le32toh(sc->sc_hcca->hcca_frame_number) + 5;
+ DPRINTFN(2,("ohci_device_isoc_enter: start next=%d\n",
+ iso->next));
+ }
+
+ if (xfer->hcpriv) {
+ for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer;
+ sitd = sitd->nextitd)
+ ohci_free_sitd(sc, sitd); /* Free ITDs in prev xfer*/
+
+ if (sitd == NULL) {
+ sitd = ohci_alloc_sitd(sc);
+ if (sitd == NULL)
+ panic("cant alloc isoc");
+ opipe->tail.itd = sitd;
+ tdphys = sitd->physaddr;
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop*/
+ sed->ed.ed_headp =
+ sed->ed.ed_tailp = htole32(tdphys);
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* Start.*/
+ }
+ }
+
+ sitd = opipe->tail.itd;
+ buf = DMAADDR(&xfer->dmabuf, 0);
+ bp0 = OHCI_PAGE(buf);
+ offs = OHCI_PAGE_OFFSET(buf);
+ nframes = xfer->nframes;
+ xfer->hcpriv = sitd;
+ for (i = ncur = 0; i < nframes; i++, ncur++) {
+ noffs = offs + xfer->frlengths[i];
+ if (ncur == OHCI_ITD_NOFFSET || /* all offsets used */
+ OHCI_PAGE(buf + noffs) > bp0 + OHCI_PAGE_SIZE) { /* too many page crossings */
+
+ /* Allocate next ITD */
+ nsitd = ohci_alloc_sitd(sc);
+ if (nsitd == NULL) {
+ /* XXX what now? */
+ printf("%s: isoc TD alloc failed\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ return;
+ }
+
+ /* Fill current ITD */
+ sitd->itd.itd_flags = htole32(
+ OHCI_ITD_NOCC |
+ OHCI_ITD_SET_SF(iso->next) |
+ OHCI_ITD_SET_DI(6) | /* delay intr a little */
+ OHCI_ITD_SET_FC(ncur));
+ sitd->itd.itd_bp0 = htole32(bp0);
+ sitd->nextitd = nsitd;
+ sitd->itd.itd_nextitd = htole32(nsitd->physaddr);
+ sitd->itd.itd_be = htole32(bp0 + offs - 1);
+ sitd->xfer = xfer;
+ sitd->flags = OHCI_ITD_ACTIVE;
+
+ sitd = nsitd;
+ iso->next = iso->next + ncur;
+ bp0 = OHCI_PAGE(buf + offs);
+ ncur = 0;
+ }
+ sitd->itd.itd_offset[ncur] = htole16(OHCI_ITD_MK_OFFS(offs));
+ offs = noffs;
+ }
+ nsitd = ohci_alloc_sitd(sc);
+ if (nsitd == NULL) {
+ /* XXX what now? */
+ printf("%s: isoc TD alloc failed\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ return;
+ }
+ /* Fixup last used ITD */
+ sitd->itd.itd_flags = htole32(
+ OHCI_ITD_NOCC |
+ OHCI_ITD_SET_SF(iso->next) |
+ OHCI_ITD_SET_DI(0) |
+ OHCI_ITD_SET_FC(ncur));
+ sitd->itd.itd_bp0 = htole32(bp0);
+ sitd->nextitd = nsitd;
+ sitd->itd.itd_nextitd = htole32(nsitd->physaddr);
+ sitd->itd.itd_be = htole32(bp0 + offs - 1);
+ sitd->xfer = xfer;
+ sitd->flags = OHCI_CALL_DONE | OHCI_ITD_ACTIVE;
+
+ iso->next = iso->next + ncur;
+ iso->inuse += nframes;
+
+ xfer->actlen = offs; /* XXX pretend we did it all */
+
+ xfer->status = USBD_IN_PROGRESS;
+
+ oxfer->ohci_xfer_flags |= OHCI_ISOC_DIRTY;
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ DPRINTF(("ohci_device_isoc_enter: frame=%d\n",
+ le32toh(sc->sc_hcca->hcca_frame_number)));
+ ohci_dump_itds(xfer->hcpriv);
+ ohci_dump_ed(sed);
+ }
+#endif
+
+ s = splusb();
+ opipe->tail.itd = nsitd;
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP);
+ sed->ed.ed_tailp = htole32(nsitd->physaddr);
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ delay(150000);
+ DPRINTF(("ohci_device_isoc_enter: after frame=%d\n",
+ le32toh(sc->sc_hcca->hcca_frame_number)));
+ ohci_dump_itds(xfer->hcpriv);
+ ohci_dump_ed(sed);
+ }
+#endif
+}
+
+usbd_status
+ohci_device_isoc_start(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed;
+ int s;
+
+ DPRINTFN(5,("ohci_device_isoc_start: xfer=%p\n", xfer));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->status != USBD_IN_PROGRESS)
+ printf("ohci_device_isoc_start: not in progress %p\n", xfer);
+#endif
+
+ /* XXX anything to do? */
+
+ s = splusb();
+ sed = opipe->sed; /* Turn off ED skip-bit to start processing */
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* ED's ITD list.*/
+ splx(s);
+
+ return (USBD_IN_PROGRESS);
+}
+
+void
+ohci_device_isoc_abort(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed;
+ ohci_soft_itd_t *sitd, *tmp_sitd;
+ int s,undone,num_sitds;
+
+ s = splusb();
+ opipe->aborting = 1;
+
+ DPRINTFN(1,("ohci_device_isoc_abort: xfer=%p\n", xfer));
+
+ /* Transfer is already done. */
+ if (xfer->status != USBD_NOT_STARTED &&
+ xfer->status != USBD_IN_PROGRESS) {
+ splx(s);
+ printf("ohci_device_isoc_abort: early return\n");
+ return;
+ }
+
+ /* Give xfer the requested abort code. */
+ xfer->status = USBD_CANCELLED;
+
+ sed = opipe->sed;
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */
+
+ num_sitds = 0;
+ sitd = xfer->hcpriv;
+#ifdef DIAGNOSTIC
+ if (sitd == NULL) {
+ splx(s);
+ printf("ohci_device_isoc_abort: hcpriv==0\n");
+ return;
+ }
+#endif
+ for (; sitd != NULL && sitd->xfer == xfer; sitd = sitd->nextitd) {
+ num_sitds++;
+#ifdef DIAGNOSTIC
+ DPRINTFN(1,("abort sets done sitd=%p\n", sitd));
+ sitd->isdone = 1;
+#endif
+ }
+
+ splx(s);
+
+ /*
+ * Each sitd has up to OHCI_ITD_NOFFSET transfers, each can
+ * take a usb 1ms cycle. Conservatively wait for it to drain.
+ * Even with DMA done, it can take awhile for the "batch"
+ * delivery of completion interrupts to occur thru the controller.
+ */
+
+ do {
+ usb_delay_ms(&sc->sc_bus, 2*(num_sitds*OHCI_ITD_NOFFSET));
+
+ undone = 0;
+ tmp_sitd = xfer->hcpriv;
+ for (; tmp_sitd != NULL && tmp_sitd->xfer == xfer;
+ tmp_sitd = tmp_sitd->nextitd) {
+ if (OHCI_CC_NO_ERROR ==
+ OHCI_ITD_GET_CC(le32toh(tmp_sitd->itd.itd_flags)) &&
+ tmp_sitd->flags & OHCI_ITD_ACTIVE &&
+ (tmp_sitd->flags & OHCI_ITD_INTFIN) == 0)
+ undone++;
+ }
+ } while( undone != 0 );
+
+
+ s = splusb();
+
+ /* Run callback. */
+ usb_transfer_complete(xfer);
+
+ if (sitd != NULL)
+ /*
+ * Only if there is a `next' sitd in next xfer...
+ * unlink this xfer's sitds.
+ */
+ sed->ed.ed_headp = htole32(sitd->physaddr);
+ else
+ sed->ed.ed_headp = 0;
+
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */
+
+ splx(s);
+}
+
+void
+ohci_device_isoc_done(usbd_xfer_handle xfer)
+{
+ /* This null routine corresponds to non-isoc "done()" routines
+ * that free the stds associated with an xfer after a completed
+ * xfer interrupt. However, in the case of isoc transfers, the
+ * sitds associated with the transfer have already been processed
+ * and reallocated for the next iteration by
+ * "ohci_device_isoc_transfer()".
+ *
+ * Routine "usb_transfer_complete()" is called at the end of every
+ * relevant usb interrupt. "usb_transfer_complete()" indirectly
+ * calls 1) "ohci_device_isoc_transfer()" (which keeps pumping the
+ * pipeline by setting up the next transfer iteration) and 2) then
+ * calls "ohci_device_isoc_done()". Isoc transfers have not been
+ * working for the ohci usb because this routine was trashing the
+ * xfer set up for the next iteration (thus, only the first
+ * UGEN_NISOREQS xfers outstanding on an open would work). Perhaps
+ * this could all be re-factored, but that's another pass...
+ */
+}
+
+usbd_status
+ohci_setup_isoc(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+ struct iso *iso = &opipe->u.iso;
+ int s;
+
+ iso->next = -1;
+ iso->inuse = 0;
+
+ s = splusb();
+ ohci_add_ed(opipe->sed, sc->sc_isoc_head);
+ splx(s);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+ohci_device_isoc_close(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+ ohci_soft_ed_t *sed;
+
+ DPRINTF(("ohci_device_isoc_close: pipe=%p\n", pipe));
+
+ sed = opipe->sed;
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop device. */
+
+ ohci_close_pipe(pipe, sc->sc_isoc_head); /* Stop isoc list, free ED.*/
+
+ /* up to NISOREQs xfers still outstanding. */
+
+#ifdef DIAGNOSTIC
+ opipe->tail.itd->isdone = 1;
+#endif
+ ohci_free_sitd(sc, opipe->tail.itd); /* Next `avail free' sitd.*/
+}
diff --git a/sys/dev/usb/ohci_pci.c b/sys/dev/usb/ohci_pci.c
new file mode 100644
index 0000000..0aa9f5d
--- /dev/null
+++ b/sys/dev/usb/ohci_pci.c
@@ -0,0 +1,370 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) at
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB Open Host Controller driver.
+ *
+ * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf
+ */
+
+/* The low level controller code for OHCI has been split into
+ * PCI probes and OHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.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/ohcireg.h>
+#include <dev/usb/ohcivar.h>
+
+#define PCI_OHCI_VENDORID_ACERLABS 0x10b9
+#define PCI_OHCI_VENDORID_AMD 0x1022
+#define PCI_OHCI_VENDORID_APPLE 0x106b
+#define PCI_OHCI_VENDORID_CMDTECH 0x1095
+#define PCI_OHCI_VENDORID_NEC 0x1033
+#define PCI_OHCI_VENDORID_NVIDIA 0x12D2
+#define PCI_OHCI_VENDORID_NVIDIA2 0x10DE
+#define PCI_OHCI_VENDORID_OPTI 0x1045
+#define PCI_OHCI_VENDORID_SIS 0x1039
+
+#define PCI_OHCI_DEVICEID_ALADDIN_V 0x523710b9
+static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB controller";
+
+#define PCI_OHCI_DEVICEID_AMD756 0x740c1022
+static const char *ohci_device_amd756 = "AMD-756 USB Controller";
+
+#define PCI_OHCI_DEVICEID_AMD766 0x74141022
+static const char *ohci_device_amd766 = "AMD-766 USB Controller";
+
+#define PCI_OHCI_DEVICEID_FIRELINK 0xc8611045
+static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB controller";
+
+#define PCI_OHCI_DEVICEID_NEC 0x00351033
+static const char *ohci_device_nec = "NEC uPD 9210 USB controller";
+
+#define PCI_OHCI_DEVICEID_NFORCE3 0x00d710de
+static const char *ohci_device_nforce3 = "nVidia nForce3 USB Controller";
+
+#define PCI_OHCI_DEVICEID_USB0670 0x06701095
+static const char *ohci_device_usb0670 = "CMD Tech 670 (USB0670) USB controller";
+
+#define PCI_OHCI_DEVICEID_USB0673 0x06731095
+static const char *ohci_device_usb0673 = "CMD Tech 673 (USB0673) USB controller";
+
+#define PCI_OHCI_DEVICEID_SIS5571 0x70011039
+static const char *ohci_device_sis5571 = "SiS 5571 USB controller";
+
+#define PCI_OHCI_DEVICEID_KEYLARGO 0x0019106b
+static const char *ohci_device_keylargo = "Apple KeyLargo USB controller";
+
+static const char *ohci_device_generic = "OHCI (generic) USB controller";
+
+#define PCI_OHCI_BASE_REG 0x10
+
+
+static int ohci_pci_attach(device_t self);
+static int ohci_pci_detach(device_t self);
+static int ohci_pci_suspend(device_t self);
+static int ohci_pci_resume(device_t self);
+
+static int
+ohci_pci_suspend(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return err;
+ ohci_power(PWR_SUSPEND, sc);
+
+ return 0;
+}
+
+static int
+ohci_pci_resume(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ u_int32_t reg, int_line;
+
+ if (pci_get_powerstate(self) != PCI_POWERSTATE_D0) {
+ device_printf(self, "chip is in D%d mode "
+ "-- setting to D0\n", pci_get_powerstate(self));
+ reg = pci_read_config(self, PCI_CBMEM, 4);
+ int_line = pci_read_config(self, PCIR_INTLINE, 4);
+ pci_set_powerstate(self, PCI_POWERSTATE_D0);
+ pci_write_config(self, PCI_CBMEM, reg, 4);
+ pci_write_config(self, PCIR_INTLINE, int_line, 4);
+ }
+
+ ohci_power(PWR_RESUME, sc);
+ bus_generic_resume(self);
+
+ return 0;
+}
+
+static const char *
+ohci_pci_match(device_t self)
+{
+ u_int32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case PCI_OHCI_DEVICEID_ALADDIN_V:
+ return (ohci_device_aladdin_v);
+ case PCI_OHCI_DEVICEID_AMD756:
+ return (ohci_device_amd756);
+ case PCI_OHCI_DEVICEID_AMD766:
+ return (ohci_device_amd766);
+ case PCI_OHCI_DEVICEID_USB0670:
+ return (ohci_device_usb0670);
+ case PCI_OHCI_DEVICEID_USB0673:
+ return (ohci_device_usb0673);
+ case PCI_OHCI_DEVICEID_FIRELINK:
+ return (ohci_device_firelink);
+ case PCI_OHCI_DEVICEID_NEC:
+ return (ohci_device_nec);
+ case PCI_OHCI_DEVICEID_NFORCE3:
+ return (ohci_device_nforce3);
+ case PCI_OHCI_DEVICEID_SIS5571:
+ return (ohci_device_sis5571);
+ case PCI_OHCI_DEVICEID_KEYLARGO:
+ return (ohci_device_keylargo);
+ default:
+ if (pci_get_class(self) == PCIC_SERIALBUS
+ && pci_get_subclass(self) == PCIS_SERIALBUS_USB
+ && pci_get_progif(self) == PCI_INTERFACE_OHCI) {
+ return (ohci_device_generic);
+ }
+ }
+
+ return NULL; /* dunno */
+}
+
+static int
+ohci_pci_probe(device_t self)
+{
+ const char *desc = ohci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return 0;
+ } else {
+ return ENXIO;
+ }
+}
+
+static int
+ohci_pci_attach(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ int err;
+ int rid;
+
+ /* XXX where does it say so in the spec? */
+ sc->sc_bus.usbrev = USBREV_1_0;
+
+ pci_enable_busmaster(self);
+
+ rid = PCI_CBMEM;
+ sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->io_res) {
+ device_printf(self, "Could not map memory\n");
+ return ENXIO;
+ }
+ sc->iot = rman_get_bustag(sc->io_res);
+ sc->ioh = rman_get_bushandle(sc->io_res);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ ohci_pci_detach(self);
+ return ENXIO;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ ohci_pci_detach(self);
+ return ENOMEM;
+ }
+ device_set_ivars(sc->sc_bus.bdev, sc);
+
+ /* ohci_pci_match will never return NULL if ohci_pci_probe succeeded */
+ device_set_desc(sc->sc_bus.bdev, ohci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_OHCI_VENDORID_ACERLABS:
+ sprintf(sc->sc_vendor, "AcerLabs");
+ break;
+ case PCI_OHCI_VENDORID_AMD:
+ sprintf(sc->sc_vendor, "AMD");
+ break;
+ case PCI_OHCI_VENDORID_APPLE:
+ sprintf(sc->sc_vendor, "Apple");
+ break;
+ case PCI_OHCI_VENDORID_CMDTECH:
+ sprintf(sc->sc_vendor, "CMDTECH");
+ break;
+ case PCI_OHCI_VENDORID_NEC:
+ sprintf(sc->sc_vendor, "NEC");
+ break;
+ case PCI_OHCI_VENDORID_NVIDIA:
+ case PCI_OHCI_VENDORID_NVIDIA2:
+ sprintf(sc->sc_vendor, "nVidia");
+ break;
+ case PCI_OHCI_VENDORID_OPTI:
+ sprintf(sc->sc_vendor, "OPTi");
+ break;
+ case PCI_OHCI_VENDORID_SIS:
+ sprintf(sc->sc_vendor, "SiS");
+ break;
+ default:
+ if (bootverbose)
+ device_printf(self, "(New OHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO,
+ (driver_intr_t *) ohci_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->ih = NULL;
+ ohci_pci_detach(self);
+ return ENXIO;
+ }
+ err = ohci_init(sc);
+ if (!err)
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+
+ if (err) {
+ device_printf(self, "USB init failed\n");
+ ohci_pci_detach(self);
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ohci_pci_detach(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+
+ /*
+ * XXX this code is not yet fit to be used as detach for the OHCI
+ * controller
+ */
+
+ /*
+ * disable interrupts that might have been switched on in ohci_init
+ */
+ if (sc->iot && sc->ioh)
+ bus_space_write_4(sc->iot, sc->ioh,
+ OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+
+ if (sc->irq_res && sc->ih) {
+ int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->ih = NULL;
+ }
+ if (sc->sc_bus.bdev) {
+ device_delete_child(self, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+ if (sc->irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res);
+ sc->irq_res = NULL;
+ }
+ if (sc->io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->io_res);
+ sc->io_res = NULL;
+ sc->iot = 0;
+ sc->ioh = 0;
+ }
+ return 0;
+}
+
+static device_method_t ohci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ohci_pci_probe),
+ DEVMETHOD(device_attach, ohci_pci_attach),
+ DEVMETHOD(device_suspend, ohci_pci_suspend),
+ DEVMETHOD(device_resume, ohci_pci_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ohci_driver = {
+ "ohci",
+ ohci_methods,
+ sizeof(ohci_softc_t),
+};
+
+static devclass_t ohci_devclass;
+
+DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0);
+DRIVER_MODULE(ohci, cardbus, ohci_driver, ohci_devclass, 0, 0);
diff --git a/sys/dev/usb/ohcireg.h b/sys/dev/usb/ohcireg.h
new file mode 100644
index 0000000..4c0a6c2
--- /dev/null
+++ b/sys/dev/usb/ohcireg.h
@@ -0,0 +1,249 @@
+/* $NetBSD: ohcireg.h,v 1.17 2000/04/01 09:27:35 augustss Exp $ */
+/* $FreeBSD$ */
+
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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_DT 0x0400 /* Device Type */
+#define OHCI_OCPM 0x0800 /* Overcurrent Protection Mode */
+#define OHCI_NOCP 0x1000 /* No Overcurrent Protection */
+#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_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
+
+#define OHCI_PAGE_SIZE 0x1000
+#define OHCI_PAGE(x) ((x) &~ 0xfff)
+#define OHCI_PAGE_OFFSET(x) ((x) & 0xfff)
+#define OHCI_PAGE_MASK(x) ((x) & 0xfff)
+
+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)
+#define OHCI_ED_MAXPMASK (0x7ff << 16)
+ ohci_physaddr_t ed_tailp;
+ ohci_physaddr_t ed_headp;
+#define OHCI_HALTED 0x00000001
+#define OHCI_TOGGLECARRY 0x00000002
+#define OHCI_HEADMASK 0xfffffffc
+ 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_INTR_MASK 0x00e00000
+#define OHCI_TD_TOGGLE_CARRY 0x00000000
+#define OHCI_TD_TOGGLE_0 0x02000000
+#define OHCI_TD_TOGGLE_1 0x03000000
+#define OHCI_TD_TOGGLE_MASK 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_ITD_NOFFSET 8
+typedef struct {
+ u_int32_t itd_flags;
+#define OHCI_ITD_GET_SF(x) ((x) & 0x0000ffff)
+#define OHCI_ITD_SET_SF(x) ((x) & 0xffff)
+#define OHCI_ITD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */
+#define OHCI_ITD_SET_DI(x) ((x) << 21)
+#define OHCI_ITD_NOINTR 0x00e00000
+#define OHCI_ITD_GET_FC(x) ((((x) >> 24) & 7)+1) /* Frame Count */
+#define OHCI_ITD_SET_FC(x) (((x)-1) << 24)
+#define OHCI_ITD_GET_CC(x) ((x) >> 28) /* Condition Code */
+#define OHCI_ITD_NOCC 0xf0000000
+ ohci_physaddr_t itd_bp0; /* Buffer Page 0 */
+ ohci_physaddr_t itd_nextitd; /* Next ITD */
+ ohci_physaddr_t itd_be; /* Buffer End */
+ u_int16_t itd_offset[OHCI_ITD_NOFFSET]; /* Buffer offsets */
+#define itd_pswn itd_offset /* Packet Status Word*/
+#define OHCI_ITD_PAGE_SELECT 0x00001000
+#define OHCI_ITD_MK_OFFS(len) (0xe000 | ((len) & 0x1fff))
+#define OHCI_ITD_PSW_LENGTH(x) ((x) & 0xfff) /* Transfer length */
+#define OHCI_ITD_PSW_GET_CC(x) ((x) >> 12) /* Condition Code */
+} ohci_itd_t;
+/* #define OHCI_ITD_SIZE 32 */
+#define OHCI_ITD_ALIGN 32
+
+
+#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
+
+/* Some delay needed when changing certain registers. */
+#define OHCI_ENABLE_POWER_DELAY 5
+#define OHCI_READ_DESC_DELAY 5
+
+#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..84b5447
--- /dev/null
+++ b/sys/dev/usb/ohcivar.h
@@ -0,0 +1,170 @@
+/* $NetBSD: ohcivar.h,v 1.30 2001/12/31 12:20:35 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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_SED_SIZE ((sizeof (struct ohci_soft_ed) + OHCI_ED_ALIGN - 1) / OHCI_ED_ALIGN * OHCI_ED_ALIGN)
+#define OHCI_SED_CHUNK (PAGE_SIZE / OHCI_SED_SIZE)
+
+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;
+ usbd_xfer_handle xfer;
+ u_int16_t len;
+ u_int16_t flags;
+#define OHCI_CALL_DONE 0x0001
+#define OHCI_ADD_LEN 0x0002
+#define OHCI_TD_HANDLED 0x0004 /* signal process_done has seen it */
+} ohci_soft_td_t;
+#define OHCI_STD_SIZE ((sizeof (struct ohci_soft_td) + OHCI_TD_ALIGN - 1) / OHCI_TD_ALIGN * OHCI_TD_ALIGN)
+#define OHCI_STD_CHUNK (PAGE_SIZE / OHCI_STD_SIZE)
+
+typedef struct ohci_soft_itd {
+ ohci_itd_t itd;
+ struct ohci_soft_itd *nextitd; /* mirrors nexttd in ITD */
+ struct ohci_soft_itd *dnext; /* next in done list */
+ ohci_physaddr_t physaddr;
+ LIST_ENTRY(ohci_soft_itd) hnext;
+ usbd_xfer_handle xfer;
+ u_int16_t flags;
+#define OHCI_ITD_ACTIVE 0x0010 /* Hardware op in progress */
+#define OHCI_ITD_INTFIN 0x0020 /* Hw completion interrupt seen.*/
+#ifdef DIAGNOSTIC
+ char isdone;
+#endif
+} ohci_soft_itd_t;
+#define OHCI_SITD_SIZE ((sizeof (struct ohci_soft_itd) + OHCI_ITD_ALIGN - 1) / OHCI_ITD_ALIGN * OHCI_ITD_ALIGN)
+#define OHCI_SITD_CHUNK (PAGE_SIZE / OHCI_SITD_SIZE)
+
+#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 */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t sc_size;
+
+#if defined(__FreeBSD__)
+ void *ih;
+
+ struct resource *io_res;
+ struct resource *irq_res;
+#endif
+
+ 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; /* enabled interrupts */
+
+ ohci_soft_ed_t *sc_isoc_head;
+ 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];
+ LIST_HEAD(, ohci_soft_itd) sc_hash_itds[OHCI_HASH_SIZE];
+
+ int sc_noport;
+ u_int8_t sc_addr; /* device address */
+ u_int8_t sc_conf; /* device configuration */
+
+#ifdef USB_USE_SOFTINTR
+ char sc_softwake;
+#endif /* USB_USE_SOFTINTR */
+
+ ohci_soft_ed_t *sc_freeeds;
+ ohci_soft_td_t *sc_freetds;
+ ohci_soft_itd_t *sc_freeitds;
+
+ SIMPLEQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */
+
+ usbd_xfer_handle sc_intrxfer;
+
+ ohci_soft_itd_t *sc_sidone;
+ ohci_soft_td_t *sc_sdone;
+
+ char sc_vendor[16];
+ int sc_id_vendor;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ void *sc_powerhook; /* cookie from power hook */
+ void *sc_shutdownhook; /* cookie from shutdown hook */
+#endif
+ u_int32_t sc_control; /* Preserved during suspend/standby */
+ u_int32_t sc_intre;
+
+ u_int sc_overrun_cnt;
+ struct timeval sc_overrun_ntc;
+
+ usb_callout_t sc_tmo_rhsc;
+
+ device_ptr_t sc_child;
+ char sc_dying;
+} ohci_softc_t;
+
+struct ohci_xfer {
+ struct usbd_xfer xfer;
+ struct usb_task abort_task;
+ u_int32_t ohci_xfer_flags;
+};
+#define OHCI_ISOC_DIRTY 0x01
+
+#define OXFER(xfer) ((struct ohci_xfer *)(xfer))
+
+usbd_status ohci_init(ohci_softc_t *);
+int ohci_intr(void *);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int ohci_detach(ohci_softc_t *, int);
+int ohci_activate(device_ptr_t, enum devact);
+#endif
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+void ohci_shutdown(void *v);
+void ohci_power(int state, void *priv);
diff --git a/sys/dev/usb/rio500_usb.h b/sys/dev/usb/rio500_usb.h
new file mode 100644
index 0000000..c2da72e
--- /dev/null
+++ b/sys/dev/usb/rio500_usb.h
@@ -0,0 +1,58 @@
+/* ----------------------------------------------------------------------
+
+ Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted under any licence of your choise which
+ meets the open source licence definiton
+ http://www.opensource.org/opd.html such as the GNU licence or the
+ BSD licence.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License or the BSD license for more details.
+
+ ----------------------------------------------------------------------
+
+ Modified for FreeBSD by Iwasa Kazmi <kzmi@ca2.so-net.ne.jp>
+
+ ---------------------------------------------------------------------- */
+
+/* $FreeBSD$ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/ioccom.h>
+#ifndef USB_VENDOR_DIAMOND
+#define USB_VENDOR_DIAMOND 0x841
+#endif
+#ifndef USB_PRODUCT_DIAMOND_RIO500USB
+#define USB_PRODUCT_DIAMOND_RIO500USB 0x1
+#endif
+#endif
+
+struct RioCommand
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+ u_int16_t length;
+#else
+ short length;
+#endif
+ int request;
+ int requesttype;
+ int value;
+ int index;
+ void *buffer;
+ int timeout;
+};
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#define RIO_SEND_COMMAND _IOWR('U', 200, struct RioCommand)
+#define RIO_RECV_COMMAND _IOWR('U', 201, struct RioCommand)
+#else
+#define RIO_SEND_COMMAND 0x1
+#define RIO_RECV_COMMAND 0x2
+#endif
+
+#define RIO_DIR_OUT 0x0
+#define RIO_DIR_IN 0x1
diff --git a/sys/dev/usb/ubsa.c b/sys/dev/usb/ubsa.c
new file mode 100644
index 0000000..b00df1e
--- /dev/null
+++ b/sys/dev/usb/ubsa.c
@@ -0,0 +1,771 @@
+/*-
+ * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Ichiro FUKUHARA (ichiro@ichiro.org).
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/interrupt.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+#ifdef USB_DEBUG
+Static int ubsadebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ubsa, CTLFLAG_RW, 0, "USB ubsa");
+SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RW,
+ &ubsadebug, 0, "ubsa debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (ubsadebug > (n)) \
+ logprintf x; \
+ } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UBSA_MODVER 1 /* module version */
+
+#define UBSA_CONFIG_INDEX 1
+#define UBSA_IFACE_INDEX 0
+
+#define UBSA_INTR_INTERVAL 100 /* ms */
+
+#define UBSA_SET_BAUDRATE 0x00
+#define UBSA_SET_STOP_BITS 0x01
+#define UBSA_SET_DATA_BITS 0x02
+#define UBSA_SET_PARITY 0x03
+#define UBSA_SET_DTR 0x0A
+#define UBSA_SET_RTS 0x0B
+#define UBSA_SET_BREAK 0x0C
+#define UBSA_SET_FLOW_CTRL 0x10
+
+#define UBSA_PARITY_NONE 0x00
+#define UBSA_PARITY_EVEN 0x01
+#define UBSA_PARITY_ODD 0x02
+#define UBSA_PARITY_MARK 0x03
+#define UBSA_PARITY_SPACE 0x04
+
+#define UBSA_FLOW_NONE 0x0000
+#define UBSA_FLOW_OCTS 0x0001
+#define UBSA_FLOW_ODSR 0x0002
+#define UBSA_FLOW_IDSR 0x0004
+#define UBSA_FLOW_IDTR 0x0008
+#define UBSA_FLOW_IRTS 0x0010
+#define UBSA_FLOW_ORTS 0x0020
+#define UBSA_FLOW_UNKNOWN 0x0040
+#define UBSA_FLOW_OXON 0x0080
+#define UBSA_FLOW_IXON 0x0100
+
+/* line status register */
+#define UBSA_LSR_TSRE 0x40 /* Transmitter empty: byte sent */
+#define UBSA_LSR_TXRDY 0x20 /* Transmitter buffer empty */
+#define UBSA_LSR_BI 0x10 /* Break detected */
+#define UBSA_LSR_FE 0x08 /* Framing error: bad stop bit */
+#define UBSA_LSR_PE 0x04 /* Parity error */
+#define UBSA_LSR_OE 0x02 /* Overrun, lost incoming byte */
+#define UBSA_LSR_RXRDY 0x01 /* Byte ready in Receive Buffer */
+#define UBSA_LSR_RCV_MASK 0x1f /* Mask for incoming data or error */
+
+/* modem status register */
+/* All deltas are from the last read of the MSR. */
+#define UBSA_MSR_DCD 0x80 /* Current Data Carrier Detect */
+#define UBSA_MSR_RI 0x40 /* Current Ring Indicator */
+#define UBSA_MSR_DSR 0x20 /* Current Data Set Ready */
+#define UBSA_MSR_CTS 0x10 /* Current Clear to Send */
+#define UBSA_MSR_DDCD 0x08 /* DCD has changed state */
+#define UBSA_MSR_TERI 0x04 /* RI has toggled low to high */
+#define UBSA_MSR_DDSR 0x02 /* DSR has changed state */
+#define UBSA_MSR_DCTS 0x01 /* CTS has changed state */
+
+struct ubsa_softc {
+ struct ucom_softc sc_ucom;
+
+ int sc_iface_number; /* interface number */
+
+ usbd_interface_handle sc_intr_iface; /* interrupt interface */
+ int sc_intr_number; /* interrupt number */
+ usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
+ u_char *sc_intr_buf; /* interrupt buffer */
+ int sc_isize;
+
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+
+ u_char sc_lsr; /* Local status register */
+ u_char sc_msr; /* ubsa status register */
+#if __FreeBSD_version >= 500000
+ void *sc_swicookie;
+#endif
+};
+
+Static void ubsa_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void ubsa_notify(void *);
+
+Static void ubsa_get_status(void *, int, u_char *, u_char *);
+Static void ubsa_set(void *, int, int, int);
+Static int ubsa_param(void *, int, struct termios *);
+Static int ubsa_open(void *, int);
+Static void ubsa_close(void *, int);
+
+Static int ubsa_request(struct ubsa_softc *, u_int8_t, u_int16_t);
+Static void ubsa_dtr(struct ubsa_softc *, int);
+Static void ubsa_rts(struct ubsa_softc *, int);
+Static void ubsa_baudrate(struct ubsa_softc *, speed_t);
+Static void ubsa_parity(struct ubsa_softc *, tcflag_t);
+Static void ubsa_databits(struct ubsa_softc *, tcflag_t);
+Static void ubsa_stopbits(struct ubsa_softc *, tcflag_t);
+Static void ubsa_flow(struct ubsa_softc *, tcflag_t, tcflag_t);
+
+struct ucom_callback ubsa_callback = {
+ ubsa_get_status,
+ ubsa_set,
+ ubsa_param,
+ NULL,
+ ubsa_open,
+ ubsa_close,
+ NULL,
+ NULL
+};
+
+Static const struct ubsa_product {
+ uint16_t vendor;
+ uint16_t product;
+} ubsa_products [] = {
+ /* BELKIN F5U103 */
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103 },
+ /* BELKIN F5U120 */
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120 },
+ /* GoHubs GO-COM232 */
+ { USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM },
+ /* GoHubs GO-COM232 */
+ { USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232 },
+ /* Peracom */
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1 },
+ { 0, 0 }
+};
+
+Static device_probe_t ubsa_match;
+Static device_attach_t ubsa_attach;
+Static device_detach_t ubsa_detach;
+
+Static device_method_t ubsa_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ubsa_match),
+ DEVMETHOD(device_attach, ubsa_attach),
+ DEVMETHOD(device_detach, ubsa_detach),
+ { 0, 0 }
+};
+
+Static driver_t ubsa_driver = {
+ "ucom",
+ ubsa_methods,
+ sizeof (struct ubsa_softc)
+};
+
+DRIVER_MODULE(ubsa, uhub, ubsa_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(ubsa, usb, 1, 1, 1);
+MODULE_DEPEND(ubsa, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(ubsa, UBSA_MODVER);
+
+#if __FreeBSD_version >= 500000
+static struct ithd *ucom_ithd;
+#endif
+
+USB_MATCH(ubsa)
+{
+ USB_MATCH_START(ubsa, uaa);
+ int i;
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ for (i = 0; ubsa_products[i].vendor != 0; i++) {
+ if (ubsa_products[i].vendor == uaa->vendor &&
+ ubsa_products[i].product == uaa->product) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ }
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(ubsa)
+{
+ USB_ATTACH_START(ubsa, sc, uaa);
+ usbd_device_handle dev;
+ struct ucom_softc *ucom;
+ usb_config_descriptor_t *cdesc;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ char *devinfo;
+ const char *devname;
+ usbd_status err;
+ int i;
+
+ dev = uaa->device;
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+ ucom = &sc->sc_ucom;
+
+ bzero(sc, sizeof (struct ubsa_softc));
+
+ /*
+ * initialize rts, dtr variables to something
+ * different from boolean 0, 1
+ */
+ sc->sc_dtr = -1;
+ sc->sc_rts = -1;
+
+ usbd_devinfo(dev, 0, devinfo);
+ /* USB_ATTACH_SETUP; */
+ ucom->sc_dev = self;
+ device_set_desc_copy(self, devinfo);
+ /* USB_ATTACH_SETUP; */
+
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ devname = USBDEVNAME(ucom->sc_dev);
+ printf("%s: %s\n", devname, devinfo);
+
+ DPRINTF(("ubsa attach: sc = %p\n", sc));
+
+ /* initialize endpoints */
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ sc->sc_intr_number = -1;
+ sc->sc_intr_pipe = NULL;
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UBSA_CONFIG_INDEX, 1);
+ if (err) {
+ printf("%s: failed to set configuration: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* get the config descriptor */
+ cdesc = usbd_get_config_descriptor(ucom->sc_udev);
+
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n",
+ USBDEVNAME(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* get the first interface */
+ err = usbd_device2interface_handle(dev, UBSA_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ printf("%s: failed to get interface: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* Find the endpoints */
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n",
+ USBDEVNAME(ucom->sc_dev), i);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->sc_intr_number = ed->bEndpointAddress;
+ sc->sc_isize = UGETW(ed->wMaxPacketSize);
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ ucom->sc_ibufsize = UGETW(ed->wMaxPacketSize);
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ ucom->sc_obufsize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ if (sc->sc_intr_number == -1) {
+ printf("%s: Could not find interrupt in\n",
+ USBDEVNAME(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* keep interface for interrupt */
+ sc->sc_intr_iface = ucom->sc_iface;
+
+ if (ucom->sc_bulkin_no == -1) {
+ printf("%s: Could not find data bulk in\n",
+ USBDEVNAME(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n",
+ USBDEVNAME(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsizepad = ucom->sc_ibufsize;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &ubsa_callback;
+
+ DPRINTF(("ubsa: in = 0x%x, out = 0x%x, intr = 0x%x\n",
+ ucom->sc_bulkin_no, ucom->sc_bulkout_no, sc->sc_intr_number));
+
+#if __FreeBSD_version >= 500000
+ swi_add(&ucom_ithd, "ucom", ubsa_notify, sc, SWI_TTY, 0,
+ &sc->sc_swicookie);
+#endif
+
+ ucom_attach(ucom);
+
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_SUCCESS_RETURN;
+
+error:
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+USB_DETACH(ubsa)
+{
+ USB_DETACH_START(ubsa, sc);
+ int rv;
+
+
+ DPRINTF(("ubsa_detach: sc = %p\n", sc));
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ sc->sc_ucom.sc_dying = 1;
+
+ rv = ucom_detach(&sc->sc_ucom);
+
+#if __FreeBSD_version >= 500000
+ ithread_remove_handler(sc->sc_swicookie);
+#endif
+
+ return (rv);
+}
+
+Static int
+ubsa_request(struct ubsa_softc *sc, u_int8_t request, u_int16_t value)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = request;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err)
+ printf("%s: ubsa_request: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+}
+
+Static void
+ubsa_dtr(struct ubsa_softc *sc, int onoff)
+{
+
+ DPRINTF(("ubsa_dtr: onoff = %d\n", onoff));
+
+ if (sc->sc_dtr == onoff)
+ return;
+ sc->sc_dtr = onoff;
+
+ ubsa_request(sc, UBSA_SET_DTR, onoff ? 1 : 0);
+}
+
+Static void
+ubsa_rts(struct ubsa_softc *sc, int onoff)
+{
+
+ DPRINTF(("ubsa_rts: onoff = %d\n", onoff));
+
+ if (sc->sc_rts == onoff)
+ return;
+ sc->sc_rts = onoff;
+
+ ubsa_request(sc, UBSA_SET_RTS, onoff ? 1 : 0);
+}
+
+Static void
+ubsa_break(struct ubsa_softc *sc, int onoff)
+{
+
+ DPRINTF(("ubsa_rts: onoff = %d\n", onoff));
+
+ ubsa_request(sc, UBSA_SET_BREAK, onoff ? 1 : 0);
+}
+
+Static void
+ubsa_set(void *addr, int portno, int reg, int onoff)
+{
+ struct ubsa_softc *sc;
+
+ sc = addr;
+ switch (reg) {
+ case UCOM_SET_DTR:
+ ubsa_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ ubsa_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ ubsa_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+}
+
+Static void
+ubsa_baudrate(struct ubsa_softc *sc, speed_t speed)
+{
+ u_int16_t value = 0;
+
+ DPRINTF(("ubsa_baudrate: speed = %d\n", speed));
+
+ switch(speed) {
+ case B0:
+ break;
+ case B300:
+ case B600:
+ case B1200:
+ case B2400:
+ case B4800:
+ case B9600:
+ case B19200:
+ case B38400:
+ case B57600:
+ case B115200:
+ case B230400:
+ value = B230400 / speed;
+ break;
+ default:
+ printf("%s: ubsa_param: unsupported baudrate, "
+ "forcing default of 9600\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev));
+ value = B230400 / B9600;
+ break;
+ };
+
+ if (speed == B0) {
+ ubsa_flow(sc, 0, 0);
+ ubsa_dtr(sc, 0);
+ ubsa_rts(sc, 0);
+ } else
+ ubsa_request(sc, UBSA_SET_BAUDRATE, value);
+}
+
+Static void
+ubsa_parity(struct ubsa_softc *sc, tcflag_t cflag)
+{
+ int value;
+
+ DPRINTF(("ubsa_parity: cflag = 0x%x\n", cflag));
+
+ if (cflag & PARENB)
+ value = (cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN;
+ else
+ value = UBSA_PARITY_NONE;
+
+ ubsa_request(sc, UBSA_SET_PARITY, value);
+}
+
+Static void
+ubsa_databits(struct ubsa_softc *sc, tcflag_t cflag)
+{
+ int value;
+
+ DPRINTF(("ubsa_databits: cflag = 0x%x\n", cflag));
+
+ switch (cflag & CSIZE) {
+ case CS5: value = 0; break;
+ case CS6: value = 1; break;
+ case CS7: value = 2; break;
+ case CS8: value = 3; break;
+ default:
+ printf("%s: ubsa_param: unsupported databits requested, "
+ "forcing default of 8\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev));
+ value = 3;
+ }
+
+ ubsa_request(sc, UBSA_SET_DATA_BITS, value);
+}
+
+Static void
+ubsa_stopbits(struct ubsa_softc *sc, tcflag_t cflag)
+{
+ int value;
+
+ DPRINTF(("ubsa_stopbits: cflag = 0x%x\n", cflag));
+
+ value = (cflag & CSTOPB) ? 1 : 0;
+
+ ubsa_request(sc, UBSA_SET_STOP_BITS, value);
+}
+
+Static void
+ubsa_flow(struct ubsa_softc *sc, tcflag_t cflag, tcflag_t iflag)
+{
+ int value;
+
+ DPRINTF(("ubsa_flow: cflag = 0x%x, iflag = 0x%x\n", cflag, iflag));
+
+ value = 0;
+ if (cflag & CRTSCTS)
+ value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS;
+ if (iflag & (IXON|IXOFF))
+ value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON;
+
+ ubsa_request(sc, UBSA_SET_FLOW_CTRL, value);
+}
+
+Static int
+ubsa_param(void *addr, int portno, struct termios *ti)
+{
+ struct ubsa_softc *sc;
+
+ sc = addr;
+
+ DPRINTF(("ubsa_param: sc = %p\n", sc));
+
+ ubsa_baudrate(sc, ti->c_ospeed);
+ ubsa_parity(sc, ti->c_cflag);
+ ubsa_databits(sc, ti->c_cflag);
+ ubsa_stopbits(sc, ti->c_cflag);
+ ubsa_flow(sc, ti->c_cflag, ti->c_iflag);
+
+ return (0);
+}
+
+Static int
+ubsa_open(void *addr, int portno)
+{
+ struct ubsa_softc *sc;
+ int err;
+
+ sc = addr;
+ if (sc->sc_ucom.sc_dying)
+ return (ENXIO);
+
+ DPRINTF(("ubsa_open: sc = %p\n", sc));
+
+ if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
+ sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(sc->sc_intr_iface,
+ sc->sc_intr_number,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intr_pipe,
+ sc,
+ sc->sc_intr_buf,
+ sc->sc_isize,
+ ubsa_intr,
+ UBSA_INTR_INTERVAL);
+ if (err) {
+ printf("%s: cannot open interrupt pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ sc->sc_intr_number);
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+Static void
+ubsa_close(void *addr, int portno)
+{
+ struct ubsa_softc *sc;
+ int err;
+
+ sc = addr;
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ DPRINTF(("ubsa_close: close\n"));
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: abort interrupt pipe failed: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: close interrupt pipe failed: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
+
+Static void
+ubsa_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ubsa_softc *sc;
+ u_char *buf;
+
+ sc = priv;
+ buf = sc->sc_intr_buf;
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ DPRINTF(("%s: ubsa_intr: abnormal status: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(status)));
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+
+ /* incidentally, Belkin adapter status bits match UART 16550 bits */
+ sc->sc_lsr = buf[2];
+ sc->sc_msr = buf[3];
+
+ DPRINTF(("%s: ubsa lsr = 0x%02x, msr = 0x%02x\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), sc->sc_lsr, sc->sc_msr));
+
+#if __FreeBSD_version >= 500000
+ swi_sched(sc->sc_swicookie, 0);
+#else
+ ubsa_notify(sc);
+#endif
+}
+
+/* Handle delayed events. */
+Static void
+ubsa_notify(void *arg)
+{
+ struct ubsa_softc *sc;
+
+ sc = arg;
+ ucom_status_change(&sc->sc_ucom);
+}
+
+Static void
+ubsa_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct ubsa_softc *sc;
+
+ DPRINTF(("ubsa_get_status\n"));
+
+ sc = addr;
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
diff --git a/sys/dev/usb/ubser.c b/sys/dev/usb/ubser.c
new file mode 100644
index 0000000..d640494
--- /dev/null
+++ b/sys/dev/usb/ubser.c
@@ -0,0 +1,1044 @@
+/*
+ * Copyright (c) 2004 Bernd Walter <ticso@freebsd.org>
+ *
+ * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $
+ * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $
+ * $Author: ticso $
+ * $Rev: 1127 $
+ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BWCT serial adapter driver
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/clist.h>
+#include <sys/file.h>
+
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+
+#include <sys/sysctl.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/usbdevs.h>
+
+#include <dev/usb/ubser.h>
+
+#ifdef USB_DEBUG
+static int ubserdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser");
+SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RW,
+ &ubserdebug, 0, "ubser debug level");
+#define DPRINTF(x) do { \
+ if (ubserdebug) \
+ logprintf x; \
+ } while (0)
+
+#define DPRINTFN(n, x) do { \
+ if (ubserdebug > (n)) \
+ logprintf x; \
+ } while (0)
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define ISSET(t, f) ((t) & (f))
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~((unsigned)(f))
+
+struct ubser_softc {
+ USBBASEDEVICE sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface; /* data interface */
+ int sc_ifaceno;
+
+ int sc_refcnt;
+ u_char sc_dying;
+ u_char sc_opening;
+ int sc_state;
+ uint8_t sc_numser;
+
+ int sc_bulkin_no; /* bulk in endpoint address */
+ usbd_pipe_handle sc_bulkin_pipe; /* bulk in pipe */
+ usbd_xfer_handle sc_ixfer; /* read request */
+ u_char *sc_ibuf; /* read buffer */
+ u_int sc_ibufsize; /* read buffer size */
+ u_int sc_ibufsizepad; /* read buffer size padded */
+
+ int sc_bulkout_no; /* bulk out endpoint address */
+ usbd_pipe_handle sc_bulkout_pipe;/* bulk out pipe */
+ usbd_xfer_handle sc_oxfer[8]; /* write request */
+ u_char *sc_obuf[8]; /* write buffer */
+ u_int sc_obufsize; /* write buffer size */
+ u_int sc_opkthdrlen; /* header length of
+ output packet */
+
+ struct cdev *dev[8];
+};
+
+Static d_open_t ubser_open;
+Static d_close_t ubser_close;
+Static d_read_t ubser_read;
+Static d_write_t ubser_write;
+Static d_ioctl_t ubser_ioctl;
+
+Static int ubserparam(struct tty *, struct termios *);
+Static void ubserstart(struct tty *);
+Static void ubserstop(struct tty *, int);
+Static usbd_status ubserstartread(struct ubser_softc *);
+Static void ubserreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void ubserwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void ubser_cleanup(struct ubser_softc *sc);
+
+static struct cdevsw ubser_cdevsw = {
+#if __FreeBSD_version > 502102
+ .d_version = D_VERSION,
+#endif
+ .d_open = ubser_open,
+ .d_close = ubser_close,
+ .d_read = ubser_read,
+ .d_write = ubser_write,
+ .d_ioctl = ubser_ioctl,
+#if __FreeBSD_version < 502103
+ .d_poll = ttypoll,
+ .d_kqfilter = ttykqfilter,
+#endif
+ .d_name = "ubser",
+#if __FreeBSD_version > 502102
+ .d_flags = D_TTY | D_NEEDGIANT,
+#else
+ .d_flags = D_TTY,
+#endif
+#if __FreeBSD_version < 500014
+ .d_bmaj = -1,
+#endif
+};
+
+USB_DECLARE_DRIVER(ubser);
+
+USB_MATCH(ubser)
+{
+ USB_MATCH_START(ubser, uaa);
+ usb_string_descriptor_t us;
+ usb_interface_descriptor_t *id;
+ usb_device_descriptor_t *dd;
+ int err;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+
+ DPRINTFN(20,("ubser: vendor=0x%x, product=0x%x\n",
+ uaa->vendor, uaa->product));
+
+ dd = usbd_get_device_descriptor(uaa->device);
+ if (dd == NULL) {
+ printf("ubser: failed to get device descriptor\n");
+ return (UMATCH_NONE);
+ }
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id == NULL) {
+ printf("ubser: failed to get interface descriptor\n");
+ return (UMATCH_NONE);
+ }
+
+ err = usbd_get_string_desc(uaa->device, dd->iManufacturer, 0, &us);
+ if (err != 0)
+ return (UMATCH_NONE);
+
+ /* check if this is a BWCT vendor specific ubser interface */
+ if (strcmp((char*)us.bString, "B\0W\0C\0T\0") == 0 &&
+ id->bInterfaceClass == 0xff && id->bInterfaceSubClass == 0x00)
+ return (UMATCH_VENDOR_IFACESUBCLASS);
+
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(ubser)
+{
+ USB_ATTACH_START(ubser, sc, uaa);
+ usbd_device_handle udev = uaa->device;
+ usb_endpoint_descriptor_t *ed;
+ usb_interface_descriptor_t *id;
+ usb_device_request_t req;
+ char *devinfo;
+ struct tty *tp;
+ usbd_status err;
+ int i;
+ int alen;
+ uint8_t epcount;
+
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+ usbd_devinfo(udev, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ DPRINTFN(10,("\nubser_attach: sc=%p\n", sc));
+
+ sc->sc_udev = udev = uaa->device;
+ sc->sc_iface = uaa->iface;
+
+ for (i = 0; i < 8; i++) {
+ sc->dev[i] = NULL;
+ }
+
+ /* get interface index */
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id == NULL) {
+ printf("ubser: failed to get interface descriptor\n");
+ return (UMATCH_NONE);
+ }
+ sc->sc_ifaceno = id->bInterfaceNumber;
+
+ /* get number of serials */
+ req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+ req.bRequest = VENDOR_GET_NUMSER;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 1);
+ err = usbd_do_request_flags(udev, &req, &sc->sc_numser,
+ USBD_SHORT_XFER_OK, &alen, USBD_DEFAULT_TIMEOUT);
+ if (err) {
+ printf("%s: cannot get number of serials\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ } else if (alen != 1) {
+ printf("%s: bogus answer on get_numser\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_numser > 8)
+ sc->sc_numser = 8;
+ printf("%s: found %i serials\n", USBDEVNAME(sc->sc_dev), sc->sc_numser);
+
+ sc->sc_ibufsize = 7;
+ sc->sc_ibufsizepad = 8;
+ sc->sc_obufsize = 7;
+ sc->sc_opkthdrlen = 1;
+
+ for (i = 0; i < sc->sc_numser; i++) {
+ sc->dev[i] = NULL;
+ }
+
+ for (i = 0; i < sc->sc_numser; i++) {
+ sc->dev[i] = make_dev(&ubser_cdevsw,
+ USBDEVUNIT(sc->sc_dev) * 8 + i,
+ UID_UUCP, GID_DIALER, 0660,
+ "%s.%d", USBDEVNAME(sc->sc_dev), i);
+ if (sc->dev[i] == NULL) {
+ printf("%s: make_dev failed\n", USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->dev[i]->si_tty = tp = ttymalloc(NULL);
+ if (sc->dev[i]->si_tty == NULL) {
+ printf("%s: ttymalloc failed\n", USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ DPRINTF(("ubser_attach: tty_attach tp = %p\n", tp));
+ tp->t_oproc = ubserstart;
+ tp->t_param = ubserparam;
+ tp->t_stop = ubserstop;
+ }
+
+ /* find our bulk endpoints */
+ epcount = 0;
+ usbd_endpoint_count(sc->sc_iface, &epcount);
+ sc->sc_bulkin_no = -1;
+ sc->sc_bulkout_no = -1;
+ for (i = 0; i < epcount; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: couldn't get ep %d\n",
+ USBDEVNAME(sc->sc_dev), i);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ }
+ if (sc->sc_bulkin_no == -1) {
+ printf("%s: could not find bulk in endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (sc->sc_bulkout_no == -1) {
+ printf("%s: could not find bulk out endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Open the bulk pipes */
+ /* Bulk-in pipe */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
+ &sc->sc_bulkin_pipe);
+ if (err) {
+ printf("%s: open bulk in error (addr %d): %s\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no,
+ usbd_errstr(err));
+ goto fail_0;
+ }
+ /* Bulk-out pipe */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ printf("%s: open bulk out error (addr %d): %s\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no,
+ usbd_errstr(err));
+ goto fail_1;
+ }
+
+ /* Allocate a request and an input buffer and start reading. */
+ sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_ixfer == NULL) {
+ goto fail_2;
+ }
+
+ sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
+ sc->sc_ibufsizepad);
+ if (sc->sc_ibuf == NULL) {
+ goto fail_3;
+ }
+
+ for (i = 0; i < 8; i++) {
+ sc->sc_oxfer[i] = NULL;
+ sc->sc_obuf[i] = NULL;
+ }
+ for (i = 0; i < sc->sc_numser; i++) {
+ sc->sc_oxfer[i] = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_oxfer[i] == NULL) {
+ goto fail_4;
+ }
+
+ sc->sc_obuf[i] = usbd_alloc_buffer(sc->sc_oxfer[i],
+ sc->sc_obufsize +
+ sc->sc_opkthdrlen);
+ if (sc->sc_obuf[i] == NULL) {
+ goto fail_4;
+ }
+ }
+
+ ubserstartread(sc);
+
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_SUCCESS_RETURN;
+
+fail_4:
+ for (i = 0; i < sc->sc_numser; i++) {
+ if (sc->sc_oxfer[i] != NULL) {
+ usbd_free_xfer(sc->sc_oxfer[i]);
+ sc->sc_oxfer[i] = NULL;
+ }
+ }
+fail_3:
+ usbd_free_xfer(sc->sc_ixfer);
+ sc->sc_ixfer = NULL;
+fail_2:
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+fail_1:
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+fail_0:
+ sc->sc_opening = 0;
+ wakeup(&sc->sc_opening);
+
+bad:
+ ubser_cleanup(sc);
+ for (i = 0; i < 8; i++) {
+ if (sc->dev[i] != NULL) {
+ tp = sc->dev[i]->si_tty;
+ if (tp != NULL) {
+ if (tp->t_state & TS_ISOPEN) {
+ ttyld_close(tp, 0);
+ ttyclose(tp);
+ }
+ }
+ destroy_dev(sc->dev[i]);
+ }
+ }
+
+ DPRINTF(("ubser_attach: ATTACH ERROR\n"));
+ free(devinfo, M_USBDEV);
+
+ USB_ATTACH_ERROR_RETURN;
+}
+
+USB_DETACH(ubser)
+{
+ USB_DETACH_START(ubser, sc);
+ int i, s;
+ struct tty *tp;
+
+ DPRINTF(("ubser_detach: sc=%p\n", sc));
+
+ sc->sc_dying = 1;
+
+ if (sc->sc_bulkin_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ if (sc->sc_bulkout_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+
+ for (i = 0; i < 8; i++) {
+ if (sc->dev[i] != NULL) {
+ tp = sc->dev[i]->si_tty;
+ if (tp != NULL) {
+ if (tp->t_state & TS_ISOPEN) {
+ ttyld_close(tp, 0);
+ ttyclose(tp);
+ }
+ }
+ destroy_dev(sc->dev[i]);
+ }
+ }
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+
+ return (0);
+}
+
+Static int
+ubserparam(struct tty *tp, struct termios *t)
+{
+ struct ubser_softc *sc;
+
+ USB_GET_SC(ubser, dev2unit(tp->t_dev) / 8, sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTF(("ubserparam: sc = %p\n", sc));
+
+ /*
+ * The firmware on our devices can only do 8n1@9600bps
+ * without handshake.
+ * We refuse to accept other configurations.
+ */
+
+ /* enshure 9600bps */
+ switch (t->c_ospeed) {
+ case 9600:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* 2 stop bits not possible */
+ if (ISSET(t->c_cflag, CSTOPB))
+ return (EINVAL);
+
+ /* XXX parity handling not possible with current firmware */
+ if (ISSET(t->c_cflag, PARENB))
+ return (EINVAL);
+
+ /* we can only do 8 data bits */
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS8:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* we can't do any kind of hardware handshaking */
+ if ((t->c_cflag &
+ (CRTS_IFLOW | CDTR_IFLOW |CDSR_OFLOW |CCAR_OFLOW)) != 0)
+ return (EINVAL);
+
+ /*
+ * XXX xon/xoff not supported by the firmware!
+ * This is handled within FreeBSD only and may overflow buffers
+ * because of delayed reaction due to device buffering.
+ */
+
+ ttsetwater(tp);
+
+ return (0);
+}
+
+Static void
+ubserstart(struct tty *tp)
+{
+ struct ubser_softc *sc;
+ struct cblock *cbp;
+ usbd_status err;
+ int s;
+ u_char *data;
+ int cnt;
+ uint8_t serial;
+
+ USB_GET_SC(ubser, dev2unit(tp->t_dev) / 8, sc);
+ serial = dev2unit(tp->t_dev) & 0x07;
+ DPRINTF(("ubserstart: sc = %p, tp = %p\n", sc, tp));
+
+ if (sc->sc_dying)
+ return;
+
+ s = spltty();
+
+ if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
+ ttwwakeup(tp);
+ DPRINTF(("ubserstart: stopped\n"));
+ goto out;
+ }
+
+ if (tp->t_outq.c_cc <= tp->t_olowat) {
+ if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
+ CLR(tp->t_state, TS_SO_OLOWAT);
+ wakeup(TSA_OLOWAT(tp));
+ }
+ selwakeuppri(&tp->t_wsel, TTIPRI);
+ if (tp->t_outq.c_cc == 0) {
+ if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
+ TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
+ CLR(tp->t_state, TS_SO_OCOMPLETE);
+ wakeup(TSA_OCOMPLETE(tp));
+ }
+ goto out;
+ }
+ }
+
+ /* Grab the first contiguous region of buffer space. */
+ data = tp->t_outq.c_cf;
+ cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
+ cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
+
+ if (cnt == 0) {
+ DPRINTF(("ubserstart: cnt == 0\n"));
+ goto out;
+ }
+
+ SET(tp->t_state, TS_BUSY);
+
+ if (cnt + sc->sc_opkthdrlen > sc->sc_obufsize) {
+ DPRINTF(("ubserstart: big buffer %d chars\n", cnt));
+ cnt = sc->sc_obufsize;
+ }
+ sc->sc_obuf[serial][0] = serial;
+ memcpy(sc->sc_obuf[serial] + sc->sc_opkthdrlen, data, cnt);
+
+
+ DPRINTF(("ubserstart: %d chars\n", cnt));
+ usbd_setup_xfer(sc->sc_oxfer[serial], sc->sc_bulkout_pipe,
+ (usbd_private_handle)tp, sc->sc_obuf[serial],
+ cnt + sc->sc_opkthdrlen,
+ USBD_NO_COPY, USBD_NO_TIMEOUT, ubserwritecb);
+ /* What can we do on error? */
+ err = usbd_transfer(sc->sc_oxfer[serial]);
+ if (err != USBD_IN_PROGRESS)
+ printf("ubserstart: err=%s\n", usbd_errstr(err));
+
+ ttwwakeup(tp);
+
+ out:
+ splx(s);
+}
+
+Static void
+ubserstop(struct tty *tp, int flag)
+{
+ struct ubser_softc *sc;
+ int s;
+
+ USB_GET_SC(ubser, dev2unit(tp->t_dev) / 8, sc);
+
+ DPRINTF(("ubserstop: %d\n", flag));
+
+ if (flag & FWRITE) {
+ DPRINTF(("ubserstop: write\n"));
+ s = spltty();
+ if (ISSET(tp->t_state, TS_BUSY)) {
+ /* XXX do what? */
+ if (!ISSET(tp->t_state, TS_TTSTOP))
+ SET(tp->t_state, TS_FLUSH);
+ }
+ splx(s);
+ }
+
+ DPRINTF(("ubserstop: done\n"));
+}
+
+Static void
+ubserwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct tty *tp;
+ struct ubser_softc *sc;
+ u_int32_t cc;
+ int s;
+
+ tp = (struct tty *)p;
+ USB_GET_SC(ubser, dev2unit(tp->t_dev) / 8, sc);
+
+ DPRINTF(("ubserwritecb: status = %d\n", status));
+
+ if (status == USBD_CANCELLED || sc->sc_dying)
+ goto error;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ printf("%s: ubserwritecb: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+ /* XXX we should restart after some delay. */
+ goto error;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
+ DPRINTF(("ubserwritecb: cc = %d\n", cc));
+ if (cc <= sc->sc_opkthdrlen) {
+ printf("%s: sent size too small, cc = %d\n",
+ USBDEVNAME(sc->sc_dev), cc);
+ goto error;
+ }
+
+ /* convert from USB bytes to tty bytes */
+ cc -= sc->sc_opkthdrlen;
+
+ s = spltty();
+ CLR(tp->t_state, TS_BUSY);
+ if (ISSET(tp->t_state, TS_FLUSH))
+ CLR(tp->t_state, TS_FLUSH);
+ else
+ ndflush(&tp->t_outq, cc);
+ ttyld_start(tp);
+ splx(s);
+
+ return;
+
+ error:
+ s = spltty();
+ CLR(tp->t_state, TS_BUSY);
+ splx(s);
+ return;
+}
+
+Static usbd_status
+ubserstartread(struct ubser_softc *sc)
+{
+ usbd_status err;
+
+ DPRINTF(("ubserstartread: start\n"));
+
+ if (sc->sc_bulkin_pipe == NULL)
+ return (USBD_NORMAL_COMPLETION);
+
+ usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
+ (usbd_private_handle)sc,
+ sc->sc_ibuf, sc->sc_ibufsizepad,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, ubserreadcb);
+
+ err = usbd_transfer(sc->sc_ixfer);
+ if (err != USBD_IN_PROGRESS) {
+ DPRINTF(("ubserstartread: err = %s\n", usbd_errstr(err)));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static void
+ubserreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct ubser_softc *sc = (struct ubser_softc *)p;
+ struct tty *tp;
+ usbd_status err;
+ u_int32_t cc;
+ u_char *cp;
+ int lostcc;
+ int s;
+
+ DPRINTF(("ubserreadcb: status = %d\n", status));
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ printf("%s: ubserreadcb: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+ /* XXX we should restart after some delay. */
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
+
+ DPRINTF(("ubserreadcb: got %d bytes from device\n", cc));
+ if (cc == 0)
+ goto resubmit;
+
+ if (cc > sc->sc_ibufsizepad) {
+ printf("%s: invalid receive data size, %d chars\n",
+ USBDEVNAME(sc->sc_dev), cc);
+ goto resubmit;
+ }
+
+ /* parse header */
+ if (cc < 1)
+ goto resubmit;
+ DPRINTF(("ubserreadcb: got %d chars for serial %d\n", cc - 1, *cp));
+ tp = sc->dev[*cp]->si_tty;
+ cp++;
+ cc--;
+
+ if (cc < 1)
+ goto resubmit;
+
+ if (!(tp->t_state & TS_ISOPEN)) /* drop data for unused serials */
+ goto resubmit;
+
+ s = spltty();
+ if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
+ if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
+ && (tp->t_iflag & IXOFF)
+ && !(tp->t_state & TS_TBLOCK))
+ ttyblock(tp);
+ lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
+ tp->t_rawcc += cc;
+ ttwakeup(tp);
+ if (tp->t_state & TS_TTSTOP
+ && (tp->t_iflag & IXANY
+ || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
+ tp->t_state &= ~TS_TTSTOP;
+ tp->t_lflag &= ~FLUSHO;
+ ubserstart(tp);
+ }
+ if (lostcc > 0)
+ printf("%s: lost %d chars\n", USBDEVNAME(sc->sc_dev),
+ lostcc);
+ } else {
+ /* Give characters to tty layer. */
+ while (cc > 0) {
+ DPRINTFN(7, ("ubserreadcb: char = 0x%02x\n", *cp));
+ if (ttyld_rint(tp, *cp) == -1) {
+ /* XXX what should we do? */
+ printf("%s: lost %d chars\n",
+ USBDEVNAME(sc->sc_dev), cc);
+ break;
+ }
+ cc--;
+ cp++;
+ }
+ }
+ splx(s);
+
+ resubmit:
+ err = ubserstartread(sc);
+ if (err) {
+ printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
+ /* XXX what should we do now? */
+ }
+
+}
+
+Static void
+ubser_cleanup(struct ubser_softc *sc)
+{
+ int i;
+
+ DPRINTF(("ubser_cleanup: closing pipes\n"));
+
+ if (sc->sc_bulkin_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+ }
+ if (sc->sc_bulkout_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+ }
+ if (sc->sc_ixfer != NULL) {
+ usbd_free_xfer(sc->sc_ixfer);
+ sc->sc_ixfer = NULL;
+ }
+ for (i = 0; i < sc->sc_numser; i++) {
+ if (sc->sc_oxfer[i] != NULL) {
+ usbd_free_xfer(sc->sc_oxfer[i]);
+ sc->sc_oxfer[i] = NULL;
+ }
+ }
+}
+
+static int
+ubser_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct ubser_softc *sc;
+ struct tty *tp;
+ int s;
+ int error;
+
+ USB_GET_SC(ubser, dev2unit(dev) / 8, sc);
+
+ if (sc->sc_dying)
+ return (ENXIO);
+
+ tp = sc->dev[dev2unit(dev) & 0x07]->si_tty;
+
+ DPRINTF(("%s: ubser_open: tp = %p\n", USBDEVNAME(sc->sc_dev), tp));
+
+ if (ISSET(tp->t_state, TS_ISOPEN) &&
+ ISSET(tp->t_state, TS_XCLUDE) &&
+ suser(p))
+ return (EBUSY);
+
+ /*
+ * Do the following if this is a first open.
+ */
+ s = spltty();
+ while (sc->sc_opening)
+ tsleep(&sc->sc_opening, PRIBIO, "ubser_op", 0);
+ sc->sc_opening = 1;
+
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ tp->t_dev = dev;
+
+ /*
+ * Initialize the termios status to the defaults. Add in the
+ * sticky bits from TIOCSFLAGS.
+ */
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ ttychars(tp);
+ ttsetwater(tp);
+
+ /*
+ * Handle initial DCD.
+ */
+ ttyld_modem(tp, 1);
+ }
+
+ sc->sc_refcnt++; /* XXX: wrong refcnt on error later on */
+ sc->sc_opening = 0;
+ wakeup(&sc->sc_opening);
+ splx(s);
+
+ error = ttyopen(dev, tp);
+ if (error)
+ goto bad;
+
+ error = ttyld_open(tp, dev);
+ if (error)
+ goto bad;
+
+ DPRINTF(("%s: ubser_open: success\n", USBDEVNAME(sc->sc_dev)));
+
+ return (0);
+
+ sc->sc_opening = 0;
+ wakeup(&sc->sc_opening);
+ splx(s);
+ return (error);
+
+bad:
+ DPRINTF(("%s: ubser_open: failed\n", USBDEVNAME(sc->sc_dev)));
+ return (error);
+}
+
+static int
+ubser_close(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct ubser_softc *sc;
+ struct tty *tp;
+
+ USB_GET_SC(ubser, dev2unit(dev) / 8, sc);
+ tp = sc->dev[dev2unit(dev) & 0x07]->si_tty;
+ DPRINTF(("%s: ubserclose\n",
+ USBDEVNAME(sc->sc_dev)));
+
+ if (!ISSET(tp->t_state, TS_ISOPEN))
+ goto quit;
+
+ if (sc->sc_dying)
+ goto quit;
+
+quit:
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (0);
+}
+
+static int
+ubser_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
+{
+ usb_device_request_t req;
+ struct ubser_softc *sc;
+ struct tty *tp;
+ int error;
+ int s;
+ int alen;
+
+ USB_GET_SC(ubser, dev2unit(dev) / 8, sc);
+ tp = sc->dev[dev2unit(dev) & 0x07]->si_tty;
+
+ DPRINTF(("ubser_ioctl: cmd = 0x%08lx\n", cmd));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ error = ttyioctl(dev, cmd, data, flag, p);
+ if (error != ENOTTY) {
+ DPRINTF(("ubser_ioctl: l_ioctl: error = %d\n", error));
+ return (error);
+ }
+
+ error = 0;
+
+ s = spltty();
+ switch (cmd) {
+ case TIOCSBRK: /* clearing break condition is done in firmware */
+ DPRINTF(("ubser_ioctl: TIOCSBRK\n"));
+ req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+ req.bRequest = VENDOR_SET_BREAK;
+ USETW(req.wValue, dev2unit(dev) & 0x07);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 0);
+ error = usbd_do_request_flags(sc->sc_udev, &req, &sc->sc_numser,
+ USBD_SHORT_XFER_OK, &alen, USBD_DEFAULT_TIMEOUT);
+ break;
+ /* XXX: something else to handle? */
+ }
+ splx(s);
+
+ return (error);
+}
+
+static int
+ubser_read(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct ubser_softc *sc;
+ struct tty *tp;
+ int error;
+
+ USB_GET_SC(ubser, dev2unit(dev) / 8, sc);
+ tp = sc->dev[dev2unit(dev) & 0x07]->si_tty;
+
+ DPRINTF(("ubser_read: tp = %p, flag = 0x%x\n", tp, flag));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ error = ttyld_read(tp, uio, flag);
+
+ DPRINTF(("ubser_read: error = %d\n", error));
+
+ return (error);
+}
+
+static int
+ubser_write(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct ubser_softc *sc;
+ struct tty *tp;
+ int error;
+
+ USB_GET_SC(ubser, dev2unit(dev) / 8, sc);
+ tp = sc->dev[dev2unit(dev) & 0x07]->si_tty;
+
+ DPRINTF(("ubser_write: tp = %p, flag = 0x%x\n", tp, flag));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ error = ttyld_write(tp, uio, flag);
+
+ DPRINTF(("ubser_write: error = %d\n", error));
+
+ return (error);
+}
+
+DRIVER_MODULE(ubser, uhub, ubser_driver, ubser_devclass, usbd_driver_load, 0);
+
diff --git a/sys/dev/usb/ubser.h b/sys/dev/usb/ubser.h
new file mode 100644
index 0000000..d1cc2c6
--- /dev/null
+++ b/sys/dev/usb/ubser.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2003 Bernd Walter <ticso@freebsd.org>
+ *
+ * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.h $
+ * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $
+ * $Author: ticso $
+ * $Rev: 1127 $
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef UBSER_H
+#define UBSER_H
+
+/* Vendor Interface Requests */
+#define VENDOR_GET_NUMSER 0x01
+#define VENDOR_SET_BREAK 0x02
+#define VENDOR_CLEAR_BREAK 0x03
+
+#endif /* UBSER_H */
diff --git a/sys/dev/usb/ucom.c b/sys/dev/usb/ucom.c
new file mode 100644
index 0000000..2d28cf7
--- /dev/null
+++ b/sys/dev/usb/ucom.c
@@ -0,0 +1,1164 @@
+/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/clist.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+#ifdef USB_DEBUG
+static int ucomdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
+ &ucomdebug, 0, "ucom debug level");
+#define DPRINTF(x) do { \
+ if (ucomdebug) \
+ logprintf x; \
+ } while (0)
+
+#define DPRINTFN(n, x) do { \
+ if (ucomdebug > (n)) \
+ logprintf x; \
+ } while (0)
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+Static d_open_t ucomopen;
+Static d_close_t ucomclose;
+Static d_read_t ucomread;
+Static d_write_t ucomwrite;
+Static d_ioctl_t ucomioctl;
+
+
+static struct cdevsw ucom_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = ucomopen,
+ .d_close = ucomclose,
+ .d_read = ucomread,
+ .d_write = ucomwrite,
+ .d_ioctl = ucomioctl,
+ .d_name = "ucom",
+ .d_flags = D_TTY | D_NEEDGIANT,
+#if __FreeBSD_version < 500014
+ .d_bmaj = -1,
+#endif
+};
+
+Static void ucom_cleanup(struct ucom_softc *);
+Static int ucomctl(struct ucom_softc *, int, int);
+Static int ucomparam(struct tty *, struct termios *);
+Static void ucomstart(struct tty *);
+Static void ucomstop(struct tty *, int);
+Static void ucom_shutdown(struct ucom_softc *);
+Static void ucom_dtr(struct ucom_softc *, int);
+Static void ucom_rts(struct ucom_softc *, int);
+Static void ucom_break(struct ucom_softc *, int);
+Static usbd_status ucomstartread(struct ucom_softc *);
+Static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void ucomstopread(struct ucom_softc *);
+
+devclass_t ucom_devclass;
+
+static moduledata_t ucom_mod = {
+ "ucom",
+ NULL,
+ NULL
+};
+
+DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+MODULE_DEPEND(ucom, usb, 1, 1, 1);
+MODULE_VERSION(ucom, UCOM_MODVER);
+
+int
+ucom_attach(struct ucom_softc *sc)
+{
+ struct tty *tp;
+ int unit;
+
+ unit = device_get_unit(sc->sc_dev);
+
+ sc->sc_tty = tp = ttymalloc(sc->sc_tty);
+ tp->t_oproc = ucomstart;
+ tp->t_param = ucomparam;
+ tp->t_stop = ucomstop;
+
+ DPRINTF(("ucom_attach: tty_attach tp = %p\n", tp));
+
+ DPRINTF(("ucom_attach: make_dev: ucom%d\n", unit));
+
+ sc->dev = make_dev(&ucom_cdevsw, unit | UCOM_CALLOUT_MASK,
+ UID_UUCP, GID_DIALER, 0660,
+ "ucom%d", unit);
+ sc->dev->si_tty = tp;
+
+ return (0);
+}
+
+int
+ucom_detach(struct ucom_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+ int s;
+
+ DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty));
+
+ sc->sc_dying = 1;
+
+ if (sc->sc_bulkin_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ if (sc->sc_bulkout_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+
+ if (tp != NULL) {
+ if (tp->t_state & TS_ISOPEN) {
+ device_printf(sc->sc_dev,
+ "still open, forcing close\n");
+ ttyld_close(tp, 0);
+ ttyclose(tp);
+ }
+ } else {
+ DPRINTF(("ucom_detach: no tty\n"));
+ return (0);
+ }
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+
+ destroy_dev(sc->dev);
+
+ return (0);
+}
+
+Static void
+ucom_shutdown(struct ucom_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+
+ DPRINTF(("ucom_shutdown\n"));
+ /*
+ * Hang up if necessary. Wait a bit, so the other side has time to
+ * notice even if we immediately open the port again.
+ */
+ if (ISSET(tp->t_cflag, HUPCL)) {
+ (void)ucomctl(sc, TIOCM_DTR, DMBIC);
+ (void)tsleep(sc, TTIPRI, "ucomsd", hz);
+ }
+}
+
+Static int
+ucomopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ int unit = UCOMUNIT(dev);
+ struct ucom_softc *sc;
+ usbd_status err;
+ struct tty *tp;
+ int s;
+ int error;
+
+ USB_GET_SC_OPEN(ucom, unit, sc);
+
+ if (sc->sc_dying)
+ return (ENXIO);
+
+ tp = sc->sc_tty;
+
+ DPRINTF(("%s: ucomopen: tp = %p\n", USBDEVNAME(sc->sc_dev), tp));
+
+ if (ISSET(tp->t_state, TS_ISOPEN) &&
+ ISSET(tp->t_state, TS_XCLUDE) &&
+ suser(p))
+ return (EBUSY);
+
+ /*
+ * Do the following iff this is a first open.
+ */
+ s = spltty();
+ while (sc->sc_opening)
+ tsleep(&sc->sc_opening, PRIBIO, "ucomop", 0);
+ sc->sc_opening = 1;
+
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ struct termios t;
+
+ sc->sc_poll = 0;
+ sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0;
+
+ tp->t_dev = dev;
+
+ /*
+ * Initialize the termios status to the defaults. Add in the
+ * sticky bits from TIOCSFLAGS.
+ */
+ t.c_ispeed = 0;
+ t.c_ospeed = TTYDEF_SPEED;
+ t.c_cflag = TTYDEF_CFLAG;
+ /* Make sure ucomparam() will do something. */
+ tp->t_ospeed = 0;
+ (void)ucomparam(tp, &t);
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ ttychars(tp);
+ ttsetwater(tp);
+
+ /*
+ * Turn on DTR. We must always do this, even if carrier is not
+ * present, because otherwise we'd have to use TIOCSDTR
+ * immediately after setting CLOCAL, which applications do not
+ * expect. We always assert DTR while the device is open
+ * unless explicitly requested to deassert it.
+ */
+ (void)ucomctl(sc, TIOCM_DTR | TIOCM_RTS, DMBIS);
+
+ /* Device specific open */
+ if (sc->sc_callback->ucom_open != NULL) {
+ error = sc->sc_callback->ucom_open(sc->sc_parent,
+ sc->sc_portno);
+ if (error) {
+ ucom_cleanup(sc);
+ sc->sc_opening = 0;
+ wakeup(&sc->sc_opening);
+ splx(s);
+ return (error);
+ }
+ }
+
+ DPRINTF(("ucomopen: open pipes in = %d out = %d\n",
+ sc->sc_bulkin_no, sc->sc_bulkout_no));
+
+ /* Open the bulk pipes */
+ /* Bulk-in pipe */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
+ &sc->sc_bulkin_pipe);
+ if (err) {
+ printf("%s: open bulk in error (addr %d): %s\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no,
+ usbd_errstr(err));
+ error = EIO;
+ goto fail_0;
+ }
+ /* Bulk-out pipe */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ printf("%s: open bulk out error (addr %d): %s\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no,
+ usbd_errstr(err));
+ error = EIO;
+ goto fail_1;
+ }
+
+ /* Allocate a request and an input buffer and start reading. */
+ sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_ixfer == NULL) {
+ error = ENOMEM;
+ goto fail_2;
+ }
+
+ sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
+ sc->sc_ibufsizepad);
+ if (sc->sc_ibuf == NULL) {
+ error = ENOMEM;
+ goto fail_3;
+ }
+
+ sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_oxfer == NULL) {
+ error = ENOMEM;
+ goto fail_3;
+ }
+
+ sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
+ sc->sc_obufsize +
+ sc->sc_opkthdrlen);
+ if (sc->sc_obuf == NULL) {
+ error = ENOMEM;
+ goto fail_4;
+ }
+
+ /*
+ * Handle initial DCD.
+ */
+ if (ISSET(sc->sc_msr, UMSR_DCD) ||
+ (minor(dev) & UCOM_CALLOUT_MASK))
+ ttyld_modem(tp, 1);
+
+ ucomstartread(sc);
+ }
+
+ sc->sc_opening = 0;
+ wakeup(&sc->sc_opening);
+ splx(s);
+
+ error = ttyopen(dev, tp);
+ if (error)
+ goto bad;
+
+ error = ttyld_open(tp, dev);
+ if (error)
+ goto bad;
+
+ ttyldoptim(tp);
+
+ DPRINTF(("%s: ucomopen: success\n", USBDEVNAME(sc->sc_dev)));
+
+ sc->sc_poll = 1;
+ sc->sc_refcnt++;
+
+ return (0);
+
+fail_4:
+ usbd_free_xfer(sc->sc_oxfer);
+ sc->sc_oxfer = NULL;
+fail_3:
+ usbd_free_xfer(sc->sc_ixfer);
+ sc->sc_ixfer = NULL;
+fail_2:
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+fail_1:
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+fail_0:
+ sc->sc_opening = 0;
+ wakeup(&sc->sc_opening);
+ splx(s);
+ return (error);
+
+bad:
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ /*
+ * We failed to open the device, and nobody else had it opened.
+ * Clean up the state as appropriate.
+ */
+ ucom_cleanup(sc);
+ }
+
+ DPRINTF(("%s: ucomopen: failed\n", USBDEVNAME(sc->sc_dev)));
+
+ return (error);
+}
+
+static int
+ucomclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct ucom_softc *sc;
+ struct tty *tp;
+ int s;
+
+ USB_GET_SC(ucom, UCOMUNIT(dev), sc);
+
+ tp = sc->sc_tty;
+
+ DPRINTF(("%s: ucomclose: unit = %d\n",
+ USBDEVNAME(sc->sc_dev), UCOMUNIT(dev)));
+
+ if (!ISSET(tp->t_state, TS_ISOPEN))
+ goto quit;
+
+ s = spltty();
+ ttyld_close(tp, flag);
+ ttyldoptim(tp);
+ ttyclose(tp);
+ splx(s);
+
+ if (sc->sc_dying)
+ goto quit;
+
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ /*
+ * Although we got a last close, the device may still be in
+ * use; e.g. if this was the dialout node, and there are still
+ * processes waiting for carrier on the non-dialout node.
+ */
+ ucom_cleanup(sc);
+ }
+
+ if (sc->sc_callback->ucom_close != NULL)
+ sc->sc_callback->ucom_close(sc->sc_parent, sc->sc_portno);
+
+ quit:
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+ return (0);
+}
+
+static int
+ucomread(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct ucom_softc *sc;
+ struct tty *tp;
+ int error;
+
+ USB_GET_SC(ucom, UCOMUNIT(dev), sc);
+ tp = sc->sc_tty;
+
+ DPRINTF(("ucomread: tp = %p, flag = 0x%x\n", tp, flag));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ error = ttyld_read(tp, uio, flag);
+
+ DPRINTF(("ucomread: error = %d\n", error));
+
+ return (error);
+}
+
+static int
+ucomwrite(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct ucom_softc *sc;
+ struct tty *tp;
+ int error;
+
+ USB_GET_SC(ucom, UCOMUNIT(dev), sc);
+ tp = sc->sc_tty;
+
+ DPRINTF(("ucomwrite: tp = %p, flag = 0x%x\n", tp, flag));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ error = ttyld_write(tp, uio, flag);
+
+ DPRINTF(("ucomwrite: error = %d\n", error));
+
+ return (error);
+}
+
+static int
+ucomioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
+{
+ struct ucom_softc *sc;
+ struct tty *tp;
+ int error;
+ int s;
+ int d;
+#if defined(COMPAT_43)
+ u_long oldcmd;
+ struct termios term;
+#endif
+
+ USB_GET_SC(ucom, UCOMUNIT(dev), sc);
+ tp = sc->sc_tty;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTF(("ucomioctl: cmd = 0x%08lx\n", cmd));
+
+#if defined(COMPAT_43)
+ term = tp->t_termios;
+ oldcmd = cmd;
+ error = ttsetcompat(tp, &cmd, data, &term);
+ if (error != 0)
+ return (error);
+ if (cmd != oldcmd)
+ data = (caddr_t)&term;
+#endif
+
+ error = ttyioctl(dev, cmd, data, flag, p);
+ ttyldoptim(tp);
+ if (error != ENOTTY) {
+ DPRINTF(("ucomioctl: l_ioctl: error = %d\n", error));
+ return (error);
+ }
+
+ s = spltty();
+
+ if (sc->sc_callback->ucom_ioctl != NULL) {
+ error = sc->sc_callback->ucom_ioctl(sc->sc_parent,
+ sc->sc_portno,
+ cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+ }
+
+ error = 0;
+
+ DPRINTF(("ucomioctl: our cmd = 0x%08lx\n", cmd));
+
+ switch (cmd) {
+ case TIOCSBRK:
+ DPRINTF(("ucomioctl: TIOCSBRK\n"));
+ ucom_break(sc, 1);
+ break;
+ case TIOCCBRK:
+ DPRINTF(("ucomioctl: TIOCCBRK\n"));
+ ucom_break(sc, 0);
+ break;
+
+ case TIOCSDTR:
+ DPRINTF(("ucomioctl: TIOCSDTR\n"));
+ (void)ucomctl(sc, TIOCM_DTR, DMBIS);
+ break;
+ case TIOCCDTR:
+ DPRINTF(("ucomioctl: TIOCCDTR\n"));
+ (void)ucomctl(sc, TIOCM_DTR, DMBIC);
+ break;
+
+ case TIOCMSET:
+ d = *(int *)data;
+ DPRINTF(("ucomioctl: TIOCMSET, 0x%x\n", d));
+ (void)ucomctl(sc, d, DMSET);
+ break;
+ case TIOCMBIS:
+ d = *(int *)data;
+ DPRINTF(("ucomioctl: TIOCMBIS, 0x%x\n", d));
+ (void)ucomctl(sc, d, DMBIS);
+ break;
+ case TIOCMBIC:
+ d = *(int *)data;
+ DPRINTF(("ucomioctl: TIOCMBIC, 0x%x\n", d));
+ (void)ucomctl(sc, d, DMBIC);
+ break;
+ case TIOCMGET:
+ d = ucomctl(sc, 0, DMGET);
+ DPRINTF(("ucomioctl: TIOCMGET, 0x%x\n", d));
+ *(int *)data = d;
+ break;
+
+ default:
+ DPRINTF(("ucomioctl: error: our cmd = 0x%08lx\n", cmd));
+ error = ENOTTY;
+ break;
+ }
+
+ splx(s);
+
+ return (error);
+}
+
+Static int
+ucomctl(struct ucom_softc *sc, int bits, int how)
+{
+ int mcr;
+ int msr;
+ int onoff;
+
+ DPRINTF(("ucomctl: bits = 0x%x, how = %d\n", bits, how));
+
+ if (how == DMGET) {
+ SET(bits, TIOCM_LE); /* always set TIOCM_LE bit */
+ DPRINTF(("ucomctl: DMGET: LE"));
+
+ mcr = sc->sc_mcr;
+ if (ISSET(mcr, UMCR_DTR)) {
+ SET(bits, TIOCM_DTR);
+ DPRINTF((" DTR"));
+ }
+ if (ISSET(mcr, UMCR_RTS)) {
+ SET(bits, TIOCM_RTS);
+ DPRINTF((" RTS"));
+ }
+
+ msr = sc->sc_msr;
+ if (ISSET(msr, UMSR_CTS)) {
+ SET(bits, TIOCM_CTS);
+ DPRINTF((" CTS"));
+ }
+ if (ISSET(msr, UMSR_DCD)) {
+ SET(bits, TIOCM_CD);
+ DPRINTF((" CD"));
+ }
+ if (ISSET(msr, UMSR_DSR)) {
+ SET(bits, TIOCM_DSR);
+ DPRINTF((" DSR"));
+ }
+ if (ISSET(msr, UMSR_RI)) {
+ SET(bits, TIOCM_RI);
+ DPRINTF((" RI"));
+ }
+
+ DPRINTF(("\n"));
+
+ return (bits);
+ }
+
+ mcr = 0;
+ if (ISSET(bits, TIOCM_DTR))
+ SET(mcr, UMCR_DTR);
+ if (ISSET(bits, TIOCM_RTS))
+ SET(mcr, UMCR_RTS);
+
+ switch (how) {
+ case DMSET:
+ sc->sc_mcr = mcr;
+ break;
+ case DMBIS:
+ sc->sc_mcr |= mcr;
+ break;
+ case DMBIC:
+ sc->sc_mcr &= ~mcr;
+ break;
+ }
+
+ onoff = ISSET(sc->sc_mcr, UMCR_DTR) ? 1 : 0;
+ ucom_dtr(sc, onoff);
+
+ onoff = ISSET(sc->sc_mcr, UMCR_RTS) ? 1 : 0;
+ ucom_rts(sc, onoff);
+
+ return (0);
+}
+
+Static void
+ucom_break(struct ucom_softc *sc, int onoff)
+{
+ DPRINTF(("ucom_break: onoff = %d\n", onoff));
+
+ if (sc->sc_callback->ucom_set == NULL)
+ return;
+ sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
+ UCOM_SET_BREAK, onoff);
+}
+
+Static void
+ucom_dtr(struct ucom_softc *sc, int onoff)
+{
+ DPRINTF(("ucom_dtr: onoff = %d\n", onoff));
+
+ if (sc->sc_callback->ucom_set == NULL)
+ return;
+ sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
+ UCOM_SET_DTR, onoff);
+}
+
+Static void
+ucom_rts(struct ucom_softc *sc, int onoff)
+{
+ DPRINTF(("ucom_rts: onoff = %d\n", onoff));
+
+ if (sc->sc_callback->ucom_set == NULL)
+ return;
+ sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
+ UCOM_SET_RTS, onoff);
+}
+
+void
+ucom_status_change(struct ucom_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+ u_char old_msr;
+ int onoff;
+
+ if (sc->sc_callback->ucom_get_status == NULL) {
+ sc->sc_lsr = 0;
+ sc->sc_msr = 0;
+ return;
+ }
+
+ old_msr = sc->sc_msr;
+ sc->sc_callback->ucom_get_status(sc->sc_parent, sc->sc_portno,
+ &sc->sc_lsr, &sc->sc_msr);
+ if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD)) {
+ if (sc->sc_poll == 0)
+ return;
+ onoff = ISSET(sc->sc_msr, UMSR_DCD) ? 1 : 0;
+ DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff));
+ ttyld_modem(tp, onoff);
+ }
+}
+
+Static int
+ucomparam(struct tty *tp, struct termios *t)
+{
+ struct ucom_softc *sc;
+ int error;
+ usbd_status uerr;
+
+ USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTF(("ucomparam: sc = %p\n", sc));
+
+ /* Check requested parameters. */
+ if (t->c_ospeed < 0) {
+ DPRINTF(("ucomparam: negative ospeed\n"));
+ return (EINVAL);
+ }
+ if (t->c_ispeed && t->c_ispeed != t->c_ospeed) {
+ DPRINTF(("ucomparam: mismatch ispeed and ospeed\n"));
+ return (EINVAL);
+ }
+
+ /*
+ * If there were no changes, don't do anything. This avoids dropping
+ * input and improves performance when all we did was frob things like
+ * VMIN and VTIME.
+ */
+ if (tp->t_ospeed == t->c_ospeed &&
+ tp->t_cflag == t->c_cflag)
+ return (0);
+
+ /* And copy to tty. */
+ tp->t_ispeed = 0;
+ tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = t->c_cflag;
+
+ if (sc->sc_callback->ucom_param == NULL)
+ return (0);
+
+ ucomstopread(sc);
+
+ error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t);
+ if (error) {
+ DPRINTF(("ucomparam: callback: error = %d\n", error));
+ return (error);
+ }
+
+ ttsetwater(tp);
+
+ if (t->c_cflag & CRTS_IFLOW) {
+ sc->sc_state |= UCS_RTS_IFLOW;
+ } else if (sc->sc_state & UCS_RTS_IFLOW) {
+ sc->sc_state &= ~UCS_RTS_IFLOW;
+ (void)ucomctl(sc, UMCR_RTS, DMBIS);
+ }
+
+ ttyldoptim(tp);
+
+ uerr = ucomstartread(sc);
+ if (uerr != USBD_NORMAL_COMPLETION)
+ return (EIO);
+
+ return (0);
+}
+
+Static void
+ucomstart(struct tty *tp)
+{
+ struct ucom_softc *sc;
+ struct cblock *cbp;
+ usbd_status err;
+ int s;
+ u_char *data;
+ int cnt;
+
+ USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
+ DPRINTF(("ucomstart: sc = %p\n", sc));
+
+ if (sc->sc_dying)
+ return;
+
+ s = spltty();
+
+ if (tp->t_state & TS_TBLOCK) {
+ if (ISSET(sc->sc_mcr, UMCR_RTS) &&
+ ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
+ DPRINTF(("ucomstart: clear RTS\n"));
+ (void)ucomctl(sc, UMCR_RTS, DMBIC);
+ }
+ } else {
+ if (!ISSET(sc->sc_mcr, UMCR_RTS) &&
+ tp->t_rawq.c_cc <= tp->t_ilowat &&
+ ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
+ DPRINTF(("ucomstart: set RTS\n"));
+ (void)ucomctl(sc, UMCR_RTS, DMBIS);
+ }
+ }
+
+ if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
+ ttwwakeup(tp);
+ DPRINTF(("ucomstart: stopped\n"));
+ goto out;
+ }
+
+ if (tp->t_outq.c_cc <= tp->t_olowat) {
+ if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
+ CLR(tp->t_state, TS_SO_OLOWAT);
+ wakeup(TSA_OLOWAT(tp));
+ }
+ selwakeuppri(&tp->t_wsel, TTIPRI);
+ if (tp->t_outq.c_cc == 0) {
+ if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
+ TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
+ CLR(tp->t_state, TS_SO_OCOMPLETE);
+ wakeup(TSA_OCOMPLETE(tp));
+ }
+ goto out;
+ }
+ }
+
+ /* Grab the first contiguous region of buffer space. */
+ data = tp->t_outq.c_cf;
+ cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
+ cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
+
+ if (cnt == 0) {
+ DPRINTF(("ucomstart: cnt == 0\n"));
+ goto out;
+ }
+
+ SET(tp->t_state, TS_BUSY);
+
+ if (cnt > sc->sc_obufsize) {
+ DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
+ cnt = sc->sc_obufsize;
+ }
+ if (sc->sc_callback->ucom_write != NULL)
+ sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno,
+ sc->sc_obuf, data, &cnt);
+ else
+ memcpy(sc->sc_obuf, data, cnt);
+
+ DPRINTF(("ucomstart: %d chars\n", cnt));
+ usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
+ (usbd_private_handle)sc, sc->sc_obuf, cnt,
+ USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
+ /* What can we do on error? */
+ err = usbd_transfer(sc->sc_oxfer);
+ if (err != USBD_IN_PROGRESS)
+ printf("ucomstart: err=%s\n", usbd_errstr(err));
+
+ ttwwakeup(tp);
+
+ out:
+ splx(s);
+}
+
+Static void
+ucomstop(struct tty *tp, int flag)
+{
+ struct ucom_softc *sc;
+ int s;
+
+ USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
+
+ DPRINTF(("ucomstop: %d\n", flag));
+
+ if (flag & FREAD) {
+ DPRINTF(("ucomstop: read\n"));
+ ucomstopread(sc);
+ }
+
+ if (flag & FWRITE) {
+ DPRINTF(("ucomstop: write\n"));
+ s = spltty();
+ if (ISSET(tp->t_state, TS_BUSY)) {
+ /* XXX do what? */
+ if (!ISSET(tp->t_state, TS_TTSTOP))
+ SET(tp->t_state, TS_FLUSH);
+ }
+ splx(s);
+ }
+
+ DPRINTF(("ucomstop: done\n"));
+}
+
+Static void
+ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct ucom_softc *sc = (struct ucom_softc *)p;
+ struct tty *tp = sc->sc_tty;
+ u_int32_t cc;
+ int s;
+
+ DPRINTF(("ucomwritecb: status = %d\n", status));
+
+ if (status == USBD_CANCELLED || sc->sc_dying)
+ goto error;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ printf("%s: ucomwritecb: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+ /* XXX we should restart after some delay. */
+ goto error;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
+ DPRINTF(("ucomwritecb: cc = %d\n", cc));
+ if (cc <= sc->sc_opkthdrlen) {
+ printf("%s: sent size too small, cc = %d\n",
+ USBDEVNAME(sc->sc_dev), cc);
+ goto error;
+ }
+
+ /* convert from USB bytes to tty bytes */
+ cc -= sc->sc_opkthdrlen;
+
+ s = spltty();
+ CLR(tp->t_state, TS_BUSY);
+ if (ISSET(tp->t_state, TS_FLUSH))
+ CLR(tp->t_state, TS_FLUSH);
+ else
+ ndflush(&tp->t_outq, cc);
+ ttyld_start(tp);
+ splx(s);
+
+ return;
+
+ error:
+ s = spltty();
+ CLR(tp->t_state, TS_BUSY);
+ splx(s);
+ return;
+}
+
+Static usbd_status
+ucomstartread(struct ucom_softc *sc)
+{
+ usbd_status err;
+
+ DPRINTF(("ucomstartread: start\n"));
+
+ sc->sc_state &= ~UCS_RXSTOP;
+
+ if (sc->sc_bulkin_pipe == NULL)
+ return (USBD_NORMAL_COMPLETION);
+
+ usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
+ (usbd_private_handle)sc,
+ sc->sc_ibuf, sc->sc_ibufsize,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, ucomreadcb);
+
+ err = usbd_transfer(sc->sc_ixfer);
+ if (err != USBD_IN_PROGRESS) {
+ DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err)));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static void
+ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct ucom_softc *sc = (struct ucom_softc *)p;
+ struct tty *tp = sc->sc_tty;
+ usbd_status err;
+ u_int32_t cc;
+ u_char *cp;
+ int lostcc;
+ int s;
+
+ DPRINTF(("ucomreadcb: status = %d\n", status));
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (!(sc->sc_state & UCS_RXSTOP))
+ printf("%s: ucomreadcb: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+ /* XXX we should restart after some delay. */
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
+ DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
+ if (cc == 0)
+ goto resubmit;
+
+ if (sc->sc_callback->ucom_read != NULL)
+ sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
+ &cp, &cc);
+
+ if (cc > sc->sc_ibufsize) {
+ printf("%s: invalid receive data size, %d chars\n",
+ USBDEVNAME(sc->sc_dev), cc);
+ goto resubmit;
+ }
+ if (cc < 1)
+ goto resubmit;
+
+ s = spltty();
+ if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
+ if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
+ && (sc->sc_state & UCS_RTS_IFLOW
+ || tp->t_iflag & IXOFF)
+ && !(tp->t_state & TS_TBLOCK))
+ ttyblock(tp);
+ lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
+ tp->t_rawcc += cc;
+ ttwakeup(tp);
+ if (tp->t_state & TS_TTSTOP
+ && (tp->t_iflag & IXANY
+ || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
+ tp->t_state &= ~TS_TTSTOP;
+ tp->t_lflag &= ~FLUSHO;
+ ucomstart(tp);
+ }
+ if (lostcc > 0)
+ printf("%s: lost %d chars\n", USBDEVNAME(sc->sc_dev),
+ lostcc);
+ } else {
+ /* Give characters to tty layer. */
+ while (cc > 0) {
+ DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
+ if (ttyld_rint(tp, *cp) == -1) {
+ /* XXX what should we do? */
+ printf("%s: lost %d chars\n",
+ USBDEVNAME(sc->sc_dev), cc);
+ break;
+ }
+ cc--;
+ cp++;
+ }
+ }
+ splx(s);
+
+ resubmit:
+ err = ucomstartread(sc);
+ if (err) {
+ printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
+ /* XXX what should we dow now? */
+ }
+
+ if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, UMCR_RTS)
+ && !(tp->t_state & TS_TBLOCK))
+ ucomctl(sc, UMCR_RTS, DMBIS);
+}
+
+Static void
+ucom_cleanup(struct ucom_softc *sc)
+{
+ DPRINTF(("ucom_cleanup: closing pipes\n"));
+
+ ucom_shutdown(sc);
+ if (sc->sc_bulkin_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+ }
+ if (sc->sc_bulkout_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+ }
+ if (sc->sc_ixfer != NULL) {
+ usbd_free_xfer(sc->sc_ixfer);
+ sc->sc_ixfer = NULL;
+ }
+ if (sc->sc_oxfer != NULL) {
+ usbd_free_xfer(sc->sc_oxfer);
+ sc->sc_oxfer = NULL;
+ }
+}
+
+Static void
+ucomstopread(struct ucom_softc *sc)
+{
+ usbd_status err;
+
+ DPRINTF(("ucomstopread: enter\n"));
+
+ if (!(sc->sc_state & UCS_RXSTOP)) {
+ sc->sc_state |= UCS_RXSTOP;
+ if (sc->sc_bulkin_pipe == NULL) {
+ DPRINTF(("ucomstopread: bulkin pipe NULL\n"));
+ return;
+ }
+ err = usbd_abort_pipe(sc->sc_bulkin_pipe);
+ if (err) {
+ DPRINTF(("ucomstopread: err = %s\n",
+ usbd_errstr(err)));
+ }
+ }
+
+ DPRINTF(("ucomstopread: leave\n"));
+}
diff --git a/sys/dev/usb/ucomvar.h b/sys/dev/usb/ucomvar.h
new file mode 100644
index 0000000..818c324
--- /dev/null
+++ b/sys/dev/usb/ucomvar.h
@@ -0,0 +1,182 @@
+/* $NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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.
+ */
+
+/* Module interface related macros */
+#define UCOM_MODVER 1
+
+#define UCOM_MINVER 1
+#define UCOM_PREFVER UCOM_MODVER
+#define UCOM_MAXVER 1
+
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~((unsigned)(f))
+#define ISSET(t, f) ((t) & (f))
+
+#define UCOM_CALLOUT_MASK 0x80
+
+#define UCOMUNIT_MASK 0x3ff7f
+#define UCOMDIALOUT_MASK 0x80000
+#define UCOMCALLUNIT_MASK 0x40000
+
+#define UCOMUNIT(x) (minor(x) & UCOMUNIT_MASK)
+#define UCOMDIALOUT(x) (minor(x) & UCOMDIALOUT_MASK)
+#define UCOMCALLUNIT(x) (minor(x) & UCOMCALLUNIT_MASK)
+
+#define UCOM_UNK_PORTNO -1 /* XXX */
+
+struct ucom_softc;
+
+struct ucom_callback {
+ void (*ucom_get_status)(void *, int, u_char *, u_char *);
+ void (*ucom_set)(void *, int, int, int);
+#define UCOM_SET_DTR 1
+#define UCOM_SET_RTS 2
+#define UCOM_SET_BREAK 3
+ int (*ucom_param)(void *, int, struct termios *);
+ int (*ucom_ioctl)(void *, int, u_long, caddr_t, int, usb_proc_ptr);
+ int (*ucom_open)(void *, int);
+ void (*ucom_close)(void *, int);
+ void (*ucom_read)(void *, int, u_char **, u_int32_t *);
+ void (*ucom_write)(void *, int, u_char *, u_char *, u_int32_t *);
+};
+
+/* modem control register */
+#define UMCR_RTS 0x02 /* Request To Send */
+#define UMCR_DTR 0x01 /* Data Terminal Ready */
+
+/* line status register */
+#define ULSR_RCV_FIFO 0x80
+#define ULSR_TSRE 0x40 /* Transmitter empty: byte sent */
+#define ULSR_TXRDY 0x20 /* Transmitter buffer empty */
+#define ULSR_BI 0x10 /* Break detected */
+#define ULSR_FE 0x08 /* Framing error: bad stop bit */
+#define ULSR_PE 0x04 /* Parity error */
+#define ULSR_OE 0x02 /* Overrun, lost incoming byte */
+#define ULSR_RXRDY 0x01 /* Byte ready in Receive Buffer */
+#define ULSR_RCV_MASK 0x1f /* Mask for incoming data or error */
+
+/* modem status register */
+/* All deltas are from the last read of the MSR. */
+#define UMSR_DCD 0x80 /* Current Data Carrier Detect */
+#define UMSR_RI 0x40 /* Current Ring Indicator */
+#define UMSR_DSR 0x20 /* Current Data Set Ready */
+#define UMSR_CTS 0x10 /* Current Clear to Send */
+#define UMSR_DDCD 0x08 /* DCD has changed state */
+#define UMSR_TERI 0x04 /* RI has toggled low to high */
+#define UMSR_DDSR 0x02 /* DSR has changed state */
+#define UMSR_DCTS 0x01 /* CTS has changed state */
+
+/* ucom state declarations */
+#define UCS_RXSTOP 0x0001 /* Rx stopped */
+#define UCS_RTS_IFLOW 0x0008 /* use RTS input flow control */
+
+struct ucom_softc {
+ USBBASEDEVICE sc_dev; /* base device */
+ usbd_device_handle sc_udev; /* USB device */
+ usbd_interface_handle sc_iface; /* data interface */
+
+ int sc_bulkin_no; /* bulk in endpoint address */
+ usbd_pipe_handle sc_bulkin_pipe; /* bulk in pipe */
+ usbd_xfer_handle sc_ixfer; /* read request */
+ u_char *sc_ibuf; /* read buffer */
+ u_int sc_ibufsize; /* read buffer size */
+ u_int sc_ibufsizepad; /* read buffer size padded */
+
+ int sc_bulkout_no; /* bulk out endpoint address */
+ usbd_pipe_handle sc_bulkout_pipe;/* bulk out pipe */
+ usbd_xfer_handle sc_oxfer; /* write request */
+ u_char *sc_obuf; /* write buffer */
+ u_int sc_obufsize; /* write buffer size */
+ u_int sc_opkthdrlen; /* header length of
+ output packet */
+
+ struct ucom_callback *sc_callback;
+ void *sc_parent;
+ int sc_portno;
+
+ struct tty *sc_tty; /* our tty */
+
+ int sc_state;
+
+ int sc_poll;
+
+ u_char sc_lsr;
+ u_char sc_msr;
+ u_char sc_mcr;
+
+ u_char sc_opening; /* lock during open */
+ int sc_refcnt;
+ u_char sc_dying; /* disconnecting */
+
+ struct cdev *dev; /* special device node */
+};
+
+extern devclass_t ucom_devclass;
+
+int ucom_attach(struct ucom_softc *);
+int ucom_detach(struct ucom_softc *);
+void ucom_status_change(struct ucom_softc *);
diff --git a/sys/dev/usb/udbp.c b/sys/dev/usb/udbp.c
new file mode 100644
index 0000000..1a126ec
--- /dev/null
+++ b/sys/dev/usb/udbp.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 1996-2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of author 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 NICK HIBMA AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Driver for arbitrary double bulk pipe devices.
+ * The driver assumes that there will be the same driver on the other side.
+ *
+ * XXX Some more information on what the framing of the IP packets looks like.
+ *
+ * To take full advantage of bulk transmission, packets should be chosen
+ * between 1k and 5k in size (1k to make sure the sending side starts
+ * straming, and <5k to avoid overflowing the system with small TDs).
+ */
+
+
+/* probe/attach/detach:
+ * Connect the driver to the hardware and netgraph
+ *
+ * udbp_setup_out_transfer(sc);
+ * Setup an outbound transfer. Only one transmit can be active at the same
+ * time.
+ * XXX If it is required that the driver is able to queue multiple requests
+ * let me know. That is slightly difficult, due to the fact that we
+ * cannot call usbd_alloc_xfer in int context.
+ *
+ * udbp_setup_in_transfer(sc)
+ * Prepare an in transfer that will be waiting for data to come in. It
+ * is submitted and sits there until data is available.
+ * The callback resubmits a new transfer on completion.
+ *
+ * The reason we submit a bulk in transfer is that USB does not know about
+ * interrupts. The bulk transfer continuously polls the device for data.
+ * While the device has no data available, the device NAKs the TDs. As soon
+ * as there is data, the transfer happens and the data comes flowing in.
+ *
+ * In case you were wondering, interrupt transfers happen exactly that way.
+ * It therefore doesn't make sense to use the interrupt pipe to signal
+ * 'data ready' and then schedule a bulk transfer to fetch it. That would
+ * incur a 2ms delay at least, without reducing bandwidth requirements.
+ *
+ */
+
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/poll.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <net/if.h>
+#include <machine/bus.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/usbhid.h>
+
+#include <dev/usb/usbdevs.h>
+
+
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_parse.h>
+#include <dev/usb/udbp.h>
+#include <netgraph/netgraph.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (udbpdebug) logprintf x
+#define DPRINTFN(n,x) if (udbpdebug>(n)) logprintf x
+int udbpdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp");
+SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RW,
+ &udbpdebug, 0, "udbp debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+#define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in msecs */
+#define UDBP_BUFFERSIZE 2048 /* maximum number of bytes in one transfer */
+
+
+struct udbp_softc {
+ device_t sc_dev; /* base device */
+ usbd_interface_handle sc_iface;
+
+ usbd_pipe_handle sc_bulkin_pipe;
+ int sc_bulkin;
+ usbd_xfer_handle sc_bulkin_xfer;
+ void *sc_bulkin_buffer;
+ int sc_bulkin_bufferlen;
+ int sc_bulkin_datalen;
+
+ usbd_pipe_handle sc_bulkout_pipe;
+ int sc_bulkout;
+ usbd_xfer_handle sc_bulkout_xfer;
+ void *sc_bulkout_buffer;
+ int sc_bulkout_bufferlen;
+ int sc_bulkout_datalen;
+
+ int flags;
+# define DISCONNECTED 0x01
+# define OUT_BUSY 0x02
+# define NETGRAPH_INITIALISED 0x04
+ node_p node; /* back pointer to node */
+ hook_p hook; /* pointer to the hook */
+ u_int packets_in; /* packets in from downstream */
+ u_int packets_out; /* packets out towards downstream */
+ struct ifqueue xmitq_hipri; /* hi-priority transmit queue */
+ struct ifqueue xmitq; /* low-priority transmit queue */
+
+};
+typedef struct udbp_softc *udbp_p;
+
+
+
+Static ng_constructor_t ng_udbp_constructor;
+Static ng_rcvmsg_t ng_udbp_rcvmsg;
+Static ng_shutdown_t ng_udbp_rmnode;
+Static ng_newhook_t ng_udbp_newhook;
+Static ng_connect_t ng_udbp_connect;
+Static ng_rcvdata_t ng_udbp_rcvdata;
+Static ng_disconnect_t ng_udbp_disconnect;
+
+/* Parse type for struct ngudbpstat */
+Static const struct ng_parse_struct_field
+ ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO;
+Static const struct ng_parse_type ng_udbp_stat_type = {
+ &ng_parse_struct_type,
+ &ng_udbp_stat_type_fields
+};
+
+/* List of commands and how to convert arguments to/from ASCII */
+Static const struct ng_cmdlist ng_udbp_cmdlist[] = {
+ {
+ NGM_UDBP_COOKIE,
+ NGM_UDBP_GET_STATUS,
+ "getstatus",
+ NULL,
+ &ng_udbp_stat_type,
+ },
+ {
+ NGM_UDBP_COOKIE,
+ NGM_UDBP_SET_FLAG,
+ "setflag",
+ &ng_parse_int32_type,
+ NULL
+ },
+ { 0 }
+};
+
+/* Netgraph node type descriptor */
+Static struct ng_type ng_udbp_typestruct = {
+ .version = NG_ABI_VERSION,
+ .name = NG_UDBP_NODE_TYPE,
+ .constructor = ng_udbp_constructor,
+ .rcvmsg = ng_udbp_rcvmsg,
+ .shutdown = ng_udbp_rmnode,
+ .newhook = ng_udbp_newhook,
+ .connect = ng_udbp_connect,
+ .rcvdata = ng_udbp_rcvdata,
+ .disconnect = ng_udbp_disconnect,
+ .cmdlist = ng_udbp_cmdlist,
+};
+
+Static int udbp_setup_in_transfer (udbp_p sc);
+Static void udbp_in_transfer_cb (usbd_xfer_handle xfer,
+ usbd_private_handle priv,
+ usbd_status err);
+
+Static int udbp_setup_out_transfer (udbp_p sc);
+Static void udbp_out_transfer_cb (usbd_xfer_handle xfer,
+ usbd_private_handle priv,
+ usbd_status err);
+
+USB_DECLARE_DRIVER(udbp);
+
+USB_MATCH(udbp)
+{
+ USB_MATCH_START(udbp, uaa);
+ usb_interface_descriptor_t *id;
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+
+ /* XXX Julian, add the id of the device if you have one to test
+ * things with. run 'usbdevs -v' and note the 3 ID's that appear.
+ * The Vendor Id and Product Id are in hex and the Revision Id is in
+ * bcd. But as usual if the revision is 0x101 then you should compare
+ * the revision id in the device descriptor with 0x101
+ * Or go search the file usbdevs.h. Maybe the device is already in
+ * there.
+ */
+ if ((uaa->vendor == USB_VENDOR_NETCHIP &&
+ uaa->product == USB_PRODUCT_NETCHIP_TURBOCONNECT))
+ return(UMATCH_VENDOR_PRODUCT);
+
+ if ((uaa->vendor == USB_VENDOR_PROLIFIC &&
+ (uaa->product == USB_PRODUCT_PROLIFIC_PL2301 ||
+ uaa->product == USB_PRODUCT_PROLIFIC_PL2302)))
+ return(UMATCH_VENDOR_PRODUCT);
+
+ if ((uaa->vendor == USB_VENDOR_ANCHOR &&
+ uaa->product == USB_PRODUCT_ANCHOR_EZLINK))
+ return(UMATCH_VENDOR_PRODUCT);
+
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(udbp)
+{
+ USB_ATTACH_START(udbp, sc, uaa);
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed, *ed_bulkin = NULL, *ed_bulkout = NULL;
+ usbd_status err;
+ char devinfo[1024];
+ int i;
+ static int ngudbp_done_init=0;
+
+ sc->flags |= DISCONNECTED;
+ /* fetch the interface handle for the first interface */
+ (void) usbd_device2interface_handle(uaa->device, 0, &iface);
+ id = usbd_get_interface_descriptor(iface);
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
+ devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
+
+ /* Find the two first bulk endpoints */
+ for (i = 0 ; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (!ed) {
+ printf("%s: could not read endpoint descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ed_bulkin = ed;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ed_bulkout = ed;
+ }
+
+ if (ed_bulkin && ed_bulkout) /* found all we need */
+ break;
+ }
+
+ /* Verify that we goething sensible */
+ if (ed_bulkin == NULL || ed_bulkout == NULL) {
+ printf("%s: bulk-in and/or bulk-out endpoint not found\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ if (ed_bulkin->wMaxPacketSize[0] != ed_bulkout->wMaxPacketSize[0] ||
+ ed_bulkin->wMaxPacketSize[1] != ed_bulkout->wMaxPacketSize[1]) {
+ printf("%s: bulk-in and bulk-out have different packet sizes %d %d %d %d\n",
+ USBDEVNAME(sc->sc_dev),
+ ed_bulkin->wMaxPacketSize[0],
+ ed_bulkout->wMaxPacketSize[0],
+ ed_bulkin->wMaxPacketSize[1],
+ ed_bulkout->wMaxPacketSize[1]);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_bulkin = ed_bulkin->bEndpointAddress;
+ sc->sc_bulkout = ed_bulkout->bEndpointAddress;
+
+ DPRINTF(("%s: Bulk-in: 0x%02x, bulk-out 0x%02x, packet size = %d\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkin, sc->sc_bulkout,
+ ed_bulkin->wMaxPacketSize[0]));
+
+ /* Allocate the in transfer struct */
+ sc->sc_bulkin_xfer = usbd_alloc_xfer(uaa->device);
+ if (!sc->sc_bulkin_xfer) {
+ goto bad;
+ }
+ sc->sc_bulkout_xfer = usbd_alloc_xfer(uaa->device);
+ if (!sc->sc_bulkout_xfer) {
+ goto bad;
+ }
+ sc->sc_bulkin_buffer = malloc(UDBP_BUFFERSIZE, M_USBDEV, M_WAITOK);
+ if (!sc->sc_bulkin_buffer) {
+ goto bad;
+ }
+ sc->sc_bulkout_buffer = malloc(UDBP_BUFFERSIZE, M_USBDEV, M_WAITOK);
+ if (!sc->sc_bulkout_xfer || !sc->sc_bulkout_buffer) {
+ goto bad;
+ }
+ sc->sc_bulkin_bufferlen = UDBP_BUFFERSIZE;
+ sc->sc_bulkout_bufferlen = UDBP_BUFFERSIZE;
+
+ /* We have decided on which endpoints to use, now open the pipes */
+ err = usbd_open_pipe(iface, sc->sc_bulkin,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
+ if (err) {
+ printf("%s: cannot open bulk-in pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkin);
+ goto bad;
+ }
+ err = usbd_open_pipe(iface, sc->sc_bulkout,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ printf("%s: cannot open bulk-out pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkout);
+ goto bad;
+ }
+
+ if (!ngudbp_done_init){
+ ngudbp_done_init=1;
+ if (ng_newtype(&ng_udbp_typestruct)) {
+ printf("ngudbp install failed\n");
+ goto bad;
+ }
+ }
+
+ if ((err = ng_make_node_common(&ng_udbp_typestruct, &sc->node)) == 0) {
+ char nodename[128];
+ sprintf(nodename, "%s", USBDEVNAME(sc->sc_dev));
+ if ((err = ng_name_node(sc->node, nodename))) {
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL;
+ goto bad;
+ } else {
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+ sc->xmitq.ifq_maxlen = IFQ_MAXLEN;
+ sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN;
+ mtx_init(&sc->xmitq.ifq_mtx, "usb_xmitq", NULL,
+ MTX_DEF);
+ mtx_init(&sc->xmitq_hipri.ifq_mtx,
+ "usb_xmitq_hipri", NULL, MTX_DEF);
+ }
+ }
+ sc->flags = NETGRAPH_INITIALISED;
+ /* sc->flags &= ~DISCONNECTED; */ /* XXX */
+
+
+ /* the device is now operational */
+
+
+ /* schedule the first incoming xfer */
+ err = udbp_setup_in_transfer(sc);
+ if (err) {
+ goto bad;
+ }
+ USB_ATTACH_SUCCESS_RETURN;
+bad:
+#if 0 /* probably done in udbp_detach() */
+ if (sc->sc_bulkout_buffer) {
+ FREE(sc->sc_bulkout_buffer, M_USBDEV);
+ }
+ if (sc->sc_bulkin_buffer) {
+ FREE(sc->sc_bulkin_buffer, M_USBDEV);
+ }
+ if (sc->sc_bulkout_xfer) {
+ usbd_free_xfer(sc->sc_bulkout_xfer);
+ }
+ if (sc->sc_bulkin_xfer) {
+ usbd_free_xfer(sc->sc_bulkin_xfer);
+ }
+#endif
+ udbp_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+
+USB_DETACH(udbp)
+{
+ USB_DETACH_START(udbp, sc);
+
+ sc->flags |= DISCONNECTED;
+
+ DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
+
+ if (sc->sc_bulkin_pipe) {
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ }
+ if (sc->sc_bulkout_pipe) {
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ }
+
+ if (sc->flags & NETGRAPH_INITIALISED) {
+ ng_rmnode_self(sc->node);
+ NG_NODE_SET_PRIVATE(sc->node, NULL);
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL; /* Paranoid */
+ }
+
+ if (sc->sc_bulkin_xfer)
+ usbd_free_xfer(sc->sc_bulkin_xfer);
+ if (sc->sc_bulkout_xfer)
+ usbd_free_xfer(sc->sc_bulkout_xfer);
+
+ if (sc->sc_bulkin_buffer)
+ free(sc->sc_bulkin_buffer, M_USBDEV);
+ if (sc->sc_bulkout_buffer)
+ free(sc->sc_bulkout_buffer, M_USBDEV);
+ return 0;
+}
+
+
+Static int
+udbp_setup_in_transfer(udbp_p sc)
+{
+ void *priv = sc; /* XXX this should probably be some pointer to
+ * struct describing the transfer (mbuf?)
+ * See also below.
+ */
+ usbd_status err;
+
+ /* XXX
+ * How should we arrange for 2 extra bytes at the start of the
+ * packet?
+ */
+
+ /* Initialise a USB transfer and then schedule it */
+
+ (void) usbd_setup_xfer( sc->sc_bulkin_xfer,
+ sc->sc_bulkin_pipe,
+ priv,
+ sc->sc_bulkin_buffer,
+ sc->sc_bulkin_bufferlen,
+ USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT,
+ udbp_in_transfer_cb);
+
+ err = usbd_transfer(sc->sc_bulkin_xfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(("%s: failed to setup in-transfer, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+ return(err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static void
+udbp_in_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ udbp_p sc = priv; /* XXX see priv above */
+ int s;
+ int len;
+ struct mbuf *m;
+
+ if (err) {
+ if (err != USBD_CANCELLED) {
+ DPRINTF(("%s: bulk-out transfer failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+ } else {
+ /* USBD_CANCELLED happens at unload of the driver */
+ return;
+ }
+
+ /* Transfer has failed, packet is not received */
+ } else {
+
+ len = xfer->actlen;
+
+ s = splimp(); /* block network stuff too */
+ if (sc->hook) {
+ /* get packet from device and send on */
+ m = m_devget(sc->sc_bulkin_buffer, len, 0, NULL, NULL);
+ NG_SEND_DATA_ONLY(err, sc->hook, m);
+ }
+ splx(s);
+
+ }
+ /* schedule the next in transfer */
+ udbp_setup_in_transfer(sc);
+}
+
+
+Static int
+udbp_setup_out_transfer(udbp_p sc)
+{
+ void *priv = sc; /* XXX this should probably be some pointer to
+ * struct describing the transfer (mbuf?)
+ * See also below.
+ */
+ int pktlen;
+ usbd_status err;
+ int s, s1;
+ struct mbuf *m;
+
+
+ s = splusb();
+ if (sc->flags & OUT_BUSY)
+ panic("out transfer already in use, we should add queuing");
+ sc->flags |= OUT_BUSY;
+ splx(s);
+ s1 = splimp(); /* Queueing happens at splnet */
+ IF_DEQUEUE(&sc->xmitq_hipri, m);
+ if (m == NULL) {
+ IF_DEQUEUE(&sc->xmitq, m);
+ }
+ splx(s1);
+
+ if (!m) {
+ sc->flags &= ~OUT_BUSY;
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ pktlen = m->m_pkthdr.len;
+ if (pktlen > sc->sc_bulkout_bufferlen) {
+ printf("%s: Packet too large, %d > %d\n",
+ USBDEVNAME(sc->sc_dev), pktlen,
+ sc->sc_bulkout_bufferlen);
+ return (USBD_IOERROR);
+ }
+
+ m_copydata(m, 0, pktlen, sc->sc_bulkout_buffer);
+ m_freem(m);
+
+ /* Initialise a USB transfer and then schedule it */
+
+ (void) usbd_setup_xfer( sc->sc_bulkout_xfer,
+ sc->sc_bulkout_pipe,
+ priv,
+ sc->sc_bulkout_buffer,
+ pktlen,
+ USBD_SHORT_XFER_OK,
+ UDBP_TIMEOUT,
+ udbp_out_transfer_cb);
+
+ err = usbd_transfer(sc->sc_bulkout_xfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(("%s: failed to setup out-transfer, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+ return(err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static void
+udbp_out_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ udbp_p sc = priv; /* XXX see priv above */
+ int s;
+
+ if (err) {
+ DPRINTF(("%s: bulk-out transfer failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+ /* Transfer has failed, packet is not transmitted */
+ /* XXX Invalidate packet */
+ return;
+ }
+
+ /* packet has been transmitted */
+
+ s = splusb(); /* mark the buffer available */
+ sc->flags &= ~OUT_BUSY;
+ udbp_setup_out_transfer(sc);
+ splx(s);
+}
+
+DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
+
+
+/***********************************************************************
+ * Start of Netgraph methods
+ **********************************************************************/
+
+/*
+ * If this is a device node so this work is done in the attach()
+ * routine and the constructor will return EINVAL as you should not be able
+ * to create nodes that depend on hardware (unless you can add the hardware :)
+ */
+Static int
+ng_udbp_constructor(node_p node)
+{
+ return (EINVAL);
+}
+
+/*
+ * Give our ok for a hook to be added...
+ * If we are not running this might kick a device into life.
+ * Possibly decode information out of the hook name.
+ * Add the hook's private info to the hook structure.
+ * (if we had some). In this example, we assume that there is a
+ * an array of structs, called 'channel' in the private info,
+ * one for each active channel. The private
+ * pointer of each hook points to the appropriate UDBP_hookinfo struct
+ * so that the source of an input packet is easily identified.
+ */
+Static int
+ng_udbp_newhook(node_p node, hook_p hook, const char *name)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(node);
+
+#if 0
+ /* Possibly start up the device if it's not already going */
+ if ((sc->flags & SCF_RUNNING) == 0) {
+ ng_udbp_start_hardware(sc);
+ }
+#endif
+
+ if (strcmp(name, NG_UDBP_HOOK_NAME) == 0) {
+ sc->hook = hook;
+ NG_HOOK_SET_PRIVATE(hook, NULL);
+ } else {
+ return (EINVAL); /* not a hook we know about */
+ }
+ return(0);
+}
+
+/*
+ * Get a netgraph control message.
+ * Check it is one we understand. If needed, send a response.
+ * We could save the address for an async action later, but don't here.
+ * Always free the message.
+ * The response should be in a malloc'd region that the caller can 'free'.
+ * A response is not required.
+ * Theoretically you could respond defferently to old message types if
+ * the cookie in the header didn't match what we consider to be current
+ * (so that old userland programs could continue to work).
+ */
+Static int
+ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(node);
+ struct ng_mesg *resp = NULL;
+ int error = 0;
+ struct ng_mesg *msg;
+
+ NGI_GET_MSG(item, msg);
+ /* Deal with message according to cookie and command */
+ switch (msg->header.typecookie) {
+ case NGM_UDBP_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_UDBP_GET_STATUS:
+ {
+ struct ngudbpstat *stats;
+
+ NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
+ if (!resp) {
+ error = ENOMEM;
+ break;
+ }
+ stats = (struct ngudbpstat *) resp->data;
+ stats->packets_in = sc->packets_in;
+ stats->packets_out = sc->packets_out;
+ break;
+ }
+ case NGM_UDBP_SET_FLAG:
+ if (msg->header.arglen != sizeof(u_int32_t)) {
+ error = EINVAL;
+ break;
+ }
+ sc->flags = *((u_int32_t *) msg->data);
+ break;
+ default:
+ error = EINVAL; /* unknown command */
+ break;
+ }
+ break;
+ default:
+ error = EINVAL; /* unknown cookie type */
+ break;
+ }
+
+ /* Take care of synchronous response, if any */
+ NG_RESPOND_MSG(error, node, item, resp);
+ NG_FREE_MSG(msg);
+ return(error);
+}
+
+/*
+ * Accept data from the hook and queue it for output.
+ */
+Static int
+ng_udbp_rcvdata(hook_p hook, item_p item)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ int error;
+ struct ifqueue *xmitq_p;
+ int s;
+ struct mbuf *m;
+ struct ng_tag_prio *ptag;
+
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ /*
+ * Now queue the data for when it can be sent
+ */
+ if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE,
+ NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) )
+ xmitq_p = (&sc->xmitq_hipri);
+ else
+ xmitq_p = (&sc->xmitq);
+
+ s = splusb();
+ IF_LOCK(xmitq_p);
+ if (_IF_QFULL(xmitq_p)) {
+ _IF_DROP(xmitq_p);
+ IF_UNLOCK(xmitq_p);
+ splx(s);
+ error = ENOBUFS;
+ goto bad;
+ }
+ _IF_ENQUEUE(xmitq_p, m);
+ IF_UNLOCK(xmitq_p);
+ if (!(sc->flags & OUT_BUSY))
+ udbp_setup_out_transfer(sc);
+ splx(s);
+ return (0);
+
+bad: /*
+ * It was an error case.
+ * check if we need to free the mbuf, and then return the error
+ */
+ NG_FREE_M(m);
+ return (error);
+}
+
+/*
+ * Do local shutdown processing..
+ * We are a persistant device, we refuse to go away, and
+ * only remove our links and reset ourself.
+ */
+Static int
+ng_udbp_rmnode(node_p node)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(node);
+ int err;
+
+ if (sc->flags & DISCONNECTED) {
+ /*
+ * WE are really going away.. hardware must have gone.
+ * Assume that the hardware drive part will clear up the
+ * sc, in fact it may already have done so..
+ * In which case we may have just segfaulted..XXX
+ */
+ return (0);
+ }
+
+ /* stolen from attach routine */
+ /* Drain the queues */
+ IF_DRAIN(&sc->xmitq_hipri);
+ IF_DRAIN(&sc->xmitq);
+
+ sc->packets_in = 0; /* reset stats */
+ sc->packets_out = 0;
+ NG_NODE_UNREF(node); /* forget it ever existed */
+
+ if ((err = ng_make_node_common(&ng_udbp_typestruct, &sc->node)) == 0) {
+ char nodename[128];
+ sprintf(nodename, "%s", USBDEVNAME(sc->sc_dev));
+ if ((err = ng_name_node(sc->node, nodename))) {
+ NG_NODE_UNREF(sc->node); /* out damned spot! */
+ sc->flags &= ~NETGRAPH_INITIALISED;
+ sc->node = NULL;
+ } else {
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+ }
+ }
+ return (err);
+}
+
+/*
+ * This is called once we've already connected a new hook to the other node.
+ * It gives us a chance to balk at the last minute.
+ */
+Static int
+ng_udbp_connect(hook_p hook)
+{
+ /* probably not at splnet, force outward queueing */
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+ /* be really amiable and just say "YUP that's OK by me! " */
+ return (0);
+}
+
+/*
+ * Dook disconnection
+ *
+ * For this type, removal of the last link destroys the node
+ */
+Static int
+ng_udbp_disconnect(hook_p hook)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ sc->hook = NULL;
+
+ if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
+ && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
+ ng_rmnode_self(NG_HOOK_NODE(hook));
+ return (0);
+}
+
diff --git a/sys/dev/usb/udbp.h b/sys/dev/usb/udbp.h
new file mode 100644
index 0000000..0b7b4ee
--- /dev/null
+++ b/sys/dev/usb/udbp.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1996-2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file was derived from src/sys/netgraph/ng_sample.h, revision 1.1
+ * written by Julian Elischer, Whistle Communications.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_UDBP_H_
+#define _NETGRAPH_UDBP_H_
+
+/* Node type name. This should be unique among all netgraph node types */
+#define NG_UDBP_NODE_TYPE "udbp"
+
+/* Node type cookie. Should also be unique. This value MUST change whenever
+ an incompatible change is made to this header file, to insure consistency.
+ The de facto method for generating cookies is to take the output of the
+ date command: date -u +'%s' */
+#define NGM_UDBP_COOKIE 944609300
+
+
+#define NG_UDBP_HOOK_NAME "data"
+
+/* Netgraph commands understood by this node type */
+enum {
+ NGM_UDBP_SET_FLAG = 1,
+ NGM_UDBP_GET_STATUS,
+};
+
+/* This structure is returned by the NGM_UDBP_GET_STATUS command */
+struct ngudbpstat {
+ u_int packets_in; /* packets in from downstream */
+ u_int packets_out; /* packets out towards downstream */
+};
+
+/*
+ * This is used to define the 'parse type' for a struct ngudbpstat, which
+ * is bascially a description of how to convert a binary struct ngudbpstat
+ * to an ASCII string and back. See ng_parse.h for more info.
+ *
+ * This needs to be kept in sync with the above structure definition
+ */
+#define NG_UDBP_STATS_TYPE_INFO { \
+ { "packets_in", &ng_parse_int32_type }, \
+ { "packets_out", &ng_parse_int32_type }, \
+ { NULL }, \
+}
+
+#endif /* _NETGRAPH_UDBP_H_ */
diff --git a/sys/dev/usb/ufm.c b/sys/dev/usb/ufm.c
new file mode 100644
index 0000000..f8d640b
--- /dev/null
+++ b/sys/dev/usb/ufm.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2001 M. Warner Losh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+#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>
+#endif
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/dsbr100io.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ufmdebug) logprintf x
+#define DPRINTFN(n,x) if (ufmdebug>(n)) logprintf x
+int ufmdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ufm, CTLFLAG_RW, 0, "USB ufm");
+SYSCTL_INT(_hw_usb_ufm, OID_AUTO, debug, CTLFLAG_RW,
+ &ufmdebug, 0, "ufm debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int ufmopen(dev_t, int, int, usb_proc_ptr);
+int ufmclose(dev_t, int, int, usb_proc_ptr);
+int ufmioctl(dev_t, u_long, caddr_t, int, usb_proc_ptr);
+
+cdev_decl(ufm);
+#elif defined(__FreeBSD__)
+d_open_t ufmopen;
+d_close_t ufmclose;
+d_ioctl_t ufmioctl;
+
+Static struct cdevsw ufm_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ufmopen,
+ .d_close = ufmclose,
+ .d_ioctl = ufmioctl,
+ .d_name = "ufm",
+#if (__FreeBSD_version < 500014)
+ .d_bmaj = -1
+#endif
+};
+#endif /*defined(__FreeBSD__)*/
+
+#define FM_CMD0 0x00
+#define FM_CMD_SET_FREQ 0x01
+#define FM_CMD2 0x02
+
+struct ufm_softc {
+ USBBASEDEVICE sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+
+ int sc_opened;
+ int sc_epaddr;
+ int sc_freq;
+
+ int sc_refcnt;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ u_char sc_dying;
+#endif
+};
+
+#define UFMUNIT(n) (minor(n))
+
+USB_DECLARE_DRIVER(ufm);
+
+USB_MATCH(ufm)
+{
+ USB_MATCH_START(ufm, uaa);
+ usb_device_descriptor_t *dd;
+
+ DPRINTFN(10,("ufm_match\n"));
+ if (!uaa->iface)
+ return UMATCH_NONE;
+
+ dd = usbd_get_device_descriptor(uaa->device);
+
+ if (dd &&
+ ((UGETW(dd->idVendor) == USB_VENDOR_CYPRESS &&
+ UGETW(dd->idProduct) == USB_PRODUCT_CYPRESS_FMRADIO)))
+ return UMATCH_VENDOR_PRODUCT;
+ else
+ return UMATCH_NONE;
+}
+
+USB_ATTACH(ufm)
+{
+ USB_ATTACH_START(ufm, sc, uaa);
+ char devinfo[1024];
+ usb_endpoint_descriptor_t *edesc;
+ usbd_device_handle udev;
+ usbd_interface_handle iface;
+ u_int8_t epcount;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ u_int8_t niface;
+#endif
+ usbd_status r;
+ char * ermsg = "<none>";
+
+ DPRINTFN(10,("ufm_attach: sc=%p\n", sc));
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ sc->sc_udev = udev = uaa->device;
+
+#if defined(__FreeBSD__)
+ if ((!uaa->device) || (!uaa->iface)) {
+ ermsg = "device or iface";
+ goto nobulk;
+ }
+ sc->sc_iface = iface = uaa->iface;
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ if (!udev) {
+ ermsg = "device";
+ goto nobulk;
+ }
+ r = usbd_interface_count(udev, &niface);
+ if (r) {
+ ermsg = "iface";
+ goto nobulk;
+ }
+ r = usbd_device2interface_handle(udev, 0, &iface);
+ if (r) {
+ ermsg = "iface";
+ goto nobulk;
+ }
+ sc->sc_iface = iface;
+#endif
+ sc->sc_opened = 0;
+ sc->sc_refcnt = 0;
+
+ r = usbd_endpoint_count(iface, &epcount);
+ if (r != USBD_NORMAL_COMPLETION) {
+ ermsg = "endpoints";
+ goto nobulk;
+ }
+
+ edesc = usbd_interface2endpoint_descriptor(iface, 0);
+ if (!edesc) {
+ ermsg = "interface endpoint";
+ goto nobulk;
+ }
+ sc->sc_epaddr = edesc->bEndpointAddress;
+
+#if defined(__FreeBSD__)
+ /* XXX no error trapping, no storing of struct cdev **/
+ (void) make_dev(&ufm_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR,
+ 0644, "ufm%d", device_get_unit(self));
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+#endif
+
+ DPRINTFN(10, ("ufm_attach: %p\n", sc->sc_udev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+
+ nobulk:
+ printf("%s: could not find %s\n", USBDEVNAME(sc->sc_dev),ermsg);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+
+int
+ufmopen(struct cdev *dev, int flag, int mode, usb_proc_ptr td)
+{
+ struct ufm_softc *sc;
+
+ int unit = UFMUNIT(dev);
+ USB_GET_SC_OPEN(ufm, unit, sc);
+
+ DPRINTFN(5, ("ufmopen: flag=%d, mode=%d, unit=%d\n",
+ flag, mode, unit));
+
+ if (sc->sc_opened)
+ return (EBUSY);
+
+ if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD))
+ return (EACCES);
+
+ sc->sc_opened = 1;
+ return (0);
+}
+
+int
+ufmclose(struct cdev *dev, int flag, int mode, usb_proc_ptr td)
+{
+ struct ufm_softc *sc;
+
+ int unit = UFMUNIT(dev);
+ USB_GET_SC(ufm, unit, sc);
+
+ DPRINTFN(5, ("ufmclose: flag=%d, mode=%d, unit=%d\n", flag, mode, unit));
+ sc->sc_opened = 0;
+ sc->sc_refcnt = 0;
+ return 0;
+}
+
+static int
+ufm_do_req(struct ufm_softc *sc, u_int8_t reqtype, u_int8_t request,
+ u_int16_t value, u_int16_t index, u_int8_t len, void *retbuf)
+{
+ int s;
+ usb_device_request_t req;
+ usbd_status err;
+
+ s = splusb();
+ req.bmRequestType = reqtype;
+ req.bRequest = request;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, len);
+ err = usbd_do_request_flags(sc->sc_udev, &req, retbuf, 0, NULL,
+ USBD_DEFAULT_TIMEOUT);
+ splx(s);
+ if (err) {
+ printf("usbd_do_request_flags returned %#x\n", err);
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+ufm_set_freq(struct ufm_softc *sc, caddr_t addr)
+{
+ int freq = *(int *)addr;
+ u_int8_t ret;
+
+ /*
+ * Freq now is in Hz. We need to convert it to the frequency
+ * that the radio wants. This frequency is 10.7MHz above
+ * the actual frequency. We then need to convert to
+ * units of 12.5kHz. We add one to the IFM to make rounding
+ * easier.
+ */
+ sc->sc_freq = freq;
+ freq = (freq + 10700001) / 12500;
+ /* This appears to set the frequency */
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD_SET_FREQ, freq >> 8,
+ freq, 1, &ret) != 0)
+ return (EIO);
+ /* Not sure what this does */
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x96, 0xb7, 1,
+ &ret) != 0)
+ return (EIO);
+ return (0);
+}
+
+static int
+ufm_get_freq(struct ufm_softc *sc, caddr_t addr)
+{
+ int *valp = (int *)addr;
+ *valp = sc->sc_freq;
+ return (0);
+}
+
+static int
+ufm_start(struct ufm_softc *sc, caddr_t addr)
+{
+ u_int8_t ret;
+
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x00, 0xc7,
+ 1, &ret))
+ return (EIO);
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD2, 0x01, 0x00,
+ 1, &ret))
+ return (EIO);
+ if (ret & 0x1)
+ return (EIO);
+ return (0);
+}
+
+static int
+ufm_stop(struct ufm_softc *sc, caddr_t addr)
+{
+ u_int8_t ret;
+
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x16, 0x1C,
+ 1, &ret))
+ return (EIO);
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD2, 0x00, 0x00,
+ 1, &ret))
+ return (EIO);
+ return (0);
+}
+
+static int
+ufm_get_stat(struct ufm_softc *sc, caddr_t addr)
+{
+ u_int8_t ret;
+
+ /*
+ * Note, there's a 240ms settle time before the status
+ * will be valid, so tsleep that amount. hz/4 is a good
+ * approximation of that. Since this is a short sleep
+ * we don't try to catch any signals to keep things
+ * simple.
+ */
+ tsleep(sc, 0, "ufmwait", hz/4);
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x00, 0x24,
+ 1, &ret))
+ return (EIO);
+ *(int *)addr = ret;
+
+ return (0);
+}
+
+int
+ufmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr td)
+{
+ struct ufm_softc *sc;
+
+ int unit = UFMUNIT(dev);
+ int error = 0;
+
+ USB_GET_SC(ufm, unit, sc);
+
+ switch (cmd) {
+ case FM_SET_FREQ:
+ error = ufm_set_freq(sc, addr);
+ break;
+ case FM_GET_FREQ:
+ error = ufm_get_freq(sc, addr);
+ break;
+ case FM_START:
+ error = ufm_start(sc, addr);
+ break;
+ case FM_STOP:
+ error = ufm_stop(sc, addr);
+ break;
+ case FM_GET_STAT:
+ error = ufm_get_stat(sc, addr);
+ break;
+ default:
+ return ENOTTY;
+ break;
+ }
+ return error;
+}
+
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+ufm_activate(device_ptr_t self, enum devact act)
+{
+ struct ufm_softc *sc = (struct ufm_softc *)self;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+
+USB_DETACH(ufm)
+{
+ USB_DETACH_START(ufm, sc);
+ struct ufm_endpoint *sce;
+ int i, dir;
+ int s;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ int maj, mn;
+
+ DPRINTF(("ufm_detach: sc=%p flags=%d\n", sc, flags));
+#elif defined(__FreeBSD__)
+ DPRINTF(("ufm_detach: sc=%p\n", sc));
+#endif
+
+ sc->sc_dying = 1;
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == ufmopen)
+ break;
+
+ /* Nuke the vnodes for any open instances (calls close). */
+ mn = self->dv_unit * USB_MAX_ENDPOINTS;
+ vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
+#elif defined(__FreeBSD__)
+ /* XXX not implemented yet */
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ return (0);
+}
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
+
+#if defined(__FreeBSD__)
+Static int
+ufm_detach(device_t self)
+{
+ DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
+ return 0;
+}
+
+DRIVER_MODULE(ufm, uhub, ufm_driver, ufm_devclass, usbd_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/uftdi.c b/sys/dev/usb/uftdi.c
new file mode 100644
index 0000000..7afb9f8
--- /dev/null
+++ b/sys/dev/usb/uftdi.c
@@ -0,0 +1,639 @@
+/* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */
+
+/*
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * FTDI FT8U100AX serial adapter driver
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+
+#include <sys/sysctl.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/usbdevs.h>
+
+#include <dev/usb/ucomvar.h>
+
+#include <dev/usb/uftdireg.h>
+
+#ifdef USB_DEBUG
+static int uftdidebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
+SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW,
+ &uftdidebug, 0, "uftdi debug level");
+#define DPRINTF(x) do { \
+ if (uftdidebug) \
+ logprintf x; \
+ } while (0)
+
+#define DPRINTFN(n, x) do { \
+ if (uftdidebug > (n)) \
+ logprintf x; \
+ } while (0)
+
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UFTDI_CONFIG_INDEX 0
+#define UFTDI_IFACE_INDEX 0
+
+
+/*
+ * These are the maximum number of bytes transferred per frame.
+ * The output buffer size cannot be increased due to the size encoding.
+ */
+#define UFTDIIBUFSIZE 64
+#define UFTDIOBUFSIZE 64
+
+struct uftdi_softc {
+ struct ucom_softc sc_ucom;
+
+ usbd_interface_handle sc_iface; /* interface */
+
+ enum uftdi_type sc_type;
+ u_int sc_hdrlen;
+
+ u_char sc_msr;
+ u_char sc_lsr;
+
+ u_int last_lcr;
+};
+
+Static void uftdi_get_status(void *, int portno, u_char *lsr, u_char *msr);
+Static void uftdi_set(void *, int, int, int);
+Static int uftdi_param(void *, int, struct termios *);
+Static int uftdi_open(void *sc, int portno);
+Static void uftdi_read(void *sc, int portno, u_char **ptr,u_int32_t *count);
+Static void uftdi_write(void *sc, int portno, u_char *to, u_char *from,
+ u_int32_t *count);
+Static void uftdi_break(void *sc, int portno, int onoff);
+
+struct ucom_callback uftdi_callback = {
+ uftdi_get_status,
+ uftdi_set,
+ uftdi_param,
+ NULL,
+ uftdi_open,
+ NULL,
+ uftdi_read,
+ uftdi_write,
+};
+
+USB_MATCH(uftdi)
+{
+ USB_MATCH_START(uftdi, uaa);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ DPRINTFN(20,("uftdi: vendor=0x%x, product=0x%x\n",
+ uaa->vendor, uaa->product));
+
+ if (uaa->vendor == USB_VENDOR_FTDI &&
+ (uaa->product == USB_PRODUCT_FTDI_SERIAL_8U100AX ||
+ uaa->product == USB_PRODUCT_FTDI_SERIAL_8U232AM ||
+ uaa->product == USB_PRODUCT_FTDI_SEMC_DSS20 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_631 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_632 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_633 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_634 ||
+ uaa->product == USB_PRODUCT_FTDI_USBSERIAL ||
+ uaa->product == USB_PRODUCT_FTDI_MX2_3 ||
+ uaa->product == USB_PRODUCT_FTDI_MX4_5 ||
+ uaa->product == USB_PRODUCT_FTDI_LK202 ||
+ uaa->product == USB_PRODUCT_FTDI_LK204))
+ return (UMATCH_VENDOR_PRODUCT);
+
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(uftdi)
+{
+ USB_ATTACH_START(uftdi, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ char *devinfo;
+ const char *devname;
+ int i;
+ usbd_status err;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ DPRINTFN(10,("\nuftdi_attach: sc=%p\n", sc));
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+
+ devname = USBDEVNAME(ucom->sc_dev);
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UFTDI_CONFIG_INDEX, 1);
+ if (err) {
+ printf("\n%s: failed to set configuration, err=%s\n",
+ devname, usbd_errstr(err));
+ goto bad;
+ }
+
+ err = usbd_device2interface_handle(dev, UFTDI_IFACE_INDEX, &iface);
+ if (err) {
+ printf("\n%s: failed to get interface, err=%s\n",
+ devname, usbd_errstr(err));
+ goto bad;
+ }
+
+ usbd_devinfo(dev, 0, devinfo);
+ /* USB_ATTACH_SETUP;*/
+ printf("%s: %s\n", devname, devinfo);
+
+ id = usbd_get_interface_descriptor(iface);
+ ucom->sc_iface = iface;
+ switch( uaa->product ){
+ case USB_PRODUCT_FTDI_SERIAL_8U100AX:
+ sc->sc_type = UFTDI_TYPE_SIO;
+ sc->sc_hdrlen = 1;
+ break;
+ case USB_PRODUCT_FTDI_SEMC_DSS20:
+ case USB_PRODUCT_FTDI_SERIAL_8U232AM:
+ case USB_PRODUCT_FTDI_CFA_631:
+ case USB_PRODUCT_FTDI_CFA_632:
+ case USB_PRODUCT_FTDI_CFA_633:
+ case USB_PRODUCT_FTDI_CFA_634:
+ case USB_PRODUCT_FTDI_USBSERIAL:
+ case USB_PRODUCT_FTDI_MX2_3:
+ case USB_PRODUCT_FTDI_MX4_5:
+ case USB_PRODUCT_FTDI_LK202:
+ case USB_PRODUCT_FTDI_LK204:
+ sc->sc_type = UFTDI_TYPE_8U232AM;
+ sc->sc_hdrlen = 0;
+ break;
+
+ default: /* Can't happen */
+ goto bad;
+ }
+
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ int addr, dir, attr;
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ printf("%s: could not read endpoint descriptor"
+ ": %s\n", devname, usbd_errstr(err));
+ goto bad;
+ }
+
+ addr = ed->bEndpointAddress;
+ dir = UE_GET_DIR(ed->bEndpointAddress);
+ attr = ed->bmAttributes & UE_XFERTYPE;
+ if (dir == UE_DIR_IN && attr == UE_BULK)
+ ucom->sc_bulkin_no = addr;
+ else if (dir == UE_DIR_OUT && attr == UE_BULK)
+ ucom->sc_bulkout_no = addr;
+ else {
+ printf("%s: unexpected endpoint\n", devname);
+ goto bad;
+ }
+ }
+ if (ucom->sc_bulkin_no == -1) {
+ printf("%s: Could not find data bulk in\n",
+ devname);
+ goto bad;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n",
+ devname);
+ goto bad;
+ }
+ ucom->sc_parent = sc;
+ ucom->sc_portno = FTDI_PIT_SIOA;
+ /* bulkin, bulkout set above */
+
+ ucom->sc_ibufsize = UFTDIIBUFSIZE;
+ ucom->sc_obufsize = UFTDIOBUFSIZE - sc->sc_hdrlen;
+ ucom->sc_ibufsizepad = UFTDIIBUFSIZE;
+ ucom->sc_opkthdrlen = sc->sc_hdrlen;
+
+
+ ucom->sc_callback = &uftdi_callback;
+#if 0
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, ucom->sc_udev,
+ USBDEV(ucom->sc_dev));
+#endif
+ DPRINTF(("uftdi: in=0x%x out=0x%x\n", ucom->sc_bulkin_no, ucom->sc_bulkout_no));
+ ucom_attach(&sc->sc_ucom);
+ free(devinfo, M_USBDEV);
+
+ USB_ATTACH_SUCCESS_RETURN;
+
+bad:
+ DPRINTF(("uftdi_attach: ATTACH ERROR\n"));
+ ucom->sc_dying = 1;
+ free(devinfo, M_USBDEV);
+
+ USB_ATTACH_ERROR_RETURN;
+}
+#if 0
+int
+uftdi_activate(device_ptr_t self, enum devact act)
+{
+ struct uftdi_softc *sc = (struct uftdi_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ if (sc->sc_subdev != NULL)
+ rv = config_deactivate(sc->sc_subdev);
+ sc->sc_ucom.sc_dying = 1;
+ break;
+ }
+ return (rv);
+}
+#endif
+#if 1
+USB_DETACH(uftdi)
+{
+ USB_DETACH_START(uftdi, sc);
+
+ int rv = 0;
+
+ DPRINTF(("uftdi_detach: sc=%p\n", sc));
+ sc->sc_ucom.sc_dying = 1;
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return rv;
+}
+#endif
+Static int
+uftdi_open(void *vsc, int portno)
+{
+ struct uftdi_softc *sc = vsc;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usb_device_request_t req;
+ usbd_status err;
+ struct termios t;
+
+ DPRINTF(("uftdi_open: sc=%p\n", sc));
+
+ if (ucom->sc_dying)
+ return (EIO);
+
+ /* Perform a full reset on the device */
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_RESET;
+ USETW(req.wValue, FTDI_SIO_RESET_SIO);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ /* Set 9600 baud, 2 stop bits, no parity, 8 bits */
+ t.c_ospeed = 9600;
+ t.c_cflag = CSTOPB | CS8;
+ (void)uftdi_param(sc, portno, &t);
+
+ /* Turn on RTS/CTS flow control */
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
+ USETW(req.wValue, 0);
+ USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ return (0);
+}
+
+Static void
+uftdi_read(void *vsc, int portno, u_char **ptr, u_int32_t *count)
+{
+ struct uftdi_softc *sc = vsc;
+ u_char msr, lsr;
+
+ DPRINTFN(15,("uftdi_read: sc=%p, port=%d count=%d\n", sc, portno,
+ *count));
+
+ msr = FTDI_GET_MSR(*ptr);
+ lsr = FTDI_GET_LSR(*ptr);
+
+#ifdef USB_DEBUG
+ if (*count != 2)
+ DPRINTFN(10,("uftdi_read: sc=%p, port=%d count=%d data[0]="
+ "0x%02x\n", sc, portno, *count, (*ptr)[2]));
+#endif
+
+ if (sc->sc_msr != msr ||
+ (sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK)) {
+ DPRINTF(("uftdi_read: status change msr=0x%02x(0x%02x) "
+ "lsr=0x%02x(0x%02x)\n", msr, sc->sc_msr,
+ lsr, sc->sc_lsr));
+ sc->sc_msr = msr;
+ sc->sc_lsr = lsr;
+ ucom_status_change(&sc->sc_ucom);
+ }
+
+ /* Pick up status and adjust data part. */
+ *ptr += 2;
+ *count -= 2;
+}
+
+Static void
+uftdi_write(void *vsc, int portno, u_char *to, u_char *from, u_int32_t *count)
+{
+ struct uftdi_softc *sc = vsc;
+
+ DPRINTFN(10,("uftdi_write: sc=%p, port=%d count=%u data[0]=0x%02x\n",
+ vsc, portno, *count, from[0]));
+
+ /* Make length tag and copy data */
+ if (sc->sc_hdrlen > 0)
+ *to = FTDI_OUT_TAG(*count, portno);
+
+ memcpy(to + sc->sc_hdrlen, from, *count);
+ *count += sc->sc_hdrlen;
+}
+
+Static void
+uftdi_set(void *vsc, int portno, int reg, int onoff)
+{
+ struct uftdi_softc *sc = vsc;
+ struct ucom_softc *ucom = vsc;
+ usb_device_request_t req;
+ int ctl;
+
+ DPRINTF(("uftdi_set: sc=%p, port=%d reg=%d onoff=%d\n", vsc, portno,
+ reg, onoff));
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ ctl = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
+ break;
+ case UCOM_SET_RTS:
+ ctl = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
+ break;
+ case UCOM_SET_BREAK:
+ uftdi_break(sc, portno, onoff);
+ return;
+ default:
+ return;
+ }
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_MODEM_CTRL;
+ USETW(req.wValue, ctl);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ DPRINTFN(2,("uftdi_set: reqtype=0x%02x req=0x%02x value=0x%04x "
+ "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest,
+ UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength)));
+ (void)usbd_do_request(ucom->sc_udev, &req, NULL);
+}
+
+Static int
+uftdi_param(void *vsc, int portno, struct termios *t)
+{
+ struct uftdi_softc *sc = vsc;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usb_device_request_t req;
+ usbd_status err;
+ int rate=0, data, flow;
+
+ DPRINTF(("uftdi_param: sc=%p\n", sc));
+
+ if (ucom->sc_dying)
+ return (EIO);
+
+ switch (sc->sc_type) {
+ case UFTDI_TYPE_SIO:
+ switch (t->c_ospeed) {
+ case 300: rate = ftdi_sio_b300; break;
+ case 600: rate = ftdi_sio_b600; break;
+ case 1200: rate = ftdi_sio_b1200; break;
+ case 2400: rate = ftdi_sio_b2400; break;
+ case 4800: rate = ftdi_sio_b4800; break;
+ case 9600: rate = ftdi_sio_b9600; break;
+ case 19200: rate = ftdi_sio_b19200; break;
+ case 38400: rate = ftdi_sio_b38400; break;
+ case 57600: rate = ftdi_sio_b57600; break;
+ case 115200: rate = ftdi_sio_b115200; break;
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case UFTDI_TYPE_8U232AM:
+ switch(t->c_ospeed) {
+ case 300: rate = ftdi_8u232am_b300; break;
+ case 600: rate = ftdi_8u232am_b600; break;
+ case 1200: rate = ftdi_8u232am_b1200; break;
+ case 2400: rate = ftdi_8u232am_b2400; break;
+ case 4800: rate = ftdi_8u232am_b4800; break;
+ case 9600: rate = ftdi_8u232am_b9600; break;
+ case 19200: rate = ftdi_8u232am_b19200; break;
+ case 38400: rate = ftdi_8u232am_b38400; break;
+ case 57600: rate = ftdi_8u232am_b57600; break;
+ case 115200: rate = ftdi_8u232am_b115200; break;
+ case 230400: rate = ftdi_8u232am_b230400; break;
+ case 460800: rate = ftdi_8u232am_b460800; break;
+ case 921600: rate = ftdi_8u232am_b921600; break;
+ default:
+ return (EINVAL);
+ }
+ break;
+ }
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_BAUD_RATE;
+ USETW(req.wValue, rate);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ DPRINTFN(2,("uftdi_param: reqtype=0x%02x req=0x%02x value=0x%04x "
+ "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest,
+ UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength)));
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CSTOPB))
+ data = FTDI_SIO_SET_DATA_STOP_BITS_2;
+ else
+ data = FTDI_SIO_SET_DATA_STOP_BITS_1;
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ data |= FTDI_SIO_SET_DATA_PARITY_ODD;
+ else
+ data |= FTDI_SIO_SET_DATA_PARITY_EVEN;
+ } else
+ data |= FTDI_SIO_SET_DATA_PARITY_NONE;
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ data |= FTDI_SIO_SET_DATA_BITS(5);
+ break;
+ case CS6:
+ data |= FTDI_SIO_SET_DATA_BITS(6);
+ break;
+ case CS7:
+ data |= FTDI_SIO_SET_DATA_BITS(7);
+ break;
+ case CS8:
+ data |= FTDI_SIO_SET_DATA_BITS(8);
+ break;
+ }
+ sc->last_lcr = data;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_DATA;
+ USETW(req.wValue, data);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ DPRINTFN(2,("uftdi_param: reqtype=0x%02x req=0x%02x value=0x%04x "
+ "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest,
+ UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength)));
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CRTSCTS)) {
+ flow = FTDI_SIO_RTS_CTS_HS;
+ USETW(req.wValue, 0);
+ } else if (ISSET(t->c_iflag, IXON|IXOFF)) {
+ flow = FTDI_SIO_XON_XOFF_HS;
+ USETW2(req.wValue, t->c_cc[VSTOP], t->c_cc[VSTART]);
+ } else {
+ flow = FTDI_SIO_DISABLE_FLOW_CTRL;
+ USETW(req.wValue, 0);
+ }
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
+ USETW2(req.wIndex, flow, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ return (0);
+}
+
+void
+uftdi_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
+{
+ struct uftdi_softc *sc = vsc;
+
+ DPRINTF(("uftdi_status: msr=0x%02x lsr=0x%02x\n",
+ sc->sc_msr, sc->sc_lsr));
+
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+}
+
+void
+uftdi_break(void *vsc, int portno, int onoff)
+{
+ struct uftdi_softc *sc = vsc;
+ struct ucom_softc *ucom = vsc;
+
+ usb_device_request_t req;
+ int data;
+
+ DPRINTF(("uftdi_break: sc=%p, port=%d onoff=%d\n", vsc, portno,
+ onoff));
+
+ if (onoff) {
+ data = sc->last_lcr | FTDI_SIO_SET_BREAK;
+ } else {
+ data = sc->last_lcr;
+ }
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_DATA;
+ USETW(req.wValue, data);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ (void)usbd_do_request(ucom->sc_udev, &req, NULL);
+}
+
+Static device_method_t uftdi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uftdi_match),
+ DEVMETHOD(device_attach, uftdi_attach),
+ DEVMETHOD(device_detach, uftdi_detach),
+
+ { 0, 0 }
+};
+
+Static driver_t uftdi_driver = {
+ "ucom",
+ uftdi_methods,
+ sizeof (struct uftdi_softc)
+};
+
+DRIVER_MODULE(uftdi, uhub, uftdi_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uftdi, usb, 1, 1, 1);
+MODULE_DEPEND(uftdi, ucom,UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
diff --git a/sys/dev/usb/uftdireg.h b/sys/dev/usb/uftdireg.h
new file mode 100644
index 0000000..15d47f9
--- /dev/null
+++ b/sys/dev/usb/uftdireg.h
@@ -0,0 +1,348 @@
+/* $NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Definitions for the FTDI USB Single Port Serial Converter -
+ * known as FTDI_SIO (Serial Input/Output application of the chipset)
+ *
+ * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side,
+ * USB on the other.
+ *
+ * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details
+ * of the protocol required to talk to the device and ongoing assistence
+ * during development.
+ *
+ * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc. is the original
+ * author of this file.
+ */
+/* Modified by Lennart Augustsson */
+
+/* Vendor Request Interface */
+#define FTDI_SIO_RESET 0 /* Reset the port */
+#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
+#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */
+#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */
+#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */
+#define FTDI_SIO_GET_STATUS 5 /* Retrieve current value of status reg */
+#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */
+#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
+
+/* Port Identifier Table */
+#define FTDI_PIT_DEFAULT 0 /* SIOA */
+#define FTDI_PIT_SIOA 1 /* SIOA */
+#define FTDI_PIT_SIOB 2 /* SIOB */
+#define FTDI_PIT_PARALLEL 3 /* Parallel */
+
+enum uftdi_type {
+ UFTDI_TYPE_SIO,
+ UFTDI_TYPE_8U232AM
+};
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_RESET
+ * wValue: Control Value
+ * 0 = Reset SIO
+ * 1 = Purge RX buffer
+ * 2 = Purge TX buffer
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * The Reset SIO command has this effect:
+ *
+ * Sets flow control set to 'none'
+ * Event char = 0x0d
+ * Event trigger = disabled
+ * Purge RX buffer
+ * Purge TX buffer
+ * Clear DTR
+ * Clear RTS
+ * baud and data format not reset
+ *
+ * The Purge RX and TX buffer commands affect nothing except the buffers
+ *
+ */
+/* FTDI_SIO_RESET */
+#define FTDI_SIO_RESET_SIO 0
+#define FTDI_SIO_RESET_PURGE_RX 1
+#define FTDI_SIO_RESET_PURGE_TX 2
+
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_BAUDRATE
+ * wValue: BaudRate value - see below
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ */
+/* FTDI_SIO_SET_BAUDRATE */
+enum {
+ ftdi_sio_b300 = 0,
+ ftdi_sio_b600 = 1,
+ ftdi_sio_b1200 = 2,
+ ftdi_sio_b2400 = 3,
+ ftdi_sio_b4800 = 4,
+ ftdi_sio_b9600 = 5,
+ ftdi_sio_b19200 = 6,
+ ftdi_sio_b38400 = 7,
+ ftdi_sio_b57600 = 8,
+ ftdi_sio_b115200 = 9
+};
+
+enum {
+ ftdi_8u232am_b300 = 0x2710,
+ ftdi_8u232am_b600 = 0x1388,
+ ftdi_8u232am_b1200 = 0x09c4,
+ ftdi_8u232am_b2400 = 0x04e2,
+ ftdi_8u232am_b4800 = 0x0271,
+ ftdi_8u232am_b9600 = 0x4138,
+ ftdi_8u232am_b19200 = 0x809c,
+ ftdi_8u232am_b38400 = 0xc04e,
+ ftdi_8u232am_b57600 = 0x0034,
+ ftdi_8u232am_b115200 = 0x001a,
+ ftdi_8u232am_b230400 = 0x000d,
+ ftdi_8u232am_b460800 = 0x4006,
+ ftdi_8u232am_b921600 = 0x8003
+};
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_DATA
+ * wValue: Data characteristics (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: No
+ *
+ * Data characteristics
+ *
+ * B0..7 Number of data bits
+ * B8..10 Parity
+ * 0 = None
+ * 1 = Odd
+ * 2 = Even
+ * 3 = Mark
+ * 4 = Space
+ * B11..13 Stop Bits
+ * 0 = 1
+ * 1 = 1.5
+ * 2 = 2
+ * B14..15 Reserved
+ *
+ */
+/* FTDI_SIO_SET_DATA */
+#define FTDI_SIO_SET_DATA_BITS(n) (n)
+#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
+#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
+#define FTDI_SIO_SET_BREAK (0x1 << 14)
+
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_MODEM_CTRL
+ * wValue: ControlValue (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
+ * command will be IGNORED without an error being returned
+ * Also - you can not set DTR and RTS with one control message
+ *
+ * ControlValue
+ * B0 DTR state
+ * 0 = reset
+ * 1 = set
+ * B1 RTS state
+ * 0 = reset
+ * 1 = set
+ * B2..7 Reserved
+ * B8 DTR state enable
+ * 0 = ignore
+ * 1 = use DTR state
+ * B9 RTS state enable
+ * 0 = ignore
+ * 1 = use RTS state
+ * B10..15 Reserved
+ */
+/* FTDI_SIO_MODEM_CTRL */
+#define FTDI_SIO_SET_DTR_MASK 0x1
+#define FTDI_SIO_SET_DTR_HIGH (1 | ( FTDI_SIO_SET_DTR_MASK << 8))
+#define FTDI_SIO_SET_DTR_LOW (0 | ( FTDI_SIO_SET_DTR_MASK << 8))
+#define FTDI_SIO_SET_RTS_MASK 0x2
+#define FTDI_SIO_SET_RTS_HIGH (2 | ( FTDI_SIO_SET_RTS_MASK << 8))
+#define FTDI_SIO_SET_RTS_LOW (0 | ( FTDI_SIO_SET_RTS_MASK << 8))
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_FLOW_CTRL
+ * wValue: Xoff/Xon
+ * wIndex: Protocol/Port - hIndex is protocl / lIndex is port
+ * wLength: 0
+ * Data: None
+ *
+ * hIndex protocol is:
+ * B0 Output handshaking using RTS/CTS
+ * 0 = disabled
+ * 1 = enabled
+ * B1 Output handshaking using DTR/DSR
+ * 0 = disabled
+ * 1 = enabled
+ * B2 Xon/Xoff handshaking
+ * 0 = disabled
+ * 1 = enabled
+ *
+ * A value of zero in the hIndex field disables handshaking
+ *
+ * If Xon/Xoff handshaking is specified, the hValue field should contain the
+ * XOFF character and the lValue field contains the XON character.
+ */
+/* FTDI_SIO_SET_FLOW_CTRL */
+#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
+#define FTDI_SIO_RTS_CTS_HS 0x1
+#define FTDI_SIO_DTR_DSR_HS 0x2
+#define FTDI_SIO_XON_XOFF_HS 0x4
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_EVENT_CHAR
+ * wValue: Event Char
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * wValue:
+ * B0..7 Event Character
+ * B8 Event Character Processing
+ * 0 = disabled
+ * 1 = enabled
+ * B9..15 Reserved
+ *
+ * FTDI_SIO_SET_EVENT_CHAR
+ *
+ * Set the special event character for the specified communications port.
+ * If the device sees this character it will immediately return the
+ * data read so far - rather than wait 40ms or until 62 bytes are read
+ * which is what normally happens.
+ */
+
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_ERROR_CHAR
+ * wValue: Error Char
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * Error Char
+ * B0..7 Error Character
+ * B8 Error Character Processing
+ * 0 = disabled
+ * 1 = enabled
+ * B9..15 Reserved
+ *
+ *
+ * FTDI_SIO_SET_ERROR_CHAR
+ * Set the parity error replacement character for the specified communications
+ * port.
+ */
+
+
+/*
+ * BmRequestType: 1100 0000b
+ * bRequest: FTDI_SIO_GET_MODEM_STATUS
+ * wValue: zero
+ * wIndex: Port
+ * wLength: 1
+ * Data: Status
+ *
+ * One byte of data is returned
+ * B0..3 0
+ * B4 CTS
+ * 0 = inactive
+ * 1 = active
+ * B5 DSR
+ * 0 = inactive
+ * 1 = active
+ * B6 Ring Indicator (RI)
+ * 0 = inactive
+ * 1 = active
+ * B7 Receive Line Signal Detect (RLSD)
+ * 0 = inactive
+ * 1 = active
+ *
+ * FTDI_SIO_GET_MODEM_STATUS
+ * Retrieve the current value of the modem status register.
+ */
+#define FTDI_SIO_CTS_MASK 0x10
+#define FTDI_SIO_DSR_MASK 0x20
+#define FTDI_SIO_RI_MASK 0x40
+#define FTDI_SIO_RLSD_MASK 0x80
+
+
+
+/*
+ *
+ * DATA FORMAT
+ *
+ * IN Endpoint
+ *
+ * The device reserves the first two bytes of data on this endpoint to contain
+ * the current values of the modem and line status registers. In the absence of
+ * data, the device generates a message consisting of these two status bytes
+ * every 40 ms.
+ *
+ * Byte 0: Modem Status
+ * NOTE: 4 upper bits have same layout as the MSR register in a 16550
+ *
+ * Offset Description
+ * B0..3 Port
+ * B4 Clear to Send (CTS)
+ * B5 Data Set Ready (DSR)
+ * B6 Ring Indicator (RI)
+ * B7 Receive Line Signal Detect (RLSD)
+ *
+ * Byte 1: Line Status
+ * NOTE: same layout as the LSR register in a 16550
+ *
+ * Offset Description
+ * B0 Data Ready (DR)
+ * B1 Overrun Error (OE)
+ * B2 Parity Error (PE)
+ * B3 Framing Error (FE)
+ * B4 Break Interrupt (BI)
+ * B5 Transmitter Holding Register (THRE)
+ * B6 Transmitter Empty (TEMT)
+ * B7 Error in RCVR FIFO
+ *
+ *
+ * OUT Endpoint
+ *
+ * This device reserves the first bytes of data on this endpoint contain the
+ * length and port identifier of the message. For the FTDI USB Serial converter
+ * the port identifier is always 1.
+ *
+ * Byte 0: Port & length
+ *
+ * Offset Description
+ * B0..1 Port
+ * B2..7 Length of message - (not including Byte 0)
+ *
+ */
+#define FTDI_PORT_MASK 0x0f
+#define FTDI_MSR_MASK 0xf0
+#define FTDI_GET_MSR(p) (((p)[0]) & FTDI_MSR_MASK)
+#define FTDI_GET_LSR(p) ((p)[1])
+#define FTDI_LSR_MASK (~0x60) /* interesting bits */
+#define FTDI_OUT_TAG(len, port) (((len) << 2) | (port))
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c
new file mode 100644
index 0000000..81ae5f2
--- /dev/null
+++ b/sys/dev/usb/ugen.c
@@ -0,0 +1,1471 @@
+/* $NetBSD: ugen.c,v 1.59 2002/07/11 21:14:28 augustss Exp $ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: ugen.c,v 1.61 2002/09/23 05:51:20 simonb Exp $
+ * $NetBSD: ugen.c,v 1.64 2003/06/28 14:21:46 darrenr Exp $
+ * $NetBSD: ugen.c,v 1.65 2003/06/29 22:30:56 fvdl Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#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>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#endif
+#include <sys/tty.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ugendebug) logprintf x
+#define DPRINTFN(n,x) if (ugendebug>(n)) logprintf x
+int ugendebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB ugen");
+SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW,
+ &ugendebug, 0, "ugen debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UGEN_CHUNK 128 /* chunk size for read */
+#define UGEN_IBSIZE 1020 /* buffer size */
+#define UGEN_BBSIZE 1024
+
+#define UGEN_NISOFRAMES 500 /* 0.5 seconds worth */
+#define UGEN_NISOREQS 6 /* number of outstanding xfer requests */
+#define UGEN_NISORFRMS 4 /* number of frames (miliseconds) per req */
+
+struct ugen_endpoint {
+ struct ugen_softc *sc;
+#if defined(__FreeBSD__)
+ struct cdev *dev;
+#endif
+ usb_endpoint_descriptor_t *edesc;
+ usbd_interface_handle iface;
+ int state;
+#define UGEN_ASLP 0x02 /* waiting for data */
+#define UGEN_SHORT_OK 0x04 /* short xfers are OK */
+ usbd_pipe_handle pipeh;
+ struct clist q;
+ struct selinfo rsel;
+ u_char *ibuf; /* start of buffer (circular for isoc) */
+ u_char *fill; /* location for input (isoc) */
+ u_char *limit; /* end of circular buffer (isoc) */
+ u_char *cur; /* current read location (isoc) */
+ u_int32_t timeout;
+ struct isoreq {
+ struct ugen_endpoint *sce;
+ usbd_xfer_handle xfer;
+ void *dmabuf;
+ u_int16_t sizes[UGEN_NISORFRMS];
+ } isoreqs[UGEN_NISOREQS];
+};
+
+struct ugen_softc {
+ USBBASEDEVICE sc_dev; /* base device */
+ usbd_device_handle sc_udev;
+#if defined(__FreeBSD__)
+ struct cdev *dev;
+#endif
+
+ char sc_is_open[USB_MAX_ENDPOINTS];
+ struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2];
+#define OUT 0
+#define IN 1
+
+ int sc_refcnt;
+ u_char sc_dying;
+};
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+cdev_decl(ugen);
+#elif defined(__FreeBSD__)
+d_open_t ugenopen;
+d_close_t ugenclose;
+d_read_t ugenread;
+d_write_t ugenwrite;
+d_ioctl_t ugenioctl;
+d_poll_t ugenpoll;
+
+
+Static struct cdevsw ugen_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ugenopen,
+ .d_close = ugenclose,
+ .d_read = ugenread,
+ .d_write = ugenwrite,
+ .d_ioctl = ugenioctl,
+ .d_poll = ugenpoll,
+ .d_name = "ugen",
+#if __FreeBSD_version < 500014
+ .d_bmaj -1
+#endif
+};
+#endif
+
+Static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr,
+ usbd_status status);
+Static void ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr,
+ usbd_status status);
+Static int ugen_do_read(struct ugen_softc *, int, struct uio *, int);
+Static int ugen_do_write(struct ugen_softc *, int, struct uio *, int);
+Static int ugen_do_ioctl(struct ugen_softc *, int, u_long,
+ caddr_t, int, usb_proc_ptr);
+#if defined(__FreeBSD__)
+Static void ugen_make_devnodes(struct ugen_softc *sc);
+Static void ugen_destroy_devnodes(struct ugen_softc *sc);
+#endif
+Static int ugen_set_config(struct ugen_softc *sc, int configno);
+Static usb_config_descriptor_t *ugen_get_cdesc(struct ugen_softc *sc,
+ int index, int *lenp);
+Static usbd_status ugen_set_interface(struct ugen_softc *, int, int);
+Static int ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx);
+
+#define UGENUNIT(n) ((minor(n) >> 4) & 0xf)
+#define UGENENDPOINT(n) (minor(n) & 0xf)
+#define UGENMINOR(u, e) (((u) << 4) | (e))
+
+USB_DECLARE_DRIVER(ugen);
+
+USB_MATCH(ugen)
+{
+ USB_MATCH_START(ugen, uaa);
+
+#if 0
+ if (uaa->matchlvl)
+ return (uaa->matchlvl);
+#endif
+ if (uaa->usegeneric)
+ return (UMATCH_GENERIC);
+ else
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(ugen)
+{
+ USB_ATTACH_START(ugen, sc, uaa);
+ usbd_device_handle udev;
+ char devinfo[1024];
+ usbd_status err;
+ int conf;
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ sc->sc_udev = udev = uaa->device;
+
+ memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
+
+ /* First set configuration index 0, the default one for ugen. */
+ err = usbd_set_config_index(udev, 0, 0);
+ if (err) {
+ printf("%s: setting configuration index 0 failed\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+ conf = usbd_get_config_descriptor(udev)->bConfigurationValue;
+
+ /* Set up all the local state for this configuration. */
+ err = ugen_set_config(sc, conf);
+ if (err) {
+ printf("%s: setting configuration %d failed\n",
+ USBDEVNAME(sc->sc_dev), conf);
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+#if defined(__FreeBSD__)
+ /* the main device, ctrl endpoint */
+ sc->dev = make_dev(&ugen_cdevsw, UGENMINOR(USBDEVUNIT(sc->sc_dev), 0),
+ UID_ROOT, GID_OPERATOR, 0644, "%s", USBDEVNAME(sc->sc_dev));
+#endif
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+#if defined(__FreeBSD__)
+Static void
+ugen_make_devnodes(struct ugen_softc *sc)
+{
+ int endptno;
+ struct cdev *dev;
+
+ for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) {
+ if (sc->sc_endpoints[endptno][IN].sc != NULL ||
+ sc->sc_endpoints[endptno][OUT].sc != NULL ) {
+ /* endpt can be 0x81 and 0x01, representing
+ * endpoint address 0x01 and IN/OUT directions.
+ * We map both endpts to the same device,
+ * IN is reading from it, OUT is writing to it.
+ *
+ * In the if clause above we check whether one
+ * of the structs is populated.
+ */
+ dev = make_dev(&ugen_cdevsw,
+ UGENMINOR(USBDEVUNIT(sc->sc_dev), endptno),
+ UID_ROOT, GID_OPERATOR, 0644,
+ "%s.%d",
+ USBDEVNAME(sc->sc_dev), endptno);
+ if (sc->sc_endpoints[endptno][IN].sc != NULL)
+ sc->sc_endpoints[endptno][IN].dev = dev;
+ if (sc->sc_endpoints[endptno][OUT].sc != NULL)
+ sc->sc_endpoints[endptno][OUT].dev = dev;
+ }
+ }
+}
+
+Static void
+ugen_destroy_devnodes(struct ugen_softc *sc)
+{
+ int endptno;
+ struct cdev *dev;
+
+ /* destroy all devices for the other (existing) endpoints as well */
+ for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) {
+ if (sc->sc_endpoints[endptno][IN].sc != NULL ||
+ sc->sc_endpoints[endptno][OUT].sc != NULL ) {
+ /* endpt can be 0x81 and 0x01, representing
+ * endpoint address 0x01 and IN/OUT directions.
+ * We map both endpoint addresses to the same device,
+ * IN is reading from it, OUT is writing to it.
+ *
+ * In the if clause above we check whether one
+ * of the structs is populated.
+ */
+ if (sc->sc_endpoints[endptno][IN].sc != NULL)
+ dev = sc->sc_endpoints[endptno][IN].dev;
+ else
+ dev = sc->sc_endpoints[endptno][OUT].dev;
+ destroy_dev(dev);
+ }
+ }
+}
+#endif
+
+Static int
+ugen_set_config(struct ugen_softc *sc, int configno)
+{
+ usbd_device_handle dev = sc->sc_udev;
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+ struct ugen_endpoint *sce;
+ u_int8_t niface, nendpt;
+ int ifaceno, endptno, endpt;
+ usbd_status err;
+ int dir;
+
+ DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",
+ USBDEVNAME(sc->sc_dev), configno, sc));
+
+#if defined(__FreeBSD__)
+ ugen_destroy_devnodes(sc);
+#endif
+
+ /* We start at 1, not 0, because we don't care whether the
+ * control endpoint is open or not. It is always present.
+ */
+ for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++)
+ if (sc->sc_is_open[endptno]) {
+ DPRINTFN(1,
+ ("ugen_set_config: %s - endpoint %d is open\n",
+ USBDEVNAME(sc->sc_dev), endptno));
+ return (USBD_IN_USE);
+ }
+
+ /* Avoid setting the current value. */
+ if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) {
+ err = usbd_set_config_no(dev, configno, 1);
+ if (err)
+ return (err);
+ }
+
+ err = usbd_interface_count(dev, &niface);
+ if (err)
+ return (err);
+ memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
+ for (ifaceno = 0; ifaceno < niface; ifaceno++) {
+ DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
+ err = usbd_device2interface_handle(dev, ifaceno, &iface);
+ if (err)
+ return (err);
+ err = usbd_endpoint_count(iface, &nendpt);
+ if (err)
+ return (err);
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x"
+ "(%d,%d), sce=%p\n",
+ endptno, endpt, UE_GET_ADDR(endpt),
+ UE_GET_DIR(endpt), sce));
+ sce->sc = sc;
+ sce->edesc = ed;
+ sce->iface = iface;
+ }
+ }
+
+#if defined(__FreeBSD__)
+ ugen_make_devnodes(sc);
+#endif
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+int
+ugenopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct ugen_softc *sc;
+ int unit = UGENUNIT(dev);
+ int endpt = UGENENDPOINT(dev);
+ usb_endpoint_descriptor_t *edesc;
+ struct ugen_endpoint *sce;
+ int dir, isize;
+ usbd_status err;
+ usbd_xfer_handle xfer;
+ void *buf;
+ int i, j;
+
+ USB_GET_SC_OPEN(ugen, unit, sc);
+
+ DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n",
+ flag, mode, unit, endpt));
+
+ if (sc == NULL || sc->sc_dying)
+ return (ENXIO);
+
+ if (sc->sc_is_open[endpt])
+ return (EBUSY);
+
+ if (endpt == USB_CONTROL_ENDPOINT) {
+ sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
+ return (0);
+ }
+
+ /* Make sure there are pipes for all directions. */
+ for (dir = OUT; dir <= IN; dir++) {
+ if (flag & (dir == OUT ? FWRITE : FREAD)) {
+ sce = &sc->sc_endpoints[endpt][dir];
+ if (sce == 0 || sce->edesc == 0)
+ return (ENXIO);
+ }
+ }
+
+ /* Actually open the pipes. */
+ /* XXX Should back out properly if it fails. */
+ for (dir = OUT; dir <= IN; dir++) {
+ if (!(flag & (dir == OUT ? FWRITE : FREAD)))
+ continue;
+ sce = &sc->sc_endpoints[endpt][dir];
+ sce->state = 0;
+ sce->timeout = USBD_NO_TIMEOUT;
+ DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n",
+ sc, endpt, dir, sce));
+ edesc = sce->edesc;
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ if (dir == OUT) {
+ err = usbd_open_pipe(sce->iface,
+ edesc->bEndpointAddress, 0, &sce->pipeh);
+ if (err)
+ return (EIO);
+ break;
+ }
+ isize = UGETW(edesc->wMaxPacketSize);
+ if (isize == 0) /* shouldn't happen */
+ return (EINVAL);
+ sce->ibuf = malloc(isize, M_USBDEV, M_WAITOK);
+ DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n",
+ endpt, isize));
+ if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1)
+ return (ENOMEM);
+ err = usbd_open_pipe_intr(sce->iface,
+ edesc->bEndpointAddress,
+ USBD_SHORT_XFER_OK, &sce->pipeh, sce,
+ sce->ibuf, isize, ugenintr,
+ USBD_DEFAULT_INTERVAL);
+ if (err) {
+ free(sce->ibuf, M_USBDEV);
+ clfree(&sce->q);
+ return (EIO);
+ }
+ DPRINTFN(5, ("ugenopen: interrupt open done\n"));
+ break;
+ case UE_BULK:
+ err = usbd_open_pipe(sce->iface,
+ edesc->bEndpointAddress, 0, &sce->pipeh);
+ if (err)
+ return (EIO);
+ break;
+ case UE_ISOCHRONOUS:
+ if (dir == OUT)
+ return (EINVAL);
+ isize = UGETW(edesc->wMaxPacketSize);
+ if (isize == 0) /* shouldn't happen */
+ return (EINVAL);
+ sce->ibuf = malloc(isize * UGEN_NISOFRAMES,
+ M_USBDEV, M_WAITOK);
+ sce->cur = sce->fill = sce->ibuf;
+ sce->limit = sce->ibuf + isize * UGEN_NISOFRAMES;
+ DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n",
+ endpt, isize));
+ err = usbd_open_pipe(sce->iface,
+ edesc->bEndpointAddress, 0, &sce->pipeh);
+ if (err) {
+ free(sce->ibuf, M_USBDEV);
+ return (EIO);
+ }
+ for(i = 0; i < UGEN_NISOREQS; ++i) {
+ sce->isoreqs[i].sce = sce;
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ goto bad;
+ sce->isoreqs[i].xfer = xfer;
+ buf = usbd_alloc_buffer
+ (xfer, isize * UGEN_NISORFRMS);
+ if (buf == 0) {
+ i++;
+ goto bad;
+ }
+ sce->isoreqs[i].dmabuf = buf;
+ for(j = 0; j < UGEN_NISORFRMS; ++j)
+ sce->isoreqs[i].sizes[j] = isize;
+ usbd_setup_isoc_xfer
+ (xfer, sce->pipeh, &sce->isoreqs[i],
+ sce->isoreqs[i].sizes,
+ UGEN_NISORFRMS, USBD_NO_COPY,
+ ugen_isoc_rintr);
+ (void)usbd_transfer(xfer);
+ }
+ DPRINTFN(5, ("ugenopen: isoc open done\n"));
+ break;
+ bad:
+ while (--i >= 0) /* implicit buffer free */
+ usbd_free_xfer(sce->isoreqs[i].xfer);
+ return (ENOMEM);
+ case UE_CONTROL:
+ sce->timeout = USBD_DEFAULT_TIMEOUT;
+ return (EINVAL);
+ }
+ }
+ sc->sc_is_open[endpt] = 1;
+ return (0);
+}
+
+int
+ugenclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+ struct ugen_endpoint *sce;
+ int dir;
+ int i;
+
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+
+ DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n",
+ flag, mode, UGENUNIT(dev), endpt));
+
+#ifdef DIAGNOSTIC
+ if (!sc->sc_is_open[endpt]) {
+ printf("ugenclose: not open\n");
+ return (EINVAL);
+ }
+#endif
+
+ if (endpt == USB_CONTROL_ENDPOINT) {
+ DPRINTFN(5, ("ugenclose: close control\n"));
+ sc->sc_is_open[endpt] = 0;
+ return (0);
+ }
+
+ for (dir = OUT; dir <= IN; dir++) {
+ if (!(flag & (dir == OUT ? FWRITE : FREAD)))
+ continue;
+ sce = &sc->sc_endpoints[endpt][dir];
+ if (sce == NULL || sce->pipeh == NULL)
+ continue;
+ DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n",
+ endpt, dir, sce));
+
+ usbd_abort_pipe(sce->pipeh);
+ usbd_close_pipe(sce->pipeh);
+ sce->pipeh = NULL;
+
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ ndflush(&sce->q, sce->q.c_cc);
+ clfree(&sce->q);
+ break;
+ case UE_ISOCHRONOUS:
+ for (i = 0; i < UGEN_NISOREQS; ++i)
+ usbd_free_xfer(sce->isoreqs[i].xfer);
+ default:
+ break;
+ }
+
+ if (sce->ibuf != NULL) {
+ free(sce->ibuf, M_USBDEV);
+ sce->ibuf = NULL;
+ clfree(&sce->q);
+ }
+ }
+ sc->sc_is_open[endpt] = 0;
+
+ return (0);
+}
+
+Static int
+ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
+{
+ struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][IN];
+ u_int32_t n, tn;
+ char buf[UGEN_BBSIZE];
+ usbd_xfer_handle xfer;
+ usbd_status err;
+ int s;
+ int error = 0;
+ u_char buffer[UGEN_CHUNK];
+
+ DPRINTFN(5, ("%s: ugenread: %d\n", USBDEVNAME(sc->sc_dev), endpt));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ if (endpt == USB_CONTROL_ENDPOINT)
+ return (ENODEV);
+
+ if (sce == NULL)
+ return (EINVAL);
+
+ if (sce->edesc == NULL) {
+ printf("ugenread: no edesc\n");
+ return (EIO);
+ }
+ if (sce->pipeh == NULL) {
+ printf("ugenread: no pipe\n");
+ return (EIO);
+ }
+
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ /* Block until activity occurred. */
+ s = splusb();
+ while (sce->q.c_cc == 0) {
+ if (flag & IO_NDELAY) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ sce->state |= UGEN_ASLP;
+ DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
+ error = tsleep(sce, PZERO | PCATCH, "ugenri", 0);
+ DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
+ if (sc->sc_dying)
+ error = EIO;
+ if (error) {
+ sce->state &= ~UGEN_ASLP;
+ break;
+ }
+ }
+ splx(s);
+
+ /* Transfer as many chunks as possible. */
+ while (sce->q.c_cc > 0 && uio->uio_resid > 0 && !error) {
+ n = min(sce->q.c_cc, uio->uio_resid);
+ if (n > sizeof(buffer))
+ n = sizeof(buffer);
+
+ /* Remove a small chunk from the input queue. */
+ q_to_b(&sce->q, buffer, n);
+ DPRINTFN(5, ("ugenread: got %d chars\n", n));
+
+ /* Copy the data to the user process. */
+ error = uiomove(buffer, n, uio);
+ if (error)
+ break;
+ }
+ break;
+ case UE_BULK:
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ return (ENOMEM);
+ while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0) {
+ DPRINTFN(1, ("ugenread: start transfer %d bytes\n",n));
+ tn = n;
+ err = usbd_bulk_transfer(
+ xfer, sce->pipeh,
+ sce->state & UGEN_SHORT_OK ?
+ USBD_SHORT_XFER_OK : 0,
+ sce->timeout, buf, &tn, "ugenrb");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else if (err == USBD_TIMEOUT)
+ error = ETIMEDOUT;
+ else
+ error = EIO;
+ break;
+ }
+ DPRINTFN(1, ("ugenread: got %d bytes\n", tn));
+ error = uiomove(buf, tn, uio);
+ if (error || tn < n)
+ break;
+ }
+ usbd_free_xfer(xfer);
+ break;
+ case UE_ISOCHRONOUS:
+ s = splusb();
+ while (sce->cur == sce->fill) {
+ if (flag & IO_NDELAY) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ sce->state |= UGEN_ASLP;
+ DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
+ error = tsleep(sce, PZERO | PCATCH, "ugenri", 0);
+ DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
+ if (sc->sc_dying)
+ error = EIO;
+ if (error) {
+ sce->state &= ~UGEN_ASLP;
+ break;
+ }
+ }
+
+ while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) {
+ if(sce->fill > sce->cur)
+ n = min(sce->fill - sce->cur, uio->uio_resid);
+ else
+ n = min(sce->limit - sce->cur, uio->uio_resid);
+
+ DPRINTFN(5, ("ugenread: isoc got %d chars\n", n));
+
+ /* Copy the data to the user process. */
+ error = uiomove(sce->cur, n, uio);
+ if (error)
+ break;
+ sce->cur += n;
+ if(sce->cur >= sce->limit)
+ sce->cur = sce->ibuf;
+ }
+ splx(s);
+ break;
+
+
+ default:
+ return (ENXIO);
+ }
+ return (error);
+}
+
+int
+ugenread(struct cdev *dev, struct uio *uio, int flag)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+ int error;
+
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+
+ sc->sc_refcnt++;
+ error = ugen_do_read(sc, endpt, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+Static int
+ugen_do_write(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
+{
+ struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][OUT];
+ u_int32_t n;
+ int error = 0;
+ char buf[UGEN_BBSIZE];
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ DPRINTFN(5, ("%s: ugenwrite: %d\n", USBDEVNAME(sc->sc_dev), endpt));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ if (endpt == USB_CONTROL_ENDPOINT)
+ return (ENODEV);
+
+ if (sce == NULL)
+ return (EINVAL);
+
+ if (sce->edesc == NULL) {
+ printf("ugenwrite: no edesc\n");
+ return (EIO);
+ }
+ if (sce->pipeh == NULL) {
+ printf("ugenwrite: no pipe\n");
+ return (EIO);
+ }
+
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_BULK:
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ return (EIO);
+ while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0) {
+ error = uiomove(buf, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n));
+ err = usbd_bulk_transfer(xfer, sce->pipeh, 0,
+ sce->timeout, buf, &n,"ugenwb");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else if (err == USBD_TIMEOUT)
+ error = ETIMEDOUT;
+ else
+ error = EIO;
+ break;
+ }
+ }
+ usbd_free_xfer(xfer);
+ break;
+ case UE_INTERRUPT:
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ return (EIO);
+ while ((n = min(UGETW(sce->edesc->wMaxPacketSize),
+ uio->uio_resid)) != 0) {
+ error = uiomove(buf, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n));
+ err = usbd_intr_transfer(xfer, sce->pipeh, 0,
+ sce->timeout, buf, &n,"ugenwi");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else if (err == USBD_TIMEOUT)
+ error = ETIMEDOUT;
+ else
+ error = EIO;
+ break;
+ }
+ }
+ usbd_free_xfer(xfer);
+ break;
+ default:
+ return (ENXIO);
+ }
+ return (error);
+}
+
+int
+ugenwrite(struct cdev *dev, struct uio *uio, int flag)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+ int error;
+
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+
+ sc->sc_refcnt++;
+ error = ugen_do_write(sc, endpt, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+ugen_activate(device_ptr_t self, enum devact act)
+{
+ struct ugen_softc *sc = (struct ugen_softc *)self;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+#endif
+
+USB_DETACH(ugen)
+{
+ USB_DETACH_START(ugen, sc);
+ struct ugen_endpoint *sce;
+ int i, dir;
+ int s;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ int maj, mn;
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ DPRINTF(("ugen_detach: sc=%p flags=%d\n", sc, flags));
+#elif defined(__FreeBSD__)
+ DPRINTF(("ugen_detach: sc=%p\n", sc));
+#endif
+
+ sc->sc_dying = 1;
+ /* Abort all pipes. Causes processes waiting for transfer to wake. */
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+ for (dir = OUT; dir <= IN; dir++) {
+ sce = &sc->sc_endpoints[i][dir];
+ if (sce && sce->pipeh)
+ usbd_abort_pipe(sce->pipeh);
+ }
+ }
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wake everyone */
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ wakeup(&sc->sc_endpoints[i][IN]);
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == ugenopen)
+ break;
+
+ /* Nuke the vnodes for any open instances (calls close). */
+ mn = self->dv_unit * USB_MAX_ENDPOINTS;
+ vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
+#elif defined(__FreeBSD__)
+ /* destroy the device for the control endpoint */
+ destroy_dev(sc->dev);
+ ugen_destroy_devnodes(sc);
+#endif
+
+ return (0);
+}
+
+Static void
+ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ struct ugen_endpoint *sce = addr;
+ /*struct ugen_softc *sc = sce->sc;*/
+ u_int32_t count;
+ u_char *ibuf;
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ugenintr: status=%d\n", status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sce->pipeh);
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ ibuf = sce->ibuf;
+
+ DPRINTFN(5, ("ugenintr: xfer=%p status=%d count=%d\n",
+ xfer, status, count));
+ DPRINTFN(5, (" data = %02x %02x %02x\n",
+ ibuf[0], ibuf[1], ibuf[2]));
+
+ (void)b_to_q(ibuf, count, &sce->q);
+
+ if (sce->state & UGEN_ASLP) {
+ sce->state &= ~UGEN_ASLP;
+ DPRINTFN(5, ("ugen_intr: waking %p\n", sce));
+ wakeup(sce);
+ }
+ selwakeuppri(&sce->rsel, PZERO);
+}
+
+Static void
+ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr,
+ usbd_status status)
+{
+ struct isoreq *req = addr;
+ struct ugen_endpoint *sce = req->sce;
+ u_int32_t count, n;
+ int i, isize;
+
+ /* Return if we are aborting. */
+ if (status == USBD_CANCELLED)
+ return;
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ DPRINTFN(5,("ugen_isoc_rintr: xfer %d, count=%d\n",
+ (int)(req - sce->isoreqs),
+ count));
+
+ /* throw away oldest input if the buffer is full */
+ if(sce->fill < sce->cur && sce->cur <= sce->fill + count) {
+ sce->cur += count;
+ if(sce->cur >= sce->limit)
+ sce->cur = sce->ibuf + (sce->limit - sce->cur);
+ DPRINTFN(5, ("ugen_isoc_rintr: throwing away %d bytes\n",
+ count));
+ }
+
+ isize = UGETW(sce->edesc->wMaxPacketSize);
+ for (i = 0; i < UGEN_NISORFRMS; i++) {
+ u_int32_t actlen = req->sizes[i];
+ char const *buf = (char const *)req->dmabuf + isize * i;
+
+ /* copy data to buffer */
+ while (actlen > 0) {
+ n = min(actlen, sce->limit - sce->fill);
+ memcpy(sce->fill, buf, n);
+
+ buf += n;
+ actlen -= n;
+ sce->fill += n;
+ if(sce->fill == sce->limit)
+ sce->fill = sce->ibuf;
+ }
+
+ /* setup size for next transfer */
+ req->sizes[i] = isize;
+ }
+
+ usbd_setup_isoc_xfer(xfer, sce->pipeh, req, req->sizes, UGEN_NISORFRMS,
+ USBD_NO_COPY, ugen_isoc_rintr);
+ (void)usbd_transfer(xfer);
+
+ if (sce->state & UGEN_ASLP) {
+ sce->state &= ~UGEN_ASLP;
+ DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce));
+ wakeup(sce);
+ }
+ selwakeuppri(&sce->rsel, PZERO);
+}
+
+Static usbd_status
+ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno)
+{
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status err;
+ struct ugen_endpoint *sce;
+ u_int8_t niface, nendpt, endptno, endpt;
+ int dir;
+
+ DPRINTFN(15, ("ugen_set_interface %d %d\n", ifaceidx, altno));
+
+ err = usbd_interface_count(sc->sc_udev, &niface);
+ if (err)
+ return (err);
+ if (ifaceidx < 0 || ifaceidx >= niface)
+ return (USBD_INVAL);
+
+ err = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface);
+ if (err)
+ return (err);
+ err = usbd_endpoint_count(iface, &nendpt);
+ if (err)
+ return (err);
+
+#if defined(__FreeBSD__)
+ /* destroy the existing devices, we remake the new ones in a moment */
+ ugen_destroy_devnodes(sc);
+#endif
+
+ /* XXX should only do this after setting new altno has succeeded */
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ sce->sc = 0;
+ sce->edesc = 0;
+ sce->iface = 0;
+ }
+
+ /* change setting */
+ err = usbd_set_interface(iface, altno);
+ if (err)
+ return (err);
+
+ err = usbd_endpoint_count(iface, &nendpt);
+ if (err)
+ return (err);
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ sce->sc = sc;
+ sce->edesc = ed;
+ sce->iface = iface;
+ }
+
+#if defined(__FreeBSD__)
+ /* make the new devices */
+ ugen_make_devnodes(sc);
+#endif
+
+ return (0);
+}
+
+/* Retrieve a complete descriptor for a certain device and index. */
+Static usb_config_descriptor_t *
+ugen_get_cdesc(struct ugen_softc *sc, int index, int *lenp)
+{
+ usb_config_descriptor_t *cdesc, *tdesc, cdescr;
+ int len;
+ usbd_status err;
+
+ if (index == USB_CURRENT_CONFIG_INDEX) {
+ tdesc = usbd_get_config_descriptor(sc->sc_udev);
+ len = UGETW(tdesc->wTotalLength);
+ if (lenp)
+ *lenp = len;
+ cdesc = malloc(len, M_TEMP, M_WAITOK);
+ memcpy(cdesc, tdesc, len);
+ DPRINTFN(5,("ugen_get_cdesc: current, len=%d\n", len));
+ } else {
+ err = usbd_get_config_desc(sc->sc_udev, index, &cdescr);
+ if (err)
+ return (0);
+ len = UGETW(cdescr.wTotalLength);
+ DPRINTFN(5,("ugen_get_cdesc: index=%d, len=%d\n", index, len));
+ if (lenp)
+ *lenp = len;
+ cdesc = malloc(len, M_TEMP, M_WAITOK);
+ err = usbd_get_config_desc_full(sc->sc_udev, index, cdesc, len);
+ if (err) {
+ free(cdesc, M_TEMP);
+ return (0);
+ }
+ }
+ return (cdesc);
+}
+
+Static int
+ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx)
+{
+ usbd_interface_handle iface;
+ usbd_status err;
+
+ err = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface);
+ if (err)
+ return (-1);
+ return (usbd_get_interface_altindex(iface));
+}
+
+Static int
+ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd,
+ caddr_t addr, int flag, usb_proc_ptr p)
+{
+ struct ugen_endpoint *sce;
+ usbd_status err;
+ usbd_interface_handle iface;
+ struct usb_config_desc *cd;
+ usb_config_descriptor_t *cdesc;
+ struct usb_interface_desc *id;
+ usb_interface_descriptor_t *idesc;
+ struct usb_endpoint_desc *ed;
+ usb_endpoint_descriptor_t *edesc;
+ struct usb_alt_interface *ai;
+ struct usb_string_desc *si;
+ u_int8_t conf, alt;
+
+ DPRINTFN(5, ("ugenioctl: cmd=%08lx\n", cmd));
+ if (sc->sc_dying)
+ return (EIO);
+
+ switch (cmd) {
+ case FIONBIO:
+ /* All handled in the upper FS layer. */
+ return (0);
+ case USB_SET_SHORT_XFER:
+ /* This flag only affects read */
+ if (endpt == USB_CONTROL_ENDPOINT)
+ return (EINVAL);
+ sce = &sc->sc_endpoints[endpt][IN];
+ if (sce == NULL)
+ return (EINVAL);
+
+ if (sce->pipeh == NULL) {
+ printf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n");
+ return (EIO);
+ }
+
+ if (*(int *)addr)
+ sce->state |= UGEN_SHORT_OK;
+ else
+ sce->state &= ~UGEN_SHORT_OK;
+ return (0);
+ case USB_SET_TIMEOUT:
+ sce = &sc->sc_endpoints[endpt][IN];
+ if (sce == NULL)
+ return (EINVAL);
+ sce->timeout = *(int *)addr;
+ return (0);
+ default:
+ break;
+ }
+
+ if (endpt != USB_CONTROL_ENDPOINT)
+ return (EINVAL);
+
+ switch (cmd) {
+#ifdef USB_DEBUG
+ case USB_SETDEBUG:
+ ugendebug = *(int *)addr;
+ break;
+#endif
+ case USB_GET_CONFIG:
+ err = usbd_get_config(sc->sc_udev, &conf);
+ if (err)
+ return (EIO);
+ *(int *)addr = conf;
+ break;
+ case USB_SET_CONFIG:
+ if (!(flag & FWRITE))
+ return (EPERM);
+ err = ugen_set_config(sc, *(int *)addr);
+ switch (err) {
+ case USBD_NORMAL_COMPLETION:
+ break;
+ case USBD_IN_USE:
+ return (EBUSY);
+ default:
+ return (EIO);
+ }
+ break;
+ case USB_GET_ALTINTERFACE:
+ ai = (struct usb_alt_interface *)addr;
+ err = usbd_device2interface_handle(sc->sc_udev,
+ ai->uai_interface_index, &iface);
+ if (err)
+ return (EINVAL);
+ idesc = usbd_get_interface_descriptor(iface);
+ if (idesc == NULL)
+ return (EIO);
+ ai->uai_alt_no = idesc->bAlternateSetting;
+ break;
+ case USB_SET_ALTINTERFACE:
+ if (!(flag & FWRITE))
+ return (EPERM);
+ ai = (struct usb_alt_interface *)addr;
+ err = usbd_device2interface_handle(sc->sc_udev,
+ ai->uai_interface_index, &iface);
+ if (err)
+ return (EINVAL);
+ err = ugen_set_interface(sc, ai->uai_interface_index, ai->uai_alt_no);
+ if (err)
+ return (EINVAL);
+ break;
+ case USB_GET_NO_ALT:
+ ai = (struct usb_alt_interface *)addr;
+ cdesc = ugen_get_cdesc(sc, ai->uai_config_index, 0);
+ if (cdesc == NULL)
+ return (EINVAL);
+ idesc = usbd_find_idesc(cdesc, ai->uai_interface_index, 0);
+ if (idesc == NULL) {
+ free(cdesc, M_TEMP);
+ return (EINVAL);
+ }
+ ai->uai_alt_no = usbd_get_no_alts(cdesc, idesc->bInterfaceNumber);
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_DEVICE_DESC:
+ *(usb_device_descriptor_t *)addr =
+ *usbd_get_device_descriptor(sc->sc_udev);
+ break;
+ case USB_GET_CONFIG_DESC:
+ cd = (struct usb_config_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, cd->ucd_config_index, 0);
+ if (cdesc == NULL)
+ return (EINVAL);
+ cd->ucd_desc = *cdesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_INTERFACE_DESC:
+ id = (struct usb_interface_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, id->uid_config_index, 0);
+ if (cdesc == NULL)
+ return (EINVAL);
+ if (id->uid_config_index == USB_CURRENT_CONFIG_INDEX &&
+ id->uid_alt_index == USB_CURRENT_ALT_INDEX)
+ alt = ugen_get_alt_index(sc, id->uid_interface_index);
+ else
+ alt = id->uid_alt_index;
+ idesc = usbd_find_idesc(cdesc, id->uid_interface_index, alt);
+ if (idesc == NULL) {
+ free(cdesc, M_TEMP);
+ return (EINVAL);
+ }
+ id->uid_desc = *idesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_ENDPOINT_DESC:
+ ed = (struct usb_endpoint_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, ed->ued_config_index, 0);
+ if (cdesc == NULL)
+ return (EINVAL);
+ if (ed->ued_config_index == USB_CURRENT_CONFIG_INDEX &&
+ ed->ued_alt_index == USB_CURRENT_ALT_INDEX)
+ alt = ugen_get_alt_index(sc, ed->ued_interface_index);
+ else
+ alt = ed->ued_alt_index;
+ edesc = usbd_find_edesc(cdesc, ed->ued_interface_index,
+ alt, ed->ued_endpoint_index);
+ if (edesc == NULL) {
+ free(cdesc, M_TEMP);
+ return (EINVAL);
+ }
+ ed->ued_desc = *edesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_FULL_DESC:
+ {
+ int len;
+ struct iovec iov;
+ struct uio uio;
+ struct usb_full_desc *fd = (struct usb_full_desc *)addr;
+ int error;
+
+ cdesc = ugen_get_cdesc(sc, fd->ufd_config_index, &len);
+ if (len > fd->ufd_size)
+ len = fd->ufd_size;
+ iov.iov_base = (caddr_t)fd->ufd_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 = UIO_READ;
+ uio.uio_procp = p;
+ error = uiomove((void *)cdesc, len, &uio);
+ free(cdesc, M_TEMP);
+ return (error);
+ }
+ case USB_GET_STRING_DESC:
+ si = (struct usb_string_desc *)addr;
+ err = usbd_get_string_desc(sc->sc_udev, si->usd_string_index,
+ si->usd_language_id, &si->usd_desc);
+ if (err)
+ return (EINVAL);
+ break;
+ case USB_DO_REQUEST:
+ {
+ struct usb_ctl_request *ur = (void *)addr;
+ int len = UGETW(ur->ucr_request.wLength);
+ struct iovec iov;
+ struct uio uio;
+ void *ptr = 0;
+ usbd_status err;
+ int error = 0;
+
+ if (!(flag & FWRITE))
+ return (EPERM);
+ /* Avoid requests that would damage the bus integrity. */
+ if ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
+ ur->ucr_request.bRequest == UR_SET_ADDRESS) ||
+ (ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
+ ur->ucr_request.bRequest == UR_SET_CONFIG) ||
+ (ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE &&
+ ur->ucr_request.bRequest == UR_SET_INTERFACE))
+ return (EINVAL);
+
+ if (len < 0 || len > 32767)
+ return (EINVAL);
+ if (len != 0) {
+ iov.iov_base = (caddr_t)ur->ucr_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->ucr_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;
+ }
+ }
+ sce = &sc->sc_endpoints[endpt][IN];
+ err = usbd_do_request_flags(sc->sc_udev, &ur->ucr_request,
+ ptr, ur->ucr_flags, &ur->ucr_actlen, sce->timeout);
+ if (err) {
+ 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);
+ }
+ case USB_GET_DEVICEINFO:
+ usbd_fill_deviceinfo(sc->sc_udev,
+ (struct usb_device_info *)addr, 1);
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+ugenioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+ int error;
+
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+
+ sc->sc_refcnt++;
+ error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, p);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+int
+ugenpoll(struct cdev *dev, int events, usb_proc_ptr p)
+{
+ struct ugen_softc *sc;
+ struct ugen_endpoint *sce;
+ int revents = 0;
+ int s;
+
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ /* XXX always IN */
+ sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
+ if (sce == NULL)
+ return (EINVAL);
+
+ if (!sce->edesc) {
+ printf("ugenpoll: no edesc\n");
+ return (EIO);
+ }
+ if (!sce->pipeh) {
+ printf("ugenpoll: no pipe\n");
+ return (EIO);
+ }
+
+ s = splusb();
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (sce->q.c_cc > 0)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(p, &sce->rsel);
+ }
+ break;
+ case UE_ISOCHRONOUS:
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (sce->cur != sce->fill)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(p, &sce->rsel);
+ }
+ break;
+ case UE_BULK:
+ /*
+ * We have no easy way of determining if a read will
+ * yield any data or a write will happen.
+ * Pretend they will.
+ */
+ revents |= events &
+ (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM);
+ break;
+ default:
+ break;
+ }
+ splx(s);
+ return (revents);
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(ugen, uhub, ugen_driver, ugen_devclass, usbd_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/ugraphire_rdesc.h b/sys/dev/usb/ugraphire_rdesc.h
new file mode 100644
index 0000000..9dd6988
--- /dev/null
+++ b/sys/dev/usb/ugraphire_rdesc.h
@@ -0,0 +1,92 @@
+/* $NetBSD: usb/ugraphire_rdesc.h,v 1.1 2000/12/29 01:47:49 augustss Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 2000 Nick Hibma <n_hibma@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+static uByte uhid_graphire_report_descr[] = {
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x01, /* USAGE (Digitizer) */
+ 0xa1, 0x01, /* COLLECTION (Application) */
+ 0x85, 0x02, /* REPORT_ID (2) */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x01, /* USAGE (Digitizer) */
+ 0xa1, 0x00, /* COLLECTION (Physical) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
+ 0x09, 0x33, /* USAGE (Touch) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x44, /* USAGE (Barrel Switch) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x00, /* USAGE (Undefined) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
+ 0x09, 0x3c, /* USAGE (Invert) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x38, /* USAGE (Transducer Index) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x32, /* USAGE (In Range) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x30, /* USAGE (Tip Pressure) */
+ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0xc0, /* END_COLLECTION */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x00, /* USAGE (Undefined) */
+ 0x85, 0x02, /* REPORT_ID (2) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */
+ 0x09, 0x00, /* USAGE (Undefined) */
+ 0x85, 0x03, /* REPORT_ID (3) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */
+ 0xc0, /* END_COLLECTION */
+};
diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c
new file mode 100644
index 0000000..0fffd21
--- /dev/null
+++ b/sys/dev/usb/uhci.c
@@ -0,0 +1,3538 @@
+/* $NetBSD: uhci.c,v 1.170 2003/02/19 01:35:04 augustss Exp $ */
+
+/* Also already incorporated from NetBSD:
+ * $NetBSD: uhci.c,v 1.172 2003/02/23 04:19:26 simonb Exp $
+ * $NetBSD: uhci.c,v 1.173 2003/05/13 04:41:59 gson Exp $
+ * $NetBSD: uhci.c,v 1.175 2003/09/12 16:18:08 mycroft Exp $
+ * $NetBSD: uhci.c,v 1.176 2003/11/04 19:11:21 mycroft Exp $
+ * $NetBSD: uhci.c,v 1.177 2003/12/29 08:17:10 toshii Exp $
+ * $NetBSD: uhci.c,v 1.178 2004/03/02 16:32:05 martin Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 e.g. PIIX3 and PIIX4.
+ *
+ * UHCI spec: http://developer.intel.com/design/USB/UHCI11D.htm
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ * PIIXn spec: ftp://download.intel.com/design/intarch/datashts/29055002.pdf
+ * ftp://download.intel.com/design/intarch/datashts/29056201.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#include <sys/select.h>
+#elif defined(__FreeBSD__)
+#include <sys/endian.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/bus_pio.h>
+#if defined(DIAGNOSTIC) && defined(__i386__)
+#include <machine/cpu.h>
+#endif
+#endif
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/endian.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>
+
+/* Use bandwidth reclamation for control transfers. Some devices choke on it. */
+/*#define UHCI_CTL_LOOP */
+
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+
+#define delay(d) DELAY(d)
+#endif
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+#if defined(__OpenBSD__)
+struct cfdriver uhci_cd = {
+ NULL, "uhci", DV_DULL
+};
+#endif
+
+#ifdef USB_DEBUG
+uhci_softc_t *thesc;
+#define DPRINTF(x) if (uhcidebug) printf x
+#define DPRINTFN(n,x) if (uhcidebug>(n)) printf x
+int uhcidebug = 0;
+int uhcinoloop = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uhci, CTLFLAG_RW, 0, "USB uhci");
+SYSCTL_INT(_hw_usb_uhci, OID_AUTO, debug, CTLFLAG_RW,
+ &uhcidebug, 0, "uhci debug level");
+SYSCTL_INT(_hw_usb_uhci, OID_AUTO, loop, CTLFLAG_RW,
+ &uhcinoloop, 0, "uhci noloop");
+#ifndef __NetBSD__
+#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f))
+#endif
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+/*
+ * The UHCI controller is little endian, so on big endian machines
+ * the data strored in memory needs to be swapped.
+ */
+#if defined(__OpenBSD__)
+#if BYTE_ORDER == BIG_ENDIAN
+#define htole32(x) (bswap32(x))
+#define le32toh(x) (bswap32(x))
+#else
+#define htole32(x) (x)
+#define le32toh(x) (x)
+#endif
+#endif
+
+struct uhci_pipe {
+ struct usbd_pipe pipe;
+ int nexttoggle;
+
+ u_char aborting;
+ usbd_xfer_handle abortstart, abortend;
+
+ /* Info needed for different pipe kinds. */
+ union {
+ /* Control pipe */
+ struct {
+ uhci_soft_qh_t *sqh;
+ usb_dma_t reqdma;
+ uhci_soft_td_t *setup, *stat;
+ u_int length;
+ } ctl;
+ /* Interrupt pipe */
+ struct {
+ int npoll;
+ int isread;
+ uhci_soft_qh_t **qhs;
+ } intr;
+ /* Bulk pipe */
+ struct {
+ uhci_soft_qh_t *sqh;
+ u_int length;
+ int isread;
+ } bulk;
+ /* Iso pipe */
+ struct iso {
+ uhci_soft_td_t **stds;
+ int next, inuse;
+ } iso;
+ } u;
+};
+
+Static void uhci_globalreset(uhci_softc_t *);
+Static usbd_status uhci_portreset(uhci_softc_t*, int);
+Static void uhci_reset(uhci_softc_t *);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+Static void uhci_shutdown(void *v);
+Static void uhci_power(int, void *);
+#endif
+Static usbd_status uhci_run(uhci_softc_t *, int run);
+Static uhci_soft_td_t *uhci_alloc_std(uhci_softc_t *);
+Static void uhci_free_std(uhci_softc_t *, uhci_soft_td_t *);
+Static uhci_soft_qh_t *uhci_alloc_sqh(uhci_softc_t *);
+Static void uhci_free_sqh(uhci_softc_t *, uhci_soft_qh_t *);
+#if 0
+Static void uhci_enter_ctl_q(uhci_softc_t *, uhci_soft_qh_t *,
+ uhci_intr_info_t *);
+Static void uhci_exit_ctl_q(uhci_softc_t *, uhci_soft_qh_t *);
+#endif
+
+Static void uhci_free_std_chain(uhci_softc_t *,
+ uhci_soft_td_t *, uhci_soft_td_t *);
+Static usbd_status uhci_alloc_std_chain(struct uhci_pipe *,
+ uhci_softc_t *, int, int, u_int16_t, usb_dma_t *,
+ uhci_soft_td_t **, uhci_soft_td_t **);
+Static void uhci_poll_hub(void *);
+Static void uhci_waitintr(uhci_softc_t *, usbd_xfer_handle);
+Static void uhci_check_intr(uhci_softc_t *, uhci_intr_info_t *);
+Static void uhci_idone(uhci_intr_info_t *);
+
+Static void uhci_abort_xfer(usbd_xfer_handle, usbd_status status);
+
+Static void uhci_timeout(void *);
+Static void uhci_timeout_task(void *);
+Static void uhci_add_ls_ctrl(uhci_softc_t *, uhci_soft_qh_t *);
+Static void uhci_add_hs_ctrl(uhci_softc_t *, uhci_soft_qh_t *);
+Static void uhci_add_bulk(uhci_softc_t *, uhci_soft_qh_t *);
+Static void uhci_remove_ls_ctrl(uhci_softc_t *,uhci_soft_qh_t *);
+Static void uhci_remove_hs_ctrl(uhci_softc_t *,uhci_soft_qh_t *);
+Static void uhci_remove_bulk(uhci_softc_t *,uhci_soft_qh_t *);
+Static int uhci_str(usb_string_descriptor_t *, int, char *);
+Static void uhci_add_loop(uhci_softc_t *sc);
+Static void uhci_rem_loop(uhci_softc_t *sc);
+
+Static usbd_status uhci_setup_isoc(usbd_pipe_handle pipe);
+Static void uhci_device_isoc_enter(usbd_xfer_handle);
+
+Static usbd_status uhci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t);
+Static void uhci_freem(struct usbd_bus *, usb_dma_t *);
+
+Static usbd_xfer_handle uhci_allocx(struct usbd_bus *);
+Static void uhci_freex(struct usbd_bus *, usbd_xfer_handle);
+
+Static usbd_status uhci_device_ctrl_transfer(usbd_xfer_handle);
+Static usbd_status uhci_device_ctrl_start(usbd_xfer_handle);
+Static void uhci_device_ctrl_abort(usbd_xfer_handle);
+Static void uhci_device_ctrl_close(usbd_pipe_handle);
+Static void uhci_device_ctrl_done(usbd_xfer_handle);
+
+Static usbd_status uhci_device_intr_transfer(usbd_xfer_handle);
+Static usbd_status uhci_device_intr_start(usbd_xfer_handle);
+Static void uhci_device_intr_abort(usbd_xfer_handle);
+Static void uhci_device_intr_close(usbd_pipe_handle);
+Static void uhci_device_intr_done(usbd_xfer_handle);
+
+Static usbd_status uhci_device_bulk_transfer(usbd_xfer_handle);
+Static usbd_status uhci_device_bulk_start(usbd_xfer_handle);
+Static void uhci_device_bulk_abort(usbd_xfer_handle);
+Static void uhci_device_bulk_close(usbd_pipe_handle);
+Static void uhci_device_bulk_done(usbd_xfer_handle);
+
+Static usbd_status uhci_device_isoc_transfer(usbd_xfer_handle);
+Static usbd_status uhci_device_isoc_start(usbd_xfer_handle);
+Static void uhci_device_isoc_abort(usbd_xfer_handle);
+Static void uhci_device_isoc_close(usbd_pipe_handle);
+Static void uhci_device_isoc_done(usbd_xfer_handle);
+
+Static usbd_status uhci_root_ctrl_transfer(usbd_xfer_handle);
+Static usbd_status uhci_root_ctrl_start(usbd_xfer_handle);
+Static void uhci_root_ctrl_abort(usbd_xfer_handle);
+Static void uhci_root_ctrl_close(usbd_pipe_handle);
+Static void uhci_root_ctrl_done(usbd_xfer_handle);
+
+Static usbd_status uhci_root_intr_transfer(usbd_xfer_handle);
+Static usbd_status uhci_root_intr_start(usbd_xfer_handle);
+Static void uhci_root_intr_abort(usbd_xfer_handle);
+Static void uhci_root_intr_close(usbd_pipe_handle);
+Static void uhci_root_intr_done(usbd_xfer_handle);
+
+Static usbd_status uhci_open(usbd_pipe_handle);
+Static void uhci_poll(struct usbd_bus *);
+Static void uhci_softintr(void *);
+
+Static usbd_status uhci_device_request(usbd_xfer_handle xfer);
+
+Static void uhci_add_intr(uhci_softc_t *, uhci_soft_qh_t *);
+Static void uhci_remove_intr(uhci_softc_t *, uhci_soft_qh_t *);
+Static usbd_status uhci_device_setintr(uhci_softc_t *sc,
+ struct uhci_pipe *pipe, int ival);
+
+Static void uhci_device_clear_toggle(usbd_pipe_handle pipe);
+Static void uhci_noop(usbd_pipe_handle pipe);
+
+Static __inline__ uhci_soft_qh_t *uhci_find_prev_qh(uhci_soft_qh_t *,
+ uhci_soft_qh_t *);
+
+#ifdef USB_DEBUG
+Static void uhci_dump_all(uhci_softc_t *);
+Static void uhci_dumpregs(uhci_softc_t *);
+Static void uhci_dump_qhs(uhci_soft_qh_t *);
+Static void uhci_dump_qh(uhci_soft_qh_t *);
+Static void uhci_dump_tds(uhci_soft_td_t *);
+Static void uhci_dump_td(uhci_soft_td_t *);
+Static void uhci_dump_ii(uhci_intr_info_t *ii);
+void uhci_dump(void);
+#endif
+
+#define UBARR(sc) bus_space_barrier((sc)->iot, (sc)->ioh, 0, (sc)->sc_size, \
+ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
+#define UWRITE1(sc, r, x) \
+ do { UBARR(sc); bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UWRITE2(sc, r, x) \
+ do { UBARR(sc); bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UWRITE4(sc, r, x) \
+ do { UBARR(sc); bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UREAD1(sc, r) (UBARR(sc), bus_space_read_1((sc)->iot, (sc)->ioh, (r)))
+#define UREAD2(sc, r) (UBARR(sc), bus_space_read_2((sc)->iot, (sc)->ioh, (r)))
+#define UREAD4(sc, r) (UBARR(sc), bus_space_read_4((sc)->iot, (sc)->ioh, (r)))
+
+#define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd)
+#define UHCISTS(sc) UREAD2(sc, UHCI_STS)
+
+#define UHCI_RESET_TIMEOUT 100 /* ms, reset timeout */
+
+#define UHCI_CURFRAME(sc) (UREAD2(sc, UHCI_FRNUM) & UHCI_FRNUM_MASK)
+
+#define UHCI_INTR_ENDPT 1
+
+struct usbd_bus_methods uhci_bus_methods = {
+ uhci_open,
+ uhci_softintr,
+ uhci_poll,
+ uhci_allocm,
+ uhci_freem,
+ uhci_allocx,
+ uhci_freex,
+};
+
+struct usbd_pipe_methods uhci_root_ctrl_methods = {
+ uhci_root_ctrl_transfer,
+ uhci_root_ctrl_start,
+ uhci_root_ctrl_abort,
+ uhci_root_ctrl_close,
+ uhci_noop,
+ uhci_root_ctrl_done,
+};
+
+struct usbd_pipe_methods uhci_root_intr_methods = {
+ uhci_root_intr_transfer,
+ uhci_root_intr_start,
+ uhci_root_intr_abort,
+ uhci_root_intr_close,
+ uhci_noop,
+ uhci_root_intr_done,
+};
+
+struct usbd_pipe_methods uhci_device_ctrl_methods = {
+ uhci_device_ctrl_transfer,
+ uhci_device_ctrl_start,
+ uhci_device_ctrl_abort,
+ uhci_device_ctrl_close,
+ uhci_noop,
+ uhci_device_ctrl_done,
+};
+
+struct usbd_pipe_methods uhci_device_intr_methods = {
+ uhci_device_intr_transfer,
+ uhci_device_intr_start,
+ uhci_device_intr_abort,
+ uhci_device_intr_close,
+ uhci_device_clear_toggle,
+ uhci_device_intr_done,
+};
+
+struct usbd_pipe_methods uhci_device_bulk_methods = {
+ uhci_device_bulk_transfer,
+ uhci_device_bulk_start,
+ uhci_device_bulk_abort,
+ uhci_device_bulk_close,
+ uhci_device_clear_toggle,
+ uhci_device_bulk_done,
+};
+
+struct usbd_pipe_methods uhci_device_isoc_methods = {
+ uhci_device_isoc_transfer,
+ uhci_device_isoc_start,
+ uhci_device_isoc_abort,
+ uhci_device_isoc_close,
+ uhci_noop,
+ uhci_device_isoc_done,
+};
+
+#define uhci_add_intr_info(sc, ii) \
+ LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ii), list)
+#define uhci_del_intr_info(ii) \
+ do { \
+ LIST_REMOVE((ii), list); \
+ (ii)->list.le_prev = NULL; \
+ } while (0)
+#define uhci_active_intr_info(ii) ((ii)->list.le_prev != NULL)
+
+Static __inline__ uhci_soft_qh_t *
+uhci_find_prev_qh(uhci_soft_qh_t *pqh, uhci_soft_qh_t *sqh)
+{
+ DPRINTFN(15,("uhci_find_prev_qh: pqh=%p sqh=%p\n", pqh, sqh));
+
+ for (; pqh->hlink != sqh; pqh = pqh->hlink) {
+#if defined(DIAGNOSTIC) || defined(USB_DEBUG)
+ if (le32toh(pqh->qh.qh_hlink) & UHCI_PTR_T) {
+ printf("uhci_find_prev_qh: QH not found\n");
+ return (NULL);
+ }
+#endif
+ }
+ return (pqh);
+}
+
+void
+uhci_globalreset(uhci_softc_t *sc)
+{
+ UHCICMD(sc, UHCI_CMD_GRESET); /* global reset */
+ usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY); /* wait a little */
+ UHCICMD(sc, 0); /* do nothing */
+}
+
+usbd_status
+uhci_init(uhci_softc_t *sc)
+{
+ usbd_status err;
+ int i, j;
+ uhci_soft_qh_t *clsqh, *chsqh, *bsqh, *sqh, *lsqh;
+ uhci_soft_td_t *std;
+
+ DPRINTFN(1,("uhci_init: start\n"));
+
+#ifdef USB_DEBUG
+ thesc = sc;
+
+ if (uhcidebug > 2)
+ uhci_dumpregs(sc);
+#endif
+
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+ uhci_globalreset(sc); /* reset the controller */
+ uhci_reset(sc);
+
+ /* Allocate and initialize real frame array. */
+ err = usb_allocmem(&sc->sc_bus,
+ UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t),
+ UHCI_FRAMELIST_ALIGN, &sc->sc_dma);
+ if (err)
+ return (err);
+ sc->sc_pframes = KERNADDR(&sc->sc_dma, 0);
+ UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */
+ UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0)); /* set frame list*/
+
+ /*
+ * Allocate a TD, inactive, that hangs from the last QH.
+ * This is to avoid a bug in the PIIX that makes it run berserk
+ * otherwise.
+ */
+ std = uhci_alloc_std(sc);
+ if (std == NULL)
+ return (USBD_NOMEM);
+ std->link.std = NULL;
+ std->td.td_link = htole32(UHCI_PTR_T);
+ std->td.td_status = htole32(0); /* inactive */
+ std->td.td_token = htole32(0);
+ std->td.td_buffer = htole32(0);
+
+ /* Allocate the dummy QH marking the end and used for looping the QHs.*/
+ lsqh = uhci_alloc_sqh(sc);
+ if (lsqh == NULL)
+ return (USBD_NOMEM);
+ lsqh->hlink = NULL;
+ lsqh->qh.qh_hlink = htole32(UHCI_PTR_T); /* end of QH chain */
+ lsqh->elink = std;
+ lsqh->qh.qh_elink = htole32(std->physaddr | UHCI_PTR_TD);
+ sc->sc_last_qh = lsqh;
+
+ /* Allocate the dummy QH where bulk traffic will be queued. */
+ bsqh = uhci_alloc_sqh(sc);
+ if (bsqh == NULL)
+ return (USBD_NOMEM);
+ bsqh->hlink = lsqh;
+ bsqh->qh.qh_hlink = htole32(lsqh->physaddr | UHCI_PTR_QH);
+ bsqh->elink = NULL;
+ bsqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sc->sc_bulk_start = sc->sc_bulk_end = bsqh;
+
+ /* Allocate dummy QH where high speed control traffic will be queued. */
+ chsqh = uhci_alloc_sqh(sc);
+ if (chsqh == NULL)
+ return (USBD_NOMEM);
+ chsqh->hlink = bsqh;
+ chsqh->qh.qh_hlink = htole32(bsqh->physaddr | UHCI_PTR_QH);
+ chsqh->elink = NULL;
+ chsqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sc->sc_hctl_start = sc->sc_hctl_end = chsqh;
+
+ /* Allocate dummy QH where control traffic will be queued. */
+ clsqh = uhci_alloc_sqh(sc);
+ if (clsqh == NULL)
+ return (USBD_NOMEM);
+ clsqh->hlink = bsqh;
+ clsqh->qh.qh_hlink = htole32(chsqh->physaddr | UHCI_PTR_QH);
+ clsqh->elink = NULL;
+ clsqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sc->sc_lctl_start = sc->sc_lctl_end = clsqh;
+
+ /*
+ * 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 == NULL || sqh == NULL)
+ return (USBD_NOMEM);
+ std->link.sqh = sqh;
+ std->td.td_link = htole32(sqh->physaddr | UHCI_PTR_QH);
+ std->td.td_status = htole32(UHCI_TD_IOS); /* iso, inactive */
+ std->td.td_token = htole32(0);
+ std->td.td_buffer = htole32(0);
+ sqh->hlink = clsqh;
+ sqh->qh.qh_hlink = htole32(clsqh->physaddr | UHCI_PTR_QH);
+ sqh->elink = NULL;
+ sqh->qh.qh_elink = htole32(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] = htole32(std->physaddr);
+ }
+
+ LIST_INIT(&sc->sc_intrhead);
+
+ SIMPLEQ_INIT(&sc->sc_free_xfers);
+
+ usb_callout_init(sc->sc_poll_handle);
+
+ /* Set up the bus struct. */
+ sc->sc_bus.methods = &uhci_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct uhci_pipe);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sc->sc_suspend = PWR_RESUME;
+ sc->sc_powerhook = powerhook_establish(uhci_power, sc);
+ sc->sc_shutdownhook = shutdownhook_establish(uhci_shutdown, sc);
+#endif
+
+ DPRINTFN(1,("uhci_init: enabling\n"));
+ UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE |
+ UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */
+
+ UHCICMD(sc, UHCI_CMD_MAXP); /* Assume 64 byte packets at frame end */
+
+ return (uhci_run(sc, 1)); /* and here we go... */
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+uhci_activate(device_ptr_t self, enum devact act)
+{
+ struct uhci_softc *sc = (struct uhci_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ if (sc->sc_child != NULL)
+ rv = config_deactivate(sc->sc_child);
+ break;
+ }
+ return (rv);
+}
+
+int
+uhci_detach(struct uhci_softc *sc, int flags)
+{
+ usbd_xfer_handle xfer;
+ int rv = 0;
+
+ if (sc->sc_child != NULL)
+ rv = config_detach(sc->sc_child, flags);
+
+ if (rv != 0)
+ return (rv);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ powerhook_disestablish(sc->sc_powerhook);
+ shutdownhook_disestablish(sc->sc_shutdownhook);
+#endif
+
+ /* Free all xfers associated with this HC. */
+ for (;;) {
+ xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers);
+ if (xfer == NULL)
+ break;
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+ free(xfer, M_USB);
+ }
+
+ /* XXX free other data structures XXX */
+
+ return (rv);
+}
+#endif
+
+usbd_status
+uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
+{
+ return (usb_allocmem(bus, size, 0, dma));
+}
+
+void
+uhci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ usb_freemem(bus, dma);
+}
+
+usbd_xfer_handle
+uhci_allocx(struct usbd_bus *bus)
+{
+ struct uhci_softc *sc = (struct uhci_softc *)bus;
+ usbd_xfer_handle xfer;
+
+ xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers);
+ if (xfer != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_FREE) {
+ printf("uhci_allocx: xfer=%p not free, 0x%08x\n", xfer,
+ xfer->busy_free);
+ }
+#endif
+ } else {
+ xfer = malloc(sizeof(struct uhci_xfer), M_USB, M_NOWAIT);
+ }
+ if (xfer != NULL) {
+ memset(xfer, 0, sizeof (struct uhci_xfer));
+ UXFER(xfer)->iinfo.sc = sc;
+#ifdef DIAGNOSTIC
+ UXFER(xfer)->iinfo.isdone = 1;
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
+ return (xfer);
+}
+
+void
+uhci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
+{
+ struct uhci_softc *sc = (struct uhci_softc *)bus;
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("uhci_freex: xfer=%p not busy, 0x%08x\n", xfer,
+ xfer->busy_free);
+ return;
+ }
+ xfer->busy_free = XFER_FREE;
+ if (!UXFER(xfer)->iinfo.isdone) {
+ printf("uhci_freex: !isdone\n");
+ return;
+ }
+#endif
+ SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+/*
+ * Shut down the controller when the system is going down.
+ */
+void
+uhci_shutdown(void *v)
+{
+ uhci_softc_t *sc = v;
+
+ DPRINTF(("uhci_shutdown: stopping the HC\n"));
+ uhci_run(sc, 0); /* stop the controller */
+}
+
+/*
+ * Handle suspend/resume.
+ *
+ * We need to switch to polling mode here, because this routine is
+ * called from an interrupt context. This is all right since we
+ * are almost suspended anyway.
+ */
+void
+uhci_power(int why, void *v)
+{
+ uhci_softc_t *sc = v;
+ int cmd;
+ int s;
+
+ s = splhardusb();
+ cmd = UREAD2(sc, UHCI_CMD);
+
+ DPRINTF(("uhci_power: sc=%p, why=%d (was %d), cmd=0x%x\n",
+ sc, why, sc->sc_suspend, cmd));
+
+ if (why != PWR_RESUME) {
+#ifdef USB_DEBUG
+ if (uhcidebug > 2)
+ uhci_dumpregs(sc);
+#endif
+ if (sc->sc_intr_xfer != NULL)
+ usb_uncallout(sc->sc_poll_handle, uhci_poll_hub,
+ sc->sc_intr_xfer);
+ sc->sc_bus.use_polling++;
+ uhci_run(sc, 0); /* stop the controller */
+
+ /* save some state if BIOS doesn't */
+ sc->sc_saved_frnum = UREAD2(sc, UHCI_FRNUM);
+ sc->sc_saved_sof = UREAD1(sc, UHCI_SOF);
+
+ UWRITE2(sc, UHCI_INTR, 0); /* disable intrs */
+
+ UHCICMD(sc, cmd | UHCI_CMD_EGSM); /* enter global suspend */
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+ sc->sc_suspend = why;
+ sc->sc_bus.use_polling--;
+ DPRINTF(("uhci_power: cmd=0x%x\n", UREAD2(sc, UHCI_CMD)));
+ } else {
+#ifdef DIAGNOSTIC
+ if (sc->sc_suspend == PWR_RESUME)
+ printf("uhci_power: weird, resume without suspend.\n");
+#endif
+ sc->sc_bus.use_polling++;
+ sc->sc_suspend = why;
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+ uhci_globalreset(sc); /* reset the controller */
+ uhci_reset(sc);
+ if (cmd & UHCI_CMD_RS)
+ uhci_run(sc, 0); /* in case BIOS has started it */
+
+ /* restore saved state */
+ UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0));
+ UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum);
+ UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof);
+
+ UHCICMD(sc, cmd | UHCI_CMD_FGR); /* force global resume */
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY);
+ UHCICMD(sc, cmd & ~UHCI_CMD_EGSM); /* back to normal */
+ UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE |
+ UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* re-enable intrs */
+ UHCICMD(sc, UHCI_CMD_MAXP);
+ uhci_run(sc, 1); /* and start traffic again */
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY);
+ sc->sc_bus.use_polling--;
+ if (sc->sc_intr_xfer != NULL)
+ usb_callout(sc->sc_poll_handle, sc->sc_ival,
+ uhci_poll_hub, sc->sc_intr_xfer);
+#ifdef USB_DEBUG
+ if (uhcidebug > 2)
+ uhci_dumpregs(sc);
+#endif
+ }
+ splx(s);
+}
+
+#ifdef USB_DEBUG
+Static void
+uhci_dumpregs(uhci_softc_t *sc)
+{
+ DPRINTFN(-1,("%s regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, "
+ "flbase=%08x, sof=%04x, portsc1=%04x, portsc2=%04x\n",
+ USBDEVNAME(sc->sc_bus.bdev),
+ UREAD2(sc, UHCI_CMD),
+ UREAD2(sc, UHCI_STS),
+ UREAD2(sc, UHCI_INTR),
+ UREAD2(sc, UHCI_FRNUM),
+ UREAD4(sc, UHCI_FLBASEADDR),
+ UREAD1(sc, UHCI_SOF),
+ UREAD2(sc, UHCI_PORTSC1),
+ UREAD2(sc, UHCI_PORTSC2)));
+}
+
+void
+uhci_dump_td(uhci_soft_td_t *p)
+{
+ char sbuf[128], sbuf2[128];
+
+ DPRINTFN(-1,("TD(%p) at %08lx = link=0x%08lx status=0x%08lx "
+ "token=0x%08lx buffer=0x%08lx\n",
+ p, (long)p->physaddr,
+ (long)le32toh(p->td.td_link),
+ (long)le32toh(p->td.td_status),
+ (long)le32toh(p->td.td_token),
+ (long)le32toh(p->td.td_buffer)));
+
+ bitmask_snprintf((u_int32_t)le32toh(p->td.td_link), "\20\1T\2Q\3VF",
+ sbuf, sizeof(sbuf));
+ bitmask_snprintf((u_int32_t)le32toh(p->td.td_status),
+ "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27"
+ "STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD",
+ sbuf2, sizeof(sbuf2));
+
+ DPRINTFN(-1,(" %s %s,errcnt=%d,actlen=%d pid=%02x,addr=%d,endpt=%d,"
+ "D=%d,maxlen=%d\n", sbuf, sbuf2,
+ UHCI_TD_GET_ERRCNT(le32toh(p->td.td_status)),
+ UHCI_TD_GET_ACTLEN(le32toh(p->td.td_status)),
+ UHCI_TD_GET_PID(le32toh(p->td.td_token)),
+ UHCI_TD_GET_DEVADDR(le32toh(p->td.td_token)),
+ UHCI_TD_GET_ENDPT(le32toh(p->td.td_token)),
+ UHCI_TD_GET_DT(le32toh(p->td.td_token)),
+ UHCI_TD_GET_MAXLEN(le32toh(p->td.td_token))));
+}
+
+void
+uhci_dump_qh(uhci_soft_qh_t *sqh)
+{
+ DPRINTFN(-1,("QH(%p) at %08x: hlink=%08x elink=%08x\n", sqh,
+ (int)sqh->physaddr, le32toh(sqh->qh.qh_hlink),
+ le32toh(sqh->qh.qh_elink)));
+}
+
+
+#if 1
+void
+uhci_dump(void)
+{
+ uhci_dump_all(thesc);
+}
+#endif
+
+void
+uhci_dump_all(uhci_softc_t *sc)
+{
+ uhci_dumpregs(sc);
+ printf("intrs=%d\n", sc->sc_bus.no_intrs);
+ /*printf("framelist[i].link = %08x\n", sc->sc_framelist[0].link);*/
+ uhci_dump_qh(sc->sc_lctl_start);
+}
+
+
+void
+uhci_dump_qhs(uhci_soft_qh_t *sqh)
+{
+ uhci_dump_qh(sqh);
+
+ /* uhci_dump_qhs displays all the QHs and TDs from the given QH onwards
+ * Traverses sideways first, then down.
+ *
+ * QH1
+ * QH2
+ * No QH
+ * TD2.1
+ * TD2.2
+ * TD1.1
+ * etc.
+ *
+ * TD2.x being the TDs queued at QH2 and QH1 being referenced from QH1.
+ */
+
+
+ if (sqh->hlink != NULL && !(le32toh(sqh->qh.qh_hlink) & UHCI_PTR_T))
+ uhci_dump_qhs(sqh->hlink);
+ else
+ DPRINTF(("No QH\n"));
+
+ if (sqh->elink != NULL && !(le32toh(sqh->qh.qh_elink) & UHCI_PTR_T))
+ uhci_dump_tds(sqh->elink);
+ else
+ DPRINTF(("No TD\n"));
+}
+
+void
+uhci_dump_tds(uhci_soft_td_t *std)
+{
+ uhci_soft_td_t *td;
+
+ for(td = std; td != NULL; td = td->link.std) {
+ uhci_dump_td(td);
+
+ /* Check whether the link pointer in this TD marks
+ * the link pointer as end of queue. This avoids
+ * printing the free list in case the queue/TD has
+ * already been moved there (seatbelt).
+ */
+ if (le32toh(td->td.td_link) & UHCI_PTR_T ||
+ le32toh(td->td.td_link) == 0)
+ break;
+ }
+}
+
+Static void
+uhci_dump_ii(uhci_intr_info_t *ii)
+{
+ usbd_pipe_handle pipe;
+ usb_endpoint_descriptor_t *ed;
+ usbd_device_handle dev;
+
+#ifdef DIAGNOSTIC
+#define DONE ii->isdone
+#else
+#define DONE 0
+#endif
+ if (ii == NULL) {
+ printf("ii NULL\n");
+ return;
+ }
+ if (ii->xfer == NULL) {
+ printf("ii %p: done=%d xfer=NULL\n",
+ ii, DONE);
+ return;
+ }
+ pipe = ii->xfer->pipe;
+ if (pipe == NULL) {
+ printf("ii %p: done=%d xfer=%p pipe=NULL\n",
+ ii, DONE, ii->xfer);
+ return;
+ }
+ if (pipe->endpoint == NULL) {
+ printf("ii %p: done=%d xfer=%p pipe=%p pipe->endpoint=NULL\n",
+ ii, DONE, ii->xfer, pipe);
+ return;
+ }
+ if (pipe->device == NULL) {
+ printf("ii %p: done=%d xfer=%p pipe=%p pipe->device=NULL\n",
+ ii, DONE, ii->xfer, pipe);
+ return;
+ }
+ ed = pipe->endpoint->edesc;
+ dev = pipe->device;
+ printf("ii %p: done=%d xfer=%p dev=%p vid=0x%04x pid=0x%04x addr=%d pipe=%p ep=0x%02x attr=0x%02x\n",
+ ii, DONE, ii->xfer, dev,
+ UGETW(dev->ddesc.idVendor),
+ UGETW(dev->ddesc.idProduct),
+ dev->address, pipe,
+ ed->bEndpointAddress, ed->bmAttributes);
+#undef DONE
+}
+
+void uhci_dump_iis(struct uhci_softc *sc);
+void
+uhci_dump_iis(struct uhci_softc *sc)
+{
+ uhci_intr_info_t *ii;
+
+ printf("intr_info list:\n");
+ for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list))
+ uhci_dump_ii(ii);
+}
+
+void iidump(void);
+void iidump(void) { uhci_dump_iis(thesc); }
+
+#endif
+
+/*
+ * This routine is executed periodically and simulates interrupts
+ * from the root controller interrupt pipe for port status change.
+ */
+void
+uhci_poll_hub(void *addr)
+{
+ usbd_xfer_handle xfer = addr;
+ usbd_pipe_handle pipe = xfer->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+ int s;
+ u_char *p;
+
+ DPRINTFN(20, ("uhci_poll_hub\n"));
+
+ usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer);
+
+ p = KERNADDR(&xfer->dmabuf, 0);
+ 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)
+ /* No change, try again in a while */
+ return;
+
+ xfer->actlen = 1;
+ xfer->status = USBD_NORMAL_COMPLETION;
+ s = splusb();
+ xfer->device->bus->intr_context++;
+ usb_transfer_complete(xfer);
+ xfer->device->bus->intr_context--;
+ splx(s);
+}
+
+void
+uhci_root_intr_done(usbd_xfer_handle xfer)
+{
+}
+
+void
+uhci_root_ctrl_done(usbd_xfer_handle xfer)
+{
+}
+
+/*
+ * Let the last QH loop back to the high speed control transfer QH.
+ * This is what intel calls "bandwidth reclamation" and improves
+ * USB performance a lot for some devices.
+ * If we are already looping, just count it.
+ */
+void
+uhci_add_loop(uhci_softc_t *sc) {
+#ifdef USB_DEBUG
+ if (uhcinoloop)
+ return;
+#endif
+ if (++sc->sc_loops == 1) {
+ DPRINTFN(5,("uhci_start_loop: add\n"));
+ /* Note, we don't loop back the soft pointer. */
+ sc->sc_last_qh->qh.qh_hlink =
+ htole32(sc->sc_hctl_start->physaddr | UHCI_PTR_QH);
+ }
+}
+
+void
+uhci_rem_loop(uhci_softc_t *sc) {
+#ifdef USB_DEBUG
+ if (uhcinoloop)
+ return;
+#endif
+ if (--sc->sc_loops == 0) {
+ DPRINTFN(5,("uhci_end_loop: remove\n"));
+ sc->sc_last_qh->qh.qh_hlink = htole32(UHCI_PTR_T);
+ }
+}
+
+/* Add high speed control QH, called at splusb(). */
+void
+uhci_add_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *eqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_add_ctrl: sqh=%p\n", sqh));
+ eqh = sc->sc_hctl_end;
+ sqh->hlink = eqh->hlink;
+ sqh->qh.qh_hlink = eqh->qh.qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
+ sc->sc_hctl_end = sqh;
+#ifdef UHCI_CTL_LOOP
+ uhci_add_loop(sc);
+#endif
+}
+
+/* Remove high speed control QH, called at splusb(). */
+void
+uhci_remove_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *pqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_remove_hs_ctrl: sqh=%p\n", sqh));
+#ifdef UHCI_CTL_LOOP
+ uhci_rem_loop(sc);
+#endif
+ /*
+ * The T bit should be set in the elink of the QH so that the HC
+ * doesn't follow the pointer. This condition may fail if the
+ * the transferred packet was short so that the QH still points
+ * at the last used TD.
+ * In this case we set the T bit and wait a little for the HC
+ * to stop looking at the TD.
+ */
+ if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ delay(UHCI_QH_REMOVE_DELAY);
+ }
+
+ pqh = uhci_find_prev_qh(sc->sc_hctl_start, sqh);
+ pqh->hlink = sqh->hlink;
+ pqh->qh.qh_hlink = sqh->qh.qh_hlink;
+ delay(UHCI_QH_REMOVE_DELAY);
+ if (sc->sc_hctl_end == sqh)
+ sc->sc_hctl_end = pqh;
+}
+
+/* Add low speed control QH, called at splusb(). */
+void
+uhci_add_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *eqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_add_ls_ctrl: sqh=%p\n", sqh));
+ eqh = sc->sc_lctl_end;
+ sqh->hlink = eqh->hlink;
+ sqh->qh.qh_hlink = eqh->qh.qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
+ sc->sc_lctl_end = sqh;
+}
+
+/* Remove low speed control QH, called at splusb(). */
+void
+uhci_remove_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *pqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_remove_ls_ctrl: sqh=%p\n", sqh));
+ /* See comment in uhci_remove_hs_ctrl() */
+ if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ delay(UHCI_QH_REMOVE_DELAY);
+ }
+ pqh = uhci_find_prev_qh(sc->sc_lctl_start, sqh);
+ pqh->hlink = sqh->hlink;
+ pqh->qh.qh_hlink = sqh->qh.qh_hlink;
+ delay(UHCI_QH_REMOVE_DELAY);
+ if (sc->sc_lctl_end == sqh)
+ sc->sc_lctl_end = pqh;
+}
+
+/* Add bulk QH, called at splusb(). */
+void
+uhci_add_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *eqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_add_bulk: sqh=%p\n", sqh));
+ eqh = sc->sc_bulk_end;
+ sqh->hlink = eqh->hlink;
+ sqh->qh.qh_hlink = eqh->qh.qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
+ sc->sc_bulk_end = sqh;
+ uhci_add_loop(sc);
+}
+
+/* Remove bulk QH, called at splusb(). */
+void
+uhci_remove_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *pqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh));
+ uhci_rem_loop(sc);
+ /* See comment in uhci_remove_hs_ctrl() */
+ if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ delay(UHCI_QH_REMOVE_DELAY);
+ }
+ pqh = uhci_find_prev_qh(sc->sc_bulk_start, sqh);
+ pqh->hlink = sqh->hlink;
+ pqh->qh.qh_hlink = sqh->qh.qh_hlink;
+ delay(UHCI_QH_REMOVE_DELAY);
+ if (sc->sc_bulk_end == sqh)
+ sc->sc_bulk_end = pqh;
+}
+
+Static int uhci_intr1(uhci_softc_t *);
+
+int
+uhci_intr(void *arg)
+{
+ uhci_softc_t *sc = arg;
+
+ if (sc->sc_dying)
+ return (0);
+
+ DPRINTFN(15,("uhci_intr: real interrupt\n"));
+ if (sc->sc_bus.use_polling) {
+#ifdef DIAGNOSTIC
+ printf("uhci_intr: ignored interrupt while polling\n");
+#endif
+ return (0);
+ }
+ return (uhci_intr1(sc));
+}
+
+int
+uhci_intr1(uhci_softc_t *sc)
+{
+
+ int status;
+ int ack;
+
+ /*
+ * It can happen that an interrupt will be delivered to
+ * us before the device has been fully attached and the
+ * softc struct has been configured. Usually this happens
+ * when kldloading the USB support as a module after the
+ * system has been booted. If we detect this condition,
+ * we need to squelch the unwanted interrupts until we're
+ * ready for them.
+ */
+ if (sc->sc_bus.bdev == NULL) {
+ UWRITE2(sc, UHCI_STS, 0xFFFF); /* ack pending interrupts */
+ uhci_run(sc, 0); /* stop the controller */
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+ return(0);
+ }
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 15) {
+ DPRINTF(("%s: uhci_intr1\n", USBDEVNAME(sc->sc_bus.bdev)));
+ uhci_dumpregs(sc);
+ }
+#endif
+ status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS;
+ if (status == 0) /* The interrupt was not for us. */
+ return (0);
+
+#if defined(DIAGNOSTIC) && defined(__NetBSD__)
+ if (sc->sc_suspend != PWR_RESUME)
+ printf("uhci_intr: suspended sts=0x%x\n", status);
+#endif
+
+ if (sc->sc_suspend != PWR_RESUME) {
+ printf("%s: interrupt while not operating ignored\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ UWRITE2(sc, UHCI_STS, status); /* acknowledge the ints */
+ return (0);
+ }
+
+ ack = 0;
+ if (status & UHCI_STS_USBINT)
+ ack |= UHCI_STS_USBINT;
+ if (status & UHCI_STS_USBEI)
+ ack |= UHCI_STS_USBEI;
+ if (status & UHCI_STS_RD) {
+ ack |= UHCI_STS_RD;
+#ifdef USB_DEBUG
+ printf("%s: resume detect\n", USBDEVNAME(sc->sc_bus.bdev));
+#endif
+ }
+ if (status & UHCI_STS_HSE) {
+ ack |= UHCI_STS_HSE;
+ printf("%s: host system error\n", USBDEVNAME(sc->sc_bus.bdev));
+ }
+ if (status & UHCI_STS_HCPE) {
+ ack |= UHCI_STS_HCPE;
+ printf("%s: host controller process error\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+ }
+ if (status & UHCI_STS_HCH) {
+ /* no acknowledge needed */
+ if (!sc->sc_dying) {
+ printf("%s: host controller halted\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+#ifdef USB_DEBUG
+ uhci_dump_all(sc);
+#endif
+ }
+ sc->sc_dying = 1;
+ }
+
+ if (!ack)
+ return (0); /* nothing to acknowledge */
+ UWRITE2(sc, UHCI_STS, ack); /* acknowledge the ints */
+
+ sc->sc_bus.no_intrs++;
+ usb_schedsoftintr(&sc->sc_bus);
+
+ DPRINTFN(15, ("%s: uhci_intr: exit\n", USBDEVNAME(sc->sc_bus.bdev)));
+
+ return (1);
+}
+
+void
+uhci_softintr(void *v)
+{
+ uhci_softc_t *sc = v;
+ uhci_intr_info_t *ii, *nextii;
+
+ DPRINTFN(10,("%s: uhci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev),
+ sc->sc_bus.intr_context));
+
+ sc->sc_bus.intr_context++;
+
+ /*
+ * 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.
+ */
+ LIST_FOREACH_SAFE(ii, &sc->sc_intrhead, list, nextii)
+ uhci_check_intr(sc, ii);
+
+#ifdef USB_USE_SOFTINTR
+ if (sc->sc_softwake) {
+ sc->sc_softwake = 0;
+ wakeup(&sc->sc_softwake);
+ }
+#endif /* USB_USE_SOFTINTR */
+
+ sc->sc_bus.intr_context--;
+}
+
+/* Check for an interrupt. */
+void
+uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii)
+{
+ uhci_soft_td_t *std, *lstd;
+ u_int32_t status;
+
+ DPRINTFN(15, ("uhci_check_intr: ii=%p\n", ii));
+#ifdef DIAGNOSTIC
+ if (ii == NULL) {
+ printf("uhci_check_intr: no ii? %p\n", ii);
+ return;
+ }
+#endif
+ if (ii->xfer->status == USBD_CANCELLED ||
+ ii->xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("uhci_check_intr: aborted xfer=%p\n", ii->xfer));
+ return;
+ }
+
+ if (ii->stdstart == NULL)
+ return;
+ lstd = ii->stdend;
+#ifdef DIAGNOSTIC
+ if (lstd == NULL) {
+ printf("uhci_check_intr: std==0\n");
+ return;
+ }
+#endif
+ /*
+ * If the last TD is still active we need to check whether there
+ * is an error somewhere in the middle, or whether there was a
+ * short packet (SPD and not ACTIVE).
+ */
+ if (le32toh(lstd->td.td_status) & UHCI_TD_ACTIVE) {
+ DPRINTFN(12, ("uhci_check_intr: active ii=%p\n", ii));
+ for (std = ii->stdstart; std != lstd; std = std->link.std) {
+ status = le32toh(std->td.td_status);
+ /* If there's an active TD the xfer isn't done. */
+ if (status & UHCI_TD_ACTIVE)
+ break;
+ /* Any kind of error makes the xfer done. */
+ if (status & UHCI_TD_STALLED)
+ goto done;
+ /* We want short packets, and it is short: it's done */
+ if ((status & UHCI_TD_SPD) &&
+ UHCI_TD_GET_ACTLEN(status) <
+ UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token)))
+ goto done;
+ }
+ DPRINTFN(12, ("uhci_check_intr: ii=%p std=%p still active\n",
+ ii, ii->stdstart));
+ return;
+ }
+ done:
+ DPRINTFN(12, ("uhci_check_intr: ii=%p done\n", ii));
+ usb_uncallout(ii->xfer->timeout_handle, uhci_timeout, ii);
+ uhci_idone(ii);
+}
+
+/* Called at splusb() */
+void
+uhci_idone(uhci_intr_info_t *ii)
+{
+ usbd_xfer_handle xfer = ii->xfer;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_soft_td_t *std;
+ u_int32_t status = 0, nstatus;
+ int actlen;
+
+ DPRINTFN(12, ("uhci_idone: ii=%p\n", ii));
+#ifdef DIAGNOSTIC
+ {
+ int s = splhigh();
+ if (ii->isdone) {
+ splx(s);
+#ifdef USB_DEBUG
+ printf("uhci_idone: ii is done!\n ");
+ uhci_dump_ii(ii);
+#else
+ printf("uhci_idone: ii=%p is done!\n", ii);
+#endif
+ return;
+ }
+ ii->isdone = 1;
+ splx(s);
+ }
+#endif
+
+ if (xfer->nframes != 0) {
+ /* Isoc transfer, do things differently. */
+ uhci_soft_td_t **stds = upipe->u.iso.stds;
+ int i, n, nframes, len;
+
+ DPRINTFN(5,("uhci_idone: ii=%p isoc ready\n", ii));
+
+ nframes = xfer->nframes;
+ actlen = 0;
+ n = UXFER(xfer)->curframe;
+ for (i = 0; i < nframes; i++) {
+ std = stds[n];
+#ifdef USB_DEBUG
+ if (uhcidebug > 5) {
+ DPRINTFN(-1,("uhci_idone: isoc TD %d\n", i));
+ uhci_dump_td(std);
+ }
+#endif
+ if (++n >= UHCI_VFRAMELIST_COUNT)
+ n = 0;
+ status = le32toh(std->td.td_status);
+ len = UHCI_TD_GET_ACTLEN(status);
+ xfer->frlengths[i] = len;
+ actlen += len;
+ }
+ upipe->u.iso.inuse -= nframes;
+ xfer->actlen = actlen;
+ xfer->status = USBD_NORMAL_COMPLETION;
+ goto end;
+ }
+
+#ifdef USB_DEBUG
+ DPRINTFN(10, ("uhci_idone: ii=%p, xfer=%p, pipe=%p ready\n",
+ ii, xfer, upipe));
+ if (uhcidebug > 10)
+ uhci_dump_tds(ii->stdstart);
+#endif
+
+ /* The transfer is done, compute actual length and status. */
+ actlen = 0;
+ for (std = ii->stdstart; std != NULL; std = std->link.std) {
+ nstatus = le32toh(std->td.td_status);
+ if (nstatus & UHCI_TD_ACTIVE)
+ break;
+
+ status = nstatus;
+ if (UHCI_TD_GET_PID(le32toh(std->td.td_token)) !=
+ UHCI_TD_PID_SETUP)
+ actlen += UHCI_TD_GET_ACTLEN(status);
+ else {
+ /*
+ * UHCI will report CRCTO in addition to a STALL or NAK
+ * for a SETUP transaction. See section 3.2.2, "TD
+ * CONTROL AND STATUS".
+ */
+ if (status & (UHCI_TD_STALLED | UHCI_TD_NAK))
+ status &= ~UHCI_TD_CRCTO;
+ }
+ }
+ /* If there are left over TDs we need to update the toggle. */
+ if (std != NULL)
+ upipe->nexttoggle = UHCI_TD_GET_DT(le32toh(std->td.td_token));
+
+ status &= UHCI_TD_ERROR;
+ DPRINTFN(10, ("uhci_idone: actlen=%d, status=0x%x\n",
+ actlen, status));
+ xfer->actlen = actlen;
+ if (status != 0) {
+#ifdef USB_DEBUG
+ char sbuf[128];
+
+ bitmask_snprintf((u_int32_t)status,
+ "\20\22BITSTUFF\23CRCTO\24NAK\25"
+ "BABBLE\26DBUFFER\27STALLED\30ACTIVE",
+ sbuf, sizeof(sbuf));
+
+ DPRINTFN((status == UHCI_TD_STALLED)*10,
+ ("uhci_idone: error, addr=%d, endpt=0x%02x, "
+ "status 0x%s\n",
+ xfer->pipe->device->address,
+ xfer->pipe->endpoint->edesc->bEndpointAddress,
+ sbuf));
+#endif
+
+ if (status == UHCI_TD_STALLED)
+ xfer->status = USBD_STALLED;
+ else
+ xfer->status = USBD_IOERROR; /* more info XXX */
+ } else {
+ xfer->status = USBD_NORMAL_COMPLETION;
+ }
+
+ end:
+ usb_transfer_complete(xfer);
+ DPRINTFN(12, ("uhci_idone: ii=%p done\n", ii));
+}
+
+/*
+ * Called when a request does not complete.
+ */
+void
+uhci_timeout(void *addr)
+{
+ uhci_intr_info_t *ii = addr;
+ struct uhci_xfer *uxfer = UXFER(ii->xfer);
+ struct uhci_pipe *upipe = (struct uhci_pipe *)uxfer->xfer.pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
+
+ DPRINTF(("uhci_timeout: uxfer=%p\n", uxfer));
+
+ if (sc->sc_dying) {
+ uhci_abort_xfer(&uxfer->xfer, USBD_TIMEOUT);
+ return;
+ }
+
+ /* Execute the abort in a process context. */
+ usb_init_task(&uxfer->abort_task, uhci_timeout_task, ii->xfer);
+ usb_add_task(uxfer->xfer.pipe->device, &uxfer->abort_task);
+}
+
+void
+uhci_timeout_task(void *addr)
+{
+ usbd_xfer_handle xfer = addr;
+ int s;
+
+ DPRINTF(("uhci_timeout_task: xfer=%p\n", xfer));
+
+ s = splusb();
+ uhci_abort_xfer(xfer, USBD_TIMEOUT);
+ splx(s);
+}
+
+/*
+ * Wait here until controller claims to have an interrupt.
+ * Then call uhci_intr and return. Use timeout to avoid waiting
+ * too long.
+ * Only used during boot when interrupts are not enabled yet.
+ */
+void
+uhci_waitintr(uhci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ int timo = xfer->timeout;
+ uhci_intr_info_t *ii;
+
+ DPRINTFN(10,("uhci_waitintr: timeout = %dms\n", timo));
+
+ xfer->status = USBD_IN_PROGRESS;
+ for (; timo >= 0; timo--) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ DPRINTFN(20,("uhci_waitintr: 0x%04x\n", UREAD2(sc, UHCI_STS)));
+ if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT)
+ uhci_intr1(sc);
+ if (xfer->status != USBD_IN_PROGRESS)
+ return;
+ }
+
+ /* Timeout */
+ DPRINTF(("uhci_waitintr: timeout\n"));
+ for (ii = LIST_FIRST(&sc->sc_intrhead);
+ ii != NULL && ii->xfer != xfer;
+ ii = LIST_NEXT(ii, list))
+ ;
+#ifdef DIAGNOSTIC
+ if (ii == NULL)
+ panic("uhci_waitintr: lost intr_info");
+#endif
+ uhci_idone(ii);
+}
+
+void
+uhci_poll(struct usbd_bus *bus)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)bus;
+
+ if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT)
+ uhci_intr1(sc);
+}
+
+void
+uhci_reset(uhci_softc_t *sc)
+{
+ 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++)
+ usb_delay_ms(&sc->sc_bus, 1);
+ if (n >= UHCI_RESET_TIMEOUT)
+ printf("%s: controller did not reset\n",
+ USBDEVNAME(sc->sc_bus.bdev));
+}
+
+usbd_status
+uhci_run(uhci_softc_t *sc, int run)
+{
+ int s, n, running;
+ u_int16_t cmd;
+
+ run = run != 0;
+ s = splhardusb();
+ DPRINTF(("uhci_run: setting run=%d\n", run));
+ cmd = UREAD2(sc, UHCI_CMD);
+ if (run)
+ cmd |= UHCI_CMD_RS;
+ else
+ cmd &= ~UHCI_CMD_RS;
+ UHCICMD(sc, cmd);
+ for(n = 0; n < 10; n++) {
+ running = !(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH);
+ /* return when we've entered the state we want */
+ if (run == running) {
+ splx(s);
+ DPRINTF(("uhci_run: done cmd=0x%x sts=0x%x\n",
+ UREAD2(sc, UHCI_CMD), UREAD2(sc, UHCI_STS)));
+ return (USBD_NORMAL_COMPLETION);
+ }
+ usb_delay_ms(&sc->sc_bus, 1);
+ }
+ splx(s);
+ printf("%s: cannot %s\n", USBDEVNAME(sc->sc_bus.bdev),
+ run ? "start" : "stop");
+ return (USBD_IOERROR);
+}
+
+/*
+ * 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 allocated chunk.
+ */
+
+uhci_soft_td_t *
+uhci_alloc_std(uhci_softc_t *sc)
+{
+ uhci_soft_td_t *std;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freetds == NULL) {
+ DPRINTFN(2,("uhci_alloc_std: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, UHCI_STD_SIZE * UHCI_STD_CHUNK,
+ UHCI_TD_ALIGN, &dma);
+ if (err)
+ return (0);
+ for(i = 0; i < UHCI_STD_CHUNK; i++) {
+ offs = i * UHCI_STD_SIZE;
+ std = KERNADDR(&dma, offs);
+ std->physaddr = DMAADDR(&dma, offs);
+ std->link.std = sc->sc_freetds;
+ sc->sc_freetds = std;
+ }
+ }
+ std = sc->sc_freetds;
+ sc->sc_freetds = std->link.std;
+ memset(&std->td, 0, sizeof(uhci_td_t));
+ return std;
+}
+
+void
+uhci_free_std(uhci_softc_t *sc, uhci_soft_td_t *std)
+{
+#ifdef DIAGNOSTIC
+#define TD_IS_FREE 0x12345678
+ if (le32toh(std->td.td_token) == TD_IS_FREE) {
+ printf("uhci_free_std: freeing free TD %p\n", std);
+ return;
+ }
+ std->td.td_token = htole32(TD_IS_FREE);
+#endif
+ std->link.std = sc->sc_freetds;
+ sc->sc_freetds = std;
+}
+
+uhci_soft_qh_t *
+uhci_alloc_sqh(uhci_softc_t *sc)
+{
+ uhci_soft_qh_t *sqh;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeqhs == NULL) {
+ DPRINTFN(2, ("uhci_alloc_sqh: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, UHCI_SQH_SIZE * UHCI_SQH_CHUNK,
+ UHCI_QH_ALIGN, &dma);
+ if (err)
+ return (0);
+ for(i = 0; i < UHCI_SQH_CHUNK; i++) {
+ offs = i * UHCI_SQH_SIZE;
+ sqh = KERNADDR(&dma, offs);
+ sqh->physaddr = DMAADDR(&dma, offs);
+ sqh->hlink = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+ }
+ }
+ sqh = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh->hlink;
+ memset(&sqh->qh, 0, sizeof(uhci_qh_t));
+ return (sqh);
+}
+
+void
+uhci_free_sqh(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ sqh->hlink = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+}
+
+void
+uhci_free_std_chain(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->link.std;
+ uhci_free_std(sc, std);
+ }
+}
+
+usbd_status
+uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len,
+ int rd, u_int16_t flags, usb_dma_t *dma,
+ uhci_soft_td_t **sp, uhci_soft_td_t **ep)
+{
+ uhci_soft_td_t *p, *lastp;
+ uhci_physaddr_t lastlink;
+ int i, ntd, l, tog, maxp;
+ u_int32_t status;
+ int addr = upipe->pipe.device->address;
+ int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+
+ DPRINTFN(8, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d speed=%d "
+ "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len,
+ upipe->pipe.device->speed, flags));
+ 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;
+ if ((flags & USBD_FORCE_SHORT_XFER) && len % maxp == 0)
+ ntd++;
+ DPRINTFN(10, ("uhci_alloc_std_chain: maxp=%d ntd=%d\n", maxp, ntd));
+ if (ntd == 0) {
+ *sp = *ep = 0;
+ DPRINTFN(-1,("uhci_alloc_std_chain: ntd=0\n"));
+ return (USBD_NORMAL_COMPLETION);
+ }
+ tog = upipe->nexttoggle;
+ if (ntd % 2 == 0)
+ tog ^= 1;
+ upipe->nexttoggle = tog ^ 1;
+ lastp = NULL;
+ lastlink = UHCI_PTR_T;
+ ntd--;
+ status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE);
+ if (upipe->pipe.device->speed == USB_SPEED_LOW)
+ status |= UHCI_TD_LS;
+ if (flags & USBD_SHORT_XFER_OK)
+ status |= UHCI_TD_SPD;
+ for (i = ntd; i >= 0; i--) {
+ p = uhci_alloc_std(sc);
+ if (p == NULL) {
+ uhci_free_std_chain(sc, lastp, NULL);
+ return (USBD_NOMEM);
+ }
+ p->link.std = lastp;
+ p->td.td_link = htole32(lastlink | UHCI_PTR_VF | UHCI_PTR_TD);
+ lastp = p;
+ lastlink = p->physaddr;
+ p->td.td_status = htole32(status);
+ if (i == ntd) {
+ /* last TD */
+ l = len % maxp;
+ if (l == 0 && !(flags & USBD_FORCE_SHORT_XFER))
+ l = maxp;
+ *ep = p;
+ } else
+ l = maxp;
+ p->td.td_token =
+ htole32(rd ? UHCI_TD_IN (l, endpt, addr, tog) :
+ UHCI_TD_OUT(l, endpt, addr, tog));
+ p->td.td_buffer = htole32(DMAADDR(dma, i * maxp));
+ tog ^= 1;
+ }
+ *sp = lastp;
+ DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n",
+ upipe->nexttoggle));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+uhci_device_clear_toggle(usbd_pipe_handle pipe)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ upipe->nexttoggle = 0;
+}
+
+void
+uhci_noop(usbd_pipe_handle pipe)
+{
+}
+
+usbd_status
+uhci_device_bulk_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+uhci_device_bulk_start(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_soft_td_t *data, *dataend;
+ uhci_soft_qh_t *sqh;
+ usbd_status err;
+ int len, isread, endpt;
+ int s;
+
+ DPRINTFN(3, ("uhci_device_bulk_start: xfer=%p len=%d flags=%d ii=%p\n",
+ xfer, xfer->length, xfer->flags, ii));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("uhci_device_bulk_transfer: a request");
+#endif
+
+ len = xfer->length;
+ endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sqh = upipe->u.bulk.sqh;
+
+ upipe->u.bulk.isread = isread;
+ upipe->u.bulk.length = len;
+
+ err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags,
+ &xfer->dmabuf, &data, &dataend);
+ if (err)
+ return (err);
+ dataend->td.td_status |= htole32(UHCI_TD_IOC);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 8) {
+ DPRINTF(("uhci_device_bulk_transfer: data(1)\n"));
+ uhci_dump_tds(data);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ ii->xfer = xfer;
+ ii->stdstart = data;
+ ii->stdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone) {
+ printf("uhci_device_bulk_transfer: not done, ii=%p\n", ii);
+ }
+ ii->isdone = 0;
+#endif
+
+ sqh->elink = data;
+ sqh->qh.qh_elink = htole32(data->physaddr | UHCI_PTR_TD);
+
+ s = splusb();
+ uhci_add_bulk(sc, sqh);
+ uhci_add_intr_info(sc, ii);
+
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ uhci_timeout, ii);
+ }
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_bulk_transfer: data(2)\n"));
+ uhci_dump_tds(data);
+ }
+#endif
+
+ if (sc->sc_bus.use_polling)
+ uhci_waitintr(sc, xfer);
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a device bulk request. */
+void
+uhci_device_bulk_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("uhci_device_bulk_abort:\n"));
+ uhci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/*
+ * Abort a device request.
+ * If this routine is called at splusb() it guarantees that the request
+ * will be removed from the hardware scheduling and that the callback
+ * for it will be called with USBD_CANCELLED status.
+ * It's impossible to guarantee that the requested transfer will not
+ * have happened since the hardware runs concurrently.
+ * If the transaction has already happened we rely on the ordinary
+ * interrupt processing to process it.
+ */
+void
+uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
+ uhci_soft_td_t *std;
+ int s;
+
+ DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status));
+
+ if (sc->sc_dying) {
+ /* If we're dying, just do the software part. */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ usb_uncallout(xfer->timeout_handle, uhci_timeout, xfer);
+ usb_transfer_complete(xfer);
+ splx(s);
+ return;
+ }
+
+ if (xfer->device->bus->intr_context || !curproc)
+ panic("uhci_abort_xfer: not in process context");
+
+ /*
+ * Step 1: Make interrupt routine and hardware ignore xfer.
+ */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ usb_uncallout(xfer->timeout_handle, uhci_timeout, ii);
+ DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii));
+ for (std = ii->stdstart; std != NULL; std = std->link.std)
+ std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
+ splx(s);
+
+ /*
+ * Step 2: Wait until we know hardware has finished any possible
+ * use of the xfer. Also make sure the soft interrupt routine
+ * has run.
+ */
+ usb_delay_ms(upipe->pipe.device->bus, 2); /* Hardware finishes in 1ms */
+ s = splusb();
+#ifdef USB_USE_SOFTINTR
+ sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+ usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+ DPRINTFN(1,("uhci_abort_xfer: tsleep\n"));
+ tsleep(&sc->sc_softwake, PZERO, "uhciab", 0);
+#endif /* USB_USE_SOFTINTR */
+ splx(s);
+
+ /*
+ * Step 3: Execute callback.
+ */
+ xfer->hcpriv = ii;
+
+ DPRINTFN(1,("uhci_abort_xfer: callback\n"));
+ s = splusb();
+#ifdef DIAGNOSTIC
+ ii->isdone = 1;
+#endif
+ usb_transfer_complete(xfer);
+ splx(s);
+}
+
+/* Close a device bulk pipe. */
+void
+uhci_device_bulk_close(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);
+}
+
+usbd_status
+uhci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+uhci_device_ctrl_start(usbd_xfer_handle xfer)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)xfer->pipe->device->bus;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ panic("uhci_device_ctrl_transfer: not a request");
+#endif
+
+ err = uhci_device_request(xfer);
+ if (err)
+ return (err);
+
+ if (sc->sc_bus.use_polling)
+ uhci_waitintr(sc, xfer);
+ return (USBD_IN_PROGRESS);
+}
+
+usbd_status
+uhci_device_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+uhci_device_intr_start(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_soft_td_t *data, *dataend;
+ uhci_soft_qh_t *sqh;
+ usbd_status err;
+ int isread, endpt;
+ int i, s;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ DPRINTFN(3,("uhci_device_intr_transfer: xfer=%p len=%d flags=%d\n",
+ xfer, xfer->length, xfer->flags));
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("uhci_device_intr_transfer: a request");
+#endif
+
+ endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sqh = upipe->u.bulk.sqh;
+
+ upipe->u.intr.isread = isread;
+
+ err = uhci_alloc_std_chain(upipe, sc, xfer->length, isread,
+ xfer->flags, &xfer->dmabuf, &data,
+ &dataend);
+ if (err)
+ return (err);
+ dataend->td.td_status |= htole32(UHCI_TD_IOC);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_intr_transfer: data(1)\n"));
+ uhci_dump_tds(data);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ s = splusb();
+ /* Set up interrupt info. */
+ ii->xfer = xfer;
+ ii->stdstart = data;
+ ii->stdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone) {
+ printf("uhci_device_intr_transfer: not done, ii=%p\n", ii);
+ }
+ 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->elink = data;
+ sqh->qh.qh_elink = htole32(data->physaddr | UHCI_PTR_TD);
+ }
+ uhci_add_intr_info(sc, ii);
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_intr_transfer: data(2)\n"));
+ uhci_dump_tds(data);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a device control request. */
+void
+uhci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("uhci_device_ctrl_abort:\n"));
+ uhci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device control pipe. */
+void
+uhci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+}
+
+/* Abort a device interrupt request. */
+void
+uhci_device_intr_abort(usbd_xfer_handle xfer)
+{
+ DPRINTFN(1,("uhci_device_intr_abort: xfer=%p\n", xfer));
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTFN(1,("uhci_device_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ uhci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device interrupt pipe. */
+void
+uhci_device_intr_close(usbd_pipe_handle pipe)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+ int i, npoll;
+ int s;
+
+ /* Unlink descriptors from controller data structures. */
+ npoll = upipe->u.intr.npoll;
+ s = splusb();
+ for (i = 0; i < npoll; i++)
+ uhci_remove_intr(sc, upipe->u.intr.qhs[i]);
+ splx(s);
+
+ /*
+ * We now have to wait for any activity on the physical
+ * descriptors to stop.
+ */
+ usb_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_USBHC);
+
+ /* XXX free other resources */
+}
+
+usbd_status
+uhci_device_request(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ usb_device_request_t *req = &xfer->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 = &UXFER(xfer)->iinfo;
+ uhci_soft_td_t *setup, *data, *stat, *next, *dataend;
+ uhci_soft_qh_t *sqh;
+ int len;
+ u_int32_t ls;
+ usbd_status err;
+ int isread;
+ int s;
+
+ DPRINTFN(3,("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->speed == USB_SPEED_LOW ? 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;
+
+ /* Set up data transaction */
+ if (len != 0) {
+ upipe->nexttoggle = 1;
+ err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags,
+ &xfer->dmabuf, &data, &dataend);
+ if (err)
+ return (err);
+ next = data;
+ dataend->link.std = stat;
+ dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_VF | UHCI_PTR_TD);
+ } else {
+ next = stat;
+ }
+ upipe->u.ctl.length = len;
+
+ memcpy(KERNADDR(&upipe->u.ctl.reqdma, 0), req, sizeof *req);
+
+ setup->link.std = next;
+ setup->td.td_link = htole32(next->physaddr | UHCI_PTR_VF | UHCI_PTR_TD);
+ setup->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
+ UHCI_TD_ACTIVE);
+ setup->td.td_token = htole32(UHCI_TD_SETUP(sizeof *req, endpt, addr));
+ setup->td.td_buffer = htole32(DMAADDR(&upipe->u.ctl.reqdma, 0));
+
+ stat->link.std = NULL;
+ stat->td.td_link = htole32(UHCI_PTR_T);
+ stat->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
+ UHCI_TD_ACTIVE | UHCI_TD_IOC);
+ stat->td.td_token =
+ htole32(isread ? UHCI_TD_OUT(0, endpt, addr, 1) :
+ UHCI_TD_IN (0, endpt, addr, 1));
+ stat->td.td_buffer = htole32(0);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_request: before transfer\n"));
+ uhci_dump_tds(setup);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ ii->xfer = xfer;
+ ii->stdstart = setup;
+ ii->stdend = stat;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone) {
+ printf("uhci_device_request: not done, ii=%p\n", ii);
+ }
+ ii->isdone = 0;
+#endif
+
+ sqh->elink = setup;
+ sqh->qh.qh_elink = htole32(setup->physaddr | UHCI_PTR_TD);
+
+ s = splusb();
+ if (dev->speed == USB_SPEED_LOW)
+ uhci_add_ls_ctrl(sc, sqh);
+ else
+ uhci_add_hs_ctrl(sc, sqh);
+ uhci_add_intr_info(sc, ii);
+#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;
+ DPRINTF(("uhci_enter_ctl_q: follow from [0]\n"));
+ for (std = sc->sc_vframes[0].htd, link = 0;
+ (link & UHCI_PTR_QH) == 0;
+ std = std->link.std) {
+ link = le32toh(std->td.td_link);
+ uhci_dump_td(std);
+ }
+ sxqh = (uhci_soft_qh_t *)std;
+ uhci_dump_qh(sxqh);
+ for (xqh = sxqh;
+ xqh != NULL;
+ xqh = (maxqh++ == 5 || xqh->hlink == sxqh ||
+ xqh->hlink == xqh ? NULL : xqh->hlink)) {
+ uhci_dump_qh(xqh);
+ }
+ DPRINTF(("Enqueued QH:\n"));
+ uhci_dump_qh(sqh);
+ uhci_dump_tds(sqh->elink);
+ }
+#endif
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ uhci_timeout, ii);
+ }
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+uhci_device_isoc_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ DPRINTFN(5,("uhci_device_isoc_transfer: xfer=%p\n", xfer));
+
+ /* Put it on our queue, */
+ err = usb_insert_transfer(xfer);
+
+ /* bail out on error, */
+ if (err && err != USBD_IN_PROGRESS)
+ return (err);
+
+ /* XXX should check inuse here */
+
+ /* insert into schedule, */
+ uhci_device_isoc_enter(xfer);
+
+ /* and start if the pipe wasn't running */
+ if (!err)
+ uhci_device_isoc_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
+
+ return (err);
+}
+
+void
+uhci_device_isoc_enter(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ struct iso *iso = &upipe->u.iso;
+ uhci_soft_td_t *std;
+ u_int32_t buf, len, status;
+ int s, i, next, nframes;
+
+ DPRINTFN(5,("uhci_device_isoc_enter: used=%d next=%d xfer=%p "
+ "nframes=%d\n",
+ iso->inuse, iso->next, xfer, xfer->nframes));
+
+ if (sc->sc_dying)
+ return;
+
+ if (xfer->status == USBD_IN_PROGRESS) {
+ /* This request has already been entered into the frame list */
+ printf("uhci_device_isoc_enter: xfer=%p in frame list\n", xfer);
+ /* XXX */
+ }
+
+#ifdef DIAGNOSTIC
+ if (iso->inuse >= UHCI_VFRAMELIST_COUNT)
+ printf("uhci_device_isoc_enter: overflow!\n");
+#endif
+
+ next = iso->next;
+ if (next == -1) {
+ /* Not in use yet, schedule it a few frames ahead. */
+ next = (UREAD2(sc, UHCI_FRNUM) + 3) % UHCI_VFRAMELIST_COUNT;
+ DPRINTFN(2,("uhci_device_isoc_enter: start next=%d\n", next));
+ }
+
+ xfer->status = USBD_IN_PROGRESS;
+ UXFER(xfer)->curframe = next;
+
+ buf = DMAADDR(&xfer->dmabuf, 0);
+ status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) |
+ UHCI_TD_ACTIVE |
+ UHCI_TD_IOS);
+ nframes = xfer->nframes;
+ s = splusb();
+ for (i = 0; i < nframes; i++) {
+ std = iso->stds[next];
+ if (++next >= UHCI_VFRAMELIST_COUNT)
+ next = 0;
+ len = xfer->frlengths[i];
+ std->td.td_buffer = htole32(buf);
+ if (i == nframes - 1)
+ status |= UHCI_TD_IOC;
+ std->td.td_status = htole32(status);
+ std->td.td_token &= htole32(~UHCI_TD_MAXLEN_MASK);
+ std->td.td_token |= htole32(UHCI_TD_SET_MAXLEN(len));
+#ifdef USB_DEBUG
+ if (uhcidebug > 5) {
+ DPRINTFN(5,("uhci_device_isoc_enter: TD %d\n", i));
+ uhci_dump_td(std);
+ }
+#endif
+ buf += len;
+ }
+ iso->next = next;
+ iso->inuse += xfer->nframes;
+
+ splx(s);
+}
+
+usbd_status
+uhci_device_isoc_start(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_soft_td_t *end;
+ int s, i;
+
+ DPRINTFN(5,("uhci_device_isoc_start: xfer=%p\n", xfer));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->status != USBD_IN_PROGRESS)
+ printf("uhci_device_isoc_start: not in progress %p\n", xfer);
+#endif
+
+ /* Find the last TD */
+ i = UXFER(xfer)->curframe + xfer->nframes;
+ if (i >= UHCI_VFRAMELIST_COUNT)
+ i -= UHCI_VFRAMELIST_COUNT;
+ end = upipe->u.iso.stds[i];
+
+#ifdef DIAGNOSTIC
+ if (end == NULL) {
+ printf("uhci_device_isoc_start: end == NULL\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ s = splusb();
+
+ /* Set up interrupt info. */
+ ii->xfer = xfer;
+ ii->stdstart = end;
+ ii->stdend = end;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone)
+ printf("uhci_device_isoc_start: not done, ii=%p\n", ii);
+ ii->isdone = 0;
+#endif
+ uhci_add_intr_info(sc, ii);
+
+ splx(s);
+
+ return (USBD_IN_PROGRESS);
+}
+
+void
+uhci_device_isoc_abort(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_soft_td_t **stds = upipe->u.iso.stds;
+ uhci_soft_td_t *std;
+ int i, n, s, nframes, maxlen, len;
+
+ s = splusb();
+
+ /* Transfer is already done. */
+ if (xfer->status != USBD_NOT_STARTED &&
+ xfer->status != USBD_IN_PROGRESS) {
+ splx(s);
+ return;
+ }
+
+ /* Give xfer the requested abort code. */
+ xfer->status = USBD_CANCELLED;
+
+ /* make hardware ignore it, */
+ nframes = xfer->nframes;
+ n = UXFER(xfer)->curframe;
+ maxlen = 0;
+ for (i = 0; i < nframes; i++) {
+ std = stds[n];
+ std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
+ len = UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token));
+ if (len > maxlen)
+ maxlen = len;
+ if (++n >= UHCI_VFRAMELIST_COUNT)
+ n = 0;
+ }
+
+ /* and wait until we are sure the hardware has finished. */
+ delay(maxlen);
+
+#ifdef DIAGNOSTIC
+ UXFER(xfer)->iinfo.isdone = 1;
+#endif
+ /* Run callback and remove from interrupt list. */
+ usb_transfer_complete(xfer);
+
+ splx(s);
+}
+
+void
+uhci_device_isoc_close(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_soft_td_t *std, *vstd;
+ struct iso *iso;
+ int i, s;
+
+ /*
+ * Make sure all TDs are marked as inactive.
+ * Wait for completion.
+ * Unschedule.
+ * Deallocate.
+ */
+ iso = &upipe->u.iso;
+
+ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++)
+ iso->stds[i]->td.td_status &= htole32(~UHCI_TD_ACTIVE);
+ usb_delay_ms(&sc->sc_bus, 2); /* wait for completion */
+
+ s = splusb();
+ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
+ std = iso->stds[i];
+ for (vstd = sc->sc_vframes[i].htd;
+ vstd != NULL && vstd->link.std != std;
+ vstd = vstd->link.std)
+ ;
+ if (vstd == NULL) {
+ /*panic*/
+ printf("uhci_device_isoc_close: %p not found\n", std);
+ splx(s);
+ return;
+ }
+ vstd->link = std->link;
+ vstd->td.td_link = std->td.td_link;
+ uhci_free_std(sc, std);
+ }
+ splx(s);
+
+ free(iso->stds, M_USBHC);
+}
+
+usbd_status
+uhci_setup_isoc(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;
+ int addr = upipe->pipe.device->address;
+ int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+ int rd = UE_GET_DIR(endpt) == UE_DIR_IN;
+ uhci_soft_td_t *std, *vstd;
+ u_int32_t token;
+ struct iso *iso;
+ int i, s;
+
+ iso = &upipe->u.iso;
+ iso->stds = malloc(UHCI_VFRAMELIST_COUNT * sizeof (uhci_soft_td_t *),
+ M_USBHC, M_WAITOK);
+
+ token = rd ? UHCI_TD_IN (0, endpt, addr, 0) :
+ UHCI_TD_OUT(0, endpt, addr, 0);
+
+ /* Allocate the TDs and mark as inactive; */
+ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
+ std = uhci_alloc_std(sc);
+ if (std == 0)
+ goto bad;
+ std->td.td_status = htole32(UHCI_TD_IOS); /* iso, inactive */
+ std->td.td_token = htole32(token);
+ iso->stds[i] = std;
+ }
+
+ /* Insert TDs into schedule. */
+ s = splusb();
+ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
+ std = iso->stds[i];
+ vstd = sc->sc_vframes[i].htd;
+ std->link = vstd->link;
+ std->td.td_link = vstd->td.td_link;
+ vstd->link.std = std;
+ vstd->td.td_link = htole32(std->physaddr | UHCI_PTR_TD);
+ }
+ splx(s);
+
+ iso->next = -1;
+ iso->inuse = 0;
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ while (--i >= 0)
+ uhci_free_std(sc, iso->stds[i]);
+ free(iso->stds, M_USBHC);
+ return (USBD_NOMEM);
+}
+
+void
+uhci_device_isoc_done(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+
+ DPRINTFN(4, ("uhci_isoc_done: length=%d\n", xfer->actlen));
+
+ if (ii->xfer != xfer)
+ /* Not on interrupt list, ignore it. */
+ return;
+
+ if (!uhci_active_intr_info(ii))
+ return;
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("uhci_device_isoc_done: xfer=%p not busy 0x%08x\n",
+ xfer, xfer->busy_free);
+ return;
+ }
+
+ if (ii->stdend == NULL) {
+ printf("uhci_device_isoc_done: xfer=%p stdend==NULL\n", xfer);
+#ifdef USB_DEBUG
+ uhci_dump_ii(ii);
+#endif
+ return;
+ }
+#endif
+
+ /* Turn off the interrupt since it is active even if the TD is not. */
+ ii->stdend->td.td_status &= htole32(~UHCI_TD_IOC);
+
+ uhci_del_intr_info(ii); /* remove from active list */
+
+#ifdef DIAGNOSTIC
+ if (ii->stdend == NULL) {
+ printf("uhci_device_isoc_done: xfer=%p stdend==NULL\n", xfer);
+#ifdef USB_DEBUG
+ uhci_dump_ii(ii);
+#endif
+ return;
+ }
+#endif
+}
+
+void
+uhci_device_intr_done(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_softc_t *sc = ii->sc;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_soft_qh_t *sqh;
+ int i, npoll;
+
+ DPRINTFN(5, ("uhci_device_intr_done: length=%d\n", xfer->actlen));
+
+ npoll = upipe->u.intr.npoll;
+ for(i = 0; i < npoll; i++) {
+ sqh = upipe->u.intr.qhs[i];
+ sqh->elink = NULL;
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ }
+ uhci_free_std_chain(sc, ii->stdstart, NULL);
+
+ /* XXX Wasteful. */
+ if (xfer->pipe->repeat) {
+ uhci_soft_td_t *data, *dataend;
+
+ DPRINTFN(5,("uhci_device_intr_done: requeing\n"));
+
+ /* This alloc cannot fail since we freed the chain above. */
+ uhci_alloc_std_chain(upipe, sc, xfer->length,
+ upipe->u.intr.isread, xfer->flags,
+ &xfer->dmabuf, &data, &dataend);
+ dataend->td.td_status |= htole32(UHCI_TD_IOC);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_intr_done: data(1)\n"));
+ uhci_dump_tds(data);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ ii->stdstart = data;
+ ii->stdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone) {
+ printf("uhci_device_intr_done: not done, ii=%p\n", ii);
+ }
+ ii->isdone = 0;
+#endif
+ for (i = 0; i < npoll; i++) {
+ sqh = upipe->u.intr.qhs[i];
+ sqh->elink = data;
+ sqh->qh.qh_elink = htole32(data->physaddr | UHCI_PTR_TD);
+ }
+ xfer->status = USBD_IN_PROGRESS;
+ /* The ii is already on the examined list, just leave it. */
+ } else {
+ DPRINTFN(5,("uhci_device_intr_done: removing\n"));
+ if (uhci_active_intr_info(ii))
+ uhci_del_intr_info(ii);
+ }
+}
+
+/* Deallocate request data structures */
+void
+uhci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_softc_t *sc = ii->sc;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ panic("uhci_device_ctrl_done: not a request");
+#endif
+
+ if (!uhci_active_intr_info(ii))
+ return;
+
+ uhci_del_intr_info(ii); /* remove from active list */
+
+ if (upipe->pipe.device->speed == USB_SPEED_LOW)
+ uhci_remove_ls_ctrl(sc, upipe->u.ctl.sqh);
+ else
+ uhci_remove_hs_ctrl(sc, upipe->u.ctl.sqh);
+
+ if (upipe->u.ctl.length != 0)
+ uhci_free_std_chain(sc, ii->stdstart->link.std, ii->stdend);
+
+ DPRINTFN(5, ("uhci_device_ctrl_done: length=%d\n", xfer->actlen));
+}
+
+/* Deallocate request data structures */
+void
+uhci_device_bulk_done(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_softc_t *sc = ii->sc;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+
+ DPRINTFN(5,("uhci_device_bulk_done: xfer=%p ii=%p sc=%p upipe=%p\n",
+ xfer, ii, sc, upipe));
+
+ if (!uhci_active_intr_info(ii))
+ return;
+
+ uhci_del_intr_info(ii); /* remove from active list */
+
+ uhci_remove_bulk(sc, upipe->u.bulk.sqh);
+
+ uhci_free_std_chain(sc, ii->stdstart, NULL);
+
+ DPRINTFN(5, ("uhci_device_bulk_done: length=%d\n", xfer->actlen));
+}
+
+/* Add interrupt QH, called with vflock. */
+void
+uhci_add_intr(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ struct uhci_vframe *vf = &sc->sc_vframes[sqh->pos];
+ uhci_soft_qh_t *eqh;
+
+ DPRINTFN(4, ("uhci_add_intr: n=%d sqh=%p\n", sqh->pos, sqh));
+
+ eqh = vf->eqh;
+ sqh->hlink = eqh->hlink;
+ sqh->qh.qh_hlink = eqh->qh.qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
+ vf->eqh = sqh;
+ vf->bandwidth++;
+}
+
+/* Remove interrupt QH. */
+void
+uhci_remove_intr(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ struct uhci_vframe *vf = &sc->sc_vframes[sqh->pos];
+ uhci_soft_qh_t *pqh;
+
+ DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", sqh->pos, sqh));
+
+ /* See comment in uhci_remove_ctrl() */
+ if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ delay(UHCI_QH_REMOVE_DELAY);
+ }
+
+ pqh = uhci_find_prev_qh(vf->hqh, sqh);
+ pqh->hlink = sqh->hlink;
+ pqh->qh.qh_hlink = sqh->qh.qh_hlink;
+ delay(UHCI_QH_REMOVE_DELAY);
+ if (vf->eqh == sqh)
+ vf->eqh = pqh;
+ vf->bandwidth--;
+}
+
+usbd_status
+uhci_device_setintr(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_device_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_device_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_USBHC, 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_device_setintr: bw=%d offs=%d\n", bestbw, bestoffs));
+
+ for(i = 0; i < npoll; i++) {
+ upipe->u.intr.qhs[i] = sqh = uhci_alloc_sqh(sc);
+ sqh->elink = NULL;
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sqh->pos = MOD(i * ival + bestoffs);
+ }
+#undef MOD
+
+ s = splusb();
+ /* Enter QHs into the controller data structures. */
+ for(i = 0; i < npoll; i++)
+ uhci_add_intr(sc, upipe->u.intr.qhs[i]);
+ splx(s);
+
+ DPRINTFN(5, ("uhci_device_setintr: returns %p\n", upipe));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Open a new pipe. */
+usbd_status
+uhci_open(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 err;
+ int ival;
+
+ DPRINTFN(1, ("uhci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
+ pipe, pipe->device->address,
+ ed->bEndpointAddress, sc->sc_addr));
+
+ upipe->aborting = 0;
+ upipe->nexttoggle = 0;
+
+ if (pipe->device->address == sc->sc_addr) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &uhci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | UHCI_INTR_ENDPT:
+ pipe->methods = &uhci_root_intr_methods;
+ break;
+ default:
+ return (USBD_INVAL);
+ }
+ } else {
+ 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 == NULL)
+ goto bad;
+ upipe->u.ctl.setup = uhci_alloc_std(sc);
+ if (upipe->u.ctl.setup == NULL) {
+ uhci_free_sqh(sc, upipe->u.ctl.sqh);
+ goto bad;
+ }
+ upipe->u.ctl.stat = uhci_alloc_std(sc);
+ if (upipe->u.ctl.stat == NULL) {
+ uhci_free_sqh(sc, upipe->u.ctl.sqh);
+ uhci_free_std(sc, upipe->u.ctl.setup);
+ goto bad;
+ }
+ err = usb_allocmem(&sc->sc_bus,
+ sizeof(usb_device_request_t),
+ 0, &upipe->u.ctl.reqdma);
+ if (err) {
+ 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;
+ ival = pipe->interval;
+ if (ival == USBD_DEFAULT_INTERVAL)
+ ival = ed->bInterval;
+ return (uhci_device_setintr(sc, upipe, ival));
+ case UE_ISOCHRONOUS:
+ pipe->methods = &uhci_device_isoc_methods;
+ return (uhci_setup_isoc(pipe));
+ case UE_BULK:
+ pipe->methods = &uhci_device_bulk_methods;
+ upipe->u.bulk.sqh = uhci_alloc_sqh(sc);
+ if (upipe->u.bulk.sqh == NULL)
+ goto bad;
+ break;
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ 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 */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* 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,
+ UICLASS_HUB,
+ UISUBCLASS_HUB,
+ UIPROTO_FSHUB,
+ 0
+};
+
+usb_endpoint_descriptor_t uhci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_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 */
+};
+
+int
+uhci_str(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);
+}
+
+/*
+ * The USB hub protocol requires that SET_FEATURE(PORT_RESET) also
+ * enables the port, and also states that SET_FEATURE(PORT_ENABLE)
+ * should not be used by the USB subsystem. As we cannot issue a
+ * SET_FEATURE(PORT_ENABLE) externally, we must ensure that the port
+ * will be enabled as part of the reset.
+ *
+ * On the VT83C572, the port cannot be successfully enabled until the
+ * outstanding "port enable change" and "connection status change"
+ * events have been reset.
+ */
+Static usbd_status
+uhci_portreset(uhci_softc_t *sc, int index)
+{
+ int lim, port, x;
+
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else
+ return (USBD_IOERROR);
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PR);
+
+ usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY);
+
+ DPRINTFN(3,("uhci port %d reset, status0 = 0x%04x\n",
+ index, UREAD2(sc, port)));
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PR);
+
+ delay(100);
+
+ DPRINTFN(3,("uhci port %d reset, status1 = 0x%04x\n",
+ index, UREAD2(sc, port)));
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PE);
+
+ for (lim = 10; --lim > 0;) {
+ usb_delay_ms(&sc->sc_bus, USB_PORT_RESET_DELAY);
+
+ x = UREAD2(sc, port);
+
+ DPRINTFN(3,("uhci port %d iteration %u, status = 0x%04x\n",
+ index, lim, x));
+
+ if (!(x & UHCI_PORTSC_CCS)) {
+ /*
+ * No device is connected (or was disconnected
+ * during reset). Consider the port reset.
+ * The delay must be long enough to ensure on
+ * the initial iteration that the device
+ * connection will have been registered. 50ms
+ * appears to be sufficient, but 20ms is not.
+ */
+ DPRINTFN(3,("uhci port %d loop %u, device detached\n",
+ index, lim));
+ break;
+ }
+
+ if (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)) {
+ /*
+ * Port enabled changed and/or connection
+ * status changed were set. Reset either or
+ * both raised flags (by writing a 1 to that
+ * bit), and wait again for state to settle.
+ */
+ UWRITE2(sc, port, URWMASK(x) |
+ (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)));
+ continue;
+ }
+
+ if (x & UHCI_PORTSC_PE)
+ /* Port is enabled */
+ break;
+
+ UWRITE2(sc, port, URWMASK(x) | UHCI_PORTSC_PE);
+ }
+
+ DPRINTFN(3,("uhci port %d reset, status2 = 0x%04x\n",
+ index, UREAD2(sc, port)));
+
+ if (lim <= 0) {
+ DPRINTFN(1,("uhci port %d reset timed out\n", index));
+ return (USBD_TIMEOUT);
+ }
+
+ sc->sc_isreset = 1;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/*
+ * Simulate a hardware hub by handling all the necessary requests.
+ */
+usbd_status
+uhci_root_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+uhci_root_ctrl_start(usbd_xfer_handle xfer)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)xfer->pipe->device->bus;
+ usb_device_request_t *req;
+ void *buf = NULL;
+ int port, x;
+ int s, len, value, index, status, change, l, totlen = 0;
+ usb_port_status_t ps;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ panic("uhci_root_ctrl_transfer: not a request");
+#endif
+ req = &xfer->request;
+
+ 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);
+
+ if (len != 0)
+ buf = KERNADDR(&xfer->dmabuf, 0);
+
+#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_HALT 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) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ USETW(uhci_devd.idVendor, sc->sc_id_vendor);
+ memcpy(buf, &uhci_devd, l);
+ break;
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = 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:
+ err = 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) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ err = 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):
+ err = 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 {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PR);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_POEDC);
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_OCIC);
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_isreset = 0;
+ err = 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:
+ err = 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 {
+ err = 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 & 0xff) != 0) {
+ err = 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) {
+ err = 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 {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ err = 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):
+ err = 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 {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ err = uhci_portreset(sc, index);
+ goto ret;
+ case UHF_PORT_POWER:
+ /* Pretend we turned on power */
+ err = USBD_NORMAL_COMPLETION;
+ goto ret;
+ 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_LOW_SPEED:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_RESET:
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->actlen = totlen;
+ err = USBD_NORMAL_COMPLETION;
+ ret:
+ xfer->status = err;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root control request. */
+void
+uhci_root_ctrl_abort(usbd_xfer_handle xfer)
+{
+ /* Nothing to do, all transfers are synchronous. */
+}
+
+/* Close the root pipe. */
+void
+uhci_root_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("uhci_root_ctrl_close\n"));
+}
+
+/* Abort a root interrupt request. */
+void
+uhci_root_intr_abort(usbd_xfer_handle xfer)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)xfer->pipe->device->bus;
+
+ usb_uncallout(sc->sc_poll_handle, uhci_poll_hub, xfer);
+ sc->sc_intr_xfer = NULL;
+
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("uhci_root_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = 0;
+ }
+ xfer->status = USBD_CANCELLED;
+#ifdef DIAGNOSTIC
+ UXFER(xfer)->iinfo.isdone = 1;
+#endif
+ usb_transfer_complete(xfer);
+}
+
+usbd_status
+uhci_root_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
+/* Start a transfer on the root interrupt pipe */
+usbd_status
+uhci_root_intr_start(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+
+ DPRINTFN(3, ("uhci_root_intr_start: xfer=%p len=%d flags=%d\n",
+ xfer, xfer->length, xfer->flags));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ sc->sc_ival = MS_TO_TICKS(xfer->pipe->endpoint->edesc->bInterval);
+ usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer);
+ sc->sc_intr_xfer = xfer;
+ return (USBD_IN_PROGRESS);
+}
+
+/* Close the root interrupt pipe. */
+void
+uhci_root_intr_close(usbd_pipe_handle pipe)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+
+ usb_uncallout(sc->sc_poll_handle, uhci_poll_hub, sc->sc_intr_xfer);
+ sc->sc_intr_xfer = NULL;
+ DPRINTF(("uhci_root_intr_close\n"));
+}
diff --git a/sys/dev/usb/uhci_pci.c b/sys/dev/usb/uhci_pci.c
new file mode 100644
index 0000000..f2946ab
--- /dev/null
+++ b/sys/dev/usb/uhci_pci.c
@@ -0,0 +1,416 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) at
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Universal Host Controller Interface
+ *
+ * UHCI spec: http://www.intel.com/
+ */
+
+/* The low level controller code for UHCI has been split into
+ * PCI probes and UHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+#if defined(__FreeBSD__)
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+#endif
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.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/uhcireg.h>
+#include <dev/usb/uhcivar.h>
+
+#define PCI_UHCI_VENDORID_INTEL 0x8086
+#define PCI_UHCI_VENDORID_VIA 0x1106
+
+#define PCI_UHCI_DEVICEID_PIIX3 0x70208086
+static const char *uhci_device_piix3 = "Intel 82371SB (PIIX3) USB controller";
+
+#define PCI_UHCI_DEVICEID_PIIX4 0x71128086
+#define PCI_UHCI_DEVICEID_PIIX4E 0x71128086 /* no separate stepping */
+static const char *uhci_device_piix4 = "Intel 82371AB/EB (PIIX4) USB controller";
+
+#define PCI_UHCI_DEVICEID_ICH 0x24128086
+static const char *uhci_device_ich = "Intel 82801AA (ICH) USB controller";
+
+#define PCI_UHCI_DEVICEID_ICH0 0x24228086
+static const char *uhci_device_ich0 = "Intel 82801AB (ICH0) USB controller";
+
+#define PCI_UHCI_DEVICEID_ICH2_A 0x24428086
+static const char *uhci_device_ich2_a = "Intel 82801BA/BAM (ICH2) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH2_B 0x24448086
+static const char *uhci_device_ich2_b = "Intel 82801BA/BAM (ICH2) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH3_A 0x24828086
+static const char *uhci_device_ich3_a = "Intel 82801CA/CAM (ICH3) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH3_B 0x24848086
+static const char *uhci_device_ich3_b = "Intel 82801CA/CAM (ICH3) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH3_C 0x24878086
+static const char *uhci_device_ich3_c = "Intel 82801CA/CAM (ICH3) USB controller USB-C";
+
+#define PCI_UHCI_DEVICEID_ICH4_A 0x24c28086
+static const char *uhci_device_ich4_a = "Intel 82801DB (ICH4) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH4_B 0x24c48086
+static const char *uhci_device_ich4_b = "Intel 82801DB (ICH4) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH4_C 0x24c78086
+static const char *uhci_device_ich4_c = "Intel 82801DB (ICH4) USB controller USB-C";
+
+#define PCI_UHCI_DEVICEID_ICH5_A 0x24d28086
+static const char *uhci_device_ich5_a = "Intel 82801EB (ICH5) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH5_B 0x24d48086
+static const char *uhci_device_ich5_b = "Intel 82801EB (ICH5) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH5_C 0x24d78086
+static const char *uhci_device_ich5_c = "Intel 82801EB (ICH5) USB controller USB-C";
+
+#define PCI_UHCI_DEVICEID_ICH5_D 0x24de8086
+static const char *uhci_device_ich5_d = "Intel 82801EB (ICH5) USB controller USB-D";
+
+#define PCI_UHCI_DEVICEID_440MX 0x719a8086
+static const char *uhci_device_440mx = "Intel 82443MX USB controller";
+
+#define PCI_UHCI_DEVICEID_460GX 0x76028086
+static const char *uhci_device_460gx = "Intel 82372FB/82468GX USB controller";
+
+#define PCI_UHCI_DEVICEID_VT83C572 0x30381106
+static const char *uhci_device_vt83c572 = "VIA 83C572 USB controller";
+
+static const char *uhci_device_generic = "UHCI (generic) USB controller";
+
+#define PCI_UHCI_BASE_REG 0x20
+
+
+static int uhci_pci_attach(device_t self);
+static int uhci_pci_detach(device_t self);
+static int uhci_pci_suspend(device_t self);
+static int uhci_pci_resume(device_t self);
+
+
+static int
+uhci_pci_suspend(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return err;
+ uhci_power(PWR_SUSPEND, sc);
+
+ return 0;
+}
+
+static int
+uhci_pci_resume(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+
+ pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
+
+ uhci_power(PWR_RESUME, sc);
+ bus_generic_resume(self);
+
+ return 0;
+}
+
+static const char *
+uhci_pci_match(device_t self)
+{
+ u_int32_t device_id = pci_get_devid(self);
+
+ if (device_id == PCI_UHCI_DEVICEID_PIIX3) {
+ return (uhci_device_piix3);
+ } else if (device_id == PCI_UHCI_DEVICEID_PIIX4) {
+ return (uhci_device_piix4);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH) {
+ return (uhci_device_ich);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH0) {
+ return (uhci_device_ich0);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH2_A) {
+ return (uhci_device_ich2_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH2_B) {
+ return (uhci_device_ich2_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH3_A) {
+ return (uhci_device_ich3_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH3_B) {
+ return (uhci_device_ich3_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH3_C) {
+ return (uhci_device_ich3_c);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH4_A) {
+ return (uhci_device_ich4_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH4_B) {
+ return (uhci_device_ich4_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH4_C) {
+ return (uhci_device_ich4_c);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH5_A) {
+ return (uhci_device_ich5_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH5_B) {
+ return (uhci_device_ich5_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH5_C) {
+ return (uhci_device_ich5_c);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH5_D) {
+ return (uhci_device_ich5_d);
+ } else if (device_id == PCI_UHCI_DEVICEID_440MX) {
+ return (uhci_device_440mx);
+ } else if (device_id == PCI_UHCI_DEVICEID_460GX) {
+ return (uhci_device_460gx);
+ } else if (device_id == PCI_UHCI_DEVICEID_VT83C572) {
+ return (uhci_device_vt83c572);
+ } else {
+ if (pci_get_class(self) == PCIC_SERIALBUS
+ && pci_get_subclass(self) == PCIS_SERIALBUS_USB
+ && pci_get_progif(self) == PCI_INTERFACE_UHCI) {
+ return (uhci_device_generic);
+ }
+ }
+
+ return NULL; /* dunno... */
+}
+
+static int
+uhci_pci_probe(device_t self)
+{
+ const char *desc = uhci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return 0;
+ } else {
+ return ENXIO;
+ }
+}
+
+static int
+uhci_pci_attach(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+ int rid;
+ int err;
+
+ pci_enable_busmaster(self);
+
+ rid = PCI_UHCI_BASE_REG;
+ sc->io_res = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid,
+ RF_ACTIVE);
+ if (!sc->io_res) {
+ device_printf(self, "Could not map ports\n");
+ return ENXIO;
+ }
+ sc->iot = rman_get_bustag(sc->io_res);
+ sc->ioh = rman_get_bushandle(sc->io_res);
+
+ /* disable interrupts */
+ bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ uhci_pci_detach(self);
+ return ENXIO;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ uhci_pci_detach(self);
+ return ENOMEM;
+ }
+ device_set_ivars(sc->sc_bus.bdev, sc);
+
+ /* uhci_pci_match must never return NULL if uhci_pci_probe succeeded */
+ device_set_desc(sc->sc_bus.bdev, uhci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_UHCI_VENDORID_INTEL:
+ sprintf(sc->sc_vendor, "Intel");
+ break;
+ case PCI_UHCI_VENDORID_VIA:
+ sprintf(sc->sc_vendor, "VIA");
+ break;
+ default:
+ if (bootverbose)
+ device_printf(self, "(New UHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+ switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USBREV_MASK) {
+ case PCI_USBREV_PRE_1_0:
+ sc->sc_bus.usbrev = USBREV_PRE_1_0;
+ break;
+ case PCI_USBREV_1_0:
+ sc->sc_bus.usbrev = USBREV_1_0;
+ break;
+ default:
+ sc->sc_bus.usbrev = USBREV_UNKNOWN;
+ break;
+ }
+
+ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO,
+ (driver_intr_t *) uhci_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->ih = NULL;
+ uhci_pci_detach(self);
+ return ENXIO;
+ }
+ /*
+ * Set the PIRQD enable bit and switch off all the others. We don't
+ * want legacy support to interfere with us XXX Does this also mean
+ * that the BIOS won't touch the keyboard anymore if it is connected
+ * to the ports of the root hub?
+ */
+#ifdef USB_DEBUG
+ if (pci_read_config(self, PCI_LEGSUP, 2) != PCI_LEGSUP_USBPIRQDEN)
+ device_printf(self, "LegSup = 0x%04x\n",
+ pci_read_config(self, PCI_LEGSUP, 2));
+#endif
+ pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
+
+ err = uhci_init(sc);
+ if (!err)
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+
+ if (err) {
+ device_printf(self, "USB init failed\n");
+ uhci_pci_detach(self);
+ return EIO;
+ }
+ return 0; /* success */
+}
+
+int
+uhci_pci_detach(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+
+ /*
+ * XXX This function is not yet complete and should not be added
+ * method list.
+ */
+#if 0
+ if uhci_init
+ was successful
+ we should call something like uhci_deinit
+#endif
+
+ /*
+ * disable interrupts that might have been switched on in
+ * uhci_init.
+ */
+ if (sc->iot && sc->ioh)
+ bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0);
+
+ if (sc->irq_res && sc->ih) {
+ int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->ih = NULL;
+ }
+ if (sc->sc_bus.bdev) {
+ device_delete_child(self, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+ if (sc->irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res);
+ sc->irq_res = NULL;
+ }
+ if (sc->io_res) {
+ bus_release_resource(self, SYS_RES_IOPORT, PCI_UHCI_BASE_REG,
+ sc->io_res);
+ sc->io_res = NULL;
+ sc->iot = 0;
+ sc->ioh = 0;
+ }
+ return 0;
+}
+
+
+static device_method_t uhci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uhci_pci_probe),
+ DEVMETHOD(device_attach, uhci_pci_attach),
+ DEVMETHOD(device_suspend, uhci_pci_suspend),
+ DEVMETHOD(device_resume, uhci_pci_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t uhci_driver = {
+ "uhci",
+ uhci_methods,
+ sizeof(uhci_softc_t),
+};
+
+static devclass_t uhci_devclass;
+
+DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0);
+DRIVER_MODULE(uhci, cardbus, uhci_driver, uhci_devclass, 0, 0);
diff --git a/sys/dev/usb/uhcireg.h b/sys/dev/usb/uhcireg.h
new file mode 100644
index 0000000..b640e9f
--- /dev/null
+++ b/sys/dev/usb/uhcireg.h
@@ -0,0 +1,193 @@
+/* $NetBSD: uhcireg.h,v 1.15 2002/02/11 11:41:30 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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_USBREV_1_1 0x11
+
+#define PCI_LEGSUP 0xc0 /* Legacy Support register */
+#define PCI_LEGSUP_USBPIRQDEN 0x2000 /* USB PIRQ D Enable */
+
+#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_STS_ALLINTRS 0x003f
+
+#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 URWMASK(x) \
+ ((x) & (UHCI_PORTSC_SUSP | UHCI_PORTSC_PR | UHCI_PORTSC_RD | UHCI_PORTSC_PE))
+
+#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_TD 0x00000000
+#define UHCI_PTR_QH 0x00000002
+#define UHCI_PTR_VF 0x00000004
+
+/*
+ * Wait this long after a QH has been removed. This gives that HC a
+ * chance to stop looking at it before it's recycled.
+ */
+#define UHCI_QH_REMOVE_DELAY 5
+
+/*
+ * The Queue Heads and Transfer Descriptors are accessed
+ * by both the CPU and the USB controller which run
+ * 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)&0xf) << 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_td_t;
+
+#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;
+} uhci_qh_t;
+
+#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..546dcb1
--- /dev/null
+++ b/sys/dev/usb/uhcivar.h
@@ -0,0 +1,205 @@
+/* $NetBSD: uhcivar.h,v 1.33 2002/02/11 11:41:30 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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, form the
+ * starts of the virtual frame list. This also has the advantage that it
+ * simplifies linking in/out of TDs/QHs 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. This QH points at another QH which is the start of the
+ * bulk 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;
+
+typedef union {
+ struct uhci_soft_qh *sqh;
+ struct uhci_soft_td *std;
+} uhci_soft_td_qh_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 {
+ struct uhci_softc *sc;
+ usbd_xfer_handle xfer;
+ 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;
+
+struct uhci_xfer {
+ struct usbd_xfer xfer;
+ uhci_intr_info_t iinfo;
+ struct usb_task abort_task;
+ int curframe;
+};
+
+#define UXFER(xfer) ((struct uhci_xfer *)(xfer))
+
+/*
+ * Extra information that we need for a TD.
+ */
+struct uhci_soft_td {
+ uhci_td_t td; /* The real TD, must be first */
+ uhci_soft_td_qh_t link; /* soft version of the td_link field */
+ uhci_physaddr_t physaddr; /* TD's physical address. */
+};
+/*
+ * Make the size such that it is a multiple of UHCI_TD_ALIGN. This way
+ * we can pack a number of soft TD together and have the real TD well
+ * aligned.
+ * NOTE: Minimum size is 32 bytes.
+ */
+#define UHCI_STD_SIZE ((sizeof (struct uhci_soft_td) + UHCI_TD_ALIGN - 1) / UHCI_TD_ALIGN * UHCI_TD_ALIGN)
+#define UHCI_STD_CHUNK (PAGE_SIZE / UHCI_STD_SIZE)
+
+/*
+ * Extra information that we need for a QH.
+ */
+struct uhci_soft_qh {
+ uhci_qh_t qh; /* The real QH, must be first */
+ uhci_soft_qh_t *hlink; /* soft version of qh_hlink */
+ uhci_soft_td_t *elink; /* soft version of qh_elink */
+ uhci_physaddr_t physaddr; /* QH's physical address. */
+ int pos; /* Timeslot position */
+};
+/* See comment about UHCI_STD_SIZE. */
+#define UHCI_SQH_SIZE ((sizeof (struct uhci_soft_qh) + UHCI_QH_ALIGN - 1) / UHCI_QH_ALIGN * UHCI_QH_ALIGN)
+#define UHCI_SQH_CHUNK (PAGE_SIZE / UHCI_SQH_SIZE)
+
+/*
+ * Information about an entry in the virtual 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 */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t sc_size;
+#if defined(__FreeBSD__)
+ void *ih;
+
+ struct resource *io_res;
+ struct resource *irq_res;
+#endif
+
+ uhci_physaddr_t *sc_pframes;
+ usb_dma_t sc_dma;
+ struct uhci_vframe sc_vframes[UHCI_VFRAMELIST_COUNT];
+
+ uhci_soft_qh_t *sc_lctl_start; /* dummy QH for low speed control */
+ uhci_soft_qh_t *sc_lctl_end; /* last control QH */
+ uhci_soft_qh_t *sc_hctl_start; /* dummy QH for high speed control */
+ uhci_soft_qh_t *sc_hctl_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_qh_t *sc_last_qh; /* dummy QH at the end */
+ u_int32_t sc_loops; /* number of QHs that wants looping */
+
+ uhci_soft_td_t *sc_freetds; /* TD free list */
+ uhci_soft_qh_t *sc_freeqhs; /* QH free list */
+
+ SIMPLEQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */
+
+ u_int8_t sc_addr; /* device address */
+ u_int8_t sc_conf; /* device configuration */
+
+ u_int8_t sc_saved_sof;
+ u_int16_t sc_saved_frnum;
+
+#ifdef USB_USE_SOFTINTR
+ char sc_softwake;
+#endif /* USB_USE_SOFTINTR */
+
+ char sc_isreset;
+ char sc_suspend;
+ char sc_dying;
+
+ LIST_HEAD(, uhci_intr_info) sc_intrhead;
+
+ /* Info for the root hub interrupt channel. */
+ int sc_ival; /* time between root hub intrs */
+ usbd_xfer_handle sc_intr_xfer; /* root hub interrupt transfer */
+ usb_callout_t sc_poll_handle;
+
+ char sc_vendor[16]; /* vendor string for root hub */
+ int sc_id_vendor; /* vendor ID for root hub */
+
+#if defined(__NetBSD__)
+ void *sc_powerhook; /* cookie from power hook */
+ void *sc_shutdownhook; /* cookie from shutdown hook */
+#endif
+
+ device_ptr_t sc_child; /* /dev/usb# device */
+} uhci_softc_t;
+
+usbd_status uhci_init(uhci_softc_t *);
+int uhci_intr(void *);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int uhci_detach(uhci_softc_t *, int);
+int uhci_activate(device_ptr_t, enum devact);
+#endif
+
+void uhci_shutdown(void *v);
+void uhci_power(int state, void *priv);
+
diff --git a/sys/dev/usb/uhid.c b/sys/dev/usb/uhid.c
new file mode 100644
index 0000000..f19424b
--- /dev/null
+++ b/sys/dev/usb/uhid.c
@@ -0,0 +1,755 @@
+/* $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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.
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#if __FreeBSD_version >= 500000
+#include <sys/mutex.h>
+#endif
+#include <sys/signalvar.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#elif defined(__FreeBSD__)
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#endif
+#include <sys/conf.h>
+#include <sys/tty.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/hid.h>
+
+/* Report descriptor for broken Wacom Graphire */
+#include <dev/usb/ugraphire_rdesc.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uhiddebug) logprintf x
+#define DPRINTFN(n,x) if (uhiddebug>(n)) logprintf x
+int uhiddebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid");
+SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RW,
+ &uhiddebug, 0, "uhid debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct uhid_softc {
+ USBBASEDEVICE sc_dev; /* base device */
+ usbd_device_handle sc_udev;
+ 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;
+
+ u_char *sc_ibuf;
+ u_char *sc_obuf;
+
+ void *sc_repdesc;
+ int sc_repdesc_size;
+
+ struct clist sc_q;
+ struct selinfo sc_rsel;
+ struct proc *sc_async; /* process that wants SIGIO */
+ u_char sc_state; /* driver state */
+#define UHID_OPEN 0x01 /* device is open */
+#define UHID_ASLP 0x02 /* waiting for device data */
+#define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */
+#define UHID_IMMED 0x08 /* return read data immediately */
+
+ int sc_refcnt;
+ u_char sc_dying;
+
+#if defined(__FreeBSD__)
+ struct cdev *dev;
+#endif
+};
+
+#define UHIDUNIT(dev) (minor(dev))
+#define UHID_CHUNK 128 /* chunk size for read */
+#define UHID_BSIZE 1020 /* buffer size */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+cdev_decl(uhid);
+#elif defined(__FreeBSD__)
+d_open_t uhidopen;
+d_close_t uhidclose;
+d_read_t uhidread;
+d_write_t uhidwrite;
+d_ioctl_t uhidioctl;
+d_poll_t uhidpoll;
+
+
+Static struct cdevsw uhid_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = uhidopen,
+ .d_close = uhidclose,
+ .d_read = uhidread,
+ .d_write = uhidwrite,
+ .d_ioctl = uhidioctl,
+ .d_poll = uhidpoll,
+ .d_name = "uhid",
+#if __FreeBSD_version < 500014
+ .d_bmaj -1
+#endif
+};
+#endif
+
+Static void uhid_intr(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+
+Static int uhid_do_read(struct uhid_softc *, struct uio *uio, int);
+Static int uhid_do_write(struct uhid_softc *, struct uio *uio, int);
+Static int uhid_do_ioctl(struct uhid_softc *, u_long, caddr_t, int,
+ usb_proc_ptr);
+
+USB_DECLARE_DRIVER(uhid);
+
+USB_MATCH(uhid)
+{
+ USB_MATCH_START(uhid, uaa);
+ usb_interface_descriptor_t *id;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id == NULL || id->bInterfaceClass != UICLASS_HID)
+ return (UMATCH_NONE);
+ if (uaa->matchlvl)
+ return (uaa->matchlvl);
+ return (UMATCH_IFACECLASS_GENERIC);
+}
+
+USB_ATTACH(uhid)
+{
+ USB_ATTACH_START(uhid, sc, uaa);
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int size;
+ void *desc;
+ usbd_status err;
+ char devinfo[1024];
+
+ sc->sc_udev = uaa->device;
+ sc->sc_iface = iface;
+ id = usbd_get_interface_descriptor(iface);
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
+ devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
+
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (ed == NULL) {
+ printf("%s: could not read endpoint descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_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,
+ UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out",
+ ed->bmAttributes & UE_XFERTYPE,
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
+ (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_ep_addr = ed->bEndpointAddress;
+
+ if (uaa->vendor == USB_VENDOR_WACOM &&
+ uaa->product == USB_PRODUCT_WACOM_GRAPHIRE /* &&
+ uaa->revision == 0x???? */) { /* XXX should use revision */
+ /* The report descriptor for the Wacom Graphire is broken. */
+ size = sizeof uhid_graphire_report_descr;
+ desc = malloc(size, M_USBDEV, M_NOWAIT);
+ if (desc == NULL)
+ err = USBD_NOMEM;
+ else {
+ err = USBD_NORMAL_COMPLETION;
+ memcpy(desc, uhid_graphire_report_descr, size);
+ }
+ } else {
+ desc = NULL;
+ err = usbd_read_report_desc(uaa->iface, &desc, &size,M_USBDEV);
+ }
+
+ if (err) {
+ printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_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;
+
+#if defined(__FreeBSD__)
+ sc->dev = make_dev(&uhid_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR,
+ 0644, "uhid%d", device_get_unit(self));
+#endif
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+uhid_activate(device_ptr_t self, enum devact act)
+{
+ struct uhid_softc *sc = (struct uhid_softc *)self;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+#endif
+
+USB_DETACH(uhid)
+{
+ USB_DETACH_START(uhid, sc);
+ int s;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ int maj, mn;
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags));
+#else
+ DPRINTF(("uhid_detach: sc=%p\n", sc));
+#endif
+
+ sc->sc_dying = 1;
+ if (sc->sc_intrpipe != NULL)
+ usbd_abort_pipe(sc->sc_intrpipe);
+
+ if (sc->sc_state & UHID_OPEN) {
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wake everyone */
+ wakeup(&sc->sc_q);
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+ }
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == uhidopen)
+ break;
+
+ /* Nuke the vnodes for any open instances (calls close). */
+ mn = self->dv_unit;
+ vdevgone(maj, mn, mn, VCHR);
+#elif defined(__FreeBSD__)
+ destroy_dev(sc->dev);
+#endif
+
+ if (sc->sc_repdesc)
+ free(sc->sc_repdesc, M_USBDEV);
+
+ return (0);
+}
+
+void
+uhid_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ struct uhid_softc *sc = addr;
+
+#ifdef USB_DEBUG
+ if (uhiddebug > 5) {
+ u_int32_t cc, i;
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
+ DPRINTF(("uhid_intr: status=%d cc=%d\n", status, cc));
+ DPRINTF(("uhid_intr: data ="));
+ for (i = 0; i < cc; i++)
+ DPRINTF((" %02x", sc->sc_ibuf[i]));
+ DPRINTF(("\n"));
+ }
+#endif
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("uhid_intr: status=%d\n", status));
+ if (status == USBD_STALLED)
+ 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->sc_q));
+ wakeup(&sc->sc_q);
+ }
+ selwakeuppri(&sc->sc_rsel, PZERO);
+ if (sc->sc_async != NULL) {
+ DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async));
+ PROC_LOCK(sc->sc_async);
+ psignal(sc->sc_async, SIGIO);
+ PROC_UNLOCK(sc->sc_async);
+ }
+}
+
+int
+uhidopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct uhid_softc *sc;
+ usbd_status err;
+
+ USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc);
+
+ DPRINTF(("uhidopen: sc=%p\n", sc));
+
+ if (sc->sc_dying)
+ return (ENXIO);
+
+ if (sc->sc_state & UHID_OPEN)
+ return (EBUSY);
+ sc->sc_state |= UHID_OPEN;
+
+ if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) {
+ sc->sc_state &= ~UHID_OPEN;
+ return (ENOMEM);
+ }
+
+ sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK);
+
+ /* Set up interrupt pipe. */
+ err = 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, USBD_DEFAULT_INTERVAL);
+ if (err) {
+ DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
+ "error=%d\n",err));
+ free(sc->sc_ibuf, M_USBDEV);
+ free(sc->sc_obuf, M_USBDEV);
+ sc->sc_ibuf = sc->sc_obuf = NULL;
+
+ sc->sc_state &= ~UHID_OPEN;
+ return (EIO);
+ }
+
+ sc->sc_state &= ~UHID_IMMED;
+
+ sc->sc_async = 0;
+
+ return (0);
+}
+
+int
+uhidclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct uhid_softc *sc;
+
+ USB_GET_SC(uhid, UHIDUNIT(dev), sc);
+
+ DPRINTF(("uhidclose: sc=%p\n", sc));
+
+ /* Disable interrupts. */
+ usbd_abort_pipe(sc->sc_intrpipe);
+ usbd_close_pipe(sc->sc_intrpipe);
+ sc->sc_intrpipe = 0;
+
+ ndflush(&sc->sc_q, sc->sc_q.c_cc);
+ clfree(&sc->sc_q);
+
+ free(sc->sc_ibuf, M_USBDEV);
+ free(sc->sc_obuf, M_USBDEV);
+ sc->sc_ibuf = sc->sc_obuf = NULL;
+
+ sc->sc_state &= ~UHID_OPEN;
+
+ sc->sc_async = 0;
+
+ return (0);
+}
+
+int
+uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag)
+{
+ int s;
+ int error = 0;
+ size_t length;
+ u_char buffer[UHID_CHUNK];
+ usbd_status err;
+
+ DPRINTFN(1, ("uhidread\n"));
+ if (sc->sc_state & UHID_IMMED) {
+ DPRINTFN(1, ("uhidread immed\n"));
+
+ err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
+ sc->sc_iid, buffer, sc->sc_isize);
+ if (err)
+ return (EIO);
+ return (uiomove(buffer, sc->sc_isize, uio));
+ }
+
+ s = splusb();
+ 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->sc_q));
+ error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0);
+ DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
+ if (sc->sc_dying)
+ error = EIO;
+ if (error) {
+ sc->sc_state &= ~UHID_ASLP;
+ break;
+ }
+ 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 && !error) {
+ 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 %lu chars\n", (u_long)length));
+
+ /* Copy the data to the user process. */
+ if ((error = uiomove(buffer, length, uio)) != 0)
+ break;
+ }
+
+ return (error);
+}
+
+int
+uhidread(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct uhid_softc *sc;
+ int error;
+
+ USB_GET_SC(uhid, UHIDUNIT(dev), sc);
+
+ sc->sc_refcnt++;
+ error = uhid_do_read(sc, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+int
+uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag)
+{
+ int error;
+ int size;
+ usbd_status err;
+
+ DPRINTFN(1, ("uhidwrite\n"));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ size = sc->sc_osize;
+ error = 0;
+ if (uio->uio_resid != size)
+ return (EINVAL);
+ error = uiomove(sc->sc_obuf, size, uio);
+ if (!error) {
+ if (sc->sc_oid)
+ err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
+ sc->sc_obuf[0], sc->sc_obuf+1, size-1);
+ else
+ err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
+ 0, sc->sc_obuf, size);
+ if (err)
+ error = EIO;
+ }
+
+ return (error);
+}
+
+int
+uhidwrite(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct uhid_softc *sc;
+ int error;
+
+ USB_GET_SC(uhid, UHIDUNIT(dev), sc);
+
+ sc->sc_refcnt++;
+ error = uhid_do_write(sc, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+int
+uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, int flag,
+ usb_proc_ptr p)
+{
+ struct usb_ctl_report_desc *rd;
+ struct usb_ctl_report *re;
+ int size, id;
+ usbd_status err;
+
+ DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ switch (cmd) {
+ case FIONBIO:
+ /* All handled in the upper FS layer. */
+ break;
+
+ case FIOASYNC:
+ if (*(int *)addr) {
+ if (sc->sc_async != NULL)
+ return (EBUSY);
+#if __FreeBSD_version >= 500000
+ sc->sc_async = p->td_proc;
+#else
+ sc->sc_async = p;
+#endif
+ DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", sc->sc_async));
+ } else
+ sc->sc_async = NULL;
+ break;
+
+ /* XXX this is not the most general solution. */
+ case TIOCSPGRP:
+ if (sc->sc_async == NULL)
+ return (EINVAL);
+ if (*(int *)addr != sc->sc_async->p_pgid)
+ return (EPERM);
+ break;
+
+ case USB_GET_REPORT_DESC:
+ rd = (struct usb_ctl_report_desc *)addr;
+ size = min(sc->sc_repdesc_size, sizeof rd->ucrd_data);
+ rd->ucrd_size = size;
+ memcpy(rd->ucrd_data, sc->sc_repdesc, size);
+ break;
+
+ case USB_SET_IMMED:
+ if (*(int *)addr) {
+ /* XXX should read into ibuf, but does it matter? */
+ err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
+ sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
+ if (err)
+ return (EOPNOTSUPP);
+
+ sc->sc_state |= UHID_IMMED;
+ } else
+ sc->sc_state &= ~UHID_IMMED;
+ break;
+
+ case USB_GET_REPORT:
+ re = (struct usb_ctl_report *)addr;
+ switch (re->ucr_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);
+ }
+ err = usbd_get_report(sc->sc_iface, re->ucr_report, id, re->ucr_data,
+ size);
+ if (err)
+ return (EIO);
+ break;
+
+ case USB_SET_REPORT:
+ re = (struct usb_ctl_report *)addr;
+ switch (re->ucr_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);
+ }
+ err = usbd_set_report(sc->sc_iface, re->ucr_report, id, re->ucr_data,
+ size);
+ if (err)
+ return (EIO);
+ break;
+
+ case USB_GET_REPORT_ID:
+ *(int *)addr = 0; /* XXX: we only support reportid 0? */
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+uhidioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
+{
+ struct uhid_softc *sc;
+ int error;
+
+ USB_GET_SC(uhid, UHIDUNIT(dev), sc);
+
+ sc->sc_refcnt++;
+ error = uhid_do_ioctl(sc, cmd, addr, flag, p);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+int
+uhidpoll(struct cdev *dev, int events, usb_proc_ptr p)
+{
+ struct uhid_softc *sc;
+ int revents = 0;
+ int s;
+
+ USB_GET_SC(uhid, UHIDUNIT(dev), sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ s = splusb();
+ 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, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c
new file mode 100644
index 0000000..e302183
--- /dev/null
+++ b/sys/dev/usb/uhub.c
@@ -0,0 +1,644 @@
+/* $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#include <sys/proc.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#include "bus_if.h"
+#endif
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+
+#define UHUB_INTR_INTERVAL 255 /* ms */
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uhubdebug) logprintf x
+#define DPRINTFN(n,x) if (uhubdebug>(n)) logprintf x
+int uhubdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB uhub");
+SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW,
+ &uhubdebug, 0, "uhub debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct uhub_softc {
+ USBBASEDEVICE 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;
+};
+
+Static usbd_status uhub_explore(usbd_device_handle hub);
+Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status);
+
+#if defined(__FreeBSD__)
+Static bus_driver_added_t uhub_driver_added;
+Static bus_child_detached_t uhub_child_detached;
+#endif
+
+
+/*
+ * We need two attachment points:
+ * hub to usb and hub to hub
+ * Every other driver only connects to hubs
+ */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+USB_DECLARE_DRIVER(uhub);
+
+/* Create the driver instance for the hub connected to hub case */
+CFATTACH_DECL(uhub_uhub, sizeof(struct uhub_softc),
+ uhub_match, uhub_attach, uhub_detach, uhub_activate);
+#elif defined(__FreeBSD__)
+USB_DECLARE_DRIVER_INIT(uhub,
+ DEVMETHOD(bus_driver_added, uhub_driver_added),
+ DEVMETHOD(bus_child_detached, uhub_child_detached),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown)
+ );
+
+/* Create the driver instance for the hub connected to usb case. */
+devclass_t uhubroot_devclass;
+
+Static device_method_t uhubroot_methods[] = {
+ DEVMETHOD(device_probe, uhub_match),
+ DEVMETHOD(device_attach, uhub_attach),
+
+ /* detach is not allowed for a root hub */
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ {0,0}
+};
+
+Static driver_t uhubroot_driver = {
+ "uhub",
+ uhubroot_methods,
+ sizeof(struct uhub_softc)
+};
+#endif
+
+USB_MATCH(uhub)
+{
+ USB_MATCH_START(uhub, uaa);
+ usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
+
+ DPRINTFN(5,("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 == NULL && dd->bDeviceClass == UDCLASS_HUB)
+ return (UMATCH_DEVCLASS_DEVSUBCLASS);
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(uhub)
+{
+ USB_ATTACH_START(uhub, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ char *devinfo;
+ usbd_status err;
+ struct usbd_hub *hub;
+ usb_device_request_t req;
+ usb_hub_descriptor_t hubdesc;
+ int p, port, nports, nremov, pwrdly;
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+
+ devinfo = malloc(1024, M_TEMP, M_NOWAIT);
+ if (devinfo == NULL) {
+ USB_ATTACH_ERROR_RETURN;
+ }
+ DPRINTFN(1,("uhub_attach\n"));
+ sc->sc_hub = dev;
+ usbd_devinfo(dev, 1, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ err = usbd_set_config_index(dev, 0, 1);
+ if (err) {
+ DPRINTF(("%s: configuration failed, error=%s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+ free(devinfo, M_TEMP);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ if (dev->depth > USB_HUB_MAX_DEPTH) {
+ printf("%s: hub depth (%d) exceeded, hub ignored\n",
+ USBDEVNAME(sc->sc_dev), USB_HUB_MAX_DEPTH);
+ free(devinfo, M_TEMP);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Get hub descriptor. */
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, (dev->address > 1 ? UDESC_HUB : 0), 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
+ DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
+ err = usbd_do_request(dev, &req, &hubdesc);
+ nports = hubdesc.bNbrPorts;
+ if (!err && nports > 7) {
+ USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
+ err = usbd_do_request(dev, &req, &hubdesc);
+ }
+ if (err) {
+ DPRINTF(("%s: getting hub descriptor failed, error=%s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+ free(devinfo, M_TEMP);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ for (nremov = 0, port = 1; port <= nports; port++)
+ if (!UHD_NOT_REMOV(&hubdesc, port))
+ nremov++;
+ printf("%s: %d port%s with %d removable, %s powered\n",
+ USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "",
+ nremov, dev->self_powered ? "self" : "bus");
+
+ hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
+ M_USBDEV, M_NOWAIT);
+ if (hub == NULL) {
+ free(devinfo, M_TEMP);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ dev->hub = hub;
+ dev->hub->hubsoftc = sc;
+ hub->explore = uhub_explore;
+ hub->hubdesc = hubdesc;
+
+ 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 != NULL &&
+ !dev->powersrc->parent->self_powered) {
+ printf("%s: bus powered hub connected to bus powered hub, "
+ "ignored\n", USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ /* Set up interrupt pipe. */
+ err = usbd_device2interface_handle(dev, 0, &iface);
+ if (err) {
+ printf("%s: no interface handle\n", USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor\n", USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ printf("%s: bad interrupt endpoint\n", USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,
+ USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status,
+ sizeof(sc->sc_status), uhub_intr, UHUB_INTR_INTERVAL);
+ if (err) {
+ printf("%s: cannot open interrupt pipe\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ /* Wait with power off for a while. */
+ usbd_delay_ms(dev, USB_POWER_DOWN_TIME);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, USBDEV(sc->sc_dev));
+
+ /*
+ * To have the best chance of success we do things in the exact same
+ * order as Windoze98. This should not be necessary, but some
+ * devices do not follow the USB specs to the letter.
+ *
+ * These are the events on the bus when a hub is attached:
+ * Get device and config descriptors (see attach code)
+ * Get hub descriptor (see above)
+ * For all ports
+ * turn on power
+ * wait for power to become stable
+ * (all below happens in explore code)
+ * For all ports
+ * clear C_PORT_CONNECTION
+ * For all ports
+ * get port status
+ * if device connected
+ * wait 100 ms
+ * turn on reset
+ * wait
+ * clear C_PORT_RESET
+ * get port status
+ * proceed with device attachment
+ */
+
+ /* Set up data structures */
+ for (p = 0; p < nports; p++) {
+ struct usbd_port *up = &hub->ports[p];
+ up->device = 0;
+ up->parent = dev;
+ up->portno = p+1;
+ if (dev->self_powered)
+ /* Self powered hub, give ports maximum current. */
+ up->power = USB_MAX_POWER;
+ else
+ up->power = USB_MIN_POWER;
+ up->restartcnt = 0;
+ }
+
+ /* XXX should check for none, individual, or ganged power? */
+
+ pwrdly = dev->hub->hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR
+ + USB_EXTRA_POWER_UP_TIME;
+ for (port = 1; port <= nports; port++) {
+ /* Turn the power on. */
+ err = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
+ if (err)
+ printf("%s: port %d power on failed, %s\n",
+ USBDEVNAME(sc->sc_dev), port,
+ usbd_errstr(err));
+ DPRINTF(("usb_init_port: turn on port %d power\n", port));
+ /* Wait for stable power. */
+ usbd_delay_ms(dev, pwrdly);
+ }
+
+ /* The usual exploration will finish the setup. */
+
+ sc->sc_running = 1;
+
+ USB_ATTACH_SUCCESS_RETURN;
+
+ bad:
+ free(hub, M_USBDEV);
+ free(devinfo, M_TEMP);
+ dev->hub = 0;
+ USB_ATTACH_ERROR_RETURN;
+}
+
+usbd_status
+uhub_explore(usbd_device_handle dev)
+{
+ usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
+ struct uhub_softc *sc = dev->hub->hubsoftc;
+ struct usbd_port *up;
+ usbd_status err;
+ int speed;
+ 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];
+ err = usbd_get_port_status(dev, port, &up->status);
+ if (err) {
+ DPRINTF(("uhub_explore: get port status failed, "
+ "error=%s\n", usbd_errstr(err)));
+ continue;
+ }
+ status = UGETW(up->status.wPortStatus);
+ change = UGETW(up->status.wPortChange);
+ DPRINTFN(3,("uhub_explore: %s port %d status 0x%04x 0x%04x\n",
+ USBDEVNAME(sc->sc_dev), port, status, change));
+ if (change & UPS_C_PORT_ENABLED) {
+ DPRINTF(("uhub_explore: C_PORT_ENABLED\n"));
+ usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
+ if (status & UPS_PORT_ENABLED) {
+ printf("%s: illegal enable change, port %d\n",
+ USBDEVNAME(sc->sc_dev), port);
+ } else {
+ /* Port error condition. */
+ if (up->restartcnt) /* no message first time */
+ printf("%s: port error, restarting "
+ "port %d\n",
+ USBDEVNAME(sc->sc_dev), port);
+
+ if (up->restartcnt++ < USBD_RESTART_MAX)
+ goto disco;
+ else
+ printf("%s: port error, giving up "
+ "port %d\n",
+ USBDEVNAME(sc->sc_dev), port);
+ }
+ }
+ if (!(change & UPS_C_CONNECT_STATUS)) {
+ DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_"
+ "STATUS\n", port));
+ /* No status change, just do recursive explore. */
+ if (up->device != NULL && up->device->hub != NULL)
+ up->device->hub->explore(up->device);
+#if 0 && defined(DIAGNOSTIC)
+ if (up->device == NULL &&
+ (status & UPS_CURRENT_CONNECT_STATUS))
+ printf("%s: connected, no device\n",
+ USBDEVNAME(sc->sc_dev));
+#endif
+ continue;
+ }
+
+ /* We have a connect status change, handle it. */
+
+ 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.
+ */
+ disco:
+ if (up->device != NULL) {
+ /* Disconnected */
+ DPRINTF(("uhub_explore: device addr=%d disappeared "
+ "on port %d\n", up->device->address, port));
+ usb_disconnect_port(up, USBDEV(sc->sc_dev));
+ usbd_clear_port_feature(dev, port,
+ UHF_C_PORT_CONNECTION);
+ }
+ if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
+ /* Nothing connected, just ignore it. */
+ DPRINTFN(3,("uhub_explore: port=%d !CURRENT_CONNECT"
+ "_STATUS\n", port));
+ continue;
+ }
+
+ /* Connected */
+
+ if (!(status & UPS_PORT_POWER))
+ printf("%s: strange, connected port %d has no power\n",
+ USBDEVNAME(sc->sc_dev), port);
+
+ /* Wait for maximum device power up time. */
+ usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY);
+
+ /* Reset port, which implies enabling it. */
+ if (usbd_reset_port(dev, port, &up->status)) {
+ printf("%s: port %d reset failed\n",
+ USBDEVNAME(sc->sc_dev), port);
+ continue;
+ }
+ /* Get port status again, it might have changed during reset */
+ err = usbd_get_port_status(dev, port, &up->status);
+ if (err) {
+ DPRINTF(("uhub_explore: get port status failed, "
+ "error=%s\n", usbd_errstr(err)));
+ continue;
+ }
+ status = UGETW(up->status.wPortStatus);
+ change = UGETW(up->status.wPortChange);
+ if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
+ /* Nothing connected, just ignore it. */
+#ifdef DIAGNOSTIC
+ printf("%s: port %d, device disappeared after reset\n",
+ USBDEVNAME(sc->sc_dev), port);
+#endif
+ continue;
+ }
+
+ /* Figure out device speed */
+ if (status & UPS_HIGH_SPEED)
+ speed = USB_SPEED_HIGH;
+ else if (status & UPS_LOW_SPEED)
+ speed = USB_SPEED_LOW;
+ else
+ speed = USB_SPEED_FULL;
+ /* Get device info and set its address. */
+ err = usbd_new_device(USBDEV(sc->sc_dev), dev->bus,
+ dev->depth + 1, speed, port, up);
+ /* XXX retry a few times? */
+ if (err) {
+ DPRINTFN(-1,("uhub_explore: usb_new_device failed, "
+ "error=%s\n", usbd_errstr(err)));
+ /* Avoid addressing problems by disabling. */
+ /* usbd_reset_port(dev, port, &up->status); */
+
+ /*
+ * The unit refused to accept a new address, or had
+ * some other serious problem. Since we cannot leave
+ * at 0 we have to disable the port instead.
+ */
+ printf("%s: device problem, disabling port %d\n",
+ USBDEVNAME(sc->sc_dev), port);
+ usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE);
+ } else {
+ /* The port set up succeeded, reset error count. */
+ up->restartcnt = 0;
+
+ if (up->device->hub)
+ up->device->hub->explore(up->device);
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+uhub_activate(device_ptr_t self, enum devact act)
+{
+ struct uhub_softc *sc = (struct uhub_softc *)self;
+ struct usbd_hub *hub = sc->sc_hub->hub;
+ usbd_device_handle dev;
+ int nports, port, i;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ if (hub == NULL) /* malfunctioning hub */
+ break;
+ nports = hub->hubdesc.bNbrPorts;
+ for(port = 0; port < nports; port++) {
+ dev = hub->ports[port].device;
+ if (dev != NULL && dev->subdevs != NULL) {
+ for (i = 0; dev->subdevs[i] != NULL; i++)
+ config_deactivate(dev->subdevs[i]);
+ }
+ }
+ break;
+ }
+ return (0);
+}
+#endif
+
+/*
+ * Called from process context when the hub is gone.
+ * Detach all devices on active ports.
+ */
+USB_DETACH(uhub)
+{
+ USB_DETACH_START(uhub, sc);
+ struct usbd_hub *hub = sc->sc_hub->hub;
+ struct usbd_port *rup;
+ int port, nports;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ DPRINTF(("uhub_detach: sc=%p flags=%d\n", sc, flags));
+#elif defined(__FreeBSD__)
+ DPRINTF(("uhub_detach: sc=%port\n", sc));
+#endif
+
+ if (hub == NULL) /* Must be partially working */
+ return (0);
+
+ usbd_abort_pipe(sc->sc_ipipe);
+ usbd_close_pipe(sc->sc_ipipe);
+
+ nports = hub->hubdesc.bNbrPorts;
+ for(port = 0; port < nports; port++) {
+ rup = &hub->ports[port];
+ if (rup->device)
+ usb_disconnect_port(rup, self);
+ }
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub,
+ USBDEV(sc->sc_dev));
+
+ free(hub, M_USBDEV);
+ sc->sc_hub->hub = NULL;
+
+ return (0);
+}
+
+#if defined(__FreeBSD__)
+/* Called when a device has been detached from it */
+Static void
+uhub_child_detached(device_t self, device_t child)
+{
+ struct uhub_softc *sc = device_get_softc(self);
+ usbd_device_handle devhub = sc->sc_hub;
+ usbd_device_handle dev;
+ int nports;
+ int port;
+ int i;
+
+ if (!devhub->hub)
+ /* should never happen; children are only created after init */
+ panic("hub not fully initialised, but child deleted?");
+
+ nports = devhub->hub->hubdesc.bNbrPorts;
+ for (port = 0; port < nports; port++) {
+ dev = devhub->hub->ports[port].device;
+ if (dev && dev->subdevs) {
+ for (i = 0; dev->subdevs[i]; i++) {
+ if (dev->subdevs[i] == child) {
+ dev->subdevs[i] = NULL;
+ return;
+ }
+ }
+ }
+ }
+}
+
+Static void
+uhub_driver_added(device_t _dev, driver_t *_driver)
+{
+ /* Don't do anything, as reprobing does not work currently. We should
+ * really call through to usbd_new_device or a function along those
+ * lines that reinitialises the device if it is not owned by any
+ * driver. But this is complicated. Manual replugging by the user is
+ * easier.
+ */
+
+ ;
+}
+#endif
+
+
+/*
+ * Hub interrupt.
+ * This an indication that some port has changed status.
+ * Notify the bus event handler thread that we need
+ * to be explored again.
+ */
+void
+uhub_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ struct uhub_softc *sc = addr;
+
+ DPRINTFN(5,("uhub_intr: sc=%p\n", sc));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_ipipe);
+ else if (status == USBD_NORMAL_COMPLETION)
+ usb_needs_explore(sc->sc_hub);
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(uhub, usb, uhubroot_driver, uhubroot_devclass, 0, 0);
+DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, usbd_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/ukbd.c b/sys/dev/usb/ukbd.c
new file mode 100644
index 0000000..1f72ebf
--- /dev/null
+++ b/sys/dev/usb/ukbd.c
@@ -0,0 +1,1486 @@
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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.
+ *
+ * Modifications for SUN TYPE 6 USB Keyboard by
+ * Jörg Peter Schley (jps@scxnet.de)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include "opt_kbd.h"
+#include "opt_ukbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ioccom.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500000
+#include <sys/limits.h>
+#else
+#include <machine/limits.h>
+#endif
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/vnode.h>
+#include <sys/sysctl.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/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+#include <dev/usb/hid.h>
+
+#include <sys/kbio.h>
+#include <dev/kbd/kbdreg.h>
+
+#define UKBD_EMULATE_ATSCANCODE 1
+
+#define DRIVER_NAME "ukbd"
+
+#define delay(d) DELAY(d)
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ukbddebug) logprintf x
+#define DPRINTFN(n,x) if (ukbddebug>(n)) logprintf x
+int ukbddebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RW,
+ &ukbddebug, 0, "ukbd debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UPROTO_BOOT_KEYBOARD 1
+
+#define NKEYCODE 6
+
+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 MAXKEYS (NMOD+2*NKEYCODE)
+
+typedef struct ukbd_softc {
+ device_t sc_dev; /* base device */
+} ukbd_softc_t;
+
+#define UKBD_CHUNK 128 /* chunk size for read */
+#define UKBD_BSIZE 1020 /* buffer size */
+
+typedef void usbd_intr_t(usbd_xfer_handle, usbd_private_handle, usbd_status);
+typedef void usbd_disco_t(void *);
+
+Static int ukbd_resume(device_t self);
+Static usbd_intr_t ukbd_intr;
+Static int ukbd_driver_load(module_t mod, int what, void *arg);
+
+USB_DECLARE_DRIVER_INIT(ukbd, DEVMETHOD(device_resume, ukbd_resume));
+
+USB_MATCH(ukbd)
+{
+ USB_MATCH_START(ukbd, uaa);
+
+ keyboard_switch_t *sw;
+ void *arg[2];
+ int unit = device_get_unit(self);
+
+ sw = kbd_get_switch(DRIVER_NAME);
+ if (sw == NULL)
+ return (UMATCH_NONE);
+
+ arg[0] = (void *)uaa;
+ arg[1] = (void *)ukbd_intr;
+ if ((*sw->probe)(unit, (void *)arg, 0))
+ return (UMATCH_NONE);
+
+ return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+}
+
+USB_ATTACH(ukbd)
+{
+ USB_ATTACH_START(ukbd, sc, uaa);
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ char devinfo[1024];
+
+ keyboard_switch_t *sw;
+ keyboard_t *kbd;
+ void *arg[2];
+ int unit = device_get_unit(self);
+
+ sw = kbd_get_switch(DRIVER_NAME);
+ if (sw == NULL)
+ USB_ATTACH_ERROR_RETURN;
+
+ id = usbd_get_interface_descriptor(iface);
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
+ devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
+
+ arg[0] = (void *)uaa;
+ arg[1] = (void *)ukbd_intr;
+ kbd = NULL;
+ if ((*sw->probe)(unit, (void *)arg, 0))
+ USB_ATTACH_ERROR_RETURN;
+ if ((*sw->init)(unit, &kbd, (void *)arg, 0))
+ USB_ATTACH_ERROR_RETURN;
+ (*sw->enable)(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (kbd_attach(kbd))
+ USB_ATTACH_ERROR_RETURN;
+#endif
+ if (bootverbose)
+ (*sw->diag)(kbd, bootverbose);
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+int
+ukbd_detach(device_t self)
+{
+ keyboard_t *kbd;
+ int error;
+
+ kbd = kbd_get_keyboard(kbd_find_keyboard(DRIVER_NAME,
+ device_get_unit(self)));
+ if (kbd == NULL) {
+ DPRINTF(("%s: keyboard not attached!?\n", USBDEVNAME(self)));
+ return ENXIO;
+ }
+ (*kbdsw[kbd->kb_index]->disable)(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ error = kbd_detach(kbd);
+ if (error)
+ return error;
+#endif
+ error = (*kbdsw[kbd->kb_index]->term)(kbd);
+ if (error)
+ return error;
+
+ DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
+
+ return (0);
+}
+
+Static int
+ukbd_resume(device_t self)
+{
+ keyboard_t *kbd;
+
+ kbd = kbd_get_keyboard(kbd_find_keyboard(DRIVER_NAME,
+ device_get_unit(self)));
+ if (kbd)
+ (*kbdsw[kbd->kb_index]->clear_state)(kbd);
+ return (0);
+}
+
+void
+ukbd_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ keyboard_t *kbd = (keyboard_t *)addr;
+
+ (*kbdsw[kbd->kb_index]->intr)(kbd, (void *)status);
+}
+
+DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0);
+
+
+#define UKBD_DEFAULT 0
+
+#define KEY_ERROR 0x01
+
+#define KEY_PRESS 0
+#define KEY_RELEASE 0x400
+#define KEY_INDEX(c) ((c) & ~KEY_RELEASE)
+
+#define SCAN_PRESS 0
+#define SCAN_RELEASE 0x80
+#define SCAN_PREFIX_E0 0x100
+#define SCAN_PREFIX_E1 0x200
+#define SCAN_PREFIX_CTL 0x400
+#define SCAN_PREFIX_SHIFT 0x800
+#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL \
+ | SCAN_PREFIX_SHIFT)
+#define SCAN_CHAR(c) ((c) & 0x7f)
+
+#define NMOD 8
+Static struct {
+ int mask, key;
+} ukbd_mods[NMOD] = {
+ { MOD_CONTROL_L, 0xe0 },
+ { MOD_CONTROL_R, 0xe4 },
+ { MOD_SHIFT_L, 0xe1 },
+ { MOD_SHIFT_R, 0xe5 },
+ { MOD_ALT_L, 0xe2 },
+ { MOD_ALT_R, 0xe6 },
+ { MOD_WIN_L, 0xe3 },
+ { MOD_WIN_R, 0xe7 },
+};
+
+#define NN 0 /* no translation */
+/*
+ * Translate USB keycodes to AT keyboard scancodes.
+ */
+/*
+ * FIXME: Mac USB keyboard generates:
+ * 0x53: keypad NumLock/Clear
+ * 0x66: Power
+ * 0x67: keypad =
+ * 0x68: F13
+ * 0x69: F14
+ * 0x6a: F15
+ */
+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, 43, 39, 40, 41, 51, 52, /* 30 - 37 */
+ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
+ 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */
+ 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */
+ 97, 100, 95, 69, 91, 55, 74, 78, /* 50 - 57 */
+ 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
+ 72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
+ NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */
+ 109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */
+ 121, 120, NN, NN, NN, NN, NN, 115, /* 80 - 87 */
+ 112, 125, 121, 123, 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 */
+ 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
+};
+
+typedef struct ukbd_state {
+ usbd_interface_handle ks_iface; /* interface */
+ usbd_pipe_handle ks_intrpipe; /* interrupt pipe */
+ struct usb_attach_arg *ks_uaa;
+ int ks_ep_addr;
+
+ struct ukbd_data ks_ndata;
+ struct ukbd_data ks_odata;
+ u_long ks_ntime[NKEYCODE];
+ u_long ks_otime[NKEYCODE];
+
+#define INPUTBUFSIZE (NMOD + 2*NKEYCODE)
+ u_int ks_input[INPUTBUFSIZE]; /* input buffer */
+ int ks_inputs;
+ int ks_inputhead;
+ int ks_inputtail;
+
+ int ks_ifstate;
+#define INTRENABLED (1 << 0)
+#define DISCONNECTED (1 << 1)
+
+ struct callout_handle ks_timeout_handle;
+
+ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
+ int ks_flags; /* flags */
+#define COMPOSE (1 << 0)
+ int ks_polling;
+ int ks_state; /* shift/lock key state */
+ int ks_accents; /* accent key index (> 0) */
+ u_int ks_composed_char; /* composed char code (> 0) */
+#ifdef UKBD_EMULATE_ATSCANCODE
+ u_int ks_buffered_char[2];
+#endif
+} ukbd_state_t;
+
+/* keyboard driver declaration */
+Static int ukbd_configure(int flags);
+Static kbd_probe_t ukbd_probe;
+Static kbd_init_t ukbd_init;
+Static kbd_term_t ukbd_term;
+Static kbd_intr_t ukbd_interrupt;
+Static kbd_test_if_t ukbd_test_if;
+Static kbd_enable_t ukbd_enable;
+Static kbd_disable_t ukbd_disable;
+Static kbd_read_t ukbd_read;
+Static kbd_check_t ukbd_check;
+Static kbd_read_char_t ukbd_read_char;
+Static kbd_check_char_t ukbd_check_char;
+Static kbd_ioctl_t ukbd_ioctl;
+Static kbd_lock_t ukbd_lock;
+Static kbd_clear_state_t ukbd_clear_state;
+Static kbd_get_state_t ukbd_get_state;
+Static kbd_set_state_t ukbd_set_state;
+Static kbd_poll_mode_t ukbd_poll;
+
+keyboard_switch_t ukbdsw = {
+ ukbd_probe,
+ ukbd_init,
+ ukbd_term,
+ ukbd_interrupt,
+ ukbd_test_if,
+ ukbd_enable,
+ ukbd_disable,
+ ukbd_read,
+ ukbd_check,
+ ukbd_read_char,
+ ukbd_check_char,
+ ukbd_ioctl,
+ ukbd_lock,
+ ukbd_clear_state,
+ ukbd_get_state,
+ ukbd_set_state,
+ genkbd_get_fkeystr,
+ ukbd_poll,
+ genkbd_diag,
+};
+
+KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure);
+
+/* local functions */
+Static int ukbd_enable_intr(keyboard_t *kbd, int on,
+ usbd_intr_t *func);
+Static timeout_t ukbd_timeout;
+
+Static int ukbd_getc(ukbd_state_t *state);
+Static int probe_keyboard(struct usb_attach_arg *uaa, int flags);
+Static int init_keyboard(ukbd_state_t *state, int *type,
+ int flags);
+Static void set_leds(ukbd_state_t *state, int leds);
+Static int set_typematic(keyboard_t *kbd, int code);
+#ifdef UKBD_EMULATE_ATSCANCODE
+Static int keycode2scancode(int keycode, int shift, int up);
+#endif
+
+/* local variables */
+
+/* the initial key map, accent map and fkey strings */
+#ifdef UKBD_DFLT_KEYMAP
+#define KBD_DFLT_KEYMAP
+#include "ukbdmap.h"
+#endif
+#include <dev/kbd/kbdtables.h>
+
+/* structures for the default keyboard */
+Static keyboard_t default_kbd;
+Static ukbd_state_t default_kbd_state;
+Static keymap_t default_keymap;
+Static accentmap_t default_accentmap;
+Static fkeytab_t default_fkeytab[NUM_FKEYS];
+
+/*
+ * The back door to the keyboard driver!
+ * This function is called by the console driver, via the kbdio module,
+ * to tickle keyboard drivers when the low-level console is being initialized.
+ * Almost nothing in the kernel has been initialied yet. Try to probe
+ * keyboards if possible.
+ * NOTE: because of the way the low-level conole is initialized, this routine
+ * may be called more than once!!
+ */
+Static int
+ukbd_configure(int flags)
+{
+ return 0;
+
+#if 0 /* not yet */
+ keyboard_t *kbd;
+ device_t device;
+ struct usb_attach_arg *uaa;
+ void *arg[2];
+
+ device = devclass_get_device(ukbd_devclass, UKBD_DEFAULT);
+ if (device == NULL)
+ return 0;
+ uaa = (struct usb_attach_arg *)device_get_ivars(device);
+ if (uaa == NULL)
+ return 0;
+
+ /* probe the default keyboard */
+ arg[0] = (void *)uaa;
+ arg[1] = (void *)ukbd_intr;
+ kbd = NULL;
+ if (ukbd_probe(UKBD_DEFAULT, arg, flags))
+ return 0;
+ if (ukbd_init(UKBD_DEFAULT, &kbd, arg, flags))
+ return 0;
+
+ /* return the number of found keyboards */
+ return 1;
+#endif
+}
+
+/* low-level functions */
+
+/* detect a keyboard */
+Static int
+ukbd_probe(int unit, void *arg, int flags)
+{
+ void **data;
+ struct usb_attach_arg *uaa;
+
+ data = (void **)arg;
+ uaa = (struct usb_attach_arg *)data[0];
+
+ /* XXX */
+ if (unit == UKBD_DEFAULT) {
+ if (KBD_IS_PROBED(&default_kbd))
+ return 0;
+ }
+ if (probe_keyboard(uaa, flags))
+ return ENXIO;
+ return 0;
+}
+
+/* reset and initialize the device */
+Static int
+ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+ keyboard_t *kbd;
+ ukbd_state_t *state;
+ keymap_t *keymap;
+ accentmap_t *accmap;
+ fkeytab_t *fkeymap;
+ int fkeymap_size;
+ void **data = (void **)arg;
+ struct usb_attach_arg *uaa = (struct usb_attach_arg *)data[0];
+
+ /* XXX */
+ if (unit == UKBD_DEFAULT) {
+ *kbdp = kbd = &default_kbd;
+ if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd))
+ return 0;
+ state = &default_kbd_state;
+ keymap = &default_keymap;
+ accmap = &default_accentmap;
+ fkeymap = default_fkeytab;
+ fkeymap_size =
+ sizeof(default_fkeytab)/sizeof(default_fkeytab[0]);
+ } else if (*kbdp == NULL) {
+ *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT);
+ if (kbd == NULL)
+ return ENOMEM;
+ bzero(kbd, sizeof(*kbd));
+ state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT);
+ keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT);
+ accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT);
+ fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT);
+ fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]);
+ if ((state == NULL) || (keymap == NULL) || (accmap == NULL)
+ || (fkeymap == NULL)) {
+ if (state != NULL)
+ free(state, M_DEVBUF);
+ if (keymap != NULL)
+ free(keymap, M_DEVBUF);
+ if (accmap != NULL)
+ free(accmap, M_DEVBUF);
+ if (fkeymap != NULL)
+ free(fkeymap, M_DEVBUF);
+ free(kbd, M_DEVBUF);
+ return ENOMEM;
+ }
+ } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) {
+ return 0;
+ } else {
+ kbd = *kbdp;
+ state = (ukbd_state_t *)kbd->kb_data;
+ keymap = kbd->kb_keymap;
+ accmap = kbd->kb_accentmap;
+ fkeymap = kbd->kb_fkeytab;
+ fkeymap_size = kbd->kb_fkeytab_size;
+ }
+
+ if (!KBD_IS_PROBED(kbd)) {
+ kbd_init_struct(kbd, DRIVER_NAME, KB_OTHER, unit, flags, 0, 0);
+ bzero(state, sizeof(*state));
+ bcopy(&key_map, keymap, sizeof(key_map));
+ bcopy(&accent_map, accmap, sizeof(accent_map));
+ bcopy(fkey_tab, fkeymap,
+ imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
+ kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
+ kbd->kb_data = (void *)state;
+
+ if (probe_keyboard(uaa, flags))
+ return ENXIO;
+ else
+ KBD_FOUND_DEVICE(kbd);
+ ukbd_clear_state(kbd);
+ state->ks_mode = K_XLATE;
+ state->ks_iface = uaa->iface;
+ state->ks_uaa = uaa;
+ state->ks_ifstate = 0;
+ callout_handle_init(&state->ks_timeout_handle);
+ /*
+ * FIXME: set the initial value for lock keys in ks_state
+ * according to the BIOS data?
+ */
+ KBD_PROBE_DONE(kbd);
+ }
+ if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
+ if (KBD_HAS_DEVICE(kbd)
+ && init_keyboard((ukbd_state_t *)kbd->kb_data,
+ &kbd->kb_type, kbd->kb_flags))
+ return ENXIO;
+ ukbd_ioctl(kbd, KDSETLED, (caddr_t)&(state->ks_state));
+ KBD_INIT_DONE(kbd);
+ }
+ if (!KBD_IS_CONFIGURED(kbd)) {
+ if (kbd_register(kbd) < 0)
+ return ENXIO;
+ if (ukbd_enable_intr(kbd, TRUE, (usbd_intr_t *)data[1]) == 0)
+ ukbd_timeout((void *)kbd);
+ KBD_CONFIG_DONE(kbd);
+ }
+
+ return 0;
+}
+
+Static int
+ukbd_enable_intr(keyboard_t *kbd, int on, usbd_intr_t *func)
+{
+ ukbd_state_t *state = (ukbd_state_t *)kbd->kb_data;
+ usbd_status err;
+
+ if (on) {
+ /* Set up interrupt pipe. */
+ if (state->ks_ifstate & INTRENABLED)
+ return EBUSY;
+
+ state->ks_ifstate |= INTRENABLED;
+ err = usbd_open_pipe_intr(state->ks_iface, state->ks_ep_addr,
+ USBD_SHORT_XFER_OK,
+ &state->ks_intrpipe, kbd,
+ &state->ks_ndata,
+ sizeof(state->ks_ndata), func,
+ USBD_DEFAULT_INTERVAL);
+ if (err)
+ return (EIO);
+ } else {
+ /* Disable interrupts. */
+ usbd_abort_pipe(state->ks_intrpipe);
+ usbd_close_pipe(state->ks_intrpipe);
+
+ state->ks_ifstate &= ~INTRENABLED;
+ }
+
+ return (0);
+}
+
+/* finish using this keyboard */
+Static int
+ukbd_term(keyboard_t *kbd)
+{
+ ukbd_state_t *state;
+ int error;
+ int s;
+
+ s = splusb();
+
+ state = (ukbd_state_t *)kbd->kb_data;
+ DPRINTF(("ukbd_term: ks_ifstate=0x%x\n", state->ks_ifstate));
+
+ untimeout(ukbd_timeout, (void *)kbd, state->ks_timeout_handle);
+ callout_handle_init(&state->ks_timeout_handle);
+
+ if (state->ks_ifstate & INTRENABLED)
+ ukbd_enable_intr(kbd, FALSE, NULL);
+ if (state->ks_ifstate & INTRENABLED) {
+ splx(s);
+ DPRINTF(("ukbd_term: INTRENABLED!\n"));
+ return ENXIO;
+ }
+
+ error = kbd_unregister(kbd);
+ DPRINTF(("ukbd_term: kbd_unregister() %d\n", error));
+ if (error == 0) {
+ kbd->kb_flags = 0;
+ if (kbd != &default_kbd) {
+ free(kbd->kb_keymap, M_DEVBUF);
+ free(kbd->kb_accentmap, M_DEVBUF);
+ free(kbd->kb_fkeytab, M_DEVBUF);
+ free(state, M_DEVBUF);
+ free(kbd, M_DEVBUF);
+ }
+ }
+
+ splx(s);
+ return error;
+}
+
+
+/* keyboard interrupt routine */
+
+Static void
+ukbd_timeout(void *arg)
+{
+ keyboard_t *kbd;
+ ukbd_state_t *state;
+ int s;
+
+ kbd = (keyboard_t *)arg;
+ state = (ukbd_state_t *)kbd->kb_data;
+ s = splusb();
+ (*kbdsw[kbd->kb_index]->intr)(kbd, (void *)USBD_NORMAL_COMPLETION);
+ state->ks_timeout_handle = timeout(ukbd_timeout, arg, hz/40);
+ splx(s);
+}
+
+Static int
+ukbd_interrupt(keyboard_t *kbd, void *arg)
+{
+ usbd_status status = (usbd_status)arg;
+ ukbd_state_t *state;
+ struct ukbd_data *ud;
+ struct timeval tv;
+ u_long now;
+ int mod, omod;
+ int key, c;
+ int i, j;
+
+ DPRINTFN(5, ("ukbd_intr: status=%d\n", status));
+ if (status == USBD_CANCELLED)
+ return 0;
+
+ state = (ukbd_state_t *)kbd->kb_data;
+ ud = &state->ks_ndata;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ukbd_intr: status=%d\n", status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(state->ks_intrpipe);
+ return 0;
+ }
+
+ if (ud->keycode[0] == KEY_ERROR)
+ return 0; /* ignore */
+
+ getmicrouptime(&tv);
+ now = (u_long)tv.tv_sec*1000 + (u_long)tv.tv_usec/1000;
+
+#define ADDKEY1(c) \
+ if (state->ks_inputs < INPUTBUFSIZE) { \
+ state->ks_input[state->ks_inputtail] = (c); \
+ ++state->ks_inputs; \
+ state->ks_inputtail = (state->ks_inputtail + 1)%INPUTBUFSIZE; \
+ }
+
+ mod = ud->modifiers;
+ omod = state->ks_odata.modifiers;
+ if (mod != omod) {
+ for (i = 0; i < NMOD; i++)
+ if (( mod & ukbd_mods[i].mask) !=
+ (omod & ukbd_mods[i].mask))
+ ADDKEY1(ukbd_mods[i].key |
+ (mod & ukbd_mods[i].mask
+ ? KEY_PRESS : KEY_RELEASE));
+ }
+
+ /* Check for released keys. */
+ for (i = 0; i < NKEYCODE; i++) {
+ key = state->ks_odata.keycode[i];
+ if (key == 0)
+ continue;
+ for (j = 0; j < NKEYCODE; j++) {
+ if (ud->keycode[j] == 0)
+ continue;
+ if (key == ud->keycode[j])
+ goto rfound;
+ }
+ ADDKEY1(key | KEY_RELEASE);
+ rfound:
+ ;
+ }
+
+ /* Check for pressed keys. */
+ for (i = 0; i < NKEYCODE; i++) {
+ key = ud->keycode[i];
+ if (key == 0)
+ continue;
+ state->ks_ntime[i] = now + kbd->kb_delay1;
+ for (j = 0; j < NKEYCODE; j++) {
+ if (state->ks_odata.keycode[j] == 0)
+ continue;
+ if (key == state->ks_odata.keycode[j]) {
+ state->ks_ntime[i] = state->ks_otime[j];
+ if (state->ks_otime[j] > now)
+ goto pfound;
+ state->ks_ntime[i] = now + kbd->kb_delay2;
+ break;
+ }
+ }
+ ADDKEY1(key | KEY_PRESS);
+ pfound:
+ ;
+ }
+
+ state->ks_odata = *ud;
+ bcopy(state->ks_ntime, state->ks_otime, sizeof(state->ks_ntime));
+ if (state->ks_inputs <= 0)
+ return 0;
+
+#ifdef USB_DEBUG
+ for (i = state->ks_inputhead, j = 0; j < state->ks_inputs; ++j,
+ i = (i + 1)%INPUTBUFSIZE) {
+ c = state->ks_input[i];
+ DPRINTF(("0x%x (%d) %s\n", c, c,
+ (c & KEY_RELEASE) ? "released":"pressed"));
+ }
+ if (ud->modifiers)
+ DPRINTF(("mod:0x%04x ", ud->modifiers));
+ for (i = 0; i < NKEYCODE; i++) {
+ if (ud->keycode[i])
+ DPRINTF(("%d ", ud->keycode[i]));
+ }
+ DPRINTF(("\n"));
+#endif /* USB_DEBUG */
+
+ if (state->ks_polling)
+ return 0;
+
+ if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
+ /* let the callback function to process the input */
+ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
+ kbd->kb_callback.kc_arg);
+ } else {
+ /* read and discard the input; no one is waiting for it */
+ do {
+ c = ukbd_read_char(kbd, FALSE);
+ } while (c != NOKEY);
+ }
+
+ return 0;
+}
+
+Static int
+ukbd_getc(ukbd_state_t *state)
+{
+ int c;
+ int s;
+
+ if (state->ks_polling) {
+ DPRINTFN(1,("ukbd_getc: polling\n"));
+ s = splusb();
+ while (state->ks_inputs <= 0)
+ usbd_dopoll(state->ks_iface);
+ splx(s);
+ }
+ s = splusb();
+ if (state->ks_inputs <= 0) {
+ c = -1;
+ } else {
+ c = state->ks_input[state->ks_inputhead];
+ --state->ks_inputs;
+ state->ks_inputhead = (state->ks_inputhead + 1)%INPUTBUFSIZE;
+ }
+ splx(s);
+ return c;
+}
+
+/* test the interface to the device */
+Static int
+ukbd_test_if(keyboard_t *kbd)
+{
+ return 0;
+}
+
+/*
+ * Enable the access to the device; until this function is called,
+ * the client cannot read from the keyboard.
+ */
+Static int
+ukbd_enable(keyboard_t *kbd)
+{
+ int s;
+
+ s = splusb();
+ KBD_ACTIVATE(kbd);
+ splx(s);
+ return 0;
+}
+
+/* disallow the access to the device */
+Static int
+ukbd_disable(keyboard_t *kbd)
+{
+ int s;
+
+ s = splusb();
+ KBD_DEACTIVATE(kbd);
+ splx(s);
+ return 0;
+}
+
+/* read one byte from the keyboard if it's allowed */
+Static int
+ukbd_read(keyboard_t *kbd, int wait)
+{
+ ukbd_state_t *state;
+ int usbcode;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ int keycode;
+ int scancode;
+#endif
+
+ state = (ukbd_state_t *)kbd->kb_data;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ if (state->ks_buffered_char[0]) {
+ scancode = state->ks_buffered_char[0];
+ if (scancode & SCAN_PREFIX) {
+ state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX;
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ } else {
+ state->ks_buffered_char[0] = state->ks_buffered_char[1];
+ state->ks_buffered_char[1] = 0;
+ return scancode;
+ }
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ /* XXX */
+ usbcode = ukbd_getc(state);
+ if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1))
+ return -1;
+ ++kbd->kb_count;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN)
+ return -1;
+
+ scancode = keycode2scancode(keycode, state->ks_ndata.modifiers,
+ usbcode & KEY_RELEASE);
+ if (scancode & SCAN_PREFIX) {
+ if (scancode & SCAN_PREFIX_CTL) {
+ state->ks_buffered_char[0] =
+ 0x1d | (scancode & SCAN_RELEASE); /* Ctrl */
+ state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX;
+ } else if (scancode & SCAN_PREFIX_SHIFT) {
+ state->ks_buffered_char[0] =
+ 0x2a | (scancode & SCAN_RELEASE); /* Shift */
+ state->ks_buffered_char[1] =
+ scancode & ~SCAN_PREFIX_SHIFT;
+ } else {
+ state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX;
+ state->ks_buffered_char[1] = 0;
+ }
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ return scancode;
+#else /* !UKBD_EMULATE_ATSCANCODE */
+ return usbcode;
+#endif /* UKBD_EMULATE_ATSCANCODE */
+}
+
+/* check if data is waiting */
+Static int
+ukbd_check(keyboard_t *kbd)
+{
+ if (!KBD_IS_ACTIVE(kbd))
+ return FALSE;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ if (((ukbd_state_t *)kbd->kb_data)->ks_buffered_char[0])
+ return TRUE;
+#endif
+ if (((ukbd_state_t *)kbd->kb_data)->ks_inputs > 0)
+ return TRUE;
+ return FALSE;
+}
+
+/* read char from the keyboard */
+Static u_int
+ukbd_read_char(keyboard_t *kbd, int wait)
+{
+ ukbd_state_t *state;
+ u_int action;
+ int usbcode;
+ int keycode;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ int scancode;
+#endif
+
+ state = (ukbd_state_t *)kbd->kb_data;
+next_code:
+ /* do we have a composed char to return? */
+ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
+ action = state->ks_composed_char;
+ state->ks_composed_char = 0;
+ if (action > UCHAR_MAX)
+ return ERRKEY;
+ return action;
+ }
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ /* do we have a pending raw scan code? */
+ if (state->ks_mode == K_RAW) {
+ if (state->ks_buffered_char[0]) {
+ scancode = state->ks_buffered_char[0];
+ if (scancode & SCAN_PREFIX) {
+ state->ks_buffered_char[0] =
+ scancode & ~SCAN_PREFIX;
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ } else {
+ state->ks_buffered_char[0] =
+ state->ks_buffered_char[1];
+ state->ks_buffered_char[1] = 0;
+ return scancode;
+ }
+ }
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ /* see if there is something in the keyboard port */
+ /* XXX */
+ usbcode = ukbd_getc(state);
+ if (usbcode == -1)
+ return NOKEY;
+ ++kbd->kb_count;
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ /* USB key index -> key code -> AT scan code */
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN)
+ return NOKEY;
+
+ /* return an AT scan code for the K_RAW mode */
+ if (state->ks_mode == K_RAW) {
+ scancode = keycode2scancode(keycode, state->ks_ndata.modifiers,
+ usbcode & KEY_RELEASE);
+ if (scancode & SCAN_PREFIX) {
+ if (scancode & SCAN_PREFIX_CTL) {
+ state->ks_buffered_char[0] =
+ 0x1d | (scancode & SCAN_RELEASE);
+ state->ks_buffered_char[1] =
+ scancode & ~SCAN_PREFIX;
+ } else if (scancode & SCAN_PREFIX_SHIFT) {
+ state->ks_buffered_char[0] =
+ 0x2a | (scancode & SCAN_RELEASE);
+ state->ks_buffered_char[1] =
+ scancode & ~SCAN_PREFIX_SHIFT;
+ } else {
+ state->ks_buffered_char[0] =
+ scancode & ~SCAN_PREFIX;
+ state->ks_buffered_char[1] = 0;
+ }
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ return scancode;
+ }
+#else /* !UKBD_EMULATE_ATSCANCODE */
+ /* return the byte as is for the K_RAW mode */
+ if (state->ks_mode == K_RAW)
+ return usbcode;
+
+ /* USB key index -> key code */
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN)
+ return NOKEY;
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ switch (keycode) {
+ case 0x38: /* left alt (compose key) */
+ if (usbcode & KEY_RELEASE) {
+ if (state->ks_flags & COMPOSE) {
+ state->ks_flags &= ~COMPOSE;
+ if (state->ks_composed_char > UCHAR_MAX)
+ state->ks_composed_char = 0;
+ }
+ } else {
+ if (!(state->ks_flags & COMPOSE)) {
+ state->ks_flags |= COMPOSE;
+ state->ks_composed_char = 0;
+ }
+ }
+ break;
+ /* XXX: I don't like these... */
+ case 0x5c: /* print screen */
+ if (state->ks_flags & ALTS)
+ keycode = 0x54; /* sysrq */
+ break;
+ case 0x68: /* pause/break */
+ if (state->ks_flags & CTLS)
+ keycode = 0x6c; /* break */
+ break;
+ }
+
+ /* return the key code in the K_CODE mode */
+ if (usbcode & KEY_RELEASE)
+ keycode |= SCAN_RELEASE;
+ if (state->ks_mode == K_CODE)
+ return keycode;
+
+ /* compose a character code */
+ if (state->ks_flags & COMPOSE) {
+ switch (keycode) {
+ /* key pressed, process it */
+ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
+ state->ks_composed_char *= 10;
+ state->ks_composed_char += keycode - 0x40;
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+ case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
+ state->ks_composed_char *= 10;
+ state->ks_composed_char += keycode - 0x47;
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+ case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
+ state->ks_composed_char *= 10;
+ state->ks_composed_char += keycode - 0x4E;
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+ case 0x52: /* keypad 0 */
+ state->ks_composed_char *= 10;
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+
+ /* key released, no interest here */
+ case SCAN_RELEASE | 0x47:
+ case SCAN_RELEASE | 0x48:
+ case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */
+ case SCAN_RELEASE | 0x4B:
+ case SCAN_RELEASE | 0x4C:
+ case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */
+ case SCAN_RELEASE | 0x4F:
+ case SCAN_RELEASE | 0x50:
+ case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */
+ case SCAN_RELEASE | 0x52: /* keypad 0 */
+ goto next_code;
+
+ case 0x38: /* left alt key */
+ break;
+
+ default:
+ if (state->ks_composed_char > 0) {
+ state->ks_flags &= ~COMPOSE;
+ state->ks_composed_char = 0;
+ return ERRKEY;
+ }
+ break;
+ }
+ }
+
+ /* keycode to key action */
+ action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
+ keycode & SCAN_RELEASE, &state->ks_state,
+ &state->ks_accents);
+ if (action == NOKEY)
+ goto next_code;
+ else
+ return action;
+}
+
+/* check if char is waiting */
+Static int
+ukbd_check_char(keyboard_t *kbd)
+{
+ ukbd_state_t *state;
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return FALSE;
+ state = (ukbd_state_t *)kbd->kb_data;
+ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0))
+ return TRUE;
+ if (state->ks_inputs > 0)
+ return TRUE;
+ return FALSE;
+}
+
+/* some useful control functions */
+Static int
+ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ /* trasnlate LED_XXX bits into the device specific bits */
+ static u_char ledmap[8] = {
+ 0, 2, 1, 3, 4, 6, 5, 7,
+ };
+ ukbd_state_t *state = kbd->kb_data;
+ int s;
+ int i;
+
+ s = splusb();
+ switch (cmd) {
+
+ case KDGKBMODE: /* get keyboard mode */
+ *(int *)arg = state->ks_mode;
+ break;
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*(int *)arg) {
+ case K_XLATE:
+ if (state->ks_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ state->ks_state &= ~LOCK_MASK;
+ state->ks_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALLTHROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (state->ks_mode != *(int *)arg) {
+ ukbd_clear_state(kbd);
+ state->ks_mode = *(int *)arg;
+ }
+ break;
+ default:
+ splx(s);
+ return EINVAL;
+ }
+ break;
+
+ case KDGETLED: /* get keyboard LED */
+ *(int *)arg = KBD_LED_VAL(kbd);
+ break;
+ case KDSETLED: /* set keyboard LED */
+ /* NOTE: lock key state in ks_state won't be changed */
+ if (*(int *)arg & ~LOCK_MASK) {
+ splx(s);
+ return EINVAL;
+ }
+ i = *(int *)arg;
+ /* replace CAPS LED with ALTGR LED for ALTGR keyboards */
+ if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
+ if (i & ALKED)
+ i |= CLKED;
+ else
+ i &= ~CLKED;
+ }
+ if (KBD_HAS_DEVICE(kbd)) {
+ set_leds(state, ledmap[i & LED_MASK]);
+ /* XXX: error check? */
+ }
+ KBD_LED_VAL(kbd) = *(int *)arg;
+ break;
+
+ case KDGKBSTATE: /* get lock key state */
+ *(int *)arg = state->ks_state & LOCK_MASK;
+ break;
+ case KDSKBSTATE: /* set lock key state */
+ if (*(int *)arg & ~LOCK_MASK) {
+ splx(s);
+ return EINVAL;
+ }
+ state->ks_state &= ~LOCK_MASK;
+ state->ks_state |= *(int *)arg;
+ splx(s);
+ /* set LEDs and quit */
+ return ukbd_ioctl(kbd, KDSETLED, arg);
+
+ case KDSETREPEAT: /* set keyboard repeat rate (new interface) */
+ splx(s);
+ if (!KBD_HAS_DEVICE(kbd))
+ return 0;
+ if (((int *)arg)[1] < 0)
+ return EINVAL;
+ if (((int *)arg)[0] < 0)
+ return EINVAL;
+ else if (((int *)arg)[0] == 0) /* fastest possible value */
+ kbd->kb_delay1 = 200;
+ else
+ kbd->kb_delay1 = ((int *)arg)[0];
+ kbd->kb_delay2 = ((int *)arg)[1];
+ return 0;
+
+ case KDSETRAD: /* set keyboard repeat rate (old interface) */
+ splx(s);
+ return set_typematic(kbd, *(int *)arg);
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ case PIO_KEYMAPENT: /* set keyboard translation table entry */
+ case PIO_DEADKEYMAP: /* set accent key translation table */
+ state->ks_accents = 0;
+ /* FALLTHROUGH */
+ default:
+ splx(s);
+ return genkbd_commonioctl(kbd, cmd, arg);
+
+#ifdef USB_DEBUG
+ case USB_SETDEBUG:
+ ukbddebug = *(int *)arg;
+ break;
+#endif
+ }
+
+ splx(s);
+ return 0;
+}
+
+/* lock the access to the keyboard */
+Static int
+ukbd_lock(keyboard_t *kbd, int lock)
+{
+ /* XXX ? */
+ return TRUE;
+}
+
+/* clear the internal state of the keyboard */
+Static void
+ukbd_clear_state(keyboard_t *kbd)
+{
+ ukbd_state_t *state;
+
+ state = (ukbd_state_t *)kbd->kb_data;
+ state->ks_flags = 0;
+ state->ks_polling = 0;
+ state->ks_state &= LOCK_MASK; /* preserve locking key state */
+ state->ks_accents = 0;
+ state->ks_composed_char = 0;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ state->ks_buffered_char[0] = 0;
+ state->ks_buffered_char[1] = 0;
+#endif
+ bzero(&state->ks_ndata, sizeof(state->ks_ndata));
+ bzero(&state->ks_odata, sizeof(state->ks_odata));
+ bzero(&state->ks_ntime, sizeof(state->ks_ntime));
+ bzero(&state->ks_otime, sizeof(state->ks_otime));
+}
+
+/* save the internal state */
+Static int
+ukbd_get_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ if (len == 0)
+ return sizeof(ukbd_state_t);
+ if (len < sizeof(ukbd_state_t))
+ return -1;
+ bcopy(kbd->kb_data, buf, sizeof(ukbd_state_t));
+ return 0;
+}
+
+/* set the internal state */
+Static int
+ukbd_set_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ if (len < sizeof(ukbd_state_t))
+ return ENOMEM;
+ bcopy(buf, kbd->kb_data, sizeof(ukbd_state_t));
+ return 0;
+}
+
+Static int
+ukbd_poll(keyboard_t *kbd, int on)
+{
+ ukbd_state_t *state;
+ usbd_device_handle dev;
+ int s;
+
+ state = (ukbd_state_t *)kbd->kb_data;
+ usbd_interface2device_handle(state->ks_iface, &dev);
+
+ s = splusb();
+ if (on) {
+ if (state->ks_polling == 0)
+ usbd_set_polling(dev, on);
+ ++state->ks_polling;
+ } else {
+ --state->ks_polling;
+ if (state->ks_polling == 0)
+ usbd_set_polling(dev, on);
+ }
+ splx(s);
+ return 0;
+}
+
+/* local functions */
+
+Static int
+probe_keyboard(struct usb_attach_arg *uaa, int flags)
+{
+ usb_interface_descriptor_t *id;
+
+ if (!uaa->iface) /* we attach to ifaces only */
+ return EINVAL;
+
+ /* Check that this is a keyboard that speaks the boot protocol. */
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id
+ && id->bInterfaceClass == UICLASS_HID
+ && id->bInterfaceSubClass == UISUBCLASS_BOOT
+ && id->bInterfaceProtocol == UPROTO_BOOT_KEYBOARD)
+ return 0; /* found it */
+
+ return EINVAL;
+}
+
+Static int
+init_keyboard(ukbd_state_t *state, int *type, int flags)
+{
+ usb_endpoint_descriptor_t *ed;
+ usbd_status err;
+
+ *type = KB_OTHER;
+
+ state->ks_ifstate |= DISCONNECTED;
+
+ ed = usbd_interface2endpoint_descriptor(state->ks_iface, 0);
+ if (!ed) {
+ printf("ukbd: could not read endpoint descriptor\n");
+ return EIO;
+ }
+
+ DPRINTFN(10,("ukbd:init_keyboard: \
+bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
+ ed->bLength, ed->bDescriptorType,
+ UE_GET_ADDR(ed->bEndpointAddress),
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
+ UE_GET_XFERTYPE(ed->bmAttributes),
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
+ UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
+ printf("ukbd: unexpected endpoint\n");
+ return EINVAL;
+ }
+
+ if ((usbd_get_quirks(state->ks_uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) {
+ err = usbd_set_protocol(state->ks_iface, 0);
+ DPRINTFN(5, ("ukbd:init_keyboard: protocol set\n"));
+ if (err) {
+ printf("ukbd: set protocol failed\n");
+ return EIO;
+ }
+ }
+ /* Ignore if SETIDLE fails since it is not crucial. */
+ usbd_set_idle(state->ks_iface, 0, 0);
+
+ state->ks_ep_addr = ed->bEndpointAddress;
+ state->ks_ifstate &= ~DISCONNECTED;
+
+ return 0;
+}
+
+Static void
+set_leds(ukbd_state_t *state, int leds)
+{
+ u_int8_t res = leds;
+
+ DPRINTF(("ukbd:set_leds: state=%p leds=%d\n", state, leds));
+
+ usbd_set_report_async(state->ks_iface, UHID_OUTPUT_REPORT, 0, &res, 1);
+}
+
+Static int
+set_typematic(keyboard_t *kbd, int code)
+{
+ static int delays[] = { 250, 500, 750, 1000 };
+ static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504 };
+
+ if (code & ~0x7f)
+ return EINVAL;
+ kbd->kb_delay1 = delays[(code >> 5) & 3];
+ kbd->kb_delay2 = rates[code & 0x1f];
+ return 0;
+}
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+Static int
+keycode2scancode(int keycode, int shift, int up)
+{
+ static int scan[] = {
+ 0x1c, 0x1d, 0x35,
+ 0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */
+ 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f,
+ 0x50, 0x51, 0x52, 0x53,
+ 0x46, /* XXX Pause/Break */
+ 0x5b, 0x5c, 0x5d,
+ /* SUN TYPE 6 USB KEYBOARD */
+ 0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e,
+ 0x20,
+ };
+ int scancode;
+
+ scancode = keycode;
+ if ((keycode >= 89) && (keycode < 89 + sizeof(scan)/sizeof(scan[0])))
+ scancode = scan[keycode - 89] | SCAN_PREFIX_E0;
+ /* Pause/Break */
+ if ((keycode == 104) && !(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))
+ scancode = 0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL;
+ if (shift & (MOD_SHIFT_L | MOD_SHIFT_R))
+ scancode &= ~SCAN_PREFIX_SHIFT;
+ return (scancode | (up ? SCAN_RELEASE : SCAN_PRESS));
+}
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+Static int
+ukbd_driver_load(module_t mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ kbd_add_driver(&ukbd_kbd_driver);
+ break;
+ case MOD_UNLOAD:
+ kbd_delete_driver(&ukbd_kbd_driver);
+ break;
+ }
+ return usbd_driver_load(mod, what, 0);
+}
diff --git a/sys/dev/usb/ulpt.c b/sys/dev/usb/ulpt.c
new file mode 100644
index 0000000..0c43de5
--- /dev/null
+++ b/sys/dev/usb/ulpt.c
@@ -0,0 +1,883 @@
+/* $NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $ */
+
+/*
+ * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/fcntl.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#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 <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.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 PAGE_SIZE
+
+#define ULPT_READS_PER_SEC 5
+#define ULPT_READ_TIMO 10
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ulptdebug) logprintf x
+#define DPRINTFN(n,x) if (ulptdebug>(n)) logprintf x
+int ulptdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt");
+SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RW,
+ &ulptdebug, 0, "ulpt debug level");
+#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 {
+ USBBASEDEVICE sc_dev;
+ usbd_device_handle sc_udev; /* device */
+ usbd_interface_handle sc_iface; /* interface */
+ int sc_ifaceno;
+
+ int sc_out;
+ usbd_pipe_handle sc_out_pipe; /* bulk out pipe */
+ usbd_xfer_handle sc_out_xfer;
+ void *sc_out_buf;
+
+ int sc_in;
+ usbd_pipe_handle sc_in_pipe; /* bulk in pipe */
+ usbd_xfer_handle sc_in_xfer;
+ void *sc_in_buf;
+
+ usb_callout_t sc_read_callout;
+ int sc_has_callout;
+
+ 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;
+
+ int sc_refcnt;
+ u_char sc_dying;
+
+#if defined(__FreeBSD__)
+ struct cdev *dev;
+ struct cdev *dev_noprime;
+#endif
+};
+
+#if defined(__NetBSD__)
+dev_type_open(ulptopen);
+dev_type_close(ulptclose);
+dev_type_write(ulptwrite);
+dev_type_read(ulptread);
+dev_type_ioctl(ulptioctl);
+
+const struct cdevsw ulpt_cdevsw = {
+ ulptopen, ulptclose, ulptread, ulptwrite, ulptioctl,
+ nostop, notty, nopoll, nommap, nokqfilter,
+};
+#elif defined(__OpenBSD__)
+cdev_decl(ulpt);
+#elif defined(__FreeBSD__)
+Static d_open_t ulptopen;
+Static d_close_t ulptclose;
+Static d_write_t ulptwrite;
+Static d_read_t ulptread;
+Static d_ioctl_t ulptioctl;
+
+
+Static struct cdevsw ulpt_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ulptopen,
+ .d_close = ulptclose,
+ .d_write = ulptwrite,
+ .d_read = ulptread,
+ .d_ioctl = ulptioctl,
+ .d_name = "ulpt",
+#if __FreeBSD_version < 500014
+ .d_bmaj -1
+#endif
+};
+#endif
+
+void ulpt_disco(void *);
+
+int ulpt_do_write(struct ulpt_softc *, struct uio *uio, int);
+int ulpt_do_read(struct ulpt_softc *, struct uio *uio, int);
+int ulpt_status(struct ulpt_softc *);
+void ulpt_reset(struct ulpt_softc *);
+int ulpt_statusmsg(u_char, struct ulpt_softc *);
+void ulpt_read_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status);
+void ulpt_tick(void *xsc);
+
+#if 0
+void ieee1284_print_id(char *);
+#endif
+
+#define ULPTUNIT(s) (minor(s) & 0x1f)
+#define ULPTFLAGS(s) (minor(s) & 0xe0)
+
+
+USB_DECLARE_DRIVER(ulpt);
+
+USB_MATCH(ulpt)
+{
+ USB_MATCH_START(ulpt, uaa);
+ usb_interface_descriptor_t *id;
+
+ DPRINTFN(10,("ulpt_match\n"));
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id != NULL &&
+ id->bInterfaceClass == UICLASS_PRINTER &&
+ id->bInterfaceSubClass == UISUBCLASS_PRINTER &&
+ (id->bInterfaceProtocol == UIPROTO_PRINTER_UNI ||
+ id->bInterfaceProtocol == UIPROTO_PRINTER_BI ||
+ id->bInterfaceProtocol == UIPROTO_PRINTER_1284))
+ return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(ulpt)
+{
+ USB_ATTACH_START(ulpt, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *ifcd = usbd_get_interface_descriptor(iface);
+ usb_interface_descriptor_t *id, *iend;
+ usb_config_descriptor_t *cdesc;
+ usbd_status err;
+ char devinfo[1024];
+ usb_endpoint_descriptor_t *ed;
+ u_int8_t epcount;
+ int i, altno;
+
+ DPRINTFN(10,("ulpt_attach: sc=%p\n", sc));
+ usbd_devinfo(dev, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
+ devinfo, ifcd->bInterfaceClass, ifcd->bInterfaceSubClass);
+
+ /* XXX
+ * Stepping through the alternate settings needs to be abstracted out.
+ */
+ cdesc = usbd_get_config_descriptor(dev);
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+ iend = (usb_interface_descriptor_t *)
+ ((char *)cdesc + UGETW(cdesc->wTotalLength));
+#ifdef DIAGNOSTIC
+ if (ifcd < (usb_interface_descriptor_t *)cdesc ||
+ ifcd >= iend)
+ panic("ulpt: iface desc out of range");
+#endif
+ /* Step through all the descriptors looking for bidir mode */
+ for (id = ifcd, altno = 0;
+ id < iend;
+ id = (void *)((char *)id + id->bLength)) {
+ if (id->bDescriptorType == UDESC_INTERFACE &&
+ id->bInterfaceNumber == ifcd->bInterfaceNumber) {
+ if (id->bInterfaceClass == UICLASS_PRINTER &&
+ id->bInterfaceSubClass == UISUBCLASS_PRINTER &&
+ (id->bInterfaceProtocol == UIPROTO_PRINTER_BI /* ||
+ id->bInterfaceProtocol == UIPROTO_PRINTER_1284 */))
+ goto found;
+ altno++;
+ }
+ }
+ id = ifcd; /* not found, use original */
+ found:
+ if (id != ifcd) {
+ /* Found a new bidir setting */
+ DPRINTF(("ulpt_attach: set altno = %d\n", altno));
+ err = usbd_set_interface(iface, altno);
+ if (err) {
+ printf("%s: setting alternate interface failed\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+ }
+
+ epcount = 0;
+ (void)usbd_endpoint_count(iface, &epcount);
+
+ sc->sc_in = -1;
+ sc->sc_out = -1;
+ for (i = 0; i < epcount; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ printf("%s: couldn't get ep %d\n",
+ USBDEVNAME(sc->sc_dev), i);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_in = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_out = ed->bEndpointAddress;
+ }
+ }
+ if (sc->sc_out == -1) {
+ printf("%s: could not find bulk out endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ if (usbd_get_quirks(dev)->uq_flags & UQ_BROKEN_BIDIR) {
+ /* This device doesn't handle reading properly. */
+ sc->sc_in = -1;
+ }
+
+ printf("%s: using %s-directional mode\n", USBDEVNAME(sc->sc_dev),
+ sc->sc_in >= 0 ? "bi" : "uni");
+
+ DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_out));
+
+ sc->sc_iface = iface;
+ sc->sc_ifaceno = id->bInterfaceNumber;
+ sc->sc_udev = dev;
+
+#if 0
+/*
+ * This code is disabled because for some mysterious reason it causes
+ * printing not to work. But only sometimes, and mostly with
+ * UHCI and less often with OHCI. *sigh*
+ */
+ {
+ usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
+ usb_device_request_t req;
+ int len, alen;
+
+ 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);
+ err = usbd_do_request_flags(dev, &req, devinfo, USBD_SHORT_XFER_OK,
+ &alen, USBD_DEFAULT_TIMEOUT);
+ if (err) {
+ printf("%s: cannot get device id\n", USBDEVNAME(sc->sc_dev));
+ } else if (alen <= 2) {
+ printf("%s: empty device id, no printer connected?\n",
+ USBDEVNAME(sc->sc_dev));
+ } else {
+ /* devinfo now contains an IEEE-1284 device ID */
+ len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff);
+ if (len > sizeof devinfo - 3)
+ len = sizeof devinfo - 3;
+ devinfo[len] = 0;
+ printf("%s: device id <", USBDEVNAME(sc->sc_dev));
+ ieee1284_print_id(devinfo+2);
+ printf(">\n");
+ }
+ }
+#endif
+
+#if defined(__FreeBSD__)
+ sc->dev = make_dev(&ulpt_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR, 0644, "ulpt%d", device_get_unit(self));
+ sc->dev_noprime = make_dev(&ulpt_cdevsw,
+ device_get_unit(self)|ULPT_NOPRIME,
+ UID_ROOT, GID_OPERATOR, 0644, "unlpt%d", device_get_unit(self));
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+ulpt_activate(device_ptr_t self, enum devact act)
+{
+ struct ulpt_softc *sc = (struct ulpt_softc *)self;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+#endif
+
+USB_DETACH(ulpt)
+{
+ USB_DETACH_START(ulpt, sc);
+ int s;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ int maj, mn;
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ DPRINTF(("ulpt_detach: sc=%p flags=%d\n", sc, flags));
+#elif defined(__FreeBSD__)
+ DPRINTF(("ulpt_detach: sc=%p\n", sc));
+#endif
+
+ sc->sc_dying = 1;
+ if (sc->sc_out_pipe != NULL)
+ usbd_abort_pipe(sc->sc_out_pipe);
+ if (sc->sc_in_pipe != NULL)
+ usbd_abort_pipe(sc->sc_in_pipe);
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* There is noone to wake, aborting the pipe is enough */
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* locate the major number */
+#if defined(__NetBSD__)
+ maj = cdevsw_lookup_major(&ulpt_cdevsw);
+#elif defined(__OpenBSD__)
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == ulptopen)
+ break;
+#endif
+
+ /* Nuke the vnodes for any open instances (calls close). */
+ mn = self->dv_unit;
+ vdevgone(maj, mn, mn, VCHR);
+#elif defined(__FreeBSD__)
+ destroy_dev(sc->dev);
+ destroy_dev(sc->dev_noprime);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ return (0);
+}
+
+int
+ulpt_status(struct ulpt_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ 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);
+ err = usbd_do_request(sc->sc_udev, &req, &status);
+ DPRINTFN(1, ("ulpt_status: status=0x%02x err=%d\n", status, err));
+ if (!err)
+ return (status);
+ else
+ return (0);
+}
+
+void
+ulpt_reset(struct ulpt_softc *sc)
+{
+ usb_device_request_t req;
+
+ DPRINTFN(1, ("ulpt_reset\n"));
+ req.bRequest = UR_SOFT_RESET;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 0);
+
+ /*
+ * There was a mistake in the USB printer 1.0 spec that gave the
+ * request type as UT_WRITE_CLASS_OTHER; it should have been
+ * UT_WRITE_CLASS_INTERFACE. Many printers use the old one,
+ * so we try both.
+ */
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ if (usbd_do_request(sc->sc_udev, &req, 0)) { /* 1.0 */
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ (void)usbd_do_request(sc->sc_udev, &req, 0); /* 1.1 */
+ }
+}
+#if 0
+static void
+ulpt_input(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ulpt_softc *sc = priv;
+ u_int32_t count;
+
+ /* Don't loop on errors or 0-length input. */
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ if (status != USBD_NORMAL_COMPLETION || count == 0)
+ return;
+
+ DPRINTFN(2,("ulpt_input: got some data\n"));
+ /* Do it again. */
+ if (xfer == sc->sc_in_xfer1)
+ usbd_transfer(sc->sc_in_xfer2);
+ else
+ usbd_transfer(sc->sc_in_xfer1);
+}
+#endif
+
+int ulptusein = 1;
+
+/*
+ * Reset the printer, then wait until it's selected and not busy.
+ */
+int
+ulptopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ u_char flags = ULPTFLAGS(dev);
+ struct ulpt_softc *sc;
+ usbd_status err;
+ int spin, error;
+
+ USB_GET_SC_OPEN(ulpt, ULPTUNIT(dev), sc);
+
+ if (sc == NULL || sc->sc_iface == NULL || sc->sc_dying)
+ 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 defined(USB_DEBUG) && defined(__FreeBSD__)
+ /* Ignoring these flags might not be a good idea */
+ if ((flags & ~ULPT_NOPRIME) != 0)
+ printf("ulptopen: flags ignored: %b\n", flags,
+ "\20\3POS_INIT\4POS_ACK\6PRIME_OPEN\7AUTOLF\10BYPASS");
+#endif
+
+
+ error = 0;
+ sc->sc_refcnt++;
+
+ if ((flags & ULPT_NOPRIME) == 0) {
+ ulpt_reset(sc);
+ if (sc->sc_dying) {
+ error = ENXIO;
+ sc->sc_state = 0;
+ goto done;
+ }
+ }
+
+ for (spin = 0; (ulpt_status(sc) & LPS_SELECT) == 0; spin += STEP) {
+ DPRINTF(("ulpt_open: waiting a while\n"));
+ if (spin >= TIMEOUT) {
+ error = EBUSY;
+ sc->sc_state = 0;
+ goto done;
+ }
+
+ /* wait 1/4 second, give up if we get a signal */
+ error = tsleep(sc, LPTPRI | PCATCH, "ulptop", STEP);
+ if (error != EWOULDBLOCK) {
+ sc->sc_state = 0;
+ goto done;
+ }
+
+ if (sc->sc_dying) {
+ error = ENXIO;
+ sc->sc_state = 0;
+ goto done;
+ }
+ }
+
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe);
+ if (err) {
+ error = EIO;
+ goto err0;
+ }
+ sc->sc_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_out_xfer == NULL) {
+ error = ENOMEM;
+ goto err1;
+ }
+ sc->sc_out_buf = usbd_alloc_buffer(sc->sc_out_xfer, ULPT_BSIZE);
+ if (sc->sc_out_buf == NULL) {
+ error = ENOMEM;
+ goto err2;
+ }
+
+ if (ulptusein && sc->sc_in != -1) {
+ DPRINTF(("ulpt_open: open input pipe\n"));
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_in,0,&sc->sc_in_pipe);
+ if (err) {
+ error = EIO;
+ goto err2;
+ }
+ sc->sc_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_in_xfer == NULL) {
+ error = ENOMEM;
+ goto err3;
+ }
+ sc->sc_in_buf = usbd_alloc_buffer(sc->sc_in_xfer, ULPT_BSIZE);
+ if (sc->sc_in_buf == NULL) {
+ error = ENOMEM;
+ goto err4;
+ }
+
+ /* If it's not opened for read the set up a reader. */
+ if (!(flags & FREAD)) {
+ DPRINTF(("ulpt_open: start read callout\n"));
+ usb_callout_init(sc->sc_read_callout);
+ usb_callout(sc->sc_read_callout, hz/5, ulpt_tick, sc);
+ sc->sc_has_callout = 1;
+ }
+ }
+
+ sc->sc_state = ULPT_OPEN;
+ goto done;
+
+ err4:
+ usbd_free_xfer(sc->sc_in_xfer);
+ sc->sc_in_xfer = NULL;
+ err3:
+ usbd_close_pipe(sc->sc_in_pipe);
+ sc->sc_in_pipe = NULL;
+ err2:
+ usbd_free_xfer(sc->sc_out_xfer);
+ sc->sc_out_xfer = NULL;
+ err1:
+ usbd_close_pipe(sc->sc_out_pipe);
+ sc->sc_out_pipe = NULL;
+ err0:
+ sc->sc_state = 0;
+
+ done:
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+ DPRINTF(("ulptopen: done, error=%d\n", error));
+ return (error);
+}
+
+int
+ulpt_statusmsg(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;
+
+ if (new & LPS_SELECT)
+ log(LOG_NOTICE, "%s: offline\n", USBDEVNAME(sc->sc_dev));
+ else if (new & LPS_NOPAPER)
+ log(LOG_NOTICE, "%s: out of paper\n", USBDEVNAME(sc->sc_dev));
+ else if (new & LPS_NERR)
+ log(LOG_NOTICE, "%s: output error\n", USBDEVNAME(sc->sc_dev));
+
+ return (status);
+}
+
+int
+ulptclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct ulpt_softc *sc;
+
+ USB_GET_SC(ulpt, ULPTUNIT(dev), sc);
+
+ if (sc->sc_state != ULPT_OPEN)
+ /* We are being forced to close before the open completed. */
+ return (0);
+
+ if (sc->sc_has_callout) {
+ usb_uncallout(sc->sc_read_callout, ulpt_tick, sc);
+ sc->sc_has_callout = 0;
+ }
+
+ if (sc->sc_out_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_out_pipe);
+ usbd_close_pipe(sc->sc_out_pipe);
+ sc->sc_out_pipe = NULL;
+ }
+ if (sc->sc_out_xfer != NULL) {
+ usbd_free_xfer(sc->sc_out_xfer);
+ sc->sc_out_xfer = NULL;
+ }
+
+ if (sc->sc_in_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_in_pipe);
+ usbd_close_pipe(sc->sc_in_pipe);
+ sc->sc_in_pipe = NULL;
+ }
+ if (sc->sc_in_xfer != NULL) {
+ usbd_free_xfer(sc->sc_in_xfer);
+ sc->sc_in_xfer = NULL;
+ }
+
+ sc->sc_state = 0;
+
+ DPRINTF(("ulptclose: closed\n"));
+ return (0);
+}
+
+int
+ulpt_do_write(struct ulpt_softc *sc, struct uio *uio, int flags)
+{
+ u_int32_t n;
+ int error = 0;
+ void *bufp;
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ DPRINTF(("ulptwrite\n"));
+ xfer = sc->sc_out_xfer;
+ bufp = sc->sc_out_buf;
+ while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) {
+ ulpt_statusmsg(ulpt_status(sc), sc);
+ error = uiomove(bufp, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("ulptwrite: transfer %d bytes\n", n));
+ err = usbd_bulk_transfer(xfer, sc->sc_out_pipe, USBD_NO_COPY,
+ USBD_NO_TIMEOUT, bufp, &n, "ulptwr");
+ if (err) {
+ DPRINTF(("ulptwrite: error=%d\n", err));
+ error = EIO;
+ break;
+ }
+ }
+
+ return (error);
+}
+
+int
+ulptwrite(struct cdev *dev, struct uio *uio, int flags)
+{
+ struct ulpt_softc *sc;
+ int error;
+
+ USB_GET_SC(ulpt, ULPTUNIT(dev), sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ sc->sc_refcnt++;
+ error = ulpt_do_write(sc, uio, flags);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+int
+ulpt_do_read(struct ulpt_softc *sc, struct uio *uio, int flags)
+{
+ u_int32_t n, on;
+ int error = 0;
+ void *bufp;
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ DPRINTF(("ulptread\n"));
+
+ if (sc->sc_in_pipe == NULL)
+ return 0;
+
+ xfer = sc->sc_in_xfer;
+ bufp = sc->sc_in_buf;
+ while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) {
+ DPRINTFN(1, ("ulptread: transfer %d bytes\n", n));
+ on = n;
+ err = usbd_bulk_transfer(xfer, sc->sc_in_pipe,
+ USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, bufp, &n, "ulptrd");
+ if (err) {
+ DPRINTF(("ulptread: error=%d\n", err));
+ error = EIO;
+ break;
+ }
+ error = uiomove(bufp, n, uio);
+ if (error)
+ break;
+ if (on != n)
+ break;
+ }
+
+ return (error);
+}
+
+int
+ulptread(struct cdev *dev, struct uio *uio, int flags)
+{
+ struct ulpt_softc *sc;
+ int error;
+
+ USB_GET_SC(ulpt, ULPTUNIT(dev), sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ sc->sc_refcnt++;
+ error = ulpt_do_read(sc, uio, flags);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+void
+ulpt_read_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ usbd_status err;
+ u_int32_t n;
+ usbd_private_handle xsc;
+ struct ulpt_softc *sc;
+
+ usbd_get_xfer_status(xfer, &xsc, NULL, &n, &err);
+ sc = xsc;
+
+ DPRINTFN(1,("ulpt_read_cb: start sc=%p, err=%d n=%d\n", sc, err, n));
+
+#ifdef ULPT_DEBUG
+ if (!err && n > 0)
+ DPRINTF(("ulpt_tick: discarding %d bytes\n", n));
+#endif
+ if (!err || err == USBD_TIMEOUT)
+ usb_callout(sc->sc_read_callout, hz / ULPT_READS_PER_SEC,
+ ulpt_tick, sc);
+}
+
+void
+ulpt_tick(void *xsc)
+{
+ struct ulpt_softc *sc = xsc;
+ usbd_status err;
+
+ if (sc == NULL || sc->sc_dying)
+ return;
+
+ DPRINTFN(1,("ulpt_tick: start sc=%p\n", sc));
+
+ usbd_setup_xfer(sc->sc_in_xfer, sc->sc_in_pipe, sc, sc->sc_in_buf,
+ ULPT_BSIZE, USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ ULPT_READ_TIMO, ulpt_read_cb);
+ err = usbd_transfer(sc->sc_in_xfer);
+ DPRINTFN(1,("ulpt_tick: err=%d\n", err));
+}
+
+int
+ulptioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
+{
+ int error = 0;
+
+ switch (cmd) {
+ default:
+ error = ENODEV;
+ }
+
+ return (error);
+}
+
+#if 0
+/* XXX This does not belong here. */
+/*
+ * Print select parts of an IEEE 1284 device ID.
+ */
+void
+ieee1284_print_id(char *str)
+{
+ char *p, *q;
+
+ for (p = str-1; p; p = strchr(p, ';')) {
+ p++; /* skip ';' */
+ if (strncmp(p, "MFG:", 4) == 0 ||
+ strncmp(p, "MANUFACTURER:", 14) == 0 ||
+ strncmp(p, "MDL:", 4) == 0 ||
+ strncmp(p, "MODEL:", 6) == 0) {
+ q = strchr(p, ';');
+ if (q)
+ printf("%.*s", (int)(q - p + 1), p);
+ }
+ }
+}
+#endif
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(ulpt, uhub, ulpt_driver, ulpt_devclass, usbd_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/umass.c b/sys/dev/usb/umass.c
new file mode 100644
index 0000000..01b9047
--- /dev/null
+++ b/sys/dev/usb/umass.c
@@ -0,0 +1,3072 @@
+/*-
+ * Copyright (c) 1999 MAEKAWA Masahide <bishop@rr.iij4u.or.jp>,
+ * Nick Hibma <n_hibma@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * $NetBSD: umass.c,v 1.28 2000/04/02 23:46:53 augustss Exp $
+ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: umass.c,v 1.67 2001/11/25 19:05:22 augustss Exp $
+ * $NetBSD: umass.c,v 1.90 2002/11/04 19:17:33 pooka Exp $
+ * $NetBSD: umass.c,v 1.108 2003/11/07 17:03:25 wiz Exp $
+ * $NetBSD: umass.c,v 1.109 2003/12/04 13:57:31 keihan Exp $
+ */
+
+/*
+ * Universal Serial Bus Mass Storage Class specs:
+ * http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf
+ * http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
+ * http://www.usb.org/developers/devclass_docs/usb_msc_cbi_1.1.pdf
+ * http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf
+ */
+
+/*
+ * Ported to NetBSD by Lennart Augustsson <augustss@NetBSD.org>.
+ * Parts of the code written by Jason R. Thorpe <thorpej@shagadelic.org>.
+ */
+
+/*
+ * The driver handles 3 Wire Protocols
+ * - Command/Bulk/Interrupt (CBI)
+ * - Command/Bulk/Interrupt with Command Completion Interrupt (CBI with CCI)
+ * - Mass Storage Bulk-Only (BBB)
+ * (BBB refers Bulk/Bulk/Bulk for Command/Data/Status phases)
+ *
+ * Over these wire protocols it handles the following command protocols
+ * - SCSI
+ * - UFI (floppy command set)
+ * - 8070i (ATAPI)
+ *
+ * UFI and 8070i (ATAPI) are transformed versions of the SCSI command set. The
+ * sc->transform method is used to convert the commands into the appropriate
+ * format (if at all necessary). For example, UFI requires all commands to be
+ * 12 bytes in length amongst other things.
+ *
+ * The source code below is marked and can be split into a number of pieces
+ * (in this order):
+ *
+ * - probe/attach/detach
+ * - generic transfer routines
+ * - BBB
+ * - CBI
+ * - CBI_I (in addition to functions from CBI)
+ * - CAM (Common Access Method)
+ * - SCSI
+ * - UFI
+ * - 8070i (ATAPI)
+ *
+ * The protocols are implemented using a state machine, for the transfers as
+ * well as for the resets. The state machine is contained in umass_*_state.
+ * The state machine is started through either umass_*_transfer or
+ * umass_*_reset.
+ *
+ * The reason for doing this is a) CAM performs a lot better this way and b) it
+ * avoids using tsleep from interrupt context (for example after a failed
+ * transfer).
+ */
+
+/*
+ * The SCSI related part of this driver has been derived from the
+ * dev/ppbus/vpo.c driver, by Nicolas Souchu (nsouch@freebsd.org).
+ *
+ * The CAM layer uses so called actions which are messages sent to the host
+ * adapter for completion. The actions come in through umass_cam_action. The
+ * appropriate block of routines is called depending on the transport protocol
+ * in use. When the transfer has finished, these routines call
+ * umass_cam_cb again to complete the CAM command.
+ */
+
+/*
+ * XXX Currently CBI with CCI is not supported because it bombs the system
+ * when the device is detached (low frequency interrupts are detached
+ * too late.
+ */
+#undef CBI_I
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_da.h>
+
+#include <cam/cam_periph.h>
+
+#ifdef USB_DEBUG
+#define DIF(m, x) if (umassdebug & (m)) do { x ; } while (0)
+#define DPRINTF(m, x) if (umassdebug & (m)) logprintf x
+#define UDMASS_GEN 0x00010000 /* general */
+#define UDMASS_SCSI 0x00020000 /* scsi */
+#define UDMASS_UFI 0x00040000 /* ufi command set */
+#define UDMASS_ATAPI 0x00080000 /* 8070i command set */
+#define UDMASS_CMD (UDMASS_SCSI|UDMASS_UFI|UDMASS_ATAPI)
+#define UDMASS_USB 0x00100000 /* USB general */
+#define UDMASS_BBB 0x00200000 /* Bulk-Only transfers */
+#define UDMASS_CBI 0x00400000 /* CBI transfers */
+#define UDMASS_WIRE (UDMASS_BBB|UDMASS_CBI)
+#define UDMASS_ALL 0xffff0000 /* all of the above */
+int umassdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, umass, CTLFLAG_RW, 0, "USB umass");
+SYSCTL_INT(_hw_usb_umass, OID_AUTO, debug, CTLFLAG_RW,
+ &umassdebug, 0, "umass debug level");
+#else
+#define DIF(m, x) /* nop */
+#define DPRINTF(m, x) /* nop */
+#endif
+
+
+/* Generic definitions */
+
+/* Direction for umass_*_transfer */
+#define DIR_NONE 0
+#define DIR_IN 1
+#define DIR_OUT 2
+
+/* device name */
+#define DEVNAME "umass"
+#define DEVNAME_SIM "umass-sim"
+
+#define UMASS_MAX_TRANSFER_SIZE 65536
+#define UMASS_DEFAULT_TRANSFER_SPEED 1000
+#define UMASS_FLOPPY_TRANSFER_SPEED 20
+
+#define UMASS_TIMEOUT 5000 /* msecs */
+
+/* CAM specific definitions */
+
+#define UMASS_SCSIID_MAX 1 /* maximum number of drives expected */
+#define UMASS_SCSIID_HOST UMASS_SCSIID_MAX
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+
+/* Bulk-Only features */
+
+#define UR_BBB_RESET 0xff /* Bulk-Only reset */
+#define UR_BBB_GET_MAX_LUN 0xfe /* Get maximum lun */
+
+/* Command Block Wrapper */
+typedef struct {
+ uDWord dCBWSignature;
+# define CBWSIGNATURE 0x43425355
+ uDWord dCBWTag;
+ uDWord dCBWDataTransferLength;
+ uByte bCBWFlags;
+# define CBWFLAGS_OUT 0x00
+# define CBWFLAGS_IN 0x80
+ uByte bCBWLUN;
+ uByte bCDBLength;
+# define CBWCDBLENGTH 16
+ uByte CBWCDB[CBWCDBLENGTH];
+} umass_bbb_cbw_t;
+#define UMASS_BBB_CBW_SIZE 31
+
+/* Command Status Wrapper */
+typedef struct {
+ uDWord dCSWSignature;
+# define CSWSIGNATURE 0x53425355
+# define CSWSIGNATURE_OLYMPUS_C1 0x55425355
+ uDWord dCSWTag;
+ uDWord dCSWDataResidue;
+ uByte bCSWStatus;
+# define CSWSTATUS_GOOD 0x0
+# define CSWSTATUS_FAILED 0x1
+# define CSWSTATUS_PHASE 0x2
+} umass_bbb_csw_t;
+#define UMASS_BBB_CSW_SIZE 13
+
+/* CBI features */
+
+#define UR_CBI_ADSC 0x00
+
+typedef unsigned char umass_cbi_cbl_t[16]; /* Command block */
+
+typedef union {
+ struct {
+ unsigned char type;
+ #define IDB_TYPE_CCI 0x00
+ unsigned char value;
+ #define IDB_VALUE_PASS 0x00
+ #define IDB_VALUE_FAIL 0x01
+ #define IDB_VALUE_PHASE 0x02
+ #define IDB_VALUE_PERSISTENT 0x03
+ #define IDB_VALUE_STATUS_MASK 0x03
+ } common;
+
+ struct {
+ unsigned char asc;
+ unsigned char ascq;
+ } ufi;
+} umass_cbi_sbl_t;
+
+
+
+struct umass_softc; /* see below */
+
+typedef void (*transfer_cb_f) (struct umass_softc *sc, void *priv,
+ int residue, int status);
+#define STATUS_CMD_OK 0 /* everything ok */
+#define STATUS_CMD_UNKNOWN 1 /* will have to fetch sense */
+#define STATUS_CMD_FAILED 2 /* transfer was ok, command failed */
+#define STATUS_WIRE_FAILED 3 /* couldn't even get command across */
+
+typedef void (*wire_reset_f) (struct umass_softc *sc, int status);
+typedef void (*wire_transfer_f) (struct umass_softc *sc, int lun,
+ void *cmd, int cmdlen, void *data, int datalen,
+ int dir, u_int timeout, transfer_cb_f cb, void *priv);
+typedef void (*wire_state_f) (usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status err);
+
+typedef int (*command_transform_f) (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+
+struct umass_devdescr_t {
+ u_int32_t vid;
+# define VID_WILDCARD 0xffffffff
+# define VID_EOT 0xfffffffe
+ u_int32_t pid;
+# define PID_WILDCARD 0xffffffff
+# define PID_EOT 0xfffffffe
+ u_int32_t rid;
+# define RID_WILDCARD 0xffffffff
+# define RID_EOT 0xfffffffe
+
+ /* wire and command protocol */
+ u_int16_t proto;
+# define UMASS_PROTO_BBB 0x0001 /* USB wire protocol */
+# define UMASS_PROTO_CBI 0x0002
+# define UMASS_PROTO_CBI_I 0x0004
+# define UMASS_PROTO_WIRE 0x00ff /* USB wire protocol mask */
+# define UMASS_PROTO_SCSI 0x0100 /* command protocol */
+# define UMASS_PROTO_ATAPI 0x0200
+# define UMASS_PROTO_UFI 0x0400
+# define UMASS_PROTO_RBC 0x0800
+# define UMASS_PROTO_COMMAND 0xff00 /* command protocol mask */
+
+ /* Device specific quirks */
+ u_int16_t quirks;
+# define NO_QUIRKS 0x0000
+ /* The drive does not support Test Unit Ready. Convert to Start Unit
+ */
+# define NO_TEST_UNIT_READY 0x0001
+ /* The drive does not reset the Unit Attention state after REQUEST
+ * SENSE has been sent. The INQUIRY command does not reset the UA
+ * either, and so CAM runs in circles trying to retrieve the initial
+ * INQUIRY data.
+ */
+# define RS_NO_CLEAR_UA 0x0002
+ /* The drive does not support START STOP. */
+# define NO_START_STOP 0x0004
+ /* Don't ask for full inquiry data (255b). */
+# define FORCE_SHORT_INQUIRY 0x0008
+ /* Needs to be initialised the Shuttle way */
+# define SHUTTLE_INIT 0x0010
+ /* Drive needs to be switched to alternate iface 1 */
+# define ALT_IFACE_1 0x0020
+ /* Drive does not do 1Mb/s, but just floppy speeds (20kb/s) */
+# define FLOPPY_SPEED 0x0040
+ /* The device can't count and gets the residue of transfers wrong */
+# define IGNORE_RESIDUE 0x0080
+ /* No GetMaxLun call */
+# define NO_GETMAXLUN 0x0100
+ /* The device uses a weird CSWSIGNATURE. */
+# define WRONG_CSWSIG 0x0200
+ /* Device cannot handle INQUIRY so fake a generic response */
+# define NO_INQUIRY 0x0400
+ /* Device cannot handle INQUIRY EVPD, return CHECK CONDITION */
+# define NO_INQUIRY_EVPD 0x0800
+};
+
+Static struct umass_devdescr_t umass_devdescrs[] = {
+ { USB_VENDOR_ASAHIOPTICAL, PID_WILDCARD, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ RS_NO_CLEAR_UA
+ },
+ { USB_VENDOR_FUJIPHOTO, USB_PRODUCT_FUJIPHOTO_MASS0100, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ RS_NO_CLEAR_UA
+ },
+ { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE_2, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_USB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_HP, USB_PRODUCT_HP_CDW8200, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_TEST_UNIT_READY | NO_START_STOP
+ },
+ { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_USBCABLE, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_TEST_UNIT_READY | NO_START_STOP | ALT_IFACE_1
+ },
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_IU_CD2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_DVR_UEH8, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP100, RID_WILDCARD,
+ /* XXX This is not correct as there are Zip drives that use ATAPI.
+ */
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_TEST_UNIT_READY
+ },
+ { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443SU2, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443U2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_DUBPXXG, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_DPCM, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_TEST_UNIT_READY | NO_START_STOP
+ },
+ { USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE | NO_GETMAXLUN | RS_NO_CLEAR_UA
+ },
+ { USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY2, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND3260, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY
+ },
+ { USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ WRONG_CSWSIG
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_UCF100, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_INQUIRY | NO_GETMAXLUN
+ },
+ { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB20AN, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB35AN, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_PLEXTOR, USB_PRODUCT_PLEXTOR_40_12_40U, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_TEST_UNIT_READY
+ },
+ { USB_VENDOR_PNY, USB_PRODUCT_PNY_ATTACHE, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_SL11R, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_TEST_UNIT_READY | NO_START_STOP | SHUTTLE_INIT
+ },
+ { USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_I_BEAD100, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ SHUTTLE_INIT
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_MSC, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_TREK, USB_PRODUCT_TREK_THUMBDRIVE_8MB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_YANO, USB_PRODUCT_YANO_U640MO, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ FORCE_SHORT_INQUIRY
+ },
+ { VID_EOT, PID_EOT, RID_EOT, 0, 0 }
+};
+
+
+/* the per device structure */
+struct umass_softc {
+ USBBASEDEVICE sc_dev; /* base device */
+ usbd_device_handle sc_udev; /* USB device */
+
+ struct cam_sim *umass_sim; /* SCSI Interface Module */
+
+ unsigned char flags; /* various device flags */
+# define UMASS_FLAGS_GONE 0x01 /* devices is no more */
+
+ u_int16_t proto; /* wire and cmd protocol */
+ u_int16_t quirks; /* they got it almost right */
+
+ usbd_interface_handle iface; /* Mass Storage interface */
+ int ifaceno; /* MS iface number */
+
+ u_int8_t bulkin; /* bulk-in Endpoint Address */
+ u_int8_t bulkout; /* bulk-out Endpoint Address */
+ u_int8_t intrin; /* intr-in Endp. (CBI) */
+ usbd_pipe_handle bulkin_pipe;
+ usbd_pipe_handle bulkout_pipe;
+ usbd_pipe_handle intrin_pipe;
+
+ /* Reset the device in a wire protocol specific way */
+ wire_reset_f reset;
+
+ /* The start of a wire transfer. It prepares the whole transfer (cmd,
+ * data, and status stage) and initiates it. It is up to the state
+ * machine (below) to handle the various stages and errors in these
+ */
+ wire_transfer_f transfer;
+
+ /* The state machine, handling the various states during a transfer */
+ wire_state_f state;
+
+ /* The command transform function is used to conver the SCSI commands
+ * into their derivatives, like UFI, ATAPI, and friends.
+ */
+ command_transform_f transform; /* command transform */
+
+ /* Bulk specific variables for transfers in progress */
+ umass_bbb_cbw_t cbw; /* command block wrapper */
+ umass_bbb_csw_t csw; /* command status wrapper*/
+ /* CBI specific variables for transfers in progress */
+ umass_cbi_cbl_t cbl; /* command block */
+ umass_cbi_sbl_t sbl; /* status block */
+
+ /* generic variables for transfers in progress */
+ /* ctrl transfer requests */
+ usb_device_request_t request;
+
+ /* xfer handles
+ * Most of our operations are initiated from interrupt context, so
+ * we need to avoid using the one that is in use. We want to avoid
+ * allocating them in the interrupt context as well.
+ */
+ /* indices into array below */
+# define XFER_BBB_CBW 0 /* Bulk-Only */
+# define XFER_BBB_DATA 1
+# define XFER_BBB_DCLEAR 2
+# define XFER_BBB_CSW1 3
+# define XFER_BBB_CSW2 4
+# define XFER_BBB_SCLEAR 5
+# define XFER_BBB_RESET1 6
+# define XFER_BBB_RESET2 7
+# define XFER_BBB_RESET3 8
+
+# define XFER_CBI_CB 0 /* CBI */
+# define XFER_CBI_DATA 1
+# define XFER_CBI_STATUS 2
+# define XFER_CBI_DCLEAR 3
+# define XFER_CBI_SCLEAR 4
+# define XFER_CBI_RESET1 5
+# define XFER_CBI_RESET2 6
+# define XFER_CBI_RESET3 7
+
+# define XFER_NR 9 /* maximum number */
+
+ usbd_xfer_handle transfer_xfer[XFER_NR]; /* for ctrl xfers */
+
+ int transfer_dir; /* data direction */
+ void *transfer_data; /* data buffer */
+ int transfer_datalen; /* (maximum) length */
+ int transfer_actlen; /* actual length */
+ transfer_cb_f transfer_cb; /* callback */
+ void *transfer_priv; /* for callback */
+ int transfer_status;
+
+ int transfer_state;
+# define TSTATE_ATTACH 0 /* in attach */
+# define TSTATE_IDLE 1
+# define TSTATE_BBB_COMMAND 2 /* CBW transfer */
+# define TSTATE_BBB_DATA 3 /* Data transfer */
+# define TSTATE_BBB_DCLEAR 4 /* clear endpt stall */
+# define TSTATE_BBB_STATUS1 5 /* clear endpt stall */
+# define TSTATE_BBB_SCLEAR 6 /* clear endpt stall */
+# define TSTATE_BBB_STATUS2 7 /* CSW transfer */
+# define TSTATE_BBB_RESET1 8 /* reset command */
+# define TSTATE_BBB_RESET2 9 /* in clear stall */
+# define TSTATE_BBB_RESET3 10 /* out clear stall */
+# define TSTATE_CBI_COMMAND 11 /* command transfer */
+# define TSTATE_CBI_DATA 12 /* data transfer */
+# define TSTATE_CBI_STATUS 13 /* status transfer */
+# define TSTATE_CBI_DCLEAR 14 /* clear ep stall */
+# define TSTATE_CBI_SCLEAR 15 /* clear ep stall */
+# define TSTATE_CBI_RESET1 16 /* reset command */
+# define TSTATE_CBI_RESET2 17 /* in clear stall */
+# define TSTATE_CBI_RESET3 18 /* out clear stall */
+# define TSTATE_STATES 19 /* # of states above */
+
+
+ /* SCSI/CAM specific variables */
+ unsigned char cam_scsi_command[CAM_MAX_CDBLEN];
+ unsigned char cam_scsi_command2[CAM_MAX_CDBLEN];
+ struct scsi_sense cam_scsi_sense;
+ struct scsi_sense cam_scsi_test_unit_ready;
+
+ int timeout; /* in msecs */
+
+ int maxlun; /* maximum LUN number */
+};
+
+#ifdef USB_DEBUG
+char *states[TSTATE_STATES+1] = {
+ /* should be kept in sync with the list at transfer_state */
+ "Attach",
+ "Idle",
+ "BBB CBW",
+ "BBB Data",
+ "BBB Data bulk-in/-out clear stall",
+ "BBB CSW, 1st attempt",
+ "BBB CSW bulk-in clear stall",
+ "BBB CSW, 2nd attempt",
+ "BBB Reset",
+ "BBB bulk-in clear stall",
+ "BBB bulk-out clear stall",
+ "CBI Command",
+ "CBI Data",
+ "CBI Status",
+ "CBI Data bulk-in/-out clear stall",
+ "CBI Status intr-in clear stall",
+ "CBI Reset",
+ "CBI bulk-in clear stall",
+ "CBI bulk-out clear stall",
+ NULL
+};
+#endif
+
+/* If device cannot return valid inquiry data, fake it */
+Static uint8_t fake_inq_data[SHORT_INQUIRY_LENGTH] = {
+ 0, /*removable*/ 0x80, SCSI_REV_2, SCSI_REV_2,
+ /*additional_length*/ 31, 0, 0, 0
+};
+
+/* USB device probe/attach/detach functions */
+USB_DECLARE_DRIVER(umass);
+Static int umass_match_proto (struct umass_softc *sc,
+ usbd_interface_handle iface,
+ usbd_device_handle udev);
+
+/* quirk functions */
+Static void umass_init_shuttle (struct umass_softc *sc);
+
+/* generic transfer functions */
+Static usbd_status umass_setup_transfer (struct umass_softc *sc,
+ usbd_pipe_handle pipe,
+ void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer);
+Static usbd_status umass_setup_ctrl_transfer (struct umass_softc *sc,
+ usbd_device_handle udev,
+ usb_device_request_t *req,
+ void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer);
+Static void umass_clear_endpoint_stall (struct umass_softc *sc,
+ u_int8_t endpt, usbd_pipe_handle pipe,
+ int state, usbd_xfer_handle xfer);
+Static void umass_reset (struct umass_softc *sc,
+ transfer_cb_f cb, void *priv);
+
+/* Bulk-Only related functions */
+Static void umass_bbb_reset (struct umass_softc *sc, int status);
+Static void umass_bbb_transfer (struct umass_softc *sc, int lun,
+ void *cmd, int cmdlen,
+ void *data, int datalen, int dir, u_int timeout,
+ transfer_cb_f cb, void *priv);
+Static void umass_bbb_state (usbd_xfer_handle xfer,
+ usbd_private_handle priv,
+ usbd_status err);
+Static int umass_bbb_get_max_lun
+ (struct umass_softc *sc);
+
+/* CBI related functions */
+Static int umass_cbi_adsc (struct umass_softc *sc,
+ char *buffer, int buflen,
+ usbd_xfer_handle xfer);
+Static void umass_cbi_reset (struct umass_softc *sc, int status);
+Static void umass_cbi_transfer (struct umass_softc *sc, int lun,
+ void *cmd, int cmdlen,
+ void *data, int datalen, int dir, u_int timeout,
+ transfer_cb_f cb, void *priv);
+Static void umass_cbi_state (usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status err);
+
+/* CAM related functions */
+Static void umass_cam_action (struct cam_sim *sim, union ccb *ccb);
+Static void umass_cam_poll (struct cam_sim *sim);
+
+Static void umass_cam_cb (struct umass_softc *sc, void *priv,
+ int residue, int status);
+Static void umass_cam_sense_cb (struct umass_softc *sc, void *priv,
+ int residue, int status);
+Static void umass_cam_quirk_cb (struct umass_softc *sc, void *priv,
+ int residue, int status);
+
+Static void umass_cam_rescan_callback
+ (struct cam_periph *periph,union ccb *ccb);
+Static void umass_cam_rescan (void *addr);
+
+Static int umass_cam_attach_sim (struct umass_softc *sc);
+Static int umass_cam_attach (struct umass_softc *sc);
+Static int umass_cam_detach_sim (struct umass_softc *sc);
+
+
+/* SCSI specific functions */
+Static int umass_scsi_transform (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+/* UFI specific functions */
+#define UFI_COMMAND_LENGTH 12 /* UFI commands are always 12 bytes */
+Static int umass_ufi_transform (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+/* ATAPI (8070i) specific functions */
+#define ATAPI_COMMAND_LENGTH 12 /* ATAPI commands are always 12 bytes */
+Static int umass_atapi_transform (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+/* RBC specific functions */
+Static int umass_rbc_transform (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+#ifdef USB_DEBUG
+/* General debugging functions */
+Static void umass_bbb_dump_cbw (struct umass_softc *sc, umass_bbb_cbw_t *cbw);
+Static void umass_bbb_dump_csw (struct umass_softc *sc, umass_bbb_csw_t *csw);
+Static void umass_cbi_dump_cmd (struct umass_softc *sc, void *cmd, int cmdlen);
+Static void umass_dump_buffer (struct umass_softc *sc, u_int8_t *buffer,
+ int buflen, int printlen);
+#endif
+
+#if defined(__FreeBSD__)
+MODULE_DEPEND(umass, cam, 1,1,1);
+#endif
+
+/*
+ * USB device probe/attach/detach
+ */
+
+/*
+ * Match the device we are seeing with the devices supported. Fill in the
+ * description in the softc accordingly. This function is called from both
+ * probe and attach.
+ */
+
+Static int
+umass_match_proto(struct umass_softc *sc, usbd_interface_handle iface,
+ usbd_device_handle udev)
+{
+ usb_device_descriptor_t *dd;
+ usb_interface_descriptor_t *id;
+ int i;
+ int found = 0;
+
+ sc->sc_udev = udev;
+ sc->proto = 0;
+ sc->quirks = 0;
+
+ dd = usbd_get_device_descriptor(udev);
+
+ /* An entry specifically for Y-E Data devices as they don't fit in the
+ * device description table.
+ */
+ if (UGETW(dd->idVendor) == USB_VENDOR_YEDATA
+ && UGETW(dd->idProduct) == USB_PRODUCT_YEDATA_FLASHBUSTERU) {
+
+ /* Revisions < 1.28 do not handle the interrupt endpoint
+ * very well.
+ */
+ if (UGETW(dd->bcdDevice) < 0x128) {
+ sc->proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI;
+ } else {
+ sc->proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI_I;
+ }
+
+ /*
+ * Revisions < 1.28 do not have the TEST UNIT READY command
+ * Revisions == 1.28 have a broken TEST UNIT READY
+ */
+ if (UGETW(dd->bcdDevice) <= 0x128)
+ sc->quirks |= NO_TEST_UNIT_READY;
+
+ sc->quirks |= RS_NO_CLEAR_UA | FLOPPY_SPEED;
+ return(UMATCH_VENDOR_PRODUCT);
+ }
+
+ /* Check the list of supported devices for a match. While looking,
+ * check for wildcarded and fully matched. First match wins.
+ */
+ for (i = 0; umass_devdescrs[i].vid != VID_EOT && !found; i++) {
+ if (umass_devdescrs[i].vid == VID_WILDCARD &&
+ umass_devdescrs[i].pid == PID_WILDCARD &&
+ umass_devdescrs[i].rid == RID_WILDCARD) {
+ printf("umass: ignoring invalid wildcard quirk\n");
+ continue;
+ }
+ if ((umass_devdescrs[i].vid == UGETW(dd->idVendor) ||
+ umass_devdescrs[i].vid == VID_WILDCARD)
+ && (umass_devdescrs[i].pid == UGETW(dd->idProduct) ||
+ umass_devdescrs[i].pid == PID_WILDCARD)) {
+ if (umass_devdescrs[i].rid == RID_WILDCARD) {
+ sc->proto = umass_devdescrs[i].proto;
+ sc->quirks = umass_devdescrs[i].quirks;
+ return (UMATCH_VENDOR_PRODUCT);
+ } else if (umass_devdescrs[i].rid ==
+ UGETW(dd->bcdDevice)) {
+ sc->proto = umass_devdescrs[i].proto;
+ sc->quirks = umass_devdescrs[i].quirks;
+ return (UMATCH_VENDOR_PRODUCT_REV);
+ } /* else RID does not match */
+ }
+ }
+
+ /* Check for a standards compliant device */
+ id = usbd_get_interface_descriptor(iface);
+ if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
+ return(UMATCH_NONE);
+
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_SCSI:
+ sc->proto |= UMASS_PROTO_SCSI;
+ break;
+ case UISUBCLASS_UFI:
+ sc->proto |= UMASS_PROTO_UFI;
+ break;
+ case UISUBCLASS_RBC:
+ sc->proto |= UMASS_PROTO_RBC;
+ break;
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ sc->proto |= UMASS_PROTO_ATAPI;
+ break;
+ default:
+ DPRINTF(UDMASS_GEN, ("%s: Unsupported command protocol %d\n",
+ USBDEVNAME(sc->sc_dev), id->bInterfaceSubClass));
+ return(UMATCH_NONE);
+ }
+
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_CBI:
+ sc->proto |= UMASS_PROTO_CBI;
+ break;
+ case UIPROTO_MASS_CBI_I:
+ sc->proto |= UMASS_PROTO_CBI_I;
+ break;
+ case UIPROTO_MASS_BBB_OLD:
+ case UIPROTO_MASS_BBB:
+ sc->proto |= UMASS_PROTO_BBB;
+ break;
+ default:
+ DPRINTF(UDMASS_GEN, ("%s: Unsupported wire protocol %d\n",
+ USBDEVNAME(sc->sc_dev), id->bInterfaceProtocol));
+ return(UMATCH_NONE);
+ }
+
+ return(UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO);
+}
+
+USB_MATCH(umass)
+{
+ USB_MATCH_START(umass, uaa);
+ struct umass_softc *sc = device_get_softc(self);
+
+ USB_MATCH_SETUP;
+
+ if (uaa->iface == NULL)
+ return(UMATCH_NONE);
+
+ return(umass_match_proto(sc, uaa->iface, uaa->device));
+}
+
+USB_ATTACH(umass)
+{
+ USB_ATTACH_START(umass, sc, uaa);
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ char devinfo[1024];
+ int i;
+ int err;
+
+ /*
+ * the softc struct is bzero-ed in device_set_driver. We can safely
+ * call umass_detach without specifically initialising the struct.
+ */
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+
+ sc->iface = uaa->iface;
+ sc->ifaceno = uaa->ifaceno;
+
+ /* initialise the proto and drive values in the umass_softc (again) */
+ (void) umass_match_proto(sc, sc->iface, uaa->device);
+
+ id = usbd_get_interface_descriptor(sc->iface);
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+#ifdef USB_DEBUG
+ printf("%s: ", USBDEVNAME(sc->sc_dev));
+ switch (sc->proto&UMASS_PROTO_COMMAND) {
+ case UMASS_PROTO_SCSI:
+ printf("SCSI");
+ break;
+ case UMASS_PROTO_ATAPI:
+ printf("8070i (ATAPI)");
+ break;
+ case UMASS_PROTO_UFI:
+ printf("UFI");
+ break;
+ case UMASS_PROTO_RBC:
+ printf("RBC");
+ break;
+ default:
+ printf("(unknown 0x%02x)", sc->proto&UMASS_PROTO_COMMAND);
+ break;
+ }
+ printf(" over ");
+ switch (sc->proto&UMASS_PROTO_WIRE) {
+ case UMASS_PROTO_BBB:
+ printf("Bulk-Only");
+ break;
+ case UMASS_PROTO_CBI: /* uses Comand/Bulk pipes */
+ printf("CBI");
+ break;
+ case UMASS_PROTO_CBI_I: /* uses Comand/Bulk/Interrupt pipes */
+ printf("CBI with CCI");
+#ifndef CBI_I
+ printf(" (using CBI)");
+#endif
+ break;
+ default:
+ printf("(unknown 0x%02x)", sc->proto&UMASS_PROTO_WIRE);
+ }
+ printf("; quirks = 0x%04x\n", sc->quirks);
+#endif
+
+#ifndef CBI_I
+ if (sc->proto & UMASS_PROTO_CBI_I) {
+ /* See beginning of file for comment on the use of CBI with CCI */
+ sc->proto = (sc->proto & ~UMASS_PROTO_CBI_I) | UMASS_PROTO_CBI;
+ }
+#endif
+
+ if (sc->quirks & ALT_IFACE_1) {
+ err = usbd_set_interface(0, 1);
+ if (err) {
+ DPRINTF(UDMASS_USB, ("%s: could not switch to "
+ "Alt Interface %d\n",
+ USBDEVNAME(sc->sc_dev), 1));
+ umass_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ }
+
+ /*
+ * In addition to the Control endpoint the following endpoints
+ * are required:
+ * a) bulk-in endpoint.
+ * b) bulk-out endpoint.
+ * and for Control/Bulk/Interrupt with CCI (CBI_I)
+ * c) intr-in
+ *
+ * The endpoint addresses are not fixed, so we have to read them
+ * from the device descriptors of the current interface.
+ */
+ for (i = 0 ; i < id->bNumEndpoints ; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->iface, i);
+ if (!ed) {
+ printf("%s: could not read endpoint descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->bulkin = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->bulkout = ed->bEndpointAddress;
+ } else if (sc->proto & UMASS_PROTO_CBI_I
+ && UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
+ sc->intrin = ed->bEndpointAddress;
+#ifdef USB_DEBUG
+ if (UGETW(ed->wMaxPacketSize) > 2) {
+ DPRINTF(UDMASS_CBI, ("%s: intr size is %d\n",
+ USBDEVNAME(sc->sc_dev),
+ UGETW(ed->wMaxPacketSize)));
+ }
+#endif
+ }
+ }
+
+ /* check whether we found all the endpoints we need */
+ if (!sc->bulkin || !sc->bulkout
+ || (sc->proto & UMASS_PROTO_CBI_I && !sc->intrin) ) {
+ DPRINTF(UDMASS_USB, ("%s: endpoint not found %d/%d/%d\n",
+ USBDEVNAME(sc->sc_dev),
+ sc->bulkin, sc->bulkout, sc->intrin));
+ umass_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Open the bulk-in and -out pipe */
+ err = usbd_open_pipe(sc->iface, sc->bulkout,
+ USBD_EXCLUSIVE_USE, &sc->bulkout_pipe);
+ if (err) {
+ DPRINTF(UDMASS_USB, ("%s: cannot open %d-out pipe (bulk)\n",
+ USBDEVNAME(sc->sc_dev), sc->bulkout));
+ umass_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ err = usbd_open_pipe(sc->iface, sc->bulkin,
+ USBD_EXCLUSIVE_USE, &sc->bulkin_pipe);
+ if (err) {
+ DPRINTF(UDMASS_USB, ("%s: could not open %d-in pipe (bulk)\n",
+ USBDEVNAME(sc->sc_dev), sc->bulkin));
+ umass_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ /* Open the intr-in pipe if the protocol is CBI with CCI.
+ * Note: early versions of the Zip drive do have an interrupt pipe, but
+ * this pipe is unused.
+ *
+ * We do not open the interrupt pipe as an interrupt pipe, but as a
+ * normal bulk endpoint. We send an IN transfer down the wire at the
+ * appropriate time, because we know exactly when to expect data on
+ * that endpoint. This saves bandwidth, but more important, makes the
+ * code for handling the data on that endpoint simpler. No data
+ * arriving concurrently.
+ */
+ if (sc->proto & UMASS_PROTO_CBI_I) {
+ err = usbd_open_pipe(sc->iface, sc->intrin,
+ USBD_EXCLUSIVE_USE, &sc->intrin_pipe);
+ if (err) {
+ DPRINTF(UDMASS_USB, ("%s: couldn't open %d-in (intr)\n",
+ USBDEVNAME(sc->sc_dev), sc->intrin));
+ umass_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ }
+
+ /* initialisation of generic part */
+ sc->transfer_state = TSTATE_ATTACH;
+
+ /* request a sufficient number of xfer handles */
+ for (i = 0; i < XFER_NR; i++) {
+ sc->transfer_xfer[i] = usbd_alloc_xfer(uaa->device);
+ if (!sc->transfer_xfer[i]) {
+ DPRINTF(UDMASS_USB, ("%s: Out of memory\n",
+ USBDEVNAME(sc->sc_dev)));
+ umass_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ }
+
+ /* Initialise the wire protocol specific methods */
+ if (sc->proto & UMASS_PROTO_BBB) {
+ sc->reset = umass_bbb_reset;
+ sc->transfer = umass_bbb_transfer;
+ sc->state = umass_bbb_state;
+ } else if (sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I)) {
+ sc->reset = umass_cbi_reset;
+ sc->transfer = umass_cbi_transfer;
+ sc->state = umass_cbi_state;
+#ifdef USB_DEBUG
+ } else {
+ panic("%s:%d: Unknown proto 0x%02x",
+ __FILE__, __LINE__, sc->proto);
+#endif
+ }
+
+ if (sc->proto & UMASS_PROTO_SCSI)
+ sc->transform = umass_scsi_transform;
+ else if (sc->proto & UMASS_PROTO_UFI)
+ sc->transform = umass_ufi_transform;
+ else if (sc->proto & UMASS_PROTO_ATAPI)
+ sc->transform = umass_atapi_transform;
+ else if (sc->proto & UMASS_PROTO_RBC)
+ sc->transform = umass_rbc_transform;
+#ifdef USB_DEBUG
+ else
+ panic("No transformation defined for command proto 0x%02x",
+ sc->proto & UMASS_PROTO_COMMAND);
+#endif
+
+ /* From here onwards the device can be used. */
+
+ if (sc->quirks & SHUTTLE_INIT)
+ umass_init_shuttle(sc);
+
+ /* Get the maximum LUN supported by the device.
+ */
+ if ((sc->proto & UMASS_PROTO_WIRE) == UMASS_PROTO_BBB)
+ sc->maxlun = umass_bbb_get_max_lun(sc);
+ else
+ sc->maxlun = 0;
+
+ if ((sc->proto & UMASS_PROTO_SCSI) ||
+ (sc->proto & UMASS_PROTO_ATAPI) ||
+ (sc->proto & UMASS_PROTO_UFI) ||
+ (sc->proto & UMASS_PROTO_RBC)) {
+ /* Prepare the SCSI command block */
+ sc->cam_scsi_sense.opcode = REQUEST_SENSE;
+ sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY;
+
+ /* register the SIM */
+ err = umass_cam_attach_sim(sc);
+ if (err) {
+ umass_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ /* scan the new sim */
+ err = umass_cam_attach(sc);
+ if (err) {
+ umass_cam_detach_sim(sc);
+ umass_detach(self);
+ USB_ATTACH_ERROR_RETURN;
+ }
+ } else {
+ panic("%s:%d: Unknown proto 0x%02x",
+ __FILE__, __LINE__, sc->proto);
+ }
+
+ sc->transfer_state = TSTATE_IDLE;
+ DPRINTF(UDMASS_GEN, ("%s: Attach finished\n", USBDEVNAME(sc->sc_dev)));
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+USB_DETACH(umass)
+{
+ USB_DETACH_START(umass, sc);
+ int err = 0;
+ int i;
+
+ DPRINTF(UDMASS_USB, ("%s: detached\n", USBDEVNAME(sc->sc_dev)));
+
+ sc->flags |= UMASS_FLAGS_GONE;
+
+ if ((sc->proto & UMASS_PROTO_SCSI) ||
+ (sc->proto & UMASS_PROTO_ATAPI) ||
+ (sc->proto & UMASS_PROTO_UFI) ||
+ (sc->proto & UMASS_PROTO_RBC))
+ /* detach the SCSI host controller (SIM) */
+ err = umass_cam_detach_sim(sc);
+
+ for (i = 0; i < XFER_NR; i++)
+ if (sc->transfer_xfer[i])
+ usbd_free_xfer(sc->transfer_xfer[i]);
+
+ /* remove all the pipes */
+ if (sc->bulkout_pipe)
+ usbd_close_pipe(sc->bulkout_pipe);
+ if (sc->bulkin_pipe)
+ usbd_close_pipe(sc->bulkin_pipe);
+ if (sc->intrin_pipe)
+ usbd_close_pipe(sc->intrin_pipe);
+
+ return(err);
+}
+
+Static void
+umass_init_shuttle(struct umass_softc *sc)
+{
+ usb_device_request_t req;
+ u_char status[2];
+
+ /* The Linux driver does this, but no one can tell us what the
+ * command does.
+ */
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = 1; /* XXX unknown command */
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->ifaceno);
+ USETW(req.wLength, sizeof status);
+ (void) usbd_do_request(sc->sc_udev, &req, &status);
+
+ DPRINTF(UDMASS_GEN, ("%s: Shuttle init returned 0x%02x%02x\n",
+ USBDEVNAME(sc->sc_dev), status[0], status[1]));
+}
+
+ /*
+ * Generic functions to handle transfers
+ */
+
+Static usbd_status
+umass_setup_transfer(struct umass_softc *sc, usbd_pipe_handle pipe,
+ void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Initialise a USB transfer and then schedule it */
+
+ (void) usbd_setup_xfer(xfer, pipe, (void *) sc, buffer, buflen, flags,
+ sc->timeout, sc->state);
+
+ err = usbd_transfer(xfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(UDMASS_BBB, ("%s: failed to setup transfer, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+ return(err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+
+Static usbd_status
+umass_setup_ctrl_transfer(struct umass_softc *sc, usbd_device_handle udev,
+ usb_device_request_t *req,
+ void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Initialise a USB control transfer and then schedule it */
+
+ (void) usbd_setup_default_xfer(xfer, udev, (void *) sc,
+ sc->timeout, req, buffer, buflen, flags, sc->state);
+
+ err = usbd_transfer(xfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(UDMASS_BBB, ("%s: failed to setup ctrl transfer, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+
+ /* do not reset, as this would make us loop */
+ return(err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static void
+umass_clear_endpoint_stall(struct umass_softc *sc,
+ u_int8_t endpt, usbd_pipe_handle pipe,
+ int state, usbd_xfer_handle xfer)
+{
+ usbd_device_handle udev;
+
+ DPRINTF(UDMASS_BBB, ("%s: Clear endpoint 0x%02x stall\n",
+ USBDEVNAME(sc->sc_dev), endpt));
+
+ usbd_interface2device_handle(sc->iface, &udev);
+
+ sc->transfer_state = state;
+
+ usbd_clear_endpoint_toggle(pipe);
+
+ sc->request.bmRequestType = UT_WRITE_ENDPOINT;
+ sc->request.bRequest = UR_CLEAR_FEATURE;
+ USETW(sc->request.wValue, UF_ENDPOINT_HALT);
+ USETW(sc->request.wIndex, endpt);
+ USETW(sc->request.wLength, 0);
+ umass_setup_ctrl_transfer(sc, udev, &sc->request, NULL, 0, 0, xfer);
+}
+
+Static void
+umass_reset(struct umass_softc *sc, transfer_cb_f cb, void *priv)
+{
+ sc->transfer_cb = cb;
+ sc->transfer_priv = priv;
+
+ /* The reset is a forced reset, so no error (yet) */
+ sc->reset(sc, STATUS_CMD_OK);
+}
+
+/*
+ * Bulk protocol specific functions
+ */
+
+Static void
+umass_bbb_reset(struct umass_softc *sc, int status)
+{
+ usbd_device_handle udev;
+
+ KASSERT(sc->proto & UMASS_PROTO_BBB,
+ ("%s: umass_bbb_reset: wrong sc->proto 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), sc->proto));
+
+ /*
+ * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class)
+ *
+ * For Reset Recovery the host shall issue in the following order:
+ * a) a Bulk-Only Mass Storage Reset
+ * b) a Clear Feature HALT to the Bulk-In endpoint
+ * c) a Clear Feature HALT to the Bulk-Out endpoint
+ *
+ * This is done in 3 steps, states:
+ * TSTATE_BBB_RESET1
+ * TSTATE_BBB_RESET2
+ * TSTATE_BBB_RESET3
+ *
+ * If the reset doesn't succeed, the device should be port reset.
+ */
+
+ DPRINTF(UDMASS_BBB, ("%s: Bulk Reset\n",
+ USBDEVNAME(sc->sc_dev)));
+
+ sc->transfer_state = TSTATE_BBB_RESET1;
+ sc->transfer_status = status;
+
+ usbd_interface2device_handle(sc->iface, &udev);
+
+ /* reset is a class specific interface write */
+ sc->request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ sc->request.bRequest = UR_BBB_RESET;
+ USETW(sc->request.wValue, 0);
+ USETW(sc->request.wIndex, sc->ifaceno);
+ USETW(sc->request.wLength, 0);
+ umass_setup_ctrl_transfer(sc, udev, &sc->request, NULL, 0, 0,
+ sc->transfer_xfer[XFER_BBB_RESET1]);
+}
+
+Static void
+umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen,
+ void *data, int datalen, int dir, u_int timeout,
+ transfer_cb_f cb, void *priv)
+{
+ KASSERT(sc->proto & UMASS_PROTO_BBB,
+ ("%s: umass_bbb_transfer: wrong sc->proto 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), sc->proto));
+
+ /* Be a little generous. */
+ sc->timeout = timeout + UMASS_TIMEOUT;
+
+ /*
+ * Do a Bulk-Only transfer with cmdlen bytes from cmd, possibly
+ * a data phase of datalen bytes from/to the device and finally a
+ * csw read phase.
+ * If the data direction was inbound a maximum of datalen bytes
+ * is stored in the buffer pointed to by data.
+ *
+ * umass_bbb_transfer initialises the transfer and lets the state
+ * machine in umass_bbb_state handle the completion. It uses the
+ * following states:
+ * TSTATE_BBB_COMMAND
+ * -> TSTATE_BBB_DATA
+ * -> TSTATE_BBB_STATUS
+ * -> TSTATE_BBB_STATUS2
+ * -> TSTATE_BBB_IDLE
+ *
+ * An error in any of those states will invoke
+ * umass_bbb_reset.
+ */
+
+ /* check the given arguments */
+ KASSERT(datalen == 0 || data != NULL,
+ ("%s: datalen > 0, but no buffer",USBDEVNAME(sc->sc_dev)));
+ KASSERT(cmdlen <= CBWCDBLENGTH,
+ ("%s: cmdlen exceeds CDB length in CBW (%d > %d)",
+ USBDEVNAME(sc->sc_dev), cmdlen, CBWCDBLENGTH));
+ KASSERT(dir == DIR_NONE || datalen > 0,
+ ("%s: datalen == 0 while direction is not NONE\n",
+ USBDEVNAME(sc->sc_dev)));
+ KASSERT(datalen == 0 || dir != DIR_NONE,
+ ("%s: direction is NONE while datalen is not zero\n",
+ USBDEVNAME(sc->sc_dev)));
+ KASSERT(sizeof(umass_bbb_cbw_t) == UMASS_BBB_CBW_SIZE,
+ ("%s: CBW struct does not have the right size (%ld vs. %d)\n",
+ USBDEVNAME(sc->sc_dev),
+ (long)sizeof(umass_bbb_cbw_t), UMASS_BBB_CBW_SIZE));
+ KASSERT(sizeof(umass_bbb_csw_t) == UMASS_BBB_CSW_SIZE,
+ ("%s: CSW struct does not have the right size (%ld vs. %d)\n",
+ USBDEVNAME(sc->sc_dev),
+ (long)sizeof(umass_bbb_csw_t), UMASS_BBB_CSW_SIZE));
+
+ /*
+ * Determine the direction of the data transfer and the length.
+ *
+ * dCBWDataTransferLength (datalen) :
+ * This field indicates the number of bytes of data that the host
+ * intends to transfer on the IN or OUT Bulk endpoint(as indicated by
+ * the Direction bit) during the execution of this command. If this
+ * field is set to 0, the device will expect that no data will be
+ * transferred IN or OUT during this command, regardless of the value
+ * of the Direction bit defined in dCBWFlags.
+ *
+ * dCBWFlags (dir) :
+ * The bits of the Flags field are defined as follows:
+ * Bits 0-6 reserved
+ * Bit 7 Direction - this bit shall be ignored if the
+ * dCBWDataTransferLength field is zero.
+ * 0 = data Out from host to device
+ * 1 = data In from device to host
+ */
+
+ /* Fill in the Command Block Wrapper
+ * We fill in all the fields, so there is no need to bzero it first.
+ */
+ USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
+ /* We don't care about the initial value, as long as the values are unique */
+ USETDW(sc->cbw.dCBWTag, UGETDW(sc->cbw.dCBWTag) + 1);
+ USETDW(sc->cbw.dCBWDataTransferLength, datalen);
+ /* DIR_NONE is treated as DIR_OUT (0x00) */
+ sc->cbw.bCBWFlags = (dir == DIR_IN? CBWFLAGS_IN:CBWFLAGS_OUT);
+ sc->cbw.bCBWLUN = lun;
+ sc->cbw.bCDBLength = cmdlen;
+ bcopy(cmd, sc->cbw.CBWCDB, cmdlen);
+
+ DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw));
+
+ /* store the details for the data transfer phase */
+ sc->transfer_dir = dir;
+ sc->transfer_data = data;
+ sc->transfer_datalen = datalen;
+ sc->transfer_actlen = 0;
+ sc->transfer_cb = cb;
+ sc->transfer_priv = priv;
+ sc->transfer_status = STATUS_CMD_OK;
+
+ /* move from idle to the command state */
+ sc->transfer_state = TSTATE_BBB_COMMAND;
+
+ /* Send the CBW from host to device via bulk-out endpoint. */
+ if (umass_setup_transfer(sc, sc->bulkout_pipe,
+ &sc->cbw, UMASS_BBB_CBW_SIZE, 0,
+ sc->transfer_xfer[XFER_BBB_CBW])) {
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ }
+}
+
+
+Static void
+umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ struct umass_softc *sc = (struct umass_softc *) priv;
+ usbd_xfer_handle next_xfer;
+
+ KASSERT(sc->proto & UMASS_PROTO_BBB,
+ ("%s: umass_bbb_state: wrong sc->proto 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), sc->proto));
+
+ /*
+ * State handling for BBB transfers.
+ *
+ * The subroutine is rather long. It steps through the states given in
+ * Annex A of the Bulk-Only specification.
+ * Each state first does the error handling of the previous transfer
+ * and then prepares the next transfer.
+ * Each transfer is done asynchronously so after the request/transfer
+ * has been submitted you will find a 'return;'.
+ */
+
+ DPRINTF(UDMASS_BBB, ("%s: Handling BBB state %d (%s), xfer=%p, %s\n",
+ USBDEVNAME(sc->sc_dev), sc->transfer_state,
+ states[sc->transfer_state], xfer, usbd_errstr(err)));
+
+ switch (sc->transfer_state) {
+
+ /***** Bulk Transfer *****/
+ case TSTATE_BBB_COMMAND:
+ /* Command transport phase, error handling */
+ if (err) {
+ DPRINTF(UDMASS_BBB, ("%s: failed to send CBW\n",
+ USBDEVNAME(sc->sc_dev)));
+ /* If the device detects that the CBW is invalid, then
+ * the device may STALL both bulk endpoints and require
+ * a Bulk-Reset
+ */
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ }
+
+ /* Data transport phase, setup transfer */
+ sc->transfer_state = TSTATE_BBB_DATA;
+ if (sc->transfer_dir == DIR_IN) {
+ if (umass_setup_transfer(sc, sc->bulkin_pipe,
+ sc->transfer_data, sc->transfer_datalen,
+ USBD_SHORT_XFER_OK,
+ sc->transfer_xfer[XFER_BBB_DATA]))
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+
+ return;
+ } else if (sc->transfer_dir == DIR_OUT) {
+ if (umass_setup_transfer(sc, sc->bulkout_pipe,
+ sc->transfer_data, sc->transfer_datalen,
+ 0, /* fixed length transfer */
+ sc->transfer_xfer[XFER_BBB_DATA]))
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+
+ return;
+ } else {
+ DPRINTF(UDMASS_BBB, ("%s: no data phase\n",
+ USBDEVNAME(sc->sc_dev)));
+ }
+
+ /* FALLTHROUGH if no data phase, err == 0 */
+ case TSTATE_BBB_DATA:
+ /* Command transport phase, error handling (ignored if no data
+ * phase (fallthrough from previous state)) */
+ if (sc->transfer_dir != DIR_NONE) {
+ /* retrieve the length of the transfer that was done */
+ usbd_get_xfer_status(xfer, NULL, NULL,
+ &sc->transfer_actlen, NULL);
+
+ if (err) {
+ DPRINTF(UDMASS_BBB, ("%s: Data-%s %db failed, "
+ "%s\n", USBDEVNAME(sc->sc_dev),
+ (sc->transfer_dir == DIR_IN?"in":"out"),
+ sc->transfer_datalen,usbd_errstr(err)));
+
+ if (err == USBD_STALLED) {
+ umass_clear_endpoint_stall(sc,
+ (sc->transfer_dir == DIR_IN?
+ sc->bulkin:sc->bulkout),
+ (sc->transfer_dir == DIR_IN?
+ sc->bulkin_pipe:sc->bulkout_pipe),
+ TSTATE_BBB_DCLEAR,
+ sc->transfer_xfer[XFER_BBB_DCLEAR]);
+ return;
+ } else {
+ /* Unless the error is a pipe stall the
+ * error is fatal.
+ */
+ umass_bbb_reset(sc,STATUS_WIRE_FAILED);
+ return;
+ }
+ }
+ }
+
+ DIF(UDMASS_BBB, if (sc->transfer_dir == DIR_IN)
+ umass_dump_buffer(sc, sc->transfer_data,
+ sc->transfer_datalen, 48));
+
+
+
+ /* FALLTHROUGH, err == 0 (no data phase or successfull) */
+ case TSTATE_BBB_DCLEAR: /* stall clear after data phase */
+ case TSTATE_BBB_SCLEAR: /* stall clear after status phase */
+ /* Reading of CSW after bulk stall condition in data phase
+ * (TSTATE_BBB_DATA2) or bulk-in stall condition after
+ * reading CSW (TSTATE_BBB_SCLEAR).
+ * In the case of no data phase or successfull data phase,
+ * err == 0 and the following if block is passed.
+ */
+ if (err) { /* should not occur */
+ /* try the transfer below, even if clear stall failed */
+ DPRINTF(UDMASS_BBB, ("%s: bulk-%s stall clear failed"
+ ", %s\n", USBDEVNAME(sc->sc_dev),
+ (sc->transfer_dir == DIR_IN? "in":"out"),
+ usbd_errstr(err)));
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ }
+
+ /* Status transport phase, setup transfer */
+ if (sc->transfer_state == TSTATE_BBB_COMMAND ||
+ sc->transfer_state == TSTATE_BBB_DATA ||
+ sc->transfer_state == TSTATE_BBB_DCLEAR) {
+ /* After no data phase, successfull data phase and
+ * after clearing bulk-in/-out stall condition
+ */
+ sc->transfer_state = TSTATE_BBB_STATUS1;
+ next_xfer = sc->transfer_xfer[XFER_BBB_CSW1];
+ } else {
+ /* After first attempt of fetching CSW */
+ sc->transfer_state = TSTATE_BBB_STATUS2;
+ next_xfer = sc->transfer_xfer[XFER_BBB_CSW2];
+ }
+
+ /* Read the Command Status Wrapper via bulk-in endpoint. */
+ if (umass_setup_transfer(sc, sc->bulkin_pipe,
+ &sc->csw, UMASS_BBB_CSW_SIZE, 0,
+ next_xfer)) {
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ }
+
+ return;
+ case TSTATE_BBB_STATUS1: /* first attempt */
+ case TSTATE_BBB_STATUS2: /* second attempt */
+ /* Status transfer, error handling */
+ if (err) {
+ DPRINTF(UDMASS_BBB, ("%s: Failed to read CSW, %s%s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err),
+ (sc->transfer_state == TSTATE_BBB_STATUS1?
+ ", retrying":"")));
+
+ /* If this was the first attempt at fetching the CSW
+ * retry it, otherwise fail.
+ */
+ if (sc->transfer_state == TSTATE_BBB_STATUS1) {
+ umass_clear_endpoint_stall(sc,
+ sc->bulkin, sc->bulkin_pipe,
+ TSTATE_BBB_SCLEAR,
+ sc->transfer_xfer[XFER_BBB_SCLEAR]);
+ return;
+ } else {
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ }
+ }
+
+ DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &sc->csw));
+
+ /* Translate weird command-status signatures. */
+ if ((sc->quirks & WRONG_CSWSIG) &&
+ UGETDW(sc->csw.dCSWSignature) == CSWSIGNATURE_OLYMPUS_C1)
+ USETDW(sc->csw.dCSWSignature, CSWSIGNATURE);
+
+ int Residue;
+ Residue = UGETDW(sc->csw.dCSWDataResidue);
+ if (Residue == 0 &&
+ sc->transfer_datalen - sc->transfer_actlen != 0)
+ Residue = sc->transfer_datalen - sc->transfer_actlen;
+
+ /* Check CSW and handle any error */
+ if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) {
+ /* Invalid CSW: Wrong signature or wrong tag might
+ * indicate that the device is confused -> reset it.
+ */
+ printf("%s: Invalid CSW: sig 0x%08x should be 0x%08x\n",
+ USBDEVNAME(sc->sc_dev),
+ UGETDW(sc->csw.dCSWSignature),
+ CSWSIGNATURE);
+
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ } else if (UGETDW(sc->csw.dCSWTag)
+ != UGETDW(sc->cbw.dCBWTag)) {
+ printf("%s: Invalid CSW: tag %d should be %d\n",
+ USBDEVNAME(sc->sc_dev),
+ UGETDW(sc->csw.dCSWTag),
+ UGETDW(sc->cbw.dCBWTag));
+
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+
+ /* CSW is valid here */
+ } else if (sc->csw.bCSWStatus > CSWSTATUS_PHASE) {
+ printf("%s: Invalid CSW: status %d > %d\n",
+ USBDEVNAME(sc->sc_dev),
+ sc->csw.bCSWStatus,
+ CSWSTATUS_PHASE);
+
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ } else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) {
+ printf("%s: Phase Error, residue = %d\n",
+ USBDEVNAME(sc->sc_dev), Residue);
+
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+
+ } else if (sc->transfer_actlen > sc->transfer_datalen) {
+ /* Buffer overrun! Don't let this go by unnoticed */
+ panic("%s: transferred %db instead of %db",
+ USBDEVNAME(sc->sc_dev),
+ sc->transfer_actlen, sc->transfer_datalen);
+
+ } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) {
+ DPRINTF(UDMASS_BBB, ("%s: Command Failed, res = %d\n",
+ USBDEVNAME(sc->sc_dev), Residue));
+
+ /* SCSI command failed but transfer was succesful */
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv, Residue,
+ STATUS_CMD_FAILED);
+ return;
+
+ } else { /* success */
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv, Residue,
+ STATUS_CMD_OK);
+
+ return;
+ }
+
+ /***** Bulk Reset *****/
+ case TSTATE_BBB_RESET1:
+ if (err)
+ printf("%s: BBB reset failed, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+
+ umass_clear_endpoint_stall(sc,
+ sc->bulkin, sc->bulkin_pipe, TSTATE_BBB_RESET2,
+ sc->transfer_xfer[XFER_BBB_RESET2]);
+
+ return;
+ case TSTATE_BBB_RESET2:
+ if (err) /* should not occur */
+ printf("%s: BBB bulk-in clear stall failed, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ /* no error recovery, otherwise we end up in a loop */
+
+ umass_clear_endpoint_stall(sc,
+ sc->bulkout, sc->bulkout_pipe, TSTATE_BBB_RESET3,
+ sc->transfer_xfer[XFER_BBB_RESET3]);
+
+ return;
+ case TSTATE_BBB_RESET3:
+ if (err) /* should not occur */
+ printf("%s: BBB bulk-out clear stall failed, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ /* no error recovery, otherwise we end up in a loop */
+
+ sc->transfer_state = TSTATE_IDLE;
+ if (sc->transfer_priv) {
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen,
+ sc->transfer_status);
+ }
+
+ return;
+
+ /***** Default *****/
+ default:
+ panic("%s: Unknown state %d",
+ USBDEVNAME(sc->sc_dev), sc->transfer_state);
+ }
+}
+
+Static int
+umass_bbb_get_max_lun(struct umass_softc *sc)
+{
+ usbd_device_handle udev;
+ usb_device_request_t req;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ int maxlun = 0;
+ u_int8_t buf = 0;
+
+ usbd_interface2device_handle(sc->iface, &udev);
+ id = usbd_get_interface_descriptor(sc->iface);
+
+ /* The Get Max Lun command is a class-specific request. */
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_BBB_GET_MAX_LUN;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, id->bInterfaceNumber);
+ USETW(req.wLength, 1);
+
+ err = usbd_do_request(udev, &req, &buf);
+ switch (err) {
+ case USBD_NORMAL_COMPLETION:
+ maxlun = buf;
+ DPRINTF(UDMASS_BBB, ("%s: Max Lun is %d\n",
+ USBDEVNAME(sc->sc_dev), maxlun));
+ break;
+ case USBD_STALLED:
+ case USBD_SHORT_XFER:
+ default:
+ /* Device doesn't support Get Max Lun request. */
+ printf("%s: Get Max Lun not supported (%s)\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ /* XXX Should we port_reset the device? */
+ break;
+ }
+
+ return(maxlun);
+}
+
+/*
+ * Command/Bulk/Interrupt (CBI) specific functions
+ */
+
+Static int
+umass_cbi_adsc(struct umass_softc *sc, char *buffer, int buflen,
+ usbd_xfer_handle xfer)
+{
+ usbd_device_handle udev;
+
+ KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I),
+ ("%s: umass_cbi_adsc: wrong sc->proto 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), sc->proto));
+
+ usbd_interface2device_handle(sc->iface, &udev);
+
+ sc->request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ sc->request.bRequest = UR_CBI_ADSC;
+ USETW(sc->request.wValue, 0);
+ USETW(sc->request.wIndex, sc->ifaceno);
+ USETW(sc->request.wLength, buflen);
+ return umass_setup_ctrl_transfer(sc, udev, &sc->request, buffer,
+ buflen, 0, xfer);
+}
+
+
+Static void
+umass_cbi_reset(struct umass_softc *sc, int status)
+{
+ int i;
+# define SEND_DIAGNOSTIC_CMDLEN 12
+
+ KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I),
+ ("%s: umass_cbi_reset: wrong sc->proto 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), sc->proto));
+
+ /*
+ * Command Block Reset Protocol
+ *
+ * First send a reset request to the device. Then clear
+ * any possibly stalled bulk endpoints.
+ *
+ * This is done in 3 steps, states:
+ * TSTATE_CBI_RESET1
+ * TSTATE_CBI_RESET2
+ * TSTATE_CBI_RESET3
+ *
+ * If the reset doesn't succeed, the device should be port reset.
+ */
+
+ DPRINTF(UDMASS_CBI, ("%s: CBI Reset\n",
+ USBDEVNAME(sc->sc_dev)));
+
+ KASSERT(sizeof(sc->cbl) >= SEND_DIAGNOSTIC_CMDLEN,
+ ("%s: CBL struct is too small (%ld < %d)\n",
+ USBDEVNAME(sc->sc_dev),
+ (long)sizeof(sc->cbl), SEND_DIAGNOSTIC_CMDLEN));
+
+ sc->transfer_state = TSTATE_CBI_RESET1;
+ sc->transfer_status = status;
+
+ /* The 0x1d code is the SEND DIAGNOSTIC command. To distinguish between
+ * the two the last 10 bytes of the cbl is filled with 0xff (section
+ * 2.2 of the CBI spec).
+ */
+ sc->cbl[0] = 0x1d; /* Command Block Reset */
+ sc->cbl[1] = 0x04;
+ for (i = 2; i < SEND_DIAGNOSTIC_CMDLEN; i++)
+ sc->cbl[i] = 0xff;
+
+ umass_cbi_adsc(sc, sc->cbl, SEND_DIAGNOSTIC_CMDLEN,
+ sc->transfer_xfer[XFER_CBI_RESET1]);
+ /* XXX if the command fails we should reset the port on the hub */
+}
+
+Static void
+umass_cbi_transfer(struct umass_softc *sc, int lun,
+ void *cmd, int cmdlen, void *data, int datalen, int dir,
+ u_int timeout, transfer_cb_f cb, void *priv)
+{
+ KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I),
+ ("%s: umass_cbi_transfer: wrong sc->proto 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), sc->proto));
+
+ /* Be a little generous. */
+ sc->timeout = timeout + UMASS_TIMEOUT;
+
+ /*
+ * Do a CBI transfer with cmdlen bytes from cmd, possibly
+ * a data phase of datalen bytes from/to the device and finally a
+ * csw read phase.
+ * If the data direction was inbound a maximum of datalen bytes
+ * is stored in the buffer pointed to by data.
+ *
+ * umass_cbi_transfer initialises the transfer and lets the state
+ * machine in umass_cbi_state handle the completion. It uses the
+ * following states:
+ * TSTATE_CBI_COMMAND
+ * -> XXX fill in
+ *
+ * An error in any of those states will invoke
+ * umass_cbi_reset.
+ */
+
+ /* check the given arguments */
+ KASSERT(datalen == 0 || data != NULL,
+ ("%s: datalen > 0, but no buffer",USBDEVNAME(sc->sc_dev)));
+ KASSERT(datalen == 0 || dir != DIR_NONE,
+ ("%s: direction is NONE while datalen is not zero\n",
+ USBDEVNAME(sc->sc_dev)));
+
+ /* store the details for the data transfer phase */
+ sc->transfer_dir = dir;
+ sc->transfer_data = data;
+ sc->transfer_datalen = datalen;
+ sc->transfer_actlen = 0;
+ sc->transfer_cb = cb;
+ sc->transfer_priv = priv;
+ sc->transfer_status = STATUS_CMD_OK;
+
+ /* move from idle to the command state */
+ sc->transfer_state = TSTATE_CBI_COMMAND;
+
+ DIF(UDMASS_CBI, umass_cbi_dump_cmd(sc, cmd, cmdlen));
+
+ /* Send the Command Block from host to device via control endpoint. */
+ if (umass_cbi_adsc(sc, cmd, cmdlen, sc->transfer_xfer[XFER_CBI_CB]))
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+}
+
+Static void
+umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ struct umass_softc *sc = (struct umass_softc *) priv;
+
+ KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I),
+ ("%s: umass_cbi_state: wrong sc->proto 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), sc->proto));
+
+ /*
+ * State handling for CBI transfers.
+ */
+
+ DPRINTF(UDMASS_CBI, ("%s: Handling CBI state %d (%s), xfer=%p, %s\n",
+ USBDEVNAME(sc->sc_dev), sc->transfer_state,
+ states[sc->transfer_state], xfer, usbd_errstr(err)));
+
+ switch (sc->transfer_state) {
+
+ /***** CBI Transfer *****/
+ case TSTATE_CBI_COMMAND:
+ if (err == USBD_STALLED) {
+ DPRINTF(UDMASS_CBI, ("%s: Command Transport failed\n",
+ USBDEVNAME(sc->sc_dev)));
+ /* Status transport by control pipe (section 2.3.2.1).
+ * The command contained in the command block failed.
+ *
+ * The control pipe has already been unstalled by the
+ * USB stack.
+ * Section 2.4.3.1.1 states that the bulk in endpoints
+ * should not be stalled at this point.
+ */
+
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen,
+ STATUS_CMD_FAILED);
+
+ return;
+ } else if (err) {
+ DPRINTF(UDMASS_CBI, ("%s: failed to send ADSC\n",
+ USBDEVNAME(sc->sc_dev)));
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+
+ return;
+ }
+
+ sc->transfer_state = TSTATE_CBI_DATA;
+ if (sc->transfer_dir == DIR_IN) {
+ if (umass_setup_transfer(sc, sc->bulkin_pipe,
+ sc->transfer_data, sc->transfer_datalen,
+ USBD_SHORT_XFER_OK,
+ sc->transfer_xfer[XFER_CBI_DATA]))
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+
+ } else if (sc->transfer_dir == DIR_OUT) {
+ if (umass_setup_transfer(sc, sc->bulkout_pipe,
+ sc->transfer_data, sc->transfer_datalen,
+ 0, /* fixed length transfer */
+ sc->transfer_xfer[XFER_CBI_DATA]))
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+
+ } else if (sc->proto & UMASS_PROTO_CBI_I) {
+ DPRINTF(UDMASS_CBI, ("%s: no data phase\n",
+ USBDEVNAME(sc->sc_dev)));
+ sc->transfer_state = TSTATE_CBI_STATUS;
+ if (umass_setup_transfer(sc, sc->intrin_pipe,
+ &sc->sbl, sizeof(sc->sbl),
+ 0, /* fixed length transfer */
+ sc->transfer_xfer[XFER_CBI_STATUS])){
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+ } else {
+ DPRINTF(UDMASS_CBI, ("%s: no data phase\n",
+ USBDEVNAME(sc->sc_dev)));
+ /* No command completion interrupt. Request
+ * sense data.
+ */
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ 0, STATUS_CMD_UNKNOWN);
+ }
+
+ return;
+
+ case TSTATE_CBI_DATA:
+ /* retrieve the length of the transfer that was done */
+ usbd_get_xfer_status(xfer,NULL,NULL,&sc->transfer_actlen,NULL);
+
+ if (err) {
+ DPRINTF(UDMASS_CBI, ("%s: Data-%s %db failed, "
+ "%s\n", USBDEVNAME(sc->sc_dev),
+ (sc->transfer_dir == DIR_IN?"in":"out"),
+ sc->transfer_datalen,usbd_errstr(err)));
+
+ if (err == USBD_STALLED) {
+ umass_clear_endpoint_stall(sc,
+ sc->bulkin, sc->bulkin_pipe,
+ TSTATE_CBI_DCLEAR,
+ sc->transfer_xfer[XFER_CBI_DCLEAR]);
+ } else {
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+ return;
+ }
+
+ DIF(UDMASS_CBI, if (sc->transfer_dir == DIR_IN)
+ umass_dump_buffer(sc, sc->transfer_data,
+ sc->transfer_actlen, 48));
+
+ if (sc->proto & UMASS_PROTO_CBI_I) {
+ sc->transfer_state = TSTATE_CBI_STATUS;
+ if (umass_setup_transfer(sc, sc->intrin_pipe,
+ &sc->sbl, sizeof(sc->sbl),
+ 0, /* fixed length transfer */
+ sc->transfer_xfer[XFER_CBI_STATUS])){
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+ } else {
+ /* No command completion interrupt. Request
+ * sense to get status of command.
+ */
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen - sc->transfer_actlen,
+ STATUS_CMD_UNKNOWN);
+ }
+ return;
+
+ case TSTATE_CBI_STATUS:
+ if (err) {
+ DPRINTF(UDMASS_CBI, ("%s: Status Transport failed\n",
+ USBDEVNAME(sc->sc_dev)));
+ /* Status transport by interrupt pipe (section 2.3.2.2).
+ */
+
+ if (err == USBD_STALLED) {
+ umass_clear_endpoint_stall(sc,
+ sc->intrin, sc->intrin_pipe,
+ TSTATE_CBI_SCLEAR,
+ sc->transfer_xfer[XFER_CBI_SCLEAR]);
+ } else {
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+ return;
+ }
+
+ /* Dissect the information in the buffer */
+
+ if (sc->proto & UMASS_PROTO_UFI) {
+ int status;
+
+ /* Section 3.4.3.1.3 specifies that the UFI command
+ * protocol returns an ASC and ASCQ in the interrupt
+ * data block.
+ */
+
+ DPRINTF(UDMASS_CBI, ("%s: UFI CCI, ASC = 0x%02x, "
+ "ASCQ = 0x%02x\n",
+ USBDEVNAME(sc->sc_dev),
+ sc->sbl.ufi.asc, sc->sbl.ufi.ascq));
+
+ if (sc->sbl.ufi.asc == 0 && sc->sbl.ufi.ascq == 0)
+ status = STATUS_CMD_OK;
+ else
+ status = STATUS_CMD_FAILED;
+
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen - sc->transfer_actlen,
+ status);
+ } else {
+ /* Command Interrupt Data Block */
+ DPRINTF(UDMASS_CBI, ("%s: type=0x%02x, value=0x%02x\n",
+ USBDEVNAME(sc->sc_dev),
+ sc->sbl.common.type, sc->sbl.common.value));
+
+ if (sc->sbl.common.type == IDB_TYPE_CCI) {
+ int err;
+
+ if ((sc->sbl.common.value&IDB_VALUE_STATUS_MASK)
+ == IDB_VALUE_PASS) {
+ err = STATUS_CMD_OK;
+ } else if ((sc->sbl.common.value & IDB_VALUE_STATUS_MASK)
+ == IDB_VALUE_FAIL ||
+ (sc->sbl.common.value & IDB_VALUE_STATUS_MASK)
+ == IDB_VALUE_PERSISTENT) {
+ err = STATUS_CMD_FAILED;
+ } else {
+ err = STATUS_WIRE_FAILED;
+ }
+
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen-sc->transfer_actlen,
+ err);
+ }
+ }
+ return;
+
+ case TSTATE_CBI_DCLEAR:
+ if (err) { /* should not occur */
+ printf("%s: CBI bulk-in/out stall clear failed, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen,
+ STATUS_CMD_FAILED);
+ return;
+
+ case TSTATE_CBI_SCLEAR:
+ if (err) /* should not occur */
+ printf("%s: CBI intr-in stall clear failed, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+
+ /* Something really bad is going on. Reset the device */
+ umass_cbi_reset(sc, STATUS_CMD_FAILED);
+ return;
+
+ /***** CBI Reset *****/
+ case TSTATE_CBI_RESET1:
+ if (err)
+ printf("%s: CBI reset failed, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+
+ umass_clear_endpoint_stall(sc,
+ sc->bulkin, sc->bulkin_pipe, TSTATE_CBI_RESET2,
+ sc->transfer_xfer[XFER_CBI_RESET2]);
+
+ return;
+ case TSTATE_CBI_RESET2:
+ if (err) /* should not occur */
+ printf("%s: CBI bulk-in stall clear failed, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ /* no error recovery, otherwise we end up in a loop */
+
+ umass_clear_endpoint_stall(sc,
+ sc->bulkout, sc->bulkout_pipe, TSTATE_CBI_RESET3,
+ sc->transfer_xfer[XFER_CBI_RESET3]);
+
+ return;
+ case TSTATE_CBI_RESET3:
+ if (err) /* should not occur */
+ printf("%s: CBI bulk-out stall clear failed, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ /* no error recovery, otherwise we end up in a loop */
+
+ sc->transfer_state = TSTATE_IDLE;
+ if (sc->transfer_priv) {
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen,
+ sc->transfer_status);
+ }
+
+ return;
+
+
+ /***** Default *****/
+ default:
+ panic("%s: Unknown state %d",
+ USBDEVNAME(sc->sc_dev), sc->transfer_state);
+ }
+}
+
+
+
+
+/*
+ * CAM specific functions (used by SCSI, UFI, 8070i (ATAPI))
+ */
+
+Static int
+umass_cam_attach_sim(struct umass_softc *sc)
+{
+ struct cam_devq *devq; /* Per device Queue */
+
+ /* A HBA is attached to the CAM layer.
+ *
+ * The CAM layer will then after a while start probing for
+ * devices on the bus. The number of SIMs is limited to one.
+ */
+
+ devq = cam_simq_alloc(1 /*maximum openings*/);
+ if (devq == NULL)
+ return(ENOMEM);
+
+ sc->umass_sim = cam_sim_alloc(umass_cam_action, umass_cam_poll,
+ DEVNAME_SIM,
+ sc /*priv*/,
+ USBDEVUNIT(sc->sc_dev) /*unit number*/,
+ 1 /*maximum device openings*/,
+ 0 /*maximum tagged device openings*/,
+ devq);
+ if (sc->umass_sim == NULL) {
+ cam_simq_free(devq);
+ return(ENOMEM);
+ }
+
+ if(xpt_bus_register(sc->umass_sim, USBDEVUNIT(sc->sc_dev)) !=
+ CAM_SUCCESS)
+ return(ENOMEM);
+
+ return(0);
+}
+
+Static void
+umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
+{
+#ifdef USB_DEBUG
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ DPRINTF(UDMASS_SCSI, ("%s:%d Rescan failed, 0x%04x\n",
+ periph->periph_name, periph->unit_number,
+ ccb->ccb_h.status));
+ } else {
+ DPRINTF(UDMASS_SCSI, ("%s%d: Rescan succeeded\n",
+ periph->periph_name, periph->unit_number));
+ }
+#endif
+
+ xpt_free_path(ccb->ccb_h.path);
+ free(ccb, M_USBDEV);
+}
+
+Static void
+umass_cam_rescan(void *addr)
+{
+ struct umass_softc *sc = (struct umass_softc *) addr;
+ struct cam_path *path;
+ union ccb *ccb = malloc(sizeof(union ccb), M_USBDEV, M_WAITOK);
+
+ memset(ccb, 0, sizeof(union ccb));
+
+ DPRINTF(UDMASS_SCSI, ("scbus%d: scanning for %s:%d:%d:%d\n",
+ cam_sim_path(sc->umass_sim),
+ USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ USBDEVUNIT(sc->sc_dev), CAM_LUN_WILDCARD));
+
+ if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->umass_sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD)
+ != CAM_REQ_CMP)
+ return;
+
+ xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/);
+ ccb->ccb_h.func_code = XPT_SCAN_BUS;
+ ccb->ccb_h.cbfcnp = umass_cam_rescan_callback;
+ ccb->crcn.flags = CAM_FLAG_NONE;
+ xpt_action(ccb);
+
+ /* The scan is in progress now. */
+}
+
+Static int
+umass_cam_attach(struct umass_softc *sc)
+{
+#ifndef USB_DEBUG
+ if (bootverbose)
+#endif
+ printf("%s:%d:%d:%d: Attached to scbus%d\n",
+ USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ USBDEVUNIT(sc->sc_dev), CAM_LUN_WILDCARD,
+ cam_sim_path(sc->umass_sim));
+
+ if (!cold) {
+ /* Notify CAM of the new device after 1 second delay. Any
+ * failure is benign, as the user can still do it by hand
+ * (camcontrol rescan <busno>). Only do this if we are not
+ * booting, because CAM does a scan after booting has
+ * completed, when interrupts have been enabled.
+ */
+
+ /* XXX This will bomb if the driver is unloaded between attach
+ * and execution of umass_cam_rescan.
+ */
+ timeout(umass_cam_rescan, sc, MS_TO_TICKS(200));
+ }
+
+ return(0); /* always succesfull */
+}
+
+/* umass_cam_detach
+ * detach from the CAM layer
+ */
+
+Static int
+umass_cam_detach_sim(struct umass_softc *sc)
+{
+ if (sc->umass_sim) {
+ if (xpt_bus_deregister(cam_sim_path(sc->umass_sim)))
+ cam_sim_free(sc->umass_sim, /*free_devq*/TRUE);
+ else
+ return(EBUSY);
+
+ sc->umass_sim = NULL;
+ }
+
+ return(0);
+}
+
+/* umass_cam_action
+ * CAM requests for action come through here
+ */
+
+Static void
+umass_cam_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct umass_softc *sc = (struct umass_softc *)sim->softc;
+
+ /* The softc is still there, but marked as going away. umass_cam_detach
+ * has not yet notified CAM of the lost device however.
+ */
+ if (sc && (sc->flags & UMASS_FLAGS_GONE)) {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: "
+ "Invalid target (gone)\n",
+ USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code));
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+
+ /* Verify, depending on the operation to perform, that we either got a
+ * valid sc, because an existing target was referenced, or otherwise
+ * the SIM is addressed.
+ *
+ * This avoids bombing out at a printf and does give the CAM layer some
+ * sensible feedback on errors.
+ */
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ case XPT_RESET_DEV:
+ case XPT_GET_TRAN_SETTINGS:
+ case XPT_SET_TRAN_SETTINGS:
+ case XPT_CALC_GEOMETRY:
+ /* the opcodes requiring a target. These should never occur. */
+ if (sc == NULL) {
+ printf("%s:%d:%d:%d:func_code 0x%04x: "
+ "Invalid target (target needed)\n",
+ DEVNAME_SIM, cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code);
+
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+ break;
+ case XPT_PATH_INQ:
+ case XPT_NOOP:
+ /* The opcodes sometimes aimed at a target (sc is valid),
+ * sometimes aimed at the SIM (sc is invalid and target is
+ * CAM_TARGET_WILDCARD)
+ */
+ if (sc == NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: "
+ "Invalid target (no wildcard)\n",
+ DEVNAME_SIM, cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code));
+
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+ break;
+ default:
+ /* XXX Hm, we should check the input parameters */
+ break;
+ }
+
+ /* Perform the requested action */
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ {
+ struct ccb_scsiio *csio = &ccb->csio; /* deref union */
+ int dir;
+ unsigned char *cmd;
+ int cmdlen;
+ unsigned char *rcmd;
+ int rcmdlen;
+
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SCSI_IO: "
+ "cmd: 0x%02x, flags: 0x%02x, "
+ "%db cmd/%db data/%db sense\n",
+ USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ csio->cdb_io.cdb_bytes[0],
+ ccb->ccb_h.flags & CAM_DIR_MASK,
+ csio->cdb_len, csio->dxfer_len,
+ csio->sense_len));
+
+ /* clear the end of the buffer to make sure we don't send out
+ * garbage.
+ */
+ DIF(UDMASS_SCSI, if ((ccb->ccb_h.flags & CAM_DIR_MASK)
+ == CAM_DIR_OUT)
+ umass_dump_buffer(sc, csio->data_ptr,
+ csio->dxfer_len, 48));
+
+ if (sc->transfer_state != TSTATE_IDLE) {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SCSI_IO: "
+ "I/O in progress, deferring (state %d, %s)\n",
+ USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ sc->transfer_state,states[sc->transfer_state]));
+ ccb->ccb_h.status = CAM_SCSI_BUSY;
+ xpt_done(ccb);
+ return;
+ }
+
+ switch(ccb->ccb_h.flags&CAM_DIR_MASK) {
+ case CAM_DIR_IN:
+ dir = DIR_IN;
+ break;
+ case CAM_DIR_OUT:
+ dir = DIR_OUT;
+ break;
+ default:
+ dir = DIR_NONE;
+ }
+
+ ccb->ccb_h.status = CAM_REQ_INPROG | CAM_SIM_QUEUED;
+
+
+ if (csio->ccb_h.flags & CAM_CDB_POINTER) {
+ cmd = (unsigned char *) csio->cdb_io.cdb_ptr;
+ } else {
+ cmd = (unsigned char *) &csio->cdb_io.cdb_bytes;
+ }
+ cmdlen = csio->cdb_len;
+ rcmd = (unsigned char *) &sc->cam_scsi_command;
+ rcmdlen = sizeof(sc->cam_scsi_command);
+
+ /* sc->transform will convert the command to the command
+ * (format) needed by the specific command set and return
+ * the converted command in a buffer pointed to be rcmd.
+ * We pass in a buffer, but if the command does not
+ * have to be transformed it returns a ptr to the original
+ * buffer (see umass_scsi_transform).
+ */
+
+ if (sc->transform(sc, cmd, cmdlen, &rcmd, &rcmdlen)) {
+ /*
+ * Handle EVPD inquiry for broken devices first
+ * NO_INQUIRY also implies NO_INQUIRY_EVPD
+ */
+ if ((sc->quirks & (NO_INQUIRY_EVPD | NO_INQUIRY)) &&
+ rcmd[0] == INQUIRY && (rcmd[1] & SI_EVPD)) {
+ struct scsi_sense_data *sense;
+
+ sense = &ccb->csio.sense_data;
+ bzero(sense, sizeof(*sense));
+ sense->error_code = SSD_CURRENT_ERROR;
+ sense->flags = SSD_KEY_ILLEGAL_REQUEST;
+ sense->add_sense_code = 0x24;
+ sense->extra_len = 10;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR |
+ CAM_AUTOSNS_VALID;
+ xpt_done(ccb);
+ return;
+ }
+ /* Return fake inquiry data for broken devices */
+ if ((sc->quirks & NO_INQUIRY) && rcmd[0] == INQUIRY) {
+ struct ccb_scsiio *csio = &ccb->csio;
+
+ memcpy(csio->data_ptr, &fake_inq_data,
+ sizeof(fake_inq_data));
+ csio->scsi_status = SCSI_STATUS_OK;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+ }
+ if ((sc->quirks & FORCE_SHORT_INQUIRY) &&
+ rcmd[0] == INQUIRY) {
+ csio->dxfer_len = SHORT_INQUIRY_LENGTH;
+ }
+ sc->transfer(sc, ccb->ccb_h.target_lun, rcmd, rcmdlen,
+ csio->data_ptr,
+ csio->dxfer_len, dir, ccb->ccb_h.timeout,
+ umass_cam_cb, (void *) ccb);
+ } else {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ }
+
+ break;
+ }
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_PATH_INQ:.\n",
+ (sc == NULL? DEVNAME_SIM:USBDEVNAME(sc->sc_dev)),
+ cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ /* host specific information */
+ cpi->version_num = 1;
+ cpi->hba_inquiry = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NO_6_BYTE;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = UMASS_SCSIID_MAX; /* one target */
+ cpi->initiator_id = UMASS_SCSIID_HOST;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "USB SCSI", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = USBDEVUNIT(sc->sc_dev);
+
+ if (sc == NULL) {
+ cpi->base_transfer_speed = 0;
+ cpi->max_lun = 0;
+ } else {
+ if (sc->quirks & FLOPPY_SPEED) {
+ cpi->base_transfer_speed = UMASS_FLOPPY_TRANSFER_SPEED;
+ } else {
+ cpi->base_transfer_speed = UMASS_DEFAULT_TRANSFER_SPEED;
+ }
+ cpi->max_lun = sc->maxlun;
+ }
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_RESET_DEV:
+ {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_RESET_DEV:.\n",
+ USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ umass_reset(sc, umass_cam_cb, (void *) ccb);
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_GET_TRAN_SETTINGS:.\n",
+ USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ cts->valid = 0;
+ cts->flags = 0; /* no disconnection, tagging */
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SET_TRAN_SETTINGS:.\n",
+ USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_CALC_GEOMETRY:
+ {
+ cam_calc_geometry(&ccb->ccg, /*extended*/1);
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_NOOP:
+ {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_NOOP:.\n",
+ (sc == NULL? DEVNAME_SIM:USBDEVNAME(sc->sc_dev)),
+ cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: "
+ "Not implemented\n",
+ (sc == NULL? DEVNAME_SIM:USBDEVNAME(sc->sc_dev)),
+ cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code));
+
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ xpt_done(ccb);
+ break;
+ }
+}
+
+/* umass_cam_poll
+ * all requests are handled through umass_cam_action, requests
+ * are never pending. So, nothing to do here.
+ */
+Static void
+umass_cam_poll(struct cam_sim *sim)
+{
+#ifdef USB_DEBUG
+ struct umass_softc *sc = (struct umass_softc *) sim->softc;
+
+ DPRINTF(UDMASS_SCSI, ("%s: CAM poll\n",
+ USBDEVNAME(sc->sc_dev)));
+#endif
+
+ /* nop */
+}
+
+
+/* umass_cam_cb
+ * finalise a completed CAM command
+ */
+
+Static void
+umass_cam_cb(struct umass_softc *sc, void *priv, int residue, int status)
+{
+ union ccb *ccb = (union ccb *) priv;
+ struct ccb_scsiio *csio = &ccb->csio; /* deref union */
+
+ csio->resid = residue;
+
+ switch (status) {
+ case STATUS_CMD_OK:
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+
+ case STATUS_CMD_UNKNOWN:
+ case STATUS_CMD_FAILED:
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ {
+ unsigned char *rcmd;
+ int rcmdlen;
+
+ /* fetch sense data */
+ /* the rest of the command was filled in at attach */
+ sc->cam_scsi_sense.length = csio->sense_len;
+
+ DPRINTF(UDMASS_SCSI,("%s: Fetching %db sense data\n",
+ USBDEVNAME(sc->sc_dev), csio->sense_len));
+
+ rcmd = (unsigned char *) &sc->cam_scsi_command;
+ rcmdlen = sizeof(sc->cam_scsi_command);
+
+ if (sc->transform(sc,
+ (unsigned char *) &sc->cam_scsi_sense,
+ sizeof(sc->cam_scsi_sense),
+ &rcmd, &rcmdlen)) {
+ if ((sc->quirks & FORCE_SHORT_INQUIRY) && (rcmd[0] == INQUIRY)) {
+ csio->sense_len = SHORT_INQUIRY_LENGTH;
+ }
+ sc->transfer(sc, ccb->ccb_h.target_lun,
+ rcmd, rcmdlen,
+ &csio->sense_data,
+ csio->sense_len, DIR_IN, ccb->ccb_h.timeout,
+ umass_cam_sense_cb, (void *) ccb);
+ } else {
+ panic("transform(REQUEST_SENSE) failed");
+ }
+ break;
+ }
+ case XPT_RESET_DEV: /* Reset failed */
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(ccb);
+ break;
+ default:
+ panic("umass_cam_cb called for func_code %d",
+ ccb->ccb_h.func_code);
+ }
+ break;
+
+ case STATUS_WIRE_FAILED:
+ /* the wire protocol failed and will have recovered
+ * (hopefully). We return an error to CAM and let CAM retry
+ * the command if necessary.
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(ccb);
+ break;
+ default:
+ panic("%s: Unknown status %d in umass_cam_cb",
+ USBDEVNAME(sc->sc_dev), status);
+ }
+}
+
+/* Finalise a completed autosense operation
+ */
+Static void
+umass_cam_sense_cb(struct umass_softc *sc, void *priv, int residue, int status)
+{
+ union ccb *ccb = (union ccb *) priv;
+ struct ccb_scsiio *csio = &ccb->csio; /* deref union */
+ unsigned char *rcmd;
+ int rcmdlen;
+
+ switch (status) {
+ case STATUS_CMD_OK:
+ case STATUS_CMD_UNKNOWN:
+ case STATUS_CMD_FAILED:
+ /* Getting sense data always succeeds (apart from wire
+ * failures).
+ */
+ if ((sc->quirks & RS_NO_CLEAR_UA)
+ && csio->cdb_io.cdb_bytes[0] == INQUIRY
+ && (csio->sense_data.flags & SSD_KEY)
+ == SSD_KEY_UNIT_ATTENTION) {
+ /* Ignore unit attention errors in the case where
+ * the Unit Attention state is not cleared on
+ * REQUEST SENSE. They will appear again at the next
+ * command.
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ } else if ((csio->sense_data.flags & SSD_KEY)
+ == SSD_KEY_NO_SENSE) {
+ /* No problem after all (in the case of CBI without
+ * CCI)
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ } else if ((sc->quirks & RS_NO_CLEAR_UA) &&
+ (csio->cdb_io.cdb_bytes[0] == READ_CAPACITY) &&
+ ((csio->sense_data.flags & SSD_KEY)
+ == SSD_KEY_UNIT_ATTENTION)) {
+ /*
+ * Some devices do not clear the unit attention error
+ * on request sense. We insert a test unit ready
+ * command to make sure we clear the unit attention
+ * condition, then allow the retry to proceed as
+ * usual.
+ */
+
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ csio->scsi_status = SCSI_STATUS_CHECK_COND;
+
+#if 0
+ DELAY(300000);
+#endif
+
+ DPRINTF(UDMASS_SCSI,("%s: Doing a sneaky"
+ "TEST_UNIT_READY\n",
+ USBDEVNAME(sc->sc_dev)));
+
+ /* the rest of the command was filled in at attach */
+
+ rcmd = (unsigned char *) &sc->cam_scsi_command2;
+ rcmdlen = sizeof(sc->cam_scsi_command2);
+
+ if (sc->transform(sc,
+ (unsigned char *)
+ &sc->cam_scsi_test_unit_ready,
+ sizeof(sc->cam_scsi_test_unit_ready),
+ &rcmd, &rcmdlen)) {
+ sc->transfer(sc, ccb->ccb_h.target_lun,
+ rcmd, rcmdlen,
+ NULL, 0, DIR_NONE, ccb->ccb_h.timeout,
+ umass_cam_quirk_cb, (void *) ccb);
+ } else {
+ panic("transform(TEST_UNIT_READY) failed");
+ }
+ break;
+ } else {
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ csio->scsi_status = SCSI_STATUS_CHECK_COND;
+ }
+ xpt_done(ccb);
+ break;
+
+ default:
+ DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n",
+ USBDEVNAME(sc->sc_dev), status));
+ ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
+ xpt_done(ccb);
+ }
+}
+
+/*
+ * This completion code just handles the fact that we sent a test-unit-ready
+ * after having previously failed a READ CAPACITY with CHECK_COND. Even
+ * though this command succeeded, we have to tell CAM to retry.
+ */
+Static void
+umass_cam_quirk_cb(struct umass_softc *sc, void *priv, int residue, int status)
+{
+ union ccb *ccb = (union ccb *) priv;
+
+ DPRINTF(UDMASS_SCSI, ("%s: Test unit ready returned status %d\n",
+ USBDEVNAME(sc->sc_dev), status));
+#if 0
+ ccb->ccb_h.status = CAM_REQ_CMP;
+#endif
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ xpt_done(ccb);
+}
+
+Static int
+umass_driver_load(module_t mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_UNLOAD:
+ case MOD_LOAD:
+ default:
+ return(usbd_driver_load(mod, what, arg));
+ }
+}
+
+/*
+ * SCSI specific functions
+ */
+
+Static int
+umass_scsi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen)
+{
+ switch (cmd[0]) {
+ case TEST_UNIT_READY:
+ if (sc->quirks & NO_TEST_UNIT_READY) {
+ KASSERT(*rcmdlen >= sizeof(struct scsi_start_stop_unit),
+ ("rcmdlen = %d < %ld, buffer too small",
+ *rcmdlen,
+ (long)sizeof(struct scsi_start_stop_unit)));
+ DPRINTF(UDMASS_SCSI, ("%s: Converted TEST_UNIT_READY "
+ "to START_UNIT\n", USBDEVNAME(sc->sc_dev)));
+ memset(*rcmd, 0, *rcmdlen);
+ (*rcmd)[0] = START_STOP_UNIT;
+ (*rcmd)[4] = SSS_START;
+ return 1;
+ }
+ /* fallthrough */
+ case INQUIRY:
+ /* some drives wedge when asked for full inquiry information. */
+ if (sc->quirks & FORCE_SHORT_INQUIRY) {
+ memcpy(*rcmd, cmd, cmdlen);
+ *rcmdlen = cmdlen;
+ (*rcmd)[4] = SHORT_INQUIRY_LENGTH;
+ return 1;
+ }
+ /* fallthrough */
+ default:
+ *rcmd = cmd; /* We don't need to copy it */
+ *rcmdlen = cmdlen;
+ }
+
+ return 1;
+}
+/* RBC specific functions */
+Static int
+umass_rbc_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen)
+{
+ switch (cmd[0]) {
+ /* these commands are defined in RBC: */
+ case READ_10:
+ case READ_CAPACITY:
+ case START_STOP_UNIT:
+ case SYNCHRONIZE_CACHE:
+ case WRITE_10:
+ case 0x2f: /* VERIFY_10 is absent from scsi_all.h??? */
+ case INQUIRY:
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case TEST_UNIT_READY:
+ case WRITE_BUFFER:
+ /* The following commands are not listed in my copy of the RBC specs.
+ * CAM however seems to want those, and at least the Sony DSC device
+ * appears to support those as well */
+ case REQUEST_SENSE:
+ case PREVENT_ALLOW:
+ *rcmd = cmd; /* We don't need to copy it */
+ *rcmdlen = cmdlen;
+ return 1;
+ /* All other commands are not legal in RBC */
+ default:
+ printf("%s: Unsupported RBC command 0x%02x",
+ USBDEVNAME(sc->sc_dev), cmd[0]);
+ printf("\n");
+ return 0; /* failure */
+ }
+}
+
+/*
+ * UFI specific functions
+ */
+Static int
+umass_ufi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen)
+{
+ /* A UFI command is always 12 bytes in length */
+ KASSERT(*rcmdlen >= UFI_COMMAND_LENGTH,
+ ("rcmdlen = %d < %d, buffer too small",
+ *rcmdlen, UFI_COMMAND_LENGTH));
+
+ *rcmdlen = UFI_COMMAND_LENGTH;
+ memset(*rcmd, 0, UFI_COMMAND_LENGTH);
+
+ switch (cmd[0]) {
+ /* Commands of which the format has been verified. They should work.
+ * Copy the command into the (zeroed out) destination buffer.
+ */
+ case TEST_UNIT_READY:
+ if (sc->quirks & NO_TEST_UNIT_READY) {
+ /* Some devices do not support this command.
+ * Start Stop Unit should give the same results
+ */
+ DPRINTF(UDMASS_UFI, ("%s: Converted TEST_UNIT_READY "
+ "to START_UNIT\n", USBDEVNAME(sc->sc_dev)));
+ (*rcmd)[0] = START_STOP_UNIT;
+ (*rcmd)[4] = SSS_START;
+ } else {
+ memcpy(*rcmd, cmd, cmdlen);
+ }
+ return 1;
+
+ case REZERO_UNIT:
+ case REQUEST_SENSE:
+ case INQUIRY:
+ case START_STOP_UNIT:
+ case SEND_DIAGNOSTIC:
+ case PREVENT_ALLOW:
+ case READ_CAPACITY:
+ case READ_10:
+ case WRITE_10:
+ case POSITION_TO_ELEMENT: /* SEEK_10 */
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case READ_12:
+ case WRITE_12:
+ memcpy(*rcmd, cmd, cmdlen);
+ return 1;
+
+ /* Other UFI commands: FORMAT_UNIT, READ_FORMAT_CAPACITY,
+ * VERIFY, WRITE_AND_VERIFY.
+ * These should be checked whether they somehow can be made to fit.
+ */
+
+ default:
+ printf("%s: Unsupported UFI command 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), cmd[0]);
+ return 0; /* failure */
+ }
+}
+
+/*
+ * 8070i (ATAPI) specific functions
+ */
+Static int
+umass_atapi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen)
+{
+ /* An ATAPI command is always 12 bytes in length. */
+ KASSERT(*rcmdlen >= ATAPI_COMMAND_LENGTH,
+ ("rcmdlen = %d < %d, buffer too small",
+ *rcmdlen, ATAPI_COMMAND_LENGTH));
+
+ *rcmdlen = ATAPI_COMMAND_LENGTH;
+ memset(*rcmd, 0, ATAPI_COMMAND_LENGTH);
+
+ switch (cmd[0]) {
+ /* Commands of which the format has been verified. They should work.
+ * Copy the command into the (zeroed out) destination buffer.
+ */
+ case INQUIRY:
+ memcpy(*rcmd, cmd, cmdlen);
+ /* some drives wedge when asked for full inquiry information. */
+ if (sc->quirks & FORCE_SHORT_INQUIRY)
+ (*rcmd)[4] = SHORT_INQUIRY_LENGTH;
+ return 1;
+
+ case TEST_UNIT_READY:
+ if (sc->quirks & NO_TEST_UNIT_READY) {
+ KASSERT(*rcmdlen >= sizeof(struct scsi_start_stop_unit),
+ ("rcmdlen = %d < %ld, buffer too small",
+ *rcmdlen,
+ (long)sizeof(struct scsi_start_stop_unit)));
+ DPRINTF(UDMASS_SCSI, ("%s: Converted TEST_UNIT_READY "
+ "to START_UNIT\n", USBDEVNAME(sc->sc_dev)));
+ memset(*rcmd, 0, *rcmdlen);
+ (*rcmd)[0] = START_STOP_UNIT;
+ (*rcmd)[4] = SSS_START;
+ return 1;
+ }
+ /* fallthrough */
+ case REZERO_UNIT:
+ case REQUEST_SENSE:
+ case START_STOP_UNIT:
+ case SEND_DIAGNOSTIC:
+ case PREVENT_ALLOW:
+ case READ_CAPACITY:
+ case READ_10:
+ case WRITE_10:
+ case POSITION_TO_ELEMENT: /* SEEK_10 */
+ case SYNCHRONIZE_CACHE:
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case READ_BUFFER:
+ case 0x42: /* READ_SUBCHANNEL */
+ case 0x43: /* READ_TOC */
+ case 0x44: /* READ_HEADER */
+ case 0x51: /* READ_DISK_INFO */
+ case 0x52: /* READ_TRACK_INFO */
+ case 0x54: /* SEND_OPC */
+ case 0x59: /* READ_MASTER_CUE */
+ case 0x5b: /* CLOSE_TR_SESSION */
+ case 0x5c: /* READ_BUFFER_CAP */
+ case 0x5d: /* SEND_CUE_SHEET */
+ case 0xa1: /* BLANK */
+ case 0xa6: /* EXCHANGE_MEDIUM */
+ case 0xad: /* READ_DVD_STRUCTURE */
+ case 0xbb: /* SET_CD_SPEED */
+ case 0xe5: /* READ_TRACK_INFO_PHILIPS */
+ memcpy(*rcmd, cmd, cmdlen);
+ return 1;
+
+ case READ_12:
+ case WRITE_12:
+ default:
+ printf("%s: Unsupported ATAPI command 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), cmd[0]);
+ return 0; /* failure */
+ }
+}
+
+
+/* (even the comment is missing) */
+
+DRIVER_MODULE(umass, uhub, umass_driver, umass_devclass, umass_driver_load, 0);
+
+
+
+#ifdef USB_DEBUG
+Static void
+umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw)
+{
+ int clen = cbw->bCDBLength;
+ int dlen = UGETDW(cbw->dCBWDataTransferLength);
+ u_int8_t *c = cbw->CBWCDB;
+ int tag = UGETDW(cbw->dCBWTag);
+ int flags = cbw->bCBWFlags;
+
+ DPRINTF(UDMASS_BBB, ("%s: CBW %d: cmd = %db "
+ "(0x%02x%02x%02x%02x%02x%02x%s), "
+ "data = %db, dir = %s\n",
+ USBDEVNAME(sc->sc_dev), tag, clen,
+ c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6? "...":""),
+ dlen, (flags == CBWFLAGS_IN? "in":
+ (flags == CBWFLAGS_OUT? "out":"<invalid>"))));
+}
+
+Static void
+umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw)
+{
+ int sig = UGETDW(csw->dCSWSignature);
+ int tag = UGETW(csw->dCSWTag);
+ int res = UGETDW(csw->dCSWDataResidue);
+ int status = csw->bCSWStatus;
+
+ DPRINTF(UDMASS_BBB, ("%s: CSW %d: sig = 0x%08x (%s), tag = %d, "
+ "res = %d, status = 0x%02x (%s)\n", USBDEVNAME(sc->sc_dev),
+ tag, sig, (sig == CSWSIGNATURE? "valid":"invalid"),
+ tag, res,
+ status, (status == CSWSTATUS_GOOD? "good":
+ (status == CSWSTATUS_FAILED? "failed":
+ (status == CSWSTATUS_PHASE? "phase":"<invalid>")))));
+}
+
+Static void
+umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, int cmdlen)
+{
+ u_int8_t *c = cmd;
+ int dir = sc->transfer_dir;
+
+ DPRINTF(UDMASS_BBB, ("%s: cmd = %db "
+ "(0x%02x%02x%02x%02x%02x%02x%s), "
+ "data = %db, dir = %s\n",
+ USBDEVNAME(sc->sc_dev), cmdlen,
+ c[0], c[1], c[2], c[3], c[4], c[5], (cmdlen > 6? "...":""),
+ sc->transfer_datalen,
+ (dir == DIR_IN? "in":
+ (dir == DIR_OUT? "out":
+ (dir == DIR_NONE? "no data phase": "<invalid>")))));
+}
+
+Static void
+umass_dump_buffer(struct umass_softc *sc, u_int8_t *buffer, int buflen,
+ int printlen)
+{
+ int i, j;
+ char s1[40];
+ char s2[40];
+ char s3[5];
+
+ s1[0] = '\0';
+ s3[0] = '\0';
+
+ sprintf(s2, " buffer=%p, buflen=%d", buffer, buflen);
+ for (i = 0; i < buflen && i < printlen; i++) {
+ j = i % 16;
+ if (j == 0 && i != 0) {
+ DPRINTF(UDMASS_GEN, ("%s: 0x %s%s\n",
+ USBDEVNAME(sc->sc_dev), s1, s2));
+ s2[0] = '\0';
+ }
+ sprintf(&s1[j*2], "%02x", buffer[i] & 0xff);
+ }
+ if (buflen > printlen)
+ sprintf(s3, " ...");
+ DPRINTF(UDMASS_GEN, ("%s: 0x %s%s%s\n",
+ USBDEVNAME(sc->sc_dev), s1, s2, s3));
+}
+#endif
diff --git a/sys/dev/usb/umct.c b/sys/dev/usb/umct.c
new file mode 100644
index 0000000..8e001f2
--- /dev/null
+++ b/sys/dev/usb/umct.c
@@ -0,0 +1,516 @@
+/*-
+ * Copyright (c) 2003 Scott Long
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the MCT (Magic Control Technology) USB-RS232 Converter.
+ * Based on the superb documentation from the linux mct_u232 driver by
+ * Wolfgang Grandeggar <wolfgang@cec.ch>.
+ * This device smells a lot like the Belkin F5U103, except that it has
+ * suffered some mild brain-damage. This driver is based off of the ubsa.c
+ * driver from Alexander Kabaev <kan@freebsd.org>. Merging the two together
+ * might be useful, though the subtle differences might lead to lots of
+ * #ifdef's.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/tty.h>
+#include <sys/interrupt.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/ucomvar.h>
+
+/* The UMCT advertises the standard 8250 UART registers */
+#define UMCT_GET_MSR 2 /* Get Modem Status Register */
+#define UMCT_GET_MSR_SIZE 1
+#define UMCT_GET_LCR 6 /* Get Line Control Register */
+#define UMCT_GET_LCR_SIZE 1
+#define UMCT_SET_BAUD 5 /* Set the Baud Rate Divisor */
+#define UMCT_SET_BAUD_SIZE 4
+#define UMCT_SET_LCR 7 /* Set Line Control Register */
+#define UMCT_SET_LCR_SIZE 1
+#define UMCT_SET_MCR 10 /* Set Modem Control Register */
+#define UMCT_SET_MCR_SIZE 1
+
+#define UMCT_INTR_INTERVAL 100
+#define UMCT_IFACE_INDEX 0
+#define UMCT_CONFIG_INDEX 1
+
+struct umct_softc {
+ struct ucom_softc sc_ucom;
+ int sc_iface_number;
+ usbd_interface_handle sc_intr_iface;
+ int sc_intr_number;
+ usbd_pipe_handle sc_intr_pipe;
+ u_char *sc_intr_buf;
+ int sc_isize;
+ uint8_t sc_lsr;
+ uint8_t sc_msr;
+ uint8_t sc_lcr;
+ uint8_t sc_mcr;
+ void *sc_swicookie;
+};
+
+Static void umct_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void umct_get_status(void *, int, u_char *, u_char *);
+Static void umct_set(void *, int, int, int);
+Static int umct_param(void *, int, struct termios *);
+Static int umct_open(void *, int);
+Static void umct_close(void *, int);
+Static void umct_notify(void *);
+
+Static struct ucom_callback umct_callback = {
+ umct_get_status, /* ucom_get_status */
+ umct_set, /* ucom_set */
+ umct_param, /* ucom_param */
+ NULL, /* ucom_ioctl */
+ umct_open, /* ucom_open */
+ umct_close, /* ucom_close */
+ NULL, /* ucom_read */
+ NULL /* ucom_write */
+};
+
+Static const struct umct_product {
+ uint16_t vendor;
+ uint16_t product;
+} umct_products[] = {
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232 },
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232 },
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232 },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109 },
+ { 0, 0 }
+};
+
+Static device_probe_t umct_match;
+Static device_attach_t umct_attach;
+Static device_detach_t umct_detach;
+
+Static device_method_t umct_methods[] = {
+ DEVMETHOD(device_probe, umct_match),
+ DEVMETHOD(device_attach, umct_attach),
+ DEVMETHOD(device_detach, umct_detach),
+ { 0, 0 }
+};
+
+Static driver_t umct_driver = {
+ "ucom",
+ umct_methods,
+ sizeof(struct umct_softc)
+};
+
+DRIVER_MODULE(umct, uhub, umct_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(umct, usb, 1, 1, 1);
+MODULE_DEPEND(umct, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(umct, 1);
+
+Static struct ithd *umct_ithd;
+
+USB_MATCH(umct)
+{
+ USB_MATCH_START(umct, uaa);
+ int i;
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ for (i = 0; umct_products[i].vendor != 0; i++) {
+ if (umct_products[i].vendor == uaa->vendor &&
+ umct_products[i].product == uaa->product) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ }
+
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(umct)
+{
+ USB_ATTACH_START(umct, sc, uaa);
+ usbd_device_handle dev;
+ struct ucom_softc *ucom;
+ usb_config_descriptor_t *cdesc;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ char *devinfo;
+ const char *devname;
+ usbd_status err;
+ int i;
+
+ dev = uaa->device;
+ devinfo = malloc(1024, M_USBDEV, M_NOWAIT | M_ZERO);
+ if (devinfo == NULL)
+ return (ENOMEM);
+ bzero(sc, sizeof(struct umct_softc));
+ ucom = &sc->sc_ucom;
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ usbd_devinfo(dev, 0, devinfo);
+ device_set_desc_copy(self, devinfo);
+ devname = USBDEVNAME(ucom->sc_dev);
+ printf("%s: %s\n", devname, devinfo);
+
+ ucom->sc_bulkout_no = -1;
+ ucom->sc_bulkin_no = -1;
+ sc->sc_intr_number = -1;
+ sc->sc_intr_pipe = NULL;
+
+ err = usbd_set_config_index(dev, UMCT_CONFIG_INDEX, 1);
+ if (err) {
+ printf("%s: failed to set configuration: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ cdesc = usbd_get_config_descriptor(ucom->sc_udev);
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n", devname);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ err = usbd_device2interface_handle(dev, UMCT_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ printf("%s: failed to get interface: %s\n", devname,
+ usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n",
+ devname, i);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /*
+ * The real bulk-in endpoint is also marked as an interrupt.
+ * The only way to differentiate it from the real interrupt
+ * endpoint is to look at the wMaxPacketSize field.
+ */
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
+ if (UGETW(ed->wMaxPacketSize) == 0x2) {
+ sc->sc_intr_number = ed->bEndpointAddress;
+ sc->sc_isize = UGETW(ed->wMaxPacketSize);
+ } else {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ ucom->sc_ibufsize = UGETW(ed->wMaxPacketSize);
+ }
+ continue;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ if (uaa->product == USB_PRODUCT_MCT_SITECOM_USB232)
+ ucom->sc_obufsize = 16; /* device is broken */
+ else
+ ucom->sc_obufsize = UGETW(ed->wMaxPacketSize);
+ continue;
+ }
+
+ printf("%s: warning - unsupported endpoint 0x%x\n", devname,
+ ed->bEndpointAddress);
+ }
+
+ if (sc->sc_intr_number == -1) {
+ printf("%s: Could not fint interrupt in\n", devname);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ sc->sc_intr_iface = ucom->sc_iface;
+
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n", devname);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &umct_callback;
+ ucom_attach(ucom);
+ swi_add(&umct_ithd, "ucom", umct_notify, sc, SWI_TTY, 0,
+ &sc->sc_swicookie);
+
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_SUCCESS_RETURN;
+
+error:
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+USB_DETACH(umct)
+{
+ USB_DETACH_START(umct, sc);
+ int rv;
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ sc->sc_ucom.sc_dying = 1;
+ ithread_remove_handler(sc->sc_swicookie);
+ rv = ucom_detach(&sc->sc_ucom);
+ return (rv);
+}
+
+Static int
+umct_request(struct umct_softc *sc, uint8_t request, int len, uint32_t value)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ uint8_t oval[4];
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = request;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, len);
+ USETDW(oval, value);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, oval);
+ if (err)
+ printf("%s: ubsa_request: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+}
+
+Static void
+umct_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct umct_softc *sc;
+ u_char *buf;
+
+ sc = (struct umct_softc *)priv;
+ buf = sc->sc_intr_buf;
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+
+ sc->sc_msr = buf[0];
+ sc->sc_lsr = buf[1];
+
+ /*
+ * Defer notifying the ucom layer as it doesn't like to be bothered
+ * from an interrupt context.
+ */
+ swi_sched(sc->sc_swicookie, 0);
+}
+
+Static void
+umct_notify(void *arg)
+{
+ struct umct_softc *sc;
+
+ sc = (struct umct_softc *)arg;
+ if (sc->sc_ucom.sc_dying == 0)
+ ucom_status_change(&sc->sc_ucom);
+}
+
+Static void
+umct_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct umct_softc *sc;
+
+ sc = addr;
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+
+ return;
+}
+
+Static void
+umct_set(void *addr, int portno, int reg, int onoff)
+{
+ struct umct_softc *sc;
+
+ sc = addr;
+ switch (reg) {
+ case UCOM_SET_BREAK:
+ sc->sc_lcr &= ~0x40;
+ sc->sc_lcr |= (onoff) ? 0x40 : 0;
+ umct_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr);
+ break;
+ case UCOM_SET_DTR:
+ sc->sc_mcr &= ~0x01;
+ sc->sc_mcr |= (onoff) ? 0x01 : 0;
+ umct_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
+ break;
+ case UCOM_SET_RTS:
+ sc->sc_mcr &= ~0x2;
+ sc->sc_mcr |= (onoff) ? 0x02 : 0;
+ umct_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
+ break;
+ default:
+ break;
+ }
+}
+
+Static int
+umct_calc_baud(u_int baud)
+{
+ switch(baud) {
+ case B300: return (0x1);
+ case B600: return (0x2);
+ case B1200: return (0x3);
+ case B2400: return (0x4);
+ case B4800: return (0x6);
+ case B9600: return (0x8);
+ case B19200: return (0x9);
+ case B38400: return (0xa);
+ case B57600: return (0xb);
+ case 115200: return (0xc);
+ case B0:
+ default:
+ break;
+ }
+
+ return (0x0);
+}
+
+Static int
+umct_param(void *addr, int portno, struct termios *ti)
+{
+ struct umct_softc *sc;
+ uint32_t value;
+
+ sc = addr;
+ value = umct_calc_baud(ti->c_ospeed);
+ umct_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value);
+
+ value = sc->sc_lcr & 0x40;
+
+ switch (ti->c_cflag & CSIZE) {
+ case CS5: value |= 0x0; break;
+ case CS6: value |= 0x1; break;
+ case CS7: value |= 0x2; break;
+ case CS8: value |= 0x3; break;
+ default: value |= 0x0; break;
+ }
+
+ value |= (ti->c_cflag & CSTOPB) ? 0x4 : 0;
+ if (ti->c_cflag & PARENB) {
+ value |= 0x8;
+ value |= (ti->c_cflag & PARODD) ? 0x0 : 0x10;
+ }
+
+ /*
+ * XXX There doesn't seem to be a way to tell the device to use flow
+ * control.
+ */
+
+ sc->sc_lcr = value;
+ umct_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value);
+
+ return (0);
+}
+
+Static int
+umct_open(void *addr, int portno)
+{
+ struct umct_softc *sc;
+ int err;
+
+ sc = addr;
+ if (sc->sc_ucom.sc_dying) {
+ return (ENXIO);
+ }
+
+ if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
+ sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number,
+ USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buf,
+ sc->sc_isize, umct_intr, UMCT_INTR_INTERVAL);
+ if (err) {
+ printf("%s: cannot open interrupt pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ sc->sc_intr_number);
+ free(sc->sc_intr_buf, M_USBDEV);
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+Static void
+umct_close(void *addr, int portno)
+{
+ struct umct_softc *sc;
+ int err;
+
+ sc = addr;
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: abort interrupt pipe failed: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: close interrupt pipe failed: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
diff --git a/sys/dev/usb/umodem.c b/sys/dev/usb/umodem.c
new file mode 100644
index 0000000..a351da3
--- /dev/null
+++ b/sys/dev/usb/umodem.c
@@ -0,0 +1,822 @@
+/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*-
+ * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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.
+ */
+
+/*
+ * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
+ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+/*
+ * TODO:
+ * - Add error recovery in various places; the big problem is what
+ * to do in a callback if there is an error.
+ * - Implement a Call Device for modems without multiplexed commands.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/bus.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/ucomvar.h>
+
+#ifdef USB_DEBUG
+int umodemdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
+SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW,
+ &umodemdebug, 0, "umodem debug level");
+#define DPRINTFN(n, x) if (umodemdebug > (n)) logprintf x
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+static const struct umodem_product {
+ u_int16_t vendor;
+ u_int16_t product;
+ u_int8_t interface;
+} umodem_products[] = {
+ /* Kyocera AH-K3001V*/
+ { USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 0 },
+ { 0, 0, 0 },
+};
+
+/*
+ * These are the maximum number of bytes transferred per frame.
+ * If some really high speed devices should use this driver they
+ * may need to be increased, but this is good enough for normal modems.
+ */
+#define UMODEMIBUFSIZE 64
+#define UMODEMOBUFSIZE 256
+
+#define UMODEM_MODVER 1 /* module version */
+
+struct umodem_softc {
+ struct ucom_softc sc_ucom;
+
+ USBBASEDEVICE sc_dev; /* base device */
+
+ usbd_device_handle sc_udev; /* USB device */
+
+ int sc_ctl_iface_no;
+ usbd_interface_handle sc_ctl_iface; /* control interface */
+ int sc_data_iface_no;
+ usbd_interface_handle sc_data_iface; /* data interface */
+
+ int sc_cm_cap; /* CM capabilities */
+ int sc_acm_cap; /* ACM capabilities */
+
+ int sc_cm_over_data;
+
+ usb_cdc_line_state_t sc_line_state; /* current line state */
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+
+ u_char sc_opening; /* lock during open */
+
+ int sc_ctl_notify; /* Notification endpoint */
+ usbd_pipe_handle sc_notify_pipe; /* Notification pipe */
+ usb_cdc_notification_t sc_notify_buf; /* Notification structure */
+ u_char sc_lsr; /* Local status register */
+ u_char sc_msr; /* Modem status register */
+};
+
+Static void *umodem_get_desc(usbd_device_handle dev, int type, int subtype);
+Static usbd_status umodem_set_comm_feature(struct umodem_softc *sc,
+ int feature, int state);
+Static usbd_status umodem_set_line_coding(struct umodem_softc *sc,
+ usb_cdc_line_state_t *state);
+
+Static void umodem_get_caps(usbd_device_handle, int *, int *);
+
+Static void umodem_get_status(void *, int portno, u_char *lsr, u_char *msr);
+Static void umodem_set(void *, int, int, int);
+Static void umodem_dtr(struct umodem_softc *, int);
+Static void umodem_rts(struct umodem_softc *, int);
+Static void umodem_break(struct umodem_softc *, int);
+Static void umodem_set_line_state(struct umodem_softc *);
+Static int umodem_param(void *, int, struct termios *);
+Static int umodem_ioctl(void *, int, u_long, caddr_t, int, usb_proc_ptr );
+Static int umodem_open(void *, int portno);
+Static void umodem_close(void *, int portno);
+Static void umodem_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+
+Static struct ucom_callback umodem_callback = {
+ umodem_get_status,
+ umodem_set,
+ umodem_param,
+ umodem_ioctl,
+ umodem_open,
+ umodem_close,
+ NULL,
+ NULL,
+};
+
+Static device_probe_t umodem_match;
+Static device_attach_t umodem_attach;
+Static device_detach_t umodem_detach;
+
+Static device_method_t umodem_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, umodem_match),
+ DEVMETHOD(device_attach, umodem_attach),
+ DEVMETHOD(device_detach, umodem_detach),
+ { 0, 0 }
+};
+
+Static driver_t umodem_driver = {
+ "ucom",
+ umodem_methods,
+ sizeof (struct umodem_softc)
+};
+
+DRIVER_MODULE(umodem, uhub, umodem_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(umodem, usb, 1, 1, 1);
+MODULE_DEPEND(umodem, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(umodem, UMODEM_MODVER);
+
+USB_MATCH(umodem)
+{
+ USB_MATCH_START(umodem, uaa);
+ usb_interface_descriptor_t *id;
+ usb_device_descriptor_t *dd;
+ int cm, acm, i, ret;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ dd = usbd_get_device_descriptor(uaa->device);
+ if (id == NULL || dd == NULL)
+ return (UMATCH_NONE);
+
+ ret = UMATCH_NONE;
+ for (i = 0; umodem_products[i].vendor != 0; i++) {
+ if (umodem_products[i].vendor == UGETW(dd->idVendor) &&
+ umodem_products[i].product == UGETW(dd->idProduct) &&
+ umodem_products[i].interface == id->bInterfaceNumber) {
+ ret = UMATCH_VENDOR_PRODUCT;
+ break;
+ }
+ }
+
+ if (ret == UMATCH_NONE &&
+ id->bInterfaceClass == UICLASS_CDC &&
+ id->bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL &&
+ id->bInterfaceProtocol == UIPROTO_CDC_AT)
+ ret = UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO;
+
+ if (ret == UMATCH_NONE)
+ return (ret);
+
+ umodem_get_caps(uaa->device, &cm, &acm);
+ if (!(cm & USB_CDC_CM_DOES_CM) ||
+ !(cm & USB_CDC_CM_OVER_DATA) ||
+ !(acm & USB_CDC_ACM_HAS_LINE))
+ return (UMATCH_NONE);
+
+ return ret;
+}
+
+USB_ATTACH(umodem)
+{
+ USB_ATTACH_START(umodem, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usb_cdc_cm_descriptor_t *cmd;
+ char *devinfo = NULL;
+ const char *devname;
+ usbd_status err;
+ int data_ifcno;
+ int i;
+ struct ucom_softc *ucom;
+
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+ usbd_devinfo(dev, 0, devinfo);
+ ucom = &sc->sc_ucom;
+ ucom->sc_dev = self;
+ sc->sc_dev = self;
+ device_set_desc_copy(self, devinfo);
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+ /*USB_ATTACH_SETUP; */
+
+ sc->sc_udev = dev;
+ sc->sc_ctl_iface = uaa->iface;
+
+ devname = USBDEVNAME(sc->sc_dev);
+ /* XXX ? use something else ? XXX */
+ id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
+ printf("%s: %s, iclass %d/%d\n", devname, devinfo,
+ id->bInterfaceClass, id->bInterfaceSubClass);
+ sc->sc_ctl_iface_no = id->bInterfaceNumber;
+
+ umodem_get_caps(dev, &sc->sc_cm_cap, &sc->sc_acm_cap);
+
+ /* Get the data interface no. */
+ cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+ if (cmd == NULL) {
+ printf("%s: no CM descriptor\n", devname);
+ goto bad;
+ }
+ sc->sc_data_iface_no = data_ifcno = cmd->bDataInterface;
+
+ printf("%s: data interface %d, has %sCM over data, has %sbreak\n",
+ devname, data_ifcno,
+ sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
+ sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
+
+ /* Get the data interface too. */
+ for (i = 0; i < uaa->nifaces; i++) {
+ if (uaa->ifaces[i] != NULL) {
+ id = usbd_get_interface_descriptor(uaa->ifaces[i]);
+ if (id != NULL && id->bInterfaceNumber == data_ifcno) {
+ sc->sc_data_iface = uaa->ifaces[i];
+ uaa->ifaces[i] = NULL;
+ }
+ }
+ }
+ if (sc->sc_data_iface == NULL) {
+ printf("%s: no data interface\n", devname);
+ goto bad;
+ }
+ ucom->sc_iface = sc->sc_data_iface;
+
+ /*
+ * Find the bulk endpoints.
+ * Iterate over all endpoints in the data interface and take note.
+ */
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+
+ id = usbd_get_interface_descriptor(sc->sc_data_iface);
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n", devname,
+ i);
+ goto bad;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ }
+
+ if (ucom->sc_bulkin_no == -1) {
+ printf("%s: Could not find data bulk in\n", devname);
+ goto bad;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n", devname);
+ goto bad;
+ }
+
+ if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_ASSUME_CM_OVER_DATA) {
+ DPRINTF(("Quirk says to assume CM over data\n"));
+ sc->sc_cm_over_data = 1;
+ } else {
+ if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
+ if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE)
+ err = umodem_set_comm_feature(sc,
+ UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
+ else
+ err = 0;
+ if (err) {
+ printf("%s: could not set data multiplex mode\n",
+ devname);
+ goto bad;
+ }
+ sc->sc_cm_over_data = 1;
+ }
+ }
+
+ /*
+ * The standard allows for notification messages (to indicate things
+ * like a modem hangup) to come in via an interrupt endpoint
+ * off of the control interface. Iterate over the endpoints on
+ * the control interface and see if there are any interrupt
+ * endpoints; if there are, then register it.
+ */
+
+ sc->sc_ctl_notify = -1;
+ sc->sc_notify_pipe = NULL;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
+ if (ed == NULL)
+ continue;
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
+ printf("%s: status change notification available\n",
+ devname);
+ sc->sc_ctl_notify = ed->bEndpointAddress;
+ }
+ }
+
+ sc->sc_dtr = -1;
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UMODEMIBUFSIZE;
+ ucom->sc_obufsize = UMODEMOBUFSIZE;
+ ucom->sc_ibufsizepad = UMODEMIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &umodem_callback;
+
+ ucom_attach(&sc->sc_ucom);
+
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_SUCCESS_RETURN;
+
+ bad:
+ ucom->sc_dying = 1;
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+Static int
+umodem_open(void *addr, int portno)
+{
+ struct umodem_softc *sc = addr;
+ int err;
+
+ DPRINTF(("umodem_open: sc=%p\n", sc));
+
+ if (sc->sc_ctl_notify != -1 && sc->sc_notify_pipe == NULL) {
+ err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_ctl_notify,
+ USBD_SHORT_XFER_OK, &sc->sc_notify_pipe, sc,
+ &sc->sc_notify_buf, sizeof(sc->sc_notify_buf),
+ umodem_intr, USBD_DEFAULT_INTERVAL);
+
+ if (err) {
+ DPRINTF(("Failed to establish notify pipe: %s\n",
+ usbd_errstr(err)));
+ return EIO;
+ }
+ }
+
+ return 0;
+}
+
+Static void
+umodem_close(void *addr, int portno)
+{
+ struct umodem_softc *sc = addr;
+ int err;
+
+ DPRINTF(("umodem_close: sc=%p\n", sc));
+
+ if (sc->sc_notify_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_notify_pipe);
+ if (err)
+ printf("%s: abort notify pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_notify_pipe);
+ if (err)
+ printf("%s: close notify pipe failed: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err));
+ sc->sc_notify_pipe = NULL;
+ }
+}
+
+Static void
+umodem_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct umodem_softc *sc = priv;
+ u_char mstatus;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+ printf("%s: abnormal status: %s\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(status));
+ return;
+ }
+
+ if (sc->sc_notify_buf.bmRequestType != UCDC_NOTIFICATION) {
+ DPRINTF(("%s: unknown message type (%02x) on notify pipe\n",
+ USBDEVNAME(sc->sc_dev),
+ sc->sc_notify_buf.bmRequestType));
+ return;
+ }
+
+ switch (sc->sc_notify_buf.bNotification) {
+ case UCDC_N_SERIAL_STATE:
+ /*
+ * Set the serial state in ucom driver based on
+ * the bits from the notify message
+ */
+ if (UGETW(sc->sc_notify_buf.wLength) != 2) {
+ printf("%s: Invalid notification length! (%d)\n",
+ USBDEVNAME(sc->sc_dev),
+ UGETW(sc->sc_notify_buf.wLength));
+ break;
+ }
+ DPRINTF(("%s: notify bytes = %02x%02x\n",
+ USBDEVNAME(sc->sc_dev),
+ sc->sc_notify_buf.data[0],
+ sc->sc_notify_buf.data[1]));
+ /* Currently, lsr is always zero. */
+ sc->sc_lsr = sc->sc_msr = 0;
+ mstatus = sc->sc_notify_buf.data[0];
+
+ if (ISSET(mstatus, UCDC_N_SERIAL_RI))
+ sc->sc_msr |= UMSR_RI;
+ if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
+ sc->sc_msr |= UMSR_DSR;
+ if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
+ sc->sc_msr |= UMSR_DCD;
+ ucom_status_change(&sc->sc_ucom);
+ break;
+ default:
+ DPRINTF(("%s: unknown notify message: %02x\n",
+ USBDEVNAME(sc->sc_dev),
+ sc->sc_notify_buf.bNotification));
+ break;
+ }
+}
+
+void
+umodem_get_caps(usbd_device_handle dev, int *cm, int *acm)
+{
+ usb_cdc_cm_descriptor_t *cmd;
+ usb_cdc_acm_descriptor_t *cad;
+
+ *cm = *acm = 0;
+
+ cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+ if (cmd == NULL) {
+ DPRINTF(("umodem_get_desc: no CM desc\n"));
+ return;
+ }
+ *cm = cmd->bmCapabilities;
+
+ cad = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+ if (cad == NULL) {
+ DPRINTF(("umodem_get_desc: no ACM desc\n"));
+ return;
+ }
+ *acm = cad->bmCapabilities;
+}
+
+void
+umodem_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct umodem_softc *sc = addr;
+
+ DPRINTF(("umodem_get_status:\n"));
+
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
+
+int
+umodem_param(void *addr, int portno, struct termios *t)
+{
+ struct umodem_softc *sc = addr;
+ usbd_status err;
+ usb_cdc_line_state_t ls;
+
+ DPRINTF(("umodem_param: sc=%p\n", sc));
+
+ USETDW(ls.dwDTERate, t->c_ospeed);
+ if (ISSET(t->c_cflag, CSTOPB))
+ ls.bCharFormat = UCDC_STOP_BIT_2;
+ else
+ ls.bCharFormat = UCDC_STOP_BIT_1;
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ ls.bParityType = UCDC_PARITY_ODD;
+ else
+ ls.bParityType = UCDC_PARITY_EVEN;
+ } else
+ ls.bParityType = UCDC_PARITY_NONE;
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ ls.bDataBits = 5;
+ break;
+ case CS6:
+ ls.bDataBits = 6;
+ break;
+ case CS7:
+ ls.bDataBits = 7;
+ break;
+ case CS8:
+ ls.bDataBits = 8;
+ break;
+ }
+
+ err = umodem_set_line_coding(sc, &ls);
+ if (err) {
+ DPRINTF(("umodem_param: err=%s\n", usbd_errstr(err)));
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+int
+umodem_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag,
+ usb_proc_ptr p)
+{
+ struct umodem_softc *sc = addr;
+ int error = 0;
+
+ if (sc->sc_ucom.sc_dying)
+ return (EIO);
+
+ DPRINTF(("umodemioctl: cmd=0x%08lx\n", cmd));
+
+ switch (cmd) {
+ case USB_GET_CM_OVER_DATA:
+ *(int *)data = sc->sc_cm_over_data;
+ break;
+
+ case USB_SET_CM_OVER_DATA:
+ if (*(int *)data != sc->sc_cm_over_data) {
+ /* XXX change it */
+ }
+ break;
+
+ default:
+ DPRINTF(("umodemioctl: unknown\n"));
+ error = ENOTTY;
+ break;
+ }
+
+ return (error);
+}
+
+void
+umodem_dtr(struct umodem_softc *sc, int onoff)
+{
+ DPRINTF(("umodem_modem: onoff=%d\n", onoff));
+
+ if (sc->sc_dtr == onoff)
+ return;
+ sc->sc_dtr = onoff;
+
+ umodem_set_line_state(sc);
+}
+
+void
+umodem_rts(struct umodem_softc *sc, int onoff)
+{
+ DPRINTF(("umodem_modem: onoff=%d\n", onoff));
+
+ if (sc->sc_rts == onoff)
+ return;
+ sc->sc_rts = onoff;
+
+ umodem_set_line_state(sc);
+}
+
+void
+umodem_set_line_state(struct umodem_softc *sc)
+{
+ usb_device_request_t req;
+ int ls;
+
+ ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
+ (sc->sc_rts ? UCDC_LINE_RTS : 0);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, ls);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, 0);
+
+ (void)usbd_do_request(sc->sc_udev, &req, 0);
+
+}
+
+void
+umodem_break(struct umodem_softc *sc, int onoff)
+{
+ usb_device_request_t req;
+
+ DPRINTF(("umodem_break: onoff=%d\n", onoff));
+
+ if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK))
+ return;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, 0);
+
+ (void)usbd_do_request(sc->sc_udev, &req, 0);
+}
+
+void
+umodem_set(void *addr, int portno, int reg, int onoff)
+{
+ struct umodem_softc *sc = addr;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ umodem_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ umodem_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ umodem_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+}
+
+usbd_status
+umodem_set_line_coding(struct umodem_softc *sc, usb_cdc_line_state_t *state)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("umodem_set_line_coding: rate=%d fmt=%d parity=%d bits=%d\n",
+ UGETDW(state->dwDTERate), state->bCharFormat,
+ state->bParityType, state->bDataBits));
+
+ if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
+ DPRINTF(("umodem_set_line_coding: already set\n"));
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_LINE_CODING;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+ err = usbd_do_request(sc->sc_udev, &req, state);
+ if (err) {
+ DPRINTF(("umodem_set_line_coding: failed, err=%s\n",
+ usbd_errstr(err)));
+ return (err);
+ }
+
+ sc->sc_line_state = *state;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void *
+umodem_get_desc(usbd_device_handle dev, int type, int subtype)
+{
+ usb_descriptor_t *desc;
+ usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
+ uByte *p = (uByte *)cd;
+ uByte *end = p + UGETW(cd->wTotalLength);
+
+ while (p < end) {
+ desc = (usb_descriptor_t *)p;
+ if (desc->bDescriptorType == type &&
+ desc->bDescriptorSubtype == subtype)
+ return (desc);
+ p += desc->bLength;
+ }
+
+ return (0);
+}
+
+usbd_status
+umodem_set_comm_feature(struct umodem_softc *sc, int feature, int state)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ usb_cdc_abstract_state_t ast;
+
+ DPRINTF(("umodem_set_comm_feature: feature=%d state=%d\n", feature,
+ state));
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_COMM_FEATURE;
+ USETW(req.wValue, feature);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
+ USETW(ast.wState, state);
+
+ err = usbd_do_request(sc->sc_udev, &req, &ast);
+ if (err) {
+ DPRINTF(("umodem_set_comm_feature: feature=%d, err=%s\n",
+ feature, usbd_errstr(err)));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+USB_DETACH(umodem)
+{
+ USB_DETACH_START(umodem, sc);
+ int rv = 0;
+
+ DPRINTF(("umodem_detach: sc=%p\n", sc));
+
+ if (sc->sc_notify_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_notify_pipe);
+ usbd_close_pipe(sc->sc_notify_pipe);
+ sc->sc_notify_pipe = NULL;
+ }
+
+ sc->sc_ucom.sc_dying = 1;
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return (rv);
+}
diff --git a/sys/dev/usb/ums.c b/sys/dev/usb/ums.c
new file mode 100644
index 0000000..708f301
--- /dev/null
+++ b/sys/dev/usb/ums.c
@@ -0,0 +1,828 @@
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/sysctl.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/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+#include <dev/usb/hid.h>
+
+#if __FreeBSD_version >= 500000
+#include <sys/mouse.h>
+#else
+#include <machine/mouse.h>
+#endif
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (umsdebug) logprintf x
+#define DPRINTFN(n,x) if (umsdebug>(n)) logprintf x
+int umsdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
+SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
+ &umsdebug, 0, "ums debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UMSUNIT(s) (minor(s)&0x1f)
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+#define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */
+
+struct ums_softc {
+ device_t 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;
+
+ usb_callout_t callout_handle; /* for spurious button ups */
+
+ int sc_enabled;
+ int sc_disconnected; /* device is gone */
+
+ int flags; /* device configuration */
+#define UMS_Z 0x01 /* z direction available */
+#define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */
+ int nbuttons;
+#define MAX_BUTTONS 7 /* chosen because sc_buttons is u_char */
+
+ u_char qbuf[QUEUE_BUFSIZE]; /* must be divisable by 3&4 */
+ u_char dummy[100]; /* XXX 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 */
+
+ struct cdev *dev; /* specfs */
+};
+
+#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
+#define MOUSE_FLAGS (HIO_RELATIVE)
+
+Static void ums_intr(usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status status);
+
+Static void ums_add_to_queue(struct ums_softc *sc,
+ int dx, int dy, int dz, int buttons);
+Static void ums_add_to_queue_timeout(void *priv);
+
+Static int ums_enable(void *);
+Static void ums_disable(void *);
+
+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;
+
+
+Static struct cdevsw ums_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ums_open,
+ .d_close = ums_close,
+ .d_read = ums_read,
+ .d_ioctl = ums_ioctl,
+ .d_poll = ums_poll,
+ .d_name = "ums",
+#if __FreeBSD_version < 500014
+ .d_bmaj -1
+#endif
+};
+
+USB_DECLARE_DRIVER(ums);
+
+USB_MATCH(ums)
+{
+ USB_MATCH_START(ums, uaa);
+ usb_interface_descriptor_t *id;
+ int size, ret;
+ void *desc;
+ usbd_status err;
+
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (!id || id->bInterfaceClass != UICLASS_HID)
+ return (UMATCH_NONE);
+
+ err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
+ if (err)
+ 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);
+}
+
+USB_ATTACH(ums)
+{
+ USB_ATTACH_START(ums, sc, uaa);
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int size;
+ void *desc;
+ usbd_status err;
+ char devinfo[1024];
+ u_int32_t flags;
+ int i;
+ struct hid_location loc_btn;
+
+ sc->sc_disconnected = 1;
+ sc->sc_iface = iface;
+ id = usbd_get_interface_descriptor(iface);
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
+ devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (!ed) {
+ printf("%s: could not read endpoint descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_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,
+ UE_GET_ADDR(ed->bEndpointAddress),
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
+ UE_GET_XFERTYPE(ed->bmAttributes),
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
+ UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
+ printf("%s: unexpected endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
+ if (err)
+ USB_ATTACH_ERROR_RETURN;
+
+ if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
+ hid_input, &sc->sc_loc_x, &flags)) {
+ printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
+ printf("%s: X report 0x%04x not supported\n",
+ USBDEVNAME(sc->sc_dev), flags);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
+ hid_input, &sc->sc_loc_y, &flags)) {
+ printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
+ printf("%s: Y report 0x%04x not supported\n",
+ USBDEVNAME(sc->sc_dev), flags);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* 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 */
+ } else {
+ sc->flags |= UMS_Z;
+ }
+ }
+
+ /* figure out the number of buttons */
+ for (i = 1; i <= MAX_BUTTONS; 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) {
+ printf("%s: no memory\n", USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ printf("%s: %d buttons%s\n", USBDEVNAME(sc->sc_dev),
+ 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) {
+ printf("%s: no memory\n", USBDEVNAME(sc->sc_dev));
+ free(sc->sc_loc_btn, M_USB);
+ 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_y.pos, sc->sc_loc_y.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 (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
+ sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->hw.iftype = MOUSE_IF_USB;
+ sc->hw.type = MOUSE_MOUSE;
+ sc->hw.model = MOUSE_MODEL_GENERIC;
+ sc->hw.hwid = 0;
+ sc->mode.protocol = MOUSE_PROTO_MSC;
+ sc->mode.rate = -1;
+ sc->mode.resolution = MOUSE_RES_UNKNOWN;
+ sc->mode.accelfactor = 0;
+ sc->mode.level = 0;
+ sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
+
+ sc->status.flags = 0;
+ sc->status.button = sc->status.obutton = 0;
+ sc->status.dx = sc->status.dy = sc->status.dz = 0;
+
+#ifndef __FreeBSD__
+ sc->rsel.si_flags = 0;
+ sc->rsel.si_pid = 0;
+#endif
+
+ sc->dev = make_dev(&ums_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR,
+ 0644, "ums%d", device_get_unit(self));
+
+ if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) {
+ DPRINTF(("%s: Spurious button up events\n",
+ USBDEVNAME(sc->sc_dev)));
+ sc->flags |= UMS_SPUR_BUT_UP;
+ }
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+
+Static int
+ums_detach(device_t self)
+{
+ struct ums_softc *sc = device_get_softc(self);
+
+ if (sc->sc_enabled)
+ ums_disable(sc);
+
+ DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
+
+ free(sc->sc_loc_btn, M_USB);
+ free(sc->sc_ibuf, M_USB);
+
+ /* someone waiting for data */
+ /*
+ * XXX If we wakeup the process here, the device will be gone by
+ * the time the process gets a chance to notice. *_close and friends
+ * should be fixed to handle this case.
+ * Or we should do a delayed detach for this.
+ * Does this delay now force tsleep to exit with an error?
+ */
+ if (sc->state & UMS_ASLEEP) {
+ sc->state &= ~UMS_ASLEEP;
+ wakeup(sc);
+ }
+ if (sc->state & UMS_SELECT) {
+ sc->state &= ~UMS_SELECT;
+ selwakeuppri(&sc->rsel, PZERO);
+ }
+
+ destroy_dev(sc->dev);
+
+ return 0;
+}
+
+void
+ums_intr(xfer, addr, status)
+ usbd_xfer_handle xfer;
+ 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;
+
+#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (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));
+ if (status == USBD_STALLED)
+ 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);
+ for (i = 0; i < sc->nbuttons; i++)
+ if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
+ buttons |= (1 << UMS_BUT(i));
+
+ if (dx || dy || dz || (sc->flags & UMS_Z)
+ || buttons != sc->status.button) {
+ DPRINTFN(5, ("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;
+ }
+
+ /*
+ * The Qtronix keyboard has a built in PS/2 port for a mouse.
+ * The firmware once in a while posts a spurious button up
+ * event. This event we ignore by doing a timeout for 50 msecs.
+ * If we receive dx=dy=dz=buttons=0 before we add the event to
+ * the queue.
+ * In any other case we delete the timeout event.
+ */
+ if (sc->flags & UMS_SPUR_BUT_UP &&
+ dx == 0 && dy == 0 && dz == 0 && buttons == 0) {
+ usb_callout(sc->callout_handle, MS_TO_TICKS(50 /*msecs*/),
+ ums_add_to_queue_timeout, (void *) sc);
+ } else {
+ usb_uncallout(sc->callout_handle,
+ ums_add_to_queue_timeout, (void *) sc);
+ ums_add_to_queue(sc, dx, dy, dz, buttons);
+ }
+ }
+}
+
+Static void
+ums_add_to_queue_timeout(void *priv)
+{
+ struct ums_softc *sc = priv;
+ int s;
+
+ s = splusb();
+ ums_add_to_queue(sc, 0, 0, 0, 0);
+ splx(s);
+}
+
+Static void
+ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons)
+{
+ /* Discard data in case of full buffer */
+ if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) {
+ DPRINTF(("Buffer full, discarded packet"));
+ return;
+ }
+
+ if (dx > 254) dx = 254;
+ if (dx < -256) dx = -256;
+ if (dy > 254) dy = 254;
+ if (dy < -256) dy = -256;
+ if (dz > 126) dz = 126;
+ if (dz < -128) dz = -128;
+
+ sc->qbuf[sc->qhead] = sc->mode.syncmask[1];
+ sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS;
+ sc->qbuf[sc->qhead+1] = dx >> 1;
+ sc->qbuf[sc->qhead+2] = dy >> 1;
+ sc->qbuf[sc->qhead+3] = dx - (dx >> 1);
+ sc->qbuf[sc->qhead+4] = dy - (dy >> 1);
+
+ if (sc->mode.level == 1) {
+ sc->qbuf[sc->qhead+5] = dz >> 1;
+ sc->qbuf[sc->qhead+6] = dz - (dz >> 1);
+ sc->qbuf[sc->qhead+7] = ((~buttons >> 3)
+ & MOUSE_SYS_EXTBUTTONS);
+ }
+
+ sc->qhead += sc->mode.packetsize;
+ sc->qcount += sc->mode.packetsize;
+ /* wrap round at end of buffer */
+ if (sc->qhead >= sizeof(sc->qbuf))
+ sc->qhead = 0;
+
+ /* someone waiting for data */
+ if (sc->state & UMS_ASLEEP) {
+ sc->state &= ~UMS_ASLEEP;
+ wakeup(sc);
+ }
+ if (sc->state & UMS_SELECT) {
+ sc->state &= ~UMS_SELECT;
+ selwakeuppri(&sc->rsel, PZERO);
+ }
+}
+Static int
+ums_enable(v)
+ void *v;
+{
+ struct ums_softc *sc = v;
+
+ usbd_status err;
+
+ if (sc->sc_enabled)
+ return EBUSY;
+
+ sc->sc_enabled = 1;
+ sc->qcount = 0;
+ sc->qhead = sc->qtail = 0;
+ sc->status.flags = 0;
+ sc->status.button = sc->status.obutton = 0;
+ sc->status.dx = sc->status.dy = sc->status.dz = 0;
+
+ callout_handle_init((struct callout_handle *)&sc->callout_handle);
+
+ /* Set up interrupt pipe. */
+ err = 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,
+ USBD_DEFAULT_INTERVAL);
+ if (err) {
+ DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
+ err));
+ sc->sc_enabled = 0;
+ return (EIO);
+ }
+ return (0);
+}
+
+Static void
+ums_disable(priv)
+ void *priv;
+{
+ struct ums_softc *sc = priv;
+
+ usb_uncallout(sc->callout_handle, ums_add_to_queue_timeout, sc);
+
+ /* Disable interrupts. */
+ usbd_abort_pipe(sc->sc_intrpipe);
+ usbd_close_pipe(sc->sc_intrpipe);
+
+ sc->sc_enabled = 0;
+
+ if (sc->qcount != 0)
+ DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
+}
+
+Static int
+ums_open(struct cdev *dev, int flag, int fmt, usb_proc_ptr p)
+{
+ struct ums_softc *sc;
+
+ USB_GET_SC_OPEN(ums, UMSUNIT(dev), sc);
+
+ return ums_enable(sc);
+}
+
+Static int
+ums_close(struct cdev *dev, int flag, int fmt, usb_proc_ptr p)
+{
+ struct ums_softc *sc;
+
+ USB_GET_SC(ums, UMSUNIT(dev), sc);
+
+ if (!sc)
+ return 0;
+
+ if (sc->sc_enabled)
+ ums_disable(sc);
+
+ return 0;
+}
+
+Static int
+ums_read(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct ums_softc *sc;
+ int s;
+ char buf[sizeof(sc->qbuf)];
+ int l = 0;
+ int error;
+
+ USB_GET_SC(ums, UMSUNIT(dev), sc);
+
+ s = splusb();
+ if (!sc) {
+ splx(s);
+ return EIO;
+ }
+
+ while (sc->qcount == 0 ) {
+ if (flag & IO_NDELAY) { /* non-blocking I/O */
+ splx(s);
+ return EWOULDBLOCK;
+ }
+
+ sc->state |= UMS_ASLEEP; /* blocking I/O */
+ error = tsleep(sc, PZERO | PCATCH, "umsrea", 0);
+ if (error) {
+ splx(s);
+ return error;
+ } else if (!sc->sc_enabled) {
+ splx(s);
+ return EINTR;
+ }
+ /* check whether the device is still there */
+
+ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ if (!sc) {
+ splx(s);
+ return EIO;
+ }
+ }
+
+ /*
+ * XXX we could optimise the use of splx/splusb somewhat. The writer
+ * process only extends qcount and qtail. We could copy them and use the copies
+ * to do the copying out of the queue.
+ */
+
+ 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(struct cdev *dev, int events, usb_proc_ptr p)
+{
+ struct ums_softc *sc;
+ int revents = 0;
+ int s;
+
+ USB_GET_SC(ums, UMSUNIT(dev), sc);
+
+ if (!sc)
+ return 0;
+
+ 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(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
+{
+ struct ums_softc *sc;
+ int error = 0;
+ int s;
+ mousemode_t mode;
+
+ USB_GET_SC(ums, UMSUNIT(dev), sc);
+
+ if (!sc)
+ return EIO;
+
+ switch(cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->hw;
+ break;
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->mode;
+ break;
+ case MOUSE_SETMODE:
+ mode = *(mousemode_t *)addr;
+
+ if (mode.level == -1)
+ /* don't change the current setting */
+ ;
+ else if ((mode.level < 0) || (mode.level > 1))
+ return (EINVAL);
+
+ s = splusb();
+ sc->mode.level = mode.level;
+
+ if (sc->mode.level == 0) {
+ if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
+ sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->mode.protocol = MOUSE_PROTO_MSC;
+ sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->mode.level == 1) {
+ if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
+ sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+
+ bzero(sc->qbuf, sizeof(sc->qbuf));
+ sc->qhead = sc->qtail = sc->qcount = 0;
+ splx(s);
+
+ break;
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->mode.level;
+ break;
+ case MOUSE_SETLEVEL:
+ if (*(int *)addr < 0 || *(int *)addr > 1)
+ return (EINVAL);
+
+ s = splusb();
+ sc->mode.level = *(int *)addr;
+
+ if (sc->mode.level == 0) {
+ if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
+ sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->mode.protocol = MOUSE_PROTO_MSC;
+ sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->mode.level == 1) {
+ if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
+ sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+
+ bzero(sc->qbuf, sizeof(sc->qbuf));
+ sc->qhead = sc->qtail = sc->qcount = 0;
+ splx(s);
+
+ 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;
+}
+
+DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0);
diff --git a/sys/dev/usb/uplcom.c b/sys/dev/usb/uplcom.c
new file mode 100644
index 0000000..6ac54d1
--- /dev/null
+++ b/sys/dev/usb/uplcom.c
@@ -0,0 +1,847 @@
+/* $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Ichiro FUKUHARA (ichiro@ichiro.org).
+ *
+ * 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.
+ */
+
+/*
+ * Simple datasheet
+ * http://www.prolific.com.tw/download/DataSheet/pl2303_ds11.PDF
+ * http://www.nisseisg.co.jp/jyouhou/_cp/@gif/2303.pdf
+ * (english)
+ *
+ */
+
+#include "opt_uplcom.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
+#ifdef USB_DEBUG
+static int uplcomdebug = 0;
+SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RW,
+ &uplcomdebug, 0, "uplcom debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (uplcomdebug > (n)) \
+ logprintf x; \
+ } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UPLCOM_MODVER 1 /* module version */
+
+#define UPLCOM_CONFIG_INDEX 0
+#define UPLCOM_IFACE_INDEX 0
+#define UPLCOM_SECOND_IFACE_INDEX 1
+
+#ifndef UPLCOM_INTR_INTERVAL
+#define UPLCOM_INTR_INTERVAL 100 /* ms */
+#endif
+
+#define UPLCOM_SET_REQUEST 0x01
+#define UPLCOM_SET_CRTSCTS 0x41
+#define RSAQ_STATUS_DSR 0x02
+#define RSAQ_STATUS_DCD 0x01
+
+struct uplcom_softc {
+ struct ucom_softc sc_ucom;
+
+ int sc_iface_number; /* interface number */
+
+ usbd_interface_handle sc_intr_iface; /* interrupt interface */
+ int sc_intr_number; /* interrupt number */
+ usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
+ u_char *sc_intr_buf; /* interrupt buffer */
+ int sc_isize;
+
+ usb_cdc_line_state_t sc_line_state; /* current line state */
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+ u_char sc_status;
+
+ u_char sc_lsr; /* Local status register */
+ u_char sc_msr; /* uplcom status register */
+};
+
+/*
+ * These are the maximum number of bytes transferred per frame.
+ * The output buffer size cannot be increased due to the size encoding.
+ */
+#define UPLCOMIBUFSIZE 256
+#define UPLCOMOBUFSIZE 256
+
+Static usbd_status uplcom_reset(struct uplcom_softc *);
+Static usbd_status uplcom_set_line_coding(struct uplcom_softc *,
+ usb_cdc_line_state_t *);
+Static usbd_status uplcom_set_crtscts(struct uplcom_softc *);
+Static void uplcom_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+
+Static void uplcom_set(void *, int, int, int);
+Static void uplcom_dtr(struct uplcom_softc *, int);
+Static void uplcom_rts(struct uplcom_softc *, int);
+Static void uplcom_break(struct uplcom_softc *, int);
+Static void uplcom_set_line_state(struct uplcom_softc *);
+Static void uplcom_get_status(void *, int, u_char *, u_char *);
+#if TODO
+Static int uplcom_ioctl(void *, int, u_long, caddr_t, int, usb_proc_ptr);
+#endif
+Static int uplcom_param(void *, int, struct termios *);
+Static int uplcom_open(void *, int);
+Static void uplcom_close(void *, int);
+
+struct ucom_callback uplcom_callback = {
+ uplcom_get_status,
+ uplcom_set,
+ uplcom_param,
+ NULL, /* uplcom_ioctl, TODO */
+ uplcom_open,
+ uplcom_close,
+ NULL,
+ NULL
+};
+
+static const struct uplcom_product {
+ uint16_t vendor;
+ uint16_t product;
+} uplcom_products [] = {
+ /* I/O DATA USB-RSAQ */
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ },
+ /* I/O DATA USB-RSAQ2 */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2 },
+ /* PLANEX USB-RS232 URS-03 */
+ { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A },
+ /* IOGEAR/ATEN UC-232A */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303 },
+ /* TDK USB-PHS Adapter UHA6400 */
+ { USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400 },
+ /* RATOC REX-USB60 */
+ { USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60 },
+ /* ELECOM UC-SGT */
+ { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT },
+ /* SOURCENEXT KeikaiDenwa 8 */
+ { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8 },
+ /* SOURCENEXT KeikaiDenwa 8 with charger */
+ { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG },
+ /* HAL Corporation Crossam2+USB */
+ { USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001 },
+ { 0, 0 }
+};
+
+Static device_probe_t uplcom_match;
+Static device_attach_t uplcom_attach;
+Static device_detach_t uplcom_detach;
+
+Static device_method_t uplcom_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uplcom_match),
+ DEVMETHOD(device_attach, uplcom_attach),
+ DEVMETHOD(device_detach, uplcom_detach),
+ { 0, 0 }
+};
+
+Static driver_t uplcom_driver = {
+ "ucom",
+ uplcom_methods,
+ sizeof (struct uplcom_softc)
+};
+
+DRIVER_MODULE(uplcom, uhub, uplcom_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uplcom, usb, 1, 1, 1);
+MODULE_DEPEND(uplcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(uplcom, UPLCOM_MODVER);
+
+static int uplcominterval = UPLCOM_INTR_INTERVAL;
+
+static int
+sysctl_hw_usb_uplcom_interval(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = uplcominterval;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+ if (0 < val && val <= 1000)
+ uplcominterval = val;
+ else
+ err = EINVAL;
+
+ return (err);
+}
+
+SYSCTL_PROC(_hw_usb_uplcom, OID_AUTO, interval, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_usb_uplcom_interval,
+ "I", "uplcom interrpt pipe interval");
+
+USB_MATCH(uplcom)
+{
+ USB_MATCH_START(uplcom, uaa);
+ int i;
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ for (i = 0; uplcom_products[i].vendor != 0; i++) {
+ if (uplcom_products[i].vendor == uaa->vendor &&
+ uplcom_products[i].product == uaa->product) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ }
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(uplcom)
+{
+ USB_ATTACH_START(uplcom, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ struct ucom_softc *ucom;
+ usb_config_descriptor_t *cdesc;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ char *devinfo;
+ const char *devname;
+ usbd_status err;
+ int i;
+
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+ ucom = &sc->sc_ucom;
+
+ bzero(sc, sizeof (struct uplcom_softc));
+
+ usbd_devinfo(dev, 0, devinfo);
+ /* USB_ATTACH_SETUP; */
+ ucom->sc_dev = self;
+ device_set_desc_copy(self, devinfo);
+ /* USB_ATTACH_SETUP; */
+
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ devname = USBDEVNAME(ucom->sc_dev);
+ printf("%s: %s\n", devname, devinfo);
+
+ DPRINTF(("uplcom attach: sc = %p\n", sc));
+
+ /* initialize endpoints */
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ sc->sc_intr_number = -1;
+ sc->sc_intr_pipe = NULL;
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UPLCOM_CONFIG_INDEX, 1);
+ if (err) {
+ printf("%s: failed to set configuration: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* get the config descriptor */
+ cdesc = usbd_get_config_descriptor(ucom->sc_udev);
+
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n",
+ USBDEVNAME(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* get the (first/common) interface */
+ err = usbd_device2interface_handle(dev, UPLCOM_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ printf("%s: failed to get interface: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* Find the interrupt endpoints */
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n",
+ USBDEVNAME(ucom->sc_dev), i);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->sc_intr_number = ed->bEndpointAddress;
+ sc->sc_isize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ if (sc->sc_intr_number == -1) {
+ printf("%s: Could not find interrupt in\n",
+ USBDEVNAME(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* keep interface for interrupt */
+ sc->sc_intr_iface = ucom->sc_iface;
+
+ /*
+ * USB-RSAQ1 has two interface
+ *
+ * USB-RSAQ1 | USB-RSAQ2
+ * -----------------+-----------------
+ * Interface 0 |Interface 0
+ * Interrupt(0x81) | Interrupt(0x81)
+ * -----------------+ BulkIN(0x02)
+ * Interface 1 | BulkOUT(0x83)
+ * BulkIN(0x02) |
+ * BulkOUT(0x83) |
+ */
+ if (cdesc->bNumInterface == 2) {
+ err = usbd_device2interface_handle(dev,
+ UPLCOM_SECOND_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ printf("%s: failed to get second interface: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+ }
+
+ /* Find the bulk{in,out} endpoints */
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n",
+ USBDEVNAME(ucom->sc_dev), i);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ }
+
+ if (ucom->sc_bulkin_no == -1) {
+ printf("%s: Could not find data bulk in\n",
+ USBDEVNAME(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n",
+ USBDEVNAME(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ sc->sc_dtr = sc->sc_rts = -1;
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UPLCOMIBUFSIZE;
+ ucom->sc_obufsize = UPLCOMOBUFSIZE;
+ ucom->sc_ibufsizepad = UPLCOMIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &uplcom_callback;
+
+ err = uplcom_reset(sc);
+
+ if (err) {
+ printf("%s: reset failed: %s\n",
+ USBDEVNAME(ucom->sc_dev), usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ DPRINTF(("uplcom: in = 0x%x, out = 0x%x, intr = 0x%x\n",
+ ucom->sc_bulkin_no, ucom->sc_bulkout_no, sc->sc_intr_number));
+
+ ucom_attach(&sc->sc_ucom);
+
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_SUCCESS_RETURN;
+
+error:
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+USB_DETACH(uplcom)
+{
+ USB_DETACH_START(uplcom, sc);
+ int rv = 0;
+
+ DPRINTF(("uplcom_detach: sc = %p\n", sc));
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ sc->sc_ucom.sc_dying = 1;
+
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return (rv);
+}
+
+Static usbd_status
+uplcom_reset(struct uplcom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UPLCOM_SET_REQUEST;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err) {
+ printf("%s: uplcom_reset: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (EIO);
+ }
+
+ return (0);
+}
+
+Static void
+uplcom_set_line_state(struct uplcom_softc *sc)
+{
+ usb_device_request_t req;
+ int ls;
+ usbd_status err;
+
+ ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
+ (sc->sc_rts ? UCDC_LINE_RTS : 0);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, ls);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err)
+ printf("%s: uplcom_set_line_status: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+}
+
+Static void
+uplcom_set(void *addr, int portno, int reg, int onoff)
+{
+ struct uplcom_softc *sc = addr;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ uplcom_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ uplcom_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ uplcom_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+}
+
+Static void
+uplcom_dtr(struct uplcom_softc *sc, int onoff)
+{
+ DPRINTF(("uplcom_dtr: onoff = %d\n", onoff));
+
+ if (sc->sc_dtr == onoff)
+ return;
+ sc->sc_dtr = onoff;
+
+ uplcom_set_line_state(sc);
+}
+
+Static void
+uplcom_rts(struct uplcom_softc *sc, int onoff)
+{
+ DPRINTF(("uplcom_rts: onoff = %d\n", onoff));
+
+ if (sc->sc_rts == onoff)
+ return;
+ sc->sc_rts = onoff;
+
+ uplcom_set_line_state(sc);
+}
+
+Static void
+uplcom_break(struct uplcom_softc *sc, int onoff)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("uplcom_break: onoff = %d\n", onoff));
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err)
+ printf("%s: uplcom_break: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+}
+
+Static usbd_status
+uplcom_set_crtscts(struct uplcom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("uplcom_set_crtscts: on\n"));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UPLCOM_SET_REQUEST;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err) {
+ printf("%s: uplcom_set_crtscts: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static usbd_status
+uplcom_set_line_coding(struct uplcom_softc *sc, usb_cdc_line_state_t *state)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF((
+"uplcom_set_line_coding: rate = %d, fmt = %d, parity = %d bits = %d\n",
+ UGETDW(state->dwDTERate), state->bCharFormat,
+ state->bParityType, state->bDataBits));
+
+ if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
+ DPRINTF(("uplcom_set_line_coding: already set\n"));
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_LINE_CODING;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, state);
+ if (err) {
+ printf("%s: uplcom_set_line_coding: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ sc->sc_line_state = *state;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static int
+uplcom_param(void *addr, int portno, struct termios *t)
+{
+ struct uplcom_softc *sc = addr;
+ usbd_status err;
+ usb_cdc_line_state_t ls;
+
+ DPRINTF(("uplcom_param: sc = %p\n", sc));
+
+ USETDW(ls.dwDTERate, t->c_ospeed);
+ if (ISSET(t->c_cflag, CSTOPB))
+ ls.bCharFormat = UCDC_STOP_BIT_2;
+ else
+ ls.bCharFormat = UCDC_STOP_BIT_1;
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ ls.bParityType = UCDC_PARITY_ODD;
+ else
+ ls.bParityType = UCDC_PARITY_EVEN;
+ } else
+ ls.bParityType = UCDC_PARITY_NONE;
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ ls.bDataBits = 5;
+ break;
+ case CS6:
+ ls.bDataBits = 6;
+ break;
+ case CS7:
+ ls.bDataBits = 7;
+ break;
+ case CS8:
+ ls.bDataBits = 8;
+ break;
+ }
+
+ err = uplcom_set_line_coding(sc, &ls);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CRTSCTS)) {
+ err = uplcom_set_crtscts(sc);
+ if (err)
+ return (EIO);
+ }
+
+ return (0);
+}
+
+Static int
+uplcom_open(void *addr, int portno)
+{
+ struct uplcom_softc *sc = addr;
+ int err;
+
+ if (sc->sc_ucom.sc_dying)
+ return (ENXIO);
+
+ DPRINTF(("uplcom_open: sc = %p\n", sc));
+
+ if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
+ sc->sc_status = 0; /* clear status bit */
+ sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(sc->sc_intr_iface,
+ sc->sc_intr_number,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intr_pipe,
+ sc,
+ sc->sc_intr_buf,
+ sc->sc_isize,
+ uplcom_intr,
+ uplcominterval);
+ if (err) {
+ printf("%s: cannot open interrupt pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ sc->sc_intr_number);
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+Static void
+uplcom_close(void *addr, int portno)
+{
+ struct uplcom_softc *sc = addr;
+ int err;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ DPRINTF(("uplcom_close: close\n"));
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: abort interrupt pipe failed: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: close interrupt pipe failed: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
+
+Static void
+uplcom_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct uplcom_softc *sc = priv;
+ u_char *buf = sc->sc_intr_buf;
+ u_char pstatus;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ DPRINTF(("%s: uplcom_intr: abnormal status: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(status)));
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+
+ DPRINTF(("%s: uplcom status = %02x\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), buf[8]));
+
+ sc->sc_lsr = sc->sc_msr = 0;
+ pstatus = buf[8];
+ if (ISSET(pstatus, RSAQ_STATUS_DSR))
+ sc->sc_msr |= UMSR_DSR;
+ if (ISSET(pstatus, RSAQ_STATUS_DCD))
+ sc->sc_msr |= UMSR_DCD;
+ ucom_status_change(&sc->sc_ucom);
+}
+
+Static void
+uplcom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct uplcom_softc *sc = addr;
+
+ DPRINTF(("uplcom_get_status:\n"));
+
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
+
+#if TODO
+Static int
+uplcom_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag,
+ usb_proc_ptr p)
+{
+ struct uplcom_softc *sc = addr;
+ int error = 0;
+
+ if (sc->sc_ucom.sc_dying)
+ return (EIO);
+
+ DPRINTF(("uplcom_ioctl: cmd = 0x%08lx\n", cmd));
+
+ switch (cmd) {
+ case TIOCNOTTY:
+ case TIOCMGET:
+ case TIOCMSET:
+ case USB_GET_CM_OVER_DATA:
+ case USB_SET_CM_OVER_DATA:
+ break;
+
+ default:
+ DPRINTF(("uplcom_ioctl: unknown\n"));
+ error = ENOTTY;
+ break;
+ }
+
+ return (error);
+}
+#endif
diff --git a/sys/dev/usb/urio.c b/sys/dev/usb/urio.c
new file mode 100644
index 0000000..8685f0d
--- /dev/null
+++ b/sys/dev/usb/urio.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (c) 2000 Iwasa Kazmi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+/*
+ * 2000/3/24 added NetBSD/OpenBSD support (from Alex Nemirovsky)
+ * 2000/3/07 use two bulk-pipe handles for read and write (Dirk)
+ * 2000/3/06 change major number(143), and copyright header
+ * some fix for 4.0 (Dirk)
+ * 2000/3/05 codes for FreeBSD 4.x - CURRENT (Thanks to Dirk-Willem van Gulik)
+ * 2000/3/01 remove retry code from urioioctl()
+ * change method of bulk transfer (no interrupt)
+ * 2000/2/28 small fixes for new rio_usb.h
+ * 2000/2/24 first version.
+ */
+
+#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>
+#endif
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/rio500_usb.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uriodebug) logprintf x
+#define DPRINTFN(n,x) if (uriodebug>(n)) logprintf x
+int uriodebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, urio, CTLFLAG_RW, 0, "USB urio");
+SYSCTL_INT(_hw_usb_urio, OID_AUTO, debug, CTLFLAG_RW,
+ &uriodebug, 0, "urio debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+/* difference of usbd interface */
+#define USBDI 1
+
+#define RIO_OUT 0
+#define RIO_IN 1
+#define RIO_NODIR 2
+
+#if defined(__NetBSD__)
+int urioopen(dev_t, int, int, struct proc *);
+int urioclose(dev_t, int, int, struct proc *p);
+int urioread(dev_t, struct uio *uio, int);
+int uriowrite(dev_t, struct uio *uio, int);
+int urioioctl(dev_t, u_long, caddr_t, int, struct proc *);
+
+cdev_decl(urio);
+#define RIO_UE_GET_DIR(p) ((UE_GET_DIR(p) == UE_DIR_IN) ? RIO_IN :\
+ ((UE_GET_DIR(p) == UE_DIR_OUT) ? RIO_OUT :\
+ RIO_NODIR))
+#elif defined(__FreeBSD__)
+d_open_t urioopen;
+d_close_t urioclose;
+d_read_t urioread;
+d_write_t uriowrite;
+d_ioctl_t urioioctl;
+
+
+Static struct cdevsw urio_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = urioopen,
+ .d_close = urioclose,
+ .d_read = urioread,
+ .d_write = uriowrite,
+ .d_ioctl = urioioctl,
+ .d_name = "urio",
+#if __FreeBSD_version < 500014
+ .d_bmaj = -1
+#endif
+};
+#define RIO_UE_GET_DIR(p) ((UE_GET_DIR(p) == UE_DIR_IN) ? RIO_IN :\
+ ((UE_GET_DIR(p) == UE_DIR_OUT) ? RIO_OUT :\
+ RIO_NODIR))
+#endif /*defined(__FreeBSD__)*/
+
+#define URIO_BBSIZE 1024
+
+struct urio_softc {
+ USBBASEDEVICE sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+
+ int sc_opened;
+ usbd_pipe_handle sc_pipeh_in;
+ usbd_pipe_handle sc_pipeh_out;
+ int sc_epaddr[2];
+
+ int sc_refcnt;
+#if defined(__FreeBSD__)
+ struct cdev *sc_dev_t;
+#endif /* defined(__FreeBSD__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ u_char sc_dying;
+#endif
+};
+
+#define URIOUNIT(n) (minor(n))
+
+#define RIO_RW_TIMEOUT 4000 /* ms */
+
+USB_DECLARE_DRIVER(urio);
+
+USB_MATCH(urio)
+{
+ USB_MATCH_START(urio, uaa);
+ usb_device_descriptor_t *dd;
+
+ DPRINTFN(10,("urio_match\n"));
+ if (!uaa->iface)
+ return UMATCH_NONE;
+
+ dd = usbd_get_device_descriptor(uaa->device);
+
+ if (dd &&
+ ((UGETW(dd->idVendor) == USB_VENDOR_DIAMOND &&
+ UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND_RIO500USB) ||
+ (UGETW(dd->idVendor) == USB_VENDOR_DIAMOND2 &&
+ (UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND2_RIO600USB ||
+ UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND2_RIO800USB))))
+ return UMATCH_VENDOR_PRODUCT;
+ else
+ return UMATCH_NONE;
+}
+
+USB_ATTACH(urio)
+{
+ USB_ATTACH_START(urio, sc, uaa);
+ char devinfo[1024];
+ usbd_device_handle udev;
+ usbd_interface_handle iface;
+ u_int8_t epcount;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ u_int8_t niface;
+#endif
+ usbd_status r;
+ char * ermsg = "<none>";
+ int i;
+
+ DPRINTFN(10,("urio_attach: sc=%p\n", sc));
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ sc->sc_udev = udev = uaa->device;
+
+#if defined(__FreeBSD__)
+ if ((!uaa->device) || (!uaa->iface)) {
+ ermsg = "device or iface";
+ goto nobulk;
+ }
+ sc->sc_iface = iface = uaa->iface;
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ if (!udev) {
+ ermsg = "device";
+ goto nobulk;
+ }
+ r = usbd_interface_count(udev, &niface);
+ if (r) {
+ ermsg = "iface";
+ goto nobulk;
+ }
+ r = usbd_device2interface_handle(udev, 0, &iface);
+ if (r) {
+ ermsg = "iface";
+ goto nobulk;
+ }
+ sc->sc_iface = iface;
+#endif
+ sc->sc_opened = 0;
+ sc->sc_pipeh_in = 0;
+ sc->sc_pipeh_out = 0;
+ sc->sc_refcnt = 0;
+
+ r = usbd_endpoint_count(iface, &epcount);
+ if (r != USBD_NORMAL_COMPLETION) {
+ ermsg = "endpoints";
+ goto nobulk;
+ }
+
+ sc->sc_epaddr[RIO_OUT] = 0xff;
+ sc->sc_epaddr[RIO_IN] = 0x00;
+
+ for (i = 0; i < epcount; i++) {
+ usb_endpoint_descriptor_t *edesc =
+ usbd_interface2endpoint_descriptor(iface, i);
+ int d;
+
+ if (!edesc) {
+ ermsg = "interface endpoint";
+ goto nobulk;
+ }
+
+ d = RIO_UE_GET_DIR(edesc->bEndpointAddress);
+ if (d != RIO_NODIR)
+ sc->sc_epaddr[d] = edesc->bEndpointAddress;
+ }
+ if ( sc->sc_epaddr[RIO_OUT] == 0xff ||
+ sc->sc_epaddr[RIO_IN] == 0x00) {
+ ermsg = "Rio I&O";
+ goto nobulk;
+ }
+
+#if defined(__FreeBSD__)
+ /* XXX no error trapping, no storing of struct cdev **/
+ sc->sc_dev_t = make_dev(&urio_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR,
+ 0644, "urio%d", device_get_unit(self));
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+#endif
+
+ DPRINTFN(10, ("urio_attach: %p\n", sc->sc_udev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+
+ nobulk:
+ printf("%s: could not find %s\n", USBDEVNAME(sc->sc_dev),ermsg);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+
+int
+urioopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+#if (USBDI >= 1)
+ struct urio_softc * sc;
+#endif
+ int unit = URIOUNIT(dev);
+ USB_GET_SC_OPEN(urio, unit, sc);
+
+ DPRINTFN(5, ("urioopen: flag=%d, mode=%d, unit=%d\n",
+ flag, mode, unit));
+
+ if (sc->sc_opened)
+ return EBUSY;
+
+ if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD))
+ return EACCES;
+
+ sc->sc_opened = 1;
+ sc->sc_pipeh_in = 0;
+ sc->sc_pipeh_out = 0;
+ if (usbd_open_pipe(sc->sc_iface,
+ sc->sc_epaddr[RIO_IN], 0, &sc->sc_pipeh_in)
+ != USBD_NORMAL_COMPLETION)
+ {
+ sc->sc_pipeh_in = 0;
+ return EIO;
+ };
+ if (usbd_open_pipe(sc->sc_iface,
+ sc->sc_epaddr[RIO_OUT], 0, &sc->sc_pipeh_out)
+ != USBD_NORMAL_COMPLETION)
+ {
+ usbd_close_pipe(sc->sc_pipeh_in);
+ sc->sc_pipeh_in = 0;
+ sc->sc_pipeh_out = 0;
+ return EIO;
+ };
+ return 0;
+}
+
+int
+urioclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+#if (USBDI >= 1)
+ struct urio_softc * sc;
+#endif
+ int unit = URIOUNIT(dev);
+ USB_GET_SC(urio, unit, sc);
+
+ DPRINTFN(5, ("urioclose: flag=%d, mode=%d, unit=%d\n", flag, mode, unit));
+ if (sc->sc_pipeh_in)
+ usbd_close_pipe(sc->sc_pipeh_in);
+
+ if (sc->sc_pipeh_out)
+ usbd_close_pipe(sc->sc_pipeh_out);
+
+ sc->sc_pipeh_in = 0;
+ sc->sc_pipeh_out = 0;
+ sc->sc_opened = 0;
+ sc->sc_refcnt = 0;
+ return 0;
+}
+
+int
+urioread(struct cdev *dev, struct uio *uio, int flag)
+{
+#if (USBDI >= 1)
+ struct urio_softc * sc;
+ usbd_xfer_handle reqh;
+#else
+ usbd_request_handle reqh;
+ usbd_private_handle r_priv;
+ void *r_buff;
+ usbd_status r_status;
+#endif
+ int unit = URIOUNIT(dev);
+ usbd_status r;
+ char buf[URIO_BBSIZE];
+ u_int32_t n, tn;
+ int error = 0;
+
+ USB_GET_SC(urio, unit, sc);
+
+ DPRINTFN(5, ("urioread: %d\n", unit));
+ if (!sc->sc_opened)
+ return EIO;
+
+#if (USBDI >= 1)
+ sc->sc_refcnt++;
+ reqh = usbd_alloc_xfer(sc->sc_udev);
+#else
+ reqh = usbd_alloc_request();
+#endif
+ if (reqh == 0)
+ return ENOMEM;
+ while ((n = min(URIO_BBSIZE, uio->uio_resid)) != 0) {
+ DPRINTFN(1, ("urioread: start transfer %d bytes\n", n));
+ tn = n;
+#if (USBDI >= 1)
+ usbd_setup_xfer(reqh, sc->sc_pipeh_in, 0, buf, tn,
+ 0, RIO_RW_TIMEOUT, 0);
+#else
+ r = usbd_setup_request(reqh, sc->sc_pipeh_in, 0, buf, tn,
+ 0, RIO_RW_TIMEOUT, 0);
+ if (r != USBD_NORMAL_COMPLETION) {
+ error = EIO;
+ break;
+ }
+#endif
+ r = usbd_sync_transfer(reqh);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTFN(1, ("urioread: error=%d\n", r));
+ usbd_clear_endpoint_stall(sc->sc_pipeh_in);
+ tn = 0;
+ error = EIO;
+ break;
+ }
+#if (USBDI >= 1)
+ usbd_get_xfer_status(reqh, 0, 0, &tn, 0);
+#else
+ usbd_get_request_status(reqh, &r_priv, &r_buff, &tn, &r_status);
+#endif
+
+ DPRINTFN(1, ("urioread: got %d bytes\n", tn));
+ error = uiomove(buf, tn, uio);
+ if (error || tn < n)
+ break;
+ }
+#if (USBDI >= 1)
+ usbd_free_xfer(reqh);
+#else
+ usbd_free_request(reqh);
+#endif
+
+ return error;
+}
+
+int
+uriowrite(struct cdev *dev, struct uio *uio, int flag)
+{
+#if (USBDI >= 1)
+ struct urio_softc * sc;
+ usbd_xfer_handle reqh;
+#else
+ usbd_request_handle reqh;
+#endif
+ int unit = URIOUNIT(dev);
+ usbd_status r;
+ char buf[URIO_BBSIZE];
+ u_int32_t n;
+ int error = 0;
+
+ USB_GET_SC(urio, unit, sc);
+
+ DPRINTFN(5, ("uriowrite: %d\n", unit));
+ if (!sc->sc_opened)
+ return EIO;
+
+#if (USBDI >= 1)
+ sc->sc_refcnt++;
+ reqh = usbd_alloc_xfer(sc->sc_udev);
+#else
+ reqh = usbd_alloc_request();
+#endif
+ if (reqh == 0)
+ return EIO;
+ while ((n = min(URIO_BBSIZE, uio->uio_resid)) != 0) {
+ error = uiomove(buf, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("uriowrite: transfer %d bytes\n", n));
+#if (USBDI >= 1)
+ usbd_setup_xfer(reqh, sc->sc_pipeh_out, 0, buf, n,
+ 0, RIO_RW_TIMEOUT, 0);
+#else
+ r = usbd_setup_request(reqh, sc->sc_pipeh_out, 0, buf, n,
+ 0, RIO_RW_TIMEOUT, 0);
+ if (r != USBD_NORMAL_COMPLETION) {
+ error = EIO;
+ break;
+ }
+#endif
+ r = usbd_sync_transfer(reqh);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTFN(1, ("uriowrite: error=%d\n", r));
+ usbd_clear_endpoint_stall(sc->sc_pipeh_out);
+ error = EIO;
+ break;
+ }
+#if (USBDI >= 1)
+ usbd_get_xfer_status(reqh, 0, 0, 0, 0);
+#endif
+ }
+
+#if (USBDI >= 1)
+ usbd_free_xfer(reqh);
+#else
+ usbd_free_request(reqh);
+#endif
+
+ return error;
+}
+
+
+int
+urioioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
+{
+#if (USBDI >= 1)
+ struct urio_softc * sc;
+#endif
+ int unit = URIOUNIT(dev);
+ struct RioCommand *rio_cmd;
+ int requesttype, len;
+ struct iovec iov;
+ struct uio uio;
+ usb_device_request_t req;
+ int req_flags = 0, req_actlen = 0;
+ void *ptr = 0;
+ int error = 0;
+ usbd_status r;
+
+ USB_GET_SC(urio, unit, sc);
+
+ switch (cmd) {
+ case RIO_RECV_COMMAND:
+ if (!(flag & FWRITE))
+ return EPERM;
+ rio_cmd = (struct RioCommand *)addr;
+ if (rio_cmd == NULL)
+ return EINVAL;
+ len = rio_cmd->length;
+
+ requesttype = rio_cmd->requesttype | UT_READ_VENDOR_DEVICE;
+ DPRINTFN(1,("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n",
+ requesttype, rio_cmd->request, rio_cmd->value, rio_cmd->index, len));
+ break;
+
+ case RIO_SEND_COMMAND:
+ if (!(flag & FWRITE))
+ return EPERM;
+ rio_cmd = (struct RioCommand *)addr;
+ if (rio_cmd == NULL)
+ return EINVAL;
+ len = rio_cmd->length;
+
+ requesttype = rio_cmd->requesttype | UT_WRITE_VENDOR_DEVICE;
+ DPRINTFN(1,("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n",
+ requesttype, rio_cmd->request, rio_cmd->value, rio_cmd->index, len));
+ break;
+
+ default:
+ return EINVAL;
+ break;
+ }
+
+ /* Send rio control message */
+ req.bmRequestType = requesttype;
+ req.bRequest = rio_cmd->request;
+ USETW(req.wValue, rio_cmd->value);
+ USETW(req.wIndex, rio_cmd->index);
+ USETW(req.wLength, len);
+
+ if (len < 0 || len > 32767)
+ return EINVAL;
+ if (len != 0) {
+ iov.iov_base = (caddr_t)rio_cmd->buffer;
+ 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 =
+ req.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_flags(sc->sc_udev, &req,
+ ptr, req_flags, &req_actlen,
+ USBD_DEFAULT_TIMEOUT);
+ if (r == USBD_NORMAL_COMPLETION) {
+ error = 0;
+ if (len != 0) {
+ if (uio.uio_rw == UIO_READ) {
+ error = uiomove(ptr, len, &uio);
+ }
+ }
+ } else {
+ error = EIO;
+ }
+
+ret:
+ if (ptr)
+ free(ptr, M_TEMP);
+ return error;
+}
+
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+urio_activate(device_ptr_t self, enum devact act)
+{
+ struct urio_softc *sc = (struct urio_softc *)self;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+
+USB_DETACH(urio)
+{
+ USB_DETACH_START(urio, sc);
+ struct urio_endpoint *sce;
+ int i, dir;
+ int s;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ int maj, mn;
+
+ DPRINTF(("urio_detach: sc=%p flags=%d\n", sc, flags));
+#elif defined(__FreeBSD__)
+ DPRINTF(("urio_detach: sc=%p\n", sc));
+#endif
+
+ sc->sc_dying = 1;
+ /* Abort all pipes. Causes processes waiting for transfer to wake. */
+#if 0
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+ for (dir = OUT; dir <= IN; dir++) {
+ sce = &sc->sc_endpoints[i][dir];
+ if (sce && sce->pipeh)
+ usbd_abort_pipe(sce->pipeh);
+ }
+ }
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wake everyone */
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ wakeup(&sc->sc_endpoints[i][IN]);
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+#else
+ if (sc->sc_pipeh_in)
+ usbd_abort_pipe(sc->sc_pipeh_in);
+
+ if (sc->sc_pipeh_out)
+ usbd_abort_pipe(sc->sc_pipeh_out);
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == urioopen)
+ break;
+
+ /* Nuke the vnodes for any open instances (calls close). */
+ mn = self->dv_unit * USB_MAX_ENDPOINTS;
+ vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ return (0);
+}
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
+
+#if defined(__FreeBSD__)
+Static int
+urio_detach(device_t self)
+{
+ struct urio_softc *sc = device_get_softc(self);
+
+ DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
+ destroy_dev(sc->sc_dev_t);
+ /* XXX not implemented yet */
+ device_set_desc(self, NULL);
+ return 0;
+}
+
+DRIVER_MODULE(urio, uhub, urio_driver, urio_devclass, usbd_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c
new file mode 100644
index 0000000..d934491
--- /dev/null
+++ b/sys/dev/usb/usb.c
@@ -0,0 +1,925 @@
+/* $NetBSD: usb.c,v 1.68 2002/02/20 20:30:12 christos Exp $ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: usb.c,v 1.70 2002/05/09 21:54:32 augustss Exp $
+ * $NetBSD: usb.c,v 1.71 2002/06/01 23:51:04 lukem Exp $
+ * $NetBSD: usb.c,v 1.73 2002/09/23 05:51:19 simonb Exp $
+ * $NetBSD: usb.c,v 1.80 2003/11/07 17:03:25 wiz Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 specifications and other documentation can be found at
+ * http://www.usb.org/developers/docs/ and
+ * http://www.usb.org/developers/devclass_docs/
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#if __FreeBSD_version >= 500000
+#include <sys/mutex.h>
+#endif
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/unistd.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/filio.h>
+#include <sys/uio.h>
+#endif
+#include <sys/kthread.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/poll.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#define USBUNIT(d) (minor(d)) /* usb_discover device nodes, kthread */
+#define USB_DEV_MINOR 255 /* event queue device */
+
+#if defined(__FreeBSD__)
+MALLOC_DEFINE(M_USB, "USB", "USB");
+MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device");
+MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller");
+
+#include "usb_if.h"
+#endif /* defined(__FreeBSD__) */
+
+#include <machine/bus.h>
+
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_quirks.h>
+
+/* Define this unconditionally in case a kernel module is loaded that
+ * has been compiled with debugging options.
+ */
+SYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging");
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) logprintf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
+int usbdebug = 0;
+SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW,
+ &usbdebug, 0, "usb debug level");
+/*
+ * 0 - do usual exploration
+ * 1 - do not use timeout exploration
+ * >1 - do no exploration
+ */
+int usb_noexplore = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct usb_softc {
+ USBBASEDEVICE sc_dev; /* base device */
+ usbd_bus_handle sc_bus; /* USB controller */
+ struct usbd_port sc_port; /* dummy port for root hub */
+
+ struct proc *sc_event_thread;
+
+ char sc_dying;
+};
+
+TAILQ_HEAD(, usb_task) usb_all_tasks;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+cdev_decl(usb);
+#elif defined(__FreeBSD__)
+d_open_t usbopen;
+d_close_t usbclose;
+d_read_t usbread;
+d_ioctl_t usbioctl;
+d_poll_t usbpoll;
+
+struct cdevsw usb_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = usbopen,
+ .d_close = usbclose,
+ .d_read = usbread,
+ .d_ioctl = usbioctl,
+ .d_poll = usbpoll,
+ .d_name = "usb",
+#if __FreeBSD_version < 500014
+ .d_bmaj = -1
+#endif
+};
+#endif
+
+Static void usb_discover(void *);
+Static void usb_create_event_thread(void *);
+Static void usb_event_thread(void *);
+Static void usb_task_thread(void *);
+Static struct proc *usb_task_thread_proc = NULL;
+
+#define USB_MAX_EVENTS 100
+struct usb_event_q {
+ struct usb_event ue;
+ TAILQ_ENTRY(usb_event_q) next;
+};
+Static TAILQ_HEAD(, usb_event_q) usb_events =
+ TAILQ_HEAD_INITIALIZER(usb_events);
+Static int usb_nevents = 0;
+Static struct selinfo usb_selevent;
+Static struct proc *usb_async_proc; /* process that wants USB SIGIO */
+Static int usb_dev_open = 0;
+Static void usb_add_event(int, struct usb_event *);
+
+Static int usb_get_next_event(struct usb_event *);
+
+Static const char *usbrev_str[] = USBREV_STR;
+
+USB_DECLARE_DRIVER_INIT(usb,
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown)
+ );
+
+#if defined(__FreeBSD__)
+MODULE_VERSION(usb, 1);
+#endif
+
+USB_MATCH(usb)
+{
+ DPRINTF(("usbd_match\n"));
+ return (UMATCH_GENERIC);
+}
+
+USB_ATTACH(usb)
+{
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ struct usb_softc *sc = (struct usb_softc *)self;
+#elif defined(__FreeBSD__)
+ struct usb_softc *sc = device_get_softc(self);
+ void *aux = device_get_ivars(self);
+ static int global_init_done = 0;
+#endif
+ usbd_device_handle dev;
+ usbd_status err;
+ int usbrev;
+ int speed;
+ struct usb_event ue;
+
+ sc->sc_dev = self;
+
+ DPRINTF(("usbd_attach\n"));
+
+ usbd_init();
+ sc->sc_bus = aux;
+ sc->sc_bus->usbctl = sc;
+ sc->sc_port.power = USB_MAX_POWER;
+
+#if defined(__FreeBSD__)
+ printf("%s", USBDEVNAME(sc->sc_dev));
+#endif
+ usbrev = sc->sc_bus->usbrev;
+ printf(": USB revision %s", usbrev_str[usbrev]);
+ switch (usbrev) {
+ case USBREV_1_0:
+ case USBREV_1_1:
+ speed = USB_SPEED_FULL;
+ break;
+ case USBREV_2_0:
+ speed = USB_SPEED_HIGH;
+ break;
+ default:
+ printf(", not supported\n");
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+ printf("\n");
+
+ /* Make sure not to use tsleep() if we are cold booting. */
+ if (cold)
+ sc->sc_bus->use_polling++;
+
+ ue.u.ue_ctrlr.ue_bus = USBDEVUNIT(sc->sc_dev);
+ usb_add_event(USB_EVENT_CTRLR_ATTACH, &ue);
+
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ /* XXX we should have our own level */
+ sc->sc_bus->soft = softintr_establish(IPL_SOFTNET,
+ sc->sc_bus->methods->soft_intr, sc->sc_bus);
+ if (sc->sc_bus->soft == NULL) {
+ printf("%s: can't register softintr\n", USBDEVNAME(sc->sc_dev));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+#else
+ usb_callout_init(sc->sc_bus->softi);
+#endif
+#endif
+
+ err = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0, speed, 0,
+ &sc->sc_port);
+ if (!err) {
+ dev = sc->sc_port.device;
+ if (dev->hub == NULL) {
+ sc->sc_dying = 1;
+ printf("%s: root device is not a hub\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+ sc->sc_bus->root_hub = dev;
+#if 1
+ /*
+ * Turning this code off will delay attachment of USB devices
+ * until the USB event thread is running, which means that
+ * the keyboard will not work until after cold boot.
+ */
+#if defined(__FreeBSD__)
+ if (cold)
+#else
+ if (cold && (sc->sc_dev.dv_cfdata->cf_flags & 1))
+#endif
+ dev->hub->explore(sc->sc_bus->root_hub);
+#endif
+ } else {
+ printf("%s: root hub problem, error=%d\n",
+ USBDEVNAME(sc->sc_dev), err);
+ sc->sc_dying = 1;
+ }
+ if (cold)
+ sc->sc_bus->use_polling--;
+
+ config_pending_incr();
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ usb_kthread_create(usb_create_event_thread, sc);
+#endif
+
+#if defined(__FreeBSD__)
+ usb_create_event_thread(sc);
+ /* The per controller devices (used for usb_discover) */
+ /* XXX This is redundant now, but old usbd's will want it */
+ make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR,
+ 0660, "usb%d", device_get_unit(self));
+ if (!global_init_done) {
+ /* The device spitting out events */
+ make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT, GID_OPERATOR,
+ 0660, "usb");
+ global_init_done = 1;
+ }
+#endif
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+void
+usb_create_event_thread(void *arg)
+{
+ struct usb_softc *sc = arg;
+ static int created = 0;
+
+ if (usb_kthread_create1(usb_event_thread, sc, &sc->sc_event_thread,
+ "%s", USBDEVNAME(sc->sc_dev))) {
+ printf("%s: unable to create event thread for\n",
+ USBDEVNAME(sc->sc_dev));
+ panic("usb_create_event_thread");
+ }
+ if (!created) {
+ created = 1;
+ TAILQ_INIT(&usb_all_tasks);
+ if (usb_kthread_create2(usb_task_thread, NULL,
+ &usb_task_thread_proc, "usbtask")) {
+ printf("unable to create task thread\n");
+ panic("usb_create_event_thread task");
+ }
+ }
+}
+
+/*
+ * Add a task to be performed by the task thread. This function can be
+ * called from any context and the task will be executed in a process
+ * context ASAP.
+ */
+void
+usb_add_task(usbd_device_handle dev, struct usb_task *task)
+{
+ int s;
+
+ s = splusb();
+ if (!task->onqueue) {
+ DPRINTFN(2,("usb_add_task: task=%p\n", task));
+ TAILQ_INSERT_TAIL(&usb_all_tasks, task, next);
+ task->onqueue = 1;
+ } else {
+ DPRINTFN(3,("usb_add_task: task=%p on q\n", task));
+ }
+ wakeup(&usb_all_tasks);
+ splx(s);
+}
+
+void
+usb_rem_task(usbd_device_handle dev, struct usb_task *task)
+{
+ int s;
+
+ s = splusb();
+ if (task->onqueue) {
+ TAILQ_REMOVE(&usb_all_tasks, task, next);
+ task->onqueue = 0;
+ }
+ splx(s);
+}
+
+void
+usb_event_thread(void *arg)
+{
+ struct usb_softc *sc = arg;
+
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+ mtx_lock(&Giant);
+#endif
+
+ DPRINTF(("usb_event_thread: start\n"));
+
+ /*
+ * In case this controller is a companion controller to an
+ * EHCI controller we need to wait until the EHCI controller
+ * has grabbed the port.
+ * XXX It would be nicer to do this with a tsleep(), but I don't
+ * know how to synchronize the creation of the threads so it
+ * will work.
+ */
+ usb_delay_ms(sc->sc_bus, 500);
+
+ /* Make sure first discover does something. */
+ sc->sc_bus->needs_explore = 1;
+ usb_discover(sc);
+ config_pending_decr();
+
+ while (!sc->sc_dying) {
+#ifdef USB_DEBUG
+ if (usb_noexplore < 2)
+#endif
+ usb_discover(sc);
+#ifdef USB_DEBUG
+ (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt",
+ usb_noexplore ? 0 : hz * 60);
+#else
+ (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt",
+ hz * 60);
+#endif
+ DPRINTFN(2,("usb_event_thread: woke up\n"));
+ }
+ sc->sc_event_thread = NULL;
+
+ /* In case parent is waiting for us to exit. */
+ wakeup(sc);
+
+ DPRINTF(("usb_event_thread: exit\n"));
+ kthread_exit(0);
+}
+
+void
+usb_task_thread(void *arg)
+{
+ struct usb_task *task;
+ int s;
+
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+ mtx_lock(&Giant);
+#endif
+
+ DPRINTF(("usb_task_thread: start\n"));
+
+ s = splusb();
+ for (;;) {
+ task = TAILQ_FIRST(&usb_all_tasks);
+ if (task == NULL) {
+ tsleep(&usb_all_tasks, PWAIT, "usbtsk", 0);
+ task = TAILQ_FIRST(&usb_all_tasks);
+ }
+ DPRINTFN(2,("usb_task_thread: woke up task=%p\n", task));
+ if (task != NULL) {
+ TAILQ_REMOVE(&usb_all_tasks, task, next);
+ task->onqueue = 0;
+ splx(s);
+ task->fun(task->arg);
+ s = splusb();
+ }
+ }
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+usbctlprint(void *aux, const char *pnp)
+{
+ /* only "usb"es can attach to host controllers */
+ if (pnp)
+ printf("usb at %s", pnp);
+
+ return (UNCONF);
+}
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
+
+int
+usbopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ int unit = USBUNIT(dev);
+ struct usb_softc *sc;
+
+ if (unit == USB_DEV_MINOR) {
+ if (usb_dev_open)
+ return (EBUSY);
+ usb_dev_open = 1;
+ usb_async_proc = 0;
+ return (0);
+ }
+
+ USB_GET_SC_OPEN(usb, unit, sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ return (0);
+}
+
+int
+usbread(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct usb_event ue;
+ int unit = USBUNIT(dev);
+ int s, error, n;
+
+ if (unit != USB_DEV_MINOR)
+ return (ENODEV);
+
+ if (uio->uio_resid != sizeof(struct usb_event))
+ return (EINVAL);
+
+ error = 0;
+ s = splusb();
+ for (;;) {
+ n = usb_get_next_event(&ue);
+ if (n != 0)
+ break;
+ if (flag & IO_NDELAY) {
+ error = EWOULDBLOCK;
+ break;
+ }
+ error = tsleep(&usb_events, PZERO | PCATCH, "usbrea", 0);
+ if (error)
+ break;
+ }
+ splx(s);
+ if (!error)
+ error = uiomove((void *)&ue, uio->uio_resid, uio);
+
+ return (error);
+}
+
+int
+usbclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ int unit = USBUNIT(dev);
+
+ if (unit == USB_DEV_MINOR) {
+ usb_async_proc = 0;
+ usb_dev_open = 0;
+ }
+
+ return (0);
+}
+
+int
+usbioctl(struct cdev *devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
+{
+ struct usb_softc *sc;
+ int unit = USBUNIT(devt);
+
+ if (unit == USB_DEV_MINOR) {
+ switch (cmd) {
+ case FIONBIO:
+ /* All handled in the upper FS layer. */
+ return (0);
+
+ case FIOASYNC:
+ if (*(int *)data)
+#if __FreeBSD_version >= 500000
+ usb_async_proc = p->td_proc;
+#else
+ usb_async_proc = p;
+#endif
+ else
+ usb_async_proc = 0;
+ return (0);
+
+ default:
+ return (EINVAL);
+ }
+ }
+
+ USB_GET_SC(usb, unit, sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ switch (cmd) {
+#if defined(__FreeBSD__)
+ /* This part should be deleted */
+ case USB_DISCOVER:
+ break;
+#endif
+ case USB_REQUEST:
+ {
+ struct usb_ctl_request *ur = (void *)data;
+ int len = UGETW(ur->ucr_request.wLength);
+ struct iovec iov;
+ struct uio uio;
+ void *ptr = 0;
+ int addr = ur->ucr_addr;
+ usbd_status err;
+ int error = 0;
+
+ DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%d\n", addr, len));
+ 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->ucr_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->ucr_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;
+ }
+ }
+ err = usbd_do_request_flags(sc->sc_bus->devices[addr],
+ &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen,
+ USBD_DEFAULT_TIMEOUT);
+ if (err) {
+ 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);
+ }
+
+ case USB_DEVICEINFO:
+ {
+ struct usb_device_info *di = (void *)data;
+ int addr = di->udi_addr;
+ usbd_device_handle dev;
+
+ if (addr < 1 || addr >= USB_MAX_DEVICES)
+ return (EINVAL);
+ dev = sc->sc_bus->devices[addr];
+ if (dev == NULL)
+ return (ENXIO);
+ usbd_fill_deviceinfo(dev, di, 1);
+ break;
+ }
+
+ case USB_DEVICESTATS:
+ *(struct usb_device_stats *)data = sc->sc_bus->stats;
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+usbpoll(struct cdev *dev, int events, usb_proc_ptr p)
+{
+ int revents, mask, s;
+ int unit = USBUNIT(dev);
+
+ if (unit == USB_DEV_MINOR) {
+ revents = 0;
+ mask = POLLIN | POLLRDNORM;
+
+ s = splusb();
+ if (events & mask && usb_nevents > 0)
+ revents |= events & mask;
+ if (revents == 0 && events & mask)
+ selrecord(p, &usb_selevent);
+ splx(s);
+
+ return (revents);
+ } else {
+#if defined(__FreeBSD__)
+ return (0); /* select/poll never wakes up - back compat */
+#else
+ return (ENXIO);
+#endif
+ }
+}
+
+/* Explore device tree from the root. */
+Static void
+usb_discover(void *v)
+{
+ struct usb_softc *sc = v;
+
+#if defined(__FreeBSD__)
+ /* splxxx should be changed to mutexes for preemption safety some day */
+ int s;
+#endif
+
+ DPRINTFN(2,("usb_discover\n"));
+#ifdef USB_DEBUG
+ if (usb_noexplore > 1)
+ return;
+#endif
+
+ /*
+ * We need mutual exclusion while traversing the device tree,
+ * but this is guaranteed since this function is only called
+ * from the event thread for the controller.
+ */
+#if defined(__FreeBSD__)
+ s = splusb();
+#endif
+ while (sc->sc_bus->needs_explore && !sc->sc_dying) {
+ sc->sc_bus->needs_explore = 0;
+#if defined(__FreeBSD__)
+ splx(s);
+#endif
+ sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
+#if defined(__FreeBSD__)
+ s = splusb();
+#endif
+ }
+#if defined(__FreeBSD__)
+ splx(s);
+#endif
+}
+
+void
+usb_needs_explore(usbd_device_handle dev)
+{
+ DPRINTFN(2,("usb_needs_explore\n"));
+ dev->bus->needs_explore = 1;
+ wakeup(&dev->bus->needs_explore);
+}
+
+/* Called at splusb() */
+int
+usb_get_next_event(struct usb_event *ue)
+{
+ struct usb_event_q *ueq;
+
+ if (usb_nevents <= 0)
+ return (0);
+ ueq = TAILQ_FIRST(&usb_events);
+#ifdef DIAGNOSTIC
+ if (ueq == NULL) {
+ printf("usb: usb_nevents got out of sync! %d\n", usb_nevents);
+ usb_nevents = 0;
+ return (0);
+ }
+#endif
+ *ue = ueq->ue;
+ TAILQ_REMOVE(&usb_events, ueq, next);
+ free(ueq, M_USBDEV);
+ usb_nevents--;
+ return (1);
+}
+
+void
+usbd_add_dev_event(int type, usbd_device_handle udev)
+{
+ struct usb_event ue;
+
+ usbd_fill_deviceinfo(udev, &ue.u.ue_device, USB_EVENT_IS_ATTACH(type));
+ usb_add_event(type, &ue);
+}
+
+void
+usbd_add_drv_event(int type, usbd_device_handle udev, device_ptr_t dev)
+{
+ struct usb_event ue;
+
+ ue.u.ue_driver.ue_cookie = udev->cookie;
+ strncpy(ue.u.ue_driver.ue_devname, USBDEVPTRNAME(dev),
+ sizeof ue.u.ue_driver.ue_devname);
+ usb_add_event(type, &ue);
+}
+
+void
+usb_add_event(int type, struct usb_event *uep)
+{
+ struct usb_event_q *ueq;
+ struct usb_event ue;
+ struct timeval thetime;
+ int s;
+
+ ueq = malloc(sizeof *ueq, M_USBDEV, M_WAITOK);
+ ueq->ue = *uep;
+ ueq->ue.ue_type = type;
+ microtime(&thetime);
+ TIMEVAL_TO_TIMESPEC(&thetime, &ueq->ue.ue_time);
+
+ s = splusb();
+ if (USB_EVENT_IS_DETACH(type)) {
+ struct usb_event_q *ueqi, *ueqi_next;
+
+ for (ueqi = TAILQ_FIRST(&usb_events); ueqi; ueqi = ueqi_next) {
+ ueqi_next = TAILQ_NEXT(ueqi, next);
+ if (ueqi->ue.u.ue_driver.ue_cookie.cookie ==
+ uep->u.ue_device.udi_cookie.cookie) {
+ TAILQ_REMOVE(&usb_events, ueqi, next);
+ free(ueqi, M_USBDEV);
+ usb_nevents--;
+ ueqi_next = TAILQ_FIRST(&usb_events);
+ }
+ }
+ }
+ if (usb_nevents >= USB_MAX_EVENTS) {
+ /* Too many queued events, drop an old one. */
+ DPRINTF(("usb: event dropped\n"));
+ (void)usb_get_next_event(&ue);
+ }
+ TAILQ_INSERT_TAIL(&usb_events, ueq, next);
+ usb_nevents++;
+ wakeup(&usb_events);
+ selwakeuppri(&usb_selevent, PZERO);
+ if (usb_async_proc != NULL) {
+ PROC_LOCK(usb_async_proc);
+ psignal(usb_async_proc, SIGIO);
+ PROC_UNLOCK(usb_async_proc);
+ }
+ splx(s);
+}
+
+void
+usb_schedsoftintr(usbd_bus_handle bus)
+{
+ DPRINTFN(10,("usb_schedsoftintr: polling=%d\n", bus->use_polling));
+#ifdef USB_USE_SOFTINTR
+ if (bus->use_polling) {
+ bus->methods->soft_intr(bus);
+ } else {
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ softintr_schedule(bus->soft);
+#else
+ if (!callout_pending(&bus->softi))
+ callout_reset(&bus->softi, 0, bus->methods->soft_intr,
+ bus);
+#endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */
+ }
+#else
+ bus->methods->soft_intr(bus);
+#endif /* USB_USE_SOFTINTR */
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+usb_activate(device_ptr_t self, enum devact act)
+{
+ struct usb_softc *sc = (struct usb_softc *)self;
+ usbd_device_handle dev = sc->sc_port.device;
+ int i, rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ if (dev != NULL && dev->cdesc != NULL && dev->subdevs != NULL) {
+ for (i = 0; dev->subdevs[i]; i++)
+ rv |= config_deactivate(dev->subdevs[i]);
+ }
+ break;
+ }
+ return (rv);
+}
+
+int
+usb_detach(device_ptr_t self, int flags)
+{
+ struct usb_softc *sc = (struct usb_softc *)self;
+ struct usb_event ue;
+
+ DPRINTF(("usb_detach: start\n"));
+
+ sc->sc_dying = 1;
+
+ /* Make all devices disconnect. */
+ if (sc->sc_port.device != NULL)
+ usb_disconnect_port(&sc->sc_port, self);
+
+ /* Kill off event thread. */
+ if (sc->sc_event_thread != NULL) {
+ wakeup(&sc->sc_bus->needs_explore);
+ if (tsleep(sc, PWAIT, "usbdet", hz * 60))
+ printf("%s: event thread didn't die\n",
+ USBDEVNAME(sc->sc_dev));
+ DPRINTF(("usb_detach: event thread dead\n"));
+ }
+
+ usbd_finish();
+
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ if (sc->sc_bus->soft != NULL) {
+ softintr_disestablish(sc->sc_bus->soft);
+ sc->sc_bus->soft = NULL;
+ }
+#else
+ callout_stop(&sc->sc_bus->softi);
+#endif
+#endif
+
+ ue.u.ue_ctrlr.ue_bus = USBDEVUNIT(sc->sc_dev);
+ usb_add_event(USB_EVENT_CTRLR_DETACH, &ue);
+
+ return (0);
+}
+#elif defined(__FreeBSD__)
+int
+usb_detach(device_t self)
+{
+ DPRINTF(("%s: unload, prevented\n", USBDEVNAME(self)));
+
+ return (EINVAL);
+}
+#endif
+
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usb, ehci, 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..e527859
--- /dev/null
+++ b/sys/dev/usb/usb.h
@@ -0,0 +1,693 @@
+/* $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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>
+#include <sys/time.h>
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/ioctl.h>
+#endif
+
+#if defined(_KERNEL)
+#include <dev/usb/usb_port.h>
+#endif /* _KERNEL */
+
+/* These two defines are used by usbd to autoload the usb kld */
+#define USB_KLD "usb" /* name of usb module */
+#define USB_UHUB "usb/uhub" /* root hub */
+
+#define USB_STACK_VERSION 2
+
+#define USB_MAX_DEVICES 128
+#define USB_START_ADDR 0
+
+#define USB_CONTROL_ENDPOINT 0
+#define USB_MAX_ENDPOINTS 16
+
+#define USB_FRAMES_PER_SECOND 1000
+
+/*
+ * 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 non-byte
+ * values.
+ */
+typedef u_int8_t uByte;
+typedef u_int8_t uWord[2];
+typedef u_int8_t uDWord[4];
+
+#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h))
+
+#if 1
+#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 UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24))
+#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \
+ (w)[1] = (u_int8_t)((v) >> 8), \
+ (w)[2] = (u_int8_t)((v) >> 16), \
+ (w)[3] = (u_int8_t)((v) >> 24))
+#else
+/*
+ * On little-endian machines that can handle unanliged accesses
+ * (e.g. i386) these macros can be replaced by the following.
+ */
+#define UGETW(w) (*(u_int16_t *)(w))
+#define USETW(w,v) (*(u_int16_t *)(w) = (v))
+#define UGETDW(w) (*(u_int32_t *)(w))
+#define USETDW(w,v) (*(u_int32_t *)(w) = (v))
+#endif
+
+#if defined(__FreeBSD__) && (__FreeBSD_version <= 500014)
+#define UPACKED __attribute__ ((packed))
+#else
+#define UPACKED __packed
+#endif
+
+typedef struct {
+ uByte bmRequestType;
+ uByte bRequest;
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+} UPACKED 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_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT)
+#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)
+#define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT)
+#define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE)
+#define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE)
+#define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER)
+#define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT)
+#define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE)
+#define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE)
+#define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER)
+#define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT)
+
+/* 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 0x01
+#define UDESC_CONFIG 0x02
+#define UDESC_STRING 0x03
+#define UDESC_INTERFACE 0x04
+#define UDESC_ENDPOINT 0x05
+#define UDESC_DEVICE_QUALIFIER 0x06
+#define UDESC_OTHER_SPEED_CONFIGURATION 0x07
+#define UDESC_INTERFACE_POWER 0x08
+#define UDESC_OTG 0x09
+#define UDESC_CS_DEVICE 0x21 /* class specific */
+#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 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_HALT 0
+#define UF_DEVICE_REMOTE_WAKEUP 1
+#define UF_TEST_MODE 2
+
+#define USB_MAX_IPACKET 8 /* maximum size of the initial packet */
+
+#define USB_2_MAX_CTRL_PACKET 64
+#define USB_2_MAX_BULK_PACKET 512
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+} UPACKED usb_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+#define UD_USB_2_0 0x0200
+#define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0)
+ 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;
+} UPACKED 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
+} UPACKED 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;
+} UPACKED usb_interface_descriptor_t;
+#define USB_INTERFACE_DESCRIPTOR_SIZE 9
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bEndpointAddress;
+#define UE_GET_DIR(a) ((a) & 0x80)
+#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7))
+#define UE_DIR_IN 0x80
+#define UE_DIR_OUT 0x00
+#define UE_ADDR 0x0f
+#define UE_GET_ADDR(a) ((a) & UE_ADDR)
+ uByte bmAttributes;
+#define UE_XFERTYPE 0x03
+#define UE_CONTROL 0x00
+#define UE_ISOCHRONOUS 0x01
+#define UE_BULK 0x02
+#define UE_INTERRUPT 0x03
+#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE)
+#define UE_ISO_TYPE 0x0c
+#define UE_ISO_ASYNC 0x04
+#define UE_ISO_ADAPT 0x08
+#define UE_ISO_SYNC 0x0c
+#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE)
+ uWord wMaxPacketSize;
+ uByte bInterval;
+} UPACKED usb_endpoint_descriptor_t;
+#define USB_ENDPOINT_DESCRIPTOR_SIZE 7
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bString[127];
+} UPACKED usb_string_descriptor_t;
+#define USB_MAX_STRING_LEN 128
+#define USB_LANGUAGE_TABLE 0 /* # of the string language id table */
+
+/* Hub specific request */
+#define UR_GET_BUS_STATE 0x02
+#define UR_CLEAR_TT_BUFFER 0x08
+#define UR_RESET_TT 0x09
+#define UR_GET_TT_STATE 0x0a
+#define UR_STOP_TT 0x0b
+
+/* 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
+#define UHF_PORT_TEST 21
+#define UHF_PORT_INDICATOR 22
+
+typedef struct {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord wHubCharacteristics;
+#define UHD_PWR 0x0003
+#define UHD_PWR_GANGED 0x0000
+#define UHD_PWR_INDIVIDUAL 0x0001
+#define UHD_PWR_NO_SWITCH 0x0002
+#define UHD_COMPOUND 0x0004
+#define UHD_OC 0x0018
+#define UHD_OC_GLOBAL 0x0000
+#define UHD_OC_INDIVIDUAL 0x0008
+#define UHD_OC_NONE 0x0010
+#define UHD_TT_THINK 0x0060
+#define UHD_TT_THINK_8 0x0000
+#define UHD_TT_THINK_16 0x0020
+#define UHD_TT_THINK_24 0x0040
+#define UHD_TT_THINK_32 0x0060
+#define UHD_PORT_IND 0x0080
+ uByte bPwrOn2PwrGood; /* delay in 2 ms units */
+#define UHD_PWRON_FACTOR 2
+ uByte bHubContrCurrent;
+ uByte DeviceRemovable[32]; /* max 255 ports */
+#define UHD_NOT_REMOV(desc, i) \
+ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1)
+ /* deprecated */ uByte PortPowerCtrlMask[1];
+} UPACKED usb_hub_descriptor_t;
+#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize0;
+ uByte bNumConfigurations;
+ uByte bReserved;
+} UPACKED usb_device_qualifier_t;
+#define USB_DEVICE_QUALIFIER_SIZE 10
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bmAttributes;
+#define UOTG_SRP 0x01
+#define UOTG_HNP 0x02
+} UPACKED usb_otg_descriptor_t;
+
+/* OTG feature selectors */
+#define UOTG_B_HNP_ENABLE 3
+#define UOTG_A_HNP_SUPPORT 4
+#define UOTG_A_ALT_HNP_SUPPORT 5
+
+typedef struct {
+ uWord wStatus;
+/* Device status flags */
+#define UDS_SELF_POWERED 0x0001
+#define UDS_REMOTE_WAKEUP 0x0002
+/* Endpoint status flags */
+#define UES_HALT 0x0001
+} UPACKED usb_status_t;
+
+typedef struct {
+ uWord wHubStatus;
+#define UHS_LOCAL_POWER 0x0001
+#define UHS_OVER_CURRENT 0x0002
+ uWord wHubChange;
+} UPACKED 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
+#define UPS_HIGH_SPEED 0x0400
+#define UPS_PORT_TEST 0x0800
+#define UPS_PORT_INDICATOR 0x1000
+ 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
+} UPACKED usb_port_status_t;
+
+/* Device class codes */
+#define UDCLASS_IN_INTERFACE 0x00
+#define UDCLASS_COMM 0x02
+#define UDCLASS_HUB 0x09
+#define UDSUBCLASS_HUB 0x00
+#define UDPROTO_FSHUB 0x00
+#define UDPROTO_HSHUBSTT 0x01
+#define UDPROTO_HSHUBMTT 0x02
+#define UDCLASS_DIAGNOSTIC 0xdc
+#define UDCLASS_WIRELESS 0xe0
+#define UDSUBCLASS_RF 0x01
+#define UDPROTO_BLUETOOTH 0x01
+#define UDCLASS_VENDOR 0xff
+
+/* Interface class codes */
+#define UICLASS_UNSPEC 0x00
+
+#define UICLASS_AUDIO 0x01
+#define UISUBCLASS_AUDIOCONTROL 1
+#define UISUBCLASS_AUDIOSTREAM 2
+#define UISUBCLASS_MIDISTREAM 3
+
+#define UICLASS_CDC 0x02 /* communication */
+#define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1
+#define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2
+#define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3
+#define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4
+#define UISUBCLASS_CAPI_CONTROLMODEL 5
+#define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6
+#define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7
+#define UIPROTO_CDC_AT 1
+
+#define UICLASS_HID 0x03
+#define UISUBCLASS_BOOT 1
+#define UIPROTO_BOOT_KEYBOARD 1
+
+#define UICLASS_PHYSICAL 0x05
+
+#define UICLASS_IMAGE 0x06
+
+#define UICLASS_PRINTER 0x07
+#define UISUBCLASS_PRINTER 1
+#define UIPROTO_PRINTER_UNI 1
+#define UIPROTO_PRINTER_BI 2
+#define UIPROTO_PRINTER_1284 3
+
+#define UICLASS_MASS 0x08
+#define UISUBCLASS_RBC 1
+#define UISUBCLASS_SFF8020I 2
+#define UISUBCLASS_QIC157 3
+#define UISUBCLASS_UFI 4
+#define UISUBCLASS_SFF8070I 5
+#define UISUBCLASS_SCSI 6
+#define UIPROTO_MASS_CBI_I 0
+#define UIPROTO_MASS_CBI 1
+#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */
+#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */
+
+#define UICLASS_HUB 0x09
+#define UISUBCLASS_HUB 0
+#define UIPROTO_FSHUB 0
+#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */
+#define UIPROTO_HSHUBMTT 1
+
+#define UICLASS_CDC_DATA 0x0a
+#define UISUBCLASS_DATA 0
+#define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */
+#define UIPROTO_DATA_HDLC 0x31 /* HDLC */
+#define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */
+#define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */
+#define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */
+#define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */
+#define UIPROTO_DATA_V42BIS 0x90 /* Data compression */
+#define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */
+#define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */
+#define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */
+#define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */
+#define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc.*/
+#define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */
+
+#define UICLASS_SMARTCARD 0x0b
+
+/*#define UICLASS_FIRM_UPD 0x0c*/
+
+#define UICLASS_SECURITY 0x0d
+
+#define UICLASS_DIAGNOSTIC 0xdc
+
+#define UICLASS_WIRELESS 0xe0
+#define UISUBCLASS_RF 0x01
+#define UIPROTO_BLUETOOTH 0x01
+
+#define UICLASS_APPL_SPEC 0xfe
+#define UISUBCLASS_FIRMWARE_DOWNLOAD 1
+#define UISUBCLASS_IRDA 2
+#define UIPROTO_IRDA 0
+
+#define UICLASS_VENDOR 0xff
+
+
+#define USB_HUB_MAX_DEPTH 5
+
+/*
+ * Minimum time a device needs to be powered down to go through
+ * a power cycle. XXX Are these time in the spec?
+ */
+#define USB_POWER_DOWN_TIME 200 /* ms */
+#define USB_PORT_POWER_DOWN_TIME 100 /* ms */
+
+#if 0
+/* These are the values from the spec. */
+#define USB_PORT_RESET_DELAY 10 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */
+#define USB_PORT_RESET_RECOVERY 10 /* ms */
+#define USB_PORT_POWERUP_DELAY 100 /* ms */
+#define USB_SET_ADDRESS_SETTLE 2 /* ms */
+#define USB_RESUME_DELAY (20*5) /* ms */
+#define USB_RESUME_WAIT 10 /* ms */
+#define USB_RESUME_RECOVERY 10 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 0 /* ms */
+#else
+/* Allow for marginal (i.e. non-conforming) devices. */
+#define USB_PORT_RESET_DELAY 50 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */
+#define USB_PORT_RESET_RECOVERY 250 /* ms */
+#define USB_PORT_POWERUP_DELAY 300 /* ms */
+#define USB_SET_ADDRESS_SETTLE 10 /* ms */
+#define USB_RESUME_DELAY (50*5) /* ms */
+#define USB_RESUME_WAIT 50 /* ms */
+#define USB_RESUME_RECOVERY 50 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 20 /* ms */
+#endif
+
+#define USB_MIN_POWER 100 /* mA */
+#define USB_MAX_POWER 500 /* mA */
+
+#define USB_BUS_RESET_DELAY 100 /* ms XXX?*/
+
+
+#define USB_UNCONFIG_NO 0
+#define USB_UNCONFIG_INDEX (-1)
+
+/*** ioctl() related stuff ***/
+
+struct usb_ctl_request {
+ int ucr_addr;
+ usb_device_request_t ucr_request;
+ void *ucr_data;
+ int ucr_flags;
+#define USBD_SHORT_XFER_OK 0x04 /* allow short reads */
+ int ucr_actlen; /* actual length transferred */
+};
+
+struct usb_alt_interface {
+ int uai_config_index;
+ int uai_interface_index;
+ int uai_alt_no;
+};
+
+#define USB_CURRENT_CONFIG_INDEX (-1)
+#define USB_CURRENT_ALT_INDEX (-1)
+
+struct usb_config_desc {
+ int ucd_config_index;
+ usb_config_descriptor_t ucd_desc;
+};
+
+struct usb_interface_desc {
+ int uid_config_index;
+ int uid_interface_index;
+ int uid_alt_index;
+ usb_interface_descriptor_t uid_desc;
+};
+
+struct usb_endpoint_desc {
+ int ued_config_index;
+ int ued_interface_index;
+ int ued_alt_index;
+ int ued_endpoint_index;
+ usb_endpoint_descriptor_t ued_desc;
+};
+
+struct usb_full_desc {
+ int ufd_config_index;
+ u_int ufd_size;
+ u_char *ufd_data;
+};
+
+struct usb_string_desc {
+ int usd_string_index;
+ int usd_language_id;
+ usb_string_descriptor_t usd_desc;
+};
+
+struct usb_ctl_report_desc {
+ int ucrd_size;
+ u_char ucrd_data[1024]; /* filled data size will vary */
+};
+
+typedef struct { u_int32_t cookie; } usb_event_cookie_t;
+
+#define USB_MAX_DEVNAMES 4
+#define USB_MAX_DEVNAMELEN 16
+struct usb_device_info {
+ u_int8_t udi_bus;
+ u_int8_t udi_addr; /* device address */
+ usb_event_cookie_t udi_cookie;
+ char udi_product[USB_MAX_STRING_LEN];
+ char udi_vendor[USB_MAX_STRING_LEN];
+ char udi_release[8];
+ u_int16_t udi_productNo;
+ u_int16_t udi_vendorNo;
+ u_int16_t udi_releaseNo;
+ u_int8_t udi_class;
+ u_int8_t udi_subclass;
+ u_int8_t udi_protocol;
+ u_int8_t udi_config;
+ u_int8_t udi_speed;
+#define USB_SPEED_LOW 1
+#define USB_SPEED_FULL 2
+#define USB_SPEED_HIGH 3
+ int udi_power; /* power consumption in mA, 0 if selfpowered */
+ int udi_nports;
+ char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN];
+ u_int8_t udi_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 ucr_report;
+ u_char ucr_data[1024]; /* filled data size will vary */
+};
+
+struct usb_device_stats {
+ u_long uds_requests[4]; /* indexed by transfer type UE_* */
+};
+
+/* Events that can be read from /dev/usb */
+struct usb_event {
+ int ue_type;
+#define USB_EVENT_CTRLR_ATTACH 1
+#define USB_EVENT_CTRLR_DETACH 2
+#define USB_EVENT_DEVICE_ATTACH 3
+#define USB_EVENT_DEVICE_DETACH 4
+#define USB_EVENT_DRIVER_ATTACH 5
+#define USB_EVENT_DRIVER_DETACH 6
+#define USB_EVENT_IS_ATTACH(n) ((n) == USB_EVENT_CTRLR_ATTACH || (n) == USB_EVENT_DEVICE_ATTACH || (n) == USB_EVENT_DRIVER_ATTACH)
+#define USB_EVENT_IS_DETACH(n) ((n) == USB_EVENT_CTRLR_DETACH || (n) == USB_EVENT_DEVICE_DETACH || (n) == USB_EVENT_DRIVER_DETACH)
+ struct timespec ue_time;
+ union {
+ struct {
+ int ue_bus;
+ } ue_ctrlr;
+ struct usb_device_info ue_device;
+ struct {
+ usb_event_cookie_t ue_cookie;
+ char ue_devname[16];
+ } ue_driver;
+ } u;
+};
+
+/* 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)
+#define USB_SET_REPORT _IOW ('U', 24, struct usb_ctl_report)
+#define USB_GET_REPORT_ID _IOR ('U', 25, int)
+
+/* Generic USB device */
+#define USB_GET_CONFIG _IOR ('U', 100, int)
+#define USB_SET_CONFIG _IOW ('U', 101, int)
+#define USB_GET_ALTINTERFACE _IOWR('U', 102, struct usb_alt_interface)
+#define USB_SET_ALTINTERFACE _IOWR('U', 103, struct usb_alt_interface)
+#define USB_GET_NO_ALT _IOWR('U', 104, struct usb_alt_interface)
+#define USB_GET_DEVICE_DESC _IOR ('U', 105, usb_device_descriptor_t)
+#define USB_GET_CONFIG_DESC _IOWR('U', 106, struct usb_config_desc)
+#define USB_GET_INTERFACE_DESC _IOWR('U', 107, struct usb_interface_desc)
+#define USB_GET_ENDPOINT_DESC _IOWR('U', 108, struct usb_endpoint_desc)
+#define USB_GET_FULL_DESC _IOWR('U', 109, struct usb_full_desc)
+#define USB_GET_STRING_DESC _IOWR('U', 110, struct usb_string_desc)
+#define USB_DO_REQUEST _IOWR('U', 111, struct usb_ctl_request)
+#define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb_device_info)
+#define USB_SET_SHORT_XFER _IOW ('U', 113, int)
+#define USB_SET_TIMEOUT _IOW ('U', 114, int)
+
+/* Modem device */
+#define USB_GET_CM_OVER_DATA _IOR ('U', 130, int)
+#define USB_SET_CM_OVER_DATA _IOW ('U', 131, int)
+
+#endif /* _USB_H_ */
diff --git a/sys/dev/usb/usb_ethersubr.c b/sys/dev/usb/usb_ethersubr.c
new file mode 100644
index 0000000..325f3db
--- /dev/null
+++ b/sys/dev/usb/usb_ethersubr.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Callbacks in the USB code operate at splusb() (actually splbio()
+ * in FreeBSD). However adding packets to the input queues has to be
+ * done at splimp(). It is conceivable that this arrangement could
+ * trigger a condition where the splimp() is ignored and the input
+ * queues could get trampled in spite of our best effors to prevent
+ * it. To work around this, we implement a special input queue for
+ * USB ethernet adapter drivers. Rather than passing the frames directly
+ * to ether_input(), we pass them here, then schedule a soft interrupt
+ * to hand them to ether_input() later, outside of the USB interrupt
+ * context.
+ *
+ * It's questional as to whether this code should be expanded to
+ * handle other kinds of devices, or handle USB transfer callbacks
+ * in general. Right now, I need USB network interfaces to work
+ * properly.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/netisr.h>
+#include <net/bpf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ethersubr.h>
+
+Static struct ifqueue usbq_rx;
+Static struct ifqueue usbq_tx;
+Static int mtx_inited = 0;
+
+Static void usbintr (void);
+
+Static void usbintr(void)
+{
+ struct mbuf *m;
+ struct usb_qdat *q;
+ struct ifnet *ifp;
+
+ /* Check the RX queue */
+ while(1) {
+ IF_DEQUEUE(&usbq_rx, m);
+ if (m == NULL)
+ break;
+ q = (struct usb_qdat *)m->m_pkthdr.rcvif;
+ ifp = q->ifp;
+ m->m_pkthdr.rcvif = ifp;
+ (*ifp->if_input)(ifp, m);
+
+ /* Re-arm the receiver */
+ (*q->if_rxstart)(ifp);
+ if (ifp->if_snd.ifq_head != NULL)
+ (*ifp->if_start)(ifp);
+ }
+
+ /* Check the TX queue */
+ while(1) {
+ IF_DEQUEUE(&usbq_tx, m);
+ if (m == NULL)
+ break;
+ ifp = m->m_pkthdr.rcvif;
+ m_freem(m);
+ if (ifp->if_snd.ifq_head != NULL)
+ (*ifp->if_start)(ifp);
+ }
+
+ return;
+}
+
+void usb_register_netisr()
+{
+ if (mtx_inited)
+ return;
+ netisr_register(NETISR_USB, (netisr_t *)usbintr, NULL, 0);
+ mtx_init(&usbq_tx.ifq_mtx, "usbq_tx_mtx", NULL, MTX_DEF);
+ mtx_init(&usbq_rx.ifq_mtx, "usbq_rx_mtx", NULL, MTX_DEF);
+ mtx_inited++;
+ return;
+}
+
+/*
+ * Must be called at splusb() (actually splbio()). This should be
+ * the case when called from a transfer callback routine.
+ */
+void usb_ether_input(m)
+ struct mbuf *m;
+{
+ IF_ENQUEUE(&usbq_rx, m);
+ schednetisr(NETISR_USB);
+
+ return;
+}
+
+void usb_tx_done(m)
+ struct mbuf *m;
+{
+ IF_ENQUEUE(&usbq_tx, m);
+ schednetisr(NETISR_USB);
+
+ return;
+}
diff --git a/sys/dev/usb/usb_ethersubr.h b/sys/dev/usb/usb_ethersubr.h
new file mode 100644
index 0000000..c8a4010
--- /dev/null
+++ b/sys/dev/usb/usb_ethersubr.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _USB_ETHERSUBR_H_
+#define _USB_ETHERSUBR_H_
+
+struct usb_qdat {
+ struct ifnet *ifp;
+ void (*if_rxstart) (struct ifnet *);
+};
+
+void usb_register_netisr (void);
+void usb_ether_input (struct mbuf *);
+void usb_tx_done (struct mbuf *);
+
+#endif
diff --git a/sys/dev/usb/usb_if.m b/sys/dev/usb/usb_if.m
new file mode 100644
index 0000000..c1b27c8
--- /dev/null
+++ b/sys/dev/usb/usb_if.m
@@ -0,0 +1,42 @@
+#
+# Copyright (c) 1992-1998 Nick Hibma <n_hibma@freebsd.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer,
+# without modification, immediately at the beginning of the file.
+# 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. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+# USB interface description
+#
+
+#include <sys/bus.h>
+
+INTERFACE usb;
+
+# The device should start probing for new drivers again
+#
+METHOD int reconfigure {
+ device_t dev;
+};
diff --git a/sys/dev/usb/usb_mem.c b/sys/dev/usb/usb_mem.c
new file mode 100644
index 0000000..73b309d
--- /dev/null
+++ b/sys/dev/usb/usb_mem.c
@@ -0,0 +1,308 @@
+/* $NetBSD: usb_mem.c,v 1.26 2003/02/01 06:23:40 thorpej Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 DMA memory allocation.
+ * We need to allocate a lot of small (many 8 byte, some larger)
+ * memory blocks that can be used for DMA. Using the bus_dma
+ * routines directly would incur large overheads in space and time.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h> /* for usbdivar.h */
+#include <machine/bus.h>
+#elif defined(__FreeBSD__)
+#include <sys/endian.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/queue.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+
+#ifdef DIAGNOSTIC
+#include <sys/proc.h>
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h> /* just for usb_dma_t */
+#include <dev/usb/usb_mem.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) logprintf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define USB_MEM_SMALL 64
+#define USB_MEM_CHUNKS (PAGE_SIZE / USB_MEM_SMALL)
+#define USB_MEM_BLOCK (USB_MEM_SMALL * USB_MEM_CHUNKS)
+
+/* This struct is overlayed on free fragments. */
+struct usb_frag_dma {
+ usb_dma_block_t *block;
+ u_int offs;
+ LIST_ENTRY(usb_frag_dma) next;
+};
+
+Static bus_dmamap_callback_t usbmem_callback;
+Static usbd_status usb_block_allocmem(bus_dma_tag_t, size_t, size_t,
+ usb_dma_block_t **);
+Static void usb_block_freemem(usb_dma_block_t *);
+
+Static LIST_HEAD(, usb_dma_block) usb_blk_freelist =
+ LIST_HEAD_INITIALIZER(usb_blk_freelist);
+Static int usb_blk_nfree = 0;
+/* XXX should have different free list for different tags (for speed) */
+Static LIST_HEAD(, usb_frag_dma) usb_frag_freelist =
+ LIST_HEAD_INITIALIZER(usb_frag_freelist);
+
+Static void
+usbmem_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ int i;
+ usb_dma_block_t *p = arg;
+
+ if (error == EFBIG) {
+ printf("usb: mapping to large\n");
+ return;
+ }
+
+ p->nsegs = nseg;
+ for (i = 0; i < nseg && i < sizeof p->segs / sizeof *p->segs; i++)
+ p->segs[i] = segs[i];
+}
+
+Static usbd_status
+usb_block_allocmem(bus_dma_tag_t tag, size_t size, size_t align,
+ usb_dma_block_t **dmap)
+{
+ usb_dma_block_t *p;
+ int s;
+
+ DPRINTFN(5, ("usb_block_allocmem: size=%lu align=%lu\n",
+ (u_long)size, (u_long)align));
+
+#ifdef DIAGNOSTIC
+ if (!curproc) {
+ printf("usb_block_allocmem: in interrupt context, size=%lu\n",
+ (unsigned long) size);
+ }
+#endif
+
+ s = splusb();
+ /* First check the free list. */
+ for (p = LIST_FIRST(&usb_blk_freelist); p; p = LIST_NEXT(p, next)) {
+ if (p->tag == tag && p->size >= size && p->align >= align) {
+ LIST_REMOVE(p, next);
+ usb_blk_nfree--;
+ splx(s);
+ *dmap = p;
+ DPRINTFN(6,("usb_block_allocmem: free list size=%lu\n",
+ (u_long)p->size));
+ return (USBD_NORMAL_COMPLETION);
+ }
+ }
+ splx(s);
+
+#ifdef DIAGNOSTIC
+ if (!curproc) {
+ printf("usb_block_allocmem: in interrupt context, failed\n");
+ return (USBD_NOMEM);
+ }
+#endif
+
+ DPRINTFN(6, ("usb_block_allocmem: no free\n"));
+ p = malloc(sizeof *p, M_USB, M_NOWAIT);
+ if (p == NULL)
+ return (USBD_NOMEM);
+
+#if __FreeBSD_version >= 500000
+ if (bus_dma_tag_create(tag, align, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ size, sizeof(p->segs) / sizeof(p->segs[0]), size,
+ BUS_DMA_ALLOCNOW, NULL, NULL, &p->tag) == ENOMEM)
+#else
+ if (bus_dma_tag_create(tag, align, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ size, sizeof(p->segs) / sizeof(p->segs[0]), size,
+ BUS_DMA_ALLOCNOW, &p->tag) == ENOMEM)
+#endif
+ {
+ goto free;
+ }
+
+ p->size = size;
+ p->align = align;
+ if (bus_dmamem_alloc(p->tag, &p->kaddr,
+ BUS_DMA_NOWAIT|BUS_DMA_COHERENT, &p->map))
+ goto tagfree;
+
+ if (bus_dmamap_load(p->tag, p->map, p->kaddr, p->size,
+ usbmem_callback, p, 0))
+ goto memfree;
+
+ /* XXX - override the tag, ok since we never free it */
+ p->tag = tag;
+ *dmap = p;
+ return (USBD_NORMAL_COMPLETION);
+
+ /*
+ * XXX - do we need to _unload? is the order of _free and _destroy
+ * correct?
+ */
+memfree:
+ bus_dmamem_free(p->tag, p->kaddr, p->map);
+tagfree:
+ bus_dma_tag_destroy(p->tag);
+free:
+ free(p, M_USB);
+ return (USBD_NOMEM);
+}
+
+/*
+ * Do not free the memory unconditionally since we might be called
+ * from an interrupt context and that is BAD.
+ * XXX when should we really free?
+ */
+Static void
+usb_block_freemem(usb_dma_block_t *p)
+{
+ int s;
+
+ DPRINTFN(6, ("usb_block_freemem: size=%lu\n", (u_long)p->size));
+ s = splusb();
+ LIST_INSERT_HEAD(&usb_blk_freelist, p, next);
+ usb_blk_nfree++;
+ splx(s);
+}
+
+usbd_status
+usb_allocmem(usbd_bus_handle bus, size_t size, size_t align, usb_dma_t *p)
+{
+ bus_dma_tag_t tag = bus->dmatag;
+ usbd_status err;
+ struct usb_frag_dma *f;
+ usb_dma_block_t *b;
+ int i;
+ int s;
+
+ /* compat w/ Net/OpenBSD */
+ if (align == 0)
+ align = 1;
+
+ /* If the request is large then just use a full block. */
+ if (size > USB_MEM_SMALL || align > USB_MEM_SMALL) {
+ DPRINTFN(1, ("usb_allocmem: large alloc %d\n", (int)size));
+ size = (size + USB_MEM_BLOCK - 1) & ~(USB_MEM_BLOCK - 1);
+ err = usb_block_allocmem(tag, size, align, &p->block);
+ if (!err) {
+ p->block->fullblock = 1;
+ p->offs = 0;
+ p->len = size;
+ }
+ return (err);
+ }
+
+ s = splusb();
+ /* Check for free fragments. */
+ for (f = LIST_FIRST(&usb_frag_freelist); f; f = LIST_NEXT(f, next))
+ if (f->block->tag == tag)
+ break;
+ if (f == NULL) {
+ DPRINTFN(1, ("usb_allocmem: adding fragments\n"));
+ err = usb_block_allocmem(tag, USB_MEM_BLOCK, USB_MEM_SMALL,&b);
+ if (err) {
+ splx(s);
+ return (err);
+ }
+ b->fullblock = 0;
+ /* XXX - override the tag, ok since we never free it */
+ b->tag = tag;
+ KASSERT(sizeof *f <= USB_MEM_SMALL, ("USB_MEM_SMALL(%d) is too small for struct usb_frag_dma(%zd)\n",
+ USB_MEM_SMALL, sizeof *f));
+ for (i = 0; i < USB_MEM_BLOCK; i += USB_MEM_SMALL) {
+ f = (struct usb_frag_dma *)((char *)b->kaddr + i);
+ f->block = b;
+ f->offs = i;
+ LIST_INSERT_HEAD(&usb_frag_freelist, f, next);
+ }
+ f = LIST_FIRST(&usb_frag_freelist);
+ }
+ p->block = f->block;
+ p->offs = f->offs;
+ p->len = USB_MEM_SMALL;
+ LIST_REMOVE(f, next);
+ splx(s);
+ DPRINTFN(5, ("usb_allocmem: use frag=%p size=%d\n", f, (int)size));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usb_freemem(usbd_bus_handle bus, usb_dma_t *p)
+{
+ struct usb_frag_dma *f;
+ int s;
+
+ if (p->block->fullblock) {
+ DPRINTFN(1, ("usb_freemem: large free\n"));
+ usb_block_freemem(p->block);
+ return;
+ }
+ f = KERNADDR(p, 0);
+ f->block = p->block;
+ f->offs = p->offs;
+ s = splusb();
+ LIST_INSERT_HEAD(&usb_frag_freelist, f, next);
+ splx(s);
+ DPRINTFN(5, ("usb_freemem: frag=%p\n", f));
+}
diff --git a/sys/dev/usb/usb_mem.h b/sys/dev/usb/usb_mem.h
new file mode 100644
index 0000000..bba88d5
--- /dev/null
+++ b/sys/dev/usb/usb_mem.h
@@ -0,0 +1,66 @@
+/* $NetBSD: usb_mem.h,v 1.18 2002/05/28 17:45:17 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 usb_dma_block {
+ bus_dma_tag_t tag;
+ bus_dmamap_t map;
+#ifdef __FreeBSD__
+ void *kaddr;
+#else
+ caddr_t kaddr;
+#endif
+ bus_dma_segment_t segs[1];
+ int nsegs;
+ size_t size;
+ size_t align;
+ int fullblock;
+ LIST_ENTRY(usb_dma_block) next;
+} usb_dma_block_t;
+
+#ifdef __FreeBSD__
+#define DMAADDR(dma, o) ((dma)->block->segs[0].ds_addr + (dma)->offs + (o))
+#else
+#define DMAADDR(dma, o) (((char *)(dma)->block->map->dm_segs[0].ds_addr) + (dma)->offs + (o))
+#endif
+#define KERNADDR(dma, o) \
+ ((void *)((char *)((dma)->block->kaddr) + (dma)->offs + (o)))
+
+usbd_status usb_allocmem(usbd_bus_handle,size_t,size_t, usb_dma_t *);
+void usb_freemem(usbd_bus_handle, usb_dma_t *);
diff --git a/sys/dev/usb/usb_port.h b/sys/dev/usb/usb_port.h
new file mode 100644
index 0000000..bb9addc
--- /dev/null
+++ b/sys/dev/usb/usb_port.h
@@ -0,0 +1,520 @@
+/* $OpenBSD: usb_port.h,v 1.18 2000/09/06 22:42:10 rahnds Exp $ */
+/* $NetBSD: usb_port.h,v 1.54 2002/03/28 21:49:19 ichiro Exp $ */
+/* $FreeBSD$ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: usb_port.h,v 1.57 2002/09/27 20:42:01 thorpej Exp $
+ * $NetBSD: usb_port.h,v 1.58 2002/10/01 01:25:26 thorpej Exp $
+ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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_PORT_H
+#define _USB_PORT_H
+
+/*
+ * Macro's to cope with the differences between operating systems.
+ */
+
+#if defined(__NetBSD__)
+/*
+ * NetBSD
+ */
+
+#include "opt_usbverbose.h"
+
+#define USB_USE_SOFTINTR
+
+#ifdef USB_DEBUG
+#define Static
+#else
+#define Static static
+#endif
+
+#define SCSI_MODE_SENSE MODE_SENSE
+
+typedef struct proc *usb_proc_ptr;
+
+typedef struct device *device_ptr_t;
+#define USBBASEDEVICE struct device
+#define USBDEV(bdev) (&(bdev))
+#define USBDEVNAME(bdev) ((bdev).dv_xname)
+#define USBDEVUNIT(bdev) ((bdev).dv_unit)
+#define USBDEVPTRNAME(bdevptr) ((bdevptr)->dv_xname)
+#define USBGETSOFTC(d) ((void *)(d))
+
+#define DECLARE_USB_DMA_T \
+ struct usb_dma_block; \
+ typedef struct { \
+ struct usb_dma_block *block; \
+ u_int offs; \
+ } usb_dma_t
+
+typedef struct callout usb_callout_t;
+#define usb_callout_init(h) callout_init(&(h))
+#define usb_callout(h, t, f, d) callout_reset(&(h), (t), (f), (d))
+#define usb_uncallout(h, f, d) callout_stop(&(h))
+
+#define usb_kthread_create1 kthread_create1
+#define usb_kthread_create kthread_create
+
+typedef int usb_malloc_type;
+
+#define Ether_ifattach ether_ifattach
+#define IF_INPUT(ifp, m) (*(ifp)->if_input)((ifp), (m))
+
+#define logprintf printf
+
+#define USB_DNAME(dname) dname
+#define USB_DECLARE_DRIVER(dname) \
+int __CONCAT(dname,_match)(struct device *, struct cfdata *, void *); \
+void __CONCAT(dname,_attach)(struct device *, struct device *, void *); \
+int __CONCAT(dname,_detach)(struct device *, int); \
+int __CONCAT(dname,_activate)(struct device *, enum devact); \
+\
+extern struct cfdriver __CONCAT(dname,_cd); \
+\
+CFATTACH_DECL(USB_DNAME(dname), \
+ sizeof(struct ___CONCAT(dname,_softc)), \
+ ___CONCAT(dname,_match), \
+ ___CONCAT(dname,_attach), \
+ ___CONCAT(dname,_detach), \
+ ___CONCAT(dname,_activate))
+
+#define USB_MATCH(dname) \
+int __CONCAT(dname,_match)(struct device *parent, struct cfdata *match, void *aux)
+
+#define USB_MATCH_START(dname, uaa) \
+ struct usb_attach_arg *uaa = aux
+
+#define USB_ATTACH(dname) \
+void __CONCAT(dname,_attach)(struct device *parent, struct device *self, void *aux)
+
+#define USB_ATTACH_START(dname, sc, uaa) \
+ struct __CONCAT(dname,_softc) *sc = \
+ (struct __CONCAT(dname,_softc) *)self; \
+ struct usb_attach_arg *uaa = aux
+
+/* Returns from attach */
+#define USB_ATTACH_ERROR_RETURN return
+#define USB_ATTACH_SUCCESS_RETURN return
+
+#define USB_ATTACH_SETUP printf("\n")
+
+#define USB_DETACH(dname) \
+int __CONCAT(dname,_detach)(struct device *self, int flags)
+
+#define USB_DETACH_START(dname, sc) \
+ struct __CONCAT(dname,_softc) *sc = \
+ (struct __CONCAT(dname,_softc) *)self
+
+#define USB_GET_SC_OPEN(dname, unit, sc) \
+ if (unit >= __CONCAT(dname,_cd).cd_ndevs) \
+ return (ENXIO); \
+ sc = __CONCAT(dname,_cd).cd_devs[unit]; \
+ if (sc == NULL) \
+ return (ENXIO)
+
+#define USB_GET_SC(dname, unit, sc) \
+ sc = __CONCAT(dname,_cd).cd_devs[unit]
+
+#define USB_DO_ATTACH(dev, bdev, parent, args, print, sub) \
+ (config_found_sm(parent, args, print, sub))
+
+#elif defined(__OpenBSD__)
+/*
+ * OpenBSD
+ */
+#define Static
+
+typedef struct proc *usb_proc_ptr;
+
+#define UCOMBUSCF_PORTNO -1
+#define UCOMBUSCF_PORTNO_DEFAULT -1
+
+#define SCSI_MODE_SENSE MODE_SENSE
+#define XS_STS_DONE ITSDONE
+#define XS_CTL_POLL SCSI_POLL
+#define XS_CTL_DATA_IN SCSI_DATA_IN
+#define XS_CTL_DATA_OUT SCSI_DATA_OUT
+#define scsipi_adapter scsi_adapter
+#define scsipi_cmd scsi_cmd
+#define scsipi_device scsi_device
+#define scsipi_done scsi_done
+#define scsipi_link scsi_link
+#define scsipi_minphys scsi_minphys
+#define scsipi_sense scsi_sense
+#define scsipi_xfer scsi_xfer
+#define xs_control flags
+#define xs_status status
+
+#define memcpy(d, s, l) bcopy((s),(d),(l))
+#define memset(d, v, l) bzero((d),(l))
+#define bswap32(x) swap32(x)
+#define bswap16(x) swap16(x)
+
+/*
+ * The UHCI/OHCI controllers are little endian, so on big endian machines
+ * the data strored in memory needs to be swapped.
+ */
+
+#if defined(letoh32)
+#define le32toh(x) letoh32(x)
+#define le16toh(x) letoh16(x)
+#endif
+
+#define usb_kthread_create1 kthread_create
+#define usb_kthread_create kthread_create_deferred
+
+#define config_pending_incr()
+#define config_pending_decr()
+
+typedef int usb_malloc_type;
+
+#define Ether_ifattach(ifp, eaddr) ether_ifattach(ifp)
+#define if_deactivate(x)
+#define IF_INPUT(ifp, m) do { \
+ struct ether_header *eh; \
+ \
+ eh = mtod(m, struct ether_header *); \
+ m_adj(m, sizeof(struct ether_header)); \
+ ether_input((ifp), (eh), (m)); \
+} while (0)
+
+#define usbpoll usbselect
+#define uhidpoll uhidselect
+#define ugenpoll ugenselect
+#define uriopoll urioselect
+#define uscannerpoll uscannerselect
+
+#define powerhook_establish(fn, sc) (fn)
+#define powerhook_disestablish(hdl)
+#define PWR_RESUME 0
+
+#define logprintf printf
+
+#define swap_bytes_change_sign16_le swap_bytes_change_sign16
+#define change_sign16_swap_bytes_le change_sign16_swap_bytes
+#define change_sign16_le change_sign16
+
+#define realloc usb_realloc
+void *usb_realloc(void *, u_int, int, int);
+
+extern int cold;
+
+typedef struct device *device_ptr_t;
+#define USBBASEDEVICE struct device
+#define USBDEV(bdev) (&(bdev))
+#define USBDEVNAME(bdev) ((bdev).dv_xname)
+#define USBDEVUNIT(bdev) ((bdev).dv_unit)
+#define USBDEVPTRNAME(bdevptr) ((bdevptr)->dv_xname)
+#define USBGETSOFTC(d) ((void *)(d))
+
+#define DECLARE_USB_DMA_T \
+ struct usb_dma_block; \
+ typedef struct { \
+ struct usb_dma_block *block; \
+ u_int offs; \
+ } usb_dma_t
+
+typedef char usb_callout_t;
+#define usb_callout_init(h)
+#define usb_callout(h, t, f, d) timeout((f), (d), (t))
+#define usb_uncallout(h, f, d) untimeout((f), (d))
+
+#define USB_DECLARE_DRIVER(dname) \
+int __CONCAT(dname,_match)(struct device *, void *, void *); \
+void __CONCAT(dname,_attach)(struct device *, struct device *, void *); \
+int __CONCAT(dname,_detach)(struct device *, int); \
+int __CONCAT(dname,_activate)(struct device *, enum devact); \
+\
+struct cfdriver __CONCAT(dname,_cd) = { \
+ NULL, #dname, DV_DULL \
+}; \
+\
+const struct cfattach __CONCAT(dname,_ca) = { \
+ sizeof(struct __CONCAT(dname,_softc)), \
+ __CONCAT(dname,_match), \
+ __CONCAT(dname,_attach), \
+ __CONCAT(dname,_detach), \
+ __CONCAT(dname,_activate), \
+}
+
+#define USB_MATCH(dname) \
+int \
+__CONCAT(dname,_match)(parent, match, aux) \
+ struct device *parent; \
+ void *match; \
+ void *aux;
+
+#define USB_MATCH_START(dname, uaa) \
+ struct usb_attach_arg *uaa = aux
+
+#define USB_ATTACH(dname) \
+void \
+__CONCAT(dname,_attach)(parent, self, aux) \
+ struct device *parent; \
+ struct device *self; \
+ void *aux;
+
+#define USB_ATTACH_START(dname, sc, uaa) \
+ struct __CONCAT(dname,_softc) *sc = \
+ (struct __CONCAT(dname,_softc) *)self; \
+ struct usb_attach_arg *uaa = aux
+
+/* Returns from attach */
+#define USB_ATTACH_ERROR_RETURN return
+#define USB_ATTACH_SUCCESS_RETURN return
+
+#define USB_ATTACH_SETUP printf("\n")
+
+#define USB_DETACH(dname) \
+int \
+__CONCAT(dname,_detach)(self, flags) \
+ struct device *self; \
+ int flags;
+
+#define USB_DETACH_START(dname, sc) \
+ struct __CONCAT(dname,_softc) *sc = \
+ (struct __CONCAT(dname,_softc) *)self
+
+#define USB_GET_SC_OPEN(dname, unit, sc) \
+ if (unit >= __CONCAT(dname,_cd).cd_ndevs) \
+ return (ENXIO); \
+ sc = __CONCAT(dname,_cd).cd_devs[unit]; \
+ if (sc == NULL) \
+ return (ENXIO)
+
+#define USB_GET_SC(dname, unit, sc) \
+ sc = __CONCAT(dname,_cd).cd_devs[unit]
+
+#define USB_DO_ATTACH(dev, bdev, parent, args, print, sub) \
+ (config_found_sm(parent, args, print, sub))
+
+#elif defined(__FreeBSD__)
+/*
+ * FreeBSD
+ */
+
+#include "opt_usb.h"
+
+#if defined(_KERNEL)
+#include <sys/malloc.h>
+
+MALLOC_DECLARE(M_USB);
+MALLOC_DECLARE(M_USBDEV);
+MALLOC_DECLARE(M_USBHC);
+
+#endif
+
+#define USBVERBOSE
+
+/* We don't use the soft interrupt code in FreeBSD. */
+#if 0
+#define USB_USE_SOFTINTR
+#endif
+
+#define Static static
+
+#define device_ptr_t device_t
+#define USBBASEDEVICE device_t
+#define USBDEV(bdev) (bdev)
+#define USBDEVNAME(bdev) device_get_nameunit(bdev)
+#define USBDEVUNIT(bdev) device_get_unit(bdev)
+#define USBDEVPTRNAME(bdev) device_get_nameunit(bdev)
+#define USBDEVUNIT(bdev) device_get_unit(bdev)
+#define USBGETSOFTC(bdev) (device_get_softc(bdev))
+
+#define DECLARE_USB_DMA_T \
+ struct usb_dma_block; \
+ typedef struct { \
+ struct usb_dma_block *block; \
+ u_int offs; \
+ u_int len; \
+ } usb_dma_t
+
+#if __FreeBSD_version >= 500000
+typedef struct thread *usb_proc_ptr;
+
+#define uio_procp uio_td
+
+#define usb_kthread_create1(f, s, p, a0, a1) \
+ kthread_create((f), (s), (p), RFHIGHPID, 0, (a0), (a1))
+#define usb_kthread_create2(f, s, p, a0) \
+ kthread_create((f), (s), (p), RFHIGHPID, 0, (a0))
+#define usb_kthread_create kthread_create
+
+#define config_pending_incr()
+#define config_pending_decr()
+
+typedef struct callout usb_callout_t;
+#define usb_callout_init(h) callout_init(&(h), 0)
+#define usb_callout(h, t, f, d) callout_reset(&(h), (t), (f), (d))
+#define usb_uncallout(h, f, d) callout_stop(&(h))
+#else
+typedef struct proc *usb_proc_ptr;
+
+#define PROC_LOCK(p)
+#define PROC_UNLOCK(p)
+
+#define usb_kthread_create1(f, s, p, a0, a1) \
+ kthread_create((f), (s), (p), (a0), (a1))
+#define usb_kthread_create2(f, s, p, a0) \
+ kthread_create((f), (s), (p), (a0))
+#define usb_kthread_create kthread_create
+
+#define config_pending_incr()
+#define config_pending_decr()
+
+typedef struct callout usb_callout_t;
+#define usb_callout_init(h) callout_init(&(h))
+#define usb_callout(h, t, f, d) callout_reset(&(h), (t), (f), (d))
+#define usb_uncallout(h, f, d) callout_stop(&(h))
+
+#define BUS_DMA_COHERENT 0
+#define ETHER_ALIGN 2
+#define BPF_MTAP(ifp, m) if ((ifp)->if_bpf) bpf_mtap((ifp), (m));
+#endif
+
+#define clalloc(p, s, x) (clist_alloc_cblocks((p), (s), (s)), 0)
+#define clfree(p) clist_free_cblocks((p))
+
+#define PWR_RESUME 0
+#define PWR_SUSPEND 1
+
+#define config_detach(dev, flag) device_delete_child(device_get_parent(dev), dev)
+
+typedef struct malloc_type *usb_malloc_type;
+
+#define USB_DECLARE_DRIVER_INIT(dname, init...) \
+Static device_probe_t __CONCAT(dname,_match); \
+Static device_attach_t __CONCAT(dname,_attach); \
+Static device_detach_t __CONCAT(dname,_detach); \
+\
+Static devclass_t __CONCAT(dname,_devclass); \
+\
+Static device_method_t __CONCAT(dname,_methods)[] = { \
+ DEVMETHOD(device_probe, __CONCAT(dname,_match)), \
+ DEVMETHOD(device_attach, __CONCAT(dname,_attach)), \
+ DEVMETHOD(device_detach, __CONCAT(dname,_detach)), \
+ init, \
+ {0,0} \
+}; \
+\
+Static driver_t __CONCAT(dname,_driver) = { \
+ #dname, \
+ __CONCAT(dname,_methods), \
+ sizeof(struct __CONCAT(dname,_softc)) \
+}; \
+MODULE_DEPEND(dname, usb, 1, 1, 1)
+
+
+#define METHODS_NONE {0,0}
+#define USB_DECLARE_DRIVER(dname) USB_DECLARE_DRIVER_INIT(dname, METHODS_NONE)
+
+#define USB_MATCH(dname) \
+Static int \
+__CONCAT(dname,_match)(device_t self)
+
+#define USB_MATCH_START(dname, uaa) \
+ struct usb_attach_arg *uaa = device_get_ivars(self)
+
+#define USB_MATCH_SETUP \
+ sc->sc_dev = self
+
+#define USB_ATTACH(dname) \
+Static int \
+__CONCAT(dname,_attach)(device_t self)
+
+#define USB_ATTACH_START(dname, sc, uaa) \
+ struct __CONCAT(dname,_softc) *sc = device_get_softc(self); \
+ struct usb_attach_arg *uaa = device_get_ivars(self)
+
+/* Returns from attach */
+#define USB_ATTACH_ERROR_RETURN return ENXIO
+#define USB_ATTACH_SUCCESS_RETURN return 0
+
+#define USB_ATTACH_SETUP \
+ sc->sc_dev = self; \
+ device_set_desc_copy(self, devinfo)
+
+#define USB_DETACH(dname) \
+Static int \
+__CONCAT(dname,_detach)(device_t self)
+
+#define USB_DETACH_START(dname, sc) \
+ struct __CONCAT(dname,_softc) *sc = device_get_softc(self)
+
+#define USB_GET_SC_OPEN(dname, unit, sc) \
+ sc = devclass_get_softc(__CONCAT(dname,_devclass), unit); \
+ if (sc == NULL) \
+ return (ENXIO)
+
+#define USB_GET_SC(dname, unit, sc) \
+ sc = devclass_get_softc(__CONCAT(dname,_devclass), unit)
+
+#define USB_DO_ATTACH(dev, bdev, parent, args, print, sub) \
+ (device_probe_and_attach((bdev)) == 0 ? (bdev) : 0)
+
+/* conversion from one type of queue to the other */
+#define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD
+#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_EMPTY STAILQ_EMPTY
+#define SIMPLEQ_FOREACH STAILQ_FOREACH
+#define SIMPLEQ_INIT STAILQ_INIT
+#define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER
+#define SIMPLEQ_ENTRY STAILQ_ENTRY
+
+#include <sys/syslog.h>
+/*
+#define logprintf(args...) log(LOG_DEBUG, args)
+*/
+#define logprintf printf
+
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_hw_usb);
+#endif
+
+#endif /* __FreeBSD__ */
+
+#endif /* _USB_PORT_H */
+
diff --git a/sys/dev/usb/usb_quirks.c b/sys/dev/usb/usb_quirks.c
new file mode 100644
index 0000000..043998c
--- /dev/null
+++ b/sys/dev/usb/usb_quirks.c
@@ -0,0 +1,136 @@
+/* $NetBSD: usb_quirks.c,v 1.42 2003/01/02 04:19:00 imp Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#ifdef USB_DEBUG
+extern int usbdebug;
+#endif
+
+#define ANY 0xffff
+
+Static const struct usbd_quirk_entry {
+ u_int16_t idVendor;
+ u_int16_t idProduct;
+ u_int16_t bcdDevice;
+ struct usbd_quirks quirks;
+} usb_quirks[] = {
+ { USB_VENDOR_KYE, USB_PRODUCT_KYE_NICHE, 0x100, { UQ_NO_SET_PROTO}},
+ { USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4,
+ 0x094, { UQ_SWAP_UNICODE}},
+ { USB_VENDOR_BTC, USB_PRODUCT_BTC_BTC7932, 0x100, { UQ_NO_STRINGS }},
+ { USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT, 0x002, { UQ_NO_STRINGS }},
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1, 0x101, { UQ_NO_STRINGS }},
+ { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_CT0405U, 0x101, { UQ_NO_STRINGS }},
+ { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, { UQ_BAD_ADC }},
+ { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, { UQ_AU_NO_XU }},
+ { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, 0x103, { UQ_BAD_ADC }},
+ { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, 0x000, { UQ_BAD_AUDIO }},
+ { USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 0x110, { UQ_SPUR_BUT_UP }},
+ { USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 0x001, { UQ_SPUR_BUT_UP }},
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100, 0x102, { UQ_BUS_POWERED }},
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0x102, { UQ_BUS_POWERED }},
+ { USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS,
+ 0x100, { UQ_ASSUME_CM_OVER_DATA | UQ_NO_STRINGS }},
+ { USB_VENDOR_SANYO, USB_PRODUCT_SANYO_SCP4900,
+ 0x000, { UQ_ASSUME_CM_OVER_DATA | UQ_NO_STRINGS }},
+ { USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41, 0x110, { UQ_POWER_CLAIM }},
+ { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U,
+ 0x000, { UQ_NO_STRINGS }},
+ { USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, 0x009, { UQ_AU_NO_FRAC }},
+ { USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE,
+ 0x100, { UQ_AU_INP_ASYNC }},
+ { USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND5010, 0x100, { UQ_NO_STRINGS }},
+ /* XXX These should have a revision number, but I don't know what they are. */
+ { USB_VENDOR_HP, USB_PRODUCT_HP_895C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_880C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_815C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_810C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_830C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_1220C, ANY, { UQ_BROKEN_BIDIR }},
+ /* YAMAHA router's ucdDevice is the version of farmware and often changes. */
+ { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTA54I,
+ ANY, { UQ_ASSUME_CM_OVER_DATA }},
+ { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTA55I,
+ ANY, { UQ_ASSUME_CM_OVER_DATA }},
+ { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTW65B,
+ ANY, { UQ_ASSUME_CM_OVER_DATA }},
+ { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTW65I,
+ ANY, { UQ_ASSUME_CM_OVER_DATA }},
+ { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMRPAD, ANY, { UQ_NO_STRINGS }},
+ { USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_CDMA_MSM,
+ ANY, { UQ_ASSUME_CM_OVER_DATA }},
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS64LX,
+ 0x100, { UQ_ASSUME_CM_OVER_DATA }},
+ { 0, 0, 0, { 0 } }
+};
+
+const struct usbd_quirks usbd_no_quirk = { 0 };
+
+const struct usbd_quirks *
+usbd_find_quirk(usb_device_descriptor_t *d)
+{
+ const struct usbd_quirk_entry *t;
+ u_int16_t vendor = UGETW(d->idVendor);
+ u_int16_t product = UGETW(d->idProduct);
+ u_int16_t revision = UGETW(d->bcdDevice);
+
+ for (t = usb_quirks; t->idVendor != 0; t++) {
+ if (t->idVendor == vendor &&
+ t->idProduct == product &&
+ (t->bcdDevice == ANY || t->bcdDevice == revision))
+ break;
+ }
+#ifdef USB_DEBUG
+ if (usbdebug && t->quirks.uq_flags)
+ logprintf("usbd_find_quirk 0x%04x/0x%04x/%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..2a36a06
--- /dev/null
+++ b/sys/dev/usb/usb_quirks.h
@@ -0,0 +1,61 @@
+/* $NetBSD: usb_quirks.h,v 1.20 2001/04/15 09:38:01 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 0x0001 /* cannot handle SET PROTOCOL. */
+#define UQ_SWAP_UNICODE 0x0002 /* has some Unicode strings swapped. */
+#define UQ_MS_REVZ 0x0004 /* mouse has Z-axis reversed */
+#define UQ_NO_STRINGS 0x0008 /* string descriptors are broken. */
+#define UQ_BAD_ADC 0x0010 /* bad audio spec version number. */
+#define UQ_BUS_POWERED 0x0020 /* device is bus powered, despite claim */
+#define UQ_BAD_AUDIO 0x0040 /* device claims audio class, but isn't */
+#define UQ_SPUR_BUT_UP 0x0080 /* spurious mouse button up events */
+#define UQ_AU_NO_XU 0x0100 /* audio device has broken extension unit */
+#define UQ_POWER_CLAIM 0x0200 /* hub lies about power status */
+#define UQ_AU_NO_FRAC 0x0400 /* don't adjust for fractional samples */
+#define UQ_AU_INP_ASYNC 0x0800 /* input is async despite claim of adaptive */
+#define UQ_ASSUME_CM_OVER_DATA 0x1000 /* modem device breaks on cm over data */
+#define UQ_BROKEN_BIDIR 0x2000 /* printer has broken bidir mode */
+};
+
+extern const struct usbd_quirks usbd_no_quirk;
+
+const struct usbd_quirks *usbd_find_quirk(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..1e27987
--- /dev/null
+++ b/sys/dev/usb/usb_subr.c
@@ -0,0 +1,1425 @@
+/* $NetBSD: usb_subr.c,v 1.99 2002/07/11 21:14:34 augustss Exp $ */
+
+/* Also already have from NetBSD:
+ * $NetBSD: usb_subr.c,v 1.102 2003/01/01 16:21:50 augustss Exp $
+ * $NetBSD: usb_subr.c,v 1.103 2003/01/10 11:19:13 augustss Exp $
+ * $NetBSD: usb_subr.c,v 1.111 2004/03/15 10:35:04 augustss Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#include <sys/select.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/proc.h>
+
+#include <machine/bus.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) logprintf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+Static usbd_status usbd_set_config(usbd_device_handle, int);
+Static void usbd_devinfo_vp(usbd_device_handle, char *, char *, int);
+Static char *usbd_get_string(usbd_device_handle, int, char *);
+Static int usbd_getnewaddr(usbd_bus_handle bus);
+#if defined(__NetBSD__)
+Static int usbd_print(void *aux, const char *pnp);
+Static int usbd_submatch(device_ptr_t, struct cfdata *cf, void *);
+#elif defined(__OpenBSD__)
+Static int usbd_print(void *aux, const char *pnp);
+Static int usbd_submatch(device_ptr_t, void *, void *);
+#endif
+Static void usbd_free_iface_data(usbd_device_handle dev, int ifcno);
+Static void usbd_kill_pipe(usbd_pipe_handle);
+Static usbd_status usbd_probe_and_attach(device_ptr_t parent,
+ usbd_device_handle dev, int port, int addr);
+
+Static u_int32_t usb_cookie_no = 0;
+
+#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 */
+
+Static const char * const usbd_error_strs[] = {
+ "NORMAL_COMPLETION",
+ "IN_PROGRESS",
+ "PENDING_REQUESTS",
+ "NOT_STARTED",
+ "INVAL",
+ "NOMEM",
+ "CANCELLED",
+ "BAD_ADDRESS",
+ "IN_USE",
+ "NO_ADDR",
+ "SET_ADDR_FAILED",
+ "NO_POWER",
+ "TOO_DEEP",
+ "IOERROR",
+ "NOT_CONFIGURED",
+ "TIMEOUT",
+ "SHORT_XFER",
+ "STALLED",
+ "INTERRUPTED",
+ "XXX",
+};
+
+const char *
+usbd_errstr(usbd_status err)
+{
+ static char buffer[5];
+
+ if (err < USBD_ERROR_MAX) {
+ return usbd_error_strs[err];
+ } else {
+ snprintf(buffer, sizeof buffer, "%d", err);
+ return buffer;
+ }
+}
+
+usbd_status
+usbd_get_string_desc(usbd_device_handle dev, int sindex, int langid,
+ usb_string_descriptor_t *sdesc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ int actlen;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_STRING, sindex);
+ USETW(req.wIndex, langid);
+ USETW(req.wLength, 2); /* only size byte first */
+ err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK,
+ &actlen, USBD_DEFAULT_TIMEOUT);
+ if (err)
+ return (err);
+
+ if (actlen < 1)
+ return (USBD_SHORT_XFER);
+
+ USETW(req.wLength, sdesc->bLength); /* the whole string */
+ return (usbd_do_request(dev, &req, sdesc));
+}
+
+char *
+usbd_get_string(usbd_device_handle dev, int si, char *buf)
+{
+ int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE;
+ usb_string_descriptor_t us;
+ char *s;
+ int i, n;
+ u_int16_t c;
+ usbd_status err;
+
+ if (si == 0)
+ return (0);
+ if (dev->quirks->uq_flags & UQ_NO_STRINGS)
+ return (0);
+ if (dev->langid == USBD_NOLANG) {
+ /* Set up default language */
+ err = usbd_get_string_desc(dev, USB_LANGUAGE_TABLE, 0, &us);
+ if (err || us.bLength < 4) {
+ dev->langid = 0; /* Well, just pick something then */
+ } else {
+ /* Pick the first language as the default. */
+ dev->langid = UGETW(us.bString[0]);
+ }
+ }
+ err = usbd_get_string_desc(dev, si, dev->langid, &us);
+ if (err)
+ 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);
+}
+
+Static void
+usbd_trim_spaces(char *p)
+{
+ char *q, *e;
+
+ if (p == NULL)
+ return;
+ q = e = p;
+ while (*q == ' ') /* skip leading spaces */
+ q++;
+ while ((*p = *q++)) /* copy string */
+ if (*p++ != ' ') /* remember last non-space */
+ e = p;
+ *e = 0; /* kill trailing spaces */
+}
+
+Static void
+usbd_devinfo_vp(usbd_device_handle dev, char *v, char *p, int usedev)
+{
+ usb_device_descriptor_t *udd = &dev->ddesc;
+ char *vendor = 0, *product = 0;
+#ifdef USBVERBOSE
+ const struct usb_knowndev *kdp;
+#endif
+
+ if (dev == NULL) {
+ v[0] = p[0] = '\0';
+ return;
+ }
+
+ if (usedev) {
+ vendor = usbd_get_string(dev, udd->iManufacturer, v);
+ usbd_trim_spaces(vendor);
+ product = usbd_get_string(dev, udd->iProduct, p);
+ usbd_trim_spaces(product);
+ if (vendor && !*vendor)
+ vendor = NULL;
+ if (product && !*product)
+ product = NULL;
+ } else {
+ vendor = NULL;
+ product = NULL;
+ }
+#ifdef USBVERBOSE
+ if (vendor == NULL || product == NULL) {
+ 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) {
+ if (vendor == NULL)
+ vendor = kdp->vendorname;
+ if (product == NULL)
+ product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ?
+ kdp->productname : NULL;
+ }
+ }
+#endif
+ if (vendor != NULL && *vendor)
+ strcpy(v, vendor);
+ else
+ sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor));
+ if (product != NULL && *product)
+ strcpy(p, product);
+ else
+ sprintf(p, "product 0x%04x", UGETW(udd->idProduct));
+}
+
+int
+usbd_printBCD(char *cp, int bcd)
+{
+ return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff));
+}
+
+void
+usbd_devinfo(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, 1);
+ cp += sprintf(cp, "%s %s", vendor, 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 += sprintf(cp, ", addr %d", dev->address);
+ *cp = 0;
+}
+
+/* Delay for a certain number of ms */
+void
+usb_delay_ms(usbd_bus_handle bus, u_int ms)
+{
+ /* Wait at least two clock ticks so we know the time has passed. */
+ if (bus->use_polling || cold)
+ delay((ms+1) * 1000);
+ else
+ tsleep(&ms, PRIBIO, "usbdly", (ms*hz+999)/1000 + 1);
+}
+
+/* Delay given a device handle. */
+void
+usbd_delay_ms(usbd_device_handle dev, u_int ms)
+{
+ usb_delay_ms(dev->bus, ms);
+}
+
+usbd_status
+usbd_reset_port(usbd_device_handle dev, int port, usb_port_status_t *ps)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ 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);
+ err = usbd_do_request(dev, &req, 0);
+ DPRINTFN(1,("usbd_reset_port: port %d reset done, error=%s\n",
+ port, usbd_errstr(err)));
+ if (err)
+ return (err);
+ n = 10;
+ do {
+ /* Wait for device to recover from reset. */
+ usbd_delay_ms(dev, USB_PORT_RESET_DELAY);
+ err = usbd_get_port_status(dev, port, ps);
+ if (err) {
+ DPRINTF(("usbd_reset_port: get status failed %d\n",
+ err));
+ return (err);
+ }
+ /* If the device disappeared, just give up. */
+ if (!(UGETW(ps->wPortStatus) & UPS_CURRENT_CONNECT_STATUS))
+ return (USBD_NORMAL_COMPLETION);
+ } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0);
+ if (n == 0)
+ return (USBD_TIMEOUT);
+ err = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET);
+#ifdef USB_DEBUG
+ if (err)
+ DPRINTF(("usbd_reset_port: clear port feature failed %d\n",
+ err));
+#endif
+
+ /* Wait for the device to recover from reset. */
+ usbd_delay_ms(dev, USB_PORT_RESET_RECOVERY);
+ return (err);
+}
+
+usb_interface_descriptor_t *
+usbd_find_idesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx)
+{
+ char *p = (char *)cd;
+ char *end = p + UGETW(cd->wTotalLength);
+ usb_interface_descriptor_t *d;
+ int curidx, lastidx, curaidx = 0;
+
+ for (curidx = lastidx = -1; p < end; ) {
+ d = (usb_interface_descriptor_t *)p;
+ DPRINTFN(4,("usbd_find_idesc: idx=%d(%d) altidx=%d(%d) len=%d "
+ "type=%d\n",
+ ifaceidx, curidx, altidx, curaidx,
+ d->bLength, d->bDescriptorType));
+ if (d->bLength == 0) /* bad descriptor */
+ break;
+ p += d->bLength;
+ if (p <= end && d->bDescriptorType == UDESC_INTERFACE) {
+ if (d->bInterfaceNumber != lastidx) {
+ lastidx = d->bInterfaceNumber;
+ curidx++;
+ curaidx = 0;
+ } else
+ curaidx++;
+ if (ifaceidx == curidx && altidx == curaidx)
+ return (d);
+ }
+ }
+ return (NULL);
+}
+
+usb_endpoint_descriptor_t *
+usbd_find_edesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx,
+ int endptidx)
+{
+ char *p = (char *)cd;
+ char *end = p + UGETW(cd->wTotalLength);
+ usb_interface_descriptor_t *d;
+ usb_endpoint_descriptor_t *e;
+ int curidx;
+
+ d = usbd_find_idesc(cd, ifaceidx, altidx);
+ if (d == NULL)
+ return (NULL);
+ if (endptidx >= d->bNumEndpoints) /* quick exit */
+ return (NULL);
+
+ curidx = -1;
+ for (p = (char *)d + d->bLength; p < end; ) {
+ e = (usb_endpoint_descriptor_t *)p;
+ if (e->bLength == 0) /* bad descriptor */
+ break;
+ p += e->bLength;
+ if (p <= end && e->bDescriptorType == UDESC_INTERFACE)
+ return (NULL);
+ if (p <= end && e->bDescriptorType == UDESC_ENDPOINT) {
+ curidx++;
+ if (curidx == endptidx)
+ return (e);
+ }
+ }
+ return (NULL);
+}
+
+usbd_status
+usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx)
+{
+ usbd_interface_handle ifc = &dev->ifaces[ifaceidx];
+ usb_interface_descriptor_t *idesc;
+ char *p, *end;
+ int endpt, nendpt;
+
+ DPRINTFN(4,("usbd_fill_iface_data: ifaceidx=%d altidx=%d\n",
+ ifaceidx, altidx));
+ idesc = usbd_find_idesc(dev->cdesc, ifaceidx, altidx);
+ if (idesc == NULL)
+ return (USBD_INVAL);
+ ifc->device = dev;
+ ifc->idesc = idesc;
+ ifc->index = ifaceidx;
+ ifc->altindex = altidx;
+ nendpt = ifc->idesc->bNumEndpoints;
+ DPRINTFN(4,("usbd_fill_iface_data: found idesc nendpt=%d\n", nendpt));
+ if (nendpt != 0) {
+ ifc->endpoints = malloc(nendpt * sizeof(struct usbd_endpoint),
+ M_USB, M_NOWAIT);
+ if (ifc->endpoints == NULL)
+ return (USBD_NOMEM);
+ } else
+ ifc->endpoints = NULL;
+ ifc->priv = NULL;
+ p = (char *)ifc->idesc + ifc->idesc->bLength;
+ end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength);
+#define ed ((usb_endpoint_descriptor_t *)p)
+ for (endpt = 0; endpt < nendpt; endpt++) {
+ DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt));
+ for (; p < end; p += ed->bLength) {
+ 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->bLength != 0 &&
+ ed->bDescriptorType == UDESC_ENDPOINT)
+ goto found;
+ if (ed->bLength == 0 ||
+ ed->bDescriptorType == UDESC_INTERFACE)
+ break;
+ }
+ /* passed end, or bad desc */
+ printf("usbd_fill_iface_data: bad descriptor(s): %s\n",
+ ed->bLength == 0 ? "0 length" :
+ ed->bDescriptorType == UDESC_INTERFACE ? "iface desc":
+ "out of data");
+ goto bad;
+ found:
+ ifc->endpoints[endpt].edesc = ed;
+ if (dev->speed == USB_SPEED_HIGH) {
+ u_int mps;
+ /* Control and bulk endpoints have max packet limits. */
+ switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+ case UE_CONTROL:
+ mps = USB_2_MAX_CTRL_PACKET;
+ goto check;
+ case UE_BULK:
+ mps = USB_2_MAX_BULK_PACKET;
+ check:
+ if (UGETW(ed->wMaxPacketSize) != mps) {
+ USETW(ed->wMaxPacketSize, mps);
+#ifdef DIAGNOSTIC
+ printf("usbd_fill_iface_data: bad max "
+ "packet size\n");
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ ifc->endpoints[endpt].refcnt = 0;
+ p += ed->bLength;
+ }
+#undef ed
+ LIST_INIT(&ifc->pipes);
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ if (ifc->endpoints != NULL) {
+ free(ifc->endpoints, M_USB);
+ ifc->endpoints = NULL;
+ }
+ return (USBD_INVAL);
+}
+
+void
+usbd_free_iface_data(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(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(usbd_device_handle dev, int no, int msg)
+{
+ int index;
+ usb_config_descriptor_t cd;
+ usbd_status err;
+
+ if (no == USB_UNCONFIG_NO)
+ return (usbd_set_config_index(dev, USB_UNCONFIG_INDEX, msg));
+
+ DPRINTFN(5,("usbd_set_config_no: %d\n", no));
+ /* Figure out what config index to use. */
+ for (index = 0; index < dev->ddesc.bNumConfigurations; index++) {
+ err = usbd_get_config_desc(dev, index, &cd);
+ if (err)
+ return (err);
+ if (cd.bConfigurationValue == no)
+ return (usbd_set_config_index(dev, index, msg));
+ }
+ return (USBD_INVAL);
+}
+
+usbd_status
+usbd_set_config_index(usbd_device_handle dev, int index, int msg)
+{
+ usb_status_t ds;
+ usb_config_descriptor_t cd, *cdp;
+ usbd_status err;
+ int i, ifcidx, nifc, len, selfpowered, power;
+
+ DPRINTFN(5,("usbd_set_config_index: dev=%p index=%d\n", dev, index));
+
+ if (dev->config != USB_UNCONFIG_NO) {
+ nifc = dev->cdesc->bNumInterface;
+
+ /* Check that all interfaces are idle */
+ for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
+ if (LIST_EMPTY(&dev->ifaces[ifcidx].pipes))
+ continue;
+ DPRINTF(("usbd_set_config_index: open pipes exist\n"));
+ return (USBD_IN_USE);
+ }
+
+ DPRINTF(("usbd_set_config_index: free old config\n"));
+ /* Free all configuration data structures. */
+ for (ifcidx = 0; ifcidx < nifc; ifcidx++)
+ usbd_free_iface_data(dev, ifcidx);
+ free(dev->ifaces, M_USB);
+ free(dev->cdesc, M_USB);
+ dev->ifaces = NULL;
+ dev->cdesc = NULL;
+ dev->config = USB_UNCONFIG_NO;
+ }
+
+ if (index == USB_UNCONFIG_INDEX) {
+ /* We are unconfiguring the device, so leave unallocated. */
+ DPRINTF(("usbd_set_config_index: set config 0\n"));
+ err = usbd_set_config(dev, USB_UNCONFIG_NO);
+ if (err)
+ DPRINTF(("usbd_set_config_index: setting config=0 "
+ "failed, error=%s\n", usbd_errstr(err)));
+ return (err);
+ }
+
+ /* Get the short descriptor. */
+ err = usbd_get_config_desc(dev, index, &cd);
+ if (err)
+ return (err);
+ len = UGETW(cd.wTotalLength);
+ cdp = malloc(len, M_USB, M_NOWAIT);
+ if (cdp == NULL)
+ return (USBD_NOMEM);
+
+ /* Get the full descriptor. Try a few times for slow devices. */
+ for (i = 0; i < 3; i++) {
+ err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp);
+ if (!err)
+ break;
+ usbd_delay_ms(dev, 200);
+ }
+ if (err)
+ goto bad;
+ if (cdp->bDescriptorType != UDESC_CONFIG) {
+ DPRINTFN(-1,("usbd_set_config_index: bad desc %d\n",
+ cdp->bDescriptorType));
+ err = USBD_INVAL;
+ goto bad;
+ }
+
+ /* Figure out if the device is self or bus powered. */
+ selfpowered = 0;
+ if (!(dev->quirks->uq_flags & UQ_BUS_POWERED) &&
+ (cdp->bmAttributes & UC_SELF_POWERED)) {
+ /* May be self powered. */
+ if (cdp->bmAttributes & UC_BUS_POWERED) {
+ /* Must ask device. */
+ if (dev->quirks->uq_flags & UQ_POWER_CLAIM) {
+ /*
+ * Hub claims to be self powered, but isn't.
+ * It seems that the power status can be
+ * determined by the hub characteristics.
+ */
+ usb_hub_descriptor_t hd;
+ usb_device_request_t req;
+ 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);
+ err = usbd_do_request(dev, &req, &hd);
+ if (!err &&
+ (UGETW(hd.wHubCharacteristics) &
+ UHD_PWR_INDIVIDUAL))
+ selfpowered = 1;
+ DPRINTF(("usbd_set_config_index: charac=0x%04x"
+ ", error=%s\n",
+ UGETW(hd.wHubCharacteristics),
+ usbd_errstr(err)));
+ } else {
+ err = usbd_get_device_status(dev, &ds);
+ if (!err &&
+ (UGETW(ds.wStatus) & UDS_SELF_POWERED))
+ selfpowered = 1;
+ DPRINTF(("usbd_set_config_index: status=0x%04x"
+ ", error=%s\n",
+ UGETW(ds.wStatus), usbd_errstr(err)));
+ }
+ } else
+ selfpowered = 1;
+ }
+ DPRINTF(("usbd_set_config_index: (addr %d) cno=%d attr=0x%02x, "
+ "selfpowered=%d, power=%d\n",
+ cdp->bConfigurationValue, dev->address, cdp->bmAttributes,
+ selfpowered, cdp->bMaxPower * 2));
+
+ /* Check if we have enough power. */
+#ifdef USB_DEBUG
+ if (dev->powersrc == NULL) {
+ DPRINTF(("usbd_set_config_index: No power source?\n"));
+ return (USBD_IOERROR);
+ }
+#endif
+ power = cdp->bMaxPower * 2;
+ if (power > dev->powersrc->power) {
+ DPRINTF(("power exceeded %d %d\n", power,dev->powersrc->power));
+ /* XXX print nicer message. */
+ if (msg)
+ printf("%s: device addr %d (config %d) exceeds power "
+ "budget, %d mA > %d mA\n",
+ USBDEVNAME(dev->bus->bdev), dev->address,
+ cdp->bConfigurationValue,
+ power, dev->powersrc->power);
+ err = USBD_NO_POWER;
+ goto bad;
+ }
+ dev->power = power;
+ dev->self_powered = selfpowered;
+
+ /* Set the actual configuration value. */
+ DPRINTF(("usbd_set_config_index: set config %d\n",
+ cdp->bConfigurationValue));
+ err = usbd_set_config(dev, cdp->bConfigurationValue);
+ if (err) {
+ DPRINTF(("usbd_set_config_index: setting config=%d failed, "
+ "error=%s\n",
+ cdp->bConfigurationValue, usbd_errstr(err)));
+ goto bad;
+ }
+
+ /* Allocate and fill interface data. */
+ nifc = cdp->bNumInterface;
+ dev->ifaces = malloc(nifc * sizeof(struct usbd_interface),
+ M_USB, M_NOWAIT);
+ if (dev->ifaces == NULL) {
+ err = USBD_NOMEM;
+ goto bad;
+ }
+ DPRINTFN(5,("usbd_set_config_index: dev=%p cdesc=%p\n", dev, cdp));
+ dev->cdesc = cdp;
+ dev->config = cdp->bConfigurationValue;
+ for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
+ err = usbd_fill_iface_data(dev, ifcidx, 0);
+ if (err) {
+ while (--ifcidx >= 0)
+ usbd_free_iface_data(dev, ifcidx);
+ goto bad;
+ }
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ free(cdp, M_USB);
+ return (err);
+}
+
+/* XXX add function for alternate settings */
+
+usbd_status
+usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface,
+ struct usbd_endpoint *ep, int ival, usbd_pipe_handle *pipe)
+{
+ usbd_pipe_handle p;
+ usbd_status err;
+
+ 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 == NULL)
+ return (USBD_NOMEM);
+ p->device = dev;
+ p->iface = iface;
+ p->endpoint = ep;
+ ep->refcnt++;
+ p->refcnt = 1;
+ p->intrxfer = 0;
+ p->running = 0;
+ p->aborting = 0;
+ p->repeat = 0;
+ p->interval = ival;
+ SIMPLEQ_INIT(&p->queue);
+ err = dev->bus->methods->open_pipe(p);
+ if (err) {
+ DPRINTFN(-1,("usbd_setup_pipe: endpoint=0x%x failed, error="
+ "%s\n",
+ ep->edesc->bEndpointAddress, usbd_errstr(err)));
+ free(p, M_USB);
+ return (err);
+ }
+ /* Clear any stall and make sure DATA0 toggle will be used next. */
+ if (UE_GET_ADDR(ep->edesc->bEndpointAddress) != USB_CONTROL_ENDPOINT) {
+ err = usbd_clear_endpoint_stall(p);
+ /* Some devices reject this command, so ignore a STALL. */
+ if (err && err != USBD_STALLED) {
+ printf("usbd_setup_pipe: failed to start endpoint, %s\n", usbd_errstr(err));
+ return (err);
+ }
+ }
+ *pipe = p;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Abort the device control pipe. */
+void
+usbd_kill_pipe(usbd_pipe_handle pipe)
+{
+ usbd_abort_pipe(pipe);
+ pipe->methods->close(pipe);
+ pipe->endpoint->refcnt--;
+ free(pipe, M_USB);
+}
+
+int
+usbd_getnewaddr(usbd_bus_handle bus)
+{
+ int addr;
+
+ for (addr = 1; addr < USB_MAX_DEVICES; addr++)
+ if (bus->devices[addr] == 0)
+ return (addr);
+ return (-1);
+}
+
+
+usbd_status
+usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev,
+ int port, int addr)
+{
+ struct usb_attach_arg uaa;
+ usb_device_descriptor_t *dd = &dev->ddesc;
+ int found, i, confi, nifaces;
+ usbd_status err;
+ device_ptr_t dv;
+ usbd_interface_handle ifaces[256]; /* 256 is the absolute max */
+
+#if defined(__FreeBSD__)
+ /*
+ * XXX uaa is a static var. Not a problem as it _should_ be used only
+ * during probe and attach. Should be changed however.
+ */
+ device_t bdev;
+ bdev = device_add_child(parent, NULL, -1);
+ if (!bdev) {
+ printf("%s: Device creation failed\n", USBDEVNAME(dev->bus->bdev));
+ return (USBD_INVAL);
+ }
+ device_set_ivars(bdev, &uaa);
+ device_quiet(bdev);
+#endif
+
+ uaa.device = dev;
+ uaa.iface = NULL;
+ uaa.ifaces = NULL;
+ uaa.nifaces = 0;
+ uaa.usegeneric = 0;
+ uaa.port = port;
+ uaa.configno = UHUB_UNK_CONFIGURATION;
+ uaa.ifaceno = UHUB_UNK_INTERFACE;
+ uaa.vendor = UGETW(dd->idVendor);
+ uaa.product = UGETW(dd->idProduct);
+ uaa.release = UGETW(dd->bcdDevice);
+
+ /* First try with device specific drivers. */
+ DPRINTF(("usbd_probe_and_attach: trying device specific drivers\n"));
+ dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch);
+ if (dv) {
+ dev->subdevs = malloc(2 * sizeof dv, M_USB, M_NOWAIT);
+ if (dev->subdevs == NULL)
+ return (USBD_NOMEM);
+ dev->subdevs[0] = dv;
+ dev->subdevs[1] = 0;
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ DPRINTF(("usbd_probe_and_attach: no device specific driver found\n"));
+
+ DPRINTF(("usbd_probe_and_attach: looping over %d configurations\n",
+ dd->bNumConfigurations));
+ /* Next try with interface drivers. */
+ for (confi = 0; confi < dd->bNumConfigurations; confi++) {
+ DPRINTFN(1,("usbd_probe_and_attach: trying config idx=%d\n",
+ confi));
+ err = usbd_set_config_index(dev, confi, 1);
+ if (err) {
+#ifdef USB_DEBUG
+ DPRINTF(("%s: port %d, set config at addr %d failed, "
+ "error=%s\n", USBDEVPTRNAME(parent), port,
+ addr, usbd_errstr(err)));
+#else
+ printf("%s: port %d, set config at addr %d failed\n",
+ USBDEVPTRNAME(parent), port, addr);
+#endif
+#if defined(__FreeBSD__)
+ device_delete_child(parent, bdev);
+#endif
+
+ return (err);
+ }
+ nifaces = dev->cdesc->bNumInterface;
+ uaa.configno = dev->cdesc->bConfigurationValue;
+ for (i = 0; i < nifaces; i++)
+ ifaces[i] = &dev->ifaces[i];
+ uaa.ifaces = ifaces;
+ uaa.nifaces = nifaces;
+ dev->subdevs = malloc((nifaces+1) * sizeof dv, M_USB,M_NOWAIT);
+ if (dev->subdevs == NULL) {
+#if defined(__FreeBSD__)
+ device_delete_child(parent, bdev);
+#endif
+ return (USBD_NOMEM);
+ }
+
+ found = 0;
+ for (i = 0; i < nifaces; i++) {
+ if (ifaces[i] == NULL)
+ continue; /* interface already claimed */
+ uaa.iface = ifaces[i];
+ uaa.ifaceno = ifaces[i]->idesc->bInterfaceNumber;
+ dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print,
+ usbd_submatch);
+ if (dv != NULL) {
+ dev->subdevs[found++] = dv;
+ dev->subdevs[found] = 0;
+ ifaces[i] = 0; /* consumed */
+
+#if defined(__FreeBSD__)
+ /* create another child for the next iface */
+ bdev = device_add_child(parent, NULL, -1);
+ if (!bdev) {
+ printf("%s: Device creation failed\n",
+ USBDEVNAME(dev->bus->bdev));
+ return (USBD_NORMAL_COMPLETION);
+ }
+ device_set_ivars(bdev, &uaa);
+ device_quiet(bdev);
+#endif
+ }
+ }
+ if (found != 0) {
+#if defined(__FreeBSD__)
+ /* remove the last created child again; it is unused */
+ device_delete_child(parent, bdev);
+#endif
+ return (USBD_NORMAL_COMPLETION);
+ }
+ free(dev->subdevs, M_USB);
+ dev->subdevs = 0;
+ }
+ /* No interfaces were attached in any of the configurations. */
+
+ if (dd->bNumConfigurations > 1) /* don't change if only 1 config */
+ usbd_set_config_index(dev, 0, 0);
+
+ DPRINTF(("usbd_probe_and_attach: no interface drivers found\n"));
+
+ /* Finally try the generic driver. */
+ uaa.iface = NULL;
+ uaa.usegeneric = 1;
+ uaa.configno = UHUB_UNK_CONFIGURATION;
+ uaa.ifaceno = UHUB_UNK_INTERFACE;
+ dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch);
+ if (dv != NULL) {
+ dev->subdevs = malloc(2 * sizeof dv, M_USB, M_NOWAIT);
+ if (dev->subdevs == 0)
+ return (USBD_NOMEM);
+ dev->subdevs[0] = dv;
+ dev->subdevs[1] = 0;
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ /*
+ * The 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_probe_and_attach: generic attach failed\n"));
+#if defined(__FreeBSD__)
+ device_delete_child(parent, bdev);
+#endif
+ 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(device_ptr_t parent, usbd_bus_handle bus, int depth,
+ int speed, int port, struct usbd_port *up)
+{
+ usbd_device_handle dev;
+ struct usbd_device *hub;
+ usb_device_descriptor_t *dd;
+ usb_port_status_t ps;
+ usbd_status err;
+ int addr;
+ int i;
+
+ DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n",
+ bus, port, depth, speed));
+ addr = usbd_getnewaddr(bus);
+ if (addr < 0) {
+ printf("%s: No free USB addresses, new device ignored.\n",
+ USBDEVNAME(bus->bdev));
+ return (USBD_NO_ADDR);
+ }
+
+ dev = malloc(sizeof *dev, M_USB, M_NOWAIT|M_ZERO);
+ if (dev == NULL)
+ return (USBD_NOMEM);
+
+ dev->bus = bus;
+
+ /* Set up default endpoint handle. */
+ dev->def_ep.edesc = &dev->def_ep_desc;
+
+ /* 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->quirks = &usbd_no_quirk;
+ dev->address = USB_START_ADDR;
+ dev->ddesc.bMaxPacketSize = 0;
+ dev->depth = depth;
+ dev->powersrc = up;
+ dev->myhub = up->parent;
+ for (hub = up->parent;
+ hub != NULL && hub->speed != USB_SPEED_HIGH;
+ hub = hub->myhub)
+ ;
+ dev->myhighhub = hub;
+ dev->speed = speed;
+ dev->langid = USBD_NOLANG;
+ dev->cookie.cookie = ++usb_cookie_no;
+
+ /* Establish the default pipe. */
+ err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
+ &dev->default_pipe);
+ if (err) {
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ up->device = dev;
+
+ /* Set the address. Do this early; some devices need that. */
+ /* Try a few times in case the device is slow (i.e. outside specs.) */
+ DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr));
+ for (i = 0; i < 15; i++) {
+ err = usbd_set_address(dev, addr);
+ if (!err)
+ break;
+ usbd_delay_ms(dev, 200);
+ if ((i & 3) == 3) {
+ DPRINTFN(-1,("usb_new_device: set address %d "
+ "failed - trying a port reset\n", addr));
+ usbd_reset_port(up->parent, port, &ps);
+ }
+ }
+ if (err) {
+ DPRINTFN(-1,("usb_new_device: set address %d failed\n", addr));
+ err = USBD_SET_ADDR_FAILED;
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+ /* Allow device time to set new address */
+ usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE);
+ dev->address = addr; /* New device address now */
+ bus->devices[addr] = dev;
+
+ dd = &dev->ddesc;
+ /* Get the first 8 bytes of the device descriptor. */
+ err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd);
+ if (err) {
+ DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc "
+ "failed\n", addr));
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ if (speed == USB_SPEED_HIGH) {
+ /* Max packet size must be 64 (sec 5.5.3). */
+ if (dd->bMaxPacketSize != USB_2_MAX_CTRL_PACKET) {
+#ifdef DIAGNOSTIC
+ printf("usbd_new_device: addr=%d bad max packet size\n",
+ addr);
+#endif
+ dd->bMaxPacketSize = USB_2_MAX_CTRL_PACKET;
+ }
+ }
+
+ DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, "
+ "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
+ addr,UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass,
+ dd->bDeviceProtocol, dd->bMaxPacketSize, dd->bLength,
+ dev->speed));
+
+ if (dd->bDescriptorType != UDESC_DEVICE) {
+ /* Illegal device descriptor */
+ DPRINTFN(-1,("usbd_new_device: illegal descriptor %d\n",
+ dd->bDescriptorType));
+ usbd_remove_device(dev, up);
+ return (USBD_INVAL);
+ }
+
+ if (dd->bLength < USB_DEVICE_DESCRIPTOR_SIZE) {
+ DPRINTFN(-1,("usbd_new_device: bad length %d\n", dd->bLength));
+ usbd_remove_device(dev, up);
+ return (USBD_INVAL);
+ }
+
+ USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize);
+
+ err = usbd_reload_device_desc(dev);
+ if (err) {
+ DPRINTFN(-1, ("usbd_new_device: addr=%d, getting full desc "
+ "failed\n", addr));
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ /* 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));
+
+ err = usbd_probe_and_attach(parent, dev, port, addr);
+ if (err) {
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ usbd_add_dev_event(USB_EVENT_DEVICE_ATTACH, dev);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_reload_device_desc(usbd_device_handle dev)
+{
+ usbd_status err;
+ int i;
+
+ /* Get the full device descriptor. */
+ for (i = 0; i < 3; ++i) {
+ err = usbd_get_device_desc(dev, &dev->ddesc);
+ if (!err)
+ break;
+ usbd_delay_ms(dev, 200);
+ }
+ if (err)
+ return (err);
+
+ /* Figure out what's wrong with this device. */
+ dev->quirks = usbd_find_quirk(&dev->ddesc);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usbd_remove_device(usbd_device_handle dev, struct usbd_port *up)
+{
+ DPRINTF(("usbd_remove_device: %p\n", dev));
+
+ if (dev->default_pipe != NULL)
+ usbd_kill_pipe(dev->default_pipe);
+ up->device = 0;
+ dev->bus->devices[dev->address] = 0;
+
+ free(dev, M_USB);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+usbd_print(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, %s", devinfo, pnp);
+ }
+ if (uaa->port != 0)
+ printf(" port %d", uaa->port);
+ if (uaa->configno != UHUB_UNK_CONFIGURATION)
+ printf(" configuration %d", uaa->configno);
+ if (uaa->ifaceno != UHUB_UNK_INTERFACE)
+ printf(" interface %d", uaa->ifaceno);
+#if 0
+ /*
+ * It gets very crowded with these locators on the attach line.
+ * They are not really needed since they are printed in the clear
+ * by each driver.
+ */
+ if (uaa->vendor != UHUB_UNK_VENDOR)
+ printf(" vendor 0x%04x", uaa->vendor);
+ if (uaa->product != UHUB_UNK_PRODUCT)
+ printf(" product 0x%04x", uaa->product);
+ if (uaa->release != UHUB_UNK_RELEASE)
+ printf(" release 0x%04x", uaa->release);
+#endif
+ return (UNCONF);
+}
+
+#if defined(__NetBSD__)
+int
+usbd_submatch(struct device *parent, struct cfdata *cf, void *aux)
+{
+#elif defined(__OpenBSD__)
+int
+usbd_submatch(struct device *parent, void *match, void *aux)
+{
+ struct cfdata *cf = match;
+#endif
+ struct usb_attach_arg *uaa = aux;
+
+ DPRINTFN(5,("usbd_submatch port=%d,%d configno=%d,%d "
+ "ifaceno=%d,%d vendor=%d,%d product=%d,%d release=%d,%d\n",
+ uaa->port, cf->uhubcf_port,
+ uaa->configno, cf->uhubcf_configuration,
+ uaa->ifaceno, cf->uhubcf_interface,
+ uaa->vendor, cf->uhubcf_vendor,
+ uaa->product, cf->uhubcf_product,
+ uaa->release, cf->uhubcf_release));
+ if (uaa->port != 0 && /* root hub has port 0, it should match */
+ ((uaa->port != 0 &&
+ cf->uhubcf_port != UHUB_UNK_PORT &&
+ cf->uhubcf_port != uaa->port) ||
+ (uaa->configno != UHUB_UNK_CONFIGURATION &&
+ cf->uhubcf_configuration != UHUB_UNK_CONFIGURATION &&
+ cf->uhubcf_configuration != uaa->configno) ||
+ (uaa->ifaceno != UHUB_UNK_INTERFACE &&
+ cf->uhubcf_interface != UHUB_UNK_INTERFACE &&
+ cf->uhubcf_interface != uaa->ifaceno) ||
+ (uaa->vendor != UHUB_UNK_VENDOR &&
+ cf->uhubcf_vendor != UHUB_UNK_VENDOR &&
+ cf->uhubcf_vendor != uaa->vendor) ||
+ (uaa->product != UHUB_UNK_PRODUCT &&
+ cf->uhubcf_product != UHUB_UNK_PRODUCT &&
+ cf->uhubcf_product != uaa->product) ||
+ (uaa->release != UHUB_UNK_RELEASE &&
+ cf->uhubcf_release != UHUB_UNK_RELEASE &&
+ cf->uhubcf_release != uaa->release)
+ )
+ )
+ return 0;
+ if (cf->uhubcf_vendor != UHUB_UNK_VENDOR &&
+ cf->uhubcf_vendor == uaa->vendor &&
+ cf->uhubcf_product != UHUB_UNK_PRODUCT &&
+ cf->uhubcf_product == uaa->product) {
+ /* We have a vendor&product locator match */
+ if (cf->uhubcf_release != UHUB_UNK_RELEASE &&
+ cf->uhubcf_release == uaa->release)
+ uaa->matchlvl = UMATCH_VENDOR_PRODUCT_REV;
+ else
+ uaa->matchlvl = UMATCH_VENDOR_PRODUCT;
+ } else
+ uaa->matchlvl = 0;
+ return ((*cf->cf_attach->ca_match)(parent, cf, aux));
+}
+
+#endif
+
+void
+usbd_fill_deviceinfo(usbd_device_handle dev, struct usb_device_info *di,
+ int usedev)
+{
+ struct usbd_port *p;
+ int i, err, s;
+
+ di->udi_bus = USBDEVUNIT(dev->bus->bdev);
+ di->udi_addr = dev->address;
+ di->udi_cookie = dev->cookie;
+ usbd_devinfo_vp(dev, di->udi_vendor, di->udi_product, usedev);
+ usbd_printBCD(di->udi_release, UGETW(dev->ddesc.bcdDevice));
+ di->udi_vendorNo = UGETW(dev->ddesc.idVendor);
+ di->udi_productNo = UGETW(dev->ddesc.idProduct);
+ di->udi_releaseNo = UGETW(dev->ddesc.bcdDevice);
+ di->udi_class = dev->ddesc.bDeviceClass;
+ di->udi_subclass = dev->ddesc.bDeviceSubClass;
+ di->udi_protocol = dev->ddesc.bDeviceProtocol;
+ di->udi_config = dev->config;
+ di->udi_power = dev->self_powered ? 0 : dev->power;
+ di->udi_speed = dev->speed;
+
+ if (dev->subdevs != NULL) {
+ for (i = 0; dev->subdevs[i] &&
+ i < USB_MAX_DEVNAMES; i++) {
+ strncpy(di->udi_devnames[i], USBDEVPTRNAME(dev->subdevs[i]),
+ USB_MAX_DEVNAMELEN);
+ di->udi_devnames[i][USB_MAX_DEVNAMELEN-1] = '\0';
+ }
+ } else {
+ i = 0;
+ }
+ for (/*i is set */; i < USB_MAX_DEVNAMES; i++)
+ di->udi_devnames[i][0] = 0; /* empty */
+
+ if (dev->hub) {
+ for (i = 0;
+ i < sizeof(di->udi_ports) / sizeof(di->udi_ports[0]) &&
+ i < dev->hub->hubdesc.bNbrPorts;
+ i++) {
+ p = &dev->hub->ports[i];
+ if (p->device)
+ err = p->device->address;
+ else {
+ s = UGETW(p->status.wPortStatus);
+ if (s & UPS_PORT_ENABLED)
+ err = USB_PORT_ENABLED;
+ else if (s & UPS_SUSPEND)
+ err = USB_PORT_SUSPENDED;
+ else if (s & UPS_PORT_POWER)
+ err = USB_PORT_POWERED;
+ else
+ err = USB_PORT_DISABLED;
+ }
+ di->udi_ports[i] = err;
+ }
+ di->udi_nports = dev->hub->hubdesc.bNbrPorts;
+ } else
+ di->udi_nports = 0;
+}
+
+void
+usb_free_device(usbd_device_handle dev)
+{
+ int ifcidx, nifc;
+
+ if (dev->default_pipe != NULL)
+ usbd_kill_pipe(dev->default_pipe);
+ if (dev->ifaces != NULL) {
+ nifc = dev->cdesc->bNumInterface;
+ for (ifcidx = 0; ifcidx < nifc; ifcidx++)
+ usbd_free_iface_data(dev, ifcidx);
+ free(dev->ifaces, M_USB);
+ }
+ if (dev->cdesc != NULL)
+ free(dev->cdesc, M_USB);
+ if (dev->subdevs != NULL)
+ free(dev->subdevs, M_USB);
+ free(dev, M_USB);
+}
+
+/*
+ * The general mechanism for detaching drivers works as follows: Each
+ * driver is responsible for maintaining a reference count on the
+ * number of outstanding references to its softc (e.g. from
+ * processing hanging in a read or write). The detach method of the
+ * driver decrements this counter and flags in the softc that the
+ * driver is dying and then wakes any sleepers. It then sleeps on the
+ * softc. Each place that can sleep must maintain the reference
+ * count. When the reference count drops to -1 (0 is the normal value
+ * of the reference count) the a wakeup on the softc is performed
+ * signaling to the detach waiter that all references are gone.
+ */
+
+/*
+ * Called from process context when we discover that a port has
+ * been disconnected.
+ */
+void
+usb_disconnect_port(struct usbd_port *up, device_ptr_t parent)
+{
+ usbd_device_handle dev = up->device;
+ const char *hubname = USBDEVPTRNAME(parent);
+ int i;
+
+ DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n",
+ up, dev, up->portno));
+
+#ifdef DIAGNOSTIC
+ if (dev == NULL) {
+ printf("usb_disconnect_port: no device\n");
+ return;
+ }
+#endif
+
+ if (dev->subdevs != NULL) {
+ DPRINTFN(3,("usb_disconnect_port: disconnect subdevs\n"));
+ for (i = 0; dev->subdevs[i]; i++) {
+ printf("%s: at %s", USBDEVPTRNAME(dev->subdevs[i]),
+ hubname);
+ if (up->portno != 0)
+ printf(" port %d", up->portno);
+ printf(" (addr %d) disconnected\n", dev->address);
+ config_detach(dev->subdevs[i], DETACH_FORCE);
+ dev->subdevs[i] = NULL;
+ }
+ }
+
+ usbd_add_dev_event(USB_EVENT_DEVICE_DETACH, dev);
+ dev->bus->devices[dev->address] = NULL;
+ up->device = NULL;
+ usb_free_device(dev);
+}
+
+#ifdef __OpenBSD__
+void *usb_realloc(void *p, u_int size, int pool, int flags)
+{
+ void *q;
+
+ q = malloc(size, pool, flags);
+ if (q == NULL)
+ return (NULL);
+ bcopy(p, q, size);
+ free(p, pool);
+ return (q);
+}
+#endif
diff --git a/sys/dev/usb/usbcdc.h b/sys/dev/usb/usbcdc.h
new file mode 100644
index 0000000..45266e4
--- /dev/null
+++ b/sys/dev/usb/usbcdc.h
@@ -0,0 +1,170 @@
+/* $NetBSD: usbcdc.h,v 1.6 2000/04/27 15:26:50 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 _USBCDC_H_
+#define _USBCDC_H_
+
+#define UDESCSUB_CDC_HEADER 0
+#define UDESCSUB_CDC_CM 1 /* Call Management */
+#define UDESCSUB_CDC_ACM 2 /* Abstract Control Model */
+#define UDESCSUB_CDC_DLM 3 /* Direct Line Management */
+#define UDESCSUB_CDC_TRF 4 /* Telephone Ringer */
+#define UDESCSUB_CDC_TCLSR 5 /* Telephone Call ... */
+#define UDESCSUB_CDC_UNION 6
+#define UDESCSUB_CDC_CS 7 /* Country Selection */
+#define UDESCSUB_CDC_TOM 8 /* Telephone Operational Modes */
+#define UDESCSUB_CDC_USBT 9 /* USB Terminal */
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uWord bcdCDC;
+} usb_cdc_header_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bmCapabilities;
+#define USB_CDC_CM_DOES_CM 0x01
+#define USB_CDC_CM_OVER_DATA 0x02
+ uByte bDataInterface;
+} usb_cdc_cm_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bmCapabilities;
+#define USB_CDC_ACM_HAS_FEATURE 0x01
+#define USB_CDC_ACM_HAS_LINE 0x02
+#define USB_CDC_ACM_HAS_BREAK 0x04
+#define USB_CDC_ACM_HAS_NETWORK_CONN 0x08
+} usb_cdc_acm_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bMasterInterface;
+ uByte bSlaveInterface[1];
+} usb_cdc_union_descriptor_t;
+
+#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00
+#define UCDC_GET_ENCAPSULATED_RESPONSE 0x01
+#define UCDC_SET_COMM_FEATURE 0x02
+#define UCDC_GET_COMM_FEATURE 0x03
+#define UCDC_ABSTRACT_STATE 0x01
+#define UCDC_COUNTRY_SETTING 0x02
+#define UCDC_CLEAR_COMM_FEATURE 0x04
+#define UCDC_SET_LINE_CODING 0x20
+#define UCDC_GET_LINE_CODING 0x21
+#define UCDC_SET_CONTROL_LINE_STATE 0x22
+#define UCDC_LINE_DTR 0x0001
+#define UCDC_LINE_RTS 0x0002
+#define UCDC_SEND_BREAK 0x23
+#define UCDC_BREAK_ON 0xffff
+#define UCDC_BREAK_OFF 0x0000
+
+typedef struct {
+ uWord wState;
+#define UCDC_IDLE_SETTING 0x0001
+#define UCDC_DATA_MULTIPLEXED 0x0002
+} usb_cdc_abstract_state_t;
+#define UCDC_ABSTRACT_STATE_LENGTH 2
+
+typedef struct {
+ uDWord dwDTERate;
+ uByte bCharFormat;
+#define UCDC_STOP_BIT_1 0
+#define UCDC_STOP_BIT_1_5 1
+#define UCDC_STOP_BIT_2 2
+ uByte bParityType;
+#define UCDC_PARITY_NONE 0
+#define UCDC_PARITY_ODD 1
+#define UCDC_PARITY_EVEN 2
+#define UCDC_PARITY_MARK 3
+#define UCDC_PARITY_SPACE 4
+ uByte bDataBits;
+} usb_cdc_line_state_t;
+#define UCDC_LINE_STATE_LENGTH 7
+
+typedef struct {
+ uByte bmRequestType;
+#define UCDC_NOTIFICATION 0xa1
+ uByte bNotification;
+#define UCDC_N_NETWORK_CONNECTION 0x00
+#define UCDC_N_RESPONSE_AVAILABLE 0x01
+#define UCDC_N_AUX_JACK_HOOK_STATE 0x08
+#define UCDC_N_RING_DETECT 0x09
+#define UCDC_N_SERIAL_STATE 0x20
+#define UCDC_N_CALL_STATE_CHANGED 0x28
+#define UCDC_N_LINE_STATE_CHANGED 0x29
+#define UCDC_N_CONNECTION_SPEED_CHANGE 0x2a
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+ uByte data[16];
+} usb_cdc_notification_t;
+#define UCDC_NOTIFICATION_LENGTH 8
+
+/*
+ * Bits set in the SERIAL STATE notifcation (first byte of data)
+ */
+
+#define UCDC_N_SERIAL_OVERRUN 0x40
+#define UCDC_N_SERIAL_PARITY 0x20
+#define UCDC_N_SERIAL_FRAMING 0x10
+#define UCDC_N_SERIAL_RI 0x08
+#define UCDC_N_SERIAL_BREAK 0x04
+#define UCDC_N_SERIAL_DSR 0x02
+#define UCDC_N_SERIAL_DCD 0x01
+
+/* Serial state bit masks */
+#define UCDC_MDM_RXCARRIER 0x01
+#define UCDC_MDM_TXCARRIER 0x02
+#define UCDC_MDM_BREAK 0x04
+#define UCDC_MDM_RING 0x08
+#define UCDC_MDM_FRAMING_ERR 0x10
+#define UCDC_MDM_PARITY_ERR 0x20
+#define UCDC_MDM_OVERRUN_ERR 0x40
+
+#endif /* _USBCDC_H_ */
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
new file mode 100644
index 0000000..9206ae4
--- /dev/null
+++ b/sys/dev/usb/usbdevs
@@ -0,0 +1,1347 @@
+$FreeBSD$
+
+/*
+ * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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
+ *
+ * Adding an ID and its string description for a device to the usbdevs file,
+ * enables the USB stack to print a useful description of the device that was
+ * connected.
+ *
+ * The ID should be added in usbdevs and then the files usbdevs.h and
+ * usbdevs_data.h need to be regenerated.
+ *
+ * # edit usbdevs
+ * make -f Makefile.usbdevs
+ * # test your change
+ * cd ../../modules/usb
+ * make
+ * # commit, if appropriate
+ * cvs -m "ID for device XYZ" commit usbdevs
+ * # commit the derived files after the $ FreeBSD $ has been updated.
+ * cvs commit usbdevs.h usbdevs_data.h
+ *
+ * Please note that these IDs do not do anything. Adding an ID here and
+ * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name
+ * available to the source code and does not change any functionality, nor
+ * does it make your device available to a specific driver.
+ * It will however make the descriptive string available if a device does not
+ * provide the string itself.
+ *
+ * After adding a vendor ID VNDR and a product ID PRDCT you will have the
+ * following extra defines:
+ * #define USB_VENDOR_VNDR 0x????
+ * #define USB_PRODUCT_VNDR_PRDCT 0x????
+ *
+ * You may have to add these defines to the respective probe routines to
+ * make the device recognised by the appropriate device driver.
+ */
+
+vendor AOX 0x03e8 AOX
+vendor ATMEL 0x03eb Atmel
+vendor MITSUMI 0x03ee Mitsumi
+vendor HP 0x03f0 Hewlett Packard
+vendor ADAPTEC 0x03f3 Adaptec
+vendor NATIONAL 0x0400 National Semiconductor
+vendor ACERLABS 0x0402 Acer Labs
+vendor FTDI 0x0403 Future Technology Devices
+vendor NEC 0x0409 NEC
+vendor KODAK 0x040a Eastman Kodak
+vendor MELCO 0x0411 Melco
+vendor CREATIVE 0x041e Creative
+vendor ADI 0x0422 ADI Systems
+vendor CATC 0x0423 Computer Access Technology
+vendor SMC2 0x0424 Standard Microsystems
+vendor GRAVIS 0x0428 Advanced Gravis Computer Tech.
+vendor SUN 0x0430 Sun Microsystems
+vendor TAUGA 0x0436 Taugagreining HF
+vendor AMD 0x0438 Advanced Micro Devices
+vendor LEXMARK 0x043d Lexmark International
+vendor NANAO 0x0440 NANAO
+vendor ALPS 0x044e Alps Electric
+vendor THRUST 0x044f Thrustmaster
+vendor TI 0x0451 Texas Instruments
+vendor ANALOGDEVICES 0x0456 Analog Devices
+vendor KYE 0x0458 KYE Systems
+vendor DIAMOND2 0x045a Diamond (Supra)
+vendor MICROSOFT 0x045e Microsoft
+vendor PRIMAX 0x0461 Primax Electronics
+vendor AMP 0x0464 AMP
+vendor CHERRY 0x046a Cherry Mikroschalter
+vendor MEGATRENDS 0x046b American Megatrends
+vendor LOGITECH 0x046d Logitech
+vendor BTC 0x046e Behavior Tech. Computer
+vendor PHILIPS 0x0471 Philips
+vendor SANYO 0x0474 Sanyo Electric
+vendor CONNECTIX 0x0478 Connectix
+vendor KENSINGTON 0x047d Kensington
+vendor LUCENT 0x047e Lucent
+vendor KYOCERA 0x0482 Kyocera Corp.
+vendor STMICRO 0x0483 STMicroelectronics
+vendor YAMAHA 0x0499 YAMAHA
+vendor COMPAQ 0x049f Compaq Computers
+vendor HITACHI 0x04a4 Hitachi, Ltd.
+vendor ACERP 0x04a5 Acer Peripherals
+vendor VISIONEER 0x04a7 Visioneer
+vendor CANON 0x04a9 Canon
+vendor NIKON 0x04b0 Nikon
+vendor IBM 0x04b3 IBM Corporation
+vendor CYPRESS 0x04b4 Cypress Semiconductor
+vendor EPSON 0x04b8 Seiko Epson
+vendor RAINBOW 0x04b9 Rainbow Technologies
+vendor IODATA 0x04bb I/O Data
+vendor TDK 0x04bf TDK
+vendor 3COMUSR 0x04c1 U.S. Robotics
+vendor METHODE 0x04c2 Methode Electronics Far East
+vendor MAXISWITCH 0x04c3 Maxi Switch
+vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research
+vendor FUJITSU 0x04c5 Fujitsu
+vendor TOSHIBAAM 0x04c6 Toshiba America Electronic Components
+vendor MICROMACRO 0x04c7 Micro Macro Technologies
+vendor KONICA 0x04c8 Konica
+vendor LITEON 0x04ca Lite-On Technology
+vendor FUJIPHOTO 0x04cb Fuji Photo Film
+vendor PHILIPSSEMI 0x04cc Philips Semiconductors
+vendor TATUNG 0x04cd Tatung Co. Of America
+vendor SCANLOGIC 0x04ce ScanLogic
+vendor MYSON 0x04cf Myson Technology
+vendor DIGI2 0x04d0 Digi International
+vendor ITTCANON 0x04d1 ITT Canon
+vendor ALTEC 0x04d2 Altec Lansing Technologies
+vendor PANASONIC 0x04da Panasonic (Matsushita)
+vendor IIYAMA 0x04e1 Iiyama
+vendor SHUTTLE 0x04e6 Shuttle Technology
+vendor SAMSUNG 0x04e8 Samsung Electronics
+vendor ANNABOOKS 0x04ed Annabooks
+vendor JVC 0x04f1 JVC
+vendor CHICONY 0x04f2 Chicony Electronics
+vendor BROTHER 0x04f9 Brother Industries
+vendor DALLAS 0x04fa Dallas Semiconductor
+vendor ACER 0x0502 Acer
+vendor 3COM 0x0506 3Com
+vendor AZTECH 0x0509 Aztech Systems
+vendor BELKIN 0x050d Belkin Components
+vendor KAWATSU 0x050f Kawatsu Semiconductor
+vendor APC 0x051d American Power Conversion
+vendor CONNECTEK 0x0522 Advanced Connectek USA
+vendor NETCHIP 0x0525 NetChip Technology
+vendor ALTRA 0x0527 ALTRA
+vendor ATI 0x0528 ATI Technologies
+vendor AKS 0x0529 Aladdin Knowledge Systems
+vendor UNIACCESS 0x0540 Universal Access
+vendor XIRLINK 0x0545 Xirlink
+vendor ANCHOR 0x0547 Anchor Chips
+vendor SONY 0x054c Sony
+vendor VISION 0x0553 VLSI Vision
+vendor ASAHIKASEI 0x0556 Asahi Kasei Microsystems
+vendor ATEN 0x0557 ATEN International
+vendor MUSTEK 0x055f Mustek Systems
+vendor TELEX 0x0562 Telex Communications
+vendor PERACOM 0x0565 Peracom Networks
+vendor ALCOR2 0x0566 Alcor Micro
+vendor WACOM 0x056a WACOM
+vendor ETEK 0x056c e-TEK Labs
+vendor EIZO 0x056d EIZO
+vendor ELECOM 0x056e Elecom
+vendor HAUPPAUGE 0x0573 Hauppauge Computer Works
+vendor BAFO 0x0576 BAFO/Quality Computer Accessories
+vendor YEDATA 0x057b Y-E Data
+vendor AVM 0x057c AVM GmbH
+vendor QUICKSHOT 0x057f Quickshot
+vendor ROLAND 0x0582 Roland
+vendor ROCKFIRE 0x0583 Rockfire
+vendor RATOC 0x0584 RATOC Systems, Inc.
+vendor ZYXEL 0x0586 ZyXEL Communication
+vendor ALCOR 0x058f Alcor Micro
+vendor IOMEGA 0x059b Iomega
+vendor ATREND 0x059c A-Trend Technology
+vendor AID 0x059d Advanced Input Devices
+vendor LACIE 0x059f LaCie
+vendor OMNIVISION 0x05a9 OmniVision
+vendor INSYSTEM 0x05ab In-System Design
+vendor APPLE 0x05ac Apple Computer
+vendor DIGI 0x05c5 Digi International
+vendor QTRONIX 0x05c7 Qtronix
+vendor ELSA 0x05cc ELSA
+vendor BRAINBOXES 0x05d1 Brainboxes Limited
+vendor ULTIMA 0x05d8 Ultima
+vendor AXIOHM 0x05d9 Axiohm Transaction Solutions
+vendor MICROTEK 0x05da Microtek
+vendor SUNTAC 0x05db SUN Corporation
+vendor LEXAR 0x05dc Lexar Media
+vendor SYMBOL 0x05e0 Symbol Technologies
+vendor GENESYS 0x05e3 Genesys Logic
+vendor FUJI 0x05e5 Fuji Electric
+vendor KEITHLEY 0x05e6 Keithley Instruments
+vendor EIZONANAO 0x05e7 EIZO Nanao
+vendor KLSI 0x05e9 Kawasaki LSI
+vendor FFC 0x05eb FFC
+vendor ANKO 0x05ef Anko Electronic
+vendor PIENGINEERING 0x05f3 P.I. Engineering
+vendor AOC 0x05f6 AOC International
+vendor CHIC 0x05fe Chic Technology
+vendor BARCO 0x0600 Barco Display Systems
+vendor BRIDGE 0x0607 Bridge Information
+vendor SOLIDYEAR 0x060b Solid Year
+vendor BIORAD 0x0614 Bio-Rad Laboratories
+vendor MACALLY 0x0618 Macally
+vendor ACTLABS 0x061c Act Labs
+vendor ALARIS 0x0620 Alaris
+vendor APEX 0x0624 Apex
+vendor AVISION 0x0638 Avision
+vendor TEAC 0x0644 TEAC
+vendor LINKSYS 0x066b Linksys
+vendor ACERSA 0x066e Acer Semiconductor America
+vendor SIGMATEL 0x066f Sigmatel
+vendor AIWA 0x0677 Aiwa
+vendor ACARD 0x0678 ACARD Technology
+vendor PROLIFIC 0x067b Prolific Technology
+vendor SIEMENS 0x067c Siemens
+vendor ADVANCELOGIC 0x0680 Avance Logic
+vendor HAGIWARA 0x0693 Hagiwara Sys-Com
+vendor MINOLTA 0x0686 Minolta
+vendor CTX 0x0698 Chuntex
+vendor ASKEY 0x069a Askey Computer
+vendor SAITEK 0x06a3 Saitek
+vendor ALCATELT 0x06b9 Alcatel Telecom
+vendor AGFA 0x06bd AGFA-Gevaert
+vendor ASIAMD 0x06be Asia Microelectronic Development
+vendor BIZLINK 0x06c4 Bizlink International
+vendor KEYSPAN 0x06cd Keyspan
+vendor AASHIMA 0x06d6 Aashima Technology
+vendor MULTITECH 0x06e0 MultiTech
+vendor ADS 0x06e1 ADS Technologies
+vendor ALCATELM 0x06e4 Alcatel Microelectronics
+vendor SIRIUS 0x06ea Sirius Technologies
+vendor BOSTON 0x06fd Boston Acoustics
+vendor SMC 0x0707 Standard Microsystems
+vendor PUTERCOM 0x0708 Putercom
+vendor MCT 0x0711 MCT
+vendor DIGITALSTREAM 0x074e Digital Stream
+vendor AUREAL 0x0755 Aureal Semiconductor
+vendor MIDIMAN 0x0763 Midiman
+vendor LINKSYS2 0x077b Linksys
+vendor GRIFFIN 0x077d Griffin Technology
+vendor SANDISK 0x0781 SanDisk Corp
+vendor LOGITEC 0x0789 Logitec Corp
+vendor BRIMAX 0x078e Brimax
+vendor AXIS 0x0792 Axis Communications
+vendor ABL 0x0794 ABL Electronics
+vendor ALFADATA 0x079d Alfadata Computer
+vendor NATIONALTECH 0x07a2 National Technical Systems
+vendor ONNTO 0x07a3 Onnto
+vendor BE 0x07a4 Be
+vendor ADMTEK 0x07a6 ADMtek
+vendor COREGA 0x07aa Corega
+vendor FREECOM 0x07ab Freecom
+vendor MICROTECH 0x07af Microtech
+vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola)
+vendor OLYMPUS 0x07b4 Olympus
+vendor ONSPEC 0x07c4 OnSpec Electronic
+vendor ABOCOM 0x07b8 AboCom Systems
+vendor KEISOKUGIKEN 0x07c1 Keisokugiken
+vendor APG 0x07c5 APG Cash Drawer
+vendor BUG 0x07c8 B.U.G.
+vendor ALLIEDTELESYN 0x07c9 Allied Telesyn International
+vendor AVERMEDIA 0x07ca AVerMedia Technologies
+vendor SIIG 0x07cc SIIG
+vendor CASIO 0x07cf CASIO
+vendor APTIO 0x07d2 Aptio Products
+vendor ARASAN 0x07da Arasan Chip Systems
+vendor ALLIEDCABLE 0x07e6 Allied Cable
+vendor STSN 0x07ef STSN
+vendor ZOOM 0x0803 Zoom Telephonics
+vendor BROADLOGIC 0x0827 BroadLogic
+vendor HANDSPRING 0x082d Handspring
+vendor ACTIONSTAR 0x0835 Action Star Enterprise
+vendor PALM 0x0830 Palm Computing
+vendor SOURCENEXT 0x0833 SOURCENEXT
+vendor ACCTON 0x083a Accton Technology
+vendor DIAMOND 0x0841 Diamond
+vendor NETGEAR 0x0846 BayNETGEAR
+vendor ACTIVEWIRE 0x0854 ActiveWire
+vendor PORTGEAR 0x085a PortGear
+vendor METRICOM 0x0870 Metricom
+vendor ADESSOKBTEK 0x087c ADESSO/Kbtek America
+vendor JATON 0x087d Jaton
+vendor APT 0x0880 APT Technologies
+vendor BOCARESEARCH 0x0885 Boca Research
+vendor ANDREA 0x08a8 Andrea Electronics
+vendor BURRBROWN 0x08bb Burr-Brown Japan
+vendor 2WIRE 0x08c8 2Wire
+vendor AIPTEK 0x08ca AIPTEK International
+vendor SMARTBRIDGES 0x08d1 SmartBridges
+vendor BILLIONTON 0x08dd Billionton Systems
+vendor EXTENDED 0x08e9 Extended Systems
+vendor MSYSTEMS 0x08ec M-Systems
+vendor AUTHENTEC 0x08ff AuthenTec
+vendor ALATION 0x0910 Alation Systems
+vendor GOHUBS 0x0921 GoHubs
+vendor BIOMETRIC 0x0929 American Biometric Company
+vendor TOSHIBA 0x0930 Toshiba Corporation
+vendor PLEXTOR 0x093b Plextor Corp.
+vendor YANO 0x094f Yano
+vendor KINGSTON 0x0951 Kingston Technology
+vendor BLUEWATER 0x0956 BlueWater Systems
+vendor AGILENT 0x0957 Agilent Technologies
+vendor PORTSMITH 0x095a Portsmith
+vendor ADIRONDACK 0x0976 Adirondack Wire & Cable
+vendor BECKHOFF 0x0978 Beckhoff
+vendor INTERSIL 0x09aa Intersil
+vendor ALTIUS 0x09b3 Altius Solutions
+vendor ARRIS 0x09c1 Arris Interactive
+vendor ACTIVCARD 0x09c3 ACTIVCARD
+vendor ACTISYS 0x09c4 ACTiSYS
+vendor AFOURTECH 0x09da A-FOUR TECH
+vendor AIMEX 0x09dc AIMEX
+vendor ADDONICS 0x09df Addonics Technologies
+vendor AKAI 0x09e8 AKAI professional M.I.
+vendor ARESCOM 0x09f5 ARESCOM
+vendor BAY 0x09f9 Bay Associates
+vendor ALTERA 0x09fb Altera
+vendor CSR 0x0a12 Cambridge Silicon Radio Ltd.
+vendor TREK 0x0a16 Trek Technology
+vendor ASAHIOPTICAL 0x0a17 Asahi Optical
+vendor BOCASYSTEMS 0x0a43 Boca Systems
+vendor BROADCOM 0x0a5c Broadcom
+vendor GREENHOUSE 0x0a6b GREENHOUSE
+vendor GEOCAST 0x0a79 Geocast Network Systems
+vendor NEODIO 0x0aec Neodio
+vendor TODOS 0x0b0c Todos Data System
+vendor HAL 0x0b41 HAL Corporation
+vendor EMS 0x0b43 EMS Production Ltd.
+vendor NEC2 0x0b62 NEC
+vendor ATI2 0x0b6f ATI
+vendor ASIX 0x0b95 ASIX Electronics
+vendor REALTEK 0x0bda RealTek
+vendor AGATE 0x0c08 Agate Technologies
+vendor DMI 0x0c0b DMI
+vendor LUWEN 0x0c76 Luwen
+vendor SMC3 0x0d5c Standard Microsystems
+vendor PNY 0x0d7d PNY
+vendor MSI 0x0db0 Micro Star International
+vendor HAWKING 0x0e66 Hawking Technologies
+vendor MICROTUNE 0x0f4d Microtune, Inc.
+vendor QUALCOMM 0x1004 Qualcomm
+vendor MOTOROLA 0x1063 Motorola
+vendor PLX 0x10b5 PLX
+vendor ASANTE 0x10bd Asante
+vendor JRC 0x1145 Japan Radio Company
+vendor DELORME 0x1163 Delorme Publishing
+vendor ACERCM 0x1189 Acer Communications & Multimedia Inc.
+vendor BELKIN2 0x1293 Belkin Components
+vendor MOBILITY 0x1342 Mobility
+vendor SHARK 0x13d2 Shark
+vendor SILICONPORTALS 0x1527 Silicon Portals
+vendor SOHOWARE 0x15e8 SOHOware
+vendor UMAX 0x1606 UMAX Data Systems
+vendor INSIDEOUT 0x1608 Inside Out Networks
+vendor ENTREGA 0x1645 Entrega
+vendor ACTIONTEC 0x1668 Actiontec Electronics
+vendor DLINK 0x2001 D-Link
+vendor VIDZMEDIA 0x3275 VidzMedia Pte Ltd
+vendor DAISY 0x3579 Daisy Technology
+vendor DELL 0x413c Dell
+vendor INTEL 0x8086 Intel
+vendor HP2 0xf003 Hewlett Packard
+
+/*
+ * List of known products. Grouped by vendor.
+ */
+
+/* 3Com products */
+product 3COM HOMECONN 0x009d HomeConnect USB Camera
+product 3COM 3CREB96 0x00a0 Bluetooth USB dongle
+product 3COM 3C19250 0x03E8 3C19250 Ethernet adapter
+product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice Faxmodem Pro
+product 3COM 3C460 0x11f8 HomeConnect 3C460
+product 3COM 3C460B 0x4601 HomeConnect 3C460B
+
+product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem
+product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA
+product 3COMUSR HOMECONN 0x009d 3Com HomeConnect camera
+product 3COMUSR USR56K 0x3021 U.S.Robotics 56000 Voice Faxmodem Pro
+
+/* AboCom products */
+product ABOCOM XX1 0x110c XX1
+product ABOCOM XX2 0x200c XX2
+product ABOCOM URE450 0x4000 URE450 Ethernet Adapter
+product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet Adapter
+product ABOCOM DSB650TX_PNA 0x4003 1/10/100 ethernet adapter
+product ABOCOM XX4 0x4004 XX4
+product ABOCOM XX5 0x4007 XX5
+product ABOCOM XX6 0x400b XX6
+product ABOCOM XX7 0x400c XX7
+product ABOCOM XX8 0x4102 XX8
+product ABOCOM XX9 0x4104 XX9
+product ABOCOM XX10 0xabc1 XX10
+
+/* Accton products */
+product ACCTON USB320_EC 0x1046 USB320-EC Ethernet Adapter
+product ACCTON SS1001 0x5046 SpeedStream Ethernet Adapter
+
+/* Acer Peripherals, Inc. products */
+product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U
+product ACERP ACERSCAN_320U 0x2022 Acerscan 320U
+product ACERP ACERSCAN_640U 0x2040 Acerscan 640U
+product ACERP ACERSCAN_620U 0x2060 Acerscan 620U
+product ACERP AWL300 0x9000 AWL300 Wireless adapter
+product ACERP AWL400 0x9001 AWL400 Wireless adapter
+
+/* ActiveWire, Inc. products */
+product ACTIVEWIRE IOBOARD 0x0100 I/O Board
+product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware
+
+/* Actiontec, Inc. products */
+product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet adapter
+
+/* ADMtek products */
+product ADMTEK PEGASUS 0x0986 AN986 USB Ethernet adapter
+product ADMTEK PEGASUSII 0x8511 AN8511 USB Ethernet adapter
+product ADMTEK PEGASUSII_2 0x8513 AN8513 USB Ethernet adapter
+
+/* ADS products */
+product ADS UBS10BT 0x0008 UBS-10BT Ethernet adapter
+
+/* Agate Technologies products */
+product AGATE QDRIVE 0x0378 Q-Drive
+
+/* AGFA products */
+product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U
+product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U
+product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch
+product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U
+product AGFA SNAPSCANE40 0x208d SnapScan e40
+product AGFA SNAPSCANE50 0x208f SnapScan e50
+product AGFA SNAPSCANE20 0x2091 SnapScan e20
+product AGFA SNAPSCANE25 0x2095 SnapScan e25
+product AGFA SNAPSCANE26 0x2097 SnapScan e26
+product AGFA SNAPSCANE52 0x20fd SnapScan e52
+
+/* AKS products */
+product AKS USBHASP 0x0001 USB-HASP 0.06
+
+/* Alcor Micro, Inc. products */
+product ALCOR2 KBD_HUB 0x2802 Kbd Hub
+
+product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub
+product ALCOR AU9814 0x9215 AU9814 Hub
+product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard
+product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub
+
+/* Altec Lansing products */
+product ALTEC ADA70 0x0070 ADA70 Speakers
+product ALTEC ASC495 0xff05 ASC495 Speakers
+
+/* American Power Conversion products */
+product APC UPSPRO500 0x0002 Back-UPS Pro 500
+
+/* Anchor products */
+product ANCHOR EZUSB 0x2131 EZUSB
+product ANCHOR EZLINK 0x2720 EZLINK
+
+/* AOX, Inc. products */
+product AOX USB101 0x0008 USB ethernet controller engine
+
+/* Apple Computer products */
+product APPLE OPTMOUSE 0x0302 Optical mouse
+product APPLE SPEAKERS 0x1101 Speakers
+
+/* Asahi Optical products */
+product ASAHIOPTICAL OPTIO230 0x0004 Digital camera
+product ASAHIOPTICAL OPTIO330 0x0006 Digital camera
+
+/* ASIX Electronics products */
+product ASIX AX88172 0x1720 USB 2.0 10/100 ethernet controller
+
+/* ATen products */
+product ATEN UC1284 0x2001 Parallel printer adapter
+product ATEN UC10T 0x2002 10Mbps ethernet adapter
+product ATEN UC232A 0x2008 Serial adapter
+
+/* Atmel Comp. products */
+product ATMEL UHB124 0x3301 UHB124 hub
+product ATMEL DWL120 0x7603 DWL-120 Wireless adapter
+product ATMEL BW002 0x7605 BW002 Wireless adapter
+product ATMEL AT76C505A 0x7614 AT76c505a Wireless adapter
+
+/* Avision products */
+product AVISION 1200U 0x0268 1200U scanner
+
+/* Belkin products */
+/*product BELKIN F5U111 0x???? F5U111 Ethernet adapter*/
+product BELKIN2 F5U002 0x0002 F5U002 Parallel printer adapter
+product BELKIN USB2LAN 0x0121 USB to LAN Converter
+product BELKIN F5U103 0x0103 F5U103 Serial adapter
+product BELKIN F5U109 0x0109 F5U109 Serial adapter
+product BELKIN F5U120 0x1203 F5U120-PC Hub
+product BELKIN F5U208 0x0208 F5U208 VideoBus II
+
+/* Billionton products */
+product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet Adapter
+product BILLIONTON USBLP100 0x0987 USB100LP
+product BILLIONTON USBEL100 0x0988 USB100EL
+product BILLIONTON USBE100 0x8511 USBE100
+
+/* Brother Industries products */
+product BROTHER HL1050 0x0002 HL-1050 laser printer
+
+/* Behavior Technology Computer products */
+product BTC BTC7932 0x6782 Keyboard with mouse port
+
+/* Broadcom products */
+product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle
+
+/* Canon, Inc. products */
+product CANON N656U 0x2206 CanoScan N656U
+product CANON N1220U 0x2207 CanoScan N1220U
+product CANON N676U 0x220d CanoScan N676U
+product CANON N1240U 0x220e CanoScan N1240U
+product CANON S10 0x3041 PowerShot S10
+product CANON S100 0x3045 PowerShot S100
+product CANON S200 0x3065 PowerShot S200
+
+/* CATC products */
+product CATC NETMATE 0x000a Netmate ethernet adapter
+product CATC NETMATE2 0x000c Netmate2 ethernet adapter
+product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer
+product CATC ANDROMEDA 0x1237 Andromeda hub
+
+/* CASIO products */
+product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB
+
+/* Cherry products */
+product CHERRY MY3000KBD 0x0001 My3000 keyboard
+product CHERRY MY3000HUB 0x0003 My3000 hub
+product CHERRY CYBOARD 0x0004 CyBoard Keyboard
+
+/* Chic Technology products */
+product CHIC MOUSE1 0x0001 mouse
+product CHIC CYPRESS 0x0003 Cypress USB Mouse
+
+/* Chicony products */
+product CHICONY KB8933 0x0001 KB-8933 keyboard
+
+/* Compaq products */
+product COMPAQ PJB100 0x504a Personal Jukebox PJB100
+
+/* Connectix products */
+product CONNECTIX QUICKCAM 0x0001 QuickCam
+
+/* Corega products */
+product COREGA ETHER_USB_T 0x0001 Ether USB-T
+product COREGA FETHER_USB_TX 0x0004 FEther USB-TX
+product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS
+product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC
+
+/* Creative products */
+product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player
+
+/* Crystalfontz products */
+product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD
+product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD
+product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD
+product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD
+product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation
+
+/* Cambridge Silicon Radio Ltd. products */
+product CSR BT_DONGLE 0x0001 Bluetooth USB dongle
+product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State
+
+/* CTX products */
+product CTX EX1300 0x9999 Ex1300 hub
+
+/* Cypress Semiconductor products */
+product CYPRESS MOUSE 0x0001 mouse
+product CYPRESS THERMO 0x0002 thermometer
+product CYPRESS FMRADIO 0x1002 FM Radio
+product CYPRESS SLIM_HUB 0x6560 Slim Hub
+
+/* Daisy Technology products */
+product DAISY DMC 0x6901 USB MultiMedia Reader
+
+/* Dallas Semiconductor products */
+product DALLAS J6502 0x4201 J-6502 speakers
+
+/* Dell products */
+product DELL BC02 0x8000 Dell BC02 Bluetooth USB Adapter
+
+/* Delorme Paublishing products */
+product DELORME EARTHMATE 0x0100 Earthmate GPS
+
+/* Diamond products */
+product DIAMOND RIO500USB 0x0001 Rio 500 USB
+
+/* Digi International products */
+product DIGI ACCELEPORT2 0x0002 AccelePort USB 2
+product DIGI ACCELEPORT4 0x0004 AccelePort USB 4
+product DIGI ACCELEPORT8 0x0008 AccelePort USB 8
+
+/* D-Link products */
+/*product DLINK DSBS25 0x0100 DSB-S25 serial adapter*/
+product DLINK DUBE100 0x1a00 10/100 ethernet adapter
+product DLINK DSB650TX4 0x200c 10/100 ethernet adapter
+product DLINK DSB650C 0x4000 10Mbps ethernet adapter
+product DLINK DSB650TX1 0x4001 10/100 ethernet adapter
+product DLINK DSB650TX 0x4002 10/100 ethernet adapter
+product DLINK DSB650TX_PNA 0x4003 1/10/100 ethernet adapter
+product DLINK DSB650TX3 0x400b 10/100 ethernet adapter
+product DLINK DSB650TX2 0x4102 10/100 ethernet adapter
+product DLINK DSB650 0xabc1 10/100 ethernet adapter
+
+/* EIZO products */
+product EIZO HUB 0x0000 hub
+product EIZO MONITOR 0x0001 monitor
+
+/* Elecom products */
+product ELECOM MOUSE29UO 0x0002 mouse 29UO
+product ELECOM LDUSBTX0 0x200c LD-USB/TX
+product ELECOM LDUSBTX1 0x4002 LD-USB/TX
+product ELECOM LDUSBLTX 0x4005 LD-USBL/TX
+product ELECOM LDUSBTX2 0x400b LD-USB/TX
+product ELECOM UCSGT 0x5003 UC-SGT
+product ELECOM LDUSBTX3 0xabc1 LD-USB/TX
+
+/* Elsa products */
+product ELSA MODEM1 0x2265 ELSA Modem Board
+product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet
+
+/* EMS products */
+product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter
+
+/* Entrega products */
+product ENTREGA 1S 0x0001 1S serial connector
+product ENTREGA 2S 0x0002 2S serial connector
+product ENTREGA 1S25 0x0003 1S25 serial connector
+product ENTREGA 4S 0x0004 4S serial connector
+product ENTREGA E45 0x0005 E45 Ethernet adapter
+product ENTREGA CENTRONICS 0x0006 Centronics connector
+product ENTREGA 1S9 0x0093 1S9 serial connector
+product ENTREGA EZUSB 0x8000 EZ-USB
+/*product ENTREGA SERIAL 0x8001 DB25 Serial connector*/
+product ENTREGA 2U4S 0x8004 2U4S serial connector/usb hub
+/*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial connector*/
+
+/* Epson products */
+product EPSON PRINTER1 0x0001 USB Printer
+product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac
+product EPSON PRINTER3 0x0003 ISD USB Smart Cable
+product EPSON PRINTER5 0x0005 USB Printer
+product EPSON 636 0x0101 Perfection 636U / 636Photo scanner
+product EPSON 610 0x0103 Perfection 610 scanner
+product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner
+product EPSON 1600 0x0107 Expression 1600 scanner
+product EPSON 1640 0x010a Perfection 1640SU scanner
+product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner
+product EPSON 640U 0x010c Perfection 640U scanner
+product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner
+product EPSON 1650 0x0110 Perfection 1650 scanner
+product EPSON GT9700F 0x0112 GT-9700F scanner
+product EPSON GT9300UF 0x011b GT-9300UF scanner
+product EPSON 3200 0x011c Perfection 3200 scanner
+product EPSON 1260 0x011d Perfection 1260 scanner
+product EPSON 1660 0x011e Perfection 1660 scanner
+product EPSON 1670 0x011f Perfection 1670 scanner
+
+/* e-TEK Labs products */
+product ETEK 1COM 0x8007 Serial port
+
+/* Extended Systems products */
+product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA
+
+/* GoHubs products */
+product GOHUBS GOCOM232 0x1001 GoCOM232 Serial converter
+
+/* Gravis products */
+product GRAVIS GAMEPADPRO 0x4001 GamePad Pro
+
+/* GREENHOUSE products */
+product GREENHOUSE KANA21 0x0001 CF-writer with Portable MP3 Player
+
+/* Griffin Technology */
+product GRIFFIN IMATE 0x0405 iMate, ADB adapter
+
+/* Freecom products */
+product FREECOM DVD 0xfc01 Connector for DVD drive
+
+/* Future Technology Devices products */
+product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial converter
+product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial converter
+
+/* Fuji photo products */
+product FUJIPHOTO MASS0100 0x0100 Mass Storage
+
+/* Fujitsu protducts */
+product FUJITSU AH_F401U 0x105b AH-F401U Air H device
+
+/* Qualcomm products */
+product QUALCOMM CDMA_MSM 0x6000 CDMA Technologies MSM phone
+
+/* General Instruments (Motorola) products */
+product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem
+
+/* Genesys Logic products */
+product GENESYS GL650 0x0604 GL650 Hub
+product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader
+product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2
+product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge
+
+/* HAL Corporation products */
+product HAL IMR001 0x0011 Crossam2+USB IR commander
+
+/* Hagiwara products */
+product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader
+product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader
+product HAGIWARA FG 0x0005 FlashGate
+
+/* Handspring, Inc. */
+product HANDSPRING VISOR 0x0100 Handspring Visor
+product HANDSPRING TREO 0x0200 Handspring Treo
+product HANDSPRING TREO600 0x0300 Handspring Treo 600
+
+/* Hauppauge Computer Works */
+product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM
+
+/* Hawking Technologies products */
+product HAWKING UF100 0x400c 10/100 USB Ethernet
+
+/* Hitachi, Ltd. products */
+product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface
+
+/* HP products */
+product HP 895C 0x0004 DeskJet 895C
+product HP 4100C 0x0101 Scanjet 4100C
+product HP S20 0x0102 Photosmart S20
+product HP 880C 0x0104 DeskJet 880C
+product HP 4200C 0x0105 ScanJet 4200C
+product HP CDWRITERPLUS 0x0107 CD-Writer Plus
+product HP KBDHUB 0x010c Multimedia Keyboard Hub
+product HP 6200C 0x0201 ScanJet 6200C
+product HP S20b 0x0202 PhotoSmart S20
+product HP 815C 0x0204 DeskJet 815C
+product HP 3300C 0x0205 ScanJet 3300C
+product HP CDW8200 0x0207 CD-Writer Plus 8200e
+product HP 1220C 0x0212 DeskJet 1220C
+product HP 810C 0x0304 DeskJet 810C/812C
+product HP 4300C 0x0305 Scanjet 4300C
+product HP G85XI 0x0311 OfficeJet G85xi
+product HP 1200 0x0317 LaserJet 1200
+product HP 5200C 0x0401 Scanjet 5200C
+product HP 830C 0x0404 DeskJet 830C
+product HP 3400CSE 0x0405 ScanJet 3400cse
+product HP 6300C 0x0601 Scanjet 6300C
+product HP 840C 0x0604 DeskJet 840c
+product HP 2200C 0x0605 ScanJet 2200C
+product HP 5300C 0x0701 Scanjet 5300C
+product HP 4400C 0x0705 Scanjet 4400C
+product HP 970CSE 0x1004 Deskjet 970Cse
+product HP 5400C 0x1005 Scanjet 5400C
+product HP 930C 0x1204 DeskJet 930c
+product HP P2000U 0x1801 Inkjet P-2000U
+product HP 640C 0x2004 DeskJet 640c
+product HP P1100 0x3102 Photosmart P1100
+product HP HN210E 0x811c Ethernet HN210E
+
+/* HP products */
+product HP2 C500 0x6002 PhotoSmart C500
+
+/* IBM Corporation */
+product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive
+
+/* Inside Out Networks products */
+product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports
+
+/* In-System products */
+product INSYSTEM F5U002 0x0002 Parallel printer adapter
+product INSYSTEM ATAPI 0x0031 ATAPI adapter
+product INSYSTEM ISD110 0x0200 IDE adapter ISD110
+product INSYSTEM ISD105 0x0202 IDE adapter ISD105
+product INSYSTEM USBCABLE 0x081a USB cable
+
+/* Intel products */
+product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera
+product INTEL TESTBOARD 0x9890 82930 test board
+
+/* Intersil products */
+product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN
+
+/* I/O DATA products */
+product IODATA USBETT 0x0901 USB ETT
+product IODATA USBETTX 0x0904 USB ETTX
+product IODATA USBETTXS 0x0913 USB ETTX
+product IODATA USBRSAQ 0x0a03 USB serial adapter USB-RSAQ1
+product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2
+product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8
+
+/* Iomega products */
+product IOMEGA ZIP100 0x0001 Zip 100
+product IOMEGA ZIP250 0x0030 Zip 250
+
+/* JVC products */
+product JVC GR_DX95 0x000a GR-DX95
+
+/* JRC products */
+product JRC AH_J3001V_J3002V 0x0001 AirH\" PHONE AH-J3001V/J3002V
+
+/* Kawasaki products */
+product KLSI DUH3E10BT 0x0008 USB ethernet controller engine
+product KLSI DUH3E10BTN 0x0009 USB ethernet controller engine
+
+/* Kawatsu products */
+product KAWATSU MH4000P 0x0003 MiniHub 4000P
+
+/* Keisokugiken Corp. products */
+product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ
+
+/* Kawasaki products */
+product KLSI DUH3E10BT 0x0008 10BT Ethernet adapter, in the DU-H3E
+
+/* Kensington products */
+product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball
+product KENSINGTON TURBOBALL 0x1005 TurboBall
+
+/* Keyspan products */
+product KEYSPAN USA28 0x0101 USA-28 serial adapter
+product KEYSPAN USA28X 0x0102 USA-28X serial adapter
+product KEYSPAN USA19 0x0103 USA-19 serial adapter
+product KEYSPAN USA18X 0x0105 USA-18X serial adapter
+product KEYSPAN USA19W 0x0106 USA-19W serial adapter
+product KEYSPAN USA49W 0x0109 USA-49W serial adapter
+product KEYSPAN USA19QW 0x0118 USA-19QW serial adapter
+
+/* Kingston products */
+product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet
+
+/* Kodak products */
+product KODAK DC220 0x0100 Digital Science DC220
+product KODAK DC260 0x0110 Digital Science DC260
+product KODAK DC265 0x0111 Digital Science DC265
+product KODAK DC290 0x0112 Digital Science DC290
+product KODAK DC240 0x0120 Digital Science DC240
+product KODAK DC280 0x0130 Digital Science DC280
+
+/* Konica Corp. Products */
+product KONICA CAMERA 0x0720 Digital Color Camera
+
+/* KYE products */
+product KYE NICHE 0x0001 Niche mouse
+product KYE NETSCROLL 0x0003 Genius NetScroll mouse
+product KYE FLIGHT2000 0x1004 Flight 2000 joystick
+product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner
+
+/* Kyocera products */
+product KYOCERA AHK3001V 0x0203 AH-K3001V
+
+/* LaCie products */
+product LACIE HD 0xa601 Hard Disk
+product LACIE CDRW 0xa602 CD R/W
+
+/* Lexar products */
+product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader
+
+/* Lexmark products */
+product LEXMARK S2450 0x0009 Optra S 2450
+
+/* Linksys products */
+product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2
+product LINKSYS USB10TX1 0x200c USB10TX
+product LINKSYS USB10T 0x2202 USB10T Ethernet
+product LINKSYS USB100TX 0x2203 USB100TX Ethernet
+product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA
+product LINKSYS USB10TA 0x2206 USB10TA Ethernet
+product LINKSYS USB10TX2 0x400b USB10TX
+product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless adapter
+product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 ethernet controller
+
+/* Logitech products */
+product LOGITECH M2452 0x0203 M2452 keyboard
+product LOGITECH M4848 0x0301 M4848 mouse
+product LOGITECH PAGESCAN 0x040f PageScan
+product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web
+product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro
+product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express
+product LOGITECH QUICKCAM 0x0850 QuickCam
+product LOGITECH N43 0xc000 N43
+product LOGITECH N48 0xc001 N48 mouse
+product LOGITECH MBA47 0xc002 M-BA47 mouse
+product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse
+product LOGITECH BD58 0xc00c BD58 mouse
+product LOGITECH UN58A 0xc030 iFeel Mouse
+product LOGITECH BB13 0xc401 USB-PS/2 Trackball
+product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme
+product LOGITECH WMRPAD 0xc20a WingMan RumblePad
+product LOGITECH WMJOY 0xc281 WingMan Force joystick
+product LOGITECH RK53 0xc501 Cordless mouse
+product LOGITECH RB6 0xc503 Cordless keyboard
+product LOGITECH MX700 0xc506 Cordless optical mouse
+product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro
+
+/* Logitec Corp. products */
+product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2
+product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2
+
+/* Lucent products */
+product LUCENT EVALKIT 0x1001 USS-720 evaluation kit
+
+/* Luwen products */
+product LUWEN EASYDISK 0x0005 EasyDisc
+
+/* Macally products */
+product MACALLY MOUSE1 0x0101 mouse
+
+/* Matrix Orbital products */
+product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial
+product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3
+product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5
+product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family
+product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family
+
+/* MCT Corp. */
+product MCT HUB0100 0x0100 Hub
+product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub
+product MCT USB232 0x0210 USB-232 Interface
+product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products
+
+/* Melco, Inc products */
+product MELCO LUATX1 0x0001 LUA-TX Ethernet
+product MELCO LUATX5 0x0005 LUA-TX Ethernet
+product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet
+product MELCO LUAKTX 0x0012 LUA-KTX Ethernet
+product MELCO DUBPXXG 0x001c USB-IDE Bridge: DUB-PxxG
+product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet
+
+/* Metricom products */
+product METRICOM RICOCHET_GS 0x0001 Ricochet GS
+
+/* Microsoft products */
+product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro
+product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse
+product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite
+product MICROSOFT DDS80 0x0014 Digital Sound System 80
+product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel
+product MICROSOFT INETPRO 0x001c Internet Keyboard Pro
+product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse
+product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro
+product MICROSOFT MN110 0x007a 10/100 USB NIC
+
+/* Microtech products */
+product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25
+product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50
+product MICROTECH DPCM 0x0006 USB CameraMate
+product MICROTECH FREECOM 0xfc01 Freecom USB-IDE
+
+/* Microtek products */
+product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner
+product MICROTEK X6U 0x0099 ScanMaker X6 - X6U
+product MICROTEK C6 0x009a Phantom C6 scanner
+product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner
+product MICROTEK V6USL 0x00a3 ScanMaker V6USL
+product MICROTEK V6USL2 0x80a3 ScanMaker V6USL
+product MICROTEK V6UL 0x80ac ScanMaker V6UL
+
+/* Midiman products */
+product MIDIMAN MIDISPORT2X2 0x1001 Midisport 2x2
+
+/* Minolta Co., Ltd. */
+product MINOLTA 2300 0x4001 Dimage 2300
+product MINOLTA S304 0x4007 Dimage S304
+product MINOLTA X 0x4009 Dimage X
+product MINOLTA 5400 0x400e Dimage 5400
+
+/* Micro Star International products */
+product MSI BT_DONGLE 0x1967 Bluetooth USB dongle
+
+/* Microtune, Inc. products */
+product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle
+
+/* Mitsumi products */
+product MITSUMI CDRRW 0x0000 CD-R/RW Drive
+product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle
+
+/* Motorola products */
+product MOTOROLA MC141555 0x1555 MC141555 hub controller
+product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem
+
+/* MultiTech products */
+product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem
+
+/* Mustek products */
+product MUSTEK 1200CU 0x0001 1200 CU scanner
+product MUSTEK 600CU 0x0002 600 CU scanner
+product MUSTEK 1200USB 0x0003 1200 USB scanner
+product MUSTEK 1200UB 0x0006 1200 UB scanner
+product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner
+product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner
+product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner
+product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner
+product MUSTEK 600USB 0x0873 600 USB scanner
+product MUSTEK MDC800 0xa800 MDC-800 digital camera
+
+/* M-Systems products */
+product MSYSTEMS DISKONKEY 0x0010 DiskOnKey
+product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey
+
+/* National Semiconductor */
+product NATIONAL BEARPAW1200 0x1000 BearPaw 1200
+product NATIONAL BEARPAW2400 0x1001 BearPaw 2400
+
+/* NEC products */
+product NEC HUB 0x55aa hub
+product NEC HUB_B 0x55ab hub
+
+/* NEODIO products */
+product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller
+product NEODIO ND5010 0x5010 Multi-format Flash Controller
+
+/* NetChip Technology Products */
+product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect
+
+/* Netgear products */
+product NETGEAR EA101 0x1001 Ethernet adapter
+product NETGEAR FA120 0x1040 USB 2.0 Ethernet adapter
+
+/* Nikon products */
+product NIKON E990 0x0102 Digital Camera E990
+
+/* Olympus products */
+product OLYMPUS C1 0x0102 C-1 Digital Camera
+product OLYMPUS C700 0x0105 C-700 Ultra Zoom
+
+/* OmniVision Technologies, Inc. products */
+product OMNIVISION OV511 0x0511 OV511 Camera
+product OMNIVISION OV511PLUS 0xa511 OV511+ Camera
+
+/* OnSpec Electronic, Inc. */
+product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader
+
+/* Palm Computing, Inc. product */
+product PALM SERIAL 0x0080 USB Serial Adaptor
+product PALM M500 0x0001 Palm m500
+product PALM M505 0x0002 Palm m505
+product PALM M515 0x0003 Palm m515
+product PALM I705 0x0020 Palm i705
+product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z
+product PALM M125 0x0040 Palm m125
+product PALM M130 0x0050 Palm m130
+product PALM TUNGSTEN_T 0x0060 Palm Tungsten T
+product PALM ZIRE31 0x0061 Palm Zire 31
+product PALM ZIRE 0x0070 Palm Zire
+
+/* Panasonic products */
+product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN
+product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN
+product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW
+product PANASONIC SDCAAE 0x1b00 MultiMediaCard Adapter
+
+/* Peracom products */
+product PERACOM SERIAL1 0x0001 Serial Converter
+product PERACOM ENET 0x0002 Ethernet adapter
+product PERACOM ENET3 0x0003 At Home Ethernet Adapter
+product PERACOM ENET2 0x0005 Ethernet adapter
+
+/* Philips products */
+product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System
+product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System
+product PHILIPS HUB 0x0201 hub
+product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera
+product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera
+product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System
+product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit
+product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player
+
+/* Philips Semiconductor products */
+product PHILIPSSEMI HUB1122 0x1122 hub
+
+/* P.I. Engineering products */
+product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter
+
+/* Plextor Corp. */
+product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U
+
+/* PLX products */
+product PLX TESTBOARD 0x9060 test board
+
+/* PNY products */
+product PNY ATTACHE 0x1300 USB 2.0 Flash Drive
+
+/* Primax products */
+product PRIMAX G2X300 0x0300 G2-200 scanner
+product PRIMAX G2E300 0x0301 G2E-300 scanner
+product PRIMAX G2300 0x0302 G2-300 scanner
+product PRIMAX G2E3002 0x0303 G2E-300 scanner
+product PRIMAX 9600 0x0340 Colorado USB 9600 scanner
+product PRIMAX 600U 0x0341 Colorado 600u scanner
+product PRIMAX 6200 0x0345 Visioneer 6200 scanner
+product PRIMAX 19200 0x0360 Colorado USB 19200 scanner
+product PRIMAX 1200U 0x0361 Colorado 1200u scanner
+product PRIMAX G600 0x0380 G2-600 scanner
+product PRIMAX 636I 0x0381 ReadyScan 636i
+product PRIMAX G2600 0x0382 G2-600 scanner
+product PRIMAX G2E600 0x0383 G2E-600 scanner
+product PRIMAX COMFORT 0x4d01 Comfort
+product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box
+product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1
+
+/* Prolific products */
+product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface
+product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface
+product PROLIFIC RSAQ2 0x04bb PL2303 Serial adapter (IODATA USB-RSAQ2)
+product PROLIFIC PL2303 0x2303 PL2303 Serial adapter (ATEN/IOGEAR UC232A)
+product PROLIFIC PL2305 0x2305 Parallel printer adapter
+product PROLIFIC ATAPI4 0x2307 ATAPI-4 Bridge Controller
+
+/* Putercom products */
+product PUTERCOM UPA100 0x047e USB-1284 BRIDGE
+
+/* Qtronix products */
+product QTRONIX 980N 0x2011 Scorpion-980N keyboard
+
+/* Quickshot products */
+product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad
+
+/* Rainbow Technologies products */
+product RAINBOW IKEY2000 0x1200 i-Key 2000
+
+/* ReakTek products */
+product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet (GREEN HOUSE)
+
+/* Roland products */
+product ROLAND UM1 0x0009 UM-1 MIDI I/F
+product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native)
+product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic)
+
+/* Rockfire products */
+product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB
+
+/* RATOC Systems products */
+product RATOC REXUSB60 0xb000 USB serial adapter REX-USB60
+
+/* Samsung products */
+product SAMSUNG ML6060 0x3008 ML-6060 laser printer
+
+/* SanDisk products */
+product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a
+product SANDISK SDDR05 0x0005 ImageMate SDDR-05
+product SANDISK SDDR31 0x0002 ImageMate SDDR-31
+product SANDISK SDDR12 0x0100 ImageMate SDDR-12
+product SANDISK SDDR09 0x0200 ImageMate SDDR-09
+product SANDISK SDDR75 0x0810 ImageMate SDDR-75
+
+/* Sanyo Electric products */
+product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone
+
+/* ScanLogic products */
+product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter
+product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner
+
+/* Shuttle Technology products */
+product SHUTTLE EUSB 0x0001 E-USB Bridge
+product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge
+product SHUTTLE SDDR09 0x0003 ImageMate SDDR09
+product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter
+product SHUTTLE HIFD 0x0007 Sony Hifd
+product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter
+product SHUTTLE CF 0x000a eUSB CompactFlash Adapter
+product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge
+product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge
+product SHUTTLE CDRW 0x0101 CD-RW Device
+product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader
+
+/* Siemens products */
+product SIEMENS SPEEDSTREAM 0x1001 SpeedStream USB
+
+/* Sigmatel products */
+product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player
+
+/* SIIG products */
+product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader
+
+/* Silicon Portals Inc. */
+product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware)
+product SILICONPORTALS YAPPHONE 0x0201 YAP Phone
+
+/* Sirius Technologies products */
+product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB
+
+/* SmartBridges products */
+product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB ethernet adapter
+product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP Adapter
+
+/* SMC products */
+product SMC 2102USB 0x0100 10Mbps ethernet adapter
+product SMC 2202USB 0x0200 10/100 ethernet adapter
+product SMC 2206USB 0x0201 EZ Connect USB Ethernet Adapter
+product SMC2 2020HUB 0x2020 USB Hub
+product SMC3 2662WUSB 0xa002 2662W-AR Wireless Adapter
+
+/* SOHOware products */
+product SOHOWARE NUB100 0x9100 10/100 USB Ethernet
+
+/* SOLID YEAR products */
+product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard
+
+/* SONY products */
+product SONY DSC 0x0010 DSC cameras
+product SONY MSACUS1 0x002d Memorystick MSAC-US1
+product SONY MSC 0x0032 MSC memory stick slot
+product SONY CLIE_35 0x0038 Sony Clie v3.5
+product SONY CLIE_40 0x0066 Sony Clie v4.0
+product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot
+product SONY CLIE_S360 0x0095 Sony Clie s360
+product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot
+product SONY CLIE_41 0x009a Sony Clie v4.1
+product SONY CLIE_NX60 0x00da Sony Clie nx60
+
+/* SOURCENEXT products */
+product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8
+product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger
+
+/* STMicroelectronics products */
+product STMICRO COMMUNICATOR 0x7554 USB Communicator
+
+/* STSN products */
+product STSN STSN0001 0x0001 Internet Access Device
+
+/* SUN Corporation products */
+product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2
+product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1
+product SUNTAC VS10U 0x0009 SUNTAC Slipper U
+product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity
+product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3
+product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4
+
+/* Sun Microsystems products */
+product SUN KEYBOARD 0x0005 Type 6 USB keyboard
+/* XXX The above is a North American PC style keyboard possibly */
+product SUN MOUSE 0x0100 Type 6 USB mouse
+
+/* Supra products */
+product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem
+product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem
+product DIAMOND2 RIO600USB 0x5001 Rio 600 USB
+product DIAMOND2 RIO800USB 0x5002 Rio 800 USB
+
+/* Taugagreining products */
+product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB)
+
+/* TDK products */
+product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664
+product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464
+product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400
+product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400
+product TDK BT_DONGLE 0x0309 Bluetooth USB dongle
+
+/* TEAC products */
+product TEAC FD05PUB 0x0000 FD-05PUB floppy
+
+/* Telex Communications products */
+product TELEX MIC1 0x0001 Enhanced USB Microphone
+
+/* Texas Intel products */
+product TI UTUSB41 0x1446 UT-USB41 hub
+product TI TUSB2046 0x2046 TUSB2046 hub
+
+/* Thrustmaster products */
+product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad
+
+/* Toshiba Corporation products */
+product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740
+
+/* Trek Technology products */
+product TREK THUMBDRIVE 0x1111 ThumbDrive
+product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB
+
+/* Ultima products */
+product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner
+
+/* UMAX products */
+product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner
+product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner
+product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner
+product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner
+product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner
+product UMAX ASTRA3400 0x0060 Astra 3400 Scanner
+
+/* Universal Access products */
+product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter
+
+/* VidzMedia products */
+product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H
+
+/* Vision products */
+product VISION VC6452V002 0x0002 CPiA Camera
+
+/* Visioneer products */
+product VISIONEER 7600 0x0211 OneTouch 7600
+product VISIONEER 5300 0x0221 OneTouch 5300
+product VISIONEER 3000 0x0224 Scanport 3000
+product VISIONEER 6100 0x0231 OneTouch 6100
+product VISIONEER 6200 0x0311 OneTouch 6200
+product VISIONEER 8100 0x0321 OneTouch 8100
+product VISIONEER 8600 0x0331 OneTouch 8600
+
+/* Wacom products */
+product WACOM CT0405U 0x0000 CT-0405-U Tablet
+product WACOM GRAPHIRE 0x0010 Graphire
+product WACOM INTUOSA5 0x0021 Intuos A5
+product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet
+
+/* Xirlink products */
+product XIRLINK PCCAM 0x8080 IBM PC Camera
+
+/* Y-E Data products */
+product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U
+
+/* Yamaha products */
+product YAMAHA UX256 0x1000 UX256 MIDI I/F
+product YAMAHA UX96 0x1008 UX96 MIDI I/F
+product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router
+product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router
+product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router
+product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router
+
+/* Yano products */
+product YANO U640MO 0x0101 U640MO-03
+
+/* Zoom Telephonics, Inc. products */
+product ZOOM 2986L 0x9700 2986L Fax modem
+
+/* ZyXEL Communication Co. products */
+product ZYXEL OMNI56K 0x1500 Omni 56K Plus
+product ZYXEL 980N 0x2011 Scorpion-980N keyboard
diff --git a/sys/dev/usb/usbdevs.h b/sys/dev/usb/usbdevs.h
new file mode 100644
index 0000000..61ea051
--- /dev/null
+++ b/sys/dev/usb/usbdevs.h
@@ -0,0 +1,1354 @@
+/* $FreeBSD$ */
+
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
+ *
+ * generated from:
+ * FreeBSD: src/sys/dev/usb/usbdevs,v 1.183 2004/06/24 05:05:56 jb Exp
+ */
+
+/*
+ * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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
+ *
+ * Adding an ID and its string description for a device to the usbdevs file,
+ * enables the USB stack to print a useful description of the device that was
+ * connected.
+ *
+ * The ID should be added in usbdevs and then the files usbdevs.h and
+ * usbdevs_data.h need to be regenerated.
+ *
+ * # edit usbdevs
+ * make -f Makefile.usbdevs
+ * # test your change
+ * cd ../../modules/usb
+ * make
+ * # commit, if appropriate
+ * cvs -m "ID for device XYZ" commit usbdevs
+ * # commit the derived files after the $ FreeBSD $ has been updated.
+ * cvs commit usbdevs.h usbdevs_data.h
+ *
+ * Please note that these IDs do not do anything. Adding an ID here and
+ * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name
+ * available to the source code and does not change any functionality, nor
+ * does it make your device available to a specific driver.
+ * It will however make the descriptive string available if a device does not
+ * provide the string itself.
+ *
+ * After adding a vendor ID VNDR and a product ID PRDCT you will have the
+ * following extra defines:
+ * #define USB_VENDOR_VNDR 0x????
+ * #define USB_PRODUCT_VNDR_PRDCT 0x????
+ *
+ * You may have to add these defines to the respective probe routines to
+ * make the device recognised by the appropriate device driver.
+ */
+
+#define USB_VENDOR_AOX 0x03e8 /* AOX */
+#define USB_VENDOR_ATMEL 0x03eb /* Atmel */
+#define USB_VENDOR_MITSUMI 0x03ee /* Mitsumi */
+#define USB_VENDOR_HP 0x03f0 /* Hewlett Packard */
+#define USB_VENDOR_ADAPTEC 0x03f3 /* Adaptec */
+#define USB_VENDOR_NATIONAL 0x0400 /* National Semiconductor */
+#define USB_VENDOR_ACERLABS 0x0402 /* Acer Labs */
+#define USB_VENDOR_FTDI 0x0403 /* Future Technology Devices */
+#define USB_VENDOR_NEC 0x0409 /* NEC */
+#define USB_VENDOR_KODAK 0x040a /* Eastman Kodak */
+#define USB_VENDOR_MELCO 0x0411 /* Melco */
+#define USB_VENDOR_CREATIVE 0x041e /* Creative */
+#define USB_VENDOR_ADI 0x0422 /* ADI Systems */
+#define USB_VENDOR_CATC 0x0423 /* Computer Access Technology */
+#define USB_VENDOR_SMC2 0x0424 /* Standard Microsystems */
+#define USB_VENDOR_GRAVIS 0x0428 /* Advanced Gravis Computer Tech. */
+#define USB_VENDOR_SUN 0x0430 /* Sun Microsystems */
+#define USB_VENDOR_TAUGA 0x0436 /* Taugagreining HF */
+#define USB_VENDOR_AMD 0x0438 /* Advanced Micro Devices */
+#define USB_VENDOR_LEXMARK 0x043d /* Lexmark International */
+#define USB_VENDOR_NANAO 0x0440 /* NANAO */
+#define USB_VENDOR_ALPS 0x044e /* Alps Electric */
+#define USB_VENDOR_THRUST 0x044f /* Thrustmaster */
+#define USB_VENDOR_TI 0x0451 /* Texas Instruments */
+#define USB_VENDOR_ANALOGDEVICES 0x0456 /* Analog Devices */
+#define USB_VENDOR_KYE 0x0458 /* KYE Systems */
+#define USB_VENDOR_DIAMOND2 0x045a /* Diamond (Supra) */
+#define USB_VENDOR_MICROSOFT 0x045e /* Microsoft */
+#define USB_VENDOR_PRIMAX 0x0461 /* Primax Electronics */
+#define USB_VENDOR_AMP 0x0464 /* AMP */
+#define USB_VENDOR_CHERRY 0x046a /* Cherry Mikroschalter */
+#define USB_VENDOR_MEGATRENDS 0x046b /* American Megatrends */
+#define USB_VENDOR_LOGITECH 0x046d /* Logitech */
+#define USB_VENDOR_BTC 0x046e /* Behavior Tech. Computer */
+#define USB_VENDOR_PHILIPS 0x0471 /* Philips */
+#define USB_VENDOR_SANYO 0x0474 /* Sanyo Electric */
+#define USB_VENDOR_CONNECTIX 0x0478 /* Connectix */
+#define USB_VENDOR_KENSINGTON 0x047d /* Kensington */
+#define USB_VENDOR_LUCENT 0x047e /* Lucent */
+#define USB_VENDOR_KYOCERA 0x0482 /* Kyocera Corp. */
+#define USB_VENDOR_STMICRO 0x0483 /* STMicroelectronics */
+#define USB_VENDOR_YAMAHA 0x0499 /* YAMAHA */
+#define USB_VENDOR_COMPAQ 0x049f /* Compaq Computers */
+#define USB_VENDOR_HITACHI 0x04a4 /* Hitachi, Ltd. */
+#define USB_VENDOR_ACERP 0x04a5 /* Acer Peripherals */
+#define USB_VENDOR_VISIONEER 0x04a7 /* Visioneer */
+#define USB_VENDOR_CANON 0x04a9 /* Canon */
+#define USB_VENDOR_NIKON 0x04b0 /* Nikon */
+#define USB_VENDOR_IBM 0x04b3 /* IBM Corporation */
+#define USB_VENDOR_CYPRESS 0x04b4 /* Cypress Semiconductor */
+#define USB_VENDOR_EPSON 0x04b8 /* Seiko Epson */
+#define USB_VENDOR_RAINBOW 0x04b9 /* Rainbow Technologies */
+#define USB_VENDOR_IODATA 0x04bb /* I/O Data */
+#define USB_VENDOR_TDK 0x04bf /* TDK */
+#define USB_VENDOR_3COMUSR 0x04c1 /* U.S. Robotics */
+#define USB_VENDOR_METHODE 0x04c2 /* Methode Electronics Far East */
+#define USB_VENDOR_MAXISWITCH 0x04c3 /* Maxi Switch */
+#define USB_VENDOR_LOCKHEEDMER 0x04c4 /* Lockheed Martin Energy Research */
+#define USB_VENDOR_FUJITSU 0x04c5 /* Fujitsu */
+#define USB_VENDOR_TOSHIBAAM 0x04c6 /* Toshiba America Electronic Components */
+#define USB_VENDOR_MICROMACRO 0x04c7 /* Micro Macro Technologies */
+#define USB_VENDOR_KONICA 0x04c8 /* Konica */
+#define USB_VENDOR_LITEON 0x04ca /* Lite-On Technology */
+#define USB_VENDOR_FUJIPHOTO 0x04cb /* Fuji Photo Film */
+#define USB_VENDOR_PHILIPSSEMI 0x04cc /* Philips Semiconductors */
+#define USB_VENDOR_TATUNG 0x04cd /* Tatung Co. Of America */
+#define USB_VENDOR_SCANLOGIC 0x04ce /* ScanLogic */
+#define USB_VENDOR_MYSON 0x04cf /* Myson Technology */
+#define USB_VENDOR_DIGI2 0x04d0 /* Digi International */
+#define USB_VENDOR_ITTCANON 0x04d1 /* ITT Canon */
+#define USB_VENDOR_ALTEC 0x04d2 /* Altec Lansing Technologies */
+#define USB_VENDOR_PANASONIC 0x04da /* Panasonic (Matsushita) */
+#define USB_VENDOR_IIYAMA 0x04e1 /* Iiyama */
+#define USB_VENDOR_SHUTTLE 0x04e6 /* Shuttle Technology */
+#define USB_VENDOR_SAMSUNG 0x04e8 /* Samsung Electronics */
+#define USB_VENDOR_ANNABOOKS 0x04ed /* Annabooks */
+#define USB_VENDOR_JVC 0x04f1 /* JVC */
+#define USB_VENDOR_CHICONY 0x04f2 /* Chicony Electronics */
+#define USB_VENDOR_BROTHER 0x04f9 /* Brother Industries */
+#define USB_VENDOR_DALLAS 0x04fa /* Dallas Semiconductor */
+#define USB_VENDOR_ACER 0x0502 /* Acer */
+#define USB_VENDOR_3COM 0x0506 /* 3Com */
+#define USB_VENDOR_AZTECH 0x0509 /* Aztech Systems */
+#define USB_VENDOR_BELKIN 0x050d /* Belkin Components */
+#define USB_VENDOR_KAWATSU 0x050f /* Kawatsu Semiconductor */
+#define USB_VENDOR_APC 0x051d /* American Power Conversion */
+#define USB_VENDOR_CONNECTEK 0x0522 /* Advanced Connectek USA */
+#define USB_VENDOR_NETCHIP 0x0525 /* NetChip Technology */
+#define USB_VENDOR_ALTRA 0x0527 /* ALTRA */
+#define USB_VENDOR_ATI 0x0528 /* ATI Technologies */
+#define USB_VENDOR_AKS 0x0529 /* Aladdin Knowledge Systems */
+#define USB_VENDOR_UNIACCESS 0x0540 /* Universal Access */
+#define USB_VENDOR_XIRLINK 0x0545 /* Xirlink */
+#define USB_VENDOR_ANCHOR 0x0547 /* Anchor Chips */
+#define USB_VENDOR_SONY 0x054c /* Sony */
+#define USB_VENDOR_VISION 0x0553 /* VLSI Vision */
+#define USB_VENDOR_ASAHIKASEI 0x0556 /* Asahi Kasei Microsystems */
+#define USB_VENDOR_ATEN 0x0557 /* ATEN International */
+#define USB_VENDOR_MUSTEK 0x055f /* Mustek Systems */
+#define USB_VENDOR_TELEX 0x0562 /* Telex Communications */
+#define USB_VENDOR_PERACOM 0x0565 /* Peracom Networks */
+#define USB_VENDOR_ALCOR2 0x0566 /* Alcor Micro */
+#define USB_VENDOR_WACOM 0x056a /* WACOM */
+#define USB_VENDOR_ETEK 0x056c /* e-TEK Labs */
+#define USB_VENDOR_EIZO 0x056d /* EIZO */
+#define USB_VENDOR_ELECOM 0x056e /* Elecom */
+#define USB_VENDOR_HAUPPAUGE 0x0573 /* Hauppauge Computer Works */
+#define USB_VENDOR_BAFO 0x0576 /* BAFO/Quality Computer Accessories */
+#define USB_VENDOR_YEDATA 0x057b /* Y-E Data */
+#define USB_VENDOR_AVM 0x057c /* AVM GmbH */
+#define USB_VENDOR_QUICKSHOT 0x057f /* Quickshot */
+#define USB_VENDOR_ROLAND 0x0582 /* Roland */
+#define USB_VENDOR_ROCKFIRE 0x0583 /* Rockfire */
+#define USB_VENDOR_RATOC 0x0584 /* RATOC Systems, Inc. */
+#define USB_VENDOR_ZYXEL 0x0586 /* ZyXEL Communication */
+#define USB_VENDOR_ALCOR 0x058f /* Alcor Micro */
+#define USB_VENDOR_IOMEGA 0x059b /* Iomega */
+#define USB_VENDOR_ATREND 0x059c /* A-Trend Technology */
+#define USB_VENDOR_AID 0x059d /* Advanced Input Devices */
+#define USB_VENDOR_LACIE 0x059f /* LaCie */
+#define USB_VENDOR_OMNIVISION 0x05a9 /* OmniVision */
+#define USB_VENDOR_INSYSTEM 0x05ab /* In-System Design */
+#define USB_VENDOR_APPLE 0x05ac /* Apple Computer */
+#define USB_VENDOR_DIGI 0x05c5 /* Digi International */
+#define USB_VENDOR_QTRONIX 0x05c7 /* Qtronix */
+#define USB_VENDOR_ELSA 0x05cc /* ELSA */
+#define USB_VENDOR_BRAINBOXES 0x05d1 /* Brainboxes Limited */
+#define USB_VENDOR_ULTIMA 0x05d8 /* Ultima */
+#define USB_VENDOR_AXIOHM 0x05d9 /* Axiohm Transaction Solutions */
+#define USB_VENDOR_MICROTEK 0x05da /* Microtek */
+#define USB_VENDOR_SUNTAC 0x05db /* SUN Corporation */
+#define USB_VENDOR_LEXAR 0x05dc /* Lexar Media */
+#define USB_VENDOR_SYMBOL 0x05e0 /* Symbol Technologies */
+#define USB_VENDOR_GENESYS 0x05e3 /* Genesys Logic */
+#define USB_VENDOR_FUJI 0x05e5 /* Fuji Electric */
+#define USB_VENDOR_KEITHLEY 0x05e6 /* Keithley Instruments */
+#define USB_VENDOR_EIZONANAO 0x05e7 /* EIZO Nanao */
+#define USB_VENDOR_KLSI 0x05e9 /* Kawasaki LSI */
+#define USB_VENDOR_FFC 0x05eb /* FFC */
+#define USB_VENDOR_ANKO 0x05ef /* Anko Electronic */
+#define USB_VENDOR_PIENGINEERING 0x05f3 /* P.I. Engineering */
+#define USB_VENDOR_AOC 0x05f6 /* AOC International */
+#define USB_VENDOR_CHIC 0x05fe /* Chic Technology */
+#define USB_VENDOR_BARCO 0x0600 /* Barco Display Systems */
+#define USB_VENDOR_BRIDGE 0x0607 /* Bridge Information */
+#define USB_VENDOR_SOLIDYEAR 0x060b /* Solid Year */
+#define USB_VENDOR_BIORAD 0x0614 /* Bio-Rad Laboratories */
+#define USB_VENDOR_MACALLY 0x0618 /* Macally */
+#define USB_VENDOR_ACTLABS 0x061c /* Act Labs */
+#define USB_VENDOR_ALARIS 0x0620 /* Alaris */
+#define USB_VENDOR_APEX 0x0624 /* Apex */
+#define USB_VENDOR_AVISION 0x0638 /* Avision */
+#define USB_VENDOR_TEAC 0x0644 /* TEAC */
+#define USB_VENDOR_LINKSYS 0x066b /* Linksys */
+#define USB_VENDOR_ACERSA 0x066e /* Acer Semiconductor America */
+#define USB_VENDOR_SIGMATEL 0x066f /* Sigmatel */
+#define USB_VENDOR_AIWA 0x0677 /* Aiwa */
+#define USB_VENDOR_ACARD 0x0678 /* ACARD Technology */
+#define USB_VENDOR_PROLIFIC 0x067b /* Prolific Technology */
+#define USB_VENDOR_SIEMENS 0x067c /* Siemens */
+#define USB_VENDOR_ADVANCELOGIC 0x0680 /* Avance Logic */
+#define USB_VENDOR_HAGIWARA 0x0693 /* Hagiwara Sys-Com */
+#define USB_VENDOR_MINOLTA 0x0686 /* Minolta */
+#define USB_VENDOR_CTX 0x0698 /* Chuntex */
+#define USB_VENDOR_ASKEY 0x069a /* Askey Computer */
+#define USB_VENDOR_SAITEK 0x06a3 /* Saitek */
+#define USB_VENDOR_ALCATELT 0x06b9 /* Alcatel Telecom */
+#define USB_VENDOR_AGFA 0x06bd /* AGFA-Gevaert */
+#define USB_VENDOR_ASIAMD 0x06be /* Asia Microelectronic Development */
+#define USB_VENDOR_BIZLINK 0x06c4 /* Bizlink International */
+#define USB_VENDOR_KEYSPAN 0x06cd /* Keyspan */
+#define USB_VENDOR_AASHIMA 0x06d6 /* Aashima Technology */
+#define USB_VENDOR_MULTITECH 0x06e0 /* MultiTech */
+#define USB_VENDOR_ADS 0x06e1 /* ADS Technologies */
+#define USB_VENDOR_ALCATELM 0x06e4 /* Alcatel Microelectronics */
+#define USB_VENDOR_SIRIUS 0x06ea /* Sirius Technologies */
+#define USB_VENDOR_BOSTON 0x06fd /* Boston Acoustics */
+#define USB_VENDOR_SMC 0x0707 /* Standard Microsystems */
+#define USB_VENDOR_PUTERCOM 0x0708 /* Putercom */
+#define USB_VENDOR_MCT 0x0711 /* MCT */
+#define USB_VENDOR_DIGITALSTREAM 0x074e /* Digital Stream */
+#define USB_VENDOR_AUREAL 0x0755 /* Aureal Semiconductor */
+#define USB_VENDOR_MIDIMAN 0x0763 /* Midiman */
+#define USB_VENDOR_LINKSYS2 0x077b /* Linksys */
+#define USB_VENDOR_GRIFFIN 0x077d /* Griffin Technology */
+#define USB_VENDOR_SANDISK 0x0781 /* SanDisk Corp */
+#define USB_VENDOR_LOGITEC 0x0789 /* Logitec Corp */
+#define USB_VENDOR_BRIMAX 0x078e /* Brimax */
+#define USB_VENDOR_AXIS 0x0792 /* Axis Communications */
+#define USB_VENDOR_ABL 0x0794 /* ABL Electronics */
+#define USB_VENDOR_ALFADATA 0x079d /* Alfadata Computer */
+#define USB_VENDOR_NATIONALTECH 0x07a2 /* National Technical Systems */
+#define USB_VENDOR_ONNTO 0x07a3 /* Onnto */
+#define USB_VENDOR_BE 0x07a4 /* Be */
+#define USB_VENDOR_ADMTEK 0x07a6 /* ADMtek */
+#define USB_VENDOR_COREGA 0x07aa /* Corega */
+#define USB_VENDOR_FREECOM 0x07ab /* Freecom */
+#define USB_VENDOR_MICROTECH 0x07af /* Microtech */
+#define USB_VENDOR_GENERALINSTMNTS 0x07b2 /* General Instruments (Motorola) */
+#define USB_VENDOR_OLYMPUS 0x07b4 /* Olympus */
+#define USB_VENDOR_ONSPEC 0x07c4 /* OnSpec Electronic */
+#define USB_VENDOR_ABOCOM 0x07b8 /* AboCom Systems */
+#define USB_VENDOR_KEISOKUGIKEN 0x07c1 /* Keisokugiken */
+#define USB_VENDOR_APG 0x07c5 /* APG Cash Drawer */
+#define USB_VENDOR_BUG 0x07c8 /* B.U.G. */
+#define USB_VENDOR_ALLIEDTELESYN 0x07c9 /* Allied Telesyn International */
+#define USB_VENDOR_AVERMEDIA 0x07ca /* AVerMedia Technologies */
+#define USB_VENDOR_SIIG 0x07cc /* SIIG */
+#define USB_VENDOR_CASIO 0x07cf /* CASIO */
+#define USB_VENDOR_APTIO 0x07d2 /* Aptio Products */
+#define USB_VENDOR_ARASAN 0x07da /* Arasan Chip Systems */
+#define USB_VENDOR_ALLIEDCABLE 0x07e6 /* Allied Cable */
+#define USB_VENDOR_STSN 0x07ef /* STSN */
+#define USB_VENDOR_ZOOM 0x0803 /* Zoom Telephonics */
+#define USB_VENDOR_BROADLOGIC 0x0827 /* BroadLogic */
+#define USB_VENDOR_HANDSPRING 0x082d /* Handspring */
+#define USB_VENDOR_ACTIONSTAR 0x0835 /* Action Star Enterprise */
+#define USB_VENDOR_PALM 0x0830 /* Palm Computing */
+#define USB_VENDOR_SOURCENEXT 0x0833 /* SOURCENEXT */
+#define USB_VENDOR_ACCTON 0x083a /* Accton Technology */
+#define USB_VENDOR_DIAMOND 0x0841 /* Diamond */
+#define USB_VENDOR_NETGEAR 0x0846 /* BayNETGEAR */
+#define USB_VENDOR_ACTIVEWIRE 0x0854 /* ActiveWire */
+#define USB_VENDOR_PORTGEAR 0x085a /* PortGear */
+#define USB_VENDOR_METRICOM 0x0870 /* Metricom */
+#define USB_VENDOR_ADESSOKBTEK 0x087c /* ADESSO/Kbtek America */
+#define USB_VENDOR_JATON 0x087d /* Jaton */
+#define USB_VENDOR_APT 0x0880 /* APT Technologies */
+#define USB_VENDOR_BOCARESEARCH 0x0885 /* Boca Research */
+#define USB_VENDOR_ANDREA 0x08a8 /* Andrea Electronics */
+#define USB_VENDOR_BURRBROWN 0x08bb /* Burr-Brown Japan */
+#define USB_VENDOR_2WIRE 0x08c8 /* 2Wire */
+#define USB_VENDOR_AIPTEK 0x08ca /* AIPTEK International */
+#define USB_VENDOR_SMARTBRIDGES 0x08d1 /* SmartBridges */
+#define USB_VENDOR_BILLIONTON 0x08dd /* Billionton Systems */
+#define USB_VENDOR_EXTENDED 0x08e9 /* Extended Systems */
+#define USB_VENDOR_MSYSTEMS 0x08ec /* M-Systems */
+#define USB_VENDOR_AUTHENTEC 0x08ff /* AuthenTec */
+#define USB_VENDOR_ALATION 0x0910 /* Alation Systems */
+#define USB_VENDOR_GOHUBS 0x0921 /* GoHubs */
+#define USB_VENDOR_BIOMETRIC 0x0929 /* American Biometric Company */
+#define USB_VENDOR_TOSHIBA 0x0930 /* Toshiba Corporation */
+#define USB_VENDOR_PLEXTOR 0x093b /* Plextor Corp. */
+#define USB_VENDOR_YANO 0x094f /* Yano */
+#define USB_VENDOR_KINGSTON 0x0951 /* Kingston Technology */
+#define USB_VENDOR_BLUEWATER 0x0956 /* BlueWater Systems */
+#define USB_VENDOR_AGILENT 0x0957 /* Agilent Technologies */
+#define USB_VENDOR_PORTSMITH 0x095a /* Portsmith */
+#define USB_VENDOR_ADIRONDACK 0x0976 /* Adirondack Wire & Cable */
+#define USB_VENDOR_BECKHOFF 0x0978 /* Beckhoff */
+#define USB_VENDOR_INTERSIL 0x09aa /* Intersil */
+#define USB_VENDOR_ALTIUS 0x09b3 /* Altius Solutions */
+#define USB_VENDOR_ARRIS 0x09c1 /* Arris Interactive */
+#define USB_VENDOR_ACTIVCARD 0x09c3 /* ACTIVCARD */
+#define USB_VENDOR_ACTISYS 0x09c4 /* ACTiSYS */
+#define USB_VENDOR_AFOURTECH 0x09da /* A-FOUR TECH */
+#define USB_VENDOR_AIMEX 0x09dc /* AIMEX */
+#define USB_VENDOR_ADDONICS 0x09df /* Addonics Technologies */
+#define USB_VENDOR_AKAI 0x09e8 /* AKAI professional M.I. */
+#define USB_VENDOR_ARESCOM 0x09f5 /* ARESCOM */
+#define USB_VENDOR_BAY 0x09f9 /* Bay Associates */
+#define USB_VENDOR_ALTERA 0x09fb /* Altera */
+#define USB_VENDOR_CSR 0x0a12 /* Cambridge Silicon Radio Ltd. */
+#define USB_VENDOR_TREK 0x0a16 /* Trek Technology */
+#define USB_VENDOR_ASAHIOPTICAL 0x0a17 /* Asahi Optical */
+#define USB_VENDOR_BOCASYSTEMS 0x0a43 /* Boca Systems */
+#define USB_VENDOR_BROADCOM 0x0a5c /* Broadcom */
+#define USB_VENDOR_GREENHOUSE 0x0a6b /* GREENHOUSE */
+#define USB_VENDOR_GEOCAST 0x0a79 /* Geocast Network Systems */
+#define USB_VENDOR_NEODIO 0x0aec /* Neodio */
+#define USB_VENDOR_TODOS 0x0b0c /* Todos Data System */
+#define USB_VENDOR_HAL 0x0b41 /* HAL Corporation */
+#define USB_VENDOR_EMS 0x0b43 /* EMS Production Ltd. */
+#define USB_VENDOR_NEC2 0x0b62 /* NEC */
+#define USB_VENDOR_ATI2 0x0b6f /* ATI */
+#define USB_VENDOR_ASIX 0x0b95 /* ASIX Electronics */
+#define USB_VENDOR_REALTEK 0x0bda /* RealTek */
+#define USB_VENDOR_AGATE 0x0c08 /* Agate Technologies */
+#define USB_VENDOR_DMI 0x0c0b /* DMI */
+#define USB_VENDOR_LUWEN 0x0c76 /* Luwen */
+#define USB_VENDOR_SMC3 0x0d5c /* Standard Microsystems */
+#define USB_VENDOR_PNY 0x0d7d /* PNY */
+#define USB_VENDOR_MSI 0x0db0 /* Micro Star International */
+#define USB_VENDOR_HAWKING 0x0e66 /* Hawking Technologies */
+#define USB_VENDOR_MICROTUNE 0x0f4d /* Microtune, Inc. */
+#define USB_VENDOR_QUALCOMM 0x1004 /* Qualcomm */
+#define USB_VENDOR_MOTOROLA 0x1063 /* Motorola */
+#define USB_VENDOR_PLX 0x10b5 /* PLX */
+#define USB_VENDOR_ASANTE 0x10bd /* Asante */
+#define USB_VENDOR_JRC 0x1145 /* Japan Radio Company */
+#define USB_VENDOR_DELORME 0x1163 /* Delorme Publishing */
+#define USB_VENDOR_ACERCM 0x1189 /* Acer Communications & Multimedia Inc. */
+#define USB_VENDOR_BELKIN2 0x1293 /* Belkin Components */
+#define USB_VENDOR_MOBILITY 0x1342 /* Mobility */
+#define USB_VENDOR_SHARK 0x13d2 /* Shark */
+#define USB_VENDOR_SILICONPORTALS 0x1527 /* Silicon Portals */
+#define USB_VENDOR_SOHOWARE 0x15e8 /* SOHOware */
+#define USB_VENDOR_UMAX 0x1606 /* UMAX Data Systems */
+#define USB_VENDOR_INSIDEOUT 0x1608 /* Inside Out Networks */
+#define USB_VENDOR_ENTREGA 0x1645 /* Entrega */
+#define USB_VENDOR_ACTIONTEC 0x1668 /* Actiontec Electronics */
+#define USB_VENDOR_DLINK 0x2001 /* D-Link */
+#define USB_VENDOR_VIDZMEDIA 0x3275 /* VidzMedia Pte Ltd */
+#define USB_VENDOR_DAISY 0x3579 /* Daisy Technology */
+#define USB_VENDOR_DELL 0x413c /* Dell */
+#define USB_VENDOR_INTEL 0x8086 /* Intel */
+#define USB_VENDOR_HP2 0xf003 /* Hewlett Packard */
+
+/*
+ * List of known products. Grouped by vendor.
+ */
+
+/* 3Com products */
+#define USB_PRODUCT_3COM_HOMECONN 0x009d /* HomeConnect USB Camera */
+#define USB_PRODUCT_3COM_3CREB96 0x00a0 /* Bluetooth USB dongle */
+#define USB_PRODUCT_3COM_3C19250 0x03E8 /* 3C19250 Ethernet adapter */
+#define USB_PRODUCT_3COM_USR56K 0x3021 /* U.S.Robotics 56000 Voice Faxmodem Pro */
+#define USB_PRODUCT_3COM_3C460 0x11f8 /* HomeConnect 3C460 */
+#define USB_PRODUCT_3COM_3C460B 0x4601 /* HomeConnect 3C460B */
+
+#define USB_PRODUCT_3COMUSR_OFFICECONN 0x0082 /* 3Com OfficeConnect Analog Modem */
+#define USB_PRODUCT_3COMUSR_USRISDN 0x008f /* 3Com U.S. Robotics Pro ISDN TA */
+#define USB_PRODUCT_3COMUSR_HOMECONN 0x009d /* 3Com HomeConnect camera */
+#define USB_PRODUCT_3COMUSR_USR56K 0x3021 /* U.S.Robotics 56000 Voice Faxmodem Pro */
+
+/* AboCom products */
+#define USB_PRODUCT_ABOCOM_XX1 0x110c /* XX1 */
+#define USB_PRODUCT_ABOCOM_XX2 0x200c /* XX2 */
+#define USB_PRODUCT_ABOCOM_URE450 0x4000 /* URE450 Ethernet Adapter */
+#define USB_PRODUCT_ABOCOM_UFE1000 0x4002 /* UFE1000 Fast Ethernet Adapter */
+#define USB_PRODUCT_ABOCOM_DSB650TX_PNA 0x4003 /* 1/10/100 ethernet adapter */
+#define USB_PRODUCT_ABOCOM_XX4 0x4004 /* XX4 */
+#define USB_PRODUCT_ABOCOM_XX5 0x4007 /* XX5 */
+#define USB_PRODUCT_ABOCOM_XX6 0x400b /* XX6 */
+#define USB_PRODUCT_ABOCOM_XX7 0x400c /* XX7 */
+#define USB_PRODUCT_ABOCOM_XX8 0x4102 /* XX8 */
+#define USB_PRODUCT_ABOCOM_XX9 0x4104 /* XX9 */
+#define USB_PRODUCT_ABOCOM_XX10 0xabc1 /* XX10 */
+
+/* Accton products */
+#define USB_PRODUCT_ACCTON_USB320_EC 0x1046 /* USB320-EC Ethernet Adapter */
+#define USB_PRODUCT_ACCTON_SS1001 0x5046 /* SpeedStream Ethernet Adapter */
+
+/* Acer Peripherals, Inc. products */
+#define USB_PRODUCT_ACERP_ACERSCAN_C310U 0x12a6 /* Acerscan C310U */
+#define USB_PRODUCT_ACERP_ACERSCAN_320U 0x2022 /* Acerscan 320U */
+#define USB_PRODUCT_ACERP_ACERSCAN_640U 0x2040 /* Acerscan 640U */
+#define USB_PRODUCT_ACERP_ACERSCAN_620U 0x2060 /* Acerscan 620U */
+#define USB_PRODUCT_ACERP_AWL300 0x9000 /* AWL300 Wireless adapter */
+#define USB_PRODUCT_ACERP_AWL400 0x9001 /* AWL400 Wireless adapter */
+
+/* ActiveWire, Inc. products */
+#define USB_PRODUCT_ACTIVEWIRE_IOBOARD 0x0100 /* I/O Board */
+#define USB_PRODUCT_ACTIVEWIRE_IOBOARD_FW1 0x0101 /* I/O Board, rev. 1 firmware */
+
+/* Actiontec, Inc. products */
+#define USB_PRODUCT_ACTIONTEC_UAT1 0x7605 /* UAT1 Wireless Ethernet adapter */
+
+/* ADMtek products */
+#define USB_PRODUCT_ADMTEK_PEGASUS 0x0986 /* AN986 USB Ethernet adapter */
+#define USB_PRODUCT_ADMTEK_PEGASUSII 0x8511 /* AN8511 USB Ethernet adapter */
+#define USB_PRODUCT_ADMTEK_PEGASUSII_2 0x8513 /* AN8513 USB Ethernet adapter */
+
+/* ADS products */
+#define USB_PRODUCT_ADS_UBS10BT 0x0008 /* UBS-10BT Ethernet adapter */
+
+/* Agate Technologies products */
+#define USB_PRODUCT_AGATE_QDRIVE 0x0378 /* Q-Drive */
+
+/* AGFA products */
+#define USB_PRODUCT_AGFA_SNAPSCAN1212U 0x0001 /* SnapScan 1212U */
+#define USB_PRODUCT_AGFA_SNAPSCAN1236U 0x0002 /* SnapScan 1236U */
+#define USB_PRODUCT_AGFA_SNAPSCANTOUCH 0x0100 /* SnapScan Touch */
+#define USB_PRODUCT_AGFA_SNAPSCAN1212U2 0x2061 /* SnapScan 1212U */
+#define USB_PRODUCT_AGFA_SNAPSCANE40 0x208d /* SnapScan e40 */
+#define USB_PRODUCT_AGFA_SNAPSCANE50 0x208f /* SnapScan e50 */
+#define USB_PRODUCT_AGFA_SNAPSCANE20 0x2091 /* SnapScan e20 */
+#define USB_PRODUCT_AGFA_SNAPSCANE25 0x2095 /* SnapScan e25 */
+#define USB_PRODUCT_AGFA_SNAPSCANE26 0x2097 /* SnapScan e26 */
+#define USB_PRODUCT_AGFA_SNAPSCANE52 0x20fd /* SnapScan e52 */
+
+/* AKS products */
+#define USB_PRODUCT_AKS_USBHASP 0x0001 /* USB-HASP 0.06 */
+
+/* Alcor Micro, Inc. products */
+#define USB_PRODUCT_ALCOR2_KBD_HUB 0x2802 /* Kbd Hub */
+
+#define USB_PRODUCT_ALCOR_MA_KBD_HUB 0x9213 /* MacAlly Kbd Hub */
+#define USB_PRODUCT_ALCOR_AU9814 0x9215 /* AU9814 Hub */
+#define USB_PRODUCT_ALCOR_SM_KBD 0x9410 /* MicroConnectors/StrongMan Keyboard */
+#define USB_PRODUCT_ALCOR_NEC_KBD_HUB 0x9472 /* NEC Kbd Hub */
+
+/* Altec Lansing products */
+#define USB_PRODUCT_ALTEC_ADA70 0x0070 /* ADA70 Speakers */
+#define USB_PRODUCT_ALTEC_ASC495 0xff05 /* ASC495 Speakers */
+
+/* American Power Conversion products */
+#define USB_PRODUCT_APC_UPSPRO500 0x0002 /* Back-UPS Pro 500 */
+
+/* Anchor products */
+#define USB_PRODUCT_ANCHOR_EZUSB 0x2131 /* EZUSB */
+#define USB_PRODUCT_ANCHOR_EZLINK 0x2720 /* EZLINK */
+
+/* AOX, Inc. products */
+#define USB_PRODUCT_AOX_USB101 0x0008 /* USB ethernet controller engine */
+
+/* Apple Computer products */
+#define USB_PRODUCT_APPLE_OPTMOUSE 0x0302 /* Optical mouse */
+#define USB_PRODUCT_APPLE_SPEAKERS 0x1101 /* Speakers */
+
+/* Asahi Optical products */
+#define USB_PRODUCT_ASAHIOPTICAL_OPTIO230 0x0004 /* Digital camera */
+#define USB_PRODUCT_ASAHIOPTICAL_OPTIO330 0x0006 /* Digital camera */
+
+/* ASIX Electronics products */
+#define USB_PRODUCT_ASIX_AX88172 0x1720 /* USB 2.0 10/100 ethernet controller */
+
+/* ATen products */
+#define USB_PRODUCT_ATEN_UC1284 0x2001 /* Parallel printer adapter */
+#define USB_PRODUCT_ATEN_UC10T 0x2002 /* 10Mbps ethernet adapter */
+#define USB_PRODUCT_ATEN_UC232A 0x2008 /* Serial adapter */
+
+/* Atmel Comp. products */
+#define USB_PRODUCT_ATMEL_UHB124 0x3301 /* UHB124 hub */
+#define USB_PRODUCT_ATMEL_DWL120 0x7603 /* DWL-120 Wireless adapter */
+#define USB_PRODUCT_ATMEL_BW002 0x7605 /* BW002 Wireless adapter */
+#define USB_PRODUCT_ATMEL_AT76C505A 0x7614 /* AT76c505a Wireless adapter */
+
+/* Avision products */
+#define USB_PRODUCT_AVISION_1200U 0x0268 /* 1200U scanner */
+
+/* Belkin products */
+/*product BELKIN F5U111 0x???? F5U111 Ethernet adapter*/
+#define USB_PRODUCT_BELKIN2_F5U002 0x0002 /* F5U002 Parallel printer adapter */
+#define USB_PRODUCT_BELKIN_USB2LAN 0x0121 /* USB to LAN Converter */
+#define USB_PRODUCT_BELKIN_F5U103 0x0103 /* F5U103 Serial adapter */
+#define USB_PRODUCT_BELKIN_F5U109 0x0109 /* F5U109 Serial adapter */
+#define USB_PRODUCT_BELKIN_F5U120 0x1203 /* F5U120-PC Hub */
+#define USB_PRODUCT_BELKIN_F5U208 0x0208 /* F5U208 VideoBus II */
+
+/* Billionton products */
+#define USB_PRODUCT_BILLIONTON_USB100 0x0986 /* USB100N 10/100 FastEthernet Adapter */
+#define USB_PRODUCT_BILLIONTON_USBLP100 0x0987 /* USB100LP */
+#define USB_PRODUCT_BILLIONTON_USBEL100 0x0988 /* USB100EL */
+#define USB_PRODUCT_BILLIONTON_USBE100 0x8511 /* USBE100 */
+
+/* Brother Industries products */
+#define USB_PRODUCT_BROTHER_HL1050 0x0002 /* HL-1050 laser printer */
+
+/* Behavior Technology Computer products */
+#define USB_PRODUCT_BTC_BTC7932 0x6782 /* Keyboard with mouse port */
+
+/* Broadcom products */
+#define USB_PRODUCT_BROADCOM_BCM2033 0x2033 /* BCM2033 Bluetooth USB dongle */
+
+/* Canon, Inc. products */
+#define USB_PRODUCT_CANON_N656U 0x2206 /* CanoScan N656U */
+#define USB_PRODUCT_CANON_N1220U 0x2207 /* CanoScan N1220U */
+#define USB_PRODUCT_CANON_N676U 0x220d /* CanoScan N676U */
+#define USB_PRODUCT_CANON_N1240U 0x220e /* CanoScan N1240U */
+#define USB_PRODUCT_CANON_S10 0x3041 /* PowerShot S10 */
+#define USB_PRODUCT_CANON_S100 0x3045 /* PowerShot S100 */
+#define USB_PRODUCT_CANON_S200 0x3065 /* PowerShot S200 */
+
+/* CATC products */
+#define USB_PRODUCT_CATC_NETMATE 0x000a /* Netmate ethernet adapter */
+#define USB_PRODUCT_CATC_NETMATE2 0x000c /* Netmate2 ethernet adapter */
+#define USB_PRODUCT_CATC_CHIEF 0x000d /* USB Chief Bus & Protocol Analyzer */
+#define USB_PRODUCT_CATC_ANDROMEDA 0x1237 /* Andromeda hub */
+
+/* CASIO products */
+#define USB_PRODUCT_CASIO_NAMELAND 0x4001 /* CASIO Nameland EZ-USB */
+
+/* Cherry products */
+#define USB_PRODUCT_CHERRY_MY3000KBD 0x0001 /* My3000 keyboard */
+#define USB_PRODUCT_CHERRY_MY3000HUB 0x0003 /* My3000 hub */
+#define USB_PRODUCT_CHERRY_CYBOARD 0x0004 /* CyBoard Keyboard */
+
+/* Chic Technology products */
+#define USB_PRODUCT_CHIC_MOUSE1 0x0001 /* mouse */
+#define USB_PRODUCT_CHIC_CYPRESS 0x0003 /* Cypress USB Mouse */
+
+/* Chicony products */
+#define USB_PRODUCT_CHICONY_KB8933 0x0001 /* KB-8933 keyboard */
+
+/* Compaq products */
+#define USB_PRODUCT_COMPAQ_PJB100 0x504a /* Personal Jukebox PJB100 */
+
+/* Connectix products */
+#define USB_PRODUCT_CONNECTIX_QUICKCAM 0x0001 /* QuickCam */
+
+/* Corega products */
+#define USB_PRODUCT_COREGA_ETHER_USB_T 0x0001 /* Ether USB-T */
+#define USB_PRODUCT_COREGA_FETHER_USB_TX 0x0004 /* FEther USB-TX */
+#define USB_PRODUCT_COREGA_FETHER_USB_TXS 0x000d /* FEther USB-TXS */
+#define USB_PRODUCT_COREGA_FETHER_USB_TXC 0x9601 /* FEther USB-TXC */
+
+/* Creative products */
+#define USB_PRODUCT_CREATIVE_NOMAD_II 0x1002 /* Nomad II MP3 player */
+
+/* Crystalfontz products */
+#define USB_PRODUCT_FTDI_CFA_631 0xfc0c /* Crystalfontz CFA-631 USB LCD */
+#define USB_PRODUCT_FTDI_CFA_632 0xfc08 /* Crystalfontz CFA-632 USB LCD */
+#define USB_PRODUCT_FTDI_CFA_633 0xfc0b /* Crystalfontz CFA-633 USB LCD */
+#define USB_PRODUCT_FTDI_CFA_634 0xfc09 /* Crystalfontz CFA-634 USB LCD */
+#define USB_PRODUCT_FTDI_SEMC_DSS20 0xfc82 /* SEMC DSS-20 SyncStation */
+
+/* Cambridge Silicon Radio Ltd. products */
+#define USB_PRODUCT_CSR_BT_DONGLE 0x0001 /* Bluetooth USB dongle */
+#define USB_PRODUCT_CSR_CSRDFU 0xffff /* USB Bluetooth Device in DFU State */
+
+/* CTX products */
+#define USB_PRODUCT_CTX_EX1300 0x9999 /* Ex1300 hub */
+
+/* Cypress Semiconductor products */
+#define USB_PRODUCT_CYPRESS_MOUSE 0x0001 /* mouse */
+#define USB_PRODUCT_CYPRESS_THERMO 0x0002 /* thermometer */
+#define USB_PRODUCT_CYPRESS_FMRADIO 0x1002 /* FM Radio */
+#define USB_PRODUCT_CYPRESS_SLIM_HUB 0x6560 /* Slim Hub */
+
+/* Daisy Technology products */
+#define USB_PRODUCT_DAISY_DMC 0x6901 /* USB MultiMedia Reader */
+
+/* Dallas Semiconductor products */
+#define USB_PRODUCT_DALLAS_J6502 0x4201 /* J-6502 speakers */
+
+/* Dell products */
+#define USB_PRODUCT_DELL_BC02 0x8000 /* Dell BC02 Bluetooth USB Adapter */
+
+/* Delorme Paublishing products */
+#define USB_PRODUCT_DELORME_EARTHMATE 0x0100 /* Earthmate GPS */
+
+/* Diamond products */
+#define USB_PRODUCT_DIAMOND_RIO500USB 0x0001 /* Rio 500 USB */
+
+/* Digi International products */
+#define USB_PRODUCT_DIGI_ACCELEPORT2 0x0002 /* AccelePort USB 2 */
+#define USB_PRODUCT_DIGI_ACCELEPORT4 0x0004 /* AccelePort USB 4 */
+#define USB_PRODUCT_DIGI_ACCELEPORT8 0x0008 /* AccelePort USB 8 */
+
+/* D-Link products */
+/*product DLINK DSBS25 0x0100 DSB-S25 serial adapter*/
+#define USB_PRODUCT_DLINK_DUBE100 0x1a00 /* 10/100 ethernet adapter */
+#define USB_PRODUCT_DLINK_DSB650TX4 0x200c /* 10/100 ethernet adapter */
+#define USB_PRODUCT_DLINK_DSB650C 0x4000 /* 10Mbps ethernet adapter */
+#define USB_PRODUCT_DLINK_DSB650TX1 0x4001 /* 10/100 ethernet adapter */
+#define USB_PRODUCT_DLINK_DSB650TX 0x4002 /* 10/100 ethernet adapter */
+#define USB_PRODUCT_DLINK_DSB650TX_PNA 0x4003 /* 1/10/100 ethernet adapter */
+#define USB_PRODUCT_DLINK_DSB650TX3 0x400b /* 10/100 ethernet adapter */
+#define USB_PRODUCT_DLINK_DSB650TX2 0x4102 /* 10/100 ethernet adapter */
+#define USB_PRODUCT_DLINK_DSB650 0xabc1 /* 10/100 ethernet adapter */
+
+/* EIZO products */
+#define USB_PRODUCT_EIZO_HUB 0x0000 /* hub */
+#define USB_PRODUCT_EIZO_MONITOR 0x0001 /* monitor */
+
+/* Elecom products */
+#define USB_PRODUCT_ELECOM_MOUSE29UO 0x0002 /* mouse 29UO */
+#define USB_PRODUCT_ELECOM_LDUSBTX0 0x200c /* LD-USB/TX */
+#define USB_PRODUCT_ELECOM_LDUSBTX1 0x4002 /* LD-USB/TX */
+#define USB_PRODUCT_ELECOM_LDUSBLTX 0x4005 /* LD-USBL/TX */
+#define USB_PRODUCT_ELECOM_LDUSBTX2 0x400b /* LD-USB/TX */
+#define USB_PRODUCT_ELECOM_UCSGT 0x5003 /* UC-SGT */
+#define USB_PRODUCT_ELECOM_LDUSBTX3 0xabc1 /* LD-USB/TX */
+
+/* Elsa products */
+#define USB_PRODUCT_ELSA_MODEM1 0x2265 /* ELSA Modem Board */
+#define USB_PRODUCT_ELSA_USB2ETHERNET 0x3000 /* Microlink USB2Ethernet */
+
+/* EMS products */
+#define USB_PRODUCT_EMS_DUAL_SHOOTER 0x0003 /* PSX gun controller converter */
+
+/* Entrega products */
+#define USB_PRODUCT_ENTREGA_1S 0x0001 /* 1S serial connector */
+#define USB_PRODUCT_ENTREGA_2S 0x0002 /* 2S serial connector */
+#define USB_PRODUCT_ENTREGA_1S25 0x0003 /* 1S25 serial connector */
+#define USB_PRODUCT_ENTREGA_4S 0x0004 /* 4S serial connector */
+#define USB_PRODUCT_ENTREGA_E45 0x0005 /* E45 Ethernet adapter */
+#define USB_PRODUCT_ENTREGA_CENTRONICS 0x0006 /* Centronics connector */
+#define USB_PRODUCT_ENTREGA_1S9 0x0093 /* 1S9 serial connector */
+#define USB_PRODUCT_ENTREGA_EZUSB 0x8000 /* EZ-USB */
+/*product ENTREGA SERIAL 0x8001 DB25 Serial connector*/
+#define USB_PRODUCT_ENTREGA_2U4S 0x8004 /* 2U4S serial connector/usb hub */
+/*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial connector*/
+
+/* Epson products */
+#define USB_PRODUCT_EPSON_PRINTER1 0x0001 /* USB Printer */
+#define USB_PRODUCT_EPSON_PRINTER2 0x0002 /* ISD USB Smart Cable for Mac */
+#define USB_PRODUCT_EPSON_PRINTER3 0x0003 /* ISD USB Smart Cable */
+#define USB_PRODUCT_EPSON_PRINTER5 0x0005 /* USB Printer */
+#define USB_PRODUCT_EPSON_636 0x0101 /* Perfection 636U / 636Photo scanner */
+#define USB_PRODUCT_EPSON_610 0x0103 /* Perfection 610 scanner */
+#define USB_PRODUCT_EPSON_1200 0x0104 /* Perfection 1200U / 1200Photo scanner */
+#define USB_PRODUCT_EPSON_1600 0x0107 /* Expression 1600 scanner */
+#define USB_PRODUCT_EPSON_1640 0x010a /* Perfection 1640SU scanner */
+#define USB_PRODUCT_EPSON_1240 0x010b /* Perfection 1240U / 1240Photo scanner */
+#define USB_PRODUCT_EPSON_640U 0x010c /* Perfection 640U scanner */
+#define USB_PRODUCT_EPSON_1250 0x010f /* Perfection 1250U / 1250Photo scanner */
+#define USB_PRODUCT_EPSON_1650 0x0110 /* Perfection 1650 scanner */
+#define USB_PRODUCT_EPSON_GT9700F 0x0112 /* GT-9700F scanner */
+#define USB_PRODUCT_EPSON_GT9300UF 0x011b /* GT-9300UF scanner */
+#define USB_PRODUCT_EPSON_3200 0x011c /* Perfection 3200 scanner */
+#define USB_PRODUCT_EPSON_1260 0x011d /* Perfection 1260 scanner */
+#define USB_PRODUCT_EPSON_1660 0x011e /* Perfection 1660 scanner */
+#define USB_PRODUCT_EPSON_1670 0x011f /* Perfection 1670 scanner */
+
+/* e-TEK Labs products */
+#define USB_PRODUCT_ETEK_1COM 0x8007 /* Serial port */
+
+/* Extended Systems products */
+#define USB_PRODUCT_EXTENDED_XTNDACCESS 0x0100 /* XTNDAccess IrDA */
+
+/* GoHubs products */
+#define USB_PRODUCT_GOHUBS_GOCOM232 0x1001 /* GoCOM232 Serial converter */
+
+/* Gravis products */
+#define USB_PRODUCT_GRAVIS_GAMEPADPRO 0x4001 /* GamePad Pro */
+
+/* GREENHOUSE products */
+#define USB_PRODUCT_GREENHOUSE_KANA21 0x0001 /* CF-writer with Portable MP3 Player */
+
+/* Griffin Technology */
+#define USB_PRODUCT_GRIFFIN_IMATE 0x0405 /* iMate, ADB adapter */
+
+/* Freecom products */
+#define USB_PRODUCT_FREECOM_DVD 0xfc01 /* Connector for DVD drive */
+
+/* Future Technology Devices products */
+#define USB_PRODUCT_FTDI_SERIAL_8U100AX 0x8372 /* 8U100AX Serial converter */
+#define USB_PRODUCT_FTDI_SERIAL_8U232AM 0x6001 /* 8U232AM Serial converter */
+
+/* Fuji photo products */
+#define USB_PRODUCT_FUJIPHOTO_MASS0100 0x0100 /* Mass Storage */
+
+/* Fujitsu protducts */
+#define USB_PRODUCT_FUJITSU_AH_F401U 0x105b /* AH-F401U Air H device */
+
+/* Qualcomm products */
+#define USB_PRODUCT_QUALCOMM_CDMA_MSM 0x6000 /* CDMA Technologies MSM phone */
+
+/* General Instruments (Motorola) products */
+#define USB_PRODUCT_GENERALINSTMNTS_SB5100 0x5100 /* SURFboard SB5100 Cable modem */
+
+/* Genesys Logic products */
+#define USB_PRODUCT_GENESYS_GL650 0x0604 /* GL650 Hub */
+#define USB_PRODUCT_GENESYS_GL641USB 0x0700 /* GL641USB CompactFlash Card Reader */
+#define USB_PRODUCT_GENESYS_GL641USB2IDE_2 0x0701 /* GL641USB USB-IDE Bridge No 2 */
+#define USB_PRODUCT_GENESYS_GL641USB2IDE 0x0702 /* GL641USB USB-IDE Bridge */
+
+/* HAL Corporation products */
+#define USB_PRODUCT_HAL_IMR001 0x0011 /* Crossam2+USB IR commander */
+
+/* Hagiwara products */
+#define USB_PRODUCT_HAGIWARA_FGSM 0x0002 /* FlashGate SmartMedia Card Reader */
+#define USB_PRODUCT_HAGIWARA_FGCF 0x0003 /* FlashGate CompactFlash Card Reader */
+#define USB_PRODUCT_HAGIWARA_FG 0x0005 /* FlashGate */
+
+/* Handspring, Inc. */
+#define USB_PRODUCT_HANDSPRING_VISOR 0x0100 /* Handspring Visor */
+#define USB_PRODUCT_HANDSPRING_TREO 0x0200 /* Handspring Treo */
+#define USB_PRODUCT_HANDSPRING_TREO600 0x0300 /* Handspring Treo 600 */
+
+/* Hauppauge Computer Works */
+#define USB_PRODUCT_HAUPPAUGE_WINTV_USB_FM 0x4d12 /* WinTV USB FM */
+
+/* Hawking Technologies products */
+#define USB_PRODUCT_HAWKING_UF100 0x400c /* 10/100 USB Ethernet */
+
+/* Hitachi, Ltd. products */
+#define USB_PRODUCT_HITACHI_DVDCAM_USB 0x001e /* DVDCAM USB HS Interface */
+
+/* HP products */
+#define USB_PRODUCT_HP_895C 0x0004 /* DeskJet 895C */
+#define USB_PRODUCT_HP_4100C 0x0101 /* Scanjet 4100C */
+#define USB_PRODUCT_HP_S20 0x0102 /* Photosmart S20 */
+#define USB_PRODUCT_HP_880C 0x0104 /* DeskJet 880C */
+#define USB_PRODUCT_HP_4200C 0x0105 /* ScanJet 4200C */
+#define USB_PRODUCT_HP_CDWRITERPLUS 0x0107 /* CD-Writer Plus */
+#define USB_PRODUCT_HP_KBDHUB 0x010c /* Multimedia Keyboard Hub */
+#define USB_PRODUCT_HP_6200C 0x0201 /* ScanJet 6200C */
+#define USB_PRODUCT_HP_S20b 0x0202 /* PhotoSmart S20 */
+#define USB_PRODUCT_HP_815C 0x0204 /* DeskJet 815C */
+#define USB_PRODUCT_HP_3300C 0x0205 /* ScanJet 3300C */
+#define USB_PRODUCT_HP_CDW8200 0x0207 /* CD-Writer Plus 8200e */
+#define USB_PRODUCT_HP_1220C 0x0212 /* DeskJet 1220C */
+#define USB_PRODUCT_HP_810C 0x0304 /* DeskJet 810C/812C */
+#define USB_PRODUCT_HP_4300C 0x0305 /* Scanjet 4300C */
+#define USB_PRODUCT_HP_G85XI 0x0311 /* OfficeJet G85xi */
+#define USB_PRODUCT_HP_1200 0x0317 /* LaserJet 1200 */
+#define USB_PRODUCT_HP_5200C 0x0401 /* Scanjet 5200C */
+#define USB_PRODUCT_HP_830C 0x0404 /* DeskJet 830C */
+#define USB_PRODUCT_HP_3400CSE 0x0405 /* ScanJet 3400cse */
+#define USB_PRODUCT_HP_6300C 0x0601 /* Scanjet 6300C */
+#define USB_PRODUCT_HP_840C 0x0604 /* DeskJet 840c */
+#define USB_PRODUCT_HP_2200C 0x0605 /* ScanJet 2200C */
+#define USB_PRODUCT_HP_5300C 0x0701 /* Scanjet 5300C */
+#define USB_PRODUCT_HP_4400C 0x0705 /* Scanjet 4400C */
+#define USB_PRODUCT_HP_970CSE 0x1004 /* Deskjet 970Cse */
+#define USB_PRODUCT_HP_5400C 0x1005 /* Scanjet 5400C */
+#define USB_PRODUCT_HP_930C 0x1204 /* DeskJet 930c */
+#define USB_PRODUCT_HP_P2000U 0x1801 /* Inkjet P-2000U */
+#define USB_PRODUCT_HP_640C 0x2004 /* DeskJet 640c */
+#define USB_PRODUCT_HP_P1100 0x3102 /* Photosmart P1100 */
+#define USB_PRODUCT_HP_HN210E 0x811c /* Ethernet HN210E */
+
+/* HP products */
+#define USB_PRODUCT_HP2_C500 0x6002 /* PhotoSmart C500 */
+
+/* IBM Corporation */
+#define USB_PRODUCT_IBM_USBCDROMDRIVE 0x4427 /* USB CD-ROM Drive */
+
+/* Inside Out Networks products */
+#define USB_PRODUCT_INSIDEOUT_EDGEPORT4 0x0001 /* EdgePort/4 serial ports */
+
+/* In-System products */
+#define USB_PRODUCT_INSYSTEM_F5U002 0x0002 /* Parallel printer adapter */
+#define USB_PRODUCT_INSYSTEM_ATAPI 0x0031 /* ATAPI adapter */
+#define USB_PRODUCT_INSYSTEM_ISD110 0x0200 /* IDE adapter ISD110 */
+#define USB_PRODUCT_INSYSTEM_ISD105 0x0202 /* IDE adapter ISD105 */
+#define USB_PRODUCT_INSYSTEM_USBCABLE 0x081a /* USB cable */
+
+/* Intel products */
+#define USB_PRODUCT_INTEL_EASYPC_CAMERA 0x0110 /* Easy PC Camera */
+#define USB_PRODUCT_INTEL_TESTBOARD 0x9890 /* 82930 test board */
+
+/* Intersil products */
+#define USB_PRODUCT_INTERSIL_PRISM_2X 0x3642 /* Prism2.x or Atmel WLAN */
+
+/* I/O DATA products */
+#define USB_PRODUCT_IODATA_USBETT 0x0901 /* USB ETT */
+#define USB_PRODUCT_IODATA_USBETTX 0x0904 /* USB ETTX */
+#define USB_PRODUCT_IODATA_USBETTXS 0x0913 /* USB ETTX */
+#define USB_PRODUCT_IODATA_USBRSAQ 0x0a03 /* USB serial adapter USB-RSAQ1 */
+#define USB_PRODUCT_IODATA_IU_CD2 0x0204 /* DVD Multi-plus unit iU-CD2 */
+#define USB_PRODUCT_IODATA_DVR_UEH8 0x0206 /* DVD Multi-plus unit DVR-UEH8 */
+
+/* Iomega products */
+#define USB_PRODUCT_IOMEGA_ZIP100 0x0001 /* Zip 100 */
+#define USB_PRODUCT_IOMEGA_ZIP250 0x0030 /* Zip 250 */
+
+/* JVC products */
+#define USB_PRODUCT_JVC_GR_DX95 0x000a /* GR-DX95 */
+
+/* JRC products */
+#define USB_PRODUCT_JRC_AH_J3001V_J3002V 0x0001 /* AirH\" PHONE AH-J3001V/J3002V */
+
+/* Kawasaki products */
+#define USB_PRODUCT_KLSI_DUH3E10BT 0x0008 /* USB ethernet controller engine */
+#define USB_PRODUCT_KLSI_DUH3E10BTN 0x0009 /* USB ethernet controller engine */
+
+/* Kawatsu products */
+#define USB_PRODUCT_KAWATSU_MH4000P 0x0003 /* MiniHub 4000P */
+
+/* Keisokugiken Corp. products */
+#define USB_PRODUCT_KEISOKUGIKEN_USBDAQ 0x0068 /* HKS-0200 USBDAQ */
+
+/* Kawasaki products */
+#define USB_PRODUCT_KLSI_DUH3E10BT 0x0008 /* 10BT Ethernet adapter, in the DU-H3E */
+
+/* Kensington products */
+#define USB_PRODUCT_KENSINGTON_ORBIT 0x1003 /* Orbit USB/PS2 trackball */
+#define USB_PRODUCT_KENSINGTON_TURBOBALL 0x1005 /* TurboBall */
+
+/* Keyspan products */
+#define USB_PRODUCT_KEYSPAN_USA28 0x0101 /* USA-28 serial adapter */
+#define USB_PRODUCT_KEYSPAN_USA28X 0x0102 /* USA-28X serial adapter */
+#define USB_PRODUCT_KEYSPAN_USA19 0x0103 /* USA-19 serial adapter */
+#define USB_PRODUCT_KEYSPAN_USA18X 0x0105 /* USA-18X serial adapter */
+#define USB_PRODUCT_KEYSPAN_USA19W 0x0106 /* USA-19W serial adapter */
+#define USB_PRODUCT_KEYSPAN_USA49W 0x0109 /* USA-49W serial adapter */
+#define USB_PRODUCT_KEYSPAN_USA19QW 0x0118 /* USA-19QW serial adapter */
+
+/* Kingston products */
+#define USB_PRODUCT_KINGSTON_KNU101TX 0x000a /* KNU101TX USB Ethernet */
+
+/* Kodak products */
+#define USB_PRODUCT_KODAK_DC220 0x0100 /* Digital Science DC220 */
+#define USB_PRODUCT_KODAK_DC260 0x0110 /* Digital Science DC260 */
+#define USB_PRODUCT_KODAK_DC265 0x0111 /* Digital Science DC265 */
+#define USB_PRODUCT_KODAK_DC290 0x0112 /* Digital Science DC290 */
+#define USB_PRODUCT_KODAK_DC240 0x0120 /* Digital Science DC240 */
+#define USB_PRODUCT_KODAK_DC280 0x0130 /* Digital Science DC280 */
+
+/* Konica Corp. Products */
+#define USB_PRODUCT_KONICA_CAMERA 0x0720 /* Digital Color Camera */
+
+/* KYE products */
+#define USB_PRODUCT_KYE_NICHE 0x0001 /* Niche mouse */
+#define USB_PRODUCT_KYE_NETSCROLL 0x0003 /* Genius NetScroll mouse */
+#define USB_PRODUCT_KYE_FLIGHT2000 0x1004 /* Flight 2000 joystick */
+#define USB_PRODUCT_KYE_VIVIDPRO 0x2001 /* ColorPage Vivid-Pro scanner */
+
+/* Kyocera products */
+#define USB_PRODUCT_KYOCERA_AHK3001V 0x0203 /* AH-K3001V */
+
+/* LaCie products */
+#define USB_PRODUCT_LACIE_HD 0xa601 /* Hard Disk */
+#define USB_PRODUCT_LACIE_CDRW 0xa602 /* CD R/W */
+
+/* Lexar products */
+#define USB_PRODUCT_LEXAR_JUMPSHOT 0x0001 /* jumpSHOT CompactFlash Reader */
+
+/* Lexmark products */
+#define USB_PRODUCT_LEXMARK_S2450 0x0009 /* Optra S 2450 */
+
+/* Linksys products */
+#define USB_PRODUCT_LINKSYS_MAUSB2 0x0105 /* Camedia MAUSB-2 */
+#define USB_PRODUCT_LINKSYS_USB10TX1 0x200c /* USB10TX */
+#define USB_PRODUCT_LINKSYS_USB10T 0x2202 /* USB10T Ethernet */
+#define USB_PRODUCT_LINKSYS_USB100TX 0x2203 /* USB100TX Ethernet */
+#define USB_PRODUCT_LINKSYS_USB100H1 0x2204 /* USB100H1 Ethernet/HPNA */
+#define USB_PRODUCT_LINKSYS_USB10TA 0x2206 /* USB10TA Ethernet */
+#define USB_PRODUCT_LINKSYS_USB10TX2 0x400b /* USB10TX */
+#define USB_PRODUCT_LINKSYS2_WUSB11 0x2219 /* WUSB11 Wireless adapter */
+#define USB_PRODUCT_LINKSYS2_USB200M 0x2226 /* USB 2.0 10/100 ethernet controller */
+
+/* Logitech products */
+#define USB_PRODUCT_LOGITECH_M2452 0x0203 /* M2452 keyboard */
+#define USB_PRODUCT_LOGITECH_M4848 0x0301 /* M4848 mouse */
+#define USB_PRODUCT_LOGITECH_PAGESCAN 0x040f /* PageScan */
+#define USB_PRODUCT_LOGITECH_QUICKCAMWEB 0x0801 /* QuickCam Web */
+#define USB_PRODUCT_LOGITECH_QUICKCAMPRO 0x0810 /* QuickCam Pro */
+#define USB_PRODUCT_LOGITECH_QUICKCAMEXP 0x0840 /* QuickCam Express */
+#define USB_PRODUCT_LOGITECH_QUICKCAM 0x0850 /* QuickCam */
+#define USB_PRODUCT_LOGITECH_N43 0xc000 /* N43 */
+#define USB_PRODUCT_LOGITECH_N48 0xc001 /* N48 mouse */
+#define USB_PRODUCT_LOGITECH_MBA47 0xc002 /* M-BA47 mouse */
+#define USB_PRODUCT_LOGITECH_WMMOUSE 0xc004 /* WingMan Gaming Mouse */
+#define USB_PRODUCT_LOGITECH_BD58 0xc00c /* BD58 mouse */
+#define USB_PRODUCT_LOGITECH_UN58A 0xc030 /* iFeel Mouse */
+#define USB_PRODUCT_LOGITECH_BB13 0xc401 /* USB-PS/2 Trackball */
+#define USB_PRODUCT_LOGITECH_WMPAD 0xc208 /* WingMan GamePad Extreme */
+#define USB_PRODUCT_LOGITECH_WMRPAD 0xc20a /* WingMan RumblePad */
+#define USB_PRODUCT_LOGITECH_WMJOY 0xc281 /* WingMan Force joystick */
+#define USB_PRODUCT_LOGITECH_RK53 0xc501 /* Cordless mouse */
+#define USB_PRODUCT_LOGITECH_RB6 0xc503 /* Cordless keyboard */
+#define USB_PRODUCT_LOGITECH_MX700 0xc506 /* Cordless optical mouse */
+#define USB_PRODUCT_LOGITECH_QUICKCAMPRO2 0xd001 /* QuickCam Pro */
+
+/* Logitec Corp. products */
+#define USB_PRODUCT_LOGITEC_LDR_H443SU2 0x0033 /* DVD Multi-plus unit LDR-H443SU2 */
+#define USB_PRODUCT_LOGITEC_LDR_H443U2 0x00b3 /* DVD Multi-plus unit LDR-H443U2 */
+
+/* Lucent products */
+#define USB_PRODUCT_LUCENT_EVALKIT 0x1001 /* USS-720 evaluation kit */
+
+/* Luwen products */
+#define USB_PRODUCT_LUWEN_EASYDISK 0x0005 /* EasyDisc */
+
+/* Macally products */
+#define USB_PRODUCT_MACALLY_MOUSE1 0x0101 /* mouse */
+
+/* Matrix Orbital products */
+#define USB_PRODUCT_FTDI_USBSERIAL 0xfa00 /* Matrix Orbital USB Serial */
+#define USB_PRODUCT_FTDI_MX2_3 0xfa01 /* Matrix Orbital MX2 or MX3 */
+#define USB_PRODUCT_FTDI_MX4_5 0xfa02 /* Matrix Orbital MX4 or MX5 */
+#define USB_PRODUCT_FTDI_LK202 0xfa03 /* Matrix Orbital VK/LK202 Family */
+#define USB_PRODUCT_FTDI_LK204 0xfa04 /* Matrix Orbital VK/LK204 Family */
+
+/* MCT Corp. */
+#define USB_PRODUCT_MCT_HUB0100 0x0100 /* Hub */
+#define USB_PRODUCT_MCT_DU_H3SP_USB232 0x0200 /* D-Link DU-H3SP USB BAY Hub */
+#define USB_PRODUCT_MCT_USB232 0x0210 /* USB-232 Interface */
+#define USB_PRODUCT_MCT_SITECOM_USB232 0x0230 /* Sitecom USB-232 Products */
+
+/* Melco, Inc products */
+#define USB_PRODUCT_MELCO_LUATX1 0x0001 /* LUA-TX Ethernet */
+#define USB_PRODUCT_MELCO_LUATX5 0x0005 /* LUA-TX Ethernet */
+#define USB_PRODUCT_MELCO_LUA2TX5 0x0009 /* LUA2-TX Ethernet */
+#define USB_PRODUCT_MELCO_LUAKTX 0x0012 /* LUA-KTX Ethernet */
+#define USB_PRODUCT_MELCO_DUBPXXG 0x001c /* USB-IDE Bridge: DUB-PxxG */
+#define USB_PRODUCT_MELCO_LUAU2KTX 0x003d /* LUA-U2-KTX Ethernet */
+
+/* Metricom products */
+#define USB_PRODUCT_METRICOM_RICOCHET_GS 0x0001 /* Ricochet GS */
+
+/* Microsoft products */
+#define USB_PRODUCT_MICROSOFT_SIDEPREC 0x0008 /* SideWinder Precision Pro */
+#define USB_PRODUCT_MICROSOFT_INTELLIMOUSE 0x0009 /* IntelliMouse */
+#define USB_PRODUCT_MICROSOFT_NATURALKBD 0x000b /* Natural Keyboard Elite */
+#define USB_PRODUCT_MICROSOFT_DDS80 0x0014 /* Digital Sound System 80 */
+#define USB_PRODUCT_MICROSOFT_SIDEWINDER 0x001a /* Sidewinder Precision Racing Wheel */
+#define USB_PRODUCT_MICROSOFT_INETPRO 0x001c /* Internet Keyboard Pro */
+#define USB_PRODUCT_MICROSOFT_INTELLIEYE 0x0025 /* IntelliEye mouse */
+#define USB_PRODUCT_MICROSOFT_INETPRO2 0x002b /* Internet Keyboard Pro */
+#define USB_PRODUCT_MICROSOFT_MN110 0x007a /* 10/100 USB NIC */
+
+/* Microtech products */
+#define USB_PRODUCT_MICROTECH_SCSIDB25 0x0004 /* USB-SCSI-DB25 */
+#define USB_PRODUCT_MICROTECH_SCSIHD50 0x0005 /* USB-SCSI-HD50 */
+#define USB_PRODUCT_MICROTECH_DPCM 0x0006 /* USB CameraMate */
+#define USB_PRODUCT_MICROTECH_FREECOM 0xfc01 /* Freecom USB-IDE */
+
+/* Microtek products */
+#define USB_PRODUCT_MICROTEK_336CX 0x0094 /* Phantom 336CX - C3 scanner */
+#define USB_PRODUCT_MICROTEK_X6U 0x0099 /* ScanMaker X6 - X6U */
+#define USB_PRODUCT_MICROTEK_C6 0x009a /* Phantom C6 scanner */
+#define USB_PRODUCT_MICROTEK_336CX2 0x00a0 /* Phantom 336CX - C3 scanner */
+#define USB_PRODUCT_MICROTEK_V6USL 0x00a3 /* ScanMaker V6USL */
+#define USB_PRODUCT_MICROTEK_V6USL2 0x80a3 /* ScanMaker V6USL */
+#define USB_PRODUCT_MICROTEK_V6UL 0x80ac /* ScanMaker V6UL */
+
+/* Midiman products */
+#define USB_PRODUCT_MIDIMAN_MIDISPORT2X2 0x1001 /* Midisport 2x2 */
+
+/* Minolta Co., Ltd. */
+#define USB_PRODUCT_MINOLTA_2300 0x4001 /* Dimage 2300 */
+#define USB_PRODUCT_MINOLTA_S304 0x4007 /* Dimage S304 */
+#define USB_PRODUCT_MINOLTA_X 0x4009 /* Dimage X */
+#define USB_PRODUCT_MINOLTA_5400 0x400e /* Dimage 5400 */
+
+/* Micro Star International products */
+#define USB_PRODUCT_MSI_BT_DONGLE 0x1967 /* Bluetooth USB dongle */
+
+/* Microtune, Inc. products */
+#define USB_PRODUCT_MICROTUNE_BT_DONGLE 0x1000 /* Bluetooth USB dongle */
+
+/* Mitsumi products */
+#define USB_PRODUCT_MITSUMI_CDRRW 0x0000 /* CD-R/RW Drive */
+#define USB_PRODUCT_MITSUMI_BT_DONGLE 0x641f /* Bluetooth USB dongle */
+
+/* Motorola products */
+#define USB_PRODUCT_MOTOROLA_MC141555 0x1555 /* MC141555 hub controller */
+#define USB_PRODUCT_MOTOROLA_SB4100 0x4100 /* SB4100 USB Cable Modem */
+
+/* MultiTech products */
+#define USB_PRODUCT_MULTITECH_ATLAS 0xf101 /* MT5634ZBA-USB modem */
+
+/* Mustek products */
+#define USB_PRODUCT_MUSTEK_1200CU 0x0001 /* 1200 CU scanner */
+#define USB_PRODUCT_MUSTEK_600CU 0x0002 /* 600 CU scanner */
+#define USB_PRODUCT_MUSTEK_1200USB 0x0003 /* 1200 USB scanner */
+#define USB_PRODUCT_MUSTEK_1200UB 0x0006 /* 1200 UB scanner */
+#define USB_PRODUCT_MUSTEK_1200USBPLUS 0x0007 /* 1200 USB Plus scanner */
+#define USB_PRODUCT_MUSTEK_1200CUPLUS 0x0008 /* 1200 CU Plus scanner */
+#define USB_PRODUCT_MUSTEK_BEARPAW1200F 0x0010 /* BearPaw 1200F scanner */
+#define USB_PRODUCT_MUSTEK_BEARPAW1200TA 0x021e /* BearPaw 1200TA scanner */
+#define USB_PRODUCT_MUSTEK_600USB 0x0873 /* 600 USB scanner */
+#define USB_PRODUCT_MUSTEK_MDC800 0xa800 /* MDC-800 digital camera */
+
+/* M-Systems products */
+#define USB_PRODUCT_MSYSTEMS_DISKONKEY 0x0010 /* DiskOnKey */
+#define USB_PRODUCT_MSYSTEMS_DISKONKEY2 0x0011 /* DiskOnKey */
+
+/* National Semiconductor */
+#define USB_PRODUCT_NATIONAL_BEARPAW1200 0x1000 /* BearPaw 1200 */
+#define USB_PRODUCT_NATIONAL_BEARPAW2400 0x1001 /* BearPaw 2400 */
+
+/* NEC products */
+#define USB_PRODUCT_NEC_HUB 0x55aa /* hub */
+#define USB_PRODUCT_NEC_HUB_B 0x55ab /* hub */
+
+/* NEODIO products */
+#define USB_PRODUCT_NEODIO_ND3260 0x3260 /* 8-in-1 Multi-format Flash Controller */
+#define USB_PRODUCT_NEODIO_ND5010 0x5010 /* Multi-format Flash Controller */
+
+/* NetChip Technology Products */
+#define USB_PRODUCT_NETCHIP_TURBOCONNECT 0x1080 /* Turbo-Connect */
+
+/* Netgear products */
+#define USB_PRODUCT_NETGEAR_EA101 0x1001 /* Ethernet adapter */
+#define USB_PRODUCT_NETGEAR_FA120 0x1040 /* USB 2.0 Ethernet adapter */
+
+/* Nikon products */
+#define USB_PRODUCT_NIKON_E990 0x0102 /* Digital Camera E990 */
+
+/* Olympus products */
+#define USB_PRODUCT_OLYMPUS_C1 0x0102 /* C-1 Digital Camera */
+#define USB_PRODUCT_OLYMPUS_C700 0x0105 /* C-700 Ultra Zoom */
+
+/* OmniVision Technologies, Inc. products */
+#define USB_PRODUCT_OMNIVISION_OV511 0x0511 /* OV511 Camera */
+#define USB_PRODUCT_OMNIVISION_OV511PLUS 0xa511 /* OV511+ Camera */
+
+/* OnSpec Electronic, Inc. */
+#define USB_PRODUCT_ONSPEC_UCF100 0xa400 /* FlashLink UCF-100 CompactFlash Reader */
+
+/* Palm Computing, Inc. product */
+#define USB_PRODUCT_PALM_SERIAL 0x0080 /* USB Serial Adaptor */
+#define USB_PRODUCT_PALM_M500 0x0001 /* Palm m500 */
+#define USB_PRODUCT_PALM_M505 0x0002 /* Palm m505 */
+#define USB_PRODUCT_PALM_M515 0x0003 /* Palm m515 */
+#define USB_PRODUCT_PALM_I705 0x0020 /* Palm i705 */
+#define USB_PRODUCT_PALM_TUNGSTEN_Z 0x0031 /* Palm Tungsten Z */
+#define USB_PRODUCT_PALM_M125 0x0040 /* Palm m125 */
+#define USB_PRODUCT_PALM_M130 0x0050 /* Palm m130 */
+#define USB_PRODUCT_PALM_TUNGSTEN_T 0x0060 /* Palm Tungsten T */
+#define USB_PRODUCT_PALM_ZIRE31 0x0061 /* Palm Zire 31 */
+#define USB_PRODUCT_PALM_ZIRE 0x0070 /* Palm Zire */
+
+/* Panasonic products */
+#define USB_PRODUCT_PANASONIC_KXLRW32AN 0x0d09 /* CD-R Drive KXL-RW32AN */
+#define USB_PRODUCT_PANASONIC_KXLCB20AN 0x0d0a /* CD-R Drive KXL-CB20AN */
+#define USB_PRODUCT_PANASONIC_KXLCB35AN 0x0d0e /* DVD-ROM & CD-R/RW */
+#define USB_PRODUCT_PANASONIC_SDCAAE 0x1b00 /* MultiMediaCard Adapter */
+
+/* Peracom products */
+#define USB_PRODUCT_PERACOM_SERIAL1 0x0001 /* Serial Converter */
+#define USB_PRODUCT_PERACOM_ENET 0x0002 /* Ethernet adapter */
+#define USB_PRODUCT_PERACOM_ENET3 0x0003 /* At Home Ethernet Adapter */
+#define USB_PRODUCT_PERACOM_ENET2 0x0005 /* Ethernet adapter */
+
+/* Philips products */
+#define USB_PRODUCT_PHILIPS_DSS350 0x0101 /* DSS 350 Digital Speaker System */
+#define USB_PRODUCT_PHILIPS_DSS 0x0104 /* DSS XXX Digital Speaker System */
+#define USB_PRODUCT_PHILIPS_HUB 0x0201 /* hub */
+#define USB_PRODUCT_PHILIPS_PCA646VC 0x0303 /* PCA646VC PC Camera */
+#define USB_PRODUCT_PHILIPS_PCVC680K 0x0308 /* PCVC680K Vesta Pro PC Camera */
+#define USB_PRODUCT_PHILIPS_DSS150 0x0471 /* DSS 150 Digital Speaker System */
+#define USB_PRODUCT_PHILIPS_UM10016 0x1552 /* ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit */
+#define USB_PRODUCT_PHILIPS_DIVAUSB 0x1801 /* DIVA USB mp3 player */
+
+/* Philips Semiconductor products */
+#define USB_PRODUCT_PHILIPSSEMI_HUB1122 0x1122 /* hub */
+
+/* P.I. Engineering products */
+#define USB_PRODUCT_PIENGINEERING_PS2USB 0x020b /* PS2 to Mac USB Adapter */
+
+/* Plextor Corp. */
+#define USB_PRODUCT_PLEXTOR_40_12_40U 0x0011 /* PlexWriter 40/12/40U */
+
+/* PLX products */
+#define USB_PRODUCT_PLX_TESTBOARD 0x9060 /* test board */
+
+/* PNY products */
+#define USB_PRODUCT_PNY_ATTACHE 0x1300 /* USB 2.0 Flash Drive */
+
+/* Primax products */
+#define USB_PRODUCT_PRIMAX_G2X300 0x0300 /* G2-200 scanner */
+#define USB_PRODUCT_PRIMAX_G2E300 0x0301 /* G2E-300 scanner */
+#define USB_PRODUCT_PRIMAX_G2300 0x0302 /* G2-300 scanner */
+#define USB_PRODUCT_PRIMAX_G2E3002 0x0303 /* G2E-300 scanner */
+#define USB_PRODUCT_PRIMAX_9600 0x0340 /* Colorado USB 9600 scanner */
+#define USB_PRODUCT_PRIMAX_600U 0x0341 /* Colorado 600u scanner */
+#define USB_PRODUCT_PRIMAX_6200 0x0345 /* Visioneer 6200 scanner */
+#define USB_PRODUCT_PRIMAX_19200 0x0360 /* Colorado USB 19200 scanner */
+#define USB_PRODUCT_PRIMAX_1200U 0x0361 /* Colorado 1200u scanner */
+#define USB_PRODUCT_PRIMAX_G600 0x0380 /* G2-600 scanner */
+#define USB_PRODUCT_PRIMAX_636I 0x0381 /* ReadyScan 636i */
+#define USB_PRODUCT_PRIMAX_G2600 0x0382 /* G2-600 scanner */
+#define USB_PRODUCT_PRIMAX_G2E600 0x0383 /* G2E-600 scanner */
+#define USB_PRODUCT_PRIMAX_COMFORT 0x4d01 /* Comfort */
+#define USB_PRODUCT_PRIMAX_MOUSEINABOX 0x4d02 /* Mouse-in-a-Box */
+#define USB_PRODUCT_PRIMAX_PCGAUMS1 0x4d04 /* Sony PCGA-UMS1 */
+
+/* Prolific products */
+#define USB_PRODUCT_PROLIFIC_PL2301 0x0000 /* PL2301 Host-Host interface */
+#define USB_PRODUCT_PROLIFIC_PL2302 0x0001 /* PL2302 Host-Host interface */
+#define USB_PRODUCT_PROLIFIC_RSAQ2 0x04bb /* PL2303 Serial adapter (IODATA USB-RSAQ2) */
+#define USB_PRODUCT_PROLIFIC_PL2303 0x2303 /* PL2303 Serial adapter (ATEN/IOGEAR UC232A) */
+#define USB_PRODUCT_PROLIFIC_PL2305 0x2305 /* Parallel printer adapter */
+#define USB_PRODUCT_PROLIFIC_ATAPI4 0x2307 /* ATAPI-4 Bridge Controller */
+
+/* Putercom products */
+#define USB_PRODUCT_PUTERCOM_UPA100 0x047e /* USB-1284 BRIDGE */
+
+/* Qtronix products */
+#define USB_PRODUCT_QTRONIX_980N 0x2011 /* Scorpion-980N keyboard */
+
+/* Quickshot products */
+#define USB_PRODUCT_QUICKSHOT_STRIKEPAD 0x6238 /* USB StrikePad */
+
+/* Rainbow Technologies products */
+#define USB_PRODUCT_RAINBOW_IKEY2000 0x1200 /* i-Key 2000 */
+
+/* ReakTek products */
+#define USB_PRODUCT_REALTEK_USBKR100 0x8150 /* USBKR100 USB Ethernet (GREEN HOUSE) */
+
+/* Roland products */
+#define USB_PRODUCT_ROLAND_UM1 0x0009 /* UM-1 MIDI I/F */
+#define USB_PRODUCT_ROLAND_UM880N 0x0014 /* EDIROL UM-880 MIDI I/F (native) */
+#define USB_PRODUCT_ROLAND_UM880G 0x0015 /* EDIROL UM-880 MIDI I/F (generic) */
+
+/* Rockfire products */
+#define USB_PRODUCT_ROCKFIRE_GAMEPAD 0x2033 /* gamepad 203USB */
+
+/* RATOC Systems products */
+#define USB_PRODUCT_RATOC_REXUSB60 0xb000 /* USB serial adapter REX-USB60 */
+
+/* Samsung products */
+#define USB_PRODUCT_SAMSUNG_ML6060 0x3008 /* ML-6060 laser printer */
+
+/* SanDisk products */
+#define USB_PRODUCT_SANDISK_SDDR05A 0x0001 /* ImageMate SDDR-05a */
+#define USB_PRODUCT_SANDISK_SDDR05 0x0005 /* ImageMate SDDR-05 */
+#define USB_PRODUCT_SANDISK_SDDR31 0x0002 /* ImageMate SDDR-31 */
+#define USB_PRODUCT_SANDISK_SDDR12 0x0100 /* ImageMate SDDR-12 */
+#define USB_PRODUCT_SANDISK_SDDR09 0x0200 /* ImageMate SDDR-09 */
+#define USB_PRODUCT_SANDISK_SDDR75 0x0810 /* ImageMate SDDR-75 */
+
+/* Sanyo Electric products */
+#define USB_PRODUCT_SANYO_SCP4900 0x0701 /* Sanyo SCP-4900 USB Phone */
+
+/* ScanLogic products */
+#define USB_PRODUCT_SCANLOGIC_SL11R 0x0002 /* SL11R IDE Adapter */
+#define USB_PRODUCT_SCANLOGIC_336CX 0x0300 /* Phantom 336CX - C3 scanner */
+
+/* Shuttle Technology products */
+#define USB_PRODUCT_SHUTTLE_EUSB 0x0001 /* E-USB Bridge */
+#define USB_PRODUCT_SHUTTLE_EUSCSI 0x0002 /* eUSCSI Bridge */
+#define USB_PRODUCT_SHUTTLE_SDDR09 0x0003 /* ImageMate SDDR09 */
+#define USB_PRODUCT_SHUTTLE_ZIOMMC 0x0006 /* eUSB MultiMediaCard Adapter */
+#define USB_PRODUCT_SHUTTLE_HIFD 0x0007 /* Sony Hifd */
+#define USB_PRODUCT_SHUTTLE_EUSBATAPI 0x0009 /* eUSB ATA/ATAPI Adapter */
+#define USB_PRODUCT_SHUTTLE_CF 0x000a /* eUSB CompactFlash Adapter */
+#define USB_PRODUCT_SHUTTLE_EUSCSI_B 0x000b /* eUSCSI Bridge */
+#define USB_PRODUCT_SHUTTLE_EUSCSI_C 0x000c /* eUSCSI Bridge */
+#define USB_PRODUCT_SHUTTLE_CDRW 0x0101 /* CD-RW Device */
+#define USB_PRODUCT_SHUTTLE_EUSBORCA 0x0325 /* eUSB ORCA Quad Reader */
+
+/* Siemens products */
+#define USB_PRODUCT_SIEMENS_SPEEDSTREAM 0x1001 /* SpeedStream USB */
+
+/* Sigmatel products */
+#define USB_PRODUCT_SIGMATEL_I_BEAD100 0x8008 /* i-Bead 100 MP3 Player */
+
+/* SIIG products */
+#define USB_PRODUCT_SIIG_DIGIFILMREADER 0x0004 /* DigiFilm-Combo Reader */
+
+/* Silicon Portals Inc. */
+#define USB_PRODUCT_SILICONPORTALS_YAPPH_NF 0x0200 /* YAP Phone (no firmware) */
+#define USB_PRODUCT_SILICONPORTALS_YAPPHONE 0x0201 /* YAP Phone */
+
+/* Sirius Technologies products */
+#define USB_PRODUCT_SIRIUS_ROADSTER 0x0001 /* NetComm Roadster II 56 USB */
+
+/* SmartBridges products */
+#define USB_PRODUCT_SMARTBRIDGES_SMARTLINK 0x0001 /* SmartLink USB ethernet adapter */
+#define USB_PRODUCT_SMARTBRIDGES_SMARTNIC 0x0003 /* smartNIC 2 PnP Adapter */
+
+/* SMC products */
+#define USB_PRODUCT_SMC_2102USB 0x0100 /* 10Mbps ethernet adapter */
+#define USB_PRODUCT_SMC_2202USB 0x0200 /* 10/100 ethernet adapter */
+#define USB_PRODUCT_SMC_2206USB 0x0201 /* EZ Connect USB Ethernet Adapter */
+#define USB_PRODUCT_SMC2_2020HUB 0x2020 /* USB Hub */
+#define USB_PRODUCT_SMC3_2662WUSB 0xa002 /* 2662W-AR Wireless Adapter */
+
+/* SOHOware products */
+#define USB_PRODUCT_SOHOWARE_NUB100 0x9100 /* 10/100 USB Ethernet */
+
+/* SOLID YEAR products */
+#define USB_PRODUCT_SOLIDYEAR_KEYBOARD 0x2101 /* Solid Year USB keyboard */
+
+/* SONY products */
+#define USB_PRODUCT_SONY_DSC 0x0010 /* DSC cameras */
+#define USB_PRODUCT_SONY_MSACUS1 0x002d /* Memorystick MSAC-US1 */
+#define USB_PRODUCT_SONY_MSC 0x0032 /* MSC memory stick slot */
+#define USB_PRODUCT_SONY_CLIE_35 0x0038 /* Sony Clie v3.5 */
+#define USB_PRODUCT_SONY_CLIE_40 0x0066 /* Sony Clie v4.0 */
+#define USB_PRODUCT_SONY_CLIE_40_MS 0x006d /* Sony Clie v4.0 Memory Stick slot */
+#define USB_PRODUCT_SONY_CLIE_S360 0x0095 /* Sony Clie s360 */
+#define USB_PRODUCT_SONY_CLIE_41_MS 0x0099 /* Sony Clie v4.1 Memory Stick slot */
+#define USB_PRODUCT_SONY_CLIE_41 0x009a /* Sony Clie v4.1 */
+#define USB_PRODUCT_SONY_CLIE_NX60 0x00da /* Sony Clie nx60 */
+
+/* SOURCENEXT products */
+#define USB_PRODUCT_SOURCENEXT_KEIKAI8 0x039f /* KeikaiDenwa 8 */
+#define USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG 0x012e /* KeikaiDenwa 8 with charger */
+
+/* STMicroelectronics products */
+#define USB_PRODUCT_STMICRO_COMMUNICATOR 0x7554 /* USB Communicator */
+
+/* STSN products */
+#define USB_PRODUCT_STSN_STSN0001 0x0001 /* Internet Access Device */
+
+/* SUN Corporation products */
+#define USB_PRODUCT_SUNTAC_DS96L 0x0003 /* SUNTAC U-Cable type D2 */
+#define USB_PRODUCT_SUNTAC_PS64P1 0x0005 /* SUNTAC U-Cable type P1 */
+#define USB_PRODUCT_SUNTAC_VS10U 0x0009 /* SUNTAC Slipper U */
+#define USB_PRODUCT_SUNTAC_IS96U 0x000a /* SUNTAC Ir-Trinity */
+#define USB_PRODUCT_SUNTAC_AS64LX 0x000b /* SUNTAC U-Cable type A3 */
+#define USB_PRODUCT_SUNTAC_AS144L4 0x0011 /* SUNTAC U-Cable type A4 */
+
+/* Sun Microsystems products */
+#define USB_PRODUCT_SUN_KEYBOARD 0x0005 /* Type 6 USB keyboard */
+/* XXX The above is a North American PC style keyboard possibly */
+#define USB_PRODUCT_SUN_MOUSE 0x0100 /* Type 6 USB mouse */
+
+/* Supra products */
+#define USB_PRODUCT_DIAMOND2_SUPRAEXPRESS56K 0x07da /* Supra Express 56K modem */
+#define USB_PRODUCT_DIAMOND2_SUPRA2890 0x0b4a /* SupraMax 2890 56K Modem */
+#define USB_PRODUCT_DIAMOND2_RIO600USB 0x5001 /* Rio 600 USB */
+#define USB_PRODUCT_DIAMOND2_RIO800USB 0x5002 /* Rio 800 USB */
+
+/* Taugagreining products */
+#define USB_PRODUCT_TAUGA_CAMERAMATE 0x0005 /* CameraMate (DPCM_USB) */
+
+/* TDK products */
+#define USB_PRODUCT_TDK_UPA9664 0x0115 /* USB-PDC Adapter UPA9664 */
+#define USB_PRODUCT_TDK_UCA1464 0x0116 /* USB-cdmaOne Adapter UCA1464 */
+#define USB_PRODUCT_TDK_UHA6400 0x0117 /* USB-PHS Adapter UHA6400 */
+#define USB_PRODUCT_TDK_UPA6400 0x0118 /* USB-PHS Adapter UPA6400 */
+#define USB_PRODUCT_TDK_BT_DONGLE 0x0309 /* Bluetooth USB dongle */
+
+/* TEAC products */
+#define USB_PRODUCT_TEAC_FD05PUB 0x0000 /* FD-05PUB floppy */
+
+/* Telex Communications products */
+#define USB_PRODUCT_TELEX_MIC1 0x0001 /* Enhanced USB Microphone */
+
+/* Texas Intel products */
+#define USB_PRODUCT_TI_UTUSB41 0x1446 /* UT-USB41 hub */
+#define USB_PRODUCT_TI_TUSB2046 0x2046 /* TUSB2046 hub */
+
+/* Thrustmaster products */
+#define USB_PRODUCT_THRUST_FUSION_PAD 0xa0a3 /* Fusion Digital Gamepad */
+
+/* Toshiba Corporation products */
+#define USB_PRODUCT_TOSHIBA_POCKETPC_E740 0x0706 /* PocketPC e740 */
+
+/* Trek Technology products */
+#define USB_PRODUCT_TREK_THUMBDRIVE 0x1111 /* ThumbDrive */
+#define USB_PRODUCT_TREK_THUMBDRIVE_8MB 0x9988 /* ThumbDrive_8MB */
+
+/* Ultima products */
+#define USB_PRODUCT_ULTIMA_1200UBPLUS 0x4002 /* 1200 UB Plus scanner */
+
+/* UMAX products */
+#define USB_PRODUCT_UMAX_ASTRA1236U 0x0002 /* Astra 1236U Scanner */
+#define USB_PRODUCT_UMAX_ASTRA1220U 0x0010 /* Astra 1220U Scanner */
+#define USB_PRODUCT_UMAX_ASTRA2000U 0x0030 /* Astra 2000U Scanner */
+#define USB_PRODUCT_UMAX_ASTRA2100U 0x0130 /* Astra 2100U Scanner */
+#define USB_PRODUCT_UMAX_ASTRA2200U 0x0230 /* Astra 2200U Scanner */
+#define USB_PRODUCT_UMAX_ASTRA3400 0x0060 /* Astra 3400 Scanner */
+
+/* Universal Access products */
+#define USB_PRODUCT_UNIACCESS_PANACHE 0x0101 /* Panache Surf USB ISDN Adapter */
+
+/* VidzMedia products */
+#define USB_PRODUCT_VIDZMEDIA_MONSTERTV 0x4fb1 /* MonsterTV P2H */
+
+/* Vision products */
+#define USB_PRODUCT_VISION_VC6452V002 0x0002 /* CPiA Camera */
+
+/* Visioneer products */
+#define USB_PRODUCT_VISIONEER_7600 0x0211 /* OneTouch 7600 */
+#define USB_PRODUCT_VISIONEER_5300 0x0221 /* OneTouch 5300 */
+#define USB_PRODUCT_VISIONEER_3000 0x0224 /* Scanport 3000 */
+#define USB_PRODUCT_VISIONEER_6100 0x0231 /* OneTouch 6100 */
+#define USB_PRODUCT_VISIONEER_6200 0x0311 /* OneTouch 6200 */
+#define USB_PRODUCT_VISIONEER_8100 0x0321 /* OneTouch 8100 */
+#define USB_PRODUCT_VISIONEER_8600 0x0331 /* OneTouch 8600 */
+
+/* Wacom products */
+#define USB_PRODUCT_WACOM_CT0405U 0x0000 /* CT-0405-U Tablet */
+#define USB_PRODUCT_WACOM_GRAPHIRE 0x0010 /* Graphire */
+#define USB_PRODUCT_WACOM_INTUOSA5 0x0021 /* Intuos A5 */
+#define USB_PRODUCT_WACOM_GD0912U 0x0022 /* Intuos 9x12 Graphics Tablet */
+
+/* Xirlink products */
+#define USB_PRODUCT_XIRLINK_PCCAM 0x8080 /* IBM PC Camera */
+
+/* Y-E Data products */
+#define USB_PRODUCT_YEDATA_FLASHBUSTERU 0x0000 /* Flashbuster-U */
+
+/* Yamaha products */
+#define USB_PRODUCT_YAMAHA_UX256 0x1000 /* UX256 MIDI I/F */
+#define USB_PRODUCT_YAMAHA_UX96 0x1008 /* UX96 MIDI I/F */
+#define USB_PRODUCT_YAMAHA_RTA54I 0x4000 /* NetVolante RTA54i Broadband&ISDN Router */
+#define USB_PRODUCT_YAMAHA_RTA55I 0x4004 /* NetVolante RTA55i Broadband VoIP Router */
+#define USB_PRODUCT_YAMAHA_RTW65B 0x4001 /* NetVolante RTW65b Broadband Wireless Router */
+#define USB_PRODUCT_YAMAHA_RTW65I 0x4002 /* NetVolante RTW65i Broadband&ISDN Wireless Router */
+
+/* Yano products */
+#define USB_PRODUCT_YANO_U640MO 0x0101 /* U640MO-03 */
+
+/* Zoom Telephonics, Inc. products */
+#define USB_PRODUCT_ZOOM_2986L 0x9700 /* 2986L Fax modem */
+
+/* ZyXEL Communication Co. products */
+#define USB_PRODUCT_ZYXEL_OMNI56K 0x1500 /* Omni 56K Plus */
+#define USB_PRODUCT_ZYXEL_980N 0x2011 /* Scorpion-980N keyboard */
diff --git a/sys/dev/usb/usbdevs_data.h b/sys/dev/usb/usbdevs_data.h
new file mode 100644
index 0000000..344193b
--- /dev/null
+++ b/sys/dev/usb/usbdevs_data.h
@@ -0,0 +1,5335 @@
+/* $FreeBSD$ */
+
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
+ *
+ * generated from:
+ * FreeBSD: src/sys/dev/usb/usbdevs,v 1.183 2004/06/24 05:05:56 jb Exp
+ */
+
+/*
+ * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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.
+ */
+
+const struct usb_knowndev usb_knowndevs[] = {
+ {
+ USB_VENDOR_3COM, USB_PRODUCT_3COM_HOMECONN,
+ 0,
+ "3Com",
+ "HomeConnect USB Camera",
+ },
+ {
+ USB_VENDOR_3COM, USB_PRODUCT_3COM_3CREB96,
+ 0,
+ "3Com",
+ "Bluetooth USB dongle",
+ },
+ {
+ USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250,
+ 0,
+ "3Com",
+ "3C19250 Ethernet adapter",
+ },
+ {
+ USB_VENDOR_3COM, USB_PRODUCT_3COM_USR56K,
+ 0,
+ "3Com",
+ "U.S.Robotics 56000 Voice Faxmodem Pro",
+ },
+ {
+ USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460,
+ 0,
+ "3Com",
+ "HomeConnect 3C460",
+ },
+ {
+ USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B,
+ 0,
+ "3Com",
+ "HomeConnect 3C460B",
+ },
+ {
+ USB_VENDOR_3COMUSR, USB_PRODUCT_3COMUSR_OFFICECONN,
+ 0,
+ "U.S. Robotics",
+ "3Com OfficeConnect Analog Modem",
+ },
+ {
+ USB_VENDOR_3COMUSR, USB_PRODUCT_3COMUSR_USRISDN,
+ 0,
+ "U.S. Robotics",
+ "3Com U.S. Robotics Pro ISDN TA",
+ },
+ {
+ USB_VENDOR_3COMUSR, USB_PRODUCT_3COMUSR_HOMECONN,
+ 0,
+ "U.S. Robotics",
+ "3Com HomeConnect camera",
+ },
+ {
+ USB_VENDOR_3COMUSR, USB_PRODUCT_3COMUSR_USR56K,
+ 0,
+ "U.S. Robotics",
+ "U.S.Robotics 56000 Voice Faxmodem Pro",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1,
+ 0,
+ "AboCom Systems",
+ "XX1",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2,
+ 0,
+ "AboCom Systems",
+ "XX2",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450,
+ 0,
+ "AboCom Systems",
+ "URE450 Ethernet Adapter",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000,
+ 0,
+ "AboCom Systems",
+ "UFE1000 Fast Ethernet Adapter",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA,
+ 0,
+ "AboCom Systems",
+ "1/10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4,
+ 0,
+ "AboCom Systems",
+ "XX4",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5,
+ 0,
+ "AboCom Systems",
+ "XX5",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6,
+ 0,
+ "AboCom Systems",
+ "XX6",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7,
+ 0,
+ "AboCom Systems",
+ "XX7",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8,
+ 0,
+ "AboCom Systems",
+ "XX8",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9,
+ 0,
+ "AboCom Systems",
+ "XX9",
+ },
+ {
+ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10,
+ 0,
+ "AboCom Systems",
+ "XX10",
+ },
+ {
+ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC,
+ 0,
+ "Accton Technology",
+ "USB320-EC Ethernet Adapter",
+ },
+ {
+ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001,
+ 0,
+ "Accton Technology",
+ "SpeedStream Ethernet Adapter",
+ },
+ {
+ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U,
+ 0,
+ "Acer Peripherals",
+ "Acerscan C310U",
+ },
+ {
+ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U,
+ 0,
+ "Acer Peripherals",
+ "Acerscan 320U",
+ },
+ {
+ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U,
+ 0,
+ "Acer Peripherals",
+ "Acerscan 640U",
+ },
+ {
+ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U,
+ 0,
+ "Acer Peripherals",
+ "Acerscan 620U",
+ },
+ {
+ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_AWL300,
+ 0,
+ "Acer Peripherals",
+ "AWL300 Wireless adapter",
+ },
+ {
+ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_AWL400,
+ 0,
+ "Acer Peripherals",
+ "AWL400 Wireless adapter",
+ },
+ {
+ USB_VENDOR_ACTIVEWIRE, USB_PRODUCT_ACTIVEWIRE_IOBOARD,
+ 0,
+ "ActiveWire",
+ "I/O Board",
+ },
+ {
+ USB_VENDOR_ACTIVEWIRE, USB_PRODUCT_ACTIVEWIRE_IOBOARD_FW1,
+ 0,
+ "ActiveWire",
+ "I/O Board, rev. 1 firmware",
+ },
+ {
+ USB_VENDOR_ACTIONTEC, USB_PRODUCT_ACTIONTEC_UAT1,
+ 0,
+ "Actiontec Electronics",
+ "UAT1 Wireless Ethernet adapter",
+ },
+ {
+ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS,
+ 0,
+ "ADMtek",
+ "AN986 USB Ethernet adapter",
+ },
+ {
+ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII,
+ 0,
+ "ADMtek",
+ "AN8511 USB Ethernet adapter",
+ },
+ {
+ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2,
+ 0,
+ "ADMtek",
+ "AN8513 USB Ethernet adapter",
+ },
+ {
+ USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT,
+ 0,
+ "ADS Technologies",
+ "UBS-10BT Ethernet adapter",
+ },
+ {
+ USB_VENDOR_AGATE, USB_PRODUCT_AGATE_QDRIVE,
+ 0,
+ "Agate Technologies",
+ "Q-Drive",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan 1212U",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan 1236U",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan Touch",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan 1212U",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan e40",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan e50",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan e20",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan e25",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan e26",
+ },
+ {
+ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52,
+ 0,
+ "AGFA-Gevaert",
+ "SnapScan e52",
+ },
+ {
+ USB_VENDOR_AKS, USB_PRODUCT_AKS_USBHASP,
+ 0,
+ "Aladdin Knowledge Systems",
+ "USB-HASP 0.06",
+ },
+ {
+ USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB,
+ 0,
+ "Alcor Micro",
+ "Kbd Hub",
+ },
+ {
+ USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_MA_KBD_HUB,
+ 0,
+ "Alcor Micro",
+ "MacAlly Kbd Hub",
+ },
+ {
+ USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_AU9814,
+ 0,
+ "Alcor Micro",
+ "AU9814 Hub",
+ },
+ {
+ USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_SM_KBD,
+ 0,
+ "Alcor Micro",
+ "MicroConnectors/StrongMan Keyboard",
+ },
+ {
+ USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_NEC_KBD_HUB,
+ 0,
+ "Alcor Micro",
+ "NEC Kbd Hub",
+ },
+ {
+ USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70,
+ 0,
+ "Altec Lansing Technologies",
+ "ADA70 Speakers",
+ },
+ {
+ USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495,
+ 0,
+ "Altec Lansing Technologies",
+ "ASC495 Speakers",
+ },
+ {
+ USB_VENDOR_APC, USB_PRODUCT_APC_UPSPRO500,
+ 0,
+ "American Power Conversion",
+ "Back-UPS Pro 500",
+ },
+ {
+ USB_VENDOR_ANCHOR, USB_PRODUCT_ANCHOR_EZUSB,
+ 0,
+ "Anchor Chips",
+ "EZUSB",
+ },
+ {
+ USB_VENDOR_ANCHOR, USB_PRODUCT_ANCHOR_EZLINK,
+ 0,
+ "Anchor Chips",
+ "EZLINK",
+ },
+ {
+ USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101,
+ 0,
+ "AOX",
+ "USB ethernet controller engine",
+ },
+ {
+ USB_VENDOR_APPLE, USB_PRODUCT_APPLE_OPTMOUSE,
+ 0,
+ "Apple Computer",
+ "Optical mouse",
+ },
+ {
+ USB_VENDOR_APPLE, USB_PRODUCT_APPLE_SPEAKERS,
+ 0,
+ "Apple Computer",
+ "Speakers",
+ },
+ {
+ USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO230,
+ 0,
+ "Asahi Optical",
+ "Digital camera",
+ },
+ {
+ USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO330,
+ 0,
+ "Asahi Optical",
+ "Digital camera",
+ },
+ {
+ USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172,
+ 0,
+ "ASIX Electronics",
+ "USB 2.0 10/100 ethernet controller",
+ },
+ {
+ USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC1284,
+ 0,
+ "ATEN International",
+ "Parallel printer adapter",
+ },
+ {
+ USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T,
+ 0,
+ "ATEN International",
+ "10Mbps ethernet adapter",
+ },
+ {
+ USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A,
+ 0,
+ "ATEN International",
+ "Serial adapter",
+ },
+ {
+ USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_UHB124,
+ 0,
+ "Atmel",
+ "UHB124 hub",
+ },
+ {
+ USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_DWL120,
+ 0,
+ "Atmel",
+ "DWL-120 Wireless adapter",
+ },
+ {
+ USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_BW002,
+ 0,
+ "Atmel",
+ "BW002 Wireless adapter",
+ },
+ {
+ USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_AT76C505A,
+ 0,
+ "Atmel",
+ "AT76c505a Wireless adapter",
+ },
+ {
+ USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U,
+ 0,
+ "Avision",
+ "1200U scanner",
+ },
+ {
+ USB_VENDOR_BELKIN2, USB_PRODUCT_BELKIN2_F5U002,
+ 0,
+ "Belkin Components",
+ "F5U002 Parallel printer adapter",
+ },
+ {
+ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN,
+ 0,
+ "Belkin Components",
+ "USB to LAN Converter",
+ },
+ {
+ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103,
+ 0,
+ "Belkin Components",
+ "F5U103 Serial adapter",
+ },
+ {
+ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109,
+ 0,
+ "Belkin Components",
+ "F5U109 Serial adapter",
+ },
+ {
+ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120,
+ 0,
+ "Belkin Components",
+ "F5U120-PC Hub",
+ },
+ {
+ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U208,
+ 0,
+ "Belkin Components",
+ "F5U208 VideoBus II",
+ },
+ {
+ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100,
+ 0,
+ "Billionton Systems",
+ "USB100N 10/100 FastEthernet Adapter",
+ },
+ {
+ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100,
+ 0,
+ "Billionton Systems",
+ "USB100LP",
+ },
+ {
+ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100,
+ 0,
+ "Billionton Systems",
+ "USB100EL",
+ },
+ {
+ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100,
+ 0,
+ "Billionton Systems",
+ "USBE100",
+ },
+ {
+ USB_VENDOR_BROTHER, USB_PRODUCT_BROTHER_HL1050,
+ 0,
+ "Brother Industries",
+ "HL-1050 laser printer",
+ },
+ {
+ USB_VENDOR_BTC, USB_PRODUCT_BTC_BTC7932,
+ 0,
+ "Behavior Tech. Computer",
+ "Keyboard with mouse port",
+ },
+ {
+ USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033,
+ 0,
+ "Broadcom",
+ "BCM2033 Bluetooth USB dongle",
+ },
+ {
+ USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U,
+ 0,
+ "Canon",
+ "CanoScan N656U",
+ },
+ {
+ USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U,
+ 0,
+ "Canon",
+ "CanoScan N1220U",
+ },
+ {
+ USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U,
+ 0,
+ "Canon",
+ "CanoScan N676U",
+ },
+ {
+ USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U,
+ 0,
+ "Canon",
+ "CanoScan N1240U",
+ },
+ {
+ USB_VENDOR_CANON, USB_PRODUCT_CANON_S10,
+ 0,
+ "Canon",
+ "PowerShot S10",
+ },
+ {
+ USB_VENDOR_CANON, USB_PRODUCT_CANON_S100,
+ 0,
+ "Canon",
+ "PowerShot S100",
+ },
+ {
+ USB_VENDOR_CANON, USB_PRODUCT_CANON_S200,
+ 0,
+ "Canon",
+ "PowerShot S200",
+ },
+ {
+ USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE,
+ 0,
+ "Computer Access Technology",
+ "Netmate ethernet adapter",
+ },
+ {
+ USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2,
+ 0,
+ "Computer Access Technology",
+ "Netmate2 ethernet adapter",
+ },
+ {
+ USB_VENDOR_CATC, USB_PRODUCT_CATC_CHIEF,
+ 0,
+ "Computer Access Technology",
+ "USB Chief Bus & Protocol Analyzer",
+ },
+ {
+ USB_VENDOR_CATC, USB_PRODUCT_CATC_ANDROMEDA,
+ 0,
+ "Computer Access Technology",
+ "Andromeda hub",
+ },
+ {
+ USB_VENDOR_CASIO, USB_PRODUCT_CASIO_NAMELAND,
+ 0,
+ "CASIO",
+ "CASIO Nameland EZ-USB",
+ },
+ {
+ USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_MY3000KBD,
+ 0,
+ "Cherry Mikroschalter",
+ "My3000 keyboard",
+ },
+ {
+ USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_MY3000HUB,
+ 0,
+ "Cherry Mikroschalter",
+ "My3000 hub",
+ },
+ {
+ USB_VENDOR_CHERRY, USB_PRODUCT_CHERRY_CYBOARD,
+ 0,
+ "Cherry Mikroschalter",
+ "CyBoard Keyboard",
+ },
+ {
+ USB_VENDOR_CHIC, USB_PRODUCT_CHIC_MOUSE1,
+ 0,
+ "Chic Technology",
+ "mouse",
+ },
+ {
+ USB_VENDOR_CHIC, USB_PRODUCT_CHIC_CYPRESS,
+ 0,
+ "Chic Technology",
+ "Cypress USB Mouse",
+ },
+ {
+ USB_VENDOR_CHICONY, USB_PRODUCT_CHICONY_KB8933,
+ 0,
+ "Chicony Electronics",
+ "KB-8933 keyboard",
+ },
+ {
+ USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_PJB100,
+ 0,
+ "Compaq Computers",
+ "Personal Jukebox PJB100",
+ },
+ {
+ USB_VENDOR_CONNECTIX, USB_PRODUCT_CONNECTIX_QUICKCAM,
+ 0,
+ "Connectix",
+ "QuickCam",
+ },
+ {
+ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T,
+ 0,
+ "Corega",
+ "Ether USB-T",
+ },
+ {
+ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX,
+ 0,
+ "Corega",
+ "FEther USB-TX",
+ },
+ {
+ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS,
+ 0,
+ "Corega",
+ "FEther USB-TXS",
+ },
+ {
+ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC,
+ 0,
+ "Corega",
+ "FEther USB-TXC",
+ },
+ {
+ USB_VENDOR_CREATIVE, USB_PRODUCT_CREATIVE_NOMAD_II,
+ 0,
+ "Creative",
+ "Nomad II MP3 player",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631,
+ 0,
+ "Future Technology Devices",
+ "Crystalfontz CFA-631 USB LCD",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632,
+ 0,
+ "Future Technology Devices",
+ "Crystalfontz CFA-632 USB LCD",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633,
+ 0,
+ "Future Technology Devices",
+ "Crystalfontz CFA-633 USB LCD",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634,
+ 0,
+ "Future Technology Devices",
+ "Crystalfontz CFA-634 USB LCD",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20,
+ 0,
+ "Future Technology Devices",
+ "SEMC DSS-20 SyncStation",
+ },
+ {
+ USB_VENDOR_CSR, USB_PRODUCT_CSR_BT_DONGLE,
+ 0,
+ "Cambridge Silicon Radio Ltd.",
+ "Bluetooth USB dongle",
+ },
+ {
+ USB_VENDOR_CSR, USB_PRODUCT_CSR_CSRDFU,
+ 0,
+ "Cambridge Silicon Radio Ltd.",
+ "USB Bluetooth Device in DFU State",
+ },
+ {
+ USB_VENDOR_CTX, USB_PRODUCT_CTX_EX1300,
+ 0,
+ "Chuntex",
+ "Ex1300 hub",
+ },
+ {
+ USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_MOUSE,
+ 0,
+ "Cypress Semiconductor",
+ "mouse",
+ },
+ {
+ USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_THERMO,
+ 0,
+ "Cypress Semiconductor",
+ "thermometer",
+ },
+ {
+ USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_FMRADIO,
+ 0,
+ "Cypress Semiconductor",
+ "FM Radio",
+ },
+ {
+ USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_SLIM_HUB,
+ 0,
+ "Cypress Semiconductor",
+ "Slim Hub",
+ },
+ {
+ USB_VENDOR_DAISY, USB_PRODUCT_DAISY_DMC,
+ 0,
+ "Daisy Technology",
+ "USB MultiMedia Reader",
+ },
+ {
+ USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502,
+ 0,
+ "Dallas Semiconductor",
+ "J-6502 speakers",
+ },
+ {
+ USB_VENDOR_DELL, USB_PRODUCT_DELL_BC02,
+ 0,
+ "Dell",
+ "Dell BC02 Bluetooth USB Adapter",
+ },
+ {
+ USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE,
+ 0,
+ "Delorme Publishing",
+ "Earthmate GPS",
+ },
+ {
+ USB_VENDOR_DIAMOND, USB_PRODUCT_DIAMOND_RIO500USB,
+ 0,
+ "Diamond",
+ "Rio 500 USB",
+ },
+ {
+ USB_VENDOR_DIGI, USB_PRODUCT_DIGI_ACCELEPORT2,
+ 0,
+ "Digi International",
+ "AccelePort USB 2",
+ },
+ {
+ USB_VENDOR_DIGI, USB_PRODUCT_DIGI_ACCELEPORT4,
+ 0,
+ "Digi International",
+ "AccelePort USB 4",
+ },
+ {
+ USB_VENDOR_DIGI, USB_PRODUCT_DIGI_ACCELEPORT8,
+ 0,
+ "Digi International",
+ "AccelePort USB 8",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100,
+ 0,
+ "D-Link",
+ "10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4,
+ 0,
+ "D-Link",
+ "10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C,
+ 0,
+ "D-Link",
+ "10Mbps ethernet adapter",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1,
+ 0,
+ "D-Link",
+ "10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX,
+ 0,
+ "D-Link",
+ "10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA,
+ 0,
+ "D-Link",
+ "1/10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3,
+ 0,
+ "D-Link",
+ "10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2,
+ 0,
+ "D-Link",
+ "10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650,
+ 0,
+ "D-Link",
+ "10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_EIZO, USB_PRODUCT_EIZO_HUB,
+ 0,
+ "EIZO",
+ "hub",
+ },
+ {
+ USB_VENDOR_EIZO, USB_PRODUCT_EIZO_MONITOR,
+ 0,
+ "EIZO",
+ "monitor",
+ },
+ {
+ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_MOUSE29UO,
+ 0,
+ "Elecom",
+ "mouse 29UO",
+ },
+ {
+ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0,
+ 0,
+ "Elecom",
+ "LD-USB/TX",
+ },
+ {
+ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1,
+ 0,
+ "Elecom",
+ "LD-USB/TX",
+ },
+ {
+ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX,
+ 0,
+ "Elecom",
+ "LD-USBL/TX",
+ },
+ {
+ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2,
+ 0,
+ "Elecom",
+ "LD-USB/TX",
+ },
+ {
+ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT,
+ 0,
+ "Elecom",
+ "UC-SGT",
+ },
+ {
+ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3,
+ 0,
+ "Elecom",
+ "LD-USB/TX",
+ },
+ {
+ USB_VENDOR_ELSA, USB_PRODUCT_ELSA_MODEM1,
+ 0,
+ "ELSA",
+ "ELSA Modem Board",
+ },
+ {
+ USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET,
+ 0,
+ "ELSA",
+ "Microlink USB2Ethernet",
+ },
+ {
+ USB_VENDOR_EMS, USB_PRODUCT_EMS_DUAL_SHOOTER,
+ 0,
+ "EMS Production Ltd.",
+ "PSX gun controller converter",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_1S,
+ 0,
+ "Entrega",
+ "1S serial connector",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_2S,
+ 0,
+ "Entrega",
+ "2S serial connector",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_1S25,
+ 0,
+ "Entrega",
+ "1S25 serial connector",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_4S,
+ 0,
+ "Entrega",
+ "4S serial connector",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45,
+ 0,
+ "Entrega",
+ "E45 Ethernet adapter",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_CENTRONICS,
+ 0,
+ "Entrega",
+ "Centronics connector",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_1S9,
+ 0,
+ "Entrega",
+ "1S9 serial connector",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_EZUSB,
+ 0,
+ "Entrega",
+ "EZ-USB",
+ },
+ {
+ USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_2U4S,
+ 0,
+ "Entrega",
+ "2U4S serial connector/usb hub",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_PRINTER1,
+ 0,
+ "Seiko Epson",
+ "USB Printer",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_PRINTER2,
+ 0,
+ "Seiko Epson",
+ "ISD USB Smart Cable for Mac",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_PRINTER3,
+ 0,
+ "Seiko Epson",
+ "ISD USB Smart Cable",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_PRINTER5,
+ 0,
+ "Seiko Epson",
+ "USB Printer",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636,
+ 0,
+ "Seiko Epson",
+ "Perfection 636U / 636Photo scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610,
+ 0,
+ "Seiko Epson",
+ "Perfection 610 scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200,
+ 0,
+ "Seiko Epson",
+ "Perfection 1200U / 1200Photo scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600,
+ 0,
+ "Seiko Epson",
+ "Expression 1600 scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640,
+ 0,
+ "Seiko Epson",
+ "Perfection 1640SU scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240,
+ 0,
+ "Seiko Epson",
+ "Perfection 1240U / 1240Photo scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U,
+ 0,
+ "Seiko Epson",
+ "Perfection 640U scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250,
+ 0,
+ "Seiko Epson",
+ "Perfection 1250U / 1250Photo scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650,
+ 0,
+ "Seiko Epson",
+ "Perfection 1650 scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F,
+ 0,
+ "Seiko Epson",
+ "GT-9700F scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF,
+ 0,
+ "Seiko Epson",
+ "GT-9300UF scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200,
+ 0,
+ "Seiko Epson",
+ "Perfection 3200 scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260,
+ 0,
+ "Seiko Epson",
+ "Perfection 1260 scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660,
+ 0,
+ "Seiko Epson",
+ "Perfection 1660 scanner",
+ },
+ {
+ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670,
+ 0,
+ "Seiko Epson",
+ "Perfection 1670 scanner",
+ },
+ {
+ USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM,
+ 0,
+ "e-TEK Labs",
+ "Serial port",
+ },
+ {
+ USB_VENDOR_EXTENDED, USB_PRODUCT_EXTENDED_XTNDACCESS,
+ 0,
+ "Extended Systems",
+ "XTNDAccess IrDA",
+ },
+ {
+ USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232,
+ 0,
+ "GoHubs",
+ "GoCOM232 Serial converter",
+ },
+ {
+ USB_VENDOR_GRAVIS, USB_PRODUCT_GRAVIS_GAMEPADPRO,
+ 0,
+ "Advanced Gravis Computer Tech.",
+ "GamePad Pro",
+ },
+ {
+ USB_VENDOR_GREENHOUSE, USB_PRODUCT_GREENHOUSE_KANA21,
+ 0,
+ "GREENHOUSE",
+ "CF-writer with Portable MP3 Player",
+ },
+ {
+ USB_VENDOR_GRIFFIN, USB_PRODUCT_GRIFFIN_IMATE,
+ 0,
+ "Griffin Technology",
+ "iMate, ADB adapter",
+ },
+ {
+ USB_VENDOR_FREECOM, USB_PRODUCT_FREECOM_DVD,
+ 0,
+ "Freecom",
+ "Connector for DVD drive",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX,
+ 0,
+ "Future Technology Devices",
+ "8U100AX Serial converter",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM,
+ 0,
+ "Future Technology Devices",
+ "8U232AM Serial converter",
+ },
+ {
+ USB_VENDOR_FUJIPHOTO, USB_PRODUCT_FUJIPHOTO_MASS0100,
+ 0,
+ "Fuji Photo Film",
+ "Mass Storage",
+ },
+ {
+ USB_VENDOR_FUJITSU, USB_PRODUCT_FUJITSU_AH_F401U,
+ 0,
+ "Fujitsu",
+ "AH-F401U Air H device",
+ },
+ {
+ USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_CDMA_MSM,
+ 0,
+ "Qualcomm",
+ "CDMA Technologies MSM phone",
+ },
+ {
+ USB_VENDOR_GENERALINSTMNTS, USB_PRODUCT_GENERALINSTMNTS_SB5100,
+ 0,
+ "General Instruments (Motorola)",
+ "SURFboard SB5100 Cable modem",
+ },
+ {
+ USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL650,
+ 0,
+ "Genesys Logic",
+ "GL650 Hub",
+ },
+ {
+ USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB,
+ 0,
+ "Genesys Logic",
+ "GL641USB CompactFlash Card Reader",
+ },
+ {
+ USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE_2,
+ 0,
+ "Genesys Logic",
+ "GL641USB USB-IDE Bridge No 2",
+ },
+ {
+ USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE,
+ 0,
+ "Genesys Logic",
+ "GL641USB USB-IDE Bridge",
+ },
+ {
+ USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001,
+ 0,
+ "HAL Corporation",
+ "Crossam2+USB IR commander",
+ },
+ {
+ USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FGSM,
+ 0,
+ "Hagiwara Sys-Com",
+ "FlashGate SmartMedia Card Reader",
+ },
+ {
+ USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FGCF,
+ 0,
+ "Hagiwara Sys-Com",
+ "FlashGate CompactFlash Card Reader",
+ },
+ {
+ USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FG,
+ 0,
+ "Hagiwara Sys-Com",
+ "FlashGate",
+ },
+ {
+ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR,
+ 0,
+ "Handspring",
+ "Handspring Visor",
+ },
+ {
+ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO,
+ 0,
+ "Handspring",
+ "Handspring Treo",
+ },
+ {
+ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600,
+ 0,
+ "Handspring",
+ "Handspring Treo 600",
+ },
+ {
+ USB_VENDOR_HAUPPAUGE, USB_PRODUCT_HAUPPAUGE_WINTV_USB_FM,
+ 0,
+ "Hauppauge Computer Works",
+ "WinTV USB FM",
+ },
+ {
+ USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100,
+ 0,
+ "Hawking Technologies",
+ "10/100 USB Ethernet",
+ },
+ {
+ USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_USB,
+ 0,
+ "Hitachi, Ltd.",
+ "DVDCAM USB HS Interface",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_895C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 895C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_4100C,
+ 0,
+ "Hewlett Packard",
+ "Scanjet 4100C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_S20,
+ 0,
+ "Hewlett Packard",
+ "Photosmart S20",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_880C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 880C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_4200C,
+ 0,
+ "Hewlett Packard",
+ "ScanJet 4200C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_CDWRITERPLUS,
+ 0,
+ "Hewlett Packard",
+ "CD-Writer Plus",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_KBDHUB,
+ 0,
+ "Hewlett Packard",
+ "Multimedia Keyboard Hub",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_6200C,
+ 0,
+ "Hewlett Packard",
+ "ScanJet 6200C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_S20b,
+ 0,
+ "Hewlett Packard",
+ "PhotoSmart S20",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_815C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 815C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_3300C,
+ 0,
+ "Hewlett Packard",
+ "ScanJet 3300C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_CDW8200,
+ 0,
+ "Hewlett Packard",
+ "CD-Writer Plus 8200e",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_1220C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 1220C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_810C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 810C/812C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_4300C,
+ 0,
+ "Hewlett Packard",
+ "Scanjet 4300C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_G85XI,
+ 0,
+ "Hewlett Packard",
+ "OfficeJet G85xi",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_1200,
+ 0,
+ "Hewlett Packard",
+ "LaserJet 1200",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_5200C,
+ 0,
+ "Hewlett Packard",
+ "Scanjet 5200C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_830C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 830C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE,
+ 0,
+ "Hewlett Packard",
+ "ScanJet 3400cse",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_6300C,
+ 0,
+ "Hewlett Packard",
+ "Scanjet 6300C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_840C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 840c",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_2200C,
+ 0,
+ "Hewlett Packard",
+ "ScanJet 2200C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_5300C,
+ 0,
+ "Hewlett Packard",
+ "Scanjet 5300C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_4400C,
+ 0,
+ "Hewlett Packard",
+ "Scanjet 4400C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_970CSE,
+ 0,
+ "Hewlett Packard",
+ "Deskjet 970Cse",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_5400C,
+ 0,
+ "Hewlett Packard",
+ "Scanjet 5400C",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_930C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 930c",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_P2000U,
+ 0,
+ "Hewlett Packard",
+ "Inkjet P-2000U",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_640C,
+ 0,
+ "Hewlett Packard",
+ "DeskJet 640c",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_P1100,
+ 0,
+ "Hewlett Packard",
+ "Photosmart P1100",
+ },
+ {
+ USB_VENDOR_HP, USB_PRODUCT_HP_HN210E,
+ 0,
+ "Hewlett Packard",
+ "Ethernet HN210E",
+ },
+ {
+ USB_VENDOR_HP2, USB_PRODUCT_HP2_C500,
+ 0,
+ "Hewlett Packard",
+ "PhotoSmart C500",
+ },
+ {
+ USB_VENDOR_IBM, USB_PRODUCT_IBM_USBCDROMDRIVE,
+ 0,
+ "IBM Corporation",
+ "USB CD-ROM Drive",
+ },
+ {
+ USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4,
+ 0,
+ "Inside Out Networks",
+ "EdgePort/4 serial ports",
+ },
+ {
+ USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_F5U002,
+ 0,
+ "In-System Design",
+ "Parallel printer adapter",
+ },
+ {
+ USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ATAPI,
+ 0,
+ "In-System Design",
+ "ATAPI adapter",
+ },
+ {
+ USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ISD110,
+ 0,
+ "In-System Design",
+ "IDE adapter ISD110",
+ },
+ {
+ USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ISD105,
+ 0,
+ "In-System Design",
+ "IDE adapter ISD105",
+ },
+ {
+ USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_USBCABLE,
+ 0,
+ "In-System Design",
+ "USB cable",
+ },
+ {
+ USB_VENDOR_INTEL, USB_PRODUCT_INTEL_EASYPC_CAMERA,
+ 0,
+ "Intel",
+ "Easy PC Camera",
+ },
+ {
+ USB_VENDOR_INTEL, USB_PRODUCT_INTEL_TESTBOARD,
+ 0,
+ "Intel",
+ "82930 test board",
+ },
+ {
+ USB_VENDOR_INTERSIL, USB_PRODUCT_INTERSIL_PRISM_2X,
+ 0,
+ "Intersil",
+ "Prism2.x or Atmel WLAN",
+ },
+ {
+ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT,
+ 0,
+ "I/O Data",
+ "USB ETT",
+ },
+ {
+ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX,
+ 0,
+ "I/O Data",
+ "USB ETTX",
+ },
+ {
+ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS,
+ 0,
+ "I/O Data",
+ "USB ETTX",
+ },
+ {
+ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ,
+ 0,
+ "I/O Data",
+ "USB serial adapter USB-RSAQ1",
+ },
+ {
+ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_IU_CD2,
+ 0,
+ "I/O Data",
+ "DVD Multi-plus unit iU-CD2",
+ },
+ {
+ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_DVR_UEH8,
+ 0,
+ "I/O Data",
+ "DVD Multi-plus unit DVR-UEH8",
+ },
+ {
+ USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP100,
+ 0,
+ "Iomega",
+ "Zip 100",
+ },
+ {
+ USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP250,
+ 0,
+ "Iomega",
+ "Zip 250",
+ },
+ {
+ USB_VENDOR_JVC, USB_PRODUCT_JVC_GR_DX95,
+ 0,
+ "JVC",
+ "GR-DX95",
+ },
+ {
+ USB_VENDOR_JRC, USB_PRODUCT_JRC_AH_J3001V_J3002V,
+ 0,
+ "Japan Radio Company",
+ "AirH\" PHONE AH-J3001V/J3002V",
+ },
+ {
+ USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT,
+ 0,
+ "Kawasaki LSI",
+ "USB ethernet controller engine",
+ },
+ {
+ USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN,
+ 0,
+ "Kawasaki LSI",
+ "USB ethernet controller engine",
+ },
+ {
+ USB_VENDOR_KAWATSU, USB_PRODUCT_KAWATSU_MH4000P,
+ 0,
+ "Kawatsu Semiconductor",
+ "MiniHub 4000P",
+ },
+ {
+ USB_VENDOR_KEISOKUGIKEN, USB_PRODUCT_KEISOKUGIKEN_USBDAQ,
+ 0,
+ "Keisokugiken",
+ "HKS-0200 USBDAQ",
+ },
+ {
+ USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT,
+ 0,
+ "Kawasaki LSI",
+ "10BT Ethernet adapter, in the DU-H3E",
+ },
+ {
+ USB_VENDOR_KENSINGTON, USB_PRODUCT_KENSINGTON_ORBIT,
+ 0,
+ "Kensington",
+ "Orbit USB/PS2 trackball",
+ },
+ {
+ USB_VENDOR_KENSINGTON, USB_PRODUCT_KENSINGTON_TURBOBALL,
+ 0,
+ "Kensington",
+ "TurboBall",
+ },
+ {
+ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28,
+ 0,
+ "Keyspan",
+ "USA-28 serial adapter",
+ },
+ {
+ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA28X,
+ 0,
+ "Keyspan",
+ "USA-28X serial adapter",
+ },
+ {
+ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19,
+ 0,
+ "Keyspan",
+ "USA-19 serial adapter",
+ },
+ {
+ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA18X,
+ 0,
+ "Keyspan",
+ "USA-18X serial adapter",
+ },
+ {
+ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19W,
+ 0,
+ "Keyspan",
+ "USA-19W serial adapter",
+ },
+ {
+ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA49W,
+ 0,
+ "Keyspan",
+ "USA-49W serial adapter",
+ },
+ {
+ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19QW,
+ 0,
+ "Keyspan",
+ "USA-19QW serial adapter",
+ },
+ {
+ USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX,
+ 0,
+ "Kingston Technology",
+ "KNU101TX USB Ethernet",
+ },
+ {
+ USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC220,
+ 0,
+ "Eastman Kodak",
+ "Digital Science DC220",
+ },
+ {
+ USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC260,
+ 0,
+ "Eastman Kodak",
+ "Digital Science DC260",
+ },
+ {
+ USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC265,
+ 0,
+ "Eastman Kodak",
+ "Digital Science DC265",
+ },
+ {
+ USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC290,
+ 0,
+ "Eastman Kodak",
+ "Digital Science DC290",
+ },
+ {
+ USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC240,
+ 0,
+ "Eastman Kodak",
+ "Digital Science DC240",
+ },
+ {
+ USB_VENDOR_KODAK, USB_PRODUCT_KODAK_DC280,
+ 0,
+ "Eastman Kodak",
+ "Digital Science DC280",
+ },
+ {
+ USB_VENDOR_KONICA, USB_PRODUCT_KONICA_CAMERA,
+ 0,
+ "Konica",
+ "Digital Color Camera",
+ },
+ {
+ USB_VENDOR_KYE, USB_PRODUCT_KYE_NICHE,
+ 0,
+ "KYE Systems",
+ "Niche mouse",
+ },
+ {
+ USB_VENDOR_KYE, USB_PRODUCT_KYE_NETSCROLL,
+ 0,
+ "KYE Systems",
+ "Genius NetScroll mouse",
+ },
+ {
+ USB_VENDOR_KYE, USB_PRODUCT_KYE_FLIGHT2000,
+ 0,
+ "KYE Systems",
+ "Flight 2000 joystick",
+ },
+ {
+ USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO,
+ 0,
+ "KYE Systems",
+ "ColorPage Vivid-Pro scanner",
+ },
+ {
+ USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V,
+ 0,
+ "Kyocera Corp.",
+ "AH-K3001V",
+ },
+ {
+ USB_VENDOR_LACIE, USB_PRODUCT_LACIE_HD,
+ 0,
+ "LaCie",
+ "Hard Disk",
+ },
+ {
+ USB_VENDOR_LACIE, USB_PRODUCT_LACIE_CDRW,
+ 0,
+ "LaCie",
+ "CD R/W",
+ },
+ {
+ USB_VENDOR_LEXAR, USB_PRODUCT_LEXAR_JUMPSHOT,
+ 0,
+ "Lexar Media",
+ "jumpSHOT CompactFlash Reader",
+ },
+ {
+ USB_VENDOR_LEXMARK, USB_PRODUCT_LEXMARK_S2450,
+ 0,
+ "Lexmark International",
+ "Optra S 2450",
+ },
+ {
+ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_MAUSB2,
+ 0,
+ "Linksys",
+ "Camedia MAUSB-2",
+ },
+ {
+ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1,
+ 0,
+ "Linksys",
+ "USB10TX",
+ },
+ {
+ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T,
+ 0,
+ "Linksys",
+ "USB10T Ethernet",
+ },
+ {
+ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX,
+ 0,
+ "Linksys",
+ "USB100TX Ethernet",
+ },
+ {
+ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1,
+ 0,
+ "Linksys",
+ "USB100H1 Ethernet/HPNA",
+ },
+ {
+ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA,
+ 0,
+ "Linksys",
+ "USB10TA Ethernet",
+ },
+ {
+ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2,
+ 0,
+ "Linksys",
+ "USB10TX",
+ },
+ {
+ USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_WUSB11,
+ 0,
+ "Linksys",
+ "WUSB11 Wireless adapter",
+ },
+ {
+ USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M,
+ 0,
+ "Linksys",
+ "USB 2.0 10/100 ethernet controller",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_M2452,
+ 0,
+ "Logitech",
+ "M2452 keyboard",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_M4848,
+ 0,
+ "Logitech",
+ "M4848 mouse",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_PAGESCAN,
+ 0,
+ "Logitech",
+ "PageScan",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMWEB,
+ 0,
+ "Logitech",
+ "QuickCam Web",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRO,
+ 0,
+ "Logitech",
+ "QuickCam Pro",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMEXP,
+ 0,
+ "Logitech",
+ "QuickCam Express",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAM,
+ 0,
+ "Logitech",
+ "QuickCam",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_N43,
+ 0,
+ "Logitech",
+ "N43",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_N48,
+ 0,
+ "Logitech",
+ "N48 mouse",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_MBA47,
+ 0,
+ "Logitech",
+ "M-BA47 mouse",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMMOUSE,
+ 0,
+ "Logitech",
+ "WingMan Gaming Mouse",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_BD58,
+ 0,
+ "Logitech",
+ "BD58 mouse",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_UN58A,
+ 0,
+ "Logitech",
+ "iFeel Mouse",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_BB13,
+ 0,
+ "Logitech",
+ "USB-PS/2 Trackball",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMPAD,
+ 0,
+ "Logitech",
+ "WingMan GamePad Extreme",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMRPAD,
+ 0,
+ "Logitech",
+ "WingMan RumblePad",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WMJOY,
+ 0,
+ "Logitech",
+ "WingMan Force joystick",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_RK53,
+ 0,
+ "Logitech",
+ "Cordless mouse",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_RB6,
+ 0,
+ "Logitech",
+ "Cordless keyboard",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_MX700,
+ 0,
+ "Logitech",
+ "Cordless optical mouse",
+ },
+ {
+ USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRO2,
+ 0,
+ "Logitech",
+ "QuickCam Pro",
+ },
+ {
+ USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443SU2,
+ 0,
+ "Logitec Corp",
+ "DVD Multi-plus unit LDR-H443SU2",
+ },
+ {
+ USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443U2,
+ 0,
+ "Logitec Corp",
+ "DVD Multi-plus unit LDR-H443U2",
+ },
+ {
+ USB_VENDOR_LUCENT, USB_PRODUCT_LUCENT_EVALKIT,
+ 0,
+ "Lucent",
+ "USS-720 evaluation kit",
+ },
+ {
+ USB_VENDOR_LUWEN, USB_PRODUCT_LUWEN_EASYDISK,
+ 0,
+ "Luwen",
+ "EasyDisc",
+ },
+ {
+ USB_VENDOR_MACALLY, USB_PRODUCT_MACALLY_MOUSE1,
+ 0,
+ "Macally",
+ "mouse",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL,
+ 0,
+ "Future Technology Devices",
+ "Matrix Orbital USB Serial",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3,
+ 0,
+ "Future Technology Devices",
+ "Matrix Orbital MX2 or MX3",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5,
+ 0,
+ "Future Technology Devices",
+ "Matrix Orbital MX4 or MX5",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202,
+ 0,
+ "Future Technology Devices",
+ "Matrix Orbital VK/LK202 Family",
+ },
+ {
+ USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204,
+ 0,
+ "Future Technology Devices",
+ "Matrix Orbital VK/LK204 Family",
+ },
+ {
+ USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100,
+ 0,
+ "MCT",
+ "Hub",
+ },
+ {
+ USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232,
+ 0,
+ "MCT",
+ "D-Link DU-H3SP USB BAY Hub",
+ },
+ {
+ USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232,
+ 0,
+ "MCT",
+ "USB-232 Interface",
+ },
+ {
+ USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232,
+ 0,
+ "MCT",
+ "Sitecom USB-232 Products",
+ },
+ {
+ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1,
+ 0,
+ "Melco",
+ "LUA-TX Ethernet",
+ },
+ {
+ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5,
+ 0,
+ "Melco",
+ "LUA-TX Ethernet",
+ },
+ {
+ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5,
+ 0,
+ "Melco",
+ "LUA2-TX Ethernet",
+ },
+ {
+ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX,
+ 0,
+ "Melco",
+ "LUA-KTX Ethernet",
+ },
+ {
+ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_DUBPXXG,
+ 0,
+ "Melco",
+ "USB-IDE Bridge: DUB-PxxG",
+ },
+ {
+ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX,
+ 0,
+ "Melco",
+ "LUA-U2-KTX Ethernet",
+ },
+ {
+ USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS,
+ 0,
+ "Metricom",
+ "Ricochet GS",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_SIDEPREC,
+ 0,
+ "Microsoft",
+ "SideWinder Precision Pro",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_INTELLIMOUSE,
+ 0,
+ "Microsoft",
+ "IntelliMouse",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_NATURALKBD,
+ 0,
+ "Microsoft",
+ "Natural Keyboard Elite",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_DDS80,
+ 0,
+ "Microsoft",
+ "Digital Sound System 80",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_SIDEWINDER,
+ 0,
+ "Microsoft",
+ "Sidewinder Precision Racing Wheel",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_INETPRO,
+ 0,
+ "Microsoft",
+ "Internet Keyboard Pro",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_INTELLIEYE,
+ 0,
+ "Microsoft",
+ "IntelliEye mouse",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_INETPRO2,
+ 0,
+ "Microsoft",
+ "Internet Keyboard Pro",
+ },
+ {
+ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110,
+ 0,
+ "Microsoft",
+ "10/100 USB NIC",
+ },
+ {
+ USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_SCSIDB25,
+ 0,
+ "Microtech",
+ "USB-SCSI-DB25",
+ },
+ {
+ USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_SCSIHD50,
+ 0,
+ "Microtech",
+ "USB-SCSI-HD50",
+ },
+ {
+ USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_DPCM,
+ 0,
+ "Microtech",
+ "USB CameraMate",
+ },
+ {
+ USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_FREECOM,
+ 0,
+ "Microtech",
+ "Freecom USB-IDE",
+ },
+ {
+ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX,
+ 0,
+ "Microtek",
+ "Phantom 336CX - C3 scanner",
+ },
+ {
+ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U,
+ 0,
+ "Microtek",
+ "ScanMaker X6 - X6U",
+ },
+ {
+ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6,
+ 0,
+ "Microtek",
+ "Phantom C6 scanner",
+ },
+ {
+ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2,
+ 0,
+ "Microtek",
+ "Phantom 336CX - C3 scanner",
+ },
+ {
+ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL,
+ 0,
+ "Microtek",
+ "ScanMaker V6USL",
+ },
+ {
+ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2,
+ 0,
+ "Microtek",
+ "ScanMaker V6USL",
+ },
+ {
+ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL,
+ 0,
+ "Microtek",
+ "ScanMaker V6UL",
+ },
+ {
+ USB_VENDOR_MIDIMAN, USB_PRODUCT_MIDIMAN_MIDISPORT2X2,
+ 0,
+ "Midiman",
+ "Midisport 2x2",
+ },
+ {
+ USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_2300,
+ 0,
+ "Minolta",
+ "Dimage 2300",
+ },
+ {
+ USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_S304,
+ 0,
+ "Minolta",
+ "Dimage S304",
+ },
+ {
+ USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_X,
+ 0,
+ "Minolta",
+ "Dimage X",
+ },
+ {
+ USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400,
+ 0,
+ "Minolta",
+ "Dimage 5400",
+ },
+ {
+ USB_VENDOR_MSI, USB_PRODUCT_MSI_BT_DONGLE,
+ 0,
+ "Micro Star International",
+ "Bluetooth USB dongle",
+ },
+ {
+ USB_VENDOR_MICROTUNE, USB_PRODUCT_MICROTUNE_BT_DONGLE,
+ 0,
+ "Microtune, Inc.",
+ "Bluetooth USB dongle",
+ },
+ {
+ USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_CDRRW,
+ 0,
+ "Mitsumi",
+ "CD-R/RW Drive",
+ },
+ {
+ USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_BT_DONGLE,
+ 0,
+ "Mitsumi",
+ "Bluetooth USB dongle",
+ },
+ {
+ USB_VENDOR_MOTOROLA, USB_PRODUCT_MOTOROLA_MC141555,
+ 0,
+ "Motorola",
+ "MC141555 hub controller",
+ },
+ {
+ USB_VENDOR_MOTOROLA, USB_PRODUCT_MOTOROLA_SB4100,
+ 0,
+ "Motorola",
+ "SB4100 USB Cable Modem",
+ },
+ {
+ USB_VENDOR_MULTITECH, USB_PRODUCT_MULTITECH_ATLAS,
+ 0,
+ "MultiTech",
+ "MT5634ZBA-USB modem",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU,
+ 0,
+ "Mustek Systems",
+ "1200 CU scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU,
+ 0,
+ "Mustek Systems",
+ "600 CU scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB,
+ 0,
+ "Mustek Systems",
+ "1200 USB scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB,
+ 0,
+ "Mustek Systems",
+ "1200 UB scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS,
+ 0,
+ "Mustek Systems",
+ "1200 USB Plus scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS,
+ 0,
+ "Mustek Systems",
+ "1200 CU Plus scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F,
+ 0,
+ "Mustek Systems",
+ "BearPaw 1200F scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA,
+ 0,
+ "Mustek Systems",
+ "BearPaw 1200TA scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB,
+ 0,
+ "Mustek Systems",
+ "600 USB scanner",
+ },
+ {
+ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_MDC800,
+ 0,
+ "Mustek Systems",
+ "MDC-800 digital camera",
+ },
+ {
+ USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY,
+ 0,
+ "M-Systems",
+ "DiskOnKey",
+ },
+ {
+ USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY2,
+ 0,
+ "M-Systems",
+ "DiskOnKey",
+ },
+ {
+ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200,
+ 0,
+ "National Semiconductor",
+ "BearPaw 1200",
+ },
+ {
+ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400,
+ 0,
+ "National Semiconductor",
+ "BearPaw 2400",
+ },
+ {
+ USB_VENDOR_NEC, USB_PRODUCT_NEC_HUB,
+ 0,
+ "NEC",
+ "hub",
+ },
+ {
+ USB_VENDOR_NEC, USB_PRODUCT_NEC_HUB_B,
+ 0,
+ "NEC",
+ "hub",
+ },
+ {
+ USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND3260,
+ 0,
+ "Neodio",
+ "8-in-1 Multi-format Flash Controller",
+ },
+ {
+ USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND5010,
+ 0,
+ "Neodio",
+ "Multi-format Flash Controller",
+ },
+ {
+ USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_TURBOCONNECT,
+ 0,
+ "NetChip Technology",
+ "Turbo-Connect",
+ },
+ {
+ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101,
+ 0,
+ "BayNETGEAR",
+ "Ethernet adapter",
+ },
+ {
+ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120,
+ 0,
+ "BayNETGEAR",
+ "USB 2.0 Ethernet adapter",
+ },
+ {
+ USB_VENDOR_NIKON, USB_PRODUCT_NIKON_E990,
+ 0,
+ "Nikon",
+ "Digital Camera E990",
+ },
+ {
+ USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C1,
+ 0,
+ "Olympus",
+ "C-1 Digital Camera",
+ },
+ {
+ USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C700,
+ 0,
+ "Olympus",
+ "C-700 Ultra Zoom",
+ },
+ {
+ USB_VENDOR_OMNIVISION, USB_PRODUCT_OMNIVISION_OV511,
+ 0,
+ "OmniVision",
+ "OV511 Camera",
+ },
+ {
+ USB_VENDOR_OMNIVISION, USB_PRODUCT_OMNIVISION_OV511PLUS,
+ 0,
+ "OmniVision",
+ "OV511+ Camera",
+ },
+ {
+ USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_UCF100,
+ 0,
+ "OnSpec Electronic",
+ "FlashLink UCF-100 CompactFlash Reader",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_SERIAL,
+ 0,
+ "Palm Computing",
+ "USB Serial Adaptor",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_M500,
+ 0,
+ "Palm Computing",
+ "Palm m500",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_M505,
+ 0,
+ "Palm Computing",
+ "Palm m505",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_M515,
+ 0,
+ "Palm Computing",
+ "Palm m515",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_I705,
+ 0,
+ "Palm Computing",
+ "Palm i705",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z,
+ 0,
+ "Palm Computing",
+ "Palm Tungsten Z",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_M125,
+ 0,
+ "Palm Computing",
+ "Palm m125",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_M130,
+ 0,
+ "Palm Computing",
+ "Palm m130",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T,
+ 0,
+ "Palm Computing",
+ "Palm Tungsten T",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31,
+ 0,
+ "Palm Computing",
+ "Palm Zire 31",
+ },
+ {
+ USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE,
+ 0,
+ "Palm Computing",
+ "Palm Zire",
+ },
+ {
+ USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLRW32AN,
+ 0,
+ "Panasonic (Matsushita)",
+ "CD-R Drive KXL-RW32AN",
+ },
+ {
+ USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB20AN,
+ 0,
+ "Panasonic (Matsushita)",
+ "CD-R Drive KXL-CB20AN",
+ },
+ {
+ USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB35AN,
+ 0,
+ "Panasonic (Matsushita)",
+ "DVD-ROM & CD-R/RW",
+ },
+ {
+ USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_SDCAAE,
+ 0,
+ "Panasonic (Matsushita)",
+ "MultiMediaCard Adapter",
+ },
+ {
+ USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1,
+ 0,
+ "Peracom Networks",
+ "Serial Converter",
+ },
+ {
+ USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET,
+ 0,
+ "Peracom Networks",
+ "Ethernet adapter",
+ },
+ {
+ USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3,
+ 0,
+ "Peracom Networks",
+ "At Home Ethernet Adapter",
+ },
+ {
+ USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2,
+ 0,
+ "Peracom Networks",
+ "Ethernet adapter",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DSS350,
+ 0,
+ "Philips",
+ "DSS 350 Digital Speaker System",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DSS,
+ 0,
+ "Philips",
+ "DSS XXX Digital Speaker System",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_HUB,
+ 0,
+ "Philips",
+ "hub",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_PCA646VC,
+ 0,
+ "Philips",
+ "PCA646VC PC Camera",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_PCVC680K,
+ 0,
+ "Philips",
+ "PCVC680K Vesta Pro PC Camera",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DSS150,
+ 0,
+ "Philips",
+ "DSS 150 Digital Speaker System",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_UM10016,
+ 0,
+ "Philips",
+ "ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit",
+ },
+ {
+ USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_DIVAUSB,
+ 0,
+ "Philips",
+ "DIVA USB mp3 player",
+ },
+ {
+ USB_VENDOR_PHILIPSSEMI, USB_PRODUCT_PHILIPSSEMI_HUB1122,
+ 0,
+ "Philips Semiconductors",
+ "hub",
+ },
+ {
+ USB_VENDOR_PIENGINEERING, USB_PRODUCT_PIENGINEERING_PS2USB,
+ 0,
+ "P.I. Engineering",
+ "PS2 to Mac USB Adapter",
+ },
+ {
+ USB_VENDOR_PLEXTOR, USB_PRODUCT_PLEXTOR_40_12_40U,
+ 0,
+ "Plextor Corp.",
+ "PlexWriter 40/12/40U",
+ },
+ {
+ USB_VENDOR_PLX, USB_PRODUCT_PLX_TESTBOARD,
+ 0,
+ "PLX",
+ "test board",
+ },
+ {
+ USB_VENDOR_PNY, USB_PRODUCT_PNY_ATTACHE,
+ 0,
+ "PNY",
+ "USB 2.0 Flash Drive",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300,
+ 0,
+ "Primax Electronics",
+ "G2-200 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300,
+ 0,
+ "Primax Electronics",
+ "G2E-300 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300,
+ 0,
+ "Primax Electronics",
+ "G2-300 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002,
+ 0,
+ "Primax Electronics",
+ "G2E-300 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600,
+ 0,
+ "Primax Electronics",
+ "Colorado USB 9600 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U,
+ 0,
+ "Primax Electronics",
+ "Colorado 600u scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200,
+ 0,
+ "Primax Electronics",
+ "Visioneer 6200 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200,
+ 0,
+ "Primax Electronics",
+ "Colorado USB 19200 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U,
+ 0,
+ "Primax Electronics",
+ "Colorado 1200u scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600,
+ 0,
+ "Primax Electronics",
+ "G2-600 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I,
+ 0,
+ "Primax Electronics",
+ "ReadyScan 636i",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600,
+ 0,
+ "Primax Electronics",
+ "G2-600 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600,
+ 0,
+ "Primax Electronics",
+ "G2E-600 scanner",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_COMFORT,
+ 0,
+ "Primax Electronics",
+ "Comfort",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_MOUSEINABOX,
+ 0,
+ "Primax Electronics",
+ "Mouse-in-a-Box",
+ },
+ {
+ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_PCGAUMS1,
+ 0,
+ "Primax Electronics",
+ "Sony PCGA-UMS1",
+ },
+ {
+ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2301,
+ 0,
+ "Prolific Technology",
+ "PL2301 Host-Host interface",
+ },
+ {
+ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2302,
+ 0,
+ "Prolific Technology",
+ "PL2302 Host-Host interface",
+ },
+ {
+ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2,
+ 0,
+ "Prolific Technology",
+ "PL2303 Serial adapter (IODATA USB-RSAQ2)",
+ },
+ {
+ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303,
+ 0,
+ "Prolific Technology",
+ "PL2303 Serial adapter (ATEN/IOGEAR UC232A)",
+ },
+ {
+ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2305,
+ 0,
+ "Prolific Technology",
+ "Parallel printer adapter",
+ },
+ {
+ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_ATAPI4,
+ 0,
+ "Prolific Technology",
+ "ATAPI-4 Bridge Controller",
+ },
+ {
+ USB_VENDOR_PUTERCOM, USB_PRODUCT_PUTERCOM_UPA100,
+ 0,
+ "Putercom",
+ "USB-1284 BRIDGE",
+ },
+ {
+ USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N,
+ 0,
+ "Qtronix",
+ "Scorpion-980N keyboard",
+ },
+ {
+ USB_VENDOR_QUICKSHOT, USB_PRODUCT_QUICKSHOT_STRIKEPAD,
+ 0,
+ "Quickshot",
+ "USB StrikePad",
+ },
+ {
+ USB_VENDOR_RAINBOW, USB_PRODUCT_RAINBOW_IKEY2000,
+ 0,
+ "Rainbow Technologies",
+ "i-Key 2000",
+ },
+ {
+ USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100,
+ 0,
+ "RealTek",
+ "USBKR100 USB Ethernet (GREEN HOUSE)",
+ },
+ {
+ USB_VENDOR_ROLAND, USB_PRODUCT_ROLAND_UM1,
+ 0,
+ "Roland",
+ "UM-1 MIDI I/F",
+ },
+ {
+ USB_VENDOR_ROLAND, USB_PRODUCT_ROLAND_UM880N,
+ 0,
+ "Roland",
+ "EDIROL UM-880 MIDI I/F (native)",
+ },
+ {
+ USB_VENDOR_ROLAND, USB_PRODUCT_ROLAND_UM880G,
+ 0,
+ "Roland",
+ "EDIROL UM-880 MIDI I/F (generic)",
+ },
+ {
+ USB_VENDOR_ROCKFIRE, USB_PRODUCT_ROCKFIRE_GAMEPAD,
+ 0,
+ "Rockfire",
+ "gamepad 203USB",
+ },
+ {
+ USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60,
+ 0,
+ "RATOC Systems, Inc.",
+ "USB serial adapter REX-USB60",
+ },
+ {
+ USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_ML6060,
+ 0,
+ "Samsung Electronics",
+ "ML-6060 laser printer",
+ },
+ {
+ USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR05A,
+ 0,
+ "SanDisk Corp",
+ "ImageMate SDDR-05a",
+ },
+ {
+ USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR05,
+ 0,
+ "SanDisk Corp",
+ "ImageMate SDDR-05",
+ },
+ {
+ USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR31,
+ 0,
+ "SanDisk Corp",
+ "ImageMate SDDR-31",
+ },
+ {
+ USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR12,
+ 0,
+ "SanDisk Corp",
+ "ImageMate SDDR-12",
+ },
+ {
+ USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR09,
+ 0,
+ "SanDisk Corp",
+ "ImageMate SDDR-09",
+ },
+ {
+ USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR75,
+ 0,
+ "SanDisk Corp",
+ "ImageMate SDDR-75",
+ },
+ {
+ USB_VENDOR_SANYO, USB_PRODUCT_SANYO_SCP4900,
+ 0,
+ "Sanyo Electric",
+ "Sanyo SCP-4900 USB Phone",
+ },
+ {
+ USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_SL11R,
+ 0,
+ "ScanLogic",
+ "SL11R IDE Adapter",
+ },
+ {
+ USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX,
+ 0,
+ "ScanLogic",
+ "Phantom 336CX - C3 scanner",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSB,
+ 0,
+ "Shuttle Technology",
+ "E-USB Bridge",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSCSI,
+ 0,
+ "Shuttle Technology",
+ "eUSCSI Bridge",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_SDDR09,
+ 0,
+ "Shuttle Technology",
+ "ImageMate SDDR09",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_ZIOMMC,
+ 0,
+ "Shuttle Technology",
+ "eUSB MultiMediaCard Adapter",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_HIFD,
+ 0,
+ "Shuttle Technology",
+ "Sony Hifd",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBATAPI,
+ 0,
+ "Shuttle Technology",
+ "eUSB ATA/ATAPI Adapter",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_CF,
+ 0,
+ "Shuttle Technology",
+ "eUSB CompactFlash Adapter",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSCSI_B,
+ 0,
+ "Shuttle Technology",
+ "eUSCSI Bridge",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSCSI_C,
+ 0,
+ "Shuttle Technology",
+ "eUSCSI Bridge",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_CDRW,
+ 0,
+ "Shuttle Technology",
+ "CD-RW Device",
+ },
+ {
+ USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBORCA,
+ 0,
+ "Shuttle Technology",
+ "eUSB ORCA Quad Reader",
+ },
+ {
+ USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM,
+ 0,
+ "Siemens",
+ "SpeedStream USB",
+ },
+ {
+ USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_I_BEAD100,
+ 0,
+ "Sigmatel",
+ "i-Bead 100 MP3 Player",
+ },
+ {
+ USB_VENDOR_SIIG, USB_PRODUCT_SIIG_DIGIFILMREADER,
+ 0,
+ "SIIG",
+ "DigiFilm-Combo Reader",
+ },
+ {
+ USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPH_NF,
+ 0,
+ "Silicon Portals",
+ "YAP Phone (no firmware)",
+ },
+ {
+ USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE,
+ 0,
+ "Silicon Portals",
+ "YAP Phone",
+ },
+ {
+ USB_VENDOR_SIRIUS, USB_PRODUCT_SIRIUS_ROADSTER,
+ 0,
+ "Sirius Technologies",
+ "NetComm Roadster II 56 USB",
+ },
+ {
+ USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK,
+ 0,
+ "SmartBridges",
+ "SmartLink USB ethernet adapter",
+ },
+ {
+ USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC,
+ 0,
+ "SmartBridges",
+ "smartNIC 2 PnP Adapter",
+ },
+ {
+ USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB,
+ 0,
+ "Standard Microsystems",
+ "10Mbps ethernet adapter",
+ },
+ {
+ USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB,
+ 0,
+ "Standard Microsystems",
+ "10/100 ethernet adapter",
+ },
+ {
+ USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB,
+ 0,
+ "Standard Microsystems",
+ "EZ Connect USB Ethernet Adapter",
+ },
+ {
+ USB_VENDOR_SMC2, USB_PRODUCT_SMC2_2020HUB,
+ 0,
+ "Standard Microsystems",
+ "USB Hub",
+ },
+ {
+ USB_VENDOR_SMC3, USB_PRODUCT_SMC3_2662WUSB,
+ 0,
+ "Standard Microsystems",
+ "2662W-AR Wireless Adapter",
+ },
+ {
+ USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100,
+ 0,
+ "SOHOware",
+ "10/100 USB Ethernet",
+ },
+ {
+ USB_VENDOR_SOLIDYEAR, USB_PRODUCT_SOLIDYEAR_KEYBOARD,
+ 0,
+ "Solid Year",
+ "Solid Year USB keyboard",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC,
+ 0,
+ "Sony",
+ "DSC cameras",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_MSACUS1,
+ 0,
+ "Sony",
+ "Memorystick MSAC-US1",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_MSC,
+ 0,
+ "Sony",
+ "MSC memory stick slot",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35,
+ 0,
+ "Sony",
+ "Sony Clie v3.5",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40,
+ 0,
+ "Sony",
+ "Sony Clie v4.0",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40_MS,
+ 0,
+ "Sony",
+ "Sony Clie v4.0 Memory Stick slot",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360,
+ 0,
+ "Sony",
+ "Sony Clie s360",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41_MS,
+ 0,
+ "Sony",
+ "Sony Clie v4.1 Memory Stick slot",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41,
+ 0,
+ "Sony",
+ "Sony Clie v4.1",
+ },
+ {
+ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60,
+ 0,
+ "Sony",
+ "Sony Clie nx60",
+ },
+ {
+ USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8,
+ 0,
+ "SOURCENEXT",
+ "KeikaiDenwa 8",
+ },
+ {
+ USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG,
+ 0,
+ "SOURCENEXT",
+ "KeikaiDenwa 8 with charger",
+ },
+ {
+ USB_VENDOR_STMICRO, USB_PRODUCT_STMICRO_COMMUNICATOR,
+ 0,
+ "STMicroelectronics",
+ "USB Communicator",
+ },
+ {
+ USB_VENDOR_STSN, USB_PRODUCT_STSN_STSN0001,
+ 0,
+ "STSN",
+ "Internet Access Device",
+ },
+ {
+ USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L,
+ 0,
+ "SUN Corporation",
+ "SUNTAC U-Cable type D2",
+ },
+ {
+ USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1,
+ 0,
+ "SUN Corporation",
+ "SUNTAC U-Cable type P1",
+ },
+ {
+ USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U,
+ 0,
+ "SUN Corporation",
+ "SUNTAC Slipper U",
+ },
+ {
+ USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U,
+ 0,
+ "SUN Corporation",
+ "SUNTAC Ir-Trinity",
+ },
+ {
+ USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS64LX,
+ 0,
+ "SUN Corporation",
+ "SUNTAC U-Cable type A3",
+ },
+ {
+ USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4,
+ 0,
+ "SUN Corporation",
+ "SUNTAC U-Cable type A4",
+ },
+ {
+ USB_VENDOR_SUN, USB_PRODUCT_SUN_KEYBOARD,
+ 0,
+ "Sun Microsystems",
+ "Type 6 USB keyboard",
+ },
+ {
+ USB_VENDOR_SUN, USB_PRODUCT_SUN_MOUSE,
+ 0,
+ "Sun Microsystems",
+ "Type 6 USB mouse",
+ },
+ {
+ USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_SUPRAEXPRESS56K,
+ 0,
+ "Diamond (Supra)",
+ "Supra Express 56K modem",
+ },
+ {
+ USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_SUPRA2890,
+ 0,
+ "Diamond (Supra)",
+ "SupraMax 2890 56K Modem",
+ },
+ {
+ USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO600USB,
+ 0,
+ "Diamond (Supra)",
+ "Rio 600 USB",
+ },
+ {
+ USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO800USB,
+ 0,
+ "Diamond (Supra)",
+ "Rio 800 USB",
+ },
+ {
+ USB_VENDOR_TAUGA, USB_PRODUCT_TAUGA_CAMERAMATE,
+ 0,
+ "Taugagreining HF",
+ "CameraMate (DPCM_USB)",
+ },
+ {
+ USB_VENDOR_TDK, USB_PRODUCT_TDK_UPA9664,
+ 0,
+ "TDK",
+ "USB-PDC Adapter UPA9664",
+ },
+ {
+ USB_VENDOR_TDK, USB_PRODUCT_TDK_UCA1464,
+ 0,
+ "TDK",
+ "USB-cdmaOne Adapter UCA1464",
+ },
+ {
+ USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400,
+ 0,
+ "TDK",
+ "USB-PHS Adapter UHA6400",
+ },
+ {
+ USB_VENDOR_TDK, USB_PRODUCT_TDK_UPA6400,
+ 0,
+ "TDK",
+ "USB-PHS Adapter UPA6400",
+ },
+ {
+ USB_VENDOR_TDK, USB_PRODUCT_TDK_BT_DONGLE,
+ 0,
+ "TDK",
+ "Bluetooth USB dongle",
+ },
+ {
+ USB_VENDOR_TEAC, USB_PRODUCT_TEAC_FD05PUB,
+ 0,
+ "TEAC",
+ "FD-05PUB floppy",
+ },
+ {
+ USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1,
+ 0,
+ "Telex Communications",
+ "Enhanced USB Microphone",
+ },
+ {
+ USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41,
+ 0,
+ "Texas Instruments",
+ "UT-USB41 hub",
+ },
+ {
+ USB_VENDOR_TI, USB_PRODUCT_TI_TUSB2046,
+ 0,
+ "Texas Instruments",
+ "TUSB2046 hub",
+ },
+ {
+ USB_VENDOR_THRUST, USB_PRODUCT_THRUST_FUSION_PAD,
+ 0,
+ "Thrustmaster",
+ "Fusion Digital Gamepad",
+ },
+ {
+ USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_POCKETPC_E740,
+ 0,
+ "Toshiba Corporation",
+ "PocketPC e740",
+ },
+ {
+ USB_VENDOR_TREK, USB_PRODUCT_TREK_THUMBDRIVE,
+ 0,
+ "Trek Technology",
+ "ThumbDrive",
+ },
+ {
+ USB_VENDOR_TREK, USB_PRODUCT_TREK_THUMBDRIVE_8MB,
+ 0,
+ "Trek Technology",
+ "ThumbDrive_8MB",
+ },
+ {
+ USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS,
+ 0,
+ "Ultima",
+ "1200 UB Plus scanner",
+ },
+ {
+ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U,
+ 0,
+ "UMAX Data Systems",
+ "Astra 1236U Scanner",
+ },
+ {
+ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U,
+ 0,
+ "UMAX Data Systems",
+ "Astra 1220U Scanner",
+ },
+ {
+ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U,
+ 0,
+ "UMAX Data Systems",
+ "Astra 2000U Scanner",
+ },
+ {
+ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U,
+ 0,
+ "UMAX Data Systems",
+ "Astra 2100U Scanner",
+ },
+ {
+ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U,
+ 0,
+ "UMAX Data Systems",
+ "Astra 2200U Scanner",
+ },
+ {
+ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400,
+ 0,
+ "UMAX Data Systems",
+ "Astra 3400 Scanner",
+ },
+ {
+ USB_VENDOR_UNIACCESS, USB_PRODUCT_UNIACCESS_PANACHE,
+ 0,
+ "Universal Access",
+ "Panache Surf USB ISDN Adapter",
+ },
+ {
+ USB_VENDOR_VIDZMEDIA, USB_PRODUCT_VIDZMEDIA_MONSTERTV,
+ 0,
+ "VidzMedia Pte Ltd",
+ "MonsterTV P2H",
+ },
+ {
+ USB_VENDOR_VISION, USB_PRODUCT_VISION_VC6452V002,
+ 0,
+ "VLSI Vision",
+ "CPiA Camera",
+ },
+ {
+ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600,
+ 0,
+ "Visioneer",
+ "OneTouch 7600",
+ },
+ {
+ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300,
+ 0,
+ "Visioneer",
+ "OneTouch 5300",
+ },
+ {
+ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000,
+ 0,
+ "Visioneer",
+ "Scanport 3000",
+ },
+ {
+ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100,
+ 0,
+ "Visioneer",
+ "OneTouch 6100",
+ },
+ {
+ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200,
+ 0,
+ "Visioneer",
+ "OneTouch 6200",
+ },
+ {
+ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100,
+ 0,
+ "Visioneer",
+ "OneTouch 8100",
+ },
+ {
+ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600,
+ 0,
+ "Visioneer",
+ "OneTouch 8600",
+ },
+ {
+ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_CT0405U,
+ 0,
+ "WACOM",
+ "CT-0405-U Tablet",
+ },
+ {
+ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_GRAPHIRE,
+ 0,
+ "WACOM",
+ "Graphire",
+ },
+ {
+ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_INTUOSA5,
+ 0,
+ "WACOM",
+ "Intuos A5",
+ },
+ {
+ USB_VENDOR_WACOM, USB_PRODUCT_WACOM_GD0912U,
+ 0,
+ "WACOM",
+ "Intuos 9x12 Graphics Tablet",
+ },
+ {
+ USB_VENDOR_XIRLINK, USB_PRODUCT_XIRLINK_PCCAM,
+ 0,
+ "Xirlink",
+ "IBM PC Camera",
+ },
+ {
+ USB_VENDOR_YEDATA, USB_PRODUCT_YEDATA_FLASHBUSTERU,
+ 0,
+ "Y-E Data",
+ "Flashbuster-U",
+ },
+ {
+ USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_UX256,
+ 0,
+ "YAMAHA",
+ "UX256 MIDI I/F",
+ },
+ {
+ USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_UX96,
+ 0,
+ "YAMAHA",
+ "UX96 MIDI I/F",
+ },
+ {
+ USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTA54I,
+ 0,
+ "YAMAHA",
+ "NetVolante RTA54i Broadband&ISDN Router",
+ },
+ {
+ USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTA55I,
+ 0,
+ "YAMAHA",
+ "NetVolante RTA55i Broadband VoIP Router",
+ },
+ {
+ USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTW65B,
+ 0,
+ "YAMAHA",
+ "NetVolante RTW65b Broadband Wireless Router",
+ },
+ {
+ USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_RTW65I,
+ 0,
+ "YAMAHA",
+ "NetVolante RTW65i Broadband&ISDN Wireless Router",
+ },
+ {
+ USB_VENDOR_YANO, USB_PRODUCT_YANO_U640MO,
+ 0,
+ "Yano",
+ "U640MO-03",
+ },
+ {
+ USB_VENDOR_ZOOM, USB_PRODUCT_ZOOM_2986L,
+ 0,
+ "Zoom Telephonics",
+ "2986L Fax modem",
+ },
+ {
+ USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_OMNI56K,
+ 0,
+ "ZyXEL Communication",
+ "Omni 56K Plus",
+ },
+ {
+ USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_980N,
+ 0,
+ "ZyXEL Communication",
+ "Scorpion-980N keyboard",
+ },
+ {
+ USB_VENDOR_AOX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AOX",
+ NULL,
+ },
+ {
+ USB_VENDOR_ATMEL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Atmel",
+ NULL,
+ },
+ {
+ USB_VENDOR_MITSUMI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Mitsumi",
+ NULL,
+ },
+ {
+ USB_VENDOR_HP, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Hewlett Packard",
+ NULL,
+ },
+ {
+ USB_VENDOR_ADAPTEC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Adaptec",
+ NULL,
+ },
+ {
+ USB_VENDOR_NATIONAL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "National Semiconductor",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACERLABS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Acer Labs",
+ NULL,
+ },
+ {
+ USB_VENDOR_FTDI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Future Technology Devices",
+ NULL,
+ },
+ {
+ USB_VENDOR_NEC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "NEC",
+ NULL,
+ },
+ {
+ USB_VENDOR_KODAK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Eastman Kodak",
+ NULL,
+ },
+ {
+ USB_VENDOR_MELCO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Melco",
+ NULL,
+ },
+ {
+ USB_VENDOR_CREATIVE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Creative",
+ NULL,
+ },
+ {
+ USB_VENDOR_ADI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ADI Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_CATC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Computer Access Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_SMC2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Standard Microsystems",
+ NULL,
+ },
+ {
+ USB_VENDOR_GRAVIS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Advanced Gravis Computer Tech.",
+ NULL,
+ },
+ {
+ USB_VENDOR_SUN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Sun Microsystems",
+ NULL,
+ },
+ {
+ USB_VENDOR_TAUGA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Taugagreining HF",
+ NULL,
+ },
+ {
+ USB_VENDOR_AMD, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Advanced Micro Devices",
+ NULL,
+ },
+ {
+ USB_VENDOR_LEXMARK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Lexmark International",
+ NULL,
+ },
+ {
+ USB_VENDOR_NANAO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "NANAO",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALPS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Alps Electric",
+ NULL,
+ },
+ {
+ USB_VENDOR_THRUST, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Thrustmaster",
+ NULL,
+ },
+ {
+ USB_VENDOR_TI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Texas Instruments",
+ NULL,
+ },
+ {
+ USB_VENDOR_ANALOGDEVICES, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Analog Devices",
+ NULL,
+ },
+ {
+ USB_VENDOR_KYE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "KYE Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_DIAMOND2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Diamond (Supra)",
+ NULL,
+ },
+ {
+ USB_VENDOR_MICROSOFT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Microsoft",
+ NULL,
+ },
+ {
+ USB_VENDOR_PRIMAX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Primax Electronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_AMP, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AMP",
+ NULL,
+ },
+ {
+ USB_VENDOR_CHERRY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Cherry Mikroschalter",
+ NULL,
+ },
+ {
+ USB_VENDOR_MEGATRENDS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "American Megatrends",
+ NULL,
+ },
+ {
+ USB_VENDOR_LOGITECH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Logitech",
+ NULL,
+ },
+ {
+ USB_VENDOR_BTC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Behavior Tech. Computer",
+ NULL,
+ },
+ {
+ USB_VENDOR_PHILIPS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Philips",
+ NULL,
+ },
+ {
+ USB_VENDOR_SANYO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Sanyo Electric",
+ NULL,
+ },
+ {
+ USB_VENDOR_CONNECTIX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Connectix",
+ NULL,
+ },
+ {
+ USB_VENDOR_KENSINGTON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Kensington",
+ NULL,
+ },
+ {
+ USB_VENDOR_LUCENT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Lucent",
+ NULL,
+ },
+ {
+ USB_VENDOR_KYOCERA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Kyocera Corp.",
+ NULL,
+ },
+ {
+ USB_VENDOR_STMICRO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "STMicroelectronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_YAMAHA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "YAMAHA",
+ NULL,
+ },
+ {
+ USB_VENDOR_COMPAQ, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Compaq Computers",
+ NULL,
+ },
+ {
+ USB_VENDOR_HITACHI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Hitachi, Ltd.",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACERP, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Acer Peripherals",
+ NULL,
+ },
+ {
+ USB_VENDOR_VISIONEER, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Visioneer",
+ NULL,
+ },
+ {
+ USB_VENDOR_CANON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Canon",
+ NULL,
+ },
+ {
+ USB_VENDOR_NIKON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Nikon",
+ NULL,
+ },
+ {
+ USB_VENDOR_IBM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "IBM Corporation",
+ NULL,
+ },
+ {
+ USB_VENDOR_CYPRESS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Cypress Semiconductor",
+ NULL,
+ },
+ {
+ USB_VENDOR_EPSON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Seiko Epson",
+ NULL,
+ },
+ {
+ USB_VENDOR_RAINBOW, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Rainbow Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_IODATA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "I/O Data",
+ NULL,
+ },
+ {
+ USB_VENDOR_TDK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "TDK",
+ NULL,
+ },
+ {
+ USB_VENDOR_3COMUSR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "U.S. Robotics",
+ NULL,
+ },
+ {
+ USB_VENDOR_METHODE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Methode Electronics Far East",
+ NULL,
+ },
+ {
+ USB_VENDOR_MAXISWITCH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Maxi Switch",
+ NULL,
+ },
+ {
+ USB_VENDOR_LOCKHEEDMER, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Lockheed Martin Energy Research",
+ NULL,
+ },
+ {
+ USB_VENDOR_FUJITSU, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Fujitsu",
+ NULL,
+ },
+ {
+ USB_VENDOR_TOSHIBAAM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Toshiba America Electronic Components",
+ NULL,
+ },
+ {
+ USB_VENDOR_MICROMACRO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Micro Macro Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_KONICA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Konica",
+ NULL,
+ },
+ {
+ USB_VENDOR_LITEON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Lite-On Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_FUJIPHOTO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Fuji Photo Film",
+ NULL,
+ },
+ {
+ USB_VENDOR_PHILIPSSEMI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Philips Semiconductors",
+ NULL,
+ },
+ {
+ USB_VENDOR_TATUNG, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Tatung Co. Of America",
+ NULL,
+ },
+ {
+ USB_VENDOR_SCANLOGIC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ScanLogic",
+ NULL,
+ },
+ {
+ USB_VENDOR_MYSON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Myson Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_DIGI2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Digi International",
+ NULL,
+ },
+ {
+ USB_VENDOR_ITTCANON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ITT Canon",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALTEC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Altec Lansing Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_PANASONIC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Panasonic (Matsushita)",
+ NULL,
+ },
+ {
+ USB_VENDOR_IIYAMA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Iiyama",
+ NULL,
+ },
+ {
+ USB_VENDOR_SHUTTLE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Shuttle Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_SAMSUNG, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Samsung Electronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_ANNABOOKS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Annabooks",
+ NULL,
+ },
+ {
+ USB_VENDOR_JVC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "JVC",
+ NULL,
+ },
+ {
+ USB_VENDOR_CHICONY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Chicony Electronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_BROTHER, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Brother Industries",
+ NULL,
+ },
+ {
+ USB_VENDOR_DALLAS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Dallas Semiconductor",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACER, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Acer",
+ NULL,
+ },
+ {
+ USB_VENDOR_3COM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "3Com",
+ NULL,
+ },
+ {
+ USB_VENDOR_AZTECH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Aztech Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_BELKIN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Belkin Components",
+ NULL,
+ },
+ {
+ USB_VENDOR_KAWATSU, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Kawatsu Semiconductor",
+ NULL,
+ },
+ {
+ USB_VENDOR_APC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "American Power Conversion",
+ NULL,
+ },
+ {
+ USB_VENDOR_CONNECTEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Advanced Connectek USA",
+ NULL,
+ },
+ {
+ USB_VENDOR_NETCHIP, 0,
+ USB_KNOWNDEV_NOPROD,
+ "NetChip Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALTRA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ALTRA",
+ NULL,
+ },
+ {
+ USB_VENDOR_ATI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ATI Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_AKS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Aladdin Knowledge Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_UNIACCESS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Universal Access",
+ NULL,
+ },
+ {
+ USB_VENDOR_XIRLINK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Xirlink",
+ NULL,
+ },
+ {
+ USB_VENDOR_ANCHOR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Anchor Chips",
+ NULL,
+ },
+ {
+ USB_VENDOR_SONY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Sony",
+ NULL,
+ },
+ {
+ USB_VENDOR_VISION, 0,
+ USB_KNOWNDEV_NOPROD,
+ "VLSI Vision",
+ NULL,
+ },
+ {
+ USB_VENDOR_ASAHIKASEI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Asahi Kasei Microsystems",
+ NULL,
+ },
+ {
+ USB_VENDOR_ATEN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ATEN International",
+ NULL,
+ },
+ {
+ USB_VENDOR_MUSTEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Mustek Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_TELEX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Telex Communications",
+ NULL,
+ },
+ {
+ USB_VENDOR_PERACOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Peracom Networks",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALCOR2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Alcor Micro",
+ NULL,
+ },
+ {
+ USB_VENDOR_WACOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "WACOM",
+ NULL,
+ },
+ {
+ USB_VENDOR_ETEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "e-TEK Labs",
+ NULL,
+ },
+ {
+ USB_VENDOR_EIZO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "EIZO",
+ NULL,
+ },
+ {
+ USB_VENDOR_ELECOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Elecom",
+ NULL,
+ },
+ {
+ USB_VENDOR_HAUPPAUGE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Hauppauge Computer Works",
+ NULL,
+ },
+ {
+ USB_VENDOR_BAFO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "BAFO/Quality Computer Accessories",
+ NULL,
+ },
+ {
+ USB_VENDOR_YEDATA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Y-E Data",
+ NULL,
+ },
+ {
+ USB_VENDOR_AVM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AVM GmbH",
+ NULL,
+ },
+ {
+ USB_VENDOR_QUICKSHOT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Quickshot",
+ NULL,
+ },
+ {
+ USB_VENDOR_ROLAND, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Roland",
+ NULL,
+ },
+ {
+ USB_VENDOR_ROCKFIRE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Rockfire",
+ NULL,
+ },
+ {
+ USB_VENDOR_RATOC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "RATOC Systems, Inc.",
+ NULL,
+ },
+ {
+ USB_VENDOR_ZYXEL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ZyXEL Communication",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALCOR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Alcor Micro",
+ NULL,
+ },
+ {
+ USB_VENDOR_IOMEGA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Iomega",
+ NULL,
+ },
+ {
+ USB_VENDOR_ATREND, 0,
+ USB_KNOWNDEV_NOPROD,
+ "A-Trend Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_AID, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Advanced Input Devices",
+ NULL,
+ },
+ {
+ USB_VENDOR_LACIE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "LaCie",
+ NULL,
+ },
+ {
+ USB_VENDOR_OMNIVISION, 0,
+ USB_KNOWNDEV_NOPROD,
+ "OmniVision",
+ NULL,
+ },
+ {
+ USB_VENDOR_INSYSTEM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "In-System Design",
+ NULL,
+ },
+ {
+ USB_VENDOR_APPLE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Apple Computer",
+ NULL,
+ },
+ {
+ USB_VENDOR_DIGI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Digi International",
+ NULL,
+ },
+ {
+ USB_VENDOR_QTRONIX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Qtronix",
+ NULL,
+ },
+ {
+ USB_VENDOR_ELSA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ELSA",
+ NULL,
+ },
+ {
+ USB_VENDOR_BRAINBOXES, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Brainboxes Limited",
+ NULL,
+ },
+ {
+ USB_VENDOR_ULTIMA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Ultima",
+ NULL,
+ },
+ {
+ USB_VENDOR_AXIOHM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Axiohm Transaction Solutions",
+ NULL,
+ },
+ {
+ USB_VENDOR_MICROTEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Microtek",
+ NULL,
+ },
+ {
+ USB_VENDOR_SUNTAC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "SUN Corporation",
+ NULL,
+ },
+ {
+ USB_VENDOR_LEXAR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Lexar Media",
+ NULL,
+ },
+ {
+ USB_VENDOR_SYMBOL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Symbol Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_GENESYS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Genesys Logic",
+ NULL,
+ },
+ {
+ USB_VENDOR_FUJI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Fuji Electric",
+ NULL,
+ },
+ {
+ USB_VENDOR_KEITHLEY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Keithley Instruments",
+ NULL,
+ },
+ {
+ USB_VENDOR_EIZONANAO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "EIZO Nanao",
+ NULL,
+ },
+ {
+ USB_VENDOR_KLSI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Kawasaki LSI",
+ NULL,
+ },
+ {
+ USB_VENDOR_FFC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "FFC",
+ NULL,
+ },
+ {
+ USB_VENDOR_ANKO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Anko Electronic",
+ NULL,
+ },
+ {
+ USB_VENDOR_PIENGINEERING, 0,
+ USB_KNOWNDEV_NOPROD,
+ "P.I. Engineering",
+ NULL,
+ },
+ {
+ USB_VENDOR_AOC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AOC International",
+ NULL,
+ },
+ {
+ USB_VENDOR_CHIC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Chic Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_BARCO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Barco Display Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_BRIDGE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Bridge Information",
+ NULL,
+ },
+ {
+ USB_VENDOR_SOLIDYEAR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Solid Year",
+ NULL,
+ },
+ {
+ USB_VENDOR_BIORAD, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Bio-Rad Laboratories",
+ NULL,
+ },
+ {
+ USB_VENDOR_MACALLY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Macally",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACTLABS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Act Labs",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALARIS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Alaris",
+ NULL,
+ },
+ {
+ USB_VENDOR_APEX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Apex",
+ NULL,
+ },
+ {
+ USB_VENDOR_AVISION, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Avision",
+ NULL,
+ },
+ {
+ USB_VENDOR_TEAC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "TEAC",
+ NULL,
+ },
+ {
+ USB_VENDOR_LINKSYS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Linksys",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACERSA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Acer Semiconductor America",
+ NULL,
+ },
+ {
+ USB_VENDOR_SIGMATEL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Sigmatel",
+ NULL,
+ },
+ {
+ USB_VENDOR_AIWA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Aiwa",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACARD, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ACARD Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_PROLIFIC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Prolific Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_SIEMENS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Siemens",
+ NULL,
+ },
+ {
+ USB_VENDOR_ADVANCELOGIC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Avance Logic",
+ NULL,
+ },
+ {
+ USB_VENDOR_HAGIWARA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Hagiwara Sys-Com",
+ NULL,
+ },
+ {
+ USB_VENDOR_MINOLTA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Minolta",
+ NULL,
+ },
+ {
+ USB_VENDOR_CTX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Chuntex",
+ NULL,
+ },
+ {
+ USB_VENDOR_ASKEY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Askey Computer",
+ NULL,
+ },
+ {
+ USB_VENDOR_SAITEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Saitek",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALCATELT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Alcatel Telecom",
+ NULL,
+ },
+ {
+ USB_VENDOR_AGFA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AGFA-Gevaert",
+ NULL,
+ },
+ {
+ USB_VENDOR_ASIAMD, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Asia Microelectronic Development",
+ NULL,
+ },
+ {
+ USB_VENDOR_BIZLINK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Bizlink International",
+ NULL,
+ },
+ {
+ USB_VENDOR_KEYSPAN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Keyspan",
+ NULL,
+ },
+ {
+ USB_VENDOR_AASHIMA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Aashima Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_MULTITECH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "MultiTech",
+ NULL,
+ },
+ {
+ USB_VENDOR_ADS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ADS Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALCATELM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Alcatel Microelectronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_SIRIUS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Sirius Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_BOSTON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Boston Acoustics",
+ NULL,
+ },
+ {
+ USB_VENDOR_SMC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Standard Microsystems",
+ NULL,
+ },
+ {
+ USB_VENDOR_PUTERCOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Putercom",
+ NULL,
+ },
+ {
+ USB_VENDOR_MCT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "MCT",
+ NULL,
+ },
+ {
+ USB_VENDOR_DIGITALSTREAM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Digital Stream",
+ NULL,
+ },
+ {
+ USB_VENDOR_AUREAL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Aureal Semiconductor",
+ NULL,
+ },
+ {
+ USB_VENDOR_MIDIMAN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Midiman",
+ NULL,
+ },
+ {
+ USB_VENDOR_LINKSYS2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Linksys",
+ NULL,
+ },
+ {
+ USB_VENDOR_GRIFFIN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Griffin Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_SANDISK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "SanDisk Corp",
+ NULL,
+ },
+ {
+ USB_VENDOR_LOGITEC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Logitec Corp",
+ NULL,
+ },
+ {
+ USB_VENDOR_BRIMAX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Brimax",
+ NULL,
+ },
+ {
+ USB_VENDOR_AXIS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Axis Communications",
+ NULL,
+ },
+ {
+ USB_VENDOR_ABL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ABL Electronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALFADATA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Alfadata Computer",
+ NULL,
+ },
+ {
+ USB_VENDOR_NATIONALTECH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "National Technical Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_ONNTO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Onnto",
+ NULL,
+ },
+ {
+ USB_VENDOR_BE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Be",
+ NULL,
+ },
+ {
+ USB_VENDOR_ADMTEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ADMtek",
+ NULL,
+ },
+ {
+ USB_VENDOR_COREGA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Corega",
+ NULL,
+ },
+ {
+ USB_VENDOR_FREECOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Freecom",
+ NULL,
+ },
+ {
+ USB_VENDOR_MICROTECH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Microtech",
+ NULL,
+ },
+ {
+ USB_VENDOR_GENERALINSTMNTS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "General Instruments (Motorola)",
+ NULL,
+ },
+ {
+ USB_VENDOR_OLYMPUS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Olympus",
+ NULL,
+ },
+ {
+ USB_VENDOR_ONSPEC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "OnSpec Electronic",
+ NULL,
+ },
+ {
+ USB_VENDOR_ABOCOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AboCom Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_KEISOKUGIKEN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Keisokugiken",
+ NULL,
+ },
+ {
+ USB_VENDOR_APG, 0,
+ USB_KNOWNDEV_NOPROD,
+ "APG Cash Drawer",
+ NULL,
+ },
+ {
+ USB_VENDOR_BUG, 0,
+ USB_KNOWNDEV_NOPROD,
+ "B.U.G.",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALLIEDTELESYN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Allied Telesyn International",
+ NULL,
+ },
+ {
+ USB_VENDOR_AVERMEDIA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AVerMedia Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_SIIG, 0,
+ USB_KNOWNDEV_NOPROD,
+ "SIIG",
+ NULL,
+ },
+ {
+ USB_VENDOR_CASIO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "CASIO",
+ NULL,
+ },
+ {
+ USB_VENDOR_APTIO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Aptio Products",
+ NULL,
+ },
+ {
+ USB_VENDOR_ARASAN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Arasan Chip Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALLIEDCABLE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Allied Cable",
+ NULL,
+ },
+ {
+ USB_VENDOR_STSN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "STSN",
+ NULL,
+ },
+ {
+ USB_VENDOR_ZOOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Zoom Telephonics",
+ NULL,
+ },
+ {
+ USB_VENDOR_BROADLOGIC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "BroadLogic",
+ NULL,
+ },
+ {
+ USB_VENDOR_HANDSPRING, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Handspring",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACTIONSTAR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Action Star Enterprise",
+ NULL,
+ },
+ {
+ USB_VENDOR_PALM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Palm Computing",
+ NULL,
+ },
+ {
+ USB_VENDOR_SOURCENEXT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "SOURCENEXT",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACCTON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Accton Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_DIAMOND, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Diamond",
+ NULL,
+ },
+ {
+ USB_VENDOR_NETGEAR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "BayNETGEAR",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACTIVEWIRE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ActiveWire",
+ NULL,
+ },
+ {
+ USB_VENDOR_PORTGEAR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "PortGear",
+ NULL,
+ },
+ {
+ USB_VENDOR_METRICOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Metricom",
+ NULL,
+ },
+ {
+ USB_VENDOR_ADESSOKBTEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ADESSO/Kbtek America",
+ NULL,
+ },
+ {
+ USB_VENDOR_JATON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Jaton",
+ NULL,
+ },
+ {
+ USB_VENDOR_APT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "APT Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_BOCARESEARCH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Boca Research",
+ NULL,
+ },
+ {
+ USB_VENDOR_ANDREA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Andrea Electronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_BURRBROWN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Burr-Brown Japan",
+ NULL,
+ },
+ {
+ USB_VENDOR_2WIRE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "2Wire",
+ NULL,
+ },
+ {
+ USB_VENDOR_AIPTEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AIPTEK International",
+ NULL,
+ },
+ {
+ USB_VENDOR_SMARTBRIDGES, 0,
+ USB_KNOWNDEV_NOPROD,
+ "SmartBridges",
+ NULL,
+ },
+ {
+ USB_VENDOR_BILLIONTON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Billionton Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_EXTENDED, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Extended Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_MSYSTEMS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "M-Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_AUTHENTEC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AuthenTec",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALATION, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Alation Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_GOHUBS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "GoHubs",
+ NULL,
+ },
+ {
+ USB_VENDOR_BIOMETRIC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "American Biometric Company",
+ NULL,
+ },
+ {
+ USB_VENDOR_TOSHIBA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Toshiba Corporation",
+ NULL,
+ },
+ {
+ USB_VENDOR_PLEXTOR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Plextor Corp.",
+ NULL,
+ },
+ {
+ USB_VENDOR_YANO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Yano",
+ NULL,
+ },
+ {
+ USB_VENDOR_KINGSTON, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Kingston Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_BLUEWATER, 0,
+ USB_KNOWNDEV_NOPROD,
+ "BlueWater Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_AGILENT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Agilent Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_PORTSMITH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Portsmith",
+ NULL,
+ },
+ {
+ USB_VENDOR_ADIRONDACK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Adirondack Wire & Cable",
+ NULL,
+ },
+ {
+ USB_VENDOR_BECKHOFF, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Beckhoff",
+ NULL,
+ },
+ {
+ USB_VENDOR_INTERSIL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Intersil",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALTIUS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Altius Solutions",
+ NULL,
+ },
+ {
+ USB_VENDOR_ARRIS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Arris Interactive",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACTIVCARD, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ACTIVCARD",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACTISYS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ACTiSYS",
+ NULL,
+ },
+ {
+ USB_VENDOR_AFOURTECH, 0,
+ USB_KNOWNDEV_NOPROD,
+ "A-FOUR TECH",
+ NULL,
+ },
+ {
+ USB_VENDOR_AIMEX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AIMEX",
+ NULL,
+ },
+ {
+ USB_VENDOR_ADDONICS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Addonics Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_AKAI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "AKAI professional M.I.",
+ NULL,
+ },
+ {
+ USB_VENDOR_ARESCOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ARESCOM",
+ NULL,
+ },
+ {
+ USB_VENDOR_BAY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Bay Associates",
+ NULL,
+ },
+ {
+ USB_VENDOR_ALTERA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Altera",
+ NULL,
+ },
+ {
+ USB_VENDOR_CSR, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Cambridge Silicon Radio Ltd.",
+ NULL,
+ },
+ {
+ USB_VENDOR_TREK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Trek Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_ASAHIOPTICAL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Asahi Optical",
+ NULL,
+ },
+ {
+ USB_VENDOR_BOCASYSTEMS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Boca Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_BROADCOM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Broadcom",
+ NULL,
+ },
+ {
+ USB_VENDOR_GREENHOUSE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "GREENHOUSE",
+ NULL,
+ },
+ {
+ USB_VENDOR_GEOCAST, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Geocast Network Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_NEODIO, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Neodio",
+ NULL,
+ },
+ {
+ USB_VENDOR_TODOS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Todos Data System",
+ NULL,
+ },
+ {
+ USB_VENDOR_HAL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "HAL Corporation",
+ NULL,
+ },
+ {
+ USB_VENDOR_EMS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "EMS Production Ltd.",
+ NULL,
+ },
+ {
+ USB_VENDOR_NEC2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "NEC",
+ NULL,
+ },
+ {
+ USB_VENDOR_ATI2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ATI",
+ NULL,
+ },
+ {
+ USB_VENDOR_ASIX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "ASIX Electronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_REALTEK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "RealTek",
+ NULL,
+ },
+ {
+ USB_VENDOR_AGATE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Agate Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_DMI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "DMI",
+ NULL,
+ },
+ {
+ USB_VENDOR_LUWEN, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Luwen",
+ NULL,
+ },
+ {
+ USB_VENDOR_SMC3, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Standard Microsystems",
+ NULL,
+ },
+ {
+ USB_VENDOR_PNY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "PNY",
+ NULL,
+ },
+ {
+ USB_VENDOR_MSI, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Micro Star International",
+ NULL,
+ },
+ {
+ USB_VENDOR_HAWKING, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Hawking Technologies",
+ NULL,
+ },
+ {
+ USB_VENDOR_MICROTUNE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Microtune, Inc.",
+ NULL,
+ },
+ {
+ USB_VENDOR_QUALCOMM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Qualcomm",
+ NULL,
+ },
+ {
+ USB_VENDOR_MOTOROLA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Motorola",
+ NULL,
+ },
+ {
+ USB_VENDOR_PLX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "PLX",
+ NULL,
+ },
+ {
+ USB_VENDOR_ASANTE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Asante",
+ NULL,
+ },
+ {
+ USB_VENDOR_JRC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Japan Radio Company",
+ NULL,
+ },
+ {
+ USB_VENDOR_DELORME, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Delorme Publishing",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACERCM, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Acer Communications & Multimedia Inc.",
+ NULL,
+ },
+ {
+ USB_VENDOR_BELKIN2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Belkin Components",
+ NULL,
+ },
+ {
+ USB_VENDOR_MOBILITY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Mobility",
+ NULL,
+ },
+ {
+ USB_VENDOR_SHARK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Shark",
+ NULL,
+ },
+ {
+ USB_VENDOR_SILICONPORTALS, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Silicon Portals",
+ NULL,
+ },
+ {
+ USB_VENDOR_SOHOWARE, 0,
+ USB_KNOWNDEV_NOPROD,
+ "SOHOware",
+ NULL,
+ },
+ {
+ USB_VENDOR_UMAX, 0,
+ USB_KNOWNDEV_NOPROD,
+ "UMAX Data Systems",
+ NULL,
+ },
+ {
+ USB_VENDOR_INSIDEOUT, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Inside Out Networks",
+ NULL,
+ },
+ {
+ USB_VENDOR_ENTREGA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Entrega",
+ NULL,
+ },
+ {
+ USB_VENDOR_ACTIONTEC, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Actiontec Electronics",
+ NULL,
+ },
+ {
+ USB_VENDOR_DLINK, 0,
+ USB_KNOWNDEV_NOPROD,
+ "D-Link",
+ NULL,
+ },
+ {
+ USB_VENDOR_VIDZMEDIA, 0,
+ USB_KNOWNDEV_NOPROD,
+ "VidzMedia Pte Ltd",
+ NULL,
+ },
+ {
+ USB_VENDOR_DAISY, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Daisy Technology",
+ NULL,
+ },
+ {
+ USB_VENDOR_DELL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Dell",
+ NULL,
+ },
+ {
+ USB_VENDOR_INTEL, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Intel",
+ NULL,
+ },
+ {
+ USB_VENDOR_HP2, 0,
+ USB_KNOWNDEV_NOPROD,
+ "Hewlett Packard",
+ 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..ece049d
--- /dev/null
+++ b/sys/dev/usb/usbdi.c
@@ -0,0 +1,1153 @@
+/* $NetBSD: usbdi.c,v 1.103 2002/09/27 15:37:38 provos Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#include "usb_if.h"
+#if defined(DIAGNOSTIC) && defined(__i386__)
+#include <machine/cpu.h>
+#endif
+#endif
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <machine/bus.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/usb_mem.h>
+
+#if defined(__FreeBSD__)
+#include "usb_if.h"
+#include <machine/clock.h>
+#define delay(d) DELAY(d)
+#endif
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) logprintf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+Static usbd_status usbd_ar_pipe(usbd_pipe_handle pipe);
+Static void usbd_do_request_async_cb
+ (usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void usbd_start_next(usbd_pipe_handle pipe);
+Static usbd_status usbd_open_pipe_ival
+ (usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *, int);
+Static int usbd_xfer_isread(usbd_xfer_handle xfer);
+
+Static int usbd_nbuses = 0;
+
+void
+usbd_init(void)
+{
+ usbd_nbuses++;
+}
+
+void
+usbd_finish(void)
+{
+ --usbd_nbuses;
+}
+
+static __inline int
+usbd_xfer_isread(usbd_xfer_handle xfer)
+{
+ if (xfer->rqflags & URQ_REQUEST)
+ return (xfer->request.bmRequestType & UT_READ);
+ else
+ return (xfer->pipe->endpoint->edesc->bEndpointAddress &
+ UE_DIR_IN);
+}
+
+#ifdef USB_DEBUG
+void
+usbd_dump_iface(struct usbd_interface *iface)
+{
+ printf("usbd_dump_iface: iface=%p\n", iface);
+ if (iface == NULL)
+ return;
+ printf(" device=%p idesc=%p index=%d altindex=%d priv=%p\n",
+ iface->device, iface->idesc, iface->index, iface->altindex,
+ iface->priv);
+}
+
+void
+usbd_dump_device(struct usbd_device *dev)
+{
+ printf("usbd_dump_device: dev=%p\n", dev);
+ if (dev == NULL)
+ return;
+ printf(" bus=%p default_pipe=%p\n", dev->bus, dev->default_pipe);
+ printf(" address=%d config=%d depth=%d speed=%d self_powered=%d "
+ "power=%d langid=%d\n",
+ dev->address, dev->config, dev->depth, dev->speed,
+ dev->self_powered, dev->power, dev->langid);
+}
+
+void
+usbd_dump_endpoint(struct usbd_endpoint *endp)
+{
+ printf("usbd_dump_endpoint: endp=%p\n", endp);
+ if (endp == NULL)
+ return;
+ printf(" edesc=%p refcnt=%d\n", endp->edesc, endp->refcnt);
+ if (endp->edesc)
+ printf(" bEndpointAddress=0x%02x\n",
+ endp->edesc->bEndpointAddress);
+}
+
+void
+usbd_dump_queue(usbd_pipe_handle pipe)
+{
+ usbd_xfer_handle xfer;
+
+ printf("usbd_dump_queue: pipe=%p\n", pipe);
+ SIMPLEQ_FOREACH(xfer, &pipe->queue, next) {
+ printf(" xfer=%p\n", xfer);
+ }
+}
+
+void
+usbd_dump_pipe(usbd_pipe_handle pipe)
+{
+ printf("usbd_dump_pipe: pipe=%p\n", pipe);
+ if (pipe == NULL)
+ return;
+ usbd_dump_iface(pipe->iface);
+ usbd_dump_device(pipe->device);
+ usbd_dump_endpoint(pipe->endpoint);
+ printf(" (usbd_dump_pipe:)\n refcnt=%d running=%d aborting=%d\n",
+ pipe->refcnt, pipe->running, pipe->aborting);
+ printf(" intrxfer=%p, repeat=%d, interval=%d\n",
+ pipe->intrxfer, pipe->repeat, pipe->interval);
+}
+#endif
+
+usbd_status
+usbd_open_pipe(usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe)
+{
+ return (usbd_open_pipe_ival(iface, address, flags, pipe,
+ USBD_DEFAULT_INTERVAL));
+}
+
+usbd_status
+usbd_open_pipe_ival(usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe, int ival)
+{
+ usbd_pipe_handle p;
+ struct usbd_endpoint *ep;
+ usbd_status err;
+ int i;
+
+ DPRINTFN(3,("usbd_open_pipe: iface=%p address=0x%x flags=0x%x\n",
+ iface, address, flags));
+
+ for (i = 0; i < iface->idesc->bNumEndpoints; i++) {
+ ep = &iface->endpoints[i];
+ if (ep->edesc == NULL)
+ return (USBD_IOERROR);
+ if (ep->edesc->bEndpointAddress == address)
+ goto found;
+ }
+ return (USBD_BAD_ADDRESS);
+ found:
+ if ((flags & USBD_EXCLUSIVE_USE) && ep->refcnt != 0)
+ return (USBD_IN_USE);
+ err = usbd_setup_pipe(iface->device, iface, ep, ival, &p);
+ if (err)
+ return (err);
+ LIST_INSERT_HEAD(&iface->pipes, p, next);
+ *pipe = p;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_open_pipe_intr(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 len,
+ usbd_callback cb, int ival)
+{
+ usbd_status err;
+ usbd_xfer_handle xfer;
+ usbd_pipe_handle ipipe;
+
+ DPRINTFN(3,("usbd_open_pipe_intr: address=0x%x flags=0x%x len=%d\n",
+ address, flags, len));
+
+ err = usbd_open_pipe_ival(iface, address, USBD_EXCLUSIVE_USE,
+ &ipipe, ival);
+ if (err)
+ return (err);
+ xfer = usbd_alloc_xfer(iface->device);
+ if (xfer == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ usbd_setup_xfer(xfer, ipipe, priv, buffer, len, flags,
+ USBD_NO_TIMEOUT, cb);
+ ipipe->intrxfer = xfer;
+ ipipe->repeat = 1;
+ err = usbd_transfer(xfer);
+ *pipe = ipipe;
+ if (err != USBD_IN_PROGRESS)
+ goto bad2;
+ return (USBD_NORMAL_COMPLETION);
+
+ bad2:
+ ipipe->intrxfer = NULL;
+ ipipe->repeat = 0;
+ usbd_free_xfer(xfer);
+ bad1:
+ usbd_close_pipe(ipipe);
+ return (err);
+}
+
+usbd_status
+usbd_close_pipe(usbd_pipe_handle pipe)
+{
+#ifdef DIAGNOSTIC
+ if (pipe == NULL) {
+ printf("usbd_close_pipe: pipe==NULL\n");
+ return (USBD_NORMAL_COMPLETION);
+ }
+#endif
+
+ if (--pipe->refcnt != 0)
+ return (USBD_NORMAL_COMPLETION);
+ if (! SIMPLEQ_EMPTY(&pipe->queue))
+ return (USBD_PENDING_REQUESTS);
+ LIST_REMOVE(pipe, next);
+ pipe->endpoint->refcnt--;
+ pipe->methods->close(pipe);
+ if (pipe->intrxfer != NULL)
+ usbd_free_xfer(pipe->intrxfer);
+ free(pipe, M_USB);
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_transfer(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ usb_dma_t *dmap = &xfer->dmabuf;
+ usbd_status err;
+ u_int size;
+ int s;
+
+ DPRINTFN(5,("usbd_transfer: xfer=%p, flags=%d, pipe=%p, running=%d\n",
+ xfer, xfer->flags, pipe, pipe->running));
+#ifdef USB_DEBUG
+ if (usbdebug > 5)
+ usbd_dump_queue(pipe);
+#endif
+ xfer->done = 0;
+
+ if (pipe->aborting)
+ return (USBD_CANCELLED);
+
+ size = xfer->length;
+ /* If there is no buffer, allocate one. */
+ if (!(xfer->rqflags & URQ_DEV_DMABUF) && size != 0) {
+ struct usbd_bus *bus = pipe->device->bus;
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_AUTO_DMABUF)
+ printf("usbd_transfer: has old buffer!\n");
+#endif
+ err = bus->methods->allocm(bus, dmap, size);
+ if (err)
+ return (err);
+ xfer->rqflags |= URQ_AUTO_DMABUF;
+ }
+
+ /* Copy data if going out. */
+ if (!(xfer->flags & USBD_NO_COPY) && size != 0 &&
+ !usbd_xfer_isread(xfer))
+ memcpy(KERNADDR(dmap, 0), xfer->buffer, size);
+
+ err = pipe->methods->transfer(xfer);
+
+ if (err != USBD_IN_PROGRESS && err) {
+ /* The transfer has not been queued, so free buffer. */
+ if (xfer->rqflags & URQ_AUTO_DMABUF) {
+ struct usbd_bus *bus = pipe->device->bus;
+
+ bus->methods->freem(bus, &xfer->dmabuf);
+ xfer->rqflags &= ~URQ_AUTO_DMABUF;
+ }
+ }
+
+ if (!(xfer->flags & USBD_SYNCHRONOUS))
+ return (err);
+
+ /* Sync transfer, wait for completion. */
+ if (err != USBD_IN_PROGRESS)
+ return (err);
+ s = splusb();
+ if (!xfer->done) {
+ if (pipe->device->bus->use_polling)
+ panic("usbd_transfer: not done");
+ tsleep(xfer, PRIBIO, "usbsyn", 0);
+ }
+ splx(s);
+ return (xfer->status);
+}
+
+/* Like usbd_transfer(), but waits for completion. */
+usbd_status
+usbd_sync_transfer(usbd_xfer_handle xfer)
+{
+ xfer->flags |= USBD_SYNCHRONOUS;
+ return (usbd_transfer(xfer));
+}
+
+void *
+usbd_alloc_buffer(usbd_xfer_handle xfer, u_int32_t size)
+{
+ struct usbd_bus *bus = xfer->device->bus;
+ usbd_status err;
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF))
+ printf("usbd_alloc_buffer: xfer already has a buffer\n");
+#endif
+ err = bus->methods->allocm(bus, &xfer->dmabuf, size);
+ if (err)
+ return (NULL);
+ xfer->rqflags |= URQ_DEV_DMABUF;
+ return (KERNADDR(&xfer->dmabuf, 0));
+}
+
+void
+usbd_free_buffer(usbd_xfer_handle xfer)
+{
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF))) {
+ printf("usbd_free_buffer: no buffer\n");
+ return;
+ }
+#endif
+ xfer->rqflags &= ~(URQ_DEV_DMABUF | URQ_AUTO_DMABUF);
+ xfer->device->bus->methods->freem(xfer->device->bus, &xfer->dmabuf);
+}
+
+void *
+usbd_get_buffer(usbd_xfer_handle xfer)
+{
+ if (!(xfer->rqflags & URQ_DEV_DMABUF))
+ return (0);
+ return (KERNADDR(&xfer->dmabuf, 0));
+}
+
+usbd_xfer_handle
+usbd_alloc_xfer(usbd_device_handle dev)
+{
+ usbd_xfer_handle xfer;
+
+ xfer = dev->bus->methods->allocx(dev->bus);
+ if (xfer == NULL)
+ return (NULL);
+ xfer->device = dev;
+ usb_callout_init(xfer->timeout_handle);
+ DPRINTFN(5,("usbd_alloc_xfer() = %p\n", xfer));
+ return (xfer);
+}
+
+usbd_status
+usbd_free_xfer(usbd_xfer_handle xfer)
+{
+ DPRINTFN(5,("usbd_free_xfer: %p\n", xfer));
+ if (xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF))
+ usbd_free_buffer(xfer);
+#if defined(__NetBSD__) && defined(DIAGNOSTIC)
+ if (callout_pending(&xfer->timeout_handle)) {
+ callout_stop(&xfer->timeout_handle);
+ printf("usbd_free_xfer: timout_handle pending");
+ }
+#endif
+ xfer->device->bus->methods->freex(xfer->device->bus, xfer);
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usbd_setup_xfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ usbd_private_handle priv, void *buffer, u_int32_t length,
+ u_int16_t flags, u_int32_t timeout,
+ usbd_callback callback)
+{
+ xfer->pipe = pipe;
+ xfer->priv = priv;
+ xfer->buffer = buffer;
+ xfer->length = length;
+ xfer->actlen = 0;
+ xfer->flags = flags;
+ xfer->timeout = timeout;
+ xfer->status = USBD_NOT_STARTED;
+ xfer->callback = callback;
+ xfer->rqflags &= ~URQ_REQUEST;
+ xfer->nframes = 0;
+}
+
+void
+usbd_setup_default_xfer(usbd_xfer_handle xfer, 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 callback)
+{
+ xfer->pipe = dev->default_pipe;
+ xfer->priv = priv;
+ xfer->buffer = buffer;
+ xfer->length = length;
+ xfer->actlen = 0;
+ xfer->flags = flags;
+ xfer->timeout = timeout;
+ xfer->status = USBD_NOT_STARTED;
+ xfer->callback = callback;
+ xfer->request = *req;
+ xfer->rqflags |= URQ_REQUEST;
+ xfer->nframes = 0;
+}
+
+void
+usbd_setup_isoc_xfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ usbd_private_handle priv, u_int16_t *frlengths,
+ u_int32_t nframes, u_int16_t flags, usbd_callback callback)
+{
+ xfer->pipe = pipe;
+ xfer->priv = priv;
+ xfer->buffer = 0;
+ xfer->length = 0;
+ xfer->actlen = 0;
+ xfer->flags = flags;
+ xfer->timeout = USBD_NO_TIMEOUT;
+ xfer->status = USBD_NOT_STARTED;
+ xfer->callback = callback;
+ xfer->rqflags &= ~URQ_REQUEST;
+ xfer->frlengths = frlengths;
+ xfer->nframes = nframes;
+}
+
+void
+usbd_get_xfer_status(usbd_xfer_handle xfer, usbd_private_handle *priv,
+ void **buffer, u_int32_t *count, usbd_status *status)
+{
+ if (priv != NULL)
+ *priv = xfer->priv;
+ if (buffer != NULL)
+ *buffer = xfer->buffer;
+ if (count != NULL)
+ *count = xfer->actlen;
+ if (status != NULL)
+ *status = xfer->status;
+}
+
+usb_config_descriptor_t *
+usbd_get_config_descriptor(usbd_device_handle dev)
+{
+#ifdef DIAGNOSTIC
+ if (dev == NULL) {
+ printf("usbd_get_config_descriptor: dev == NULL\n");
+ return (NULL);
+ }
+#endif
+ return (dev->cdesc);
+}
+
+usb_interface_descriptor_t *
+usbd_get_interface_descriptor(usbd_interface_handle iface)
+{
+#ifdef DIAGNOSTIC
+ if (iface == NULL) {
+ printf("usbd_get_interface_descriptor: dev == NULL\n");
+ return (NULL);
+ }
+#endif
+ return (iface->idesc);
+}
+
+usb_device_descriptor_t *
+usbd_get_device_descriptor(usbd_device_handle dev)
+{
+ return (&dev->ddesc);
+}
+
+usb_endpoint_descriptor_t *
+usbd_interface2endpoint_descriptor(usbd_interface_handle iface, u_int8_t index)
+{
+ if (index >= iface->idesc->bNumEndpoints)
+ return (0);
+ return (iface->endpoints[index].edesc);
+}
+
+usbd_status
+usbd_abort_pipe(usbd_pipe_handle pipe)
+{
+ usbd_status err;
+ int s;
+
+#ifdef DIAGNOSTIC
+ if (pipe == NULL) {
+ printf("usbd_close_pipe: pipe==NULL\n");
+ return (USBD_NORMAL_COMPLETION);
+ }
+#endif
+ s = splusb();
+ err = usbd_ar_pipe(pipe);
+ splx(s);
+ return (err);
+}
+
+usbd_status
+usbd_clear_endpoint_stall(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTFN(8, ("usbd_clear_endpoint_stall\n"));
+
+ /*
+ * Clearing en endpoint stall resets the endpoint toggle, so
+ * do the same to the HC toggle.
+ */
+ pipe->methods->cleartoggle(pipe);
+
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_HALT);
+ USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(dev, &req, 0);
+#if 0
+XXX should we do this?
+ if (!err) {
+ pipe->state = USBD_PIPE_ACTIVE;
+ /* XXX activate pipe */
+ }
+#endif
+ return (err);
+}
+
+usbd_status
+usbd_clear_endpoint_stall_async(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ usb_device_request_t req;
+ usbd_status err;
+
+ pipe->methods->cleartoggle(pipe);
+
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_HALT);
+ USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress);
+ USETW(req.wLength, 0);
+ err = usbd_do_request_async(dev, &req, 0);
+ return (err);
+}
+
+void
+usbd_clear_endpoint_toggle(usbd_pipe_handle pipe)
+{
+ pipe->methods->cleartoggle(pipe);
+}
+
+usbd_status
+usbd_endpoint_count(usbd_interface_handle iface, u_int8_t *count)
+{
+#ifdef DIAGNOSTIC
+ if (iface == NULL || iface->idesc == NULL) {
+ printf("usbd_endpoint_count: NULL pointer\n");
+ return (USBD_INVAL);
+ }
+#endif
+ *count = iface->idesc->bNumEndpoints;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_interface_count(usbd_device_handle dev, u_int8_t *count)
+{
+ if (dev->cdesc == NULL)
+ return (USBD_NOT_CONFIGURED);
+ *count = dev->cdesc->bNumInterface;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usbd_interface2device_handle(usbd_interface_handle iface,
+ usbd_device_handle *dev)
+{
+ *dev = iface->device;
+}
+
+usbd_status
+usbd_device2interface_handle(usbd_device_handle dev,
+ u_int8_t ifaceno, usbd_interface_handle *iface)
+{
+ if (dev->cdesc == NULL)
+ return (USBD_NOT_CONFIGURED);
+ if (ifaceno >= dev->cdesc->bNumInterface)
+ return (USBD_INVAL);
+ *iface = &dev->ifaces[ifaceno];
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_device_handle
+usbd_pipe2device_handle(usbd_pipe_handle pipe)
+{
+ return (pipe->device);
+}
+
+/* XXXX use altno */
+usbd_status
+usbd_set_interface(usbd_interface_handle iface, int altidx)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ void *endpoints;
+
+ if (LIST_FIRST(&iface->pipes) != 0)
+ return (USBD_IN_USE);
+
+ endpoints = iface->endpoints;
+ err = usbd_fill_iface_data(iface->device, iface->index, altidx);
+ if (err)
+ return (err);
+
+ /* new setting works, we can free old endpoints */
+ if (endpoints != NULL)
+ free(endpoints, M_USB);
+
+#ifdef DIAGNOSTIC
+ if (iface->idesc == NULL) {
+ printf("usbd_set_interface: NULL pointer\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ req.bmRequestType = UT_WRITE_INTERFACE;
+ req.bRequest = UR_SET_INTERFACE;
+ USETW(req.wValue, iface->idesc->bAlternateSetting);
+ USETW(req.wIndex, iface->idesc->bInterfaceNumber);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(iface->device, &req, 0));
+}
+
+int
+usbd_get_no_alts(usb_config_descriptor_t *cdesc, int ifaceno)
+{
+ char *p = (char *)cdesc;
+ char *end = p + UGETW(cdesc->wTotalLength);
+ usb_interface_descriptor_t *d;
+ int n;
+
+ for (n = 0; p < end; p += d->bLength) {
+ d = (usb_interface_descriptor_t *)p;
+ if (p + d->bLength <= end &&
+ d->bDescriptorType == UDESC_INTERFACE &&
+ d->bInterfaceNumber == ifaceno)
+ n++;
+ }
+ return (n);
+}
+
+int
+usbd_get_interface_altindex(usbd_interface_handle iface)
+{
+ return (iface->altindex);
+}
+
+usbd_status
+usbd_get_interface(usbd_interface_handle iface, u_int8_t *aiface)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_INTERFACE;
+ req.bRequest = UR_GET_INTERFACE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, iface->idesc->bInterfaceNumber);
+ USETW(req.wLength, 1);
+ return (usbd_do_request(iface->device, &req, aiface));
+}
+
+/*** Internal routines ***/
+
+/* Dequeue all pipe operations, called at splusb(). */
+Static usbd_status
+usbd_ar_pipe(usbd_pipe_handle pipe)
+{
+ usbd_xfer_handle xfer;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(2,("usbd_ar_pipe: pipe=%p\n", pipe));
+#ifdef USB_DEBUG
+ if (usbdebug > 5)
+ usbd_dump_queue(pipe);
+#endif
+ pipe->repeat = 0;
+ pipe->aborting = 1;
+ while ((xfer = SIMPLEQ_FIRST(&pipe->queue)) != NULL) {
+ DPRINTFN(2,("usbd_ar_pipe: pipe=%p xfer=%p (methods=%p)\n",
+ pipe, xfer, pipe->methods));
+ /* Make the HC abort it (and invoke the callback). */
+ pipe->methods->abort(xfer);
+ /* XXX only for non-0 usbd_clear_endpoint_stall(pipe); */
+ }
+ pipe->aborting = 0;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Called at splusb() */
+void
+usb_transfer_complete(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ usb_dma_t *dmap = &xfer->dmabuf;
+ int repeat = pipe->repeat;
+ int polling;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(5, ("usb_transfer_complete: pipe=%p xfer=%p status=%d "
+ "actlen=%d\n", pipe, xfer, xfer->status, xfer->actlen));
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_ONQU) {
+ printf("usb_transfer_complete: xfer=%p not busy 0x%08x\n",
+ xfer, xfer->busy_free);
+ return;
+ }
+#endif
+
+#ifdef DIAGNOSTIC
+ if (pipe == NULL) {
+ printf("usbd_transfer_cb: pipe==0, xfer=%p\n", xfer);
+ return;
+ }
+#endif
+ polling = pipe->device->bus->use_polling;
+ /* XXXX */
+ if (polling)
+ pipe->running = 0;
+
+ if (!(xfer->flags & USBD_NO_COPY) && xfer->actlen != 0 &&
+ usbd_xfer_isread(xfer)) {
+#ifdef DIAGNOSTIC
+ if (xfer->actlen > xfer->length) {
+ printf("usb_transfer_complete: actlen > len %d > %d\n",
+ xfer->actlen, xfer->length);
+ xfer->actlen = xfer->length;
+ }
+#endif
+ memcpy(xfer->buffer, KERNADDR(dmap, 0), xfer->actlen);
+ }
+
+ /* if we allocated the buffer in usbd_transfer() we free it here. */
+ if (xfer->rqflags & URQ_AUTO_DMABUF) {
+ if (!repeat) {
+ struct usbd_bus *bus = pipe->device->bus;
+ bus->methods->freem(bus, dmap);
+ xfer->rqflags &= ~URQ_AUTO_DMABUF;
+ }
+ }
+
+ if (!repeat) {
+ /* Remove request from queue. */
+#ifdef DIAGNOSTIC
+ if (xfer != SIMPLEQ_FIRST(&pipe->queue))
+ printf("usb_transfer_complete: bad dequeue %p != %p\n",
+ xfer, SIMPLEQ_FIRST(&pipe->queue));
+ xfer->busy_free = XFER_BUSY;
+#endif
+ SIMPLEQ_REMOVE_HEAD(&pipe->queue, next);
+ }
+ DPRINTFN(5,("usb_transfer_complete: repeat=%d new head=%p\n",
+ repeat, SIMPLEQ_FIRST(&pipe->queue)));
+
+ /* Count completed transfers. */
+ ++pipe->device->bus->stats.uds_requests
+ [pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE];
+
+ xfer->done = 1;
+ if (!xfer->status && xfer->actlen < xfer->length &&
+ !(xfer->flags & USBD_SHORT_XFER_OK)) {
+ DPRINTFN(-1,("usbd_transfer_cb: short transfer %d<%d\n",
+ xfer->actlen, xfer->length));
+ xfer->status = USBD_SHORT_XFER;
+ }
+
+ if (xfer->callback)
+ xfer->callback(xfer, xfer->priv, xfer->status);
+
+#ifdef DIAGNOSTIC
+ if (pipe->methods->done != NULL)
+ pipe->methods->done(xfer);
+ else
+ printf("usb_transfer_complete: pipe->methods->done == NULL\n");
+#else
+ pipe->methods->done(xfer);
+#endif
+
+ if ((xfer->flags & USBD_SYNCHRONOUS) && !polling)
+ wakeup(xfer);
+
+ if (!repeat) {
+ /* XXX should we stop the queue on all errors? */
+ if ((xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT) &&
+ pipe->iface != NULL) /* not control pipe */
+ pipe->running = 0;
+ else
+ usbd_start_next(pipe);
+ }
+}
+
+usbd_status
+usb_insert_transfer(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ usbd_status err;
+ int s;
+
+ DPRINTFN(5,("usb_insert_transfer: pipe=%p running=%d timeout=%d\n",
+ pipe, pipe->running, xfer->timeout));
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("usb_insert_transfer: xfer=%p not busy 0x%08x\n",
+ xfer, xfer->busy_free);
+ return (USBD_INVAL);
+ }
+ xfer->busy_free = XFER_ONQU;
+#endif
+ s = splusb();
+ SIMPLEQ_INSERT_TAIL(&pipe->queue, xfer, next);
+ if (pipe->running)
+ err = USBD_IN_PROGRESS;
+ else {
+ pipe->running = 1;
+ err = USBD_NORMAL_COMPLETION;
+ }
+ splx(s);
+ return (err);
+}
+
+/* Called at splusb() */
+void
+usbd_start_next(usbd_pipe_handle pipe)
+{
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ SPLUSBCHECK;
+
+#ifdef DIAGNOSTIC
+ if (pipe == NULL) {
+ printf("usbd_start_next: pipe == NULL\n");
+ return;
+ }
+ if (pipe->methods == NULL || pipe->methods->start == NULL) {
+ printf("usbd_start_next: pipe=%p no start method\n", pipe);
+ return;
+ }
+#endif
+
+ /* Get next request in queue. */
+ xfer = SIMPLEQ_FIRST(&pipe->queue);
+ DPRINTFN(5, ("usbd_start_next: pipe=%p, xfer=%p\n", pipe, xfer));
+ if (xfer == NULL) {
+ pipe->running = 0;
+ } else {
+ err = pipe->methods->start(xfer);
+ if (err != USBD_IN_PROGRESS) {
+ printf("usbd_start_next: error=%d\n", err);
+ pipe->running = 0;
+ /* XXX do what? */
+ }
+ }
+}
+
+usbd_status
+usbd_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data)
+{
+ return (usbd_do_request_flags(dev, req, data, 0, 0,
+ USBD_DEFAULT_TIMEOUT));
+}
+
+usbd_status
+usbd_do_request_flags(usbd_device_handle dev, usb_device_request_t *req,
+ void *data, u_int16_t flags, int *actlen, u_int32_t timo)
+{
+ return (usbd_do_request_flags_pipe(dev, dev->default_pipe, req,
+ data, flags, actlen, timo));
+}
+
+usbd_status
+usbd_do_request_flags_pipe(usbd_device_handle dev, usbd_pipe_handle pipe,
+ usb_device_request_t *req, void *data, u_int16_t flags, int *actlen,
+ u_int32_t timeout)
+{
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+#ifdef DIAGNOSTIC
+#if defined(__i386__) && defined(__FreeBSD__)
+ KASSERT(curthread->td_intr_nesting_level == 0,
+ ("usbd_do_request: in interrupt context"));
+#endif
+ if (dev->bus->intr_context) {
+ printf("usbd_do_request: not in process context\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ xfer = usbd_alloc_xfer(dev);
+ if (xfer == NULL)
+ return (USBD_NOMEM);
+ usbd_setup_default_xfer(xfer, dev, 0, timeout, req,
+ data, UGETW(req->wLength), flags, 0);
+ xfer->pipe = pipe;
+ err = usbd_sync_transfer(xfer);
+#if defined(USB_DEBUG) || defined(DIAGNOSTIC)
+ if (xfer->actlen > xfer->length)
+ DPRINTF(("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, xfer->request.bmRequestType,
+ xfer->request.bRequest, UGETW(xfer->request.wValue),
+ UGETW(xfer->request.wIndex),
+ UGETW(xfer->request.wLength),
+ xfer->length, xfer->actlen));
+#endif
+ if (actlen != NULL)
+ *actlen = xfer->actlen;
+ if (err == USBD_STALLED) {
+ /*
+ * The control endpoint has stalled. Control endpoints
+ * should not halt, but some may do so anyway so clear
+ * any halt condition.
+ */
+ usb_device_request_t treq;
+ usb_status_t status;
+ u_int16_t s;
+ usbd_status nerr;
+
+ treq.bmRequestType = UT_READ_ENDPOINT;
+ treq.bRequest = UR_GET_STATUS;
+ USETW(treq.wValue, 0);
+ USETW(treq.wIndex, 0);
+ USETW(treq.wLength, sizeof(usb_status_t));
+ usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT,
+ &treq, &status,sizeof(usb_status_t),
+ 0, 0);
+ nerr = usbd_sync_transfer(xfer);
+ if (nerr)
+ goto bad;
+ s = UGETW(status.wStatus);
+ DPRINTF(("usbd_do_request: status = 0x%04x\n", s));
+ if (!(s & UES_HALT))
+ goto bad;
+ treq.bmRequestType = UT_WRITE_ENDPOINT;
+ treq.bRequest = UR_CLEAR_FEATURE;
+ USETW(treq.wValue, UF_ENDPOINT_HALT);
+ USETW(treq.wIndex, 0);
+ USETW(treq.wLength, 0);
+ usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT,
+ &treq, &status, 0, 0, 0);
+ nerr = usbd_sync_transfer(xfer);
+ if (nerr)
+ goto bad;
+ }
+
+ bad:
+ usbd_free_xfer(xfer);
+ return (err);
+}
+
+void
+usbd_do_request_async_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+#if defined(USB_DEBUG) || defined(DIAGNOSTIC)
+ if (xfer->actlen > xfer->length)
+ DPRINTF(("usbd_do_request: overrun addr=%d type=0x%02x req=0x"
+ "%02x val=%d index=%d rlen=%d length=%d actlen=%d\n",
+ xfer->pipe->device->address,
+ xfer->request.bmRequestType,
+ xfer->request.bRequest, UGETW(xfer->request.wValue),
+ UGETW(xfer->request.wIndex),
+ UGETW(xfer->request.wLength),
+ xfer->length, xfer->actlen));
+#endif
+ usbd_free_xfer(xfer);
+}
+
+/*
+ * Execute a request without waiting for completion.
+ * Can be used from interrupt context.
+ */
+usbd_status
+usbd_do_request_async(usbd_device_handle dev, usb_device_request_t *req,
+ void *data)
+{
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ xfer = usbd_alloc_xfer(dev);
+ if (xfer == NULL)
+ return (USBD_NOMEM);
+ usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT, req,
+ data, UGETW(req->wLength), 0, usbd_do_request_async_cb);
+ err = usbd_transfer(xfer);
+ if (err != USBD_IN_PROGRESS) {
+ usbd_free_xfer(xfer);
+ return (err);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+const struct usbd_quirks *
+usbd_get_quirks(usbd_device_handle dev)
+{
+#ifdef DIAGNOSTIC
+ if (dev == NULL) {
+ printf("usbd_get_quirks: dev == NULL\n");
+ return 0;
+ }
+#endif
+ return (dev->quirks);
+}
+
+/* XXX do periodic free() of free list */
+
+/*
+ * Called from keyboard driver when in polling mode.
+ */
+void
+usbd_dopoll(usbd_interface_handle iface)
+{
+ iface->device->bus->methods->do_poll(iface->device->bus);
+}
+
+void
+usbd_set_polling(usbd_device_handle dev, int on)
+{
+ if (on)
+ dev->bus->use_polling++;
+ else
+ dev->bus->use_polling--;
+ /* When polling we need to make sure there is nothing pending to do. */
+ if (dev->bus->use_polling)
+ dev->bus->methods->soft_intr(dev->bus);
+}
+
+
+usb_endpoint_descriptor_t *
+usbd_get_endpoint_descriptor(usbd_interface_handle iface, u_int8_t address)
+{
+ struct usbd_endpoint *ep;
+ int i;
+
+ for (i = 0; i < iface->idesc->bNumEndpoints; i++) {
+ ep = &iface->endpoints[i];
+ if (ep->edesc->bEndpointAddress == address)
+ return (iface->endpoints[i].edesc);
+ }
+ return (0);
+}
+
+/*
+ * usbd_ratecheck() can limit the number of error messages that occurs.
+ * When a device is unplugged it may take up to 0.25s for the hub driver
+ * to notice it. If the driver continuosly tries to do I/O operations
+ * this can generate a large number of messages.
+ */
+int
+usbd_ratecheck(struct timeval *last)
+{
+ if (last->tv_sec == time_second)
+ return (0);
+ last->tv_sec = time_second;
+ return (1);
+}
+
+/*
+ * Search for a vendor/product pair in an array. The item size is
+ * given as an argument.
+ */
+const struct usb_devno *
+usb_match_device(const struct usb_devno *tbl, u_int nentries, u_int sz,
+ u_int16_t vendor, u_int16_t product)
+{
+ while (nentries-- > 0) {
+ u_int16_t tproduct = tbl->ud_product;
+ if (tbl->ud_vendor == vendor &&
+ (tproduct == product || tproduct == USB_PRODUCT_ANY))
+ return (tbl);
+ tbl = (const struct usb_devno *)((const char *)tbl + sz);
+ }
+ return (NULL);
+}
+
+#if defined(__FreeBSD__)
+int
+usbd_driver_load(module_t mod, int what, void *arg)
+{
+ /* XXX should implement something like a function that removes all generic devices */
+
+ return (0);
+}
+
+#endif
diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h
new file mode 100644
index 0000000..b9e1c1e
--- /dev/null
+++ b/sys/dev/usb/usbdi.h
@@ -0,0 +1,281 @@
+/* $NetBSD: usbdi.h,v 1.62 2002/07/11 21:14:35 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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_xfer *usbd_xfer_handle;
+typedef void *usbd_private_handle;
+
+typedef enum { /* keep in sync with usbd_status_msgs */
+ USBD_NORMAL_COMPLETION = 0, /* must be 0 */
+ USBD_IN_PROGRESS, /* 1 */
+ /* errors */
+ USBD_PENDING_REQUESTS, /* 2 */
+ USBD_NOT_STARTED, /* 3 */
+ USBD_INVAL, /* 4 */
+ USBD_NOMEM, /* 5 */
+ USBD_CANCELLED, /* 6 */
+ USBD_BAD_ADDRESS, /* 7 */
+ USBD_IN_USE, /* 8 */
+ USBD_NO_ADDR, /* 9 */
+ USBD_SET_ADDR_FAILED, /* 10 */
+ USBD_NO_POWER, /* 11 */
+ USBD_TOO_DEEP, /* 12 */
+ USBD_IOERROR, /* 13 */
+ USBD_NOT_CONFIGURED, /* 14 */
+ USBD_TIMEOUT, /* 15 */
+ USBD_SHORT_XFER, /* 16 */
+ USBD_STALLED, /* 17 */
+ USBD_INTERRUPTED, /* 18 */
+
+ USBD_ERROR_MAX /* must be last */
+} usbd_status;
+
+typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+
+/* Open flags */
+#define USBD_EXCLUSIVE_USE 0x01
+
+/* Use default (specified by ep. desc.) interval on interrupt pipe */
+#define USBD_DEFAULT_INTERVAL (-1)
+
+/* Request flags */
+#define USBD_NO_COPY 0x01 /* do not copy data to DMA buffer */
+#define USBD_SYNCHRONOUS 0x02 /* wait for completion */
+/* in usb.h #define USBD_SHORT_XFER_OK 0x04*/ /* allow short reads */
+#define USBD_FORCE_SHORT_XFER 0x08 /* force last short packet on write */
+
+#define USBD_NO_TIMEOUT 0
+#define USBD_DEFAULT_TIMEOUT 5000 /* ms = 5 s */
+
+#if defined(__FreeBSD__)
+#endif
+
+usbd_status usbd_open_pipe(usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe);
+usbd_status usbd_close_pipe(usbd_pipe_handle pipe);
+usbd_status usbd_transfer(usbd_xfer_handle req);
+usbd_xfer_handle usbd_alloc_xfer(usbd_device_handle);
+usbd_status usbd_free_xfer(usbd_xfer_handle xfer);
+void usbd_setup_xfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ usbd_private_handle priv, void *buffer,
+ u_int32_t length, u_int16_t flags, u_int32_t timeout,
+ usbd_callback);
+void usbd_setup_default_xfer(usbd_xfer_handle xfer, 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);
+void usbd_setup_isoc_xfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ usbd_private_handle priv, u_int16_t *frlengths,
+ u_int32_t nframes, u_int16_t flags, usbd_callback);
+void usbd_get_xfer_status(usbd_xfer_handle xfer, usbd_private_handle *priv,
+ void **buffer, u_int32_t *count, usbd_status *status);
+usb_endpoint_descriptor_t *usbd_interface2endpoint_descriptor
+ (usbd_interface_handle iface, u_int8_t address);
+usbd_status usbd_abort_pipe(usbd_pipe_handle pipe);
+usbd_status usbd_clear_endpoint_stall(usbd_pipe_handle pipe);
+usbd_status usbd_clear_endpoint_stall_async(usbd_pipe_handle pipe);
+void usbd_clear_endpoint_toggle(usbd_pipe_handle pipe);
+usbd_status usbd_endpoint_count(usbd_interface_handle dev, u_int8_t *count);
+usbd_status usbd_interface_count(usbd_device_handle dev, u_int8_t *count);
+void usbd_interface2device_handle(usbd_interface_handle iface,
+ usbd_device_handle *dev);
+usbd_status usbd_device2interface_handle(usbd_device_handle dev,
+ u_int8_t ifaceno, usbd_interface_handle *iface);
+
+usbd_device_handle usbd_pipe2device_handle(usbd_pipe_handle);
+void *usbd_alloc_buffer(usbd_xfer_handle xfer, u_int32_t size);
+void usbd_free_buffer(usbd_xfer_handle xfer);
+void *usbd_get_buffer(usbd_xfer_handle xfer);
+usbd_status usbd_sync_transfer(usbd_xfer_handle req);
+usbd_status usbd_open_pipe_intr(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, int);
+usbd_status usbd_do_request(usbd_device_handle pipe, usb_device_request_t *req,
+ void *data);
+usbd_status usbd_do_request_async(usbd_device_handle pipe,
+ usb_device_request_t *req, void *data);
+usbd_status usbd_do_request_flags(usbd_device_handle pipe,
+ usb_device_request_t *req,
+ void *data, u_int16_t flags, int*, u_int32_t);
+usbd_status usbd_do_request_flags_pipe(
+ usbd_device_handle dev, usbd_pipe_handle pipe,
+ usb_device_request_t *req, void *data, u_int16_t flags, int *actlen,
+ u_int32_t);
+usb_interface_descriptor_t *usbd_get_interface_descriptor
+ (usbd_interface_handle iface);
+usb_config_descriptor_t *usbd_get_config_descriptor(usbd_device_handle dev);
+usb_device_descriptor_t *usbd_get_device_descriptor(usbd_device_handle dev);
+usbd_status usbd_set_interface(usbd_interface_handle, int);
+int usbd_get_no_alts(usb_config_descriptor_t *, int);
+usbd_status usbd_get_interface(usbd_interface_handle iface, u_int8_t *aiface);
+void usbd_fill_deviceinfo(usbd_device_handle, struct usb_device_info *, int);
+int usbd_get_interface_altindex(usbd_interface_handle iface);
+
+usb_interface_descriptor_t *usbd_find_idesc(usb_config_descriptor_t *cd,
+ int iindex, int ano);
+usb_endpoint_descriptor_t *usbd_find_edesc(usb_config_descriptor_t *cd,
+ int ifaceidx, int altidx,
+ int endptidx);
+
+void usbd_dopoll(usbd_interface_handle);
+void usbd_set_polling(usbd_device_handle dev, int on);
+
+const char *usbd_errstr(usbd_status err);
+
+void usbd_add_dev_event(int, usbd_device_handle);
+void usbd_add_drv_event(int, usbd_device_handle, device_ptr_t);
+
+void usbd_devinfo(usbd_device_handle, int, char *);
+const struct usbd_quirks *usbd_get_quirks(usbd_device_handle);
+usb_endpoint_descriptor_t *usbd_get_endpoint_descriptor
+ (usbd_interface_handle iface, u_int8_t address);
+
+usbd_status usbd_reload_device_desc(usbd_device_handle);
+
+int usbd_ratecheck(struct timeval *last);
+
+/*
+ * The usb_task structs form a queue of things to run in the USB event
+ * thread. Normally this is just device discovery when a connect/disconnect
+ * has been detected. But it may also be used by drivers that need to
+ * perform (short) tasks that must have a process context.
+ */
+struct usb_task {
+ TAILQ_ENTRY(usb_task) next;
+ void (*fun)(void *);
+ void *arg;
+ char onqueue;
+};
+
+void usb_add_task(usbd_device_handle dev, struct usb_task *task);
+void usb_rem_task(usbd_device_handle dev, struct usb_task *task);
+#define usb_init_task(t, f, a) ((t)->fun = (f), (t)->arg = (a), (t)->onqueue = 0)
+
+struct usb_devno {
+ u_int16_t ud_vendor;
+ u_int16_t ud_product;
+};
+const struct usb_devno *usb_match_device(const struct usb_devno *tbl,
+ u_int nentries, u_int sz, u_int16_t vendor, u_int16_t product);
+#define usb_lookup(tbl, vendor, product) \
+ usb_match_device((const struct usb_devno *)(tbl), sizeof (tbl) / sizeof ((tbl)[0]), sizeof ((tbl)[0]), (vendor), (product))
+#define USB_PRODUCT_ANY 0xffff
+
+/* NetBSD attachment information */
+
+/* Attach data */
+struct usb_attach_arg {
+ int port;
+ int configno;
+ int ifaceno;
+ int vendor;
+ int product;
+ int release;
+ int matchlvl;
+ usbd_device_handle device; /* current device */
+ usbd_interface_handle iface; /* current interface */
+ int usegeneric;
+ usbd_interface_handle *ifaces; /* all interfaces */
+ int nifaces; /* number of interfaces */
+};
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+/* 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 */
+#define UMATCH_VENDOR_PRODUCT_REV (-10)
+#define UMATCH_VENDOR_PRODUCT (-20)
+#define UMATCH_VENDOR_DEVCLASS_DEVPROTO (-30)
+#define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO (-40)
+#define UMATCH_DEVCLASS_DEVSUBCLASS (-50)
+#define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE (-60)
+#define UMATCH_VENDOR_PRODUCT_CONF_IFACE (-70)
+#define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO (-80)
+#define UMATCH_VENDOR_IFACESUBCLASS (-90)
+#define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO (-100)
+#define UMATCH_IFACECLASS_IFACESUBCLASS (-110)
+#define UMATCH_IFACECLASS (-120)
+#define UMATCH_IFACECLASS_GENERIC (-130)
+#define UMATCH_GENERIC (-140)
+#define UMATCH_NONE (ENXIO)
+
+#endif
+
+#if defined(__FreeBSD__)
+int usbd_driver_load(module_t mod, int what, void *arg);
+#endif
+
+/* XXX Perhaps USB should have its own levels? */
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+#define splusb splsoftnet
+#else
+#define splusb splsoftclock
+#endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */
+#else
+#define splusb splbio
+#endif /* USB_USE_SOFTINTR */
+#define splhardusb splbio
+#define IPL_USB IPL_BIO
diff --git a/sys/dev/usb/usbdi_util.c b/sys/dev/usb/usbdi_util.c
new file mode 100644
index 0000000..8fdd1cc
--- /dev/null
+++ b/sys/dev/usb/usbdi_util.c
@@ -0,0 +1,508 @@
+/* $NetBSD: usbdi_util.c,v 1.36 2001/11/13 06:24:57 lukem Exp $ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/proc.h>
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/bus.h>
+#endif
+
+#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) logprintf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+usbd_status
+usbd_get_desc(usbd_device_handle dev, int type, int index, int len, void *desc)
+{
+ usb_device_request_t req;
+
+ DPRINTFN(3,("usbd_get_desc: type=%d, index=%d, len=%d\n",
+ type, index, len));
+
+ 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(usbd_device_handle dev, int confidx,
+ usb_config_descriptor_t *d)
+{
+ usbd_status err;
+
+ DPRINTFN(3,("usbd_get_config_desc: confidx=%d\n", confidx));
+ err = usbd_get_desc(dev, UDESC_CONFIG, confidx,
+ USB_CONFIG_DESCRIPTOR_SIZE, d);
+ if (err)
+ return (err);
+ if (d->bDescriptorType != UDESC_CONFIG) {
+ DPRINTFN(-1,("usbd_get_config_desc: confidx=%d, bad desc "
+ "len=%d type=%d\n",
+ confidx, d->bLength, d->bDescriptorType));
+ return (USBD_INVAL);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_config_desc_full(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(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(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(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(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(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_hub_feature(usbd_device_handle dev, int sel)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_set_hub_feature(usbd_device_handle dev, int sel)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_clear_port_feature(usbd_device_handle dev, int port, int 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(usbd_device_handle dev, int port, int 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(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;
+
+ DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n",
+ iface, report, id->bInterfaceNumber));
+ if (id == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ 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(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;
+
+ DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
+ if (ifd == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ 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(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;
+
+ DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len));
+ if (ifd == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ 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(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;
+
+ DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
+ if (id == 0)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ 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(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;
+
+ DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id));
+ if (ifd == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ 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(usbd_device_handle dev, int ifcno,
+ 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); /* report id should be 0 */
+ USETW(req.wIndex, ifcno);
+ USETW(req.wLength, size);
+ return (usbd_do_request(dev, &req, d));
+}
+
+usb_hid_descriptor_t *
+usbd_get_hid_descriptor(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;
+
+ if (idesc == NULL)
+ return (0);
+ usbd_interface2device_handle(ifc, &dev);
+ 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_read_report_desc(usbd_interface_handle ifc, void **descp, int *sizep,
+ usb_malloc_type mem)
+{
+ usb_interface_descriptor_t *id;
+ usb_hid_descriptor_t *hid;
+ usbd_device_handle dev;
+ usbd_status err;
+
+ usbd_interface2device_handle(ifc, &dev);
+ id = usbd_get_interface_descriptor(ifc);
+ if (id == NULL)
+ return (USBD_INVAL);
+ hid = usbd_get_hid_descriptor(ifc);
+ if (hid == NULL)
+ return (USBD_IOERROR);
+ *sizep = UGETW(hid->descrs[0].wDescriptorLength);
+ *descp = malloc(*sizep, mem, M_NOWAIT);
+ if (*descp == NULL)
+ return (USBD_NOMEM);
+ err = usbd_get_report_descriptor(dev, id->bInterfaceNumber,
+ *sizep, *descp);
+ if (err) {
+ free(*descp, mem);
+ *descp = NULL;
+ return (err);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_config(usbd_device_handle dev, u_int8_t *conf)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_CONFIG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ return (usbd_do_request(dev, &req, conf));
+}
+
+Static void usbd_bulk_transfer_cb(usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status status);
+Static void
+usbd_bulk_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ wakeup(xfer);
+}
+
+usbd_status
+usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ u_int16_t flags, u_int32_t timeout, void *buf,
+ u_int32_t *size, char *lbl)
+{
+ usbd_status err;
+ int s, error;
+
+ usbd_setup_xfer(xfer, pipe, 0, buf, *size,
+ flags, timeout, usbd_bulk_transfer_cb);
+ DPRINTFN(1, ("usbd_bulk_transfer: start transfer %d bytes\n", *size));
+ s = splusb(); /* don't want callback until tsleep() */
+ err = usbd_transfer(xfer);
+ if (err != USBD_IN_PROGRESS) {
+ splx(s);
+ return (err);
+ }
+ error = tsleep(xfer, PZERO | PCATCH, lbl, 0);
+ splx(s);
+ if (error) {
+ DPRINTF(("usbd_bulk_transfer: tsleep=%d\n", error));
+ usbd_abort_pipe(pipe);
+ return (USBD_INTERRUPTED);
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
+ DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size));
+ if (err) {
+ DPRINTF(("usbd_bulk_transfer: error=%d\n", err));
+ usbd_clear_endpoint_stall(pipe);
+ }
+ return (err);
+}
+
+Static void usbd_intr_transfer_cb(usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status status);
+Static void
+usbd_intr_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ wakeup(xfer);
+}
+
+usbd_status
+usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ u_int16_t flags, u_int32_t timeout, void *buf,
+ u_int32_t *size, char *lbl)
+{
+ usbd_status err;
+ int s, error;
+
+ usbd_setup_xfer(xfer, pipe, 0, buf, *size,
+ flags, timeout, usbd_intr_transfer_cb);
+ DPRINTFN(1, ("usbd_intr_transfer: start transfer %d bytes\n", *size));
+ s = splusb(); /* don't want callback until tsleep() */
+ err = usbd_transfer(xfer);
+ if (err != USBD_IN_PROGRESS) {
+ splx(s);
+ return (err);
+ }
+ error = tsleep(xfer, PZERO | PCATCH, lbl, 0);
+ splx(s);
+ if (error) {
+ DPRINTF(("usbd_intr_transfer: tsleep=%d\n", error));
+ usbd_abort_pipe(pipe);
+ return (USBD_INTERRUPTED);
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
+ DPRINTFN(1,("usbd_intr_transfer: transferred %d\n", *size));
+ if (err) {
+ DPRINTF(("usbd_intr_transfer: error=%d\n", err));
+ usbd_clear_endpoint_stall(pipe);
+ }
+ return (err);
+}
+
+void
+usb_detach_wait(device_ptr_t dv)
+{
+ DPRINTF(("usb_detach_wait: waiting for %s\n", USBDEVPTRNAME(dv)));
+ if (tsleep(dv, PZERO, "usbdet", hz * 60))
+ printf("usb_detach_wait: %s didn't detach\n",
+ USBDEVPTRNAME(dv));
+ DPRINTF(("usb_detach_wait: %s done\n", USBDEVPTRNAME(dv)));
+}
+
+void
+usb_detach_wakeup(device_ptr_t dv)
+{
+ DPRINTF(("usb_detach_wakeup: for %s\n", USBDEVPTRNAME(dv)));
+ wakeup(dv);
+}
diff --git a/sys/dev/usb/usbdi_util.h b/sys/dev/usb/usbdi_util.h
new file mode 100644
index 0000000..01cf425
--- /dev/null
+++ b/sys/dev/usb/usbdi_util.h
@@ -0,0 +1,89 @@
+/* $NetBSD: usbdi_util.h,v 1.23 2001/10/26 17:58:22 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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(usbd_device_handle dev, int type,
+ int index, int len, void *desc);
+usbd_status usbd_get_config_desc(usbd_device_handle, int,
+ usb_config_descriptor_t *);
+usbd_status usbd_get_config_desc_full(usbd_device_handle, int, void *, int);
+usbd_status usbd_get_device_desc(usbd_device_handle dev,
+ usb_device_descriptor_t *d);
+usbd_status usbd_set_address(usbd_device_handle dev, int addr);
+usbd_status usbd_get_port_status(usbd_device_handle,
+ int, usb_port_status_t *);
+usbd_status usbd_set_hub_feature(usbd_device_handle dev, int);
+usbd_status usbd_clear_hub_feature(usbd_device_handle, int);
+usbd_status usbd_set_port_feature(usbd_device_handle dev, int, int);
+usbd_status usbd_clear_port_feature(usbd_device_handle, int, int);
+usbd_status usbd_get_device_status(usbd_device_handle, usb_status_t *);
+usbd_status usbd_get_hub_status(usbd_device_handle, usb_hub_status_t *);
+usbd_status usbd_set_protocol(usbd_interface_handle dev, int report);
+usbd_status usbd_get_report_descriptor(usbd_device_handle dev, int ifcno,
+ int size, void *d);
+struct usb_hid_descriptor *usbd_get_hid_descriptor(usbd_interface_handle ifc);
+usbd_status usbd_set_report(usbd_interface_handle iface, int type, int id,
+ void *data,int len);
+usbd_status usbd_set_report_async(usbd_interface_handle iface, int type,
+ int id, void *data, int len);
+usbd_status usbd_get_report(usbd_interface_handle iface, int type, int id,
+ void *data, int len);
+usbd_status usbd_set_idle(usbd_interface_handle iface, int duration, int id);
+usbd_status usbd_read_report_desc(usbd_interface_handle ifc, void **descp,
+ int *sizep, usb_malloc_type mem);
+usbd_status usbd_get_config(usbd_device_handle dev, u_int8_t *conf);
+usbd_status usbd_get_string_desc(usbd_device_handle dev, int sindex,
+ int langid, usb_string_descriptor_t *sdesc);
+void usbd_delay_ms(usbd_device_handle, u_int);
+
+
+usbd_status usbd_set_config_no(usbd_device_handle dev, int no, int msg);
+usbd_status usbd_set_config_index(usbd_device_handle dev, int index, int msg);
+
+usbd_status usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ u_int16_t flags, u_int32_t timeout, void *buf,
+ u_int32_t *size, char *lbl);
+
+usbd_status usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ u_int16_t flags, u_int32_t timeout, void *buf,
+ u_int32_t *size, char *lbl);
+
+void usb_detach_wait(device_ptr_t);
+void usb_detach_wakeup(device_ptr_t);
+
diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h
new file mode 100644
index 0000000..ae2c092
--- /dev/null
+++ b/sys/dev/usb/usbdivar.h
@@ -0,0 +1,314 @@
+/* $NetBSD: usbdivar.h,v 1.70 2002/07/11 21:14:36 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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__)
+#include <sys/callout.h>
+#endif
+
+/* From usb_mem.h */
+DECLARE_USB_DMA_T;
+
+struct usbd_xfer;
+struct usbd_pipe;
+
+struct usbd_endpoint {
+ usb_endpoint_descriptor_t *edesc;
+ int refcnt;
+};
+
+struct usbd_bus_methods {
+ usbd_status (*open_pipe)(struct usbd_pipe *pipe);
+ void (*soft_intr)(void *);
+ void (*do_poll)(struct usbd_bus *);
+ usbd_status (*allocm)(struct usbd_bus *, usb_dma_t *,
+ u_int32_t bufsize);
+ void (*freem)(struct usbd_bus *, usb_dma_t *);
+ struct usbd_xfer * (*allocx)(struct usbd_bus *);
+ void (*freex)(struct usbd_bus *, struct usbd_xfer *);
+};
+
+struct usbd_pipe_methods {
+ usbd_status (*transfer)(usbd_xfer_handle xfer);
+ usbd_status (*start)(usbd_xfer_handle xfer);
+ void (*abort)(usbd_xfer_handle xfer);
+ void (*close)(usbd_pipe_handle pipe);
+ void (*cleartoggle)(usbd_pipe_handle pipe);
+ void (*done)(usbd_xfer_handle xfer);
+};
+
+struct usbd_port {
+ usb_port_status_t status;
+ u_int16_t power; /* mA of current on port */
+ u_int8_t portno;
+ u_int8_t restartcnt;
+#define USBD_RESTART_MAX 5
+ struct usbd_device *device; /* Connected device */
+ struct usbd_device *parent; /* The ports hub */
+};
+
+struct usbd_hub {
+ usbd_status (*explore)(usbd_device_handle hub);
+ void *hubsoftc;
+ usb_hub_descriptor_t hubdesc;
+ struct usbd_port ports[1];
+};
+
+struct usb_softc;
+
+/*****/
+
+struct usbd_bus {
+ /* Filled by HC driver */
+ USBBASEDEVICE bdev; /* base device, host adapter */
+ struct usbd_bus_methods *methods;
+ u_int32_t pipe_size; /* size of a pipe struct */
+ /* 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;
+ int intr_context;
+ u_int no_intrs;
+ int usbrev; /* USB revision */
+#define USBREV_UNKNOWN 0
+#define USBREV_PRE_1_0 1
+#define USBREV_1_0 2
+#define USBREV_1_1 3
+#define USBREV_2_0 4
+#define USBREV_STR { "unknown", "pre 1.0", "1.0", "1.1", "2.0" }
+
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ void *soft; /* soft interrupt cookie */
+#else
+ struct callout softi;
+#endif
+#endif
+
+ bus_dma_tag_t dmatag; /* DMA tag */
+};
+
+struct usbd_device {
+ struct usbd_bus *bus; /* our controller */
+ struct usbd_pipe *default_pipe; /* pipe 0 */
+ u_int8_t address; /* device addess */
+ u_int8_t config; /* current configuration # */
+ u_int8_t depth; /* distance from root hub */
+ u_int8_t speed; /* low/full/high speed */
+ u_int8_t self_powered; /* flag for self powered */
+ u_int16_t power; /* mA the device uses */
+ int16_t langid; /* language for strings */
+#define USBD_NOLANG (-1)
+ usb_event_cookie_t cookie; /* unique connection id */
+ struct usbd_port *powersrc; /* upstream hub port, or 0 */
+ struct usbd_device *myhub; /* upstream hub */
+ struct usbd_device *myhighhub; /* closest high speed hub */
+ struct usbd_endpoint def_ep; /* for pipe 0 */
+ usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */
+ struct usbd_interface *ifaces; /* array of all interfaces */
+ usb_device_descriptor_t ddesc; /* device descriptor */
+ usb_config_descriptor_t *cdesc; /* full config descr */
+ const struct usbd_quirks *quirks; /* device quirks, always set */
+ struct usbd_hub *hub; /* only if this is a hub */
+ device_ptr_t *subdevs; /* sub-devices, 0 terminated */
+};
+
+struct usbd_interface {
+ struct usbd_device *device;
+ usb_interface_descriptor_t *idesc;
+ int index;
+ int altindex;
+ 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;
+ int refcnt;
+ char running;
+ char aborting;
+ SIMPLEQ_HEAD(, usbd_xfer) queue;
+ LIST_ENTRY(usbd_pipe) next;
+
+ usbd_xfer_handle intrxfer; /* used for repeating requests */
+ char repeat;
+ int interval;
+
+ /* Filled by HC driver. */
+ struct usbd_pipe_methods *methods;
+};
+
+struct usbd_xfer {
+ 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;
+ __volatile char done;
+#ifdef DIAGNOSTIC
+ u_int32_t busy_free;
+#define XFER_FREE 0x46524545
+#define XFER_BUSY 0x42555359
+#define XFER_ONQU 0x4f4e5155
+#endif
+
+ /* For control pipe */
+ usb_device_request_t request;
+
+ /* For isoc */
+ u_int16_t *frlengths;
+ int nframes;
+
+ /* For memory allocation */
+ struct usbd_device *device;
+ usb_dma_t dmabuf;
+
+ int rqflags;
+#define URQ_REQUEST 0x01
+#define URQ_AUTO_DMABUF 0x10
+#define URQ_DEV_DMABUF 0x20
+
+ SIMPLEQ_ENTRY(usbd_xfer) next;
+
+ void *hcpriv; /* private use by the HC driver */
+
+ usb_callout_t timeout_handle;
+};
+
+void usbd_init(void);
+void usbd_finish(void);
+
+#ifdef USB_DEBUG
+void usbd_dump_iface(struct usbd_interface *iface);
+void usbd_dump_device(struct usbd_device *dev);
+void usbd_dump_endpoint(struct usbd_endpoint *endp);
+void usbd_dump_queue(usbd_pipe_handle pipe);
+void usbd_dump_pipe(usbd_pipe_handle pipe);
+#endif
+
+/* Routines from usb_subr.c */
+int usbctlprint(void *, const char *);
+void usb_delay_ms(usbd_bus_handle, u_int);
+usbd_status usbd_reset_port(usbd_device_handle dev,
+ int port, usb_port_status_t *ps);
+usbd_status usbd_setup_pipe(usbd_device_handle dev,
+ usbd_interface_handle iface,
+ struct usbd_endpoint *, int,
+ usbd_pipe_handle *pipe);
+usbd_status usbd_new_device(device_ptr_t parent,
+ usbd_bus_handle bus, int depth,
+ int lowspeed, int port,
+ struct usbd_port *);
+void usbd_remove_device(usbd_device_handle, struct usbd_port *);
+int usbd_printBCD(char *cp, int bcd);
+usbd_status usbd_fill_iface_data(usbd_device_handle dev, int i, int a);
+void usb_free_device(usbd_device_handle);
+
+usbd_status usb_insert_transfer(usbd_xfer_handle xfer);
+void usb_transfer_complete(usbd_xfer_handle xfer);
+void usb_disconnect_port(struct usbd_port *up, device_ptr_t);
+
+/* Routines from usb.c */
+void usb_needs_explore(usbd_device_handle);
+void usb_schedsoftintr(struct usbd_bus *);
+
+/*
+ * XXX This check is extremely bogus. Bad Bad Bad.
+ */
+#if defined(DIAGNOSTIC) && 0
+#define SPLUSBCHECK \
+ do { int _s = splusb(), _su = splusb(); \
+ if (!cold && _s != _su) printf("SPLUSBCHECK failed 0x%x!=0x%x, %s:%d\n", \
+ _s, _su, __FILE__, __LINE__); \
+ splx(_s); \
+ } while (0)
+#else
+#define SPLUSBCHECK
+#endif
+
+/* Locator stuff. */
+
+#if defined(__NetBSD__)
+#include "locators.h"
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+/* XXX these values are used to statically bind some elements in the USB tree
+ * to specific driver instances. This should be somehow emulated in FreeBSD
+ * but can be done later on.
+ * The values are copied from the files.usb file in the NetBSD sources.
+ */
+#define UHUBCF_PORT_DEFAULT -1
+#define UHUBCF_CONFIGURATION_DEFAULT -1
+#define UHUBCF_INTERFACE_DEFAULT -1
+#define UHUBCF_VENDOR_DEFAULT -1
+#define UHUBCF_PRODUCT_DEFAULT -1
+#define UHUBCF_RELEASE_DEFAULT -1
+#endif
+
+#if defined (__OpenBSD__)
+#define UHUBCF_PORT 0
+#define UHUBCF_CONFIGURATION 1
+#define UHUBCF_INTERFACE 2
+#define UHUBCF_VENDOR 3
+#define UHUBCF_PRODUCT 4
+#define UHUBCF_RELEASE 5
+#endif
+
+#define uhubcf_port cf_loc[UHUBCF_PORT]
+#define uhubcf_configuration cf_loc[UHUBCF_CONFIGURATION]
+#define uhubcf_interface cf_loc[UHUBCF_INTERFACE]
+#define uhubcf_vendor cf_loc[UHUBCF_VENDOR]
+#define uhubcf_product cf_loc[UHUBCF_PRODUCT]
+#define uhubcf_release cf_loc[UHUBCF_RELEASE]
+#define UHUB_UNK_PORT UHUBCF_PORT_DEFAULT /* wildcarded 'port' */
+#define UHUB_UNK_CONFIGURATION UHUBCF_CONFIGURATION_DEFAULT /* wildcarded 'configuration' */
+#define UHUB_UNK_INTERFACE UHUBCF_INTERFACE_DEFAULT /* wildcarded 'interface' */
+#define UHUB_UNK_VENDOR UHUBCF_VENDOR_DEFAULT /* wildcarded 'vendor' */
+#define UHUB_UNK_PRODUCT UHUBCF_PRODUCT_DEFAULT /* wildcarded 'product' */
+#define UHUB_UNK_RELEASE UHUBCF_RELEASE_DEFAULT /* wildcarded 'release' */
+
diff --git a/sys/dev/usb/usbhid.h b/sys/dev/usb/usbhid.h
new file mode 100644
index 0000000..fbc1704
--- /dev/null
+++ b/sys/dev/usb/usbhid.h
@@ -0,0 +1,184 @@
+/* $NetBSD: usbhid.h,v 1.9 2000/09/03 19:09:14 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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];
+} UPACKED usb_hid_descriptor_t;
+#define USB_HID_DESCRIPTOR_SIZE(n) (9+(n)*3)
+
+/* Usage pages */
+#define HUP_UNDEFINED 0x0000
+#define HUP_GENERIC_DESKTOP 0x0001
+#define HUP_SIMULATION 0x0002
+#define HUP_VR_CONTROLS 0x0003
+#define HUP_SPORTS_CONTROLS 0x0004
+#define HUP_GAMING_CONTROLS 0x0005
+#define HUP_KEYBOARD 0x0007
+#define HUP_LEDS 0x0008
+#define HUP_BUTTON 0x0009
+#define HUP_ORDINALS 0x000a
+#define HUP_TELEPHONY 0x000b
+#define HUP_CONSUMER 0x000c
+#define HUP_DIGITIZERS 0x000d
+#define HUP_PHYSICAL_IFACE 0x000e
+#define HUP_UNICODE 0x0010
+#define HUP_ALPHANUM_DISPLAY 0x0014
+#define HUP_MONITOR 0x0080
+#define HUP_MONITOR_ENUM_VAL 0x0081
+#define HUP_VESA_VC 0x0082
+#define HUP_VESA_CMD 0x0083
+#define HUP_POWER 0x0084
+#define HUP_BATTERY_SYSTEM 0x0085
+#define HUP_BARCODE_SCANNER 0x008b
+#define HUP_SCALE 0x008c
+#define HUP_CAMERA_CONTROL 0x0090
+#define HUP_ARCADE 0x0091
+#define HUP_MICROSOFT 0xff00
+
+/* 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
+
+/* Usages Digitizers */
+#define HUD_UNDEFINED 0x0000
+#define HUD_TIP_PRESSURE 0x0030
+#define HUD_BARREL_PRESSURE 0x0031
+#define HUD_IN_RANGE 0x0032
+#define HUD_TOUCH 0x0033
+#define HUD_UNTOUCH 0x0034
+#define HUD_TAP 0x0035
+#define HUD_QUALITY 0x0036
+#define HUD_DATA_VALID 0x0037
+#define HUD_TRANSDUCER_INDEX 0x0038
+#define HUD_TABLET_FKEYS 0x0039
+#define HUD_PROGRAM_CHANGE_KEYS 0x003a
+#define HUD_BATTERY_STRENGTH 0x003b
+#define HUD_INVERT 0x003c
+#define HUD_X_TILT 0x003d
+#define HUD_Y_TILT 0x003e
+#define HUD_AZIMUTH 0x003f
+#define HUD_ALTITUDE 0x0040
+#define HUD_TWIST 0x0041
+#define HUD_TIP_SWITCH 0x0042
+#define HUD_SEC_TIP_SWITCH 0x0043
+#define HUD_BARREL_SWITCH 0x0044
+#define HUD_ERASER 0x0045
+#define HUD_TABLET_PICK 0x0046
+
+#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/dev/usb/uscanner.c b/sys/dev/usb/uscanner.c
new file mode 100644
index 0000000..e085c69
--- /dev/null
+++ b/sys/dev/usb/uscanner.c
@@ -0,0 +1,700 @@
+/* $NetBSD: uscanner.c,v 1.30 2002/07/11 21:14:36 augustss Exp$ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uscanner.c,v 1.33 2002/09/23 05:51:24 simonb Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology
+ * and Nick Hibma (n_hibma@qubesoft.com).
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#endif
+#include <sys/tty.h>
+#include <sys/file.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/conf.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include <dev/usb/usbdevs.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uscannerdebug) logprintf x
+#define DPRINTFN(n,x) if (uscannerdebug>(n)) logprintf x
+int uscannerdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uscanner, CTLFLAG_RW, 0, "USB uscanner");
+SYSCTL_INT(_hw_usb_uscanner, OID_AUTO, debug, CTLFLAG_RW,
+ &uscannerdebug, 0, "uscanner debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct uscan_info {
+ struct usb_devno devno;
+ u_int flags;
+#define USC_KEEP_OPEN 1
+};
+
+/* Table of scanners that may work with this driver. */
+static const struct uscan_info uscanner_devs[] = {
+ /* Acer Peripherals */
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U }, 0 },
+
+ /* AGFA */
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52 }, 0 },
+
+ /* Avision */
+ {{ USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U }, 0 },
+
+ /* Canon */
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U }, 0 },
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U }, 0 },
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U }, 0 },
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U }, 0 },
+
+ /* Kye */
+ {{ USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO }, 0 },
+
+ /* HP */
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_2200C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_3300C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_4100C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_4200C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_4300C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_S20 }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_5200C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_5300C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_5400C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_6200C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_6300C }, 0 },
+
+ /* Microtek */
+ {{ USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2 }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6 }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2 }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL }, 0 },
+
+ /* Minolta */
+ {{ USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400 }, 0 },
+
+ /* Mustek */
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS }, 0 },
+
+ /* National */
+ {{ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200 }, 0 },
+ {{ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400 }, 0 },
+
+ /* Primax */
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600 }, 0 },
+
+ /* Epson */
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200 }, USC_KEEP_OPEN },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F }, USC_KEEP_OPEN },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF }, 0 },
+
+ /* UMAX */
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400 }, 0 },
+
+ /* Visioneer */
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600 }, 0 },
+
+ /* Ultima */
+ {{ USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS }, 0 },
+
+};
+#define uscanner_lookup(v, p) ((const struct uscan_info *)usb_lookup(uscanner_devs, v, p))
+
+#define USCANNER_BUFFERSIZE 1024
+
+struct uscanner_softc {
+ USBBASEDEVICE sc_dev; /* base device */
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+#if defined(__FreeBSD__)
+ struct cdev *dev;
+#endif
+
+ u_int sc_dev_flags;
+
+ usbd_pipe_handle sc_bulkin_pipe;
+ int sc_bulkin;
+ usbd_xfer_handle sc_bulkin_xfer;
+ void *sc_bulkin_buffer;
+ int sc_bulkin_bufferlen;
+ int sc_bulkin_datalen;
+
+ usbd_pipe_handle sc_bulkout_pipe;
+ int sc_bulkout;
+ usbd_xfer_handle sc_bulkout_xfer;
+ void *sc_bulkout_buffer;
+ int sc_bulkout_bufferlen;
+ int sc_bulkout_datalen;
+
+ u_char sc_state;
+#define USCANNER_OPEN 0x01 /* opened */
+
+ int sc_refcnt;
+ u_char sc_dying;
+};
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+cdev_decl(uscanner);
+#elif defined(__FreeBSD__)
+d_open_t uscanneropen;
+d_close_t uscannerclose;
+d_read_t uscannerread;
+d_write_t uscannerwrite;
+d_poll_t uscannerpoll;
+
+
+Static struct cdevsw uscanner_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = uscanneropen,
+ .d_close = uscannerclose,
+ .d_read = uscannerread,
+ .d_write = uscannerwrite,
+ .d_poll = uscannerpoll,
+ .d_name = "uscanner",
+#if __FreeBSD_version < 500014
+ .d_bmaj -1
+#endif
+};
+#endif
+
+Static int uscanner_do_read(struct uscanner_softc *, struct uio *, int);
+Static int uscanner_do_write(struct uscanner_softc *, struct uio *, int);
+Static void uscanner_do_close(struct uscanner_softc *);
+
+#define USCANNERUNIT(n) (minor(n))
+
+USB_DECLARE_DRIVER(uscanner);
+
+USB_MATCH(uscanner)
+{
+ USB_MATCH_START(uscanner, uaa);
+
+ if (uaa->iface != NULL)
+ return UMATCH_NONE;
+
+ return (uscanner_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+USB_ATTACH(uscanner)
+{
+ USB_ATTACH_START(uscanner, sc, uaa);
+ usb_interface_descriptor_t *id = 0;
+ usb_endpoint_descriptor_t *ed, *ed_bulkin = NULL, *ed_bulkout = NULL;
+ char devinfo[1024];
+ int i;
+ usbd_status err;
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ sc->sc_dev_flags = uscanner_lookup(uaa->vendor, uaa->product)->flags;
+
+ sc->sc_udev = uaa->device;
+
+ err = usbd_set_config_no(uaa->device, 1, 1); /* XXX */
+ if (err) {
+ printf("%s: setting config no failed\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* XXX We only check the first interface */
+ err = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface);
+ if (!err && sc->sc_iface)
+ id = usbd_get_interface_descriptor(sc->sc_iface);
+ if (err || id == 0) {
+ printf("%s: could not get interface descriptor, err=%d,id=%p\n",
+ USBDEVNAME(sc->sc_dev), err, id);
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Find the two first bulk endpoints */
+ for (i = 0 ; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == 0) {
+ printf("%s: could not read endpoint descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ed_bulkin = ed;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ed_bulkout = ed;
+ }
+
+ if (ed_bulkin && ed_bulkout) /* found all we need */
+ break;
+ }
+
+ /* Verify that we goething sensible */
+ if (ed_bulkin == NULL || ed_bulkout == NULL) {
+ printf("%s: bulk-in and/or bulk-out endpoint not found\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_bulkin = ed_bulkin->bEndpointAddress;
+ sc->sc_bulkout = ed_bulkout->bEndpointAddress;
+
+#ifdef __FreeBSD__
+ /* the main device, ctrl endpoint */
+ sc->dev = make_dev(&uscanner_cdevsw, USBDEVUNIT(sc->sc_dev),
+ UID_ROOT, GID_OPERATOR, 0644, "%s", USBDEVNAME(sc->sc_dev));
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+int
+uscanneropen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct uscanner_softc *sc;
+ int unit = USCANNERUNIT(dev);
+ usbd_status err;
+
+ USB_GET_SC_OPEN(uscanner, unit, sc);
+
+ DPRINTFN(5, ("uscanneropen: flag=%d, mode=%d, unit=%d\n",
+ flag, mode, unit));
+
+ if (sc->sc_dying)
+ return (ENXIO);
+
+ if (sc->sc_state & USCANNER_OPEN)
+ return (EBUSY);
+
+ sc->sc_state |= USCANNER_OPEN;
+
+ sc->sc_bulkin_buffer = malloc(USCANNER_BUFFERSIZE, M_USBDEV, M_WAITOK);
+ sc->sc_bulkout_buffer = malloc(USCANNER_BUFFERSIZE, M_USBDEV, M_WAITOK);
+ /* No need to check buffers for NULL since we have WAITOK */
+
+ sc->sc_bulkin_bufferlen = USCANNER_BUFFERSIZE;
+ sc->sc_bulkout_bufferlen = USCANNER_BUFFERSIZE;
+
+ /* We have decided on which endpoints to use, now open the pipes */
+ if (sc->sc_bulkin_pipe == NULL) {
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
+ if (err) {
+ printf("%s: cannot open bulk-in pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkin);
+ uscanner_do_close(sc);
+ return (EIO);
+ }
+ }
+ if (sc->sc_bulkout_pipe == NULL) {
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ printf("%s: cannot open bulk-out pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkout);
+ uscanner_do_close(sc);
+ return (EIO);
+ }
+ }
+
+ sc->sc_bulkin_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulkin_xfer == NULL) {
+ uscanner_do_close(sc);
+ return (ENOMEM);
+ }
+ sc->sc_bulkout_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulkout_xfer == NULL) {
+ uscanner_do_close(sc);
+ return (ENOMEM);
+ }
+
+ return (0); /* success */
+}
+
+int
+uscannerclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
+{
+ struct uscanner_softc *sc;
+
+ USB_GET_SC(uscanner, USCANNERUNIT(dev), sc);
+
+ DPRINTFN(5, ("uscannerclose: flag=%d, mode=%d, unit=%d\n",
+ flag, mode, USCANNERUNIT(dev)));
+
+#ifdef DIAGNOSTIC
+ if (!(sc->sc_state & USCANNER_OPEN)) {
+ printf("uscannerclose: not open\n");
+ return (EINVAL);
+ }
+#endif
+
+ uscanner_do_close(sc);
+
+ return (0);
+}
+
+void
+uscanner_do_close(struct uscanner_softc *sc)
+{
+ if (sc->sc_bulkin_xfer) {
+ usbd_free_xfer(sc->sc_bulkin_xfer);
+ sc->sc_bulkin_xfer = NULL;
+ }
+ if (sc->sc_bulkout_xfer) {
+ usbd_free_xfer(sc->sc_bulkout_xfer);
+ sc->sc_bulkout_xfer = NULL;
+ }
+
+ if (!(sc->sc_dev_flags & USC_KEEP_OPEN)) {
+ if (sc->sc_bulkin_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+ }
+ if (sc->sc_bulkout_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+ }
+ }
+
+ if (sc->sc_bulkin_buffer) {
+ free(sc->sc_bulkin_buffer, M_USBDEV);
+ sc->sc_bulkin_buffer = NULL;
+ }
+ if (sc->sc_bulkout_buffer) {
+ free(sc->sc_bulkout_buffer, M_USBDEV);
+ sc->sc_bulkout_buffer = NULL;
+ }
+
+ sc->sc_state &= ~USCANNER_OPEN;
+}
+
+Static int
+uscanner_do_read(struct uscanner_softc *sc, struct uio *uio, int flag)
+{
+ u_int32_t n, tn;
+ usbd_status err;
+ int error = 0;
+
+ DPRINTFN(5, ("%s: uscannerread\n", USBDEVNAME(sc->sc_dev)));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ while ((n = min(sc->sc_bulkin_bufferlen, uio->uio_resid)) != 0) {
+ DPRINTFN(1, ("uscannerread: start transfer %d bytes\n",n));
+ tn = n;
+
+ err = usbd_bulk_transfer(
+ sc->sc_bulkin_xfer, sc->sc_bulkin_pipe,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
+ sc->sc_bulkin_buffer, &tn,
+ "uscnrb");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else if (err == USBD_TIMEOUT)
+ error = ETIMEDOUT;
+ else
+ error = EIO;
+ break;
+ }
+ DPRINTFN(1, ("uscannerread: got %d bytes\n", tn));
+ error = uiomove(sc->sc_bulkin_buffer, tn, uio);
+ if (error || tn < n)
+ break;
+ }
+
+ return (error);
+}
+
+int
+uscannerread(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct uscanner_softc *sc;
+ int error;
+
+ USB_GET_SC(uscanner, USCANNERUNIT(dev), sc);
+
+ sc->sc_refcnt++;
+ error = uscanner_do_read(sc, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+ return (error);
+}
+
+Static int
+uscanner_do_write(struct uscanner_softc *sc, struct uio *uio, int flag)
+{
+ u_int32_t n;
+ int error = 0;
+ usbd_status err;
+
+ DPRINTFN(5, ("%s: uscanner_do_write\n", USBDEVNAME(sc->sc_dev)));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ while ((n = min(sc->sc_bulkout_bufferlen, uio->uio_resid)) != 0) {
+ error = uiomove(sc->sc_bulkout_buffer, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("uscanner_do_write: transfer %d bytes\n", n));
+ err = usbd_bulk_transfer(
+ sc->sc_bulkout_xfer, sc->sc_bulkout_pipe,
+ 0, USBD_NO_TIMEOUT,
+ sc->sc_bulkout_buffer, &n,
+ "uscnwb");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else
+ error = EIO;
+ break;
+ }
+ }
+
+ return (error);
+}
+
+int
+uscannerwrite(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct uscanner_softc *sc;
+ int error;
+
+ USB_GET_SC(uscanner, USCANNERUNIT(dev), sc);
+
+ sc->sc_refcnt++;
+ error = uscanner_do_write(sc, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return (error);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+uscanner_activate(device_ptr_t self, enum devact act)
+{
+ struct uscanner_softc *sc = (struct uscanner_softc *)self;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+#endif
+
+USB_DETACH(uscanner)
+{
+ USB_DETACH_START(uscanner, sc);
+ int s;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ int maj, mn;
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ DPRINTF(("uscanner_detach: sc=%p flags=%d\n", sc, flags));
+#elif defined(__FreeBSD__)
+ DPRINTF(("uscanner_detach: sc=%p\n", sc));
+#endif
+
+ sc->sc_dying = 1;
+ sc->sc_dev_flags = 0; /* make close really close device */
+
+ /* Abort all pipes. Causes processes waiting for transfer to wake. */
+ if (sc->sc_bulkin_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ if (sc->sc_bulkout_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away. */
+ usb_detach_wait(USBDEV(sc->sc_dev));
+ }
+ splx(s);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == uscanneropen)
+ break;
+
+ /* Nuke the vnodes for any open instances (calls close). */
+ mn = self->dv_unit * USB_MAX_ENDPOINTS;
+ vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
+#elif defined(__FreeBSD__)
+ /* destroy the device for the control endpoint */
+ destroy_dev(sc->dev);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ return (0);
+}
+
+int
+uscannerpoll(struct cdev *dev, int events, usb_proc_ptr p)
+{
+ struct uscanner_softc *sc;
+ int revents = 0;
+
+ USB_GET_SC(uscanner, USCANNERUNIT(dev), sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ /*
+ * We have no easy way of determining if a read will
+ * yield any data or a write will happen.
+ * Pretend they will.
+ */
+ revents |= events &
+ (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM);
+
+ return (revents);
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(uscanner, uhub, uscanner_driver, uscanner_devclass, usbd_driver_load, 0);
+#endif
diff --git a/sys/dev/usb/uvisor.c b/sys/dev/usb/uvisor.c
new file mode 100644
index 0000000..66f5fc1
--- /dev/null
+++ b/sys/dev/usb/uvisor.c
@@ -0,0 +1,588 @@
+/* $NetBSD: uvisor.c,v 1.9 2001/01/23 14:04:14 augustss Exp $ */
+/* $FreeBSD$ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uvisor.c,v 1.12 2001/11/13 06:24:57 lukem Exp $
+ * $NetBSD: uvisor.c,v 1.13 2002/02/11 15:11:49 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.14 2002/02/27 23:00:03 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.15 2002/06/16 15:01:31 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.16 2002/07/11 21:14:36 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.17 2002/08/13 11:38:15 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.18 2003/02/05 00:50:14 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.19 2003/02/07 18:12:37 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.20 2003/04/11 01:30:10 simonb Exp $
+ */
+
+
+/*
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * 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.
+ */
+
+/*
+ * Handspring Visor (Palmpilot compatible PDA) driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#endif
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/sysctl.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/usbdevs.h>
+
+#include <dev/usb/ucomvar.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uvisordebug) printf x
+#define DPRINTFN(n,x) if (uvisordebug>(n)) printf x
+int uvisordebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor");
+SYSCTL_INT(_hw_usb_uvisor, OID_AUTO, debug, CTLFLAG_RW,
+ &uvisordebug, 0, "uvisor debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UVISOR_CONFIG_INDEX 0
+#define UVISOR_IFACE_INDEX 0
+#define UVISOR_MODVER 1
+
+/* From the Linux driver */
+/*
+ * UVISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transfered to the host for the specified endpoint.
+ * Currently this is not used, and always returns 0x0001
+ */
+#define UVISOR_REQUEST_BYTES_AVAILABLE 0x01
+
+/*
+ * UVISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
+ * is now closing the pipe. An empty packet is sent in response.
+ */
+#define UVISOR_CLOSE_NOTIFICATION 0x02
+
+/*
+ * UVISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
+ * get the endpoints used by the connection.
+ */
+#define UVISOR_GET_CONNECTION_INFORMATION 0x03
+
+
+/*
+ * UVISOR_GET_CONNECTION_INFORMATION returns data in the following format
+ */
+#define UVISOR_MAX_CONN 8
+struct uvisor_connection_info {
+ uWord num_ports;
+ struct {
+ uByte port_function_id;
+ uByte port;
+ } connections[UVISOR_MAX_CONN];
+};
+#define UVISOR_CONNECTION_INFO_SIZE 18
+
+/* struct uvisor_connection_info.connection[x].port defines: */
+#define UVISOR_ENDPOINT_1 0x01
+#define UVISOR_ENDPOINT_2 0x02
+
+/* struct uvisor_connection_info.connection[x].port_function_id defines: */
+#define UVISOR_FUNCTION_GENERIC 0x00
+#define UVISOR_FUNCTION_DEBUGGER 0x01
+#define UVISOR_FUNCTION_HOTSYNC 0x02
+#define UVISOR_FUNCTION_CONSOLE 0x03
+#define UVISOR_FUNCTION_REMOTE_FILE_SYS 0x04
+
+/*
+ * Unknown PalmOS stuff.
+ */
+#define UVISOR_GET_PALM_INFORMATION 0x04
+#define UVISOR_GET_PALM_INFORMATION_LEN 0x14
+
+
+/*
+ * Crank down UVISORBUFSIZE from 1024 to 64 to avoid a problem where
+ * the Palm device and the USB host controller deadlock. The USB host
+ * controller is expecting an early-end-of-transmission packet with 0
+ * data, and the Palm doesn't send one because it's already
+ * communicated the amount of data it's going to send in a header
+ * (which ucom/uvisor are oblivious to). This is the problem that has
+ * been known on the pilot-link lists as the "[Free]BSD USB problem",
+ * but not understood.
+ */
+#define UVISORIBUFSIZE 64
+#define UVISOROBUFSIZE 1024
+
+struct uvisor_softc {
+ struct ucom_softc sc_ucom;
+ u_int16_t sc_flags;
+};
+
+Static usbd_status uvisor_init(struct uvisor_softc *);
+
+Static usbd_status clie_3_5_init(struct uvisor_softc *);
+
+Static void uvisor_close(void *, int);
+
+struct ucom_callback uvisor_callback = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ uvisor_close,
+ NULL,
+ NULL,
+};
+
+Static device_probe_t uvisor_match;
+Static device_attach_t uvisor_attach;
+Static device_detach_t uvisor_detach;
+Static device_method_t uvisor_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uvisor_match),
+ DEVMETHOD(device_attach, uvisor_attach),
+ DEVMETHOD(device_detach, uvisor_detach),
+ { 0, 0 }
+ };
+
+
+Static driver_t uvisor_driver = {
+ "ucom",
+ uvisor_methods,
+ sizeof (struct uvisor_softc)
+};
+
+DRIVER_MODULE(uvisor, uhub, uvisor_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uvisor, usb, 1, 1, 1);
+MODULE_DEPEND(uvisor, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(uvisor, UVISOR_MODVER);
+
+struct uvisor_type {
+ struct usb_devno uv_dev;
+ u_int16_t uv_flags;
+#define PALM4 0x0001
+};
+static const struct uvisor_type uvisor_devs[] = {
+ {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR }, 0 },
+ {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO }, PALM4 },
+ {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M500 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M505 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M515 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_I705 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M125 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M130 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31 }, PALM4 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40 }, 0 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41 }, PALM4 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360 }, PALM4 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60 }, PALM4 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35 }, 0 },
+/* {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_25 }, PALM4 },*/
+};
+#define uvisor_lookup(v, p) ((const struct uvisor_type *)usb_lookup(uvisor_devs, v, p))
+
+
+USB_MATCH(uvisor)
+{
+ USB_MATCH_START(uvisor, uaa);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ DPRINTFN(20,("uvisor: vendor=0x%x, product=0x%x\n",
+ uaa->vendor, uaa->product));
+
+ return (uvisor_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+USB_ATTACH(uvisor)
+{
+ USB_ATTACH_START(uvisor, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ char *devinfo;
+ const char *devname;
+ int i;
+ usbd_status err;
+ struct ucom_softc *ucom;
+
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+ ucom = &sc->sc_ucom;
+
+ bzero(sc, sizeof (struct uvisor_softc));
+ usbd_devinfo(dev, 0, devinfo);
+
+ ucom->sc_dev = self;
+ device_set_desc_copy(self, devinfo);
+
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ devname = USBDEVNAME(ucom->sc_dev);
+ printf("%s: %s\n", devname, devinfo);
+
+ DPRINTFN(10,("\nuvisor_attach: sc=%p\n", sc));
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UVISOR_CONFIG_INDEX, 1);
+ if (err) {
+ printf("\n%s: failed to set configuration, err=%s\n",
+ devname, usbd_errstr(err));
+ goto bad;
+ }
+
+ err = usbd_device2interface_handle(dev, UVISOR_IFACE_INDEX, &iface);
+ if (err) {
+ printf("\n%s: failed to get interface, err=%s\n",
+ devname, usbd_errstr(err));
+ goto bad;
+ }
+
+ printf("%s: %s\n", devname, devinfo);
+
+ sc->sc_flags = uvisor_lookup(uaa->vendor, uaa->product)->uv_flags;
+
+ id = usbd_get_interface_descriptor(iface);
+
+ ucom->sc_udev = dev;
+ ucom->sc_iface = iface;
+
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ int addr, dir, attr;
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ printf("%s: could not read endpoint descriptor"
+ ": %s\n", devname, usbd_errstr(err));
+ goto bad;
+ }
+
+ addr = ed->bEndpointAddress;
+ dir = UE_GET_DIR(ed->bEndpointAddress);
+ attr = ed->bmAttributes & UE_XFERTYPE;
+ if (dir == UE_DIR_IN && attr == UE_BULK)
+ ucom->sc_bulkin_no = addr;
+ else if (dir == UE_DIR_OUT && attr == UE_BULK)
+ ucom->sc_bulkout_no = addr;
+ else {
+ printf("%s: unexpected endpoint\n", devname);
+ goto bad;
+ }
+ }
+ if (ucom->sc_bulkin_no == -1) {
+ printf("%s: Could not find data bulk in\n",
+ USBDEVNAME(ucom->sc_dev));
+ goto bad;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n",
+ USBDEVNAME(ucom->sc_dev));
+ goto bad;
+ }
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UVISORIBUFSIZE;
+ ucom->sc_obufsize = UVISOROBUFSIZE;
+ ucom->sc_ibufsizepad = UVISORIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &uvisor_callback;
+
+ if (uaa->vendor == USB_VENDOR_SONY &&
+ uaa->product == USB_PRODUCT_SONY_CLIE_35)
+ err = clie_3_5_init(sc);
+ else
+ err = uvisor_init(sc);
+
+ if (err) {
+ printf("%s: init failed, %s\n", USBDEVNAME(ucom->sc_dev),
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, ucom->sc_udev,
+ USBDEV(ucom->sc_dev));
+
+ DPRINTF(("uvisor: in=0x%x out=0x%x\n", ucom->sc_bulkin_no, ucom->sc_bulkout_no));
+ ucom_attach(&sc->sc_ucom);
+
+ USB_ATTACH_SUCCESS_RETURN;
+
+bad:
+ DPRINTF(("uvisor_attach: ATTACH ERROR\n"));
+ ucom->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+}
+
+#if 0
+
+int
+uvisor_activate(device_ptr_t self, enum devact act)
+{
+ struct uvisor_softc *sc = (struct uvisor_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ if (sc->sc_subdev != NULL)
+ rv = config_deactivate(sc->sc_subdev);
+ sc->sc_dying = 1;
+ break;
+ }
+ return (rv);
+}
+
+#endif
+
+USB_DETACH(uvisor)
+{
+ USB_DETACH_START(uvisor, sc);
+ int rv = 0;
+
+ DPRINTF(("uvisor_detach: sc=%p\n", sc));
+ sc->sc_ucom.sc_dying = 1;
+ rv = ucom_detach(&sc->sc_ucom);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_ucom.sc_udev,
+ USBDEV(sc->sc_ucom.sc_dev));
+
+ return (rv);
+}
+
+usbd_status
+uvisor_init(struct uvisor_softc *sc)
+{
+ usbd_status err;
+ usb_device_request_t req;
+ struct uvisor_connection_info coninfo;
+ int actlen;
+ uWord avail;
+ char buffer[256];
+
+ DPRINTF(("uvisor_init: getting connection info\n"));
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_CONNECTION_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE);
+ err = usbd_do_request_flags(sc->sc_ucom.sc_udev, &req, &coninfo,
+ USBD_SHORT_XFER_OK, &actlen,
+ USBD_DEFAULT_TIMEOUT);
+ if (err)
+ return (err);
+
+#ifdef USB_DEBUG
+ {
+ int i, np;
+ char *string;
+
+ np = UGETW(coninfo.num_ports);
+ printf("%s: Number of ports: %d\n", USBDEVNAME(sc->sc_ucom.sc_dev), np);
+ for (i = 0; i < np; ++i) {
+ switch (coninfo.connections[i].port_function_id) {
+ case UVISOR_FUNCTION_GENERIC:
+ string = "Generic";
+ break;
+ case UVISOR_FUNCTION_DEBUGGER:
+ string = "Debugger";
+ break;
+ case UVISOR_FUNCTION_HOTSYNC:
+ string = "HotSync";
+ break;
+ case UVISOR_FUNCTION_REMOTE_FILE_SYS:
+ string = "Remote File System";
+ break;
+ default:
+ string = "unknown";
+ break;
+ }
+ printf("%s: port %d, is for %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), coninfo.connections[i].port,
+ string);
+ }
+ }
+#endif
+
+ if (sc->sc_flags & PALM4) {
+ /* Palm OS 4.0 Hack */
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_PALM_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_PALM_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+ }
+
+ DPRINTF(("uvisor_init: getting available bytes\n"));
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_REQUEST_BYTES_AVAILABLE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 5);
+ USETW(req.wLength, sizeof avail);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, &avail);
+ if (err)
+ return (err);
+ DPRINTF(("uvisor_init: avail=%d\n", UGETW(avail)));
+
+ DPRINTF(("uvisor_init: done\n"));
+ return (err);
+}
+
+usbd_status
+clie_3_5_init(struct uvisor_softc *sc)
+{
+ usbd_status err;
+ usb_device_request_t req;
+ char buffer[256];
+
+ /*
+ * Note that PEG-300 series devices expect the following two calls.
+ */
+
+ /* get the config number */
+ DPRINTF(("clie_3_5_init: getting config info\n"));
+ req.bmRequestType = UT_READ;
+ req.bRequest = UR_GET_CONFIG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+
+ /* get the interface number */
+ DPRINTF(("clie_3_5_init: get the interface number\n"));
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_INTERFACE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+
+#ifdef USB_DEBUG
+ {
+ struct uvisor_connection_info coninfo;
+ int i, np;
+ char *string;
+
+ np = UGETW(coninfo.num_ports);
+ DPRINTF(("%s: Number of ports: %d\n", USBDEVNAME(sc->sc_ucom.sc_dev), np));
+ for (i = 0; i < np; ++i) {
+ switch (coninfo.connections[i].port_function_id) {
+ case UVISOR_FUNCTION_GENERIC:
+ string = "Generic";
+ break;
+ case UVISOR_FUNCTION_DEBUGGER:
+ string = "Debugger";
+ break;
+ case UVISOR_FUNCTION_HOTSYNC:
+ string = "HotSync";
+ break;
+ case UVISOR_FUNCTION_REMOTE_FILE_SYS:
+ string = "Remote File System";
+ break;
+ default:
+ string = "unknown";
+ break;
+ }
+ DPRINTF(("%s: port %d, is for %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), coninfo.connections[i].port,
+ string));
+ }
+ }
+#endif
+
+ DPRINTF(("clie_3_5_init: done\n"));
+ return (err);
+}
+
+void
+uvisor_close(void *addr, int portno)
+{
+ struct uvisor_softc *sc = addr;
+ usb_device_request_t req;
+ struct uvisor_connection_info coninfo; /* XXX ? */
+ int actlen;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT; /* XXX read? */
+ req.bRequest = UVISOR_CLOSE_NOTIFICATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE);
+ (void)usbd_do_request_flags(sc->sc_ucom.sc_udev, &req, &coninfo,
+ USBD_SHORT_XFER_OK, &actlen,
+ USBD_DEFAULT_TIMEOUT);
+}
diff --git a/sys/dev/usb/uvscom.c b/sys/dev/usb/uvscom.c
new file mode 100644
index 0000000..22738d6
--- /dev/null
+++ b/sys/dev/usb/uvscom.c
@@ -0,0 +1,947 @@
+/* $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $ */
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * uvscom: SUNTAC Slipper U VS-10U driver.
+ * Slipper U is a PC card to USB converter for data communication card
+ * adapter. It supports DDI Pocket's Air H" C@rd, C@rd H" 64, NTT's P-in,
+ * P-in m@ater and various data communication card adapters.
+ */
+
+#include "opt_uvscom.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#if defined(__FreeBSD__)
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#if __FreeBSD_version >= 500014
+#include <sys/selinfo.h>
+#else
+#include <sys/select.h>
+#endif
+#else
+#include <sys/ioctl.h>
+#include <sys/device.h>
+#endif
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom");
+#ifdef USB_DEBUG
+static int uvscomdebug = 0;
+SYSCTL_INT(_hw_usb_uvscom, OID_AUTO, debug, CTLFLAG_RW,
+ &uvscomdebug, 0, "uvscom debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (uvscomdebug > (n)) \
+ logprintf x; \
+ } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UVSCOM_MODVER 1 /* module version */
+
+#define UVSCOM_CONFIG_INDEX 0
+#define UVSCOM_IFACE_INDEX 0
+
+#ifndef UVSCOM_INTR_INTERVAL
+#define UVSCOM_INTR_INTERVAL 100 /* mS */
+#endif
+
+#define UVSCOM_UNIT_WAIT 5
+
+/* Request */
+#define UVSCOM_SET_SPEED 0x10
+#define UVSCOM_LINE_CTL 0x11
+#define UVSCOM_SET_PARAM 0x12
+#define UVSCOM_READ_STATUS 0xd0
+#define UVSCOM_SHUTDOWN 0xe0
+
+/* UVSCOM_SET_SPEED parameters */
+#define UVSCOM_SPEED_150BPS 0x00
+#define UVSCOM_SPEED_300BPS 0x01
+#define UVSCOM_SPEED_600BPS 0x02
+#define UVSCOM_SPEED_1200BPS 0x03
+#define UVSCOM_SPEED_2400BPS 0x04
+#define UVSCOM_SPEED_4800BPS 0x05
+#define UVSCOM_SPEED_9600BPS 0x06
+#define UVSCOM_SPEED_19200BPS 0x07
+#define UVSCOM_SPEED_38400BPS 0x08
+#define UVSCOM_SPEED_57600BPS 0x09
+#define UVSCOM_SPEED_115200BPS 0x0a
+
+/* UVSCOM_LINE_CTL parameters */
+#define UVSCOM_BREAK 0x40
+#define UVSCOM_RTS 0x02
+#define UVSCOM_DTR 0x01
+#define UVSCOM_LINE_INIT 0x08
+
+/* UVSCOM_SET_PARAM parameters */
+#define UVSCOM_DATA_MASK 0x03
+#define UVSCOM_DATA_BIT_8 0x03
+#define UVSCOM_DATA_BIT_7 0x02
+#define UVSCOM_DATA_BIT_6 0x01
+#define UVSCOM_DATA_BIT_5 0x00
+
+#define UVSCOM_STOP_MASK 0x04
+#define UVSCOM_STOP_BIT_2 0x04
+#define UVSCOM_STOP_BIT_1 0x00
+
+#define UVSCOM_PARITY_MASK 0x18
+#define UVSCOM_PARITY_EVEN 0x18
+#if 0
+#define UVSCOM_PARITY_UNK 0x10
+#endif
+#define UVSCOM_PARITY_ODD 0x08
+#define UVSCOM_PARITY_NONE 0x00
+
+/* Status bits */
+#define UVSCOM_TXRDY 0x04
+#define UVSCOM_RXRDY 0x01
+
+#define UVSCOM_DCD 0x08
+#define UVSCOM_NOCARD 0x04
+#define UVSCOM_DSR 0x02
+#define UVSCOM_CTS 0x01
+#define UVSCOM_USTAT_MASK (UVSCOM_NOCARD | UVSCOM_DSR | UVSCOM_CTS)
+
+struct uvscom_softc {
+ struct ucom_softc sc_ucom;
+
+ int sc_iface_number;/* interface number */
+
+ usbd_interface_handle sc_intr_iface; /* interrupt interface */
+ int sc_intr_number; /* interrupt number */
+ usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
+ u_char *sc_intr_buf; /* interrupt buffer */
+ int sc_isize;
+
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+
+ u_char sc_lsr; /* Local status register */
+ u_char sc_msr; /* uvscom status register */
+
+ uint16_t sc_lcr; /* Line control */
+ u_char sc_usr; /* unit status */
+};
+
+/*
+ * These are the maximum number of bytes transferred per frame.
+ * The output buffer size cannot be increased due to the size encoding.
+ */
+#define UVSCOMIBUFSIZE 512
+#define UVSCOMOBUFSIZE 64
+
+#ifndef UVSCOM_DEFAULT_OPKTSIZE
+#define UVSCOM_DEFAULT_OPKTSIZE 8
+#endif
+
+Static usbd_status uvscom_shutdown(struct uvscom_softc *);
+Static usbd_status uvscom_reset(struct uvscom_softc *);
+Static usbd_status uvscom_set_line_coding(struct uvscom_softc *,
+ uint16_t, uint16_t);
+Static usbd_status uvscom_set_line(struct uvscom_softc *, uint16_t);
+Static usbd_status uvscom_set_crtscts(struct uvscom_softc *);
+Static void uvscom_get_status(void *, int, u_char *, u_char *);
+Static void uvscom_dtr(struct uvscom_softc *, int);
+Static void uvscom_rts(struct uvscom_softc *, int);
+Static void uvscom_break(struct uvscom_softc *, int);
+
+Static void uvscom_set(void *, int, int, int);
+Static void uvscom_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+#if TODO
+Static int uvscom_ioctl(void *, int, u_long, caddr_t, int, usb_proc_ptr);
+#endif
+Static int uvscom_param(void *, int, struct termios *);
+Static int uvscom_open(void *, int);
+Static void uvscom_close(void *, int);
+
+struct ucom_callback uvscom_callback = {
+ uvscom_get_status,
+ uvscom_set,
+ uvscom_param,
+ NULL, /* uvscom_ioctl, TODO */
+ uvscom_open,
+ uvscom_close,
+ NULL,
+ NULL
+};
+
+static const struct usb_devno uvscom_devs [] = {
+ /* SUNTAC U-Cable type A4 */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4 },
+ /* SUNTAC U-Cable type D2 */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L },
+ /* SUNTAC Ir-Trinity */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U },
+ /* SUNTAC U-Cable type P1 */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1 },
+ /* SUNTAC Slipper U */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U },
+};
+#define uvscom_lookup(v, p) usb_lookup(uvscom_devs, v, p)
+
+Static device_probe_t uvscom_match;
+Static device_attach_t uvscom_attach;
+Static device_detach_t uvscom_detach;
+
+Static device_method_t uvscom_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uvscom_match),
+ DEVMETHOD(device_attach, uvscom_attach),
+ DEVMETHOD(device_detach, uvscom_detach),
+ { 0, 0 }
+};
+
+Static driver_t uvscom_driver = {
+ "ucom",
+ uvscom_methods,
+ sizeof (struct uvscom_softc)
+};
+
+DRIVER_MODULE(uvscom, uhub, uvscom_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uvscom, usb, 1, 1, 1);
+MODULE_DEPEND(uvscom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(uvscom, UVSCOM_MODVER);
+
+static int uvscomobufsiz = UVSCOM_DEFAULT_OPKTSIZE;
+static int uvscominterval = UVSCOM_INTR_INTERVAL;
+
+static int
+sysctl_hw_usb_uvscom_opktsize(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = uvscomobufsiz;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+ if (0 < val && val <= UVSCOMOBUFSIZE)
+ uvscomobufsiz = val;
+ else
+ err = EINVAL;
+
+ return (err);
+}
+
+static int
+sysctl_hw_usb_uvscom_interval(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = uvscominterval;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+ if (0 < val && val <= 1000)
+ uvscominterval = val;
+ else
+ err = EINVAL;
+
+ return (err);
+}
+
+SYSCTL_PROC(_hw_usb_uvscom, OID_AUTO, opktsize, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_usb_uvscom_opktsize,
+ "I", "uvscom output packet size");
+SYSCTL_PROC(_hw_usb_uvscom, OID_AUTO, interval, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_usb_uvscom_interval,
+ "I", "uvscom interrpt pipe interval");
+
+USB_MATCH(uvscom)
+{
+ USB_MATCH_START(uvscom, uaa);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ return (uvscom_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+USB_ATTACH(uvscom)
+{
+ USB_ATTACH_START(uvscom, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ struct ucom_softc *ucom;
+ usb_config_descriptor_t *cdesc;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ char *devinfo;
+ const char *devname;
+ usbd_status err;
+ int i;
+
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+ ucom = &sc->sc_ucom;
+
+ bzero(sc, sizeof (struct uvscom_softc));
+
+ usbd_devinfo(dev, 0, devinfo);
+ /* USB_ATTACH_SETUP; */
+ ucom->sc_dev = self;
+ device_set_desc_copy(self, devinfo);
+ /* USB_ATTACH_SETUP; */
+
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ devname = USBDEVNAME(ucom->sc_dev);
+ printf("%s: %s\n", devname, devinfo);
+
+ DPRINTF(("uvscom attach: sc = %p\n", sc));
+
+ /* initialize endpoints */
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ sc->sc_intr_number = -1;
+ sc->sc_intr_pipe = NULL;
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UVSCOM_CONFIG_INDEX, 1);
+ if (err) {
+ printf("%s: failed to set configuration, err=%s\n",
+ devname, usbd_errstr(err));
+ goto error;
+ }
+
+ /* get the config descriptor */
+ cdesc = usbd_get_config_descriptor(ucom->sc_udev);
+
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n",
+ USBDEVNAME(ucom->sc_dev));
+ goto error;
+ }
+
+ /* get the common interface */
+ err = usbd_device2interface_handle(dev, UVSCOM_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ printf("%s: failed to get interface, err=%s\n",
+ devname, usbd_errstr(err));
+ goto error;
+ }
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ /* Find endpoints */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n",
+ USBDEVNAME(ucom->sc_dev), i);
+ goto error;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->sc_intr_number = ed->bEndpointAddress;
+ sc->sc_isize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ if (ucom->sc_bulkin_no == -1) {
+ printf("%s: Could not find data bulk in\n",
+ USBDEVNAME(ucom->sc_dev));
+ goto error;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n",
+ USBDEVNAME(ucom->sc_dev));
+ goto error;
+ }
+ if (sc->sc_intr_number == -1) {
+ printf("%s: Could not find interrupt in\n",
+ USBDEVNAME(ucom->sc_dev));
+ goto error;
+ }
+
+ sc->sc_dtr = sc->sc_rts = 0;
+ sc->sc_lcr = UVSCOM_LINE_INIT;
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UVSCOMIBUFSIZE;
+ ucom->sc_obufsize = uvscomobufsiz;
+ ucom->sc_ibufsizepad = UVSCOMIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &uvscom_callback;
+
+ err = uvscom_reset(sc);
+
+ if (err) {
+ printf("%s: reset failed, %s\n", USBDEVNAME(ucom->sc_dev),
+ usbd_errstr(err));
+ goto error;
+ }
+
+ DPRINTF(("uvscom: in = 0x%x out = 0x%x intr = 0x%x\n",
+ ucom->sc_bulkin_no, ucom->sc_bulkout_no, sc->sc_intr_number));
+
+ ucom_attach(&sc->sc_ucom);
+
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_SUCCESS_RETURN;
+
+error:
+ ucom->sc_dying = 1;
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+USB_DETACH(uvscom)
+{
+ USB_DETACH_START(uvscom, sc);
+ int rv = 0;
+
+ DPRINTF(("uvscom_detach: sc = %p\n", sc));
+
+ sc->sc_ucom.sc_dying = 1;
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return (rv);
+}
+
+Static usbd_status
+uvscom_readstat(struct uvscom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ uint16_t r;
+
+ DPRINTF(("%s: send readstat\n", USBDEVNAME(sc->sc_ucom.sc_dev)));
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_READ_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 2);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, &r);
+ if (err) {
+ printf("%s: uvscom_readstat: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ DPRINTF(("%s: uvscom_readstat: r = %d\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), r));
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static usbd_status
+uvscom_shutdown(struct uvscom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("%s: send shutdown\n", USBDEVNAME(sc->sc_ucom.sc_dev)));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_SHUTDOWN;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
+ if (err) {
+ printf("%s: uvscom_shutdown: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static usbd_status
+uvscom_reset(struct uvscom_softc *sc)
+{
+ DPRINTF(("%s: uvscom_reset\n", USBDEVNAME(sc->sc_ucom.sc_dev)));
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static usbd_status
+uvscom_set_crtscts(struct uvscom_softc *sc)
+{
+ DPRINTF(("%s: uvscom_set_crtscts\n", USBDEVNAME(sc->sc_ucom.sc_dev)));
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static usbd_status
+uvscom_set_line(struct uvscom_softc *sc, uint16_t line)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("%s: uvscom_set_line: %04x\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), line));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_LINE_CTL;
+ USETW(req.wValue, line);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
+ if (err) {
+ printf("%s: uvscom_set_line: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static usbd_status
+uvscom_set_line_coding(struct uvscom_softc *sc, uint16_t lsp, uint16_t ls)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("%s: uvscom_set_line_coding: %02x %02x\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), lsp, ls));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_SET_SPEED;
+ USETW(req.wValue, lsp);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
+ if (err) {
+ printf("%s: uvscom_set_line_coding: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_SET_PARAM;
+ USETW(req.wValue, ls);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
+ if (err) {
+ printf("%s: uvscom_set_line_coding: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static void
+uvscom_dtr(struct uvscom_softc *sc, int onoff)
+{
+ DPRINTF(("%s: uvscom_dtr: onoff = %d\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), onoff));
+
+ if (sc->sc_dtr == onoff)
+ return; /* no change */
+
+ sc->sc_dtr = onoff;
+
+ if (onoff)
+ SET(sc->sc_lcr, UVSCOM_DTR);
+ else
+ CLR(sc->sc_lcr, UVSCOM_DTR);
+
+ uvscom_set_line(sc, sc->sc_lcr);
+}
+
+Static void
+uvscom_rts(struct uvscom_softc *sc, int onoff)
+{
+ DPRINTF(("%s: uvscom_rts: onoff = %d\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), onoff));
+
+ if (sc->sc_rts == onoff)
+ return; /* no change */
+
+ sc->sc_rts = onoff;
+
+ if (onoff)
+ SET(sc->sc_lcr, UVSCOM_RTS);
+ else
+ CLR(sc->sc_lcr, UVSCOM_RTS);
+
+ uvscom_set_line(sc, sc->sc_lcr);
+}
+
+Static void
+uvscom_break(struct uvscom_softc *sc, int onoff)
+{
+ DPRINTF(("%s: uvscom_break: onoff = %d\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), onoff));
+
+ if (onoff)
+ uvscom_set_line(sc, SET(sc->sc_lcr, UVSCOM_BREAK));
+}
+
+Static void
+uvscom_set(void *addr, int portno, int reg, int onoff)
+{
+ struct uvscom_softc *sc = addr;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ uvscom_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ uvscom_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ uvscom_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+}
+
+Static int
+uvscom_param(void *addr, int portno, struct termios *t)
+{
+ struct uvscom_softc *sc = addr;
+ usbd_status err;
+ uint16_t lsp;
+ uint16_t ls;
+
+ DPRINTF(("%s: uvscom_param: sc = %p\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), sc));
+
+ ls = 0;
+
+ switch (t->c_ospeed) {
+ case B150:
+ lsp = UVSCOM_SPEED_150BPS;
+ break;
+ case B300:
+ lsp = UVSCOM_SPEED_300BPS;
+ break;
+ case B600:
+ lsp = UVSCOM_SPEED_600BPS;
+ break;
+ case B1200:
+ lsp = UVSCOM_SPEED_1200BPS;
+ break;
+ case B2400:
+ lsp = UVSCOM_SPEED_2400BPS;
+ break;
+ case B4800:
+ lsp = UVSCOM_SPEED_4800BPS;
+ break;
+ case B9600:
+ lsp = UVSCOM_SPEED_9600BPS;
+ break;
+ case B19200:
+ lsp = UVSCOM_SPEED_19200BPS;
+ break;
+ case B38400:
+ lsp = UVSCOM_SPEED_38400BPS;
+ break;
+ case B57600:
+ lsp = UVSCOM_SPEED_57600BPS;
+ break;
+ case B115200:
+ lsp = UVSCOM_SPEED_115200BPS;
+ break;
+ default:
+ return (EIO);
+ }
+
+ if (ISSET(t->c_cflag, CSTOPB))
+ SET(ls, UVSCOM_STOP_BIT_2);
+ else
+ SET(ls, UVSCOM_STOP_BIT_1);
+
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ SET(ls, UVSCOM_PARITY_ODD);
+ else
+ SET(ls, UVSCOM_PARITY_EVEN);
+ } else
+ SET(ls, UVSCOM_PARITY_NONE);
+
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ SET(ls, UVSCOM_DATA_BIT_5);
+ break;
+ case CS6:
+ SET(ls, UVSCOM_DATA_BIT_6);
+ break;
+ case CS7:
+ SET(ls, UVSCOM_DATA_BIT_7);
+ break;
+ case CS8:
+ SET(ls, UVSCOM_DATA_BIT_8);
+ break;
+ default:
+ return (EIO);
+ }
+
+ err = uvscom_set_line_coding(sc, lsp, ls);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CRTSCTS)) {
+ err = uvscom_set_crtscts(sc);
+ if (err)
+ return (EIO);
+ }
+
+ return (0);
+}
+
+Static int
+uvscom_open(void *addr, int portno)
+{
+ struct uvscom_softc *sc = addr;
+ int err;
+ int i;
+
+ if (sc->sc_ucom.sc_dying)
+ return (ENXIO);
+
+ DPRINTF(("uvscom_open: sc = %p\n", sc));
+
+ /* change output packet size */
+ sc->sc_ucom.sc_obufsize = uvscomobufsiz;
+
+ if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
+ DPRINTF(("uvscom_open: open interrupt pipe.\n"));
+
+ sc->sc_usr = 0; /* clear unit status */
+
+ err = uvscom_readstat(sc);
+ if (err) {
+ DPRINTF(("%s: uvscom_open: readstat faild\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev)));
+ return (ENXIO);
+ }
+
+ sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(sc->sc_ucom.sc_iface,
+ sc->sc_intr_number,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intr_pipe,
+ sc,
+ sc->sc_intr_buf,
+ sc->sc_isize,
+ uvscom_intr,
+ uvscominterval);
+ if (err) {
+ printf("%s: cannot open interrupt pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ sc->sc_intr_number);
+ return (ENXIO);
+ }
+ } else {
+ DPRINTF(("uvscom_open: did not open interrupt pipe.\n"));
+ }
+
+ if ((sc->sc_usr & UVSCOM_USTAT_MASK) == 0) {
+ /* unit is not ready */
+
+ for (i = UVSCOM_UNIT_WAIT; i > 0; --i) {
+ tsleep(&err, TTIPRI, "uvsop", hz); /* XXX */
+ if (ISSET(sc->sc_usr, UVSCOM_USTAT_MASK))
+ break;
+ }
+ if (i == 0) {
+ DPRINTF(("%s: unit is not ready\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev)));
+ return (ENXIO);
+ }
+
+ /* check PC card was inserted */
+ if (ISSET(sc->sc_usr, UVSCOM_NOCARD)) {
+ DPRINTF(("%s: no card\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev)));
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+Static void
+uvscom_close(void *addr, int portno)
+{
+ struct uvscom_softc *sc = addr;
+ int err;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ DPRINTF(("uvscom_close: close\n"));
+
+ uvscom_shutdown(sc);
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: abort interrupt pipe failed: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: close interrupt pipe failed: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
+
+Static void
+uvscom_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct uvscom_softc *sc = priv;
+ u_char *buf = sc->sc_intr_buf;
+ u_char pstatus;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ printf("%s: uvscom_intr: abnormal status: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev),
+ usbd_errstr(status));
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+
+ DPRINTFN(2, ("%s: uvscom status = %02x %02x\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), buf[0], buf[1]));
+
+ sc->sc_lsr = sc->sc_msr = 0;
+ sc->sc_usr = buf[1];
+
+ pstatus = buf[0];
+ if (ISSET(pstatus, UVSCOM_TXRDY))
+ SET(sc->sc_lsr, ULSR_TXRDY);
+ if (ISSET(pstatus, UVSCOM_RXRDY))
+ SET(sc->sc_lsr, ULSR_RXRDY);
+
+ pstatus = buf[1];
+ if (ISSET(pstatus, UVSCOM_CTS))
+ SET(sc->sc_msr, UMSR_CTS);
+ if (ISSET(pstatus, UVSCOM_DSR))
+ SET(sc->sc_msr, UMSR_DSR);
+ if (ISSET(pstatus, UVSCOM_DCD))
+ SET(sc->sc_msr, UMSR_DCD);
+
+ ucom_status_change(&sc->sc_ucom);
+}
+
+Static void
+uvscom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct uvscom_softc *sc = addr;
+
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
+
+#if TODO
+Static int
+uvscom_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag,
+ usb_proc_ptr p)
+{
+ struct uvscom_softc *sc = addr;
+ int error = 0;
+
+ if (sc->sc_ucom.sc_dying)
+ return (EIO);
+
+ DPRINTF(("uvscom_ioctl: cmd = 0x%08lx\n", cmd));
+
+ switch (cmd) {
+ case TIOCNOTTY:
+ case TIOCMGET:
+ case TIOCMSET:
+ break;
+
+ default:
+ DPRINTF(("uvscom_ioctl: unknown\n"));
+ error = ENOTTY;
+ break;
+ }
+
+ return (error);
+}
+#endif
OpenPOWER on IntegriCloud