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/07_01
Bis zum letzten Mal haben wir zusätzlich zu den grundlegenden Systemaufrufen (Öffnen, Schließen, Lesen, Schreiben) ioctl implementiert. Dies deckt die meisten der minimal erforderlichen Schnittstellen für einen Gerätetreiber ab (Auswahl / Abfrage überspringen).
Wenn Sie ein Gerät (z. B. einen Sensor oder Aktor) steuern möchten, möchten Sie möglicherweise Parameter für das Debuggen ändern oder abrufen. Es wäre sehr praktisch, dies von der Shell aus tun zu können. Dies kann durch Lesen / Schreiben in Dateien im proc-Dateisystem (procfs) erreicht werden. Angenommen, Sie erstellen die folgende Datei.
Sie können den Sensorwert überprüfen, indem Sie den Wert von "/ proc / mydevice_sensor0" (z. B. "cat") lesen, oder Sie können den Motor steuern, indem Sie den Wert von "/ proc / mydevice_motor0" (z. B. "echo") schreiben. ). Grundsätzlich entspricht dies dem Lesen / Schreiben des bisher erstellten Gerätetreibers. Bisher wurde es jedoch für die Gerätedatei (/ dev / mydevice
) gelesen / geschrieben. Dieses Mal machen wir das für die procfs-Datei. Sie können so viele machen, wie Sie möchten. Dies ist nützlich, wenn Sie ein einzelnes Gerät und viele Parameter haben, die Sie überprüfen möchten.
In den Kommentaren wies @rarul darauf hin. Es wird empfohlen, procfs nur zum Anordnen von Prozessinformationen und Debugfs für Debugging-Zwecke zu verwenden, wie oben erwähnt. Ich werde das nächste Mal Debugfs beschreiben. Es sollte nicht zum Debuggen verwendet werden, aber ich denke, es ist eine gute Möglichkeit, procfs zu erstellen, also lasse ich diesen Artikel so wie er ist.
Um eine Schnittstelle für procfs zu erstellen, registrieren Sie einfach den Dateinamen und die Funktion, die Sie beim Lesen / Schreiben aufrufen möchten, mit der Funktion "proc_create", in die der Treiber geladen wird (insmod). Stellen Sie bei der Registrierung die Lese- / Schreibhandlerfunktion in der Tabelle struct file_operations
ein. Tatsächlich ist dies genau das gleiche Verfahren wie das Registrieren eines Handlers für einen normalen Gerätetreiber. Der einzige Unterschied ist die verwendete Funktion. Verwenden Sie die Funktion remove_proc_entry
, um es zu entfernen.
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>
/***Informationen zu diesem Gerät***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDevice" /* /proc/Gerätename in Geräten usw. angezeigt.*/
#define PROC_NAME "MyDevice_test" /* /Der Name von procfs, die in proc erstellt werden sollen*/
/*procfs Testvariablen*/
static char proc_test_string[16];
static int flag_read = 0;
/* /proc/MyDevice_Funktion beim Zugriff auf Test aufgerufen*/
static int mydevice_proc_open(struct inode *inode, struct file *file)
{
printk("mydevice_proc_open\n");
flag_read = 0;
return 0;
}
/* /proc/MyDevice_Funktion beim Lesen des Tests aufgerufen*/
static ssize_t mydevice_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk("mydevice_proc_read\n");
if (flag_read == 0) {
int len;
len = sprintf(buf, "%s\n", proc_test_string);
flag_read = 1;
return len;
} else {
return 0;
}
}
/* /proc/MyDevice_Funktion, die beim Schreiben des Tests aufgerufen wird*/
static ssize_t mydevice_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
printk("mydevice_proc_write\n");
if (count > sizeof(proc_test_string)) count = sizeof(proc_test_string) - 1;
if (copy_from_user(proc_test_string, buf, count)) {
return -EFAULT;
}
proc_test_string[count] = '\0';
return count;
}
/*Handlertabelle für procfs*/
static struct file_operations mydevice_proc_fops = {
.owner = THIS_MODULE,
.open = mydevice_proc_open,
.read = mydevice_proc_read,
.write = mydevice_proc_write,
};
/*Straße(insmod)Funktionen, die manchmal aufgerufen werden*/
static int mydevice_init(void)
{
printk("mydevice_init\n");
struct proc_dir_entry *entry;
/*Erstellen Sie procfs*/
entry = proc_create(PROC_NAME, S_IRUGO | S_IWUGO, NULL, &mydevice_proc_fops);
if (entry == NULL) {
printk(KERN_ERR "proc_create\n");
return -ENOMEM;
}
return 0;
}
/*Entladen(rmmod)Funktionen, die manchmal aufgerufen werden*/
static void mydevice_exit(void)
{
printk("mydevice_exit\n");
/*Procfs loswerden*/
remove_proc_entry(PROC_NAME, NULL);
}
module_init(mydevice_init);
module_exit(mydevice_exit);
Erstellen wir eine Datei mit dem Namen "/ proc / MyDevice_test". Die Verarbeitung beim Lesen dieser Datei (mydevice_proc_read
), die Verarbeitung beim Schreiben ( mydevice_proc_write
) und die Verarbeitung beim Öffnen (mydevice_proc_open
) sind implementiert. Der Prozess beim Öffnen wird immer jedes Mal aufgerufen, wenn Sie aus der Shell lesen oder schreiben. Ich denke, es ist okay ohne es. In der Tat wird Schließen weggelassen.
mydevice_proc_write
enthält die benutzerdefinierte Zeichenfolge in einer internen statischen Variablen ( proc_test_string
). Gibt den Inhalt von mydevice_proc_read
zurück. Ich möchte, dass Sie es nur einmal pro Lesung aufrufen (cat
), also verwalte ich die Flagge und lösche sie zum Zeitpunkt des Öffnens. Ich denke, es gibt einen besseren Weg, dies zu tun.
Verwenden Sie beim Laden des Moduls die Funktion proc_create
, um die Datei / proc / MyDevice_test
zu erstellen. S_IRUGO | S_IWUGO
ist dasselbe wie 0666 und gewährt allen Benutzern RW-Zugriff.
Erstellen und laden Sie mit dem folgenden Befehl.
make
sudo insmod MyDeviceModule.ko
ls /proc/MyDevice_test
/proc/MyDevice_test
echo "abc" > /proc/MyDevice_test
cat /proc/MyDevice_test
abc
Nach dem Laden wird / proc / MyDevice_test
erstellt. Schreiben Sie dazu den Wert mit echo. Wenn Sie es danach mit cat lesen, wird der Wert ausgegeben, den Sie zuvor geschrieben haben.
Der Inhalt dieses Artikels entspricht dem Inhalt von "Linux Device Driver Programming (Yutaka Hirata)". Das Buch verwendete "create_proc_entry", um sich beim proc-Dateisystem zu registrieren. Es ist jedoch jetzt veraltet. Dieser Artikel verwendet stattdessen "proc_create".
Wie Sie in den Kommentaren betont haben, tauscht procfs ursprünglich Informationen über den Prozess aus. Wie wir im nächsten Artikel sehen werden, tauscht debugfs Informationen zum Debuggen aus.
Abgesehen von diesen gibt es einen Mechanismus zum Austausch von Parametern mit Modulen. Sie können die Modulinformationen und -parameter als sysfs überprüfen, die im Verzeichnis / sys abgelegt werden können. Dies sieht genauso aus wie procfs, aber procfs ist der alte Weg, und wie ich in diesem Artikel geschrieben habe, können Devadora-Entwickler es hinzufügen. Infolgedessen ist es chaotisch geworden. sysfs muss korrekt registriert sein. Und jede Datei ist nach Verzeichnissen organisiert und verwaltet.
Es gibt verschiedene Methoden, aber wenn Sie nur die Parameter anzeigen möchten, können Sie dies mit einem Hilfsmakro namens "module_param ()" tun. Auf diese Weise können Sie beim Laden eines Moduls Parameter einstellen und die Werte von Parametern (Variablen) lesen. Es scheint jedoch, dass die Parameter nach dem Laden des Kernels nicht geändert werden können.
Ich werde es tatsächlich benutzen. Sie müssen lediglich die Variable deklarieren und module_param ()
lesen. Ein Beispiel für die Verwendung der Variablen param1
als Parameter ist unten dargestellt.
static int param1 = 10;
module_param(param1, int, S_IRUGO);
Sie können den Wert von param1
überprüfen, indem Sie / sys / module / MyDeviceModule / parameters / param1
lesen. Sie können während insmod auch einen Wert angeben.
sudo insmod MyDeviceModule.ko
cat /sys/module/MyDeviceModule/parameters/param1
10
sudo rmmod MyDeviceModule
sudo insmod MyDeviceModule.ko param1=20
cat /sys/module/MyDeviceModule/parameters/param1
20
Recommended Posts