usbhcduhcilib.c
来自「cpc-1631的BSP包for VxWorks操作系统」· C语言 代码 · 共 2,337 行 · 第 1/5 页
C
2,337 行
{
/* Set the forward link of this QH to the forward link of the
* QH which is currently last on the bulk list.
*/
if (ENABLE_BANDWIDTH_RECLAMATION (pHost))
{
if (pHost->pLastBulkQh->qh.qhLink == UHC_END_OF_LIST)
pWork->pQh->qh.qhLink = pHost->pciPtrToBulkAnchorQh;
else
pWork->pQh->qh.qhLink = pHost->pLastBulkQh->qh.qhLink;
}
else
{
pWork->pQh->qh.qhLink = pHost->pLastBulkQh->qh.qhLink;
}
DMA_FLUSH (&pWork->pQh->qh, sizeof (pWork->pQh->qh));
/* Set the forward link of the previous QH to this QH. */
pHost->pLastBulkQh->qh.qhLink = QH_TO_PCIPTR (pWork->pQh);
DMA_FLUSH (&pHost->pLastBulkQh->qh.qhLink,
sizeof (pHost->pLastBulkQh->qh.qhLink));
pHost->pLastBulkQh = pWork->pQh;
}
/***************************************************************************
*
* unscheduleIrp - Removes QH/TDs for IRP from work UHC work lists
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleIrp
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
/* un-Scheduling proceeds differently for each transaction type. */
switch (pWork->pPipe->transferType)
{
case USB_XFRTYPE_ISOCH:
unscheduleIsochTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_INTERRUPT:
unscheduleInterruptTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_CONTROL:
unscheduleControlTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_BULK:
unscheduleBulkTransfer (pHost, pIrp, pWork);
break;
}
}
/***************************************************************************
*
* scheduleIrp - try to schedule an IRP in the frame list
*
* RETURNS: N/A
*/
LOCAL VOID scheduleIrp
(
pHCD_HOST pHost,
pUSB_IRP pIrp
)
{
pIRP_WORKSPACE pWork = (pIRP_WORKSPACE) pIrp->hcdPtr;
pHCD_PIPE pPipe = pWork->pPipe;
/* prepare TDs */
assignTds (pHost, pIrp, pWork);
/* Mark the time the IRP was first scheduled. */
if (!pWork->irpRunning)
{
pWork->irpRunning = TRUE;
pWork->startTime = OSS_TIME ();
}
/* Scheduling proceeds differently for each transaction type. */
switch (pPipe->transferType)
{
case USB_XFRTYPE_ISOCH:
scheduleIsochTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_INTERRUPT:
scheduleInterruptTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_CONTROL:
scheduleControlTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_BULK:
scheduleBulkTransfer (pHost, pIrp, pWork);
break;
}
}
/***************************************************************************
*
* rescheduleTds - Examine the given IRP and see if it needs servicing
*
* <pIrp> is an IRP which may/may not have been assigned QHs and TDs and
* currently be schedule for execution. Examine the <pIrp> and its
* associated USB_WORKSPACE in <pWork> (which might be NULL). If no work
* has been scheduled or if no TDs have been completed by the UHC, then
* we do nothing. If there are TDs which have been completed, then we
* release them to the "free" pool. If work remains to be done, then we
* reschedule the TDs. If no work remains to be done or if there is an
* error, then we report that the IRP is complete.
*
* RETURNS: OK = IRP completed ok
* PENDING = IRP still being executed
* S_usbHcdLib_xxx = IRP failed with error
*/
LOCAL int rescheduleTds
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
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 ((
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?