Der ioctl
Die Funktion ist nützlich, um einen Gerätetreiber zu implementieren, um die Konfiguration auf dem Gerät festzulegen. z.B. ein Drucker mit Konfigurationsoptionen zum Überprüfen und Einstellen der Schriftartfamilie, Schriftgröße usw. ioctl
könnte verwendet werden, um die aktuelle Schriftart zu erhalten und die Schriftart auf eine neue festzulegen. Eine Benutzeranwendung verwendet ioctl
um einen Code an einen Drucker zu senden, der ihm mitteilt, die aktuelle Schriftart zurückzugeben oder die Schriftart auf eine neue einzustellen.
int ioctl(int fd, int request, ...)
fd
ist der Dateideskriptor, der vonopen
zurückgegeben wird;request
ist Anfragecode. B.GETFONT
erhält die aktuelle Schriftart vom Drucker,SETFONT
stellt die Schriftart auf dem Drucker ein;- das dritte Argument ist
void *
. Abhängig vom zweiten Argument kann das dritte vorhanden sein oder nicht, z. wenn das zweite ArgumentSETFONT
ist , kann das dritte Argument der Schriftartname sein, z. B."Arial"
;
int request
ist nicht nur ein Makro. Eine Benutzeranwendung ist erforderlich, um einen Anforderungscode zu generieren, und das Gerätetreibermodul, um zu bestimmen, mit welcher Konfiguration auf dem Gerät gespielt werden muss. Die Anwendung sendet den Anforderungscode mit ioctl
und verwendet dann den Anforderungscode im Gerätetreibermodul, um zu bestimmen, welche Aktion ausgeführt werden soll.
Ein Anfragecode besteht aus 4 Hauptteilen
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).
Wenn der Anfragecode SETFONT
ist Um die Schriftart auf einem Drucker festzulegen, erfolgt die Datenübertragung von der Benutzeranwendung zum Gerätetreibermodul (Die Benutzeranwendung sendet den Schriftartnamen "Arial"
an den Drucker). Wenn der Anforderungscode GETFONT
ist , Richtung vom Drucker zur Benutzeranwendung.
Um einen Anforderungscode zu generieren, bietet Linux einige vordefinierte funktionsähnliche Makros.
1._IO(MAGIC, SEQ_NO)
beide sind 8 Bit, 0 bis 255, z. Angenommen, wir möchten den Drucker anhalten. Dazu ist keine Datenübertragung erforderlich. Wir würden also den Anfragecode wie folgt generieren
#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM)
und verwenden Sie jetzt ioctl
als
ret_val = ioctl(fd, PAUSE_PRIN);
Der entsprechende Systemaufruf im Treibermodul empfängt den Code und hält den Drucker an.
__IOW(MAGIC, SEQ_NO, TYPE)
MAGIC
undSEQ_NO
sind die gleichen wie oben undTYPE
gibt den Typ des nächsten Arguments an, erinnern Sie sich an das dritte Argument vonioctl
istvoid *
. W in__IOW
gibt an, dass der Datenfluss von der Benutzeranwendung zum Treibermodul erfolgt. Nehmen wir als Beispiel an, wir möchten die Druckerschriftart auf"Arial"
setzen .
#define PRIN_MAGIC 'S'
#define SEQ_NO 1
#define SETFONT __IOW(PRIN_MAGIC, SEQ_NO, unsigned long)
weiter,
char *font = "Arial";
ret_val = ioctl(fd, SETFONT, font);
Jetzt font
ist ein Zeiger, was bedeutet, dass es sich um eine Adresse handelt, die am besten als unsigned long
dargestellt wird , daher der dritte Teil von _IOW
erwähnt den Typ als solchen. Außerdem wird diese Schriftartadresse an den entsprechenden Systemaufruf weitergegeben, der im Gerätetreibermodul als unsigned long
implementiert ist und wir müssen es vor der Verwendung in den richtigen Typ umwandeln. Der Kernel-Space kann auf den User-Space zugreifen und daher funktioniert dies. zwei weitere funktionsähnliche Makros sind __IOR(MAGIC, SEQ_NO, TYPE)
und __IORW(MAGIC, SEQ_NO, TYPE)
wobei der Datenfluss vom Kernel-Space zum User-Space bzw. in beide Richtungen erfolgt.
Bitte lassen Sie mich wissen, ob dies hilft!
Ein ioctl
, was "Eingabe-Ausgabe-Steuerung" bedeutet, ist eine Art gerätespezifischer Systemaufruf. Es gibt nur wenige Systemaufrufe in Linux (300-400), die nicht ausreichen, um alle einzigartigen Funktionen auszudrücken, die Geräte haben können. Ein Treiber kann also ein ioctl definieren, das es einer Userspace-Anwendung ermöglicht, ihm Befehle zu senden. Ioctls sind jedoch nicht sehr flexibel und neigen dazu, etwas unübersichtlich zu werden (Dutzende von "magischen Zahlen", die einfach funktionieren ... oder nicht) und können auch unsicher sein, wenn Sie einen Puffer an den Kernel übergeben - schlechte Handhabung kann brechen Dinge leicht.
Eine Alternative ist der sysfs
Schnittstelle, wo Sie eine Datei unter /sys/
anlegen und lesen/schreiben Sie das, um Informationen vom und zum Treiber zu erhalten. Ein Beispiel für die Einrichtung:
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);
Und während der Treiberinstallation:
device_create_file(dev, &dev_attr_version);
Sie hätten dann eine Datei für Ihr Gerät in /sys/
, zum Beispiel /sys/block/myblk/version
für einen Blocktreiber.
Eine andere Methode für stärkere Nutzung ist Netlink, eine IPC-Methode (Inter-Process-Communication), um mit Ihrem Treiber über eine BSD-Socket-Schnittstelle zu kommunizieren. Dies wird beispielsweise von den WLAN-Treibern verwendet. Sie kommunizieren dann mit ihm aus dem Userspace mit dem libnl
oder libnl3
Bibliotheken.