diff options
Diffstat (limited to 'sys/dev/usb')
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 |