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)

(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à : navigation, rechercher

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