/*++

Copyright (c) 2000 Microsoft Corporation


Module Name:

    RootHub.c

Abstract:
    
    This file implements the OpenHCI root hub support.  Rather than following the model
    of writing an emulation of a real hub that supports transfers, this module instead
    acts as a driver for the root-hub.  It detects and reports devices just as a hub driver
    would.  Since hub drivers do not in the XBOX world expose external interfaces, it
    is much simpler this way.

    
Environment:

    Designed for XBOX.

Notes:

Revision History:

    02-10-00 created by Mitchell Dernis (mitchd)

--*/
//
//  Pull in OS headers
//
#include <ntos.h>

//
//  Setup the debug information for this file (see ..\inc\debug.h)
//
#define MODULE_POOL_TAG          'DCHO'
#include <debug.h>
DEFINE_USB_DEBUG_FUNCTIONS("OHCD");


__inline UCHAR RootHubPortToPortNumber(UCHAR RootHubPort)
/*++
    Routine Description:
        The root hub internally uses ports 0 to 3.  However, this
        translates to ports 1 to 4 with a normal hub. 
--*/
{
    return RootHubPort+1;
}

__inline ULONG PortNumberToRootHubPort(ULONG PortNumber)
{
    return PortNumber-1;
}

//
//  Pull in usb headers
//
#include <hcdi.h>       //Interface between USBD and HCD
#include "ohcd.h"       //Private OHCD stuff


VOID
OHCD_RootHubResetDpc(
    PKDPC Dpc,
    PVOID DeviceExtensionPtr,
    PVOID Bogus1,
    PVOID Bogus2
    );

