Le Sous-système IIO de Linux pour les Pilotes de Capteurs

Introduction au Sous-système IIO

Avec la prolifération des appareils mobiles, de l'IoT et de l'IIoT, la demande de capteurs a explosé. Des capteurs tels que les accéléromètres, les capteurs de lumière, les gyroscopes, les baromètres et les magnétomètres, couramment présents dans les smartphones et les appareils portables, sont essentiellement des convertisseurs analogique-numérique (CAN). Ils transmettent leurs données brutes converties via des interfaces telles que I2C ou SPI.

Pour gérer cette complexité croissante, le noyau Linux a introduit le sous-système Industrial I/O (IIO). Le sous-système IIO est conçu pour les périphériques dont le composant fondamental est un CAN, transformant les signaux analogiques bruts en données numériques qui sont ensuite transmises via des interfaces de communication standard.

Architecture du Sous-système IIO

  • IIO sysfs : Fournit une interface pour que l'espace utilisateur accède et configure les périphériques IIO.
  • IIO Core : Gère l'allocation, l'initialisation et l'enregistrement des périphériques IIO, des déclencheurs IIO et des tampons IIO. Les fichiers principaux incluent industrialio-core.c, industrialio-buffer.c et industrialio-event.c.
  • IIO Driver : Ce sont les pilotes spécifiques à chaque périphérique IIO.

Structures de Données Clés

struct iio_dev : Description du Périphérique IIO

La structure iio_dev décrit un périphérique IIO spécifique. Elle contient des informations essentielles telles que l'identifiant du périphérique, le module du pilote, les modes de fonctionnement pris en charge, les tampons, les déclencheurs, la liste des canaux et le nom du périphérique.


struct iio_dev {
    int id;
    struct module *driver_module;
    int modes; // Modes pris en charge par le périphérique
    int currentmode; // Mode actuel du périphérique
    struct device dev;
    struct iio_event_interface *event_interface;
    struct iio_buffer *buffer; // Tampon
    struct list_head buffer_list; // Liste des tampons correspondants
    int scan_bytes; // Nombre d'octets capturés et fournis au tampon
    struct mutex mlock;
    const unsigned long *available_scan_masks; // Masques de balayage disponibles
    // ... autres membres
    struct iio_trigger *trig; // Déclencheur IIO actuel
    struct iio_poll_func *pollfunc;
    struct iio_chan_spec const *channels; // Liste des canaux du périphérique IIO
    int num_channels; // Nombre de canaux du périphérique IIO
    // ... autres membres
    const char *name;
    const struct iio_info *info;
    // ... autres membres
};

Les modes de fonctionnement sont définis comme suit :

  • INDIO_DIRECT_MODE : Accès via l'interface sysfs.
  • INDIO_BUFFER_TRIGGERED : Prise en charge du déclenchement de tampon matériel.
  • INDIO_BUFFER_SOFTWARE : Prise en charge du déclenchement de tampon logiciel.
  • INDIO_BUFFER_HARDWARE : Prise en charge des tampons matériels.

struct iio_buffer_setup_ops

Ces opérations sont appelées lors de l'activation ou de la désactivation des tampons, permettant une configuration avant et après ces actions.


struct iio_buffer_setup_ops {
    int (*preenable)(struct iio_dev *); // Appelée avant l'activation du tampon
    int (*postenable)(struct iio_dev *); // Appelée après l'activation du tampon
    int (*predisable)(struct iio_dev *); // Appelée avant la désactivation du tampon
    int (*postdisable)(struct iio_dev *); // Appelée après la désactivation du tampon
    bool (*validate_scan_mask)(struct iio_dev *indio_dev, const unsigned long *scan_mask); // Vérifie la validité du masque de balayage
};

struct iio_info : Attributs et Fonctions d'Implémentation

La structure iio_info contient les attributs et les fonctions d'implémentation pour un périphérique IIO spécifique. Elle définit les opérations de lecture et d'écriture brutes, la gestion des événements et la validation des déclencheurs.


