如何获取PCI设备的配置和位置信息
1个回答
2017-05-27 · 互联网+时代高效组织信息化平台
关注
展开全部
在 Windows 2000 和更高版本的 Windows 操作系统中,控制硬件总线的则是它们各自的总线驱动程序,而不是 HAL。因此,在 Windows 2000 和更高版本的 Windows 操作系统中,过去用于提供总线相关信息的所有 Hal API 都已过时。
在Windows 2000 和更高版本的 Windows 操作系统中,驱动程序无须查询设备即可查找资源。驱动程序通过即插即用 (PnP) 管理器的 IRP_MN_START_DEVICE 请求来获取这些资源。通常,正确编写的驱动程序不需要任何这类信息就能正常工作。如果由于某种原因,驱动程序需要获取这些信息,请参照下面的代码示例来获取资源。驱动程序应当是设备驱动程序堆栈的一部分,因为它需要设备的基础物理设备对象 (PDO) 才能发送 PnP 请求。
下面的代码示例演示了如何获取配置信息: NTSTATUS ReadWriteConfigSpace( IN PDEVICE_OBJECT DeviceObject, IN ULONG ReadOrWrite, // 0 for read 1 for write IN PVOID Buffer, IN ULONG Offset, IN ULONG Length ) { KEVENT event; NTSTATUS status; PIRP irp; IO_STATUS_BLOCK ioStatusBlock; PIO_STACK_LOCATION irpStack; PDEVICE_OBJECT targetObject; PAGED_CODE(); 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 ); if (ReadOrWrite == 0) { irpStack->MinorFunction = IRP_MN_READ_CONFIG; }else { irpStack->MinorFunction = IRP_MN_WRITE_CONFIG; } irpStack->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG; irpStack->Parameters.ReadWriteConfig.Buffer = Buffer; irpStack->Parameters.ReadWriteConfig.Offset = Offset; irpStack->Parameters.ReadWriteConfig.Length = Length; // // 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; } 由于只能在 PASSIVE_LEVEL 级别发送 PnP I/O 请求数据包 (IRP),因此不能使用上面的函数在 DISPATCH_LEVEL 级别获取配置信息。
可以执行下列步骤以在 DISPATCH_LEVEL 级别访问配置空间: 1.在PASSIVE_LEVEL 级别发送一个 IRP_MN_QUERY_INTERFACE,以便从 PCI 总线驱动程序获取直接调用接口结构 (BUS_INTERFACE_STANDARD)。将该结构存储在非分页池内存中(通常存储在 DevcieExtension 中)。2.调用SetBusData 和 GetBusData,以便在 DISPATCH_LEVEL 级别访问配置空间。3.由于PCI 总线驱动程序将在它返回之前获取接口上的引用计数,因此当不再需要该接口时,必须取消对它的引用。4.请使用以下函数在 PASSIVE_LEVEL 级别获取 BUS_INTERFACE_STANDARD: 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; } 以下代码说明了如何使用接口直接调用函数获取总线数据。 bytes = busInterfaceStandard.GetBusData(<BR/> busInterfaceStandard.Context, PCI_WHICHSPACE_CONFIG, Buffer Offset, Length); 如果不再需要该接口,请使用以下代码取消对其的引用。取消对该接口的引用之后,请勿调用任何接口例程。 (busInterfaceStandard.InterfaceDereference)( (PVOID)busInterfaceStandard.Context); 请对目标设备的 PDO 使用 IoGetDeviceProperty 函数,以获取总线号、功能号和设备号,如下所示: ULONG propertyAddress, length; USHORT FunctionNumber; DeviceNumber; // // Get the BusNumber. Please read the warning to follow. // 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); 重要说明:PCI 总线编号可能是动态的,会随时发生变化。因此,不建议根据总线编号或使用该信息来直接访问 PCI 端口。这可能会引发系统故障。
在Windows 2000 和更高版本的 Windows 操作系统中,驱动程序无须查询设备即可查找资源。驱动程序通过即插即用 (PnP) 管理器的 IRP_MN_START_DEVICE 请求来获取这些资源。通常,正确编写的驱动程序不需要任何这类信息就能正常工作。如果由于某种原因,驱动程序需要获取这些信息,请参照下面的代码示例来获取资源。驱动程序应当是设备驱动程序堆栈的一部分,因为它需要设备的基础物理设备对象 (PDO) 才能发送 PnP 请求。
下面的代码示例演示了如何获取配置信息: NTSTATUS ReadWriteConfigSpace( IN PDEVICE_OBJECT DeviceObject, IN ULONG ReadOrWrite, // 0 for read 1 for write IN PVOID Buffer, IN ULONG Offset, IN ULONG Length ) { KEVENT event; NTSTATUS status; PIRP irp; IO_STATUS_BLOCK ioStatusBlock; PIO_STACK_LOCATION irpStack; PDEVICE_OBJECT targetObject; PAGED_CODE(); 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 ); if (ReadOrWrite == 0) { irpStack->MinorFunction = IRP_MN_READ_CONFIG; }else { irpStack->MinorFunction = IRP_MN_WRITE_CONFIG; } irpStack->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG; irpStack->Parameters.ReadWriteConfig.Buffer = Buffer; irpStack->Parameters.ReadWriteConfig.Offset = Offset; irpStack->Parameters.ReadWriteConfig.Length = Length; // // 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; } 由于只能在 PASSIVE_LEVEL 级别发送 PnP I/O 请求数据包 (IRP),因此不能使用上面的函数在 DISPATCH_LEVEL 级别获取配置信息。
可以执行下列步骤以在 DISPATCH_LEVEL 级别访问配置空间: 1.在PASSIVE_LEVEL 级别发送一个 IRP_MN_QUERY_INTERFACE,以便从 PCI 总线驱动程序获取直接调用接口结构 (BUS_INTERFACE_STANDARD)。将该结构存储在非分页池内存中(通常存储在 DevcieExtension 中)。2.调用SetBusData 和 GetBusData,以便在 DISPATCH_LEVEL 级别访问配置空间。3.由于PCI 总线驱动程序将在它返回之前获取接口上的引用计数,因此当不再需要该接口时,必须取消对它的引用。4.请使用以下函数在 PASSIVE_LEVEL 级别获取 BUS_INTERFACE_STANDARD: 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; } 以下代码说明了如何使用接口直接调用函数获取总线数据。 bytes = busInterfaceStandard.GetBusData(<BR/> busInterfaceStandard.Context, PCI_WHICHSPACE_CONFIG, Buffer Offset, Length); 如果不再需要该接口,请使用以下代码取消对其的引用。取消对该接口的引用之后,请勿调用任何接口例程。 (busInterfaceStandard.InterfaceDereference)( (PVOID)busInterfaceStandard.Context); 请对目标设备的 PDO 使用 IoGetDeviceProperty 函数,以获取总线号、功能号和设备号,如下所示: ULONG propertyAddress, length; USHORT FunctionNumber; DeviceNumber; // // Get the BusNumber. Please read the warning to follow. // 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); 重要说明:PCI 总线编号可能是动态的,会随时发生变化。因此,不建议根据总线编号或使用该信息来直接访问 PCI 端口。这可能会引发系统故障。
本回答被网友采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询