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.
https://github.com/take-iwiw/DeviceDriverLesson/tree/master/08_01
La dernière fois, j'ai utilisé procfs pour créer une interface pour le débogage du pilote de périphérique. Cependant, procfs est essentiellement un endroit pour mettre des informations sur les processus, et il n'est pas bon de l'utiliser pour le débogage. Il est recommandé d'utiliser debugfs pour le débogage. (Merci d'avoir signalé @rarul.)
Cette fois, je vais essayer d'implémenter la même chose que la dernière fois en utilisant ce debugfs. En particulier,
/sys/kernel/debug/MyDevice/prm1
Vous permet de lire et d'écrire des paramètres de débogage en accédant à. Veuillez noter que ce chemin peut changer en fonction de l'environnement.
Il existe deux manières principales de créer une interface debugfs. Je décrirai chaque méthode.
Pour créer une interface pour debugfs, enregistrez simplement le nom du fichier et la fonction que vous voulez appeler en lecture / écriture avec la fonction debugfs_create_file ()
où le pilote est chargé (insmod). Lors de l'enregistrement, définissez la fonction de gestion de lecture / écriture dans la table struct file_operations
. C'est exactement la même chose que le procfs précédent et l'enregistrement normal du gestionnaire de pilote de périphérique.
Cependant, cela créera un fichier pour l'accès à debugfs dans le répertoire racine de debugfs. Bien sûr, il existe également des fichiers pour d'autres modules du noyau, alors organisez-les dans un répertoire. Utilisez debugfs_create_dir ()
pour créer un répertoire pour debugfs. En mettant la valeur de retour (entrée) de debugfs_create_dir ()
dans le troisième argument de debugfs_create_file ()
, le fichier sera créé sous le répertoire créé.
Parfois, vous voulez simplement lire et écrire des paramètres pour le débogage, mais définir une fonction de lecture / écriture à chaque fois peut être fastidieux. Si vous n'avez besoin que d'opérations de lecture / écriture, vous pouvez créer des débogages avec une seule fonction d'assistance. Par exemple, pour accéder à une variable 32 bits, utilisez debugfs_create_u32 ()
(la définition n'était pas signée, mais j'ai pu entrer et sortir des nombres négatifs). Si vous voulez lire et écrire en hexadécimal, utilisez debugfs_create_x32 ()
. Il existe également des types booléens, des ensembles de registres (adresses et nombres) et des fonctions pour les objets blob qui peuvent être utilisés pour les données binaires. Pour le moment, je n'utiliserai que des nombres 32 bits.
Lorsque vous déchargez le noyau, vous devez supprimer le fichier debugfs que vous avez créé. Pour le supprimer, utilisez debugfs_remove ()
. Vous pouvez maintenant supprimer le fichier créé. Cependant, il est difficile de les effacer un par un. Surtout, il est difficile de se souvenir des informations d'entrée lors de leur création. C'est pourquoi debugfs_remove_recursive
() est fourni. En mettant l'entrée (valeur de retour de debugfs_create_dir ()
) du répertoire créé dans cette fonction, les fichiers en dessous seront également supprimés de manière récursive.
Le code ressemble à ceci: Il a debug_prm1
statiquement comme paramètre de débogage. Créez votre propre nom de répertoire sous le répertoire racine de debugfs "MyDevice" et conservez les informations d'entrée dans debug_entry_dir
.
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>
#include <linux/debugfs.h>
/***Informations sur cet appareil***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDevice" /* /proc/Nom de l'appareil affiché sur les appareils, etc.*/
/*Variables de débogage*/
struct dentry *debug_entry_dir; /*entrée de répertoire debugfs*/
static int debug_prm1; /*Paramètres de débogage(pour le test) */
static int debug_read_size = 0; /*Nombre d'octets à lire dans une ouverture*/
/* /sys/kernel/debug/MyDevice/debug_Fonction appelée lors de l'accès à prm1*/
static int mydevice_debug_open(struct inode *inode, struct file *file)
{
printk("mydevice_proc_open\n");
debug_read_size = 4; //Lire 4 octets à la fois
return 0;
}
/* /sys/kernel/debug/MyDevice/debug_Fonction appelée lors de la lecture de prm1*/
static ssize_t mydevice_debug_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk("mydevice_proc_read\n");
if (debug_read_size > 0) {
/*Lorsqu'il reste des données à sortir*/
/*Numéro de type entier conservé(debug_prm1)Est sortie sous forme de chaîne de caractères*/
int len;
len = sprintf(buf, "%d\n", debug_prm1); //Copier en fait_to_devrait être un utilisateur
debug_read_size -= 4;
return len;
} else {
return 0;
}
}
/* /sys/kernel/debug/MyDevice/debug_Fonction appelée lors de l'écriture de prm1*/
static ssize_t mydevice_debug_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
printk("mydevice_proc_write\n");
/*La chaîne de caractères saisie est un nombre de type entier(debug_prm1)Tenir comme*/
sscanf(buf, "%d", &debug_prm1); //Copier en fait_from_devrait être un utilisateur
return count;
}
/*Table des gestionnaires pour les débogages*/
static struct file_operations debug_debug_prm1_fops = {
.owner = THIS_MODULE,
.open = mydevice_debug_open,
.read = mydevice_debug_read,
.write = mydevice_debug_write,
};
/*Route(insmod)Fonctions parfois appelées*/
static int mydevice_init(void)
{
printk("mydevice_init\n");
/*Créer un répertoire pour debufs*/
debug_entry_dir = debugfs_create_dir(DRIVER_NAME, NULL);
if (debug_entry_dir == NULL) {
printk(KERN_ERR "debugfs_create_dir\n");
return -ENOMEM;
}
/*Méthode 1:Méthode d'enregistrement de la table du gestionnaire*/
debugfs_create_file("prm1", S_IRUGO | S_IWUGO, debug_entry_dir, NULL, &debug_debug_prm1_fops);
/*Méthode 2:Méthode de la fonction d'assistance*/
debugfs_create_u32("_prm1", S_IRUGO | S_IWUGO, debug_entry_dir, &debug_prm1);
debugfs_create_x32("_prm1_hex", S_IRUGO | S_IWUGO, debug_entry_dir, &debug_prm1);
return 0;
}
/*Décharger(rmmod)Fonctions parfois appelées*/
static void mydevice_exit(void)
{
printk("mydevice_exit\n");
/*Débarrassez-vous de pour debufs(Les fichiers enfants sont également supprimés automatiquement) */
debugfs_remove_recursive(debug_entry_dir);
}
module_init(mydevice_init);
module_exit(mydevice_exit);
La méthode 1 nécessitait de définir la fonction d'entrée et la table des gestionnaires, mais la méthode 2 se termine par une ligne. Bien sûr, la méthode 2 est simple, mais si vous avez besoin de traiter quelque chose en même temps que la réécriture des paramètres, il semble que vous deviez utiliser la méthode 1. (Par exemple, lors du changement des registres (paramètres) de l'appareil connecté par i2c.)
Construisez et chargez avec la commande suivante.
make
sudo insmod MyDeviceModule.ko
sudo ls /sys/kernel/debug/MyDevice
prm1 _prm1 _prm1_hex
Ensuite, vous pouvez voir que prm1
, _prm1
et _prm1_hex
sont créés sous / sys / kernel / debug / MyDevice
. «prm1» est implémenté par la méthode 1, et «_prm1» et «_prm1_hex» sont implémentés par la méthode 2. Accédez à toutes les mêmes variables. (Puisque le nom a été implémenté de cette manière, il est simplement devenu prm1 et n'a pas de signification particulière.)
sudo bash -c 'echo 12 > /sys/kernel/debug/MyDevice/prm1'
sudo cat /sys/kernel/debug/MyDevice/prm1
12
sudo cat /sys/kernel/debug/MyDevice/_prm1
12
sudo cat /sys/kernel/debug/MyDevice/_prm1_hex
0x0000000c
sudo bash -c 'echo 13 > /sys/kernel/debug/MyDevice/_prm1'
sudo bash -c 'echo 0x0f > /sys/kernel/debug/MyDevice/_prm1_hex'
Après cela, vous pouvez lire et écrire avec chat et écho. Le résultat sera le même car toutes les valeurs de la même variable sont entrées et sorties. Il semble que 0x soit automatiquement ajouté lorsqu'il s'agit d'un nombre hexadécimal.
Ce n'est peut-être qu'une tarte à la râpe.
Cette fois, lors de la création de chaque fichier debugfs, j'ai spécifié S_IRUGO | S_IWUGO
comme droit d'accès. Cela devrait être lisible et inscriptible par tous les utilisateurs. Mais en réalité, j'ai dû ajouter sudo
lors de l'exécution. Cela est dû au fait que les répertoires parents, / sys
, / sys / kernel
et / sys / kernel / debug
ne bénéficient pas d'un accès utilisateur général.
Je pense que cela peut être changé en modifiant les options de montage pour ces répertoires (probablement quelque part dans le script qui s'exécute au démarrage). Mais je ne l'ai pas suivi profondément et je ne pense pas qu'il devrait être changé.
Recommended Posts