Last reviewed and updated: 10 August 2020
When you’re new to the world of Windows driver development, nothing seems simple. Take, for example, the job of monitoring system activity or collecting information from kernel mode. You might expect this to be a relatively straight-forward project. And, it is… once you understand a few basics about the different types of software-only drivers that you can write.
Software-only drivers (or just “software drivers” as they are most often called) are drivers that do not interact with device hardware. That is, they (a) do not claim hardware resources such as registers, ports, or interrupts, (b) do not manage the operation of a hardware device, and (c) do not attach to an already existing device stack that has a Function Driver. Software drivers are often referred to as “Kernel Services” because they provide non-hardware related functions, such as system monitoring or data collection. For example, if you need to monitor changes to the Registry or know which executable images are loaded into the system, you almost certainly would want to write a software driver.
There are two types of software-only drivers:
- Legacy-style software drivers
- PnP-aware software drivers
Legacy-style software drivers are based on the original, Windows NT, driver model. This is the model that Windows NT used before PnP and power management were introduced. Legacy-style software drivers are the most common, and the most simple, type of software driver. This type of software driver is appropriate for just about any task for which you would need a software-only driver and is therefore the type of software driver that we at OSR typically recommend people write.
Some people mistakenly believe that all Windows drivers need to be PnP and Power Management aware. While this is generally true for drivers that support hardware, it is not true for software-only drivers. Legacy-style software drivers are in no way deprecated and are in fact still 100% supported by Windows. Think about it: The purpose of supporting PnP is to allow a driver to respond to the dynamic arrival and departure of a device. The purpose of Power Management is to allow a driver to participate in system power state transitions (such as the transition to sleep or hibernate) and to manage a device’s power state. Therefore, unless you’re writing a software-only driver that needs to be aware of power-state transitions (which would be a rare thing) a legacy-style software driver is exactly the type you want to write.
Of course, it is possible to write a software-only driver that is aware of PnP and Power Management events. This type of driver, referred to as a PnP-aware software driver, can be written using either the WDM or WDF models (though the WDF model is certainly most highly recommended). PnP-aware software drivers are “root enumerated”, that is they are started by the PnP Manager, and exist in their own unique branch of the PnP device tree. Unlike their legacy-style cousins, PnP-aware software drivers work very much like typical hardware drivers: They receive the full complement of PnP and Power requests and are required to handle those requests just like a driver that supports hardware. The PnP and Power Managers don’t make any special concessions for a driver just because it’s root-enumerated and is not associated with any hardware.
Which Type to Choose
There are advantages and disadvantages to each type of software driver. The driver type you choose to implement will depend both on your needs and on the development models with which you are familiar.
Legacy-style software drivers are unquestionably the simplest type of software driver to write. However, writing this type of driver does require that you have some knowledge of standard Windows driver architecture. You’ll need to code a DriverEntry entry point and a dispatch entry point for each IRP major function you want to support. You’ll need to understand the different transfer types (Direct, Buffered, and Neither I/O), and be comfortable dealing directly with IRPs (to do such things as retrieve parameters from the IRP’s I/O Stack Location) and I/O completion. None of these things is difficult, of course. But if your experience lies primarily in the realm of WDF, you might view having to learn these additional concepts as a bit of an annoyance.
As mentioned previously, the alternative to developing a legacy-style software driver is to write a software driver that is PnP-aware. If you’re already familiar with KMDF, this might be the easiest option for you. The driver you write will be exactly like any other KMDF driver: You code a DriverEntry entry point that calls WdfDriverCreate, and an EvtDriverDeviceAdd entry point that creates your WDFDEVICE and one more WDFQUEUEs, which in turn contain a selection of I/O Event Processing Callbacks. The WDFQUEUEs present WDFREQUESTs to the I/O Event Processing Callbacks, and you process these requests as appropriate. You may choose, or not, to handle power management events using the usual WDF mechanisms. Getting your power management code “right” isn’t likely to be a major effort, because KMDF will handle most of the details of this typically onerous task for you.
There are reasons to choose to write one type of software-only driver over the other, aside from just familiarity with the development model. These reasons have to do with the work that you need your software driver to perform.
One of the most interesting characteristics of legacy-style software drivers is that they live in a “parallel universe” to that inhabited by drivers that are PnP/Power aware. Thus, a legacy-style software driver isn’t merely excused from obeying the standard rules that apply to drivers that support PnP/Power, it actually lives in an environment where no PnP or Power Management exists. This means that even if a legacy-style software driver supplies handlers for PnP or Power IRPs, it will never receive those IRPs because the driver executes within an environment in which these IRPs are not supported. So if you need to write a software driver that’s aware, for example, of changes to the system’s power state a legacy-style software driver won’t do the job – You’ll need to write a PnP-aware software driver.
It’s important to realize that even though legacy-style software drivers live in a “parallel universe”, this does not limit their ability to interact with PnP- or Power-aware drivers. Legacy drivers can send requests to and receive requests from any other driver in the system (hardware or software-only, PnP-aware or legacy). Also, legacy-style drivers can be informed of the arrival and departure of classes of PnP devices by registering a callback using IoRegisterPlugPlayNotification.
Software Driver != Filter Driver
One common misconception about Windows software-only drivers is that they are the same as filter drivers. While filter drivers don’t typically claim any hardware resources and almost never interact directly with hardware, they do attach to a device stack that contains an FDO. In that stack, they monitor, manage, or modify the operation of the underlying device. Filter drivers are almost always loaded via the PnP process and often need to be aware of the power state of the device stack in which they reside. A filter driver typically needs to understand the details of the hardware operations that take place within its stack. As a result, a filter driver is a lot more like a device driver than a software-only driver.
Because most software-only drivers are legacy-style drivers, an extra note about using legacy-style drivers as filter drivers is in order here: Legacy-style drivers are not well-suited to act as filter drivers for a PnP-aware device stack. Even if you can contrive to get a legacy-style software driver inserted at the top of (or, somehow, within) a stack of PnP drivers, this is unlikely to be a reliable or supportable configuration. Why? Well, that’s a discussion that we’ll reserve for when we discuss more about writing filters.
Go To It!
There are many things in Windows that are either difficult or impossible to control from User Mode. In addition, collecting certain kinds of data is often much easier in Kernel Mode. Let’s say you want to control which programs get executed on a system, or you want to monitor all write operations to the Registry. In this case, a software-only driver is exactly what you need!
You might be surprised to learn that an example legacy-style software driver is provided with the Windows Driver Kit. Look under \src\general\registry for an example of a driver that filters Registry operations. It’s not a particularly simple (or particularly well-written) example but it does demonstrate the Configuration Manager monitoring functions, including some advanced use of transactions.
So, that’s the scoop about Windows software drivers. For most monitoring and reporting tasks, you’ll almost always want to choose to write a simple legacy-style software driver. They’re fully supported by Windows, and they’re entirely immune from having to deal with the pain that is PnP and Power Management, because they live in an environment where PnP/Power does not exist. Of course, if you’re already familiar with KMDF or you need to be aware of system power state transitions, you can write a software-only driver using that model. Either way, your job should be relatively simple now that you understand your options.
Happy software-driver writing!