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