Barebones working kernel driver

This commit is contained in:
2024-12-23 10:55:11 +01:00
parent e2ff142cb0
commit 93b92c2d86
3 changed files with 416 additions and 0 deletions

93
skeletton.c Normal file
View File

@@ -0,0 +1,93 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
// 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 <maxime.rohmer@hesge.ch>");
MODULE_DESCRIPTION("Module to drive the joystick on the myLab1 card");
MODULE_VERSION("0.1");

174
skeletton_gpio.c Normal file
View File

@@ -0,0 +1,174 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
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 <maxime.rohmer@hesge.ch>");
MODULE_DESCRIPTION("Module to drive the joystick on the myLab1 card");
MODULE_VERSION("0.1");

149
tp6.md
View File

@@ -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. 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