Skip to content

Kernel Module 4 Timer

Frank Bauernöppel edited this page Mar 19, 2017 · 2 revisions

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

Alternatives

High-resolution timer and tasklets: see function tasklet_hrtimer_start

Further reading

Clone this wiki locally