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


Использование интерфейсов Driver-Defined

Драйверы могут определять интерфейсы, специфичные для устройств, к которым могут обращаться другие драйверы. Эти определяемые драйвером интерфейсы могут состоять из набора вызываемых подпрограмм, набора структур данных или обоих. Драйвер обычно предоставляет указатели на эти подпрограммы и структуры в структуре интерфейса, определяемой драйвером, который драйвер предоставляет другим драйверам.

Например, драйвер шины может предоставить одну или несколько процедур, которые драйверы более высокого уровня могут вызывать для получения сведений о дочернем устройстве, если эта информация недоступна в списке ресурсов дочернего устройства.

Пример набора определяемых драйвером интерфейсов, которые описаны в WDK, см. в разделе "Процедуры USB". Также ознакомьтесь с версией образца тостера, основанной на фреймворке.

Создание интерфейса

Каждый определяемый драйвером интерфейс определяется следующим образом:

  • A GUID

  • Номер версии

  • Структура интерфейса, определяемая драйвером

  • Справочные и расшифровочные подпрограммы

Чтобы создать интерфейс и сделать его доступным для других драйверов, драйверы на основе платформы могут использовать следующие действия:

  1. Определите структуру интерфейса.

    Первый элемент этой определяемой драйвером структуры должен быть структурой заголовков INTERFACE . Дополнительные члены могут включать данные интерфейса и указатели на дополнительные структуры или подпрограммы, которые может вызывать другой драйвер.

    Драйвер должен предоставить структуру WDF_QUERY_INTERFACE_CONFIG , описывающую определенный интерфейс.

    Примечание.

    При использовании WDF_QUERY_INTERFACE_CONFIG WDF не поддерживает несколько версий одного интерфейса, использующих один и тот же GUID интерфейса.

    В результате при вводе новой версии существующего интерфейса рекомендуется создать новый GUID вместо изменения полей размера или версии структуры ИНТЕРФЕЙСА .

    Если драйвер повторно использует тот же GUID интерфейса с измененными полями размера или версии , драйвер не должен предоставлять WDF_QUERY_INTERFACE_CONFIG и вместо этого должен предоставлять подпрограмму обратного вызова EvtDeviceWdmIrpPreprocess для IRP_MN_QUERY_INTERFACE.

  2. Вызвать WdfDeviceAddQueryInterface.

    Метод WdfDeviceAddQueryInterface выполняет следующие действия:

    • Хранит сведения об интерфейсе, например идентификаторе GUID, номере версии и размере структуры, поэтому платформа может распознать запрос другого драйвера для интерфейса.
    • Регистрирует необязательную функцию обратного вызова событий EvtDeviceProcessQueryInterfaceRequest , которая вызывается платформой, когда другой драйвер запрашивает интерфейс.

Каждый экземпляр определяемого драйвером интерфейса связан с отдельным устройством, поэтому драйверы обычно вызывают функцию обратного вызова WdfDeviceAddQueryInterface из функции обратного вызова EvtDriverDeviceAdd или EvtChildListCreateDevice .

Доступ к интерфейсу

Если драйвер определил интерфейс, другой драйвер на основе платформы может запросить доступ к интерфейсу, вызвав WdfFdoQueryForInterface и передав GUID, номер версии, указатель на структуру и размер структуры. Платформа создает запрос ввода-вывода и отправляет его в начало стека драйверов.

Драйвер обычно вызывает WdfFdoQueryForInterface из функции обратного вызова EvtDriverDeviceAdd . Кроме того, если драйвер должен освободить интерфейс, когда устройство не находится в рабочем состоянии, драйвер может вызвать WdfFdoQueryForInterface из функции обратного вызова EvtDevicePrepareHardware и вызвать процедуру снятия ссылки интерфейса из функции обратного вызова EvtDeviceReleaseHardware.

Если драйвер A запрашивает у драйвера B интерфейс, определенный драйвером B, фреймворк обрабатывает запрос для драйвера B. Фреймворк проверяет, является ли GUID и версия поддерживаемым интерфейсом, и что размер структуры, который драйвер A предоставил, достаточно велик для хранения интерфейса.

Когда драйвер вызывает WdfFdoQueryForInterface, запрос ввода-вывода, который создает платформа, проходит весь путь до нижней части стека драйверов. Если простой стек драйверов состоит из трех драйверов — A, B и C— и если драйвер A запрашивает интерфейс, оба драйвера B и driver C могут поддерживать интерфейс. Например, драйвер B может заполнить структуру интерфейса драйвера A перед передачей запроса на драйвер C. Драйвер C может предоставить функцию обратного вызова EvtDeviceProcessQueryInterfaceRequest , которая проверяет содержимое структуры интерфейса и, возможно, изменяет их.

