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.

Connecting to Interrupts

A WDM driver receives its interrupt resource assignments in its Plug and Play IRP StartDevice handler. Each interrupt has these details:

• 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 IoConnectInterrupt when you are ready to handle interrupts.

First, declare a PKINTERRUPT object that will be initialized by IoConnectInterrupt. Like most drivers, WdmIo declares this in its device extension. However, if all your devices use the same interrupt, this object may well need to be a driver global; you would need to connect to the interrupt only once (e.g., in your DriverEntry routine).

Table 17.1 shows the parameters to pass to IoConnectInterrupt. Apart from the various assigned interrupt values, you must pass the name of your interrupt handling routine and a context to pass to it. The WdmIo driver uses the device extension pointer as this context parameter. If all your devices share an interrupt, you will need to use a different context pointer; the interrupt handler will have to work out for itself to which device the interrupt refers.

The only complication to IoConnectInterrupt is what happens when you use more than one interrupt. You need to call IoConnectInterrupt once for each interrupt, using the same initialized spin lock pointer in each call. This is used to resolve tensions between the various handlers so that only one is called at once. Pass the highest DIRQL value in the SynchronizeIrql parameter in each call.

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 IoDisconnectInterrupt before your device disappears. And you must disconnect from the interrupt if the Plug and Play system stops your device. The WdmIo driver always stops its device when the device is about to be removed, so the StopDevice routine in DeviceIo.cpp is the only place where WdmIo disconnects its interrupt handler.

The WdmIo driver connects to its interrupt handler when it processes a PHDIO_IRQ_CONNECT command in its ProcessCmds routine. It remembers if it connected successfully in the device extension ConnectedToInterrupt field. If the device is stopped for Plug and Play resource reallocation, this flag is left true after IoDisconnectInterrupt is called. When the device is restarted, WdmIo reconnects to the new interrupt if ConnectedToInterrupt is true.

The actual call to IoConnectInterrupt is done in a system worker thread as this function must be called at PASSIVE_LEVEL IRQL. In this case, it is the work item that completes the IRP and starts the next IRP using IoStartNextPacket.

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 ConnectedToInterrupt field, as mentioned before, indicates whether WdmIo

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×