Passing an IRP to the next lower driver (also called forwarding an IRP) is the IRP equivalent of a subroutine call. When a driver forwards an IRP, it must populate the next I/O stack location with parameters, advance the IRP stack pointer, and invoke the next driver’s dispatch routine. In essence, the driver is calling down the IRP stack.
To pass an IRP, a driver typically takes the following steps:
1. Set up the parameters for the next I/O stack location. The driver can either:
· Call the IoGetNextIrpStackLocation routine to get a pointer to the next I/O stack location, and then copy the required parameters to that location.
· Call the IoCopyCurrentIrpStackLocationToNext routine (if the driver sets an IoCompletion routine in step 2), or the IoSkipCurrentIrpStackLocation routine (if the driver does not set an IoCompletion routine in step 2) to pass the same parameters used for the current location.
Note: Drivers must not use the RtlCopyMemory routine to copy the current parameters. This routine copies the pointer to the current driver’s IoCompletion routine, thus causing the IoCompletion routine to be called more than once.
2. Set an IoCompletion routine for post-processing, if necessary, by calling the IoSetCompletionRoutine routine. If the driver sets an IoCompletion routine, it must call IoCopyCurrentIrpStackLocationToNext in step 1.
3. Pass the request to the next driver by calling the IoCallDriver routine. This routine automatically advances the IRP stack pointer and invokes the next driver’s dispatch routine.
After a driver passes the IRP to the next driver, it no longer owns the IRP and must not attempt to access it. The IRP could be freed or completed by another driver or on another thread. Attempting to access an IRP in such a situation can cause a system crash. If the driver requires access to the IRP after passing it down the stack, the driver must set an IoCompletion routine. When the I/O Manager calls the IoCompletion routine, the driver regains ownership of the IRP for the duration of the IoCompletion routine. Thus the IoCompletion routine can access the fields in the IRP.
If the driver’s dispatch routine must also process the IRP after lower drivers have completed it, the IoCompletion routine must return STATUS_MORE_PROCESSING_REQUIRED, which returns ownership of the IRP to the dispatch routine. As a result, the I/O Manager stops processing the IRP and leaves the ultimate completion of the IRP to the dispatch routine. The dispatch routine can later call IoCompleteRequest to finish completion or can mark the IRP pending for further processing.