Assuming its DispatchRead, DispatchWrite, or DispatchDeviceControl routine determined that a request was valid and called IoStartPacket, a device driver’s StartIo routine is called to process the packet next. If the packet is queued by IoStartPacket, eventually a call to IoStartNextPacket from the driver’s DpcForIsr or CustomDpc routine causes the StartIo routine to be called with the dequeued IRP.
The StartIo routine calls IoGetCurrentIrpStackLocation and determines which operation must be performed to satisfy the request. It preprocesses the IRP in any way necessary, such as splitting up a large DMA transfer request into partial-transfer ranges and saving state about the Length of an incoming transfer request that must be split, before programming the physical device to carry out the I/O request.
The StartIo routine must use a driver-supplied SynchCritSection routine to perform the necessary programming if access to the physical device (and/or the device extension) must be synchronized with the driver’s ISR. For more information, see Section 7.2.3.
Any NT device driver that uses direct I/O either reads data into or writes data from a locked-down buffer, described by a memory descriptor list (MDL), that the driver finds in the IRP at Irp->MdlAddress. Such a driver commonly uses buffered I/O for device control requests. For more information, see Section 7.2.2.3.
NT drivers cannot use the address mappings contained in any MDL, including an MDL at Irp->MdlAddress, directly.
Instead, NT device drivers that use PIO remap user-space buffers by calling MmGetSystemAddressForMdl with the Irp->MdlAddress. NT device drivers that use DMA also pass Irp->MdlAddress to support routines during their transfer operations to have the buffer addresses remapped to logical ranges for their devices.
Unless a closely coupled higher-level driver pre-splits large DMA transfer requests for the underlying device driver, an NT device driver’s StartIo routine must split up each transfer request that is larger than its device can manage in a single transfer operation. NT device drivers that use system DMA are required to split transfer requests that are too large for the system DMA controller or for their device(s) to handle in a single transfer operation.
If the device is a slave DMA device, its driver must synchronize transfers through a system DMA controller with a driver-allocated adapter object, representing the DMA channel, and a driver-supplied AdapterControl routine. The driver of a busmaster DMA device also must use a driver-allocated adapter object to synchronize its transfers and must supply an AdapterControl routine if it uses the system’s packet-based DMA support.
Depending on an NT device driver’s design, it might synchronize transfer and device control operations on a physical device with a controller object and supply a ControllerControl routine.
For more information about DMA transfers, adapter objects, and controller objects, see Chapter 3. For more information about driver-supplied AdapterControl and ControllerControl routines, see also Chapter 11.