Creating Timers in the Kernel

From RidgeRun Developer Connection
Jump to: navigation, search

Using Timer

The kernel keeps a flow of time by means of timer interrupts. These timers are measured by means of jiffies, this means that their precision can only be as low as these values. This can be configured in the open-menu in the following location:

Kernel Features --> Timer Frequency

This can be modified in values from 100 Hz to 1000 Hz

The default in the TX2, for example, is at 250 Hz which means that it has a precision of 4 ms. Be aware that, since the kernel depends on this value for a lot of its operations, changing it might have adverse effects on the performance of the system.


Usage Example

#include <linux/timer.h>
#include <linux/jiffies.h>

#define TIMEOUT 5000    /* milliseconds */
static struct timer_list custom_timer;
int count = 0;

...

/* Timer Callback function. This will be called when timer expires */
void timer_callback(unsigned long data)
{
     /* do your timer stuff here */
    printk(KERN_INFO "Timer Callback function Called [%d]\n",count++);
    
    /*
       Re-enable timer. Because this function will be called only first time. 
       If we re-enable this will work like periodic timer. 
    */
    mod_timer(&custom_timer, jiffies + msecs_to_jiffies(TIMEOUT));
}

...


static int __init driver_init(void)
{
    ...

    /* setup your timer to call my_timer_callback */
    setup_timer(&custom_timer, timer_callback, 0);
 
    /* setup timer interval to based on TIMEOUT Macro */
    mod_timer(&custom_timer, jiffies + msecs_to_jiffies(TIMEOUT));
 
    printk(KERN_INFO "Device Driver Insert...Done!!!\n");
    return 0
}

Using HR Timer

Using high-resolution timers will allow getting a better precision, this is still a software operation and can suffer from jitter.

These timers operate on the domain of nanoseconds rather than jiffies.

#include <linux/hrtimer.h>
#include <linux/time.h>

static struct hrtimer dexr_hrtimer;

static enum hrtimer_restart dexr_hrtimer_handler(struct hrtimer *timer)
{
        struct dexr_sync_signal *priv = dexr_head;
        int ret = HRTIMER_RESTART; /* This callback will by default self-restart */

        /**
         * Be warned that the kernel will behave here as a atomic context, so all operations should behave accordingly
         * i.e. they shouldn't sleep. So, for example:
         *
         * spinlocks should be used instead of mutexlocks
         * kmallocks should have the GFP_ATOMIC flag
         *
         */

        if (priv->enabled) {
                /* If restarting the next wakeup should be moved forward */
                hrtimer_forward_now(&dexr_hrtimer, ns_to_ktime(priv->nsecs_period));
        } else {
                /* If wishing to stop return HRTIMER_NORESTART */
                ret = HRTIMER_NORESTART;
        }
exit:
        return ret;
}

...


static int __init driver_init(void)
{
        ...

        /* setup your timer to call my_timer_callback */
        hrtimer_init(&dexr_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

        /* setup timer callback */
        dexr_hrtimer.function = &dexr_hrtimer_handler;
 
        printk(KERN_INFO "Device Driver Insert...Done!!!\n");
        return 0
}

The init timer has two arguments the clock that it uses which can be any of the following variables:

#define CLOCK_REALTIME			0
#define CLOCK_MONOTONIC			1
#define CLOCK_PROCESS_CPUTIME_ID	2
#define CLOCK_THREAD_CPUTIME_ID		3
#define CLOCK_MONOTONIC_RAW		4
#define CLOCK_REALTIME_COARSE		5
#define CLOCK_MONOTONIC_COARSE		6
#define CLOCK_BOOTTIME			7
#define CLOCK_REALTIME_ALARM		8
#define CLOCK_BOOTTIME_ALARM		9

It also has a mode available:

 * HRTIMER_MODE_ABS		- Time value is absolute
 * HRTIMER_MODE_REL		- Time value is relative to now
 * HRTIMER_MODE_PINNED		- Timer is bound to CPU (is only considered
 *				  when starting the timer)
 * HRTIMER_MODE_SOFT		- Timer callback function will be executed in
 *				  soft irq context, available from 4.16
 * HRTIMER_MODE_HARD		- Timer callback function will be executed in
 *				  hard irq context even on PREEMPT_RT, available from 5.4

These modes can also be combined, more information here.