📄 usbhcdsl811hslib.c
字号:
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.
*/
#ifdef INCLUDE_USB_PCI
pWork->pLastTdInUse->td.linkPtr = TO_PCIPTR (pTd)
| TO_LITTLEL (UHCI_LINK_VF);
#else
pWork->pLastTdInUse->td.linkPtr = TO_PCIPTR (pTd) | UHCI_LINK_VF;
#endif
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));
}
/* Update the bandwidth-in-use for this controller.
*
* NOTE: The pWork->nanoseconds field is 0 for all transfer types
* except low speed control transfers, so the following calculation
* often has no effect.
*/
pHost->nanoseconds += pWork->nanoseconds - nanosecondsAtStart;
}
/***************************************************************************
*
* unlinkIsochTd - unlink an isochronous TD
*
* Searches the indicated <frame> for a TD which corresponds to the
* IRP_WORKSPACE <pWork> (and hence which correspond to a given TD).
* If found, the TD is unlinked.
*
* RETURNS: N/A
*/
LOCAL VOID unlinkIsochTd
(
pHCD_HOST pHost,
pIRP_WORKSPACE pWork,
UINT32 frame
)
{
pUINT32 pPciPtrToTd;
pTD_WRAPPER pTd;
/* Search frame for a TD belonging to this IRP and remove it from the
* work list. We've exhausted the list of isochronous TDs when we
* reach the "interrupt anchor" QH.
*/
pPciPtrToTd = &pHost->pFrameList->linkPtr [frame];
pTd = TD_FROM_PCIPTR (*pPciPtrToTd);
while (pTd != (pTD_WRAPPER) &pHost->pIntAnchorQhs [frame])
{
if (pTd->sw.pWork == pWork)
{
/* We found a TD for this IRP. Unlink it. */
*pPciPtrToTd = pTd->td.linkPtr;
DMA_FLUSH (pPciPtrToTd, sizeof (*pPciPtrToTd));
--pWork->isochCount;
break;
}
pPciPtrToTd = (pUINT32) &pTd->td.linkPtr;
pTd = TD_FROM_PCIPTR (pTd->td.linkPtr);
}
}
/***************************************************************************
*
* unscheduleIsochTransfer - removes isochronous transfer from work list
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleIsochTransfer
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
UINT16 frame;
/* Remove all pending isoch TDs for this IRP from the work list */
for (frame = 0;
frame < UHCI_FRAME_LIST_ENTRIES && pWork->isochCount > 0;
frame++)
{
unlinkIsochTd (pHost, pWork, frame);
}
}
/***************************************************************************
*
* scheduleIsochFrames - insert isoch frames into work list
*
* Schedules isochronous transfers into frames.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleIsochFrames
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
pTD_WRAPPER pTd;
UINT16 frame;
/* Schedule all initialized TDs onto the work list */
while ((pTd = pWork->pNextIsochTd) != NULL &&
pWork->isochCount < UHCI_FRAME_LIST_ENTRIES)
{
/* Link the TD into the work list. */
pTd->sw.frameNo = frame = pWork->isochNext;
pTd->td.linkPtr = pHost->pFrameList->linkPtr [frame];
DMA_FLUSH (&pTd->td.linkPtr, sizeof (pTd->td.linkPtr));
pHost->pFrameList->linkPtr [pWork->isochNext] = TO_PCIPTR (pTd);
DMA_FLUSH (&pHost->pFrameList->linkPtr [frame],
sizeof (pHost->pFrameList->linkPtr [frame]));
/* Advance to the next isoch TD for this IRP. */
pWork->pNextIsochTd = pTd->sw.pNext;
if (++pWork->isochNext == UHCI_FRAME_LIST_ENTRIES)
pWork->isochNext = 0;
pWork->isochCount++;
}
}
/***************************************************************************
*
* scheduleIsochTransfer - inserts isoch IRP in frame list
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -