📄 usbhcdohcilib.c
字号:
{
pED_WRAPPER pCurEd;
pED_WRAPPER pNextEd;
/* We need to halt processing of control transfers before de-linking. */
HC_CLR_BITS (OHCI_HC_CONTROL, OHCI_CTL_CLE);
hcSynch (pHost);
/* Find the pipe's ED and de-link it. */
if ((pCurEd = FROM_PCIPTR (HC_DWORD_IN (OHCI_HC_CONTROL_HEAD_ED)))
== pPipe->pEd)
{
/* ED is the head of the list */
HC_DWORD_OUT (OHCI_HC_CONTROL_HEAD_ED,
FROM_LITTLEL (pPipe->pEd->ed.nextEd));
}
else
{
/* Walk the list looking for the ED.
*
* NOTE: We know the ED must be on the list, so there's no need
* to check for the end of list as we proceed.
*/
while ((pNextEd = ED_FROM_PCIPTR (pCurEd->ed.nextEd)) != pPipe->pEd)
pCurEd = pNextEd;
pCurEd->ed.nextEd = pPipe->pEd->ed.nextEd;
}
/*
* shutOffListProcessing() halts isoch, interrupt and bulk transfers
* when a device is disconnected, we make sure they are re-enabled.
*/
HC_SET_BITS(OHCI_HC_CONTROL,OHCI_CTL_PLE);
HC_SET_BITS(OHCI_HC_CONTROL,OHCI_CTL_IE);
HC_SET_BITS(OHCI_HC_CONTROL,OHCI_CTL_BLE);
/* Re-enable the control list. */
HC_SET_BITS (OHCI_HC_CONTROL, OHCI_CTL_CLE);
}
/***************************************************************************
*
* scheduleControlPipe - inserts control pipe into the schedule
*
* Inserts the control transfer into the portion of the frame list
* appropriate for control transfers.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleControlPipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
pED_WRAPPER pLastEd = FROM_PCIPTR (HC_DWORD_IN (OHCI_HC_CONTROL_HEAD_ED));
pED_WRAPPER pNextEd;
/* Flush the ED out of the cache before giving it to the HC. */
DMA_FLUSH (pPipe->pEd, sizeof (*pPipe->pEd));
if (pLastEd == NULL)
{
/* This will be the first ED on the control list. */
HC_DWORD_OUT (OHCI_HC_CONTROL_HEAD_ED, TO_PCIPTR (pPipe->pEd));
}
else
{
/* Append this ED to the end of the control list. */
while ((pNextEd = ED_FROM_PCIPTR (pLastEd->ed.nextEd)) != NULL)
pLastEd = pNextEd;
pLastEd->ed.nextEd = TO_LITTLEL (TO_PCIPTR (pPipe->pEd));
DMA_FLUSH (&pLastEd->ed.nextEd, sizeof (pLastEd->ed.nextEd));
}
}
/***************************************************************************
*
* unscheduleBulkPipe - removes bulk transfer from the schedule
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleBulkPipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
pED_WRAPPER pCurEd;
pED_WRAPPER pNextEd;
/* We need to halt processing of bulk transfers before de-linking. */
HC_CLR_BITS (OHCI_HC_CONTROL, OHCI_CTL_BLE);
hcSynch (pHost);
/* Find the pipe's ED and de-link it. */
if ((pCurEd = FROM_PCIPTR (HC_DWORD_IN (OHCI_HC_BULK_HEAD_ED)))
== pPipe->pEd)
{
/* ED is the head of the list. */
HC_DWORD_OUT (OHCI_HC_BULK_HEAD_ED,
FROM_LITTLEL (pPipe->pEd->ed.nextEd));
}
else
{
/* Walk the list looking for the ED.
*
* NOTE: We know the ED must be on the list, so there's no need
* to check for the end of list as we proceed.
*/
while ((pNextEd = ED_FROM_PCIPTR (pCurEd->ed.nextEd)) != pPipe->pEd)
pCurEd = pNextEd;
pCurEd->ed.nextEd = pPipe->pEd->ed.nextEd;
}
/*
* shutOffListProcessing() halts isoch, interrupt and bulk transfers
* when a device is disconnected, we make sure they are re-enabled.
*/
HC_SET_BITS(OHCI_HC_CONTROL,OHCI_CTL_PLE);
HC_SET_BITS(OHCI_HC_CONTROL,OHCI_CTL_IE);
HC_SET_BITS(OHCI_HC_CONTROL,OHCI_CTL_BLE);
}
/***************************************************************************
*
* scheduleBulkPipe - inserts bulk pipe into the schedule
*
* Inserts the bulk transfer into the portion of the frame list appropriate
* for bulk transfers.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleBulkPipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
pED_WRAPPER pLastEd = FROM_PCIPTR (HC_DWORD_IN (OHCI_HC_BULK_HEAD_ED));
pED_WRAPPER pNextEd;
/* Flush the ED out of the cache before giving it to the HC. */
DMA_FLUSH (pPipe->pEd, sizeof (*pPipe->pEd));
if (pLastEd == NULL)
{
/* This will be the first ED on the bulk list. */
HC_DWORD_OUT (OHCI_HC_BULK_HEAD_ED, TO_PCIPTR (pPipe->pEd));
}
else
{
/* Append this ED to the end of the bulk list. */
while ((pNextEd = ED_FROM_PCIPTR (pLastEd->ed.nextEd)) != NULL)
pLastEd = pNextEd;
pLastEd->ed.nextEd = TO_LITTLEL (TO_PCIPTR (pPipe->pEd));
DMA_FLUSH (&pLastEd->ed.nextEd, sizeof (pLastEd->ed.nextEd));
}
}
/***************************************************************************
*
* unschedulePipe - Removes pipe from schedule
*
* RETURNS: N/A
*/
LOCAL VOID unschedulePipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
/* un-Scheduling proceeds differently for each transaction type. */
switch (pPipe->transferType)
{
case USB_XFRTYPE_ISOCH:
unscheduleIsochPipe (pHost, pPipe);
break;
case USB_XFRTYPE_INTERRUPT:
unscheduleInterruptPipe (pHost, pPipe);
break;
case USB_XFRTYPE_CONTROL:
unscheduleControlPipe (pHost, pPipe);
break;
case USB_XFRTYPE_BULK:
unscheduleBulkPipe (pHost, pPipe);
break;
}
}
/***************************************************************************
*
* schedulePipe - Adds a pipe to the schedule
*
* RETURNS: N/A
*/
LOCAL VOID schedulePipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
/* Scheduling proceeds differently for each transaction type. */
switch (pPipe->transferType)
{
case USB_XFRTYPE_ISOCH:
scheduleIsochPipe (pHost, pPipe);
break;
case USB_XFRTYPE_INTERRUPT:
scheduleInterruptPipe (pHost, pPipe);
break;
case USB_XFRTYPE_CONTROL:
scheduleControlPipe (pHost, pPipe);
break;
case USB_XFRTYPE_BULK:
scheduleBulkPipe (pHost, pPipe);
break;
}
}
/***************************************************************************
*
* 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.
*
* 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
* releaseTd() 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,
pHCD_PIPE pPipe,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
pUSB_BFR_LIST pBfrList;
pTD_WRAPPER pTd;
UINT32 bytesThroughFrameCount;
UINT16 bytesThisFrame;
UINT16 isochLen;
UINT32 isochOffset;
UINT16 maxLen;
UINT32 nanoseconds;
UINT32 control = 0;
UINT32 curBfrPtr;
UINT32 bfrEnd;
UINT16 frameCount;
UINT16 psw;
UINT32 pciPtrToNextFree;
UINT16 i;
/* 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.
*/
if (pWork->bfrNo == pIrp->bfrCount)
return;
pBfrList = &pIrp->bfrList [pWork->bfrNo];
while ((pWork->bfrOffset < pBfrList->bfrLen ||
(pBfrList->bfrLen == 0 &&
!pWork->zeroLenMapped)) &&
pPipe->freeTdCount > 0)
{
/* Calculate the length of this TD and determine if there are any
* bandwidth limitations which would prevent scheduling it 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,
min (pBfrList->bfrLen - pWork->bfrOffset,
pPipe->maxPacketSize),
HC_HOST_DELAY,
HC_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)
{
/*
* By default USB_BUFFER_SWAP is a no-op, but it can be
* overridden in BSP stubs as necessary.
*/
USB_BUFFER_SWAP ((pVOID *) pBfrList->pBfr, pBfrList->bfrLen);
USER_FLUSH (pBfrList->pBfr, pBfrList->bfrLen);
}
/* Grab the first free TD for our use...Then advance the "free TD
* index" so that it points to the next free TD. (Since freeTdCount
* is initialized to be one less than tdCount, we are guaranteed that
* there are always at least two free TDs when we reach this point in
* the code.
*/
pTd = &pPipe->pTds [pPipe->freeTdIndex];
pTd->sw.inUse = 1;
pPipe->freeTdCount--;
do
{
if (++pPipe->freeTdIndex == pPipe->tdCount)
pPipe->freeTdIndex = 0;
}
while (pPipe->pTds [pPipe->freeTdIndex].sw.inUse != 0);
/* OHCI TD's can span up to two, 4k pages. Based on this limit,
* calculate the maximum amount of data we'll try to transfer with
* this TD.
*/
curBfrPtr = TO_PCIPTR ((pVOID) &pBfrList->pBfr [pWork->bfrOffset]);
maxLen = OHCI_PAGE_SIZE - (curBfrPtr & ~OHCI_PAGE_MASK) + \
OHCI_PAGE_SIZE;
maxLen = min (maxLen, pBfrList->bfrLen - pWork->bfrOffset);
/* Initialize TD.
*
* NOTE: By convention, TD is always cleared to 0's at initialization
* or after being discarded by the previous transfer.
*/
switch (pBfrList->pid)
{
case USB_PID_SETUP:
control = OHCI_TGCTL_PID_SETUP;
break;
case USB_PID_OUT:
control = OHCI_TGCTL_PID_OUT;
break;
case USB_PID_IN:
control = OHCI_TGCTL_PID_IN;
break;
}
if (pPipe->transferType == USB_XFRTYPE_ISOCH)
{
/* Isochronous TD */
/* Map up to a complete TD (eight frames). */
frameCount = 0;
isochOffset = 0;
for (i = 0; i < OHCI_ISO_PSW_CNT && maxLen > 0; i++)
{
/* Calculate the packet length for the next frame. */
bytesThroughFrameCount = (pWork->frameCount + 1) *
pPipe->bandwidth / 1000L;
bytesThisFrame = bytesThroughFrameCount - pWork->bytesSoFar;
/* NOTE: If maxLen is less than the desired number of bytes
* for this frame *AND* we are not yet at the end of this
* BFR_LIST entry, then don't schedule any more frames for
* this TD...For this situation to arise we must be near a
* page boundary crossing...The next TD will begin one page
* later and will allow us to schedule the full frame as
* desired.
*/
if (maxLen < bytesThisFrame &&
pWork->bfrOffset + maxLen != pBfrList->bfrLen)
break;
isochLen = min (bytesThisFrame, maxLen);
isochLen = min (isochLen, pPipe->maxPacketSize);
if (pIrp->dataBlockSize != 0 && isochLen > pIrp->dataBlockSize)
{
isochLen = (isochLen / pIrp->dataBlockSize) *
pIrp->dataBlockSize;
}
pWork->bytesSoFar += isochLen;
maxLen -= isochLen;
pWork->frameCount++;
frameCount++;
/* Initialize the TD PSW for this frame. */
psw = OHCI_TIPSW_CC_FMT (OHCI_CC_NOT_ACCESSED);
psw |= (curBfrPtr + isochOffset) & OHCI_ISO_OFFSET_MASK;
if ((curBfrPtr & OHCI_PAGE_MASK) !=
((curBfrPtr + isochOffset) & OHCI_PAGE_MASK))
{
psw |= OHCI_ISO_OFFSET_BE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -