Each common IRP type has a struct within the
The key to understanding I/O stack locations is to realize that each driver needs to look at only one, the 'current stack location'. The information in the IRP header structure and the information in the current stack location are the parameters that a driver uses to process an IRP.
Let's go off track slightly to ask why Microsoft provides a set of I/O stack locations. If you have a stack of drivers that process an IRP, the highest might be a network protocol driver that can accept read requests of any length. This driver might know that the underlying transport driver can only cope with read transfers of up to 1024 bytes. Its job is to break up long transfers into a series of blocks, each with a maximum size of 1024 bytes. When the protocol driver calls the transport driver it, sets up the next I/O stack location with the transfer size set to 1024. When the transport driver processes the IRP, its current I/O stack location has this value, and it should proceed happily to process the IRP. When it has finished, the IRP is passed back to the protocol driver. This checks that the transfer worked and — assuming it did — sets up the next transfer and calls the transport driver again.
In this approach, the protocol driver sends the transport driver IRPs one by one. However, a more sophisticated protocol driver could allocate new IRPs, enough to move all the data. It could then issue them all to the transport driver. The protocol driver would have to check carefully that all the IRPs finished correctly. When all the data has been moved, one way or another, the protocol driver can finally complete its own original IRP.
This example reveals a problem. The field that contains the pointer to the data to be transferred is not in the I/O stack location, but in the IRP header. The transfer length is in the stack. Surely the data pointer ought to be in there as well. The fact that the original stack location is not changed by the call to the lower driver is good, as it makes it easier for a driver to remember how many bytes to transfer.
However, this reveals another common difficulty with IRPs — determining where to store a driver's own information about an IRP. Suppose the protocol driver wanted to remember something simple (e.g., how many bytes it had sent so far)[15].
The ideal place for some storage for drivers would be the I/O stack location. However, there is no space specifically reserved for drivers. Nonetheless, Read IRPs have a ULONG at Parameters.Read.Key in the current I/O stack location that can be used safely, although the DDK does not specifically say so. Write IRPs have a similar ULONG at Parameters.Write.Key.
There is some room in the IRP header that can be used by drivers — a
PVOID p = Irp->Tail.Overlay.DriverContext[0];
However, it is not safe to use these locations for storing context while an IRP is processed by lower drivers, for the simple reason that these other drivers may use this memory too.
The final point to note about I/O stack locations is that you can use different major function code when you call a lower driver. For example, you might implement a read request by sending the lower driver an IOCTL.
Common IRP Parameters
This section lists the parameters that are set for the common IRPs. In the following discussion, 'the stack' means the current I/O stack location.
The main parameter of interest to the Create IRP handler is the
Other parameters to the Create IRP are given in the Parameters.Create structure in the stack, such as the
If need be, you can double-check that the
If you have queued up Read or Write IRPs for this file, the I/O Manager will have cancelled them before the close request is received. It does this by calling an IRP's Cancel routine and issuing a Cleanup IRP, as described in Chapter 16.
The Parameters.Read structure in the IRP stack has
The user buffer can be specified in one of two ways, depending on whether your driver uses
The
The parameters for Write IRPs are identical to Read IRPs, except that the relevant parameters are in the stack Parameters.Write structure.
The Parameters.DeviceIoControl structure in the IRP stack has
The user buffer is specified using one or more of the
User Buffers
As a driver can run in the context of any thread, a plain pointer into the user's address space is not guaranteed to access the correct memory.
A driver can use two main methods to access the user's buffer properly, either Buffered I/O or Direct I/O. When you create a device, you must set the DO_BUFFERED_IO bit in the
If you use Buffered I/O, the kernel makes the user's buffer available in some nonpaged memory and stores a suitable pointer for you in the
This technique is the easiest one for driver writers to use. However, it is slightly slower overall, as the operating system usually will have to copy the user buffer into or out of non-paged memory.
It is faster to use a Memory Descriptor List (MDL). However, this is only available to hardware that can perform Direct Memory Access (DMA). DMA and MDLs are not explained in this book, although Chapter 24 lists the changes in W2000 for those of you who have used DMA in NT 4 and earlier.
The MDL of the user's buffer is put in the
A final and uncommon technique for accessing a user's buffer is to use neither Buffered I/O nor Direct I/O. In this case, the user's buffer pointer is simply put in the
DeviceIoControl requests can use a combination of these user buffer access techniques. Each IOCTL can use a different method, if need be. However, most drivers simply use Buffered I/O, as IOCTL buffers are usually fairly small. I shall show how to define IOCTLs shortly.
The
If you use METHOD_BUFFERED,
For both METHOD_IN_DIRECT and METHOD_OUT_DIRECT the
Finally, for METHOD_NEITHER, the input buffer user space pointer is put in
Wdm1 Dispatch Routines
The dispatch routines for the Wdm1 driver are in the file Dispatch.cpp, which is available on the book CD-ROM. These routines all run at PASSIVE_LEVEL IRQL, so they can be put in paged memory. All the basic dispatch routines complete the IRP straightaway.
The dispatch routines include various
Listing 7.1