一、背景
一个简单的字符设备驱动程序,程序里面需要实现IO
口的下降沿中断。驱动程序编译正常,注册正常,也能正常识别出IO
中断,并响应中断处理函数。在卸载驱动程序时,提示如下:
WARNING: CPU: 0 PID: 100 at kernel/irq/manage.c:1346 __free_irq+0xa4/0x1ec()
Trying to free already-free IRQ 46
二、问题分析、解决
1. 中断相关的函数原型与程序中的调用方法
申请中断
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
request_irq( g_key.irq_num, key_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"key_irq", &g_key );
释放中断
void free_irq(unsigned int irq, void *dev_id);
free_irq( g_key.irq_num, 0 );
2. 怀疑与疑问
如上所示,free_irq
第二个参数填入的是0
,并没有填入 dev_id
。释放中断不是直接根据中断号就能释放了吗?跟dev_id
有关?
3. 查阅 free_irq 的源码
3.1 free_irq
free_irq 定义在文件 linux_kernel/kernel/irq/manage.c
。
void free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return;
#ifdef CONFIG_SMP
if (WARN_ON(desc->affinity_notify))
desc->affinity_notify = NULL;
#endif
chip_bus_lock(desc);
kfree(__free_irq(irq, dev_id));
chip_bus_sync_unlock(desc);
}
从上可以看到,释放中断的主要操作是在函数 __free_irq
中实现,dev_id
参数也是传进 __free_irq
中使用。所以需要查看 __free_irq
函数的实现。
3.2 查阅 __free_irq 的源码实现
static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
...
for (;;) {
action = *action_ptr;
if (!action) {
WARN(1, "Trying to free already-free IRQ %d\n", irq);
raw_spin_unlock_irqrestore(&desc->lock, flags);
return NULL;
}
if (action->dev_id == dev_id)
break;
action_ptr = &action->next;
}
...
}
上面源码,略去了前后部分代码。源码中,会判断request_irq
填入的 dev_id
和 free_irq
的dev_id
(也就是第二个参数),如果一致,才会退出循环。
4. 问题解决
将free_irq
调用改成如下所示:
free_irq( g_key.irq_num, &g_key );