Sync USB support (host controller part) with FreeBSD6.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 10 Dec 2006 02:03:57 +0000 (02:03 +0000)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 10 Dec 2006 02:03:57 +0000 (02:03 +0000)
Following changes on FreeBSD-CURRENT(by iedowse@freebsd.org) are merged:
ehci.c rev 1.52
ohci.c rev 1.167
uhci.c rev 1.172
usb.c rev 1.111
usbdi.h rev 1.61
"Use a different task queue for host controller and peripheral driver
 tasks. Since the host controllers rely on tasks to process transfer
 timeouts, if a synchronous transfer from a driver was invoked from
 a task and timed out, it would never complete because the single
 task thread was stuck performing the synchronous transfer so couldn't
 process the timeout."

As of this commit, only following kernel APIs are changed:
- usbd_get_string_desc() takes one more argument which returns the actual
  size of the string description.
- usb_add_task() takes one more argument.  The extra arguemnt is used
  to specify to which task queue the task should be added.  There is
  two task queues defined, one for peripheral driver (USB_TASKQ_DRIVER),
  one for host controller (USB_TASKQ_HC).

ugen(4) is adapted according to the above kernel API changes.

Thank all of the folks for their work on the USB support.

Tested-by:
swildner@, vbd@, joerg@(1.6 backport), Max Herrgard <herrgard@gmail.com>

# Though VT6202 is supported by ehci(4), I still have problems with this
# chip, even with the updated ehci(4).

26 files changed:
sys/bus/usb/ehci.c
sys/bus/usb/ehci_pci.c
sys/bus/usb/ehcireg.h
sys/bus/usb/ehcivar.h
sys/bus/usb/hid.c
sys/bus/usb/ohci.c
sys/bus/usb/ohci_pci.c
sys/bus/usb/ohcireg.h
sys/bus/usb/ohcivar.h
sys/bus/usb/uhci.c
sys/bus/usb/uhci_pci.c
sys/bus/usb/uhcireg.h
sys/bus/usb/uhcivar.h
sys/bus/usb/uhub.c
sys/bus/usb/usb.c
sys/bus/usb/usb.h
sys/bus/usb/usb_ethersubr.c
sys/bus/usb/usb_quirks.c
sys/bus/usb/usb_quirks.h
sys/bus/usb/usb_subr.c
sys/bus/usb/usbdi.c
sys/bus/usb/usbdi.h
sys/bus/usb/usbdi_util.c
sys/bus/usb/usbdi_util.h
sys/bus/usb/usbdivar.h
sys/dev/usbmisc/ugen/ugen.c

index be35710..47eafb5 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $NetBSD: ehci.c,v 1.67 2004/07/06 04:18:05 mycroft 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.25 2006/10/25 20:55:51 dillon Exp $
- */
+/*     $NetBSD: ehci.c,v 1.91 2005/02/27 00:27:51 perry Exp $ */
+/*     $FreeBSD: src/sys/dev/usb/ehci.c,v 1.36.2.3 2006/09/24 13:39:04 iedowse Exp $   */
+/*     $DragonFly: src/sys/bus/usb/ehci.c,v 1.26 2006/12/10 02:03:56 sephe Exp $       */
 
 /*
  * Copyright (c) 2004 The NetBSD Foundation, Inc.
 
 /*
  * TODO:
- * 1) hold off explorations by companion controllers until ehci has started.
- *
- * 2) The EHCI driver lacks support for isochronous transfers, so
+ * 1) The EHCI driver lacks support for isochronous transfers, so
  *    devices using them don't work.
  *
- * 3) The hub driver needs to handle and schedule the transaction translator,
- *    to assign place in frame where different devices get to go. See chapter
- *    on hubs in USB 2.0 for details.
+ * 2) Interrupt transfer scheduling does not manage the time available
+ *    in each frame, so it is possible for the transfers to overrun
+ *    the end of the frame.
  *
- * 4) command failures are not recovered correctly
+ * 3) Command failures are not recovered correctly.
  */
 
 #include <sys/param.h>
@@ -75,7 +71,9 @@
 #include <sys/proc.h>
 #include <sys/queue.h>
 #include <sys/sysctl.h>
+#ifdef __DragonFly__
 #include <sys/thread2.h>
+#endif
 
 #include <machine/cpu.h>
 #include <machine/endian.h>
 #include <bus/usb/ehcivar.h>
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-#include <machine/clock.h>
-
 #define delay(d)                DELAY(d)
 #endif
 
 #ifdef USB_DEBUG
-#define DPRINTF(x)     do { if (ehcidebug) logprintf x; } while(0)
-#define DPRINTFN(n,x)  do { if (ehcidebug>(n)) logprintf x; } while(0)
+#define EHCI_DEBUG USB_DEBUG
+#define DPRINTF(x)     do { if (ehcidebug) logprintf x; } while (0)
+#define DPRINTFN(n,x)  do { if (ehcidebug>(n)) logprintf x; } while (0)
 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,
@@ -112,7 +109,6 @@ SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW,
 
 struct ehci_pipe {
        struct usbd_pipe pipe;
-       int nexttoggle;
 
        ehci_soft_qh_t *sqh;
        union {
@@ -127,7 +123,9 @@ struct ehci_pipe {
                        /*ehci_soft_qtd_t *setup, *data, *stat;*/
                } ctl;
                /* Interrupt pipe */
-               /* XXX */
+               struct {
+                       u_int length;
+               } intr;
                /* Bulk pipe */
                struct {
                        u_int length;
@@ -137,11 +135,6 @@ struct ehci_pipe {
        } 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 *);
@@ -198,7 +191,7 @@ 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, const char *);
+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 *);
@@ -217,6 +210,9 @@ Static void         ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *,
 
 Static usbd_status     ehci_device_request(usbd_xfer_handle xfer);
 
+Static usbd_status     ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *,
+                           int ival);
+
 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 *);
@@ -226,7 +222,7 @@ 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
+#ifdef EHCI_DEBUG
 Static void            ehci_dump_regs(ehci_softc_t *);
 void                   ehci_dump(void);
 Static ehci_softc_t    *theehci;
@@ -325,9 +321,10 @@ ehci_init(ehci_softc_t *sc)
        usbd_status err;
        ehci_soft_qh_t *sqh;
        u_int ncomp;
+       int lev;
 
        DPRINTF(("ehci_init: start\n"));
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        theehci = sc;
 #endif
 
@@ -369,7 +366,6 @@ ehci_init(ehci_softc_t *sc)
        sc->sc_bus.usbrev = USBREV_2_0;
 
        /* Reset the controller */
-       EOWRITE4(sc, EHCI_USBINTR, 0);  /* disable interrupts */
        DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev)));
        EOWRITE4(sc, EHCI_USBCMD, 0);   /* Halt controller */
        usb_delay_ms(&sc->sc_bus, 1);
@@ -388,16 +384,18 @@ ehci_init(ehci_softc_t *sc)
 
        /* 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 0: sc->sc_flsize = 1024; break;
+       case 1: sc->sc_flsize = 512; break;
+       case 2: sc->sc_flsize = 256; break;
        case 3: return (USBD_IOERROR);
        }
-       err = usb_allocmem(&sc->sc_bus, sc->sc_flsize,
+       err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t),
                           EHCI_FLALIGN_ALIGN, &sc->sc_fldma);
        if (err)
                return (err);
        DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize));
+       sc->sc_flist = KERNADDR(&sc->sc_fldma, 0);
+       EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0));
 
        /* Set up the bus struct. */
        sc->sc_bus.methods = &ehci_bus_methods;
@@ -410,6 +408,49 @@ ehci_init(ehci_softc_t *sc)
 
        sc->sc_eintrs = EHCI_NORMAL_INTRS;
 
+       /*
+        * Allocate the interrupt dummy QHs. These are arranged to give
+        * poll intervals that are powers of 2 times 1ms.
+        */
+       for (i = 0; i < EHCI_INTRQHS; i++) {
+               sqh = ehci_alloc_sqh(sc);
+               if (sqh == NULL) {
+                       err = USBD_NOMEM;
+                       goto bad1;
+               }
+               sc->sc_islots[i].sqh = sqh;
+       }
+       lev = 0;
+       for (i = 0; i < EHCI_INTRQHS; i++) {
+               if (i == EHCI_IQHIDX(lev + 1, 0))
+                       lev++;
+               sqh = sc->sc_islots[i].sqh;
+               if (i == 0) {
+                       /* The last (1ms) QH terminates. */
+                       sqh->qh.qh_link = EHCI_NULL;
+                       sqh->next = NULL;
+               } else {
+                       /* Otherwise the next QH has half the poll interval */
+                       sqh->next =
+                           sc->sc_islots[EHCI_IQHIDX(lev - 1, i + 1)].sqh;
+                       sqh->qh.qh_link = htole32(sqh->next->physaddr |
+                           EHCI_LINK_QH);
+               }
+               sqh->qh.qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH));
+               sqh->qh.qh_endphub = htole32(EHCI_QH_SET_MULT(1));
+               sqh->qh.qh_curqtd = EHCI_NULL;
+               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;
+       }
+       /* Point the frame list at the last level (128ms). */
+       for (i = 0; i < sc->sc_flsize; i++) {
+               sc->sc_flist[i] = htole32(EHCI_LINK_QH |
+                   sc->sc_islots[EHCI_IQHIDX(EHCI_IPOLLRATES - 1,
+                   i)].sqh->physaddr);
+       }
+
        /* Allocate dummy QH that starts the async list. */
        sqh = ehci_alloc_sqh(sc);
        if (sqh == NULL) {
@@ -422,13 +463,14 @@ ehci_init(ehci_softc_t *sc)
        sqh->qh.qh_link =
            htole32(sqh->physaddr | EHCI_LINK_QH);
        sqh->qh.qh_curqtd = EHCI_NULL;
-       sqh->next = NULL;
+       sqh->prev = sqh; /*It's a circular list.. */
+       sqh->next = sqh;
        /* 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
+#ifdef EHCI_DEBUG
        if (ehcidebug) {
                ehci_dump_sqh(sqh);
        }
@@ -443,12 +485,15 @@ ehci_init(ehci_softc_t *sc)
 
        lockinit(&sc->sc_doorbell_lock, "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 */
+                EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */
                 (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) |
                 EHCI_CMD_ASE |
-                /* EHCI_CMD_PSE | */
+                EHCI_CMD_PSE |
                 EHCI_CMD_RS);
 
        /* Take over port ownership */
@@ -476,23 +521,20 @@ ehci_init(ehci_softc_t *sc)
        return (err);
 }
 
-void
-ehci_init_intrs(ehci_softc_t *sc)
-{
-       /* Enable interrupts */
-       EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
-}
-
 int
 ehci_intr(void *v)
 {
        ehci_softc_t *sc = v;
 
-       if (sc == NULL || sc->sc_dying)
+       if (sc->sc_dying || (sc->sc_flags & EHCI_SCFLG_DONEINIT) == 0)
                return (0);
 
        /* If we get an interrupt while polling, then just ignore it. */
        if (sc->sc_bus.use_polling) {
+               u_int32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
+
+               if (intrs)
+                       EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */
 #ifdef DIAGNOSTIC
                DPRINTFN(16, ("ehci_intr: ignored interrupt while polling\n"));
 #endif
@@ -509,14 +551,6 @@ ehci_intr1(ehci_softc_t *sc)
 
        DPRINTFN(20,("ehci_intr1: enter\n"));
 
-       /* In case the interrupt occurs before initialization has completed. */
-       if (sc == NULL) {
-#ifdef DIAGNOSTIC
-               printf("ehci_intr1: sc == NULL\n");
-#endif
-               return (0);
-       }
-
        intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
        if (!intrs)
                return (0);
@@ -675,7 +709,7 @@ ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
        lsqtd = ex->sqtdend;
 #ifdef DIAGNOSTIC
        if (lsqtd == NULL) {
-               printf("ehci_check_intr: sqtd==0\n");
+               printf("ehci_check_intr: lsqtd==0\n");
                return;
        }
 #endif
@@ -705,6 +739,7 @@ ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
  done:
        DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex));
        usb_uncallout(ex->xfer.timeout_handle, ehci_timeout, ex);
+       usb_rem_task(ex->xfer.pipe->device, &ex->abort_task);
        ehci_idone(ex);
 }
 
@@ -712,10 +747,12 @@ 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;
-       ehci_soft_qtd_t *sqtd;
-       u_int32_t status = 0, nstatus;
-       int actlen;
+#endif
+       ehci_soft_qtd_t *sqtd, *lsqtd;
+       u_int32_t status = 0, nstatus = 0;
+       int actlen, cerr;
 
        DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex));
 #ifdef DIAGNOSTIC
@@ -723,7 +760,7 @@ ehci_idone(struct ehci_xfer *ex)
                crit_enter();
                if (ex->isdone) {
                        crit_exit();
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
                        printf("ehci_idone: ex is done!\n   ");
                        ehci_dump_exfer(ex);
 #else
@@ -742,51 +779,42 @@ ehci_idone(struct ehci_xfer *ex)
                return;
        }
 
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        DPRINTFN(/*10*/2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer, epipe));
        if (ehcidebug > 10)
                ehci_dump_sqtds(ex->sqtdstart);
 #endif
 
        /* The transfer is done, compute actual length and status. */
+       lsqtd = ex->sqtdend;
        actlen = 0;
-       for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) {
+       for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd=sqtd->nextqtd) {
                nstatus = le32toh(sqtd->qtd.qtd_status);
                if (nstatus & EHCI_QTD_ACTIVE)
                        break;
 
                status = nstatus;
                /* halt is ok if descriptor is last, and complete */
-               if(sqtd->qtd.qtd_next == EHCI_NULL &&
-                  EHCI_QTD_GET_BYTES(status) == 0)
+               if (sqtd->qtd.qtd_next == EHCI_NULL &&
+                   EHCI_QTD_GET_BYTES(status) == 0)
                        status &= ~EHCI_QTD_HALTED;
                if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP)
                        actlen += sqtd->len - EHCI_QTD_GET_BYTES(status);
        }
 
-       /* If there are left over TDs we need to update the toggle. */
-       if (sqtd != NULL) {
-               printf("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus);
-#if 0
-               ehci_dump_sqh(epipe->sqh);
-               ehci_dump_sqtds(ex->sqtdstart);
-#endif
-               epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus);
-       }
-
-       status &= EHCI_QTD_STATERRS;
-       DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n",
-                          xfer->length, actlen, status));
+       cerr = EHCI_QTD_GET_CERR(status);
+       DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, cerr=%d, "
+           "status=0x%x\n", xfer->length, actlen, cerr, status));
        xfer->actlen = actlen;
-       if (status != 0) {
-#ifdef USB_DEBUG
+       if ((status & EHCI_QTD_HALTED) != 0) {
+#ifdef EHCI_DEBUG
                char sbuf[128];
 
                bitmask_snprintf((u_int32_t)status,
-                                "\20\7HALTED\6BUFERR\5BABBLE\4XACTERR"
-                                "\3MISSED", sbuf, sizeof(sbuf));
+                   "\20\7HALTED\6BUFERR\5BABBLE\4XACTERR"
+                   "\3MISSED\2SPLIT\1PING", sbuf, sizeof(sbuf));
 
-               DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2,
+               DPRINTFN(2,
                         ("ehci_idone: error, addr=%d, endpt=0x%02x, "
                          "status 0x%s\n",
                          xfer->pipe->device->address,
@@ -797,7 +825,7 @@ ehci_idone(struct ehci_xfer *ex)
                        ehci_dump_sqtds(ex->sqtdstart);
                }
 #endif
-               if (status == EHCI_QTD_HALTED)
+               if ((status & EHCI_QTD_BABBLE) == 0 && cerr > 0)
                        xfer->status = USBD_STALLED;
                else
                        xfer->status = USBD_IOERROR; /* more info XXX */
@@ -817,18 +845,19 @@ ehci_idone(struct ehci_xfer *ex)
 void
 ehci_waitintr(ehci_softc_t *sc, usbd_xfer_handle xfer)
 {
-       int timo;
+       int timo = xfer->timeout;
+       int usecs;
        u_int32_t intrs;
 
        xfer->status = USBD_IN_PROGRESS;
-       for (timo = xfer->timeout; timo >= 0; timo--) {
+       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
+#ifdef EHCI_DEBUG
                if (ehcidebug > 15)
                        ehci_dump_regs(sc);
 #endif
@@ -850,7 +879,7 @@ void
 ehci_poll(struct usbd_bus *bus)
 {
        ehci_softc_t *sc = (ehci_softc_t *)bus;
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        static int last;
        int new;
        new = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
@@ -864,33 +893,41 @@ ehci_poll(struct usbd_bus *bus)
                ehci_intr1(sc);
 }
 
-#if defined(__NetBSD__) || defined(__OpenBSD__)
 int
 ehci_detach(struct ehci_softc *sc, int flags)
 {
        int rv = 0;
 
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        if (sc->sc_child != NULL)
                rv = config_detach(sc->sc_child, flags);
 
        if (rv != 0)
                return (rv);
+#else
+       sc->sc_dying = 1;
+#endif
 
+       EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+       EOWRITE4(sc, EHCI_USBCMD, 0);
+       EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
        usb_uncallout(sc->sc_tmo_intrlist, ehci_intrlist_timeout, sc);
        usb_uncallout(sc->sc_tmo_pcd, ehci_pcd_enable, sc);
 
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        if (sc->sc_powerhook != NULL)
                powerhook_disestablish(sc->sc_powerhook);
        if (sc->sc_shutdownhook != NULL)
                shutdownhook_disestablish(sc->sc_shutdownhook);
+#endif
 
        usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
 
+       usb_freemem(&sc->sc_bus, &sc->sc_fldma);
        /* XXX free other data structures XXX */
 
        return (rv);
 }
-#endif
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
 int
@@ -920,79 +957,142 @@ ehci_activate(device_ptr_t self, enum devact act)
  * called from an interrupt 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;
+       u_int32_t cmd, hcr;
+       int i;
 
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why));
-       ehci_dump_regs(sc);
+       if (ehcidebug > 0)
+               ehci_dump_regs(sc);
 #endif
 
        crit_enter();
 
        switch (why) {
        case PWR_SUSPEND:
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        case PWR_STANDBY:
+#endif
                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);
+
+               for (i = 1; i <= sc->sc_noport; i++) {
+                       cmd = EOREAD4(sc, EHCI_PORTSC(i));
+                       if ((cmd & EHCI_PS_PO) == 0 &&
+                           (cmd & EHCI_PS_PE) == EHCI_PS_PE)
+                               EOWRITE4(sc, EHCI_PORTSC(i),
+                                   cmd | EHCI_PS_SUSP);
                }
-               ctl |= EHCI_HCFS_SUSPEND;
-               OWRITE4(sc, EHCI_CONTROL, ctl);
-#endif
-               usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+
+               sc->sc_cmd = EOREAD4(sc, EHCI_USBCMD);
+
+               cmd = sc->sc_cmd & ~(EHCI_CMD_ASE | EHCI_CMD_PSE);
+               EOWRITE4(sc, EHCI_USBCMD, cmd);
+
+               for (i = 0; i < 100; i++) {
+                       hcr = EOREAD4(sc, EHCI_USBSTS) &
+                           (EHCI_STS_ASS | EHCI_STS_PSS);
+                       if (hcr == 0)
+                               break;
+
+                       usb_delay_ms(&sc->sc_bus, 1);
+               }
+               if (hcr != 0) {
+                       printf("%s: reset timeout\n",
+                           USBDEVNAME(sc->sc_bus.bdev));
+               }
+
+               cmd &= ~EHCI_CMD_RS;
+               EOWRITE4(sc, EHCI_USBCMD, cmd);
+
+               for (i = 0; i < 100; i++) {
+                       hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+                       if (hcr == EHCI_STS_HCH)
+                               break;
+
+                       usb_delay_ms(&sc->sc_bus, 1);
+               }
+               if (hcr != EHCI_STS_HCH) {
+                       printf("%s: config timeout\n",
+                           USBDEVNAME(sc->sc_bus.bdev));
+               }
+
                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
+
+               /* restore things in case the bios sucks */
+               EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
+               EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0));
+               EOWRITE4(sc, EHCI_ASYNCLISTADDR,
+                   sc->sc_async_head->physaddr | EHCI_LINK_QH);
+               EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+               hcr = 0;
+               for (i = 1; i <= sc->sc_noport; i++) {
+                       cmd = EOREAD4(sc, EHCI_PORTSC(i));
+                       if ((cmd & EHCI_PS_PO) == 0 &&
+                           (cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP) {
+                               EOWRITE4(sc, EHCI_PORTSC(i),
+                                   cmd | EHCI_PS_FPR);
+                               hcr = 1;
+                       }
+               }
+
+               if (hcr) {
+                       usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+
+                       for (i = 1; i <= sc->sc_noport; i++) {
+                               cmd = EOREAD4(sc, EHCI_PORTSC(i));
+                               if ((cmd & EHCI_PS_PO) == 0 &&
+                                   (cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)
+                                       EOWRITE4(sc, EHCI_PORTSC(i),
+                                           cmd & ~EHCI_PS_FPR);
+                       }
+               }
+
+               EOWRITE4(sc, EHCI_USBCMD, sc->sc_cmd);
+
+               for (i = 0; i < 100; i++) {
+                       hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+                       if (hcr != EHCI_STS_HCH)
+                               break;
+
+                       usb_delay_ms(&sc->sc_bus, 1);
+               }
+               if (hcr == EHCI_STS_HCH) {
+                       printf("%s: config timeout\n",
+                           USBDEVNAME(sc->sc_bus.bdev));
+               }
+
+               usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+
                sc->sc_bus.use_polling--;
                break;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        case PWR_SOFTSUSPEND:
        case PWR_SOFTSTANDBY:
        case PWR_SOFTRESUME:
                break;
+#endif
        }
        crit_exit();
-}
+
+#ifdef EHCI_DEBUG
+       DPRINTF(("ehci_power: sc=%p\n", sc));
+       if (ehcidebug > 0)
+               ehci_dump_regs(sc);
 #endif
+}
 
 /*
  * Shut down the controller when the system is going down.
  */
-#if defined(__NetBSD__) || defined(__OpenBSD__)
 void
 ehci_shutdown(void *v)
 {
@@ -1002,7 +1102,6 @@ ehci_shutdown(void *v)
        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)
@@ -1010,7 +1109,7 @@ 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
+#ifdef EHCI_DEBUG
        if (err)
                printf("ehci_allocm: usb_allocmem()=%d\n", err);
 #endif
@@ -1043,6 +1142,9 @@ ehci_allocx(struct usbd_bus *bus)
        }
        if (xfer != NULL) {
                memset(xfer, 0, sizeof(struct ehci_xfer));
+               usb_init_task(&EXFER(xfer)->abort_task, ehci_timeout_task,
+                   xfer);
+               EXFER(xfer)->ehci_xfer_flags = 0;
 #ifdef DIAGNOSTIC
                EXFER(xfer)->isdone = 1;
                xfer->busy_free = XFER_BUSY;
@@ -1082,7 +1184,10 @@ ehci_device_clear_toggle(usbd_pipe_handle pipe)
        if (ehcidebug)
                usbd_dump_pipe(pipe);
 #endif
-       epipe->nexttoggle = 0;
+       KASSERT((epipe->sqh->qh.qh_qtd.qtd_status &
+           htole32(EHCI_QTD_ACTIVE)) == 0,
+           ("ehci_device_clear_toggle: queue active"));
+       epipe->sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE_MASK);
 }
 
 Static void
@@ -1090,7 +1195,7 @@ ehci_noop(usbd_pipe_handle pipe)
 {
 }
 
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
 void
 ehci_dump_regs(ehci_softc_t *sc)
 {
@@ -1149,7 +1254,7 @@ ehci_dump_sqtds(ehci_soft_qtd_t *sqtd)
        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;
+               stop = sqtd->qtd.qtd_next & htole32(EHCI_LINK_TERMINATE);
        }
        if (sqtd)
                printf("dump aborted, too many TDs\n");
@@ -1232,16 +1337,23 @@ ehci_open(usbd_pipe_handle pipe)
        struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
        ehci_soft_qh_t *sqh;
        usbd_status err;
-       int speed, naks;
+       int ival, speed, naks;
+       int hshubaddr, hshubport;
 
        DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
                     pipe, addr, ed->bEndpointAddress, sc->sc_addr));
 
+       if (dev->myhsport) {
+               hshubaddr = dev->myhsport->parent->address;
+               hshubport = dev->myhsport->portno;
+       } else {
+               hshubaddr = 0;
+               hshubport = 0;
+       }
+
        if (sc->sc_dying)
                return (USBD_IOERROR);
 
-       epipe->nexttoggle = 0;
-
        if (addr == sc->sc_addr) {
                switch (ed->bEndpointAddress) {
                case USB_CONTROL_ENDPOINT:
@@ -1263,6 +1375,15 @@ ehci_open(usbd_pipe_handle pipe)
        case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break;
        default: panic("ehci_open: bad device speed %d", dev->speed);
        }
+       if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) {
+               printf("%s: *** WARNING: opening low/full speed device, this "
+                      "does not work yet.\n",
+                      USBDEVNAME(sc->sc_bus.bdev));
+               DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
+                           hshubaddr, hshubport));
+               return USBD_INVAL;
+       }
+
        naks = 8;               /* XXX */
        sqh = ehci_alloc_sqh(sc);
        if (sqh == NULL)
@@ -1272,22 +1393,25 @@ ehci_open(usbd_pipe_handle pipe)
                EHCI_QH_SET_ADDR(addr) |
                EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
                EHCI_QH_SET_EPS(speed) |
-               EHCI_QH_DTC |
+               (xfertype == UE_CONTROL ? EHCI_QH_DTC : 0) |
                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 */
+               EHCI_QH_SET_MULT(1) |
+               EHCI_QH_SET_HUBA(hshubaddr) |
+               EHCI_QH_SET_PORT(hshubport) |
+               EHCI_QH_SET_CMASK(0x1c) |
+               EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0)
                );
        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);
