So erstellen Sie einen eingebetteten Linux-Gerätetreiber (3)

Drittes Mal: Registrieren von Systemaufrufhandlern und -treibern (dynamische Methode)

Über diese Serie

HowTo-Artikel zum Entwickeln eingebetteter Linux-Gerätetreiber als Kernelmodule. Der gesamte Inhalt dieses Artikels kann auf Raspberry Pi ausgeführt werden.

Der gesamte Quellcode, der in diesem Artikel angezeigt wird

https://github.com/take-iwiw/DeviceDriverLesson/tree/master/03_01 https://github.com/take-iwiw/DeviceDriverLesson/tree/master/03_02

Inhalt dieser Zeit

Beim letzten Mal habe ich versucht, die Hauptnummer des Geräts statisch festzulegen und im Kernel zu registrieren. Diese Methode scheint derzeit jedoch nicht empfohlen zu werden. Dieses Mal zeigen wir Ihnen, wie Sie dies dynamisch einstellen. Versuchen Sie außerdem, mithilfe des Mechanismus von udev automatisch eine Gerätedatei zu erstellen.

Kleinere Korrektur

CFILES = myDeviceDriver.c

obj-m := MyDeviceModule.o
MyDeviceModule-objs := $(CFILES:.c=.o)

ccflags-y += -std=gnu99 -Wall -Wno-declaration-after-statement

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

Weisen Sie dynamisch eine Hauptnummer zu und registrieren Sie sie im Kernel

Quellcode des Gerätetreibers

Systemaufruf-Handler-Funktionen wie Öffnen / Schließen / Lesen / Schreiben sind dieselben wie beim letzten Mal. Wichtig ist der Registrierungsprozess innerhalb der Funktion mydevice_init.

myDeviceDriver.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <asm/current.h>
#include <asm/uaccess.h>

MODULE_LICENSE("Dual BSD/GPL");

/* /proc/Gerätename in Geräten usw. angezeigt.*/
#define DRIVER_NAME "MyDevice"

/*Startnummer und Anzahl der in diesem Gerätetreiber verwendeten Nebennummern(=Anzahl der Geräte) */
static const unsigned int MINOR_BASE = 0;
static const unsigned int MINOR_NUM  = 2;	/*Die kleine Zahl ist 0~ 1 */

/*Hauptnummer dieses Gerätetreibers(Dynamisch entscheiden) */
static unsigned int mydevice_major;

/*Zeichen Geräteobjekt*/
static struct cdev mydevice_cdev;

/*Funktion beim Öffnen aufgerufen*/
static int mydevice_open(struct inode *inode, struct file *file)
{
	printk("mydevice_open");
	return 0;
}

/*Funktion wird beim Schließen aufgerufen*/
static int mydevice_close(struct inode *inode, struct file *file)
{
	printk("mydevice_close");
	return 0;
}

/*Funktion beim Lesen aufgerufen*/
static ssize_t mydevice_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	printk("mydevice_read");
	buf[0] = 'A';
	return 1;
}

/*Funktion zum Zeitpunkt des Schreibens aufgerufen*/
static ssize_t mydevice_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	printk("mydevice_write");
	return 1;
}

/*Handlertabelle für verschiedene Systemaufrufe*/
struct file_operations s_mydevice_fops = {
	.open    = mydevice_open,
	.release = mydevice_close,
	.read    = mydevice_read,
	.write   = mydevice_write,
};

/*Straße(insmod)Funktionen, die manchmal aufgerufen werden*/
static int mydevice_init(void)
{
	printk("mydevice_init\n");

	int alloc_ret = 0;
	int cdev_err = 0;
	dev_t dev;

	/* 1.Sichern Sie sich eine kostenlose Hauptnummer*/
	alloc_ret = alloc_chrdev_region(&dev, MINOR_BASE, MINOR_NUM, DRIVER_NAME);
	if (alloc_ret != 0) {
		printk(KERN_ERR  "alloc_chrdev_region = %d\n", alloc_ret);
		return -1;
	}

	/* 2.Erhaltene dev( =Hauptnummer+Kleine Zahl)Holen Sie sich und behalten Sie die Hauptnummer von*/
	mydevice_major = MAJOR(dev);
	dev = MKDEV(mydevice_major, MINOR_BASE);	/*Nicht notwendig? */

	/* 3.Initialisierung der cdev-Struktur und Registrierung der Systemaufruf-Handler-Tabelle*/
	cdev_init(&mydevice_cdev, &s_mydevice_fops);
	mydevice_cdev.owner = THIS_MODULE;

	/* 4.Dieser Gerätetreiber(cdev)Zum Kernel*/
	cdev_err = cdev_add(&mydevice_cdev, dev, MINOR_NUM);
	if (cdev_err != 0) {
		printk(KERN_ERR  "cdev_add = %d\n", cdev_err);
		unregister_chrdev_region(dev, MINOR_NUM);
		return -1;
	}

	return 0;
}

