[玩转kernel系列]中断 - 拳不离手、曲不离口 - Speak with your code, my friend, not your word.
[玩转kernel系列]从今天起,我开一个《玩转kernel》系列,同时期待大家的讨论。

[玩转kernel系列]中断

clem posted @ 2009年5月03日 02:58 in Kernel with tags linux 中断 , 2516 阅读

ARM里的中断通常是指IRQFIQ,以IRQ来讲,ARMIRQ的处理过程大概是这样:  

外部设备遇到某一事件发出一个IRQ中断给中断控制器,中断控制器对这个IRQ进行硬件上的处理,把一些信息记在中断控制器的寄存器上,然后中断控制器通过IRQ中断线给ARM发一个信号。ARM收到信号,开始进行以下处理:  

1)将当前状态的cpsr拷贝到IRQ状态的spsr中。  

2)将pc拷贝到IRQ状态的lr中。  

3)屏蔽cpsr中的IRQ位和FIQ位。  

4)跳转入中断向量表的IRQ表项执行(改变pc的值)。

 以上都是ARM cpu做的事,不需要程序员插手,程序员编的代码需要接在后面处理。

 程序员需要做的是接下来的步骤:

(5) 备份上下文。

e.g.:

       sub         lr, lr, #4

       stmfd      sp!, {r0 - r12, lr}

(6)跳入handler。

 e.g.:

        bl IRQ_Handler

 (7):恢复上下文。

 e.g.:

        ldmfd      sp!, {r0 - r12, sp}^

 就这么简单!

 来看kernel对应的代码

 

 /* arch/arm/kernel/entry-armv.S */

  1. __irq_svc:
  2.         svc_entry
  3.  
  4. #ifdef CONFIG_TRACE_IRQFLAGS
  5.         bl      trace_hardirqs_off
  6. #endif
  7. #ifdef CONFIG_PREEMPT
  8.         get_thread_info tsk
  9.         ldr     r8, [tsk, #TI_PREEMPT]              @ get preempt count
  10.         add     r7, r8, #1         @ increment it
  11.         str     r7, [tsk, #TI_PREEMPT]
  12. #endif
  13.  
  14.         irq_handler
  15. #ifdef CONFIG_PREEMPT
  16.         str     r8, [tsk, #TI_PREEMPT]              @ restore preempt count
  17.         ldr     r0, [tsk, #TI_FLAGS]  @ get flags
  18.         teq     r8, #0                            @ if preempt count != 0
  19.         movne   r0, #0                    @ force flags to 0
  20.         tst     r0, #_TIF_NEED_RESCHED
  21.         blne    svc_preempt
  22. #endif
  23.         ldr     r0, [sp, #S_PSR]          @ irqs are already disabled
  24.         msr     spsr_cxsf, r0
  25. #ifdef CONFIG_TRACE_IRQFLAGS
  26.         tst     r0, #PSR_I_BIT
  27.         bleq    trace_hardirqs_on
  28. #endif
  29.         ldmia   sp, {r0 - pc}^               @ load r0 - pc, cpsr

 

就是高亮的三句。

 

首尾两句简单,关键是中间的handler,来看handler的源码。

 

/* arch/arm/kernel/entry-armv.S */

  1.         .macro  irq_handler
  2.         get_irqnr_preamble r5, lr
  3. 1:      get_irqnr_and_base r0, r6, r5, lr
  4.         movne   r1, sp
  5.         @
  6.         @ routine called with r0 = irq number, r1 = struct pt_regs *
  7.         @
  8.         adrne   lr, 1b
  9.         bne     asm_do_IRQ
  10.  
  11. #ifdef CONFIG_SMP
  12.         /*
  13.          * XXX
  14.          *
  15.          * this macro assumes that irqstat (r6) and base (r5) are
  16.          * preserved from get_irqnr_and_base above
  17.          */
  18.         test_for_ipi r0, r6, r5, lr
  19.         movne   r0, sp
  20.         adrne   lr, 1b
  21.         bne     do_IPI
  22.  
  23. #ifdef CONFIG_LOCAL_TIMERS
  24.         test_for_ltirq r0, r6, r5, lr
  25.         movne   r0, sp
  26.         adrne   lr, 1b
  27.         bne     do_local_timer
  28. #endif
  29. #endif
  30.  
  31.         .endm

handler里最重要的事情就是取得中断号,然后根据中断号去索引到具体的ISR。具体的ISR就在Do_asm_IRQ里啦,来看这个函数的源码。

/* arch/arm/kernel/irq.c */

 

  1. asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
  2. {
  3.         struct pt_regs *old_regs = set_irq_regs(regs);
  4.         struct irq_desc *desc = irq_desc + irq;
  5.  
  6.         /*
  7.          * Some hardware gives randomly wrong interrupts.  Rather
  8.          * than crashing, do something sensible.
  9.          */
  10.         if (irq >= NR_IRQS)
  11.                 desc = &bad_irq_desc;
  12.  
  13.         irq_enter();
  14.  
  15.         desc_handle_irq(irq, desc);
  16.  
  17.         /* AT91 specific workaround */
  18.         irq_finish(irq);
  19.  
  20.         irq_exit();
  21.         set_irq_regs(old_regs);
  22. }

 

中核心的这句话是desc_handle_irq(irq,desc);跳入后发现这个函数就调用了一句 话,desc->handle_irq(irq, desc),好,具体的ISR就在里面,是通过多态函数的方法调用的,而这个多态函数是在start_kernel中的一些初始化程序中挂上去的。

许你想看一个具体的handle_irq(irq, desc),看一下desc->handle_irq()到底指到哪里去了。但是kernel里的东西就是复杂,你想一眼看穿它,是不那么容易的。 所以我们暂且把代码扔掉,来想一想,这个handle_irq()到底是做什么的。很明显,它至少会起ISR的作用,不管它另外做了什么乱七八糟的事 情,ISR是它的重要目的。那我们先来找一个ISR,我们的开发板At91Sam9261上的网卡是DM9000,我们就到DM9000的驱动里去找 ISR

 /* drivers\net\dm9000.c */

 

 

  1. static int
  2. dm9000_open(struct net_device *dev)
  3. {
  4.         board_info_t *db = dev->priv;
  5.         unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
  6.  
  7.         if (netif_msg_ifup(db))
  8.                 dev_dbg(db->dev, "enabling %s\n", dev->name);
  9.  
  10.         /* If there is no IRQ type specified, default to something that
  11.          * may work, and tell the user that this is a problem */
  12.  
  13.         if (irqflags == IRQF_TRIGGER_NONE)
  14.                 dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");
  15.  
  16.         irqflags |= IRQF_SHARED;
  17.  
  18.         if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))
  19.                 return -EAGAIN;
  20.  
  21.         /* Initialize DM9000 board */
  22.         dm9000_reset(db);
  23.         dm9000_init_dm9000(dev);
  24.  
  25.         /* Init driver variable */
  26.         db->dbug_cnt = 0;
  27.  
  28.         mii_check_media(&db->mii, netif_msg_link(db), 1);
  29.         netif_start_queue(dev);
  30.        
  31.         dm9000_schedule_poll(db);
  32.  
  33.         return 0;
  34. }

好,找到request_irq()就行了,其中有一个参数就是ISR,也就是dm9000_interrupt(),让我们来看这个ISR

 /* drivers\net\dm9000.c */

  1. static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
  2. {
  3.         struct net_device *dev = dev_id;
  4.         board_info_t *db = dev->priv;
  5.         int int_status;
  6.         u8 reg_save;
  7.  
  8.         dm9000_dbg(db, 3, "entering %s\n", __func__);
  9.  
  10.         /* A real interrupt coming */
  11.  
  12.         spin_lock(&db->lock);
  13.  
  14.         /* Save previous register address */
  15.         reg_save = readb(db->io_addr);
  16.  
  17.         /* Disable all interrupts */
  18.         iow(db, DM9000_IMR, IMR_PAR);
  19.  
  20.         /* Got DM9000 interrupt status */
  21.         int_status = ior(db, DM9000_ISR);       /* Got ISR */
  22.         iow(db, DM9000_ISR, int_status);        /* Clear ISR status */
  23.  
  24.         if (netif_msg_intr(db))
  25.                 dev_dbg(db->dev, "interrupt status %02x\n", int_status);
  26.  
  27.         /* Received the coming packet */
  28.         if (int_status & ISR_PRS)
  29.                 dm9000_rx(dev);
  30.  
  31.         /* Trnasmit Interrupt check */
  32.         if (int_status & ISR_PTS)
  33.                 dm9000_tx_done(dev, db);
  34.  
  35.         if (db->type != TYPE_DM9000E) {
  36.                 if (int_status & ISR_LNKCHNG) {
  37.                         /* fire a link-change request */
  38.                         schedule_delayed_work(&db->phy_poll, 1);
  39.                 }
  40.         }
  41.  
  42.         /* Re-enable interrupt mask */
  43.         iow(db, DM9000_IMR, db->imr_all);
  44.  
  45.         /* Restore previous register address */
  46.         writeb(reg_save, db->io_addr);
  47.  
  48.         spin_unlock(&db->lock);
  49.  
  50.         return IRQ_HANDLED;
  51. }

个很明显就是做的一些中断之后真正需要干的事,所以这是整个中断体系中非常重要的一个环节。你可以不知道kernel是怎么绕来绕去绕到这个函数,但要知 道怎么去写这个函数。处理每一种中断的ISR具有很大的差异,需要不断的去认识,而且难度相对于kernel来说,ISR还是比较低的,但是如我前面所 说,它很重要。继续我们的kernel之旅。

我们看到dm9000_interrupt()这个ISR,参数和handle_irq(irq, desc)不一样。

static irqreturn_t dm9000_interrupt(int irq, void *dev_id)这个函数,第二个参数是void*,而handle_irq的第二个参数是struct irq_desc*,所以desc->handle_irq(irq, desc)调用的不是ISR。我说过这个多态函数是在start_kernel里的某个init函数里初始化进去的,init_IRQ();

/* arch\arm\kernel\irq.c */

  1. void __init init_IRQ(void)
  2. {
  3.         int irq;
  4.  
  5.         for (irq = 0; irq < NR_IRQS; irq++)
  6.                 irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
  7.  
  8. #ifdef CONFIG_SMP
  9.         bad_irq_desc.affinity = CPU_MASK_ALL;
  10.         bad_irq_desc.cpu = smp_processor_id();
  11. #endif
  12.         init_arch_irq();
  13. }

先看NR_IRQS,一个宏,在9261这块板子指定的头文件里,定义的是

 

/*  arch\arm\mach-at91\include\mach\irq.h */

  1. #define NR_AIC_IRQS 32
  2.  
  3.  
  4. /*
  5. * Acknowledge interrupt with AIC after interrupt has been handled.
  6. *   (by kernel/irq.c)
  7. */
  8. #define irq_finish(irq) do { at91_sys_write(AT91_AIC_EOICR, 0); } while (0)
  9.  
  10.  
  11. /*
  12. * IRQ interrupt symbols are the AT91xxx_ID_* symbols
  13. * for IRQs handled directly through the AIC, or else the AT91_PIN_*
  14. * symbols in gpio.h for ones handled indirectly as GPIOs.
  15. * We make provision for 5 banks of GPIO.
  16. */
  17. #define NR_IRQS    (NR_AIC_IRQS + (5 * 32))

32+5*32=192。跳入init_arch_irq(),可惜init_arch_irq()又是个多态函数,而且kernel这次用全局函数指针的方式来封装了这个多态函数。

 

/* ar ch\arm\kernel\irq.c */

 

  1. void (*init_arch_irq)(void) __initdata = NULL;

 

 

/* arch\arm\kernel\setup.c */

 

  1. void __init setup_arch(char **cmdline_p)
  2. {
  3.         struct tag *tags = (struct tag *)&init_tags;
  4.         struct machine_desc *mdesc;
  5.         char *from = default_command_line;
  6.  
  7.         setup_processor();
  8.         mdesc = setup_machine(machine_arch_type);
  9.         machine_name = mdesc->name;
  10.  
  11.         if (mdesc->soft_reboot)
  12.                 reboot_setup("s");
  13.  
  14.         if (__atags_pointer)
  15.                 tags = phys_to_virt(__atags_pointer);
  16.         else if (mdesc->boot_params)
  17.                 tags = phys_to_virt(mdesc->boot_params);
  18.  
  19.         /*
  20.          * If we have the old style parameters, convert them to
  21.          * a tag list.
  22.          */
  23.         if (tags->hdr.tag != ATAG_CORE)
  24.                 convert_to_tag_list(tags);
  25.         if (tags->hdr.tag != ATAG_CORE)
  26.                 tags = (struct tag *)&init_tags;
  27.  
  28.         if (mdesc->fixup)
  29.                 mdesc->fixup(mdesc, tags, &from, &meminfo);
  30.  
  31.         if (tags->hdr.tag == ATAG_CORE) {
  32.                 if (meminfo.nr_banks != 0)
  33.                         squash_mem_tags(tags);
  34.                 save_atags(tags);
  35.                 parse_tags(tags);
  36.         }
  37.  
  38.         init_mm.start_code = (unsigned long) &_text;
  39.         init_mm.end_code   = (unsigned long) &_etext;
  40.         init_mm.end_data   = (unsigned long) &_edata;
  41.         init_mm.brk        = (unsigned long) &_end;
  42.  
  43.         memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
  44.         boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
  45.         parse_cmdline(cmdline_p, from);
  46.         paging_init(&meminfo, mdesc);
  47.         request_standard_resources(&meminfo, mdesc);
  48.  
  49. #ifdef CONFIG_SMP
  50.         smp_init_cpus();
  51. #endif
  52.  
  53.         cpu_init();
  54.  
  55.         /*
  56.          * Set up various architecture-specific pointers
  57.          */
  58.         init_arch_irq = mdesc->init_irq;
  59.         system_timer = mdesc->timer;
  60.         init_machine = mdesc->init_machine;
  61.  
  62. #ifdef CONFIG_VT
  63. #if defined(CONFIG_VGA_CONSOLE)
  64.         conswitchp = &vga_con;
  65. #elif defined(CONFIG_DUMMY_CONSOLE)
  66.         conswitchp = &dummy_con;
  67. #endif
  68. #endif
  69.         early_trap_init();
  70. }


 

实就是mdesc->init_irq,经验告诉我们,这个多态函数也是在start_kernel的那一堆初始化函数里挂上去的,但是不容易找到 是哪一个。我们只好来反推,这需要一点经验,其实init_interrupts不止一个地方有,kernel里有很多,我们找到了一个与平台相关的 init_interrupts,对于我的开发板9261来说,是at91sam9261_init_interrupts()这个函数。

/* arch\arm\mach-at91 */

  1. void __init at91sam9261_init_interrupts(unsigned int priority[NR_AIC_IRQS])
  2. {
  3.         if (!priority)
  4.                 priority = at91sam9261_default_irq_priority;
  5.  
  6.         /* Initialize the AIC interrupt controller */
  7.         at91_aic_init(priority);
  8.  
  9.         /* Enable GPIO interrupts */
  10.         at91_gpio_irq_setup();
  11. }

我觉得看代码的时候,不能死盯着代码跳来跳去,这是很低级的,更应该想一想整体的架构,往往都是有一定逻辑的。像我们看到平台相关的初始化,应该能想到,会有其他平台无关的函数来调它,于是grep之,果不其然。

/* arch\arm\mach-at91\board-sam9261ek.c */

  1. static void __init ek_init_irq(void)
  2. {
  3.         at91sam9261_init_interrupts(NULL);
  4. }

再往上grep,我们发现。

 

  1. MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK")
  2.         /* Maintainer: Atmel */
  3.         .phys_io        = AT91_BASE_SYS,
  4.         .io_pg_offst    = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
  5.         .boot_params    = AT91_SDRAM_BASE + 0x100,
  6.         .timer    = &at91sam926x_timer,
  7.         .map_io  = ek_map_io,
  8.         .init_irq       = ek_init_irq,
  9.         .init_machine   = ek_board_init,
  10. MACHINE_END


 

如果你不知道这件马甲似的MACHINE_START...MACHINE_END,把定义找出来。

/* arch\arm\include\asm\mach\arch.h */

  1. #define MACHINE_START(_type,_name)                  \
  2. static const struct machine_desc __mach_desc_##_type    \
  3.  __used       \
  4.  __attribute__((__section__(".arch.info.init"))) = {    \
  5.         .nr          = MACH_TYPE_##_type,  \
  6.         .name      = _name,
  7.  
  8. #define MACHINE_END                    \
  9. };

额,这件马甲里装的是一个结构体,而且这个结构体的类型是,,,struct machine_desc,mdesc->init_irq 中的mdesc就是这个类型的指针,呵呵,应该想到了吧,ek_init_irq就是mdesc->init_irq的一个特例,这确实证据不够确 凿,不过先不用管这么多,说不定你以后看到MACHINE_START...MACHINE_END就知道是干嘛的了。

现在我们重新来看

/* arch\arm\kernel\irq.c */

 

  1. void __init init_IRQ(void)
  2. {
  3.         int irq;
  4.  
  5.         for (irq = 0; irq < NR_IRQS; irq++)
  6.                 irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
  7.  
  8. #ifdef CONFIG_SMP
  9.         bad_irq_desc.affinity = CPU_MASK_ALL;
  10.         bad_irq_desc.cpu = smp_processor_id();
  11. #endif
  12.         init_arch_irq();
  13. }

把上面绕来绕去的在整理一下,得出它的一个特例就是


  1. void __init init_IRQ(void)
  2. {
  3.     int irq;
  4.  
  5.     for (irq = 0; irq < 192; irq++)
  6.         irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
  7.  
  8. #ifdef CONFIG_SMP
  9.     bad_irq_desc.affinity = CPU_MASK_ALL;
  10.     bad_irq_desc.cpu = smp_processor_id();
  11. #endif
  12.  
  13.     /* arch\arm\mach-at91\at91sam9261.c */
  14.  
  15.     /* Initialize the AIC interrupt controller */
  16.     at91_aic_init(priority);
  17.  
  18.     /* Enable GPIO interrupts */
  19.     at91_gpio_irq_setup();
  20. }

 

其实道理很简单,在init_IRQ里需要把ISR和中断号关联起来,通过中断机制是可以读到中断号的,所以只要关联起来就可以索引到ISR。而这里是用一个结构体irq_desc与中断号相关联,而ISR,就包装在这个结构体里面。让我们来看这个诡异的结构体映射方式。

/* include\linux\Irq.h */

  1. struct irq_desc {
  2.         irq_flow_handler_t      handle_irq;
  3.         struct irq_chip  *chip;
  4.         struct msi_desc  *msi_desc;
  5.         void            *handler_data;
  6.         void            *chip_data;
  7.         struct irqaction        *action;        /* IRQ action list */
  8.         unsigned int        status;          /* IRQ status */
  9.  
  10.         unsigned int        depth;            /* nested irq disables */
  11.         unsigned int        wake_depth; /* nested wake enables */
  12.         unsigned int        irq_count;  /* For detecting broken IRQs */
  13.         unsigned int        irqs_unhandled;
  14.         unsigned long      last_unhandled;    /* Aging timer for unhandled count */
  15.         spinlock_t            lock;
  16. #ifdef CONFIG_SMP
  17.         cpumask_t              affinity;
  18.         unsigned int        cpu;
  19. #endif
  20. #if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
  21.         cpumask_t              pending_mask;
  22. #endif
  23. #ifdef CONFIG_PROC_FS
  24.         struct proc_dir_entry   *dir;
  25. #endif
  26.         const char            *name;
  27. } ____cacheline_internodealigned_in_smp;
  28.  
  29. extern struct irq_desc irq_desc[NR_IRQS]



ISR就和那个指针有关,详细点说,那个指针是irqaction链表的头指针,而每个irqaction节点里包含一个ISR

/* include\linux\interrupt.h */

  1. typedef irqreturn_t (*irq_handler_t)(int, void *);
  2.  
  3. struct irqaction {
  4.         irq_handler_t handler;
  5.         unsigned long flags;
  6.         cpumask_t mask;
  7.         const char *name;
  8.         void *dev_id;
  9.         struct irqaction *next;
  10.         int irq;
  11.         struct proc_dir_entry *dir;
  12. };

 

handler是一个函数指针,指向一个类型为irqreturn_t,参数为intvoid*的函数。再回过头去看看我们的ISR

另外把链表这个词记住,以后一提ISR,就想到有那么一个链表。

好,来看那个全局数组,这就是那个索引表,原来建一张索引表就这么容易,就是一个结构体数组。不过数组名和类型名一模一样是不是有点过分阿。

现在看前面那个for循环,就知道,它是针对每个索引,在往空的表项里灌内容,也就是构造表项。其实后面也差不多就是在构造,其中一个核心就应该是把海量的ISR灌到它们应该去的表项里去。最终做到中断号索引和ISR表项能够正确映射。

 

来看     at91_aic_init()

 /* arch\arm\mach-at91\irq.c */

 

  1. void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
  2. {
  3.         unsigned int i;
  4.  
  5.         /*
  6.          * The IVR is used by macro get_irqnr_and_base to read and verify.
  7.          * The irq number is NR_AIC_IRQS when a spurious interrupt has occurred.
  8.          */
  9.         for (i = 0; i < NR_AIC_IRQS; i++) {
  10.                 /* Put irq number in Source Vector Register: */
  11.                 at91_sys_write(AT91_AIC_SVR(i), i);
  12.                 /* Active Low interrupt, with the specified priority */
  13.                 at91_sys_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]);
  14.  
  15.                 set_irq_chip(i, &at91_aic_chip);
  16.                 set_irq_handler(i, handle_level_irq);
  17.                 set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
  18.  
  19.                 /* Perform 8 End Of Interrupt Command to make sure AIC will not Lock out nIRQ */
  20.                 if (i < 8)
  21.                         at91_sys_write(AT91_AIC_EOICR, 0);
  22.         }
  23.  
  24.         /*
  25.          * Spurious Interrupt ID in Spurious Vector Register is NR_AIC_IRQS
  26.          * When there is no current interrupt, the IRQ Vector Register reads the value stored in AIC_SPU
  27.          */
  28.         at91_sys_write(AT91_AIC_SPU, NR_AIC_IRQS);
  29.  
  30.         /* No debugging in AIC: Debug (Protect) Control Register */
  31.         at91_sys_write(AT91_AIC_DCR, 0);
  32.  
  33.         /* Disable and clear all interrupts initially */
  34.         at91_sys_write(AT91_AIC_IDCR, 0xFFFFFFFF);
  35.         at91_sys_write(AT91_AIC_ICCR, 0xFFFFFFFF);
  36. }


 


除了构造索引表就是操作硬件接口,我们只看核心的那句。跳入set_irq_handler()

/* include\linux\Irq.h */

 

  1. static inline void
  2. set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
  3. {
  4.         __set_irq_handler(irq, handle, 0, NULL);
  5. }

 

/* kernel\irq\chip.c */

  1. void
  2. __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
  3.                   const char *name)
  4. {
  5.         struct irq_desc *desc;
  6.         unsigned long flags;
  7.  
  8.         if (irq >= NR_IRQS) {
  9.                 printk(KERN_ERR
  10.                        "Trying to install type control for IRQ%d\n", irq);
  11.                 return;
  12.         }
  13.  
  14.         desc = irq_desc + irq;
  15.  
  16.         if (!handle)
  17.                 handle = handle_bad_irq;
  18.         else if (desc->chip == &no_irq_chip) {
  19.                 printk(KERN_WARNING "Trying to install %sinterrupt handler "
  20.                        "for IRQ%d\n", is_chained ? "chained " : "", irq);
  21.                 /*
  22.                  * Some ARM implementations install a handler for really dumb
  23.                  * interrupt hardware without setting an irq_chip. This worked
  24.                  * with the ARM no_irq_chip but the check in setup_irq would
  25.                  * prevent us to setup the interrupt at all. Switch it to
  26.                  * dummy_irq_chip for easy transition.
  27.                  */
  28.                 desc->chip = &dummy_irq_chip;
  29.         }
  30.  
  31.         spin_lock_irqsave(&desc->lock, flags);
  32.  
  33.         /* Uninstall? */
  34.         if (handle == handle_bad_irq) {
  35.                 if (desc->chip != &no_irq_chip)
  36.                         mask_ack_irq(desc, irq);
  37.                 desc->status |= IRQ_DISABLED;
  38.                 desc->depth = 1;
  39.         }
  40.         desc->handle_irq = handle;
  41.         desc->name = name;
  42.  
  43.         if (handle != handle_bad_irq && is_chained) {
  44.                 desc->status &= ~IRQ_DISABLED;
  45.                 desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
  46.                 desc->depth = 0;
  47.                 desc->chip->unmask(irq);
  48.         }
  49.         spin_unlock_irqrestore(&desc->lock, flags);
  50. }

似曾相识的desc->handle_irq,到上面去找找,之前发现desc->handle_irq(irq, desc)调的不是ISR,那是谁呢?现在看来是handlehandle是什么?传进来的参数,是那个handle_level_irq,跳入。

/ * kernel\irq\chip.c */

 

  1. void
  2. handle_level_irq(unsigned int irq, struct irq_desc *desc)
  3. {
  4.         unsigned int cpu = smp_processor_id();
  5.         struct irqaction *action;
  6.         irqreturn_t action_ret;
  7.  
  8.         spin_lock(&desc->lock);
  9.         mask_ack_irq(desc, irq);
  10.  
  11.         if (unlikely(desc->status & IRQ_INPROGRESS))
  12.                 goto out_unlock;
  13.         desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
  14.         kstat_cpu(cpu).irqs[irq]++;
  15.  
  16.         /*
  17.          * If its disabled or no action available
  18.          * keep it masked and get out of here
  19.          */
  20.         action = desc->action;
  21.         if (unlikely(!action || (desc->status & IRQ_DISABLED)))
  22.                 goto out_unlock;
  23.  
  24.         desc->status |= IRQ_INPROGRESS;
  25.         spin_unlock(&desc->lock);
  26.  
  27.         action_ret = handle_IRQ_event(irq, action);
  28.         if (!noirqdebug)
  29.                 note_interrupt(irq, desc, action_ret);
  30.  
  31.         spin_lock(&desc->lock);
  32.         desc->status &= ~IRQ_INPROGRESS;
  33.         if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
  34.                 desc->chip->unmask(irq);
  35. out_unlock:
  36.         spin_unlock(&desc->lock);
  37. }

 

