Utilisation d’interfaces d’appareil (WDF)

Une interface d’appareil est un lien symbolique vers un appareil Plug-and-Play (PnP) qu’une application peut utiliser pour accéder à l’appareil. Une application en mode utilisateur peut passer le nom de lien symbolique de l’interface à un élément d’API, tel que la fonction CreateFile Microsoft Win32. Pour obtenir le nom de lien symbolique d’une interface d’appareil, l’application en mode utilisateur peut appeler des fonctions configuration manager ou SetupApi. Pour plus d’informations, consultez Énumération des interfaces d’appareil installées.

Chaque interface d’appareil appartient à une classe d’interface d’appareil. Par exemple, une pile de pilotes pour un périphérique CD-ROM peut fournir une interface qui appartient à la classe GUID_DEVINTERFACE_CDROM. L’un des pilotes du périphérique CD-ROM enregistre un instance de la classe GUID_DEVINTERFACE_CDROM pour informer le système et les applications qu’un périphérique CD-ROM est disponible. Pour plus d’informations sur les classes d’interface d’appareil, consultez Vue d’ensemble des classes d’interface d’appareil.

Inscription d’une interface d’appareil

Pour inscrire une instance d’une classe d’interface d’appareil, un pilote basé sur l’infrastructure peut appeler WdfDeviceCreateDeviceInterface avant ou après le démarrage de l’appareil. Si le pilote prend en charge plusieurs instances de l’interface, il peut affecter une chaîne de référence unique à chaque instance.

Une fois que le pilote a inscrit une interface de périphérique, le pilote peut appeler WdfDeviceRetrieveDeviceInterfaceString pour obtenir le nom de lien symbolique que le système a attribué à l’interface de l’appareil.

Pour plus d’informations sur les autres façons dont les pilotes peuvent inscrire des interfaces d’appareil, consultez Inscription d’une classe d’interface d’appareil.

Activation et désactivation d’une interface d’appareil

Les interfaces créées avant le démarrage de l’appareil (par exemple à partir d’EvtDriverDeviceAdd, EvtChildListCreateDevice ou EvtDevicePrepareHardware) sont automatiquement activées par l’infrastructure lorsque l’appareil passe par l’énumération PnP et démarre. Pour empêcher l’activation automatique de l’interface lors du démarrage PnP, appelez WdfDeviceSetDeviceInterfaceStateEx à partir de la même fonction de rappel (définissez le paramètre EnableInterface sur FALSE) pour cette interface avant le démarrage de PnP.

Les interfaces créées après le démarrage de l’appareil ne sont pas automatiquement activées. Le pilote doit appeler WdfDeviceSetDeviceInterfaceState ou WdfDeviceSetDeviceInterfaceStateEx pour activer ces interfaces.

Toutes les interfaces sont automatiquement désactivées lorsque l’appareil subit la suppression pnP. Notez que les changements d’état d’alimentation de l’appareil ou le rééquilibrage des ressources PnP ne modifient pas l’état de l’interface.

Un pilote peut désactiver et réactiver une interface d’appareil si nécessaire. Par exemple, si un pilote détermine que son appareil a cessé de répondre, il peut appeler WdfDeviceSetDeviceInterfaceState ou WdfDeviceSetDeviceInterfaceStateEx pour désactiver les interfaces de l’appareil et empêcher les applications d’obtenir de nouveaux handles à l’interface. (Les handles existants de l’interface ne sont pas affectés.) Si l’appareil devient disponible ultérieurement, le pilote peut appeler WdfDeviceSetDeviceInterfaceState ou WdfDeviceSetDeviceInterfaceStateEx pour réactiver les interfaces.

Réception de demandes d’accès à une interface d’appareil

Lorsqu’une application ou un composant en mode noyau demande l’accès à l’interface de périphérique d’un pilote, l’infrastructure appelle la fonction de rappel EvtDeviceFileCreate du pilote. Le pilote peut appeler WdfFileObjectGetFileName pour obtenir le nom de l’appareil ou du fichier auquel l’application ou le composant en mode noyau accède. Si le pilote a spécifié une chaîne de référence lors de l’inscription de l’interface de l’appareil, le système d’exploitation inclut la chaîne de référence dans le fichier ou le nom de l’appareil retourné par WdfFileObjectGetFileName .

Accès à l’interface de périphérique d’un autre pilote

Cette section montre comment un pilote KMDF (Kernel-Mode Driver Framework) ou un pilote UMDF (User-Mode Driver Framework) version 2 s’inscrit pour notification d’arrivée ou de suppression d’une interface de périphérique fournie par un autre pilote, puis crée une cible d’E/S distante pour communiquer avec l’appareil représenté par l’interface de l’appareil.

Pour plus d’informations sur la façon de procéder dans un pilote UMDF version 1, consultez Utilisation d’interfaces de périphérique dans les pilotes UMDF.

Pour s’inscrire à la notification des événements d’interface d’appareil, un pilote KMDF appelle IoRegisterPlugPlayNotification, tandis qu’un pilote UMDF 2 appelle CM_Register_Notification. Dans les deux cas, le pilote appelle la routine appropriée à partir de sa fonction de rappel EvtDriverDeviceAdd .

