Guide de démarrage rapide NFC CX

Ce guide montre comment écrire un pilote fonctionnel NFC à l’aide du pilote NFC Class Extension (NFC CX).

Note

Un pilote qui utilise un pilote d’extension de classe dans son implémentation est appelé « pilote client ». Autrement dit, un client du pilote d’extension de classe.

Prérequis

Responsabilités du pilote client

Le pilote NFC CX est chargé de gérer les demandes d’E/S envoyées au pilote et de créer les paquets de commande NCI appropriés. Le pilote client est chargé d’envoyer ces paquets NCI au contrôleur NFC et de renvoyer les paquets de réponse NCI au pilote NFC CX.

Il appartient au pilote client de déterminer comment envoyer les paquets NCI au contrôleur NFC. Ce processus varie en fonction du type de bus matériel utilisé. Les bus courants utilisés par les contrôleurs NFC incluent I2C, SPI et USB.

Code de projet complet

Une version complète de cet exemple de code est disponible sur GitHub : exemple de pilote client NFC CX.

Configuration du projet

  1. Dans Visual Studio, créez un projet « User Mode Driver, Empty (UMDF V2) ».

    Dans le menu Fichier , pointez sur Nouveau, puis sélectionnez Projet. Dans le nœud Visual C++, sous Pilotes Windows, sélectionnez WDF, puis sélectionnez Pilote en mode utilisateur, Vide (UMDF V2)

    Capture d’écran montrant la boîte de dialogue Visual Studio « New Project » avec « User Mode Driver, Empty (UMDF V2) » sélectionné.

  2. Ouvrez le fichier INF.

    Dans Explorateur de solutions, sous le nœud <project-name>, dans le dossier Driver Files, double-cliquez sur <project-name>.inf.

  3. Dans le fichier INF, supprimez la classe d’appareil personnalisée en procédant comme suit :

    1. Supprimez les deux sections suivantes :

      [ClassInstall32]
      AddReg=SampleClass_RegistryAdd
      
      [SampleClass_RegistryAdd]
      HKR,,,,%ClassName%
      HKR,,Icon,,"-10"
      
    2. Dans la section [Strings], supprimez la ligne suivante.

      ClassName="Samples" ; TODO: edit ClassName
      
  4. Dans le fichier INF, définissez la classe de périphérique du pilote sur Proximity :

    1. Remplacez la valeur Class par Proximity
    2. Remplacez la valeur ClassGuid par {5630831C-06C9-4856-B327-F5D32586E060}
      • Il s’agit du GUID de la classe d’appareil de proximité.
    [Version]
    ...
    Class=Proximity
    ClassGuid={5630831C-06C9-4856-B327-F5D32586E060} ; Proximity class GUID
    ...
    
  5. Dans le fichier INF, ajoutez une référence à l’extension de classe NFC. Cela garantit que Windows Driver Framework (WDF) charge le pilote NFC CX lorsque le pilote client se charge.

    1. Recherchez la section <project-name>_Install.
    2. Ajoutez UmdfExtensions=NfcCx0102.
    [<project-name>_Install]
    ...
    UmdfExtensions=NfcCx0102
    
  6. Dans les paramètres de génération du pilote, ajoutez une liaison à l’extension de classe NFC. Cela garantit que l’API NFC CX est disponible pendant la compilation du code.

    1. Dans Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Propriétés. Dans Propriétés de configuration, sous Paramètres du pilote, sélectionnez NFC.
    2. Vérifiez que la configuration est définie sur All Configurations.
    3. Vérifiez que Plateforme est définie sur All Platforms.
    4. Définissez Lier à l’extension de classe NFC sur Yes.

    boîte de dialogue Visual Studio : Pages de propriétés MyNfcDriver, affichant « Lien vers l’extension de classe NFC » définie sur « Oui ».

  7. Ajoutez un fichier nommé Driver.cpp au projet.

  8. Créez une DriverEntry routine dans Driver.cpp. Il s’agit du point d’entrée du pilote. Son objectif principal est d’initialiser WDF et d’enregistrer la fonction de rappel EvtDriverDeviceAdd.

    #include <windows.h>
    #include <wdf.h>
    
    #include "Device.h" // created in Step 9
    
    // The entry point for the driver.
    extern "C" NTSTATUS DriverEntry(
        _In_ PDRIVER_OBJECT DriverObject,
        _In_ PUNICODE_STRING RegistryPath
        )
    {
        NTSTATUS status = STATUS_SUCCESS;
    
        // Specify `DeviceContext::AddDevice` as the
        // `EvtDriverDeviceAdd` function for the driver.
        WDF_DRIVER_CONFIG driverConfig;
        WDF_DRIVER_CONFIG_INIT(&driverConfig, DeviceContext::AddDevice);
    
        // Initialize WDF.
        status = WdfDriverCreate(
            DriverObject,
            RegistryPath,
            WDF_NO_OBJECT_ATTRIBUTES,
            &driverConfig,
            WDF_NO_HANDLE);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
        return STATUS_SUCCESS;
    }
    
  9. Ajoutez deux fichiers nommés Device.cpp et Device.h au projet.

  10. Dans Device.h, définissez la classe DeviceContext.

    #pragma once
    
    #include <windows.h>
    #include <wdf.h>
    #include <NfcCx.h>
    
    // The class that will store the driver's custom state for
    // a device instance.
    class DeviceContext
    {
    public:
        // Implementation of `EvtDriverDeviceAdd`.
        static NTSTATUS AddDevice(
            _In_ WDFDRIVER Driver,
            _Inout_ PWDFDEVICE_INIT DeviceInit);
    
    private:
        // Implementation of `EvtDevicePrepareHardware`.
        static NTSTATUS PrepareHardware(
            _In_ WDFDEVICE Device,
            _In_ WDFCMRESLIST ResourcesRaw,
            _In_ WDFCMRESLIST ResourcesTranslated);
    
        // Implementation of `EvtDeviceReleaseHardware`.
        static NTSTATUS ReleaseHardware(
            _In_ WDFDEVICE Device,
            _In_ WDFCMRESLIST ResourcesTranslated);
    
        // Implementation of `EvtDeviceD0Entry`.
        static NTSTATUS D0Entry(
            _In_ WDFDEVICE Device,
            _In_ WDF_POWER_DEVICE_STATE PreviousState);
    
        // Implementation of `EvtDeviceD0Exit`.
        static NTSTATUS D0Exit(
            _In_ WDFDEVICE Device,
            _In_ WDF_POWER_DEVICE_STATE TargetState);
    
        // Implementation of `EvtNfcCxWriteNciPacket`.
        static void WriteNciPacket(
            _In_ WDFDEVICE Device,
            _In_ WDFREQUEST Request);
    };
    
    // Define the `DeviceGetContext` function.
    WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DeviceContext, DeviceGetContext);
    
  11. Dans Device.cpp, commencez la définition de la DeviceContext::AddDevice fonction.

    #include "Device.h"
    
    NTSTATUS DeviceContext::AddDevice(
        _In_ WDFDRIVER Driver,
        _Inout_ PWDFDEVICE_INIT DeviceInit)
    {
        NTSTATUS status;
    
    
  12. Définissez la configuration de l’appareil NFC CX. La configuration de l’appareil inclut la fourniture de la fonction EvtNfcCxWriteNciPacket de rappel. Ce rappel reçoit les paquets NCI du pilote NFC CX que le pilote client doit transférer vers le contrôleur NFC.

        // Create the NfcCx config.
        NFC_CX_CLIENT_CONFIG nfcCxConfig;
        NFC_CX_CLIENT_CONFIG_INIT(&nfcCxConfig, NFC_CX_TRANSPORT_CUSTOM);
        nfcCxConfig.EvtNfcCxWriteNciPacket = WriteNciPacket;
        nfcCxConfig.DriverFlags = NFC_CX_DRIVER_ENABLE_EEPROM_WRITE_PROTECTION;
    
        // Set the NfcCx config.
        status = NfcCxDeviceInitConfig(DeviceInit, &nfcCxConfig);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
  13. Inscrivez les fonctions de rappel de gestion de l’alimentation PnP requises par votre pilote client.

    Un pilote client classique nécessite probablement les fonctions EvtDevicePrepareHardware, EvtDeviceReleaseHardware, EvtDeviceD0Entry et EvtDeviceD0Exit. Les exigences peuvent varier selon la manière dont votre pilote client gère l’alimentation.

        // Create the PnP power callbacks configuration.
        WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks;
        WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks);
        pnpCallbacks.EvtDevicePrepareHardware = PrepareHardware;
        pnpCallbacks.EvtDeviceReleaseHardware = ReleaseHardware;
        pnpCallbacks.EvtDeviceD0Entry = D0Entry;
        pnpCallbacks.EvtDeviceD0Exit = D0Exit;
    
        // Set the PnP power callbacks.
        WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks);
    
  14. Appelez la WdfDeviceCreate fonction pour créer l’objet WDFDEVICE .

        // Create WDF object attributes for the WDFDEVICE object.
        WDF_OBJECT_ATTRIBUTES deviceAttributes;
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DeviceContext);
    
        // Create the device.
        WDFDEVICE device;
        status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
  15. Appelez la fonction NfcCxDeviceInitialize.

    Cette fonction doit être appelée une fois l’objet WDFDEVICE créé pour permettre au pilote NFC CX de terminer l’initialisation de l’instance de périphérique.

        // Let NFC CX finish initializing the device instance.
        status = NfcCxDeviceInitialize(device);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
  16. Appelez NfcCxSetRfDiscoveryConfig pour spécifier les technologies et protocoles NFC pris en charge par le contrôleur NFC.

        // Create the RF config. (Enable everything.)
        NFC_CX_RF_DISCOVERY_CONFIG discoveryConfig;
        NFC_CX_RF_DISCOVERY_CONFIG_INIT(&discoveryConfig);
        discoveryConfig.PollConfig =
            NFC_CX_POLL_NFC_A | NFC_CX_POLL_NFC_B |
            NFC_CX_POLL_NFC_F_212 | NFC_CX_POLL_NFC_F_424 |
            NFC_CX_POLL_NFC_15693 | NFC_CX_POLL_NFC_ACTIVE |
            NFC_CX_POLL_NFC_A_KOVIO;
        discoveryConfig.NfcIPMode =
            NFC_CX_NFCIP_NFC_A | NFC_CX_NFCIP_NFC_F_212 |
            NFC_CX_NFCIP_NFC_F_424 | NFC_CX_NFCIP_NFC_ACTIVE |
            NFC_CX_NFCIP_NFC_ACTIVE_A | NFC_CX_NFCIP_NFC_ACTIVE_F_212 |
            NFC_CX_NFCIP_NFC_ACTIVE_F_424;
        discoveryConfig.NfcIPTgtMode =
            NFC_CX_NFCIP_TGT_NFC_A | NFC_CX_NFCIP_TGT_NFC_F |
            NFC_CX_NFCIP_TGT_NFC_ACTIVE_A | NFC_CX_NFCIP_TGT_NFC_ACTIVE_F;
        discoveryConfig.NfcCEMode =
            NFC_CX_CE_NFC_A | NFC_CX_CE_NFC_B |
            NFC_CX_CE_NFC_F;
    
        // Set the RF config.
        status = NfcCxSetRfDiscoveryConfig(device, &discoveryConfig);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
  17. Terminez la DeviceContext::AddDevice fonction.

        return STATUS_SUCCESS;
    }
    
  18. Implémentez les fonctions de rappel ReleaseHardware et PrepareHardware.

    Ces deux fonctions sont utilisées pour initialiser et annuler l’initialisation des ressources matérielles affectées à l’instance d’appareil du contrôleur NFC. Leur implémentation dépend du type de bus auquel l’appareil est connecté (par exemple, I2C, SPI et USB).

    NTSTATUS DeviceContext::PrepareHardware(
        _In_ WDFDEVICE Device,
        _In_ WDFCMRESLIST ResourcesRaw,
        _In_ WDFCMRESLIST ResourcesTranslated)
    {
        // FIX ME: Initialize hardware resources.
        return STATUS_SUCCESS;
    }
    
    NTSTATUS DeviceContext::ReleaseHardware(
        _In_ WDFDEVICE Device,
        _In_ WDFCMRESLIST ResourcesTranslated)
    {
        // FIX ME: Uninitialize hardware resources.
        return STATUS_SUCCESS;
    }
    
  19. Appelez la NfcCxHardwareEvent fonction avec HostActionStart et HostActionStop pour démarrer et arrêter la machine à états NCI aux moments appropriés.

    Certains pilotes effectuent cette opération pendant les rappels de gestion de l’alimentation PnP D0Entry et D0Exit. Cela peut varier en fonction de la façon dont votre pilote client gère la gestion de l’alimentation.

    // Device exiting low power state (or is booting up).
    NTSTATUS DeviceContext::D0Entry(
        _In_ WDFDEVICE Device,
        _In_ WDF_POWER_DEVICE_STATE PreviousState)
    {
        (void)PreviousState;
    
        NTSTATUS status;
    
        // Invoke the HostActionStart event, so that the NFC CX initializes
        // the NFC Controller.
        NFC_CX_HARDWARE_EVENT eventArgs = {};
        eventArgs.HostAction = HostActionStart;
    
        status = NfcCxHardwareEvent(Device, &eventArgs);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
        return STATUS_SUCCESS;
    }
    
    // Device entering low power state.
    NTSTATUS DeviceContext::D0Exit(
        _In_ WDFDEVICE Device,
        _In_ WDF_POWER_DEVICE_STATE TargetState)
    {
        (void)TargetState;
    
        NTSTATUS status;
    
        // Trigger the HostActionStop event, so that the NFC CX
        // uninitializes the NFC Controller.
        NFC_CX_HARDWARE_EVENT eventArgs = {};
        eventArgs.HostAction = HostActionStop;
    
        status = NfcCxHardwareEvent(Device, &eventArgs);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
        return STATUS_SUCCESS;
    }
    
  20. Implémentez la fonction WriteNciPacket.

    Ce rappel est appelé par le NFC CX lorsqu’il existe un paquet NCI à envoyer au contrôleur NFC.

    void DeviceContext::WriteNciPacket(
        _In_ WDFDEVICE Device,
        _In_ WDFREQUEST Request)
    {
        NTSTATUS status;
    
        // Get the NCI packet as a raw byte buffer.
        void* nciPacket;
        size_t nciPacketLength;
        status = WdfRequestRetrieveInputBuffer(Request, 0, &nciPacket, &nciPacketLength);
        if (!NT_SUCCESS(status))
        {
            WdfRequestComplete(Request, status);
            return;
        }
    
        // FIX ME: Use the NCI packet in some way.
    
        // FIX ME: Call `WdfRequestComplete` on `Request` with failure
        // or success `NTSTATUS` code.
    };
    
  21. Appelez la NfcCxNciReadNotification fonction lorsque le contrôleur NFC a un paquet NCI qui doit être envoyé au NFC CX. En règle générale, effectuez cette opération dans un rappel d’événement matériel.

    Par exemple:

Logging

Envisagez d’ajouter la journalisation au pilote client pour faciliter le débogage. Le suivi ETW et le suivi WPP sont de bonnes options.