-
Notifications
You must be signed in to change notification settings - Fork 9
Kernel Module 4 Timer
Prerequisites: Kernel Module 3 - GPIO
Goal:
Add a timer my_timer
to the init function of my_module
and remove it in the exit function.
When my_timer
expires, a callback function is called. The callback function just
prints a message on the console an starts my_timer
Prerequisites: Kernel Module 3 - GPIO
Goal:
Add a timer my_timer
to the init function of my_module
and remove it in the exit function.
When my_timer
expires, a callback function is called. The callback function just
prints a message on the console an starts my_timer
again, and again, and again ...
The callback function runs in an interrupt context and cannot do much (see "further reading" below). It is common practice to use a workqueue for serious work. The timer callback adds work to a workqueue and the kernel will (later) check the workqueue and execute the work in a safe kernel thread context.
#include <linux/timer.h>
// for tiny delays like udelay, mdelay
// for longer delays you may use schedule_timeout
#include <linux/delay.h>
// for kernel clock ticks ("jiffies")
#include <linux/jiffies.h>
static struct timer_list my_timer;
// delay between two calls of my_timer_function in jiffies
static const int delay_jiffies = 1 * HZ;
// callback function that is called after delay
// note: this is a software interrupt, i.e. you
// - must not do excessive work here,
// - must not call functions that may sleep
// - may use printk (to ease debugging)
// - might want to add a workqueue to do serious work
static void my_timer_function(unsigned long data)
{
printk( KERN_NOTICE "I was here, jiffies=%lu\n", jiffies );
// timers are one-shot, so we have to set-up the next timer event:
my_timer.expires += delay_jiffies; // add another delay period
add_timer(&my_timer);
}
static int __init hello_init(void)
{
printk(KERN_INFO "Hello world!\n");
init_timer(&my_timer);
my_timer.data = (unsigned long)NULL; // a context pointer (poor man's "this" pointer)
my_timer.function = &my_timer_function;
my_timer.expires = jiffies + delay_jiffies; // = now + delay
add_timer(&my_timer);
return 0; // Non-zero return means that the module couldn't be loaded.
}
static void __exit hello_cleanup(void)
{
printk(KERN_INFO "Cleaning up module.\n");
del_timer(&my_timer);
}
Cross-compile and copy my_module.ko to the target. After insmod a message should appear periodically until rmmod my_module is executed:
root@raspberrypi:~ $ insmod /my_module.ko
Hello world!
root@raspberrypi:~ $ I was here, jiffies=4294944708
I was here, jiffies=4294944808
I was here, jiffies=4294944908
I was here, jiffies=4294945008
I was here, jiffies=4294945108
I was here, jiffies=4294945208
...
root@raspberrypi:~ $ rmmod my_module
Cleaning up module.
root@raspberrypi:~ $
Note that we see 100 jiffies per message. The delay was initialized to 1 Hz, so HZ is 100 on the target. For portability always use HZ or better of the time conversion functions supplied by the kernel, but never the standard (user-mode) library functions.
If you want to wait for, e.g. 5 seconds, use 5*HZ
(which is IMHO counter-intuitive) or use one of the many conversion functions like msecs_to_jiffies(5000)
.
Next: Kernel Module 5 - IRQ
High-resolution timer and tasklets: see function tasklet_hrtimer_start