Heim > Artikel > System-Tutorial > Linux Intermediate – „Treiber“ Geringes Wissen, das erlernt werden muss, um Hardware zu steuern
Bewusstsein steigern
Das Linux-System unterteilt Geräte in drei Kategorien: Zeichengeräte, Blockgeräte und Netzwerkgeräte.
In der oberen Ebene rufen wir die Open-Funktion der C-Sprache auf und rufen pin4 unter /dev auf, um sie lesbar und beschreibbar zu öffnen. **== Wenn die obere Ebene open für den Kernel aufgerufen wird, tritt ein Soft-Interrupt auf. Die Interrupt-Nummer ist 0X80 und wird aus dem Benutzerbereich eingegeben ==**
open("/dev/pin4",O_RDWR);
(Kernel-Funktion) auf und system_call ermittelt die gewünschte Gerätenummer anhand des Gerätenamens /dev/pin4. system_call
VFS an (sys_open
Die Öffnungsfunktion in Pin 4, Unsere Öffnungsfunktion in Pin 4 dient der Bedienung des Registers
Hier Bildbeschreibung einfügen
“
Wenn wir einen Treiber schreiben, tun wir einfach Fügen Sie einen Treiber hinzu:
Was bewirkt das Hinzufügen eines Treibers?Gerätename
Gerätenummer
- Gerätetreiberfunktion (Betriebsregister zum Ansteuern des E/A-Ports)
- “
==Zusammenfassung==Wenn Sie dev
untenpin4
Pin, der Prozess ist: dev
下面的pin4
引脚,过程是:用户态调用open(“/de/pin4”,O_RDWR
),对于内核来说,上层调用open函数会触发一个软中断(系统调用专用,中断号是0x80,0x80代表发生了一个系统调用),系统进入内核态,并走到system_call
,可以认为这个就是此软中断的中断服务程序入口,然后通过传递过来的系统调用号来决定调用相应的系统调用服务程序(在这里是调用VFS
中的sys_open
)。sys_open
会在内核的驱动链表里面根据设备名和设备号查找到相关的驱动函数(每一个驱动函数是一个节点
User mode call open
"/de/pin4",O_RDWR
), für den Kernel For Beispielsweise löst der Aufruf der Open-Funktion von der oberen Ebene aus einen Soft-Interrupt aus (speziell für Systemaufrufe, die Interrupt-Nummer ist 0x80, 0x80 bedeutet, dass ein Systemaufruf stattgefunden hat). system_call
, Sie können sich dies wie folgt vorstellen: Der Eingang zum Interrupt-Dienstprogramm des Soft-Interrupts, und dann wird bestimmt, das entsprechende Systemaufruf-Dienstprogramm über die übergebene Systemaufrufnummer aufzurufen (hier heißt es<code style="font-size: 14px;padding: 2px 4px in VFS
;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;color: #10f5d6c">sys_open ). sys_open findet die entsprechende Treiberfunktion in der Kernel-Treiberliste basierend auf dem Gerätenamen und der Gerätenummer (<code style="font-size: 14px;padding: 2px 4px; border-radius: 4px; margin: 0 2px ; Schriftfamilie: Operator Mono, Consolas, Monaco, Menlo, monospace;color: #10f5d6c">Jede Treiberfunktion ist ein Knoten
), **==Die Treiberfunktion enthält Code zur Steuerung des IO-Ports über Register. und kann dann den E/A-Port steuern, um verwandte Funktionen zu implementieren ==**.
2. Detaillierte Erklärung jeder Komponente“
“
Benutzerprofil:
open,read,write,fork,pthread,socket
. C-Bibliothek (muss in der Linux-Standardbibliothek enthalten sein): Es handelt sich um Clibary, die eine Schnittstelle für das Programm zur Steuerung der Arbeit des Kernels bereitstellt. Der Aufruf wird hier gekapselt und implementiert und vom geschriebenen aufgerufen Anwendung.
“
Kernelstatus:
🎜“🎜🎜
Wenn Benutzer ein bestimmtes Hardwaregerät verwenden möchten, benötigen sie einen Kernel-Modus-Gerätetreiber und fahren dann die Hardware zum Laufen, wie im vorherigen Artikel erwähnt, wiringPi库
bietet Benutzern eine Schnittstelle zur Steuerung von Hardwaregeräten. Wenn keine WiringPi-Bibliothek vorhanden ist, müssen Sie die Funktionen der WiringPi-Bibliothek selbst implementieren, dh den Gerätetreiber selbst schreiben. Auf diese Weise können wir die Entwicklung auch abschließen, wenn wir einen anderen Platinentyp erhalten.
Alles in Linux ist eine DateiAlle Arten von Dateien und Geräten (wie Maus, Tastatur, Bildschirm, Flash, Speicher, Netzwerkkarte, wie in der Abbildung unten gezeigt) sind alle Dateien, alsoda es sich um Dateien handelt, Sie können Dateioperationsfunktionen verwenden, um diese Geräte zu bedienen .
Ich habe eine Frage: Woher wissen Dateioperationsfunktionen wie Öffnen und Lesen, um welches Hardwaregerät es sich bei der geöffneten Datei handelt? ①Geben Sie den entsprechenden Dateinamen in die open-Funktion ein, um das entsprechende Gerät zu steuern. ②Pass ==Gerätenummer (Hauptgerätenummer und Nebengerätenummer)==. Darüber hinaus müssen wir auch den Speicherort dieser Treiber verstehen und wissen, wie sie implementiert werden. Jedes Hardwaregerät entspricht einem anderen Treiber (diese Treiber werden von uns selbst implementiert).
Die Linux-Geräteverwaltung ist eng mit dem Dateisystem integriertVerschiedene Geräte werden im /dev-Verzeichnis in Form von Dateien gespeichert, genannt ==Gerätedateien==*. Anwendungen können diese Gerätedateien öffnen, schließen, lesen und schreiben und Vorgänge auf dem Gerät ausführen, genau wie normale Datendateien. **Um diese Geräte zu verwalten, nummeriert das System die Geräte**,*Jede Gerätenummer ist unterteilt in ==Hauptgerätenummer== und ==Nebengerätenummer==* (wie in der Abbildung unten gezeigt: ). *****Hauptgerätenummer** wird zur Unterscheidung verschiedener Gerätetypen verwendet, während **Nebengerätenummer zur Unterscheidung mehrerer Geräte desselben Typs verwendet wird. Für häufig verwendete Geräte gibt es unter Linux herkömmliche Nummern. Beispielsweise ist die Hauptgerätenummer einer Festplatte 3. ****Ein Zeichengerät oder Blockgerät hat eine Hauptgerätenummer und eine Nebengerätenummer. ==Die Hauptgerätenummer und die Nebengerätenummer werden gemeinsam als Gerätenummer bezeichnet==**.
“ZeichengerätetreiberDie Hauptgerätenummer
wird zur Darstellung eines bestimmten Treibers verwendet. Nebengerätenummer
“
wird verwendet, um jedes Gerät darzustellen, das diesen Treiber verwendet.Ein eingebettetes System verfügt beispielsweise über zwei LED-Anzeigen und die LED-Leuchten müssen unabhängig voneinander ein- oder ausgeschaltet werden. Anschließend können Sie einen
für eine LED-Leuchte schreiben und dessen Hauptgerätenummer als Gerät Nr. 5 und seine Nebengerätenummern als 1 bzw. 2 registrieren. Hier stellen die sekundären Gerätenummern jeweils zwei LED-Leuchten dar.
“
Treiber für alle Geräte verwalten, hinzufügen oder suchen
ruft den Treiber auf und der Benutzerbereich der Anwendungsschicht verwendet die Open-Funktion, um ihn zu findenHinzufügen
添加
是发生在我们编写完驱动程序,加载到内核。查找
erfolgt, nachdem wir den Treiber fertig geschrieben und in den Kernel geladen haben.Suchen
. Die Reihenfolge, in der der Treiber in die verknüpfte Liste eingefügt wird, wird durch die Gerätenummer abgerufen, das heißt, die Hauptgerätenummer und die Nebengerätenummer können nicht nur verschiedene Gerätetypen und verschiedene Gerätetypen unterscheiden , sondern auch den Treiber an eine bestimmte Position in der verknüpften Liste laden , die Entwicklung des unten vorgestellten Treibercodes ist nichts anderes als Hinzufügen des Treibers (Hinzufügen der Gerätenummer, des Gerätenamens und der Gerätetreiberfunktion) und
Aufrufen der Fahrer.
“
Hinzugefügt:
Funktionsprinzip des Zeichengerätetreibers In der Linux-Welt ist alles eine Datei und alle Hardwaregeräteoperationen werden in Dateioperationen auf der Anwendungsebene abstrahiert. Wir wissen, dass die Anwendungsschicht, wenn sie auf ein Hardwaregerät zugreifen möchte, den der Hardware entsprechenden Treiber aufrufen muss. Es gibt so viele Treiber im Linux-Kernel. Wie kann eine Anwendung den zugrunde liegenden Treiber genau aufrufen?
==Wichtiges Wissen:==🎜🎜struct inode
-Struktur beschrieben. Diese Struktur zeichnet alle Informationen der Datei auf, wie z. B. Dateityp, Zugriffsrechte usw. /dev
目录或者其他如/sys
-Verzeichnis der Anwendungsschicht. struct file
(3) Nachdem die Struktur struct cdev gefunden wurde, zeichnet der Linux-Kernel die erste Adresse des Speicherplatzes auf, in dem sich die Struktur struct cdev im i_cdev-Mitglied der Struktur inode Struktur befindet, und zeichnet die in der Struktur aufgezeichnete Adresse der Funktionsoperationsschnittstelle auf struct cdev-Struktur. Im f_ops-Mitglied der Strukturdateistruktur.
(4) Wenn die Aufgabe abgeschlossen ist, gibt die VFS-Schicht einen Dateideskriptor (fd) an die Anwendung zurück. Dieses fd entspricht der Strukturdateistruktur. Als nächstes kann die Anwendung der oberen Schicht die Strukturdatei über fd finden und dann die Funktionsschnittstelle file_operation zum Betreiben von Zeichengeräten in der Strukturdatei finden.
Unter diesen wurden cdev_init und cdev_add in der Eingabefunktion des Treibers aufgerufen, um die Bindung des Zeichengeräts und der Betriebsschnittstelle der Funktion file_operation abzuschließen und den Zeichentreiber beim Kernel zu registrieren.
Schreiben Sie Treibercode basierend auf dem Framework:
#include #include #include #include void main() { int fd,data; fd = open("/dev/pin4",O_RDWR); if(fdprintf("open fail\n"); perror("reson:"); } else{ printf("open successful\n"); } fd=write(fd,'1',1); }-Kernel-Treiber **==Das einfachste Framework für Zeichengerätetreiber==**:
Framework-Code für Zeichengerätetreiber
#include //file_operations声明 #include //module_init module_exit声明 #include //__init __exit 宏定义声明 #include //class devise声明 #include //copy_from_user 的头文件 #include //设备号 dev_t 类型声明 #include //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno; //设备号,devno是用来接收创建设备号函数的返回值,销毁的时候需要传这个参数 static int major =231; //主设备号 static int minor =0; //次设备号 static char *module_name="pin4"; //模块名 //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_open\n"); //内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_write\n"); //内核的打印函数和printf类似 return 0; } //将上面的函数赋值给一个结构体中,方便下面加载到到驱动链表中去 static struct file_operations pin4_fops = { //static防止其他文件也有同名pin4_fops //static限定这个结构体的作用,仅仅只在这个文件。 .owner = THIS_MODULE, .open = pin4_open, .write = pin4_write, }; /* 上面的代码等同于以下代码(但是在单片机keil的编译环境里面不允许以上写法): 里面的每个pin4_fops结构体成员单独赋值 static struct file_operations pin4_fops; pin4_fops.owner = THIS_MODULE; pin4_fops.open = pin4_open; pin4_fops.write = pin4_write; */ //static限定这个结构体的作用,仅仅只在这个文件。 int __init pin4_drv_init(void) //真实的驱动入口 { int ret; devno = MKDEV(major,minor); //2. 创建设备号 ret = register_chrdev(major, module_name,&pin4_fops); //3. 注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE,"myfirstdemo");//由代码在dev下自动生成设备,创建一个类 pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件,先有上面那一行代码,创建一个类然后这行代码,类下面再创建一个设备。 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class,devno);//先销毁设备 class_destroy(pin4_class);//再销毁类 unregister_chrdev(major, module_name); //卸载驱动 } module_init(pin4_drv_init); //入口,内核加载驱动的时候,这个宏(不是函数)会被调用,去调用pin4_drv_init这个函数 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");Gerätenamen manuell erstellen
sudo mknod +设备名字 +设备类型(c表示字符设备驱动) +主设备号+次设备号
b: Erstellen Sie eine Block-Spezialdatei (gepuffert). c, u: Erstellen Sie eine spezielle Zeichendatei (ungepuffert). p: FIFO erstellen, Um den manuell erstellten Gerätenamen zu löschen, einfach rm. Wie im Bild unten gezeigt: Öffnen Sie ein Gerät über das Oberschichtprogramm Wenn kein Treiber vorhanden ist, wird beim Ausführen ein Fehler gemeldet. Im Kerneltreiber übergeben die Oberschichtsystemaufrufe open,wirte
函数会触发sys_call
、sys_call会调用sys_open,
和sys_write
, sys_open und sys_write die Hauptgerätenummer und fügen Sie das Gerät in die Treiberliste des Kernels ein. Suchen Sie den Treiber und führen Sie den Befehl „Öffnen und Schreiben“ aus. Damit der gesamte Vorgang reibungslos abläuft, müssen wir zunächst den Treiber (Gerätetreiberdatei) vorbereiten.
Gerätetreiberdateien haben einen festen Rahmen:
module_init(pin4_drv_init);
//入口 去调用 pin4_drv_init
Funktionint __init pin4_drv_init(void)
//Echter Fahrereinstiegdevno = MKDEV(major,minor);
// Gerätenummer erstellenregister_chrdev(major, module_name,&pin4_fops);
//Registrieren Sie den Treiber und weisen Sie den Kernel an, die oben vorbereitete Struktur zur verknüpften Liste des Kernel-Treibers hinzuzufügenpin4_class=class_create(THIS_MODULE,"myfirstdemo");
//Generieren Sie das Gerät automatisch unter Entwicklung per Code und erstellen Sie eine Klassepin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);
//Gerätedatei erstellen. /dev
eine zusätzliche Datei zum Öffnen unserer oberen Ebene zu erstellensudo mknod +设备名字 +设备类型(c表示字符设备驱动) +主设备号+次设备号
Kompilierung des Laufwerksmodulcodes (für die Modulkompilierung ist ein konfigurierter Kernel-Quellcode erforderlich. Das Suffix des nach der Kompilierung und Verbindung generierten Kernelmoduls lautet **.ko
. Der Kompilierungsprozess wechselt zunächst in das Kernel-Quellcodeverzeichnis und liest die oberste Ebene Makefile-Datei und dann Kehren Sie dann in das Verzeichnis zurück, in dem sich der Modulquellcode befindet): **
#include //file_operations声明 #include //module_init module_exit声明 #include //__init __exit 宏定义声明 #include //class devise声明 #include //copy_from_user 的头文件 #include //设备号 dev_t 类型声明 #include //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno; //设备号 static int major =231; //主设备号 static int minor =0; //次设备号 static char *module_name="pin4"; //模块名 //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_open\n"); //内核的打印函数和printf类似 return 0; } //read函数 static int pin4_read(struct file *file,char __user *buf,size_t count,loff_t *ppos) { printk("pin4_read\n"); //内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_write\n"); //内核的打印函数和printf类似 return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open = pin4_open, .write = pin4_write, .read = pin4_read, }; //static限定这个结构体的作用,仅仅只在这个文件。 int __init pin4_drv_init(void) //真实的驱动入口 { int ret; devno = MKDEV(major,minor); //创建设备号 ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE,"myfirstdemo");//让代码在dev下自动>生成设备 pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class,devno); class_destroy(pin4_class); unregister_chrdev(major, module_name); //卸载驱动 } module_init(pin4_drv_init); //入口,内核加载驱动的时候,这个宏会被调用,去调用pin4_drv_init这个函数 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
/SYSTEM/linux-rpi-4.19.y/drivers/char
将以上代码复制到一个文件中,然后下一步要做的是就是:将上面的驱动代码编译生成模块,再修改Makefile。(你放那个文件下,就改哪个文件下的Makefile)obj-m += pin4drive.o
添加到Makefile中即可。下图:Makefile文件图
.ko
文件发送给树莓派**然后回/SYSTEM/linux-rpi-4.19.y
下使用指令:ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
进行编译生成驱动模块。然后将生成的.ko
文件发送给树莓派:scp drivers/char/pin4driver.ko pi@192.168.0.104:/home/pi
编译生成驱动模块会生成以下几个文件:
.o
的文件是object文件,.ko
是kernel object,与.o的区别在于其多了一些sections,比如.modinfo
。.modinfo section
是由kernel source里的modpost工具生成的, 包括MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE, device ID table以及模块依赖关系等等。depmod 工具根据.modinfo section生成modules.dep, modules.*map等文件,以便modprobe更方便的加载模块。“
- Während des Kompilierungsprozesses haben wir die folgenden Schritte durchlaufen:
- Geben Sie zunächst das Verzeichnis ein, in dem sich der Linux-Kernel befindet, und kompilieren Sie die Datei pin4drive.o
- Beim Ausführen von MODPOST wird eine temporäre Datei pin4drive.mod.c generiert und dann pin4drive.mod.o basierend auf dieser Datei kompiliert,
- Verbinden Sie dann die Dateien pin4drive.o und pin4drive.mod.o, um die Modulzieldatei pin4drive.ko zu erhalten,
- Verlassen Sie abschließend das Verzeichnis, in dem sich der Linux-Kernel befindet.
“
Kompilieren Sie pin4test.c (Aufrufcode der oberen Ebene) und senden Sie es an den Raspberry Pi. Sie können sehen, dass im Pi-Verzeichnis zwei Dateien gesendet werden, wie in der Abbildung unten dargestellt:
.ko文件
和pin4test
Kernel-Treiber laden
sudo insmod pin4drive.ko
加载内核驱动(相当于通过insmod调用了module_init这个宏,然后将整个结构体加载到驱动链表中) 加载完成后就可以在dev
下面看到名字为pin4
Sie können überprüfen, ob der Treiber installiert wurde.
lsmod
sudo chmod 666 /dev/pin4
Dann erneut ausführenpin4test
An der Oberfläche gibt es keine Informationsausgabe. Tatsächlich gibt es Druckinformationen im Kernel, aber die obere Schicht kann sie nicht sehen. Wenn Sie die vom Kernel gedruckten Informationen anzeigen möchten, müssen Sie kann den Befehl verwenden: dmesg |grep pin4
. Wie im Bild unten gezeigt: Dies bedeutet, dass der Fahreranruf erfolgreich warNach der Installation des Treibers können Sie den Befehl: pin4test
表面上看没有任何信息输出,其实内核里面有打印信息只是上层看不到如果想要查看内核打印的信息可以使用指令:dmesg |grep pin4
(ko muss nicht geschrieben werden) verwenden, um den Treiber zu deinstallieren.
Warum muss das generierte Treibermodul auf einer virtuellen Maschine generiert werden? Funktioniert der Raspberry Pi nicht?
Das Generieren des Treibermoduls erfordert eine Kompilierungsumgebung (Linux-Quellcode und Kompilierung, Sie müssen den Linux-Kernel-Quellcode herunterladen, der mit der Systemversion identisch ist. Er kann auch auf dem Raspberry Pi kompiliert werden, aber Kompilieren auf dem Raspberry Pi ist sehr ineffizient und benötigt sehr viel Zeit . In diesem Artikel geht es um die lokale Kompilierung des Raspberry Pi-Treibers.
Das obige ist der detaillierte Inhalt vonLinux Intermediate – „Treiber“ Geringes Wissen, das erlernt werden muss, um Hardware zu steuern. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!