Any NT driver can register an IoTimer routine after it creates one or more device objects by calling IoInitializeTimer when the driver initializes. After the driver has initialized, it can enable the timer by calling IoStartTimer. Figure 3.19 illustrates these calls.
Figure 3.19 Using a Timer Object for an IoTimer Routine
As Figure 3.19 shows, the driver calls IoInitializeTimer with the entry point of its IoTimer routine and pointers to a driver-created device object and TimerContext area in which the driver maintains whatever context its IoTimer routine uses. The I/O Manager associates the device object with a Kernel timer object, which the I/O Manager sets up to time out every second.
After the driver calls IoStartTimer, its IoTimer routine is called once per second until the driver calls IoStopTimer. An NT driver can re-enable calls to its IoTimer routine with IoStartTimer.
On entry, the IoTimer routine is given DeviceObject and Context pointers to the associated device object and TimerContext area that was set up when the driver called IoInitializeTimer.
Because an IoTimer routine is run at IRQL DISPATCH_LEVEL, its TimerContext area must be in resident, system-space memory. Most NT drivers that have IoTimer routines use the DeviceObject->DeviceExtension of the associated device object as such a Context-accessible area, but it can be in a controller extension if the driver uses a controller object (see Section 3.4) or in nonpaged pool allocated by the driver.
For more information about SynchCritSection routines, see Chapter 10. See also the section about using spin locks in Chapter 16 for more information about KeSynchronizeExecution.
For more information about initializing and using executive spin locks, see Chapter 16.
For more information about the functionality required of an IoTimer routine, see Chapter 14.