1268 字
6 分钟
Linux内核定时器
新版本Kernel下定时器API得到了比较好的优化,以正点原子阿尔法基于4.4版本kernel的旧版API为例做对比:
定时器回调函数:
旧版:
void timer_function(unsigned long arg) { struct timer_dev *dev = (struct timer_dev *)arg; static int sta = 1;
/* ....... */
}新版:
void timer_function(struct timer_list *t){ struct timer_dev *dev = from_timer(dev, t, timer); /* 从定时器实例获取父级结构体 */ static int sta = 1;
/* ....... */}新版定时器回调函数形参直接为timer_list实例,而定时器父级结构体不再通过arg获取,kernel提供了from_timer函数:
#define from_timer(var, callback_timer, timer_fieldname) \ container_of(callback_timer, typeof(*var), timer_fieldname)这个宏自container_of()扩展而来:
/** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * * WARNING: any const qualifier of @ptr is lost. */#define container_of(ptr, type, member) ({ \ void *__mptr = (void *)(ptr); \ static_assert(__same_type(*(ptr), ((type *)0)->member) || \ __same_type(*(ptr), void), \ "pointer type mismatch in container_of()"); \ ((type *)(__mptr - offsetof(type, member))); })container_of能从结构体成员指针推导出其父级结构体的指针:
ptr:指向结构体member成员的指针。type:包含member成员的结构体类型。member:type结构体中的成员变量名。
扩展后,from_timer能根据以下形参计算出其所属的父级结构体(这里为timer_dev)的指针:
var:目标结构体的变量(不是指针,而是变量名)。callback_timer:指向timer_list结构体的指针(定时器回调传入的参数)。timer_fieldname:timer_list在var结构体中的字段名。
定时器初始化函数
旧版
旧版API比较复杂,还要手动设置结构体成员:
init_timer(&timerdev.timer);timerdev.timer.function = timer_function;timerdev.timer.data = (unsigned long)&timerdev;新版
Kernel提供timer_setup(),这个宏实际上由init_timer_key()函数扩展而来:
#define timer_setup(timer, callback, flags) \ __init_timer((timer), (callback), (flags))#define __init_timer(_timer, _fn, _flags) \ init_timer_key((_timer), (_fn), (_flags), NULL, NULL)void init_timer_key(struct timer_list *timer, void (*func)(struct timer_list*), unsigned int flags, const char *name, struct lock_class_key *key)
usage: initialize a timer
Parameters:
struct timer_list *timerthe timer to be initialized
void (*func)(struct timer_list *)timer callback function
unsigned int flagstimer flags
const char *namename of the timer
struct lock_class_key *keylockdep class key of the fake lock used for tracking timer sync lock dependencies
Description:
init_timer_key() must be done to a timer prior to calling any of the other timer functions.实际上timer_setup(&timerdev.timer, timer_function, 0)就等于:
init_timer_key(&timerdev.timer, timer_function, 0, NULL, NULL);注意这里的timer_function必须使用timer_list*类型作为形参,不能用旧版的arg去传递。
从正点原子阿尔法第五十章示例代码修改而来的新代码
#include <linux/types.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/libata.h> /* 新版kernel不再支持ide.h */#include <linux/init.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/gpio.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/of.h>#include <linux/of_address.h>#include <linux/of_gpio.h>#include <asm/mach/map.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/semaphore.h>#include <linux/timer.h>
#define TIMER_CNT 1#define TIMER_NAME "timer"#define CLOSE_CMD (_IO(0xEF, 0x1))#define OPEN_CMD (_IO(0xEF, 0x2))#define SETPERIOD_CMD (_IO(0xEF, 0x3))#define LED_ON 1#define LED_OFF 0
/* Timer device struct */struct timer_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; struct device_node *nd; int led_gpio; int timeperiod; /* 定时器周期(ms) */ struct timer_list timer; /* 定时器 */ spinlock_t lock; /* 自旋锁 */};
struct timer_dev timerdev; /* 声明定时器实例 */
static int led_init(void){ int ret = 0;
/* 从设备树查找led节点 */ timerdev.nd = of_find_node_by_path("/gpioled"); if (timerdev.nd == NULL){ return -EINVAL; }
/* 从节点查找gpiopin */ timerdev.led_gpio = of_get_named_gpio(timerdev.nd, "led-gpio", 0); if (timerdev.led_gpio < 0){ printk("Unable get led gpio\r\n"); return -EINVAL; }
/* 申请GPIO */ gpio_request(timerdev.led_gpio, "led");
/* 上拉输出 */ ret = gpio_direction_output(timerdev.led_gpio, 1); if (ret < 0){ printk("cannot set gpio\r\n"); return ret; }
return 0;}
static int timer_open(struct inode *inode, struct file *filp){ int ret = 0; filp->private_data = &timerdev; /* 设置私有变量以保存结构体 */
timerdev.timeperiod = 1000; /* 默认定时器周期1000ms */
/* 初始化LED */ ret = led_init(); if (ret < 0){ printk("led initialize failed\r\n"); return ret; }
return 0;}
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){ struct timer_dev *dev = (struct timer_dev *)filp->private_data; /* 从fops获取保存的timerdev */ int timerperiod; unsigned long flags;
switch (cmd) { /* 关闭定时器 */ case CLOSE_CMD: del_timer_sync(&dev->timer); break;
/* 打开定时器 */ case OPEN_CMD: spin_lock_irqsave(&dev->lock, flags); /* 上锁 */ timerperiod = dev->timeperiod; /* 保护状态下修改timerperiod为默认定时器周期 */ spin_unlock_irqrestore(&dev->lock, flags); /* 解锁 */ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod)); /* 重启定时器 */ break;
/* 修改定时器周期 */ case SETPERIOD_CMD: spin_lock_irqsave(&dev->lock, flags); /* 上锁 */ dev->timeperiod = arg; /* 保护状态下从用户空间获取定时器周期并赋值 */ spin_unlock_irqrestore(&dev->lock, flags); /* 解锁 */ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg)); /* 重启定时器 */ break;
default: break; } return 0;}
static struct file_operations timer_fops = { .owner = THIS_MODULE, .open = timer_open, .unlocked_ioctl = timer_unlocked_ioctl,};
/* 定时器回调 */void timer_function(struct timer_list *t){ struct timer_dev *dev = from_timer(dev, t, timer); /* 从定时器实例获取父级结构体 */ static int sta = 1; int timerperiod; unsigned long flags;
/* 翻转LED */ sta = !sta; gpio_set_value(dev->led_gpio, sta);
spin_lock_irqsave(&dev->lock, flags); /* 上锁 */ timerperiod = dev->timeperiod; /* 保护状态下获取当前定时器周期 */ spin_unlock_irqrestore(&dev->lock, flags); /* 解锁 */ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod)); /* 重启定时器 */}
static int __init timer_init(void){ /* 初始化自旋锁 */ spin_lock_init(&timerdev.lock);
/* 注册设备 */ if (timerdev.major){ timerdev.devid = MKDEV(timerdev.major, 0); register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME); }else{ alloc_chrdev_region(&timerdev.devid, 0, 1, TIMER_NAME); timerdev.major = MAJOR(timerdev.devid); timerdev.minor = MINOR(timerdev.devid); } printk("Device reg ok, major = %d, minor = %d\r\n", timerdev.major, timerdev.minor);
/* 配置CDEV*/ timerdev.cdev.owner = THIS_MODULE; cdev_init(&timerdev.cdev, &timer_fops); cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);
/* 配置CLASS */ timerdev.class = class_create(TIMER_NAME); if (IS_ERR(timerdev.class)){ return PTR_ERR(timerdev.class); }
/* 配置DEVICE */ timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME); if (IS_ERR(timerdev.device)) { return PTR_ERR(timerdev.device); }
/* 初始化定时器 */ timer_setup(&timerdev.timer, timer_function, 0);
return 0;}
static void __exit timer_exit(void){ gpio_set_value(timerdev.led_gpio, 1); del_timer_sync(&timerdev.timer);
cdev_del(&timerdev.cdev); unregister_chrdev_region(timerdev.devid, TIMER_CNT);
device_destroy(timerdev.class, timerdev.devid); class_destroy(timerdev.class);}
module_init(timer_init);module_exit(timer_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("aki");