As described previously, Windows provides the initial interrupt handler for IRQ0-IRQ15. If your driver has connected to one of these interrupts (IRQ7 in the default case for WdmIo), it calls your interrupt handling routine to service the interrupt. More than one device can share the same interrupt line, so your first job is to determine if it really was your device that interrupted the processor.
Most hardware has a mind of its own, especially when riddled with by users, and so is likely to generate interrupts at the most awkward time. In particular, you can guarantee that the interrupts will not occur in the context of the user mode thread that should receive its data. Note that an interrupt may occur at any point in your driver code, so you have to be especially careful that you can cope.
Naturally, the Windows kernel helps considerably. In particular, Critical Section routines forcibly increase the IRQ level (IRQL) to the appropriate Device IRQL (DIRQL) so that an interrupt cannot intervene while your Critical Section routine is running. In an x86 system, it sets the appropriate bit in the processor status register that disables INTR interrupts.
Most devices that interrupt have some similar sort of interrupt disabling facility. At power up, a device's interrupts are usually disabled. However, it is best to disable your device's interrupts at the first possible opportunity. Once your driver has installed its interrupt handler, you can enable interrupts in the device. Even now, you should note that interrupts may not conveniently arrive after you have started processing your read request. From the word go, a modem might start interrupting with incoming data. Your driver may well not have received a Read IRP yet, so you will have to decide what to do with these incoming bytes. You could buffer them somewhere in the expectation of a Read IRP coming round the corner. Or you could just dump them on the floor.
A WDM driver receives its interrupt resource assignments in its Plug and Play IRP
• Interrupt level[42]
• Interrupt vector
• Interrupt affinity: a set of bits indicating which processors it can use in a multiprocessor system
• Interrupt mode: latched or level sensitive
Usually, you just need to store these details and pass them on to
First, declare a PKINTERRUPT object that will be initialized by
Table 17.1 shows the parameters to pass to
The only complication to
Table 17.1 IoConnectInterrupt function
NTSTATUS IoConnectInterrupt | (IRQL==PASSIVE_LEVEL) |
---|---|
Parameter | Description |
OUT PKINTERRUPT *InterruptObject | Interrupt object pointer |
IN PKSERVICE_ROUTINE ServiceRoutine | Name of interrupt handling routine |
IN PVOID ServiceContext | Context to pass to the interrupt handler, usually the device extension pointer |
IN PKSPIN_LOCK SpinLock | Optional spin lock parameter used when a driver uses more than one interrupt; NULL, otherwise |
IN ULONG Vector | Assigned interrupt vector |
IN KIRQL Irql | Assigned interrupt IRQL |
IN KIRQL SynchronizeIrql | The highest IRQL of the interrupts that a driver uses (i.e., usually the same as IRQL) |
IN KINTERRUPT_MODE InterruptMode | Assigned LevelSensitive or Latched value |
IN BOOLEAN ShareVector | TRUE if the interrupt vector is shareable |
IN KAFFINITY ProcessorEnableMask | Assigned interrupt affinity |
IN BOOLEAN FloatingSave | TRUE if the floating-point stack should be saved For x86 systems, this value must be FALSE |
Do not forget to disconnect your interrupt handler using
The WdmIo driver connects to its interrupt handler when it processes a PHDIO_IRQ_CONNECT command in its
The actual call to
WdmIo Reads and Writes
Before I look at its interrupt handler, let's see how WdmIo starts and runs its interrupt-driven reads and writes.
Listing 17.1 shows the mass of extra fields that are needed in the device extension. The