Guida introduttiva a NFC CX

Questa guida illustra come scrivere un driver funzionale NFC usando il driver NFC Class Extension (NFC CX).

Nota

Un driver che usa un driver di estensione di classe nell'implementazione è noto come "driver client". Cioè, un client del driver di estensione della classe.

Prerequisiti

Responsabilità del driver del cliente

Il driver CX NFC è responsabile della gestione delle richieste di I/O inviate al driver e della creazione dei pacchetti di comandi NCI pertinenti. Il driver client è responsabile dell'invio di tali pacchetti NCI al controller NFC e dell'invio dei pacchetti di risposta NCI al driver CX NFC.

Spetta al driver client determinare come inviare i pacchetti NCI al controller NFC. Questo processo varia a seconda del tipo di bus hardware usato. I bus comuni usati dai controller NFC includono I2C, SPI e USB.

Codice del progetto completo

Una versione completa di questo codice di esempio è disponibile in GitHub: esempio di driver client CX NFC.

Configurazione del progetto

  1. In Visual Studio creare un nuovo progetto "Driver in modalità utente, Vuoto (UMDF V2)".

    Scegliere Nuovo dal menu Filee quindi selezionare Progetto. Nel nodo Visual C++, in Driver di Windows, seleziona WDF, e quindi seleziona Driver in modalità utente, Vuoto (UMDF V2)

    Screenshot che mostra la finestra di dialogo

  2. Aprire il file INF.

    In Esplora soluzioni, nel nodo <project-name>, nella cartella File driver fare doppio clic su <project-name.inf>.

  3. Nel file INF rimuovere la classe di dispositivo personalizzata seguendo questa procedura:

    1. Rimuovere le due sezioni seguenti:

      [ClassInstall32]
      AddReg=SampleClass_RegistryAdd
      
      [SampleClass_RegistryAdd]
      HKR,,,,%ClassName%
      HKR,,Icon,,"-10"
      
    2. [Strings] Nella sezione rimuovere la riga seguente.

      ClassName="Samples" ; TODO: edit ClassName
      
  4. Nel file INF impostare la classe di dispositivo del driver su Prossimità:

    1. Modificare il valore di Class in Proximity
    2. Modificare il valore di ClassGuid in {5630831C-06C9-4856-B327-F5D32586E060}
      • Questo è il GUID della classe del dispositivo di prossimità.
    [Version]
    ...
    Class=Proximity
    ClassGuid={5630831C-06C9-4856-B327-F5D32586E060} ; Proximity class GUID
    ...
    
  5. Nel file INF aggiungere un riferimento all'estensione di classe NFC. In questo modo si garantisce che Windows Driver Framework (WDF) carichi il driver CX NFC quando viene caricato il driver client.

    1. Trova la sezione <project-name>_Install.
    2. Aggiungere UmdfExtensions=NfcCx0102.
    [<project-name>_Install]
    ...
    UmdfExtensions=NfcCx0102
    
  6. Nelle impostazioni di compilazione del driver collegarsi all'estensione della classe NFC. In questo modo si garantisce che l'API CX NFC sia disponibile durante la compilazione del codice.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Proprietà. In Proprietà di configurazione, in Impostazioni driver selezionare NFC.
    2. Assicurarsi che Configuration sia impostato su All Configurations.
    3. Assicurarsi che Platform sia impostato su All Platforms.
    4. Configura Collegamento all'estensione della classe NFC su Yes.

    Finestra di dialogo di Visual Studio: Pagine delle proprietà MyNfcDriver, con la funzione

  7. Aggiungere un file denominato Driver.cpp al progetto.

  8. Crea una DriverEntry routine in Driver.cpp. Questo è il punto di ingresso per il driver. Lo scopo principale è inizializzare WDF e registrare la EvtDriverDeviceAdd funzione di callback.

    #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. Aggiungere due file denominati Device.cpp e Device.h al progetto.

  10. In Device.h definire 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. Nella Device.cpp, iniziare la definizione della funzione DeviceContext::AddDevice.

    #include "Device.h"
    
    NTSTATUS DeviceContext::AddDevice(
        _In_ WDFDRIVER Driver,
        _Inout_ PWDFDEVICE_INIT DeviceInit)
    {
        NTSTATUS status;
    
    
  12. Impostare la configurazione del dispositivo CX NFC. La configurazione del dispositivo include la EvtNfcCxWriteNciPacket funzione di callback. Questo callback riceve i pacchetti NCI dal driver CX NFC che il driver client deve inoltrare al controller 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. Registrare i callback di alimentazione PnP richiesti dal driver client.

    È probabile che un driver client tipico richieda le funzioni EvtDevicePrepareHardware, EvtDeviceReleaseHardware, EvtDeviceD0Entry e EvtDeviceD0Exit. I requisiti possono variare a seconda del modo in cui il driver client gestisce la gestione dell'alimentazione.

        // 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. Chiamare la WdfDeviceCreate funzione per creare l'oggetto 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. Chiamare la funzione NfcCxDeviceInitialize.

    Questa funzione deve essere chiamata dopo la creazione dell'oggetto WDFDEVICE per consentire al driver CX NFC di completare l'inizializzazione dell'istanza del dispositivo.

        // Let NFC CX finish initializing the device instance.
        status = NfcCxDeviceInitialize(device);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
  16. Chiamare NfcCxSetRfDiscoveryConfig per specificare le tecnologie NFC e i protocolli supportati dal controller 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. Termina la funzione DeviceContext::AddDevice.

        return STATUS_SUCCESS;
    }
    
  18. Implementare le funzioni di callback PrepareHardware e ReleaseHardware.

    Queste due funzioni vengono usate per inizializzare e deinizializzare le risorse hardware assegnate all'istanza del dispositivo del controller NFC. La loro implementazione dipende dal tipo di bus a cui il dispositivo è connesso (ad esempio, I2C, SPI e 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. Chiamare la funzione NfcCxHardwareEvent con HostActionStart e HostActionStop per avviare e arrestare la macchina a stati NCI nei momenti appropriati.

    Alcuni driver eseguono questa operazione durante i callback di alimentazione PnP di D0Entry e D0Exit. Tuttavia, questo può variare a seconda del modo in cui il driver client gestisce il risparmio energetico.

    // 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. Implementare la WriteNciPacket funzione .

    Questo callback viene chiamato da NFC CX quando è presente un pacchetto NCI da inviare al controller 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. Chiamare la NfcCxNciReadNotification funzione quando il controller NFC ha un pacchetto NCI che deve essere inviato a NFC CX. In genere, eseguire questa operazione in un callback di eventi hardware.

    Per esempio:

Registrazione

Prendere in considerazione l'aggiunta della registrazione al driver client per semplificare il debug. Sia la traccia ETW che la traccia WPP sono opzioni valide.