/******************** * Zeichengerätetreiber************************/ (1) Einführung in Zeichengerätetreiber Mit Zeichengeräten sind Geräte gemeint, auf die über Byte-Streams zugegriffen wird, und die Treiber für Zeichengeräte werden als Zeichengerätetreiber bezeichnet. Dieser Treibertyp ist für die meisten einfachen Hardwaregeräte geeignet. Bei einem parallelen Drucker greifen wir beispielsweise darauf zu, indem wir eine Gerätedatei (wie /dev/printer) unter /dev erstellen. Die Benutzeranwendung öffnet das Gerät/den Drucker mit der Standardfunktion „Öffnen“, schreibt dann mit „Write“ Daten in die Datei und liest mit „Read“ Daten daraus. Aufrufvorgang:
Der sogenannte Treiber stellt die endgültige Schreibfunktion bereit und kommuniziert direkt mit dem Drucker, indem er auf die Register der Druckerhardware zugreift. (2) Hauptgerätenummer und Nebengerätenummer a. Einführung zur Gerätenummer Der Zugriff auf Zeichengeräte erfolgt über Gerätedateien innerhalb des Dateisystems. Diese Dateien befinden sich im Ordner /dev. Verwenden Sie zum Anzeigen „ls -l“. Geräte werden durch Gerätenummern identifiziert. Die Gerätenummer besteht aus zwei Teilen, der Hauptgerätenummer und der Nebengerätenummer. Normalerweise identifiziert die Hauptgerätenummer den Treiber, der dem Gerät entspricht. Linux lässt zu, dass mehrere Treiber dieselbe Hauptgerätenummer verwenden. Mithilfe der Nebengerätenummer wird das Gerät bestimmt, auf das sich die Gerätedatei bezieht. Im Kernel wird der dev_t-Typ <linux/types.h> zum Speichern von Gerätenummern verwendet. Der 2.4-Kernel verwendet 16-Bit-Gerätenummern (8 Bit für Master und 8 Bit für Slave), während der 2.6-Kernel 32 Bit verwendet, 12 Bit für Master und 20 Bit für Slave. Um im Treiber auf die Gerätenummer zuzugreifen, sollten Sie die in <linux/kdev_t.h> definierten Makros verwenden. Holen Sie sich die Gerätenummer:
b. Gerätenummern vergeben und freigeben Bevor ein Zeichengerät erstellt wird, muss der Treiber die Gerätenummer abrufen. verteilen: #include <linux/fs.h> int register_chrdev_region(dev_t zuerst, vorzeichenloses int count, char *name); //first: der Startwert des zu vergebenden Gerätenummernbereichs (die sekundäre Gerätenummer wird normalerweise auf 0 gesetzt) //count: angefragter fortlaufender Nummernbereich //name: zur Nummer gehörender Gerätename (siehe /proc/devices) Sie können den Kernel auch auffordern, eine dynamische Zuweisung vorzunehmen: int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name); //firstminor: normalerweise 0 //*dev: speichert die vom Kernel zurückgegebene Gerätenummer freigeben: void unregister_chrdev_region(dev_t zuerst, unsigned int count); //Wird in der Clear-Funktion des Moduls aufgerufen Die vom Kernel zugewiesenen Gerätenummern finden Sie in Documentation/devices.txt. c. Gerätedateien erstellen Sobald das Gerätetreibermodul die Haupt- und Nebengerätenummern vom System angefordert und über insmod in den Kernel geladen hat, können wir auf das Gerät zugreifen, indem wir eine Gerätedatei unter /dev erstellen. Erstellen eines Zeichengeräts: $>mknod /dev/mychar c major minor Wir verwenden häufig die Methode der dynamischen Zuweisung von Haupt- und Nebengerätenummern im Treiber, sodass es nicht zu Konflikten mit den im System vorhandenen Gerätenummern kommt. Bei dynamischer Zuweisung müssen die Gerätedateien unter /dev auch dynamisch durch Analyse von /proc/devices erstellt werden. Siehe die Skripte char_load und char_unload. (3) Grundlegende Datenstruktur von Zeichengeräten Die drei grundlegenden Datenstrukturen, die am engsten mit Zeichengerätetreibern verwandt sind, sind: file, file_oepeations und inode a.file_operations-Datenstruktur Die Struktur enthält mehrere Funktionszeiger. Diese Funktionen sind diejenigen, die tatsächlich mit der Hardware interagieren. Vom Benutzerbereich aufgerufene Funktionen wie „Öffnen“ und „Schreiben“ rufen letztendlich die Funktion auf, auf die der Zeiger hier zeigt. Jeder geöffneten Datei ist ein Satz von Funktionen zugeordnet. Siehe <linux/fs.h> und S. 54 des Treiberbuchs 2.6 Initialisierung der Kernelstruktur: Struktur file_operations my_fops = { .owner = DIESES_MODUL, .llseek = mein_llseek, .read = mein_Lesen, .write = mein_write, .ioctl = mein_ioctl, .open = mein_open, .release = mein_Release, } 2.4 Initialisierung der Kernelstruktur: Struktur file_operations my_fops = { Besitzer: THIS_MODULE, llseek: mein_llseek, ... } b.Dateistruktur <linux/fs.h> Datei ist eine Kernelstruktur, die tatsächlich dem Dateideskriptor fd entspricht, der vom Benutzer nach dem Öffnen der Datei zurückgegeben wird. Die Dateistruktur stellt eine geöffnete Datei dar. Jede geöffnete Datei im System hat eine entsprechende Dateistruktur im Kernelspeicher. Es wird vom Kernel beim Öffnen erstellt und an alle Funktionen übergeben, die mit der Datei arbeiten, bis zur letzten Schließfunktion. An diesem Punkt gibt der Kernel die Struktur frei, nachdem alle Instanzen der Datei geschlossen wurden. Nachdem der Benutzerbereichsprozess einen neuen Prozess geforkt hat, teilen sich der neue und der alte Prozess den offenen Dateideskriptor fd. Dieser Vorgang erstellt keine neue Dateistruktur im Kernelbereich, sondern erhöht nur die Anzahl der erstellten Dateistrukturen. Siehe <linux/fs.h> mode_t f_mode; FMODE_READ und FMODE_WRITE werden verwendet, um anzugeben, ob die Datei lesbar oder beschreibbar ist. loff_t f_pos; Aktuelle Lese- und Schreibposition, loff_t ist 64 Bit unsigned int f_flags; Dateiflags wie O_RDONLY, O_NONBLOCK, O_SYNC. Die Flags sind in <linux/fcntl.h> definiert. Struktur file_operations *f_op; Dateibezogene Operationen. Der Kernel weist diesem Zeiger beim Ausführen von „open“ einen Wert zu. Sie können der Öffnungsmethode des Treibers je nach Nebengerätenummer unterschiedliche f_ops zuweisen. void *private; Die Struktur, die das Hardwaregerät darstellt, wird normalerweise „private“ zugewiesen. struct dentry *f_dentry; Die der Datei entsprechende Verzeichniseintragsstruktur (Dentry). Auf den Inode kann über filp->f_dentry->d_inode zugegriffen werden. Der Rest der Datei hat wenig mit dem Treiber zu tun. c.inode-Struktur Der Kernel verwendet die Inode-Struktur, um eine tatsächliche Datei darzustellen, bei der es sich um eine normale Datei oder eine Gerätedatei handeln kann. Jede Datei hat nur eine Inode-Struktur, es können jedoch mehrere Dateistrukturen entsprechend dem Dateideskriptor vorhanden sein (mehrere offene Aufrufe). Diese Dateien verweisen alle auf denselben Inode. Inode ist in <linux/fs.h> definiert dev_t i_rdev; Für die Inode-Struktur, die die Gerätedatei darstellt, enthält i_rdev die tatsächliche Gerätenummer struct cdev *i_cdev cdev ist eine interne Kernelstruktur, die ein Zeichengerät darstellt. Wenn der Inode ein Zeichengerät darstellt, zeigt i_cdev auf die Struktur cdev im Kernel. Andere Strukturen haben wenig mit Gerätetreibern zu tun. Verwenden Sie das folgende Makro, um die Gerätenummer vom Inode abzurufen:
(4) Registrierung von Zeichengeräten Der Kernel verwendet die Struktur struct cdev, um ein Zeichengerät darzustellen. Unser Treiber muss sein eigenes CDev im Kernel registrieren. Siehe <linux/cdev.h> a. Normalerweise fügen Sie cdev zur Gerätestruktur hinzu Struktur scull_dev{ ... struct cdev cdev; /* Zeichengerätestruktur*/ } b. Initialisierung void cdev_init(Struktur cdev *cdev, Struktur file_operations *fops) c. Legen Sie den Inhalt in cdev fest
d. Fügen Sie das Set cdev zum Kernel hinzu int cdev_add(Struktur cdev *dev, dev_t Num, vorzeichenlose int Anzahl); //num: die erste Nummer, die dem Gerät entspricht //count: die Anzahl der Gerätenummern, die dem Gerät zugeordnet sind, normalerweise 1 //Sobald cdev_add zurückkehrt, betrachtet der Kernel das Gerät als verwendbar, daher muss die Hardwareinitialisierung des Geräts vor dem Aufruf abgeschlossen sein. (5) Registrierungsfunktion im alten Stil Die alten Registrierungsfunktionen aus 2.4 sind in vielen Treiberfunktionen noch vorhanden, neuer Code sollte sie jedoch nicht verwenden. registrieren: int register_chrdev(unsigned int major, const char *Name, Struktur file_operations *fops); //Registrieren Sie 0 bis 255 als Nebengerätenummer für die angegebene Hauptgerätenummer und erstellen Sie für jedes Gerät eine entsprechende Standard-CDev-Struktur. Abmelden: int unregister_chrdev(unsigned int major, Konstanten-Char *Name); (6) öffnen und loslassen a.öffnen Die Geräteinitialisierung wird in der Open-Methode des Treibers abgeschlossen. Nach Abschluss des Open kann die Hardware verwendet werden und das Benutzerprogramm kann über Schreib- und andere Methoden auf das Gerät zugreifen. Die Open-Arbeit umfasst:
offener Prototyp; int (*öffnen) (Struktur Inode *Inode, Struktur Datei *Filp); //Holen Sie sich den Dev-Zeiger über den Inode im Open-Modus und weisen Sie ihn file->private_data zu. //Struktur scull_dev *dev; //dev = enthalten_von(inode->i_cdev, Struktur scull_dev, cdev); //filp->private_data = dev; // (Wenn dev statisch zugewiesen ist, können Sie in Methoden wie open oder write direkt auf dev zugreifen. Wenn dev jedoch während module_init dynamisch zugewiesen wird, können Sie seinen Zeiger nur über die obige Methode abrufen.) b.freigabe Nicht jeder Close-Aufruf führt zu einem Aufruf der Release-Methode. Erst wenn der Dateizähler Null erreicht, wird Release aufgerufen, um die Dev-Struktur freizugeben.) (7) Lesen und Schreiben Die Aufgabe des Lesens und Schreibens besteht darin, Daten aus dem Benutzerbereich in den Kernel zu kopieren oder Kerneldaten in den Benutzerbereich zu kopieren. Sein Prototyp ist: ssize_t lesen (Strukturdatei *filp, char __user *buff, size_t Anzahl, loff_t *offp); ssize_t schreiben (Struktur Datei *filp, const char __user *buff, size_t Anzahl, loff_t *offp); //buff: Zeiger auf den Benutzerspeicherplatzpuffer //offp: der Speicherort, an dem der Benutzer Zugriffsvorgänge in der Datei ausführt //Beim Lesen und Schreiben sollte offp nach dem Kopieren der Daten aktualisiert und die tatsächliche Anzahl der kopierten Bytes zurückgegeben werden. (8) Datenaustausch mit dem Benutzerbereich __user *buff ist beim Lesen und Schreiben ein Zeiger auf den Benutzerbereich. Der Kernel kann nicht direkt auf dessen Inhalt verweisen (d. h. er kann nicht direkt auf den Wert von Buff zugreifen). Das Kopieren der Daten ist über die vom Kernel bereitgestellten Funktionen erforderlich. Die Gründe sind:
Die Funktionen zum Datenaustausch zwischen Kernel und Userspace sind in <asm/uaccess.h> dargestellt. wie:
Jede Funktion, die auf den Benutzerbereich zugreift, muss schlaffähig sein, und diese Funktionen müssen wiedereintrittsfähig sein. Wenn der Rückgabewert von Funktionen wie copy_to_user ungleich 0 ist, sollte beim Lesen oder Schreiben -EFAULT an den Benutzerbereich zurückgegeben werden. Die Hauptgerätenummer wird verwendet, um den Gerätetreiber anzugeben, und die Nebengerätenummer gibt das Gerät an, das diesen Treiber verwendet. Im Kernel stellt dev_t die Gerätenummer dar, die aus der Hauptgerätenummer und der Nebengerätenummer besteht. #include <linux/kdev_t.h> #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) //Ermitteln Sie die Hauptgerätenummer basierend auf der Gerätenummer#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) //Ermitteln Sie die Nebengerätenummer#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) //Generieren Sie die Gerätenummer basierend auf den angegebenen Haupt- und Nebengerätenummern#include <linux/fs.h> //Statisch: Für die angegebene Gerätenummer anwenden, „from“ bezieht sich auf die Gerätenummer, „count“ bezieht sich darauf, wie viele Geräte (kleinere Gerätenummern) diesen Treiber verwenden, Gerätename int register_chrdev_region(dev_t from, unsigned count, const char *name); //Die Länge des Namens darf 64 Bytes nicht überschreiten. //Gerätenummern dynamisch anwenden. Der Kernel weist ungenutzte Hauptgerätenummern zu. Die zugewiesenen Geräte werden in dev gespeichert. baseminor bezieht sich auf die Startnummer der Nebengerätenummer. count bezieht sich auf die Anzahl der Geräte. name Gerätename int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *Name) //Gerätenummer freigeben, „from“ bezieht sich auf die Gerätenummer, „count“ bezieht sich auf die Anzahl der Geräte void unregister_chrdev_region(dev_t from, unsigned count) //cat /proc/devices kann die Gerätenutzung anzeigen. In der Datei documentation/devices.txt des Kernel-Quellcodes können Sie die statische Zuweisung von Gerätenummern anzeigen. ///Der Kernel verwendet struct cdev, um einen Zeichengerätetreiber zu beschreiben. #include <linux/cdev.h> Struktur cdev { struct kobject kobj; //Der Kernel verwaltet Zeichengerätetreiber struct module *owner; //Normalerweise auf THIS_MODULE gesetzt, um zu verhindern, dass das Treibermodul während der Verwendung entladen wird const struct file_operations *ops; //Bedienung (vfs) struct list_head list; //Da mehrere Geräte denselben Treiber verwenden können, verwenden Sie eine verknüpfte Liste zum Aufzeichnen von dev_t dev; //Gerätenummer unsigned int count; //Anzahl der Geräte}; ////////Zeichengerätetreiber////////// 1. Gerätenummer beantragen 2. Definieren Sie ein CDEV-Gerätetreiberobjekt Struktur cdev mycdev; //Definiere ein Dateioperationsobjekt von file_operations struct file_operations fops = { .owner = DIESES_MODUL, .read = Lesefunktion…. }; 3. Verknüpfen Sie das fops-Objekt mit mycdev cdev_init(&mycdev, &fops); //mycdev.ops = &fops; mycdev.owner = DIESES_MODUL; 4. Fügen Sie den Gerätetreiber zum Kernel hinzu und geben Sie die dem Treiber entsprechende Gerätenummer an cdev_add(&mycdev, Gerätenummer, Anzahl der sekundären Gerätenummern); 5. Entfernen Sie beim Deinstallieren des Moduls den Gerätetreiber aus dem Kernel und heben Sie die Registrierung der Gerätenummer auf. cdev_del(&mycdev); ////////////Gerätedatei erstellen mknod /dev/device Dateiname c Hauptgerätenummer Nebengerätenummer //////////Inode-Knotenobjekt beschreibt eine Datei/Gerätedatei, einschließlich Berechtigungen, Gerätenummer und anderen Informationen struct inode { ... dev_t i_rdev; //Gerätenummer, die der Gerätedatei struct cdev *i_cdev entspricht; //Zeigt auf die Adresse des entsprechenden Gerätetreiberobjekts... }; ////Das Dateiobjekt beschreibt den Dateideskriptor, der beim Öffnen der Datei erstellt und beim Schließen zerstört wird. struct file { ... const struct file_operations *f_op; //Die Adresse des entsprechenden Dateioperationsobjekts unsigned int f_flags; //Datei öffnen-Flag fmode_t f_mode; //Berechtigung loff_t f_pos; //Dateideskriptor-Offset struct fown_struct f_owner; //Zu welchem Prozess gehört es unsigned int f_uid, f_gid; void *private_data; //Für die Verwendung durch Treiberprogrammierer … }; Die Gerätenummer der Gerätedatei kann über das Mitglied f_path.dentry->d_inode->i_rdev in der Datei abgerufen werden ///Fehlercode in <asm/errno.h> //// /////////Struktur file_operations //// Inode stellt das Knotenobjekt der von der Anwendung geöffneten Datei dar und Datei stellt den Dateideskriptor dar, der durch Öffnen der Datei erhalten wird. Gibt 0 zurück, wenn erfolgreich, gibt einen Fehlercode zurück, wenn fehlgeschlagen buf zeigt auf den Puffer im Benutzerprozess und len gibt die Größe von buf an (vom Benutzer beim Aufruf von read übergeben) „off“ stellt den Operationsoffset des fl-Dateideskriptors dar und der Rückgabewert ist die Anzahl der Datenbytes, die dem Benutzer tatsächlich gegeben wurden. Der Benutzerprozess gibt Daten an den Treiber weiter to bezieht sich auf den Puffer des Benutzerprozesses, from bezieht sich auf den Puffer des Treibers, der die Daten enthält, n ist die Anzahl der Bytes und der Rückgabewert ist 0 to bezieht sich auf den Treiber... vom Benutzer... n ist wie viele Bytes, .... statisch inline unsigniert lang __muss_überprüfen copy_to_user(void __user *to, const void *von, unsigned long n) { wenn (Zugriff_ok(VERIFY_WRITE, zu, n)) n = __copy_to_user(nach, von, n); return n; //Der Rückgabewert ist die Anzahl der noch zu kopierenden Bytes} extern inline lang copy_from_user(void *to, const void __user *from, lang n)
/////////// ///Dynamisch Speicher beantragen und löschen. Die Größe entspricht der Größe der Anwendung (nicht mehr als 128 KB). //flags ist das Flag (normalerweise GFP_KERNEL). Gibt die Adresse zurück, wenn erfolgreich, NULL, wenn fehlgeschlagen // GFP_ATOMIC, benutze den Notfallspeicherpool des Systems void *kmalloc(size_t size, gfp_t flags); //Der Speicher muss nach der Anwendung geleert werden void *kzalloc(size_t size, gfp_t flags); //Der beantragte Speicher wurde geleert void kfree(const void *objp); //Kmalloc/kzallocs Speicher zurückfordern void *vmalloc(unsigned long size); //Großen Speicherplatz beantragen void vfree(const void *addr); //Vmallocs Speicher zurückfordern // Der von kmalloc beantragte Speicher hat kontinuierliche physische Adressen, während vmalloc nicht unbedingt kontinuierlich ist ///// container_of(ptr, type, member) Typ ist eine Struktur, die Mitglieder enthält, //ptr ist die Adresse des Mitglieds der Typstruktur. //Dieses Makro ruft die erste Adresse der Strukturvariablen entsprechend der Adresse des Strukturmitglieds ab#define container_of(ptr, type, member) ({ \ const typeof( ((Typ *)0)->Mitglied ) *__mptr = (ptr); \ (Typ *)( (Zeichen *)__mptr - Offset von (Typ, Mitglied) );}) #define offsetof(TYP, MITGLIED) ((size_t) &((TYP *)0)->MITGLIED) 15 Typdefinitionsstruktur led_dev_t { 16 dev_t meindevid; 17 vorzeichenlose Ganzzahl *rLEDCON; 18 vorzeichenlose Ganzzahl *rLEDDAT; 19 Struktur cdev mycdev; 20 }LED_DEV; LED_DEV myled; //ind->i_cdev ist die Adresse, die auf das Mitglied von myled.mycdev zeigt. //Die erste Adresse der Strukturvariablen myled kann durch container_of(ind->i_cdev, LED_DEV, mycdev); abgerufen werden. /////// Gerätedateien automatisch erstellen//// #include <linux/device.h> 1. Strukturklasse *cl; cl = class_create(owner, name); //owner bezieht sich auf das Modul, zu dem es gehört, Name Klassenname//Nach der Erstellung können Sie /sys/class/class name void class_destroy(struct class *cls); anzeigen. //Wird verwendet, um die erstellte Klasse zu zerstören 2. Gerätedateien erstellen Strukturgerät *device_create(Strukturklasse *cls, Strukturgerät *parent, dev_t devt, void *drvdata, const char *fmt, ...) __attribute__((format(printf, 5, 6))); device_create(class, NULL, device number, NULL, "mydev%d", 88); //Erstelle eine Gerätedatei mit dem Namen mydev88 im Verzeichnis /dev/ void device_destroy(struct class *cls, dev_t devt); //Wird verwendet, um die erstellte Gerätedatei zu zerstören//////// int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) ; //Gerätenummer registrieren und Treiberobjekt erstellen void unregister_chrdev(unsigned int major, const char *name); //Gerätenummer abmelden und Treiberobjekt löschen static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) { returniere __register_chrdev(Haupt, 0, 256, Name, FOPs); } int __register_chrdev(unsigned int major, unsigned int baseminor, vorzeichenlose int Anzahl, const char *Name, const struct file_operations *fops) { Struktur char_device_struct *cd; Struktur cdev *cdev; int err = -ENOMEM; cd = __register_chrdev_region(Haupt-, Basis-Neben-, Anzahl-, Name); wenn (IS_ERR(cd)) gib PTR_ERR(cd) zurück; cdev = cdev_alloc(); wenn (!cdev) gehe zu out2; cdev->Besitzer = fops->Besitzer; cdev->ops = fops; kobject_set_name(&cdev->kobj, "%s", Name); Fehler = cdev_add (cdev, MKDEV (cd-> Haupt-, Basis-Moll), Anzahl); wenn (Fehler) gehe raus; cd->cdev = cdev; Major zurückgeben? 0: cd->major; aus: kobject_put(&cdev->kobj); aus2: kfree(__unregister_chrdev_region(cd->Haupt-, Basis-Neben-, Anzahl)); Rückgabefehler; } Zusammenfassen Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM. Wenn Sie mehr darüber erfahren möchten, schauen Sie sich bitte die folgenden Links an Das könnte Sie auch interessieren:
|
>>: So aktivieren Sie die MySQL-Remoteverbindung auf einem Linux-Server
Inhaltsverzeichnis 1. Grundprinzipien 2. Spezifis...
Vorwort Im aktuellen JavaScript gibt es kein Konz...
Wenn die Tabelle breit ist, kann es zu einem Über...
1. Einleitung Mit Imagemaps können Sie Bereiche e...
Offizielle Website-Adresse von CentOS https://www...
Inhaltsverzeichnis 1. Was ist Curry 2. Verwendung...
Methode 1: Absenden über den Absenden-Button <...
In diesem Artikelbeispiel wird der spezifische Co...
Bei der Verwendung von lepus3.7 zur Überwachung d...
Vorwort CSS-Raster sind normalerweise in verschie...
Vorwort Wenn Sie MySQL 6.0.x oder höher (JAR) ver...
Inhaltsverzeichnis Vorwort Was sind erstellbare S...
Parallelitätsfunktionen Zeit für i in `grep serve...
Viele Konzepte im UI-Design mögen in der Formulie...
Inhaltsverzeichnis 1. Einleitung 2. Lassen Sie un...