Книга: Writing Windows WDM Device Drivers

IRP Structure

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 MajorFunction is the IRP code (e.g., IRP_MJ_READ for a Read IRP). Some IRPs, such as IRP_ MJ_PNP, use the MinorFunction field to specify which particular Plug and Play function is being requested.

Each common IRP type has a struct within the Parameters union in the IO_STACK_LOCATION structure. For example, for Read IRPs, the Parameters.Read.Length field is the number of bytes to transfer. The parameters that are valid for each common IRP are described in the following text.

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.

I/O Stack Locations

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 DriverContext[4] in Tail.Overlay. Use the following code to access the first of these locations.

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.

Оглавление книги


Генерация: 0.991. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз