Compartilhar via


Obtendo informações de configuração do dispositivo em IRQL = DISPATCH_LEVEL

O método ilustrado na seção Obtendo informações de configuração do dispositivo em IRQL = PASSIVE_LEVEL usa irps (pacotes de solicitação de E/S) e, portanto, só é válido para drivers em execução em IRQL = PASSIVE_LEVEL. Os drivers em execução em IRQL = DISPATCH_LEVEL devem usar uma interface de barramento para obter dados de espaço de configuração do dispositivo. Para obter esses dados, você pode usar uma interface específica do barramento ou a interface de barramento independente de barramento fornecida pelo sistema , BUS_INTERFACE_STANDARD.

A interface de GUID_BUS_INTERFACE_STANDARD (definida em wdmguid.h) permite que os drivers de dispositivo façam chamadas diretas para rotinas de motorista de ônibus pai em vez de usar pacotes de solicitação de E/S (IRP) para se comunicar com o motorista do ônibus. Em particular, essa interface permite que os drivers acessem rotinas que o motorista do ônibus fornece para as seguintes funções:

  • Traduzindo endereços de barramento
  • Recuperando uma estrutura de adaptador de DMA nos casos em que o adaptador de barramento dá suporte a DMA
  • Lendo e definindo o espaço de configuração do barramento para um dispositivo específico no barramento

Para usar essa interface, envie um IRP_MN_QUERY_INTERFACE IRP para o driver de barramento com InterfaceType = GUID_BUS_INTERFACE_STANDARD. O driver de barramento fornece um ponteiro para uma estrutura BUS_INTERFACE_STANDARD que contém ponteiros para as rotinas individuais da interface.

É preferível usar BUS_INTERFACE_STANDARD sempre que possível, porque um número de ônibus não é necessário para recuperar informações de configuração ao usar BUS_INTERFACE_STANDARD, enquanto os motoristas geralmente devem identificar o número do ônibus ao recuperar interfaces específicas do ônibus. Os números de ônibus para alguns ônibus, como o PCI, podem mudar dinamicamente. Portanto, os motoristas não devem depender do número do ônibus para acessar as portas PCI diretamente. Isso pode levar à falha do sistema.

Três etapas são necessárias ao acessar o espaço de configuração de um dispositivo PCI em IRQL = DISPATCH_LEVEL:

  1. Envie uma solicitação de IRP_MN_QUERY_INTERFACE em IRQL = PASSIVE_LEVEL para obter a estrutura de interface de chamada direta (BUS_INTERFACE_STANDARD) do driver de barramento PCI. Armazene isso em uma memória de pool nãopaged (normalmente em uma extensão de dispositivo).

  2. Chame as rotinas da interface BUS_INTERFACE_STANDARD , SetBusData e GetBusData, para acessar o espaço de configuração da PCI em IRQL = DISPATCH_LEVEL.

  3. Desreferenciar a interface. O driver de barramento PCI usa uma contagem de referência na interface antes de retornar, portanto, o driver que acessa a interface deve desreferi-la, uma vez que ela não é mais necessária.

O exemplo de código a seguir demonstra como implementar estas três etapas:

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

O snippet de código a seguir mostra como usar a rotina de interface GetBusData para obter os dados de espaço de configuração (etapa 2).

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

Quando o driver é feito com a interface , ele pode usar código semelhante ao snippet a seguir para desreferenciar a interface (etapa 3). Os drivers não devem chamar rotinas de interface depois de desreferenciar a interface.

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

A interface sincroniza o acesso do chamador ao hardware do barramento com o acesso do driver de barramento PCI. O gravador de driver não precisa se preocupar em criar bloqueios de rotação para evitar a disputa com o motorista do barramento PCI pelo acesso ao hardware do ônibus.

Observe que, se tudo o que é necessário são números de barramento, função e dispositivo, geralmente é desnecessário recorrer a uma interface de barramento para obter essas informações. Esses dados podem ser recuperados indiretamente passando o PDO do dispositivo de destino para a função IoGetDeviceProperty da seguinte maneira:

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