4. 中断共享原理:共享中断的注册机制、共享中断处理函数链、irqflags与IRQF_SHARED标志

在嵌入式 Android 系统中,硬件中断线(IRQ)是稀缺资源。多个功能独立的外设(如触摸屏、加速度计、接近传感器)可能共用同一条物理中断线。中断共享(Shared Interrupt)机制允许这些设备注册到同一个 IRQ 号上,由内核在中断触发时依次调用所有注册的处理函数,直到找到能处理该中断的设备。

4.1 共享中断的注册机制

共享中断的注册通过 request_threaded_irq() 或 request_irq() 完成。与独占中断不同,共享中断在注册时必须携带 IRQF_SHARED 标志,并且要求所有共享该 IRQ 的驱动都设置该标志。

注册接口原型:
int request_irq(unsigned int irq,
                irq_handler_t handler,
                unsigned long irqflags,
                const char *devname,
                void *dev_id);

共享注册关键约束:

  • 所有共享者必须设置 IRQF_SHARED:内核在注册时会检查已有中断描述符的 flags,若已有 handler 未设置 IRQF_SHARED,则新注册返回 -EBUSY。
  • dev_id 参数必须唯一且非 NULL:该指针在释放中断时用于标识具体设备,同时在内核遍历 handler 链时作为匹配依据。
  • 中断触发方式必须一致:所有共享设备必须使用相同的触发类型(电平触发或边沿触发),通常由 Bootloader 或设备树固定。
注册流程示意: 驱动调用 request_irq(irq, handler, IRQF_SHARED | IRQF_TRIGGER_RISING, "my_device", dev) → 内核查找 irq_desc → 若已有共享链则追加 handler → 若首次注册则配置硬件中断控制器。
共享中断注册流程 共享中断注册流程 驱动调用 request_irq 检查 irq_desc 状态 已有独占 handler? 返回 -EBUSY 已有共享链? 追加 handler 到链表 首次注册? 配置硬件中断控制器 注册成功 dev_id 必须唯一且非 NULL 触发方式必须一致

4.2 共享中断处理函数链

当共享中断触发时,内核不会立即判断是哪个设备产生了中断,而是遍历该 IRQ 对应的 处理函数链表(action list),依次调用每个注册的 handler,直到某个 handler 返回 IRQ_HANDLED 或 IRQ_WAKE_THREAD。

链表节点字段 类型 说明
handler irq_handler_t 中断处理函数指针,返回 irqreturn_t
thread_fn irq_handler_t 线程化中断处理函数(若使用 threaded IRQ)
dev_id void * 设备标识指针,用于释放和匹配
irqflags unsigned long 注册时传入的标志位,包含 IRQF_SHARED
next struct irqaction * 指向下一个共享 handler 的指针
内核遍历共享链的核心逻辑(简化):
struct irqaction *action;
for (action = desc->action; action; action = action->next) {
    irqreturn_t ret = action->handler(irq, action->dev_id);
    if (ret == IRQ_HANDLED || ret == IRQ_WAKE_THREAD) {
        handled = 1;
        break;  // 找到处理者后停止遍历
    }
}
if (!handled)
    pr_warn("Spurious interrupt (no one handled)\n");

注意:如果所有 handler 都返回 IRQ_NONE,内核会记录一次“伪中断”(spurious interrupt),并可能逐渐禁用该 IRQ。

共享中断处理函数链 共享中断处理函数链(action list) 中断控制器 IRQ 触发 action[0] 触摸屏 handler action[1] 加速度计 handler action[2] 接近传感器 handler 返回 IRQ_NONE 返回 IRQ_NONE 返回 IRQ_HANDLED 遍历顺序:按注册先后依次调用 遇到 IRQ_HANDLED 即停止,否则记录伪中断

4.3 irqflags 与 IRQF_SHARED 标志

irqflags 是注册中断时传入的标志位掩码,控制中断的行为特性。共享中断场景下,IRQF_SHARED 是最核心的标志,但通常与其他标志组合使用。

标志位 值 含义 共享场景要求
IRQF_SHARED 0x00000080 允许共享中断线 所有共享者必须设置
IRQF_TRIGGER_RISING 0x00000001 上升沿触发 所有共享者必须一致
IRQF_TRIGGER_FALLING 0x00000002 下降沿触发 所有共享者必须一致
IRQF_TRIGGER_HIGH 0x00000004 高电平触发 所有共享者必须一致
IRQF_TRIGGER_LOW 0x00000008 低电平触发 所有共享者必须一致
IRQF_ONESHOT 0x00002000 中断处理期间屏蔽该 IRQ 线程化中断常用,可共享
IRQF_NO_SUSPEND 0x00004000 系统休眠时不关闭中断 可共享,需谨慎
⚠️ 共享中断的常见陷阱:
  • 触发方式不一致:一个设备用上升沿,另一个用下降沿,会导致中断丢失或重复触发。
  • dev_id 为 NULL:内核在释放中断时无法匹配,导致 free_irq 失败或误删其他设备的 handler。
  • handler 未正确检查设备状态:共享 handler 必须读取硬件寄存器确认中断是否来自本设备,否则会错误消费其他设备的中断。
  • 电平触发共享需谨慎:电平触发中断在共享时,如果一个设备未及时清中断,会导致 IRQ 持续拉低,死锁整个中断线。
irqflags 组合与共享约束 irqflags 组合与共享约束 IRQF_SHARED IRQF_TRIGGER_* 必须一致 IRQF_ONESHOT 可共享 约束条件 所有共享设备必须使用相同的触发标志 | dev_id 必须唯一 | handler 必须检查设备状态
最佳实践: 在 Android 设备驱动中,共享中断的 handler 应首先读取硬件状态寄存器,确认中断是否来自本设备。若确认是本设备中断,则处理并返回 IRQ_HANDLED;否则立即返回 IRQ_NONE,让链中下一个 handler 处理。这种“先检查、后处理”的模式是共享中断正确性的基石。