Condividi tramite


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 della memoria mappato allo spazio indirizzi 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 ai dispositivi PWM esposti tramite le API WinRT pwm definite nello spazio dei nomi Windows.Devices.Pwm.

Nota

Se hai un modulo PWM 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 astratta in un singolo controller e in uno o più pin. Il controllo del controller o dei pin viene eseguito tramite gli IOCTL definiti da PWM. Ad esempio, un driver display LCD invia tali richieste al driver PWM per controllare il livello di backlight LCD.

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

  • Guidare un motore servo.
  • Guidare un carico come led o motore a pennello 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 di impulso rettangolare con larghezza degli impulsi modulari, 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 lavoro. La frequenza d'onda (f) è il reciproco del periodo della forma d'onda f=1/T. Il ciclo di servizio descrive la proporzione del tempo "on" o "Attivo" rispetto all'intervallo regolare o al periodo di tempo; un ciclo di lavoro basso corrisponde a una media di potenza di output bassa, perché l'alimentazione è spenta per la maggior parte del tempo. Il ciclo del dovere è espresso in percentuale in cui il 100% è completamente attivo, il 0% è completamente disattivato, il 50% è "Attivo" 50% del tempo.

Driver.

Accesso al controller PWM esposto dal sistema e ai pin

Il driver PWM deve 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 nell'implementazione del driver della funzione di callback EVT_WDF_DRIVER_DEVICE_ADD. 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 IOCTL definiti da PWM. Prima di inviare IOCTLs, l'applicazione o il driver del mittente devono aprire un handle di file al controller e il pin specificando il relativo collegamento simbolico. Ciò richiede che il driver registri per la creazione e la chiusura di eventi di file. Vedere (collegamento)

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 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 di PWM dalle app UWP, queste proprietà dell'interfaccia del dispositivo devono essere impostate.

  • DEVPKEY_DeviceInterface_Restricted

    In base al modello di accesso al dispositivo UWP corrente, l'impostazione della proprietà dell'interfaccia del 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 schema all'interfaccia del dispositivo PWM dei dispositivi PWM connessi in modo statico è necessaria per l'uso del metodo factory 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 dello schema siano univoci nel sistema, ma che non vengono applicati. Almeno, non dovrebbero esserci 2 dispositivi PWM con lo stesso nome schema. In caso contrario, 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. Di seguito è riportato 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 a 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 assegnato a ACPI (_HID) è FSCL00E0 e il relativo ID univoco (_UID) è 0,...,3. L'esposizione di tutti i dispositivi PWM a UWP richiederà le sezioni INF che impostano l'DEVPKEY_DeviceInterface_Restricted in modo che corrispondano all'ID hardware ACPI\FSCL00E0.

    Questa modalità di 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 ogni proprietà dell'interfaccia del dispositivo può essere specificato in ogni nodo del dispositivo ACPI _DSD metodo come Proprietà dispositivo. Il driver deve eseguire 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 nel DSDT ACPI 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 creare e chiudere eventi di file implementando 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 è per 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 al controller e aggiungere richieste di creazione.

  4. Mantenere i controller e l'integrità dello stato dei pin per 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 del 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 di aggiunta 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, completare 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.

Ecco 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, singolo writer. 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 condivisione durante l'apertura di un handle di file. Il modello di condivisione DDI opta per una semantica di condivisione più semplice in cui viene usata solo la specifica di accesso desiderato per controllare l'accesso. La specifica Share Access non svolge alcun ruolo nel modello di condivisione e non viene 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 Share Access non è 0, negare l'accesso e completare la richiesta con STATUS_SHARING_VIOLATION.

  2. Se l'accesso desiderato è di sola lettura, concedere l'accesso e continuare l'elaborazione EVT_WDF_DEVICE_FILE_CREATE.

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

Se il controller o il 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.

Ecco un esempio che illustra come estrarre l'accesso desiderato e l'accesso condiviso 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; 
} 

Controller e indipendenza 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 nel 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 a più processi:

    • 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.

    • Driver di visualizzazione in modalità kernel che controlla la backlight LCD tramite Pin0 mantenendo il controller per la scrittura e il blocco del periodo PWM. Allo stesso tempo, un servizio Win32 usa il periodo PWM impostato dal driver di visualizzazione e usa Pin1 per dimare un 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 altre informazioni, vedere la sezione delle macchine a stati.

Controller e pin state machines

Definizione dello stato del controller

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

Macchina a stati del controller.

La macchina a stati del controller sottostante è allineata al centro solo sullo stato Is-Opened-For-Write. Il valore del periodo desiderato viene anche 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

Funzionalità stato Valore predefinito Descrizione
Aperto per la scrittura Falso False indica che il pin è chiuso o aperto per la lettura; True indica che è aperto per la scrittura.
Active-Duty-Cycle 0
Is-Started Falso False indica arrestato; True indica che è stato avviato.

Aggiunge la macchina a stati.

La macchina a stati del pin è allineata al centro della combinazione dei 2 stati Is-Opened-For-Write e Is-Started. Altri stati di pin, ad esempio polarità e cicli di servizio attivi, vengono lasciati perché i 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 lasciato perché non è valido in tale stato.

Qualsiasi transizione non menzionata per uno stato specificato 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 dell'accesso desiderato e al controller o al pin Is-Opened-For-Write 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 indicati 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 o 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 annullare l'apertura del controller o del pin per la scrittura (Is-Opened-For-Write = false).

    In questo esempio vengono implementati i passaggi di convalida dell'accesso indicati 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 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 nello stato controller/pin corrente.

  4. Validità dei singoli parametri di input. Ad esempio, un punto 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 esito positivo. Esempio di foe, impostando esattamente lo stesso periodo già impostato, arrestando un pin già arrestato, impostando la 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 devono essere implementati, ma hanno esito negativo 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 di file pin.

STATUS_BUFFER_TOO_SMALL

Le dimensioni del buffer di input o di output sono inferiori alle dimensioni minime necessarie 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 gli IOCTL con buffer di input e/o di output definiti includono uno struct corrispondente che descrive tale buffer, in cui i nomi degli struct di input e di output hanno rispettivamente INPUT e _OUTPUT suffisso. 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), come verrebbe 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.
  • STATUS_SUCCESS
  • 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 che ha esattamente le dimensioni dello struct PWM_CONTROLLER_INFO. Il driver deduce la versione della struttura dalle dimensioni del buffer di output della richiesta.

Se la dimensione del buffer è inferiore alla dimensione della versione più bassa della struttura, la richiesta viene completata usando lo stato di completamento IOCTL di STATUS_BUFFER_TOO_SMALL. In caso contrario, il driver presuppone la versione della struttura più elevata che può essere inserita nel buffer di output fornito e completa correttamente la richiesta.

Una versione di 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.
  • STATUS_SUCCESS
  • 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 effettivo 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 essere compreso nell'intervallo 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.
  • 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 la percentuale del ciclo di servizio corrente 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.

  • STATUS_SUCCESS
  • 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 servizio 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.

  • STATUS_SUCCESS
  • 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.

  • STATUS_SUCCESS
  • 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 è Attiva alta o Bassa attiva, come definito nell'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 viene arrestato e la polarità richiesta è diversa dalla polarità del pin corrente, la richiesta viene completata con un valore 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à, arrestare prima il pin, modificare la polarità e quindi avviare il pin.

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

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_INVALID_PARAMETER
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_START Avvia la generazione del segnale PWM (Pulse Width Modulation) su un pin o un canale. Per verificare se è stato 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.

>
  • STATUS_SUCCESS
  • 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 è stato 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.

  • STATUS_SUCCESS
  • 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 PWM_PIN_IS_STARTED_OUTPUT. Lo stato avviato ha un valore booleano true. Lo stato arrestato è false.

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.

  • STATUS_SUCCESS
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL