分享

如何获取 PCI 设备的配置和位置信息

 iceberg_horn 2010-03-02


在作为目标设备驱动程序堆栈的一部分(充当函数或筛选器驱动程序)的驱动程序中,包含外围组件互连 (PCI) 设备的配置和位置信息(如 BusNumber、Devi...

在作为目标设备驱动程序堆栈的一部分(充当函数或筛选器驱动程序)的驱动程序中,包含外围组件互连 (PCI) 设备的配置和位置信息(如 BusNumber、DeviceNumber 和 Function Number),本文介绍如何获取这些信息。

更多信息
在 Windows NT 4.0 上,驱动程序通过扫描总线,并调用 HalGetBusData 和 HalGetBusDataByOffset 这两个 API...

在 Windows NT 4.0 上,驱动程序通过扫描总线,并调用 HalGetBusData 和 HalGetBusDataByOffset 这两个 API 来获取此信息。在 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 的 Microsoft Win32 设备驱动程序工具包
  • Microsoft Windows XP 驱动程序开发工具包
  • Microsoft Windows Server 2003 Driver Development Kit
  • Microsoft Windows 2000 Professional Edition
  • Microsoft Windows XP Home Edition
  • Microsoft Windows XP Professional Edition
  • Microsoft Windows Server 2003 Service Pack 1

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多