web-dev-qa-db-fra.com

Pilote de périphérique IOCTL Linux

Quelqu'un peut-il m'expliquer,

  1. Qu'est-ce que IOCTL?
  2. A quoi cela sert?
  3. Comment puis-je l'utiliser?
  4. Pourquoi ne puis-je pas définir une nouvelle fonction faisant le même travail que IOCTL?
115
flashdisk

Un ioctl, qui signifie "contrôle entrée-sortie", est un type d'appel système spécifique à un périphérique. Il n’existe que peu d’appels système sous Linux (300 à 400), ce qui n’est pas suffisant pour exprimer toutes les fonctions uniques que les périphériques peuvent avoir. Ainsi, un pilote peut définir un ioctl qui permet à une application 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 ... 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 l'interface sysfs, dans laquelle vous configurez un fichier sous /sys/ et que vous le lisez/écrit pour obtenir des informations à partir du pilote. Un exemple de la façon de configurer ceci:

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 aurez alors un fichier pour votre périphérique dans /sys/, par exemple, /sys/block/myblk/version pour un pilote de bloc.

Netlink est une autre méthode d'utilisation plus importante, qui est une méthode IPC (communication entre processus) permettant de communiquer avec votre pilote via une interface de socket BSD. Ceci est utilisé, par exemple, par les pilotes WiFi. Vous communiquez ensuite avec celui-ci à partir de l'espace utilisateur à l'aide des bibliothèques libnl ou libnl3.

89
Inductiveload

La fonction ioctl 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 permettant de vérifier et de définir la famille de polices, la taille de la police, etc. ioctl peut être utilisée pour obtenir la police actuelle et en définir une nouvelle. Une application utilisateur utilise ioctl pour envoyer un code à une imprimante en lui demandant de renvoyer la police actuelle ou de définir une nouvelle police.

int ioctl(int fd, int request, ...)
  1. fd est un descripteur de fichier, celui renvoyé par open;
  2. request est le code de demande. Par exemple, GETFONT obtiendra la police actuelle de l'imprimante, SETFONT définira la police sur l'imprimante;
  3. le troisième argument est void *. Selon le deuxième argument, le troisième peut être ou ne pas être présent, par ex. si le deuxième argument est SETFONT, 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 requise pour générer un code de demande et le module de pilote de périphérique pour déterminer la configuration sur laquelle le périphérique doit être lu. L'application envoie le code de demande à l'aide de ioctl, puis utilise le code de demande dans le module du pilote de périphérique pour déterminer l'action à effectuer.

Un code de demande a 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 demande est SETFONT pour définir la police sur une imprimante, le transfert des données se fera 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 demande 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 prédéfinies, similaires à des fonctions.

1 ._IO(MAGIC, SEQ_NO) sont tous deux 8 bits, 0 à 255, par exemple. Disons que nous voulons mettre l'imprimante en pause. Cela ne nécessite pas de transfert de données. Donc, nous générerions le code de demande comme ci-dessous

#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM) 

et maintenant, utilisez 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.

  1. __IOW(MAGIC, SEQ_NO, TYPE)MAGIC et SEQ_NO sont les mêmes que ci-dessus, et TYPE donne le type de l'argument suivant, rappelle le troisième argument de ioctl est void *. W dans __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 que c'est 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 en type correct avant de l'utiliser. L'espace du noyau peut accéder à l'espace utilisateur et donc cela fonctionne. Les deux autres macros similaires à une fonction sont __IOR(MAGIC, SEQ_NO, TYPE) et __IORW(MAGIC, SEQ_NO, TYPE), où le flux de données se fera de l'espace noyau vers l'espace utilisateur et dans les deux sens, respectivement.

S'il vous plaît laissez-moi savoir si cela aide!

147
anukalp