新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > linux驱动之内核定时器驱动设计

linux驱动之内核定时器驱动设计

作者:时间:2016-12-01来源:网络收藏
我的环境:
Fedora 14 内核版本为2.6.38.1
开发板:ARM9 TQ2440
移植内核版本:linux-2.6.30.4
定时器在linux内核中主要是采用一个结构体实现的。但是需要注意定时器是一个只运行一次的对象,也就是当一个定时器结束以后,还需要重现添加定时器。但是可以采用mod_timer()函数动态的改变定时器到达时间。
这个驱动主要实现内核定时器的基本操作。内核定时器主要是是通过下面的结构体struct timer_list实现。需要的头文件包括#include,但是在实际开发过程中不需要包含该头文件,因为在sched.h中包含了该头文件。
struct timer_list {
struct list_head entry;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
定时器的实现主要是该结构体的填充和部分函数的配合即可完成。其中红色的部分是最主要的几个元素,1、expires主要是用来定义定时器到期的时间,通常采用jiffies这个全局变量和HZ这个全局变量配合设置该元素的值。比如expires = jiffies + n*HZ,其中jiffies是自启动以来的滴答数,HZ是一秒种的滴答数。
2、function可以知道是一个函数指针,该函数就是定时器的处理函数,类似我们在中断中的中断函数,其实定时器和中断有很大的相似性。定时器处理函数是自己定义的函数。
3、data通常是实现参数的传递,从function的参数类型可以知道,data可以作为定时器处理函数的参数。
其他的元素可以通过内核的函数来初始化。
初始化函数为:
init_timer(struct timer_list * timer);
或者直接DEFINE_TIMER宏实现定义和初始化操作。
#define DEFINE_TIMER(_name, _function, _expires, _data)
struct timer_list _name =
TIMER_INITIALIZER(_function, _expires, _data)
添加定时器到内核的函数:
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
删除定时器函数,如果定时器的定时时间还没有到达,那么才可以删除定时器:
int del_timer(struct timer_list *timer)
修改定时器的到达时间,该函数的特点是,不管定时器是否到达时间,都会重现添加一个定时器到内核。所以可以在定时处理函数中可以调用该函数修改需要重新定义的到达时间。
int mode_timer(struct timer_list *timer,unsigned long expires)
int mod_timer(struct timer_list *timer, unsigned long expires)
{
/*
* This is a common optimization triggered by the
* networking code - if the timer is re-modified
* to be the same thing then just return:
*/
if (timer->expires == expires && timer_pending(timer))
return 1;
/*注意调用的条件,也就是说明当前的定时器为链表的最后一个*/
return __mod_timer(timer, expires, false);
}
static inline int
__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
{
struct tvec_base *base, *new_base;
unsigned long flags;
int ret;
ret = 0;
timer_stats_timer_set_start_info(timer);
BUG_ON(!timer->function);
base = lock_timer_base(timer, &flags);
if (timer_pending(timer)) {
detach_timer(timer, 0);
ret = 1;
} else {
if (pending_only)
goto out_unlock;
}
debug_timer_activate(timer);
new_base = __get_cpu_var(tvec_bases);
if (base != new_base) {
/*
* We are trying to schedule the timer on the local CPU.
* However we cant change timers base while it is running,
* otherwise del_timer_sync() cant detect that the timers
* handler yet has not finished. This also guarantees that
* the timer is serialized wrt itself.
*/
if (likely(base->running_timer != timer)) {
/* See the comment in lock_timer_base() */
timer_set_base(timer, NULL);
spin_unlock(&base->lock);
base = new_base;
spin_lock(&base->lock);
timer_set_base(timer, base);
}
}
timer->expires = expires;
internal_add_timer(base, timer);
out_unlock:
spin_unlock_irqrestore(&base->lock, flags);
return ret;
}
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
unsigned long expires = timer->expires;
unsigned long idx = expires - base->timer_jiffies;
struct list_head *vec;
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
vec = base->tv1.vec + i;
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
vec = base->tv2.vec + i;
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = base->tv3.vec + i;
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
vec = base->tv4.vec + i;
} else if ((signed long) idx < 0) {
/*
* Can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
*/
vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
} else {
int i;
/* If the timeout is larger than 0xffffffff on 64-bit
* architectures then we use the maximum timeout:
*/
if (idx > 0xffffffffUL) {
idx = 0xffffffffUL;
expires = idx + base->timer_jiffies;
}
i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
vec = base->tv5.vec + i;
}
/*
* Timers are FIFO:
*/
/*添加到链表的最后,这说明mod_timer实现了重新注册一个定时器的操作*/
list_add_tail(&timer->entry, vec);
}
从上面的分析可以看出,mod_timer的实现过程比较复杂,但是基本上说明了mod_timer函数重新注册定时器的操作过程。
一般而言定时器的基本操作主要是上面的几个函数。
我的基于内核定时器的驱动函数如下,参考了宋宝华的Linux设备驱动开发详解(第二版)。
上一页 1 2 下一页

评论


技术专区

关闭