Driver PWM per un modulo PWM on-SoC

Per fornire l'accesso a un controller PWM (Pulse Width Modulation) che fa parte del SoC e collegato alla memoria mappata nello spazio indirizzi del SoC, è necessario scrivere un driver in modalità kernel. Il driver deve registrare l'interfaccia della classe di dispositivo del controller PWM in modo che le app UWP possano accedere al sistema esposto ai dispositivi PWM tramite le API WinRT PWM definite nello spazio dei nomi Windows.Devices.Pwm.

Annotazioni

Se hai un modulo PWM aggiuntivo su I2C, SPI o un controller UART, puoi accedere al modulo da un'app UWP usando le API definite nello spazio dei nomi Windows.Devices.Pwm e Windows.Devices.Pwm.Provider .

Un dispositivo PWM viene astratto in un singolo controller e uno o più pin. Il controllo del controller e dei pin viene effettuato tramite gli IOCTL definiti dal PWM. Ad esempio, un driver di visualizzazione LCD invia tali richieste al driver PWM per controllare il livello di backlight LCD.

Generazione di segnali PWM multi-controller e multicanale con periodo configurabile, polarità e ciclo di attività che consente le attività seguenti:

  • Guidare un motore servo.
  • Pilotare un carico come LED o motore a spazzole DC.
  • Generare un segnale analogico.
  • Generare un orologio preciso.

Questo argomento descrive,

  • Come abilitare l'accesso UWP ai dispositivi PWM esposti dal sistema.

  • Come gestire le richieste IOCTL di PWM inviate da un'applicazione Win32 o da un driver in modalità kernel di terze parti.

Destinatari

  • OEM e IHV che sviluppano un sistema con un controller PWM on-SoC.

Ultimo aggiornamento

  • Agosto 2017

versione di Windows

  • Versione di Windows 10

API Importanti

Informazioni su PWM

PWM descrive la tecnica di base per generare un'onda d'impulso rettangolare con larghezza degli impulsi modulati con conseguente variazione del valore medio della forma d'onda.

Una forma d'onda PWM può essere categorizzata in base a 2 parametri: periodo di forma d'onda (T) e ciclo di attività. La frequenza d'onda (f) è il reciproco del periodo della forma d'onda f=1/T. Il ciclo di lavoro descrive la proporzione del tempo "on" o "Attivo" rispetto all'intervallo regolare o al periodo di tempo; un ciclo basso corrisponde a una media di potenza a basso output, perché l'alimentazione è spenta per la maggior parte del tempo. Il ciclo di lavoro è espresso in percentuale: 100% significa che è completamente acceso, 0% che è completamente spento, mentre 50% indica che è "Attivo" per il 50% del tempo.

Driver.

Accesso al controller PWM esposto dal sistema e ai pin

Il driver PWM è tenuto a registrare
GUID_DEVINTERFACE_PWM_CONTROLLER come GUID dell'interfaccia del dispositivo per esporre e accedere ai dispositivi 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}" 

Per registrare il GUID dell'interfaccia del dispositivo, il driver deve chiamare WdfDeviceCreateDeviceInterface nella funzione di callback EVT_WDF_DRIVER_DEVICE_ADD implementata dal driver. Dopo la registrazione, il sistema assegna un collegamento simbolico al controller e ai pin.

Un'applicazione o un altro driver può controllare il controller o i pin tramite gli IOCTL definiti per PWM. Prima di inviare gli IOCTL, le applicazioni o i driver del mittente devono aprire un handle di file al controller e al pin specificando il loro collegamento simbolico. È quindi necessario che il driver esecriva gli eventi di creazione e chiusura dei file. Visualizza (link)

Di seguito è riportato un esempio del percorso simbolico per il controller:

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

Analogamente, il formato dei pin è il seguente: il formato del percorso è il seguente:

<DeviceInterfaceSymbolicLinkName>\<PinNumber>

dove <PinNumber> è l'indice in base 0 del pin da aprire.

Di seguito è riportato un esempio del percorso simbolico per i pin:

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

Per aprire l'handle di file, l'applicazione deve chiamare le API di Configuration Manager (CM_Get_Device_Interface_*).

