Comment créer un pilote de périphérique Linux intégré (3)

Troisième fois: enregistrement des gestionnaires et des pilotes d'appels système (méthode dynamique)

À propos de cette série

Article HowTo pour développer des pilotes de périphériques Linux embarqués en tant que modules de noyau. Tout le contenu de cet article peut être exécuté sur Raspberry Pi.

Le code source complet qui apparaît dans cet article

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

Contenu de cette époque

La dernière fois, j'ai essayé la méthode consistant à définir le numéro majeur de l'appareil de manière statique et à l'enregistrer dans le noyau. Cependant, cette méthode ne semble pas recommandée pour le moment. Cette fois, nous allons vous montrer comment définir cela de manière dynamique. Essayez également de créer automatiquement un fichier de périphérique en utilisant le mécanisme d'udev.

Correction mineure

--Il semble que vous deviez montrer la licence du module noyau. Sans cela, vous recevrez un avertissement au moment de la construction. À la suite du livre, définissez les licences suivantes. - MODULE_LICENSE("Dual BSD/GPL");

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

Attribuez dynamiquement un numéro majeur et enregistrez-le dans le noyau

Code source du pilote de périphérique

Les fonctions du gestionnaire d'appels système telles que ouvrir / fermer / lire / écrire sont les mêmes que la dernière fois. L'important est le processus d'enregistrement dans la fonction 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/Nom de l'appareil affiché sur les appareils, etc.*/
#define DRIVER_NAME "MyDevice"

/*Numéro de départ et nombre de nombres mineurs utilisés dans ce pilote de périphérique(=Nombre d'appareils) */
static const unsigned int MINOR_BASE = 0;
static const unsigned int MINOR_NUM  = 2;	/*Le nombre mineur est 0~ 1 */

/*Numéro majeur de ce pilote de périphérique(Décider dynamiquement) */
static unsigned int mydevice_major;

/*Objet périphérique de caractère*/
static struct cdev mydevice_cdev;

/*Fonction appelée à l'ouverture*/
static int mydevice_open(struct inode *inode, struct file *file)
{
	printk("mydevice_open");
	return 0;
}

/*Fonction appelée à la fermeture*/
static int mydevice_close(struct inode *inode, struct file *file)
{
	printk("mydevice_close");
	return 0;
}

/*Fonction appelée lors de la lecture*/
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;
}

/*Fonction appelée au moment de l'écriture*/
static ssize_t mydevice_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	printk("mydevice_write");
	return 1;
}

/*Table des gestionnaires pour divers appels système*/
struct file_operations s_mydevice_fops = {
	.open    = mydevice_open,
	.release = mydevice_close,
	.read    = mydevice_read,
	.write   = mydevice_write,
};

/*Route(insmod)Fonctions parfois appelées*/
static int mydevice_init(void)
{
	printk("mydevice_init\n");

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

	/* 1.Sécurisez un numéro majeur gratuit*/
	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.Obtenu dev( =Numéro majeur+Numéro mineur)Obtenez le numéro de mesure et conservez-le*/
	mydevice_major = MAJOR(dev);
	dev = MKDEV(mydevice_major, MINOR_BASE);	/*Inutile? */

	/* 3.Initialisation de la structure cdev et enregistrement de la table du gestionnaire d'appels système*/
	cdev_init(&mydevice_cdev, &s_mydevice_fops);
	mydevice_cdev.owner = THIS_MODULE;

	/* 4.Ce pilote de périphérique(cdev)Vers le noyau*/
	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;
}

/*Décharger(rmmod)Fonctions parfois appelées*/
static void mydevice_exit(void)
{
	printk("mydevice_exit\n");

	dev_t dev = MKDEV(mydevice_major, MINOR_BASE);
	
	/* 5.Ce pilote de périphérique(cdev)Depuis le noyau*/
	cdev_del(&mydevice_cdev);

	/* 6.Supprimez l'enregistrement de numéro majeur utilisé par ce pilote de périphérique*/
	unregister_chrdev_region(dev, MINOR_NUM);
}

module_init(mydevice_init);
module_exit(mydevice_exit);

Construire et charger

La construction et le chargement sont les mêmes que la dernière fois. Bien qu'il s'agisse d'une création de fichier de périphérique, puisque le nombre majeur est déterminé dynamiquement, il est difficile de vérifier chacun d'eux, je vais donc le résumer avec la commande suivante. À propos, dans l'environnement Raspeye, le nombre majeur est 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

