之前了解了一下linux的模块编程,也对简单字符设备驱动框架有了一定了解,前两天看了韦东山老师有关按键中断的视频,自己又查阅了一些资料之后总结出以下知识点,希望能对同样是初学linux驱动的朋友们有一些参考价值。
一、简单使用
从使用的角度分析,使用linux内核的api来进行最简单的中断注册和中断注销。
中断注册:
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char* devname,void *dev_id);
irq:要申请的中断号,要根据硬件原理图来确定
handler:表示要注册的中断处理函数,内核发生时会自动进行调用
irqflags:表示中断处理的属性。
devname:表示设备的名称,该名称会在/proc/interrupts中显示。interrupts记录了设备和中断号对应的关系。
dev_id:这个参数是为共享中断而设置的。
request_irq()函数成功返回0,错误时返回-EINVAL或者-ENOMEM。
中断释放:
void free_irq(unsigned int irq,void *dev_id)
irq:释放申请的中断号
dev_id:为共享中断线而设立的
在使用中断时,比较重要的步骤是编写自己的中断处理函数,这要根据具体的功能来定。
它的原型是irq_handler_t handler (int irq, void *dev_id)。
二、中断处理体系结构分析
linux内核将所有的中断统一编号,使用一个irq_desc的结构数组来描述中断,数组的下标与中断号相对应,即若你的开发板有32根中断线,那么就有32个irq_desc结构体与之对应,你可能问那共享中断线的外设怎么办?先看看下面的分析。
1.irq_desc结构体
struct irq_desc{
irq_flow_handler_t handle_irq;//当前中断的处理函数入口
struct irq_chip *chip; //底层的硬件访问
...
struct irqaction *action //用户提供的中断处理函数链表
unsigned int status; // IRQ状态
...
const char *name; //中断名称
}
在irq_chip *chip中封装了许多对硬件的操作,比如设置寄存器来屏蔽中断、使能中断、清除中断等。
struct irqaction *action这个结构体是根据我们在调用request_irq()时,根据传入的参数进行创建的。
struct irqaction{
irq_handler_t handler;//用户注册的中断处理函数
unsigned long flags;//中断标志
cpumask_t mask;//用于SMP
const char *name;//用户注册的中断名称
void *dev_id;//用户传给本结构体中handler的参数,还可以用来区分共享中断
struct irqaction *next;
int irq;//中断号
struct proc_dir_entry *dir;
};
可以看到我们在调用request_irq()注册中断时传入的参数都在这个结构体中。
这个结构体中的handler 和上面结构体里的handler_irq是不同的,
当发生中断时,内核根据中断号找到对应的irq_desc数组,然后调用irq_desc数组中的handler_irq函数进行一些操作(包括调用cpu中的函数进行硬件的操作),之后找到对应action链表中的handler函数,这才会进入我们自己编写的中断服务程序。
我这里截取了韦东山老师书上的一张图来说明这几个结构体的关系。
可以看到irq_desc[]数组中的每一项对应一个中断线,每个irq_desc结构体中的chip指向一些对底层硬件的操作,每个irq_desc中的action对应的一个用户自己注册的中断处理函数,而多个action可以连接成一个链表。当一个irq_desc中有多个action结构体时可以理解为该中断为共享中断,这就是共享中断的处理方式——一个irq_desc对应多个action结构,这些action结构之间则是通过你在注册中断时传入的dev_id来进行区分的。
韦老师书中总结了中断处理流程:
(1)发生中断时,CPU执行异常向量vector_irq代码
(2)在vetcot_irq中,最终会调用中断处理的总入口函数asm_do_IRQ
(3)asm_do_IRQ根据中断号调用irq_desc数组项中的handle_irq
(4)handle_irq会使用chip成员中的函数来设置硬件,比如清除中断、禁止中断、重新使能中断等
(5)handle_irq逐个调用用户在action链表中注册的处理函数
根据我自己的理解,irq_desc结构体中的一些成员,在系统初始化时就已经被设置好了比如
irq_flow_handler_t handle_irq;//当前中断的处理函数入口
struct irq_chip *chip; //底层的硬件访问
unsigned int status; // IRQ状态
这三个成员。
就拿chip来说,这是一个跟具体硬件有关的结构体,软件操作必须能和具体的硬件配套,所以在初始化时就已经用相关硬件操作初始化过chip了,等中断到来时直接调用即可。
虽然自己也查阅了一些书,但毕竟没有深入阅读并理解2.6的内核源码,对驱动中的中断也只能理解到这里,如果有不对的地方,希望大家指出来。