获取 IRQL = DISPATCH_LEVEL 处的设备配置信息

IRQL = PASSIVE_LEVEL 获取设备配置信息 部分中演示的方法利用 I/O 请求数据包 (IRP) ,因此仅对以 IRQL = PASSIVE_LEVEL 运行的驱动程序有效。 在 IRQL = DISPATCH_LEVEL 上运行的驱动程序必须使用总线接口来获取设备配置空间数据。 若要获取此数据,可以使用特定于总线的接口或系统提供的独立于总线的总线接口 ,BUS_INTERFACE_STANDARD

) 中 wdmguid.h 定义的GUID_BUS_INTERFACE_STANDARD接口 (使设备驱动程序能够直接调用父总线驱动程序例程,而不是使用 I/O 请求数据包 (IRP) 与总线驱动程序通信。 具体而言,此接口使驱动程序能够访问总线驱动程序为以下函数提供的例程:

  • 转换总线地址
  • 在总线适配器支持 DMA 的情况下检索 DMA 适配器结构
  • 读取和设置总线上特定设备的总线配置空间

若要使用此接口,请将 interfaceType = GUID_BUS_INTERFACE_STANDARD IRP_MN_QUERY_INTERFACE IRP 发送到总线驱动程序。 总线驱动程序提供指向BUS_INTERFACE_STANDARD结构的指针,该结构包含指向接口的各个例程的指针。

最好尽可能使用 BUS_INTERFACE_STANDARD ,因为在使用 BUS_INTERFACE_STANDARD 时不需要总线编号来检索配置信息,而驱动程序在检索特定于总线的接口时通常必须标识总线编号。 某些总线(如 PCI)的总线编号可以动态更改。 因此,驱动程序不应依赖于总线编号来直接访问 PCI 端口。 这样做可能会导致系统故障。

在 IRQL = DISPATCH_LEVEL 访问 PCI 设备的配置空间时,需要执行三个步骤:

  1. 在 IRQL = PASSIVE_LEVEL 发送IRP_MN_QUERY_INTERFACE请求,从 PCI 总线驱动程序获取直接调用接口结构 (BUS_INTERFACE_STANDARD) 。 将此内存存储在非分页池内存中, (通常存储在设备扩展) 中。

  2. 调用 BUS_INTERFACE_STANDARD 接口例程 SetBusDataGetBusData,以访问 IRQL = DISPATCH_LEVEL的 PCI 配置空间。

  3. 取消引用接口。 PCI 总线驱动程序在返回之前对接口进行引用计数,因此访问接口的驱动程序必须取消引用,一旦不再需要该接口。

以下代码示例演示如何实现这三个步骤:

NTSTATUS
GetPCIBusInterfaceStandard(
    IN  PDEVICE_OBJECT DeviceObject,
    OUT PBUS_INTERFACE_STANDARD BusInterfaceStandard
    )
/*++
Routine Description:
    This routine gets the bus interface standard information from the PDO.
Arguments:
    DeviceObject - Device object to query for this information.
    BusInterface - Supplies a pointer to the retrieved information.
Return Value:
    NT status.
--*/ 
{
    KEVENT event;
    NTSTATUS status;
    PIRP irp;
    IO_STATUS_BLOCK ioStatusBlock;
    PIO_STACK_LOCATION irpStack;
    PDEVICE_OBJECT targetObject;

    Bus_KdPrint(("GetPciBusInterfaceStandard entered.\n"));
    KeInitializeEvent(&event, NotificationEvent, FALSE);
    targetObject = IoGetAttachedDeviceReference(DeviceObject);
    irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
                                       targetObject,
                                       NULL,
                                       0,
                                       NULL,
                                       &event,
                                       &ioStatusBlock);
    if (irp == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto End;
    }
    irpStack = IoGetNextIrpStackLocation( irp );
    irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
    irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_BUS_INTERFACE_STANDARD;
    irpStack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD);
    irpStack->Parameters.QueryInterface.Version = 1;
    irpStack->Parameters.QueryInterface.Interface = (PINTERFACE)BusInterfaceStandard;
    irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;

    // Initialize the status to error in case the bus driver does not 
    // set it correctly.
    irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
    status = IoCallDriver(targetObject, irp);
    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatusBlock.Status;
    }
End:
    // Done with reference
    ObDereferenceObject(targetObject);
    return status;
}

以下代码片段演示如何使用 GetBusData 接口例程 (步骤 2) 获取配置空间数据。

 bytes = busInterfaceStandard.GetBusData(
                    busInterfaceStandard.Context,
                    PCI_WHICHSPACE_CONFIG,
                    Buffer
                    Offset,
                    Length);

驱动程序完成接口后,可以使用类似于以下代码片段的代码来取消引用接口 (步骤 3) 。 取消引用接口后,驱动程序不得调用接口例程。

    (busInterfaceStandard.InterfaceDereference)(
                    (PVOID)busInterfaceStandard.Context);

接口将调用方对总线硬件的访问与 PCI 总线驱动程序的访问权限同步。 驱动程序编写器无需担心创建旋转锁,以避免与 PCI 总线驱动程序争用以访问总线硬件。

请注意,如果只需要总线、功能和设备编号,通常不需要使用总线接口来获取此信息。 通过将目标设备的 PDO 传递给 IoGetDeviceProperty 函数,可以间接检索此数据,如下所示:

    ULONG   propertyAddress, length;
    USHORT  FunctionNumber, DeviceNumber;

    // Get the BusNumber. Be warned that bus numbers may be
    // dynamic and therefore subject to change unpredictably!!!
    IoGetDeviceProperty(PhysicalDeviceObject,
                        DevicePropertyBusNumber,
                        sizeof(ULONG),
                        (PVOID)&BusNumber,
                        &length);

    // Get the DevicePropertyAddress
    IoGetDeviceProperty(PhysicalDeviceObject,
                        DevicePropertyAddress,
                        sizeof(ULONG),
                        (PVOID)&propertyAddress,
                        &length);

    // For PCI, the DevicePropertyAddress has device number 
    // in the high word and the function number in the low word. 
    FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF);
    DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF);