2021-10-09

1. 中断的概念

中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的 CPU 暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)。

软件对硬件进行配置后,软件期望等待硬件的某种状态(比如,收到了数据),这里有两种方式,一种是轮询(polling): CPU 不断的去读硬件状态。另一种是当硬件完成某种事件后,给 CPU 一个中断,让 CPU 停下手上的事情,去处理这个中断。很显然,中断的交互方式提高了系统的吞吐。

当 CPU 收到一个中断 (IRQ)的时候,会去执行该中断对应的处理函数(ISR)。普通情况下,会有一个中断向量表,向量表中定义了 CPU 对应的每一个外设资源的中断处理程序的入口,当发生对应的中断的时候, CPU 直接跳转到这个入口执行程序。也就是中断上下文。(注意:中断上下文中,不可阻塞睡眠)。

 

2. Linux 中断 top/bottom

玩过 MCU 的人都知道,中断服务程序的设计最好是快速完成任务并退出,因为此刻系统处于被中断中。但是在 ISR 中又有一些必须完成的事情,比如:清中断标志,读/写数据,寄存器操作等。

在 Linux 中,同样也是这个要求,希望尽快的完成 ISR。但事与愿违,有些 ISR 中任务繁重,会消耗很多时间,导致响应速度变差。Linux 中针对这种情况,将中断分为了两部分:

1. 上半部(top half):收到一个中断,立即执行,有严格的时间限制,只做一些必要的工作,比如:应答,复位等。这些工作都是在所有中断被禁止的情况下完成的

2. 底半部(bottom half):能够被推迟到后面完成的任务会在底半部进行。在适合的时机,下半部会被开中断执行。(具体的机制在下一篇文章分析)

 

3. 中断处理程序

驱动程序可以使用接口:


   
   
  1. request_irq( unsigned int irq, irq_handler_t handler, unsigned long flags,
  2. const char *name, void *dev)

像系统申请注册一个中断处理程序。

其中的参数:

参数含义
irq表了该中断的中断号,一般 CPU 的中断号都会事先定义好。
handler中断发生后的 ISR
flags中断标志( IRQF_DISABLED / IRQFSAMPLE_RANDOM / IRQF_TIMER / IRQF_SHARED)
name 中断相关的设备 ASCII 文本,例如 "keyboard",这些名字会在 /proc/irq 和 /proc/interrupts 文件使用
dev用于共享中断线,传递驱动程序的设备结构。非共享类型的中断,直接设置成为 NULL

 

 

 

 

 

 

 

中断标志 flag 的含义:

标志含义
IRQF_DISABLED设置这个标志的话,意味着内核在处理这个 ISR 期间,要禁止其他中断(多数情况不使用这个)
IRQFSAMPLE_RANDOM表明这个设备产生的中断对内核熵池有贡献
IRQF_TIMER为系统定时器准备的标志
IRQF_SHARED表明多个中断处理程序之间共享中断线。同一个给定的线上注册每个处理程序,必须设置这个

 

 

 

 

 

 

调用 request _irq 成功执行返回 0。常见错误是 -EBUSY,表示给定的中断线已经在使用(或者没有指定 IRQF_SHARED)

注意:request_irq 函数可能引起睡眠,所以不允许在中断上下文或者不允许睡眠的代码中调用

 

释放中断:

const void *free_irq(unsigned int irq, void *dev_id)
   
   

用于释放中断处理函数。

注意:Linux 中的中断处理程序是无须重入的。当给定的中断处理程序正在执行的时候,其中断线在所有的处理器上都会被屏蔽掉,以防在同一个中断线上又接收到另一个新的中断。通常情况下,除了该中断的其他中断都是打开的,也就是说其他的中断线上的重点都能够被处理,但是当前的中断线总是被禁止的,故,同一个中断处理程序是绝对不会被自己嵌套的

 

4. 中断上下文

与进程上下文不一样,内核执行中断服务程序的时候,处于中断上下文。中断处理程序并没有自己的独立的栈,而是使用了内核栈,其大小一般是有限制的(32bit 机器 8KB)。所以其必须短小精悍。同时中断服务程序是打断了正常的程序流程,这一点上也必须保证快速的执行。同时中断上下文中是不允许睡眠,阻塞的。

中断上下文不能睡眠的原因是

