From 1550dfd9ba7e2047cd70d4bfeae5cf28891bbb0f Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 30 Dec 2003 01:01:48 +0000 Subject: [PATCH] Bring in the entire FreeBSD-5 USB infrastructure. As of this commit my USB camera, Hard Drive, Mouse, and Sony memory key all work and I can even unplug and replug them in without crashing the port. Not all drivers and subsystems compile as of this commit, but the ones that do not are very close. --- sys/bus/cam/cam.c | 17 +- sys/bus/usb/FILES | 20 +- sys/bus/usb/Makefile.usbdevs | 3 +- sys/bus/usb/devlist2h.awk | 9 +- sys/bus/usb/dsbr100io.h | 6 +- sys/bus/usb/ehci.c | 2820 +++++++++++++++++++++++++++ sys/bus/usb/ehci_pci.c | 342 ++++ sys/bus/usb/ehcireg.h | 293 +++ sys/bus/usb/ehcivar.h | 156 ++ sys/bus/usb/hid.c | 40 +- sys/bus/usb/hid.h | 14 +- sys/bus/usb/kue_fw.h | 10 +- sys/bus/usb/ohci.c | 1677 +++++++++++----- sys/bus/usb/ohci_pci.c | 370 ++++ sys/bus/usb/ohcireg.h | 24 +- sys/bus/usb/ohcivar.h | 58 +- sys/bus/usb/rio500_usb.h | 6 +- sys/bus/usb/ugraphire_rdesc.h | 95 + sys/bus/usb/uhci.c | 1669 ++++++++++------ sys/bus/usb/uhci_pci.c | 415 ++++ sys/bus/usb/uhcireg.h | 28 +- sys/bus/usb/uhcivar.h | 62 +- sys/bus/usb/uhub.c | 140 +- sys/bus/usb/usb.c | 465 +++-- sys/bus/usb/usb.h | 42 +- sys/bus/usb/usb_ethersubr.c | 22 +- sys/bus/usb/usb_ethersubr.h | 4 +- sys/bus/usb/usb_if.m | 4 +- sys/bus/usb/usb_mem.c | 306 +++ sys/bus/usb/usb_mem.h | 53 +- sys/bus/usb/usb_port.h | 436 ++++- sys/bus/usb/usb_quirks.c | 41 +- sys/bus/usb/usb_quirks.h | 15 +- sys/bus/usb/usb_subr.c | 352 ++-- sys/bus/usb/usbcdc.h | 20 +- sys/bus/usb/usbdevs | 5 +- sys/bus/usb/usbdevs.h | 5 +- sys/bus/usb/usbdevs_data.h | 12 +- sys/bus/usb/usbdi.c | 300 ++- sys/bus/usb/usbdi.h | 215 +- sys/bus/usb/usbdi_util.c | 135 +- sys/bus/usb/usbdi_util.h | 87 +- sys/bus/usb/usbdivar.h | 91 +- sys/bus/usb/usbhid.h | 12 +- sys/conf/files | 12 +- sys/dev/netif/aue/if_aue.c | 705 +++---- sys/dev/netif/aue/if_auereg.h | 50 +- sys/dev/netif/axe/Makefile | 10 + sys/dev/netif/axe/if_axe.c | 1207 ++++++++++++ sys/dev/netif/axe/if_axereg.h | 180 ++ sys/dev/netif/cue/if_cue.c | 302 +-- sys/dev/netif/cue/if_cuereg.h | 19 +- sys/dev/netif/kue/if_kue.c | 184 +- sys/dev/netif/kue/if_kuereg.h | 20 +- sys/dev/usbmisc/ubsa/ubsa.c | 42 +- sys/dev/usbmisc/ucom/ucom.c | 20 +- sys/dev/usbmisc/ucom/ucomvar.h | 8 +- sys/dev/usbmisc/udbp/udbp.c | 844 ++++++++ sys/dev/usbmisc/udbp/udbp.h | 81 + sys/dev/usbmisc/ufm/ufm.c | 37 +- sys/dev/usbmisc/uftdi/uftdi.c | 19 +- sys/dev/usbmisc/uftdi/uftdireg.h | 8 +- sys/dev/usbmisc/ugen/ugen.c | 382 ++-- sys/dev/usbmisc/uhid/uhid.c | 119 +- sys/dev/usbmisc/ukbd/ukbd.c | 97 +- sys/dev/usbmisc/ulpt/ulpt.c | 310 ++- sys/dev/usbmisc/umass/umass.c | 1085 +++++------ sys/dev/usbmisc/umct/umct.c | 514 +++++ sys/dev/usbmisc/umodem/umodem.c | 1237 ++++-------- sys/dev/usbmisc/ums/ums.c | 76 +- sys/dev/usbmisc/uplcom/uplcom.c | 26 +- sys/dev/usbmisc/urio/urio.c | 108 +- sys/dev/usbmisc/uscanner/uscanner.c | 51 +- sys/dev/usbmisc/uvisor/uvisor.c | 45 +- sys/dev/usbmisc/uvscom/uvscom.c | 10 +- sys/net/if.c | 19 +- sys/net/if_var.h | 10 +- usr.sbin/usbd/usbd.8 | 6 +- usr.sbin/usbd/usbd.c | 35 +- usr.sbin/usbd/usbd.conf.5 | 4 +- usr.sbin/usbdevs/usbdevs.8 | 6 +- usr.sbin/usbdevs/usbdevs.c | 18 +- 82 files changed, 14338 insertions(+), 4464 deletions(-) create mode 100644 sys/bus/usb/ehci.c create mode 100644 sys/bus/usb/ehci_pci.c create mode 100644 sys/bus/usb/ehcireg.h create mode 100644 sys/bus/usb/ehcivar.h create mode 100644 sys/bus/usb/ohci_pci.c create mode 100644 sys/bus/usb/ugraphire_rdesc.h create mode 100644 sys/bus/usb/uhci_pci.c create mode 100644 sys/bus/usb/usb_mem.c create mode 100644 sys/dev/netif/axe/Makefile create mode 100644 sys/dev/netif/axe/if_axe.c create mode 100644 sys/dev/netif/axe/if_axereg.h create mode 100644 sys/dev/usbmisc/udbp/udbp.c create mode 100644 sys/dev/usbmisc/udbp/udbp.h create mode 100644 sys/dev/usbmisc/umct/umct.c diff --git a/sys/bus/cam/cam.c b/sys/bus/cam/cam.c index dd33ad3942..ca84daddca 100644 --- a/sys/bus/cam/cam.c +++ b/sys/bus/cam/cam.c @@ -26,12 +26,27 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/cam/cam.c,v 1.3 1999/08/28 00:40:38 peter Exp $ - * $DragonFly: src/sys/bus/cam/cam.c,v 1.4 2003/12/29 23:30:58 dillon Exp $ + * $DragonFly: src/sys/bus/cam/cam.c,v 1.5 2003/12/30 01:01:40 dillon Exp $ */ #include +#ifdef _KERNEL +#include +#include +#include +#else +#include +#include +#endif #include "cam.h" #include "cam_ccb.h" +#include "scsi/scsi_all.h" +#include + +#ifdef _KERNEL +#include +#include "cam_xpt.h" +#endif void cam_strvis(u_int8_t *dst, const u_int8_t *src, int srclen, int dstlen) diff --git a/sys/bus/usb/FILES b/sys/bus/usb/FILES index 88840fa339..410aac37ce 100644 --- a/sys/bus/usb/FILES +++ b/sys/bus/usb/FILES @@ -1,5 +1,5 @@ -$FreeBSD: src/sys/dev/usb/FILES,v 1.2.2.1 2002/03/04 04:01:35 alfred Exp $ -$DragonFly: src/sys/bus/usb/FILES,v 1.2 2003/06/17 04:28:32 dillon Exp $ +$FreeBSD: src/sys/dev/usb/FILES,v 1.6 2003/04/14 14:04:07 ticso Exp $ +$DragonFly: src/sys/bus/usb/FILES,v 1.3 2003/12/30 01:01:44 dillon Exp $ A small roadmap of the USB files: @@ -9,16 +9,26 @@ 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 -ugen.c generic driver that can handle access to any USB device +[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 @@ -28,9 +38,10 @@ 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 (bulk only for now) +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 @@ -48,4 +59,5 @@ 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/bus/usb/Makefile.usbdevs b/sys/bus/usb/Makefile.usbdevs index 70f34061f5..1e3c2adaa1 100644 --- a/sys/bus/usb/Makefile.usbdevs +++ b/sys/bus/usb/Makefile.usbdevs @@ -1,6 +1,7 @@ # The files usbdevs.h and usbdevs_data.h are generated from usbdevs # -# $DragonFly: src/sys/bus/usb/Attic/Makefile.usbdevs,v 1.1 2003/08/22 23:51:55 dillon Exp $ +# $FreeBSD: src/sys/dev/usb/Makefile.usbdevs,v 1.2 2000/03/15 22:13:50 n_hibma Exp $ +# $DragonFly: src/sys/bus/usb/Attic/Makefile.usbdevs,v 1.2 2003/12/30 01:01:44 dillon Exp $ AWK= awk UNAME= uname diff --git a/sys/bus/usb/devlist2h.awk b/sys/bus/usb/devlist2h.awk index 8629d9e0bf..50b880f2ba 100644 --- a/sys/bus/usb/devlist2h.awk +++ b/sys/bus/usb/devlist2h.awk @@ -1,7 +1,8 @@ #! /usr/bin/awk -f -# $NetBSD: devlist2h.awk,v 1.6 1999/08/17 16:06:20 augustss Exp $ -# $FreeBSD: src/sys/dev/usb/devlist2h.awk,v 1.1 1999/11/18 18:02:44 n_hibma Exp $ -# $DragonFly: src/sys/bus/usb/Attic/devlist2h.awk,v 1.3 2003/08/15 01:19:54 dillon Exp $ +# +# $NetBSD: usb/devlist2h.awk,v 1.9 2001/01/18 20:28:22 jdolecek Exp $ +# $FreeBSD: src/sys/dev/usb/devlist2h.awk,v 1.2 2002/04/01 19:22:04 joe Exp $ +# $DragonFly: src/sys/bus/usb/Attic/devlist2h.awk,v 1.4 2003/12/30 01:01:44 dillon Exp $ # # Copyright (c) 1995, 1996 Christopher G. Demetriou # All rights reserved. @@ -153,7 +154,7 @@ END { printf("\n") > dfile - printf("struct usb_knowndev usb_knowndevs[] = {\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", diff --git a/sys/bus/usb/dsbr100io.h b/sys/bus/usb/dsbr100io.h index 5d02012007..4f00a50c62 100644 --- a/sys/bus/usb/dsbr100io.h +++ b/sys/bus/usb/dsbr100io.h @@ -28,8 +28,10 @@ * its contributors. */ -/* $FreeBSD: src/sys/dev/usb/dsbr100io.h,v 1.1.2.1 2002/03/04 04:01:35 alfred Exp $ */ -/* $DragonFly: src/sys/bus/usb/dsbr100io.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $FreeBSD: src/sys/dev/usb/dsbr100io.h,v 1.1 2002/03/04 03:51:19 alfred Exp $ + * $DragonFly: src/sys/bus/usb/dsbr100io.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #include diff --git a/sys/bus/usb/ehci.c b/sys/bus/usb/ehci.c new file mode 100644 index 0000000000..c0233da5ec --- /dev/null +++ b/sys/bus/usb/ehci.c @@ -0,0 +1,2820 @@ +/* + * $NetBSD: ehci.c,v 1.46 2003/03/09 19:51:13 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/ehci.c,v 1.5 2003/11/10 00:20:52 joe Exp $ + * $DragonFly: src/sys/bus/usb/ehci.c,v 1.1 2003/12/30 01:01:44 dillon Exp $ + */ + +/* Also ported from NetBSD: + * $NetBSD: ehci.c,v 1.50 2003/10/18 04:50:35 simonb Exp $ + */ + +/* + * TODO + * hold off explorations by companion controllers until ehci has started. + */ + +/* + * 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. + */ + +/* + * 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 + * + */ + +#include +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#elif defined(__FreeBSD__) +#include +#include +#include +#include +#include +#include +#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__) +#include +#endif +#endif +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#if defined(__FreeBSD__) +#include + +#define delay(d) DELAY(d) +#endif + +#ifdef 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; + 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 USB_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 USB_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 USB_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; + + 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 = 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_SET_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; +#ifdef USB_DEBUG + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; +#endif + 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 USB_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 USB_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; + 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) { + if (!(xfer->rqflags & URQ_REQUEST)) + printf("ehci_idone: need toggle update\n"); +#if 0 + epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token)); +#endif + } + + 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 USB_DEBUG + char sbuf[128]; + + bitmask_snprintf((u_int32_t)status, + "\20\3MISSEDMICRO\4XACT\5BABBLE\6BABBLE" + "\7HALTED", + 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 USB_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 USB_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 USB_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 USB_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->sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); +} + +Static void +ehci_noop(usbd_pipe_handle pipe) +{ +} + +#ifdef USB_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(""); + 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); + + 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(ed->bEndpointAddress) | + EHCI_QH_SET_EPS(speed) | /* XXX */ + /* XXX 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 USB_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 USB_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; + /* Keep toggle, clear the rest, including length. */ + sqh->qh.qh_qtd.qtd_status &= htole32(EHCI_QTD_TOGGLE); +} + +/* + * 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 != 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 USB_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 USB_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, offset; + int i; + 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)); + 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 */ + /* XXX Data toggle */ + ); + + 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 + + /* XXX true for EHCI? */ + /* the length must be a multiple of the max size */ + curlen -= curlen % UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); + 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); +#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)); + 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; + + 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 REMOVED DFly */) + 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; + + /* XXX + * Since we're messing with the QH we must know the HC is in sync. + * This needs to go away since it slows down control transfers. + * Removing it entails: + * - fill the QH only once with addr & wMaxPacketSize + * - put the correct data toggles in the qtds and set DTC + */ + /* ehci_sync_hc(sc); */ + /* Update device address and length since they may have changed. */ + /* 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_QG_MPLMASK))) | + htole32( + EHCI_QH_SET_ADDR(addr) | + /* EHCI_QH_DTC | */ + EHCI_QH_SET_MPL(UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize)) + ); + /* Clear toggle */ + sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); + + /* Set up data transaction */ + if (len != 0) { + ehci_soft_qtd_t *end; + + 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); + /* Start toggle at 1. */ + /*next->qtd.td_flags |= htole32(EHCI_QTD_TOGGLE);*/ + } else { + next = stat; + } + + memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req); + + setup->qtd.qtd_status = htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | + EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_BYTES(sizeof *req) + ); + setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 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_IOC + ); + stat->qtd.qtd_buffer[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 USB_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 USB_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 USB_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 USB_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/bus/usb/ehci_pci.c b/sys/bus/usb/ehci_pci.c new file mode 100644 index 0000000000..835099d02e --- /dev/null +++ b/sys/bus/usb/ehci_pci.c @@ -0,0 +1,342 @@ +/*- + * 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. + * $FreeBSD: src/sys/dev/usb/ehci_pci.c,v 1.9 2003/12/17 17:15:41 peter Exp $ + * $DragonFly: src/sys/bus/usb/ehci_pci.c,v 1.1 2003/12/30 01:01:44 dillon Exp $ + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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_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(self, SYS_RES_MEMORY, &rid, + 0, ~0, 1, 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(self, SYS_RES_IRQ, &rid, 0, ~0, 1, + 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_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/bus/usb/ehcireg.h b/sys/bus/usb/ehcireg.h new file mode 100644 index 0000000000..1bd16e37d4 --- /dev/null +++ b/sys/bus/usb/ehcireg.h @@ -0,0 +1,293 @@ +/* + * $NetBSD: ehcireg.h,v 1.13 2001/11/23 01:16:27 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/ehcireg.h,v 1.1 2003/04/14 14:04:07 ticso Exp $ + * $DragonFly: src/sys/bus/usb/ehcireg.h,v 1.1 2003/12/30 01:01:44 dillon Exp $ + */ +/* + * 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_TOGGLE 0x80000000 + ehci_physaddr_t qtd_buffer[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_QG_MPLMASK 0x07ff0000 +#define EHCI_QH_GET_CTL(x) (((x) >> 26) & 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/bus/usb/ehcivar.h b/sys/bus/usb/ehcivar.h new file mode 100644 index 0000000000..b2cdd2ce10 --- /dev/null +++ b/sys/bus/usb/ehcivar.h @@ -0,0 +1,156 @@ +/* + * $NetBSD: ehcivar.h,v 1.12 2001/12/31 12:16:57 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/ehcivar.h,v 1.1 2003/04/14 14:04:07 ticso Exp $ + * $DragonFly: src/sys/bus/usb/ehcivar.h,v 1.1 2003/12/30 01:01:44 dillon Exp $ + */ + +/* + * 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/bus/usb/hid.c b/sys/bus/usb/hid.c index 85a3055243..5ac7c1c6a0 100644 --- a/sys/bus/usb/hid.c +++ b/sys/bus/usb/hid.c @@ -1,7 +1,8 @@ -/* $NetBSD: hid.c,v 1.15 2000/04/27 15:26:46 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11.2.6 2002/11/06 14:03:37 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/hid.c,v 1.3 2003/08/07 21:16:47 dillon Exp $ */ - +/* + * $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ + * $FreeBSD: src/sys/dev/usb/hid.c,v 1.23 2003/08/24 17:55:54 obrien Exp $ + * $DragonFly: src/sys/bus/usb/hid.c,v 1.4 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. @@ -45,7 +46,7 @@ #include #endif #include - + #include "usb.h" #include "usbhid.h" @@ -79,6 +80,7 @@ struct hid_data { Static void hid_clear_local(struct hid_item *c) { + c->usage = 0; c->usage_minimum = 0; c->usage_maximum = 0; @@ -96,8 +98,7 @@ hid_start_parse(void *d, int len, int kindset) { struct hid_data *s; - s = malloc(sizeof *s, M_TEMP, M_WAITOK); - memset(s, 0, sizeof *s); + s = malloc(sizeof *s, M_TEMP, M_WAITOK|M_ZERO); s->start = s->p = d; s->end = (char *)d + len; s->kindset = kindset; @@ -107,6 +108,7 @@ hid_start_parse(void *d, int len, int kindset) 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); @@ -189,7 +191,7 @@ hid_get_item(struct hid_data *s, struct hid_item *h) printf("BAD LENGTH %d\n", bSize); continue; } - + switch (bType) { case 0: /* Main */ switch (bTag) { @@ -204,8 +206,8 @@ hid_get_item(struct hid_data *s, struct hid_item *h) s->multi = 0; c->loc.count = 1; if (s->minset) { - for (i = c->usage_minimum; - i <= c->usage_maximum; + for (i = c->usage_minimum; + i <= c->usage_maximum; i++) { s->usages[s->nu] = i; if (s->nu < MAXUSAGE-1) @@ -217,7 +219,7 @@ hid_get_item(struct hid_data *s, struct hid_item *h) } else { *h = *c; h->next = 0; - c->loc.pos += + c->loc.pos += c->loc.size * c->loc.count; hid_clear_local(c); s->minset = 0; @@ -307,9 +309,9 @@ hid_get_item(struct hid_data *s, struct hid_item *h) case 2: /* Local */ switch (bTag) { case 0: - if (bSize == 1) + if (bSize == 1) dval = c->_usage_page | (dval&0xff); - else if (bSize == 2) + else if (bSize == 2) dval = c->_usage_page | (dval&0xffff); c->usage = dval; if (s->nu < MAXUSAGE) @@ -318,16 +320,16 @@ hid_get_item(struct hid_data *s, struct hid_item *h) break; case 1: s->minset = 1; - if (bSize == 1) + if (bSize == 1) dval = c->_usage_page | (dval&0xff); - else if (bSize == 2) + else if (bSize == 2) dval = c->_usage_page | (dval&0xffff); c->usage_minimum = dval; break; case 2: - if (bSize == 1) + if (bSize == 1) dval = c->_usage_page | (dval&0xff); - else if (bSize == 2) + else if (bSize == 2) dval = c->_usage_page | (dval&0xffff); c->usage_maximum = dval; break; @@ -421,7 +423,7 @@ hid_get_data(u_char *buf, struct hid_location *loc) return (0); data = 0; - s = hpos / 8; + s = hpos / 8; for (i = hpos; i < hpos+hsize; i += 8) data |= buf[i / 8] << ((i / 8 - s) * 8); data >>= hpos % 8; @@ -429,7 +431,7 @@ hid_get_data(u_char *buf, struct hid_location *loc) hsize = 32 - hsize; /* Sign extend */ data = ((int32_t)data << hsize) >> hsize; - DPRINTFN(10,("hid_get_data: loc %d/%d = %lu\n", + DPRINTFN(10,("hid_get_data: loc %d/%d = %lu\n", loc->pos, loc->size, (long)data)); return (data); } diff --git a/sys/bus/usb/hid.h b/sys/bus/usb/hid.h index bdb70e0dd1..687ba1deef 100644 --- a/sys/bus/usb/hid.h +++ b/sys/bus/usb/hid.h @@ -1,6 +1,8 @@ -/* $NetBSD: hid.h,v 1.5 2000/04/27 15:26:46 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/hid.h,v 1.7.2.3 2000/10/31 23:23:29 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/hid.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: hid.h,v 1.6 2000/06/01 14:28:57 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/hid.h,v 1.12 2003/07/04 01:50:38 jmg Exp $ + * $DragonFly: src/sys/bus/usb/hid.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -39,7 +41,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -enum hid_kind { +enum hid_kind { hid_input, hid_output, hid_feature, hid_collection, hid_endcollection }; @@ -85,8 +87,8 @@ 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, +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/bus/usb/kue_fw.h b/sys/bus/usb/kue_fw.h index 9e68010418..d1fa0aaf77 100644 --- a/sys/bus/usb/kue_fw.h +++ b/sys/bus/usb/kue_fw.h @@ -29,8 +29,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/kue_fw.h,v 1.1 2000/01/05 04:27:07 wpaul Exp $ - * $DragonFly: src/sys/bus/usb/kue_fw.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/kue_fw.h,v 1.2 2000/04/03 20:58:23 n_hibma Exp $ + * $DragonFly: src/sys/bus/usb/kue_fw.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ */ /* @@ -86,7 +86,7 @@ #define KUE_QTINTR_LOAD_CODE_HIGH 0x9C /* Firmware code segment */ -static unsigned char kue_code_seg[] = +Static unsigned char kue_code_seg[] = { /******************************************/ /* NOTE: B6/C3 is data header signature */ @@ -578,7 +578,7 @@ static unsigned char kue_code_seg[] = }; /* Firmware fixup (data?) segment */ -static unsigned char kue_fix_seg[] = +Static unsigned char kue_fix_seg[] = { /******************************************/ /* NOTE: B6/C3 is data header signature */ @@ -681,6 +681,6 @@ static unsigned char kue_fix_seg[] = /* Fixup command. */ #define KUE_TRIGCMD_OFFSET 5 -static unsigned char kue_trig_seg[] = { +Static unsigned char kue_trig_seg[] = { 0xb6, 0xc3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00 }; diff --git a/sys/bus/usb/ohci.c b/sys/bus/usb/ohci.c index d530dd2c00..46578207cf 100644 --- a/sys/bus/usb/ohci.c +++ b/sys/bus/usb/ohci.c @@ -1,6 +1,14 @@ -/* $NetBSD: ohci.c,v 1.64 2000/01/19 00:23:58 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/ohci.c,v 1.39.2.9 2003/03/05 17:09:44 shiba Exp $ */ -/* $DragonFly: src/sys/bus/usb/ohci.c,v 1.4 2003/08/07 21:16:47 dillon Exp $ */ +/* + * $NetBSD: ohci.c,v 1.138 2003/02/08 03:32:50 ichiro Exp $ + * $FreeBSD: src/sys/dev/usb/ohci.c,v 1.141 2003/12/22 15:40:10 shiba Exp $ + * $DragonFly: src/sys/bus/usb/ohci.c,v 1.5 2003/12/30 01:01:44 dillon 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 $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -42,18 +50,19 @@ /* * USB Open Host Controller driver. * - * OHCI spec: ftp://ftp.compaq.com/pub/supportinformation/papers/hcir1_0a.exe - * USB spec: http://www.usb.org/developers/data/usb11.pdf + * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html + * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #include #include #include -#if defined(__NetBSD__) || defined(__OpenBSD__) #include +#if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #elif defined(__FreeBSD__) +#include #include #include #include @@ -97,6 +106,9 @@ 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) @@ -106,10 +118,14 @@ SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RW, * 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 LE(x) (bswap32(x)) +#define htole32(x) (bswap32(x)) +#define le32toh(x) (bswap32(x)) #else -#define LE(x) (x) +#define htole32(x) (x) +#define le32toh(x) (x) +#endif #endif struct ohci_pipe; @@ -124,11 +140,11 @@ 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 *); +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, u_int16_t, usb_dma_t *, + ohci_softc_t *, int, int, usbd_xfer_handle, ohci_soft_td_t *, ohci_soft_td_t **); #if defined(__NetBSD__) || defined(__OpenBSD__) @@ -137,27 +153,25 @@ 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_waitintr(ohci_softc_t *, - usbd_xfer_handle); +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 void ohci_process_done(ohci_softc_t *, - ohci_physaddr_t); 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_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 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 *); @@ -167,67 +181,79 @@ 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 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 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 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 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 void ohci_device_isoc_done(usbd_xfer_handle); -Static usbd_status ohci_device_setintr(ohci_softc_t *sc, +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, char *); +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 pipe, - ohci_soft_ed_t *head); -Static void ohci_abort_xfer(usbd_xfer_handle xfer, - usbd_status status); -Static void ohci_abort_xfer_end(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 OWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)) -#define OREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r)) -#define OREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r)) +#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] = +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, @@ -236,6 +262,7 @@ Static u_int8_t revbits[OHCI_NO_INTRS] = 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; @@ -269,6 +296,7 @@ struct ohci_pipe { Static struct usbd_bus_methods ohci_bus_methods = { ohci_open, + ohci_softintr, ohci_poll, ohci_allocm, ohci_freem, @@ -276,16 +304,16 @@ Static struct usbd_bus_methods ohci_bus_methods = { ohci_freex, }; -Static struct usbd_pipe_methods ohci_root_ctrl_methods = { +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, - 0, + ohci_root_ctrl_done, }; -Static struct usbd_pipe_methods ohci_root_intr_methods = { +Static struct usbd_pipe_methods ohci_root_intr_methods = { ohci_root_intr_transfer, ohci_root_intr_start, ohci_root_intr_abort, @@ -294,7 +322,7 @@ Static struct usbd_pipe_methods ohci_root_intr_methods = { ohci_root_intr_done, }; -Static struct usbd_pipe_methods ohci_device_ctrl_methods = { +Static struct usbd_pipe_methods ohci_device_ctrl_methods = { ohci_device_ctrl_transfer, ohci_device_ctrl_start, ohci_device_ctrl_abort, @@ -303,7 +331,7 @@ Static struct usbd_pipe_methods ohci_device_ctrl_methods = { ohci_device_ctrl_done, }; -Static struct usbd_pipe_methods ohci_device_intr_methods = { +Static struct usbd_pipe_methods ohci_device_intr_methods = { ohci_device_intr_transfer, ohci_device_intr_start, ohci_device_intr_abort, @@ -312,7 +340,7 @@ Static struct usbd_pipe_methods ohci_device_intr_methods = { ohci_device_intr_done, }; -Static struct usbd_pipe_methods ohci_device_bulk_methods = { +Static struct usbd_pipe_methods ohci_device_bulk_methods = { ohci_device_bulk_transfer, ohci_device_bulk_start, ohci_device_bulk_abort, @@ -340,11 +368,11 @@ ohci_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: if (sc->sc_child != NULL) rv = config_deactivate(sc->sc_child); + sc->sc_dying = 1; break; } return (rv); @@ -357,14 +385,19 @@ ohci_detach(struct ohci_softc *sc, int flags) if (sc->sc_child != NULL) rv = config_detach(sc->sc_child, flags); - + if (rv != 0) return (rv); -#if defined(__NetBSD__) + 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); @@ -384,10 +417,10 @@ ohci_alloc_sed(ohci_softc_t *sc) err = usb_allocmem(&sc->sc_bus, OHCI_SED_SIZE * OHCI_SED_CHUNK, OHCI_ED_ALIGN, &dma); if (err) - return (0); + return (NULL); for(i = 0; i < OHCI_SED_CHUNK; i++) { offs = i * OHCI_SED_SIZE; - sed = (ohci_soft_ed_t *)((char *)KERNADDR(&dma, offs)); + sed = KERNADDR(&dma, offs); sed->physaddr = DMAADDR(&dma, offs); sed->next = sc->sc_freeeds; sc->sc_freeeds = sed; @@ -421,21 +454,24 @@ ohci_alloc_std(ohci_softc_t *sc) err = usb_allocmem(&sc->sc_bus, OHCI_STD_SIZE * OHCI_STD_CHUNK, OHCI_TD_ALIGN, &dma); if (err) - return (0); + return (NULL); + s = splusb(); for(i = 0; i < OHCI_STD_CHUNK; i++) { offs = i * OHCI_STD_SIZE; - std = (ohci_soft_td_t *)((char *)KERNADDR(&dma, offs)); + 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; - - s = splusb(); + std->xfer = NULL; ohci_hash_add_td(sc, std); splx(s); @@ -449,54 +485,62 @@ ohci_free_std(ohci_softc_t *sc, ohci_soft_td_t *std) s = splusb(); ohci_hash_rem_td(sc, std); - splx(s); - 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 len, int rd, u_int16_t flags, usb_dma_t *dma, - ohci_soft_td_t *std, ohci_soft_td_t **rstd) + 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, dataphysend; - u_int32_t intr, tdflags; + ohci_physaddr_t dataphys; + u_int32_t tdflags; int offset = 0; - int curlen; + int len, curlen; + usb_dma_t *dma = &xfer->dmabuf; + u_int16_t flags = xfer->flags; - DPRINTFN(len < 4096,("ohci_alloc_std_chain: start len=%d\n", len)); + DPRINTFN(alen < 4096,("ohci_alloc_std_chain: start len=%d\n", alen)); - cur = std; + len = alen; + cur = sp; - dataphysend = OHCI_PAGE(DMAADDR(dma, len - 1)); - tdflags = - (rd ? OHCI_TD_IN : OHCI_TD_OUT) | - OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | - (flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0); + 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 == 0) + if (next == NULL) goto nomem; dataphys = DMAADDR(dma, offset); - /* The OHCI hardware can handle at most one page crossing. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) - if (OHCI_PAGE(dataphys) == dataphysend || - OHCI_PAGE(dataphys) + OHCI_PAGE_SIZE == dataphysend) -#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. + /* + * 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) == dataphysend) -#endif - { + 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 { @@ -507,62 +551,62 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc, * the case of an mbuf cluster). You'll get an early * short packet. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) /* must use multiple TDs, fill as much as possible. */ - curlen = 2 * OHCI_PAGE_SIZE - - OHCI_PAGE_MASK(dataphys); - if (curlen > len) /* may have fit in one page */ - curlen = len; -#elif defined(__FreeBSD__) - /* See comment above (XXX) */ - curlen = OHCI_PAGE_SIZE - - OHCI_PAGE_MASK(dataphys); + 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 " - "dataphysend=0x%08x len=%d curlen=%d\n", - dataphys, dataphysend, - len, curlen)); + "len=%d curlen=%d\n", + dataphys, len, curlen)); len -= curlen; - intr = len == 0 ? OHCI_TD_SET_DI(1) : OHCI_TD_NOINTR; - cur->td.td_flags = LE(tdflags | intr); - cur->td.td_cbp = LE(dataphys); + cur->td.td_flags = tdflags; + cur->td.td_cbp = htole32(dataphys); cur->nexttd = next; - cur->td.td_nexttd = LE(next->physaddr); - cur->td.td_be = LE(dataphys + curlen - 1); + 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) && - len % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize) == 0) { + alen % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize) == 0) { /* Force a 0 length transfer at the end. */ - cur->td.td_flags = LE(tdflags | OHCI_TD_NOINTR); cur = next; next = ohci_alloc_std(sc); - if (next == 0) + if (next == NULL) goto nomem; - cur->td.td_flags = LE(tdflags | OHCI_TD_SET_DI(1)); + cur->td.td_flags = tdflags; cur->td.td_cbp = 0; /* indicate 0 length packet */ cur->nexttd = next; - cur->td.td_nexttd = LE(next->physaddr); + 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")); } - cur->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; - *rstd = next; + *ep = cur; return (USBD_NORMAL_COMPLETION); @@ -590,35 +634,63 @@ ohci_alloc_sitd(ohci_softc_t *sc) { ohci_soft_itd_t *sitd; usbd_status err; - int i, offs; + 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_STD_SIZE * OHCI_STD_CHUNK, - OHCI_TD_ALIGN, &dma); + err = usb_allocmem(&sc->sc_bus, OHCI_SITD_SIZE * OHCI_SITD_CHUNK, + OHCI_ITD_ALIGN, &dma); if (err) - return (0); - for(i = 0; i < OHCI_STD_CHUNK; i++) { - offs = i * OHCI_STD_SIZE; - sitd = (ohci_soft_itd_t *)((char*)KERNADDR(&dma, offs)); + 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 = 0; + 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 @@ -627,7 +699,7 @@ ohci_init(ohci_softc_t *sc) ohci_soft_ed_t *sed, *psed; usbd_status err; int i; - u_int32_t s, ctl, ival, hcr, fm, per, rev; + u_int32_t rev; DPRINTF(("ohci_init: start\n")); #if defined(__OpenBSD__) @@ -640,7 +712,7 @@ ohci_init(ohci_softc_t *sc) OHCI_REV_LEGACY(rev) ? ", legacy support" : ""); if (OHCI_REV_HI(rev) != 1 || OHCI_REV_LO(rev) != 0) { - printf("%s: unsupported OHCI revision\n", + printf("%s: unsupported OHCI revision\n", USBDEVNAME(sc->sc_bus.bdev)); sc->sc_bus.usbrev = USBREV_UNKNOWN; return (USBD_INVAL); @@ -649,15 +721,18 @@ ohci_init(ohci_softc_t *sc) 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, + err = usb_allocmem(&sc->sc_bus, OHCI_HCCA_SIZE, OHCI_HCCA_ALIGN, &sc->sc_hccadma); if (err) return (err); - sc->sc_hcca = (struct ohci_hcca *)KERNADDR(&sc->sc_hccadma, 0); + sc->sc_hcca = KERNADDR(&sc->sc_hccadma, 0); memset(sc->sc_hcca, 0, OHCI_HCCA_SIZE); sc->sc_eintrs = OHCI_NORMAL_INTRS; @@ -668,7 +743,7 @@ ohci_init(ohci_softc_t *sc) err = USBD_NOMEM; goto bad1; } - sc->sc_ctrl_head->ed.ed_flags |= LE(OHCI_ED_SKIP); + 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); @@ -676,7 +751,7 @@ ohci_init(ohci_softc_t *sc) err = USBD_NOMEM; goto bad2; } - sc->sc_bulk_head->ed.ed_flags |= LE(OHCI_ED_SKIP); + 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); @@ -684,7 +759,7 @@ ohci_init(ohci_softc_t *sc) err = USBD_NOMEM; goto bad3; } - sc->sc_isoc_head->ed.ed_flags |= LE(OHCI_ED_SKIP); + 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++) { @@ -697,21 +772,70 @@ ohci_init(ohci_softc_t *sc) } /* All ED fields are set to 0. */ sc->sc_eds[i] = sed; - sed->ed.ed_flags |= LE(OHCI_ED_SKIP); + 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 = LE(psed->physaddr); + 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]] = - LE(sc->sc_eds[OHCI_NO_EDS-OHCI_NO_INTRS+i]->physaddr); + 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_ctrl_head); + bad2: + ohci_free_sed(sc, sc->sc_bulk_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); @@ -730,6 +854,8 @@ ohci_init(ohci_softc_t *sc) 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")); @@ -737,6 +863,7 @@ ohci_init(ohci_softc_t *sc) 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: @@ -765,8 +892,7 @@ ohci_init(ohci_softc_t *sc) } if (hcr) { printf("%s: reset timeout\n", USBDEVNAME(sc->sc_bus.bdev)); - err = USBD_IOERROR; - goto bad5; + return (USBD_IOERROR); } #ifdef USB_DEBUG if (ohcidebug > 15) @@ -801,58 +927,37 @@ ohci_init(ohci_softc_t *sc) per = OHCI_PERIODIC(ival); /* 90% periodic */ OWRITE4(sc, OHCI_PERIODIC_START, per); - OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); /* Enable port power */ + /* 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. + */ + 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 - - /* Set up the bus struct. */ - sc->sc_bus.methods = &ohci_bus_methods; - sc->sc_bus.pipe_size = sizeof(struct ohci_pipe); - -#if defined(__NetBSD__) - sc->sc_powerhook = powerhook_establish(ohci_power, sc); - sc->sc_shutdownhook = shutdownhook_establish(ohci_shutdown, sc); -#endif - 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_ctrl_head); - bad2: - ohci_free_sed(sc, sc->sc_bulk_head); - bad1: - usb_freemem(&sc->sc_bus, &sc->sc_hccadma); - return (err); } usbd_status ohci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) { -#if defined(__NetBSD__) || defined(__OpenBSD__) - struct ohci_softc *sc = (struct ohci_softc *)bus; -#endif - - return (usb_allocmem(&sc->sc_bus, size, 0, dma)); + return (usb_allocmem(bus, size, 0, dma)); } void ohci_freem(struct usbd_bus *bus, usb_dma_t *dma) { -#if defined(__NetBSD__) || defined(__OpenBSD__) - struct ohci_softc *sc = (struct ohci_softc *)bus; -#endif - - usb_freemem(&sc->sc_bus, dma); + usb_freemem(bus, dma); } usbd_xfer_handle @@ -862,12 +967,23 @@ ohci_allocx(struct usbd_bus *bus) usbd_xfer_handle xfer; xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); - if (xfer != NULL) - SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next); - else - xfer = malloc(sizeof(*xfer), M_USB, M_NOWAIT); - if (xfer != NULL) - memset(xfer, 0, sizeof *xfer); + 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); } @@ -875,14 +991,29 @@ 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. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) void ohci_shutdown(void *v) { @@ -902,15 +1033,55 @@ ohci_shutdown(void *v) void ohci_power(int why, void *v) { -#ifdef USB_DEBUG ohci_softc_t *sc = v; + u_int32_t ctl; + int s; +#ifdef USB_DEBUG DPRINTF(("ohci_power: sc=%p, why=%d\n", sc, why)); - /* XXX should suspend/resume */ 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); } -#endif #ifdef USB_DEBUG void @@ -948,8 +1119,8 @@ ohci_dumpregs(ohci_softc_t *sc) 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", - LE(sc->sc_hcca->hcca_frame_number), - LE(sc->sc_hcca->hcca_done_head))); + le32toh(sc->sc_hcca->hcca_frame_number), + le32toh(sc->sc_hcca->hcca_done_head))); } #endif @@ -960,6 +1131,9 @@ 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 @@ -968,7 +1142,7 @@ ohci_intr(void *p) return (0); } - return (ohci_intr1(sc)); + return (ohci_intr1(sc)); } Static int @@ -977,6 +1151,8 @@ 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 @@ -985,8 +1161,8 @@ ohci_intr1(ohci_softc_t *sc) return (0); } - intrs = 0; - done = LE(sc->sc_hcca->hcca_done_head); + 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 @@ -1000,46 +1176,45 @@ ohci_intr1(ohci_softc_t *sc) * HcInterruptStatus should be checked to determine its cause. */ if (done != 0) { - sc->sc_hcca->hcca_done_head = 0; if (done & ~OHCI_DONE_INTRS) intrs = OHCI_WDH; if (done & OHCI_DONE_INTRS) { intrs |= OREAD4(sc, OHCI_INTERRUPT_STATUS); done &= ~OHCI_DONE_INTRS; } - } else { - intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS); - } + sc->sc_hcca->hcca_done_head = 0; + } else + intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS) & ~OHCI_WDH; - if (intrs == 0) { - /* nothing to be done ?! */ + if (intrs == 0) /* nothing to be done (PCI shared interrupt) */ return (0); - } - - intrs &= ~OHCI_MIE; /* mask out Master Interrupt Enable */ - /* Acknowledge any interrupts that have happened */ - OWRITE4(sc, OHCI_INTERRUPT_STATUS, intrs); - - /* Any interrupts we had enabled? */ + 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=%x(%x) eintr=%x\n", + 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) { - printf("%s: scheduling overrun\n",USBDEVNAME(sc->sc_bus.bdev)); + 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 */ - intrs &= ~OHCI_SO; + eintrs &= ~OHCI_SO; } if (eintrs & OHCI_WDH) { - ohci_process_done(sc, done); - intrs &= ~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)); @@ -1053,20 +1228,25 @@ ohci_intr1(ohci_softc_t *sc) } if (eintrs & OHCI_RHSC) { ohci_rhsc(sc, sc->sc_intrxfer); - intrs &= ~OHCI_RHSC; - - /* + /* * 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--; - /* Block unprocessed interrupts. XXX */ - OWRITE4(sc, OHCI_INTERRUPT_DISABLE, intrs); - sc->sc_eintrs &= ~intrs; + 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); } @@ -1084,6 +1264,17 @@ ohci_rhsc_able(ohci_softc_t *sc, int on) } } +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", @@ -1106,77 +1297,113 @@ char *ohci_cc_strs[] = { #endif void -ohci_process_done(ohci_softc_t *sc, ohci_physaddr_t done) +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_soft_td_t *std, *sdone, *stdnext; + ohci_softc_t *sc = v; + ohci_soft_itd_t *sitd, *sidone, *sitdnext; + ohci_soft_td_t *std, *sdone, *stdnext; usbd_xfer_handle xfer; - int len, cc; + struct ohci_pipe *opipe; + int len, cc, s; - DPRINTFN(10,("ohci_process_done: done=0x%08lx\n", (u_long)done)); + DPRINTFN(10,("ohci_softintr: enter\n")); - /* Reverse the done list and store the reversed list in sdone */ - sdone = NULL; - for (; done; done = LE(std->td.td_nexttd)) { - std = ohci_hash_find_td(sc, done & LE(OHCI_TAILMASK)); - if (std == NULL) { -#ifdef USB_DEBUG - DPRINTF(("%s: Invalid done queue 0x%08x", - USBDEVNAME(sc->sc_bus.bdev), done)); - ohci_dumpregs(sc); -#endif - /* XXX Should we compare the list of active TDs with - * the list of TDs queued at EDs to handle the ones that - * are not listed on any of the ED queues and therefore - * must be finished? - */ - return; - } + sc->sc_bus.intr_context++; - std->dnext = sdone; - sdone = std; - } + 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")); - for (std = sdone; std; std = std->dnext) - ohci_dump_td(sdone); + ohci_dump_tds(sdone); } #endif for (std = sdone; std; std = stdnext) { xfer = std->xfer; stdnext = std->dnext; - DPRINTFN(5, ("ohci_process_done: std=%p xfer=%p hcpriv=%p\n", - std, xfer, (xfer? xfer->hcpriv:NULL))); + 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 + /* + * 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; } - cc = OHCI_TD_GET_CC(LE(std->td.td_flags)); - usb_untimeout(ohci_timeout, xfer, xfer->timo_handle); if (xfer->status == USBD_CANCELLED || xfer->status == USBD_TIMEOUT) { - DPRINTF(("ohci_process_done: cancel/timeout, xfer=%p\n", + DPRINTF(("ohci_process_done: cancel/timeout %p\n", xfer)); /* Handled by abort routine. */ - } else if (cc == OHCI_CC_NO_ERROR) { - DPRINTFN(15, ("ohci_process_done: no error, xfer=%p\n", - xfer)); - len = std->len; - if (std->td.td_cbp != 0) - len -= LE(std->td.td_be) - - LE(std->td.td_cbp) + 1; - if (std->flags & OHCI_ADD_LEN) - xfer->actlen += len; + 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 { @@ -1186,13 +1413,12 @@ ohci_process_done(ohci_softc_t *sc, ohci_physaddr_t done) * the endpoint. */ ohci_soft_td_t *p, *n; - struct ohci_pipe *opipe = - (struct ohci_pipe *)xfer->pipe; + 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))])); - DPRINTF(("ohci_process_done: err cc=%d (%s), xfer=%p\n", - OHCI_TD_GET_CC(LE(std->td.td_flags)), - ohci_cc_strs[OHCI_TD_GET_CC(LE(std->td.td_flags))], - xfer)); /* Mark all the TDs in the done queue for the current * xfer as handled @@ -1202,34 +1428,100 @@ ohci_process_done(ohci_softc_t *sc, ohci_physaddr_t done) p->flags |= OHCI_TD_HANDLED; } - /* remove TDs for the current xfer from the ED */ + /* remove TDs */ for (p = std; p->xfer == xfer; p = n) { n = p->nexttd; ohci_free_std(sc, p); } - opipe->sed->ed.ed_headp = LE(p->physaddr); - /* XXX why is this being done? Why not OHCI_BLF too */ + /* 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_ctrl_done: xfer=%p\n", xfer)); + DPRINTFN(10,("ohci_device_ctrl_done: xfer=%p\n", xfer)); #ifdef DIAGNOSTIC if (!(xfer->rqflags & URQ_REQUEST)) { - panic("ohci_ctrl_done: not a request\n"); + panic("ohci_device_ctrl_done: not a request"); } #endif xfer->hcpriv = NULL; @@ -1244,7 +1536,7 @@ ohci_device_intr_done(usbd_xfer_handle xfer) ohci_soft_td_t *data, *tail; - DPRINTFN(10,("ohci_intr_done: xfer=%p, actlen=%d\n", + DPRINTFN(10,("ohci_device_intr_done: xfer=%p, actlen=%d\n", xfer, xfer->actlen)); xfer->hcpriv = NULL; @@ -1257,24 +1549,24 @@ ohci_device_intr_done(usbd_xfer_handle xfer) return; } tail->xfer = NULL; - - data->td.td_flags = LE( - OHCI_TD_IN | OHCI_TD_NOCC | + + 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 |= LE(OHCI_TD_R); - data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf, 0)); + data->td.td_flags |= htole32(OHCI_TD_R); + data->td.td_cbp = htole32(DMAADDR(&xfer->dmabuf, 0)); data->nexttd = tail; - data->td.td_nexttd = LE(tail->physaddr); - data->td.td_be = LE(LE(data->td.td_cbp) + xfer->length - 1); + 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; - ohci_hash_add_td(sc, data); - sed->ed.ed_tailp = LE(tail->physaddr); + sed->ed.ed_tailp = htole32(tail->physaddr); opipe->tail.td = tail; } } @@ -1282,7 +1574,7 @@ ohci_device_intr_done(usbd_xfer_handle xfer) void ohci_device_bulk_done(usbd_xfer_handle xfer) { - DPRINTFN(10,("ohci_bulk_done: xfer=%p, actlen=%d\n", + DPRINTFN(10,("ohci_device_bulk_done: xfer=%p, actlen=%d\n", xfer, xfer->actlen)); xfer->hcpriv = NULL; @@ -1292,13 +1584,12 @@ void ohci_rhsc(ohci_softc_t *sc, usbd_xfer_handle xfer) { usbd_pipe_handle pipe; - struct ohci_pipe *opipe; 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", + DPRINTF(("ohci_rhsc: sc=%p xfer=%p hstatus=0x%08x\n", sc, xfer, hstatus)); if (xfer == NULL) { @@ -1307,12 +1598,12 @@ ohci_rhsc(ohci_softc_t *sc, usbd_xfer_handle xfer) } pipe = xfer->pipe; - opipe = (struct ohci_pipe *)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); } @@ -1329,6 +1620,12 @@ 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 @@ -1344,6 +1641,8 @@ ohci_waitintr(ohci_softc_t *sc, usbd_xfer_handle xfer) 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 @@ -1359,9 +1658,6 @@ ohci_waitintr(ohci_softc_t *sc, usbd_xfer_handle xfer) /* Timeout */ DPRINTF(("ohci_waitintr: timeout\n")); -#ifdef USB_DEBUG - ohci_dumpregs(sc); -#endif xfer->status = USBD_TIMEOUT; usb_transfer_complete(xfer); /* XXX should free TD */ @@ -1371,6 +1667,15 @@ 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); @@ -1384,7 +1689,7 @@ ohci_device_request(usbd_xfer_handle xfer) 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, *data = 0, *stat, *next, *tail; + ohci_soft_td_t *setup, *stat, *next, *tail; ohci_soft_ed_t *sed; int isread; int len; @@ -1397,7 +1702,7 @@ ohci_device_request(usbd_xfer_handle xfer) 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, + UGETW(req->wIndex), len, addr, opipe->pipe.endpoint->edesc->bEndpointAddress)); setup = opipe->tail.td; @@ -1418,58 +1723,49 @@ ohci_device_request(usbd_xfer_handle xfer) /* Update device address and length since they may have changed. */ /* XXX This only needs to be done once, but it's too early in open. */ - sed->ed.ed_flags = LE( - (LE(sed->ed.ed_flags) & ~(OHCI_ED_ADDRMASK | OHCI_ED_MAXPMASK)) | + /* 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) { - data = ohci_alloc_std(sc); - if (data == NULL) { - err = USBD_NOMEM; - goto bad3; - } - data->td.td_flags = LE( - (isread ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC | - OHCI_TD_TOGGLE_1 | OHCI_TD_NOINTR | - (xfer->flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0)); - data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf, 0)); - data->nexttd = stat; - data->td.td_nexttd = LE(stat->physaddr); - data->td.td_be = LE(LE(data->td.td_cbp) + len - 1); - data->len = len; - data->xfer = xfer; - data->flags = OHCI_ADD_LEN; + ohci_soft_td_t *std = stat; - next = data; - stat->flags = OHCI_CALL_DONE; - } else { - next = stat; - /* XXX ADD_LEN? */ - stat->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; + 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 = LE(OHCI_TD_SETUP | OHCI_TD_NOCC | - OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); - setup->td.td_cbp = LE(DMAADDR(&opipe->u.ctl.reqdma, 0)); + 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 = LE(next->physaddr); - setup->td.td_be = LE(LE(setup->td.td_cbp) + sizeof *req - 1); - setup->len = 0; /* XXX The number of byte we count */ + 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 = LE( - (isread ? OHCI_TD_OUT : OHCI_TD_IN) | OHCI_TD_NOCC | - OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); + 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 = LE(tail->physaddr); + stat->td.td_nexttd = htole32(tail->physaddr); stat->td.td_be = 0; + stat->flags = OHCI_CALL_DONE; stat->len = 0; stat->xfer = xfer; @@ -1483,20 +1779,24 @@ ohci_device_request(usbd_xfer_handle xfer) /* Insert ED in schedule */ s = splusb(); - sed->ed.ed_tailp = LE(tail->physaddr); + 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_timeout(ohci_timeout, xfer, - MS_TO_TICKS(xfer->timeout), xfer->timo_handle); + usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + ohci_timeout, xfer); } splx(s); #ifdef USB_DEBUG - if (ohcidebug > 25) { - usb_delay_ms(&sc->sc_bus, 5); + 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); } @@ -1518,11 +1818,13 @@ ohci_device_request(usbd_xfer_handle xfer) 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 = LE(sed->physaddr); + head->ed.ed_nexted = htole32(sed->physaddr); } /* @@ -1531,15 +1833,15 @@ ohci_add_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head) void ohci_rem_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head) { - ohci_soft_ed_t *p; + ohci_soft_ed_t *p; SPLUSBCHECK; /* XXX */ - for (p = head; p == NULL && p->next != sed; p = p->next) + for (p = head; p != NULL && p->next != sed; p = p->next) ; if (p == NULL) - panic("ohci_rem_ed: ED not found\n"); + panic("ohci_rem_ed: ED not found"); p->next = sed->next; p->ed.ed_nexted = sed->ed.ed_nexted; } @@ -1584,11 +1886,11 @@ ohci_hash_find_td(ohci_softc_t *sc, ohci_physaddr_t a) /* if these are present they should be masked out at an earlier * stage. */ - KASSERT((a&~OHCI_TAILMASK) == 0, ("%s: 0x%b has lower bits set\n", + 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]); + for (std = LIST_FIRST(&sc->sc_hash_tds[h]); std != NULL; std = LIST_NEXT(std, hnext)) if (std->physaddr == a) @@ -1596,21 +1898,78 @@ ohci_hash_find_td(ohci_softc_t *sc, ohci_physaddr_t a) DPRINTF(("%s: ohci_hash_find_td: addr 0x%08lx not found\n", USBDEVNAME(sc->sc_bus.bdev), (u_long) a)); - return NULL; + 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: xfer=%p\n", xfer)); + DPRINTF(("ohci_timeout_task: xfer=%p\n", xfer)); s = splusb(); - xfer->device->bus->intr_context++; ohci_abort_xfer(xfer, USBD_TIMEOUT); - xfer->device->bus->intr_context--; splx(s); } @@ -1625,34 +1984,71 @@ ohci_dump_tds(ohci_soft_td_t *std) void ohci_dump_td(ohci_soft_td_t *std) { - DPRINTF(("TD(%p) at %08lx: %b delay=%d ec=%d cc=%d\ncbp=0x%08lx " - "nexttd=0x%08lx be=0x%08lx\n", - std, (u_long)std->physaddr, - (int)LE(std->td.td_flags), - "\20\23R\24OUT\25IN\31TOG1\32SETTOGGLE", - OHCI_TD_GET_DI(LE(std->td.td_flags)), - OHCI_TD_GET_EC(LE(std->td.td_flags)), - OHCI_TD_GET_CC(LE(std->td.td_flags)), - (u_long)LE(std->td.td_cbp), - (u_long)LE(std->td.td_nexttd), (u_long)LE(std->td.td_be))); + 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) { - DPRINTF(("ED(%p) at %08lx: addr=%d endpt=%d maxp=%d %b\n" - "tailp=0x%8b headp=0x%8b nexted=0x%08lx\n", - sed, (u_long)sed->physaddr, - OHCI_ED_GET_FA(LE(sed->ed.ed_flags)), - OHCI_ED_GET_EN(LE(sed->ed.ed_flags)), - OHCI_ED_GET_MAXP(LE(sed->ed.ed_flags)), - (int)LE(sed->ed.ed_flags), - "\20\14OUT\15IN\16LOWSPEED\17SKIP\20ISO", - (int)(uintptr_t)LE(sed->ed.ed_tailp), - "\20\1BIT1\2BIT2", - (int)(uintptr_t)LE(sed->ed.ed_headp), - "\20\1HALT\2CARRY", - (u_long)LE(sed->ed.ed_nexted))); + 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 @@ -1666,7 +2062,7 @@ ohci_open(usbd_pipe_handle 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 = NULL; + ohci_soft_td_t *std; ohci_soft_itd_t *sitd; ohci_physaddr_t tdphys; u_int32_t fmt; @@ -1676,6 +2072,13 @@ ohci_open(usbd_pipe_handle pipe) 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: @@ -1694,36 +2097,37 @@ ohci_open(usbd_pipe_handle pipe) opipe->sed = sed; if (xfertype == UE_ISOCHRONOUS) { sitd = ohci_alloc_sitd(sc); - if (sitd == NULL) { - ohci_free_sitd(sc, sitd); + if (sitd == NULL) goto bad1; - } opipe->tail.itd = sitd; - tdphys = LE(sitd->physaddr); + 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) { - ohci_free_std(sc, std); + if (std == NULL) goto bad1; - } opipe->tail.td = std; - tdphys = LE(std->physaddr); - fmt = OHCI_ED_FORMAT_GEN; + tdphys = std->physaddr; + fmt = OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD; } - sed->ed.ed_flags = LE( - OHCI_ED_SET_FA(addr) | + sed->ed.ed_flags = htole32( + OHCI_ED_SET_FA(addr) | OHCI_ED_SET_EN(ed->bEndpointAddress) | - OHCI_ED_DIR_TD | - (dev->lowspeed ? OHCI_ED_SPEED : 0) | fmt | + (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 = tdphys; + 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), + err = usb_allocmem(&sc->sc_bus, + sizeof(usb_device_request_t), 0, &opipe->u.ctl.reqdma); if (err) goto bad; @@ -1751,12 +2155,14 @@ ohci_open(usbd_pipe_handle pipe) return (USBD_NORMAL_COMPLETION); bad: - ohci_free_std(sc, std); + if (std != NULL) + ohci_free_std(sc, std); bad1: - ohci_free_sed(sc, sed); + if (sed != NULL) + ohci_free_sed(sc, sed); bad0: return (USBD_NOMEM); - + } /* @@ -1773,32 +2179,38 @@ ohci_close_pipe(usbd_pipe_handle pipe, ohci_soft_ed_t *head) s = splusb(); #ifdef DIAGNOSTIC - sed->ed.ed_flags |= LE(OHCI_ED_SKIP); - if ((sed->ed.ed_tailp & LE(OHCI_TAILMASK)) - != (sed->ed.ed_headp & LE(OHCI_HEADMASK))) { - ohci_physaddr_t td = sed->ed.ed_headp; + 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; - for (std = LIST_FIRST(&sc->sc_hash_tds[HASH(td)]); - std != NULL; - std = LIST_NEXT(std, hnext)) - if (std->physaddr == td) - break; + 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)LE(sed->ed.ed_headp), (int)LE(sed->ed.ed_tailp), + (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 ((sed->ed.ed_tailp & LE(OHCI_TAILMASK)) - != (sed->ed.ed_headp & LE(OHCI_HEADMASK))) + 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 @@ -1812,70 +2224,99 @@ void ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status) { struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; - ohci_soft_ed_t *sed; + 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\n", xfer, opipe)); + DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p sed=%p\n", xfer, opipe,sed)); - xfer->status = 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, ohci_timeout, xfer); + usb_transfer_complete(xfer); + splx(s); + } - usb_untimeout(ohci_timeout, xfer, xfer->timo_handle); + if (xfer->device->bus->intr_context /* || !curproc REMOVED DFly */) + panic("ohci_abort_xfer: not in process context"); - sed = opipe->sed; - sed->ed.ed_flags |= LE(OHCI_ED_SKIP); /* force hardware skip */ -#ifdef USB_DEBUG + /* + * 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)); - ohci_dump_ed(sed); -#endif - -#if 1 - if (xfer->device->bus->intr_context) { - /* We have no process context, so we can't use tsleep(). */ - timeout(ohci_abort_xfer_end, xfer, hz / USB_FRAMES_PER_SECOND); - } else { -#if defined(DIAGNOSTIC) && defined(__FreeBSD__) - KASSERT(mycpu->gd_intr_nesting_level == 0, - ("ohci_abort_req in interrupt context")); -#endif - usb_delay_ms(opipe->pipe.device->bus, 1); - ohci_abort_xfer_end(xfer); - } -#else - delay(1000); - ohci_abort_xfer_end(xfer); -#endif -} - -void -ohci_abort_xfer_end(void *v) -{ - usbd_xfer_handle xfer = v; - 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_td_t *p, *n; - int s; + 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) { - printf("ohci_abort_xfer: hcpriv==0\n"); 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")); + } - sed = opipe->sed; - DPRINTFN(2,("ohci_abort_xfer: set hd=%x, tl=%x\n", - (int)LE(p->physaddr), (int)LE(sed->ed.ed_tailp))); - sed->ed.ed_headp = p->physaddr; /* unlink TDs */ - sed->ed.ed_flags &= LE(~OHCI_ED_SKIP); /* remove hardware skip */ + /* + * 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); @@ -1942,7 +2383,7 @@ Static usb_hub_descriptor_t ohci_hubd = { }; Static int -ohci_str(usb_string_descriptor_t *p, int l, char *s) +ohci_str(usb_string_descriptor_t *p, int l, const char *s) { int i; @@ -1988,6 +2429,9 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) usbd_status err; u_int32_t v; + if (sc->sc_dying) + return (USBD_IOERROR); + #ifdef DIAGNOSTIC if (!(xfer->rqflags & URQ_REQUEST)) /* XXX panic */ @@ -1995,7 +2439,7 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) #endif req = &xfer->request; - DPRINTFN(4,("ohci_root_ctrl_control type=0x%02x request=%02x\n", + DPRINTFN(4,("ohci_root_ctrl_control type=0x%02x request=%02x\n", req->bmRequestType, req->bRequest)); len = UGETW(req->wLength); @@ -2010,7 +2454,7 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) 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. */ @@ -2134,6 +2578,7 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) 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: @@ -2178,13 +2623,13 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) hubd = ohci_hubd; hubd.bNbrPorts = sc->sc_noport; USETW(hubd.wHubCharacteristics, - (v & OHCI_NPS ? UHD_PWR_NO_SWITCH : + (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) + 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); @@ -2241,8 +2686,13 @@ ohci_root_ctrl_start(usbd_xfer_handle xfer) DPRINTFN(5,("ohci_root_ctrl_transfer: reset port %d\n", index)); OWRITE4(sc, port, UPS_RESET); - for (i = 0; i < 10; i++) { - usb_delay_ms(&sc->sc_bus, 10); + 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; } @@ -2308,6 +2758,9 @@ 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); @@ -2334,7 +2787,7 @@ 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; @@ -2362,6 +2815,9 @@ 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 */ @@ -2406,7 +2862,7 @@ ohci_device_clear_toggle(usbd_pipe_handle pipe) { struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; - opipe->sed->ed.ed_headp &= LE(~OHCI_TOGGLECARRY); + opipe->sed->ed.ed_headp &= htole32(~OHCI_TOGGLECARRY); } Static void @@ -2440,6 +2896,9 @@ ohci_device_bulk_start(usbd_xfer_handle xfer) int s, len, isread, endpt; usbd_status err; + if (sc->sc_dying) + return (USBD_IOERROR); + #ifdef DIAGNOSTIC if (xfer->rqflags & URQ_REQUEST) { /* XXX panic */ @@ -2461,14 +2920,19 @@ ohci_device_bulk_start(usbd_xfer_handle xfer) opipe->u.bulk.length = len; /* Update device address */ - sed->ed.ed_flags = LE( - (LE(sed->ed.ed_flags) & ~OHCI_ED_ADDRMASK) | + 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->flags, - &xfer->dmabuf, data, &tail); + 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); @@ -2477,11 +2941,13 @@ ohci_device_bulk_start(usbd_xfer_handle xfer) DPRINTFN(4,("ohci_device_bulk_start: ed_flags=0x%08x td_flags=0x%08x " "td_cbp=0x%08x td_be=0x%08x\n", - (int)LE(sed->ed.ed_flags), (int)LE(data->td.td_flags), - (int)LE(data->td.td_cbp), (int)LE(data->td.td_be))); + (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 > 4) { + if (ohcidebug > 5) { ohci_dump_ed(sed); ohci_dump_tds(data); } @@ -2491,21 +2957,20 @@ ohci_device_bulk_start(usbd_xfer_handle xfer) s = splusb(); for (tdp = data; tdp != tail; tdp = tdp->nexttd) { tdp->xfer = xfer; - ohci_hash_add_td(sc, tdp); } - sed->ed.ed_tailp = LE(tail->physaddr); + sed->ed.ed_tailp = htole32(tail->physaddr); opipe->tail.td = tail; - sed->ed.ed_flags &= LE(~OHCI_ED_SKIP); + sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); if (xfer->timeout && !sc->sc_bus.use_polling) { - usb_timeout(ohci_timeout, xfer, - MS_TO_TICKS(xfer->timeout), xfer->timo_handle); + 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 > 5) { - usb_delay_ms(&sc->sc_bus, 5); + if (ohcidebug > 10) { + delay(10000); DPRINTF(("ohci_device_intr_transfer: status=%x\n", OREAD4(sc, OHCI_COMMAND_STATUS))); ohci_dump_ed(sed); @@ -2525,7 +2990,7 @@ ohci_device_bulk_abort(usbd_xfer_handle xfer) ohci_abort_xfer(xfer, USBD_CANCELLED); } -/* +/* * Close a device bulk pipe. */ Static void @@ -2566,13 +3031,16 @@ ohci_device_intr_start(usbd_xfer_handle xfer) 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\n"); + panic("ohci_device_intr_transfer: a request"); #endif len = xfer->length; @@ -2583,15 +3051,15 @@ ohci_device_intr_start(usbd_xfer_handle xfer) return (USBD_NOMEM); tail->xfer = NULL; - data->td.td_flags = LE( - OHCI_TD_IN | OHCI_TD_NOCC | + 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 |= LE(OHCI_TD_R); - data->td.td_cbp = LE(DMAADDR(&xfer->dmabuf, 0)); + data->td.td_flags |= htole32(OHCI_TD_R); + data->td.td_cbp = htole32(DMAADDR(&xfer->dmabuf, 0)); data->nexttd = tail; - data->td.td_nexttd = LE(tail->physaddr); - data->td.td_be = LE(LE(data->td.td_cbp) + len - 1); + 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; @@ -2607,10 +3075,9 @@ ohci_device_intr_start(usbd_xfer_handle xfer) /* Insert ED in schedule */ s = splusb(); - ohci_hash_add_td(sc, data); - sed->ed.ed_tailp = LE(tail->physaddr); + sed->ed.ed_tailp = htole32(tail->physaddr); opipe->tail.td = tail; - sed->ed.ed_flags &= LE(~OHCI_ED_SKIP); + sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); #if 0 /* @@ -2657,14 +3124,14 @@ ohci_device_intr_close(usbd_pipe_handle pipe) DPRINTFN(1,("ohci_device_intr_close: pipe=%p nslots=%d pos=%d\n", pipe, nslots, pos)); s = splusb(); - sed->ed.ed_flags |= LE(OHCI_ED_SKIP); - if ((sed->ed.ed_tailp & LE(OHCI_TAILMASK)) - != (sed->ed.ed_headp & LE(OHCI_HEADMASK))) + 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 ((sed->ed.ed_tailp & LE(OHCI_TAILMASK)) - != (sed->ed.ed_headp & LE(OHCI_HEADMASK))) - panic("%s: Intr pipe %p still has TDs queued\n", + 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 @@ -2672,7 +3139,7 @@ ohci_device_intr_close(usbd_pipe_handle pipe) ; #ifdef DIAGNOSTIC if (p == NULL) - panic("ohci_device_intr_close: ED not found\n"); + panic("ohci_device_intr_close: ED not found"); #endif p->next = sed->next; p->ed.ed_nexted = sed->ed.ed_nexted; @@ -2727,7 +3194,7 @@ ohci_device_setintr(ohci_softc_t *sc, struct ohci_pipe *opipe, int ival) bestbw = bw; } } - DPRINTFN(2, ("ohci_setintr: best=%d(%d..%d) bestbw=%d\n", + DPRINTFN(2, ("ohci_setintr: best=%d(%d..%d) bestbw=%d\n", best, slow, shigh, bestbw)); s = splusb(); @@ -2735,7 +3202,7 @@ ohci_device_setintr(ohci_softc_t *sc, struct ohci_pipe *opipe, int ival) sed->next = hsed->next; sed->ed.ed_nexted = hsed->ed.ed_nexted; hsed->next = sed; - hsed->ed.ed_nexted = LE(sed->physaddr); + hsed->ed.ed_nexted = htole32(sed->physaddr); splx(s); for (j = 0; j < nslots; j++) @@ -2768,7 +3235,7 @@ ohci_device_isoc_transfer(usbd_xfer_handle xfer) /* insert into schedule, */ ohci_device_isoc_enter(xfer); - /* and put on interrupt list if the pipe wasn't running */ + /* and start if the pipe wasn't running */ if (!err) ohci_device_isoc_start(SIMPLEQ_FIRST(&xfer->pipe->queue)); @@ -2783,97 +3250,294 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer) ohci_softc_t *sc = (ohci_softc_t *)dev->bus; ohci_soft_ed_t *sed = opipe->sed; struct iso *iso = &opipe->u.iso; - ohci_soft_itd_t *sitd, *nsitd; - ohci_physaddr_t buf, offs; + 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 ncross = 0; int s; - s = splusb(); + 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); - sitd->itd.itd_bp0 = LE(buf & OHCI_ITD_PAGE_MASK); + bp0 = OHCI_PAGE(buf); + offs = OHCI_PAGE_OFFSET(buf); nframes = xfer->nframes; - offs = buf & OHCI_ITD_OFFSET_MASK; + xfer->hcpriv = sitd; for (i = ncur = 0; i < nframes; i++, ncur++) { + noffs = offs + xfer->frlengths[i]; if (ncur == OHCI_ITD_NOFFSET || /* all offsets used */ - ncross > 1) { /* too many page crossings */ - + 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? */ - splx(s); + printf("%s: isoc TD alloc failed\n", + USBDEVNAME(sc->sc_bus.bdev)); return; } - sitd->nextitd = nsitd; - sitd->itd.itd_nextitd = LE(nsitd->physaddr); - sitd->itd.itd_flags = LE( - OHCI_ITD_NOCC | + + /* Fill current ITD */ + sitd->itd.itd_flags = htole32( + OHCI_ITD_NOCC | OHCI_ITD_SET_SF(iso->next) | - OHCI_ITD_NOINTR | - OHCI_ITD_SET_FC(OHCI_ITD_NOFFSET)); - sitd->itd.itd_be = LE(LE(sitd->itd.itd_bp0) + offs - 1); - nsitd->itd.itd_bp0 = LE((buf + offs) & OHCI_ITD_PAGE_MASK); + 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; + iso->next = iso->next + ncur; + bp0 = OHCI_PAGE(buf + offs); ncur = 0; - ncross = 0; } - /* XXX byte order */ - sitd->itd.itd_offset[i] = - offs | (ncross == 1 ? OHCI_ITD_PAGE_SELECT : 0); - offs += xfer->frlengths[i]; - /* XXX update ncross */ + sitd->itd.itd_offset[ncur] = htole16(OHCI_ITD_MK_OFFS(offs)); + offs = noffs; } nsitd = ohci_alloc_sitd(sc); if (nsitd == NULL) { /* XXX what now? */ - splx(s); + printf("%s: isoc TD alloc failed\n", + USBDEVNAME(sc->sc_bus.bdev)); return; } - sitd->nextitd = nsitd; - sitd->itd.itd_nextitd = LE(nsitd->physaddr); - sitd->itd.itd_flags = LE( - OHCI_ITD_NOCC | + /* 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_be = LE(LE(sitd->itd.itd_bp0) + offs - 1); + 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_tailp = LE(nsitd->physaddr); - /* XXX update ED */ + 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) { - printf("ohci_device_isoc_start: not implemented\n"); - return (USBD_INVAL); + 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) { - printf("ohci_device_isoc_done: not implemented\n"); + /* 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); } @@ -2882,8 +3546,19 @@ 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)); - ohci_close_pipe(pipe, sc->sc_isoc_head); - ohci_free_sitd(sc, opipe->tail.itd); + + 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/bus/usb/ohci_pci.c b/sys/bus/usb/ohci_pci.c new file mode 100644 index 0000000000..7aefc7cbd8 --- /dev/null +++ b/sys/bus/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. + * + * $FreeBSD: src/sys/dev/usb/ohci_pci.c,v 1.38 2003/12/22 15:18:46 shiba Exp $ + * $DragonFly: src/sys/bus/usb/ohci_pci.c,v 1.1 2003/12/30 01:01:44 dillon Exp $ + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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(self, SYS_RES_MEMORY, &rid, + 0, ~0, 1, 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(self, SYS_RES_IRQ, &rid, 0, ~0, 1, + 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/bus/usb/ohcireg.h b/sys/bus/usb/ohcireg.h index 5e4528e7a4..11a28589c9 100644 --- a/sys/bus/usb/ohcireg.h +++ b/sys/bus/usb/ohcireg.h @@ -1,6 +1,8 @@ -/* $NetBSD: ohcireg.h,v 1.11 2000/01/16 10:35:24 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/ohcireg.h,v 1.13.2.1 2000/07/02 11:43:58 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/ohcireg.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: ohcireg.h,v 1.17 2000/04/01 09:27:35 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/ohcireg.h,v 1.20 2003/07/15 23:12:54 jmg Exp $ + * $DragonFly: src/sys/bus/usb/ohcireg.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* @@ -111,6 +113,9 @@ #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 @@ -144,6 +149,7 @@ struct ohci_hcca { #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 { @@ -165,7 +171,6 @@ typedef struct { #define OHCI_ED_SET_MAXP(s) ((s) << 16) #define OHCI_ED_MAXPMASK (0x7ff << 16) ohci_physaddr_t ed_tailp; -#define OHCI_TAILMASK 0xfffffffc ohci_physaddr_t ed_headp; #define OHCI_HALTED 0x00000001 #define OHCI_TOGGLECARRY 0x00000002 @@ -185,9 +190,11 @@ typedef struct { #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 @@ -211,19 +218,18 @@ typedef struct { #define OHCI_ITD_GET_CC(x) ((x) >> 28) /* Condition Code */ #define OHCI_ITD_NOCC 0xf0000000 ohci_physaddr_t itd_bp0; /* Buffer Page 0 */ -#define OHCI_ITD_OFFSET_MASK 0x00000fff -#define OHCI_ITD_PAGE_MASK (~OHCI_ITD_OFFSET_MASK) 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 @@ -239,4 +245,8 @@ typedef struct { #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/bus/usb/ohcivar.h b/sys/bus/usb/ohcivar.h index 53d8ee68a7..108f94769f 100644 --- a/sys/bus/usb/ohcivar.h +++ b/sys/bus/usb/ohcivar.h @@ -1,6 +1,8 @@ -/* $NetBSD: ohcivar.h,v 1.18 2000/01/18 20:11:00 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/ohcivar.h,v 1.15.2.4 2000/10/31 23:23:29 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/ohcivar.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: ohcivar.h,v 1.30 2001/12/31 12:20:35 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/ohcivar.h,v 1.36 2003/12/22 15:18:46 shiba Exp $ + * $DragonFly: src/sys/bus/usb/ohcivar.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -45,7 +47,7 @@ typedef struct ohci_soft_ed { 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 128 +#define OHCI_SED_CHUNK (PAGE_SIZE / OHCI_SED_SIZE) typedef struct ohci_soft_td { ohci_td_t td; @@ -61,15 +63,24 @@ typedef struct ohci_soft_td { #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 128 +#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 64 +#define OHCI_SITD_CHUNK (PAGE_SIZE / OHCI_SITD_SIZE) #define OHCI_NO_EDS (2*OHCI_NO_INTRS-1) @@ -79,6 +90,7 @@ 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; @@ -98,12 +110,17 @@ typedef struct ohci_softc { 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_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; @@ -112,17 +129,37 @@ typedef struct ohci_softc { 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__) - void *sc_powerhook; +#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__) @@ -131,3 +168,6 @@ 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/bus/usb/rio500_usb.h b/sys/bus/usb/rio500_usb.h index 958530a338..28f1bcceca 100644 --- a/sys/bus/usb/rio500_usb.h +++ b/sys/bus/usb/rio500_usb.h @@ -19,8 +19,10 @@ ---------------------------------------------------------------------- */ -/* $FreeBSD: src/sys/dev/usb/rio500_usb.h,v 1.1.2.1 2001/09/27 17:43:05 alfred Exp $ */ -/* $DragonFly: src/sys/bus/usb/rio500_usb.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $FreeBSD: src/sys/dev/usb/rio500_usb.h,v 1.1 2000/04/08 17:02:13 n_hibma Exp $ + * $DragonFly: src/sys/bus/usb/rio500_usb.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #include diff --git a/sys/bus/usb/ugraphire_rdesc.h b/sys/bus/usb/ugraphire_rdesc.h new file mode 100644 index 0000000000..c8001c6ef0 --- /dev/null +++ b/sys/bus/usb/ugraphire_rdesc.h @@ -0,0 +1,95 @@ +/* + * $NetBSD: usb/ugraphire_rdesc.h,v 1.1 2000/12/29 01:47:49 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/ugraphire_rdesc.h,v 1.1 2002/04/07 17:04:01 joe Exp $ + * $DragonFly: src/sys/bus/usb/ugraphire_rdesc.h,v 1.1 2003/12/30 01:01:44 dillon Exp $ + */ +/* + * Copyright (c) 2000 Nick Hibma + * 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/bus/usb/uhci.c b/sys/bus/usb/uhci.c index b379495a57..905da822e3 100644 --- a/sys/bus/usb/uhci.c +++ b/sys/bus/usb/uhci.c @@ -1,9 +1,18 @@ /* * $NetBSD: uhci.c,v 1.80 2000/01/19 01:16:38 augustss Exp $ - * $FreeBSD: src/sys/dev/usb/uhci.c,v 1.40.2.11 2003/08/22 06:59:11 njl Exp $ - * $DragonFly: src/sys/bus/usb/uhci.c,v 1.7 2003/12/29 06:42:12 dillon Exp $ + * $NetBSD: uhci.c,v 1.170 2003/02/19 01:35:04 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/uhci.c,v 1.149 2003/11/10 00:08:41 joe Exp $ + * $DragonFly: src/sys/bus/usb/uhci.c,v 1.8 2003/12/30 01:01:44 dillon 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 $ + */ + + /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. @@ -45,8 +54,8 @@ * USB Universal Host Controller driver. * Handles e.g. PIIX3 and PIIX4. * - * UHCI spec: http://www.intel.com/design/usb/uhci11d.pdf - * USB spec: http://www.usb.org/developers/data/usb11.pdf + * 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 */ @@ -59,6 +68,7 @@ #include #include #elif defined(__FreeBSD__) +#include #include #include #include @@ -82,6 +92,9 @@ #include "uhcireg.h" #include "uhcivar.h" +/* Use bandwidth reclamation for control transfers. Some devices choke on it. */ +/*#define UHCI_CTL_LOOP */ + #if defined(__FreeBSD__) #include @@ -97,12 +110,19 @@ struct cfdriver uhci_cd = { #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) @@ -112,16 +132,23 @@ SYSCTL_INT(_hw_usb_uhci, OID_AUTO, debug, CTLFLAG_RW, * 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 LE(x) (bswap32(x)) +#define htole32(x) (bswap32(x)) +#define le32toh(x) (bswap32(x)) #else -#define LE(x) (x) +#define htole32(x) (x) +#define le32toh(x) (x) +#endif #endif struct uhci_pipe { struct usbd_pipe pipe; - uhci_intr_info_t *iinfo; int nexttoggle; + + u_char aborting; + usbd_xfer_handle abortstart, abortend; + /* Info needed for different pipe kinds. */ union { /* Control pipe */ @@ -134,6 +161,7 @@ struct uhci_pipe { /* Interrupt pipe */ struct { int npoll; + int isread; uhci_soft_qh_t **qhs; } intr; /* Bulk pipe */ @@ -150,47 +178,47 @@ struct uhci_pipe { } u; }; -/* - * The uhci_intr_info free list can be global since they contain - * no dma specific data. The other free lists do. - */ -LIST_HEAD(, uhci_intr_info) uhci_ii_free; - -Static void uhci_busreset(uhci_softc_t *); +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 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 uhci_soft_qh_t *uhci_alloc_sqh(uhci_softc_t *); Static void uhci_free_sqh(uhci_softc_t *, uhci_soft_qh_t *); -Static uhci_intr_info_t *uhci_alloc_intr_info(uhci_softc_t *); -Static void uhci_free_intr_info(uhci_intr_info_t *ii); #if 0 Static void uhci_enter_ctl_q(uhci_softc_t *, uhci_soft_qh_t *, - uhci_intr_info_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 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_softc_t *, int, int, u_int16_t, usb_dma_t *, uhci_soft_td_t **, uhci_soft_td_t **); -Static void uhci_timo(void *); +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_abort_xfer_end(void *v); Static void uhci_timeout(void *); -Static void uhci_lock_frames(uhci_softc_t *); -Static void uhci_unlock_frames(uhci_softc_t *); -Static void uhci_add_ctrl(uhci_softc_t *, uhci_soft_qh_t *); +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_ctrl(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); @@ -205,69 +233,85 @@ 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 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 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 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 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 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 *, int, uhci_soft_qh_t *); -Static void uhci_remove_intr(uhci_softc_t *, int, uhci_soft_qh_t *); -Static usbd_status uhci_device_setintr(uhci_softc_t *sc, +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 UWRITE1(sc, r, x) bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)) -#define UWRITE2(sc, r, x) bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)) -#define UWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)) -#define UREAD1(sc, r) bus_space_read_1((sc)->iot, (sc)->ioh, (r)) -#define UREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r)) -#define UREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r)) +#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 /* reset timeout */ +#define UHCI_RESET_TIMEOUT 100 /* ms, reset timeout */ #define UHCI_CURFRAME(sc) (UREAD2(sc, UHCI_FRNUM) & UHCI_FRNUM_MASK) @@ -275,6 +319,7 @@ Static void uhci_dump_td(uhci_soft_td_t *); struct usbd_bus_methods uhci_bus_methods = { uhci_open, + uhci_softintr, uhci_poll, uhci_allocm, uhci_freem, @@ -282,16 +327,16 @@ struct usbd_bus_methods uhci_bus_methods = { uhci_freex, }; -struct usbd_pipe_methods uhci_root_ctrl_methods = { +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, - 0, + uhci_root_ctrl_done, }; -struct usbd_pipe_methods uhci_root_intr_methods = { +struct usbd_pipe_methods uhci_root_intr_methods = { uhci_root_intr_transfer, uhci_root_intr_start, uhci_root_intr_abort, @@ -336,8 +381,33 @@ struct usbd_pipe_methods uhci_device_isoc_methods = { 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_busreset(uhci_softc_t *sc) +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 */ @@ -349,23 +419,24 @@ uhci_init(uhci_softc_t *sc) { usbd_status err; int i, j; - uhci_soft_qh_t *csqh, *bsqh, *sqh; + 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 - uhci_run(sc, 0); /* stop the controller */ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */ - - uhci_busreset(sc); + uhci_globalreset(sc); /* reset the controller */ + uhci_reset(sc); /* Allocate and initialize real frame array. */ - err = usb_allocmem(&sc->sc_bus, + err = usb_allocmem(&sc->sc_bus, UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t), UHCI_FRAMELIST_ALIGN, &sc->sc_dma); if (err) @@ -374,24 +445,61 @@ uhci_init(uhci_softc_t *sc) 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->qh.qh_hlink = LE(UHCI_PTR_T); /* end of QH chain */ - bsqh->qh.qh_elink = LE(UHCI_PTR_T); + 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 the dummy QH where control traffic will be queued. */ - csqh = uhci_alloc_sqh(sc); - if (csqh == NULL) + /* Allocate dummy QH where high speed control traffic will be queued. */ + chsqh = uhci_alloc_sqh(sc); + if (chsqh == NULL) return (USBD_NOMEM); - csqh->hlink = bsqh; - csqh->qh.qh_hlink = LE(bsqh->physaddr | UHCI_PTR_Q); - csqh->qh.qh_elink = LE(UHCI_PTR_T); - sc->sc_ctl_start = sc->sc_ctl_end = csqh; + 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. @@ -402,39 +510,43 @@ uhci_init(uhci_softc_t *sc) if (std == NULL || sqh == NULL) return (USBD_NOMEM); std->link.sqh = sqh; - std->td.td_link = LE(sqh->physaddr | UHCI_PTR_Q); - std->td.td_status = LE(UHCI_TD_IOS); /* iso, inactive */ - std->td.td_xtoken = LE(0); - std->td.td_buffer = LE(0); - sqh->hlink = csqh; - sqh->qh.qh_hlink = LE(csqh->physaddr | UHCI_PTR_Q); - sqh->elink = 0; - sqh->qh.qh_elink = LE(UHCI_PTR_T); + + 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] = LE(std->physaddr); + 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; -#if defined(__NetBSD__) 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 | + 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 */ @@ -452,7 +564,6 @@ uhci_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: if (sc->sc_child != NULL) @@ -470,11 +581,11 @@ uhci_detach(struct uhci_softc *sc, int flags) if (sc->sc_child != NULL) rv = config_detach(sc->sc_child, flags); - + if (rv != 0) return (rv); -#if defined(__NetBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) powerhook_disestablish(sc->sc_powerhook); shutdownhook_disestablish(sc->sc_shutdownhook); #endif @@ -484,9 +595,9 @@ uhci_detach(struct uhci_softc *sc, int flags) xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); if (xfer == NULL) break; - SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next); + SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next); free(xfer, M_USB); - } + } /* XXX free other data structures XXX */ @@ -497,14 +608,13 @@ uhci_detach(struct uhci_softc *sc, int flags) usbd_status uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) { - return (usb_allocmem(&((struct uhci_softc *)bus)->sc_bus, size, 0, - dma)); + return (usb_allocmem(bus, size, 0, dma)); } void uhci_freem(struct usbd_bus *bus, usb_dma_t *dma) { - usb_freemem(&((struct uhci_softc *)bus)->sc_bus, dma); + usb_freemem(bus, dma); } usbd_xfer_handle @@ -514,12 +624,25 @@ uhci_allocx(struct usbd_bus *bus) usbd_xfer_handle xfer; xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); - if (xfer != NULL) - SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next); - else - xfer = malloc(sizeof(*xfer), M_USB, M_NOWAIT); - if (xfer != NULL) - memset(xfer, 0, sizeof *xfer); + 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); } @@ -528,6 +651,18 @@ 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); } @@ -547,7 +682,7 @@ uhci_shutdown(void *v) * 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 + * called from an interrupt context. This is all right since we * are almost suspended anyway. */ void @@ -557,10 +692,10 @@ uhci_power(int why, void *v) int cmd; int s; - s = splusb(); + s = splhardusb(); cmd = UREAD2(sc, UHCI_CMD); - DPRINTF(("uhci_power: sc=%p, why=%d (was %d), cmd=0x%x\n", + DPRINTF(("uhci_power: sc=%p, why=%d (was %d), cmd=0x%x\n", sc, why, sc->sc_suspend, cmd)); if (why != PWR_RESUME) { @@ -568,9 +703,9 @@ uhci_power(int why, void *v) if (uhcidebug > 2) uhci_dumpregs(sc); #endif - if (sc->sc_has_timo != NULL) - usb_untimeout(uhci_timo, sc->sc_has_timo, - sc->sc_has_timo->timo_handle); + 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 */ @@ -578,6 +713,8 @@ uhci_power(int why, void *v) 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; @@ -601,15 +738,15 @@ uhci_power(int why, void *v) 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 | + 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_has_timo != NULL) - usb_timeout(uhci_timo, sc->sc_has_timo, - sc->sc_ival, sc->sc_has_timo->timo_handle); + 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); @@ -638,50 +775,58 @@ uhci_dumpregs(uhci_softc_t *sc) 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)LE(p->td.td_link), - (long)LE(p->td.td_status), - (long)LE(p->td.td_xtoken), - (long)LE(p->td.td_buffer))); - DPRINTFN(-1,(" %b %b,errcnt=%d,actlen=%d pid=%02x,addr=%d,endpt=%d," - "D=%d,maxlen=%d\n", - (int)LE(p->td.td_link), - "\20\1T\2Q\3VF", - (int)LE(p->td.td_status), - "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27" - "STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD", - UHCI_TD_GET_ERRCNT(LE(p->td.td_status)), - UHCI_TD_GET_ACTLEN(LE(p->td.td_status)), - UHCI_TD_GET_PID(LE(p->td.td_xtoken)), - UHCI_TD_GET_DEVADDR(LE(p->td.td_xtoken)), - UHCI_TD_GET_ENDPT(LE(p->td.td_xtoken)), - UHCI_TD_GET_DT(LE(p->td.td_xtoken)), - UHCI_TD_GET_MAXLEN(LE(p->td.td_xtoken)))); + (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, LE(sqh->qh.qh_hlink), LE(sqh->qh.qh_elink))); + (int)sqh->physaddr, le32toh(sqh->qh.qh_hlink), + le32toh(sqh->qh.qh_elink))); } - -#if 0 +#if 1 void -uhci_dump() +uhci_dump(void) { - uhci_softc_t *sc = uhci; + 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_ctl_start->qh.hlink); + /*printf("framelist[i].link = %08x\n", sc->sc_framelist[0].link);*/ + uhci_dump_qh(sc->sc_lctl_start); } -#endif - void uhci_dump_qhs(uhci_soft_qh_t *sqh) @@ -703,12 +848,12 @@ uhci_dump_qhs(uhci_soft_qh_t *sqh) */ - if (sqh->hlink != NULL && !(sqh->qh.qh_hlink & UHCI_PTR_T)) + 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 && !(sqh->qh.qh_elink & UHCI_PTR_T)) + if (sqh->elink != NULL && !(le32toh(sqh->qh.qh_elink) & UHCI_PTR_T)) uhci_dump_tds(sqh->elink); else DPRINTF(("No TD\n")); @@ -727,11 +872,74 @@ uhci_dump_tds(uhci_soft_td_t *std) * printing the free list in case the queue/TD has * already been moved there (seatbelt). */ - if (td->td.td_link & UHCI_PTR_T || - td->td.td_link == 0) + 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 /* @@ -739,7 +947,7 @@ uhci_dump_tds(uhci_soft_td_t *std) * from the root controller interrupt pipe for port status change. */ void -uhci_timo(void *addr) +uhci_poll_hub(void *addr) { usbd_xfer_handle xfer = addr; usbd_pipe_handle pipe = xfer->pipe; @@ -747,9 +955,9 @@ uhci_timo(void *addr) int s; u_char *p; - DPRINTFN(20, ("uhci_timo\n")); + DPRINTFN(20, ("uhci_poll_hub\n")); - usb_timeout(uhci_timo, xfer, sc->sc_ival, xfer->timo_handle); + usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer); p = KERNADDR(&xfer->dmabuf, 0); p[0] = 0; @@ -764,7 +972,6 @@ uhci_timo(void *addr) xfer->actlen = 1; xfer->status = USBD_NORMAL_COMPLETION; s = splusb(); - xfer->hcpriv = 0; xfer->device->bus->intr_context++; usb_transfer_complete(xfer); xfer->device->bus->intr_context--; @@ -777,97 +984,132 @@ uhci_root_intr_done(usbd_xfer_handle xfer) } void -uhci_lock_frames(uhci_softc_t *sc) +uhci_root_ctrl_done(usbd_xfer_handle xfer) { - int s = splusb(); +} - while (sc->sc_vflock & UHCI_HAS_LOCK) { - sc->sc_vflock |= UHCI_WANT_LOCK; - tsleep(&sc->sc_vflock, 0, "uhcqhl", 0); +/* + * 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); } - sc->sc_vflock = UHCI_HAS_LOCK; - splx(s); } void -uhci_unlock_frames(uhci_softc_t *sc) -{ - int s = splusb(); - - sc->sc_vflock &= ~UHCI_HAS_LOCK; - if (sc->sc_vflock & UHCI_WANT_LOCK) - wakeup(&sc->sc_vflock); - splx(s); +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); + } } -/* - * Allocate an interrupt information struct. A free list is kept - * for fast allocation. - */ -uhci_intr_info_t * -uhci_alloc_intr_info(uhci_softc_t *sc) +/* Add high speed control QH, called at splusb(). */ +void +uhci_add_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) { - uhci_intr_info_t *ii; + uhci_soft_qh_t *eqh; - ii = LIST_FIRST(&uhci_ii_free); - if (ii) - LIST_REMOVE(ii, list); - else { - ii = malloc(sizeof(uhci_intr_info_t), M_USBHC, M_NOWAIT); - } - ii->sc = sc; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif + SPLUSBCHECK; - return ii; + 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_free_intr_info(uhci_intr_info_t *ii) +uhci_remove_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) { - LIST_INSERT_HEAD(&uhci_ii_free, ii, list); /* and put on free list */ + 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 control QH, called at splusb(). */ +/* Add low speed control QH, called at splusb(). */ void -uhci_add_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) +uhci_add_ls_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_ctl_end; - sqh->hlink = eqh->hlink; + 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 = LE(sqh->physaddr | UHCI_PTR_Q); - sc->sc_ctl_end = sqh; + eqh->hlink = sqh; + eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH); + sc->sc_lctl_end = sqh; } -/* Remove control QH, called at splusb(). */ +/* Remove low speed control QH, called at splusb(). */ void -uhci_remove_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) +uhci_remove_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) { uhci_soft_qh_t *pqh; SPLUSBCHECK; - DPRINTFN(10, ("uhci_remove_ctrl: sqh=%p\n", sqh)); - for (pqh = sc->sc_ctl_start; pqh->hlink != sqh; pqh=pqh->hlink) -#if defined(DIAGNOSTIC) || defined(USB_DEBUG) - if (LE(pqh->qh.qh_hlink) & UHCI_PTR_T) { - printf("uhci_remove_ctrl: QH not found\n"); - return; - } -#else - ; -#endif - pqh->hlink = sqh->hlink; + 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; - if (sc->sc_ctl_end == sqh) - sc->sc_ctl_end = pqh; + delay(UHCI_QH_REMOVE_DELAY); + if (sc->sc_lctl_end == sqh) + sc->sc_lctl_end = pqh; } /* Add bulk QH, called at splusb(). */ @@ -880,11 +1122,12 @@ uhci_add_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh) DPRINTFN(10, ("uhci_add_bulk: sqh=%p\n", sqh)); eqh = sc->sc_bulk_end; - sqh->hlink = eqh->hlink; + sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; - eqh->hlink = sqh; - eqh->qh.qh_hlink = LE(sqh->physaddr | UHCI_PTR_Q); + 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(). */ @@ -896,28 +1139,46 @@ uhci_remove_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh) SPLUSBCHECK; DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh)); - for (pqh = sc->sc_bulk_start; pqh->hlink != sqh; pqh = pqh->hlink) -#if defined(DIAGNOSTIC) || defined(USB_DEBUG) - if (LE(pqh->qh.qh_hlink) & UHCI_PTR_T) { - printf("uhci_remove_bulk: QH not found\n"); - return; - } -#else - ; -#endif + 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; - uhci_intr_info_t *ii; /* * It can happen that an interrupt will be delivered to @@ -937,12 +1198,11 @@ uhci_intr(void *arg) #ifdef USB_DEBUG if (uhcidebug > 15) { - DPRINTF(("%s: uhci_intr\n", USBDEVNAME(sc->sc_bus.bdev))); + DPRINTF(("%s: uhci_intr1\n", USBDEVNAME(sc->sc_bus.bdev))); uhci_dumpregs(sc); } #endif - - status = UREAD2(sc, UHCI_STS); + status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS; if (status == 0) /* The interrupt was not for us. */ return (0); @@ -951,6 +1211,13 @@ uhci_intr(void *arg) 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; @@ -958,7 +1225,9 @@ uhci_intr(void *arg) 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; @@ -966,22 +1235,43 @@ uhci_intr(void *arg) } if (status & UHCI_STS_HCPE) { ack |= UHCI_STS_HCPE; - printf("%s: host controller process error\n", + printf("%s: host controller process error\n", USBDEVNAME(sc->sc_bus.bdev)); } if (status & UHCI_STS_HCH) { /* no acknowledge needed */ - printf("%s: host controller halted\n", - USBDEVNAME(sc->sc_bus.bdev)); + 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) /* acknowledge the ints */ - UWRITE2(sc, UHCI_STS, ack); - else /* nothing to acknowledge */ - return (0); + if (!ack) + return (0); /* nothing to acknowledge */ + UWRITE2(sc, UHCI_STS, ack); /* acknowledge the ints */ - sc->sc_bus.intr_context++; 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; + + 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 @@ -994,14 +1284,17 @@ uhci_intr(void *arg) * We scan all interrupt descriptors to see if any have * completed. */ - for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list)) + LIST_FOREACH(ii, &sc->sc_intrhead, list) uhci_check_intr(sc, ii); - DPRINTFN(10, ("%s: uhci_intr: exit\n", USBDEVNAME(sc->sc_bus.bdev))); +#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--; - - return (1); } /* Check for an interrupt. */ @@ -1018,6 +1311,12 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *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; @@ -1029,35 +1328,33 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii) #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 + * is an error somewhere in the middle, or whether there was a * short packet (SPD and not ACTIVE). */ - if (LE(lstd->td.td_status) & UHCI_TD_ACTIVE) { - DPRINTFN(15, ("uhci_check_intr: active ii=%p\n", ii)); + 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 = LE(std->td.td_status); + 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 - */ + /* 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(LE(std->td.td_xtoken))) + UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token))) { goto done; + } } - DPRINTFN(15, ("uhci_check_intr: ii=%p std=%p still active\n", - ii, ii->stdstart)); + DPRINTFN(12, ("uhci_check_intr: ii=%p std=%p still active\n", + ii, ii->stdstart)); return; } done: - - usb_untimeout(uhci_timeout, ii, ii->timeout_handle); + DPRINTFN(12, ("uhci_check_intr: ii=%p done\n", ii)); + usb_uncallout(ii->xfer->timeout_handle, uhci_timeout, ii); uhci_idone(ii); } @@ -1071,12 +1368,18 @@ uhci_idone(uhci_intr_info_t *ii) 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; @@ -1084,22 +1387,16 @@ uhci_idone(uhci_intr_info_t *ii) } #endif - if (xfer->status == USBD_CANCELLED || - xfer->status == USBD_TIMEOUT) { - DPRINTF(("uhci_idone: aborted xfer=%p\n", xfer)); - return; - } - if (xfer->nframes != 0) { /* Isoc transfer, do things differently. */ uhci_soft_td_t **stds = upipe->u.iso.stds; - int i, n, nframes; + int i, n, nframes, len; DPRINTFN(5,("uhci_idone: ii=%p isoc ready\n", ii)); nframes = xfer->nframes; actlen = 0; - n = xfer->hcprivint; + n = UXFER(xfer)->curframe; for (i = 0; i < nframes; i++) { std = stds[n]; #ifdef USB_DEBUG @@ -1110,15 +1407,15 @@ uhci_idone(uhci_intr_info_t *ii) #endif if (++n >= UHCI_VFRAMELIST_COUNT) n = 0; - status = LE(std->td.td_status); - actlen += UHCI_TD_GET_ACTLEN(status); + 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; - xfer->hcpriv = ii; - usb_transfer_complete(xfer); - return; + goto end; } #ifdef USB_DEBUG @@ -1131,31 +1428,49 @@ uhci_idone(uhci_intr_info_t *ii) /* The transfer is done, compute actual length and status. */ actlen = 0; for (std = ii->stdstart; std != NULL; std = std->link.std) { - nstatus = LE(std->td.td_status); + nstatus = le32toh(std->td.td_status); if (nstatus & UHCI_TD_ACTIVE) break; status = nstatus; - if (UHCI_TD_GET_PID(LE(std->td.td_xtoken)) != UHCI_TD_PID_SETUP) + 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(LE(std->td.td_xtoken)); + upipe->nexttoggle = UHCI_TD_GET_DT(le32toh(std->td.td_token)); status &= UHCI_TD_ERROR; - DPRINTFN(10, ("uhci_check_intr: actlen=%d, status=0x%x\n", + 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%b\n", + "status 0x%s\n", xfer->pipe->device->address, xfer->pipe->endpoint->edesc->bEndpointAddress, - (int)status, - "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27" - "STALLED\30ACTIVE")); + sbuf)); +#endif if (status == UHCI_TD_STALLED) xfer->status = USBD_STALLED; else @@ -1163,8 +1478,10 @@ uhci_idone(uhci_intr_info_t *ii) } else { xfer->status = USBD_NORMAL_COMPLETION; } - xfer->hcpriv = ii; + + end: usb_transfer_complete(xfer); + DPRINTFN(12, ("uhci_idone: ii=%p done\n", ii)); } /* @@ -1174,17 +1491,33 @@ 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: ii=%p\n", ii)); + DPRINTF(("uhci_timeout: uxfer=%p\n", uxfer)); -#ifdef USB_DEBUG - if (uhcidebug > 10) - uhci_dump_tds(ii->stdstart); -#endif + 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)); - ii->xfer->device->bus->intr_context++; - uhci_abort_xfer(ii->xfer, USBD_TIMEOUT); - ii->xfer->device->bus->intr_context--; + s = splusb(); + uhci_abort_xfer(xfer, USBD_TIMEOUT); + splx(s); } /* @@ -1206,7 +1539,7 @@ uhci_waitintr(uhci_softc_t *sc, usbd_xfer_handle xfer) 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_intr(sc); + uhci_intr1(sc); if (xfer->status != USBD_IN_PROGRESS) return; } @@ -1214,12 +1547,12 @@ uhci_waitintr(uhci_softc_t *sc, usbd_xfer_handle xfer) /* Timeout */ DPRINTF(("uhci_waitintr: timeout\n")); for (ii = LIST_FIRST(&sc->sc_intrhead); - ii != NULL && ii->xfer != xfer; + ii != NULL && ii->xfer != xfer; ii = LIST_NEXT(ii, list)) ; #ifdef DIAGNOSTIC if (ii == NULL) - panic("uhci_waitintr: lost intr_info\n"); + panic("uhci_waitintr: lost intr_info"); #endif uhci_idone(ii); } @@ -1230,26 +1563,23 @@ uhci_poll(struct usbd_bus *bus) uhci_softc_t *sc = (uhci_softc_t *)bus; if (UREAD2(sc, UHCI_STS) & UHCI_STS_USBINT) - uhci_intr(sc); + uhci_intr1(sc); } -#if 0 void -uhci_reset(void *p) +uhci_reset(uhci_softc_t *sc) { - uhci_softc_t *sc = p; int n; UHCICMD(sc, UHCI_CMD_HCRESET); /* The reset bit goes low when the controller is done. */ - for (n = 0; n < UHCI_RESET_TIMEOUT && + for (n = 0; n < UHCI_RESET_TIMEOUT && (UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET); n++) - delay(100); + usb_delay_ms(&sc->sc_bus, 1); if (n >= UHCI_RESET_TIMEOUT) - printf("%s: controller did not reset\n", + printf("%s: controller did not reset\n", USBDEVNAME(sc->sc_bus.bdev)); } -#endif usbd_status uhci_run(uhci_softc_t *sc, int run) @@ -1258,7 +1588,7 @@ uhci_run(uhci_softc_t *sc, int run) u_int16_t cmd; run = run != 0; - s = splusb(); + s = splhardusb(); DPRINTF(("uhci_run: setting run=%d\n", run)); cmd = UREAD2(sc, UHCI_CMD); if (run) @@ -1309,7 +1639,7 @@ uhci_alloc_std(uhci_softc_t *sc) return (0); for(i = 0; i < UHCI_STD_CHUNK; i++) { offs = i * UHCI_STD_SIZE; - std = (uhci_soft_td_t *)((char *)KERNADDR(&dma, offs)); + std = KERNADDR(&dma, offs); std->physaddr = DMAADDR(&dma, offs); std->link.std = sc->sc_freetds; sc->sc_freetds = std; @@ -1326,11 +1656,11 @@ uhci_free_std(uhci_softc_t *sc, uhci_soft_td_t *std) { #ifdef DIAGNOSTIC #define TD_IS_FREE 0x12345678 - if (std->td.td_xtoken == LE(TD_IS_FREE)) { + if (le32toh(std->td.td_token) == TD_IS_FREE) { printf("uhci_free_std: freeing free TD %p\n", std); return; } - std->td.td_xtoken = LE(TD_IS_FREE); + std->td.td_token = htole32(TD_IS_FREE); #endif std->link.std = sc->sc_freetds; sc->sc_freetds = std; @@ -1352,7 +1682,7 @@ uhci_alloc_sqh(uhci_softc_t *sc) return (0); for(i = 0; i < UHCI_SQH_CHUNK; i++) { offs = i * UHCI_SQH_SIZE; - sqh = (uhci_soft_qh_t *)((char *)KERNADDR(&dma, offs)); + sqh = KERNADDR(&dma, offs); sqh->physaddr = DMAADDR(&dma, offs); sqh->hlink = sc->sc_freeqhs; sc->sc_freeqhs = sqh; @@ -1371,22 +1701,9 @@ uhci_free_sqh(uhci_softc_t *sc, uhci_soft_qh_t *sqh) sc->sc_freeqhs = sqh; } -#if 0 -/* - * Enter a list of transfers onto a control queue. - * Called at splusb() - */ -void -uhci_enter_ctl_q(uhci_softc_t *sc, uhci_soft_qh_t *sqh, uhci_intr_info_t *ii) -{ - DPRINTFN(5, ("uhci_enter_ctl_q: sqh=%p\n", sqh)); - -} -#endif - void uhci_free_std_chain(uhci_softc_t *sc, uhci_soft_td_t *std, - uhci_soft_td_t *stdend) + uhci_soft_td_t *stdend) { uhci_soft_td_t *p; @@ -1397,9 +1714,9 @@ uhci_free_std_chain(uhci_softc_t *sc, uhci_soft_td_t *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_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; @@ -1408,9 +1725,9 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, 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 ls=%d " - "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len, - upipe->pipe.device->lowspeed, flags)); + 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"); @@ -1429,28 +1746,25 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, if (ntd % 2 == 0) tog ^= 1; upipe->nexttoggle = tog ^ 1; - lastp = 0; + lastp = NULL; lastlink = UHCI_PTR_T; ntd--; status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE); - if (upipe->pipe.device->lowspeed) + 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, 0); + uhci_free_std_chain(sc, lastp, NULL); return (USBD_NOMEM); } p->link.std = lastp; - if (lastlink == UHCI_PTR_T) - p->td.td_link = LE(lastlink); - else - p->td.td_link = LE(lastlink|UHCI_PTR_VF); + p->td.td_link = htole32(lastlink | UHCI_PTR_VF | UHCI_PTR_TD); lastp = p; lastlink = p->physaddr; - p->td.td_status = LE(status); + p->td.td_status = htole32(status); if (i == ntd) { /* last TD */ l = len % maxp; @@ -1459,14 +1773,14 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, *ep = p; } else l = maxp; - p->td.td_xtoken = - LE(rd ? UHCI_TD_IN (l, endpt, addr, tog) : - UHCI_TD_OUT(l, endpt, addr, tog)); - p->td.td_buffer = LE(DMAADDR(dma, i * 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", + DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n", upipe->nexttoggle)); return (USBD_NORMAL_COMPLETION); } @@ -1493,8 +1807,9 @@ uhci_device_bulk_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -1505,23 +1820,26 @@ 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 = upipe->iinfo; + 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_transfer: xfer=%p len=%d flags=%d\n", - xfer, xfer->length, xfer->flags)); + 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\n"); + panic("uhci_device_bulk_transfer: a request"); #endif len = xfer->length; - endpt = xfer->pipe->endpoint->edesc->bEndpointAddress; + endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; isread = UE_GET_DIR(endpt) == UE_DIR_IN; sqh = upipe->u.bulk.sqh; @@ -1532,7 +1850,7 @@ uhci_device_bulk_start(usbd_xfer_handle xfer) &xfer->dmabuf, &data, &dataend); if (err) return (err); - dataend->td.td_status |= LE(UHCI_TD_IOC); + dataend->td.td_status |= htole32(UHCI_TD_IOC); #ifdef USB_DEBUG if (uhcidebug > 8) { @@ -1545,9 +1863,6 @@ uhci_device_bulk_start(usbd_xfer_handle xfer) ii->xfer = xfer; ii->stdstart = data; ii->stdend = dataend; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_bulk_transfer: not done, ii=%p\n", ii); @@ -1556,17 +1871,17 @@ uhci_device_bulk_start(usbd_xfer_handle xfer) #endif sqh->elink = data; - sqh->qh.qh_elink = LE(data->physaddr); - sqh->intr_info = ii; + sqh->qh.qh_elink = htole32(data->physaddr | UHCI_PTR_TD); s = splusb(); uhci_add_bulk(sc, sqh); - LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); + uhci_add_intr_info(sc, ii); if (xfer->timeout && !sc->sc_bus.use_polling) { - usb_timeout(uhci_timeout, ii, MS_TO_TICKS(xfer->timeout), - ii->timeout_handle); + usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + uhci_timeout, ii); } + xfer->status = USBD_IN_PROGRESS; splx(s); #ifdef USB_DEBUG @@ -1590,54 +1905,79 @@ uhci_device_bulk_abort(usbd_xfer_handle xfer) 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_intr_info_t *ii = upipe->iinfo; + 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)); - /* Make interrupt routine ignore it, */ - xfer->status = 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; + } - /* don't timeout, */ - usb_untimeout(uhci_timeout, ii, ii->timeout_handle); + if (xfer->device->bus->intr_context /* || !curproc REMOVED DFly */) + panic("uhci_abort_xfer: not in process context"); - /* make hardware ignore it, */ - for (std = ii->stdstart; std != 0; std = std->link.std) - std->td.td_status &= LE(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); + /* + * 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); - xfer->hcpriv = ii; + /* + * 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, 0, "uhciab", 0); +#endif /* USB_USE_SOFTINTR */ + splx(s); -#if 1 - /* Make sure hardware has completed. */ - if (xfer->device->bus->intr_context) { - /* We have no process context, so we can't use tsleep(). */ - timeout(uhci_abort_xfer_end, xfer, hz / USB_FRAMES_PER_SECOND); - } else { -#if defined(DIAGNOSTIC) && defined(__FreeBSD__) - KASSERT(mycpu->gd_intr_nesting_level == 0, - ("ohci_abort_req in interrupt context")); -#endif - usb_delay_ms(xfer->pipe->device->bus, 1); - /* and call final part of interrupt handler. */ - uhci_abort_xfer_end(xfer); - } -#else - delay(1000); - uhci_abort_xfer_end(xfer); -#endif -} + /* + * Step 3: Execute callback. + */ + xfer->hcpriv = ii; -void -uhci_abort_xfer_end(void *v) -{ - usbd_xfer_handle xfer = v; - int s; + DPRINTFN(1,("uhci_abort_xfer: callback\n")); s = splusb(); +#ifdef DIAGNOSTIC + ii->isdone = 1; +#endif usb_transfer_complete(xfer); splx(s); } @@ -1651,8 +1991,6 @@ uhci_device_bulk_close(usbd_pipe_handle pipe) uhci_softc_t *sc = (uhci_softc_t *)dev->bus; uhci_free_sqh(sc, upipe->u.bulk.sqh); - uhci_free_intr_info(upipe->iinfo); - /* XXX free other resources */ } usbd_status @@ -1665,8 +2003,9 @@ uhci_device_ctrl_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -1677,9 +2016,12 @@ 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\n"); + panic("uhci_device_ctrl_transfer: not a request"); #endif err = uhci_device_request(xfer); @@ -1701,8 +2043,9 @@ uhci_device_intr_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -1713,25 +2056,36 @@ 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 = upipe->iinfo; + 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\n"); + panic("uhci_device_intr_transfer: a request"); #endif - err = uhci_alloc_std_chain(upipe, sc, xfer->length, 1, xfer->flags, - &xfer->dmabuf, &data, &dataend); + 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 |= LE(UHCI_TD_IOC); + dataend->td.td_status |= htole32(UHCI_TD_IOC); #ifdef USB_DEBUG if (uhcidebug > 10) { @@ -1746,9 +2100,6 @@ uhci_device_intr_start(usbd_xfer_handle xfer) ii->xfer = xfer; ii->stdstart = data; ii->stdend = dataend; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_intr_transfer: not done, ii=%p\n", ii); @@ -1756,13 +2107,15 @@ uhci_device_intr_start(usbd_xfer_handle xfer) ii->isdone = 0; #endif - DPRINTFN(10,("uhci_device_intr_transfer: qhs[0]=%p\n", + 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 = LE(data->physaddr); + 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 @@ -1788,10 +2141,6 @@ uhci_device_ctrl_abort(usbd_xfer_handle xfer) void uhci_device_ctrl_close(usbd_pipe_handle pipe) { - struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; - - uhci_free_intr_info(upipe->iinfo); - /* XXX free other resources? */ } /* Abort a device interrupt request. */ @@ -1801,7 +2150,7 @@ 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 = 0; + xfer->pipe->intrxfer = NULL; } uhci_abort_xfer(xfer, USBD_CANCELLED); } @@ -1812,19 +2161,17 @@ 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, s, npoll; - - upipe->iinfo->stdstart = 0; /* inactive */ + int i, npoll; + int s; /* Unlink descriptors from controller data structures. */ npoll = upipe->u.intr.npoll; - uhci_lock_frames(sc); + s = splusb(); for (i = 0; i < npoll; i++) - uhci_remove_intr(sc, upipe->u.intr.qhs[i]->pos, - upipe->u.intr.qhs[i]); - uhci_unlock_frames(sc); + 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. */ @@ -1834,11 +2181,6 @@ uhci_device_intr_close(usbd_pipe_handle pipe) uhci_free_sqh(sc, upipe->u.intr.qhs[i]); free(upipe->u.intr.qhs, M_USBHC); - s = splusb(); - LIST_REMOVE(upipe->iinfo, list); /* remove from active list */ - splx(s); - uhci_free_intr_info(upipe->iinfo); - /* XXX free other resources */ } @@ -1851,7 +2193,7 @@ uhci_device_request(usbd_xfer_handle xfer) uhci_softc_t *sc = (uhci_softc_t *)dev->bus; int addr = dev->address; int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; - uhci_intr_info_t *ii = upipe->iinfo; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_soft_td_t *setup, *data, *stat, *next, *dataend; uhci_soft_qh_t *sqh; int len; @@ -1866,7 +2208,7 @@ uhci_device_request(usbd_xfer_handle xfer) UGETW(req->wIndex), UGETW(req->wLength), addr, endpt)); - ls = dev->lowspeed ? UHCI_TD_LS : 0; + ls = dev->speed == USB_SPEED_LOW ? UHCI_TD_LS : 0; isread = req->bmRequestType & UT_READ; len = UGETW(req->wLength); @@ -1883,7 +2225,7 @@ uhci_device_request(usbd_xfer_handle xfer) return (err); next = data; dataend->link.std = stat; - dataend->td.td_link = LE(stat->physaddr | UHCI_PTR_VF); + dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_VF | UHCI_PTR_TD); } else { next = stat; } @@ -1892,19 +2234,20 @@ uhci_device_request(usbd_xfer_handle xfer) memcpy(KERNADDR(&upipe->u.ctl.reqdma, 0), req, sizeof *req); setup->link.std = next; - setup->td.td_link = LE(next->physaddr | UHCI_PTR_VF); - setup->td.td_status = LE(UHCI_TD_SET_ERRCNT(3) | ls | UHCI_TD_ACTIVE); - setup->td.td_xtoken = LE(UHCI_TD_SETUP(sizeof *req, endpt, addr)); - setup->td.td_buffer = LE(DMAADDR(&upipe->u.ctl.reqdma, 0)); - - stat->link.std = 0; - stat->td.td_link = LE(UHCI_PTR_T); - stat->td.td_status = LE(UHCI_TD_SET_ERRCNT(3) | ls | + 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_xtoken = - LE(isread ? UHCI_TD_OUT(0, endpt, addr, 1) : - UHCI_TD_IN (0, endpt, addr, 1)); - stat->td.td_buffer = LE(0); + + 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) { @@ -1917,9 +2260,6 @@ uhci_device_request(usbd_xfer_handle xfer) ii->xfer = xfer; ii->stdstart = setup; ii->stdend = stat; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_request: not done, ii=%p\n", ii); @@ -1928,12 +2268,14 @@ uhci_device_request(usbd_xfer_handle xfer) #endif sqh->elink = setup; - sqh->qh.qh_elink = LE(setup->physaddr); - sqh->intr_info = ii; + sqh->qh.qh_elink = htole32(setup->physaddr | UHCI_PTR_TD); s = splusb(); - uhci_add_ctrl(sc, sqh); - LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); + 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; @@ -1943,17 +2285,17 @@ uhci_device_request(usbd_xfer_handle xfer) 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_Q) == 0; + (link & UHCI_PTR_QH) == 0; std = std->link.std) { - link = LE(std->td.td_link); + 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)) { + xqh = (maxqh++ == 5 || xqh->hlink == sxqh || + xqh->hlink == xqh ? NULL : xqh->hlink)) { uhci_dump_qh(xqh); } DPRINTF(("Enqueued QH:\n")); @@ -1962,9 +2304,10 @@ uhci_device_request(usbd_xfer_handle xfer) } #endif if (xfer->timeout && !sc->sc_bus.use_polling) { - usb_timeout(uhci_timeout, ii, - MS_TO_TICKS(xfer->timeout), ii->timeout_handle); + usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + uhci_timeout, ii); } + xfer->status = USBD_IN_PROGRESS; splx(s); return (USBD_NORMAL_COMPLETION); @@ -1989,7 +2332,7 @@ uhci_device_isoc_transfer(usbd_xfer_handle xfer) /* insert into schedule, */ uhci_device_isoc_enter(xfer); - /* and put on interrupt list if the pipe wasn't running */ + /* and start if the pipe wasn't running */ if (!err) uhci_device_isoc_start(SIMPLEQ_FIRST(&xfer->pipe->queue)); @@ -2003,7 +2346,7 @@ uhci_device_isoc_enter(usbd_xfer_handle xfer) 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; + uhci_soft_td_t *std; u_int32_t buf, len, status; int s, i, next, nframes; @@ -2011,8 +2354,12 @@ uhci_device_isoc_enter(usbd_xfer_handle xfer) "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 */ } @@ -2029,12 +2376,12 @@ uhci_device_isoc_enter(usbd_xfer_handle xfer) } xfer->status = USBD_IN_PROGRESS; - xfer->hcprivint = next; + UXFER(xfer)->curframe = next; buf = DMAADDR(&xfer->dmabuf, 0); - status = LE(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) | - UHCI_TD_ACTIVE | - UHCI_TD_IOS)); + 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++) { @@ -2042,12 +2389,12 @@ uhci_device_isoc_enter(usbd_xfer_handle xfer) if (++next >= UHCI_VFRAMELIST_COUNT) next = 0; len = xfer->frlengths[i]; - std->td.td_buffer = LE(buf); + std->td.td_buffer = htole32(buf); if (i == nframes - 1) - status |= LE(UHCI_TD_IOC); - std->td.td_status = status; - std->td.td_xtoken &= LE(~UHCI_TD_MAXLEN_MASK); - std->td.td_xtoken |= LE(UHCI_TD_SET_MAXLEN(len)); + 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)); @@ -2067,38 +2414,46 @@ 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 = upipe->iinfo; + 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 = xfer->hcprivint + xfer->nframes; + 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; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC - if (!ii->isdone) { + if (!ii->isdone) printf("uhci_device_isoc_start: not done, ii=%p\n", ii); - } ii->isdone = 0; #endif - LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); - + uhci_add_intr_info(sc, ii); + splx(s); return (USBD_IN_PROGRESS); @@ -2108,35 +2463,46 @@ void uhci_device_isoc_abort(usbd_xfer_handle xfer) { struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; - uhci_intr_info_t *ii = upipe->iinfo; uhci_soft_td_t **stds = upipe->u.iso.stds; uhci_soft_td_t *std; - int i, n, nframes; + 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; + } - /* Make interrupt routine ignore it, */ + /* Give xfer the requested abort code. */ xfer->status = USBD_CANCELLED; /* make hardware ignore it, */ nframes = xfer->nframes; - n = xfer->hcprivint; + n = UXFER(xfer)->curframe; + maxlen = 0; for (i = 0; i < nframes; i++) { std = stds[n]; - std->td.td_status &= LE(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); + 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; } - xfer->hcpriv = ii; + /* and wait until we are sure the hardware has finished. */ + delay(maxlen); - /* make sure hardware has completed, */ - if (xfer->device->bus->intr_context) { - /* We have no process context, so we can't use tsleep(). */ - timeout(uhci_abort_xfer_end, xfer, hz / USB_FRAMES_PER_SECOND); - } else { - usb_delay_ms(xfer->pipe->device->bus, 1); - /* and call final part of interrupt handler. */ - uhci_abort_xfer_end(xfer); - } +#ifdef DIAGNOSTIC + UXFER(xfer)->iinfo.isdone = 1; +#endif + /* Run callback and remove from interrupt list. */ + usb_transfer_complete(xfer); + + splx(s); } void @@ -2147,7 +2513,7 @@ uhci_device_isoc_close(usbd_pipe_handle pipe) uhci_softc_t *sc = (uhci_softc_t *)dev->bus; uhci_soft_td_t *std, *vstd; struct iso *iso; - int i; + int i, s; /* * Make sure all TDs are marked as inactive. @@ -2158,10 +2524,10 @@ uhci_device_isoc_close(usbd_pipe_handle pipe) iso = &upipe->u.iso; for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) - iso->stds[i]->td.td_status &= LE(~UHCI_TD_ACTIVE); + iso->stds[i]->td.td_status &= htole32(~UHCI_TD_ACTIVE); usb_delay_ms(&sc->sc_bus, 2); /* wait for completion */ - uhci_lock_frames(sc); + s = splusb(); for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { std = iso->stds[i]; for (vstd = sc->sc_vframes[i].htd; @@ -2171,14 +2537,14 @@ uhci_device_isoc_close(usbd_pipe_handle pipe) if (vstd == NULL) { /*panic*/ printf("uhci_device_isoc_close: %p not found\n", std); - uhci_unlock_frames(sc); + splx(s); return; } vstd->link = std->link; vstd->td.td_link = std->td.td_link; uhci_free_std(sc, std); } - uhci_unlock_frames(sc); + splx(s); free(iso->stds, M_USBHC); } @@ -2195,36 +2561,36 @@ uhci_setup_isoc(usbd_pipe_handle pipe) uhci_soft_td_t *std, *vstd; u_int32_t token; struct iso *iso; - int i; + int i, s; iso = &upipe->u.iso; iso->stds = malloc(UHCI_VFRAMELIST_COUNT * sizeof (uhci_soft_td_t *), M_USBHC, M_WAITOK); - token = LE(rd ? UHCI_TD_IN (0, endpt, addr, 0) : - UHCI_TD_OUT(0, endpt, addr, 0)); + 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 = LE(UHCI_TD_IOS); /* iso, inactive */ - std->td.td_xtoken = token; + std->td.td_status = htole32(UHCI_TD_IOS); /* iso, inactive */ + std->td.td_token = htole32(token); iso->stds[i] = std; } /* Insert TDs into schedule. */ - uhci_lock_frames(sc); + 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 = LE(std->physaddr); + vstd->td.td_link = htole32(std->physaddr | UHCI_PTR_TD); } - uhci_unlock_frames(sc); + splx(s); iso->next = -1; iso->inuse = 0; @@ -2241,43 +2607,79 @@ uhci_setup_isoc(usbd_pipe_handle pipe) void uhci_device_isoc_done(usbd_xfer_handle xfer) { - uhci_intr_info_t *ii = xfer->hcpriv; + 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 &= LE(~UHCI_TD_IOC); + ii->stdend->td.td_status &= htole32(~UHCI_TD_IOC); + + uhci_del_intr_info(ii); /* remove from active list */ - LIST_REMOVE(ii, list); /* 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 = xfer->hcpriv; + 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_intr_done: length=%d\n", xfer->actlen)); + 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 = 0; - sqh->qh.qh_elink = LE(UHCI_PTR_T); + sqh->elink = NULL; + sqh->qh.qh_elink = htole32(UHCI_PTR_T); } - uhci_free_std_chain(sc, ii->stdstart, 0); + 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, 1, xfer->flags, + uhci_alloc_std_chain(upipe, sc, xfer->length, + upipe->u.intr.isread, xfer->flags, &xfer->dmabuf, &data, &dataend); - dataend->td.td_status |= LE(UHCI_TD_IOC); + dataend->td.td_status |= htole32(UHCI_TD_IOC); #ifdef USB_DEBUG if (uhcidebug > 10) { @@ -2289,9 +2691,6 @@ uhci_device_intr_done(usbd_xfer_handle xfer) ii->stdstart = data; ii->stdend = dataend; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_intr_done: not done, ii=%p\n", ii); @@ -2301,10 +2700,14 @@ uhci_device_intr_done(usbd_xfer_handle xfer) for (i = 0; i < npoll; i++) { sqh = upipe->u.intr.qhs[i]; sqh->elink = data; - sqh->qh.qh_elink = LE(data->physaddr); + 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 { - ii->stdstart = 0; /* mark as inactive */ + DPRINTFN(5,("uhci_device_intr_done: removing\n")); + if (uhci_active_intr_info(ii)) + uhci_del_intr_info(ii); } } @@ -2312,79 +2715,91 @@ uhci_device_intr_done(usbd_xfer_handle xfer) void uhci_device_ctrl_done(usbd_xfer_handle xfer) { - uhci_intr_info_t *ii = xfer->hcpriv; + 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_ctrl_done: not a request\n"); + panic("uhci_device_ctrl_done: not a request"); #endif - LIST_REMOVE(ii, list); /* remove from active list */ + if (!uhci_active_intr_info(ii)) + return; - uhci_remove_ctrl(sc, upipe->u.ctl.sqh); + 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_ctrl_done: length=%d\n", xfer->actlen)); + 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 = xfer->hcpriv; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_softc_t *sc = ii->sc; struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; - LIST_REMOVE(ii, list); /* remove from active list */ + 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, 0); + uhci_free_std_chain(sc, ii->stdstart, NULL); - DPRINTFN(5, ("uhci_bulk_done: length=%d\n", xfer->actlen)); + 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, int n, uhci_soft_qh_t *sqh) +uhci_add_intr(uhci_softc_t *sc, uhci_soft_qh_t *sqh) { - struct uhci_vframe *vf = &sc->sc_vframes[n]; + struct uhci_vframe *vf = &sc->sc_vframes[sqh->pos]; uhci_soft_qh_t *eqh; - DPRINTFN(4, ("uhci_add_intr: n=%d sqh=%p\n", n, sqh)); + 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 = LE(sqh->physaddr | UHCI_PTR_Q); + eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH); vf->eqh = sqh; vf->bandwidth++; } -/* Remove interrupt QH, called with vflock. */ +/* Remove interrupt QH. */ void -uhci_remove_intr(uhci_softc_t *sc, int n, uhci_soft_qh_t *sqh) +uhci_remove_intr(uhci_softc_t *sc, uhci_soft_qh_t *sqh) { - struct uhci_vframe *vf = &sc->sc_vframes[n]; + struct uhci_vframe *vf = &sc->sc_vframes[sqh->pos]; uhci_soft_qh_t *pqh; - DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", n, sqh)); + DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", sqh->pos, sqh)); - for (pqh = vf->hqh; pqh->hlink != sqh; pqh = pqh->hlink) -#if defined(DIAGNOSTIC) || defined(USB_DEBUG) - if (LE(pqh->qh.qh_hlink) & UHCI_PTR_T) { - DPRINTF(("uhci_remove_intr: QH not found\n")); - return; - } -#else - ; -#endif + /* 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--; @@ -2397,7 +2812,7 @@ uhci_device_setintr(uhci_softc_t *sc, struct uhci_pipe *upipe, int ival) int i, npoll, s; u_int bestbw, bw, bestoffs, offs; - DPRINTFN(2, ("uhci_setintr: pipe=%p\n", upipe)); + DPRINTFN(2, ("uhci_device_setintr: pipe=%p\n", upipe)); if (ival == 0) { printf("uhci_setintr: 0 interval\n"); return (USBD_INVAL); @@ -2406,13 +2821,13 @@ uhci_device_setintr(uhci_softc_t *sc, struct uhci_pipe *upipe, int ival) if (ival > UHCI_VFRAMELIST_COUNT) ival = UHCI_VFRAMELIST_COUNT; npoll = (UHCI_VFRAMELIST_COUNT + ival - 1) / ival; - DPRINTFN(2, ("uhci_setintr: ival=%d npoll=%d\n", ival, npoll)); + DPRINTFN(2, ("uhci_device_setintr: ival=%d npoll=%d\n", ival, npoll)); upipe->u.intr.npoll = npoll; - upipe->u.intr.qhs = + 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. */ @@ -2425,30 +2840,23 @@ uhci_device_setintr(uhci_softc_t *sc, struct uhci_pipe *upipe, int ival) bestoffs = offs; } } - DPRINTFN(1, ("uhci_setintr: bw=%d offs=%d\n", bestbw, bestoffs)); + DPRINTFN(1, ("uhci_device_setintr: bw=%d offs=%d\n", bestbw, bestoffs)); - upipe->iinfo->stdstart = 0; for(i = 0; i < npoll; i++) { upipe->u.intr.qhs[i] = sqh = uhci_alloc_sqh(sc); - sqh->elink = 0; - sqh->qh.qh_elink = LE(UHCI_PTR_T); + sqh->elink = NULL; + sqh->qh.qh_elink = htole32(UHCI_PTR_T); sqh->pos = MOD(i * ival + bestoffs); - sqh->intr_info = upipe->iinfo; } #undef MOD s = splusb(); - LIST_INSERT_HEAD(&sc->sc_intrhead, upipe->iinfo, list); - splx(s); - - uhci_lock_frames(sc); /* Enter QHs into the controller data structures. */ for(i = 0; i < npoll; i++) - uhci_add_intr(sc, upipe->u.intr.qhs[i]->pos, - upipe->u.intr.qhs[i]); - uhci_unlock_frames(sc); + uhci_add_intr(sc, upipe->u.intr.qhs[i]); + splx(s); - DPRINTFN(5, ("uhci_setintr: returns %p\n", upipe)); + DPRINTFN(5, ("uhci_device_setintr: returns %p\n", upipe)); return (USBD_NORMAL_COMPLETION); } @@ -2463,8 +2871,12 @@ uhci_open(usbd_pipe_handle pipe) int ival; DPRINTFN(1, ("uhci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", - pipe, pipe->device->address, + 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: @@ -2477,9 +2889,6 @@ uhci_open(usbd_pipe_handle pipe) return (USBD_INVAL); } } else { - upipe->iinfo = uhci_alloc_intr_info(sc); - if (upipe->iinfo == 0) - return (USBD_NOMEM); switch (ed->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &uhci_device_ctrl_methods; @@ -2497,8 +2906,8 @@ uhci_open(usbd_pipe_handle pipe) uhci_free_std(sc, upipe->u.ctl.setup); goto bad; } - err = usb_allocmem(&sc->sc_bus, - sizeof(usb_device_request_t), + 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); @@ -2527,7 +2936,6 @@ uhci_open(usbd_pipe_handle pipe) return (USBD_NORMAL_COMPLETION); bad: - uhci_free_intr_info(upipe->iinfo); return (USBD_NOMEM); } @@ -2608,6 +3016,101 @@ uhci_str(usb_string_descriptor_t *p, int l, char *s) 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. */ @@ -2621,8 +3124,9 @@ uhci_root_ctrl_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -2638,13 +3142,16 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) 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\n"); + panic("uhci_root_ctrl_transfer: not a request"); #endif req = &xfer->request; - DPRINTFN(2,("uhci_root_ctrl_control type=0x%02x request=%02x\n", + DPRINTFN(2,("uhci_root_ctrl_control type=0x%02x request=%02x\n", req->bmRequestType, req->bRequest)); len = UGETW(req->wLength); @@ -2659,7 +3166,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) 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. */ @@ -2780,27 +3287,27 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) } switch(value) { case UHF_PORT_ENABLE: - x = UREAD2(sc, port); + x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: - x = UREAD2(sc, port); + x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: - x = UREAD2(sc, port); + x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); break; case UHF_C_PORT_CONNECTION: - x = UREAD2(sc, port); + x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_CSC); break; case UHF_C_PORT_ENABLE: - x = UREAD2(sc, port); + x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_POEDC); break; case UHF_C_PORT_OVER_CURRENT: - x = UREAD2(sc, port); + x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_OCIC); break; case UHF_C_PORT_RESET: @@ -2827,7 +3334,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) goto ret; } if (len > 0) { - *(u_int8_t *)buf = + *(u_int8_t *)buf = (UREAD2(sc, port) & UHCI_PORTSC_LS) >> UHCI_PORTSC_LS_SHIFT; totlen = 1; @@ -2865,21 +3372,21 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) } x = UREAD2(sc, port); status = change = 0; - if (x & UHCI_PORTSC_CCS ) + if (x & UHCI_PORTSC_CCS) status |= UPS_CURRENT_CONNECT_STATUS; - if (x & UHCI_PORTSC_CSC ) + if (x & UHCI_PORTSC_CSC) change |= UPS_C_CONNECT_STATUS; - if (x & UHCI_PORTSC_PE ) + if (x & UHCI_PORTSC_PE) status |= UPS_PORT_ENABLED; - if (x & UHCI_PORTSC_POEDC) + if (x & UHCI_PORTSC_POEDC) change |= UPS_C_PORT_ENABLED; - if (x & UHCI_PORTSC_OCI ) + if (x & UHCI_PORTSC_OCI) status |= UPS_OVERCURRENT_INDICATOR; - if (x & UHCI_PORTSC_OCIC ) + if (x & UHCI_PORTSC_OCIC) change |= UPS_C_OVERCURRENT_INDICATOR; - if (x & UHCI_PORTSC_SUSP ) + if (x & UHCI_PORTSC_SUSP) status |= UPS_SUSPEND; - if (x & UHCI_PORTSC_LSDA ) + if (x & UHCI_PORTSC_LSDA) status |= UPS_LOW_SPEED; status |= UPS_PORT_POWER; if (sc->sc_isreset) @@ -2906,26 +3413,16 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) } switch(value) { case UHF_PORT_ENABLE: - x = UREAD2(sc, port); + x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: - x = UREAD2(sc, port); + x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: - x = UREAD2(sc, port); - UWRITE2(sc, port, x | UHCI_PORTSC_PR); - usb_delay_ms(&sc->sc_bus, 10); - UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); - delay(100); - x = UREAD2(sc, port); - UWRITE2(sc, port, x | UHCI_PORTSC_PE); - delay(100); - DPRINTFN(3,("uhci port %d reset, status = 0x%04x\n", - index, UREAD2(sc, port))); - sc->sc_isreset = 1; - break; + err = uhci_portreset(sc, index); + goto ret; case UHF_PORT_POWER: /* Pretend we turned on power */ err = USBD_NORMAL_COMPLETION; @@ -2951,7 +3448,6 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) err = USBD_NORMAL_COMPLETION; ret: xfer->status = err; - xfer->hcpriv = 0; s = splusb(); usb_transfer_complete(xfer); splx(s); @@ -2978,14 +3474,17 @@ uhci_root_intr_abort(usbd_xfer_handle xfer) { uhci_softc_t *sc = (uhci_softc_t *)xfer->pipe->device->bus; - usb_untimeout(uhci_timo, xfer, xfer->timo_handle); - sc->sc_has_timo = NULL; + 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); } @@ -2999,8 +3498,9 @@ uhci_root_intr_transfer(usbd_xfer_handle xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -3012,12 +3512,15 @@ 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_transfer: xfer=%p len=%d flags=%d\n", + 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_timeout(uhci_timo, xfer, sc->sc_ival, xfer->timo_handle); - sc->sc_has_timo = xfer; + usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer); + sc->sc_intr_xfer = xfer; return (USBD_IN_PROGRESS); } @@ -3027,7 +3530,7 @@ uhci_root_intr_close(usbd_pipe_handle pipe) { uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; - usb_untimeout(uhci_timo, pipe->intrxfer, pipe->intrxfer->timo_handle); - sc->sc_has_timo = NULL; + 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/bus/usb/uhci_pci.c b/sys/bus/usb/uhci_pci.c new file mode 100644 index 0000000000..01fe869b7e --- /dev/null +++ b/sys/bus/usb/uhci_pci.c @@ -0,0 +1,415 @@ +/*- + * 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. + * + * $FreeBSD: src/sys/dev/usb/uhci_pci.c,v 1.51 2003/11/28 05:28:29 imp Exp $ + * $DragonFly: src/sys/bus/usb/uhci_pci.c,v 1.1 2003/12/30 01:01:44 dillon Exp $ + */ + +/* 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 +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +#include + +#include +#include +#include +#endif + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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); + + 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(self, SYS_RES_IOPORT, &rid, + 0, ~0, 1, 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(self, SYS_RES_IRQ, &rid, + 0, ~0, 1, + 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/bus/usb/uhcireg.h b/sys/bus/usb/uhcireg.h index a1afbbd423..3a92eea1ec 100644 --- a/sys/bus/usb/uhcireg.h +++ b/sys/bus/usb/uhcireg.h @@ -1,6 +1,8 @@ -/* $NetBSD: uhcireg.h,v 1.9 1999/11/20 00:57:09 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/uhcireg.h,v 1.14.2.1 2000/07/02 11:43:59 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/uhcireg.h,v 1.3 2003/06/21 17:27:24 dillon Exp $ */ +/* + * $NetBSD: uhcireg.h,v 1.15 2002/02/11 11:41:30 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/uhcireg.h,v 1.21 2003/07/04 01:50:38 jmg Exp $ + * $DragonFly: src/sys/bus/usb/uhcireg.h,v 1.4 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -76,6 +78,7 @@ #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 @@ -85,7 +88,6 @@ #define UHCI_FRNUM 0x06 #define UHCI_FRNUM_MASK 0x03ff - #define UHCI_FLBASEADDR 0x08 @@ -107,6 +109,9 @@ #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 @@ -115,12 +120,19 @@ typedef u_int32_t uhci_physaddr_t; #define UHCI_PTR_T 0x00000001 -#define UHCI_PTR_Q 0x00000002 +#define UHCI_PTR_TD 0x00000000 +#define UHCI_PTR_QH 0x00000002 #define UHCI_PTR_VF 0x00000004 /* - * The Queue Heads and Transfer Descriptors and accessed - * by both the CPU and the USB controller which runs + * 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 @@ -148,7 +160,7 @@ typedef struct { #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_xtoken; + u_int32_t td_token; #define UHCI_TD_PID_IN 0x00000069 #define UHCI_TD_PID_OUT 0x000000e1 #define UHCI_TD_PID_SETUP 0x0000002d diff --git a/sys/bus/usb/uhcivar.h b/sys/bus/usb/uhcivar.h index fa7818c767..490e96ab0b 100644 --- a/sys/bus/usb/uhcivar.h +++ b/sys/bus/usb/uhcivar.h @@ -1,6 +1,8 @@ -/* $NetBSD: uhcivar.h,v 1.21 2000/01/18 20:11:01 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/uhcivar.h,v 1.16.2.5 2000/10/31 23:23:29 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/uhcivar.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: uhcivar.h,v 1.33 2002/02/11 11:41:30 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/uhcivar.h,v 1.36 2003/07/15 23:19:49 jmg Exp $ + * $DragonFly: src/sys/bus/usb/uhcivar.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -42,13 +44,14 @@ /* * To avoid having 1024 TDs for each isochronous transfer we introduce * a virtual frame list. Every UHCI_VFRAMELIST_COUNT entries in the real - * frame list points to a non-active TD. These, in turn, which form the - * starts of the virtual frame list. This also has the advantage that it - * simplifies linking in/out TD/QH in the schedule. + * 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. + * 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. */ @@ -75,14 +78,20 @@ typedef struct uhci_intr_info { uhci_soft_td_t *stdstart; uhci_soft_td_t *stdend; LIST_ENTRY(uhci_intr_info) list; -#if defined(__FreeBSD__) - struct callout_handle timeout_handle; -#endif /* defined(__FreeBSD__) */ #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. */ @@ -91,14 +100,14 @@ struct uhci_soft_td { 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 128 /*(PAGE_SIZE / UHCI_TD_SIZE)*/ +#define UHCI_STD_CHUNK (PAGE_SIZE / UHCI_STD_SIZE) /* * Extra information that we need for a QH. @@ -109,12 +118,10 @@ struct uhci_soft_qh { uhci_soft_td_t *elink; /* soft version of qh_elink */ uhci_physaddr_t physaddr; /* QH's physical address. */ int pos; /* Timeslot position */ - uhci_intr_info_t *intr_info; /* Who to call on completion. */ -/* XXX should try to shrink with 4 bytes to fit into 32 bytes */ }; /* 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 128 /*(PAGE_SIZE / UHCI_QH_SIZE)*/ +#define UHCI_SQH_CHUNK (PAGE_SIZE / UHCI_SQH_SIZE) /* * Information about an entry in the virtual frame list. @@ -131,6 +138,7 @@ 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; @@ -142,10 +150,14 @@ typedef struct uhci_softc { usb_dma_t sc_dma; struct uhci_vframe sc_vframes[UHCI_VFRAMELIST_COUNT]; - uhci_soft_qh_t *sc_ctl_start; /* dummy QH for control */ - uhci_soft_qh_t *sc_ctl_end; /* last control QH */ + uhci_soft_qh_t *sc_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 */ @@ -158,18 +170,20 @@ typedef struct uhci_softc { 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 hug intrs */ - usbd_xfer_handle sc_has_timo; /* root hub interrupt transfer */ - - char sc_vflock; /* for lock virtual frame list */ -#define UHCI_HAS_LOCK 1 -#define UHCI_WANT_LOCK 2 + 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 */ @@ -179,7 +193,7 @@ typedef struct uhci_softc { void *sc_shutdownhook; /* cookie from shutdown hook */ #endif - device_ptr_t sc_child; /* /dev/usb device */ + device_ptr_t sc_child; /* /dev/usb# device */ } uhci_softc_t; usbd_status uhci_init(uhci_softc_t *); diff --git a/sys/bus/usb/uhub.c b/sys/bus/usb/uhub.c index 00ed415dad..0affe4d94f 100644 --- a/sys/bus/usb/uhub.c +++ b/sys/bus/usb/uhub.c @@ -1,6 +1,8 @@ -/* $NetBSD: uhub.c,v 1.47 2000/09/24 02:08:38 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.21.2.7 2002/11/06 20:23:50 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/uhub.c,v 1.3 2003/08/07 21:16:47 dillon Exp $ */ +/* + * $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $ + * $FreeBSD: src/sys/dev/usb/uhub.c,v 1.54 2003/08/24 17:55:55 obrien Exp $ + * $DragonFly: src/sys/bus/usb/uhub.c,v 1.4 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -40,7 +42,7 @@ */ /* - * USB spec: http://www.usb.org/developers/docs.htm + * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #include @@ -54,8 +56,8 @@ #include #include #include "bus_if.h" -#include #endif +#include #include @@ -69,10 +71,10 @@ #ifdef USB_DEBUG #define DPRINTF(x) if (uhubdebug) logprintf x #define DPRINTFN(n,x) if (uhubdebug>(n)) logprintf x -static int uhubdebug = 0; +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"); + &uhubdebug, 0, "uhub debug level"); #else #define DPRINTF(x) #define DPRINTFN(n,x) @@ -90,11 +92,12 @@ 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 @@ -104,24 +107,24 @@ Static bus_child_detached_t uhub_child_detached; USB_DECLARE_DRIVER(uhub); /* Create the driver instance for the hub connected to hub case */ -struct cfattach uhub_uhub_ca = { - sizeof(struct uhub_softc), uhub_match, uhub_attach, - uhub_detach, uhub_activate -}; +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), @@ -140,9 +143,9 @@ 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. */ @@ -155,7 +158,7 @@ USB_ATTACH(uhub) { USB_ATTACH_START(uhub, sc, uaa); usbd_device_handle dev = uaa->device; - char devinfo[1024]; + char *devinfo; usbd_status err; struct usbd_hub *hub; usb_device_request_t req; @@ -163,7 +166,11 @@ USB_ATTACH(uhub) 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); @@ -174,19 +181,21 @@ USB_ATTACH(uhub) 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; - USETW(req.wValue, 0); + 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")); @@ -199,6 +208,7 @@ USB_ATTACH(uhub) 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; } @@ -211,17 +221,19 @@ USB_ATTACH(uhub) hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port), M_USBDEV, M_NOWAIT); - if (hub == NULL) + 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 ? dev->powersrc->parent->self_powered : 0)); if (!dev->self_powered && dev->powersrc->parent != NULL && @@ -248,10 +260,10 @@ USB_ATTACH(uhub) } err = usbd_open_pipe_intr(iface, ed->bEndpointAddress, - USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status, + 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", + printf("%s: cannot open interrupt pipe\n", USBDEVNAME(sc->sc_dev)); goto bad; } @@ -259,6 +271,8 @@ USB_ATTACH(uhub) /* 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 @@ -276,6 +290,7 @@ USB_ATTACH(uhub) * For all ports * get port status * if device connected + * wait 100 ms * turn on reset * wait * clear C_PORT_RESET @@ -304,7 +319,7 @@ USB_ATTACH(uhub) /* 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", + 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)); @@ -320,6 +335,7 @@ USB_ATTACH(uhub) bad: free(hub, M_USBDEV); + free(devinfo, M_TEMP); dev->hub = 0; USB_ATTACH_ERROR_RETURN; } @@ -331,6 +347,7 @@ uhub_explore(usbd_device_handle dev) struct uhub_softc *sc = dev->hub->hubsoftc; struct usbd_port *up; usbd_status err; + int speed; int port; int change, status; @@ -353,8 +370,8 @@ uhub_explore(usbd_device_handle dev) } status = UGETW(up->status.wPortStatus); change = UGETW(up->status.wPortChange); - DPRINTFN(3,("uhub_explore: port %d status 0x%04x 0x%04x\n", - port, status, change)); + 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); @@ -380,8 +397,14 @@ uhub_explore(usbd_device_handle dev) DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_" "STATUS\n", port)); /* No status change, just do recursive explore. */ - if (up->device && up->device->hub) + 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; } @@ -404,7 +427,7 @@ uhub_explore(usbd_device_handle dev) 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, + usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); } if (!(status & UPS_CURRENT_CONNECT_STATUS)) { @@ -425,15 +448,38 @@ uhub_explore(usbd_device_handle dev) /* Reset port, which implies enabling it. */ if (usbd_reset_port(dev, port, &up->status)) { - printf("uhub_explore: port=%d reset failed\n", - port); + 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, status & UPS_LOW_SPEED, - port, up); + 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, " @@ -441,7 +487,7 @@ uhub_explore(usbd_device_handle dev) /* 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. @@ -472,7 +518,6 @@ uhub_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: if (hub == NULL) /* malfunctioning hub */ @@ -520,7 +565,9 @@ USB_DETACH(uhub) 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; @@ -540,7 +587,7 @@ uhub_child_detached(device_t self, device_t child) int port; int i; - if (!devhub->hub) + if (!devhub->hub) /* should never happen; children are only created after init */ panic("hub not fully initialised, but child deleted?"); @@ -557,6 +604,19 @@ uhub_child_detached(device_t self, device_t child) } } } + +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 @@ -572,10 +632,10 @@ 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_NORMAL_COMPLETION) + if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_ipipe); - - usb_needs_explore(sc->sc_hub->bus); + else if (status == USBD_NORMAL_COMPLETION) + usb_needs_explore(sc->sc_hub); } #if defined(__FreeBSD__) diff --git a/sys/bus/usb/usb.c b/sys/bus/usb/usb.c index fce536a782..7719284ce6 100644 --- a/sys/bus/usb/usb.c +++ b/sys/bus/usb/usb.c @@ -1,6 +1,14 @@ -/* $NetBSD: usb.c,v 1.33 1999/11/22 21:57:09 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usb.c,v 1.26.2.9 2002/11/13 15:15:22 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/usb.c,v 1.8 2003/08/07 21:16:47 dillon Exp $ */ +/* + * $NetBSD: usb.c,v 1.68 2002/02/20 20:30:12 christos Exp $ + * $FreeBSD: src/sys/dev/usb/usb.c,v 1.95 2003/11/09 23:54:21 joe Exp $ + * $DragonFly: src/sys/bus/usb/usb.c,v 1.9 2003/12/30 01:01:44 dillon 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 $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -48,20 +56,29 @@ #include #include #include +#include #include +#if __FreeBSD_version >= 500000 +#include +#endif #if defined(__NetBSD__) || defined(__OpenBSD__) #include -#include -#include #elif defined(__FreeBSD__) +#include #include #include #include #include #endif +#include +#include #include #include +#if __FreeBSD_version >= 500014 +#include +#else #include +#endif #include #include #include @@ -97,13 +114,7 @@ SYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging"); int usbdebug = 0; SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW, &usbdebug, 0, "usb debug level"); -#ifdef USB_DEBUG -extern int uhcidebug; -#endif -#ifdef USB_DEBUG -extern int ohcidebug; -#endif -/* +/* * 0 - do usual exploration * 1 - do not use timeout exploration * >1 - do no exploration @@ -119,20 +130,17 @@ struct usb_softc { usbd_bus_handle sc_bus; /* USB controller */ struct usbd_port sc_port; /* dummy port for root hub */ -#if defined(__FreeBSD__) - /* This part should be deleted when kthreads is available */ - struct selinfo sc_consel; /* waiting for connect change */ -#else - struct proc *sc_event_thread; -#endif + struct thread *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_open_t usbopen; d_close_t usbclose; d_read_t usbread; d_ioctl_t usbioctl; @@ -158,23 +166,24 @@ struct cdevsw usb_cdevsw = { }; #endif -Static usbd_status usb_discover(struct usb_softc *); -#if defined(__NetBSD__) || defined(__OpenBSD__) +Static void usb_discover(void *); Static void usb_create_event_thread(void *); Static void usb_event_thread(void *); -#endif +Static void usb_task_thread(void *); +Static usb_proc_ptr usb_task_thread_proc = NULL; -#define USB_MAX_EVENTS 50 +#define USB_MAX_EVENTS 100 struct usb_event_q { struct usb_event ue; - SIMPLEQ_ENTRY(usb_event_q) next; + TAILQ_ENTRY(usb_event_q) next; }; -Static SIMPLEQ_HEAD(, usb_event_q) usb_events = - SIMPLEQ_HEAD_INITIALIZER(usb_events); +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 thread *usb_async_proc; /* process who wants USB SIGIO */ +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 *); @@ -191,6 +200,10 @@ USB_DECLARE_DRIVER_INIT(usb, DEVMETHOD(device_shutdown, bus_generic_shutdown) ); +#if defined(__FreeBSD__) +MODULE_VERSION(usb, 1); +#endif + USB_MATCH(usb) { DPRINTF(("usbd_match\n")); @@ -209,6 +222,8 @@ USB_ATTACH(usb) usbd_device_handle dev; usbd_status err; int usbrev; + int speed; + struct usb_event ue; sc->sc_dev = self; @@ -224,51 +239,90 @@ USB_ATTACH(usb) #endif usbrev = sc->sc_bus->usbrev; printf(": USB revision %s", usbrev_str[usbrev]); - if (usbrev != USBREV_1_0 && usbrev != USBREV_1_1) { + 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"); - err = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0, 0, 0, + /* 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", + 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 (cold) { - sc->sc_bus->use_polling++; +#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); - sc->sc_bus->use_polling--; - } #endif } else { - printf("%s: root hub problem, error=%d\n", - USBDEVNAME(sc->sc_dev), err); + printf("%s: root hub problem, error=%d\n", + USBDEVNAME(sc->sc_dev), err); sc->sc_dying = 1; } + if (cold) + sc->sc_bus->use_polling--; - kthread_create(usb_create_event_thread, sc); + 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, - 0644, "usb%d", device_get_unit(self)); + 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, - 0644, "usb"); + 0660, "usb"); global_init_done = 1; } #endif @@ -276,18 +330,62 @@ USB_ATTACH(usb) USB_ATTACH_SUCCESS_RETURN; } -#if defined(__NetBSD__) || defined(__OpenBSD__) void usb_create_event_thread(void *arg) { struct usb_softc *sc = arg; + static int created = 0; - if (kthread_create1(usb_event_thread, sc, &sc->sc_event_thread, - "%s", sc->sc_dev.dv_xname)) { + 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", - sc->sc_dev.dv_xname); + 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 @@ -297,20 +395,36 @@ usb_event_thread(void *arg) 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); - (void)tsleep(&sc->sc_bus->needs_explore, 0, "usbevt", #ifdef USB_DEBUG - usb_noexplore ? 0 : + (void)tsleep(&sc->sc_bus->needs_explore, 0, "usbevt", + usb_noexplore ? 0 : hz * 60); +#else + (void)tsleep(&sc->sc_bus->needs_explore, 0, "usbevt", + hz * 60); #endif - hz*60 - ); DPRINTFN(2,("usb_event_thread: woke up\n")); } - sc->sc_event_thread = 0; + sc->sc_event_thread = NULL; /* In case parent is waiting for us to exit. */ wakeup(sc); @@ -319,6 +433,37 @@ usb_event_thread(void *arg) kthread_exit(); } +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, 0, "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) { @@ -342,14 +487,14 @@ usbopen(dev_t dev, int flag, int mode, usb_proc_ptr p) usb_dev_open = 1; usb_async_proc = NULL; return (0); - } else { - USB_GET_SC_OPEN(usb, unit, sc); + } - if (sc->sc_dying) - return (EIO); + USB_GET_SC_OPEN(usb, unit, sc); - return (0); - } + if (sc->sc_dying) + return (EIO); + + return (0); } int @@ -410,10 +555,16 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) case FIONBIO: /* All handled in the upper FS layer. */ return (0); - + case FIOASYNC: if (*(int *)data) +#if defined(__DragonFly__) + usb_async_proc = p->td_proc; +#elif __FreeBSD_version >= 500000 + usb_async_proc = p->td_proc; +#else usb_async_proc = p; +#endif else usb_async_proc = NULL; return (0); @@ -429,18 +580,10 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) return (EIO); switch (cmd) { -#if defined(__FreeBSD__) - /* This part should be deleted when kthreads is available */ +#if defined(__FreeBSD__) + /* This part should be deleted */ case USB_DISCOVER: - usb_discover(sc); break; -#endif -#ifdef USB_DEBUG - case USB_SETDEBUG: - usbdebug = ((*(int *)data) & 0x000000ff); - uhcidebug = ((*(int *)data) & 0x0000ff00) >> 8; - ohcidebug = ((*(int *)data) & 0x00ff0000) >> 16; - break; #endif case USB_REQUEST: { @@ -456,7 +599,7 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) 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 || + if (addr < 0 || addr >= USB_MAX_DEVICES || sc->sc_bus->devices[addr] == 0) return (EINVAL); if (len != 0) { @@ -468,7 +611,7 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = - ur->ucr_request.bmRequestType & UT_READ ? + ur->ucr_request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_td = p; ptr = malloc(len, M_TEMP, M_WAITOK); @@ -479,7 +622,8 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) } } err = usbd_do_request_flags(sc->sc_bus->devices[addr], - &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen); + &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen, + USBD_DEFAULT_TIMEOUT); if (err) { error = EIO; goto ret; @@ -506,9 +650,9 @@ usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) if (addr < 1 || addr >= USB_MAX_DEVICES) return (EINVAL); dev = sc->sc_bus->devices[addr]; - if (dev == 0) + if (dev == NULL) return (ENXIO); - usbd_fill_deviceinfo(dev, di); + usbd_fill_deviceinfo(dev, di, 1); break; } @@ -533,33 +677,16 @@ usbpoll(dev_t dev, int events, usb_proc_ptr p) mask = POLLIN | POLLRDNORM; s = splusb(); - if ((events & mask) && usb_nevents > 0) + if (events & mask && usb_nevents > 0) revents |= events & mask; - if (revents == 0 && (events & mask)) { - DPRINTFN(2,("usb: sleeping on %p\n", &usb_selevent)); + if (revents == 0 && events & mask) selrecord(p, &usb_selevent); - } splx(s); return (revents); } else { #if defined(__FreeBSD__) - /* This part should be deleted when kthreads is available */ - struct usb_softc *sc; - - USB_GET_SC(usb, unit, sc); - - revents = 0; - mask = POLLOUT | POLLRDNORM; - - s = splusb(); - if ((events & mask) && sc->sc_bus->needs_explore) - revents |= events & mask; - if (revents == 0 && (events & mask)) - selrecord(p, &sc->sc_consel); - splx(s); - - return (revents); + return (0); /* select/poll never wakes up - back compat */ #else return (ENXIO); #endif @@ -567,15 +694,23 @@ usbpoll(dev_t dev, int events, usb_proc_ptr p) } /* Explore device tree from the root. */ -usbd_status -usb_discover(struct usb_softc *sc) +Static void +usb_discover(void *v) { + struct usb_softc *sc = v; + #if defined(__FreeBSD__) - /* The splxxx parts should be deleted when kthreads is available */ + /* 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. @@ -596,19 +731,14 @@ usb_discover(struct usb_softc *sc) #if defined(__FreeBSD__) splx(s); #endif - - return (USBD_NORMAL_COMPLETION); } void -usb_needs_explore(usbd_bus_handle bus) +usb_needs_explore(usbd_device_handle dev) { - bus->needs_explore = 1; -#if defined(__FreeBSD__) - /* This part should be deleted when kthreads is available */ - selwakeup(&bus->usbctl->sc_consel); -#endif - wakeup(&bus->needs_explore); + DPRINTFN(2,("usb_needs_explore\n")); + dev->bus->needs_explore = 1; + wakeup(&dev->bus->needs_explore); } /* Called at splusb() */ @@ -619,51 +749,112 @@ usb_get_next_event(struct usb_event *ue) if (usb_nevents <= 0) return (0); - ueq = SIMPLEQ_FIRST(&usb_events); + 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; - SIMPLEQ_REMOVE_HEAD(&usb_events, ueq, next); + TAILQ_REMOVE(&usb_events, ueq, next); free(ueq, M_USBDEV); usb_nevents--; return (1); } void -usbd_add_event(int type, usbd_device_handle dev) +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_nevents >= USB_MAX_EVENTS) { + 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); } - /* Don't want to wait here inside splusb() */ - ueq = malloc(sizeof *ueq, M_USBDEV, M_NOWAIT); - if (ueq == NULL) { - printf("usb: no memory, event dropped\n"); - splx(s); - return; - } - ueq->ue.ue_type = type; - ueq->ue.u.ue_driver.ue_cookie = dev->cookie; - usbd_fill_deviceinfo(dev, &ueq->ue.u.ue_device); - microtime(&thetime); - TIMEVAL_TO_TIMESPEC(&thetime, &ueq->ue.ue_time); - SIMPLEQ_INSERT_TAIL(&usb_events, ueq, next); + TAILQ_INSERT_TAIL(&usb_events, ueq, next); + usb_nevents++; wakeup(&usb_events); - selwakeup(&usb_selevent); - if (usb_async_proc != NULL && usb_async_proc->td_proc) - psignal(usb_async_proc->td_proc, SIGIO); + 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, devact act) +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; @@ -672,11 +863,10 @@ usb_activate(device_ptr_t self, devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: sc->sc_dying = 1; - if (dev && dev->cdesc && dev->subdevs) { + if (dev != NULL && dev->cdesc != NULL && dev->subdevs != NULL) { for (i = 0; dev->subdevs[i]; i++) rv |= config_deactivate(dev->subdevs[i]); } @@ -689,17 +879,18 @@ 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) + if (sc->sc_port.device != NULL) usb_disconnect_port(&sc->sc_port, self); /* Kill off event thread. */ - if (sc->sc_event_thread) { + if (sc->sc_event_thread != NULL) { wakeup(&sc->sc_bus->needs_explore); if (tsleep(sc, 0, "usbdet", hz * 60)) printf("%s: event thread didn't die\n", @@ -708,6 +899,21 @@ usb_detach(device_ptr_t self, int flags) } 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__) @@ -724,4 +930,5 @@ usb_detach(device_t self) #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/bus/usb/usb.h b/sys/bus/usb/usb.h index 359fcead56..d8f9a1a834 100644 --- a/sys/bus/usb/usb.h +++ b/sys/bus/usb/usb.h @@ -1,6 +1,8 @@ -/* $NetBSD: usb.h,v 1.38 1999/10/20 21:02:39 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usb.h,v 1.17.2.11 2002/11/13 15:15:22 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/usb.h,v 1.3 2003/08/07 21:16:47 dillon Exp $ */ +/* + * $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usb.h,v 1.38 2002/11/06 21:37:21 joe Exp $ + * $DragonFly: src/sys/bus/usb/usb.h,v 1.4 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -48,27 +50,18 @@ #if defined(__NetBSD__) || defined(__OpenBSD__) #include +#endif #if defined(_KERNEL) #include "usb_port.h" #endif /* _KERNEL */ -#elif defined(__FreeBSD__) -#if defined(_KERNEL) -#include - -MALLOC_DECLARE(M_USB); -MALLOC_DECLARE(M_USBDEV); -MALLOC_DECLARE(M_USBHC); - -#include "usb_port.h" -#endif /* _KERNEL */ -#endif /* __FreeBSD__ */ - /* these three defines are used by usbd to autoload the usb kld */ -#define USB_KLD "usb" +#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 @@ -337,7 +330,7 @@ typedef struct { (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1) /* deprecated */ uByte PortPowerCtrlMask[1]; } UPACKED usb_hub_descriptor_t; -#define USB_HUB_DESCRIPTOR_SIZE 8 /* includes deprecated PortPowerCtrlMask */ +#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */ typedef struct { uByte bLength; @@ -513,18 +506,20 @@ typedef struct { #if 0 /* These are the values from the spec. */ #define USB_PORT_RESET_DELAY 10 /* ms */ -#define USB_PORT_RESET_SETTLE 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_TIME (20*5) /* 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_RESET_RECOVERY 50 /* ms */ -#define USB_PORT_POWERUP_DELAY 200 /* 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 */ @@ -616,7 +611,10 @@ struct usb_device_info { u_int8_t udi_subclass; u_int8_t udi_protocol; u_int8_t udi_config; - u_int8_t udi_lowspeed; + 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]; diff --git a/sys/bus/usb/usb_ethersubr.c b/sys/bus/usb/usb_ethersubr.c index e3a4042512..f2f5f15bcd 100644 --- a/sys/bus/usb/usb_ethersubr.c +++ b/sys/bus/usb/usb_ethersubr.c @@ -29,10 +29,9 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/usb_ethersubr.c,v 1.4.2.4 2002/11/06 14:23:20 joe Exp $ - * $DragonFly: src/sys/bus/usb/usb_ethersubr.c,v 1.5 2003/11/08 07:57:45 dillon Exp $ * - * $FreeBSD: src/sys/dev/usb/usb_ethersubr.c,v 1.4.2.4 2002/11/06 14:23:20 joe Exp $ + * $FreeBSD: src/sys/dev/usb/usb_ethersubr.c,v 1.17 2003/11/14 11:09:45 johan Exp $ + * $DragonFly: src/sys/bus/usb/usb_ethersubr.c,v 1.6 2003/12/30 01:01:44 dillon Exp $ */ /* @@ -72,10 +71,11 @@ Static struct ifqueue usbq_rx; Static struct ifqueue usbq_tx; +Static int mtx_inited = 0; Static void usbintr(struct mbuf *m); -Static void usbintr(struct mbuf *m) +Static void usbintr(struct mbuf *m) /* dummy mbuf */ { struct ether_header *eh; struct usb_qdat *q; @@ -118,8 +118,12 @@ Static void usbintr(struct mbuf *m) return; } -void usb_register_netisr() +void +usb_register_netisr(void) { + if (mtx_inited) + return; + mtx_inited = 1; netisr_register(NETISR_USB, cpu0_portfn, usbintr); return; } @@ -128,14 +132,14 @@ void usb_register_netisr() * 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; +void +usb_ether_input(struct mbuf *m) { netisr_queue(NETISR_USB, m); } -void usb_tx_done(m) - struct mbuf *m; +void +usb_tx_done(struct mbuf *m) { netisr_queue(NETISR_USB, m); } diff --git a/sys/bus/usb/usb_ethersubr.h b/sys/bus/usb/usb_ethersubr.h index 518a92542a..dafe6a856f 100644 --- a/sys/bus/usb/usb_ethersubr.h +++ b/sys/bus/usb/usb_ethersubr.h @@ -29,8 +29,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/usb_ethersubr.h,v 1.4.2.1 2002/11/06 14:23:20 joe Exp $ - * $DragonFly: src/sys/bus/usb/usb_ethersubr.h,v 1.3 2003/09/15 23:38:12 hsu Exp $ + * $FreeBSD: src/sys/dev/usb/usb_ethersubr.h,v 1.6 2003/03/04 23:19:55 jlemon Exp $ + * $DragonFly: src/sys/bus/usb/usb_ethersubr.h,v 1.4 2003/12/30 01:01:44 dillon Exp $ */ #ifndef _USB_ETHERSUBR_H_ diff --git a/sys/bus/usb/usb_if.m b/sys/bus/usb/usb_if.m index eda84ad4e1..a3ff90c135 100644 --- a/sys/bus/usb/usb_if.m +++ b/sys/bus/usb/usb_if.m @@ -25,8 +25,8 @@ # (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: src/sys/dev/usb/usb_if.m,v 1.7.2.2 2000/05/07 14:58:03 n_hibma Exp $ -# $DragonFly: src/sys/bus/usb/usb_if.m,v 1.2 2003/06/17 04:28:32 dillon Exp $ +# $FreeBSD: src/sys/dev/usb/usb_if.m,v 1.9 2000/04/08 14:17:05 dfr Exp $ +# $DragonFly: src/sys/bus/usb/usb_if.m,v 1.3 2003/12/30 01:01:44 dillon Exp $ # # USB interface description diff --git a/sys/bus/usb/usb_mem.c b/sys/bus/usb/usb_mem.c new file mode 100644 index 0000000000..7fd66b2ea0 --- /dev/null +++ b/sys/bus/usb/usb_mem.c @@ -0,0 +1,306 @@ +/* + * $NetBSD: usb_mem.c,v 1.26 2003/02/01 06:23:40 thorpej Exp $ + * $FreeBSD: src/sys/dev/usb/usb_mem.c,v 1.5 2003/10/04 22:13:21 joe Exp $ + * $DragonFly: src/sys/bus/usb/usb_mem.c,v 1.1 2003/12/30 01:01:44 dillon 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. + */ + +/* + * 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 +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include /* for usbdivar.h */ +#include +#elif defined(__FreeBSD__) +#include +#include +#include +#endif +#include + +#include +#include + +#ifdef DIAGNOSTIC +#include +#endif + +#include +#include +#include /* just for usb_dma_t */ +#include + +#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/bus/usb/usb_mem.h b/sys/bus/usb/usb_mem.h index a8adcad1aa..c6b3c627e8 100644 --- a/sys/bus/usb/usb_mem.h +++ b/sys/bus/usb/usb_mem.h @@ -1,6 +1,8 @@ -/* $NetBSD: usb_mem.h,v 1.9 1999/10/13 08:10:58 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usb_mem.h,v 1.10.2.2 2000/10/31 23:23:30 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/usb_mem.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: usb_mem.h,v 1.18 2002/05/28 17:45:17 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usb_mem.h,v 1.20 2003/09/01 01:07:24 jmg Exp $ + * $DragonFly: src/sys/bus/usb/usb_mem.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -39,11 +41,14 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) 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; @@ -52,39 +57,13 @@ typedef struct usb_dma_block { LIST_ENTRY(usb_dma_block) next; } usb_dma_block_t; -#define DMAADDR(dma, offset) ((dma)->block->segs[0].ds_addr + (dma)->offs + (offset)) -#define KERNADDR(dma, offset) ((void *)((dma)->block->kaddr + (dma)->offs) + (offset)) - -usbd_status usb_allocmem(usbd_bus_handle,size_t,size_t, usb_dma_t *); -void usb_freemem(usbd_bus_handle, usb_dma_t *); - -#elif defined(__FreeBSD__) - -/* - * FreeBSD does not have special functions for dma memory, so let's keep it - * simple for now. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* for vtophys */ - -#define usb_allocmem(t,s,a,p) (*(p) = malloc(s, M_USB, M_NOWAIT), (*(p) == NULL? USBD_NOMEM: USBD_NORMAL_COMPLETION)) -#define usb_freemem(t,p) (free(*(p), M_USB)) - -#ifdef __alpha__ -#define DMAADDR(dma, offset) (alpha_XXX_dmamap((vm_offset_t) *(dma) + (offset))) +#ifdef __FreeBSD__ +#define DMAADDR(dma, o) ((dma)->block->segs[0].ds_addr + (dma)->offs + (o)) #else -#define DMAADDR(dma, offset) (vtophys(*(dma) + (offset))) -#endif -#define KERNADDR(dma, offset) ((void *) (*(dma) + (offset))) +#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/bus/usb/usb_port.h b/sys/bus/usb/usb_port.h index 05ac120415..5984e679db 100644 --- a/sys/bus/usb/usb_port.h +++ b/sys/bus/usb/usb_port.h @@ -1,6 +1,14 @@ -/* $NetBSD: usb_port.h,v 1.15 1999/11/16 12:04:28 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usb_port.h,v 1.25.2.7 2002/08/12 14:19:49 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/usb_port.h,v 1.4 2003/06/27 01:53:24 dillon Exp $ */ +/* + * $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: src/sys/dev/usb/usb_port.h,v 1.65 2003/11/09 23:54:21 joe Exp $ + * $DragonFly: src/sys/bus/usb/usb_port.h,v 1.5 2003/12/30 01:01:44 dillon Exp $ + */ + +/* 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. @@ -39,8 +47,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifndef _USB_PORT_H +#define _USB_PORT_H -/* +/* * Macro's to cope with the differences between operating systems. */ @@ -51,7 +61,15 @@ #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; @@ -59,8 +77,9 @@ typedef struct device *device_ptr_t; #define USBBASEDEVICE struct device #define USBDEV(bdev) (&(bdev)) #define USBDEVNAME(bdev) ((bdev).dv_xname) -#define USBDEVPTRNAME(bdevptr) ((bdevptr)->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; \ @@ -69,11 +88,22 @@ typedef struct device *device_ptr_t; u_int offs; \ } usb_dma_t -#define usb_timeout(f, d, t, h) timeout((f), (d), (t)) -#define usb_untimeout(f, d, h) untimeout((f), (d)) +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 *); \ @@ -82,32 +112,21 @@ int __CONCAT(dname,_activate)(struct device *, enum devact); \ \ extern struct cfdriver __CONCAT(dname,_cd); \ \ -struct cfattach __CONCAT(dname,_ca) = { \ - sizeof(struct __CONCAT(dname,_softc)), \ - __CONCAT(dname,_match), \ - __CONCAT(dname,_attach), \ - __CONCAT(dname,_detach), \ - __CONCAT(dname,_activate), \ -} +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)(parent, match, aux) \ - struct device *parent; \ - struct cfdata *match; \ - void *aux; +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_MATCH_SETUP /* nop */ - #define USB_ATTACH(dname) \ -void \ -__CONCAT(dname,_attach)(parent, self, aux) \ - struct device *parent; \ - struct device *self; \ - void *aux; +void __CONCAT(dname,_attach)(struct device *parent, struct device *self, void *aux) #define USB_ATTACH_START(dname, sc, uaa) \ struct __CONCAT(dname,_softc) *sc = \ @@ -121,10 +140,7 @@ __CONCAT(dname,_attach)(parent, self, aux) \ #define USB_ATTACH_SETUP printf("\n") #define USB_DETACH(dname) \ -int \ -__CONCAT(dname,_detach)(self, flags) \ - struct device *self; \ - int flags; +int __CONCAT(dname,_detach)(struct device *self, int flags) #define USB_DETACH_START(dname, sc) \ struct __CONCAT(dname,_softc) *sc = \ @@ -134,7 +150,7 @@ __CONCAT(dname,_detach)(self, flags) \ if (unit >= __CONCAT(dname,_cd).cd_ndevs) \ return (ENXIO); \ sc = __CONCAT(dname,_cd).cd_devs[unit]; \ - if (!sc) \ + if (sc == NULL) \ return (ENXIO) #define USB_GET_SC(dname, unit, sc) \ @@ -147,29 +163,90 @@ __CONCAT(dname,_detach)(self, flags) \ /* * OpenBSD */ -#define Static static +#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 kthread_create1 kthread_create -#define kthread_create kthread_create_deferred +#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 PWR_SUSPEND 1 -typedef struct device device_ptr_t; +#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 USBDEVPTRNAME(bdevptr) ((bdevptr)->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; \ @@ -178,8 +255,10 @@ typedef struct device device_ptr_t; u_int offs; \ } usb_dma_t -#define usb_timeout(f, d, t, h) timeout((f), (d), (t)) -#define usb_untimeout(f, d, h) untimeout((f), (d)) +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 *); \ @@ -191,7 +270,7 @@ struct cfdriver __CONCAT(dname,_cd) = { \ NULL, #dname, DV_DULL \ }; \ \ -struct cfattach __CONCAT(dname,_ca) = { \ +const struct cfattach __CONCAT(dname,_ca) = { \ sizeof(struct __CONCAT(dname,_softc)), \ __CONCAT(dname,_match), \ __CONCAT(dname,_attach), \ @@ -209,8 +288,6 @@ __CONCAT(dname,_match)(parent, match, aux) \ #define USB_MATCH_START(dname, uaa) \ struct usb_attach_arg *uaa = aux -#define USB_MATCH_SETUP /* nop */ - #define USB_ATTACH(dname) \ void \ __CONCAT(dname,_attach)(parent, self, aux) \ @@ -243,7 +320,7 @@ __CONCAT(dname,_detach)(self, flags) \ if (unit >= __CONCAT(dname,_cd).cd_ndevs) \ return (ENXIO); \ sc = __CONCAT(dname,_cd).cd_devs[unit]; \ - if (!sc) \ + if (sc == NULL) \ return (ENXIO) #define USB_GET_SC(dname, unit, sc) \ @@ -252,38 +329,75 @@ __CONCAT(dname,_detach)(self, flags) \ #define USB_DO_ATTACH(dev, bdev, parent, args, print, sub) \ (config_found_sm(parent, args, print, sub)) -#elif defined(__FreeBSD__) -/* - * FreeBSD +#elif defined(__DragonFly__) +/*************************************************************************** + * DRAGONFLY */ - #include "opt_usb.h" +#if defined(_KERNEL) +#include + +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. */ +/* #undef USB_USE_SOFTINTR */ + #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 -#define DECLARE_USB_DMA_T typedef char * usb_dma_t +#define PROC_LOCK(p) +#define PROC_UNLOCK(p) +#define uio_procp uio_td + +#define selwakeuppri(p, pri) selwakeup(p) typedef struct thread *usb_proc_ptr; /* XXX Change this when FreeBSD has memset */ -#define memcpy(d, s, l) bcopy((s),(d),(l)) -#define memset(d, v, l) bzero((d),(l)) -#define bswap32(x) swap32(x) -#define kthread_create1(function, sc, priv, string, name) -#define kthread_create(create_function, sc) -#define kthread_exit() +#define memcpy(d, s, l) bcopy((s),(d),(l)) +#define memset(d, v, l) bzero((d),(l)) +#define bswap32(x) swap32(x) + +#define config_pending_incr() +#define config_pending_decr() + +#define usb_kthread_create(f, s) \ + kthread_create(f, s, NULL, "dummy") +#define usb_kthread_create1(f, s, p, name, arg) \ + kthread_create(f, s, p, name, arg) +#define usb_kthread_create2(f, s, p, name) \ + kthread_create(f, s, p, name) + +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_timeout(f, d, t, h) ((h) = timeout((f), (d), (t))) -#define usb_untimeout(f, d, h) untimeout((f), (d), (h)) +#define ETHER_ALIGN 2 +#define BPF_MTAP(ifp, m) if ((ifp)->if_bpf) bpf_mtap((ifp), (m)); #define clalloc(p, s, x) (clist_alloc_cblocks((p), (s), (s)), 0) #define clfree(p) clist_free_cblocks((p)) @@ -291,6 +405,10 @@ typedef struct thread *usb_proc_ptr; #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); \ @@ -310,7 +428,10 @@ 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) @@ -349,7 +470,7 @@ __CONCAT(dname,_detach)(device_t self) #define USB_GET_SC_OPEN(dname, unit, sc) \ sc = devclass_get_softc(__CONCAT(dname,_devclass), unit); \ - if (!sc) \ + if (sc == NULL) \ return (ENXIO) #define USB_GET_SC(dname, unit, sc) \ @@ -359,18 +480,202 @@ __CONCAT(dname,_detach)(device_t self) (device_probe_and_attach((bdev)) == 0 ? (bdev) : 0) /* conversion from one type of queue to the other */ -/* XXX In FreeBSD SIMPLEQ_REMOVE_HEAD only removes the head element. +#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 +/* +#define logprintf(args...) log(LOG_DEBUG, args) +*/ +#define logprintf printf + +#ifdef SYSCTL_DECL +SYSCTL_DECL(_hw_usb); +#endif + +#elif defined(__FreeBSD__) +/*************************************************************************** + * FREEBSD */ -#define SIMPLEQ_REMOVE_HEAD(h, e, f) do { \ - if ( (e) != SIMPLEQ_FIRST((h)) ) \ - panic("Removing other than first element"); \ - STAILQ_REMOVE_HEAD(h, f); \ -} while (0) + +#include "opt_usb.h" + +#if defined(_KERNEL) +#include + +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 @@ -386,3 +691,6 @@ SYSCTL_DECL(_hw_usb); #endif #endif /* __FreeBSD__ */ + +#endif /* _USB_PORT_H */ + diff --git a/sys/bus/usb/usb_quirks.c b/sys/bus/usb/usb_quirks.c index 3a0035d8a9..7eceff6441 100644 --- a/sys/bus/usb/usb_quirks.c +++ b/sys/bus/usb/usb_quirks.c @@ -1,6 +1,9 @@ -/* $NetBSD: usb_quirks.c,v 1.26 2000/04/27 15:26:50 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usb_quirks.c,v 1.21.2.8 2003/02/12 14:05:57 sanpei Exp $ */ -/* $DragonFly: src/sys/bus/usb/usb_quirks.c,v 1.3 2003/08/07 21:16:47 dillon Exp $ */ +/* + * $NetBSD: usb_quirks.c,v 1.26 2000/04/27 15:26:50 augustss Exp $ + * $NetBSD: usb_quirks.c,v 1.42 2003/01/02 04:19:00 imp Exp $ + * $FreeBSD: src/sys/dev/usb/usb_quirks.c,v 1.34 2003/08/24 17:55:55 obrien Exp $ + * $DragonFly: src/sys/bus/usb/usb_quirks.c,v 1.4 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -53,14 +56,14 @@ extern int usbdebug; #define ANY 0xffff -Static struct usbd_quirk_entry { +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, + { 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 }}, @@ -76,14 +79,22 @@ Static struct usbd_quirk_entry { { 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 }}, + 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 }}, @@ -93,26 +104,30 @@ Static struct usbd_quirk_entry { 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 }}, { 0, 0, 0, { 0 } } }; -struct usbd_quirks usbd_no_quirk = { 0 }; +const struct usbd_quirks usbd_no_quirk = { 0 }; -struct usbd_quirks * +const struct usbd_quirks * usbd_find_quirk(usb_device_descriptor_t *d) { - struct usbd_quirk_entry *t; + 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 == UGETW(d->idVendor) && - t->idProduct == UGETW(d->idProduct) && - (t->bcdDevice == ANY || t->bcdDevice == UGETW(d->bcdDevice))) + 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", + 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 diff --git a/sys/bus/usb/usb_quirks.h b/sys/bus/usb/usb_quirks.h index 80b7dbda97..e213abe0c1 100644 --- a/sys/bus/usb/usb_quirks.h +++ b/sys/bus/usb/usb_quirks.h @@ -1,6 +1,8 @@ -/* $NetBSD: usb_quirks.h,v 1.11 2000/04/27 15:26:50 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usb_quirks.h,v 1.11.2.6 2002/08/24 08:00:32 nsayer Exp $ */ -/* $DragonFly: src/sys/bus/usb/usb_quirks.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: usb_quirks.h,v 1.20 2001/04/15 09:38:01 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usb_quirks.h,v 1.16 2001/07/05 10:12:59 n_hibma Exp $ + * $DragonFly: src/sys/bus/usb/usb_quirks.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -50,12 +52,13 @@ struct usbd_quirks { #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_AU_NO_FRAC 0x0400 /* audio don't adjust for fractional samples */ +#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 struct usbd_quirks usbd_no_quirk; +extern const struct usbd_quirks usbd_no_quirk; -struct usbd_quirks *usbd_find_quirk(usb_device_descriptor_t *); +const struct usbd_quirks *usbd_find_quirk(usb_device_descriptor_t *); diff --git a/sys/bus/usb/usb_subr.c b/sys/bus/usb/usb_subr.c index 029b9d33c9..fd3ae1e2a6 100644 --- a/sys/bus/usb/usb_subr.c +++ b/sys/bus/usb/usb_subr.c @@ -1,6 +1,13 @@ -/* $NetBSD: usb_subr.c,v 1.76 2000/04/27 15:26:50 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.23.2.8 2003/01/17 17:46:24 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/usb_subr.c,v 1.5 2003/08/07 21:16:47 dillon Exp $ */ +/* + * $NetBSD: usb_subr.c,v 1.99 2002/07/11 21:14:34 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.58 2003/09/01 07:47:42 ticso Exp $ + * $DragonFly: src/sys/bus/usb/usb_subr.c,v 1.6 2003/12/30 01:01:44 dillon 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 $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -76,19 +83,21 @@ extern int usbdebug; #define DPRINTFN(n,x) #endif -Static usbd_status usbd_set_config(usbd_device_handle, int); +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 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; @@ -110,7 +119,7 @@ struct usb_knowndev { #include "usbdevs_data.h" #endif /* USBVERBOSE */ -Static const char *usbd_error_strs[] = { +Static const char * const usbd_error_strs[] = { "NORMAL_COMPLETION", "IN_PROGRESS", "PENDING_REQUESTS", @@ -160,7 +169,7 @@ usbd_get_string_desc(usbd_device_handle dev, int sindex, int langid, 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); + &actlen, USBD_DEFAULT_TIMEOUT); if (err) return (err); @@ -189,7 +198,7 @@ usbd_get_string(usbd_device_handle dev, int si, char *buf) /* 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 English then */ + dev->langid = 0; /* Well, just pick something then */ } else { /* Pick the first language as the default. */ dev->langid = UGETW(us.bString[0]); @@ -207,20 +216,36 @@ usbd_get_string(usbd_device_handle dev, int si, char *buf) *s++ = c; else if ((c & 0x00ff) == 0 && swap) *s++ = c >> 8; - else + else *s++ = '?'; } *s++ = 0; return (buf); } -void -usbd_devinfo_vp(usbd_device_handle dev, char *v, char *p) +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 - struct usb_knowndev *kdp; + const struct usb_knowndev *kdp; #endif if (dev == NULL) { @@ -228,32 +253,39 @@ usbd_devinfo_vp(usbd_device_handle dev, char *v, char *p) return; } - vendor = usbd_get_string(dev, udd->iManufacturer, v); - product = usbd_get_string(dev, udd->iProduct, p); + 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); + } 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) && + if (kdp->vendor == UGETW(udd->idVendor) && (kdp->product == UGETW(udd->idProduct) || (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) break; } if (kdp->vendorname != NULL) { - if (!vendor) - vendor = kdp->vendorname; - if (!product) - product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ? + if (vendor == NULL) + vendor = kdp->vendorname; + if (product == NULL) + product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ? kdp->productname : NULL; } } #endif - if (vendor != NULL) + if (vendor != NULL && *vendor) strcpy(v, vendor); else sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor)); - if (product != NULL) + if (product != NULL && *product) strcpy(p, product); else sprintf(p, "product 0x%04x", UGETW(udd->idProduct)); @@ -273,7 +305,7 @@ usbd_devinfo(usbd_device_handle dev, int showclass, char *cp) char product[USB_MAX_STRING_LEN]; int bcdDevice, bcdUSB; - usbd_devinfo_vp(dev, vendor, product); + usbd_devinfo_vp(dev, vendor, product, 1); cp += sprintf(cp, "%s %s", vendor, product); if (showclass) cp += sprintf(cp, ", class %d/%d", @@ -312,7 +344,7 @@ 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); @@ -333,6 +365,9 @@ usbd_reset_port(usbd_device_handle dev, int port, usb_port_status_t *ps) 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); @@ -359,7 +394,7 @@ usbd_find_idesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx) 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", + "type=%d\n", ifaceidx, curidx, altidx, curaidx, d->bLength, d->bDescriptorType)); if (d->bLength == 0) /* bad descriptor */ @@ -380,7 +415,7 @@ usbd_find_idesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx) } usb_endpoint_descriptor_t * -usbd_find_edesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx, +usbd_find_edesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx, int endptidx) { char *p = (char *)cd; @@ -416,15 +451,17 @@ 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)); - ifc->device = dev; - ifc->idesc = usbd_find_idesc(dev->cdesc, ifaceidx, altidx); - if (ifc->idesc == 0) + 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; @@ -443,7 +480,6 @@ usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx) for (endpt = 0; endpt < nendpt; endpt++) { DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt)); for (; p < end; p += ed->bLength) { - ed = (usb_endpoint_descriptor_t *)p; DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p " "len=%d type=%d\n", p, end, ed->bLength, ed->bDescriptorType)); @@ -455,13 +491,35 @@ usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx) break; } /* passed end, or bad desc */ - DPRINTF(("usbd_fill_iface_data: bad descriptor(s): %s\n", - ed->bLength == 0 ? "0 length" : - ed->bDescriptorType == UDESC_INTERFACE ? "iface desc": - "out of data")); + 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; } @@ -470,8 +528,10 @@ usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx) return (USBD_NORMAL_COMPLETION); bad: - if (ifc->endpoints != NULL) + if (ifc->endpoints != NULL) { free(ifc->endpoints, M_USB); + ifc->endpoints = NULL; + } return (USBD_INVAL); } @@ -578,18 +638,43 @@ usbd_set_config_index(usbd_device_handle dev, int index, int msg) /* May be self powered. */ if (cdp->bmAttributes & UC_BUS_POWERED) { /* Must ask device. */ - 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))); + 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) attr=0x%02x, " - "selfpowered=%d, power=%d\n", - dev->address, cdp->bmAttributes, + 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. */ @@ -601,12 +686,13 @@ usbd_set_config_index(usbd_device_handle dev, int index, int msg) #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, + USBDEVNAME(dev->bus->bdev), dev->address, + cdp->bConfigurationValue, power, dev->powersrc->power); err = USBD_NO_POWER; goto bad; @@ -627,7 +713,7 @@ usbd_set_config_index(usbd_device_handle dev, int index, int msg) /* Allocate and fill interface data. */ nifc = cdp->bNumInterface; - dev->ifaces = malloc(nifc * sizeof(struct usbd_interface), + dev->ifaces = malloc(nifc * sizeof(struct usbd_interface), M_USB, M_NOWAIT); if (dev->ifaces == NULL) { err = USBD_NOMEM; @@ -673,6 +759,7 @@ usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface, p->refcnt = 1; p->intrxfer = 0; p->running = 0; + p->aborting = 0; p->repeat = 0; p->interval = ival; SIMPLEQ_INIT(&p->queue); @@ -695,6 +782,7 @@ usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface, void usbd_kill_pipe(usbd_pipe_handle pipe) { + usbd_abort_pipe(pipe); pipe->methods->close(pipe); pipe->endpoint->refcnt--; free(pipe, M_USB); @@ -724,17 +812,17 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev, 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); - device_set_ivars(bdev, &uaa); if (!bdev) { printf("%s: Device creation failed\n", USBDEVNAME(dev->bus->bdev)); return (USBD_INVAL); } + device_set_ivars(bdev, &uaa); device_quiet(bdev); #endif @@ -816,12 +904,12 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev, #if defined(__FreeBSD__) /* create another child for the next iface */ bdev = device_add_child(parent, NULL, -1); - device_set_ivars(bdev, &uaa); 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 } @@ -848,9 +936,6 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev, uaa.usegeneric = 1; uaa.configno = UHUB_UNK_CONFIGURATION; uaa.ifaceno = UHUB_UNK_INTERFACE; - uaa.vendor = UHUB_UNK_VENDOR; - uaa.product = UHUB_UNK_PRODUCT; - uaa.release = UHUB_UNK_RELEASE; 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); @@ -861,7 +946,7 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev, 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. @@ -882,27 +967,28 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev, */ usbd_status usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, - int lowspeed, int port, struct usbd_port *up) + 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 lowspeed=%d\n", - bus, port, depth, lowspeed)); + 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", + 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); + dev = malloc(sizeof *dev, M_USB, M_NOWAIT|M_ZERO); if (dev == NULL) return (USBD_NOMEM); - memset(dev, 0, sizeof(*dev)); dev->bus = bus; @@ -920,9 +1006,15 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, dev->quirks = &usbd_no_quirk; dev->address = USB_START_ADDR; dev->ddesc.bMaxPacketSize = 0; - dev->lowspeed = lowspeed != 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; @@ -937,12 +1029,14 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, up->device = dev; dd = &dev->ddesc; /* Try a few times in case the device is slow (i.e. outside specs.) */ - for (i = 0; i < 3; i++) { + for (i = 0; i < 15; i++) { /* Get the first 8 bytes of the device descriptor. */ err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd); if (!err) break; usbd_delay_ms(dev, 200); + if ((i & 3) == 3) + usbd_reset_port(up->parent, port, &ps); } if (err) { DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc " @@ -951,11 +1045,22 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, 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, ls=%d\n", + "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->lowspeed)); + dd->bDeviceProtocol, dd->bMaxPacketSize, dd->bLength, + dev->speed)); if (dd->bDescriptorType != UDESC_DEVICE) { /* Illegal device descriptor */ @@ -1000,7 +1105,7 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, dev->power = USB_MIN_POWER; dev->self_powered = 0; - DPRINTF(("usbd_new_device: new dev (addr %d), dev=%p, parent=%p\n", + 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); @@ -1008,8 +1113,9 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, usbd_remove_device(dev, up); return (err); } - - usbd_add_event(USB_EVENT_DEVICE_ATTACH, dev); + + usbd_add_dev_event(USB_EVENT_DEVICE_ATTACH, dev); + return (USBD_NORMAL_COMPLETION); } @@ -1039,7 +1145,7 @@ 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; @@ -1069,7 +1175,7 @@ usbd_print(void *aux, const char *pnp) 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. @@ -1106,54 +1212,54 @@ usbd_submatch(struct device *parent, void *match, void *aux) 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) - ) + 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) +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; - - if (dev->subdevs) { - 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'; /* terminate */ - } - } else { - i = 0; - } - for (/*i is set */; i < USB_MAX_DEVNAMES; i++) - di->udi_devnames[i][0] = 0; /* empty */ - - usbd_devinfo_vp(dev, di->udi_vendor, di->udi_product); + 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); @@ -1163,10 +1269,23 @@ usbd_fill_deviceinfo(usbd_device_handle dev, struct usb_device_info *di) di->udi_protocol = dev->ddesc.bDeviceProtocol; di->udi_config = dev->config; di->udi_power = dev->self_powered ? 0 : dev->power; - di->udi_lowspeed = dev->lowspeed; + 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; + for (i = 0; i < sizeof(di->udi_ports) / sizeof(di->udi_ports[0]) && i < dev->hub->hubdesc.bNbrPorts; i++) { @@ -1235,7 +1354,7 @@ usb_disconnect_port(struct usbd_port *up, device_ptr_t parent) const char *hubname = USBDEVPTRNAME(parent); int i; - DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", + DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", up, dev, up->portno)); #ifdef DIAGNOSTIC @@ -1245,34 +1364,19 @@ usb_disconnect_port(struct usbd_port *up, device_ptr_t parent) } #endif - if (dev->cdesc == NULL) { - /* Partially attached device, just drop it. */ - dev->bus->devices[dev->address] = 0; - up->device = 0; - return; - } - - usbd_add_event(USB_EVENT_DEVICE_DETACH, dev); - 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]), + 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); -#if defined(__NetBSD__) || defined(__OpenBSD__) config_detach(dev->subdevs[i], DETACH_FORCE); -#elif defined(__FreeBSD__) - device_delete_child(device_get_parent(dev->subdevs[i]), - dev->subdevs[i]); -#endif - } } - /*usbd_add_event(USB_EVENT_DEVICE_DETACH, dev);*/ + /*usbd_add_dev_event(USB_EVENT_DEVICE_DETACH, dev);*/ dev->bus->devices[dev->address] = NULL; up->device = NULL; usb_free_device(dev); diff --git a/sys/bus/usb/usbcdc.h b/sys/bus/usb/usbcdc.h index 7a364a4c11..08302b2895 100644 --- a/sys/bus/usb/usbcdc.h +++ b/sys/bus/usb/usbcdc.h @@ -1,6 +1,8 @@ -/* $NetBSD: usbcdc.h,v 1.3 1999/01/03 01:09:18 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usbcdc.h,v 1.7.2.2 2000/10/31 23:01:16 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/usbcdc.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: usbcdc.h,v 1.6 2000/04/27 15:26:50 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usbcdc.h,v 1.10 2003/01/09 04:24:28 imp Exp $ + * $DragonFly: src/sys/bus/usb/usbcdc.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -147,6 +149,18 @@ typedef struct { } 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 diff --git a/sys/bus/usb/usbdevs b/sys/bus/usb/usbdevs index 4af2b9c7a3..2585c401ab 100644 --- a/sys/bus/usb/usbdevs +++ b/sys/bus/usb/usbdevs @@ -35,8 +35,8 @@ * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/dev/usb/usbdevs,v 1.11.2.50 2003/12/22 07:52:56 sanpei Exp $ - * $DragonFly: src/sys/bus/usb/Attic/usbdevs,v 1.4 2003/12/29 06:42:12 dillon Exp $ - */ + * $DragonFly: src/sys/bus/usb/Attic/usbdevs,v 1.5 2003/12/30 01:01:44 dillon Exp $ +$FreeBSD: src/sys/dev/usb/usbdevs,v 1.149 2003/12/19 12:19:12 sanpei Exp $ /* * List of known USB vendors @@ -989,7 +989,6 @@ product PALM TUNGSTEN_T 0x0060 Palm Tungsten T 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 diff --git a/sys/bus/usb/usbdevs.h b/sys/bus/usb/usbdevs.h index c0109a45a4..5756137426 100644 --- a/sys/bus/usb/usbdevs.h +++ b/sys/bus/usb/usbdevs.h @@ -38,8 +38,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/usbdevs,v 1.11.2.50 2003/12/22 07:52:56 sanpei Exp $ - * $DragonFly: src/sys/bus/usb/Attic/usbdevs.h,v 1.4 2003/12/29 06:42:12 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/usbdevs.h,v 1.157 2003/12/19 12:21:11 sanpei Exp $ + * $DragonFly: src/sys/bus/usb/Attic/usbdevs.h,v 1.5 2003/12/30 01:01:44 dillon Exp $ */ /* @@ -993,7 +993,6 @@ #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 */ diff --git a/sys/bus/usb/usbdevs_data.h b/sys/bus/usb/usbdevs_data.h index 634a85e02c..17b6c283c8 100644 --- a/sys/bus/usb/usbdevs_data.h +++ b/sys/bus/usb/usbdevs_data.h @@ -38,8 +38,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/usbdevs,v 1.11.2.50 2003/12/22 07:52:56 sanpei Exp $ - * $DragonFly: src/sys/bus/usb/Attic/usbdevs_data.h,v 1.4 2003/12/29 06:42:12 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/usbdevs,v 1.149 2003/12/19 12:19:12 sanpei Exp $ + * $DragonFly: src/sys/bus/usb/Attic/usbdevs_data.h,v 1.5 2003/12/30 01:01:44 dillon Exp $ */ /* @@ -78,7 +78,7 @@ * make the device recognised by the appropriate device driver. */ -struct usb_knowndev usb_knowndevs[] = { +const struct usb_knowndev usb_knowndevs[] = { { USB_VENDOR_3COM, USB_PRODUCT_3COM_HOMECONN, 0, @@ -2299,12 +2299,6 @@ struct usb_knowndev usb_knowndevs[] = { "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, diff --git a/sys/bus/usb/usbdi.c b/sys/bus/usb/usbdi.c index 74b7acde84..6f9d59b027 100644 --- a/sys/bus/usb/usbdi.c +++ b/sys/bus/usb/usbdi.c @@ -1,6 +1,8 @@ -/* $NetBSD: usbdi.c,v 1.60 2000/01/19 00:23:58 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usbdi.c,v 1.34.2.7 2002/11/06 14:03:37 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/usbdi.c,v 1.5 2003/08/07 21:16:47 dillon Exp $ */ +/* + * $NetBSD: usbdi.c,v 1.103 2002/09/27 15:37:38 provos Exp $ + * $FreeBSD: src/sys/dev/usb/usbdi.c,v 1.84 2003/11/09 23:56:19 joe Exp $ + * $DragonFly: src/sys/bus/usb/usbdi.c,v 1.6 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -78,11 +80,12 @@ extern int usbdebug; #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_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); + (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; @@ -98,10 +101,8 @@ usbd_finish(void) --usbd_nbuses; } -Static __inline int usbd_xfer_isread(usbd_xfer_handle xfer); -Static __inline int -usbd_xfer_isread(xfer) - usbd_xfer_handle xfer; +static __inline int +usbd_xfer_isread(usbd_xfer_handle xfer) { if (xfer->rqflags & URQ_REQUEST) return (xfer->request.bmRequestType & UT_READ); @@ -112,7 +113,40 @@ usbd_xfer_isread(xfer) #ifdef USB_DEBUG void -usbd_dump_queue(usbd_pipe_handle); +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) @@ -120,26 +154,39 @@ usbd_dump_queue(usbd_pipe_handle pipe) usbd_xfer_handle xfer; printf("usbd_dump_queue: pipe=%p\n", pipe); - for (xfer = SIMPLEQ_FIRST(&pipe->queue); - xfer; - xfer = SIMPLEQ_NEXT(xfer, next)) { + 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_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, +{ + return (usbd_open_pipe_ival(iface, address, flags, pipe, USBD_DEFAULT_INTERVAL)); } -usbd_status +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; @@ -167,7 +214,7 @@ usbd_open_pipe_ival(usbd_interface_handle iface, u_int8_t address, return (USBD_NORMAL_COMPLETION); } -usbd_status +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, @@ -180,7 +227,7 @@ usbd_open_pipe_intr(usbd_interface_handle iface, u_int8_t address, 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, + err = usbd_open_pipe_ival(iface, address, USBD_EXCLUSIVE_USE, &ipipe, ival); if (err) return (err); @@ -220,7 +267,7 @@ usbd_close_pipe(usbd_pipe_handle pipe) if (--pipe->refcnt != 0) return (USBD_NORMAL_COMPLETION); - if (SIMPLEQ_FIRST(&pipe->queue) != 0) + if (! SIMPLEQ_EMPTY(&pipe->queue)) return (USBD_PENDING_REQUESTS); LIST_REMOVE(pipe, next); pipe->endpoint->refcnt--; @@ -248,6 +295,9 @@ usbd_transfer(usbd_xfer_handle xfer) #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) { @@ -264,7 +314,7 @@ usbd_transfer(usbd_xfer_handle xfer) } /* Copy data if going out. */ - if (!(xfer->flags & USBD_NO_COPY) && size != 0 && + if (!(xfer->flags & USBD_NO_COPY) && size != 0 && !usbd_xfer_isread(xfer)) memcpy(KERNADDR(dmap, 0), xfer->buffer, size); @@ -290,22 +340,7 @@ usbd_transfer(usbd_xfer_handle xfer) if (!xfer->done) { if (pipe->device->bus->use_polling) panic("usbd_transfer: not done\n"); - /* XXX Temporary hack XXX */ - if (xfer->flags & USBD_NO_TSLEEP) { - int i; - usbd_bus_handle bus = pipe->device->bus; - int to = xfer->timeout * 1000; - for (i = 0; i < to; i += 10) { - delay(10); - bus->methods->do_poll(bus); - if (xfer->done) - break; - } - if (!xfer->done) - pipe->methods->abort(xfer); - } else - /* XXX End hack XXX */ - tsleep(xfer, 0, "usbsyn", 0); + tsleep(xfer, 0, "usbsyn", 0); } splx(s); return (xfer->status); @@ -325,9 +360,13 @@ 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 (0); + return (NULL); xfer->rqflags |= URQ_DEV_DMABUF; return (KERNADDR(&xfer->dmabuf, 0)); } @@ -353,7 +392,7 @@ usbd_get_buffer(usbd_xfer_handle xfer) return (KERNADDR(&xfer->dmabuf, 0)); } -usbd_xfer_handle +usbd_xfer_handle usbd_alloc_xfer(usbd_device_handle dev) { usbd_xfer_handle xfer; @@ -362,16 +401,23 @@ usbd_alloc_xfer(usbd_device_handle dev) 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_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); } @@ -487,7 +533,7 @@ usbd_interface2endpoint_descriptor(usbd_interface_handle iface, u_int8_t index) return (iface->endpoints[index].edesc); } -usbd_status +usbd_status usbd_abort_pipe(usbd_pipe_handle pipe) { usbd_status err; @@ -504,8 +550,8 @@ usbd_abort_pipe(usbd_pipe_handle pipe) splx(s); return (err); } - -usbd_status + +usbd_status usbd_clear_endpoint_stall(usbd_pipe_handle pipe) { usbd_device_handle dev = pipe->device; @@ -514,8 +560,8 @@ usbd_clear_endpoint_stall(usbd_pipe_handle pipe) DPRINTFN(8, ("usbd_clear_endpoint_stall\n")); - /* - * Clearing en endpoint stall resets the enpoint toggle, so + /* + * Clearing en endpoint stall resets the endpoint toggle, so * do the same to the HC toggle. */ pipe->methods->cleartoggle(pipe); @@ -536,7 +582,7 @@ XXX should we do this? return (err); } -usbd_status +usbd_status usbd_clear_endpoint_stall_async(usbd_pipe_handle pipe) { usbd_device_handle dev = pipe->device; @@ -560,15 +606,20 @@ usbd_clear_endpoint_toggle(usbd_pipe_handle pipe) pipe->methods->cleartoggle(pipe); } - -usbd_status +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_status usbd_interface_count(usbd_device_handle dev, u_int8_t *count) { if (dev->cdesc == NULL) @@ -577,15 +628,14 @@ usbd_interface_count(usbd_device_handle dev, u_int8_t *count) return (USBD_NORMAL_COMPLETION); } -usbd_status +void usbd_interface2device_handle(usbd_interface_handle iface, usbd_device_handle *dev) { *dev = iface->device; - return (USBD_NORMAL_COMPLETION); } -usbd_status +usbd_status usbd_device2interface_handle(usbd_device_handle dev, u_int8_t ifaceno, usbd_interface_handle *iface) { @@ -609,19 +659,27 @@ 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); - if (iface->endpoints) - free(iface->endpoints, M_USB); - iface->endpoints = 0; - iface->idesc = 0; - + 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); @@ -640,7 +698,7 @@ usbd_get_no_alts(usb_config_descriptor_t *cdesc, int ifaceno) for (n = 0; p < end; p += d->bLength) { d = (usb_interface_descriptor_t *)p; - if (p + d->bLength <= end && + if (p + d->bLength <= end && d->bDescriptorType == UDESC_INTERFACE && d->bInterfaceNumber == ifaceno) n++; @@ -683,13 +741,15 @@ usbd_ar_pipe(usbd_pipe_handle pipe) 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", + 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); } @@ -706,6 +766,13 @@ usb_transfer_complete(usbd_xfer_handle xfer) 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) { @@ -739,18 +806,18 @@ usb_transfer_complete(usbd_xfer_handle xfer) } } - if (pipe->methods->done != NULL) - pipe->methods->done(xfer); - 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, xfer, next); + 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 @@ -767,14 +834,23 @@ usb_transfer_complete(usbd_xfer_handle 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 */ + if ((xfer->status == USBD_CANCELLED || + xfer->status == USBD_TIMEOUT) && + pipe->iface != NULL) /* not control pipe */ pipe->running = 0; else usbd_start_next(pipe); @@ -788,8 +864,16 @@ usb_insert_transfer(usbd_xfer_handle xfer) usbd_status err; int s; - DPRINTFN(5,("usb_insert_transfer: pipe=%p running=%d timeout=%d\n", + 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) @@ -837,22 +921,33 @@ usbd_start_next(usbd_pipe_handle pipe) } } + 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)); + 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(usbd_device_handle dev, - usb_device_request_t *req, void *data, u_int16_t flags, int *actlen) +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(__FreeBSD__) - KASSERT(mycpu->gd_intr_nesting_level == 0, +#if defined(__i386__) && defined(__FreeBSD__) + KASSERT(curthread->td_intr_nesting_level == 0, ("usbd_do_request: in interrupt context")); #endif if (dev->bus->intr_context) { @@ -864,8 +959,9 @@ usbd_do_request_flags(usbd_device_handle dev, 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), flags, 0); + 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) @@ -873,14 +969,14 @@ usbd_do_request_flags(usbd_device_handle dev, "%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), + 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. @@ -930,11 +1026,11 @@ usbd_do_request_async_cb(usbd_xfer_handle xfer, usbd_private_handle priv, 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->pipe->device->address, xfer->request.bmRequestType, xfer->request.bRequest, UGETW(xfer->request.wValue), - UGETW(xfer->request.wIndex), - UGETW(xfer->request.wLength), + UGETW(xfer->request.wIndex), + UGETW(xfer->request.wLength), xfer->length, xfer->actlen)); #endif usbd_free_xfer(xfer); @@ -964,9 +1060,15 @@ usbd_do_request_async(usbd_device_handle dev, usb_device_request_t *req, return (USBD_NORMAL_COMPLETION); } -struct usbd_quirks * +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); } @@ -982,12 +1084,15 @@ usbd_dopoll(usbd_interface_handle iface) } void -usbd_set_polling(usbd_interface_handle iface, int on) +usbd_set_polling(usbd_device_handle dev, int on) { if (on) - iface->device->bus->use_polling++; + dev->bus->use_polling++; else - iface->device->bus->use_polling--; + 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); } @@ -1005,6 +1110,23 @@ usbd_get_endpoint_descriptor(usbd_interface_handle iface, u_int8_t address) 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 0 + static struct timeval errinterval = { 0, 250000 }; /* 0.25 s*/ + + return (ratecheck(last, &errinterval)); +#endif + return (1); +} + /* * Search for a vendor/product pair in an array. The item size is * given as an argument. @@ -1014,7 +1136,9 @@ 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) { - if (tbl->ud_vendor == vendor && tbl->ud_product == product) + 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); } @@ -1026,7 +1150,7 @@ 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); } diff --git a/sys/bus/usb/usbdi.h b/sys/bus/usb/usbdi.h index d6c0e119eb..9e5a03a98e 100644 --- a/sys/bus/usb/usbdi.h +++ b/sys/bus/usb/usbdi.h @@ -1,6 +1,8 @@ -/* $NetBSD: usbdi.h,v 1.39 2000/01/19 00:23:59 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usbdi.h,v 1.21.2.3 2002/02/14 02:30:47 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/usbdi.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: usbdi.h,v 1.62 2002/07/11 21:14:35 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usbdi.h,v 1.48 2003/07/14 20:31:03 joe Exp $ + * $DragonFly: src/sys/bus/usb/usbdi.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -46,33 +48,33 @@ 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 */ +typedef enum { /* keep in sync with usbd_status_msgs */ USBD_NORMAL_COMPLETION = 0, /* must be 0 */ - USBD_IN_PROGRESS, + USBD_IN_PROGRESS, /* 1 */ /* errors */ - USBD_PENDING_REQUESTS, - USBD_NOT_STARTED, - USBD_INVAL, - USBD_NOMEM, - USBD_CANCELLED, - USBD_BAD_ADDRESS, - USBD_IN_USE, - USBD_NO_ADDR, - USBD_SET_ADDR_FAILED, - USBD_NO_POWER, - USBD_TOO_DEEP, - USBD_IOERROR, - USBD_NOT_CONFIGURED, - USBD_TIMEOUT, - USBD_SHORT_XFER, - USBD_STALLED, - USBD_INTERRUPTED, + 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_ERROR_MAX /* must be last */ } usbd_status; typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle, - usbd_status); + usbd_status); /* Open flags */ #define USBD_EXCLUSIVE_USE 0x01 @@ -86,9 +88,6 @@ typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle, /* in usb.h #define USBD_SHORT_XFER_OK 0x04*/ /* allow short reads */ #define USBD_FORCE_SHORT_XFER 0x08 /* force last short packet on write */ -/* XXX Temporary hack XXX */ -#define USBD_NO_TSLEEP 0x80 /* XXX use busy wait */ - #define USBD_NO_TIMEOUT 0 #define USBD_DEFAULT_TIMEOUT 5000 /* ms = 5 s */ @@ -96,96 +95,108 @@ typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle, #define USB_CDEV_MAJOR 108 #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); +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_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); -usbd_status 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_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 req, u_int32_t size); -void usbd_free_buffer(usbd_xfer_handle req); +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 *); +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_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 dev, struct usb_device_info *di); +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); +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_interface_handle iface, int on); +void usbd_set_polling(usbd_device_handle dev, int on); const char *usbd_errstr(usbd_status err); -void usbd_add_event(int, usbd_device_handle); +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 *); -struct usbd_quirks *usbd_get_quirks(usbd_device_handle); +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_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; @@ -194,6 +205,7 @@ 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 */ @@ -205,6 +217,7 @@ struct usb_attach_arg { int vendor; int product; int release; + int matchlvl; usbd_device_handle device; /* current device */ usbd_interface_handle iface; /* current interface */ int usegeneric; @@ -258,13 +271,15 @@ struct usb_attach_arg { int usbd_driver_load(module_t mod, int what, void *arg); #endif -/* - * XXX - * splusb MUST be the lowest level interrupt so that within USB callbacks - * the level can be raised the appropriate level. - * XXX Should probably use a softsplusb. - */ -/* XXX */ +/* 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 -/* XXX */ diff --git a/sys/bus/usb/usbdi_util.c b/sys/bus/usb/usbdi_util.c index 9d3b870774..cd97fe68b9 100644 --- a/sys/bus/usb/usbdi_util.c +++ b/sys/bus/usb/usbdi_util.c @@ -1,6 +1,9 @@ -/* $NetBSD: usbdi_util.c,v 1.24 1999/11/17 23:00:50 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usbdi_util.c,v 1.15.2.5 2002/11/06 14:03:37 joe Exp $ */ -/* $DragonFly: src/sys/bus/usb/usbdi_util.c,v 1.4 2003/08/07 21:16:47 dillon Exp $ */ +/* + * $NetBSD: usbdi_util.c,v 1.24 1999/11/17 23:00:50 augustss Exp $ + * $NetBSD: usbdi_util.c,v 1.36 2001/11/13 06:24:57 lukem Exp $ + * $FreeBSD: src/sys/dev/usb/usbdi_util.c,v 1.31 2003/08/24 17:55:55 obrien Exp $ + * $DragonFly: src/sys/bus/usb/usbdi_util.c,v 1.5 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -70,6 +73,9 @@ 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); @@ -79,19 +85,20 @@ usbd_get_desc(usbd_device_handle dev, int type, int index, int len, void *desc) } usbd_status -usbd_get_config_desc(usbd_device_handle dev, int conf, +usbd_get_config_desc(usbd_device_handle dev, int confidx, usb_config_descriptor_t *d) { usbd_status err; - DPRINTFN(3,("usbd_get_config_desc: conf=%d\n", conf)); - err = usbd_get_desc(dev, UDESC_CONFIG, conf, - USB_CONFIG_DESCRIPTOR_SIZE, d); + 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: conf %d, bad desc %d\n", - conf, d->bDescriptorType)); + 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); @@ -108,7 +115,7 @@ 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, + return (usbd_get_desc(dev, UDESC_DEVICE, 0, USB_DEVICE_DESCRIPTOR_SIZE, d)); } @@ -123,7 +130,7 @@ usbd_get_device_status(usbd_device_handle dev, usb_status_t *st) 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) @@ -136,7 +143,7 @@ usbd_get_hub_status(usbd_device_handle dev, usb_hub_status_t *st) 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) @@ -223,15 +230,12 @@ 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; - usbd_status err; DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n", iface, report, id->bInterfaceNumber)); if (id == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_PROTOCOL; USETW(req.wValue, report); @@ -247,14 +251,11 @@ usbd_set_report(usbd_interface_handle iface, int type, int id, void *data, usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); usbd_device_handle dev; usb_device_request_t req; - usbd_status err; DPRINTFN(4, ("usbd_set_report: len=%d\n", len)); if (ifd == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, type, id); @@ -270,14 +271,11 @@ usbd_set_report_async(usbd_interface_handle iface, int type, int id, void *data, usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); usbd_device_handle dev; usb_device_request_t req; - usbd_status err; DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len)); if (ifd == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, type, id); @@ -293,14 +291,11 @@ usbd_get_report(usbd_interface_handle iface, int type, int id, void *data, usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); usbd_device_handle dev; usb_device_request_t req; - usbd_status err; DPRINTFN(4, ("usbd_set_report: len=%d\n", len)); - if (id == NULL) + if (id == 0) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_REPORT; USETW2(req.wValue, type, id); @@ -315,14 +310,11 @@ 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; - usbd_status err; DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id)); if (ifd == NULL) return (USBD_IOERROR); - err = usbd_interface2device_handle(iface, &dev); - if (err) - return (err); + usbd_interface2device_handle(iface, &dev); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_IDLE; USETW2(req.wValue, duration, id); @@ -332,14 +324,14 @@ usbd_set_idle(usbd_interface_handle iface, int duration, int id) } usbd_status -usbd_get_report_descriptor(usbd_device_handle dev, int ifcno, int repid, +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, repid); + 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)); @@ -353,13 +345,10 @@ usbd_get_hid_descriptor(usbd_interface_handle ifc) usb_config_descriptor_t *cdesc; usb_hid_descriptor_t *hd; char *p, *end; - usbd_status err; if (idesc == NULL) return (0); - err = usbd_interface2device_handle(ifc, &dev); - if (err) - return (0); + usbd_interface2device_handle(ifc, &dev); cdesc = usbd_get_config_descriptor(dev); p = (char *)idesc + idesc->bLength; @@ -376,17 +365,15 @@ usbd_get_hid_descriptor(usbd_interface_handle ifc) } usbd_status -usbd_alloc_report_desc(usbd_interface_handle ifc, void **descp, int *sizep, - struct malloc_type *mem) +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; - err = usbd_interface2device_handle(ifc, &dev); - if (err) - return (err); + usbd_interface2device_handle(ifc, &dev); id = usbd_get_interface_descriptor(ifc); if (id == NULL) return (USBD_INVAL); @@ -397,9 +384,8 @@ usbd_alloc_report_desc(usbd_interface_handle ifc, void **descp, int *sizep, *descp = malloc(*sizep, mem, M_NOWAIT); if (*descp == NULL) return (USBD_NOMEM); - /* XXX should not use 0 Report ID */ - err = usbd_get_report_descriptor(dev, id->bInterfaceNumber, 0, - *sizep, *descp); + err = usbd_get_report_descriptor(dev, id->bInterfaceNumber, + *sizep, *descp); if (err) { free(*descp, mem); *descp = NULL; @@ -408,7 +394,7 @@ usbd_alloc_report_desc(usbd_interface_handle ifc, void **descp, int *sizep, return (USBD_NORMAL_COMPLETION); } -usbd_status +usbd_status usbd_get_config(usbd_device_handle dev, u_int8_t *conf) { usb_device_request_t req; @@ -421,9 +407,8 @@ usbd_get_config(usbd_device_handle dev, u_int8_t *conf) 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); Static void usbd_bulk_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) @@ -455,7 +440,7 @@ usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe, usbd_abort_pipe(pipe); return (USBD_INTERRUPTED); } - usbd_get_xfer_status(xfer, 0, 0, size, &err); + 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)); @@ -464,6 +449,48 @@ usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle 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, 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) { @@ -472,11 +499,11 @@ usb_detach_wait(device_ptr_t dv) 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/bus/usb/usbdi_util.h b/sys/bus/usb/usbdi_util.h index 4b37e56426..d08b62cd57 100644 --- a/sys/bus/usb/usbdi_util.h +++ b/sys/bus/usb/usbdi_util.h @@ -1,6 +1,8 @@ -/* $NetBSD: usbdi_util.h,v 1.17 1999/09/05 19:32:19 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usbdi_util.h,v 1.9.2.2 2000/10/31 23:23:30 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/usbdi_util.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: usbdi_util.h,v 1.23 2001/10/26 17:58:22 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usbdi_util.h,v 1.16 2003/07/04 01:50:39 jmg Exp $ + * $DragonFly: src/sys/bus/usb/usbdi_util.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -39,60 +41,51 @@ * 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_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); + 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_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 dev, - usb_hub_status_t *st); +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 repid, 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); -#if defined(__NetBSD__) || defined(__OpenBSD__) -usbd_status usbd_alloc_report_desc - (usbd_interface_handle ifc, void **descp, int *sizep, int mem); -#elif defined(__FreeBSD__) -usbd_status usbd_alloc_report_desc - (usbd_interface_handle ifc, void **descp, int *sizep, struct malloc_type * mem); -#endif -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_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_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_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/bus/usb/usbdivar.h b/sys/bus/usb/usbdivar.h index 4206f18903..a6e835f3bd 100644 --- a/sys/bus/usb/usbdivar.h +++ b/sys/bus/usb/usbdivar.h @@ -1,6 +1,8 @@ -/* $NetBSD: usbdivar.h,v 1.46 2000/01/19 01:16:40 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.14.2.2 2000/10/31 23:23:30 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/usbdivar.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: usbdivar.h,v 1.70 2002/07/11 21:14:36 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.40 2003/07/15 22:42:37 jmg Exp $ + * $DragonFly: src/sys/bus/usb/usbdivar.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -39,6 +41,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#if defined(__NetBSD__) +#include +#endif + /* From usb_mem.h */ DECLARE_USB_DMA_T; @@ -52,13 +58,13 @@ struct usbd_endpoint { 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); + 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 *); + void (*freex)(struct usbd_bus *, struct usbd_xfer *); }; struct usbd_pipe_methods { @@ -76,7 +82,7 @@ struct usbd_port { u_int8_t portno; u_int8_t restartcnt; #define USBD_RESTART_MAX 5 - struct usbd_device *device; + struct usbd_device *device; /* Connected device */ struct usbd_device *parent; /* The ports hub */ }; @@ -110,11 +116,18 @@ struct usbd_bus { #define USBREV_PRE_1_0 1 #define USBREV_1_0 2 #define USBREV_1_1 3 -#define USBREV_STR { "unknown", "pre 1.0", "1.0", "1.1" } +#define USBREV_2_0 4 +#define USBREV_STR { "unknown", "pre 1.0", "1.0", "1.1", "2.0" } -#if defined(__NetBSD__) || defined(__OpenBSD__) - bus_dma_tag_t dmatag; /* DMA tag */ +#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 { @@ -123,19 +136,21 @@ struct usbd_device { u_int8_t address; /* device addess */ u_int8_t config; /* current configuration # */ u_int8_t depth; /* distance from root hub */ - u_int8_t lowspeed; /* lowspeed flag */ + 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 */ - struct usbd_quirks *quirks; /* device quirks, always set */ + 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 */ }; @@ -156,6 +171,7 @@ struct usbd_pipe { struct usbd_endpoint *endpoint; int refcnt; char running; + char aborting; SIMPLEQ_HEAD(, usbd_xfer) queue; LIST_ENTRY(usbd_pipe) next; @@ -178,6 +194,12 @@ struct usbd_xfer { 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; @@ -198,35 +220,37 @@ struct usbd_xfer { SIMPLEQ_ENTRY(usbd_xfer) next; void *hcpriv; /* private use by the HC driver */ - int hcprivint; -#if defined(__FreeBSD__) - struct callout_handle timo_handle; -#endif + 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); -void usbd_devinfo_vp(usbd_device_handle, char *, char *); usbd_status usbd_reset_port(usbd_device_handle dev, - int port, usb_port_status_t *ps); + 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 *); + 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); +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); @@ -234,10 +258,13 @@ void usb_transfer_complete(usbd_xfer_handle xfer); void usb_disconnect_port(struct usbd_port *up, device_ptr_t); /* Routines from usb.c */ -int usb_bus_count(void); -void usb_needs_explore(usbd_bus_handle); +void usb_needs_explore(usbd_device_handle); +void usb_schedsoftintr(struct usbd_bus *); -#ifdef DIAGNOSTIC +/* + * 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", \ diff --git a/sys/bus/usb/usbhid.h b/sys/bus/usb/usbhid.h index 7e01bbfb4d..22c8485aed 100644 --- a/sys/bus/usb/usbhid.h +++ b/sys/bus/usb/usbhid.h @@ -1,6 +1,8 @@ -/* $NetBSD: usbhid.h,v 1.9 2000/09/03 19:09:14 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/usbhid.h,v 1.7.2.2 2000/10/31 23:03:00 n_hibma Exp $ */ -/* $DragonFly: src/sys/bus/usb/usbhid.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: usbhid.h,v 1.9 2000/09/03 19:09:14 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/usbhid.h,v 1.13 2002/01/02 20:16:53 joe Exp $ + * $DragonFly: src/sys/bus/usb/usbhid.h,v 1.3 2003/12/30 01:01:44 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -55,10 +57,6 @@ #define UR_GET_PROTOCOL 0x03 #define UR_SET_PROTOCOL 0x0b -#if defined(__FreeBSD__) -#define UPACKED __attribute__ ((packed)) -#endif - typedef struct usb_hid_descriptor { uByte bLength; uByte bDescriptorType; diff --git a/sys/conf/files b/sys/conf/files index 399c7cdca2..b1fcdb98f1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.36 2003/12/10 23:48:07 hsu Exp $ +# $DragonFly: src/sys/conf/files,v 1.37 2003/12/30 01:01:38 dillon Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -637,6 +637,7 @@ kern/kern_switch.c standard kern/lwkt_thread.c standard kern/lwkt_msgport.c standard kern/lwkt_rwlock.c standard +#kern/lwkt_caps.c standard kern/kern_synch.c standard kern/kern_syscalls.c standard kern/kern_sysctl.c standard @@ -1162,11 +1163,13 @@ vm/vm_zone.c standard dev/misc/streams/streams.c optional streams # # USB support -bus/pci/uhci_pci.c optional uhci -bus/pci/ohci_pci.c optional ohci bus/usb/usb_if.m optional usb bus/usb/uhci.c optional uhci +bus/usb/uhci_pci.c optional uhci bus/usb/ohci.c optional ohci +bus/usb/ohci_pci.c optional ohci +bus/usb/ehci.c optional ehci +bus/usb/ehci_pci.c optional ehci dev/usbmisc/ucom/ucom.c optional ucom dev/usbmisc/uvisor/uvisor.c optional uvisor ucom dev/usbmisc/uvscom/uvscom.c optional uvscom ucom @@ -1175,6 +1178,7 @@ bus/usb/usbdi.c optional usb bus/usb/usbdi_util.c optional usb #bus/usb/usb_mem.c optional usb bus/usb/usb_ethersubr.c optional usb +bus/usb/usb_mem.c optional usb bus/usb/usb_subr.c optional usb bus/usb/usb_quirks.c optional usb bus/usb/hid.c optional usb @@ -1193,8 +1197,10 @@ dev/usbmisc/umass/umass.c optional umass dev/usbmisc/umodem/umodem.c optional umodem dev/usbmisc/uscanner/uscanner.c optional uscanner dev/netif/aue/if_aue.c optional aue +dev/netif/axe/if_axe.c optional axe dev/netif/cue/if_cue.c optional cue dev/netif/kue/if_kue.c optional kue +dev/netif/rue/if_rue.c optional rue bus/isa/isa_if.m optional isa bus/isa/isa_common.c optional isa bus/isa/isahint.c optional isa diff --git a/sys/dev/netif/aue/if_aue.c b/sys/dev/netif/aue/if_aue.c index 4df0e9e072..db1e39aa26 100644 --- a/sys/dev/netif/aue/if_aue.c +++ b/sys/dev/netif/aue/if_aue.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * @@ -29,15 +29,15 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/if_aue.c,v 1.19.2.18 2003/06/14 15:56:48 trhodes Exp $ - * $DragonFly: src/sys/dev/netif/aue/if_aue.c,v 1.5 2003/11/20 22:07:26 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/if_aue.c,v 1.78 2003/12/17 14:23:07 sanpei Exp $ + * $DragonFly: src/sys/dev/netif/aue/if_aue.c,v 1.6 2003/12/30 01:01:45 dillon Exp $ * * $FreeBSD: src/sys/dev/usb/if_aue.c,v 1.19.2.18 2003/06/14 15:56:48 trhodes Exp $ */ /* - * ADMtek AN986 Pegasus USB to ethernet driver. Datasheet is available - * from http://www.admtek.com.tw. + * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver. + * Datasheet is available from http://www.admtek.com.tw. * * Written by Bill Paul * Electrical Engineering Department @@ -80,8 +80,11 @@ #include -#include /* for DELAY */ #include +#include +#if __FreeBSD_version < 500000 +#include +#endif #include #include @@ -95,75 +98,88 @@ #include "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. - */ -Static struct aue_type aue_devs[] = { - { 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_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_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_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 }, - { 0, 0, 0 } +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_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 struct usb_qdat aue_qdat; -Static int aue_match(device_t); -Static int aue_attach(device_t); -Static int aue_detach(device_t); +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 *); +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 *); @@ -180,24 +196,24 @@ 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_t); +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_t, int, int); -Static int aue_miibus_writereg(device_t, int, int, int); -Static void aue_miibus_statchg(device_t); +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 u_int32_t aue_crc(caddr_t); +Static uint32_t aue_mchash(const uint8_t *); Static void aue_reset(struct aue_softc *); -Static int csr_read_1(struct aue_softc *, int); -Static int csr_write_1(struct aue_softc *, int, int); -Static int csr_read_2(struct aue_softc *, int); -Static int csr_write_2(struct aue_softc *, int, int); +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 */ @@ -227,27 +243,26 @@ Static driver_t aue_driver = { Static devclass_t aue_devclass; DECLARE_DUMMY_MODULE(if_aue); -DRIVER_MODULE(if_aue, uhub, aue_driver, aue_devclass, usbd_driver_load, 0); +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) \ - csr_write_1(sc, reg, csr_read_1(sc, reg) | (x)) + aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x)) #define AUE_CLRBIT(sc, reg, x) \ - csr_write_1(sc, reg, csr_read_1(sc, reg) & ~(x)) + aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x)) Static int -csr_read_1(struct aue_softc *sc, int reg) +aue_csr_read_1(struct aue_softc *sc, int reg) { usb_device_request_t req; usbd_status err; u_int8_t val = 0; - int s; - if (sc->aue_gone) + if (sc->aue_dying) return(0); - s = splusb(); + AUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = AUE_UR_READREG; @@ -255,29 +270,28 @@ csr_read_1(struct aue_softc *sc, int reg) USETW(req.wIndex, reg); USETW(req.wLength, 1); - err = usbd_do_request_flags(sc->aue_udev, &req, - &val, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->aue_udev, &req, &val); - splx(s); + AUE_UNLOCK(sc); - if (err) - return(0); + if (err) { + return (0); + } - return(val); + return (val); } Static int -csr_read_2(struct aue_softc *sc, int reg) +aue_csr_read_2(struct aue_softc *sc, int reg) { usb_device_request_t req; usbd_status err; u_int16_t val = 0; - int s; - if (sc->aue_gone) - return(0); + if (sc->aue_dying) + return (0); - s = splusb(); + AUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = AUE_UR_READREG; @@ -285,28 +299,27 @@ csr_read_2(struct aue_softc *sc, int reg) USETW(req.wIndex, reg); USETW(req.wLength, 2); - err = usbd_do_request_flags(sc->aue_udev, &req, - &val, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->aue_udev, &req, &val); - splx(s); + AUE_UNLOCK(sc); - if (err) - return(0); + if (err) { + return (0); + } - return(val); + return (val); } Static int -csr_write_1(struct aue_softc *sc, int reg, int val) +aue_csr_write_1(struct aue_softc *sc, int reg, int val) { usb_device_request_t req; usbd_status err; - int s; - if (sc->aue_gone) - return(0); + if (sc->aue_dying) + return (0); - s = splusb(); + AUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = AUE_UR_WRITEREG; @@ -314,28 +327,27 @@ csr_write_1(struct aue_softc *sc, int reg, int val) USETW(req.wIndex, reg); USETW(req.wLength, 1); - err = usbd_do_request_flags(sc->aue_udev, &req, - &val, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->aue_udev, &req, &val); - splx(s); + AUE_UNLOCK(sc); - if (err) - return(-1); + if (err) { + return (-1); + } - return(0); + return (0); } Static int -csr_write_2(struct aue_softc *sc, int reg, int val) +aue_csr_write_2(struct aue_softc *sc, int reg, int val) { usb_device_request_t req; usbd_status err; - int s; - if (sc->aue_gone) - return(0); + if (sc->aue_dying) + return (0); - s = splusb(); + AUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = AUE_UR_WRITEREG; @@ -343,15 +355,15 @@ csr_write_2(struct aue_softc *sc, int reg, int val) USETW(req.wIndex, reg); USETW(req.wLength, 2); - err = usbd_do_request_flags(sc->aue_udev, &req, - &val, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->aue_udev, &req, &val); - splx(s); + AUE_UNLOCK(sc); - if (err) - return(-1); + if (err) { + return (-1); + } - return(0); + return (0); } /* @@ -361,14 +373,13 @@ Static void aue_eeprom_getword(struct aue_softc *sc, int addr, u_int16_t *dest) { int i; - u_int16_t word = 0; + u_int16_t word = 0; - csr_write_1(sc, AUE_EE_REG, addr); - csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ); + 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 (csr_read_1(sc, AUE_EE_CTL) & - AUE_EECTL_DONE) + if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE) break; } @@ -377,7 +388,7 @@ aue_eeprom_getword(struct aue_softc *sc, int addr, u_int16_t *dest) sc->aue_unit); } - word = csr_read_2(sc, AUE_EE_DATA); + word = aue_csr_read_2(sc, AUE_EE_DATA); *dest = word; return; @@ -405,14 +416,12 @@ aue_read_eeprom(struct aue_softc *sc, caddr_t dest, int off, int cnt, int swap) } Static int -aue_miibus_readreg(device_t dev, int phy, int reg) +aue_miibus_readreg(device_ptr_t dev, int phy, int reg) { - struct aue_softc *sc; + struct aue_softc *sc = USBGETSOFTC(dev); int i; u_int16_t val = 0; - sc = device_get_softc(dev); - /* * The Am79C901 HomePNA PHY actually contains * two transceivers: a 1Mbps HomePNA PHY and a @@ -423,53 +432,48 @@ aue_miibus_readreg(device_t dev, int phy, int reg) * happens to be configured for MII address 3, * so we filter that out. */ - if (sc->aue_info->aue_vid == USB_VENDOR_ADMTEK && - sc->aue_info->aue_did == USB_PRODUCT_ADMTEK_PEGASUS) { + if (sc->aue_vendor == USB_VENDOR_ADMTEK && + sc->aue_product == USB_PRODUCT_ADMTEK_PEGASUS) { if (phy == 3) - return(0); + return (0); #ifdef notdef if (phy != 1) - return(0); + return (0); #endif } - csr_write_1(sc, AUE_PHY_ADDR, phy); - csr_write_1(sc, AUE_PHY_CTL, reg|AUE_PHYCTL_READ); + 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 (csr_read_1(sc, AUE_PHY_CTL) & - AUE_PHYCTL_DONE) + 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); + printf("aue%d: MII read timed out\n", sc->aue_unit); } - val = csr_read_2(sc, AUE_PHY_DATA); + val = aue_csr_read_2(sc, AUE_PHY_DATA); - return(val); + return (val); } Static int -aue_miibus_writereg(device_t dev, int phy, int reg, int data) +aue_miibus_writereg(device_ptr_t dev, int phy, int reg, int data) { - struct aue_softc *sc; + struct aue_softc *sc = USBGETSOFTC(dev); int i; if (phy == 3) - return(0); + return (0); - sc = device_get_softc(dev); - - csr_write_2(sc, AUE_PHY_DATA, data); - csr_write_1(sc, AUE_PHY_ADDR, phy); - csr_write_1(sc, AUE_PHY_CTL, reg|AUE_PHYCTL_WRITE); + 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 (csr_read_1(sc, AUE_PHY_CTL) & - AUE_PHYCTL_DONE) + if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) break; } @@ -482,35 +486,32 @@ aue_miibus_writereg(device_t dev, int phy, int reg, int data) } Static void -aue_miibus_statchg(device_t dev) +aue_miibus_statchg(device_ptr_t dev) { - struct aue_softc *sc; - struct mii_data *mii; + struct aue_softc *sc = USBGETSOFTC(dev); + struct mii_data *mii = GET_MII(sc); - sc = device_get_softc(dev); - mii = device_get_softc(sc->aue_miibus); - - AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB|AUE_CTL0_TX_ENB); + 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) { + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); - } else { + else AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); - } - AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB|AUE_CTL0_TX_ENB); + + 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_info->aue_flags & LSYS) { - u_int16_t auxmode; + if (sc->aue_flags & LSYS) { + u_int16_t auxmode; auxmode = aue_miibus_readreg(dev, 0, 0x1b); aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04); } @@ -522,9 +523,11 @@ aue_miibus_statchg(device_t dev) #define AUE_BITS 6 Static u_int32_t -aue_crc(caddr_t addr) +aue_mchash(const uint8_t *addr) { - u_int32_t idx, bit, data, crc; + uint32_t crc; + int idx, bit; + uint8_t data; /* Compute CRC for the address value. */ crc = 0xFFFFFFFF; /* initial value */ @@ -555,14 +558,18 @@ aue_setmulti(struct aue_softc *sc) /* first, zot all the existing hash bits */ for (i = 0; i < 8; i++) - csr_write_1(sc, AUE_MAR0 + i, 0); + aue_csr_write_1(sc, AUE_MAR0 + i, 0); /* now program new ones */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; - ifma = ifma->ifma_link.le_next) { +#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 = aue_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + h = aue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); AUE_SETBIT(sc, AUE_MAR + (h >> 3), 1 << (h & 0x7)); } @@ -573,14 +580,14 @@ Static void aue_reset_pegasus_II(struct aue_softc *sc) { /* Magic constants taken from Linux driver. */ - csr_write_1(sc, AUE_REG_1D, 0); - csr_write_1(sc, AUE_REG_7B, 2); + 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) - csr_write_1(sc, AUE_REG_81, 6); + aue_csr_write_1(sc, AUE_REG_81, 6); else #endif - csr_write_1(sc, AUE_REG_81, 2); + aue_csr_write_1(sc, AUE_REG_81, 2); } Static void @@ -591,7 +598,7 @@ aue_reset(struct aue_softc *sc) AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC); for (i = 0; i < AUE_TIMEOUT; i++) { - if (!(csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) + if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) break; } @@ -607,23 +614,24 @@ aue_reset(struct aue_softc *sc) * Note: We force all of the GPIO pins low first, *then* * enable the ones we want. */ - csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0); - csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1); - - /* Grrr. LinkSys has to be different from everyone else. */ - if (sc->aue_info->aue_flags & LSYS) { - csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1); - csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1| - AUE_GPIO_OUT0); + 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_info->aue_flags & PII) + 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; + return; } /* @@ -632,21 +640,12 @@ aue_reset(struct aue_softc *sc) USB_MATCH(aue) { USB_MATCH_START(aue, uaa); - struct aue_type *t; - - if (!uaa->iface) - return(UMATCH_NONE); - t = aue_devs; - while(t->aue_vid) { - if (uaa->vendor == t->aue_vid && - uaa->product == t->aue_did) { - return(UMATCH_VENDOR_PRODUCT); - } - t++; - } + if (uaa->iface != NULL) + return (UMATCH_NONE); - return(UMATCH_NONE); + return (aue_lookup(uaa->vendor, uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); } /* @@ -657,39 +656,41 @@ USB_ATTACH(aue) { USB_ATTACH_START(aue, sc, uaa); char devinfo[1024]; - int s; 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 aue_type *t; - - s = splimp(); bzero(sc, sizeof(struct aue_softc)); - sc->aue_iface = uaa->iface; + + 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); - splx(s); USB_ATTACH_ERROR_RETURN; } - t = aue_devs; - while(t->aue_vid) { - if (uaa->vendor == t->aue_vid && - uaa->product == t->aue_did) { - sc->aue_info = t; - break; - } - t++; + 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; } - id = usbd_get_interface_descriptor(uaa->iface); + 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); @@ -697,25 +698,30 @@ USB_ATTACH(aue) /* Find endpoints. */ for (i = 0; i < id->bNumEndpoints; i++) { - ed = usbd_interface2endpoint_descriptor(uaa->iface, i); - if (!ed) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (ed == NULL) { printf("aue%d: couldn't get ep %d\n", sc->aue_unit, i); - splx(s); USB_ATTACH_ERROR_RETURN; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && - (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { + 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 && - (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { + 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 && - (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { + 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); @@ -733,8 +739,7 @@ USB_ATTACH(aue) ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; - ifp->if_unit = sc->aue_unit; - ifp->if_name = "aue"; + 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; @@ -761,7 +766,10 @@ USB_ATTACH(aue) 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); - splx(s); + AUE_UNLOCK(sc); +#if __FreeBSD_version >= 500000 + mtx_destroy(&sc->aue_mtx); +#endif USB_ATTACH_ERROR_RETURN; } @@ -771,12 +779,16 @@ USB_ATTACH(aue) /* * 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_gone = 0; + sc->aue_dying = 0; - splx(s); + AUE_UNLOCK(sc); USB_ATTACH_SUCCESS_RETURN; } @@ -785,16 +797,18 @@ aue_detach(device_ptr_t dev) { struct aue_softc *sc; struct ifnet *ifp; - int s; - - s = splusb(); sc = device_get_softc(dev); + AUE_LOCK(sc); ifp = &sc->arpcom.ac_if; - sc->aue_gone = 1; + 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]); @@ -804,9 +818,13 @@ aue_detach(device_ptr_t dev) if (sc->aue_ep[AUE_ENDPT_INTR] != NULL) usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_INTR]); #endif - splx(s); - return(0); + AUE_UNLOCK(sc); +#if __FreeBSD_version >= 500000 + mtx_destroy(&sc->aue_mtx); +#endif + + return (0); } /* @@ -822,7 +840,7 @@ aue_newbuf(struct aue_softc *sc, struct aue_chain *c, struct mbuf *m) if (m_new == NULL) { printf("aue%d: no memory for rx list " "-- packet dropped!\n", sc->aue_unit); - return(ENOBUFS); + return (ENOBUFS); } MCLGET(m_new, M_DONTWAIT); @@ -830,7 +848,7 @@ aue_newbuf(struct aue_softc *sc, struct aue_chain *c, struct mbuf *m) printf("aue%d: no memory for rx list " "-- packet dropped!\n", sc->aue_unit); m_freem(m_new); - return(ENOBUFS); + return (ENOBUFS); } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; } else { @@ -842,7 +860,7 @@ aue_newbuf(struct aue_softc *sc, struct aue_chain *c, struct mbuf *m) m_adj(m_new, ETHER_ALIGN); c->aue_mbuf = m_new; - return(0); + return (0); } Static int @@ -858,15 +876,15 @@ aue_rx_list_init(struct aue_softc *sc) c->aue_sc = sc; c->aue_idx = i; if (aue_newbuf(sc, c, NULL) == ENOBUFS) - return(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 (ENOBUFS); } } - return(0); + return (0); } Static int @@ -885,45 +903,42 @@ aue_tx_list_init(struct aue_softc *sc) if (c->aue_xfer == NULL) { c->aue_xfer = usbd_alloc_xfer(sc->aue_udev); if (c->aue_xfer == NULL) - return(ENOBUFS); + return (ENOBUFS); } c->aue_buf = malloc(AUE_BUFSZ, M_USBDEV, M_NOWAIT); if (c->aue_buf == NULL) - return(ENOBUFS); + return (ENOBUFS); } - return(0); + 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; + struct aue_softc *sc = priv; struct ifnet *ifp; struct aue_intrpkt *p; - int s; - - s = splimp(); - sc = priv; + AUE_LOCK(sc); ifp = &sc->arpcom.ac_if; if (!(ifp->if_flags & IFF_RUNNING)) { - splx(s); + AUE_UNLOCK(sc); return; } if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { - splx(s); + 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]); - splx(s); + AUE_UNLOCK(sc); return; } @@ -935,7 +950,7 @@ aue_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) if (p->aue_txstat0 & (AUE_TXSTAT0_LATECOLL & AUE_TXSTAT0_EXCESSCOLL)) ifp->if_collisions++; - splx(s); + AUE_UNLOCK(sc); return; } #endif @@ -947,10 +962,12 @@ aue_rxstart(struct ifnet *ifp) 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; } @@ -960,6 +977,7 @@ aue_rxstart(struct ifnet *ifp) USBD_NO_TIMEOUT, aue_rxeof); usbd_transfer(c->aue_xfer); + AUE_UNLOCK(sc); return; } @@ -977,18 +995,24 @@ aue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) int total_len = 0; struct aue_rxpkt r; - c = priv; - sc = c->aue_sc; + if (sc->aue_dying) + return; + AUE_LOCK(sc); ifp = &sc->arpcom.ac_if; - if (!(ifp->if_flags & IFF_RUNNING)) + if (!(ifp->if_flags & IFF_RUNNING)) { + AUE_UNLOCK(sc); return; + } if (status != USBD_NORMAL_COMPLETION) { - if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + AUE_UNLOCK(sc); return; - printf("aue%d: usb error on rx: %s\n", sc->aue_unit, - usbd_errstr(status)); + } + 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; @@ -1021,7 +1045,7 @@ aue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) /* Put the packet on the special USB input queue. */ usb_ether_input(m); - + AUE_UNLOCK(sc); return; done: @@ -1031,6 +1055,7 @@ done: USBD_NO_TIMEOUT, aue_rxeof); usbd_transfer(xfer); + AUE_UNLOCK(sc); return; } @@ -1042,28 +1067,24 @@ done: Static void aue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { - struct aue_softc *sc; - struct aue_chain *c; + struct aue_chain *c = priv; + struct aue_softc *sc = c->aue_sc; struct ifnet *ifp; usbd_status err; - int s; - s = splimp(); - - c = priv; - sc = c->aue_sc; + AUE_LOCK(sc); ifp = &sc->arpcom.ac_if; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { - splx(s); + 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]); - splx(s); + AUE_UNLOCK(sc); return; } @@ -1082,7 +1103,7 @@ aue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) else ifp->if_opackets++; - splx(s); + AUE_UNLOCK(sc); return; } @@ -1090,40 +1111,33 @@ aue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) Static void aue_tick(void *xsc) { - struct aue_softc *sc; + struct aue_softc *sc = xsc; struct ifnet *ifp; struct mii_data *mii; - int s; - - s = splimp(); - sc = xsc; - - if (sc == NULL) { - splx(s); + if (sc == NULL) return; - } + + AUE_LOCK(sc); ifp = &sc->arpcom.ac_if; - mii = device_get_softc(sc->aue_miibus); + mii = GET_MII(sc); if (mii == NULL) { - splx(s); + AUE_UNLOCK(sc); return; } mii_tick(mii); - if (!sc->aue_link) { - mii_pollstat(mii); - if (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); + 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); - splx(s); + AUE_UNLOCK(sc); return; } @@ -1163,35 +1177,42 @@ aue_encap(struct aue_softc *sc, struct mbuf *m, int idx) err = usbd_transfer(c->aue_xfer); if (err != USBD_IN_PROGRESS) { aue_stop(sc); - return(EIO); + return (EIO); } sc->aue_cdata.aue_tx_cnt++; - return(0); + return (0); } Static void aue_start(struct ifnet *ifp) { - struct aue_softc *sc; + struct aue_softc *sc = ifp->if_softc; struct mbuf *m_head = NULL; - sc = ifp->if_softc; + AUE_LOCK(sc); - if (!sc->aue_link) + if (!sc->aue_link) { + AUE_UNLOCK(sc); return; + } - if (ifp->if_flags & IFF_OACTIVE) + if (ifp->if_flags & IFF_OACTIVE) { + AUE_UNLOCK(sc); return; + } IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) + 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; } @@ -1199,8 +1220,7 @@ aue_start(struct ifnet *ifp) * If there's a BPF listener, bounce a copy of this frame * to him. */ - if (ifp->if_bpf) - bpf_mtap(ifp, m_head); + BPF_MTAP(ifp, m_head); ifp->if_flags |= IFF_OACTIVE; @@ -1208,6 +1228,7 @@ aue_start(struct ifnet *ifp) * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; + AUE_UNLOCK(sc); return; } @@ -1217,45 +1238,44 @@ aue_init(void *xsc) { struct aue_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; - struct mii_data *mii; + struct mii_data *mii = GET_MII(sc); struct aue_chain *c; usbd_status err; - int i, s; + int i; - if (ifp->if_flags & IFF_RUNNING) - return; + AUE_LOCK(sc); - s = splimp(); + if (ifp->if_flags & IFF_RUNNING) { + AUE_UNLOCK(sc); + return; + } /* * Cancel pending I/O and free all RX/TX buffers. */ aue_reset(sc); - mii = device_get_softc(sc->aue_miibus); - /* Set MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) - csr_write_1(sc, AUE_PAR0 + i, sc->arpcom.ac_enaddr[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) { + if (ifp->if_flags & IFF_PROMISC) AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); - } else { + 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); - splx(s); + 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); - splx(s); + AUE_UNLOCK(sc); return; } @@ -1267,9 +1287,10 @@ aue_init(void *xsc) aue_setmulti(sc); /* Enable RX and TX */ - csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND|AUE_CTL0_RX_ENB); + 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. */ @@ -1278,7 +1299,7 @@ aue_init(void *xsc) if (err) { printf("aue%d: open rx pipe failed: %s\n", sc->aue_unit, usbd_errstr(err)); - splx(s); + AUE_UNLOCK(sc); return; } err = usbd_open_pipe(sc->aue_iface, sc->aue_ed[AUE_ENDPT_TX], @@ -1286,7 +1307,7 @@ aue_init(void *xsc) if (err) { printf("aue%d: open tx pipe failed: %s\n", sc->aue_unit, usbd_errstr(err)); - splx(s); + AUE_UNLOCK(sc); return; } @@ -1298,7 +1319,7 @@ aue_init(void *xsc) if (err) { printf("aue%d: open intr pipe failed: %s\n", sc->aue_unit, usbd_errstr(err)); - splx(s); + AUE_UNLOCK(sc); return; } #endif @@ -1315,10 +1336,10 @@ aue_init(void *xsc) ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; - (void)splx(s); - sc->aue_stat_ch = timeout(aue_tick, sc, hz); + AUE_UNLOCK(sc); + return; } @@ -1328,22 +1349,18 @@ aue_init(void *xsc) Static int aue_ifmedia_upd(struct ifnet *ifp) { - struct aue_softc *sc; - struct mii_data *mii; - - sc = ifp->if_softc; + struct aue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); - mii = device_get_softc(sc->aue_miibus); sc->aue_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)) + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); - return(0); + return (0); } /* @@ -1352,12 +1369,9 @@ aue_ifmedia_upd(struct ifnet *ifp) Static void aue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { - struct aue_softc *sc; - struct mii_data *mii; - - sc = ifp->if_softc; + struct aue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); - mii = device_get_softc(sc->aue_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; @@ -1369,18 +1383,13 @@ 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 ifreq *ifr = (struct ifreq *)data; struct mii_data *mii; - int s, error = 0; + int error = 0; - s = splimp(); + AUE_LOCK(sc); switch(command) { - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && @@ -1407,27 +1416,27 @@ aue_ioctl(struct ifnet *ifp, u_long command, caddr_t data) break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: - mii = device_get_softc(sc->aue_miibus); + mii = GET_MII(sc); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: - error = EINVAL; + error = ether_ioctl(ifp, command, data); break; } - (void)splx(s); + AUE_UNLOCK(sc); - return(error); + return (error); } Static void aue_watchdog(struct ifnet *ifp) { - struct aue_softc *sc; + struct aue_softc *sc = ifp->if_softc; struct aue_chain *c; usbd_status stat; - sc = ifp->if_softc; + AUE_LOCK(sc); ifp->if_oerrors++; printf("aue%d: watchdog timeout\n", sc->aue_unit); @@ -1438,7 +1447,7 @@ aue_watchdog(struct ifnet *ifp) if (ifp->if_snd.ifq_head != NULL) aue_start(ifp); - + AUE_UNLOCK(sc); return; } @@ -1453,11 +1462,12 @@ aue_stop(struct aue_softc *sc) struct ifnet *ifp; int i; + AUE_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; - csr_write_1(sc, AUE_CTL0, 0); - csr_write_1(sc, AUE_CTL1, 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); @@ -1546,6 +1556,7 @@ aue_stop(struct aue_softc *sc) sc->aue_link = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + AUE_UNLOCK(sc); return; } @@ -1560,9 +1571,11 @@ 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/netif/aue/if_auereg.h b/sys/dev/netif/aue/if_auereg.h index 2f88533352..6cb4becbca 100644 --- a/sys/dev/netif/aue/if_auereg.h +++ b/sys/dev/netif/aue/if_auereg.h @@ -29,8 +29,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/if_auereg.h,v 1.5.2.1 2001/12/11 14:57:04 sumikawa Exp $ - * $DragonFly: src/sys/dev/netif/aue/if_auereg.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/if_auereg.h,v 1.17 2003/10/04 21:41:01 joe Exp $ + * $DragonFly: src/sys/dev/netif/aue/if_auereg.h,v 1.3 2003/12/30 01:01:45 dillon Exp $ */ /* @@ -47,20 +47,11 @@ * the RX case, the data includes an optional RX status word. */ -#define AUE_VENDORID_ADMTEK 0x07A6 -#define AUE_DEVICEID_PEGASUS 0x0986 - -#define AUE_VENDORID_BILLIONTON 0x08DD -#define AUE_DEVICEID_USB100 0x0986 - -#define AUE_VENDORID_MELCO 0x0411 -#define AUE_DEVICEID_LUATX 0x0001 - - #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 @@ -206,15 +197,6 @@ struct aue_rxpkt { #define AUE_RXSTAT_DRIBBLE 0x10 #define AUE_RXSTAT_MASK 0x1E -struct aue_type { - u_int16_t aue_vid; - u_int16_t aue_did; - u_int16_t aue_flags; -#define LSYS 0x0001 /* use Linksys reset */ -#define PNA 0x0002 /* has Home PNA */ -#define PII 0x0004 /* Pegasus II chip */ -}; - #define AUE_TX_LIST_CNT 1 #define AUE_RX_LIST_CNT 1 @@ -241,23 +223,43 @@ struct aue_cdata { #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; - struct aue_type *aue_info; + 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; - u_int8_t aue_gone; 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; }; +#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 ETHER_ALIGN 2 #define AUE_BUFSZ 1536 #define AUE_MIN_FRAMELEN 60 #define AUE_INTR_INTERVAL 100 /* ms */ diff --git a/sys/dev/netif/axe/Makefile b/sys/dev/netif/axe/Makefile new file mode 100644 index 0000000000..5f8d94a943 --- /dev/null +++ b/sys/dev/netif/axe/Makefile @@ -0,0 +1,10 @@ +# $DragonFly: src/sys/dev/netif/axe/Makefile,v 1.1 2003/12/30 01:01:46 dillon Exp $ + +S = ${.CURDIR}/../.. +.PATH: $S/dev/usb +KMOD = if_axe +SRCS = if_axe.c opt_bdg.h opt_usb.h device_if.h bus_if.h +SRCS += miibus_if.h +KMODDEPS = miibus + +.include diff --git a/sys/dev/netif/axe/if_axe.c b/sys/dev/netif/axe/if_axe.c new file mode 100644 index 0000000000..5a18ccf34c --- /dev/null +++ b/sys/dev/netif/axe/if_axe.c @@ -0,0 +1,1207 @@ +/* + * Copyright (c) 1997, 1998, 1999, 2000-2003 + * Bill Paul . 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: src/sys/dev/usb/if_axe.c,v 1.10 2003/12/08 07:54:14 obrien Exp $ + * $DragonFly: src/sys/dev/netif/axe/if_axe.c,v 1.1 2003/12/30 01:01:46 dillon Exp $ + */ +/* + * 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 + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../mii_layer//mii.h" +#include "../mii_layer/miivar.h" + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#include "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_LINKSYS_USB200M }, + { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120 }, + { 0, 0 } +}; + +Static struct usb_qdat axe_qdat; + +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 uint32_t axe_mchash(const uint8_t *); + +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 uint32_t +axe_mchash(const uint8_t *addr) +{ + uint32_t crc, carry; + int idx, bit; + uint8_t data; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (idx = 0; idx < 6; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01); + crc <<= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return((crc >> 26) & 0x0000003F); +} + +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 = axe_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + 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; + + /* + * An ASIX chip was detected. Inform the world. + */ + printf("axe%d: Ethernet address: %6D\n", sc->axe_unit, eaddr, ":"); + + 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_output = ether_output; + 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; + + axe_qdat.ifp = ifp; + 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 *)&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); + + axe_reset(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; + } + + /* 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/netif/axe/if_axereg.h b/sys/dev/netif/axe/if_axereg.h new file mode 100644 index 0000000000..76e5793b97 --- /dev/null +++ b/sys/dev/netif/axe/if_axereg.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1997, 1998, 1999, 2000-2003 + * Bill Paul . 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: src/sys/dev/usb/if_axereg.h,v 1.2 2003/06/15 21:45:43 wpaul Exp $ + * $DragonFly: src/sys/dev/netif/axe/if_axereg.h,v 1.1 2003/12/30 01:01:46 dillon Exp $ + */ + +/* + * 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; +}; + +#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/netif/cue/if_cue.c b/sys/dev/netif/cue/if_cue.c index 39263e7222..2d41bc646c 100644 --- a/sys/dev/netif/cue/if_cue.c +++ b/sys/dev/netif/cue/if_cue.c @@ -29,10 +29,9 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/if_cue.c,v 1.7.2.6 2002/11/06 14:23:20 joe Exp $ - * $DragonFly: src/sys/dev/netif/cue/if_cue.c,v 1.4 2003/11/20 22:07:26 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/if_cue.c,v 1.45 2003/12/08 07:54:14 obrien Exp $ + * $DragonFly: src/sys/dev/netif/cue/if_cue.c,v 1.5 2003/12/30 01:01:46 dillon Exp $ * - * $FreeBSD: src/sys/dev/usb/if_cue.c,v 1.7.2.6 2002/11/06 14:23:20 joe Exp $ */ /* @@ -68,8 +67,11 @@ #include -#include /* for DELAY */ #include +#include +#if __FreeBSD_version < 500000 +#include +#endif #include #include @@ -92,9 +94,9 @@ Static struct cue_type cue_devs[] = { Static struct usb_qdat cue_qdat; -Static int cue_match(device_t); -Static int cue_attach(device_t); -Static int cue_detach(device_t); +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 *); @@ -109,17 +111,17 @@ 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_t); +Static void cue_shutdown(device_ptr_t); Static void cue_setmulti(struct cue_softc *); -Static u_int32_t cue_crc(caddr_t); +Static uint32_t cue_mchash(const uint8_t *); Static void cue_reset(struct cue_softc *); -Static int csr_read_1(struct cue_softc *, int); -Static int csr_write_1(struct cue_softc *, int, int); -Static int csr_read_2(struct cue_softc *, int); +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 csr_write_2(struct cue_softc *, int, int); +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 *); @@ -143,26 +145,27 @@ Static driver_t cue_driver = { Static devclass_t cue_devclass; DECLARE_DUMMY_MODULE(if_cue); -DRIVER_MODULE(if_cue, uhub, cue_driver, cue_devclass, usbd_driver_load, 0); +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) \ - csr_write_1(sc, reg, csr_read_1(sc, reg) | (x)) + cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x)) #define CUE_CLRBIT(sc, reg, x) \ - csr_write_1(sc, reg, csr_read_1(sc, reg) & ~(x)) + cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x)) Static int -csr_read_1(struct cue_softc *sc, int reg) +cue_csr_read_1(struct cue_softc *sc, int reg) { usb_device_request_t req; usbd_status err; u_int8_t val = 0; - int s; - if (sc->cue_gone) + if (sc->cue_dying) return(0); - s = splusb(); + CUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; @@ -170,10 +173,9 @@ csr_read_1(struct cue_softc *sc, int reg) USETW(req.wIndex, reg); USETW(req.wLength, 1); - err = usbd_do_request_flags(sc->cue_udev, - &req, &val, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->cue_udev, &req, &val); - splx(s); + CUE_UNLOCK(sc); if (err) return(0); @@ -182,17 +184,16 @@ csr_read_1(struct cue_softc *sc, int reg) } Static int -csr_read_2(struct cue_softc *sc, int reg) +cue_csr_read_2(struct cue_softc *sc, int reg) { usb_device_request_t req; usbd_status err; u_int16_t val = 0; - int s; - if (sc->cue_gone) + if (sc->cue_dying) return(0); - s = splusb(); + CUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; @@ -200,10 +201,9 @@ csr_read_2(struct cue_softc *sc, int reg) USETW(req.wIndex, reg); USETW(req.wLength, 2); - err = usbd_do_request_flags(sc->cue_udev, - &req, &val, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->cue_udev, &req, &val); - splx(s); + CUE_UNLOCK(sc); if (err) return(0); @@ -212,16 +212,15 @@ csr_read_2(struct cue_softc *sc, int reg) } Static int -csr_write_1(struct cue_softc *sc, int reg, int val) +cue_csr_write_1(struct cue_softc *sc, int reg, int val) { usb_device_request_t req; usbd_status err; - int s; - if (sc->cue_gone) + if (sc->cue_dying) return(0); - s = splusb(); + CUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_WRITEREG; @@ -229,10 +228,9 @@ csr_write_1(struct cue_softc *sc, int reg, int val) USETW(req.wIndex, reg); USETW(req.wLength, 0); - err = usbd_do_request_flags(sc->cue_udev, - &req, &val, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->cue_udev, &req, NULL); - splx(s); + CUE_UNLOCK(sc); if (err) return(-1); @@ -242,16 +240,15 @@ csr_write_1(struct cue_softc *sc, int reg, int val) #ifdef notdef Static int -csr_write_2(struct cue_softc *sc, int reg, int val) +cue_csr_write_2(struct cue_softc *sc, int reg, int val) { usb_device_request_t req; usbd_status err; - int s; - if (sc->cue_gone) + if (sc->cue_dying) return(0); - s = splusb(); + CUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_WRITEREG; @@ -259,10 +256,9 @@ csr_write_2(struct cue_softc *sc, int reg, int val) USETW(req.wIndex, reg); USETW(req.wLength, 0); - err = usbd_do_request_flags(sc->cue_udev, - &req, &val, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->cue_udev, &req, NULL); - splx(s); + CUE_UNLOCK(sc); if (err) return(-1); @@ -276,12 +272,11 @@ cue_mem(struct cue_softc *sc, int cmd, int addr, void *buf, int len) { usb_device_request_t req; usbd_status err; - int s; - if (sc->cue_gone) + if (sc->cue_dying) return(0); - s = splusb(); + CUE_LOCK(sc); if (cmd == CUE_CMD_READSRAM) req.bmRequestType = UT_READ_VENDOR_DEVICE; @@ -292,10 +287,9 @@ cue_mem(struct cue_softc *sc, int cmd, int addr, void *buf, int len) USETW(req.wIndex, addr); USETW(req.wLength, len); - err = usbd_do_request_flags(sc->cue_udev, - &req, &buf, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->cue_udev, &req, buf); - splx(s); + CUE_UNLOCK(sc); if (err) return(-1); @@ -308,12 +302,11 @@ cue_getmac(struct cue_softc *sc, void *buf) { usb_device_request_t req; usbd_status err; - int s; - if (sc->cue_gone) + if (sc->cue_dying) return(0); - s = splusb(); + CUE_LOCK(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_GET_MACADDR; @@ -321,10 +314,9 @@ cue_getmac(struct cue_softc *sc, void *buf) USETW(req.wIndex, 0); USETW(req.wLength, ETHER_ADDR_LEN); - err = usbd_do_request_flags(sc->cue_udev, - &req, buf, USBD_NO_TSLEEP, NULL); + err = usbd_do_request(sc->cue_udev, &req, buf); - splx(s); + CUE_UNLOCK(sc); if (err) { printf("cue%d: read MAC address failed\n", sc->cue_unit); @@ -337,10 +329,12 @@ cue_getmac(struct cue_softc *sc, void *buf) #define CUE_POLY 0xEDB88320 #define CUE_BITS 9 -Static u_int32_t -cue_crc(caddr_t addr) +Static uint32_t +cue_mchash(const uint8_t *addr) { - u_int32_t idx, bit, data, crc; + uint32_t crc; + int idx, bit; + uint8_t data; /* Compute CRC for the address value. */ crc = 0xFFFFFFFF; /* initial value */ @@ -365,8 +359,8 @@ cue_setmulti(struct cue_softc *sc) 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); + cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, + &sc->cue_mctab, CUE_MCAST_TABLE_LEN); return; } @@ -375,12 +369,16 @@ cue_setmulti(struct cue_softc *sc) sc->cue_mctab[i] = 0; /* now program new ones */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; - ifma = ifma->ifma_link.le_next) { +#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_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); - sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); + h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); } /* @@ -388,8 +386,12 @@ cue_setmulti(struct cue_softc *sc) * so we can receive broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { - h = cue_crc(etherbroadcastaddr); - sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); +#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, @@ -403,23 +405,16 @@ cue_reset(struct cue_softc *sc) { usb_device_request_t req; usbd_status err; - int s; - if (sc->cue_gone) + if (sc->cue_dying) return; - s = splusb(); - 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_flags(sc->cue_udev, - &req, NULL, USBD_NO_TSLEEP, NULL); - - splx(s); - + err = usbd_do_request(sc->cue_udev, &req, NULL); if (err) printf("cue%d: reset failed\n", sc->cue_unit); @@ -459,15 +454,12 @@ USB_ATTACH(cue) { USB_ATTACH_START(cue, sc, uaa); char devinfo[1024]; - int s; u_char eaddr[ETHER_ADDR_LEN]; struct ifnet *ifp; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int i; - s = splimp(); - bzero(sc, sizeof(struct cue_softc)); sc->cue_iface = uaa->iface; sc->cue_udev = uaa->device; @@ -476,7 +468,6 @@ USB_ATTACH(cue) if (usbd_set_config_no(sc->cue_udev, CUE_CONFIG_NO, 0)) { printf("cue%d: getting interface handle failed\n", sc->cue_unit); - splx(s); USB_ATTACH_ERROR_RETURN; } @@ -492,21 +483,26 @@ USB_ATTACH(cue) if (!ed) { printf("cue%d: couldn't get ep %d\n", sc->cue_unit, i); - splx(s); USB_ATTACH_ERROR_RETURN; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && - (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { + 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 && - (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { + 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 && - (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { + 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); @@ -525,8 +521,7 @@ USB_ATTACH(cue) ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; - ifp->if_unit = sc->cue_unit; - ifp->if_name = "cue"; + 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; @@ -543,12 +538,16 @@ USB_ATTACH(cue) /* * 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_gone = 0; + sc->cue_dying = 0; - splx(s); + CUE_UNLOCK(sc); USB_ATTACH_SUCCESS_RETURN; } @@ -557,16 +556,18 @@ cue_detach(device_ptr_t dev) { struct cue_softc *sc; struct ifnet *ifp; - int s; - - s = splusb(); sc = device_get_softc(dev); + CUE_LOCK(sc); ifp = &sc->arpcom.ac_if; - sc->cue_gone = 1; + 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]); @@ -575,7 +576,10 @@ cue_detach(device_ptr_t dev) if (sc->cue_ep[CUE_ENDPT_INTR] != NULL) usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_INTR]); - splx(s); + CUE_UNLOCK(sc); +#if __FreeBSD_version >= 500000 + mtx_destroy(&sc->cue_mtx); +#endif return(0); } @@ -673,10 +677,12 @@ cue_rxstart(struct ifnet *ifp) 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; } @@ -685,6 +691,7 @@ cue_rxstart(struct ifnet *ifp) 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; } @@ -705,16 +712,22 @@ cue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) c = priv; sc = c->cue_sc; + CUE_LOCK(sc); ifp = &sc->arpcom.ac_if; - if (!(ifp->if_flags & IFF_RUNNING)) + if (!(ifp->if_flags & IFF_RUNNING)) { + CUE_UNLOCK(sc); return; + } if (status != USBD_NORMAL_COMPLETION) { - if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + CUE_UNLOCK(sc); return; - printf("cue%d: usb error on rx: %s\n", sc->cue_unit, - usbd_errstr(status)); + } + 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; @@ -740,6 +753,7 @@ cue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) /* Put the packet on the special USB input queue. */ usb_ether_input(m); + CUE_UNLOCK(sc); return; done: @@ -748,6 +762,7 @@ done: 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; } @@ -764,24 +779,22 @@ cue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) struct cue_chain *c; struct ifnet *ifp; usbd_status err; - int s; - - s = splimp(); 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) { - splx(s); + 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]); - splx(s); + CUE_UNLOCK(sc); return; } @@ -800,7 +813,7 @@ cue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) else ifp->if_opackets++; - splx(s); + CUE_UNLOCK(sc); return; } @@ -810,29 +823,26 @@ cue_tick(void *xsc) { struct cue_softc *sc; struct ifnet *ifp; - int s; - - s = splimp(); sc = xsc; - if (sc == NULL) { - splx(s); + if (sc == NULL) return; - } + + CUE_LOCK(sc); ifp = &sc->arpcom.ac_if; - ifp->if_collisions += csr_read_2(sc, CUE_TX_SINGLECOLL); - ifp->if_collisions += csr_read_2(sc, CUE_TX_MULTICOLL); - ifp->if_collisions += csr_read_2(sc, CUE_TX_EXCESSCOLL); + 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 (csr_read_2(sc, CUE_RX_FRAMEERR)) + if (cue_csr_read_2(sc, CUE_RX_FRAMEERR)) ifp->if_ierrors++; sc->cue_stat_ch = timeout(cue_tick, sc, hz); - splx(s); + CUE_UNLOCK(sc); return; } @@ -881,17 +891,23 @@ cue_start(struct ifnet *ifp) struct mbuf *m_head = NULL; sc = ifp->if_softc; + CUE_LOCK(sc); - if (ifp->if_flags & IFF_OACTIVE) + if (ifp->if_flags & IFF_OACTIVE) { + CUE_UNLOCK(sc); return; + } IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) + 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; } @@ -899,8 +915,7 @@ cue_start(struct ifnet *ifp) * If there's a BPF listener, bounce a copy of this frame * to him. */ - if (ifp->if_bpf) - bpf_mtap(ifp, m_head); + BPF_MTAP(ifp, m_head); ifp->if_flags |= IFF_OACTIVE; @@ -908,6 +923,7 @@ cue_start(struct ifnet *ifp) * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; + CUE_UNLOCK(sc); return; } @@ -919,12 +935,12 @@ cue_init(void *xsc) struct ifnet *ifp = &sc->arpcom.ac_if; struct cue_chain *c; usbd_status err; - int i, s; + int i; if (ifp->if_flags & IFF_RUNNING) return; - s = splimp(); + CUE_LOCK(sc); /* * Cancel pending I/O and free all RX/TX buffers. @@ -935,10 +951,10 @@ cue_init(void *xsc) /* Set MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) - csr_write_1(sc, CUE_PAR0 - i, sc->arpcom.ac_enaddr[i]); + cue_csr_write_1(sc, CUE_PAR0 - i, sc->arpcom.ac_enaddr[i]); /* Enable RX logic. */ - csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON|CUE_ETHCTL_MCAST_ON); + 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) { @@ -950,14 +966,14 @@ cue_init(void *xsc) /* Init TX ring. */ if (cue_tx_list_init(sc) == ENOBUFS) { printf("cue%d: tx list init failed\n", sc->cue_unit); - splx(s); + 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); - splx(s); + CUE_UNLOCK(sc); return; } @@ -968,15 +984,15 @@ cue_init(void *xsc) * Set the number of RX and TX buffers that we want * to reserve inside the ASIC. */ - csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); - csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); + 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. */ - csr_write_1(sc, CUE_ADVANCED_OPMODES, + cue_csr_write_1(sc, CUE_ADVANCED_OPMODES, CUE_AOP_EMBED_RXLEN|0x01); /* 1 wait state */ /* Program the LED operation. */ - csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); + 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], @@ -984,7 +1000,7 @@ cue_init(void *xsc) if (err) { printf("cue%d: open rx pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); - splx(s); + CUE_UNLOCK(sc); return; } err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_TX], @@ -992,7 +1008,7 @@ cue_init(void *xsc) if (err) { printf("cue%d: open tx pipe failed: %s\n", sc->cue_unit, usbd_errstr(err)); - splx(s); + CUE_UNLOCK(sc); return; } @@ -1008,7 +1024,7 @@ cue_init(void *xsc) ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; - (void)splx(s); + CUE_UNLOCK(sc); sc->cue_stat_ch = timeout(cue_tick, sc, hz); @@ -1019,16 +1035,11 @@ Static int cue_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct cue_softc *sc = ifp->if_softc; - int s, error = 0; + int error = 0; - s = splimp(); + CUE_LOCK(sc); switch(command) { - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && @@ -1056,11 +1067,11 @@ cue_ioctl(struct ifnet *ifp, u_long command, caddr_t data) error = 0; break; default: - error = EINVAL; + error = ether_ioctl(ifp, command, data); break; } - (void)splx(s); + CUE_UNLOCK(sc); return(error); } @@ -1070,9 +1081,10 @@ 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); @@ -1083,6 +1095,7 @@ cue_watchdog(struct ifnet *ifp) if (ifp->if_snd.ifq_head != NULL) cue_start(ifp); + CUE_UNLOCK(sc); return; } @@ -1098,10 +1111,12 @@ cue_stop(struct cue_softc *sc) struct ifnet *ifp; int i; + CUE_LOCK(sc); + ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; - csr_write_1(sc, CUE_ETHCTL, 0); + cue_csr_write_1(sc, CUE_ETHCTL, 0); cue_reset(sc); untimeout(cue_tick, sc, sc->cue_stat_ch); @@ -1181,6 +1196,7 @@ cue_stop(struct cue_softc *sc) } ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + CUE_UNLOCK(sc); return; } @@ -1196,8 +1212,10 @@ cue_shutdown(device_ptr_t dev) sc = device_get_softc(dev); + CUE_LOCK(sc); cue_reset(sc); cue_stop(sc); + CUE_UNLOCK(sc); return; } diff --git a/sys/dev/netif/cue/if_cuereg.h b/sys/dev/netif/cue/if_cuereg.h index e513d79be7..f5d9380a63 100644 --- a/sys/dev/netif/cue/if_cuereg.h +++ b/sys/dev/netif/cue/if_cuereg.h @@ -29,8 +29,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/if_cuereg.h,v 1.5 2000/01/28 02:15:30 wpaul Exp $ - * $DragonFly: src/sys/dev/netif/cue/if_cuereg.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/if_cuereg.h,v 1.12 2003/10/04 21:41:01 joe Exp $ + * $DragonFly: src/sys/dev/netif/cue/if_cuereg.h,v 1.3 2003/12/30 01:01:46 dillon Exp $ */ /* @@ -117,7 +117,6 @@ #define CUE_MCAST_TABLE_LEN 64 #define CUE_TIMEOUT 1000 -#define ETHER_ALIGN 2 #define CUE_BUFSZ 1536 #define CUE_MIN_FRAMELEN 60 #define CUE_RX_FRAMES 1 @@ -176,8 +175,20 @@ struct cue_softc { int cue_unit; u_int8_t cue_mctab[CUE_MCAST_TABLE_LEN]; int cue_if_flags; - u_int8_t cue_gone; 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; }; + +#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/netif/kue/if_kue.c b/sys/dev/netif/kue/if_kue.c index 3d1b40e95f..75e91b604f 100644 --- a/sys/dev/netif/kue/if_kue.c +++ b/sys/dev/netif/kue/if_kue.c @@ -29,10 +29,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/if_kue.c,v 1.17.2.9 2003/04/13 02:39:25 murray Exp $ - * $DragonFly: src/sys/dev/netif/kue/if_kue.c,v 1.4 2003/11/20 22:07:29 dillon Exp $ - * - * $FreeBSD: src/sys/dev/usb/if_kue.c,v 1.17.2.9 2003/04/13 02:39:25 murray Exp $ + * $FreeBSD: src/sys/dev/usb/if_kue.c,v 1.17.2.9 2003/04/13 02:39:25 murray Exp $ + * $DragonFly: src/sys/dev/netif/kue/if_kue.c,v 1.5 2003/12/30 01:01:46 dillon Exp $ */ /* @@ -83,8 +81,11 @@ #include -#include /* for DELAY */ #include +#include +#if __FreeBSD_version < 500000 +#include +#endif #include #include @@ -96,6 +97,9 @@ #include "if_kuereg.h" #include +MODULE_DEPEND(kue, usb, 1, 1, 1); +MODULE_DEPEND(kue, ether, 1, 1, 1); + /* * Various supported device vendors/products. */ @@ -116,15 +120,17 @@ Static struct kue_type kue_devs[] = { { 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 struct usb_qdat kue_qdat; -Static int kue_match(device_t); -Static int kue_attach(device_t); -Static int kue_detach(device_t); -Static void kue_shutdown(device_t); +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 *); @@ -167,7 +173,7 @@ Static driver_t kue_driver = { Static devclass_t kue_devclass; DECLARE_DUMMY_MODULE(if_kue); -DRIVER_MODULE(if_kue, uhub, kue_driver, kue_devclass, usbd_driver_load, 0); +DRIVER_MODULE(kue, uhub, kue_driver, kue_devclass, usbd_driver_load, 0); /* * We have a custom do_request function which is almost like the @@ -184,7 +190,7 @@ kue_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data) xfer = usbd_alloc_xfer(dev); usbd_setup_default_xfer(xfer, dev, 0, 500000, req, - data, UGETW(req->wLength), USBD_SHORT_XFER_OK|USBD_NO_TSLEEP, 0); + data, UGETW(req->wLength), USBD_SHORT_XFER_OK, 0); err = usbd_sync_transfer(xfer); usbd_free_xfer(xfer); return(err); @@ -196,14 +202,13 @@ 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; - int s; - if (sc->kue_gone) + if (sc->kue_dying) return(USBD_NORMAL_COMPLETION); dev = sc->kue_udev; - s = splusb(); + KUE_LOCK(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; @@ -214,7 +219,7 @@ kue_setword(struct kue_softc *sc, u_int8_t breq, u_int16_t word) err = kue_do_request(dev, &req, NULL); - splx(s); + KUE_UNLOCK(sc); return(err); } @@ -226,14 +231,13 @@ kue_ctl(struct kue_softc *sc, int rw, u_int8_t breq, u_int16_t val, usbd_device_handle dev; usb_device_request_t req; usbd_status err; - int s; dev = sc->kue_udev; - if (sc->kue_gone) + if (sc->kue_dying) return(USBD_NORMAL_COMPLETION); - s = splusb(); + KUE_LOCK(sc); if (rw == KUE_CTL_WRITE) req.bmRequestType = UT_WRITE_VENDOR_DEVICE; @@ -247,7 +251,7 @@ kue_ctl(struct kue_softc *sc, int rw, u_int8_t breq, u_int16_t val, err = kue_do_request(dev, &req, data); - splx(s); + KUE_UNLOCK(sc); return(err); } @@ -327,8 +331,12 @@ kue_setmulti(struct kue_softc *sc) sc->kue_rxfilt &= ~KUE_RXFILT_ALLMULTI; - for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; - ifma = ifma->ifma_link.le_next) { +#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; /* @@ -363,7 +371,9 @@ kue_setmulti(struct kue_softc *sc) Static void kue_reset(struct kue_softc *sc) { - if (usbd_set_config_no(sc->kue_udev, KUE_CONFIG_NO, 0)) { + 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); } @@ -404,15 +414,12 @@ USB_ATTACH(kue) { USB_ATTACH_START(kue, sc, uaa); char devinfo[1024]; - int s; struct ifnet *ifp; usbd_status err; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int i; - s = splimp(); - bzero(sc, sizeof(struct kue_softc)); sc->kue_iface = uaa->iface; sc->kue_udev = uaa->device; @@ -430,24 +437,32 @@ USB_ATTACH(kue) if (!ed) { printf("kue%d: couldn't get ep %d\n", sc->kue_unit, i); - splx(s); USB_ATTACH_ERROR_RETURN; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && - (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { + 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 && - (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { + 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 && - (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { + 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)) { - splx(s); + KUE_UNLOCK(sc); +#if __FreeBSD_version >= 500000 + mtx_destroy(&sc->kue_mtx); +#endif USB_ATTACH_ERROR_RETURN; } @@ -472,8 +487,7 @@ USB_ATTACH(kue) ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; - ifp->if_unit = sc->kue_unit; - ifp->if_name = "kue"; + 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; @@ -490,11 +504,16 @@ USB_ATTACH(kue) /* * 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_gone = 0; + sc->kue_dying = 0; + + KUE_UNLOCK(sc); - splx(s); USB_ATTACH_SUCCESS_RETURN; } @@ -503,17 +522,19 @@ kue_detach(device_ptr_t dev) { struct kue_softc *sc; struct ifnet *ifp; - int s; - - s = splusb(); sc = device_get_softc(dev); + KUE_LOCK(sc); ifp = &sc->arpcom.ac_if; - sc->kue_gone = 1; + 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]); @@ -525,7 +546,10 @@ kue_detach(device_ptr_t dev) if (sc->kue_mcfilters != NULL) free(sc->kue_mcfilters, M_USBDEV); - splx(s); + KUE_UNLOCK(sc); +#if __FreeBSD_version >= 500000 + mtx_destroy(&sc->kue_mtx); +#endif return(0); } @@ -622,6 +646,7 @@ kue_rxstart(struct ifnet *ifp) 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) { @@ -635,6 +660,8 @@ kue_rxstart(struct ifnet *ifp) USBD_NO_TIMEOUT, kue_rxeof); usbd_transfer(c->kue_xfer); + KUE_UNLOCK(sc); + return; } @@ -654,16 +681,22 @@ Static void kue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, c = priv; sc = c->kue_sc; + KUE_LOCK(sc); ifp = &sc->arpcom.ac_if; - if (!(ifp->if_flags & IFF_RUNNING)) + if (!(ifp->if_flags & IFF_RUNNING)) { + KUE_UNLOCK(sc); return; + } if (status != USBD_NORMAL_COMPLETION) { - if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + KUE_UNLOCK(sc); return; - printf("kue%d: usb error on rx: %s\n", sc->kue_unit, - usbd_errstr(status)); + } + 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; @@ -691,6 +724,7 @@ Static void kue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, /* Put the packet on the special USB input queue. */ usb_ether_input(m); + KUE_UNLOCK(sc); return; done: @@ -700,6 +734,7 @@ done: 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; } @@ -716,26 +751,25 @@ kue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) struct kue_chain *c; struct ifnet *ifp; usbd_status err; - int s; - - s = splimp(); 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) { - splx(s); + 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]); - splx(s); + KUE_UNLOCK(sc); return; } @@ -752,7 +786,7 @@ kue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) else ifp->if_opackets++; - splx(s); + KUE_UNLOCK(sc); return; } @@ -802,17 +836,23 @@ kue_start(struct ifnet *ifp) struct mbuf *m_head = NULL; sc = ifp->if_softc; + KUE_LOCK(sc); - if (ifp->if_flags & IFF_OACTIVE) + if (ifp->if_flags & IFF_OACTIVE) { + KUE_UNLOCK(sc); return; + } IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) + 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; } @@ -820,8 +860,7 @@ kue_start(struct ifnet *ifp) * If there's a BPF listener, bounce a copy of this frame * to him. */ - if (ifp->if_bpf) - bpf_mtap(ifp, m_head); + BPF_MTAP(ifp, m_head); ifp->if_flags |= IFF_OACTIVE; @@ -829,6 +868,7 @@ kue_start(struct ifnet *ifp) * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; + KUE_UNLOCK(sc); return; } @@ -840,12 +880,14 @@ kue_init(void *xsc) struct ifnet *ifp = &sc->arpcom.ac_if; struct kue_chain *c; usbd_status err; - int i, s; + int i; - if (ifp->if_flags & IFF_RUNNING) - return; + KUE_LOCK(sc); - s = splimp(); + if (ifp->if_flags & IFF_RUNNING) { + KUE_UNLOCK(sc); + return; + } /* Set MAC address */ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, @@ -872,14 +914,14 @@ kue_init(void *xsc) /* Init TX ring. */ if (kue_tx_list_init(sc) == ENOBUFS) { printf("kue%d: tx list init failed\n", sc->kue_unit); - splx(s); + 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); - splx(s); + KUE_UNLOCK(sc); return; } @@ -892,7 +934,7 @@ kue_init(void *xsc) if (err) { printf("kue%d: open rx pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); - splx(s); + KUE_UNLOCK(sc); return; } @@ -901,7 +943,7 @@ kue_init(void *xsc) if (err) { printf("kue%d: open tx pipe failed: %s\n", sc->kue_unit, usbd_errstr(err)); - splx(s); + KUE_UNLOCK(sc); return; } @@ -917,7 +959,7 @@ kue_init(void *xsc) ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; - (void)splx(s); + KUE_UNLOCK(sc); return; } @@ -926,16 +968,11 @@ Static int kue_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct kue_softc *sc = ifp->if_softc; - int s, error = 0; + int error = 0; - s = splimp(); + KUE_LOCK(sc); switch(command) { - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && @@ -965,11 +1002,11 @@ kue_ioctl(struct ifnet *ifp, u_long command, caddr_t data) error = 0; break; default: - error = EINVAL; + error = ether_ioctl(ifp, command, data); break; } - (void)splx(s); + KUE_UNLOCK(sc); return(error); } @@ -982,7 +1019,7 @@ kue_watchdog(struct ifnet *ifp) usbd_status stat; sc = ifp->if_softc; - + KUE_LOCK(sc); ifp->if_oerrors++; printf("kue%d: watchdog timeout\n", sc->kue_unit); @@ -992,6 +1029,7 @@ kue_watchdog(struct ifnet *ifp) if (ifp->if_snd.ifq_head != NULL) kue_start(ifp); + KUE_UNLOCK(sc); return; } @@ -1007,6 +1045,7 @@ kue_stop(struct kue_softc *sc) struct ifnet *ifp; int i; + KUE_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; @@ -1086,6 +1125,7 @@ kue_stop(struct kue_softc *sc) } ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + KUE_UNLOCK(sc); return; } diff --git a/sys/dev/netif/kue/if_kuereg.h b/sys/dev/netif/kue/if_kuereg.h index 635716a89d..f2f62306bf 100644 --- a/sys/dev/netif/kue/if_kuereg.h +++ b/sys/dev/netif/kue/if_kuereg.h @@ -29,8 +29,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/if_kuereg.h,v 1.5 2000/01/28 02:15:31 wpaul Exp $ - * $DragonFly: src/sys/dev/netif/kue/if_kuereg.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/if_kuereg.h,v 1.13 2003/10/04 21:41:01 joe Exp $ + * $DragonFly: src/sys/dev/netif/kue/if_kuereg.h,v 1.3 2003/12/30 01:01:46 dillon Exp $ */ /* @@ -111,7 +111,6 @@ struct kue_ether_desc { #define KUE_RXFILT_MULTICAST 0x0010 #define KUE_TIMEOUT 1000 -#define ETHER_ALIGN 2 #define KUE_BUFSZ 1536 #define KUE_MIN_FRAMELEN 60 @@ -122,6 +121,7 @@ struct kue_ether_desc { #define KUE_CTL_WRITE 0x02 #define KUE_CONFIG_NO 1 +#define KUE_IFACE_IDX 0 /* * The interrupt endpoint is currently unused @@ -167,8 +167,20 @@ struct kue_softc { usbd_pipe_handle kue_ep[KUE_ENDPT_MAX]; int kue_unit; int kue_if_flags; - u_int8_t kue_gone; 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; }; + +#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/usbmisc/ubsa/ubsa.c b/sys/dev/usbmisc/ubsa/ubsa.c index 53002d00f1..a76e869f15 100644 --- a/sys/dev/usbmisc/ubsa/ubsa.c +++ b/sys/dev/usbmisc/ubsa/ubsa.c @@ -23,6 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + /* * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. @@ -58,8 +59,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/ubsa.c,v 1.2.2.2 2003/11/30 12:53:40 akiyama Exp $ - * $DragonFly: src/sys/dev/usbmisc/ubsa/ubsa.c,v 1.4 2003/12/29 06:42:13 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/ubsa.c,v 1.11 2003/11/16 12:13:39 akiyama Exp $ + * $DragonFly: src/sys/dev/usbmisc/ubsa/ubsa.c,v 1.5 2003/12/30 01:01:46 dillon Exp $ */ #include @@ -69,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -92,7 +94,7 @@ #include "../ucom/ucomvar.h" -#ifdef UBSA_DEBUG +#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, @@ -177,9 +179,13 @@ struct ubsa_softc { 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); @@ -247,6 +253,10 @@ 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); @@ -401,6 +411,11 @@ USB_ATTACH(ubsa) 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); @@ -430,6 +445,10 @@ USB_DETACH(ubsa) rv = ucom_detach(&sc->sc_ucom); +#if __FreeBSD_version >= 500000 + ithread_remove_handler(sc->sc_swicookie); +#endif + return (rv); } @@ -617,9 +636,10 @@ ubsa_param(void *addr, int portno, struct termios *ti) { struct ubsa_softc *sc; + sc = addr; + DPRINTF(("ubsa_param: sc = %p\n", sc)); - sc = addr; ubsa_baudrate(sc, ti->c_ospeed); ubsa_parity(sc, ti->c_cflag); ubsa_databits(sc, ti->c_cflag); @@ -720,6 +740,20 @@ ubsa_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) 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); } diff --git a/sys/dev/usbmisc/ucom/ucom.c b/sys/dev/usbmisc/ucom/ucom.c index 4fa113559c..cb4cf9e33b 100644 --- a/sys/dev/usbmisc/ucom/ucom.c +++ b/sys/dev/usbmisc/ucom/ucom.c @@ -1,9 +1,9 @@ /* * $NetBSD: ucom.c,v 1.39 2001/08/16 22:31:24 augustss Exp $ - * $FreeBSD: src/sys/dev/usb/ucom.c,v 1.24.2.5 2003/11/30 12:48:52 akiyama Exp $ - * $DragonFly: src/sys/dev/usbmisc/ucom/ucom.c,v 1.11 2003/12/29 18:07:38 dillon Exp $ + * $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ + * $FreeBSD: src/sys/dev/usb/ucom.c,v 1.35 2003/11/16 11:58:21 akiyama Exp $ + * $DragonFly: src/sys/dev/usbmisc/ucom/ucom.c,v 1.12 2003/12/30 01:01:46 dillon Exp $ */ - /*- * Copyright (c) 2001-2002, Shunsuke Akiyama . * All rights reserved. @@ -351,7 +351,7 @@ ucomopen(dev_t dev, int flag, int mode, usb_proc_ptr td) &sc->sc_bulkin_pipe); if (err) { printf("%s: open bulk in error (addr %d): %s\n", - USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no, + USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no, usbd_errstr(err)); error = EIO; goto fail_0; @@ -735,7 +735,7 @@ ucom_dtr(struct ucom_softc *sc, int onoff) if (sc->sc_callback->ucom_set == NULL) return; - sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, + sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, UCOM_SET_DTR, onoff); } @@ -746,7 +746,7 @@ ucom_rts(struct ucom_softc *sc, int onoff) if (sc->sc_callback->ucom_set == NULL) return; - sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, + sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno, UCOM_SET_RTS, onoff); } @@ -886,7 +886,7 @@ ucomstart(struct tty *tp) CLR(tp->t_state, TS_SO_OLOWAT); wakeup(TSA_OLOWAT(tp)); } - selwakeup(&tp->t_wsel); + 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) { @@ -920,7 +920,7 @@ ucomstart(struct tty *tp) memcpy(sc->sc_obuf, data, cnt); DPRINTF(("ucomstart: %d chars\n", cnt)); - usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, + 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? */ @@ -1026,8 +1026,8 @@ ucomstartread(struct ucom_softc *sc) if (sc->sc_bulkin_pipe == NULL) return (USBD_NORMAL_COMPLETION); - usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe, - (usbd_private_handle)sc, + 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); diff --git a/sys/dev/usbmisc/ucom/ucomvar.h b/sys/dev/usbmisc/ucom/ucomvar.h index 9c77f6734a..41b4bdf57e 100644 --- a/sys/dev/usbmisc/ucom/ucomvar.h +++ b/sys/dev/usbmisc/ucom/ucomvar.h @@ -1,6 +1,8 @@ -/* $NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/ucomvar.h,v 1.2.2.1 2002/08/08 18:45:04 joe Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/ucom/ucomvar.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/ucomvar.h,v 1.2 2002/07/31 14:34:35 joe Exp $ + * $DragonFly: src/sys/dev/usbmisc/ucom/ucomvar.h,v 1.3 2003/12/30 01:01:46 dillon Exp $ + */ /*- * Copyright (c) 2001-2002, Shunsuke Akiyama . diff --git a/sys/dev/usbmisc/udbp/udbp.c b/sys/dev/usbmisc/udbp/udbp.c new file mode 100644 index 0000000000..2b4169a002 --- /dev/null +++ b/sys/dev/usbmisc/udbp/udbp.c @@ -0,0 +1,844 @@ +/* + * 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. + * + * $FreeBSD: src/sys/dev/usb/udbp.c,v 1.24 2003/08/24 17:55:55 obrien Exp $ + * $DragonFly: src/sys/dev/usbmisc/udbp/Attic/udbp.c,v 1.1 2003/12/30 01:01:46 dillon Exp $ + */ + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#if __FreeBSD_version >= 500014 +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + +#include +#include +#include +#include + +#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 = { + NG_ABI_VERSION, + NG_UDBP_NODE_TYPE, + NULL, + ng_udbp_constructor, + ng_udbp_rcvmsg, + ng_udbp_rmnode, + ng_udbp_newhook, + NULL, + ng_udbp_connect, + ng_udbp_rcvdata, + ng_udbp_disconnect, + 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; + meta_p meta; + + NGI_GET_M(item, m); + NGI_GET_META(item, meta); + NG_FREE_ITEM(item); + /* + * Now queue the data for when it can be sent + */ + if (meta && meta->priority > 0) { + 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); + NG_FREE_META(meta); + 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/usbmisc/udbp/udbp.h b/sys/dev/usbmisc/udbp/udbp.h new file mode 100644 index 0000000000..7c364e024c --- /dev/null +++ b/sys/dev/usbmisc/udbp/udbp.h @@ -0,0 +1,81 @@ +/* + * 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: src/sys/dev/usb/udbp.h,v 1.3 2003/07/04 01:50:38 jmg Exp $ + * $DragonFly: src/sys/dev/usbmisc/udbp/Attic/udbp.h,v 1.1 2003/12/30 01:01:46 dillon Exp $ + */ + +#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/usbmisc/ufm/ufm.c b/sys/dev/usbmisc/ufm/ufm.c index d411d508e8..7e9dad231c 100644 --- a/sys/dev/usbmisc/ufm/ufm.c +++ b/sys/dev/usbmisc/ufm/ufm.c @@ -28,8 +28,10 @@ * its contributors. */ -/* $FreeBSD: src/sys/dev/usb/ufm.c,v 1.1.2.3 2002/11/06 14:41:01 joe Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/ufm/ufm.c,v 1.4 2003/08/07 21:17:14 dillon Exp $ */ +/* + * $FreeBSD: src/sys/dev/usb/ufm.c,v 1.16 2003/10/04 21:41:01 joe Exp $ + * $DragonFly: src/sys/dev/usbmisc/ufm/ufm.c,v 1.5 2003/12/30 01:01:46 dillon Exp $ + */ #include #include @@ -158,7 +160,7 @@ USB_ATTACH(ufm) usbd_status r; char * ermsg = ""; - DPRINTFN(10,("ufm_attach: sc=%p\n", sc)); + 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); @@ -179,12 +181,12 @@ USB_ATTACH(ufm) r = usbd_interface_count(udev, &niface); if (r) { ermsg = "iface"; - goto nobulk; + goto nobulk; } r = usbd_device2interface_handle(udev, 0, &iface); if (r) { ermsg = "iface"; - goto nobulk; + goto nobulk; } sc->sc_iface = iface; #endif @@ -192,7 +194,7 @@ USB_ATTACH(ufm) sc->sc_refcnt = 0; r = usbd_endpoint_count(iface, &epcount); - if (r != USBD_NORMAL_COMPLETION) { + if (r != USBD_NORMAL_COMPLETION) { ermsg = "endpoints"; goto nobulk; } @@ -232,7 +234,7 @@ ufmopen(dev_t dev, int flag, int mode, usb_proc_ptr td) int unit = UFMUNIT(dev); USB_GET_SC_OPEN(ufm, unit, sc); - DPRINTFN(5, ("ufmopen: flag=%d, mode=%d, unit=%d\n", + DPRINTFN(5, ("ufmopen: flag=%d, mode=%d, unit=%d\n", flag, mode, unit)); if (sc->sc_opened) @@ -256,7 +258,7 @@ ufmclose(dev_t dev, int flag, int mode, usb_proc_ptr td) DPRINTFN(5, ("ufmclose: flag=%d, mode=%d, unit=%d\n", flag, mode, unit)); sc->sc_opened = 0; sc->sc_refcnt = 0; - return 0; + return 0; } static int @@ -273,7 +275,8 @@ ufm_do_req(struct ufm_softc *sc, u_int8_t reqtype, u_int8_t 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); + 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); @@ -293,7 +296,7 @@ ufm_set_freq(struct ufm_softc *sc, caddr_t addr) * 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. + * easier. */ sc->sc_freq = freq; freq = (freq + 10700001) / 12500; @@ -302,7 +305,7 @@ ufm_set_freq(struct ufm_softc *sc, caddr_t addr) 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, + if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x96, 0xb7, 1, &ret) != 0) return (EIO); return (0); @@ -320,8 +323,8 @@ 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, + + 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, @@ -336,7 +339,7 @@ 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); @@ -350,7 +353,7 @@ 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 @@ -363,7 +366,7 @@ ufm_get_stat(struct ufm_softc *sc, caddr_t addr) 1, &ret)) return (EIO); *(int *)addr = ret; - + return (0); } @@ -465,7 +468,7 @@ USB_DETACH(ufm) #if defined(__FreeBSD__) Static int ufm_detach(device_t self) -{ +{ DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); return 0; } diff --git a/sys/dev/usbmisc/uftdi/uftdi.c b/sys/dev/usbmisc/uftdi/uftdi.c index 15223c5145..124f2c0cf1 100644 --- a/sys/dev/usbmisc/uftdi/uftdi.c +++ b/sys/dev/usbmisc/uftdi/uftdi.c @@ -1,7 +1,7 @@ /* - * $NetBSD: uftdi.c,v 1.12 2002/07/18 14:44:10 scw Exp $ - * $FreeBSD: src/sys/dev/usb/uftdi.c,v 1.3.2.3 2003/07/21 11:50:06 akiyama Exp $ - * $DragonFly: src/sys/dev/usbmisc/uftdi/uftdi.c,v 1.4 2003/12/29 06:42:16 dillon Exp $ + * $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ + * $FreeBSD: src/sys/dev/usb/uftdi.c,v 1.10 2003/08/24 17:55:55 obrien Exp $ + * $DragonFly: src/sys/dev/usbmisc/uftdi/uftdi.c,v 1.5 2003/12/30 01:01:46 dillon Exp $ */ /* @@ -109,7 +109,7 @@ SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW, struct uftdi_softc { struct ucom_softc sc_ucom; - + usbd_interface_handle sc_iface; /* interface */ enum uftdi_type sc_type; @@ -213,9 +213,9 @@ USB_ATTACH(uftdi) 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); @@ -224,7 +224,7 @@ USB_ATTACH(uftdi) ": %s\n", devname, usbd_errstr(err)); goto bad; } - + addr = ed->bEndpointAddress; dir = UE_GET_DIR(ed->bEndpointAddress); attr = ed->bmAttributes & UE_XFERTYPE; @@ -265,7 +265,7 @@ USB_ATTACH(uftdi) 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: @@ -285,7 +285,6 @@ uftdi_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: if (sc->sc_subdev != NULL) @@ -300,7 +299,7 @@ uftdi_activate(device_ptr_t self, enum devact act) USB_DETACH(uftdi) { USB_DETACH_START(uftdi, sc); - + int rv = 0; DPRINTF(("uftdi_detach: sc=%p\n", sc)); diff --git a/sys/dev/usbmisc/uftdi/uftdireg.h b/sys/dev/usbmisc/uftdi/uftdireg.h index 5ec83eff4e..856aa70d58 100644 --- a/sys/dev/usbmisc/uftdi/uftdireg.h +++ b/sys/dev/usbmisc/uftdi/uftdireg.h @@ -1,6 +1,8 @@ -/* $NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/uftdireg.h,v 1.1.2.1 2002/11/21 01:28:17 ticso Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/uftdi/uftdireg.h,v 1.2 2003/06/17 04:28:32 dillon Exp $ */ +/* + * $NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/uftdireg.h,v 1.1 2002/08/11 23:32:33 joe Exp $ + * $DragonFly: src/sys/dev/usbmisc/uftdi/uftdireg.h,v 1.3 2003/12/30 01:01:46 dillon Exp $ + */ /* * Definitions for the FTDI USB Single Port Serial Converter - diff --git a/sys/dev/usbmisc/ugen/ugen.c b/sys/dev/usbmisc/ugen/ugen.c index c4100a426c..d9ae692f0a 100644 --- a/sys/dev/usbmisc/ugen/ugen.c +++ b/sys/dev/usbmisc/ugen/ugen.c @@ -1,6 +1,16 @@ -/* $NetBSD: ugen.c,v 1.27 1999/10/28 12:08:38 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/ugen.c,v 1.38.2.9 2002/11/06 14:41:01 joe Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/ugen/ugen.c,v 1.7 2003/08/07 21:17:14 dillon Exp $ */ +/* + * $NetBSD: ugen.c,v 1.27 1999/10/28 12:08:38 augustss Exp $ + * $NetBSD: ugen.c,v 1.59 2002/07/11 21:14:28 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/ugen.c,v 1.81 2003/11/09 09:17:22 tanimura Exp $ + * $DragonFly: src/sys/dev/usbmisc/ugen/ugen.c,v 1.8 2003/12/30 01:01:46 dillon 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 $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -57,8 +67,11 @@ #endif #include #include +#if __FreeBSD_version >= 500014 +#include +#else #include -#include +#endif #include #include #include @@ -89,6 +102,9 @@ SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW, struct ugen_endpoint { struct ugen_softc *sc; +#if defined(__FreeBSD__) + dev_t dev; +#endif usb_endpoint_descriptor_t *edesc; usbd_interface_handle iface; int state; @@ -113,6 +129,9 @@ struct ugen_endpoint { struct ugen_softc { USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; +#if defined(__FreeBSD__) + dev_t dev; +#endif char sc_is_open[USB_MAX_ENDPOINTS]; struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2]; @@ -155,17 +174,21 @@ Static struct cdevsw ugen_cdevsw = { }; #endif -Static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, - usbd_status status); +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); +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); + 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); @@ -179,6 +202,10 @@ USB_MATCH(ugen) { USB_MATCH_START(ugen, uaa); +#if 0 + if (uaa->matchlvl) + return (uaa->matchlvl); +#endif if (uaa->usegeneric) return (UMATCH_GENERIC); else @@ -192,7 +219,7 @@ USB_ATTACH(ugen) 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); @@ -204,7 +231,7 @@ USB_ATTACH(ugen) /* 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", + printf("%s: setting configuration index 0 failed\n", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; @@ -214,15 +241,80 @@ USB_ATTACH(ugen) /* Set up all the local state for this configuration. */ err = ugen_set_config(sc, conf); if (err) { - printf("%s: setting configuration %d failed\n", + 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; + dev_t 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; + dev_t 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) { @@ -238,6 +330,10 @@ ugen_set_config(struct ugen_softc *sc, int configno) 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. */ @@ -249,9 +345,9 @@ ugen_set_config(struct ugen_softc *sc, int configno) return (USBD_IN_USE); } + /* Avoid setting the current value. */ if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) { - /* Avoid setting the current value. */ - err = usbd_set_config_no(dev, configno, 0); + err = usbd_set_config_no(dev, configno, 1); if (err) return (err); } @@ -270,45 +366,21 @@ ugen_set_config(struct ugen_softc *sc, int configno) return (err); for (endptno = 0; endptno < nendpt; endptno++) { ed = usbd_interface2endpoint_descriptor(iface,endptno); - endpt = UE_GET_ADDR(ed->bEndpointAddress); - dir = UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN? - IN : OUT; - - sce = &sc->sc_endpoints[endpt][dir]; + 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, endpt, dir, sce)); - + "(%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__) - /* the main device, ctrl endpoint */ - make_dev(&ugen_cdevsw, UGENMINOR(USBDEVUNIT(sc->sc_dev), 0), - UID_ROOT, GID_OPERATOR, 0644, "%s", USBDEVNAME(sc->sc_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. - */ - make_dev(&ugen_cdevsw, - UGENMINOR(USBDEVUNIT(sc->sc_dev), endptno), - UID_ROOT, GID_OPERATOR, 0644, - "%s.%d", - USBDEVNAME(sc->sc_dev), endptno); - } - } + ugen_make_devnodes(sc); #endif return (USBD_NORMAL_COMPLETION); @@ -330,7 +402,7 @@ ugenopen(dev_t dev, int flag, int mode, usb_proc_ptr p) USB_GET_SC_OPEN(ugen, unit, sc); - DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", + DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", flag, mode, unit, endpt)); if (sc == NULL || sc->sc_dying) @@ -361,22 +433,29 @@ ugenopen(dev_t dev, int flag, int mode, usb_proc_ptr p) 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", + 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", + 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, + 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) { @@ -387,7 +466,7 @@ ugenopen(dev_t dev, int flag, int mode, usb_proc_ptr p) DPRINTFN(5, ("ugenopen: interrupt open done\n")); break; case UE_BULK: - err = usbd_open_pipe(sce->iface, + err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) return (EIO); @@ -402,7 +481,7 @@ ugenopen(dev_t dev, int flag, int mode, usb_proc_ptr p) 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", + DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n", endpt, isize)); err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); @@ -439,6 +518,7 @@ ugenopen(dev_t dev, int flag, int mode, usb_proc_ptr p) usbd_free_xfer(sce->isoreqs[i].xfer); return (ENOMEM); case UE_CONTROL: + sce->timeout = USBD_DEFAULT_TIMEOUT; return (EINVAL); } } @@ -479,7 +559,7 @@ ugenclose(dev_t dev, int flag, int mode, usb_proc_ptr p) sce = &sc->sc_endpoints[endpt][dir]; if (sce == NULL || sce->pipeh == NULL) continue; - DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", + DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", endpt, dir, sce)); usbd_abort_pipe(sce->pipeh); @@ -501,6 +581,7 @@ ugenclose(dev_t dev, int flag, int mode, usb_proc_ptr p) if (sce->ibuf != NULL) { free(sce->ibuf, M_USBDEV); sce->ibuf = NULL; + clfree(&sce->q); } } sc->sc_is_open[endpt] = 0; @@ -528,7 +609,9 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) if (endpt == USB_CONTROL_ENDPOINT) return (ENODEV); -#ifdef DIAGNOSTIC + if (sce == NULL) + return (EINVAL); + if (sce->edesc == NULL) { printf("ugenread: no edesc\n"); return (EIO); @@ -537,11 +620,10 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) printf("ugenread: no pipe\n"); return (EIO); } -#endif switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: - /* Block until activity occured. */ + /* Block until activity occurred. */ s = splusb(); while (sce->q.c_cc == 0) { if (flag & IO_NDELAY) { @@ -549,7 +631,7 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; - DPRINTFN(5, ("ugenread: sleep on %p\n", sc)); + DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); error = tsleep(sce, PCATCH, "ugenri", 0); DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (sc->sc_dying) @@ -586,8 +668,8 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) tn = n; err = usbd_bulk_transfer( xfer, sce->pipeh, - sce->state & UGEN_SHORT_OK ? - USBD_SHORT_XFER_OK : 0, + sce->state & UGEN_SHORT_OK ? + USBD_SHORT_XFER_OK : 0, sce->timeout, buf, &tn, "ugenrb"); if (err) { if (err == USBD_INTERRUPTED) @@ -613,7 +695,7 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; - DPRINTFN(5, ("ugenread: sleep on %p\n", sc)); + DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); error = tsleep(sce, PCATCH, "ugenri", 0); DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (sc->sc_dying) @@ -643,7 +725,7 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) splx(s); break; - + default: return (ENXIO); } @@ -676,7 +758,7 @@ ugen_do_write(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) usbd_xfer_handle xfer; usbd_status err; - DPRINTFN(5, ("%s: ugen_do_write: %d\n", USBDEVNAME(sc->sc_dev), endpt)); + DPRINTFN(5, ("%s: ugenwrite: %d\n", USBDEVNAME(sc->sc_dev), endpt)); if (sc->sc_dying) return (EIO); @@ -684,16 +766,17 @@ ugen_do_write(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) if (endpt == USB_CONTROL_ENDPOINT) return (ENODEV); -#ifdef DIAGNOSTIC + if (sce == NULL) + return (EINVAL); + if (sce->edesc == NULL) { - printf("ugen_do_write: no edesc\n"); + printf("ugenwrite: no edesc\n"); return (EIO); } if (sce->pipeh == NULL) { - printf("ugen_do_write: no pipe\n"); + printf("ugenwrite: no pipe\n"); return (EIO); } -#endif switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_BULK: @@ -704,12 +787,38 @@ ugen_do_write(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) error = uiomove(buf, n, uio); if (error) break; - DPRINTFN(1, ("ugen_do_write: transfer %d bytes\n", n)); - err = usbd_bulk_transfer(xfer, sce->pipeh, 0, + 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; @@ -748,7 +857,6 @@ ugen_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: sc->sc_dying = 1; @@ -766,10 +874,6 @@ USB_DETACH(ugen) int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; -#elif defined(__FreeBSD__) - int endptno; - dev_t dev; - struct vnode *vp; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) @@ -809,33 +913,8 @@ USB_DETACH(ugen) vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR); #elif defined(__FreeBSD__) /* destroy the device for the control endpoint */ - dev = makedev(UGEN_CDEV_MAJOR, UGENMINOR(USBDEVUNIT(sc->sc_dev), 0)); - vp = SLIST_FIRST(&dev->si_hlist); - if (vp) - VOP_REVOKE(vp, REVOKEALL); - destroy_dev(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. - */ - dev = makedev(UGEN_CDEV_MAJOR, - UGENMINOR(USBDEVUNIT(sc->sc_dev), endptno)); - vp = SLIST_FIRST(&dev->si_hlist); - if (vp) - VOP_REVOKE(vp, REVOKEALL); - - destroy_dev(dev); - } - } + destroy_dev(sc->dev); + ugen_destroy_devnodes(sc); #endif return (0); @@ -854,42 +933,45 @@ ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ugenintr: status=%d\n", status)); - usbd_clear_endpoint_stall_async(sce->pipeh); + 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", + 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); } - selwakeup(&sce->rsel); + selwakeuppri(&sce->rsel, PZERO); } Static void -ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, +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", req - sce->isoreqs, + DPRINTFN(5,("ugen_isoc_rintr: xfer %d, count=%d\n", + (int)(req - sce->isoreqs), count)); /* throw away oldest input if the buffer is full */ @@ -901,15 +983,25 @@ ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, count)); } - /* copy data to buffer */ - while (count > 0) { - n = min(count, sce->limit - sce->fill); - memcpy(sce->fill, req->dmabuf, n); + 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; + } - count -= 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, @@ -921,7 +1013,7 @@ ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce)); wakeup(sce); } - selwakeup(&sce->rsel); + selwakeuppri(&sce->rsel, PZERO); } Static usbd_status @@ -941,13 +1033,20 @@ ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno) 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; @@ -975,6 +1074,12 @@ ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno) sce->edesc = ed; sce->iface = iface; } + +#if defined(__FreeBSD__) + /* make the new devices */ + ugen_make_devnodes(sc); +#endif + return (0); } @@ -1020,7 +1125,7 @@ ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx) err = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface); if (err) - return (-1); + return (-1); return (usbd_get_interface_altindex(iface)); } @@ -1056,12 +1161,12 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, sce = &sc->sc_endpoints[endpt][IN]; if (sce == NULL) return (EINVAL); -#ifdef DIAGNOSTIC + if (sce->pipeh == NULL) { printf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n"); return (EIO); } -#endif + if (*(int *)addr) sce->state |= UGEN_SHORT_OK; else @@ -1071,12 +1176,6 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, sce = &sc->sc_endpoints[endpt][IN]; if (sce == NULL) return (EINVAL); -#ifdef DIAGNOSTIC - if (sce->pipeh == NULL) { - printf("ugenioctl: USB_SET_TIMEOUT, no pipe\n"); - return (EIO); - } -#endif sce->timeout = *(int *)addr; return (0); default: @@ -1113,7 +1212,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, break; case USB_GET_ALTINTERFACE: ai = (struct usb_alt_interface *)addr; - err = usbd_device2interface_handle(sc->sc_udev, + err = usbd_device2interface_handle(sc->sc_udev, ai->uai_interface_index, &iface); if (err) return (EINVAL); @@ -1126,7 +1225,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, if (!(flag & FWRITE)) return (EPERM); ai = (struct usb_alt_interface *)addr; - err = usbd_device2interface_handle(sc->sc_udev, + err = usbd_device2interface_handle(sc->sc_udev, ai->uai_interface_index, &iface); if (err) return (EINVAL); @@ -1187,7 +1286,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, 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, + edesc = usbd_find_edesc(cdesc, ed->ued_interface_index, alt, ed->ued_endpoint_index); if (edesc == NULL) { free(cdesc, M_TEMP); @@ -1215,18 +1314,14 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = UIO_READ; - uio.uio_td = p; -#if defined(__NetBSD__) || defined(__OpenBSD__) - error = uiomove((caddr_t)cdesc, len, &uio); -#elif defined(__FreeBSD__) + uio.uio_procp = p; error = uiomove((void *)cdesc, len, &uio); -#endif 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, + err = usbd_get_string_desc(sc->sc_udev, si->usd_string_index, si->usd_language_id, &si->usd_desc); if (err) return (EINVAL); @@ -1263,7 +1358,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = - ur->ucr_request.bmRequestType & UT_READ ? + ur->ucr_request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_td = p; ptr = malloc(len, M_TEMP, M_WAITOK); @@ -1273,8 +1368,9 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, goto ret; } } - err = usbd_do_request_flags(sc->sc_udev, &ur->ucr_request, - ptr, ur->ucr_flags, &ur->ucr_actlen); + 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; @@ -1293,7 +1389,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, } case USB_GET_DEVICEINFO: usbd_fill_deviceinfo(sc->sc_udev, - (struct usb_device_info *)addr); + (struct usb_device_info *)addr, 1); break; default: return (EINVAL); @@ -1334,7 +1430,7 @@ ugenpoll(dev_t dev, int events, usb_proc_ptr p) sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; if (sce == NULL) return (EINVAL); -#ifdef DIAGNOSTIC + if (!sce->edesc) { printf("ugenpoll: no edesc\n"); return (EIO); @@ -1343,7 +1439,7 @@ ugenpoll(dev_t dev, int events, usb_proc_ptr p) printf("ugenpoll: no pipe\n"); return (EIO); } -#endif + s = splusb(); switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: @@ -1363,12 +1459,12 @@ ugenpoll(dev_t dev, int events, usb_proc_ptr p) } 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 & + revents |= events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM); break; default: diff --git a/sys/dev/usbmisc/uhid/uhid.c b/sys/dev/usbmisc/uhid/uhid.c index 0b31ae5f50..11534a806c 100644 --- a/sys/dev/usbmisc/uhid/uhid.c +++ b/sys/dev/usbmisc/uhid/uhid.c @@ -1,6 +1,12 @@ -/* $NetBSD: uhid.c,v 1.38 2000/04/27 15:26:48 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/uhid.c,v 1.27.2.12 2002/11/06 20:23:50 joe Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/uhid/uhid.c,v 1.6 2003/08/07 21:17:14 dillon Exp $ */ +/* + * $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $ + * $FreeBSD: src/sys/dev/usb/uhid.c,v 1.65 2003/11/09 09:17:22 tanimura Exp $ + * $DragonFly: src/sys/dev/usbmisc/uhid/uhid.c,v 1.7 2003/12/30 01:01:47 dillon Exp $ + */ + +/* Also already merged from NetBSD: + * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -46,11 +52,16 @@ #include #include #include +#include #include +#if __FreeBSD_version >= 500000 +#include +#endif #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include +#include #elif defined(__FreeBSD__) #include #include @@ -60,8 +71,11 @@ #endif #include #include -#include +#if __FreeBSD_version >= 500014 +#include +#else #include +#endif #include #include #include @@ -70,13 +84,13 @@ #include #include +#include #include #include #include -#if defined(__FreeBSD__) && defined(__i386__) -#include -#endif +/* Report descriptor for broken Wacom Graphire */ +#include #ifdef USB_DEBUG #define DPRINTF(x) if (uhiddebug) logprintf x @@ -169,7 +183,7 @@ Static void uhid_intr(usbd_xfer_handle, usbd_private_handle, 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_proc_ptr); USB_DECLARE_DRIVER(uhid); @@ -177,12 +191,14 @@ 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); } @@ -196,7 +212,7 @@ USB_ATTACH(uhid) void *desc; usbd_status err; char devinfo[1024]; - + sc->sc_udev = uaa->device; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); @@ -216,7 +232,7 @@ USB_ATTACH(uhid) DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", - ed->bLength, ed->bDescriptorType, + ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR, UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", ed->bmAttributes & UE_XFERTYPE, @@ -231,14 +247,29 @@ USB_ATTACH(uhid) sc->sc_ep_addr = ed->bEndpointAddress; - desc = 0; - err = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USBDEV); + 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); @@ -266,7 +297,6 @@ uhid_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: sc->sc_dying = 1; @@ -282,8 +312,6 @@ USB_DETACH(uhid) int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; -#elif defined(__FreeBSD__) - struct vnode *vp; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) @@ -317,14 +345,11 @@ USB_DETACH(uhid) mn = self->dv_unit; vdevgone(maj, mn, mn, VCHR); #elif defined(__FreeBSD__) - vp = SLIST_FIRST(&sc->dev->si_hlist); - if (vp) - VOP_REVOKE(vp, REVOKEALL); - destroy_dev(sc->dev); #endif - free(sc->sc_repdesc, M_USBDEV); + if (sc->sc_repdesc) + free(sc->sc_repdesc, M_USBDEV); return (0); } @@ -337,7 +362,7 @@ uhid_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) #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 =")); @@ -352,21 +377,24 @@ uhid_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("uhid_intr: status=%d\n", status)); - sc->sc_state |= UHID_NEEDCLEAR; + 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)); + DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); wakeup(&sc->sc_q); } - selwakeup(&sc->sc_rsel); + 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); } } @@ -375,18 +403,6 @@ uhidopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct uhid_softc *sc; usbd_status err; -#if defined(__FreeBSD__) && defined(__i386__) - static int hid_opened; - - if (hid_opened == 0) { - int s; - s = splhigh(); - tty_imask |= bio_imask; - update_intr_masks(); - splx(s); - hid_opened = 1; - } -#endif USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc); @@ -408,14 +424,16 @@ uhidopen(dev_t dev, int flag, int mode, usb_proc_ptr p) 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, + 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); } @@ -446,6 +464,7 @@ uhidclose(dev_t dev, int flag, int mode, usb_proc_ptr p) free(sc->sc_ibuf, M_USBDEV); free(sc->sc_obuf, M_USBDEV); + sc->sc_ibuf = sc->sc_obuf = NULL; sc->sc_state &= ~UHID_OPEN; @@ -466,7 +485,7 @@ uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) 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) @@ -481,7 +500,7 @@ uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) return (EWOULDBLOCK); } sc->sc_state |= UHID_ASLP; - DPRINTFN(5, ("uhidread: sleep on %p\n", sc)); + DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); error = tsleep(&sc->sc_q, PCATCH, "uhidrea", 0); DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); if (sc->sc_dying) @@ -539,7 +558,7 @@ uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) usbd_status err; DPRINTFN(1, ("uhidwrite\n")); - + if (sc->sc_dying) return (EIO); @@ -600,8 +619,14 @@ uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, int flag, if (*(int *)addr) { if (sc->sc_async != NULL) return (EBUSY); +#if defined(__DragonFly__) + sc->sc_async = p->td_proc; +#elif __FreeBSD_version >= 500000 sc->sc_async = p->td_proc; - DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", p)); +#else + sc->sc_async = p; +#endif + DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", sc->sc_async)); } else sc->sc_async = NULL; break; @@ -682,6 +707,10 @@ uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, int flag, return (EIO); break; + case USB_GET_REPORT_ID: + *(int *)addr = 0; /* XXX: we only support reportid 0? */ + break; + default: return (EINVAL); } diff --git a/sys/dev/usbmisc/ukbd/ukbd.c b/sys/dev/usbmisc/ukbd/ukbd.c index a86223e5cf..de82d95c17 100644 --- a/sys/dev/usbmisc/ukbd/ukbd.c +++ b/sys/dev/usbmisc/ukbd/ukbd.c @@ -1,5 +1,7 @@ -/* $FreeBSD: src/sys/dev/usb/ukbd.c,v 1.24.2.6 2002/11/06 20:23:50 joe Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/ukbd/ukbd.c,v 1.3 2003/08/07 21:17:14 dillon Exp $ */ +/* + * $FreeBSD: src/sys/dev/usb/ukbd.c,v 1.45 2003/10/04 21:41:01 joe Exp $ + * $DragonFly: src/sys/dev/usbmisc/ukbd/ukbd.c,v 1.4 2003/12/30 01:01:47 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -39,7 +41,7 @@ */ /* - * HID spec: http://www.usb.org/developers/data/usbhid10.pdf + * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf */ #include "opt_kbd.h" @@ -51,10 +53,17 @@ #include #include #include -#include #include +#if __FreeBSD_version >= 500000 +#include +#else +#include +#endif +#if __FreeBSD_version >= 500014 +#include +#else #include -#include +#endif #include #include @@ -234,8 +243,6 @@ ukbd_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0); -#include -#include #define UKBD_DEFAULT 0 @@ -270,7 +277,7 @@ Static struct { }; #define NN 0 /* no translation */ -/* +/* * Translate USB keycodes to AT keyboard scancodes. */ /* @@ -427,7 +434,7 @@ 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. @@ -568,7 +575,7 @@ ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) 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? */ @@ -603,12 +610,12 @@ ukbd_enable_intr(keyboard_t *kbd, int on, usbd_intr_t *func) /* 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, + err = usbd_open_pipe_intr(state->ks_iface, state->ks_ep_addr, USBD_SHORT_XFER_OK, &state->ks_intrpipe, kbd, - &state->ks_ndata, + &state->ks_ndata, sizeof(state->ks_ndata), func, USBD_DEFAULT_INTERVAL); if (err) @@ -687,28 +694,25 @@ Static int ukbd_interrupt(keyboard_t *kbd, void *arg) { usbd_status status = (usbd_status)arg; - ukbd_state_t *state = (ukbd_state_t *)kbd->kb_data; - struct ukbd_data *ud = &state->ks_ndata; + ukbd_state_t *state; + struct ukbd_data *ud; struct timeval tv; u_long now; int mod, omod; int key, c; int i, j; -#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; \ - } - 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)); - usbd_clear_endpoint_stall_async(state->ks_intrpipe); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(state->ks_intrpipe); return 0; } @@ -718,14 +722,21 @@ ukbd_interrupt(keyboard_t *kbd, void *arg) 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) != + if (( mod & ukbd_mods[i].mask) != (omod & ukbd_mods[i].mask)) - ADDKEY1(ukbd_mods[i].key | - (mod & ukbd_mods[i].mask + ADDKEY1(ukbd_mods[i].key | + (mod & ukbd_mods[i].mask ? KEY_PRESS : KEY_RELEASE)); } @@ -744,7 +755,7 @@ ukbd_interrupt(keyboard_t *kbd, void *arg) rfound: ; } - + /* Check for pressed keys. */ for (i = 0; i < NKEYCODE; i++) { key = ud->keycode[i]; @@ -837,7 +848,7 @@ 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. */ @@ -904,13 +915,13 @@ ukbd_read(keyboard_t *kbd, int wait) usbcode & KEY_RELEASE); if (scancode & SCAN_PREFIX) { if (scancode & SCAN_PREFIX_CTL) { - state->ks_buffered_char[0] = + 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] = + state->ks_buffered_char[0] = 0x2a | (scancode & SCAN_RELEASE); /* Shift */ - state->ks_buffered_char[1] = + state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX_SHIFT; } else { state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX; @@ -1000,12 +1011,12 @@ next_code: usbcode & KEY_RELEASE); if (scancode & SCAN_PREFIX) { if (scancode & SCAN_PREFIX_CTL) { - state->ks_buffered_char[0] = + 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] = + state->ks_buffered_char[0] = 0x2a | (scancode & SCAN_RELEASE); state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX_SHIFT; @@ -1167,7 +1178,7 @@ ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) state->ks_state &= ~LOCK_MASK; state->ks_state |= KBD_LED_VAL(kbd); } - /* FALL THROUGH */ + /* FALLTHROUGH */ case K_RAW: case K_CODE: if (state->ks_mode != *(int *)arg) { @@ -1242,7 +1253,7 @@ ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) case PIO_KEYMAPENT: /* set keyboard translation table entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ state->ks_accents = 0; - /* FALL THROUGH */ + /* FALLTHROUGH */ default: splx(s); return genkbd_commonioctl(kbd, cmd, arg); @@ -1314,19 +1325,21 @@ 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(state->ks_iface, on); + usbd_set_polling(dev, on); ++state->ks_polling; } else { --state->ks_polling; if (state->ks_polling == 0) - usbd_set_polling(state->ks_iface, on); + usbd_set_polling(dev, on); } splx(s); return 0; @@ -1338,7 +1351,7 @@ 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; @@ -1358,7 +1371,7 @@ 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; @@ -1431,10 +1444,10 @@ Static int keycode2scancode(int keycode, int shift, int up) { static int scan[] = { - 0x1c, 0x1d, 0x35, + 0x1c, 0x1d, 0x35, 0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */ - 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f, - 0x50, 0x51, 0x52, 0x53, + 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x46, /* XXX Pause/Break */ 0x5b, 0x5c, 0x5d, }; diff --git a/sys/dev/usbmisc/ulpt/ulpt.c b/sys/dev/usbmisc/ulpt/ulpt.c index bd5571b781..88aea9d639 100644 --- a/sys/dev/usbmisc/ulpt/ulpt.c +++ b/sys/dev/usbmisc/ulpt/ulpt.c @@ -1,7 +1,7 @@ /* - * $NetBSD: ulpt.c,v 1.29 1999/11/17 23:00:50 augustss Exp $ - * $FreeBSD: src/sys/dev/usb/ulpt.c,v 1.26.2.14 2003/06/22 13:54:30 iedowse Exp $ - * $DragonFly: src/sys/dev/usbmisc/ulpt/ulpt.c,v 1.6 2003/12/29 06:42:17 dillon Exp $ + * $NetBSD: ulpt.c,v 1.55 2002/10/23 09:14:01 jdolecek Exp $ + * $FreeBSD: src/sys/dev/usb/ulpt.c,v 1.59 2003/09/28 20:48:13 phk Exp $ + * $DragonFly: src/sys/dev/usbmisc/ulpt/ulpt.c,v 1.7 2003/12/30 01:01:47 dillon Exp $ */ /* @@ -42,11 +42,9 @@ */ /* - * Printer Class spec: http://www.usb.org/developers/data/usbprn10.pdf + * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF */ -/* XXX Note in the manpage the ULPT_NOPRIME version of the printer */ - #include #include #include @@ -55,18 +53,20 @@ #include #include #elif defined(__FreeBSD__) +#include #include #include #endif #include #include -#include #include #include #include #include #include +#include +#include #define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */ #define STEP hz/4 @@ -100,8 +100,15 @@ struct ulpt_softc { usbd_device_handle sc_udev; /* device */ usbd_interface_handle sc_iface; /* interface */ int sc_ifaceno; - usbd_pipe_handle sc_bulkpipe; /* bulk pipe */ - int sc_bulk; + + int sc_out; + usbd_pipe_handle sc_out_pipe; /* bulk out pipe */ + + int sc_in; + usbd_pipe_handle sc_in_pipe; /* bulk in pipe */ + usbd_xfer_handle sc_in_xfer1; + usbd_xfer_handle sc_in_xfer2; + u_char sc_junk[64]; /* somewhere to dump input */ u_char sc_state; #define ULPT_OPEN 0x01 /* device is open */ @@ -120,7 +127,17 @@ struct ulpt_softc { #endif }; -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) +dev_type_open(ulptopen); +dev_type_close(ulptclose); +dev_type_write(ulptwrite); +dev_type_ioctl(ulptioctl); + +const struct cdevsw ulpt_cdevsw = { + ulptopen, ulptclose, noread, ulptwrite, ulptioctl, + nostop, notty, nopoll, nommap, nokqfilter, +}; +#elif defined(__OpenBSD__) cdev_decl(ulpt); #elif defined(__FreeBSD__) Static d_open_t ulptopen; @@ -157,7 +174,9 @@ int ulpt_status(struct ulpt_softc *); void ulpt_reset(struct ulpt_softc *); int ulpt_statusmsg(u_char, struct ulpt_softc *); +#if 0 void ieee1284_print_id(char *); +#endif #define ULPTUNIT(s) (minor(s) & 0x1f) #define ULPTFLAGS(s) (minor(s) & 0xe0) @@ -169,7 +188,7 @@ 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); @@ -178,7 +197,8 @@ USB_MATCH(ulpt) id->bInterfaceClass == UICLASS_PRINTER && id->bInterfaceSubClass == UISUBCLASS_PRINTER && (id->bInterfaceProtocol == UIPROTO_PRINTER_UNI || - id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) + id->bInterfaceProtocol == UIPROTO_PRINTER_BI || + id->bInterfaceProtocol == UIPROTO_PRINTER_1284)) return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); return (UMATCH_NONE); } @@ -188,41 +208,105 @@ 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 *id = usbd_get_interface_descriptor(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; - usbd_status err; - + 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, id->bInterfaceClass, id->bInterfaceSubClass); - - /* Figure out which endpoint is the bulk out endpoint. */ - ed = usbd_interface2endpoint_descriptor(iface, 0); - if (ed == NULL) - goto nobulk; - if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT || - (ed->bmAttributes & UE_XFERTYPE) != UE_BULK) { - /* In case we are using a bidir protocol... */ - ed = usbd_interface2endpoint_descriptor(iface, 1); - if (ed == NULL) - goto nobulk; - if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT || - (ed->bmAttributes & UE_XFERTYPE) != UE_BULK) - goto nobulk; + 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; + } } - sc->sc_bulk = ed->bEndpointAddress; - DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_bulk)); - sc->sc_iface = iface; - err = usbd_interface2device_handle(iface, &sc->sc_udev); - if (err) { + 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 /* @@ -241,7 +325,7 @@ USB_ATTACH(ulpt) 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); + &alen, USBD_DEFAULT_TIMEOUT); if (err) { printf("%s: cannot get device id\n", USBDEVNAME(sc->sc_dev)); } else if (alen <= 2) { @@ -268,12 +352,10 @@ USB_ATTACH(ulpt) UID_ROOT, GID_OPERATOR, 0644, "unlpt%d", device_get_unit(self)); #endif - USB_ATTACH_SUCCESS_RETURN; + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + USBDEV(sc->sc_dev)); - nobulk: - printf("%s: could not find bulk endpoint\n", USBDEVNAME(sc->sc_dev)); - sc->sc_dying = 1; - USB_ATTACH_ERROR_RETURN; + USB_ATTACH_SUCCESS_RETURN; } #if defined(__NetBSD__) || defined(__OpenBSD__) @@ -285,7 +367,6 @@ ulpt_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: sc->sc_dying = 1; @@ -301,8 +382,6 @@ USB_DETACH(ulpt) int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; -#elif defined(__FreeBSD__) - struct vnode *vp; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) @@ -312,8 +391,10 @@ USB_DETACH(ulpt) #endif sc->sc_dying = 1; - if (sc->sc_bulkpipe != NULL) - usbd_abort_pipe(sc->sc_bulkpipe); + 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) { @@ -325,25 +406,25 @@ USB_DETACH(ulpt) #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__) - vp = SLIST_FIRST(&sc->dev->si_hlist); - if (vp) - VOP_REVOKE(vp, REVOKEALL); - vp = SLIST_FIRST(&sc->dev_noprime->si_hlist); - if (vp) - VOP_REVOKE(vp, REVOKEALL); - 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); } @@ -373,14 +454,45 @@ ulpt_reset(struct ulpt_softc *sc) usb_device_request_t req; DPRINTFN(1, ("ulpt_reset\n")); - req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SOFT_RESET; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_ifaceno); USETW(req.wLength, 0); - (void)usbd_do_request(sc->sc_udev, &req, 0); + + /* + * 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 */ + } } +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); +} + +int ulptusein = 1; + /* * Reset the printer, then wait until it's selected and not busy. */ @@ -411,44 +523,93 @@ ulptopen(dev_t dev, int flag, int mode, usb_proc_ptr p) "\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; - return (ENXIO); + 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; - return (EBUSY); + goto done; } /* wait 1/4 second, give up if we get a signal */ error = tsleep((caddr_t)sc, PCATCH, "ulptop", STEP); if (error != EWOULDBLOCK) { sc->sc_state = 0; - return (error); + goto done; } if (sc->sc_dying) { + error = ENXIO; sc->sc_state = 0; - return (ENXIO); + goto done; } } - err = usbd_open_pipe(sc->sc_iface, sc->sc_bulk, 0, &sc->sc_bulkpipe); + err = usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe); if (err) { sc->sc_state = 0; - return (EIO); + error = EIO; + goto done; + } + + 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; + usbd_close_pipe(sc->sc_out_pipe); + sc->sc_out_pipe = NULL; + sc->sc_state = 0; + goto done; + } + sc->sc_in_xfer1 = usbd_alloc_xfer(sc->sc_udev); + sc->sc_in_xfer2 = usbd_alloc_xfer(sc->sc_udev); + if (sc->sc_in_xfer1 == NULL || sc->sc_in_xfer2 == NULL) { + error = ENOMEM; + if (sc->sc_in_xfer1 != NULL) { + usbd_free_xfer(sc->sc_in_xfer1); + sc->sc_in_xfer1 = NULL; + } + if (sc->sc_in_xfer2 != NULL) { + usbd_free_xfer(sc->sc_in_xfer2); + sc->sc_in_xfer2 = NULL; + } + usbd_close_pipe(sc->sc_out_pipe); + sc->sc_out_pipe = NULL; + usbd_close_pipe(sc->sc_in_pipe); + sc->sc_in_pipe = NULL; + sc->sc_state = 0; + goto done; + } + usbd_setup_xfer(sc->sc_in_xfer1, sc->sc_in_pipe, sc, + sc->sc_junk, sizeof sc->sc_junk, USBD_SHORT_XFER_OK, + USBD_NO_TIMEOUT, ulpt_input); + usbd_setup_xfer(sc->sc_in_xfer2, sc->sc_in_pipe, sc, + sc->sc_junk, sizeof sc->sc_junk, USBD_SHORT_XFER_OK, + USBD_NO_TIMEOUT, ulpt_input); + usbd_transfer(sc->sc_in_xfer1); /* ignore failed start */ } sc->sc_state = ULPT_OPEN; - DPRINTF(("ulptopen: done\n")); - return (0); +done: + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + + DPRINTF(("ulptopen: done, error=%d\n", error)); + return (error); } int @@ -481,8 +642,23 @@ ulptclose(dev_t dev, int flag, int mode, usb_proc_ptr p) /* We are being forced to close before the open completed. */ return (0); - usbd_close_pipe(sc->sc_bulkpipe); - sc->sc_bulkpipe = 0; + if (sc->sc_out_pipe != NULL) { + usbd_close_pipe(sc->sc_out_pipe); + sc->sc_out_pipe = 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_xfer1 != NULL) { + usbd_free_xfer(sc->sc_in_xfer1); + sc->sc_in_xfer1 = NULL; + } + if (sc->sc_in_xfer2 != NULL) { + usbd_free_xfer(sc->sc_in_xfer2); + sc->sc_in_xfer2 = NULL; + } + } sc->sc_state = 0; @@ -514,7 +690,7 @@ ulpt_do_write(struct ulpt_softc *sc, struct uio *uio, int flags) if (error) break; DPRINTFN(1, ("ulptwrite: transfer %d bytes\n", n)); - err = usbd_bulk_transfer(xfer, sc->sc_bulkpipe, USBD_NO_COPY, + 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)); @@ -561,7 +737,7 @@ ulptioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) #if 0 /* XXX This does not belong here. */ /* - * Print select parts of a IEEE 1284 device ID. + * Print select parts of an IEEE 1284 device ID. */ void ieee1284_print_id(char *str) diff --git a/sys/dev/usbmisc/umass/umass.c b/sys/dev/usbmisc/umass/umass.c index d814ff14bb..c50ba64e31 100644 --- a/sys/dev/usbmisc/umass/umass.c +++ b/sys/dev/usbmisc/umass/umass.c @@ -24,18 +24,22 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/umass.c,v 1.11.2.22 2003/12/22 20:30:25 sanpei Exp $ - * $DragonFly: src/sys/dev/usbmisc/umass/umass.c,v 1.5 2003/12/29 06:42:19 dillon Exp $ * $NetBSD: umass.c,v 1.28 2000/04/02 23:46:53 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/umass.c,v 1.96 2003/12/19 12:19:11 sanpei Exp $ + * $DragonFly: src/sys/dev/usbmisc/umass/umass.c,v 1.6 2003/12/30 01:01:47 dillon Exp $ */ /* - * Ported to NetBSD by Lennart Augustsson . - * Parts of the code written my Jason R. Thorpe . + * Universal Serial Bus Mass Storage Class specs: + * http://www.usb.org/developers/data/devclass/usbmassover_11.pdf + * http://www.usb.org/developers/data/devclass/usbmassbulk_10.pdf + * http://www.usb.org/developers/data/devclass/usbmass-cbi10.pdf + * http://www.usb.org/developers/data/devclass/usbmass-ufi10.pdf */ /* - * The PDF documentation can be found at http://www.usb.org/developers/ + * Ported to NetBSD by Lennart Augustsson . + * Parts of the code written my Jason R. Thorpe . */ /* @@ -102,7 +106,6 @@ #include #include #include -#include #include #include @@ -116,7 +119,6 @@ #include #include -#include #include #ifdef USB_DEBUG @@ -154,37 +156,17 @@ SYSCTL_INT(_hw_usb_umass, OID_AUTO, debug, CTLFLAG_RW, #define DEVNAME_SIM "umass-sim" #define UMASS_MAX_TRANSFER_SIZE 65536 -#define UMASS_DEFAULT_TRANSFER_SPEED 150 /* in kb/s, conservative est. */ +#define UMASS_DEFAULT_TRANSFER_SPEED 1000 #define UMASS_FLOPPY_TRANSFER_SPEED 20 -#define UMASS_ZIP100_TRANSFER_SPEED 650 #define UMASS_TIMEOUT 5000 /* msecs */ /* CAM specific definitions */ -/* We only have one bus */ -#define UMASS_SCSI_BUS 0 - -/* All USB drives are 'connected' to one SIM (SCSI controller). umass3 - * ends up being target 3 on that SIM. When a request for target 3 - * comes in we fetch the softc with devclass_get_softc(target_id). - * - * The SIM is the highest target number. This makes sure that umass0 corresponds - * to target 0 on the USB SCSI bus. - */ -#ifndef USB_DEBUG -#define UMASS_SCSIID_MAX 32 /* maximum number of drives expected */ -#else -/* while debugging avoid unnecessary clutter in the output at umass_cam_rescan - * (XPT_PATH_INQ) - */ -#define UMASS_SCSIID_MAX 3 /* maximum number of drives expected */ -#endif +#define UMASS_SCSIID_MAX 1 /* maximum number of drives expected */ #define UMASS_SCSIID_HOST UMASS_SCSIID_MAX -#define UMASS_SIM_UNIT 0 /* we use one sim for all drives */ - -#define MS_TO_TICKS(ms) ((ms) * hz / 1000) +#define MS_TO_TICKS(ms) ((ms) * hz / 1000) /* Bulk-Only features */ @@ -259,7 +241,7 @@ typedef void (*transfer_cb_f) (struct umass_softc *sc, void *priv, 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, + void *cmd, int cmdlen, void *data, int datalen, int dir, transfer_cb_f cb, void *priv); typedef void (*wire_state_f) (usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err); @@ -269,48 +251,19 @@ typedef int (*command_transform_f) (struct umass_softc *sc, unsigned char **rcmd, int *rcmdlen); -/* the per device structure */ -struct umass_softc { - USBBASEDEVICE sc_dev; /* base device */ - usbd_device_handle sc_udev; /* USB device */ +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 - unsigned char flags; /* various device flags */ -# define UMASS_FLAGS_GONE 0x01 /* devices is no more */ - - unsigned char drive; -# define DRIVE_GENERIC 0 /* use defaults for this one */ -# define ZIP_100 1 /* to be used for quirks */ -# define ZIP_250 2 -# define SHUTTLE_EUSB 3 -# define INSYSTEM_USBCABLE 4 - - unsigned char quirks; - /* The drive does not support Test Unit Ready. Convert to - * Start Unit - * Y-E Data, Zip 100 - */ -# define NO_TEST_UNIT_READY 0x01 - /* 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. - * Y-E Data - */ -# define RS_NO_CLEAR_UA 0x02 - /* The drive does not support START STOP. - * Shuttle E-USB - */ -# define NO_START_STOP 0x04 - /* Don't ask for full inquiry data (255b). */ -# define FORCE_SHORT_INQUIRY 0x08 - /* The device uses a weird CSWSIGNATURE. */ -# define WRONG_CSWSIG 0x10 - /* The device can't count and gets the residue of transfers wrong */ -# define IGNORE_RESIDUE 0x80 - - - unsigned int proto; -# define UMASS_PROTO_UNKNOWN 0x0000 /* unknown protocol */ + /* 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 @@ -321,6 +274,156 @@ struct umass_softc { # 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_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_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_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_OLYMPUS, USB_PRODUCT_OLYMPUS_C1, RID_WILDCARD, + UMASS_PROTO_SCSI | UMASS_PROTO_BBB, + WRONG_CSWSIG + }, + { 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_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_CBI_I, + NO_QUIRKS + }, + { 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 */ @@ -347,12 +450,12 @@ struct umass_softc { * 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_cbl_t cbl; /* command block */ umass_cbi_sbl_t sbl; /* status block */ /* generic variables for transfers in progress */ @@ -374,7 +477,7 @@ struct umass_softc { # 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 @@ -391,31 +494,32 @@ struct umass_softc { int transfer_dir; /* data direction */ void *transfer_data; /* data buffer */ int transfer_datalen; /* (maximum) length */ - int transfer_actlen; /* actual 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_IDLE 0 -# define TSTATE_BBB_COMMAND 1 /* CBW transfer */ -# define TSTATE_BBB_DATA 2 /* Data transfer */ -# define TSTATE_BBB_DCLEAR 3 /* clear endpt stall */ -# define TSTATE_BBB_STATUS1 4 /* clear endpt stall */ -# define TSTATE_BBB_SCLEAR 5 /* clear endpt stall */ -# define TSTATE_BBB_STATUS2 6 /* CSW transfer */ -# define TSTATE_BBB_RESET1 7 /* reset command */ -# define TSTATE_BBB_RESET2 8 /* in clear stall */ -# define TSTATE_BBB_RESET3 9 /* out clear stall */ -# define TSTATE_CBI_COMMAND 10 /* command transfer */ -# define TSTATE_CBI_DATA 11 /* data transfer */ -# define TSTATE_CBI_STATUS 12 /* status transfer */ -# define TSTATE_CBI_DCLEAR 13 /* clear ep stall */ -# define TSTATE_CBI_SCLEAR 14 /* clear ep stall */ -# define TSTATE_CBI_RESET1 15 /* reset command */ -# define TSTATE_CBI_RESET2 16 /* in clear stall */ -# define TSTATE_CBI_RESET3 17 /* out clear stall */ -# define TSTATE_STATES 18 /* # of states above */ +# 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 */ @@ -424,13 +528,13 @@ struct umass_softc { struct scsi_sense cam_scsi_sense; struct scsi_sense cam_scsi_test_unit_ready; - int transfer_speed; /* in kb/s */ 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", @@ -453,8 +557,11 @@ char *states[TSTATE_STATES+1] = { }; #endif -struct cam_sim *umass_sim; /* SCSI Interface Module */ - +/* 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); @@ -473,7 +580,7 @@ Static usbd_status umass_setup_transfer (struct umass_softc *sc, 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, + 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, @@ -492,7 +599,7 @@ Static void umass_bbb_state (usbd_xfer_handle xfer, 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, @@ -518,12 +625,11 @@ Static void umass_cam_quirk_cb (struct umass_softc *sc, void *priv, Static void umass_cam_rescan_callback (struct cam_periph *periph,union ccb *ccb); -Static void umass_cam_rescan (struct umass_softc *sc); +Static void umass_cam_rescan (void *addr); -Static int umass_cam_attach_sim (void); +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 (void); -Static int umass_cam_detach (struct umass_softc *sc); +Static int umass_cam_detach_sim (struct umass_softc *sc); /* SCSI specific functions */ @@ -532,13 +638,13 @@ Static int umass_scsi_transform (struct umass_softc *sc, unsigned char **rcmd, int *rcmdlen); /* UFI specific functions */ -#define UFI_COMMAND_LENGTH 12 /* UFI commands are always 12b */ +#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 12b */ +#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); @@ -550,10 +656,9 @@ Static int umass_rbc_transform (struct umass_softc *sc, #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_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 @@ -568,8 +673,8 @@ MODULE_DEPEND(umass, cam, 1,1,1); /* * Match the device we are seeing with the devices supported. Fill in the - * proto and drive fields in the softc accordingly. - * This function is called from both probe and attach. + * description in the softc accordingly. This function is called from both + * probe and attach. */ Static int @@ -578,60 +683,18 @@ umass_match_proto(struct umass_softc *sc, usbd_interface_handle iface, { usb_device_descriptor_t *dd; usb_interface_descriptor_t *id; + int i; + int found = 0; sc->sc_udev = udev; - - /* - * Fill in sc->drive and sc->proto and return a match - * value if both are determined and 0 otherwise. - */ - - sc->drive = DRIVE_GENERIC; - sc->proto = UMASS_PROTO_UNKNOWN; - sc->transfer_speed = UMASS_DEFAULT_TRANSFER_SPEED; + sc->proto = 0; + sc->quirks = 0; dd = usbd_get_device_descriptor(udev); - /* XXX ATAPI support is untested. Don't use it for the moment */ - if (UGETW(dd->idVendor) == USB_VENDOR_SHUTTLE - && UGETW(dd->idProduct) == USB_PRODUCT_SHUTTLE_EUSB) { - sc->drive = SHUTTLE_EUSB; -#if CBI_I - sc->proto = UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I; -#else - sc->proto = UMASS_PROTO_ATAPI | UMASS_PROTO_CBI; -#endif - sc->quirks |= NO_TEST_UNIT_READY | NO_START_STOP; - return(UMATCH_VENDOR_PRODUCT); - } - - if (UGETW(dd->idVendor) == USB_VENDOR_INSYSTEM - && UGETW(dd->idProduct) == USB_PRODUCT_INSYSTEM_USBCABLE) { - sc->drive = INSYSTEM_USBCABLE; - sc->proto = UMASS_PROTO_ATAPI | UMASS_PROTO_CBI; - sc->quirks |= NO_TEST_UNIT_READY | NO_START_STOP; - return(UMATCH_VENDOR_PRODUCT); - } - - if (UGETW(dd->idVendor) == USB_VENDOR_SIGMATEL && - UGETW(dd->idProduct) == USB_PRODUCT_SIGMATEL_I_BEAD100) { - /* XXX Really need SHUTTLE_INIT quirk from FreeBSD-current */ - sc->drive = SHUTTLE_EUSB; - } - - /* - * The Pentax Optio cameras require RS_NO_CLEAR_UA - * PR: kern/46369, kern/50271 + /* An entry specifically for Y-E Data devices as they don't fit in the + * device description table. */ - if (UGETW(dd->idVendor) == USB_VENDOR_ASAHIOPTICAL) { - sc->quirks |= RS_NO_CLEAR_UA; - } - - if (UGETW(dd->idVendor) == USB_VENDOR_FUJIPHOTO - && UGETW(dd->idProduct) == USB_PRODUCT_FUJIPHOTO_MASS0100) { - sc->quirks |= RS_NO_CLEAR_UA; - } - if (UGETW(dd->idVendor) == USB_VENDOR_YEDATA && UGETW(dd->idProduct) == USB_PRODUCT_YEDATA_FLASHBUSTERU) { @@ -641,11 +704,7 @@ umass_match_proto(struct umass_softc *sc, usbd_interface_handle iface, if (UGETW(dd->bcdDevice) < 0x128) { sc->proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI; } else { -#if CBI_I sc->proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI_I; -#else - sc->proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI; -#endif } /* @@ -655,75 +714,48 @@ umass_match_proto(struct umass_softc *sc, usbd_interface_handle iface, if (UGETW(dd->bcdDevice) <= 0x128) sc->quirks |= NO_TEST_UNIT_READY; - sc->quirks |= RS_NO_CLEAR_UA; - sc->transfer_speed = UMASS_FLOPPY_TRANSFER_SPEED; + sc->quirks |= RS_NO_CLEAR_UA | FLOPPY_SPEED; return(UMATCH_VENDOR_PRODUCT); } - id = usbd_get_interface_descriptor(iface); - if (id == NULL || id->bInterfaceClass != UICLASS_MASS) - return(UMATCH_NONE); - - if (UGETW(dd->idVendor) == USB_VENDOR_SONY - && id->bInterfaceSubClass==0xff) { - - /* Sony DSC devices set the sub class to 0xff - * instead of 1 (RBC). Fix that here. - */ - id->bInterfaceSubClass = 1; - - /* They also should be able to do higher speed. - */ - sc->transfer_speed = 500; + /* 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 */ + } } - if (UGETW(dd->idVendor) == USB_VENDOR_OLYMPUS && - UGETW(dd->idProduct) == USB_PRODUCT_OLYMPUS_C1) { - /* - * The Olympus C-1 camera uses a different command-status - * signature. - */ - sc->quirks |= WRONG_CSWSIG; - } - if (UGETW(dd->idVendor) == USB_VENDOR_GENESYS && - (UGETW(dd->idProduct) == USB_PRODUCT_GENESYS_GL641USB2IDE || - UGETW(dd->idProduct) == USB_PRODUCT_GENESYS_GL641USB)) { - sc->quirks |= FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE; - } - if (UGETW(dd->idVendor) == USB_VENDOR_MELCO && - UGETW(dd->idProduct) == USB_PRODUCT_MELCO_DUBPXXG) { - sc->quirks |= FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE; - } + /* Check for a standards compliant device */ - if (UGETW(dd->idVendor) == USB_VENDOR_MSYSTEMS && - UGETW(dd->idProduct) == USB_PRODUCT_MSYSTEMS_DISKONKEY2) { - sc->proto = UMASS_PROTO_ATAPI | UMASS_PROTO_BBB; - } - /* Logitec DVD multi plus unit */ - if (UGETW(dd->idVendor) == USB_VENDOR_LOGITEC && - UGETW(dd->idProduct) == USB_PRODUCT_LOGITEC_LDR_H443U2) { - sc->proto = UMASS_PROTO_SCSI; - } - if (UGETW(dd->idVendor) == USB_VENDOR_PANASONIC && - UGETW(dd->idProduct) == USB_PRODUCT_PANASONIC_KXLCB20AN) { - sc->proto = UMASS_PROTO_SCSI | UMASS_PROTO_BBB; - } - if (UGETW(dd->idVendor) == USB_VENDOR_PANASONIC && - UGETW(dd->idProduct) == USB_PRODUCT_PANASONIC_KXLCB35AN) { - sc->proto = UMASS_PROTO_SCSI | UMASS_PROTO_BBB; - } - if (UGETW(dd->idVendor) == USB_VENDOR_PNY && - UGETW(dd->idProduct) == USB_PRODUCT_PNY_ATTACHE) { - sc->proto = UMASS_PROTO_SCSI | UMASS_PROTO_BBB; - sc->quirks |= IGNORE_RESIDUE; - } + 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->transfer_speed = UMASS_FLOPPY_TRANSFER_SPEED; sc->proto |= UMASS_PROTO_UFI; break; case UISUBCLASS_RBC: @@ -731,7 +763,6 @@ umass_match_proto(struct umass_softc *sc, usbd_interface_handle iface, break; case UISUBCLASS_SFF8020I: case UISUBCLASS_SFF8070I: - /* XXX ATAPI support is untested. Don't use it for the moment */ sc->proto |= UMASS_PROTO_ATAPI; break; default: @@ -745,20 +776,11 @@ umass_match_proto(struct umass_softc *sc, usbd_interface_handle iface, sc->proto |= UMASS_PROTO_CBI; break; case UIPROTO_MASS_CBI_I: -#if CBI_I sc->proto |= UMASS_PROTO_CBI_I; -#else - sc->proto |= UMASS_PROTO_CBI; -#endif break; case UIPROTO_MASS_BBB_OLD: - sc->proto |= UMASS_PROTO_BBB; - break; case UIPROTO_MASS_BBB: - sc->drive = ZIP_100; sc->proto |= UMASS_PROTO_BBB; - sc->transfer_speed = UMASS_ZIP100_TRANSFER_SPEED; - sc->quirks |= NO_TEST_UNIT_READY; break; default: DPRINTF(UDMASS_GEN, ("%s: Unsupported wire protocol %d\n", @@ -806,9 +828,9 @@ USB_ATTACH(umass) (void) umass_match_proto(sc, sc->iface, uaa->device); id = usbd_get_interface_descriptor(sc->iface); - printf("%s: %s", USBDEVNAME(sc->sc_dev), devinfo); + printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); #ifdef USB_DEBUG - printf(", "); + printf("%s: ", USBDEVNAME(sc->sc_dev)); switch (sc->proto&UMASS_PROTO_COMMAND) { case UMASS_PROTO_SCSI: printf("SCSI"); @@ -831,19 +853,29 @@ USB_ATTACH(umass) case UMASS_PROTO_BBB: printf("Bulk-Only"); break; - case UMASS_PROTO_CBI: /* uses Comand/Bulk pipes */ + 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 - printf("\n"); - if (sc->drive == INSYSTEM_USBCABLE) { + if (sc->quirks & ALT_IFACE_1) { err = usbd_set_interface(0, 1); if (err) { DPRINTF(UDMASS_USB, ("%s: could not switch to " @@ -942,7 +974,7 @@ USB_ATTACH(umass) } /* initialisation of generic part */ - sc->transfer_state = TSTATE_IDLE; + sc->transfer_state = TSTATE_ATTACH; /* request a sufficient number of xfer handles */ for (i = 0; i < XFER_NR; i++) { @@ -960,13 +992,13 @@ USB_ATTACH(umass) sc->reset = umass_bbb_reset; sc->transfer = umass_bbb_transfer; sc->state = umass_bbb_state; - } else if ((sc->proto & UMASS_PROTO_CBI) || (sc->proto & UMASS_PROTO_CBI_I)) { + } 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\n", + panic("%s:%d: Unknown proto 0x%02x", __FILE__, __LINE__, sc->proto); #endif } @@ -981,13 +1013,13 @@ USB_ATTACH(umass) sc->transform = umass_rbc_transform; #ifdef USB_DEBUG else - panic("No transformation defined for command proto 0x%02x\n", + 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->drive == SHUTTLE_EUSB) + if (sc->quirks & SHUTTLE_INIT) umass_init_shuttle(sc); /* Get the maximum LUN supported by the device. @@ -1005,27 +1037,25 @@ USB_ATTACH(umass) sc->cam_scsi_sense.opcode = REQUEST_SENSE; sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY; - /* If this is the first device register the SIM */ - if (umass_sim == NULL) { - err = umass_cam_attach_sim(); - if (err) { - umass_detach(self); - USB_ATTACH_ERROR_RETURN; - } + /* register the SIM */ + err = umass_cam_attach_sim(sc); + if (err) { + umass_detach(self); + USB_ATTACH_ERROR_RETURN; } - - /* Attach the new device to our SCSI host controller (SIM) */ + /* 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\n", + 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; @@ -1045,8 +1075,8 @@ USB_DETACH(umass) (sc->proto & UMASS_PROTO_ATAPI) || (sc->proto & UMASS_PROTO_UFI) || (sc->proto & UMASS_PROTO_RBC)) - /* detach the device from the SCSI host controller (SIM) */ - err = umass_cam_detach(sc); + /* detach the SCSI host controller (SIM) */ + err = umass_cam_detach_sim(sc); for (i = 0; i < XFER_NR; i++) if (sc->transfer_xfer[i]) @@ -1073,11 +1103,14 @@ umass_init_shuttle(struct umass_softc *sc) * command does. */ req.bmRequestType = UT_READ_VENDOR_DEVICE; - req.bRequest = 1; + 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])); } /* @@ -1176,7 +1209,8 @@ umass_bbb_reset(struct umass_softc *sc, int status) usbd_device_handle udev; KASSERT(sc->proto & UMASS_PROTO_BBB, - ("sc->proto == 0x%02x wrong for umass_bbb_reset\n", sc->proto)); + ("%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) @@ -1196,7 +1230,7 @@ umass_bbb_reset(struct umass_softc *sc, int status) DPRINTF(UDMASS_BBB, ("%s: Bulk Reset\n", USBDEVNAME(sc->sc_dev))); - + sc->transfer_state = TSTATE_BBB_RESET1; sc->transfer_status = status; @@ -1218,8 +1252,8 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen, transfer_cb_f cb, void *priv) { KASSERT(sc->proto & UMASS_PROTO_BBB, - ("sc->proto == 0x%02x wrong for umass_bbb_transfer\n", - sc->proto)); + ("%s: umass_bbb_transfer: wrong sc->proto 0x%02x\n", + USBDEVNAME(sc->sc_dev), sc->proto)); /* * Do a Bulk-Only transfer with cmdlen bytes from cmd, possibly @@ -1229,7 +1263,7 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen, * 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 + * machine in umass_bbb_state handle the completion. It uses the * following states: * TSTATE_BBB_COMMAND * -> TSTATE_BBB_DATA @@ -1254,13 +1288,13 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen, ("%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 (%d vs. %d)\n", + ("%s: CBW struct does not have the right size (%ld vs. %d)\n", USBDEVNAME(sc->sc_dev), - sizeof(umass_bbb_cbw_t), UMASS_BBB_CBW_SIZE)); + (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 (%d vs. %d)\n", + ("%s: CSW struct does not have the right size (%ld vs. %d)\n", USBDEVNAME(sc->sc_dev), - sizeof(umass_bbb_csw_t), UMASS_BBB_CSW_SIZE)); + (long)sizeof(umass_bbb_csw_t), UMASS_BBB_CSW_SIZE)); /* * Determine the direction of the data transfer and the length. @@ -1286,7 +1320,7 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen, * We fill in all the fields, so there is no need to bzero it first. */ USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); - /* We don't care what the initial value was, as long as the values are unique */ + /* 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) */ @@ -1326,7 +1360,8 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_xfer_handle next_xfer; KASSERT(sc->proto & UMASS_PROTO_BBB, - ("sc->proto == 0x%02x wrong for umass_bbb_state\n",sc->proto)); + ("%s: umass_bbb_state: wrong sc->proto 0x%02x\n", + USBDEVNAME(sc->sc_dev), sc->proto)); /* * State handling for BBB transfers. @@ -1440,7 +1475,7 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, 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 || @@ -1468,6 +1503,8 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, case TSTATE_BBB_STATUS1: /* first attempt */ case TSTATE_BBB_STATUS2: /* second attempt */ /* Status transfer, error handling */ + { + int Residue; if (err) { DPRINTF(UDMASS_BBB, ("%s: Failed to read CSW, %s%s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(err), @@ -1479,9 +1516,9 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, */ 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]); + sc->bulkin, sc->bulkin_pipe, + TSTATE_BBB_SCLEAR, + sc->transfer_xfer[XFER_BBB_SCLEAR]); return; } else { umass_bbb_reset(sc, STATUS_WIRE_FAILED); @@ -1496,6 +1533,11 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, UGETDW(sc->csw.dCSWSignature) == CSWSIGNATURE_OLYMPUS_C1) USETDW(sc->csw.dCSWSignature, CSWSIGNATURE); + 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 @@ -1529,49 +1571,35 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, return; } else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) { printf("%s: Phase Error, residue = %d\n", - USBDEVNAME(sc->sc_dev), - UGETDW(sc->csw.dCSWDataResidue)); - + 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 %d bytes instead of %d bytes\n", + panic("%s: transferred %db instead of %db", USBDEVNAME(sc->sc_dev), sc->transfer_actlen, sc->transfer_datalen); - } else if (sc->transfer_datalen - sc->transfer_actlen - != UGETDW(sc->csw.dCSWDataResidue) - && !(sc->quirks & IGNORE_RESIDUE)) { - DPRINTF(UDMASS_BBB, ("%s: actlen=%d != residue=%d\n", - USBDEVNAME(sc->sc_dev), - sc->transfer_datalen - sc->transfer_actlen, - UGETDW(sc->csw.dCSWDataResidue))); - - umass_bbb_reset(sc, STATUS_WIRE_FAILED); - return; } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) { DPRINTF(UDMASS_BBB, ("%s: Command Failed, res = %d\n", - USBDEVNAME(sc->sc_dev), - UGETDW(sc->csw.dCSWDataResidue))); + USBDEVNAME(sc->sc_dev), Residue)); /* SCSI command failed but transfer was succesful */ sc->transfer_state = TSTATE_IDLE; - sc->transfer_cb(sc, sc->transfer_priv, - UGETDW(sc->csw.dCSWDataResidue), + 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, - UGETDW(sc->csw.dCSWDataResidue), + sc->transfer_cb(sc, sc->transfer_priv, Residue, STATUS_CMD_OK); return; } + } /***** Bulk Reset *****/ case TSTATE_BBB_RESET1: @@ -1612,7 +1640,7 @@ umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv, /***** Default *****/ default: - panic("%s: Unknown state %d\n", + panic("%s: Unknown state %d", USBDEVNAME(sc->sc_dev), sc->transfer_state); } } @@ -1640,9 +1668,9 @@ umass_bbb_get_max_lun(struct umass_softc *sc) 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)); - maxlun = buf; break; case USBD_STALLED: case USBD_SHORT_XFER: @@ -1668,7 +1696,8 @@ umass_cbi_adsc(struct umass_softc *sc, char *buffer, int buflen, usbd_device_handle udev; KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I), - ("sc->proto == 0x%02x wrong for umass_cbi_adsc\n",sc->proto)); + ("%s: umass_cbi_adsc: wrong sc->proto 0x%02x\n", + USBDEVNAME(sc->sc_dev), sc->proto)); usbd_interface2device_handle(sc->iface, &udev); @@ -1689,11 +1718,12 @@ umass_cbi_reset(struct umass_softc *sc, int status) # define SEND_DIAGNOSTIC_CMDLEN 12 KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I), - ("sc->proto == 0x%02x wrong for umass_cbi_reset\n",sc->proto)); + ("%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. @@ -1707,11 +1737,11 @@ umass_cbi_reset(struct umass_softc *sc, int status) DPRINTF(UDMASS_CBI, ("%s: CBI Reset\n", USBDEVNAME(sc->sc_dev))); - + KASSERT(sizeof(sc->cbl) >= SEND_DIAGNOSTIC_CMDLEN, - ("%s: CBL struct is too small (%d < %d)\n", + ("%s: CBL struct is too small (%ld < %d)\n", USBDEVNAME(sc->sc_dev), - sizeof(sc->cbl), SEND_DIAGNOSTIC_CMDLEN)); + (long)sizeof(sc->cbl), SEND_DIAGNOSTIC_CMDLEN)); sc->transfer_state = TSTATE_CBI_RESET1; sc->transfer_status = status; @@ -1736,8 +1766,8 @@ umass_cbi_transfer(struct umass_softc *sc, int lun, transfer_cb_f cb, void *priv) { KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I), - ("sc->proto == 0x%02x wrong for umass_cbi_transfer\n", - sc->proto)); + ("%s: umass_cbi_transfer: wrong sc->proto 0x%02x\n", + USBDEVNAME(sc->sc_dev), sc->proto)); /* * Do a CBI transfer with cmdlen bytes from cmd, possibly @@ -1747,7 +1777,7 @@ umass_cbi_transfer(struct umass_softc *sc, int lun, * 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 + * machine in umass_cbi_state handle the completion. It uses the * following states: * TSTATE_CBI_COMMAND * -> XXX fill in @@ -1775,6 +1805,8 @@ umass_cbi_transfer(struct umass_softc *sc, int lun, /* 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); @@ -1787,7 +1819,8 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, struct umass_softc *sc = (struct umass_softc *) priv; KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I), - ("sc->proto == 0x%02x wrong for umass_cbi_state\n", sc->proto)); + ("%s: umass_cbi_state: wrong sc->proto 0x%02x\n", + USBDEVNAME(sc->sc_dev), sc->proto)); /* * State handling for CBI transfers. @@ -1810,7 +1843,7 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, * 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 stalled at this point. + * should not be stalled at this point. */ sc->transfer_state = TSTATE_IDLE; @@ -1826,7 +1859,7 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, return; } - + sc->transfer_state = TSTATE_CBI_DATA; if (sc->transfer_dir == DIR_IN) { if (umass_setup_transfer(sc, sc->bulkin_pipe, @@ -1892,7 +1925,6 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, if (sc->proto & UMASS_PROTO_CBI_I) { sc->transfer_state = TSTATE_CBI_STATUS; - memset(&sc->sbl, 0, sizeof(sc->sbl)); if (umass_setup_transfer(sc, sc->intrin_pipe, &sc->sbl, sizeof(sc->sbl), 0, /* fixed length transfer */ @@ -1932,7 +1964,7 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, 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. @@ -1948,7 +1980,10 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, else status = STATUS_CMD_FAILED; - /* No sense, command successfull */ + 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", @@ -1972,8 +2007,8 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, sc->transfer_state = TSTATE_IDLE; sc->transfer_cb(sc, sc->transfer_priv, - sc->transfer_datalen - sc->transfer_actlen, - err); + sc->transfer_datalen-sc->transfer_actlen, + err); } } return; @@ -2040,7 +2075,7 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, /***** Default *****/ default: - panic("%s: Unknown state %d\n", + panic("%s: Unknown state %d", USBDEVNAME(sc->sc_dev), sc->transfer_state); } } @@ -2053,7 +2088,7 @@ umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv, */ Static int -umass_cam_attach_sim() +umass_cam_attach_sim(struct umass_softc *sc) { struct cam_devq *devq; /* Per device Queue */ @@ -2067,17 +2102,20 @@ umass_cam_attach_sim() if (devq == NULL) return(ENOMEM); - umass_sim = cam_sim_alloc(umass_cam_action, umass_cam_poll, DEVNAME_SIM, - NULL /*priv*/, UMASS_SIM_UNIT /*unit number*/, + 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 (umass_sim == NULL) { + if (sc->umass_sim == NULL) { cam_simq_free(devq); return(ENOMEM); } - if(xpt_bus_register(umass_sim, UMASS_SCSI_BUS) != CAM_SUCCESS) + if(xpt_bus_register(sc->umass_sim, USBDEVUNIT(sc->sc_dev)) != + CAM_SUCCESS) return(ENOMEM); return(0); @@ -2088,12 +2126,12 @@ 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, ("scbus%d: Rescan failed, 0x%04x\n", - cam_sim_path(umass_sim), + DPRINTF(UDMASS_SCSI, ("%s:%d Rescan failed, 0x%04x\n", + periph->periph_name, periph->unit_number, ccb->ccb_h.status)); } else { - DPRINTF(UDMASS_SCSI, ("scbus%d: Rescan succeeded\n", - cam_sim_path(umass_sim))); + DPRINTF(UDMASS_SCSI, ("%s%d: Rescan succeeded\n", + periph->periph_name, periph->unit_number)); } #endif @@ -2102,19 +2140,20 @@ umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) } Static void -umass_cam_rescan(struct umass_softc *sc) +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(umass_sim), - USBDEVNAME(sc->sc_dev), cam_sim_path(umass_sim), + 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(umass_sim), + if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->umass_sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) return; @@ -2131,36 +2170,26 @@ umass_cam_rescan(struct umass_softc *sc) Static int umass_cam_attach(struct umass_softc *sc) { - /* SIM already attached at module load. The device is a target on the - * one SIM we registered: target device_get_unit(self). - */ - - /* The artificial limit UMASS_SCSIID_MAX is there because CAM expects - * a limit to the number of targets that are present on a SIM. - */ - if (device_get_unit(sc->sc_dev) >= UMASS_SCSIID_MAX) { - printf("scbus%d: Increase UMASS_SCSIID_MAX (currently %d) in %s" - " and try again.\n", - cam_sim_path(umass_sim), UMASS_SCSIID_MAX, __FILE__); - return(1); - } - #ifndef USB_DEBUG if (bootverbose) #endif - printf("%s:%d:%d:%d: Attached to scbus%d as device %d\n", - USBDEVNAME(sc->sc_dev), cam_sim_path(umass_sim), - USBDEVUNIT(sc->sc_dev), CAM_LUN_WILDCARD, - cam_sim_path(umass_sim), USBDEVUNIT(sc->sc_dev)); + 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. Any failure is benign, as the - * user can still do it by hand (camcontrol rescan ). - * Only do this if we are not booting, because CAM does a scan - * after booting has completed, when interrupts have been - * enabled. + /* 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 ). 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. */ - umass_cam_rescan(sc); + timeout(umass_cam_rescan, sc, MS_TO_TICKS(200)); } return(0); /* always succesfull */ @@ -2171,48 +2200,20 @@ umass_cam_attach(struct umass_softc *sc) */ Static int -umass_cam_detach_sim() +umass_cam_detach_sim(struct umass_softc *sc) { - if (umass_sim) - return(EBUSY); /* XXX CAM can't handle disappearing SIMs yet */ - - if (umass_sim) { - if (xpt_bus_deregister(cam_sim_path(umass_sim))) - cam_sim_free(umass_sim, /*free_devq*/TRUE); + 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); - umass_sim = NULL; - } - - return(0); -} - -Static int -umass_cam_detach(struct umass_softc *sc) -{ - struct cam_path *path; - - if (umass_sim) { - /* detach of sim not done until module unload */ - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d: losing CAM device entry\n", - USBDEVNAME(sc->sc_dev), cam_sim_path(umass_sim), - USBDEVUNIT(sc->sc_dev), CAM_LUN_WILDCARD)); - - if (xpt_create_path(&path, NULL, cam_sim_path(umass_sim), - USBDEVUNIT(sc->sc_dev), CAM_LUN_WILDCARD) - != CAM_REQ_CMP) - return(ENOMEM); - - xpt_async(AC_LOST_DEVICE, path, NULL); - xpt_free_path(path); + sc->umass_sim = NULL; } return(0); } - - /* umass_cam_action * CAM requests for action come through here */ @@ -2220,8 +2221,7 @@ umass_cam_detach(struct umass_softc *sc) Static void umass_cam_action(struct cam_sim *sim, union ccb *ccb) { - struct umass_softc *sc = devclass_get_softc(umass_devclass, - ccb->ccb_h.target_id); + 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. @@ -2229,7 +2229,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) 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(umass_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_TID_INVALID; @@ -2254,7 +2254,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) if (sc == NULL) { printf("%s:%d:%d:%d:func_code 0x%04x: " "Invalid target (target needed)\n", - DEVNAME_SIM, cam_sim_path(umass_sim), + DEVNAME_SIM, cam_sim_path(sc->umass_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); @@ -2272,7 +2272,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) 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(umass_sim), + DEVNAME_SIM, cam_sim_path(sc->umass_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code)); @@ -2300,7 +2300,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) 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(umass_sim), + 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, @@ -2317,8 +2317,8 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) if (sc->transfer_state != TSTATE_IDLE) { DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SCSI_IO: " - "I/O requested while busy (state %d, %s)\n", - USBDEVNAME(sc->sc_dev), cam_sim_path(umass_sim), + "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; @@ -2346,7 +2346,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) cmd = (unsigned char *) &csio->cdb_io.cdb_bytes; } cmdlen = csio->cdb_len; - rcmd = (unsigned char *) &sc->cam_scsi_command; + rcmd = (unsigned char *) &sc->cam_scsi_command; rcmdlen = sizeof(sc->cam_scsi_command); /* sc->transform will convert the command to the command @@ -2358,7 +2358,39 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) */ if (sc->transform(sc, cmd, cmdlen, &rcmd, &rcmdlen)) { - if ((sc->quirks & FORCE_SHORT_INQUIRY) && (rcmd[0] == INQUIRY)) { + /* + * 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, @@ -2378,7 +2410,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_PATH_INQ:.\n", (sc == NULL? DEVNAME_SIM:USBDEVNAME(sc->sc_dev)), - cam_sim_path(umass_sim), + cam_sim_path(sc->umass_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); /* host specific information */ @@ -2388,18 +2420,24 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) cpi->hba_misc = PIM_NO_6_BYTE; cpi->hba_eng_cnt = 0; cpi->max_target = UMASS_SCSIID_MAX; /* one target */ - if (sc == NULL) - cpi->max_lun = 0; - else - cpi->max_lun = sc->maxlun; 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 = UMASS_SCSI_BUS; - if (sc) - cpi->base_transfer_speed = sc->transfer_speed; + 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); @@ -2408,19 +2446,19 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) case XPT_RESET_DEV: { DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_RESET_DEV:.\n", - USBDEVNAME(sc->sc_dev), cam_sim_path(umass_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_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(umass_sim), + USBDEVNAME(sc->sc_dev), cam_sim_path(sc->umass_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); cts->valid = 0; @@ -2433,7 +2471,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) case XPT_SET_TRAN_SETTINGS: { DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SET_TRAN_SETTINGS:.\n", - USBDEVNAME(sc->sc_dev), cam_sim_path(umass_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_FUNC_NOTAVAIL; @@ -2442,51 +2480,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) } case XPT_CALC_GEOMETRY: { - struct ccb_calc_geometry *ccg = &ccb->ccg; - u_int32_t size_mb; - u_int32_t secs_per_cylinder; - int extended = 1; - - DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_CALC_GEOMETRY: " - "Volume size = %d\n", - USBDEVNAME(sc->sc_dev), cam_sim_path(umass_sim), - ccb->ccb_h.target_id, ccb->ccb_h.target_lun, - ccg->volume_size)); - - /* XXX We should probably ask the drive for the details - * instead of cluching them up ourselves - */ - if (sc->drive == ZIP_100) { - ccg->heads = 64; - ccg->secs_per_track = 32; - ccg->cylinders = ccg->volume_size / ccg->heads - / ccg->secs_per_track; - ccb->ccb_h.status = CAM_REQ_CMP; - break; - } else if (sc->proto & UMASS_PROTO_UFI) { - ccg->heads = 2; - if (ccg->volume_size == 2880) - ccg->secs_per_track = 18; - else - ccg->secs_per_track = 9; - ccg->cylinders = 80; - break; - } else { - size_mb = ccg->volume_size - / ((1024L * 1024L) / ccg->block_size); - - if (size_mb >= 1024 && extended) { - ccg->heads = 255; - ccg->secs_per_track = 63; - } else { - ccg->heads = 64; - ccg->secs_per_track = 32; - } - secs_per_cylinder = ccg->heads * ccg->secs_per_track; - ccg->cylinders = ccg->volume_size / secs_per_cylinder; - ccb->ccb_h.status = CAM_REQ_CMP; - } - + cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); break; } @@ -2494,7 +2488,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) { DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_NOOP:.\n", (sc == NULL? DEVNAME_SIM:USBDEVNAME(sc->sc_dev)), - cam_sim_path(umass_sim), + cam_sim_path(sc->umass_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun)); ccb->ccb_h.status = CAM_REQ_CMP; @@ -2505,7 +2499,7 @@ umass_cam_action(struct cam_sim *sim, union ccb *ccb) 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(umass_sim), + cam_sim_path(sc->umass_sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code)); @@ -2564,8 +2558,7 @@ umass_cam_cb(struct umass_softc *sc, void *priv, int residue, int status) sc->cam_scsi_sense.length = csio->sense_len; DPRINTF(UDMASS_SCSI,("%s: Fetching %db sense data\n", - USBDEVNAME(sc->sc_dev), - sc->cam_scsi_sense.length)); + USBDEVNAME(sc->sc_dev), csio->sense_len)); rcmd = (unsigned char *) &sc->cam_scsi_command; rcmdlen = sizeof(sc->cam_scsi_command); @@ -2583,7 +2576,7 @@ umass_cam_cb(struct umass_softc *sc, void *priv, int residue, int status) csio->sense_len, DIR_IN, umass_cam_sense_cb, (void *) ccb); } else { - panic("transform(REQUEST_SENSE) failed\n"); + panic("transform(REQUEST_SENSE) failed"); } break; } @@ -2592,7 +2585,7 @@ umass_cam_cb(struct umass_softc *sc, void *priv, int residue, int status) xpt_done(ccb); break; default: - panic("umass_cam_cb called for func_code %d\n", + panic("umass_cam_cb called for func_code %d", ccb->ccb_h.func_code); } break; @@ -2606,7 +2599,7 @@ umass_cam_cb(struct umass_softc *sc, void *priv, int residue, int status) xpt_done(ccb); break; default: - panic("%s: Unknown status %d in umass_cam_cb\n", + panic("%s: Unknown status %d in umass_cam_cb", USBDEVNAME(sc->sc_dev), status); } } @@ -2624,11 +2617,11 @@ umass_cam_sense_cb(struct umass_softc *sc, void *priv, int residue, int status) switch (status) { case STATUS_CMD_OK: case STATUS_CMD_UNKNOWN: - /* Getting sense data succeeded. The length of the sense data - * is not returned in any way. The sense data itself contains - * the length of the sense data that is valid. + case STATUS_CMD_FAILED: + /* Getting sense data always succeeds (apart from wire + * failures). */ - if (sc->quirks & RS_NO_CLEAR_UA + if ((sc->quirks & RS_NO_CLEAR_UA) && csio->cdb_io.cdb_bytes[0] == INQUIRY && (csio->sense_data.flags & SSD_KEY) == SSD_KEY_UNIT_ATTENTION) { @@ -2644,20 +2637,28 @@ umass_cam_sense_cb(struct umass_softc *sc, void *priv, int residue, int status) * CCI) */ ccb->ccb_h.status = CAM_REQ_CMP; - } else if ((sc->quirks & RS_NO_CLEAR_UA) && /* XXX */ + } 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)) { - /* 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. - */ + /* + * 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; - DPRINTF(UDMASS_SCSI,("%s: Doing a sneaky TEST_UNIT_READY\n", +#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 */ @@ -2675,7 +2676,7 @@ umass_cam_sense_cb(struct umass_softc *sc, void *priv, int residue, int status) NULL, 0, DIR_NONE, umass_cam_quirk_cb, (void *) ccb); } else { - panic("transform(TEST_UNIT_READY) failed\n"); + panic("transform(TEST_UNIT_READY) failed"); } break; } else { @@ -2694,6 +2695,11 @@ umass_cam_sense_cb(struct umass_softc *sc, void *priv, int residue, int status) } } +/* + * 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) { @@ -2701,27 +2707,21 @@ umass_cam_quirk_cb(struct umass_softc *sc, void *priv, int residue, int status) DPRINTF(UDMASS_SCSI, ("%s: Test unit ready returned status %d\n", USBDEVNAME(sc->sc_dev), status)); - ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; +#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) { - int err; - switch (what) { case MOD_UNLOAD: - err = umass_cam_detach_sim(); - if (err) - return(err); - return(usbd_driver_load(mod, what, arg)); case MOD_LOAD: - /* We don't attach to CAM at this point, because it will try - * and malloc memory for it. This is not possible when the - * boot loader loads umass as a module before the kernel - * has been bootstrapped. - */ default: return(usbd_driver_load(mod, what, arg)); } @@ -2739,8 +2739,9 @@ umass_scsi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, case TEST_UNIT_READY: if (sc->quirks & NO_TEST_UNIT_READY) { KASSERT(*rcmdlen >= sizeof(struct scsi_start_stop_unit), - ("rcmdlen = %d < %d, buffer too small", - *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); @@ -2757,13 +2758,13 @@ umass_scsi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, (*rcmd)[4] = SHORT_INQUIRY_LENGTH; return 1; } - /* fallthrough */ + /* fallthrough */ default: *rcmd = cmd; /* We don't need to copy it */ *rcmdlen = cmdlen; } - return 1; /* success */ + return 1; } /* RBC specific functions */ Static int @@ -2790,7 +2791,7 @@ umass_rbc_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, case PREVENT_ALLOW: *rcmd = cmd; /* We don't need to copy it */ *rcmdlen = cmdlen; - return 1; /* success */ + return 1; /* All other commands are not legal in RBC */ default: printf("%s: Unsupported RBC command 0x%02x", @@ -2803,7 +2804,6 @@ umass_rbc_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, /* * UFI specific functions */ - Static int umass_ufi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, unsigned char **rcmd, int *rcmdlen) @@ -2816,22 +2816,24 @@ umass_ufi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, *rcmdlen = UFI_COMMAND_LENGTH; memset(*rcmd, 0, UFI_COMMAND_LENGTH); - /* Handle any quirks */ - if (cmd[0] == TEST_UNIT_READY - && 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))); - cmd[0] = START_STOP_UNIT; - cmd[4] = SSS_START; - return 1; - } - switch (cmd[0]) { - /* Commands of which the format has been verified. They should work. */ + /* 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: @@ -2844,31 +2846,19 @@ umass_ufi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, case POSITION_TO_ELEMENT: /* SEEK_10 */ case MODE_SELECT_10: case MODE_SENSE_10: - /* Copy the command into the (zeroed out) destination buffer */ + case READ_12: + case WRITE_12: memcpy(*rcmd, cmd, cmdlen); - return 1; /* success */ + return 1; - /* Other UFI commands: FORMAT_UNIT, MODE_SELECT, READ_FORMAT_CAPACITY, + /* Other UFI commands: FORMAT_UNIT, READ_FORMAT_CAPACITY, * VERIFY, WRITE_AND_VERIFY. * These should be checked whether they somehow can be made to fit. */ - /* These commands are known _not_ to work. They should be converted - * The 6 byte commands can be switched off with a CAM quirk. See - * the entry for the Y-E data drive. - */ - case READ_6: - case WRITE_6: - case MODE_SENSE_6: - case MODE_SELECT_6: - case READ_12: - case WRITE_12: default: - printf("%s: Unsupported UFI command 0x%02x", + printf("%s: Unsupported UFI command 0x%02x\n", USBDEVNAME(sc->sc_dev), cmd[0]); - if (cmdlen == 6) - printf(", 6 byte command should have been converted"); - printf("\n"); return 0; /* failure */ } } @@ -2880,7 +2870,7 @@ Static int umass_atapi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, unsigned char **rcmd, int *rcmdlen) { - /* A ATAPI command is always 12 bytes in length */ + /* An ATAPI command is always 12 bytes in length. */ KASSERT(*rcmdlen >= ATAPI_COMMAND_LENGTH, ("rcmdlen = %d < %d, buffer too small", *rcmdlen, ATAPI_COMMAND_LENGTH)); @@ -2889,12 +2879,22 @@ umass_atapi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, memset(*rcmd, 0, ATAPI_COMMAND_LENGTH); switch (cmd[0]) { - /* Commands of which the format has been verified. They should work. */ + /* 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 < %d, buffer too small", - *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); @@ -2903,15 +2903,6 @@ umass_atapi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, 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 */ case REZERO_UNIT: case REQUEST_SENSE: case START_STOP_UNIT: @@ -2921,28 +2912,17 @@ umass_atapi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen, case READ_10: case WRITE_10: case POSITION_TO_ELEMENT: /* SEEK_10 */ + case SYNCHRONIZE_CACHE: case MODE_SELECT_10: case MODE_SENSE_10: - /* Copy the command into the (zeroed out) destination buffer */ memcpy(*rcmd, cmd, cmdlen); - return 1; /* success */ + return 1; - /* These commands are known _not_ to work. They should be converted - * The 6 byte commands can be switched off with a CAM quirk. See - * the entry for the Y-E data drive. - */ - case READ_6: - case WRITE_6: - case MODE_SENSE_6: - case MODE_SELECT_6: case READ_12: case WRITE_12: default: - printf("%s: Unsupported ATAPI command 0x%02x", + printf("%s: Unsupported ATAPI command 0x%02x\n", USBDEVNAME(sc->sc_dev), cmd[0]); - if (cmdlen == 6) - printf(", 6 byte command should have been converted"); - printf("\n"); return 0; /* failure */ } } @@ -2966,7 +2946,7 @@ umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw) DPRINTF(UDMASS_BBB, ("%s: CBW %d: cmd = %db " "(0x%02x%02x%02x%02x%02x%02x%s), " - "data = %d bytes, dir = %s\n", + "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": @@ -2990,6 +2970,23 @@ umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw) (status == CSWSTATUS_PHASE? "phase":""))))); } +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": ""))))); +} + Static void umass_dump_buffer(struct umass_softc *sc, u_int8_t *buffer, int buflen, int printlen) diff --git a/sys/dev/usbmisc/umct/umct.c b/sys/dev/usbmisc/umct/umct.c new file mode 100644 index 0000000000..eba26ac75c --- /dev/null +++ b/sys/dev/usbmisc/umct/umct.c @@ -0,0 +1,514 @@ +/*- + * 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. + * + * $FreeBSD: src/sys/dev/usb/umct.c,v 1.5 2003/11/16 12:13:39 akiyama Exp $ + * $DragonFly: src/sys/dev/usbmisc/umct/umct.c,v 1.1 2003/12/30 01:01:47 dillon Exp $ + */ + +/* + * Driver for the MCT (Magic Control Technology) USB-RS232 Converter. + * Based on the superb documentation from the linux mct_u232 driver by + * Wolfgang Grandeggar . + * 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 . Merging the two together + * might be useful, though the subtle differences might lead to lots of + * #ifdef's. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* 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/usbmisc/umodem/umodem.c b/sys/dev/usbmisc/umodem/umodem.c index 83dfdf39b1..bab3bc4783 100644 --- a/sys/dev/usbmisc/umodem/umodem.c +++ b/sys/dev/usbmisc/umodem/umodem.c @@ -1,6 +1,35 @@ -/* $NetBSD: umodem.c,v 1.5 1999/01/08 11:58:25 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/usb/umodem.c,v 1.17.2.9 2002/11/06 20:23:50 joe Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/umodem/umodem.c,v 1.7 2003/08/07 21:54:29 dillon Exp $ */ +/* + * $NetBSD: umodem.c,v 1.5 1999/01/08 11:58:25 augustss Exp $ + * $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ + * $FreeBSD: src/sys/dev/usb/umodem.c,v 1.48 2003/08/24 17:55:55 obrien Exp $ + * $DragonFly: src/sys/dev/usbmisc/umodem/umodem.c,v 1.8 2003/12/30 01:01:47 dillon Exp $ + */ + +/*- + * Copyright (c) 2003, 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. + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -40,7 +69,8 @@ */ /* - * Comm Class spec: http://www.usb.org/developers/data/usbcdc11.pdf + * Comm Class spec: http://www.usb.org/developers/data/devclass/usbcdc10.pdf + * http://www.usb.org/developers/data/devclass/usbcdc11.pdf */ /* @@ -54,24 +84,16 @@ #include #include #include -#include -#if defined(__NetBSD__) || defined(__OpenBSD__) -#include -#include -#elif defined(__FreeBSD__) -#include #include -#include -#endif #include #include -#include #include #include +#include #include #include +#include #include -#include #include #include @@ -79,38 +101,35 @@ #include #include #include +#include #include #include #ifdef USB_DEBUG -#define DPRINTF(x) if(umodemdebug) logprintf x -#define DPRINTFN(n, x) if(umodemdebug > (n)) logprintf x 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 DPRINTF(x) #define DPRINTFN(n, x) #endif +#define DPRINTF(x) DPRINTFN(0, x) -/* 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 UMODEMUNIT_MASK 0x3ffff -#define UMODEMDIALOUT_MASK 0x80000 -#define UMODEMCALLUNIT_MASK 0x40000 - -#define UMODEMUNIT(x) (minor(x) & UMODEMUNIT_MASK) -#define UMODEMDIALOUT(x) (minor(x) & UMODEMDIALOUT_MASK) -#define UMODEMCALLUNIT(x) (minor(x) & UMODEMCALLUNIT_MASK) - +/* + * 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 */ @@ -120,106 +139,94 @@ struct umodem_softc { int sc_data_iface_no; usbd_interface_handle sc_data_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 */ - - int sc_bulkout_no; /* bulk out endpoint address */ - usbd_pipe_handle sc_bulkout_pipe;/* bulk out pipe */ - usbd_xfer_handle sc_oxfer; /* read request */ - int sc_cm_cap; /* CM capabilities */ int sc_acm_cap; /* ACM capabilities */ int sc_cm_over_data; - struct tty *sc_tty; /* our tty */ - 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 */ - u_char sc_dying; /* disconnecting */ -#if defined(__FreeBSD__) - dev_t dev; /* special device node */ -#endif + 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 */ }; -#if defined(__NetBSD__) || defined(__OpenBSD__) -cdev_decl(umodem); - -#elif defined(__FreeBSD__) -d_open_t umodemopen; -d_close_t umodemclose; -d_read_t umodemread; -d_write_t umodemwrite; -d_ioctl_t umodemioctl; - -#define UMODEM_CDEV_MAJOR 124 - -static struct cdevsw umodem_cdevsw = { - /* name */ "umodem", - /* maj */ UMODEM_CDEV_MAJOR, - /* flags */ D_TTY | D_KQFILTER, - /* port */ NULL, - /* autoq */ 0, - - /* open */ umodemopen, - /* close */ umodemclose, - /* read */ umodemread, - /* write */ umodemwrite, - /* ioctl */ umodemioctl, - /* poll */ ttypoll, - /* mmap */ nommap, - /* strategy */ nostrategy, - /* dump */ nodump, - /* psize */ nopsize, - /* kqfilter */ ttykqfilter +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) }; -#endif -void *umodem_get_desc - (usbd_device_handle dev, int type, int subtype); -usbd_status umodem_set_comm_feature - (struct umodem_softc *sc, int feature, int state); -usbd_status umodem_set_line_coding - (struct umodem_softc *sc, usb_cdc_line_state_t *state); - -void umodem_get_caps (usbd_device_handle, int *, int *); -void umodem_cleanup (struct umodem_softc *); -int umodemparam (struct tty *, struct termios *); -void umodemstart (struct tty *); -void umodemstop (struct tty *, int); -struct tty * umodemtty (dev_t dev); -void umodem_shutdown (struct umodem_softc *); -void umodem_modem (struct umodem_softc *, int); -void umodem_break (struct umodem_softc *, int); -usbd_status umodemstartread (struct umodem_softc *); -void umodemreadcb (usbd_xfer_handle, usbd_private_handle, - usbd_status status); -void umodemwritecb (usbd_xfer_handle, usbd_private_handle, - usbd_status status); - -USB_DECLARE_DRIVER(umodem); +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; int cm, acm; - - if (!uaa->iface) + + if (uaa->iface == NULL) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); - if (id == 0 || + if (id == NULL || id->bInterfaceClass != UICLASS_CDC || id->bInterfaceSubClass != UISUBCLASS_ABSTRACT_CONTROL_MODEL || id->bInterfaceProtocol != UIPROTO_CDC_AT) return (UMATCH_NONE); - + umodem_get_caps(uaa->device, &cm, &acm); if (!(cm & USB_CDC_CM_DOES_CM) || !(cm & USB_CDC_CM_OVER_DATA) || @@ -236,296 +243,310 @@ USB_ATTACH(umodem) usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usb_cdc_cm_descriptor_t *cmd; - char devinfo[1024]; + char *devinfo = NULL; + const char *devname; usbd_status err; - int data_ifaceno; + int data_ifcno; int i; - struct tty *tp; - - usbd_devinfo(uaa->device, 0, devinfo); - USB_ATTACH_SETUP; + 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", USBDEVNAME(sc->sc_dev), - devinfo, id->bInterfaceClass, id->bInterfaceSubClass); + 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) { - DPRINTF(("%s: no CM desc\n", USBDEVNAME(sc->sc_dev))); + if (cmd == NULL) { + printf("%s: no CM descriptor\n", devname); goto bad; } - sc->sc_data_iface_no = data_ifaceno = cmd->bDataInterface; + sc->sc_data_iface_no = data_ifcno = cmd->bDataInterface; printf("%s: data interface %d, has %sCM over data, has %sbreak\n", - USBDEVNAME(sc->sc_dev), data_ifaceno, + 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]) { + if (uaa->ifaces[i] != NULL) { id = usbd_get_interface_descriptor(uaa->ifaces[i]); - if (id->bInterfaceNumber == data_ifaceno) { + if (id != NULL && id->bInterfaceNumber == data_ifcno) { sc->sc_data_iface = uaa->ifaces[i]; - uaa->ifaces[i] = 0; + uaa->ifaces[i] = NULL; } } } - if (!sc->sc_data_iface) { - printf("%s: no data interface\n", USBDEVNAME(sc->sc_dev)); + 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. + /* + * Find the bulk endpoints. * Iterate over all endpoints in the data interface and take note. */ - sc->sc_bulkin_no = sc->sc_bulkout_no = -1; + 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) { - printf("%s: no endpoint descriptor for %d\n", - USBDEVNAME(sc->sc_dev), 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 && - (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { - sc->sc_bulkin_no = ed->bEndpointAddress; + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + ucom->sc_bulkin_no = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && - (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { - sc->sc_bulkout_no = ed->bEndpointAddress; + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + ucom->sc_bulkout_no = ed->bEndpointAddress; } } - if (sc->sc_bulkin_no == -1) { - DPRINTF(("%s: Could not find data bulk in\n", - USBDEVNAME(sc->sc_dev))); + if (ucom->sc_bulkin_no == -1) { + printf("%s: Could not find data bulk in\n", devname); goto bad; } - if (sc->sc_bulkout_no == -1) { - DPRINTF(("%s: Could not find data bulk out\n", - USBDEVNAME(sc->sc_dev))); + 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) { - err = umodem_set_comm_feature(sc, UCDC_ABSTRACT_STATE, - UCDC_DATA_MULTIPLEXED); - if (err) - goto bad; - sc->sc_cm_over_data = 1; - } + 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; + } } -#if defined(__NetBSD__) || defined(__OpenBSD__) - sc->sc_tty = tp = ttymalloc(); -#elif defined(__FreeBSD__) - sc->sc_tty = tp = ttymalloc(sc->sc_tty); -#endif - tp->t_oproc = umodemstart; - tp->t_param = umodemparam; - tp->t_stop = umodemstop; - DPRINTF(("umodem_attach: tty_attach %p\n", tp)); -#if defined(__NetBSD__) || defined(__OpenBSD__) - tty_attach(tp); -#endif + /* + * 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. + */ -#if defined(__FreeBSD__) - DPRINTF(("umodem_attach: make_dev: umodem%d\n", device_get_unit(self))); - sc->dev = make_dev(&umodem_cdevsw, device_get_unit(self), - UID_UUCP, GID_DIALER, 0660, - "umodem%d", device_get_unit(self)); - sc->dev->si_tty = tp; -#endif + 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: - DPRINTF(("umodem_attach: BAD -> DYING\n")); - sc->sc_dying = 1; + ucom->sc_dying = 1; + free(devinfo, M_USBDEV); USB_ATTACH_ERROR_RETURN; } -void -umodem_get_caps(usbd_device_handle dev, int *cm, int *acm) +Static int +umodem_open(void *addr, int portno) { - usb_cdc_cm_descriptor_t *cmd; - usb_cdc_acm_descriptor_t *cad; + struct umodem_softc *sc = addr; + int err; - *cm = *acm = 0; + DPRINTF(("umodem_open: sc=%p\n", sc)); - cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); - if (!cmd) { - DPRINTF(("umodem_get_desc: no CM desc\n")); - return; - } - *cm = cmd->bmCapabilities; + 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); - cad = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); - if (!cad) { - DPRINTF(("umodem_get_desc: no ACM desc\n")); - return; + if (err) { + DPRINTF(("Failed to establish notify pipe: %s\n", + usbd_errstr(err))); + return EIO; + } } - *acm = cad->bmCapabilities; -} -void -umodemstart(struct tty *tp) -{ - struct umodem_softc *sc; - struct cblock *cbp; - int s; - u_char *data; - int cnt; + return 0; +} - USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc); +Static void +umodem_close(void *addr, int portno) +{ + struct umodem_softc *sc = addr; + int err; - if (sc->sc_dying) - return; + DPRINTF(("umodem_close: sc=%p\n", sc)); - s = spltty(); - if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { - ttwwakeup(tp); - DPRINTFN(4,("umodemstart: stopped\n")); - goto out; + 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; } +} -#if defined(__NetBSD__) || defined(__OpenBSD__) - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (ISSET(tp->t_state, TS_ASLEEP)) { - CLR(tp->t_state, TS_ASLEEP); - wakeup(&tp->t_outq); - } - selwakeup(&tp->t_wsel); - if (tp->t_outq.c_cc == 0) - goto out; - } -#elif defined(__FreeBSD__) - 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)); - } - selwakeup(&tp->t_wsel); - 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; - } - } -#endif +Static void +umodem_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct umodem_softc *sc = priv; + u_char mstatus; - /* Grab the first contiguous region of buffer space. */ - data = tp->t_outq.c_cf; -#if defined(__NetBSD__) || defined(__OpenBSD__) - cnt = ndqb(&tp->t_outq, 0); -#elif defined(__FreeBSD__) - 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); -#endif + if (sc->sc_ucom.sc_dying) + return; - if (cnt == 0) { - DPRINTF(("umodemstart: cnt==0\n")); - splx(s); + 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; } - SET(tp->t_state, TS_BUSY); - - DPRINTFN(4,("umodemstart: %d chars\n", cnt)); - /* XXX what can we do on error? */ - usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, - (usbd_private_handle)sc, data, cnt, - 0, USBD_NO_TIMEOUT, umodemwritecb); - (void)usbd_transfer(sc->sc_oxfer); + 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; + } - ttwwakeup(tp); -out: - splx(s); + 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 -umodemwritecb(usbd_xfer_handle xfer, usbd_private_handle priv, - usbd_status status) +umodem_get_caps(usbd_device_handle dev, int *cm, int *acm) { - struct umodem_softc *sc = (struct umodem_softc *)priv; - struct tty *tp = sc->sc_tty; - u_int32_t cc; - int s; + usb_cdc_cm_descriptor_t *cmd; + usb_cdc_acm_descriptor_t *cad; - DPRINTFN(5,("umodemwritecb: status=%d\n", status)); + *cm = *acm = 0; - if (status == USBD_CANCELLED) + 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; - if (status != USBD_NORMAL_COMPLETION) { - DPRINTF(("umodemwritecb: status=%d\n", status)); - usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); - /* XXX we should restart after some delay. */ + 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; - usbd_get_xfer_status(xfer, 0, 0, &cc, 0); - DPRINTFN(5,("umodemwritecb: cc=%d\n", cc)); + DPRINTF(("umodem_get_status:\n")); - 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); - (*linesw[tp->t_line].l_start)(tp); - splx(s); + if (lsr != NULL) + *lsr = sc->sc_lsr; + if (msr != NULL) + *msr = sc->sc_msr; } int -umodemparam(struct tty *tp, struct termios *t) +umodem_param(void *addr, int portno, struct termios *t) { - struct umodem_softc *sc; + struct umodem_softc *sc = addr; + usbd_status err; usb_cdc_line_state_t ls; - USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc); - - if (sc->sc_dying) - return (EIO); - - /* Check requested parameters. */ - if (t->c_ospeed < 0) - return (EINVAL); - if (t->c_ispeed && t->c_ispeed != t->c_ospeed) - 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; + DPRINTF(("umodem_param: sc=%p\n", sc)); USETDW(ls.dwDTERate, t->c_ospeed); if (ISSET(t->c_cflag, CSTOPB)) @@ -553,414 +574,28 @@ umodemparam(struct tty *tp, struct termios *t) ls.bDataBits = 8; break; } - /* XXX what can we if it fails? */ - (void)umodem_set_line_coding(sc, &ls); - - /* - * Update the tty layer's idea of the carrier bit, in case we changed - * CLOCAL or MDMBUF. We don't hang up here; we only do that by - * explicit request. - */ - (void) (*linesw[tp->t_line].l_modem)(tp, 1 /* XXX carrier */ ); - - return (0); -} - -int -umodemopen(dev_t dev, int flag, int mode, usb_proc_ptr td) -{ - int unit = UMODEMUNIT(dev); - struct umodem_softc *sc; - usbd_status err; - struct tty *tp; - int s; - int error; - struct proc *p = td->td_proc; - - KKASSERT(p != NULL); - - USB_GET_SC_OPEN(umodem, unit, sc); - - if (sc->sc_dying) - return (EIO); - -#if defined(__NetBSD__) || defined(__OpenBBSD__) - if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0) - return (ENXIO); -#endif - - tp = sc->sc_tty; - - DPRINTF(("%s: umodemopen: tp=%p\n", USBDEVNAME(sc->sc_dev), tp)); - - if (ISSET(tp->t_state, TS_ISOPEN) && - ISSET(tp->t_state, TS_XCLUDE) && - p->p_ucred->cr_uid != 0) - return (EBUSY); - - /* - * Do the following iff this is a first open. - */ - s = spltty(); - while (sc->sc_opening) - tsleep(&sc->sc_opening, 0, "umdmop", 0); - sc->sc_opening = 1; - -#if defined(__NetBSD__) || defined(__OpenBSD__) - if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { -#elif defined(__FreeBSD__) - if (!ISSET(tp->t_state, TS_ISOPEN)) { -#endif - struct termios t; - - 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 umodemparam() will do something. */ - tp->t_ospeed = 0; - (void) umodemparam(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. - */ - umodem_modem(sc, 1); - - DPRINTF(("umodemopen: open pipes\n")); - - /* Open the bulk pipes */ - err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkin_no, 0, - &sc->sc_bulkin_pipe); - if (err) { - DPRINTF(("%s: cannot open bulk out pipe (addr %d)\n", - USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no)); - return (EIO); - } - err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkout_no, - USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); - if (err) { - DPRINTF(("%s: cannot open bulk in pipe (addr %d)\n", - USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no)); - usbd_close_pipe(sc->sc_bulkin_pipe); - return (EIO); - } - - /* Allocate a request and an input buffer and start reading. */ - sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev); - if (sc->sc_ixfer == 0) { - usbd_close_pipe(sc->sc_bulkin_pipe); - usbd_close_pipe(sc->sc_bulkout_pipe); - return (ENOMEM); - } - sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev); - if (sc->sc_oxfer == 0) { - usbd_close_pipe(sc->sc_bulkin_pipe); - usbd_close_pipe(sc->sc_bulkout_pipe); - usbd_free_xfer(sc->sc_ixfer); - return (ENOMEM); - } - sc->sc_ibuf = malloc(UMODEMIBUFSIZE, M_USBDEV, M_WAITOK); - umodemstartread(sc); - } - sc->sc_opening = 0; - wakeup(&sc->sc_opening); - splx(s); - -#if defined(__NetBSD__) || defined(__OpenBSD__) - error = ttyopen(tp, UMODEMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); - if (error) - goto bad; -#elif defined(__FreeBSD__) - error = ttyopen(dev, tp); - if (error) - goto bad; -#endif - - error = (*linesw[tp->t_line].l_open)(dev, tp); - if (error) - goto bad; - - return (0); - -bad: -#if defined(__NetBSD__) || defined(__OpenBSD__) - if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { -#elif defined(__FreeBSD__) - if (!ISSET(tp->t_state, TS_ISOPEN)) { -#endif - /* - * We failed to open the device, and nobody else had it opened. - * Clean up the state as appropriate. - */ - umodem_cleanup(sc); - } - - return (error); -} - -usbd_status -umodemstartread(struct umodem_softc *sc) -{ - usbd_status err; - DPRINTFN(5,("umodemstartread: start\n")); - usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe, - (usbd_private_handle)sc, - sc->sc_ibuf, UMODEMIBUFSIZE, USBD_SHORT_XFER_OK, - USBD_NO_TIMEOUT, umodemreadcb); - - err = usbd_transfer(sc->sc_ixfer); - if (err != USBD_IN_PROGRESS) - return (err); - - return (USBD_NORMAL_COMPLETION); -} - -void -umodemreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) -{ - struct umodem_softc *sc = (struct umodem_softc *)p; - struct tty *tp = sc->sc_tty; - int (*rint) (int c, struct tty *tp) = linesw[tp->t_line].l_rint; - usbd_status err; - u_int32_t cc; - u_char *cp; - int s; - - if (status == USBD_CANCELLED) - return; - - if (status != USBD_NORMAL_COMPLETION) { - DPRINTF(("umodemreadcb: status=%d\n", status)); - usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); - /* XXX we should restart after some delay. */ - return; - } - - usbd_get_xfer_status(xfer, 0, (void **)&cp, &cc, 0); - DPRINTFN(5,("umodemreadcb: got %d chars, tp=%p\n", cc, tp)); - s = spltty(); - /* Give characters to tty layer. */ - while (cc-- > 0) { - DPRINTFN(7,("umodemreadcb: char=0x%02x\n", *cp)); - if ((*rint)(*cp++, tp) == -1) { - /* XXX what should we do? */ - break; - } - } - splx(s); - - err = umodemstartread(sc); + err = umodem_set_line_coding(sc, &ls); if (err) { - printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev)); - /* XXX what should we dow now? */ - } -} - -int -umodemclose(dev_t dev, int flag, int mode, usb_proc_ptr p) -{ - struct umodem_softc *sc; - struct tty *tp; - int s; - - USB_GET_SC(umodem, UMODEMUNIT(dev), sc); - - tp = sc->sc_tty; - - DPRINTF(("%s: umodemclose sc_tty=%p\n", USBDEVNAME(sc->sc_dev), tp)); - - if (!ISSET(tp->t_state, TS_ISOPEN)) - return (0); - - DPRINTF(("%s: umodemclose lclose(%p,%d)\n", USBDEVNAME(sc->sc_dev), tp,flag)); - - s=spltty(); - DPRINTF(("%s: umodemclose lclose=%p\n", USBDEVNAME(sc->sc_dev), linesw[tp->t_line].l_close)); - (*linesw[tp->t_line].l_close)(tp, flag); - - DPRINTF(("%s: umodemclose ttyclose(%p)\n", USBDEVNAME(sc->sc_dev), tp)); - ttyclose(tp); - splx(s); - - DPRINTF(("%s: umodemclose sc->sc_dying=%d\n", USBDEVNAME(sc->sc_dev), sc->sc_dying)); - if (sc->sc_dying) - return (0); - - DPRINTF(("%s: umodemclose tp->t_state=%d\n", USBDEVNAME(sc->sc_dev), tp->t_state)); -#if defined(__NetBSD__) || defined(__OpenBSD__) - if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { -#elif defined(__FreeBSD__) - if (!ISSET(tp->t_state, TS_ISOPEN)) { -#endif - /* - * 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. - */ - DPRINTF(("%s: umodemclose umodem_cleanup(%p)\n", USBDEVNAME(sc->sc_dev), sc)); - umodem_cleanup(sc); + DPRINTF(("umodem_param: err=%s\n", usbd_errstr(err))); + return (ENOTTY); } - DPRINTF(("%s: umodemclose return\n", USBDEVNAME(sc->sc_dev))); - return (0); } - -void -umodem_cleanup(struct umodem_softc *sc) -{ - umodem_shutdown(sc); - - DPRINTFN(5, ("%s: umodem_cleanup: closing pipes\n", - USBDEVNAME(sc->sc_dev))); - - usbd_abort_pipe(sc->sc_bulkin_pipe); - usbd_close_pipe(sc->sc_bulkin_pipe); - usbd_abort_pipe(sc->sc_bulkout_pipe); - usbd_close_pipe(sc->sc_bulkout_pipe); - usbd_free_xfer(sc->sc_ixfer); - usbd_free_xfer(sc->sc_oxfer); - free(sc->sc_ibuf, M_USBDEV); -} int -umodemread(dev_t dev, struct uio *uio, int flag) +umodem_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag, + usb_proc_ptr p) { - struct umodem_softc *sc; - struct tty *tp; - - USB_GET_SC(umodem, UMODEMUNIT(dev), sc); + struct umodem_softc *sc = addr; + int error = 0; - tp = sc->sc_tty; - - if (sc->sc_dying) + if (sc->sc_ucom.sc_dying) return (EIO); - - return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); -} - -int -umodemwrite(dev_t dev, struct uio *uio, int flag) -{ - struct umodem_softc *sc; - struct tty *tp; - - USB_GET_SC(umodem, UMODEMUNIT(dev), sc); - tp = sc->sc_tty; - - if (sc->sc_dying) - return (EIO); - - return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); -} - -void -umodemstop(struct tty *tp, int flag) -{ - struct umodem_softc *sc; - int s; - - USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc); - - DPRINTF(("umodemstop: %d\n", flag)); - s = spltty(); - if (ISSET(tp->t_state, TS_BUSY)) { - DPRINTF(("umodemstop: XXX\n")); - /* XXX do what? */ - if (!ISSET(tp->t_state, TS_TTSTOP)) - SET(tp->t_state, TS_FLUSH); - } - splx(s); -} - -struct tty * -umodemtty(dev_t dev) -{ - struct umodem_softc *sc; - struct tty *tp; - - USB_GET_SC(umodem, UMODEMUNIT(dev), sc); - - tp = sc->sc_tty; - - return (tp); -} - -int -umodemioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) -{ - struct umodem_softc *sc; - struct tty *tp; - int error; - int s; - int bits; - - USB_GET_SC(umodem, UMODEMUNIT(dev), sc); - - tp = sc->sc_tty; - - if (sc->sc_dying) - return (EIO); - DPRINTF(("umodemioctl: cmd=0x%08lx\n", cmd)); - error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); - if (error >= 0) - return (error); - -#if defined(__NetBSD__) || defined(__OpenBSD__) - error = ttioctl(tp, cmd, data, flag, p); -#elif defined(__FreeBSD__) - error = ttioctl(tp, cmd, data, flag); -#endif - if (error >= 0) - return (error); - - DPRINTF(("umodemioctl: our cmd=0x%08lx\n", cmd)); - s = spltty(); - switch (cmd) { - case TIOCSBRK: - umodem_break(sc, 1); - break; - - case TIOCCBRK: - umodem_break(sc, 0); - break; - - case TIOCSDTR: - umodem_modem(sc, 1); - break; - - case TIOCCDTR: - umodem_modem(sc, 0); - break; - - case TIOCMGET: - bits = TIOCM_LE; - if(sc->sc_dtr) - bits |= TIOCM_DTR; - *(int *)data = bits; - break; - - case TIOCMSET: - break; - case USB_GET_CM_OVER_DATA: *(int *)data = sc->sc_cm_over_data; break; @@ -974,53 +609,52 @@ umodemioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) default: DPRINTF(("umodemioctl: unknown\n")); error = ENOTTY; - splx(s); - return (error); + break; } - splx(s); - return(0); + return (error); } void -umodem_shutdown(struct umodem_softc *sc) +umodem_dtr(struct umodem_softc *sc, int onoff) { - struct tty *tp = sc->sc_tty; + DPRINTF(("umodem_modem: onoff=%d\n", onoff)); - DPRINTF(("%s: umodem_shutdown\n", USBDEVNAME(sc->sc_dev))); - /* - * 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)) { - umodem_modem(sc, 0); -#if defined(__NetBSD__) || defined(__OpenBSD__) - (void) tsleep(sc, 0, ttclos, hz); -#elif defined(__FreeBSD__) - (void) tsleep(sc, 0, "umdmsd", hz); -#endif - } + if (sc->sc_dtr == onoff) + return; + sc->sc_dtr = onoff; + + umodem_set_line_state(sc); } void -umodem_modem(struct umodem_softc *sc, int onoff) +umodem_rts(struct umodem_softc *sc, int onoff) { - usb_device_request_t req; + DPRINTF(("umodem_modem: onoff=%d\n", onoff)); - DPRINTF(("%s: umodem_modem: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff)); - - if (sc->sc_dtr == 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, onoff ? UCDC_LINE_DTR : 0); + 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); - sc->sc_dtr = onoff; } void @@ -1028,7 +662,7 @@ umodem_break(struct umodem_softc *sc, int onoff) { usb_device_request_t req; - DPRINTF(("%s: umodem_break: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff)); + DPRINTF(("umodem_break: onoff=%d\n", onoff)); if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) return; @@ -1042,6 +676,59 @@ umodem_break(struct umodem_softc *sc, int onoff) (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) { @@ -1068,6 +755,9 @@ umodem_set_comm_feature(struct umodem_softc *sc, int feature, int state) 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); @@ -1077,120 +767,29 @@ umodem_set_comm_feature(struct umodem_softc *sc, int feature, int state) err = usbd_do_request(sc->sc_udev, &req, &ast); if (err) { - DPRINTF(("%s: umodem_set_comm_feat: feature=%d failed, err=%d\n", - USBDEVNAME(sc->sc_dev), feature, err)); + DPRINTF(("umodem_set_comm_feature: feature=%d, err=%s\n", + feature, usbd_errstr(err))); return (err); } return (USBD_NORMAL_COMPLETION); } -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(("%s: umodem_set_line_cod: rate=%d fmt=%d parity=%d bits=%d\n", - USBDEVNAME(sc->sc_dev), UGETDW(state->dwDTERate), - state->bCharFormat, state->bParityType, state->bDataBits)); - - if (bcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) { - DPRINTF(("%s: umodem_set_line_coding: already set\n", - USBDEVNAME(sc->sc_dev))); - 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(("%s: umodem_set_line_coding: failed, err=%d\n", - USBDEVNAME(sc->sc_dev), err)); - return (err); - } - - sc->sc_line_state = *state; - - return (USBD_NORMAL_COMPLETION); -} - -#if defined(__NetBSD__) || defined(__OpenBSD__) -int -umodem_activate(device_ptr_t self, enum devact act) -{ - struct umodem_softc *sc = (struct umodem_softc *)self; - - switch (act) { - case DVACT_ACTIVATE: - return (EOPNOTSUPP); - break; - - case DVACT_DEACTIVATE: - sc->sc_dying = 1; - break; - } - return (0); -} -#endif - USB_DETACH(umodem) { USB_DETACH_START(umodem, sc); -#if defined(__NetBSD__) || defined(__OpenBSD__) - int maj, mn; - - DPRINTF(("umodem_detach: sc=%p flags=%d tp=%p\n", - sc, flags, sc->sc_tty)); -#elif defined(__FreeBSD__) - DPRINTF(("umodem_detach: sc=%p flags=%d, tp=%p\n", - sc, 0, sc->sc_tty)); -#endif + int rv = 0; - sc->sc_dying = 1; + DPRINTF(("umodem_detach: sc=%p\n", sc)); -#ifdef DIAGNOSTIC - if (sc->sc_tty == 0) { - DPRINTF(("umodem_detach: no tty\n")); - return (0); + 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; } -#endif - /* use refernce count? XXX */ + sc->sc_ucom.sc_dying = 1; + rv = ucom_detach(&sc->sc_ucom); -#if defined(__NetBSD__) || defined(__OpenBSD__) - /* locate the major number */ - for (maj = 0; maj < nchrdev; maj++) - if (cdevsw[maj].d_open == umodemopen) - break; - - /* Nuke the vnodes for any open instances. */ - mn = self->dv_unit; - vdevgone(maj, mn, mn, VCHR); - vdevgone(maj, mn, mn | UMODEMDIALOUT_MASK, VCHR); - vdevgone(maj, mn, mn | UMODEMCALLUNIT_MASK, VCHR); -#elif defined(__FreeBSD__) - /* XXX not yet implemented */ -#endif - -#if defined(__FreeBSD__) - destroy_dev(sc->dev); -#endif - - /* Detach and free the tty. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) - tty_detach(sc->sc_tty); - ttyfree(sc->sc_tty); - sc->sc_tty = 0; -#endif - - return (0); + return (rv); } - -#if defined(__FreeBSD__) -DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, usbd_driver_load,0); -#endif diff --git a/sys/dev/usbmisc/ums/ums.c b/sys/dev/usbmisc/ums/ums.c index 61538d5ad2..019c582ee1 100644 --- a/sys/dev/usbmisc/ums/ums.c +++ b/sys/dev/usbmisc/ums/ums.c @@ -1,5 +1,7 @@ -/* $FreeBSD: src/sys/dev/usb/ums.c,v 1.36.2.6 2002/11/06 20:23:50 joe Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/ums/ums.c,v 1.5 2003/08/07 21:17:15 dillon Exp $ */ +/* + * $FreeBSD: src/sys/dev/usb/ums.c,v 1.64 2003/11/09 09:17:22 tanimura Exp $ + * $DragonFly: src/sys/dev/usbmisc/ums/ums.c,v 1.6 2003/12/30 01:01:47 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -39,7 +41,7 @@ */ /* - * HID spec: http://www.usb.org/developers/data/usbhid10.pdf + * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf */ #include @@ -52,8 +54,11 @@ #include #include #include +#if __FreeBSD_version >= 500014 +#include +#else #include -#include +#endif #include #include #include @@ -67,7 +72,11 @@ #include #include +#if __FreeBSD_version >= 500000 +#include +#else #include +#endif #ifdef USB_DEBUG #define DPRINTF(x) if (umsdebug) logprintf x @@ -83,7 +92,7 @@ SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW, #define UMSUNIT(s) (minor(s)&0x1f) -#define MS_TO_TICKS(ms) ((ms) * hz / 1000) +#define MS_TO_TICKS(ms) ((ms) * hz / 1000) #define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */ @@ -99,7 +108,7 @@ struct ums_softc { struct hid_location sc_loc_x, sc_loc_y, sc_loc_z; struct hid_location *sc_loc_btn; - struct callout_handle timeout_handle; /* for spurious button ups */ + usb_callout_t callout_handle; /* for spurious button ups */ int sc_enabled; int sc_disconnected; /* device is gone */ @@ -174,18 +183,18 @@ USB_MATCH(ums) 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_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); + err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); if (err) return (UMATCH_NONE); - if (hid_is_collection(desc, size, + if (hid_is_collection(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) ret = UMATCH_IFACECLASS; else @@ -208,7 +217,7 @@ USB_ATTACH(ums) 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); @@ -226,7 +235,7 @@ USB_ATTACH(ums) DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", - ed->bLength, ed->bDescriptorType, + ed->bLength, ed->bDescriptorType, UE_GET_ADDR(ed->bEndpointAddress), UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out", UE_GET_XFERTYPE(ed->bmAttributes), @@ -239,7 +248,7 @@ USB_ATTACH(ums) USB_ATTACH_ERROR_RETURN; } - err = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); + err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); if (err) USB_ATTACH_ERROR_RETURN; @@ -283,7 +292,7 @@ USB_ATTACH(ums) hid_input, &loc_btn, 0)) break; sc->nbuttons = i - 1; - sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons, + 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)); @@ -311,12 +320,12 @@ USB_ATTACH(ums) #ifdef USB_DEBUG DPRINTF(("ums_attach: sc=%p\n", sc)); - DPRINTF(("ums_attach: X\t%d/%d\n", + 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", + 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", + 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", @@ -346,8 +355,10 @@ USB_ATTACH(ums) 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, @@ -367,7 +378,6 @@ Static int ums_detach(device_t self) { struct ums_softc *sc = device_get_softc(self); - struct vnode *vp; if (sc->sc_enabled) ums_disable(sc); @@ -377,10 +387,6 @@ ums_detach(device_t self) free(sc->sc_loc_btn, M_USB); free(sc->sc_ibuf, M_USB); - vp = SLIST_FIRST(&sc->dev->si_hlist); - if (vp) - VOP_REVOKE(vp, REVOKEALL); - /* someone waiting for data */ /* * XXX If we wakeup the process here, the device will be gone by @@ -395,7 +401,7 @@ ums_detach(device_t self) } if (sc->state & UMS_SELECT) { sc->state &= ~UMS_SELECT; - selwakeup(&sc->rsel); + selwakeuppri(&sc->rsel, PZERO); } destroy_dev(sc->dev); @@ -426,7 +432,8 @@ ums_intr(xfer, addr, status) if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ums_intr: status=%d\n", status)); - usbd_clear_endpoint_stall_async(sc->sc_intrpipe); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(sc->sc_intrpipe); return; } @@ -469,12 +476,11 @@ ums_intr(xfer, addr, status) */ if (sc->flags & UMS_SPUR_BUT_UP && dx == 0 && dy == 0 && dz == 0 && buttons == 0) { - usb_timeout(ums_add_to_queue_timeout, (void *) sc, - MS_TO_TICKS(50 /*msecs*/), sc->timeout_handle); + usb_callout(sc->callout_handle, MS_TO_TICKS(50 /*msecs*/), + ums_add_to_queue_timeout, (void *) sc); } else { - usb_untimeout(ums_add_to_queue_timeout, (void *) sc, - sc->timeout_handle); - + usb_uncallout(sc->callout_handle, + ums_add_to_queue_timeout, (void *) sc); ums_add_to_queue(sc, dx, dy, dz, buttons); } } @@ -534,7 +540,7 @@ ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons) } if (sc->state & UMS_SELECT) { sc->state &= ~UMS_SELECT; - selwakeup(&sc->rsel); + selwakeuppri(&sc->rsel, PZERO); } } Static int @@ -555,11 +561,11 @@ ums_enable(v) sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; - callout_handle_init(&sc->timeout_handle); + 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, + 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) { @@ -577,7 +583,7 @@ ums_disable(priv) { struct ums_softc *sc = priv; - usb_untimeout(ums_add_to_queue_timeout, sc, sc->timeout_handle); + usb_uncallout(sc->callout_handle, ums_add_to_queue_timeout, sc); /* Disable interrupts. */ usbd_abort_pipe(sc->sc_intrpipe); @@ -637,7 +643,7 @@ ums_read(dev_t dev, struct uio *uio, int flag) splx(s); return EWOULDBLOCK; } - + sc->state |= UMS_ASLEEP; /* blocking I/O */ error = tsleep(sc, PCATCH, "umsrea", 0); if (error) { @@ -710,7 +716,7 @@ ums_poll(dev_t dev, int events, usb_proc_ptr p) return revents; } - + int ums_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { diff --git a/sys/dev/usbmisc/uplcom/uplcom.c b/sys/dev/usbmisc/uplcom/uplcom.c index e5e7d18663..dd10ba0b4f 100644 --- a/sys/dev/usbmisc/uplcom/uplcom.c +++ b/sys/dev/usbmisc/uplcom/uplcom.c @@ -1,7 +1,7 @@ /* - * $NetBSD: uplcom.c,v 1.20 2001/07/31 12:33:11 ichiro Exp $ - * $FreeBSD: src/sys/dev/usb/uplcom.c,v 1.8.2.5 2003/11/30 13:05:37 akiyama Exp $ - * $DragonFly: src/sys/dev/usbmisc/uplcom/uplcom.c,v 1.4 2003/12/29 06:42:20 dillon Exp $ + * $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $ + * $FreeBSD: src/sys/dev/usb/uplcom.c,v 1.17 2003/11/16 13:13:16 akiyama Exp $ + * $DragonFly: src/sys/dev/usbmisc/uplcom/uplcom.c,v 1.5 2003/12/30 01:01:47 dillon Exp $ */ /*- @@ -313,7 +313,7 @@ USB_ATTACH(uplcom) DPRINTF(("uplcom attach: sc = %p\n", sc)); - /* initialize endpoints */ + /* initialize endpoints */ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1; sc->sc_intr_number = -1; sc->sc_intr_pipe = NULL; @@ -338,7 +338,7 @@ USB_ATTACH(uplcom) } /* get the (first/common) interface */ - err = usbd_device2interface_handle(dev, UPLCOM_IFACE_INDEX, + err = usbd_device2interface_handle(dev, UPLCOM_IFACE_INDEX, &ucom->sc_iface); if (err) { printf("%s: failed to get interface: %s\n", @@ -365,7 +365,7 @@ USB_ATTACH(uplcom) 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) { @@ -387,11 +387,11 @@ USB_ATTACH(uplcom) * Interrupt(0x81) | Interrupt(0x81) * -----------------+ BulkIN(0x02) * Interface 1 | BulkOUT(0x83) - * BulkIN(0x02) | + * BulkIN(0x02) | * BulkOUT(0x83) | */ if (cdesc->bNumInterface == 2) { - err = usbd_device2interface_handle(dev, + err = usbd_device2interface_handle(dev, UPLCOM_SECOND_IFACE_INDEX, &ucom->sc_iface); if (err) { @@ -400,7 +400,7 @@ USB_ATTACH(uplcom) ucom->sc_dying = 1; goto error; } - } + } /* Find the bulk{in,out} endpoints */ @@ -502,9 +502,9 @@ uplcom_reset(struct uplcom_softc *sc) req.bRequest = UPLCOM_SET_REQUEST; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_number); - USETW(req.wLength, 0); + USETW(req.wLength, 0); - err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 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)); @@ -711,7 +711,7 @@ uplcom_open(void *addr, int portno) { struct uplcom_softc *sc = addr; int err; - + if (sc->sc_ucom.sc_dying) return (ENXIO); @@ -741,7 +741,7 @@ uplcom_open(void *addr, int portno) } Static void -uplcom_close(void *addr, int portno) +uplcom_close(void *addr, int portno) { struct uplcom_softc *sc = addr; int err; diff --git a/sys/dev/usbmisc/urio/urio.c b/sys/dev/usbmisc/urio/urio.c index ff426cb1a9..ea55ef91d7 100644 --- a/sys/dev/usbmisc/urio/urio.c +++ b/sys/dev/usbmisc/urio/urio.c @@ -28,8 +28,10 @@ * its contributors. */ -/* $FreeBSD: src/sys/dev/usb/urio.c,v 1.11.2.4 2002/11/06 14:41:01 joe Exp $ */ -/* $DragonFly: src/sys/dev/usbmisc/urio/urio.c,v 1.6 2003/08/07 21:17:15 dillon Exp $ */ +/* + * $FreeBSD: src/sys/dev/usb/urio.c,v 1.28 2003/08/25 22:01:06 joe Exp $ + * $DragonFly: src/sys/dev/usbmisc/urio/urio.c,v 1.7 2003/12/30 01:01:47 dillon Exp $ + */ /* * 2000/3/24 added NetBSD/OpenBSD support (from Alex Nemirovsky) @@ -91,26 +93,18 @@ SYSCTL_INT(_hw_usb_urio, OID_AUTO, debug, CTLFLAG_RW, #endif /* difference of usbd interface */ -#if defined(__FreeBSD__) -#if (__FreeBSD__ >= 4) - #define USBDI 1 -#else - #define USBDI 0 -#endif -#elif defined(__NetBSD__) || defined(__OpenBSD__) - #define USBDI 1 -#endif +#define USBDI 1 #define RIO_OUT 0 #define RIO_IN 1 #define RIO_NODIR 2 #if defined(__NetBSD__) -int urioopen(dev_t, int, int, usb_proc_ptr); -int urioclose(dev_t, int, int, usb_proc_ptr); +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, usb_proc_ptr); +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 :\ @@ -125,7 +119,6 @@ d_ioctl_t urioioctl; #define URIO_CDEV_MAJOR 143 -#if (__FreeBSD__ >= 4) Static struct cdevsw urio_cdevsw = { /* name */ "urio", /* cmaj */ URIO_CDEV_MAJOR, @@ -139,17 +132,6 @@ Static struct cdevsw urio_cdevsw = { #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)) -#else -Static struct cdevsw urio_cdevsw = { - urioopen, urioclose, urioread, uriowrite, - urioioctl, nostop, nullreset, nodevtotty, - seltrue, nommap, nostrat, - "urio", NULL, -1 -}; -#define USBBASEDEVICE bdevice -#define RIO_UE_GET_DIR(p) UE_GET_IN(p) -#endif - #endif /*defined(__FreeBSD__)*/ #define URIO_BBSIZE 1024 @@ -165,6 +147,9 @@ struct urio_softc { int sc_epaddr[2]; int sc_refcnt; +#if defined(__FreeBSD__) + dev_t sc_dev_t; +#endif /* defined(__FreeBSD__) */ #if defined(__NetBSD__) || defined(__OpenBSD__) u_char sc_dying; #endif @@ -212,7 +197,7 @@ USB_ATTACH(urio) char * ermsg = ""; int i; - DPRINTFN(10,("urio_attach: sc=%p\n", sc)); + 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); @@ -233,12 +218,12 @@ USB_ATTACH(urio) r = usbd_interface_count(udev, &niface); if (r) { ermsg = "iface"; - goto nobulk; + goto nobulk; } r = usbd_device2interface_handle(udev, 0, &iface); if (r) { ermsg = "iface"; - goto nobulk; + goto nobulk; } sc->sc_iface = iface; #endif @@ -248,7 +233,7 @@ USB_ATTACH(urio) sc->sc_refcnt = 0; r = usbd_endpoint_count(iface, &epcount); - if (r != USBD_NORMAL_COMPLETION) { + if (r != USBD_NORMAL_COMPLETION) { ermsg = "endpoints"; goto nobulk; } @@ -257,7 +242,7 @@ USB_ATTACH(urio) sc->sc_epaddr[RIO_IN] = 0x00; for (i = 0; i < epcount; i++) { - usb_endpoint_descriptor_t *edesc = + usb_endpoint_descriptor_t *edesc = usbd_interface2endpoint_descriptor(iface, i); int d; @@ -265,7 +250,7 @@ USB_ATTACH(urio) ermsg = "interface endpoint"; goto nobulk; } - + d = RIO_UE_GET_DIR(edesc->bEndpointAddress); if (d != RIO_NODIR) sc->sc_epaddr[d] = edesc->bEndpointAddress; @@ -277,12 +262,10 @@ USB_ATTACH(urio) } #if defined(__FreeBSD__) - #if (__FreeBSD__ >= 4) /* XXX no error trapping, no storing of dev_t */ - (void) make_dev(&urio_cdevsw, device_get_unit(self), + sc->sc_dev_t = make_dev(&urio_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, 0644, "urio%d", device_get_unit(self)); - #endif #elif defined(__NetBSD__) || defined(__OpenBSD__) usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, USBDEV(sc->sc_dev)); @@ -307,7 +290,7 @@ urioopen(dev_t dev, int flag, int mode, usb_proc_ptr p) int unit = URIOUNIT(dev); USB_GET_SC_OPEN(urio, unit, sc); - DPRINTFN(5, ("urioopen: flag=%d, mode=%d, unit=%d\n", + DPRINTFN(5, ("urioopen: flag=%d, mode=%d, unit=%d\n", flag, mode, unit)); if (sc->sc_opened) @@ -319,18 +302,18 @@ urioopen(dev_t dev, int flag, int mode, usb_proc_ptr p) 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) + 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) + 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); + usbd_close_pipe(sc->sc_pipeh_in); sc->sc_pipeh_in = 0; sc->sc_pipeh_out = 0; return EIO; @@ -348,17 +331,17 @@ urioclose(dev_t dev, int flag, int mode, usb_proc_ptr p) 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_in) + usbd_close_pipe(sc->sc_pipeh_in); - if (sc->sc_pipeh_out) - usbd_close_pipe(sc->sc_pipeh_out); + 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; + return 0; } int @@ -531,7 +514,7 @@ urioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) 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", + 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; @@ -544,7 +527,7 @@ urioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) 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", + 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; @@ -571,7 +554,7 @@ urioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = - req.bmRequestType & UT_READ ? + req.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_td = p; ptr = malloc(len, M_TEMP, M_WAITOK); @@ -582,8 +565,9 @@ urioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) } } - r = usbd_do_request_flags(sc->sc_udev, &req, - ptr, req_flags, &req_actlen); + 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) { @@ -655,11 +639,11 @@ USB_DETACH(urio) } splx(s); #else - if (sc->sc_pipeh_in) - usbd_abort_pipe(sc->sc_pipeh_in); + if (sc->sc_pipeh_in) + usbd_abort_pipe(sc->sc_pipeh_in); - if (sc->sc_pipeh_out) - usbd_abort_pipe(sc->sc_pipeh_out); + if (sc->sc_pipeh_out) + usbd_abort_pipe(sc->sc_pipeh_out); s = splusb(); if (--sc->sc_refcnt >= 0) { @@ -678,8 +662,6 @@ USB_DETACH(urio) /* 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, @@ -692,17 +674,15 @@ USB_DETACH(urio) #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; } -#if (__FreeBSD__ >= 4) DRIVER_MODULE(urio, uhub, urio_driver, urio_devclass, usbd_driver_load, 0); -#else -CDEV_DRIVER_MODULE(urio, uhub, urio_driver, urio_devclass, - URIO_CDEV_MAJOR, urio_cdevsw, usbd_driver_load, 0); -#endif - #endif diff --git a/sys/dev/usbmisc/uscanner/uscanner.c b/sys/dev/usbmisc/uscanner/uscanner.c index ab5032e586..e95013c9ac 100644 --- a/sys/dev/usbmisc/uscanner/uscanner.c +++ b/sys/dev/usbmisc/uscanner/uscanner.c @@ -1,7 +1,11 @@ /* - * $NetBSD: uscanner.c,v 1.26 2001/12/31 12:15:22 augustss Exp $ - * $FreeBSD: src/sys/dev/usb/uscanner.c,v 1.2.2.16 2003/12/22 20:00:55 sanpei Exp $ - * $DragonFly: src/sys/dev/usbmisc/uscanner/uscanner.c,v 1.5 2003/12/29 06:42:21 dillon Exp $ + * $NetBSD: uscanner.c,v 1.30 2002/07/11 21:14:36 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/uscanner.c,v 1.48 2003/12/22 19:58:27 sanpei Exp $ + * $DragonFly: src/sys/dev/usbmisc/uscanner/uscanner.c,v 1.6 2003/12/30 01:01:48 dillon Exp $ + */ + +/* Also already merged from NetBSD: + * $NetBSD: uscanner.c,v 1.33 2002/09/23 05:51:24 simonb Exp $ */ /* @@ -58,7 +62,11 @@ #endif #include #include +#if __FreeBSD_version >= 500014 +#include +#else #include +#endif #include #include #include @@ -194,6 +202,7 @@ static const struct uscan_info uscanner_devs[] = { {{ 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 }, @@ -218,6 +227,9 @@ struct uscanner_softc { USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; usbd_interface_handle sc_iface; +#if defined(__FreeBSD__) + dev_t dev; +#endif u_int sc_dev_flags; @@ -249,7 +261,6 @@ d_open_t uscanneropen; d_close_t uscannerclose; d_read_t uscannerread; d_write_t uscannerwrite; -d_ioctl_t uscannerioctl; d_poll_t uscannerpoll; #define USCANNER_CDEV_MAJOR 156 @@ -265,7 +276,7 @@ Static struct cdevsw uscanner_cdevsw = { /* close */ uscannerclose, /* read */ uscannerread, /* write */ uscannerwrite, - /* ioctl */ uscannerioctl, + /* ioctl */ noioctl, /* poll */ uscannerpoll, /* mmap */ nommap, /* strategy */ nostrategy, @@ -360,10 +371,13 @@ USB_ATTACH(uscanner) #ifdef __FreeBSD__ /* the main device, ctrl endpoint */ - make_dev(&uscanner_cdevsw, USBDEVUNIT(sc->sc_dev), + 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; } @@ -376,7 +390,7 @@ uscanneropen(dev_t dev, int flag, int mode, usb_proc_ptr p) USB_GET_SC_OPEN(uscanner, unit, sc); - DPRINTFN(5, ("uscanneropen: flag=%d, mode=%d, unit=%d\n", + DPRINTFN(5, ("uscanneropen: flag=%d, mode=%d, unit=%d\n", flag, mode, unit)); if (sc->sc_dying) @@ -602,7 +616,6 @@ uscanner_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: sc->sc_dying = 1; @@ -618,9 +631,6 @@ USB_DETACH(uscanner) int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; -#elif defined(__FreeBSD__) - dev_t dev; - struct vnode *vp; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) @@ -656,13 +666,12 @@ USB_DETACH(uscanner) vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR); #elif defined(__FreeBSD__) /* destroy the device for the control endpoint */ - dev = makedev(USCANNER_CDEV_MAJOR, USBDEVUNIT(sc->sc_dev)); - vp = SLIST_FIRST(&dev->si_hlist); - if (vp) - VOP_REVOKE(vp, REVOKEALL); - destroy_dev(dev); + destroy_dev(sc->dev); #endif + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + return (0); } @@ -677,23 +686,17 @@ uscannerpoll(dev_t dev, int events, usb_proc_ptr p) 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 & + revents |= events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM); return (revents); } -int -uscannerioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) -{ - return (EINVAL); -} - #if defined(__FreeBSD__) DRIVER_MODULE(uscanner, uhub, uscanner_driver, uscanner_devclass, usbd_driver_load, 0); #endif diff --git a/sys/dev/usbmisc/uvisor/uvisor.c b/sys/dev/usbmisc/uvisor/uvisor.c index 011c51bd9d..9900bc4913 100644 --- a/sys/dev/usbmisc/uvisor/uvisor.c +++ b/sys/dev/usbmisc/uvisor/uvisor.c @@ -1,19 +1,20 @@ /* * $NetBSD: uvisor.c,v 1.9 2001/01/23 14:04:14 augustss Exp $ - * $FreeBSD: src/sys/dev/usb/uvisor.c,v 1.7.2.7 2003/11/12 00:19:50 joe Exp $ - * $DragonFly: src/sys/dev/usbmisc/uvisor/uvisor.c,v 1.4 2003/12/29 06:42:22 dillon Exp $ + * $FreeBSD: src/sys/dev/usb/uvisor.c,v 1.16 2003/11/08 11:23:07 joe Exp $ + * $DragonFly: src/sys/dev/usbmisc/uvisor/uvisor.c,v 1.5 2003/12/30 01:01:48 dillon Exp $ */ -/* This version of uvisor is heavily based upon the version in NetBSD - * but is missing the following patches: - * - * 1.10 needed? connect a ucom to each of the uvisor ports - * 1.11 needed ucom has an "info" attach message - use it - * 1.12 not needed rcsids - * 1.13 already merged extra arg to usbd_do_request_flags - * 1.14 already merged sony and palm support - * 1.15 already merged sony clie - * 1.16 already merged trailing whites +/* + * 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 $ */ /* @@ -231,7 +232,7 @@ static const struct uvisor_type uvisor_devs[] = { USB_MATCH(uvisor) { USB_MATCH_START(uvisor, uaa); - + if (uaa->iface != NULL) return (UMATCH_NONE); @@ -305,7 +306,7 @@ USB_ATTACH(uvisor) ": %s\n", devname, usbd_errstr(err)); goto bad; } - + addr = ed->bEndpointAddress; dir = UE_GET_DIR(ed->bEndpointAddress); attr = ed->bmAttributes & UE_XFERTYPE; @@ -328,7 +329,7 @@ USB_ATTACH(uvisor) USBDEVNAME(ucom->sc_dev)); goto bad; } - + ucom->sc_parent = sc; ucom->sc_portno = UCOM_UNK_PORTNO; /* bulkin, bulkout set above */ @@ -345,6 +346,9 @@ USB_ATTACH(uvisor) 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); @@ -389,6 +393,9 @@ USB_DETACH(uvisor) 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); } @@ -409,7 +416,8 @@ uvisor_init(struct uvisor_softc *sc) 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_SHORT_XFER_OK, &actlen, + USBD_DEFAULT_TIMEOUT); if (err) return (err); @@ -436,7 +444,7 @@ uvisor_init(struct uvisor_softc *sc) break; default: string = "unknown"; - break; + break; } printf("%s: port %d, is for %s\n", USBDEVNAME(sc->sc_ucom.sc_dev), coninfo.connections[i].port, @@ -497,5 +505,6 @@ uvisor_close(void *addr, int portno) 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_SHORT_XFER_OK, &actlen, + USBD_DEFAULT_TIMEOUT); } diff --git a/sys/dev/usbmisc/uvscom/uvscom.c b/sys/dev/usbmisc/uvscom/uvscom.c index 1e56652604..fe82355a34 100644 --- a/sys/dev/usbmisc/uvscom/uvscom.c +++ b/sys/dev/usbmisc/uvscom/uvscom.c @@ -1,4 +1,3 @@ -/* $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $ */ /*- * Copyright (c) 2001-2002, Shunsuke Akiyama . * All rights reserved. @@ -24,8 +23,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/usb/uvscom.c,v 1.9.2.3 2003/02/13 13:03:25 sanpei Exp $ - * $DragonFly: src/sys/dev/usbmisc/uvscom/uvscom.c,v 1.4 2003/08/07 21:17:15 dillon Exp $ + * $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $ + * $FreeBSD: src/sys/dev/usb/uvscom.c,v 1.19 2003/11/16 12:26:10 akiyama Exp $ + * $DragonFly: src/sys/dev/usbmisc/uvscom/uvscom.c,v 1.5 2003/12/30 01:01:48 dillon Exp $ */ /* @@ -661,7 +661,7 @@ uvscom_param(void *addr, int portno, struct termios *t) default: return (EIO); } - + if (ISSET(t->c_cflag, CSTOPB)) SET(ls, UVSCOM_STOP_BIT_2); else @@ -711,7 +711,7 @@ uvscom_open(void *addr, int portno) struct uvscom_softc *sc = addr; int err; int i; - + if (sc->sc_ucom.sc_dying) return (ENXIO); diff --git a/sys/net/if.c b/sys/net/if.c index 8581a9d5a5..0ca8c32ee4 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -32,7 +32,7 @@ * * @(#)if.c 8.3 (Berkeley) 1/4/94 * $FreeBSD: src/sys/net/if.c,v 1.85.2.23 2003/04/15 18:11:19 fjoe Exp $ - * $DragonFly: src/sys/net/if.c,v 1.9 2003/11/09 02:22:36 dillon Exp $ + * $DragonFly: src/sys/net/if.c,v 1.10 2003/12/30 01:01:48 dillon Exp $ */ #include "opt_compat.h" @@ -1647,6 +1647,23 @@ ifmaof_ifpforaddr(sa, ifp) return ifma; } +/* + * The name argument must be a pointer to storage which will last as + * long as the interface does. For physical devices, the result of + * device_get_name(dev) is a good choice and for pseudo-devices a + * static string works well. + */ +void +if_initname(struct ifnet *ifp, const char *name, int unit) +{ + ifp->if_name = name; /* XXX change to dname */ + ifp->if_unit = unit; /* XXX change to dunit */ + if (unit != IF_DUNIT_NONE) + snprintf(ifp->if_xname, IFNAMSIZ, "%s%d", name, unit); + else + strlcpy(ifp->if_xname, name, IFNAMSIZ); +} + int if_printf(struct ifnet *ifp, const char *fmt, ...) { diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 0a32b0975d..8f9ae91b1e 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -32,7 +32,7 @@ * * From: @(#)if.h 8.1 (Berkeley) 6/10/93 * $FreeBSD: src/sys/net/if_var.h,v 1.18.2.16 2003/04/15 18:11:19 fjoe Exp $ - * $DragonFly: src/sys/net/if_var.h,v 1.5 2003/11/22 19:30:56 asmodai Exp $ + * $DragonFly: src/sys/net/if_var.h,v 1.6 2003/12/30 01:01:48 dillon Exp $ */ #ifndef _NET_IF_VAR_H_ @@ -82,6 +82,8 @@ struct ether_header; #include /* XXX */ #endif /* _KERNEL */ +#define IF_DUNIT_NONE -1 + TAILQ_HEAD(ifnethead, ifnet); /* we use TAILQs so that the order of */ TAILQ_HEAD(ifaddrhead, ifaddr); /* instantiation is preserved in the list */ TAILQ_HEAD(ifprefixhead, ifprefix); @@ -132,12 +134,15 @@ struct ifqueue { */ struct ifnet { void *if_softc; /* pointer to driver state */ - char *if_name; /* name, e.g. ``en'' or ``lo'' */ + /* XXX if_name -> if_dname */ + const char *if_name; /* name, e.g. ``en'' or ``lo'' */ TAILQ_ENTRY(ifnet) if_link; /* all struct ifnets are chained */ + char if_xname[IFNAMSIZ]; /* external name (name + unit) */ struct ifaddrhead if_addrhead; /* linked list of addresses per if */ int if_pcount; /* number of promiscuous listeners */ struct bpf_if *if_bpf; /* packet filter structure */ u_short if_index; /* numeric abbreviation for this if */ + /* XXX if_unit -> if_dunit */ short if_unit; /* sub-unit for lower level driver */ short if_timer; /* time 'til if_watchdog called */ short if_flags; /* up/down, broadcast, etc. */ @@ -401,6 +406,7 @@ void if_attach(struct ifnet *); int if_delmulti(struct ifnet *, struct sockaddr *); void if_detach(struct ifnet *); void if_down(struct ifnet *); +void if_initname(struct ifnet *, const char *, int); int if_printf(struct ifnet *, const char *, ...) __printflike(2, 3); void if_route(struct ifnet *, int flag, int fam); int if_setlladdr(struct ifnet *, const u_char *, int); diff --git a/usr.sbin/usbd/usbd.8 b/usr.sbin/usbd/usbd.8 index c595e8f974..f943002d17 100644 --- a/usr.sbin/usbd/usbd.8 +++ b/usr.sbin/usbd/usbd.8 @@ -1,4 +1,3 @@ -.\" $NetBSD: usbd.8,v 1.2 1998/07/13 11:01:50 augustss Exp $ .\" Copyright (c) 1998 The NetBSD Foundation, Inc. .\" All rights reserved. .\" @@ -32,8 +31,9 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.\" $FreeBSD: src/usr.sbin/usbd/usbd.8,v 1.9.2.9 2003/05/10 23:45:44 murray Exp $ -.\" $DragonFly: src/usr.sbin/usbd/usbd.8,v 1.2 2003/06/17 04:30:03 dillon Exp $ +.\" $NetBSD: usbd.8,v 1.2 1998/07/13 11:01:50 augustss Exp $ +.\" $FreeBSD: src/usr.sbin/usbd/usbd.8,v 1.20 2003/05/04 22:13:00 murray Exp $ +.\" $DragonFly: src/usr.sbin/usbd/usbd.8,v 1.3 2003/12/30 01:01:48 dillon Exp $ .\" .Dd July 12, 1998 .Dt USBD 8 diff --git a/usr.sbin/usbd/usbd.c b/usr.sbin/usbd/usbd.c index 892bdfa268..e60badcc87 100644 --- a/usr.sbin/usbd/usbd.c +++ b/usr.sbin/usbd/usbd.c @@ -1,6 +1,8 @@ -/* $NetBSD: usbd.c,v 1.4 1998/12/09 00:57:19 augustss Exp $ */ -/* $FreeBSD: src/usr.sbin/usbd/usbd.c,v 1.10.2.6 2002/12/31 09:05:27 maxim Exp $ */ -/* $DragonFly: src/usr.sbin/usbd/usbd.c,v 1.4 2003/11/03 19:31:44 eirikn Exp $ */ +/* + * $NetBSD: usbd.c,v 1.4 1998/12/09 00:57:19 augustss Exp $ + * $FreeBSD: src/usr.sbin/usbd/usbd.c,v 1.29 2003/10/25 22:03:10 jmg Exp $ + * $DragonFly: src/usr.sbin/usbd/usbd.c,v 1.5 2003/12/30 01:01:48 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -55,11 +57,14 @@ #include #include #include +#include #include -#include -#include #include +#include +#include +#include #include +#include #include #include @@ -162,8 +167,8 @@ typedef struct action_match_s { /* the function returns 0 for failure, 1 for all arguments found and 2 for * arguments left over in trail. */ -typedef int (*config_field_fn)(action_t *action, char *args, - char **trail); +typedef int (*config_field_fn) __P((action_t *action, char *args, + char **trail)); int set_device_field(action_t *action, char *args, char **trail); int set_vendor_field(action_t *action, char *args, char **trail); @@ -202,11 +207,11 @@ config_field_t config_fields[] = { /* prototypes for some functions */ -void print_event(struct usb_event *event); -void print_action(action_t *action, int i); -void print_actions(void); -int find_action(struct usb_device_info *devinfo, - action_match_t *action_match); +void print_event __P((struct usb_event *event)); +void print_action __P((action_t *action, int i)); +void print_actions __P((void)); +int find_action __P((struct usb_device_info *devinfo, + action_match_t *action_match)); void @@ -424,7 +429,7 @@ read_configuration(void) char *field; /* first part, the field name */ char *args; /* second part, arguments */ char *trail; /* remaining part after parsing, should be '' */ - int len; /* length of current line */ + size_t len; /* length of current line */ int i,j; /* loop counters */ action_t *action = NULL; /* current action */ @@ -909,7 +914,7 @@ process_event_queue(int fd) break; case USB_EVENT_DRIVER_ATTACH: if (verbose) - printf("USB_EVENT_DRIVER_DETACH\n"); + printf("USB_EVENT_DRIVER_ATTACH\n"); break; case USB_EVENT_DRIVER_DETACH: if (verbose) @@ -927,8 +932,6 @@ main(int argc, char **argv) { int error, i; int ch; /* getopt option */ - extern char *optarg; /* from getopt */ - extern int optind; /* from getopt */ int debug = 0; /* print debugging output */ int explore_once = 0; /* don't do only explore */ int handle_events = 1; /* do handle the event queue */ diff --git a/usr.sbin/usbd/usbd.conf.5 b/usr.sbin/usbd/usbd.conf.5 index 9ab1d51cd2..666ee19bca 100644 --- a/usr.sbin/usbd/usbd.conf.5 +++ b/usr.sbin/usbd/usbd.conf.5 @@ -23,8 +23,8 @@ .\" (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: src/usr.sbin/usbd/usbd.conf.5,v 1.6.2.7 2003/03/11 22:31:34 trhodes Exp $ -.\" $DragonFly: src/usr.sbin/usbd/usbd.conf.5,v 1.2 2003/06/17 04:30:03 dillon Exp $ +.\" $FreeBSD: src/usr.sbin/usbd/usbd.conf.5,v 1.13 2003/01/30 22:38:54 trhodes Exp $ +.\" $DragonFly: src/usr.sbin/usbd/usbd.conf.5,v 1.3 2003/12/30 01:01:48 dillon Exp $ .\" .\" Many parts of this manual have been snarfed from the pccard.conf (5) man .\" page, copyright by Andrew McRae. diff --git a/usr.sbin/usbdevs/usbdevs.8 b/usr.sbin/usbdevs/usbdevs.8 index e8be3d27d1..ac47ac0d2b 100644 --- a/usr.sbin/usbdevs/usbdevs.8 +++ b/usr.sbin/usbdevs/usbdevs.8 @@ -1,4 +1,3 @@ -.\" $NetBSD: usbdevs.8,v 1.5 2000/10/15 12:44:11 bjh21 Exp $ .\" Copyright (c) 1999 The NetBSD Foundation, Inc. .\" All rights reserved. .\" @@ -32,8 +31,9 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.\" $FreeBSD: src/usr.sbin/usbdevs/usbdevs.8,v 1.5.2.3 2003/03/12 22:08:16 trhodes Exp $ -.\" $DragonFly: src/usr.sbin/usbdevs/usbdevs.8,v 1.2 2003/06/17 04:30:03 dillon Exp $ +.\" $NetBSD: usbdevs.8,v 1.5 2000/10/15 12:44:11 bjh21 Exp $ +.\" $FreeBSD: src/usr.sbin/usbdevs/usbdevs.8,v 1.10 2002/07/14 14:46:46 charnier Exp $ +.\" $DragonFly: src/usr.sbin/usbdevs/usbdevs.8,v 1.3 2003/12/30 01:01:48 dillon Exp $ .\" .Dd October 15, 2000 .Dt USBDEVS 8 diff --git a/usr.sbin/usbdevs/usbdevs.c b/usr.sbin/usbdevs/usbdevs.c index 15c3ddebd1..94553989f1 100644 --- a/usr.sbin/usbdevs/usbdevs.c +++ b/usr.sbin/usbdevs/usbdevs.c @@ -1,6 +1,8 @@ -/* $NetBSD: usbdevs.c,v 1.17 2001/02/19 23:22:48 cgd Exp $ */ -/* $FreeBSD: src/usr.sbin/usbdevs/usbdevs.c,v 1.5.2.3 2002/11/13 15:15:21 joe Exp $ */ -/* $DragonFly: src/usr.sbin/usbdevs/usbdevs.c,v 1.3 2003/08/08 04:18:48 dillon Exp $ */ +/* + * $NetBSD: usbdevs.c,v 1.17 2001/02/19 23:22:48 cgd Exp $ + * $FreeBSD: src/usr.sbin/usbdevs/usbdevs.c,v 1.8 2002/04/22 13:44:47 des Exp $ + * $DragonFly: src/usr.sbin/usbdevs/usbdevs.c,v 1.4 2003/12/30 01:01:48 dillon Exp $ + */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -65,7 +67,7 @@ int main(int, char **); void usage() { - fprintf(stderr, "Usage: %s [-a addr] [-d] [-f dev] [-v]\n", + fprintf(stderr, "usage: %s [-a addr] [-d] [-f dev] [-v]\n", getprogname()); exit(1); } @@ -89,8 +91,12 @@ usbdev(int f, int a, int rec) printf("addr %d: ", a); done[a] = 1; if (verbose) { - if (di.udi_lowspeed) - printf("low speed, "); + switch (di.udi_speed) { + case USB_SPEED_LOW: printf("low speed, "); break; + case USB_SPEED_FULL: printf("full speed, "); break; + case USB_SPEED_HIGH: printf("high speed, "); break; + default: break; + } if (di.udi_power) printf("power %d mA, ", di.udi_power); else -- 2.41.0