A USB client driver sends in and out transfers to its device. At times transfers to/from the device fail and the client driver needs to recover from the error condition. Failure may be due to a legitimate error response from the USB device (e.g. transfer failed with status USBD_STATUS_STALL_PID) or it may be unexpected (e.g. transfer failed with status USBD_STATUS_XACT_ERROR).
Various articles do talk about the recovery process, however they are not comprehensive. Thus it can be confusing for a client driver writer. Some examples are:
In this blog series, I will provide clear guidelines on the steps a client driver should take to recover from USB transfer errors. This blog will give you an overview of the recovery process, and the future ones will describe the recovery steps needed by a WDM client driver, a WDF client driver and a WinUSB driver.
The Recovery Process:
When a transfer to/from the device fails, the corresponding pipe transitions into halted state. No further transfers for that pipe are processed until the error condition on the pipe is cleared. A client driver can do the following types of error recovery:
(In case the error is due to device having disconnected, client driver should not invoke the following error recovery)
- Reset pipe: clears the error condition on the pipe on which the error occurred
- Reset port: resets the USB device thereby bringing it to a clean state
- Cycle Port: resets the USB device and in addition performs PNP re-enumeration of the client driver stack
Usually it is a good idea to do a reset pipe first, and if the problem persists do the reset port or cycle port.
Note that some of the operations mentioned below can only be executed at PASSIVE LEVEL, thus your driver may need to queue a work item.
Reset pipe (for a non-default pipe)
This is the first step that the client driver should do to recover from a USB error. The client driver uses this mechanism if a previous transfer to its non-default pipe failed. Failure can be due to a device error (such as USBD_STATUS_STALL_PID or USBD_STATUS_BABBLE_DETECTED), or due to an error reported by the host controller (such as USBD_STATUS_XACT_ERROR).
To reset the pipe, the client driver must do the following steps:
- Cancel all IO (if any) for the faulting pipe and wait for it to complete. To do this, the client driver would typically perform an abort pipe operation and wait for it to complete. The client driver should not send any new IO for that pipe until the reset pipe operation is complete.
- Request a reset pipe operation.
In response to a reset pipe request, the core USB stack performs the following steps:
- Clears the error condition on the device by sending a CLEAR_FEATURE control transfer request to clear the pipe's ENDPOINT_HALT feature. (Note: This operation is not done for an isochronous pipe because isochronous pipes auto clear this error condition)
- Clears the error condition in the host controller hardware by clearing the halted state of the pipe and by resetting the data toggle of the pipe to 0. These operations only affect the host controller and not the device.
Client driver need not worry about the error conditions on the default control pipe of the device. These errors are cleared automatically.
If even after resetting the pipe a transfer to the pipe still fails, the client driver can request a reset port operation. Reset port is more expensive than resetting the pipe and may result in more state loss.
To reset the port, the client driver must do the following steps:
- Cancel all IO (if any) for the device and wait for it to complete. The client driver should not send any new IO until the reset port operation is complete.
- Request a reset port operation.
In response to a reset port request, the core USB stack performs the following steps:
- Resets the USB device and re-enumerates it on the USB bus.
- Brings it to the same configuration as it was before the reset.
- Ensures that all the client driver's pipe handles remain valid.
Kindly note it is not possible to reset an individual function of a multi-function (composite) device. For a composite device, if the client driver of one function requests a reset operation, the whole device would be reset. This has a potential to affect operation of drivers for the other functions of that device (especially if the USB device that was reset maintained state). Unfortunately there is no current solution for this problem, thus it's important that the client driver first tries to reset the pipe before resetting the port.
Cycle port operation is similar to the device being unplugged and then plugged back in, except that the device is not electrically disconnected. Think of it as software unplug and re-plug. Cycle port may be used if the client driver itself wants to re-rebuild its PNP stack, in addition to resetting the device.
To cycle the port, the client driver must do the following steps:
- Cancel all IO (if any) for the device and wait for it to complete. The client driver should not send any new IO until the cycle port operation is complete.
- Request a cycle port operation.
In response to a cycle port request, core USB stack performs full PNP re-enumeration of the device. It performs the following steps:
- Reports to the PNP that the device has been disconnected. As a result PNP manager tears down (surprise-remove) the client driver stack for the USB device.
- Resets the device, and re-enumerate it on the USB bus.
- Reports to the PNP that a device has been connected. As a result PNP manager re-builds the stack for the USB device.
As a result of cycle port operation any application that has a handle open to the device will get a device removal notification (assuming the application registered for it). In response, the application may report a device disconnected message to the user. Thus a client driver may want to be careful before it chooses to cycle the port.
For a composite device, similar to reset port, cycle port operation would be carried out for the device and thus would affect all functions for the device.
- Pankaj Gupta, USB Developer, Microsoft