📄 usbhcdohcilib.c
字号:
pTd->tdi.psw [i] = TO_LITTLEW (psw);
isochOffset += isochLen;
}
/*
* Packet status words need to be swapped before sending on
* some particular controllers.
* By default USB_BUFFER_SWAP is a no-op, but it can be
* overridden in BSP stubs as necessary.
*/
USB_BUFFER_SWAP ((pVOID *) &pTd->tdi.psw [0],
frameCount * sizeof (UINT16));
bfrEnd = curBfrPtr + isochOffset - 1;
pWork->bfrOffset += isochOffset;
/* Initialize remaining fields in isochronous TD. */
control |= OHCI_TICTL_FC_FMT (frameCount);
control |= OHCI_TICTL_SF_FMT (pWork->isochNext);
pWork->isochNext = FINDEX (pWork->isochNext + frameCount);
pTd->tdi.control = TO_LITTLEL (control);
pTd->tdi.bp0 = TO_LITTLEL (curBfrPtr & OHCI_PAGE_MASK);
pTd->tdi.be = TO_LITTLEL (bfrEnd);
}
else
{
/* General TD */
control |= OHCI_TGCTL_BFR_RND | OHCI_TGCTL_TOGGLE_USETD;
/* If this transfer does not complete the BFR_LIST entry, then
* truncate it to an even number of frames.
*/
if (maxLen > pPipe->maxPacketSize &&
pWork->bfrOffset + maxLen != pBfrList->bfrLen)
{
maxLen = (maxLen / pPipe->maxPacketSize) * pPipe->maxPacketSize;
}
if (maxLen == 0)
{
pWork->zeroLenMapped = TRUE;
curBfrPtr = bfrEnd = 0;
}
else
{
bfrEnd = curBfrPtr + maxLen - 1;
}
pWork->bfrOffset += maxLen;
/* The last BFR_LIST entry for a control transfer (the status
* packet) is alwasy DATA1.
*/
if (pPipe->transferType == USB_XFRTYPE_CONTROL &&
pWork->bfrNo == pIrp->bfrCount - 1)
{
control |= OHCI_TGCTL_TOGGLE_DATA1;
}
else
{
control |= (pIrp->dataToggle == USB_DATA0) ?
OHCI_TGCTL_TOGGLE_DATA0 :
OHCI_TGCTL_TOGGLE_DATA1;
}
pTd->tdg.control = TO_LITTLEL (control);
pTd->tdg.cbp = TO_LITTLEL (curBfrPtr);
pTd->tdg.be = TO_LITTLEL (bfrEnd);
}
pTd->sw.curBfrPtr = curBfrPtr;
/* Link the TD back to the workspace for the IRP. */
pTd->sw.pWork = pWork;
/* Store the time required to execute this TD */
pHost->nanoseconds += nanoseconds;
pTd->sw.nanoseconds = nanoseconds;
/* Append TD to the end of the ED's TD queue.
*
* NOTE: pTd->tdg.nextTd == pTd->tdi.nextTd. */
pciPtrToNextFree = TO_LITTLEL (TO_PCIPTR (&pPipe->pTds [pPipe->freeTdIndex]));
pTd->tdg.nextTd = pciPtrToNextFree;
DMA_FLUSH (pTd, sizeof (OHCI_TD_ISO)+sizeof(OHCI_TD_GEN));
pPipe->pEd->ed.tdTail = pciPtrToNextFree;
DMA_FLUSH (&pPipe->pEd->ed.tdTail, sizeof (pPipe->pEd->ed.tdTail));
/* If necessary, notify the HC that new a new transfer is ready. */
switch (pPipe->transferType)
{
case USB_XFRTYPE_CONTROL:
HC_SET_BITS (OHCI_HC_COMMAND_STATUS, OHCI_CS_CLF);
break;
case USB_XFRTYPE_BULK:
HC_SET_BITS (OHCI_HC_COMMAND_STATUS, OHCI_CS_BLF);
break;
}
}
}
/***************************************************************************
*
* scheduleOtherIrps - schedules other IRPs if TDs are available on a pipe
*
* RETURNS: N/A
*/
LOCAL VOID scheduleOtherIrps
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
pUSB_IRP pIrp;
pUSB_IRP pNextIrp;
pIRP_WORKSPACE pWork;
pIrp = usbListFirst (&pHost->busIrps);
while (pIrp != NULL && pPipe->freeTdCount > 0)
{
pNextIrp = usbListNext (&pIrp->hcdLink);
pWork = (pIRP_WORKSPACE) pIrp->hcdPtr;
if (pWork->pPipe == pPipe)
assignTds (pHost, pPipe, pIrp, pWork);
pIrp = pNextIrp;
}
}
/***************************************************************************
*
* releaseTd - Update an IRP based on a completed IRP.
*
* Sets <result> field in <pIrp> to OK or S_usbHcdLib_xxx if IRP completed.
*
* NOTE: Automatically reschedules TDs.
*
* RETURNS: N/A
*/
LOCAL VOID releaseTd
(
pHCD_HOST pHost,
pHCD_PIPE pPipe,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork,
pTD_WRAPPER pTd
)
{
pUSB_BFR_LIST pBfrList;
UINT32 control;
UINT32 curBfrPtr;
UINT32 bfrEnd;
UINT32 tdHead;
UINT16 frameCount;
UINT16 psw;
UINT16 actLen;
BOOL underrun = FALSE;
UINT16 i;
/*
* In some case, transmitted data is still used at either drivers or
* applications and need to be re-swapped if they are swapped before
* sending.
*/
if (pIrp->bfrList[pWork->bfrNo].pid == USB_PID_SETUP ||
pIrp->bfrList[pWork->bfrNo].pid == USB_PID_OUT)
{
if (pTd->sw.curBfrPtr != 0)
{
if (FROM_LITTLEL (pTd->tdg.cbp) == 0)
{
/*
* By default USB_BUFFER_SWAP is a no-op, but it can be
* overridden in BSP stubs as necessary.
*/
USB_BUFFER_SWAP ((pVOID *) pTd->sw.curBfrPtr,
FROM_LITTLEL (pTd->tdg.be) - pTd->sw.curBfrPtr + 1);
}
else
{
/*
* By default USB_BUFFER_SWAP is a no-op, but it can be
* overridden in BSP stubs as necessary.
*/
USB_BUFFER_SWAP ((pVOID *) pTd->sw.curBfrPtr,
FROM_LITTLEL (pTd->tdg.cbp) - pTd->sw.curBfrPtr + 1);
}
}
}
/* Release the bandwidth this TD was consuming. */
pHost->nanoseconds -= pTd->sw.nanoseconds;
/* Examine the TD. */
control = FROM_LITTLEL (pTd->tdg.control);
switch (OHCI_TGCTL_CC (control))
{
case OHCI_CC_NO_ERROR:
break;
case OHCI_CC_CRC:
case OHCI_CC_NO_RESPONSE:
pIrp->result = S_usbHcdLib_CRC_TIMEOUT;
break;
case OHCI_CC_BITSTUFF:
pIrp->result = S_usbHcdLib_BITSTUFF_FAULT;
break;
case OHCI_CC_STALL:
pIrp->result = S_usbHcdLib_STALLED;
break;
case OHCI_CC_DATA_TOGGLE:
pIrp->result = S_usbHcdLib_DATA_TOGGLE_FAULT;
break;
case OHCI_CC_PID_CHECK:
case OHCI_CC_UNEXPECTED_PID:
pIrp->result = S_usbHcdLib_PID_FAULT;
break;
case OHCI_CC_DATA_OVERRUN:
case OHCI_CC_DATA_UNDERRUN:
case OHCI_CC_BFR_OVERRUN:
case OHCI_CC_BFR_UNDERRUN:
pIrp->result = S_usbHcdLib_DATA_BFR_FAULT;
break;
default:
pIrp->result = S_usbHcdLib_GENERAL_FAULT;
break;
}
/* If there was an error, then the HC will have halted the ED. Un-halt
* it so future transfers can take place.
*/
tdHead = FROM_LITTLEL (pPipe->pEd->ed.tdHead);
if (pIrp->result != PENDING)
{
pPipe->pEd->ed.tdHead = TO_LITTLEL (tdHead & ~OHCI_PTR_HALTED);
DMA_FLUSH (&pPipe->pEd->ed.tdHead, sizeof (pPipe->pEd->ed.tdHead));
}
/* If ok so far, calculate the actual amount of data transferred. */
if (pIrp->result == PENDING)
{
pBfrList = &pIrp->bfrList [pWork->bfrNo];
/* Note: In following assignment: pTd->tdg.be == pTd->tdi.be */
bfrEnd = FROM_LITTLEL (pTd->tdg.be);
if (pPipe->transferType == USB_XFRTYPE_ISOCH)
{
/* For an isoch transfer make sure each frame was executed. */
frameCount = OHCI_TICTL_FC (control);
/*
* Re-swap packet status words before reading if they are swapped
* at transmit.
* By default USB_BUFFER_SWAP is a no-op, but it can be
* overridden in BSP stubs as necessary.
*/
USB_BUFFER_SWAP ((pVOID *) &pTd->tdi.psw [0],
frameCount * sizeof (UINT16));
for (actLen = i = 0; i < frameCount; i++)
{
psw = FROM_LITTLEW (pTd->tdi.psw [i]);
if (OHCI_TIPSW_CC (psw) != OHCI_CC_NO_ERROR)
{
pIrp->result = S_usbHcdLib_ISOCH_FAULT;
break;
}
}
actLen = bfrEnd - pTd->sw.curBfrPtr + 1;
}
else
{
/* For a general transfer, calculate the actual data transferred */
curBfrPtr = FROM_LITTLEL (pTd->tdg.cbp);
if (pTd->sw.curBfrPtr == 0)
{
/* we requested a 0-length transfer */
actLen = 0;
}
else
{
/* we requested a transfer of non-zero length */
if (curBfrPtr == 0)
{
/* transfer was successful...full length requested. */
actLen = bfrEnd - pTd->sw.curBfrPtr + 1;
}
else
{
/* short packet was transferred. curBfrPtr addresses
* the byte following the last byte transferred.
*/
actLen = curBfrPtr - pTd->sw.curBfrPtr;
}
}
/* Update the IRP's data toggle. */
pIrp->dataToggle = ((tdHead & OHCI_PTR_TGL_CARRY) == 0) ?
USB_DATA0 : USB_DATA1;
}
/* Update the BFR_LIST entry and check for underrun. */
pBfrList->actLen += actLen;
if (pTd->sw.curBfrPtr != 0 &&
actLen < bfrEnd - pTd->sw.curBfrPtr + 1)
{
underrun = TRUE;
if ((pIrp->flags & USB_FLAG_SHORT_FAIL) != 0)
{
pIrp->result = S_usbHcdLib_SHORT_PACKET;
}
}
if (pBfrList->actLen == pBfrList->bfrLen || underrun)
{
pWork->bfrNo++;
pWork->bfrOffset = 0;
pWork->zeroLenMapped = FALSE;
}
}
/* Indicate that the TD is no longer in use. */
memset (pTd, 0, sizeof (*pTd));
pPipe->freeTdCount++;
/* Check if the IRP is complete. */
if (pIrp->result == PENDING)
{
if (pWork->bfrNo == pIrp->bfrCount)
{
/* Yes, all buffers for the IRP have been processed. */
pIrp->result = OK;
}
else
{
/* No, schedule more work for the IRP. */
assignTds (pHost, pPipe, pIrp, pWork);
}
}
/* If this is an isochronous pipe and if we've fully mapped the current
* IRP, then check if there are other IRPs which need to be scheduled.
*/
if (pPipe->transferType == USB_XFRTYPE_ISOCH &&
((pWork->bfrNo + 1 == pIrp->bfrCount &&
pWork->bfrOffset ==
pIrp->bfrList [pWork->bfrNo].bfrLen) ||
pWork->bfrNo == pIrp->bfrCount))
{
scheduleOtherIrps (pHost, pPipe);
}
/* If the IRP is complete, invalidate the cache for any input buffers. */
if (pIrp->result != PENDING)
{
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);
/*
* By default USB_BUFFER_SWAP is a no-op, but can be
* overridden in BSP stubs as necessary.
*/
USB_BUFFER_SWAP ((pVOID *) pBfrList->pBfr, pBfrList->actLen);
}
}
}
}
/***************************************************************************
*
* unscheduleIrp - unschedules IRP
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleIrp
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
pHCD_PIPE pPipe = pWork->pPipe;
UINT16 i;
UINT16 tdIndex;
BOOL tdDeleted;
/* If the IRP has no workspace, then it cannot be using TDs, so return
* immediately.
*/
if (pWork == NULL)
return;
/* Halt processing on this ED. */
pPipe->pEd->ed.control |= TO_LITTLEL (OHCI_EDCTL_SKIP);
DMA_FLUSH (&pPipe->pEd->ed.control, sizeof (pPipe->pEd->ed.control));
hcSynch (pHost);
/* Search the pipe which owns this IRP to see if any of its TDs are
* in use by this IRP. If so, release them and allow another IRP (if
* one exists) to use them.
*/
tdIndex = pPipe->freeTdIndex;
for (i = 0; i < pPipe->tdCount; i++)
{
tdDeleted = FALSE;
if (pPipe->pTds [tdIndex].sw.pWork == pWork)
{
pPipe->freeTdCount++;
memset (&pPipe->pTds [tdIndex], 0, sizeof (pPipe->pTds [tdIndex]));
tdDeleted = TRUE;
}
if (++tdIndex == pPipe->tdCount)
tdIndex = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -