您现在的位置是:主页 > news > 西安企业网站建设哪家专业/做推广怎么赚钱
西安企业网站建设哪家专业/做推广怎么赚钱
admin2025/5/12 18:56:09【news】
简介西安企业网站建设哪家专业,做推广怎么赚钱,最专业网站建设开发,广告文案策划ARM里的中断通常是指IRQ和FIQ,以IRQ来讲,ARM对IRQ的处理过程大概是这样: 外部设备遇到某一事件发出一个IRQ中断给中断控制器,中断控制器对这个IRQ进行硬件上的处理,把一些信息记在中断控制器的寄存器上,…
ARM里的中断通常是指IRQ和FIQ,以IRQ来讲,ARM对IRQ的处理过程大概是这样:
外部设备遇到某一事件发出一个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 */
- __irq_svc:
- svc_entry
- #ifdef CONFIG_TRACE_IRQFLAGS
- bl trace_hardirqs_off
- #endif
- #ifdef CONFIG_PREEMPT
- get_thread_info tsk
- ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
- add r7, r8, #1 @ increment it
- str r7, [tsk, #TI_PREEMPT]
- #endif
- irq_handler
- #ifdef CONFIG_PREEMPT
- str r8, [tsk, #TI_PREEMPT] @ restore preempt count
- ldr r0, [tsk, #TI_FLAGS] @ get flags
- teq r8, #0 @ if preempt count != 0
- movne r0, #0 @ force flags to 0
- tst r0, #_TIF_NEED_RESCHED
- blne svc_preempt
- #endif
- ldr r0, [sp, #S_PSR] @ irqs are already disabled
- msr spsr_cxsf, r0
- #ifdef CONFIG_TRACE_IRQFLAGS
- tst r0, #PSR_I_BIT
- bleq trace_hardirqs_on
- #endif
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
就是高亮的三句。
首尾两句简单,关键是中间的handler,来看handler的源码。
/* arch/arm/kernel/entry-armv.S */
- .macro irq_handler
- get_irqnr_preamble r5, lr
- 1: get_irqnr_and_base r0, r6, r5, lr
- movne r1, sp
- @
- @ routine called with r0 = irq number, r1 = struct pt_regs *
- @
- adrne lr, 1b
- bne asm_do_IRQ
- #ifdef CONFIG_SMP
- /*
- * XXX
- *
- * this macro assumes that irqstat (r6) and base (r5) are
- * preserved from get_irqnr_and_base above
- */
- test_for_ipi r0, r6, r5, lr
- movne r0, sp
- adrne lr, 1b
- bne do_IPI
- #ifdef CONFIG_LOCAL_TIMERS
- test_for_ltirq r0, r6, r5, lr
- movne r0, sp
- adrne lr, 1b
- bne do_local_timer
- #endif
- #endif
- .endm
handler里最重要的事情就是取得中断号,然后根据中断号去索引到具体的ISR。具体的ISR就在Do_asm_IRQ里啦,来看这个函数的源码。
/* arch/arm/kernel/irq.c */
- asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
- {
- struct pt_regs *old_regs = set_irq_regs(regs);
- struct irq_desc *desc = irq_desc + irq;
- /*
- * Some hardware gives randomly wrong interrupts. Rather
- * than crashing, do something sensible.
- */
- if (irq >= NR_IRQS)
- desc = &bad_irq_desc;
- irq_enter();
- desc_handle_irq(irq, desc);
- /* AT91 specific workaround */
- irq_finish(irq);
- irq_exit();
- set_irq_regs(old_regs);
- }
其 中核心的这句话是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 */
- static int
- dm9000_open(struct net_device *dev)
- {
- board_info_t *db = dev->priv;
- unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
- if (netif_msg_ifup(db))
- dev_dbg(db->dev, "enabling %s\n", dev->name);
- /* If there is no IRQ type specified, default to something that
- * may work, and tell the user that this is a problem */
- if (irqflags == IRQF_TRIGGER_NONE)
- dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");
- irqflags |= IRQF_SHARED;
- if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))
- return -EAGAIN;
- /* Initialize DM9000 board */
- dm9000_reset(db);
- dm9000_init_dm9000(dev);
- /* Init driver variable */
- db->dbug_cnt = 0;
- mii_check_media(&db->mii, netif_msg_link(db), 1);
- netif_start_queue(dev);
- dm9000_schedule_poll(db);
- return 0;
- }
好,找到request_irq()就行了,其中有一个参数就是ISR,也就是dm9000_interrupt(),让我们来看这个ISR。
/* drivers\net\dm9000.c */
- static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
- {
- struct net_device *dev = dev_id;
- board_info_t *db = dev->priv;
- int int_status;
- u8 reg_save;
- dm9000_dbg(db, 3, "entering %s\n", __func__);
- /* A real interrupt coming */
- spin_lock(&db->lock);
- /* Save previous register address */
- reg_save = readb(db->io_addr);
- /* Disable all interrupts */
- iow(db, DM9000_IMR, IMR_PAR);
- /* Got DM9000 interrupt status */
- int_status = ior(db, DM9000_ISR); /* Got ISR */
- iow(db, DM9000_ISR, int_status); /* Clear ISR status */
- if (netif_msg_intr(db))
- dev_dbg(db->dev, "interrupt status %02x\n", int_status);
- /* Received the coming packet */
- if (int_status & ISR_PRS)
- dm9000_rx(dev);
- /* Trnasmit Interrupt check */
- if (int_status & ISR_PTS)
- dm9000_tx_done(dev, db);
- if (db->type != TYPE_DM9000E) {
- if (int_status & ISR_LNKCHNG) {
- /* fire a link-change request */
- schedule_delayed_work(&db->phy_poll, 1);
- }
- }
- /* Re-enable interrupt mask */
- iow(db, DM9000_IMR, db->imr_all);
- /* Restore previous register address */
- writeb(reg_save, db->io_addr);
- spin_unlock(&db->lock);
- return IRQ_HANDLED;
- }
这 个很明显就是做的一些中断之后真正需要干的事,所以这是整个中断体系中非常重要的一个环节。你可以不知道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 */
- void __init init_IRQ(void)
- {
- int irq;
- for (irq = 0; irq < NR_IRQS; 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();
- }
先看NR_IRQS,一个宏,在9261这块板子指定的头文件里,定义的是
/* arch\arm\mach-at91\include\mach\irq.h */
- #define NR_AIC_IRQS 32
- /*
- * Acknowledge interrupt with AIC after interrupt has been handled.
- * (by kernel/irq.c)
- */
- #define irq_finish(irq) do { at91_sys_write(AT91_AIC_EOICR, 0); } while (0)
- /*
- * IRQ interrupt symbols are the AT91xxx_ID_* symbols
- * for IRQs handled directly through the AIC, or else the AT91_PIN_*
- * symbols in gpio.h for ones handled indirectly as GPIOs.
- * We make provision for 5 banks of GPIO.
- */
- #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 */
- void (*init_arch_irq)(void) __initdata = NULL;
/* arch\arm\kernel\setup.c */
- void __init setup_arch(char **cmdline_p)
- {
- struct tag *tags = (struct tag *)&init_tags;
- struct machine_desc *mdesc;
- char *from = default_command_line;
- setup_processor();
- mdesc = setup_machine(machine_arch_type);
- machine_name = mdesc->name;
- if (mdesc->soft_reboot)
- reboot_setup("s");
- if (__atags_pointer)
- tags = phys_to_virt(__atags_pointer);
- else if (mdesc->boot_params)
- tags = phys_to_virt(mdesc->boot_params);
- /*
- * If we have the old style parameters, convert them to
- * a tag list.
- */
- if (tags->hdr.tag != ATAG_CORE)
- convert_to_tag_list(tags);
- if (tags->hdr.tag != ATAG_CORE)
- tags = (struct tag *)&init_tags;
- if (mdesc->fixup)
- mdesc->fixup(mdesc, tags, &from, &meminfo);
- if (tags->hdr.tag == ATAG_CORE) {
- if (meminfo.nr_banks != 0)
- squash_mem_tags(tags);
- save_atags(tags);
- parse_tags(tags);
- }
- init_mm.start_code = (unsigned long) &_text;
- init_mm.end_code = (unsigned long) &_etext;
- init_mm.end_data = (unsigned long) &_edata;
- init_mm.brk = (unsigned long) &_end;
- memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
- boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
- parse_cmdline(cmdline_p, from);
- paging_init(&meminfo, mdesc);
- request_standard_resources(&meminfo, mdesc);
- #ifdef CONFIG_SMP
- smp_init_cpus();
- #endif
- cpu_init();
- /*
- * Set up various architecture-specific pointers
- */
- init_arch_irq = mdesc->init_irq;
- system_timer = mdesc->timer;
- init_machine = mdesc->init_machine;
- #ifdef CONFIG_VT
- #if defined(CONFIG_VGA_CONSOLE)
- conswitchp = &vga_con;
- #elif defined(CONFIG_DUMMY_CONSOLE)
- conswitchp = &dummy_con;
- #endif
- #endif
- early_trap_init();
- }
其 实就是mdesc->init_irq,经验告诉我们,这个多态函数也是在start_kernel的那一堆初始化函数里挂上去的,但是不容易找到 是哪一个。我们只好来反推,这需要一点经验,其实init_interrupts不止一个地方有,kernel里有很多,我们找到了一个与平台相关的 init_interrupts,对于我的开发板9261来说,是at91sam9261_init_interrupts()这个函数。
/* arch\arm\mach-at91 */
- void __init at91sam9261_init_interrupts(unsigned int priority[NR_AIC_IRQS])
- {
- if (!priority)
- priority = at91sam9261_default_irq_priority;
- /* Initialize the AIC interrupt controller */
- at91_aic_init(priority);
- /* Enable GPIO interrupts */
- at91_gpio_irq_setup();
- }
我觉得看代码的时候,不能死盯着代码跳来跳去,这是很低级的,更应该想一想整体的架构,往往都是有一定逻辑的。像我们看到平台相关的初始化,应该能想到,会有其他平台无关的函数来调它,于是grep之,果不其然。
/* arch\arm\mach-at91\board-sam9261ek.c */
- static void __init ek_init_irq(void)
- {
- at91sam9261_init_interrupts(NULL);
- }
再往上grep,我们发现。
- MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK")
- /* Maintainer: Atmel */
- .phys_io = AT91_BASE_SYS,
- .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
- .boot_params = AT91_SDRAM_BASE + 0x100,
- .timer = &at91sam926x_timer,
- .map_io = ek_map_io,
- .init_irq = ek_init_irq,
- .init_machine = ek_board_init,
- MACHINE_END
如果你不知道这件马甲似的MACHINE_START...MACHINE_END,把定义找出来。
/* arch\arm\include\asm\mach\arch.h */
- #define MACHINE_START(_type,_name) \
- static const struct machine_desc __mach_desc_##_type \
- __used \
- __attribute__((__section__(".arch.info.init"))) = { \
- .nr = MACH_TYPE_##_type, \
- .name = _name,
- #define MACHINE_END \
- };
额,这件马甲里装的是一个结构体,而且这个结构体的类型是,,,struct machine_desc,mdesc->init_irq 中的mdesc就是这个类型的指针,呵呵,应该想到了吧,ek_init_irq就是mdesc->init_irq的一个特例,这确实证据不够确 凿,不过先不用管这么多,说不定你以后看到MACHINE_START...MACHINE_END就知道是干嘛的了。
现在我们重新来看
/* arch\arm\kernel\irq.c */
- void __init init_IRQ(void)
- {
- int irq;
- for (irq = 0; irq < NR_IRQS; 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();
- }
把上面绕来绕去的在整理一下,得出它的一个特例就是
- void __init init_IRQ(void)
- {
- int irq;
- for (irq = 0; irq < 192; 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
- /* arch\arm\mach-at91\at91sam9261.c */
- /* Initialize the AIC interrupt controller */
- at91_aic_init(priority);
- /* Enable GPIO interrupts */
- at91_gpio_irq_setup();
- }
其实道理很简单,在init_IRQ里需要把ISR和中断号关联起来,通过中断机制是可以读到中断号的,所以只要关联起来就可以索引到ISR。而这里是用一个结构体irq_desc与中断号相关联,而ISR,就包装在这个结构体里面。让我们来看这个诡异的结构体映射方式。
/* include\linux\Irq.h */
- struct irq_desc {
- 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
- #if defined(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_internodealigned_in_smp;
- extern struct irq_desc irq_desc[NR_IRQS];
ISR就和那个指针有关,详细点说,那个指针是irqaction链表的头指针,而每个irqaction节点里包含一个ISR。
/* include\linux\interrupt.h */
- typedef irqreturn_t (*irq_handler_t)(int, void *);
- struct irqaction {
- irq_handler_t handler;
- unsigned long flags;
- cpumask_t mask;
- const char *name;
- void *dev_id;
- struct irqaction *next;
- int irq;
- struct proc_dir_entry *dir;
- };
handler是一个函数指针,指向一个类型为irqreturn_t,参数为int和void*的函数。再回过头去看看我们的ISR。
另外把链表这个词记住,以后一提ISR,就想到有那么一个链表。
好,来看那个全局数组,这就是那个索引表,原来建一张索引表就这么容易,就是一个结构体数组。不过数组名和类型名一模一样是不是有点过分阿。
现在看前面那个for循环,就知道,它是针对每个索引,在往空的表项里灌内容,也就是构造表项。其实后面也差不多就是在构造,其中一个核心就应该是把海量的ISR灌到它们应该去的表项里去。最终做到中断号索引和ISR表项能够正确映射。
来看 at91_aic_init()。
/* arch\arm\mach-at91\irq.c */
- void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
- {
- unsigned int i;
- /*
- * The IVR is used by macro get_irqnr_and_base to read and verify.
- * The irq number is NR_AIC_IRQS when a spurious interrupt has occurred.
- */
- for (i = 0; i < NR_AIC_IRQS; i++) {
- /* Put irq number in Source Vector Register: */
- at91_sys_write(AT91_AIC_SVR(i), i);
- /* Active Low interrupt, with the specified priority */
- at91_sys_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]);
- set_irq_chip(i, &at91_aic_chip);
- set_irq_handler(i, handle_level_irq);
- set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
- /* Perform 8 End Of Interrupt Command to make sure AIC will not Lock out nIRQ */
- if (i < 8)
- at91_sys_write(AT91_AIC_EOICR, 0);
- }
- /*
- * Spurious Interrupt ID in Spurious Vector Register is NR_AIC_IRQS
- * When there is no current interrupt, the IRQ Vector Register reads the value stored in AIC_SPU
- */
- at91_sys_write(AT91_AIC_SPU, NR_AIC_IRQS);
- /* No debugging in AIC: Debug (Protect) Control Register */
- at91_sys_write(AT91_AIC_DCR, 0);
- /* Disable and clear all interrupts initially */
- at91_sys_write(AT91_AIC_IDCR, 0xFFFFFFFF);
- at91_sys_write(AT91_AIC_ICCR, 0xFFFFFFFF);
- }
除了构造索引表就是操作硬件接口,我们只看核心的那句。跳入set_irq_handler()。
/* include\linux\Irq.h */
- static inline void
- set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
- {
- __set_irq_handler(irq, handle, 0, NULL);
- }
/* kernel\irq\chip.c */
- void
- __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
- const char *name)
- {
- struct irq_desc *desc;
- unsigned long flags;
- if (irq >= NR_IRQS) {
- printk(KERN_ERR
- "Trying to install type control for IRQ%d\n", irq);
- return;
- }
- desc = irq_desc + irq;
- if (!handle)
- handle = handle_bad_irq;
- else if (desc->chip == &no_irq_chip) {
- printk(KERN_WARNING "Trying to install %sinterrupt handler "
- "for IRQ%d\n", is_chained ? "chained " : "", irq);
- /*
- * Some ARM implementations install a handler for really dumb
- * interrupt hardware without setting an irq_chip. This worked
- * with the ARM no_irq_chip but the check in setup_irq would
- * prevent us to setup the interrupt at all. Switch it to
- * dummy_irq_chip for easy transition.
- */
- desc->chip = &dummy_irq_chip;
- }
- spin_lock_irqsave(&desc->lock, flags);
- /* Uninstall? */
- if (handle == handle_bad_irq) {
- if (desc->chip != &no_irq_chip)
- mask_ack_irq(desc, irq);
- desc->status |= IRQ_DISABLED;
- desc->depth = 1;
- }
- 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->unmask(irq);
- }
- spin_unlock_irqrestore(&desc->lock, flags);
- }
似曾相识的desc->handle_irq,到上面去找找,之前发现desc->handle_irq(irq, desc)调的不是ISR,那是谁呢?现在看来是handle,handle是什么?传进来的参数,是那个handle_level_irq,跳入。
/ * kernel\irq\chip.c */
- void
- handle_level_irq(unsigned int irq, struct irq_desc *desc)
- {
- unsigned int cpu = smp_processor_id();
- struct irqaction *action;
- irqreturn_t action_ret;
- spin_lock(&desc->lock);
- mask_ack_irq(desc, irq);
- if (unlikely(desc->status & IRQ_INPROGRESS))
- goto out_unlock;
- desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
- kstat_cpu(cpu).irqs[irq]++;
- /*
- * If its disabled or no action available
- * keep it masked and get out of here
- */
- action = desc->action;
- if (unlikely(!action || (desc->status & IRQ_DISABLED)))
- goto out_unlock;
- desc->status |= IRQ_INPROGRESS;
- spin_unlock(&desc->lock);
- action_ret = handle_IRQ_event(irq, action);
- if (!noirqdebug)
- note_interrupt(irq, desc, action_ret);
- spin_lock(&desc->lock);
- desc->status &= ~IRQ_INPROGRESS;
- if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
- desc->chip->unmask(irq);
- out_unlock:
- spin_unlock(&desc->lock);
- }
可爱的参数,终于匹配了,看来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 */
- irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
- {
- irqreturn_t ret, retval = IRQ_NONE;
- unsigned int status = 0;
- handle_dynamic_tick(action);
- 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;
- }
多么明显的一个遍历链表的操作阿,我们看到了action->handler(),这就是ISR!看来一个中断号对应的不是一个ISR,而是一群 ISR。那我们在驱动里注册ISR的时候是不是也是把它注册到一个链表呢,当然是啦,只不过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)
- {
- struct irqaction *action;
- 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;
- if (irq >= NR_IRQS)
- return -EINVAL;
- if (irq_desc[irq].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;
- #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 do this before actually registering it, to make sure that
- * a 'real' IRQ doesn't run in parallel with our fake
- */
- unsigned long flags;
- local_irq_save(flags);
- handler(irq, dev_id);
- local_irq_restore(flags);
- }
- #endif
- retval = setup_irq(irq, action);
- if (retval)
- kfree(action);
- return retval;
- }
创建一个action节点,用ISR等成员去构造它,然后把这个节点和对应的中断号扔给上层函数。
/* kernel\irq\manage.c */
- int setup_irq(unsigned int irq, struct irqaction *new)
- {
- struct irq_desc *desc = irq_desc + irq;
- struct irqaction *old, **p;
- const char *old_name = NULL;
- unsigned long flags;
- int shared = 0;
- int ret;
- if (irq >= NR_IRQS)
- return -EINVAL;
- if (desc->chip == &no_irq_chip)
- return -ENOSYS;
- /*
- * Some drivers like serial.c use request_irq() heavily,
- * so we have to be careful not to interfere with a
- * running system.
- */
- if (new->flags & IRQF_SAMPLE_RANDOM) {
- /*
- * This function might sleep, we want to call it first,
- * outside of the atomic block.
- * Yes, this might clear the entropy pool if the wrong
- * driver is attempted to be loaded, without actually
- * installing a new handler, but is this really a problem,
- * only the sysadmin is able to do this.
- */
- rand_initialize_irq(irq);
- }
- /*
- * The following block of code has to be executed atomically
- */
- spin_lock_irqsave(&desc->lock, flags);
- p = &desc->action;
- old = *p;
- if (old) {
- /*
- * Can't share interrupts unless both agree to and are
- * the same type (level, edge, polarity). So both flag
- * fields must have IRQF_SHARED set and the bits which
- * set the trigger type must match.
- */
- if (!((old->flags & new->flags) & IRQF_SHARED) ||
- ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
- old_name = old->name;
- goto mismatch;
- }
- #if defined(CONFIG_IRQ_PER_CPU)
- /* All handlers must agree on per-cpuness */
- if ((old->flags & IRQF_PERCPU) !=
- (new->flags & IRQF_PERCPU))
- goto mismatch;
- #endif
- /* 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->chip, irq, new->flags);
- if (ret) {
- spin_unlock_irqrestore(&desc->lock, flags);
- return ret;
- }
- } else
- compat_irq_chip_set_default_handler(desc);
- #if defined(CONFIG_IRQ_PER_CPU)
- if (new->flags & IRQF_PERCPU)
- desc->status |= IRQ_PER_CPU;
- #endif
- desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
- IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
- if (!(desc->status & IRQ_NOAUTOEN)) {
- desc->depth = 0;
- desc->status &= ~IRQ_DISABLED;
- if (desc->chip->startup)
- desc->chip->startup(irq);
- else
- desc->chip->enable(irq);
- } else
- /* Undo nested disables: */
- desc->depth = 1;
- /* Set default affinity mask once everything is setup */
- irq_select_affinity(irq);
- }
- *p = new;
- /* Exclude IRQ from balancing */
- if (new->flags & IRQF_NOBALANCING)
- desc->status |= IRQ_NO_BALANCING;
- /* 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);
- new->dir = NULL;
- register_handler_proc(irq, new);
- return 0;
- mismatch:
- #ifdef CONFIG_DEBUG_SHIRQ
- if (!(new->flags & IRQF_PROBE_SHARED)) {
- printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
- if (old_name)
- printk(KERN_ERR "current handler: %s\n", old_name);
- dump_stack();
- }
- #endif
- spin_unlock_irqrestore(&desc->lock, flags);
- return -EBUSY;
- }
根 据中断号,取下索引表中的对应表项,取出其中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()负责映射。不过这里面涉及到另外的一些知识,留到后面再讲。