Developing Drivers with the Windows Driver Foundation: WDF Fundamentals
- 4/25/2007
I/O Model
When Windows sends an I/O request to a WDF driver, the framework receives the request and handles the mechanics of dispatching, queuing, completing, and canceling requests on behalf of its drivers. When an I/O request arrives, the framework determines whether it should handle the request itself or invoke a callback to let the WDF driver handle the request. If the WDF driver is to handle the request, the framework packages the data into a framework request object and passes the object to the driver.
The framework keeps track of every I/O request. Because the framework is aware of all active requests, it can call the appropriate callbacks when an I/O request is canceled, the system’s power state changes, the device is removed, and so forth.
A WDF driver manages the flow of I/O requests by creating one or more queue objects and configuring each object for:
The type of I/O requests that the queue handles.
How requests are dispatched from the queue.
How power management events affect the queue.
WDF drivers register queue callbacks to receive requests, and the queue object dispatches requests by invoking the appropriate callback. A WDF driver can configure a queue object to dispatch requests in one of the following three ways:
Parallel The queue object pushes requests to the driver as soon as they arrive. More than one request can be active at the same time.
Sequential The queue object pushes requests to the driver, but does not dispatch a new request until the previous request has been completed or forwarded to another queue.
Manual The driver pulls requests from the queue as needed.
Plug and Play and power management events can affect the state of I/O queues. The framework provides integrated Plug and Play and power management support for I/O request queues, and it integrates queuing with request cancellation. A WDF driver can configure a queue so that the framework starts, stops, or resumes queuing as appropriate in response to Plug and Play or power events. WDF drivers can also explicitly start, stop, resume, and purge queues, as required.
I/O Request Cancellation
Because Windows I/O is inherently asynchronous, canceled I/O requests are often difficult to handle correctly. A driver must cope with several potential race conditions that require one or more locks. WDM drivers must manage the necessary locks by themselves, and the required code is typically scattered among several driver routines. The framework provides default handling for I/O request cancellation by managing the locks for the I/O queues and by canceling queued requests without requiring driver intervention. WDF drivers that use the WDF defaults typically require little if any cancellation code.
With WDF, when an I/O request is canceled:
By default, the framework manages cancellation for requests that are in a queue.
Requests that have been removed from a queue and dispatched to a WDF driver cannot be canceled unless the driver has specifically marked them as cancelable.
WDF drivers can specify whether the framework can cancel requests that the driver is actively processing.
This feature allows a WDF driver to easily support cancellation of long-running requests. The framework helps the WDF driver manage the inherent race conditions, and the driver is primarily responsible for the required code to cancel the request.
Chapter 8, discusses I/O.
I/O Targets
WDF drivers must sometimes send I/O requests to other drivers. For example:
WDF drivers can sometimes process all I/O requests themselves, but many drivers must pass at least some of the requests that they receive down the stack for further processing.
WDF drivers must sometimes initiate I/O requests.
For example, function drivers sometimes send device I/O control requests down the stack to get information from the bus driver.
WDF drivers must sometimes send I/O requests to an entirely different device stack.
For example, a driver might require information from another device stack before it can complete a request.
WDF drivers send requests to an I/O target, which is a framework object that represents the driver that is to receive the request. The default I/O target for a WDF driver is the next lower driver in the device stack. However, I/O targets can also represent another driver in the same stack or a driver in an entirely different stack. WDF drivers can send a request to an I/O target synchronously or asynchronously. They can also specify a time-out value for either type of request to limit how long the framework will wait before canceling the request.
I/O target objects support a programming interface that WDF drivers use for purposes such as tracking the state of the target, formatting requests in a target-specific way, obtaining information about the target, and receiving notification if the target is removed. I/O target objects also track queued and sent requests, and they can cancel outstanding requests if changes occur in the state of the target device or the WDF driver that sent the request.
Chapter 9 discusses I/O target objects.
How to Handle Nonfatal Errors
WDF drivers call WDF methods for many different purposes. Many of these function calls can fail:
Sometimes the error is so serious that the driver cannot recover.
Such fatal errors cause UMDF to crash the host process and cause KMDF to issue a bug check.
Some errors are less serious and do not affect device or driver operation to the extent that the driver cannot recover.
In that case, the function reports the nature of the error to the driver by returning an appropriate status value—an HRESULT value for UMDF drivers or an NTSTATUS value for KMDF drivers. The driver can then handle the error as appropriate.
Chapter 22 discusses fatal errors.
You must be scrupulous about checking return values for errors to ensure that they are handled properly. However, only WDF functions that return a status value can fail. All other functions are guaranteed to simply return a value of the appropriate type, although that return value could be NULL in some cases.
Sometimes the WDF driver itself detects errors. However, only those callbacks that return a status value must be concerned with returning errors. In that case, the callback reports nonfatal errors to the framework by returning the appropriate status value.
Reporting UMDF Errors
The HRESULT type supports multiple success and failure codes:
UMDF drivers can check the HRESULT values returned by UMDF methods for an error condition by passing the returned value to the SUCCEEDED or FAILED macro.
If appropriate, the driver can examine the actual error code to determine how to proceed.
UMDF driver callbacks that encounter an error should return the appropriate HRESULT value.
The WDK reference page for each method lists the HRESULT values that the framework is prepared to receive.
Chapter 18 discusses HRESULT values in detail.
Reporting KMDF Errors
The NTSTATUS type also supports multiple success and failure codes:
KMDF drivers can check the NTSTATUS values returned by KMDF functions by passing the returned value to the NT_SUCCESS macro.
If appropriate, examine the actual error code to determine how to proceed.
KMDF driver callbacks that encounter an error should return the appropriate NTSTATUS value.
The WDK reference page for each function lists the NTSTATUS values that the framework is prepared to receive.
The KMDF custom NTSTATUS values are defined in %wdk%\inc\wdf\kmdf\VersionNumber\Wdfstatus.h.