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

9ème fois: Appeler une fonction d'un autre module du noyau / Utiliser la fonction de contrôle GPIO

À 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/09_01 https://github.com/take-iwiw/DeviceDriverLesson/tree/master/09_02

Contenu de cette époque

Dans le quatrième opus de cette série, j'ai implémenté un pilote de périphérique GPIO pour Raspeye. À ce moment-là, le contrôle était effectué en frappant directement le registre. L'adresse du registre et la valeur de consigne ont été définies en consultant la fiche technique du BCM2835. Ce sont des informations "dépendantes de la puce". Lorsque je crée un pilote de périphérique qui contrôle un périphérique externe tel qu'un capteur ou un moteur, je ne veux pas voir la fiche technique de chaque puce. Il existe une fonction pour le contrôle GPIO, alors utilisons-la.

En relation avec cela, je voudrais essayer d'appeler d'abord les fonctions définies dans d'autres modules du noyau.

Appeler une fonction définie dans un autre module du noyau

Comme mentionné précédemment dans 4e: Implémentation en lecture / écriture et histoire de la mémoire, le noyau a un espace mémoire au total. Partager Cela inclut également les modules du noyau. Par conséquent, vous pouvez appeler les fonctions d'autres modules du noyau à partir du module du noyau que vous implémentez, ou vous pouvez appeler les fonctions qui sont statiquement intégrées dans le noyau lui-même.

Créer le module noyau A qui fournit des fonctions

Pour créer une fonction qui peut être appelée depuis d'autres modules, définissez simplement la fonction puis exportez-la avec ʻEXPORT_SYMBOL. ʻEXPORT_SYMBOL enregistre la fonction dans la table des symboles du noyau afin qu'elle puisse être appelée par d'autres modules du noyau.

Créez un module qui définit uniquement les fonctions d'entrée pour le chargement (insmod) et le déchargement (rmmod) et la fonction (mydevicea_func ()). Appelons cela MyDeviceDriverA. À l'origine, la déclaration de fonction doit être décrite dans l'en-tête, mais elle est gênante, elle est donc omise.

make pour créer MyDeviceDriverA.ko.

myDeviceDriverA.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

/***Informations sur cet appareil***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDeviceA"				/* /proc/Nom de l'appareil affiché sur les appareils, etc.*/

void mydevicea_func(void)
{
	printk("This is a message in mydevicea_func\n");
}
/*Inscrivez-vous dans la table des symboles du noyau. Rendez-le appelable à partir d'autres modules du noyau*/
EXPORT_SYMBOL(mydevicea_func);


/*Route(insmod)Fonctions parfois appelées*/
static int mydevicea_init(void)
{
	printk("[A]: mydevicea_init\n");
	mydevicea_func();

	return 0;
}

/*Décharger(rmmod)Fonctions parfois appelées*/
static void mydevicea_exit(void)
{
	printk("[A]: mydevicea_exit\n");
}

module_init(mydevicea_init);
module_exit(mydevicea_exit);

Créez le module B du noyau qui appelle la fonction fournie

Créez le module de noyau B qui appelle la fonction préparée précédemment. Vous pouvez l'appeler comme un langage C normal. Puisque l'en-tête de déclaration de fonction est omis cette fois, il est déclaré côté appelant avec extern. Je ne suis pas très sage. Au moment du chargement (insmod) de ce module B, essayez d'appeler la fonction précédente (mydevicea_func ()).

make pour créer MyDeviceDriverB.ko. L'état réel de mydevicea_func () n'est pas ici, mais il n'y a pas de problème lors de la création d'un module de noyau. Ceci est dû au fait que les modules make for kernel ne compilent et ne créent que des fichiers objets, pas des liens.

myDeviceDriverB.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

/***Informations sur cet appareil***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDeviceB"				/* /proc/Nom de l'appareil affiché sur les appareils, etc.*/

/*Route(insmod)Fonctions parfois appelées*/
static int mydeviceb_init(void)
{
	printk("[B]: mydeviceb_init\n");

	extern void mydevicea_func(void);
	mydevicea_func();

	return 0;
}

