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.
1.Such a driver’s StartIo routine must call IoAllocateController with its ControllerControl routine to synchronize operations through the physical controller.
2.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.
1.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.
2.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.