早期的内核版本中,几乎所有的中断都是由__do_IRQ函数进行处理,但是,因为各种中断请求的电气特性会有所不同,又或者中断控制器的特性也不同,这会导致以下这些处理也会有所不同:
- 何时对中断控制器发出ack回应;
- mask_irq和unmask_irq的处理;
- 中断控制器是否需要eoi回应?
- 何时打开cpu的本地irq中断?以便允许irq的嵌套;
- 中断数据结构的同步和保护;
声明:本博内容均由http://blog.youkuaiyun.com/droidphone原创,转载请注明出处,谢谢!
为此,通用中断子系统把几种常用的流控类型进行了抽象,并为它们实现了相应的标准函数,我们只要选择相应的函数,赋值给irq所对应的irq_desc结构的handle_irq字段中即可。这些标准的回调函数都是irq_flow_handler_t类型:
- handle_simple_irq
用于简易流控处理;
- handle_level_irq
用于电平触发中断的流控处理; - handle_edge_irq
用于边沿触发中断的流控处理;
-
handle_fasteoi_irq 用于需要响应eoi的中断控制器;
- handle_percpu_irq
用于只在单一cpu响应的中断;
- handle_nested_irq
用于处理使用线程的嵌套中断;
驱动程序和板级代码可以通过以下几个API设置irq的流控函数:
- irq_set_handler();
- irq_set_chip_and_handler();
- irq_set_chip_and_handler_name();
以下这个序列图展示了整个通用中断子系统的中断响应过程,flow_handle一栏就是中断流控层的生命周期:
2. handle_simple_irq
该函数没有实现任何实质性的流控操作,在把irq_desc结构锁住后,直接调用handle_irq_event处理irq_desc中的action链表,它通常用于多路复用(类似于中断控制器级联)中的子中断,由父中断的流控回调中调用。或者用于无需进行硬件控制的中断中。以下是它的经过简化的代码:
- void
- handle_simple_irq(unsigned
int irq, struct irq_desc *desc) - {
-
raw_spin_lock(&desc->lock); -
...... -
handle_irq_event(desc); -
- out_unlock:
-
raw_spin_unlock(&desc->lock); - }
3. handle_level_irq
- void
- handle_level_irq(unsigned
int irq, struct irq_desc *desc) - {
-
raw_spin_lock(&desc->lock); -
mask_ack_irq(desc); -
-
if (unlikely(irqd_irq_inprogress(&desc->irq_data))) -
goto out_unlock; -
...... -
-
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) -
goto out_unlock; -
-
handle_irq_event(desc); -
-
if (!irqd_irq_disabled(&desc->irq_data) && !(desc->istate & IRQS_ONESHOT)) -
unmask_irq(desc); - out_unlock:
-
raw_spin_unlock(&desc->lock); - }
状态 | 红色 | 绿色 |
---|---|---|
IRQ_PROGRESS | | |
是否允许本地cpu中断 | | |
是否允许该设备再次触发中断(可能由其它cpu响应) | | |
4. handle_edge_irq
状态 | | |
IRQ_PROGRESS | | |
是否允许本地cpu中断 | | |
是否允许该设备再次触发中断(可能由其它cpu响应) | | |
是否处于中断上下文 | | |
由图4.1也可以看出, 在处理软件中断(softirq)期间,此时仍然处于中断上下文中,但是cpu的本地中断是处于打开状态的,这表明此时嵌套中断允许发生,不过这不要紧,因为重要的处理已经完成,被嵌套的也只是软件中断部分而已。
5. handle_fasteoi_irq
- void
- handle_fasteoi_irq(unsigned
int irq, struct irq_desc *desc) - {
-
raw_spin_lock(&desc->lock); -
-
if (unlikely(irqd_irq_inprogress(&desc->irq_data))) -
if (!irq_check_poll(desc)) -
goto out; -
...... -
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { -
desc->istate |= IRQS_PENDING; -
mask_irq(desc); -
goto out; -
} -
-
if (desc->istate & IRQS_ONESHOT) -
mask_irq(desc); -
-
preflow_handler(desc); -
handle_irq_event(desc); -
- out_eoi:
-
desc->irq_data.chip->irq_eoi(&desc->irq_data); - out_unlock:
-
raw_spin_unlock(&desc->lock); -
return; -
...... - }
6. handle_percpu_irq
- void
- handle_percpu_irq(unsigned
int irq, struct irq_desc *desc) - {
-
struct irq_chip *chip = irq_desc_get_chip(desc); -
-
kstat_incr_irqs_this_cpu(irq, desc); -
-
if (chip->irq_ack) -
chip->irq_ack(&desc->irq_data); -
-
handle_irq_event_percpu(desc, desc->action); -
-
if (chip->irq_eoi) -
chip->irq_eoi(&desc->irq_data); - }
7. handle_nested_irq
该函数用于实现其中一种中断共享机制,当多个中断共享某一根中断线时,我们可以把这个中断线作为父中断,共享该中断的各个设备作为子中断,在父中断的中断线程中决定和分发响应哪个设备的请求,在得出真正发出请求的子设备后,调用handle_nested_irq来响应中断。所以,该函数是在进程上下文执行的,我们也无需扫描和执行irq_desc结构中的action链表。父中断在初始化时必须通过irq_set_nested_thread函数明确告知中断子系统:这些子中断属于线程嵌套中断类型,这样驱动程序在申请这些子中断时,内核不会为它们建立自己的中断线程,所有的子中断共享父中断的中断线程。
- void
handle_nested_irq(unsigned int irq) - {
-
...... -
might_sleep(); -
-
raw_spin_lock_irq(&desc->lock); -
...... -
action = desc->action; -
if (unlikely(!action || irqd_irq_disabled(&desc->irq_data))) -
goto out_unlock; -
-
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); -
raw_spin_unlock_irq(&desc->lock); -
-
action_ret = action->thread_fn(action->irq, action->dev_id); -
-
raw_spin_lock_irq(&desc->lock); -
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); -
- out_unlock:
-
raw_spin_unlock_irq(&desc->lock); - }