/*Décharger(rmmod)Fonctions parfois appelées*/
static void mydeviceb_exit(void)
{
	printk("[B]: mydeviceb_exit\n");
}

module_init(mydeviceb_init);
module_exit(mydeviceb_exit);

Essayez de courir

Commencez par charger MyDeviceDriverA.ko.

sudo insmod MyDeviceModuleA.ko
dmesg
[16909.979207] [A]: mydevicea_init
[16909.979223] This is a message in mydevicea_func

Après le chargement, si vous regardez le journal avec dmesg, vous pouvez voir que le processus d'initialisation du module A et la fonction ont été appelés dans init. Je ne pense pas que ce soit un problème.

Ensuite, chargez MyDeviceDriverB.ko.

sudo insmod MyDeviceModuleB.ko
dmesg
[17087.119434] [B]: mydeviceb_init
[17087.119449] This is a message in mydevicea_func

Ensuite, vous pouvez voir que le module B peut également appeler la fonction définie dans le module A de cette manière.

Dépendances

La dépendance est que le module B utilise le module A. Par conséquent, le module A doit être chargé avant de charger le module B. Sinon, une erreur se produira lors du chargement du module B. De même, le module B doit être déchargé avant que le module A puisse être déchargé. Si vous essayez de décharger le module A d'abord, vous obtiendrez une erreur.

Si vous implémentez correctement le contenu nécessaire et placez le module de noyau créé (.ko) à l'endroit approprié, vous pouvez utiliser mod probe au lieu de ʻins mod` pour charger automatiquement les modules dépendants également. Il semble qu'ils le feront.

Créer un module de noyau qui utilise les fonctions de contrôle GPIO

Fonction de contrôle GPIO dépendant de la puce

Comme mentionné au début, tant que vous créez des pilotes de périphériques pour les périphériques externes et les périphériques embarqués, je ne pense pas que vous ferez les paramètres de registre en regardant la fiche technique de la puce. Si vous êtes l'ingénieur d'un fabricant de SoC, que vous souhaitez étendre les fonctions ou si vous vous portez volontaire pour développer des dispositifs dépendant de la puce, vous en aurez besoin. (Cela ne peut donc pas être complètement hors de propos. Il peut y avoir des bugs.)

Lors du contrôle de GPIO à partir du pilote de périphérique, nous appelons les fonctions créées par ces personnes. Pour le moment, tout le monde ne l'implémente pas dans un format disjoint, mais l'implémente pour qu'il ait une interface comme celle de linux / gpio.h. En conséquence, les utilisateurs (via les développeurs Devadora) peuvent utiliser les fonctions de linux / gpio.h pour contrôler GPIO. Vous pouvez utiliser le même code sur une autre puce si vous utilisez les fonctions ici. (En regardant la documentation, il dit quelque chose comme "GPIO a un large éventail de fonctions, alors suivez linux / gpio.h autant que possible." Par conséquent, cela peut différer selon la puce utilisée. Il y a.)

La puce pour laquelle le processus de contrôle GPIO est utilisé est déterminée par les paramètres au moment de la construction du noyau. Dans le cas de la tarte à la râpe, vous devriez utiliser le processus pour bcm2835. Le traitement GPIO pour BCM2835 était dans pinctrl-bcm2835.c. Je ne l'ai pas suivi profondément, mais je pense qu'appeler la fonction dans linux / gpio.h mènera finalement à chaque processus de pinctrl-bcm2835.c. (En plus du contrôle de base du GPIO, il existe également un contrôle MUX en tant que broche fonctionnelle, donc cela semble assez compliqué.)

__ Quoi qu'il en soit, vous pouvez contrôler GPIO en utilisant les fonctions de linux / gpio.h __

Fonction de contrôle GPIO

Cette fonction est requise pour le contrôle GPIO de base.

Créer un module de noyau qui utilise les fonctions de contrôle GPIO

Comme il est difficile de mettre en œuvre la lecture / écriture, créez un pilote de périphérique (module noyau) avec les spécifications simples suivantes. En principe, on suppose que la LED est connectée au GPIO4 de Raspeye et que le bouton est connecté au GPIO17. La LED est connectée à 3,3 V via une résistance. Le bouton est connecté à GND et le côté GPIO17 est tiré vers le haut. Si cela pose problème, vous pouvez voir la sortie de GPIO4 avec un testeur, ou l'entrée de GPIO17 peut être directement connectée à 3,3V / GND.

Spécifications du module noyau à créer

--Lors du chargement d'un module (insmod)

Le code ressemble à ceci: Le gestionnaire d'interruption sera mydevice_gpio_intr (). Ceci est enregistré avec request_irq () au moment du chargement.

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 <linux/gpio.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>

/***Informations sur cet appareil***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDevice"				/* /proc/Nom de l'appareil affiché sur les appareils, etc.*/

#define GPIO_PIN_LED 4
#define GPIO_PIN_BTN 17

static irqreturn_t mydevice_gpio_intr(int irq, void *dev_id)
{
	printk("mydevice_gpio_intr\n");

	int btn;
	btn = gpio_get_value(GPIO_PIN_BTN);
	printk("button = %d\n", btn);
	return IRQ_HANDLED;
}

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

	/*Sortie GPIO4 pour LED. La valeur initiale est 1(High) */
	gpio_direction_output(GPIO_PIN_LED, 1);
	/*0 à GPIO4 pour LED(Low)Pour sortir*/
	gpio_set_value(GPIO_PIN_LED, 0);

	/*Entrez GPIO17 pour le bouton*/
	gpio_direction_input(GPIO_PIN_BTN);

	/*Obtenez le numéro d'interruption GPIO17 pour le bouton*/
	int irq = gpio_to_irq(GPIO_PIN_BTN);
	printk("gpio_to_irq = %d\n", irq);

	/*Enregistrer le gestionnaire d'interruption GPIO17 pour le bouton*/
	if (request_irq(irq, (void*)mydevice_gpio_intr, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "mydevice_gpio_intr", (void*)mydevice_gpio_intr) < 0) {
		printk(KERN_ERR "request_irq\n");
		return -1;
	}

	return 0;
}

/*Décharger(rmmod)Fonctions parfois appelées*/
static void mydevice_exit(void)
{
	printk("mydevice_exit\n");
	int irq = gpio_to_irq(GPIO_PIN_BTN);
	free_irq(irq, (void*)mydevice_gpio_intr);
}

module_init(mydevice_init);
module_exit(mydevice_exit);

Essayez de courir

Construisez et chargez comme suit.

make
sudo insmod MyDeviceModule.ko

La LED doit s'allumer. Après cela, essayez d'appuyer plusieurs fois sur le bouton ou de connecter GPIO17 à 3,3 V / GND.

dmesg
[19652.388837] mydevice_init
[19652.388873] gpio_to_irq = 183
[19654.100437] mydevice_gpio_intr
[19654.100457] button = 0
[19656.061705] mydevice_gpio_intr
[19656.061727] button = 1

Si vous regardez le journal avec dmesg, vous pouvez voir que le gestionnaire d'interruption enregistré est appelé et que la valeur d'entrée GPIO y est imprimée. En passant, vous pouvez vérifier l'état de l'interruption dans / proc / interrupts.

cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
 183:          7          0          0          0  pinctrl-bcm2835  17 Edge      mydevice_gpio_intr

Quels sont les paramètres de pull-up / pull-down?

Il ne semble y avoir aucun moyen général de le définir à partir du code (https://raspberrypi.stackexchange.com/questions/44924/how-to-set-pull-up-down-resistors-in-a-kernel-module). Il semble être défini dans l'arborescence des périphériques. En regardant brcm, bcm2835-gpio.txt, il semble qu'il soit défini en utilisant brcm, pull dans le fichier dts.

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 bits Partie 2-
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 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 rendre les caractères de Word Cloud monochromatiques
Comment créer mon propre serveur Linux
Comment créer un bot LINE à intelligence artificielle avec l'API de messagerie Flask + LINE
[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 à 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)
[Cocos2d-x] Comment créer une liaison de script (partie 1)
Comment trouver des fichiers volumineux sous Linux