Freigeben über


Abrufen von Gerätekonfigurationsinformationen unter IRQL = DISPATCH_LEVEL

Die im Abschnitt Abrufen von Gerätekonfigurationsinformationen bei IRQL = PASSIVE_LEVEL veranschaulichte Methode verwendet E/A-Anforderungspakete (IRPs) und ist daher nur für Treiber gültig, die unter IRQL = PASSIVE_LEVEL ausgeführt werden. Treiber, die unter IRQL = DISPATCH_LEVEL ausgeführt werden, müssen eine Busschnittstelle verwenden, um Daten zum Gerätekonfigurationsraum abzurufen. Zum Abrufen dieser Daten können Sie eine busspezifische Schnittstelle oder die vom System bereitgestellte busunabhängige Busschnittstelle verwenden, BUS_INTERFACE_STANDARD.

Die GUID_BUS_INTERFACE_STANDARD-Schnittstelle (definiert in wdmguid.h) ermöglicht Gerätetreibern, direkte Anrufe an übergeordnete Bustreiberroutinen zu tätigen, anstatt E/A-Anforderungspakete (IRP) für die Kommunikation mit dem Bustreiber zu verwenden. Insbesondere ermöglicht diese Schnittstelle Treibern den Zugriff auf Routinen, die der Bustreiber für die folgenden Funktionen bereitstellt:

  • Übersetzen von Busadressen
  • Abrufen einer DMA-Adapterstruktur in Fällen, in denen der Busadapter DMA unterstützt
  • Lesen und Festlegen des Buskonfigurationsraums für ein bestimmtes Gerät auf dem Bus

Um diese Schnittstelle zu verwenden, senden Sie einen IRP_MN_QUERY_INTERFACE IRP mit InterfaceType = GUID_BUS_INTERFACE_STANDARD an Ihren Bustreiber. Der Bustreiber stellt einen Zeiger auf eine BUS_INTERFACE_STANDARD-Struktur bereit, die Zeiger auf die einzelnen Routinen der Schnittstelle enthält.

Es ist vorzuziehen, nach Möglichkeit BUS_INTERFACE_STANDARD zu verwenden, da keine Busnummer erforderlich ist, um Konfigurationsinformationen abzurufen, wenn BUS_INTERFACE_STANDARD verwendet werden, während Treiber häufig die Busnummer beim Abrufen busspezifischer Schnittstellen identifizieren müssen. Die Busnummern für einige Busse, z. B. PCI, können sich dynamisch ändern. Daher sollten Treiber nicht von der Busnummer abhängig sein, um direkt auf die PCI-Ports zuzugreifen. Dies kann zu Systemausfällen führen.

Beim Zugriff auf den Konfigurationsraum eines PCI-Geräts unter IRQL = DISPATCH_LEVEL sind drei Schritte erforderlich:

  1. Senden Sie eine IRP_MN_QUERY_INTERFACE Anforderung unter IRQL = PASSIVE_LEVEL, um die Direct-Call-Schnittstellenstruktur (BUS_INTERFACE_STANDARD) vom PCI-Bustreiber abzurufen. Speichern Sie diese in einem nicht auslagernden Poolspeicher (in der Regel in einer Geräteerweiterung).

  2. Rufen Sie die BUS_INTERFACE_STANDARD-Schnittstellenroutinen SetBusData und GetBusData auf, um auf den PCI-Konfigurationsraum unter IRQL = DISPATCH_LEVEL zuzugreifen.

  3. Rückschluss auf die Schnittstelle. Der PCI-Bustreiber nimmt eine Referenzanzahl für die Schnittstelle an, bevor sie zurückgegeben wird, sodass der Treiber, der auf die Schnittstelle zugreift, sie ableiten muss, sobald sie nicht mehr benötigt wird.

Im folgenden Codebeispiel wird veranschaulicht, wie diese drei Schritte implementiert werden:

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

Der folgende Codeausschnitt zeigt, wie Sie die GetBusData-Schnittstellenroutine verwenden, um die Konfigurationsraumdaten abzurufen (Schritt 2).

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

Wenn der Treiber mit der Schnittstelle fertig ist, kann er Code ähnlich dem folgenden Codeausschnitt verwenden, um die Schnittstelle zu dereferenzieren (Schritt 3). Treiber dürfen keine Schnittstellenroutinen aufrufen, nachdem sie die Schnittstelle deeferencinging.

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

Die Schnittstelle synchronisiert den Zugriff des Aufrufers auf die Bushardware mit dem Zugriff des PCI-Bustreibers. Der Treiberschreiber muss sich keine Gedanken über das Erstellen von Drehsperren machen, um nicht mit dem PCI-Bustreiber für den Zugriff auf Bushardware zu kämpfen.

Beachten Sie, dass, wenn nur Bus-, Funktions- und Gerätenummern erforderlich sind, es in der Regel unnötig ist, auf eine Busschnittstelle zurückzugreifen, um diese Informationen abzurufen. Diese Daten können indirekt abgerufen werden, indem das PDO des Zielgeräts wie folgt an die IoGetDeviceProperty-Funktion übergeben wird:

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