3.5.2 Registering an ISR

An NT device driver must call IoConnectInterrupt when it initializes in order to register its ISR. Figure 3.16 illustrates this call.

Figure 3.16 Setting Up Interrupt Objects

As Figure 3.16 shows, when a device driver calls IoConnectInterrupt, the I/O Manager calls the Kernel to set up the driver's interrupt object(s):

1.Unless the ISR handles more than one interrupt vector for different devices, the DriverEntry routine registers the ISR with the following parameters:

·A pointer to the driver's storage area for the interrupt object pointer returned by IoConnectInterrupt

·A NULL spin lock pointer, unless the driver itself has allocated storage for and already initialized an InterruptSpinLock with KeInitializeSpinLock

·A pointer to the ServiceContext area the ISR will use when it is called and the entry point for the ISR

·The (mapped) DeviceSystemVector obtained from HalGetInterruptVector

·The SystemDirql assigned to that vector for this machine, also obtained from HalGetInterruptVector

·The same IRQL value for the SynchronizeIrql parameter

·The ProcessorMask, indicating the set of processors on which the device can interrupt in this machine, also obtained from HalGetInterruptVector

·Whether the interrupt mode is LevelSensitive or Latched

·Whether the device can share the vector

·Whether to save the floating-point registers when the device interrupts

2.The I/O Manager allocates sufficient resident memory for as many interrupt objects as the input ProcessorMask indicates, which could be as many interrupt objects as processors in SMP machines or could be a lesser number, depending on the ProcessorMask. The I/O Manager also provides storage for an interrupt spin lock and initializes it if the driver passes a NULL pointer to IoConnectInterrupt (see Step 1).

3.The I/O Manager calls KeInitializeInterrupt for each interrupt object.

4.The I/O Manager also calls KeConnectInterrupt for each initialized interrupt object to connect it to a particular processor on which the device can interrupt in the machine. This call actually sets the given DeviceSystemVector in the Kernel's IDT for a particular processor, as hinted in Figure 3.16.

5.When all necessary interrupt object(s) have been initialized and connected on the processor(s), IoConnectInterrupt returns a pointer to the set of interrupt objects. NT driver routines must pass the returned PointerToInterruptObject(s) in their calls to KeSynchronizeExecution.

6.As Figure 3.16 shows, when a device interrupt occurs on a given processor, the driver's ISR is run on that processor at DIRQL and given a pointer to the ServiceContext that was set up when the driver called IoConnectInterrupt.

If an NT driver must handle interrupts from more than one device, each with a different interrupt vector, it can have a single multivector ISR for all its devices, or it can have more than one ISR, up to one for each vector. Such a driver's DriverEntry routine must register the ISR as follows:

1.The DriverEntry routine must call KeInitializeSpinLock with a pointer to driver-provided storage for an InterruptSpinLock that must be in resident memory (in a device extension, controller extension, or nonpaged pool allocated by the driver).

2.For each vector an ISR handles, the DriverEntry routine must call HalGetInterruptVector and IoConnectInterrupt as described in Step 1, except for the following parameters:

·Such a driver must pass a pointer to its storage for an initialized InterruptSpinLock, rather than a NULL spin lock pointer.

·Such a driver must specify a SynchronizeIrql value that is the highest SystemDirql assigned to any device for which its ISR(s) handle interrupts.

The ISR(s) of such a driver must run at the highest DIRQL assigned to the DeviceSystemVectors the driver handles, as must any driver-supplied SynchCritSection routine that accesses device registers (or data) shared with an ISR.

Because an ISR runs at a relatively high IRQL, it must return control as quickly as possible. Consequently, an NT driver's ISR should do as little I/O processing as it can, usually the following:

1.Determine whether its device actually generated the interrupt and return FALSE as soon as possible if it did not.

2.Otherwise, stop the device from generating interrupts, save whatever context is necessary about the operation that caused the interrupt, and queue a DPC to complete the interrupt-driven I/O operation at a lower IRQL.

For more information about DPC objects, see Section 3.6, next.

For more information about using an interrupt spin lock, KeInitializeSpinLock, and KeSynchronizeExecution, see the section on spin locks in Chapter 16. For more information about ISRs and SynchCritSection routines, see also Chapters 4, 8, and 10.