可爱的参数,终于匹配了,看来desc->handle_irq(irq, desc)调的就是这个handle_level_irq 了,并且,往下看,它还把desc的马甲脱了,唤出了action,并且又调了一个函数。前面说了,action是一个链表头指针,这个链表里就有包含着 ISR阿,看来离调ISR越来越近了。另外要说的是,这里的传进来得desc,是和中断号相匹配的desc表项,不信到前面去看

asmlinkage void __exception asm_do_IRQ()这个函数。这里用全局结构体数组就轻松的实现了一张颇有规模的索引表,还是值得借鉴。好,跳入handle_IRQ_event(irq, action)

/* kernel\irq\handle.c */

 

  1. irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
  2. {
  3.         irqreturn_t ret, retval = IRQ_NONE;
  4.         unsigned int status = 0;
  5.  
  6.         handle_dynamic_tick(action);
  7.  
  8.         if (!(action->flags & IRQF_DISABLED))
  9.                 local_irq_enable_in_hardirq();
  10.  
  11.         do {
  12.                 ret = action->handler(irq, action->dev_id);
  13.                 if (ret == IRQ_HANDLED)
  14.                         status |= action->flags;
  15.                 retval |= ret;
  16.                 action = action->next;
  17.         } while (action);
  18.  
  19.         if (status & IRQF_SAMPLE_RANDOM)
  20.                 add_interrupt_randomness(irq);
  21.         local_irq_disable();
  22.  
  23.         return retval;
  24. }


 

多么明显的一个遍历链表的操作阿,我们看到了action->handler(),这就是ISR!看来一个中断号对应的不是一个ISR,而是一群 ISR。那我们在驱动里注册ISR的时候是不是也是把它注册到一个链表呢,当然是啦,只不过request_irq()封装了这一行为。

/* kernel\irq\manage.c */

 

  1. int request_irq(unsigned int irq, irq_handler_t handler,
  2.                 unsigned long irqflags, const char *devname, void *dev_id)
  3. {
  4.         struct irqaction *action;
  5.         int retval;
  6.  
  7. #ifdef CONFIG_LOCKDEP
  8.         /*
  9.          * Lockdep wants atomic interrupt handlers:
  10.          */
  11.         irqflags |= IRQF_DISABLED;
  12. #endif
  13.         /*
  14.          * Sanity-check: shared interrupts must pass in a real dev-ID,
  15.          * otherwise we'll have trouble later trying to figure out
  16.          * which interrupt is which (messes up the interrupt freeing
  17.          * logic etc).
  18.          */
  19.         if ((irqflags & IRQF_SHARED) && !dev_id)
  20.                 return -EINVAL;
  21.         if (irq >= NR_IRQS)
  22.                 return -EINVAL;
  23.         if (irq_desc[irq].status & IRQ_NOREQUEST)
  24.                 return -EINVAL;
  25.         if (!handler)
  26.                 return -EINVAL;
  27.  
  28.         action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
  29.         if (!action)
  30.                 return -ENOMEM;
  31.  
  32.         action->handler = handler;
  33.         action->flags = irqflags;
  34.         cpus_clear(action->mask);
  35.         action->name = devname;
  36.         action->next = NULL;
  37.         action->dev_id = dev_id;
  38.  
  39. #ifdef CONFIG_DEBUG_SHIRQ
  40.         if (irqflags & IRQF_SHARED) {
  41.                 /*
  42.                  * It's a shared IRQ -- the driver ought to be prepared for it
  43.                  * to happen immediately, so let's make sure....
  44.                  * We do this before actually registering it, to make sure that
  45.                  * a 'real' IRQ doesn't run in parallel with our fake
  46.                  */
  47.                 unsigned long flags;
  48.  
  49.                 local_irq_save(flags);
  50.                 handler(irq, dev_id);
  51.                 local_irq_restore(flags);
  52.         }
  53. #endif
  54.  
  55.         retval = setup_irq(irq, action);
  56.         if (retval)
  57.                 kfree(action);
  58.  
  59.         return retval;
  60. }


 

创建一个action节点,用ISR等成员去构造它,然后把这个节点和对应的中断号扔给上层函数。

/* kernel\irq\manage.c */

 

  1. int setup_irq(unsigned int irq, struct irqaction *new)
  2. {
  3.         struct irq_desc *desc = irq_desc + irq;
  4.         struct irqaction *old, **p;
  5.         const char *old_name = NULL;
  6.         unsigned long flags;
  7.         int shared = 0;
  8.         int ret;
  9.  
  10.         if (irq >= NR_IRQS)
  11.                 return -EINVAL;
  12.  
  13.         if (desc->chip == &no_irq_chip)
  14.                 return -ENOSYS;
  15.         /*
  16.          * Some drivers like serial.c use request_irq() heavily,
  17.          * so we have to be careful not to interfere with a
  18.          * running system.
  19.          */
  20.         if (new->flags & IRQF_SAMPLE_RANDOM) {
  21.                 /*
  22.                  * This function might sleep, we want to call it first,
  23.                  * outside of the atomic block.
  24.                  * Yes, this might clear the entropy pool if the wrong
  25.                  * driver is attempted to be loaded, without actually
  26.                  * installing a new handler, but is this really a problem,
  27.                  * only the sysadmin is able to do this.
  28.                  */
  29.                 rand_initialize_irq(irq);
  30.         }
  31.  
  32.         /*
  33.          * The following block of code has to be executed atomically
  34.          */
  35.         spin_lock_irqsave(&desc->lock, flags);
  36.         p = &desc->action;
  37.         old = *p;
  38.         if (old) {
  39.                 /*
  40.                  * Can't share interrupts unless both agree to and are
  41.                  * the same type (level, edge, polarity). So both flag
  42.                  * fields must have IRQF_SHARED set and the bits which
  43.                  * set the trigger type must match.
  44.                  */
  45.                 if (!((old->flags & new->flags) & IRQF_SHARED) ||
  46.                     ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
  47.                         old_name = old->name;
  48.                         goto mismatch;
  49.                 }
  50.  
  51. #if defined(CONFIG_IRQ_PER_CPU)
  52.                 /* All handlers must agree on per-cpuness */
  53.                 if ((old->flags & IRQF_PERCPU) !=
  54.                     (new->flags & IRQF_PERCPU))
  55.                         goto mismatch;
  56. #endif
  57.  
  58.                 /* add new interrupt at end of irq queue */
  59.                 do {
  60.                         p = &old->next;
  61.                         old = *p;
  62.                 } while (old);
  63.                 shared = 1;
  64.         }
  65.  
  66.         if (!shared) {
  67.                 irq_chip_set_defaults(desc->chip);
  68.  
  69.                 /* Setup the type (level, edge polarity) if configured: */
  70.                 if (new->flags & IRQF_TRIGGER_MASK) {
  71.                         ret = __irq_set_trigger(desc->chip, irq, new->flags);
  72.  
  73.                         if (ret) {
  74.                                 spin_unlock_irqrestore(&desc->lock, flags);
  75.                                 return ret;
  76.                         }
  77.                 } else
  78.                         compat_irq_chip_set_default_handler(desc);
  79. #if defined(CONFIG_IRQ_PER_CPU)
  80.                 if (new->flags & IRQF_PERCPU)
  81.                         desc->status |= IRQ_PER_CPU;
  82. #endif
  83.  
  84.                 desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
  85.                                   IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
  86.  
  87.                 if (!(desc->status & IRQ_NOAUTOEN)) {
  88.                         desc->depth = 0;
  89.                         desc->status &= ~IRQ_DISABLED;
  90.                         if (desc->chip->startup)
  91.                                 desc->chip->startup(irq);
  92.                         else
  93.                                 desc->chip->enable(irq);
  94.                 } else
  95.                         /* Undo nested disables: */
  96.                         desc->depth = 1;
  97.  
  98.                 /* Set default affinity mask once everything is setup */
  99.                 irq_select_affinity(irq);
  100.         }
  101.  
  102.         *p = new;
  103.  
  104.         /* Exclude IRQ from balancing */
  105.         if (new->flags & IRQF_NOBALANCING)
  106.                 desc->status |= IRQ_NO_BALANCING;
  107.  
  108.         /* Reset broken irq detection when installing new handler */
  109.         desc->irq_count = 0;
  110.         desc->irqs_unhandled = 0;
  111.  
  112.         /*
  113.          * Check whether we disabled the irq via the spurious handler
  114.          * before. Reenable it and give it another chance.
  115.          */
  116.         if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) {
  117.                 desc->status &= ~IRQ_SPURIOUS_DISABLED;
  118.                 __enable_irq(desc, irq);
  119.         }
  120.  
  121.         spin_unlock_irqrestore(&desc->lock, flags);
  122.  
  123.         new->irq = irq;
  124.         register_irq_proc(irq);
  125.         new->dir = NULL;
  126.         register_handler_proc(irq, new);
  127.  
  128.         return 0;
  129.  
  130. mismatch:
  131. #ifdef CONFIG_DEBUG_SHIRQ
  132.         if (!(new->flags & IRQF_PROBE_SHARED)) {
  133.                 printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
  134.                 if (old_name)
  135.                         printk(KERN_ERR "current handler: %s\n", old_name);
  136.                 dump_stack();
  137.         }
  138. #endif
  139.         spin_unlock_irqrestore(&desc->lock, flags);
  140.         return -EBUSY;
  141. }

 

根 据中断号,取下索引表中的对应表项,取出其中action链表的表头指针,遍历链表,直到最后一项,将传进来的action节点加入该链表。这样我们的 DM9000 ISR就加入了中断号相关的ISR链表中了。这样arm一旦收到DM9000发出的中断,便通过中断控制器查出中断号,然后根据这个中断号跳入对应的 ISR链表,遍历执行,其中就会执行到我们的DM9000  ISR。而中断肯定是发生在kernel初始化之后,而我们在初始化的时候将中断号与ISR的索引表建立。为实际的中断跳入ISR做好了准备工作。另外提 一下中断号是通过查中断控制器的寄存器得到的,而前面这个函数叫   at91_aic_init()aic全称(Advanced Interrupt Controller)
另外还有个at91_gpio_irq_setup();其实和at91_aic_init()原理差不多,at91_aic_init()映射了中断号 0-31的中断源,31后面的中断源由at91_gpio_irq_setup()负责映射。不过这里面涉及到另外的一些知识,留到后面再讲。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter