Grundkenner Treiber unter Linux

Grundkenner Treiber unter Linux
Wir werden die Linux -Art der Implementierung des Charaktertreibers durchlaufen. Wir werden zuerst versuchen zu verstehen, was der Charakter -Treiber ist und wie das Linux -Framework es uns ermöglicht, den Charakter -Treiber hinzuzufügen. Danach werden wir die Beispiel für den Beispiel -Test der Benutzer Speicherplatz durchführen. Diese Testanwendung verwendet den vom Treiber aufgedeckten Geräteknoten zum Schreiben und Lesen der Daten aus dem Kernel -Speicher.

Beschreibung

Beginnen wir die Diskussion mit dem Charakter -Treiber unter Linux. Kernel kategorisiert die Treiber in drei Kategorien:

Charakterfahrer - Dies sind die Treiber, die nicht zu viel Daten haben, um damit umzugehen. Nur wenige Beispiele für Charakter -Treiber sind Touchscreen -Treiber, UART -Treiber usw. Dies alle sind die Charakter -Treiber, da die Datenübertragung durch Zeichen durch Zeichen durchgeführt wird.

Blocktreiber - Dies sind die Treiber, die mit zu vielen Daten zu tun haben. Die Datenübertragung erfolgt blockiert blockiert, da zu viel Daten übertragen werden müssen. Beispiel für Blocktreiber sind SATA, NVME usw.

Netzwerktreiber - Dies sind die Treiber, die in der Netzwerkgruppe der Fahrer funktionieren. Hier erfolgt die Datenübertragung in Form von Datenpaketen. Funkfahrer wie Atheros fallen in diese Kategorie.

In dieser Diskussion werden wir uns nur auf den Charakter -Treiber konzentrieren.

Als Beispiel werden wir die einfachen Lese-/Schreibvorgänge nehmen, um den grundlegenden Charakter -Treiber zu verstehen. Im Allgemeinen hat jeder Gerätetreiber diese beiden Mindestvorgänge. Zusätzlicher Betrieb kann geöffnet sein, schließen, ioctl usw. In unserem Beispiel hat unser Fahrer den Speicher im Kernelraum. Dieser Speicher wird vom Gerätetreiber zugewiesen und kann als Gerätespeicher betrachtet werden. Der Treiber erstellt die Geräteschnittstelle im Verzeichnis /dev, das von den Benutzerraumprogrammen verwendet werden kann, um auf den Treiber zuzugreifen und die vom Treiber unterstützten Vorgänge auszuführen. Für das UserSpace -Programm sind diese Vorgänge genau wie bei allen anderen Dateivorgängen. Das Benutzerraumprogramm muss die Gerätedatei öffnen, um die Instanz des Geräts zu erhalten. Wenn der Benutzer den Lesevorgang ausführen möchte, kann der Read -System -Aufruf dazu verwendet werden. Wenn der Benutzer den Schreibvorgang ausführen möchte, kann der Schreibsystemaufruf verwendet werden, um den Schreibvorgang zu erreichen.

Charakterfahrer

Betrachten wir, um den Charakter -Treiber mit den Lesen/Schreibdatenvorgängen zu implementieren.

Wir beginnen mit der Instanz der Gerätedaten. In unserem Fall handelt es sich um "struct cdrv_device_data".

Wenn wir die Felder dieser Struktur sehen, haben wir CDEV, Gerätepuffer, Puffergröße, Klasseninstanz und Geräteobjekt. Dies sind die Mindestfelder, auf denen wir den Charakter -Treiber implementieren sollten. Es hängt von dem Implementierer ab, auf dem zusätzliche Felder hinzufügen möchten, um die Funktion des Treibers zu verbessern. Hier versuchen wir, die Mindestfunktion zu erreichen.

Als nächstes sollten wir das Objekt der Gerätedatenstruktur erstellen. Wir verwenden den Anweisungen, um den Speicher auf statische Weise zuzuweisen.

struktur cdrv_device_data char_device [cdrv_max_minors];

Dieser Speicher kann auch dynamisch mit „Kmalloc“ zugewiesen werden. Lassen Sie uns die Implementierung so einfach wie möglich halten.

Wir sollten die Implementierung von Lese- und Schreibfunktionen nehmen. Der Prototyp dieser beiden Funktionen wird durch das Gerätetreiber -Framework von Linux definiert. Die Implementierung dieser Funktionen muss Benutzer definiert werden. In unserem Fall haben wir Folgendes berücksichtigt:

Lesen Sie: Die Operation, um die Daten aus dem Treiberspeicher in den UserSpace zu erhalten.

static ssize_t cdrv_read (strukturdatei *Datei, char __user *user_buffer, size_t size, loff_t *offset);

Schreiben Sie: Die Operation zum Speichern der Daten im Treiberspeicher vom UserSpace im Treiberspeicher.

static ssize_t cdrv_write (strukturdatei *Datei, const char __user *user_buffer, size_t size, loff_t *offset);

Beide Operationen, lesen und schreiben, müssen als Teil von struct File_operations cdrv_fops registriert werden. Diese sind im Rahmen der Linux -Geräte -Treiber im Init_cdrv () des Treibers registriert. In der Funktion init_cdrv () werden alle Setup -Aufgaben ausgeführt. Nur wenige Aufgaben sind wie folgt:

  • Klasse erstellen
  • Geräteinstanz erstellen
  • Zuleiten

Der vollständige Beispielcode für den Treiber für den grundlegenden Zeichen für Zeichen ist wie folgt:

#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
#define cdrv_major 42
#define cdrv_max_minors 1
#define buf_len 256
#define cdrv_device_name "cdrv_dev"
#define cdrv_class_name "cdrv_class"
struktur cdrv_device_data
Struct CDEV CDEV;
char buffer [buf_len];
size_t Größe;
struct class* cdrv_class;
struct device* cdrv_dev;
;
struktur cdrv_device_data char_device [cdrv_max_minors];
static ssize_t cdrv_write (strukturdatei *Datei, const char __user *user_buffer,
Größe_t Größe, LOFF_T * Offset)

struct cdrv_device_data *cdrv_data = & char_device [0];
ssize_t len ​​= min (cdrv_data-> Größe - *Offset, Größe);
printk ("Schreiben: Bytes =%d \ n", Größe);
if (len buffer + *offset, user_buffer, len))
return -efault;
*Offset += len;
Rückkehr Len;

static ssize_t cdrv_read (strukturdatei *Datei, char __user *user_buffer,
Größe_t Größe, LOFF_T *Offset)

struct cdrv_device_data *cdrv_data = & char_device [0];
ssize_t len ​​= min (cdrv_data-> Größe - *Offset, Größe);
if (Len Buffer + *Offset, Len))
return -efault;
*Offset += len;
printk ("lesen: bytes =%d \ n", Größe);
Rückkehr Len;

static int cdrv_open (Struct Inode *Inode, Struct -Datei *Datei)
printk (KERN_INFO "CDRV: Gerät öffnen \ n");
Rückkehr 0;

static int cdrv_release (struct Inode *Inode, strukturdatei *Datei)
printk (KERN_INFO "CDRV: Gerät geschlossen \ n");
Rückkehr 0;

const struct file_operations cdrv_fops =
.Besitzer = this_module,
.open = cdrv_open,
.read = cdrv_read,
.write = cdrv_write,
.Release = cdrv_release,
;
init init_cdrv (void)

int count, ret_val;
printk ("init der grundlegende Zeichenfahrer ... starten \ n");
ret_val = register_chrdev_region (mkdev (cdrv_major, 0), cdrv_max_minors,
"cdrv_device_driver");
if (ret_val != 0)
printk ("register_chrdev_region (): fehlgeschlagen mit Fehlercode:%d \ n", ret_val);
ret_val zurückgeben;

für (count = 0; count < CDRV_MAX_MINORS; count++)
CDEV_INIT (& char_device [count].CDEV & CDRV_FOPS);
CDEV_ADD (& char_device [count].CDEV, MKDEV (cdrv_major, count), 1);
char_device [count].cdrv_class = class_create (this_module, cdrv_class_name);
if (is_err (char_device [count].cdrv_class))
printk (KERN_ALERT "CDRV: Register -Geräteklasse fehlgeschlagen \ n");
return ptr_err (char_device [count].cdrv_class);

char_device [count].size = buf_len;
printk (KERN_INFO "CDRV -Geräteklasse erfolgreich registriert \ n");
char_device [count].CDRV_DEV = Device_Create (char_device [count].cdrv_class, null, mkdev (cdrv_major, count), null, cdrv_device_name);

Rückkehr 0;

void Cleanup_cdrv (void)

int Count;
für (count = 0; count < CDRV_MAX_MINORS; count++)
Device_Destroy (char_device [count].cdrv_class & char_device [count].cdrv_dev);
class_destroy (char_device [count].cdrv_class);
CDEV_DEL (& char_device [count].CDEV);

unregister_chrdev_region (mkdev (cdrv_major, 0), cdrv_max_minors);
printk ("den grundlegenden Charakter -Treiber verlassen ... \ n");

module_init (init_cdrv);
Module_exit (CleanUp_cdrv);
Module_license ("GPL");
Module_author ("Sushil Rathore");
Module_description ("Beispielcharakter -Treiber");
Module_version ("1.0 ");

Wir erstellen ein Beispiel -Makefile, um den grundlegenden Charakter -Treiber und die Test -App zu kompilieren. Unser Treibercode ist in CRDV vorhanden.c und der Test -App -Code ist in cdrv_app vorhanden.C.

OBJ-M+= CDRV.Ö
alle:
make -c/lib/module/$ (Shell uname -r)/build/m = $ (pwd) Module
$ (Cc) cdrv_app.c -o cdrv_app
sauber:
make -c/lib/module/$ (Shell uname -r)/bauen/m = $ (pwd) sauber
RM CDRV_App
~

Nachdem die Ausstellung an das Makefile erfolgt, sollten wir die folgenden Protokolle erhalten. Wir bekommen auch den CDRV.KO und ausführbare Datei (cdrv_app) für unsere Test -App:

root@haxv-shrathore-2:/home/cienauser/kernel_articles#
make -c/lib/module/4.15.0-197-generisch/bauen/m =/home/cienauser/kernel_articles Module
Make [1]: Eingabe des Verzeichnisses '/usr/src/Linux-Header-4.15.0-197-generisch '
CC [M]/Home/Cienauser/Kernel_Articles/CDRV.Ö
Gebäudemodule, Stufe 2.
Modpost 1 Module
Cc/home/cienauser/kernel_articles/cdrv.Mod.Ö
Ld [m]/home/cienauser/kernel_articles/cdrv.ko
Make [1]: Verzeichnis '/usr/src/Linux-Header-4 verlassen.15.0-197-generisch '
CC CDRV_APP.c -o cdrv_app

Hier ist der Beispielcode für die Test -App. Dieser Code implementiert die Test -App, die die vom CDRV -Treiber erstellte Gerätedatei öffnet und die „Testdaten“ darauf schreibt. Anschließend liest es die Daten des Treibers und druckt sie nach dem Lesen der Daten als „Testdaten“ aus.

#enthalten
#enthalten
#Define Device_file "/dev/cdrv_dev"
char *data = "testen data";
char read_buff [256];
int main ()

int fd;
int rc;
fd = open (Device_file, o_wronly, 0644);
if (fd<0)

Perror ("Öffnen der Datei: \ n");
Return -1;

RC = Write (FD, Daten, Strlen (Daten) +1);
if (rc<0)

perror ("Datei schreiben: \ n");
Return -1;

printf ("geschrieben Bytes =%d, data =%s \ n", rc, data);
schließen (fd);
fd = open (Device_file, o_rdonly);
if (fd<0)

Perror ("Öffnen der Datei: \ n");
Return -1;

rc = read (fd, read_buff, strlen (Daten) +1);
if (rc<0)

Perror ("Lesendatei: \ n");
Return -1;

printf ("bytes read =%d, data =%s \ n", rc, read_buff);
schließen (fd);
Rückkehr 0;

Sobald wir alle Dinge vorhanden haben, können wir den folgenden Befehl verwenden, um den grundlegenden Charakter -Treiber in den Linux -Kernel einzulegen:

root@haxv-shrathore-2:/home/cienauser/kernel_articles# Insmod CDRV.ko
root@haxv-shrathore-2:/home/cienauser/kernel_articles#

Nach dem Einfügen des Moduls erhalten wir die folgenden Nachrichten mit DMESG und erhalten die in /dev as /dev /cdrv_dev erstellte Gerätedatei:

root@haxv-shrathore-2:/home/cienauser/kernel_articles# dmesg
[160.015595] CDRV: Laden von Out-of-Bree-Modul-Taints-Kernel.
[160.015688] CDRV: Modulverifizierung fehl
[160.016173] Initieren Sie den grundlegenden Charakter -Treiber… Start
[160.016225] CDRV -Geräteklasse erfolgreich registriert
root@haxv-shrathore-2:/home/cienauser/kernel_articles#

Führen Sie nun die Test -App mit dem folgenden Befehl in der Linux -Shell aus. Die endgültige Nachricht druckt die Lesedaten aus dem Treiber, die genau das entsprechen wie das, was wir in der Schreibvorlage geschrieben haben:

root@haxv-shrathore-2:/home/cienauser/kernel_articles# ./cdrv_app
geschriebene Bytes = 10, Daten = Testdaten
Bytes lesen = 10, Data = Testdaten
root@haxv-shrathore-2:/home/cienauser/kernel_articles#

Wir haben nur wenige zusätzliche Drucke im Schreib- und Lesweg, die mit Hilfe des DMESG -Befehls gesehen werden können. Wenn wir den DMESG -Befehl ausgeben, erhalten wir die folgende Ausgabe:

root@haxv-shrathore-2:/home/cienauser/kernel_articles# dmesg
[160.015595] CDRV: Laden von Out-of-Bree-Modul-Taints-Kernel.
[160.015688] CDRV: Modulverifizierung fehl
[160.016173] Initieren Sie den grundlegenden Charakter -Treiber… Start
[160.016225] CDRV -Geräteklasse erfolgreich registriert
[228.533614] CDRV: Gerät offen
[228.533620] Schreiben: Bytes = 10
[228.533771] CDRV: Gerät geschlossen
[228.533776] CDRV: Gerät offen
[228.533779] Lesen Sie: Bytes = 10
[228.533792] CDRV: Gerät geschlossen
root@haxv-shrathore-2:/home/cienauser/kernel_articles#

Abschluss

Wir haben den grundlegenden Charakter -Treiber durchlaufen, der die grundlegenden Schreib- und Lesevorgänge implementiert. Wir haben auch das Beispiel -Makefile besprochen, um das Modul zusammen mit der Test -App zu kompilieren. Die Test -App wurde geschrieben und besprochen, um die Schreibvorgänge aus dem Benutzerbereich auszuführen und zu lesen. Wir haben auch die Zusammenstellung und Ausführung des Modul- und Test -App mit Protokollen demonstriert. Die Test -App schreibt nur wenige Bytes von Testdaten und liest sie dann zurück. Der Benutzer kann beide Daten vergleichen, um die korrekte Funktion des Treibers und der Test -App zu bestätigen.