2.3.3 Other Standard NT Driver Routines
As Figure 2.4 shows, NT drivers have other standard routines along with those for which they set entry points in their respective driver objects. Most standard driver routines and some of the configuration-dependent objects they use are defined by the I/O Manager. The ISR, SynchCritSection routine, and those shown in Figure 2.4 with names containing the word “custom” are defined by the NT Kernel.
With few exceptions (SCSI and video miniport drivers), each NT driver’s DriverEntry routine must also create one or more device objects representing the physical, logical, or virtual device(s) for which it carries out I/O requests. As Figure 2.4 shows, the I/O Manager maintains information about driver-created device objects in the corresponding driver object.
Most NT drivers use the device extension of each device object they create to maintain device-specific state about their I/O operations and to store pointers to any system resources that they must allocate in order to have other standard routines. For example, the DDCustomTimerDpc routine shown in Figure 2.4 requires the driver to supply storage for Kernel-defined timer and DPC objects.
As mentioned in Section 2.2, the set of standard driver routines for lowest-level drivers shown on the left in Figure 2.4 is necessarily different from the set for higher-level drivers. Some of the routines shown in Figure 2.4 are device-dependent or configuration-dependent requirements. Others are optional: a driver writer may choose to implement such a routine depending on the nature or configuration of the driver’s device(s), on the driver’s design, and on the driver’s position in a chain of layered NT drivers.
In addition to the Dispatch, StartIo, and Unload routines for which NT drivers set entry points in their respective driver objects, the system-defined standard NT driver routines include the following:
Standard Routine | NT Driver Level |
InterruptService | lowest-level only |
Drivers for physical devices that generate interrupts must have an ISR. The ISR must stop the device from interrupting. Then, it should do only what is necessary to save state and queue a DPC to complete interrupt-driven I/O operations at a lower hardware priority (IRQL) than that at which the ISR executes.
DpcForIsr | lowest-level only |
Drivers that have an ISR also should have a DPC, either a DpcForIsr or one or more CustomDpc routines, to complete interrupt-driven I/O operations.
SynchCritSection | lowest-level only |
Any device driver whose routines share data or device registers with its ISR must have one or more SynchCritSection routines to access the shared data or registers in a multiprocessor-safe manner.
Cancel | any level (highest driver in any chain) |
Drivers in which IRPs might remain queued for an indefinite interval (so a user could cancel a previously submitted I/O request) must have one or more Cancel routines to complete user-cancelled I/O requests. Examples of NT drivers that should have Cancel routines are keyboard, mouse, parallel, serial, and sound device drivers (or drivers layered over them), and file system drivers.
AdapterControl | lowest-level only |
Any driver whose device uses system DMA must have an AdapterControl routine in order to carry out transfer operations through a system DMA controller between its device and system physical memory. Any driver whose DMA device is a nonSCSI busmaster usually must have an AdapterControl routine.
ControllerControl | lowest-level only |
A device driver that must synchronize operations through a physical controller, such as an “AT” disk controller, to similar devices can have a ControllerControl routine.
IoCompletion | intermediate or highest-level |
Higher-level drivers that monitor on an IRP-specific basis how lower-level drivers carried out particular requests can have one or more IoCompletion routines. Higher-level drivers that allocate IRPs to send requests to lower drivers must have an IoCompletion routine. See Section 2.2 for a description of how higher-level drivers can use an IoCompletion routine.
IoTimer | any level |
Drivers that need to be called periodically to determine if a device operation has timed out, to update some driver-defined variable (such as a counter), or for some other reason can have an IoTimer routine. An IoTimer routine is actually a DPC routine, associated with a device object, that the I/O Manager calls once per second. An NT driver can have an IoTimer routine for each device object that it creates.
CustomTimerDpc | any level |
Drivers that need to be called periodically at finer-grained intervals than once per second or at variable intervals can have a CustomTimerDpc routine, rather than an IoTimer routine. NT drivers also can have one or more CustomTimerDpc routines in addition to their IoTimer routines.
CustomDpc | lowest-level only |
Any driver that needs to finish an interrupt-driven I/O operation later at a lower hardware priority (IRQL) can have a CustomDpc routine. Few lowest-level drivers have CustomDpc routines to be queued from their ISRs unless their devices require more than one DpcForIsr routine to complete a varied set of interrupt-driven I/O operations.
Note that the CustomTimerDpc and IoTimer routines shown in Figure 2.4 are actually system-defined CustomDpc routines that execute after a system clock interrupt occurs.
Reinitialize | any level |
Any driver that needs to intialize itself in stages can have a Reinitialize routine. A Reinitialize routine is called after the DriverEntry routine has returned control and other NT drivers have initialized themselves.
By convention, the system-supplied NT drivers prepend an identifying, driver- or device-specific prefix to the name of every standard routine except DriverEntry, shown in Figure 2.4 as “DD.” Following this convention makes it easier to debug and maintain NT drivers.