add task 6
This commit is contained in:
parent
ed31555471
commit
2fca35a3d5
3 changed files with 190 additions and 0 deletions
38
README.md
38
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 <majeur> 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
|
||||
|
|
9
task_6/Makefile
Normal file
9
task_6/Makefile
Normal file
|
@ -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
|
143
task_6/misc-device.c
Normal file
143
task_6/misc-device.c
Normal file
|
@ -0,0 +1,143 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/* TODO
|
||||
* créer char device avec un mineur dynamique
|
||||
* appeler le device eudyptula
|
||||
*/
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#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 <rick@gnous.eu>");
|
||||
MODULE_DESCRIPTION("Création d'un device accessible en lecture et écriture.");
|
Loading…
Reference in a new issue