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

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

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

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

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

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

  • Глобальный уникальный идентификатор.

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

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

  • Процедуры ссылок и разыменования

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

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

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

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

    Примечание

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

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

    Если драйвер повторно использует один и тот же GUID интерфейса с измененными полями Size или Version , драйвер не должен предоставлять 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, и драйвер 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. Затем он вызывает функцию обратного вызова EvtDeviceProcessQueryInterfaceRequest драйвера B, если она существует, чтобы она могла изучить и, возможно, изменить значения интерфейса.

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

Обслуживание счетчика ссылок

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

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

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

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

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

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