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


Драйвер PWM для модуля ШИМ на основе SoC

Чтобы предоставить доступ к контроллеру импульсной модуляции ширины (PWM), который является частью SoC и сопоставлен памяти с адресным пространством SoC, необходимо создать драйвер в режиме ядра. Драйвер должен зарегистрировать интерфейс класса устройства контроллера ШИМ, чтобы приложения UWP могли получить доступ к предоставляемым системой ШИМ-устройствам с помощью ИНТЕРФЕЙСОВ API WinRT для PWM, определенных в пространстве имен Windows.Devices.Pwm.

Примечание

Если у вас есть дополнительный модуль PWM с I2C, SPI или контроллером UART, вы можете получить доступ к модулю из приложения UWP с помощью ИНТЕРФЕЙСов API, определенных в пространстве имен Windows.Devices.Pwm и Windows.Devices.Pwm.Provider .

Устройство ШИМ абстрагируется в один контроллер и один или несколько контактов. Управление контроллером или контактами осуществляется с помощью определяемых ШИМ IOCTL. Например, драйвер ЖК-дисплея отправляет такие запросы драйверу ШИМ для управления уровнем подсветки ЖК-дисплея.

Создание сигналов ШИМ с несколькими контроллерами и несколькими каналами с настраиваемым периодом, полярностью и жизненным циклом, что позволяет выполнять следующие задачи:

  • Привод серводвига.
  • Приводите к нагрузке, например светодиодный или dc щеткой двигатель.
  • Создайте аналоговый сигнал.
  • Создайте точные часы.

В этом разделе описывается,

  • Как включить доступ UWP к предоставляемым системой ШИМ-устройствам.

  • Как обрабатывать запросы IOCTL PWM, отправленные приложением Win32 или сторонним драйвером режима ядра.

Целевая аудитория

  • Изготовители оборудования и IHV, разрабатывая систему с контроллером ШИМ на основе SoC.

Последнее обновление

  • Август 2017 г.

Версия Windows

  • Версия Windows 10

Важные API

Сведения о ШИМ

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

Волновую форму ШИМ можно классифицировать по 2 параметрам: периоду формы волны (T) и длительному циклу. Частота волн (f) — это обратная часть периода формы волны f=1/T. Рабочий цикл описывает долю времени "включено" или "активно" по отношению к регулярному интервалу или "периоду" времени; низкий рабочий цикл соответствует среднему значению низкой выходной мощности, так как питание отключено в течение большей части времени. Рабочий цикл выражается в процентах, где 100 % полностью включен, 0 % полностью отключены, 50 % являются активными в 50 % случаев.

Драйверы.

Доступ к контроллеру и контактам ШИМ, предоставляемым системой

Драйвер ШИМ должен зарегистрировать
GUID_DEVINTERFACE_PWM_CONTROLLER в качестве GUID интерфейса устройства для предоставления доступа к устройствам ШИМ и доступа к ним.

// {60824B4C-EED1-4C9C-B49C-1B961461A819} 

DEFINE_GUID(GUID_DEVINTERFACE_PWM_CONTROLLER, 0x60824b4c, 0xeed1, 0x4c9c, 0xb4, 0x9c, 0x1b, 0x96, 0x14, 0x61, 0xa8, 0x19); 

#define GUID_DEVINTERFACE_PWM_CONTROLLER_WSZ L"{60824B4C-EED1-4C9C-B49C-1B961461A819}" 

Чтобы зарегистрировать GUID интерфейса устройства, драйвер должен вызвать WdfDeviceCreateDeviceInterface в реализации драйвера функции обратного вызова EVT_WDF_DRIVER_DEVICE_ADD. После регистрации система назначает символьную ссылку контроллеру и контактам.

Приложение или другой драйвер может управлять контроллером или контактами с помощью определяемых ШИМ IOCTL. Перед отправкой ioCTL приложение-отправитель или драйвер должны открыть дескриптор файла для контроллера и маркера, указав его символьную ссылку. Для этого драйвер должен зарегистрировать события создания и закрытия файла. См. (ссылка)

Ниже приведен пример символьного пути для контроллера:

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}  

Аналогичным образом, формат контактов выглядит следующим образом: формат пути выглядит следующим образом:

<DeviceInterfaceSymbolicLinkName>\<PinNumber>

где <PinNumber> — индекс открываемого закрепления на основе 0.

Ниже приведен пример символьного пути для контактов:

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}\0 ; Opens pin 0 

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}\0001 ; Opens pin 1 with the leading 0s have no effect.

Чтобы открыть дескриптор файла, приложение должно вызвать API Configuration Manager (CM_Get_Device_Interface_*).

После открытия дескриптора файла приложение может отправлять эти запросы, вызывая функцию DeviceIoControl. См. раздел PWM.

Драйвер должен использовать предоставленную процедуру поддержки PWM PwmParsePinPath для анализа и проверки путей закрепления и извлечения номера контакта.

Настройка свойств интерфейса устройства

Чтобы использовать API WinRT pwm из приложений UWP, необходимо задать эти свойства интерфейса устройства .

  • DEVPKEY_DeviceInterface_Restricted

    В соответствии с текущей моделью доступа к устройствам UWP, чтобы предоставить приложениям UWP доступ к интерфейсу устройства ШИМ, необходимо задать значение FALSE.

  • DEVPKEY_DeviceInterface_SchematicName (ссылка???)

    Для использования фабричного метода PwmController.GetDeviceSelector(FriendlyName) требуется присвоение схематического имени интерфейсу устройства ШИМ статически подключенных устройств ШИМ. Имя схемы — это имя, присвоенное устройству ШИМ в схеме проектирования системы, например (PWM0, PWM_1 и т. д.). Предполагается, что имена схемы уникальны в системе, но они не применяются. По крайней мере, не должно быть 2 ШИМ-устройства с одинаковым именем схемы, в противном случае поведение WinRT PWM PwmController.GetDeviceSelector(FriendlyName) будет недетерминированным.

Свойства можно задать одним из двух способов:

  1. Использование INF-файла для драйвера PWM

    Используйте директиву AddProperty , чтобы задать свойства устройства. INF-файл должен разрешать задавать разные значения для одного и того же свойства в одном или подмножестве экземпляров устройства ШИМ. Ниже приведен пример настройки DEVPKEY_DeviceInterface_Restricted.

    ;***************************************** 
    ; Device interface installation 
    ;***************************************** 
    
    [PWM_Device.NT.Interfaces] 
    AddInterface={60824B4C-EED1-4C9C-B49C-1B961461A819},,PWM_Interface 
    
    [PWM_Interface] 
    AddProperty=PWM_Interface_AddProperty 
    
    ; Set DEVPKEY_DeviceInterface_Restricted property to false to allow UWP access 
    ; to the device interface without the need to be bound with device metadata. 
    ; If Restricted property is set to true, then only applications which are bound 
    ; with device metadata would be allowed access to the device interface. 
    
    [PWM_Interface_AddProperty] 
    {026e516e-b814-414b-83cd-856d6fef4822},6,0x11,,0 
    

    Не все проекты имеют одинаковую политику предоставления устройств ШИМ для UWP. Например, политика может разрешать UWP доступ к подмножества экземпляров устройств ШИМ. Для предоставления подмножества устройств ШИМ или присвоения другого значения свойства одному или подмножеству экземпляров устройств ШИМ требуется наличие разных идентификаторов оборудования для каждого экземпляра устройства ШИМ и выборочное сопоставление разделов INF на основе политики.

    Рассмотрим архитектуру на основе SoC, в которой есть четыре идентичных экземпляра устройства ШИМ (блоки IP) с именем PWM0,...,PWM3, где их идентификатор оборудования ACPI (_HID) FSCL00E0, а уникальный идентификатор (_UID) — 0,...,3. Для предоставления UWP всех ШИМ-устройств потребуются разделы INF, которые задают DEVPKEY_DeviceInterface_Restricted для сопоставления с идентификатором оборудования ACPI\FSCL00E0.

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

  2. Программным способом в драйвере PWM

    Драйвер PWM может вызвать IoSetDeviceInterfacePropertyData, чтобы задать свойства интерфейса устройства в реализации EVT_WDF_DRIVER_DEVICE_ADD после создания и публикации интерфейса устройства ШИМ. Драйвер отвечает за выбор значения и свойства устройства. Эта информация обычно хранится в системном ACPI для проектов на основе SoC. Значение каждого свойства интерфейса устройства можно указать в каждом узле устройства ACPI _DSD методе как Свойства устройства. Драйвер должен запросить the_DSD из ACPI, проанализировать данные свойств устройства, извлечь значение каждого свойства и назначить ему интерфейс устройства.

    Программная установка свойств делает драйвер и его INF-файл переносимым в разных структурах и, следовательно, BSP, где единственное изменение будет в ACPI DSDT, определяющем каждый узел устройства ШИМ. Однако чтение и анализ двоичных блоков ACPI является трудоемким и требует большого объема кода, который может быть подвержен ошибкам и уязвимостям, что приводит к большей поверхности ошибок.

