2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
In USB 2.0 and earlier devices, a bulk endpoint can send or receive a single stream of data through the endpoint. In USB 3.0 devices, a bulk endpoint can send and receive multiple streams of data through the endpoint.
The Microsoft-provided USB driver stack in Windows supports multiple streams. This enables the client driver to send independent I/O requests to each stream associated with a bulk endpoint in a USB 3.0 device without serializing requests to different streams.
To the client driver, streams represent multiple logical endpoints with the same set of characteristics. To send a request to a specific stream, the client driver needs a handle to the stream (similar to a pipe handle to an endpoint). The URB for stream I/O requests is similar to the URB for I/O requests to bulk endpoints. The only difference is the pipe handle. To send an I/O request to a stream, the driver specifies the pipe handle in the stream.
During device configuration, the client driver sends a select configuration request and, optionally, a select interface request. These requests retrieve a set of pipe handles for the endpoints defined in the active settings for the interface. For endpoints that support streams, the endpoint pipe handles can be used to send I/O requests to the default stream (the first stream) until the driver opens a stream.
If the client driver wants to send requests to streams other than the default stream, the driver must open and obtain handles to all streams. To do this, the client driver sends an open stream request by specifying the number of streams to open. After the client driver is done using the streams, the driver can choose to close them by sending a close stream request.
The Kernel-Mode Driver Framework (KMDF) does not natively support static streams. Client drivers must use the Windows Driver Model (WDM) to open and close streams. User-Mode Driver Framework (UMDF) client drivers cannot use the static streams feature.
The following may contain some comments labeled WDM Drivers. These notes describe routines for WDM-based USB client drivers that want to send stream requests.
Before a client driver can open or close a stream, the driver must have:
1. Call the WdfUsbTargetDeviceCreateWithParameters method. The method requires the USBD_CLIENT_CONTRACT_VERSION_602 client contract version. By specifying this version, the client driver must follow a set of rules.
Called to retrieve the WDFUSBDEVICE handle of the framework's USB target device object. This handle is required to make subsequent calls on the opened stream. Typically, a client driver registers itself in the driver's EVT_WDF_DEVICE_PREPARE_HARDWARE event callback routine.
WDM drivers: Call the USBD_CreateHandle routine and obtain the USBD handle that the driver registered in the USB driver stack.
2. Configure the device and obtain a WDFUSBPIPE pipe handle for the bulk endpoint that supports streaming. To obtain the pipe handle, call the WdfUsbInterfaceGetConfiguredPipe method on the current alternate settings for the interface in the selected configuration.
WDM driver: Gets the USBD pipe handle by sending a select-configuration or select-interface request.
1. Determine whether the underlying USB driver stack and host controller support the static streams capability by calling the WdfUsbTargetDeviceQueryUsbCapability method. Typically, the client driver calls this routine in the driver's EVT_WDF_DEVICE_PREPARE_HARDWARE event callback routine.
WDM drivers: Call the USBD_QueryUsbCapability routine. Typically, a driver queries for capabilities to use in the driver's start-device routine (IRP_MN_START_DEVICE).
Provide the following information:
WDM Driver: Passes the USBD handle retrieved in the previous call to USBD_CreateHandle.
If the client driver wants to use a specific feature, the driver must first query the underlying USB driver stack to determine if the driver stack and host controller support the feature. If the feature is supported, then and only then should the driver send a request to use the feature. Some requests require a URB, such as the stream features discussed in step 5. For these requests, make sure that you use the same handle to query the feature and to allocate the URB. This is because the driver stack uses the handle to keep track of supported features that the driver can use.
For example, if you obtained a USBD_HANDLE by calling USBD_CreateHandle, query the driver stack by calling USBD_QueryUsbCapability, and allocate a URB by calling USBD_UrbAllocate. Pass the same USBD_HANDLE in both calls.
If you call the KMDF methods, WdfUsbTargetDeviceQueryUsbCapability and WdfUsbTargetDeviceCreateUrb, specify the same WDFUSBDEVICE handle for the framework target object in these method calls.
2. Evaluate the returned NTSTATUS value. If the routine completes successfully, STATUS_SUCCESS is returned, and static stream functionality is supported. Otherwise, the method returns the appropriate error code.
3. Determine the number of streams to open. The maximum number of streams that can be opened is subject to the following restrictions:
To determine the maximum number of streams, choose the smaller of the two values supported by the host controller and the endpoint.
4. Allocate an array of USBD_STREAM_INFORMATION structures containing n elements, where n is the number of streams to be opened. The client driver is responsible for freeing this array after the driver has finished using the streams.
5. Allocate a URB for the open stream request by calling the WdfUsbTargetDeviceCreateUrb method. If the call completes successfully, the method retrieves the address of the WDF memory object and the URB structure allocated by the USB driver stack.
WDM drivers: Call the USBD_UrbAllocate routine.
6. Set the URB format for the open stream request. URB uses the _URB_OPEN_STATIC_STREAMS structure to define the request. To set the format of the URB, you need to:
To format the URB, call UsbBuildOpenStaticStreamsRequest and pass the required information as parameter values. Make sure that the number of streams specified to UsbBuildOpenStaticStreamsRequest does not exceed the maximum number of streams supported.
7. Send the URB as a WDF request object by calling the WdfRequestSend method. To send the request synchronously, call the WdfUsbTargetDeviceSendUrbSynchronously method instead.
WDM driver: Associates a URB with an IRP and submits the IRP to the USB driver stack.
8. After the request is completed, check the status of the request.If a USB driver stack request fails, the URB status contains the relevant error code.
If the status of the request (IRP or WDF request object) indicates USBD_STATUS_SUCCESS, the request completed successfully. Examine the array of USBD_STREAM_INFORMATION structures received upon completion. The array is populated with information about the requested streams. The USB driver stack populates each structure in the array with stream information, such as the handle received by USBD_PIPE_HANDLE, the stream identifier, and the maximum numeric transfer size. The stream is now ready to transfer data.
For an open stream request, a URB and array need to be allocated. After the open stream request is completed, the client driver must release the URB by calling the WdfObjectDelete method on the associated WDF memory object. If the driver sent the request synchronously by calling WdfUsbTargetDeviceSendUrbSynchronously, the WDF memory object must be released after the method returns. If the client driver sent the request asynchronously by calling WdfRequestSend, the driver must release the WDF memory object in the driver-implemented completion routine associated with the request.
You can release the stream array after the client driver has finished using the streams, or store the stream array for an I/O request. In the code example included below, the driver stores the stream array in the device context. The driver releases the device context before releasing the device object.
How to transfer data to a specific stream
To send a data transfer request to a specific stream, a WDF request object is required. Typically, the client driver does not need to allocate a WDF request object. When the I/O manager receives a request from an application, the I/O manager creates an IRP for the request. This IRP is intercepted by the framework. The framework then allocates a WDF request object to represent the IRP. After that, the framework passes the WDF request object to the client driver. The client driver can then associate the request object with a data transfer URB and send it to the USB driver stack.
If the client driver has not received a WDF request object from the framework and wants to send a request asynchronously, the driver must allocate a WDF request object by calling the WdfRequestCreate method, format the new object by calling WdfUsbTargetPipeFormatRequestForUrb, and send the request by calling WdfRequestSend.
In the synchronous case, passing a WDF request object is optional.
To transfer data to a stream, you must use a URB. You must set the format of the URB by calling WdfUsbTargetPipeFormatRequestForUrb.
The stream does not support the following WDF methods:
The following procedure assumes that the client driver receives a request object from the framework.
WDM drivers: Allocate a URB by calling USBD_UrbAllocate and format it for bulk transfers (see _URB_BULK_OR_INTERRUPT_TRANSFER). To format the URB, you can call UsbBuildInterruptOrBulkTransferRequest or manually format the URB structure. Specify the handle to the stream in the UrbBulkOrInterruptTransfer.PipeHandle member of the URB.
The client driver can close streams after the driver has finished using the streams. However, a close stream request is optional. The USB driver stack closes all streams when the endpoint associated with the streams is deconfigured. Endpoints are deconfigured when an alternate configuration or interface is selected, when a device is removed, and so on. If the client driver wants to open a different number of streams, it must close the streams. To send a close stream request:
1. Allocate a URB structure by calling WdfUsbTargetDeviceCreateUrb.
2. Set the URB format for the close stream request. The UrbPipeRequest member of the URB structure is a _URB_PIPE_REQUEST structure. Fill in its members as follows:
3. Send the URB as a WDF request by calling WdfRequestSend or WdfUsbTargetDeviceSendUrbSynchronously.
The close handle request closes all streams previously opened by the client driver. The client driver cannot use the request to close a specific stream in the endpoint.
The USB driver stack performs validation on received URBs. To avoid validation errors:
Sometimes, transfers to or from an endpoint might fail. Such failures might be caused by error conditions on the endpoint or the host controller, such as a stop or halt condition. To clear the error condition, the client driver first cancels the pending transfer and then resets the pipe associated with the endpoint. To cancel the pending transfer, the client driver can send an abort pipe request. To reset the pipe, the client driver must send a reset pipe request.
For stream transfers, abort-pipe and reset-pipe requests are not supported for a single stream associated with a bulk endpoint. If a transfer on a specific stream's pipe fails, the host controller stops transfers on all other pipes for other streams (). To recover from the error condition, the client driver should manually cancel the transfer to each stream. The client driver must then send a reset-pipe request to the bulk endpoint using the pipe handle. For this request, the client driver must specify the endpoint's pipe handle in a _URB_PIPE_REQUEST structure and set the URB function (Hdr.Function) to URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL.
The following code example shows how to open a stream.
- NTSTATUS
- OpenStreams (
- _In_ WDFDEVICE Device,
- _In_ WDFUSBPIPE Pipe)
- {
- NTSTATUS status;
- PDEVICE_CONTEXT deviceContext;
- PPIPE_CONTEXT pipeContext;
- USHORT cStreams = 0;
- USBD_PIPE_HANDLE usbdPipeHandle;
- WDFMEMORY urbMemory = NULL;
- PURB urb = NULL;
-
- PAGED_CODE();
-
- deviceContext =GetDeviceContext(Device);
- pipeContext = GetPipeContext (Pipe);
-
- if (deviceContext->MaxStreamsController == 0)
- {
- TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
- "%!FUNC! Static streams are not supported.");
-
- status = STATUS_NOT_SUPPORTED;
- goto Exit;
- }
-
- // If static streams are not supported, number of streams supported is zero.
-
- if (pipeContext->MaxStreamsSupported == 0)
- {
- status = STATUS_DEVICE_CONFIGURATION_ERROR;
-
- TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
- "%!FUNC! Static streams are not supported by the endpoint.");
-
- goto Exit;
- }
-
- // Determine the number of streams to open.
- // Compare the number of streams supported by the endpoint with the
- // number of streams supported by the host controller, and choose the
- // lesser of the two values. The deviceContext->MaxStreams value was
- // obtained in a previous call to WdfUsbTargetDeviceQueryUsbCapability
- // that determined whether or not static streams is supported and
- // retrieved the maximum number of streams supported by the
- // host controller. The device context stores the values for IN and OUT
- // endpoints.
-
- // Allocate an array of USBD_STREAM_INFORMATION structures to store handles to streams.
- // The number of elements in the array is the number of streams to open.
- // The code snippet stores the array in its device context.
-
- cStreams = min(deviceContext->MaxStreamsController, pipeContext->MaxStreamsSupported);
-
- // Allocate an array of streams associated with the IN bulk endpoint
- // This array is released in CloseStreams.
-
- pipeContext->StreamInfo = (PUSBD_STREAM_INFORMATION) ExAllocatePoolWithTag (
- NonPagedPool,
- sizeof (USBD_STREAM_INFORMATION) * cStreams,
- USBCLIENT_TAG);
-
- if (pipeContext->StreamInfo == NULL)
- {
- status = STATUS_INSUFFICIENT_RESOURCES;
-
- TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
- "%!FUNC! Could not allocate stream information array.");
-
- goto Exit;
- }
-
- RtlZeroMemory (pipeContext->StreamInfo,
- sizeof (USBD_STREAM_INFORMATION) * cStreams);
-
- // Get USBD pipe handle from the WDF target pipe object. The client driver received the
- // endpoint pipe handles during device configuration.
-
- usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle (Pipe);
-
- // Allocate an URB for the open streams request.
- // WdfUsbTargetDeviceCreateUrb returns the address of the
- // newly allocated URB and the WDFMemory object that
- // contains the URB.
-
- status = WdfUsbTargetDeviceCreateUrb (
- deviceContext->UsbDevice,
- NULL,
- &urbMemory,
- &urb);
-
- if (status != STATUS_SUCCESS)
- {
- TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
- "%!FUNC! Could not allocate URB for an open-streams request.");
-
- goto Exit;
- }
-
- // Format the URB for the open-streams request.
- // The UsbBuildOpenStaticStreamsRequest inline function formats the URB by specifying the
- // pipe handle to the entire bulk endpoint, number of streams to open, and the array of stream structures.
-
- UsbBuildOpenStaticStreamsRequest (
- urb,
- usbdPipeHandle,
- (USHORT)cStreams,
- pipeContext->StreamInfo);
-
- // Send the request synchronously.
- // Upon completion, the USB driver stack populates the array of with handles to streams.
-
- status = WdfUsbTargetPipeSendUrbSynchronously (
- Pipe,
- NULL,
- NULL,
- urb);
-
- if (status != STATUS_SUCCESS)
- {
- goto Exit;
- }
-
- Exit:
- if (urbMemory)
- {
- WdfObjectDelete (urbMemory);
- }
-
- return status;
- }