Programmed I/O device drivers must perform extra initialization during DriverEntry (or AddDevice for WDM drivers). Similarly, the driver's Unload routine must be extended to remove the additional resources allocated. Initializing the Start I/O Entry PointIf a driver has a Start I/O routine, it must be announced during DriverEntry. This is done by storing the address of the Start I/O routine into the DriverStartIo field of the driver object, as in the following code fragment. NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath ) { : // Export other driver entry points // pDriverObject->DriverStartIo = StartIo; pDriverObject->DriverUnload = DriverUnload; pDriverObject->MajorFunction[ IRP_MJ_CREATE ] = DispatchOpenClose; : } An unitialized DriverStartIo field within the driver object results in an access violation (and blue screen crash) when the Dispatch routines invoke IoStartPacket. Initializing a DpcForIsr RoutineThe I/O Manager provides a simplified version of the DPC mechanism for use with standard interrupt processing. One special DPC object may be associated with each device object, DpcForIsr. To utilize this mechanism, a driver must call IoInitializeDpcRequest to associate the DpcForIsr routine with the Device object (typically from DriverEntry or AddDevice). During the actual interrupt service (ISR), the driver schedules the DPC by invoking IoRequestDpc. Of course, this simplified mechanism is quite restrictive. Some drivers require multiple DPCs for different circumstances. A subsequent chapter explains the process of creating custom DPCs for multiple purposes. Connecting to an Interrupt SourceAll interrupts are initially handled by the kernel of Windows 2000. As explained in chapter 1, this is done so that portability to multiple platforms can be easily achieved. The kernel dispatches interrupts to a driver's ISR by creation of and then connection to an interrupt object. These steps are accomplished by the I/O Manger's IoConnectInterrupt function, described in Table 8.3. The driver's ISR address is passed (along with nine other input parameters) to this function so that the kernel associates a specific hardware interrupt with it. IoConnectInterrupt returns a pointer to an interrupt object (via the first argument). This pointer should be saved in the Device or Controller Extension since it will be needed to ultimately disconnect from the interrupt source or to execute any SynchCritSection routines.
The use of interrupt objects requires care in several areas. First, if an ISR handles more than one interrupt vector, or if a driver has more than one ISR, a spin lock must be supplied to prevent collisions over the ISR's ServiceContext. Second, if the ISR manages more than one interrupt vector, or a driver has more than one ISR, ensure that the value specified for SynchronizeIrql is the highest DIRQL value of any of the vectors used. Finally, a driver's Interrupt Service routine must be ready to run as soon as IoConnectInterrupt is called. Clearly, interrupts at the IRQL specified may preempt any additional initialization attempted by a driver, and the ISR must be able to handle these interrupts correctly. In general, the following sequence should be used:
Disconnecting from an Interrupt SourceIf a driver is capable of being unloaded, it needs to detach its Interrupt Service routine from the kernel's list of interrupt handlers before the driver is removed from memory. If the device generates an interrupt after the driver is unloaded, the kernel will try to call the address in nonpaged pool where the ISR used to live. This results in a system crash. Disconnecting from an interrupt is a two-step procedure. First, use KeSyncrhonizeExecution and a SynchCritSection routine to disable the device and prevent it from generating any further interrupts. Second, remove the ISR from the kernel's list of handlers by passing the device's interrupt object to IoDisconnectInterrupt.
|