Pilote PWM pour un module PWM intégré au SoC

Pour fournir l’accès à un contrôleur pwM (Pulse width modulation) qui fait partie du SoC et mappé en mémoire à l’espace d’adressage SoC, vous devez écrire un pilote en mode noyau. Le pilote doit enregistrer l'interface de classe de périphérique du contrôleur PWM afin que les applications UWP puissent accéder aux périphériques PWM exposés par le biais des API WinRT définies dans l'espace de noms Windows.Devices.Pwm.

Remarque

Si vous disposez d’un module PWM complémentaire sur I2C, SPI ou un contrôleur UART, vous pouvez accéder au module à partir d’une application UWP à l’aide des API définies dans l’espace de noms Windows.Devices.Pwm et Windows.Devices.Pwm.Provider .

Un dispositif PWM est représenté par un seul contrôleur et une ou plusieurs broches. Le contrôle du contrôleur ou des broches s'effectue par le biais des IOCTL définis par PWM. Par exemple, un pilote d’affichage LCD envoie ces demandes au pilote PWM pour contrôler le niveau de rétro-éclairage LCD.

Génération de signal PWM à plusieurs contrôleurs et multicanaux avec période configurable, polarité et cycle de travail qui permet les tâches suivantes :

  • Actionner un moteur à servo.
  • Conduisez une charge telle que led ou moteur brossé DC.
  • Générez un signal analogique.
  • Générez une horloge précise.

Cette rubrique décrit,

  • Comment activer l’accès UWP aux dispositifs PWM exposés par le système.

  • Comment gérer les requêtes IOCTL PWM envoyées par une application Win32 ou un pilote en mode noyau tiers.

Public prévu

  • Oem et IHVS développant un système avec un contrôleur PWM on-SoC.

Dernière mise à jour

  • Août 2017

version de Windows

  • Version de Windows 10

Les APIs importantes

À propos de PWM

PWM décrit la technique de base pour générer une onde d’impulsion rectangulaire avec une largeur d’impulsions modulée, ce qui entraîne la variation de la valeur moyenne de la forme d’onde.

Une forme d’onde PWM peut être catégorisée par 2 paramètres : période de forme d’onde (T) et cycle de travail. La fréquence de forme d’onde (f) est la réciproque de la période de forme d’onde f=1/T. Le rapport cyclique décrit la proportion de la durée « marche » ou « Active » par rapport à l'intervalle régulier ou à la « Période » de temps ; un faible rapport cyclique correspond à une moyenne de faible puissance de sortie, car l'alimentation est coupée la plupart du temps. Le cycle de service est exprimé en pourcentage où 100% étant entièrement sur, 0% étant entièrement désactivé, 50% étant « actif » 50% du temps.

Pilotes.

Accès au contrôleur et aux broches PWM exposés par le système

Le pilote PWM doit s’inscrire
GUID_DEVINTERFACE_PWM_CONTROLLER comme GUID d'interface de périphérique pour exposer et accéder aux dispositifs 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}" 

Pour enregistrer le GUID de l’interface de périphérique, le pilote doit appeler WdfDeviceCreateDeviceInterface dans l’implémentation par le pilote de la fonction de rappel EVT_WDF_DRIVER_DEVICE_ADD. Après l’inscription, le système affecte un lien symbolique au contrôleur et aux broches.

Une application ou un autre pilote peut contrôler le contrôleur ou les broches via les IOCTL définis par PWM. Avant d’envoyer les IOCTLs, l’application ou le pilote expéditeur doit ouvrir un descripteur de fichier au contrôleur et à la broche en spécifiant son lien symbolique. Cela nécessite que le pilote s’inscrive pour créer et fermer des événements de fichier. Voir (lien)

Voici un exemple de chemin symbolique pour le contrôleur :

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

De même, le format des broches est le suivant : Le format du chemin d’accès est le suivant :

<DeviceInterfaceSymbolicLinkName>\<PinNumber>

<PinNumber> est l'indice basé sur 0 de la broche à ouvrir.

Voici un exemple de chemin symbolique pour les broches :

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

Pour ouvrir le handle de fichier, l’application doit appeler les API Configuration Manager (CM_Get_Device_Interface_*).

Une fois le handle de fichier ouvert, l’application peut envoyer ces requêtes en appelant la fonction DeviceIoControl. Consultez la section PWM.

