简介

本片文章的目的主要是为了深入理解request_irq函数。来分析Linux怎么处理中断函数的。

  1. static inline int __must_check
  2. request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
  3. const char *name, void *dev)
  4. request_threaded_irq(irq, handler, NULL, flags, name, dev); # 我们使用 request_irq ,显然不属于线程中断。因此 thread_fn 设置为 thread_fn , 很多时候我们只需要关注 irq 和 handler 就可以了
  5. int request_threaded_irq(unsigned int irq, irq_handler_t handler, # irq 软中断号 中断的回调函数
  6. irq_handler_t thread_fn, unsigned long irqflags,
  7. const char *devname, void *dev_id)
  8. desc = irq_to_desc(irq); # 从 irq_desc 数组返回描述符
  9. action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); # 分配 irqaction 结构体
  10. action->handler = handler; # 中断的回调函数
  11. action->thread_fn = thread_fn; # 线程化的函数
  12. action->flags = irqflags;
  13. action->name = devname;
  14. action->dev_id = dev_id;
  15. # 设置irq
  16. retval = __setup_irq(irq, desc, action);
  17. #

irq_to_desc

这个函数将会通过 irq 来访问 irq_desc 数组。

  1. struct irq_desc *irq_to_desc(unsigned int irq)
  2. {
  3. return (irq < NR_IRQS) ? irq_desc + irq : NULL;
  4. }

irq_desc 数组是怎么定义的了?

  1. struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
  2. [0 ... NR_IRQS-1] = {
  3. .handle_irq = handle_bad_irq,
  4. .depth = 1,
  5. .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
  6. }
  7. };

按照上面的代码,我们可以知道这个数组长度为NR_IRQS

  1. root@zhou 23:14:53 ~/1/1/Linux-4.9.88 # global NR_IRQS | grep
  2. arch/arm/include/asm/irq.h
  3. arch/arm/mach-davinci/include/mach/irqs.h
  4. arch/arm/mach-ebsa110/include/mach/irqs.h
  5. include/asm-generic/irq.h

我并没有在对应的mach文件夹下面找到NR_IRQS ,因此可以推测,是在 include/asm-generic/irq.h 中。

  1. # include/asm-generic/irq.h
  2. #define NR_IRQS 64

__setup_irq

这个函数将会注册一个irqaction 结构体。然后链接到irq_desc结构体中。

  1. static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
  2. old_ptr = &desc->action; # 保存当前的 desc 地址,
  3. old = *old_ptr; # 取old_ptr 指向的地址的数据 地址
  4. # 这里很重要,将传进来的 new 链接到 desc 的成员参数
  5. do {
  6. /*
  7. * Or all existing action->thread_mask bits,
  8. * so we can find the next zero bit for this
  9. * new action.
  10. */
  11. thread_mask |= old->thread_mask;
  12. old_ptr = &old->next; # 获得下一个 irqaction的二级指针
  13. old = *old_ptr; # 得到其地址
  14. } while (old); # 找到一个为NULL 的 irqaction
  15. *old_ptr = new; # 将新的 irqaction 放进来

old_ptr 参数作为一个二级指针。

参考资料

ARM 软中断指令SWI
Linux系统调用列表
韦东山:剥丝抽茧分析Linux中断系统中的重要数据结构
21.7。Linux系统调用
21.8.2. ARM 调用约定