All system-defined I/O control codes for IRP_MJ_DEVICE_CONTROL requests can be considered public in the sense that they are exported to one or more user-mode protected subsystems that run on top of the Windows NT executive. As public device I/O control codes, some are also assumed to be exported to user-mode applications native to a protected subsystem, particularly to Win32 applications.
For a new kind of device, the driver’s designer can define a public set of I/O control codes for IRP_MJ_DEVICE_CONTROL requests. However, since such a set of codes should be generally useful to other drivers of similar devices in the future, public I/O control codes must have the approval of and must be built into the system by Microsoft Corporation.
For new devices or for common kinds of devices with special features, driver designers also can define a set of I/O control codes for IRP_MJ_INTERNAL_DEVICE_CONTROL requests. Such a set of internal I/O control codes can be used by paired kernel-mode drivers to control the underlying device.
For example, kernel-mode drivers designed to the class/port model might use such a set of internal I/O control codes to take advantage of the special features of a particular device or type of device. The system-defined SCSI class/port interface uses this technique to define a SCSI-specific set of requests that class drivers send down to the system SCSI port driver, which transforms them into OS-independent SCSI requests for HBA-specific miniport drivers.
When it is sent a device control request, a class driver sets up the next-lower port driver’s I/O stack location in the IRP and passes the request on to the underlying device driver, like any other higher-level driver.
A class driver also can allocate IRPs for I/O control requests and send them to the underlying port driver as follows:
By calling the GDI function EngDeviceIoControl, a display driver also can send privately defined, device-specific I/O control requests, as well as system-defined public I/O control requests, through the system video port driver down to the corresponding adapter-specific video miniport driver.
With a call to DeviceIoControl, a user-mode VDD can send I/O control requests to the corresponding kernel-mode driver for an MS-DOS-application-dedicated device.
For more information about the functionality of video miniport drivers and display drivers, see the Graphics Driver Design Guide. For additional VDD details see Virtual DOS Drivers.
Figure 1.1 illustrates the layout of I/O control codes.
Figure 1.1 I/O Control Code Layout
Designers of drivers for a new FILE_DEVICE_XXX type of device must set the Common flag at bit 31 in the private I/O control codes they define. Those who define a private set of I/O control codes for IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL requests also must set the Custom flag at bit 13 in the I/O control codes they define.
All system-defined I/O control codes have both these C flags cleared.
Driver writers can use the system-supplied macro CTL_CODE to set up new I/O control codes. To define an I/O control code, follow these guidelines for using CTL_CODE:
METHOD_BUFFERED if the driver transfers small amounts of data for the request
With this method, IRPs containing the I/O control code will supply a pointer to the buffer into which or from which to transfer data at
Irp->AssociatedIrp.SystemBuffer. Most I/O control codes for device
and intermediate drivers use this TransferType value.
METHOD_IN_DIRECT if the underlying device driver will read a large amount of data for the request using DMA or PIO and must transfer the data quickly
With this method, IRPs containing the I/O control code will supply a pointer to an MDL, describing the output buffer at Irp->MdlAddress.
METHOD_OUT_DIRECT if the underlying device driver will write a large amount of data to the device for the request using DMA or PIO and must transfer the data quickly
With this method, IRPs containing the I/O control code will supply a pointer to an MDL, describing the data buffer, at Irp->MdlAddress.
METHOD_NEITHER if the driver can be sent such a request only while it is running in the context of the thread that originates the I/O control request
Only a highest-level kernel-mode driver is guaranteed to meet this condition, so this value is seldom used for the I/O control codes passed to device drivers. With this method, the highest-level driver must determine whether to set up buffered or direct access to user data on receipt of the request, possibly must lock down the user buffer, and must wrap its access to the user buffer in a structured exception handler. Otherwise, the originating user-mode caller might change the buffered data out from under the driver or the caller could be swapped out just as the driver is accessing the user buffer.
FILE_ANY_ACCESS if the driver can carry out the requested operation for any caller that has a handle for the file object representing the target device object
FILE_READ_DATA if the driver can carry out the requested operation only for a caller with read access rights
With this required access, the underlying device driver transfers data from the device to system memory.
FILE_WRITE_DATA if the driver can carry out the requested operation only for a caller with write access rights
With this required access, the underlying device driver transfers data from system memory to its device.
(FILE_READ_DATA | FILE_WRITE_DATA) if the caller must have both read and write access rights
With this required access, the underlying device driver transfers data between system memory and the device
Most public I/O control requests sent to device drivers are assigned FILE_ANY_ACCESS as their RequiredAccess value, particularly those sent to drivers of exclusive devices and those that are buffered by the I/O Manager or a higher-level driver. Many internal I/O control requests for system-supplied drivers also specify this type of RequiredAccess.
However, for certain types of devices, the public I/O control codes require the caller to have read access rights, write access rights, or both.
For example, the definition of the public I/O control code IOCTL_DISK_SET_PARTITION_INFO shows that this I/O request can be sent to a disk driver and to all drivers layered above the disk driver only if the caller has both read and write access rights, as shown by the following definition:
#define IOCTL_DISK_SET_PARTITION_INFO\ CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\ FILE_READ_DATA | FILE_WRITE_DATA)
Driver designers who want to define a set of public control codes must consult with Microsoft Corporation to have new codes added to the system header files. Private I/O control codes should be defined in the driver(s)’ device-specific header files.