Table of Contents
1、tasklet 初始化
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
func //处理函数(耗时,不紧急的事情)
data //给处理函数传递的参数,一般传递的指针
2、tasklet 处理函数
//tasklet处理函数
static void btn_tasklet_func(unsigned long data)
{
int *pdata = (int *)data;
printk("%s: mydata = %#x\n", __func__, *pdata);
}
在某些场合,需要在底半部实现休眠的工作,那么使用tasklet就没法满足这个需求,因为tasklet工作在中断上下文中,不能引起休眠。所以这个时候需要使用工作队列来将复杂,耗时的工作退后执行。
3、tasklet的登记调度
tasklet_schedule(&mytasklet)
一旦完成登记,中断处理函数就可以立即返回,CPU就会在适当的时间去执行底半部tasklet的处理函数,完成耗时,不紧急的事情。tasklet还是工作在中断(只不过是另外的定时中断)(类似软中断)
参考代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
//定义按键硬件相关的数据结构
struct button_resource {
int irq; //中断号
char *name; //中断名称
};
//初始化按键信息
static struct button_resource btn_info[] = {
[0] = {
.irq = IRQ_EINT(0),
.name = "KEY_UP"
},
[1] = {
.irq = IRQ_EINT(1),
.name = "KEY_DOWN"
}
};
static int mydata = 0x55;
//tasklet处理函数
static void btn_tasklet_func(unsigned long data)
{
int *pdata = (int *)data;
printk("%s: mydata = %#x\n", __func__, *pdata);
}
//分配工作和延时工作
static struct work_struct btn_work;
static struct delayed_work btn_dwork;
//分配初始化tasklet
//名称为btn_tasklet,处理函数btn_tasklet_func,传递的参数是mydata地址
static DECLARE_TASKLET(btn_tasklet,
btn_tasklet_func, (unsigned long)&mydata);
//中断处理函数就是顶半部
static irqreturn_t button_isr(int irq, void *dev_id)
{
//1.登记底半部的tasklet
//cpu会在适当的时候执行对应的处理函数
tasklet_schedule(&btn_tasklet);
printk("%s\n", __func__);
return IRQ_HANDLED; //处理完毕
}
static int btn_init(void)
{
int i;
printk("register irq!\n");
for (i = 0; i < ARRAY_SIZE(btn_info); i++)
request_irq(btn_info[i].irq, button_isr,
IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
btn_info[i].name, &btn_info[i]);
return 0;
}
static void btn_exit(void)
{
int i;
printk("unregister irq!\n");
//注意注册中断传递的参数和释放中断传递的参数一定要一致!
for(i = 0; i < ARRAY_SIZE(btn_info); i++)
free_irq(btn_info[i].irq, &btn_info[i]);
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");