📄 usbhcduhcilib.c
字号:
{ pTD_WRAPPER pTd; pUSB_BFR_LIST pBfrList; UINT32 nanosecondsAtStart; UINT32 ctlSts; UINT32 token; UINT16 actLen; BOOL underrun = FALSE; int s = PENDING; UINT16 i; /* If there is no workspace, this IRP has not been scheduled yet. */ if (pWork == NULL) return PENDING; /* Record the bus time already used by this transfer. */ nanosecondsAtStart = pWork->nanoseconds; /* Examine the "in use" TDs. If any are complete, return them to the * free pool for this IRP. If any completed with errors, indicate that * the IRP has failed. * * NOTE: Complete TDs will always be in front. If we encouter a TD * which is not complete, then we need not check any further TDs in the * list. If we detect an underrun on an IN transfer, then we continue * to process the TD list anyway, moving TDs from the in-use list to the * free list. */ while (s == PENDING && pWork->pTdInUse != NULL) { /* Examine the TDs status */ pTd = pWork->pTdInUse; DMA_INVALIDATE (pTd, sizeof (*pTd)); ctlSts = FROM_LITTLEL (pTd->td.ctlSts); if ((ctlSts & UHCI_TDCS_STS_ACTIVE) != 0 && !underrun) { /* TD is still active. Stop checking this list. */ break; } else { /* Release the bandwidth this TD was consuming. */ pWork->nanoseconds -= pTd->sw.nanoseconds; if (!underrun) { /* This TD is no longer active. Did it * complete successfully? */ if ((ctlSts & UHCI_TDCS_STS_STALLED) != 0) s = S_usbHcdLib_STALLED; else if ((ctlSts & UHCI_TDCS_STS_DBUFERR) != 0) s = S_usbHcdLib_DATA_BFR_FAULT; else if ((ctlSts & UHCI_TDCS_STS_BABBLE) != 0) s = S_usbHcdLib_BABBLE; else if ((ctlSts & UHCI_TDCS_STS_TIME_CRC) != 0) s = S_usbHcdLib_CRC_TIMEOUT; else if ((ctlSts & UHCI_TDCS_STS_BITSTUFF) != 0) s = S_usbHcdLib_BITSTUFF_FAULT; } if (!underrun && s == PENDING) { /* There doesn't appear to be an error. Update the actlen * field in the IRP. */ pBfrList = &pIrp->bfrList [pWork->bfrNo]; /* If this in an IN and the actLen was shorter than the * expected size, then the transfer is complete. If the * IRP flag USB_FLAG_SHORT_FAIL is set, then fail the * transfer. */ actLen = UHCI_TDCS_ACTLEN (ctlSts); pBfrList->actLen += actLen; token = FROM_LITTLEL (pTd->td.token); if (UHCI_TDTOK_PID (token) == USB_PID_IN && actLen < UHCI_TDTOK_MAXLEN (token)) underrun = TRUE; if (pBfrList->actLen == pBfrList->bfrLen || underrun) { pWork->bfrNo++; pWork->bfrOffset = 0; pWork->zeroLenMapped = FALSE; } if (underrun && (pIrp->flags & USB_FLAG_SHORT_FAIL) != 0) s = S_usbHcdLib_SHORT_PACKET; if (underrun && pWork->pQh != NULL) { /* When an underrun is detected, there is still a QH * pointing to the TD which caused the underrun. In * this case, we need to get the TD out of the work * list before we can re-use it. */ pWork->pQh->qh.tdLink = UHC_END_OF_LIST; DMA_FLUSH (&pWork->pQh->qh.tdLink, sizeof (pWork->pQh->qh.tdLink)); } } /* If this is an isochronous transfer, delink it from the * UHC work list. */ if (pWork->pPipe->transferType == USB_XFRTYPE_ISOCH) unlinkIsochTd (pHost, pTd->sw.pWork, pTd->sw.frameNo); /* Delink this TD from the in-use pool. If this happens to * be the last TD in use, then update pLastTdInUse. */ if ((pWork->pTdInUse = pTd->sw.pNext) == NULL) pWork->pLastTdInUse = NULL; /* Link TD to free pool. */ pTd->sw.pNext = pWork->pTdFree; pWork->pTdFree = pTd; } } /* Update the bus tally of bandwidth in use. */ if (s == PENDING) { /* Transfer is ok. Release bandwidth from TDs we processed */ pHost->nanoseconds -= nanosecondsAtStart - pWork->nanoseconds; } else { /* Transfer has failed. Release all associated bandwidth. */ pHost->nanoseconds -= nanosecondsAtStart; } /* If the IRP has not failed, and if there is work remaining to be done, * and if there are entries on the free TD pool, then prepare additional * TDs. If the IRP has not failed and there is no work remaining to be * done then mark the IRP as successful. */ if (s == PENDING) { if (pWork->bfrNo == pIrp->bfrCount) { s = OK; } else { /* For interrupt, control, and bulk transfers the QH is already * in the UHC work list. So, re-assigning TDs will continue the * transfer. However, for isochronous transfers, fresh TDs need * to be injected into the work list. */ assignTds (pHost, pIrp, pWork); if (pWork->pPipe->transferType == USB_XFRTYPE_ISOCH) scheduleIsochFrames (pHost, pIrp, pWork); } } /* Check if IRP has finished */ if (s != PENDING) { /* The IRP has finished, invalidate the cache for any input buffers */ for (i = 0; i < pIrp->bfrCount; i++) { pBfrList = &pIrp->bfrList [i]; if (pBfrList->pid == USB_PID_IN && pBfrList->actLen > 0) USER_INVALIDATE (pBfrList->pBfr, pBfrList->actLen); } } return s; }/***************************************************************************** hcSynch - give the host controller a chance to synchronize** It appears that certain UHCI host controllers (e.g., Via Tech)* pre-fetch QHs. This creates a problem for us when we want to* unlink the QH, as the host controller still has a record of it.* To make sure the host controller doesn't declare a consistency* error when it examines the QH/TDs associated with the QH, we* wait until the current frame ends before freeing the QH/TD* memory. On the next frame, the host controller will re-fetch* all QHs, and it won't see the ones we've removed.** RETURNS: N/A*/LOCAL VOID hcSynch ( pHCD_HOST pHost ) { UINT16 currentFrameNo = getFrameNo (pHost); while (getFrameNo (pHost) == currentFrameNo) semTake(pHost->hcSyncSem, 1); }/***************************************************************************** cancelIrp - cancel's an outstanding IRP** If the IRP's result code is not PENDING, then we cannot cancel* the IRP as it has either already completed or it is not yet enqueued.** RETURNS: OK if IRP canceled* S_usbHcdLib_CANNOT_CANCEL if IRP already completed*/LOCAL int cancelIrp ( pHCD_HOST pHost, pUSB_IRP pIrp, int result /* error to assign to IRP */ ) { pIRP_WORKSPACE pWork; int s = OK; if (pIrp->result != PENDING) s = S_usbHcdLib_CANNOT_CANCEL; else { /* The IRP is pending. Unlink it. */ usbListUnlink (&pIrp->hcdLink); /* If this IRP is a "bus" IRP - as opposed to a root IRP - then * remove it from the UHC's work list. */ if ((pWork = (pIRP_WORKSPACE) pIrp->hcdPtr) != NULL && pWork->pPipe->busAddress != pHost->rootAddress) { /* Remove QHs/TDs from the work list and release workspace. */ unscheduleIrp (pHost, pIrp, pWork); HC_SYNCH (pHost); freeIrpWorkspace (pHost, pIrp); } setIrpResult (pIrp, result); } return s; }/***************************************************************************** processUhcInterrupt - process a hardware interrupt from the UHC** Determine the cause of a hardware interrupt and service it. If the* interrupt results in the completion of one or more IRPs, handle the* completion processing for those IRPs.** RETURNS: N/A*/LOCAL VOID processUhcInterrupt ( pHCD_HOST pHost ) { pUSB_IRP pIrp; pUSB_IRP pNextIrp; pIRP_WORKSPACE pWork; LIST_HEAD completeIrps = {0}; int irpsCompleted = 0; int result; /* evaluate the interrupt condition */ if ((pHost->intBits & (UHCI_STS_PROCERR | UHCI_STS_HOSTERR)) != 0) { /* No response, just clear error condition */ pHost->intBits &= ~(UHCI_STS_PROCERR | UHCI_STS_HOSTERR); pHost->hostErrCount++; } if ((pHost->intBits & UHCI_STS_RESUME) != 0) { /* Remote device is driving resume signalling. If the HCD client * installed a management callback, invoke it. */ pHost->intBits &= ~UHCI_STS_RESUME; if (pHost->mngmtCallback != NULL) (*pHost->mngmtCallback) (pHost->mngmtCallbackParam, pHost->handle, 0 /* bus number */, HCD_MNGMT_RESUME); } if ((pHost->intBits & (UHCI_STS_USBERR | UHCI_STS_USBINT)) != 0) { /* Completion of one or more USB transactions detected. */ pHost->intBits &= ~(UHCI_STS_USBERR | UHCI_STS_USBINT); /* Search for one or more IRPs which have been completed * (or partially completed). */ pIrp = usbListFirst (&pHost->busIrps); while (pIrp != NULL) { pNextIrp = usbListNext (&pIrp->hcdLink); /* As we walk the list of IRPs, try to reschedule QHs and TDs * which are complete. */ pWork = (pIRP_WORKSPACE) pIrp->hcdPtr; if ((result = rescheduleTds (pHost, pIrp, pWork)) != PENDING) { /* The indicated IRP is complete. */ irpsCompleted++; /* De-link the QH for this transfer. * * NOTE: We defer freeing the workspace associated with each * IRP until we're ready to do the callbacks. This gives us * a chance to synchronize with the host controller, below. */ unscheduleIrp (pHost, pIrp, pWork); /* Unlink the IRP from the list of active IRPs */ usbListUnlink (&pIrp->hcdLink); pIrp->result = result; usbListLink (&completeIrps, pIrp, &pIrp->hcdLink, LINK_TAIL); } pIrp = pNextIrp; } /* Invoke IRP callbacks for all completed IRPs. */ if (irpsCompleted > 0) HC_SYNCH (pHost); while ((pIrp = usbListFirst (&completeIrps)) != NULL) { usbListUnlink (&pIrp->hcdLink); freeIrpWorkspace (pHost, pIrp); setIrpResult (pIrp, pIrp->result); } } }/***************************************************************************** checkIrpTimeout - Checks for IRPs which may have timed-out** RETURNS: N/A*/LOCAL VOID checkIrpTimeout ( pHCD_HOST pHost /* host to check */ ) { pUSB_IRP pIrp; pUSB_IRP pNextIrp; pIRP_WORKSPACE pWork; UINT32 now; /* Search for one or more IRPs which have been completed * (or partially completed). */ now = OSS_TIME (); pIrp = usbListFirst (&pHost->busIrps); while (pIrp != NULL) { pNextIrp = usbListNext (&pIrp->hcdLink); pWork = (pIRP_WORKSPACE) pIrp->hcdPtr; if (pIrp->timeout != USB_TIMEOUT_NONE && pWork->irpRunning && now - pWork->startTime > pIrp->timeout) { /* This IRP has exceeded its run time. */ cancelIrp (pHost, pIrp, S_usbHcdLib_TIMEOUT); } pIrp = pNextIrp; } }/***************************************************************************** isHubStatus - checks status on root hub** RETURNS: hub and port status bitmap as defined by USB*/LOCAL UINT8 isHubStatus ( pHCD_HOST pHost ) { UINT8 hubStatus = 0; UINT16 port; UINT16 portReg; UINT16 portStatus; for (port = 0; port < UHCI_PORTS; port++) { portReg = UHCI_PORTSC1 + port * 2; portStatus = UHC_WORD_IN (portReg); if ((portStatus & UHCI_PORT_CNCTCHG) != 0 || (portStatus & UHCI_PORT_ENBCHG) != 0 || pHost->cSuspend [port] || pHost->cReset [port]) { hubStatus |= USB_HUB_ENDPOINT_STS_PORT0 << port; } } return hubStatus; }/***************************************************************************** intThread - Thread invoked when hardware interrupts are detected** By convention, the <param> t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -