Passing IRPs down the Driver Stack
When a driver's dispatch routine receives an IRP, it must call IoGetCurrentIrpStackLocation so that it can check its own I/O stack location and determine that any parameters are valid. If the driver cannot satisfy and complete the request itself, it can do one of the following:
Pass the IRP on for further processing by lower-level drivers.
Create one or more new IRPs and pass them down to lower-level drivers.
A higher-level driver should pass an I/O request on to a next-lower driver as follows:
If the driver will pass the input IRP on to the next lower-level driver, the dispatch routine should call IoSkipCurrentIrpStackLocation or IoCopyCurrentIrpStackLocationToNext to set up the I/O stack location of the next-lower driver.
If the driver calls IoAllocateIrp to allocate one or more additional IRPs for lower drivers, the dispatch routine must initialize the next-lower driver's I/O stack location by following the steps that are described in Processing IRPs in an Intermediate-Level Driver.
The dispatch routine can modify some of the parameters in the next-lower driver's I/O stack location for certain requests. For example, a higher-level driver might modify the parameters for a large transfer request when the underlying device has a known limit in transfer capacity, and reuse the IRP to send partial-transfer requests to the underlying device driver.
Call IoSetCompletionRoutine.
If the dispatch routine is passing a received IRP to the next-lower driver, setting an IoCompletion routine is optional but useful, because the routine can perform such tasks as determining how lower drivers completed the request, reusing the IRP for partial transfers, updating whatever state the driver maintains if it tracks IRPs, and retrying a request that was returned with an error.
If the dispatch routine has allocated new IRPs, setting an IoCompletion routine is required because the routine must release each IRP after lower drivers have completed it.
For more information about IoCompletion routines, see Completing IRPs.
Call IoCallDriver with each IRP to be processed by lower drivers.
Return an appropriate NTSTATUS value, such as:
STATUS_PENDING
The driver usually returns STATUS_PENDING if the input IRP is an asynchronous request, such as IRP_MJ_READ or IRP_MJ_WRITE.
The result of the call to IoCallDriver
The driver frequently returns the result of the call to IoCallDriver if the input IRP is a synchronous request, such as IRP_MJ_CREATE.
A lowest-level device driver passes any IRP that it cannot complete in its dispatch routine on to other driver routines as follows:
Call IoMarkIrpPending with the input IRP.
Call IoStartPacket to pass on or queue the IRP to the driver's StartIo routine, unless the driver manages its own internal IRP queuing, as described in Driver-Managed IRP Queues.
If the driver does not have a StartIo routine but handles cancelable IRPs, it must either register a Cancel routine or implement a cancel-safe IRP queue. For more information about Cancel routines, see Canceling IRPs.
Return STATUS_PENDING.