16.8.2.1 Calling IoQueryDeviceDescription
NT device drivers can call IoQueryDeviceDescription to obtain the system-detected and system-supplied hardware configuration information about their devices from the registry.
Supplying a ConfigCallback Routine
A device driver that calls IoQueryDeviceDescription must provide a ConfigCallback routine, defined as follows:
NTSTATUS
(*PIO_QUERY_DEVICE_ROUTINE) (
IN PVOID Context, //driver-determined
IN PUNICODE_STRING PathName,
IN INTERFACE_TYPE BusType, //system-defined enum
IN ULONG BusNumber, //system-assigned count
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
IN CONFIGURATION_TYPE ControllerType, //system-defined enum
IN ULONG ControllerNumber,
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
IN CONFIGURATION_TYPE PeripheralType, //system-defined enum
IN ULONG PeripheralNumber,
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
);
The Context pointer passed to a ConfigCallback routine accesses a driver-determined storage area. However, the kernel-mode stack is limited in size: somewhat less than two pages are available for data storage by the DriverEntry and ConfigCallback routines and the amount of information returned cannot be predetermined. NT drivers usually call ExAllocatePool or ExAllocatePoolWithTag to allocate a paged pool buffer for the configuration information passed between their DriverEntry and ConfigCallback routines. The DriverEntry routine can free such a buffer when it has consumed and processed (as described in Section 16.8.3) the information supplied by its ConfigCallback routine.
Note the order of the bus, controller, and peripheral parameters to a ConfigCallback routine. The corresponding parameters to IoQueryDeviceDescription are declared to be optional, but the DriverEntry routine must supply appropriate values for at least one of the following sets of parameters when it calls IoQueryDeviceDescription:
·BusType and BusNumber
·ControllerType and ControllerNumber
·PeripheralType and PeripheralNumber
Depending on the information requested when the DriverEntry routine calls IoQueryDeviceDescription, the PathName passed to the ConfigCallback routine points to a registry key that contains successively more device-specific and detailed information. For example, if a driver requests information only about a particular I/O bus in the machine, the PathName passed to the ConfigCallback routine would point to the key containing bus information. If the driver also requests information about both a ControllerType and a PeripheralType, the PathName parameter to the ConfigCallback routine would point to the registry key containing peripheral information.
Making Calls to IoQueryDeviceDescription
Figure 16.7 illustrates a device driver's call to IoQueryDeviceDescription.
Figure 16.7 Getting Hardware Configuration Information from the Registry
Driver-supplied pointers for Context and the ConfigCallback routine are required parameters to IoQueryDeviceDescription. All other parameters are optional, so the caller can specify one or more of the bus, controller, and peripheral parameter sets, possibly in separate calls to IoQueryDeviceDescription with separate ConfigCallback routines. NT drivers usually need device-type-specific hardware information about a given ControllerType and/or PeripheralType.
System-defined enumerated values for the BusType parameter indicate the types of interfaces that I/O buses in Windows NT machines provide. The driver for a device that must be connected on an ISA bus would set BusType to the system-defined INTERFACE_TYPE value Isa when it called IoQueryDeviceDescription.
A device driver could also search for its devices on every I/O bus in the machine. Any driver can set BusType to successive values by using the system-defined value MaximumInterfaceType as a loop-control variable for calling IoQueryDeviceDescription, particularly if a driver constrains its query by specifying values for the ControllerType and/or PeripheralType parameters.
IoQueryDeviceDescription returns STATUS_OBJECT_NAME_NOT_FOUND if a particular type of bus is not available in the machine.
NT device drivers can use the following system-defined CONFIGURATION_TYPE values for the ControllerType and PeripheralType parameters to IoQueryDeviceDescription:
ControllerType Values: | PeripheralType Values: |
DiskController | DiskPeripheral |
TapeController | FloppyDiskPeripheral |
CdromController | TapePeripheral |
WormController | ModemPeripheral |
SerialController | MonitorPeripheral |
NetworkController | PrinterPeripheral |
DisplayController | PointerPeripheral |
ParallelController | KeyboardPeripheral |
PointerController | TerminalPeripheral |
KeyboardController | OtherPeripheral |
AudioController | LinePeripheral |
OtherController | NetworkPeripheral |
Most of these defined type names are self-explanatory. However, the PeripheralType value MonitorPeripheral indicates a video monitor for which there is a corresponding video adapter. The value TerminalPeripheral indicates a so-called "dumb terminal" that displays data usually sent through a serial port. The value LinePeripheral indicates a peripheral device connected to the machine by a controller line.
Depending on which optional parameters the DriverEntry routine passes to IoQueryDeviceDescription, its ConfigCallback routine is given values for the corresponding parameters when it is called. A ConfigCallback routine can save these values (such as system-assigned bus numbers) in a temporary buffer, usually allocated from paged pool as described in Section 16.4.1.3 and released from the DriverEntry routine, or on the stack for subsequent calls to HAL routines.
IoQueryDeviceXxx Pointers Input to ConfigCallback
Depending on which optional parameters the DriverEntry routine passes to IoQueryDeviceDescription, a ConfigCallback routine is also given one or more nonNULL pointers to BusInformation, ControllerInformation, and PeripheralInformation, as shown in Figure 16.7. Each of these parameters is actually an array of pointers whose elements select ARC- or NtDetect-supplied information in the \Registry\Machine\Hardware\Description tree.
NT device drivers can use any of the following system-defined values to get to the kind(s) of bus, controller, or peripheral information they need from the registry:
·IoQueryDeviceIdentifier points to any "hardware" name that the ARC component found for the device or that the x86-based OS loader created for the device, which the system translates into a Unicode string when this name is written into the registry.
·IoQueryDeviceConfigurationData points to any bus-relative hardware configuration information that the ARC or NtDetect component found about the driver's device.
·IoQueryDeviceComponentInformation points to any information that the ARC or NtDetect component found about a device's subcomponents.
As shown in Figure 16.7, NT device drivers usually get information from the registry of type CM_FULL_RESOURCE_DESCRIPTOR, for which they use the ControllerInformation[IoQueryDeviceConfigurationData] and/or PeripheralInformation[IoQueryDeviceConfigurationData] pointers.
Processing Configuration Information in ConfigCallback
As Figure 16.7 also shows, the IoQueryDeviceXxx pointers are valid only within the ConfigCallback routine. Moreover, NT drivers can read the value entries of subkeys under the key \Registry\Machine\Hardware\Description but cannot modify them. It is a ConfigCallback routine's responsibility to copy pertinent system-supplied data it finds to a driver-allocated buffer (or driver-structured area on the stack) so the DriverEntry routine can use and, if necessary, supplement this information when the ConfigCallback routine returns control.
The CM_FULL_RESOURCE_DESCRIPTOR-type information shown in Figure 16.7 includes all controller- or peripheral-specific information that can be detected about one or more of the following:
·An inclusive, but bus-relative, range of physical Port addresses
The DriverEntry (or ConfigCallback) routine calls HalTranslateBusAddress to get a mapped system value that the driver uses (or remaps by calling MmMapIoSpace if HalTranslateBusAddress indicates that the Port range is in memory, rather than I/O space) to access its device ports thereafter.
·An inclusive range of physical Memory addresses used by the device
The DriverEntry (or ConfigCallback) routine calls HalTranslateBusAddress to get a mapped range, which it passes to MmMapIoSpace to get a remapped system range that the driver uses to access its device-dedicated memory thereafter.
·The bus-relative Interrupt vector for its device and the IRQL for that vector
The DriverEntry or ConfigCallback routine calls HalGetInterruptVector to get a mapped system vector, DIRQL, and processor affinity mask that the DriverEntry routine can pass to IoConnectInterrupt when it registers the driver's ISR.
·The bus-relative physical Dma channel or port (on MicroChannel-type buses) to which its device is connected
The DriverEntry or ConfigCallback routine fills in a DEVICE_DESCRIPTION structure using this information, and calls HalGetAdapter so its device can do DMA transfer operations thereafter. For more information about devices that use DMA, see the section on adapter objects in Chapter 3.
Note that it is possible for a ConfigCallback routine to call HalTranslateBusAddress, HalGetInterruptVector, or HalGetAdapter, as long as it passes the values returned by these HAL routines to the DriverEntry routine. Note also that an NT device driver is required to call IoReportResourceUsage with the same values returned by IoGetDeviceDescription before it accesses the device, as explained in Section 16.8.3, so a ConfigCallback routine must not return control before it saves the information, if any, that it obtained from the registry for the DriverEntry routine.
The DeviceSpecificData, shown in Figure 16.7 at the tail of each partial resource descriptor in a partial resource list, is generally a device-type-specific structure. The system defines DeviceSpecificData structures for many kinds of peripheral devices. See ntddk.h for details.
On Return from ConfigCallback
Depending on the platform and/or type of bus, an NT driver might get only some of the machine-specific hardware information that it needs by calling IoQueryDeviceDescription. Depending on the device, some NT drivers also interrogate their devices to supplement system-supplied hardware configuration information.
While it supplies as much hardware configuration information as possible to each device driver, the system also requires each device driver to supply hardware configuration information about itself (and/or each of its devices) in the registry, as one of the following:
·A CM_RESOURCE_LIST, containing one or more CM_RESOURCE_DESCRIPTORs, which can include DeviceSpecificData entries, that IoReportResourceUsage successfully claimed in the registry for the caller
·An IO_RESOURCE_REQUIREMENTS_LIST of preferred and alternative (or fixed) hardware resources, from which IoAssignResources returns a CM_RESOURCE_LIST describing those it claimed in the registry for the caller
·A pointer to a variable, at which HalAssignSlotResources returns the address of a buffered CM_RESOURCE_LIST, describing the hardware resources it claimed in the registry for a device on a dynamically configurable I/O bus with a published standard interface, such as PCI
NT device driver writers should consider the following an implementation guideline:
Every NT device driver must claim all the hardware resources in the machine that the driver and its device(s) need to carry out I/O operations before the driver actually initializes its device(s).
For more information about claiming these resources in the registry, see Section 16.8.3.