Linux 内核定时器实验

该代码实现了一个Linux内核驱动,用于控制GPIO引脚上的LED灯,使用定时器进行周期性开关。驱动程序包含了设备打开、关闭、写入和ioctl接口,ioctl接口用于设置定时器周期、打开和关闭定时器。定时器回调函数负责切换LED的状态。此外,还有一个用户空间的应用程序,通过ioctl调用来操作LED和定时器。
摘要由CSDN通过智能技术生成

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、内核定时器不是周期性运行的,超时以后会自动关闭,如果想要实现周期性定时,需要在定时处理函数中重新开启定时器。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值