Le pilote doit utiliser la routine de support PwmParsePinPath fournie pour analyser et valider les chemins des broches et extraire le numéro de broche.

Définition des propriétés de l’interface d’appareil

Pour utiliser les API WinRT PWM à partir d’applications UWP, ces propriétés d’interface d’appareil doivent être définies.

  • DEVPKEY_DeviceInterface_Restricted

    Conformément au modèle d’accès actuel de l’appareil UWP, la définition de la propriété d’interface d’appareil restreinte sur FALSE est nécessaire pour permettre aux applications UWP d’accéder à l’interface d’appareil PWM.

  • DEVPKEY_DeviceInterface_SchematicName (lien ???)

    L’affectation d’un nom schématique à l’interface d’appareil PWM des appareils PWM connectés statiquement est nécessaire pour utiliser la méthode d’usine PwmController.GetDeviceSelector(FriendlyName). Le nom schématique est le nom donné à l’appareil PWM dans les schémas de conception du système, par exemple (PWM0, PWM_1, et..). Les noms schématiques sont supposés être uniques dans le système, mais cela n’est pas appliqué. Il est impératif qu'il n'y ait pas 2 appareils PWM avec le même nom de schéma, sinon le comportement de WinRT PwmController.GetDeviceSelector(FriendlyName) ne sera pas déterministe.

Les propriétés peuvent être définies de deux façons :

  1. Utilisation du fichier INF pour le pilote PWM

    Utilisez la directive AddProperty pour définir les propriétés de l’appareil. Le fichier INF doit autoriser la définition de valeurs différentes pour la même propriété sur un ou sous-ensemble des instances d’appareil PWM. Voici un exemple de définition de 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 
    

    Toutes les conceptions n’ont pas la même stratégie pour exposer l’appareil PWM à UWP. Par exemple, la stratégie peut autoriser l’accès UWP à un sous-ensemble des instances d’appareil PWM. L'exposition d'un sous-ensemble des périphériques PWM ou l'assignation d'une valeur de propriété différente à un ou un sous-ensemble des instances de périphériques PWM nécessite d'avoir un ID matériel différent pour chaque instance de périphérique PWM et de faire correspondre de manière sélective aux sections INF en fonction de la stratégie.

    Envisagez une conception basée sur soC où il existe quatre instances d’appareil PWM identiques nommées PWM0,...,PWM3 où leur ID matériel affecté ACPI (_HID) est FSCL00E0 et leur ID unique (_UID) est 0,...,3. L'exposition de tous les appareils PWM à UWP nécessite les sections INF qui définissent la clef DEVPKEY_DeviceInterface_Restricted pour correspondre à l'ID matériel ACPI\FSCL00E0.

    Cette façon de définir les propriétés ne nécessite aucune modification dans le code du pilote. Il s’agit d’une option plus simple, car la maintenance d’un fichier INF est plus facile qu’un binaire de pilote. Le compromis est que cette approche nécessite un fichier INF personnalisé pour chaque conception.

  2. Programmatiquement dans le pilote PWM

    Un pilote PWM peut appeler IoSetDeviceInterfacePropertyData pour définir les propriétés de l’interface d’appareil dans leur implémentation EVT_WDF_DRIVER_DEVICE_ADD après avoir créé et publié l’interface d’appareil PWM. Le pilote est chargé de décider de la valeur à attribuer et de la propriété de l’appareil. Ces informations sont généralement stockées dans le système ACPI pour les conceptions basées sur soC. La valeur de chaque propriété d'interface de périphérique peut être spécifiée dans chaque nœud de périphérique ACPI par la méthode _DSD comme propriétés de périphérique. Le pilote doit interroger the_DSD à partir d’ACPI, analyser les données des propriétés de l’appareil, extraire la valeur de chaque propriété et l’affecter à l’interface de l’appareil.

    La définition programmatique des propriétés rend le pilote et son fichier INF portables entre les conceptions et, par conséquent, les BSP où la seule modification se trouve dans le DSDT ACPI définissant chaque nœud d’appareil PWM. Toutefois, la lecture et l’analyse des blocs binaires ACPI sont fastidieux et nécessitent un grand nombre de code qui peuvent être sujettes à des erreurs et des vulnérabilités entraînant une surface d’erreur plus grande.

Gestion des événements d’ouverture/fermeture de fichier

