CPAC+Øvelse+5

toc =Øvelse 5: GPIO Linux Device Driver= Vi skal skrive en character device driver så vi kan sætte og aflæse værdier på vores GPIO (J5) som er mappet til GPIO130-137

Den skal sættes op på følgende måde:
 * ~ Pin ||~ Name ||~ OMAP GPIO ||~ Direction ||
 * = 1 ||= GPIO1_7 ||= GPIO137 ||= OUT ||
 * = 2 ||= GPIO1_6 ||= GPIO136 ||= OUT ||
 * = 3 ||= GPIO1_5 ||= GPIO135 ||= OUT ||
 * = 4 ||= GPIO1_4 ||= GPIO134 ||= OUT ||
 * = 5 ||= GPIO1_3 ||= GPIO133 ||= IN ||
 * = 6 ||= GPIO1_2 ||= GPIO132 ||= IN ||
 * = 7 ||= GPIO1_1 ||= GPIO131 ||= IN ||
 * = 8 ||= GPIO1_0 ||= GPIO130 ||= IN ||
 * = 9 ||= GND ||=  ||=   ||
 * = 10 ||= 3.3V ||=  ||=   ||

Dette er den færdige driver.

 code format="c" struct gpioPort{ unsigned int num; enum direnu { in, out } dir; const char label[10]; // Very important to specify a size. Just [] causes havoc to gpio indexing } gpio[] = { {130, in, "gpio0_0"}, {131, in, "gpio0_1"}, {132, in, "gpio0_2"}, {133, in, "gpio0_3"}, {134, out, "gpio0_4"}, {135, out, "gpio0_5"}, {136, out, "gpio0_6"}, {137, out, "gpio0_7"}, {139, out, "led3"} }; code Læg mærke til at GPIO139, som er mappet til LED3, er blevet tilføjet.
 * Relevant kode udsnit**

Implementer ”init”
Init er den funktion der bliver kaldt når vi indsætter modulet i kernen. Så det er her skaffer os adgang til de ressourcer vi skal bruge. code format="c" /* * Request GPIO Ressources */ for (i=0; i<gpio_len; ++i) if ((err = gpio_request(gpio[i].num, gpio[i].label)) < 0) {       printk (KERN_ALERT "Error %d requesting %s ressource\n", err, gpio[i].label); return err; } code Udover at skaffe os adgang sætter vi også vores porte op så de passer til vores opsætning. (se ovenstående tabel) code format="c" /* * Set GPIO Direction */ for (i=0; i<gpio_len; ++i) {   if (gpio[i].dir == in) {       if ((err = gpio_direction_input(gpio[i].num)) < 0) {           printk (KERN_ALERT "Error %d setting %s as input\n", err, gpio[i].label); return err; }   }    else {        if ((err = gpio_direction_output(gpio[i].num, 0)) < 0) {           printk (KERN_ALERT "Error %d setting %s as output\n", err, gpio[i].label); return err; }   } } code

Implementer ”exit”
Exit er den funktion der bliver kaldt når modulet bliver fjernet fra kernen, dvs. det er den funktion der skal rydde op. Her sætter vi alle portene til inputs. code format="c" /* * Set GPIOs to Inputs * (leave things as when we entered) */ for (i=0; i<gpio_len; ++i) if ((err = gpio_direction_input(gpio[i].num)) < 0) printk (KERN_ALERT "Error %d setting %s as input\n", err, gpio[i].label); code Hvorefter vi frigiver de ressourcer vi tog da vi indsatte modulet. code format="c" /* * Free GPIO Ressources */ for (i=0; i<gpio_len; ++i) gpio_free(gpio[i].num); code

Implementer ”open” og ”release”
I open og release skal vi tjekke om det er de rigtige major/minor numre der bliver skrevet til. Da dette er et lukket system kan vi godt tillade os at bruge et statisk major nummer. Hvis det var f.eks. en workstation hvor en bruger kunne finde på at tilføje ny hardware skal vi have systemet til at give os et major nummer for at sikre os at vi ikke tager et allerede brugt nummer. code format="c" /* * Check if major/minor is valid */ if (!check_major(major)) return -1; if (!check_minor(minor)) return -1; code Da major/minor numre bliver tjekket en del steder skrev vi et par funktionen for ikke at copy/paste kode. code format="c" bool check_major(int major) {   if (major == MYGPIO_MAJOR) return true; printk(KERN_ALERT "Error: major number is invalid\n"); return false; }

bool check_minor(int minor) {   if (minor >= 0 && minor < gpio_len) return true; printk(KERN_ALERT "Error: minor number is invalid\n"); return false; } code

Implementer ”write”
Write bliver kaldt når der skal skrives til en GPIO (et ben/en pin). Denne funktion tager indput fra brugeren, men da brugeren arbejder i user-space, som bruger virtuel hukommelses adresser, og modulet arbejder i kernel-space, som bruger absolute hukommelses adresser, bliver vi nød til at bruge en funktion som kan kopiere brugerens indput over i kernel-space. Det gøres via. code format="c" /* * Copy data from user space * to kernel space */ copy_from_user(kbuf, ubuf, len); code Når indput er lagt over i kernel-space er det lige til at sætte værdien. code format="c" /* * Set GPIO port * (if an output port) */

/**** Use gpio_set_value on appropriate port ****/ if (gpio[minor].dir == out) gpio_set_value(gpio[minor].num, value); else {   printk (KERN_ALERT "Error trying to write to none output on %s\n", gpio[minor].label); return -1; } code

Implementer ”read”
Read er den funktion der bliver kaldt når der skal aflæses en værdi fra en GPIO (et ben/en pin). code format="c" /* * Read GPIO port * (if an input port) */ if (gpio[minor].dir == in) result = gpio_get_value(gpio[minor].num); else {   printk (KERN_ALERT "Error trying to read none input %s\n", gpio[minor].label); return -1; } code Lige som før da vi skulle gå fra user til kernel-space bliver vi igen nød til at bruge en funktion for at gå fra kernel-space til user-space via. code format="c" /* * Copy string result * to User Space */ copy_to_user(buf, readBuf, len); code

=Test= Først indsætter vi modulet i kernen: code root@DevKit8000:~# insmod mygpio.ko code

LED test
Så skal vi oprette en node til vores led3. Led3 har minor nummer 8 i vores driver. //Jævnfør gpioPort for sammenhæng mellem GPIO# og minor nummer// code root@DevKit8000:~# mknod /dev/led3 c 62 8 code Hvorefter vi kan tænde og slukke for LED3 således: code // Tænd LED3 root@DevKit8000:~# echo 1 > /dev/led3 // Sluk LED3 root@DevKit8000:~# echo 0 > /dev/led3 code

I/O lus
Nu vil vi gerne teste om vi kan læse et indput samt sætte et output andet end LED3. Vi placere en lus mellem ben 4 (GPIO134) og 6 (GPIO132). //Jævnfør gpioPort for sammenhæng mellem GPIO# og minor nummer// Hvorefter vi opretter de noder der passer dertil. code root@DevKit8000:~# mknod /dev/myGpioIn c 62 2 root@DevKit8000:~# mknod /dev/myGpioOut c 62 4 code Vi bruger nu et lille reader program ([|rd]) til at læse vores indput fil (/dev/myGpioIn). Grunden til vi ikke bare bruger er fordi vores indput fil ikke indeholder en [|EOF]  så  går helt amok.

Vi læser vores indput: code root@DevKit8000:~# rd /dev/myGpioIn using file: /dev/myGpioIn read elements: 2

str = 0 code Vi ser at den er lav, så vi prøver at sætte vores output til høj og tjekker igen. code root@DevKit8000:~# echo 1 > /dev/myGpioOut

root@DevKit8000:~# rd /dev/myGpioIn using file: /dev/myGpioIn read elements: 2

str = 1 code Den skiftede til høj dvs. det virker.