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):
-
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
-
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).
-
The I/O Manager calls KeInitializeInterrupt for each interrupt object.
-
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.
-
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.
-
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:
-
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).
-
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:
-
Determine whether its device actually generated the interrupt and return FALSE
as soon as possible if it did not.
-
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.