Un pilote PWM doit s'inscrire pour les événements de création et de fermeture de fichiers en implémentant les fonctions de rappel EVT_WDF_DEVICE_FILE_CREATE et EVT_WDF_FILE_CLEANUP/EVT_WDF_FILE_CLOSE. Dans l’implémentation, le pilote doit effectuer ces tâches :

  1. Déterminez si la demande de création concerne un contrôleur ou une broche.

  2. Si la demande concerne une broche, le pilote doit analyser et valider le chemin d’accès de la broche pour les demandes de création de broche et vérifier que le numéro de broche demandé se trouve dans les limites du contrôleur.

  3. Accordez ou refusez l’accès au contrôleur et épinglez les demandes de création.

  4. Maintenez l'intégrité des contrôleurs et de l'état des broches selon une machine d'état bien définie.

Dans l’ensemble de tâches précédent, la deuxième tâche de validation peut être effectuée dans EVT_WDF_DEVICE_FILE_CREATE, comme suit :

  1. Si le nom de fichier associé à l’objet de fichier de requête a la valeur Null, terminez la requête avec STATUS_INVALID_DEVICE_REQUEST.

  2. Si le nom de fichier associé à l’objet de fichier de requête est une chaîne vide, il s’agit d’une demande de création de contrôleur, sinon il s’agit d’une requête de création d’épingle.

  3. S'il s'agit d'une demande pour créer une épingle, suivez ces étapes :

    1. Analysez le chemin de broche en fonction du format <DecimalName> et extrayez le numéro de broche en utilisant PwmParsePinPath.

    2. Si l’analyse et la validation du chemin de broche ont échoué, finalisez la requête avec STATUS_NO_SUCH_FILE.

    3. Si le numéro d’épingle est supérieur ou égal au nombre d’épingles du contrôleur, terminez la requête avec STATUS_NO_SUCH_FILE. Veuillez noter que le numéro de broche est un index basé sur zéro.

    4. Sinon, continuez le traitement de EVT_WDF_DEVICE_FILE_CREATE.

Voici un exemple de code qui implémente les étapes de validation mentionnées précédemment pour un gestionnaire 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 
    // 
} 

Contrôleur et partage d’épingles

Le modèle de partage pour le contrôleur et les broches suit les plusieurs lecteurs, modèle d’enregistreur unique. Un contrôleur/épingle peut être ouvert pour lecture par plusieurs appelants, mais un seul appelant peut ouvrir ce contrôleur/épingle pour l’écriture à la fois.

Ce modèle peut être implémenté à l’aide d’une combinaison d’indicateurs d’accès souhaité et d’accès partagé lors de l’ouverture d’un handle de fichier. Le modèle de partage DDI opte pour une sémantique de partage plus simple où seule la spécification d’accès souhaitée est utilisée pour contrôler l’accès. La spécification Share Access ne joue aucun rôle dans le modèle de partage et n’est pas respectée si elle est spécifiée lors de l’ouverture d’un contrôleur ou d’une broche.

Dans EVT_WDF_DEVICE_FILE_CREATE, la demande de création d’accès souhaité et d’accès au partage doit être extraite et validée en fonction de l’état du contrôleur/du code pin, ou comme suit :

  1. Si Share Access n’est pas 0, refusez l’accès et effectuez une demande avec STATUS_SHARING_VIOLATION.

  2. Si l’accès souhaité est en lecture seule, accordez l’accès et poursuivez le traitement EVT_WDF_DEVICE_FILE_CREATE.

  3. Si l’accès souhaité est destiné à l’écriture, puis :

Si le contrôleur/épingle est déjà ouvert pour l’écriture, refusez l’accès et terminez la demande avec STATUS_SHARING_VIOLATION, sinon accordez l’accès, marquez le contrôleur ou l’épingle comme ouvert pour l’écriture et poursuivez le traitement EVT_WDF_DEVICE_FILE_CREATE.

Voici un exemple montrant comment extraire l’accès souhaité et partager l’accès à partir d’une demande de création :

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

Indépendance du contrôleur et de l’épingle

Le contrôleur et l’épingle ont une relation parent-enfant. Pour ouvrir une broche, vous devez d’abord ouvrir son contrôleur parent. Une autre méthode consiste à examiner les broches comme des entités indépendantes où son contrôleur parent fournit un seul service à la broche qui définit la période PWM globale pour toutes les broches contenues sur ce contrôleur.

Voici des exemples de scénarios :

  • Accès à un seul processus :

    • Le processus A peut activer la broche, définir son cycle de service, la démarrer en utilisant la période par défaut du contrôleur, et plus tard activer le contrôleur et définir sa période selon les besoins. Il se peut qu’il n’ait jamais besoin d’ouvrir le contrôleur si la période par défaut est OK pour l’application.

    • Un processus peut avoir plusieurs threads, chacun contrôlant une broche différente sous le même contrôleur.

  • Accès multiprocesseur :

    • Un utilitaire de ligne de commande peut ouvrir le contrôleur avec un accès en lecture seule à des fins d’affichage des informations sur la console. En même temps, une tâche UWP en arrière-plan peut ouvrir le contrôleur pour l’écriture et contrôler des LED avec une seule broche.

    • Un pilote d'affichage en mode noyau qui contrôle le rétroéclairage LCD via Pin0, tout en tenant le contrôleur pour l'écriture et en verrouillant la période PWM. En même temps, un service Win32 utilise la période PWM définie par le pilote d’affichage et utilise Pin1 pour faireimer une LED pour communiquer un état à l’utilisateur.

Notez qu’il existe des implications importantes pour l’ouverture et la fermeture d’un contrôleur indépendamment de ses broches. Pour plus d’informations, consultez la section des machines d’état.

Contrôleur et automates d'état de broche

Définition de l’état du contrôleur

Fonctionnalité de gestion des états Valeur par défaut Descriptif
Ouvert-For-Write Faux False indique que le contrôleur est fermé ou ouvert pour lecture ; True indique qu’il est ouvert pour l’écriture.
Desired-Period PériodeMinimale

Ordinateur d’état du contrôleur.

La machine à états du contrôleur ci-dessous est centrée sur l’état Is-Opened-For-Write uniquement. La valeur de période souhaitée est également omise, car elle n'affecte pas le type d'opérations pouvant être effectuées sur le contrôleur. Notez que chaque fois qu’un contrôleur ouvert pour l’écriture est fermé par l’appelant qui l’a ouvert pour écriture, le contrôleur est réinitialisé à ses valeurs par défaut (période souhaitée par défaut).

Définition de l'état des broches

Fonctionnalité d’état Valeur par défaut Descriptif
Ouvert-For-Write Faux False indique que le port est fermé ou bien ouvert pour la lecture ; True indique qu’il est ouvert pour l'écriture.
Active-Duty-Cycle 0
Is-Started Faux False indique arrêté ; True indique démarré.

Machine à états des broches.

La machine à état pin est centrée autour de la combinaison des 2 états Is-Opened-For-Write et Is-Started. D’autres états de broche comme la polarité et les cycles de service actifs sont laissés hors service, car leurs valeurs n’affectent pas le type d’opérations qui peuvent être effectuées sur la broche. Notez que chaque fois qu'une broche ouverte pour l'écriture est fermée par l'appelant qui l'a ouverte, la broche se réinitialise à ses paramètres par défaut (arrêtée, polarité par défaut et cycle de travail actif). Notez également que la transition Set-Polarity est exclue dans un état où Is-Started = true, car elle n’est pas valide dans cet état.

Toute transition qui n’est pas mentionnée pour un état donné implique que cette transition n’est pas valide ou n’est pas possible et que la demande correspondante doit être effectuée avec l’état d’erreur approprié.

Considérations relatives à l’implémentation pour les transitions d’état

  • Dans EVT_WDF_DEVICE_FILE_CREATE, le pilote doit accorder ou refuser l’accès en fonction du niveau d'accès souhaité de la demande de création, ainsi que de l'état Is-OpenedFor-Write du contrôleur ou de la broche, comme suit :

    Si la requête a un accès souhaité en écriture et que le contrôleur/épingle est déjà ouvert pour l’écriture, terminez la requête avec STATUS_SHARING_VIOLATION, sinon marquez le contrôleur/épingle comme ouvert pour l’écriture (Is-Opened-For-Write = true), accordez l’accès et poursuivez le traitement.

    Cet exemple implémente les étapes de validation d’accès mentionnées précédemment pour un gestionnaire EVT_WDF_DEVICE_FILE_CREATE où la logique de verrouillage nécessaire pour gérer les demandes de création de fichiers simultanées est omise :

    //
    // 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;
    }
    
  • Dans EVT_WDF_FILE_CLOSE/EVT_WDF_FILE_CLEANUP, le pilote doit maintenir l’intégrité de l'état du contrôleur et de l'état des broches.

    Si l’objet de fichier appartient à un contrôleur/épingle qui a ouvert ce contrôleur/épingle pour l’écriture, réinitialisez le contrôleur/épingle à l’état par défaut et supprimez l’ouverture de ce contrôleur/épingle pour l’écriture (Is-Opened-For-Write = false).

    Cet exemple implémente les étapes de validation d’accès mentionnées précédemment pour un gestionnaire EVT_WDF_DEVICE_FILE_CLOSE où la logique de verrouillage nécessaire pour gérer les demandes de fermeture de fichiers simultanées est omise.

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

Demandes IOCTL PWM

Les demandes IOCTL PWM sont envoyées par une application ou un autre pilote et sont destinées à un contrôleur ou à une broche spécifique.

Contrôleur IOCTLs

Fixer les IOCTLs

Pour chaque demande IOCTL, le pilote PWM doit vérifier les éléments suivants :

  1. L’opération demandée (code IOCTL) est valide pour l’objet de fichier associé à la requête.

  2. Demandez des mémoires tampons d’entrée et de sortie et vérifiez qu’elles sont au moins de la taille minimale attendue.

  3. Validité de l’opération demandée dans l’état actuel du contrôleur/du code pin.

  4. Validité des paramètres d’entrée individuels. Par exemple : une période de zéro souhaitée est un paramètre non valide pour IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD.

Codes d’état d’achèvement IOCTL

Le pilote PWM doit remplir la demande IOCTL avec le code d’état approprié. Voici les codes d’état d’achèvement courants. En général, un IOCTL qui définit une propriété avec une valeur déjà définie doit toujours réussir. Par exemple, la définition exacte de la même période que celle qui est déjà définie, l'arrêt d'une broche déjà arrêtée, le paramétrage de la polarité déjà défini, et ainsi de suite.

STATUS_NOT_SUPPORTED

L’opération IOCTL demandée n’est pas implémentée ou prise en charge. Par exemple, certains contrôleurs peuvent ne pas prendre en charge la définition de la polarité du signal de sortie, dans ce cas IOCTL_PWM_PIN_SET_POLARITY doit être implémentée, mais échouer avec STATUS_NOT_SUPPORTED pour la polarité non par défaut.

STATUS_INVALID_DEVICE_REQUEST

La demande IOCTL a été envoyée à la cible incorrecte. Par exemple, une demande IOCTL du contrôleur a été envoyée à l’aide d’un handle de fichier pin.

STATUS_BUFFER_TOO_SMALL

La taille de la mémoire tampon d’entrée ou de sortie est inférieure à la taille minimale requise pour le traitement de la demande. Un pilote WDF qui utilise WdfRequestRetrieveInputBuffer ou WdfRequestRetrieveOutputBuffer pour récupérer et valider les mémoires tampons d’entrée et de sortie peut retourner son état d’erreur correspondant tel quel. Tous les IOCTL avec des mémoires tampons d’entrée et/ou de sortie définies ont un struct correspondant qui décrit cette mémoire tampon, où les noms de struct d’entrée et de sortie ont respectivement les postfix INPUT et _OUTPUT. La taille minimale de la mémoire tampon d’entrée est sizeof(PWMINPUT), tandis que la taille minimale de la mémoire tampon de sortie est sizeof(PWM_OUTPUT).

Code IOCTL Descriptif
IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD Récupère la période de signal de sortie effective du contrôleur PWM (Pulse Width Modulation), telle qu’elle serait mesurée sur ses canaux de sortie. Retourne une valeur PWM_CONTROLLER_GET_ACTUAL_PERIOD_OUTPUT. Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.
  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_GET_INFO Récupère des informations sur un contrôleur PWM (Pulse Width Modulation). Ces informations ne changent pas après l’initialisation du contrôleur.

L’appelant doit passer une mémoire tampon de sortie qui a exactement la taille du struct PWM_CONTROLLER_INFO. Le pilote déduit la version de la structure à partir de la taille de la mémoire tampon de sortie de la requête.

Si la taille de la mémoire tampon est inférieure à la taille de la version de structure la plus basse, la demande est terminée à l’aide d’un état d’achèvement IOCTL de STATUS_BUFFER_TOO_SMALL. Sinon, le pilote part du principe que la version de structure la plus élevée peut s’adapter à la mémoire tampon de sortie fournie et termine la requête avec succès.

Une version PWM_CONTROLLER_INFO plus récente a une taille d’octet supérieure à celle de la version précédente

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.
  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD Définit la période de signal de sortie d’un contrôleur PWM (Pulse Width Modulation) sur une valeur suggérée.

Le contrôleur PWM tente de définir la période aussi proche que possible de la valeur demandée en fonction de ses fonctionnalités. La période effective est retournée en tant que sortie IOCTL. Il peut être récupéré ultérieurement en utilisant la commande IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD.

La période souhaitée doit être supérieure à zéro (0) et dans la plage de périodes prise en charge par le contrôleur. Autrement dit, il doit être dans la plage de MinimumPeriod et MaximumPeriod, inclus, qui peuvent être récupérés à l'aide de la fonction IOCTL_PWM_CONTROLLER_GET_INFO.

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.
  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • PARAMÈTRE_NON_VALIDE
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_BROCHE_OBTENIR_TAUX_D_ACTIVITÉ_CYCLIQUE Récupère le pourcentage de cycle de service actuel d’une broche ou d’un canal. Le code de commande retourne le pourcentage sous la forme de la structure PWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE_OUTPUT.

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.

  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_DÉFINIR_POURCENTAGE_CYCLE_D_ACTIVITÉ_ACTIF Définissez une valeur de pourcentage de cycle de travail souhaitée pour la broche ou le canal du contrôleur. Le code de contrôle spécifie le pourcentage sous la forme d’une structure PWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE_INPUT.

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.

  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_POLARITY Récupère la polarité du signal actuel de la broche ou du canal. Le code de contrôle reçoit la polarité du signal sous forme de structure PWM_PIN_GET_POLARITY_OUTPUT. La polarité du signal est Active High ou Active Low, telle que définie dans l’énumération PWM_POLARITY.

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.

  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_POLARITY Définit la polarité du signal de la broche ou du canal. Le code de contrôle définit la polarité du signal en fonction d’une structure PWM_PIN_SET_POLARITY_INPUT. La polarité du signal est Active High ou Active Low, telle que définie dans l’énumération PWM_POLARITY.

La modification de la polarité n’est autorisée que lorsque la broche est arrêtée. Vous pouvez indiquer si l’épingle est arrêtée à l’aide du code de contrôle IOCTL_PWM_PIN_IS_STARTED. Si la broche est arrêtée et que la polarité demandée est différente de la polarité de broche actuelle, la requête est terminée avec une valeur STATUS_INVALID_DEVICE_STATE.

La modification de la polarité pendant le démarrage d’une broche peut entraîner des perturbations sur certains contrôleurs PWM (Pulse Width Modulation). Si vous souhaitez modifier la polarité, arrêtez d’abord la broche, modifiez la polarité, puis démarrez la broche.

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.

  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • PARAMÈTRE_NON_VALIDE
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_START Démarre la génération du signal PWM (Pulse Width Modulation) sur une broche ou un canal. Pour vérifier si une broche est démarrée, utilisez IOCTL_PWM_PIN_IS_STARTED.

L’émission de ce IOCTL sur une broche ou un canal qui est déjà démarré n’a aucun effet, mais réussit.

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.

>
  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_STOP Arrête la génération du signal PWM (Pulse Width Modulation) sur une broche ou un canal. Pour vérifier si un port est démarré, utilisez IOCTL_PWM_PIN_IS_STARTED.

L’émission de ce IOCTL sur une broche ou un canal qui est déjà arrêté n’a aucun effet, mais réussit.

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.

  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_IS_STARTED Récupère l’état de génération de signal pour une broche ou un canal. Chaque broche a un état de démarrage ou d’arrêt en tant que structure PWM_PIN_IS_STARTED_OUTPUT. L’état démarré a une valeur booléenne true. L'état arrêté n'est pas vrai.

Par défaut, une broche est arrêtée lorsqu’elle est ouverte et revient à l’état arrêté lorsqu’elle est fermée ou libérée.

Irp-IoStatus.Status> est défini sur l’une des valeurs de la liste suivante.

  • STATUT_RÉUSSI
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL