From 93b92c2d869e3d0d21711ac782e757948493f573 Mon Sep 17 00:00:00 2001 From: maxluli Date: Mon, 23 Dec 2024 10:55:11 +0100 Subject: [PATCH] Barebones working kernel driver --- skeletton.c | 93 +++++++++++++++++++++++++ skeletton_gpio.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++ tp6.md | 149 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 416 insertions(+) create mode 100644 skeletton.c create mode 100644 skeletton_gpio.c diff --git a/skeletton.c b/skeletton.c new file mode 100644 index 0000000..bccdbbe --- /dev/null +++ b/skeletton.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include + +// Operation prototypes +static int dev_open(struct inode *, struct file *); + +int major; +struct class *j_class; +struct device *j_device; + +// The driver's file operations +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = dev_open +}; + +/** + * Driver initialization code. + */ +static int __init mylab1_joystick_dev_init(void) { + // TODO + // 1) Register the device by dynamically obtaining a major number + major = register_chrdev(0, "mylab1_joystick", &fops); + if(major < 0){ + pr_info("mylab1_joystick: could not get a major number, initialisation failed\n"); + return -1; + } + // 2) Create the class + j_class = class_create("mylab1_joystick"); + if (IS_ERR(j_class)){ + pr_info("mylab1_joystick: could not create class, initialisation failed\n"); + return -1; + } + // 3) Create the device in /dev + j_device = device_create(j_class, NULL, MKDEV(major, 0), NULL, "mylab1_joystick"); + if (IS_ERR(j_device)){ + pr_info("mylab1_joystick: could not create device, initialisation failed\n"); + return -1; + } + // 6) Request the necessary GPIOs + // 7) Register an IRQ handler per GPIO + pr_info("mylab1_joystick: driver initialized\n"); + return 0; +} + +/** + * This function is called when the module is unloaded. + */ +static void __exit mylab1_joystick_dev_exit(void) { + // TODO + // 1) Destroy the device + device_destroy(j_class,MKDEV(major, 0)); + // 2) Destroy the class + class_destroy(j_class); + // 4) Unregister the device + unregister_chrdev(major,"mylab1_joystick"); + // 5) Free the IRQs + // 6) Free the GPIOs + pr_info("mylab1_joystick: driver destroyed\n"); +} + +/** + * Open operation + */ +static int dev_open(struct inode *inod, struct file *f) { + pr_info("mylab1_joystick: device opened\n"); + return 0; +} + +/** + * Joystick left IRQ handler + */ +/* +static irqreturn_t mylab_left_irq_handler(int irq, void *dev_id) { + // TODO + // - Operations to be done when the left position of the joystick is triggered + // - At minimum, the joystick state must be updated + return (irqreturn_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly +} +*/ + +module_init(mylab1_joystick_dev_init); +module_exit(mylab1_joystick_dev_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jaime Coder "); +MODULE_DESCRIPTION("Module to drive the joystick on the myLab1 card"); +MODULE_VERSION("0.1"); \ No newline at end of file diff --git a/skeletton_gpio.c b/skeletton_gpio.c new file mode 100644 index 0000000..4ea18b2 --- /dev/null +++ b/skeletton_gpio.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include +#include + +const int JOYSTICK_UP = 16; //GPIO0 +const int JOYSTICK_DOWN = 17; //GPIO0 +const int JOYSTICK_LEFT = 21; //GPIO0 +const int JOYSTICK_RIGHT = 27 + 32; //GPIO1 offset of 32 on GPIO 1 +const int JOYSTICK_BUTTON = 23; //GPIO0 + +#define IRQ_NAME "myjoystick_irq" +int irqNbr_UP,irqNbr_DOWN,irqNbr_LEFT,irqNbr_RIGHT,irqNbr_BUTTON = 0; + +// Operation prototypes +static int dev_open(struct inode *, struct file *); + +int major; +struct class *j_class; +struct device *j_device; + +// The driver's file operations +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = dev_open +}; + +/* + * Joystick IRQ handler + */ +static irqreturn_t mylab_irq_handler(int irq, void *dev_id) { + int gpio = (int)dev_id; // GPIO number passed as dev_id + int value = gpio_get_value(gpio); // Get the current GPIO value + if (value == 1) { + pr_info("GPIO %d: Rising edge detected\n", gpio); + } else { + pr_info("GPIO %d: Falling edge detected\n", gpio); + } + return (irqreturn_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly +} + +/** + * Driver initialization code. + */ +static int __init mylab1_joystick_dev_init(void) { + // TODO + // 1) Register the device by dynamically obtaining a major number + major = register_chrdev(0, "mylab1_joystick", &fops); + if(major < 0){ + pr_info("mylab1_joystick: could not get a major number, initialisation failed\n"); + return -1; + } + // 2) Create the class + j_class = class_create("mylab1_joystick"); + if (IS_ERR(j_class)){ + pr_info("mylab1_joystick: could not create class, initialisation failed\n"); + return -1; + } + // 3) Create the device in /dev + j_device = device_create(j_class, NULL, MKDEV(major, 0), NULL, "mylab1_joystick"); + if (IS_ERR(j_device)){ + pr_info("mylab1_joystick: could not create device, initialisation failed\n"); + return -1; + } + // 6) Request the necessary GPIOs + int err = 0; + err = gpio_request_one(JOYSTICK_UP,GPIOF_DIR_IN,"UP"); + if(err != 0) + pr_info("mylab1_joystick: Could not request GPIO %d\n",JOYSTICK_UP); + err = gpio_request_one(JOYSTICK_DOWN,GPIOF_DIR_IN,"DOWN"); + if(err != 0) + pr_info("mylab1_joystick: Could not request GPIO %d\n",JOYSTICK_DOWN); + err = gpio_request_one(JOYSTICK_LEFT,GPIOF_DIR_IN,"UP"); + if(err != 0) + pr_info("mylab1_joystick: Could not request GPIO %d\n",JOYSTICK_LEFT); + err = gpio_request_one(JOYSTICK_RIGHT,GPIOF_DIR_IN,"UP"); + if(err != 0) + pr_info("mylab1_joystick: Could not request GPIO %d\n",JOYSTICK_RIGHT); + err = gpio_request_one(JOYSTICK_BUTTON,GPIOF_DIR_IN,"UP"); + if(err != 0) + pr_info("mylab1_joystick: Could not request GPIO %d\n",JOYSTICK_BUTTON); + // 7) Register an IRQ handler per GPIO + int irqres = 0; + + // JOYSTICK UP + irqNbr_UP = gpio_to_irq(JOYSTICK_UP); + if(irqNbr_UP < 0) + pr_info("mylab1_joystick: Could not request IRQ number on GPIO : %d\n",JOYSTICK_UP); + + irqres = request_irq(irqNbr_UP, mylab_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQ_NAME, (void*)JOYSTICK_UP); + if(irqres < 0) + pr_info("mylab1_joystick: Could not request IRQ on GPIO : %d\n",JOYSTICK_UP); + //JOYSTICK DOWN + irqNbr_DOWN = gpio_to_irq(JOYSTICK_DOWN); + if(irqNbr_DOWN < 0) + pr_info("mylab1_joystick: Could not request IRQ number on GPIO : %d\n",JOYSTICK_DOWN); + + irqres = request_irq(irqNbr_DOWN, mylab_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQ_NAME, (void*)JOYSTICK_DOWN); + if(irqres < 0) + pr_info("mylab1_joystick: Could not request IRQ on GPIO : %d\n",JOYSTICK_DOWN); + // JOYSTICK LEFT + irqNbr_LEFT = gpio_to_irq(JOYSTICK_LEFT); + if(irqNbr_LEFT < 0) + pr_info("mylab1_joystick: Could not request IRQ number on GPIO : %d\n",JOYSTICK_LEFT); + + irqres = request_irq(irqNbr_LEFT, mylab_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQ_NAME, (void*)JOYSTICK_LEFT); + if(irqres < 0) + pr_info("mylab1_joystick: Could not request IRQ on GPIO : %d\n",JOYSTICK_LEFT); + // JOYSTICK RIGHT + irqNbr_RIGHT = gpio_to_irq(JOYSTICK_RIGHT); + if(irqNbr_RIGHT < 0) + pr_info("mylab1_joystick: Could not request IRQ number on GPIO : %d\n",JOYSTICK_RIGHT); + + irqres = request_irq(irqNbr_RIGHT, mylab_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQ_NAME, (void*)JOYSTICK_RIGHT); + if(irqres < 0) + pr_info("mylab1_joystick: Could not request IRQ on GPIO : %d\n",JOYSTICK_RIGHT); + // JOYSTICK BUTTON + irqNbr_BUTTON = gpio_to_irq(JOYSTICK_BUTTON); + if(irqNbr_BUTTON < 0) + pr_info("mylab1_joystick: Could not request IRQ number on GPIO : %d\n",JOYSTICK_BUTTON); + + irqres = request_irq(irqNbr_BUTTON, mylab_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQ_NAME, (void*)JOYSTICK_BUTTON); + if(irqres < 0) + pr_info("mylab1_joystick: Could not request IRQ on GPIO : %d\n",JOYSTICK_BUTTON); + + //init finished + pr_info("mylab1_joystick: driver initialized\n"); + return 0; +} + +/** + * This function is called when the module is unloaded. + */ +static void __exit mylab1_joystick_dev_exit(void) { + // TODO + // 1) Destroy the device + device_destroy(j_class,MKDEV(major, 0)); + // 2) Destroy the class + class_destroy(j_class); + // 4) Unregister the device + unregister_chrdev(major,"mylab1_joystick"); + // 5) Free the IRQs + free_irq(irqNbr_UP, (void*)JOYSTICK_UP); + free_irq(irqNbr_DOWN, (void*)JOYSTICK_DOWN); + free_irq(irqNbr_LEFT, (void*)JOYSTICK_LEFT); + free_irq(irqNbr_RIGHT, (void*)JOYSTICK_RIGHT); + free_irq(irqNbr_BUTTON, (void*)JOYSTICK_BUTTUN); + // 6) Free the GPIOs + gpio_free(JOYSTICK_UP); + gpio_free(JOYSTICK_DOWN); + gpio_free(JOYSTICK_LEFT); + gpio_free(JOYSTICK_RIGHT); + gpio_free(JOYSTICK_BUTTON); + pr_info("mylab1_joystick: driver destroyed\n"); +} + +/** + * Open operation + */ +static int dev_open(struct inode *inod, struct file *f) { + pr_info("mylab1_joystick: device opened\n"); + return 0; +} + +module_init(mylab1_joystick_dev_init); +module_exit(mylab1_joystick_dev_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("defenestration en cours "); +MODULE_DESCRIPTION("Module to drive the joystick on the myLab1 card"); +MODULE_VERSION("0.1"); \ No newline at end of file diff --git a/tp6.md b/tp6.md index 53f657c..f9796cf 100644 --- a/tp6.md +++ b/tp6.md @@ -455,3 +455,152 @@ static void __exit mylab1_joystick_dev_exit(void) { Normalement avec le squelette modifié de la sorte on devrait avoir un driver minimal qui ne sert à rien mais qui se créée correctement dans le kernel linux. +Voici a quoi ressemble un makefile classique pour compiler notre module: + +```makefile +# kernel headers +KDIR := /home/moi/tp/kernel/linux-6.5 +# module name mymodule.c +obj-m := mymodule.o + +modules: + $(MAKE) -C $(KDIR) M=$(PWD) $@ +clean: + $(MAKE) -C $(KDIR) M=$(PWD) $@ +``` + +Le KDIR to kernel correspond à l'endroit ou on a les sources du kernel. + +`make mrproper` + +`make sama5_defconfig` + +`make -j 6` //obligé de mettre 6 car dans mon cas car sans specifier le nombre de cpu sur ma VM, le -j crééait des instances en boucle et arrivait à saturer la memoire + +Je peux lancer la compilation, la config est pas parfaite mais la le but est pas de faire un kernel pour notre board mais juste d'avoir un kernel compilé pour ensuite pouvoir compiler notre module. + +Le chemin complet : "/home/moi/tp/kernel/linux-6.5" + +Quand le kernel a fini d'être compilé on peut faire un `make` pour compiler notre (pour le moment) inutile module et ca fonctionne ! Voici le contenu du repertoire après compilation. Le code du module minimal est trouvable sous `skeletton.c` dans le git + +`Makefile Module.symvers modules.order mymodule.c mymodule.ko mymodule.mod mymodule.mod.c mymodule.mod.o mymodule.o` + +On va avoir besoin du .ko pour tester notre module. + +`sudo insmod mymodule.ko` + +Le resultat de la commande est : + +``` +mymodule: loading out-of-tree module taints kernel. +mylab1_joystick: driver initialized +``` + +Ce qui est plutôt bon signe. + +On peut voir la liste des modules avec lsmod + +``` +~ # lsmod +mymodule 12288 0 - Live 0xbf052000 (O) +ehci_atmel 12288 0 - Live 0xbf04c000 +ehci_hcd 45056 1 ehci_atmel, Live 0xbf03e000 +usb_storage 49152 0 - Live 0xbf02f000 +usbcore 180224 3 ehci_atmel,ehci_hcd,usb_storage, Live 0xbf000000 +``` + +Pour voir si des messages d'erreurs ont été levés on peut aller regarder dans les logs avec `dmesg | tail` + +Et les deux seuls messages que l'on peut voir qui concernent notre module sont : + +``` +mymodule: loading out-of-tree module taints kernel. +mylab1_joystick: driver initialized +``` + +On peut même le voir dans /sys/module `ls /sys/module | grep mymodule` + +pour retirer le module on peut utiliser la commande rmmod mymodule `rmmod mymodule` + +``` +~ # rmmod mymodule +mylab1_joystick: driver destroyed +``` + +Maintenant que on a driver qui s'enregistre correctemement et se désenregistre correctement on peut passer à la suite : Qu'il fasse un truc du coup + +Pour la partie gestion des GPIO voici le code qui permet de request les différents GPIO : + +```c +const int JOYSTICK_UP = 16; //GPIO0 +const int JOYSTICK_DOWN = 17; //GPIO0 +const int JOYSTICK_LEFT = 21; //GPIO0 +const int JOYSTICK_RIGHT = 27 + 32; //GPIO1 offset of 32 on GPIO 1 +const int JOYSTICK_BUTTON = 23; //GPIO0 + +int err = 0; +err = gpio_request_one(JOYSTICK_UP,GPIOF_DIR_IN,"UP"); +if(err != 0) + pr_info("mylab1_joystick: Could not request GPIO %d\n",JOYSTICK_UP); +``` + +Et pour rendre les GPIO : + +```c +gpio_free(JOYSTICK_UP); +``` + +Ensuite pour demander les interruptions + +```c +#define IRQ_NAME "myjoystick_irq" +int irqNbr_UP = 0; +int irqNbr_DOWN = 0; +int irqNbr_LEFT = 0; +int irqNbr_RIGHT = 0; +int irqNbr_BUTTON = 0; + +int irqres = 0; + +irqNbr_UP = gpio_to_irq(JOYSTICK_UP); +if(irqNbr_UP < 0) + pr_info("mylab1_joystick: Could not request IRQ number on GPIO : %d\n",JOYSTICK_UP); +irqres = request_irq(irqNbr_UP, mylab_gpio_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQ_NAME, (void*)JOYSTICK_UP); +if(irqres < 0) + pr_info("mylab1_joystick: Could not request IRQ on GPIO : %d\n",JOYSTICK_UP); +``` + +pour les free +```c +free_irq(irqNbr_UP, NULL); +``` + +Pour lire des valeurs : +```c +int res = 0; +res = gpio_get_value(JOYSTICK_UP); +``` + +Et voici le code de l'interruption + +```c +static irqreturn_t mylab_irq_handler(int irq, void *dev_id) { + int gpio = (int)dev_id; // GPIO number passed as dev_id + int value = gpio_get_value(gpio); // Get the current GPIO value + if (value == 1) { + pr_info("GPIO %d: Rising edge detected\n", gpio); + } else { + pr_info("GPIO %d: Falling edge detected\n", gpio); + } + return (irqreturn_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly +} +``` + +Avec le code que on peut trouver sous `skeletton_gpio.c` dans le git, on a un code qui va detecter quand un bouton du joystick a été pressé ou lâché et l'ecrit dans les logs + + + + + + +