Files
LinuxEmbarque/tp6.md

25 KiB
Raw Blame History

Travail pratique 6

Objectif

Le but du TP est de pouvoir commencer à utiliser les GPIO en jouant avec une carte MyLab1. Ensuite l'idée est de faire notre propre module driver pour notre kernel embarqué qui s'occupe de gèrer le joystick et pouvoir l'utiliser ensuite dans un jeu.

Connectique...

Bon c'est un peu le bazard cette histoire, la carte myLab a des connecteurs un peu fébriles ou du moins pas très compatibles avec les cables dupont que l'on a et on a très peu d'informations sur le pcb pour se retrouver ce qui est très frustrant.

Pinout sama5d3

Voici tous les ports que on peut utiliser sur notre carte:

PA16, PA17, PA21, PA23, PB15, PB25, PB26, PB28, PB29, PC9, PC16, PC17, PC22, PC23, PC24, PC28, PC29, PE31

Nous allons utiliser les pins PA16 PA17 PA21 PA23 et PB27 car ils sont plutôt proches sur la carte et ils groupés et pour GND et +3.3V on va aller les chercher sur un autre endroit de la carte car c'est plus simple.

Voici un tableau des ports

Device Mylab Sama5d3
Up 2.24 PA16
Down 2.26 PA17
Left 2.25 PA21
Right 1.27 PB27
Button 2.23 PA23
+3.3V 2.1 +3.3v
GND 1.1 GND

En suivant ce tableau tout le long du tableau on devrait eviter toute confusion de câbles.

C'est un peu la galère pour tout faire tenir mais bon normalement ca passe.

librairie libgpiod

On peut voir dans le cours que les gpios sont visibles sur notre board dans /sys/class/gpio

/sys/class/gpio # ls
export       gpiochip128  gpiochip64   unexport
gpiochip0    gpiochip32   gpiochip96

On a bien nos 5 chips GPIO qui exposent nos 160 pins.

Mais depuis le kernel 4.8 on doit utiliser l'API GPIO et le code du projet libgpio se trouve sous "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/"

Si on regarde dans /dev on voit bien nos controlleurs gpio également :

/dev # ls -a | grep gpio
gpiochip0
gpiochip1
gpiochip2
gpiochip3
gpiochip4

Et on voit que ils n'ont pas le même nom. Si on se fie au tableau trouvé dans le cours on peut directement voir quel controlleur utilise quels pins parmis ceux que on utilise :

Ports Controlleur
PA16 gpiochip0
PA17 gpiochip0
PA21 gpiochip0
PA23 gpiochip0
PB27 gpiochip1

On peut récupèrer les sources de la librairie gpiod avec git clone https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git si on utilise l'HTTPS

Pour pouvoir build cette lib on va avoir besoin d'outils que je vais installer sur la vm de compilation croisée.

sudo apt install dh-autoreconf sudo apt install autoconf-archive

Je vais créer un repertoire de destination pour le build de la lib /home/moi/tp/lib/compiledLib

Ensuite on peut lancer les commandes :

git checkout v2.1.3

./autogen.sh --enable-tools=yes

Mais je me retrouve avec cette erreur

./autogen.sh --enable-tools=yes
autoreconf: export WARNINGS=
autoreconf: Entering directory '.'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force -I m4
autoreconf: configure.ac: tracing
autoreconf: running: libtoolize --copy --force
libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, 'autostuff'.
libtoolize: copying file 'autostuff/ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'm4'.
libtoolize: copying file 'm4/libtool.m4'
libtoolize: copying file 'm4/ltoptions.m4'
libtoolize: copying file 'm4/ltsugar.m4'
libtoolize: copying file 'm4/ltversion.m4'
libtoolize: copying file 'm4/lt~obsolete.m4'
autoreconf: configure.ac: not using Intltool
autoreconf: configure.ac: not using Gtkdoc
autoreconf: running: aclocal --force -I m4
autoreconf: running: /usr/bin/autoconf --force
configure.ac:88: error: possibly undefined macro: AC_CHECK_HEADERS
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.
configure.ac:213: error: possibly undefined macro: AC_LANG_PUSH
configure.ac:216: error: possibly undefined macro: AC_LANG_POP
autoreconf: error: /usr/bin/autoconf failed with exit status: 1

J'avais besoin de cette librairie pour faire fonctionner la commande

sudo apt install pkg-config

Et ensuite on peut faire la suite

./configure --prefix=/home/moi/tp/lib/compiledLib --host=arm-linux --enable-tools

Ensuite on peut comnpiler la lib

make

Après avoir compilé on peut installer la lib ce qui devrait installer les fichiers dans compiledLib

make install

Si on retourne dans notre repertoire nouvellement créé "compiledLib" et dedans on peut voir une architecture que l'on connait déja un peu de librairies.

Dans le sous repertoire /lib on peut retrouver ces fichiers

libgpiod.a libgpiod.la libgpiod.so libgpiod.so.3 libgpiod.so.3.1.1 pkgconfig

On va prendre tous les fichiers en .so sauf ligpio.so et les envoyer sur notre nfsroot sous /lib exactement comme dans le tp4 et la libc.

A partir de la on devrait pouvoir créer des programmes c qui utilisent la librairie GPIO.

Quand on veut le compiler

arm-buildroot-linux-musleabi-gcc testGpio.c -o gpio Mais ca ne marche pas car notre machine de compilation croisée ne connait pas la lib gpio

Pour le developpement on a besoin du libgpiod.so, du gpiod.h et potentiellement le libgpio.a si on fait de la compilation statique.

-l = nom de la librairie "gpiod" -I = chemin vers le .h "/home/moi/tp/lib/compiledLib/include/gpiod.h" -L = chemin vers le .so "/home/moi/tp/lib/compiledLib/lib/libgpiod.so"

arm-buildroot-linux-musleabi-gcc testGpio.c -o gpio -I /home/moi/tp/lib/compiledLib/include/ -lgpiod -L /home/moi/tp/lib/compiledLib/lib/

### Lecture du joystick via les outils libgpiod

Voici le premier bout de code qui permet de simplement de lister tous les devices GPIO sur la board embarquée :

#include <gpiod.h>
#include <stdio.h>

int main(void) {
    struct gpiod_chip *chip;
    char chip_name[32];
    int i = 0;

    printf("GPIO Chips on the system:\n");

    //Ouais c'est moi qui ai fait tout ce code t'inquiète
    // Iterate over possible chip names (gpiochip0, gpiochip1, etc.)
    for (i = 0; i < 10; i++) {
        snprintf(chip_name, sizeof(chip_name), "/dev/gpiochip%d", i);
        chip = gpiod_chip_open(chip_name);
        if (!chip)
            continue; // Skip if chip doesn't exist

        printf("- %s\n", chip_name);
        gpiod_chip_close(chip);
    }

    return 0;
}

Après avoir tftp le fichier sur la machine hôte du nfsroot et avoir changé les permissions pour permettre l'execution "chmod +x executable" on peut lancer le programme depuis notre système embarqué et on obtient ce resultat :

GPIO Chips on the system:
- /dev/gpiochip0
- /dev/gpiochip1
- /dev/gpiochip2
- /dev/gpiochip3
- /dev/gpiochip4

On pourra utiliser ces strings quand on voudra parler des différents GPIO dans la suite de nos codes.

On a deux features à implementer dans un premier temps :

  1. afficher en boucle l'étât du joystick (ex "haut", "bas", "gauche", "droite")
  2. Quand le bouton du joystick est pressé ca ferme le programme.

Voici un exemple de code que j'ai trouvé sur le git de gpiod et que j'ai très légèrement modifié pour qu'il ecrive en boucle la valeur du joystick up

// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>

/* Minimal example of reading a single line. */

#include <errno.h>
#include <gpiod.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<unistd.h>

/* Request a line as input. */
static struct gpiod_line_request *request_input_line(const char *chip_path,
						     unsigned int offset,
						     const char *consumer){
	struct gpiod_request_config *req_cfg = NULL;
	struct gpiod_line_request *request = NULL;
	struct gpiod_line_settings *settings;
	struct gpiod_line_config *line_cfg;
	struct gpiod_chip *chip;
	int ret;

	chip = gpiod_chip_open(chip_path);
	if (!chip)
		return NULL;

	settings = gpiod_line_settings_new();
	if (!settings)
		goto close_chip;

	gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);

	line_cfg = gpiod_line_config_new();
	if (!line_cfg)
		goto free_settings;

	ret = gpiod_line_config_add_line_settings(line_cfg, &offset, 1,
						  settings);
	if (ret)
		goto free_line_config;

	if (consumer) {
		req_cfg = gpiod_request_config_new();
		if (!req_cfg)
			goto free_line_config;

		gpiod_request_config_set_consumer(req_cfg, consumer);
	}

	request = gpiod_chip_request_lines(chip, req_cfg, line_cfg);

	gpiod_request_config_free(req_cfg);

free_line_config:
	gpiod_line_config_free(line_cfg);

free_settings:
	gpiod_line_settings_free(settings);

close_chip:
	gpiod_chip_close(chip);

	return request;
}
int main(void)
{
	static const char *const gpio_0 = "/dev/gpiochip0";
    static const char *const gpio_1 = "/dev/gpiochip1";
	static const unsigned int JOYSTICK_UP = 16;     //GPIO0
    static const unsigned int JOYSTICK_DOWN = 17;   //GPIO0
    static const unsigned int JOYSTICK_LEFT = 21;   //GPIO0
    static const unsigned int JOYSTICK_RIGHT = 27;  //GPIO1
    static const unsigned int JOYSTICK_BUTTON = 23; //GPIO0


    struct gpiod_line_request *up_request;
    struct gpiod_line_request *down_request;
    struct gpiod_line_request *left_request;
    struct gpiod_line_request *right_request;
    struct gpiod_line_request *button_request;

    enum gpiod_line_value value;

	up_request = request_input_line(gpio_0, JOYSTICK_UP, "get-line-value");
    down_request = request_input_line(gpio_0, JOYSTICK_DOWN, "get-line-value");
    left_request = request_input_line(gpio_0, JOYSTICK_LEFT, "get-line-value");
    right_request = request_input_line(gpio_1, JOYSTICK_RIGHT, "get-line-value");
    button_request = request_input_line(gpio_0, JOYSTICK_BUTTON, "get-line-value");

	if (!up_request) {
		printf("failed to request line: %s\n");
        return EXIT_FAILURE;
    }
    if (!down_request) {
		printf("failed to request line: %s\n");
        return EXIT_FAILURE;
    }
    if (!left_request) {
		printf("failed to request line: %s\n");
        return EXIT_FAILURE;
    }
    if (!right_request) {
		printf("failed to request line: %s\n");
        return EXIT_FAILURE;
    }
    if (!button_request) {
		printf("failed to request line: %s\n");
        return EXIT_FAILURE;
    }

    printf("Listening to joystick press\n");

	while(true){
        sleep(0.1);
	    value = gpiod_line_request_get_value(up_request, JOYSTICK_UP);
        if (value == GPIOD_LINE_VALUE_INACTIVE)
            printf("UP\n");
        value = gpiod_line_request_get_value(down_request, JOYSTICK_DOWN);
        if (value == GPIOD_LINE_VALUE_INACTIVE)
            printf("DOWN\n");
        value = gpiod_line_request_get_value(left_request, JOYSTICK_LEFT);
        if (value == GPIOD_LINE_VALUE_INACTIVE)
            printf("LEFT\n");
        value = gpiod_line_request_get_value(right_request, JOYSTICK_RIGHT);
        if (value == GPIOD_LINE_VALUE_INACTIVE)
            printf("RIGHT\n");
        value = gpiod_line_request_get_value(button_request, JOYSTICK_BUTTON);
        if (value == GPIOD_LINE_VALUE_INACTIVE)
            break;
	}
	/* not strictly required here, but if the app wasn't exiting... */
	gpiod_line_request_release(up_request);
    gpiod_line_request_release(down_request);
    gpiod_line_request_release(left_request);
    gpiod_line_request_release(right_request);
    gpiod_line_request_release(button_request);
	return EXIT_SUCCESS;
}
}
~ # echo $?
0

[Q2] Expliquez limplémentation de votre programme et indiquez où il se trouve sur votre git.

Le code ci dessus devrait faire en permanence du pulling et dès que le joystick est bougé on l'affiche.

J'ai ajouté le fait que quand on appuie sur le bouton du joystick ca arrête le programme et que le sleep(0.1) permette à la boucle de ne s'executer que toutes les 0.1s pour ne pas surcharger le processeur.

Le code a été adapté d'un code existant dispo sur le git de libgpiod

Lecture du joystick via lAPI C libgpiod

[Q1]

En fait j'avais pas compris que il fallait utiliser les outils dabord et ensuite l'API. J'ai commencé direct avec l'API.

Mais on peut facilement imaginer un petit script bash qui utiliserait juste les outils avec des commande simples genre gpioget et gpioset

Pour le makefile voici un exemple très simple:

CC = arm-buildroot-linux-musleabi-gcc
SRC = testJoystick.c
TARGET = joystick
INCLUDE_DIR = /home/moi/tp/lib/compiledLib/include/
LIB_DIR = /home/moi/tp/lib/compiledLib/lib/
LIBS = -lgpiod

all: $(TARGET)

$(TARGET): $(SRC)
	$(CC) $(SRC) -o $(TARGET) -I $(INCLUDE_DIR) $(LIBS) -L $(LIB_DIR)

clean:
	rm -f $(TARGET)

Développement dun module noyau

On crée un repertoire sur la VM de developpement sous /tp/module

On va prendre le squelette du module sur le git

On va y aller étape par étape :

1 initialisation du driver

Les étapes pour bien initialiser notre driver sont :

  1. Enregistrer notre module en obtenant un numéro majeur
  2. Créer la classe
  3. Créer le module dans /dev

Les trois étapes ci dessus ont pu ête implémentées avec l'aide des slides du cours.

  1. Demander les différents GPIOS
  2. Enregistrer les différentes interruptions pour chaque GPIO
int major;
struct class *j_class;
struct device *j_device;
//mapping des fonctions classiques d'un char device
static const struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = dev_open
};
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;
}

par la même occasion avec le code ci dessus on peut déja préparer la sortie du module :

static void __exit mylab1_joystick_dev_exit(void) {
	// TODO
	// 1) Destroy the device
    device_destroy(j_device,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");
}

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:

# 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 :

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 :

gpio_free(JOYSTICK_UP);

Ensuite pour demander les interruptions

#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

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 :

int res = 0;
res = gpio_get_value(JOYSTICK_UP);

Et voici le code de l'interruption

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.

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.

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();

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);
    // 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;
    }
    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
mymodule: loading out-of-tree module taints kernel.
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.

Utilisation du module noyau depuis lespace utilisateur

La le but ca va être d'utiliser notre module avec un programme en C.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

#define PATH_MODULE "/dev/mylab1_joystick"

int main(){
    int fd;
    int buffer[5] = {1,1,1,1,1};
    int read_err;
    int offset = 0;
    fd = open(PATH_MODULE,O_RDONLY,S_IRUSR);
    if(fd < 0){
        printf("Could not retrieve module at PATH_MODULE\n");
        return fd;
    }
    while(1){

        read_err = read(fd,buffer,5 * sizeof(int));
        if(read_err < 0)
            printf("Could not read module\n");

        if(!buffer[0])
            printf("UP\n");
        if(!buffer[1])
            printf("DOWN\n");
        if(!buffer[2])
            printf("LEFT\n");
        if(!buffer[3])
            printf("RIGHT\n");
        if(!buffer[4])
            break;

        usleep(100); //1000 microseconds delay to not stress the cpu too much 
        
        //printf("Up %d,Down %d,Left %d,Right %d,Button %d\n",buffer[0],buffer[1],buffer[2],buffer[3],buffer[4]);
    }
    close(fd);
    printf("Exiting the app !\n");
}

arm-buildroot-linux-musleabi-gcc moduletester.c -o moduletester

chmod +x moduletester

Ensuite quand on essaie de le lancer on a l'erreur : could not retrieve module par ce que je viens de lancer mon kernel et que le module n'est pas directement chargé par défaut pour le moment.

Donc insmod mymodule.ko

./mymodule.ko

Va savoir pourquoi quand le gpio est pas pressé c'est 1 et pas 0, bon ca demande juste une simple inversion.

Pour le makefile on peut utiliser ca :

# Name of the target executable
TARGET = moduletester

# Compiler
CC = arm-buildroot-linux-musleabi-gcc

# Source file
SRC = moduletester.c

# Default target
all: $(TARGET)

$(TARGET): $(SRC)
	$(CC) $(SRC) -o $(TARGET)

# Clean up generated files
clean:
	rm -f $(TARGET)

[Q4] Expliquez le fonctionnement de votre programme, notamment comment vous obtenez les états du joystick. Indiquez où se trouvent les sources du programme ainsi que le Makefile correspondant sur votre git.

Le code du testeur n'est pas sur le git car il est assez petit pour tenir sur ce markdown. J'ai mis un exemple de makefile juste au dessus. Pour recupèrer les valeurs du driver on le lit comme un fichier et comme ca a été prévu lors de la conception du driver, les données sont envoyées sous forme de tableau avec cinq positions ou dans chaque position on a l'étât du GPIO correspondant.

Compilation croisée de la librairie ncurses