struct iio_info {
    struct module *driver_module;
    // ... autres membres
    int (*read_raw)(struct iio_dev *indio_dev,
            struct iio_chan_spec const *chan,
            int *val, int *val2, long mask); // Fonction principale de lecture/écriture
    // ... autres fonctions de lecture/écriture et de gestion d'événements
    int (*validate_trigger)(struct iio_dev *indio_dev,
                struct iio_trigger *trig);
    // ... autres membres
};

Les fonctions read_raw et write_raw utilisent val et val2 pour représenter les données. Le noyau Linux ne gérant pas directement les opérations en virgule flottante, val2 est utilisé pour représenter la partie fractionnaire, multipliée par une puissance de 10 prédéfinie par les macros suivantes :

  • IIO_VAL_INT : Valeur entière simple.
  • IIO_VAL_INT_PLUS_MICRO : La partie fractionnaire est multipliée par 1 000 000.
  • IIO_VAL_INT_PLUS_NANO : La partie fractionnaire est multipliée par 1 000 000 000.
  • IIO_VAL_FRACTIONAL : La valeur est val / val2.
  • IIO_VAL_FRACTIONAL_LOG2 : La valeur est val >> val2.

Le paramètre mask est utilisé pour spécifier quel type d'information est lu ou écrit (par exemple, IIO_CHAN_INFO_RAW pour les données brutes, IIO_CHAN_INFO_SCALE pour l'échelle/résolution).

struct iio_chan_spec : Attributs du Canal

Chaque canal d'un périphérique IIO est décrit par struct iio_chan_spec. Cette structure définit le type de canal (tension, courant, accélération, etc.), l'adresse du registre, le format des données balayées et les masques d'informations partagées.


enum iio_chan_type {
    IIO_VOLTAGE, IIO_CURRENT, IIO_ACCEL, IIO_ANGL_VEL,
    IIO_TEMP, // Et bien d'autres...
};

struct iio_chan_spec {
    enum iio_chan_type type; // Type de canal
    int channel;
    int channel2; // Peut être un modificateur de canal
    unsigned long address; // Adresse du registre du périphérique
    int scan_index; // Index dans le tampon de balayage
    struct {
        char sign; // 's' pour signé, 'u' pour non signé
        u8 realbits; // Nombre de bits de données réels
        u8 storagebits; // Nombre total de bits de stockage
        u8 shift; // Décalage à droite
        u8 repeat;
        enum iio_endian endianness; // Boutisme (BE/LE)
    } scan_type;
    long info_mask_separate; // Masque d'informations spécifiques à ce canal
    long info_mask_shared_by_type; // Masque d'informations partagées par type de canal
    // ... autres masques d'information
    unsigned modified:1; // Indique si channel2 est un modificateur
    unsigned indexed:1; // Indique si channel est un index
    unsigned output:1; // Indique si c'est un canal de sortie
    // ... autres drapeaux
};

Les modificateurs de canal comme IIO_MOD_X, IIO_MOD_Y, IIO_MOD_Z sont utilisés pour différencier les axes des capteurs multi-axes.

Les masques d'information (info_mask_separate, info_mask_shared_by_type, etc.) permettent de définir quelles propriétés sont accessibles pour un canal donné et si elles sont partagées avec d'autres canaux.

struct iio_trigger : Déclenchement de l'Acquisition de Données

Les déclencheurs IIO initient l'acquisition de données en réponse à un événement, tel qu'une interruption de fin de données, une interruption périodique ou une opération d'écriture sysfs.


struct iio_trigger {
    const struct iio_trigger_ops *ops;
    // ... autres membres
};

struct iio_buffer : Stockage des Données Acquises

Le tampon IIO stocke les données acquises avant qu'elles ne soient lues par l'espace utilisateur. Il gère la taille, le nombre d'octets par donnée et l'interface d'accès.

API du Sous-système IIO

Enregistrement et Désenregistrement de iio_dev

Les fonctions iio_device_alloc et devm_iio_device_alloc sont utilisées pour allouer la structure iio_dev. L'enregistrement se fait avec iio_device_register ou devm_iio_device_register, et le désenregistrement avec iio_device_unregister ou devm_iio_device_unregister.

L'enregistrement via sysfs crée les entrées correspondantes dans /sys/bus/iio/devices/, permettant l'interaction avec le périphérique.

Initialisation du Sous-système IIO

L'initialisation du sous-système IIO implique l'enregistrement du bus IIO, l'allocation d'un numéro de périphérique caractère, et la création du répertoire debugfs pour IIO.

Exemple de Pilote : ICM20608

L'exemple de pilote pour le capteur ICM20608 (accéléromètre, gyroscope, température) illustre l'utilisation du sous-système IIO.

Configuration du Noyau

Pour utiliser IIO, il faut activer les options correspondantes dans la configuration du noyau Linux :

  • Device Drivers -> Industrial I/O support
  • Cocher Enable buffer support within IIO
  • Cocher Industrial I/O buffering based on kfifo

Analyse du Code du Pilote (ICM20608)

Le pilote ICM20608 définit les canaux pour la température, le gyroscope (axes X, Y, Z) et l'accéléromètre (axes X, Y, Z). Chaque canal est configuré avec son type, son masque d'information et son type de balayage.

  • struct icm20608_dev : Structure privée du pilote contenant le pointeur vers le périphérique SPI, la configuration du regmap et un mutex.
  • icm20608_channels[] : Tableau de structures iio_chan_spec décrivant chaque canal du capteur.
  • Fonctions *_read_raw et *_write_raw : Implémentent la logique pour lire et écrire les données brutes, l'échelle et les biais de calibration via l'interface sysfs. Les échelles (résolutions) sont définies en utilisant les macros IIO_VAL_INT_PLUS_MICRO et IIO_VAL_INT_PLUS_NANO.
  • Fonction icm20608_write_raw_get_fmt : Spécifie comment les données écrites par l'espace utilisateur doivent être interprétées (par exemple, par quel facteur multiplier les valeurs fractionnaires).
  • Fonction probe : Initialise le périphérique, enregistre le pilote IIO et configure le regmap pour la communication SPI.
  • Fonction remove : Nettoie les ressources lors du retrait du pilote.

Test du Pilote ICM20608

Après compilation et chargement du module, les périphériques IIO apparaissent dans /sys/bus/iio/devices/. Pour l'ICM20608, un appareil comme iio:device1 sera créé. Les fichiers tels que in_accel_x_raw, in_accel_scale, in_anglvel_x_raw, etc., permettent de lire et écrire les données et les configurations du capteur.

Un programme d'application C peut lire ces fichiers pour obtenir les données brutes et calculées du capteur.

Exemple de Pilote : ADC VF610

L'exemple du pilote pour le contrôleur ADC VF610 sur les plateformes NXP i.MX6ULL illustre l'utilisation du sous-système IIO pour les convertisesurs analogique-numérique.

Description du DTS

Le Device Tree Source (DTS) décrit le matériel ADC, y compris son adresse mémoire, ses interruptions, ses horloges, le nombre de canaux et les ressources d'alimentation (régulateurs).

Activation du Pilote ADC

Le pilote doit être activé dans la configuration du noyau :

  • Device Drivers -> Industrial I/O support -> Analog to digital converters -> Freescale vf610 ADC driver

Analyse du Code du Pilote (VF610 ADC)

Le pilote vf610_adc.c :

  • Fonction probe : Alloue et initialise la structure iio_dev, récupère les ressources matérielles (registres, interruptions, horloges, alimentation) via le DTS, configure le contrôleur ADC et enregistre le périphérique IIO.
  • Fonction vf610_read_raw : Implémente la logique pour lire les données brutes du CAN. Les valeurs sont généralement exprimées en tension. L'échelle (résolution) est fournie pour convertir les données brutes en tension réelle.

Test de l'ADC VF610

Les données de l'ADC VF610 sont accessibles via sysfs dans /sys/bus/iio/devices/iio:device0/. Des fichiers comme in_voltage1_raw (données brutes du canal 1) et in_voltage_scale (résolutoin en mV) permettent d'interagir avec le CAN.

Une application C peut lire ces fichiers pour obtenir la tension mesurée par l'ADC. Il est important de noter que le calcul final de la tension réelle implique des opérations en virgule flottante, qui doivent être gérées correctement lors de la compilation de l'application.

Étiquettes: IIO Linux kernel Drivers Sensors ADC

Publié le 2 août à 16h59