The Windows kernel is not designed to interact with devices by itself. It depends on device drivers to detect attached devices, mediate communication between the device and the Windows kernel, and expose the device's capabilities to clients such as applications. Windows provides an abstract device support interface called a driver model. The job of driver developers is to provide an implementation of that interface to support the specific requirements of their device.
In more practical terms, the usual purpose of a driver is to handle communication between applications and a device. In many ways, drivers operate much like services. For example, drivers:
Run in the background, separate from application processes, and can be accessed by multiple users.
Drivers have the same lifetime as their devices. The driver starts up when Windows discovers the device and shuts down when the device is removed.
Respond to externally generated I/O requests.
These requests are typically generated by applications, by Windows, or sometimes by other drivers.
Do not have a user interface.
Users typically interact with a driver by directing an application to generate an I/O request.
Run in an address space that is different from that of the application that generates an I/O request.
Drivers are different from services in several important ways. They:
Communicate with core Windows services and devices through their own specialized programming interface, called the DDI.
Are based on the Windows I/O model, which is distinctly different from the models that services or applications use.
Can communicate directly with kernel-mode components.
Kernel-mode drivers run entirely in kernel mode. However, even UMDF drivers must communicate with underlying kernel-mode drivers to transfer data to or from a device.
This chapter provides a conceptual description of how drivers fit into the Windows operating system and how they manage the flow of requests between clients and devices. Although this book is about WDF, the focus of this chapter is primarily the older WDM, which is based on a DDI exposed directly by the Windows kernel. WDM provides great flexibility, but software developers have found implementing drivers with WDM to be a challenging task. However, it is important to have at least a basic understanding of WDM:
WDF is designed to supersede WDM as the primary Windows driver model by providing an abstraction layer over WDM; WDM still operates in the background. To understand WDF, you must understand some basic WDM concepts.
At a conceptual level, WDF and WDM drivers have a similar structure and handle I/O requests in much the same way. Most of the discussion in this chapter applies to both WDM and WDF drivers, even if the implementation details are different.
The chapter focuses on kernel-mode drivers and programming techniques, because all driver developers should have a basic understanding of kernel-mode concepts. Developers interested in user-mode drivers will still benefit from understanding the fundamentals of kernel-mode concepts. For example, the structure of a UMDF driver is similar to that of a WDM or KMDF driver, and UMDF drivers handle I/O requests in much the same way as kernel-mode drivers.
To understand how drivers work, the best place to start is to examine how drivers fit into the core Windows operating system. The Windows system architecture consists of a number of layers, with applications at the top and hardware at the bottom. Figure 2-1 is a simplified architectural diagram of Windows, showing how drivers integrate into the overall architecture. The key components are described following this diagram.
Figure 2-1: Windows core operating system architecture
Applications typically initiate I/O requests. However, applications and services run in user mode and have no direct access to kernel-mode components. Instead, they issue I/O requests by calling the appropriate Windows API function, which in turn uses Windows components such as Ntdll.dll to pass the request to the appropriate kernel-mode component.
Windows is divided into two distinctly different operating modes: user mode and kernel mode. Applications run in user mode, whereas the core operating system (the kernel)-which includes all kernel-mode drivers-runs in kernel mode. The two modes have distinctly different capabilities and associated risks:
User-mode programs are not trusted by the Windows core operating system. They run in a restricted environment that prevents them from compromising other applications or the core operating system.
Kernel-mode programs-including drivers-are trusted components of the Windows core operating system. They operate with relatively few restrictions and some corresponding risks.
The user mode/kernel mode boundary works somewhat like a one-way mirror. Applications can only communicate with the kernel indirectly, through the Windows API. When an application issues an I/O request, it cannot "see" into the kernel to pass the request directly to a kernel-mode driver. Kernel-mode drivers can "see" into user mode and pass data directly to an application. However, this approach involves security risks and is used only for limited purposes. Typically, when a kernel-mode driver returns data to an application, it does so indirectly, through one of the kernel subsystems.
Kernel subsystems handle much of the core Windows functionality. They expose the DDI routines that allow drivers to interact with the system. Drivers interact most frequently with the following subsystems:
I/O manager Facilitates communication between applications and devices. The I/O manager receives I/O requests from user mode and passes them to the appropriate driver. It also receives completed I/O requests from the driver and passes any data back to user mode and, ultimately, to the application that issued the request.
PnP manager Handles Plug and Play tasks such as enumerating the devices attached to a bus, constructing device stacks for every device, and handling the process of adding or removing devices while the system is running.
Power manager Handles changes in the computer's power state such as transitioning between the fully-powered working state and hibernation.
Other kernel subsystems that expose DDI routines include the memory manager and the process and thread manager. Although the kernel subsystems are separate components, complex dependencies exist between them. For example, the PnP manager's behavior depends on the power state and vice versa.
Drivers provide an interface between their devices and the kernel subsystems. The kernel subsystems send I/O requests to the drivers, which process the request and communicate with the devices as required. Any data that the driver obtains from a device is typically passed back to the kernel subsystem that generated the request. Drivers also respond to requests from kernel subsystems such as the PnP manager or power manager to handle tasks such as preparing a device for removal or for hibernation.
Info See Microsoft Windows Internals, 4th edition, by Solomon and Russinovich, for a thorough discussion of Windows architecture (ISBN 0-7356-1917-4)-available at http://www.microsoft.com/MSPress/books/6710.aspx.
The Windows driver architecture is designed to support four key features:
Modular layered architecture Devices can be serviced by several drivers, which are organized in a stack.
Packet-based I/O All requests are handled with packets, which are passed between the system and the driver, and from one driver to another in the stack.
Asynchronous I/O Drivers can handle requests without waiting for the request to be completed.
Dynamic loading and unloading The lifetime of a driver is tied to the lifetime of its device. Drivers are not unloaded until all associated devices have been removed.
When a kernel subsystem sends an I/O request to a device, one or more drivers process the request. Each driver has an associated device object to represent the driver's participation in the processing of I/O requests for that device. The device object is a data structure that includes pointers to the driver's dispatch functions, which allow the I/O manager to communicate with the driver.
The device objects are arranged in a device stack, with a separate stack for each device. Typically, "device stack" refers to the stack of device objects, plus the associated drivers. However, a device stack is associated with a single device, whereas a set of drivers can service multiple device stacks. The set of drivers is sometimes referred to as a "driver stack."
The example in Figure 2-2 shows two devices-each with its own device stack-that are serviced by a single set of drivers.
Figure 2-2: The device stack
A device stack is constructed from the following components:
Bus driver and physical device object The bottom of the stack is a physical device object (PDO), which is associated with a bus driver. Devices are usually attached to a standard hardware bus such as PCI or USB. A bus driver typically manages several pieces of hardware that are attached to the physical bus.
For example, when the bus driver is installed, it enumerates the devices attached to the bus and requests resources for those devices. The PnP manager uses that information to assign resources to each device. Each device has its own PDO. The PnP manager identifies the drivers for each device and constructs an appropriate device stack on top of each PDO.
Function driver and functional device object The core of the device stack is the functional device object (FDO), which is associated with a function driver. The function driver translates the Windows abstraction of a device into the actual commands that are required to transfer data to and from a real device. It provides an "upper edge"-also called a device interface-for applications and services to interact with and usually controls how the device responds to changes in Plug and Play or power state. The function driver's "lower edge" handles communication with the device or other drivers such as a lower filter driver or the bus driver.
Filter drivers and filter device objects Device stacks can have multiple filter device objects (filter DOs), which can be placed above or below the FDO. Each filter DO is associated with a filter driver. Filter drivers are optional, but often present. They are the typical way by which third-party vendors can add value to a device stack. The usual purpose of a filter driver is to modify some of the I/O requests as they pass through the device stack, much like an audio filter modifies an audio stream.
For example, filter drivers can be used to encrypt or decrypt read and write requests. Filter drivers can also be used for purposes that do not require modification of I/O requests, such as tracking requests and reporting the information back to a monitoring application.
The three types of device objects differ in detail, but they work in much the same way to allow the system to process I/O requests. See "Kernel Objects and Data Structures" and "The Windows I/O Model" later in this chapter for a discussion about how a device stack handles I/O requests.
Systems usually have several levels of buses. The lowest level bus can have one or more buses attached to it, as well as stand-alone devices. Those buses can, in turn, have buses and standalone devices attached to them, and so on. Most bus drivers therefore must serve two roles: as a function driver for the underlying bus and as an enumerator and manager for any attached devices.
During system startup, the PnP manager starts with the lowest level bus and does the following:
Loads the bus driver, which enumerates PDOs for each device attached to its physical bus and requests resources for those devices.
Assigns resources to each device.
Queries its database to determine which driver is associated with each device.
Constructs a device stack on top of each PDO.
Starts each device.
Queries each device for any PDOs that it might enumerate.
Repeats steps 2 through 6, as required.
The result of this process is represented by a hierarchical structure called the device tree. Windows represents each device stack by a node in the tree called a devnode. The PnP manager uses a device's devnode to store configuration information and to track the device. The base of the tree is an abstract device called the root device.
Figure 2-3 shows a simple device tree.
Figure 2-3: Sample Plug and Play device tree