How Programmed IO Works

< BACK  NEXT >
[oR]

How Programmed I/O Works

This 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/O

In 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.

  1. An IRP request (typically an IRP_MJ_READ or IRP_MJ_WRITE) determines that device interaction is required to complete the request. The Dispatch routine queues the IRP for eventual delivery to the driver's Start I/O routine.

  2. The Start I/O routine performs any necessary preprocessing and setup based on the IRP's function code. It then starts the device, typically by writing or reading the first word of device data.

  3. Eventually, the device generates an interrupt, which the kernel passes to the driver's Interrupt Service Routine (ISR).

  4. If there is additional data for the driver to download, the ISR starts the next transfer. Steps 3 and 4 repeat until the entire data transfer specified in the request is complete.

  5. When the entire transfer is complete, the ISR queues a request to fire the driver's DpcForIsr routine. As described in chapter 3, DPC routines run at a lower IRQL level than the ISR.

  6. The I/O Manager's DPC dispatcher eventually runs the DpcForIsr routine scheduled by the ISR. The DpcForIsr routine marks the IRP as complete and informs the I/O Manager that another waiting IRP can now be presented to the Start I/O routine, repeating the entire cycle.

Synchronizing Driver Routines

As is apparent from the programmed I/O process just described, there are at least four separate code paths executing at three different IRQL levels.

  • The original I/O request is handled by a Dispatch routine running at PASSIVE_LEVEL IRQL.

  • The driver's Start I/O routine runs at DISPATCH_LEVEL IRQL.

  • The driver's ISR runs at the device's DIRQL level.

  • The driver's DpcForIsr routine runs at DISPATCH_LEVEL IRQL.

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.

Table 8.1. Function Prototype for a SynchCritSection Routine
BOOLEAN SynchCritSection IRQL == DIRQL
Parameter Description
IN PVOID pContext Context passed to KeSynchronizeExecution
Return value TRUE success
FALSE something failed

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.

Table 8.2. Function Prototype for KeSynchronizeExecution
BOOLEAN KeSynchronizeExecution IRQL < DIRQL
Parameter Description
IN PKINTERRUPT pInterruptObj Pointer to interrupt object
IN PKSYNCHRONIZE_ROUTINE pRoutine SynchCritSection callback routine
IN PVOID pContext Argument passed to SynchCritSection
Return value Value returned by SynchCritSection

< BACK  NEXT >


The Windows 2000 Device Driver Book(c) A Guide for Programmers
The Windows 2000 Device Driver Book: A Guide for Programmers (2nd Edition)
ISBN: 0130204315
EAN: 2147483647
Year: 2000
Pages: 156

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net