Обработка событий открытия и закрытия файлов

Драйвер PWM должен зарегистрировать события создания и закрытия файлов, реализовав функции обратного вызова EVT_WDF_DEVICE_FILE_CREATE и EVT_WDF_FILE_CLEANUP/EVT_WDF_FILE_CLOSE. В реализации драйвер должен выполнять следующие задачи:

  1. Определите, является ли запрос на создание контроллером или пин-кодом.

  2. Если запрос предназначен для пин-кода, драйвер должен проанализировать и проверить путь к пин-коду для создания запросов на закрепление и убедиться, что запрошенный номер пин-кода находится в пределах контроллера.

  3. Предоставьте или запретите доступ к контроллеру и пин-кодам, создающим запросы.

  4. Поддерживайте целостность состояния контроллеров и закреплений для четко определенного конечного автомата.

В предыдущем наборе задач вторая задача проверки может выполняться в EVT_WDF_DEVICE_FILE_CREATE следующим образом:

  1. Если имя файла, связанное с объектом файла запроса, имеет значение NULL, завершите запрос с помощью STATUS_INVALID_DEVICE_REQUEST.

  2. Если имя файла, связанное с объектом файла запроса, является пустой строкой, то это запрос на создание контроллера, в противном случае — запрос на создание пин-кода.

  3. Если это запрос на создание пин-кода, то:

    1. Выполните синтаксический анализ пути закрепления на основе формата <DecimalName> и извлеките номер контакта, вызвав PwmParsePinPath.

    2. Если синтаксический анализ и проверка пути закрепления завершилось сбоем, завершите запрос с помощью STATUS_NO_SUCH_FILE.

    3. Если число контактов больше или равно количеству контактов контроллера, завершите запрос STATUS_NO_SUCH_FILE. Обратите внимание, что номер закрепления является индексом отсчитываемого от нуля.

    4. В противном случае продолжайте обработку EVT_WDF_DEVICE_FILE_CREATE.

Ниже приведен пример кода, который реализует ранее упомянутые шаги проверки для обработчика EVT_WDF_DEVICE_FILE_CREATE:

EVT_WDF_DEVICE_FILE_CREATE PwmEvtDeviceFileCreate;

VOID 

