summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorjmcneill <jmcneill@NetBSD.org>2008-08-02 22:23:18 +0000
committerjmcneill <jmcneill@NetBSD.org>2008-08-02 22:23:18 +0000
commit4e6a45810796b21f37e6da23da4c483713e5416d (patch)
tree7a85430b6ee4b9ffe6216e76956490b6187b5296 /sys/dev
parentbd85f4f93060d41e406092d8a76cee5ae1f87d65 (diff)
High speed isochronous support, from Jeremy Morse as part of Google
Summer of Code 2008.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/ehci.c851
-rw-r--r--sys/dev/usb/ehcireg.h37
-rw-r--r--sys/dev/usb/ehcivar.h34
3 files changed, 867 insertions, 55 deletions
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c
index 88e51bfab5c..86d88de3e32 100644
--- a/sys/dev/usb/ehci.c
+++ b/sys/dev/usb/ehci.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ehci.c,v 1.138 2008/06/28 17:42:53 bouyer Exp $ */
+/* $NetBSD: ehci.c,v 1.139 2008/08/02 22:23:18 jmcneill Exp $ */
/*
* Copyright (c) 2004,2005 The NetBSD Foundation, Inc.
@@ -54,7 +54,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.138 2008/06/28 17:42:53 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.139 2008/08/02 22:23:18 jmcneill Exp $");
#include "ohci.h"
#include "uhci.h"
@@ -118,7 +118,10 @@ struct ehci_pipe {
u_int length;
} bulk;
/* Iso pipe */
- /* XXX */
+ struct {
+ u_int next_frame;
+ u_int cur_xfers;
+ } isoc;
} u;
};
@@ -128,6 +131,8 @@ Static void ehci_softintr(void *);
Static int ehci_intr1(ehci_softc_t *);
Static void ehci_waitintr(ehci_softc_t *, usbd_xfer_handle);
Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *);
+Static void ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *);
+Static void ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *);
Static void ehci_idone(struct ehci_xfer *);
Static void ehci_timeout(void *);
Static void ehci_timeout_task(void *);
@@ -192,6 +197,13 @@ Static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *,
Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *,
ehci_soft_qtd_t *);
+Static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *sc);
+Static void ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd);
+Static void ehci_rem_free_itd_chain(ehci_softc_t *sc,
+ struct ehci_xfer *exfer);
+Static void ehci_abort_isoc_xfer(usbd_xfer_handle xfer,
+ usbd_status status);
+
Static usbd_status ehci_device_request(usbd_xfer_handle xfer);
Static usbd_status ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *,
@@ -215,9 +227,13 @@ Static void ehci_dump_sqtds(ehci_soft_qtd_t *);
Static void ehci_dump_sqtd(ehci_soft_qtd_t *);
Static void ehci_dump_qtd(ehci_qtd_t *);
Static void ehci_dump_sqh(ehci_soft_qh_t *);
-#ifdef DIAGNOSTIC
+#if notyet
+Static void ehci_dump_sitd(struct ehci_soft_itd *itd);
+Static void ehci_dump_itd(struct ehci_soft_itd *);
Static void ehci_dump_exfer(struct ehci_xfer *);
#endif
+#ifdef DIAGNOSTIC
+#endif
#endif
#define EHCI_NULL htole32(EHCI_LINK_TERMINATE)
@@ -398,8 +414,19 @@ ehci_init(ehci_softc_t *sc)
return (err);
DPRINTF(("%s: flsize=%d\n", device_xname(sc->sc_dev),sc->sc_flsize));
sc->sc_flist = KERNADDR(&sc->sc_fldma, 0);
+
+ for (i = 0; i < sc->sc_flsize; i++) {
+ sc->sc_flist[i] = EHCI_NULL;
+ }
+
EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0));
+ sc->sc_softitds = malloc(sc->sc_flsize * sizeof(ehci_soft_itd_t *),
+ M_USB, M_WAITOK | M_ZERO);
+ if (sc->sc_softitds == NULL)
+ return ENOMEM;
+ LIST_INIT(&sc->sc_freeitds);
+
/* Set up the bus struct. */
sc->sc_bus.methods = &ehci_bus_methods;
sc->sc_bus.pipe_size = sizeof(struct ehci_pipe);
@@ -684,19 +711,34 @@ ehci_softintr(void *v)
void
ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
{
- ehci_soft_qtd_t *sqtd, *lsqtd;
- u_int32_t status;
+ int attr;
DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex));
+ attr = ex->xfer.pipe->endpoint->edesc->bmAttributes;
+ if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS)
+ ehci_check_itd_intr(sc, ex);
+ else
+ ehci_check_qh_intr(sc, ex);
+
+ return;
+}
+
+void
+ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+ ehci_soft_qtd_t *sqtd, *lsqtd;
+ __uint32_t status;
+
if (ex->sqtdstart == NULL) {
- printf("ehci_check_intr: sqtdstart=NULL\n");
+ printf("ehci_check_qh_intr: not valid sqtd\n");
return;
}
+
lsqtd = ex->sqtdend;
#ifdef DIAGNOSTIC
if (lsqtd == NULL) {
- printf("ehci_check_intr: lsqtd==0\n");
+ printf("ehci_check_qh_intr: lsqtd==0\n");
return;
}
#endif
@@ -744,6 +786,72 @@ ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
}
void
+ehci_check_itd_intr(ehci_softc_t *sc, struct ehci_xfer *ex) {
+ ehci_soft_itd_t *itd;
+ int i;
+
+ if (ex->itdstart == NULL) {
+ printf("ehci_check_itd_intr: not valid itd\n");
+ return;
+ }
+
+ itd = ex->itdend;
+#ifdef DIAGNOSTIC
+ if (itd == NULL) {
+ printf("ehci_check_itd_intr: itdend == 0\n");
+ return;
+ }
+#endif
+
+ /*
+ * Step 1, check no active transfers in last itd, meaning we're finished
+ */
+
+ usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl),
+ sizeof(itd->itd.itd_ctl), BUS_DMASYNC_POSTWRITE |
+ BUS_DMASYNC_POSTREAD);
+
+ for (i = 0; i < 8; i++) {
+ if (le32toh(itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE)
+ break;
+ }
+
+ if (i == 8) {
+ goto done; /* All 8 descriptors inactive, it's done */
+ }
+
+ /*
+ * Step 2, check for errors in status bits, throughout chain...
+ */
+
+ DPRINTFN(12, ("ehci_check_itd_intr: active ex=%p\n", ex));
+
+ for (itd = ex->itdstart; itd != ex->itdend; itd = itd->xfer_next) {
+ usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl),
+ sizeof(itd->itd.itd_ctl), BUS_DMASYNC_POSTWRITE |
+ BUS_DMASYNC_POSTREAD);
+
+ for (i = 0; i < 8; i++) {
+ if (le32toh(itd->itd.itd_ctl[i]) & (EHCI_ITD_BUF_ERR |
+ EHCI_ITD_BABBLE | EHCI_ITD_ERROR))
+ break;
+ }
+ if (i != 8) { /* Error in one of the itds */
+ goto done;
+ }
+ } /* itd search loop */
+
+ DPRINTFN(12, ("ehci_check_itd_intr: ex %p itd %p still active\n", ex,
+ ex->itdstart));
+ return;
+
+done:
+ DPRINTFN(12, ("ehci_check_itd_intr: ex=%p done\n", ex));
+ usb_uncallout(ex->xfer.timeout_handle, ehci_timeout, ex);
+ ehci_idone(ex);
+}
+
+void
ehci_idone(struct ehci_xfer *ex)
{
usbd_xfer_handle xfer = &ex->xfer;
@@ -770,7 +878,6 @@ ehci_idone(struct ehci_xfer *ex)
splx(s);
}
#endif
-
if (xfer->status == USBD_CANCELLED ||
xfer->status == USBD_TIMEOUT) {
DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer));
@@ -784,9 +891,65 @@ ehci_idone(struct ehci_xfer *ex)
#endif
/* The transfer is done, compute actual length and status. */
+
+ if (UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes)
+ == UE_ISOCHRONOUS) {
+ /* Isoc transfer */
+ struct ehci_soft_itd *itd;
+ int i, nframes, len, uframes;
+
+ nframes = 0;
+ actlen = 0;
+
+ switch (xfer->pipe->endpoint->edesc->bInterval) {
+ case 0:
+ panic("ehci: isoc xfer suddenly has 0 bInterval, invalid\n");
+ case 1: uframes = 1; break;
+ case 2: uframes = 2; break;
+ case 3: uframes = 4; break;
+ default: uframes = 8; break;
+ }
+
+ for (itd = ex->itdstart; itd != NULL; itd = itd->xfer_next) {
+ usb_syncmem(&itd->dma,itd->offs + offsetof(ehci_itd_t,itd_ctl),
+ sizeof(itd->itd.itd_ctl), BUS_DMASYNC_POSTWRITE |
+ BUS_DMASYNC_POSTREAD);
+
+ for (i = 0; i < 8; i += uframes) {
+ /* XXX - driver didn't fill in the frame full
+ * of uframes. This leads to scheduling
+ * inefficiencies, but working around
+ * this doubles complexity of tracking
+ * an xfer.
+ */
+ if (nframes >= xfer->nframes)
+ break;
+
+ status = le32toh(itd->itd.itd_ctl[i]);
+ len = EHCI_ITD_GET_LEN(status);
+ xfer->frlengths[nframes++] = len;
+ actlen += len;
+ }
+
+ if (nframes >= xfer->nframes)
+ break;
+ }
+
+ xfer->actlen = actlen;
+ xfer->status = USBD_NORMAL_COMPLETION;
+ if (xfer->rqflags & URQ_DEV_DMABUF) {
+ usb_syncmem(&xfer->dmabuf, 0, ex->isoc_len,
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ }
+
+ goto end;
+ }
+
+ /* Continue processing xfers using queue heads */
+
lsqtd = ex->sqtdend;
actlen = 0;
- for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd=sqtd->nextqtd) {
+ for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd = sqtd->nextqtd) {
usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
nstatus = le32toh(sqtd->qtd.qtd_status);
@@ -794,10 +957,11 @@ ehci_idone(struct ehci_xfer *ex)
break;
status = nstatus;
- if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP)
+ 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.
* The default pipe doesn't need it since control transfers
@@ -843,10 +1007,11 @@ ehci_idone(struct ehci_xfer *ex)
status &= EHCI_QTD_STATERRS | EHCI_QTD_PINGSTATE;
else
status &= EHCI_QTD_STATERRS;
- if (status == 0) /* no other errors means a stall */
+ if (status == 0) /* no other errors means a stall */ {
xfer->status = USBD_STALLED;
- else
+ } else {
xfer->status = USBD_IOERROR; /* more info XXX */
+ }
/* XXX need to reset TT on missed microframe */
if (status & EHCI_QTD_MISSEDMICRO) {
ehci_softc_t *sc =
@@ -860,6 +1025,10 @@ ehci_idone(struct ehci_xfer *ex)
xfer->status = USBD_NORMAL_COMPLETION;
}
+ end:
+ /* XXX transfer_complete memcpys out transfer data (for in endpoints)
+ * during this call, before methods->done is called: dma sync required
+ * beforehand? */
usb_transfer_complete(xfer);
DPRINTFN(/*12*/2, ("ehci_idone: ex=%p done\n", ex));
}
@@ -1332,13 +1501,52 @@ ehci_dump_sqh(ehci_soft_qh_t *sqh)
sizeof(sqh->qh), BUS_DMASYNC_PREREAD);
}
+#if notyet
+void
+ehci_dump_itd(struct ehci_soft_itd *itd)
+{
+ ehci_isoc_trans_t t;
+ ehci_isoc_bufr_ptr_t b, b2, b3;
+ int i;
+
+ printf("ITD: next phys=%X\n", itd->itd.itd_next);
+
+ for (i = 0; i < 8;i++) {
+ t = le32toh(itd->itd.itd_ctl[i]);
+ printf("ITDctl %d: stat=%X len=%X ioc=%X pg=%X offs=%X\n", i,
+ EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t),
+ EHCI_ITD_GET_IOC(t), EHCI_ITD_GET_PG(t),
+ EHCI_ITD_GET_OFFS(t));
+ }
+ printf("ITDbufr: ");
+ for (i = 0; i < 7; i++)
+ printf("%X,", EHCI_ITD_GET_BPTR(le32toh(itd->itd.itd_bufr[i])));
+
+ b = le32toh(itd->itd.itd_bufr[0]);
+ b2 = le32toh(itd->itd.itd_bufr[1]);
+ b3 = le32toh(itd->itd.itd_bufr[2]);
+ printf("\nep=%X daddr=%X dir=%d maxpkt=%X multi=%X\n",
+ EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2),
+ EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3));
+}
+
+void
+ehci_dump_sitd(struct ehci_soft_itd *itd)
+{
+ printf("SITD %p next=%p prev=%p xfernext=%p physaddr=%X slot=%d\n",
+ itd, itd->u.frame_list.next, itd->u.frame_list.prev,
+ itd->xfer_next, itd->physaddr, itd->slot);
+}
+#endif
+
#ifdef DIAGNOSTIC
-Static void
+void
ehci_dump_exfer(struct ehci_xfer *ex)
{
- printf("ehci_dump_exfer: ex=%p\n", ex);
+ printf("ehci_dump_exfer: ex=%p sqtdstart=%p end=%p itdstart=%p end=%p isdone=%d\n", ex, ex->sqtdstart, ex->sqtdend, ex->itdstart, ex->itdend, ex->isdone);
}
#endif
+
#endif
usbd_status
@@ -1381,6 +1589,8 @@ ehci_open(usbd_pipe_handle pipe)
pipe->methods = &ehci_root_intr_methods;
break;
default:
+ DPRINTF(("ehci_open: bad bEndpointAddress 0x%02x\n",
+ ed->bEndpointAddress));
return (USBD_INVAL);
}
return (USBD_NORMAL_COMPLETION);
@@ -1394,8 +1604,8 @@ ehci_open(usbd_pipe_handle pipe)
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 isoc device, "
- "this does not work yet.\n",
+ printf("%s: *** Error: opening low/full speed isoc device on"
+ "ehci, this does not work yet. Feel free to implement\n",
device_xname(sc->sc_dev));
DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
hshubaddr, hshubport));
@@ -1403,36 +1613,42 @@ ehci_open(usbd_pipe_handle pipe)
}
naks = 8; /* XXX */
- sqh = ehci_alloc_sqh(sc);
- if (sqh == NULL)
- return (USBD_NOMEM);
- /* qh_link filled when the QH is added */
- sqh->qh.qh_endp = htole32(
- EHCI_QH_SET_ADDR(addr) |
- EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
- EHCI_QH_SET_EPS(speed) |
- EHCI_QH_DTC |
- EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
- (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
- EHCI_QH_CTL : 0) |
- EHCI_QH_SET_NRL(naks)
- );
- sqh->qh.qh_endphub = htole32(
- EHCI_QH_SET_MULT(1) |
- EHCI_QH_SET_HUBA(hshubaddr) |
- EHCI_QH_SET_PORT(hshubport) |
- EHCI_QH_SET_CMASK(0x08) | /* XXX */
- EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x02 : 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);
- usb_syncmem(&sqh->dma, sqh->offs,
- sizeof(sqh->qh), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- epipe->sqh = sqh;
+ /* Allocate sqh for everything, save isoc xfers */
+ if (xfertype != UE_ISOCHRONOUS) {
+ sqh = ehci_alloc_sqh(sc);
+ if (sqh == NULL)
+ return (USBD_NOMEM);
+ /* qh_link filled when the QH is added */
+ sqh->qh.qh_endp = htole32(
+ EHCI_QH_SET_ADDR(addr) |
+ EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
+ EHCI_QH_SET_EPS(speed) |
+ EHCI_QH_DTC |
+ EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
+ (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
+ EHCI_QH_CTL : 0) |
+ EHCI_QH_SET_NRL(naks)
+ );
+ sqh->qh.qh_endphub = htole32(
+ EHCI_QH_SET_MULT(1) |
+ EHCI_QH_SET_HUBA(hshubaddr) |
+ EHCI_QH_SET_PORT(hshubport) |
+ EHCI_QH_SET_CMASK(0x08) | /* XXX */
+ EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x02 : 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);
+
+ usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ epipe->sqh = sqh;
+ } else {
+ sqh = NULL;
+ } /*xfertype == UE_ISOC*/
switch (xfertype) {
case UE_CONTROL:
@@ -1478,15 +1694,29 @@ ehci_open(usbd_pipe_handle pipe)
break;
case UE_ISOCHRONOUS:
pipe->methods = &ehci_device_isoc_methods;
- /* FALLTHROUGH */
+ if (ed->bInterval == 0 || ed->bInterval >= 16) {
+ printf("ehci: opening pipe with invalid bInterval\n");
+ err = USBD_INVAL;
+ goto bad;
+ }
+ if (UGETW(ed->wMaxPacketSize) == 0) {
+ printf("ehci: zero length endpoint open request\n");
+ err = USBD_INVAL;
+ goto bad;
+ }
+ epipe->u.isoc.next_frame = 0;
+ epipe->u.isoc.cur_xfers = 0;
+ break;
default:
+ DPRINTF(("ehci: bad xfer type %d\n", xfertype));
err = USBD_INVAL;
goto bad;
}
return (USBD_NORMAL_COMPLETION);
bad:
- ehci_free_sqh(sc, sqh);
+ if (sqh != NULL)
+ ehci_free_sqh(sc, sqh);
return (err);
}
@@ -1610,6 +1840,55 @@ ehci_sync_hc(ehci_softc_t *sc)
DPRINTFN(2,("ehci_sync_hc: exit\n"));
}
+/*Call at splusb*/
+void
+ehci_rem_free_itd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer)
+{
+ struct ehci_soft_itd *itd, *prev;
+
+ prev = NULL;
+
+ if (exfer->itdstart == NULL || exfer->itdend == NULL)
+ panic("ehci isoc xfer being freed, but with no itd chain\n");
+
+ for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+ prev = itd->u.frame_list.prev;
+ /* Unlink itd from hardware chain, or frame array */
+ if (prev == NULL) { /* We're at the table head */
+ sc->sc_softitds[itd->slot] = itd->u.frame_list.next;
+ sc->sc_flist[itd->slot] = itd->itd.itd_next;
+ usb_syncmem(&sc->sc_fldma,
+ sizeof(ehci_link_t) * itd->slot,
+ sizeof(ehci_link_t),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ if (itd->u.frame_list.next != NULL)
+ itd->u.frame_list.next->u.frame_list.prev = NULL;
+ } else {
+ /* XXX this part is untested... */
+ prev->itd.itd_next = itd->itd.itd_next;
+ usb_syncmem(&itd->dma,
+ itd->offs + offsetof(ehci_itd_t, itd_next),
+ sizeof(itd->itd.itd_next), BUS_DMASYNC_PREWRITE);
+
+ prev->u.frame_list.next = itd->u.frame_list.next;
+ if (itd->u.frame_list.next != NULL)
+ itd->u.frame_list.next->u.frame_list.prev = prev;
+ }
+ }
+
+ prev = NULL;
+ for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+ if (prev != NULL)
+ ehci_free_itd(sc, prev);
+ prev = itd;
+ }
+ if (prev)
+ ehci_free_itd(sc, prev);
+ exfer->itdstart = NULL;
+ exfer->itdend = NULL;
+}
+
/***********/
/*
@@ -2477,6 +2756,82 @@ ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd,
}
}
+ehci_soft_itd_t *
+ehci_alloc_itd(ehci_softc_t *sc)
+{
+ struct ehci_soft_itd *itd, *freeitd;
+ usbd_status err;
+ int i, s, offs, frindex, previndex;
+ usb_dma_t dma;
+
+ s = splusb();
+
+ /* Find an itd that wasn't freed this frame or last frame. This can
+ * discard itds that were freed before frindex wrapped around
+ * XXX - can this lead to thrashing? Could fix by enabling wrap-around
+ * interrupt and fiddling with list when that happens */
+ frindex = (EOREAD4(sc, EHCI_FRINDEX) + 1) >> 3;
+ previndex = (frindex != 0) ? frindex - 1 : sc->sc_flsize;
+
+ freeitd = NULL;
+ LIST_FOREACH(itd, &sc->sc_freeitds, u.free_list) {
+ if (itd == NULL)
+ break;
+ if (itd->slot != frindex && itd->slot != previndex) {
+ freeitd = itd;
+ break;
+ }
+ }
+
+ if (freeitd == NULL) {
+ DPRINTFN(2, ("ehci_alloc_itd allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK,
+ EHCI_PAGE_SIZE, &dma);
+
+ if (err) {
+ DPRINTF(("ehci_alloc_itd, alloc returned %d\n", err));
+ return NULL;
+ }
+
+ for (i = 0; i < EHCI_ITD_CHUNK; i++) {
+ offs = i * EHCI_ITD_SIZE;
+ itd = KERNADDR(&dma, offs);
+ itd->physaddr = DMAADDR(&dma, offs);
+ itd->dma = dma;
+ itd->offs = offs;
+ LIST_INSERT_HEAD(&sc->sc_freeitds, itd, u.free_list);
+ }
+ freeitd = LIST_FIRST(&sc->sc_freeitds);
+ }
+
+ itd = freeitd;
+ LIST_REMOVE(itd, u.free_list);
+ memset(&itd->itd, 0, sizeof(ehci_itd_t));
+ usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_next),
+ sizeof(itd->itd.itd_next), BUS_DMASYNC_PREWRITE |
+ BUS_DMASYNC_PREREAD);
+
+ itd->u.frame_list.next = NULL;
+ itd->u.frame_list.prev = NULL;
+ itd->xfer_next = NULL;
+ itd->slot = 0;
+ splx(s);
+
+ return itd;
+}
+
+void
+ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd)
+{
+ int s;
+
+ s = splusb();
+ LIST_INSERT_AFTER(LIST_FIRST(&sc->sc_freeitds), itd, u.free_list);
+ splx(s);
+}
+
+
+
/****************/
/*
@@ -2534,7 +2889,7 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
return;
}
- if (xfer->device->bus->intr_context || !curproc)
+ if (xfer->device->bus->intr_context)
panic("ehci_abort_xfer: not in process context");
/*
@@ -2660,6 +3015,93 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
}
void
+ehci_abort_isoc_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+ ehci_isoc_trans_t trans_status;
+ struct ehci_pipe *epipe;
+ struct ehci_xfer *exfer;
+ ehci_softc_t *sc;
+ struct ehci_soft_itd *itd;
+ int s, i, wake;
+
+ epipe = (struct ehci_pipe *) xfer->pipe;
+ exfer = EXFER(xfer);
+ sc = epipe->pipe.device->bus->hci_private;
+
+ DPRINTF(("ehci_abort_isoc_xfer: xfer %p pipe %p\n", xfer, epipe));
+
+ if (sc->sc_dying) {
+ s = splusb();
+ xfer->status = status;
+ usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer);
+ usb_transfer_complete(xfer);
+ splx(s);
+ return;
+ }
+
+ if (xfer->hcflags & UXFER_ABORTING) {
+ DPRINTFN(2, ("ehci_abort_isoc_xfer: already aborting\n"));
+
+#ifdef DIAGNOSTIC
+ if (status == USBD_TIMEOUT)
+ printf("ehci_abort_xfer: TIMEOUT while aborting\n");
+#endif
+
+ xfer->status = status;
+ DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
+ xfer->hcflags |= UXFER_ABORTWAIT;
+ while (xfer->hcflags & UXFER_ABORTING)
+ tsleep(&xfer->hcflags, PZERO, "ehciiaw", 0);
+ return;
+ }
+ xfer->hcflags |= UXFER_ABORTING;
+
+ xfer->status = status;
+ usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer);
+
+ s = splusb();
+ for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+ usb_syncmem(&itd->dma,
+ itd->offs + offsetof(ehci_itd_t, itd_ctl),
+ sizeof(itd->itd.itd_ctl),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+
+ for (i = 0; i < 8; i++) {
+ trans_status = le32toh(itd->itd.itd_ctl[i]);
+ trans_status &= ~EHCI_ITD_ACTIVE;
+ itd->itd.itd_ctl[i] = htole32(trans_status);
+ }
+
+ usb_syncmem(&itd->dma,
+ itd->offs + offsetof(ehci_itd_t, itd_ctl),
+ sizeof(itd->itd.itd_ctl),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+ splx(s);
+
+ s = splusb();
+#ifdef USB_USE_SOFTINTR
+ sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+ usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+ tsleep(&sc->sc_softwake, PZERO, "ehciab", 0);
+#endif /* USB_USE_SOFTINTR */
+ splx(s);
+
+#ifdef DIAGNOSTIC
+ exfer->isdone = 1;
+#endif
+ wake = xfer->hcflags & UXFER_ABORTWAIT;
+ xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
+ usb_transfer_complete(xfer);
+ if (wake)
+ wakeup(&xfer->hcflags);
+
+ return;
+}
+
+void
ehci_timeout(void *addr)
{
struct ehci_xfer *exfer = addr;
@@ -3256,6 +3698,11 @@ ehci_device_intr_abort(usbd_xfer_handle xfer)
DPRINTFN(1, ("echi_device_intr_abort: remove\n"));
xfer->pipe->intrxfer = NULL;
}
+ /*
+ * XXX - abort_xfer uses ehci_sync_hc, which syncs via the advance
+ * async doorbell. That's dependant on the async list, wheras
+ * intr xfers are periodic, should not use this?
+ */
ehci_abort_xfer(xfer, USBD_CANCELLED);
}
@@ -3340,22 +3787,324 @@ ehci_device_intr_done(usbd_xfer_handle xfer)
Static usbd_status
ehci_device_isoc_transfer(usbd_xfer_handle xfer)
{
- return USBD_IOERROR;
+ usbd_status err;
+
+ err = usb_insert_transfer(xfer);
+ if (err && err != USBD_IN_PROGRESS)
+ return err;
+
+ return ehci_device_isoc_start(xfer);
}
+
Static usbd_status
ehci_device_isoc_start(usbd_xfer_handle xfer)
{
- return USBD_IOERROR;
+ struct ehci_pipe *epipe;
+ usbd_device_handle dev;
+ ehci_softc_t *sc;
+ struct ehci_xfer *exfer;
+ ehci_soft_itd_t *itd, *prev, *start, *stop;
+ usb_dma_t *dma_buf;
+ int i, j, k, frames, uframes;
+ int s, trans_count, offs, total_length;
+ int frindex;
+
+ start = NULL;
+ prev = NULL;
+ itd = NULL;
+ trans_count = 0;
+ total_length = 0;
+ exfer = (struct ehci_xfer *) xfer;
+ sc = xfer->pipe->device->bus->hci_private;
+ dev = xfer->pipe->device;
+ epipe = (struct ehci_pipe *)xfer->pipe;
+
+ /*
+ * To allow continuous transfers, above we start all transfers
+ * immediately. However, we're still going to get usbd_start_next call
+ * this when another xfer completes. So, check if this is already
+ * in progress or not
+ */
+
+ if (exfer->itdstart != NULL)
+ return USBD_IN_PROGRESS;
+
+ DPRINTFN(2, ("ehci_device_isoc_start: xfer %p len %d flags %d\n",
+ xfer, xfer->length, xfer->flags));
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ /*
+ * To avoid complication, don't allow a request right now that'll span
+ * the entire frame table. To within 4 frames, to allow some leeway
+ * on either side of where the hc currently is.
+ */
+ if ((1 << (epipe->pipe.endpoint->edesc->bInterval)) *
+ xfer->nframes >= (sc->sc_flsize - 4) * 8) {
+ printf("ehci: isoc descriptor requested that spans the entire frametable, too many frames\n");
+ return USBD_INVAL;
+ }
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("ehci_device_isoc_start: request\n");
+
+ if (!exfer->isdone)
+ printf("ehci_device_isoc_start: not done, ex = %p\n", exfer);
+ exfer->isdone = 0;
+#endif
+
+ /*
+ * Step 1: Allocate and initialize itds, how many do we need?
+ * One per transfer if interval >= 8 microframes, fewer if we use
+ * multiple microframes per frame.
+ */
+
+ i = epipe->pipe.endpoint->edesc->bInterval;
+ if (i > 16 || i == 0) {
+ /* Spec page 271 says intervals > 16 are invalid */
+ DPRINTF(("ehci_device_isoc_start: bInvertal %d invalid\n", i));
+ return USBD_INVAL;
+ } else if (i >= 4) {
+ frames = xfer->nframes;
+ uframes = 8;
+ } else {
+ frames = xfer->nframes + 0x7; /* 7 added for rounding up */
+ uframes = 0;
+ switch (i) {
+ case 1: frames /= 8; uframes = 1; break;
+ case 2: frames /= 4; uframes = 2; break;
+ case 3: frames /= 2; uframes = 4; break;
+ }
+ }
+
+ if (frames == 0) {
+ DPRINTF(("ehci_device_isoc_start: frames == 0\n"));
+ return USBD_INVAL;
+ }
+
+ dma_buf = &xfer->dmabuf;
+ offs = 0;
+
+ for (i = 0; i < frames; i++) {
+ int froffs = offs;
+ itd = ehci_alloc_itd(sc);
+
+ if (prev != NULL) {
+ prev->itd.itd_next =
+ htole32(itd->physaddr | EHCI_LINK_ITD);
+ usb_syncmem(&itd->dma,
+ itd->offs + offsetof(ehci_itd_t, itd_next),
+ sizeof(itd->itd.itd_next), BUS_DMASYNC_POSTWRITE);
+
+ prev->xfer_next = itd;
+ } else {
+ start = itd;
+ }
+
+ /*
+ * Step 1.5, initialize uframes
+ */
+ for (j = 0; j < 8; j += uframes) {
+ /* Calculate which page in the list this starts in */
+ int addr = DMAADDR(dma_buf, froffs);
+ addr = EHCI_PAGE_OFFSET(addr);
+ addr += (offs - froffs);
+ addr = EHCI_PAGE(addr);
+ addr /= EHCI_PAGE_SIZE;
+
+ /* This gets the initial offset into the first page,
+ * looks how far further along the current uframe
+ * offset is. Works out how many pages that is.
+ */
+
+ itd->itd.itd_ctl[j] = htole32 ( EHCI_ITD_ACTIVE |
+ EHCI_ITD_SET_LEN(xfer->frlengths[trans_count]) |
+ EHCI_ITD_SET_PG(addr) |
+ EHCI_ITD_SET_OFFS(EHCI_PAGE_OFFSET(DMAADDR(dma_buf,offs))));
+
+ total_length += xfer->frlengths[trans_count];
+ offs += xfer->frlengths[trans_count];
+ trans_count++;
+
+ if (trans_count >= xfer->nframes) { /*Set IOC*/
+ itd->itd.itd_ctl[j] |= htole32(EHCI_ITD_IOC);
+ }
+ }
+
+ /* Step 1.75, set buffer pointers. To simplify matters, all
+ * pointers are filled out for the next 7 hardware pages in
+ * the dma block, so no need to worry what pages to cover
+ * and what to not.
+ */
+
+ for (j=0; j < 7; j++) {
+ /*
+ * Don't try to lookup a page that's past the end
+ * of buffer
+ */
+ int page_offs = EHCI_PAGE(froffs + (EHCI_PAGE_SIZE * j));
+ if (page_offs >= dma_buf->block->size)
+ break;
+
+ int page = DMAADDR(dma_buf, page_offs);
+ page = EHCI_PAGE(page);
+ itd->itd.itd_bufr[j] =
+ htole32(EHCI_ITD_SET_BPTR(page) |
+ EHCI_LINK_ITD);
+ }
+
+ /*
+ * Other special values
+ */
+
+ k = epipe->pipe.endpoint->edesc->bEndpointAddress;
+ itd->itd.itd_bufr[0] |= htole32(EHCI_ITD_SET_EP(UE_GET_ADDR(k)) |
+ EHCI_ITD_SET_DADDR(epipe->pipe.device->address));
+
+ k = (UE_GET_DIR(epipe->pipe.endpoint->edesc->bEndpointAddress))
+ ? 1 : 0;
+ j = UE_GET_SIZE(UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize));
+ itd->itd.itd_bufr[1] |= htole32(EHCI_ITD_SET_DIR(k) |
+ EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j)));
+
+ /* FIXME: handle invalid trans */
+ itd->itd.itd_bufr[2] |=
+ htole32(EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1));
+
+ usb_syncmem(&itd->dma,
+ itd->offs + offsetof(ehci_itd_t, itd_next),
+ sizeof(ehci_itd_t),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ prev = itd;
+ } /* End of frame */
+
+ stop = itd;
+ stop->xfer_next = NULL;
+ exfer->isoc_len = total_length;
+
+ /*
+ * Part 2: Transfer descriptors have now been set up, now they must
+ * be scheduled into the period frame list. Erk. Not wanting to
+ * complicate matters, transfer is denied if the transfer spans
+ * more than the period frame list.
+ */
+
+ s = splusb();
+
+ /* Start inserting frames */
+ if (epipe->u.isoc.cur_xfers > 0) {
+ frindex = epipe->u.isoc.next_frame;
+ } else {
+ frindex = EOREAD4(sc, EHCI_FRINDEX);
+ frindex = frindex >> 3; /* Erase microframe index */
+ frindex += 2;
+ }
+
+ if (frindex >= sc->sc_flsize)
+ frindex &= (sc->sc_flsize - 1);
+
+ /* Whats the frame interval? */
+ i = (1 << epipe->pipe.endpoint->edesc->bInterval);
+ if (i / 8 == 0)
+ i = 1;
+ else
+ i /= 8;
+
+ itd = start;
+ for (j = 0; j < frames; j++) {
+ if (itd == NULL)
+ panic("ehci: unexpectedly ran out of isoc itds, isoc_start\n");
+
+ itd->itd.itd_next = sc->sc_flist[frindex];
+ if (itd->itd.itd_next == 0)
+ /* FIXME: frindex table gets initialized to NULL
+ * or EHCI_NULL? */
+ itd->itd.itd_next = htole32(EHCI_NULL);
+
+ usb_syncmem(&itd->dma,
+ itd->offs + offsetof(ehci_itd_t, itd_next),
+ sizeof(itd->itd.itd_next),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ sc->sc_flist[frindex] = htole32(EHCI_LINK_ITD | itd->physaddr);
+
+ usb_syncmem(&sc->sc_fldma,
+ sizeof(ehci_link_t) * frindex,
+ sizeof(ehci_link_t),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ itd->u.frame_list.next = sc->sc_softitds[frindex];
+ sc->sc_softitds[frindex] = itd;
+ if (itd->u.frame_list.next != NULL)
+ itd->u.frame_list.next->u.frame_list.prev = itd;
+ itd->slot = frindex;
+ itd->u.frame_list.prev = NULL;
+
+ frindex += i;
+ if (frindex >= sc->sc_flsize)
+ frindex -= sc->sc_flsize;
+
+ itd = itd->xfer_next;
+ }
+
+ epipe->u.isoc.cur_xfers++;
+ epipe->u.isoc.next_frame = frindex;
+
+ exfer->itdstart = start;
+ exfer->itdend = stop;
+ exfer->sqtdstart = NULL;
+ exfer->sqtdstart = NULL;
+
+ ehci_add_intr_list(sc, exfer);
+ xfer->status = USBD_IN_PROGRESS;
+ xfer->done = 0;
+ splx(s);
+
+ if (sc->sc_bus.use_polling) {
+ printf("Starting ehci isoc xfer with polling. Bad idea?\n");
+ ehci_waitintr(sc, xfer);
+ }
+
+ return USBD_IN_PROGRESS;
}
+
Static void
ehci_device_isoc_abort(usbd_xfer_handle xfer)
{
+ DPRINTFN(1, ("ehci_device_isoc_abort: xfer = %p\n", xfer));
+ ehci_abort_isoc_xfer(xfer, USBD_CANCELLED);
}
+
Static void
ehci_device_isoc_close(usbd_pipe_handle pipe)
{
+ printf("ehci_device_isoc_close: nothing in the pipe to free?\n");
}
+
Static void
ehci_device_isoc_done(usbd_xfer_handle xfer)
{
+ struct ehci_xfer *exfer;
+ ehci_softc_t *sc;
+ struct ehci_pipe *epipe;
+ int s;
+
+ exfer = EXFER(xfer);
+ sc = xfer->pipe->device->bus->hci_private;
+ epipe = (struct ehci_pipe *) xfer->pipe;
+
+ s = splusb();
+ epipe->u.isoc.cur_xfers--;
+ if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
+ ehci_del_intr_list(exfer);
+ ehci_rem_free_itd_chain(sc, exfer);
+ }
+ splx(s);
+
+ usb_syncmem(&xfer->dmabuf, 0, xfer->length, BUS_DMASYNC_POSTWRITE |
+ BUS_DMASYNC_POSTREAD);
+
}
diff --git a/sys/dev/usb/ehcireg.h b/sys/dev/usb/ehcireg.h
index e21e1843c4c..8349ba35e1e 100644
--- a/sys/dev/usb/ehcireg.h
+++ b/sys/dev/usb/ehcireg.h
@@ -1,4 +1,4 @@
-/* $NetBSD: ehcireg.h,v 1.27 2008/04/28 20:23:58 martin Exp $ */
+/* $NetBSD: ehcireg.h,v 1.28 2008/08/02 22:23:18 jmcneill Exp $ */
/*
* Copyright (c) 2001, 2004 The NetBSD Foundation, Inc.
@@ -188,10 +188,41 @@ typedef u_int32_t ehci_link_t;
typedef u_int32_t ehci_physaddr_t;
+typedef u_int32_t ehci_isoc_trans_t;
+typedef u_int32_t ehci_isoc_bufr_ptr_t;
+
/* Isochronous Transfer Descriptor */
typedef struct {
- volatile ehci_link_t itd_next;
- /* XXX many more */
+ volatile ehci_link_t itd_next;
+ volatile ehci_isoc_trans_t itd_ctl[8];
+#define EHCI_ITD_GET_STATUS(x) (((x) >> 28) & 0xf)
+#define EHCI_ITD_SET_STATUS(x) (((x) & 0xf) << 28)
+#define EHCI_ITD_ACTIVE 0x80000000
+#define EHCI_ITD_BUF_ERR 0x40000000
+#define EHCI_ITD_BABBLE 0x20000000
+#define EHCI_ITD_ERROR 0x10000000
+#define EHCI_ITD_GET_LEN(x) (((x) >> 16) & 0xfff)
+#define EHCI_ITD_SET_LEN(x) (((x) & 0xfff) << 16)
+#define EHCI_ITD_IOC 0x8000
+#define EHCI_ITD_GET_IOC(x) (((x) >> 15) & 1)
+#define EHCI_ITD_SET_IOC(x) (((x) << 15) & EHCI_ITD_IOC)
+#define EHCI_ITD_GET_PG(x) (((x) >> 12) & 0xf)
+#define EHCI_ITD_SET_PG(x) (((x) & 0xf) << 12)
+#define EHCI_ITD_GET_OFFS(x) (((x) >> 0) & 0xfff)
+#define EHCI_ITD_SET_OFFS(x) (((x) & 0xfff) << 0)
+ volatile ehci_isoc_bufr_ptr_t itd_bufr[7];
+#define EHCI_ITD_GET_BPTR(x) ((x) & 0xfffff000)
+#define EHCI_ITD_SET_BPTR(x) ((x) & 0xfffff000)
+#define EHCI_ITD_GET_EP(x) (((x) >> 8) & 0xf)
+#define EHCI_ITD_SET_EP(x) (((x) & 0xf) << 8)
+#define EHCI_ITD_GET_DADDR(x) ((x) & 0x7f)
+#define EHCI_ITD_SET_DADDR(x) ((x) & 0x7f)
+#define EHCI_ITD_GET_DIR(x) (((x) >> 11) & 1)
+#define EHCI_ITD_SET_DIR(x) (((x) & 1) << 11)
+#define EHCI_ITD_GET_MAXPKT(x) ((x) & 0x7ff)
+#define EHCI_ITD_SET_MAXPKT(x) ((x) & 0x7ff)
+#define EHCI_ITD_GET_MULTI(x) ((x) & 0x3)
+#define EHCI_ITD_SET_MULTI(x) ((x) & 0x3)
} ehci_itd_t;
#define EHCI_ITD_ALIGN 32
diff --git a/sys/dev/usb/ehcivar.h b/sys/dev/usb/ehcivar.h
index 161bfe2dfcd..f8f63f1c8b3 100644
--- a/sys/dev/usb/ehcivar.h
+++ b/sys/dev/usb/ehcivar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: ehcivar.h,v 1.31 2008/06/28 17:42:53 bouyer Exp $ */
+/* $NetBSD: ehcivar.h,v 1.32 2008/08/02 22:23:18 jmcneill Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -54,12 +54,36 @@ typedef struct ehci_soft_qh {
#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)
+typedef struct ehci_soft_itd {
+ ehci_itd_t itd;
+ union {
+ struct {
+ /* soft_itds links in a periodic frame*/
+ struct ehci_soft_itd *next;
+ struct ehci_soft_itd *prev;
+ } frame_list;
+ /* circular list of free itds */
+ LIST_ENTRY(ehci_soft_itd) free_list;
+ } u;
+ struct ehci_soft_itd *xfer_next; /* Next soft_itd in xfer */
+ ehci_physaddr_t physaddr;
+ usb_dma_t dma;
+ int offs;
+ int slot;
+ struct timeval t; /* store free time */
+} ehci_soft_itd_t;
+#define EHCI_ITD_SIZE ((sizeof(struct ehci_soft_itd) + EHCI_QH_ALIGN - 1) / EHCI_ITD_ALIGN * EHCI_ITD_ALIGN)
+#define EHCI_ITD_CHUNK (EHCI_PAGE_SIZE / EHCI_ITD_SIZE)
+
struct ehci_xfer {
struct usbd_xfer xfer;
struct usb_task abort_task;
LIST_ENTRY(ehci_xfer) inext; /* list of active xfers */
ehci_soft_qtd_t *sqtdstart;
ehci_soft_qtd_t *sqtdend;
+ ehci_soft_itd_t *itdstart;
+ ehci_soft_itd_t *itdend;
+ u_int isoc_len;
int isdone; /* used only when DIAGNOSTIC is defined */
};
#define EXFER(xfer) ((struct ehci_xfer *)(xfer))
@@ -81,6 +105,8 @@ struct ehci_soft_islot {
#define EHCI_HASH_SIZE 128
#define EHCI_COMPANION_MAX 8
+#define EHCI_FREE_LIST_INTERVAL 100
+
typedef struct ehci_softc {
device_t sc_dev;
struct usbd_bus sc_bus;
@@ -107,10 +133,16 @@ typedef struct ehci_softc {
struct ehci_soft_islot sc_islots[EHCI_INTRQHS];
+ /* jcmm - an array matching sc_flist, but with software pointers,
+ * not hardware address pointers
+ */
+ struct ehci_soft_itd **sc_softitds;
+
LIST_HEAD(, ehci_xfer) sc_intrhead;
ehci_soft_qh_t *sc_freeqhs;
ehci_soft_qtd_t *sc_freeqtds;
+ LIST_HEAD(sc_freeitds, ehci_soft_itd) sc_freeitds;
int sc_noport;
u_int8_t sc_hasppc; /* has Port Power Control */