mandag den 8. november 2010

Opgave 8
I denne øvelse skal vi lave en tilføjelse til vores GPIO driver. Vha et ioctl kald skal det være muligt at sætte en output pin til at toggle med et fast interval.

Implementer ioctl kald
ved ioctl kladet udskriver vi til kernen:
IOCTL kladet implimenteres ligesom en read eller write funktion i driveren. IOTCL kan tilgås fra userspace ligesom read og write, og bruges ofte til at sætte driveren op til noget speceilt, det kunne f.eks. være baud-rate på en serial-driver.
IOCTL funktionen skal kaldes med et IOCTL-nummer. Da ioctl-numrene er globale skal man først sikre sig at man vælger et nummer der ikke er optaget. Alt under 1000 er med stor sikkerhed optaget allerede (enten af systemet eller andre drivere) så derfor valgte vi to vilkårlige tal over 1000. Valget faldt på 1101 og 1102.

Det første vi gør er at tilføje IOCTL i file_operations, så muligheden for IOCTL er der:
I vores file_operations struct tilføjer vi muligheden for ioctl-kald.

struct file_operations mygpio_Fops =
{
.owner = THIS_MODULE,
.open = mygpio_open,
.release = mygpio_release,
.write = mygpio_write,
.read = mygpio_read,
.ioctl = mygpio_ioctl,
};

Dernæst skal vi angive vores magiske IOCTL numre:
#define JOE1 1101
#define JOE2 1102

Så implimentere vi IOCTL-funktionen:
size_t mygpio_ioctl(struct inode* Inode, struct file* filep, unsigned int cmd, unsigned long arg)
{
int minor = MINOR(filep->f_dentry->d_inode->i_rdev);
switch(cmd)
{
case JOE1:
printk(KERN_ALERT "Det var en etter\n");
break;
case JOE2:
printk(KERN_ALERT "Det var en toer %i\n", arg);
break;
default:
printk(KERN_ALERT "Defualt default!\n");
break;
}
return 0;
}

Læg mærke til case: JOE2, hvor vi samtidig udskriver arg, denne angiver nemlig hvilke argumenter vi sender med.

Vi laver en applikation der kan kontrollere driveren fra userspace:

#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/timer.h>

int main()
{

int fd = open ( "/dev/joenode1", O_RDWR | 0666);

ioctl(fd, 1101); //burde udskrive Det var en etter til kernen
close(filePtr);
int fd = open ( "/dev/joenode1", O_RDWR | 0666);

ioctl(fd, 1102, 7); //sender 7 med som argument
//burde udskrive Det var en toer 7 til kernen
close(filePtr);
return 0;
}

Test:
Først indsætter vi modulet, og opretter noden, og køre dmesg:

kører vores testprogram, og ser hvad der sker:

Implementer toggler
vi valgte kun at implimentere en timer der ikke belastede CPU'en da denne er den mest anvendelige, og vi var lidt bagefter med CPAC opgaverne..


Til timeren brugte vi siden her som inspiration:
og kom frem til følgende kode som fungerede:
size_t mygpio_ioctl(struct inode* Inode, struct file* filep, unsigned int cmd, unsigned long arg)
{
int minor = MINOR(filep->f_dentry->d_inode->i_rdev);
switch(cmd)
{
case INIT_TIMER:
printk("Init timer!\n");
init_timer(&my_timer);
//jiffies og HZ er system vaiabler der angiver tiden
my_timer.expires = jiffies + arg*HZ;
my_timer.function = timer_func;
my_timer.data = arg;
add_timer(&my_timer);
break;
case SET_INTERVAL:
printk("Setting interval %i\n", arg);
my_timer.expires = jiffies + arg*HZ;
break;
case EXIT_TIMER:
printk("Exit timer!\n");
del_timer_sync(&my_timer);
break;
default:
printk("Defualt case entered!\n");
}
return 0;
}

//og her den timer_func der udføres når den udløber
static void timer_func(long int arg)
{
printk("Entering timer_func");
//init_timer(&my_timer);
add_timer(&my_timer);
my_timer.expires = jiffies + arg*HZ;
my_timer.function = timer_func;
my_timer.data = arg;
if(val == 1)
{
printk("Setting val %i\n", val);
gpio_set_value(139, 1);
val = 0;
}
else
{
printk("Setting val %i\n", val);
gpio_set_value(139, 0);
val = 1;
}
}

VI fik LED'erne til at blinke med et interval vi sendte fra userspace med et kald til IOCTL, med numret for den anden IOCTL kommando, hvor vi kunne sætte intervallet for blinket, og den gjorde og alt var godt : ) : ) : ) : )

1 kommentar: