Linux内核中断处理流程:
(1) 发生中断时,CPU执行异常向量vector_irq的代码(通过向量表跳转到异常向量处理区);
(2) 异常向量处理区会调用asm_do_IRQ函数,这是中断函数的总入口函数;
(3) asm_do_IRQ 会根据中断号调用相应irq_desc[]数组项中的handler_irq;
(4) handler_irq会先调用irq_desc结构体中的chip函数对底层进行操作,如开中断,禁止中断,清楚中断等;
(5) handler_irq然后一一调用action链表中注册的中断处理函数handler(这个才是用户自己编写的中断处理函数,通过request_irq来注册)。
核心成员:
中断驱动程序中,request_irq()是驱动程序的核心,
内核支持中,irq_desc[]是中断结构体系的核心。
irq_desc[]是一个指向irq_desc结构的数组, irq_desc结构是各个设备中断服务例程的描述符。
硬件中断请求——异常向量表——异常向量处理函数(调用asm_do_IRQ)——根据中断号n跳转到irq_desc[n]的地址,这个地址也是一个入口函数handler_irq。
struct irq_desc {
irq_flow_handler_t handle_irq;
//入口处理函数,依次调用chip中的函数与链表irqaction中注册的用户中断处理函数
struct irq_chip *chip; //结构体地址,底层硬件操作
void *handler_data;
void *chip_data;
struct irqaction *action; //用户注册的结构体链表,为用户中断处理函数
unsigned int status;
unsigned intdepth;
unsigned int wake_depth;
unsigned int irq_count;
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#ifdefined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry*dir;
#endif
const char *name;
} ____cacheline_aligned;
根据irq_desc的基础知识,request_irq的各个参数代表的意思如下:
int request_irq(
unsigned int irq,
irqreturn_t (*handler)(int, void *,struct pt_regs *),
unsigned long irqflags,
const char *devname,
void *dev_id );
irq 为中断号,即irq_desc[irq]数组中的下标。根据中断号irq可以找到对应的irq_desc[irq]。
handler为用户注册的中断处理函数,用来将其注册到structirqaction *action的链表当中。
irqflags中断标志,指定了快速中断或中断共享等中断处理属性,在structirqaction *action中需要用到。
devname指定设备驱动程序的名称。
dev_id:传入中断处理程序的参数,可以为NULL,在注册共享中断时,此参数不能为NULL,作为共享中断时的中断区别参数。
当不同设备共用同一个中断时,这个时候需要对中断进行区别,使用dev_id,因为中断号是一样的,但是主设备是不一样的。
用户开发中断驱动需要做的工作:
(1) 字符设备驱动基本的操作,见笔记linux 驱动.docx
(2) ko模块中的fops成员组中,open()函数增加向内核注册中断处理函数request_irq(),以及从内核释放已注册中断处理函数free_irq()。
(3) ko模块中的fops成员组中,增加read()函数,此函数实现休眠功能,如果有动作,则读取,如果没有则休眠。
(4) ko模块中,增加用户中断处理函数,用于注册。
(5) 用户使用时中,先insmod .ko,(再mknod /dev/deive_name),然后在用户程序中,先open(),然后read(),实现无中断则睡眠,有中断则根据中断处理结果进行输出显示。