L’exemple de code suivant montre comment un pilote UMDF 2 local s’inscrit pour les notifications, puis ouvre la cible d’E/S distante.

  1. Le pilote distant s’inscrit pour une interface d’appareil en appelant WdfDeviceCreateDeviceInterface à partir d’EvtDriverDeviceAdd.

        UNICODE_STRING ref;
        RtlInitUnicodeString(&ref, MY_HID_FILTER_REFERENCE_STRING);
        status = WdfDeviceCreateDeviceInterface(
                     hDevice,
                     (LPGUID) &GUID_DEVINTERFACE_MY_HIDFILTER_DRIVER,
                     &ref // ReferenceString
                 );
    
        if (!NT_SUCCESS (status)) {
            MyKdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status));
            return status;
        }
    
    
  2. Le pilote local appelle CM_Register_Notification à partir d’EvtDriverDeviceAdd pour s’inscrire à la notification lorsqu’une interface d’appareil est disponible. Fournissez un pointeur vers une routine de rappel de notification que l’infrastructure appelle lorsque les interfaces d’appareil sont disponibles.

    DWORD cmRet;
        CM_NOTIFY_FILTER cmFilter;
    
        ZeroMemory(&cmFilter, sizeof(cmFilter));
        cmFilter.cbSize = sizeof(cmFilter);
        cmFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
        cmFilter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_MY_HIDFILTER_DRIVER;
    
        cmRet = CM_Register_Notification(
                    &cmFilter,                     // PCM_NOTIFY_FILTER pFilter,
                    (PVOID) hDevice,               // PVOID pContext,
                    MyCmInterfaceNotification,    // PCM_NOTIFY_CALLBACK pCallback,
                    &fdoData->CmNotificationHandle // PHCMNOTIFICATION pNotifyContext
                    );
        if (cmRet != CR_SUCCESS) {
            MyKdPrint( ("CM_Register_Notification failed, error %d\n", cmRet));
            status = STATUS_UNSUCCESSFUL;
            return status;
        }   
    
  3. Le système appelle la routine de rappel de notification du pilote local chaque fois que l’interface de périphérique spécifiée arrive ou est supprimée. La routine de rappel peut examiner le paramètre EventData pour déterminer l’interface d’appareil qui est arrivée. Il peut ensuite mettre en file d’attente un élément de travail pour ouvrir l’interface de l’appareil.

    DWORD 
    MyCmInterfaceNotification(
        _In_ HCMNOTIFICATION       hNotify,
        _In_opt_ PVOID             Context,
        _In_ CM_NOTIFY_ACTION      Action,
        _In_reads_bytes_(EventDataSize) PCM_NOTIFY_EVENT_DATA EventData,
        _In_ DWORD                 EventDataSize
        )
    {
        PFDO_DATA fdoData;
        UNICODE_STRING name;
        WDFDEVICE device;
        NTSTATUS status;
        WDFWORKITEM workitem;
    
        UNREFERENCED_PARAMETER(hNotify);
        UNREFERENCED_PARAMETER(EventDataSize);
    
        device = (WDFDEVICE) Context;
        fdoData = ToasterFdoGetData(device);
    
        switch(Action) {
        case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL: 
            MyKdPrint( ("MyCmInterfaceNotification: Arrival of %S\n",
                EventData->u.DeviceInterface.SymbolicLink));
    
            //
            // Enqueue a work item to open target
            //
    
            break;
        case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL: 
            MyKdPrint( ("MyCmInterfaceNotification: removal of %S\n",
                EventData->u.DeviceInterface.SymbolicLink));
            break;
        default:
            MyKdPrint( ("MyCmInterfaceNotification: Arrival unknown action\n"));
            break;
        }
    
        return 0;
    }
    
  4. À partir de la fonction de rappel d’élément de travail, le pilote local appelle WdfIoTargetCreate pour créer la cible distante, et WdfIoTargetOpen pour ouvrir une cible d’E/S distante.

    Lors de l’appel de WdfIoTargetOpen, le pilote inscrit éventuellement une fonction de rappel EvtIoTargetQueryRemove pour recevoir une notification de suppression, ainsi que la possibilité de refuser la suppression. Si le pilote ne fournit pas EvtIoTargetQueryRemove, l’infrastructure ferme la cible d’E/S lorsque l’appareil est supprimé.

    Dans de rares cas, un pilote UMDF 2 peut appeler CM_Register_Notification une deuxième fois pour s’inscrire à une notification de suppression d’appareil. Par exemple, si le pilote appelle CreateFile pour obtenir un HANDLE à l’interface de l’appareil, il doit s’inscrire pour la notification de suppression de l’appareil afin qu’il puisse répondre correctement aux tentatives de suppression de requête. Dans la plupart des cas, le pilote UMDF 2 appelle CM_Register_Notification une seule fois et s’appuie sur la prise en charge de WDF pour la suppression des appareils.

    VOID 
    EvtWorkItem(
        _In_ WDFWORKITEM WorkItem
    )
    {
        // 
        // create and open remote target
        //
    
        return;
    }
    

Inscription à la notification de l’arrivée de l’interface d’appareil et de la suppression d’appareil