Opgave+8

= Formål = I en device driver ønsker vi nogle gange at foretage noget autonomt, uden at vi tilgår denne, eks vha read / write. Et godt eksempel kunne være en driver som henter værdier fra en A/D konverter med et fast interval og gemmer værdierne i en fifo buffer. User space applikationen kan herved læse en mængde samples ad gangen og ikke have nær så stramme tidskrav, som hvis der skulle læses én sample ad gangen. Dette vil øge den præcisionen af samplingstidspunktet I denne øvelse skal vi lave en tilføjelse til vores eksisterende GPIO driver. Vha et ioctl kald skal det være muligt at sætte en output pin til at toggle med et fast interval. Du vil komme til at lære om:
 * IOCTL kald
 * Kernel Timers
 * Kernel Interrupts
 * Blocking I/O

Selve opgaven kan ses her

= Øvelses Steps = Lav en driver som kan toggle et GPIO output (1 0 1 0 1…). Driveren skal indeholde to ioctl kald til hhv at starte/stoppe timeren samt til at sætte frekevnsen af outputtet.


 * a) Implementer ioctl kald Start med at implementere et ioctl kald i driveren og en lille tilhørende testapplikation. Du kan i driveren evt blot udskrive til kernen ved hjælp af printk, når en given IOCTL funktion kaldes.**

Sender Test_Message til kernemodulet(module.c) når programmet køres (Test_Message er defineret til 1). code format="cpp"
 * toggler.c**
 * 1) include 
 * 2) include 
 * 3) include 
 * 4) include 


 * 1) define Test_Message 1

int main(int narg, char *argp[]) {   int fd;

fd = open("/dev/adc0", O_RDWR); printf("fd: %i\n", fd);

ioctl(fd, Test_Message);

close(fd); } code Følgende er udvidelse til GPIO kernemodulet. Der udskriver en kernel alert når Test_Message modtages fra toggler.c. code format="cpp" ssize_t cdrv_ioctl(struct inode *Inode, struct file *filep,                          unsigned int cmd, unsigned long arg) {   int minor = MINOR(filep->f_dentry->d_inode->i_rdev);
 * module.c**
 * 1) define Test_Message 1

switch(cmd) {      case Test_Message: printk(KERN_ALERT "Test_Message modtaget\n"); break; default: printk(KERN_ALERT "Ukendt ioctl-kommando: %d\n", cmd); }   return 0; }

struct file_operations dac7612_Fops = {   .owner   = THIS_MODULE, .open   = cdrv_open, .release = cdrv_release, .ioctl = cdrv_ioctl, }; code


 * b) Implementer toggler Implementer en periodisk timer til at styre et GPIO output, du kan evt vælge en af lysdioderne hvis de ikke er reserveret af andre drivere.**

code format="cpp"
 * module.c**
 * 1) define TOGGLE_ON 1
 * 2) define TOGGLE_OFF 2
 * 3) define SET_FREQUENCY 3


 * 1) define JIFFIES_HZ 128

bool led_val = false; int interval = 1; // Tid i sek mellem hver toggle struct timer_list my_timer; ...

static void timer_func(unsigned long arg) {   // Start ny timer my_timer.expires = jiffies + interval*JIFFIES_HZ; add_timer(&my_timer);

// Toggle LED3 led_val = !led_val; gpio_set_value(164, led_val); } code I init-funktionen tilføjes følgende for at initiere my_timer og for at initiere lysdioden (LED3). code format="cpp" // Initier timer til at køre timer_func når timeren udløber my_timer.function = &timer_func; init_timer(&my_timer);

if (gpio_request(164, "gpio164") < 0) printk("Kunne ikke requeste GPIO164\n");

gpio_direction_output(164, true); code

ioctl-funktionen udvides til at kunne modtage/behandle de nye beskeder. code format="cpp" switch(cmd) {      case TOGGLE_ON: printk("TOGGLE_ON modtaget\n"); my_timer.expires = jiffies + interval*JIFFIES_HZ; add_timer(&my_timer); break; case TOGGLE_OFF: printk("TOGGLE_OFF modtaget\n"); del_timer_sync(&my_timer); break; case SET_FREQUENCY: // Toggler-programmet sender arg med som en integer printk("SET_FREQUENCY modtaget Arg: %d\n", arg); interval = arg; break; default: printk(KERN_ALERT "Ukendt ioctl-kommando: %d\n", cmd); } code

Userspace-programmet opdateres til at understøtte de nye kommandoer. Der kan bruges "./toggler on" og "./toggler off" til at slå kernemodulets timer til og fra og "./toggler set #" hvor # er intervallet i hele sekunder som kernemodulet slår lysdioden til eller fra. code format="cpp" int main(int narg, char *argp[]) {   int fd; int interval;
 * toggler.c**

if (narg > 1) {       fd = open("/dev/adc0", O_RDWR);

if (strcmp(argp[1], "on") == 0) {           printf("Starter..\n"); ioctl(fd, TOGGLE_ON); }       else if (strcmp(argp[1], "off") == 0) {           printf("Slukker..\n"); ioctl(fd, TOGGLE_OFF); }       else if (strcmp(argp[1], "set") == 0 && narg > 2) {           interval = atoi(argp[2]); if (interval > 0) {               printf("Saetter interval til: %d\n", interval); ioctl(fd, SET_FREQUENCY, interval); }       }        close(fd); } } code Modulet og userspace-programmet virker som forventet. Lysdioden slukker og tænder hvert sekund. Sættes "./toggler set 3", slukker og tænder lysdioden kun hvert 3. sekund. "./toggler on" og "./toggler off" virker også.


 * c) Når det virker, kan du prøve at køre ”top” fra linux prompten og check hvor belastet processoren er. Prøv at ændre din implementation til at bruge en busy-wait metode og se hvad som sker ifht processor belasting.**

Med den periodiske timer bruges der 0% CPU når programmet kører. Der implementeres i stedet en busy-wait metode code format="cpp" int target_jiffies = jiffies + interval*JIFFIES_HZ; while (jiffies < target_jiffies) ; code LED'en blinker igen som den skal hvert sekund. CPU'en belastes dog så meget at vi ikke kunne bruge "top" til at checke hvor belastet processeren var ved brug af programmet. Timere er derfor klart at foretrække.