How the System Finds and Loads Drivers
A Plug and Play device has an electronic signature that the system can detect. For Plug and Play devices, a system bus driver detects the existence of the hardware and reads the signature to determine what kind of hardware it is. Thereafter, an automatic process based on the registry and INF files allows the system to load the right driver.
A legacy device does
have any electronic signature, so the system can’t detect it automatically. The end
Whichever method the system uses to detect hardware and load a driver, the driver itself will be a WDM driver that reacts
Device and Driver Layering
Before I can make sense of the hardware detection and driver loading processes, I need to explain the concept of driver layering
In the Windows Driver Model, each hardware device has at least two device drivers. One of these drivers, which we call the function driver , is what you’ve always thought of as being the device driver. It understands all the details about how to make the hardware work. It’s responsible for initiating I/O operations, for handling the interrupts that occur when those operations finish, and for providing a way for the end user to exercise any control over the device that might be appropriate.
A monolithic WDM function driver is a single executable file with dynamic links to NTOSKRNL.EXE, which contains the kernel of the operating system, and HAL.DLL, which contains the hardware abstraction layer (HAL). A function driver can dynamically link to other kernel-mode DLLs too. In situations in which Microsoft has provided a class driver for your type of hardware, your minidriver will dynamically link to the class driver. The combination of minidriver and class driver adds up to a single function driver. You may see pictures in which things called class drivers appear to be above or below a minidriver. I prefer to think of those so-called “class” drivers as free-standing filter drivers and to use the
We call the other of the two drivers that every device has the bus driver . It’s responsible for managing the connection between the hardware and the computer. For example, the bus driver for the Peripheral Component Interconnect (PCI) bus is the software component that actually detects that your card is plugged into a PCI slot and determines the requirements your card has for I/O-mapped or memory-mapped connections with the host. It’s also the software that turns on or off the flow of electrical current to your card’s slot.
Some devices have more than two drivers. We use the generic term
to describe these other drivers. Some filter drivers simply watch as the function driver
Referring once again to Figure 2-2, notice that each of the four drivers shown for a hypothetical device has a connection to one of the DEVICE_OBJECT structures in the left column. The acronyms used in the structures are these:
PDO stands for physical device object. The bus driver uses this object to represent the connection between the device and the bus.
FDO stands for function device object. The function driver uses this object to manage the functionality of the device.
FiDO stands for filter device object. A filter driver uses this object as a place to store the information it needs to keep about the hardware and its filtering activities. (The early beta releases of the Windows 2000 DDK used the term FiDO, and I adopted it then. The DDK no longer uses this term because, I guess, it was
What Is a Bus?I’ve already used the terms bus and bus driver pretty
This is a pretty broad definition. Not only does it include items such as the PCI bus, but it also includes a Small Computer System Interface (SCSI) adapter, a parallel port, a serial port, a USB hub, and so on—anything, in fact, that can have another device plugged into it.
The definition also includes a notional
that exists only as a figment of our
Plug and Play Devices
To repeat what I said earlier, a Plug and Play device is one that has an electronic signature that a bus driver can interrogate to learn the identity of a device. Here are some examples of these signatures:
A PCI card has a configuration space that the PCI bus driver can read via dedicated memory or I/O port addresses. The configuration space contains vendor and product identification information.
A USB device returns a device descriptor in response to a standardized control-pipe transaction. The device descriptor contains vendor and product identification information.
A Personal Computer Memory Card International Association (PCMCIA) device has attribute memory that the PCMCIA bus driver can read in order to determine the identity of the card.
A bus driver for a Plug and Play bus has the ability to enumerate its bus by scanning all possible slots at start-up time. Drivers for buses that support hot plugging of devices during a session (as do USB and PCMCIA) also monitor some
When a bus driver detects the insertion or removal of hardware, it calls IoInvalidateDeviceRelations to notify the PnP Manager that the bus’s population of child devices has changed. To obtain an updated list of the PDOs for the child devices, the PnP Manager sends an IRP to the bus driver. The major function code for this IRP is IRP_MJ_PNP , and the minor function code is IRP_MN_QUERY_DEVICE_RELATIONS , with a code indicating that the PnP Manager is looking for the so-called “bus” relations. This is point 2 in Figure 2-3.
Each IRP has a major and a minor function code. The major function code indicates what sort of request the IRP contains. IRP_MJ_PNP is the major function code for
In response to the bus relations query, the bus driver returns its list of PDOs. The PnP Manager can easily determine which of the PDOs represent devices that it hasn’t yet
The PnP Manager will send another IRP to the bus driver, this time with the minor function code IRP_MN_QUERY_ID . This is point 3 in Figure 2-3. In fact, the PnP Manager sends several such IRPs, each with an operand that instructs the bus driver to return a particular type of identifier. One of the identifiers, the device identifier, uniquely specifies the type of device. A device identifier is just a string, and it might look like one of these examples:
PCI\VEN_102C&DEV_00E0&SUBSYS_00000000 USB\VID_0547&PID_2125&REV_0002 PCMCIA\MEGAHERTZ-CC10BT/2-BF05
Each bus driver has its own scheme for formatting the electronic signature information it gathers into an identifier string. I’ll discuss the identifier strings used by common bus drivers in Chapter 15. That chapter is also the place to look for information about INF files and about where the various registry keys described in the text are in the registry hierarchy and what sorts of information are kept in the keys.
The PnP Manager uses the device identifier to locate a hardware key in the system registry. For the moment, let’s assume that this is the first time your particular device has been plugged into the computer. In that case, there won’t yet be a hardware key for your type of device. This is where the setup subsystem steps in to figure out what software is needed to support your device. (See point 4 in Figure 2-3.)
Installation instructions for all types of hardware exist in files with the extension .INF. Each INF contains one or more model statements that relate particular device identifier strings to install sections within that INF file. Confronted with
When the setup subsystem finds the right model statement, it carries out the instructions you provide in an install section. These instructions probably include copying some files onto the end user’s hard drive, defining a new driver service in the registry, and so on. By the end of the process, the setup program will have created the hardware key in the registry and installed all of the software you provided.
Now step back a few paragraphs and suppose that this was not the first time this particular computer had seen an instance of your hardware. For example, maybe we’re talking about a USB device that the user introduced to the system long ago and that the user is now reattaching to the system. In that case, the PnP Manager would have found the hardware key and would not have needed to invoke the setup program. So the PnP Manager would skip around all the setup processing to point 5 in Figure 2-3.
At this point, the PnP Manager
Next the PnP Manager calls your
Finally the PnP Manager is ready to configure the hardware. It works with a set of resource arbitrators to assign resources to your device. If that can be done—and it usually can be—the PnP Manager sends an IRP_MJ_PNP to your driver with the minor function code IRP_MN_START_DEVICE . Your driver handles this IRP by configuring and connecting various kernel resources, following which your hardware is ready to use.
Windows NT Drivers ContrastedThe process described in the text for how Windows XP (and, indeed, Windows 2000, Windows 95, and all successors of Windows 95) finds and loads drivers requires the driver to be relatively passive. Windows NT 4.0 and before worked quite differently. In those systems, you would have provided some sort of setup program to install your driver. Your setup program would have modified the registry to cause your driver to be loaded during the next system restart. At that time, the system would load your driver and call your DriverEntry routine.
would have somehow determined which instances of your hardware were actually present. You might have scanned all possible slots of a PCI bus, for example, or assumed that each instance of your device corresponded to a
After detecting your own hardware, your DriverEntry routine would go on to assign and reserve I/O resources and then to do the configuration and connection steps that a present-day WDM driver does. As you can see, then, WDM drivers have much less work to do to get started than did drivers for earlier versions of Windows NT.
I use the term legacy device to describe any device that isn’t Plug and Play, meaning that the operating system can’t detect its existence automatically. Let’s suppose your device fits this category. After purchasing your device, the end user will first invoke the Add New Hardware Wizard and will make a series of dialog selections to lead the setup program to an install section in an INF file. (See Figure 2-4, point 1.)
The setup program
Finally the setup program instructs the end user to restart the system (point 4). The designers of the setup system expected that the end user would now need to follow the manufacturer’s directions to configure the card by setting
Following the restart (or following the end user’s decision to bypass the restart), the root enumerator will scan the registry and find the newly added device. Thereafter, the process of loading your driver is nearly identical to that for a Plug and Play device. See Figure 2-5.
Most of the sample drivers in the companion content are for fake hardware, and you install them as if the (nonexistent) hardware were a legacy device. One or two of the samples work with I/O ports and interrupts. The respective INF files contain LogConfig sections to cause the PnP Manager to assign these resources. If you install one of these drivers via the Add New Hardware Wizard, the system will think that a power-off restart is needed, but you don’t really need to restart.
In the preceding sections, I described how the system loads the correct driver for a single device. That description begs the question of how the system manages to load drivers for all the hardware in the computer. The answer is that it uses a recursive process.
In the first instance, the PnP Manager invokes the root enumerator to find all hardware that can’t electronically announce its presence—including the primary hardware bus (such as PCI). The root bus driver gets information about the computer from the registry, which was initialized by the Windows XP Setup program. Setup got the information by running an elaborate hardware detection program and by asking the end user suitable questions. Consequently, the root bus driver knows enough to create a PDO for the primary bus.
The function driver for the primary bus can then enumerate its own hardware electronically. When a bus driver enumerates hardware, it acts in the guise of an ordinary function driver. Having detected a piece of hardware, however, the driver switches roles: it becomes a bus driver and creates a new PDO for the
Order of Driver Loading
I said earlier that devices can have upper and lower filter drivers as well as a function driver. Two registry keys associated with the device contain information about filter drivers. The
, which contains information about an instance of your hardware, can have
values that specify filter drivers for that instance. There is another registry key for the class to which the device belongs. For example, a mouse belongs to the Mouse class, which you could probably have figured out without me telling you. The
can also contain
values. They specify filter drivers that the system will load for every device
No matter where it appears, an
value is of type
and can therefore contain one or more null-
Windows 98/Me doesn’t support the REG_MULTI_SZ registry type and doesn’t fully support Unicode. In Windows 98/Me, the UpperFilters and LowerFilters values are REG_BINARY values that contain multiple null-terminated ANSI strings followed by an extra null terminator.
It may be important in some situations to understand in what order the system calls drivers. The actual process of “loading” a driver entails mapping its code image into virtual memory, and the order in which that’s done is actually not very interesting. You might be interested, however, in knowing the order of calls to the AddDevice functions in the various drivers. (Refer to Figure 2-7.)
The system first calls the AddDevice functions in any lower filter drivers specified in the device key for the device, in the order in which they appear in the LowerFilters value.
Then the system calls AddDevice in any lower filter drivers specified in the class key. Again, the calls occur in the order in which the drivers appear in the LowerFilters string.
The system calls AddDevice in the driver specified by the Service value in the device key. This is the function driver.
The system calls AddDevice for any upper filter drivers specified in the device key, in the order in which they appear in the UpperFilters data string.
Finally the system calls AddDevice for any upper filter drivers specified in the class key, in the order in which they appear in the UpperFilters data string.
As I explain later in this chapter, each AddDevice function creates a kernel DEVICE_OBJECT and links it into the stack rooted in the PDO. Therefore, the order of calls to AddDevice governs the order of device objects in the stack and, ultimately, the order in which drivers see IRPs.
You might have noticed that the loading of upper and lower filters belonging to the class and to the device instance isn’t neatly nested as you might have expected. Before I knew the facts, I guessed that device-level filters would be closer to the function driver than class-level filters.
The formal layering of drivers in the WDM facilitates routing IRPs from one driver to another in a predictable way. Figure 2-2 illustrates the general idea: whenever the system wants to carry out an operation on a device, it sends an IRP to the topmost filter driver in the stack. That driver can decide to process the IRP, to pass the IRP down to the next level, or to do both. Each driver that sees the IRP makes the same decision. Eventually, the IRP might reach the bus driver in its PDO role. The bus driver does not usually pass the IRP any further, despite what Figure 2-6 might seem to imply. Rather, the bus driver usually completes the IRP. In some situations, the bus driver will pass the same IRP to the stack (the parent driver stack) in which it plays the FDO role. In other situations, the bus driver will create a secondary IRP and pass it to the parent driver stack.
How the Device Stack Is ImplementedI’ll show you the DEVICE_OBJECT data structure a bit later in this chapter. The
field is purposely not documented because its proper use requires synchronization with code that might be deleting device objects from memory. You and I are allowed to call
to find the topmost device object in a given stack. That function also
Similarly, to know which device object is immediately underneath you, you need to save a pointer when you first add your object to the stack. Since each of the drivers in a stack will have its own unknowable way of implementing the downward pointers used for IRP dispatching, it’s not practical to alter the device stack once the stack has been created.
A few examples should clarify the relationship between FiDOs, FDOs, and PDOs. The first example concerns a read operation directed to a device that happens to be on a secondary PCI bus that itself attaches to the main bus through a PCI-to-PCI bridge chip. To keep things simple, let’s suppose there’s one FiDO for this device, as illustrated in Figure 2-8. You’ll learn in later chapters that a read request turns into an IRP with the major function code
. Such a request would flow first to the upper FiDO and then to the function driver for the device. (That driver is the one for the device object
A variation on the first example is shown in Figure 2-9. Here we have a read request for a device plugged into a USB hub that itself is plugged into the host controller. The complete device tree therefore contains stacks for the device, for the hub, and for the host controller. The IRP_MJ_READ flows through the FiDO to the function driver, which then sends one or more IRPs of a different kind downward to its own PDO. The PDO driver for a USB device is USBHUB.SYS, and it forwards the IRPs to the topmost driver in the host controller device stack, skipping the two-driver stack for the USB hub in the middle of the figure.
The third example is similar to the first except that the IRP in question is a notification concerning whether a disk drive on a PCI bus will be used as the repository for a system paging file. You’ll learn in Chapter 6 that this notification takes the form of an
request with the minor function code
. In this case, the FiDO driver
Visualizing the Device Tree
This particular device uses only two device objects. The PDO is managed by USBHUB.SYS, whereas the FDO is managed by USB42. In the first of these screen shots, you can see other information about the PDO.
It’s worth experimenting with DEVVIEW on your own system to see how various drivers are layered for the hardware you own.
The Windows 2000 Device Driver Book: A Guide for Programmers (2nd Edition)
Windows System Programming (4th Edition) (Addison-Wesley Microsoft Technology Series)
Developing Drivers with the Windows Driver Foundation (Pro Developer)
Windowsu00ae Internals: Including Windows Server 2008 and Windows Vista, Fifth Edition (Pro Developer)