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

文章翻译 文章翻译
文章编号: 253232 - 查看本文应用于的产品
本文的发布号曾为 CHS253232
展开全部 | 关闭全部

概要

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

更多信息

在 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 端口。这可能会引发系统故障。

属性

文章编号: 253232 - 最后修改: 2005年12月23日 - 修订: 2.1
这篇文章中的信息适用于:
  • 适用于 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
关键字:?
kbhowto kbwdm KB253232
Microsoft和/或其各供应商对于为任何目的而在本服务器上发布的文件及有关图形所含信息的适用性,不作任何声明。 所有该等文件及有关图形均"依样"提供,而不带任何性质的保证。Microsoft和/或其各供应商特此声明,对所有与该等信息有关的保证和条件不负任何责任,该等保证和条件包括关于适销性、符合特定用途、所有权和非侵权的所有默示保证和条件。在任何情况下,在由于使用或运行本服务器上的信息所引起的或与该等使用或运行有关的诉讼中,Microsoft和/或其各供应商就因丧失使用、数据或利润所导致的任何特别的、间接的、衍生性的损害或任何因使用而丧失所导致的之损害、数据或利润不负任何责任。

提供反馈

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com