#pragma code_seg(".XPPCINIT")
VOID
FASTCALL
OHCD_RootHubInitialize(
    POHCD_DEVICE_EXTENSION      DeviceExtension
)
/*++

Routine Description:

    Reads basic info regarding the root-hub, and turns
    on the interrupts for the root-hub.

    1) Determine how many ports we have.
    2) Process initial connections.
    3) Turn on interrupts for hot-plug detection later

Arguments:

Return Value:
    
--*/               
{
    POHCI_OPERATIONAL_REGISTERS operationalRegisters = DeviceExtension->OperationalRegisters;
    POHCD_ROOT_HUB_OBJECT       rhObject = &DeviceExtension->RootHubObject;
#ifndef OHCD_XBOX_HARDWARE_ONLY
    HC_RH_DESCRIPTOR_A          rhDescriptorA;
    HC_RH_DESCRIPTOR_B          rhDescriptorB;
#endif //OHCD_XBOX_HARDWARE_ONLY    
    HC_RH_STATUS                rhStatus;
    HC_RH_PORT_STATUS           rhPortStatus;
    ULONG                       index;
    ULONG                       mask;
    OHCD_PORT_INFO              portInfo;
    KIRQL                       oldIrql;
    
    USB_DBG_ENTRY_PRINT(("Entering OHCD_RootHubInitialize"));
    
    
    //
    //  Initialize the watchdog timer and related DPC,
    //  for catching failed resets.
    //
    KeInitializeTimer(&rhObject->ResetTimeoutTimer);
    KeInitializeDpc(
        &rhObject->ResetTimeoutDPC,
        OHCD_RootHubResetDpc,
        (PVOID)DeviceExtension
        );


#ifdef OHCD_XBOX_HARDWARE_ONLY
    #if (USB_HOST_CONTROLLER_CONFIGURATION==USB_SINGLE_HOST_CONTROLLER)
    rhObject->NumberOfPorts = 4;
    #else
    rhObject->NumberOfPorts = 2;
    #endif
    USB_DBG_TRACE_PRINT(("  NumberDownstreamPorts     : %d", (ULONG)rhObject->NumberOfPorts));
#else
    //
    //  Get Information on root-hub, store number of ports.
    //
    rhDescriptorA.ul = READ_REGISTER_ULONG(&operationalRegisters->HcRhDescriptorA.ul);
    rhObject->NumberOfPorts = rhDescriptorA.NumberDownstreamPorts;
    if(rhDescriptorA.NoPowerSwitch)
    {
        USB_DBG_WARN_PRINT(("USB Card Doesn't support power switching!!"));
    } else
    {
    
        //
        //  Calculate the relative PowerOnToGoodTime in 100 ns.  The negative sign is
        //  so the KeDelayExecutionThread will treat it as a relative time.
        //
        rhObject->PowerOnToGoodTime.QuadPart = -rhDescriptorA.PowerOnToPowerGoodTime*2*10000;
    
        //
        //  Build changes to write back to descriptor A
        //
        if(!rhDescriptorA.PowerSwitchingMode || !rhDescriptorA.OverCurrentProtectionMode)
        {
            USB_DBG_TRACE_PRINT(("Switch to per-port power and overcurrent protection:"));
            rhDescriptorA.PowerSwitchingMode = 1;   //  Switch the PowerSwitchingMode to Per-port
            rhDescriptorA.OverCurrentProtectionMode = 1; // Per port overcurrent protection
            WRITE_REGISTER_ULONG(&operationalRegisters->HcRhDescriptorA.ul, rhDescriptorA.ul);
        }

        //
        //  As a diagnostic make sure that our changes were accepted
        //  we only support per-port power.
        //
        #if DBG
        rhDescriptorA.ul = READ_REGISTER_ULONG(&operationalRegisters->HcRhDescriptorA.ul);
        if(!rhDescriptorA.PowerSwitchingMode || !rhDescriptorA.OverCurrentProtectionMode)
        {
            USB_DBG_ERROR_PRINT(("Root Hub didn't accept ganged -> per-port changes.  Incompatible hardware!"));
        }
        #endif
    }    


#if DBG
    if(0xFF==rhDescriptorA.PowerOnToPowerGoodTime)
    {
        //
        //  In truth, there may be other cards that require this long delay.
        //  The Entrega with the CMD chip is just the one we are worried about.
        //
        USB_DBG_WARN_PRINT(("CMD USB CARD DETECTED(requires long power-on delay)!"));
    }
#endif 

    USB_DBG_TRACE_PRINT(("Root Hub's Descriptor A:"));
    USB_DBG_TRACE_PRINT(("  NumberDownstreamPorts     : %d", (ULONG)rhObject->NumberOfPorts));
    USB_DBG_TRACE_PRINT(("  NoPowerSwitching          : %s", rhDescriptorA.NoPowerSwitch ? "TRUE" : "FALSE"));
    USB_DBG_TRACE_PRINT(("  PowerSwitchingMode        : %s", rhDescriptorA.PowerSwitchingMode ? "Per-Port" : "Ganged"));
    USB_DBG_TRACE_PRINT(("  DeviceType                : %s", rhDescriptorA.DeviceType ? "Compound" : "Simple"));
    USB_DBG_TRACE_PRINT(("  OverCurrentProtectionMode : %s", rhDescriptorA.OverCurrentProtectionMode ? "Per-Port" : "Overall"));
    USB_DBG_TRACE_PRINT(("  NoOverCurrentProtection   : %s", rhDescriptorA.NoOverCurrentProtection ? "TRUE" : "FALSE"));
    USB_DBG_TRACE_PRINT(("  PowerOnToPowerGoodTime    : %d ms", (ULONG)rhDescriptorA.PowerOnToPowerGoodTime*2));
#endif //OHCD_XBOX_HARDWARE_ONLY   
    
    //
    //  Display info in Descriptor B
    //
#ifndef OHCD_XBOX_HARDWARE_ONLY
    rhDescriptorB.ul = READ_REGISTER_ULONG(&operationalRegisters->HcRhDescriptorB.ul);
    if(!rhDescriptorA.NoPowerSwitch)
    {
        //
        //  Make sure the PortPowerControlMask is set so that every port is per-port power.
        //
        mask = (rhObject->NumberOfPorts << 2) - 2;
        if(mask != (rhDescriptorB.PortPowerControlMask & mask))
        {
            rhDescriptorB.PortPowerControlMask |= mask;
            WRITE_REGISTER_ULONG(&operationalRegisters->HcRhDescriptorB.ul, rhDescriptorB.ul);
            //
            //  As a diagnostic read back the changes, and make sure that they took.
            //
            #if DBG
            rhDescriptorB.ul = READ_REGISTER_ULONG(&operationalRegisters->HcRhDescriptorB.ul);
            if(mask != (rhDescriptorB.PortPowerControlMask & mask))
            {
                USB_DBG_ERROR_PRINT(("Root Hub didn't accept PortPowerControlMask changes.  Incompatible hardware!"));
            }
            #endif
        }

        #if DBG 
        USB_DBG_TRACE_PRINT(("Root Hub's Descriptor B:"));
        for(index = 1, mask = 2; index <= (int)rhObject->NumberOfPorts; index++, mask <<= 1)
        {
            USB_DBG_TRACE_PRINT(("  DeviceRemovable(%d)      : %s", index, 
                ((ULONG)rhDescriptorB.DeviceRemovable & mask)? "Not Removable" : "Removable" ));
            USB_DBG_TRACE_PRINT(("  PortPowerControlMask(%d) : %s", index, 
                ((ULONG)rhDescriptorB.PortPowerControlMask & mask)? "Per-port" : "Ganged" ));   
        }
        #endif

        //
        //  Power on ports
        //
        /*  Not necessary, everything is per-port.
        ASSERT(!rhDescriptorA.NoPowerSwitch); //We don't support cards that are always powered.
        rhStatus.HubStatus = HC_RH_STATUS_DeviceRemoteWakeupEnable; - we don't support remote wakeup.
        rhStatus.HubStatus = 0;
        rhStatus.HubStatusChange = HC_RH_STATUS_LocalPower;
        WRITE_REGISTER_ULONG(&operationalRegisters->HcRhStatus.ul, rhStatus.ul);
        */
    }
#endif //OHCD_XBOX_HARDWARE_ONLY
    //
    //  Get the hub status
    //
    rhStatus.ul = READ_REGISTER_ULONG(&operationalRegisters->HcRhStatus.ul);
    USB_DBG_TRACE_PRINT(("Root Hub's Status:"));
    USB_DBG_TRACE_PRINT(("  LocalPowerStatus          : Not Readable"));
    USB_DBG_TRACE_PRINT(("  OverCurrentIndicator      : %s", 
                (rhStatus.HubStatus &  HC_RH_STATUS_OvercurrentIndicator )? "OverCurrent" : "OK"));
    USB_DBG_TRACE_PRINT(("  DeviceRemoteWakeupEnable  : %s",
                (rhStatus.HubStatus & HC_RH_STATUS_DeviceRemoteWakeupEnable) ? "ON" : "OFF"));
    //
    //  Clear the status change bits (so that we can get future notifications)
    //  You clear bits by writing a one to them.  Writing a one to the HubStatus
    //  actually changes settings which we don't want to.  So basically
    //  we clear the HubStatus back and write the status change bits back on themselves.
    //
    rhStatus.HubStatus = 0;
    WRITE_REGISTER_ULONG(&operationalRegisters->HcRhStatus.ul, rhStatus.ul);

#ifndef OHCD_XBOX_HARDWARE_ONLY    
    //
    //  Power on each port
    //
    if(!rhDescriptorA.NoPowerSwitch)
    {
        // 7 ms delay hack for some timing challenged cards
        LARGE_INTEGER   prePowerDelayHack;
        prePowerDelayHack.QuadPart = -7*10000;
        KeDelayExecutionThread(KernelMode, FALSE, &prePowerDelayHack);

        rhPortStatus.PortStatus = HC_RH_PORT_STATUS_SetPortPower;
        rhPortStatus.PortStatusChange = 0x0000;
        for(index=0; index < (int)rhObject->NumberOfPorts; index++)
        {
            WRITE_REGISTER_ULONG(&operationalRegisters->HcRhPortStatus[index].ul, rhPortStatus.ul);
        }
        //
        //  Wait the PowerOnToGoodTime
        //
        KeDelayExecutionThread(KernelMode, FALSE, &rhObject->PowerOnToGoodTime);
    }    
#endif //OHCD_XBOX_HARDWARE_ONLY

    //
    //  Retrieve the status of each port
    //
    RtlZeroMemory((PVOID)&portInfo, sizeof(OHCD_PORT_INFO));
    for(index=0, mask = 1; index < (int)rhObject->NumberOfPorts; index++, mask <<=1)
    {
        rhPortStatus.ul = READ_REGISTER_ULONG(&operationalRegisters->HcRhPortStatus[index].ul);
    
        //
        //  Traceouts, so we can see what is happening
        //
        USB_DBG_TRACE_PRINT(("Status of Port %d:", index+1));
        USB_DBG_TRACE_PRINT(("  CurrentConnectStatus      : %s",  
            (rhPortStatus.PortStatus  & HC_RH_PORT_STATUS_CurrentConnectStatus) ? "Connected" : "Disconnected"));
        USB_DBG_TRACE_PRINT(("  PortEnableStatus          : %s",
            (rhPortStatus.PortStatus  & HC_RH_PORT_STATUS_PortEnableStatus) ? "Enabled" : "Disabled"));
        USB_DBG_TRACE_PRINT(("  PortSuspendStatus         : %s",
            (rhPortStatus.PortStatus  & HC_RH_PORT_STATUS_PortSuspendStatus) ? "Suspended" : "Not Suspended"));
        USB_DBG_TRACE_PRINT(("  PortOverCurrentIndicator  : %s",
            (rhPortStatus.PortStatus  & HC_RH_PORT_STATUS_PortOverCurrentIndicator) ? "OverCurrent" : "OK"));
        USB_DBG_TRACE_PRINT(("  PortResetStatus           : %s",
            (rhPortStatus.PortStatus  & HC_RH_PORT_STATUS_PortResetStatus) ? "Reset Signal Active" : "Reset Signal Not Active"));
        USB_DBG_TRACE_PRINT(("  PortPowerStatus           : %s",
            (rhPortStatus.PortStatus  & HC_RH_PORT_STATUS_PortPowerStatus) ? "Power On" : "Power Off"));
        USB_DBG_TRACE_PRINT(("  LowSpeedDeviceAttached    : %s\n",
            (rhPortStatus.PortStatus  & HC_RH_PORT_STATUS_LowSpeedDeviceAttach) ? "LowSpeed" : "FullSpeed"));

        //
        //  We are looking just for connected status.
        //
        if(rhPortStatus.PortStatus  & HC_RH_PORT_STATUS_CurrentConnectStatus)
        {
            portInfo.PortsConnected |= mask;
            portInfo.PortsConnectionChanged |= mask;
        }

        //
        //  Clear the status change bits
        //
        rhPortStatus.PortStatus = 0;
        WRITE_REGISTER_ULONG(&operationalRegisters->HcRhPortStatus[index].ul, rhPortStatus.ul);
    }

    //
    //  Start interrupt processing for root-hub.
    //
    WRITE_REGISTER_ULONG(&operationalRegisters->HcInterruptEnable, HCINT_RootHubStatusChange);

    //
    //  Now we will call a common routine to process any devices we found.
    //  Since it is also called from the DPC for the ISR it expects to be 
    //  DISPATCH_LEVEL.
    //
    oldIrql = KeRaiseIrqlToDpcLevel();
    OHCD_RootHubProcessHotPlug(DeviceExtension, &portInfo);
    KeLowerIrql(oldIrql);

    USB_DBG_EXIT_PRINT(("Exiting OHCD_RootHubInitialize"));
}

