diff --git a/README.md b/README.md index 6abd099..f5972fa 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,44 @@ Il faut le décharger à la main avec `rmmod`. * [usb-skeleton.c (Code Kernel)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/usb-skeleton.c) * [Nihaal - Eudyptula Challenge Task 5](https://nihaal.me/post/ec5/) +## Tache 6 + +Il existe plusieurs façons pour créer un nouveau fichier dans `/dev`. J'ai +cependant décidé d'implémenter qu'une seule manière pour l'instant. + +Pour pouvoir créer un fichier dans `/dev`, il faut créer le périphérique à la +main. Il faut connaitre le numéro majeur du driver, ce qui se fait ou en +regardant dans le fichier `/proc/devices` ou en regardant le journal du kernel. +Il suffit de taper la commande suivante pour créer le fichier : +`mknod /dev/eudyptula c 0`. N'oubliez pas de changer les droits si +vous souhaitez écrire dedans ! + +**Je n'ai pas suivi les consignes à la lettre, en effet je n'utilise pas un +*misc char device* mais un *char device*.** C'est pour ça qu'il faut créer à +la main le device. Je l'ai codé directement comme ça, ne trouvant pas beaucoup +d'informations sur les *misc char devices*. En lisant des codes sources ainsi +que la documentation, je comprends mieux comment en faire mais j'ai décidé de +ne pas changer tout le code que j'avais fait. Cepedant, je le modifierai si +l'on doit faire un *char device* plus tard. + +Lorsqu'on ouvre le device, on reçoit la chaine `coucou c'est rick`. Si l'on +souhaite écrire dans le fichier, il faut taper `congruent`, sinon un message +d'erreur est retourné. + +```bash +cat /dev/eudyptula +# coucou c'est rick +echo "congruent" > /dev/eudyptula +# aucune réponse, $? vaut 0 +echo "bonjour12" > /dev/eudyptula +-bash: echo: erreur d'écriture : Argument invalide +# $? vaut 1 +``` + + * [Chapitre 3, Linux Device Drivers, 3rd Edition](https://lwn.net/Kernel/LDD3/) + * [Linux Kernel Labs - Character device drivers](https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html#read-and-write) + * [Nihall - Misc character devices](https://nihaal.me/post/misc_char_devices/) + ## Informations diverses Je liste dans cette section quelques informations que j'ai pu découvrir en diff --git a/task_6/Makefile b/task_6/Makefile new file mode 100644 index 0000000..696b7d9 --- /dev/null +++ b/task_6/Makefile @@ -0,0 +1,9 @@ +obj-m += misc-device.o + +PWD := $(CURDIR) + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean diff --git a/task_6/misc-device.c b/task_6/misc-device.c new file mode 100644 index 0000000..c25d70d --- /dev/null +++ b/task_6/misc-device.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* TODO + * créer char device avec un mineur dynamique + * appeler le device eudyptula + */ + +#include +#include +#include +#include +#include + +#define BUFFER_LENGTH 10 +#define NUMBER_MINORS 1 + +static char *msg; +static int major; + +static ssize_t my_read(struct file *, char __user *buffer, size_t length, + loff_t *offset) +{ + /* le nombre de bytes qu'on va lire */ + int bytes = 0; + /* le pointeur qui va parcourir notre message pour le mettre dans le buffer + * du userspace + */ + const char *msg_tmp = msg; + + /* si on essaie de lire plus que le message */ + if (!*(msg + *offset)) { + *offset = 0; /* on reset l'offset */ + return 0; /* on a rien lu */ + } + + /* on commence à l'offset */ + msg_tmp += *offset; + + while (*msg_tmp && bytes < length) { + /* on va copier char par char le message dans le userspace */ + put_user(*(msg_tmp), (buffer + bytes)); + bytes++; + msg_tmp++; + } + + /* on déplace l'offset du nombre de bytes qu'on a lu */ + *offset += bytes; + + return bytes; +} + +static ssize_t my_write(struct file *, const char __user *buffer, + size_t size, loff_t *) +{ + int ret; + /* là où on stocke l'entrée utilisateur */ + char *tmp_buffer; + + /* on peut pas écrire dans un buffer sans place */ + if (size == 0) + return 0; + + /* on alloue BUFFER_LENGTH qui vaut exactement la taille de "congruent", le + * mot qu'on souhaite détecter + */ + tmp_buffer = kzalloc(BUFFER_LENGTH, GFP_KERNEL); + + /* si on arrive pas à copier les données utilisateurs, on retourne une + * erreur + */ + if (copy_from_user(tmp_buffer, buffer, BUFFER_LENGTH)) { + kfree(tmp_buffer); + return -EINVAL; + } + + /* comme on copie des bytes et non une string, il faut mettre le byte de fin + * de string manuellement, à la fin du buffer. + */ + *(tmp_buffer + BUFFER_LENGTH - 1) = '\0'; + pr_info("Test %s-\n", tmp_buffer); + + if (!strcmp(tmp_buffer, "congruent")) + /* on retourne la longueur du buffer écrit pour dire que tout est OK */ + ret = BUFFER_LENGTH; + else + ret = -EINVAL; + + kfree(tmp_buffer); + return ret; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = my_read, + .write = my_write, +}; + +static struct cdev *my_cdev; + +static int __init my_init(void) +{ + dev_t dev; + int ret; + + msg = kmalloc(19, GFP_KERNEL); + strcpy(msg, "coucou c'est rick\n"); + + /* on demande un numéro majeur dynamiquement avec uniquement un seul numéro + * mineur + */ + ret = alloc_chrdev_region(&dev, 0, NUMBER_MINORS, "eudyptula"); + if (ret) + pr_err("Erreur lors de l'initialisation du device : %d\n", ret); + major = MAJOR(dev); /* on récupère le numéro majeur alloué */ + /* on initialise notre chardevice */ + my_cdev = cdev_alloc(); + my_cdev->ops = &fops; + my_cdev->owner = THIS_MODULE; + ret = cdev_add(my_cdev, dev, NUMBER_MINORS); + if (ret) + pr_err("Erreur lors de la création du device : %d\n", ret); + + pr_info("Initialisation terminée, majeur: %d\n", major); + return 0; +} + +static void __exit my_exit(void) +{ + dev_t dev; + + pr_info("Suppression du device avec pour majeur: %d\n", major); + kfree(msg); + cdev_del(my_cdev); + dev = MKDEV(major, 0); + unregister_chrdev_region(dev, NUMBER_MINORS); +} + +module_init(my_init); +module_exit(my_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("rick "); +MODULE_DESCRIPTION("Création d'un device accessible en lecture et écriture.");