PCI デバイスの構成情報と場所情報の取得方法

概要

この資料では、対象のデバイスのドライバ スタックにある任意のドライバ (関数ドライバまたはフィルタ ドライバ) において、PCI (Peripheral Component Interconnect) デバイスの構成情報と場所情報 (バス番号、デバイス番号、機能番号など) を取得する方法について説明します。

詳細

Windows NT 4.0 では、ドライバは、バスのスキャンと HalGetBusData API および HalGetBusDataByOffset API を呼び出すことによって構成情報および場所情報を取得します。Windows 2000 以降のオペレーティング システムでは、ハードウェア バスは HAL ではなくオペレーティング システムの各バス ドライバによって制御されます。そのため、バスに関する情報を提供していたすべての HAL API が、Windows 2000 以降のオペレーティング システムでは使用されなくなりました。


Windows 2000 以降のオペレーティング システムでは、ドライバはデバイスに問い合わせてリソースを検出する必要がなくなりました。ドライバは、IRP_MN_START_DEVICE 要求によって、プラグ アンド プレイ (PnP) マネージャからリソースを取得します。通常、適切に設計されたドライバであれば、正常に機能するためにこのような情報が必要になることはありません。何らかの理由によりこのような情報が必要になる場合は、リソースの取得方法を示した以下のサンプル コードを使用します。ドライバは、基礎となっている、デバイスの物理デバイス オブジェクト (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) を取得します。これを非ページ プール メモリ (通常は DeviceExtension) に格納します。
  2. SetBusData と GetBusData を呼び出して、DISPATCH_LEVEL で構成領域にアクセスします。
  3. PCI バス ドライバは、インターフェイスの参照数を取得してから返されるので、不要になったインターフェイスについては、参照を解除する必要があります。
  4. 次の関数を使用して、PASSIVE_LEVEL で BUS_INTERFACE_STANDARD を取得します。
    NTSTATUS
    GetPCIBusInterfaceStandard(
    IN PDEVICE_OBJECT DeviceObject,
    OUT PBUS_INTERFACE_STANDARDBusInterfaceStandard
    )
    /*++

    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 - 最終更新日: 2008/07/20 - リビジョン: 1

フィードバック