I/O Request Packets (IRPs)
I/O Request Packets (IRPs) are fundamental data structures used by the Windows operating system's I/O subsystem to communicate I/O operations between user-mode applications, the kernel, and device drivers. An IRP encapsulates all the information necessary to describe an I/O request, such as reading from or writing to a device, controlling a device, or querying device information.
What is an IRP?
An IRP is a dynamically allocated structure, typically defined by the IO_STACK_LOCATION
structure within the larger IRP
structure. Each driver in the I/O stack for a particular device receives a pointer to the same IRP. However, each driver typically uses its own IO_STACK_LOCATION
within the IRP to store context specific to its processing of the I/O request.
IRP Structure and Key Fields
The IRP
structure contains a variety of fields, but the most critical part for driver interaction is the array of IO_STACK_LOCATION
structures.
-
IO_STACK_LOCATION
: This structure represents a driver's view and handling of an I/O request. Each driver in the I/O path has its own stack location. Key fields withinIO_STACK_LOCATION
include:MajorFunction
: Specifies the type of I/O operation (e.g., IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_DEVICE_CONTROL).MinorFunction
: Provides more specific details about the major function.Parameters
: A union containing parameters specific to the I/O operation (e.g., buffer pointers, lengths for read/write; control codes for device control).DeviceObject
: A pointer to the device object that the current driver is managing.FileObject
: A pointer to the file object associated with the request.CompletionRoutine
: A pointer to a routine that the driver can register to be called when the IRP is completed by lower drivers or the system.
-
IoStatus
: AnIO_STATUS_BLOCK
structure within the IRP that the driver fills with the status of the operation (e.g., success, failure, pending) and information about the number of bytes transferred.
IRP Flow and Processing
When an I/O operation is initiated (e.g., by a call to ReadFile
or DeviceIoControl
in user mode), the I/O Manager:
- Allocates an
IRP
structure. - Initializes the IRP's
IO_STACK_LOCATION
for the highest-level driver in the device stack. - Passes the IRP down the driver stack.
- Each driver in the stack processes the IRP, potentially modifying its own stack location and passing it to the next lower driver using
IoCallDriver
. - Once the IRP reaches the lowest driver (often a port driver or the hardware abstraction layer), it is processed, and the device operation is performed.
- The IRP then propagates back up the stack. Each driver that receives the IRP on its way up can perform completion processing (e.g., setting the final status, freeing resources).
Common IRP Major Function Codes
The MajorFunction
field in the IO_STACK_LOCATION
identifies the type of operation. Some common codes include:
IRP_MJ_CREATE
: Opening a device or file.IRP_MJ_CLOSE
: Closing a device or file.IRP_MJ_READ
: Reading data from a device.IRP_MJ_WRITE
: Writing data to a device.IRP_MJ_DEVICE_CONTROL
: Sending custom control codes to a device.IRP_MJ_QUERY_INFORMATION
: Querying information about a device or file.IRP_MJ_SET_INFORMATION
: Setting information about a device or file.IRP_MJ_PNP
: Plug and Play operations.IRP_MJ_POWER
: Power management operations.
Driver Responsibility
Device drivers must implement dispatch routines for the IRP major function codes they support. These routines are typically pointed to by entries in the driver's dispatch table. The driver's dispatch routine examines the MajorFunction
code to determine how to handle the request.
// Example of a driver's dispatch table entry (simplified)
NTSTATUS MyDriverDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
// ... Get IoStackLocation ...
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->MajorFunction) {
case IRP_MJ_READ:
// Handle read request
break;
case IRP_MJ_WRITE:
// Handle write request
break;
case IRP_MJ_DEVICE_CONTROL:
// Handle device control request
break;
// ... other cases ...
default:
// Unhandled request
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Irp->IoStatus.Status;
}
// ... more processing or passing down ...
}
Understanding IRPs is crucial for developing robust and efficient Windows device drivers. Their well-defined structure and managed flow allow for complex I/O operations to be handled reliably across a wide range of hardware.