内核驱动:中断机制
A9处理器:EXYNOS4412
中断:硬件产生(总是需要硬件电路上的一根中断线):USB线是有VCC、GND、D+、D-四根线组成,所以是不能产生中断的;
中断产生的一般过程:中断源产生中断------>中断控制器(根据中断优先级)------>ARM处理器
对于4412处理器来说,搞清中断源和中断号的对应关系是一个很复杂的工作具体可参考arch/arm/mach-exynos/include/mach/irqs.h头文件
ARM平台查找中断号:
1.芯片内部外设:利用设备的名字在irqs.h中查找中断号;
2.芯片外部外设:设备的中断连接到4412的GPIO引脚,找到GPIO引脚的GPIO号,然后调用函数gpio_to_irq();
中断函数的注册:
1.需要包含头文件<mach/irqs.h>,如果要找4412内部设备的中断号,只能看文件<mach/irqs.h>
=======================================================
linux的中断处理机制
GPIO产生中断,将中断给GPIO控制器,GPIO控制器再将中断交给GIC处理,最后GIC交与cpu做处理:如下图:
mask:设备上级组织(GIC或者GPIO控制器)一般在中断到达的过程中屏蔽中断;同时,CPU也可以通过控制CPSR寄存器来屏蔽来自GIC的所有中断;
中断处理机制
当中断产生后,内核会保护当前进程的执行现场,并进入异常处理,通过异常像量表找到产生中断的中断号,并由中断号,在内核中找到中断号为100的irq_desc:主要用于描述中断信息,并执行中断处理函数(需要用户有注册行为)。用户在处理中断的过程中,首先要先写好中断处理函数,封装到iqaction并由iqaction想内核进行注册,从而是handler函数有效;
ps:
1.linux内核在启动时分配了一个irq_desc的数组,数组中共有NR_IRQS个成员。每个irq_desc中记录对应中断的各类信息,比如中断的处理函数,中断的发生次数等。irq_desc 结构体定义在<linux/irqdesc.h中>
2.irqaction定义在<linux/interrupt.h>每个irqaction用于封装一个中断处理函数。结构体由驱动人员负责分配。irqaction中包含中断号,中断处理函数指针,中断的执行标志,中断名等。
3.中断处理函数的返回值为 irq_handler_t类型;定义在<linux/interrupt.h>,如下:
irqreturn_t (*irq_handler_t)(int, void *);
中断处理函数。由驱动负责实现,记录在irqaction中。
irqreturn_t只有两个值,IRQ_NONE/IRQ_HANDLED。如果中断不是由本设备引起的,则返回IRQ_NONE,否则返回IRQ_HANDLED。
函数参数irq为中断号,void *为传递给中断处理函数的参数,对应irqaction->dev_id。
中断处理函数的编写与注册
驱动人员在设计中断处理函数时,要遵循的要求是:
(1)可嵌套不可重入
(2)不能睡眠
(3)如果硬件有中断的状态寄存器,软件要负责清除中断的标志位。一般来说,如果不清除标志位,设备无法再次产生中断
(4)中断处理函数的注册和注销
0.前提
需要包含的头文件
#include <linux/interrupt.h>
#include <mach/irqs.h> //片内外设
#include <linux/gpio.h> //片外外设
#include <mach/gpio.h>
1.确定中断号
正如刚才如上所述:如果是外部IO中断,那么我们可以通过GPIO号获取中断号:
eg:#define KEY_IRQ gpio_to_irq(gpio号);
2.中断处理函数编写
中断处理函数的格式:
eg:
//参数1:int irq :中断号
//参数2:void * dev_id :为传递给中断处理函数的参数
//返回值:IRQ_NONE:中断不是由本设备引起的
IRQ_HANDLED:中断是由本设备引起的
static irqreturn_t key_service(int irq, void *dev_id)
{
//根据硬件要求完成相应工作
...
return IRQ_HANDLED 或 IRQ_NONE;
}
3.注册中断处理函数(必须检查返回值)
u32 flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;//定义中断的标志:下降沿/上升沿/低电平/高电平触发中断
int ret;
ret = request_irq( KEY_IRQ, /* 中断号 */
key_service, /* 中断处理函数 */
flags, /* 中断的标志 */
"xxx", /* 中断处理函数的名字 */
dev_id);/* dev_id为传给中断处理函数的参数,一般会设置为私有结构体的指针,非共享>中断可以为NULL */
if (ret) {
printk("Cannot register interrupt handler\n");
return -1;
}
4.注销中断处理函数
free_irq(irq, dev_id); //参数为中断号和dev_id。 dev_id一定要和request_irq中的最后一个参数一致
5.屏蔽中断
1.可以人为关闭(mask)/打开某个中断:
disable_irq(int irq);
enable_irq(int irq);
这两个函数是可以嵌套的,也就是说,如果对一个中断disable了3次,需要调用enable函数3次才能打开对该中断的屏蔽;
2.还可以屏蔽本cpu的中断:
local_irq_disable();
local_irq_enable();