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


Создание исходного драйвера HID с помощью Виртуальной платформы HID (VHF)

В этом разделе объясняется, как:

  • Создайте исходный драйвер Kernel-Mode Driver Framework (KMDF)HID, который отправляет отчеты о чтении HID в Windows.
  • Загрузите драйвер VHF в качестве нижнего фильтра в драйвер источника HID в стеке виртуальных устройств HID.

Сведения о написании исходного драйвера HID, который передает данные HID в операционную систему.

Устройство ввода HID, например клавиатура, мышь, перо, сенсорный ввод или кнопка, отправляет различные отчеты в операционную систему, чтобы она понимала назначение устройства и выполняла необходимые действия. Отчеты представлены в виде коллекций HID и использования HID. Устройство отправляет эти отчеты через различные транспорты, некоторые из которых поддерживаются Windows, например HID через I2C и HID через USB. В некоторых случаях транспорт может не поддерживаться Windows или отчеты могут не сопоставляться напрямую с реальным оборудованием. Это может быть поток данных в формате HID, который отправляется другим программным компонентом для виртуального оборудования, например, для кнопок или датчиков без GPIO. Например, рассмотрим данные акселерометра с телефона, который ведет себя как игровой контроллер, передаваемые по беспроводной сети на компьютер. В другом примере компьютер может получать удаленные входные данные от устройства Miracast по протоколу UIBC.

В предыдущих версиях Windows для поддержки новых транспортных средств (реального оборудования или программного обеспечения) необходимо было написать транспортный мини-накопитель HID и привязать его к встроенному драйверу microsoft, Hidclass.sys. Пара классов и мини-драйверов предоставляла коллекции HID, такие как коллекции верхнего уровня , драйверам верхнего уровня и приложениям в пользовательском режиме. В этой модели задачей было написание мини-диска, который может быть сложной задачей.

Начиная с Windows 10, новая виртуальная платформа HID Framework (VHF) избавляет от необходимости писать транспортный мини-диск. Вместо этого можно написать исходный драйвер HID с помощью программных интерфейсов KMDF или WDM. Платформа состоит из статической библиотеки, предоставляемой корпорацией Майкрософт, которая предоставляет элементы программирования, используемые драйвером. Он также включает встроенный драйвер корпорации Майкрософт, который перечисляет одно или несколько дочерних устройств и переходит к созданию и управлению виртуальным деревом HID.

Примечание

В этом выпуске VHF поддерживает драйвер источника HID только в режиме ядра.

В этом разделе описывается архитектура платформы, виртуальное дерево устройств HID и сценарии конфигурации.

Дерево виртуальных устройств HID

На этом изображении в дереве устройств показаны драйверы и связанные с ними объекты устройств.

Схема виртуального дерева устройств HID.

Драйвер источника HID (ваш драйвер)

Исходный драйвер HID ссылается на Vhfkm.lib и включает Vhf.h в свой проект сборки. Драйвер можно написать с помощью модели драйвера Windows (WDM) или платформы Kernel-Mode драйвера (KMDF), которая является частью Платформы драйверов Windows (WDF). Драйвер можно загрузить как драйвер фильтра или драйвер функции в стеке устройств.

Статическая библиотека VHF (vhfkm.lib)

Статическая библиотека входит в комплект драйверов Windows (WDK) для Windows 10. Библиотека предоставляет программные интерфейсы, такие как подпрограммы и функции обратного вызова, которые используются драйвером источника HID. Когда драйвер вызывает функцию, статическая библиотека перенаправит запрос в драйвер VHF, который обрабатывает запрос.

Драйвер VHF (Vhf.sys)

Встроенный драйвер, предоставляемый корпорацией Майкрософт. Этот драйвер должен быть загружен в качестве драйвера нижнего фильтра ниже драйвера в стеке исходных устройств HID. Драйвер VHF динамически перечисляет дочерние устройства и создает объекты физических устройств (PDO) для одного или нескольких устройств HID, указанных в исходном драйвере HID. Он также реализует функциональные возможности мини-драйвера HID Transport для перечисленных дочерних устройств.

