Pilote de périphérique ioctl Linux

Quelqu'un peut-il m'expliquer,

  1. qu'est-Ce que IOCTL?
  2. à Quoi sert-il?
  3. Comment puis-je l'utiliser?
  4. Pourquoi ne puis-je pas définir une nouvelle fonction qui fait le même travail que IOCTL?
84
demandé sur Abhijeet Kasurde 2013-04-04 13:48:05

2 réponses

Un ioctl, qui signifie "contrôle entrée-sortie" est une sorte d'appel système spécifique au périphérique. Il n'y a que quelques appels système sous Linux (300-400), qui ne sont pas suffisants pour exprimer toutes les fonctions uniques que les périphériques peuvent avoir. Ainsi, un conducteur peut définir un ioctl qui permet une application utilisateur d'envoyer des commandes. Cependant, les ioctl ne sont pas très flexibles et ont tendance à être un peu encombrés (des dizaines de "nombres magiques" qui fonctionnent juste... ou non), et peut également être non sécurisé, lorsque vous passez un tampon dans le noyau - mauvaise manipulation peut casser les choses facilement.

Une alternative est l'interface sysfs, où vous configurez un fichier sous /sys/ et lisez/écrivez cela pour obtenir des informations depuis et vers le 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 serait alors un fichier pour votre appareil dans /sys/, par exemple, /sys/block/myblk/version pour un pilote de bloc.

Une autre méthode pour une utilisation plus lourde est netlink, qui est une méthode IPC (inter-process communication) à laquelle parler votre pilote sur une interface de socket BSD. Ceci est utilisé, par exemple, par les pilotes WiFi. Vous communiquez ensuite avec elle depuis l'espace utilisateur en utilisant les bibliothèques libnl ou libnl3.

68
répondu Inductiveload 2014-02-18 17:52:40

ioctl la fonction est utile lorsque l'on implémente un pilote de périphérique pour définir la configuration sur le périphérique. par exemple, une imprimante dispose d'options de configuration pour vérifier et définir la police, taille de police, etc. ioctl pourrait être utilisé pour obtenir la police actuelle ainsi que pour définir la police sur une autre. Dans l'application utilisateur, utilisez ioctl pour envoyer un code à une imprimante lui indiquant de retourner la police actuelle ou de définir une police sur une nouvelle.

int ioctl(int fd, int request, ...)
  1. {[9] } est un descripteur de fichier, celui renvoyé par ouvrir
  2. request est le code de requête. par exemple, GETFONT obtiendra la police actuelle de l'imprimante, SETFONT définira la police sur une imprimante.
  3. le troisième argument est void *. Selon le deuxième argument, le troisième peut ou non être présent. par exemple, si le deuxième argument est SETFONT, le troisième argument peut donner le nom de la police comme ARIAL.

Alors maintenant int request n'est pas seulement une macro, on est nécessaire pour générer du code de requête à utiliser par l'application utilisateur et le module de pilote de périphérique pour déterminer lequel configuration sur l'appareil doit être joué avec. On envoie un code de requête à l'aide de ioctl à partir de l'application utilisateur, puis utilise le code de requête dans le module 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 requête est SETFONT pour définir la police sur une imprimante, la direction pour le transfert de données sera de l'application utilisateur au module de pilote de périphérique. L'utilisateur envoie le nom de police Arial à l'imprimante. Si le code de demande est GETFONT, la direction est de l'imprimante à application utilisateur.

Pour générer du code de requête, Linux fournit des fonctions prédéfinies telles que des macros.

1.{[13] } les deux sont 8 bits, 0 à 255, par exemple disons que nous voulons mettre en pause l'imprimante. Cela ne nécessite pas de transfert adata. Nous générerions donc le code de demande comme ci-dessous

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

Maintenant, utilisez ioctl comme

    ret_val = ioctl(fd, PAUSE_PRIN);

L'appel système correspondant dans le module 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, mais la troisième partie donne le type de l'argument suivant, rappelons le troisième argument de ioctl est void *. W dans __IOW indique que la direction des données va de l'application utilisateur au module pilote. Prenons un exemple, Supposons que l'on dise à l'imprimante de définir la police sur Arial.

    #define PRIN_MAGIC 'S'
    #define SEQ_NO 1
    #define SETFONT __IOW(PRIN_MAGIC, SEQ_NO, unsigned long)
    

En Outre,

    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 passée à appel système correspondant implémenté dans le module de pilote de périphérique comme unsigned long et nous devons le lancer au type approprié avant de l'utiliser. L'espace noyau peut accéder à l'espace utilisateur et donc cela fonctionne. les deux autres fonctions comme les macros sont __IOR(MAGIC, SEQ_NO, TYPE) et __IORW(MAGIC, SEQ_NO, TYPE) où la direction du flux de données sera de l'espace noyau à l'espace utilisateur et dans les deux sens respectivement.

Faites-moi savoir si cela aide!

117
répondu anukalp 2016-05-09 19:17:58