Attempt to follow the procedure described in section 4.10 of the
EHCI spec for linking in new qTDs into an asynchronous QH. This requires that there is a qTD marked as not active and not halted at the start of the QH's list, and the hardware will know to re-fetch the qTD on each pass rather than just looking at the overlay qTD: "The host controller must be able to advance the queue from the Fetch QH state in order to avoid all hardware/software race conditions. This simple mechanism allows software to simply link qTDs to the queue head and activate them, then the host controller will always find them if/when they are reachable." This is achieved by keeping an "inactivesqtd" entry on the QH list, and re-using it each time as the start of the next transfer, and allocating a new qTD to become the next inactivesqtd. Then a new transfer can be activated by just setting its "active" flag, which avoids all the previous messing with overlay qTD state in ehci_set_qh_qtd().
This commit is contained in:
parent
1f03f1f3b1
commit
72d76006ec
@ -211,9 +211,10 @@ Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *);
|
||||
Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *);
|
||||
Static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *,
|
||||
ehci_softc_t *, int, int, usbd_xfer_handle,
|
||||
ehci_soft_qtd_t *, ehci_soft_qtd_t *,
|
||||
ehci_soft_qtd_t **, ehci_soft_qtd_t **);
|
||||
Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *,
|
||||
ehci_soft_qtd_t *);
|
||||
Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qh_t *,
|
||||
ehci_soft_qtd_t *, ehci_soft_qtd_t *);
|
||||
|
||||
Static usbd_status ehci_device_request(usbd_xfer_handle xfer);
|
||||
|
||||
@ -223,7 +224,7 @@ Static usbd_status ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *,
|
||||
Static void ehci_add_qh(ehci_soft_qh_t *, ehci_soft_qh_t *);
|
||||
Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *,
|
||||
ehci_soft_qh_t *);
|
||||
Static void ehci_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_t *);
|
||||
Static void ehci_activate_qh(ehci_soft_qh_t *, ehci_soft_qtd_t *);
|
||||
Static void ehci_sync_hc(ehci_softc_t *);
|
||||
|
||||
Static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *);
|
||||
@ -449,7 +450,6 @@ ehci_init(ehci_softc_t *sc)
|
||||
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++) {
|
||||
@ -475,8 +475,7 @@ ehci_init(ehci_softc_t *sc)
|
||||
/* 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;
|
||||
sqh->qh.qh_qtd.qtd_status = htole32(0);
|
||||
#ifdef EHCI_DEBUG
|
||||
if (ehcidebug) {
|
||||
ehci_dump_sqh(sqh);
|
||||
@ -762,11 +761,10 @@ void
|
||||
ehci_idone(struct ehci_xfer *ex)
|
||||
{
|
||||
usbd_xfer_handle xfer = &ex->xfer;
|
||||
#ifdef USB_DEBUG
|
||||
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
|
||||
#endif
|
||||
ehci_soft_qtd_t *sqtd, *lsqtd;
|
||||
u_int32_t status = 0, nstatus = 0;
|
||||
ehci_physaddr_t nextphys, altnextphys;
|
||||
int actlen, cerr;
|
||||
|
||||
DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex));
|
||||
@ -800,6 +798,30 @@ ehci_idone(struct ehci_xfer *ex)
|
||||
ehci_dump_sqtds(ex->sqtdstart);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Make sure that the QH overlay qTD does not reference any
|
||||
* of the qTDs we are about to free. This is probably only
|
||||
* necessary if the transfer is marked as HALTED.
|
||||
*/
|
||||
nextphys = EHCI_LINK_ADDR(le32toh(epipe->sqh->qh.qh_qtd.qtd_next));
|
||||
altnextphys =
|
||||
EHCI_LINK_ADDR(le32toh(epipe->sqh->qh.qh_qtd.qtd_altnext));
|
||||
for (sqtd = ex->sqtdstart; sqtd != ex->sqtdend->nextqtd;
|
||||
sqtd = sqtd->nextqtd) {
|
||||
if (sqtd->physaddr == nextphys) {
|
||||
epipe->sqh->qh.qh_qtd.qtd_next =
|
||||
htole32(ex->sqtdend->nextqtd->physaddr);
|
||||
DPRINTFN(4, ("ehci_idone: updated overlay next ptr\n"));
|
||||
|
||||
}
|
||||
if (sqtd->physaddr == altnextphys) {
|
||||
DPRINTFN(4,
|
||||
("ehci_idone: updated overlay altnext ptr\n"));
|
||||
epipe->sqh->qh.qh_qtd.qtd_altnext =
|
||||
htole32(ex->sqtdend->nextqtd->physaddr);
|
||||
}
|
||||
}
|
||||
|
||||
/* The transfer is done, compute actual length and status. */
|
||||
lsqtd = ex->sqtdend;
|
||||
actlen = 0;
|
||||
@ -810,8 +832,7 @@ ehci_idone(struct ehci_xfer *ex)
|
||||
|
||||
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 == lsqtd && 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);
|
||||
@ -1310,6 +1331,7 @@ ehci_dump_sqh(ehci_soft_qh_t *sqh)
|
||||
u_int32_t endp, endphub;
|
||||
|
||||
printf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr);
|
||||
printf(" sqtd=%p inactivesqtd=%p\n", sqh->sqtd, sqh->inactivesqtd);
|
||||
printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n");
|
||||
endp = le32toh(qh->qh_endp);
|
||||
printf(" endp=0x%08x\n", endp);
|
||||
@ -1422,9 +1444,7 @@ ehci_open(usbd_pipe_handle pipe)
|
||||
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;
|
||||
/* The overlay qTD was already set up by ehci_alloc_sqh(). */
|
||||
sqh->qh.qh_qtd.qtd_status =
|
||||
htole32(EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle));
|
||||
|
||||
@ -1513,27 +1533,38 @@ ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
|
||||
ehci_sync_hc(sc);
|
||||
}
|
||||
|
||||
/* Restart a QH following the addition of a qTD. */
|
||||
void
|
||||
ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd)
|
||||
ehci_activate_qh(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd)
|
||||
{
|
||||
int i;
|
||||
u_int32_t status;
|
||||
KASSERT((sqtd->qtd.qtd_status & htole32(EHCI_QTD_ACTIVE)) == 0,
|
||||
("ehci_activate_qh: already active"));
|
||||
|
||||
/* 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;
|
||||
/* Set !HALTED && !ACTIVE to start execution, preserve some fields */
|
||||
sqh->qh.qh_qtd.qtd_status = status;
|
||||
/*
|
||||
* When a QH is idle, the overlay qTD should be marked as not
|
||||
* halted and not active. This causes the host controller to
|
||||
* retrieve the real qTD on each pass (rather than just examinig
|
||||
* the overlay), so it will notice when we activate the qTD.
|
||||
*/
|
||||
if (sqtd == sqh->sqtd) {
|
||||
/* Check that the hardware is in the state we expect. */
|
||||
if (EHCI_LINK_ADDR(le32toh(sqh->qh.qh_qtd.qtd_next)) !=
|
||||
sqtd->physaddr) {
|
||||
#ifdef EHCI_DEBUG
|
||||
printf("ehci_activate_qh: unexpected next ptr\n");
|
||||
ehci_dump_sqh(sqh);
|
||||
ehci_dump_sqtds(sqh->sqtd);
|
||||
#endif
|
||||
sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr);
|
||||
sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
|
||||
}
|
||||
/* Ensure the flags are correct. */
|
||||
sqh->qh.qh_qtd.qtd_status &= htole32(EHCI_QTD_PINGSTATE |
|
||||
EHCI_QTD_TOGGLE_MASK);
|
||||
}
|
||||
|
||||
/* Now activate the qTD. */
|
||||
sqtd->qtd.qtd_status |= htole32(EHCI_QTD_ACTIVE);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2183,6 +2214,7 @@ ehci_soft_qh_t *
|
||||
ehci_alloc_sqh(ehci_softc_t *sc)
|
||||
{
|
||||
ehci_soft_qh_t *sqh;
|
||||
ehci_soft_qtd_t *sqtd;
|
||||
usbd_status err;
|
||||
int i, offs;
|
||||
usb_dma_t dma;
|
||||
@ -2205,17 +2237,36 @@ ehci_alloc_sqh(ehci_softc_t *sc)
|
||||
sc->sc_freeqhs = sqh;
|
||||
}
|
||||
}
|
||||
/* Allocate the initial inactive sqtd. */
|
||||
sqtd = ehci_alloc_sqtd(sc);
|
||||
if (sqtd == NULL)
|
||||
return (NULL);
|
||||
sqtd->qtd.qtd_status = htole32(0);
|
||||
sqtd->qtd.qtd_next = EHCI_NULL;
|
||||
sqtd->qtd.qtd_altnext = EHCI_NULL;
|
||||
|
||||
sqh = sc->sc_freeqhs;
|
||||
sc->sc_freeqhs = sqh->next;
|
||||
memset(&sqh->qh, 0, sizeof(ehci_qh_t));
|
||||
|
||||
/* The overlay QTD should begin zeroed. */
|
||||
sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr);
|
||||
sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
|
||||
sqh->qh.qh_qtd.qtd_status = 0;
|
||||
for (i = 0; i < EHCI_QTD_NBUFFERS; i++) {
|
||||
sqh->qh.qh_qtd.qtd_buffer[i] = 0;
|
||||
sqh->qh.qh_qtd.qtd_buffer_hi[i] = 0;
|
||||
}
|
||||
sqh->next = NULL;
|
||||
sqh->prev = NULL;
|
||||
sqh->sqtd = sqtd;
|
||||
sqh->inactivesqtd = sqtd;
|
||||
return (sqh);
|
||||
}
|
||||
|
||||
void
|
||||
ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh)
|
||||
{
|
||||
ehci_free_sqtd(sc, sqh->inactivesqtd);
|
||||
sqh->next = sc->sc_freeqhs;
|
||||
sc->sc_freeqhs = sqh;
|
||||
}
|
||||
@ -2253,7 +2304,13 @@ ehci_alloc_sqtd(ehci_softc_t *sc)
|
||||
s = splusb();
|
||||
sqtd = sc->sc_freeqtds;
|
||||
sc->sc_freeqtds = sqtd->nextqtd;
|
||||
memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t));
|
||||
sqtd->qtd.qtd_next = EHCI_NULL;
|
||||
sqtd->qtd.qtd_altnext = EHCI_NULL;
|
||||
sqtd->qtd.qtd_status = 0;
|
||||
for (i = 0; i < EHCI_QTD_NBUFFERS; i++) {
|
||||
sqtd->qtd.qtd_buffer[i] = 0;
|
||||
sqtd->qtd.qtd_buffer_hi[i] = 0;
|
||||
}
|
||||
sqtd->nextqtd = NULL;
|
||||
sqtd->xfer = NULL;
|
||||
splx(s);
|
||||
@ -2274,8 +2331,8 @@ ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
|
||||
|
||||
usbd_status
|
||||
ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
|
||||
int alen, int rd, usbd_xfer_handle xfer,
|
||||
ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep)
|
||||
int alen, int rd, usbd_xfer_handle xfer, ehci_soft_qtd_t *start,
|
||||
ehci_soft_qtd_t *newinactive, ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep)
|
||||
{
|
||||
ehci_soft_qtd_t *next, *cur;
|
||||
ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys;
|
||||
@ -2306,10 +2363,20 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
|
||||
if (iscontrol)
|
||||
qtdstatus |= EHCI_QTD_SET_TOGGLE(1);
|
||||
|
||||
cur = ehci_alloc_sqtd(sc);
|
||||
*sp = cur;
|
||||
if (cur == NULL)
|
||||
goto nomem;
|
||||
if (start != NULL) {
|
||||
/*
|
||||
* If we are given a starting qTD, assume it is linked into
|
||||
* an active QH so be careful not to mark it active.
|
||||
*/
|
||||
cur = start;
|
||||
*sp = cur;
|
||||
qtdstatus &= ~EHCI_QTD_ACTIVE;
|
||||
} else {
|
||||
cur = ehci_alloc_sqtd(sc);
|
||||
*sp = cur;
|
||||
if (cur == NULL)
|
||||
goto nomem;
|
||||
}
|
||||
for (;;) {
|
||||
dataphyspage = EHCI_PAGE(dataphys);
|
||||
/* The EHCI hardware can handle at most 5 pages. */
|
||||
@ -2390,7 +2457,9 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
|
||||
#endif
|
||||
}
|
||||
cur->nextqtd = next;
|
||||
cur->qtd.qtd_next = cur->qtd.qtd_altnext = nextphys;
|
||||
cur->qtd.qtd_next = nextphys;
|
||||
/* Make sure to stop after a short transfer. */
|
||||
cur->qtd.qtd_altnext = htole32(newinactive->physaddr);
|
||||
cur->qtd.qtd_status =
|
||||
htole32(qtdstatus | EHCI_QTD_SET_BYTES(curlen));
|
||||
cur->xfer = xfer;
|
||||
@ -2407,6 +2476,7 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
|
||||
}
|
||||
if (len == 0)
|
||||
break;
|
||||
qtdstatus |= EHCI_QTD_ACTIVE;
|
||||
DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n"));
|
||||
offset += curlen;
|
||||
dataphys = DMAADDR(dma, offset);
|
||||
@ -2426,16 +2496,27 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
|
||||
return (USBD_NOMEM);
|
||||
}
|
||||
|
||||
/* Free the chain starting at sqtd and end at the qTD before sqtdend */
|
||||
Static void
|
||||
ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd,
|
||||
ehci_soft_qtd_t *sqtdend)
|
||||
ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qh_t *sqh,
|
||||
ehci_soft_qtd_t *sqtd, ehci_soft_qtd_t *sqtdend)
|
||||
{
|
||||
ehci_soft_qtd_t *p;
|
||||
ehci_soft_qtd_t *p, **prevp;
|
||||
int i;
|
||||
|
||||
DPRINTFN(10,("ehci_free_sqtd_chain: sqtd=%p sqtdend=%p\n",
|
||||
sqtd, sqtdend));
|
||||
|
||||
/* First unlink the chain from the QH's software qTD list. */
|
||||
prevp = &sqh->sqtd;
|
||||
for (p = sqh->sqtd; p != NULL; p = p->nextqtd) {
|
||||
if (p == sqtd) {
|
||||
*prevp = sqtdend;
|
||||
break;
|
||||
}
|
||||
prevp = &p->nextqtd;
|
||||
}
|
||||
KASSERT(p != NULL, ("ehci_free_sqtd_chain: chain not found"));
|
||||
for (i = 0; sqtd != sqtdend; sqtd = p, i++) {
|
||||
p = sqtd->nextqtd;
|
||||
ehci_free_sqtd(sc, sqtd);
|
||||
@ -2481,10 +2562,10 @@ 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, *snext, **psqtd;
|
||||
ehci_soft_qtd_t *sqtd, *snext;
|
||||
ehci_physaddr_t cur, us, next;
|
||||
int s;
|
||||
int hit;
|
||||
int hit, i;
|
||||
/* int count = 0; */
|
||||
ehci_soft_qh_t *psqh;
|
||||
|
||||
@ -2577,13 +2658,12 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
||||
|
||||
/* We will change them to point here */
|
||||
snext = exfer->sqtdend->nextqtd;
|
||||
next = snext ? htole32(snext->physaddr) : EHCI_NULL;
|
||||
next = htole32(snext->physaddr);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
@ -2591,11 +2671,8 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
||||
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.
|
||||
@ -2608,7 +2685,6 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
||||
* (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++;
|
||||
@ -2622,17 +2698,13 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
||||
* 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"));
|
||||
sqh->qh.qh_qtd.qtd_next = htole32(snext->physaddr);
|
||||
sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
|
||||
sqh->qh.qh_qtd.qtd_status &=
|
||||
htole32(EHCI_QTD_TOGGLE_MASK);
|
||||
for (i = 0; i < EHCI_QTD_NBUFFERS; i++) {
|
||||
sqh->qh.qh_qtd.qtd_buffer[i] = 0;
|
||||
sqh->qh.qh_qtd.qtd_buffer_hi[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2761,7 +2833,7 @@ ehci_device_ctrl_done(usbd_xfer_handle xfer)
|
||||
{
|
||||
struct ehci_xfer *ex = EXFER(xfer);
|
||||
ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
|
||||
/*struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;*/
|
||||
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
|
||||
|
||||
DPRINTFN(10,("ehci_ctrl_done: xfer=%p\n", xfer));
|
||||
|
||||
@ -2773,7 +2845,8 @@ ehci_device_ctrl_done(usbd_xfer_handle xfer)
|
||||
|
||||
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);
|
||||
ehci_free_sqtd_chain(sc, epipe->sqh, ex->sqtdstart,
|
||||
ex->sqtdend->nextqtd);
|
||||
}
|
||||
|
||||
DPRINTFN(5, ("ehci_ctrl_done: length=%d\n", xfer->actlen));
|
||||
@ -2807,7 +2880,7 @@ ehci_device_request(usbd_xfer_handle xfer)
|
||||
usbd_device_handle dev = epipe->pipe.device;
|
||||
ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
|
||||
int addr = dev->address;
|
||||
ehci_soft_qtd_t *setup, *stat, *next;
|
||||
ehci_soft_qtd_t *newinactive, *setup, *stat, *next;
|
||||
ehci_soft_qh_t *sqh;
|
||||
int isread;
|
||||
int len;
|
||||
@ -2823,11 +2896,14 @@ ehci_device_request(usbd_xfer_handle xfer)
|
||||
UGETW(req->wIndex), len, addr,
|
||||
epipe->pipe.endpoint->edesc->bEndpointAddress));
|
||||
|
||||
setup = ehci_alloc_sqtd(sc);
|
||||
if (setup == NULL) {
|
||||
newinactive = ehci_alloc_sqtd(sc);
|
||||
if (newinactive == NULL) {
|
||||
err = USBD_NOMEM;
|
||||
goto bad1;
|
||||
}
|
||||
newinactive->qtd.qtd_status = htole32(0);
|
||||
newinactive->qtd.qtd_next = EHCI_NULL;
|
||||
newinactive->qtd.qtd_altnext = EHCI_NULL;
|
||||
stat = ehci_alloc_sqtd(sc);
|
||||
if (stat == NULL) {
|
||||
err = USBD_NOMEM;
|
||||
@ -2835,6 +2911,8 @@ ehci_device_request(usbd_xfer_handle xfer)
|
||||
}
|
||||
|
||||
sqh = epipe->sqh;
|
||||
setup = sqh->inactivesqtd;
|
||||
sqh->inactivesqtd = newinactive;
|
||||
epipe->u.ctl.length = len;
|
||||
|
||||
/* Update device address and length since they may have changed
|
||||
@ -2853,22 +2931,21 @@ ehci_device_request(usbd_xfer_handle xfer)
|
||||
ehci_soft_qtd_t *end;
|
||||
|
||||
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
|
||||
&next, &end);
|
||||
NULL, newinactive, &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);
|
||||
end->qtd.qtd_next = htole32(stat->physaddr);
|
||||
end->qtd.qtd_altnext = htole32(newinactive->physaddr);
|
||||
} else {
|
||||
next = stat;
|
||||
}
|
||||
|
||||
memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req);
|
||||
|
||||
/* Clear toggle */
|
||||
/* Clear toggle, and do not activate until complete */
|
||||
setup->qtd.qtd_status = htole32(
|
||||
EHCI_QTD_ACTIVE |
|
||||
EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
|
||||
EHCI_QTD_SET_CERR(3) |
|
||||
EHCI_QTD_SET_TOGGLE(0) |
|
||||
@ -2877,7 +2954,8 @@ ehci_device_request(usbd_xfer_handle xfer)
|
||||
setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0));
|
||||
setup->qtd.qtd_buffer_hi[0] = 0;
|
||||
setup->nextqtd = next;
|
||||
setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr);
|
||||
setup->qtd.qtd_next = htole32(next->physaddr);
|
||||
setup->qtd.qtd_altnext = htole32(newinactive->physaddr);
|
||||
setup->xfer = xfer;
|
||||
setup->len = sizeof *req;
|
||||
|
||||
@ -2890,8 +2968,9 @@ ehci_device_request(usbd_xfer_handle xfer)
|
||||
);
|
||||
stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */
|
||||
stat->qtd.qtd_buffer_hi[0] = 0; /* XXX not needed? */
|
||||
stat->nextqtd = NULL;
|
||||
stat->qtd.qtd_next = stat->qtd.qtd_altnext = EHCI_NULL;
|
||||
stat->nextqtd = newinactive;
|
||||
stat->qtd.qtd_next = htole32(newinactive->physaddr);
|
||||
stat->qtd.qtd_altnext = htole32(newinactive->physaddr);
|
||||
stat->xfer = xfer;
|
||||
stat->len = 0;
|
||||
|
||||
@ -2912,9 +2991,9 @@ ehci_device_request(usbd_xfer_handle xfer)
|
||||
exfer->isdone = 0;
|
||||
#endif
|
||||
|
||||
/* Insert qTD in QH list. */
|
||||
/* Activate the new qTD in the QH list. */
|
||||
s = splusb();
|
||||
ehci_set_qh_qtd(sqh, setup);
|
||||
ehci_activate_qh(sqh, setup);
|
||||
if (xfer->timeout && !sc->sc_bus.use_polling) {
|
||||
usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
|
||||
ehci_timeout, xfer);
|
||||
@ -2938,9 +3017,10 @@ ehci_device_request(usbd_xfer_handle xfer)
|
||||
return (USBD_NORMAL_COMPLETION);
|
||||
|
||||
bad3:
|
||||
sqh->inactivesqtd = setup;
|
||||
ehci_free_sqtd(sc, stat);
|
||||
bad2:
|
||||
ehci_free_sqtd(sc, setup);
|
||||
ehci_free_sqtd(sc, newinactive);
|
||||
bad1:
|
||||
DPRINTFN(-1,("ehci_device_request: no memory\n"));
|
||||
xfer->status = err;
|
||||
@ -2972,7 +3052,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
|
||||
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
|
||||
usbd_device_handle dev = epipe->pipe.device;
|
||||
ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
|
||||
ehci_soft_qtd_t *data, *dataend;
|
||||
ehci_soft_qtd_t *data, *dataend, *newinactive;
|
||||
ehci_soft_qh_t *sqh;
|
||||
usbd_status err;
|
||||
int len, isread, endpt;
|
||||
@ -2996,14 +3076,30 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
|
||||
|
||||
epipe->u.bulk.length = len;
|
||||
|
||||
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
|
||||
&dataend);
|
||||
if (err) {
|
||||
DPRINTFN(-1,("ehci_device_bulk_start: no memory\n"));
|
||||
newinactive = ehci_alloc_sqtd(sc);
|
||||
if (newinactive == NULL) {
|
||||
DPRINTFN(-1,("ehci_device_bulk_start: no sqtd memory\n"));
|
||||
err = USBD_NOMEM;
|
||||
xfer->status = err;
|
||||
usb_transfer_complete(xfer);
|
||||
return (err);
|
||||
}
|
||||
newinactive->qtd.qtd_status = htole32(0);
|
||||
newinactive->qtd.qtd_next = EHCI_NULL;
|
||||
newinactive->qtd.qtd_altnext = EHCI_NULL;
|
||||
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
|
||||
sqh->inactivesqtd, newinactive, &data, &dataend);
|
||||
if (err) {
|
||||
DPRINTFN(-1,("ehci_device_bulk_start: no memory\n"));
|
||||
ehci_free_sqtd(sc, newinactive);
|
||||
xfer->status = err;
|
||||
usb_transfer_complete(xfer);
|
||||
return (err);
|
||||
}
|
||||
dataend->nextqtd = newinactive;
|
||||
dataend->qtd.qtd_next = htole32(newinactive->physaddr);
|
||||
dataend->qtd.qtd_altnext = htole32(newinactive->physaddr);
|
||||
sqh->inactivesqtd = newinactive;
|
||||
|
||||
#ifdef EHCI_DEBUG
|
||||
if (ehcidebug > 5) {
|
||||
@ -3024,7 +3120,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
|
||||
#endif
|
||||
|
||||
s = splusb();
|
||||
ehci_set_qh_qtd(sqh, data);
|
||||
ehci_activate_qh(sqh, data);
|
||||
if (xfer->timeout && !sc->sc_bus.use_polling) {
|
||||
usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
|
||||
ehci_timeout, xfer);
|
||||
@ -3080,14 +3176,15 @@ ehci_device_bulk_done(usbd_xfer_handle xfer)
|
||||
{
|
||||
struct ehci_xfer *ex = EXFER(xfer);
|
||||
ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
|
||||
/*struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;*/
|
||||
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
|
||||
|
||||
DPRINTFN(10,("ehci_bulk_done: xfer=%p, actlen=%d\n",
|
||||
xfer, xfer->actlen));
|
||||
|
||||
if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
|
||||
ehci_del_intr_list(ex); /* remove from active list */
|
||||
ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL);
|
||||
ehci_free_sqtd_chain(sc, epipe->sqh, ex->sqtdstart,
|
||||
ex->sqtdend->nextqtd);
|
||||
}
|
||||
|
||||
DPRINTFN(5, ("ehci_bulk_done: length=%d\n", xfer->actlen));
|
||||
@ -3141,7 +3238,7 @@ ehci_device_intr_start(usbd_xfer_handle 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_qtd_t *data, *dataend, *newinactive;
|
||||
ehci_soft_qh_t *sqh;
|
||||
usbd_status err;
|
||||
int len, isread, endpt;
|
||||
@ -3165,14 +3262,29 @@ ehci_device_intr_start(usbd_xfer_handle xfer)
|
||||
|
||||
epipe->u.intr.length = len;
|
||||
|
||||
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
|
||||
&dataend);
|
||||
newinactive = ehci_alloc_sqtd(sc);
|
||||
if (newinactive == NULL) {
|
||||
DPRINTFN(-1,("ehci_device_intr_start: no sqtd memory\n"));
|
||||
err = USBD_NOMEM;
|
||||
xfer->status = err;
|
||||
usb_transfer_complete(xfer);
|
||||
return (err);
|
||||
}
|
||||
newinactive->qtd.qtd_status = htole32(0);
|
||||
newinactive->qtd.qtd_next = EHCI_NULL;
|
||||
newinactive->qtd.qtd_altnext = EHCI_NULL;
|
||||
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
|
||||
sqh->inactivesqtd, newinactive, &data, &dataend);
|
||||
if (err) {
|
||||
DPRINTFN(-1, ("ehci_device_intr_start: no memory\n"));
|
||||
xfer->status = err;
|
||||
usb_transfer_complete(xfer);
|
||||
return (err);
|
||||
}
|
||||
dataend->nextqtd = newinactive;
|
||||
dataend->qtd.qtd_next = htole32(newinactive->physaddr);
|
||||
dataend->qtd.qtd_altnext = htole32(newinactive->physaddr);
|
||||
sqh->inactivesqtd = newinactive;
|
||||
|
||||
#ifdef EHCI_DEBUG
|
||||
if (ehcidebug > 5) {
|
||||
@ -3193,7 +3305,7 @@ ehci_device_intr_start(usbd_xfer_handle xfer)
|
||||
#endif
|
||||
|
||||
s = splusb();
|
||||
ehci_set_qh_qtd(sqh, data);
|
||||
ehci_activate_qh(sqh, data);
|
||||
if (xfer->timeout && !sc->sc_bus.use_polling) {
|
||||
usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
|
||||
ehci_timeout, xfer);
|
||||
@ -3250,7 +3362,7 @@ ehci_device_intr_done(usbd_xfer_handle xfer)
|
||||
struct ehci_xfer *ex = EXFER(xfer);
|
||||
ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
|
||||
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
|
||||
ehci_soft_qtd_t *data, *dataend;
|
||||
ehci_soft_qtd_t *data, *dataend, *newinactive;
|
||||
ehci_soft_qh_t *sqh;
|
||||
usbd_status err;
|
||||
int len, isread, endpt, s;
|
||||
@ -3258,22 +3370,38 @@ ehci_device_intr_done(usbd_xfer_handle xfer)
|
||||
DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n",
|
||||
xfer, xfer->actlen));
|
||||
|
||||
sqh = epipe->sqh;
|
||||
if (xfer->pipe->repeat) {
|
||||
ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL);
|
||||
ehci_free_sqtd_chain(sc, sqh, ex->sqtdstart,
|
||||
ex->sqtdend->nextqtd);
|
||||
|
||||
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;
|
||||
|
||||
newinactive = ehci_alloc_sqtd(sc);
|
||||
if (newinactive == NULL) {
|
||||
DPRINTFN(-1,
|
||||
("ehci_device_intr_done: no sqtd memory\n"));
|
||||
err = USBD_NOMEM;
|
||||
xfer->status = err;
|
||||
return;
|
||||
}
|
||||
newinactive->qtd.qtd_status = htole32(0);
|
||||
newinactive->qtd.qtd_next = EHCI_NULL;
|
||||
newinactive->qtd.qtd_altnext = EHCI_NULL;
|
||||
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
|
||||
&data, &dataend);
|
||||
sqh->inactivesqtd, newinactive, &data, &dataend);
|
||||
if (err) {
|
||||
DPRINTFN(-1, ("ehci_device_intr_done: no memory\n"));
|
||||
xfer->status = err;
|
||||
return;
|
||||
}
|
||||
dataend->nextqtd = newinactive;
|
||||
dataend->qtd.qtd_next = htole32(newinactive->physaddr);
|
||||
dataend->qtd.qtd_altnext = htole32(newinactive->physaddr);
|
||||
sqh->inactivesqtd = newinactive;
|
||||
|
||||
/* Set up interrupt info. */
|
||||
exfer->sqtdstart = data;
|
||||
@ -3287,7 +3415,7 @@ ehci_device_intr_done(usbd_xfer_handle xfer)
|
||||
#endif
|
||||
|
||||
s = splusb();
|
||||
ehci_set_qh_qtd(sqh, data);
|
||||
ehci_activate_qh(sqh, data);
|
||||
if (xfer->timeout && !sc->sc_bus.use_polling) {
|
||||
usb_callout(xfer->timeout_handle,
|
||||
MS_TO_TICKS(xfer->timeout), ehci_timeout, xfer);
|
||||
@ -3297,7 +3425,8 @@ ehci_device_intr_done(usbd_xfer_handle xfer)
|
||||
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);
|
||||
ehci_free_sqtd_chain(sc, sqh, ex->sqtdstart,
|
||||
ex->sqtdend->nextqtd);
|
||||
}
|
||||
#undef exfer
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ typedef struct ehci_soft_qh {
|
||||
struct ehci_soft_qh *next;
|
||||
struct ehci_soft_qh *prev;
|
||||
struct ehci_soft_qtd *sqtd;
|
||||
struct ehci_soft_qtd *inactivesqtd;
|
||||
ehci_physaddr_t physaddr;
|
||||
int islot; /* Interrupt list slot. */
|
||||
} ehci_soft_qh_t;
|
||||
|
Loading…
Reference in New Issue
Block a user