So I've been sitting on this diff for quite some months now because I
wanted to fix some other issues asking for some more tests.
Various people already tested it, I need this in order to be able to
properly integrate xhci(4) to our stack. The main goal of this diff
is to remove the need to re-open the default pipe when attaching a
USB device. But instead of staying with the previous hack to update
the address and packet length in every device request, it changes the
way asynchronous queue heads are used. Instead of using the same queue
head for every transfer of an endpoint we use a different one per
transfer. While such approach might put more pressure on memory
allocation for the moment in certain situations, it will allow us to
abort transfers more easily by simply removing the queue head from the
asynchronous list. The idea is to improve the situation when a timeout
fires and try to abort a transfer.
So I'm asking for regression. If this diff fixes one of your problem
with ehci(4), that's a good news, if it doesn't that's not its goal.
But if you find new problem, please report to me.
As usual, don't test with critical data.
Index: ehci.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/ehci.c,v
retrieving revision 1.164
diff -u -p -r1.164 ehci.c
--- ehci.c 5 Aug 2014 20:26:15 -0000 1.164
+++ ehci.c 9 Aug 2014 09:57:26 -0000
@@ -82,7 +82,6 @@ struct pool *ehcixfer;
struct ehci_pipe {
struct usbd_pipe pipe;
- struct ehci_soft_qh *sqh;
union {
/* Control pipe */
struct {
@@ -156,11 +155,12 @@ void ehci_pcd(struct ehci_softc *, stru
void ehci_disown(struct ehci_softc *, int, int);
struct ehci_soft_qh *ehci_alloc_sqh(struct ehci_softc *);
-void ehci_free_sqh(struct ehci_softc *, struct ehci_soft_qh *);
+void ehci_free_sqh(struct ehci_softc *, struct usbd_xfer *);
+int ehci_setup_sqh(struct ehci_softc *, struct usbd_xfer *);
struct ehci_soft_qtd *ehci_alloc_sqtd(struct ehci_softc *);
void ehci_free_sqtd(struct ehci_softc *, struct ehci_soft_qtd *);
-usbd_status ehci_alloc_sqtd_chain(struct ehci_softc *, u_int,
+int ehci_alloc_sqtd_chain(struct ehci_softc *, u_int,
struct usbd_xfer *, struct ehci_soft_qtd **, struct
ehci_soft_qtd **);
void ehci_free_sqtd_chain(struct ehci_softc *, struct ehci_xfer *);
@@ -178,7 +178,8 @@ usbd_status ehci_device_setintr(struct e
void ehci_add_qh(struct ehci_soft_qh *, struct ehci_soft_qh *);
void ehci_rem_qh(struct ehci_softc *, struct ehci_soft_qh *);
-void ehci_set_qh_qtd(struct ehci_soft_qh *, struct ehci_soft_qtd *);
+void ehci_insert_qh(struct ehci_softc *, struct usbd_xfer *,
+ struct ehci_soft_qtd *);
void ehci_sync_hc(struct ehci_softc *);
void ehci_close_pipe(struct usbd_pipe *);
@@ -480,10 +481,6 @@ ehci_init(struct ehci_softc *sc)
return (USBD_NORMAL_COMPLETION);
-#if 0
- bad2:
- ehci_free_sqh(sc, sc->sc_async_head);
-#endif
bad1:
usb_freemem(&sc->sc_bus, &sc->sc_fldma);
return (err);
@@ -720,6 +717,12 @@ ehci_check_qh_intr(struct ehci_softc *sc
DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex));
timeout_del(&xfer->timeout_handle);
usb_rem_task(xfer->pipe->device, &xfer->abort_task);
+ ehci_rem_qh(sc, ex->sqh);
+
+ /* XXX Not needed for all transfers. */
+ xfer->pipe->endpoint->savedtoggle =
+ EHCI_QTD_GET_TOGGLE(letoh32(ex->sqh->qh.qh_qtd.qtd_status));
+
ehci_idone(xfer);
}
@@ -777,9 +780,6 @@ void
ehci_idone(struct usbd_xfer *xfer)
{
struct ehci_xfer *ex = (struct ehci_xfer *)xfer;
-#ifdef EHCI_DEBUG
- struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
-#endif
struct ehci_soft_qtd *sqtd;
u_int32_t status = 0, nstatus = 0;
int actlen, cerr;
@@ -809,7 +809,7 @@ ehci_idone(struct usbd_xfer *xfer)
}
#ifdef EHCI_DEBUG
- DPRINTFN(/*10*/2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer,
epipe));
+ DPRINTFN(2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer, xfer->pipe));
if (ehcidebug > 10)
ehci_dump_sqtds(ex->sqtdstart);
#endif
@@ -897,6 +897,7 @@ ehci_idone(struct usbd_xfer *xfer)
actlen += sqtd->len - EHCI_QTD_GET_BYTES(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));
@@ -916,7 +917,7 @@ ehci_idone(struct usbd_xfer *xfer)
xfer->pipe->endpoint->edesc->bEndpointAddress,
sbuf));
if (ehcidebug > 2) {
- ehci_dump_sqh(epipe->sqh);
+ ehci_dump_sqh(ex->sqh);
ehci_dump_sqtds(ex->sqtdstart);
}
#endif
@@ -1202,19 +1203,11 @@ ehci_freex(struct usbd_bus *bus, struct
void
ehci_device_clear_toggle(struct usbd_pipe *pipe)
{
- struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
-
- DPRINTF(("ehci_device_clear_toggle: epipe=%p status=0x%x\n",
- epipe, epipe->sqh->qh.qh_qtd.qtd_status));
#if defined(EHCI_DEBUG) && defined(USB_DEBUG)
if (ehcidebug)
usbd_dump_pipe(pipe);
#endif
-#ifdef DIAGNOSTIC
- if ((epipe->sqh->qh.qh_qtd.qtd_status & htole32(EHCI_QTD_ACTIVE)) != 0)
- panic("ehci_device_clear_toggle: queue active");
-#endif
- epipe->sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE_MASK);
+ pipe->endpoint->savedtoggle = 0;
}
#ifdef EHCI_DEBUG
@@ -1416,32 +1409,18 @@ ehci_dump_exfer(struct ehci_xfer *ex)
usbd_status
ehci_open(struct usbd_pipe *pipe)
{
- struct usbd_device *dev = pipe->device;
- struct ehci_softc *sc = (struct ehci_softc *)dev->bus;
- usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
- u_int8_t addr = dev->address;
- u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
+ struct ehci_softc *sc = (struct ehci_softc *)pipe->device->bus;
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
- struct ehci_soft_qh *sqh;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
+ u_int8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
usbd_status err;
- int s;
- int ival, speed, naks;
- int hshubaddr, hshubport;
DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d\n",
- pipe, addr, ed->bEndpointAddress));
+ pipe, pipe->device->address, ed->bEndpointAddress));
if (sc->sc_bus.dying)
return (USBD_IOERROR);
- if (dev->myhsport) {
- hshubaddr = dev->myhsport->parent->address;
- hshubport = dev->myhsport->portno;
- } else {
- hshubaddr = 0;
- hshubport = 0;
- }
-
/* Root Hub */
if (pipe->device->depth == 0) {
switch (ed->bEndpointAddress) {
@@ -1457,125 +1436,115 @@ ehci_open(struct usbd_pipe *pipe)
return (USBD_NORMAL_COMPLETION);
}
- /* XXX All this stuff is only valid for async. */
- switch (dev->speed) {
- case USB_SPEED_LOW:
- speed = EHCI_QH_SPEED_LOW;
- break;
- case USB_SPEED_FULL:
- speed = EHCI_QH_SPEED_FULL;
- break;
- case USB_SPEED_HIGH:
- speed = EHCI_QH_SPEED_HIGH;
- break;
- default:
- panic("ehci_open: bad device speed %d", dev->speed);
- }
- if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) {
- printf("%s: Error opening low/full speed isoc endpoint.\n"
- "A low/full speed device is attached to a USB2 hub, and "
- "transaction translations are not yet supported.\n"
- "Reattach the device to the root hub instead.\n",
- sc->sc_bus.bdev.dv_xname);
- DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
- hshubaddr, hshubport));
- return (USBD_INVAL);
- }
-
- naks = 8; /* XXX */
-
- /* 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) |
- (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) |
- EHCI_QH_SET_HUBA(hshubaddr) |
- EHCI_QH_SET_PORT(hshubport) |
- EHCI_QH_SET_CMASK(0x1c) | /* XXX */
- EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0)
- );
- sqh->qh.qh_curqtd = htole32(EHCI_LINK_TERMINATE);
- /* Fill the overlay qTD */
- sqh->qh.qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE);
- sqh->qh.qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE);
- sqh->qh.qh_qtd.qtd_status =
- htole32(EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle));
-
- 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:
err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t),
0, &epipe->u.ctl.reqdma);
-#ifdef EHCI_DEBUG
- if (err)
- printf("ehci_open: usb_allocmem()=%d\n", err);
-#endif
if (err)
- goto bad;
+ return (err);
pipe->methods = &ehci_device_ctrl_methods;
- s = splusb();
- ehci_add_qh(sqh, sc->sc_async_head);
- splx(s);
break;
case UE_BULK:
pipe->methods = &ehci_device_bulk_methods;
- s = splusb();
- ehci_add_qh(sqh, sc->sc_async_head);
- splx(s);
break;
case UE_INTERRUPT:
pipe->methods = &ehci_device_intr_methods;
- ival = pipe->interval;
- if (ival == USBD_DEFAULT_INTERVAL)
- ival = ed->bInterval;
- s = splusb();
- err = ehci_device_setintr(sc, sqh, ival);
- splx(s);
- return (err);
+ break;
case UE_ISOCHRONOUS:
- pipe->methods = &ehci_device_isoc_methods;
+ if (pipe->device->speed != USB_SPEED_HIGH) {
+ printf("%s: transaction translator not supported.\n",
+ sc->sc_bus.bdev.dv_xname);
+ return (USBD_INVAL);
+ }
+
if (ed->bInterval == 0 || ed->bInterval > 16) {
printf("ehci: opening pipe with invalid bInterval\n");
- err = USBD_INVAL;
- goto bad;
+ return (USBD_INVAL);
}
if (UGETW(ed->wMaxPacketSize) == 0) {
printf("ehci: zero length endpoint open request\n");
- err = USBD_INVAL;
- goto bad;
+ return (USBD_INVAL);
}
epipe->u.isoc.next_frame = 0;
epipe->u.isoc.cur_xfers = 0;
+ pipe->methods = &ehci_device_isoc_methods;
break;
default:
DPRINTF(("ehci: bad xfer type %d\n", xfertype));
return (USBD_INVAL);
}
+
return (USBD_NORMAL_COMPLETION);
+}
-bad:
- if (sqh != NULL)
- ehci_free_sqh(sc, sqh);
- return (err);
+int
+ehci_setup_sqh(struct ehci_softc *sc, struct usbd_xfer *xfer)
+{
+ struct ehci_xfer *ex = (struct ehci_xfer *)xfer;
+ usb_endpoint_descriptor_t *ed = xfer->pipe->endpoint->edesc;
+ u_int8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
+ int speed, hshubaddr, hshubport;
+ struct ehci_soft_qh *sqh;
+
+
+ if (xfer->device->myhsport) {
+ hshubaddr = xfer->device->myhsport->parent->address;
+ hshubport = xfer->device->myhsport->portno;
+ } else {
+ hshubaddr = 0;
+ hshubport = 0;
+ }
+
+ /* XXX All this stuff is only valid for async. */
+ switch (xfer->device->speed) {
+ case USB_SPEED_LOW:
+ speed = EHCI_QH_SPEED_LOW;
+ break;
+ case USB_SPEED_FULL:
+ speed = EHCI_QH_SPEED_FULL;
+ break;
+ case USB_SPEED_HIGH:
+ speed = EHCI_QH_SPEED_HIGH;
+ break;
+ default:
+ panic("ehci_open: bad device speed %d", xfer->device->speed);
+ }
+
+ sqh = ehci_alloc_sqh(sc);
+ if (sqh == NULL)
+ return (ENOMEM);
+
+ /* qh_link filled when the QH is added */
+ sqh->qh.qh_endp = htole32(
+ EHCI_QH_SET_ADDR(xfer->device->address) |
+ EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
+ EHCI_QH_SET_EPS(speed) |
+ (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(8)
+ );
+ sqh->qh.qh_endphub = htole32(
+ EHCI_QH_SET_MULT(1) |
+ EHCI_QH_SET_HUBA(hshubaddr) |
+ EHCI_QH_SET_PORT(hshubport) |
+ EHCI_QH_SET_CMASK(0x1c) | /* XXX */
+ EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0)
+ );
+ sqh->qh.qh_curqtd = htole32(EHCI_LINK_TERMINATE);
+
+ /* Fill the overlay qTD */
+ sqh->qh.qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE);
+ sqh->qh.qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE);
+ sqh->qh.qh_qtd.qtd_status =
+ htole32(EHCI_QTD_SET_TOGGLE(xfer->pipe->endpoint->savedtoggle));
+
+ usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ ex->sqh = sqh;
+
+ return (0);
}
/*
@@ -1628,16 +1597,20 @@ ehci_rem_qh(struct ehci_softc *sc, struc
usb_syncmem(&sqh->prev->dma,
sqh->prev->offs + offsetof(struct ehci_qh, qh_link),
sizeof(sqh->prev->qh.qh_link), BUS_DMASYNC_PREWRITE);
-
- ehci_sync_hc(sc);
}
void
-ehci_set_qh_qtd(struct ehci_soft_qh *sqh, struct ehci_soft_qtd *sqtd)
+ehci_insert_qh(struct ehci_softc *sc, struct usbd_xfer *xfer,
+ struct ehci_soft_qtd *sqtd)
{
- int i;
+ struct ehci_xfer *ex = (struct ehci_xfer *)xfer;
+ usb_endpoint_descriptor_t *ed = xfer->pipe->endpoint->edesc;
+ struct ehci_soft_qh *sqh = ex->sqh;
+ int i, ival;
u_int32_t status;
+ SPLUSBCHECK;
+
/* Save toggle bit and ping status. */
usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
@@ -1665,6 +1638,23 @@ ehci_set_qh_qtd(struct ehci_soft_qh *sqh
sqh->offs + offsetof(struct ehci_qh, qh_qtd.qtd_status),
sizeof(sqh->qh.qh_qtd.qtd_status),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+ case UE_CONTROL:
+ case UE_BULK:
+ ehci_add_qh(sqh, sc->sc_async_head);
+ break;
+ case UE_INTERRUPT:
+ ival = xfer->pipe->interval;
+ if (ival == USBD_DEFAULT_INTERVAL)
+ ival = ed->bInterval;
+ ehci_device_setintr(sc, sqh, ival);
+ break;
+ default:
+ panic("wrong xfer type");
+ }
+
+ ehci_add_intr_list(sc, ex);
}
/*
@@ -2350,13 +2340,15 @@ out:
}
void
-ehci_free_sqh(struct ehci_softc *sc, struct ehci_soft_qh *sqh)
+ehci_free_sqh(struct ehci_softc *sc, struct usbd_xfer *xfer)
{
+ struct ehci_xfer *ex = (struct ehci_xfer *)xfer;
int s;
s = splusb();
- sqh->next = sc->sc_freeqhs;
- sc->sc_freeqhs = sqh;
+ ex->sqh->next = sc->sc_freeqhs;
+ sc->sc_freeqhs = ex->sqh;
+ ex->sqh = NULL;
splx(s);
}
@@ -2408,7 +2400,7 @@ ehci_free_sqtd(struct ehci_softc *sc, st
splx(s);
}
-usbd_status
+int
ehci_alloc_sqtd_chain(struct ehci_softc *sc, u_int alen, struct usbd_xfer
*xfer,
struct ehci_soft_qtd **sp, struct ehci_soft_qtd **ep)
{
@@ -2434,6 +2426,7 @@ ehci_alloc_sqtd_chain(struct ehci_softc
mps = UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize);
forceshort = ((xfer->flags & USBD_FORCE_SHORT_XFER) || len == 0) &&
len % mps == 0;
+
/*
* The control transfer data stage always starts with a toggle of 1.
* For other transfers we let the hardware track the toggle state.
@@ -2548,18 +2541,16 @@ ehci_alloc_sqtd_chain(struct ehci_softc
DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n",
*sp, *ep));
- return (USBD_NORMAL_COMPLETION);
+ return (0);
nomem:
/* XXX free chain */
- DPRINTFN(-1,("ehci_alloc_sqtd_chain: no memory\n"));
- return (USBD_NOMEM);
+ return (ENOMEM);
}
void
ehci_free_sqtd_chain(struct ehci_softc *sc, struct ehci_xfer *ex)
{
- struct ehci_pipe *epipe = (struct ehci_pipe *)ex->xfer.pipe;
struct ehci_soft_qtd *sqtd, *next;
DPRINTFN(10,("ehci_free_sqtd_chain: sqtd=%p\n", ex->sqtdstart));
@@ -2569,7 +2560,8 @@ ehci_free_sqtd_chain(struct ehci_softc *
ehci_free_sqtd(sc, sqtd);
}
ex->sqtdstart = ex->sqtdend = NULL;
- epipe->sqh->sqtd = NULL;
+ ex->sqh->sqtd = NULL;
+ ex->sqh = NULL;
}
struct ehci_soft_itd *
@@ -2646,26 +2638,19 @@ ehci_free_itd(struct ehci_softc *sc, str
splx(s);
}
-/****************/
-
-/*
- * Close a reqular pipe.
- * Assumes that there are no pending transactions.
- */
void
ehci_close_pipe(struct usbd_pipe *pipe)
{
- struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
struct ehci_softc *sc = (struct ehci_softc *)pipe->device->bus;
- struct ehci_soft_qh *sqh = epipe->sqh;
- int s;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
- s = splusb();
- ehci_rem_qh(sc, sqh);
- splx(s);
- pipe->endpoint->savedtoggle =
- EHCI_QTD_GET_TOGGLE(letoh32(sqh->qh.qh_qtd.qtd_status));
- ehci_free_sqh(sc, epipe->sqh);
+ /* Root Hub */
+ if (pipe->device->depth == 0)
+ return;
+
+ if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_CONTROL)
+ usb_freemem(&sc->sc_bus, &epipe->u.ctl.reqdma);
}
/*
@@ -2682,13 +2667,10 @@ void
ehci_abort_xfer(struct usbd_xfer *xfer, usbd_status status)
{
struct ehci_softc *sc = (struct ehci_softc *)xfer->device->bus;
- struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
struct ehci_xfer *ex = (struct ehci_xfer*)xfer;
- struct ehci_soft_qh *sqh = epipe->sqh;
- struct ehci_soft_qtd *sqtd;
int s;
- DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));
+ DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, xfer->pipe));
if (sc->sc_bus.dying) {
/* If we're dying, just do the software part. */
@@ -2725,57 +2707,25 @@ ehci_abort_xfer(struct usbd_xfer *xfer,
return;
}
- /*
- * Step 1: Make interrupt routine and timeouts ignore xfer.
- */
+#ifdef DIAGNOSTIC
+ ex->isdone = 1;
+#endif
s = splusb();
ex->ehci_xfer_flags |= EHCI_XFER_ABORTING;
xfer->status = status; /* make software ignore it */
+
timeout_del(&xfer->timeout_handle);
usb_rem_task(xfer->device, &xfer->abort_task);
- splx(s);
+ ehci_rem_qh(sc, ex->sqh);
- /*
- * Step 2: Deactivate all of the qTDs that we will be removing,
- * otherwise the queue head may go active again.
- */
- usb_syncmem(&sqh->dma,
- sqh->offs + offsetof(struct ehci_qh, qh_qtd.qtd_status),
- sizeof(sqh->qh.qh_qtd.qtd_status),
- BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED);
- usb_syncmem(&sqh->dma,
- sqh->offs + offsetof(struct ehci_qh, qh_qtd.qtd_status),
- sizeof(sqh->qh.qh_qtd.qtd_status),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
-
- for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) {
- usb_syncmem(&sqtd->dma,
- sqtd->offs + offsetof(struct ehci_qtd, qtd_status),
- sizeof(sqtd->qtd.qtd_status),
- BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- sqtd->qtd.qtd_status = htole32(EHCI_QTD_HALTED);
- usb_syncmem(&sqtd->dma,
- sqtd->offs + offsetof(struct ehci_qtd, qtd_status),
- sizeof(sqtd->qtd.qtd_status),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- }
+ /* Issue a handshake to ensure we can free the tranfer's DMA memory. */
ehci_sync_hc(sc);
- /*
- * 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.
- */
- s = splusb();
- sc->sc_softwake = 1;
- usb_schedsoftintr(&sc->sc_bus);
- tsleep(&sc->sc_softwake, PZERO, "ehciab", 0);
+ usb_syncmem(&ex->sqh->dma, ex->sqh->offs, sizeof(ex->sqh->qh),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ xfer->pipe->endpoint->savedtoggle =
+ EHCI_QTD_GET_TOGGLE(letoh32(ex->sqh->qh.qh_qtd.qtd_status));
-#ifdef DIAGNOSTIC
- ex->isdone = 1;
-#endif
/* Do the wakeup first to avoid touching the xfer after the callback. */
ex->ehci_xfer_flags &= ~EHCI_XFER_ABORTING;
if (ex->ehci_xfer_flags & EHCI_XFER_ABORTWAIT) {
@@ -3002,7 +2952,6 @@ ehci_device_request(struct usbd_xfer *xf
struct ehci_xfer *ex = (struct ehci_xfer *)xfer;
usb_device_request_t *req = &xfer->request;
struct ehci_soft_qtd *setup, *stat, *next;
- struct ehci_soft_qh *sqh;
u_int len = UGETW(req->wLength);
usbd_status err;
int s;
@@ -3013,6 +2962,9 @@ ehci_device_request(struct usbd_xfer *xf
UGETW(req->wIndex), UGETW(req->wLength), xfer->device->address,
xfer->pipe->endpoint->edesc->bEndpointAddress));
+ if (ehci_setup_sqh(sc, xfer))
+ return (USBD_NOMEM);
+
setup = ehci_alloc_sqtd(sc);
if (setup == NULL) {
err = USBD_NOMEM;
@@ -3024,15 +2976,19 @@ ehci_device_request(struct usbd_xfer *xf
goto bad2;
}
- sqh = epipe->sqh;
+ KASSERT(EHCI_QH_GET_ADDR(letoh32(ex->sqh->qh.qh_endp)) ==
+ xfer->device->address);
+ KASSERT(EHCI_QH_GET_MPL(letoh32(ex->sqh->qh.qh_endp)) ==
+ UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize));
/* Set up data transaction */
if (len != 0) {
struct ehci_soft_qtd *end;
- err = ehci_alloc_sqtd_chain(sc, len, xfer, &next, &end);
- if (err)
+ if (ehci_alloc_sqtd_chain(sc, len, xfer, &next, &end)) {
+ err = USBD_NOMEM;
goto bad3;
+ }
end->qtd.qtd_status &= htole32(~EHCI_QTD_IOC);
end->nextqtd = stat;
end->qtd.qtd_next =
@@ -3079,7 +3035,7 @@ ehci_device_request(struct usbd_xfer *xf
#ifdef EHCI_DEBUG
if (ehcidebug > 5) {
DPRINTF(("ehci_device_request:\n"));
- ehci_dump_sqh(sqh);
+ ehci_dump_sqh(ex->sqh);
ehci_dump_sqtds(setup);
}
#endif
@@ -3087,22 +3043,18 @@ ehci_device_request(struct usbd_xfer *xf
ex->sqtdstart = setup;
ex->sqtdend = stat;
#ifdef DIAGNOSTIC
- if (!ex->isdone) {
- printf("ehci_device_request: not done, ex=%p\n", ex);
- }
ex->isdone = 0;
#endif
- /* Insert qTD in QH list. */
+ xfer->status = USBD_IN_PROGRESS;
+
s = splusb();
- ehci_set_qh_qtd(sqh, setup);
+ ehci_insert_qh(sc, xfer, setup);
if (xfer->timeout && !sc->sc_bus.use_polling) {
timeout_del(&xfer->timeout_handle);
timeout_set(&xfer->timeout_handle, ehci_timeout, xfer);
timeout_add_msec(&xfer->timeout_handle, xfer->timeout);
}
- ehci_add_intr_list(sc, ex);
- xfer->status = USBD_IN_PROGRESS;
splx(s);
#ifdef EHCI_DEBUG
@@ -3112,7 +3064,7 @@ ehci_device_request(struct usbd_xfer *xf
delay(10000);
ehci_dump_regs(sc);
ehci_dump_sqh(sc->sc_async_head);
- ehci_dump_sqh(sqh);
+ ehci_dump_sqh(ex->sqh);
ehci_dump_sqtds(setup);
}
#endif
@@ -3125,6 +3077,7 @@ ehci_device_request(struct usbd_xfer *xf
ehci_free_sqtd(sc, setup);
bad1:
DPRINTFN(-1,("ehci_device_request: no memory\n"));
+ ehci_free_sqh(sc, xfer);
xfer->status = err;
usb_transfer_complete(xfer);
return (err);
@@ -3150,11 +3103,8 @@ usbd_status
ehci_device_bulk_start(struct usbd_xfer *xfer)
{
struct ehci_softc *sc = (struct ehci_softc *)xfer->device->bus;
- struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
struct ehci_xfer *ex = (struct ehci_xfer *)xfer;
struct ehci_soft_qtd *data, *dataend;
- struct ehci_soft_qh *sqh;
- usbd_status err;
int s;
DPRINTFN(2, ("ehci_device_bulk_start: xfer=%p len=%u flags=%d\n",
@@ -3168,20 +3118,21 @@ ehci_device_bulk_start(struct usbd_xfer
panic("ehci_device_bulk_start: a request");
#endif
- sqh = epipe->sqh;
+ if (ehci_setup_sqh(sc, xfer))
+ return (USBD_NOMEM);
- err = ehci_alloc_sqtd_chain(sc, xfer->length, xfer, &data, &dataend);
- if (err) {
+ if (ehci_alloc_sqtd_chain(sc, xfer->length, xfer, &data, &dataend)) {
DPRINTFN(-1,("ehci_device_bulk_start: no memory\n"));
- xfer->status = err;
+ ehci_free_sqh(sc, xfer);
+ xfer->status = USBD_NOMEM;
usb_transfer_complete(xfer);
- return (err);
+ return (USBD_NOMEM);
}
#ifdef EHCI_DEBUG
if (ehcidebug > 5) {
DPRINTF(("ehci_device_bulk_start: data(1)\n"));
- ehci_dump_sqh(sqh);
+ ehci_dump_sqh(ex->sqh);
ehci_dump_sqtds(data);
}
#endif
@@ -3190,39 +3141,20 @@ ehci_device_bulk_start(struct usbd_xfer
ex->sqtdstart = data;
ex->sqtdend = dataend;
#ifdef DIAGNOSTIC
- if (!ex->isdone) {
- printf("ehci_device_bulk_start: not done, ex=%p\n", ex);
- }
ex->isdone = 0;
#endif
+ xfer->status = USBD_IN_PROGRESS;
+
s = splusb();
- ehci_set_qh_qtd(sqh, data);
+ ehci_insert_qh(sc, xfer, data);
if (xfer->timeout && !sc->sc_bus.use_polling) {
timeout_del(&xfer->timeout_handle);
timeout_set(&xfer->timeout_handle, ehci_timeout, xfer);
timeout_add_msec(&xfer->timeout_handle, xfer->timeout);
}
- ehci_add_intr_list(sc, ex);
- xfer->status = USBD_IN_PROGRESS;
splx(s);
-#ifdef EHCI_DEBUG
- if (ehcidebug > 10) {
- DPRINTF(("ehci_device_bulk_start: data(2)\n"));
- delay(10000);
- DPRINTF(("ehci_device_bulk_start: data(3)\n"));
- ehci_dump_regs(sc);
-#if 0
- printf("async_head:\n");
- ehci_dump_sqh(sc->sc_async_head);
-#endif
- printf("sqh:\n");
- ehci_dump_sqh(sqh);
- ehci_dump_sqtds(data);
- }
-#endif
-
if (sc->sc_bus.use_polling)
ehci_waitintr(sc, xfer);
@@ -3312,10 +3244,7 @@ ehci_device_intr_start(struct usbd_xfer
{
struct ehci_softc *sc = (struct ehci_softc *)xfer->device->bus;
struct ehci_xfer *ex = (struct ehci_xfer *)xfer;
- struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
struct ehci_soft_qtd *data, *dataend;
- struct ehci_soft_qh *sqh;
- usbd_status err;
int s;
DPRINTFN(2, ("ehci_device_intr_start: xfer=%p len=%u flags=%d\n",
@@ -3329,20 +3258,21 @@ ehci_device_intr_start(struct usbd_xfer
panic("ehci_device_intr_start: a request");
#endif
- sqh = epipe->sqh;
+ if (ehci_setup_sqh(sc, xfer))
+ return (USBD_NOMEM);
- err = ehci_alloc_sqtd_chain(sc, xfer->length, xfer, &data, &dataend);
- if (err) {
+ if (ehci_alloc_sqtd_chain(sc, xfer->length, xfer, &data, &dataend)) {
DPRINTFN(-1, ("ehci_device_intr_start: no memory\n"));
- xfer->status = err;
+ ehci_free_sqh(sc, xfer);
+ xfer->status = USBD_NOMEM;
usb_transfer_complete(xfer);
- return (err);
+ return (USBD_NOMEM);
}
#ifdef EHCI_DEBUG
if (ehcidebug > 5) {
DPRINTF(("ehci_device_intr_start: data(1)\n"));
- ehci_dump_sqh(sqh);
+ ehci_dump_sqh(ex->sqh);
ehci_dump_sqtds(data);
}
#endif
@@ -3356,15 +3286,15 @@ ehci_device_intr_start(struct usbd_xfer
ex->isdone = 0;
#endif
+ xfer->status = USBD_IN_PROGRESS;
+
s = splusb();
- ehci_set_qh_qtd(sqh, data);
+ ehci_insert_qh(sc, xfer, data);
if (xfer->timeout && !sc->sc_bus.use_polling) {
timeout_del(&xfer->timeout_handle);
timeout_set(&xfer->timeout_handle, ehci_timeout, xfer);
timeout_add_msec(&xfer->timeout_handle, xfer->timeout);
}
- ehci_add_intr_list(sc, ex);
- xfer->status = USBD_IN_PROGRESS;
splx(s);
#ifdef EHCI_DEBUG
@@ -3374,7 +3304,7 @@ ehci_device_intr_start(struct usbd_xfer
DPRINTF(("ehci_device_intr_start: data(3)\n"));
ehci_dump_regs(sc);
printf("sqh:\n");
- ehci_dump_sqh(sqh);
+ ehci_dump_sqh(ex->sqh);
ehci_dump_sqtds(data);
}
#endif
@@ -3409,32 +3339,30 @@ void
ehci_device_intr_done(struct usbd_xfer *xfer)
{
struct ehci_softc *sc = (struct ehci_softc *)xfer->device->bus;
- struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
struct ehci_xfer *ex = (struct ehci_xfer *)xfer;
struct ehci_soft_qtd *data, *dataend;
- struct ehci_soft_qh *sqh;
- usbd_status err;
int s;
- DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n",
- xfer, xfer->actlen));
-
- if (xfer->pipe->repeat) {
+ if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
+ ehci_del_intr_list(sc, ex); /* remove from active list */
ehci_free_sqtd_chain(sc, ex);
-
usb_syncmem(&xfer->dmabuf, 0, xfer->length,
usbd_xfer_isread(xfer) ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
- sqh = epipe->sqh;
+ }
- err = ehci_alloc_sqtd_chain(sc, xfer->length, xfer, &data,
&dataend);
- if (err) {
- DPRINTFN(-1, ("ehci_device_intr_done: no memory\n"));
- xfer->status = err;
+ if (xfer->pipe->repeat) {
+ if (ehci_setup_sqh(sc, xfer)) {
+ xfer->status = USBD_NOMEM;
+ return;
+ }
+
+ if (ehci_alloc_sqtd_chain(sc, xfer->length, xfer, &data,
+ &dataend)) {
+ xfer->status = USBD_NOMEM;
return;
}
- /* Set up interrupt info. */
ex->sqtdstart = data;
ex->sqtdend = dataend;
#ifdef DIAGNOSTIC
@@ -3445,22 +3373,16 @@ ehci_device_intr_done(struct usbd_xfer *
ex->isdone = 0;
#endif
+ xfer->status = USBD_IN_PROGRESS;
+
s = splusb();
- ehci_set_qh_qtd(sqh, data);
+ ehci_insert_qh(sc, xfer, data);
if (xfer->timeout && !sc->sc_bus.use_polling) {
timeout_del(&xfer->timeout_handle);
timeout_set(&xfer->timeout_handle, ehci_timeout, xfer);
timeout_add_msec(&xfer->timeout_handle, xfer->timeout);
}
splx(s);
-
- xfer->status = USBD_IN_PROGRESS;
- } else if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
- ehci_del_intr_list(sc, ex); /* remove from active list */
- ehci_free_sqtd_chain(sc, ex);
- usb_syncmem(&xfer->dmabuf, 0, xfer->length,
- usbd_xfer_isread(xfer) ?
- BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
}
}
Index: ehcivar.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/ehcivar.h,v
retrieving revision 1.32
diff -u -p -r1.32 ehcivar.h
--- ehcivar.h 16 May 2014 18:17:03 -0000 1.32
+++ ehcivar.h 9 Aug 2014 09:57:26 -0000
@@ -79,6 +79,7 @@ struct ehci_soft_itd {
struct ehci_xfer {
struct usbd_xfer xfer;
TAILQ_ENTRY(ehci_xfer) inext; /* list of active xfers */
+ struct ehci_soft_qh *sqh;
struct ehci_soft_qtd *sqtdstart;
struct ehci_soft_qtd *sqtdend;
struct ehci_soft_itd *itdstart;