Partager via


Écrire un pilote source HID à l’aide de Virtual HID Framework (VHF)

Cette rubrique explique comment :

  • Écrivez un pilote source Kernel-Mode Driver Framework (KMDF)HID qui envoie des rapports de lecture HID à Windows.
  • Chargez le pilote VHF en tant que 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 des données HID au système d’exploitation.

Un périphérique d’entrée HID, tel qu’un clavier, une souris, un stylet, une touche tactile 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 sur USB. Dans certains cas, le transport peut ne pas être pris en charge par Windows, ou les rapports peuvent ne pas être directement mappés 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 du matériel virtuel, par exemple, pour des boutons ou des capteurs non GPIO. Par exemple, considérez les données accéléromètres d’un téléphone qui se comporte comme une manette de jeu, envoyées sans fil à un PC. Dans un autre exemple, un ordinateur peut recevoir une entrée à distance à partir d’un appareil Miracast à l’aide du protocole UIBC.

Dans les versions précédentes de Windows, pour prendre en charge de nouveaux transports (matériel ou logiciel réel), vous deviez é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 les collections HID, telles que les collections de niveau supérieur aux pilotes de niveau supérieur et aux applications en mode utilisateur. Dans ce modèle, le défi consistait à écrire le minidriver, ce qui peut être une tâche complexe.

À partir 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 création et à la gestion d’une arborescence HID virtuelle.

Notes

Dans cette version, VHF prend en charge un pilote source HID uniquement en mode noyau.

Cette rubrique décrit l’architecture de l’infrastructure, l’arborescence d’appareils HID virtuelle et les scénarios de configuration.

Arborescence d’appareils HID virtuels

Dans cette image, l’arborescence des appareils affiche les pilotes et les objets de périphérique associés.

Diagramme d’une arborescence d’appareils HID virtuelle.

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 du modèle de pilote Windows (WDM) ou de l’infrastructure de pilote Kernel-Mode (KMDF) qui fait partie de Windows Driver Frameworks (WDF). 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)

Un 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 appareils HID spécifiés par votre pilote source HID. Il implémente également la fonctionnalité de mini-pilote hid Transport des appareils enfants énumérés.

Paire de pilotes de classe HID (Hidclass.sys, Mshidkmdf.sys)

La paire Hidclass/Mshidkmdf énumère les collections de niveau supérieur (TLC) de la même façon qu’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 de périphériques.

Notes

Dans certains scénarios, un client HID peut avoir besoin d’identifier la source des 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é en tant qu’ID de conteneur de l’appareil HID virtuel par VHF.

Client HID (application)

Interroge et consomme les TLC signalés par la pile d’appareils HID.

Configuration requise pour l’en-tête et la bibliothèque

Cette procédure décrit comment écrire un pilote source HID simple qui signale les boutons du casque au système d’exploitation. Dans ce cas, le pilote qui implémente ce code peut être un pilote audio KMDF existant qui a été modifié pour agir en tant que bouton de casque de création de rapports de source HID à l’aide de VHF.

  1. Incluez Vhf.h, inclus dans wdK pour Windows 10.

  2. Lien vers vhfkm.lib, inclus dans wdK.

  3. 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, de taille 8 bits (1 octet). Les trois premiers bits sont destinés aux boutons de milieu du casque, d’augmenter le volume et de réduire le volume. 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 en appelant la méthode VhfCreate . Le pilote doit appeler VhfCreate à PASSIVE_LEVEL après l’appel 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 la définition d’informations sur l’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, la paire détermine le type de requête et crée une requête IOCTL HID Minidriver appropriée et la transfère à VHF. Lors de l’obtention de la requête IOCTL, VHF peut gérer la demande, s’appuyer sur le pilote source HID pour la traiter ou terminer la requête avec STATUS_NOT_SUPPORTED.

VHF gère ces IOCTL :

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 le périphérique virtuel HID. Si le pilote n’enregistre pas de rappel, VHF termine la demande avec status STATUS_NOT_SUPPORTED.

VHF appelle les fonctions de rappel d’événement implémentées par le pilote source HID pour ces IOCTL :

Pour tout autre HID Minidriver IOCTL, VHF complète la demande avec STATUS_NOT_SUPPORTED.

L’appareil HID virtuel est supprimé en appelant le VhfDelete. Le rappel EvtVhfCleanup est requis si le pilote a alloué des ressources à 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.

Notes

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 ultérieurement après le retour à partir du 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 changements d’état en envoyant des rapports d’entrée via des 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ée (DPC) qui traite le rapport d’entrée et le soumet à VHF, qui envoie les informations au système d’exploitation. Par défaut, VHF met en mémoire tampon le rapport et le pilote source HID peut commencer à envoyer des rapports d’entrée HID à mesure qu’ils arrivent. Cela et é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 dupliquée, le pilote source HID peut implémenter la fonction de rappel EvtVhfReadyForNextReadReport et suivre si VHF a appelé ce rappel. S’il a été appelé précédemment, le pilote source HID peut appeler VhfReadReportSubmit pour envoyer un rapport. Il doit attendre qu’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é 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 d’appareil. VhfDelete retourne après la suppression de l’appareil HID virtuel. Si le pilote appelle VhfDelete de manière 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 maximum DISPATCH_LEVEL. Dans ce cas, le pilote doit avoir inscrit et implémenté une fonction de rappel EvtVhfCleanup lorsqu’il a précédemment appelé VhfCreate. Voici la séquence d’événements lorsque le pilote source HID souhaite supprimer le périphérique HID virtuel :

  1. Le pilote source HID arrête de lancer des appels dans VHF.
  2. La source HID appelle VhfDelete avec Wait défini sur FALSE.
  3. VHF arrête l’appel des fonctions de rappel implémentées par le pilote source HID.
  4. VHF commence à signaler l’appareil comme manquant au Gestionnaire PnP. À ce stade, l’appel VhfDelete peut retourner.
  5. Lorsque l’appareil est signalé comme un appareil manquant, VHF appelle EvtVhfCleanup si le pilote source HID a enregistré son implémentation.
  6. Après le retour d’EvtVhfCleanup , 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"