//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
//
// 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 OHCI
//
//                             CPipe (ADT)
//                           /             \
//                  CQueuedPipe (ADT)       CIsochronousPipe
//                /         |       \ 
//              /           |         \
//   CControlPipe    CInterruptPipe    CBulkPipe
// 
// 
// Notes: 
// 
//

#include "cpipe.hpp"
#include "cphysmem.hpp"
#include "chw.hpp"
#include "cohcd.hpp"


#ifndef _PREFAST_
#pragma warning(disable: 4068) // Disable pragma warnings
#endif


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 COhcd * const pCOhcd)
//
// 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 COhcd::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 OHCD_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 = OHCD_MAX_INTERRUPT_INTERVAL + x, where
        //   x = 0, 1, 2, 3, .., OHCD_MAX_INTERRUPT_INTERVAL - 1. The QH #i will be 
        //   scheduled by any frame which has index == x (mod OHCD_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 * OHCD_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 * OHCD_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 <= OHCD_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 < OHCD_MAX_INTERRUPT_INTERVAL; ++index)
            pCOhcd->CHW::m_pInterruptTable[index] = GetQHPhysAddr( m_interruptQHTree[index+OHCD_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 ~COhcd()
// ******************************************************************
{
#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
            }
            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;
        if (pNext == pTD) {
            RETAILMSG(1,(TEXT("OHCI: CHCCAera::SignalCheckForDoneTransfers: Hardware gives us TD link to itself, reset to NULL!!!")));
            ASSERT(FALSE);
            pNext = NULL;
        }
        
        pTD->paNextTd.td = pPrev;
        pPrev = pTD;
        pTD = pNext;
    }

    // At this point, the list is connected by virtaddrs in forward chronological
    // order of completion. pTail is the va of the most recently completed TD
    // and pPrev is the va of the oldest completed TD.
    //
    // Just in case we took two interrupts before the CFDT thread got a chance
    // to run, let's append our new list to the end of any pre-existing one.
    // We need to steal the list head in order to do that.
    P_TD pOldQueueHead = (P_TD) InterlockedExchange((LPLONG)&m_pDoneHead, 0L);
    P_TD *ppOldQueueTail = &pOldQueueHead;
    while (*ppOldQueueTail)
        ppOldQueueTail = &(*ppOldQueueTail)->paNextTd.td;
    *ppOldQueueTail = pPrev;

    // Now put the new queue back in place. Ignore the old value because we
    // already know that it was zero.
#ifdef DEBUG
    DWORD old = InterlockedExchange((LPLONG)&m_pDoneHead, (LONG)pOldQueueHead);
    DEBUGCHK( old == 0 );
#else
    (void) InterlockedExchange((LPLONG)&m_pDoneHead, (LONG)pOldQueueHead);
#endif

    // Tell the CFDT thread that it has more work to do.
    SetEvent( m_hCheckForDoneTransfersEvent );
}

ULONG CALLBACK CHCCAera::CheckForDoneTransfersThreadStub( IN PVOID context )
{
    return  ((CHCCAera *)context)->CheckForDoneTransfersThread();
}


#if defined(MIPSIV)
#define __OPT_PLAT_FLAG TRUE
#else   // defined(MIPSIV)
#define __OPT_PLAT_FLAG FALSE
#endif  // defined(MIPSIV)
#define __OPT_BUGNUMSTRING "27332"
#define __OPT_VER_OFF
#include "optimizer.h"

// ******************************************************************
// Scope: private static
ULONG CHCCAera::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: Before invoking the CheckForDoneTransfers method on the owning pipe
// (see code), we advance past any additional TDs retired for the same transfer.
// There are two conditions under which this might happen: the more common one
// is that the transfer is not yet complete; this is harmless so long as we
// remember to bump the count of completed TDs. The less common - and more
// dangerous - situation is when a TD for the setup or data stage of a control
// transfer errors out and the Control/Bulk ratio is more than 1/1. In this case
// the HC may attempt to continue processing the ED without remembering that it
// just halted the ED.
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CHCCAera::CheckForDoneTransfersThread\n")) );

    DEBUGCHK( m_hCheckForDoneTransfersEvent != NULL );

    while ( !m_fCheckTransferThreadClosing ) {
        WaitForSingleObject( m_hCheckForDoneTransfersEvent, INFINITE );
        if ( m_fCheckTransferThreadClosing ) {
            break;
        }

        // Must be set when this thread is signalled but under stress conditions
        // it could get temporarily unset by the time we get here. If that
        // happens then we just wait for the event to get signalled again.
        TDLINK pTD;
        pTD.td = (P_TD) InterlockedExchange((LPLONG)&m_pDoneHead, 0L);
        while (pTD.td) {            
            CPipe * pTDPipe;
            if (pTD.td->bfIsIsoch) {
                pTDPipe =  pTD.itd->pPipe;
                ASSERT(pTD.itd->pTransfer->GetPipe() == pTDPipe);
            }
            else {
                pTDPipe = pTD.td->pPipe ;
                ASSERT( pTD.td->pTransfer->GetPipe() == pTDPipe);
            }
            // these fields all have the same offsets for TDs and ITDs
            if (pTD.td->bfDiscard) {
                // the transfer has been aborted already and completed elsewhere
                DEBUGMSG(ZONE_WARNING, (TEXT("CPipe::CFDT DISCARDING td %08x nxt %08x xfr %08x\n"), pTD.td, pTD.td->paNextTd.td,pTDPipe));
                pTD = pTD.td->paNextTd;
                continue;
            }
            DEBUGMSG(ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("CPipe::CFDT td %08x nxt %08x xfr %08x\n"), pTD.td, pTD.td->paNextTd,pTDPipe));
            // advance pTD first because it will get freed if it's the last TD of the transfer
            // see notes above for why we can and want to advance multiple
            TDLINK pNextTD =  pTD.td->paNextTd; // same offset as for itd
            pTDPipe->CheckForDoneTransfers(pTD);
            pTD = pNextTD;
        }
    }
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("-CHCCAera::CheckForDoneTransfersThread\n")) );
    return 0;
}

#if defined(MIPSIV)
#define __OPT_PLAT_FLAG TRUE
#else   // defined(MIPSIV)
#define __OPT_PLAT_FLAG FALSE
#endif  // defined(MIPSIV)
#define __OPT_BUGNUMSTRING "27332"
#define __OPT_VER_RESTORE
#include "optimizer.h"
// ******************************************************************
// Scope: protected static 
BOOL CHCCAera::AddToBusyPipeList( IN CPipe * const pPipe,
                               IN const BOOL fHighPriority )
//
// Purpose: Add the pipe indicated by pPipe to our list of busy pipes.
//          This allows us to check for completed transfers after 
//          getting an interrupt, and being signaled via 
//          SignalCheckForDoneTransfers
//
// Parameters: pPipe - pipe to add to busy list
//
//             fHighPriority - if TRUE, add pipe to start of busy list,
//                             else add pipe to end of list.
//
// Returns: TRUE if pPipe successfully added to list, else FALSE
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE, (TEXT("+CHCCAera::AddToBusyPipeList - new pipe(%s) 0x%x\n"), pPipe->GetPipeType(), pPipe, fHighPriority ));
    UNREFERENCED_PARAMETER(pPipe);
    UNREFERENCED_PARAMETER(fHighPriority);
 
    PREFAST_DEBUGCHK( pPipe != NULL );
    BOOL fSuccess = FALSE;

    // make sure there nothing on the pipe already (it only gets officially added after this function succeeds).
    DEBUGCHK( !pPipe->m_fTransferInProgress );

    EnterCriticalSection( &m_csBusyPipeListLock );
    #ifdef DEBUG
        m_debug_numItemsOnBusyPipeList++;
    #endif // DEBUG
    fSuccess = TRUE;
    LeaveCriticalSection( &m_csBusyPipeListLock );

    DEBUGMSG( ZONE_PIPE, (TEXT("-CHCCAera::AddToBusyPipeList - new pipe(%s) 0x%x, pri %d, returning BOOL %d\n"), pPipe->GetPipeType(), pPipe, fHighPriority, fSuccess) );
    return fSuccess;
}

// ******************************************************************
// Scope: protected static
void CHCCAera::RemoveFromBusyPipeList( IN CPipe * const pPipe )
//
// Purpose: Remove this pipe from our busy pipe list. This happens if
//          the pipe is suddenly aborted or closed while a transfer
//          is in progress
//
// Parameters: pPipe - pipe to remove from busy list
//
// Returns: Nothing
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CHCCAera::RemoveFromBusyPipeList - pipe(%s) 0x%x\n"), pPipe->GetPipeType(), pPipe ) );
    UNREFERENCED_PARAMETER(pPipe);

    EnterCriticalSection( &m_csBusyPipeListLock );
    LeaveCriticalSection( &m_csBusyPipeListLock );

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CHCCAera::RemoveFromBusyPipeList, removed pipe(%s) 0x%x\n"), pPipe->GetPipeType(), pPipe));
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CHCCAera::RemoveFromBusyPipeList, pipe(%s) 0x%x was not on busy list\n"), pPipe->GetPipeType(), pPipe ));
}


// ******************************************************************               
// Scope: protected static
void CHCCAera::HandleReclamationLoadChange( IN const BOOL fAddingTransfer  )
//
// Purpose: This function is called whenever transfers which use bandwidth
//          reclamation (high speed Bulk/Control) are added/removed.
//          If there are transfers for which reclamation is needed, this
//          function will clear the termination bit of m_pFinalQH. Otherwise,
//          it will set the termination bit to prevent infinite QH processing.
//
// Parameters: fAddingTransfer - if TRUE, a reclamation transfer is being added
//                               to the schedule. If FALSE, a reclamation transfer
//                               has just finished/aborted
//
// Returns: Nothing
//
// Notes: 
// ******************************************************************
{
    // important that this be static - this variable tracks the total
    // number of reclamation transfers in progress on the USB

    DEBUGMSG( ZONE_TRANSFER, (TEXT("+CPipe::HandleReclamationLoadChange - %s\n"), ((fAddingTransfer) ? TEXT("Add Reclamation Transfer") : TEXT("Remove Reclamation Transfer")) ) );
    UNREFERENCED_PARAMETER(fAddingTransfer);

    DEBUGMSG( ZONE_TRANSFER, (TEXT("-CPipe::HandleReclamationLoadChange - %s\n"), ((fAddingTransfer) ? TEXT("Add Reclamation Transfer") : TEXT("Remove Reclamation Transfer")) ) );
}


// ******************************************************************
// Scope: public 
CPipe::CPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
              IN const BOOL fIsLowSpeed,
              IN const UCHAR bDeviceAddress,
              IN COhcd * const pCOhcd)
//
// Purpose: constructor for CPipe
//
// Parameters: lpEndpointDescriptor - pointer to endpoint descriptor for
//                                    this pipe (assumed non-NULL)
//
//             fIsLowSpeed - indicates if this pipe is low speed
//
// Returns: Nothing.
//
// Notes: Most of the work associated with setting up the pipe
//        should be done via OpenPipe. The constructor actually
//        does very minimal work.
//
//        Do not modify static variables here!!!!!!!!!!!
// ******************************************************************
: CPipeAbs(lpEndpointDescriptor->bEndpointAddress )
, m_usbEndpointDescriptor( *lpEndpointDescriptor )
, m_pCOhcd(pCOhcd)
, m_fIsLowSpeed( !!fIsLowSpeed ) // want to ensure m_fIsLowSpeed is 0 or 1
, m_fIsHalted( FALSE )
, m_bEndpointAddress( lpEndpointDescriptor->bEndpointAddress )
, m_private_address( bDeviceAddress )
, m_bBusAddress( m_private_address )
, m_pED( NULL )
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CPipe::CPipe\n")) );
    // CPipe::Initialize should already have been called by now
    // to set up the schedule and init static variables
    DEBUGCHK( m_pCOhcd->CHCCAera::m_debug_fInitializeAlreadyCalled );

    InitializeCriticalSection( &m_csPipeLock );
    m_fTransferInProgress= FALSE;

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CPipe::CPipe\n")) );
}

// ******************************************************************
// Scope: public virtual 
CPipe::~CPipe( )
//
// Purpose: Destructor for CPipe
//
// Parameters: None
//
// Returns: Nothing.
//
// Notes:   Most of the work associated with destroying the Pipe
//          should be done via ClosePipe
//
//          Do not delete static variables here!!!!!!!!!!!
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CPipe::~CPipe\n")) );
    // transfers should be aborted or closed before deleting object
    DEBUGCHK( m_fTransferInProgress == FALSE );
    DeleteCriticalSection( &m_csPipeLock );
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CPipe::~CPipe\n")) );
}
inline ULONG CPipe::GetQHPhysAddr( IN P_ED virtAddr ) {
        DEBUGCHK( virtAddr != NULL &&
                  m_pCOhcd->CHCCAera::m_pCPhysMem->VaToPa( PUCHAR(virtAddr) ) % 16 == 0 );
        return m_pCOhcd->m_pCPhysMem->VaToPa( PUCHAR(virtAddr) );
}

inline ULONG CPipe::GetTDPhysAddr( IN const P_TD virtAddr ) {
        DEBUGCHK( virtAddr != NULL );
        return m_pCOhcd->CHCCAera::m_pCPhysMem->VaToPa( PUCHAR(virtAddr) );
    };

inline ULONG CPipe::GetTDPhysAddr( IN const P_ITD virtAddr ) {
        DEBUGCHK( virtAddr != NULL );
        return m_pCOhcd->CHCCAera::m_pCPhysMem->VaToPa( PUCHAR(virtAddr) );
};


// ******************************************************************
// Scope: public
HCD_REQUEST_STATUS CPipe::IsPipeHalted( OUT LPBOOL const lpbHalted )
//
// Purpose: Return whether or not this pipe is halted (stalled)
//
// Parameters: lpbHalted - pointer to BOOL which receives
//                         TRUE if pipe halted, else FALSE
//
// Returns: requestOK 
//
// Notes:  Caller should check for lpbHalted to be non-NULL
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CPipe(%s)::IsPipeHalted\n"), GetPipeType()) );

    DEBUGCHK( lpbHalted ); // should be checked by COhcd

    EnterCriticalSection( &m_csPipeLock );
    if (lpbHalted!=NULL)
        *lpbHalted = m_fIsHalted;
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CPipe(%s)::IsPipeHalted, *lpbHalted = %d, returning HCD_REQUEST_STATUS %d\n"), GetPipeType(), *lpbHalted, requestOK) );
    return requestOK;
}

// ******************************************************************
// Scope: public
void CPipe::ClearHaltedFlag( void )
//
// Purpose: Clears the pipe is halted flag
//
// Parameters: None
//
// Returns: Nothing 
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CPipe(%s)::ClearHaltedFlag\n"), GetPipeType() ) );

    EnterCriticalSection( &m_csPipeLock );
    DEBUGMSG( ZONE_WARNING && !m_fIsHalted, (TEXT("CPipe(%s)::ClearHaltedFlag - warning! Called on non-stalled pipe\n"), GetPipeType()) );
    m_fIsHalted = FALSE;
    m_pED->bfHalted = 0;
    m_pED->bfToggleCarry = 0;
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CPipe(%s)::ClearHaltedFlag\n"), GetPipeType()) );
}
// ******************************************************************               
// Scope: protected virtual
DWORD CPipe::GetMemoryAllocationFlags( void ) const
//
// Purpose: Get flags for allocating memory from the CPhysMem class.
//          Descended pipes can over-ride this if they want to
//          specify different memory alloc flags (i.e. block or not,
//          high priority or not, etc)
//
// Parameters: None
//
// Returns: DWORD representing memory allocation flags
//
// Notes: 
// ******************************************************************
{
    // default choice - not high priority, no blocking
    return CPHYSMEM_FLAG_NOBLOCK;
}

// ******************************************************************               
// Scope: public
CQueuedPipe::CQueuedPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
                          IN const BOOL fIsLowSpeed,
                          IN const UCHAR bDeviceAddress,
                          IN COhcd * const pCOhcd)
//
// Purpose: Constructor for CQueuedPipe
//
// Parameters: See CPipe::CPipe
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
: CPipe( lpEndpointDescriptor, fIsLowSpeed, bDeviceAddress,pCOhcd )   // constructor for base class
, TDQueue(pCOhcd->CHCCAera::m_pCPhysMem , 0x4 , min(0x400,gcTdPageSize))
, m_fIsReclamationPipe( FALSE ) // this gets set to TRUE later as need arises
//, m_dataToggle( 1 ) // data toggle for this pipe
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CQueuedPipe::CQueuedPipe\n")) );
    m_pTransfer = NULL;
    m_pLastTransfer = m_pFirstTransfer = NULL;
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CQueuedPipe::CQueuedPipe\n")) );
}

// ******************************************************************               
// Scope: public virtual
CQueuedPipe::~CQueuedPipe( )
//
// Purpose: Destructor for CQueuedPipe
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CQueuedPipe::~CQueuedPipe\n")) );
    // queue should be freed via ClosePipe before calling destructor
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CQueuedPipe::~CQueuedPipe\n")) );
}

// ******************************************************************               
// Scope: public (Implements CPipe::ClosePipe = 0)
HCD_REQUEST_STATUS CQueuedPipe::ClosePipe( void )
//
// Purpose: Abort any transfers associated with this pipe, and
//          remove its data structures from the schedule
//
// Parameters: None
//
// Returns: requestOK
//
// Notes: 
// ******************************************************************
{
    HCD_REQUEST_STATUS status = requestOK;
    
    DEBUGMSG( ZONE_PIPE, (TEXT("+CQueuedPipe(%s)::ClosePipe\n"), GetPipeType() ) );
    // Delete outstanding transfer first.
    EnterCriticalSection( &m_csPipeLock );
    while ( m_pFirstTransfer) {
        CTransfer * pNextTransfer = m_pFirstTransfer->GetNextTransfer();
        m_pFirstTransfer->Canceled();
        delete m_pFirstTransfer;
        m_pFirstTransfer = pNextTransfer;
    }
    m_pLastTransfer = NULL;
    LeaveCriticalSection( &m_csPipeLock );

    // Don't enter CS before calling AbortTransfer, since
    // that function will need to leave the pipe CS
    // at a certain point to avoid deadlock, and
    // won't be able to do so if the CS is nested.

    LPVOID lpCancelId;
    do {
        EnterCriticalSection( &m_csPipeLock );
        if ( m_fTransferInProgress && m_pTransfer) {
            lpCancelId = (LPVOID)((m_pTransfer->GetSTransfer())->lpvCancelId);
        }
        else 
            lpCancelId = NULL;
        LeaveCriticalSection( &m_csPipeLock );
        if (lpCancelId!=NULL)
            AbortTransfer( NULL, // callback function
                           NULL, // callback param
                           lpCancelId);
    }
    while (lpCancelId!=NULL);

    EnterCriticalSection( &m_csPipeLock );
    // transfer was either never issued, finished before we got to
    // this function, or aborted above.
    DEBUGCHK( !m_fTransferInProgress );
    ASSERT(m_pTransfer==NULL);
    ASSERT(m_pFirstTransfer==NULL);
    ASSERT(m_pLastTransfer==NULL);

    m_pED->bfSkip = 1;
    m_pCOhcd->CHW::WaitOneFrame();

    EnterCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );
    ULONG *pNextPhys = GetListHead(FALSE);
    ULONG thisPhys = GetQHPhysAddr(m_pED);

    if ((*pNextPhys & 0xf)!=0) { // Bad address. Skip the free. Warning: We leak memory here if it is happen.
        ASSERT(FALSE);
        status = requestFailed;
    }
    else {
        while (*pNextPhys != 0 && *pNextPhys != thisPhys) {
            P_ED pnext = (P_ED) m_pCOhcd->CHCCAera::m_pCPhysMem->PaToVa(*pNextPhys);
            pNextPhys = &pnext->paNextEd;
        }
        if (*pNextPhys == 0) {
            DEBUGMSG(ZONE_WARNING, (TEXT("!CQueuedPipe(%s)::ClosePipe %08x doesn't exist!\n"), GetPipeType(), m_pED));
            status = requestFailed;
        } else {
            DEBUGCHK( *pNextPhys == thisPhys );
            *pNextPhys = m_pED->paNextEd;
            DEBUGCHK( (m_pED->paTdQueueHead & ~3) == m_pED->paTdQueueTail );
            m_pCOhcd->CHCCAera::m_pCPhysMem->FreeMemory((PUCHAR)m_pED, m_pCOhcd->CHCCAera::m_pCPhysMem->VaToPa((PUCHAR)m_pED),
                                    GetMemoryAllocationFlags());
            m_pED = 0;
        }
    }
    LeaveCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );
    
    QueueDeInit();

    (void) GetListHead(TRUE);
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_PIPE, (TEXT("-CQueuedPipe(%s)::ClosePipe\n"), GetPipeType() ) );
    return status;
}
// ******************************************************************               
// Scope: public 
HCD_REQUEST_STATUS CQueuedPipe::IssueTransfer( 
                                    IN const UCHAR address,
                                    IN LPTRANSFER_NOTIFY_ROUTINE const lpStartAddress,
                                    IN LPVOID const lpvNotifyParameter,
                                    IN const DWORD dwFlags,
                                    IN LPCVOID const lpvControlHeader,
                                    IN const DWORD dwStartingFrame,
                                    IN const DWORD dwFrames,
                                    IN LPCDWORD const aLengths,
                                    IN const DWORD dwBufferSize,     
                                    IN_OUT LPVOID const lpvClientBuffer,
                                    IN const ULONG paBuffer,
                                    IN LPCVOID const lpvCancelId,
                                    OUT LPDWORD const adwIsochErrors,
                                    OUT LPDWORD const adwIsochLengths,
                                    OUT LPBOOL const lpfComplete,
                                    OUT LPDWORD const lpdwBytesTransferred,
                                    OUT LPDWORD const lpdwError )
//
// Purpose: Issue a Transfer on this pipe
//
// Parameters: address - USB address to send transfer to
//
//             OTHER PARAMS - see comment in COhcd::IssueTransfer
//
// Returns: requestOK if transfer issued ok, else requestFailed
//
// Notes:   
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER, (TEXT("+CPipe(%s)::IssueTransfer, address = %d\n"), GetPipeType(), address) );
    STransfer sTransfer = { address,lpStartAddress,lpvNotifyParameter, dwFlags,lpvControlHeader,0, 
        dwStartingFrame, dwFrames,  aLengths,  dwBufferSize,  lpvClientBuffer, paBuffer, 
        lpvCancelId,  adwIsochErrors, adwIsochLengths,  lpfComplete,   lpdwBytesTransferred, lpdwError
    };
    HCD_REQUEST_STATUS  status = requestFailed;
    CTransfer * pTransfer = new CTransfer(this, sTransfer,NULL);

    EnterCriticalSection( &m_csPipeLock );
    if (pTransfer) {        
        if (m_bBusAddress == 0) {
            if (address != 0) {
                // Following assertion is not required by USB but if it's not true then
                // there are synchronization issues between here and ScheduleTransfer.
                DEBUGCHK( m_fTransferInProgress == FALSE );
                // This is the non-const private member to which m_bBusAddress is a reference:
                m_private_address = address;
            }
        } else
            DEBUGCHK( m_bBusAddress == address );

        if ( AreTransferParametersValid(pTransfer->GetSTransfer()) ) {
            if (m_pLastTransfer!=NULL) {
                m_pLastTransfer->SetNextTransfer(pTransfer);
                m_pLastTransfer = pTransfer;
            }
            else
                m_pLastTransfer = pTransfer;
            
            if (m_pFirstTransfer==NULL)
                m_pFirstTransfer = m_pLastTransfer;
#ifdef DEBUG
            if (m_fTransferInProgress) {
                DEBUGCHK(  m_pTransfer!=NULL );
            }
            DEBUGCHK( m_pLastTransfer != NULL && m_pLastTransfer->GetNextTransfer()== NULL );
            DEBUGCHK( m_pFirstTransfer!=NULL);
#endif //DEBUG
            if (m_pTransfer==NULL)
                status = ScheduleTransfer();
            else
                 status= requestOK;
        }
        else {
            ASSERT(FALSE);
            delete pTransfer;
        }
    }    
    else {
         DEBUGMSG( ZONE_ERROR, (TEXT("-CPipe(%s)::IssueTransfer cannot allocate transfer params!")
                                       TEXT(" returning Failed\n"), GetPipeType() ) );
    }
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_TRANSFER, (TEXT("-CPipe(%s)::IssueTransfer - address = %d, xfr %08x can %08x returning STATUS %d\n"), GetPipeType(), address, pTransfer, lpvCancelId, status) );
    return status;
}

// ******************************************************************               
// Scope: public (implements CPipe::AbortTransfer = 0)
HCD_REQUEST_STATUS CQueuedPipe::AbortTransfer( 
                                IN const LPTRANSFER_NOTIFY_ROUTINE lpCancelAddress,
                                IN const LPVOID lpvNotifyParameter,
                                IN LPCVOID lpvCancelId )
//
// Purpose: Abort any transfer on this pipe if its cancel ID matches
//          that which is passed in.
//
// Parameters: lpCancelAddress - routine to callback after aborting transfer
//
//             lpvNotifyParameter - parameter for lpCancelAddress callback
//
//             lpvCancelId - identifier for transfer to abort
//
// Returns: requestOK if transfer aborted
//          requestFailed if lpvCancelId doesn't match currently executing
//                 transfer, or if there is no transfer in progress
//
// Notes:
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER, (TEXT("+CQueuedPipe(%s)::AbortTransfer - lpvCancelId = 0x%x\n"), GetPipeType(), lpvCancelId) );

    HCD_REQUEST_STATUS status = requestFailed;
    CTransfer         *pTransfer = NULL;

    EnterCriticalSection( &m_csPipeLock );

    if (m_pTransfer!=NULL && m_pTransfer->GetSTransfer()->lpvCancelId == lpvCancelId) {
        // Now we need to ensure that the hardware is no longer accessing
        // the TDs which made up this transfer. Accomplish this by skipping
        // the ED, relinquishing the critsec, and waiting for the next frame.
        // At the start of the next frame, the WDH interrupt should fire and
        // that will cause the CFDT thread (which has higher priority than
        // whoever is calling AbortTransfer) to run. CFDT will flush leftover
        // TDs (and potentially complete the transfer pre-abortion).
        m_pED->bfSkip = 1;
        for(DWORD dwIndex=0;dwIndex<1000;dwIndex++) {
            LeaveCriticalSection( &m_csPipeLock );
            m_pCOhcd->CHW::WaitOneFrame();
            EnterCriticalSection( &m_csPipeLock );
            if ( m_pTransfer==NULL ||  
                    m_pTransfer->GetSTransfer()->lpvCancelId != lpvCancelId ||
                    (m_pED->paTdQueueHead & ~3) == GetTDQueueHeadPhys()){ //All the TD Matched.
                break;
            }
        }
        if (!m_fTransferInProgress || m_pTransfer==NULL ||  m_pTransfer->GetSTransfer()->lpvCancelId != lpvCancelId) {
            // Too late; the transfer already completed.
            m_pED->bfSkip = 0;
            pTransfer = NULL;
        } else {
            // Set the discard bit on any TDs not yet processed in the done queue
            // Since the CFDT thread has higher priority than this thread, the fact
            // that we are executing here means it's OK to tweak the bits.
            ASSERT((m_pED->paTdQueueHead & ~3) == GetTDQueueHeadPhys());// Make Sure Queue is Empty.
            QueueInit();
            // We need to complete the transfer ourselves
            m_pED->paTdQueueHead = m_pED->paTdQueueTail = GetTDQueueHeadPhys();
            m_pED->bfSkip = 0;
            m_fTransferInProgress = FALSE;
            pTransfer = m_pTransfer;
            m_pTransfer= NULL;
        }
    } else {
        // Transfer is not the current one so just nuke it from the list of pending transfers
        CTransfer * pPrevTransfer=NULL;
        pTransfer = m_pFirstTransfer;
        while ( pTransfer != NULL && pTransfer->GetSTransfer()->lpvCancelId != lpvCancelId) {
             pPrevTransfer = pTransfer;
             pTransfer = pTransfer->GetNextTransfer();
        }
        if ( pTransfer!=NULL) { // Found it
            if (pPrevTransfer!=NULL) { // Not at first
                pPrevTransfer ->SetNextTransfer(pTransfer->GetNextTransfer());
            }
            else {
                m_pFirstTransfer = pTransfer->GetNextTransfer();
            }
            if (pTransfer == m_pLastTransfer) { // This is Last one.
                m_pLastTransfer = pPrevTransfer;
            }
        }
    }

    if ( pTransfer != NULL ) {
        DEBUGCHK( pTransfer->GetSTransfer()->lpvCancelId == lpvCancelId );
        DEBUGCHK( AreTransferParametersValid( pTransfer->GetSTransfer()) );
        pTransfer->Canceled();
        delete pTransfer;
        if ( lpCancelAddress ) {
            __try { // calling the Cancel function
                ( *lpCancelAddress )( lpvNotifyParameter );
            } __except( EXCEPTION_EXECUTE_HANDLER ) {
                  DEBUGMSG( ZONE_ERROR, (TEXT("CQueuedPipe::AbortTransfer - exception executing cancellation callback function\n")) );
            }
        }
        if (m_pFirstTransfer)
            ScheduleTransfer();
        status = requestOK;
    }

    LeaveCriticalSection( &m_csPipeLock );
    DEBUGMSG( ZONE_TRANSFER, (TEXT("-CQueuedPipe(%s)::AbortTransfer - lpvCancelId = 0x%x, returning HCD_REQUEST_STATUS %d\n"), GetPipeType(), lpvCancelId, status) );
    return status;
}

// ******************************************************************               
// Scope: private
void  CQueuedPipe::AbortQueue( void ) 
//
// Purpose: Abort the current transfer (i.e., queue of TDs).
//
// Parameters: pQH - pointer to Queue Head for transfer to abort
//
// Returns: Nothing
//
// Notes: not used for OHCI
// ******************************************************************
{
    DEBUGCHK(0);
}

// ******************************************************************               
// Scope: protected (Implements CPipe::CheckForDoneTransfers = 0)
BOOL CQueuedPipe::CheckForDoneTransfers( TDLINK pCurTD )
//
// Purpose: Check if the transfer on this pipe is finished, and 
//          take the appropriate actions - i.e. remove the transfer
//          data structures and call any notify routines
//
// Parameters: None
//
// Returns: TRUE if transfer is done, else FALSE
//
// Notes:
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER, (TEXT("+CQueuedPipe(%s)::CheckForDoneTransfers\n"), GetPipeType() ) );

    EnterCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_TRANSFER && !m_fTransferInProgress, (TEXT("CQueuedPipe(%s)::CheckForDoneTransfers - queue was already finished on entry\n"), GetPipeType() ) );
    // TD should DONE on sequence 
    if (pCurTD.td==GetTDQueueHead() &&  m_fTransferInProgress  && m_pTransfer!=NULL) {
        ASSERT(pCurTD.td->pTransfer == m_pTransfer);
        if (m_pTransfer->DoneTD()) { // Complete
            if (m_pED->bfHalted!=0) { 
                m_fIsHalted=TRUE;
                //  Some Condition need reset Halt Auto
                if (GetType() ==TYPE_CONTROL)
                    m_fIsHalted=FALSE;
                else
                if ( m_pTransfer->GetFirstError() ==USB_DATA_UNDERRUN_ERROR && (m_pTransfer->GetSTransfer()->dwFlags & USB_SHORT_TRANSFER_OK)) {
                    m_fIsHalted=FALSE;
                }
                else
                if (m_pTransfer->GetFirstError() == USB_NO_ERROR)
                    m_fIsHalted=FALSE;
            }            
            DEBUGMSG(ZONE_TRANSFER, (TEXT("CQueuedPipe::CheckForDoneTransfers: completing xfr %08x\n"), m_pTransfer));
        }
        else {
            if (m_pED->bfSkip ==0 ) { // Only do schedule when endpoint is not stopped.
                DWORD dwLastQueueAddr = m_pED->paTdQueueTail;
                BOOL bReturn=m_pTransfer->ScheduleTD();
                ASSERT(bReturn);
                m_pED->paTdQueueTail = GetTDQueueTailPhysAddr();
                if (dwLastQueueAddr != m_pED->paTdQueueTail)
                    UpdateListControl(TRUE, TRUE);
            }
            BOOL fTransferDone = !m_fTransferInProgress;
            LeaveCriticalSection( &m_csPipeLock );
            DEBUGMSG( ZONE_TRANSFER, (TEXT("-CQueuedPipe(%s)::CheckForDoneTransfers, returning BOOL %d\n"), GetPipeType(), fTransferDone) );
            return fTransferDone;
        }
    }
    else { // Error Condition.
        ASSERT(FALSE);
        DEBUGMSG( ZONE_ERROR, (TEXT("!!!!CQueuedPipe::CheckForDoneTransfers -  Unwanted TD =%x \n"),pCurTD));
    }

    m_pED->bfSkip = 1;
    DWORD toggleCarry = m_pED->bfToggleCarry; // this field is actually part of QueueHead
    if (!IsEDQEmpty(m_pED)) {
        if (m_pED->bfHalted == 0 ) { // It is live endpoint
            m_pCOhcd->CHW::WaitOneFrame();  //need to find a way around this
        }
        // ED stopped. We can clean up our QUEUE here.
        QueueInit();
        m_pED->paTdQueueHead = m_pED->paTdQueueTail = GetTDQueueHeadPhys();
    }
    delete m_pTransfer;
    m_pTransfer = NULL;
    m_fTransferInProgress = FALSE;
    
    if (GetType () == TYPE_CONTROL) 
        m_pED->bfToggleCarry = 1;
    else
        m_pED->bfToggleCarry = toggleCarry;
        
    m_pED->bfHalted = m_fIsHalted ? 1 : 0;
    m_pED->bfSkip = 0;
    
    if (m_pFirstTransfer) // This is More transfer
        ScheduleTransfer();
    BOOL fTransferDone = !m_fTransferInProgress;
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_TRANSFER, (TEXT("-CQueuedPipe(%s)::CheckForDoneTransfers, returning BOOL %d\n"), GetPipeType(), fTransferDone) );
    return fTransferDone;
}


// ******************************************************************               
// Scope: protected virtual
void CQueuedPipe::UpdateInterruptQHTreeLoad( IN const UCHAR, // branch - ignored
                                             IN const int ) // deltaLoad - ignored
//
// Purpose: Change the load counts of all members in m_interruptQHTree
//          which are on branch "branch" by deltaLoad. This needs
//          to be done when interrupt QHs are added/removed to be able
//          to find optimal places to insert new queues
//
// Parameters: branch - indicates index into m_interruptQHTree array
//                      after which the branch point occurs (i.e. if
//                      a new queue is inserted after m_interruptQHTree[ 8 ],
//                      branch will be 8)
//
//             deltaLoad - amount by which to alter the Load counts 
//                         of affected m_interruptQHTree members
//
// Returns: Nothing
//
// Notes: Currently, there is nothing to do for BULK/CONTROL queues.
//        INTERRUPT should override this function
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CQueuedPipe(%s)::UpdateInterruptQHTreeLoad\n"), GetPipeType() ) );
    DEBUGMSG( ZONE_PIPE && ZONE_WARNING, (TEXT("CQueuedPipe(%s)::UpdateInterruptQHTreeLoad - doing nothing\n"), GetPipeType()) );
    DEBUGCHK( (m_usbEndpointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) != USB_ENDPOINT_TYPE_INTERRUPT );
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CQueuedPipe(%s)::UpdateInterruptQHTreeLoad\n"), GetPipeType() ) );
}

// ******************************************************************               
// Scope: public
CBulkPipe::CBulkPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
                      IN const BOOL fIsLowSpeed,
                      IN const UCHAR bDeviceAddress,
                      IN COhcd * const pCOhcd)
//
// Purpose: Constructor for CBulkPipe
//
// Parameters: See CQueuedPipe::CQueuedPipe
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
: CQueuedPipe( lpEndpointDescriptor, fIsLowSpeed, bDeviceAddress,pCOhcd ) // constructor for base class
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CBulkPipe::CBulkPipe\n")) );
    DEBUGCHK( m_usbEndpointDescriptor.bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE &&
              m_usbEndpointDescriptor.bLength >= sizeof( USB_ENDPOINT_DESCRIPTOR ) &&
              (m_usbEndpointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK );

    DEBUGCHK( !fIsLowSpeed ); // bulk pipe must be high speed

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CBulkPipe::CBulkPipe\n")) );
}

// ******************************************************************               
// Scope: public
CBulkPipe::~CBulkPipe( )
//
// Purpose: Destructor for CBulkPipe
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CBulkPipe::~CBulkPipe\n")) );
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CBulkPipe::~CBulkPipe\n")) );
}


// ******************************************************************               
// Scope: public (Implements CPipe::OpenPipe = 0)
HCD_REQUEST_STATUS CBulkPipe::OpenPipe( void )
//
// Purpose: Create the data structures necessary to conduct
//          transfers on this pipe
//
// Parameters: None
//
// Returns: requestOK - if pipe opened
//
//          requestFailed - if pipe was not opened
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE, (TEXT("+CBulkPipe::OpenPipe\n") ) );

    EnterCriticalSection( &m_csPipeLock );

    // if this fails, we have a low speed Bulk device
    // which is not allowed by the USB spec
    DEBUGCHK( !m_fIsLowSpeed );
    if (!QueueInit()) {
        LeaveCriticalSection( &m_csPipeLock );
        DEBUGMSG( ZONE_WARNING, (TEXT("-CBulkPipe::OpenPipe - no memory for TD\n")) );
        return requestFailed;
    }

    if ( !m_pCOhcd->CHCCAera::m_pCPhysMem->AllocateMemory(DEBUG_PARAM( TEXT("OpenPipe ED") )
                                      sizeof(ED),
                                      (PUCHAR *) &m_pED,
                                      GetMemoryAllocationFlags() ) ) {
        LeaveCriticalSection( &m_csPipeLock );
        DEBUGMSG( ZONE_WARNING, (TEXT("-CBulkPipe::OpenPipe - no memory for ED\n")) );
        return requestFailed;
    }
#ifdef DEBUG
    m_pCOhcd->CHCCAera::m_debug_QHMemoryAllocated += sizeof( ED );
    DEBUGMSG( ZONE_QH, (TEXT("CBulkPipe::OpenPipe - alloc 1 ED, total bytes = %d\n"),
                        m_pCOhcd->CHCCAera::m_debug_QHMemoryAllocated) );
#endif // DEBUG

    memset((PUCHAR) m_pED, 0, sizeof(ED));
    m_pED->bfFunctionAddress = m_bBusAddress;
    m_pED->bfEndpointNumber = m_bEndpointAddress;
    m_pED->bfDirection = USB_ENDPOINT_DIRECTION_IN(m_bEndpointAddress) ? TD_IN_PID : TD_OUT_PID;
    m_pED->bfIsLowSpeed = 0;
    m_pED->bfIsIsochronous =  0;
    m_pED->bfMaxPacketSize = m_usbEndpointDescriptor.wMaxPacketSize;
    m_pED->paTdQueueTail = m_pED->paTdQueueHead = GetTDQueueHeadPhys();

    EnterCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );
    m_pED->paNextEd = *(m_pCOhcd->CHW::m_pBulkHead);
    *(m_pCOhcd->CHW::m_pBulkHead) = m_pCOhcd->CHCCAera::m_pCPhysMem->VaToPa((PUCHAR) m_pED);
    LeaveCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );

    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_PIPE, (TEXT("-CBulkPipe::OpenPipe, returning HCD_REQUEST_STATUS %d\n"), 0) );
    return requestOK;
}

ULONG * CBulkPipe::GetListHead( IN const BOOL fEnable ) 
{
    m_pCOhcd->CHW::ListControl(CHW::LIST_BULK, fEnable, FALSE);
    return m_pCOhcd->CHW::m_pBulkHead;
};
void CBulkPipe::UpdateListControl(BOOL bEnable, BOOL bFill) 
{
    m_pCOhcd->CHW::ListControl(CHW::LIST_BULK, bEnable, bFill);
}


// ******************************************************************               
// Scope: private (Implements CPipe::AreTransferParametersValid = 0)
BOOL CBulkPipe::AreTransferParametersValid( const STransfer *pTransfer ) const 
//
// Purpose: Check whether this class' transfer parameters are valid.
//          This includes checking m_transfer, m_pPipeQH, etc
//
// Parameters: None (all parameters are vars of class)
//
// Returns: TRUE if parameters valid, else FALSE
//
// Notes: Assumes m_csPipeLock already held
// ******************************************************************
{
    if (pTransfer == NULL)
        return FALSE;
    
    //DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CBulkPipe::AreTransferParametersValid\n")) );

    // these parameters aren't used by CBulkPipe, so if they are non NULL,
    // it doesn't present a serious problem. But, they shouldn't have been
    // passed in as non-NULL by the calling driver.
    DEBUGCHK( pTransfer->adwIsochErrors == NULL && // ISOCH
              pTransfer->adwIsochLengths == NULL && // ISOCH
              pTransfer->aLengths == NULL && // ISOCH
              pTransfer->lpvControlHeader == NULL ); // CONTROL
    // this is also not a serious problem, but shouldn't happen in normal
    // circumstances. It would indicate a logic error in the calling driver.
    DEBUGCHK( !(pTransfer->lpfnCallback == NULL && pTransfer->lpvCallbackParameter != NULL) );
    // DWORD                     pTransfer->dwStartingFrame (ignored - ISOCH)
    // DWORD                     pTransfer->dwFrames (ignored - ISOCH)

    BOOL fValid = (
                    pTransfer->address > 0 &&
                    pTransfer->address <= USB_MAX_ADDRESS &&
                    (pTransfer->lpvClientBuffer != NULL || pTransfer->dwBufferSize == 0) &&
                    // paClientBuffer could be 0 or !0
                    pTransfer->lpfComplete != NULL &&
                    pTransfer->lpdwBytesTransferred != NULL &&
                    pTransfer->lpdwError != NULL );

    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE && !fValid, (TEXT("!CBulkPipe::AreTransferParametersValid, returning BOOL %d\n"), fValid) );
    return fValid;
}

// ******************************************************************               
// Scope: private (Implements CPipe::ScheduleTransfer = 0)
HCD_REQUEST_STATUS CBulkPipe::ScheduleTransfer( void ) 
//
// Purpose: Schedule a USB Transfer on this pipe
//
// Parameters: None (all parameters are in m_transfer)
//
// Returns: requestOK if transfer issued ok, else requestFailed
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CBulkPipe::ScheduleTransfer %08x\n"), m_pTransfer) );

    EnterCriticalSection( &m_csPipeLock );


    // specific to CBulkPipe
    if ( !m_fTransferInProgress || m_pTransfer ==NULL) {
    //From OHCI 1.0a section 5.2.8.2
        if (m_pTransfer ==NULL && m_pFirstTransfer!=NULL) {
            DEBUGCHK( m_pED->bfMaxPacketSize <= 64 );
            m_pTransfer = m_pFirstTransfer;
            m_pFirstTransfer = m_pFirstTransfer->GetNextTransfer();
            if (m_pFirstTransfer == NULL) { // This is end of Queue
                m_pLastTransfer = NULL;
            }
            if (!IsTDQueueFull()) {
                BOOL bReturn=m_pTransfer ->ScheduleTD();            
                ASSERT(bReturn);
                m_pED->paTdQueueTail = GetTDQueueTailPhysAddr();
            }
            else
                ASSERT(FALSE);

            m_fTransferInProgress = 1;
            m_pCOhcd->CHW::ListControl(CHW::LIST_BULK, TRUE, TRUE);
        }
    }
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("-CBulkPipe::ScheduleTransfer, returning HCD_REQUEST_STATUS %d\n"), requestOK) );
    return requestOK;
}


// ******************************************************************               
// Scope: public
CControlPipe::CControlPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
                            IN const BOOL fIsLowSpeed,
                            IN const UCHAR bDeviceAddress ,
                            IN COhcd * const pCOhcd)
//
// Purpose: Constructor for CControlPipe
//
// Parameters: See CQueuedPipe::CQueuedPipe
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
: CQueuedPipe( lpEndpointDescriptor, fIsLowSpeed, bDeviceAddress,pCOhcd ) // constructor for base class
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CControlPipe::CControlPipe\n")) );
    DEBUGCHK( m_usbEndpointDescriptor.bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE &&
              m_usbEndpointDescriptor.bLength >= sizeof( USB_ENDPOINT_DESCRIPTOR ) &&
              (m_usbEndpointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_CONTROL );

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CControlPipe::CControlPipe\n")) );
}

// ******************************************************************               
// Scope: public
CControlPipe::~CControlPipe( )
//
// Purpose: Destructor for CControlPipe
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CControlPipe::~CControlPipe\n")) );
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CControlPipe::~CControlPipe\n")) );
}

// ******************************************************************               
// Scope: public (Implements CPipe::OpenPipe = 0)
HCD_REQUEST_STATUS CControlPipe::OpenPipe( void )
//
// Purpose: Create the data structures necessary to conduct
//          transfers on this pipe
//
// Parameters: None
//
// Returns: requestOK - if pipe opened
//
//          requestFailed - if pipe was not opened
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE, (TEXT("+CControlPipe::OpenPipe\n") ) );

    EnterCriticalSection( &m_csPipeLock );
    if (!QueueInit()) {
        LeaveCriticalSection( &m_csPipeLock );
        DEBUGMSG( ZONE_WARNING, (TEXT("-CBulkPipe::OpenPipe - no memory for TD\n")) );
        return requestFailed;
    }

    if ( !m_pCOhcd->CHCCAera::m_pCPhysMem->AllocateMemory(DEBUG_PARAM( TEXT("OpenPipe ED") )
                                      sizeof(ED),
                                      (PUCHAR *) &m_pED,
                                      GetMemoryAllocationFlags() ) ) {
        LeaveCriticalSection( &m_csPipeLock );
        DEBUGMSG( ZONE_WARNING, (TEXT("-CControlPipe::OpenPipe - no memory for ED\n")) );
        return requestFailed;
    }
#ifdef DEBUG
    m_pCOhcd->CHCCAera::m_debug_QHMemoryAllocated += sizeof( ED );
    DEBUGMSG( ZONE_QH, (TEXT("CControlPipe::OpenPipe - alloc 1 ED, total bytes = %d\n"),
                        m_pCOhcd->CHCCAera::m_debug_QHMemoryAllocated) );
#endif // DEBUG
    
    memset((PUCHAR) m_pED, 0, sizeof(ED));
    m_pED->bfFunctionAddress = m_bBusAddress;
    m_pED->bfEndpointNumber = m_bEndpointAddress;
    m_pED->bfDirection = TD_SETUP_PID;
    m_pED->bfIsLowSpeed = m_fIsLowSpeed ? 1 : 0;
    m_pED->bfIsIsochronous =  0;
    m_pED->bfMaxPacketSize = m_usbEndpointDescriptor.wMaxPacketSize;
    m_pED->paTdQueueTail = m_pED->paTdQueueHead =GetTDQueueHeadPhys();

    EnterCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );
    m_pED->paNextEd = *(m_pCOhcd->CHW::m_pControlHead);
    *(m_pCOhcd->CHW::m_pControlHead) = m_pCOhcd->CHCCAera::m_pCPhysMem->VaToPa((PUCHAR) m_pED);
    LeaveCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );

    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_PIPE, (TEXT("-CControlPipe::OpenPipe\n") ) );
    return requestOK;
}

// ******************************************************************
// Scope: public
void CControlPipe::ChangeMaxPacketSize( IN const USHORT wMaxPacketSize )
//
// Purpose: Update the max packet size for this pipe. This should
//          ONLY be done for control endpoint 0 pipes. When the endpoint0
//          pipe is first opened, it has a max packet size of 
//          ENDPOINT_ZERO_MIN_MAXPACKET_SIZE. After reading the device's
//          descriptor, the device attach procedure can update the size.
//
// Parameters: wMaxPacketSize - new max packet size for this pipe
//
// Returns: Nothing
//
// Notes:   This function should only be called by the Hub AttachDevice
//          procedure
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CControlPipe::ChangeMaxPacketSize - new wMaxPacketSize = %d\n"), wMaxPacketSize) );

    EnterCriticalSection( &m_csPipeLock );

    // this pipe should be for endpoint 0, control pipe
    DEBUGCHK( (m_usbEndpointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_CONTROL &&
              (m_usbEndpointDescriptor.bEndpointAddress & TD_ENDPOINT_MASK) == 0 );
    // update should only be called if the old address was ENDPOINT_ZERO_MIN_MAXPACKET_SIZE
    DEBUGCHK( m_usbEndpointDescriptor.wMaxPacketSize == ENDPOINT_ZERO_MIN_MAXPACKET_SIZE );
    // this function should only be called if we are increasing the max packet size.
    // in addition, the USB spec 1.0 section 9.6.1 states only the following
    // wMaxPacketSize are allowed for endpoint 0
    DEBUGCHK( wMaxPacketSize > ENDPOINT_ZERO_MIN_MAXPACKET_SIZE &&
              (wMaxPacketSize == 16 ||
               wMaxPacketSize == 32 ||
               wMaxPacketSize == 64) );
    if (m_usbEndpointDescriptor.wMaxPacketSize != wMaxPacketSize) {
        m_pED->bfSkip = 1;
        m_pCOhcd->CHW::WaitOneFrame();
        m_usbEndpointDescriptor.wMaxPacketSize = wMaxPacketSize;
        m_pED->bfMaxPacketSize = wMaxPacketSize;
        if (QueueReInit(max(GetMaxTDDataSize(),(DWORD)m_usbEndpointDescriptor.wMaxPacketSize & 0x7ff))) {
            m_pED->paTdQueueTail = m_pED->paTdQueueHead =GetTDQueueHeadPhys();
            m_pED->bfSkip = 0;
        }
    }
    ASSERT(m_pED->bfMaxPacketSize == wMaxPacketSize);
    // Re-Initialize the PIPE because maxPacketSize has changed.

    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CControlPipe::ChangeMaxPacketSize - new wMaxPacketSize = %d\n"), wMaxPacketSize) );
}
ULONG * CControlPipe::GetListHead( IN const BOOL fEnable ) 
{
    m_pCOhcd->CHW::ListControl(CHW::LIST_CONTROL, fEnable, FALSE);
    return m_pCOhcd->CHW::m_pControlHead;
};
void CControlPipe::UpdateListControl(BOOL bEnable, BOOL bFill) 
{
    m_pCOhcd->CHW::ListControl(CHW::LIST_CONTROL, bEnable, bFill);
}
// ******************************************************************               
// Scope: private (Implements CPipe::AreTransferParametersValid = 0)
BOOL CControlPipe::AreTransferParametersValid( const STransfer *pTransfer ) const 
//
// Purpose: Check whether this class' transfer parameters are valid.
//          This includes checking m_transfer, m_pPipeQH, etc
//
// Parameters: None (all parameters are vars of class)
//
// Returns: TRUE if parameters valid, else FALSE
//
// Notes: Assumes m_csPipeLock already held
// ******************************************************************
{
    if (pTransfer == NULL)
        return FALSE;
    
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CControlPipe::AreTransferParametersValid\n")) );

    // these parameters aren't used by CControlPipe, so if they are non NULL,
    // it doesn't present a serious problem. But, they shouldn't have been
    // passed in as non-NULL by the calling driver.
    DEBUGCHK( pTransfer->adwIsochErrors == NULL && // ISOCH
              pTransfer->adwIsochLengths == NULL && // ISOCH
              pTransfer->aLengths == NULL ); // ISOCH
    // this is also not a serious problem, but shouldn't happen in normal
    // circumstances. It would indicate a logic error in the calling driver.
    DEBUGCHK( !(pTransfer->lpfnCallback == NULL && pTransfer->lpvCallbackParameter != NULL) );
    // DWORD                     pTransfer->dwStartingFrame; (ignored - ISOCH)
    // DWORD                     pTransfer->dwFrames; (ignored - ISOCH)

    BOOL fValid = ( 
                    pTransfer->address <= USB_MAX_ADDRESS &&
                    pTransfer->lpvControlHeader != NULL &&
                    pTransfer->lpfComplete != NULL &&
                    pTransfer->lpdwBytesTransferred != NULL &&
                    pTransfer->lpdwError != NULL );
    if ( fValid ) {
        if ( pTransfer->dwFlags & USB_IN_TRANSFER ) {
            fValid = (pTransfer->lpvClientBuffer != NULL &&
                      // paClientBuffer could be 0 or !0
                      pTransfer->dwBufferSize > 0);
        } else {
            fValid = ( (pTransfer->lpvClientBuffer == NULL && 
                        pTransfer->paClientBuffer == 0 &&
                        pTransfer->dwBufferSize == 0) ||
                       (pTransfer->lpvClientBuffer != NULL &&
                        // paClientBuffer could be 0 or !0
                        pTransfer->dwBufferSize > 0) );
        }
    }

    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("-CControlPipe::AreTransferParametersValid, returning BOOL %d\n"), fValid) );
    return fValid;
}

// ******************************************************************               
// Scope: private (Implements CPipe::ScheduleTransfer = 0)
HCD_REQUEST_STATUS CControlPipe::ScheduleTransfer( void ) 
//
// Purpose: Schedule a USB Transfer on this pipe
//
// Parameters: None (all parameters are in m_transfer)
//
// Returns: requestOK if transfer issued ok, else requestFailed
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CControlPipe::ScheduleTransfer\n")) );
    
    EnterCriticalSection( &m_csPipeLock );
    
    // this should already have been checked by CPipe::IssueTransfer
    m_pED->bfFunctionAddress = m_bBusAddress;

    if ( !m_fTransferInProgress || m_pTransfer ==NULL ) {
    //From OHCI 1.0a section 5.2.8.2
        if ( m_pTransfer==NULL && m_pFirstTransfer!=NULL ) {
            DEBUGCHK( m_pED->bfMaxPacketSize <= 64 );
            m_pTransfer = m_pFirstTransfer;
            DEBUGCHK( m_pED->bfFunctionAddress == m_pTransfer->GetSTransfer()->address );
            m_pFirstTransfer = m_pFirstTransfer->GetNextTransfer();
            if (m_pFirstTransfer == NULL) { // This is end of Queue
                m_pLastTransfer = NULL;
            }
            if (!IsTDQueueFull()) {
                BOOL bReturn=m_pTransfer ->ScheduleTD();            
                ASSERT(bReturn);
                m_pED->paTdQueueTail =GetTDQueueTailPhysAddr();
            }
            else
                ASSERT(FALSE);
        }
        else 
            ASSERT(FALSE);
        m_fTransferInProgress = 1;
        m_pCOhcd->CHW::ListControl(CHW::LIST_CONTROL, TRUE, TRUE);
    }    
    LeaveCriticalSection( &m_csPipeLock );
    
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("-CControlPipe::ScheduleTransfer\n")) );
    return requestOK;
}

// ******************************************************************               
// Scope: public
CInterruptPipe::CInterruptPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
                                IN const BOOL fIsLowSpeed,
                                IN const UCHAR bDeviceAddress,
                                IN COhcd * const pCOhcd)
//
// Purpose: Constructor for CInterruptPipe
//
// Parameters: See CQueuedPipe::CQueuedPipe
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
: CQueuedPipe( lpEndpointDescriptor, fIsLowSpeed, bDeviceAddress, pCOhcd ) // constructor for base class
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CInterruptPipe::CInterruptPipe\n")) );
    DEBUGCHK( m_usbEndpointDescriptor.bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE &&
              m_usbEndpointDescriptor.bLength >= sizeof( USB_ENDPOINT_DESCRIPTOR ) &&
              (m_usbEndpointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT );

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CInterruptPipe::CInterruptPipe\n")) );
}

// ******************************************************************               
// Scope: public
CInterruptPipe::~CInterruptPipe( )
//
// Purpose: Destructor for CInterruptPipe
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CInterruptPipe::~CInterruptPipe\n")) );
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CInterruptPipe::~CInterruptPipe\n")) );
}

// ******************************************************************               
// Scope: private
int CInterruptPipe::ComputeLoad( IN const int branch ) const
//
// Purpose: Calculates the bandwidth load on the specified
//          branch of the interruptQHTree
//
// Parameters: branch to examine
//
// Returns: the number of active EDs on the branch.
//
// Notes: Caller must hold the QHScheduleLock critsec.
// ******************************************************************
{
    // Since we know that this method exists solely for the purpose of placing
    // a new interrupt pipe ED into the tree, and since the new pipe's service
    // interval cannot change over the course of said operation, we really
    // only need to find the load up to the next service level. Following that
    // node, nothing can change because the QHScheduleLock is being held.

    DEBUGCHK( branch >= 1 && branch < OHCD_MAX_INTERRUPT_INTERVAL*2 );
    
    // The head of the next service level is the link as calculated
    // in CPipe::Initialize. To make that calculation, we have to find
    // the largest power of two that is less than or equal to branch.
    int pow = OHCD_MAX_INTERRUPT_INTERVAL;
    while (pow > branch)
        pow >>= 1;
    int link = (pow ^ branch) | (pow >> 1);

    DEBUGCHK( link >= 0 && link < OHCD_MAX_INTERRUPT_INTERVAL );
    P_ED pEndED = m_pCOhcd->CHCCAera::m_interruptQHTree[link];

    DEBUGCHK( m_pCOhcd->CHCCAera::m_interruptQHTree[branch] != NULL && pEndED != NULL );
    int load = 0;
    for ( P_ED pED = (P_ED) m_pCOhcd->CHCCAera::m_pCPhysMem->PaToVa(m_pCOhcd->CHCCAera::m_interruptQHTree[branch]->paNextEd);
          pED != pEndED;
          pED = (P_ED) m_pCOhcd->CHCCAera::m_pCPhysMem->PaToVa(pED->paNextEd) ) {
        ++load;
    }
    return load;
}

// ******************************************************************               
// Scope: public (Implements CPipe::OpenPipe = 0)
HCD_REQUEST_STATUS CInterruptPipe::OpenPipe( void )
//
// Purpose: Create the data structures necessary to conduct
//          transfers on this pipe
//
// Parameters: None
//
// Returns: requestOK - if pipe opened
//
//          requestFailed - if pipe was not opened
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE, (TEXT("+CInterruptPipe::OpenPipe\n") ) );

    HCD_REQUEST_STATUS status = requestFailed;

    EnterCriticalSection( &m_csPipeLock );
    if (!QueueInit()) {
        LeaveCriticalSection( &m_csPipeLock );
        DEBUGMSG( ZONE_WARNING, (TEXT("-CBulkPipe::OpenPipe - no memory for TD\n")) );
        return requestFailed;
    }


    // if this fails, someone is trying to open
    // an already opened pipe
    DEBUGCHK( m_pED == NULL );
    
    if ( !m_pCOhcd->CHCCAera::m_pCPhysMem->AllocateMemory(DEBUG_PARAM( TEXT("OpenPipe ED") )
                                      sizeof(ED),
                                      (PUCHAR *) &m_pED,
                                      GetMemoryAllocationFlags() ) ) {
        DEBUGMSG( ZONE_WARNING, (TEXT("-CInterruptPipe::OpenPipe - no memory for ED\n")) );
        LeaveCriticalSection( &m_csPipeLock );
        return requestFailed;
    }
#ifdef DEBUG
    m_pCOhcd->CHCCAera::m_debug_QHMemoryAllocated += sizeof( ED );
    DEBUGMSG( ZONE_QH, (TEXT("CInterruptPipe::OpenPipe - alloc 1 ED, total bytes = %d\n"),
                        m_pCOhcd->CHCCAera::m_debug_QHMemoryAllocated) );
#endif // DEBUG

    // Interrupt QHs are a bit complicated. See comment
    // in Initialize() routine as well.
    //
    // Essentially, we need to change the poll interval to be
    // a power of 2 which is <= OHCD_MAX_INTERRUPT_INTERVAL,
    // and then find a good spot in the m_pCOhcd->CHCCAera::m_interruptQHTree to
    // insert the new QH
    //
    // first, find a new pollinterval
    {
        if ( m_usbEndpointDescriptor.bInterval >= 1 ) {
            UCHAR newInterval = OHCD_MAX_INTERRUPT_INTERVAL;
            while ( newInterval > m_usbEndpointDescriptor.bInterval ) {
                newInterval >>= 1;
            }
#ifdef DEBUG
            if ( newInterval != m_usbEndpointDescriptor.bInterval ) {
                DEBUGCHK( newInterval >= 1 &&
                          newInterval < m_usbEndpointDescriptor.bInterval &&
                          OHCD_MAX_INTERRUPT_INTERVAL % newInterval == 0 );
                DEBUGMSG( ZONE_WARNING, (TEXT("CInterruptPipe::OpenPipe - setting the poll interval to %d ms instead of %d ms\n"), newInterval, m_usbEndpointDescriptor.bInterval ));
            }
#endif // DEBUG
            m_usbEndpointDescriptor.bInterval = newInterval;
        } else {
            DEBUGCHK( 0 );// is there REALLY a device with an interrupt
            // endpoint of poll interval 0??? Or, has there
            // been an error retrieving descriptors
            DEBUGMSG( ZONE_WARNING, (TEXT("CInterruptPipe::OpenPipe - warning! Interrupt pipe has poll interval 0. Changing to 1\n")) );
            m_usbEndpointDescriptor.bInterval = 1;
        }
    }


    memset((PUCHAR) m_pED, 0, sizeof(ED));
    m_pED->bfFunctionAddress = m_bBusAddress;
    m_pED->bfEndpointNumber = m_bEndpointAddress;
    m_pED->bfDirection = USB_ENDPOINT_DIRECTION_IN(m_bEndpointAddress) ? TD_IN_PID : TD_OUT_PID;
    m_pED->bfIsLowSpeed = m_fIsLowSpeed ? 1 : 0;
    m_pED->bfIsIsochronous =  0;
    m_pED->bfMaxPacketSize = m_usbEndpointDescriptor.wMaxPacketSize;
    m_pED->paTdQueueTail = m_pED->paTdQueueHead = GetTDQueueHeadPhys();
    // set the nextEd link after computing the branch into which we'll be inserted

    EnterCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );

    // now find where to insert the QH
    // Given a poll interval of 2^k, we can insert the QH
    // into the interrupt tree after any of the QHs
    // 2^k, 2^k + 1, ... , 2^(k+1) - 1
    UCHAR insertAfter = m_usbEndpointDescriptor.bInterval;
    int insertLoad = 1000;      // higher than largest possible load
    for (int index = insertAfter; index < 2 * m_usbEndpointDescriptor.bInterval; ++index) {
        register int load = ComputeLoad(index);
        if (load < insertLoad) {
            insertAfter = index;
            insertLoad = load;
            if (load == 0)
                break;          // can't get any lower than zero so shortcut it out of here
        }
    }
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("CInterruptPipe::OpenPipe - inserting interrupt QH after m_pCOhcd->CHCCAera::m_interruptQHTree[ %d ], load count = %d\n"), insertAfter, insertLoad ) );

    m_iListHead = insertAfter;
    m_pED->paNextEd = m_pCOhcd->CHCCAera::m_interruptQHTree[insertAfter]->paNextEd;
    m_pCOhcd->CHCCAera::m_interruptQHTree[insertAfter]->paNextEd = GetQHPhysAddr( m_pED );

//    UpdateInterruptQHTreeLoad( (UCHAR) m_pPipeQH->dwInterruptTree.BranchIndex, 1 );

    LeaveCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );

    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_PIPE, (TEXT("-CInterruptPipe::OpenPipe, returning HCD_REQUEST_STATUS %d\n"), 0) );
    return requestOK;
}

// ******************************************************************               
// Scope: private (over-rides CQueuedPipe::UpdateInterruptQHTreeLoad)
void CInterruptPipe::UpdateInterruptQHTreeLoad( IN const UCHAR branch,
                                                IN const int   deltaLoad )
//
// Purpose: Change the load counts of all members in m_pCOhcd->CHCCAera::m_interruptQHTree
//          which are on branch "branch" by deltaLoad. This needs
//          to be done when interrupt QHs are added/removed to be able
//          to find optimal places to insert new queues
//
// Parameters: See CQueuedPipe::UpdateInterruptQHTreeLoad
//
// Returns: Nothing
//
// Notes:
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CInterruptPipe::UpdateInterruptQHTreeLoad - branch = %d, deltaLoad = %d\n"), branch, deltaLoad) );
    DEBUGCHK( branch >= 1 && branch < 2 * OHCD_MAX_INTERRUPT_INTERVAL );

#ifdef JEFFRO
    // first step - need to find the greatest power of 2 which is
    // <= branch
    UCHAR pow = OHCD_MAX_INTERRUPT_INTERVAL;
    while ( pow > branch ) {
        pow >>= 1;
    }

    EnterCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );

    // In the reverse direction, any queue which will eventually
    // point to m_pCOhcd->CHCCAera::m_interruptQHTree[ branch ] needs to get
    // its dwInterruptTree.Load incremented. These are the queues
    // branch + n * pow, n = 1, 2, 3, ...
    for ( UCHAR link = branch + pow; link < 2 * OHCD_MAX_INTERRUPT_INTERVAL; link += pow ) {
        DEBUGCHK( m_pCOhcd->CHCCAera::m_interruptQHTree[ link ]->dwInterruptTree.Load <= m_pCOhcd->CHCCAera::m_interruptQHTree[ branch ]->dwInterruptTree.Load );
        m_pCOhcd->CHCCAera::m_interruptQHTree[ link ]->dwInterruptTree.Load += deltaLoad;
    }
    // In the forward direction, any queue that 
    // m_pCOhcd->CHCCAera::m_interruptQHTree[ branch ] eventually points to
    // needs its dwInterruptTree.Load incremented. These queues
    // are found using the same algorithm as CPipe::Initialize();
    link = branch;
    while ( link >= 1 ) {
        DEBUGCHK( ( link & pow ) &&
                  ( (link ^ pow) | (pow / 2) ) < link );
        DEBUGCHK( link == branch ||
                  m_pCOhcd->CHCCAera::m_interruptQHTree[ link ]->dwInterruptTree.Load + deltaLoad >= m_pCOhcd->CHCCAera::m_interruptQHTree[ branch ]->dwInterruptTree.Load );
        m_pCOhcd->CHCCAera::m_interruptQHTree[ link ]->dwInterruptTree.Load += deltaLoad;
        link ^= pow;
        pow >>= 1;
        link |= pow;
    }

    LeaveCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );
#else
    UNREFERENCED_PARAMETER(deltaLoad );
    UNREFERENCED_PARAMETER(branch);
#endif //JEFFRO

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CInterruptPipe::UpdateInterruptQHTreeLoad - branch = %d, deltaLoad = %d\n"), branch, deltaLoad) );
}

// ******************************************************************               
// USHORT CInterruptPipe::GetNumTDsNeeded( const STransfer *pTransfer ) const 
//
// CInterruptPipe inherits this method from CQueuedPipe.
//
ULONG * CInterruptPipe::GetListHead( IN const BOOL fEnable ) 
{
    m_pCOhcd->CHW::ListControl(CHW::LIST_INTERRUPT, fEnable, FALSE);
    // return the address of the QH beginning the list for this pipe
    return &(m_pCOhcd->CHCCAera::m_interruptQHTree[m_iListHead]->paNextEd);
};
void CInterruptPipe::UpdateListControl(BOOL bEnable, BOOL bFill) 
{
    m_pCOhcd->CHW::ListControl(CHW::LIST_INTERRUPT, bEnable, bFill);
}

// ******************************************************************               
// Scope: private (Implements CPipe::AreTransferParametersValid = 0)
BOOL CInterruptPipe::AreTransferParametersValid( const STransfer *pTransfer ) const
//
// Purpose: Check whether this class' transfer parameters are valid.
//          This includes checking m_transfer, m_pPipeQH, etc
//
// Parameters: None (all parameters are vars of class)
//
// Returns: TRUE if parameters valid, else FALSE
//
// Notes: Assumes m_csPipeLock already held
// ******************************************************************
{
    if (pTransfer == NULL)
        return FALSE;
    
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CInterruptPipe::AreTransferParametersValid\n")) );

    // these parameters aren't used by CInterruptPipe, so if they are non NULL,
    // it doesn't present a serious problem. But, they shouldn't have been
    // passed in as non-NULL by the calling driver.
    DEBUGCHK( pTransfer->adwIsochErrors == NULL && // ISOCH
              pTransfer->adwIsochLengths == NULL && // ISOCH
              pTransfer->aLengths == NULL && // ISOCH
              pTransfer->lpvControlHeader == NULL ); // CONTROL
    // this is also not a serious problem, but shouldn't happen in normal
    // circumstances. It would indicate a logic error in the calling driver.
    DEBUGCHK( !(pTransfer->lpfnCallback == NULL && pTransfer->lpvCallbackParameter != NULL) );
    // DWORD                     pTransfer->dwStartingFrame (ignored - ISOCH)
    // DWORD                     pTransfer->dwFrames (ignored - ISOCH)

    BOOL fValid = ( m_pED != NULL &&
                    pTransfer->address > 0 &&
                    pTransfer->address <= USB_MAX_ADDRESS &&
                    (pTransfer->lpvClientBuffer != NULL || pTransfer->dwBufferSize == 0) &&
                    // paClientBuffer could be 0 or !0
                    pTransfer->lpfComplete != NULL &&
                    pTransfer->lpdwBytesTransferred != NULL &&
                    pTransfer->lpdwError != NULL );

    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("-CInterruptPipe::AreTransferParametersValid, returning BOOL %d\n"), fValid) );
    return fValid;
}

// ******************************************************************               
// Scope: private (Implements CPipe::ScheduleTransfer = 0)
HCD_REQUEST_STATUS CInterruptPipe::ScheduleTransfer( void ) 
//
// Purpose: Schedule a USB Transfer on this pipe
//
// Parameters: None (all parameters are in m_transfer)
//
// Returns: requestOK if transfer issued ok, else requestFailed
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CInterruptPipe::ScheduleTransfer\n")) );
    
    EnterCriticalSection( &m_csPipeLock );
    if ( !m_fTransferInProgress || m_pTransfer ==NULL) {
    //From OHCI 1.0a section 5.2.8.2
        if ( m_pTransfer ==NULL && m_pFirstTransfer!=NULL) {
            DEBUGCHK( m_pED->bfMaxPacketSize <= 64 );
            m_pTransfer = m_pFirstTransfer;
            m_pFirstTransfer = m_pFirstTransfer->GetNextTransfer();
            DEBUGMSG(ZONE_TRANSFER, (TEXT("CInterruptPipe::ScheduleTransfer dwFlags(0x%X), cancelId=0x%X\n"),
                             m_pTransfer->GetSTransfer()->dwFlags, m_pTransfer->GetSTransfer()->lpvCancelId));

            DEBUGCHK( m_pED->bfFunctionAddress == m_pTransfer->GetSTransfer()->address );
            if (m_pFirstTransfer == NULL) { // This is end of Queue
                m_pLastTransfer = NULL;
            }
            if (!IsTDQueueFull()) {
                BOOL bReturn=m_pTransfer ->ScheduleTD();            
                ASSERT(bReturn);
                m_pED->paTdQueueTail = GetTDQueueTailPhysAddr();
            }
            else
                ASSERT(FALSE);

            m_fTransferInProgress = 1;
            m_pCOhcd->CHW::ListControl(CHW::LIST_INTERRUPT, TRUE, TRUE);
        }
    }
        
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("-CInterruptPipe::ScheduleTransfer, returning HCD_REQUEST_STATUS %d\n"), 0) );
    return requestOK;
}

// ******************************************************************               
// Scope: public
CIsochronousPipe::CIsochronousPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
                                    IN const BOOL fIsLowSpeed,
                                    IN const UCHAR bDeviceAddress,
                                    IN COhcd * const pCOhcd)
//
// Purpose: Constructor for CIsochronousPipe
//
// Parameters: See CPipe::CPipe
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
: CPipe( lpEndpointDescriptor, fIsLowSpeed, bDeviceAddress ,pCOhcd ) // constructor for base class
, ITDQueue(pCOhcd->CHCCAera::m_pCPhysMem , 0x20 , lpEndpointDescriptor->wMaxPacketSize & 0x7ff)
, m_pWakeupTD( NULL ) // TD used to wake us up for transfers scheduled in future
, m_fUsingWakeupTD( FALSE ) // wakeup TD is not in use yet
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CIsochronousPipe::CIsochronousPipe\n")) );
    DEBUGCHK( m_usbEndpointDescriptor.bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE &&
              m_usbEndpointDescriptor.bLength >= sizeof( USB_ENDPOINT_DESCRIPTOR ) &&
              (m_usbEndpointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_ISOCHRONOUS );
    m_pFirstTransfer =  m_pLastTransfer = 0;      // ptr to last transfer in queue

    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CIsochronousPipe::CIsochronousPipe\n")) );
}

// ******************************************************************               
// Scope: public
CIsochronousPipe::~CIsochronousPipe( )
//
// Purpose: Destructor for CIsochronousPipe
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: Do not modify static variables here!!
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("+CIsochronousPipe::~CIsochronousPipe\n")) );
    // m_pWakeupTD should have been freed by the time we get here
    DEBUGCHK( m_pWakeupTD == NULL );
    DEBUGCHK( !m_fUsingWakeupTD );
    DEBUGMSG( ZONE_PIPE && ZONE_VERBOSE, (TEXT("-CIsochronousPipe::~CIsochronousPipe\n")) );
}

// ******************************************************************               
// Scope: public (Implements CPipe::OpenPipe = 0)
HCD_REQUEST_STATUS CIsochronousPipe::OpenPipe( void )
//
// Purpose: Inserting the necessary (empty) items into the
//          schedule to permit future transfers
//
// Parameters: None
//
// Returns: requestOK if pipe opened successfuly
//          requestFailed if pipe not opened
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_PIPE, (TEXT("+CIsochronousPipe::OpenPipe\n")) );

    HCD_REQUEST_STATUS status = requestOK;

    EnterCriticalSection( &m_csPipeLock );
    
    if (!QueueInit()) {
        LeaveCriticalSection( &m_csPipeLock );
        DEBUGMSG( ZONE_WARNING, (TEXT("-CIsochronousPipe::OpenPipe - no memory for TD\n")) );
        return requestFailed;
    }


    DEBUGCHK( m_usbEndpointDescriptor.bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE &&
              m_usbEndpointDescriptor.bLength >= sizeof( USB_ENDPOINT_DESCRIPTOR ) &&
              (m_usbEndpointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_ISOCHRONOUS );

    // if this fails, someone is trying to open
    // an already opened pipe
    DEBUGCHK( m_pED == NULL );
    
    if ( !m_pCOhcd->CHCCAera::m_pCPhysMem->AllocateMemory(DEBUG_PARAM( TEXT("OpenPipe ED") )
                                      sizeof(ED),
                                      (PUCHAR *) &m_pED,
                                      GetMemoryAllocationFlags() ) ) {
        DEBUGMSG( ZONE_WARNING, (TEXT("-CIsochronousPipe::OpenPipe - no memory for ED\n")) );
        LeaveCriticalSection( &m_csPipeLock );
        return requestFailed;
    }
#ifdef DEBUG
    m_pCOhcd->CHCCAera::m_debug_QHMemoryAllocated += sizeof( ED );
    DEBUGMSG( ZONE_QH, (TEXT("CIsochronousPipe::OpenPipe - alloc 1 ED, total bytes = %d\n"),
                        m_pCOhcd->CHCCAera::m_debug_QHMemoryAllocated) );
#endif // DEBUG

    memset((PUCHAR) m_pED, 0, sizeof(ED));
    m_pED->bfFunctionAddress = m_bBusAddress;
    m_pED->bfEndpointNumber = m_bEndpointAddress; // lops off the direction bit
    m_pED->bfDirection = USB_ENDPOINT_DIRECTION_IN(m_bEndpointAddress) ? TD_IN_PID : TD_OUT_PID;
    m_pED->bfIsLowSpeed = m_fIsLowSpeed ? 1 : 0;
    m_pED->bfIsIsochronous =  1;
    m_pED->bfMaxPacketSize = m_usbEndpointDescriptor.wMaxPacketSize;
    m_pED->paTdQueueTail = m_pED->paTdQueueHead = GetITDQueueHeadPhys();
    m_pFirstTransfer =  m_pLastTransfer = 0;      // ptr to last transfer in queue

    // set the nextEd link after computing the branch into which we'll be inserted

    EnterCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );
    m_pED->paNextEd = m_pCOhcd->CHCCAera::m_interruptQHTree[0]->paNextEd;
    m_pCOhcd->CHCCAera::m_interruptQHTree[0]->paNextEd = GetQHPhysAddr( m_pED );
//    UpdateInterruptQHTreeLoad( (UCHAR) m_pPipeQH->dwInterruptTree.BranchIndex, 1 );
    LeaveCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );

    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_PIPE, (TEXT("-CIsochronousPipe::OpenPipe, returning HCD_REQUEST_STATUS %d\n"), status ) );
    return status;
}

// ******************************************************************               
// Scope: public (Implements CPipe::ClosePipe = 0)
HCD_REQUEST_STATUS CIsochronousPipe::ClosePipe( void )
//
// Purpose: Abort any transfers associated with this pipe, and
//          remove its data structures from the schedule
//
// Parameters: None
//
// Returns: requestOK
//
// Notes: 
// ******************************************************************
{
    HCD_REQUEST_STATUS status = requestOK;
    
    DEBUGMSG( ZONE_PIPE, (TEXT("+CIsochronousPipe::ClosePipe\n")) );

    // Don't enter CS before calling AbortTransfer, since
    // that function will need to leave the pipe CS
    // at a certain point to avoid deadlock, and
    // won't be able to do so if the CS is nested.

    LPVOID lpCancelId;
    do {
        EnterCriticalSection( &m_csPipeLock );
        lpCancelId = (m_pFirstTransfer!=NULL?(LPVOID)(m_pFirstTransfer->GetSTransfer()->lpvCancelId):NULL);
        LeaveCriticalSection( &m_csPipeLock );
        AbortTransfer( NULL,
                       NULL,
                       lpCancelId );
    } while (lpCancelId!=NULL);
    
    EnterCriticalSection( &m_csPipeLock );

    // transfer was either never issued, finished before we got to
    // this function, or aborted above.
    DEBUGCHK( !m_fTransferInProgress );

    m_pED->bfSkip = 1;
    m_pCOhcd->CHW::WaitOneFrame();

    EnterCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );
    ULONG *pNextPhys = GetListHead(FALSE);
    ULONG thisPhys = GetQHPhysAddr(m_pED);

    while (*pNextPhys != 0 && *pNextPhys != thisPhys) {
        P_ED pnext = (P_ED) m_pCOhcd->CHCCAera::m_pCPhysMem->PaToVa(*pNextPhys);
        pNextPhys = &pnext->paNextEd;
    }
    if (*pNextPhys == 0) {
        DEBUGMSG(ZONE_WARNING, (TEXT("!CIsochronous::ClosePipe %08x doesn't exist!\n"), m_pED));
        status = requestFailed;
    } else {
        DEBUGCHK( *pNextPhys == GetQHPhysAddr(m_pED) );
        *pNextPhys = m_pED->paNextEd;

        m_pCOhcd->CHCCAera::m_pCPhysMem->FreeMemory((PUCHAR)m_pED, m_pCOhcd->CHCCAera::m_pCPhysMem->VaToPa((PUCHAR)m_pED),
                                GetMemoryAllocationFlags());
        m_pED = 0;
    }
    LeaveCriticalSection( &m_pCOhcd->CHCCAera::m_csQHScheduleLock );

    QueueDeInit();
    
    (void) GetListHead(TRUE);

    LeaveCriticalSection( &m_csPipeLock );
    DEBUGMSG( ZONE_PIPE, (TEXT("-CIsochronousPipe::ClosePipe\n")) );

    return status;
}

// ******************************************************************               
// Scope: public 
HCD_REQUEST_STATUS CIsochronousPipe::IssueTransfer( 
                                    IN const UCHAR address,
                                    IN LPTRANSFER_NOTIFY_ROUTINE const lpStartAddress,
                                    IN LPVOID const lpvNotifyParameter,
                                    IN const DWORD dwFlags,
                                    IN LPCVOID const lpvControlHeader,
                                    IN const DWORD dwStartingFrame,
                                    IN const DWORD dwFrames,
                                    IN LPCDWORD const aLengths,
                                    IN const DWORD dwBufferSize,     
                                    IN_OUT LPVOID const lpvClientBuffer,
                                    IN const ULONG paBuffer,
                                    IN LPCVOID const lpvCancelId,
                                    OUT LPDWORD const adwIsochErrors,
                                    OUT LPDWORD const adwIsochLengths,
                                    OUT LPBOOL const lpfComplete,
                                    OUT LPDWORD const lpdwBytesTransferred,
                                    OUT LPDWORD const lpdwError )
//
// Purpose: Issue a Transfer on this pipe
//
// Parameters: address - USB address to send transfer to
//
//             OTHER PARAMS - see comment in COhcd::IssueTransfer
//
// Returns: requestOK if transfer issued ok, else requestFailed
//
// Notes:   
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER, (TEXT("+CPipe(%s)::IssueTransfer, address = %d\n"), GetPipeType(), address) );
    STransfer sTransfer = { address,lpStartAddress,lpvNotifyParameter, dwFlags,lpvControlHeader,0, 
        dwStartingFrame, dwFrames,  aLengths,  dwBufferSize,  lpvClientBuffer, paBuffer, 
        lpvCancelId,  adwIsochErrors, adwIsochLengths,  lpfComplete,   lpdwBytesTransferred, lpdwError
    };
    HCD_REQUEST_STATUS  status = requestFailed;
    
    CITransfer * pTransfer = new CITransfer(this, sTransfer,NULL);
    if (pTransfer == NULL)
        return requestFailed;
    
    EnterCriticalSection( &m_csPipeLock );
    
    DWORD dwCurrentFrameNumber;
    m_pCOhcd->CHW::GetFrameNumber(&dwCurrentFrameNumber);
    if (pTransfer->GetSTransfer()->dwFlags & USB_START_ISOCH_ASAP) {
        if (m_pLastTransfer!=NULL) {
            pTransfer->GetSTransfer()->dwStartingFrame = m_pLastTransfer->GetSTransfer()->dwStartingFrame + m_pLastTransfer->GetSTransfer()->dwFrames ;
            if ((long)(pTransfer->GetSTransfer()->dwStartingFrame - dwCurrentFrameNumber) < 3 )
                pTransfer->GetSTransfer()->dwStartingFrame = dwCurrentFrameNumber + 3;
        }
        else
            // the frame number could have just rolled over,
            // so add 1ms. Then, add another 2ms to account for
            // delay in scheduling this transfer
            (pTransfer->GetSTransfer())->dwStartingFrame = dwCurrentFrameNumber + 3;
    }
    
    if ( (long)((pTransfer->GetSTransfer())->dwStartingFrame - dwCurrentFrameNumber) <  3) {
        SetLastError(ERROR_CAN_NOT_COMPLETE);
        DEBUGMSG( ZONE_TRANSFER|ZONE_WARNING,
                  (TEXT("!CIsochronousPipe::AddTransfer - cannot meet the schedule")
                   TEXT(" (reqFrame=%08x, curFrame=%08x\n"),
                   pTransfer->GetSTransfer()->dwStartingFrame, dwCurrentFrameNumber + 3) );
        delete pTransfer;
        pTransfer = NULL;
    }
    
    if (pTransfer) {
        if ( AreTransferParametersValid(pTransfer->GetSTransfer()) && pTransfer->CheckFrame(m_usbEndpointDescriptor.wMaxPacketSize)) {
            DEBUGMSG( ZONE_TRANSFER, (TEXT("CIsochronousPipe::IssueTransfer(put transfer 0x%x) int the list \n"),pTransfer));
            if (m_pLastTransfer!=NULL) {
                m_pLastTransfer->SetNextTransfer(pTransfer);
                m_pLastTransfer = pTransfer;
                ASSERT(m_pFirstTransfer !=NULL);
            }
            else {
                ASSERT(m_pFirstTransfer == NULL);
                m_pLastTransfer = pTransfer;
            }
            
            if (m_pFirstTransfer==NULL)
                m_pFirstTransfer = m_pLastTransfer;
            
            if (m_bBusAddress == 0) {
                if (address != 0) {
                    // Following assertion is not required by USB but if it's not true then
                    // there are synchronization issues between here and ScheduleTransfer.
                    DEBUGCHK( m_fTransferInProgress == FALSE );
                    // This is the non-const private member to which m_bBusAddress is a reference:
                    m_private_address = address;
                }
            } else
                DEBUGCHK( m_bBusAddress == address );
            status = ScheduleTransfer();
        }
        else {
            ASSERT(FALSE);
            delete pTransfer;
        }
    }    
    else {
         DEBUGMSG( ZONE_ERROR, (TEXT("-CPipe(%s)::IssueTransfer cannot allocate transfer params!")
                                       TEXT(" returning Failed\n"), GetPipeType() ) );
    }
    m_fTransferInProgress = (m_pFirstTransfer!=NULL);
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_TRANSFER, (TEXT("-CPipe(%s)::IssueTransfer - address = %d, xfr %08x can %08x returning STATUS %d\n"), GetPipeType(), address, pTransfer, lpvCancelId, status) );
    return status;
}

// ******************************************************************               
// Scope: public (Implements CPipe::AbortTransfer = 0)
HCD_REQUEST_STATUS CIsochronousPipe::AbortTransfer( 
                                    IN const LPTRANSFER_NOTIFY_ROUTINE lpCancelAddress,
                                    IN const LPVOID lpvNotifyParameter,
                                    IN LPCVOID lpvCancelId )
//
// Purpose: Abort any transfer on this pipe if its cancel ID matches
//          that which is passed in.
//
// Parameters: lpCancelAddress - routine to callback after aborting transfer
//
//             lpvNotifyParameter - parameter for lpCancelAddress callback
//
//             lpvCancelId - identifier for transfer to abort
//
// Returns: requestOK if transfer aborted
//          requestFailed if lpvCancelId doesn't match currently executing
//                 transfer, or if there is no transfer in progress
//
// Notes:
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER, (TEXT("+CIsochronousPipe::AbortTransfer\n")));

    HCD_REQUEST_STATUS status = requestFailed;
    DEBUGMSG( ZONE_PIPE && !m_fTransferInProgress,
              (TEXT("CIsochronousPipe::AbortTransfer - no transfer is in progress\n")) );
    EnterCriticalSection( &m_csPipeLock );

    CITransfer *    pTransfer = m_pFirstTransfer;
    CITransfer *    pPrev = 0;
    while (pTransfer !=NULL && pTransfer->GetSTransfer()->lpvCancelId != lpvCancelId) {
        pPrev = pTransfer;
        pTransfer = pTransfer->GetNextTransfer();
    }
    if (pTransfer) { 
        ASSERT(pTransfer->GetSTransfer()->lpvCancelId==lpvCancelId);
        if (pTransfer ->IsStartQueue()){    
            // Stop Endpoint
            m_pED->bfSkip = 1;
            m_pCOhcd->CHW::WaitOneFrame();
            // Re-Initial Queue.
            QueueInit();
            // Cancel all the transfer that has been queued.
            CITransfer *    mpCur = m_pFirstTransfer;
            BOOL bIncludeCanceled = FALSE;
            while (mpCur !=NULL && mpCur->IsStartQueue()) {
                CITransfer * pNext = mpCur->GetNextTransfer();
                if (mpCur == pTransfer)
                    bIncludeCanceled = TRUE;
                mpCur->Canceled();
                delete mpCur;
                mpCur=pNext;                
            }
            ASSERT(  bIncludeCanceled == TRUE);
            m_pFirstTransfer = mpCur;
            if (m_pFirstTransfer == NULL) 
                m_pLastTransfer = NULL;            
            // Now we need to complete the transfer ourselves
            m_pED->paTdQueueHead = m_pED->paTdQueueTail = GetITDQueueHeadPhys();
            m_fTransferInProgress = FALSE;
            m_pED->bfSkip = 0;
            ScheduleTransfer() ;
        } else {
            // Transfer is not the current one so just nuke it from the list of pending transfers
            pTransfer->Canceled();
            if (pPrev!=NULL) {
                pPrev->SetNextTransfer(pTransfer->GetNextTransfer());
            }
            else { // this is header
                m_pFirstTransfer = pTransfer->GetNextTransfer();
            }
            if (pTransfer == m_pLastTransfer) { // Last One
                m_pLastTransfer = pPrev;
            }
            delete pTransfer;
            
        }
    }

    if ( lpCancelAddress ) {
        __try { // calling the Cancel function
            ( *lpCancelAddress )( lpvNotifyParameter );
        } __except( EXCEPTION_EXECUTE_HANDLER ) {
              DEBUGMSG( ZONE_ERROR, (TEXT("CIsochronousPipe::AbortTransfer - exception executing cancellation callback function\n")) );
        }
    }
    status = requestOK;

    LeaveCriticalSection( &m_csPipeLock );
    DEBUGMSG( ZONE_TRANSFER, (TEXT("-CIsochronousPipe::AbortTransfer status=%d\n"), status));
    return status;
}
ULONG * CIsochronousPipe::GetListHead( IN const BOOL fEnable ) 
{
    m_pCOhcd->CHW::ListControl(CHW::LIST_ISOCH, fEnable, FALSE);
    return &(m_pCOhcd->CHCCAera::m_interruptQHTree[0]->paNextEd);
};
void CIsochronousPipe::UpdateListControl(BOOL bEnable, BOOL bFill) 
{
    m_pCOhcd->CHW::ListControl(CHW::LIST_ISOCH, bEnable,  bFill);
}

// ******************************************************************               
// Scope: private (Implements CPipe::AreTransferParametersValid = 0)
BOOL CIsochronousPipe::AreTransferParametersValid( const STransfer *pTransfer ) const
//
// Purpose: Check whether this class' transfer parameters, stored in
//          m_transfer, are valid.
//
// Parameters: None (all parameters are vars of class)
//
// Returns: TRUE if parameters valid, else FALSE
//
// Notes: Assumes m_csPipeLock already held
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("+CIsochronousPipe::AreTransferParametersValid\n")) );

    if (pTransfer == NULL)
        return FALSE;

    // these parameters aren't used by CIsochronousPipe, so if they are non NULL,
    // it doesn't present a serious problem. But, they shouldn't have been
    // passed in as non-NULL by the calling driver.
    DEBUGCHK( pTransfer->lpvControlHeader == NULL ); // CONTROL
    // this is also not a serious problem, but shouldn't happen in normal
    // circumstances. It would indicate a logic error in the calling driver.
    DEBUGCHK( !(pTransfer->lpfnCallback == NULL && pTransfer->lpvCallbackParameter != NULL) );

    BOOL fValid = ( 
                    pTransfer->address > 0 &&
                    pTransfer->address <= USB_MAX_ADDRESS &&
                    pTransfer->lpvClientBuffer != NULL &&
                    // paClientBuffer could be 0 or !0
                    pTransfer->dwBufferSize > 0 &&
                    pTransfer->adwIsochErrors != NULL &&
                    pTransfer->adwIsochLengths != NULL &&
                    pTransfer->aLengths != NULL &&
                    pTransfer->dwFrames > 0 &&
                    pTransfer->dwFrames < gcTdIsochMaxFrames &&
                    pTransfer->lpfComplete != NULL &&
                    pTransfer->lpdwBytesTransferred != NULL &&
                    pTransfer->lpdwError != NULL );

    DEBUGMSG( ZONE_TRANSFER && ZONE_VERBOSE, (TEXT("-CIsochronousPipe::AreTransferParametersValid, returning BOOL %d\n"), fValid) );
    return fValid;
}

// ******************************************************************               
// Scope: private (over-rides CPipe::GetMemoryAllocationFlags)
DWORD CIsochronousPipe::GetMemoryAllocationFlags( void ) const
//
// Purpose: Get flags for allocating memory from the CPhysMem class.
//
// Parameters: None
//
// Returns: DWORD representing memory allocation flags
//
// Notes: 
// ******************************************************************
{
    return CPHYSMEM_FLAG_HIGHPRIORITY | CPHYSMEM_FLAG_NOBLOCK;
}

// ******************************************************************               
// Scope: private (Implements CPipe::ScheduleTransfer = 0)
HCD_REQUEST_STATUS CIsochronousPipe::ScheduleTransfer( void ) 
//
// Purpose: Schedule a USB Transfer on this pipe
//
// Parameters: None (all parameters are in m_transfer)
//
// Returns: requestOK if transfer issued ok, else requestFailed
//
// Notes: 
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER, (TEXT("+CIsochronousPipe::ScheduleTransfer\n")) );

    HCD_REQUEST_STATUS status = requestOK;

    EnterCriticalSection( &m_csPipeLock );
    
    CITransfer * pCur = m_pFirstTransfer ;
    while (!IsITDQueueFull() && pCur) {
        DEBUGCHK( m_pED->bfFunctionAddress == pCur->GetSTransfer()->address );
        if (pCur->IsCompleteQueue()) { // All data has been queue.
            pCur = pCur->GetNextTransfer();
        }
        else {
            DEBUGMSG( ZONE_TRANSFER, (TEXT("CIsochronousPipe::ScheduleTransfer(schd 0x%x) \n"),pCur));
            if (pCur->ScheduleITD()!=TRUE) {
                ASSERT(FALSE);
                break;
            }
        }
    }
    m_pED->paTdQueueTail = GetITDQueueTailPhysAddr();
    m_fTransferInProgress = (m_pFirstTransfer!=NULL);
    m_pCOhcd->CHW::ListControl(CHW::LIST_ISOCH, TRUE, TRUE);

    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_TRANSFER, (TEXT("-CIsochronousPipe::ScheduleTransfer, returning HCD_REQUEST_STATUS %d\n"), status) );
    return status;
}

// ******************************************************************               
// Scope: private (Implements CPipe::CheckForDoneTransfers = 0)
BOOL CIsochronousPipe::CheckForDoneTransfers( TDLINK pCurTD )
//
// Purpose: Check if the transfer on this pipe is finished, and 
//          take the appropriate actions - i.e. remove the transfer
//          data structures and call any notify routines
//
// Parameters: None
//
// Returns: TRUE if this pipe is no longer busy; FALSE if there are still
//          some pending transfers.
//
// Notes:
// ******************************************************************
{
    DEBUGMSG( ZONE_TRANSFER, (TEXT("+CIsochronousPipe::CheckForDoneTransfers\n")) );

    EnterCriticalSection( &m_csPipeLock );

    if (pCurTD.itd ==GetITDQueueHead()  && m_pFirstTransfer!=NULL) {
        ASSERT(pCurTD.itd->pTransfer == m_pFirstTransfer );

        CITransfer * pCurTrans = m_pFirstTransfer;
        if (pCurTrans->DoneITD()) {
            DEBUGMSG(ZONE_TRANSFER, (TEXT("CheckForDoneTransfers::CheckForDoneTransfers: completing xfr %08x\n"),pCurTrans));
            m_pFirstTransfer = pCurTrans->GetNextTransfer();
            if (m_pFirstTransfer==NULL)
                m_pLastTransfer = NULL;
            
            delete pCurTrans;
        }
    }
    else
        ASSERT(m_pFirstTransfer == NULL);
    ScheduleTransfer( ) ;
    LeaveCriticalSection( &m_csPipeLock );

    DEBUGMSG( ZONE_TRANSFER, (TEXT("-CIsochronousPipe::CheckForDoneTransfers, returning \n")) );
    return TRUE;
};
CPipeAbs * CreateBulkPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
               IN const BOOL fIsLowSpeed,
               IN const UCHAR bDeviceAddress,
               IN CHcd * const pChcd)
{ 
    return new CBulkPipe(lpEndpointDescriptor,fIsLowSpeed,bDeviceAddress,(COhcd * const)pChcd);
}
CPipeAbs * CreateControlPipe(IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
                  IN const BOOL fIsLowSpeed,
                  IN const UCHAR bDeviceAddress,
                  IN CHcd * const pChcd)
{ 
    return new CControlPipe(lpEndpointDescriptor,fIsLowSpeed,bDeviceAddress,(COhcd * const)pChcd);
}

CPipeAbs * CreateInterruptPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
               IN const BOOL fIsLowSpeed,
               IN const UCHAR bDeviceAddress,
               IN CHcd * const pChcd)
{ 
    return new CInterruptPipe(lpEndpointDescriptor,fIsLowSpeed,bDeviceAddress,(COhcd * const)pChcd);
}

CPipeAbs * CreateIsochronousPipe( IN const LPCUSB_ENDPOINT_DESCRIPTOR lpEndpointDescriptor,
               IN const BOOL fIsLowSpeed,
               IN const UCHAR bDeviceAddress,
               IN CHcd * const pChcd)
{ 
    return new CIsochronousPipe(lpEndpointDescriptor,fIsLowSpeed,bDeviceAddress,(COhcd * const)pChcd);
}


