Extending the Dispatch Interface

< BACK  NEXT >
[oR]

Much of the I/O Manager operation supports a standard Read/Write abstraction. The requestor supplies a buffer and transfer length, and data is transferred from or to the device. Not all devices or their operations always fit this abstraction. For example, a disk format or repartition are requests that are not well-suited to a normal Read or Write operation. Such kinds of requests are handled with one of two extensible I/O function request codes. These codes allow any number of driver-specific operations, without the restrictions of the Read/Write abstraction.

  • IRP_MJ_DEVICE_CONTROL allows for extended requests from user-mode clients through the Win32 DeviceIoControl call. The I/O Manager constructs an IRP with this MajorFunction code and an IoControlCode value (subcode) as specified by the caller of DeviceIoControl.

  • IRP_MJ_INTERNAL_DEVICE_CONTROL allows for extended requests from kernel-mode code. No access to these operations is provided for user-mode requests. This facility is primarily used by other drivers in a layered stack to communicate special requests. Otherwise, the internal version of the device control operation is identical to the standard version. An IoControlCode value is placed into the IRP by the requestor.

As should be apparent, the implementation of either of these Dispatch routines requires a secondary dispatch based on the value of IoControlCode in the IRP. This value is also known as the IOCTL device control code. Since the secondary dispatch mechanism is completely contained within the driver's private routine(s), the interpretation of the IOCTL value is driver-specific. The remainder of this section describes the details of the device control interface.

Defining Private IOCTL Values

The IOCTL values passed to a driver follow a specific structure. Figure 7.2 illustrates the fields of this 32-bit structure. The DDK includes a macro, CTL_CODE, that offers a convenient mechanism to generate IOCTL values. Table 7.3 describes the arguments to this macro.

Figure 7.2. Layout of the IOCTL code structure.
graphics/07fig02.gif

IOCTL Argument-Passing Method

The extended functions defined with an IOCTL value within a driver often require an input or output buffer. For example, a driver might report performance data using an IOCTL value. The data reported would be transferred through a buffer supplied by the user. Indeed, the Win32 DeviceIoControl function defines parameters for two buffers, one for input, one for output. The buffer transfer mechanism provided by the I/O Manager is defined within the IOCTOL value itself. It can be either buffered or direct I/O. As described previously, with buffered I/O, the I/O Manager copies the user buffer (into or out of) nonpaged system memory, where driver code can then conveniently operate. With direct I/O, the driver is given direct access to the user buffer.

Interestingly, the driver's overall strategy for buffer handling (defined during DriverEntry) is not enforced for IOCTL transfers. Instead, the buffer transfer mechanism is defined with each IOCTL value specification and is a field within the IOCTL structure. This provides maximum flexibility when performing DeviceIoControl operations.

Table 7.3. The CTL_CODE Macro Arguments
CTL_CODE Macro
Parameter Description
DeviceType FILE_DEVICE_XXX value supplied to IoCreateDevice
0x0000 to 0x7FFF - reserved for Microsoft
0x8000 to 0xFFFF - customer defined
ControlCode Driver-defined IOCTL code
0x000 to 0x7FF - reserved for Microsoft (public)
0x800 to 0xFFF - customer (private) defined
TransferType Buffer passing mechanism for this control code
METHOD_BUFFERED
METHOD_IN_DIRECT
METHOD_OUT_DIRECT
METHOD_NEITHER
RequiredAccess Requestor access requirement
FILE_ANY_ACCESS
FILE_READ_DATA
FILE_WRITE_DATA
FILE_READ_DATA | FILE_WRITE_DATA

The TransferType field of the IOCTL field is two-bits wide and defines one of the following:

  • METHOD_BUFFERED.

    The I/O Manager copies the user buffer to and from an intermediate nonpaged pool buffer on behalf of the driver.

  • METHOD_IN_DIRECT.

    The I/O Manager provides a list of pages that encompass the user buffer. The driver uses this list to provide direct I/O (using DMA or programmed I/O) from the device into user space (i.e., like a Read operation).

  • METHOD_OUT_DIRECT.

    The I/O Manager provides a list of pages that encompass the user buffer. The driver uses this list to provide direct I/O from user space into the device (i.e., like a Write operation).

  • METHOD_NEITHER.

    The I/O Manager does not assist with the buffer transfer. The user's original buffer address (presumably from paged memory) is provided to the driver.

Since the TransferType field is a part of the IOCTL code itself, the public codes defined by Microsoft specify the I/O transfer mechanism. For private IOCTL values (driver defined), any appropriate transfer mechanism can be defined. For small, slower transfer, buffered I/O is appropriate. For faster, larger transfers, direct I/O is most suitable.

Writing IOCTL Header Files

Since both the driver project itself and all the clients of the driver need symbolic definitions for the IOCTL codes, it is customary for the driver author to provide a separate header file with device control-code definitions. This header file should also contain any structure definitions that describe the buffer contents of specific control operations. A Win32 program needs to include WINIOCTL.h before including the driver's IOCTL header. A driver project needs to include DEVIOCTL.h before including the driver-specific IOCTL header. These files define the CTL_CODE macro, among other things. The following is an example of an IOCTL header file:

 #define IOCTL_MISSLEDEVICE_AIM CTL_CODE(  \                     FILE_DEVICE_UNKNOWN,                     0x801,                \                     METHOD_BUFFERED,      \                     FILE_ACCESS_ANY ) // Structures used by IOCTL_MISSLEDEVICE_AIM typedef struct _AIM_IN_BUFFER {      ULONG Longitude;      ULONG Latitude; } AIM_IN_BUFFER, *PAIM_IN_BUFFER; typedef struct _AIM_OUT_BUFFER {      ULONG ExtendedStatus; } AIM_OUT_BUFFER, *PAIM_OUT_BUFFER; #define IOCTL_MISSLEDEVICE_LAUNCH CTL_CODE( \                     FILE_DEVICE_UNKNOWN,    \                     0x802,                  \                     METHOD_NEITHER,         \                     FILE_ACCESS_ANY ) 

Processing IOCTL Requests

Once a driver has announced Dispatch routines for either IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL function codes, the I/O Manager starts passing IRPs directly to driver code. The interpretation of the IOCTL Device Control code is strictly the responsibility of the driver. Not even the various fields of the IOCTL code itself are verified by the I/O Manager prior to invocation of the driver's Dispatch routine. Any random number passed by the requestor as an IOCTL code finds its way to the driver.

Therefore, the typical structure of a device control Dispatch routine is a large switch statement. The following is an example of such a routine:

 NTSTATUS DispatchIoControl(  IN PDEVICE_OBJECT pDO,                              IN PIRP pIrp ) {      NTSTATUS status = STATUS_SUCCESS;      PDEVICE_EXTENSION pDE;      PVOID userBuffer;      ULONG inSize;      ULONG outSize;      ULONG controlCode;  // will be the IOCTL request      // The stack location contains the user buffer info      PIO_STACK_LOCATION pIrpStack;      pIrpStack = IoGetCurrentIrpStackLocation( pIrp );      // Dig out the IOCTL request      controlCode = pIrpStack->          Parameters.DeviceIoControl.IoControlCode;      // and the requested transfer sizes      inSize = pIrpStack->          Parameters.DeviceIoControl.InputBufferLength;      OutSize = pIrpStack->      Parameters.DeivceIoControl.OutputBufferLength;      //      // Now perform the secondary dispatch      switch (controlCode) {      case IOCTL_MISSLEDEVICEAIM:           // Always validate parameters for each case...           if (inSize < sizeof(AIM_IN_BUFFER) ||                (outSize < sizeof(AIM_OUT_BUFFER) ) {                status = STATUS_INVALID_BUFFER_SIZE;                break;           }           // Valid IRP values - start the device           IoMarkIrpPending( pIrp );           IoStartPacket( pDO, pIrp, 0, NULL);           return STATUS_PENDING;      case IOCTL_DEVICE_LAUNCH:          if (inSize > 0 || outSize > 0) {               // Is it really an error to pass buffers               // to a function that doesn't use them?               // Maybe not, but the caller is now forced               // to re-think the purpose of the call.               status = STATUS_INVALID_PARAMETER;               break;          }         // Same kind of processing start the device         // :         return STATUS_PENDING;      default:         // Driver received unrecognized control code         status = STATUS_INVALID_DEVICE_REQUEST;         break;     }     // Valid control code cases returned above.     // Execution here means an error occurred.     // Fail the IRP request...pIrp->IoStatus.Status = status;     pIrp->IoStatus.Information = 0;  // no data xfered     IoCompleteRequest( pIrp, IO_NO_INCREMENT )     return status; } 

Managing IOCTL Buffers

IOCTL requests can specify both an input and an output buffer in the same call. As a result, they present a read-after-write abstraction to the caller. IOCTL requests differ in user buffer access in two ways.

  • The buffer transfer mechanism is specified with the IOCTL control-code definition, independent of the overall device object strategy.

  • There are two buffers involved, one for input, one for output.

The following sections describe how the different buffer strategies work with IOCTL control codes.

METHOD_BUFFERED

The I/O Manager allocates a single temporary buffer from nonpaged pool, large enough to hold the larger of the caller's input or output buffer. The address of this pool buffer is placed in the IRP's AssociatedIrp. SystemBuffer field. It then copies the requestor's input buffer into the pool buffer and sets the UserBuffer field of the IRP to the user-space output buffer address.

Upon completion of the IOCTL request, the I/O Manager copies the contents of the system buffer into the requestor's user-space buffer. Notice that only a single internal buffer is presented to the driver code, even though the user has specified independent input and output buffers. The driver code must take care to extract all necessary information from the requestor's input before performing writes into the output buffer.

METHOD_IN_DIRECT

The I/O Manager checks the accessibility of the requester's input buffer and locks it into physical memory. It then builds an MDL for the input buffer and stores a pointer to the MDL in the MdlAddress field of the IRP.

It also allocates a temporary output buffer from nonpaged pool and stores the address of this buffer in the IRP's AssociatedIrp.SystemBuffer field. The IRP's UserBuffer field is set to the original caller's output buffer address. When the IOCTL IRP is completed, the contents of the system buffer are copied into the caller's original output buffer.

METHOD_OUT_DIRECT

The I/O Manager checks the accessibility of the caller's output buffer and locks it down in physical memory. It then builds an MDL for the output buffer and stores a pointer to the MDL in the MdlAddress field of the IRP.

The I/O Manager also allocates a temporary input buffer from non-paged pool and stores its address in the IRP's AssociatedIrp. SystemBuffer field. It copies the contents of the caller's original input buffer into the system buffer and sets the IRP's UserBuffer field to NULL.

METHOD_NEITHER

The I/O Manager puts the address of the caller's input buffer in the Parameters.DeviceIoControl.Type3InputBuffer field of the IRP's current I/O stack location. It stores the address of the output buffer in the IRP's UserBuffer field. Both of these are user-space addresses.

< BACK  NEXT >


The Windows 2000 Device Driver Book(c) A Guide for Programmers
The Windows 2000 Device Driver Book: A Guide for Programmers (2nd Edition)
ISBN: 0130204315
EAN: 2147483647
Year: 2000
Pages: 156

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net