Compartir a través de


Obtención de información de configuración de dispositivos en IRQL = DISPATCH_LEVEL

El método que se muestra en la sección Obtención de información de configuración del dispositivo en IRQL = PASSIVE_LEVEL usa paquetes de solicitud de E/S (IRP) y, por tanto, solo es válido para los controladores que se ejecutan en IRQL = PASSIVE_LEVEL. Los controladores que se ejecutan en IRQL = DISPATCH_LEVEL deben usar una interfaz de bus para obtener datos del espacio de configuración del dispositivo. Para obtener estos datos, puede usar una interfaz específica del bus o la interfaz de bus independiente del bus proporcionada por el sistema, BUS_INTERFACE_STANDARD.

La interfaz GUID_BUS_INTERFACE_STANDARD (definida en wdmguid.h) permite a los controladores de dispositivo realizar llamadas directas a rutinas de controladores de bus primarios en lugar de usar paquetes de solicitud de E/S (IRP) para comunicarse con el controlador de bus. En concreto, esta interfaz permite a los controladores acceder a rutinas que proporciona el controlador de bus para las siguientes funciones:

  • Traducción de direcciones de bus
  • Recuperación de una estructura de adaptador DMA en casos en los que el adaptador de bus admite DMA
  • Lectura y configuración del espacio de configuración del bus para un dispositivo determinado en el bus

Para usar esta interfaz, envíe una IRP_MN_QUERY_INTERFACE IRP al controlador de bus con InterfaceType = GUID_BUS_INTERFACE_STANDARD. El controlador de bus proporciona un puntero a una estructura BUS_INTERFACE_STANDARD que contiene punteros a las rutinas individuales de la interfaz.

Es preferible usar BUS_INTERFACE_STANDARD siempre que sea posible, ya que no se requiere un número de bus para recuperar información de configuración al usar BUS_INTERFACE_STANDARD, mientras que los controladores a menudo deben identificar el número de bus al recuperar interfaces específicas del bus. Los números de autobús de algunos autobuses, como PCI, pueden cambiar dinámicamente. Por lo tanto, los controladores no deben depender del número de autobús para acceder directamente a los puertos PCI. Si lo hace, podría provocar un error en el sistema.

Se requieren tres pasos al acceder al espacio de configuración de un dispositivo PCI en IRQL = DISPATCH_LEVEL:

  1. Envíe una solicitud de IRP_MN_QUERY_INTERFACE en IRQL = PASSIVE_LEVEL para obtener la estructura de la interfaz de llamada directa (BUS_INTERFACE_STANDARD) del controlador de bus PCI. Almacénelo en una memoria de grupo no paginada (normalmente en una extensión de dispositivo).

  2. Llame a las rutinas de interfaz de BUS_INTERFACE_STANDARD , SetBusData y GetBusData, para acceder al espacio de configuración pci en IRQL = DISPATCH_LEVEL.

  3. Desreferenciar la interfaz. El controlador de bus PCI toma un recuento de referencias en la interfaz antes de que vuelva, por lo que el controlador que accede a la interfaz debe desreferenciarlo, una vez que ya no sea necesario.

En el ejemplo de código siguiente se muestra cómo implementar estos tres pasos:

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

El siguiente fragmento de código muestra cómo usar la rutina de interfaz GetBusData para obtener los datos del espacio de configuración (paso 2).

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

Cuando el controlador se realiza con la interfaz , puede usar código similar al siguiente fragmento de código para desreferenciar la interfaz (paso 3). Los controladores no deben llamar a rutinas de interfaz después de desreferenciar la interfaz.

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

La interfaz sincroniza el acceso del autor de la llamada al hardware del bus con el acceso del controlador de bus PCI. El escritor de controladores no tiene que preocuparse por la creación de bloqueos de giro para evitar que el controlador de bus PCI tenga acceso al hardware del bus.

Tenga en cuenta que, si todo lo necesario es bus, función y números de dispositivo, normalmente no es necesario recurrir a una interfaz de bus para obtener esta información. Estos datos se pueden recuperar indirectamente pasando el PDO del dispositivo de destino a la función IoGetDeviceProperty de la siguiente manera:

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