MPS E4 - Kernel modules


Exercise 4 - Kernel modules

Formål

Denne øvelse giver erfaring med at skrive, kompilere og indsætte kernemoduler. Kernemoduler er de "containere" i hvilke vi fremadrette vil lægge vore Linux Device Drivere. Forståelsen af disse er derfor grundlæggende for at kunne skrive device drivere. Som første trin i at kunne lave device drivers, koncentrerer vi os derfor om kerne moduler.

Forberedelse

Du skal have et funktionelt VM-Ware Image og et Devkit8000.
Desuden skal følgende være udført:
§  Du skal have en udpakket version af linux-3.2.6 source træet (brug 7zr fra pakken p7zip pakken, som skal installeres)
§  Det at bygge en kerne er en flertrins raket. Først bruges en default konfiguration iha_devkit8000_defconfig, dernæst kompiles kernen. Se nedenfor:
~/source/linux-3.2.6$ make ARCH=arm CROSS_COMPILE=arm-angstrom-linux-gnueabi- iha_devkit8000_defconfig
~/source/linux-3.2.6$ make ARCH=arm CROSS_COMPILE=arm-angstrom-linux-gnueabi- -j4 uImage
Som det fremgår laves der et uImage (makefile target). Dette uImage kan I lægge ned på partitionen der hedder LABEL1 på jeres SD kort, hvorved at det bliver denne kerne som systemet booter på næste gang.
Bemærk at kompileringen af kernemoduler kan være lidt besværlig første gang, men når først tingene er sat rigtig op vha. make filer, source tree mm, så kører det ret gnidningsløst.

Øvelses Steps

Første stop på vejen til at skrive en device driver, er at bygge et kerne modul og prøve at indsætte det i kernen. Dette er hvad dagens første øvelse går ud på. Som vi oplevede i sidste øvelse, så tilgås de fleste enheder som filer i Linux. I øvelse step 2 vil vi prøve at tilgå lysdioden fra sidste gang, men denne gang fra en user space applikation.

Step 1: Lav dit første Kernemodul

Lav et ”hello_world” kerne modul og indsæt det i kernen.
a) Skriv ”hello_world” programmet: Lad dig inspirere kraftigt af kapitlet (se kapitel 2) i Linux Device Drivers (LDD3). Dermed også sagt driveren skal kunne det samme som "hello_world" driveren, hverken mere eller mindre end driver der beskrives i LDD3.


b) Skriv en Makefile:

Se slides igennem og kapitlet i bogen, bemærk at linierne med $(MAKE) SKAL begynde med en tab !!! Ellers se på makefile opgaven fra I3ISU. Hvor er jeres kernel source henne?


c) Kompilér programmet og kopiér det til target vha scp (dvs du skal bruge USB kablet)
Compile:


/..../hello_world$ scp hello.ko root@10.9.8.2:
Kan du ikke få forbindelse til target, så check din netværksopsætning og skriv evt:
/..../hello_world$ sudo ifup usb0
evt.
/..../hello_world$ sudo ifdown usb0
forinden.
Har du stadigvæk problemer, så se øvelse fra ISU: "Connecting to Target"
For at læse kerne beskeder, altså de som bliver outputtet med ”printk”, kan du benytte ”dmesg” kommandoen på target. Evt kan du åbne en ny terminal.

Copy to target


d) Indsæt modulet i kernen (insmod) og check med dmesg at det lykkedes.



e) Tag modulet ud igen (rmmod) og se at det også lykkedes.




Step 2: Få Lysdioden fra sidste øvelse til at lyse

Et kernemodul arbejder som bekendt i Linux kernen, hvor vi har adgang til alle ressourcer. Du skal i det følgende prøve at få SYS_LED2 tænde og slukke, ligesom i sidste øvelse. I dit hello_world modul skal du indsætte følgende kodestumper. Find selv ud af hvor de skal være, men det at tænde for SYS_LED2 skal placeres i init funktion, og den at slukke i exit funktionen. Bemærk ligeledes at der skal ryddes op igen i exit funktionen.
LÆS grundigt alle detaljer inden du går igang!!!
// Libraries
#include <linux/gpio.h>
#include <linux/ioport.h>
#include <asm/io.h>

// Pointere til IO6 Registre
volatile unsigned long *REG_GPIO6_OE;
volatile unsigned long *REG_GPIO6_DIN;
volatile unsigned long *REG_GPIO6_DOUT;

// Bede kernen om lov til at benytte memory området
if (request_mem_region (0x49058034, 12, "hello") == NULL) {
           printk ("Allocation for I/O memory range is failed\n");
           return 0;
       }

// Tildele addresser til pointere (mappet til en anden end den fysiske)
REG_GPIO6_OE = ioremap( 0x49058034, 1);
REG_GPIO6_DIN = ioremap(0x49058038, 1);
REG_GPIO6_DOUT = ioremap(0x4905803c, 1);

// Skrive 32-bit
iowrite32(<value>, <address pointer>);

Når du skal tilgå memory skal du bruge følgende funktioner:
// Læse 32-bit
<value> = ioread32(<address pointer>);
I din exit funktion skal du indsætte:
// Frigiv memory
release_mem_region (0x49058034, 12);
Du skal nu bruge ioread32/iowrite32 til at tænde for SYS_LED når modulet indsættes og slukke for den når det tages ud.

Explanation of the code above.
I/O memory regions must be allocated prior to use. The interface for allocation of memory regions (defined in <linux/ioport.h>) is:
struct resource *request_mem_region(unsigned long start_adr, unsigned long len,  char *name);                          

This function allocates a memory region of “
len” bytes, starting at “start_adr”. If all goes well, a non-NULL pointer is returned; otherwise the return value is NULL. All I/O memory allocations are listed in /proc/iomem.

NOTE: In this exercise we need to set OE for the SYS_LED3, read it’s value and write a new value (so that we only change the bit controlling SYS_LED3).
This means that we need the addresses:

  • 0x49058034
  • 0x49058038
  • 0x4905803c
Each address fills out 4 bytes but are otherwise consecutive. This means we need to allocate 12 bytes from starting address 0x49058034.


Memory regions should be freed when no longer needed, using:
void release_mem_region(unsigned long start_adr, unsigned long len);


void *ioremap( unsigned long phys_addr, unsigned long size )
Remap I/O into kernel address space. Phys_addr is the beginning of the physical address range. Size is the size of a physical address range.  It returns a virtual start of mapped range.

Code LED.c:


Code Makefile:


Compile:


Execute:



Verification:
Sysled_2 is on after running insmod LED.ko, and off after running rmmod LED.ko.

Step 3: Få en lysdiode til at lyse vha fil operationer (OPTIONAL)

Skriv en applikation som sætter tænder/slukker for en lysdiode.
For at tilgå enhederne som der allerede er skrevet device drivers til benyttes fil operationer. I applikationen skal du således benytte posix C I/O funktionerne: open(), close(), read(), write(). Læs mere om funktionerne her:http://linux.die.net/man/2/open / close / read / write
Skal man styre lysdioden i en applikation skal man gøre således:
§  Åbne filen som write-only (da dette er et output fra systemet)
§  Skrive værdien til filen
§  Lukke filen
For at åbne filen gør man sådan her:
fd = open("/sys/class/leds/<led name>/brightness", O_WRONLY)
Fil pointeren (fd) bruges så til de efterfølgende operationer.
Du kan skrive ls /sys/class/leds for at se hvilke LEDs som er tilrådighed
Du skal inkludere følgende biblioteker:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
For at skrive til lysdioden skal du skrive en enkelt char indeholdene ascii ’0’ eller ’1’.
Endnu mere om open/close mm kan findes her:http://www.opengroup.org/onlinepubs/007908775/xsh/open.html

Ingen kommentarer:

Send en kommentar