Пара драйверов класса HID (Hidclass.sys, Mshidkmdf.sys)

Пара Hidclass/Mshidkmdf перечисляет коллекции верхнего уровня (TLC) аналогично перечислению этих коллекций для реального устройства HID. Клиент HID может продолжать запрашивать и использовать TLCs так же, как реальное устройство HID. Эта пара драйверов устанавливается как драйвер функции в стеке устройств.

Примечание

В некоторых сценариях клиенту HID может потребоваться определить источник данных HID. Например, система имеет встроенный датчик и получает данные от удаленного датчика того же типа. Система может выбрать один датчик, чтобы быть более надежным. Чтобы различать два датчика, подключенные к системе, клиент HID запрашивает идентификатор контейнера TLC. В этом случае исходный драйвер HID может предоставить идентификатор контейнера, который передается VHF как идентификатор контейнера виртуального устройства HID.

Клиент HID (приложение)

Запрашивает и использует TLCs, о которых сообщает стек устройств HID.

Требования к заголовкам и библиотекам

В этой процедуре описывается, как написать простой драйвер источника HID, который передает кнопки гарнитуры в операционную систему. В этом случае драйвером, реализующим этот код, может быть существующий аудиодрайвер KMDF, который был изменен для работы в качестве источника HID, сообщающего кнопки гарнитуры с помощью VHF.

  1. Включите Vhf.h, включенный в WDK для Windows 10.

  2. Ссылка на файл vhfkm.lib, включенный в WDK.

  3. Создайте дескриптор отчета HID, который устройство хочет сообщить операционной системе. В этом примере дескриптор отчета HID описывает кнопки гарнитуры. В отчете указывается входной отчет HID размером 8 бит (1 байт). Первые три бита предназначены для кнопок среднего, увеличения громкости и уменьшения громкости гарнитуры. Оставшиеся биты не используются.

    UCHAR HeadSetReportDescriptor[] = {
        0x05, 0x01,         // USAGE_PAGE (Generic Desktop Controls)
        0x09, 0x0D,         // USAGE (Portable Device Buttons)
        0xA1, 0x01,         // COLLECTION (Application)
        0x85, 0x01,         //   REPORT_ID (1)
        0x05, 0x09,         //   USAGE_PAGE (Button Page)
        0x09, 0x01,         //   USAGE (Button 1 - HeadSet : middle button)
        0x09, 0x02,         //   USAGE (Button 2 - HeadSet : volume up button)
        0x09, 0x03,         //   USAGE (Button 3 - HeadSet : volume down button)
        0x15, 0x00,         //   LOGICAL_MINIMUM (0)
        0x25, 0x01,         //   LOGICAL_MAXIMUM (1)
        0x75, 0x01,         //   REPORT_SIZE (1)
        0x95, 0x03,         //   REPORT_COUNT (3)
        0x81, 0x02,         //   INPUT (Data,Var,Abs)
        0x95, 0x05,         //   REPORT_COUNT (5)
        0x81, 0x03,         //   INPUT (Cnst,Var,Abs)
        0xC0,               // END_COLLECTION
    };
    

Создание виртуального устройства HID

Инициализируйте структуру VHF_CONFIG , вызвав макрос VHF_CONFIG_INIT , а затем вызовите метод VhfCreate . Драйвер должен вызывать VhfCreate по PASSIVE_LEVEL после вызова WdfDeviceCreate , как правило, в функции обратного вызова EvtDriverDeviceAdd драйвера.

В вызове VhfCreate драйвер может указать определенные параметры конфигурации, например операции, которые необходимо обрабатывать асинхронно, или задать сведения об устройстве (идентификаторы поставщика или продукта).

Например, приложение запрашивает TLC. Когда пара драйверов класса HID получает этот запрос, пара определяет тип запроса и создает соответствующий запрос IOCTL HID Minidriver и пересылает его в VHF. Получив запрос IOCTL, VHF может обрабатывать запрос, полагаться на исходный драйвер HID для его обработки или завершить запрос с STATUS_NOT_SUPPORTED.

VHF обрабатывает следующие ioCTL:

Если запрос имеет значение GetFeature, SetFeature, WriteReport или GetInputReport, а драйвер источника HID зарегистрировал соответствующую функцию обратного вызова, VHF вызывает функцию обратного вызова. В этой функции исходный драйвер HID может получать или задавать данные HID для виртуального устройства HID. Если драйвер не регистрирует обратный вызов, VHF завершает запрос с состоянием STATUS_NOT_SUPPORTED.

VHF вызывает функции обратного вызова событий, реализованные драйвером источника HID, для следующих списков IOCTL:

  • IOCTL_HID_READ_REPORT

    Если драйвер хочет обработать политику буферизации при отправке буфера для получения входного отчета HID, он должен реализовать EvtVhfReadyForNextReadReport и указать указатель в члене EvtVhfAsyncOperationGetInputReport . Дополнительные сведения см. в разделе Отправка входного отчета HID.

  • IOCTL_HID_GET_FEATURE или IOCTL_HID_SET_FEATURE

    Если драйвер хочет асинхронно получить или задать отчет о функциях HID, драйвер должен реализовать функцию EvtVhfAsyncOperation и указать указатель на функцию реализации get или set в элементе EvtVhfAsyncOperationGetFeature или EvtVhfAsyncOperationSetFeatureVHF_CONFIG.

  • IOCTL_HID_GET_INPUT_REPORT

    Если драйвер хочет получить входной отчет HID асинхронно, драйвер должен реализовать функцию EvtVhfAsyncOperation и указать указатель на функцию в элементе EvtVhfAsyncOperationGetInputReportVHF_CONFIG.

  • IOCTL_HID_WRITE_REPORT

    Если драйвер хочет асинхронно записать входной отчет HID, драйвер должен реализовать функцию EvtVhfAsyncOperation и указать указатель на функцию в элементе EvtVhfAsyncOperationWriteReportVHF_CONFIG.

Для любого другого hiD Minidriver IOCTL VHF завершает запрос с STATUS_NOT_SUPPORTED.

Виртуальное устройство HID удаляется путем вызова VhfDelete. Обратный вызов EvtVhfCleanup требуется, если драйвер выделил ресурсы для виртуального устройства HID. Драйвер должен реализовать функцию EvtVhfCleanup и указать указатель на нее в элементе EvtVhfCleanupVHF_CONFIG. EvtVhfCleanup вызывается до завершения вызова VhfDelete . Дополнительные сведения см. в разделе Удаление виртуального устройства HID.

Примечание

После завершения асинхронной операции драйвер должен вызвать VhfAsyncOperationComplete , чтобы задать результаты операции. Метод можно вызвать из обратного вызова события или позже после возвращения из обратного вызова.

NTSTATUS
VhfSourceCreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
)

{
    WDF_OBJECT_ATTRIBUTES   deviceAttributes;
    PDEVICE_CONTEXT deviceContext;
    VHF_CONFIG vhfConfig;
    WDFDEVICE device;
    NTSTATUS status;

    PAGED_CODE();

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
    deviceAttributes.EvtCleanupCallback = VhfSourceDeviceCleanup;

    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);

    if (NT_SUCCESS(status))
    {
        deviceContext = DeviceGetContext(device);

        VHF_CONFIG_INIT(&vhfConfig,
            WdfDeviceWdmGetDeviceObject(device),
            sizeof(VhfHeadSetReportDescriptor),
            VhfHeadSetReportDescriptor);

        status = VhfCreate(&vhfConfig, &deviceContext->VhfHandle);

        if (!NT_SUCCESS(status)) {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "VhfCreate failed %!STATUS!", status);
            goto Error;
        }

        status = VhfStart(deviceContext->VhfHandle);
        if (!NT_SUCCESS(status)) {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "VhfStart failed %!STATUS!", status);
            goto Error;
        }

    }

Error:
    return status;
}

Отправка входного отчета HID

Отправьте входной отчет HID, вызвав VhfReadReportSubmit.

Как правило, устройство HID отправляет сведения об изменениях состояния, отправляя входные отчеты через прерывания. Например, устройство гарнитуры может отправлять отчет при изменении состояния кнопки. В таком случае вызывается подпрограмма обслуживания прерывания драйвера (ISR). В этой подпрограмме драйвер может запланировать отложенный вызов процедуры (DPC), который обрабатывает входной отчет и отправляет его в VHF, который отправляет сведения в операционную систему. По умолчанию VHF буферизует отчет, и драйвер источника HID может начать отправку входных отчетов HID по мере их поступления. Это устраняет необходимость в драйвере источника HID для реализации сложной синхронизации.

Драйвер источника HID может отправлять входные отчеты, реализовав политику буферизации для ожидающих отчетов. Чтобы избежать дублирования буферизации, драйвер источника HID может реализовать функцию обратного вызова EvtVhfReadyForNextReadReport и отслеживать, вызывал ли VHF этот обратный вызов. Если он был вызван ранее, драйвер источника HID может вызвать VhfReadReportSubmit для отправки отчета. Он должен дождаться вызова EvtVhfReadyForNextReadReport , прежде чем снова вызвать VhfReadReportSubmit .

VOID
MY_SubmitReadReport(
    PMY_CONTEXT  Context,
    BUTTON_TYPE  ButtonType,
    BUTTON_STATE ButtonState
    )
{
    PDEVICE_CONTEXT deviceContext = (PDEVICE_CONTEXT)(Context);

    if (ButtonState == ButtonStateUp) {
        deviceContext->VhfHidReport.ReportBuffer[0] &= ~(0x01 << ButtonType);
    } else {
        deviceContext->VhfHidReport.ReportBuffer[0] |=  (0x01 << ButtonType);
    }

    status = VhfReadReportSubmit(deviceContext->VhfHandle, &deviceContext->VhfHidReport);

    if (!NT_SUCCESS(status)) {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,"VhfReadReportSubmit failed %!STATUS!", status);
    }
}

Удаление виртуального устройства HID

Удалите виртуальное устройство HID, вызвав VhfDelete.

VhfDelete можно назвать синхронным или асинхронно, указав параметр Wait. Для синхронного вызова метод должен вызываться в PASSIVE_LEVEL, например из EvtCleanupCallback объекта устройства. VhfDelete возвращается после удаления виртуального устройства HID. Если драйвер вызывает VhfDelete асинхронно, он возвращается немедленно, а VHF вызывает EvtVhfCleanup после завершения операции удаления. Метод можно вызывать при максимальном DISPATCH_LEVEL. В этом случае драйвер должен зарегистрировать и реализовать функцию обратного вызова EvtVhfCleanup , когда он ранее назывался VhfCreate. Ниже приведена последовательность событий, когда драйверу источника HID требуется удалить виртуальное устройство HID:

  1. Драйвер источника HID перестает инициировать вызовы в VHF.
  2. Источник HID вызывает VhfDelete , а для параметра Wait задано значение FALSE.
  3. VHF прекращает вызов функций обратного вызова, реализованных драйвером источника HID.
  4. VHF начинает сообщать об отсутствии устройства в диспетчере PnP. На этом этапе может вернуться вызов VhfDelete.
  5. Когда устройство сообщается как отсутствующий, VHF вызывает EvtVhfCleanup , если исходный драйвер HID зарегистрировал свою реализацию.
  6. После возврата EvtVhfCleanup VHF выполняет очистку.
VOID
VhfSourceDeviceCleanup(
_In_ WDFOBJECT DeviceObject
)
{
    PDEVICE_CONTEXT deviceContext;

    PAGED_CODE();

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");

    deviceContext = DeviceGetContext(DeviceObject);

    if (deviceContext->VhfHandle != WDF_NO_HANDLE)
    {
        VhfDelete(deviceContext->VhfHandle, TRUE);
    }

}

Установка исходного драйвера HID

В INF-файле, который устанавливает исходный драйвер HID, убедитесь, что вы объявили Vhf.sys в качестве драйвера более низкого фильтра для исходного драйвера HID с помощью директивы AddReg.

[HIDVHF_Inst.NT.HW]
AddReg = HIDVHF_Inst.NT.AddReg

[HIDVHF_Inst.NT.AddReg]
HKR,,"LowerFilters",0x00010000,"vhf"