9.2 DpcForIsr and CustomDpc Functionality
In addition to the basic responsibilities of these routines, already mentioned in Sections 9.1.2 and 9.1.3, every DpcForIsr or CustomDpc routine is responsible for using ISR-provided context to complete one or more interrupt-driven I/O operations.
In other words, besides ensuring that the next device I/O operation is started promptly and completing the current IRP, the work done by any DpcForIsr or CustomDpc routine depends on the driver's design and/or on the nature of its device.
For example, the DpcForIsr or CustomDpc routine also can do any of the following in order to complete an interrupt-driven I/O operation:
·Retry an operation that has timed out or failed.
·Call IoAllocateErrorLogEntry, set up an error log packet to report a device I/O error, and call IoWriteErrorLogEntry.
For more information about handling I/O errors, see Chapter 16.
·If the driver uses buffered I/O or the current IRP requests a device control operation, transfer data read in from the device to the system buffer at Irp->AssociatedIrp.SystemBuffer before completing the current IRP.
·If the driver uses direct I/O, save state about a just completed partial-transfer operation, calculate the next partial-transfer range, and use a driver-supplied SynchCritSection routine to program the device for the next partial-transfer operation.
Even a driver that uses buffered I/O might have to split up a transfer request if its device has very limited transfer capabilities.
·If the driver uses packet-based DMA, call IoFlushAdapterBuffers after each device transfer operation, and call IoFreeAdapterChannel or IoFreeMapRegisters when a sequence of partial transfers is done and the full transfer request is satisfied.
If a requested transfer is only partly satisfied by a single DMA operation, the DpcForIsr is usually responsible for setting up one or more DMA operations until the current IRP's requested Length (number of bytes) has been fully transferred.
For more information about using DMA, see Chapter 3. See also Chapter 11 for more information about AdapterControl routines and Chapter 16 for more information about maintaining cache coherency during DMA.
·If the driver uses PIO, call KeFlushIoBuffers at the end of each device transfer operation if the current IRP requests a read.
If a requested transfer is only partly satisfied by a single PIO operation, the DpcForIsr (or CustomDpc) is usually responsible for setting up one or more transfer operations until the current IRP's requested Length (number of bytes) has been fully transferred.
For more information about using PIO, see Chapter 3. See also Chapter 16 for more information about maintaining processor cache coherency during PIO reads.
·If the driver has a ControllerControl routine, call IoFreeController when a requested operation is complete.
For more information about controller objects, see Chapter 3. For more information about ControllerControl routines, see Chapter 11.
Note that an NT device driver's DpcForIsr (or CustomDpc) routine usually does most of the driver's device I/O processing to satisfy IRPs. This routine also shares some of the responsibility for queueing IRPs to the device with the driver's Dispatch routines.
NT device driver writers should consider the following a general design guideline
Any DpcForIsr or CustomDpc routine should call IoStartNextPacket as soon as it can safely make this call: that is, without possibly causing a resource conflict or race condition with the driver's StartIo routine or with any other routine the StartIo routine causes to run.
If a driver manages its own queueing of IRPs, its DpcForIsr or CustomDpc routine should notify the driver as soon as it is safe to dequeue the next IRP and to set up the device for the next request.
A DpcForIsr or CustomDpc routine must call IoStartNextPacket, or otherwise notify the appropriate driver routine when device I/O processing for the next request can be started, before the DpcForIsr or CustomDpc returns control. Depending on the driver and its device, this can occur well before the DpcForIsr or CustomDpc routine completes the current IRP with IoCompleteRequest, or it can occur immediately before this routine completes the current IRP and returns control.