![]() |
Pilotes de périphériques pour le noyau Linux |
Le sous-système input du noyau permet de gérer tous les périphériques capturant les actions de l’utilisateur : notamment le clavier, la souris, mais également les manettes de jeu, les boutons divers, etc.
Il se décompose principalement en trois parties :
input_dev
Chaque pilote de périphérique qui veut s’enregistrer auprès du sous-système input est décrit par la structure struct input_dev
définie dans include/linux/input.h.
Cette structure, complexe, doit être allouée et initialisée à l’aide de la fonction :
Comme pour les autres fonctions devm_*
, la structure sera automatiquement désallouée lorsque le périphérique dev
disparaîtra. L’ancienne fonction non automatique est toujours disponible :
Dans ce cas, la structure doit être désallouée explicitement en appelant la fonction :
Nous allons maintenant décrire les principaux champs de la structure struct input_dev
.
const char *name
: nom du périphérique
struct input_id id
: identifiant du périphérique (bustype
indique le bus sur lequel est connecté le périphérique (voir liste dans include/uapi/linux/input.h), vendor
et product
indiquent l’identifiant du fabricant et du produit)
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]
: champ de bits indiquant les types d’événements susceptibles d’être générés par le périphérique. La liste des événements et leur signification est disponible dans Documentation/input/event-codes.rst. Retenons EV_KEY
(touches), EV_REL
(valeurs sur un axe avec changements relatifs, i.e. par rapport à la position précédente, exemple souri), EV_ABS
(valeurs absolues sur un axe, exemple joystick) et EV_SW
(switch on/off).
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]
: pour les périphériques ayant indiqué EV_KEY
dans le champ evbit
, ce champ de bits indique les touches présentes (voir les symboles KEY_
et BTN_
dans include/linux/input.h).
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]
: pour les périphériques ayant indiqué EV_REL
dans le champ evbit
, ce champ de bits indique les axes présents (voir les symboles REL_
dans include/linux/input.h).
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]
: pour les périphériques ayant indiqué EV_ABS
dans le champ evbit
, ce champ de bits indique les axes présents (voir les symboles ABS_
dans include/linux/input.h).
struct input_absinfo *absinfo
: pour les périphériques ayant indiqué EV_ABS
dans le champ evbit
, cette structure décrit les propriétés de chacun des axes (valeur minimale, maximale, marge de bruit et zone neutre). Ce champ peut être manipulé à l’aide de la fonction :
void input_set_abs_params(struct input_dev *dev, unsigned int axis,
int min, int max, int fuzz, int flat);
int (*open)(struct input_dev *dev)
: pointeur vers une fonction qui sera appelée la première fois qu’une application ouvrira le périphérique. Cette fonction n’est pas nécessaire mais elle permet de différer certaines actions (mise en place du polling, activation des interruptions en provenance du périphérique, etc.).
void (*close)(struct input_dev *dev)
: pointeur vers une fonction qui sera appelée lorsque plus aucune application n’utilise le périphérique. De même que pour open
, cette fonction n’est pas obligatoire mais elle permet par exemple de désactiver les interruptions en provenance du périphérique quand il n’est pas utilisé.
Une fois la structure struct input_dev
correctement renseignée, il faut appeler la fonction :
pour enregistrer le pilote auprès du sous-système input.
L’opération inverse est réalisée à l’aide de la fonction :
Lorsqu’un événement survient, il faut le signaler à l’aide des fonctions suivantes en fonction du type d’événement.
Pour une touche (EV_KEY
) :
Pour une valeur relative (EV_REL
) :
Pour une valeur absolue (EV_ABS
) :
À chaque fois, code
indique la touche ou l’axe concerné (KEY_
, BTN_
, REL_
, ABS_
) et value
la valeur (booléen dans le cas d’une touche ou valeur relative ou absolue selon le cas).
Une fois les différents événements rapportés, il faut ensuite impérativement appeler la fonction :
D’autres fonctions sont disponibles ainsi qu’une fonction plus générique : void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
.
Le fichier Documentation/input/input-programming.rst contient un exemple documenté d’un pilote de périphérique input.
Il existe une sous-classe qui permet de faciliter l’écriture d’un pilote pour un périphérique input qui nécessite d’être interrogé (polled) régulièrement, i.e. qui n’est pas capable de générer une interruption lorsqu’un événement survient.
Un tel périphérique est représenté par la structure struct input_polled_dev
définie dans include/linux/input-polldev.h.
struct input_polled_dev {
void *private;
void (*open)(struct input_polled_dev *dev);
void (*close)(struct input_polled_dev *dev);
void (*poll)(struct input_polled_dev *dev);
unsigned int poll_interval; /* msec */
unsigned int poll_interval_max; /* msec */
unsigned int poll_interval_min; /* msec */
struct input_dev *input;
/* ... */
}
Cette structure doit être allouée et initialisée à l’aide d’une des deux fonctions suivantes :
struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev);
struct input_polled_dev *input_allocate_polled_device(void);
Elle peut être désallouée à l’aide de la fonction :
struct input_polled_dev
Les fonctions open
et close
fonctionnent de la même manière que celles de la structure input_dev
.
La fonction poll
est importante. C’est cette fonction qui sera appelée régulièrement par le sous-système input et qui est en charge d’interroger le périphérique et de générer les événements correspondants si nécessaire.
poll_interval
indique la fréquence (par défaut 500 ms) à laquelle la fonction poll
est appelée.
Les champs de la structure struct input_dev *input
(et notamment name
, evbit
, keybit
, relbit
et/ou absbit
) doivent aussi être remplis avant l’enregistrement.
Une fois la structure correctement remplie, le périphérique peut être enregistré auprès du sous-système input à l’aide de la fonction :
Le périphérique peut être désenregistré à l’aide de la fonction :
Au niveau utilisateur, les périphériques peuvent être accédés via plusieurs interfaces en fonction de leur type (une interface est spécialisée pour les claviers, une autre pour les souris, etc.). Une interface générique evdev
permet de récupérer simplement tous les événements.
Les périphériques sont représentés par un fichier spécial /dev/input/eventX
.
Chaque lecture dans un de ces fichiers renvoie une structure struct input_event
décrivant un événement :
struct input_event {
struct timeval time;
unsigned short type;
unsigned short code;
unsigned int value;
};
Ces fichiers spéciaux supportent les lectures bloquantes (read
bloque en attendant un événement) et non bloquante (read
retourne tout de suite si aucun événement ne s’est produit). Il est également possible d’utiliser select
.
Un petit utilitaire, evtest
(installé sur les maquettes de TP), permet de visualiser facilement tous les événements.
Vous allez faire en sorte que le pilote de l’accéléromètre s’enregistre comme un périphérique input disposant de trois axes (X
, Y
et Z
).
Une première variante de votre pilote utilisera les interruptions et s’enregistrera comme un périphérique input “classique”. La seconde variante utilisera input_polled_dev
sans interruptions.
© Copyright 2020 Guillaume Duc. Le contenu de cette page est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International (à l'exception des exemples de code tirés du noyau Linux et qui sont distribués sous leurs licences d'origine).