How Programmed I/O WorksThis section describes the events that occur during a programmed I/O operation as well as the actions that a driver must take in response to these events. What Happens During Programmed I/OIn a programmed I/O operation, the CPU transfers each unit of data to or from the device in response to an interrupt. The following sequence of events occurs.
Synchronizing Driver RoutinesAs is apparent from the programmed I/O process just described, there are at least four separate code paths executing at three different IRQL levels.
If any of these code paths share registers or memory areas, synchronization is required. Without such protection, an interrupt might arrive while a lower-level IRQL routine is using the shared resource, and an inconsistent state could result. The solution to this problem is to place code that touches shared resources in a SynchCritSection routine. Table 8.1 shows the prototype for one of these routines. This technique for synchronization requires that the code which touches the shared resource be placed in an appropriate SynchCritSection routine. When the shared resource is required, a code path invokes KeSynchronizeExecution (see Table 8.2), which takes the function address of the SynchCritSection as an argument. This kernel function raises the IRQL level of the calling code path to the DIRQL level of the device's interrupt object, acquires the object's spin lock, and then calls the supplied SynchCritSection routine.
While the SynchCritSection is running, it cannot be interrupted by any code running at DIRQL or below (which includes the device's ISR) and is thus guaranteed temporary exclusive access to the resource. When the SynchCritSection routine returns, KeSynchronizeExecution releases the spin lock, drops IRQL back to its original level, and then returns to the caller. Notice that the call to KeSynchronizeExecution receives avoid* argument, pContext. This single argument is passed to the subsequent call to the SynchCritSection routine. It allows passing of instance data to the callback routine and is typically a pointer to the device or controller extension.
|