📄 cpipe.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Module Name:
// CPipe.cpp
// Abstract:
// Implements the Pipe class for managing open pipes for UHCI
//
// CPipe (ADT)
// / \
// CQueuedPipe (ADT) CIsochronousPipe
// / | \
// / | \
// CControlPipe CInterruptPipe CBulkPipe
//
//
// Notes:
//
//
#include "cpipe.hpp"
#include "cphysmem.hpp"
#include "chw.hpp"
#include "cuhcd.hpp"
//#pragma warning (4 : 4100) // temporary, will clean later. JEFFRO.
CHCCAera::CHCCAera(IN CPhysMem* const pCPhysMem)
:m_pCPhysMem(pCPhysMem)
{
#ifdef DEBUG
m_debug_fInitializeAlreadyCalled = FALSE;
#endif // DEBUG
// schedule related vars
//CRITICAL_SECTION CPipe::m_csFrameListLock;
//PULONG CPipe::m_vaFrameList = NULL;
m_pFinalQH = NULL;
#ifdef DEBUG
m_debug_TDMemoryAllocated = 0;
m_debug_QHMemoryAllocated = 0;
m_debug_BufferMemoryAllocated = 0;
m_debug_ControlExtraMemoryAllocated = 0;
#endif // DEBUG
// Handle Done Transfers thread variables
m_pDoneHead = 0;
m_fCheckTransferThreadClosing = FALSE;
m_hCheckForDoneTransfersEvent = NULL;
m_hCheckForDoneTransfersThread = NULL;
m_pBusyPipeList = NULL;
#ifdef DEBUG
m_debug_numItemsOnBusyPipeList = 0;
#endif // DEBUG
numReclamationTransfers=0;
InitializeCriticalSection( &m_csBusyPipeListLock );
InitializeCriticalSection( &m_csQHScheduleLock );
}
CHCCAera::~CHCCAera()
{
DeInitialize();
DeleteCriticalSection( &m_csBusyPipeListLock );
// DeleteCriticalSection( &m_csFrameListLock );
DeleteCriticalSection( &m_csQHScheduleLock );
}
// *****************************************************************
// Scope: public static
BOOL CHCCAera::Initialize(IN CUhcd * const pCUhcd)
//
// Purpose: Initialize CPipe's static variables. This
// also sets up the original empty schedule
// with the frame list, and interrupt Queue Head tree.
// We also set up a thread for processing done transfers
//
// Parameters: pCPhysMem - pointer to memory manager object
//
// Returns: TRUE - if everything initialized ok
// FALSE - in case of failure
//
// Notes: This function is only called from the CUhcd::Initialize routine.
// It should only be called once, to initialize the static variables
// ******************************************************************
{
DEBUGMSG(ZONE_INIT, (TEXT("+CPipe::Initialize\n")));
#ifdef DEBUG // only call this once to init static vars/schedule
DEBUGCHK( m_debug_fInitializeAlreadyCalled == FALSE );
m_debug_fInitializeAlreadyCalled = TRUE;
#endif // DEBUG
DEBUGCHK( m_fCheckTransferThreadClosing == FALSE &&
m_hCheckForDoneTransfersThread == NULL &&
m_hCheckForDoneTransfersEvent == NULL );
DEBUGCHK( m_pBusyPipeList == NULL &&
m_debug_numItemsOnBusyPipeList == 0 );
if ( m_pCPhysMem == NULL ) {
DEBUGCHK( 0 ); // This should never happen
DEBUGMSG(ZONE_ERROR, (TEXT("-CPipe::Initialize, no memory manager object!!\n")));
return FALSE;
}
// Init the list of EDs for scheduling transfers in the periodic lists
memset( m_interruptQHTree, 0, sizeof(m_interruptQHTree) );
// set up the periodic (intr/isoch) scheduling structures
{
// We set this up as follows:
// (demonstrated for UHCD_MAX_INTERRUPT_INTERVAL = 4)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// level 2 level 1 level 0 Full Speed QH End of Schedule
//
// frame #0 -> QH #4 \
// QH #2
// frame #2 -> QH #6 / \
// \ (1ms intr) (isoch)
// QH # 1 - - - - QH #0 - - - - FinalQH
// /
// frame #1 -> QH #5 \ /
// QH #3
// frame #3 -> QH #7 /
//
// 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 EDs will be placed after QH #0.
//
// INTERRUPT transfers will be placed at the appropriate point within the QH
// tree so that they are scheduled at regular intervals.
// we need 2 * UHCD_MAX_INTERRUPT_INTERVAL + 1 queue heads which
// will always remain in the schedule and never be freed
P_ED vaQHList = NULL;
#define CPIPE_INITIALIZE_ED_MEMORY_NEEDED DWORD( (2 * UHCD_MAX_INTERRUPT_INTERVAL + 1) * sizeof( ED ) )
if ( !m_pCPhysMem->AllocateMemory( DEBUG_PARAM( TEXT("Permanent EDs"), )
CPIPE_INITIALIZE_ED_MEMORY_NEEDED,
(PUCHAR *) &vaQHList,
CPHYSMEM_FLAG_NOBLOCK ) ) {
DEBUGMSG(ZONE_ERROR, (TEXT("-CPipe::Initialize - Could not get EDs for schedule\n")));
return FALSE;
}
#ifdef DEBUG
m_debug_QHMemoryAllocated += CPIPE_INITIALIZE_ED_MEMORY_NEEDED;
DEBUGMSG( ZONE_QH, (TEXT("CPipe::Initialize - allocate persistent QHs, total bytes = %d\n"), m_debug_QHMemoryAllocated) );
#endif // DEBUG
m_pFinalQH = vaQHList;
vaQHList += 1;
m_interruptQHTree[0] = vaQHList;
vaQHList += 1;
// 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
memset( m_pFinalQH, 0, sizeof(ED) );
m_pFinalQH->bfSkip = 1;
m_pFinalQH->paNextEd = 0;
// QH # 0 points to m_pFinalQH
memset( m_interruptQHTree[0], 0, sizeof(ED) );
m_interruptQHTree[0]->bfIsIsochronous = 1;
m_interruptQHTree[0]->bfSkip = 1;
m_interruptQHTree[0]->paNextEd = GetQHPhysAddr( m_pFinalQH );
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 ] = vaQHList;
vaQHList += 1;
const UCHAR link = (index ^ pow) | (pow >> 1);
DEBUGCHK( m_interruptQHTree[ link ] != NULL );
memset( m_interruptQHTree[index], 0, sizeof(ED) );
m_interruptQHTree[index]->bfSkip = 1;
m_interruptQHTree[index]->paNextEd = GetQHPhysAddr( m_interruptQHTree[link] );
}
}
// Fill in the HCCA Interrupt ED ListHeads.
for (int index = 0; index < UHCD_MAX_INTERRUPT_INTERVAL; ++index)
pCUhcd->CHW::m_pInterruptTable[index] = GetQHPhysAddr( m_interruptQHTree[index+UHCD_MAX_INTERRUPT_INTERVAL] );
}
// m_hCheckForDoneTransfersEvent - Auto Reset, and Initial State = non-signaled
m_hCheckForDoneTransfersEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( m_hCheckForDoneTransfersEvent == NULL ) {
DEBUGMSG(ZONE_ERROR, (TEXT("-CPipe::Initialize. Error creating process done transfers event\n")));
return FALSE;
}
// set up our thread to check for done transfers
// currently, the context passed to CheckForDoneTransfersThread is ignored
m_hCheckForDoneTransfersThread = CreateThread( 0, 0, CheckForDoneTransfersThreadStub, (PVOID)this, 0, NULL );
if ( m_hCheckForDoneTransfersThread == NULL ) {
DEBUGMSG(ZONE_ERROR, (TEXT("-CPipe::Initialize. Error creating process done transfers thread\n")));
return FALSE;
}
CeSetThreadPriority( m_hCheckForDoneTransfersThread, g_IstThreadPriority + RELATIVE_PRIO_CHECKDONE );
DEBUGMSG(ZONE_INIT, (TEXT("-CPipe::Initialize. Success!\n")));
return TRUE;
}
// *****************************************************************
// Scope: public static
void CHCCAera::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 );
m_fCheckTransferThreadClosing = FALSE;
// m_pCPhysMem->FreeSpecialMemory( PUCHAR(m_vaFrameList));
}
// ******************************************************************
// Scope: public static
void CHCCAera::SignalCheckForDoneTransfers( DWORD paDoneHead )
//
// 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 );
DEBUGCHK( paDoneHead != 0 ); // spurious interrupts are filtered in CHW
// Traverse the list of done TDs, reversing it in place and simultaneously
// relinking it with virtual instead of physical addresses.
P_TD pTD = (P_TD) m_pCPhysMem->PaToVa(paDoneHead);
P_TD pTail = pTD;
P_TD pPrev = 0;
while (pTD) {
DWORD paNext = pTD->paNextTd.phys;
P_TD pNext = paNext ? (P_TD) m_pCPhysMem->PaToVa(paNext) : 0;
pTD->paNextTd.td = pPrev;
pPrev = pTD;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -