Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se explica lo siguiente:
- Escriba un controlador de origen Kernel-Mode Driver Framework (KMDF HID) que envíe informes de lectura HID a Windows.
- Cargue el controlador VHF como filtro inferior al controlador de origen HID en la pila de dispositivos HID virtual.
Obtenga información sobre cómo escribir un controlador de origen HID que informe de datos HID al sistema operativo.
Un dispositivo de entrada HID, como un teclado, un mouse, un lápiz, un toque o un botón, envía varios informes al sistema operativo para que pueda comprender el propósito del dispositivo y tomar las medidas necesarias. Los informes están en forma de colecciones HID y usos de HID. El dispositivo envía esos informes a través de varios transportes, algunos de los cuales Admite Windows, como HID a través de I2C y HID a través de USB. En algunos casos, Windows no admite el transporte o los informes no se asignan directamente al hardware real. Puede ser un flujo de datos en formato HID que otro componente de software envía para hardware virtual, como para botones o sensores que no son GPIO. Por ejemplo, imagina los datos del acelerómetro de un teléfono que se comporta como un controlador de juego, enviados de forma inalámbrica a un PC. En otro ejemplo, un equipo puede recibir la entrada remota de un dispositivo Miracast mediante el protocolo UIBC.
En versiones anteriores de Windows, para admitir nuevos transportes (hardware real o software), tenía que escribir un minidriver de transporte HID y asociarlo al controlador de clase integrada proporcionado por Microsoft, Hidclass.sys. El par de controladores de clase/mini proporcionó las colecciones HID, como Top-Level Colecciones a controladores de nivel superior y aplicaciones en modo de usuario. En ese modelo, el desafío era escribir el minidriver, que puede ser una tarea compleja.
A partir de Windows 10, la nueva plataforma de HID virtual (VHF) elimina la necesidad de desarrollar un controlador mínimo de transporte. En su lugar, puede escribir un controlador de origen HID mediante interfaces de programación KMDF o WDM. El marco consta de una biblioteca estática proporcionada por Microsoft que expone los elementos de programación usados por el controlador. También incluye un controlador proporcionado por Microsoft que enumera uno o varios dispositivos secundarios y procede a construir y gestionar un árbol HID virtual.
Nota:
En esta versión, VHF solo admite un controlador de origen HID en modo kernel.
En este artículo se describe la arquitectura del marco, el árbol de dispositivos HID virtual y los escenarios de configuración.
Árbol de dispositivos HID virtuales
En esta imagen, el árbol de dispositivos muestra los controladores y sus objetos de dispositivo asociados.
Controlador de origen HID (tu controlador)
El controlador de origen HID se vincula a Vhfkm.lib e incluye Vhf.h en su proyecto de compilación. El controlador se puede escribir mediante windows Driver Model (WDM) o Kernel-Mode Driver Framework (KMDF) que forma parte de Windows Driver Frameworks (WDF). El controlador se puede cargar como un controlador de filtro o un controlador de función en la pila de dispositivos.
Biblioteca estática VHF (vhfkm.lib)
La biblioteca estática se incluye en el Kit de controladores de Windows (WDK) para Windows 10. La biblioteca expone interfaces de programación, como rutinas y funciones de devolución de llamada, que son utilizadas por el controlador de origen HID. Cuando el controlador llama a una función, la biblioteca estática reenvía la solicitud al controlador VHF que controla la solicitud.
Controlador VHF (Vhf.sys)
Un controlador incluido proporcionado por Microsoft. Este controlador debe cargarse como un controlador de filtro inferior debajo de su controlador en la pila de dispositivos de origen de HID. El controlador VHF enumera dinámicamente los dispositivos secundarios y crea objetos de dispositivo físico (PDO) para uno o varios dispositivos HID que especifica tu controlador de origen HID. También implementa la funcionalidad del mini-controlador HID Transport de los dispositivos secundarios enumerados.
Par de controladores de clase HID (Hidclass.sys, Mshidkmdf.sys)
El par Hidclass/Mshidkmdf enumera Top-Level Collections (TLC) similar a cómo enumera esas colecciones para un dispositivo HID real. Un cliente HID puede seguir solicitando y consumir los TLC igual que un dispositivo HID real. Esta pareja de controladores se instala como controlador funcional en la pila de dispositivos.
Nota:
En algunos escenarios, es posible que un cliente HID necesite identificar el origen de datos HID. Por ejemplo, un sistema tiene un sensor integrado y recibe datos de un sensor remoto del mismo tipo. Es posible que el sistema quiera elegir un sensor para que sea más confiable. Para diferenciar entre los dos sensores conectados al sistema, el cliente HID consulta el identificador de contenedor del TLC. En este caso, un controlador de origen HID puede proporcionar el identificador de contenedor, que se notifica como el identificador de contenedor del dispositivo HID virtual por VHF.
Cliente HID (aplicación)
Consulta y consume las TLCs que reporta la pila de dispositivos HID.
Requisitos de encabezado y biblioteca
En este procedimiento se describe cómo escribir un controlador de origen HID que notifica botones de auriculares al sistema operativo. En este caso, el controlador que implementa este código puede ser un controlador de audio KMDF modificado existente, que funciona como fuente HID para reportar los botones de los auriculares mediante VHF.
Incluya Vhf.h del WDK.
Vínculo a vhfkm.lib incluido en el WDK.
Elabora un descriptor de informe HID que tu dispositivo debe reportar al sistema operativo. En este ejemplo, el descriptor de informe HID describe los botones del casco. El informe especifica un informe de entrada HID, tamaño de 8 bits (1 byte). Los tres primeros bits son para los botones central, de volumen superior y de reducción de volumen del casco. Los bits restantes están sin usar.
UCHAR HeadSetReportDescriptor[] = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop Controls) 0x09, 0x0D, // USAGE (Portable Device Buttons) 0xA1, 0x01, // COLLECTION (Application) 0x85, 0x01, // REPORT_ID (1) 0x05, 0x09, // USAGE_PAGE (Button Page) 0x09, 0x01, // USAGE (Button 1 - HeadSet : middle button) 0x09, 0x02, // USAGE (Button 2 - HeadSet : volume up button) 0x09, 0x03, // USAGE (Button 3 - HeadSet : volume down button) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x03, // REPORT_COUNT (3) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x05, // REPORT_COUNT (5) 0x81, 0x03, // INPUT (Cnst,Var,Abs) 0xC0, // END_COLLECTION };
Creación de un dispositivo HID virtual
Inicialice una estructura VHF_CONFIG llamando a la macro VHF_CONFIG_INIT y, a continuación, llame al método VhfCreate . El controlador debe llamar a VhfCreate en PASSIVE_LEVEL después de la llamada WdfDeviceCreate , normalmente, en la función de devolución de llamada EvtDriverDeviceAdd del controlador.
En la llamada VhfCreate , el controlador puede especificar determinadas opciones de configuración, como operaciones que se deben procesar de forma asincrónica o establecer información del dispositivo (identificadores de proveedor o producto).
Por ejemplo, una aplicación solicita un TLC. Cuando el par de controladores de clase HID recibe esa solicitud, el par determina el tipo de solicitud y crea una solicitud IOCTL de Minidriver HID adecuada, reenvíándola a VHF. Al recibir la solicitud IOCTL, VHF puede gestionar la solicitud, confiar en el controlador de origen HID para procesarla, o completar la solicitud con ESTADO_NO_SOPORTADO.
VHF controla estos IOCTLs:
- IOCTL_HID_GET_STRING
- IOCTL_HID_GET_DEVICE_ATTRIBUTES
- IOCTL_HID_GET_DEVICE_DESCRIPTOR
- IOCTL_HID_GET_REPORT_DESCRIPTOR
Si la solicitud es GetFeature, SetFeature, WriteReport o GetInputReport, y el controlador de origen HID registró una función de devolución de llamada correspondiente, VHF invoca la función de devolución de llamada. Dentro de esa función, el controlador de origen HID puede obtener o establecer datos HID para el dispositivo virtual HID. Si el controlador no registra una retrollamada, VHF completa la solicitud con el estado STATUS_NOT_SUPPORTED.
VHF invoca las funciones de devolución de llamada de eventos implementadas por el controlador de origen HID para estas IOCTLs:
-
Si el controlador quiere manejar la política de almacenamiento en búfer al presentar un búfer para obtener el informe de entrada HID, debe implementar EvtVhfReadyForNextReadReport y especificar un puntero en el miembro EvtVhfAsyncOperationGetInputReport. Para obtener más información, vea Enviar el informe de entrada HID.
IOCTL_HID_GET_FEATURE o IOCTL_HID_SET_FEATURE
Si el controlador quiere obtener o establecer un informe de características HID de forma asincrónica, el controlador debe implementar la función EvtVhfAsyncOperation y especificar un puntero a la función de implementación get o set en el miembro EvtVhfAsyncOperationGetFeature o EvtVhfAsyncOperationSetFeature de VHF_CONFIG.
-
Si el controlador quiere obtener un informe de entrada HID de forma asincrónica, el controlador debe implementar la función EvtVhfAsyncOperation y especificar un puntero a la función en el miembro EvtVhfAsyncOperationGetInputReport de VHF_CONFIG.
-
Si el controlador quiere obtener una escritura de un informe de entrada HID de forma asincrónica, el controlador debe implementar la función EvtVhfAsyncOperation y especificar un puntero a la función en el miembro EvtVhfAsyncOperationWriteReport de VHF_CONFIG.
Para cualquier otro IOCTL de Minidriver HID, VHF completa la solicitud con STATUS_NOT_SUPPORTED.
El dispositivo HID virtual se elimina llamando a VhfDelete. El método EvtVhfCleanup es necesario si el controlador asignó recursos para el dispositivo HID virtual. El controlador debe implementar la función EvtVhfCleanup y especificar un puntero a esa función en el miembro EvtVhfCleanup de VHF_CONFIG. Se invoca EvtVhfCleanup antes de que finalice la llamada de VhfDelete . Para obtener más información, consulte Eliminación del dispositivo HID virtual.
Nota:
Una vez completada una operación asincrónica, el controlador debe llamar a VhfAsyncOperationComplete para establecer los resultados de la operación. Puede llamar al método desde la devolución de llamada del evento o en un momento posterior después de completar la devolución de llamada.
NTSTATUS
VhfSourceCreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
WDF_OBJECT_ATTRIBUTES deviceAttributes;
PDEVICE_CONTEXT deviceContext;
VHF_CONFIG vhfConfig;
WDFDEVICE device;
NTSTATUS status;
PAGED_CODE();
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
deviceAttributes.EvtCleanupCallback = VhfSourceDeviceCleanup;
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (NT_SUCCESS(status))
{
deviceContext = DeviceGetContext(device);
VHF_CONFIG_INIT(&vhfConfig,
WdfDeviceWdmGetDeviceObject(device),
sizeof(VhfHeadSetReportDescriptor),
VhfHeadSetReportDescriptor);
status = VhfCreate(&vhfConfig, &deviceContext->VhfHandle);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "VhfCreate failed %!STATUS!", status);
goto Error;
}
status = VhfStart(deviceContext->VhfHandle);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "VhfStart failed %!STATUS!", status);
goto Error;
}
}
Error:
return status;
}
Envío del informe de entrada HID
Envíe el informe de entrada HID llamando a VhfReadReportSubmit.
Normalmente, un dispositivo HID envía información sobre los cambios de estado mediante el envío de informes de entrada a través de interrupciones. Por ejemplo, el dispositivo de auriculares podría enviar un informe cuando cambia el estado de un botón. En tal caso, se invoca la rutina de servicio de interrupción (ISR) del controlador. En esa rutina, el controlador podría programar una llamada a procedimiento diferido (DPC) que procesa el informe de entrada y la envía a VHF, que envía la información al sistema operativo. De forma predeterminada, VHF almacena en búfer el informe y el controlador de origen HID puede empezar a enviar informes de entrada HID conforme llegan. Este almacenamiento en búfer elimina la necesidad de que el controlador de origen HID implemente una sincronización compleja.
El controlador de origen HID puede enviar informes de entrada mediante la implementación de la política de almacenamiento en búfer para los informes pendientes. Para evitar el almacenamiento en búfer duplicado, el controlador de origen HID puede implementar la función de devolución de llamada EvtVhfReadyForNextReadReport y realizar un seguimiento de si VHF invocó esta devolución de llamada. Si se invocó anteriormente, el controlador de origen HID puede llamar a VhfReadReportSubmit para enviar un informe. Debe esperar a que EvtVhfReadyForNextReadReport se invoque antes de poder llamar a VhfReadReportSubmit de nuevo.
VOID
MY_SubmitReadReport(
PMY_CONTEXT Context,
BUTTON_TYPE ButtonType,
BUTTON_STATE ButtonState
)
{
PDEVICE_CONTEXT deviceContext = (PDEVICE_CONTEXT)(Context);
if (ButtonState == ButtonStateUp) {
deviceContext->VhfHidReport.ReportBuffer[0] &= ~(0x01 << ButtonType);
} else {
deviceContext->VhfHidReport.ReportBuffer[0] |= (0x01 << ButtonType);
}
status = VhfReadReportSubmit(deviceContext->VhfHandle, &deviceContext->VhfHidReport);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,"VhfReadReportSubmit failed %!STATUS!", status);
}
}
Eliminación del dispositivo HID virtual
Elimine el dispositivo HID virtual llamando a VhfDelete.
VhfDelete se puede llamar de forma sincrónica o asincrónica especificando el parámetro Wait. Para una llamada sincrónica, se debe llamar al método en PASSIVE_LEVEL, como desde EvtCleanupCallback del objeto de dispositivo. VhfDelete devuelve después de eliminar el dispositivo HID virtual. Si el controlador llama a VhfDelete de forma asincrónica, devuelve inmediatamente y VHF invoca EvtVhfCleanup una vez completada la operación de eliminación. Se puede llamar al método al máximo DISPATCH_LEVEL. En este caso, el controlador necesitaba registrar e implementar una función de devolución de llamada EvtVhfCleanup al haber llamado previamente a VhfCreate. Esta es la secuencia de eventos cuando el controlador de origen HID quiere eliminar el dispositivo HID virtual:
- El controlador de origen HID deja de iniciar llamadas a VHF.
- El origen HID llama a VhfDelete con el parámetro Wait configurado como FALSE.
- VHF deja de invocar las funciones de devolución de llamada implementadas por el controlador de origen HID.
- El VHF comienza a notificar que el dispositivo está ausente al Administrador de PnP. En este momento, la llamada de VhfDelete podría devolverse.
- Cuando el dispositivo se notifica como un dispositivo que falta, VHF invoca EvtVhfCleanup si el controlador de origen HID registró su implementación.
- Después de que EvtVhfCleanup retorne un valor, VHF realiza su limpieza.
VOID
VhfSourceDeviceCleanup(
_In_ WDFOBJECT DeviceObject
)
{
PDEVICE_CONTEXT deviceContext;
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");
deviceContext = DeviceGetContext(DeviceObject);
if (deviceContext->VhfHandle != WDF_NO_HANDLE)
{
VhfDelete(deviceContext->VhfHandle, TRUE);
}
}
Instalación del controlador de origen HID
En el archivo INF que instala el controlador de origen HID, asegúrese de declarar Vhf.sys como controlador de filtro inferior al controlador de origen HID mediante la directiva AddReg.
[HIDVHF_Inst.NT.HW]
AddReg = HIDVHF_Inst.NT.AddReg
[HIDVHF_Inst.NT.AddReg]
HKR,,"LowerFilters",0x00010000,"vhf"