📄 usbhcdohcilib.c
字号:
{ /* 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; } 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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -