Recupero di informazioni sulla configurazione del dispositivo in IRQL = DISPATCH_LEVEL

Il metodo illustrato nella sezione Ottenere informazioni sulla configurazione del dispositivo in IRQL = PASSIVE_LEVEL usa pacchetti di richiesta I/O (IRP) e pertanto è valido solo per i driver in esecuzione in IRQL = PASSIVE_LEVEL. I driver in esecuzione in IRQL = DISPATCH_LEVEL devono usare un'interfaccia del bus per ottenere i dati sullo spazio di configurazione del dispositivo. Per ottenere questi dati, è possibile usare un'interfaccia specifica del bus o l'interfaccia del bus indipendente dal sistema, BUS_INTERFACE_STANDARD.

L'interfaccia GUID_BUS_INTERFACE_STANDARD (definita in wdmguid.h) consente ai driver di dispositivo di effettuare chiamate dirette alle routine del driver del bus padre anziché usare pacchetti di richiesta I/O (IRP) per comunicare con il driver del bus. In particolare, questa interfaccia consente ai driver di accedere alle routine fornite dal driver del bus per le funzioni seguenti:

  • Conversione degli indirizzi del bus
  • Recupero di una struttura di adattatore DMA nei casi in cui l'adattatore bus supporta DMA
  • Lettura e impostazione dello spazio di configurazione del bus per un determinato dispositivo sul bus

Per usare questa interfaccia, inviare un IRP_MN_QUERY_INTERFACE IRP al driver del bus con InterfaceType = GUID_BUS_INTERFACE_STANDARD. Il driver del bus fornisce un puntatore a una struttura BUS_INTERFACE_STANDARD che contiene puntatori alle singole routine dell'interfaccia.

È preferibile usare BUS_INTERFACE_STANDARD , se possibile, perché un numero di bus non è necessario recuperare le informazioni di configurazione quando si utilizza BUS_INTERFACE_STANDARD, mentre i driver devono spesso identificare il numero del bus durante il recupero di interfacce specifiche del bus. I numeri di bus per alcuni autobus, ad esempio PCI, possono cambiare dinamicamente. Pertanto, i driver non devono dipendere dal numero del bus per accedere direttamente alle porte PCI. In questo modo potrebbe verificarsi un errore di sistema.

Tre passaggi sono necessari quando si accede allo spazio di configurazione di un dispositivo PCI in IRQL = DISPATCH_LEVEL:

  1. Inviare una richiesta di IRP_MN_QUERY_INTERFACE in IRQL = PASSIVE_LEVEL per ottenere la struttura dell'interfaccia di chiamata diretta (BUS_INTERFACE_STANDARD) dal driver del bus PCI. Archiviare questa operazione in una memoria del pool non di pagina (in genere in un'estensione del dispositivo).

  2. Chiamare le routine di interfaccia BUS_INTERFACE_STANDARD , SetBusData e GetBusData, per accedere allo spazio di configurazione PCI in IRQL = DISPATCH_LEVEL.

  3. Dereferenziare l'interfaccia. Il driver del bus PCI accetta un conteggio dei riferimenti sull'interfaccia prima che venga restituito, quindi il driver che accede all'interfaccia deve dereferenziarlo, una volta che non è più necessario.

L'esempio di codice seguente illustra come implementare questi tre passaggi:

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

Il frammento di codice seguente illustra come usare la routine di interfaccia GetBusData per ottenere i dati dello spazio di configurazione (passaggio 2).

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

Quando il driver viene eseguito con l'interfaccia, può usare codice simile al frammento di codice seguente per dereferenziare l'interfaccia (passaggio 3). I driver non devono chiamare le routine dell'interfaccia dopo la dereferenza dell'interfaccia.

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

L'interfaccia sincronizza l'accesso del chiamante all'hardware del bus con l'accesso del driver del bus PCI. Il writer del driver non deve preoccuparsi della creazione di blocchi di rotazione per evitare di affrontare il driver del bus PCI per l'accesso all'hardware del bus.

Si noti che, se sono necessari tutti i numeri di bus, funzione e dispositivo, in genere non è necessario ricorrere a un'interfaccia del bus per ottenere queste informazioni. Questi dati possono essere recuperati indirettamente passando l'oggetto PDO del dispositivo di destinazione alla funzione IoGetDeviceProperty come indicato di seguito:

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