📄 usbhcdohcilib.c
字号:
/* If we deleted this TD, then advance head of TD list. */
if (tdDeleted)
{
pPipe->pEd->ed.tdHead =
TO_LITTLEL (TO_PCIPTR (&pPipe->pTds [tdIndex]));
DMA_FLUSH (&pPipe->pEd->ed.tdHead, sizeof (pPipe->pEd->ed.tdHead));
}
}
/* Allow the ED to resume. */
pPipe->pEd->ed.control &= ~(TO_LITTLEL (OHCI_EDCTL_SKIP));
DMA_FLUSH (&pPipe->pEd->ed.control, sizeof (pPipe->pEd->ed.control));
}
/***************************************************************************
*
* serviceBusIrps - Services completed USB transactions
*
* RETURNS: N/A
*/
LOCAL VOID serviceBusIrps
(
pHCD_HOST pHost
)
{
UINT32 pciPtrToTd;
pTD_WRAPPER pFirstTd;
pTD_WRAPPER pTd;
pUSB_IRP pIrp;
pIRP_WORKSPACE pWork;
LIST_HEAD completeIrps = {0};
/* Walk the list of complete TDs and update the status of their
* respective IRPs.
*
* NOTE: TDs are in reverse order on the list...That is, the TDs
* which executed first are behind TDs which executed later. We
* need to process them in the order in which they executed.
*/
DMA_INVALIDATE (&pHost->pHcca->doneHead, sizeof (pHost->pHcca->doneHead));
pFirstTd = NULL;
pciPtrToTd = pHost->pHcca->doneHead & TO_LITTLEL (OHCI_PTR_MEM_MASK);
while ((pTd = TD_FROM_PCIPTR (pciPtrToTd)) != NULL)
{
DMA_INVALIDATE (pTd, sizeof (*pTd));
pTd->sw.doneLink = pFirstTd;
pFirstTd = pTd;
pciPtrToTd = pTd->tdg.nextTd & TO_LITTLEL (OHCI_PTR_MEM_MASK);
}
/* pFirstTd now points to a list of TDs in the order in which they
* were completed.
*/
while (pFirstTd != NULL)
{
pTd = pFirstTd;
pFirstTd = pTd->sw.doneLink;
pIrp = pTd->sw.pWork->pIrp;
releaseTd (pHost, pTd->sw.pWork->pPipe, pIrp, pTd->sw.pWork, pTd);
if (pIrp->result != PENDING)
{
/* IRP is finished...move it to the list of completed IRPs. */
usbListUnlink (&pIrp->hcdLink);
usbListLink (&completeIrps, pIrp, &pIrp->hcdLink, LINK_TAIL);
}
}
/* Invoke IRP callbacks for all completed IRPs. */
while ((pIrp = usbListFirst (&completeIrps)) != NULL)
{
usbListUnlink (&pIrp->hcdLink);
/* See if other IRPs can take advantage of freed TDs. */
pWork = (pIRP_WORKSPACE) pIrp->hcdPtr;
if (pIrp->result != OK)
unscheduleIrp (pHost, pIrp, pWork);
scheduleOtherIrps (pHost, pWork->pPipe);
/* Release IRP workspace and invoke callback. */
freeIrpWorkspace (pHost, pIrp);
setIrpResult (pIrp, pIrp->result);
}
}
/***************************************************************************
*
* scheduleIrp - schedules IRP for execution
*
* RETURNS: N/A
*/
LOCAL VOID scheduleIrp
(
pHCD_HOST pHost,
pHCD_PIPE pPipe,
pUSB_IRP pIrp
)
{
pIRP_WORKSPACE pWork = (pIRP_WORKSPACE) pIrp->hcdPtr;
/* Mark the time the IRP started */
pWork->irpRunning = TRUE; /* running... */
pWork->startTime = OSS_TIME (); /* and its start time */
/* Assign starting frame number for isoch transfers */
if (pPipe->transferType == USB_XFRTYPE_ISOCH)
{
if ((pIrp->flags & USB_FLAG_ISO_ASAP) != 0)
pWork->isochNext = FINDEX (getFrameNo (pHost) + HC_FRAME_SKIP);
else
pWork->isochNext = FINDEX (pIrp->startFrame);
}
/* Assign TDs to direct data transfer. */
assignTds (pHost, pPipe, pIrp, pWork);
}
/***************************************************************************
*
* cancelIrp - cancel's an outstanding IRP
*
* If the IRP's result code is not PENDING, then we cannot cancel
* the IRP as it has either already completed or it is not yet enqueued.
*
* RETURNS: OK if IRP canceled
* S_usbHcdLib_CANNOT_CANCEL if IRP already completed
*/
LOCAL int cancelIrp
(
pHCD_HOST pHost,
pUSB_IRP pIrp,
int result /* error to assign to IRP */
)
{
pIRP_WORKSPACE pWork;
pHCD_PIPE pPipe;
int s = OK;
if (pIrp->result != PENDING)
s = S_usbHcdLib_CANNOT_CANCEL;
else
{
/* The IRP is pending. Unlink it. */
usbListUnlink (&pIrp->hcdLink);
/* If this IRP is a "bus" IRP - as opposed to a root IRP - then
* remove it from the HC's work list.
*/
if ((pWork = (pIRP_WORKSPACE) pIrp->hcdPtr) != NULL &&
pWork->pPipe->busAddress != pHost->rootAddress)
{
/* Remove QHs/TDs from the work list and release workspace. */
pPipe = pWork->pPipe;
unscheduleIrp (pHost, pIrp, pWork);
freeIrpWorkspace (pHost, pIrp);
/* This may create an opportunity to schedule other IRPs. */
scheduleOtherIrps (pHost, pPipe);
}
setIrpResult (pIrp, result);
}
return s;
}
/***************************************************************************
*
* shutOffListProcessing - shut of host controller interrupts for list proc.
*
* Permits the host controller from processing the periodic, interrupt and bulk
* TD lists.
*
* RETURNS: N/A.
*/
LOCAL void shutOffListProcessing
(
pHCD_HOST pHost
)
{
UINT32 controlRegChange;
hcSynch (pHost);
/*
* Read the register and mask off the bits that denote list processing
* is enabled
*/
/* Periodic List = Isoc transfers */
controlRegChange = HC_DWORD_IN (OHCI_HC_CONTROL) & (OHCI_CTL_PLE);
pHost->pHcControlReg = controlRegChange;
HC_CLR_BITS (OHCI_HC_CONTROL, pHost->pHcControlReg);
/* Interrupt List = Interrupt transfers */
controlRegChange = HC_DWORD_IN (OHCI_HC_CONTROL) &(OHCI_CTL_IE);
pHost->pHcControlReg = controlRegChange;
HC_CLR_BITS (OHCI_HC_CONTROL, pHost->pHcControlReg);
/* Bulk List = Bulk transfers */
controlRegChange = HC_DWORD_IN (OHCI_HC_CONTROL) & OHCI_CTL_BLE;
pHost->pHcControlReg = controlRegChange;
HC_CLR_BITS (OHCI_HC_CONTROL, pHost->pHcControlReg);
}
/***************************************************************************
*
* isHubStatus - checks status on root hub
*
* RETURNS: hub and port status bitmap as defined by USB
*/
LOCAL UINT8 isHubStatus
(
pHCD_HOST pHost
)
{
UINT8 hubStatus = 0;
UINT16 port;
UINT32 portRegOffset;
UINT32 portChange;
UINT32 hcRhPortStatusReg;
for (port = 0; port < pHost->numPorts; port++)
{
portRegOffset = OHCI_HC_RH_PORT_STATUS + port * sizeof (UINT32);
/* Read & clear pending change status */
hcRhPortStatusReg = HC_DWORD_IN (portRegOffset);
portChange = hcRhPortStatusReg &
(OHCI_RHPS_CSC |
OHCI_RHPS_PESC |
OHCI_RHPS_PSSC |
OHCI_RHPS_OCIC |
OHCI_RHPS_PRSC);
if (portChange != 0)
HC_DWORD_OUT (portRegOffset, portChange);
hcSynch (pHost);
/* Combine the change bits reported by the HC with those we
* already know about. Then report if a change is pending.
*/
pHost->pRhPortChange [port] |= portChange;
if ((pHost->pRhPortChange [port] &
(OHCI_RHPS_CSC |
OHCI_RHPS_PESC |
OHCI_RHPS_PSSC |
OHCI_RHPS_OCIC |
OHCI_RHPS_PRSC)) != 0)
{
hubStatus |= USB_HUB_ENDPOINT_STS_PORT0 << port;
}
/* there was a change - a device has been plugged, or unplugged */
if ( (hcRhPortStatusReg & OHCI_RHPS_CSC) !=0)
/* A device has been unplugged */
if ( (hcRhPortStatusReg & OHCI_RHPS_CCS) == 0 )
{
/* Stop the host controller from processing TD's */
shutOffListProcessing (pHost);
}
}
return hubStatus;
}
/***************************************************************************
*
* serviceRootIrps - Services root hub transactions
*
* RETURNS: N/A
*/
LOCAL VOID serviceRootIrps
(
pHCD_HOST pHost
)
{
UINT8 hubStatus;
UINT16 irpCount;
pUSB_IRP pIrp;
if ((hubStatus = isHubStatus (pHost)) != 0)
{
for (irpCount = pHost->rootIrpCount; irpCount > 0; irpCount--)
{
pIrp = usbListFirst (&pHost->rootIrps);
--pHost->rootIrpCount;
usbListUnlink (&pIrp->hcdLink);
*(pIrp->bfrList [0].pBfr) = hubStatus;
pIrp->bfrList [0].actLen = 1;
setIrpResult (pIrp, OK);
}
}
}
/***************************************************************************
*
* processInterrupt - process a hardware interrupt from the HC
*
* Determine the cause of a hardware interrupt and service it. If the
* interrupt results in the completion of one or more IRPs, handle the
* completion processing for those IRPs.
*
* RETURNS: N/A
*/
LOCAL VOID processInterrupt
(
pHCD_HOST pHost
)
{
UINT32 intBits = HC_DWORD_IN (OHCI_HC_INT_STATUS) & INT_ENABLE_MASK;
/* evaluate the interrupt condition */
if ((intBits & OHCI_INT_SO) != 0)
{
/* Scheduling overrun detected. Record it. */
pHost->errScheduleOverrun++;
HC_DWORD_OUT (OHCI_HC_INT_STATUS, OHCI_INT_SO); /* acknowledge */
}
if ((intBits & OHCI_INT_UE) != 0)
{
/* Unrecoverable error detected. Record it. */
pHost->errUnrecoverable++;
HC_DWORD_OUT (OHCI_HC_INT_STATUS, OHCI_INT_UE); /* acknowledge */
}
if ((intBits & OHCI_INT_RD) != 0)
{
/* Resume detected. */
if (pHost->mngmtCallback != NULL)
(*pHost->mngmtCallback) (pHost->mngmtCallbackParam,
pHost->handle,
0 /* bus number */,
HCD_MNGMT_RESUME);
HC_DWORD_OUT (OHCI_HC_INT_STATUS, OHCI_INT_RD); /* acknowledge */
}
if ((intBits & OHCI_INT_RHSC) != 0)
{
/* Root host change detected. */
serviceRootIrps (pHost);
HC_DWORD_OUT (OHCI_HC_INT_STATUS, OHCI_INT_RHSC); /* acknowledge */
}
if ((intBits & OHCI_INT_WDH) != 0)
{
/* The HC has updated the done queue. This implies completion
* of one or more USB transactions detected.
*/
serviceBusIrps (pHost);
pHost->pHcca->doneHead = 0;
HC_DWORD_OUT (OHCI_HC_INT_STATUS, OHCI_INT_WDH); /* acknowledge */
}
/* Re-enable HC interrupts */
HC_DWORD_OUT (OHCI_HC_INT_ENABLE, INT_ENABLE_MASK);
}
/***************************************************************************
*
* checkIrpTimeout - Checks for IRPs which may have timed-out
*
* RETURNS: N/A
*/
LOCAL VOID checkIrpTimeout
(
pHCD_HOST pHost /* host to check */
)
{
pUSB_IRP pIrp;
pUSB_IRP pNextIrp;
pIRP_WORKSPACE pWork;
UINT32 now;
/* Search for one or more IRPs which have been completed
* (or partially completed).
*/
now = OSS_TIME ();
pIrp = usbListFirst (&pHost->busIrps);
while (pIrp != NULL)
{
pNextIrp = usbListNext (&pIrp->hcdLink);
pWork = (pIRP_WORKSPACE) pIrp->hcdPtr;
if (pIrp->timeout != USB_TIMEOUT_NONE && pWork->irpRunning &&
now - pWork->startTime > pIrp->timeout)
{
/* This IRP has exceeded its run time. */
cancelIrp (pHost, pIrp, S_usbHcdLib_TIMEOUT);
}
pIrp = pNextIrp;
}
}
/***************************************************************************
*
* intThread - Thread invoked when hardware interrupts are detected
*
* By convention, the <param> to this thread is a pointer to an HCD_HOST.
* This thread waits on the intPending semaphore in the HCD_HOST which is
* signalled by the actual interrupt handler. This thread will continue
* to process interrupts until intThreadExitRequest is set
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -