1. 内核定时器的作用

当中断触发时,修改定时器时间间隔,进入定时器回调函数,待完成回调则恢复。

 

2. 定时器嵌入其他数据结构

 

 

 struct timer_list timer, 这个结构体作为定时器的数据结构,通过jiffies进行时间间隔的配置:

mod_timer(&timer,jiffies + msecs_to_jiffies(40));

 

旧版本的定时器数据结构:

struct timer_list{
    /* OTHERS */
    unsigned long expires;
    void (*function)(unsigned long);
    unsigned long data;
    /* OTHERS */
};

 新版本的定时器数据结构:

struct timer_list {
    /*
     * All fields that change during normal runtime grouped to the
     * same cacheline
     */
    struct hlist_node   entry;
    unsigned long       expires;
    void            (*function)(struct timer_list *);
    u32         flags;

#ifdef CONFIG_LOCKDEP
    struct lockdep_map  lockdep_map;
#endif
};

 此时的struct timer_list * 作为一个结构体指针传进去,这时候有一个神秘的接口可以把该结构体找到并且正确传递到函数中。

参考这个博客就知道这个数据结构怎么用:

https://lwn.net/Articles/735892/

 

3. 怎么用timer_list接口

1. timer_setup(&timer,current_key_timer_func,0); 初始化
2. mod_timer(&timer,jiffies + msecs_to_jiffies(40));其他地方调用启动定时器
3. 回调接口
static void current_key_timer_func(struct timer_list *tm)
{
    struct key_table_list *dev = from_timer(dev,tm,timer);
}

 把tm 传递给timer然后赋给dev,dev 存在一个timer。

 

4. 一个中断的驱动demo可以融入io中断、原子锁、tasklet以及定时器。

/* tasklet func */
static void keys_tasklet_func(unsigned long data)
{
    /* fetch which gpio's */
    struct key_table_list *dev = ( struct key_table_list * ) data;
    ev_press   = 1;

    /* mod_timer set */
    mod_timer(&dev->timer,jiffies + msecs_to_jiffies(40));
}

/* interrupt API */
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
    struct key_table_list *dev = ( struct key_table_list * ) dev_id;
    int tmp_value = -1;
    tmp_value = gpio_get_value(dev->gpio);
    printk("button interrupt at [%d] key [No.%d]......key_status [%s]\n",
            dev->gpio,
            dev->key,
            (tmp_value > 0) ? "pulled-up" : "pressed-down");

    tasklet_schedule(&dev->tasklet);

    return IRQ_RETVAL(IRQ_HANDLED);
}

/* current key function */
static void current_key_timer_func(struct timer_list *tm)
{
    struct key_table_list *dev = from_timer(dev,tm,timer);

    int level = -1;
    level = gpio_get_value(dev->gpio);

    atomic_set(&dev->status,level);
    wake_up_interruptible(&button_waitq);

}

 只提供关键代码。

 

4. 中断机制

1. 底半部机制: tasklet、工作队列、软中断和线程化irq。

tasklet:软中断执行上下文,顶半步返回,利用tasklet处理。

工作队列: 内核线程执行上下文,调度和睡眠。

软中断: 软中断执行上下文,不允许睡眠。

线程化irq: request_threaded_irq 和 devm_request_threaded_irq 申请中断。