How do I access bus configuration space from a WDM driver?
Accessing Bus Configuration Space in WDM
A sometimes overlooked aspect of Win32 Driver Model is that access to bus configuration space, provided by HAL calls under Windows NT 4.0, is provided by system bus drivers.
For example, consider the PCI bus. To read and write the PCI configuration space under NT 4.0, a driver called HalGetBusDataByOffset and HalSetBusDataByOffset. These two calls belong to a set of services that are available on Windows 98, but whose prototypes do not appear in wdm.h. A driver that includes the prototypes (which can still be found in ntddk.h) can call these services, but clearly the use of these services is discouraged.
The WDM way to access the configuration space is by sending a request to the bus driver. A WDM driver always communicates with the bus driver through a Physical Device Object, or PDO. PDOs are the data structures that the system uses to dynamically model its configuration. A bus driver creates a PDO for each physical device that it detects. A device driver receives from the system a pointer to the PDO of each device that the driver controls. When the device driver needs to read the configuration space of a device, it sends to the PDO for that device a request of type IRP_MJ_PNP, with minor function IRP_MN_READ_CONFIG. Similarly, IRP_MN_WRITE_CONFIG is used for write operations. The request is routed to the bus driver, which is the driver responsible for the PDO.
The parameter structure for this IRP bears a close resemblance to the parameters for the Hal calls:
A driver simply sets up the IRP and calls the PDO. Here is some Driver::Works code that demonstrates the usage of this IRP:
ASSERT (TopOfStackDevice != NULL);
KPnpLowerDevice Ld(TopOfStackDevice, NULL, &status);
KIrp I ( KIrp::Allocate(Ld.StackRequirement()) );
PIO_STACK_LOCATION pStack = I.NextStackLocation();
pStack->MajorFunction = IRP_MJ_PNP;
pStack->MinorFunction = IRP_MN_READ_CONFIG;
pStack->Parameters.ReadWriteConfig.WhichSpace = 0;
pStack->Parameters.ReadWriteConfig.Buffer = Buffer;
pStack->Parameters.ReadWriteConfig.Offset = Offset;
pStack->Parameters.ReadWriteConfig.Length = Count;
status = Ld.CallWaitComplete(I, TRUE, &info);
Architecturally, using system bus drivers to read and write configuration space has some clear advantages. By decoupling the functionality of the various buses, the system is made more modular and flexible. Furthermore, using an IRP-based interface rather than a procedural interface enables lower filter drivers to supplement or alter the functionality of the system.