⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpipe.cpp

📁 Latest USB 802.3, HID printer and mass storage divers from Microsoft for Platform Builder 4.2.
💻 CPP
📖 第 1 页 / 共 5 页
字号:
        //   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 + -