参考博文http://blog.chinaunix.net/uid-25845340-id-2983156.html/** * struct irq_desc - interrupt descriptor * @irq: interrupt number for this descriptor * @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()] * @chip: low level interrupt hardware access * @msi_desc: MSI descriptor * @handler_data: per-IRQ data for the irq_chip methods * @chip_data: platform-specific per-chip private data for the chip * methods, to allow shared chip implementations * @action: the irq action chain * @status: status information * @depth: disable-depth, for nested irq_disable() calls * @wake_depth: enable depth, for multiple set_irq_wake() callers * @irq_count: stats field to detect stalled irqs * @irqs_unhandled: stats field for spurious unhandled interrupts * @last_unhandled: aging timer for unhandled count * @lock: locking for SMP * @affinity: IRQ affinity on SMP * @cpu: cpu index useful for balancing * @pending_mask: pending rebalanced interrupts * @dir: /proc/irq/ procfs entry * @name: flow handler name for /proc/interrupts output */struct irq_desc { unsigned int irq; irq_flow_handler_t handle_irq; struct irq_chip *chip; struct msi_desc *msi_desc; void *handler_data; void *chip_data; struct irqaction *action; /* IRQ action list */ unsigned int status; /* IRQ status */ unsigned int depth; /* nested irq disables */ unsigned int wake_depth; /* nested wake enables */ unsigned int irq_count; /* For detecting broken IRQs */ unsigned int irqs_unhandled; unsigned long last_unhandled; /* Aging timer for unhandled count */ spinlock_t lock;#ifdef CONFIG_SMP cpumask_t affinity; unsigned int cpu;#endif#ifdef CONFIG_GENERIC_PENDING_IRQ cpumask_t pending_mask;#endif#ifdef CONFIG_PROC_FS struct proc_dir_entry *dir;#endif const char *name;} ____cacheline_internodealigned_in_smp;extern struct irq_desc irq_desc[NR_IRQS]; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////尤entry_armv.s 里面的中断底层汇编可知asm_do_IRQ函数式中断的C语言总入口函数。它在arch/arm/kernel/irq.c中定义 /** * 我们暂且可以认为,绝大部分中断是从汇编跳到本函数处理的。当然,IPI和local_timer不是。 * irq: 产生中断的外部中断号。 * regs: 被中断打断的寄存器现场。 */ asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs){ /** * 将当前正在处理的中断现场保存到每CPU变量__irq_regs中去。 * 这样做的目的,是为了在其他代码中,直接读取__irq_regs中的值,找到中断前的现场。 * 而不用将regs参数层层传递下去。 */ struct pt_regs *old_regs = set_irq_regs(regs); irq_enter(); /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (irq >= NR_IRQS) //中断号错误,超过最大中断号 handle_bad_irq(irq, &bad_irq_desc); else generic_handle_irq(irq);///* 这里进行正常的中断处理 */ /* AT91 specific workaround */ irq_finish(irq); irq_exit();//恢复__irq_regs每CPU变量的内容。 set_irq_regs(old_regs);}////////////////include/asm-generic/irq_regs.h//这个函数就是记录中断现场static inline struct pt_regs *set_irq_regs(struct pt_regs *new_regs){ struct pt_regs *old_regs, **pp_regs = &__get_cpu_var(__irq_regs); old_regs = *pp_regs; *pp_regs = new_regs; return old_regs;}/////////////////////////////// /** * 中断退出过程,主要处理以下内容: * 1、调试钩子,记录退出中断的事实。 * 2、在任务的抢占计数字段中,递减中断计数 * 3、处理软中断 * 4、调用rcu模块的函数,表示已经退出中断。 */ /* * Exit an interrupt context. Process softirqs if needed and possible: */void irq_exit(void){ account_system_vtime(current); trace_hardirq_exit(); sub_preempt_count(IRQ_EXIT_OFFSET); if (!in_interrupt() && local_softirq_pending()) invoke_softirq();#ifdef CONFIG_NO_HZ /* Make sure that timer wheel updates are propagated */ if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched()) tick_nohz_stop_sched_tick(0); rcu_irq_exit();#endif preempt_enable_no_resched();}///////////////////////////////// /** * 调用irq_enter表示进入中断处理过程。该函数进行如下处理: * 1、rcu模块记录内部计数,表示当前退出了NOHZ状态。关于rcu,需要整整一本书来描述,请从http://xiebaoyou.download.csdn.net下载深入理解并行编程>了解更多内容。 * 2、处理NOHZ事件。 * 3、将当前任务的抢占计数中的硬中断计数加1.该计数表示当前中断嵌套层次。 * 4、调试信息,表示当前已经进入中断的事实。 *//* * Enter an interrupt context. */void irq_enter(void){ int cpu = smp_processor_id(); if (idle_cpu(cpu) && !in_interrupt()) { __irq_enter(); tick_check_idle(cpu); } else __irq_enter();}/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc){ s3c_irq_demux_timer(irq, IRQ_TIMER0);}////////////////////////////////////////////////////////////// /* Timer interrupt handling */static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq){ generic_handle_irq(sub_irq);}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////中定义static inline void generic_handle_irq(unsigned int irq){ // * 根据中断描述符中的信息,得到该中断ISR,并处理中断。 generic_handle_irq_desc(irq, irq_to_desc(irq));} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////* * Architectures call this to let the generic IRQ layer * handle an interrupt. If the descriptor is attached to an * irqchip-style controller then we call the ->handle_irq() handler, * and it calls __do_IRQ() if it's attached to an irqtype-style controller. */中定义 static inline void generic_handle_irq_desc(unsigned int irq, 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); //此处调用s3c24xx_init_irq()中 set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8)的s3c_irq_demux_extint8() else __do_IRQ(irq);#endif} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////handle.cunsigned int __do_IRQ(unsigned int irq){ struct irq_desc *desc = irq_to_desc(irq); struct irqaction *action; unsigned int status; spin_lock(&desc->lock); /* * REPLAY is when Linux resends an IRQ that was dropped earlier * WAITING is used by probe to mark irqs that are being tested */ status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); status |= IRQ_PENDING; /* we _want_ to handle it */ /* * If the IRQ is disabled for whatever reason, we cannot * use the action we have. */ action = NULL; if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action; status &= ~IRQ_PENDING; /* we commit to handling */ status |= IRQ_INPROGRESS; /* we are handling it */ } desc->status = status; /* * If there is no IRQ handler or it was disabled, exit early. * Since we set PENDING, if another processor is handling * a different instance of this same irq, the other processor * will take care of it. */ if (unlikely(!action)) goto out; /* * Edge triggered interrupts need to remember * pending events. * This applies to any hw interrupts that allow a second * instance of the same irq to arrive while we are in do_IRQ * or in the handler. But the code here only handles the _second_ * instance of the irq, not the third or fourth. So it is mostly * useful for irq hardware that does not mask cleanly in an * SMP environment. */ for (;;) { irqreturn_t action_ret; spin_unlock(&desc->lock); action_ret = handle_IRQ_event(irq, action);//通过此函数逐个执行action链表中用户注册的中断处理函数 if (!noirqdebug) note_interrupt(irq, desc, action_ret); spin_lock(&desc->lock); if (likely(!(desc->status & IRQ_PENDING))) break; desc->status &= ~IRQ_PENDING; } desc->status &= ~IRQ_INPROGRESS;out: /* * The ->end() handler has to deal with interrupts which got * disabled while the handler was running. */ desc->chip->end(irq); spin_unlock(&desc->lock); return 1;}/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////handle.c/** * handle_IRQ_event - irq action chain handler * @irq: the interrupt number * @action: the interrupt action chain for this irq * * Handles the action chain of an irq event */irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action){ irqreturn_t ret, retval = IRQ_NONE; unsigned int status = 0; if (!(action->flags & IRQF_DISABLED)) local_irq_enable_in_hardirq(); do { ret = action->handler(irq, action->dev_id); if (ret == IRQ_HANDLED) status |= action->flags; retval |= ret; action = action->next; } while (action); if (status & IRQF_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable(); return retval;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////init/main.c asmlinkage void __init start_kernel(void) --->调用 arch/arm/kernel/irq.c void __init init_IRQ(void)void __init init_IRQ(void){ int irq; for (irq = 0; irq irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;#ifdef CONFIG_SMP bad_irq_desc.affinity = CPU_MASK_ALL; bad_irq_desc.cpu = smp_processor_id();#endif init_arch_irq();// 即为 s3c6410_init_irq//////////////////////////////////////////////////////////////////以下为注释//set_up.c 中有setup_arch()中有init_arch_irq=mdesc->init_irq;对于S3C2410,S3C2440开发板,中有MACHINE_START(SMDK6410, "SMDK6410") /* Maintainer: Ben Dooks */ .phys_io = S3C_PA_UART & 0xfff00000, .io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc, .boot_params = S3C64XX_PA_SDRAM + 0x100, .init_irq = s3c6410_init_irq, .map_io = smdk6410_map_io, .init_machine = smdk6410_machine_init, .timer = &s3c64xx_timer,MACHINE_END//////////////////////////////////////////////////////////////////////以上为注释} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void __init s3c6410_init_irq(void){ /* VIC0 is missing IRQ7, VIC1 is fully populated. */ s3c64xx_init_irq(~0 & ~(1}//////////////////////////////////////////////////////////////////////////////////////////////void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid){ int uart, irq; printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); /* initialise the pair of VICs */ vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid); vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid); /* add the timer sub-irqs */ set_irq_chained_handler(IRQ_TIMER0_VIC, s3c_irq_demux_timer0); set_irq_chained_handler(IRQ_TIMER1_VIC, s3c_irq_demux_timer1); set_irq_chained_handler(IRQ_TIMER2_VIC, s3c_irq_demux_timer2); set_irq_chained_handler(IRQ_TIMER3_VIC, s3c_irq_demux_timer3); set_irq_chained_handler(IRQ_TIMER4_VIC, s3c_irq_demux_timer4); for (irq = IRQ_TIMER0; irq set_irq_chip(irq, &s3c_irq_timer); set_irq_handler(irq, handle_level_irq); set_irq_flags(irq, IRQF_VALID); } for (uart = 0; uart s3c64xx_uart_irq(&uart_irqs[uart]);}////////////////////////////////////* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] * are consecutive when looking up the interrupt in the demux routines. */static struct uart_irq uart_irqs[] = { [0] = { .regs = S3C_VA_UART0, .base_irq = IRQ_S3CUART_BASE0, .parent_irq = IRQ_UART0, }, [1] = { .regs = S3C_VA_UART1, .base_irq = IRQ_S3CUART_BASE1, .parent_irq = IRQ_UART1, }, [2] = { .regs = S3C_VA_UART2, .base_irq = IRQ_S3CUART_BASE2, .parent_irq = IRQ_UART2, }, [3] = { .regs = S3C_VA_UART3, .base_irq = IRQ_S3CUART_BASE3, .parent_irq = IRQ_UART3, },};////////////////// static void __init s3c64xx_uart_irq(struct uart_irq *uirq){ void *reg_base = uirq->regs; unsigned int irq; int offs; /* mask all interrupts at the start. */ __raw_writel(0xf, reg_base + S3C64XX_UINTM); for (offs = 0; offs irq = uirq->base_irq + offs; set_irq_chip(irq, &s3c_irq_uart); set_irq_chip_data(irq, uirq); set_irq_handler(irq, handle_level_irq); set_irq_flags(irq, IRQF_VALID); } set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart);}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// include/linux/irq.h中static inline voidset_irq_chained_handler(unsigned int irq, irq_flow_handler_t handle){ __set_irq_handler(irq, handle, 1, NULL);}//////////////////////////////////////////////////////////////////////////////////////////////////////kernel/irq/chip.cvoid __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name){ struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; desc->handle_irq = handle; desc->name = name; if (handle != handle_bad_irq && is_chained) { desc->status &= ~IRQ_DISABLED; desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE; desc->depth = 0; desc->chip->startup(irq); } spin_unlock_irqrestore(&desc->lock, flags);}//////////////////////////////////////////////static inline struct irq_desc *irq_to_desc(unsigned int irq){ return (irq}///////////////////////////////////////////////////////////////////////////////////////////////////////////irq.h中static inline struct irq_desc *irq_to_desc(unsigned int irq){ return (irq}///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 用户注册中断处理函数: manage.c/** * request_irq - allocate an interrupt line * @irq: Interrupt line to allocate * @handler: Function to be called when the IRQ occurs * @irqflags: Interrupt type flags * @devname: An ascii name for the claiming device 传递给request_irq的字符串,用来在/proc/interrupts中显示中断的拥有者 * @dev_id: A cookie passed back to the handler function 用于共享的中断信号线 * * This call allocates interrupt resources and enables the * interrupt line and IRQ handling. From the point this * call is made your handler function may be invoked. Since * your handler function must clear any interrupt the board * raises, you must take care both to initialise your hardware * and to set up the interrupt handler in the right order. * * Dev_id must be globally unique. Normally the address of the * device data structure is used as the cookie. Since the handler * receives this value it makes sense to use it. * * If your interrupt is shared you must pass a non NULL dev_id * as this is required when freeing the interrupt. * * Flags: * * IRQF_SHARED Interrupt is shared * IRQF_DISABLED Disable local interrupts while processing * IRQF_SAMPLE_RANDOM The interrupt can be used for entropy * IRQF_TRIGGER_* Specify active edge(s) or level * */在Linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)irq是要申请的硬件中断号。handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。irqflags是中断处理的属性,若设置了IRQF_DISABLED(老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED(老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)devname设置中断名称,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称。dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id){ struct irqaction *action; struct irq_desc *desc; int retval;#ifdef CONFIG_LOCKDEP /* * Lockdep wants atomic interrupt handlers: */ irqflags |= IRQF_DISABLED;#endif /* * Sanity-check: shared interrupts must pass in a real dev-ID, * otherwise we'll have trouble later trying to figure out * which interrupt is which (messes up the interrupt freeing * logic etc). */ if ((irqflags & IRQF_SHARED) && !dev_id) return -EINVAL; desc = irq_to_desc(irq); //根据中断号来获得关于中断资源的描述 if (desc->status & IRQ_NOREQUEST) //如果中断资源被设置为无法获得。 return -EINVAL; if (!handler) // --中断必须要有中断处理函数 return -EINVAL; action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); if (!action) return -ENOMEM; action->handler = handler; action->flags = irqflags; cpus_clear(action->mask); action->name = devname; action->next = NULL; action->dev_id = dev_id; ////__setup_irq函数,该函数才是真正的将中断注册 retval = __setup_irq(irq, desc, action); if (retval) kfree(action);#ifdef CONFIG_DEBUG_SHIRQ if (irqflags & IRQF_SHARED) { /* * It's a shared IRQ -- the driver ought to be prepared for it * to happen immediately, so let's make sure.... * We disable the irq to make sure that a 'real' IRQ doesn't * run in parallel with our fake. */ unsigned long flags; disable_irq(irq); local_irq_save(flags); handler(irq, dev_id); //???为啥在这里立即调用了中断的handler函数(这是一个参数) 看看宏CONFIG_DEBUG_SHIRQ local_irq_restore(flags); enable_irq(irq); }#endif return retval;} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////manage.c中 ////__setup_irq函数,该函数才是真正的将中断注册 /* * Internal function to register an irqaction - typically used to * allocate special interrupts that are part of the architecture. */static int__setup_irq(unsigned int irq, struct irq_desc * desc, struct irqaction *new){ struct irqaction *old, **p; const char *old_name = NULL; unsigned long flags; int shared = 0; int ret; /* * The following block of code has to be executed atomically */ spin_lock_irqsave(&desc->lock, flags); p = &desc->action; old = *p; if (old) { /* add new interrupt at end of irq queue */ ????????????????????????????、 do { p = &old->next; old = *p; } while (old); shared = 1; } if (!shared) { irq_chip_set_defaults(desc->chip); /* Setup the type (level, edge polarity) if configured: */ if (new->flags & IRQF_TRIGGER_MASK) { ret = __irq_set_trigger(desc, irq, new->flags); if (ret) { spin_unlock_irqrestore(&desc->lock, flags); return ret; } } else compat_irq_chip_set_default_handler(desc); desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); if (!(desc->status & IRQ_NOAUTOEN)) { desc->depth = 0; desc->status &= ~IRQ_DISABLED; desc->chip->startup(irq); } else /* Undo nested disables: */ desc->depth = 1; /* Exclude IRQ from balancing if requested */ if (new->flags & IRQF_NOBALANCING) desc->status |= IRQ_NO_BALANCING; /* Set default affinity mask once everything is setup */ do_irq_select_affinity(irq, desc); } else if ((new->flags & IRQF_TRIGGER_MASK) && (new->flags & IRQF_TRIGGER_MASK) != (desc->status & IRQ_TYPE_SENSE_MASK)) { /* hope the handler works with the actual trigger mode... */ pr_warning("IRQ %d uses trigger mode %d; requested %d\n", irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK), (int)(new->flags & IRQF_TRIGGER_MASK)); } *p = new; /* Reset broken irq detection when installing new handler */ desc->irq_count = 0; desc->irqs_unhandled = 0; /* * Check whether we disabled the irq via the spurious handler * before. Reenable it and give it another chance. */ if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) { desc->status &= ~IRQ_SPURIOUS_DISABLED; __enable_irq(desc, irq); } spin_unlock_irqrestore(&desc->lock, flags); new->irq = irq; register_irq_proc(irq, desc); new->dir = NULL; register_handler_proc(irq, new); return 0;mismatch: spin_unlock_irqrestore(&desc->lock, flags); return -EBUSY;} /////////////////////////////////////////arch/arm/kernel/setup.c void __init setup_arch(char **cmdline_p) 调用early_trap_init(void) arch/arm/kernel/traps.cvoid __init early_trap_init(void){// include/linux/autoconf.h #define CONFIG_VECTORS_BASE 0xffff0000 unsigned long vectors = CONFIG_VECTORS_BASE; extern char __stubs_start[], __stubs_end[]; extern char __vectors_start[], __vectors_end[]; extern char __kuser_helper_start[], __kuser_helper_end[]; int kuser_sz = __kuser_helper_end - __kuser_helper_start; /* * Copy the vectors, stubs and kuser helpers (in entry-armv.S) * into the vector page, mapped at 0xffff0000, and ensure these * are visible to the instruction stream. *///将_vectors_start到__vectors_start之间的代码拷贝到0xffff0000 memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz); /* * Copy signal return handlers into the vector page, and * set sigreturn to be a pointer to these. */ memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, sizeof(sigreturn_codes)); flush_icache_range(vectors, vectors + PAGE_SIZE); modify_domain(DOMAIN_USER, DOMAIN_CLIENT);} 中断共享如果开发板上按键的中断已经被另外的驱动程序注册中断了,而我现在又想再注册一次这个中断,这就出现了一个中断号不止对应一个中断函数的情况。注意,这里与硬件上的共享中断不一样,这里是指,当一个中断信号来了,基于操作系统,一个中断的到来可以调用多个中断处理程序,与硬件无关。SA_SHIRQ:这个标志表明多个中断处理程序可以共享一个中断号。