📄 usbhcdohcilib.c
字号:
/***************************************************************************
*
* ohciLongRead - Read a 32 bit value from the OHCI controller.
*
* RETURNS: A big-endian adjusted UINT32
*/
LOCAL UINT32 ohciLongRead
(
pHCD_HOST pHost,
UINT32 offset
)
{
CACHE_PIPE_FLUSH ();
return FROM_LITTLEL (*((volatile UINT32 *) (pHost->memBase + \
(offset) / 4)));
}
/***************************************************************************
*
* waitOnBits - wait for a word register bit to reach the desired state
*
* RETURNS: TRUE if successful, else FALSE if desired bit state not detected
*/
LOCAL BOOL waitOnBits
(
pHCD_HOST pHost,
UINT32 p,
UINT32 bitMask,
UINT32 bitState
)
{
UINT32 start = OSS_TIME ();
BOOL desiredState;
while (!(desiredState = ((HC_DWORD_IN (p) & bitMask) == bitState)) && \
OSS_TIME () - start < BIT_TIMEOUT)
;
return desiredState;
}
/***************************************************************************
*
* getFrameNo - returns current hardware frame number
*
* RETURNS: value of current frame number
*/
LOCAL UINT16 getFrameNo
(
pHCD_HOST pHost
)
{
return OHCI_FMN_FN (HC_DWORD_IN (OHCI_HC_FM_NUMBER));
}
/***************************************************************************
*
* setFrameInterval - sets OHCI frame interval
*
* RETURNS: N/A
*/
LOCAL VOID setFrameInterval
(
pHCD_HOST pHost,
UINT16 sofInterval
)
{
UINT32 fmi;
fmi = HC_DWORD_IN (OHCI_HC_FM_INTERVAL) & ~OHCI_FMI_FIT;
fmi ^= OHCI_FMI_FIT;
fmi |= OHCI_FMI_FI_FMT (sofInterval);
fmi |= OHCI_FMI_FSMPS_FMT (((sofInterval - MAX_FRAME_OVERHEAD) * 6) / 7);
HC_DWORD_OUT (OHCI_HC_FM_INTERVAL, fmi);
pHost->sofInterval = sofInterval;
}
/***************************************************************************
*
* hcSynch - give the host controller a chance to synchronize
*
* RETURNS: N/A
*/
LOCAL VOID hcSynch
(
pHCD_HOST pHost
)
{
UINT16 currentFrameNo = getFrameNo (pHost);
while (getFrameNo (pHost) == currentFrameNo);
semTake (pHost->hcSyncSem, 1);
semTake (pHost->hcSyncSem, 1);
}
/***************************************************************************
*
* setIrpResult - sets result in IRP and executes IRP callback
*
* RETURNS: value from Irp result field
*/
LOCAL int setIrpResult
(
pUSB_IRP pIrp,
int result
)
{
if (result != PENDING)
{
pIrp->result = result;
if (pIrp->usbdCallback != NULL)
(*pIrp->usbdCallback) (pIrp);
else if (pIrp->userCallback != NULL)
(*pIrp->userCallback) (pIrp);
}
return result;
}
/***************************************************************************
*
* calcIntInterval - calculates the scheduling interval for interrupt transfer
*
* RETURNS: Actual interval to be used for interrupt transfer
*/
LOCAL UINT16 calcIntInterval
(
UINT16 interval /* 1 <= requested interval <= OHCI_INT_ED_COUNT */
)
{
UINT16 i;
/* Select an interval which is the largest power of two less than or
* equal to the requested interval.
*/
for (i = 2; i < OHCI_INT_ED_COUNT + 1; i <<= 1)
{
if (i > interval)
break;
}
return i >> 1;
}
/***************************************************************************
*
* freeIrpWorkspace - releases IRP_WORKSPACE
*
* Releases the IRP_WORKSPACE associated with an IRP (if there is one).
*
* RETURNS: N/A
*/
LOCAL VOID freeIrpWorkspace
(
pHCD_HOST pHost,
pUSB_IRP pIrp
)
{
pIRP_WORKSPACE pWork = (pIRP_WORKSPACE) pIrp->hcdPtr;
if (pWork != NULL)
{
OSS_FREE (pWork);
pIrp->hcdPtr = NULL;
}
}
/***************************************************************************
*
* allocIrpWorkspace - creates workspace for IRP
*
* Creates an IRP_WORKSPACE structure to manage the IRP data transfer.
*
* RETURNS: TRUE if successful, else FALSE
*/
LOCAL BOOL allocIrpWorkspace
(
pHCD_HOST pHost,
pHCD_PIPE pPipe,
pUSB_IRP pIrp
)
{
pIRP_WORKSPACE pWork;
/* Allocate IRP_WORKSPACE */
if ((pWork = OSS_CALLOC (sizeof (*pWork))) == NULL)
return FALSE;
pIrp->hcdPtr = pWork;
pWork->pPipe = pPipe;
pWork->pIrp = pIrp;
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;
}
}
/***************************************************************************
*
* unscheduleIsochPipe - removes an isoch pipe from the schedule
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleIsochPipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
pUINT32 pPciPtrToEd;
pED_WRAPPER pCurEd;
/* Note: Instead of halting periodic transfers before delinking, we
* immediately delink them and afterward wait for the next frame to start...
* This ensures that caller won't de-allocate the structures while the
* HC may still be referencing them.
*/
/* Walk the isoch. list until we find the ED to be removed. */
pPciPtrToEd = (pUINT32) &pHost->pIsochAnchorEd->ed.nextEd;
while ((pCurEd = ED_FROM_PCIPTR (*pPciPtrToEd)) != pPipe->pEd)
{
pPciPtrToEd = (pUINT32) &pCurEd->ed.nextEd;
}
*pPciPtrToEd = pPipe->pEd->ed.nextEd;
DMA_FLUSH (pPciPtrToEd, sizeof (*pPciPtrToEd));
/*
* 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);
/* Wait for the HC to make sure the structure is really released. */
hcSynch (pHost);
}
/***************************************************************************
*
* scheduleIsochPipe - inserts isoch pipe into the schedule
*
* Schedules an isochronous transfer for the first time.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleIsochPipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
pED_WRAPPER pLastEd;
pED_WRAPPER pNextEd;
/* Flush the ED out of the cache before giving it to the HC. */
DMA_FLUSH (pPipe->pEd, sizeof (*pPipe->pEd));
/* Append this ED to the end of the isoch. list. */
pLastEd = pHost->pIsochAnchorEd;
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));
}
/***************************************************************************
*
* unscheduleInterruptPipe - removes interrupt pipe from the schedule
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleInterruptPipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
pUINT32 pPciPtrToEd;
pED_WRAPPER pCurEd;
UINT16 i;
/* Note: Instead of halting periodic transfers before delinking, we
* immediately delink them and afterward wait for the next frame to start...
* This ensures that caller won't de-allocate the structures while the
* HC may still be referencing them.
*/
/* Remove the pipe from the interrupt lists. */
for (i = 0; i < OHCI_INT_ED_COUNT; i += pPipe->actInterval)
{
/* Walk this list until we find the ED to be removed. */
pPciPtrToEd = &pHost->pHcca->intEdTable [i];
while ((pCurEd = ED_FROM_PCIPTR (*pPciPtrToEd)) != pHost->pIsochAnchorEd &&
pCurEd->sw.pPipe->actInterval >= pPipe->actInterval &&
pCurEd != pPipe->pEd)
{
pPciPtrToEd = (pUINT32) &pCurEd->ed.nextEd;
}
if (pCurEd == pPipe->pEd)
{
*pPciPtrToEd = pPipe->pEd->ed.nextEd;
DMA_FLUSH (pPciPtrToEd, sizeof (*pPciPtrToEd));
}
}
/*
* 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);
/* Wait for the HC to make sure the structure is really released. */
hcSynch (pHost);
}
/***************************************************************************
*
* scheduleInterruptPipe - inserts interrupt pipe into the schedule
*
* Schedules an interrupt transfer repeatedly in the frame list as
* indicated by the service interval.
*
* RETURNS: N/A
*/
LOCAL VOID scheduleInterruptPipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
{
pUINT32 pPciPtrToEd;
pED_WRAPPER pCurEd;
UINT16 i;
/* Schedule the pipe onto the interrupt lists. */
for (i = 0; i < OHCI_INT_ED_COUNT; i += pPipe->actInterval)
{
/* Walk this list until we hit the isoch. anchor or until we
* find an pipe with a smaller interval (higher frequency).
*/
pPciPtrToEd = &pHost->pHcca->intEdTable [i];
while ((pCurEd = ED_FROM_PCIPTR (*pPciPtrToEd)) != pHost->pIsochAnchorEd &&
pCurEd->sw.pPipe->actInterval >= pPipe->actInterval &&
pCurEd != pPipe->pEd)
{
pPciPtrToEd = (pUINT32) &pCurEd->ed.nextEd;
}
if (pCurEd != pPipe->pEd)
{
if (i == 0)
{
pPipe->pEd->ed.nextEd = *pPciPtrToEd;
DMA_FLUSH (pPipe->pEd, sizeof (*pPipe->pEd));
}
*pPciPtrToEd = TO_LITTLEL (TO_PCIPTR (pPipe->pEd));
DMA_FLUSH (pPciPtrToEd, sizeof (*pPciPtrToEd));
}
}
}
/***************************************************************************
*
* unscheduleControlPipe - removes control pipe from the schedule
*
* RETURNS: N/A
*/
LOCAL VOID unscheduleControlPipe
(
pHCD_HOST pHost,
pHCD_PIPE pPipe
)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -