An NT device driver can register its DpcForIsr by calling IoInitializeDpcRequest when the driver initializes. After the driver is loaded and handling interrupt-driven I/O requests, the ISR calls IoRequestDpc just before it returns control to have the DpcForIsr routine queued for execution. Figure 3.17 illustrates calls to these routines.
Figure 3.17 Using a DPC Object for a DpcForIsr Routine
As Figure 3.17 shows, calling IoInitializeDpcRequest associates a Kernel DPC object with a driver-supplied DpcForIsr routine and a driver-created device object. The I/O Manager allocates memory for the DPC object and calls KeInitializeDpc on the driver’s behalf.
When the ISR is called to handle a device interrupt at DIRQL, it should return control to the system as soon as possible for better overall system and driver performance. Usually, an ISR merely stops the device from generating more interrupts, gathers whatever context information the DpcForIsr routine needs to complete the operation that caused the interrupt, calls IoRequestDpc, and returns.
As Figure 3.17 shows, the ISR passes a pointer to the device object, representing the target device for which the operation was carried out, a pointer to the DeviceObject->CurrentIrp, and a pointer to a driver-determined Context for the operation to IoRequestDpc. The I/O Manager calls KeInsertQueueDpc on the driver’s behalf, and the corresponding DPC object is queued until IRQL falls below DISPATCH_LEVEL on a processor. Then, the Kernel dequeues the DPC object and the driver’s DpcForIsr is run on the processor at IRQL DISPATCH_LEVEL.
On entry, the DpcForIsr is given a pointer to the DPC object and the DeviceObject, current Irp, and Context pointers passed in the ISR’s call to IoRequestDpc. The Context-accessible area must be in resident memory. Unless the driver overlaps I/O for the target device, such a context area is usually in the DeviceObject->DeviceExtension, 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. The DpcForIsr is responsible for doing whatever is necessary to complete the I/O requested in the current IRP.
Even in a uniprocessor machine, the ISR could be called again if the device interrupts while or before the DpcForIsr is run. If this occurs, the DpcForIsr routine is run only once. In other words, there is no one-to-one correspondence between an ISR’s calls to IoRequestDpc and instantiations of the DpcForIsr routine if an NT driver overlaps I/O operations for its target device objects.
For more information about the functionality required of a DpcForIsr routine, see Chapter 9.