The first technique to make your routine reentrant is to use local variables. Each separate call to Wdm1Read definitely has its own separate set of variables on the kernel stack.
Do not use global variables or variables in the device extension unless you protect access to them. However, when these variables are first set up (DriverEntry or AddDevice), you do not need to take special precautions.
The Wdm1 driver uses a
There are two other main techniques to achieve reentrancy in dispatch routines. The first is to use the services of the I/O Manager to create a device queue of IRPs. The IRPs in the device queue are passed one at a time to a
The second technique is to use Critical section routines if your device interrupts the computer, as explained in full in Chapter 16. Calling
There are three main techniques for handling IRPs.
• Handle immediately
• Put in a queue and process one by one
• Pass down to a lower-level driver
Only the simplest drivers, like Wdm1, can handle IRPs straightaway. Even so, you must still take precautions to ensure that your driver dispatch routines are reentrant.
Drivers that access real pieces of hardware usually want to serialize access to the hardware. As mentioned previously, the I/O Manager in the kernel provides a device queue that you can use. Dispatch routines call the kernel to put an IRP into the device queue. The I/O Manager calls your
Drivers might want to use more than one device queue. For example, a serial port driver will usually want to have two device queues; one for incoming read data and one for outgoing write data.
A driver can use a different queuing strategy. For example, the
The final main technique for handling IRPs is to pass them down for handling in a lower-level driver. This approach is common in WDM device drivers. Some drivers simply pass the handling of some IRPs to the next lower driver and forget about them. If you take a more active interest, you can inspect the results after all the lower-level drivers have handled the IRP. These techniques are covered later.
A variant on this theme is for a driver to build a new IRP (or IRPs), send it down to lower drivers, and process the results afterwards.
When a driver has finished working on an IRP, it must tell the I/O Manager. This is called IRP
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = BytesTxd;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
Finally,
If a dispatch routine does not process an IRP immediately, it must mark the IRP as pending using
If an IRP is queued and its associated process dies unexpectedly (or calls CancelIo to cancel overlapped IRPs), these pending IRPs must be cancelled. You must set an IRP's cancel routine to handle this circumstance, and to handle the Cleanup IRP. Chapter 16 describes these options in full. Wdm1 does not queue IRPs, so handling these options is not necessary.
IRP Structure
Figure 7.1 shows that an I/O Request Packet consists of a header IRP structure followed by a series of stack locations, each an IO_STACK_LOCATION structure. The information in the header and the current IRP stack location tell a driver what to do.
Figure 7.1 IRP overview
Table 7.2 lists some of the fields in the IRP header structure that a driver can access, while Table 7.3 gives the general layout of the IO_STACK_LOCATION structure.
Table 7.2 Some IRP structure fields
Field | Description |
---|---|
IO_STATUS_BLOCK IoStatus | Completion status of IRP |
PVOID AssociatedIrp.SystemBuffer | System space buffer (for Buffered I/O) |
PMDL MdlAddress | Memory Description List (for Direct I/O) |
BOOLEAN Cancel | Set if IRP has been cancelled |
ULONG Flags | IRP Flags |
Table 7.3 Some IO_STACK_LOCATION structure fields
typedef struct _IO_STACK_LOCATION {
UCHAR MajorFunction;
UCHAR MinorFunction;
// …
union {
struct { … } Create;
struct { … } Read;
struct { … } Write;
struct { … } DeviceIoControl;
} Parameters;
// …
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
The fact that there is an IRP header and several associated I/O stack locations can be a source of confusion. However, the stack locations are a powerful tool for processing IRPs when several layered drivers have to access an IRP in turn.
The I/O stack location contains most of the important information about the IRP. The