Поделиться через


Краткое руководство по началу работы с NFC CX

В этом руководстве показано, как написать функциональный драйвер NFC с помощью драйвера NFC Class Extension (NFC CX).

Примечание

Драйвер, использующий драйвер расширения класса в своей реализации, называется клиентским драйвером. То есть клиент драйвера расширения класса.

Предварительные требования

Обязанности водителя клиента

Драйвер NFC CX отвечает за обработку запросов ввода-вывода, отправленных драйверу, и создание соответствующих пакетов команд NCI. Драйвер клиента отвечает за отправку этих пакетов NCI контроллеру NFC и обратной отправки пакетов ответа NCI драйверу NFC CX.

Драйвер клиента определяет, как отправлять пакеты NCI на nfc-контроллер. Этот процесс зависит от типа используемой аппаратной шины. Распространенные шины, используемые контроллерами NFC, включают I2C, SPI и USB.

Полный код проекта

Полная версия этого примера кода доступна на сайте GitHub: пример драйвера клиента NFC CX.

Настройка проекта

  1. В Visual Studio создайте проект "Драйвер пользовательского режима, пустой (UMDF V2)".

    В меню Файл последовательно выберите команды Создатьи Проект. В узле Visual C++ в разделе Драйверы Windows выберите WDF, а затем — Драйвер пользовательского режима, Пустой (UMDF V2)

    Снимок экрана: диалоговое окно

  2. Откройте INF-файл.

    В Обозреватель решений в <узле имя> проекта в папке Файлы драйверов дважды щелкните <файл project-name.inf>.

  3. В INF-файле удалите пользовательский класс устройства, выполнив следующие действия.

    1. Удалите следующие два раздела:

      [ClassInstall32]
      AddReg=SampleClass_RegistryAdd
      
      [SampleClass_RegistryAdd]
      HKR,,,,%ClassName%
      HKR,,Icon,,"-10"
      
    2. [Strings] В разделе удалите следующую строку.

      ClassName="Samples" ; TODO: edit ClassName
      
  4. В INF-файле задайте для класса устройства драйвера значение Proximity:

    1. Измените значение Class на Proximity
    2. Измените значение ClassGuid на {5630831C-06C9-4856-B327-F5D32586E060}
      • Это GUID класса устройства Proximity.
    [Version]
    ...
    Class=Proximity
    ClassGuid={5630831C-06C9-4856-B327-F5D32586E060} ; Proximity class GUID
    ...
    
  5. В INF-файле добавьте ссылку на расширение класса NFC. Это гарантирует, что Windows Driver Framework (WDF) загружает драйвер NFC CX при загрузке драйвера клиента.

    1. Найдите раздел <project-name>_Install.
    2. Добавьте UmdfExtensions=NfcCx0102.
    [<project-name>_Install]
    ...
    UmdfExtensions=NfcCx0102
    
  6. В параметрах сборки драйвера установите ссылку на расширение класса NFC. Это гарантирует, что API NFC CX будет доступен во время компиляции кода.

    1. В обозревателе решений щелкните правой кнопкой мыши проект и выберите Свойства. В разделе Свойства конфигурации в разделе Параметры драйвера выберите NFC.
    2. Убедитесь, что для параметра Конфигурация задано значение All Configurations.
    3. Убедитесь, что для параметра Platform задано значение All Platforms.
    4. Задайте для link to NFC Class Extension значениеYes.

    Диалоговое окно Visual Studio: Страницы свойств MyNfcDriver, где для параметра

  7. Добавьте в проект файл с именем Driver.cpp .

  8. Создайте подпрограмму DriverEntry в Driver.cpp. Это точка входа для драйвера. Его основной целью является инициализация WDF и регистрация функции обратного 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. Добавьте в проект два файла с именами Device.cpp и Device.h .

  10. В Device.hопределите 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. В Device.cppначинается DeviceContext::AddDevice определение функции.

    #include "Device.h"
    
    NTSTATUS DeviceContext::AddDevice(
        _In_ WDFDRIVER Driver,
        _Inout_ PWDFDEVICE_INIT DeviceInit)
    {
        NTSTATUS status;
    
    
  12. Настройка конфигурации устройства NFC CX. Конфигурация устройства включает в себя предоставление функции обратного EvtNfcCxWriteNciPacket вызова. Этот обратный вызов получает пакеты NCI от драйвера NFC CX, который драйвер клиента должен перенаправить контроллеру 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. Зарегистрируйте обратные вызовы питания PnP, необходимые драйверу клиента.

    Для типичного клиентского драйвера, скорее EvtDevicePrepareHardwareвсего, потребуются функции , EvtDeviceReleaseHardware, EvtDeviceD0Entry и EvtDeviceD0Exit . Требования могут отличаться в зависимости от того, как драйвер клиента обрабатывает управление питанием.

        // 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. Вызовите функцию WdfDeviceCreate , чтобы создать 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. Вызовите функцию NfcCxDeviceInitialize.

    Эта функция должна вызываться после WDFDEVICE создания объекта , чтобы позволить драйверу NFC CX завершить инициализацию экземпляра устройства.

        // Let NFC CX finish initializing the device instance.
        status = NfcCxDeviceInitialize(device);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    
  16. Вызовите NfcCxSetRfDiscoveryConfig , чтобы указать технологии и протоколы NFC, поддерживаемые контроллером 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. Завершите функцию DeviceContext::AddDevice .

        return STATUS_SUCCESS;
    }
    
  18. Реализуйте функции обратного PrepareHardware вызова и ReleaseHardware .

    Эти две функции используются для инициализации и неинициализации аппаратных ресурсов, назначенных экземпляру устройства контроллера NFC. Их реализация зависит от типа шины, к которому подключено устройство (например, I2C, SPI и 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. Вызовите функцию NfcCxHardwareEvent с HostActionStart помощью и HostActionStop , чтобы запустить и остановить конечный автомат NCI в соответствующее время.

    Некоторые драйверы делают это во время обратных D0Entry вызовов питания PnP и D0Exit . Однако это может отличаться в зависимости от того, как драйвер клиента обрабатывает управление питанием.

    // 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. Реализуйте функцию WriteNciPacket .

    Этот обратный вызов вызывается NFC CX при наличии пакета NCI для отправки на контроллер 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. Вызовите функцию, NfcCxNciReadNotification если контроллер NFC содержит пакет NCI, который должен быть отправлен в NFC CX. Обычно это делается в обратном вызове события оборудования.

    Пример:

Ведение журнала

Рассмотрите возможность добавления ведения журнала в драйвер клиента, чтобы упростить отладку. Хорошими вариантами являются трассировка etw и трассировка WPP .