+       sqh->qh.qh_qtd.qtd_status =
+           htole32(EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle));
 
        epipe->sqh = sqh;
 
@@ -1295,7 +1419,7 @@ ehci_open(usbd_pipe_handle pipe)
        case UE_CONTROL:
                err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t),
                                   0, &epipe->u.ctl.reqdma);
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
                if (err)
                        printf("ehci_open: usb_allocmem()=%d\n", err);
 #endif
@@ -1314,7 +1438,10 @@ ehci_open(usbd_pipe_handle pipe)
                break;
        case UE_INTERRUPT:
                pipe->methods = &ehci_device_intr_methods;
-               return (USBD_INVAL);
+               ival = pipe->interval;
+               if (ival == USBD_DEFAULT_INTERVAL)
+                       ival = ed->bInterval;
+               return (ehci_device_setintr(sc, sqh, ival));
        case UE_ISOCHRONOUS:
                pipe->methods = &ehci_device_isoc_methods;
                return (USBD_INVAL);
@@ -1330,17 +1457,22 @@ ehci_open(usbd_pipe_handle pipe)
 }
 
 /*
- * Add an ED to the schedule.  Called while in a critical section. 
+ * Add an ED to the schedule.  Called while in a critical section.
+ * If in the async schedule, it will always have a next.
+ * If in the intr schedule it may not.
  */
 void
 ehci_add_qh(ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
 {
        sqh->next = head->next;
+       sqh->prev = head;
        sqh->qh.qh_link = head->qh.qh_link;
        head->next = sqh;
+       if (sqh->next)
+               sqh->next->prev = sqh;
        head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH);
 
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        if (ehcidebug > 5) {
                printf("ehci_add_qh:\n");
                ehci_dump_sqh(sqh);
@@ -1350,33 +1482,40 @@ ehci_add_qh(ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
 
 /*
  * Remove an ED from the schedule.  Called while in a critical section.
+ * Will always have a 'next' if it's in the async list as it's circular.
  */
 void
 ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
 {
-       ehci_soft_qh_t *p;
-
        /* 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;
-
+       sqh->prev->qh.qh_link = sqh->qh.qh_link;
+       sqh->prev->next = sqh->next;
+       if (sqh->next)
+               sqh->next->prev = sqh->prev;
        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);
+       int i;
+       u_int32_t status;
+
+       /* Save toggle bit and ping status. */
+       status = sqh->qh.qh_qtd.qtd_status &
+           htole32(EHCI_QTD_TOGGLE_MASK |
+                   EHCI_QTD_SET_STATUS(EHCI_QTD_PINGSTATE));
+       /* Set HALTED to make hw leave it alone. */
+       sqh->qh.qh_qtd.qtd_status =
+           htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED));
        sqh->qh.qh_curqtd = 0;
        sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr);
+       sqh->qh.qh_qtd.qtd_altnext = 0;
+       for (i = 0; i < EHCI_QTD_NBUFFERS; i++)
+               sqh->qh.qh_qtd.qtd_buffer[i] = 0;
        sqh->sqtd = sqtd;
-       /* Clear halt */
-       sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_HALTED);
+       /* Set !HALTED && !ACTIVE to start execution, preserve some fields */
+       sqh->qh.qh_qtd.qtd_status = status;
 }
 
 /*
@@ -1490,7 +1629,7 @@ Static usb_hub_descriptor_t ehci_hubd = {
 };
 
 Static int
-ehci_str( usb_string_descriptor_t *p, int l, const char *s)
+ehci_str(usb_string_descriptor_t *p, int l, char *s)
 {
        int i;
 
@@ -1546,7 +1685,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 #endif
        req = &xfer->request;
 
-       DPRINTFN(4,("ehci_root_ctrl_start type=0x%02x request=%02x\n",
+       DPRINTFN(4,("ehci_root_ctrl_start: type=0x%02x request=%02x\n",
                    req->bmRequestType, req->bRequest));
 
        len = UGETW(req->wLength);
@@ -1573,9 +1712,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
                }
                break;
        case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
-               DPRINTFN(8,("ehci_root_ctrl_start wValue=0x%04x\n", value));
-               if (len == 0)
-                       break;
+               DPRINTFN(8,("ehci_root_ctrl_start: wValue=0x%04x\n", value));
                switch(value >> 8) {
                case UDESC_DEVICE:
                        if ((value & 0xff) != 0) {
@@ -1624,9 +1761,14 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
                        memcpy(buf, &ehci_endpd, l);
                        break;
                case UDESC_STRING:
+                       if (len == 0)
+                               break;
                        *(u_int8_t *)buf = 0;
                        totlen = 1;
                        switch (value & 0xff) {
+                       case 0: /* Language table */
+                               totlen = ehci_str(buf, len, "\001");
+                               break;
                        case 1: /* Vendor */
                                totlen = ehci_str(buf, len, sc->sc_vendor);
                                break;
@@ -1752,8 +1894,6 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 #endif
                break;
        case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
-               if (len == 0)
-                       break;
                if ((value & 0xff) != 0) {
                        err = USBD_IOERROR;
                        goto ret;
@@ -2030,7 +2170,7 @@ ehci_alloc_sqh(ehci_softc_t *sc)
                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
+#ifdef EHCI_DEBUG
                if (err)
                        printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err);
 #endif
@@ -2048,6 +2188,7 @@ ehci_alloc_sqh(ehci_softc_t *sc)
        sc->sc_freeqhs = sqh->next;
        memset(&sqh->qh, 0, sizeof(ehci_qh_t));
        sqh->next = NULL;
+       sqh->prev = NULL;
        return (sqh);
 }
 
@@ -2070,7 +2211,7 @@ ehci_alloc_sqtd(ehci_softc_t *sc)
                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
+#ifdef EHCI_DEBUG
                if (err)
                        printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err);
 #endif
@@ -2115,16 +2256,16 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
        ehci_soft_qtd_t *next, *cur;
        ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys;
        u_int32_t qtdstatus;
-       int len, curlen, mps;
-       int i, tog;
-       int offset;
+       int len, curlen, mps, offset;
+       int i, iscontrol;
        usb_dma_t *dma = &xfer->dmabuf;
-       u_int16_t flags = xfer->flags;
 
        DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen));
 
        offset = 0;
        len = alen;
+       iscontrol = (epipe->pipe.endpoint->edesc->bmAttributes & UE_XFERTYPE) ==
+           UE_CONTROL;
        dataphys = DMAADDR(dma, 0);
        dataphyslastpage = EHCI_PAGE(DMAADDR(dma, len - 1));
        qtdstatus = EHCI_QTD_ACTIVE |
@@ -2134,8 +2275,12 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
            /* BYTES set below */
            ;
        mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
-       tog = epipe->nexttoggle;
-       qtdstatus |= EHCI_QTD_SET_TOGGLE(tog);
+       /*
+        * The control transfer data stage always starts with a toggle of 1.
+        * For other transfers we let the hardware track the toggle state.
+        */
+       if (iscontrol)
+               qtdstatus |= EHCI_QTD_SET_TOGGLE(1);
 
        cur = ehci_alloc_sqtd(sc);
        *sp = cur;
@@ -2149,6 +2294,7 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
                    EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE) {
                        /* we can handle it in this QTD */
                        curlen = len;
+               }
 #elif defined(__FreeBSD__) || defined(__DragonFly__)
                /* XXX This is pretty broken: Because we do not allocate
                 * a contiguous buffer (contiguous in physical pages) we
@@ -2158,8 +2304,9 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
                 */
                if (dataphyspage == dataphyslastpage) {
                        curlen = len;
+               }
 #endif
-               else {
+               else {
 #if defined(__NetBSD__) || defined(__OpenBSD__)
                        /* must use multiple TDs, fill as much as possible. */
                        curlen = EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE -
@@ -2184,10 +2331,7 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
                        curlen -= curlen % mps;
                        DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, "
                                    "curlen=%d\n", curlen));
-#ifdef DIAGNOSTIC
-                       if (curlen == 0)
-                               panic("ehci_alloc_sqtd_chain: curlen == 0");
-#endif
+                       KASSERT(curlen != 0, ("ehci_alloc_std: curlen == 0"));
                }
                DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x "
                            "dataphyslastpage=0x%08x len=%d curlen=%d\n",
@@ -2195,15 +2339,7 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
                            len, curlen));
                len -= curlen;
 
-               /*
-                * Allocate another transfer if there's more data left, 
-                * or if force last short transfer flag is set and we're 
-                * allocating a multiple of the max packet size.
-                */
-               if (len != 0 ||
-                   ((flags & USBD_FORCE_SHORT_XFER) &&
-                   (curlen % mps) == 0 && !rd && curlen != 0)
-               ) {
+               if (len != 0) {
                        next = ehci_alloc_sqtd(sc);
                        if (next == NULL)
                                goto nomem;
@@ -2213,8 +2349,7 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
                        nextphys = EHCI_NULL;
                }
 
-               for (i = 0; i * EHCI_PAGE_SIZE < 
-                           curlen + EHCI_PAGE_OFFSET(dataphys); i++) {
+               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);
@@ -2235,13 +2370,15 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
                cur->len = curlen;
                DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n",
                            dataphys, dataphys + curlen));
-               /* adjust the toggle based on the number of packets in this
-                  qtd */
-               if (((curlen + mps - 1) / mps) & 1) {
-                       tog ^= 1;
-                       qtdstatus ^= EHCI_QTD_TOGGLE_MASK;
+               if (iscontrol) {
+                       /*
+                        * adjust the toggle based on the number of packets
+                        * in this qtd
+                        */
+                       if (((curlen + mps - 1) / mps) & 1)
+                               qtdstatus ^= EHCI_QTD_TOGGLE_MASK;
                }
-               if (next == NULL)
+               if (len == 0)
                        break;
                DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n"));
                offset += curlen;
@@ -2250,7 +2387,6 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
        }
        cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC);
        *ep = cur;
-       epipe->nexttoggle = tog;
 
        DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n",
                     *sp, *ep));
@@ -2295,6 +2431,8 @@ ehci_close_pipe(usbd_pipe_handle pipe, ehci_soft_qh_t *head)
        crit_enter();
        ehci_rem_qh(sc, sqh, head);
        crit_exit();
+       pipe->endpoint->savedtoggle =
+           EHCI_QTD_GET_TOGGLE(le32toh(sqh->qh.qh_qtd.qtd_status));
        ehci_free_sqh(sc, epipe->sqh);
 }
 
@@ -2316,10 +2454,11 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
        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;
+       ehci_soft_qtd_t *sqtd, *snext, **psqtd;
+       ehci_physaddr_t cur, us, next;
        int hit;
+       /* int count = 0; */
+       ehci_soft_qh_t *psqh;
 
        DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));
 
@@ -2328,6 +2467,7 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
                crit_enter();
                xfer->status = status;  /* make software ignore it */
                usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer);
+               usb_rem_task(epipe->pipe.device, &exfer->abort_task);
                usb_transfer_complete(xfer);
                crit_exit();
                return;
@@ -2337,26 +2477,48 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
                panic("ehci_abort_xfer: not in process context");
 
        /*
-        * Step 1: Make interrupt routine and hardware ignore xfer.
+        * If an abort is already in progress then just wait for it to
+        * complete and return.
+        */
+       if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING) {
+               DPRINTFN(2, ("ehci_abort_xfer: already aborting\n"));
+               /* No need to wait if we're aborting from a timeout. */
+               if (status == USBD_TIMEOUT)
+                       return;
+               /* Override the status which might be USBD_TIMEOUT. */
+               xfer->status = status;
+               DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
+               exfer->ehci_xfer_flags |= EHCI_XFER_ABORTWAIT;
+               while (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING)
+                       tsleep(&exfer->ehci_xfer_flags, 0, "ehciaw", 0);
+               return;
+       }
+
+       /*
+        * Step 1: Make interrupt routine and timeouts ignore xfer.
         */
        crit_enter();
+       exfer->ehci_xfer_flags |= EHCI_XFER_ABORTING;
        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;
-       }
+       usb_rem_task(epipe->pipe.device, &exfer->abort_task);
        crit_exit();
 
        /*
         * Step 2: Wait until we know hardware has finished any possible
-        * use of the xfer.  Also make sure the soft interrupt routine
-        * has run.
+        * use of the xfer. We do this by removing the entire
+        * queue from the async schedule and waiting for the doorbell.
+        * Nothing else should be touching the queue now.
+        */
+       psqh = sqh->prev;
+       ehci_rem_qh(sc, sqh, psqh);
+
+       /*
+        * Step 3:  make sure the soft interrupt routine
+        * has run. This should remove any completed items off the queue.
+        * The hardware has no reference to completed items (TDs).
+        * It's safe to remove them at any time.
         */
-       ehci_sync_hc(sc);
        crit_enter();
 #ifdef USB_USE_SOFTINTR
        sc->sc_softwake = 1;
@@ -2365,41 +2527,103 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
 #ifdef USB_USE_SOFTINTR
        tsleep(&sc->sc_softwake, 0, "ehciab", 0);
 #endif /* USB_USE_SOFTINTR */
-       crit_exit();
 
        /*
-        * Step 3: Remove any vestiges of the xfer from the hardware.
+        * Step 4: 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.
+        * into or even 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.
+        *
+        * first we need to see if there are any transfers
+        * on this queue before the xfer we are aborting.. we need
+        * to update any pointers that point to us to point past
+        * the aborting xfer.  (If there is something past us).
+        * Hardware and software.
         */
-       crit_enter();           /* 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"));
-       }
+
+       /* If they initially point here. */
+       us = exfer->sqtdstart->physaddr;
+
+       /* We will change them to point here */
+       snext = exfer->sqtdend->nextqtd;
+       next = snext ? htole32(snext->physaddr) : EHCI_NULL;
 
        /*
-        * Step 4: Execute callback.
+        * Now loop through any qTDs before us and keep track of the pointer
+        * that points to us for the end.
+        */
+       psqtd = &sqh->sqtd;
+       sqtd = sqh->sqtd;
+       while (sqtd && sqtd != exfer->sqtdstart) {
+               hit |= (cur == sqtd->physaddr);
+               if (EHCI_LINK_ADDR(le32toh(sqtd->qtd.qtd_next)) == us)
+                       sqtd->qtd.qtd_next = next;
+               if (EHCI_LINK_ADDR(le32toh(sqtd->qtd.qtd_altnext)) == us)
+                       sqtd->qtd.qtd_altnext = next;
+               psqtd = &sqtd->nextqtd;
+               sqtd = sqtd->nextqtd;
+       }
+               /* make the software pointer bypass us too */
+       *psqtd = exfer->sqtdend->nextqtd;
+
+       /*
+        * If we already saw the active one then we are pretty much done.
+        * We've done all the relinking we need to do.
+        */
+       if (!hit) {
+
+               /*
+                * Now reinitialise the QH to point to the next qTD
+                * (if there is one). We only need to do this if
+                * it was previously pointing to us.
+                */
+               sqtd = exfer->sqtdstart;
+               for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) {
+                       if (cur == sqtd->physaddr) {
+                               hit++;
+                       }
+                       if (sqtd == exfer->sqtdend)
+                               break;
+               }
+               sqtd = sqtd->nextqtd;
+               /*
+                * Only need to alter the QH if it was pointing at a qTD
+                * that we are removing.
+                */
+               if (hit) {
+                       if (snext) {
+                               ehci_set_qh_qtd(sqh, snext);
+                       } else {
+
+                               sqh->qh.qh_curqtd = 0; /* unlink qTDs */
+                               sqh->qh.qh_qtd.qtd_status &=
+                                   htole32(EHCI_QTD_TOGGLE_MASK);
+                               sqh->qh.qh_qtd.qtd_next =
+                                   sqh->qh.qh_qtd.qtd_altnext
+                                       = EHCI_NULL;
+                               DPRINTFN(1,("ehci_abort_xfer: no hit\n"));
+                       }
+               }
+       }
+       ehci_add_qh(sqh, psqh);
+       /*
+        * Step 5: Execute callback.
         */
 #ifdef DIAGNOSTIC
        exfer->isdone = 1;
 #endif
+       /* Do the wakeup first to avoid touching the xfer after the callback. */
+       exfer->ehci_xfer_flags &= ~EHCI_XFER_ABORTING;
+       if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTWAIT) {
+               exfer->ehci_xfer_flags &= ~EHCI_XFER_ABORTWAIT;
+               wakeup(&exfer->ehci_xfer_flags);
+       }
        usb_transfer_complete(xfer);
 
+       /* printf("%s: %d TDs aborted\n", __func__, count); */
        crit_exit();
 #undef exfer
 }
@@ -2423,8 +2647,8 @@ ehci_timeout(void *addr)
        }
 
        /* 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);
+       usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task,
+                    USB_TASKQ_HC);
 }
 
 void
@@ -2439,11 +2663,11 @@ ehci_timeout_task(void *addr)
 }
 
 /*
- * Some EHCI chips from VIA seem to trigger interrupts before writing back the
- * qTD status, or miss signalling occasionally under heavy load.  If the host
- * machine is too fast, we we can miss transaction completion - when we scan
- * the active list the transaction still seems to be active.  This generally
- * exhibits itself as a umass stall that never recovers.
+ * Some EHCI chips from VIA / ATI seem to trigger interrupts before writing
+ * back the qTD status, or miss signalling occasionally under heavy load.
+ * If the host machine is too fast, we can miss transaction completion - when
+ * we scan the active list the transaction still seems to be active. This
+ * generally exhibits itself as a umass stall that never recovers.
  *
  * We work around this behaviour by setting up this callback after any softintr
  * that completes with transactions still pending, giving us another chance to
@@ -2560,7 +2784,7 @@ ehci_device_request(usbd_xfer_handle xfer)
        isread = req->bmRequestType & UT_READ;
        len = UGETW(req->wLength);
 
-       DPRINTFN(3,("ehci_device_request type=0x%02x, request=0x%02x, "
+       DPRINTFN(3,("ehci_device_request: 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,
@@ -2595,12 +2819,11 @@ ehci_device_request(usbd_xfer_handle xfer)
        if (len != 0) {
                ehci_soft_qtd_t *end;
 
-               /* Start toggle at 1. */
-               epipe->nexttoggle = 1;
                err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
                          &next, &end);
                if (err)
                        goto bad3;
+               end->qtd.qtd_status &= htole32(~EHCI_QTD_IOC);
                end->nextqtd = stat;
                end->qtd.qtd_next =
                end->qtd.qtd_altnext = htole32(stat->physaddr);
@@ -2639,7 +2862,7 @@ ehci_device_request(usbd_xfer_handle xfer)
        stat->xfer = xfer;
        stat->len = 0;
 
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        if (ehcidebug > 5) {
                DPRINTF(("ehci_device_request:\n"));
                ehci_dump_sqh(sqh);
@@ -2667,7 +2890,7 @@ ehci_device_request(usbd_xfer_handle xfer)
        xfer->status = USBD_IN_PROGRESS;
        crit_exit();
 
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        if (ehcidebug > 10) {
                DPRINTF(("ehci_device_request: status=%x\n",
                         EOREAD4(sc, EHCI_USBSTS)));
@@ -2748,7 +2971,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
                return (err);
        }
 
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        if (ehcidebug > 5) {
                DPRINTF(("ehci_device_bulk_start: data(1)\n"));
                ehci_dump_sqh(sqh);
@@ -2776,7 +2999,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
        xfer->status = USBD_IN_PROGRESS;
        crit_exit();
 
-#ifdef USB_DEBUG
+#ifdef EHCI_DEBUG
        if (ehcidebug > 10) {
                DPRINTF(("ehci_device_bulk_start: data(2)\n"));
                delay(10000);
@@ -2838,31 +3061,210 @@ ehci_device_bulk_done(usbd_xfer_handle xfer)
 
 /************************/
 
+Static usbd_status
+ehci_device_setintr(ehci_softc_t *sc, ehci_soft_qh_t *sqh, int ival)
+{
+       struct ehci_soft_islot *isp;
+       int islot, lev;
+
+       /* Find a poll rate that is large enough. */
+       for (lev = EHCI_IPOLLRATES - 1; lev > 0; lev--)
+               if (EHCI_ILEV_IVAL(lev) <= ival)
+                       break;
+
+       /* Pick an interrupt slot at the right level. */
+       /* XXX could do better than picking at random. */
+       islot = EHCI_IQHIDX(lev, karc4random());
+
+       sqh->islot = islot;
+       isp = &sc->sc_islots[islot];
+       ehci_add_qh(sqh, isp->sqh);
+
+       return (USBD_NORMAL_COMPLETION);
+}
+
 Static usbd_status
 ehci_device_intr_transfer(usbd_xfer_handle xfer)
 {
-       return USBD_IOERROR;
+       usbd_status err;
+
+       /* Insert last in queue. */
+       err = usb_insert_transfer(xfer);
+       if (err)
+               return (err);
+
+       /*
+        * Pipe isn't running (otherwise err would be USBD_INPROG),
+        * so start it first.
+        */
+       return (ehci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
 }
 
 Static usbd_status
 ehci_device_intr_start(usbd_xfer_handle xfer)
 {
-       return USBD_IOERROR;
+#define exfer EXFER(xfer)
+       struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+       usbd_device_handle dev = xfer->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;
+
+       DPRINTFN(2, ("ehci_device_intr_start: 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_intr_start: 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.intr.length = len;
+
+       err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
+           &dataend);
+       if (err) {
+               DPRINTFN(-1, ("ehci_device_intr_start: no memory\n"));
+               xfer->status = err;
+               usb_transfer_complete(xfer);
+               return (err);
+       }
+
+#ifdef EHCI_DEBUG
+       if (ehcidebug > 5) {
+               DPRINTF(("ehci_device_intr_start: 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_intr_start: not done, ex=%p\n", exfer);
+       }
+       exfer->isdone = 0;
+#endif
+
+       crit_enter();
+       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;
+       crit_exit();
+
+#ifdef EHCI_DEBUG
+       if (ehcidebug > 10) {
+               DPRINTF(("ehci_device_intr_start: data(2)\n"));
+               delay(10000);
+               DPRINTF(("ehci_device_intr_start: data(3)\n"));
+               ehci_dump_regs(sc);
+               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_intr_abort(usbd_xfer_handle xfer)
 {
+       DPRINTFN(1, ("ehci_device_intr_abort: xfer=%p\n", xfer));
+       if (xfer->pipe->intrxfer == xfer) {
+               DPRINTFN(1, ("ehci_device_intr_abort: remove\n"));
+               xfer->pipe->intrxfer = NULL;
+       }
+       ehci_abort_xfer(xfer, USBD_CANCELLED);
 }
 
 Static void
 ehci_device_intr_close(usbd_pipe_handle pipe)
 {
+       ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+       struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+       struct ehci_soft_islot *isp;
+
+       isp = &sc->sc_islots[epipe->sqh->islot];
+       ehci_close_pipe(pipe, isp->sqh);
 }
 
 Static void
 ehci_device_intr_done(usbd_xfer_handle xfer)
 {
+#define exfer EXFER(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;
+       ehci_soft_qtd_t *data, *dataend;
+       ehci_soft_qh_t *sqh;
+       usbd_status err;
+       int len, isread, endpt;
+
+       DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n",
+           xfer, xfer->actlen));
+
+       if (xfer->pipe->repeat) {
+               ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL);
+
+               len = epipe->u.intr.length;
+               xfer->length = len;
+               endpt = epipe->pipe.endpoint->edesc->bEndpointAddress;
+               isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+               sqh = epipe->sqh;
+
+               err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
+                   &data, &dataend);
+               if (err) {
+                       DPRINTFN(-1, ("ehci_device_intr_done: no memory\n"));
+                       xfer->status = err;
+                       return;
+               }
+
+               /* Set up interrupt info. */
+               exfer->sqtdstart = data;
+               exfer->sqtdend = dataend;
+#ifdef DIAGNOSTIC
+               if (!exfer->isdone) {
+                       printf("ehci_device_intr_done: not done, ex=%p\n",
+                           exfer);
+               }
+               exfer->isdone = 0;
+#endif
+
+               crit_enter();
+               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);
+               }
+               crit_exit();
+
+               xfer->status = USBD_IN_PROGRESS;
+       } else 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);
+       }
+#undef exfer
 }
 
 /************************/
index a0450e0..e996154 100644 (file)
@@ -1,4 +1,4 @@
-/*-
+/*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
@@ -33,8 +33,9 @@
  * 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.11 2006/10/25 20:55:52 dillon Exp $
+ *
+ * $FreeBSD: src/sys/dev/usb/ehci_pci.c,v 1.18.2.1 2006/01/26 01:43:13 iedowse Exp $
+ * $DragonFly: src/sys/bus/usb/ehci_pci.c,v 1.12 2006/12/10 02:03:56 sephe Exp $
  */
 
 /*
@@ -112,7 +113,7 @@ static const char *ehci_device_ich5 = "Intel 82801EB/R USB 2.0 controller";
 static const char *ehci_device_ich6 = "Intel 82801FB USB 2.0 controller";
 #define PCI_EHCI_DEVICEID_ICH7         0x27cc8086
 static const char *ehci_device_ich7 = "Intel 82801GB/R USB 2.0 controller";
-
 /* NEC */
 #define PCI_EHCI_DEVICEID_NEC          0x00e01033
 static const char *ehci_device_nec = "NEC uPD 720100 USB 2.0 controller";
@@ -142,9 +143,62 @@ static const char *ehci_device_generic = "EHCI (generic) USB 2.0 controller";
 
 #define PCI_EHCI_BASE_REG      0x10
 
+#ifdef USB_DEBUG
+#define EHCI_DEBUG USB_DEBUG
+#define DPRINTF(x)     do { if (ehcidebug) logprintf x; } while (0)
+extern int ehcidebug;
+#else
+#define DPRINTF(x)
+#endif
 
 static int ehci_pci_attach(device_t self);
 static int ehci_pci_detach(device_t self);
+static int ehci_pci_shutdown(device_t self);
+static int ehci_pci_suspend(device_t self);
+static int ehci_pci_resume(device_t self);
+static void ehci_pci_givecontroller(device_t self);
+static void ehci_pci_takecontroller(device_t self);
+
+static int
+ehci_pci_suspend(device_t self)
+{
+       ehci_softc_t *sc = device_get_softc(self);
+       int err;
+
+       err = bus_generic_suspend(self);
+       if (err)
+               return (err);
+       ehci_power(PWR_SUSPEND, sc);
+
+       return 0;
+}
+
+static int
+ehci_pci_resume(device_t self)
+{
+       ehci_softc_t *sc = device_get_softc(self);
+
+       ehci_pci_takecontroller(self);
+       ehci_power(PWR_RESUME, sc);
+       bus_generic_resume(self);
+
+       return 0;
+}
+
+static int
+ehci_pci_shutdown(device_t self)
+{
+       ehci_softc_t *sc = device_get_softc(self);
+       int err;
+
+       err = bus_generic_shutdown(self);
+       if (err)
+               return (err);
+       ehci_shutdown(sc);
+       ehci_pci_givecontroller(self);
+
+       return 0;
+}
 
 static const char *
 ehci_pci_match(device_t self)
@@ -184,8 +238,8 @@ ehci_pci_match(device_t self)
                return (ehci_device_nf4);
        case PCI_EHCI_DEVICEID_ISP156X:
                return (ehci_device_isp156x);
-       case PCI_EHCI_DEVICEID_VIA:
-               return (ehci_device_via);
+       case PCI_EHCI_DEVICEID_VIA:
+               return (ehci_device_via);
        default:
                if (pci_get_class(self) == PCIC_SERIALBUS
                    && pci_get_subclass(self) == PCIS_SERIALBUS_USB
@@ -244,8 +298,8 @@ ehci_pci_attach(device_t self)
        pci_enable_busmaster(self);
 
        rid = PCI_CBMEM;
-       sc->io_res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid,
-           0, ~0, 1, RF_ACTIVE);
+       sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+           RF_ACTIVE);
        if (!sc->io_res) {
                device_printf(self, "Could not map memory\n");
                return ENXIO;
@@ -254,7 +308,7 @@ ehci_pci_attach(device_t self)
        sc->ioh = rman_get_bushandle(sc->io_res);
 
        rid = 0;
-       sc->irq_res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1,
+       sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
            RF_SHAREABLE | RF_ACTIVE);
        if (sc->irq_res == NULL) {
                device_printf(self, "Could not allocate irq\n");
@@ -267,7 +321,7 @@ ehci_pci_attach(device_t self)
                ehci_pci_detach(self);
                return ENOMEM;
        }
-       device_set_ivars(sc->sc_bus.bdev, sc);
+       device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
 
        /* ehci_pci_match will never return NULL if ehci_pci_probe succeeded */
        device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self));
@@ -281,6 +335,9 @@ ehci_pci_attach(device_t self)
        case PCI_EHCI_VENDORID_APPLE:
                sprintf(sc->sc_vendor, "Apple");
                break;
+       case PCI_EHCI_VENDORID_ATI:
+               sprintf(sc->sc_vendor, "ATI");
+               break;
        case PCI_EHCI_VENDORID_CMDTECH:
                sprintf(sc->sc_vendor, "CMDTECH");
                break;
@@ -354,8 +411,8 @@ ehci_pci_attach(device_t self)
                        if (res != 0 || buscount != 1)
                                continue;
                        bsc = device_get_softc(nbus[0]);
-                       printf("ehci_pci_attach: companion %s\n",
-                       USBDEVNAME(bsc->bdev));
+                       DPRINTF(("ehci_pci_attach: companion %s\n",
+                           USBDEVNAME(bsc->bdev)));
                        sc->sc_comps[ncomp++] = bsc;
                        if (ncomp >= EHCI_COMPANION_MAX)
                                break;
@@ -363,18 +420,18 @@ ehci_pci_attach(device_t self)
        }
        sc->sc_ncomp = ncomp;
 
+       ehci_pci_takecontroller(self);
        err = ehci_init(sc);
-       if (!err)
+       if (!err) {
+               sc->sc_flags |= EHCI_SCFLG_DONEINIT;
                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;
        }
-       ehci_init_intrs(sc);
        return 0;
 }
 
@@ -383,10 +440,10 @@ 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
-        */
+       if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
+               ehci_detach(sc, 0);
+               sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
+       }
 
        /*
         * disable interrupts that might have been switched on in ehci_init
@@ -420,11 +477,65 @@ ehci_pci_detach(device_t self)
        return 0;
 }
 
+static void
+ehci_pci_takecontroller(device_t self)
+{
+       ehci_softc_t *sc = device_get_softc(self);
+       u_int32_t cparams, eec, legsup;
+       int eecp, i;
+
+       cparams = EREAD4(sc, EHCI_HCCPARAMS);
+
+       /* Synchronise with the BIOS if it owns the controller. */
+       for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
+            eecp = EHCI_EECP_NEXT(eec)) {
+               eec = pci_read_config(self, eecp, 4);
+               if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
+                       continue;
+               legsup = eec;
+               pci_write_config(self, eecp, legsup | EHCI_LEGSUP_OSOWNED, 4);
+               if (legsup & EHCI_LEGSUP_BIOSOWNED) {
+                       printf("%s: waiting for BIOS to give up control\n",
+                           USBDEVNAME(sc->sc_bus.bdev));
+                       for (i = 0; i < 5000; i++) {
+                               legsup = pci_read_config(self, eecp, 4);
+                               if ((legsup & EHCI_LEGSUP_BIOSOWNED) == 0)
+                                       break;
+                               DELAY(1000);
+                       }
+                       if (legsup & EHCI_LEGSUP_BIOSOWNED)
+                               printf("%s: timed out waiting for BIOS\n",
+                                   USBDEVNAME(sc->sc_bus.bdev));
+               }
+       }
+}
+
+static void
+ehci_pci_givecontroller(device_t self)
+{
+       ehci_softc_t *sc = device_get_softc(self);
+       u_int32_t cparams, eec, legsup;
+       int eecp;
+
+       cparams = EREAD4(sc, EHCI_HCCPARAMS);
+       for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
+            eecp = EHCI_EECP_NEXT(eec)) {
+               eec = pci_read_config(self, eecp, 4);
+               if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
+                       continue;
+               legsup = eec;
+               pci_write_config(self, eecp, legsup & ~EHCI_LEGSUP_OSOWNED, 4);
+       }
+}
+
 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),
+       DEVMETHOD(device_detach, ehci_pci_detach),
+       DEVMETHOD(device_suspend, ehci_pci_suspend),
+       DEVMETHOD(device_resume, ehci_pci_resume),
+       DEVMETHOD(device_shutdown, ehci_pci_shutdown),
 
        /* Bus interface */
        DEVMETHOD(bus_print_child, bus_generic_print_child),
index 9c5771d..9725642 100644 (file)
@@ -1,8 +1,7 @@
-/*
- * $NetBSD: ehcireg.h,v 1.18 2004/10/22 10:38:17 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.5 2005/08/27 19:00:49 asmodai Exp $
- */
+/*     $NetBSD: ehcireg.h,v 1.18 2004/10/22 10:38:17 augustss Exp $    */
+/*     $FreeBSD: src/sys/dev/usb/ehcireg.h,v 1.7.2.1 2006/01/26 01:43:13 iedowse Exp $ */
+/*     $DragonFly: src/sys/bus/usb/ehcireg.h,v 1.6 2006/12/10 02:03:56 sephe Exp $     */
+
 /*
  * Copyright (c) 2001 The NetBSD Foundation, Inc.
  * All rights reserved.
 
 #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 Extended Capabilities */
+#define EHCI_EC_LEGSUP         0x01
+
+#define EHCI_EECP_NEXT(x)      (((x) >> 8) & 0xff)
+#define EHCI_EECP_ID(x)                ((x) & 0xff)
+
+/* Legacy support extended capability */
+#define EHCI_LEGSUP_LEGSUP     0x01
+#define  EHCI_LEGSUP_OSOWNED   0x01000000 /* OS owned semaphore */
+#define  EHCI_LEGSUP_BIOSOWNED 0x00010000 /* BIOS owned semaphore */
+#define EHCI_LEGSUP_USBLEGCTLSTS 0x04
 
 /*** EHCI capability registers ***/
 
@@ -212,6 +219,7 @@ typedef struct {
        ehci_link_t     qtd_altnext;
        u_int32_t       qtd_status;
 #define EHCI_QTD_GET_STATUS(x) (((x) >>  0) & 0xff)
+#define EHCI_QTD_SET_STATUS(x) ((x) <<  0)
 #define  EHCI_QTD_ACTIVE       0x80
 #define  EHCI_QTD_HALTED       0x40
 #define  EHCI_QTD_BUFERR       0x20
index 515cbdc..c21b287 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $NetBSD: ehcivar.h,v 1.19 2005/04/29 15:04:29 augustss Exp $
- * $FreeBSD: src/sys/dev/usb/ehcivar.h,v 1.2 2004/08/01 18:47:42 iedowse Exp $
- * $DragonFly: src/sys/bus/usb/ehcivar.h,v 1.7 2006/05/02 16:12:01 dillon Exp $
- */
+/*     $NetBSD: ehcivar.h,v 1.19 2005/04/29 15:04:29 augustss Exp $    */
+/*     $FreeBSD: src/sys/dev/usb/ehcivar.h,v 1.9.2.1 2006/01/26 01:43:13 iedowse Exp $ */
+/*     $DragonFly: src/sys/bus/usb/ehcivar.h,v 1.8 2006/12/10 02:03:56 sephe Exp $     */
 
 /*
  * Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -54,9 +52,10 @@ typedef struct ehci_soft_qtd {
 typedef struct ehci_soft_qh {
        ehci_qh_t qh;
        struct ehci_soft_qh *next;
+       struct ehci_soft_qh *prev;
        struct ehci_soft_qtd *sqtd;
        ehci_physaddr_t physaddr;
-       int islot;
+       int islot;              /* Interrupt list slot. */
 } 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)
@@ -67,10 +66,14 @@ struct ehci_xfer {
        LIST_ENTRY(ehci_xfer) inext; /* list of active xfers */
        ehci_soft_qtd_t *sqtdstart;
        ehci_soft_qtd_t *sqtdend;
+       u_int32_t ehci_xfer_flags;
 #ifdef DIAGNOSTIC
        int isdone;
 #endif
 };
+#define EHCI_XFER_ABORTING     0x0001  /* xfer is aborting. */
+#define EHCI_XFER_ABORTWAIT    0x0002  /* abort completion is being awaited. */
+
 #define EXFER(xfer) ((struct ehci_xfer *)(xfer))
 
 /* Information about an entry in the interrupt list. */
@@ -89,7 +92,9 @@ struct ehci_soft_islot {
 
 #define EHCI_HASH_SIZE 128
 #define EHCI_COMPANION_MAX 8
-#define EHCI_SCFLG_LOSTINTRBUG 0x0002  /* workaround for VIA chipsets */
+
+#define EHCI_SCFLG_DONEINIT    0x0001  /* ehci_init() has been called. */
+#define EHCI_SCFLG_LOSTINTRBUG 0x0002  /* workaround for VIA / ATI chipsets */
 
 typedef struct ehci_softc {
        struct usbd_bus sc_bus;         /* base device */
@@ -108,8 +113,8 @@ typedef struct ehci_softc {
        char sc_vendor[32];             /* vendor string for root hub */
        int sc_id_vendor;               /* vendor ID for root hub */
 
-#if defined(__NetBSD__) || defined(__OpenBSD__)
        u_int32_t sc_cmd;               /* shadow of cmd reg during suspend */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        void *sc_powerhook;             /* cookie from power hook */
        void *sc_shutdownhook;          /* cookie from shutdown hook */
 #endif
@@ -121,7 +126,9 @@ typedef struct ehci_softc {
        usb_dma_t sc_fldma;
        ehci_link_t *sc_flist;
        u_int sc_flsize;
+#if !defined(__FreeBSD__) && !defined(__DragonFly__)
        u_int sc_rand;                  /* XXX need proper intr scheduling */
+#endif
 
        struct ehci_soft_islot sc_islots[EHCI_INTRQHS];
 
@@ -172,12 +179,13 @@ typedef struct ehci_softc {
 #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 *);
-void           ehci_init_intrs(ehci_softc_t *);
 int            ehci_intr(void *);
-#if defined(__NetBSD__) || defined(__OpenBSD__)
 int            ehci_detach(ehci_softc_t *, int);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
 int            ehci_activate(device_ptr_t, enum devact);
 #endif
+void           ehci_power(int state, void *priv);
+void           ehci_shutdown(void *v);
 
 #define MS_TO_TICKS(ms) ((ms) * hz / 1000)
 
index 027d594..f1a46ba 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * $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.8 2006/09/05 00:55:36 dillon Exp $
+ * $DragonFly: src/sys/bus/usb/hid.c,v 1.9 2006/12/10 02:03:56 sephe Exp $
  */
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
 #endif
 #include <sys/malloc.h>
 
-#include "usb.h"
-#include "usbhid.h"
+#include <bus/usb/usb.h>
+#include <bus/usb/usbhid.h>
 
-#include "hid.h"
+#include <bus/usb/hid.h>
 
 #ifdef USB_DEBUG
 #define DPRINTF(x)     if (usbdebug) logprintf x
index 7f94dc2..de915d1 100644 (file)
@@ -1,14 +1,17 @@
-/*
- * $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.16 2006/10/25 20:55:52 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.154.2.4 2006/06/26 00:31:25 iedowse Exp $  */
+/*     $DragonFly: src/sys/bus/usb/ohci.c,v 1.17 2006/12/10 02:03:56 sephe Exp $       */
+
 /* Also, already ported:
  *     $NetBSD: ohci.c,v 1.140 2003/05/13 04:42:00 gson Exp $
  *     $NetBSD: ohci.c,v 1.141 2003/09/10 20:08:29 mycroft Exp $
  *     $NetBSD: ohci.c,v 1.142 2003/10/11 03:04:26 toshii Exp $
  *     $NetBSD: ohci.c,v 1.143 2003/10/18 04:50:35 simonb Exp $
- *     $NetBSD: 1.144 - 1.150 ported
+ *     $NetBSD: ohci.c,v 1.144 2003/11/23 19:18:06 augustss Exp $
+ *     $NetBSD: ohci.c,v 1.145 2003/11/23 19:20:25 augustss Exp $
+ *     $NetBSD: ohci.c,v 1.146 2003/12/29 08:17:10 toshii Exp $
+ *     $NetBSD: ohci.c,v 1.147 2004/06/22 07:20:35 mycroft Exp $
+ *     $NetBSD: ohci.c,v 1.148 2004/06/22 18:27:46 mycroft Exp $
  */
 
 /*
 #include <sys/proc.h>
 #include <sys/queue.h>
 #include <sys/sysctl.h>
-
+#ifdef __DragonFly__
 #include <sys/thread2.h>
+#endif
 
 #include <machine/endian.h>
 
-#include "usb.h"
-#include "usbdi.h"
-#include "usbdivar.h"
-#include "usb_mem.h"
-#include "usb_quirks.h"
+#include <bus/usb/usb.h>
+#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdivar.h>
+#include <bus/usb/usb_mem.h>
+#include <bus/usb/usb_quirks.h>
 
-#include "ohcireg.h"
-#include "ohcivar.h"
+#include <bus/usb/ohcireg.h>
+#include <bus/usb/ohcivar.h>
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-#include <machine/clock.h>
-
 #define delay(d)                DELAY(d)
 #endif
 
@@ -377,17 +379,22 @@ ohci_activate(device_ptr_t self, enum devact act)
        }
        return (rv);
 }
+#endif
 
 int
 ohci_detach(struct ohci_softc *sc, int flags)
 {
-       int rv = 0;
+       int i, rv = 0;
 
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        if (sc->sc_child != NULL)
                rv = config_detach(sc->sc_child, flags);
 
        if (rv != 0)
                return (rv);
+#else
+       sc->sc_dying = 1;
+#endif
 
        usb_uncallout(sc->sc_tmo_rhsc, ohci_rhsc_enable, sc);
 
@@ -396,13 +403,20 @@ ohci_detach(struct ohci_softc *sc, int flags)
        shutdownhook_disestablish(sc->sc_shutdownhook);
 #endif
 
+       OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+       OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+
        usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
 
-       /* free data structures XXX */
+       for (i = 0; i < OHCI_NO_EDS; i++)
+               ohci_free_sed(sc, sc->sc_eds[i]);
+       ohci_free_sed(sc, sc->sc_isoc_head);
+       ohci_free_sed(sc, sc->sc_bulk_head);
+       ohci_free_sed(sc, sc->sc_ctrl_head);
+       usb_freemem(&sc->sc_bus, &sc->sc_hccadma);
 
        return (rv);
 }
-#endif
 
 ohci_soft_ed_t *
 ohci_alloc_sed(ohci_softc_t *sc)
@@ -508,7 +522,7 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
        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);
+           OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_SET_DI(6));
 
        for (;;) {
                next = ohci_alloc_std(sc);
@@ -531,33 +545,31 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
                 *
                 * 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.
+                * 
+                * Note that if we are gathering data from multiple SMALL
+                * segments, e.g. mbufs, we need to do special gymnastics,
+                * e.g. bounce buffering or data aggregation,
+                * BEFORE WE GET HERE because a bulk USB transfer must
+                * consist of maximally sized packets right up to the end.
+                * A shorter than maximal packet means that it is the end
+                * of the transfer. If the data transfer length is a
+                * multiple of the packet size, then a 0 byte
+                * packet will be the signal of the end of transfer.
+                * Since packets can't cross TDs this means that
+                * each TD except the last one must cover an exact multiple
+                * of the maximal packet length.
                 */
-               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 */
+               if (OHCI_PAGE_OFFSET(dataphys) + len <= (2 * OHCI_PAGE_SIZE)) {
+                       /* We can handle all that remains in this TD */
                        curlen = len;
                } else {
-                       /* XXX The calculation below is wrong and could
-                        * result in a packet that is not a multiple of the
-                        * MaxPacketSize in the case where the buffer does not
-                        * start on an appropriate address (like for example in
-                        * the case of an mbuf cluster). You'll get an early
-                        * short packet.
-                        */
                        /* must use multiple TDs, fill as much as possible. */
                        curlen = 2 * OHCI_PAGE_SIZE -
                                 OHCI_PAGE_OFFSET(dataphys);
                        /* the length must be a multiple of the max size */
                        curlen -= curlen %
                            UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize);
-#ifdef DIAGNOSTIC
-                       if (curlen == 0)
-                               panic("ohci_alloc_std: curlen == 0");
-#endif
+                       KASSERT((curlen != 0), ("ohci_alloc_std: curlen == 0"));
                }
                DPRINTFN(4,("ohci_alloc_std_chain: dataphys=0x%08x "
                            "len=%d curlen=%d\n",
@@ -638,7 +650,7 @@ ohci_alloc_sitd(ohci_softc_t *sc)
        if (sc->sc_freeitds == NULL) {
                DPRINTFN(2, ("ohci_alloc_sitd: allocating chunk\n"));
                crit_exit();
-               err = usb_allocmem(&sc->sc_bus, 
+               err = usb_allocmem(&sc->sc_bus,
                                   OHCI_SITD_SIZE * OHCI_SITD_CHUNK,
                                   OHCI_ITD_ALIGN, &dma);
                if (err)
@@ -826,12 +838,6 @@ ohci_init(ohci_softc_t *sc)
        return (err);
 }
 
-void
-ohci_init_intrs(ohci_softc_t *sc)
-{
-       OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE);
-}
-
 Static usbd_status
 ohci_controller_init(ohci_softc_t *sc)
 {
@@ -906,12 +912,9 @@ ohci_controller_init(ohci_softc_t *sc)
        OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0));
        OWRITE4(sc, OHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr);
        OWRITE4(sc, OHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr);
-
-       /*
-        * disable all interrupts to avoid events while we are initializing
-        * the device.
-        */
+       /* disable all interrupts and then switch on all desired interrupts */
        OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+       OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE);
        /* switch on desired functional features */
        ctl = OREAD4(sc, OHCI_CONTROL);
        ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR);
@@ -987,6 +990,9 @@ ohci_allocx(struct usbd_bus *bus)
        }
        if (xfer != NULL) {
                memset(xfer, 0, sizeof (struct ohci_xfer));
+               usb_init_task(&OXFER(xfer)->abort_task, ohci_timeout_task,
+                   xfer);
+               OXFER(xfer)->ohci_xfer_flags = 0;
 #ifdef DIAGNOSTIC
                xfer->busy_free = XFER_BUSY;
 #endif
@@ -1000,40 +1006,12 @@ 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;
-       ohci_soft_itd_t **scanp;
-       struct ohci_pipe *opipe;
-       ohci_soft_ed_t *sed;
-       ohci_physaddr_t tdphys;
-       int neednewtail;
 
-        if (oxfer->ohci_xfer_flags & OHCI_ISOC_DIRTY) {
+       if (oxfer->ohci_xfer_flags & OHCI_ISOC_DIRTY) {
                crit_enter();
-               opipe = (struct ohci_pipe *)xfer->pipe;
-               KKASSERT(opipe != NULL);
-               sed = opipe->sed;
-
-               scanp = (ohci_soft_itd_t **)&xfer->hcpriv;
-               neednewtail = 0;
-               while ((sitd = *scanp) != NULL) {
-                       if (sitd->xfer != xfer)
-                               break;
-                       if (opipe->tail.itd == sitd)
-                               neednewtail = 1;
-                       *scanp = sitd->nextitd;
-                       sitd->nextitd = NULL;
+               for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer;
+                    sitd = sitd->nextitd)
                        ohci_free_sitd(sc, sitd);
-               }
-               if (neednewtail) {
-                       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.*/
-               }
                crit_exit();
        }
 
@@ -1115,8 +1093,6 @@ ohci_power(int why, void *v)
                usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY);
                sc->sc_control = sc->sc_intre = 0;
                sc->sc_bus.use_polling--;
-               ohci_init_intrs(sc);
-
        }
        crit_exit();
 }
