143 lines
3.3 KiB
C
143 lines
3.3 KiB
C
// 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.");
|