MPS E5 - Char Driver



Formål

Denne øvelse giver erfaring med at skrive, kompilere og indsætte en basal Linux Device Driver.

Forberedelse

§  Du skal have et funktionelt VM-Ware Image og et Devkit8000. 
§  Du skal være i stand til at kunne kompilere et kerne modul.
§  Du skal have læst kapitel 2+3 i Linux Device Drivers ed. 3
§  Du skal have skimmet dokumentationen i ~/sources/linux-3.2.6/Documentation/gpio.txt
§  Du skal bruge den Makefile I fandt frem til under sidste opgave (eller fik udleveret)

Øvelsen

Lav 2 drivere, der hhv. håndtere en læsning og en skrivning.
§  Driver 1: Læsning fra devicet (/dev/user_key) returnere hvorvidt USER_KEY er trykket ned eller ej.
§  Driver 2: Skrivning til devicet (/dev/sys_led3) bestemmer hvorvidt SYS_LED3 er tændt eller slukket.
NB! 2 Drivere medførere 2x makefile + 2xdriver c fil, navnene er desuden guivet ovenfor.
Vi genbruger således de GPIO'er vi fandt frem til fra U-BOOT opgaven. Mht. brugen af gpio er det så smart indrettet at der allerede er lavet understøttelse for OMAP’ens GPIO i Linux kernen. Der er med andre ord allerede implementeret en lav-niveau driver som skriver til GPIO_OE, GPIO_DATAIN og GPIO_DATAOUT registrene i OMAP CPUen. Disse registre er som bekendt memory mappede og har hver sin addresse. Den allerede eksisterende lavere niveau driver tilgår disse ved at skrive direkte til de pågældende adresser. Vi skal således benytte de metoder som kernen allerede understøtter og ikke skrive direkte til register adresserne. Dette kunne sagtens have været tilfældet ved et system med en mindre udviklet kerne.
Metoderne til håndtering af gpio nedarver vi fra ~/sources/linux-3.2.6/include/linux/gpio.h Se dokumentationen under ~/sources/linux-3.2.6/Documentation/gpio.txt Bemærk at det er yderst simpelt at håndtere gpio, vi skal blot angive OMAP’ens gpio nummer for at specificere hvilken vi ønsker at arbejde på.
Vigtige metoder fra gpio.h(kan findes under ~/sources/linux-3.2.6/include/linux/gpio.h):
/* get interrupt line attached to GPIO port # */
unsigned int gpio_to_irq(unsigned int gpio);

/* request GPIO, returning 0 or negative errno.
* non-null labels may be useful for diagnostics.
*/
int gpio_request(unsigned gpio, const char *label);

/* release previously-claimed GPIO */
void gpio_free(unsigned gpio);

/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int default_value);

/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);

/* GPIO OUTPUT */
void gpio_set_value(unsigned gpio, int value);
Include filer til jeres driver, så I kan komme i gang:
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/module.h>
Derudover når du leder efter en funktion i kernen bla. for at se dens signatur og evt. brugsmønster i andre drivere så brug http://lxr.linux.no/linux+v3.2.6. Ellers se slides / lektie der vil I kunne finde alt det i søger. Alternativt så er google altid til tjeneste.

GPIO LDD driverne i steps

Nedenfor er vist en overfladisk skabelon som kan bruges som inspiration når I skal lave de 2 drivere. Bemærk at driver 1 ikke har en write() funktion, da den kun bør kunne læse fra USER_KEY. Den gpio som svare til USER_KEY skal altså være sat til input, som er default. Derimod skal driver 2 have både en write() og en read() funktion. write() funktionen er naturligvis for at kunne tænde/slukke for SYS_LED3, mens read() er til for at kunne få at vide om den er tændt eller slukket. I det sidste tilfælde skal gpio'en være et output.
HUSK! At udfylde Fops struct'en, module_init, module_exit, samt licens, author og description
a) Implementer ”init” og ”exit” Start med at implementere koden til ”init” og ”exit”. Det er her at du skal requeste og free gpio’s, samt sætte gpio_direction til at styre pin’ens retning. Samtidigt er afgørende at du/I laver en ordentlig fejlhåndtering i init().
static int mygpio_init(void)
{
// Request GPIO
// Set GPIO direction (in or out)
// Make device no
// Register Device
// Cdev Init
// Add Cdev
}
static void mygpio_exit(void)
{
// Unregister Device
// Delete Cdev
// Free GPIO
}
Bootkey:



Sys_led4:



b) Implementer ”open” og ”release” Implementer koden til ”open” og ”release”.
int mygpio_open(struct inode *inode, struct file *filep)
{
   int major, minor;

   major = MAJOR(inode->i_rdev);
   minor = MINOR(inode->i_rdev);
   printk("Opening MyGpio Device [major], [minor]: %i, %i\n", major, minor);

}
int mygpio_release(struct inode *inode, struct file *filep)
{
   int minor, major;
  
   major = MAJOR(inode->i_rdev);
   minor = MINOR(inode->i_rdev);

}

Same for both bootkey and sysled


c) Implementer ”read” (Driver 1 - læsning af USER_KEY + Driver 2 - Er den tændt eller ?) Skriv koden for ”read”, dvs den kode som eksekveres når en applikation forsøger at læse fra et device.
ssize_t mygpio_read(struct file *filep, char __user *buf,
                 size_t count, loff_t *f_pos)
{
 char readBuf[MAXLEN];
 int len, result=-1, minor;
 minor = MINOR(filep->f_dentry->d_inode->i_rdev);


 // Hvilke trin er det der skal udføres?
 // Hint int 2 string via sprintf() - antagelsen er at det er strenge der sendes til og fra user space. Det debugging lettere.

 *f_pos += len;
 return len;   
}
Same code for both bootkey and sysled.


Når du skal teste, skal du indsætte modulet i kernen og oprette en node (ex: mknod /dev/mygpio c 62 0). Du kan med fordel lave en lille script fil som indsætter modulet og opretter device nodes. Nodes skal kun oprettes en gang (indtil du hiver strømmer). Husk at da du har 2 drivers skal de have hver deres major nummer.
Bemærk derudover at programmer som f.eks "cat" læser indtil de møder en End of File karakter (EOF). "cat" vil derfor læses uendeligt fra driveren og læsse bunkevis af data ud på terminalen. Alternativt kan du skrive en lille app á la denne og kompilere den med "arm-amgstrom-linux-gnueabi-gcc -o rd rd.c".

Following have been made on beagleboard:
insmod name
mknod /dev/name c major minor
To read, following command has been used:
cat /dev/name
This gives a continuous read from the driver.

d) Implementer ”write” (Driver 2 - Skrivning til SYS_LED3 - altså om den er tændt eller ej.)
ssize_t mygpio_write(struct file *filep, const char __user *ubuf,
                 size_t count, loff_t *f_pos)
{
 int minor, len, value;
 char kbuf[MAXLEN];
  
 // Hvilke trin er det der skal udføres?
 // Hint string 2 int via sscanf() - antagelsen er at det er strenge der sendes til og fra user space. Det debugging lettere.

 return count;
}

Code:


Test:
Echo 1 > /dev/sysled_4  //Makes sysled4 go off
Echo 0 > /dev/sysled_4  //Makes sysled4 go on
It successfully change the output of sysled_4.

Ingen kommentarer:

Send en kommentar