Uso de interfaces de Driver-Defined

Los controladores pueden definir interfaces específicas del dispositivo a las que pueden acceder otros controladores. Estas interfaces definidas por el controlador pueden constar de un conjunto de rutinas invocables, un conjunto de estructuras de datos o ambos. Normalmente, el controlador proporciona punteros a estas rutinas y estructuras en una estructura de interfaz definida por el controlador, que el controlador pone a disposición de otros controladores.

Por ejemplo, un controlador de bus puede proporcionar una o varias rutinas a las que los controladores de nivel superior pueden llamar para obtener información sobre un dispositivo secundario, si esa información no está disponible en la lista de recursos del dispositivo secundario.

Para obtener un ejemplo de un conjunto de interfaces definidas por el controlador que se documentan en el WDK, consulte Rutinas USB. Consulte también la versión basada en el marco del ejemplo de tostadora .

Creación de una interfaz

Cada interfaz definida por el controlador se especifica mediante:

  • Un identificador GUID

  • Un número de versión

  • Una estructura de interfaz definida por el controlador

  • Rutinas de referencia y desreferencia

Para crear una interfaz y ponerla a disposición de otros controladores, los controladores basados en marcos pueden seguir estos pasos:

  1. Defina una estructura de interfaz.

    El primer miembro de esta estructura definida por el controlador debe ser una estructura de encabezado INTERFACE . Los miembros adicionales pueden incluir datos de interfaz y punteros a estructuras o rutinas adicionales a las que puede llamar otro controlador.

    El controlador debe proporcionar una estructura WDF_QUERY_INTERFACE_CONFIG , que describe la interfaz que ha definido.

    Nota

    Al usar WDF_QUERY_INTERFACE_CONFIG, WDF no admite varias versiones de una sola interfaz que usen el mismo GUID de interfaz.

    Como resultado, al introducir una nueva versión de una interfaz existente, se recomienda crear un NUEVO GUID en lugar de revisar los campos Tamaño o Versión de la estructura INTERFACE .

    Si el controlador reutiliza el mismo GUID de interfaz con los campos Tamaño o Versión modificados, el controlador no debe proporcionar WDF_QUERY_INTERFACE_CONFIG y, en su lugar, debe proporcionar una rutina de devolución de llamada EvtDeviceWdmIrpPreprocess para IRP_MN_QUERY_INTERFACE.

  2. Llame a WdfDeviceAddQueryInterface.

    El método WdfDeviceAddQueryInterface hace lo siguiente:

    • Almacena información sobre la interfaz, como su GUID, el número de versión y el tamaño de la estructura, por lo que el marco puede reconocer la solicitud de otro controlador para la interfaz.
    • Registra una función de devolución de llamada de evento EvtDeviceProcessQueryInterfaceRequest opcional, que el marco llama cuando otro controlador solicita la interfaz.

Cada instancia de una interfaz definida por el controlador está asociada a un dispositivo individual, por lo que los controladores suelen llamar a WdfDeviceAddQueryInterface desde una función de devolución de llamada EvtDriverDeviceAdd o EvtChildListCreateDevice .

Acceso a una interfaz

Si el controlador ha definido una interfaz, otro controlador basado en marco puede solicitar acceso a la interfaz llamando a WdfFdoQueryForInterface y pasando un GUID, número de versión, puntero a una estructura y el tamaño de la estructura. El marco crea una solicitud de E/S y la envía a la parte superior de la pila de controladores.

Normalmente, un controlador llama a WdfFdoQueryForInterface desde una función de devolución de llamada EvtDriverDeviceAdd . Como alternativa, si el controlador debe liberar la interfaz cuando el dispositivo no está en su estado de funcionamiento, el controlador puede llamar a WdfFdoQueryForInterface desde dentro de una función de devolución de llamada EvtDevicePrepareHardware y llamar a la rutina de desreferencia de la interfaz desde dentro de una función de devolución de llamada EvtDeviceReleaseHardware .

Si el controlador A solicita al controlador B una interfaz que ha definido el controlador B, el marco controla la solicitud del controlador B. El marco comprueba que el GUID y la versión representan una interfaz compatible y que el tamaño de estructura que proporciona el controlador A es lo suficientemente grande como para contener la interfaz.

Cuando un controlador llama a WdfFdoQueryForInterface, la solicitud de E/S que crea el marco viaja hasta la parte inferior de la pila del controlador. Si una pila de controladores simple consta de tres controladores (A, B y C) y si el controlador A solicita una interfaz, tanto el controlador B como el controlador C pueden admitir la interfaz. Por ejemplo, el controlador B podría rellenar la estructura de la interfaz del controlador A antes de pasar la solicitud al controlador C. El controlador C puede proporcionar una función de devolución de llamada EvtDeviceProcessQueryInterfaceRequest que examine el contenido de la estructura de la interfaz y, posiblemente, las modifique.

Si el controlador A necesita acceder a la interfaz del controlador B y el controlador B es un destino de E/S remoto (es decir, un controlador que está en una pila de controladores diferente), el controlador A debe llamar a WdfIoTargetQueryForInterface en lugar de WdfFdoQueryForInterface.

Uso de One-Way o comunicación de Two-Way

Puede definir una interfaz que proporcione una comunicación unidireccional o una que proporcione comunicación bidireccional. Para especificar la comunicación bidireccional, el controlador establece el miembro ImportInterface de su estructura WDF_QUERY_INTERFACE_CONFIGen TRUE.

Si la interfaz proporciona comunicación unidireccional y si el controlador A solicita la interfaz del controlador B, los datos de la interfaz fluyen solo del controlador B al controlador A. Cuando el marco recibe la solicitud del controlador A para una interfaz que admite la comunicación unidireccional, el marco copia los valores de interfaz definidos por el controlador en la estructura de la interfaz del controlador A. A continuación, llama a la función de devolución de llamada EvtDeviceProcessQueryInterfaceRequest del controlador B, si existe, para que pueda examinar y posiblemente modificar los valores de la interfaz.

Si la interfaz proporciona comunicación bidireccional, la estructura de la interfaz contiene algunos miembros que el controlador A rellena antes de enviar la solicitud al controlador B. El controlador B puede leer los valores de parámetro que proporcionó el controlador A y tomar decisiones, en función de esos valores, sobre la información que se debe proporcionar al controlador A. Cuando el marco recibe la solicitud del controlador A para una interfaz que admite la comunicación bidireccional, el marco llama a la función de devolución de llamada EvtDeviceProcessQueryInterfaceRequest del controlador B para que pueda examinar los valores recibidos y proporcionar valores de salida. Para la comunicación bidireccional, la función de devolución de llamada es necesaria porque el marco no copia ningún valor de interfaz en la estructura de la interfaz del controlador A.

Mantenimiento de un recuento de referencias

Cada interfaz debe incluir una función de referencia y una función de desreferencia, que incrementan y reducen un recuento de referencias para la interfaz. El controlador que define la interfaz especifica las direcciones de estas funciones en su estructura INTERFACE .

Cuando el controlador A solicita al controlador B una interfaz, el marco llama a la función de referencia de la interfaz antes de que la interfaz esté disponible para el controlador A. Cuando el controlador A ha terminado de usar la interfaz , debe llamar a la función de desreferencia de la interfaz.

Las funciones de referencia y desreferencia para la mayoría de las interfaces pueden ser funciones sin operación que no hacen nada. El marco proporciona funciones de recuento de referencias sin operación, WdfDeviceInterfaceReferenceNoOp y WdfDeviceInterfaceDereferenceNoOp, que la mayoría de los controladores pueden usar.

La única vez que los controladores deben realizar un seguimiento del recuento de referencias de una interfaz y proporcionar funciones de referencia y desreferencia reales es cuando el controlador A solicita una interfaz desde un destino de E/S remoto (es decir, un controlador que se encuentra en una pila de controladores diferente). En este caso, el controlador B (en una pila diferente) debe implementar un recuento de referencias para que pueda impedir que se quite su dispositivo mientras el controlador A usa la interfaz del controlador B.

Si va a diseñar el controlador B, que define una interfaz, debe decidir si se tendrá acceso a la interfaz del controlador desde una pila de controladores diferente. (El controlador B no puede determinar si una solicitud de su interfaz procede de la pila de controladores local o de una pila remota). Si el controlador admitirá solicitudes de interfaz desde una pila remota, el controlador debe implementar un recuento de referencias.

Si está diseñando el controlador A, que tiene acceso a la interfaz en el destino de E/S remoto, el controlador debe proporcionar una función de devolución de llamada EvtIoTargetQueryRemove que libere la interfaz cuando el dispositivo del controlador B está a punto de quitarse, una función de devolución de llamada EvtIoTargetRemoveComplete que libere la interfaz cuando el dispositivo del controlador B se quite por sorpresa y un EvtIoTargetRemoveCanceled . función de devolución de llamada que vuelve a adquirir la interfaz si se canceló un intento de quitar el dispositivo.