mardi 18 septembre 2007

Un driver caractère pour linux

Pour faire suite à l'article précédent, je vais vous présenter un deuxième driver "hello world", qui lui est "un vrai" pilote de périphérique.

Le source



#include linux/fs.h
#include linux/sched.h
#include linux/errno.h
#include linux/init.h
#include linux/module.h
#include linux/kernel.h
#include asm/current.h
#include asm/segment.h
#include asm/uaccess.h

/* le message à retourner */
char message[80]="bonjour monde";

/* ma fonction open. */
int mon_open(struct inode *inode,struct file *filep)
{
return 0;
}

/* ma fonction close. */
int mon_release(struct inode *inode,struct file *filep)
{
return 0;
}

/* ma fonction lecture. */
ssize_t mon_read(struct file *filep,char *buff,size_t count,loff_t *offp )
{
/* fonction pour copier le tampon depuis l'espace du noyau vers l'espace utilisateur*/
if ( copy_to_user(buff,message,strlen(message)) != 0 )
printk( "Kernel -> Erreur copie vers espace utilisateur\n" );
return strlen(message);

}

/* ma fonction lecture */
ssize_t mon_write(struct file *filep,const char *buff,size_t count,loff_t *offp )
{
/* On ne fait rien en écriture */
return 0;
}

/* Cette structure permet de définir les 4 fonctions de base que doit remplir */
/* un pilote caractère. Ces 4 fonctions sont celles décrites par les prototypes */
/* que l'on vient de décrire. */
/* Ces fonctions sont l'ouverture du périphérique, sa fermeture, l'écriture et */
/* la lecture. */
struct file_operations mon_fops={
open: mon_open,
read: mon_read,
write: mon_write,
release:mon_release,
};

MODULE_AUTHOR("olivier thebault");
MODULE_DESCRIPTION("Hello world");
MODULE_LICENSE("GPL");

static int hello_init(void) {
/* enregistre le driver auprès du système. */
/* On précise son numéro majeur, son nom */
/* et la structure file_operations qui */
/* contient les références aux fonction de */
/* base du driver. */
/* Ce driver ne fait que retourner le message */
/* "message". */
if(register_chrdev(234,"Bonjour Monde",&mon_fops)){
printk("<1>déclaration impossible");
}
return 0;
}


static void hello_exit(void) {
/* On désenregistre le driver */
unregister_chrdev(234,"Bonjour Monde");
}

module_init(hello_init);
module_exit(hello_exit);



L'installation

Une fois la commande insmod appelée, il faut déclarer le chemin /dev du périphérique avec
la commande mknod /dev/bonjour c 234 0.

Test

La commande cat /dev/bonjour devrait vous permettre de voir le message Bonjour Monde.

Comment écrire un module pour linux ?

Après avoir fait des essais autour d'un driver windows, j'étais quelque peu échaudé par ce genre d'activité.

C'est pourquoi, je me suis contenté de faire un petit driver expérimental sous linux. Rien de
fabuleux, juste pour m'habituer à la démarche. Pour linux, un driver peut se présenter sous le forme
d'un module, ou directement être compilé dans le noyau.

Préparation
Pour commencer il faut bien avoir installer le compilateur gcc et make. Ensuite il faut s'assurer que les headers du kernel soient présents. (j'ai fait mes premiers pas sur un noyau 2.6.18)
Ensuite à partir de là, il s'agit de créer des liens symboliques sur les entêtes du noyau, de la manière suivante :

ln -s /lib/modules/2.6.18-5-686/build/ /usr/src/linux
ln -s /usr/src/linux-headers-2.6.18-5-686 /lib/modules/2.6.18-5-686/build/

Maintenant, la compilation du module devrait marcher...

Mon premier module à un but unique : s'inscrire dans la liste des modules chargés.

Le code source (helloworld1.c)


#include linux/init.h
#include linux/module.h
#include linux/kernel.h

/* Les sources contiennent une série de macros permettant de faire */
/* des déclarations diverses au sujets des modules, comme ici la */
/* licence.*/
MODULE_LICENSE("GPL");

static int hello_init(void) {
printk("<1>Bonjour monde\n");
return 0;
}

static void hello_exit(void) {
printk("<1>Au revoir\n");
}

module_init(hello_init);
module_exit(hello_exit);

Le seule chose que ce driver fait, c'est d'écrire un message en log système lorsqu'on
charge et lorsque on le décharge. Il est un peu prématuré de parler de driver pour ce
petit module car il n'est pas capable de faire grand chose. Il ne possède pas de
numéro majeur/mineur non plus.

La compilation

  • Le Makefile

obj-m := helloworld1.o

  • Un petit script pour lancer la compilation

make -C /usr/src/linux-headers-2.6.18-5-686 M=`pwd` modules


Bien entendu, ce script est à adapter en fonction de la version du noyau.

L'installation

par la commande insmod ./helloworld.ko

ensuite pour vérifier : lsmod

Module Size Used by
helloworld1 1408 0
ppdev 8676 0
lp 11012 0
button 6672 0
.....

puis on le désinstalle : rmmod helloworld1.ko

lsmod

Module Size Used by
ppdev 8676 0
lp 11012 0
button 6672 0
ac 5188 0
......

dans le log système on trouve :

Sep 18 20:56:58 localhost kernel: Bonjour monde
Sep 18 20:58:11 localhost kernel: Au revoir

En fait, tout cela est très simple, en tout cas plus simple que sur un système windows.