1、 中断处理的时候,不应该发生进程切换,因为在中断context中,唯一能打断当前中断handler的只有更高优先级的中断,它不会被进程打断,如果在 中断context中休眠,则没有办法唤醒它,因为所有的wake_up_xxx都是针对某个进程而言的,而在中断context中,没有进程的概念,没 有一个task_struct(这点对于softirq和tasklet一样),因此真的休眠了,比如调用了会导致block的例程,内核几乎肯定会死。

2、schedule()在切换进程时,保存当前的进程上下文(CPU寄存器的值、进程的状态以及堆栈中的内容),以便以后恢复此进程运行。中断发生后,内核会先保存当前被中断的进程上下文(在调用中断处理程序后恢复);

但在中断处理程序里,CPU寄存器的值肯定已经变化了吧(最重要的程序计数器PC、堆栈SP等),如果此时因为睡眠或阻塞操作调用了schedule(),则保存的进程上下文就不是当前的进程context了.所以不可以在中断处理程序中调用schedule()。

3、内核中schedule()函数本身在进来的时候判断是否处于中断上下文:

if(unlikely(in_interrupt()))

BUG();

因此,强行调用schedule()的结果就是内核BUG。

4、中断handler会使用被中断的进程内核堆栈,但不会对它有任何影响,因为handler使用完后会完全清除它使用的那部分堆栈,恢复被中断前的原貌。

5、处于中断context时候,内核是不可抢占的。因此,如果休眠,则内核一定挂起。

 

5. 举例

比如 RTC 驱动程序 (drivers/char/rtc.c)。在 RTC 驱动的初始化阶段,会调用到 rtc_init 函数:

module_init(rtc_init);
   
   

在这个初始化函数中调用到了 request_irq 用于申请中断资源,并注册服务程序:


   
   
  1. static int __init rtc_init(void)
  2. {
  3. ...
  4. rtc_int_handler_ptr = rtc_interrupt;
  5. ...
  6. request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)
  7. ...
  8. }

RTC_IRQ 是中断号,和处理器绑定。

rtc_interrupt 是中断处理程序:


   
   
  1. static irqreturn_t rtc_interrupt(int irq, void *dev_id)
  2. {
  3. /*
  4. * Can be an alarm interrupt, update complete interrupt,
  5. * or a periodic interrupt. We store the status in the
  6. * low byte and the number of interrupts received since
  7. * the last read in the remainder of rtc_irq_data.
  8. */
  9. spin_lock(&rtc_lock);
  10. rtc_irq_data += 0x100;
  11. rtc_irq_data &= ~ 0xff;
  12. if (is_hpet_enabled()) {
  13. /*
  14. * In this case it is HPET RTC interrupt handler
  15. * calling us, with the interrupt information
  16. * passed as arg1, instead of irq.
  17. */
  18. rtc_irq_data |= ( unsigned long)irq & 0xF0;
  19. } else {
  20. rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
  21. }
  22. if (rtc_status & RTC_TIMER_ON)
  23. mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/ 100);
  24. spin_unlock(&rtc_lock);
  25. wake_up_interruptible(&rtc_wait);
  26. kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
  27. return IRQ_HANDLED;
  28. }

每次收到 RTC 中断,就会调用进这个函数。

 

6. 中断处理流程

发生中断时,CPU执行异常向量vector_irq的代码, 即异常向量表中的中断异常的代码,它是一个跳转指令,跳去执行真正的中断处理程序,在vector_irq里面,最终会调用中断处理的总入口函数。

C 语言的入口为 : asm_do_IRQ(unsigned int irq, struct pt_regs *regs)


   
   
  1. asmlinkage void __exception_irq_entry
  2. asm_do_IRQ (unsigned int irq, struct pt_regs *regs)
  3. {
  4. handle_IRQ(irq, regs);
  5. }

该函数的入参 irq 为中断号。

asm_do_IRQ -> handle_IRQ


   
   
  1. void handle_IRQ(unsigned int irq, struct pt_regs *regs)
  2. {
  3. __handle_domain_irq( NULL, irq, false, regs);
  4. }

handle_IRQ -> __handle_domain_irq


   
   
  1. int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
  2. bool lookup, struct pt_regs *regs)
  3. {
  4. struct pt_regs *old_regs = set_irq_regs(regs);
  5. unsigned int irq = hwirq;
  6. int ret = 0;
  7. irq_enter();
  8. #ifdef CONFIG_IRQ_DOMAIN
  9. if (lookup)
  10. irq = irq_find_mapping(domain, hwirq);
  11. #endif
  12. /*
  13. * Some hardware gives randomly wrong interrupts. Rather
  14. * than crashing, do something sensible.
  15. */
  16. if (unlikely(!irq || irq >= nr_irqs)) {
  17. ack_bad_irq(irq);
  18. ret = -EINVAL;
  19. } else {
  20. generic_handle_irq(irq);
  21. }
  22. irq_exit();
  23. set_irq_regs(old_regs);
  24. return ret;
  25. }

这里请注意:

先调用了 irq_enter 标记进入了硬件中断:

irq_enter是更新一些系统的统计信息,同时在__irq_enter宏中禁止了进程的抢占。虽然在产生IRQ时,ARM会自动把CPSR中的I位置位,禁止新的IRQ请求,直到中断控制转到相应的流控层后才通过local_irq_enable()打开。那为何还要禁止抢占?这是因为要考虑中断嵌套的问题,一旦流控层或驱动程序主动通过local_irq_enable打开了IRQ,而此时该中断还没处理完成,新的irq请求到达,这时代码会再次进入irq_enter,在本次嵌套中断返回时,内核不希望进行抢占调度,而是要等到最外层的中断处理完成后才做出调度动作,所以才有了禁止抢占这一处理

再调用 generic_handle_irq

最后调用 irq_exit 删除进入硬件中断的标记

__handle_domain_irq -> generic_handle_irq


   
   
  1. int generic_handle_irq(unsigned int irq)
  2. {
  3. struct irq_desc *desc = irq_to_desc(irq);
  4. if (!desc)
  5. return -EINVAL;
  6. generic_handle_irq_desc(desc);
  7. return 0;
  8. }
  9. EXPORT_SYMBOL_GPL(generic_handle_irq);

首先在函数 irq_to_desc 中根据发生中断的中断号,去取出它的 irq_desc 中断描述结构,然后调用 generic_handle_irq_desc


   
   
  1. static inline void generic_handle_irq_desc(struct irq_desc *desc)
  2. {
  3. desc->handle_irq(desc);
  4. }

这里调用了 handle_irq 函数。

所以,在上述流程中,还需要分析 irq_to_desc  流程:


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

NR_IRQS 是支持的总的中断个数,当然,irq 不能够大于这个数目。所以返回 irq_desc + irq。

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. };

这里是这个数组的初始化的地方。所有的 handle_irq 函数都被初始化成为了 handle_bad_irq。

细心的观众可能发现了,调用这个 desc->handle_irq(desc) 函数,并不是咱们注册进去的中断处理函数啊,因为两个函数的原型定义都不一样。这个 handle_irq irq_flow_handler_t 类型,而我们注册进去的服务程序是 irq_handler_t,这两个明显不是同一个东西,所以这里我们还需要继续分析。

 

6.1 中断相关的数据结构

Linux 中断相关的数据结构有 3 个

结构名称作用
irq_descIRQ 的软件层面上的资源描述
irqactionIRQ 的通用操作
irq_chip对应每个芯片的具体实现

 

 

 

 

 

irq_desc 结构如下


   
   
  1. struct irq_desc {
  2. struct irq_common_data irq_common_data;
  3. struct irq_data irq_data;
  4. unsigned int __percpu *kstat_irqs;
  5. irq_flow_handler_t handle_irq;
  6. #ifdef CONFIG_IRQ_PREFLOW_FASTEOI
  7. irq_preflow_handler_t preflow_handler;
  8. #endif
  9. struct irqaction *action; /* IRQ action list */
  10. unsigned int status_use_accessors;
  11. unsigned int core_internal_state__do_not_mess_with_it;
  12. unsigned int depth; /* nested irq disables */
  13. unsigned int wake_depth; /* nested wake enables */
  14. unsigned int irq_count; /* For detecting broken IRQs */
  15. unsigned long last_unhandled; /* Aging timer for unhandled count */
  16. unsigned int irqs_unhandled;
  17. atomic_t threads_handled;
  18. int threads_handled_last;
  19. raw_spinlock_t lock;
  20. struct cpumask *percpu_enabled;
  21. const struct cpumask *percpu_affinity;
  22. #ifdef CONFIG_SMP
  23. const struct cpumask *affinity_hint;
  24. struct irq_affinity_notify *affinity_notify;
  25. #ifdef CONFIG_GENERIC_PENDING_IRQ
  26. cpumask_var_t pending_mask;
  27. #endif
  28. #endif
  29. unsigned long threads_oneshot;
  30. atomic_t threads_active;
  31. wait_queue_head_t wait_for_threads;
  32. #ifdef CONFIG_PM_SLEEP
  33. unsigned int nr_actions;
  34. unsigned int no_suspend_depth;
  35. unsigned int cond_suspend_depth;
  36. unsigned int force_resume_depth;
  37. #endif
  38. #ifdef CONFIG_PROC_FS
  39. struct proc_dir_entry *dir;
  40. #endif
  41. #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
  42. struct dentry *debugfs_file;
  43. const char *dev_name;
  44. #endif
  45. #ifdef CONFIG_SPARSE_IRQ
  46. struct rcu_head rcu;
  47. struct kobject kobj;
  48. #endif
  49. struct mutex request_mutex;
  50. int parent_irq;
  51. struct module *owner;
  52. const char *name;
  53. } ____cacheline_internodealigned_in_smp;

 

irqaction 结构如下:


   
   
  1. /**
  2. * struct irqaction - per interrupt action descriptor
  3. * @handler: interrupt handler function
  4. * @name: name of the device
  5. * @dev_id: cookie to identify the device
  6. * @percpu_dev_id: cookie to identify the device
  7. * @next: pointer to the next irqaction for shared interrupts
  8. * @irq: interrupt number
  9. * @flags: flags (see IRQF_* above)
  10. * @thread_fn: interrupt handler function for threaded interrupts
  11. * @thread: thread pointer for threaded interrupts
  12. * @secondary: pointer to secondary irqaction (force threading)
  13. * @thread_flags: flags related to @thread
  14. * @thread_mask: bitmask for keeping track of @thread activity
  15. * @dir: pointer to the proc/irq/NN/name entry
  16. */
  17. struct irqaction {
  18. irq_handler_t handler;
  19. void *dev_id;
  20. void __percpu *percpu_dev_id;
  21. struct irqaction *next;
  22. irq_handler_t thread_fn;
  23. struct task_struct *thread;
  24. struct irqaction *secondary;
  25. unsigned int irq;
  26. unsigned int flags;
  27. unsigned long thread_flags;
  28. unsigned long thread_mask;
  29. const char *name;
  30. struct proc_dir_entry *dir;
  31. } ____cacheline_internodealigned_in_smp;

 

irq_chip 描述如下:


   
   
  1. /**
  2. * struct irq_chip - hardware interrupt chip descriptor
  3. *
  4. * @parent_device: pointer to parent device for irqchip
  5. * @name: name for /proc/interrupts
  6. * @irq_startup: start up the interrupt (defaults to ->enable if NULL)
  7. * @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
  8. * @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
  9. * @irq_disable: disable the interrupt
  10. * @irq_ack: start of a new interrupt
  11. * @irq_mask: mask an interrupt source
  12. * @irq_mask_ack: ack and mask an interrupt source
  13. * @irq_unmask: unmask an interrupt source
  14. * @irq_eoi: end of interrupt
  15. * @irq_set_affinity: Set the CPU affinity on SMP machines. If the force
  16. * argument is true, it tells the driver to
  17. * unconditionally apply the affinity setting. Sanity
  18. * checks against the supplied affinity mask are not
  19. * required. This is used for CPU hotplug where the
  20. * target CPU is not yet set in the cpu_online_mask.
  21. * @irq_retrigger: resend an IRQ to the CPU
  22. * @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
  23. * @irq_set_wake: enable/disable power-management wake-on of an IRQ
  24. * @irq_bus_lock: function to lock access to slow bus (i2c) chips
  25. * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
  26. * @irq_cpu_online: configure an interrupt source for a secondary CPU
  27. * @irq_cpu_offline: un-configure an interrupt source for a secondary CPU
  28. * @irq_suspend: function called from core code on suspend once per
  29. * chip, when one or more interrupts are installed
  30. * @irq_resume: function called from core code on resume once per chip,
  31. * when one ore more interrupts are installed
  32. * @irq_pm_shutdown: function called from core code on shutdown once per chip
  33. * @irq_calc_mask: Optional function to set irq_data.mask for special cases
  34. * @irq_print_chip: optional to print special chip info in show_interrupts
  35. * @irq_request_resources: optional to request resources before calling
  36. * any other callback related to this irq
  37. * @irq_release_resources: optional to release resources acquired with
  38. * irq_request_resources
  39. * @irq_compose_msi_msg: optional to compose message content for MSI
  40. * @irq_write_msi_msg: optional to write message content for MSI
  41. * @irq_get_irqchip_state: return the internal state of an interrupt
  42. * @irq_set_irqchip_state: set the internal state of a interrupt
  43. * @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine
  44. * @ipi_send_single: send a single IPI to destination cpus
  45. * @ipi_send_mask: send an IPI to destination cpus in cpumask
  46. * @flags: chip specific flags
  47. */
  48. struct irq_chip {
  49. struct device *parent_device;
  50. const char *name;
  51. unsigned int (*irq_startup)(struct irq_data *data);
  52. void (*irq_shutdown)(struct irq_data *data);
  53. void (*irq_enable)(struct irq_data *data);
  54. void (*irq_disable)(struct irq_data *data);
  55. void (*irq_ack)(struct irq_data *data);
  56. void (*irq_mask)(struct irq_data *data);
  57. void (*irq_mask_ack)(struct irq_data *data);
  58. void (*irq_unmask)(struct irq_data *data);
  59. void (*irq_eoi)(struct irq_data *data);
  60. int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
  61. int (*irq_retrigger)(struct irq_data *data);
  62. int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
  63. int (*irq_set_wake)(struct irq_data *data, unsigned int on);
  64. void (*irq_bus_lock)(struct irq_data *data);
  65. void (*irq_bus_sync_unlock)(struct irq_data *data);
  66. void (*irq_cpu_online)(struct irq_data *data);
  67. void (*irq_cpu_offline)(struct irq_data *data);
  68. void (*irq_suspend)(struct irq_data *data);
  69. void (*irq_resume)(struct irq_data *data);
  70. void (*irq_pm_shutdown)(struct irq_data *data);
  71. void (*irq_calc_mask)(struct irq_data *data);
  72. void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
  73. int (*irq_request_resources)(struct irq_data *data);
  74. void (*irq_release_resources)(struct irq_data *data);
  75. void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
  76. void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
  77. int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
  78. int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
  79. int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
  80. void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
  81. void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
  82. unsigned long flags;
  83. };

irq_chip 是一串和芯片相关的函数指针,这里定义的非常的全面,基本上和 IRQ 相关的可能出现的操作都全部定义进去了,具体根据不同的芯片,需要在不同的芯片的地方去初始化这个结构,然后这个结构会嵌入到通用的 IRQ 处理软件中去使用,使得软件处理逻辑和芯片逻辑完全的分开。

好,我们接下来继续前进。

 

6.2 初始化 Chip 相关的 IRQ

众所周知,启动的时候,C 语言从 start_kernel 开始,在这里面,调用了和 machine 相关的 IRQ 的初始化 init_IRQ()


   
   
  1. asmlinkage __visible void __init start_kernel(void)
  2. {
  3. char *command_line;
  4. char *after_dashes;
  5. .....
  6. early_irq_init();
  7. init_IRQ();
  8. .....
  9. }

在 init_IRQ 中,调用了 machine_desc->init_irq()


   
   
  1. void __init init_IRQ(void)
  2. {
  3. int ret;
  4. if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
  5. irqchip_init();
  6. else
  7. machine_desc->init_irq();
  8. if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&
  9. (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {
  10. if (!outer_cache.write_sec)
  11. outer_cache.write_sec = machine_desc->l2c_write_sec;
  12. ret = l2x0_of_init(machine_desc->l2c_aux_val,
  13. machine_desc->l2c_aux_mask);
  14. if (ret && ret != -ENODEV)
  15. pr_err( "L2C: failed to init: %d\n", ret);
  16. }
  17. uniphier_cache_init();
  18. }

machine_desc->init_irq() 完成对中断控制器的初始化,为每个irq_desc结构安装合适的流控handler,为每个irq_desc结构安装irq_chip指针,使他指向正确的中断控制器所对应的irq_chip结构的实例,同时,如果该平台中的中断线有多路复用(多个中断公用一个irq中断线)的情况,还应该初始化irq_desc中相应的字段和标志,以便实现中断控制器的级联。

这里初始化的时候回调用到具体的芯片相关的中断初始化的地方。

例如:


   
   
  1. int __init s5p_init_irq_eint(void)
  2. {
  3. int irq;
  4. for (irq = IRQ_EINT( 0); irq <= IRQ_EINT( 15); irq++)
  5. irq_set_chip(irq, &s5p_irq_vic_eint);
  6. for (irq = IRQ_EINT( 16); irq <= IRQ_EINT( 31); irq++) {
  7. irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
  8. set_irq_flags(irq, IRQF_VALID);
  9. }
  10. irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
  11. return 0;
  12. }

而在这些里面,都回去调用类似于:


   
   
  1. void
  2. irq_set_chip_and_handler_name (unsigned int irq, struct irq_chip *chip,
  3. irq_flow_handler_t handle, const char *name);
  4. irq_set_handler( unsigned int irq, irq_flow_handler_t handle)
  5. {
  6. __irq_set_handler(irq, handle, 0, NULL);
  7. }
  8. static inline void
  9. irq_set_chained_handler (unsigned int irq, irq_flow_handler_t handle)
  10. {
  11. __irq_set_handler(irq, handle, 1, NULL);
  12. }
  13. void
  14. irq_set_chained_handler_and_data (unsigned int irq, irq_flow_handler_t handle,
  15. void *data);

这些函数定义在 include/linux/irq.h 文件。是对芯片初始化的时候可见的 APIs,用于指定中断“流控”中的 :

irq_flow_handler_t handle

也就是中断来的时候,最后那个函数调用。

中断流控函数,分几种,电平触发的中断,边沿触发的,等:


   
   
  1. /*
  2. * Built-in IRQ handlers for various IRQ types,
  3. * callable via desc->handle_irq()
  4. */
  5. extern void handle_level_irq(struct irq_desc *desc);
  6. extern void handle_fasteoi_irq(struct irq_desc *desc);
  7. extern void handle_edge_irq(struct irq_desc *desc);
  8. extern void handle_edge_eoi_irq(struct irq_desc *desc);
  9. extern void handle_simple_irq(struct irq_desc *desc);
  10. extern void handle_untracked_irq(struct irq_desc *desc);
  11. extern void handle_percpu_irq(struct irq_desc *desc);
  12. extern void handle_percpu_devid_irq(struct irq_desc *desc);
  13. extern void handle_bad_irq(struct irq_desc *desc);
  14. extern void handle_nested_irq(unsigned int irq);

而在这些处理函数里,会去调用到 : handle_irq_event 

比如:


   
   
  1. /**
  2. * handle_level_irq - Level type irq handler
  3. * @desc: the interrupt description structure for this irq
  4. *
  5. * Level type interrupts are active as long as the hardware line has
  6. * the active level. This may require to mask the interrupt and unmask
  7. * it after the associated handler has acknowledged the device, so the
  8. * interrupt line is back to inactive.
  9. */
  10. void handle_level_irq(struct irq_desc *desc)
  11. {
  12. raw_spin_lock(&desc->lock);
  13. mask_ack_irq(desc);
  14. if (!irq_may_run(desc))
  15. goto out_unlock;
  16. desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
  17. /*
  18. * If its disabled or no action available
  19. * keep it masked and get out of here
  20. */
  21. if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
  22. desc->istate |= IRQS_PENDING;
  23. goto out_unlock;
  24. }
  25. kstat_incr_irqs_this_cpu(desc);
  26. handle_irq_event(desc);
  27. cond_unmask_irq(desc);
  28. out_unlock:
  29. raw_spin_unlock(&desc->lock);
  30. }

而这个 handle_irq_event 则是调用了处理,handle_irq_event_percpu


   
   
  1. irqreturn_t handle_irq_event(struct irq_desc *desc)
  2. {
  3. irqreturn_t ret;
  4. desc->istate &= ~IRQS_PENDING;
  5. irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
  6. raw_spin_unlock(&desc->lock);
  7. ret = handle_irq_event_percpu(desc);
  8. raw_spin_lock(&desc->lock);
  9. irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
  10. return ret;
  11. }

handle_irq_event_percpu->__handle_irq_event_percpu-> 【action->handler()

这里终于看到了调用 的地方了,就是咱们通过 request_irq 注册进去的函数


 

7. /proc/interrupts

这个 proc 下放置了对应中断号的中断次数和对应的 dev-name

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值