linux驱动中断框架

从零开始触发中断到cpu执行

1. 按下中断,cpu找到异常向量入口

在汇编文件找到异常向量入口: arch\arm\kernel\entry-armv.S

   .section .vectors, "ax", %progbits
.L__vectors_start:
    W(b)    vector_rst
    W(b)    vector_und
    W(ldr)  pc, .L__vectors_start + 0x1000
    W(b)    vector_pabt
    W(b)    vector_dabt
    W(b)    vector_addrexcptn
    W(b)    vector_irq
    W(b)    vector_fiq

比如我们的按键中断就是—> vector_irq

2. 根据异常向量找到__irq_usr或__irq_svc

根据SPSR寄存器的值,判断被中断时CPU是处于USR状态还是SVC状态,然后调用下面的__irq_usr或__irq_svc

/*
 * Interrupt dispatcher
 */
    vector_stub irq, IRQ_MODE, 4   // 相当于 vector_irq: ..., 
                                   // 它会根据SPSR寄存器的值,
                                   // 判断被中断时CPU是处于USR状态还是SVC状态, 
                                   // 然后调用下面的__irq_usr或__irq_svc

    .long   __irq_usr               @  0  (USR_26 / USR_32)
    .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
    .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
    .long   __irq_svc               @  3  (SVC_26 / SVC_32)
    .long   __irq_invalid           @  4
    .long   __irq_invalid           @  5
    .long   __irq_invalid           @  6
    .long   __irq_invalid           @  7
    .long   __irq_invalid           @  8
    .long   __irq_invalid           @  9
    .long   __irq_invalid           @  a
    .long   __irq_invalid           @  b
    .long   __irq_invalid           @  c
    .long   __irq_invalid           @  d
    .long   __irq_invalid           @  e
    .long   __irq_invalid           @  f

__irq_usr/__irq_svc: 这2个函数的处理过程类似
保存现场
调用 irq_handler
恢复现场

3.用irq_handler调用handle_arch_irq

在handle_arch_irq里根据不同的芯片找到它的中断调用函数

对于S3C2440, 找到s3c24xx_handle_irq 是用于处理中断的第一个C语言入口函数

4.执行s3c24xx_handle_irq函数

s3c24xx_handle_irq 找到我们的中断寄存器(interrupt control)
得到hwirq(比如0/1/2/3)硬件中断号,根据硬件中断号的到唯一的virtirq(虚拟中断号)图中有
在这里插入图片描述
根据虚拟中断号进入irq_dcsc数组找到触发中断的一项
在这里插入图片描述

5.进入irq_dcsc处理中断

调用虚拟中断项里------->handle_irq
handle_irq---->*action(链表)
*action(链表)----->handler(具体处理函数)
调用虚拟中断项里---->irq_data
irq_data---->irq_data.chip(清理中断)
在这里插入图片描述
所以使用这种强大的框架我们就只用编写handler(具体处理函数)

6.当一个中段号有多个设备时候的处理

假设我们的0号中断连接了 net芯片 和cam芯片
当有一个芯片触发中断的时候 也会调用 s3c24xx_handle_irq 用于处理中断的第一个C语言入口函数
判断出是0号中断产生了触发
在这里插入图片描述
根据硬件中断号找到虚拟中断号,进入irq_dcsc数组
调用数组里的handle_irq
handle_irq 分别调用 cam的handle net的handle
看是哪一个触发的中断并进行处理

7.当一个硬件中段号有多个中断引脚触发时

比如图中的在硬件中断号为4的地方,同时包括了引脚4-7
在这里插入图片描述
当在调用调用 s3c24xx_handle_irq时候 会去寄存器里查看是哪个引脚被触发,从而算出虚拟中断号
在这里插入图片描述
查看寄存器
在这里插入图片描述

对于上面的硬件中断如何找到虚拟中断

以前中断号(virq)跟硬件密切相关,现在的趋势是中断号跟硬件无关, 仅仅是一个标号而已

以前

对于每一个硬件中断(hwirq)都预先确定它的中断号(virq)
这些中断号一般都写在一个头文件里, 比如arch\arm\mach-s3c24xx\include\mach\irqs.h

使用时

a. 执行 request_irq(virq, my_handler) :
内核根据virq可以知道对应的硬件中断, 然后去设置、使能中断等
b. 发生硬件中断时,
内核读取硬件信息, 确定hwirq, 反算出virq,
然后调用 irq_desc[virq].handle_irq, 最终会用到my_handler

怎么根据hwirq计算出virq?

硬件上有多个intc(中断控制器),
对于同一个hwirq数值, 会对应不同的virq
所以在讲hwirq时,应该强调"是哪一个intc的hwirq",
在描述hwirq转换为virq时, 引入一个概念: irq_domain, 域, 在这个域里hwirq转换为某一个virq
在这里插入图片描述

现在

当中断控制器越来越多、当中断越来越多,上述方法(virq和hwirq固定绑定)有缺陷:
a. 增加工作量, 你需要给每一个中断确定它的中断号, 写出对应的宏, 可能有成百上千个
b. 你要确保每一个硬件中断对应的中断号互不重复

有什么方法改进?

a. hwirq跟virq之间不再绑定
b. 要使用某个hwirq时,
先在irq_desc数组中找到一个空闲项, 它的位置就是virq
再在irq_desc[virq]中放置处理函数

新中断体系中, 怎么使用中断:

a.以前是request_irq发起,
现在是先在设备树文件中声明想使用哪一个中断(哪一个中断控制器下的哪一个中断)

b. 内核解析设备树时,
会根据"中断控制器"确定irq_domain,
根据"哪一个中断"确定hwirq,
然后在irq_desc数组中找出一个空闲项, 它的位置就是virq
并且把virq和hwirq的关系保存在irq_domain中: irq_domain.linear_revmap[hwirq] = virq;

c. 驱动程序 request_irq(virq, my_handler)

d. 发生硬件中断时,
内核读取硬件信息, 确定hwirq, 确定 virq = irq_domain.linear_revmap[hwirq];
然后调用 irq_desc[virq].handle_irq, 最终会用到my_handler

举例

假设要使用子中断控制器(subintc)的n号中断, 它发生时会导致父中断控制器(intc)的m号中断:
a. 设备树表明要使用
subintc表示要使用
b. 解析设备树时,
会为找到空闲项 irq_desc[virq’], sub irq_domain.linear_revmap[n] = virq’;

会为 找到空闲项 irq_desc[virq], irq_domain.linear_revmap[m] = virq;
并且设置它的handle_irq为某个分析函数demux_func

c. 驱动程序 request_irq(virq’, my_handler)

d. 发生硬件中断时,
内核读取intc硬件信息, 确定hwirq = m, 确定 virq = irq_domain.linear_revmap[m];
然后调用 irq_desc[m].handle_irq, 即demux_func

e. demux_func:
读取sub intc硬件信息, 确定hwirq = n, 确定 virq’ = sub irq_domain.linear_revmap[n];
然后调用 irq_desc[n].handle_irq, 即my_handler

查看中断框架

在中断框架中系统帮我们做了,分为6点
1,CPU 自动进入“异常模式”。调用“异常处理函数”。
2,在“异常处理函数”中如跳到“b vector_irq + stubs_offset”。
3,调用到列表中的“__irq_usr”后,可以具体分析这个“__irq_usr”中处理的情况
4,“asm_do_IRQ”调用“irq_desc[IRQ 中断下标]以中断为下标取出里面的一项“handle_irq”
5,“handle_edge_irq”做的事:
①,desc->chip->ack() 清中断
②,handle_IRQ_event() 处理中断
③,取出“action”链表中的成员,执行“action->handler”。
6,核心在“irq_desc” 结构数组:
分析“irq_desc[]”结构数组:里面有“action 链表”,有“chip”等。

我们自己的程序在 irqrequest 我们用request_irq函数告诉内核处理函数是什么

在这里插入图片描述
图为linux内核中断体系架构 内核里面有数组irq_desc[0] (中断号为下标)
handle_irq 执行 a清中断 b调用action链表里面的各种处理函数
chip 指向底成硬件的访问函数 用来屏蔽中断,使能中断,响应中断
action 指向我们注册的中断处理函数

所以我们在链表里把自己的函数填入irqaction结构

使用request_irq 先看看源码的定义
在这里插入图片描述

传入参数:

irq:中断号。
handler:处理函数。
irqflags:上升沿触发,下降沿触发,边沿触发等。指定了快速中断或中断共享等中断处理属性.
*devname:中断名字。通常是设备驱动程序的名称。改值用在 /proc/interrupt 系统 (虚拟)
文件上,或内核发生中断错误时使用。
第 5 个参数 dev_id 可作为共享中断时的中断区别参数,也可以用来指定中断服务函数需
要参考的数据地址。

内部实现

1 setup_irq (irq,action)
a 分配了irqaction结构

   b  找到desc[0] 中断数组项,看是否为空的,或共享中断(一个引脚有多个中断),加入新的中断功能

2 desc->chip->settpye
把引脚设置为中断引脚
3 desc->chip->startup/enable
引脚使能

最后卸载irq

free_irq(irq,dev_id)
作用 : 出链 禁止中断

如何写int request_irq

int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags const char *devname,void *dev_id)

先看 电路图我的是jz2440 v3
在这里插入图片描述
则四个按键对应着 0,2,11,19
中断号 在irq.h 里已经写好了

第一个参数unsigned int irq

在这里插入图片描述
在这里插入图片描述

第二个参数处理函数irq_handler_t handler

在这里插入图片描述

第三个参数

在irq.h里面选择什么时候触发
在这里插入图片描述
这里选择用双边缘触发

第四个参数

取一个名字。如“原理图”有按键 key4,所以这个名字用“S4

第五个参数

是 dev_id,它是“free_irq”函数用来从“action”链表中删除“irqaction”结构。使
用参 1 中断号“irq”定位“action”链表,再使用“dev_id”在“action”链表中找到卸载的表
项。同一个中断的不同中断处理函数必须使用不同的“dev_id”来区分,这要求注册共享中
断时参数“dev_id”必须惟一。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值