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 + -
显示快捷键?