#pragma code_seg(".XPPCODE")

VOID
FASTCALL
OHCD_RootHubProcessInterrupt(
    POHCD_DEVICE_EXTENSION      DeviceExtension
)
/*++

Routine Description:

    Process interrupts destined for the Root Hub.
    This routine is called LEVEL_DISPATCH from within
    the IsrDPC.
    
Arguments:

Return Value:
    
--*/          
{
    POHCI_OPERATIONAL_REGISTERS operationalRegisters = DeviceExtension->OperationalRegisters;
    HC_RH_STATUS                rhStatus;
    HC_RH_PORT_STATUS           rhPortStatus;
    ULONG                       index;
    ULONG                       mask;
    OHCD_PORT_INFO              portInfo;
    ULONG                       numPorts = DeviceExtension->RootHubObject.NumberOfPorts;

    USB_DBG_ENTRY_PRINT(("Entering OHCD_RootHubProcessInterrupt"));

    //
    //  Check to see if we have an overcurrent on the hub.
    //  This is the only global (i.e. NOT per port) cause of a root hub interrupt.
    //
    rhStatus.ul = READ_REGISTER_ULONG(&DeviceExtension->OperationalRegisters->HcRhPortStatus->ul);
    if(rhStatus.HubStatusChange & HC_RH_STATUS_OvercurrentIndicator)
    {
        USB_DBG_TRACE_PRINT(("The root-hub is indicating over-current!"));
    }
    //
    //  Now retrieve the status of each port
    //
    RtlZeroMemory((PVOID)&portInfo, sizeof(OHCD_PORT_INFO));
    for(index=0, mask = 1; index < numPorts; index++, mask <<=1)
    {
        rhPortStatus.ul = READ_REGISTER_ULONG(&operationalRegisters->HcRhPortStatus[index].ul);
        //
        //  If reset status change
        //
        if(rhPortStatus.PortStatusChange & HC_RH_PORT_STATUS_PortResetStatus)
        {
            
            PFNHCD_RESET_COMPLETE   CompleteProc = DeviceExtension->RootHubObject.ResetComplete;
            PVOID                   Context = DeviceExtension->RootHubObject.ResetContext;
            
            //
            //  If the complete proc has not been called (due to a timeout)
            //  then stop the watchdog and call it now.
            //
            if(CompleteProc)
            {
                
                //
                //  Stop the reset's watchdog timer
                //
                KeCancelTimer(&DeviceExtension->RootHubObject.ResetTimeoutTimer);
                USB_DBG_TRACE_PRINT(("ResetCompleted: Port = %d, Context = 0X%0.8x", index+1, Context));

                //
                //  Call the completion routine  (setting the status according to 
                //  low speed or not).
                //
                DeviceExtension->RootHubObject.ResetComplete = NULL;
                DeviceExtension->RootHubObject.ResetContext = 0;
                CompleteProc(
                    (rhPortStatus.PortStatus & HC_RH_PORT_STATUS_LowSpeedDeviceAttach) ? 
                    USBD_STATUS_LOWSPEED : USBD_STATUS_SUCCESS,
                    Context);
            }
        }

        //
        //  If the connect status changed, we need to process it.
        //
        if(rhPortStatus.PortStatusChange & HC_RH_PORT_STATUS_CurrentConnectStatus)
        {
            //
            //  Set the connect change bit.
            //
            portInfo.PortsConnectionChanged |= mask;
            //
            //  If connected, set the connect bit.
            //
            if(rhPortStatus.PortStatus & HC_RH_PORT_STATUS_CurrentConnectStatus)
            {
                portInfo.PortsConnected |= mask;
            }
        }
        //
        //  Check for overcurrent indicators
        //
        if(rhPortStatus.PortStatusChange & HC_RH_PORT_STATUS_PortOverCurrentIndicator)
        {
            //
            //  It appears that this can just be ignored, other things will happen (like the
            //  port being disabled) that we will deal with properly.
            //
            USB_DBG_WARN_PRINT(("Port %d of the root hub is indicating over-current!", index));
        }
        //
        //  Check for automatic disabling of a port
        //
        if(rhPortStatus.PortStatusChange & HC_RH_PORT_STATUS_PortEnableStatus)
        {
            USB_DBG_TRACE_PRINT(("The root-hub disabled port %d!  Usually a remove. Usually it was removed.", index));
        }

        //
        //  Clear the status change bits, so we can get notifications of further changes
        //
        rhPortStatus.PortStatus = 0;
        WRITE_REGISTER_ULONG(&operationalRegisters->HcRhPortStatus[index].ul, rhPortStatus.ul);
    }

    //
    //  Notify USBD about hot-plug events.
    //
    OHCD_RootHubProcessHotPlug(DeviceExtension, &portInfo);

    USB_DBG_EXIT_PRINT(("Exiting OHCD_RootHubProcessInterrupt"));
}


VOID
FASTCALL
OHCD_RootHubProcessHotPlug(
    IN POHCD_DEVICE_EXTENSION   DeviceExtension,
    IN POHCD_PORT_INFO          PortInfo
)
/*++

Routine Description:

    Does the work of reporting connect and disconnect
    events to USBD and the higher driver layers.  It is
    called from two different contexts.

    1) Just a straight call from OHCD_RootHubInitialize
        to process the connected devices found initially.
    2) As a worker thread, from a work item queued by OHCD_RootProcessInterrupt
        which runs in the context of IsrDPC.

    This routine needs to operate at LEVEL_PASSIVE, because the higher level need to synchronously
    process each detected device, which may be time consuming.  The reason behind this is that a newly
    detected device reports at the default USB address (0).  Only one device reporting at address zero
    should be enabled at a time.  Once USBD sets the address, we can then move on to enabling the next
    device.

Arguments:

Return Value:
    
--*/          
{
    UCHAR portIndex = 0;
    UCHAR portMask = 1;
    USB_DBG_ENTRY_PRINT(("Entering OHCD_RootHubProcessHotPlug"));

    ASSERT_DISPATCH_LEVEL();

    //
    //  Keep walking through port indices as long as there is
    //  a change we haven't processed yet.
    //
    while(portIndex < DeviceExtension->RootHubObject.NumberOfPorts)
    {
        //
        //  If this port has change, notify USBD of the change.
        //
        if(PortInfo->PortsConnectionChanged&portMask)
        {
            //
            //  Handle new connect
            //
            if(PortInfo->PortsConnected&portMask)
            {
                //
                //  Check for rapid fire remove\add, in which
                //  case do a remove before the add.
                //
                if(DeviceExtension->RootHubObject.DeviceDetectedBitmap&portMask)
                {
                    USBD_DeviceDisconnected(
                        (PVOID)DeviceExtension,
                        RootHubPortToPortNumber(portIndex)
                        );
                    USB_DBG_WARN_PRINT(("Remove and Add in one interrupt."));
                } else
                {
                    DeviceExtension->RootHubObject.DeviceDetectedBitmap |= portMask; //record that this
                                                                                     //port has a device.
                }
                //
                //  Report the device to USBD.  That is all we must do.
                //  (USBD expects a base of 1 for port numbers)
                USBD_DeviceConnected( (PVOID)DeviceExtension, RootHubPortToPortNumber(portIndex));
            }
            else
            //
            //  Handle Disconnect
            //
            {
                //
                //  Check for rapid fire add\remove, only call device connected
                //  if we called device connected at some point.
                //
                if(DeviceExtension->RootHubObject.DeviceDetectedBitmap&portMask)
                {
                    //
                    //  Tell USBD, which will synchronously cleanup
                    //
                    //  (USBD expects a base of 1 for port numbers)
                    DeviceExtension->RootHubObject.DeviceDetectedBitmap &= ~portMask;
                    USBD_DeviceDisconnected(
                        (PVOID)DeviceExtension,
                        RootHubPortToPortNumber(portIndex)
                        );
                } else
                {
                    USB_DBG_TRACE_PRINT(("Add and Remove in one interrupt."));
                }
            }
        }
        //
        //  Move on to next port
        //
        portIndex++;
        portMask <<= 1;
    }
    
    USB_DBG_EXIT_PRINT(("Exiting OHCD_RootHubProcessHotPlug"));
}


VOID
HCD_ResetRootHubPort(
    IN PVOID                    HcdDeviceExtension,
    IN ULONG                    PortNumber,
    IN PFNHCD_RESET_COMPLETE    ResetCompleteProc,
    IN PVOID                    CompleteContext
    )
/*++

Routine Description:

    Resets a port on the root hub.  Only one reset is allowed at a time.
    
Arguments:
    HcdDeviceExtension  - our device extension (must cast)
    PortNumber          - number of port to reset (0 based)
Return Value:
    
--*/
{
    POHCD_DEVICE_EXTENSION  deviceExtension = (POHCD_DEVICE_EXTENSION)HcdDeviceExtension;
    HC_RH_PORT_STATUS       rhPortStatus;
    LARGE_INTEGER           resetTimeOut;

    USB_DBG_TRACE_PRINT(("HCD_ResetRootHubPort: Port = %d, Context = 0X%0.8x", PortNumber, CompleteContext));
    //
    //  Convert port number to root hub port number
    //
    PortNumber=PortNumberToRootHubPort(PortNumber);

    //
    //  Validate Range
    //
    ASSERT(deviceExtension->RootHubObject.NumberOfPorts > PortNumber);
    
    //
    //  There should not be a reset currently pending.
    //
    ASSERT(NULL == deviceExtension->RootHubObject.ResetComplete);
    
    //
    //  Record the completion information.
    //
    deviceExtension->RootHubObject.ResetComplete = ResetCompleteProc;
    deviceExtension->RootHubObject.ResetContext = CompleteContext;
    
    //
    //  Reset the port
    //
    rhPortStatus.PortStatus = HC_RH_PORT_STATUS_SetPortReset;
    WRITE_REGISTER_ULONG(
        &deviceExtension->OperationalRegisters->HcRhPortStatus[PortNumber].ul,
        rhPortStatus.ul
        );

    //
    //  Set a watchdog timer.  Resets have been known to fail.
    //
    resetTimeOut.QuadPart = -10000 * 100; //allow 100 ms, it should take only 10 ms according to spec.
                                           //20 ms wasn't long enough for some cards.
    KeSetTimer(
        &deviceExtension->RootHubObject.ResetTimeoutTimer,
        resetTimeOut,
        &deviceExtension->RootHubObject.ResetTimeoutDPC
        );
}

