How to get the configuration and location information of a PCI device

Article translations Article translations
Article ID: 253232 - View products that this article applies to.
This article was previously published under Q253232
Expand all | Collapse all

SUMMARY

This article describes how you can get the configuration and location information (such as BusNumber, DeviceNumber, and Function Number) of a Peripheral Component Interconnect (PCI) device in a driver that is part of the target device's driver stack either as a function or filter driver.

MORE INFORMATION

On Windows NT 4.0, drivers get this information by scanning the bus and calling the HalGetBusData and HalGetBusDataByOffset APIs. On Windows 2000 and later Windows operating systems, the hardware buses are controlled by their respective bus drivers and not by HAL. Therefore, all of the Hal APIs that used to provide bus-related information are obsolete in Windows 2000 and later Windows operating systems.

On Windows 2000 and later Windows operating systems, a driver does not need to query the device to find resources. The driver gets the resources from the Plug and Play (PnP) manager in its IRP_MN_START_DEVICE request. Typically, a well-written driver would not require any of this information to function correctly. If for some reason the driver requires this information, the code sample to follow shows how to get the resources. The driver should be part of the device's driver stack because it requires the underlying physical device objects (PDO) of the device to send the PnP request.

The following code sample demonstrates how to get the configuration information:
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;

}
				
Because you can only send the PnP I/O Request Packets (IRPs) at PASSIVE_LEVEL, you cannot use the above function to get the configuration information at DISPATCH_LEVEL.

You can perform the following steps to access the configuration space at DISPATCH_LEVEL:
  1. Send an IRP_MN_QUERY_INTERFACE at PASSIVE_LEVEL to get the direct-call interface structure (BUS_INTERFACE_STANDARD) from the PCI bus driver. Store this in a nonpaged pool memory (typically in DevcieExtension).
  2. Call the SetBusData and GetBusData to access the configuration space at DISPATCH_LEVEL.
  3. The PCI bus driver takes a reference count on the interface before it returns, so you must dereference the interface when it is no longer needed.
  4. Use the following function to get the BUS_INTERFACE_STANDARD at PASSIVE_LEVEL:
    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;
    
    }
    					
The following code shows how you can use the interface direct-call function to get the bus data.
    bytes = busInterfaceStandard.GetBusData(<BR/>
                    busInterfaceStandard.Context,
                    PCI_WHICHSPACE_CONFIG,
                    Buffer
                    Offset,
                    Length);
				
When you no longer need the interface, use the following code to dereference the interface with the code to follow. Do not call any interface routines after you dereference the interface.
    (busInterfaceStandard.InterfaceDereference)(
                (PVOID)busInterfaceStandard.Context);
				
Use the IoGetDeviceProperty function on the PDO of the target device to get the Bus, Function, and Device numbers as follows:
    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);

				
Important PCI Bus numbers may be dynamic and can change at any moment. Therefore, it is not good to depend on the bus number or use that information to access the PCI ports directly. This may lead to system failure.

Properties

Article ID: 253232 - Last Review: September 8, 2005 - Revision: 2.1
APPLIES TO
  • Microsoft Win32 Device Driver Kit for Windows 2000
  • Microsoft Windows XP Driver Development Kit
  • Microsoft Windows Server 2003 Driver Development Kit
  • Microsoft Windows 2000 Professional Edition
  • Microsoft Windows XP Home Edition
  • Microsoft Windows XP Professional
  • Microsoft Windows Server 2003 Service Pack 1
Keywords: 
kbhowto kbwdm KB253232

Give Feedback

 

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