前几篇文章介绍了中断的原理、基本框架、上下部使用之工作队列、线程化中断,这篇文章介绍另一种方法—线程化中断 tasklet。 此方法运行在中断上下文,不可休眠,延时。
我使用的硬件是imx6q,原理都一样。原来的beep引脚是output模式,电路上把它改成了按键。
下面程序的文件名为interrupt_tasklet.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/irq.h>
#define IMX_GPIO_NR(bank, nr) (((bank) - 1) * 32 + (nr)) //平台相关
#define CYNO_GPIO_BEEP_NUM IMX_GPIO_NR(6,10) //本程序使用的gpio口
//定义gpio引脚的结构体
static struct pin_desc{
int irq;
unsigned char *name;
unsigned int pin;
};
//实例化一个具体的引脚
static struct pin_desc beep_desc = {
0,
"beep_num",
CYNO_GPIO_BEEP_NUM
};
//生命tasklet触发函数,也就是中断下半部函数
void beep_tasklet_func(unsigned long data);
//生命一个tasklet,名字为beep_tasklet,并且关联触发函数
DECLARE_TASKLET(beep_tasklet, beep_tasklet_func, 0);
int flag = 0;
//中断下半部函数实现
void beep_tasklet_func(unsigned long data){
flag++;
printk(KERN_INFO "-------\n");
if(flag >= 60){
printk(KERN_INFO "%s : flag = %d\n", __func__, flag);
flag = 0;
}
}
/*
无论什么时候调用tasklet_schedule
一定是上半部代码执行结束,再执行下半部代码
*/
static irqreturn_t beep_interrupt_handler(int irq, void *dev_id)
{
printk(KERN_INFO "%s : top tasklet func\n", __func__);
tasklet_schedule(&beep_tasklet); //触发下半部代码
printk(KERN_INFO "%s : bottom tasklet func\n", __func__);
return IRQ_HANDLED;
}
//模块加载执行
static int interrupt_tasklet_init(void)
{
int ret;
printk(KERN_INFO "%s\n", __func__);
//申请gpio
if(gpio_request(beep_desc.pin ,beep_desc.name)){
printk(KERN_ERR "%s : request gpio %d error\n", __func__, beep_desc.pin);
goto err_gpio_request;
}
//设置gpio方向为输入
gpio_direction_input(beep_desc.pin);
//动态获取irq端口号
beep_desc.irq = gpio_to_irq(beep_desc.pin);
printk(KERN_INFO "%s : the irq num is %d\n", __func__, beep_desc.irq);
//申请中断,并设置触发方式为下降沿,设置中断处理函数(上半部)
ret = request_irq(beep_desc.irq, beep_interrupt_handler , IRQF_TRIGGER_FALLING , beep_desc.name , &beep_desc);
if(ret){
printk(KERN_ERR "%s : request_irq is error\n", __func__);
goto err_request_irq;
}
printk("%s : init end\n", __func__);
return 0;
//处理错误
err_request_irq:
free_irq(beep_desc.irq, &beep_desc);
err_gpio_request:
gpio_free(beep_desc.pin);
return -1;
}
//驱动卸载执行
static void interrupt_tasklet_exit(void)
{
printk("%s\n", __func__);
free_irq(beep_desc.irq, &beep_desc);
gpio_free(beep_desc.pin);
}
module_init(interrupt_tasklet_init);
module_exit(interrupt_tasklet_exit);
MODULE_AUTHOR("xiaolei");
MODULE_DESCRIPTION("interrupt tasklet use");
MODULE_LICENSE("GPL");
tasklet运行在终端上下文,不可延时、睡眠。 我个人的理解是如果中断出发后处理事情的时间比较短可以使用此方法。 如果时间比较长,建议使用线程化中断或者work_queue。