Retrouvez cet article dans : Linux Magazine 101
Concept des espaces de travail
Le principe et les principales caractéristiques des trois espaces de travail suivants doivent être assimilés :- l’espace matériel (hardware space) ;
- l’espace noyau (kernel space) ;
- l’espace utilisateur (user space).
Principales structures du noyau à utiliser
FILE OPERATION: se situe dans<linux/fs.h>
struct file_operations mon_module_fops = {.owner = THIS_MODULE, .read = mon_module_read, .write = mon_module_write, .ioctl = mon_module_ioctl, .open = mon_module_open, .release = mon_module_release, ... };
FILE STRUCTURE: se situe dans<linux/fs.h>
struct file {const struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; void *private_data; ... };
CDEV STRUCTURE: se situe dans<linux/cdev.h>
Les fonctions suivantes devront être utilisées dans la fonction d’initialisation et de sortie du module. Dans la fonction d’initialisation :struct cdev {struct kobject kobj; struct module *owner; struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
void cdev_init(struct cdev *cdev, struct file_operations *fops); int cdev_add(struct cdev *cdev, dev_t num, unsigned int count);Dans la fonction de sortie :
void cdev_del(struct cdev *cdev);
mon_module_dev STRUCTURE: que vous créer dans votre fichier " .h "
Cette structure doit être allouée dynamiquement et sera désallouée à la fin de l’utilisation du périphérique. On intègre à notre propre structure, la structure cdev.struct mon_module_dev {struct cdev cdev; /*Char device structure*/ ...... }
Quelques outils pour la programmation
Valeurs pouvant être retournées par les fonctions
Les valeurs suivantes sont utilisées dans certaines fonctions du noyau, des fonctions que vous devez implémenter et des portions de codes que vous pouvez rajouter en fonction de vos souhaits (liste non exhaustive) :EINVAL: argument est invalide.EFAULT: pointeur pointant en dehors de l’espace d’adressage accessible.ENOMEM: mémoire insuffisante pour le noyau.EINTR: l’appel système a été interrompu par un signal avant d’avoir pu lire ou écrire quoi que ce soit.ENOTTY: erreur sur le descripteur de fichier.EPERM: droits insuffisants.
Allocation dynamique de variables et de structures
La fonction utilisée pour l’allocation dynamique de structures dans le noyau ressemble à l’allocation dynamique de structures dans l’espace utilisateur. La seule différence est l’argument de priorité à spécifier. La fonctionvoid *kmalloc(size_t size, int priority)
size: taille en octet de l’espace mémoire à allouer ;priority:
GFP_KERNEL: allocation mémoire " normale du noyauP_USER: allocation mémoire pour l’espace utilisateurGFP_ATOMIC: allocation provenant du gestionnaire d’interruptions (liste non exhaustive) ;
returnun pointeur de la mémoire allouée ou NULL si non alloué.
void kfree(const void *ptr)
Méthodes de débogage
Il existe plusieurs manières pour le débogage de votre programme.- Lecture directe des messages du noyau
- La spécification des flags du noyau dans certains
printk():
L’affichage de ces niveaux de messages (les niveaux de priorités dépendent de votre noyau et du démon klogd. Les définitions des niveaux de priorités de ces types de messages sont dans le fichier
KERN_EMERG: système inutilisable ;KERN_ALERT: action devant être effectuée immédiatement ;KERN_CRIT: condition critique ;KERN_ERR: condition d’erreur ;KERN_WARNING: condition d’avertissement ;KERN_NOTICE: normal, mais condition significative ;KERN_INFO: information ;KERN_DEBUG: messages de débogage.
- L’utilisation de logiciels tels que kdb ou kgdb, voire autres...
Étapes de codage
Remarque Une macro est une fonction noyau permettant d’identifier des valeurs particulières.Enregistrement du pilote de périphérique en tant que module
Sous Linux, un module est une partie de code qui peut être inséré dynamiquement (le système étant allumé) dans le noyau sans recompiler l’ensemble de ce dernier. Il n’est pas nécessaire de redémarrer le système afin que notre rajout soit opérationnel. Cependant, il est également possible d’effectuer une configuration afin que notre module soit chargé dès le démarrage du système. Un module peut utiliser toute autre fonction, avoir un accès aux entrées/sorties (Input/Output) et exécuter des parties du noyau. Concrètement, un module est un fichier binaire d’extension .ko qui signifie " kernel object ". Un même pilote de périphérique peut être utilisé quel que soit le nombre de périphériques identiques connectés à votre ordinateur, ainsi que pour plusieurs types de périphériques différents. Nous verrons par la suite comment se distingue l’utilisation du pilote. Un module est constitué de trois parties :- le point d’entrée ;
- le programme (pilote de périphérique) ;
- le point de sortie.
Paramètres de votre module
La commandeMODULE_AUTHOR("Jeremie Pilette");
MODULE_DESCRIPTION("Device driver");
MODULE_SUPPORTED_DEVICE("Reference(s) of peripheral(s)");
MODULE_LICENSE(“GPL/BSD”);
Paramètres d’enregistrement du module au sein du noyau
Chaque pilote est enregistré au sein du noyau à l’aide de deux numéros.- le " major number " ;
- le " minor number ".
#define N_DEV 1 mon_module_major = 0; mon_module_minor = 0; static dev_t devno; /*To store Major and minor numbers*/Variables :static int __init mon_module_init(void) {int result; /*Get the device numbers*/ if(mon_module_major) { devno = MKDEV(mon_module_major, mon_module_minor); result = register_chrdev_region(devno, N_DEV, “mon_module”); printk(“MAJOR: %d\nMINOR%d\n”, mon_module_major, mon_module_minor); } else { result = alloc_chrdev_region(&devno, mon_module_minor, N_DEV, “mon_module”); mon_module_major = MAJOR(devno); printk(KERN_INFO “MON_MODULE: MAJOR: %d\nMON_MODULE: MINOR:%d\n”, mon_module_major, mon_module _minor); } if(result < 0) { printk(KERN_WARNING “MON_MODULE: can’t get major %d\n”, mon_module_major); return result; } ... }
mon_module_major: initialisation du majormon_module_minor: initialisation du minordevno: variable noyau de typedev_tcontenant le " major number ", ainsi que le " minor number "N_DEV: le nombre de périphériques qui vont utiliser le pilote de périphérique.
MKDEV: macro qui associe le " major " et " minor " dans une seule variabledevno.MAJOR: macro qui extrait le " major " de la variabledevno.
/*Allocation statique*/ int register_chrdev_region(dev_t devno, unsigned int count, const char *name)
devno: la variable contenant les deux numéros ;count: le nombre de périphériques utilisés ;name: le nom de votre périphérique ;return 0si succès, valeur négative si échec.
/*Allocation dynamique*/ int alloc_chrdev_region(dev_t *devno, unsigned int baseminor, unsigned int count, const char *name)
devno: la variable contenant les deux numéros ;baseminor: la première valeur que vous souhaitez attribuer à votre périphérique. En général " 0 " ;count: le nombre de périphériques utilisés ;name: le nom de votre périphérique ;return 0si succès, valeur négative si échec.
Ajout du module au sein du noyau
Il s’agit du point d’entrée du module. Le programmeur implémente la fonctionmodule_init(mon_module_init);L’exemple suivant correspond à l’initialisation et l’ajout de la structure
static int __init mon_module_init(void) {
struct mon_module_dev *sdev = NULL;
/*Allocate a private structure and reference it as driver’s data*/
sdev = (struct mon_module_dev *)kmalloc(sizeof(struct mon_module_dev), GFP_KERNEL);
if(sdev == NULL) {
printk(KERN_WARNING "MON_MODULE: unable to allocate private structure\n");
return -ENOMEM;
}
/*Get the device numbers*/
...
/*Init the cdev structure*/
cdev_init(&sdev->cdev, &mon_module_fops);
sdev->cdev.owner = THIS_MODULE;
...
/*Add the device to the kernel*/
cdev_add(&sdev->cdev, mon_module_minor, 1);
...
return 0;
}
Variables :
Sdev: la structure de votre périphérique que vous compléterez en fonction de vos besoins.THIS_MODULE: ceci est la variable permettant de dire au noyau que cette structure ne sera utilisé que par votre module.
void cdev_init(struct cdev *cdev, struct file_operations *fops)
cdev: la structurecdevajoutée à votre noyau, que vous avez placé dans votre structuresdev;fops: correspond àmon_module_fops, il s’agit de votre file operation.
int cdev_add(struct cdev *cdev, dev_t num, unsigned int count)
cdev: la structurecdevajoutée à votre noyau, que vous avez placé dans votre structuresdev;num: ceci est simplement le numéro mineurmon_module_minor;count: correspond au nombre total de périphériques utilisant le pilote ;return 0si réussi, valeur négative si erreur.
Suppression du pilote au sein du noyau
Il s’agit du point de sortie module. Le programmeur implémente la fonctionmodule_exit(mon_module_exit);Exemple :
Variables :static void __exit mon_module_exit(void) {struct mon_module_dev *sdev; /*Delete the cdev structure*/ cdev_del(&sdev->cdev); /*Free the allocated memory*/ kfree(sdev); /*Unregister Driver from the kernel*/ unregister_chrdev_region(devno, N_DEV); }
sdev: la structure de votre périphérique que vous compléterez en fonction de vos besoins.
void unregister_chrdev_region(dev_t devno, unsigned int N_DEV)
devno: la variable contenant les deux numéros ;N_DEV: le nombre de périphériques qui ont été utilisés par le pilote de périphérique.
Ouverture du périphérique
La méthode static int mon_module_open(struct inode *inode, struct file *filp) {
int minor;
struct mon_module_dev *sdev = NULL; /*Device information*/
/*Allocate and fill any data structure to be put in filp->private_data*/
sdev = container_of(inode->i_cdev, struct mon_module_dev, cdev);
filp->private_data = sdev;
return 0;
}
Variables :
sdev: la structure de votre périphérique que vous compléterez en fonction de vos besoins ;filp->private_data: pointeur de sauvegarde de votre structure initialisée.
int mon_module_open(struct inode *inode, struct file *filp)
inode: structure interne au noyau, non utilisée par le programmeur ;filp: correspond àmon_module_fops, il s’agit de votre file operation ;return 0
Fermeture du périphérique
La méthodePrototypes :int mon_module_release(struct inode *inode, struct file *filp) {struct mon_module_dev *sdev; filp->private_data = NULL; sdev = NULL; return 0; }
int mon_module_release(struct inode *inode, struct file *filp)
inode: structure interne au noyau, non utilisé par le programmeur ;filp: correspond àmon_module_fops, il s’agit de votre file operation ;return 0
Portes d’accès entre espaces
a) La liaison entre l’espace matériel et l’espace noyau s’effectue par l’intermédiaire d’un gestionnaire de mémoire (memory management). Le système d’adressage du périphérique étant différent de celui du noyau, le gestionnaire de mémoire traduit les adresses physiques du périphérique en adresses virtuelles pour l’espace noyau. Vous devez savoir si votre périphérique effectue la liaison avec votre ordinateur par l’intermédiaire de ports d’entrées/sorties ou par l’allocation mémoire d’entrées/sorties. Le code sera alors différent en fonction de cette caractéristique. Ports d’entrées/sorties La fonctionUne ressource étant allouée lors de l’initialisation, il faut donc la désallouer lors de la fin d’utilisation. Exemple :static int __init mon_module_init(void) {struct mon_module_dev *sdev = NULL; int *port = NULL; int i; /*Allocate a private structure and reference it as driver’s data*/ ... /*Get the device numbers*/ ... /*Init the cdev structure*/ ... /*Requested I/O ports*/ port = request_region(FIRST_PORT, PORT_NBER, ‘’my_device’’); if(port == NULL) printk(KERN_ERR ‘’MON_MODULE: Error port attribution\n’’); /*Add the device to the kernel*/ ... return 0; }
Vous pouvez utiliser les commandesstatic void __exit mon_module_exit(void) {struct mon_module_dev *sdev; int i; /*Delete the cdev structure*/ ... /*Free the allocated memory*/ ... /*Release I/O ports*/ release_region(FIRST_PORT, PORT_NBER); /*Unregister Driver from the kernel*/ ... }
FIRST_PORT: numéro de port que vous aurez attribué dans un fichier .h en hexadécimal ;PORT_NBER: nombre de ports que vous aurez attribués dans un fichier .h en hexadécimal.
<linux/ioport.h> struct ressource *request_region(unsigned long first, unsigned long n, const char *name)
first: numéro du premier port souhaité ;n: nombre de ports nécessaires à votre périphérique ;name: nom de votre périphérique ;return NULLsi échec.
viod release_region(unsigned long start, unsigned long n)
start: numéro du premier port ;n: nombre de port.
De même, la ressource allouée doit pouvoir être désallouée. Exemple :static int __init mon_module_init(void) {struct mon_module_dev *sdev = NULL; int *mem = NULL; int i; /*Allocate a private structure and reference it as driver’s data*/ ... /*Get the device numbers*/ ... /*Init the cdev structure*/ ... /*Requested I/O memories*/ mem = request_mem_region(BaseAddr, LenAddr, ‘’my_device’’); if(mem == NULL) printk(KERN_ERR ‘’MON_MODULE: Error memory allocation\n’’); /*Add the device to the kernel*/ ... return 0; }
Variables :static void __exit mon_module_exit(void) {struct mon_module_dev *sdev; int i; /*Delete the cdev structure*/ ... /*Free the allocated memory*/ ... /*Release I/O memories*/ release_mem_region(BaseAddr, LenAddr); /*Unregister Driver from the kernel*/ ... }
BaseAddr: adresse de base que vous aurez attribuée dans un fichier .h en hexadécimal ;LenAddr: longueur de la plage d’adresse souhaitée que vous aurez attribuée dans un fichier .h en hexadécimal.
<linux/ioport.h> struct ressource *request_mem_region(unsigned long start, unsigned long length, char *name)
start: adresse de base, de départ ;length: longueur de l’allocation requise ;name: nom de votre périphérique ;return NULLsi échec.
void * ioremap(unsigned long addr, unsigned long size)
addr: correspond à l’adresse physique de base de votre périphérique ;size: correspond à la longueur des adresses physiques du périphérique ;returnun pointeur sur les adresses virtuelles.
void iounmap(void *addr)
addr: adresse virtuelle attribuée
Vous pouvez utiliser les commandes



Transfert de données entre espace matériel et espace noyau
Afin de coder cette partie, vous devez posséder une bonne documentation de votre périphérique pour connaître la liste des registres accessibles en lecture et/ou écriture, ainsi que le nombre de lignes d’interruption que le périphérique peut mettre à disposition. C’est la fonction d’interruption qui gère ces transferts de données. Le périphérique génère une interruption sur le processeur de votre machine lorsque le périphérique souhaite communiquer des informations à l’utilisateur ou lorsque le périphérique reçoit ou émet une information. Cela dépend du périphérique. Là où les lignes d’interruption doivent être déclarée(s) lors de l’initialisation du module. (Prototype :static int __init mon_module_init(void) {struct mon_module_dev *sdev = NULL; int req; /*Allocate a private structure and reference it as driver’s data*/ ... /*Get the device numbers*/ ... /*Init the cdev structure*/ ... /*Requested I/O ports or I/O memories*/ ... /*Add the device to the kernel*/ ... /*Declare the interrupt line*/ req = request_irq(sdev->irq, mon_module_interrupt, IRQF_SHARED, "Device_name", sdev); if(req < 0) { printk(KERN_WARNING "MON_MODULE: Can’t get assigned irq %d\n", sdev->irq); return 0; }
int request_irq(unsigned int irq, mon_module_interrupt, unsigned long flags, const char *dev_name, void *dev_id)
irq: numéro de la ligne d’interruption ;mon_module_interrupt: nom du prototype de la fonction d’interruption ;flags: permet de définir le type de l’interruption ;-
SA_INTERRUPT: pour interruption "rapide" -
SA_SHIRQ: pour ligne d’interruption partagée - (liste non exhaustive)
-
dev_name: le nom de votre périphérique ;dev_id: numéro d’identification du périphérique (pour interruption partagée).
- Identifier la raison de l’interruption. Les raisons peuvent être les suivantes : arrivée d’une donnée, fin d’une transmission, indication d’erreurs...
- Changer la valeur du bit signalant l’interruption afin de permettre d’autres interruptions. Il s’agit ici de modifier la valeur d’un bit du principal registre de configuration des interruptions du périphérique.
- Appeler les fonctions du noyau pour la lecture ou l’écriture de données en fonction de la source d’interruption. Les fonctions appelées dépendent de la taille d’adressage des registres. Il peut s’agir de 8, 16 ou 32 bits. Dans ce cas, la valeur est précisée dans la fonction à utiliser.
Prototypes : Interruptionirqreturn_t mon_module_interrupt(int irq, void *dev_id, struct pt_regs *regs) {struct mon_module_dev *sdev = (mon_module_dev *)dev_id; /*Le code dépend de votre périphérique*/ /*Identifier la raison de l’interruption*/ /*Changer la valeur du bit signalant l’interruption*/ /*Appeler les fonctions du noyau pour la lecture ou l’écriture des données*/ return IRQ_HANDLED; }
irqreturn_t mon_module_interrupt(int irq, void *dev_id, struct pt_regs *regs)
irq: le numéro de l’interruption concernée ;dev_id: utilisé pour les lignes d’interruption partagées. Identifiant complémentaire de l’interruption lorsque la ligne d’interruption est partagée ;Regs: rarement utilisé. Non utilisé dans notre cas (registres stockés sur la pile) ;return IRQ_HANDLED(si l’interruption a bien été gérée).
Lecture:
int ioread8(void __iomem *addr)
int ioread16(void __iomem *addr)
int ioread32(void __iomem *addr)
addr: pointeur de l’adresse mémoire concernée
Ecriture:
void iowrite8(u8 val, void __iomem *addr)
void iowrite16(u16 val, void __iomem *addr)
void iowrite32(u32 val, void __iomem *addr)
val: Valeur à copiier à l’adresse mémoier "addr"
addr: pointeur de l’adresse mémoire concernée
Si vous utiliser les adresses directement :
Lecture:
unsigned int readb(addr)
unsigned int readw(addr)
unsigned int readl(addr)
addr: pointeur de l’adresse mémoire concernée
Ecriture:
void writeb(unsigned value, addr)
void writew(unsigned value, addr)
void writel(unsigned value, addr)
val: Valeur à copier à l’adresse mémoier "addr"
addr: pointeur de l’adresse mémoire concernée
Là où les lignes d’interruptions sont une ressource allouée, il ne faut pas oublier de la désallouer lors de l’arrêt du périphérique.
Exemple :
Prototype :static void __exit mon_module_exit(void) {struct mon_module_dev *sdev; /*Delete the cdev structure*/ ... /*Free the allocated memory*/ ... /*Free the interrupt line*/ free_irq(sdev->irq, sdev); /*Free the virtual memory addresses*/ ... /*Release I/O ports or I/O memories*/ ... /*Unregister Driver from the kernel*/ ... }
void free_irq(unsigned int irq, void *dev_id)
irq: numéro de la ligne d’interruption ;dev_id: identifiant complémentaire de la ligne d’interruption.
Transfert de données entre espace noyau et espace utilisateur
Le programmeur doit coder les deux méthodesssize_t mon_module_read(struct file *filp, char __user *buffer, size_t count, loff_t *offp)
filp: correspond àmon_module_fops, il s’agit de votre file operation ;buffer: mémoire tampon de l’espace utilisateur où seront placées les données ;count: taille des données à transférer ;offp: pointeur des données en cours de traitement ;returnle nombre d’octets lus.
ssize_t mon_module_write(struct file *filp, const char __user *buffer, size_t count, loff_t *offp)
filp: correspond àmon_module_fops, il s’agit de votre file operation ;buffer: mémoire tampon de l’espace utilisateur où sont les données à copier dans l’espace noyau ;count: taille des données à transférer ;offp: pointeur des données en cours de traitement ;returnle nombre d’octets écrits.
unsigned long copy_to_user(void __user *to, const void *from, unsigned long count)
to: mémoire tampon de l’espace utilisateur ;from: mémoire tampon de l’espace noyau ;count: nombre d’octets à copier en une fois, à l’appel de la fonction copy_to_user ;returnla quantité de mémoire restante à être copiée.
unsigned long copy_from_user(void *to, const void __user *from, unsigned long count)
to: mémoire tampon de l’espace noyau ;from: mémoire tampon de l’espace utilisateur ;count: nombre d’octets à copier en une fois, à l’appel de la fonctioncopy_from_user;returnla quantité de mémoire restante à être copiée.
Création des fichiers spéciaux
Les fichiers spéciaux sont créés à l’aide de la commande mknod. Exemple :mknod /dev/mon_moduleRemarque La commande
Synthèse
Le schéma suivant synthétise le fonctionnement global du pilote de périphérique.
Contrôle du périphérique
La fonction_IO(magic number, numero1)_IOW(magic number, numero2, type)_IOR(magic number, numero3, type)_IORW(magic number, numero4, type)
/*Magic number*/ #define MON_MODULE_MAGIC 0xcd /*A vous de voir pour cette valeur hexadécimale*/ /*ioctl commands*/ #define OPEN_CD _IO(MON_MODULE_MAGIC, 100) #define READ_IR _IOR(MON_MODULE_MAGIC, 101, int) ...Exemple :
int mon_module_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) {
struct pc104plus_dev *sdev = filp->private_data;
...
switch(cmd) {
/*Ouverture tiroir CD-ROM*/
case OPEN_CD:
...
break;
/*Lecture registre d’interruption*/
case READ_IR:
...
break;
}
return -ENOTTY;
}
Prototypes :
int ioctl(struct inode *inode, struct file *fops, unsigned int cmd, unsigned long arg)
inode: structure interne au noyau, non utilisée par le programmeur ;fops: correspond àmon_module_fops, il s’agit de votre file operation ;cmd: il s’agit de la variable attribuée pour chaque commande dans le fichier .h ;arg: argument optionnel de validité ;return –ENOTTY.
Compilation du programme (fichier Makefile)
Depuis la version du noyau 2.6, le fichier Makefile est différent d’un fichier Makefile classique d’un simple programme de l’espace utilisateur. Voici un exemple contenant les éléments principaux : Exemple :#Makefile for Linux driver 2.6.x
OBJS= mon_module.o
mon_module_OBJS= mon_module_main.o \
mon_module_xxxx.o \
mon_module_yyyy.o
#=============================
# Select kernel source folder
#=============================
KERNEL_SRC=/lib/modules/$(shell uname -r)/build
#=============
# Compilation
#=============
#If KERNELRELEASE is defined, we’ve been invoked from the kernel
#build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := $(OBJS)
mon_module-objs := $(mon_module_OBJS)
#Otherwise we were called directly from the command line;
#invoke the kernel build system
else
KERNELDIR ?= $(KERNEL_SRC)
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
endif
#==============
# Delete files
#==============
clean:
$(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) clean
rm -f Module.symvers
OBJS représente le nom de votre module après compilation. Ici, votre module sera le fichier Configuration du système pour l’insertion du module
Il existe deux commandes pour insérer son module dans le noyau. La commandeln -s /chemin_depuis_la_racine/mon_module.ko /lib/modules/version_du_noyau/2) Tapez la commande
depmod3) Le module et le lien symbolique doivent être autorisés en exécution :
chmod +x mon_module.ko chmod +x /lib/modules/version_du_noyau/mon_module.ko4) Éditez un fichier
install mon_module /sbin/modprobe --ignore-install mon_module remove mon_module /sbin/modprobe -r --ignore-remove mon_module5) Créez les liens symboliques pour les deux scripts
ln -s /chemin_depuis_la_racine/mon_module_mkdev.sh /etc/nom_repertoire/ ln -s /chemin_depuis_la_racine/mon_module_rmdev.sh /etc/nom_repertoire/
Récapitulatif de quelques fichiers " header " à inclure :
#include <linux/module.h> /*Pour la création du module*/
#include <linux/init.h> /*Pour l’utilisation du module*/
#include <linux/kernel.h> /*Pour l’utilisation de printk()*/
#include <linux/fs.h> /*Pour la structure file_operations*/
#include <linux/types.h> /*Pour l’utilisation de types comme size_t*/
#include <linux/cdev.h> /*Pour la structure cdev*/
#include <linux/errno.h> /*Pour la liste de erreurs*/
#include <linux/sched.h> /*Pour la déclaration de la ligne d’interruption*/
#include <linux/interrupt.h> /*Pour la fonction d’interruption*/
#include <linux/slab.h> /*Pour l’allocation mémoire kmalloc()/kfree()*/
#include <asm/io.h> /*Pour les fonctions ioremap/iounmap*/
#include <asm/uaccess.h> /*Pour les fonctions copy_{from,to}_user()*/
#include <asm/ioctl.h> /*Pour la fonction ioctl()*/
Remarques générales :
N’oubliez pas de faire un fichier- Linux device drivers, 3ème édition (Linux 2.6)
- Linux Treiber Entwickeln (Linux 2.6)
- Le noyau Linux (pour noyau 2.6)
Retrouvez cet article dans : Linux Magazine 101





Donnez votre avis
Vous devez avoir ouvert une session pour écrire un commentaire.