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.

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.

Create IRP, IRP_MJ_CREATE

The main parameter of interest to the Create IRP handler is the FileObject field in the stack. This is a pointer to a _FILE_OBJECT structure. The FileName field in here is a UNICODE_STRING with any characters after the basic device name. If you appended file to the symbolic link name found in the GetDeviceViaInterface routine in Wdm1Test, file would appear in FileName. If no characters are appended, FileName has a length of zero.

Other parameters to the Create IRP are given in the Parameters.Create structure in the stack, such as the FileAttributes and ShareAccess.

Close IRP, IRP_MJ_CLOSE

If need be, you can double-check that the FileObject in the IRP header matches the one you were sent in the create request.

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.

Read IRP, IRP_MJ_READ

The Parameters.Read structure in the IRP stack has Length and ByteOffset fields that say how many bytes are requested and the file pointer. ByteOffset is a 64-bit integer stored in a LARGE_INTEGER structure. The Microsoft compiler can handle this type directly (i.e., Parameters.Read.ByteOffset.Quad Part is an __int64). If you need to specify 64-bit constant values in your code, append i64 to the constant (e.g., 100i64).

The user buffer can be specified in one of two ways, depending on whether your driver uses Buffered I/O or Direct I/O. See the following text for details of these terms. If using Buffered I/O, a pointer to the user buffer is in the IRP header at AssociatedIrp.SystemBuffer. For Direct I/O, a Memory Descriptor List (MDL) is in the IRP header in the MdlAd-dress field.

The Key field in the IRP stack Parameters.Read structure does not seem to be used for anything, and so could be used by a driver for any purpose.

Write IRP, IRP_MJ_WRITE

The parameters for Write IRPs are identical to Read IRPs, except that the relevant parameters are in the stack Parameters.Write structure.

IOCTL IRP, IRP_MJ_DEVICE_CONTROL

The Parameters.DeviceIoControl structure in the IRP stack has IoControlCode, InputBufferLength, and OutputBufferLength parameters.

The user buffer is specified using one or more of the AssociatedIrp.SystemBuffer, MdlAddress, or stack Parameters.DeviceIoControl.Type3InputBuffer fields. See the next section for details.

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 Flags field of the new device object to use Buffered I/O. For Direct I/O, set the DO_DIRECT_IO bit in Flags.

Buffered I/O

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 AssociatedIrp.SystemBuffer field of the IRP header. Simply read or write this memory in your driver.

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.

Direct I/O

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 MdlAddress field of the IRP header.

Neither

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 UserBuffer field of the IRP header. If you are certain that your driver is the first driver to receive a request, the dispatch routine can directly access the buffer, as the driver will be operating in the context of the user's thread. Be very careful if you try to use this technique.

DeviceIoControl Buffers

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 TransferType portion of the actual IOCTL code indicates the buffer access technique. For Buffered I/O, specify METHOD_BUFFERED for TransferType. For Direct I/O, use either METHOD_IN_DIRECT or METHOD_OUT_DIRECT.

If you use METHOD_BUFFERED, AssociatedIrp.SystemBuffer is used for the input and output buffer. The buffer size is the maximum of the user's input and output buffer sizes. As the same memory is used for both input and output, make sure that you use (or copy) the input data before you start writing any output data.

For both METHOD_IN_DIRECT and METHOD_OUT_DIRECT the DeviceIoControl input data appears in buffered memory at Irp- >AssociatedIrp.SystemBuffer. In both cases, an MDL for the DeviceIoControl output data is put in Irp->MdlAddress. The size of the read or write buffer is in Parameters.DeviceIoControl.OutputBufferLength.

Finally, for METHOD_NEITHER, the input buffer user space pointer is put in Parameters.DeviceIoControl.Type3InputBuffer in the stack. The user space output buffer pointer is put in the IRP header UserBuffer field.

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 DebugPrint trace calls in the code. If you use the checked build version of Wdm1, you can view the trace output using the DebugPrint Monitor application. Listing 7.1 shows the (slightly edited) DebugPrint output on Windows 2000. The Wdm1 driver was started at around 12:00 and the Wdm1Test program was run at 12:10. You can follow the program execution as each test in Wdm1Test is run. The DebugPrint output would be different in Windows 98 because the SetFilePointer function does not work for device files.

Listing 7.1 DebugPrint output in Windows 2000

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×