S3C2440中的中断处理最终是通过IRQ实现的,在Linux驱动之异常处理体系结构简析已经介绍了IRQ异常的处理过程,最终分析到了一个C函数asm_do_IRQ,接下来继续分析asm_do_IRQ,目标是推导出中断的处理过程。
看到asm_do_irq函数,它位于arch\arm\kernel\Irq.c中。它先根据irq中断号从irq_desc 数组中取出这个中断对应的desc结构体,irq中断号是根据INTOFFSET寄存器的值来确定的,这个寄存器里的值根据中断的来源不同会置位相应的位,它在调用C函数asm_do_IRQ之前被存放在r0中,在C函数中即是irq。
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;//根据irq中断号求出当前中断的desc结构 /* * 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,接着看到desc_handle_irq函数,它是一个内联函数,它位于include\asm-arm\mach\irq.h
/* * Obsolete inline function for calling irq descriptor handlers. */ static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc) { desc->handle_irq(irq, desc);//调用handle_irq处理中断 }
从中可以得出一个很重要的数据结构irq_desc ,它的结构如下
struct irq_desc { irq_flow_handler_t handle_irq;//handle_irq处理函数 struct irq_chip *chip; struct msi_desc *msi_desc; void *handler_data; void *chip_data; struct irqaction *action; /* IRQ action list */action链表 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; 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;
接着搜索handle_irq函数看看它是在哪里被定义的,经过层层分析,最终找到了调用它的顶层函数init_IRQ,下面列出调用过程:其中init_arch_irq函数是由MACHINE_START这个宏定义的,它在start_kernel->setup_arch(init\Main.c)时已经初始化。它是跟单板结构相关的,详情参考Linux移植之内核启动过程引导阶段分析
init_IRQ init_arch_irq();//init_arch_irq = mdesc->init_irq,这个是在machine_desc结构中定义的 s3c24xx_init_irq set_irq_handler __set_irq_handler desc->handle_irq = handle;
接着看到init_arch_irq,即s3c24xx_init_irq(arch\arm\plat-s3c24xx\Irq.c)函数:这个函数设置了irq_desc的一些变量,后面就可以调用这些变量。比如说handle_edge_irq即是最终的中断处理函数
660 void __init s3c24xx_init_irq(void) 661 { ... ... 751 /* external interrupts */ 752 753 for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { 754 irqdbf("registering irq %d (ext int)\n", irqno); 755 set_irq_chip(irqno, &s3c_irq_eint0t4); //设置irq_desc->chip = &s3c_irq_eint0t4 756 set_irq_handler(irqno, handle_edge_irq);//设置irq_desc->handler = handle_edge_irq,处理入口函数 757 set_irq_flags(irqno, IRQF_VALID); //设置irq_desc->status为IRQF_VALID表示可以使用前面定义的这几个函数 758 } ... ... 800 }
接着看到handle_edge_irq,它位于kernel\irq\Chip.c中,它的主要功能是首先是清0中断标志,然后运行中断处理函数handle_IRQ_event
445 void fastcall 446 handle_edge_irq(unsigned int irq, struct irq_desc *desc) 447 { ... ... 468 /* Start handling the irq */ 469 desc->chip->ack(irq);//清0中断标志,响应中断 ... ... 488 if (unlikely((desc->status & 489 (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) == 490 (IRQ_PENDING | IRQ_MASKED))) { 491 desc->chip->unmask(irq);//开启中断 492 desc->status &= ~IRQ_MASKED; 493 } 497 action_ret = handle_IRQ_event(irq, action);//中断处理函数 ... ... 507 }
继续往下看handle_IRQ_event,它位于kernel\irq\Handle.c,它会不断的检查desc结构里的action结构链表,然后调用里面的处理函数action->handler,函数的参数为irq、atcion->dev_id,这个函数就是真正的处理函数,而这个函数可以由用户定义。
129 irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) 130 { ... ... 139 do { 140 ret = action->handler(irq, action->dev_id);//运行handler函数,这个函数是自己构建的 141 if (ret == IRQ_HANDLED) 142 status |= action->flags;//设置状态 143 retval |= ret; 144 action = action->next; 145 } while (action);//检查action链表上是否还有函数未执行 ... ... 152}
那么action->handler这个函数在哪里被定义呢,接着搜索action->handler被定义的地方,最终发现在request_irq,这个函数位于kernel\irq\Manage.c,
500 int request_irq(unsigned int irq, irq_handler_t handler, 501 unsigned long irqflags, const char *devname, void *dev_id) 502 { ... ... 527 action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);//分配一个action结构体 528 if (!action) 529 return -ENOMEM; 530 531 action->handler = handler;//设置处理函数handler 532 action->flags = irqflags; //设置标志irqflags。中断方式 533 cpus_clear(action->mask); 534 action->name = devname; //设置设备名称 535 action->next = NULL; //设置下一个action为空 536 action->dev_id = dev_id; //设置设备id ... ... 559 retval = setup_irq(irq, action);//将上面设备的action结构放入action链表 560 if (retval) 561 kfree(action); 562 563 return retval; 564 }
这个函数的主要作用是构建一个ation结构,然后用request_irq传入的四个参数初始化它,ation结构如下:
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; };
最终会调用setup_irq,将初始化的内容放入action链表,这个函数同样位于kernel\irq\Manage.c。它的功能简述为
1、将新的action放入链表
2、设置中断触发方式
3、启动中断
255 int setup_irq(unsigned int irq, struct irqaction *new) 256 { ... ... 285 /* 286 * The following block of code has to be executed atomically 287 */ 288 spin_lock_irqsave(&desc->lock, flags); 289 p = &desc->action;//获得当前desc结构的action结构 290 old = *p; 291 if (old) {//如果已经存在了action结构 298 if (!((old->flags & new->flags) & IRQF_SHARED) || 299 ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {//判断新的action结构与现有的action结构是否使用相同的触发方式,是否是可共享的 300 old_name = old->name; 301 goto mismatch;//如果不一样,则跳转到mismatch 302 } ... ... 311 /* add new interrupt at end of irq queue */ 312 do {//腾出空间,准备将新的结构放入当前的action链表 313 p = &old->next; 314 old = *p; 315 } while (old); 316 shared = 1; 317 } 318 319 *p = new;//将新的结构放入当前的action链表 ... ... if (!shared) { 326 irq_chip_set_defaults(desc->chip);//设置一些chip结构还没设置的指针 #if defined(CONFIG_IRQ_PER_CPU) if (new->flags & IRQF_PERCPU) desc->status |= IRQ_PER_CPU; #endif /* Setup the type (level, edge polarity) if configured: */ if (new->flags & IRQF_TRIGGER_MASK) { if (desc->chip && desc->chip->set_type) desc->chip->set_type(irq, 337 new->flags & IRQF_TRIGGER_MASK);//设置中断的触发方式,配置EXTINTX寄存器以及GPXCON寄存器 ... ... if (!(desc->status & IRQ_NOAUTOEN)) { desc->depth = 0; desc->status &= ~IRQ_DISABLED; if (desc->chip->startup) 357 desc->chip->startup(irq);//开始中断 else 359 desc->chip->enable(irq);//开始中断 } else /* Undo nested disables: */ desc->depth = 1; } ... ... 387 }
所有如果要注册一个新的中断,那么可以调用request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)来注册。
同样的,如果要消除已经注册的中断,需要调用free_irq(unsigned int irq,void *dev_id)函数来注销,它的执行过程与request刚好相反
1、根据中断号irq、dev_iq从action链表中找到表项,然后移除它
2、如果它是唯一的表项,,则调用desc->chip->shutdown(irq);或desc->chip->disable(irq);来关闭irq号的中断。
以上就是整个linux的中断体系结构的简单描述,更复杂的下半部机制后面深入了解后再去描述。