Driver PWM para um módulo PWM integrado ao SoC

Para fornecer acesso a um controlador PWM (modulação de largura de pulso) que faz parte do SoC e mapeado no espaço de endereço do SoC, você precisa escrever um driver no modo kernel. O driver deve registrar a interface de classe de dispositivo do controlador do PWM para que os aplicativos UWP possam acessar os dispositivos PWM expostos pelo sistema por meio das APIs PWM WinRT definidas no namespace Windows.Devices.Pwm.

Observação

Se você tiver um módulo PWM adicional sobre I2C, SPI ou um controlador UART, poderá acessar o módulo de um aplicativo UWP usando as APIs definidas no namespace Windows.Devices.Pwm e Windows.Devices.Pwm.Provider.

Um dispositivo PWM é abstraído em um único controlador e um ou mais pinos. O controle tanto do controlador quanto dos pinos é realizado por meio dos IOCTLs definidos através do PWM. Por exemplo, um driver de exibição LCD envia essas solicitações para o driver PWM para controlar o nível de backlight do LCD.

Geração de sinais PWM com controladores múltiplos e multicanais, com período configurável, polaridade e ciclo de trabalho, que possibilita as seguintes tarefas:

  • Acione um motor de servo.
  • Conduza uma carga como um LED ou motor de corrente contínua escovado.
  • Gere um sinal analógico.
  • Gerar um relógio preciso.

Este tópico descreve:

  • Como habilitar o acesso UWP aos dispositivos PWM expostos pelo sistema.

  • Como lidar com solicitações PWM IOCTL enviadas por um aplicativo Win32 ou um driver de modo kernel de terceiros.

Público-alvo pretendido

  • OEMs e IHVs desenvolvendo um sistema com um controlador PWM no SoC.

Última atualização

  • Agosto de 2017

versão do Windows

  • Versão do Windows 10

APIs importantes

Sobre o PWM

A PWM descreve a técnica básica para gerar uma onda de pulso retangular com largura de pulso modulada, resultando na variação do valor médio da forma de onda.

Uma forma de onda PWM pode ser categorizada por dois parâmetros: período de forma de onda (T) e ciclo de trabalho. A frequência de forma de onda (f) é a recíproca do período de forma de onda f=1/T. O ciclo de trabalho descreve a proporção do tempo 'ativo' em relação ao intervalo regular ou 'período' de tempo; um ciclo de trabalho baixo corresponde a uma potência de saída média baixa, porque a potência está inativa durante a maior parte do tempo. O ciclo de trabalho é expresso em porcentagem em que 100% estar totalmente ativado, 0% estar totalmente desativado, 50% sendo 'Ativo' 50% do tempo.

drivers.

Acessando o controlador PWM disponibilizado pelo sistema e os pinos.

O driver PWM deve se registrar
GUID_DEVINTERFACE_PWM_CONTROLLER como GUID da interface do dispositivo para expor e acessar dispositivos PWM.

// {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}" 

Para registrar o GUID da interface do dispositivo, o driver deve chamar o método WdfDeviceCreateDeviceInterface na implementação da função de retorno de chamada EVT_WDF_DRIVER_DEVICE_ADD do driver. Após o registro, o sistema atribui um link simbólico ao controlador e aos pinos.

Um aplicativo ou outro driver pode controlar tanto o controlador quanto os pinos por meio das IOCTL definidas pelo PWM. Antes de enviar os IOCTLs, o aplicativo remetente ou driver deve abrir um identificador de arquivo para o controlador e o pino, especificando o link simbólico correspondente. Isso exige que o driver se registre para eventos de criação e fechamento de arquivos. Consulte (link)

Aqui está um exemplo do caminho simbólico para o controlador:

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

Da mesma forma, o formato dos pinos é o seguinte: O formato do caminho é o seguinte:

<DeviceInterfaceSymbolicLinkName>\<PinNumber>

onde <PinNumber> está o índice baseado em 0 do pino a ser aberto.

Aqui está um exemplo do caminho simbólico para pinos:

\??\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.

Para abrir o identificador de arquivo, o aplicativo deve chamar as APIs do Configuration Manager (CM_Get_Device_Interface_*).

Depois que o identificador de arquivo for aberto, o aplicativo poderá enviar essas solicitações chamando a função DeviceIoControl. Consulte a seção PWM.

O driver deve usar a rotina de suporte PWM fornecida, PwmParsePinPath, para analisar e validar caminhos de pino e extrair o número do pino.

Definindo propriedades da interface do dispositivo

Para usar APIs WinRT PWM de aplicativos UWP, essas propriedades de interface do dispositivo devem ser definidas.

  • DEVPKEY_DeviceInterface_Restricted

    De acordo com o modelo de acesso ao dispositivo UWP atual, a configuração da propriedade de interface de dispositivo restrita como FALSE é necessária para conceder aos aplicativos UWP acesso à interface do dispositivo PWM.

  • DEVPKEY_DeviceInterface_SchematicName (link???)

    Atribuir um nome esquemático à interface de dispositivos PWM conectados estaticamente é necessário para usar o método de fábrica PwmController.GetDeviceSelector(FriendlyName). O nome do esquema é o nome dado ao dispositivo PWM nos esquemas de design do sistema, por exemplo, (PWM0, PWM_1, et..). Os nomes de esquema são considerados exclusivos em todo o sistema, mas isso não é imposto. Pelo menos, não deve haver dois dispositivos PWM com o mesmo nome esquemático, caso contrário, o comportamento do PwmController.GetDeviceSelector(FriendlyName) do WinRT será não-determinístico.

As propriedades podem ser definidas de duas maneiras:

  1. Usando o arquivo INF para o driver PWM

    Use a diretiva AddProperty para definir as propriedades do dispositivo. O arquivo INF deve permitir a configuração de valores diferentes para a mesma propriedade em um ou subconjunto das instâncias do dispositivo PWM. Aqui está um exemplo de configuração 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 
    

    Nem todos os designs têm a mesma política para expor o dispositivo PWM à UWP. Por exemplo, a política pode ser permitir que o UWP acesse um subconjunto das instâncias do dispositivo PWM. Expor um subconjunto dos dispositivos PWM ou atribuir um valor de propriedade diferente a uma ou um subconjunto das instâncias do dispositivo PWM requer ter uma ID de hardware diferente para cada instância de dispositivo PWM e fazer a correspondência seletiva de seções INF com base na política.

    Considere um design baseado em SoC em que há quatro instâncias de dispositivo PWM idênticas (Blocos DE IP) denominadas PWM0,...,PWM3, em que a ID de Hardware atribuída à ACPI (_HID) é FSCL00E0 e sua ID Exclusiva (_UID) é 0,...,3. Expor todos os dispositivos PWM para UWP exigirá as seções INF que definem o DEVPKEY_DeviceInterface_Restricted para encaixar com o ID de Hardware ACPI\FSCL00E0.

    Essa maneira de definir propriedades não requer nenhuma alteração no código do driver. Essa é uma opção mais fácil, pois a manutenção de um arquivo INF é mais fácil do que um binário de driver. A compensação é que essa abordagem requer um arquivo INF personalizado para cada design.

  2. Usando programação no driver PWM

    Um driver PWM pode chamar IoSetDeviceInterfacePropertyData para definir as propriedades da interface do dispositivo em sua implementação de EVT_WDF_DRIVER_DEVICE_ADD, após criar e publicar a interface do dispositivo PWM. O driver é responsável por decidir o valor a ser atribuído e a propriedade do dispositivo. Essas informações geralmente são armazenadas no ACPI do sistema para designs baseados em SoC. O valor de cada propriedade da interface do dispositivo pode ser especificado no método _DSD do nó de dispositivo ACPI como Propriedades do Dispositivo. O driver deve consultar the_DSD do ACPI, analisar os dados das Propriedades do Dispositivo, extrair o valor de cada propriedade e atribuí-lo à interface do dispositivo.

    Definir programaticamente as propriedades torna o driver e seu arquivo INF portáteis entre designs e, portanto, BSPs em que a única alteração seria no DSDT ACPI que define cada nó de dispositivo PWM. No entanto, ler e analisar blocos binários ACPI é um entediante e requer muito código que pode ser propenso a erros e vulnerabilidades, resultando em uma superfície de erro maior.

