|< BACK NEXT >|
Structure of a Kernel-Mode Driver
A kernel-mode driver looks very different from conventional applications. In general, a driver is just a collection of routines that are called by operating system software (usually the I/O Manager). Flow
Depending on the driver, the I/O Manager might call a driver routine in any of the following situations:
The remainder of this section
Driver Initialization and Cleanup Routines
When a driver loads into the system, various
The I/O Manager calls this routine when a driver is first loaded, perhaps as early as boot time, but drivers may be dynamically loaded at any time. The DriverEntry routine
Some drivers may not be able to complete their initialization during DriverEntry. This could occur if the driver was dependent on another driver that had yet to load, or if the driver needed more support from the operating system than had
The I/O Manager calls the Unload routine of a driver when a driver is unloaded dynamically. It must reverse every action that DriverEntry performed, leaving behind no allocated resource. This includes removing the system-wide device name supplied during initialization for all controlled devices. For WDM drivers, device resources (and
Surprisingly, the Unload routine is not invoked during a system shutdown. Since the system is "going away" anyway, a perfect cleanup is not required. Instead, the I/O Manager invokes a driver's Shutdown routine to provide an opportunity to place hardware into a quiescent state.
BUGCHECK CALLBACK ROUTINE
If a driver needs to gain control in the event of a system crash, it can provide a Bugcheck routine. This routine, when properly registered, is called by the kernel during an orderly "crash" process (truly an oxymoronic phrase).
I/O System Service Dispatch Routines
When the I/O Manager receives a request from a user-mode application, the type of request (read, write, etc.) is converted into a function code. The I/O Manager identifies the appropriate driver to handle the request, then calls one of several Dispatch routines in the driver. There is one Dispatch routine per function code supported by a driver.
The driver's appropriate Dispatch routine verifies the request and, if necessary,
OPEN AND CLOSE OPERATIONS
All drivers must provide a CreateDispatch routine that handles the Win32 CreateFile request. Drivers that must perform cleanup on the Win32 CloseHandle call must supply a CloseDispatch routine.
Depending on the device, a driver may have individual Dispatch routines for handling data transfers and control operations. The Win32 functions ReadFile, WriteFile, and DeviceIoControl are dispatched to supplied routines in the driver by the I/O Manager. Again, a driver need only supply routines for those operations it supports.
If a user program makes a request of an I/O device for which the driver does not supply a Dispatch routine, the program receives an error indicating that the
Data Transfer Routines
Device operations involve a number of different driver routines, depending on the nature and complexity of the device.
START I/O ROUTINE
The I/O Manager calls the driver's Start I/O routine each time a device should begin the start of a data transfer. The request is generated by the I/O Manager each time an outstanding I/O request completes and another request is waiting in the queue. In other words, the I/O Manager (by default) queues and serializes all I/O requests, restarting the affected device only after the previous request completes. (Complex devices capable of dealing with simultaneous requests may choose not to utilize this serialized approach.)
The start I/O routine, supplied by the driver, allocates resources needed to process the requested I/O and sets the device in motion.
INTERRUPT SERVICE ROUTINE (ISR)
The Kernel's interrupt dispatcher calls a driver's Interrupt Service routine each time the device generates an interrupt. The ISR is responsible for complete
As described earlier, only the most minimal of servicing should be performed within the ISR itself. If additional,
A driver can supply zero or more DPC routines that perform or complete routine device operations. This might include the release of system resources (such as a DMA page descriptor), reporting error conditions, marking I/O requests as complete, and starting the next device operation, as necessary.
If only one DPC is required for completion of interrupt servicing, the I/O Manager supports a simplified mechanism called a DpcForIsr routine. However, some drivers may wish to provide many DPCs for varied purposes. Perhaps two different DPCs, one that completes a write operation and one that completes a read operation, is
Resource Synchronization Callbacks
Recalling the design goals of Windows 2000, all kernel-mode code (including device driver code) must be reentrant. Driver code must consider that multiple threads in one or more processes may make simultaneous I/O requests. Contention issues between the two simultaneous requests must be handled safely and correctly by the reentrant driver code.
The I/O Manager provides several synchronization services to deal with the issue of shared resources among simultaneous requests. These synchronization routines
The technique employed to synchronize within kernel-mode code is to supply the address of a callback routine dedicated to synchronization of a specific resource. When a driver needs access to a shared resource, it queues a request for that resource with the assistance of the I/O Manager. When the resource becomes available, the I/O Manager invokes the driver-supplied callback routine associated with the request. Of course, this means that multiple requests for the resource are serialized and that at any given time, only one thread context may "own" the resource.
There are three types of synchronization callback routines supported by the I/O Manager. These are discussed in the following sections.
Sometimes a single controller (card) supports more than one function. Further, the functions may share a single set of controller registers. Thus, only one function on such cards may be in operation at a time. Typically, the Start I/O routine requests exclusive ownership of the controller. When granted, the ControllerControl callback routine executes. When the I/O completes, the driver releases the controller, usually within a DpcForIsr routine.
DMA hardware is another shared resource that must be passed from driver to driver. Before performing DMA, the driver requests exclusive ownership of the proper DMA hardware -typically a DMA channel. When ownership is granted, the AdapterControl callback routine executes.
Interrupt service occurs at a device-specific DIRQL while remaining driver code operates at DISPATCH_LEVEL or below. If the lower IRQL sections of code ever touch resources used by the ISR, that operation must execute inside of a SynchCritSection routine. Resources in this category include all device control registers and any other context or state information shared with the Interrupt Service routine.
SynchCritSection routines operate somewhat differently than other synchronization techniques. Once the callback occurs, the IRQL level is raised to the device's DIRQL level. Thus, lower IRQL sections of code temporarily operate at device DIRQL, preventing interruption by the ISR. When the SynchCritSection completes, the IRQL is restored to its original value.
Other Driver Routines
In addition to the basic set of routines just described, a driver may contain any of the following additional functions.