Changes between Initial Version and Version 1 of SujetTP2-2016


Ignore:
Timestamp:
Jan 29, 2016, 4:48:28 AM (9 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • SujetTP2-2016

    v1 v1  
     1= TP3 : Premier pilote =
     2
     3== Objectif ==
     4
     5L'objectif de la séance est de commander les LEDS et les boutons poussoir en passant par un pilote.
     6Lors de la dernière séance pour commander les LEDS et accéder aux boutons, vous aviez dû mapper dans l'espace virtuel de l'utilisateur la zone de mémoire permettant l'accès aux GPIO. Il vous fallait avoir les droits du root pour cela. Désormais, les LED et boutons seront accessibles en mode utilisateur.
     7
     8Nous allons donc créer un pilote pour le périphérique LED+!BoutonsPoussoir.
     9Ce pilote sera accessible dans par le pseudo-fichier `/dev/ledbtn` (noter que vous serez peut-être obligé de changer un peu le nom pour éviter les conflits avec vos camarades).
     10
     11Par exemple vous pourrez écrire cela dans un fichier `test.c` ci-dessous. Ce que fait ce programme importe peu. Ce qui est important c'est que ce programme s'exécute entièrement en mode utilisateur.
     12Le comportement proposé ici du pilote est le suivant.
     13* Pour les LEDS, on envoie un tableau de caractère de 4 cases. La case 0 définit l'état de la LED 0 (`'0'` LED éteinte sinon LED allumée).
     14* Pour les boutons, on propose un tableau de caractères de 2 cases. Le pilote lit l'état du bouton 0 et le met dans la case 0, et l'état du bouton 1 et le met dans la case 1. Quand le bouton est relâché, le pilote met le caractère `'0'`, quand le bouton est enfoncé, le pilote met la valeur `'1'`.
     15
     16C'est une proposition, vous pouvez faire comme bon vous semble.
     17{{{
     18#include <stdio.h>
     19
     20char led[2];
     21char btn[1];
     22 
     23int main()
     24{
     25   int fd = open("/dev/ledbtn", O_RDWR);
     26   if (fd < 0) {
     27      fprintf(stderr, "Erreur d'ouverture du pilote LED et Boutons\n");
     28      exit(1);
     29   }
     30   do {
     31      for ( i=0; i<4; i++) {
     32         memcpy( LED, "00", 2);
     33         LED[i] = '1';
     34         write( fd, LED, 4);
     35         sleep( 1);
     36      }
     37      read( fd, btn, 2);
     38   } while (btn[0] == '0');
     39   return 0;
     40}
     41}}}
     42
     43== Étape 1 : création et test d'un module noyau ==
     44
     45La première étape consiste à créer un module noyau, l'insérer puis l'effacer du noyau.
     46
     47Le module minimal se compose d'une fonction d'initialisation et d'une fonction de cleanup, dans le fichier `module.c`suivant:
     48
     49{{{
     50#include <linux/module.h>
     51#include <linux/init.h>
     52
     53MODULE_LICENSE("GPL");
     54MODULE_AUTHOR("Charlie, 2015");
     55MODULE_DESCRIPTION("Module, aussitot insere, aussitot efface");
     56
     57static int __init mon_module_init(void)
     58{
     59   printk(KERN_DEBUG "Hello World !\n");
     60   return 0;
     61}
     62
     63static void __exit mon_module_cleanup(void)
     64{
     65   printk(KERN_DEBUG "Goodbye World!\n");
     66}
     67
     68module_init(mon_module_init);
     69module_exit(mon_module_cleanup);
     70}}}
     71
     72Ce fichier est cross compilé et copié sur la Raspberry Pi cible avec le fichier `Makefile` suivant:
     73{{{
     74CARD_NUMB       = 23
     75ROUTER          = 132.227.102.36
     76LOGIN           = franck
     77LAB             = lab2
     78
     79MODULE          = module
     80
     81CROSSDIR        = /users/enseig/franck/peri
     82KERNELDIR       = $(CROSSDIR)/linux-rpi-3.18.y
     83CROSS_COMPILE   = $(CROSSDIR)/arm-bcm2708hardfp-linux-gnueabi/bin/bcm2708hardfp-
     84       
     85obj-m           += $(MODULE).o
     86default:;       make -C $(KERNELDIR) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) modules
     87clean:;         make -C $(KERNELDIR) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) clean
     88upload:;        scp -P50$(CARD_NUMB) $(MODULE).ko pi@$(ROUTER):$(LOGIN)/$(LAB)
     89}}}
     90
     91
     92 ||'''''Note:'''[[BR]]Ce Makefile a besoin des sources compilées du noyau. Si vous voulez le faire chez vous, il faut que vous preniez les sources de votre distribution. Vous pouvez suivre le tutoriel très clair [http://www.chicoree.fr/w/Compilation_crois%C3%A9e_d%27un_module_Linux_pour_Rasberry_Pi Compilation croisée d'un module linux pour Raspberry Pi]''.||
     93
     94Sur votre compte enseignement, vous devez:
     95* Créer ces fichiers et bien sûr, les comprendre.
     96* '''changer la valeur des variables `CARD_NUMB`, `LOGIN`et `LAB` afin de les adapter respectivement au numéro de la carte choisie, au nom du répertoire créé par vous sur la raspberry et au nom du sous-répertoire créé par vous pour ce TP'''. Les répertoires et sous-répertoires doivent exister et vous devez donc commencer par vous logger sur votre carte Raspberry PI avec `ssh` pour les créer.
     97* Compiler le module avec la commande `make`.
     98* Copier sur la raspberry avec scp avec la commande `make upload`.
     99
     100Sur la carte Raspberry PI, vous devez:
     101* Dans le répertoire `$(LOGIN)/$(LAB)'(par exemple `franck/lab2`) où vous avez copié votre module
     102
     103{{{
     104$ sudo insmod ./module.ko
     105$ lsmod
     106$ dmesg
     107$ sudo rmmod module
     108$ lsmod
     109$ dmesg
     110}}}
     111* Les commandes `lsmod`et `dmesg` permettent de voir que les actions du module.
     112
     113
     114== Étape 2 : ajout des paramètres au module ==
     115
     116Votre driver devra être paramétré pour lui indiquer le numéro de ports utilisés pour les LEDS et les boutons.
     117Dans un premier temps vous allez vous contenter d'indiquer le nombre de LED et de boutons pour le module de test, mais il faudra être plus précis pour le vrai driver.
     118
     119Vous devez ajouter dans module.c (faite d'équivalent pour les boutons):
     120{{{
     121static int led;
     122module_param(LED, int, 0);
     123MODULE_PARM_DESC(LED, "Nombre de led");
     124
     125static int __init mon_module_init(void)
     126{
     127    printk(KERN_DEBUG "Hello World !\n");
     128    printk(KERN_DEBUG "LED=%d !\n", LED);
     129    return 0;
     130}
     131}}}
     132Le paramètre est défini au moment de l'insertion.
     133{{{
     134$ insmod ./module.o led=4
     135}}}
     136
     137== Étape 3 : création d'un driver qui ne fait rien mais dans le noyau ==
     138
     139Votre driver va être intégré dans un module. Vous allez donc créer un module nommé `ledbtn` paramétré avec les numéros de ports pour les LEDS et les boutons. Vous utiliserez un nouveau répertoire. Vous modifierez le Makefile en conséquence.
     140
     141* Vous ajoutez dans le fichier `.c` du module:
     142 
     143{{{
     144struct file_operations fops_ledbtn =
     145{
     146    .open       = open_ledbtn,
     147    .read       = read_ledbtn,
     148    .write      = write_ledbtn,
     149    .release    = release_ledbtn
     150};
     151static int
     152open_ledbtn(struct inode *inode, struct file *file) {
     153    printk(KERN_DEBUG "open()\n");
     154    return 0;
     155}
     156
     157static ssize_t
     158read_ledbtn(struct file *file, char *buf, size_t count, loff_t *ppos) {
     159    printk(KERN_DEBUG "read()\n");
     160    return count;
     161}
     162
     163static ssize_t
     164write_ledbtn(struct file *file, const char *buf, size_t count, loff_t *ppos) {
     165    printk(KERN_DEBUG "write()\n");
     166    return count;
     167}
     168
     169static int
     170release_ledbtn(struct inode *inode, struct file *file) {
     171    printk(KERN_DEBUG "close()\n");
     172    return 0;
     173}
     174}}}
     175* Vous allez enregistrer ce driver dans ce module en ajoutant la fonction d'enregistrement dans la fonction init du module. Vous devez aussi prendre en compte les paramètres. C'est à vous de décider comment.
     176
     177{{{
     178int major = register_chrdev(0, "ledbtn" &fops_ledbtn); // 0 est le numéro majeur qu'on laisse choisir par linux
     179}}}
     180* Vous allez décharger le driver dans ce module en ajoutant dans la fonction exit du module:
     181
     182{{{
     183unregister_chrdev(major, "ledbtn");
     184}}}
     185
     186* Vous devez compiler, déplacer le module (upload) et le charger (insmod).
     187* Vous allez chercher dans le fichier `/proc/devices` le numéro `major` choisi par linux.
     188* vous allez maintenant créer le noeud dans le répertoire `/dev`et le rendre accessible par tous.
     189  Le numéro mineur est 0 car il n'y a qu'une seule instance.
     190
     191{{{
     192sudo mknod /dev/ledbtn c major 0
     193sudo chmod a+rw /dev/ledbtn
     194}}}
     195
     196Le test de votre driver peut se faire par les commandes suivantes (avant de faire un vrai programme):
     197
     198{{{
     199$ echo "rien" > /dev/ledbtn
     200$ dd bs=1 count=1 < /dev/ledbtn
     201$ dmesg
     202}}}
     203
     204== Étape 4 : accès aux GPIO depuis les fonctions du pilote ==
     205
     206Pour vous aider, voici un code qui fait clignoter la LED du GPIO04 dans le module noyau.
     207* Pour l'accès aux GPIOs vous voyez que l'on peut simplifier les calculs d'adresses en utilisant une structure représentant l'organisation des registres.
     208* Vous noterez également que l'adresse physique de base des GPIO (ici 0x20200000) est mappé dans l'espace virtuel du noyau à l'adresse '''io_addresse'''.
     209* Vous trouvez aussi le moyen de faire faire périodiquement une fonction au système grâce à une file d'attente temporelle.
     210Inspirez-vous de code pour votre pilote.
     211
     212{{{
     213#include <linux/module.h>
     214#include <linux/init.h>
     215#include <asm/io.h>
     216#include <mach/platform.h>
     217
     218static const int LED0 = 4;
     219
     220//------------------------------------------------------------------------------
     221//                                GPIO ACCES
     222//------------------------------------------------------------------------------
     223
     224struct gpio_s
     225{
     226    uint32_t gpfsel[7];
     227    uint32_t gpset[3];
     228    uint32_t gpclr[3];
     229    uint32_t gplev[3];
     230    uint32_t gpeds[3];
     231    uint32_t gpren[3];
     232    uint32_t gpfen[3];
     233    uint32_t gphen[3];
     234    uint32_t gplen[3];
     235    uint32_t gparen[3];
     236    uint32_t gpafen[3];
     237    uint32_t gppud[1];
     238    uint32_t gppudclk[3];
     239    uint32_t test[1];
     240}
     241*gpio_regs = (struct gpio_s *)__io_address(GPIO_BASE);;
     242
     243enum {FUN_INPUT, FUN_OUTPUT};
     244
     245static void gpio_fsel(int pin, int fun)
     246{
     247    uint32_t reg = pin / 10;
     248    uint32_t bit = (pin % 10) * 3;
     249    uint32_t mask = 0b111 << bit;
     250    gpio_regs->gpfsel[reg] = (gpio_regs->gpfsel[reg] & ~mask) | ((fun << bit) & mask);
     251}
     252
     253static void gpio_write(int pin, bool val)
     254{
     255    if (val)
     256        gpio_regs->gpset[pin / 32] = (1 << (pin % 32));
     257    else
     258        gpio_regs->gpclr[pin / 32] = (1 << (pin % 32));
     259}
     260
     261//------------------------------------------------------------------------------
     262//                             TIMER PROGRAMMING
     263//------------------------------------------------------------------------------
     264
     265static struct timer_list led_blink_timer;
     266static int led_blink_period = 1000;
     267
     268static void led_blink_handler(unsigned long unused)
     269{
     270    static bool on = false;
     271    on = !on;
     272    gpio_write(LED0, on);
     273    mod_timer(&led_blink_timer, jiffies + msecs_to_jiffies(led_blink_period));
     274}
     275
     276//------------------------------------------------------------------------------
     277//                              MODULE INIT & EXIT
     278//------------------------------------------------------------------------------
     279
     280MODULE_LICENSE("GPL");
     281MODULE_AUTHOR("Franck from http://sysprogs.com/VisualKernel/tutorials/raspberry/leddriver/)");
     282MODULE_DESCRIPTION("leds on-off");
     283 
     284static int __init LedBlinkModule_init(void)
     285{
     286    int result;
     287
     288    gpio_fsel(LED0, FUN_OUTPUT);
     289    gpio_write(LED0, 1);
     290    setup_timer(&led_blink_timer, led_blink_handler, 0);
     291    result = mod_timer(&led_blink_timer, jiffies + msecs_to_jiffies(led_blink_period));
     292    BUG_ON(result < 0);
     293    return 0;
     294}
     295
     296static void __exit LedBlinkModule_exit(void)
     297{
     298    gpio_fsel(LED0, FUN_INPUT);
     299    del_timer(&led_blink_timer);
     300}
     301
     302module_init(LedBlinkModule_init);
     303module_exit(LedBlinkModule_exit);
     304
     305}}}