获取 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 设备的配置空间时,需要执行三个步骤:
在 IRQL = PASSIVE_LEVEL 发送IRP_MN_QUERY_INTERFACE请求,从 PCI 总线驱动程序获取直接调用接口结构 (BUS_INTERFACE_STANDARD) 。 将此内存存储在非分页池内存中, (通常存储在设备扩展) 中。
调用 BUS_INTERFACE_STANDARD 接口例程 SetBusData 和 GetBusData,以访问 IRQL = DISPATCH_LEVEL的 PCI 配置空间。
取消引用接口。 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);