xbox-kernel/private/ntos/dd/usb/ohcd/roothub.c
2020-09-30 17:17:25 +02:00

720 lines
25 KiB
C

/*++
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);
}
}