Linux内核定时器驱动程序
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/ioctl.h>
#define TIMERLED_CNT 1
#define TIMERLED_NAME "timerled"
#define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */
#define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */
#define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令 */
struct timerled_dev {
int major;
int minor;
dev_t devide;//设备号
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node * bl_nd1;/*设备节点*/
int led_timer;/*属性编号*/
struct timer_list timer;/*定义一个定时器*/
unsigned long timerperiod;/*定义一个周期*/
};
struct timerled_dev timerled;
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int timerled_open(struct inode *inode, struct file *filp)
{
filp->private_data = &timerled; /* 设置私有数据 */
timerled.timerperiod=1000;
printk("chrdevbase open!\r\n");
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t timerled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
struct timerled_dev *dev=(struct timerled_dev * )filp->private_data;
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0]; /* 获取状态值 */
if(ledstat == 0) {
gpio_set_value(dev->led_timer,0); /* 打开 LED 灯 */
} else if(ledstat == 1) {
gpio_set_value(dev->led_timer,1); /* 关闭 LED 灯 */
}
return 0;
}
/*
* @description : ioctl函数,
* @param - filp : 要打开的设备文件(文件描述符)
* @param - cmd : 应用程序发送过来的命令
* @param - arg : 参数
* @return : 0 成功;其他 失败
*/
static long timerled_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct timerled_dev *dev = (struct timerled_dev *)filp->private_data;
int timerperiod;
unsigned long flags;
switch (cmd) {
case CLOSE_CMD: /* 关闭定时器 */
del_timer_sync(&dev->timer);
printk("\n");
printk("CLOSE_CMD\n");
break;
case OPEN_CMD: /* 打开定时器 */
printk("\n");
printk("OPEN_CMD\n");
timerperiod = dev->timerperiod;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
break;
case SETPERIOD_CMD: /* 设置定时器周期 */
printk("\n");
printk("SETPERIOD_CMD\n");
dev->timerperiod = arg;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
break;
default:
break;
}
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int timerled_release(struct inode *inode, struct file *filp)
{
struct timerled_dev *dev=(struct timerled_dev * )filp->private_data;
printk("timerled release!\r\n");
return 0;
}
static struct file_operations timerled_ops={
.owner=THIS_MODULE,
.open=timerled_open,
.release=timerled_release,
.write=timerled_write,
.unlocked_ioctl = timerled_unlocked_ioctl,
};
/*定时器回调函数*/
void timer_function(unsigned long t)
{
struct timerled_dev *dev=(struct timerled_dev * )t;
static int sta=0;
//dev->timerperiod=1000;
unsigned long period;
period = dev->timerperiod;
sta=!sta;
gpio_set_value(timerled.led_timer,sta);
mod_timer(&timerled.timer,jiffies+msecs_to_jiffies(period));
printk("jiffies=%#x\n",jiffies);
}
static int __init timerled_init(void)
{
int ret=0;
/*获取设备节点*/
timerled.bl_nd1=of_find_node_by_path("/gpioled");
if(timerled.bl_nd1==NULL)
{
printk("of_find_node_by_path failed\r\n");
return -EINVAL;
}
/*获取设备cd-gpio属性的第一个GPIO编号*/
timerled.led_timer=of_get_named_gpio(timerled.bl_nd1,"cd-gpio",0);
if(timerled.led_timer<0)
{
printk("of_get_named_gpio failed\r\n");
return -EINVAL;
}
/*申请一个GPIO管脚*/
ret=gpio_request(timerled.led_timer,"cd-gpio");
if(ret==0){
printk("gpio_requests success\n");
}
/*将GPIO设置为输出,默认输出高电平*/
ret=gpio_direction_output(timerled.led_timer,1);
if(ret<0)
{
printk("gpio_direction_output failed\n");
}
/*注册设备号*/
timerled.major=0;
if(timerled.major){
timerled.devide=MKDEV(timerled.major,0);
register_chrdev_region(timerled.devide,TIMERLED_CNT,TIMERLED_NAME);
}else{
ret=alloc_chrdev_region(&timerled.devide, 0, TIMERLED_CNT, TIMERLED_NAME); /* 申请设备号 */
if(ret<0)
{
goto fail_devide;
}
timerled.major = MAJOR(timerled.devide); /* 获取分配号的主设备号 */
timerled.minor = MINOR(timerled.devide); /* 获取分配号的次设备号 */
printk("major=%d\n",timerled.major);
printk("minor=%d\n",timerled.minor);
}
/*初始化cdev*/
timerled.cdev.owner=THIS_MODULE;
cdev_init(&timerled.cdev, &timerled_ops);
/* 3、添加一个cdev */
ret= cdev_add(&timerled.cdev, timerled.devide, TIMERLED_CNT);
if(ret<0)
{
goto fail_cdev;
}
//创建设备节点
/* 4、创建类 */
timerled.class = class_create(THIS_MODULE, TIMERLED_NAME);
if (IS_ERR(timerled.class)) {
ret= PTR_ERR(timerled.class);
goto fail_class;
}
/* 5、创建设备 */
timerled.device = device_create(timerled.class, NULL, timerled.devide, NULL, TIMERLED_NAME);
if (IS_ERR(timerled.device)) {
ret=PTR_ERR(timerled.device);
goto fail_device;
}
/*初始化定时器*/
init_timer(&timerled.timer);
timerled.timer.function=timer_function;
//timerled.timer.expires=jiffies+msecs_to_jiffies(1000);
timerled.timer.data=(unsigned long)&timerled;
//timerled.timerperiod=5000;
//add_timer(&timerled.timer);/*启动定时器*/
return 0;
fail_device:
class_destroy(timerled.class);
fail_class:
cdev_del(&timerled.cdev);
fail_cdev:
unregister_chrdev_region(timerled.devide, TIMERLED_CNT);
fail_devide:
return ret;
}
static void __exit timerled_exit(void)
{
del_timer(&timerled.timer);
gpio_set_value(timerled.led_timer,1);
device_destroy(timerled.class,timerled.devide);
class_destroy(timerled.class);
cdev_del(&timerled.cdev);
gpio_free(timerled.led_timer);
unregister_chrdev_region(timerled.devide, TIMERLED_CNT);
printk("exit\n");
printk("exit\n");
}
/*模块的加载与卸载*/
module_init(timerled_init);
module_exit(timerled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liuchuanqiang");
定时器应用程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
/*
argc:应用程序参数个数
argv[]:具体的参数内容,字符串形式,这个函数中的第一个参数内容./timerledAPP;
第二个参数内容filename;
执行文件格式:
./timerledAPP /dev/timerled
*/
#define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */
#define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */
#define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令 */
int main(int argc,char *argv[])
{
int ret=0;
int fd=0;
char *filename;
int cmd;
int arg;
unsigned char str[100];
filename=argv[1];//一个字符串的声明
printf("filename=%s\n",filename);
if(argc!=2)
{
printf("error\n");
return -1;
}
//打开文件
fd=open(filename, O_RDWR);
//判断有没有打开成功
if(fd<0)
{
printf("open failed\r\n");
return -1;
}
printf("please input cmd:");
scanf("%d",&cmd);
if (ret != 1) { /* 参数输入错误 */
gets(str); /* 防止卡死 */
}
if(cmd == 1){ /* 关闭LED灯 */
cmd = CLOSE_CMD;
ioctl(fd, cmd, arg);
}
else if(cmd == 2){ /* 打开LED灯 */
cmd = OPEN_CMD;
ioctl(fd, cmd, arg);
}
else if(cmd == 3) {
cmd = SETPERIOD_CMD; /* 设置周期值 */
printf("Input Timer Period:");
ret = scanf("%d", &arg);
if (ret != 1) { /* 参数输入错误 */
gets(str); /* 防止卡死 */
}
ioctl(fd, cmd, arg); /* 控制定时器的打开和关闭 */
}
//关闭文件
ret=close(fd);
if(ret<0)
{
printf("close failed\r\n");
return -1;
}
return 0;
}
程序说明:
1、定时器的使用步骤:
init_timer(&timerled.timer);/*初始化定时器*/
timerled.timer.function=timer_function;/*定时器回调函数*/
timerled.timer.expires=jiffies+msecs_to_jiffies(1000);/*定时器超时时间*/
timerled.timer.data=(unsigned long)&timerled;/* 要传递给 function 函数的参数 */
add_timer(&timerled.timer);/*启动定时器*/
2、本实验通过应用程序设置定时器的周期,将LED进行周期性的亮灭。
3、static long timerled_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)通过timerled_unlocked_ioctl函数对内核驱动读写。该函数对应应用程序的ioctl函数。
ioctl命令是自己定义的,但是要符合Linux规则:
#define _IO(type, nr) 没有参数的命令
#define _IOR(type, nr,size) 从驱动读取数据
#define _IOW(type, nr,size) 从驱动写入数据
#define _IOWR(type, nr,size)双向数据传输
Type是幻数,nr是序号,size是大小
4、del_timer(&timerled.timer);删除一个定时器
int del_timer(struct timer_list * timer)
函数参数和返回值含义如下:
timer:要删除的定时器。
返回值:0,定时器还没被激活;1,定时器已经激活。
5、mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));函数用于修改定时值,如果定时器还没有激活的话,mod_timer 函数会激活定时器。
int mod_timer(struct timer_list *timer, unsigned long expires)
函数参数和返回值含义如下:
timer:要修改超时时间(定时值)的定时器。
expires:修改后的超时时间。
返回值:0,调用 mod_timer 函数前定时器未被激活;1,调用 mod_timer 函数前定时器已
被激活。
6、ioctl函数是用来控制定时器(打开、关闭、设置定时周期)使用的;回调函数是定时器到时时执行程序的。
7、内核定时器不是周期性运行的,超时以后会自动关闭,如果想要实现周期性定时,需要在定时处理函数中重新开启定时器。