Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article explique comment :
- Écrivez un pilote source Kernel-Mode Driver Framework (KMDF)HID qui envoie des rapports de lecture HID à Windows.
- Chargez le pilote VHF comme filtre inférieur sur le pilote source HID dans la pile de périphériques HID virtuelle.
Découvrez comment écrire un pilote source HID qui signale les données HID au système d’exploitation.
Un appareil d’entrée HID, tel qu’un clavier, une souris, un stylet, un toucher ou un bouton, envoie différents rapports au système d’exploitation afin qu’il puisse comprendre l’objectif de l’appareil et prendre les mesures nécessaires. Les rapports sont sous la forme de collections HID et d’utilisations HID. L’appareil envoie ces rapports sur différents transports, dont certains sont pris en charge par Windows, tels que HID sur I2C et HID via USB. Dans certains cas, Windows ne prend pas en charge le transport, ou les rapports ne correspondent pas directement au matériel réel. Il peut s’agir d’un flux de données au format HID envoyé par un autre composant logiciel pour le matériel virtuel, par exemple pour les boutons ou capteurs non GPIO. Par exemple, considérez les données d’accéléromètre à partir d’un téléphone qui se comporte en tant que contrôleur de jeu, envoyées sans fil à un PC. Dans un autre exemple, un ordinateur peut recevoir une entrée distante à partir d’un appareil Miracast à l’aide du protocole UIBC.
Dans les versions précédentes de Windows, pour prendre en charge les nouveaux transports (matériel réel ou logiciel), vous devez écrire un minidriver de transport HID et le lier au pilote de classe intégré fourni par Microsoft, Hidclass.sys. La paire de pilotes classe/mini a fourni des collections HID, telle que les collectionsTop-Level, aux pilotes de niveau supérieur et aux applications en mode utilisateur. Dans ce modèle, le défi était d’écrire le minidriver, qui peut être une tâche complexe.
À compter de Windows 10, le nouveau VHF (Virtual HID Framework) élimine la nécessité d’écrire un minidriver de transport. Au lieu de cela, vous pouvez écrire un pilote source HID à l’aide d’interfaces de programmation KMDF ou WDM. L’infrastructure se compose d’une bibliothèque statique fournie par Microsoft qui expose les éléments de programmation utilisés par votre pilote. Il inclut également un pilote intégré fourni par Microsoft qui énumère un ou plusieurs appareils enfants et procède à la génération et à la gestion d’une arborescence HID virtuelle.
Remarque
Dans cette version, VHF ne prend en charge un pilote source HID qu’en mode noyau.
Cet article décrit l’architecture de l’infrastructure, l’arborescence d’appareils HID virtuel et les scénarios de configuration.
Arborescence d’appareils HID virtuels
Dans cette image, l’arborescence des périphériques affiche les pilotes et leurs objets de périphérique associés.
Pilote source HID (votre pilote)
Le pilote source HID est lié à Vhfkm.lib et inclut Vhf.h dans son projet de build. Le pilote peut être écrit à l’aide de Windows Driver Model (WDM) ou de Kernel-Mode Driver Framework (KMDF) qui fait partie des frameworks WDF (Windows Driver Frameworks). Le pilote peut être chargé en tant que pilote de filtre ou pilote de fonction dans la pile de périphériques.
Bibliothèque statique VHF (vhfkm.lib)
La bibliothèque statique est incluse dans le Kit de pilotes Windows (WDK) pour Windows 10. La bibliothèque expose des interfaces de programmation telles que des routines et des fonctions de rappel utilisées par votre pilote source HID. Lorsque votre pilote appelle une fonction, la bibliothèque statique transfère la requête au pilote VHF qui gère la requête.
Pilote VHF (Vhf.sys)
Pilote intégré fourni par Microsoft. Ce pilote doit être chargé en tant que pilote de filtre inférieur sous votre pilote dans la pile de périphériques source HID. Le pilote VHF énumère dynamiquement les appareils enfants et crée des objets de périphérique physique (PDO) pour un ou plusieurs périphériques HID spécifiés par votre pilote source HID. Il implémente également la fonctionnalité de minipilote HID Transport correspondant aux périphériques énumérés.
Paire de pilotes de classe HID (Hidclass.sys, Mshidkmdf.sys)
La paire Hidclass/Mshidkmdf énumère Top-Level Collections (TLC) similaire à la façon dont elle énumère ces collections pour un appareil HID réel. Un client HID peut continuer à demander et à consommer les TLC comme un appareil HID réel. Cette paire de pilotes est installée en tant que pilote de fonction dans la pile d’appareils.
Remarque
Dans certains scénarios, un client HID peut avoir besoin d’identifier la source de données HID. Par exemple, un système dispose d’un capteur intégré et reçoit des données d’un capteur distant du même type. Le système peut choisir un capteur pour être plus fiable. Pour différencier les deux capteurs connectés au système, le client HID interroge l’ID de conteneur du TLC. Dans ce cas, un pilote source HID peut fournir l’ID de conteneur, qui est signalé comme l’ID de conteneur de l’appareil HID virtuel par VHF.
Client HID (application)
Interroge et consomme les TLC que la pile de périphériques HID signale.
Configuration requise pour l’en-tête et la bibliothèque
Cette procédure explique comment écrire un pilote source HID qui signale des boutons de casque au système d’exploitation. Dans ce cas, le pilote qui implémente ce code peut être un pilote audio KMDF existant et modifié, servant de source HID pour signaler les boutons du casque en utilisant la VHF.
Incluez Vhf.h depuis le WDK.
Lien vers vhfkm.lib inclus dans wdK.
Créez un descripteur de rapport HID que votre appareil souhaite signaler au système d’exploitation. Dans cet exemple, le descripteur de rapport HID décrit les boutons du casque. Le rapport spécifie un rapport d’entrée HID, taille 8 bits (1 octet). Les trois premiers bits concernent les boutons central, de volume vers le haut et de volume vers le bas du casque. Les bits restants ne sont pas utilisés.
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 };
Créer un appareil HID virtuel
Initialisez une structure VHF_CONFIG en appelant la macro VHF_CONFIG_INIT , puis appelez la méthode VhfCreate . Le pilote doit appeler VhfCreate à PASSIVE_LEVEL après avoir effectué l'appel de WdfDeviceCreate, généralement dans la fonction de rappel EvtDriverDeviceAdd du pilote.
Dans l’appel VhfCreate , le pilote peut spécifier certaines options de configuration, telles que les opérations qui doivent être traitées de manière asynchrone ou définir les informations d’appareil (ID de fournisseur/produit).
Par exemple, une application demande un TLC. Lorsque la paire de pilotes de classe HID reçoit cette requête, elle détermine le type de requête et crée une requête IOCTL Minidriver HID appropriée, en la transférant à VHF. Lors de l’obtention de la demande IOCTL, VHF peut gérer la demande, s’appuyer sur le pilote source HID pour traiter la demande, ou terminer la requête avec STATUS_NOT_SUPPORTED.
VHF gère ces IOCTL :
- IOCTL_HID_GET_STRING
- IOCTL_HID_GET_DEVICE_ATTRIBUTES
- IOCTL_HID_GET_DEVICE_DESCRIPTOR
- IOCTL_HID_GET_REPORT_DESCRIPTOR
Si la requête est GetFeature, SetFeature, WriteReport ou GetInputReport, et que le pilote source HID a inscrit une fonction de rappel correspondante, VHF appelle la fonction de rappel. Dans cette fonction, le pilote source HID peut obtenir ou définir des données HID pour l’appareil virtuel HID. Si le pilote n’inscrit pas de rappel, VHF termine la demande avec l’état STATUS_NOT_SUPPORTED.
VHF appelle les fonctions de rappel d’événements implémentées par le pilote source HID pour ces IOCTL :
-
Si le pilote souhaite gérer la stratégie de mise en mémoire tampon lors de l’envoi d’une mémoire tampon pour obtenir le rapport d’entrée HID, il doit implémenter l’EvtVhfReadyForNextReadReport et spécifier un pointeur dans le membre EvtVhfAsyncOperationGetInputReport . Pour plus d’informations, consultez Envoyer le rapport d’entrée HID.
IOCTL_HID_GET_FEATURE ou IOCTL_HID_SET_FEATURE
Si le pilote souhaite obtenir ou définir un rapport de fonctionnalité HID de manière asynchrone, le pilote doit implémenter la fonction EvtVhfAsyncOperation et spécifier un pointeur vers la fonction d’implémentation get ou set dans le membre EvtVhfAsyncOperationGetFeature ou EvtVhfAsyncOperationSetFeature de VHF_CONFIG.
-
Si le pilote souhaite obtenir un rapport d’entrée HID de manière asynchrone, le pilote doit implémenter la fonction EvtVhfAsyncOperation et spécifier un pointeur vers la fonction dans le membre EvtVhfAsyncOperationGetInputReport de VHF_CONFIG.
-
Si le pilote souhaite obtenir un rapport d’entrée HID de manière asynchrone, le pilote doit implémenter la fonction EvtVhfAsyncOperation et spécifier un pointeur vers la fonction dans le membre EvtVhfAsyncOperationWriteReport de VHF_CONFIG.
Pour tout autre IOCTL de minipilote HID, VHF termine la requête avec STATUS_NOT_SUPPORTED.
L’appareil HID virtuel est supprimé en appelant VhfDelete. Le rappel EvtVhfCleanup est requis si le pilote a alloué des ressources pour l’appareil HID virtuel. Le pilote doit implémenter la fonction EvtVhfCleanup et spécifier un pointeur vers cette fonction dans le membre EvtVhfCleanup de VHF_CONFIG. EvtVhfCleanup est appelé avant la fin de l’appel VhfDelete . Pour plus d’informations, consultez Supprimer l’appareil HID virtuel.
Remarque
Une fois l’opération asynchrone terminée, le pilote doit appeler VhfAsyncOperationComplete pour définir les résultats de l’opération. Vous pouvez appeler la méthode à partir du rappel d’événement ou à un moment ultérieur après le retour renvoyé par le rappel.
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;
}
Envoyer le rapport d’entrée HID
Envoyez le rapport d’entrée HID en appelant VhfReadReportSubmit.
En règle générale, un appareil HID envoie des informations sur les modifications d’état en envoyant des rapports d’entrée par le biais d’interruptions. Par exemple, l’appareil casque peut envoyer un rapport lorsque l’état d’un bouton change. Dans ce cas, la routine de service d’interruption (ISR) du pilote est appelée. Dans cette routine, le pilote peut planifier un appel de procédure différé (DPC) qui traite le rapport d’entrée et l’envoie à VHF, qui envoie les informations au système d’exploitation. Par défaut, VHF stocke temporairement le rapport et le pilote source HID peut commencer à soumettre des rapports d’entrée HID à mesure qu’ils arrivent. Cette mise en mémoire tampon élimine la nécessité pour le pilote source HID d’implémenter une synchronisation complexe.
Le pilote source HID peut envoyer des rapports d’entrée en implémentant la stratégie de mise en mémoire tampon pour les rapports en attente. Pour éviter la mise en mémoire tampon en double, le pilote source HID peut implémenter la fonction de rappel EvtVhfReadyForNextReadReport et suivre si VHF a invoqué ce rappel. S’il a été appelé précédemment, le pilote source HID peut appeler VhfReadReportSubmit pour envoyer un rapport. Il doit attendre que EvtVhfReadyForNextReadReport soit appelé avant de pouvoir appeler À nouveau VhfReadReportSubmit .
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);
}
}
Supprimer l’appareil HID virtuel
Supprimez l’appareil HID virtuel en appelant VhfDelete.
VhfDelete peut être appelé de façon synchrone ou asynchrone en spécifiant le paramètre Wait. Pour un appel synchrone, la méthode doit être appelée à PASSIVE_LEVEL, par exemple à partir d’EvtCleanupCallback de l’objet de l’appareil. VhfDelete retourne après la suppression de l’appareil HID virtuel. Si le pilote appelle VhfDelete de façon asynchrone, il retourne immédiatement et VHF appelle EvtVhfCleanup une fois l’opération de suppression terminée. La méthode peut être appelée au niveau DISPATCH_LEVEL maximum. Dans ce cas, le pilote devait inscrire et implémenter une fonction de rappel EvtVhfCleanup lorsqu’il avait précédemment appelé VhfCreate. Voici la séquence d’événements lorsque le pilote source HID souhaite supprimer l’appareil HID virtuel :
- Le pilote source HID cesse de lancer des appels vers VHF.
- La source HID appelle VhfDelete avec Wait défini sur FALSE.
- VHF arrête l’appel des fonctions de rappel implémentées par le pilote source HID.
- VHF commence à signaler l’appareil comme manquant au Gestionnaire PnP. À ce stade, l’appel VhfDelete peut aboutir.
- Lorsque le périphérique est signalé comme un périphérique manquant, VHF appelle EvtVhfCleanup si le pilote source HID a enregistré son implémentation.
- Une fois que EvtVhfCleanup a été retourné, VHF effectue son nettoyage.
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);
}
}
Installer le pilote source HID
Dans le fichier INF qui installe le pilote source HID, veillez à déclarer Vhf.sys en tant que pilote de filtre inférieur à votre pilote source HID à l’aide de la directive AddReg.
[HIDVHF_Inst.NT.HW]
AddReg = HIDVHF_Inst.NT.AddReg
[HIDVHF_Inst.NT.AddReg]
HKR,,"LowerFilters",0x00010000,"vhf"