前两篇文章介绍了中断的原理和基本框架,但是实际使用过程中会用到中断上下文的编程方法,这里介绍work_queue.
我使用的硬件是imx6q,原理都一样。原来的beep引脚是output模式,电路上把它改成了按键。
下面程序的文件名为interrupt_work_queue.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 <linux/workqueue.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引脚的结构体
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
};
//定义一个工作队列
struct work_struct beep_wq;
//下半部执行代码,work_queue是运行在线程的,是可以休眠和延时的
void beep_wq_func(struct work_struct *work)
{
printk(KERN_INFO "%s \n", __func__);
}
//上半部执行代码
static irqreturn_t beep_interrupt_handler(int irq, void *dev_id)
{
printk(KERN_INFO "%s : start\n", __func__);
schedule_work(&beep_wq); //呼叫中断下半部函数,但是此代码无论添加在上半部任何地方,都是上半部代码执行结束,下半部代码才开始执行
printk(KERN_INFO "%S : end\n", __func__);
return IRQ_HANDLED;
}
驱动初始化,当驱动加载或者insmod interrupt_work_queue.ko(此程序文件名)时被执行
static int interrupt_work_queue_init(void)
{
int ret;
printk(KERN_INFO "%s : init start\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);
//动态获取中断号
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;
}
//初始化工作队列,并关联响应函数
INIT_WORK(&beep_wq, beep_wq_func);
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;
}
//驱动卸载执行此函数,rmmod interrupt_work_queue
static void interrupt_work_queue_exit(void)
{
printk("%s\n", __func__);
free_irq(beep_desc.irq, &beep_desc);
gpio_free(beep_desc.pin);
}
module_init(interrupt_work_queue_init);
module_exit(interrupt_work_queue_exit);
MODULE_AUTHOR("xiaolei");
MODULE_DESCRIPTION("interrupt work_queue use");
MODULE_LICENSE("GPL");
工作队列的方法适用很多地方,比如我公司的项目中,触摸屏的中断响应使用的就是这种方法。下篇文章介绍运行在进程上下文的另一种方法 — 线程化中断。