?? chw.cpp
字號:
DEBUGCHK( g_fPowerResuming == FALSE );
// order is important! resuming indicates that the hcd object is temporarily invalid
// while powerup simply signals that a powerup event has occurred. once the powerup
// flag is cleared, we will repeat this whole sequence should it get resignalled.
g_fPowerUpFlag = FALSE;
g_fPowerResuming = TRUE;
const PUCHAR pBufVirt = m_pMem->m_pVirtBase, pBufPhys = m_pMem->m_pPhysBase;
DWORD cb0 = m_pMem->m_cbTotal, cb1 = m_pMem->m_cbHighPri;
DeviceDeInitialize();
while (1) { // breaks out upon successful reinit of the object
m_pMem->ReInit();
if (DeviceInitialize())
break;
// getting here means we couldn't reinit the HCD object!
DEBUGMSG(ZONE_ERROR, (TEXT("USB cannot reinit the HCD at CE resume; retrying...\n")));
DeviceDeInitialize();
Sleep(15000);
}
// the hcd object is valid again. if a power event occurred between the two flag
// assignments above then the IST will reinitiate this sequence.
g_fPowerResuming = FALSE;
if (g_fPowerUpFlag)
PowerMgmtCallback(TRUE);
return 0;
}
// ******************************************************************
VOID CHW::PowerMgmtCallback( IN BOOL fOff )
//
// Purpose: System power handler - called when device goes into/out of
// suspend.
//
// Parameters: fOff - if TRUE indicates that we're entering suspend,
// else signifies resume
//
// Returns: Nothing
//
// Notes: This needs to be implemented for HCDI
// ******************************************************************
{
if ( fOff )
{
if ((GetCapability() & HCD_SUSPEND_RESUME)!= 0 ) {
m_bDoResume=TRUE;
SuspendHostController();
}
else {
m_bDoResume=FALSE;
StopHostController();
}
}
else
{ // resuming...
g_fPowerUpFlag = TRUE;
if (m_bDoResume)
ResumeHostController();
if (!g_fPowerResuming)
// can't use member data while `this' is invalid
SetInterruptEvent(m_dwSysIntr);
}
return;
}
VOID CHW::SuspendHostController()
{
if ( m_portBase != 0 ) {
m_portBase->HcControl.HCFS = HcRegisters::HcControl::HCFS_SUSPEND;
}
}
VOID CHW::ResumeHostController()
{
if ( m_portBase != 0 ) {
m_portBase->HcControl.HCFS = HcRegisters::HcControl::HCFS_RESUME;
}
}
DWORD CHW::SetCapability(DWORD dwCap)
{
m_dwCapability |= dwCap;
if ( (m_dwCapability & HCD_SUSPEND_RESUME)!=0) {
KernelIoControl(IOCTL_HAL_ENABLE_WAKE, &m_dwSysIntr, sizeof(m_dwSysIntr), NULL, 0, NULL);
if (m_portBase) {
m_portBase->HcControl.RWC=1;// OHCI 7.2.1
m_portBase->HcControl.RWE=1;
m_portBase->HcInterruptEnable.RD=1;
m_portBase->HcRhStatus.reg= HcRegisters::HcRhStatus::DRWE;
}
}
return m_dwCapability;
};
DWORD CHW::UsbInterruptThreadStub( IN PVOID context )
{
return ((CHW *)context)->UsbInterruptThread();
}
// ******************************************************************
DWORD CHW::UsbInterruptThread( )
//
// Purpose: Main IST to handle interrupts from the USB host controller
//
// Parameters: context - parameter passed in when starting thread,
// (currently unused)
//
// Returns: 0 on thread exit.
//
// Notes:
//
// This function is private
// ******************************************************************
{
DEBUGMSG(ZONE_INIT && ZONE_VERBOSE, (TEXT("+CHW::Entered USBInterruptThread\n")));
while ( !m_fUsbInterruptThreadClosing ) {
WaitForSingleObject(m_hUsbInterruptEvent, INFINITE);
if ( m_fUsbInterruptThreadClosing ) {
break;
}
// the cast is used here to remove the volatile qualifier so we can cache the register
union HcRegisters::HcInterruptStatus usbsts = const_cast<HcRegisters *>(m_portBase)->HcInterruptStatus;
#ifdef DEBUG
DWORD dwFrame;
GetFrameNumber(&dwFrame); // calls UpdateFrameCounter
DEBUGMSG( ZONE_REGISTERS, (TEXT("!!!interrupt!!!! on frame index + 1 = 0x%08x, USBSTS = 0x%08x\n"), dwFrame, usbsts.reg ) );
#else
UpdateFrameCounter();
#endif // DEBUG
// This flag gets cleared in the resume thread.
if (g_fPowerUpFlag && !g_fPowerResuming)
{
if (m_bDoResume) {
g_fPowerUpFlag=FALSE;
Sleep(20);
EnterOperationalState();
}
else {
HcdPdd_InitiatePowerUp((DWORD)m_pPddContext);
HANDLE ht;
while ((ht = CreateThread(NULL, 0, CeResumeThreadStub, this, 0, NULL)) == NULL) {
RETAILMSG(1, (TEXT("HCD IST: cannot spin a new thread to handle CE resume of USB host controller; sleeping.\n")));
Sleep(15000); // 15 seconds later, maybe it'll work.
}
CeSetThreadPriority( ht, g_IstThreadPriority );
CloseHandle(ht);
// The CE resume thread will force this IST to exit so we'll be cooperative proactively.
break;
}
}
if (usbsts.RHSC) {
}
if (usbsts.WDH) {
DWORD paDoneHead = m_pHCCA->HccaDoneHead & ~1; // ignore LSb (see OHCI figure 4-5)
m_pHCCA->HccaDoneHead = 0;
DEBUGCHK( (paDoneHead & 0xF) == 0 );
DEBUGCHK( paDoneHead != 0 ); // Check Hardware Register Value agains OHCI spec. 7.1.4
if (paDoneHead) {
SignalCheckForDoneTransfers( paDoneHead );
}
}
// Acknowledge the interrupt(s)
m_portBase->HcInterruptStatus.reg = usbsts.reg;
InterruptDone(m_dwSysIntr);
}
DEBUGMSG(ZONE_INIT && ZONE_VERBOSE, (TEXT("-CHW::Leaving USBInterruptThread\n")));
return (0);
}
// ******************************************************************
void CHW::UpdateFrameCounter( void )
//
// Purpose: Updates our internal frame counter
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: The OHCI frame number register is 16 bits long.
// Thus, the counter will wrap approx. every 64 seconds.
// We maintain an additional 16 bits internally so we
// needn't wrap for 50 days.
//
// This function should be called at least once a minute;
// otherwise we could miss frames.
//
// ******************************************************************
{
EnterCriticalSection( &m_csFrameCounter );
#ifdef DEBUG
// If this fails, we haven't been called in a long time,
// so the frame number is no longer accurate
if (GetTickCount() - dwTickCountLastTime >= 64000 )
DEBUGMSG(1, (TEXT("!UHCI - CHW::UpdateFrameCounter missed frame count;")
TEXT(" isoch packets may have been dropped.\n")));
dwTickCountLastTime = GetTickCount();
#endif // DEBUG
WORD fn = m_pHCCA->HccaFrameNumber;
if (fn < lastFn)
++m_wFrameHigh;
lastFn = fn;
LeaveCriticalSection( &m_csFrameCounter );
}
// ******************************************************************
BOOL CHW::GetFrameNumber(OUT LPDWORD lpdwFrameNumber )
//
// Purpose: Return the current frame number
//
// Parameters: None
//
// Returns: 32 bit current frame number
//
// Notes: See also comment in UpdateFrameCounter
// ******************************************************************
{
UpdateFrameCounter();
*lpdwFrameNumber = ((DWORD) m_wFrameHigh << 16) | m_pHCCA->HccaFrameNumber ;
return TRUE;
}
// ******************************************************************
BOOL CHW::WaitOneFrame( void )
//
// Purpose: Block the current thread until the HC hardware is
// no longer processing the current USB frame.
//
// Parameters: None
//
// Returns: TRUE on success, FALSE if the HW is unavailable or not running.
//
// Notes:
// ******************************************************************
{
// is the host controller operational?
if ( m_portBase != 0 ) {
if(m_portBase->HcControl.HCFS != HcRegisters::HcControl::HCFS_OPERATIONAL) {
// no, just wait a short while and return
Sleep(10);
return TRUE;
}
}
// The OS' system clock - prior to CE3.0 - has 25ms granularity
// which is just too high. Later versions have a 1ms clock.
//
// Sleep may not be enough if the frame has been made longer
// so we poll after the 1ms. If the frame has been made shorter
// then this will just take too long.
m_pHCCA->HccaPad1 = 1; // HC will write 0 here when updating FN (OHCI Figure 4-5)
#ifndef CE_PREv3
Sleep(1);
#endif
while (m_pHCCA->HccaPad1)
; // spin
return TRUE;
}
// ******************************************************************
BOOL CHW::GetFrameLength(OUT LPUSHORT lpuFrameLength)
//
// Purpose: Return the current frame length in 12 MHz clocks
// (i.e. 12000 = 1ms)
//
// Parameters: None
//
// Returns: frame length
//
// Notes:
// ******************************************************************
{
*lpuFrameLength=m_portBase->HcFmInterval.FI;
return TRUE;
}
// ******************************************************************
BOOL CHW::SetFrameLength( IN HANDLE /*hEvent*/,
IN const USHORT /*uFrameLength*/ )
//
// Purpose: Set the Frame Length in 12 Mhz clocks. i.e. 12000 = 1ms
//
// Parameters: hEvent - event to set when frame has reached required
// length
//
// uFrameLength - new frame length
//
// Returns: TRUE if frame length changed, else FALSE
//
// Notes:
// ******************************************************************
{
BOOL fSuccess = FALSE;
// to prevent multiple threads from simultaneously adjusting the
// frame length, InterlockedTestExchange is used. This is
// cheaper than using a critical section.
#ifdef JEFFRO
if ( FALSE == InterlockedTestExchange( &m_fFrameLengthIsBeingAdjusted,
FALSE, // Test value (Old value)
TRUE ) ) { // New value
// m_fFrameLengthIsBeingAdjusted was set to TRUE
// by the InterlockedTestExchange
if ( uFrameLength >= UHCD_SOFMOD_MINIMUM_LENGTH &&
uFrameLength <= UHCD_SOFMOD_MAXIMUM_LENGTH &&
hEvent != NULL ) {
// ok, all the params are fine
m_hAdjustDoneCallbackEvent = hEvent;
m_uNewFrameLength = uFrameLength;
InterlockedExchange( &m_fStopAdjustingFrameLength, FALSE );
// the frame length needs to be adjusted over
// many frames, so we need a separate thread
HANDLE hWorkerThread = CreateThread( 0, 0, UsbAdjustFrameLengthThreadStub, (LPVOID) this, 0, NULL );
if ( hWorkerThread != NULL ) {
CeSetThreadPriority( hWorkerThread, g_IstThreadPriority + RELATIVE_PRIO_ADJUST_FRAME );
CloseHandle( hWorkerThread );
hWorkerThread = NULL;
fSuccess = TRUE;
}
}
if ( !fSuccess ) {
// we didn't succeed, so change m_fFrameLengthIsBeingAdjusted
// back to FALSE
#ifdef DEBUG
LONG oldValue =
#endif // DEBUG
InterlockedExchange( &m_fFrameLengthIsBeingAdjusted, FALSE );
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -