//
// 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:
//     CHW.cpp
// Abstract:
//     This file implements the OHCI specific register routines
//
// Notes:
//
//

#include "chw.hpp"
#include "cpipe.hpp"
#include "cOhcd.hpp"
#include <nkintr.h>


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


CHW::CHW( IN const REGISTER portBase,
                      IN const DWORD dwSysIntr,
                      IN CPhysMem *pCPhysMem,
                      IN LPVOID pvOhcdPddObject)
:m_portBase((HcRegisters *)portBase)
,m_pMem(pCPhysMem)
,m_pPddContext(pvOhcdPddObject)
,m_dwSysIntr(dwSysIntr)
{
// definitions for static variables
    g_fPowerUpFlag = FALSE;
    g_fPowerResuming = FALSE;
    m_pHCCA = 0;
    m_wFrameHigh = 0;
    m_hUsbInterruptEvent = NULL;
    m_hUsbHubChangeEvent = NULL;
    m_hUsbInterruptThread = NULL;
    m_fUsbInterruptThreadClosing = FALSE;

    m_fFrameLengthIsBeingAdjusted = FALSE;
    m_fStopAdjustingFrameLength = FALSE;
    m_hAdjustDoneCallbackEvent = NULL;
    m_uNewFrameLength = 0;

    m_pControlHead = 0;
    m_pBulkHead = 0;
    m_pInterruptTable = 0;
    InitializeCriticalSection( &m_csFrameCounter );
    m_dwCapability = 0;
    m_bDoResume=FALSE;
}
CHW::~CHW()
{
    DeInitialize();
    DeleteCriticalSection( &m_csFrameCounter );
}
// ******************************************************************
BOOL CHW::Initialize(void)
//
// Purpose: Reset and Configure the Host Controller with the schedule.
//
// Parameters: portBase - base address for host controller registers
//
//             dwSysIntr - system interrupt number to use for USB
//                         interrupts from host controller
//
//             frameListPhysAddr - physical address of frame list index
//                                 maintained by CPipe class
//
//             pvOhcdPddObject - PDD specific structure used during suspend/resume
//
// Returns: TRUE if initialization succeeded, else FALSE
//
// Notes: This function is only called from the COhcd::Initialize routine.
//
//        This function is static
// ******************************************************************
{
    DEBUGMSG( ZONE_INIT, (TEXT("+CHW::Initialize base=0x%x, intr=0x%x\n"), m_portBase, m_dwSysIntr));
#ifdef DEBUG
    dwTickCountLastTime = GetTickCount();
#endif
    WORD lastFn = 0;

    DEBUGCHK( m_wFrameHigh == 0 );

    if ( m_portBase == 0 ) {
        DEBUGMSG( ZONE_ERROR, (TEXT("-CHW::Initialize - zero Register Base\n")));
        return FALSE;
    }
    // OHCI 5.1.1.3.3 // OS Driver, SMM Active.
    if ( m_portBase->HcControl.IR!=0 ) {
        DEBUGMSG(ZONE_INIT, (TEXT("CHW::Initialize - SMM Active, Disable it.\n")));
        m_portBase->HcCommandStatus.OCR = 1;
        DWORD dwCount = 1000; //Wait a second to succeed.
        while (m_portBase->HcControl.IR!=0 && dwCount--) {
            Sleep(1);
        }
        if (m_portBase->HcControl.IR) {
            RETAILMSG(1, (TEXT("CHW::Initialize - SMM Active, Can not Disable it.\n")));
            ASSERT(FALSE);
        }
    }
    // OHCI 5.1.1.3.4 // OS Driver, BIOS Active
    else  if (m_portBase->HcControl.HCFS!=HcRegisters::HcControl::HCFS_RESET) {
        DEBUGMSG(ZONE_INIT, (TEXT("CHW::Initialize - BIOS Active \n")));
        if (m_portBase->HcControl.HCFS!=HcRegisters::HcControl::HCFS_OPERATIONAL) {
            m_portBase->HcControl.HCFS=HcRegisters::HcControl::HCFS_RESUME;
            Sleep(20); // 20 ms delay
        }
    }
    // Clear the Emuration.
    if (m_portBase->HcRevision.LEGACY) { // Legacy Support.
        //Clear HceControl
        PDWORD pHceControl = ((PDWORD)m_portBase)+(0x100/sizeof(DWORD)) ;
        *pHceControl = 0 ;
    }

    // set up the Host Controller Communications Area
    if (m_pMem->AllocateSpecialMemory(sizeof(HCCA), (PUCHAR *) &m_pHCCA) == FALSE) {
        DEBUGMSG(ZONE_ERROR, (TEXT("-CHW::Initialize, cannot allocate HCCA!!\n")));
        return FALSE;
    }
    memset((PUCHAR)m_pHCCA, 0, sizeof(HCCA));

    // Reset the HC hardware
    DEBUGMSG(ZONE_INIT && ZONE_REGISTERS, (TEXT("CHW::Initialize - signalling H/W reset\n")));
    m_portBase->HcCommandStatus.HCR = 1;
    while (m_portBase->HcCommandStatus.HCR == 1)
        ; // wait for the reset operation to complete (max 10us)
    m_portBase->HcControl.HCFS = HcRegisters::HcControl::HCFS_RESET;

    m_portBase->HcHCCA = m_pMem->VaToPa((PUCHAR) m_pHCCA);  // locate the HCCA

    // These calculations are from the OHCI 1.0a spec, section 5.4
    // The 210 is the maximum amount of USB protocol overhead per frame, measured in bit-times.
    m_portBase->HcFmInterval.FSMPS = (m_portBase->HcFmInterval.FI - 210) * 6 / 7;
    m_portBase->HcPeriodicStart.PS = m_portBase->HcFmInterval.FI * 9 / 10;

    m_portBase->HcControl.CBSR = 3; // 4:1 Control/Bulk ratio
    DEBUGMSG(ZONE_INIT && ZONE_REGISTERS, (TEXT("CHW::Initialize - end H/W reset\n")));

    DEBUGMSG(ZONE_INIT && ZONE_REGISTERS && ZONE_VERBOSE, (TEXT("CHW::Initialize - initing schedule\n")));
    m_wFrameHigh = 0;           // reset the frame counter
    DEBUGMSG(ZONE_INIT && ZONE_REGISTERS && ZONE_VERBOSE, (TEXT("CHW::Initialize - \n") ));

    // Re-initialize the public constants now that we know the portBase
    // This'll be done properly - in a constructor - in the future.
    m_pControlHead = const_cast<PDWORD>(&m_portBase->HcControlHeadED);
    m_pBulkHead = const_cast<PDWORD>(&m_portBase->HcBulkHeadED);
    m_pInterruptTable = const_cast<PDWORD>(m_pHCCA->HccaInterruptTable);

    // m_hUsbInterrupt - Auto Reset, and Initial State = non-signaled
    DEBUGCHK( m_hUsbInterruptEvent == NULL );
    m_hUsbInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
    m_hUsbHubChangeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
    if ( m_hUsbInterruptEvent == NULL || m_hUsbHubChangeEvent==NULL ){
        DEBUGMSG(ZONE_ERROR, (TEXT("-CHW::Initialize. Error creating USBInterrupt or USBHubEvent event\n")));
        return FALSE;
    }

    InterruptDisable( m_dwSysIntr ); // Just to make sure this is really ours.
    // Initialize Interrupt. When interrupt id # m_sysIntr is triggered,
    // m_hUsbInterruptEvent will be signaled. Last 2 params must be NULL
    if ( !InterruptInitialize( m_dwSysIntr, m_hUsbInterruptEvent, NULL, NULL) ) {
        DEBUGMSG(ZONE_ERROR, (TEXT("-CHW::Initialize. Error on InterruptInitialize\r\n")));
        return FALSE;
    }

    // Apply power to all root hub ports, and wait for good to go. 7.4.1 & 7.4.3
    m_portBase->HcRhDescriptorA.NPS = 1;
    m_portBase->HcRhStatus.reg= HcRegisters::HcRhStatus::LPSC;
    Sleep(m_portBase->HcRhDescriptorA.POTPGT * 2);
    
    // Start up our IST - the parameter passed to the thread
    // is unused for now
    DEBUGCHK( m_hUsbInterruptThread == NULL &&
              m_fUsbInterruptThreadClosing == FALSE );
    m_hUsbInterruptThread = CreateThread( 0, 0, UsbInterruptThreadStub, this, 0, NULL );
    if ( m_hUsbInterruptThread == NULL ) {
        DEBUGMSG(ZONE_ERROR, (TEXT("-CHW::Initialize. Error creating IST\n")));
        return FALSE;
    }
    CeSetThreadPriority( m_hUsbInterruptThread, g_IstThreadPriority );

    // Enable interrupts
    m_portBase->HcInterruptEnable.WDH = 1; // writeback done head was written
    m_portBase->HcInterruptEnable.UE  = 1; // unrecoverable error
    m_portBase->HcInterruptEnable.FNO = 1; // frame number overflow
    m_portBase->HcInterruptEnable.MIE = 1; // master interrupt enable
    
    // Enable Periodic Endpt scan before enable controller. Some HCD require this.
    ListControl(LIST_INTERRUPT, TRUE, TRUE);
    DEBUGMSG( ZONE_INIT, (TEXT("-CHW::Initialize, success!\n")));
    return TRUE;
}

// ******************************************************************
void CHW::DeInitialize( void )
//
// Purpose: Delete any resources associated with static members
//
// Parameters: none
//
// Returns: nothing
//
// Notes: This function is only called from the ~COhcd() routine.
//
//        This function is static
// ******************************************************************
{
    m_fUsbInterruptThreadClosing = TRUE; // tell USBInterruptThread that we are closing
    // tell adjustment thread (if it exists) to close
    InterlockedExchange( &m_fStopAdjustingFrameLength, TRUE );

    if ( m_fFrameLengthIsBeingAdjusted ) {
        Sleep( 20 ); // give adjustment thread time to close
        DEBUGCHK( !m_fFrameLengthIsBeingAdjusted );
    }
    // m_hAdjustDoneCallbackEvent <- don't need to do anything to this
    // m_uNewFrameLength <- don't need to do anything to this

    // Wake up the interrupt thread and give it time to die.
    if ( m_hUsbInterruptEvent ) {
        SetEvent(m_hUsbInterruptEvent);
        if ( m_hUsbInterruptThread ) {
            DWORD dwWaitReturn = WaitForSingleObject(m_hUsbInterruptThread, 1000);
            if ( dwWaitReturn != WAIT_OBJECT_0 ) {
                DEBUGCHK( 0 );
            }
            CloseHandle(m_hUsbInterruptThread);
            m_hUsbInterruptThread = NULL;
        }
        // we have to close our interrupt before closing the event!
        InterruptDisable( m_dwSysIntr );

        CloseHandle(m_hUsbInterruptEvent);
        m_hUsbInterruptEvent = NULL;
    } else {
        InterruptDisable( m_dwSysIntr );
    }

    if ( m_hUsbHubChangeEvent) {
        SetEvent(m_hUsbHubChangeEvent);
        CloseHandle (m_hUsbHubChangeEvent);
        m_hUsbHubChangeEvent = NULL;
    }

    m_fUsbInterruptThreadClosing = FALSE;
    m_wFrameHigh = 0;
}