Manipulando eventos de abertura/fechamento de arquivo

Um driver PWM precisa se registrar para criar e fechar eventos de arquivo implementando funções de retorno de chamada EVT_WDF_DEVICE_FILE_CREATE e EVT_WDF_FILE_CLEANUP/EVT_WDF_FILE_CLOSE. Na implementação, o driver deve executar estas tarefas:

  1. Determine se a solicitação de criação é para um controlador ou um pino.

  2. Se a solicitação for para um pin, o driver deverá analisar e validar o caminho do pino para criar solicitações de pin e verificar se o número de pino solicitado está dentro dos limites do controlador.

  3. Conceda ou negue o acesso ao controlador e fixe solicitações de criação.

  4. Manter a integridade dos controladores e pinos por uma máquina de estados bem definida.

No conjunto anterior de tarefas, a segunda tarefa de validação pode ser executada em EVT_WDF_DEVICE_FILE_CREATE, da seguinte maneira:

  1. Se o nome do arquivo associado ao objeto de arquivo de solicitação for nulo, conclua a solicitação com STATUS_INVALID_DEVICE_REQUEST.

  2. Se o nome do arquivo associado ao objeto de arquivo de solicitação for uma cadeia de caracteres vazia, essa será uma solicitação de criação do controlador, caso contrário, será uma solicitação de criação de pin.

  3. Se essa for uma solicitação de criação de pin, então:

    1. Analise o caminho do pino com base no formato <DecimalName> e extraia o número do pino chamando PwmParsePinPath.

    2. Se a análise e a validação do caminho do pino falharem, conclua a solicitação com STATUS_NO_SUCH_FILE.

    3. Se o número de pinos for maior ou igual à contagem de pinos do controlador, conclua a solicitação com STATUS_NO_SUCH_FILE. Observe que o número de pino é um índice baseado em zero.

    4. Caso contrário, continue processando EVT_WDF_DEVICE_FILE_CREATE.

Aqui está um código de exemplo que implementa as etapas de validação mencionadas anteriormente para um manipulador de 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 
    // 
} 

Controlador e compartilhamento de pinos

O modelo de compartilhamento para controlador e pinos segue o padrão de múltiplos leitores, um único gravador. Um controlador/pin pode ser aberto para leitura por vários chamadores, mas apenas um chamador pode abri-lo para escrita por vez.

Esse modelo pode ser implementado usando uma combinação de sinalizadores de Acesso Desejado e Compartilhamento de Acesso ao abrir um identificador de arquivo. O modelo de compartilhamento de DDI opta por uma semântica de compartilhamento mais simples, em que apenas a especificação de Acesso Desejado é usada para controlar o acesso. A especificação do Share Access não desempenha nenhuma função no modelo de compartilhamento e não será obedecida, caso especificada ao abrir um controlador ou um pino.

Em EVT_WDF_DEVICE_FILE_CREATE, a solicitação create Desired Access e Share Access deve ser extraída e validada com base no estado do controlador/pin ou da seguinte maneira:

  1. Se o Acesso Compartilhado não for 0, negue o acesso e conclua a solicitação com STATUS_SHARING_VIOLATION.

  2. Se o Acesso Desejado for somente leitura, conceda acesso e continue processando EVT_WDF_DEVICE_FILE_CREATE.

  3. Se o Acesso Desejado for para gravação, então:

Se o controlador/pino já estiver aberto para gravação, negue o acesso e conclua a solicitação com STATUS_SHARING_VIOLATION, caso contrário, conceda acesso, marque o controlador ou pino como aberto para gravação e continue processando EVT_WDF_DEVICE_FILE_CREATE.

Aqui está um exemplo que mostra como extrair Acesso Desejado e Compartilhar Acesso de uma solicitação de criação:

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; 
} 

Independência de controlador e fixação

Controlador e pin têm relação pai-filho. Para abrir um pino, você deve primeiro abrir seu controlador principal. Outra maneira é examinar os pinos como entidades independentes em que seu controlador pai fornece apenas um serviço para o pino que está definindo o período PWM global para todos os pinos contidos nesse controlador.

Veja aqui alguns cenários de exemplo:

  • Acesso de processo único:

    • O processo A pode abrir o pino, definir seu ciclo de trabalho, iniciá-lo usando o período padrão do controlador e, posteriormente, abrir o controlador e definir seu período conforme a necessidade. Talvez não seja necessário abrir o controlador se o período padrão for adequado para a aplicação.

    • Um processo pode ter vários threads onde cada um controla um pino diferente no mesmo controlador.

  • Acesso a vários processos:

    • Um utilitário de linha de comando pode abrir o controlador com acesso somente leitura para mostrar informações no console. Ao mesmo tempo, uma tarefa UWP em segundo plano pode abrir o controlador para gravação e controlar um LED com um único pino.

    • Um driver de exibição no modo kernel que controla o backlight do LCD por meio do Pin0, mantendo o controlador para gravação e bloqueio do período PWM. Ao mesmo tempo, um serviço Win32 usa o período PWM definido pelo driver de exibição e está usando Pin1 para diminuir algum LED para comunicar algum status ao usuário.

Observe que há algumas implicações importantes para abrir e fechar um controlador independentemente de seus pinos. Para obter mais informações, consulte a seção de computadores de estado.

Controlador e máquinas de estado de pino

Definição de estado do controlador

Recurso de estado Valor padrão Descrição
Is-Opened-For-Write Falso False indica que o controlador está fechado ou aberto para leitura; True indica que ele está aberto para gravação.
Desired-Period PeríodoMínimo

Computador de estado do controlador.

A máquina de estados do controlador abaixo é centralizada apenas no estado Is-Opened-For-Write. O valor de período desejado também é deixado de fora porque não afeta o tipo de operação que pode ser feita no controlador. Observe que sempre que um controlador aberto para gravação é fechado pelo chamador que o abriu para gravação, o controlador é redefinido para seus padrões (período padrão desejado).

Definição de estado de fixação

Recurso de estado Valor padrão Descrição
Is-Opened-For-Write Falso False indica que o pino está fechado ou está aberto para leitura; True indica que ele está aberto para gravação.
Ativo-Duty-Cycle 0
Is-Started Falso False indica parado; True indica iniciado.

Máquina de estados dos pinos.

O computador de estado do pino é centralizado em torno da combinação dos dois estados Is-Opened-For-Write e Is-Started. Outros estados de pino, como polaridade e ciclos ativos, são deixados de fora porque seus valores não afetam o tipo de operações que podem ser executadas no pino. Observe que sempre que um pino aberto para gravação for fechado pelo chamador que o abriu para gravação, o pino é redefinido para seus padrões (parado, polaridade padrão e ciclo de trabalho ativo). Observe também que a transição de Set-Polarity em um estado em que Is-Started = true é omitida porque é inválida nesse estado.

Qualquer transição que não seja mencionada para um determinado estado implica que essa transição é inválida ou não é possível e a solicitação correspondente deve ser concluída com o status de erro apropriado.

Considerações de implementação para transições de estado

  • Em EVT_WDF_DEVICE_FILE_CREATE, o driver deve conceder ou negar acesso com base no acesso desejado especificado na solicitação de criação e no estado Is-Opened-For-Write do controlador ou do pin, conforme segue:

    Se a solicitação tiver acesso de gravação desejado e o controlador/pino já estiver aberto para gravação, conclua a solicitação com STATUS_SHARING_VIOLATION, caso contrário, marque o controlador/pino como aberto para gravação (Is-Opened-For-Write = true), conceda acesso e continue o processamento.

    Este exemplo implementa as etapas de validação de acesso mencionadas anteriormente para um manipulador de EVT_WDF_DEVICE_FILE_CREATE em que a lógica de bloqueio necessária para lidar com solicitações de criação de arquivo simultâneas é omitida:

    //
    // 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;
    }
    
  • Em EVT_WDF_FILE_CLOSE/EVT_WDF_FILE_CLEANUP, o driver deve manter a integridade do estado do controlador/pino.

    Se o objeto de arquivo pertencer a um controlador/pino que abriu esse controlador/pino para gravação, redefina o controlador/pino para o estado padrão e desmarca esse controlador/pin de ser aberto para gravação (Is-Opened-For-Write = false).

    Este exemplo implementa as etapas de validação de acesso mencionadas anteriormente para um manipulador de EVT_WDF_DEVICE_FILE_CLOSE em que a lógica de bloqueio necessária para lidar com solicitações de fechamento de arquivo simultâneas é omitida.

    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.");
        }
    }
    

Solicitações de IOCTL do PWM

As solicitações de PWM IOCTL são enviadas por um aplicativo ou outro driver e são direcionadas para um controlador ou um pino específico.

IOCTLs do controlador

Pin IOCTLs

Para cada solicitação IOCTL, o driver PWM deve verificar o seguinte:

  1. A operação solicitada (código IOCTL) é válida para o objeto de arquivo associado à solicitação.

  2. Solicite buffers de entrada e saída e verifique se eles são pelo menos do tamanho mínimo esperado.

  3. A validade da operação solicitada no estado atual do controlador/pin.

  4. A validade dos parâmetros de entrada individuais. Por exemplo: um período desejado igual a zero é um parâmetro inválido para IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD.

Códigos de status de conclusão IOCTL

O driver PWM deve concluir a solicitação IOCTL com o código de status apropriado. Aqui estão os códigos de status de conclusão comuns. Em geral, um IOCTL que define uma propriedade com valor que já está definido deve sempre ter êxito. Por exemplo, definir exatamente o mesmo período que já está definido, parar um pino que já está parado, definir a polaridade que já está definida e assim por diante.

STATUS_NOT_SUPPORTED

A operação IOCTL solicitada não é implementada nem suportada. Por exemplo, alguns controladores podem não dar suporte à configuração da polaridade do sinal de saída; em tal caso, IOCTL_PWM_PIN_SET_POLARITY deve ser implementado, mas falhar com STATUS_NOT_SUPPORTED para a polaridade que não seja a padrão.

STATUS_INVALID_DEVICE_REQUEST

A solicitação IOCTL foi enviada para o destino errado. Por exemplo, uma solicitação IOCTL do controlador foi enviada usando um identificador de arquivo 'pin'.

STATUS_BUFFER_TOO_SMALL

O tamanho do buffer de entrada ou saída é menor que o tamanho mínimo necessário do buffer para processar a solicitação. Um driver WDF que usa WdfRequestRetrieveInputBuffer ou WdfRequestRetrieveOutputBuffer para recuperar e validar os buffers de entrada e saída pode retornar o status de erro correspondente como está. Todos os IOCTLs com buffers de entrada e/ou saída definidos para eles têm um struct correspondente que descreve esse buffer, em que os nomes do struct de entrada e saída têm INPUT e _OUTPUT postfix, respectivamente. O tamanho mínimo do buffer de entrada é sizeof (PWMINPUT), enquanto o tamanho mínimo do buffer de saída é sizeof(PWM_OUTPUT).

Código IOCTL Descrição
IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD Recupera o período de sinal de saída efetivo do controlador PWM (Pulse Width Modulation), pois ele seria medido em seus canais de saída. Retorna o valor PWM_CONTROLLER_GET_ACTUAL_PERIOD_OUTPUT. Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.
  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_GET_INFO (Obter informações do controlador PWM) Recupera informações sobre um controlador PWM (Pulse Width Modulation). Essas informações não são alteradas depois que o controlador é inicializado.

O chamador deve passar um buffer de saída que tenha exatamente o tamanho do struct PWM_CONTROLLER_INFO. O driver infere a versão da estrutura a partir do tamanho do buffer de saída da solicitação.

Se o tamanho do buffer for menor que o tamanho da versão de estrutura mais baixa, a solicitação será concluída usando um status de conclusão IOCTL de STATUS_BUFFER_TOO_SMALL. Caso contrário, o driver assume a versão de estrutura mais alta que pode caber no buffer de saída fornecido e conclui a solicitação com êxito.

Uma versão de PWM_CONTROLLER_INFO mais recente tem um tamanho de byte maior que o da versão anterior

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.
  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD Define o período de sinal de saída de um controlador PWM (Pulse Width Modulation) como um valor sugerido.

O controlador PWM tenta definir o período o mais próximo possível do valor solicitado com base em seus recursos. O período efetivo é retornado como uma saída IOCTL. Posteriormente, ele pode ser recuperado usando IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD.

O período desejado deve ser maior que zero (0) e no intervalo de períodos com suporte do controlador. Ou seja, ele deve estar no intervalo de MinimumPeriod e MaximumPeriod, inclusive, que podem ser recuperados usando IOCTL_PWM_CONTROLLER_GET_INFO.

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.
  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_INVALID_PARAMETER
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE Recupera a porcentagem atual do ciclo de direitos para um pino ou canal. O código de controle retorna a porcentagem na estrutura PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE_OUTPUT.

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE (Configurar porcentagem de ciclo ativo de duty do pino PWM) Defina um valor de porcentagem de ciclo de dever desejado para o pino ou canal do controlador. O código de controle especifica a porcentagem como uma estrutura de PWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE_INPUT.

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_POLARITY Recupera a polaridade do sinal atual do pino ou canal. O código de controle obtém a polaridade do sinal como uma estrutura PWM_PIN_GET_POLARITY_OUTPUT. A polaridade do sinal é Ativo Alto ou Ativo Baixo, como definido na enumeração PWM_POLARITY.

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_POLARITY Define a polaridade do sinal do pino ou canal. O código de controle define a polaridade do sinal com base em uma estrutura de PWM_PIN_SET_POLARITY_INPUT. A polaridade do sinal é Ativo Alto ou Ativo Baixo, conforme definido na enumeração PWM_POLARITY.

A alteração da polaridade somente é permitida quando o pino está parado. Você pode verificar se o pino está parado usando o código de controle IOCTL_PWM_PIN_IS_STARTED. Se o pino estiver desativado e a polaridade solicitada diferir da polaridade atual do pino, a solicitação será concluída com um valor de STATUS_INVALID_DEVICE_STATE.

A alteração da polaridade durante o funcionamento de um pino pode levar a falhas em alguns controladores PWM (Pulse Width Modulation). Se você quiser alterar a polaridade, interrompa primeiramente o pino, altere a polaridade e em seguida inicie o pino novamente.

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_INVALID_PARAMETER
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_START Inicia a geração do sinal PWM (Pulse Width Modulation) em um pino ou canal. Para verificar se um pino está iniciado, use IOCTL_PWM_PIN_IS_STARTED.

A emissão desse IOCTL em um pin ou canal que já foi iniciado não tem efeito, mas é bem-sucedida.

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.

>
  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_STOP Interrompe a geração de sinal PWM (Pulse Width Modulation) em um pino ou canal. Para verificar se um pino está iniciado, use IOCTL_PWM_PIN_IS_STARTED.

A emissão desse IOCTL em um pino ou canal que já está parado não tem efeito, mas ainda assim é bem-sucedida.

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_IS_STARTED Recupera o estado da geração de sinal para um pino ou canal. Cada pino tem um estado de ativado ou parado como uma estrutura PWM_PIN_IS_STARTED_OUTPUT. O estado iniciado tem um valor booliano verdadeiro. O estado parado é falso.

Por padrão, um pino é interrompido quando aberto e retorna ao estado parado quando fechado ou liberado.

Irp-IoStatus.Status> é definido como um dos valores na lista a seguir.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL