一、中断的注册流程
中断的操作流程主要分为两部分:中断资源的注册、中断资源的注销。
中断的操作一般与硬件有联系,特别是gpio,这里也gpio为例。
(1)从设备树中的结点获得相应的gpio的参数。
/* 设备树中获取按键client端的结点 */
Input.nd = of_find_node_by_path(BUTTONTREEPATH);
if(Input.nd == NULL)
{
printk("button nod not find\r\n");
return -EINVAL;
}
Input.gpio[i].gpio = of_get_named_gpio(Input.nd, BUTTONGPIONAME, i);
if(Input.gpio[i].gpio < 0)
{
printk("can not get goio%d\r\n",i);
Input.gpio[i].gpio = -1;
}else
{
printk("gpio:%d\r\n",Input.gpio[i].gpio);
}
(2)设置gpio的相关配置。
gpio_request(Input.gpio[i].gpio, Input.gpio[i].name);
gpio_direction_input(Input.gpio[i].gpio);
(3)对应gpio的线管中断参数设置。
Input.gpio[i].irqnum = gpio_to_irq(Input.gpio[i].gpio);
/* 中断函数相关设置,所有同类型按键调用同一个中断函数 */
Input.gpio[i].handler = key0_handler;
Input.gpio[i].event = KEY_0; /* 事件的类型 */
printk("get gpio%d irq\r\n" ,Input.gpio[i].gpio);
(4)中断号申请并使能。
ret = request_irq(Input.gpio[i].irqnum, Input.gpio[i].handler,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
Input.gpio[i].name, &Input.gpio[i]);
if(ret < 0)
{
printk("irq %s request failed!\r\n", Input.gpio[i].name);
return -EFAULT;
}else
{
printk("request gpio%d irq\r\n" ,Input.gpio[i].gpio);
}
二、中断的常用函数和关节结构体
①、使能中断,初始化相应的寄存器。
②、注册中断服务函数,也就是向 irqTable 数组的指定标号处写入中断服务函数
②、中断发生以后进入 IRQ 中断服务函数,在 IRQ 中断服务函数在数组 irqTable 里面查找
具体的中断处理函数,找到以后执行相应的中断处理函数。
(1)中断号
每个中断都有一个中断号,通过中断号即可区分不同的中断
(2)request_irq 函数
在 Linux 内核中要想使用某个中断是需要申请的,request_irq 函数用于申请中断,request_irq 函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函 数。request_irq 函数会激活(使能)中断,所以不需要我们手动去使能中断,request_irq 函数原型 如下:
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev)
函数参数和返回值含义如下:
irq:要申请中断的中断号。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志,这里我们介绍几个常用的中断标志,这些标志可以通过“|”来实现多种组合
标志 | 描述 |
---|---|
IRQF_SHARED
|
多个设备共享一个中断线,共享的所有中断都必须指定此标志。
如果使用共享中断的话,request_irq 函数的 dev 参数就是唯一
区分他们的标志。
|
IRQF_ONESHOT
|
单次中断,中断执行一次就结束。
|
IRQF_TRIGGER_NONE
|
无触发。
|
IRQF_TRIGGER_RISING
|
上升沿触发。
|
IRQF_TRIGGER_FALLING
|
下降沿触发。
|
IRQF_TRIGGER_HIGH
|
高电平触发。
|
IRQF_TRIGGER_LOW
|
低电平触发。
|
10 enum irqreturn {
11 IRQ_NONE = (0 << 0),
12 IRQ_HANDLED = (1 << 0), //中断处理完成
13 IRQ_WAKE_THREAD = (1 << 1), //唤醒中断的下半部分的时候使用
14 };
15
16 typedef enum irqreturn irqreturn_t;
三、中断的设备树操作
如果使用设备树的话就需要在设备树中设置好中断属性信息,Linux 内核通过读取设备树
中 的 中断 属性 信息 来配 置中 断。 对于 中断 控制 器 而言 ,设 备树 绑定 信息 参考 文档
Documentation/devicetree/bindings/arm/gic.txt。打开 imx6ull.dtsi 文件,其中的 intc 节点就是
I.MX6ULL 的中断控制器节点,节点内容如下所示:
1 intc: interrupt-controller@00a01000 {
2 compatible = "arm,cortex-a7-gic";
3 #interrupt-cells = <3>;
4 interrupt-controller;
5 reg = <0x00a01000 0x1000>,
6 <0x00a02000 0x100>;
7 };
第 2 行,compatible 属性值为“arm,cortex-a7-gic”在 Linux 内核源码中搜索“arm,cortex-a7-
gic”即可找到 GIC 中断控制器驱动文件。
第 3 行,#interrupt-cells 和#address-cells、#size-cells 一样。表示此中断控制器下设备的 cells
大小,对于设备而言,会使用 interrupts 属性描述中断信息,#interrupt-cells 描述了 interrupts 属
性的 cells 大小,也就是一条信息有几个 cells。每个 cells 都是 32 位整形值,对于 ARM 处理的
GIC 来说,一共有 3 个 cells,这三个 cells 的含义如下:
第一个 cells:中断类型,0 表示 SPI 中断,1 表示 PPI 中断。
第二个 cells:中断号,对于 SPI 中断来说中断号的范围为 0~987,对于 PPI 中断来说中断
号的范围为 0~15。
第三个 cells:标志,bit[3:0]表示中断触发类型,为 1 的时候表示上升沿触发,为 2 的时候
表示下降沿触发,为 4 的时候表示高电平触发,为 8 的时候表示低电平触发。bit[15:8]为 PPI 中
断的 CPU 掩码。
第 4 行,interrupt-controller 节点为空,表示当前节点是中断控制器。
1 gpio5: gpio@020ac000 {
2 compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
3 reg = <0x020ac000 0x4000>;
4 interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
5 <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
6 gpio-controller;
7 #gpio-cells = <2>;
8 interrupt-controller;
9 #interrupt-cells = <2>;
10 };
①、#interrupt-cells,指定中断源的信息 cells 个数。
②、interrupt-controller,表示当前节点为中断控制器。
③、interrupts,指定中断号,触发方式等。
④、interrupt-parent,指定父中断,也就是中断控制器。
四、中断的上、下端操作
内容比较多,这里不在说