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

6ème: Implémentation d'ioctl

À 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/06_01

Contenu de cette époque

Jusqu'à la dernière fois, j'ai expliqué comment implémenter les appels système de base (ouvrir, fermer, lire, écrire). De plus, en les utilisant, j'ai en fait créé un pilote de périphérique pour GPIO de Raspeye. Cette fois, je vais également implémenter un appel système appelé ioctl.

Qu'est-ce que ioctl

#include <sys/ioctl.h>
int ioctl(int d, int request, ...);

L'appel système défini ci-dessus. Entrez le descripteur de fichier (fd) obtenu par open dans le premier argument. Le deuxième argument s'appelle une requête, mais c'est une commande. Le troisième argument (longueur variable) est un paramètre. En utilisant cet ioctl, vous pouvez ajouter librement des interfaces côté pilote.

Jusqu'à présent, j'ai utilisé cdev pour créer des pilotes de périphériques. En conséquence, il est reconnu par le noyau comme un pilote de périphérique de type caractère. Par conséquent, lors de l'interaction avec l'application utilisateur en lecture et en écriture, le type de caractère a été utilisé. C'est bien pour les interactions simples, mais ce n'est pas suffisant lorsque vous contrôlez réellement l'appareil. Par exemple, il est nécessaire de spécifier un nombre pour le réglage de la vitesse de communication SPI ou le réglage de l'adresse d'esclave I2C, mais cela ne peut pas être fait uniquement par lecture et écriture. Dans ce cas, ajoutez une interface côté pilote de périphérique. Utilisez ioctl pour cela.

Remarque

Comme mentionné ci-dessus, ioctl définit ses propres commandes et paramètres pour chaque pilote de périphérique. Par conséquent, la spécification (en-tête) à vérifier lors de l'utilisation d'ioctl n'est pas ioctl.h, mais l'en-tête (ou le code source) préparé par chaque pilote de périphérique. Cette fois, je vais essayer de créer un pilote de périphérique moi-même, donc je pense que vous pouvez vous en faire une idée.

Implémentation du gestionnaire d'appels système ioctl

Définition d'en-tête

Là encore, ioctl définit ses propres commandes et paramètres côté pilote de périphérique. Les commandes sont des nombres de type int et les paramètres sont généralement des structures. (Aucun paramètre n'est requis). Créez un en-tête avec ces définitions. Puisqu'il sera référencé lorsque l'utilisateur l'utilisera, ce sera un fichier séparé.

myDeviceDriver.h


#ifndef MY_DEVICE_DRIVER_H_
#define MY_DEVICE_DRIVER_H_

#include <linux/ioctl.h>

/***Paramètres pour ioctl(3e argument)Définition de***/
struct mydevice_values {
	int val1;
	int val2;
};


/***Commande pour ioctl(request,2ème argument)Définition de***/
/*Type de commande pour IOCTL utilisé par ce pilote de périphérique. Tout va bien'M'Essayez de*/
#define MYDEVICE_IOC_TYPE 'M'

/*Une commande pour définir une valeur pour Devadora. Le paramètre est mydevice_type de valeurs*/
#define MYDEVICE_SET_VALUES _IOW(MYDEVICE_IOC_TYPE, 1, struct mydevice_values)

/*Une commande pour obtenir la valeur de Devadora. Le paramètre est mydevice_type de valeurs*/
#define MYDEVICE_GET_VALUES _IOR(MYDEVICE_IOC_TYPE, 2, struct mydevice_values)


#endif /* MY_DEVICE_DRIVER_H_ */

Pour le moment, cette fois, je vais créer une commande pour lire et écrire deux valeurs (val1 et val2). Cela n'a pas de signification particulière. Les noms de commande (demande) doivent être «MYDEVICE_SET_VALUES» et «MYDEVICE_GET_VALUES». Cette commande fonctionne en logique même si vous tapez le nombre directement. Par exemple, vous pouvez effectuer les opérations suivantes.

#define MYDEVICE_SET_VALUES 3	//Les nombres n'ont pas de signification particulière. Tout ce qui est unique dans ce pilote est OK
#define MYDEVICE_GET_VALUES 4	//Les nombres n'ont pas de signification particulière. Tout ce qui est unique dans ce pilote est OK

En fait, à partir d'informations telles que "si le paramètre est en lecture ou en écriture ou en lecture / écriture", "type", "numéro unique", "taille du paramètre", _IO, _IOW, _IOR, Il semble qu'il soit courant de générer en utilisant la macro _IOWR. Cette fois, le nom du pilote de périphérique est "myDeviceDriver", j'ai donc défini le type sur "M".

En tant que type de paramètre, définissez également une structure struct mydevice_values qui a deux valeurs.

Implémentation du pilote de périphérique

Le code côté pilote de périphérique n'a besoin que d'une fonction de gestionnaire à utiliser lorsque ioctl est appelé et d'un enregistrement dans la table struct file_operations. Vous trouverez ci-dessous le code extrait uniquement des parties pertinentes.

myDeviceDriver.c


#include "myDeviceDriver.h"

/*Variables contenant des valeurs pour les tests ioctl*/
static struct mydevice_values stored_values;

/*Fonction appelée lors de ioctl*/
static long mydevice_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	printk("mydevice_ioctl\n");
	
	switch (cmd) {
	case MYDEVICE_SET_VALUES:
		printk("MYDEVICE_SET_VALUES\n");
		if (copy_from_user(&stored_values, (void __user *)arg, sizeof(stored_values))) {
			return -EFAULT;
		}
		break;
	case MYDEVICE_GET_VALUES:
		printk("MYDEVICE_GET_VALUES\n");
		if (copy_to_user((void __user *)arg, &stored_values, sizeof(stored_values))) {
			return -EFAULT;
		}
		break;
	default:
		printk(KERN_WARNING "unsupported command %d\n", cmd);
		return -EFAULT;
	}
	return 0;
}

/*Table des gestionnaires pour divers appels système*/
static struct file_operations mydevice_fops = {
	.open    = mydevice_open,
	.release = mydevice_close,
	.read    = mydevice_read,
	.write   = mydevice_write,
	.unlocked_ioctl = mydevice_ioctl,
	.compat_ioctl   = mydevice_ioctl,	// for 32-bit App
};

Les commandes et les paramètres sont définis dans le fichier d'en-tête, ils sont donc inclus. De plus, cette fois, j'ai fait une commande pour définir et OBTENIR la valeur comme un essai, j'ai donc préparé une variable statique pour la contenir. Dans l'implémentation réelle, veuillez le gérer correctement en utilisant private_data dans la structure de fichiers.

C'est le processus dans lequel la fonction mydevice_ioctl est appelée par ioctl. Comme vous pouvez le voir, il n'y a qu'une instruction switch-case pour la commande (demande). Puisque le pointeur vers le paramètre est en arg, transtypez-le de manière appropriée en lecture et en écriture. C'est la même chose que lire et écrire. Enfin, inscrivez-vous dans la table struct file_operations. Bien que non décrite ici, cette table est enregistrée avec cdev_init, cdev_add lorsque le noyau est chargé.

Remarque

Le contenu de cet article est en ligne avec le contenu de "Linux Device Driver Programming (Yutaka Hirata)". Enregistrez la fonction dans struct file_operations pour enregistrer le gestionnaire ioctl. Dans le livre, je me suis inscrit en utilisant le membre .ioctl. Cependant, il est désormais obsolète. À la place, utilisez .unlocked_ioctl et .compat_ioctl. Il semble y avoir deux raisons de prendre en charge les applications 32 bits dans un environnement 64 bits. Les détails ont été expliqués à ici.

Dans le livre, comment prendre en charge les deux environnements 32 bits / 64 bits, tels que l'ajout de remplissage, a également été présenté. Peut-être que .compat_ioctl a été ajouté pour éviter cela. peut être.

Essayez d'appeler ioctl depuis le programme utilisateur

Essayez d'appeler ioctl du pilote de périphérique implémenté avec le programme de test suivant. Définissez la valeur ({1, 2}) avec MYDEVICE_SET_VALUES. Après cela, essayez d'obtenir la valeur avec MYDEVICE_GET_VALUES.

test.c


#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include "myDeviceDriver.h"

int main()
{
	int fd;
	struct mydevice_values values_set;
	struct mydevice_values values_get;
	values_set.val1 = 1;
	values_set.val2 = 2;

	if ((fd = open("/dev/mydevice0", O_RDWR)) < 0) perror("open");

	if (ioctl(fd, MYDEVICE_SET_VALUES, &values_set) < 0) perror("ioctl_set");
	if (ioctl(fd, MYDEVICE_GET_VALUES, &values_get) < 0) perror("ioctl_get");
	
	printf("val1 = %d, val2 = %d\n", values_get.val1, values_get.val2);

	if (close(fd) != 0) perror("close");
	return 0;
}

Exécuter

Tout d'abord, créez et chargez le pilote de périphérique. Ensuite, créez et exécutez le programme de test.

make
sudo insmod MyDeviceModule.ko
gcc test.c
./a.out
val1 = 1, val2 = 2

Ensuite, vous pouvez voir que la valeur définie peut être lue comme ceci.

en conclusion

Même si vous êtes un utilisateur qui n'écrit pas vous-même le pilote de périphérique, ioctl est toujours requis lorsque vous touchez un périphérique. C'était toujours compliqué et je n'étais pas sûr, mais en l'implémentant moi-même, je pouvais bien le comprendre.

Recommended Posts

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 bits Partie 2-
Comment créer un laboratoire de piratage - Kali Linux (2020.1) VirtualBox 64-bit edition -
Comment créer un fichier ISO (image CD) sous Linux
Comment faire une traduction japonais-anglais
Comment créer un bot slack
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 rendre les caractères de Word Cloud monochromatiques
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 mettre à jour PHP sur Amazon Linux 2
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