2236 lines
74 KiB
C++
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
tree.c
Abstract:
This module contains the code to manage the device tree.
The entry points to this module are:
Called During DriverEntry:
CDeviceTree::CDeviceTree
Called by root-hubs to indicate devices hot-plug events:
USBD_DeviceConnected
USBD_DeviceDisconnected
Called by hubs to indicate devices hot-plug events:
IUsbDevice::DeviceConnected
IUsbDevice::DeviceDisconnected
Called by class drivers to indicate the end of initialization or cleanup:
IUsbDevice::AddComplete
IUsbDevice::RemoveComplete
Environment:
kernel mode only
Notes:
Revision History:
02-11-00 : create by Mitchell Dernis (mitchd)
--*/
//
// Pull in OS headers
//
#define _XAPI_
extern "C" {
#include <ntos.h>
}
#include <ntrtl.h>
#include <nturtl.h>
#include <xtl.h>
#include <xapidrv.h>
//
// Setup the debug information for this file (see ..\inc\debug.h)
//
#define MODULE_POOL_TAG 'DBSU'
#include <debug.h>
//
// Pull in usbd headers
//
#include <usbd.h>
DEFINE_USB_DEBUG_FUNCTIONS("USBD");
//---------------------------------------------------------------------
// Forward declarations of routines defined and used only in this module
//---------------------------------------------------------------------
VOID USBD_DeviceEnumStage0();
VOID USBD_DeviceEnumStagePre1(USBD_STATUS UsbdStatus, PVOID Context);
VOID FASTCALL USBD_fDeviceEnumStage1(IUsbDevice *Device);
VOID USBD_DeviceEnumStage2(PURB Urb, IUsbDevice *Device);
VOID USBD_DeviceEnumStage3(PURB Urb, IUsbDevice *Device);
VOID USBD_DeviceEnumStagePre4(PURB Urb, IUsbDevice *Device);
VOID FASTCALL USBD_fDeviceEnumStage4(PURB Urb, IUsbDevice *Device);
VOID USBD_DeviceEnumStage5(PURB Urb, IUsbDevice *Device);
VOID USBD_DeviceEnumStage6(PURB Urb, IUsbDevice *Device);
VOID USBD_DeviceAbortEnum(PURB Unreferenced, IUsbDevice *Device);
VOID FASTCALL USBD_fDeviceAbortEnum2(IUsbDevice *Device);
VOID USBD_DeviceEnumNextPending(PURB Urb, IUsbDevice *Device);
VOID USBD_CompleteDeviceDisconnected(IUsbDevice *Device);
VOID USBD_SetEnumWatchDog();
VOID USBD_ClearEnumWatchDog();
VOID FASTCALL USBD_LoadClassDriver(IUsbDevice *Device, PNP_CLASS_ID ClassId);
UCHAR FASTCALL USBD_AllocateUsbAddress(PUSBD_HOST_CONTROLLER HostController);
VOID FASTCALL USBD_FreeUsbAddress(PUSBD_HOST_CONTROLLER HostController, UCHAR DeviceAddress);
//
// We do not include the CRT, but we need new.
// we declare it static, so we don't conflict on linking
// with other modules overriding new.
//
#pragma warning(disable:4211)
static __inline void * __cdecl operator new(size_t memSize){return RTL_ALLOCATE_HEAP(memSize);}
#pragma warning(default:4211)
//--------------------------------------------
// Declare device tree. (Don't worry about
// the parameter, we call the C'tor explicitely
// from driver entry with the correct value.)
//--------------------------------------------
// HACK: Call the fake default constructor to avoid having the real
// : constructor being called twice.
//
CDeviceTree g_DeviceTree;
#pragma code_seg(".XPPCINIT")
void
CDeviceTree::Init(ULONG NodeCount, ULONG MaxCompositeInterfaces)
/*++
Routine Description:
All of the initialization is done in this init function, because the
c'tor cannot be called reliably in all link environments.
Arguments:
DeviceExtension - Pointer to USBD device extension.
Return Value:
None
--*/
{
m_InProgress = FALSE;
m_FirstFree = 0;
m_FirstPendingEnum = NULL;
m_CurrentEnum = NULL;
m_NodeCount = NodeCount;
m_Devices = new IUsbDevice[m_NodeCount];
m_MaxCompositeInterfaces = MaxCompositeInterfaces;
for(char nodeIndex = 0; nodeIndex < (m_NodeCount-1); nodeIndex++)
{
m_Devices[nodeIndex].m_NextFree = nodeIndex+1;
}
KeInitializeDpc(&m_EnumDpc, USBD_DeviceEnumTimerProc, NULL);
KeInitializeTimer(&m_EnumTimer);
}
#pragma code_seg(".XPPCODE")
VOID
USBD_DeviceConnected(
IN PVOID HcdExtension,
IN UCHAR PortNumber
)
/*++
Routine Description:
Called by the root-hub code in the HCD. We find
the device for the root-hub by looking in the DeviceObject,
and then call the IUsbDevice::DeviceConntected.
--*/
{
//
// Start with a retry count of 5.
//
USBD_HostControllerFromHCDExtension(HcdExtension)->RootHub->DeviceConnected(PortNumber, 5);
}
VOID IUsbDevice::DeviceConnected(UCHAR PortNumber, UCHAR RetryCount)
/*++
Routine Description:
Called by the hub driver or indirectly through USBD_DeviceConnected for the root-hub in the HCD.
This routine should be called at DISPATCH_LEVEL. To avoid blocking on device transfers this
routine kicks off a cascade of routines that enumerate a device.
1) IUsbDevice::DeviceConnected
a) Allocate Device Node
b) Acquire Enum Lock
i) If not acquired, add node to enum pending list, and return.
c) start 100ms timer, so that device can settle.
2) USBD_DeviceEnumStage0
a) reset port.
3) USBD_DeviceEnumStage1
a) open default endpoint
b) get DeviceDescriptor (8 bytes)
4) USBD_DeviceEnumStage2
a) allocate address
b) set address
5) USBD_DeviceEnumStage3
a) close default endpoint
6) USBD_DeviceEnumStage4
b) if device requires function driver
i) load function driver
ii) Class Driver calls IUsbDevice::AddComplete when it is done enumerating.
c) if device requires interface driver(s)
i) open default endpoint
ii) get configuration descriptor
7) USBD_DeviceEnumStage5
a) Set the configuration
8) USBD_DeviceEnumStage6
a) allocate nodes for each interfaces.
b) load class driver for first interface found.
c) Class Driver calls IUsbDevice::AddComplete when it is done enumerating.
9) IUsbDevice::AddComplete
a) if it is an interface and has more siblings, load a class driver for the next sibling.
i) Class Driver calls IUsbDevice::AddComplete when it is done enumerating.
b) Close the default endpoint when all the sibling drivers are loaded, cascading to USBD_DeviceEnumNextPending.
10) USBD_DeviceEnumNextPending
a) If there are devices pending enumeration, takes one off the pending list and goes vack to 3)
Each of the routines ends by sending an asynchronous request down to the HCD.
The completion routine is always the next routine in the cascade.
Arguments:
PortNumber - Port number on hub (temporarily high-bit can be set to indicate low speed)
LowSpeed - Set if a low speed device was detected.
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
ASSERT_DISPATCH_LEVEL();
USB_DBG_ENTRY_PRINT(("Entering USBD_DeviceConnected"));
USB_DBG_TRACE_PRINT(("%s device detected in Port %d of Hub 0x%0.8x", (LowSpeed) ? "LowSpeed" : "FullSpeed", PortNumber, this));
ASSERT((UDN_TYPE_HUB == m_Type) || (UDN_TYPE_ROOT_HUB == m_Type));
//
// Allocate a device.
//
IUsbDevice *newDevice = g_DeviceTree.AllocDevice();
if(!newDevice)
{
USB_DBG_WARN_PRINT(("Number of Devices Attached, exceeds the limits of our tree!"));
USB_DBG_EXIT_PRINT(("Exiting USBD_DeviceConnected, tree full"));
return;
}
//
// Initialize the couple of things we know
//
newDevice->m_Type = UDN_TYPE_PENDING_ENUM;
newDevice->m_PortNumber = PortNumber;
newDevice->m_ClassDriver = NULL;
newDevice->m_HostController = m_HostController;
//
// Plug the node into the tree.
//
InsertChild(newDevice);
//
// Serialize Device Enumeration
//
if(g_DeviceTree.m_InProgress)
{
//*
//* An enumeration is in progress, queue our new node
//* onto the pending list.
//*
//
// Set the retry count in the node.
//
newDevice->m_RetryCount = RetryCount;
//
// Record the earliest time that enumeration may start.
//
newDevice->m_EarliestEnumTime.QuadPart = KeQueryInterruptTime(); //Get a time stamp.
newDevice->m_EarliestEnumTime.QuadPart += 100*1000; //Add 100 ms for power up.
//
// Add the new device to the end of the pending list.
//
newDevice->m_NextPending = NULL;
if(!g_DeviceTree.m_FirstPendingEnum)
{
g_DeviceTree.m_FirstPendingEnum = newDevice;
} else
{
IUsbDevice *lastPending = g_DeviceTree.m_FirstPendingEnum;
while(lastPending->m_NextPending) lastPending = lastPending->m_NextPending;
lastPending->m_NextPending = newDevice;
}
//
// It is queued, so we can do nothing more.
//
USB_DBG_EXIT_PRINT(("Exiting USBD_DeviceConnected, queued pending enum node"));
return;
}
//
// We are actually working on this node.
//
newDevice->m_Type = UDN_TYPE_ENUMERATING;
//
// Mark the global enum block in progress.
// Store the node information we are working on.
//
g_DeviceTree.m_InProgress = TRUE;
g_DeviceTree.m_DeviceRemoved = FALSE;
g_DeviceTree.m_CurrentEnum = newDevice;
g_DeviceTree.m_RetryCount = RetryCount;
g_DeviceTree.m_EnumStage = USBD_ENUM_DEVICE_CONNECTED;
//
// Until we change it the address is 0
//
newDevice->m_Address = 0;
//
// Give newly plugged device a change to settle, 100ms according to specification.
//
g_DeviceTree.m_TimerReason = USBD_TIMER_REASON_STAGE0;
LARGE_INTEGER waitTime;
waitTime.QuadPart = -100*10000; //100ms wait
KeSetTimer(&g_DeviceTree.m_EnumTimer, waitTime, &g_DeviceTree.m_EnumDpc);
}
__inline void USBD_SetEnumWatchDog()
{
LARGE_INTEGER waitTime;
g_DeviceTree.m_TimerReason = USBD_TIMER_REASON_WATCHDOG;
waitTime.QuadPart = -5000*(LONGLONG)10000; //5 seconds (long, but that is the USB 1.1 specification: section 9.2.6.4)
KeSetTimer(&g_DeviceTree.m_EnumTimer, waitTime, &g_DeviceTree.m_EnumDpc);
}
__inline void USBD_ClearEnumWatchDog()
{
KeCancelTimer(&g_DeviceTree.m_EnumTimer);
}
VOID
USBD_DeviceEnumTimerProc(
IN PKDPC Dpc,
IN PVOID Unused1,
IN PVOID Unused2,
IN PVOID Unused3
)
{
switch(g_DeviceTree.m_TimerReason)
{
case USBD_TIMER_REASON_STAGE0:
USBD_DeviceEnumStage0();
break;
case USBD_TIMER_REASON_WATCHDOG:
USB_DBG_WARN_PRINT(("Enumeration Timed out"));
HCD_CancelRequest(
USBD_GetHCDExtension(g_DeviceTree.m_CurrentEnum->m_HostController),
&g_DeviceTree.m_EnumUrb
); //This should force the hung URB to complete.
break;
case USBD_TIMER_REASON_CONTINUE_STAGE1:
USBD_fDeviceEnumStage1(g_DeviceTree.m_CurrentEnum);
break;
case USBD_TIMER_REASON_CONTINUE_STAGE4:
USBD_fDeviceEnumStage4(&g_DeviceTree.m_EnumUrb,g_DeviceTree.m_CurrentEnum);
break;
default:
// The reason should always be set before the timer.
// So this is a bug in the code that set the timer if this case is hit.
ASSERT(FALSE);
}
}
void USBD_DeviceEnumStage0()
/*++
Routine Description:
Zeroeth stage of device enumeration after IUsbDevice::DeviceConnected. See IUsbDevice::DeviceConnected
for explanation of stages. In this routine we:
- reset the port
Arguments:
None
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
IUsbDevice *device = g_DeviceTree.m_CurrentEnum;
PVOID hcdDeviceExtension = USBD_GetHCDExtension(device->m_HostController);
USB_DBG_ENTRY_PRINT(("Entering USBD_DeviceEnumStage0"));
//
// Verify that the device was not removed while we were waiting.
//
if(g_DeviceTree.m_DeviceRemoved)
{
USB_DBG_ENTRY_PRINT(("Stage 0 detects device removal."));
USBD_DeviceAbortEnum(NULL, device);
return;
}
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_0;
//
// Reset Port - Either we need to ask HCD to do it for the RootHub,
// or we need to ask the USBHUB (there had better be one
// and only one linked to us.
//
IUsbDevice *parent = device->GetParent();
if(UDN_TYPE_ROOT_HUB == parent->m_Type)
{
HCD_ResetRootHubPort(
hcdDeviceExtension,
device->GetHubPort(),
(PFNHCD_RESET_COMPLETE)USBD_DeviceEnumStagePre1,
(PVOID)device
);
}
else
{
//
// Otherwise ask our hub driver component to handle the port reset.
//
USBHUB_DisableResetPort(parent, device->GetHubPort(), (PVOID)device, FALSE);
}
USB_DBG_EXIT_PRINT(("Exiting USBD_DeviceEnumStage0"));
}
void IUsbDevice::ResetComplete(USBD_STATUS UsbdStatus, PVOID Context)
/*++
* Wraps USBD_DeviceEnumStage1 for Hub Class Driver.
*
--*/
{
USBD_DeviceEnumStagePre1(UsbdStatus, Context);
}
VOID
USBD_DeviceEnumStagePre1(
USBD_STATUS UsbdStatus,
PVOID Context
)
/*++
Routine Description:
Section 9.2.6.2 stipulates a 10 ms recovery
time after the 10 ms RESET. This
routine sets a 10 ms timer.
--*/
{
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_PRE1;
//
// Check that the reset port worked, and that the
// device has not been ripped out between
// enumeration stages.
//
if(USBD_ERROR(UsbdStatus))
{
USB_DBG_WARN_PRINT(("Pre Stage 1 detects failue: UsbdStatus = 0x%0.8x", UsbdStatus));
USBD_DeviceAbortEnum(NULL, (IUsbDevice *)Context);
return;
}
//
// Set the speed here.
//
IUsbDevice *device = (IUsbDevice *)Context;
if(USBD_STATUS_LOWSPEED == UsbdStatus)
{
device->m_PortNumber |= UDN_LOWSPEED_PORT;
} else
{
ASSERT(!(device->m_PortNumber&UDN_LOWSPEED_PORT));
}
//
// A ten millisecond wait is required after reset.
//
LARGE_INTEGER waitTime;
g_DeviceTree.m_TimerReason = USBD_TIMER_REASON_CONTINUE_STAGE1;
waitTime.QuadPart = -10*10000; //10 ms wait
KeSetTimer(&g_DeviceTree.m_EnumTimer, waitTime, &g_DeviceTree.m_EnumDpc);
}
void
FASTCALL
USBD_fDeviceEnumStage1(
IUsbDevice *Device
)
/*++
Routine Description:
First stage of device enumeration after IUsbDevice::DeviceConnected.
See IUsbDevice::DeviceConnected for explanation of stages.
In this routine we:
- open the default endpoint
- get the DeviceDescriptor
Arguments:
UsbdStatus - Status of reset port.
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
PVOID hcdDeviceExtension = USBD_GetHCDExtension(Device->m_HostController);
ASSERT_DISPATCH_LEVEL();
USB_DBG_ENTRY_PRINT(("Entering USBD_DeviceEnumStage1"));
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_1;
if(g_DeviceTree.m_DeviceRemoved)
{
USB_DBG_WARN_PRINT(("Stage 1 detects device removal."));
USBD_DeviceAbortEnum(NULL, Device);
return;
}
//
// Fill out URB for opening an endpoint
//
USB_BUILD_OPEN_ENDPOINT(
&g_DeviceTree.m_EnumUrb.OpenEndpoint,
0, //Address is 0 - default address
USB_ENDPOINT_TYPE_CONTROL,
USBD_DEFAULT_MAXPACKET0,
0, //Interval
);
g_DeviceTree.m_EnumUrb.OpenEndpoint.LowSpeed = Device->GetLowSpeed();
g_DeviceTree.m_EnumUrb.OpenEndpoint.FunctionAddress = 0;
//
// This URB is required to be synchronous and not allowed to fail
// according to specification. Why?
// * Control endpoint, so no bandwidth problems
// * Open endpoint does not communicate over wire.
//
HCD_SubmitRequest(
hcdDeviceExtension,
&g_DeviceTree.m_EnumUrb
);
//
// Retrieve the handle from the URB
//
Device->m_DefaultEndpoint = g_DeviceTree.m_EnumUrb.OpenEndpoint.EndpointHandle;
ASSERT(Device->m_DefaultEndpoint); //Though it is not suppose to fail under any circumstance, check in debug!
//
// Now issue an asynchronous request for
// the device descriptor. This will also
// move us on to the next stage.
//
// Don't use USB_BUILD_GET_DESCRIPTOR macro, because
// it was intended for class drivers, which don't know the
// EndpointHandle.
//
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.Length = sizeof(URB_CONTROL_TRANSFER);
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.Function = URB_FUNCTION_CONTROL_TRANSFER;
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.CompleteProc = (PURB_COMPLETE_PROC)USBD_DeviceEnumStage2;
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.CompleteContext = (PVOID)Device;
g_DeviceTree.m_EnumUrb.ControlTransfer.EndpointHandle = Device->m_DefaultEndpoint;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferBuffer = (PVOID)&g_DeviceTree.m_DeviceDescriptor;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferBufferLength = sizeof(USB_DEVICE_DESCRIPTOR8);
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferDirection = USB_TRANSFER_DIRECTION_IN;
g_DeviceTree.m_EnumUrb.ControlTransfer.ShortTransferOK = FALSE;
g_DeviceTree.m_EnumUrb.ControlTransfer.InterruptDelay = USBD_DELAY_INTERRUPT_0_MS;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.bmRequestType = USB_DEVICE_TO_HOST;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wValue = (USB_DEVICE_DESCRIPTOR_TYPE << 8);
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wIndex = 0;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wLength = sizeof(USB_DEVICE_DESCRIPTOR8);
//
// We don't worry about errors. The completion
// routine will get called even for errors, and
// we will just let the next stage deal with it.
//
USBD_SetEnumWatchDog();
HCD_SubmitRequest(
hcdDeviceExtension,
&g_DeviceTree.m_EnumUrb
);
USB_DBG_EXIT_PRINT(("Exiting USBD_DeviceEnumStage1"));
return;
}
VOID
USBD_DeviceEnumStage2(
PURB Urb,
IUsbDevice *device
)
/*++
Routine Description:
Second stage of device enumeration fter IUsbDevice::DeviceConnected.
See IUsbDevice::DeviceConnected for explanation of stages.
In this routine we:
- allocate address
- set address
Arguments:
Urb - Pointer to URB (it had better be &g_DeviceTree.m_EnumUrb)
Device - Device (passed as the context for complete procedure)
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
ASSERT_DISPATCH_LEVEL();
USBD_ClearEnumWatchDog();
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_2;
//
// Check that the get device descriptor worked, and that the
// device has not been ripped out between
// enumeration stages.
//
if(USBD_ERROR(Urb->Header.Status) ||g_DeviceTree.m_DeviceRemoved)
{
USB_DBG_WARN_PRINT(("Stage 2 detects failue: Removed = %s, UsbdStatus = 0x%0.8x", g_DeviceTree.m_DeviceRemoved ? "TRUE":"FALSE", Urb->Header.Status));
//
// USBD_DeviceEnumStage3 close the default endpoint.
// If it sees the error or DeviceRemoved
// it will redirect the enumeration sequence to abort.
//
USBD_DeviceEnumStage3(Urb, device);
return;
}
//
// Do some sanity checks on the descriptor, and reject
// the device if they fail.
//
if(
(Urb->ControlTransfer.TransferBufferLength < 8) ||
(g_DeviceTree.m_DeviceDescriptor.bMaxPacketSize0 > 64) ||
(USB_DEVICE_DESCRIPTOR_TYPE != g_DeviceTree.m_DeviceDescriptor.bDescriptorType) ||
((sizeof(USB_DEVICE_DESCRIPTOR8) != g_DeviceTree.m_DeviceDescriptor.bLength) &&
(sizeof(USB_DEVICE_DESCRIPTOR) != g_DeviceTree.m_DeviceDescriptor.bLength))
)
{
USB_DBG_WARN_PRINT(("A device returned a corrupt device descriptor. Retries are allowed."));
Urb->Header.Status = USBD_STATUS_REQUEST_FAILED;
USBD_DeviceEnumStage3(Urb, device);
return;
}
//
// Dump the device descriptor in debug builds
//
USB_DBG_TRACE_PRINT(("Device Descriptor:"));
USB_DBG_TRACE_PRINT((" bcdUSB = 0x%0.4x", g_DeviceTree.m_DeviceDescriptor.bcdUSB));
USB_DBG_TRACE_PRINT((" bDeviceClass = %d", (long)g_DeviceTree.m_DeviceDescriptor.bDeviceClass));
USB_DBG_TRACE_PRINT((" bDeviceSubClass = %d", (long)g_DeviceTree.m_DeviceDescriptor.bDeviceSubClass));
USB_DBG_TRACE_PRINT((" bDeviceProtocol = %d", (long)g_DeviceTree.m_DeviceDescriptor.bDeviceProtocol));
USB_DBG_TRACE_PRINT((" bMaxPacketSize0 = %d", (long)g_DeviceTree.m_DeviceDescriptor.bMaxPacketSize0));
//
// Record MaxPacket0 from device descriptor
//
device->m_MaxPacket0 = g_DeviceTree.m_DeviceDescriptor.bMaxPacketSize0;
//
// Allocate an address
//
device->m_Address = USBD_AllocateUsbAddress(device->m_HostController);
//
// This should never happen, because our tree
// supports fewer devices than we have addresses.
// So if we hit this ASSERT it probably means
// that someone didn't free an address on removal.
//
ASSERT(device->m_Address);
//
// Set the address - No Macro for this one, because
// class drivers should never do it.
//
// Items commented are either correct from last transfer, or not necessary.
//
//g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.Length = sizeof(URB_CONTROL_TRANSFER);
//g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.Function = URB_FUNCTION_CONTROL_TRANSFER;
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.CompleteProc = (PURB_COMPLETE_PROC)USBD_DeviceEnumStage3;
//g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.CompleteContext = (PVOID)deviceNode;
//g_DeviceTree.m_EnumUrb.ControlTransfer.EndpointHandle = g_DeviceTree.m_EndpointHandle;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferBuffer = NULL;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferBufferLength = 0;
//g_DeviceTree.m_EnumUrb.ControlTransfer.TransferDirection = USB_TRANSFER_DIRECTION_IN;
//g_DeviceTree.m_EnumUrb.ControlTransfer.ShortTransferOK = FALSE;
//g_DeviceTree.m_EnumUrb.ControlTransfer.InterruptDelay = USBD_DELAY_INTERRUPT_0_MS;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.bmRequestType = USB_HOST_TO_DEVICE;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.bRequest = USB_REQUEST_SET_ADDRESS;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wValue = device->m_Address;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wIndex = 0;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wLength = 0;
//
// We don't worry about errors. The completion
// routine will get called even for errors, and
// we will just let the next stage deal with it.
//
USBD_SetEnumWatchDog();
HCD_SubmitRequest(USBD_GetHCDExtension(device->m_HostController), &g_DeviceTree.m_EnumUrb);
USB_DBG_EXIT_PRINT(("Exiting USBD_DeviceEnumStage2"));
return;
}
VOID
USBD_DeviceEnumStage3(
PURB Urb,
IUsbDevice *device
)
/*++
Routine Description:
Third stage of device enumeration after IUsbDevice::DeviceConnected.
See IUsbDevice::DeviceConnected for explanation of stages.
In this routine we:
- close default endpoint
***
*** BECAUSE THIS STAGE CLOSES THE DEFAULT ENDPOINT WHETHER THE PREVIOUS
*** OPERATION FAILED OR NOT, THIS STAGE IS CALLED BY OTHER STAGES
*** TO ABORT ENUMERATION IF THE DEFAULT ENDPOINT IS OPEN.
***
Arguments:
Urb - Pointer to URB (it had better be &g_DeviceTree.m_EnumUrb)
Device - Device (passed as the context for complete procedure)
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
USB_DBG_ENTRY_PRINT(("Entering USBD_DeviceEnumStage3"));
ASSERT_DISPATCH_LEVEL();
USBD_ClearEnumWatchDog();
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_3;
//
// Check if the address was set properly, and that the
// device has not been ripped out between
// enumeration stages.
//
if(USBD_ERROR(Urb->Header.Status) || g_DeviceTree.m_DeviceRemoved)
{
USB_DBG_ENTRY_PRINT(("Stage 3 detects failue: Removed = %s, UsbdStatus = 0x%0.8x", g_DeviceTree.m_DeviceRemoved ? "TRUE":"FALSE", Urb->Header.Status));
//
// Redirect the close the default endpoint normally done in this
// stage, to abort enumeration instead.
//
g_DeviceTree.m_EnumUrb.Header.CompleteProc = (PURB_COMPLETE_PROC)USBD_DeviceAbortEnum;
}
else
{
g_DeviceTree.m_EnumUrb.Header.CompleteProc = (PURB_COMPLETE_PROC)USBD_DeviceEnumStagePre4;
}
g_DeviceTree.m_EnumUrb.Header.Length = sizeof(URB_CLOSE_ENDPOINT);
g_DeviceTree.m_EnumUrb.Header.Function = URB_FUNCTION_CLOSE_ENDPOINT;
//g_DeviceTree.m_EnumUrb.Header.CompleteContext = (PVOID)device;
//g_DeviceTree.m_EnumUrb.CloseEndpoint.EndpointHandle = g_DeviceTree.m_EndpointHandle;
//
// We don't worry about errors. The completion
// routine will get called even for errors, and
// we will just let the next stage deal with it.
// A close cannot time out.
//
HCD_SubmitRequest(
USBD_GetHCDExtension(device->m_HostController),
&g_DeviceTree.m_EnumUrb
);
//
// We can just mark the default endpoint as closed right now.
//
device->m_DefaultEndpoint = NULL;
USB_DBG_EXIT_PRINT(("Exiting USBD_DeviceEnumStage3"));
return;
}
VOID
USBD_DeviceEnumStagePre4(
PURB Urb,
IUsbDevice *Device
)
/*++
Routine Description:
A fine point of the specification is that a device may require
2 ms after a SET_ADDRESS request before it has to repsond to
the new address. In stage 2 we set the address. In stage 3,
we closed the endpoint. However, testing shows that this is
not enough time.
So this "pre4" stage is introduced to insert an explicit
2 ms delay before stage 4.
USBD_TIMER_REASON_CONTINUE_STAGE4 tells the timer routine
that it should continue with stage 4 when this times out.
--*/
{
//
// Set Address take two millseconds to settle, so we
// set a timer before proceding to stage 4
//
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_PRE4;
LARGE_INTEGER waitTime;
g_DeviceTree.m_TimerReason = USBD_TIMER_REASON_CONTINUE_STAGE4;
waitTime.QuadPart = -2*10000; //2ms wait
KeSetTimer(&g_DeviceTree.m_EnumTimer, waitTime, &g_DeviceTree.m_EnumDpc);
}
VOID
FASTCALL
USBD_fDeviceEnumStage4(
PURB Urb,
IUsbDevice *Device
)
/*++
Routine Description:
Fourth stage of device enumeration after IUsbDevice::DeviceConnected.
See IUsbDevice::DeviceConnected for explanation of stages.
In this routine we have two possibilities:
Device requires a function driver:
- Load the class driver.
- We will complete enumeration when the function Driver
calls IUsbDevice::AddComplete
Device is a composite device:
- Reopen default endpoint - with correct address and bMaxPacket0.
- Get the Configuration descriptor and go on to stage 5
Arguments:
Urb - Pointer to URB (it had better be &g_DeviceTree.m_EnumUrb)
Device - Device (passed as the context for complete procedure)
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
ASSERT_DISPATCH_LEVEL();
USB_DBG_ENTRY_PRINT(("Entering USBD_DeviceEnumStage4"));
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_4;
//
// Closing an endpoint cannot fail. So we don't need to check, but
// we do need to check to make sure the device has not been removed.
//
if(g_DeviceTree.m_DeviceRemoved)
{
USB_DBG_ENTRY_PRINT(("Stage 4 detects device removal"));
//
// The default endpoint is closed so just abort
//
USBD_DeviceAbortEnum(NULL, Device);
return;
}
//
// This is a function driver
//
if(0 != g_DeviceTree.m_DeviceDescriptor.bDeviceClass)
{
//
// Change the node type (remembering that hubs are special)
//
if(USB_DEVICE_CLASS_HUB == g_DeviceTree.m_DeviceDescriptor.bDeviceClass)
{
Device->m_Type = UDN_TYPE_HUB;
}
else
{
Device->m_Type = UDN_TYPE_FUNCTION;
}
//
// Load the class driver
//
PNP_CLASS_ID classId;
classId.USB.bClass = g_DeviceTree.m_DeviceDescriptor.bDeviceClass;
classId.USB.bSubClass = g_DeviceTree.m_DeviceDescriptor.bDeviceSubClass;
classId.USB.bProtocol = g_DeviceTree.m_DeviceDescriptor.bDeviceProtocol;
classId.USB.bClassSpecificType = PNP_DEVICE_LEVEL_CLASS;
USBD_LoadClassDriver(Device, classId);
return;
}
//
// OK, we are either a COMPOSITE_FUNCTION or INTERFACE_FUNCTION
// so we need to get the configuration descriptor and move on to the
// next stage.
//
//*
//* Open the default endpoint, this time get the address
//* and stuff correct.
//*
//
// Initialize the enum urb to reopen the endpoint with the correct address
// and the correct max packet
USB_BUILD_OPEN_ENDPOINT(
&g_DeviceTree.m_EnumUrb.OpenEndpoint,
0, //Endpoint Address is 0 - default endpoint
USB_ENDPOINT_TYPE_CONTROL,
g_DeviceTree.m_DeviceDescriptor.bMaxPacketSize0,
0 //Interval
);
g_DeviceTree.m_EnumUrb.OpenEndpoint.LowSpeed = Device->GetLowSpeed();
g_DeviceTree.m_EnumUrb.OpenEndpoint.FunctionAddress = Device->m_Address;
//
// This URB is required to be synchronous and not allowed to fail
// according to specification. Why?
// * Control endpoint, so no bandwidth problems
// * Open endpoint does not communicate over wire.
//
HCD_SubmitRequest(
USBD_GetHCDExtension(Device->m_HostController),
&g_DeviceTree.m_EnumUrb
);
//
// We want the default endpoint to be available to the class driver
// in later stages.
//
Device->m_DefaultEndpoint = g_DeviceTree.m_EnumUrb.OpenEndpoint.EndpointHandle;
ASSERT(Device->m_DefaultEndpoint); //Though it is not suppose to fail under any circumstance, check in debug!
//
// Now get the configuration descriptor and go on to next stage
//
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.Length = sizeof(URB_CONTROL_TRANSFER);
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.Function = URB_FUNCTION_CONTROL_TRANSFER;
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.CompleteProc = (PURB_COMPLETE_PROC)USBD_DeviceEnumStage5;
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.CompleteContext = (PVOID)Device;
g_DeviceTree.m_EnumUrb.ControlTransfer.EndpointHandle = Device->m_DefaultEndpoint;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferBuffer = (PVOID)&g_DeviceTree.m_ConfigurationDescriptorBuffer;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferBufferLength = USBD_MAX_CONFIG_DESC_SIZE;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferDirection = USB_TRANSFER_DIRECTION_IN;
g_DeviceTree.m_EnumUrb.ControlTransfer.ShortTransferOK = TRUE;
g_DeviceTree.m_EnumUrb.ControlTransfer.InterruptDelay = USBD_DELAY_INTERRUPT_0_MS;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.bmRequestType = USB_DEVICE_TO_HOST;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8);
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wIndex = 0;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wLength = USBD_MAX_CONFIG_DESC_SIZE;
//
// We don't worry about errors. The completion
// routine will get called even for errors, and
// we will just let the next stage deal with it.
//
USBD_SetEnumWatchDog();
HCD_SubmitRequest(
USBD_GetHCDExtension(Device->m_HostController),
&g_DeviceTree.m_EnumUrb
);
return;
}
VOID
USBD_DeviceEnumStage5(
PURB Urb,
IUsbDevice *Device
)
/*++
Routine Description:
Fifth stage of device enumeration after IUsbDevice::DeviceConnected.
See IUsbDevice::DeviceConnected for explanation of stages.
In this routine we:
- set the configuration.
Arguments:
Urb - Pointer to URB (it had better be &g_DeviceTree.m_EnumUrb)
Device - Device (passed as the context for complete procedure)
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor;
ASSERT_DISPATCH_LEVEL();
USBD_ClearEnumWatchDog();
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_5;
//
// Check if we got the configuration descriptor, and that the
// device has not been ripped out between enumeration stages.
//
if(USBD_ERROR(Urb->Header.Status) || g_DeviceTree.m_DeviceRemoved)
{
USB_DBG_ENTRY_PRINT(("Stage 5 detects failue: Removed = %s, UsbdStatus = 0x%0.8x", g_DeviceTree.m_DeviceRemoved ? "TRUE":"FALSE", Urb->Header.Status));
//
// USBD_DeviceEnumStage3 close the default endpoint.
// When it sees the error or DeviceRemoved,
// it will redirect the enumeration sequence to abort.
//
USBD_DeviceEnumStage3(Urb, Device);
return;
}
//
// Set the configurationDescriptor.
//
configurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)g_DeviceTree.m_ConfigurationDescriptorBuffer;
//
// Verify the the configurationDescriptor does not exceed the Xbox
// limit.
//
if(USBD_MAX_CONFIG_DESC_SIZE < configurationDescriptor->wTotalLength)
{
g_DeviceTree.m_RetryCount = 0; //Don't retry this
USB_DBG_WARN_PRINT(("Unsupported Hardware: Configuration Descriptor Length exceeds Xbox limit(%d bytes), disabling device", USBD_MAX_CONFIG_DESC_SIZE));
Urb->Header.Status = USBD_STATUS_UNSUPPORTED_DEVICE;
USBD_DeviceEnumStage3(Urb, Device);
return;
}
//
// Verify that the entire descriptor was sent.
//
if(configurationDescriptor->wTotalLength != Urb->ControlTransfer.TransferBufferLength)
{
USB_DBG_WARN_PRINT(("Device did not return entire configuration descriptor, reenumerating"));
Urb->Header.Status = USBD_STATUS_ERROR;
//Retries will be allowed.
USBD_DeviceEnumStage3(Urb, Device);
return;
}
//
// Now set the configuration and go on to next stage
//
//g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.Length = sizeof(URB_CONTROL_TRANSFER);
//g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.Function = URB_FUNCTION_CONTROL_TRANSFER;
g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.CompleteProc = (PURB_COMPLETE_PROC)USBD_DeviceEnumStage6;
//g_DeviceTree.m_EnumUrb.ControlTransfer.Hdr.CompleteContext = (PVOID)deviceNode;
//g_DeviceTree.m_EnumUrb.ControlTransfer.EndpointHandle = GLOBAL_USBD_EnumBlock.EndpointHandle;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferBuffer = NULL;
g_DeviceTree.m_EnumUrb.ControlTransfer.TransferBufferLength = 0;
//g_DeviceTree.m_EnumUrb.ControlTransfer.TransferDirection = USB_TRANSFER_DIRECTION_IN;
//g_DeviceTree.m_EnumUrb.ControlTransfer.ShortTransferOK = TRUE;
//g_DeviceTree.m_EnumUrb.ControlTransfer.InterruptDelay = USBD_DELAY_INTERRUPT_0_MS;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.bmRequestType = USB_HOST_TO_DEVICE;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.bRequest = USB_REQUEST_SET_CONFIGURATION;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wValue = configurationDescriptor->bConfigurationValue;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wIndex = 0;
g_DeviceTree.m_EnumUrb.ControlTransfer.SetupPacket.wLength = 0;
//
// We don't worry about errors. The completion
// routine will get called even for errors, and
// we will just let the next stage deal with it.
//
USBD_SetEnumWatchDog();
HCD_SubmitRequest(
USBD_GetHCDExtension(Device->m_HostController),
&g_DeviceTree.m_EnumUrb
);
return;
}
VOID
USBD_DeviceEnumStage6(
PURB Urb,
IUsbDevice *Device
)
/*++
Routine Description:
Sixth stage of device enumeration after IUsbDevice::DeviceConnected.
See IUsbDevice::DeviceConnected for explanation of stages.
In this routine we have either a INTERACE_FUNCTION or COMPOSITE FUNCTION.
If it is a COMPOSITE_FUNCTION we
- Allocate additional nodes for each of the interfaces and plug them into the tree.
- Begin notifying the interface drivers
If it is an INTERFACE_FUNCTION we
- Notify the interface driver
Arguments:
Urb - Pointer to URB (it had better be &g_DeviceTree.m_EnumUrb)
DeviceNode - DeviceNode (passed as the context for complete procedure)
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
ASSERT_DISPATCH_LEVEL();
USBD_ClearEnumWatchDog();
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_6;
//
// Check if we got the configuration descriptor, and that the
// device has not been ripped out between enumeration stages.
//
if(USBD_ERROR(Urb->Header.Status) || g_DeviceTree.m_DeviceRemoved)
{
USB_DBG_ENTRY_PRINT(("Stage 6 detects failue: Removed = %s, UsbdStatus = 0x%0.8x", g_DeviceTree.m_DeviceRemoved ? "TRUE":"FALSE", Urb->Header.Status));
//
// USBD_DeviceEnumStage3 close the default endpoint.
// When it sees the error or DeviceRemoved,
// it will redirect the enumeration sequence to abort.
//
USBD_DeviceEnumStage3(Urb, Device);
return;
}
//
// After a successful set configuration clear the data toggle bits.
//
Device->m_DataToggleBits = 0;
//
// Begin parsing the configuration descriptor
//
PUCHAR currentParseLocation = g_DeviceTree.m_ConfigurationDescriptorBuffer;
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)g_DeviceTree.m_ConfigurationDescriptorBuffer;
//
// Find the first interface, this will be needed for both
// UDN_TYPE_INTERFACE_FUNCTION and UDN_TYPE_COMPOSITE_FUNCTION
//
do
{
currentParseLocation += ((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bLength;
//
// Check to make sure we haven't walked off the end of the conifugration descriptor,
// and that we are not in an endless loop.
//
if(
(currentParseLocation >= (g_DeviceTree.m_ConfigurationDescriptorBuffer + USBD_MAX_CONFIG_DESC_SIZE)) ||
(0 == ((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bLength)
){
USB_DBG_WARN_PRINT(("Corrupt Configuration Descriptor."));
g_DeviceTree.m_RetryCount = 0; //Don't retry this
Urb->Header.Status = USBD_STATUS_UNSUPPORTED_DEVICE;
USBD_DeviceEnumStage3(Urb, Device);
return;
}
}while(((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bDescriptorType != USB_INTERFACE_DESCRIPTOR_TYPE);
g_DeviceTree.m_InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)currentParseLocation;
//
// If it has only one interface than it is an UDN_TYPE_INTERFACE_FUNCTION
//
if(1 == configurationDescriptor->bNumInterfaces || 0 == g_DeviceTree.m_MaxCompositeInterfaces)
{
//
// Fill out UDN_TYPE_INTERFACE_FUNCTION specific stuff
//
Device->m_Type = UDN_TYPE_INTERFACE_FUNCTION;
}
else
{
IUsbDevice *interfaceDevice;
ULONG interfaceIndex;
//
// Change the main device node to UDN_TYPE_COMPOSITE_FUNCTION
//
Device->m_Type = UDN_TYPE_COMPOSITE_FUNCTION;
Device->m_FirstChild = UDN_INVALID_NODE_INDEX;
//
// Allocates device for all of the interfaces
//
//
for(interfaceIndex = 0; interfaceIndex < configurationDescriptor->bNumInterfaces; interfaceIndex++)
{
//
// Refuse to enumerate more interfaces on a composite device
// then any of the drivers registered for
if(g_DeviceTree.m_MaxCompositeInterfaces <= interfaceIndex)
{
break;
}
//
// Allocate a device on our tree
//
interfaceDevice = g_DeviceTree.AllocDevice();
if(NULL == interfaceDevice)
{
USB_DBG_WARN_PRINT(("Number of Devices Attached, exceeds the limits of our tree!"));
break;
}
//
// initialize basic information from the composite device
//
interfaceDevice->m_Type = UDN_TYPE_INTERFACE;
interfaceDevice->m_PortNumber = (interfaceIndex+1) | (Device->m_PortNumber & UDN_LOWSPEED_PORT);
interfaceDevice->m_Address = Device->m_Address;
interfaceDevice->m_DefaultEndpoint = Device->m_DefaultEndpoint;
interfaceDevice->m_HostController = Device->m_HostController;
interfaceDevice->m_MaxPacket0 = Device->m_MaxPacket0;
//
// Plug node into the tree as a child of the composite device
//
Device->InsertChild(interfaceDevice);
}
//
// The parents defaultendpoint has to appear closed, so
// right after a child interface is complete, it can
// immediately open the default endpoint.
//
Device->m_DefaultEndpoint = NULL;
//
// We are done with the UDN_TYPE_COMPOSITE_FUNCTION
// move on to processing its first child.
//
Device = Device->GetFirstChild();
}
//
// OK set the interface.
//
Device->m_bInterfaceNumber = g_DeviceTree.m_InterfaceDescriptor->bInterfaceNumber;
//
// Load the class driver
//
PNP_CLASS_ID classId;
classId.USB.bClass = g_DeviceTree.m_InterfaceDescriptor->bInterfaceClass;
classId.USB.bSubClass = g_DeviceTree.m_InterfaceDescriptor->bInterfaceSubClass;
classId.USB.bProtocol = g_DeviceTree.m_InterfaceDescriptor->bInterfaceProtocol;
classId.USB.bClassSpecificType = PNP_INTERFACE_LEVEL_CLASS;
USBD_LoadClassDriver(Device, classId);
return;
}
VOID
IUsbDevice::AddComplete(
USBD_STATUS Status
)
/*++
Routine Description:
Last stage of enumeration after IUsbDevice::DeviceConnected. See IUsbDevice::DeviceConnected
for explanation of stages.
This routine is called a by a class driver to when it has completed the class specific enumeration
steps. This routine is necessary, because the class driver may have to make asynchronous requests
in order to complete its initialization, and therefore USBD needs to be notified when the
class driver is done.
The different types of devices require some special handindling.
1) For UDN_TYPE_INTERFACE devices if there is a sibling: it load the class driver for the sibling and return. Control
will come back to this routine when the class driver for the sibling has completed its enumeration steps.
2) For the last UDN_TYPE_INTERFACE of a UDN_TYPE_COMPOSITE_FUNCTION, or for UDN_TYPE_INTERFACE_FUNCTION it closes
the default endpoint. This function cascades to USBD_DeviceEnumNextPending.
3) For UDN_TYPE_FUNCTION it just calls USBD_DeviceEnumNextPending directly.
Arguments:
Status - Status from the class driver. Should be one of:
USBD_STATUS_SUCCESS - Device successfully initialized.
USBD_STATUS_UNSUPPORTED_DEVICE - Device not supported by the class driver.
NotifylParam - lParam to send with Add and Notifications.
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
ASSERT_DISPATCH_LEVEL();
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_ADD_COMPLETE;
//
// If the class driver rejected the device, than
// the there is no class driver.
//
if(USBD_STATUS_UNSUPPORTED_DEVICE == Status)
{
USB_DBG_WARN_PRINT(("Unsupported Device or Class Driver not present."));
m_ClassDriver = NULL;
} else
{
//
// Notify XAPI
//
if(0xFF != m_ClassSpecificType)
{
PXPP_DEVICE_TYPE deviceType = m_ClassDriver->DeviceTypes[m_ClassSpecificType];
if(deviceType)
{
XdReportDeviceInsertionRemoval(deviceType, m_ExternalPort, TRUE);
}
}
}
//
// Check for UDN_TYPE_FUNCTION and call USBD_DeviceEnumNextPending
//
if(
(m_Type == UDN_TYPE_FUNCTION) ||
(m_Type == UDN_TYPE_HUB)
)
{
//
// If the class driver failed it, then abort enumeration.
//
if(USBD_ERROR(Status))
{
if(!m_ClassDriver) g_DeviceTree.m_RetryCount = 0;
USBD_DeviceAbortEnum(NULL, this);
return;
}
//
// Functions are completely enumerated, nothing left to do,
// except move on to the next pending.
//
USBD_DeviceEnumNextPending(NULL, this);
return;
}
//
// The composite device is the parent of theis device if of type UDN_TYPE_INTERFACE_FUNCTION,
// and this device's parent if UDN_TYPE_INTERFACE. Assume it is this
// device for now, and the case below will change it if necessary.
//
IUsbDevice *compositeDevice = this;
//
// Mark the default endpoint as closed, but cache the endpointhandle
// so we can really close it. We do this here because it applies to
// the last sibling if UDN_TYPE_INTERFACE, and to UDN_TYPE_INTERFACE_FUNCTION.
// Normally, the UDN_TYPE_COMPOSITE node is non-NULL if any of its children
// are non-NULL, but during enumeration is an exception. This is so that
// immediately after a ClassDriver calls AddComplete it can open the default
// endpoint, and there is no race condition w.r.t. its sibilings completing enumeration.
// So before loaded even the first interface of the composite, we set the UDN_TYPE_COMPOSITE's
// m_DefaultEndpoint to NULL, while setting m_DefaultEndpoint for all the children to
// the same endpoint which was opened for enumeration purposes. Now, if the first
// child calls AddComplete and is immediately opened before its siblings finish enumeration
// it can reopen the default endpoint (getting a different handle). This code cannot step on that.
// So if AddComplete is called for UDN_TYPE_INTERFACE and it is the last sibling we close the
// handle in UDN_TYPE_INTERFACE, and totally ignore what is in its parent (which may be NULL,
// or may be a different endpoint handle). Note below, that we don't get as far as closing the
// endpoint if this is not the last sibling. If it is called for UDN_TYPE_INTERFACE_FUNCTION
// then there is nothing subtle going on.
//
PVOID endpointHandle = m_DefaultEndpoint;
m_DefaultEndpoint = NULL;
//
// There is some difference of type UDN_TYPE_INTERFACE.
// a) There may be siblings that have not have drivers loaded yet.
// b) The compositeDevice (the one that really owns the default endpoint)
// is our parent, not us.
//
PURB_COMPLETE_PROC CompleteProc = (PURB_COMPLETE_PROC)USBD_DeviceEnumNextPending;
if(m_Type == UDN_TYPE_INTERFACE)
{
IUsbDevice *Sibling = GetSibling();
//
// As long as this has siblings there must be more interfaces.
//
if(NULL != Sibling)
{
//
// Begin parsing the configuration descriptor
//
PUCHAR currentParseLocation = (PUCHAR)g_DeviceTree.m_InterfaceDescriptor;
//
// Parse to the next interface
//
do
{
currentParseLocation += ((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bLength;
ASSERT(currentParseLocation < (g_DeviceTree.m_ConfigurationDescriptorBuffer + USBD_MAX_CONFIG_DESC_SIZE));
}while(((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bDescriptorType != USB_INTERFACE_DESCRIPTOR_TYPE);
g_DeviceTree.m_InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)currentParseLocation;
//
// Now that we have the next interface set the siblings m_bInterfaceNumber.
//
Sibling->m_bInterfaceNumber = g_DeviceTree.m_InterfaceDescriptor->bInterfaceNumber;
//
// Load a driver for the sibling,
// and return.
//
PNP_CLASS_ID classId;
classId.USB.bClass = g_DeviceTree.m_InterfaceDescriptor->bInterfaceClass;
classId.USB.bSubClass = g_DeviceTree.m_InterfaceDescriptor->bInterfaceSubClass;
classId.USB.bProtocol = g_DeviceTree.m_InterfaceDescriptor->bInterfaceProtocol;
classId.USB.bClassSpecificType = PNP_INTERFACE_LEVEL_CLASS;
USBD_LoadClassDriver(Sibling, classId);
return;
}
else
{
//
// The next have this routine needs to know the compositeDevice
// which is this device's parent.
compositeDevice = GetParent();
}
} else
{
if(USBD_ERROR(Status))
{
if(!m_ClassDriver) g_DeviceTree.m_RetryCount = 0;
CompleteProc = (PURB_COMPLETE_PROC)USBD_DeviceAbortEnum;
}
}
//
// Close the temporary enumeration time default endpoint.
//
g_DeviceTree.m_EnumUrb.Header.Length = sizeof(URB_CLOSE_ENDPOINT);
g_DeviceTree.m_EnumUrb.Header.Function = URB_FUNCTION_CLOSE_ENDPOINT;
g_DeviceTree.m_EnumUrb.Header.CompleteProc = CompleteProc;
g_DeviceTree.m_EnumUrb.Header.CompleteContext = (PVOID)compositeDevice;
g_DeviceTree.m_EnumUrb.CloseEndpoint.EndpointHandle = endpointHandle;
//
// Close endpoint is not allowed to fail.
//
HCD_SubmitRequest(
USBD_GetHCDExtension(compositeDevice->m_HostController),
&g_DeviceTree.m_EnumUrb
);
}
VOID
USBD_DeviceAbortEnum(
PURB Urb,
IUsbDevice *Device
)
/*++
Routine Description:
If enumeration failed we need to stop enumerating and continue with the
next pending device. By the time we get to this routine, you
can be sure that the device is closed. We call
--*/
{
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_ABORT1;
if(!g_DeviceTree.m_DeviceRemoved)
//
// If the device was not removed, but failed, we need to
// disable it before continuing.
//
{
USB_DBG_WARN_PRINT(("Enumeration Failed"));
//
// Disable the port before completing abort, otherwise
// we could have an address conflict.
//
IUsbDevice *parent = Device->GetParent();
if(UDN_TYPE_ROOT_HUB == parent->m_Type)
{
HCD_DisableRootHubPort(USBD_GetHCDExtension(Device->m_HostController), Device->GetHubPort());
Device->DisableComplete(USBD_STATUS_SUCCESS, (PVOID)Device);
} else
{
USBHUB_DisableResetPort(parent, Device->GetHubPort(), (PVOID)Device, TRUE);
}
return;
} else
{
//
// Don't retry if the device was removed.
//
g_DeviceTree.m_RetryCount = 0;
}
USBD_fDeviceAbortEnum2(Device);
}
void IUsbDevice::DisableComplete(USBD_STATUS UsbdStatus, PVOID Context)
/*++
* Only gets called as part of abort.
*
--*/
{
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_DISABLE_COMPLETE;
IUsbDevice *device = (IUsbDevice *)Context;
//
// If the disable failed and the device was not removed
// then we cannot free the address. We fake out
// USBD_fDeviceAbortEnum2 by forgetting the address, this
// is an address leak;
//
if(USBD_ERROR(UsbdStatus) && !g_DeviceTree.m_DeviceRemoved)
{
//This is OK as long as it is extremely rare and difficult to induce.
USB_DBG_WARN_PRINT(("Address %d is about to leak, a device failed enumeration, and then the port disable failed.", device->m_Address));
device->m_Address = 0; //leak the address.
}
//
// Don't retry devices that have been removed.
//
if(g_DeviceTree.m_DeviceRemoved)
{
g_DeviceTree.m_RetryCount = 0;
}
USBD_fDeviceAbortEnum2(device);
}
VOID FASTCALL USBD_fDeviceAbortEnum2(IUsbDevice *Device)
/*++
* Finish up abort
*
--*/
{
UCHAR retryCount = 0;
UCHAR retryPort = 0;
IUsbDevice *parent = NULL;
g_DeviceTree.m_EnumStage = USBD_ENUM_STAGE_ABORT2;
//
// If the device is not removed
//
if(!g_DeviceTree.m_DeviceRemoved)
{
parent = Device->GetParent();
ASSERT(parent);
//
// Now remove it from its parent's tree
//
parent->RemoveChild(Device);
//
// Resubmit the device for enumeration if
// the retry count is non-zero.
//
if(g_DeviceTree.m_RetryCount)
{
retryCount = g_DeviceTree.m_RetryCount;
retryPort = Device->GetHubPort();
}
}
//
// If the address is non-zero free it.
//
if(Device->m_Address)
{
USBD_FreeUsbAddress(Device->m_HostController, Device->m_Address);
}
//
// Free the node
//
g_DeviceTree.FreeDevice(Device);
//
// If we detected a need for a retry, fire it here
//
if(retryCount)
{
parent->DeviceConnected(retryPort, --retryCount);
}
//
// Continue enumeration.
//
g_DeviceTree.m_DeviceRemoved = FALSE;
USBD_DeviceEnumNextPending(NULL, Device);
}
VOID
USBD_DeviceEnumNextPending(
PURB Urb,
IUsbDevice *Device
)
/*++
Routine Description:
After enumeration of a device is complete, this function is called to enumerate the next
device pendning enumeration. If there are none it clears the InProgress flag, so that
when a device is detected it can be enumerated.
This routine is cascaded to from AddComplete, so g_DeviceTree.m_CurrentEnum
will equal Device. This would prevent IUsbDevice::DeviceDisconnected from completing.
So we should also make one last check to see if the device was removed.
Arguments:
Urb - Could be NULL. So don't use it.
Device - Device which was just enumerated.
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
ASSERT_DISPATCH_LEVEL();
//
// One last check to make sure the device we were working on is not gone.
//
if(g_DeviceTree.m_DeviceRemoved)
{
USB_DBG_ENTRY_PRINT(("USBD_DeviceEnumNextPending detects device removal"));
//
// We are essentially in the running stage now, so just finish off the device disconnected
// operation.
USBD_CompleteDeviceDisconnected(Device);
}
g_DeviceTree.m_DeviceRemoved = FALSE;
//
// If nothing else is pending, clear the InProgress flag and
// we are done.
//
if(NULL == g_DeviceTree.m_FirstPendingEnum)
{
g_DeviceTree.m_CurrentEnum = NULL;
g_DeviceTree.m_InProgress = FALSE;
return;
}
//---------------------------------------------
// One or more devices are pending enumeration
//---------------------------------------------
//
// Pop a device off the pending list, and into current enum.
//
g_DeviceTree.m_CurrentEnum = g_DeviceTree.m_FirstPendingEnum;
g_DeviceTree.m_FirstPendingEnum = g_DeviceTree.m_CurrentEnum->m_NextPending;
g_DeviceTree.m_RetryCount = g_DeviceTree.m_CurrentEnum->m_RetryCount;
g_DeviceTree.m_EnumStage = USBD_ENUM_DEVICE_CONNECTED;
g_DeviceTree.m_CurrentEnum->m_Type = UDN_TYPE_ENUMERATING;
g_DeviceTree.m_CurrentEnum->m_ClassDriver = NULL;
g_DeviceTree.m_CurrentEnum->m_Address = 0;
//
// If it is too early to enumerate than wait until
// it is time, otherwise start stage 0 now.
LARGE_INTEGER currentTime;
currentTime.QuadPart = KeQueryInterruptTime();
if(currentTime.QuadPart > g_DeviceTree.m_CurrentEnum->m_EarliestEnumTime.QuadPart)
{
USBD_DeviceEnumStage0();
}
else
{
g_DeviceTree.m_TimerReason = USBD_TIMER_REASON_STAGE0;
KeSetTimer(&g_DeviceTree.m_EnumTimer, g_DeviceTree.m_CurrentEnum->m_EarliestEnumTime, &g_DeviceTree.m_EnumDpc);
USB_DBG_TRACE_PRINT(("Current Tick = %d, Earliest Enum = %d", currentTime.LowPart, g_DeviceTree.m_CurrentEnum->m_EarliestEnumTime.LowPart));
}
}
VOID
USBD_DeviceDisconnected(
IN PVOID HcdExtension,
IN UCHAR PortNumber
)
/*++
Routine Description:
Called by the root-hub code in the HCD. We find
the device for the root-hub by looking in the DeviceObject,
and then call the IUsbDevice::DeviceDisconntected.
--*/
{
USBD_HostControllerFromHCDExtension(HcdExtension)->RootHub->DeviceDisconnected(PortNumber);
}
VOID IUsbDevice::DeviceDisconnected(UCHAR PortNumber)
/*++
Routine Description:
Called by the hub driver or through USBD_DeviceDisconnected by the root-hub code in the HCD
to report the disconnection of a USB device. This routine must be called at DISPATCH_LEVEL.
A device can be in one of three states when it is removed:
1) Pending Enumeration - it is in the list of devices waiting to be enumerated.
2) Enumerating - it is currently being enumerated.
3) Functioning - it has completed enumeration and is being used.
The handling depends on the state.
1) Pending Enumeration:
* Remove the device from its parents list of children.
* Remove the device from the list of devices waiting enumeration
* Put the device back on the free list.
It is all done synchronously.
2) Enumerating:
*Remove the device from its parents list of children.
*Mark the g_DeviceTree.m_DeviceRemoved. The next time an enumeration stage is started it will:
If the class driver has not been loaded:
* Close any open endpoints if necessary (shunting to stage 3).
* Call USBD_AbortEnumDevice which calls IUsbDevice::RemoveComplete.
If the class driver(s) are loaded, it calls USBD_CompleteDeviceDisconnected
3) Functioning:
This routine:
* Remove it from its parents list of children.
USBD_CompleteDeviceDisconnected:
* If it is a UDN_TYPE_HUB, UDN_TYPE_FUNCTION, UDN_TYPE_INTERFACE_FUNCTION, notify the class driver
that it is gone.
* For UDN_TYPE_COMPOSITE_FUNCTION we need to walk all of its children and notify each class driver that it
is gone.
Class Driver:
* Close all endpoints.
* The hub driver must call USBD_DeviceDisconnected (this routine) for each of its children.
* Call IUsbDevice::RemoveComplete for the device.
* It should notify its clients that the device is gone, and perform any additional cleanup it
needs to.
IUsbDevice::RemoveComplete:
* Finishes reclaiming the node.
Arguments:
PortNumber - Port number on hub (temporarily high-bit can be set to indicate low speed)
Return Value:
None.
This code returns from multiple places!
Context:
Must be called at DISPATCH_LEVEL
--*/
{
USB_DBG_ENTRY_PRINT(("Entering USBD_DeviceDisconnected"));
USB_DBG_TRACE_PRINT(("Device removed from Port %d of Hub 0x%0.8x", (ULONG)GetHubPort(), this));
ASSERT_DISPATCH_LEVEL();
ASSERT((UDN_TYPE_HUB == m_Type) || (UDN_TYPE_ROOT_HUB == m_Type));
//
// Find the node, among its parents children.
//
IUsbDevice *removedDevice = FindChild(PortNumber);
if(NULL == removedDevice)
{
USB_DBG_TRACE_PRINT(("USBD_DeviceDisconnected called for a device which does not exist."));
USB_DBG_TRACE_PRINT(("This will happen when an unsupported (or in some cases failed) device is removed."));
USB_DBG_TRACE_PRINT(("It also may happen as a result of some device failures."));
return;
}
//
// Remove the device from this hub's list of children.
//
RemoveChild(removedDevice);
//
// Handle disconnect if node is pending enumeration.
//
if(UDN_TYPE_PENDING_ENUM == removedDevice->m_Type)
{
//
// Remove the node from the pending list
//
if(g_DeviceTree.m_FirstPendingEnum == removedDevice)
{
g_DeviceTree.m_FirstPendingEnum = removedDevice->m_NextPending;
} else
//
// Not the first one, so we need to walk the pending list until we find it.
//
{
IUsbDevice *prevPending = g_DeviceTree.m_FirstPendingEnum;
while(removedDevice != prevPending->m_NextPending)
{
prevPending = prevPending->m_NextPending;
ASSERT(prevPending);
}
prevPending->m_NextPending = removedDevice->m_NextPending;
}
removedDevice->m_NextPending = NULL;
//
// Return the node to the free list.
//
g_DeviceTree.FreeDevice(removedDevice);
//
// The device is removed we are done
//
return;
}
//
// Handle disconnect if node is enumerating.
//
if(
g_DeviceTree.m_InProgress &&
(g_DeviceTree.m_CurrentEnum == removedDevice)
)
{
//
// Just tell the enumeration code that the device
// it is working on is now gone.
//
g_DeviceTree.m_DeviceRemoved = TRUE;
return;
}
//
// Handle disconnect if node is functioning
//
USBD_CompleteDeviceDisconnected(removedDevice);
USB_DBG_EXIT_PRINT(("Exiting USBD_DeviceDisonnected"));
}
VOID
USBD_CompleteDeviceDisconnected(
IUsbDevice *Device
)
/*++
Routine Description:
Utility function to complete the last step of hte USBD_DeviceDisconnected routine:
notifying the class drivers that the device has been removed. This has been abstracted,
because it is called in two places:
1) End of IUsbDevice::DeviceDisconnected.
2) From USBD_DeviceEnumNextPending.
The latter cases arises if the device is physically removed and DeviceDeviceDisconnected is called for
a device which has completed USBD_DeviceEnumStage6(for UDN_TYPE_COMPOSITE_FUNCTION, or UDN_TYPE_INTERFACE_FUNCTION) or
USBD_DeviceEnumStage4(for UDN_TYPE_FUNCITON and UDN_TYPE_HUB), but the AddComplete has not yet been called
by the class driver (or all the class drivers - in the case of UDN_TYPE_COMPOSITE_FUNCTION).
--*/
{
if(UDN_TYPE_COMPOSITE_FUNCTION == Device->m_Type)
{
//
// Walk children (i.e. interface and notify each driver. There is no need for serialization
// here so we just call them all.
//
IUsbDevice *child = Device->GetFirstChild();
while(child)
{
//Cache the next child, because RemoveComplete,
//removes it from the parent's list of children.
IUsbDevice *nextChild = child->GetSibling();
if(child->m_ClassDriver)
{
child->m_ClassDriver->RemoveDevice(child);
} else
{
child->RemoveComplete();
}
child = nextChild; //prepare for next interation
}
}
else
{
if(Device->m_ClassDriver)
{
Device->m_ClassDriver->RemoveDevice(Device);
} else
{
Device->RemoveComplete();
}
}
}
VOID IUsbDevice::RemoveComplete()
/*++
Routine Description:
When a device is disconnected, USBD_DeviceDisconnected calls each of the class drivers to give
them a chance to cleanup. Since they may need to perform asynchronous operations to complete their
cleanup (such as closing endpoints), completion of removal cannot proceed when the class driver returns
from the notification. Therefore class drivers must call this routine when they are done cleaning up.
Likewise, if removal occurs during enumeration, enumeration eventually ends up here. At this point, there are
two tasks left: return the node to the free list, and free the USB address.
However, UDN_TYPE_INTERFACE nodes have a little more work. They are children of UDN_TYPE_COMPOSITE_FUNCTION.
Since the parent UDN_TYPE_COMPOSITE_FUNCTION is needed for closing endpoints, it is still attached. So this routine
must remove the node from its parents' child list, and return the node to the free list. Upon removing the
last child, it should free the parent too. The USB address should be freed with parent, not the children.
Arguments:
DeviceObject - DeviceObject of host controller for detecting hub
DetectingHub - Device Node for detecting hub
PortNumber - Port number on hub (temporarily high-bit can be set to indicate low speed)
Return Value:
None.
Context:
Must be called at DISPATCH_LEVEL
--*/
{
ASSERT_DISPATCH_LEVEL();
//
// Notify XAPI
//
if(0xFF != m_ClassSpecificType)
{
PXPP_DEVICE_TYPE deviceType = m_ClassDriver->DeviceTypes[m_ClassSpecificType];
if(deviceType)
{
XdReportDeviceInsertionRemoval(deviceType, m_ExternalPort, FALSE);
}
}
//
// For UDN_TYPE_INTERFACE, remove the node from its parents' child list,
// and free parent if no more children
//
if(UDN_TYPE_INTERFACE == m_Type)
{
//
// Remove the parents list of children
// and free parent if no more children
IUsbDevice *parent = GetParent();
if(FALSE == parent->RemoveChild(this))
{
//
// Free the USB address
//
USBD_FreeUsbAddress(parent->m_HostController, parent->m_Address);
//
// Free the device.
//
g_DeviceTree.FreeDevice(parent);
}
}
else
{
//
// For non-UDN_TYPE_INTERFACE nodes
//
if(m_Address) //we have to check in case, we were aborted before a successful SET_ADDRESS
{
USBD_FreeUsbAddress(m_HostController, m_Address);
}
}
//
// Free node.
//
g_DeviceTree.FreeDevice(this);
return;
}
void IUsbDevice::DeviceNotResponding()
/*++
Routine Description:
If a device stops responding for any reason, a class driver
Arguments:
DeviceObject - DeviceObject of host controller for detecting hub
DetectingHub - Device Node for detecting hub
PortNumber - Port number on hub (temporarily high-bit can be set to indicate low speed)
Return Value:
None.
Context:
Must be called at DISPATCH_LEVEL
--*/
{
ASSERT_DISPATCH_LEVEL();
IUsbDevice *device;
//
// If the device is UDN_TYPE_INTERFACE, then the
// real node is the parent
//
if(UDN_TYPE_INTERFACE == m_Type)
{
device = GetParent();
ASSERT(device);
} else
{
device = this;
}
//
// If it has a parent, it is not pending
// remove, so we should reenumerate it.
//
IUsbDevice *parent = device->GetParent();
if(parent)
{
UCHAR hubPort = device->GetHubPort();
//
// Report the device as disconnected
//
parent->DeviceDisconnected(hubPort);
//
// Report the device as connected.
//
parent->DeviceConnected(hubPort, 5);
}
}
VOID
FASTCALL
USBD_LoadClassDriver(IUsbDevice *Device, PNP_CLASS_ID ClassId)
/*++
Routine Description:
This routine is a helper function for device enumeration. It is used three times
so it is extracted. It kicks off the Class Driver portion of enumeration.
Given a class ID it calls the USBD_FindClassDriver. If it finds one
it calls the class drivers AddDevice. Otherwise, it calls Device->AddComplete
with an error code.
Arguments:
Device - The device for which to load a driver.
ClassId = PnP Class ID for the driver to load.
Return Value:
None.
Context:
Must be called at DISPATCH_LEVEL
--*/
{
//
// Sets up the external port number
//
Device->SetExternalPort();
if(XDEVICE_ILLEGAL_PORT == Device->m_ExternalPort)
{
Device->AddComplete(USBD_STATUS_UNSUPPORTED_DEVICE);
return;
}
Device->m_ClassDriver = USBD_FindClassDriver(ClassId);
//
// Call AddDevice if we found a class driver
//
if(Device->m_ClassDriver)
{
Device->m_ClassDriver->AddDevice(Device);
//
// The class driver will call AddComplete, when it
// has completed its enumeration steps.
//
} else
//
// We don't have a class driver, so call AddComplete
// with a failure code.
//
{
Device->AddComplete(USBD_STATUS_UNSUPPORTED_DEVICE);
}
return;
}
UCHAR
FASTCALL
USBD_AllocateUsbAddress(
IN PUSBD_HOST_CONTROLLER HostController
)
/*++
Routine Description:
Arguments:
Return Value:
Valid USB address (1..127) to use for this device,
returns 0 if no device address available.
--*/
{
UCHAR address = 0;
UCHAR dwordIndex = 0;
ULONG bitMask = 1;
//
// The AddressList consists of four DWORDS.
// Together they form a 128 bit bitfield.
// bits that are set, are address that are
// taken.
//
//
// Search for a free address
//
while(++address < 128)
{
//
// If the address is available, claim it and break
// out of loop.
//
if(!(HostController->AddressList[dwordIndex] & bitMask))
{
HostController->AddressList[dwordIndex] |= bitMask;
break;
}
bitMask <<= 1;
if(!bitMask)
{
dwordIndex++;
bitMask = 1;
}
}
//
// Handle case of out of addresses (we fell through the loop).
//
// if(address == 128) address = 0;
address &= 0x007F; //This is more efficient than above.
return address;
}
VOID
FASTCALL
USBD_FreeUsbAddress(
IN PUSBD_HOST_CONTROLLER HostController,
IN UCHAR DeviceAddress
)
/*++
Routine Description:
Arguments:
Return Value:
Valid USB address (1..127) to use for this device,
returns 0 if no device address available.
--*/
{
UCHAR dwordIndex = 0;
//
// Sanity check address range
//
ASSERT(DeviceAddress > 0 && DeviceAddress < 128);
//
// Reduce the DeviceAddress to a dwordIndex and
// a bitIndex (DeviceAddress will be the bitIndex
// when we leave this loop.
//
DeviceAddress--;
while(DeviceAddress > 31)
{
dwordIndex++;
DeviceAddress -= 32;
}
//
// Now clear the bit
//
HostController->AddressList[dwordIndex] &= ~(1 << DeviceAddress);
}