Once the device operation begins, the actual data transfer is driven by the arrival of hardware interrupts. When an interrupt arrives, the driver's Interrupt Service routine acknowledges the request and either transfers the next piece of data or invokes a DPC routine. Execution ContextWhen the kernel receives a device interrupt, it uses its collection of interrupt objects to locate an ISR willing to service the event. It does this by running through all the interrupt objects attached to the DIRQL of the interrupt and calling ISRs until one of them claims the interrupt. The kernel interrupt dispatcher calls an ISR at the synchronization IRQL specified in the call to IoConnectInterrupt. Usually this is the DIRQL level of the device. The kernel dispatcher also acquires and releases the device spin lock. Running at such a high IRQL, there are several things an ISR isn't allowed to do. In addition to the usual warning about page faults, an ISR shouldn't try to allocate or free various system resources (like memory). If system support routines must be called from an ISR, check for restrictions on the level of which they can be run. Such calls might require delegation to a DPC routine. As shown in Table 8.5, the kernel passes a pointer to whatever context information was identified in the original call to IoConnectInterrupt. Most often, this is a pointer to the Device or Controller Extension. What the Interrupt Service Routine DoesThe Interrupt Service routine is the real workhorse in a programmed I/O driver. In general, one of these routines does the following:
Always code an ISR for speed. Any work that isn't absolutely essential should go in a DPC routine. It is especially important that an ISR determine whether or not it will handle an interrupt immediately. There may be a number of other ISRs waiting in line for a given interrupt, and nonessential preprocessing blocks their proper operation.
|