I think my module is ready

This commit is contained in:
2024-12-23 11:39:22 +01:00
parent 93b92c2d86
commit 6ddb10c4a5
2 changed files with 336 additions and 2 deletions

207
module.c Normal file
View File

@@ -0,0 +1,207 @@
#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;
static int UP,DOWN,LEFT,RIGHT,BUTTON = 0;
int major;
struct class *j_class;
struct device *j_device;
// Operation prototypes
static int dev_open(struct inode *, struct file *);
static ssize_t dev_read(struct file *filep, char __user *buffer, size_t len, loff_t *offset){
int joystick_status[5] = {UP,DOWN,LEFT,RIGHT,BUTTON};
int data_len = 5*sizeof(int);
// Ensure we only send data once
if (*offset > 0)
return 0; // End of file
// Copy data to user-space buffer
if (copy_to_user(buffer, joystick_status, data_len)) {
pr_err("Failed to send data to user\n");
return -EFAULT;
}
// Update the file offset
*offset += data_len;
return 0;
}
// The driver's file operations
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = dev_open,
.read = dev_read
};
/*
* 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
switch(gpio){
case JOYSTICK_UP:
UP = value;
break;
case JOYSTICK_DOWN:
DOWN = value;
break;
case JOYSTICK_LEFT:
LEFT = value;
break;
case JOYSTICK_RIGHT:
RIGHT = value;
break;
case JOYSTICK_BUTTON:
BUTTON = value;
break;
default:
//Wtf?
break;
}
return (irqreturn_t) IRQ_HANDLED;
}
/**
* 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_BUTTON);
// 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");

131
tp6.md
View File

@@ -572,9 +572,11 @@ if(irqres < 0)
pour les free
```c
free_irq(irqNbr_UP, NULL);
free_irq(irqNbr_UP, (void*)JOYSTICK_UP);
```
ATTENTION, dans le free_irq le second paramètre doit être identique au request_irq. Si il est null il doit l'être également ici et sinon il doit contenir la même valeur sinon ca ne permet pas de free les IRQ correctement au déchargement du module. (Non j'ai pas mis 30min a comprendre pourquoi ca voulait pas marcher)
Pour lire des valeurs :
```c
int res = 0;
@@ -596,7 +598,132 @@ static irqreturn_t mylab_irq_handler(int irq, void *dev_id) {
}
```
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
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.
Dans le tp il est demandé à ce que l'interruption garde en memoire l'état des joysticks pour que quand le user vienne effectuer un read on lui renvoie l'étât (UP,DOWN,LEFT,RIGHT,BUTTON, ou NULL)
L'interruption n'aura alors comme travail que de mettre à jour les étâts.
```c
static int UP,DOWN,LEFT,RIGHT,BUTTON = 0;
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
switch(gpio){
case JOYSTICK_UP:
UP = value;
break;
case JOYSTICK_DOWN:
DOWN = value;
break;
case JOYSTICK_LEFT:
LEFT = value;
break;
case JOYSTICK_RIGHT:
RIGHT = value;
break;
case JOYSTICK_BUTTON:
BUTTON = value;
break;
default:
//Wtf?
break;
}
return (irqreturn_t) IRQ_HANDLED;
}
```
Ok on pourrait imaginer un code qui met juste une valeur codée genre 1 = UP et 2 = DOWN comme ca pas besoin de cinq variables, mais j'aime l'idée que notre joystick pourrait avoir plusieurs positions en même temps pour aller en diagonale par exemple. Je ne sais pas si c'est physiquement possible sur notre mylab1 mais ca m'est égal.
Maintenant le moment tant attendu, on implémente la methode read();
```c
static ssize_t dev_read(struct file *filep, char __user *buffer, size_t len, loff_t *offset){
int joystick_status[5] = {UP,DOWN,LEFT,RIGHT,BUTTON};
int data_len = 5*sizeof(int);
// Ensure we only send data once
if (*offset > 0)
return 0; // End of file
// Copy data to user-space buffer
if (copy_to_user(buffer, joystick_status, data_len)) {
pr_err("Failed to send data to user\n");
return -EFAULT;
}
// Update the file offset
*offset += data_len;
return 0;
}
```
Dans le code ci dessus on envoie simplement un tableau avec toutes les valeurs des GPIO pour plus de simplicité du côté user pour les recuperer.
Verifications du bon fonctionnement du module terminé:
```
~ # insmod mymodule.ko
mylab1_joystick: driver initialized
```
/dev
```
/dev # ls /dev | grep mylab
mylab1_joystick
```
/proc/devices
```
/proc # cat /proc/devices | grep mylab
245 mylab1_joystick
```
/sys/class
```
/sys/class # ls /sys/class | grep mylab
mylab1_joystick
```
/proc/interrupts
```
/proc # cat /proc/interrupts | grep my
58: 17 GPIO 16 Edge myjoystick_irq
59: 8 GPIO 17 Edge myjoystick_irq
60: 10 GPIO 21 Edge myjoystick_irq
61: 15 GPIO 27 Edge myjoystick_irq
62: 6 GPIO 23 Edge myjoystick_irq
```
Et quand on le décharge :
```
~ # rmmod mymodule
mylab1_joystick: driver destroyed
```
Aucun message d'erreur rien.
Et si on retourne dans /proc/interrupts
```
~ # cat /proc/interrupts | grep my
~ #
```
On voit bien que elles ont toutes été free.
### [Q3] Expliquez le fonctionnement de votre module et les parties sensibles de son implémentation.
Sur le git les trois niveaux de modules se trouvent sous
- `skeletton.c` qui est un module inutile qui ne sert qu'à s'enregistrer et se desenregistrer
- `skeletton_gpio.c` qui est une version du module qui s'occupe de mettre en place les gpios et qui les affiche en message d'erreur ce qui est pratique pour debug
- `module.c` qui est la version finale du module qui devrait être utilisée par un user et qui alloue et désaloue tout correctement en principe
Le fonctionnement est décrit plus haut.