Dopo l'apertura dell'handle di file, l'applicazione può inviare queste richieste chiamando la funzione DeviceIoControl. Vedere la sezione PWM.

Il driver deve usare la routine di supporto di PWM fornita PwmParsePinPath per analizzare e convalidare i percorsi dei pin ed estrarre il numero di pin.

Impostazione delle proprietà dell'interfaccia del dispositivo

Per usare le API WinRT PWM dalle app UWP, è necessario impostare queste proprietà dell'interfaccia del dispositivo .

  • DEVPKEY_DeviceInterface_Restricted

    In base al modello di accesso al dispositivo UWP corrente, l'impostazione della proprietà Interfaccia dispositivo con restrizioni su FALSE è necessaria per concedere alle app UWP l'accesso all'interfaccia del dispositivo PWM.

  • DEVPKEY_DeviceInterface_SchematicName (collegamento???)

    L'assegnazione di un nome schematico all'interfaccia del dispositivo PWM dei dispositivi PWM connessi staticamente è necessaria per utilizzare il metodo di fabbrica PwmController.GetDeviceSelector(FriendlyName). Il nome dello schema è il nome assegnato al dispositivo PWM negli schemi di progettazione del sistema, ad esempio (PWM0, PWM_1, et.).). Si presuppone che i nomi degli schemi siano univoci nel sistema, ma tale univocità non è applicata. Almeno, non dovrebbero esserci 2 dispositivi PWM con lo stesso nome schema, altrimenti il comportamento PwmController.GetDeviceSelector(FriendlyName) di WinRT PWM sarà non deterministico.

Le proprietà possono essere impostate in uno dei due modi seguenti:

  1. Uso del file INF per il driver PWM

    Usare la direttiva AddProperty per impostare le proprietà del dispositivo. Il file INF deve consentire l'impostazione di valori diversi per la stessa proprietà in uno o sottoinsieme delle istanze del dispositivo PWM. Ecco un esempio di impostazione 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 
    

    Non tutti i progetti hanno gli stessi criteri per esporre il dispositivo PWM alla piattaforma UWP. Ad esempio, il criterio potrebbe essere quello di consentire l'accesso UWP a un subset delle istanze del dispositivo PWM. L'esposizione di un subset dei dispositivi PWM o l'assegnazione di un valore di proprietà diverso a uno o a un subset delle istanze del dispositivo PWM richiede un ID hardware diverso per ogni istanza del dispositivo PWM e associare in modo selettivo le sezioni INF in base ai criteri.

    Si consideri una progettazione basata su SoC in cui sono presenti quattro istanze di dispositivo PWM identiche denominate PWM0,...,PWM3, in cui l'ID hardware (_HID) ACPI assegnato è FSCL00E0 e il relativo ID univoco (_UID) è 0,...,3. L'esposizione di tutti i dispositivi PWM a UWP richiederà che le sezioni INF impostino il DEVPKEY_DeviceInterface_Restricted in modo da corrispondere all'ID hardware ACPI\FSCL00E0.

    In questo modo, l'impostazione delle proprietà non richiede alcuna modifica nel codice del driver. Si tratta di un'opzione più semplice perché la manutenzione di un file INF è più semplice rispetto a un file binario del driver. Il compromesso è che questo approccio richiede un file INF personalizzato per ogni progettazione.

  2. A livello di codice nel driver PWM

    Un driver PWM può chiamare IoSetDeviceInterfacePropertyData per impostare le proprietà dell'interfaccia del dispositivo nell'implementazione EVT_WDF_DRIVER_DEVICE_ADD dopo la creazione e la pubblicazione dell'interfaccia del dispositivo PWM. Il driver è responsabile della decisione del valore da assegnare e della proprietà del dispositivo. Tali informazioni vengono in genere archiviate nel sistema ACPI per le progettazioni basate su SoC. Il valore di ciascuna proprietà dell'interfaccia dispositivo può essere specificato nel metodo _DSD di ogni nodo del dispositivo ACPI come proprietà del dispositivo. Il driver deve eseguire una query the_DSD da ACPI, analizzare i dati delle proprietà del dispositivo, estrarre il valore di ogni proprietà e assegnarlo all'interfaccia del dispositivo.

    L'impostazione delle proprietà a livello di codice rende il driver e il relativo file INF portabili tra le progettazioni e di conseguenza i BSP in cui l'unica modifica si troverebbe nell'ACPI DSDT definendo ogni nodo del dispositivo PWM. Tuttavia, la lettura e l'analisi di blocchi binari ACPI è noioso e richiede un sacco di codice che può essere soggetto a errori e vulnerabilità che causano una superficie di errore più grande.

Gestione degli eventi di apertura/chiusura dei file

Un driver PWM deve registrarsi per eventi di creazione e chiusura di file implementando le funzioni di callback EVT_WDF_DEVICE_FILE_CREATE e EVT_WDF_FILE_CLEANUP/EVT_WDF_FILE_CLOSE. Nell'implementazione il driver deve eseguire queste attività:

  1. Determinare se la richiesta di creazione è per un controller o un pin.

  2. Se la richiesta è relativa a un pin, il driver deve analizzare e convalidare il percorso del pin per creare richieste di pin e assicurarsi che il numero di pin richiesto si trova all'interno dei limiti del controller.

  3. Concedere o negare l'accesso alle richieste di creazione per controller e pin.

  4. Mantenere l'integrità dello stato dei controller e dei pin secondo una macchina a stati ben definita.

Nel set di attività precedente, la seconda attività di convalida può essere eseguita in EVT_WDF_DEVICE_FILE_CREATE, come indicato di seguito:

  1. Se il nome file associato all'oggetto file di richiesta è Null, completare la richiesta con STATUS_INVALID_DEVICE_REQUEST.

  2. Se il nome file associato all'oggetto file di richiesta è una stringa vuota, si tratta di una richiesta di creazione del controller. In caso contrario, si tratta di una richiesta di creazione pin.

  3. Se si tratta di una richiesta di creazione pin, procedere come segue:

    1. Analizzare il percorso del pin in base al formato <DecimalName> ed estrarre il numero di pin chiamando PwmParsePinPath.

    2. Se l'analisi e la convalida del percorso del pin non sono riuscite, completare la richiesta con STATUS_NO_SUCH_FILE.

    3. Se il numero di pin è maggiore o uguale al numero di pin del controller, completate la richiesta con STATUS_NO_SUCH_FILE. Si noti che il numero di pin è un indice in base zero.

    4. In caso contrario, continuare l'elaborazione EVT_WDF_DEVICE_FILE_CREATE.

Di seguito è riportato un codice di esempio che implementa i passaggi di convalida indicati in precedenza per un gestore 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 
    // 
} 

Controller e condivisione dei pin

Il modello di condivisione per controller e pin segue il modello con più lettori e uno scrittore singolo. Un controller/pin può essere aperto per la lettura da più chiamanti, ma un solo chiamante può aprire tale controller/pin per la scrittura alla volta.

Tale modello può essere implementato usando una combinazione di flag di accesso desiderato e di accesso condiviso all'apertura di un handle di file. Il modello di condivisione DDI opta per una semantica di condivisione più semplice, in cui viene utilizzata solo la specifica di Accesso Desiderato per controllare l'accesso. La specifica Share Access non svolge alcun ruolo nel modello di condivisione e non sarà rispettata se specificata quando si apre un controller o un pin.

In EVT_WDF_DEVICE_FILE_CREATE, la richiesta di creazione dell'accesso desiderato e l'accesso condiviso devono essere estratte e convalidate in base allo stato controller/pin o come indicato di seguito:

  1. Se l'Accesso di condivisione non è 0, negare l'accesso e completare la richiesta con STATUS_SHARING_VIOLATION.

  2. Se l'accesso richiesto è in modalità di sola lettura, concedere l'accesso e continuare l'elaborazione dell'evento EVT_WDF_DEVICE_FILE_CREATE.

  3. Se l'accesso desiderato è per la scrittura, eseguire le operazioni seguenti:

Se il controller/pin è già aperto per la scrittura, negare l'accesso e completare la richiesta con STATUS_SHARING_VIOLATION, altrimenti concedere l'accesso, contrassegnare il controller o il pin come aperto per la scrittura e continuare l'elaborazione EVT_WDF_DEVICE_FILE_CREATE.

Di seguito è riportato un esempio che illustra come estrarre l'accesso desiderato e condividere l'accesso da una richiesta di creazione:

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

Indipendenza di controller e pin

Il controller e il pin hanno una relazione padre-figlio. Per aprire un pin, è prima necessario aprire il controller padre. Un altro modo consiste nell'esaminare i pin come entità indipendenti in cui il controller padre fornisce un solo servizio al pin che imposta il periodo PWM globale per tutti i pin contenuti in tale controller.

Ecco alcuni scenari di esempio:

  • Accesso a processo singolo:

    • Il processo A può aprire il pin, impostarne il ciclo di attività, avviarlo usando il periodo predefinito del controller e successivamente aprire il controller e impostarne il periodo su richiesta. Potrebbe non essere mai necessario aprire il controller se il periodo predefinito è OK per l'applicazione.

    • Un processo può avere più thread in cui ognuno controlla un pin diverso nello stesso controller.

  • Accesso multiprocesso:

    • Un'utilità della riga di comando può aprire il controller con accesso di sola lettura ai fini della visualizzazione delle informazioni nella console. Allo stesso tempo, un'attività UWP in background può aprire il controller per la scrittura e controlla alcuni LED con un singolo pin.

    • Un driver di visualizzazione in modalità kernel che controlla la retroilluminazione LCD tramite Pin0 mantenendo il controllo per la scrittura e bloccando il periodo PWM. Allo stesso tempo, un servizio Win32 usa il periodo PWM impostato dal driver di visualizzazione e usa Pin1 per oscurare alcuni LED per comunicare alcuni stati all'utente.

Si noti che esistono alcune implicazioni importanti per l'apertura e la chiusura di un controller indipendentemente dai relativi pin. Per ulteriori informazioni, vedere la sezione sui diagrammi a stati.

Controller e macchine a stati per pin

Definizione dello stato del controller

Funzionalità di stato Valore predefinito Descrizione
Aperto-For-Write Falso False indica che il controller è chiuso o aperto per la lettura; True indica che è aperto per la scrittura.
Desired-Period PeriodoMinimo

Macchina a stati finiti del controller.

La macchina a stati del controller sottostante è incentrata solo sullo stato Is-Opened-For-Write. Anche il valore del periodo desiderato viene escluso perché non influisce sul tipo di operazione che può essere eseguito sul controller. Si noti che ogni volta che un controller aperto per la scrittura viene chiuso dal chiamante che lo ha aperto per la scrittura, il controller viene reimpostato sulle impostazioni predefinite (periodo desiderato predefinito).

Aggiungere la definizione dello stato

Funzione di stato Valore predefinito Descrizione
Aperto-For-Write Falso False indica che il pin è chiuso o aperto per la lettura; True indica che è aperto per la scrittura.
Attivo-Duty-Cycle 0
Is-Started Falso False indica arrestato; True indica avviato.

Stato dei pin della macchina.

La macchina a stati del pin è incentrata sulla combinazione dei due stati Is-Opened-For-Write e Is-Started. Altri stati di pin come polarità e cicli di lavoro attivi vengono lasciati fuori perché i relativi valori non influiscono sul tipo di operazioni che possono essere eseguite sul pin. Si noti che ogni volta che un pin aperto per la scrittura viene chiuso dal chiamante che lo ha aperto per la scrittura, il pin ottiene i valori predefiniti (arrestata, polarità predefinita e ciclo di attività attivo). Si noti anche che Set-Polarity transizione su uno stato con Is-Started = true viene interrotta perché non è valida in tale stato.

Qualsiasi transizione non menzionata per un determinato stato implica che tale transizione non è valida o non è possibile e la richiesta corrispondente deve essere completata con lo stato di errore appropriato.

Considerazioni sull'implementazione per le transizioni di stato

  • In EVT_WDF_DEVICE_FILE_CREATE, il driver deve concedere o negare l'accesso in base alla richiesta di creazione con l'accesso desiderato e allo stato Is-Opened-For-Write del controller o del pin, come indicato di seguito.

    Se la richiesta ha accesso desiderato in scrittura e il controller/pin è già aperto per la scrittura, completare la richiesta con STATUS_SHARING_VIOLATION, altrimenti contrassegnare il controller/pin come aperto per la scrittura (Is-Opened-For-Write = true), concedere l'accesso e continuare l'elaborazione.

    In questo esempio vengono implementati i passaggi di convalida dell'accesso menzionati in precedenza per un gestore EVT_WDF_DEVICE_FILE_CREATE in cui viene omessa la logica di blocco necessaria per gestire le richieste di creazione di file simultanee:

    //
    // 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;
    }
    
  • In EVT_WDF_FILE_CLOSE/EVT_WDF_FILE_CLEANUP, il driver deve mantenere l'integrità dello stato del controller e del pin.

    Se l'oggetto file appartiene a un controller/pin che ha aperto tale controller/pin per la scrittura, reimpostare il controller/pin sullo stato predefinito e deselezionare l'apertura del controller/pin per la scrittura (Is-Opened-For-Write = false).

    In questo esempio vengono implementati i passaggi di convalida dell'accesso menzionati in precedenza per un gestore EVT_WDF_DEVICE_FILE_CLOSE in cui viene omessa la logica di blocco necessaria per gestire le richieste di chiusura dei file simultanee.

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

Richieste IOCTL di PWM

Le richieste IOCTL di PWM vengono inviate da un'applicazione o da un altro driver e sono destinate a un controller o a un pin specifico.

Controller IOCTLs

Pin IOCTLs

Per ogni richiesta IOCTL, il driver PWM deve verificare quanto segue:

  1. L'operazione richiesta (codice IOCTL) è valida per l'oggetto file associato alla richiesta.

  2. Richiedere buffer di input e output e assicurarsi che siano almeno delle dimensioni minime previste.

  3. Validità dell'operazione richiesta nella condizione attuale del controller/pin.

  4. Validità dei singoli parametri di input. Ad esempio, un periodo desiderato pari a zero è un parametro non valido per IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD.

Codici di stato di completamento IOCTL

Il driver PWM deve completare la richiesta IOCTL con il codice di stato appropriato. Ecco i codici di stato di completamento comuni. In generale, un IOCTL che imposta una proprietà con valore già impostato dovrebbe avere sempre esito positivo. Ad esempio, impostando lo stesso periodo già impostato, arrestando un pin già arrestato, impostando una polarità già impostata e così via.

STATUS_NOT_SUPPORTED

L'operazione IOCTL richiesta non è implementata o supportata. Ad esempio, alcuni controller potrebbero non supportare l'impostazione della polarità del segnale di output. In questo caso, IOCTL_PWM_PIN_SET_POLARITY dovrebbe essere implementato, ma fallire con STATUS_NOT_SUPPORTED per la polarità non predefinita.

STATUS_INVALID_DEVICE_REQUEST

La richiesta IOCTL è stata inviata alla destinazione errata. Ad esempio, una richiesta IOCTL del controller è stata inviata usando un handle del file pin.

STATUS_BUFFER_TOO_SMALL

La dimensione del buffer di input o di output è inferiore alla dimensione minima richiesta del buffer per l'elaborazione della richiesta. Un driver WDF che usa WdfRequestRetrieveInputBuffer o WdfRequestRetrieveOutputBuffer per recuperare e convalidare i buffer di input e output può restituire lo stato di errore corrispondente così come è. Tutti i IOCTL con buffer di input e/o di output definiti per loro hanno uno struct corrispondente che descrive tale buffer, in cui i nomi degli struct di input e di output hanno rispettivamente INPUT e _OUTPUT prefisso. La dimensione minima del buffer di input è sizeof(PWMINPUT) mentre la dimensione minima del buffer di output è sizeof(PWM_OUTPUT).

Codice IOCTL Descrizione
IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD Recupera il periodo di segnale di output effettivo del controller PWM (Pulse Width Modulation), in quanto viene misurato sui canali di output. Restituisce un valore PWM_CONTROLLER_GET_ACTUAL_PERIOD_OUTPUT. Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.
  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_GET_INFO Recupera informazioni su un controller PWM (Pulse Width Modulation). Queste informazioni non cambiano dopo l'inizializzazione del controller.

Il chiamante deve passare un buffer di output con esattamente le dimensioni dello struct PWM_CONTROLLER_INFO. Il driver deduce la versione della struttura dalla dimensione del buffer di output della richiesta.

Se le dimensioni del buffer sono inferiori alle dimensioni della versione della struttura più bassa, la richiesta viene completata usando uno stato di completamento IOCTL di STATUS_BUFFER_TOO_SMALL. In caso contrario, il driver presuppone la versione della struttura più elevata che può rientrare nel buffer di output fornito e completa correttamente la richiesta.

Una versione PWM_CONTROLLER_INFO più recente ha una dimensione di byte maggiore di quella della versione precedente

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.
  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD Imposta il periodo di segnale di output di un controller PWM (Pulse Width Modulation) su un valore suggerito.

Il controller PWM tenta di impostare il periodo più vicino possibile al valore richiesto in base alle relative funzionalità. Il periodo di validità viene restituito come output IOCTL. Può essere recuperato in un secondo momento usando IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD.

Il periodo desiderato deve essere maggiore di zero (0) e nell'intervallo di periodi supportato dal controller. Ciò significa che deve trovarsi nell'intervallo di MinimumPeriod e MaximumPeriod, inclusivo, che può essere recuperato usando IOCTL_PWM_CONTROLLER_GET_INFO.

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.
  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATO_PARAMETRO_NON_VALIDO
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE Recupera la percentuale corrente del ciclo di lavoro per un pin o un canale. Il codice di controllo restituisce la percentuale come struttura PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE_OUTPUT.

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.

  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE Impostare un valore percentuale del ciclo di lavoro desiderato per il pin o il canale del controller. Il codice di controllo specifica la percentuale come struttura PWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE_INPUT.

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.

  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_POLARITY Recupera la polarità del segnale corrente del pin o del canale. Il codice di controllo ottiene la polarità del segnale come struttura PWM_PIN_GET_POLARITY_OUTPUT. La polarità del segnale è Attiva alta o Bassa attiva, come definito nell'enumerazione PWM_POLARITY.

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.

  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_POLARITY Imposta la polarità del segnale del pin o del canale. Il codice di controllo imposta la polarità del segnale in base a una struttura PWM_PIN_SET_POLARITY_INPUT. La polarità del segnale è "Attivo alto" o "Attivo basso", come definito nella enumerazione PWM_POLARITY.

La modifica della polarità è consentita solo quando il pin viene arrestato. È possibile stabilire se il pin viene arrestato usando il codice di controllo IOCTL_PWM_PIN_IS_STARTED. Se il pin è fermo e la polarità richiesta è diversa da quella corrente del pin, la richiesta viene completata con un valore di STATUS_INVALID_DEVICE_STATE.

La modifica della polarità durante l'avvio di un pin può causare problemi su alcuni controller PWM (Pulse Width Modulation). Se si desidera modificare la polarità, fermare innanzitutto il pin, modificare la polarità e poi riavviare il pin.

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.

  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATO_PARAMETRO_NON_VALIDO
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_START Avvia la generazione del segnale Pulse Width Modulation (PWM) su un pin o un canale. Per verificare se viene avviato un pin, usare IOCTL_PWM_PIN_IS_STARTED.

L'emissione di questo IOCTL su un pin o un canale già avviato non ha alcun effetto, ma ha esito positivo.

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.

>
  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_STOP Arresta la generazione del segnale PWM (Pulse Width Modulation) su un pin o un canale. Per verificare se viene avviato un pin, usare IOCTL_PWM_PIN_IS_STARTED.

L'emissione di questo IOCTL su un pin o un canale già arrestato non ha alcun effetto, ma ha esito positivo.

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.

  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_IS_STARTED Recupera lo stato di generazione del segnale per un pin o un canale. Ogni pin ha uno stato avviato o arrestato come struttura di PWM_PIN_IS_STARTED_OUTPUT. Lo stato avviato ha un valore booleano di true. Lo stato arrestato è falso.

Per impostazione predefinita, un pin viene arrestato all'apertura e torna allo stato arrestato quando viene chiuso o rilasciato.

Irp-IoStatus.Status> è impostato su uno dei valori nell'elenco seguente.

  • STATO_RIUSCITO
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL