usbhcduhcilib.c

来自「cpc-1631的BSP包for VxWorks操作系统」· C语言 代码 · 共 2,337 行 · 第 1/5 页

C
2,337
字号

    /* return results */

    if (pQhCount != NULL)
	   *pQhCount = qhCount;

    if (pTdCount != NULL)
	    *pTdCount = tdCount;
    }


/***************************************************************************
*
* freeIrpWorkspace - releases IRP_WORKSPACE and related QHs and TDs
*
* Releases all QHs and TDs associated with an IRP_WORKSPACE and frees
* the IRP_WORKSPACE structure itself.
*
* RETURNS: N/A
*/

LOCAL VOID freeIrpWorkspace
    (
    pHCD_HOST pHost,
    pUSB_IRP pIrp
    )

    {
    pIRP_WORKSPACE pWork = (pIRP_WORKSPACE) pIrp->hcdPtr;

    if (pWork != NULL)
	{
	if (pWork->pQh != NULL)
	    DMA_FREE (pWork->pQh);

	if (pWork->pTdPool != NULL)
	    DMA_FREE (pWork->pTdPool);

	OSS_FREE (pWork);

	pIrp->hcdPtr = NULL;
	}
    }


/***************************************************************************
*
* allocIrpWorkspace - allocates QH and TD structures for IRP
*
* Calculates the required number of QH and TD structures for an IRP
* and allocates aligned memory.  Also creates an IRP_WORKSPACE structure
* to manage the QHs and TDs allocated for the IRP and links it into the
* IRP.
*
* RETURNS: TRUE if successful, else FALSE
*/

LOCAL BOOL allocIrpWorkspace
    (
    pHCD_HOST pHost,
    pHCD_PIPE pPipe,
    pUSB_IRP pIrp
    )

    {
    pIRP_WORKSPACE pWork;
    UINT16 tdLen;
    UINT16 i;


    /* Allocate IRP_WORKSPACE */

    if ((pWork = OSS_CALLOC (sizeof (*pWork))) == NULL)
	return FALSE;

    pIrp->hcdPtr = pWork;
    pWork->pPipe = pPipe;
    pWork->pIrp = pIrp;


    /* Calculate number of QHs and TDs */

    calcQhTd (pHost, pPipe, pIrp, &pWork->qhCount, &pWork->tdCount);


    /* Allocate QHs and TDs */

    if (pWork->qhCount > 0)
	{
	if ((pWork->pQh = DMA_MALLOC (sizeof (QH_WRAPPER), 
	    UHCI_QH_ALIGNMENT)) == NULL)
	    {
	    freeIrpWorkspace (pHost, pIrp);
	    return FALSE;
	    }

	memset (pWork->pQh, 0, sizeof (QH_WRAPPER));
	pWork->pQh->sw.pWork = pWork;
	pWork->pQh->qh.tdLink = UHC_END_OF_LIST;
	}

    if (pWork->tdCount > 0)
	{
	tdLen = pWork->tdCount * sizeof (TD_WRAPPER);

	if ((pWork->pTdPool = DMA_MALLOC (tdLen, UHCI_TD_ALIGNMENT)) == NULL)
	    {
	    freeIrpWorkspace (pHost, pIrp);
	    return FALSE;
	    }

	memset (pWork->pTdPool, 0, tdLen);
	}


    /* Create a chain of TDs in the "free" list */

    pWork->pTdInUse = pWork->pTdFree = NULL;

    for (i = 0; i < pWork->tdCount; i++)
	{
	pWork->pTdPool [i].sw.pWork = pWork;

	pWork->pTdPool [i].sw.pNext = pWork->pTdFree;
	pWork->pTdFree = &pWork->pTdPool [i];
	}


    /* Initialize the pointer to the last TD in use */

    pWork->pLastTdInUse = NULL;


    return TRUE;
    }


/***************************************************************************
*
* isBandwidthTracked - determines if bandwidth is tracked for this transfer
*
* Since the USBD has already reserved adequate bandwidth for isochronous
* and interrupt transfers, we only track low speed control transfers.
*
* RETURNS: TRUE if bandwidth tracked, else FALSE.
*/

LOCAL BOOL isBandwidthTracked
    (
    pHCD_PIPE pPipe
    )

    {
    if (pPipe->transferType == USB_XFRTYPE_CONTROL && 
	pPipe->speed == USB_SPEED_LOW)
	return TRUE;

    return FALSE;
    }


/***************************************************************************
*
* dirFromPid - returns USB_DIR_xxxx based on USB_PID_xxxx
*
* RETURNS: USB_DIR_xxxx
*/

LOCAL UINT16 dirFromPid
    (
    UINT16 pid
    )

    {
    switch (pid)
    {
    case USB_PID_SETUP:
    case USB_PID_OUT:	
	return USB_DIR_OUT;

    case USB_PID_IN:	
	return USB_DIR_IN;

    default:	    
	return USB_DIR_IN;
    }
    }


/***************************************************************************
*
* assignTds - assign TDs for each IRP bfrList[] entry
*
* Assigns and initializes TDs to map the next bfrList[] entry in the IRP.  
* Stops when all all bfrList[] entries have been mapped, when all TDs for 
* the IRP have been exhausted, or when buffer bandwidth calculations indicate 
* we shouldn't map any more TDs at this time.  
*
* This function also updates the pHost->nanoseconds field with the bandwidth
* required by this transfer if appropriate.
*
* This mapping has the effect of moving TDs from the free list to the "in
* use" list in the IRP_WORKSPACE.  TDs in the "in use" list are always
* linked in the order in which they should be executed.
*
* NOTE: We choose to map only one bfrList[] entry at a time to simplify the
* the handling of input underrun.  When an underrun occurs, the 
* rescheduleTds() function releases all TDs scheduled up through that point.
* We then schedule TDs for the following bfrList[] entries if any.
*
* RETURNS: N/A
*/

LOCAL VOID assignTds
    (
    pHCD_HOST pHost,
    pUSB_IRP pIrp,
    pIRP_WORKSPACE pWork
    )

    {
    pHCD_PIPE pPipe = pWork->pPipe;
    pTD_WRAPPER pTd;
    pUSB_BFR_LIST pBfrList;
    UINT32 bytesThroughFrameCount;
    UINT16 maxLen;
    UINT32 nanoseconds;
    UINT32 nanosecondsAtStart;
    UINT32 ctlSts;
    UINT32 token;


    /* Record the bus time already used by this transfer. */

    nanosecondsAtStart = pWork->nanoseconds;


    /* Assign TDs to map the current bfrList[] entry.  Stop when the buffer is 
     * fully mapped, when we run out of TDs, or when bus bandwidth calculations 
     * indicate we should not allocate more TDs at this time.
     */

    pBfrList = &pIrp->bfrList [pWork->bfrNo];

    while ((pWork->bfrOffset < pBfrList->bfrLen ||
	   (pBfrList->bfrLen == 0 && !pWork->zeroLenMapped)) && 
	   pWork->pTdFree != NULL)
	{

	/* Calculate the length of this TD. */

	if (pPipe->transferType == USB_XFRTYPE_ISOCH)
	    {
	    pWork->frameCount++;

	    bytesThroughFrameCount = pWork->frameCount 
				     * pPipe->bandwidth 
				     / 1000L;

	    maxLen = min (bytesThroughFrameCount - pWork->bytesSoFar,
	    pBfrList->bfrLen - pWork->bfrOffset);

	    maxLen = min (maxLen, pPipe->maxPacketSize);

	    if (pIrp->dataBlockSize != 0 && maxLen > pIrp->dataBlockSize)
		    maxLen = (maxLen / pIrp->dataBlockSize) 
			     * pIrp->dataBlockSize;

	    pWork->bytesSoFar += maxLen;
	    }
	else
	    {
	    /* Transfer length calculation for non-isoch pipes. */

	    maxLen = min (pBfrList->bfrLen - pWork->bfrOffset, 
			  pPipe->maxPacketSize);
	    }


	/* Determine if there are any bandwidth limitations which would prevent 
	 * scheduling this TD at this time.
	 *
	 * NOTE: Since the USBD has already checked bandwidth availability for
	 * isochronous and interrupt transfers, we need only check bandwidth
	 * availability for low speed control transfers.
	 */

	if (isBandwidthTracked (pPipe))
	    {
	    nanoseconds = usbTransferTime (pPipe->transferType, 
	    dirFromPid (pBfrList->pid), pPipe->speed, maxLen, 
	    UHC_HOST_DELAY, UHC_HUB_LS_SETUP);

	    if (pHost->nanoseconds + nanoseconds > USB_LIMIT_ALL)
		{
		/* There isn't enough bandwidth at this time.  Stop 
		 * scheduling for this transfer.
		 */
		
		break;
		}
	    }
	else
	    {
	    nanoseconds = 0;
	    }


	/* If this is the first time we've mapped a part of this bfrList[] entry,
	 * then flush the cache.  Note that we do this for input buffers as well
	 * since we don't know where the user's buffer is allocated and some CPU
	 * cache architectures may get confused near cache-line boundaries.
	 */

	if (pWork->bfrOffset == 0 && pBfrList->bfrLen > 0)
	    {
	    USER_FLUSH (pBfrList->pBfr, pBfrList->bfrLen);
	    }


	/* Unlink the TD from the free list. */

	pTd = pWork->pTdFree;
	pWork->pTdFree = pTd->sw.pNext;


	/* Initialize TD buffer pointer. */

	pTd->td.bfrPtr = TO_PCIPTR (&pBfrList->pBfr [pWork->bfrOffset]);
	pWork->bfrOffset += maxLen;

	if (maxLen == 0)
	    pWork->zeroLenMapped = TRUE;


	/* Initialize TD control/status word */ 

	ctlSts = UHCI_TDCS_SHORT | UHCI_TDCS_ERRCTR_3ERR;
	ctlSts |= (pPipe->speed == USB_SPEED_LOW) ? UHCI_TDCS_LOWSPEED : 0;
	ctlSts |= (pPipe->transferType == USB_XFRTYPE_ISOCH) ? 
		   UHCI_TDCS_ISOCH : 0;
	ctlSts |= UHCI_TDCS_STS_ACTIVE;


	/* enable IOC (interrupt on complete) if this is the last TD.  If
	 * this is an isochronous transfer, also enable IOC on a regular
	 * interval defined by ISOCH_INT_INTERVAL. 
	 */

	if (pWork->bfrOffset == pBfrList->bfrLen || pWork->pTdFree == NULL)
	    ctlSts |= UHCI_TDCS_COMPLETE;

	if (pPipe->transferType == USB_XFRTYPE_ISOCH)
	    {
	    pWork->isochTdsCreated++;

	    if (pWork->isochTdsCreated % ISOCH_INT_INTERVAL == 0)
		    ctlSts |= UHCI_TDCS_COMPLETE;
	    }

	pTd->td.ctlSts = TO_LITTLEL (ctlSts);


	/* Initialize TD token word */

	token = UHCI_TDTOK_MAXLEN_FMT (maxLen);

	if (pPipe->transferType != USB_XFRTYPE_ISOCH)
	    {
	    /* Normally, the data toggle begins with DATA0 and alternates
	     * between DATA0 and DATA1.  However, the last transfer for
	     * a control transfer - the status packet - is always a DATA1. 
	     */

	    if (pPipe->transferType == USB_XFRTYPE_CONTROL &&
		pWork->bfrNo == pIrp->bfrCount - 1)
		{
		token |= UHCI_TDTOK_DATA_TOGGLE;
		}
	    else
		{
		if (pIrp->dataToggle == USB_DATA0)
		    {
		    pIrp->dataToggle = USB_DATA1;
		    }
		else 
		    {
		    token |= UHCI_TDTOK_DATA_TOGGLE;
		    pIrp->dataToggle = USB_DATA0;
		    }
		}
	    }
	    
	token |= UHCI_TDTOK_ENDPT_FMT (pPipe->endpoint);
	token |= UHCI_TDTOK_DEVADRS_FMT (pPipe->busAddress);
	token |= UHCI_TDTOK_PID_FMT (pBfrList->pid);

	pTd->td.token = TO_LITTLEL (token);


	/* Store the time required to execute this TD */

	pTd->sw.nanoseconds = nanoseconds;
	pWork->nanoseconds += nanoseconds;


	/* put the TD at the end of the "in use" list.
	 *
	 * NOTE: If this QH is already active, then as soon as we
	 * set the linkPtr of the last TD in use we must expect
	 * that the UHC may begin executing this TD (does not apply
	 * to isochronous TDs for which there is no QH).
	 */

	pTd->td.linkPtr = UHC_END_OF_LIST;
	pTd->sw.pNext = NULL;

	if (pWork->pTdInUse == NULL)
	    {
	    pWork->pTdInUse = pTd;
	    }

	if (pPipe->transferType == USB_XFRTYPE_ISOCH &&
	    pWork->pNextIsochTd == NULL)
	    {
	    pWork->pNextIsochTd = pTd;
	    }

	DMA_FLUSH (&pTd->td, sizeof (pTd->td));

	if (pWork->pLastTdInUse != NULL)
	    {
	    pWork->pLastTdInUse->sw.pNext = pTd;

	    /* For non-isochronous transfers, append this TD to the list
	     * of TDs assigned to the IRP.	(Isochronous TDs are inserted
	     * into the schedule by calling scheduleIsochFrames().
	     */

	    if (pPipe->transferType != USB_XFRTYPE_ISOCH)
		{
		/* NOTE: Setting the "Vf" bit improves performance by 
		 * allowing the host controller to exchange data 
		 * continuously with the device until the device NAKs.
		 */

		pWork->pLastTdInUse->td.linkPtr = TO_PCIPTR (pTd) 
						  | TO_LITTLEL (UHCI_LINK_VF);

		DMA_FLUSH (&pWork->pLastTdInUse->td.linkPtr, 
			   sizeof (pWork->pLastTdInUse->td.linkPtr));
		}
	    }

	pWork->pLastTdInUse = pTd;
	}


    /* If there is a QH, we should initialize it to point to the beginning of 
     * the TD list.
     */

    if (pWork->pQh != NULL && pWork->pQh->qh.tdLink == UHC_END_OF_LIST)
	{
	/* Initialize QH "vertical" pointer */

	pWork->pQh->qh.tdLink = TO_PCIPTR (pWork->pTdInUse);
	DMA_FLUSH (&pWork->pQh->qh.tdLink, sizeof (pWork->pQh->qh.tdLink));

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?