4.1 Handling an Interrupt

MiniportISR is passed two arguments, InterruptRecognized and QueueMiniportHandleInterrupt, that must be set before returning. MiniportISR must set InterruptRecognized to TRUE if it recognizes the interrupt or FALSE if it does not. If the interrupt is shared, and the miniport does not recognize the interrupt, it returns FALSE and the interrupt will be passed to the next possible ISR function for this interrupt until a function recognizes the interrupt, or all possible functions have been called. If MiniportISR recognizes the interrupt and wants its MiniportHandleInterrupt queued, it must set QueueMiniportHandleInterrupt to TRUE. NDIS does the actual queuing of the MiniportHandleInterrupt function when MiniportISR returns. If the miniport sets InterruptRecognized to FALSE, the value of QueueMiniportHandleInterrupt is assumed to be FALSE.

While an ISR is executing, all other interrupts on the processor at the same or lower IRQL are masked off. Therefore, it is important for a miniport to minimize the time spent in MiniportISR. Otherwise, the overall performance of the system degrades, as well as I/O throughput for the miniport.

It is important for a miniport that manages a NIC that shares an interrupt with other devices on the same bus to quickly determine if the interrupt belongs to it. If it does not, the miniport should set InterruptRecognized to FALSE and return immediately.

If the interrupt is shared and belongs to the miniport, or if the interrupt is not shared but is recognized by the miniport (it is not spurious), the miniport should disable interrupts on its NIC, and then capture the interrupt-specific information it needs from the NIC registers, either immediately in MiniportISR if absolutely necessary, or preferably in the MiniportHandleInterrupt function, which runs at a lower IRQL.

Interrupts must be disabled by MiniportISR to ensure that when MiniportHandleInterrupt runs, no information in the device has been overwritten by a subsequent interrupt. MiniportISR should read and save only that information which cannot be accessed after it returns. Having done this, MiniportISR should return.

If the miniport does not want its MiniportHandleInterrupt function queued, and it disabled interrupts previously in MiniportISR, it must reenable the interrupt on the NIC before returning from MiniportISR.

Generally, MiniportISR determines and records the reason for the interrupt, copies any volatile interrupt-specific information it needs to handle the interrupt into a permanent structure, usually somewhere in the MiniportAdapterContext passed to MiniportISR, and clears the interrupt on the NIC.

Synchronizing with an ISR Function

A miniport’s MiniportISR function and its MiniportDisableInterrupt function run at DIRQL. Other miniport code runs at IRQL <= DISPATCH_LEVEL. To prevent race conditions, any miniport function that shares resources with the MiniportISR or MiniportDisableInterrupt function must synchronize access to the shared resources. A function synchronizes with MiniportISR and MiniportDisableInterrupt by calling NdisMSynchronizeWithInterrupt, supplying a MiniportSynchronizeISR function. MiniportSynchronizeISR runs at DIRQL and can safely access the shared resource.

For example, MiniportHandleInterrupt calls NdisMSynchronizeWithInterrupt before clearing an interrupt status register. This NDIS call takes a SynchronizeFunction argument. Within NdisMSynchronizeWithInterrupt, NDIS calls the miniport’s SynchronizeFunction at DIRQL, so that this function can safely clear the interrupt status register and be sure that MiniportISR or MiniportDisableInterrupt are not modifying the same register at the same time. SynchronizeFunction should execute as quickly as possible like the MiniportISR and MiniportDisableInterrupt functions.

The execution of MiniportISR and MiniportDisableInterrupt, both of which run at DIRQL, is synchronized with a spin lock associated with the interrupt object.