Guia de início rápido do NFC CX

Este guia demonstra como escrever um driver funcional NFC usando o driver NFC CX (Extensão de Classe NFC).

Note

Um driver que usa um driver de extensão de classe em sua implementação é conhecido como um "driver cliente". Ou seja, um cliente do driver de extensão de classe.

Prerequisites

Responsabilidades do driver do cliente

O driver CX NFC é responsável por lidar com solicitações de E/S enviadas ao driver e criar os pacotes de comando NCI relevantes. O driver cliente é responsável por enviar esses pacotes de NCI para o Controlador NFC e enviar de volta os pacotes de resposta NCI para o driver CX NFC.

Cabe ao driver cliente determinar como enviar os pacotes de NCI para o controlador NFC. Esse processo varia dependendo do tipo de barramento de hardware usado. Os barramentos comuns usados pelos controladores NFC incluem I2C, SPI e USB.

Código completo do projeto

Uma versão completa deste código de exemplo está disponível em GitHub: exemplo de driver de cliente NFC CX.

Configuração do projeto

  1. No Visual Studio, crie um novo projeto "Driver do Modo de Usuário, Vazio (UMDF V2)".

    No menu Arquivo , aponte para Novoe selecione Projeto. No nó Visual C++, em Drivers do Windows, selecione WDF e, em seguida, selecione Driver do Modo de Usuário, Vazio (UMDF V2)

    Captura de tela que mostra a caixa de diálogo

  2. Abra o arquivo INF.

    Em Gerenciador de Soluções, no nó >project-name<, na pasta Arquivos de Driver, clique duas vezes em <project-name>.inf.

  3. No arquivo INF, remova a classe de dispositivo personalizada usando as seguintes etapas:

    1. Remova as duas seções a seguir:

      [ClassInstall32]
      AddReg=SampleClass_RegistryAdd
      
      [SampleClass_RegistryAdd]
      HKR,,,,%ClassName%
      HKR,,Icon,,"-10"
      
    2. Na seção [Strings], remova a linha a seguir.

      ClassName="Samples" ; TODO: edit ClassName
      
  4. No arquivo INF, defina a classe de dispositivo do driver como Proximidade:

    1. Alterar o valor de Class para Proximity
    2. Alterar o valor de ClassGuid para {5630831C-06C9-4856-B327-F5D32586E060}
      • Este é o GUID da classe de dispositivo De proximidade.
    [Version]
    ...
    Class=Proximity
    ClassGuid={5630831C-06C9-4856-B327-F5D32586E060} ; Proximity class GUID
    ...
    
  5. No arquivo INF, adicione uma referência à Extensão de Classe NFC. Isso garante que o WDF (Windows Driver Framework) carregue o driver NFC CX quando o driver cliente for carregado.

    1. Localize a seção <project-name>_Install.
    2. Adicione UmdfExtensions=NfcCx0102.
    [<project-name>_Install]
    ...
    UmdfExtensions=NfcCx0102
    
  6. Nas configurações de build do driver, vincule-se à Extensão de Classe NFC. Isso garante que a API CX nfc esteja disponível durante a compilação de código.

    1. Em Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Propriedades. Em Propriedades de Configuração, em Configurações de Driver, selecione NFC.
    2. Verifique se a configuração está definida como All Configurations.
    3. Verifique se a Plataforma está definida como All Platforms.
    4. Definir link para a extensão de classe NFC como Yes.

    Visual Studio caixa de diálogo: Páginas de Propriedades do MyNfcDriver, mostrando

  7. Adicione o arquivo com o nome Driver.cpp ao projeto.

  8. Criar uma DriverEntry rotina em Driver.cpp. Esse é o ponto de entrada para o driver. Sua principal finalidade é inicializar o WDF e registrar a função EvtDriverDeviceAdd de retorno de chamada.

    #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. Adicione dois arquivos nomeados Device.cpp e Device.h ao projeto.

  10. Em Device.h, defina a DeviceContext classe.

    #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. Em Device.cpp, inicie a definição de função DeviceContext::AddDevice.

    #include "Device.h"
    
    NTSTATUS DeviceContext::AddDevice(
        _In_ WDFDRIVER Driver,
        _Inout_ PWDFDEVICE_INIT DeviceInit)
    {
        NTSTATUS status;
    
    
  12. Defina a configuração do dispositivo NFC CX. A configuração do dispositivo inclui o fornecimento da função de callback EvtNfcCxWriteNciPacket. Esse retorno de chamada recebe os pacotes de NCI do driver NFC CX que o driver cliente deve encaminhar para o Controlador 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. Registre os callbacks de energia PnP necessários para o seu driver cliente.

    Um driver cliente típico provavelmente exigirá a EvtDevicePrepareHardware, a EvtDeviceReleaseHardware, a EvtDeviceD0Entry e a EvtDeviceD0Exit. Os requisitos podem variar dependendo de como o driver cliente lida com o gerenciamento de energia.

        // 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. Chame a função WdfDeviceCreate para criar o objeto 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. Chame a função NfcCxDeviceInitialize.

    Essa função deve ser chamada depois que o WDFDEVICE objeto tiver sido criado para permitir que o driver NFC CX conclua sua inicialização da instância do dispositivo.

        // Let NFC CX finish initializing the device instance.
        status = NfcCxDeviceInitialize(device);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
  16. Chame NfcCxSetRfDiscoveryConfig para especificar as tecnologias e os protocolos NFC compatíveis com o controlador 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. Encerre a função DeviceContext::AddDevice.

        return STATUS_SUCCESS;
    }
    
  18. Implemente as funções de callback PrepareHardware e ReleaseHardware.

    Essas duas funções são usadas para inicializar e desinicializar os recursos de hardware atribuídos à instância do dispositivo do Controlador NFC. Sua implementação depende do tipo de barramento ao qual o dispositivo está conectado (por exemplo, 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. Chame a função NfcCxHardwareEvent com HostActionStart e HostActionStop para iniciar e parar a máquina de estados NCI nos momentos apropriados.

    Alguns drivers fazem isso durante os retornos de chamada de energia PnP D0Entry e D0Exit. No entanto, isso pode variar dependendo de como o driver cliente lida com o gerenciamento de energia.

    // 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. Implemente a função WriteNciPacket.

    Esse callback é chamado pelo NFC CX quando há um pacote NCI para enviar ao Controlador 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. Chame a função NfcCxNciReadNotification quando o Controlador NFC tiver um pacote NCI que deve ser enviado para o NFC CX. Normalmente, faça isso em um retorno de chamada de evento de hardware.

    Por exemplo:

Logging

Considere adicionar o registro em log ao driver cliente para facilitar a depuração. O rastreamento de ETW e o rastreamento do WPP são boas opções.