#define __KERNEL__ #define MODULE #include #include #include #include #include #include #include #include #include #include #include #include #include #define true 1 #define false 0 /* C'est le nom que nous choisissons pour notre périphérique. Nous l'utilisons également comme préfixe sur des fonctions telles que les points d'entrée apparaissant dans la structure file_operations. */ #define DEV_NAME "pp" static int Major; /* Ce sont des prototypes pour les résidents de la structure file_operations */ static ssize_t pp_read(struct file *, char *, size_t, loff_t *); static ssize_t pp_write(struct file *, const char *, size_t, loff_t *); static int pp_open(struct inode *, struct file *); static int pp_close(struct inode *, struct file *); /* C'est la structure file_operations. Comme la fonction init_module la déclarera avec le noyau, le noyau connaîtra tous les points d'entrée qu'elle contient. */ struct file_operations Fops = { owner: THIS_MODULE, read: pp_read, write: pp_write, open: pp_open, release: pp_close, }; /* La fonction pp_probe n'a aucun rôle ici mais nous rappelle qu'un pilote 'réel' peut avoir besoin de détecter des ressources matérielles. Ces ressources pourraient plus tard être allouées dans init_module. */ static int pp_probe(void){ return 0; } /* La fonction pp_read est un moignon mais au moins elle effectue un printk, à des fins de traçage, lorsqu'elle est appelée. */ static ssize_t pp_read(struct file *file, char *buff, size_t ctr, loff_t *woof) { printk(KERN_ALERT "\npp_read active.\n"); return 0; } /* La fonction pp_write est un moignon mais au moins elle effectue un printk, à des fins de traçage, lorsqu'elle est appelée. */ static ssize_t pp_write(struct file *file, const char *buff, size_t ctr, loff_t *woof) { printk(KERN_ALERT "\npp_write active.\n"); return 0; } /* La fonction pp_open effectue un pintk à des fins de traçage. */ static int pp_open(struct inode *inode, struct file *file) { printk(KERN_ALERT "\nUne instance de %s a été ouverte.\n", DEV_NAME); return 0; } /* La fonction pp_close effectue un printk à des fins de traçage. */ static int pp_close(struct inode *inode, struct file *file) { printk(KERN_ALERT "\nUne instance de %s a été fermée.\n", DEV_NAME); return 0; } /* Ensuite, nous verrons que comme cet init_module * déclare la structure file_operations, le noyau connaîtra les points d'entrée qu'elle contient * retourne un numéro majeur * appelle pp_probe pour rechercher des ressources matérielles Si des ressources matérielles étaient trouvées, il faudrait les allouer pour que ce pilote puisse les utiliser, probablement dans la portée d'init_module. */ int init_module(void) { Major = register_chrdev( 0, DEV_NAME, &Fops); if (Major lt; 0) { printk("Déclaration impossible !\n"); return Major; } if (pp_probe() < 0) { unregister_chrdev(Major, DEV_NAME); printk(KERN_ALERT "pp_probe() failure!\n"); return -1; } printk(KERN_ALERT "\nDéclaré %s, au numéro majeur = %d.\n\n", DEV_NAME, Major); printk("Pour utiliser %s, vous devez créer un fichier de périphérique.\n", DEV_NAME); printk("Si cela n'a pas déjà été fait, alors saisissez :\n"); printk(" mknod /dev/%s c %d 0\n\n", DEV_NAME, Major); printk("Définissez également les permissions appropriées pour /dev/%s.\n\n", DEV_NAME); return 0; } /* La fonction cleanup_module annule la déclaration du pilote et, dans un pilote 'réel', libérerait toute ressource allouée par by init_module. */ void cleanup_module(void) { int ret; ret = unregister_chrdev(Major, DEV_NAME); if (ret < 0) printk(KERN_ALERT "\nProblème d'annulation de déclaration où ret = %d\n\n", ret); else printk(KERN_ALERT "\nAnnulé la déclaration de %s, au numéro majeur = %d\n\n", DEV_NAME, Major); }