次の方法で共有


IRQL = DISPATCH_LEVEL のデバイス構成情報の取得

IRQL = PASSIVE_LEVEL のデバイス構成情報の取得」の説明にある方法は I/O 要求パケット (IRP) を使用しているので、IRQL = PASSIVE_LEVEL で動作しているドライバーでのみ有効です。 IRQL = DISPATCH_LEVEL で動作しているドライバーでデバイスの構成領域データを取得するには、バス インターフェイスを使用する必要があります。 このデータを取得するには、バス固有のインターフェイスを使用するか、システムに用意されている、バスに依存しないバス インターフェイス BUS_INTERFACE_STANDARD を使用します。

wdmguid.h で定義されている GUID_BUS_INTERFACE_STANDARD インターフェイスを使用すると、I/O 要求パケット (IRP) を使用して親バス ドライバーと通信せずに、デバイス ドライバーから親バス ドライバー ルーチンを直接呼び出すことができます。 特に、このインターフェイスを使用すると、バス ドライバーが以下の各機能向けに用意しているルーチンにドライバーからアクセスできます。

  • バス アドレスを変換する
  • バス アダプターが DMA をサポートしている場合に DMA アダプター構造体を取得する
  • バス上にある特定デバイスのバス構成領域を読み取り、また設定する

このインターフェイスを使用するには、InterfaceType = GUID_BUS_INTERFACE_STANDARD を指定して、バス ドライバーに IRP_MN_QUERY_INTERFACE IRP を送信します。 インターフェイスの個々のルーチンへのポインターを記述した BUS_INTERFACE_STANDARD 構造体へのポインターがバス ドライバーから提供されます。

可能な限り、BUS_INTERFACE_STANDARD を使用することをお勧めします。BUS_INTERFACE_STANDARD を使用すれば、構成情報の取得でバス番号が必要になることはありませんが、バス固有のインターフェイスを取得する場合は、一般的にドライバーによるバス番号の特定が必要になるからです。 PCI のように、バス番号が動的に変化するバスもあります。 したがって、バス番号に依存してドライバーから PCI ポートに直接アクセスしないようにする必要があります。 そのようにするとシステム障害の原因となることがあります。

IRQL = DISPATCH_LEVEL で PCI デバイスの構成領域にアクセスするには、次の 3 つの手順に従う必要があります。

  1. IRQL = PASSIVE_LEVEL で IRP_MN_QUERY_INTERFACE 要求を送信して、PCI バス ドライバーから直接呼び出しインターフェイス構造体 (BUS_INTERFACE_STANDARD) を取得します。 非ページ プール メモリ (通常はデバイス拡張機能にあります) に、この構造体を格納します。

  2. BUS_INTERFACE_STANDARD インターフェイス ルーチンである SetBusDataGetBusData を呼び出し、IRQL = DISPATCH_LEVEL で PCI 構成領域にアクセスします。

  3. インターフェイスへの参照を解放します。 PCI バス ドライバーは、返る前にインターフェイスへの参照カウントを受け取ります。したがって、このインターフェイスにアクセスしているドライバーは、インターフェイスが不要になった時点でその参照を解放する必要があります。

この 3 つの手順の実装方法を示すコード例を次に示します。

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;
}

次のコード スニペットは、GetBusData インターフェイス ルーチンを使用して構成領域データを取得する方法 (手順 2) を示しています。

 bytes = busInterfaceStandard.GetBusData(
                    busInterfaceStandard.Context,
                    PCI_WHICHSPACE_CONFIG,
                    Buffer
                    Offset,
                    Length);

インターフェイスでの処理を完了したドライバーは、次のスニペットのようなコードを使用してインターフェイスへの参照を解放できます (手順 3)。 インターフェイスへの参照を解放した後は、ドライバーからインターフェイス ルーチンを呼び出さないようにする必要があります。

    (busInterfaceStandard.InterfaceDereference)(
                    (PVOID)busInterfaceStandard.Context);

インターフェイスは、呼び出し元によるバス ハードウェアへのアクセスを PCI バス ドライバーによるアクセスと同期します。 ドライバーを作成する際に、バス ハードウェアへのアクセスで PCI バス ドライバーとの競合を回避する目的でスピン ロックを作成する必要はありません。

バス、関数、デバイス番号のみを必要としているのであれば、通常は、それらの情報を取得するためにバス インターフェイスに頼る必要はありません。 次のようにターゲット デバイスの PDO を IoGetDeviceProperty 関数に渡すことで、そのデータを間接的に取得できます。

    ULONG   propertyAddress, length;
    USHORT  FunctionNumber, DeviceNumber;

    // Get the BusNumber. Be warned that bus numbers may be
    // dynamic and therefore subject to change unpredictably!!!
    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);