Figure 1 shows the path that an I/O request takes from an application to a UMDF driver.
Callers
use the Win32 API to send I/O requests to devices that are managed by
UMDF drivers, just as they do for any other device. The Win32 API calls
the appropriate kernel mode I/O routine, and the Windows kernel I/O
manager creates an I/O request packet (IRP) and sends this IRP to the
driver at the top of the kernel mode device stack for the target device.
For a device that is managed by a UMDF driver, the reflector is the
driver at the top of the kernel mode device stack.
The reflector creates three types of device objects:
up device object—This
is at the top of the kernel mode stack for the device and thus is the
reflector’s target for IRPs from the I/O manager. When an IRP arrives,
the reflector uses interprocess communications to forward it to the User
Mode Driver host process, in which the framework and driver run. UMDF
interprets the request and calls methods in the driver’s event callback
interfaces to handle it. The reflector creates an up device object for
each device stack in which it participates.
down
device object—This is the reflector’s target for I/O requests that
originate in the User Mode Driver and is therefore the default I/O
target for the bottom driver in the user mode device stack. A User Mode
Driver might issue an I/O request to perform a device I/O control
operation on its own device or to retrieve data from another device to
complete one of its own requests. The reflector creates a down device
object for each stack in which it participates.
control
device object—This manages I/O requests to and from the driver manager,
which are not part of the normal I/O flow to the driver. Instead, the
control device object enables “sideband” I/O between the reflector and
the driver manager, which is independent of the normal flow of I/O,
power management, and Plug and Play requests. The reflector creates only
one control device object per system.
In addition to creating
IRPs to represent user I/O requests, the I/O manager sends IRPs to
notify drivers of Plug and Play requests, power management requests,
change to device status, and queries about device and driver resources
(among other purposes). Therefore, the UMDF I/O mode encompasses both
I/O request dispatching and Plug and Play and power notification. The following sections will describe these aspects of the model in more detail.
1. I/O Request Dispatching
UMDF dispatches I/O
requests to the driver, manages I/O cancellation and completion, and
ensures that the Plug and Play and power state of the device is
compatible with performing device I/O. Depending on the type of I/O
request, the UMDF either queues the request or invokes a method in a
callback interface.
UMDF provides
configurable I/O queue objects that a driver can instantiate. The driver
specifies which types of requests to place in each queue and how to
dispatch those requests. Each queue can hold one or more types of
requests.
UMDF queues and
dispatches requests according to the driver’s specifications:
sequentially (one at a time), in parallel (as soon as they arrive), or
manually (at the driver’s explicit request). If Plug and Play or power
management events affect queuing, a UMDF can start, stop, or resume
queuing as appropriate, depending on how the driver configured the
queue.
The driver provides
callback interfaces to handle I/O requests on its queues. To dispatch a
request, UMDF calls a method in the corresponding callback interface.
For example, to dispatch a read request, UMDF calls the OnRead method of the driver’s IQueueCallbackRead interface. The driver can implement request type-specific interfaces (such as IQueueCallbackRead); it can also optionally implement the default I/O callback interface IQueueCallbackDefaultIoHandler
that UMDF calls when it receives a create, read, write, or device I/O
control request for which the driver has not implemented any other
interface.
2. Create, Cleanup, and Close Requests
UMDF can call the driver
to handle create, cleanup, and close requests or can automatically
forward them to the default I/O target (typically, the next lower driver
in the driver stack). The framework queues create requests if the
driver configures a queue accordingly. It does not queue cleanup or
close requests. Table 1 shows the interfaces and methods that a driver must implement to support, cleanup, and close requests.
Table 1. Supporting Create, Cleanup, and Close Requests
Request Type | Interface | Method |
---|
Create | IQueueCallbackCreate | OnCreateFile |
Cleanup | IFileCallbackCleanup | OnCleanupFile |
Close | IFileCallbackClose | OnCloseFile |
Automatic forwarding is
useful for drivers that process some types of I/O requests but not
others. For example, a filter driver might inspect the data that is
being written to a file but might not look at create, cleanup, or close
requests. Therefore, it would have a callback interface for write
request but would enable automatic forwarding for create, cleanup, and
close.
A driver configures automatic forwarding by calling the AutoForwardCreateCleanupClose method on the IWDFDeviceInitialize
interface before it creates the device object. This method sets a flag
that indicates whether the framework should forward these requests. Its
only parameter is one of three enumerators:
WdfDefault
indicates that the framework should use its defaults for forwarding.
The defaults differ for filter and function drivers, as the following
sections describe.
WdfTrue indicates that the framework should forward requests to the default I/O target.
WdfFalse
indicates that the framework should not forward any create, cleanup, or
close requests. If the driver does not implement the required
interfaces to handle such requests, the framework fails the request.
In addition to the setting of the AutoForwardCreateCleanupClose flag, whether the framework dispatches, forwards, or completes create, cleanup, and close requests depends on the following:
Whether this is a filter driver or a function driver.
Whether the driver implements the callback interface for the request type.
For create request only, whether the driver configures a queue for the requests.
The following sections describe what the framework does with such requests for each type of driver.
2.1. Create, Cleanup, and Close in a Filter Driver
The driver calls IWDFDeviceInitialize::AutoForwardCreateCleanupClose and sets WdfDefault. A UMDF driver identifies itself as a filter driver by calling the IWDFDeviceInitialize::SetFilter method.
UMDF forwards cleanup and
close requests for filter drivers to the default I/O target. If a filter
driver does not implement the IQueueCallbackCreate::OnCreateFile method, UMDF forwards create requests, too. However, if the filter driver implements OnCreateFile, UMDF by default calls this method when a create request arrives. OnCreateFile should perform whatever filtering tasks are required and then, if appropriate, forward the request to the default I/O target.
If the filter driver sets WdfTrue in the call to IWDFDeviceInitialize::AutoForwardCreateCleanupClose, UMDF forwards create requests unless the driver implements OnCreateFile. If the filter driver sets WdfFalse, UMDF calls the corresponding method if the driver implements it; otherwise, UMDF fails the request.
If the filter driver completes a create request for a file object, it should set AutoForwardCreateCleanupClose to WdfFalse so that UMDF completes cleanup and close requests for the file object instead of forwarding them.
2.2. Create, Cleanup, and Close in a Function Driver
In a function driver, if a create request arrives for which the driver has neither implemented the OnCreateFile
method nor configured a queue to receive create request, UMDF opens a
file object to represent the device and completes the request with S_OK.
Therefore, any function driver that does not accept create or open
requests from user mode applications—and thus does not register a device
interface—must implement an IQueueCallbackCreate::OnCreateFile
method that explicitly fails such requests. Supplying a method to fail
create requests ensures that a rogue application cannot gain access to
the device.
To handle file cleanup and close requests, a driver implements the IFileCallbackCleanup and IFileCallbackClose
interfaces. If a function driver does not implement such interfaces,
UMDF closes the file object and completes the request with S_OK.
3. Create, Read, Write, and Device I/O Control Requests
For
read, write, and device I/O control requests, the driver creates one or
more queues and configures each queue to receive one or more types of
I/O requests. For create requests, the driver can configure automatic
forwarding, as described in the preceding section, or can direct the
requests to a queue.
When such a request arrives, the I/O request handler:
Determines whether
the driver has created a queue that handles this type of request (either
by explicitly configuring the queue for the request type or by creating
a default I/O queue) or has implemented a default I/O handler. If
neither is true, the handler fails a read, write, or device I/O control
request if this is a function driver. If this is a filter driver, the
handler forwards the request to the default I/O target.
Determines
whether the queue is accepting requests and the device is powered on
(in the DO state). If both are true, the handler creates an I/O request
object to represent the request and adds it to the queue. If the queue
is not accepting requests, the handler fails the request.
Notifies the Plug and Play handler to power up the device if the queue is power managed and the device is not in the DO state.
Queues the request.
Figure 2 summarizes the flow of a create, read, write, or device I/O control request through the framework to the driver.