📄 cpipe.cpp
字号:
// frame #0 ->(isoch)-> QH #4 \
// QH #2
// frame #2 ->(isoch)-> QH #6 / \
// (low speed) (full speed)
// QH # 1 - - - - QH #0 - - - - - - FinalQH
// / ^ |
// frame #1 ->(isoch)-> QH #5 \ | |
// QH #3 -------------------
// frame #3 ->(isoch)-> QH #7 / (Reclamation)
//
// etc for rest of frames
//
// The outermost QHs will be numbered i = UHCD_MAX_INTERRUPT_INTERVAL + x, where
// x = 0, 1, 2, 3, .., UHCD_MAX_INTERRUPT_INTERVAL - 1. The QH #i will be
// scheduled by any frame which has index == x (mod UHCD_MAX_INTERRUPT_INTERVAL)
//
// Note that the QHs in the kth level will be executed every
// 2^k frames. Take any number n. If 2^k <= n < 2^(k+1), then
// QH #n is placed in the tree at level k. When we want to schedule
// a new queue every 2^k frames, we can place it after any existing
// tree queues 2^k, 2^k + 1, 2^k + 2, ..., 2^(k+1) - 1
//
// Given QH #n (n > 1), if n in binary is 1x(remaining_bits)b,
// then QH #n links to QH # 01(remaining_bits)b.
// For instance, 7 = 111b links to 3 = 011b
// and also 4 = 100b links to 2 = 010b
//
// ISOCHRONOUS TDs will be placed in between the frame list and the first level
// of the interrupt QH tree.
//
// INTERRUPT transfers will be placed at the appropriate point within the QH
// tree so that they are scheduled at regular intervals.
//
// CONTROL transfers go directly after QH #1, if they are low speed, or if they
// have max packet size > MAX_RECLAMATION_PACKET_SIZE. They go after QH #0 if they
// are full speed and have max packet size <= MAX_RECLAMATION_PACKET_SIZE.
//
// BULK transfers are always high speed. If the max packet size is less than
// MAX_RECLAMATION_PACKET_SIZE, the transfer goes before FinalQH, else it
// goes *before* QH # 0
// we need 2 * UHCD_MAX_INTERRUPT_INTERVAL + 1 queue heads which
// will always remain in the schedule and never be freed
PUCHAR vaQHList = NULL;
#define CPIPE_INITIALIZE_QH_MEMORY_NEEDED DWORD( (2 * UHCD_MAX_INTERRUPT_INTERVAL + 1) * sizeof( UHCD_QH ) )
if ( !m_pCPhysMem->AllocateMemory( DEBUG_PARAM( TEXT("Permanent QHs"), )
CPIPE_INITIALIZE_QH_MEMORY_NEEDED,
&vaQHList,
CPHYSMEM_FLAG_NOBLOCK ) ) {
DEBUGMSG(ZONE_ERROR, (TEXT("-CPipe::Initialize - Could not get QHs for schedule\n")));
return FALSE;
}
#ifdef DEBUG
m_debug_QHMemoryAllocated += CPIPE_INITIALIZE_QH_MEMORY_NEEDED;
DEBUGMSG( ZONE_QH, (TEXT("CPipe::Initialize - allocate persistant QHs, total bytes = %d\n"), m_debug_QHMemoryAllocated) );
#endif // DEBUG
m_pFinalQH = (PUHCD_QH) vaQHList;
vaQHList += sizeof( UHCD_QH );
m_interruptQHTree[ 0 ] = (PUHCD_QH) vaQHList;
vaQHList += sizeof( UHCD_QH );
// m_pFinalQH is at the very end of the schedule, and points back
// to QH # 0. For now, the terminate bit is set in HLink, because
// there are no high speed bulk/control transfers scheduled
InitializeQH( m_pFinalQH, // QH to initialize
m_interruptQHTree[ 0 ], // previous QH
GetQHPhysAddr( m_interruptQHTree[ 0 ] )
| QUEUE_HEAD_LINK_POINTER_QH
| QUEUE_HEAD_LINK_POINTER_TERMINATE, // HW link to next QH
m_interruptQHTree[ 0 ] ); // next QH
// QH # 0 points to m_pFinalQH
InitializeQH( m_interruptQHTree[ 0 ], // QH to initialize
NULL, // previous QH - we'll set this below
GetQHPhysAddr( m_pFinalQH )
| QUEUE_HEAD_LINK_POINTER_QH
| QUEUE_HEAD_LINK_POINTER_VALID, // HW link to next QH
m_pFinalQH ); // next QH
#ifndef UHCD_DONT_ALLOW_RECLAMATION_FOR_CONTROL_PIPES
//
// Implement reclamation workaround from Intel PIIX4E specification update.
//
if ( !m_pCPhysMem->AllocateMemory( DEBUG_PARAM( TEXT("QH#0 Pseudo TD"), )
sizeof( UHCD_TD ),
(PUCHAR *) &m_pFinalQH->vaVertTD,
CPHYSMEM_FLAG_NOBLOCK ) ) {
DEBUGMSG( ZONE_ERROR, (TEXT("-CPipe::Initialize - no memory for QH#0 pseudo TD\n")) );
return FALSE;
}
#ifdef DEBUG
m_debug_TDMemoryAllocated += sizeof( UHCD_TD );
DEBUGMSG( ZONE_TD, (TEXT("CPipe::Initialize - alloc 1 TD, total bytes = %d\n"), m_debug_TDMemoryAllocated ) );
#endif // DEBUG
InitializeTD( m_pFinalQH->vaVertTD,
GetTDPhysAddr( m_pFinalQH->vaVertTD )
| TD_LINK_POINTER_TD
| TD_LINK_POINTER_VALID
| TD_LINK_POINTER_BREADTH_FIRST,
NULL, // vaNextTD (use NULL so destructor doesn't have to detect loops)
FALSE, FALSE, FALSE, // fIOC, fIsoch, fLowSpd
TD_OUT_PID, 0, 0, 0, // pid, addr, ep, dataTog
TD_MAXLENGTH_NULL_BUFFER, 0 // maxLen, HW_paBuffer (this TD will always be inactive)
);
m_pFinalQH->vaVertTD->Active = 0;
m_pFinalQH->HW_paVLink = GetTDPhysAddr( m_pFinalQH->vaVertTD )
| QUEUE_ELEMENT_LINK_POINTER_TD
| QUEUE_ELEMENT_LINK_POINTER_VALID;
#endif // UHCD_DONT_ALLOW_RECLAMATION_FOR_CONTROL_PIPES
for ( UCHAR pow = 1; pow <= UHCD_MAX_INTERRUPT_INTERVAL; pow <<= 1 ) {
for ( UCHAR index = pow; index < (pow << 1); index++ ) {
DEBUGCHK( m_interruptQHTree[ index ] == NULL );
m_interruptQHTree[ index ] = PUHCD_QH( vaQHList );
vaQHList += sizeof( UHCD_QH );
const UCHAR link = (index ^ pow) | (pow >> 1);
DEBUGCHK( m_interruptQHTree[ link ] != NULL );
// IMPORTANT!!! Previous fields are not set in the interruptQHTree
// structure, since there are 2 previous QHs. Instead, we use
// the fact that previous QHs can be calculated by index number.
// The previous field of one of the interruptQHtree members is not
// guaranteed to be valid anywhere.
InitializeQH( m_interruptQHTree[ index ], // QH to initialize
NULL, // previous QH not set for interrupt tree
GetQHPhysAddr( m_interruptQHTree[ link ] )
| QUEUE_HEAD_LINK_POINTER_QH
| QUEUE_HEAD_LINK_POINTER_VALID, // HW link to next QH
m_interruptQHTree[ link ] ); // next QH
// there are originally no scheduled interrupts
m_interruptQHTree[ index ]->dwInterruptTree.Load = 0;
}
}
DEBUGCHK( m_interruptQHTree[ 0 ]->vaPrevQH == NULL );
m_interruptQHTree[ 0 ]->vaPrevQH = m_interruptQHTree[ 1 ];
}
// now set up the frame list
for ( DWORD frame = 0; frame < FRAME_LIST_LENGTH; frame++ ) {
DEBUGCHK( sizeof( m_vaFrameList[ frame ] ) == sizeof( FRAME_LIST_POINTER ) );
m_vaFrameList[ frame ] = GetQHPhysAddr( m_interruptQHTree[ QHTreeEntry( frame ) ] )
| FRAME_LIST_POINTER_QH
| FRAME_LIST_POINTER_VALID;
}
// we need one persistant inactive Isoch TD to guarantee that
// we are interrupted at least once every FRAME_LIST_LENGTH frames.
// Otherwise, our internal frame counter in CHW will miss frames.
{
#define CPIPE_INITIALIZE_TD_MEMORY_NEEDED sizeof( UHCD_TD )
if ( !m_pCPhysMem->AllocateMemory(
DEBUG_PARAM( TEXT("Permanent Isoch TD"), )
CPIPE_INITIALIZE_TD_MEMORY_NEEDED,
(PUCHAR*)&m_pFrameSynchTD,
CPHYSMEM_FLAG_NOBLOCK ) ) {
DEBUGMSG( ZONE_ERROR, (TEXT("-CPipe::Initialize - no memory for Frame Synch Isoch TD\n")) );
return FALSE;
}
#ifdef DEBUG
m_debug_TDMemoryAllocated += CPIPE_INITIALIZE_TD_MEMORY_NEEDED;
DEBUGMSG( ZONE_TD, (TEXT("CPipe::Initialize - create permanent TDs, total bytes = %d\n"), m_debug_TDMemoryAllocated) );
#endif // DEBUG
InitializeTD( m_pFrameSynchTD,
m_vaFrameList[ 0 ], // HW_paLink
NULL, // vaNextTD
1, // InterruptOnComplete
1, // Isochronous
0, // Low Speed Control (not used)
TD_OUT_PID, // PID (must be valid, even though not used)
USB_MAX_ADDRESS, // Device Address (not used)
0, // Device Endpoint (not used)
0, // Data Toggle (not used)
TD_MAXLENGTH_NULL_BUFFER, // no data buffer
0 ); // physical address of data buffer
// Active is set to 1 by InitializeTD
DEBUGCHK( m_pFrameSynchTD->Active == 1 );
m_pFrameSynchTD->Active = 0;
// insert into schedule
m_vaFrameList[ 0 ] = GetTDPhysAddr( m_pFrameSynchTD )
| FRAME_LIST_POINTER_TD
| FRAME_LIST_POINTER_VALID;
}
DEBUGMSG(ZONE_INIT, (TEXT("-CUHCIFrame::Initialize. Success!\n")));
return TRUE;
}
// *****************************************************************
// Scope: public static
void CUHCIFrame::DeInitialize( void )
//
// Purpose: DeInitialize any static variables
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: This function should only be called from the ~CUhcd()
// ******************************************************************
{
#ifdef DEBUG // don't call this twice
DEBUGCHK( m_debug_fInitializeAlreadyCalled );
m_debug_fInitializeAlreadyCalled = FALSE;
#endif // DEBUG
m_fCheckTransferThreadClosing = TRUE;
// Wake up the CheckForDoneTransfersThread and give it time to die
if ( m_hCheckForDoneTransfersEvent ) {
SetEvent( m_hCheckForDoneTransfersEvent );
if ( m_hCheckForDoneTransfersThread ) {
DWORD dwWaitReturn = WaitForSingleObject( m_hCheckForDoneTransfersThread, 5000 );
if ( dwWaitReturn != WAIT_OBJECT_0 ) {
DEBUGCHK( 0 ); // check why thread is blocked
TerminateThread( m_hCheckForDoneTransfersThread, DWORD(-1) );
}
CloseHandle( m_hCheckForDoneTransfersThread );
m_hCheckForDoneTransfersThread = NULL;
}
CloseHandle( m_hCheckForDoneTransfersEvent );
m_hCheckForDoneTransfersEvent = NULL;
}
// all busy pipes should have been closed by now
DEBUGCHK( m_pBusyPipeList == NULL &&
m_debug_numItemsOnBusyPipeList == 0 );
DeleteCriticalSection( &m_csBusyPipeListLock );
DeleteCriticalSection( &m_csFrameListLock );
DeleteCriticalSection( &m_csQHScheduleLock );
m_fCheckTransferThreadClosing = FALSE;
#ifndef UHCD_DONT_ALLOW_RECLAMATION_FOR_CONTROL_PIPES
m_pCPhysMem->FreeMemory( PUCHAR(m_pFinalQH->vaVertTD),
GetTDPhysAddr( m_pFinalQH->vaVertTD ),
CPHYSMEM_FLAG_NOBLOCK );
#endif
m_pCPhysMem->FreeMemory( PUCHAR(m_pFinalQH),
GetQHPhysAddr( m_pFinalQH ),
CPHYSMEM_FLAG_NOBLOCK );
m_pFinalQH = NULL;
m_pCPhysMem->FreeMemory( PUCHAR(m_pFrameSynchTD),
GetTDPhysAddr( m_pFrameSynchTD ),
CPHYSMEM_FLAG_NOBLOCK );
m_vaFrameList = NULL;
}
// ******************************************************************
// Scope: public static
void CUHCIFrame::SignalCheckForDoneTransfers( void )
//
// Purpose: This function is called when an interrupt is received by
// the CHW class. We then signal the CheckForDoneTransfersThread
// to check for any transfers which have completed
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: DO MINIMAL WORK HERE!! Most processing should be handled by
// The CheckForDoneTransfersThread. If this procedure blocks, it
// will adversely affect the interrupt processing thread.
// ******************************************************************
{
DEBUGCHK( m_hCheckForDoneTransfersEvent && m_hCheckForDoneTransfersThread );
SetEvent( m_hCheckForDoneTransfersEvent );
}
// ******************************************************************
ULONG CALLBACK CUHCIFrame::CheckForDoneTransfersThreadStub( IN PVOID pContext)
{
return ((CUHCIFrame *)pContext)->CheckForDoneTransfersThread( );
}
// Scope: private static
ULONG CUHCIFrame::CheckForDoneTransfersThread( )
//
// Purpose: Thread for checking whether busy pipes are done their
// transfers. This thread should be activated whenever we
// get a USB transfer complete interrupt (this can be
// requested by the InterruptOnComplete field of the TD)
//
// Parameters: 32 bit pointer passed when instantiating thread (ignored)
//
// Returns: 0 on thread exit
//
// Notes:
// ******************************************************************
{
SetKMode(TRUE);
DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CPipe::CheckForDoneTransfersThread\n")) );
PPIPE_LIST_ELEMENT pPrev = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -