To support interrupt handling for its device, a KMDF driver must
Create an interrupt object.
Enable and disable the interrupt.
Optionally perform pre-enable and post-disable processing that is related to the interrupt.
Handle interrupts as they occur.
1. Creating an Interrupt Object
A driver typically creates its interrupt objects (WDFINTERRUPT) in its EvtDriverDeviceAdd
callback. The driver must have an interrupt object for each interrupt
vector or message-signaled interrupt (MSI) that each of its devices
support. Each interrupt object must include pointers to EvtInterruptIsr and EvtInterruptDpc event callback functions and may also include additional information.
The framework calls EvtInterruptIsr when an interrupt occurs. This callback runs at device interrupt request level (DIRQL) for the device and is the equivalent of the WDM InterruptService function. The EvtInterruptIsr callback queues a DPC to perform additional interrupt-related processing. The framework calls the driver’s EvtInterruptDpc callback when the DPC is ready to run. The EvtInterruptDpc callback runs at DISPATCH_LEVEL and is equivalent of the WDM DpcForisr function.
Creating an interrupt object, like creating any
other WDF object, involves filling in a configuration structure and
calling a creation method. The driver calls the WDF_INTERRUPT_CONFIG_INIT function to initialize the WDF_INTERRUPT_CONFIG structure with pointers to the EvtInterruptIsr and EvtInterruptDpc
callbacks. After initializing the structure, the driver can set
additional information in it, including a pointer to a spin lock and
pointers to the EvtInterruptEnable and EvtInterruptDisable callbacks, which enable and disable interrupts for the device. KMDF calls these functions at DIRQL while holding the interrupt spin lock during device power state transitions and when the driver calls WdfEnableInterrupt or WdfDisableInterrupt.
If the driver must perform additional tasks
immediately after the interrupt is enabled and before it is disabled,
it should also register the EvtDeviceDOEntryPostInterruptsEnabled and EvtDevice-DOExitPreInterruptsDisabled callbacks. KMDF calls both of these functions at PASSIVE_LEVEL without holding the interrupt spin lock.
To create the interrupt object, the driver calls the WdfInterruptCreate
method and passes a handle to the device object, a pointer to the
interrupt configuration structure, a pointer to an attribute
configuration block, and a pointer to a variable that receives the
handle to the interrupt object. Drivers typically specify WDF_NO_ATTRIBUTES when creating an interrupt object.
2. Code to Create an Interrupt Object
The PCIDRV sample creates its interrupt object in the NICAllocateSoftwareResources function, which is called by PciDrvEvtDeviceAdd (the driver’s EvtDriverDeviceAdd callback). The following code shows how the PCIDRV sample creates its interrupt object:
WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
NICEvtInterruptIsr,
NICEvtInterruptDpc);
interruptConfig.EvtInterruptEnable =
NICEvtInterruptEnable;
interruptConfig.EvtInterruptDisable =
NICEvtInterruptDisable;
status = WdfInterruptCreate(FdoData->WdfDevice,
&interruptConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&FdoData->WdfInterrupt);
If(!NT_SUCCESS (status)) {
return status;
}
The PCIDRV sample configures the interrupt object by specifying pointers to NICEvtInterruptIsr and NICEvtInterruptDpc, which are called to handle interrupts and to perform deferred interrupt processing at IRQ DISPATCH_LEVEL, respectively. The driver also sets pointers to NICEvtInterruptEnable and NICEvtInterruptDisable, which enable and disable interrupts in the hardware. The call to WdfInterruptCreate returns a handle to the interrupt object, which the driver stores in the context area of its device object.
3. Enabling and Disabling Interrupts
A KMDF driver’s EvtInterruptEnable callback enables interrupts for its device. KMDF calls this function during a device transition to DO, after EvtDeviceDOEntry has returned. The callback is called at DIRQL
for the device with the interrupt spin lock held; therefore, the
callback should quickly enable the interrupt and return. If the driver
requires additional processing after enabling its interrupt, it should
set an EvtDeviceDOEntryPostInterruptsEnable callback, which KMDF calls at PASSIVE_LEVEL.
EvtInterruptEnable
is called with two parameters: a handle to the interrupt object and a
handle to the device object that is associated with the interrupt
object. The driver passes the device object handle to the accessor
function for its device context area, where it has stored information
about its device registers. With the returned pointer, the driver can
access the hardware registers as required to enable the interrupt.
A KMDF driver’s EvtInterruptDisable callback disables interrupts for its device. KMDF calls this function during a device transition out of the DO state, but before it calls EvtDeviceDOExit. Like EvtInterruptEnable, EvtInterruptDisable is called at DIRQL
for the device and with the interrupt spin lock held; therefore, it
should quickly disable the interrupt and return. If the driver requires
additional processing before disabling its interrupt, it should set an EvtDeviceDOExitPreInterruptsDisabled callback, which KMDF calls at PASSIVE_LEVEL.
The EvtInterruptDisable callback is passed the same two parameters as the EvtInterruptEnable callback and proceeds to undo the actions that were performed in that callback.