Escritura de un controlador cliente de controlador de funciones
En este artículo se describen las diversas tareas que realiza un controlador cliente del controlador de funciones al interactuar con la extensión del controlador de funciones USB (UFX).
API importantes
Describe las diversas tareas que realiza un controlador cliente del controlador de funciones al interactuar con la extensión del controlador de funciones USB (UFX). UFX y el controlador de cliente se comunican entre sí mediante métodos de exportación y funciones de devolución de llamada de eventos. Los métodos de exportación ( denominados UfxDeviceXxx o UfxEndpointXxx) se exportan mediante UFX e invocan el controlador de cliente. Las funciones de devolución de llamada ( denominadas EVT_UFX_Xxx)se implementan en el controlador de cliente e invocan por UFX.
UFX llama a todas las funciones de devolución de llamada del controlador de cliente de forma asincrónica y una devolución de llamada a la vez por objeto. Por ejemplo, hay un objeto de dispositivo USB y tres objetos de punto de conexión. Como máximo, se pueden llamar cuatro funciones de devolución de llamada (una para el dispositivo y otra para cada punto de conexión). Para cada método de devolución de llamada, UFX espera hasta que el controlador cliente llama a UfxDeviceEventComplete para indicar que el controlador ha completado la solicitud. El único método de exportación que UFX escucha mientras espera estas exportaciones es UfxDeviceNotifyHardwareFailure. Muchas funciones de devolución de llamada de cliente son opcionales. Las funciones necesarias son las siguientes:
- EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD
- EVT_UFX_DEVICE_ENDPOINT_ADD
- EVT_UFX_DEVICE_HOST_CONNECT
- EVT_UFX_DEVICE_HOST_DISCONNECT
- EVT_UFX_DEVICE_ADDRESSED
Inicialización
- El controlador cliente del controlador de funciones inicia el proceso de inicialización cuando Windows Driver Foundation (WDF) invoca la implementación del controlador cliente de la devolución de llamada de EVT_WDF_DRIVER_DEVICE_ADD . En esa implementación, se espera que el controlador de cliente llame a UfxFdoInit y, a continuación, cree el objeto de dispositivo mediante una llamada a WdfDeviceCreate.
- El controlador cliente llama a UfxDeviceCreate para crear el objeto de dispositivo USB y recuperar el identificador UFXDEVICE.
- El controlador cliente llama a UfxDeviceNotifyHardwareReady para indicar a UFX que ahora puede invocar las funciones de devolución de llamada del controlador cliente.
- UFX realiza tareas de inicialización como:
- UFX llama a la implementación de EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD del controlador cliente para crear el punto de conexión predeterminado.
- UFX crea objetos de dispositivo físico secundarios (PDA) para interfaces compatibles con el dispositivo.
- UFX espera la activación del controlador de clase de dispositivo cuando envía la solicitud de IOCTL_INTERNAL_USBFN_ACTIVATE_USB_BUS . También espera a que el controlador cliente llame a UfxDeviceNotifyAttach que indica que el dispositivo se ha conectado.
Notificación del controlador de clase
Para recibir una notificación de los paquetes de instalación y el estado del bus, un controlador de clase debe enviar una solicitud de IOCTL_INTERNAL_USBFN_ACTIVATE_USB_BUS . UFX pone estas solicitudes en colas de notificaciones específicas del controlador de clase. Al recibir una notificación sobre un evento de autobús del controlador cliente, UFX extrae de cada cola adecuada y completa la solicitud. Para evitar que falten notificaciones a los controladores de clase, UFX mantiene una cola de notificaciones de tamaño fijo para el controlador de clase.
Eventos de asociación y desasociación de dispositivos
UFX supone que el dispositivo se desasocia hasta que el controlador cliente del controlador de funciones llama a UfxDeviceNotifyAttach.
Después de esa llamada, UFX establece el estado del dispositivo en Powered tal como se define en la especificación USB. Para notificar al controlador cliente el cambio de estado, UFX invoca la implementación de EVT_UFX_DEVICE_USB_STATE_CHANGE del controlador cliente.
UFX notifica al controlador del cargador (Cad.sys) para ayudar a cargar el dispositivo. UFX también notifica a los controladores de clase completando IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION solicitudes enviadas previamente por los controladores de clase.
El controlador cliente debe llamar a UfxDeviceNotifyDetach cuando se desasocia el bus. El cliente solo debe llamar a detach una vez después de cada llamada a UfxDeviceNotifyAttach. Después de la llamada UfxDeviceNotifyDetach , UFX llama a EVT_UFX_DEVICE_HOST_DISCONNECT (si no se trata de un cambio de interfaz). UFX continúa con todas las tareas de limpieza, como purgar todas las colas de puntos de conexión e iniciar la cola de punto de conexión predeterminada. UFX llama a EVT_UFX_DEVICE_USB_STATE_CHANGE y notifica a los controladores de clase completando IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION solicitudes.
Error de hardware
Si se produce un error de hardware, se espera que el controlador cliente llame a UfxDeviceNotifyHardwareFailure. En respuesta, UFX anulará la pila de dispositivos y podría intentar recuperarse de esta situación llamando al EVT_UFX_DEVICE_CONTROLLER_RESET del controlador cliente. El cliente debe restablecer el controlador a su estado inicial. Si se produce otro error de hardware, el cliente debe llamar a UfxDeviceNotifyHardwareFailure de nuevo. En la segunda llamada, UFX registrará su estado y comprobación de errores.
Detección de puertos
La detección de puertos se realiza mediante UFX. Llama a la función de devolución de llamada del controlador de cliente del controlador de función EVT_UFX_DEVICE_PORT_DETECT para determinar el tipo de puerto al que está conectado el dispositivo. El cliente responde llamando a UfxDevicePortDetectComplete o UfxDevicePortDetectCompleteEx con uno de los tipos de puerto definidos en USBFN_PORT_TYPE.
Si el cliente no puede determinar el tipo de puerto, el cliente debe notificar UsbfnUnknownPort. Si el puerto es desconocido o un puerto de bajada, UFX llama a la función EVT_UFX_DEVICE_HOST_CONNECT del controlador cliente. UFX escucha el autobús durante algún tiempo. Si el puerto es desconocido, pero hay tráfico, como un paquete de instalación, UFX asume UsbfnStandardDownstreamPort. De lo contrario, UFX asigna el puerto a UsbfnInvalidDedicatedChargingPort. Una vez determinado un tipo de puerto, UFX notifica Cad.sys y llama a la función EVT_UFX_DEVICE_PORT_CHANGE del controlador cliente. En la función, se espera que el controlador cliente cambie el estado del hardware para que coincida con el tipo de puerto UFX.
Creación de puntos de conexión
UFX crea el punto de conexión predeterminado (punto de conexión 0) llamando al EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD del controlador cliente para que pueda controlar los paquetes de instalación enviados por el host. UFX crea otros puntos de conexión llamando a EVT_UFX_DEVICE_ENDPOINT_ADD. UFX solo crea puntos de conexión después de que el controlador cliente llame a UfxDeviceNotifyHardwareReady. En estas funciones de devolución de llamada, se espera que el controlador de cliente llame a UfxEndpointCreate al objeto de punto de conexión y obtenga su identificador UFXENDPOINT. UFX establece el elemento primario en el PDO del controlador de clase asociado a la interfaz a la que pertenece el punto de conexión. El elemento primario del punto de conexión predeterminado es el objeto de dispositivo USB. Un punto de conexión contiene dos objetos de cola de marco: una cola de transferencia y una cola de comandos, a las que solo se puede tener acceso cuando el dispositivo está en estado Configurado (con la excepción del punto de conexión 0, al que se puede acceder después de que UFX llame a EVT_UFX_DEVICE_HOST_CONNECT).
- Solicitudes de cola de comandos
- IOCTL_INTERNAL_USBFN_GET_PIPE_STATE
- IOCTL_INTERNAL_USBFN_SET_PIPE_STATE
- IOCTL_INTERNAL_USBFN_DESCRIPTOR_UPDATE
- Transferencia de solicitudes de cola
- IOCTL_INTERNAL_USBFN_TRANSFER_IN
- IOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKT
- IOCTL_INTERNAL_USBFN_TRANSFER_OUT
- IOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_IN
- IOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_OUT
Enumeración de dispositivos
El controlador cliente no debe permitir conexiones a un host antes de que UFX llame al EVT_UFX_DEVICE_HOST_CONNECT del controlador. La enumeración de dispositivos comienza cuando el controlador cliente llama a UfxDeviceNotifyReset. En el estado Predeterminado , UFX controla los paquetes de configuración estándar.
Reset
UFX purga todas las colas de puntos de conexión y envía una solicitud de IOCTL_INTERNAL_USBFN_DESCRIPTOR_UPDATE al controlador cliente para actualizar wMaxPacketSize del punto de conexión 0. UFX inicia la cola del punto de conexión predeterminado y establece el estado en Predeterminado.
Default
UFX llama a la función EVT_UFX_DEVICE_USB_STATE_CHANGE del controlador cliente. También notifica a los controladores de clase del estado. Una vez que UFX recibe el paquete de configuración estándar SET_ADDRESS, UFX establece el estado en Solucionado.
Dirigida
UFX llama a la función EVT_UFX_DEVICE_ADDRESSED del controlador cliente para indicar al cliente la dirección que debe usar. - Si la dirección es 0, UFX vuelve a establecer el estado en Default y llama a EVT_UFX_DEVICE_USB_STATE_CHANGE y notifica a los controladores de clase. Al recibir el paquete de configuración estándar SET_CONFIGURATION, UFX establece el estado en Configurado.
Configurado
Si la configuración seleccionada es 0, UFX purga los puntos de conexión de la interfaz y establece el estado en Solucionado. UFX envía una solicitud de IOCTL_INTERNAL_USBFN_DESCRIPTOR_UPDATE al controlador cliente para actualizar wMaxPacketSize de los puntos de conexión de interfaz. UFX garantiza que todas las colas de puntos de conexión de interfaz hayan terminado de purgar e iniciar las colas de puntos de conexión de interfaz. Si el tipo de puerto no es UsbfnStandardDownstreamPort o UsbfnChargingDownstreamPort, UFX cambia el tipo de puerto a UsbfnStandardDownstreamPort e informa Cad.sys; el controlador cliente llamando a EVT_UFX_DEVICE_PORT_CHANGE y EVT_UFX_DEVICE_USB_STATE_CHANGE para actualizar el estado; los controladores de clase del estado configurado.
Transferencias de control estándar
UFX puede controlar las transferencias de control en el punto de conexión predeterminado en cualquier momento después de llamar a EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD, en la que el controlador cliente crea el punto de conexión predeterminado mediante. Todas las transferencias de control comienzan con un paquete de configuración de 8 bytes. Para enviar un paquete de instalación al host, el controlador de cliente debe llamar a UfxEndpointNotifySetup. UFX completa las transferencias de control estándar. Si hay datos asociados a la transferencia de control, UFX lee y escribe en el punto de conexión de control predeterminado según corresponda.
Transferencias de control no estándar
Si UFX no puede controlar una transferencia de control, la transferencia se reenvía al controlador de clase adecuado completando una solicitud de IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION . Las transferencias de control pueden producirse en cualquier punto de conexión que se define como un punto de conexión de control en el descriptor de punto de conexión. Las transferencias de control en puntos de conexión distintos del punto de conexión de control predeterminado siempre son transferencias de control no estándar. Si el punto de conexión de control es el punto de conexión de control predeterminado, UFX notificará a los controladores de clase de los paquetes de instalación que se marcan como solicitudes de clase para ese controlador de clase. Si el punto de conexión de control pertenece a una interfaz, UFX notifica al controlador de clase asociado a esa interfaz. Si es necesario, se espera que los controladores de clase lean y escriban en el punto de conexión de control.
Transferencias de datos
Los controladores de clase inician las transferencias de datos mediante el envío de solicitudes de IOCTL_INTERNAL_USBFN_TRANSFER_IN, IOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKT o IOCTL_INTERNAL_USBFN_TRANSFER_OUT . Después de validar cada una de esas solicitudes, UFX la reenvía a la cola de puntos de conexión adecuada que el controlador cliente controlará. Se espera que el controlador de cliente realice una validación adicional. El controlador cliente recibe solicitudes de transferencia en las colas de puntos de conexión. El controlador de cliente puede recuperar tantas solicitudes de esta cola como necesite para maximizar el uso del bus. El controlador cliente debe completar las solicitudes correctas con STATUS_SUCCESS. El controlador debe realizar un mejor intento de cancelar solicitudes y completar las solicitudes canceladas con STATUS_CANCELLED si se cancela. Si se pasan parámetros no válidos, el controlador cliente completa la solicitud con STATUS_INVALID_PARAMETER.
Transferencias de control
Las transferencias de control comienzan con un paquete de configuración de 8 bytes. Para enviar un paquete de instalación al host, el controlador de cliente debe llamar a UfxEndpointNotifySetup. UFX notifica a los controladores de clase de transferencias de control no estándar completando solicitudes de notificación. Tanto los clientes como UFX usan IOCTL_INTERNAL_USBFN_TRANSFER_IN, IOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKT o IOCTL_INTERNAL_USBFN_TRANSFER_OUT para leer y escribir en el punto de conexión de control predeterminado. Sin embargo, una interfaz puede definir otros puntos de conexión de control, que solo puede usar el controlador de clase correspondiente. Los puntos de conexión de control se pueden detener en respuesta a un paquete de configuración. Los controladores de clase envían la solicitud de IOCTL_INTERNAL_USBFN_SET_PIPE_STATE para detener el punto de conexión. Se espera que el hardware o el controlador de cliente reanuden inmediatamente el tráfico en el punto de conexión después de enviar la detención. Los puntos de conexión de control también pueden enviar y recibir paquetes de longitud cero (ZLP) sin datos anteriores. El controlador de cliente y UFX pueden hacerlo mediante IOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_IN y IOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_OUT.
Transferencias masivas e interrupciones
Las transferencias masivas garantizan la entrega de datos y se usan para enviar grandes cantidades de datos. Las transferencias se pueden enviar en un punto de conexión masivo mediante IOCTL_INTERNAL_USBFN_TRANSFER_IN, IOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKT o IOCTL_INTERNAL_USBFN_TRANSFER_OUT. Los puntos de conexión masivos se pueden detener de forma similar a controlar los puntos de conexión mediante IOCTL_INTERNAL_USBFN_SET_PIPE_STATE. Se espera que el controlador de cliente envíe un paquete STALL en respuesta a todas las solicitudes de host y mantenga las solicitudes IOCTL. A diferencia de los puntos de conexión de control, un punto de conexión masivo detenido permanece detenido hasta que el estado de detención se borra explícitamente.
Las transferencias de interrupción son como transferencias masivas, pero tienen una latencia garantizada. Las transferencias de interrupción tienen la misma interfaz que las transferencias masivas, pero no tienen funcionalidades de streaming.
Transferencias isócrónicas
No se espera que el controlador de cliente admita transferencias isócrónicas en esta versión.
Administración de energía
El controlador cliente posee todos los aspectos de la administración de energía. Dado que las funciones de devolución de llamada son asincrónicas, se espera que el controlador de cliente vuelva a un estado de alimentación adecuado y complete la solicitud antes de llamar a la función de exportación completa de eventos adecuada, como UfxDeviceEventComplete.
UFX está en un estado working si el estado del dispositivo (definido en USBFN_DEVICE_STATE) es UsbfnDeviceStateSuspended y UsbfnDeviceStateAttached y no ha notificado un tipo de puerto. Como alternativa, UFX ha notificado el tipo de puerto (definido en USBFN_PORT_TYPE) UsbfnStandardDownstreamPort o UsbfnChargingDownstreamPort.
UFX entra y sale de un estado Working llamando a EVT_UFX_DEVICE_USB_STATE_CHANGE o EVT_UFX_DEVICE_PORT_CHANGE implementaciones. La transición a o desde un estado Working se completa cuando el controlador cliente llama a UfxDeviceEventComplete.
En un estado de trabajo, UFX puede llamar a cualquier devolución de llamada. Aunque no está en estado de trabajo, UFX solo llama a EVT_UFX_DEVICE_USB_STATE_CHANGE para entrar en un estado de trabajo; EVT_UFX_DEVICE_REMOTE_WAKEUP_SIGNAL para emitir una reactivación remota durante la suspensión (si se admite).
Suspensión del dispositivo
La suspensión del dispositivo se produce cuando no hay tráfico en el bus durante 3 milisegundos. En este caso, el controlador cliente debe informar a UFX cuando detecte la suspensión y reanudación llamando a UfxDeviceNotifySuspend y UfxDeviceNotifyResume. Al recibir esas llamadas, UFX llama a EVT_UFX_DEVICE_USB_STATE_CHANGE y notifica a los controladores de clase completando IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION solicitudes. Si el dispositivo admite la reactivación remota y está habilitada por el host, UFX puede llamar a llamadas EVT_UFX_DEVICE_USB_STATE_CHANGE mientras se suspende para emitir una señal de reactivación remota.