Hello, my name is Martin Borve.  I am a software developer on the Windows USB team.  In this post I will provide a detailed description of the enumeration process used by the Windows 7 core USB driver stack - from the point the stack detects a presence of device to the point it indicates to the PnP manager that a new device has arrived.

Device enumeration for a USB port begins when the hub indicate a connect status change via the hub’s interrupt endpoint.  If the port status indicates a newly connected device, the USB hub driver will use the following sequences of steps to enumerate the device:

  1. Port Stabilization Debounce
  2. First Port Reset
  3. First Device Descriptor Request
  4. Second Port Reset
  5. Set USB Address
  6. Second Device Descriptor Request
  7. Configuration Descriptor Request
  8. MS OS Descriptor Query
  9. Serial Number String Descriptor Query
  10. MS OS Extended Configuration Descriptor Request
  11. MS OS Container ID Descriptor Query
  12. Language ID Query
  13. Product ID String Query
  14. Device Qualifier Descriptor Query
  15. Duplicate Device Detection
  16. Report New Device to PnP Manager

Let us find out in detail how the software stack interacts with the device in each of these steps by describing which hardware events are handled, which ones are ignored, how long the software waits for the next event to occur, when does the software attempt reenumeration, how many times it reattempts, what constitutes a broken device, what leads to enumeration failure, etc.

The steps 1 through 7 are required for the device to be enumerated and reported to the system. In the next blog post, I will summarize all the hardware issues during enumeration that can either cause the software stack to not report the device at all or report to the system as “Unknown Device” by providing  “USB\VID_0000&PID_0000” as the Device ID.

NOTE: Enumeration information provided here applies to 1.1 and 2.0 devices. The sequence and timing of various operations may change for 2.1+ and 3.0+ devices.

Port Stabilization Debounce

The hub driver must observe a period of at least 100ms where there are no port connect changes (USB 2.0 spec, 7.1.7.3, TATTDB).  If the port has not stabilized after 200ms, the hub driver will disable the port and cancel enumeration.  No device will be reported to PnP.

First Port Reset

Once the port debounce has completed successfully, the hub driver will issue a reset request for the port.  In normal operation this will result in the port status transitioning to a connected and enabled state, and the device itself will respond to the default USB address of 0.

Port resets of all USB devices are serialized via an “enumeration lock” on a per host controller basis, as only one USB device can be enabled with the default USB address 0 at a any one time.  The hub driver will acquire the enumeration lock prior to issuing the first port reset request, and will release it when the device has been assigned a non-default USB address, or when enumeration has been cancelled.  The hub driver uses a 5 second timeout for the port reset request in case it never completes.

While waiting for the first port reset to complete, the hub driver must be able to deal with the following events:

Device Disconnect

Enumeration is cancelled.   No device is reported to PnP.

Overcurrent Change

Enumeration is normally cancelled in this case, unless the over-current is determined to be spurious.  No device is reported to PnP.

Timeout of Port Reset

In the case of a timeout of the first reset request, the hub driver will attempt to retry enumeration up to 3 times by returning to the beginning of the “First Port Reset” state.  A delay of 500ms occurs between each retry to allow the device to settle.  If the port reset times out on the 3rd retry, enumeration will be cancelled and an “Unknown Device” will be reported to PnP.

If the port reset request completes successfully, the hub driver will proceed based on the current port state as follows:

Device Disconnected

Enumeration is cancelled.  No device is reported to PnP.

Port Connected and Disabled

Port reset completion is ignored.  Reset timeout will be allowed to run, and port reset will be retried as appropriate.

Port Connected and Suspended

Enumeration is cancelled. No device is reported to PnP.

Port Overcurrent

Port reset completion is ignored. Reset timeout will be allowed to run, and port reset will be retried as appropriate.

Port Enabled and Connected

This indicates a successful reset of the port.  The hub driver delays at least 10ms to allow for reset recovery (USB 2.0 spec, 7.1.7.3, TRSTRCY).  The hub driver moves to the “First Device Descriptor Request" state.   

First Device Descriptor Request

The USB driver stack issues a request for the USB Device Descriptor (GET_DESCRIPTOR for Descriptor Type DEVICE), using the default USB address of 0, and a maximum packet size of 8 bytes for low-speed devices and 64 bytes for full and high-speed devices.

This descriptor request is used solely to determine the correct maximum packet size for the default control endpoint, as specified in the Device Descriptor’s bMaxPacketSize0 field at offset 7.  USB devices are required to return at least the first 8 bytes of the Device Descriptor when they are at the default USB address (USB 2.0 spec, 5.5.3).

When requesting the device descriptor the hub driver will specify a transfer size of 64 bytes.  This is done because some older USB devices will behave badly if the request size is smaller.  We’ve also found some USB devices will babble when returning the device descriptor, but will still return valid data in at least the first 8 bytes.  For this reason transfer errors will be ignored if at least 8 bytes of data were returned by the device. 

If the request for the device descriptor fails, the hub driver will retry enumeration up to three times by returning to the “First Port Reset” state.  If the hub driver has already retried enumeration 3 times, enumeration will be cancelled and an “Unknown Device” will be reported to PnP.

If the device descriptor request succeeds, the hub driver will move to the “Second Port Reset” step.  All further control transfers for the default endpoint will use the maximum packet size specified in the Device Descriptor. 

Second Port Reset

In the early days of USB some USB devices would become confused by a second request for the Device Descriptor if they did not return the complete Device Descriptor for the first request.  To allow these devices to enumerate successfully it was necessary to reset the port between the first and second requests for the Device Descriptor.

The hub driver uses a 5 second timeout for the second port reset request in case it never completes.  While waiting for the second port reset to complete, the hub driver must be able to deal with the following events (this is essentially identical to the handling in the first reset request):

Device Disconnect

Enumeration is cancelled.   No device is reported to PnP.

Overcurrent Change

Enumeration is cancelled.  No device is reported to PnP. 

Timeout of Port Reset

In the case of a timeout of the second reset request, the hub driver will attempt to retry enumeration up to 3 times by returning to the beginning of the “First Port Reset” state.  If the port reset times out on the 3rd retry, enumeration will be cancelled and an “Unknown Device” will be reported to PnP. 

If the port reset request completes successfully, the hub driver will proceed based on the current port state as follows:

Device Disconnected

Enumeration is cancelled.  No device is reported to PnP.

Port Connected and Disabled

Port reset completion is ignored.  Reset timeout will be allowed to run, and port reset will be retried as appropriate.

Port Connected and Suspended

Enumeration is cancelled. No device is reported to PnP.

Port Overcurrent

Port reset completion is ignored. Reset timeout will be allowed to run, and port reset will be retried as appropriate.

Port Enabled and Connected

This indicates a successful reset of the port.  The hub driver delays at least 10ms to allow for reset recovery (USB 2.0 spec, 7.1.7.3, TRSTRCY).  The hub driver moves to the next enumeration state “Set USB Address".  The hub driver will delay 100ms after successfully resetting the port if enumeration had to be retried at least once. 

Set USB Address

The USB driver stack allocates a unique (per-controller) USB device address and issues a SET_ADDRESS request to the device.  If the SET_ADDRESS request fails or times out, enumeration is cancelled and an “Unknown Device” is reported to PnP.

If the SET_ADDRESS is successful, the hub driver will wait for at least 10ms to allow for device stabilization before moving to the “Second Device Descriptor Request” state. 

Second Device Descriptor Request

The USB driver stack will issue a second request for the full USB Device Descriptor (GET_DESCRIPTOR for Descriptor Type DEVICE).  If the request fails or times out, the port is disabled and enumeration is retried by returning to the “First Port Reset” state.  If the hub driver has already retried enumeration 3 times, enumeration is cancelled and an “Unknown Device” is reported to PnP.

Upon successful completion of the second request for the Device Descriptor, the hub driver will validate the Device Descriptor as follows:

  • The bLength field is equal to or greater than the size of a USB Device Descriptor, as defined in the USB 2.0 specification.
  • The bDescriptorType field is equal to descriptor type DEVICE (1).

If the validation fails, the port is disabled and enumeration is retried by returning to the “First Port Reset” state.  If the hub driver has already retried enumeration 3 times, enumeration is cancelled and an “Unknown Device” is reported to PnP.

Upon successful validation of the Device Descriptor, the hub driver will cache the descriptor, release the enumeration lock, and move to the “Configuration Descriptor Request” state. 

Configuration Descriptor Request

The USB driver stack will issue a request for the device’s USB Configuration Descriptor (GET_DESCRIPTOR for Descriptor Type CONFIGURATION).  For compatibility reasons the configuration descriptor request will specify a length of 255 bytes.

If the Configuration Descriptor request completes with an error or times out, the hub driver will disable the port and retry enumeration by returning to the “First Port Reset” state.  If the hub driver has already retried enumeration 3 times, enumeration is cancelled and an “Unknown Device” is reported to PnP.

If the Configuration Descriptor request completes successfully, the hub driver will verify that the number of bytes returned for the request is greater than or equal to the Configuration Descriptor’s wTotalLength value.  If it is not greater than or equal to wTotalLength, the hub driver will retry the Configuration Descriptor request once to insure the device is not returning invalid data in the descriptor.

Upon successful completion of the Configuration Descriptor request, the USB driver stack will validate the descriptor as follows:

  • The bLength field is equal to or greater than the size of a USB Configuration Descriptor, as defined in the USB 2.0 specification.
  • The bDescriptorType field is equal to descriptor type CONFIGURATION (2).

If the validation fails the hub driver will disable the port and retry enumeration by returning to the “First Port Reset” state.  If the hub driver has already retried enumeration 3 times, enumeration is cancelled and an “Unknown Device” is reported to PnP.

If the validation is successful, the Configuration Descriptor is cached and the hub driver moves on to the “MS OS Descriptor Query” state. 

MS OS Descriptor Query

Microsoft has defined a set of vendor specific USB descriptors called Microsoft OS Feature Descriptors, which are queried for at the time of device enumeration. 

If the USB Device Descriptor’s bcdUSB field is equal to 0x0100 or 0x0110, the hub driver will skip the query for the MS OS Descriptor and move to the “Serial Number String Descriptor Query” state.

If the hub driver has never before enumerated a device with the same VID/PID/Revision as the device being enumerated, it will query the device for the MS OS String Descriptor (GET_DESCRIPTOR for Descriptor Type STRING), which uses index 0xEE.  It will specify a language ID of 0x00.

If the device returns an MS OS Descriptor, the hub driver will validate the descriptor as follows:

  • The MicrosoftString field must be equal to “MSFT100”.

Once validated, the value in the descriptor’s bVendorCode field will be stored in the registry on a per VID/PID/Revision basis, under the USBFLAGS registry subkey in the “osvc” registry value.  Subsequent enumerations of any device with the same VID/PID/Revision will read the bVendorCode from this registry value rather than querying the device.
The hub driver will then move to the “Serial Number String Descriptor Query” state. 

Serial Number String Descriptor Query

If the USB Device Descriptor reports a non-zero serial number string index, the hub driver will query for the serial number string descriptor (GET_DESCRIPTOR for Descriptor Type STRING) using the American English language ID (0x409) and the serial number string index.  
The hub driver performs the following validation on all string descriptors:

  • The number of bytes returned for the request must be greater than or equal to the bLength field.
  • The number of bytes returned for the request must be greater than or equal to the bLength field.
  • The bLength field must be larger than 2 bytes.
  • The bDescriptorType field must be equal to descriptor type STRING (3).
  • The bLength field must be an even number, as the string is a Unicode string.

If this validation passes, the hub driver performs the following validation specifically for the serial number string descriptor:

  • The string is non-NULL.
  • The string is not longer than 255 bytes.
  • The descriptor type returned in the descriptor is type STRING.
  • The number of bytes in the string is an even number, as it’s a Unicode string.
  • The string does not contain any invalid characters:
    • The character must have a value greater than or equal to 0x20.
    • The character must have a value less than or equal to 0x7F.
    • The character must not be a comma (‘,’=0x2C).

If any of the above validation fails the serial number will be discarded, otherwise it will be cached.

Regardless of the results of the serial number query and validation, the hub driver will move on to the “MS OS Extended Configuration Descriptor Request” state if the device supports the MS OS Descriptor, otherwise it will move to the “Language ID Query”. 

MS OS Extended Configuration Descriptor Request

If the device is not a composite device the hub driver will issue a request for the MS OS Extended Configuration Descriptor.

Software will issue an initial request for this descriptor by specifying a data size equal to the descriptor’s header.  This will be used to determine the existence of the descriptor, and to determine its size.  If the request is successful the hub driver will validate the header as follows:

  • The returned number of bytes must be equal to the defined size of the header.
  • The binary-coded decimal header version field must be 1.00.
  • The header’s wIndex field must be set to 4.
  • The header’s bCount field must be non-zero.
  • The header’s dwLength field must be equal to the size of the header, plus the value of the bCount field times the size of the configuration descriptor’s function structure.

Once the descriptor header is validated, the hub driver will re-issue the request for the descriptor using the size of the entire descriptor as returned by the device in the descriptor’s header.

If a device successfully returns an MS OS Extended Configuration Descriptor, the hub driver will validate the descriptor as follows:

  • The standard USB configuration descriptor will be parsed for functions described by Interface Association Descriptors and single-interface functions to determine the total number of functions on the device.
  • The following validation will be performed on the MS OS Extended Configuration Descriptor Header:
    • The header’s dwLength value must be greater than or equal to the size of the header structure.
    • The header’s dwLength value must be less than or equal to the size of the header plus 256 Extended Configuration Descriptor functions.
    • The header’s dwLength value must be less than or equal to the number of bytes returned by the device for the descriptor.
    • The header’s wIndex value must equal 4, the MS Extended Configuration Descriptor Index.
    • The header’s bCount value must be less than or equal to the number of functions that were found in the USB Configuration Descriptor.
    • The header’s dwLength value must be greater than or equal to the size of the header structure plus the header’s bCount times the size of the Extended Configuration Descriptor Function structure.
  • The following validation will be performed on each function descriptor:
    • bFirstInterfaceNumber is less than or equal to 256.
    • bFirstInterfaceNumber corresponds to the first interface of a function described by an Interface Association Descriptor or a single-interface function in the device’s USB Configuration Descriptor.
    • The CompatibleID string only contains upper-case ASCII characters(“A”-“Z”), numbers(“0”-“9”), and/or  the underscore character (“_”).
    • The SubCompatibleID string only contains upper-case ASCII characters(“A”-“Z”), numbers(“0”-“9”), and/or  the underscore character (“_”).
    • The number of function descriptors must be equal to the bCount value in the header.

Software will move to the “MS OS Container ID Descriptor Query” state. 

MS OS Container ID Descriptor Query

Windows 7 introduces the concept of a Container ID, which is used to group all functions that are part of a physical device.  For details on how USB generates Container IDs, please refer to this whitepaper.

If the VID/PID/Revision of the device has been previously flagged as not supporting the Container ID Descriptor via the registry, the hub driver will move to the “Language ID Query” state.

If neither the hub descriptor nor the ACPI namespace describes the device as non-removable, and the device supports the MS OS Descriptor, the USB stack will query the device for the MS OS Container ID, otherwise the hub driver will move to the “Language ID Query” state.

A device must indicate support for the container ID by setting bit 1 of the bFlags flag of the MS OS Descriptor.  If this bit is set, software will issue a request for the MS OS Container ID Descriptor header.  The setup packet will specify a wIndex of 6, and the Device as the recipient.  If a descriptor is successfully returned, the hub driver will validate the descriptor as follows:

  • The returned number of bytes must equal the size of the MS OS Container ID Descriptor header.
  • The MS OS Container ID Descriptor header’s bcdVersion must be 0x100.
  • The MS OS Container ID Descriptor header’s wIndex must be 6.
  • The MS OS Container ID Descriptor header’s dwLength must be equal to the size of a MS OS Container ID Descriptor.

If the header is successfully validated, software will issue a request for the entire MS OS Container ID Descriptor.  The setup packet will again specify a wIndex of 6, and the Device as the recipient.

If the descriptor is successfully returned, the hub driver will validate the descriptor as follows:

  • The returned number of bytes is equal to the defined size of the MS OS Container ID Descriptor
  • The Container ID is not all zeros.

If the request for the Container ID Descriptor fails for any reason, a registry value will be set to indicate the hub driver should skip this request in future enumerations of devices with the same VID/PID/Revision.  The hub driver will then disable the port and retry enumeration by returning to the “First Port Reset” state.  If the hub driver has already retried enumeration 3 times, enumeration is cancelled and an “Unknown Device” is reported to PnP. 

Language ID Query

The USB hub driver will query the device for the array of supported language IDs.  The hub driver will issue a query for the string descriptor at index 0, which is an array of 2-byte LANGID codes supported by the device, as defined in section 9.6.7 of the USB 2.0 specification.

The hub driver will perform standard string descriptor validation on the Language ID string descriptor, as described previously in the description of the “Serial Number String Descriptor Query” state.  If the string is found to be valid, the hub driver will cache the data returned in the string.  It will then move to the “Product ID String Query” state. 

Product ID String Query

If the USB Device Descriptor’s iProduct field is non zero the hub driver will issue a request for the Product ID string using the string index specified in the iProduct field and the English language ID of 0x409.

If the string descriptor request completes successfully, the hub driver will perform standard string descriptor validation on the Product ID string descriptor.  If the validation is successful, the hub driver will cache the string. It will then move to the “Device Qualifier Descriptor Query” state. 

Device Qualifier Descriptor Query

If the device is attached to a USB 1.1 hub, is operating at Full-Speed, and its USB Device Descriptor bcdUSB field is greater than or equal to 0x200, the hub driver will issue a GET_DESCRIPTOR for descriptor type DEVICE_QUALIFIER (6). 

The successful completion of the request indicates the device can support USB 2.0 high-speed operation.   

Duplicate Device Detection

The USB driver stack must deal with an artifact of the EHCI companion controller design where a device can move quickly between the USB 2.0 EHCI host controller and a companion USB 1.1 controller when the USB 2.0 controller is enabled or disabled.  This creates a scenario where the new instance of a USB device can be reported to the PnP Manager before its previous instance on the other host controller had been reported as removed to PnP Manager.  This results in a bugcheck if the device has a serial number, as PnP Manager would see two device nodes reporting the same unique instance IDs.  This behavior can also occur when a USB device is moved to a different port while the system is suspended.

The USB hub driver maintains a list of all USB devices currently attached to the system and have been reported to the PnP Manager. If the currently enumerating device has a serial number, software will search this list for any device that has the same vendor ID, product ID, revision number, and serial number.  If no matching device is found, the hub driver will move to the “Report New Device To PnP Manager” state.

If a matching device is found the hub driver will handle it based the following logic:

  1. If the matching device and enumerating device are on the same port, it is a case where the device quickly detached/re-attached (possible a spurious connect change).  No action is taken, as the previous instance will be reported as removed at the same time the new instance is reported as arrived to PnP Manager.
  2. If the matching device is detected as being no longer physically present, software will delay 5 seconds, waiting for the matching device to be reported to PnP Manager as removed.  If the matching device is not reported as removed at the end of the delay, the port is disabled and enumeration is retried by returning to the “First Port Reset” state.  If software has already retried enumeration 3 times, the port is disabled and enumeration is cancelled.  No device will be reported to PnP Manager.
  3. If the matching device is still physically present, it is a case of two identical devices with the same serial number.  In this case the serial number for newly enumerated device will be discarded, to prevent PnP Manager from triggering a bugcheck.

Report New Device to PnP Manager

At this point the USB device has been successfully enumerated and the hub driver will report the new device to PnP.  This involves calling IoInvalidateDeviceRelations and reporting then new device when handling IRP_MN_QUERY_DEVICE_RELATIONS/BusRelations.