4.1 Standard NT Driver Routines
All NT driver writers must implement certain system-defined standard routines and can implement others, along with any internal routines that the driver designer chooses, as mentioned in Chapter 2.
Figure 4.1 shows the NT driver object, representing a driver’s load image, and the set of standard routines for NT drivers.
Figure 4.1 Standard NT Driver Routines
As Figure 4.1 shows, all NT drivers must have a basic set of standard routines in order to process IRPs. Otherwise, the set of standard routines that can or must be implemented depends on whether the driver controls a physical device or is layered over such a device driver, as well as on the nature of the underlying physical device.
Every NT driver must have the following types of routines, which are defined by the NT I/O Manager:
DriverEntry
NTSTATUS
(*PDRIVER_INITIALIZE) (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
Every NT driver must have an initialization routine, which the I/O Manager calls automatically if this routine is explicitly named DriverEntry. For more information about a DriverEntry routine’s functional requirements, see Chapter 5.
Dispatch
NTSTATUS
(*PDRIVER_DISPATCH) (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
Every NT driver must have at least one Dispatch routine, but any driver can have a separate Dispatch routine for each IRP major function code (IRP_MJ_XXX) it handles. For more information about Dispatch routines’ functional requirements, see Chapter 6. For device-type-specific information about which major function codes NT drivers must handle, see the Kernel-mode Driver Reference.
StartIo (or Queue-management)
VOID
(*PDRIVER_STARTIO) (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
If an NT device driver cannot complete all possible I/O requests in its Dispatch routines, it either must have a StartIo routine or must set up one or more driver-created internal queues and manage its own queueing of IRPs. A higher-level NT driver might have a StartIo routine, and it can have driver-created internal queues of IRPs. For more information about the StartIo routine’s functional requirements and about managing queues of IRPs, see Chapter 7.
Depending on an NT driver’s level in a chain of layered drivers, if any, and on the nature of the underlying physical device, an NT driver can (or must) have the following types of routines, which are defined by the NT I/O Manager or Kernel:
Reinitialize
VOID
(*PDRIVER_REINITIALIZE) (
IN PDRIVER_OBJECT DriverObject,
IN PVOID Context,
IN ULONG Count
);
In addition to its DriverEntry routine, an NT driver can have a Reinitialize routine to be called one or more times later in the system boot process after its DriverEntry routine returns control. For more information about an optional Reinitialize routine’s functionality, see Chapter 5.
InterruptService (ISR)
BOOLEAN
(*PKSERVICE_ROUTINE) (
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext // usually points to device object
);
Any driver of a physical device that generates interrupts must have an ISR. Such a driver is always the lowest-level driver in any chain of layered NT drivers. For more information about an ISR’s functional requirements, see Chapter 8.
DpcForIsr or CustomDpc
VOID
(*PIO_DPC_ROUTINE) (
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
(*PKDEFERRED_ROUTINE) (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
Any driver that has an ISR must have a DpcForIsr or CustomDpc routine, and can have additional CustomDpc routines. For more information about the DpcForIsr routine’s functional requirements and about CustomDpc routines, see Chapter 9.
SynchCritSection
BOOLEAN
(*PKSYNCHRONIZE_ROUTINE) (
IN PVOID SynchronizeContext
);
Any lowest-level device driver with data or device registers shared between its ISR and other driver routines must have one or more SynchCritSection routines. For more information about a SynchCritSection routine’s functional requirements, see Chapter 10.
AdapterControl and/or ControllerControl
IO_ALLOCATION_ACTION
(*PDRIVER_CONTROL) (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
);
Any driver of a device that uses system DMA or packet-based busmaster DMA must have an AdapterControl routine. Any device driver that must synchronize operations through a physical controller to similar devices (or device channels) can have a ControllerControl routine. For more information about these routines’ functional requirements, see Chapter 11.
Cancel
VOID
(*PDRIVER_CANCEL) (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
Keyboard, mouse, serial, parallel, and sound device drivers, as well as file system drivers, have Cancel routines. Any driver in which IRPs might remain queued for an indefinite interval (so that a user could cause a previously submitted I/O request to be cancelled) must have one or more Cancel routines. Usually, the highest NT driver in a chain has a Cancel routine. For more information about a Cancel routine’s functional requirements, see Chapter 12.
IoCompletion
NTSTATUS
(*PIO_COMPLETION_ROUTINE) (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
Any higher-level NT driver that allocates IRPs to send to lower drivers must have at least one IoCompletion routine to free all IRPs that the driver allocates. However, any higher-level driver can have one or more IoCompletion routines. Other driver routines can set up an IoCompletion routine to be called when all lower-level drivers have finished processing a given IRP. For a general discussion of how higher-level drivers can use IoCompletion routines, see Chapter 2. See also Section 4.3 for an overview of how a representative NT intermediate driver’s IoCompletion routine processes IRPs. For more information about an IoCompletion routine’s functional requirements, see Chapter 13.
IoTimer and/or CustomTimerDpc
VOID
(*PIO_TIMER_ROUTINE) (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
);
VOID
(*PKDEFERRED_ROUTINE) (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1, // reserved for system use
IN PVOID SystemArgument2 // reserved for system use
);
To monitor whether an I/O operation has timed out or for some other purpose determined by the driver designer, any driver can have IoTimer and/or CustomTimerDpc routines. An IoTimer routine is called once per second when the driver has enabled the timer. A CustomTimerDpc routine can be called at finer-grained intervals or at variable intervals. For more information about timer routines’ functional requirements, see Chapter 14.
Unload
VOID
(*PDRIVER_UNLOAD) (
IN PDRIVER_OBJECT DriverObject
);
An NT driver must have an Unload routine if it can be unloaded while the system continues to run. For more information about an Unload routine’s functional requirements, see Chapter 15.
As the preceding declarations for standard NT driver routines show, the current IRP and target device object are input parameters to many standard routines defined by the I/O Manager. Every NT driver processes each IRP in stages through its set of standard routines. Sections 4.2 and 4.3 describe the general path for an IRP through a representative lowest-level physical device driver and a representative intermediate driver, respectively.