Le ioctl
La fonction est utile pour implémenter un pilote de périphérique afin de définir la configuration sur le périphérique. par exemple. une imprimante disposant d'options de configuration pour vérifier et définir la famille de polices, la taille de police, etc. ioctl
peut être utilisé pour obtenir la police actuelle ainsi que pour définir la police sur une nouvelle. Une application utilisateur utilise ioctl
pour envoyer un code à une imprimante lui indiquant de renvoyer la police actuelle ou de définir la police sur une nouvelle.
int ioctl(int fd, int request, ...)
fd
est le descripteur de fichier, celui renvoyé paropen
;request
est le code de requête. par exempleGETFONT
obtiendra la police actuelle de l'imprimante,SETFONT
définira la police sur l'imprimante ;- le troisième argument est
void *
. Selon le deuxième argument, le troisième peut être présent ou non, par ex. si le deuxième argument estSETFONT
, le troisième argument peut être le nom de la police tel que"Arial"
;
int request
n'est pas qu'une macro. Une application utilisateur est nécessaire pour générer un code de demande et le module de pilote de périphérique pour déterminer avec quelle configuration sur le périphérique doit être joué. L'application envoie le code de requête en utilisant ioctl
puis utilise le code de requête dans le module du pilote de périphérique pour déterminer l'action à effectuer.
Un code de requête comporte 4 parties principales
1. A Magic number - 8 bits
2. A sequence number - 8 bits
3. Argument type (typically 14 bits), if any.
4. Direction of data transfer (2 bits).
Si le code de la requête est SETFONT
pour définir la police sur une imprimante, la direction du transfert de données sera de l'application utilisateur au module de pilote de périphérique (l'application utilisateur envoie le nom de la police "Arial"
à l'imprimante). Si le code de requête est GETFONT
, le sens va de l'imprimante à l'application utilisateur.
Afin de générer un code de requête, Linux fournit des macros de type fonction prédéfinies.
1._IO(MAGIC, SEQ_NO)
les deux sont 8 bits, 0 à 255, par ex. disons que nous voulons mettre l'imprimante en pause. Cela ne nécessite pas de transfert de données. Nous générerions donc le code de requête comme ci-dessous
#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM)
et utilisez maintenant ioctl
comme
ret_val = ioctl(fd, PAUSE_PRIN);
L'appel système correspondant dans le module du pilote recevra le code et mettra l'imprimante en pause.
__IOW(MAGIC, SEQ_NO, TYPE)
MAGIC
etSEQ_NO
sont les mêmes que ci-dessus, etTYPE
donne le type du prochain argument, rappelle le troisième argument deioctl
estvoid *
. W en__IOW
indique que le flux de données va de l'application utilisateur au module de pilote. Par exemple, supposons que nous voulions définir la police de l'imprimante sur"Arial"
.
#define PRIN_MAGIC 'S'
#define SEQ_NO 1
#define SETFONT __IOW(PRIN_MAGIC, SEQ_NO, unsigned long)
plus loin,
char *font = "Arial";
ret_val = ioctl(fd, SETFONT, font);
Maintenant font
est un pointeur, ce qui signifie qu'il s'agit d'une adresse mieux représentée par unsigned long
, d'où la troisième partie de _IOW
mentionne le type en tant que tel. En outre, cette adresse de police est transmise à l'appel système correspondant implémenté dans le module de pilote de périphérique sous la forme unsigned long
et nous devons le convertir au bon type avant de l'utiliser. L'espace noyau peut accéder à l'espace utilisateur et cela fonctionne donc. les deux autres macros de type fonction sont __IOR(MAGIC, SEQ_NO, TYPE)
et __IORW(MAGIC, SEQ_NO, TYPE)
où le flux de données ira de l'espace noyau à l'espace utilisateur et dans les deux sens respectivement.
N'hésitez pas à me faire savoir si cela vous aide !
Un ioctl
, ce qui signifie que le "contrôle d'entrée-sortie" est une sorte d'appel système spécifique à l'appareil. Il n'y a que quelques appels système sous Linux (300-400), qui ne suffisent pas à exprimer toutes les fonctions uniques que les périphériques peuvent avoir. Ainsi, un pilote peut définir un ioctl qui permet à une application de l'espace utilisateur de lui envoyer des commandes. Cependant, les ioctls ne sont pas très flexibles et ont tendance à être un peu encombrés (des dizaines de "nombres magiques" qui fonctionnent simplement... ou pas), et peuvent également être peu sûrs, car vous passez un tampon dans le noyau - une mauvaise manipulation peut casser les choses facilement.
Une alternative est le sysfs
interface, où vous configurez un fichier sous /sys/
et lire/écrire cela pour obtenir des informations de et vers le conducteur. Un exemple de configuration :
static ssize_t mydrvr_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", DRIVER_RELEASE);
}
static DEVICE_ATTR(version, S_IRUGO, mydrvr_version_show, NULL);
Et pendant la configuration du pilote :
device_create_file(dev, &dev_attr_version);
Vous auriez alors un fichier pour votre appareil en /sys/
, par exemple, /sys/block/myblk/version
pour un pilote de bloc.
Une autre méthode pour une utilisation plus intensive est netlink, qui est une méthode IPC (communication inter-processus) pour parler à votre pilote via une interface socket BSD. Ceci est utilisé, par exemple, par les pilotes WiFi. Vous communiquez ensuite avec lui depuis l'espace utilisateur en utilisant le libnl
ou libnl3
bibliothèques.