📄 chw.cpp
字号:
// this allows us not to have to alert the PDD that the addresses have changed.
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;
}
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;
}
#ifdef DEBUG
USHORT usbsts = Read_USBSTS();
DWORD dwFrame;
GetFrameNumber(&dwFrame); // calls UpdateFrameCounter
DEBUGMSG( ZONE_REGISTERS, (TEXT("!!!interrupt!!!! on frame index + 1 = 0x%08x, USBSTS = 0x%04x\n"), dwFrame, usbsts ) );
#else
UpdateFrameCounter();
#endif // DEBUG
Clear_USBSTS( );
// TODO - differentiate between USB interrupts, which are
// for transfers, and host interrupts (UHCI spec 2.1.2).
// For the former, we need to call CPipe::SignalCheckForDoneTransfers.
// For the latter, we need to call whoever will handle
// resume/error processing.
// For now, we just notify CPipe so that transfers
// can be checked for completion
// This flag gets cleared in the resume thread.
if(g_fPowerUpFlag)
{
if (m_bDoResume) {
g_fPowerUpFlag=FALSE;
// UHCI 2.1.1
WORD wUSBCmd = Read_USBCMD();
Sleep(20);
Write_USBCMD(wUSBCmd & ~UHCD_USBCMD_FORCE_GLOBAL_RESUME);
}
else {
if (g_fPowerResuming) {
// this means we've restarted an IST and it's taken an early interrupt;
// just pretend it didn't happen for now because we're about to be told to exit again.
continue;
}
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;
}
}
else
//CPipe::SignalCheckForDoneTransfers( );
SignalCheckForDoneTransfers( );
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 UHCI frame number register is only 11 bits, or 2047
// long. Thus, the counter will wrap approx. every 2 seconds.
// That is insufficient for Isoch Transfers, which
// may need to be scheduled out into the future by more
// than 2 seconds. So, we maintain an internal 32 bit counter
// for the frame number, which will wrap in 50 days.
//
// This function should be called at least once every two seconds,
// otherwise we will miss frames.
//
// ******************************************************************
{
#ifdef DEBUG
DWORD dwTickCountLastTime = GetTickCount();
#endif
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 >= 2000 )
DEBUGMSG(1, (TEXT("!UHCI - CHW::UpdateFrameCounter missed frame count;")
TEXT(" isoch packets may have been dropped.\n")));
dwTickCountLastTime = GetTickCount();
#endif // DEBUG
// This algorithm is right out of the Win98 uhcd.c code
USHORT currentFRNUM = Read_FRNUM() & UHCD_FRNUM_MASK;
// check whether the MSB in m_frameCounterLowPart and currentFRNUM differ
if ( (m_frameCounterLowPart ^ currentFRNUM) & UHCD_FRNUM_COUNTER_MSB ) {
// Yes, they are different. Update m_frameCounterHighPart
m_frameCounterHighPart += (UHCD_FRNUM_COUNTER_MSB << 1) -
((currentFRNUM ^ m_frameCounterHighPart) & UHCD_FRNUM_COUNTER_MSB);
}
m_frameCounterLowPart = currentFRNUM;
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
// ******************************************************************
{
EnterCriticalSection( &m_csFrameCounter );
// This algorithm is right out of the Win98 uhcd.c code
UpdateFrameCounter();
DWORD frame = ((m_frameCounterLowPart & UHCD_FRNUM_INDEX_MASK) | m_frameCounterHighPart) +
((m_frameCounterLowPart ^ m_frameCounterHighPart) & UHCD_FRNUM_COUNTER_MSB);
LeaveCriticalSection( &m_csFrameCounter );
*lpdwFrameNumber=frame;
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:
// ******************************************************************
{
#ifdef CE_PREv3
// Use the hardware, Luke!
// The OS' system clock (used for scheduling) has 25ms granularity
// which is just too high.
if ((Read_USBCMD() & UHCD_USBCMD_RUN_STOP) == 0)
// We check the USBCMD register instead of the USBSTS register because
// Intel's HC can be stopped without the HCHALTED bit being set.
return FALSE;
DWORD frame0 = GetFrameNumber();
while (GetFrameNumber() == frame0)
; // spin
#else // 3.0 and later use 1ms system clock
// This isn't totally accurate (e.g., when the frame length changes)
// but it's close enough and keeps us from spinning.
Sleep(1);
#endif
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: Only part of the frame length is stored in the hardware
// register, so an offset needs to be added.
// ******************************************************************
{
*lpuFrameLength=(Read_SOFMOD() & UHCD_SOFMOD_MASK) + UHCD_SOFMOD_MINIMUM_LENGTH;
return TRUE;
}
// ******************************************************************
BOOL CHW::SetFrameLength( IN HANDLE hEvent,
IN 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.
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, 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 );
DEBUGCHK( oldValue );
}
}
return fSuccess;
}
// ******************************************************************
BOOL CHW::StopAdjustingFrame( void )
//
// Purpose: Stop modifying the host controller frame length
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -