====本文系本站原创,欢迎转载! 转载请注明出处:http://blog.csdn.net/yyplc====
Linux通过以下函数来注册中断以及中断相关的入口函数handle,只有先注册IRQ,才能正常使用。
int set_irq_chip(unsigned int irq, structirq_chip *chip) static inline void set_irq_handler(unsigned intirq, irq_flow_handler_t handle) static inline void set_irq_chained_handler(unsignedint irq, irq_flow_handler_t handle)
实现的代码如下:
for(irqno = IRQ_EINT4t7;irqno <= IRQ_ADCPARENT; irqno++) { /*set all the s3c2410 internal irqs */ switch(irqno) { /*deal with the special IRQs (cascaded) */ caseIRQ_EINT4t7: caseIRQ_EINT8t23: caseIRQ_UART0: caseIRQ_UART1: caseIRQ_UART2: caseIRQ_ADCPARENT: set_irq_chip(irqno,&s3c_irq_level_chip); set_irq_handler(irqno,handle_level_irq); //电平触发型 break; caseIRQ_RESERVED6: caseIRQ_RESERVED24: /*no IRQ here */ break; default: //irqdbf("registeringirq %d (s3c irq)\n", irqno); set_irq_chip(irqno,&s3c_irq_chip); set_irq_handler(irqno,handle_edge_irq); //边缘触发型 set_irq_flags(irqno,IRQF_VALID); } /*级联中断的注册*/ set_irq_chained_handler(IRQ_EINT4t7,s3c_irq_demux_extint4t7); set_irq_chained_handler(IRQ_EINT8t23,s3c_irq_demux_extint8); set_irq_chained_handler(IRQ_UART0,s3c_irq_demux_uart0); set_irq_chained_handler(IRQ_UART1,s3c_irq_demux_uart1); set_irq_chained_handler(IRQ_UART2,s3c_irq_demux_uart2); set_irq_chained_handler(IRQ_ADCPARENT,s3c_irq_demux_adc); /*externalinterrupts */ for(irqno =IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { irqdbf("registeringirq %d (ext int)\n", irqno); set_irq_chip(irqno,&s3c_irq_eint0t4); set_irq_handler(irqno,handle_edge_irq); set_irq_flags(irqno,IRQF_VALID); } for(irqno =IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { irqdbf("registeringirq %d (extended s3c irq)\n", irqno); set_irq_chip(irqno,&s3c_irqext_chip); set_irq_handler(irqno,handle_edge_irq); set_irq_flags(irqno,IRQF_VALID); } /*register the uartinterrupts */ irqdbf("s3c2410:registering external interrupts\n"); for(irqno =IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) { irqdbf("registeringirq %d (s3c uart0 irq)\n", irqno); set_irq_chip(irqno,&s3c_irq_uart0); set_irq_handler(irqno,handle_level_irq); set_irq_flags(irqno,IRQF_VALID); } for(irqno =IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) { irqdbf("registeringirq %d (s3c uart1 irq)\n", irqno); set_irq_chip(irqno,&s3c_irq_uart1); set_irq_handler(irqno,handle_level_irq); set_irq_flags(irqno,IRQF_VALID); } for(irqno =IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) { irqdbf("registeringirq %d (s3c uart2 irq)\n", irqno); set_irq_chip(irqno,&s3c_irq_uart2); set_irq_handler(irqno,handle_level_irq); set_irq_flags(irqno,IRQF_VALID); } for(irqno = IRQ_TC;irqno <= IRQ_ADC; irqno++) { //具体注册IRQ_TC、IRQ_ADC irqdbf("registeringirq %d (s3c adc irq)\n", irqno); set_irq_chip(irqno,&s3c_irq_adc); set_irq_handler(irqno,handle_edge_irq); set_irq_flags(irqno,IRQF_VALID); }
从以上代码中可以看出,注册中断主要是注册中断服务程序入口。Linux中将所有的中断号用一个stuctirq_desc数据结构进行统一管理,每个中断号或者一组中断号(如级联的中断),对应一个structirq_desc, 所以根据相应的中断号,可以获取对应的中断描述结构irq_desc:
struct irq_desc *desc = irq_to_desc(irq); irq_desc数据结构如下: struct irq_desc { unsigned int irq; //中断号 … irq_flow_handler_t handle_irq; //系统中断处理的入口函数 structirq_chip *chip; // 对应的irq_chip结构,定义了与中断处理有关的函数 … structirqaction *action; //用户的中断处理函数,调用request_irq时添加 unsigned int status; // IRQ的状态 … spinlock_t lock; … const char *name; } ____cacheline_internodealigned_in_smp;
irq_chip结构定义了各种中断相关的处理行为,如开启或禁止中断以及中断服务完成的之后对相关的中断寄存器进行处理。关于电平触发型和边缘型触发中断入口函数可以从irq_chip结构中的看出只有ack函数的处理不同:
struct irq_chip s3c_irq_level_chip = { .name = "s3c-level", .ack = s3c_irq_maskack, .mask = s3c_irq_mask, .unmask = s3c_irq_unmask, .set_wake = s3c_irq_wake }; struct irq_chip s3c_irq_chip = { .name = "s3c", .ack = s3c_irq_ack, .mask = s3c_irq_mask, .unmask = s3c_irq_unmask, .set_wake = s3c_irq_wake };
mask = __raw_readl(S3C2410_INTMSK);
__raw_writel(mask|bitval, S3C2410_INTMSK);
实现的功能是,屏蔽相应的中断。
在early_trap_init()中已经进行了中断(异常)向量的初始化,将异常向量表从物理地址0x00000000拷贝到虚拟0xffff0000的虚拟地址处。异常向量在arch/arm/kernel/entry-armv.S中定义:
.globl __vectors_start __vectors_start: swi SYS_ERROR0 b vector_und + stubs_offset ldr pc, .LCvswi + stubs_offset b vector_pabt + stubs_offset b vector_dabt + stubs_offset b vector_addrexcptn + stubs_offset b vector_irq + stubs_offset b vector_fiq + stubs_offset .globl __vectors_end
那么当有中断产生时:
/* * Interrupthandling. Preserves r7, r8, r9 */ .macro irq_handler get_irqnr_preambler5, lr 1: get_irqnr_and_baser0, r6, r5, lr movne r1, sp @ @ routinecalled with r0 = irq number, r1 = struct pt_regs * @ adrne lr, 1b bne asm_do_IRQ //跳转到这里中断的总入口函数
这里的asm_do_IRQ对应arch/arm/kernel/irq.c中:
asmlinkage void __exception asm_do_IRQ(unsigned int irq,struct pt_regs *regs)
所以这是Linux中所有中断的总入口函数。asm_doIRQ()调用generic_handle_irq()再后调用
generic_handle_irq_desc(),最后到各个irq_desc的处理。
static inline void generic_handle_irq_desc(unsigned intirq, struct irq_desc *desc) { #ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ desc->handle_irq(irq,desc); #else if(likely(desc->handle_irq)) desc->handle_irq(irq,desc); else __do_IRQ(irq); #endif }
从总入口到用户的中断处理函数的流程:
asm_do_IRQ() –-> generic_handle_irq() –->irq_dsc->handle() à handle_IRQ_event() à irq_des->action()
最后对中断处理流程进行简单总结:
(1)总入口函数asm_do_IRQ,获取中断号irq
(2)asm_do_IRQ根据中断号调用各中断号所注册的中断入口函数irq_desc[irq]->handle_irq
(3)最后在中断入口函数中调用handle_IRQ_event()依次执行用户的中断处理函数action