Eigener Treiber für Linux



  • Hallo zusammen,

    ich habe vor einigen Tagen angefangen mir etwas Wissen über Linux Treiber anzulesen (in erster Linie für mein Raspberry Pi etc.) und hatte mir überlegt dieses Wissen mit einem eigenen GPIO Treiber zu testen (soll danach ausgebaut werden).
    Dazu habe ich diesen Grundtreiber entwickelt:

    #include <linux/fs.h>
    #include <linux/version.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <asm/uaccess.h>  
    #include <linux/ioctl.h>
    #include <asm/io.h>
    
    // Peripherieinformationen
    #define Peripherie_Basis	0x7E000000													// Virtuelle Startadresse der Peripherie						
    #define GPIO_Basis			(Peripherie_Basis + 0x200000)
    
    // Treiberinformationen
    #define NAME				"TestTreiber"												// Name des Treibers
    #define DRIVER_MAJOR 		240															// Major Nummer
    
    // IO-Controls
    #define IOCTL_GETVALUE 		0x0001
    
    // Programmierer
    MODULE_AUTHOR("Daniel Kampert");
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("LED Treiber");
    MODULE_SUPPORTED_DEVICE("none");       
    
    static volatile uint32_t  *gpioRegs;
    char Array[5];  
    
    static int driver_open( struct inode *geraete_datei, struct file *instanz )
    {
    	gpioRegs = (uint32_t *)ioremap(0x20200000, 0xB4);
    	*(gpioRegs + 1) = (1 << 21);
    	*(gpioRegs + 7) = (1 << 17);
    	printk("Treiber geoeffnet\n");
        return 0;
    }
    
    /*static int my_ioctl(struct inode *geraetedatei, struct file *instanz, unsigned int cmd, unsigned long arg)
    {
    	switch(cmd)
    	{
    		case IOCTL_GETVALUE:
    			printk("Funktioniert!\n");
    			break;
    		default:
    			printk("Keine Funktion\n");
    	}
    	return 0;
    }*/
    
    static int driver_close(struct inode *geraete_datei, struct file *instanz )
    {
    	printk(NAME);
        printk(" wird beendet....\n");
    	*(gpioRegs + 10) = (1 << 17);
        return 0;
    }
    
    static ssize_t driver_read( struct file *instanz, char *user, size_t count, loff_t *offset )
    {
    	return count;
    }
    
    static ssize_t driver_write( struct file *instanz, const char __user *Buffer, size_t count, loff_t *offs)
    {
    
    	return count;
    }
    
    // Fileoperations
    static struct file_operations fops = {                     
        .owner= THIS_MODULE,
        .read= driver_read,
    	.write= driver_write,
        .open= driver_open, 
        .release= driver_close,
    	//.unlocked_ioctl= my_ioctl,
    };
    
    // Treiber beim Betriebssystem anmelden
    static int __init mod_init(void)
    {
        if(register_chrdev(DRIVER_MAJOR, NAME, &fops) == 0) 
    	{
    		printk(NAME);
    		printk(" erfolgreich angemeldet!\n");
            return 0;
    	}
    	else
    	{
    	    printk("Anmeldung fehlgeschlagen!\n");
    		return -EIO;
    	}
    }
    
    // Treiber vom Betriebssystem abmelden
    static void __exit mod_exit(void)
    {
        unregister_chrdev(DRIVER_MAJOR, NAME);  
    }
    
    module_init( mod_init );
    module_exit( mod_exit );
    

    Der funktioniert an sich auch ganz gut....hatte auch mal was mit IO-Controls versucht, aber das klappte noch nicht....das ist auch erst einmal egal.

    Nun möchte ich das der Treiber die LED nicht beim Öffnen einschaltet, sondern wenn ich eine "1" in den Treiber schreibe, sprich in meine driver_write() Funktion muss etwas passendes rein.
    Funktioniert da ein einfacher String-Compare bzw. sonstige String-Funktionen? Oder muss man das auf Kernelebene anders machen? (Wie gesagt, ich bin noch nicht ganz fit da drin und mache das eigentlich auch nur aus "Spaß an der Freude" 🙂 ).

    Vielen Dank für eure Hilfe 🙂

    Gruß
    Daniel



  • Ein string Vergleich dürfte ganz normal funktionieren. Sowas wie malloc darfst du natürlich nicht aufrufen, aber Vergleichen sollte ganz normal funktionieren.



  • Hallo,

    danke für die Antwort.
    Aber ich verstehe jetzt noch nicht, wie ich einzelne Zeichen an den Treiber schicken kann und er die direkt weiter verarbeitet?
    Weil ich habe ein Python Programm, welches diesen Treiber öffnet und dann eine "1", eine "0" und einen ungültigen Wert reinschreiben soll. In dem Treiber lasse ich mir die Eingabe über printk() ausgeben, aber ich erhalte immer sowas hier:

    Jul 20 01:37:10 MyRaspberry kernel: [293198.859801] TestTreiber erfolgreich angemeldet!
    Jul 20 01:37:10 MyRaspberry kernel: [293199.065573] Treiber geoeffnet
    Jul 20 01:37:13 MyRaspberry kernel: [293202.072006] 10ga
    TestTreiber wird beendet....kernel: [293202.072006]

    Normalerweise sollte da sowas stehen wie:

    Jul 20 01:37:13 MyRaspberry kernel: [293202.072006] 1
    Jul 20 01:37:13 MyRaspberry kernel: [293202.072007] 0
    Jul 20 01:37:13 MyRaspberry kernel: [293202.072008] ga

    Um dieses Problem zu umgehen müsste ich den Treiber öffnen, was rein schreiben, Treiber schließen und das ganze für einen anderen Wert wiederholen.
    Wie kann ich es so machen, dass er die Eingaben aus dem Userspace Programm direkt verarbeitet? 😕

    Hier nochmal mein Code

    #include <linux/fs.h>
    #include <linux/version.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <asm/uaccess.h>  
    #include <linux/ioctl.h>
    #include <asm/io.h>
    
    // Peripherieinformationen
    #define Peripherie_Basis	0x7E000000													// Virtuelle Startadresse der Peripherie						
    #define GPIO_Basis			(Peripherie_Basis + 0x200000)
    
    // Treiberinformationen
    #define NAME				"TestTreiber"												// Name des Treibers
    #define DRIVER_MAJOR 		240															// Major Nummer
    
    // IO-Controls
    #define IOCTL_GETVALUE 		0x0001
    
    // Programmierer
    MODULE_AUTHOR("Daniel Kampert");
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("LED Treiber");
    MODULE_SUPPORTED_DEVICE("none");       
    
    static volatile uint32_t  *gpioRegs;
    char Array[5];  
    
    static int driver_open( struct inode *geraete_datei, struct file *instanz )
    {
    	gpioRegs = (uint32_t *)ioremap(0x20200000, 0xB4);
    	*(gpioRegs + 1) = (1 << 21);
    	*(gpioRegs + 7) = (1 << 17);
    	printk("Treiber geoeffnet\n");
        return 0;
    }
    
    /*static int my_ioctl(struct inode *geraetedatei, struct file *instanz, unsigned int cmd, unsigned long arg)
    {
    	switch(cmd)
    	{
    		case IOCTL_GETVALUE:
    			printk("Funktioniert!\n");
    			break;
    		default:
    			printk("Keine Funktion\n");
    	}
    	return 0;
    }*/
    
    static int driver_close(struct inode *geraete_datei, struct file *instanz )
    {
    	printk(NAME);
        printk(" wird beendet....\n");
    	*(gpioRegs + 10) = (1 << 17);
        return 0;
    }
    
    static ssize_t driver_read( struct file *instanz, char *user, size_t count, loff_t *offset )
    {
    	return count;
    }
    
    static ssize_t driver_write( struct file *Instanz, const char *Buffer, size_t Count, loff_t *offs)
    {
    	char Input_Buffer[6] = "";
    	int ret;
    
    	// Daten vom Userspace in den Kernelspace kopieren
    	ret = copy_from_user(Input_Buffer, Buffer, Count);
    
    	printk(Input_Buffer);
    	printk("\n\r");
    
    	return Count;
    }
    
    // Fileoperations
    static struct file_operations fops = {                     
        .owner= THIS_MODULE,
        .read= driver_read,
    	.write= driver_write,
        .open= driver_open, 
        .release= driver_close,
    	//.unlocked_ioctl= my_ioctl,
    };
    
    // Treiber beim Betriebssystem anmelden
    static int __init mod_init(void)
    {
        if(register_chrdev(DRIVER_MAJOR, NAME, &fops) == 0) 
    	{
    		printk(NAME);
    		printk(" erfolgreich angemeldet!\n");
            return 0;
    	}
    	else
    	{
    	    printk("Anmeldung fehlgeschlagen!\n");
    		return -EIO;
    	}
    }
    
    // Treiber vom Betriebssystem abmelden
    static void __exit mod_exit(void)
    {
        unregister_chrdev(DRIVER_MAJOR, NAME);  
    }
    
    module_init( mod_init );
    module_exit( mod_exit );
    


  • Vermutlich benutzt dein Python-Programm Buffered-IO? Also ruft write(2) erst auf, wenn du die Datei schließt, der Puffer voll ist oder du explizit Flush aufrufst? Probier mal ein C-Programm, das direkt die Systemcalls open/read/write benutzt.



  • Hallo,

    danke für den Hinweis. Ja, das scheint wirklich so zu sein. Habe das Python Programm nun durch ein C-Programm mit Systemcalls ersetzt.
    Nun klappt es. Im Systemlog steht nun folgendes:

    Jul 20 12:13:20 MyRaspberry kernel: [331369.501798] Treiber geoeffnet
    Jul 20 12:13:20 MyRaspberry kernel: [331369.503536] 1
    Jul 20 12:13:21 MyRaspberry kernel: [331370.505379] 2
    Jul 20 12:13:22 MyRaspberry kernel: [331371.508955] fdf
    Jul 20 12:13:23 MyRaspberry kernel: [331372.510625] TestTreiber wird beendet....

    Aber schön wieder was gelernt 🙂
    Nicht gewusst das Python explizite Bedingungen hat bevor es in eine Datei schreibt. 🙂
    Vielen Dank für die Hilfe!

    Gruß
    Daniel



  • So ich habe direkt eine neue Frage....
    Ich habe mir ein Skript geschrieben, welches den Treiber kompiliert, ihn anmeldet und eine Gerätedatei anlegt.
    Ich teile mit dem Unterprogramm

    // Treiber beim Betriebssystem anmelden
    static int __init mod_init(void)
    {
        if(register_chrdev(DRIVER_MAJOR, NAME, &fops) == 0) 
    	{
    		printk(NAME);
    		printk(" erfolgreich angemeldet!\n");
            return 0;
    	}
    	else
    	{
    	    printk("Anmeldung fehlgeschlagen!\n");
    		return -EIO;
    	}
    }
    

    Dem Betriebssystem ja mit wie der Treiber angemeldet werden muss. Er wird auch unter "lsmod" als "Treiber" angezeigt.
    Wenn ich aber nun "modinfo Treiber" angebe, bekomme ich einen Error, dass das Modul nicht gefunden wurde.
    Habe ich da an der Funktion was falsch verstanden?



  • Was sagt modinfo denn, wenn du als Parameter direkt die Datei übergibst?

    Irgendwo muss modinfo ja die Datei finden können. Hast du dein Modul in die entsprechenden Standard-Pfade kopiert oder lädst du es von wo anders? Modinfo liest nicht aus dem RAM, sondern aus den .ko-Dateien.



  • Hey,

    ahh. Der Fehler lag im Aufruf. Ich hab es immer

    modinfo Treiber

    aufgerufen, aber es muss

    modinfo Treiber.ko

    heißen 🙂
    Danke für den Denkanstoß 🙂


Anmelden zum Antworten