Working with USB Pipes
The framework represents each pipe in a USB interface as a framework USB pipe object. When a driver configures a USB device, the framework creates a framework USB pipe object for each pipe in each selected interface. Pipe object methods enable a driver to perform the following operations:
Obtaining Pipe Information
After calling WdfUsbInterfaceGetConfiguredPipe to obtain a handle to a framework USB pipe object, your driver can call the following methods that the USB pipe object defines for obtaining information about the USB pipe:
- WdfUsbTargetPipeGetIoTarget
-
Returns a handle to the I/O target object that is associated with a USB pipe. The driver can pass this handle to WdfRequestSend.
- WdfUsbTargetPipeGetInformation
-
Retrieves information about a USB pipe and its endpoint.
- WdfUsbTargetPipeGetType
-
Returns the type of a USB pipe.
- WdfUsbTargetPipeIsInEndpoint
-
Determines whether a USB pipe is connected to an input endpoint.
- WdfUsbTargetPipeIsOutEndpoint
-
Determines whether a USB pipe is connected to an output endpoint.
- WDF_USB_PIPE_DIRECTION_IN
-
Determines whether a USB endpoint is an input endpoint.
- WDF_USB_PIPE_DIRECTION_OUT
-
Determines whether a USB endpoint is an output endpoint.
For related information, see How to enumerate USB pipes.
Reading from a Pipe
To read data from a USB input pipe, your driver can use any (or all) of the following three techniques:
-
Read data synchronously
To read data synchronously from a USB input pipe, your driver can call the WdfUsbTargetPipeReadSynchronously method. This method builds and sends a read request, and it returns after the I/O operation has completed.
-
Read data asynchronously
To read data asynchronously from a USB input pipe, your driver can call the WdfUsbTargetPipeFormatRequestForRead method to build a read request. Then the driver can call WdfRequestSend to send the request asynchronously (or synchronously).
-
Read data asynchronously and continuously
A continuous reader is a framework-supplied mechanism for ensuring that a read request is always available to a USB pipe. This mechanism guarantees that the driver will always be ready to receive data from a device that provides an asynchronous, unsolicited input stream. For example, a driver for a network interface card (NIC) might use a continuous reader to receive input data.
To configure a continuous reader for an input pipe, the driver's EvtDevicePrepareHardware callback function must call the WdfUsbTargetPipeConfigContinuousReader method. This method queues a set of read requests to the device's I/O target.
Also, the driver's EvtDeviceD0Entry callback function must call WdfIoTargetStart to start the continuous reader and the driver's EvtDeviceD0Exit callback function must call WdfIoTargetStop to stop the continuous reader.
Each time that data is available from the device, the I/O target will complete a read request and the framework will call one of two callback functions: EvtUsbTargetPipeReadComplete, if the I/O target successfully read the data, or EvtUsbTargetPipeReadersFailed, if the I/O target reports an error.
If you do not supply the optional EvtUsbTargetPipeReadersFailed callback, the framework responds to a failed read attempt by sending another read request. Therefore if the bus is in a state where it is not accepting reads, the framework continually sends new requests to recover from a failed read.
After a driver has called WdfUsbTargetPipeConfigContinuousReader, the driver cannot use WdfUsbTargetPipeReadSynchronously or WdfRequestSend to send I/O requests to the pipe unless the driver's EvtUsbTargetPipeReadersFailed callback function is called and returns FALSE.
By default, the framework reports an error if your driver specifies a read buffer that is not a multiple of the pipe's maximum packet size. Your driver can call WdfUsbTargetPipeSetNoMaximumPacketSizeCheck to disable this test of read buffer sizes.
For related information, see:
- How to send USB bulk transfer requests
- How to transfer data to USB isochronous endpoints
- How to use the continuous reader for reading data from a USB pipe
Writing to a Pipe
To write data to a USB output pipe, your driver can use one (or both) of the following techniques:
-
Write data synchronously
To write data synchronously to a USB output pipe, your driver can call the WdfUsbTargetPipeWriteSynchronously method. This method builds and sends a write request, and it returns after the I/O operation has completed.
-
Write data asynchronously
To write data asynchronously to a USB input pipe, your driver can call the WdfUsbTargetPipeFormatRequestForWrite method to build a write request. Then the driver can call WdfRequestSend to send the request asynchronously.
For related information, see How to send USB bulk transfer requests.
Stopping and Resetting a Pipe
Your driver can call the following methods to stop or reset a USB pipe:
- WdfUsbTargetPipeAbortSynchronously
-
Synchronously sends a request to stop a USB pipe.
- WdfUsbTargetPipeFormatRequestForAbort
-
Formats a request to stop a USB pipe. The driver can call WdfRequestSend to send the request synchronously or asynchronously.
- WdfUsbTargetPipeResetSynchronously
-
Synchronously sends a request to reset a USB pipe.
- WdfUsbTargetPipeFormatRequestForReset
-
Formats a request to reset a USB pipe. The driver must call WdfRequestSend to send the request synchronously or asynchronously.
If your driver's USB target completes an I/O request with an error status value, your driver should do the following:
-
Stop the pipe, and cancel any additional I/O requests that the driver has sent to the USB target, if the target has not completed the requests.
Call WdfIoTargetStop with the WdfIoTargetCancelSentIo flag set.
-
Synchronously send an abort request to the pipe.
Call WdfUsbTargetPipeAbortSynchronously, or call WdfUsbTargetPipeFormatRequestForAbort followed by WdfRequestSend with the WDF_REQUEST_SEND_OPTION_SYNCHRONOUS flag set.
-
Synchronously send a reset request to the pipe.
Call WdfUsbTargetPipeResetSynchronously, or call WdfUsbTargetPipeFormatRequestForReset followed by WdfRequestSend with the WDF_REQUEST_SEND_OPTION_SYNCHRONOUS flag set.
-
Restart the pipe.
Call WdfIoTargetStart.
-
Resend the I/O request that failed, and all I/O requests that followed the failed request.
After a significant number of multiple failures, the driver should attempt to reset the USB port by doing the following:
-
Stop all active pipes, and cancel any additional I/O requests that the driver has sent to each pipe's USB target, if the target has not completed them.
For each active pipe, call WdfIoTargetStop with the WdfIoTargetCancelSentIo flag set.
-
Synchronously send a request to reset the USB port.
-
Restart the pipes.
Call WdfIoTargetStart for each pipe that the driver stopped.
-
Resend the last I/O request that failed, and all I/O requests that followed the failed request.
For related information, see How to recover from USB pipe errors.
Sending an URB to a Pipe
If your KMDF driver communicates with a USB pipe by sending I/O requests that contain URBs, the driver can call the following methods:
- WdfUsbTargetPipeSendUrbSynchronously (KMDF only)
-
Synchronously sends an I/O request that contains a URB.
- WdfUsbTargetPipeFormatRequestForUrb (KMDF only)
-
Formats an I/O request that contains a URB. The driver can call WdfRequestSend to send the request synchronously or asynchronously.
- WdfUsbTargetPipeWdmGetPipeHandle (KMDF only)
-
Returns a device's USBD pipe handle. Some URBs require this handle.