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