Implémentation d'un périphérique caractère
De Centre de Ressources Numériques - Labomedia
Révision de 12 décembre 2014 à 20:42 par Mushussu (discussion | contributions)
ModuleDMX.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h> // Permet d'ouvrir et de fermer un fichier et de lire et d'écrire
#include <linux/cdev.h>
#include <linux/semaphore.h> // Utilisé pour la synchronisation
#include <asm/uaccess.h> // Permet l'accès des données entre l'espace du Noyau et celui de l'utilisateur
// Création d'une structure pour notre périphérique
struct fake_device {
char data[100];
struct semaphore sem;
} virtual_device;
// Pour enregistrer le périphérique plus tard nous avons besoin d'un objet cdev et d'autres variables
struct cdev *mcdev; // m est préfixe veut dire que c'est le notre cdev = character device
int major_number;
int ret; //Variable de retour de fonctions, car la pile du noyau est très petite.
//Si nous déclarions des variables tout au long du programme cela engorgerait la pile rapidement
dev_t dev_num; // Enregistrera le nombre Majeur que le noyau nous donnera
// Le nom apparaît dans /proc/devices
#define DEVICE_NAME "DMX"
// Méthode appelée lors de l'ouverture du périphérique
// inode représente le fichier sur le disque
// file est une représentation abstraite du fichier
int device_open(struct inode *inode, struct file *filp) {
// Permet à un seul processus d'ouvrir ce périphérique par l'utilisation d'un semaphore
if (down_interruptible(&virtual_device.sem) != 0) {
printk(KERN_ALERT "DMX : Impossible de bloquer le peripherique pendant l'ouverture\n");
return -1;
}
printk(KERN_INFO "DMX : Le peripherique est ouvert\n");
return 0;
}
// Méthode appelée quand un utiliasteur veut lire des informations provenant du périphérique
ssize_t device_read(struct file* filp, char* bufStoreData, size_t bufCount, loff_t* curOffset) {
// Prend les données l'espace utilisateur (processus) vers l'espace du Noyau (périphérique)
//
printk(KERN_ALERT "DMX : Ecriture de donnees\n");
ret = copy_to_user(bufStoreData, virtual_device.data, bufCount);
return ret;
}
// Méthode appelée quand un utiliasteur veut lire des informations provenant du périphérique
ssize_t device_write(struct file* filp, const char* bufStoreData, size_t bufCount, loff_t* curOffset) {
// Prend les données de l'espace Noyau (périphérique) vers l'espace utlisateur (processus)
//
int i;
printk(KERN_ALERT "DMX : Lecture de donnees\n");
ret = copy_from_user(virtual_device.data, bufStoreData, bufCount);
for (i = 0; i < 100; i++) {
virtual_device.data[i]++;
}
return ret;
}
int device_release(struct inode *inode, struct file *filp) {
// Réalise l'inverse de d'ouverture en utilisant up pour le semaphore et permet ainsi
// aux autres processus de pouvoir utiliser le périphérique à présent
up(&virtual_device.sem);
printk(KERN_ALERT "DMX : Le peripherique est ferme\n");
return 0;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
static int driver_entry(void) {
// L'enregistrement de notre périphérique dans le système se fait en deux temps :
// 1 : Utilisation dynamique pour assigner notre périphérique
ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ALERT "DMX : impossible d'obtenir un nombre majeur\n");
return ret;
}
major_number = MAJOR(dev_num); // Extrait le nombre Majeur
printk(KERN_INFO "DMX : Le nombre majeur est %d\n", major_number);
printk(KERN_INFO "Utiliser \"mknod /dev/%s c %d 0\" pour le fichier de peripherique\n", DEVICE_NAME, major_number);
// 2 :
mcdev = cdev_alloc(); // Initialisation de notre périphérique
mcdev->ops = &fops; // Pointeur sur struct file_operations
mcdev->owner = THIS_MODULE;
// Après l'avoir initalisé, il faut l'ajouter au noyau
//
ret = cdev_add(mcdev, dev_num, 1);
if (ret < 0) {
printk(KERN_ALERT "DMX : Impossible de charger le peripherique dans le noyau\n");
return ret;
}
// Initialisation du semaphore
sema_init(&virtual_device.sem, 1);
return 0;
}
static void driver_exit(void) {
cdev_del(mcdev);
unregister_chrdev_region(dev_num, 1);
printk(KERN_ALERT "DMX : Le module est decharge\n");
}
module_init(driver_entry);
module_exit(driver_exit);
Makefile
obj-m += ModuleDMX.o
all: clean compile
compile:
make ARCH=arm CROSS_COMPILE=${CCPREFIX} -C /home/{nomUtilisateur}/rpi/linux-rpi-3.10.y M=$(PWD) modules
clean:
make -C /home/{nomUtilisateur}/rpi/linux-rpi-3.10.y M=$(PWD) clean