@@ -1169,7 +1145,7 @@ ohci_intr(void *p)
 {
        ohci_softc_t *sc = p;
 
-       if (sc == NULL || sc->sc_dying)
+       if (sc->sc_dying || (sc->sc_flags & OHCI_SCFLG_DONEINIT) == 0)
                return (0);
 
        /* If we get an interrupt while polling, then just ignore it. */
@@ -1192,7 +1168,7 @@ ohci_intr1(ohci_softc_t *sc)
        DPRINTFN(14,("ohci_intr1: enter\n"));
 
        /* In case the interrupt occurs before initialization has completed. */
-       if (sc == NULL || sc->sc_hcca == NULL) {
+       if (sc->sc_hcca == NULL) {
 #ifdef DIAGNOSTIC
                printf("ohci_intr: sc->sc_hcca == NULL\n");
 #endif
@@ -1375,11 +1351,12 @@ ohci_softintr(void *v)
 {
        ohci_softc_t *sc = v;
        ohci_soft_itd_t *sitd, *sidone, *sitdnext;
-       ohci_soft_td_t  *std,  *sdone,  *stdnext;
+       ohci_soft_td_t  *std,  *sdone,  *stdnext, *p, *n;
        usbd_xfer_handle xfer;
        struct ohci_pipe *opipe;
        int len, cc;
-
+       int i, j, iframes;
+       
        DPRINTFN(10,("ohci_softintr: enter\n"));
 
        sc->sc_bus.intr_context++;
@@ -1405,14 +1382,11 @@ ohci_softintr(void *v)
                stdnext = std->dnext;
                DPRINTFN(10, ("ohci_process_done: std=%p xfer=%p hcpriv=%p\n",
                                std, xfer, (xfer ? xfer->hcpriv : NULL)));
-               if (xfer == NULL || (std->flags & OHCI_TD_HANDLED)) {
+               if (xfer == NULL) {
                        /*
                         * 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;
                }
@@ -1423,7 +1397,6 @@ ohci_softintr(void *v)
                        /* Handled by abort routine. */
                        continue;
                }
-               usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
 
                len = std->len;
                if (std->td.td_cbp != 0)
@@ -1435,38 +1408,32 @@ ohci_softintr(void *v)
                        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;
-                               crit_enter();
-                               usb_transfer_complete(xfer);
-                               crit_exit();
-                       }
-                       ohci_free_std(sc, std);
-               } else {
+               if (cc != OHCI_CC_NO_ERROR) {
                        /*
                         * Endpoint is halted.  First unlink all the TDs
                         * belonging to the failed transfer, and then restart
                         * the endpoint.
                         */
-                       ohci_soft_td_t *p, *n;
                        opipe = (struct ohci_pipe *)xfer->pipe;
 
                        DPRINTFN(15,("ohci_process_done: error cc=%d (%s)\n",
                          OHCI_TD_GET_CC(le32toh(std->td.td_flags)),
                          ohci_cc_strs[OHCI_TD_GET_CC(le32toh(std->td.td_flags))]));
-
-
-                       /* Mark all the TDs in the done queue for the current
-                        * xfer as handled
-                        */
-                       for (p = stdnext; p; p = p->dnext) {
-                               if (p->xfer == xfer)
-                                       p->flags |= OHCI_TD_HANDLED;
+                       usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
+                       usb_rem_task(OXFER(xfer)->xfer.pipe->device,
+                           &OXFER(xfer)->abort_task);
+
+                       /* Remove all this xfer's TDs from the done queue. */
+                       for (p = std; p->dnext != NULL; p = p->dnext) {
+                               if (p->dnext->xfer != xfer)
+                                       continue;
+                               p->dnext = p->dnext->dnext;
                        }
+                       /* The next TD may have been removed. */
+                       stdnext = std->dnext;
 
-                       /* remove TDs */
-                       for (p = std; p->xfer == xfer; p = n) {
+                       /* Remove all TDs belonging to this xfer. */
+                       for (p = xfer->hcpriv; p->xfer == xfer; p = n) {
                                n = p->nexttd;
                                ohci_free_std(sc, p);
                        }
@@ -1482,7 +1449,27 @@ ohci_softintr(void *v)
                        crit_enter();
                        usb_transfer_complete(xfer);
                        crit_exit();
+                       continue;
                }
+               /*
+                * Skip intermediate TDs. They remain linked from
+                * xfer->hcpriv and we free them when the transfer completes.
+                */
+               if ((std->flags & OHCI_CALL_DONE) == 0)
+                       continue;
+
+               /* Normal transfer completion */
+               usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
+               usb_rem_task(OXFER(xfer)->xfer.pipe->device,
+                   &OXFER(xfer)->abort_task);
+               for (p = xfer->hcpriv; p->xfer == xfer; p = n) {
+                       n = p->nexttd;
+                       ohci_free_std(sc, p);
+               }
+               xfer->status = USBD_NORMAL_COMPLETION;
+               crit_enter();
+               usb_transfer_complete(xfer);
+               crit_exit();
        }
 
 #ifdef USB_DEBUG
@@ -1519,21 +1506,31 @@ ohci_softintr(void *v)
                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;
-                               crit_enter();
-                               usb_transfer_complete(xfer);
-                               crit_exit();
+               if (sitd->flags & OHCI_CALL_DONE) {
+                       ohci_soft_itd_t *next;
+
+                       opipe->u.iso.inuse -= xfer->nframes;
+                       xfer->status = USBD_NORMAL_COMPLETION;
+                       for (i = 0, sitd = xfer->hcpriv;;sitd = next) {
+                               next = sitd->nextitd;
+                               if (OHCI_ITD_GET_CC(sitd->itd.itd_flags) != OHCI_CC_NO_ERROR)
+                                       xfer->status = USBD_IOERROR;
+
+                               if (xfer->status == USBD_NORMAL_COMPLETION) {
+                                       iframes = OHCI_ITD_GET_FC(sitd->itd.itd_flags);
+                                       for (j = 0; j < iframes; i++, j++) {
+                                               len = le16toh(sitd->itd.itd_offset[j]);
+                                               len =
+                                                  (OHCI_ITD_PSW_GET_CC(len) ==
+                                                   OHCI_CC_NOT_ACCESSED) ? 0 :
+                                                   OHCI_ITD_PSW_LENGTH(len);
+                                               xfer->frlengths[i] = len;
+                                       }
+                               }
+                               if (sitd->flags & OHCI_CALL_DONE)
+                                       break;
                        }
-               } else {
-                       /* XXX Do more */
-                       xfer->status = USBD_IOERROR;
+
                        crit_enter();
                        usb_transfer_complete(xfer);
                        crit_exit();
@@ -1785,7 +1782,7 @@ ohci_device_request(usbd_xfer_handle xfer)
        memcpy(KERNADDR(&opipe->u.ctl.reqdma, 0), req, sizeof *req);
 
        setup->td.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC |
-                                    OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR);
+                                    OHCI_TD_TOGGLE_0 | OHCI_TD_SET_DI(6));
        setup->td.td_cbp = htole32(DMAADDR(&opipe->u.ctl.reqdma, 0));
        setup->nexttd = next;
        setup->td.td_nexttd = htole32(next->physaddr);
@@ -1875,10 +1872,22 @@ ohci_rem_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head)
        /* XXX */
        for (p = head; p != NULL && p->next != sed; p = p->next)
                ;
+#if 0
        if (p == NULL)
                panic("ohci_rem_ed: ED not found");
-       p->next = sed->next;
-       p->ed.ed_nexted = sed->ed.ed_nexted;
+#else
+       /*
+        * XXX
+        * p == NULL if ohci is detaching and there are still devices
+        * using ohci (e.g. usb sticks are still plugged in).  But
+        * the real solution should be correcting ohci_free_sed() or
+        * correctly use it.
+        */
+       if (p != NULL) {
+               p->next = sed->next;
+               p->ed.ed_nexted = sed->ed.ed_nexted;
+       }
+#endif
 }
 
 /*
@@ -1893,7 +1902,7 @@ ohci_rem_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head)
 
 #define HASH(a) (((a) >> 4) % OHCI_HASH_SIZE)
 /*
- * Called from a critical section 
+ * Called from a critical section
  */
 void
 ohci_hash_add_td(ohci_softc_t *sc, ohci_soft_td_t *std)
@@ -1904,7 +1913,7 @@ ohci_hash_add_td(ohci_softc_t *sc, ohci_soft_td_t *std)
 }
 
 /*
- * Called from a critical section 
+ * Called from a critical section
  */
 void
 ohci_hash_rem_td(ohci_softc_t *sc, ohci_soft_td_t *std)
@@ -1937,7 +1946,7 @@ ohci_hash_find_td(ohci_softc_t *sc, ohci_physaddr_t a)
 }
 
 /*
- * Called from a critical section 
+ * Called from a critical section
  */
 void
 ohci_hash_add_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
@@ -1951,7 +1960,7 @@ ohci_hash_add_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
 }
 
 /*
- * Called from a critical section 
+ * Called from a critical section
  */
 void
 ohci_hash_rem_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
@@ -1991,8 +2000,8 @@ ohci_timeout(void *addr)
        }
 
        /* 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);
+       usb_add_task(oxfer->xfer.pipe->device, &oxfer->abort_task,
+                    USB_TASKQ_HC);
 }
 
 void
@@ -2154,7 +2163,9 @@ ohci_open(usbd_pipe_handle pipe)
                        (dev->speed == USB_SPEED_LOW ? OHCI_ED_SPEED : 0) |
                        fmt |
                        OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize)));
-               sed->ed.ed_headp = sed->ed.ed_tailp = htole32(tdphys);
+               sed->ed.ed_headp = htole32(tdphys |
+                   (pipe->endpoint->savedtoggle ? OHCI_TOGGLECARRY : 0));
+               sed->ed.ed_tailp = htole32(tdphys);
 
                switch (xfertype) {
                case UE_CONTROL:
@@ -2239,13 +2250,15 @@ ohci_close_pipe(usbd_pipe_handle pipe, ohci_soft_ed_t *head)
        /* Make sure the host controller is not touching this ED */
        usb_delay_ms(&sc->sc_bus, 1);
        crit_exit();
+       pipe->endpoint->savedtoggle =
+           (le32toh(sed->ed.ed_headp) & OHCI_TOGGLECARRY) ? 1 : 0;
        ohci_free_sed(sc, opipe->sed);
 }
 
 /*
  * Abort a device request.
  * If this routine is called from a critical section it guarantees that
- * the request will be removed from the hardware scheduling and 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.
@@ -2255,6 +2268,7 @@ ohci_close_pipe(usbd_pipe_handle pipe, ohci_soft_ed_t *head)
 void
 ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
 {
+       struct ohci_xfer *oxfer = OXFER(xfer);
        struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
        ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
        ohci_soft_ed_t *sed = opipe->sed;
@@ -2269,20 +2283,40 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
                crit_enter();
                xfer->status = status;  /* make software ignore it */
                usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
+               usb_rem_task(xfer->pipe->device, &OXFER(xfer)->abort_task);
                usb_transfer_complete(xfer);
                crit_exit();
-               return;
        }
 
        if (xfer->device->bus->intr_context /* || !curproc REMOVED DFly */)
                panic("ohci_abort_xfer: not in process context");
 
+       /*
+        * If an abort is already in progress then just wait for it to
+        * complete and return.
+        */
+       if (oxfer->ohci_xfer_flags & OHCI_XFER_ABORTING) {
+               DPRINTFN(2, ("ohci_abort_xfer: already aborting\n"));
+               /* No need to wait if we're aborting from a timeout. */
+               if (status == USBD_TIMEOUT)
+                       return;
+               /* Override the status which might be USBD_TIMEOUT. */
+               xfer->status = status;
+               DPRINTFN(2, ("ohci_abort_xfer: waiting for abort to finish\n"));
+               oxfer->ohci_xfer_flags |= OHCI_XFER_ABORTWAIT;
+               while (oxfer->ohci_xfer_flags & OHCI_XFER_ABORTING)
+                       tsleep(&oxfer->ohci_xfer_flags, 0, "ohciaw", 0);
+               return;
+       }
+
        /*
         * Step 1: Make interrupt routine and hardware ignore xfer.
         */
        crit_enter();
+       oxfer->ohci_xfer_flags |= OHCI_XFER_ABORTING;
        xfer->status = status;  /* make software ignore it */
        usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
+       usb_rem_task(xfer->pipe->device, &OXFER(xfer)->abort_task);
        crit_exit();
        DPRINTFN(1,("ohci_abort_xfer: stop ed=%p\n", sed));
        sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */
@@ -2301,7 +2335,6 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
 #ifdef USB_USE_SOFTINTR
        tsleep(&sc->sc_softwake, 0, "ohciab", 0);
 #endif /* USB_USE_SOFTINTR */
-       crit_exit();
 
        /*
         * Step 3: Remove any vestiges of the xfer from the hardware.
@@ -2310,10 +2343,10 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
         * the TDs of this xfer we check if the hardware points to
         * any of them.
         */
-       crit_enter();
        p = xfer->hcpriv;
 #ifdef DIAGNOSTIC
        if (p == NULL) {
+               oxfer->ohci_xfer_flags &= ~OHCI_XFER_ABORTING; /* XXX */
                crit_exit();
                printf("ohci_abort_xfer: hcpriv is NULL\n");
                return;
@@ -2335,7 +2368,7 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
        }
        /* Zap headp register if hardware pointed inside the xfer. */
        if (hit) {
-               DPRINTFN(1,("ohci_abort_xfer: set hd=0x%08x, tl=0x%08x\n",
+               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 {
@@ -2350,6 +2383,12 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
        /*
         * Step 5: Execute callback.
         */
+       /* Do the wakeup first to avoid touching the xfer after the callback. */
+       oxfer->ohci_xfer_flags &= ~OHCI_XFER_ABORTING;
+       if (oxfer->ohci_xfer_flags & OHCI_XFER_ABORTWAIT) {
+               oxfer->ohci_xfer_flags &= ~OHCI_XFER_ABORTWAIT;
+               wakeup(&oxfer->ohci_xfer_flags);
+       }
        usb_transfer_complete(xfer);
 
        crit_exit();
@@ -3284,7 +3323,6 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer)
        struct iso *iso = &opipe->u.iso;
        struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer;
        ohci_soft_itd_t *sitd, *nsitd;
-       ohci_soft_itd_t **scanp;
        ohci_physaddr_t buf, offs, noffs, bp0, tdphys;
        int i, ncur, nframes;
 
@@ -3302,27 +3340,14 @@ ohci_device_isoc_enter(usbd_xfer_handle xfer)
                            iso->next));
        }
 
-       /*
-        * Not sure what is going on here.  This appears to be trying to
-        * free the previous xfer related to xfer, but why wouldn't
-        * sitd->xfer always equal xfer during the scan ?
-        */
        if (xfer->hcpriv) {
-               int neednewtail = 0;
-
                crit_enter();
-               scanp = (ohci_soft_itd_t **)&xfer->hcpriv;
-               while ((sitd = *scanp) != NULL) {
-                       if (sitd->xfer != xfer)
-                               break;
-                       if (opipe->tail.itd == sitd)
-                               neednewtail = 1;
-                       *scanp = sitd->nextitd;
-                       sitd->nextitd = NULL;
-                       ohci_free_sitd(sc, sitd);
-               }
+               for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer;
+                    sitd = sitd->nextitd)
+                       ohci_free_sitd(sc, sitd); /* Free ITDs in prev xfer*/
                crit_exit();
-               if (neednewtail) {
+
+               if (sitd == NULL) {
                        sitd = ohci_alloc_sitd(sc);
                        if (sitd == NULL)
                                panic("cant alloc isoc");
@@ -3465,7 +3490,7 @@ ohci_device_isoc_abort(usbd_xfer_handle xfer)
        ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
        ohci_soft_ed_t *sed;
        ohci_soft_itd_t *sitd, *tmp_sitd;
-       int undone,num_sitds;
+       int undone, num_sitds;
 
        crit_enter();
        opipe->aborting = 1;
@@ -3605,5 +3630,4 @@ ohci_device_isoc_close(usbd_pipe_handle pipe)
        opipe->tail.itd->isdone = 1;
 #endif
        ohci_free_sitd(sc, opipe->tail.itd);    /* Next `avail free' sitd.*/
-       opipe->tail.itd = NULL;
 }
index 20bcde9..a941baa 100644 (file)
@@ -1,4 +1,4 @@
-/*-
+/*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
@@ -34,8 +34,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/ohci_pci.c,v 1.38 2003/12/22 15:18:46 shiba Exp $
- * $DragonFly: src/sys/bus/usb/ohci_pci.c,v 1.5 2006/10/25 20:55:52 dillon Exp $
+ * $FreeBSD: src/sys/dev/usb/ohci_pci.c,v 1.44.2.1 2006/01/29 01:26:46 iedowse Exp $
+ * $DragonFly: src/sys/bus/usb/ohci_pci.c,v 1.6 2006/12/10 02:03:56 sephe Exp $
  */
 
 /*
 #define PCI_OHCI_VENDORID_ACERLABS     0x10b9
 #define PCI_OHCI_VENDORID_AMD          0x1022
 #define PCI_OHCI_VENDORID_APPLE                0x106b
+#define PCI_OHCI_VENDORID_ATI          0x1002
 #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_VENDORID_SUN          0x108e
 
 #define PCI_OHCI_DEVICEID_ALADDIN_V    0x523710b9
 static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB controller";
@@ -89,6 +91,10 @@ 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_SB400_1      0x43741002
+#define PCI_OHCI_DEVICEID_SB400_2      0x43751002
+static const char *ohci_device_sb400 = "ATI SB400 USB Controller";
+
 #define PCI_OHCI_DEVICEID_FIRELINK     0xc8611045
 static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB controller";
 
@@ -110,6 +116,9 @@ 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";
 
+#define PCI_OHCI_DEVICEID_PCIO2USB     0x1103108e
+static const char *ohci_device_pcio2usb = "Sun PCIO-2 USB controller";
+
 static const char *ohci_device_generic = "OHCI (generic) USB controller";
 
 #define PCI_OHCI_BASE_REG      0x10
@@ -138,17 +147,20 @@ static int
 ohci_pci_resume(device_t self)
 {
        ohci_softc_t *sc = device_get_softc(self);
-       u_int32_t reg, int_line;
+
+#ifndef BURN_BRIDGES
+       uint32_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);
+               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);
        }
+#endif /* !BURN_BRIDGES */
 
        ohci_power(PWR_RESUME, sc);
        bus_generic_resume(self);
@@ -168,6 +180,9 @@ ohci_pci_match(device_t self)
                return (ohci_device_amd756);
        case PCI_OHCI_DEVICEID_AMD766:
                return (ohci_device_amd766);
+       case PCI_OHCI_DEVICEID_SB400_1:
+       case PCI_OHCI_DEVICEID_SB400_2:
+               return (ohci_device_sb400);
        case PCI_OHCI_DEVICEID_USB0670:
                return (ohci_device_usb0670);
        case PCI_OHCI_DEVICEID_USB0673:
@@ -182,6 +197,8 @@ ohci_pci_match(device_t self)
                return (ohci_device_sis5571);
        case PCI_OHCI_DEVICEID_KEYLARGO:
                return (ohci_device_keylargo);
+       case PCI_OHCI_DEVICEID_PCIO2USB:
+               return (ohci_device_pcio2usb);
        default:
                if (pci_get_class(self) == PCIC_SERIALBUS
                    && pci_get_subclass(self) == PCIS_SERIALBUS_USB
@@ -218,9 +235,17 @@ ohci_pci_attach(device_t self)
 
        pci_enable_busmaster(self);
 
+       /*
+        * Some Sun PCIO-2 USB controllers have their intpin register
+        * bogusly set to 0, although it should be 4. Correct that.
+        */
+       if (pci_get_devid(self) == PCI_OHCI_DEVICEID_PCIO2USB &&
+           pci_get_intpin(self) == 0)
+               pci_set_intpin(self, 4);
+
        rid = PCI_CBMEM;
-       sc->io_res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid,
-           0, ~0, 1, RF_ACTIVE);
+       sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+           RF_ACTIVE);
        if (!sc->io_res) {
                device_printf(self, "Could not map memory\n");
                return ENXIO;
@@ -229,7 +254,7 @@ ohci_pci_attach(device_t self)
        sc->ioh = rman_get_bushandle(sc->io_res);
 
        rid = 0;
-       sc->irq_res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1,
+       sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
            RF_SHAREABLE | RF_ACTIVE);
        if (sc->irq_res == NULL) {
                device_printf(self, "Could not allocate irq\n");
@@ -242,7 +267,7 @@ ohci_pci_attach(device_t self)
                ohci_pci_detach(self);
                return ENOMEM;
        }
-       device_set_ivars(sc->sc_bus.bdev, sc);
+       device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
 
        /* ohci_pci_match will never return NULL if ohci_pci_probe succeeded */
        device_set_desc(sc->sc_bus.bdev, ohci_pci_match(self));
@@ -256,6 +281,9 @@ ohci_pci_attach(device_t self)
        case PCI_OHCI_VENDORID_APPLE:
                sprintf(sc->sc_vendor, "Apple");
                break;
+       case PCI_OHCI_VENDORID_ATI:
+               sprintf(sc->sc_vendor, "ATI");
+               break;
        case PCI_OHCI_VENDORID_CMDTECH:
                sprintf(sc->sc_vendor, "CMDTECH");
                break;
@@ -288,14 +316,16 @@ ohci_pci_attach(device_t self)
                return ENXIO;
        }
        err = ohci_init(sc);
-       if (!err)
+       if (!err) {
+               sc->sc_flags |= OHCI_SCFLG_DONEINIT;
                err = device_probe_and_attach(sc->sc_bus.bdev);
+       }
+
        if (err) {
                device_printf(self, "USB init failed\n");
                ohci_pci_detach(self);
                return EIO;
        }
-       ohci_init_intrs(sc);
        return 0;
 }
 
@@ -304,17 +334,10 @@ 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->sc_flags & OHCI_SCFLG_DONEINIT) {
+               ohci_detach(sc, 0);
+               sc->sc_flags &= ~OHCI_SCFLG_DONEINIT;
+       }
 
        if (sc->irq_res && sc->ih) {
                int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
@@ -346,6 +369,7 @@ static device_method_t ohci_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe, ohci_pci_probe),
        DEVMETHOD(device_attach, ohci_pci_attach),
+       DEVMETHOD(device_detach, ohci_pci_detach),
        DEVMETHOD(device_suspend, ohci_pci_suspend),
        DEVMETHOD(device_resume, ohci_pci_resume),
        DEVMETHOD(device_shutdown, bus_generic_shutdown),
index 11a2858..c51c1eb 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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 $
- */
+/*     $NetBSD: ohcireg.h,v 1.17 2000/04/01 09:27:35 augustss Exp $    */
+/*     $FreeBSD: src/sys/dev/usb/ohcireg.h,v 1.22 2005/01/06 01:43:28 imp Exp $        */
+/*     $DragonFly: src/sys/bus/usb/ohcireg.h,v 1.4 2006/12/10 02:03:56 sephe Exp $     */
 
 
 /*
 #define OHCI_LES (OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE)
 #define OHCI_ALL_INTRS (OHCI_SO | OHCI_WDH | OHCI_SF | OHCI_RD | OHCI_UE | \
                         OHCI_FNO | OHCI_RHSC | OHCI_OC)
-#define OHCI_NORMAL_INTRS (OHCI_SO | OHCI_WDH | OHCI_RD | OHCI_UE | OHCI_RHSC)
+#define OHCI_NORMAL_INTRS (OHCI_WDH | OHCI_RD | OHCI_UE | OHCI_RHSC)
 
 #define OHCI_FSMPS(i) (((i-210)*6/7) << 16)
 #define OHCI_PERIODIC(i) ((i)*9/10)
index 2896002..b1b8a21 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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.5 2005/06/10 18:33:04 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.40.2.1 2005/12/04 05:52:23 iedowse Exp $        */
+/*     $DragonFly: src/sys/bus/usb/ohcivar.h,v 1.6 2006/12/10 02:03:56 sephe Exp $     */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -60,7 +58,6 @@ typedef struct ohci_soft_td {
        u_int16_t flags;
 #define OHCI_CALL_DONE 0x0001
 #define OHCI_ADD_LEN   0x0002
-#define OHCI_TD_HANDLED        0x0004          /* signal process_done has seen it */
 } ohci_soft_td_t;
 #define OHCI_STD_SIZE ((sizeof (struct ohci_soft_td) + OHCI_TD_ALIGN - 1) / OHCI_TD_ALIGN * OHCI_TD_ALIGN)
 #define OHCI_STD_CHUNK (PAGE_SIZE / OHCI_STD_SIZE)
@@ -86,8 +83,11 @@ typedef struct ohci_soft_itd {
 
 #define OHCI_HASH_SIZE 128
 
+#define OHCI_SCFLG_DONEINIT    0x0001  /* ohci_init() done. */
+
 typedef struct ohci_softc {
        struct usbd_bus sc_bus;         /* base device */
+       int sc_flags;
        bus_space_tag_t iot;
        bus_space_handle_t ioh;
        bus_size_t sc_size;
@@ -147,7 +147,9 @@ typedef struct ohci_softc {
 
        usb_callout_t sc_tmo_rhsc;
 
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        device_ptr_t sc_child;
+#endif
        char sc_dying;
 } ohci_softc_t;
 
@@ -157,14 +159,15 @@ struct ohci_xfer {
        u_int32_t ohci_xfer_flags;
 };
 #define OHCI_ISOC_DIRTY  0x01
+#define OHCI_XFER_ABORTING     0x02    /* xfer is aborting. */
+#define OHCI_XFER_ABORTWAIT    0x04    /* abort completion is being awaited. */
 
 #define OXFER(xfer) ((struct ohci_xfer *)(xfer))
 
 usbd_status    ohci_init(ohci_softc_t *);
-void           ohci_init_intrs(ohci_softc_t *);
 int            ohci_intr(void *);
+int            ohci_detach(ohci_softc_t *, int);
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-int            ohci_detach(ohci_softc_t *, int);
 int            ohci_activate(device_ptr_t, enum devact);
 #endif
 
index 2b00fcc..633fa78 100644 (file)
@@ -1,18 +1,17 @@
-/*
- * $NetBSD: uhci.c,v 1.80 2000/01/19 01:16:38 augustss 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.17 2006/10/25 20:55:52 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.162.2.1 2006/03/01 01:59:04 iedowse Exp $  */
+/*     $DragonFly: src/sys/bus/usb/uhci.c,v 1.18 2006/12/10 02:03:56 sephe Exp $       */
 
 /*     Also already incorporated from NetBSD:
  *     $NetBSD: uhci.c,v 1.172 2003/02/23 04:19:26 simonb Exp $
  *     $NetBSD: uhci.c,v 1.173 2003/05/13 04:41:59 gson Exp $
  *     $NetBSD: uhci.c,v 1.175 2003/09/12 16:18:08 mycroft Exp $
  *     $NetBSD: uhci.c,v 1.176 2003/11/04 19:11:21 mycroft Exp $
+ *     $NetBSD: uhci.c,v 1.177 2003/12/29 08:17:10 toshii Exp $
+ *     $NetBSD: uhci.c,v 1.178 2004/03/02 16:32:05 martin Exp $
+ *     $NetBSD: uhci.c,v 1.180 2004/07/17 20:12:03 mycroft Exp $
  */
 
-
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
  * All rights reserved.
 #include <sys/proc.h>
 #include <sys/queue.h>
 #include <sys/sysctl.h>
+#ifdef __DragonFly__
 #include <sys/thread2.h>
+#endif
 
 #include <machine/endian.h>
 
-#include "usb.h"
-#include "usbdi.h"
-#include "usbdivar.h"
-#include "usb_mem.h"
-#include "usb_quirks.h"
+#include <bus/usb/usb.h>
+#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdivar.h>
+#include <bus/usb/usb_mem.h>
+#include <bus/usb/usb_quirks.h>
 
-#include "uhcireg.h"
-#include "uhcivar.h"
+#include <bus/usb/uhcireg.h>
+#include <bus/usb/uhcivar.h>
 
 /* Use bandwidth reclamation for control transfers. Some devices choke on it. */
 /*#define UHCI_CTL_LOOP */
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-#include <machine/clock.h>
-
 #define delay(d)               DELAY(d)
 #endif
 
@@ -278,8 +277,8 @@ Static usbd_status  uhci_device_setintr(uhci_softc_t *sc,
 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 *);
+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 *);
@@ -384,13 +383,12 @@ struct usbd_pipe_methods uhci_device_isoc_methods = {
        LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ii), list)
 #define uhci_del_intr_info(ii) \
        do { \
-               ++ii->sc->sc_intrhead_deletion_counter; \
                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 *
+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));
@@ -572,6 +570,7 @@ uhci_activate(device_ptr_t self, enum devact act)
        }
        return (rv);
 }
+#endif
 
 int
 uhci_detach(struct uhci_softc *sc, int flags)
@@ -579,11 +578,16 @@ uhci_detach(struct uhci_softc *sc, int flags)
        usbd_xfer_handle xfer;
        int rv = 0;
 
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        if (sc->sc_child != NULL)
                rv = config_detach(sc->sc_child, flags);
 
        if (rv != 0)
                return (rv);
+#endif
+
+       UWRITE2(sc, UHCI_INTR, 0);              /* disable interrupts */
+       uhci_run(sc, 0);
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
        powerhook_disestablish(sc->sc_powerhook);
@@ -600,10 +604,10 @@ uhci_detach(struct uhci_softc *sc, int flags)
        }
 
        /* XXX free other data structures XXX */
+       usb_freemem(&sc->sc_bus, &sc->sc_dma);
 
        return (rv);
 }
-#endif
 
 usbd_status
 uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
@@ -638,6 +642,9 @@ uhci_allocx(struct usbd_bus *bus)
        if (xfer != NULL) {
                memset(xfer, 0, sizeof (struct uhci_xfer));
                UXFER(xfer)->iinfo.sc = sc;
+               usb_init_task(&UXFER(xfer)->abort_task, uhci_timeout_task,
+                   xfer);
+               UXFER(xfer)->uhci_xfer_flags = 0;
 #ifdef DIAGNOSTIC
                UXFER(xfer)->iinfo.isdone = 1;
                xfer->busy_free = XFER_BUSY;
@@ -726,9 +733,15 @@ uhci_power(int why, void *v)
 #endif
                sc->sc_bus.use_polling++;
                sc->sc_suspend = why;
+               UWRITE2(sc, UHCI_INTR, 0);      /* disable interrupts */
+               uhci_globalreset(sc);           /* reset the controller */
+               uhci_reset(sc);
                if (cmd & UHCI_CMD_RS)
                        uhci_run(sc, 0); /* in case BIOS has started it */
 
+               uhci_globalreset(sc);
+               uhci_reset(sc);
+
                /* restore saved state */
                UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0));
                UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum);
@@ -785,29 +798,29 @@ uhci_dump_td(uhci_soft_td_t *p)
                     (long)le32toh(p->td.td_buffer)));
 
        bitmask_snprintf((u_int32_t)le32toh(p->td.td_link), "\20\1T\2Q\3VF",
-                       sbuf, sizeof(sbuf));
+                        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));
+                        "\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))));
+                    "D=%d,maxlen=%d\n", sbuf, sbuf2,
+                    UHCI_TD_GET_ERRCNT(le32toh(p->td.td_status)),
+                    UHCI_TD_GET_ACTLEN(le32toh(p->td.td_status)),
+                    UHCI_TD_GET_PID(le32toh(p->td.td_token)),
+                    UHCI_TD_GET_DEVADDR(le32toh(p->td.td_token)),
+                    UHCI_TD_GET_ENDPT(le32toh(p->td.td_token)),
+                    UHCI_TD_GET_DT(le32toh(p->td.td_token)),
+                    UHCI_TD_GET_MAXLEN(le32toh(p->td.td_token))));
 }
 
 void
 uhci_dump_qh(uhci_soft_qh_t *sqh)
 {
        DPRINTFN(-1,("QH(%p) at %08x: hlink=%08x elink=%08x\n", sqh,
-               (int)sqh->physaddr, le32toh(sqh->qh.qh_hlink),
-               le32toh(sqh->qh.qh_elink)));
+                    (int)sqh->physaddr, le32toh(sqh->qh.qh_hlink),
+                    le32toh(sqh->qh.qh_elink)));
 }
 
 #if 1
@@ -1146,9 +1159,10 @@ uhci_intr(void *arg)
 {
        uhci_softc_t *sc = arg;
 
-       if (sc->sc_dying)
+       if (sc->sc_dying || (sc->sc_flags & UHCI_SCFLG_DONEINIT) == 0)
                return (0);
 
+       DPRINTFN(15,("uhci_intr: real interrupt\n"));
        if (sc->sc_bus.use_polling) {
 #ifdef DIAGNOSTIC
                DPRINTFN(16, ("uhci_intr: ignored interrupt while polling\n"));
@@ -1252,8 +1266,7 @@ void
 uhci_softintr(void *v)
 {
        uhci_softc_t *sc = v;
-       uhci_intr_info_t *ii;
-       int last_deletion_counter;
+       uhci_intr_info_t *ii, *nextii;
 
        DPRINTFN(10,("%s: uhci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev),
                     sc->sc_bus.intr_context));
@@ -1270,17 +1283,9 @@ uhci_softintr(void *v)
         * output on a slow console).
         * We scan all interrupt descriptors to see if any have
         * completed.
-        *
-        * XXX horrible hack - use a counter to detect if  the list is
-        * modified out from under us and rescan if it is.
         */
-again:
-       last_deletion_counter = sc->sc_intrhead_deletion_counter;
-       LIST_FOREACH(ii, &sc->sc_intrhead, list) {
+       LIST_FOREACH_MUTABLE(ii, &sc->sc_intrhead, list, nextii)
                uhci_check_intr(sc, ii);
-               if (sc->sc_intrhead_deletion_counter != last_deletion_counter)
-                       goto again;
-       }
 
 #ifdef USB_USE_SOFTINTR
        if (sc->sc_softwake) {
@@ -1321,7 +1326,7 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii)
                return;
        }
 #endif
-       /* 
+       /*
         * If the last TD is still active we need to check whether there
         * is an error somewhere in the middle, or whether there was a
         * short packet (SPD and not ACTIVE).
@@ -1344,12 +1349,13 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii)
                        }
                }
                DPRINTFN(12, ("uhci_check_intr: ii=%p std=%p still active\n",
-                       ii, ii->stdstart));
+                             ii, ii->stdstart));
                return;
        }
 done:
        DPRINTFN(12, ("uhci_check_intr: ii=%p done\n", ii));
        usb_uncallout(ii->xfer->timeout_handle, uhci_timeout, ii);
+       usb_rem_task(ii->xfer->pipe->device, &UXFER(ii->xfer)->abort_task);
        uhci_idone(ii);
 }
 
@@ -1455,9 +1461,9 @@ uhci_idone(uhci_intr_info_t *ii)
                char sbuf[128];
 
                bitmask_snprintf((u_int32_t)status,
-                               "\20\22BITSTUFF\23CRCTO\24NAK\25"
-                               "BABBLE\26DBUFFER\27STALLED\30ACTIVE",
-                               sbuf, sizeof(sbuf));
+                                "\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, "
@@ -1498,8 +1504,8 @@ uhci_timeout(void *addr)
        }
 
        /* 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);
+       usb_add_task(uxfer->xfer.pipe->device, &uxfer->abort_task,
+                    USB_TASKQ_HC);
 }
 
 void
@@ -1720,8 +1726,8 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len,
        int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
 
        DPRINTFN(8, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d speed=%d "
-                     "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len,
-                     upipe->pipe.device->speed, flags));
+                    "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");
@@ -1900,8 +1906,8 @@ uhci_device_bulk_abort(usbd_xfer_handle xfer)
 
 /*
  * Abort a device request.
- * If this routine is called from a critical section.  It guarentees that
- * the request will be removed from the hardware scheduling and that 
+ * If this routine is called from a critical section.  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.
@@ -1911,7 +1917,8 @@ uhci_device_bulk_abort(usbd_xfer_handle xfer)
 void
 uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
 {
-       uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+       struct uhci_xfer *uxfer = UXFER(xfer);
+       uhci_intr_info_t *ii = &uxfer->iinfo;
        struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
        uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
        uhci_soft_td_t *std;
@@ -1921,8 +1928,9 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
        if (sc->sc_dying) {
                /* If we're dying, just do the software part. */
                crit_enter();
-               xfer->status = status;  /* make software ignore it */
+               xfer->status = status;  /* make software ignore it */
                usb_uncallout(xfer->timeout_handle, uhci_timeout, xfer);
+               usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
                usb_transfer_complete(xfer);
                crit_exit();
                return;
@@ -1931,12 +1939,32 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
        if (xfer->device->bus->intr_context /* || !curproc REMOVED DFly */)
                panic("uhci_abort_xfer: not in process context");
 
+       /*
+        * If an abort is already in progress then just wait for it to
+        * complete and return.
+        */
+       if (uxfer->uhci_xfer_flags & UHCI_XFER_ABORTING) {
+               DPRINTFN(2, ("uhci_abort_xfer: already aborting\n"));
+               /* No need to wait if we're aborting from a timeout. */
+               if (status == USBD_TIMEOUT)
+                       return;
+               /* Override the status which might be USBD_TIMEOUT. */
+               xfer->status = status;
+               DPRINTFN(2, ("uhci_abort_xfer: waiting for abort to finish\n"));
+               uxfer->uhci_xfer_flags |= UHCI_XFER_ABORTWAIT;
+               while (uxfer->uhci_xfer_flags & UHCI_XFER_ABORTING)
+                       tsleep(&uxfer->uhci_xfer_flags, 0, "uhciaw", 0);
+               return;
+       }
+
        /*
         * Step 1: Make interrupt routine and hardware ignore xfer.
         */
        crit_enter();
-       xfer->status = status;  /* make software ignore it */
+       uxfer->uhci_xfer_flags |= UHCI_XFER_ABORTING;
+       xfer->status = status;  /* make software ignore it */
        usb_uncallout(xfer->timeout_handle, uhci_timeout, ii);
+       usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
        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));
@@ -1957,22 +1985,22 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
        DPRINTFN(1,("uhci_abort_xfer: tsleep\n"));
        tsleep(&sc->sc_softwake, 0, "uhciab", 0);
 #endif /* USB_USE_SOFTINTR */
-       crit_exit();
 
        /*
         * Step 3: Execute callback.
         */
-       xfer->hcpriv = ii;
-
        DPRINTFN(1,("uhci_abort_xfer: callback\n"));
-
-       crit_enter();
 #ifdef DIAGNOSTIC
        ii->isdone = 1;
 #endif
+       /* Do the wakeup first to avoid touching the xfer after the callback. */
+       uxfer->uhci_xfer_flags &= ~UHCI_XFER_ABORTING;
+       if (uxfer->uhci_xfer_flags & UHCI_XFER_ABORTWAIT) {
+               uxfer->uhci_xfer_flags &= ~UHCI_XFER_ABORTWAIT;
+               wakeup(&uxfer->uhci_xfer_flags);
+       }
        usb_transfer_complete(xfer);
        crit_exit();
-       return;
 }
 
 /* Close a device bulk pipe. */
@@ -1984,6 +2012,7 @@ 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);
+       pipe->endpoint->savedtoggle = upipe->nexttoggle;
 }
 
 usbd_status
@@ -2866,7 +2895,7 @@ uhci_open(usbd_pipe_handle pipe)
                     ed->bEndpointAddress, sc->sc_addr));
 
        upipe->aborting = 0;
-       upipe->nexttoggle = 0;
+       upipe->nexttoggle = pipe->endpoint->savedtoggle;
 
        if (pipe->device->address == sc->sc_addr) {
                switch (ed->bEndpointAddress) {
@@ -3170,8 +3199,6 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer)
                break;
        case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
                DPRINTFN(2,("uhci_root_ctrl_control wValue=0x%04x\n", value));
-               if (len == 0)
-                       break;
                switch(value >> 8) {
                case UDESC_DEVICE:
                        if ((value & 0xff) != 0) {
@@ -3201,6 +3228,8 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer)
                        memcpy(buf, &uhci_endpd, l);
                        break;
                case UDESC_STRING:
+                       if (len == 0)
+                               break;
                        *(u_int8_t *)buf = 0;
                        totlen = 1;
                        switch (value & 0xff) {
@@ -3332,9 +3361,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer)
                }
                break;
        case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
-               if (len == 0)
-                       break;
-               if (value != 0) {
+               if ((value & 0xff) != 0) {
                        err = USBD_IOERROR;
                        goto ret;
                }
index 419c251..f534cd0 100644 (file)
@@ -1,4 +1,4 @@
-/*-
+/*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
@@ -34,8 +34,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/uhci_pci.c,v 1.51 2003/11/28 05:28:29 imp Exp $
- * $DragonFly: src/sys/bus/usb/uhci_pci.c,v 1.8 2006/10/25 20:55:52 dillon Exp $
+ * $FreeBSD: src/sys/dev/usb/uhci_pci.c,v 1.57 2005/03/01 07:50:11 imp Exp $
+ * $DragonFly: src/sys/bus/usb/uhci_pci.c,v 1.9 2006/12/10 02:03:56 sephe Exp $
  */
 
 /* Universal Host Controller Interface
@@ -125,16 +125,16 @@ static const char *uhci_device_ich5_c = "Intel 82801EB (ICH5) USB controller USB
 static const char *uhci_device_ich5_d = "Intel 82801EB (ICH5) USB controller USB-D";
 
 #define PCI_UHCI_DEVICEID_ICH6_A       0x26588086
-static const char *uhci_device_ich6_a = "Intel 82801FB (ICH6) USB controller USB-A";
+static const char *uhci_device_ich6_a = "Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-A";
 
 #define PCI_UHCI_DEVICEID_ICH6_B       0x26598086
-static const char *uhci_device_ich6_b = "Intel 82801FB (ICH6) USB controller USB-B";
+static const char *uhci_device_ich6_b = "Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-B";
 
 #define PCI_UHCI_DEVICEID_ICH6_C       0x265a8086
-static const char *uhci_device_ich6_c = "Intel 82801FB (ICH6) USB controller USB-C";
+static const char *uhci_device_ich6_c = "Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-C";
 
 #define PCI_UHCI_DEVICEID_ICH6_D       0x265b8086
-static const char *uhci_device_ich6_d = "Intel 82801FB (ICH6) USB controller USB-D";
+static const char *uhci_device_ich6_d = "Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-D";
 
 #define PCI_UHCI_DEVICEID_440MX                0x719a8086
 static const char *uhci_device_440mx = "Intel 82443MX USB controller";
@@ -175,6 +175,8 @@ uhci_pci_resume(device_t self)
 {
        uhci_softc_t *sc = device_get_softc(self);
 
+       pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
+
        uhci_power(PWR_RESUME, sc);
        bus_generic_resume(self);
 
@@ -266,8 +268,8 @@ uhci_pci_attach(device_t self)
        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);
+       sc->io_res = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid,
+           RF_ACTIVE);
        if (!sc->io_res) {
                device_printf(self, "Could not map ports\n");
                return ENXIO;
@@ -279,8 +281,7 @@ uhci_pci_attach(device_t self)
        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,
+       sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
            RF_SHAREABLE | RF_ACTIVE);
        if (sc->irq_res == NULL) {
                device_printf(self, "Could not allocate irq\n");
@@ -293,7 +294,7 @@ uhci_pci_attach(device_t self)
                uhci_pci_detach(self);
                return ENOMEM;
        }
-       device_set_ivars(sc->sc_bus.bdev, sc);
+       device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
 
        /* uhci_pci_match must never return NULL if uhci_pci_probe succeeded */
        device_set_desc(sc->sc_bus.bdev, uhci_pci_match(self));
@@ -323,6 +324,14 @@ uhci_pci_attach(device_t self)
                break;
        }
 
+       err = bus_setup_intr(self, sc->irq_res, 0,
+           (driver_intr_t *) uhci_intr, sc, &sc->ih, NULL);
+       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
@@ -337,24 +346,16 @@ uhci_pci_attach(device_t self)
        pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
 
        err = uhci_init(sc);
-       if (!err)
+       if (!err) {
+               sc->sc_flags |= UHCI_SCFLG_DONEINIT;
                err = device_probe_and_attach(sc->sc_bus.bdev);
+       }
 
        if (err) {
                device_printf(self, "USB init failed\n");
                uhci_pci_detach(self);
                return EIO;
        }
-
-       err = bus_setup_intr(self, sc->irq_res, 0,
-           (driver_intr_t *) uhci_intr, sc, &sc->ih, NULL);
-       if (err) {
-               device_printf(self, "Could not setup irq, %d\n", err);
-               sc->ih = NULL;
-               uhci_pci_detach(self);
-               return ENXIO;
-       }
-
        return 0;               /* success */
 }
 
@@ -363,22 +364,11 @@ 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
+       if (sc->sc_flags & UHCI_SCFLG_DONEINIT) {
+               uhci_detach(sc, 0);
+               sc->sc_flags &= ~UHCI_SCFLG_DONEINIT;
+       }
 
-       /*
-        * 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);
@@ -412,6 +402,7 @@ static device_method_t uhci_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe, uhci_pci_probe),
        DEVMETHOD(device_attach, uhci_pci_attach),
+       DEVMETHOD(device_detach, uhci_pci_detach),
        DEVMETHOD(device_suspend, uhci_pci_suspend),
        DEVMETHOD(device_resume, uhci_pci_resume),
        DEVMETHOD(device_shutdown, bus_generic_shutdown),
index 3a92eea..06cf34b 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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 $
- */
+/*     $NetBSD: uhcireg.h,v 1.15 2002/02/11 11:41:30 augustss Exp $    */
+/*     $FreeBSD: src/sys/dev/usb/uhcireg.h,v 1.22 2005/01/06 01:43:28 imp Exp $        */
+/*     $DragonFly: src/sys/bus/usb/uhcireg.h,v 1.5 2006/12/10 02:03:56 sephe Exp $     */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
index 3ba27af..e83eb63 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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.5 2006/07/18 02:03:11 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.40 2005/03/19 19:08:46 iedowse Exp $    */
+/*     $DragonFly: src/sys/bus/usb/uhcivar.h,v 1.6 2006/12/10 02:03:56 sephe Exp $     */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -88,8 +86,12 @@ struct uhci_xfer {
        uhci_intr_info_t iinfo;
        struct usb_task abort_task;
        int curframe;
+       u_int32_t uhci_xfer_flags;
 };
 
+#define UHCI_XFER_ABORTING     0x0001  /* xfer is aborting. */
+#define UHCI_XFER_ABORTWAIT    0x0002  /* abort completion is being awaited. */
+
 #define UXFER(xfer) ((struct uhci_xfer *)(xfer))
 
 /*
@@ -134,8 +136,11 @@ struct uhci_vframe {
        u_int bandwidth;                /* max bandwidth used by this frame */
 };
 
+#define UHCI_SCFLG_DONEINIT    0x0001  /* uhci_init() done */
+
 typedef struct uhci_softc {
        struct usbd_bus sc_bus;         /* base device */
+       int sc_flags;
        bus_space_tag_t iot;
        bus_space_handle_t ioh;
        bus_size_t sc_size;
@@ -179,7 +184,6 @@ typedef struct uhci_softc {
        char sc_dying;
 
        LIST_HEAD(, uhci_intr_info) sc_intrhead;
-       int  sc_intrhead_deletion_counter;
 
        /* Info for the root hub interrupt channel. */
        int sc_ival;                    /* time between root hub intrs */
@@ -194,13 +198,15 @@ typedef struct uhci_softc {
        void *sc_shutdownhook;          /* cookie from shutdown hook */
 #endif
 
+#if defined(__NetBSD__) || defined(__OpenBSD__)
        device_ptr_t sc_child;          /* /dev/usb# device */
+#endif
 } uhci_softc_t;
 
 usbd_status    uhci_init(uhci_softc_t *);
 int            uhci_intr(void *);
-#if defined(__NetBSD__) || defined(__OpenBSD__)
 int            uhci_detach(uhci_softc_t *, int);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
 int            uhci_activate(device_ptr_t, enum devact);
 #endif
 
index 8033749..1999cb6 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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.9 2006/10/25 20:55:52 dillon Exp $
- */
+/*     $NetBSD: uhub.c,v 1.68 2004/06/29 06:30:05 mycroft Exp $        */
+/*     $FreeBSD: src/sys/dev/usb/uhub.c,v 1.69.2.1 2005/12/18 15:51:31 iedowse Exp $   */
+/*     $DragonFly: src/sys/bus/usb/uhub.c,v 1.10 2006/12/10 02:03:56 sephe Exp $       */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
 #elif defined(__FreeBSD__) || defined(__DragonFly__)
 #include <sys/module.h>
 #include <sys/bus.h>
-#include "bus_if.h"
+#include <sys/lock.h>
+#ifdef __FreeBSD__
+#include <sys/mutex.h>
+#else
+#include <sys/thread2.h>
+#endif
 #endif
 #include <sys/sysctl.h>
 
-#include "usb.h"
-#include "usbdi.h"
-#include "usbdi_util.h"
-#include "usbdivar.h"
+#include <bus/usb/usb.h>
+#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdi_util.h>
+#include <bus/usb/usbdivar.h>
 
 #define UHUB_INTR_INTERVAL 255 /* ms */
 
@@ -85,13 +88,17 @@ struct uhub_softc {
        u_int8_t                sc_status[1];   /* XXX more ports */
        u_char                  sc_running;
 };
+#define UHUB_PROTO(sc) ((sc)->sc_hub->ddesc.bDeviceProtocol)
+#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
+#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
 
 Static usbd_status uhub_explore(usbd_device_handle hub);
 Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status);
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-Static bus_driver_added_t uhub_driver_added;
 Static bus_child_detached_t uhub_child_detached;
+Static bus_child_location_str_t uhub_child_location_str;
+Static bus_child_pnpinfo_str_t uhub_child_pnpinfo_str;
 #endif
 
 
@@ -109,21 +116,27 @@ CFATTACH_DECL(uhub_uhub, sizeof(struct uhub_softc),
     uhub_match, uhub_attach, uhub_detach, uhub_activate);
 #elif defined(__FreeBSD__) || defined(__DragonFly__)
 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)
-                       );
+       DEVMETHOD(bus_child_detached, uhub_child_detached),
+       DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_str),
+       DEVMETHOD(bus_child_location_str, uhub_child_location_str),
+       DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+       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(bus_child_detached, uhub_child_detached),
+       DEVMETHOD(bus_child_location_str, uhub_child_location_str),
+       DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_str),
+       DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
        DEVMETHOD(device_probe, uhub_match),
        DEVMETHOD(device_attach, uhub_attach),
-
-       /* detach is not allowed for a root hub */
+       DEVMETHOD(device_detach, uhub_detach),
        DEVMETHOD(device_suspend, bus_generic_suspend),
        DEVMETHOD(device_resume, bus_generic_resume),
        DEVMETHOD(device_shutdown, bus_generic_shutdown),
@@ -158,20 +171,26 @@ USB_ATTACH(uhub)
        usbd_device_handle dev = uaa->device;
        char *devinfo;
        usbd_status err;
-       struct usbd_hub *hub;
+       struct usbd_hub *hub = NULL;
        usb_device_request_t req;
        usb_hub_descriptor_t hubdesc;
        int p, port, nports, nremov, pwrdly;
        usbd_interface_handle iface;
        usb_endpoint_descriptor_t *ed;
+       struct usbd_tt *tts = NULL;
 
        devinfo = kmalloc(1024, M_TEMP, M_WAITOK);
        DPRINTFN(1,("uhub_attach\n"));
        sc->sc_hub = dev;
        usbd_devinfo(dev, 1, devinfo);
        USB_ATTACH_SETUP;
-       printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
 
+       if (dev->depth > 0 && UHUB_IS_HIGH_SPEED(sc)) {
+               printf("%s: %s transaction translator%s\n",
+                   USBDEVNAME(sc->sc_dev),
+                   UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple",
+                   UHUB_IS_SINGLE_TT(sc) ? "" : "s");
+       }
        err = usbd_set_config_index(dev, 0, 1);
        if (err) {
                DPRINTF(("%s: configuration failed, error=%s\n",
@@ -214,6 +233,11 @@ USB_ATTACH(uhub)
               USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "",
               nremov, dev->self_powered ? "self" : "bus");
 
+       if (nports == 0) {
+               printf("%s: no ports, hub ignored\n", USBDEVNAME(sc->sc_dev));
+               goto bad;
+       }
+
        hub = kmalloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
                     M_USBDEV, M_WAITOK);
        dev->hub = hub;
@@ -289,10 +313,15 @@ USB_ATTACH(uhub)
         *        proceed with device attachment
         */
 
+       if (UHUB_IS_HIGH_SPEED(sc)) {
+               tts = kmalloc((UHUB_IS_SINGLE_TT(sc) ? 1 : nports) *
+                             sizeof(struct usbd_tt), M_USBDEV, M_WAITOK);
+       }
+
        /* Set up data structures */
        for (p = 0; p < nports; p++) {
                struct usbd_port *up = &hub->ports[p];
-               up->device = 0;
+               up->device = NULL;
                up->parent = dev;
                up->portno = p+1;
                if (dev->self_powered)
@@ -300,6 +329,13 @@ USB_ATTACH(uhub)
                        up->power = USB_MAX_POWER;
                else
                        up->power = USB_MIN_POWER;
+               up->restartcnt = 0;
+               if (UHUB_IS_HIGH_SPEED(sc)) {
+                       up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p];
+                       up->tt->hub = hub;
+               } else {
+                       up->tt = NULL;
+               }
        }
 
        /* XXX should check for none, individual, or ganged power? */
@@ -325,9 +361,10 @@ USB_ATTACH(uhub)
        USB_ATTACH_SUCCESS_RETURN;
 
  bad:
-       kfree(hub, M_USBDEV);
+       if (hub)
+               kfree(hub, M_USBDEV);
        kfree(devinfo, M_TEMP);
-       dev->hub = 0;
+       dev->hub = NULL;
        USB_ATTACH_ERROR_RETURN;
 }
 
@@ -364,9 +401,12 @@ uhub_explore(usbd_device_handle dev)
                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"));
+                       DPRINTF(("uhub_explore: C_PORT_ENABLED 0x%x\n", change));
                        usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
-                       if (status & UPS_PORT_ENABLED) {
+                       if (change & UPS_C_CONNECT_STATUS) {
+                               /* Ignore the port error if the device
+                                  vanished. */
+                       } else if (status & UPS_PORT_ENABLED) {
                                printf("%s: illegal enable change, port %d\n",
                                       USBDEVNAME(sc->sc_dev), port);
                        } else {
@@ -381,7 +421,7 @@ uhub_explore(usbd_device_handle dev)
                                else
                                        printf("%s: port error, giving up "
                                               "port %d\n",
-                                              USBDEVNAME(sc->sc_dev), port);
+                                         USBDEVNAME(sc->sc_dev), port);
                        }
                }
                if (!(change & UPS_C_CONNECT_STATUS)) {
@@ -461,6 +501,15 @@ uhub_explore(usbd_device_handle dev)
                        continue;
                }
 
+#if 0
+               if (UHUB_IS_HIGH_SPEED(sc) && !(status & UPS_HIGH_SPEED)) {
+                       printf("%s: port %d, transaction translation not "
+                           "implemented, low/full speed device ignored\n",
+                           USBDEVNAME(sc->sc_dev), port);
+                       continue;
+               }
+#endif
+
                /* Figure out device speed */
                if (status & UPS_HIGH_SPEED)
                        speed = USB_SPEED_HIGH;
@@ -483,8 +532,8 @@ uhub_explore(usbd_device_handle dev)
                         * some other serious problem.  Since we cannot leave
                         * at 0 we have to disable the port instead.
                         */
-                       printf("%s: device problem, disabling port %d\n",
-                              USBDEVNAME(sc->sc_dev), port);
+                       printf("%s: device problem (%s), disabling port %d\n",
+                              USBDEVNAME(sc->sc_dev), usbd_errstr(err), port);
                        usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE);
                } else {
                        /* The port set up succeeded, reset error count. */
@@ -544,8 +593,12 @@ USB_DETACH(uhub)
        DPRINTF(("uhub_detach: sc=%port\n", sc));
 #endif
 
-       if (hub == NULL)                /* Must be partially working */
+       crit_enter();
+
+       if (hub == NULL) {              /* Must be partially working */
+               crit_exit();
                return (0);
+       }
 
        usbd_abort_pipe(sc->sc_ipipe);
        usbd_close_pipe(sc->sc_ipipe);
@@ -560,53 +613,144 @@ USB_DETACH(uhub)
        usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub,
                           USBDEV(sc->sc_dev));
 
+       if (hub->ports[0].tt)
+               kfree(hub->ports[0].tt, M_USBDEV);
        kfree(hub, M_USBDEV);
        sc->sc_hub->hub = NULL;
 
+       crit_exit();
+
        return (0);
 }
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-/* Called when a device has been detached from it */
-Static void
-uhub_child_detached(device_t self, device_t child)
+int
+uhub_child_location_str(device_t cbdev, device_t child, char *buf,
+    size_t buflen)
 {
-       struct uhub_softc *sc = device_get_softc(self);
-       usbd_device_handle devhub = sc->sc_hub;
-       usbd_device_handle dev;
-       int nports;
-       int port;
-       int i;
-
-       if (!devhub->hub)
-               /* should never happen; children are only created after init */
-               panic("hub not fully initialised, but child deleted?");
-
-       nports = devhub->hub->hubdesc.bNbrPorts;
-       for (port = 0; port < nports; port++) {
-               dev = devhub->hub->ports[port].device;
-               if (dev && dev->subdevs) {
-                       for (i = 0; dev->subdevs[i]; i++) {
-                               if (dev->subdevs[i] == child) {
-                                       dev->subdevs[i] = NULL;
-                                       return;
-                               }
-                       }
-               }
-       }
+       struct uhub_softc *sc = device_get_softc(cbdev);
+       usbd_device_handle devhub = sc->sc_hub;
+       usbd_device_handle dev;
+       int nports;
+       int port;
+       int i;
+
+       crit_enter();
+       nports = devhub->hub->hubdesc.bNbrPorts;
+       for (port = 0; port < nports; port++) {
+               dev = devhub->hub->ports[port].device;
+               if (dev && dev->subdevs) {
+                       for (i = 0; dev->subdevs[i]; i++) {
+                               if (dev->subdevs[i] == child) {
+                                       if (dev->ifacenums == NULL) {
+                                               snprintf(buf, buflen,
+                                                   "port=%i", port);
+                                       } else {
+                                               snprintf(buf, buflen,
+                                                   "port=%i interface=%i",
+                                                   port, dev->ifacenums[i]);
+                                       }
+                                       goto found_dev;
+                               }
+                       }
+               }
+       }
+       DPRINTFN(0,("uhub_child_location_str: device not on hub\n"));
+       buf[0] = '\0';
+found_dev:
+       crit_exit();
+       return (0);
 }
 
-Static void
-uhub_driver_added(device_t _dev, driver_t *_driver)
+int
+uhub_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
+    size_t buflen)
 {
-       /* 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.
-        */
+       struct uhub_softc *sc = device_get_softc(cbdev);
+       usbd_device_handle devhub = sc->sc_hub;
+       usbd_device_handle dev;
+       struct usbd_interface *iface;
+       char serial[128];
+       int nports;
+       int port;
+       int i;
+
+       crit_enter();
+       nports = devhub->hub->hubdesc.bNbrPorts;
+       for (port = 0; port < nports; port++) {
+               dev = devhub->hub->ports[port].device;
+               if (dev && dev->subdevs) {
+                       for (i = 0; dev->subdevs[i]; i++) {
+                               if (dev->subdevs[i] == child) {
+                                       goto found_dev;
+                               }
+                       }
+               }
+       }
+       DPRINTFN(0,("uhub_child_pnpinfo_str: device not on hub\n"));
+       buf[0] = '\0';
+       crit_exit();
+       return (0);
 
-        ;
+found_dev:
+       /* XXX can sleep */
+       (void)usbd_get_string(dev, dev->ddesc.iSerialNumber, &serial[0]);
+       if (dev->ifacenums == NULL) {
+               snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
+                   "devclass=0x%02x devsubclass=0x%02x "
+                   "release=0x%04x sernum=\"%s\"",
+                   UGETW(dev->ddesc.idVendor), UGETW(dev->ddesc.idProduct),
+                   dev->ddesc.bDeviceClass, dev->ddesc.bDeviceSubClass,
+                   UGETW(dev->ddesc.bcdDevice), serial);
+       } else {
+               iface = &dev->ifaces[dev->ifacenums[i]];
+               snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
+                   "devclass=0x%02x devsubclass=0x%02x "
+                   "release=0x%04x sernum=\"%s\" "
+                   "intclass=0x%02x intsubclass=0x%02x",
+                   UGETW(dev->ddesc.idVendor), UGETW(dev->ddesc.idProduct),
+                   dev->ddesc.bDeviceClass, dev->ddesc.bDeviceSubClass,
+                   UGETW(dev->ddesc.bcdDevice), serial,
+                   iface->idesc->bInterfaceClass,
+                   iface->idesc->bInterfaceSubClass);
+       }
+       crit_exit();
+       return (0);
+}
+
+Static void
+uhub_child_detached(device_t self, device_t child)
+{
+       struct uhub_softc *sc = device_get_softc(self);
+       usbd_device_handle devhub = sc->sc_hub;
+       usbd_device_handle dev = NULL;
+       int nports, port, i = 0;
+
+       crit_enter();
+       nports = devhub->hub->hubdesc.bNbrPorts;
+       for (port = 0; port < nports; port++) {
+               dev = devhub->hub->ports[port].device;
+               if (dev && dev->subdevs) {
+                       for (i = 0; dev->subdevs[i]; ++i) {
+                               if (dev->subdevs[i] == child)
+                                       goto found_dev;
+                       }
+               }
+       }
+       crit_exit();
+       return;
+
+found_dev:
+#if 0
+       printf("%s: at %s", USBDEVPTRNAME(dev->subdevs[i]),
+              USBDEVPTRNAME(self));
+       if (port != 0)
+               printf(" port %d", port);
+       printf(" (addr %d) disconnected\n", dev->address);
+#endif
+       kfree(device_get_ivars(dev->subdevs[i]), M_USB);
+       dev->subdevs[i] = NULL;
+       crit_exit();
 }
 #endif
 
index ee6647f..7db1783 100644 (file)
@@ -1,13 +1,12 @@
-/*
- * $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.22 2006/10/25 20:55:52 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.106 2005/03/27 15:31:23 iedowse Exp $       */
+/*     $DragonFly: src/sys/bus/usb/usb.c,v 1.23 2006/12/10 02:03:56 sephe Exp $        */
 
 /* Also already merged from NetBSD:
  *     $NetBSD: usb.c,v 1.70 2002/05/09 21:54:32 augustss Exp $
  *     $NetBSD: usb.c,v 1.71 2002/06/01 23:51:04 lukem Exp $
  *     $NetBSD: usb.c,v 1.73 2002/09/23 05:51:19 simonb Exp $
+ *     $NetBSD: usb.c,v 1.80 2003/11/07 17:03:25 wiz Exp $
  */
 
 /*
@@ -49,8 +48,8 @@
 
 /*
  * USB specifications and other documentation can be found at
- * http://www.usb.org/developers/data/ and
- * http://www.usb.org/developers/index.html .
+ * http://www.usb.org/developers/docs/ and
+ * http://www.usb.org/developers/devclass_docs/
  */
 
 #include <sys/param.h>
@@ -58,7 +57,7 @@
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
-#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+#if __FreeBSD_version >= 500000
 #include <sys/mutex.h>
 #endif
 #if defined(__NetBSD__) || defined(__OpenBSD__)
@@ -67,6 +66,7 @@
 #include <sys/unistd.h>
 #include <sys/module.h>
 #include <sys/bus.h>
+#include <sys/fcntl.h>
 #include <sys/filio.h>
 #include <sys/uio.h>
 #endif
@@ -75,7 +75,7 @@
 #include <sys/conf.h>
 #include <sys/device.h>
 #include <sys/poll.h>
-#if defined(__FreeBSD__) && __FreeBSD_version >= 500014
+#if __FreeBSD_version >= 500014
 #include <sys/selinfo.h>
 #else
 #include <sys/select.h>
@@ -85,9 +85,9 @@
 #include <sys/sysctl.h>
 #include <sys/thread2.h>
 
-#include "usb.h"
-#include "usbdi.h"
-#include "usbdi_util.h"
+#include <bus/usb/usb.h>
+#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdi_util.h>
 
 #define USBUNIT(d)     (minor(d))      /* usb_discover device nodes, kthread */
 #define USB_DEV_MINOR  255             /* event queue device */
@@ -98,10 +98,10 @@ MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device");
 MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller");
 
 #include "usb_if.h"
-#endif /* defined(__FreeBSD__) */
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */
 
-#include "usbdivar.h"
-#include "usb_quirks.h"
+#include <bus/usb/usbdivar.h>
+#include <bus/usb/usb_quirks.h>
 
 /* Define this unconditionally in case a kernel module is loaded that
  * has been compiled with debugging options.
@@ -127,6 +127,10 @@ int        usb_noexplore = 0;
 
 struct usb_softc {
        USBBASEDEVICE   sc_dev;         /* base device */
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+       cdev_t          sc_usbdev;
+       TAILQ_ENTRY(usb_softc) sc_coldexplist; /* cold needs-explore list */
+#endif
        usbd_bus_handle sc_bus;         /* USB controller */
        struct usbd_port sc_port;       /* dummy port for root hub */
 
@@ -135,7 +139,13 @@ struct usb_softc {
        char            sc_dying;
 };
 
-TAILQ_HEAD(, usb_task) usb_all_tasks;
+struct usb_taskq {
+       TAILQ_HEAD(, usb_task)  tasks;
+       usb_proc_ptr            task_thread_proc;
+       const char              *name;
+       int                     taskcreated;    /* task thread exists. */
+};
+static struct usb_taskq usb_taskq[USB_NUM_TASKQS];
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
 cdev_decl(usb);
@@ -157,10 +167,20 @@ struct dev_ops usb_ops = {
 #endif
 
 Static void    usb_discover(void *);
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+Static bus_child_detached_t usb_child_detached;
+#endif
 Static void    usb_create_event_thread(void *);
 Static void    usb_event_thread(void *);
 Static void    usb_task_thread(void *);
-Static usb_proc_ptr    usb_task_thread_proc = NULL;
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+Static cdev_t usb_dev;         /* The /dev/usb device. */
+Static int usb_ndevs;                  /* Number of /dev/usbN devices. */
+/* Busses to explore at the end of boot-time device configuration. */
+Static TAILQ_HEAD(, usb_softc) usb_coldexplist =
+    TAILQ_HEAD_INITIALIZER(usb_coldexplist);
+#endif
 
 #define USB_MAX_EVENTS 100
 struct usb_event_q {
@@ -177,14 +197,10 @@ Static void usb_add_event(int, struct usb_event *);
 
 Static int usb_get_next_event(struct usb_event *);
 
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-/* Flag to see if we are in the cold boot process. */
-extern int cold;
-#endif
-
 Static const char *usbrev_str[] = USBREV_STR;
 
 USB_DECLARE_DRIVER_INIT(usb,
+                       DEVMETHOD(bus_child_detached, usb_child_detached),
                        DEVMETHOD(device_suspend, bus_generic_suspend),
                        DEVMETHOD(device_resume, bus_generic_resume),
                        DEVMETHOD(device_shutdown, bus_generic_shutdown)
@@ -207,7 +223,7 @@ USB_ATTACH(usb)
 #elif defined(__FreeBSD__) || defined(__DragonFly__)
        struct usb_softc *sc = device_get_softc(self);
        void *aux = device_get_ivars(self);
-       static int global_init_done = 0;
+       cdev_t tmp_dev;
 #endif
        usbd_device_handle dev;
        usbd_status err;
@@ -284,11 +300,20 @@ USB_ATTACH(usb)
                 * the keyboard will not work until after cold boot.
                 */
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-               if (cold)
+               if (cold) {
+                       /* Explore high-speed busses before others. */
+                       if (speed == USB_SPEED_HIGH) {
+                               /* XXX This hangs system. */
+                               /* dev->hub->explore(sc->sc_bus->root_hub); */
+                       } else {
+                               TAILQ_INSERT_TAIL(&usb_coldexplist, sc,
+                                   sc_coldexplist);
+                       }
+               }
 #else
                if (cold && (sc->sc_dev.dv_cfdata->cf_flags & 1))
-#endif
                        dev->hub->explore(sc->sc_bus->root_hub);
+#endif
 #endif
        } else {
                printf("%s: root hub problem, error=%d\n",
@@ -307,26 +332,30 @@ USB_ATTACH(usb)
        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 */
-       if (!global_init_done) {
+       dev_ops_add(&usb_ops, -1, device_get_unit(self));
+       tmp_dev = make_dev(&usb_ops, device_get_unit(self),
+                          UID_ROOT, GID_OPERATOR, 0660,
+                          "usb%d", device_get_unit(self));
+       sc->sc_usbdev = reference_dev(tmp_dev);
+       if (usb_ndevs++ == 0) {
                /* The device spitting out events */
                dev_ops_add(&usb_ops, -1, USB_DEV_MINOR);
-               make_dev(&usb_ops, USB_DEV_MINOR, UID_ROOT, GID_OPERATOR,
-                       0660, "usb");
-               global_init_done = 1;
+               tmp_dev = make_dev(&usb_ops, USB_DEV_MINOR,
+                                  UID_ROOT, GID_OPERATOR, 0660, "usb");
+               usb_dev = reference_dev(tmp_dev);
        }
-       dev_ops_add(&usb_ops, -1, device_get_unit(self));
-       make_dev(&usb_ops, device_get_unit(self), UID_ROOT, GID_OPERATOR,
-               0660, "usb%d", device_get_unit(self));
 #endif
 
        USB_ATTACH_SUCCESS_RETURN;
 }
 
+Static const char *taskq_names[] = USB_TASKQ_NAMES;
+
 void
 usb_create_event_thread(void *arg)
 {
        struct usb_softc *sc = arg;
-       static int created = 0;
+       int i;
 
        if (usb_kthread_create1(usb_event_thread, sc, &sc->sc_event_thread,
                           "%s", USBDEVNAME(sc->sc_dev))) {
@@ -334,13 +363,19 @@ usb_create_event_thread(void *arg)
                       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");
+
+       for (i = 0; i < USB_NUM_TASKQS; i++) {
+               struct usb_taskq *taskq = &usb_taskq[i];
+
+               if (taskq->taskcreated == 0) {
+                       taskq->taskcreated = 1;
+                       taskq->name = taskq_names[i];
+                       TAILQ_INIT(&taskq->tasks);
+                       if (usb_kthread_create2(usb_task_thread, taskq,
+                           &taskq->task_thread_proc, taskq->name)) {
+                               printf("unable to create task thread\n");
+                               panic("usb_create_event_thread task");
+                       }
                }
        }
 }
@@ -351,17 +386,46 @@ usb_create_event_thread(void *arg)
  * context ASAP.
  */
 void
-usb_add_task(usbd_device_handle dev, struct usb_task *task)
+usb_add_task(usbd_device_handle dev, struct usb_task *task, int queue)
 {
+       struct usb_taskq *taskq;
+
        crit_enter();
-       if (!task->onqueue) {
+
+       taskq = &usb_taskq[queue];
+       if (task->queue == -1) {
                DPRINTFN(2,("usb_add_task: task=%p\n", task));
-               TAILQ_INSERT_TAIL(&usb_all_tasks, task, next);
-               task->onqueue = 1;
+               TAILQ_INSERT_TAIL(&taskq->tasks, task, next);
+               task->queue = queue;
        } else {
                DPRINTFN(3,("usb_add_task: task=%p on q\n", task));
        }
-       wakeup(&usb_all_tasks);
+       wakeup(&taskq->tasks);
+
+       crit_exit();
+}
+
+void
+usb_do_task(usbd_device_handle dev, struct usb_task *task, int queue,
+           int time_out)
+{
+       struct usb_taskq *taskq;
+
+       crit_enter();
+
+       taskq = &usb_taskq[queue];
+       if (task->queue == -1) {
+               DPRINTFN(2,("usb_add_task: task=%p\n", task));
+               TAILQ_INSERT_TAIL(&taskq->tasks, task, next);
+               task->queue = queue;
+       } else {
+               DPRINTFN(3,("usb_add_task: task=%p on q\n", task));
+       }
+       wakeup(&taskq->tasks);
+
+       /* Wait until task is finished */
+       tsleep((&taskq->tasks + 1), 0, "usbdotsk", time_out);
+
        crit_exit();
 }
 
@@ -369,9 +433,9 @@ void
 usb_rem_task(usbd_device_handle dev, struct usb_task *task)
 {
        crit_enter();
-       if (task->onqueue) {
-               TAILQ_REMOVE(&usb_all_tasks, task, next);
-               task->onqueue = 0;
+       if (task->queue != -1) {
+               TAILQ_REMOVE(&usb_taskq[task->queue].tasks, task, next);
+               task->queue = -1;
        }
        crit_exit();
 }
@@ -381,6 +445,10 @@ usb_event_thread(void *arg)
 {
        struct usb_softc *sc = arg;
 
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+       mtx_lock(&Giant);
+#endif
+
        DPRINTF(("usb_event_thread: start\n"));
 
        /*
@@ -393,6 +461,8 @@ usb_event_thread(void *arg)
         */
        usb_delay_ms(sc->sc_bus, 500);
 
+       crit_enter();
+
        /* Make sure first discover does something. */
        sc->sc_bus->needs_explore = 1;
        usb_discover(sc);
@@ -413,6 +483,8 @@ usb_event_thread(void *arg)
        }
        sc->sc_event_thread = NULL;
 
+       crit_exit();
+
        /* In case parent is waiting for us to exit. */
        wakeup(sc);
 
@@ -424,29 +496,40 @@ void
 usb_task_thread(void *arg)
 {
        struct usb_task *task;
+       struct usb_taskq *taskq;
 
 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
        mtx_lock(&Giant);
 #endif
 
-       DPRINTF(("usb_task_thread: start\n"));
-
        crit_enter();
-       for (;;) {
-               task = TAILQ_FIRST(&usb_all_tasks);
+
+       taskq = arg;
+       DPRINTF(("usb_task_thread: start taskq %s\n", taskq->name));
+
+       while (usb_ndevs > 0) {
+               task = TAILQ_FIRST(&taskq->tasks);
                if (task == NULL) {
-                       tsleep(&usb_all_tasks, 0, "usbtsk", 0);
-                       task = TAILQ_FIRST(&usb_all_tasks);
+                       tsleep(&taskq->tasks, 0, "usbtsk", 0);
+                       task = TAILQ_FIRST(&taskq->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;
+                       TAILQ_REMOVE(&taskq->tasks, task, next);
+                       task->queue = -1;
                        crit_exit();
                        task->fun(task->arg);
                        crit_enter();
+                       wakeup((&taskq->tasks + 1));
                }
        }
+
+       crit_exit();
+
+       taskq->taskcreated = 0;
+       wakeup(&taskq->taskcreated);
+
+       DPRINTF(("usb_event_thread: exit\n"));
 }
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
@@ -695,22 +778,16 @@ usb_discover(void *v)
         * but this is guaranteed since this function is only called
         * from the event thread for the controller.
         */
-#if defined(__FreeBSD__) || defined(__DragonFly__)
        crit_enter();
-#endif
        while (sc->sc_bus->needs_explore && !sc->sc_dying) {
                sc->sc_bus->needs_explore = 0;
-#if defined(__FreeBSD__) || defined(__DragonFly__)
+
                crit_exit();
-#endif
                sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
-#if defined(__FreeBSD__) || defined(__DragonFly__)
                crit_enter();
-#endif
+
        }
-#if defined(__FreeBSD__) || defined(__DragonFly__)
        crit_exit();
-#endif
 }
 
 void
@@ -826,11 +903,10 @@ usb_schedsoftintr(usbd_bus_handle bus)
 #endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */
        }
 #else
-       bus->methods->soft_intr(bus);
+       bus->methods->soft_intr(bus);
 #endif /* USB_USE_SOFTINTR */
 }
 
-
 #if defined(__NetBSD__) || defined(__OpenBSD__)
 int
 usb_activate(device_ptr_t self, enum devact act)
@@ -853,11 +929,11 @@ usb_activate(device_ptr_t self, enum devact act)
        }
        return (rv);
 }
+#endif
 
-int
-usb_detach(device_ptr_t self, int flags)
+USB_DETACH(usb)
 {
-       struct usb_softc *sc = (struct usb_softc *)self;
+       USB_DETACH_START(usb, sc);
        struct usb_event ue;
 
        DPRINTF(("usb_detach: start\n"));
@@ -877,6 +953,26 @@ usb_detach(device_ptr_t self, int flags)
                DPRINTF(("usb_detach: event thread dead\n"));
        }
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+       destroy_dev(sc->sc_usbdev);
+       if (--usb_ndevs == 0) {
+               int i;
+
+               destroy_dev(usb_dev);
+               usb_dev = NULL;
+
+               for (i = 0; i < USB_NUM_TASKQS; i++) {
+                       struct usb_taskq *taskq = &usb_taskq[i];
+                       wakeup(&taskq->tasks);
+                       if (tsleep(&taskq->taskcreated, 0, "usbtdt",
+                           hz * 60)) {
+                               printf("usb task thread %s didn't die\n",
+                                   taskq->name);
+                       }
+               }
+       }
+#endif
+
        usbd_finish();
 
 #ifdef USB_USE_SOFTINTR
@@ -895,19 +991,38 @@ usb_detach(device_ptr_t self, int flags)
 
        return (0);
 }
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-int
-usb_detach(device_t self)
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+Static void
+usb_child_detached(device_t self, device_t child)
 {
-       DPRINTF(("%s: unload, prevented\n", USBDEVNAME(self)));
+       struct usb_softc *sc = device_get_softc(self);
 
-       return (EINVAL);
+       /* XXX, should check it is the right device. */
+       sc->sc_port.device = NULL;
 }
-#endif
 
+/* Explore USB busses at the end of device configuration. */
+Static void
+usb_cold_explore(void *arg)
+{
+       struct usb_softc *sc;
+
+       KASSERT(cold || TAILQ_EMPTY(&usb_coldexplist),
+           ("usb_cold_explore: busses to explore when !cold"));
+       while (!TAILQ_EMPTY(&usb_coldexplist)) {
+               sc = TAILQ_FIRST(&usb_coldexplist);
+               TAILQ_REMOVE(&usb_coldexplist, sc, sc_coldexplist);
+
+               sc->sc_bus->use_polling++;
+               sc->sc_port.device->hub->explore(sc->sc_bus->root_hub);
+               sc->sc_bus->use_polling--;
+       }
+}
 
-#if defined(__FreeBSD__) || defined(__DragonFly__)
 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);
+SYSINIT(usb_cold_explore, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE,
+    usb_cold_explore, NULL);
 #endif
index d8f9a1a..d505e2a 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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 $
- */
+/*     $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $        */
+/*     $FreeBSD: src/sys/dev/usb/usb.h,v 1.39.2.1 2006/01/20 22:47:49 mux Exp $    */
+/*     $DragonFly: src/sys/bus/usb/usb.h,v 1.5 2006/12/10 02:03:56 sephe Exp $ */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
 #endif
 
 #if defined(_KERNEL)
-#include "usb_port.h"
+#include <bus/usb/usb_port.h>
 #endif /* _KERNEL */
 
-/* these three defines are used by usbd to autoload the usb kld */
+/* These two defines are used by usbd to autoload the usb kld */
 #define USB_KLD                "usb"           /* name of usb module */
 #define USB_UHUB       "usb/uhub"      /* root hub */
 
@@ -492,6 +490,8 @@ typedef struct {
 #define  UIPROTO_IRDA                  0
 
 #define UICLASS_VENDOR         0xff
+#define  UISUBCLASS_XBOX360_CONTROLLER 0x5d
+#define  UIPROTO_XBOX360_GAMEPAD       0x01
 
 
 #define USB_HUB_MAX_DEPTH 5
index d713155..d12de05 100644 (file)
@@ -31,7 +31,7 @@
  *
  *
  * $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.16 2006/05/20 06:32:36 dillon Exp $
+ * $DragonFly: src/sys/bus/usb/usb_ethersubr.c,v 1.17 2006/12/10 02:03:56 sephe Exp $
  */
 
 /*
@@ -107,4 +107,3 @@ usb_ether_input(struct mbuf *m)
        netisr_queue(NETISR_USB, m);
        crit_exit();
 }
-
index 7eceff6..d49e9b2 100644 (file)
@@ -1,9 +1,6 @@
-/*
- * $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 $
- */
+/*     $NetBSD: usb_quirks.c,v 1.50 2004/06/23 02:30:52 mycroft Exp $  */
+/*     $FreeBSD: src/sys/dev/usb/usb_quirks.c,v 1.41.2.4 2006/02/15 22:51:08 iedowse Exp $     */
+/*     $DragonFly: src/sys/bus/usb/usb_quirks.c,v 1.5 2006/12/10 02:03:56 sephe Exp $  */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include "usb.h"
+
+#include <bus/usb/usb.h>
 
 #include "usbdevs.h"
-#include "usb_quirks.h"
+#include <bus/usb/usb_quirks.h>
 
 #ifdef USB_DEBUG
 extern int usbdebug;
@@ -65,10 +62,6 @@ Static const struct usbd_quirk_entry {
  { USB_VENDOR_KYE, USB_PRODUCT_KYE_NICHE,          0x100, { UQ_NO_SET_PROTO}},
  { USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4,
                                                    0x094, { UQ_SWAP_UNICODE}},
- { USB_VENDOR_BTC, USB_PRODUCT_BTC_BTC7932,        0x100, { UQ_NO_STRINGS }},
- { USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT,        0x002, { UQ_NO_STRINGS }},
- { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1, 0x101, { UQ_NO_STRINGS }},
- { USB_VENDOR_WACOM, USB_PRODUCT_WACOM_CT0405U,     0x101, { UQ_NO_STRINGS }},
  { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502,            0x0a2, { UQ_BAD_ADC }},
  { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502,            0x0a2, { UQ_AU_NO_XU }},
  { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70,      0x103, { UQ_BAD_ADC }},
@@ -78,16 +71,13 @@ Static const struct usbd_quirk_entry {
  { USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100,         0x102, { UQ_BUS_POWERED }},
  { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232,          0x102, { UQ_BUS_POWERED }},
  { USB_VENDOR_METRICOM, USB_PRODUCT_METRICOM_RICOCHET_GS,
-       0x100, { UQ_ASSUME_CM_OVER_DATA | UQ_NO_STRINGS }},
+       0x100, { UQ_ASSUME_CM_OVER_DATA }},
  { USB_VENDOR_SANYO, USB_PRODUCT_SANYO_SCP4900,
-       0x000, { UQ_ASSUME_CM_OVER_DATA | UQ_NO_STRINGS }},
+       0x000, { UQ_ASSUME_CM_OVER_DATA }},
  { USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41,          0x110, { UQ_POWER_CLAIM }},
- { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U,
-                                                   0x000, { UQ_NO_STRINGS }},
  { USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1,       0x009, { UQ_AU_NO_FRAC }},
  { USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE,
                                                    0x100, { UQ_AU_INP_ASYNC }},
- { USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND5010,    0x100, { UQ_NO_STRINGS }},
  /* XXX These should have a revision number, but I don't know what they are. */
  { USB_VENDOR_HP, USB_PRODUCT_HP_895C,             ANY,   { UQ_BROKEN_BIDIR }},
  { USB_VENDOR_HP, USB_PRODUCT_HP_880C,             ANY,   { UQ_BROKEN_BIDIR }},
@@ -104,8 +94,21 @@ Static const 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 }},
-
+ { USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_CDMA_MSM,
+       ANY, { UQ_ASSUME_CM_OVER_DATA }},
+ { USB_VENDOR_QUALCOMM2, USB_PRODUCT_QUALCOMM2_CDMA_MSM,
+       ANY, { UQ_ASSUME_CM_OVER_DATA }},
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS64LX,
+       0x100, { UQ_ASSUME_CM_OVER_DATA }},
+ /* Devices which should be ignored by uhid */
+ { USB_VENDOR_APC, USB_PRODUCT_APC_UPSPRO500,
+       ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE,
+       ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1,
+       ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2,
+       ANY, { UQ_HID_IGNORE }},
  { 0, 0, 0, { 0 } }
 };
 
index e213abe..bcf1bd4 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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 $
- */
+/*     $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.17.2.3 2006/02/15 22:51:08 iedowse Exp $     */
+/*     $DragonFly: src/sys/bus/usb/usb_quirks.h,v 1.4 2006/12/10 02:03:56 sephe Exp $  */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -57,6 +55,8 @@ struct usbd_quirks {
 #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 */
+#define UQ_HID_IGNORE  0x8000  /* device should be ignored by hid class */
+                                       
 };
 
 extern const struct usbd_quirks usbd_no_quirk;
index fb43052..8b82162 100644 (file)
@@ -1,12 +1,15 @@
-/*
- * $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.14 2006/10/25 20:55:52 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.76.2.3 2006/03/01 01:59:05 iedowse Exp $       */
+/*     $DragonFly: src/sys/bus/usb/usb_subr.c,v 1.15 2006/12/10 02:03:56 sephe Exp $   */
 
 /* Also already have from NetBSD:
  *     $NetBSD: usb_subr.c,v 1.102 2003/01/01 16:21:50 augustss Exp $
  *     $NetBSD: usb_subr.c,v 1.103 2003/01/10 11:19:13 augustss Exp $
+ *     $NetBSD: usb_subr.c,v 1.111 2004/03/15 10:35:04 augustss Exp $
+ *     $NetBSD: usb_subr.c,v 1.114 2004/06/23 02:30:52 mycroft Exp $
+ *     $NetBSD: usb_subr.c,v 1.115 2004/06/23 05:23:19 mycroft Exp $
+ *     $NetBSD: usb_subr.c,v 1.116 2004/06/23 06:27:54 mycroft Exp $
+ *     $NetBSD: usb_subr.c,v 1.119 2004/10/23 13:26:33 augustss Exp $
  */
 
 /*
@@ -46,6 +49,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "opt_usb.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/bus.h>
 #include <sys/proc.h>
 
-#include "usb.h"
+#include <bus/usb/usb.h>
 
-#include "usbdi.h"
-#include "usbdi_util.h"
-#include "usbdivar.h"
+#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdi_util.h>
+#include <bus/usb/usbdivar.h>
 #include "usbdevs.h"
-#include "usb_quirks.h"
+#include <bus/usb/usb_quirks.h>
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-#include <machine/clock.h>
 #define delay(d)         DELAY(d)
 #endif
 
@@ -78,7 +82,6 @@ extern int usbdebug;
 
 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);
@@ -150,7 +153,7 @@ usbd_errstr(usbd_status err)
 
 usbd_status
 usbd_get_string_desc(usbd_device_handle dev, int sindex, int langid,
-                    usb_string_descriptor_t *sdesc)
+                    usb_string_descriptor_t *sdesc, int *sizep)
 {
        usb_device_request_t req;
        usbd_status err;
@@ -166,54 +169,22 @@ usbd_get_string_desc(usbd_device_handle dev, int sindex, int langid,
        if (err)
                return (err);
 
-       if (actlen < 1)
+       if (actlen < 2)
                return (USBD_SHORT_XFER);
 
        USETW(req.wLength, sdesc->bLength);     /* the whole string */
-       return (usbd_do_request(dev, &req, sdesc));
-}
-
-char *
-usbd_get_string(usbd_device_handle dev, int si, char *buf)
-{
-       int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE;
-       usb_string_descriptor_t us;
-       char *s;
-       int i, n;
-       u_int16_t c;
-       usbd_status err;
-
-       if (si == 0)
-               return (0);
-       if (dev->quirks->uq_flags & UQ_NO_STRINGS)
-               return (0);
-       if (dev->langid == USBD_NOLANG) {
-               /* Set up default language */
-               err = usbd_get_string_desc(dev, USB_LANGUAGE_TABLE, 0, &us);
-               if (err || us.bLength < 4) {
-                       dev->langid = 0; /* Well, just pick something then */
-               } else {
-                       /* Pick the first language as the default. */
-                       dev->langid = UGETW(us.bString[0]);
-               }
-       }
-       err = usbd_get_string_desc(dev, si, dev->langid, &us);
+       err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK,
+               &actlen, USBD_DEFAULT_TIMEOUT);
        if (err)
-               return (0);
-       s = buf;
-       n = us.bLength / 2 - 1;
-       for (i = 0; i < n; i++) {
-               c = UGETW(us.bString[i]);
-               /* Convert from Unicode, handle buggy strings. */
-               if ((c & 0xff00) == 0)
-                       *s++ = c;
-               else if ((c & 0x00ff) == 0 && swap)
-                       *s++ = c >> 8;
-               else
-                       *s++ = '?';
+               return (err);
+
+       if (actlen != sdesc->bLength) {
+               DPRINTFN(-1, ("usbd_get_string_desc: expected %d, got %d\n",
+                   sdesc->bLength, actlen));
        }
-       *s++ = 0;
-       return (buf);
+
+       *sizep = actlen;
+       return (USBD_NORMAL_COMPLETION);
 }
 
 Static void
@@ -247,10 +218,20 @@ usbd_devinfo_vp(usbd_device_handle dev, char *v, char *p, int usedev)
        }
 
        if (usedev) {
-               vendor = usbd_get_string(dev, udd->iManufacturer, v);
+               if (usbd_get_string(dev, udd->iManufacturer, v))
+                       vendor = NULL;
+               else
+                       vendor = v;
                usbd_trim_spaces(vendor);
-               product = usbd_get_string(dev, udd->iProduct, p);
+               if (usbd_get_string(dev, udd->iProduct, p))
+                       product = NULL;
+               else
+                       product = p;
                usbd_trim_spaces(product);
+               if (vendor && !*vendor)
+                       vendor = NULL;
+               if (product && !*product)
+                       product = NULL;
        } else {
                vendor = NULL;
                product = NULL;
@@ -294,15 +275,17 @@ void
 usbd_devinfo(usbd_device_handle dev, int showclass, char *cp)
 {
        usb_device_descriptor_t *udd = &dev->ddesc;
+       usbd_interface_handle iface;
        char vendor[USB_MAX_STRING_LEN];
        char product[USB_MAX_STRING_LEN];
        int bcdDevice, bcdUSB;
+       usb_interface_descriptor_t *id;
 
        usbd_devinfo_vp(dev, vendor, product, 1);
        cp += sprintf(cp, "%s %s", vendor, product);
-       if (showclass)
+       if (showclass & USBD_SHOW_DEVICE_CLASS)
                cp += sprintf(cp, ", class %d/%d",
-                             udd->bDeviceClass, udd->bDeviceSubClass);
+                   udd->bDeviceClass, udd->bDeviceSubClass);
        bcdUSB = UGETW(udd->bcdUSB);
        bcdDevice = UGETW(udd->bcdDevice);
        cp += sprintf(cp, ", rev ");
@@ -310,6 +293,14 @@ usbd_devinfo(usbd_device_handle dev, int showclass, char *cp)
        *cp++ = '/';
        cp += usbd_printBCD(cp, bcdDevice);
        cp += sprintf(cp, ", addr %d", dev->address);
+       if (showclass & USBD_SHOW_INTERFACE_CLASS)
+       {
+               /* fetch the interface handle for the first interface */
+               (void)usbd_device2interface_handle(dev, 0, &iface);
+               id = usbd_get_interface_descriptor(iface);
+               cp += sprintf(cp, ", iclass %d/%d",
+                   id->bInterfaceClass, id->bInterfaceSubClass);
+       }
        *cp = 0;
 }
 
@@ -513,6 +504,7 @@ usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx)
                        }
                }
                ifc->endpoints[endpt].refcnt = 0;
+               ifc->endpoints[endpt].savedtoggle = 0;
                p += ed->bLength;
        }
 #undef ed
@@ -576,15 +568,23 @@ usbd_set_config_index(usbd_device_handle dev, int index, int msg)
        usb_status_t ds;
        usb_config_descriptor_t cd, *cdp;
        usbd_status err;
-       int ifcidx, nifc, len, selfpowered, power;
+       int i, ifcidx, nifc, len, selfpowered, power;
 
        DPRINTFN(5,("usbd_set_config_index: dev=%p index=%d\n", dev, index));
 
-       /* XXX check that all interfaces are idle */
        if (dev->config != USB_UNCONFIG_NO) {
+               nifc = dev->cdesc->bNumInterface;
+
+               /* Check that all interfaces are idle */
+               for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
+                       if (LIST_EMPTY(&dev->ifaces[ifcidx].pipes))
+                               continue;
+                       DPRINTF(("usbd_set_config_index: open pipes exist\n"));
+                       return (USBD_IN_USE);
+               }
+
                DPRINTF(("usbd_set_config_index: free old config\n"));
                /* Free all configuration data structures. */
-               nifc = dev->cdesc->bNumInterface;
                for (ifcidx = 0; ifcidx < nifc; ifcidx++)
                        usbd_free_iface_data(dev, ifcidx);
                kfree(dev->ifaces, M_USB);
@@ -610,8 +610,13 @@ usbd_set_config_index(usbd_device_handle dev, int index, int msg)
                return (err);
        len = UGETW(cd.wTotalLength);
        cdp = kmalloc(len, M_USB, M_INTWAIT);
-       /* Get the full descriptor. */
-       err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp);
+       /* Get the full descriptor.  Try a few times for slow devices. */
+       for (i = 0; i < 3; i++) {
+               err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp);
+               if (!err)
+                       break;
+               usbd_delay_ms(dev, 200);
+       }
        if (err)
                goto bad;
        if (cdp->bDescriptorType != UDESC_CONFIG) {
@@ -755,9 +760,7 @@ usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface,
                kfree(p, M_USB);
                return (err);
        }
-       /* Clear any stall and make sure DATA0 toggle will be used next. */
-       if (UE_GET_ADDR(ep->edesc->bEndpointAddress) != USB_CONTROL_ENDPOINT)
-               usbd_clear_endpoint_stall(p);
+
        *pipe = p;
        return (USBD_NORMAL_COMPLETION);
 }
@@ -793,23 +796,23 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev,
        int found, i, confi, nifaces;
        usbd_status err;
        device_ptr_t dv;
+       device_ptr_t *tmpdv;
        usbd_interface_handle ifaces[256]; /* 256 is the absolute max */
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-       /*
-        * 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;
+       /* XXX FreeBSD may leak resources on failure cases -- fixme */
+       device_t bdev;
+       struct usb_attach_arg *uaap;
+
        bdev = device_add_child(parent, NULL, -1);
        if (!bdev) {
-           printf("%s: Device creation failed\n", USBDEVNAME(dev->bus->bdev));
-           return (USBD_INVAL);
+               device_printf(parent, "Device creation failed\n");
+               return (USBD_INVAL);
        }
-       device_set_ivars(bdev, &uaa);
        device_quiet(bdev);
+       uaap = kmalloc(sizeof(uaa), M_USB, M_WAITOK);
+       device_set_ivars(bdev, uaap);
 #endif
-
        uaa.device = dev;
        uaa.iface = NULL;
        uaa.ifaces = NULL;
@@ -824,14 +827,23 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev,
 
        /* First try with device specific drivers. */
        DPRINTF(("usbd_probe_and_attach: trying device specific drivers\n"));
+
+       dev->ifacenums = NULL;
+       dev->subdevs = kmalloc(2 * sizeof dv, M_USB, M_WAITOK);
+       dev->subdevs[0] = bdev;
+       dev->subdevs[1] = 0;
+       *uaap = uaa;
        dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch);
        if (dv) {
-               dev->subdevs = kmalloc(2 * sizeof dv, M_USB, M_INTWAIT);
-               dev->subdevs[0] = dv;
-               dev->subdevs[1] = 0;
                return (USBD_NORMAL_COMPLETION);
        }
-
+       /*
+        * Free subdevs so we can reallocate it larger for the number of
+        * interfaces 
+        */
+       tmpdv = dev->subdevs;
+       dev->subdevs = NULL;
+       kfree(tmpdv, M_USB);
        DPRINTF(("usbd_probe_and_attach: no device specific driver found\n"));
 
        DPRINTF(("usbd_probe_and_attach: looping over %d configurations\n",
@@ -850,10 +862,6 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev,
                        printf("%s: port %d, set config at addr %d failed\n",
                               USBDEVPTRNAME(parent), port, addr);
 #endif
-#if defined(__FreeBSD__) || defined(__DragonFly__)
-                       device_delete_child(parent, bdev);
-#endif
-
                        return (err);
                }
                nifaces = dev->cdesc->bNumInterface;
@@ -862,7 +870,10 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev,
                        ifaces[i] = &dev->ifaces[i];
                uaa.ifaces = ifaces;
                uaa.nifaces = nifaces;
-               dev->subdevs = kmalloc((nifaces+1) * sizeof dv, M_USB, M_INTWAIT);
+               dev->subdevs = kmalloc((nifaces+1) * sizeof dv, M_USB,
+                                      M_WAITOK);
+               dev->ifacenums = kmalloc((nifaces) * sizeof(*dev->ifacenums),
+                                        M_USB, M_WAITOK);
 
                found = 0;
                for (i = 0; i < nifaces; i++) {
@@ -870,35 +881,45 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev,
                                continue; /* interface already claimed */
                        uaa.iface = ifaces[i];
                        uaa.ifaceno = ifaces[i]->idesc->bInterfaceNumber;
+                       dev->subdevs[found] = bdev;
+                       dev->subdevs[found + 1] = 0;
+                       dev->ifacenums[found] = i;
+                       *uaap = uaa;
                        dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print,
                                           usbd_submatch);
                        if (dv != NULL) {
-                               dev->subdevs[found++] = dv;
-                               dev->subdevs[found] = 0;
                                ifaces[i] = 0; /* consumed */
+                               found++;
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
                                /* create another child for the next iface */
                                bdev = device_add_child(parent, NULL, -1);
                                if (!bdev) {
-                                       printf("%s: Device creation failed\n",
-                                       USBDEVNAME(dev->bus->bdev));
+                                       device_printf(parent,
+                                           "Device add failed\n");
                                        return (USBD_NORMAL_COMPLETION);
                                }
-                               device_set_ivars(bdev, &uaa);
+                               uaap = kmalloc(sizeof(uaa), M_USB, M_WAITOK);
+                               device_set_ivars(bdev, uaap);
                                device_quiet(bdev);
 #endif
+                       } else {
+                               dev->subdevs[found] = 0;
                        }
                }
                if (found != 0) {
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-                       /* remove the last created child again; it is unused */
+                       /* remove the last created child.  It is unused */
                        device_delete_child(parent, bdev);
+                       /* kfree(uaap, M_USB); */ /* May be needed? xxx */
 #endif
                        return (USBD_NORMAL_COMPLETION);
                }
-               kfree(dev->subdevs, M_USB);
-               dev->subdevs = 0;
+               tmpdv = dev->subdevs;
+               dev->subdevs = NULL;
+               kfree(tmpdv, M_USB);
+               kfree(dev->ifacenums, M_USB);
+               dev->ifacenums = NULL;
        }
        /* No interfaces were attached in any of the configurations. */
 
@@ -912,11 +933,12 @@ 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;
+       dev->subdevs = kmalloc(2 * sizeof dv, M_USB, M_WAITOK);
+       dev->subdevs[0] = bdev;
+       dev->subdevs[1] = 0;
+       *uaap = uaa;
        dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch);
        if (dv != NULL) {
-               dev->subdevs = kmalloc(2 * sizeof dv, M_USB, M_INTWAIT);
-               dev->subdevs[0] = dv;
-               dev->subdevs[1] = 0;
                return (USBD_NORMAL_COMPLETION);
        }
 
@@ -926,9 +948,6 @@ usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev,
         * fully operational and not harming anyone.
         */
        DPRINTF(("usbd_probe_and_attach: generic attach failed\n"));
-#if defined(__FreeBSD__) || defined(__DragonFly__)
-       device_delete_child(parent, bdev);
-#endif
        return (USBD_NORMAL_COMPLETION);
 }
 
@@ -943,13 +962,14 @@ usbd_status
 usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth,
                int speed, int port, struct usbd_port *up)
 {
-       usbd_device_handle dev;
+       usbd_device_handle dev, adev;
        struct usbd_device *hub;
        usb_device_descriptor_t *dd;
        usb_port_status_t ps;
        usbd_status err;
        int addr;
        int i;
+       int p;
 
        DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n",
                 bus, port, depth, speed));
@@ -980,11 +1000,27 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth,
        dev->depth = depth;
        dev->powersrc = up;
        dev->myhub = up->parent;
-       for (hub = up->parent;
+
+       up->device = dev;
+
+       /* Locate port on upstream high speed hub */
+       for (adev = dev, hub = up->parent;
             hub != NULL && hub->speed != USB_SPEED_HIGH;
-            hub = hub->myhub)
+            adev = hub, hub = hub->myhub)
                ;
-       dev->myhighhub = hub;
+       if (hub) {
+               for (p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) {
+                       if (hub->hub->ports[p].device == adev) {
+                               dev->myhsport = &hub->hub->ports[p];
+                               goto found;
+                       }
+               }
+               panic("usbd_new_device: cannot find HS port\n");
+       found:
+               DPRINTFN(1,("usbd_new_device: high speed port %d\n", p));
+       } else {
+               dev->myhsport = NULL;
+       }
        dev->speed = speed;
        dev->langid = USBD_NOLANG;
        dev->cookie.cookie = ++usb_cookie_no;
@@ -997,11 +1033,20 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth,
                return (err);
        }
 
-       up->device = dev;
-
        /* Set the address.  Do this early; some devices need that. */
-       err = usbd_set_address(dev, addr);
+       /* Try a few times in case the device is slow (i.e. outside specs.) */
        DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr));
+       for (i = 0; i < 15; i++) {
+               err = usbd_set_address(dev, addr);
+               if (!err)
+                       break;
+               usbd_delay_ms(dev, 200);
+               if ((i & 3) == 3) {
+                       DPRINTFN(-1,("usb_new_device: set address %d "
+                           "failed - trying a port reset\n", addr));
+                       usbd_reset_port(up->parent, port, &ps);
+               }
+       }
        if (err) {
                DPRINTFN(-1,("usb_new_device: set address %d failed\n", addr));
                err = USBD_SET_ADDR_FAILED;
@@ -1010,20 +1055,12 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth,
        }
        /* Allow device time to set new address */
        usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE);
-       dev->address = addr;    /* New device address now */
+       dev->address = addr;    /* New device address now */
        bus->devices[addr] = dev;
 
        dd = &dev->ddesc;
-       /* Try a few times in case the device is slow (i.e. outside specs.) */
-       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);
-       }
+       /* Get the first 8 bytes of the device descriptor. */
+       err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd);
        if (err) {
                DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc "
                              "failed\n", addr));
@@ -1093,7 +1130,7 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth,
 usbd_status
 usbd_reload_device_desc(usbd_device_handle dev)
 {
-       usbd_status err = 0;
+       usbd_status err;
        int i;
 
        /* Get the full device descriptor. */
@@ -1119,8 +1156,8 @@ usbd_remove_device(usbd_device_handle dev, struct usbd_port *up)
 
        if (dev->default_pipe != NULL)
                usbd_kill_pipe(dev->default_pipe);
-       up->device = 0;
-       dev->bus->devices[dev->address] = 0;
+       up->device = NULL;
+       dev->bus->devices[dev->address] = NULL;
 
        kfree(dev, M_USB);
 }
@@ -1243,11 +1280,13 @@ usbd_fill_deviceinfo(usbd_device_handle dev, struct usb_device_info *di,
        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';
+               for (i = 0; dev->subdevs[i] && i < USB_MAX_DEVNAMES; i++) {
+                       if (device_is_attached(dev->subdevs[i]))
+                               strlcpy(di->udi_devnames[i],
+                                   USBDEVPTRNAME(dev->subdevs[i]),
+                                   USB_MAX_DEVNAMELEN);
+                       else
+                               di->udi_devnames[i][0] = 0;
                 }
         } else {
                 i = 0;
@@ -1298,6 +1337,8 @@ usb_free_device(usbd_device_handle dev)
                kfree(dev->cdesc, M_USB);
        if (dev->subdevs != NULL)
                kfree(dev->subdevs, M_USB);
+       if (dev->ifacenums != NULL)
+               kfree(dev->ifacenums, M_USB);
        kfree(dev, M_USB);
 }
 
@@ -1360,11 +1401,11 @@ usb_realloc(void *p, u_int size, int pool, int flags)
 {
        void *q;
 
-       q = kmalloc(size, pool, flags);
+       q = malloc(size, pool, flags);
        if (q == NULL)
                return (NULL);
        bcopy(p, q, size);
-       kfree(p, pool);
+       free(p, pool);
        return (q);
 }
 #endif
index 6463c05..fba603d 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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.12 2006/12/06 20:14:47 tgen Exp $
- */
+/*     $NetBSD: usbdi.c,v 1.106 2004/10/24 12:52:40 augustss Exp $     */
+/*     $FreeBSD: src/sys/dev/usb/usbdi.c,v 1.91.2.1 2005/12/15 00:36:00 iedowse Exp $  */
+/*     $DragonFly: src/sys/bus/usb/usbdi.c,v 1.13 2006/12/10 02:03:57 sephe Exp $      */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/kernel.h>
+#include <sys/device.h>
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
 #include <sys/module.h>
 #include <sys/bus.h>
 #include "usb_if.h"
 #if defined(DIAGNOSTIC) && defined(__i386__)
 #include <machine/cpu.h>
 #endif
+#endif
 #include <sys/malloc.h>
 #include <sys/proc.h>
+#ifdef __DragonFly__
 #include <sys/thread2.h>
+#endif
 
-#include "usb.h"
-#include "usbdi.h"
-#include "usbdi_util.h"
-#include "usbdivar.h"
-#include "usb_mem.h"
+#include <bus/usb/usb.h>
+#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdi_util.h>
+#include <bus/usb/usbdivar.h>
+#include <bus/usb/usb_mem.h>
+#include <bus/usb/usb_quirks.h>
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
-#include "usb_if.h"
-#include <machine/clock.h>
 #define delay(d)       DELAY(d)
 #endif
 
@@ -237,7 +241,7 @@ usbd_open_pipe_intr(usbd_interface_handle iface, u_int8_t address,
        ipipe->repeat = 1;
        err = usbd_transfer(xfer);
        *pipe = ipipe;
-       if (err != USBD_IN_PROGRESS)
+       if (err != USBD_IN_PROGRESS && err)
                goto bad2;
        return (USBD_NORMAL_COMPLETION);
 
@@ -333,7 +337,7 @@ usbd_transfer(usbd_xfer_handle xfer)
        crit_enter();
        if (!xfer->done) {
                if (pipe->device->bus->use_polling)
-                       panic("usbd_transfer: not done\n");
+                       panic("usbd_transfer: not done");
                tsleep(xfer, 0, "usbsyn", 0);
        }
        crit_exit();
@@ -550,6 +554,12 @@ usbd_abort_pipe(usbd_pipe_handle pipe)
        return (err);
 }
 
+usbd_status
+usbd_abort_default_pipe(usbd_device_handle dev)
+{
+       return (usbd_abort_pipe(dev->default_pipe));
+}
+
 usbd_status
 usbd_clear_endpoint_stall(usbd_pipe_handle pipe)
 {
@@ -726,7 +736,7 @@ usbd_get_interface(usbd_interface_handle iface, u_int8_t *aiface)
 
 /*** Internal routines ***/
 
-/* Dequeue all pipe operations, called from critical section. */
+/* Dequeue all pipe operations, called from critical section. */
 Static usbd_status
 usbd_ar_pipe(usbd_pipe_handle pipe)
 {
@@ -750,12 +760,15 @@ usbd_ar_pipe(usbd_pipe_handle pipe)
        return (USBD_NORMAL_COMPLETION);
 }
 
-/* Called from a critical section. */
+/* Called from critical section */
 void
 usb_transfer_complete(usbd_xfer_handle xfer)
 {
        usbd_pipe_handle pipe = xfer->pipe;
        usb_dma_t *dmap = &xfer->dmabuf;
+       int sync = xfer->flags & USBD_SYNCHRONOUS;
+       int erred = xfer->status == USBD_CANCELLED ||
+           xfer->status == USBD_TIMEOUT;
        int repeat = pipe->repeat;
        int polling;
 
@@ -826,26 +839,28 @@ usb_transfer_complete(usbd_xfer_handle xfer)
                xfer->status = USBD_SHORT_XFER;
        }
 
-       if (xfer->callback)
-               xfer->callback(xfer, xfer->priv, xfer->status);
-
-#ifdef DIAGNOSTIC
-       if (pipe->methods->done != NULL)
+       /*
+        * For repeat operations, call the callback first, as the xfer
+        * will not go away and the "done" method may modify it. Otherwise
+        * reverse the order in case the callback wants to free or reuse
+        * the xfer.
+        */
+       if (repeat) {
+               if (xfer->callback)
+                       xfer->callback(xfer, xfer->priv, xfer->status);
                pipe->methods->done(xfer);
-       else
-               printf("usb_transfer_complete: pipe->methods->done == NULL\n");
-#else
-       pipe->methods->done(xfer);
-#endif
+       } else {
+               pipe->methods->done(xfer);
+               if (xfer->callback)
+                       xfer->callback(xfer, xfer->priv, xfer->status);
+       }
 
-       if ((xfer->flags & USBD_SYNCHRONOUS) && !polling)
+       if (sync && !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 (erred && pipe->iface != NULL)       /* not control pipe */
                        pipe->running = 0;
                else
                        usbd_start_next(pipe);
@@ -880,7 +895,7 @@ usb_insert_transfer(usbd_xfer_handle xfer)
        return (err);
 }
 
-/* Called from a critical section. */
+/* Called from critical section */
 void
 usbd_start_next(usbd_pipe_handle pipe)
 {
@@ -913,7 +928,6 @@ usbd_start_next(usbd_pipe_handle pipe)
        }
 }
 
-
 usbd_status
 usbd_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data)
 {
@@ -939,8 +953,13 @@ usbd_do_request_flags_pipe(usbd_device_handle dev, usbd_pipe_handle pipe,
 
 #ifdef DIAGNOSTIC
 #if defined(__i386__) && (defined(__FreeBSD__) || defined(__DragonFly__))
-       KASSERT(mycpu->gd_intr_nesting_level == 0,
+#if defined(__FreeBSD__)
+       KASSERT(curthread->td_intr_nesting_level == 0,
                ("usbd_do_request: in interrupt context"));
+#elif defined(__DragonFly__)
+       KASSERT(mycpu->gd_intr_nesting_level == 0,
+               ("usbd_do_request: in interrupt context"));
+#endif
 #endif
        if (dev->bus->intr_context) {
                printf("usbd_do_request: not in process context\n");
@@ -1045,7 +1064,7 @@ usbd_do_request_async(usbd_device_handle dev, usb_device_request_t *req,
        usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT, req,
            data, UGETW(req->wLength), 0, usbd_do_request_async_cb);
        err = usbd_transfer(xfer);
-       if (err != USBD_IN_PROGRESS) {
+       if (err != USBD_IN_PROGRESS && err) {
                usbd_free_xfer(xfer);
                return (err);
        }
@@ -1111,11 +1130,9 @@ usbd_get_endpoint_descriptor(usbd_interface_handle iface, u_int8_t address)
 int
 usbd_ratecheck(struct timeval *last)
 {
-#if 0
-       static struct timeval errinterval = { 0, 250000 }; /* 0.25 s*/
-
-       return (ratecheck(last, &errinterval));
-#endif
+       if (last->tv_sec == time_second)
+               return (0);
+       last->tv_sec = time_second;
        return (1);
 }
 
@@ -1137,6 +1154,86 @@ usb_match_device(const struct usb_devno *tbl, u_int nentries, u_int sz,
        return (NULL);
 }
 
+
+void
+usb_desc_iter_init(usbd_device_handle dev, usbd_desc_iter_t *iter)
+{
+       const usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
+
+        iter->cur = (const uByte *)cd;
+        iter->end = (const uByte *)cd + UGETW(cd->wTotalLength);
+}
+
+const usb_descriptor_t *
+usb_desc_iter_next(usbd_desc_iter_t *iter)
+{
+       const usb_descriptor_t *desc;
+
+       if (iter->cur + sizeof(usb_descriptor_t) >= iter->end) {
+               if (iter->cur != iter->end)
+                       printf("usb_desc_iter_next: bad descriptor\n");
+               return NULL;
+       }
+       desc = (const usb_descriptor_t *)iter->cur;
+       if (desc->bLength == 0) {
+               printf("usb_desc_iter_next: descriptor length = 0\n");
+               return NULL;
+       }
+       iter->cur += desc->bLength;
+       if (iter->cur > iter->end) {
+               printf("usb_desc_iter_next: descriptor length too large\n");
+               return NULL;
+       }
+       return desc;
+}
+
+usbd_status
+usbd_get_string(usbd_device_handle dev, int si, char *buf)
+{
+       int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE;
+       usb_string_descriptor_t us;
+       char *s;
+       int i, n;
+       u_int16_t c;
+       usbd_status err;
+       int size;
+
+       buf[0] = '\0';
+       if (si == 0)
+               return (USBD_INVAL);
+       if (dev->quirks->uq_flags & UQ_NO_STRINGS)
+               return (USBD_STALLED);
+       if (dev->langid == USBD_NOLANG) {
+               /* Set up default language */
+               err = usbd_get_string_desc(dev, USB_LANGUAGE_TABLE, 0, &us,
+                   &size);
+               if (err || size < 4) {
+                       DPRINTFN(-1,("usbd_get_string: getting lang failed, using 0\n"));
+                       dev->langid = 0; /* Well, just pick something then */
+               } else {
+                       /* Pick the first language as the default. */
+                       dev->langid = UGETW(us.bString[0]);
+               }
+       }
+       err = usbd_get_string_desc(dev, si, dev->langid, &us, &size);
+       if (err)
+               return (err);
+       s = buf;
+       n = size / 2 - 1;
+       for (i = 0; i < n; i++) {
+               c = UGETW(us.bString[i]);
+               /* Convert from Unicode, handle buggy strings. */
+               if ((c & 0xff00) == 0)
+                       *s++ = c;
+               else if ((c & 0x00ff) == 0 && swap)
+                       *s++ = c >> 8;
+               else
+                       *s++ = '?';
+       }
+       *s++ = 0;
+       return (USBD_NORMAL_COMPLETION);
+}
+
 #if defined(__FreeBSD__) || defined(__DragonFly__)
 int
 usbd_driver_load(module_t mod, int what, void *arg)
index 366ddbd..07f3b98 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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.6 2006/12/06 20:14:47 tgen Exp $
- */
+/*     $NetBSD: usbdi.h,v 1.64 2004/10/23 13:26:34 augustss Exp $      */
+/*     $FreeBSD: src/sys/dev/usb/usbdi.h,v 1.59 2005/05/16 06:58:43 imp Exp $  */
+/*     $DragonFly: src/sys/bus/usb/usbdi.h,v 1.7 2006/12/10 02:03:57 sephe Exp $       */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -41,6 +39,9 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef _USBDI_H_
+#define _USBDI_H_
+
 typedef struct usbd_bus                *usbd_bus_handle;
 typedef struct usbd_device     *usbd_device_handle;
 typedef struct usbd_interface  *usbd_interface_handle;
@@ -91,83 +92,79 @@ typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle,
 #define USBD_NO_TIMEOUT 0
 #define USBD_DEFAULT_TIMEOUT 5000 /* ms = 5 s */
 
-#if defined(__FreeBSD__) || defined(__DragonFly__)
+#ifdef __DragonFly__
 #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_status usbd_open_pipe(usbd_interface_handle, u_int8_t,
+                          u_int8_t, usbd_pipe_handle *);
+usbd_status usbd_close_pipe(usbd_pipe_handle);
+usbd_status usbd_transfer(usbd_xfer_handle);
 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_status usbd_free_xfer(usbd_xfer_handle);
+void usbd_setup_xfer(usbd_xfer_handle, usbd_pipe_handle,
+                    usbd_private_handle, void *,
+                    u_int32_t, u_int16_t, u_int32_t,
                     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);
+void usbd_setup_default_xfer(usbd_xfer_handle, usbd_device_handle,
+                            usbd_private_handle, u_int32_t,
+                            usb_device_request_t *, void *,
+                            u_int32_t, u_int16_t, usbd_callback);
+void usbd_setup_isoc_xfer(usbd_xfer_handle, usbd_pipe_handle,
+                         usbd_private_handle, u_int16_t *,
+                         u_int32_t, u_int16_t, usbd_callback);
+void usbd_get_xfer_status(usbd_xfer_handle, usbd_private_handle *,
+                         void **, u_int32_t *, usbd_status *);
 usb_endpoint_descriptor_t *usbd_interface2endpoint_descriptor
-                       (usbd_interface_handle iface, u_int8_t address);
-usbd_status usbd_abort_pipe(usbd_pipe_handle pipe);
-usbd_status usbd_clear_endpoint_stall(usbd_pipe_handle pipe);
-usbd_status usbd_clear_endpoint_stall_async(usbd_pipe_handle pipe);
-void usbd_clear_endpoint_toggle(usbd_pipe_handle pipe);
-usbd_status usbd_endpoint_count(usbd_interface_handle dev, u_int8_t *count);
-usbd_status usbd_interface_count(usbd_device_handle dev, u_int8_t *count);
-void usbd_interface2device_handle(usbd_interface_handle iface,
-                                        usbd_device_handle *dev);
-usbd_status usbd_device2interface_handle(usbd_device_handle dev,
-                             u_int8_t ifaceno, usbd_interface_handle *iface);
+                       (usbd_interface_handle, u_int8_t);
+usbd_status usbd_abort_pipe(usbd_pipe_handle);
+usbd_status usbd_abort_default_pipe(usbd_device_handle);
+usbd_status usbd_clear_endpoint_stall(usbd_pipe_handle);
+usbd_status usbd_clear_endpoint_stall_async(usbd_pipe_handle);
+void usbd_clear_endpoint_toggle(usbd_pipe_handle);
+usbd_status usbd_endpoint_count(usbd_interface_handle, u_int8_t *);
+usbd_status usbd_interface_count(usbd_device_handle, u_int8_t *);
+void usbd_interface2device_handle(usbd_interface_handle,
+                                        usbd_device_handle *);
+usbd_status usbd_device2interface_handle(usbd_device_handle,
+                             u_int8_t, usbd_interface_handle *);
 
 usbd_device_handle usbd_pipe2device_handle(usbd_pipe_handle);
-void *usbd_alloc_buffer(usbd_xfer_handle xfer, u_int32_t size);
-void usbd_free_buffer(usbd_xfer_handle xfer);
-void *usbd_get_buffer(usbd_xfer_handle xfer);
-usbd_status usbd_sync_transfer(usbd_xfer_handle req);
-usbd_status usbd_open_pipe_intr(usbd_interface_handle iface, u_int8_t address,
-                               u_int8_t flags, usbd_pipe_handle *pipe,
-                               usbd_private_handle priv, void *buffer,
-                               u_int32_t length, usbd_callback, int);
-usbd_status usbd_do_request(usbd_device_handle pipe, usb_device_request_t *req,
-                           void *data);
-usbd_status usbd_do_request_async(usbd_device_handle pipe,
-                                 usb_device_request_t *req, void *data);
-usbd_status usbd_do_request_flags(usbd_device_handle pipe,
-                                 usb_device_request_t *req,
-                                 void *data, u_int16_t flags, int*, u_int32_t);
-usbd_status usbd_do_request_flags_pipe(
-       usbd_device_handle dev, usbd_pipe_handle pipe,
-       usb_device_request_t *req, void *data, u_int16_t flags, int *actlen,
-       u_int32_t);
-int usbd_get_speed(usbd_device_handle dev);
+void *usbd_alloc_buffer(usbd_xfer_handle, u_int32_t);
+void usbd_free_buffer(usbd_xfer_handle);
+void *usbd_get_buffer(usbd_xfer_handle);
+usbd_status usbd_sync_transfer(usbd_xfer_handle);
+usbd_status usbd_open_pipe_intr(usbd_interface_handle, u_int8_t,
+                               u_int8_t, usbd_pipe_handle *,
+                               usbd_private_handle, void *,
+                               u_int32_t, usbd_callback, int);
+usbd_status usbd_do_request(usbd_device_handle, usb_device_request_t *, void *);
+usbd_status usbd_do_request_async(usbd_device_handle,
+                                 usb_device_request_t *, void *);
+usbd_status usbd_do_request_flags(usbd_device_handle, usb_device_request_t *,
+                                 void *, u_int16_t, int*, u_int32_t);
+usbd_status usbd_do_request_flags_pipe(usbd_device_handle, usbd_pipe_handle,
+       usb_device_request_t *, void *, u_int16_t, int *, 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);
+usb_config_descriptor_t *usbd_get_config_descriptor(usbd_device_handle);
+usb_device_descriptor_t *usbd_get_device_descriptor(usbd_device_handle);
+int usbd_get_speed(usbd_device_handle);
 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);
+usbd_status  usbd_get_interface(usbd_interface_handle, u_int8_t *);
 void usbd_fill_deviceinfo(usbd_device_handle, struct usb_device_info *, int);
-int usbd_get_interface_altindex(usbd_interface_handle iface);
+int usbd_get_interface_altindex(usbd_interface_handle);
 
-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 *,
+                                           int, int);
+usb_endpoint_descriptor_t *usbd_find_edesc(usb_config_descriptor_t *,
+                                          int, int, int);
 
 void usbd_dopoll(usbd_interface_handle);
-void usbd_set_polling(usbd_device_handle dev, int on);
+void usbd_set_polling(usbd_device_handle, int);
 
-const char *usbd_errstr(usbd_status err);
+const char *usbd_errstr(usbd_status);
 
 void usbd_add_dev_event(int, usbd_device_handle);
 void usbd_add_drv_event(int, usbd_device_handle, device_ptr_t);
@@ -175,12 +172,22 @@ void usbd_add_drv_event(int, usbd_device_handle, device_ptr_t);
 void usbd_devinfo(usbd_device_handle, int, char *);
 const struct usbd_quirks *usbd_get_quirks(usbd_device_handle);
 usb_endpoint_descriptor_t *usbd_get_endpoint_descriptor
-                       (usbd_interface_handle iface, u_int8_t address);
+                       (usbd_interface_handle, u_int8_t);
 
 usbd_status usbd_reload_device_desc(usbd_device_handle);
 
 int usbd_ratecheck(struct timeval *last);
 
+usbd_status usbd_get_string(usbd_device_handle dev, int si, char *buf);
+
+/* An iterator for descriptors. */
+typedef struct {
+       const uByte *cur;
+       const uByte *end;
+} usbd_desc_iter_t;
+void usb_desc_iter_init(usbd_device_handle dev, usbd_desc_iter_t *iter);
+const usb_descriptor_t *usb_desc_iter_next(usbd_desc_iter_t *iter);
+
 /*
  * 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
@@ -191,19 +198,24 @@ struct usb_task {
        TAILQ_ENTRY(usb_task) next;
        void (*fun)(void *);
        void *arg;
-       char onqueue;
+       int queue;
 };
+#define USB_TASKQ_HC           0
+#define USB_TASKQ_DRIVER       1
+#define USB_NUM_TASKQS         2
+#define USB_TASKQ_NAMES                {"usbtask-hc", "usbtask-dr"}
 
-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)
+void usb_do_task(usbd_device_handle, struct usb_task *, int, int);
+void usb_add_task(usbd_device_handle, struct usb_task *, int);
+void usb_rem_task(usbd_device_handle, struct usb_task *);
+#define usb_init_task(t, f, a) ((t)->fun = (f), (t)->arg = (a), (t)->queue = -1)
 
 struct usb_devno {
        u_int16_t ud_vendor;
        u_int16_t ud_product;
 };
-const struct usb_devno *usb_match_device(const struct usb_devno *tbl,
-       u_int nentries, u_int sz, u_int16_t vendor, u_int16_t product);
+const struct usb_devno *usb_match_device(const struct usb_devno *,
+       u_int, u_int, u_int16_t, u_int16_t);
 #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
@@ -268,7 +280,28 @@ struct usb_attach_arg {
 
 #endif
 
+#define USBD_SHOW_DEVICE_CLASS         0x1
+#define USBD_SHOW_INTERFACE_CLASS      0x2
+
 #if defined(__FreeBSD__) || defined(__DragonFly__)
 int usbd_driver_load(module_t mod, int what, void *arg);
+
+static __inline int
+usb_get_port(device_t dev)
+{
+       struct usb_attach_arg *uap = device_get_ivars(dev);
+       return (uap->port);
+}
+
+static __inline struct usbd_interface *
+usb_get_iface(device_t dev)
+{
+       struct usb_attach_arg *uap = device_get_ivars(dev);
+       return (uap->iface);
+}
+
 #endif
 
+#define IPL_USB IPL_BIO
+
+#endif /* _USBDI_H_ */
index 5d52780..5a714ad 100644 (file)
@@ -1,9 +1,6 @@
-/*
- * $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.9 2006/09/05 00:55:36 dillon Exp $
- */
+/*     $NetBSD: usbdi_util.c,v 1.42 2004/12/03 08:53:40 augustss Exp $ */
+/*     $FreeBSD: src/sys/dev/usb/usbdi_util.c,v 1.34 2005/03/01 08:01:22 sobomax Exp $ */
+/*     $DragonFly: src/sys/bus/usb/usbdi_util.c,v 1.10 2006/12/10 02:03:57 sephe Exp $ */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -45,6 +42,7 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/module.h>
 #include <sys/malloc.h>
 #if defined(__NetBSD__) || defined(__OpenBSD__)
 #include <sys/proc.h>
 #elif defined(__FreeBSD__) || defined(__DragonFly__)
 #include <sys/bus.h>
 #endif
+#ifdef __DragonFly__
 #include <sys/thread2.h>
+#endif
 
-#include "usb.h"
-#include "usbhid.h"
+#include <bus/usb/usb.h>
+#include <bus/usb/usbhid.h>
 
-#include "usbdi.h"
-#include "usbdi_util.h"
+#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdi_util.h>
 
 #ifdef USB_DEBUG
 #define DPRINTF(x)     if (usbdebug) logprintf x
@@ -224,6 +224,25 @@ usbd_set_port_feature(usbd_device_handle dev, int port, int sel)
        return (usbd_do_request(dev, &req, 0));
 }
 
+usbd_status
+usbd_get_protocol(usbd_interface_handle iface, u_int8_t *report)
+{
+       usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
+       usbd_device_handle dev;
+       usb_device_request_t req;
+
+       DPRINTFN(4, ("usbd_get_protocol: iface=%p, endpt=%d\n",
+                    iface, id->bInterfaceNumber));
+       if (id == NULL)
+               return (USBD_IOERROR);
+       usbd_interface2device_handle(iface, &dev);
+       req.bmRequestType = UT_READ_CLASS_INTERFACE;
+       req.bRequest = UR_GET_PROTOCOL;
+       USETW(req.wValue, 0);
+       USETW(req.wIndex, id->bInterfaceNumber);
+       USETW(req.wLength, 1);
+       return (usbd_do_request(dev, &req, report));
+}
 
 usbd_status
 usbd_set_protocol(usbd_interface_handle iface, int report)
@@ -293,8 +312,8 @@ usbd_get_report(usbd_interface_handle iface, int type, int id, void *data,
        usbd_device_handle dev;
        usb_device_request_t req;
 
-       DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
-       if (id == 0)
+       DPRINTFN(4, ("usbd_get_report: len=%d\n", len));
+       if (ifd == NULL)
                return (USBD_IOERROR);
        usbd_interface2device_handle(iface, &dev);
        req.bmRequestType = UT_READ_CLASS_INTERFACE;
@@ -348,7 +367,7 @@ usbd_get_hid_descriptor(usbd_interface_handle ifc)
        char *p, *end;
 
        if (idesc == NULL)
-               return (0);
+               return (NULL);
        usbd_interface2device_handle(ifc, &dev);
        cdesc = usbd_get_config_descriptor(dev);
 
@@ -362,7 +381,7 @@ usbd_get_hid_descriptor(usbd_interface_handle ifc)
                if (hd->bDescriptorType == UDESC_INTERFACE)
                        break;
        }
-       return (0);
+       return (NULL);
 }
 
 usbd_status
@@ -468,7 +487,7 @@ usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
        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));
-       crit_enter();           /* don't want callback until tsleep() */
+       crit_enter();           /* don't want callback until tsleep() */
        err = usbd_transfer(xfer);
        if (err != USBD_IN_PROGRESS) {
                crit_exit();
@@ -506,3 +525,20 @@ usb_detach_wakeup(device_ptr_t dv)
        DPRINTF(("usb_detach_wakeup: for %s\n", USBDEVPTRNAME(dv)));
        wakeup(dv);
 }
+
+const usb_descriptor_t *
+usb_find_desc(usbd_device_handle dev, int type, int subtype)
+{
+       usbd_desc_iter_t iter;
+       const usb_descriptor_t *desc;
+
+       usb_desc_iter_init(dev, &iter);
+       for (;;) {
+               desc = usb_desc_iter_next(&iter);
+               if (!desc || (desc->bDescriptorType == type &&
+                             (subtype == USBD_SUBTYPE_ANY ||
+                              subtype == desc->bDescriptorSubtype)))
+                       break;
+       }
+       return desc;
+}
index d08b62c..8ee07ec 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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 $
- */
+/*     $NetBSD: usbdi_util.h,v 1.31 2004/12/03 08:53:40 augustss Exp $ */
+/*     $FreeBSD: src/sys/dev/usb/usbdi_util.h,v 1.19 2005/03/01 08:01:22 sobomax Exp $ */
+/*     $DragonFly: src/sys/bus/usb/usbdi_util.h,v 1.4 2006/12/10 02:03:57 sephe Exp $  */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -57,6 +55,7 @@ usbd_status   usbd_set_port_feature(usbd_device_handle dev, int, int);
 usbd_status    usbd_clear_port_feature(usbd_device_handle, int, int);
 usbd_status    usbd_get_device_status(usbd_device_handle, usb_status_t *);
 usbd_status    usbd_get_hub_status(usbd_device_handle, usb_hub_status_t *);
+usbd_status    usbd_get_protocol(usbd_interface_handle dev, u_int8_t *report);
 usbd_status    usbd_set_protocol(usbd_interface_handle dev, int report);
 usbd_status    usbd_get_report_descriptor(usbd_device_handle dev, int ifcno,
                                           int size, void *d);
@@ -72,7 +71,8 @@ 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);
+                                    int langid, usb_string_descriptor_t *sdesc,
+                                    int *sizep);
 void           usbd_delay_ms(usbd_device_handle, u_int);
 
 
@@ -90,3 +90,7 @@ usbd_status usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
 void usb_detach_wait(device_ptr_t);
 void usb_detach_wakeup(device_ptr_t);
 
+const usb_descriptor_t *usb_find_desc(usbd_device_handle dev, int type,
+                                     int subtype);
+#define USBD_SUBTYPE_ANY (~0)
+
index 82cbfdd..41d5afe 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * $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.5 2005/06/02 20:40:40 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.43.2.1 2006/03/01 01:59:05 iedowse Exp $       */
+/*     $DragonFly: src/sys/bus/usb/usbdivar.h,v 1.6 2006/12/10 02:03:57 sephe Exp $    */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -54,6 +52,7 @@ struct usbd_pipe;
 struct usbd_endpoint {
        usb_endpoint_descriptor_t *edesc;
        int                     refcnt;
+       int                     savedtoggle;
 };
 
 struct usbd_bus_methods {
@@ -76,6 +75,10 @@ struct usbd_pipe_methods {
        void                  (*done)(usbd_xfer_handle xfer);
 };
 
+struct usbd_tt {
+       struct usbd_hub         *hub;
+};
+
 struct usbd_port {
        usb_port_status_t       status;
        u_int16_t               power;  /* mA of current on port */
@@ -84,6 +87,7 @@ struct usbd_port {
 #define USBD_RESTART_MAX 5
        struct usbd_device     *device; /* Connected device */
        struct usbd_device     *parent; /* The ports hub */
+       struct usbd_tt         *tt; /* Transaction translator (if any) */
 };
 
 struct usbd_hub {
@@ -144,7 +148,7 @@ struct usbd_device {
        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_port       *myhsport;      /* closest high speed port */
        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 */
@@ -153,6 +157,7 @@ struct usbd_device {
        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 */
+       uint8_t                *ifacenums;     /* sub-device interfacenumbers */
 };
 
 struct usbd_interface {
index bef167a..f2a72ee 100644 (file)
@@ -2,7 +2,7 @@
  * $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.21 2006/09/10 01:26:37 dillon Exp $
+ * $DragonFly: src/sys/dev/usbmisc/ugen/ugen.c,v 1.22 2006/12/10 02:03:57 sephe Exp $
  */
 
 /* 
@@ -1350,12 +1350,16 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd,
                return (error);
        }
        case USB_GET_STRING_DESC:
+       {
+               int size;
+
                si = (struct usb_string_desc *)addr;
                err = usbd_get_string_desc(sc->sc_udev, si->usd_string_index,
-                         si->usd_language_id, &si->usd_desc);
+                         si->usd_language_id, &si->usd_desc, &size);
                if (err)
                        return (EINVAL);
                break;
+       }
        case USB_DO_REQUEST:
        {
                struct usb_ctl_request *ur = (void *)addr;