Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В статье описывается выполнение следующих задач:
- Напишите исходный драйвер 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, такие как коллекцииTop-Level для драйверов верхнего уровня и приложений пользовательского режима. В этой модели задача заключалась в написании мини-драйвера, поскольку это может быть сложной задачей.
Начиная с Windows 10, новая виртуальная платформа HID Framework (VHF) устраняет необходимость записи мини-driver транспорта. Вместо этого можно написать исходный драйвер HID с помощью интерфейсов программирования KMDF или WDM. Платформа состоит из статической библиотеки Майкрософт, которая предоставляет элементы программирования, используемые драйвером. Он также включает в себя драйвер, предоставляемый корпорацией Майкрософт, который перечисляет одно или несколько дочерних устройств и переходит к созданию и управлению виртуальным деревом HID.
Примечание.
В этом выпуске VHF поддерживает драйвер источника HID только в режиме ядра.
В этой статье описывается архитектура платформы, дерево виртуальных устройств HID и сценарии конфигурации.
Дерево устройств Virtual HID
На этом изображении дерево устройств отображает драйверы и связанные с ними объекты устройства.
Драйвер источника HID
Исходный драйвер HID ссылается на Vhfkm.lib и включает Vhf.h в свой проект сборки. Драйвер можно записать с помощью модели драйверов Windows (WDM) или Kernel-Mode Driver Framework (KMDF), которая входит в состав Платформы драйверов Windows (WDF). Драйвер можно загрузить в качестве драйвера фильтра или драйвера функции в стеке устройств.
Статическая библиотека VHF (vhfkm.lib)
Статическая библиотека включена в комплект драйверов Windows (WDK) для Windows 10. Библиотека предоставляет интерфейсы программирования, такие как подпрограммы и функции обратного вызова, используемые драйвером источника HID. Когда драйвер вызывает функцию, статическая библиотека перенаправит запрос на драйвер VHF, обрабатывающий запрос.
Драйвер VHF (Vhf.sys)
Встроенный драйвер, предоставленный корпорацией Майкрософт. Этот драйвер должен быть загружен в качестве драйвера нижнего фильтра ниже вашего драйвера в стеке устройств HID. Драйвер VHF динамически перечисляет дочерние устройства и создает объекты физических устройств (PDO) для одного или нескольких устройств HID, которые указывает исходный драйвер HID. Он также реализует функции мини-драйвера транспорта HID для перечисленных дочерних устройств.
Пара драйверов класса HID (Hidclass.sys, Mshidkmdf.sys)
Пара Hidclass/Mshidkmdf перечисляет коллекцииTop-Level (TLC), аналогичные перечислению этих коллекций для реального устройства HID. Клиент HID может продолжать запрашивать и использовать TLCs так же, как реальное устройство HID. Эта пара драйверов устанавливается в качестве драйвера функции в стеке устройств.
Примечание.
В некоторых сценариях клиенту HID может потребоваться определить источник данных HID. Например, система имеет встроенный датчик и получает данные от удаленного датчика того же типа. Система может потребоваться выбрать один датчик, чтобы быть более надежным. Чтобы различать два датчика, подключенные к системе, клиент HID запрашивает идентификатор контейнера TLC. В этом случае драйвер источника HID может указать идентификатор контейнера, который сообщается как идентификатор контейнера виртуального устройства HID VHF.
Клиент HID (приложение)
Запрашивает и использует TLC, которые сообщаются стеком устройств HID.
Требования к заголовкам и библиотекам
В этой процедуре описывается, как написать исходный драйвер HID, который сообщает кнопки гарнитуры в операционную систему. В этом случае драйвер, реализующий этот код, может быть существующим измененным звуковым драйвером KMDF, который действует как HID-источник, сообщающий о кнопках гарнитуры через VHF.
Включите Vhf.h из WDK.
Ссылка на vhfkm.lib, включенная в WDK.
Создайте дескриптор отчета 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 получает этот запрос, пара определяет тип запроса и создает соответствующий запрос HID Minidriver IOCTL , перенаправляя его в VHF. После получения запроса IOCTL VHF может обрабатывать запрос, полагаться на драйвер источника HID для его обработки или завершения запроса с помощью STATUS_NOT_SUPPORTED.
VHF обрабатывает эти ioCTLs:
- IOCTL_HID_GET_STRING
- IOCTL_HID_GET_DEVICE_ATTRIBUTES
- IOCTL_HID_GET_DEVICE_DESCRIPTOR
- IOCTL_HID_GET_REPORT_DESCRIPTOR
Если запрос — GetFeature, SetFeature, WriteReport или GetInputReport, а драйвер источника HID зарегистрировал соответствующую функцию обратного вызова, VHF вызывает функцию обратного вызова. В этой функции драйвер источника HID может получить или задать данные HID для виртуального устройства HID. Если драйвер не регистрирует обратный вызов, VHF завершает запрос с состоянием STATUS_NOT_SUPPORTED.
VHF вызывает реализованные драйвером источника HID функции обратного вызова событий для следующих управляющих кодов ввода-вывода (IOCTLs):
-
Если драйвер хочет обработать политику буферизации при отправке буфера для получения входного отчета HID, он должен реализовать evtVhfReadyForNextReadReport и указать указатель в члене EvtVhfAsyncOperationGetInputReport . Дополнительные сведения см. в разделе "Отправка входного отчета HID".
IOCTL_HID_GET_FEATURE или IOCTL_HID_SET_FEATURE
Если драйвер хочет получить или задать отчет о функциях HID асинхронно, драйвер должен реализовать функцию EvtVhfAsyncOperation и указать указатель на функцию получения или задания реализации в элементе EvtVhfAsyncOperationGetFeature или EvtVhfAsyncOperationSetFeatureVHF_CONFIG.
-
Если драйвер хочет получить отчет ввода HID асинхронно, драйвер должен реализовать функцию EvtVhfAsyncOperation и указать указатель на функцию в элементе EvtVhfAsyncOperationGetInputReportVHF_CONFIG.
-
Если драйвер хочет запросить запись отчета ввода HID асинхронно, драйвер должен реализовать функцию EvtVhfAsyncOperation и указать указатель на функцию в члене EvtVhfAsyncOperationWriteReport структуры VHF_CONFIG.
Для любого другого IOCTL минидрайвера HID 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:
- Драйвер источника HID перестает инициировать вызовы в VHF.
- Источник HID вызывает VhfDelete с параметром Wait, установленным в FALSE.
- VHF прекращает выполнение функций обратного вызова, реализованных в драйвере источника HID.
- VHF начинает сообщать диспетчеру PnP об устройстве как об отсутствующем. На этом этапе вызов VhfDelete может возвратиться.
- Когда устройство сообщается как отсутствующее устройство, если драйвер источника HID зарегистрировал свою реализацию, VHF вызывает EvtVhfCleanup.
- После того как 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"