3.7.2 Registering and Queueing a CustomTimerDpc Routine
Any NT driver can register a CustomTimerDpc routine when the driver is loaded by calling the following routines:
1.KeInitializeDpc to set up its CustomTimerDpc routine
2.KeInitializeTimer or KeInitializeTimerEx to set up a timer object
After the driver has initialized, it can call KeSetTimer or KeSetTimerEx to set up the timer object with an interval and with the DPC object in order to have its CustomTimerDpc called when the given interval expires. Figure 3.20 illustrates these calls.
Figure 3.20 Using Timer and DPC Objects for a CustomTimerDpc Routine
As Figure 3.20 shows, the driver must supply storage both for a DPC object and a timer object if it has a CustomTimerDpc routine. Most NT drivers that have CustomTimerDpc routines provide the storage for these objects in the device extension of a driver-created device object (see Section 3.2) or in other resident memory allocated by the driver.
The DriverEntry routine must first initialize the DPC object by calling KeInitializeDpc and then initialize the timer object by calling KeInitializeTimer or KeInitializeTimerEx. For more information about calls to KeInitializeDpc, see Section 3.6.2, earlier in this chapter.
As Figure 3.20 shows, the driver must pass a pointer to its storage for the timer object when it calls KeInitializeTimer or KeInitializeTimerEx.
After the driver has initialized, it can queue the CustomTimerDpc to be called after a driver-determined interval by calling KeSetTimer with Dpc and Timer object pointers, and with a given DueTime expressed in units of 100 nanoseconds, as shown in Figure 3.20. A positive value for DueTime specifies an absolute time at which the CustomTimerDpc routine should be called. A negative value for DueTime specifies a relative expiration time. Absolute expiration times track any changes in system time; relative expiration times are not affected by system time changes.
To invoke a CustomTimerDpc routine repeatedly, set the timer using KeSetTimerEx and specify a recurring interval in the Period parameter. KeSetTimerEx is just like KeSetTimer except for this additional parameter.
As shown in Figure 3.20, the call to KeSetTimer or KeSetTimerEx queues the timer object for the given interval:
1.When the given DueTime expires, the timer object is dequeued and set to the Signaled state.
2.If every processor in the machine is currently running code at an IRQL greater than or equal to DISPATCH_LEVEL, the DPC object associated with the timer object is put in a DPC queue. Otherwise, the CustomTimerDpc routine is called.
3.If the DPC object was queued when the DueTime interval expired, the CustomTimerDpc routine is called as soon the IRQL on any processor in the machine falls below DISPATCH_LEVEL.
The CustomTimerDpc routine, like all DPCs, is run at IRQL DISPATCH_LEVEL.
A Kernel timer object interval is approximately ten milliseconds, so a driver with a CustomTimerDpc has more control over the intervals at which its CustomTimerDpc routine is run than an IoTimer routine.
Like a DPC object, only one instantiation of a given timer object can be queued at any given moment. Calling KeSetTimer or KeSetTimerEx again with the same Timer object pointer cancels the queued timer object and resets it.
For more information about CustomTimerDpc routines, see also Chapter 14.
A driver writer who implements a CustomTimerDpc must follow the same guidelines concerning the DeferredContext area as for the TimerContext area of an IoTimer routine, as already described in Section 3.7.1.