前几篇文章介绍了中断的原理、基本框架、上下部使用之工作队列,这篇文章介绍另一种方法—线程化中断 thread.
我使用的硬件是imx6q,原理都一样。原来的beep引脚是output模式,电路上把它改成了按键。
下面程序的文件名为interrupt_threaded.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
};
//上半部处理代码
static irqreturn_t beep_interrupt_top_handler(int irq, void *dev_id)
{
printk(KERN_INFO "%s\n", __func__);
return IRQ_WAKE_THREAD ; //只要返回 IRQ_WAKE_THREAD, 系统默认开始调用下半部的处理代码,所以一定要返回这个flag
}
//下半部处理代码
static irqreturn_t beep_interrupt_bottom_handler(int irq, void *dev_id)
{
//私有数据示例
struct pin_desc *dev_beep = dev_id;
printk(KERN_INFO "%s\n", __func__);
printk(KERN_INFO "%s : dev_beep->irq = %d\n", __func__, dev_beep->irq);
printk(KERN_INFO "%s : dev_beep->name = %s\n", __func__, dev_beep->name);
printk(KERN_INFO "%s : dev_beep->pin = %d\n", __func__, dev_beep->pin);
return IRQ_HANDLED;
}
//模块初始化代码,驱动安装时被调用
static int interrupt_threaded_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_threaded_irq(beep_desc.irq, beep_interrupt_top_handler, beep_interrupt_bottom_handler , IRQF_ONESHOT | IRQF_TRIGGER_FALLING , beep_desc.name , &beep_desc); //一定要添加IRQF_ONESHOT标志位,该标志位表述线程化中断执行结束以后再次打开本中断,开始执行时默认关闭本中断
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_threaded_exit(void)
{
printk("%s\n", __func__);
free_irq(beep_desc.irq, &beep_desc);
gpio_free(beep_desc.pin);
}
module_init(interrupt_threaded_init);
module_exit(interrupt_threaded_exit);
MODULE_AUTHOR("xiaolei");
MODULE_DESCRIPTION("interrupt threaded use");
MODULE_LICENSE("GPL");
线程化中断中要注意IRQF_ONESHOT标志位的使用,实践过程中,如果不添加此标志位,编译会报错。