VOID
HCD_DisableRootHubPort(
    IN PVOID                    HcdExtension,
    IN ULONG                    PortNumber
    )
{
    POHCD_DEVICE_EXTENSION deviceExtension = (POHCD_DEVICE_EXTENSION)HcdExtension;
    HC_RH_PORT_STATUS       rhPortStatus;
    //
    //  Convert port number to root hub port number
    //
    PortNumber=PortNumberToRootHubPort(PortNumber);
    //
    //  Validate Range
    //
    ASSERT(deviceExtension->RootHubObject.NumberOfPorts > PortNumber);
    //
    //  Disable the port
    //
    rhPortStatus.PortStatus = HC_RH_PORT_STATUS_ClearPortEnable;
    WRITE_REGISTER_ULONG(
        &deviceExtension->OperationalRegisters->HcRhPortStatus[PortNumber].ul,
        rhPortStatus.ul
        );
}


VOID
OHCD_RootHubResetDpc(
    PKDPC Dpc,
    PVOID DeviceExtensionPtr,
    PVOID Bogus1,
    PVOID Bogus2
    )
{
    POHCD_DEVICE_EXTENSION deviceExtension = (POHCD_DEVICE_EXTENSION) DeviceExtensionPtr;
    PFNHCD_RESET_COMPLETE   CompleteProc = deviceExtension->RootHubObject.ResetComplete;
    PVOID                   Context = deviceExtension->RootHubObject.ResetContext;

    if(CompleteProc)
    {
        //
        //  This is usually nothing, but a device that was removed during the reset.
        //
        //  It has been found in the past to be indicative of other serious problems, though.
        //  Like the host being locked up because of bad register values.
        //
        USB_DBG_WARN_PRINT(("A port reset timed out on the root hub!"));

        //
        //  Call the completion routine with the bad news,
        //  it is up to the caller to recover.
        //
        USB_DBG_WARN_PRINT(("Reset Timed Out: Context = 0X%0.8x",  Context));
        deviceExtension->RootHubObject.ResetComplete = NULL;
        deviceExtension->RootHubObject.ResetContext = NULL;
        CompleteProc(USBD_STATUS_REQUEST_FAILED, Context);
    }
}

