Quelqu'un peut-il m'expliquer,
- C'est quoi
IOCTL
? - A quoi cela sert?
- Comment puis-je l'utiliser?
- Pourquoi ne puis-je pas définir une nouvelle fonction qui fait le même travail que
IOCTL
?
Quelqu'un peut-il m'expliquer,
IOCTL
?IOCTL
?Réponses:
An ioctl
, qui signifie «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 sont pas suffisants 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 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 juste ... 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' sysfs
interface, où vous configurez un fichier sous /sys/
et que vous lisez / écrivez pour obtenir des informations depuis et vers le pilote. Un exemple de la façon de configurer cela:
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 périphérique /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 de socket BSD. Ceci est utilisé, par exemple, par les pilotes WiFi. Vous communiquez ensuite avec lui depuis l'espace utilisateur à l'aide des bibliothèques libnl
ou libnl3
.
La ioctl
fonction est utile pour implémenter un pilote de périphérique pour définir la configuration sur le périphérique. par exemple, une imprimante qui a des options de configuration pour vérifier et définir la famille de polices, la taille de la police, etc. ioctl
pourrait être utilisée pour obtenir la police actuelle ainsi que pour définir la police sur une nouvelle police. 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 police.
int ioctl(int fd, int request, ...)
fd
est le descripteur de fichier, celui renvoyé par open
;request
est le code de demande. par exemple GETFONT
obtiendra la police actuelle de l'imprimante, SETFONT
définira la police sur l'imprimante;void *
. Selon le deuxième argument, le troisième peut être présent ou non, par exemple, 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 avec quelle configuration sur le périphérique doit être joué. L'application envoie le code de demande en utilisant ioctl
, puis utilise le code de demande dans le module de pilote de périphérique pour déterminer l'action à effectuer.
Un code de demande comprend 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
de 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 "Arial"
de la police à l'imprimante). Si le code de demande est GETFONT
, la direction va de l'imprimante vers l'application utilisateur.
Afin de générer un code de requête, Linux fournit des macros de type fonction prédéfinies.
1. les _IO(MAGIC, SEQ_NO)
deux sont 8 bits, de 0 à 255, par exemple, 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 demande comme ci-dessous
#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM)
et maintenant utiliser ioctl
comme
ret_val = ioctl(fd, PAUSE_PRIN);
L'appel système correspondant dans le module de pilote recevra le code et mettra l'imprimante en pause.
__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, rappelez-vous le troisième argument de ioctl
est void *
. W in __IOW
indique que le flux de données va de l'application utilisateur au module 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 comme unsigned long
, d'où la troisième partie du _IOW
type de mentions 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 unsigned long
et nous devons le convertir au type approprié avant de l'utiliser. L'espace noyau peut accéder à l'espace utilisateur et donc cela fonctionne. les deux autres macros fonctionnelles sont __IOR(MAGIC, SEQ_NO, TYPE)
et __IORW(MAGIC, SEQ_NO, TYPE)
où le flux de données sera de l'espace noyau à l'espace utilisateur et dans les deux sens respectivement.
Faites-moi savoir si cela vous aide!