wake-up-neo.net

IOCTL Linux-Gerätetreiber

Kann mir jemand erklären,

  1. Was ist IOCTL?
  2. Was wird es verwendet?
  3. Wie kann ich es benutzen?
  4. Warum kann ich keine neue Funktion definieren, die genauso funktioniert wie IOCTL?
115
flashdisk

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 möglicherweise haben. So kann ein Fahrer eine ioctl definieren, die es einer Userspace-Anwendung ermöglicht, Bestellungen zu senden. Ioctls sind jedoch nicht sehr flexibel und neigen dazu, ein bisschen unübersichtlich zu werden (Dutzende von "magischen Zahlen", die einfach funktionieren ... oder nicht), und können auch unsicher sein, wenn Sie einen Puffer in den Kernel übergeben - eine schlechte Handhabung kann brechen Dinge leicht.

Eine Alternative ist die Schnittstelle sysfs, in der Sie unter /sys/ Eine Datei einrichten und diese lesen/schreiben, um Informationen vom und zum Treiber abzurufen. 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 eine stärkere Nutzung ist Netlink. Hierbei handelt es sich um eine IPC (prozessübergreifende Kommunikation), mit der Sie über eine BSD-Socket-Schnittstelle mit Ihrem Treiber kommunizieren können. Dies wird beispielsweise von den WLAN-Treibern verwendet. Sie kommunizieren dann mit dem Benutzerbereich über die Bibliotheken libnl oder libnl3.

89
Inductiveload

Die Funktion ioctl ist nützlich, um einen Gerätetreiber zum Festlegen der Konfiguration auf dem Gerät zu implementieren. z.B. Ein Drucker mit Konfigurationsoptionen zum Überprüfen und Festlegen der Schriftfamilie, Schriftgröße usw. ioctl kann verwendet werden, um die aktuelle Schrift abzurufen und die Schrift auf eine neue zu setzen. Eine Benutzeranwendung verwendet ioctl, um einen Code an einen Drucker zu senden, der ihn auffordert, die aktuelle Schriftart zurückzugeben oder die Schriftart auf eine neue zu setzen.

int ioctl(int fd, int request, ...)
  1. fd ist ein Dateideskriptor, der von open zurückgegeben wird.
  2. request ist der Anforderungscode. Beispiel: GETFONT ruft die aktuelle Schriftart vom Drucker ab. SETFONT legt die Schriftart auf dem Drucker fest.
  3. das dritte Argument ist void *. In Abhängigkeit von dem zweiten Argument kann das dritte vorhanden sein oder nicht, z. Wenn das zweite Argument SETFONT ist, kann das dritte Argument der Schriftartname sein, z. B. "Arial";

int request Ist nicht nur ein Makro. Eine Benutzeranwendung muss einen Anforderungscode generieren und das Gerätetreibermodul muss 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 Anforderungscode 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 Anforderungscode SETFONT zum Festlegen der Schriftart auf einem Drucker lautet, erfolgt die Datenübertragung von der Benutzeranwendung zum Gerätetreibermodul (Die Benutzeranwendung sendet den Schriftartnamen "Arial" An den Drucker). . Wenn der Anforderungscode GETFONT lautet, erfolgt die Weiterleitung vom Drucker zur Benutzeranwendung.

Um einen Anforderungscode zu generieren, stellt Linux einige vordefinierte funktionsähnliche Makros bereit.

1 ._IO(MAGIC, SEQ_NO) beide sind 8 Bits, 0 bis 255, z. Nehmen wir an, wir möchten den Drucker anhalten. Dies erfordert keine Datenübertragung. Also würden wir den Anforderungscode wie folgt generieren

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

und benutze nun ioctl als

ret_val = ioctl(fd, PAUSE_PRIN);

Der entsprechende Systemaufruf im Treibermodul empfängt den Code und hält den Drucker an.

  1. __IOW(MAGIC, SEQ_NO, TYPE)MAGIC und SEQ_NO sind die gleichen wie oben, und TYPE gibt den Typ des nächsten Arguments an, erinnern Sie sich an das dritte Argument von ioctl ist void *. W in __IOW Zeigt an, dass der Datenfluss von der Benutzeranwendung zum Treibermodul erfolgt. Angenommen, 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)

des Weiteren,

char *font = "Arial";
ret_val = ioctl(fd, SETFONT, font); 

Nun ist font ein Zeiger, was bedeutet, dass es sich um eine Adresse handelt, die am besten als unsigned long Dargestellt wird. Daher erwähnt der dritte Teil von _IOW Den Typ als solchen. Außerdem wird diese Schriftartadresse an den entsprechenden Systemaufruf übergeben, der im Einheitentreibermodul als unsigned long Implementiert ist, und wir müssen sie vor der Verwendung in den richtigen Typ umwandeln. Der Kernelraum kann auf den Benutzerraum zugreifen, und daher funktioniert dies. Die anderen beiden funktionsähnlichen Makros sind __IOR(MAGIC, SEQ_NO, TYPE) und __IORW(MAGIC, SEQ_NO, TYPE), wobei der Datenfluss vom Kernel-Space zum User-Space und in beide Richtungen erfolgt.

Bitte lassen Sie mich wissen, ob dies hilft!

147
anukalp