IRPs coming in to an NT device driver’s Dispatch routines do not necessarily require the driver to do I/O processing on its device. Usually, each NT driver’s Dispatch routine(s) completes a subset of required requests, as well as those IRPs that have parameter errors in the driver’s I/O stack location, as described in Chapter 6.
However, every NT device driver with a StartIo routine is likely to call IoStartPacket from its Dispatch routines for IRP_MJ_READ and/or IRP_MJ_WRITE requests, and usually for a subset of the I/O control codes it supports for IRP_MJ_DEVICE_CONTROL requests.
A StartIo routine is the second stage in satisfying a device I/O request. When a Dispatch routine calls IoStartPacket with such an IRP, the I/O Manager either passes the IRP directly to the StartIo routine for further processing or inserts the packet into the device queue that was associated with the target device object when it was created.
As a general rule, a device driver that uses buffered I/O has a simpler StartIo routine than one that uses direct I/O. NT device drivers that use buffered I/O transfer small amounts of data for each transfer request, while those that use direct I/O (whether DMA or PIO) transfer large amounts of data to or from locked-down buffers that can span physical page boundaries in system memory.
Higher-level NT drivers layered above NT device drivers usually set up their device objects to match those of their respective device drivers. However, a highest-level NT driver, particularly NT file system drivers, can set up device objects for neither direct nor buffered I/O. For more information about setting up device objects for buffered and direct I/O, see Chapter 3.