I think my module is ready
This commit is contained in:
207
module.c
Normal file
207
module.c
Normal 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
131
tp6.md
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user