一、中断介绍
所谓中断是指CPU在执行程序的过程中,出现了某些突发事件需要紧急处理,CPU必须暂时停止当前的工作,转去执行处理突发事件,处理完毕又返回原程序被中断的位置继续执行。
在ARM多核处理器中最常用的中断控制器是GIC,支持三类中断
1、SGI:Software Generated Interrupt,软件产生的中断,用于多核的核间通信
2、PPI:Private Peripheral Interrupt,某个CPU私有外设的中断,这类外设的中断只能发给绑定的那个CPU
3、SPI:Shared Peripheral Interrupt 共享外设中断,这类外设的中断可以路由到任何一个CPU
在proc/interrupts文件可以获得中断信息。
二 、中断API
看一下常用的中断相关的API函数
1、申请中断
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
功能:申请中断
参数:irq:中断号,这个中断号不是硬件手册上的中断号,而是linux的中断号。
handler:中断处理函数
flags:标志位 #define IRQF_TRIGGER_RISING0x00000001 上升沿触发
#define IRQF_TRIGGER_FALLING0x00000002 下降沿触发
#define IRQF_TRIGGER_HIGH0x00000004 高电平触发
#define IRQF_TRIGGER_LOW 0x00000008 低电平触发 IRQF_SHARED 共享标志,表示该中断可以被多个设备共享
name:中断的名称
dev:要传递给中断服务程序的私有数据,一般设置为这个设备的结构体或者为NULL
返回值:成功返回0,返回-EINVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经占用且不能共享。
2、释放中断
void free_irq(unsigned int irq, void *dev_id)
参数与申请中断中的参数一样。
三、中断上下部机制
1、tasklet
2、工作队列
struct work_struct {
atomic_long_t data; //传递给工作函数的参数
struct list_head entry;
work_func_t func; //工作函数,可以理解就是底半部函数。
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
工作队列对象结构体。使用工作队列需要首先定义一个工作队列对象
3、taskelet与工作队列的异同
实例,在迅为4412开发板上,实现按键中断
原理图如下
我们只拿一个来做例子,UART_RING,看他对应的GPIO口
它对应的是GPIOX1_1
得到中断号有一个API gpio_to_irq(gpio),三星定义了GPIOX1_1的gpio值为EXYNOS4_GPX1(1),这样就可以得到中断号了。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <mach/gpio-exynos4.h>
#define TASKLET;
int irq1;
#ifdef WORK_QUEUE
struct work_struct key_wq; //定义一个工作队列对象
#endif
void key_tasklet_func(unsigned long data);
#ifdef TASKLET
DECLARE_TASKLET(key_tasklet,key_tasklet_func,0); //绑定tasklet对象和函数
#endif
void key_tasklet_func(unsigned long data)
{
printk(KERN_INFO"key_tasklet_func enter...\n");
}
irqreturn_t key_handler(int irq, void *date)
{
printk(KERN_INFO"key1 enter...\n");
#ifdef TASKLET
tasklet_schedule(&key_tasklet); //执行tasklet底半部
#endif
#ifdef WORK_QUEUE
schedule_work(&key_wq); //执行工作队列底半部
#endif
return IRQ_HANDLED;
}
static int __init demo_key_init(void)
{
int ret = 0;
irq1 = gpio_to_irq(EXYNOS4_GPX1(1));
ret = request_irq(irq1,key_handler,IRQF_TRIGGER_FALLING,"KEY222",NULL);
if(ret < 0){
printk(KERN_INFO"request_irq fail...%s,%d,ret:%d\n",__func__,__LINE__,ret);
return -EINVAL;
printk(KERN_INFO"%s,%d\n",__func__,__LINE__);
#ifdef WORK_QUEUE
INIT_WORK(&key_wq,key_tasklet_func); //初始化一个工作队列
#endif
return 0;
}
static void __exit demo_key_exit(void)
{
free_irq(irq1,NULL);
printk(KERN_INFO"%s,%d\n",__func__,__LINE__);
}
module_init(demo_key_init);
module_exit(demo_key_exit);
MODULE_LICENSE("GPL");