/*Entladen(rmmod)Funktionen, die manchmal aufgerufen werden*/
static void mydevice_exit(void)
{
	printk("mydevice_exit\n");

	dev_t dev = MKDEV(mydevice_major, MINOR_BASE);
	
	/* 5.Dieser Gerätetreiber(cdev)Aus dem Kernel*/
	cdev_del(&mydevice_cdev);

	/* 6.Entfernen Sie die von diesem Gerätetreiber verwendete Hauptnummernregistrierung*/
	unregister_chrdev_region(dev, MINOR_NUM);
}

module_init(mydevice_init);
module_exit(mydevice_exit);

Bauen und laden

Erstellen und Laden sind die gleichen wie beim letzten Mal. Obwohl es sich um eine Gerätedateierstellung handelt, ist es schwierig, jede zu überprüfen, da die Hauptnummer dynamisch bestimmt wird. Daher werde ich sie mit dem folgenden Befehl zusammenfassen. In der Raspeye-Umgebung ist die Hauptzahl übrigens 242.

make
sudo insmod  MyDeviceModule.ko
sudo mknod --mode=666 /dev/mydevice0 c `grep MyDevice /proc/devices | awk '{print $1;}'` 0
sudo mknod --mode=666 /dev/mydevice1 c `grep MyDevice /proc/devices | awk '{print $1;}'` 1
sudo mknod --mode=666 /dev/mydevice2 c `grep MyDevice /proc/devices | awk '{print $1;}'` 2

Ich habe / dev / mydevice0, / dev / mydevice1 und / dev / mydevice2 experimentell gemacht. Wenn cdev_add fertig ist, wird die Anzahl der Geräte als 2 angegeben. Ich kann also / dev / mydevice2 erstellen, aber wenn ich versuche, es zu öffnen oder darauf zuzugreifen, erhalte ich eine Fehlermeldung wie "cat: / dev / mydevice2: Kein solches Gerät oder keine solche Adresse".

Verarbeitung beenden

Löschen Sie es wie im statischen Fall unten.

sudo rmmod MyDeviceModule
sudo rm /dev/mydevice0
sudo rm /dev/mydevice1
sudo rm /dev/mydevice2

Hinweis: Wenn Sie die Verarbeitung für jede Nebennummer ändern möchten

Beispielsweise möchten Sie möglicherweise den Prozess zwischen der Nebennummer 0 (/ dev / mydevice0) und der Nebennummer 1 (/ dev / mydevice1) ändern. In der Lese- / Schreib-Handler-Funktion können Sie die Verarbeitung durch die Nebenzahl nach Switch-Case teilen, dies kann jedoch auch durch Trennen der zu registrierenden Handler-Tabelle realisiert werden. Bereiten Sie im obigen Code "s_mydevice_fops" speziell für die Anzahl der Minderjährigen vor, deren Verarbeitung Sie teilen möchten (z. B. "s_mydevice_fops0" und "s_mydevice_fops1"). Indem Sie "struct cdev mydevice_cdev;" in ein Array verwandeln und den Verarbeitungsinhalt von 3, 4 und 5 unterschiedlich einstellen ("cdev_init ()", "cdev_add ()", "cdev_del ()"), Ich kann damit umgehen.

Erstellen Sie automatisch eine Gerätedatei (/ dev / XXX), die udev entspricht

Mit der obigen Methode können Sie jetzt den Gerätetreiber laden, dem dynamisch die Hauptnummer zugewiesen wurde. Sie müssen die Gerätedatei jedoch manuell erstellen, was mühsam ist. Linux hat einen Mechanismus namens udev. Wenn Sie beim Laden des Treibers eine Klasse in / sys / class / registrieren, erkennt ein Daemon namens udevd diese und erstellt automatisch eine Gerätedatei. In Bezug auf die Implementierung ist ein zusätzlicher Prozess zum "Registrieren einer Klasse in / sys / class / beim Laden eines Treibers" erforderlich.

Hinweis

Eigentlich scheint udev für die Plug & Play-Funktion verwendet zu werden. Wenn Sie das Gerätetreibermodul (.ko) an einem bestimmten Speicherort (/ lib / modules /) ablegen, wird der entsprechende Gerätetreiber anscheinend automatisch geladen, wenn das Gerät erkannt wird. Insmod ist daher nicht erforderlich.

Quellcode des Gerätetreibers

Nur mydevice_init und mydevice_exit haben sich geändert, daher werde ich sie einfach extrahieren. Klassenregistrierungs- und Löschprozesse wurden hinzugefügt.

myDeviceDriver.c(Teilweise weggelassen)



/*Gerätetreiberklassenobjekt*/
static struct class *mydevice_class = NULL;

/*Straße(insmod)Funktionen, die manchmal aufgerufen werden*/
static int mydevice_init(void)
{
	printk("mydevice_init\n");

	int alloc_ret = 0;
	int cdev_err = 0;
	dev_t dev;

	/* 1.Sichern Sie sich eine kostenlose Hauptnummer*/
	alloc_ret = alloc_chrdev_region(&dev, MINOR_BASE, MINOR_NUM, DRIVER_NAME);
	if (alloc_ret != 0) {
		printk(KERN_ERR  "alloc_chrdev_region = %d\n", alloc_ret);
		return -1;
	}

	/* 2.Erhaltene dev( =Hauptnummer+Kleine Zahl)Holen Sie sich und behalten Sie die Hauptnummer von*/
	mydevice_major = MAJOR(dev);
	dev = MKDEV(mydevice_major, MINOR_BASE);	/*Nicht notwendig? */

	/* 3.Initialisierung der cdev-Struktur und Registrierung der Systemaufruf-Handler-Tabelle*/
	cdev_init(&mydevice_cdev, &s_mydevice_fops);
	mydevice_cdev.owner = THIS_MODULE;

	/* 4.Dieser Gerätetreiber(cdev)Zum Kernel*/
	cdev_err = cdev_add(&mydevice_cdev, dev, MINOR_NUM);
	if (cdev_err != 0) {
		printk(KERN_ERR  "cdev_add = %d\n", alloc_ret);
		unregister_chrdev_region(dev, MINOR_NUM);
		return -1;
	}

	/* 5.Registrieren Sie die Klasse dieses Geräts(/sys/class/mydevice/machen) */
	mydevice_class = class_create(THIS_MODULE, "mydevice");
	if (IS_ERR(mydevice_class)) {
		printk(KERN_ERR  "class_create\n");
		cdev_del(&mydevice_cdev);
		unregister_chrdev_region(dev, MINOR_NUM);
		return -1;
	}

	/* 6. /sys/class/mydevice/mydevice*machen*/
	for (int minor = MINOR_BASE; minor < MINOR_BASE + MINOR_NUM; minor++) {
		device_create(mydevice_class, NULL, MKDEV(mydevice_major, minor), NULL, "mydevice%d", minor);
	}

	return 0;
}

/*Entladen(rmmod)Funktionen, die manchmal aufgerufen werden*/
static void mydevice_exit(void)
{
	printk("mydevice_exit\n");

	dev_t dev = MKDEV(mydevice_major, MINOR_BASE);
	
	/* 7. /sys/class/mydevice/mydevice*Löschen*/
	for (int minor = MINOR_BASE; minor < MINOR_BASE + MINOR_NUM; minor++) {
		device_destroy(mydevice_class, MKDEV(mydevice_major, minor));
	}

	/* 8.Entfernen Sie die Klassenregistrierung für dieses Gerät(/sys/class/mydevice/Löschen) */
	class_destroy(mydevice_class);

	/* 9.Dieser Gerätetreiber(cdev)Aus dem Kernel*/
	cdev_del(&mydevice_cdev);

	/* 10.Entfernen Sie die von diesem Gerätetreiber verwendete Hauptnummernregistrierung*/
	unregister_chrdev_region(dev, MINOR_NUM);
}

module_init(mydevice_init);
module_exit(mydevice_exit);

Fügen Sie eine Regeldatei hinzu

Mit dem obigen Code wird zum Zeitpunkt von insmod / sys / class / mydevice / mydevice0 / dev erstellt. Der udev-Mechanismus erstellt automatisch die Gerätedatei "/ dev / mydevice0" basierend auf den Informationen in "/ sys / class / mydevice / mydevice0 / dev".

Standardmäßig haben Sie jedoch keine Zugriffsrechte für allgemeine Benutzer. Fügen Sie eine Regeldatei hinzu, um die Zugriffsberechtigungen zu ändern. Die Regeldatei befindet sich in / etc / udev / rules.d /. Die Regeldatei selbst beginnt mit einer Nummer und scheint eine beliebige Datei mit der Erweiterung .rules zu sein. Für Raspeye gibt es von Anfang an eine Regeldatei mit dem Namen "/ etc / udev / rules.d / 99-com.rules", sodass Sie sie dort hinzufügen können. Ich wollte es teilen, also tat ich Folgendes. Mit Raspeye konnte ich keine neue Datei in "/ etc / udev / rules.d /" erstellen, selbst wenn ich "sudo" hinzugefügt habe. Deshalb bin ich einmal mit "sudo -i" in den Superuser-Modus gegangen und habe dann den Vorgang ausgeführt.

sudo -i
echo 'KERNEL=="mydevice[0-9]*", GROUP="root", MODE="0666"' >>  /etc/udev/rules.d/81-my.rules
exit

Bauen und laden

Versuchen Sie zu bauen und insmod. Dieses Mal werden Gerätedateien (/ dev / mydevice0 und / dev / mydevice1) automatisch nur durch Insmoding erstellt.

make
sudo insmod  MyDeviceModule.ko
ls -a /dev/my*
/dev/mydevice0  /dev/mydevice1

Außerdem wird eine Datei mit Geräteinformationen im Verzeichnis / sys / class / mydevice / mydevice0 / und darunter erstellt. Wenn udev nicht funktioniert, überprüfen Sie dies auch hier.

cat /sys/class/mydevice/mydevice0/dev
242:0
cat /sys/class/mydevice/mydevice0/uevent
MAJOR=242
MINOR=0
DEVNAME=mydevice0

Hinweis

Der Inhalt dieses Artikels entspricht dem Inhalt von "Linux Device Driver Programming (Yutaka Hirata)". Das Buch verwendet "class_device_create", um die Klasse zu erstellen. Class_device_create ist jedoch jetzt veraltet. Dieser Artikel verwendet stattdessen "device_create".

Recommended Posts

So erstellen Sie einen eingebetteten Linux-Gerätetreiber (11)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (8)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (1)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (4)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (7)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (2)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (3)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (6)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (5)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (10)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (9)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (12) (vollständig)
Wie man Yubico Yubikey von Manjaro Linux erkennt
So erstellen Sie ein interaktives CLI-Tool mit Golang
So erstellen Sie einen HTTPS-Server mit Go / Gin
[Python] Erstellen einer Adjazenzmatrix / Adjazenzliste [Graphentheorie]
So erstellen Sie ein Hacking-Labor - Kali Linux (2020.1) VirtualBox 64-Bit-Edition -
Wie erstelle ich ein Python-Paket (geschrieben für Praktikanten)
So erstellen Sie eine ISO-Datei (CD-Image) unter Linux
Wie erstelle ich eine japanisch-englische Übersetzung?
Wie man einen lockeren Bot macht
So installieren Sie VMware-Tools unter Linux
Wie erstelle ich einen Crawler?
So erstellen Sie eine rekursive Funktion
So installieren Sie MBDyn (Linux Ubuntu)
[Blender] So erstellen Sie ein Blender-Plug-In
[Blender] So erstellen Sie Blender-Skripte mehrsprachig
Wie erstelle ich einen Crawler?
So erstellen Sie den MongoDB C-Sprachtreiber
So überprüfen Sie die Linux-Betriebssystemversion
So machen Sie einen String in Python zu einem Array oder ein Array zu einem String
So bringen Sie den Druckertreiber für Oki Mac in Linux
Wie baue ich meinen eigenen Linux-Server?
Wie man Selen so leicht wie möglich macht
[Linux] Unterteilen von Dateien und Ordnern
So erstellen Sie einen LINE-Bot mit künstlicher Intelligenz mit der Flask + LINE Messaging-API
So installieren Sie das aws-session-manager-Plugin unter Manajro Linux
[Python] Wie man eine Klasse iterierbar macht
python3 So installieren Sie ein externes Modul
So erstellen Sie eine NVIDIA Docker-Umgebung
So konvertieren Sie Python in eine exe-Datei
Ich möchte wissen, wie LINUX funktioniert!
[Linux] Verwendung des Befehls echo
So zeigen Sie Piktogramme unter Manjaro Linux an
So installieren Sie Pakete unter Alpine Linux
[Cocos2d-x] So erstellen Sie eine Skriptbindung (Teil 2)
So bedienen Sie Linux von der Konsole aus
So installieren Sie das Windows-Subsystem für Linux
So schalten Sie Linux unter Ultra96-V2 aus
So aktualisieren Sie die Sicherheit unter CentOS Linux 8
Ich möchte ein Automatisierungsprogramm erstellen!
Wie installiere ich php7.4 unter Linux (Ubuntu)
So machen Sie Multi-Boot-USB (Windows 10-kompatibel)
So erstellen Sie einen benutzerdefinierten Backtrader-Indikator
Wie erstelle ich eine Pelican Site Map?