Если драйвер A должен получить доступ к интерфейсу драйвера B, а драйвер B — это удаленный целевой объект ввода-вывода (то есть драйвер, который находится в другом стеке драйверов), драйвер A должен вызывать WdfIoTargetQueryForInterface вместо WdfFdoQueryForInterface.

Использование коммуникации One-Way или Two-Way

Вы можете определить интерфейс, обеспечивающий односторонняя связь или двухсторонняя связь. Чтобы указать двустороннее взаимодействие, драйвер устанавливает элемент ImportInterface своей структуры WDF_QUERY_INTERFACE_CONFIG в TRUE.

Если интерфейс обеспечивает односторонняя связь, и если драйвер A запрашивает интерфейс драйвера B, данные интерфейса передаются только от драйвера B к драйверу A. Когда платформа получает запрос драйвера A для интерфейса, поддерживающего односторонняя связь, платформа копирует значения интерфейса, определяемые драйвером, в структуру интерфейса A драйвера. Затем он вызывает функцию обратного вызова драйвера B EvtDeviceProcessQueryInterfaceRequest , если она существует, поэтому она может изучить и, возможно, изменить значения интерфейса.

Если интерфейс обеспечивает двустороннее взаимодействие, структура интерфейса содержит некоторые элементы, которые драйвер A заполняет перед отправкой запроса драйверу B. Драйвер B может прочесть значения параметров, предоставленные драйвером A, и на их основе определить, какую информацию следует предоставлять драйверу A. Когда платформа получает запрос драйвера A на интерфейс, поддерживающий двустороннее взаимодействие, она вызывает функцию обратного вызова EvtDeviceProcessQueryInterfaceRequest, чтобы изучить полученные значения и предоставить выходные данные. Для двустороннего взаимодействия требуется функция обратного вызова, так как платформа не копирует значения интерфейса в структуру интерфейса драйвера A.

Поддержание счетчика ссылок

Каждый интерфейс должен включать ссылочную функцию и функцию разыменования, которая увеличивает и уменьшает количество ссылок для интерфейса. Драйвер, определяющий интерфейс, указывает адреса этих функций в структуре ИНТЕРФЕЙСА .

Когда драйвер A запрашивает драйвер B для интерфейса, платформа вызывает эталонную функцию интерфейса, прежде чем сделать интерфейс доступным для драйвера A. Когда драйвер A завершит работу с интерфейсом, он должен вызвать функцию деконференции интерфейса.

Функции ссылок и разыменования для большинства интерфейсов могут быть пустыми функциями no-op. Платформа предоставляет no-op функции счетчика ссылок, WdfDeviceInterfaceReferenceNoOp и WdfDeviceInterfaceDereferenceNoOp, которые могут использовать большинство драйверов.

Единственный случай, когда драйверы должны отслеживать количество ссылок интерфейса и предоставлять функции увеличения и уменьшения ссылок, — это когда драйвер A запрашивает интерфейс у удаленного целевого объекта ввода-вывода (то есть драйвера, находящегося в другом стеке драйверов). В этом случае драйвер B (в другом стеке) должен реализовать счетчик ссылок, чтобы предотвратить удаление его устройства в то время, когда драйвер A использует интерфейс драйвера B.

Если вы разрабатываете драйвер B, который определяет интерфейс, необходимо решить, будет ли доступ к интерфейсу драйвера из другого стека драйверов. (Драйвер B не может определить, является ли запрос на его интерфейс из локального стека драйверов или из удаленного стека.) Если драйвер будет поддерживать запросы интерфейса из удаленного стека, драйвер должен реализовать счетчик ссылок.

Если вы разрабатываете драйвер A, который обращается к интерфейсу в удаленном целевом объекте ввода-вывода, драйвер должен предоставить функцию обратного вызова EvtIoTargetQueryRemove, которая освобождает интерфейс, когда устройство B драйвера B собираются удалить; функцию обратного вызова EvtIoTargetRemoveComplete, которая освобождает интерфейс при неожиданном удалении устройства B; и функцию обратного вызова EvtIoTargetRemoveCanceled, которая повторно запрашивает интерфейс, если попытка удалить устройство была отменена.