J'ai créé / dev / mydevice0, / dev / mydevice1 et / dev / mydevice2 expérimentalement. Lorsque cdev_add est terminé, le nombre de périphériques est spécifié comme 2. Je peux donc créer / dev / mydevice2, mais quand j'essaye de l'ouvrir ou d'y accéder, j'obtiens une erreur comme cat: / dev / mydevice2: No such device or addres.

Terminer le traitement

Comme pour le cas statique, supprimez-le ci-dessous.

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

Remarque: si vous souhaitez modifier le traitement de chaque numéro mineur

Par exemple, vous pouvez changer le processus entre le numéro mineur 0 (/ dev / mydevice0) et le numéro mineur 1 (/ dev / mydevice1). Dans la fonction de gestionnaire de lecture / écriture, vous pouvez utiliser le nombre mineur pour diviser le traitement par cas de commutateur, mais cela peut également être réalisé en séparant la table de gestionnaire à enregistrer. Plus précisément, dans le code ci-dessus, préparez s_mydevice_fops pour le nombre de mineurs dont vous voulez diviser le traitement (par exemple, s_mydevice_fops0 et s_mydevice_fops1). En transformant struct cdev mydevice_cdev; en un tableau et en définissant différemment le contenu de traitement de 3, 4 et 5 (cdev_init (), cdev_add (), cdev_del (), respectivement), Je peux le faire.

Créer automatiquement un fichier de périphérique (/ dev / XXX) correspondant à udev

Avec la méthode ci-dessus, vous pouvez maintenant charger le pilote de périphérique auquel est attribué dynamiquement le numéro majeur. Cependant, vous devez créer le fichier de périphérique manuellement, ce qui est fastidieux. Linux a un mécanisme appelé udev. Si vous enregistrez une classe dans / sys / class / lors du chargement du pilote, un démon appelé udevd la détectera et créera automatiquement un fichier de périphérique. En termes d'implémentation, un processus supplémentaire d '"enregistrement d'une classe dans / sys / class / lors du chargement d'un pilote" est requis.

Remarque

En fait, udev semble être utilisé pour la fonction plug & play. Si vous placez les modules de pilote de périphérique (.ko) dans un emplacement spécial (/ lib / modules /), il semble que le pilote de périphérique correspondant sera chargé automatiquement lorsque le périphérique est détecté. Par conséquent, insmod n'est pas nécessaire.

Code source du pilote de périphérique

Seuls mydevice_init et mydevice_exit ont changé, donc je vais juste les extraire. Des processus d'enregistrement et de suppression de classe ont été ajoutés respectivement.

myDeviceDriver.c(Partiellement omis)



/*Objet de classe de pilote de périphérique*/
static struct class *mydevice_class = NULL;

/*Route(insmod)Fonctions parfois appelées*/
static int mydevice_init(void)
{
	printk("mydevice_init\n");

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

	/* 1.Sécurisez un numéro majeur gratuit*/
	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.Obtenu dev( =Numéro majeur+Numéro mineur)Obtenez le numéro de mesure et conservez-le*/
	mydevice_major = MAJOR(dev);
	dev = MKDEV(mydevice_major, MINOR_BASE);	/*Inutile? */

	/* 3.Initialisation de la structure cdev et enregistrement de la table du gestionnaire d'appels système*/
	cdev_init(&mydevice_cdev, &s_mydevice_fops);
	mydevice_cdev.owner = THIS_MODULE;

	/* 4.Ce pilote de périphérique(cdev)Vers le noyau*/
	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.Enregistrer la classe de cet appareil(/sys/class/mydevice/faire) */
	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*faire*/
	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;
}

/*Décharger(rmmod)Fonctions parfois appelées*/
static void mydevice_exit(void)
{
	printk("mydevice_exit\n");

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

	/* 8.Supprimer l'enregistrement de cours pour cet appareil(/sys/class/mydevice/Supprimer) */
	class_destroy(mydevice_class);

	/* 9.Ce pilote de périphérique(cdev)Depuis le noyau*/
	cdev_del(&mydevice_cdev);

	/* 10.Supprimez l'enregistrement de numéro majeur utilisé par ce pilote de périphérique*/
	unregister_chrdev_region(dev, MINOR_NUM);
}

module_init(mydevice_init);
module_exit(mydevice_exit);

Ajouter un fichier de règles

Avec le code ci-dessus, / sys / class / mydevice / mydevice0 / dev sera créé au moment de insmod. Le mécanisme udev crée automatiquement le fichier de périphérique / dev / mydevice0 basé sur les informations contenues dans / sys / class / mydevice / mydevice0 / dev.

Cependant, par défaut, vous n'aurez pas de privilèges d'accès pour les utilisateurs généraux. Ajoutez un fichier de règles pour modifier les autorisations d'accès. Le fichier de règles se trouve dans / etc / udev / rules.d /. Le fichier de règles lui-même commence par un nombre et semble être n'importe quel fichier avec une extension .rules. Pour Raspeye, il existe depuis le début un fichier de règles appelé / etc / udev / rules.d / 99-com.rules, vous pouvez donc l'ajouter ici. Je voulais le diviser, alors j'ai fait ce qui suit. Avec Raspeye, je ne pouvais pas créer de nouveau fichier dans / etc / udev / rules.d / même si j'avais ajouté sudo, donc je suis entré en mode super utilisateur une fois avec sudo -i et j'ai ensuite effectué l'opération.

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

Construire et charger

Essayez de construire et insmod. Cette fois, les fichiers de périphérique (/ dev / mydevice0 et / dev / mydevice1) sont automatiquement créés simplement par insmoding.

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

De plus, un fichier contenant des informations sur le périphérique est créé dans le répertoire / sys / class / mydevice / mydevice0 / et en dessous. Si udev ne fonctionne pas, vérifiez également ici.

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

Mise en garde

Le contenu de cet article est en ligne avec le contenu de "Linux Device Driver Programming (Yutaka Hirata)". Le livre utilise class_device_create pour créer la classe. Cependant, class_device_create est maintenant obsolète. Cet article utilise device_create à la place.

Recommended Posts

Comment créer un pilote de périphérique Linux intégré (11)
Comment créer un pilote de périphérique Linux intégré (8)
Comment créer un pilote de périphérique Linux intégré (1)
Comment créer un pilote de périphérique Linux intégré (4)
Comment créer un pilote de périphérique Linux intégré (7)
Comment créer un pilote de périphérique Linux intégré (2)
Comment créer un pilote de périphérique Linux intégré (3)
Comment créer un pilote de périphérique Linux intégré (6)
Comment créer un pilote de périphérique Linux intégré (5)
Comment créer un pilote de périphérique Linux intégré (10)
Comment créer un pilote de périphérique Linux intégré (9)
Comment créer un pilote de périphérique Linux intégré (12) (Terminé)
Comment faire reconnaître Yubico Yubikey par Manjaro Linux
Comment créer un outil CLI interactif avec Golang
Comment créer un serveur HTTPS avec Go / Gin
[Python] Comment créer une matrice de contiguïté / liste de contiguïté [Théorie des graphes]
Comment créer un laboratoire de piratage - Kali Linux (2020.1) VirtualBox 64-bit edition -
Comment créer un package Python (écrit pour un stagiaire)
Comment créer un fichier ISO (image CD) sous Linux
Comment faire une traduction japonais-anglais
Comment créer un bot slack
Comment installer VMware-Tools sur Linux
Comment créer un robot - Avancé
Comment créer une fonction récursive
Comment installer MBDyn (Linux Ubuntu)
[Blender] Comment créer un plug-in Blender
[Blender] Comment rendre les scripts Blender multilingues
Comment créer un robot - Basic
Comment créer un pilote de langage MongoDB C
Comment vérifier la version du système d'exploitation Linux
Comment transformer une chaîne en tableau ou un tableau en chaîne en Python
Comment obtenir le pilote d'imprimante pour Oki Mac sous Linux
Comment créer mon propre serveur Linux
Comment rendre le sélénium aussi léger que possible
[Linux] Comment subdiviser des fichiers et des dossiers
Comment créer un bot LINE à intelligence artificielle avec l'API de messagerie Flask + LINE
Comment installer aws-session-manager-plugin sur Manajro Linux
[Python] Comment rendre une classe itérable
python3 Comment installer un module externe
Comment créer un environnement NVIDIA Docker
Comment convertir Python en fichier exe
Je veux savoir comment fonctionne LINUX!
[Linux] Comment utiliser la commande echo
Comment afficher des pictogrammes sur Manjaro Linux
Comment installer des packages sur Alpine Linux
[Cocos2d-x] Comment créer une liaison de script (partie 2)
Comment faire fonctionner Linux depuis la console
Comment installer le sous-système Windows pour Linux
Comment mettre hors tension de Linux sur Ultra96-V2
Comment mettre à jour la sécurité sur CentOS Linux 8
Je veux faire un programme d'automatisation!
Comment installer php7.4 sur Linux (Ubuntu)
Comment créer une clé USB à démarrage multiple (compatible Windows 10)
Comment créer un indicateur personnalisé Backtrader
Comment créer un plan de site Pelican