Heim  >  Artikel  >  System-Tutorial  >  Linux Intermediate – „Treiber“ Geringes Wissen, das erlernt werden muss, um Hardware zu steuern

Linux Intermediate – „Treiber“ Geringes Wissen, das erlernt werden muss, um Hardware zu steuern

WBOY
WBOYnach vorne
2024-02-12 15:21:131249Durchsuche
Linux中级——“驱动” 控制硬件必须学会的底层知识

Bewusstsein steigern

1. Was ist ein Fahrer? Der Treiber kapselt die Vorgänge des zugrunde liegenden Hardwaregeräts und stellt Funktionsschnittstellen zur oberen Schicht bereit.

Geräteklassifizierung:

Das Linux-System unterteilt Geräte in drei Kategorien: Zeichengeräte, Blockgeräte und Netzwerkgeräte.

    Zeichengerät
  • : Bezieht sich auf ein Gerät, das nur Byte für Byte lesen und schreiben kann. Es kann nicht zufällig bestimmte Daten im Gerätespeicher lesen. Die Daten müssen der Reihe nach gelesen werden. Zu den gängigen Zeichengeräten gehören Mäuse, Tastaturen, serielle Schnittstellen, Konsolen und LED-Geräte. Zeichengerätetreiber implementieren normalerweise mindestens die Systemaufrufe „Öffnen“, „Schließen“, „Lesen“ und „Schreiben“. (/dev/console) und die serielle Schnittstelle (/dev/ttyS0 und ähnliche Geräte) sind zwei Zeichengeräte, die das abstrakte Konzept von „Stream“ gut veranschaulichen können. Blockgerät: bezieht sich auf ein Gerät, das Daten einer bestimmten Länge von jedem Ort auf dem Gerät lesen kann. Zu den Blockgeräten gehören Festplatten, Festplatten, USB-Flash-Laufwerke, SD-Karten usw.
  • Netzwerkgerät: Ein Netzwerkgerät kann ein Hardwaregerät sein, beispielsweise eine Netzwerkkarte; es kann aber auch ein reines Softwaregerät sein, beispielsweise eine Loopback-Schnittstelle (lo). Eine Netzwerkschnittstelle ist für das Senden und Empfangen von Datenpaketen verantwortlich .
  • Linux中级——“驱动” 控制硬件必须学会的底层知识Lassen Sie uns ein Beispiel geben, um den gesamten Anrufprozess zu erklären

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 ==**

  1. open ruft open("/dev/pin4",O_RDWR); (Kernel-Funktion) auf und system_call ermittelt die gewünschte Gerätenummer anhand des Gerätenamens /dev/pin4.
  2. Passen Sie dann die virtuelle Datei system_callVFS an (
  3. Um die genaue Hardware für den Aufruf der oberen Schicht
  4. zu vereinheitlichen), rufen Sie in VFS auf, sys_open wird in der Treiberliste gefunden und der Treiber basierend auf gesucht Hauptgerätenummer und Nebengerätenummersys_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
  1. 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).

Das System wechselt in den Kernel-Status und geht zu 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:

  • Es bezieht sich auf die Ebene, auf der Benutzer Programme schreiben und ausführen. Der Benutzermodus erfordert die Grundlage von C und der C-Bibliothek während der Entwicklung. Die C-Bibliothek befasst sich mit Dateien, Prozessen, Kommunikation zwischen Prozessen, Threads, Netzwerken und Schnittstellen. GTk) 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.
  • Verschiedene APIs in der C-Bibliothek. Was als Kernel-Status bezeichnet wird, der den Betrieb des Kernels steuert
.

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 . Linux中级——“驱动” 控制硬件必须学会的底层知识

  • 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: ). ***Linux中级——“驱动” 控制硬件必须学会的底层知识**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==**.

    Die 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
    Zeichengerätetreiber

    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.

==Treiberverknüpfte Liste==

Treiber für alle Geräte verwalten, hinzufügen oder suchen
Hinzufügen添加是发生在我们编写完驱动程序,加载到内核
查找 erfolgt, nachdem wir den Treiber fertig geschrieben und in den Kernel geladen haben. Suchen

ruft den Treiber auf und der Benutzerbereich der Anwendungsschicht verwendet die Open-Funktion, um ihn zu finden

. 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

.

  • Wie findet die Funktion system_call die detaillierte Systemaufruf-Dienstroutine? Finden Sie die Systemaufruftabelle sys_call_table anhand der Systemaufrufnummer! Wenn der Soft-Interrupt-Befehl INT 0x80 ausgeführt wird, wird die Systemaufrufnummer im eax-Register
  • abgelegt. Die system_call-Funktion kann das eax-Register lesen, um es zu erhalten, es dann mit 4 multiplizieren, um eine Offset-Adresse zu generieren, und dann Verwenden Sie sys_call_table als Basisadresse. Durch Hinzufügen der Basisadresse zur Offset-Adresse erhalten Sie die Adresse der detaillierten Systemaufruf-Dienstroutine! Dann ist es an der Zeit, die Systemdienstroutine aufzurufen.

Hinzugefügt:
  1. Jeder Systemaufruf entspricht einer Systemaufrufnummer, und die Systemaufrufnummer entspricht der entsprechenden Verarbeitungsfunktion im Kernel.
  2. Alle Systemaufrufe werden über den Interrupt 0x80 ausgelöst.
  3. Bei Verwendung eines Systemaufrufs wird die Systemaufrufnummer über das EAX-Register an den Kernel übergeben, und die Eingabeparameter des Systemaufrufs werden wiederum über ebx, ecx ... an den Kernel übergeben
  4. Wie bei Funktionen wird der Rückgabewert des Systemaufrufs in eax gespeichert und alles muss aus eax entnommen werden

3. Funktionsprinzip des Zeichengerätetreibers

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:==🎜🎜
  1. Im Linux-Dateisystem wird jede Datei durch eine struct inode-Struktur beschrieben. Diese Struktur zeichnet alle Informationen der Datei auf, wie z. B. Dateityp, Zugriffsrechte usw.
  2. Im Linux-Betriebssystem verfügt jeder Treiber über eine entsprechende Datei im /dev目录或者其他如/sys-Verzeichnis der Anwendungsschicht.
  3. Im Linux-Betriebssystem hat jeder Treiber eine Gerätenummer.
  4. Im Linux-Betriebssystem weist das Linux-Betriebssystem jedes Mal, wenn eine Datei geöffnet wird, eine ****-Struktur auf der VFS-Ebene zu, um die geöffnete Datei zu beschreiben. struct file
  5. (1) Wenn die Öffnungsfunktion eine Gerätedatei öffnet, können Sie anhand der Informationen, die in der Struktur-Inode-Struktur beschrieben werden, die der Gerätedatei entspricht, den Typ des als nächstes zu bedienenden Geräts (Zeichengerät oder Blockgerät) ermitteln Es wird auch eine Struktur zugewiesen.
Linux中级——“驱动” 控制硬件必须学会的底层知识(2) Anhand der in der Struktur-Inode-Struktur aufgezeichneten Gerätenummer kann der entsprechende Treiber gefunden werden. Hier nehmen wir Zeichengeräte als Beispiel. Im Linux-Betriebssystem verfügt jedes Zeichengerät über eine Struktur vom Typ struct cdev. Diese Struktur beschreibt alle Informationen des Zeichengeräts. Die wichtigste davon ist die Bedienfunktionsschnittstelle des Zeichengeräts.

(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:

Aufrufcode der oberen Schicht: Operationsgesteuerter Code der oberen Schicht (pin4test.c):

  • #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

  • Der obige Zeichen-Gerätetreibercode enthält was es dem Code ermöglicht, das Gerät automatisch unter dev zu generierenDarüber hinaus können wir auch den Gerätenamen manuell erstellen. Anleitung: 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:

Ausführungsprozess des Treiber-Frameworks:

  • Ö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:

    1. module_init(pin4_drv_init); //入口 去调用 pin4_drv_initFunktion
    2. int __init pin4_drv_init(void) //Echter Fahrereinstieg
    3. Fahrereinstiegdevno = MKDEV(major,minor); // Gerätenummer erstellen
    4. register_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ügen
    5. pin4_class=class_create(THIS_MODULE,"myfirstdemo");//Generieren Sie das Gerät automatisch unter Entwicklung per Code und erstellen Sie eine Klasse
    6. pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //Gerätedatei erstellen.
    7. Die Hauptsache ist, /deveine zusätzliche Datei zum Öffnen unserer oberen Ebene zu erstellen
    8. Wenn nicht, können Sie das Gerät auch manuell erstellensudo mknod +设备名字 +设备类型(c表示字符设备驱动) +主设备号+次设备号

Kompilierung des Treibermodulcodes

Kompilierung des Treibermodulcodes

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)
  • 文件内容如下图所示:(-y表示编译进内核,-m表示生成驱动模块,CONFIG_表示是根据config生成的) 所以只需要将obj-m += pin4drive.o添加到Makefile中即可。下图:Makefile文件图Linux中级——“驱动” 控制硬件必须学会的底层知识
  • 编译生成驱动模块,将生成的**.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编译生成驱动模块会生成以下几个文件:Linux中级——“驱动” 控制硬件必须学会的底层知识
  • .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:
  1. Geben Sie zunächst das Verzeichnis ein, in dem sich der Linux-Kernel befindet, und kompilieren Sie die Datei pin4drive.o
  2. Beim Ausführen von MODPOST wird eine temporäre Datei pin4drive.mod.c generiert und dann pin4drive.mod.o basierend auf dieser Datei kompiliert,
  3. Verbinden Sie dann die Dateien pin4drive.o und pin4drive.mod.o, um die Modulzieldatei pin4drive.ko zu erhalten,
  4. 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文件pin4testLinux中级——“驱动” 控制硬件必须学会的底层知识Kernel-Treiber laden

Dann verwenden Sie den Befehl: Der Gerätetreiber (dies bezieht sich auf die Codezeile static char *module_name="pin4"; //Modulname im Treibercode), und die Gerätenummer bezieht sich auch auf den Code.

sudo insmod pin4drive.ko加载内核驱动(相当于通过insmod调用了module_init这个宏,然后将整个结构体加载到驱动链表中) 加载完成后就可以在dev下面看到名字为pin4Sie können überprüfen, ob der Treiber installiert wurde.

lsmod

Wir führen dann ./pin4test aus, um den oberen Code auszuführen.
    Der folgende Fehler tritt beim Ausführen des oberen Codes auf: Dies bedeutet, dass keine Berechtigung vorliegt.
  • Verwenden Sie den Befehl: Geben Sie Berechtigungen für pin4, damit jeder ihn erfolgreich öffnen kann. Linux中级——“驱动” 控制硬件必须学会的底层知识 sudo chmod 666 /dev/pin4Dann erneut ausführenpin4testAn 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 war

Nach 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 das generierte Treibermodul auf einer virtuellen Maschine generiert werden muss

  • 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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:lxlinux.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen