HowTo-Artikel zum Entwickeln eingebetteter Linux-Gerätetreiber als Kernelmodule. Der gesamte Inhalt dieses Artikels kann auf Raspberry Pi ausgeführt werden.
https://github.com/take-iwiw/DeviceDriverLesson/tree/master/08_01
Beim letzten Mal habe ich procfs verwendet, um eine Schnittstelle für das Debuggen von Gerätetreibern zu erstellen. Procfs ist jedoch im Wesentlichen ein Ort, an dem Informationen über Prozesse abgelegt werden können, und es ist nicht gut, sie zum Debuggen zu verwenden. Es wird empfohlen, debugfs zum Debuggen zu verwenden. (Vielen Dank für den Hinweis von @rarul.)
Dieses Mal werde ich versuchen, dasselbe wie beim letzten Mal mit diesen Debugfs zu implementieren. Speziell,
/sys/kernel/debug/MyDevice/prm1
Ermöglicht das Lesen und Schreiben von Debug-Parametern durch Zugriff. Bitte beachten Sie, dass sich dieser Pfad je nach Umgebung ändern kann.
Es gibt zwei Möglichkeiten, eine Debugfs-Schnittstelle zu erstellen. Ich werde jede Methode beschreiben.
Um eine Schnittstelle für Debugfs zu erstellen, registrieren Sie einfach den Dateinamen und die Funktion, die Sie beim Lesen / Schreiben aufrufen möchten, mit der Funktion "debugfs_create_file ()", in die der Treiber geladen wird (insmod). Stellen Sie bei der Registrierung die Lese- / Schreibhandlerfunktion in der Tabelle struct file_operations
ein. Dies ist genau das gleiche wie bei den vorherigen Prozessen und der normalen Registrierung des Gerätetreiber-Handlers.
Dadurch wird jedoch eine Datei für den Debugfs-Zugriff im Stammverzeichnis von Debugfs erstellt. Natürlich gibt es auch Dateien für andere Kernelmodule, also organisieren Sie sie in einem Verzeichnis. Verwenden Sie debugfs_create_dir ()
, um ein Verzeichnis für debugfs zu erstellen. Indem Sie den Rückgabewert (Eintrag) von "debugfs_create_dir ()" in das dritte Argument von "debugfs_create_file ()" einfügen, wird eine Datei unter dem erstellten Verzeichnis erstellt.
Manchmal möchten Sie nur Parameter zum Debuggen lesen und schreiben, aber das Definieren einer Lese- / Schreibfunktion jedes Mal kann mühsam sein. Wenn Sie nur Lese- / Schreibvorgänge benötigen, können Sie Debugfs mit nur einer Hilfsfunktion erstellen. Um beispielsweise auf eine 32-Bit-Variable zuzugreifen, verwenden Sie debugfs_create_u32 ()
(die Definition war ohne Vorzeichen, aber ich konnte negative Zahlen eingeben und ausgeben). Wenn Sie hexadezimal lesen und schreiben möchten, verwenden Sie debugfs_create_x32 ()
. Es gibt auch Bool-Typen, Registersätze (Adressen und Nummern) und Funktionen für Blobs, die für Binärdaten verwendet werden können. Vorerst werde ich nur 32-Bit-Zahlen verwenden.
Wenn Sie den Kernel entladen, müssen Sie die von Ihnen erstellte Debugfs-Datei löschen. Verwenden Sie zum Entfernen debugfs_remove ()
. Sie können jetzt die erstellte Datei löschen. Es ist jedoch mühsam, sie einzeln zu löschen. Vor allem ist es schwierig, sich die Eintragsinformationen zu merken, als sie erstellt wurden. Zu diesem Zweck wird debugfs_remove_recursive
() bereitgestellt. Wenn Sie den Eintrag (Rückgabewert von debugfs_create_dir ()
) des erstellten Verzeichnisses in diese Funktion einfügen, werden die Dateien darunter auch rekursiv gelöscht.
Der Code sieht folgendermaßen aus: Es hat debug_prm1
statisch als Parameter für das Debuggen. Erstellen Sie Ihr eigenes Verzeichnis unter dem Debugfs-Stammverzeichnis als "MyDevice" und behalten Sie die Eintragsinformationen in "debug_entry_dir".
myDeviceDriver.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/debugfs.h>
/***Informationen zu diesem Gerät***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDevice" /* /proc/Gerätename in Geräten usw. angezeigt.*/
/*Debug-Variablen*/
struct dentry *debug_entry_dir; /*debugfs Verzeichniseintrag*/
static int debug_prm1; /*Debug-Parameter(zum Test) */
static int debug_read_size = 0; /*Anzahl der zu lesenden Bytes in einem Open*/
/* /sys/kernel/debug/MyDevice/debug_Funktion, die beim Zugriff auf prm1 aufgerufen wird*/
static int mydevice_debug_open(struct inode *inode, struct file *file)
{
printk("mydevice_proc_open\n");
debug_read_size = 4; //Lesen Sie jeweils 4 Bytes
return 0;
}
/* /sys/kernel/debug/MyDevice/debug_Funktion, die beim Lesen von prm1 aufgerufen wird*/
static ssize_t mydevice_debug_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk("mydevice_proc_read\n");
if (debug_read_size > 0) {
/*Wenn noch Daten ausgegeben werden müssen*/
/*Ganzzahlige Typnummer, die gehalten wird(debug_prm1)Wird als Zeichenkette ausgegeben*/
int len;
len = sprintf(buf, "%d\n", debug_prm1); //Eigentlich kopieren_to_sollte Benutzer sein
debug_read_size -= 4;
return len;
} else {
return 0;
}
}
/* /sys/kernel/debug/MyDevice/debug_Funktion, die beim Schreiben von prm1 aufgerufen wird*/
static ssize_t mydevice_debug_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
printk("mydevice_proc_write\n");
/*Die eingegebene Zeichenfolge ist eine Ganzzahl(debug_prm1)Halten Sie als*/
sscanf(buf, "%d", &debug_prm1); //Eigentlich kopieren_from_sollte Benutzer sein
return count;
}
/*Handlertabelle für Debugfs*/
static struct file_operations debug_debug_prm1_fops = {
.owner = THIS_MODULE,
.open = mydevice_debug_open,
.read = mydevice_debug_read,
.write = mydevice_debug_write,
};
/*Straße(insmod)Funktionen, die manchmal aufgerufen werden*/
static int mydevice_init(void)
{
printk("mydevice_init\n");
/*Erstellen Sie ein Verzeichnis für Debufs*/
debug_entry_dir = debugfs_create_dir(DRIVER_NAME, NULL);
if (debug_entry_dir == NULL) {
printk(KERN_ERR "debugfs_create_dir\n");
return -ENOMEM;
}
/*Methode 1:Registrierungsmethode für Handlertabellen*/
debugfs_create_file("prm1", S_IRUGO | S_IWUGO, debug_entry_dir, NULL, &debug_debug_prm1_fops);
/*Methode 2:Hilfsfunktionsmethode*/
debugfs_create_u32("_prm1", S_IRUGO | S_IWUGO, debug_entry_dir, &debug_prm1);
debugfs_create_x32("_prm1_hex", S_IRUGO | S_IWUGO, debug_entry_dir, &debug_prm1);
return 0;
}
/*Entladen(rmmod)Funktionen, die manchmal aufgerufen werden*/
static void mydevice_exit(void)
{
printk("mydevice_exit\n");
/*Für Debufs loswerden(Untergeordnete Dateien werden ebenfalls automatisch gelöscht) */
debugfs_remove_recursive(debug_entry_dir);
}
module_init(mydevice_init);
module_exit(mydevice_exit);
Methode 1 erforderte die Definition der Eingabefunktion und der Handlertabelle, Methode 2 endet jedoch mit einer Zeile. Natürlich ist Methode 2 einfach, aber wenn Sie etwas gleichzeitig mit dem Umschreiben der Parameter verarbeiten müssen, müssen Sie anscheinend Methode 1 verwenden. (Zum Beispiel beim Ändern der Register (Parameter) des über i2c angeschlossenen Geräts.)
Erstellen und laden Sie mit dem folgenden Befehl.
make
sudo insmod MyDeviceModule.ko
sudo ls /sys/kernel/debug/MyDevice
prm1 _prm1 _prm1_hex
Dann können Sie sehen, dass "prm1", "_prm1" und "_prm1_hex" unter "/ sys / kernel / debug / MyDevice" erstellt werden. "prm1" wird durch Methode 1 implementiert, und "_prm1" und "_prm1_hex" werden durch Methode 2 implementiert. Greifen Sie auf dieselben Variablen zu. (Da der Name auf diese Weise implementiert wurde, wurde er nur zu prm1 und hat keine besondere Bedeutung.)
sudo bash -c 'echo 12 > /sys/kernel/debug/MyDevice/prm1'
sudo cat /sys/kernel/debug/MyDevice/prm1
12
sudo cat /sys/kernel/debug/MyDevice/_prm1
12
sudo cat /sys/kernel/debug/MyDevice/_prm1_hex
0x0000000c
sudo bash -c 'echo 13 > /sys/kernel/debug/MyDevice/_prm1'
sudo bash -c 'echo 0x0f > /sys/kernel/debug/MyDevice/_prm1_hex'
Danach können Sie mit Katze und Echo lesen und schreiben. Das Ergebnis ist das gleiche, da alle Werte derselben Variablen eingegeben und ausgegeben werden. Es scheint, dass 0x automatisch hinzugefügt wird, wenn es sich um eine Hexadezimalzahl handelt.
Dies kann nur eine Raspeltorte sein. Dieses Mal habe ich beim Erstellen jeder Debugfs-Datei "S_IRUGO | S_IWUGO" als Zugriffsrecht angegeben. Dies sollte für alle Benutzer lesbar und beschreibbar sein. Aber in Wirklichkeit musste ich beim Laufen "sudo" hinzufügen. Dies liegt daran, dass den übergeordneten Verzeichnissen "/ sys", "/ sys / kernel" und "/ sys / kernel / debug" keine allgemeinen Benutzerzugriffsrechte gewährt werden.
Ich denke, dies kann geändert werden, indem die Mount-Optionen für diese Verzeichnisse geändert werden (wahrscheinlich irgendwo im Skript, das beim Start ausgeführt wird). Aber ich habe es nicht tief verfolgt und ich denke nicht, dass es geändert werden sollte.
Recommended Posts