📄 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"
// ******************************************************************
void InitializeTD( OUT PUHCD_TD const pTD,
IN const TD_LINK_POINTER_PHYSICAL_ADDRESS HW_paLink,
IN const PUHCD_TD vaNextTD,
IN const UCHAR InterruptOnComplete,
IN const UCHAR Isochronous,
IN const BOOL LowSpeedControl,
IN const DWORD PID,
IN const UCHAR Address,
IN const UCHAR Endpoint,
IN const USHORT DataToggle,
IN const DWORD MaxLength,
IN const TD_BUFFER_PHYSICAL_ADDRESS HW_paBuffer,
IN const BOOL bShortPacketOk /*= FALSE*/)
//
// Purpose: Fill in Transfer Descriptor fields
//
// Parameters: pTD - pointer to transfer descriptor to fill in
//
// Rest of Params - various transfer descriptor fields
//
// Returns: Nothing
//
// Notes: MaxLength field should already be encoded by caller into
// (n-1) form
// ******************************************************************
{
// HW DWORD 1 - Hardware link to the next item in schedule
pTD->HW_paLink = HW_paLink;
// HW DWORD 2
// pTD->ActualLength <- don't need to set
// pTD->Reserved_1 <- don't need to set
pTD->Active = 1;
pTD->StatusField = TD_STATUS_NO_ERROR;
DEBUGCHK( (InterruptOnComplete & 1) == InterruptOnComplete );
pTD->InterruptOnComplete = InterruptOnComplete;
DEBUGCHK( (Isochronous & 1) == Isochronous );
pTD->Isochronous = Isochronous;
DEBUGCHK( (LowSpeedControl & 1) == LowSpeedControl );
pTD->LowSpeedControl = LowSpeedControl;
pTD->ErrorCounter = TD_ERRORCOUNTER_INTERRUPT_AFTER_THREE;
pTD->ShortPacketDetect = bShortPacketOk ? 1 : 0;
// pTD->ReservedMBZ <- don't need to set
// HW DWORD 3
DEBUGCHK( PID == TD_IN_PID ||
PID == TD_OUT_PID ||
PID == TD_SETUP_PID );
pTD->PID = PID;
DEBUGCHK( Address <= USB_MAX_ADDRESS );
pTD->Address = Address;
DEBUGCHK( (Endpoint & TD_ENDPOINT_MASK) == Endpoint );
pTD->Endpoint = Endpoint;
DEBUGCHK( (DataToggle & 1) == DataToggle );
pTD->DataToggle = DataToggle;
// pTD->Reserved_2 <- don't need to set
// MaxLength is a nonzero length minus one or, for a zero length TD,
// it might be seen here as a 32-bit -1 or as a zero-extended 11-bit -1.
DEBUGCHK( (MaxLength == TD_MAXLENGTH_NULL_BUFFER) ||
(MaxLength == (DWORD)-1) ||
(MaxLength <= TD_MAXLENGTH_MAX && HW_paBuffer != 0) );
pTD->MaxLength = MaxLength;
// HW DWORD 4 - physical address of buffer
pTD->HW_paBuffer = HW_paBuffer;
// SW DWORD 5 - virt addr of previous Isoch TD
pTD->vaPrevIsochTD = NULL;
// SW DWORD 6 - virt addr of next TD in list
pTD->vaNextTD = vaNextTD;
#ifdef DEBUG
// SW DWORD 7 - Unused for now
pTD->dwUNUSED1 = 0xdeadbeef;
// SW DWORD 8 - Unused for now
pTD->dwUNUSED2 = 0xdeadbeef;
#endif // DEBUG
}
// ******************************************************************
void InitializeQH( OUT PUHCD_QH const pQH,
IN const PUHCD_QH vaPrevQH,
IN const QUEUE_HEAD_LINK_POINTER_PHYSICAL_ADDRESS HW_paHLink,
IN const PUHCD_QH vaNextQH )
//
// Purpose: Fill in Queue Head Fields
//
// Parameters: pQH - pointer to Queue Head to Initialize
//
// Rest of Params - various Queue Head fields
//
// Returns: Nothing
//
// Notes:
// ******************************************************************
{
DEBUGCHK( pQH != NULL );
// HW DWORD #1 - Horizontal link
pQH->HW_paHLink = HW_paHLink;
// HW DWORD #2 - Vertical link - No Transfer Descriptors right now
pQH->HW_paVLink = QUEUE_ELEMENT_LINK_POINTER_TERMINATE;
// SW DWORD #3 - previous QH in schedule, or NULL
pQH->vaPrevQH = vaPrevQH;
// SW DWORD #4 - next QH in schedule, or NULL
pQH->vaNextQH = vaNextQH;
// SW DWORD #5 - first Transfer Descriptor - NULL for now
pQH->vaVertTD = NULL;
#ifdef DEBUG
// SW DWORD #6
pQH->dwInterruptTree.Load = 0xdeadbeef;
// SW DWORD #7
pQH->dwUNUSED1 = 0xdeadbeef;
// SW DWORD #8
pQH->dwUNUSED2 = 0xdeadbeef;
#endif // DEBUG
}
CUHCIFrame::CUHCIFrame(IN CPhysMem* const pCPhysMem )
: m_pCPhysMem( pCPhysMem)
{
// memory manager
numReclamationTransfers = 0;
m_pCUhcd=NULL;
#ifdef DEBUG
m_debug_fInitializeAlreadyCalled = FALSE;
#endif // DEBUG
// schedule related vars
m_vaFrameList = NULL;
m_pFinalQH = NULL;
m_pFrameSynchTD = 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_fCheckTransferThreadClosing = FALSE;
m_hCheckForDoneTransfersEvent = NULL;
m_hCheckForDoneTransfersThread = NULL;
m_pBusyPipeList = NULL;
#ifdef DEBUG
m_debug_numItemsOnBusyPipeList = 0;
#endif // DEBUG
};
CUHCIFrame::~CUHCIFrame()
{
DeInitialize();
}
// *****************************************************************
// Scope: public static
BOOL CUHCIFrame::Initialize( CUhcd * 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("+CUHCIFrame::Initialize\n")));
m_pCUhcd=pCUhcd;
#ifdef DEBUG // only call this once to init static vars/schedule
DEBUGCHK( m_debug_fInitializeAlreadyCalled == FALSE );
m_debug_fInitializeAlreadyCalled = TRUE;
#endif // DEBUG
// TDs and QHs must satisfy certain size/alignment criteria
DEBUGCHK( sizeof( UHCD_TD ) == TD_REQUIRED_SIZE_IN_BYTES );
DEBUGCHK( sizeof( UHCD_QH ) % QH_ALIGNMENT_BOUNDARY == 0 );
// memory manager will provide alignment - check if it
// will satisfy our TD/QH requirements
DEBUGCHK( CPHYSMEM_MEMORY_ALIGNMENT % TD_ALIGNMENT_BOUNDARY == 0 );
DEBUGCHK( CPHYSMEM_MEMORY_ALIGNMENT % QH_ALIGNMENT_BOUNDARY == 0 );
// init static variables
// frame list variables
InitializeCriticalSection( &m_csFrameListLock );
DEBUGCHK( m_vaFrameList == NULL );
// QH scheduling variables
InitializeCriticalSection( &m_csQHScheduleLock );
memset( m_interruptQHTree, 0, 2 * UHCD_MAX_INTERRUPT_INTERVAL * sizeof( PUHCD_QH ) );
// check for done transfers thread variables
DEBUGCHK( m_fCheckTransferThreadClosing == FALSE &&
m_hCheckForDoneTransfersThread == NULL &&
m_hCheckForDoneTransfersEvent == NULL );
DEBUGCHK( m_pBusyPipeList == NULL &&
m_debug_numItemsOnBusyPipeList == 0 );
InitializeCriticalSection( &m_csBusyPipeListLock );
if ( m_pCPhysMem == NULL ) {
DEBUGCHK( 0 ); // This should never happen
DEBUGMSG(ZONE_ERROR, (TEXT("-CPipe::Initialize, no memory manager object!!\n")));
return FALSE;
}
// 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 );
// ask CHW about the frame list
{
//
// The frame list is just an array of FRAME_LIST_LENGTH pointers. Each
// is either invalid, or points to a TD/QH which starts the schedule for
// that frame.
//
// frame list should be 4Kb according to UHCI spec
DEBUGCHK( FRAME_LIST_SIZE_IN_BYTES == 4096 );
// frame list should be same size as USBPAGESIZE used by memory manager
DEBUGCHK( FRAME_LIST_SIZE_IN_BYTES == USBPAGESIZE );
DEBUGCHK( m_vaFrameList == NULL );
m_vaFrameList = m_pCUhcd->CHW::GetFrameListAddr(); // was allocated from CPhysMem by CHW
// the frame list MUST be aligned on a 4Kb memory boundary
if ( GetFrameListPhysAddr() % FRAME_LIST_SIZE_IN_BYTES != 0 ) {
DEBUGMSG(ZONE_ERROR, (TEXT("-CPipe::Initialize - Error. Unable to create frame list. m_vaFrameList = 0x%X\n"), m_vaFrameList ));
DEBUGCHK( 0 ); // this should never happen
return FALSE;
}
DEBUGCHK( m_vaFrameList != NULL );
DEBUGMSG(ZONE_INIT && ZONE_VERBOSE, (TEXT("CPipe::Initialize - Created frame list. m_vaFrameList = 0x%X, PhysAddr=0x%X\n"), m_vaFrameList, GetFrameListPhysAddr() ));
}
// set up the queue head 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
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -