[DESCRIPTION]
在调试红外驱动的过程中,发现在调用 enable_irq() 函数时 Linux 内核日志会打印出这样的信息:Unbalanced enable for IRQ。
request_irq(infrared_irq, infrared_interrut_handler, IRQF_TRIGGER_FALLING, "infrared", NULL));
enable_irq_wake(infrared_irq)
[SOLUTION]
(1)为了搞清楚这个现象的原因,分析了 IRQ 的相关代码,发现其中使用了 struct irq_desc 结构体的成员变量 depth 来记录 disable irq 的嵌套深度。也就是说,enable_irq() 和 disable_irq() 调用可以嵌套,但调用次数要匹配。disable_irq() 调用要在前面,否则就会提示:Unbalanced enable for IRQ。
// 中断描述符结构体
struct irq_desc {
... // 省略部分代码
unsigned int depth; /* nested irq disables */
... // 省略部分代码
};
// 使能中断 API
void enable_irq(unsigned int irq)
{
... // 省略部分代码
__enable_irq(desc);
... // 省略部分代码
}
void __enable_irq(struct irq_desc *desc)
{
switch (desc->depth) {
case 0:
err_out:
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", // [DESCRIPTION] 提到的内核日志
irq_desc_get_irq(desc));
break;
case 1: {
if (desc->istate & IRQS_SUSPENDED)
goto err_out;
/* Prevent probing on this irq: */
irq_settings_set_noprobe(desc);
/*
* Call irq_startup() not irq_enable() here because the
* interrupt might be marked NOAUTOEN. So irq_startup()
* needs to be invoked when it gets enabled the first
* time. If it was already started up, then irq_startup()
* will invoke irq_enable() under the hood.
*/
irq_startup(desc, IRQ_RESEND, IRQ_START_COND);
break;
}
default:
desc->depth--;
}
}
// 禁用中断 API
void disable_irq(unsigned int irq)
{
if (!__disable_irq_nosync(irq))
synchronize_irq(irq);
}
static int __disable_irq_nosync(unsigned int irq)
{
... // 省略部分代码
__disable_irq(desc);
... // 省略部分代码
}
void __disable_irq(struct irq_desc *desc)
{
if (!desc->depth++) {
irq_disable(desc);
}
}
(2)去掉 enable_irq() 调用后,内核日志不再打印 Unbalanced enable for IRQ。