10.2.1 Programming the Device for an I/O Operation
For any incoming IRP, NT device drivers do as much I/O processing as possible
at IRQL PASSIVE_LEVEL in their Dispatch routines (or, possibly driver threads)
or at IRQL DISPATCH_LEVEL, as in their StartIo routines, before they program
their devices for an I/O operation.
In addition, a device driver’s StartIo routine must postpone its call to KeSynchronizeExecution
with a SynchCritSection routine that programs the device to the following
standard driver routines:
-
If an NT driver sets up a controller object to represent a device controller
with attached devices, its StartIo routine cannot program the device
controller, which might already be in use for an I/O operation on another
attached device, until the physical controller is not busy.
-
Such a driver’s StartIo routine must call IoAllocateController with its
ControllerControl routine to synchronize operations through the physical
controller.
-
Only then can its ControllerControl routine call KeSynchronizeExecution
with a SynchCritSection routine that programs the physical controller for the
requested operation to/from the target device.
If a transfer operation through such a driver’s controller does not fully
satisfy a particular IRP, the driver’s DpcForIsr routine also must call KeSynchronizeExecution
with the same SynchCritSection routine to reprogram the physical controller
for each additional transfer necessary to satisfy the current IRP.
For more information about controller objects, see Chapter 3. For more
information about ControllerControl routines, see also Chapter 11.
-
If an NT driver sets up an adapter object for system or packet-based busmaster
DMA, its StartIo routine (or possibly, driver-created thread or
ControllerControl routine) cannot program a slave device until the system DMA
controller, which might be busy transferring data for another slave device, is
available or until the system has allocated the DMA map registers for such a
driver’s busmaster adapter.
-
Such a driver’s StartIo routine must call IoAllocateAdapterChannel with
its AdapterControl routine in order to synchronize operations through the
system DMA controller or the driver’s busmaster DMA adapter.
-
Only then can its AdapterControl routine call KeSynchronizeExecution
with a SynchCritSection routine that programs the driver’s device for a DMA
transfer.
If a DMA transfer operation on such a driver’s device/adapter does not fully
satisfy a particular IRP, the driver’s DpcForIsr routine also must call KeSynchronizeExecution
with the same SynchCritSection routine to reprogram the device/adapter for
each additional DMA transfer necessary to satisfy the current IRP.
For more information about adapter objects and how to stage DMA processing,
see Chapter 3. For more information about AdapterControl routines, see also
Chapter 11.
NT device driver writers
should consider the following general guidelines for designing, implementing,
and calling SynchCritSection routines that program the device for I/O
operations
-
A device driver should do all preprocessing for an IRP, set up any necessary
state for other driver routines that also process device I/O requests, and
acquire all necessary resources, such as a access to a device controller,
system DMA channel, or set of DMA map registers, before it calls KeSynchronizeExecution
to have a driver-supplied SynchCritSection routine program the device for an
I/O operation.
-
A SynchCritSection routine that programs the device for I/O operations must
return control as quickly as possible. For this reason, such a
SynchCritSection routine should do only what is necessary to set up the device
for I/O.
-
A device driver can have a set of SynchCritSection routines to program the
device, if necessary. That is, the driver of a device for which setting up a
read request differs markedly from setting up certain device control requests
might have separate SynchCritSection routines to program its device for each
type of request.
-
An NT device driver should not have a single, large, general-purpose
SynchCritSection routine with a switch statement or many nested if..then..else
statements to determine what operations it will carry out to program the
device for an I/O operation and/or what state information to update. On the
other hand, a driver also should avoid having a set of many SynchCritSection
routines, each responsible for programming only a single device register.
Every SynchCritSection routine must return control as quickly as possible,
because running any SynchCritSection routine prevents the driver’s ISR from
getting any work done if it is running concurrently.