// ******************************************************************
void CHW::EnterOperationalState( void )
//
// Purpose: Signal the host controller to start processing the schedule
//
// Parameters: None
//
// Returns: Nothing.
//
// Notes: This function is only called from the COhcd::Initialize routine.
//        It assumes that CPipe::Initialize and CHW::Initialize
//        have already been called.
//
//        This function is static
// ******************************************************************
{
    DEBUGMSG( ZONE_INIT, (TEXT("+CHW::EnterOperationalState\n")));

    DEBUGMSG(ZONE_INIT && ZONE_REGISTERS && ZONE_VERBOSE, (TEXT("CHW::EnterOperationalState - setting USBCMD run bit\n")));
    m_portBase->HcControl.HCFS = HcRegisters::HcControl::HCFS_OPERATIONAL;

    DEBUGMSG( ZONE_INIT, (TEXT("-CHW::EnterOperationalState\n")));
}

// ******************************************************************
void CHW::StopHostController( void )
//
// Purpose: Signal the host controller to stop processing the schedule
//
// Parameters: None
//
// Returns: Nothing.
//
// Notes: This function can be called from the power handler callbacks and must
//        therefore abide by the restrictions. No system calls, no blocking.
//        Hence no DEBUGMSG's either.
//        This function is static
// ******************************************************************
{
    //DEBUGMSG( ZONE_INIT, (TEXT("+CHW::StopHostController\n")));
    if ( m_portBase != 0 ) {
        m_portBase->HcInterruptDisable.MIE = 1; // disable all interrupts
        m_portBase->HcControl.HCFS = HcRegisters::HcControl::HCFS_RESET;
    }
    //DEBUGMSG( ZONE_INIT, (TEXT("-CHW::StopHostController\n")));
}

// ******************************************************************
void CHW::ListControl( IN const DWORD bfList, IN const BOOL fEnable, IN const BOOL fFill )
//
// Purpose: Instruct the host controller to modify its list management policies.
//
// Parameters: a bitfield indicating which lists to affect;
//             whether to enable or disable processing of the affected lists;    
//         and whether to signal the HC that new transfers are on the affected lists.
//
// Returns: Nothing.
//
// Notes: It's kind of pointless to set bFill when a list is disabled.
//        Also, fFill is ignored for periodic (intr, isoch) lists.
//        Enabling a list may also reset its currentED pointer
//        because CPipe only knows about the HeadED pointers;
//        it does so only when the list starts out disabled in order
//        to avoid tickling a case whereby the HC becomes confused about
//        what the current ED is during TD retirement.
//
//        This function is static
// ******************************************************************
{
    DEBUGMSG( ZONE_INIT && ZONE_VERBOSE, (TEXT("+CHW::ListControl %x ena:%d fill:%d\n"), bfList, fEnable, fFill));

    if (bfList & LIST_CONTROL) {
        if (fEnable && !m_portBase->HcControl.CLE)
            m_portBase->HcControlCurrentED = 0;
        m_portBase->HcControl.CLE=fEnable;
        if (fFill) m_portBase->HcCommandStatus.CLF=1;
    }
    if (bfList & LIST_BULK) {
        if (fEnable && !m_portBase->HcControl.BLE)
            m_portBase->HcBulkCurrentED = 0;
        m_portBase->HcControl.BLE=fEnable;
        if (fFill) m_portBase->HcCommandStatus.BLF=1;
    }
    if (bfList & LIST_INTERRUPT) {
        m_portBase->HcControl.PLE=fEnable;
    }
    if (bfList & LIST_ISOCH) {
        if (fEnable)
            m_portBase->HcControl.PLE=fEnable;
        m_portBase->HcControl.IE=fEnable;
    }
}
DWORD CALLBACK CHW::CeResumeThreadStub( IN PVOID context )
{
    return ((CHW *)context)->CeResumeThread();
}

// ******************************************************************
DWORD CHW::CeResumeThread ()
//
// Purpose: Force the HCD to reset and regenerate itself after power loss.
//
// Parameters: None
//
// Returns: Nothing.
//
// Notes: Because the PDD is probably maintaining pointers to the Hcd and Memory
//   objects, we cannot free/delete them and then reallocate. Instead, we destruct
//   them explicitly and use the placement form of the new operator to reconstruct
//   them in situ. The two flags synchronize access to the objects so that they
//   cannot be accessed before being reconstructed while also guaranteeing that
//   we don't miss power-on events that occur during the reconstruction.
//
//        This function is static
// ******************************************************************
{
    // reconstruct the objects at the same addresses where they were before;
    // this allows us not to have to alert the PDD that the addresses have changed.

    DEBUGCHK( g_fPowerResuming == FALSE );

    // order is important! resuming indicates that the hcd object is temporarily invalid
    // while powerup simply signals that a powerup event has occurred. once the powerup
    // flag is cleared, we will repeat this whole sequence should it get resignalled.
    g_fPowerUpFlag = FALSE;
    g_fPowerResuming = TRUE;

    const PUCHAR pBufVirt = m_pMem->m_pVirtBase, pBufPhys = m_pMem->m_pPhysBase;
    DWORD cb0 = m_pMem->m_cbTotal, cb1 = m_pMem->m_cbHighPri;

    DeviceDeInitialize();
    while (1) {  // breaks out upon successful reinit of the object

        m_pMem->ReInit();

        if (DeviceInitialize())
            break;

        // getting here means we couldn't reinit the HCD object!
        DEBUGMSG(ZONE_ERROR, (TEXT("USB cannot reinit the HCD at CE resume; retrying...\n")));
        DeviceDeInitialize();
        Sleep(15000);
    }

    // the hcd object is valid again. if a power event occurred between the two flag
    // assignments above then the IST will reinitiate this sequence.
    g_fPowerResuming = FALSE;
    if (g_fPowerUpFlag)
        PowerMgmtCallback(TRUE);
    
    return 0;
}
// ******************************************************************
VOID CHW::PowerMgmtCallback( IN BOOL fOff )
//
// Purpose: System power handler - called when device goes into/out of
//          suspend.
//
// Parameters:  fOff - if TRUE indicates that we're entering suspend,
//                     else signifies resume
//
// Returns: Nothing
//
// Notes: This needs to be implemented for HCDI
// ******************************************************************
{
    if ( fOff )
    {
        if ((GetCapability() & HCD_SUSPEND_RESUME)!= 0  ) {
            m_bDoResume=TRUE; 
            SuspendHostController();
        }
        else {
            m_bDoResume=FALSE;            
            StopHostController();
        }
    }
    else
    {   // resuming...
        g_fPowerUpFlag = TRUE;
        if (m_bDoResume)
            ResumeHostController();
        if (!g_fPowerResuming)
            // can't use member data while `this' is invalid
            SetInterruptEvent(m_dwSysIntr);
    }

    return;
}
BOOL    CHW::SuspendHC()
{
    BOOL fReturn = FALSE;
    if (m_portBase != 0 && 
            (GetCapability() & (HCD_SUSPEND_ON_REQUEST | HCD_SUSPEND_RESUME)) == (HCD_SUSPEND_ON_REQUEST | HCD_SUSPEND_RESUME) ) {
        if (m_portBase->HcControl.HCFS == HcRegisters::HcControl::HCFS_OPERATIONAL) { 
            HcRegisters::OHCI_Ints maskRegValue;
            maskRegValue.reg=0;
            maskRegValue.RD=1;
            m_portBase->HcInterruptEnable.reg=maskRegValue.reg;                
            SuspendHostController();
            fReturn = TRUE;
        }
    }
    return fReturn;
}

VOID CHW::SuspendHostController()
{
    if ( m_portBase != 0 ) {
        m_portBase->HcControl.HCFS = HcRegisters::HcControl::HCFS_SUSPEND;
    }
}
VOID CHW::ResumeHostController()
{
    if ( m_portBase != 0 ) {
        m_portBase->HcControl.HCFS = HcRegisters::HcControl::HCFS_RESUME;
    }
    ResumeNotification();

}

DWORD CHW::SetCapability(DWORD dwCap)
{
    m_dwCapability |= dwCap; 
    HcRegisters::OHCI_Ints maskRegValue; 
    BOOL fRemoteWakeup = FALSE;
    if ( (m_dwCapability & HCD_SUSPEND_RESUME)!=0) {
        if (m_portBase) {
            m_portBase->HcControl.RWC=1;// OHCI 7.2.1
            m_portBase->HcControl.RWE=1;
            if (m_portBase->HcControl.RWC !=0 && m_portBase->HcControl.RWE!=0) {
                maskRegValue.reg=0;
                maskRegValue.RD=1;
                m_portBase->HcInterruptEnable.reg=maskRegValue.reg;
                m_portBase->HcRhStatus.reg= HcRegisters::HcRhStatus::DRWE;
                KernelIoControl(IOCTL_HAL_ENABLE_WAKE, &m_dwSysIntr, sizeof(m_dwSysIntr), NULL, 0, NULL);
                fRemoteWakeup = TRUE;
            }
        }
        if (!fRemoteWakeup) {
            DEBUGMSG(ZONE_WARNING, (TEXT("CHW::SetCapability. RemoteWake is not support by this HC.\n")));
            m_dwCapability  &= ~ HCD_SUSPEND_RESUME;
        }
    }
    if ( (m_dwCapability & HCD_ROOT_HUB_INTERRUPT)!=0) {
        maskRegValue.reg=0;
        m_portBase->HcInterruptEnable.RHSC= 1; // root hub status change
        m_portBase->HcInterruptEnable.reg=maskRegValue.reg;
    };
    return m_dwCapability;
};

DWORD CHW::UsbInterruptThreadStub( IN PVOID context )
{
    return ((CHW *)context)->UsbInterruptThread();
}

// ******************************************************************
DWORD CHW::UsbInterruptThread( )
//
// Purpose: Main IST to handle interrupts from the USB host controller
//
// Parameters: context - parameter passed in when starting thread,
//                       (currently unused)
//
// Returns: 0 on thread exit.
//
// Notes:
//
//        This function is private
// ******************************************************************
{
    DEBUGMSG(ZONE_INIT && ZONE_VERBOSE, (TEXT("+CHW::Entered USBInterruptThread\n")));

    while ( !m_fUsbInterruptThreadClosing ) {
        WaitForSingleObject(m_hUsbInterruptEvent, INFINITE);
        if ( m_fUsbInterruptThreadClosing ) {
            break;
        }

        // the cast is used here to remove the volatile qualifier so we can cache the register
        union HcRegisters::HcInterruptStatus usbsts ;
        usbsts.reg = (m_portBase)->HcInterruptStatus.reg;
#ifdef DEBUG
        DWORD dwFrame;
        GetFrameNumber(&dwFrame); // calls UpdateFrameCounter
        DEBUGMSG( ZONE_REGISTERS, (TEXT("!!!interrupt!!!! on frame index + 1 = 0x%08x, USBSTS = 0x%08x\n"), dwFrame, usbsts.reg ) );
#else
        UpdateFrameCounter();
#endif // DEBUG

        // This flag gets cleared in the resume thread.
        if (g_fPowerUpFlag && !g_fPowerResuming)
        {
            if (m_bDoResume) {
                g_fPowerUpFlag=FALSE;
                Sleep(20);
                EnterOperationalState();
            }
            else {
                HcdPdd_InitiatePowerUp((DWORD)m_pPddContext);
                HANDLE ht;
                while ((ht = CreateThread(NULL, 0, CeResumeThreadStub, this, 0, NULL)) == NULL) {
                    RETAILMSG(1, (TEXT("HCD IST: cannot spin a new thread to handle CE resume of USB host controller; sleeping.\n")));
                    Sleep(15000);  // 15 seconds later, maybe it'll work.
                }
                CeSetThreadPriority( ht, g_IstThreadPriority );
                CloseHandle(ht);
                
                // The CE resume thread will force this IST to exit so we'll be cooperative proactively.
                break;
            }
        }
        if (usbsts.RD && m_portBase->HcControl.HCFS == HcRegisters::HcControl::HCFS_RESUME ) { // Resume Interrupt.
            Sleep(20);
            EnterOperationalState();
            ResumeNotification();
        }
        if (usbsts.RHSC) {
            // disable RHSC interrupt.
            HcRegisters::OHCI_Ints maskRegValue;
            maskRegValue.reg=0;// root hub status change
            maskRegValue.RHSC=1;
            m_portBase->HcInterruptDisable.reg=maskRegValue.reg;                
            SetEvent(m_hUsbHubChangeEvent);
        }
        if (usbsts.WDH) {
            DWORD paDoneHead = m_pHCCA->HccaDoneHead & ~1; // ignore LSb (see OHCI figure 4-5)
            m_pHCCA->HccaDoneHead = 0;
            DEBUGCHK( (paDoneHead & 0xF) == 0 );
            DEBUGCHK( paDoneHead != 0 ); // Check Hardware Register Value agains OHCI spec. 7.1.4
            if (paDoneHead) {
                SignalCheckForDoneTransfers( paDoneHead );
            }
        }
        // Acknowledge the interrupt(s)
        m_portBase->HcInterruptStatus.reg = usbsts.reg;
        InterruptDone(m_dwSysIntr);
    }

    DEBUGMSG(ZONE_INIT && ZONE_VERBOSE, (TEXT("-CHW::Leaving USBInterruptThread\n")));

    return (0);
}

// ******************************************************************
void CHW::UpdateFrameCounter( void )
//
// Purpose: Updates our internal frame counter
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: The OHCI frame number register is 16 bits long.
//        Thus, the counter will wrap approx. every 64 seconds.
//        We maintain an additional 16 bits internally so we
//        needn't wrap for 50 days.
//
//        This function should be called at least once a minute;
//        otherwise we could miss frames.
//
// ******************************************************************
{

    EnterCriticalSection( &m_csFrameCounter );

#ifdef DEBUG
    // If this fails, we haven't been called in a long time,
    // so the frame number is no longer accurate
    if (GetTickCount() - dwTickCountLastTime >= 64000 )
        DEBUGMSG(1, (TEXT("!OHCI - CHW::UpdateFrameCounter missed frame count;")
                     TEXT(" isoch packets may have been dropped.\n")));
    dwTickCountLastTime = GetTickCount();
#endif // DEBUG

    WORD fn = m_pHCCA->HccaFrameNumber;
    if (fn < lastFn)
        ++m_wFrameHigh;
    lastFn = fn;

    LeaveCriticalSection( &m_csFrameCounter );
}

// ******************************************************************
BOOL CHW::GetFrameNumber(OUT LPDWORD lpdwFrameNumber )
//
// Purpose: Return the current frame number
//
// Parameters: None
//
// Returns: 32 bit current frame number
//
// Notes: See also comment in UpdateFrameCounter
// ******************************************************************
{
    UpdateFrameCounter();
    *lpdwFrameNumber = ((DWORD) m_wFrameHigh << 16) | m_pHCCA->HccaFrameNumber ;
    return TRUE;
}

// ******************************************************************
BOOL CHW::WaitOneFrame( void )
//
// Purpose: Block the current thread until the HC hardware is
//          no longer processing the current USB frame.
//
// Parameters: None
//
// Returns: TRUE on success, FALSE if the HW is unavailable or not running.
//
// Notes:
// ******************************************************************
{
    // is the host controller operational?
    if ( m_portBase != 0 ) {
        if(m_portBase->HcControl.HCFS != HcRegisters::HcControl::HCFS_OPERATIONAL) {
            // no, just wait a short while and return
            Sleep(10);
            return TRUE;
        }
    }

    // The OS' system clock - prior to CE3.0 - has 25ms granularity
    // which is just too high. Later versions have a 1ms clock.
    //
    // Sleep may not be enough if the frame has been made longer
    // so we poll after the 1ms. If the frame has been made shorter
    // then this will just take too long.
    m_pHCCA->HccaPad1 = 1; // HC will write 0 here when updating FN (OHCI Figure 4-5)
    Sleep(1);
    
    WORD wFN = m_pHCCA->HccaFrameNumber;
    while (m_pHCCA->HccaPad1) {   
        // in case HccaPad1 doesn't work. Here is second defense.
        if( m_pHCCA->HccaFrameNumber != wFN )
            break;
    }

    return TRUE;
}

// ******************************************************************
BOOL CHW::GetFrameLength(OUT LPUSHORT lpuFrameLength)
//
// Purpose: Return the current frame length in 12 MHz clocks
//          (i.e. 12000 = 1ms)
//
// Parameters: None
//
// Returns: frame length
//
// Notes:
// ******************************************************************
{
    *lpuFrameLength=m_portBase->HcFmInterval.FI;
    return TRUE;
}

// ******************************************************************
BOOL CHW::SetFrameLength( IN HANDLE /*hEvent*/,
                          IN const USHORT /*uFrameLength*/ )
//
// Purpose: Set the Frame Length in 12 Mhz clocks. i.e. 12000 = 1ms
//
// Parameters:  hEvent - event to set when frame has reached required
//                       length
//
//              uFrameLength - new frame length
//
// Returns: TRUE if frame length changed, else FALSE
//
// Notes:
// ******************************************************************
{
    BOOL fSuccess = FALSE;

    // to prevent multiple threads from simultaneously adjusting the
    // frame length, InterlockedTestExchange is used. This is
    // cheaper than using a critical section.
    return fSuccess;
}

// ******************************************************************
BOOL CHW::StopAdjustingFrame( void )
//
// Purpose: Stop modifying the host controller frame length
//
// Parameters: None
//
// Returns: TRUE
//
// Notes:
// ******************************************************************
{
    InterlockedExchange( &m_fStopAdjustingFrameLength, TRUE );
    return TRUE;
}
DWORD CHW::UsbAdjustFrameLengthThreadStub(PVOID context)
{
    return ((CHW *)context)->UsbAdjustFrameLengthThread( );
}

// ******************************************************************
DWORD CHW::UsbAdjustFrameLengthThread( )
//
// Purpose: Worker thread to handle frame length adjustment
//
// Parameters: context - parameter passed in when starting thread,
//                       (currently unused)
//
// Returns: 0 on thread exit.
//
// Notes:
//
//        This function is private
// ******************************************************************
{
    DEBUGMSG(ZONE_REGISTERS && ZONE_VERBOSE, (TEXT("+CHW::Entered UsbAdjustFrameLengthThread\n")));


    return (0);
}

// ******************************************************************
BOOL CHW::DidPortStatusChange( IN const UCHAR port )
//
// Purpose: Determine whether the status of root hub port # "port" changed
//
// Parameters: port - 0 for the hub itself, otherwise the hub port number
//
// Returns: TRUE if status changed, else FALSE
//
// Notes:
// ******************************************************************
{
    USB_HUB_AND_PORT_STATUS s;
    CHW::GetPortStatus(port, s);
    return s.change.word ? TRUE : FALSE;
}

// ******************************************************************
BOOL CHW::GetPortStatus( IN const UCHAR port,
                         OUT USB_HUB_AND_PORT_STATUS& rStatus )
//
// Purpose: This function will return the current root hub port
//          status in a non-hardware specific format
//
// Parameters: port - 0 for the hub itself, otherwise the hub port number
//
//             rStatus - reference to USB_HUB_AND_PORT_STATUS to get the
//                       status
//
// Returns: TRUE
//
// Notes:
// ******************************************************************
{
    memset( &rStatus, 0, sizeof( USB_HUB_AND_PORT_STATUS ) );
    if ( port > 0 &&  port <= GetNumberOfDownStreamPort() ) {
        
        // read the port status register (compiler complains when we do it the easy way)
        union HcRegisters::HcRhPortStatus sr;
        sr.reg = m_portBase->HcRhPortStatus[port-1].reg;

        // Now fill in the USB_HUB_AND_PORT_STATUS structure
        rStatus.change.port.ConnectStatusChange = sr.CSC;
        rStatus.change.port.PortEnableChange = sr.PESC;
        rStatus.change.port.OverCurrentChange = sr.OCIC;
        rStatus.change.port.SuspendChange = sr.PSSC;
        rStatus.change.port.ResetChange = sr.PRSC;
        rStatus.status.port.DeviceIsLowSpeed = sr.LSDA;
        rStatus.status.port.PortConnected = sr.CCS;
        rStatus.status.port.PortEnabled = sr.PES;
        rStatus.status.port.PortOverCurrent = sr.POCI;
        // we assume root hub ports are always powered, but believe the HW.
        rStatus.status.port.PortPower = sr.PPS;
        rStatus.status.port.PortReset = sr.PRS;
        rStatus.status.port.PortSuspended = sr.PSS;
    }
#ifdef DEBUG // these are available in OHCI but this driver doesn't use them
    else {
        // request refers to a root hub port
        DEBUGCHK ( port <= GetNumberOfDownStreamPort() );
        // request is to Hub. rStatus was already memset to 0 above.
        DEBUGCHK( port == 0 );
        // local power supply good
        DEBUGCHK( rStatus.status.hub.LocalPowerStatus == 0 );
        // no over current condition
        DEBUGCHK( rStatus.status.hub.OverCurrentIndicator == 0 );
        // no change in power supply status
        DEBUGCHK( rStatus.change.hub.LocalPowerChange == 0 );
        // no change in over current status
        DEBUGCHK( rStatus.change.hub.OverCurrentIndicatorChange == 0 );
    }
#endif // DEBUG

    return TRUE;
}

// ******************************************************************
void CHW::GetRootHubDescriptor( OUT USB_HUB_DESCRIPTOR &descriptor )
//
// Purpose: Calculate and return a hub descriptor for the root hub so
//          that the upper-level code can pretend it's an external hub.
//
// Parameters: a reference to a descriptor to fill in.
//
// Returns: nothing
//
// Notes: assumes the hardware is sufficiently initialized.
// ******************************************************************
{
    PREFAST_DEBUGCHK ( m_portBase != 0 );
    
    descriptor.bNumberOfPorts = GetNumberOfDownStreamPort();
    DEBUGCHK( descriptor.bNumberOfPorts >= 1 && descriptor.bNumberOfPorts <= 15 );

    // root hub is not part of a compound device (per OHCI 7.4.1)
    DEBUGCHK( m_portBase->HcRhDescriptorA.DT == 0 );
    descriptor.wHubCharacteristics =
        (m_portBase->HcRhDescriptorA.NPS
         ? USB_HUB_CHARACTERISTIC_NO_POWER_SWITCHING
         : m_portBase->HcRhDescriptorA.PSM
         ? USB_HUB_CHARACTERISTIC_INDIVIDUAL_POWER_SWITCHING
         : USB_HUB_CHARACTERISTIC_GANGED_POWER_SWITCHING) |
        USB_HUB_CHARACTERISTIC_NOT_PART_OF_COMPOUND_DEVICE |
        (m_portBase->HcRhDescriptorA.NOCP
         ? USB_HUB_CHARACTERISTIC_NO_OVER_CURRENT_PROTECTION
         : m_portBase->HcRhDescriptorA.OCPM
         ? USB_HUB_CHARACTERISTIC_INDIVIDUAL_OVER_CURRENT_PROTECTION
         : USB_HUB_CHARACTERISTIC_GLOBAL_OVER_CURRENT_PROTECTION);
    
    descriptor.bPowerOnToPowerGood = m_portBase->HcRhDescriptorA.POTPGT;
    descriptor.bHubControlCurrent = 0;

    int bmsize = 1 + descriptor.bNumberOfPorts / 8; // bytes to represent nports+1 bits
    for (int i=0; i<bmsize; ++i) {
        descriptor.bRemoveAndPowerMask[i] = (UCHAR) (m_portBase->HcRhDescriptorB.DR >> i) & 0xFF;
        descriptor.bRemoveAndPowerMask[i+bmsize] = 0xFF; // must be 0xFF, USB spec 1.1, table 11-8
    }

    // min size assumes seven or fewer ports so we must account for more.
    descriptor.bDescriptorLength = USB_HUB_DESCRIPTOR_MINIMUM_SIZE + (bmsize-1)*2;
    descriptor.bDescriptorType = USB_HUB_DESCRIPTOR_TYPE;
}

// ******************************************************************
BOOL CHW::RootHubFeature( IN const UCHAR port,
                          IN const UCHAR setOrClearFeature,
                          IN const USHORT feature )
//
// Purpose: This function clears all the status change bits associated with
//          the specified root hub port.
//
// Parameters: port - 0 for the hub itself, otherwise the hub port number
//
// Returns: TRUE iff the requested operation is valid, FALSE otherwise.
//
// Notes: Assume that caller has already verified the parameters from a USB
//        perspective. The HC hardware may only support a subset of that
//        (which is indeed the case for OHCI).
// ******************************************************************
{
    
    if ( !(port >= 1 && port <= GetNumberOfDownStreamPort()) ) {
        // request is to Hub
        ASSERT(FALSE);
        return FALSE;
    }

    DWORD dwPortStatus = 0;
    if (setOrClearFeature == USB_REQUEST_SET_FEATURE)
        switch (feature) {
          case USB_HUB_FEATURE_PORT_RESET:              dwPortStatus = HcRegisters::HcRhPortStatus::SPR; break;
          case USB_HUB_FEATURE_PORT_SUSPEND:            dwPortStatus = HcRegisters::HcRhPortStatus::SPS; break;
          case USB_HUB_FEATURE_PORT_POWER:              dwPortStatus = HcRegisters::HcRhPortStatus::SPP; break;
          case USB_HUB_FEATURE_PORT_ENABLE:             dwPortStatus = HcRegisters::HcRhPortStatus::SPE; break;
          default: return FALSE;
        }
    else
        switch (feature) {
          case USB_HUB_FEATURE_PORT_ENABLE:             dwPortStatus = HcRegisters::HcRhPortStatus::CPE; break;
          case USB_HUB_FEATURE_PORT_SUSPEND:            dwPortStatus = HcRegisters::HcRhPortStatus::CPS; break;
          case USB_HUB_FEATURE_C_PORT_CONNECTION:       dwPortStatus = HcRegisters::HcRhPortStatus::CCSC; break;
          case USB_HUB_FEATURE_C_PORT_ENABLE:           dwPortStatus = HcRegisters::HcRhPortStatus::CPESC; break;
          case USB_HUB_FEATURE_C_PORT_RESET:            dwPortStatus = HcRegisters::HcRhPortStatus::CPRSC; break;
          case USB_HUB_FEATURE_C_PORT_SUSPEND:          dwPortStatus = HcRegisters::HcRhPortStatus::CPSSC; break;
          case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:     dwPortStatus = HcRegisters::HcRhPortStatus::COCIC; break;
          case USB_HUB_FEATURE_PORT_POWER:              dwPortStatus = HcRegisters::HcRhPortStatus::CPP; break;
          default: return FALSE;
        }

    m_portBase->HcRhPortStatus[port-1].reg = dwPortStatus;

    return TRUE;
}

// ******************************************************************
BOOL CHW::ResetAndEnablePort( IN const UCHAR port )
//
// Purpose: reset/enable device on the given port so that when this
//          function completes, the device is listening on address 0
//
// Parameters: port - root hub port # to reset/enable
//
// Returns: TRUE if port reset and enabled, else FALSE
//
// Notes: This function takes approx 60 ms to complete, and assumes
//        that the caller is handling any critical section issues
//        so that two different ports (i.e. root hub or otherwise)
//        are not reset at the same time.
// ******************************************************************
{
    BOOL fSuccess = TRUE;

    if (!( port >= 1 && port <= GetNumberOfDownStreamPort()) ) {
        ASSERT(FALSE);
        return FALSE;
    }

    // turn on reset bit
    RootHubFeature(port, USB_REQUEST_SET_FEATURE, USB_HUB_FEATURE_PORT_RESET);
    
    // USB 1.1 spec section 7.1.7.3 recommends 50ms for root hub ports
    // but OHCI hardware clears reset after only 10ms.
    Sleep( 50 );
    while (m_portBase->HcRhPortStatus[port-1].PRS) Sleep( 1 );

    // Verify that the port has finished resetting and enabling itself
    DEBUGCHK ( m_portBase->HcRhPortStatus[port-1].PRS == 0 );
    RootHubFeature(port, USB_REQUEST_CLEAR_FEATURE, USB_HUB_FEATURE_C_PORT_RESET);
    RootHubFeature(port, USB_REQUEST_SET_FEATURE, USB_HUB_FEATURE_PORT_ENABLE);
    DEBUGMSG( ZONE_REGISTERS && ( m_portBase->HcRhPortStatus[port-1].PES != 1 ),
        (TEXT("PES is not set OHCI 7.4.4. Device may be disconnected, bus error or overcurrent condition is detected\n  ")));

    // USB 1.1 spec, 7.1.7.3 - device may take up to 10 ms
    // to recover after reset is removed
    Sleep( 15 );

    DEBUGMSG( ZONE_REGISTERS, (TEXT("Root hub, after reset & enable, port %d \n"), port) );
    return fSuccess;
}

// ******************************************************************
void CHW::DisablePort( IN const UCHAR port )
//
// Purpose: disable the given root hub port
//
// Parameters: port - port # to disable
//
// Returns: nothing
//
// Notes: This function will take about 10ms to complete
// ******************************************************************
{
    RootHubFeature(port, USB_REQUEST_CLEAR_FEATURE, USB_HUB_FEATURE_PORT_ENABLE);
}
BOOL CHW::WaitForPortStatusChange (HANDLE m_hHubChanged)
{
    if (m_hUsbHubChangeEvent && (m_dwCapability & HCD_ROOT_HUB_INTERRUPT)!=0) {
        HcRegisters::OHCI_Ints maskRegValue;
        maskRegValue.reg=0;// root hub status change
        maskRegValue.RHSC=1;
        m_portBase->HcInterruptEnable.reg=maskRegValue.reg;                
        if (m_hHubChanged!=NULL) {
            HANDLE hArray[2];
            hArray[0]=m_hHubChanged;
            hArray[1]=m_hUsbHubChangeEvent;
            WaitForMultipleObjects(2,hArray,FALSE,INFINITE);
        }
        else
            WaitForSingleObject(m_hUsbHubChangeEvent,INFINITE);
        return TRUE;
    }
    return FALSE;
}
#ifdef DEBUG
// ******************************************************************
void CHW::DumpAllRegisters( void )
//
// Purpose: Queries Host Controller for all registers, and prints
//          them to DEBUG output. Register definitions are in OHCI spec 2.1
//
// Parameters: None
//
// Returns: Nothing
//
// Notes: used in DEBUG mode only
//
//        This function is static
// ******************************************************************
{
    DEBUGMSG(ZONE_REGISTERS, (TEXT("CHW - DUMP REGISTERS BEGIN\n")));
    for ( USHORT port = 1; port <= HCD_NUM_ROOT_HUB_PORTS; port++ ) {
        ;
    }
    DEBUGMSG(ZONE_REGISTERS, (TEXT("CHW - DUMP REGISTERS DONE\n")));
}
#endif