PwmEvtDeviceFileCreate ( 
    WDFDEVICE WdfDevice, 
    WDFREQUEST WdfRequest, 
    WDFFILEOBJECT WdfFileObject 
    ) 
{ 

    UNICODE_STRING* filenamePtr = WdfFileObjectGetFileName(WdfFileObject); 
    IMXPWM_DEVICE_CONTEXT* deviceContextPtr = PwmGetDeviceContext(WdfDevice); 
    NTSTATUS status; 
    ULONG pinNumber; 

    // 
    // Parse and validate the filename associated with the file object 
    // 

    bool isPinInterface; 

    if (filenamePtr == nullptr) { 

        WdfRequestComplete(WdfRequest, STATUS_INVALID_DEVICE_REQUEST); 

        return; 

    } else if (filenamePtr->Length > 0) { 

        // 
        // A non-empty filename means to open a pin under the controller namespace 
        // 

        status = PwmParsePinPath(filenamePtr, &pinNumber); 

        if (!NT_SUCCESS(status)) { 

            WdfRequestComplete(WdfRequest, status); 

            return; 

        } 


        if (pinNumber >= deviceContextPtr->ControllerInfo.PinCount) { 

            WdfRequestComplete(WdfRequest, STATUS_NO_SUCH_FILE); 

            return; 

        } 


        isPinInterface = true; 

    } else { 

        // 
        // An empty filename means that the create is against the root controller 
        // 

        isPinInterface = false; 
    } 

    // 
    // Continue request processing here 
    // 
} 

Общий доступ к контроллеру и контакту

Модель общего доступа для контроллера и контактов соответствует шаблону нескольких модулей чтения, один модуль записи. Контроллер или контакт может быть открыт для чтения несколькими вызывающими абонентами, но только один вызывающий объект может открыть этот контроллер или пин-код для записи одновременно.

Эта модель может быть реализована с помощью сочетания флагов Desired Access и Share Access при открытии дескриптора файла. Модель общего доступа DDI выбирает более простую семантику общего доступа, где для управления доступом используется только спецификация desired Access. Спецификация общего доступа не играет никакой роли в модели общего доступа и не учитывается, если она указана при открытии контроллера или пин-кода.

В EVT_WDF_DEVICE_FILE_CREATE запрос на создание Desired Access and Share Access должен быть извлечен и проверен на основе состояния контроллера или пин-кода или следующим образом:

  1. Если общий доступ не равен 0, откажите доступ и завершите запрос с STATUS_SHARING_VIOLATION.

  2. Если требуется доступ только для чтения, предоставьте доступ и продолжите обработку EVT_WDF_DEVICE_FILE_CREATE.

  3. Если требуемый доступ предназначен для записи, выполните приведенные далее действия.

Если контроллер или пин-код уже открыт для записи, отклоните доступ и завершите запрос с STATUS_SHARING_VIOLATION, в противном случае предоставьте доступ, пометьте контроллер или пин-код как открытый для записи и продолжите обработку EVT_WDF_DEVICE_FILE_CREATE.

Ниже приведен пример, показывающий, как извлечь требуемый доступ и общий доступ из запроса на создание:

void 
PwmCreateRequestGetAccess( 
    _In_ WDFREQUEST WdfRequest, 
    _Out_ ACCESS_MASK* DesiredAccessPtr, 
    _Out_ ULONG* ShareAccessPtr 
    ) 
{ 

    NT_ASSERT(ARGUMENT_PRESENT(DesiredAccessPtr)); 

    NT_ASSERT(ARGUMENT_PRESENT(ShareAccessPtr)); 


    WDF_REQUEST_PARAMETERS wdfRequestParameters; 

    WDF_REQUEST_PARAMETERS_INIT(&wdfRequestParameters); 

    WdfRequestGetParameters(WdfRequest, &wdfRequestParameters); 


    NT_ASSERTMSG( 

        "Expected create request", 
        wdfRequestParameters.Type == WdfRequestTypeCreate); 


    *DesiredAccessPtr = 
        wdfRequestParameters.Parameters.Create.SecurityContext->DesiredAccess; 

    *ShareAccessPtr = wdfRequestParameters.Parameters.Create.ShareAccess; 
} 

Независимость контроллера и закрепления

Контроллер и закрепление имеют связь "родители-потомки". Чтобы открыть контакт, необходимо сначала открыть его родительский контроллер. Другой способ — взглянуть на контакты как независимые сущности, где его родительский контроллер предоставляет только одну службу для контакта, которая задает глобальный период ШИМ для всех контактов, содержащихся на этом контроллере.

Ниже приведены некоторые примеры сценариев.

  • Однопроцессный доступ:

    • Процесс А может открыть контакт, задать его цикл работы, запустить его, используя период по умолчанию контроллера, а затем открыть контроллер и задать его период по требованию. Возможно, никогда не потребуется открывать контроллер, если период по умолчанию для приложения — ОК.

    • Процесс может иметь несколько потоков, каждый из которых управляет разными контактами под одним контроллером.

  • Многопроцессный доступ:

    • Служебная программа командной строки может открыть контроллер с доступом только для чтения для отображения информации в консоли. В то же время фоновая задача UWP может открыть контроллер для записи и управлять некоторыми светодиодными индикаторами с помощью одного контакта.

    • Драйвер дисплея в режиме ядра, который управляет подсветкой ЖК-дисплея через Pin0, удерживая контроллер для записи и блокировки периода ШИМ. В то же время служба Win32 использует период PWM, заданный драйвером дисплея, и использует Pin1 для тумнеть некоторые светодиодные индикаторы, чтобы сообщить пользователю о состоянии.

Обратите внимание, что открытие и закрытие контроллера независимо от его контактов имеет некоторые важные последствия. Дополнительные сведения см. в разделе конечных машин.

Контроллер и конечные машины закрепления

Определение состояния контроллера

Функция состояния Значение по умолчанию Описание
Is-opened-for-Write Неверно Значение False означает, что контроллер либо закрыт, либо открыт для чтения; Значение True указывает, что он открыт для записи.
Desired-Period MinimumPeriod

Конечный автомат контроллера.

Конечный автомат контроллера, приведенный ниже, сосредоточен только вокруг состояния Is-Opened-For-Write. Необходимое значение периода также не учитывается, так как оно не влияет на тип операции, которую можно выполнить на контроллере. Обратите внимание, что всякий раз, когда контроллер, открытый для записи, закрывается вызывающей стороны, открывшей его для записи, контроллер сбрасывается до значений по умолчанию (по умолчанию требуется период).

Определение состояния закрепления

Функция состояния Значение по умолчанию Описание
Is-opened-for-Write Неверно Значение False означает, что контакт закрыт или открыт для чтения; Значение True указывает, что он открыт для записи.
Активный — служебный цикл 0
Is-Started Неверно False указывает, что остановлено; Значение True указывает, что запущено.

Закрепляет конечный автомат.

Конечный автомат закреплений сосредоточен на сочетании двух состояний : Открыт для записи и Запущен. Другие состояния контактов, такие как полярность и активные циклы, не используются, так как их значения не влияют на тип операций, которые могут выполняться с контактом. Обратите внимание, что всякий раз, когда контакт, открытый для записи, закрывается вызывающим элементом, открывающим его для записи, контакт получает значение по умолчанию (остановлено, полярность по умолчанию и активный цикл работы). Также обратите внимание, что Set-Polarity переход на состояние с Is-Started = true не используется, так как в этом состоянии он недопустим.

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

Рекомендации по реализации переходов состояний

  • В EVT_WDF_DEVICE_FILE_CREATE драйвер должен предоставлять или запрещать доступ на основе требуемого доступа к запросу на создание и состояния контроллера или закрепления Is-Opened-For-Write следующим образом:

    Если у запроса есть требуемый доступ на запись и контроллер или пин-код уже открыт для записи, завершите запрос с помощью STATUS_SHARING_VIOLATION, в противном случае пометьте контроллер/пин-код как открытый для записи (Is-Opened-For-Write = true), предоставьте доступ и продолжите обработку.

    В этом примере реализуются упомянутые ранее шаги проверки доступа для обработчика EVT_WDF_DEVICE_FILE_CREATE, где отсутствует необходимая логика блокировки для обработки параллельных запросов на создание файлов:

    //
    // Verify request desired access
    //
    
    const bool hasWriteAccess = desiredAccess & FILE_WRITE_DATA;
    
    if (isPinInterface) {
        PWM_PIN_STATE* pinPtr = deviceContextPtr->Pins + pinNumber;
        if (hasWriteAccess) {
            if (pinPtr->IsOpenForReadWrite) {
                PWM_LOG_TRACE("Pin%lu access denied.", pinNumber);
                WdfRequestComplete(WdfRequest, STATUS_SHARING_VIOLATION);
                return;
            }
            pinPtr->IsOpenForReadWrite = true;
        }
        PWM_LOG_TRACE(
            "Pin%lu Opened. (IsOpenForReadWrite = %lu)",
            pinNumber,
            (pinPtr->IsOpenForReadWrite ? 1 : 0));
    
    } else {
        if (hasWriteAccess) {
            if (deviceContextPtr->IsControllerOpenForReadWrite) {
                PWM_LOG_TRACE("Controller access denied.");
                WdfRequestComplete(WdfRequest, STATUS_SHARING_VIOLATION);
                return;
            }
            deviceContextPtr->IsControllerOpenForReadWrite = true;
        }
        PWM_LOG_TRACE(
            "Controller Opened. (IsControllerOpenForReadWrite = %lu)",
            (deviceContextPtr->IsControllerOpenForReadWrite ? 1 : 0));
    }
    
    //
    // Allocate and fill a file object context
    //
    IMXPWM_FILE_OBJECT_CONTEXT* fileObjectContextPtr;
    {
        WDF_OBJECT_ATTRIBUTES wdfObjectAttributes;
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
            &wdfObjectAttributes,
            IMXPWM_FILE_OBJECT_CONTEXT);
    
        void* contextPtr;
        NTSTATUS status = WdfObjectAllocateContext(
                WdfFileObject,
                &wdfObjectAttributes,
                &contextPtr);
        if (!NT_SUCCESS(status)) {
            IMXPWM_LOG_ERROR(
                "WdfObjectAllocateContext(...) failed. (status = %!STATUS!)",
                status);
            WdfRequestComplete(WdfRequest, status);
            return;
        }
    
        fileObjectContextPtr =
            static_cast<IMXPWM_FILE_OBJECT_CONTEXT*>(contextPtr);
    
        NT_ASSERT(fileObjectContextPtr != nullptr);
        fileObjectContextPtr->IsPinInterface = isPinInterface;
        fileObjectContextPtr->IsOpenForWrite = hasWriteAccess;
        fileObjectContextPtr->PinNumber = pinNumber;
    }
    
  • В EVT_WDF_FILE_CLOSE/ EVT_WDF_FILE_CLEANUP драйвер должен поддерживать целостность состояния контроллера или контакта.

    Если объект файла принадлежит контроллеру или контакту, открывшему этот контроллер или пин-код для записи, то сбросьте контроллер или закрепление в состояние по умолчанию и снимите метку с открытого для записи (Is-Opened-For-Write = false).

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

    EVT_WDF_DEVICE_FILE_CLOSE PwmEvtFileClose;
    
    VOID
    PwmEvtFileClose (
        WDFFILEOBJECT WdfFileObject
        )
    {
        WDFDEVICE wdfDevice = WdfFileObjectGetDevice(WdfFileObject);
        PWM_DEVICE_CONTEXT* deviceContextPtr = PwmGetDeviceContext(wdfDevice);
        PWM_FILE_OBJECT_CONTEXT* fileObjectContextPtr = PwmGetFileObjectContext(WdfFileObject);
    
        if (fileObjectContextPtr->IsPinInterface) {
            if (fileObjectContextPtr->IsOpenForReadWrite) {
                const ULONG pinNumber = fileObjectContextPtr->PinNumber;
    
                NTSTATUS status = PwmResetPinDefaults(deviceContextPtr, pinNumber);
                if (!NT_SUCCESS(status)) {
                    PWM_LOG_ERROR(
                        "PwmResetPinDefaults(...) failed. "
                        "(pinNumber = %lu, status = %!STATUS!)",
                        pinNumber,
                        status);
                    //
                    // HW Error Recovery
                    //
                }
    
                NT_ASSERT(deviceContextPtr->Pins[pinNumber].IsOpenForReadWrite);
                deviceContextPtr->Pins[pinNumber].IsOpenForReadWrite = false;
            }
    
            PWM_LOG_TRACE("Pin%lu Closed.", fileObjectContextPtr->PinNumber);
    
        } else {
            if (fileObjectContextPtr->IsOpenForReadWrite) {
                NTSTATUS status = PwmResetControllerDefaults(deviceContextPtr);
                if (!NT_SUCCESS(status)) {
                    IMXPWM_LOG_ERROR(
                        "PwmResetControllerDefaults(...) failed. (status = %!STATUS!)",
                        status);
                    //
                    // HW Error Recovery
                    //  
                }
    
                NT_ASSERT(deviceContextPtr->IsControllerOpenForReadWrite);
                deviceContextPtr->IsControllerOpenForReadWrite = false;
            }
    
            PWM_LOG_TRACE("Controller Closed.");
        }
    }
    

Запросы IOCTL PWM

Запросы IOCTL PWM отправляются приложением или другим драйвером и предназначены для контроллера или определенного пин-кода.

IoCTL контроллера

Закрепление IOCTL

Для каждого запроса IOCTL драйвер PWM должен проверить следующее:

  1. Запрошенная операция (код IOCTL) допустима для объекта файла, связанного с запросом.

  2. Запросите входные и выходные буферы и убедитесь, что они имеют по крайней мере минимальный ожидаемый размер.

  3. Допустимость запрошенной операции в текущем состоянии контроллера или контакта.

  4. Допустимость отдельных входных параметров. Например, нужный нулевой период является недопустимым параметром для IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD.

Коды состояния завершения IOCTL

Драйвер PWM должен завершить запрос IOCTL с соответствующим кодом состояния. Ниже приведены распространенные коды состояния завершения. Как правило, IOCTL, устанавливающий свойство со значением, которое уже задано, всегда должно выполняться успешно. Например, задание того же периода, который уже задан, остановка уже остановленного контакта, установка уже установленной полярности и т. д.

STATUS_NOT_SUPPORTED

Запрошенная операция IOCTL не реализована и не поддерживается. Например, некоторые контроллеры могут не поддерживать настройку полярности выходного сигнала. В этом случае IOCTL_PWM_PIN_SET_POLARITY следует реализовать, но сбой с STATUS_NOT_SUPPORTED для полярности, отличной от полярности по умолчанию.

STATUS_INVALID_DEVICE_REQUEST

Запрос IOCTL был отправлен неправильному целевому объекту. Например, запрос IOCTL контроллера был отправлен с помощью дескриптора пин-файла.

STATUS_BUFFER_TOO_SMALL

Размер входного или выходного буфера меньше минимального размера буфера, необходимого для обработки запроса. Драйвер WDF, использующий WdfRequestRetrieveInputBuffer или WdfRequestRetrieveOutputBuffer для получения и проверки входных и выходных буферов, может возвращать соответствующее состояние ошибки как есть. Все ioCTL с определенными входными и /или выходными буферами имеют соответствующую структуру, описывающую этот буфер, где имена входных и выходных структур имеют постфикс INPUT и _OUTPUT соответственно. Минимальный размер входного буфера равен sizeof (ШИМINPUT), а минимальный размер выходного буфера — sizeof(PWM_OUTPUT).

Код IOCTL Описание
IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD Извлекает период эффективного выходного сигнала контроллера импульсной ширины модуляции (PWM) по мере его измерения в его выходных каналах. Возвращает значение PWM_CONTROLLER_GET_ACTUAL_PERIOD_OUTPUT. Irp-IoStatus.Status> имеет одно из значений в следующем списке.
  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_GET_INFO Извлекает сведения о контроллере импульсной ширины модуляции (PWM). Эти сведения не изменяются после инициализации контроллера.

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

Если размер буфера меньше размера самой низкой версии структуры, запрос выполняется с помощью состояния завершения IOCTL STATUS_BUFFER_TOO_SMALL. В противном случае драйвер предполагает максимальную версию структуры, которая может поместиться в предоставленный выходной буфер, и успешно завершает запрос.

Более новая версия PWM_CONTROLLER_INFO имеет размер байтов, превышающий размер предыдущей версии.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.
  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD Задает для периода выходного сигнала контроллера широтной модуляции импульса (PWM) предлагаемое значение.

Контроллер PWM пытается установить период, который максимально близок к запрошенным значениям на основе его возможностей. Действующий период возвращается в виде выходных данных IOCTL. Позже его можно получить с помощью IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD.

Требуемый период должен быть больше нуля (0) и находиться в диапазоне периодов, поддерживаемых контроллером. То есть он должен находиться в диапазоне MinimumPeriod и MaximumPeriod включительно, которые можно получить с помощью IOCTL_PWM_CONTROLLER_GET_INFO.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.
  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_INVALID_PARAMETER
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE Извлекает процент текущего цикла выполнения для контакта или канала. Код элемента управления возвращает процент в виде структуры PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE_OUTPUT.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE Задайте требуемое значение в процентах от служебного цикла для пин-кода контроллера или канала. Код элемента управления указывает процент в виде структуры PWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE_INPUT.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_POLARITY Извлекает текущую полярность сигнала контакта или канала. Код элемента управления получает полярность сигнала в виде структуры PWM_PIN_GET_POLARITY_OUTPUT. Полярность сигнала — "Активный высокий" или "Активный низкий", как определено в перечислении PWM_POLARITY.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_POLARITY Задает полярность сигнала контакта или канала. Код элемента управления задает полярность сигнала на основе структуры PWM_PIN_SET_POLARITY_INPUT. Полярность сигнала имеет значение "Активный высокий" или "Активный низкий", как определено в перечислении PWM_POLARITY.

Изменение полярности допускается только в том случае, если закрепление остановлено. Вы можете определить, остановлен ли закрепление, с помощью кода элемента управления IOCTL_PWM_PIN_IS_STARTED. Если закрепление остановлено, а запрошенная полярность отличается от полярности текущего контакта, запрос завершается со значением STATUS_INVALID_DEVICE_STATE.

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

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_INVALID_PARAMETER
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_START Запускает создание сигнала импульсной ширины модуляции (PWM) на контакте или канале. Чтобы проверка, запускается ли закрепление, используйте IOCTL_PWM_PIN_IS_STARTED.

Выдача этого IOCTL в уже запущенном контакте или канале не оказывает никакого влияния, но выполняется успешно.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

>
  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_STOP Останавливает создание сигнала импульсной ширины модуляции (PWM) на контакте или канале. Чтобы проверка, запускается ли закрепление, используйте IOCTL_PWM_PIN_IS_STARTED.

Выдача этого IOCTL в контакте или канале, который уже остановлен, не оказывает никакого влияния, но успешно.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_IS_STARTED Извлекает состояние создания сигнала для контакта или канала. Каждое закрепление имеет состояние "Запущено" или "Остановлено" как PWM_PIN_IS_STARTED_OUTPUT структуры. Запущенное состояние имеет логическое значение true. Остановленное состояние — false.

По умолчанию закрепление останавливается при открытии и возвращается в остановленное состояние при закрытии или освобождении.

Irp-IoStatus.Status> имеет одно из значений в следующем списке.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL