📄 usbhcdsl811hslib.c
字号:
*
* Schedules an isochronous transfer for the first time.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleIsochTransfer
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
/* Isochronous transfers are scheduled immediately if the
* USB_IRP.flag USB_FLAG_ISO_ASAP is set else beginning in the
* frame specified by USB_IRP.startFrame. The number of frames
* to be scheduled is equal to the number of TDs in the
* IRP_WORKSPACE.
*/
if ((pIrp->flags & USB_FLAG_ISO_ASAP) != 0)
pWork->isochNext = FINDEX (getFrameNo (pHost) + UHC_FRAME_SKIP);
else
pWork->isochNext = FINDEX (pIrp->startFrame);
pWork->isochCount = 0;
scheduleIsochFrames (pHost, pIrp, pWork);
}
/***************************************************************************
*
* unscheduleInterruptTransfer - removes interrupt transfer from work list
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleInterruptTransfer
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
pQH_WRAPPER pCurrentQh;
pQH_WRAPPER pNextQh;
UINT16 i;
/* We need to search the list of interrupt QHs to find any references
* to the indicated QH and purge each one.
*/
for (i = 0; i < UHCI_FRAME_LIST_ENTRIES; i += pWork->interval)
{
pCurrentQh = &pHost->pIntAnchorQhs [i];
while ((pNextQh = QH_FROM_PCIPTR (pCurrentQh->qh.qhLink))
!= pHost->pCtlAnchorQh &&
pNextQh->sw.pWork->interval >= pWork->interval &&
pNextQh != pWork->pQh)
{
pCurrentQh = pNextQh;
}
if (pNextQh == pWork->pQh)
{
/* We found a reference to this QH. Modify the QH list to
* skip over it. NOTE: pNextQh == pWork->pQh. */
pCurrentQh->qh.qhLink = pNextQh->qh.qhLink;
DMA_FLUSH (&pCurrentQh->qh.qhLink, sizeof (pCurrentQh->qh.qhLink));
}
}
}
/***************************************************************************
*
* scheduleInterruptTransfer - inserts interrupt IRP in frame list
*
* Schedules an interrupt transfer repeatedly in the frame list as
* indicated by the service interval.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleInterruptTransfer
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
pQH_WRAPPER pExistingQh;
pQH_WRAPPER pNextQh;
UINT16 i;
/* Calculate the service interval we'll actually use. */
pWork->interval = calcIntInterval (pWork->pPipe->interval);
/* Schedule interrupt transfer periodically throughout frame list */
for (i = 0; i < UHCI_FRAME_LIST_ENTRIES; i += pWork->interval)
{
/* Find the appropriate point in the list of interrupt QHs for this
* frame into which to schedule this QH. This QH needs to be
* scheduled ahead of all interrupt QHs which are serviced more
* frequently (ie., least frequently serviced QHs come first).
*/
pExistingQh = &pHost->pIntAnchorQhs [i];
while ((pNextQh = QH_FROM_PCIPTR (pExistingQh->qh.qhLink))
!= pHost->pCtlAnchorQh &&
pNextQh->sw.pWork->interval >= pWork->interval &&
pNextQh != pWork->pQh)
{
pExistingQh = pNextQh;
}
if (pNextQh != pWork->pQh)
{
if (i == 0)
{
pWork->pQh->qh.qhLink = pExistingQh->qh.qhLink;
DMA_FLUSH (&pWork->pQh->qh, sizeof (pWork->pQh->qh));
}
pExistingQh->qh.qhLink = QH_TO_PCIPTR (pWork->pQh);
DMA_FLUSH (&pExistingQh->qh.qhLink, sizeof (pExistingQh->qh.qhLink));
}
}
}
/***************************************************************************
*
* unscheduleControlTransfer - removes control transfer from work list
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleControlTransfer
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
pQH_WRAPPER pCurrentQh;
pQH_WRAPPER pNextQh;
/* Find the control transfer in the work list and unlink it. */
pCurrentQh = pHost->pCtlAnchorQh;
while ((pNextQh = QH_FROM_PCIPTR (pCurrentQh->qh.qhLink)) != pWork->pQh)
{
pCurrentQh = pNextQh;
}
if (pNextQh == pWork->pQh)
{
/* Bipass pWork->pQh. */
pCurrentQh->qh.qhLink = pWork->pQh->qh.qhLink;
DMA_FLUSH (&pCurrentQh->qh.qhLink, sizeof (pCurrentQh->qh.qhLink));
if (pHost->pLastCtlQh == pWork->pQh)
pHost->pLastCtlQh = pCurrentQh;
}
}
/***************************************************************************
*
* scheduleControlTransfer - inserts control IRP in frame list
*
* Inserts the control transfer into the portion of the frame list
* appropriate for control transfers.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleControlTransfer
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
pQH_WRAPPER pExistingQh;
pQH_WRAPPER pNextQh;
/* Low speed control transfers must always be scheduled before full
* speed control transfers in order to ensure that they execute
* completely within a specified frame.
*/
if (pWork->pPipe->speed == USB_SPEED_LOW)
{
/* Insert this low speed transfer after currently scheduled low
* speed transfers and ahead of any high speed transfers.
*/
pExistingQh = pHost->pCtlAnchorQh;
while ((pNextQh = QH_FROM_PCIPTR (pExistingQh->qh.qhLink))
!= pHost->pBulkAnchorQh &&
pNextQh->sw.pWork->pPipe->speed == USB_SPEED_LOW)
{
pExistingQh = pNextQh;
}
}
else
{
/* This is a full speed transfer. Add the QH to the end of the list
* of control QHs.
*/
pExistingQh = pHost->pLastCtlQh;
}
/* Set this QH's forward link to pExistingQh's forward link */
if ((pWork->pQh->qh.qhLink = pExistingQh->qh.qhLink) ==
pHost->pciPtrToBulkAnchorQh)
{
pHost->pLastCtlQh = pWork->pQh;
}
DMA_FLUSH (&pWork->pQh->qh, sizeof (pWork->pQh->qh));
/* Set the forward link of the previous QH to this QH. */
pExistingQh->qh.qhLink = QH_TO_PCIPTR (pWork->pQh);
DMA_FLUSH (&pExistingQh->qh.qhLink, sizeof (pExistingQh->qh.qhLink));
}
/***************************************************************************
*
* unscheduleBulkTransfer - removes bulk transfer from work list
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleBulkTransfer
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
pQH_WRAPPER pCurrentQh;
pQH_WRAPPER pNextQh;
/* Find the bulk transfer in the work list and unlink it. */
pCurrentQh = pHost->pBulkAnchorQh;
while ((pNextQh = QH_FROM_PCIPTR (pCurrentQh->qh.qhLink)) != pWork->pQh)
{
pCurrentQh = pNextQh;
}
if (pNextQh == pWork->pQh)
{
/* Bipass pWork->pQh. */
if (ENABLE_BANDWIDTH_RECLAMATION (pHost))
{
if (pCurrentQh == pHost->pBulkAnchorQh &&
pNextQh->qh.qhLink == pHost->pciPtrToBulkAnchorQh)
{
pCurrentQh->qh.qhLink = UHC_END_OF_LIST;
}
else
{
pCurrentQh->qh.qhLink = pNextQh->qh.qhLink;
}
}
else
{
pCurrentQh->qh.qhLink = pNextQh->qh.qhLink;
}
DMA_FLUSH (&pCurrentQh->qh.qhLink, sizeof (pCurrentQh->qh.qhLink));
if (pHost->pLastBulkQh == pNextQh)
pHost->pLastBulkQh = pCurrentQh;
}
}
/***************************************************************************
*
* scheduleBulkTransfer - inserts bulk IRP in frame list
*
* Inserts the bulk transfer into the portion of the frame list appropriate
* for bulk transfers.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleBulkTransfer
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
/* Set the forward link of this QH to the forward link of the
* QH which is currently last on the bulk list.
*/
if (ENABLE_BANDWIDTH_RECLAMATION (pHost))
{
if (pHost->pLastBulkQh->qh.qhLink == UHC_END_OF_LIST)
pWork->pQh->qh.qhLink = pHost->pciPtrToBulkAnchorQh;
else
pWork->pQh->qh.qhLink = pHost->pLastBulkQh->qh.qhLink;
}
else
{
pWork->pQh->qh.qhLink = pHost->pLastBulkQh->qh.qhLink;
}
DMA_FLUSH (&pWork->pQh->qh, sizeof (pWork->pQh->qh));
/* Set the forward link of the previous QH to this QH. */
pHost->pLastBulkQh->qh.qhLink = QH_TO_PCIPTR (pWork->pQh);
DMA_FLUSH (&pHost->pLastBulkQh->qh.qhLink,
sizeof (pHost->pLastBulkQh->qh.qhLink));
pHost->pLastBulkQh = pWork->pQh;
}
/***************************************************************************
*
* unscheduleIrp - Removes QH/TDs for IRP from work UHC work lists
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleIrp
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
pIRP_WORKSPACE pWork
)
{
/* un-Scheduling proceeds differently for each transaction type. */
switch (pWork->pPipe->transferType)
{
case USB_XFRTYPE_ISOCH:
unscheduleIsochTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_INTERRUPT:
unscheduleInterruptTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_CONTROL:
unscheduleControlTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_BULK:
unscheduleBulkTransfer (pHost, pIrp, pWork);
break;
}
}
/***************************************************************************
*
* scheduleIrp - try to schedule an IRP in the frame list
*
* RETURNS: N/A
*/
LOCAL VOID scheduleIrp
(
pHCD_HOST pHost,
pUSB_IRP pIrp
)
{
pIRP_WORKSPACE pWork = (pIRP_WORKSPACE) pIrp->hcdPtr;
pHCD_PIPE pPipe = pWork->pPipe;
/* prepare TDs */
assignTds (pHost, pIrp, pWork);
/* Mark the time the IRP was first scheduled. */
if (!pWork->irpRunning)
{
pWork->irpRunning = TRUE;
pWork->startTime = OSS_TIME ();
}
/* Scheduling proceeds differently for each transaction type. */
switch (pPipe->transferType)
{
case USB_XFRTYPE_ISOCH:
scheduleIsochTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_INTERRUPT:
scheduleInterruptTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_CONTROL:
scheduleControlTransfer (pHost, pIrp, pWork);
break;
case USB_XFRTYPE_BULK:
scheduleBulkTransfer (pHost, pIrp, pWork);
break;
}
}
/***************************************************************************
*
* rescheduleTds - Examine the given IRP and see if it needs servicing
*
* <pIrp> is an IRP which may/may not have been assigned QHs and TDs and
* currently be schedule for execution. Examine the <pIrp> and its
* associated USB_WORKSPACE in <pWork> (which might be NULL). If no work
* has been scheduled or if no TDs have been completed by the UHC, then
* we do nothing. If there are TDs which have been completed, then we
* release them to the "free" pool. If work remains to be done, then we
* reschedule the TDs. If no work remains to be done or if there is an
* error, then we report that the IRP is complete.
*
* RETURNS: OK = IRP completed ok
* PENDING = IRP still being executed
* S_usbHcdLib_xxx = IRP fai
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -