共用方式為


在 IRQL = DISPATCH_LEVEL取得裝置組態資訊

IRQL = PASSIVE_LEVEL 區段取得裝置設定資訊 中所述的方法會使用 I/O 要求封包(IRP),因此只適用於在 IRQL = PASSIVE_LEVEL執行的驅動程式。 在 IRQL = DISPATCH_LEVEL執行的驅動程式必須使用總線介面來取得裝置設定空間數據。 若要取得此數據,您可以使用總線特定介面或系統提供的總線獨立總線介面, BUS_INTERFACE_STANDARD

GUID_BUS_INTERFACE_STANDARD介面(定義在 wdmguid.h中)可讓設備驅動器直接呼叫父總線驅動程式例程,而不是使用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 裝置的組態空間時,需要三個步驟:

  1. 在 IRQL = PASSIVE_LEVEL 傳送 IRP_MN_QUERY_INTERFACE 要求,以從 PCI 總線驅動程式取得直接呼叫介面結構(BUS_INTERFACE_STANDARD)。 將此儲存在非分頁集區內存中(通常是在裝置擴充功能中)。

  2. 呼叫 BUS_INTERFACE_STANDARD 介面例程 SetBusDataGetBusData,以存取 IRQL = DISPATCH_LEVEL的 PCI 組態空間。

  3. 取消引用介面。 PCI 總線驅動程式會在介面傳回之前先取得介面的參考計數,因此存取介面的驅動程式必須在不再需要時解除參考。

下列程式代碼範例示範如何實作這三個步驟:

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