linux kernel irq相关函数详解
在Linux内核驱动开发中,处理中断涉及一系列关键函数,正确使用这些函数对确保驱动的稳定性和性能至关重要。以下是disable_irq
、free_irq
、platform_get_irq
和request_irq
等函数的详细解析,涵盖其功能、用法、注意事项及示例代码。
一、核心函数详解
1. request_irq
:注册中断处理程序
-
功能:申请中断线并绑定中断处理函数。
-
函数原型:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev_id);
-
参数:
-
irq
:中断号(通过platform_get_irq
获取)。 -
handler
:中断处理函数(如irq_handler_t
类型)。 -
flags
:中断标志(如IRQF_SHARED
、IRQF_TRIGGER_RISING
)。 -
name
:中断名称(在/proc/interrupts
中显示)。 -
dev_id
:设备标识符(用于共享中断时的唯一标识)。
-
-
返回值:成功返回0,失败返回错误码。
-
示例:
ret = request_irq(irq_num, my_interrupt_handler, IRQF_SHARED, "my_device", dev); if (ret) {pr_err("Failed to request IRQ %d\n", irq_num);return ret; }
2. platform_get_irq
:获取平台设备中断号
-
功能:从设备树或平台资源中提取中断号。
-
函数原型:
int platform_get_irq(struct platform_device *dev, unsigned int num);
-
参数:
-
dev
:平台设备结构体指针。 -
num
:中断资源索引(通常为0,表示第一个中断)。
-
-
返回值:成功返回中断号,失败返回负数错误码。
-
示例:
int irq = platform_get_irq(pdev, 0); if (irq < 0) {dev_err(&pdev->dev, "Failed to get IRQ\n");return irq; }
3. free_irq
:释放中断资源
-
功能:解除中断处理函数并释放中断线。
-
函数原型:
void free_irq(unsigned int irq, void *dev_id);
-
参数:
-
irq
:中断号。 -
dev_id
:与request_irq
时一致的设备标识符。
-
-
注意事项:
-
必须在驱动卸载(如
remove
函数)中调用。 -
共享中断时,
dev_id
必须唯一匹配。
-
-
示例:
free_irq(irq_num, dev);
4. disable_irq
与 enable_irq
:禁用/启用中断
-
功能:临时禁用或重新启用中断线。
-
函数原型:
void disable_irq(unsigned int irq); void enable_irq(unsigned int irq);
-
变体:
-
disable_irq_nosync
:立即禁用中断,不等待当前处理完成。 -
enable_irq
:需与disable_irq
成对调用。
-
-
注意事项:
-
多次调用
disable_irq
需对应相同次数的enable_irq
。 -
避免在中断上下文中调用
disable_irq
(可能导致死锁)。
-
-
示例:
disable_irq(irq_num); // 执行关键操作(如修改共享数据) enable_irq(irq_num);
二、使用场景与流程
1. 驱动初始化(Probe函数)
-
获取中断号:通过
platform_get_irq
获取硬件中断号。 -
注册中断处理程序:调用
request_irq
绑定处理函数。 -
可选配置:设置中断触发方式(如边沿触发)或共享标志。
2. 中断处理函数
-
典型结构:
static irqreturn_t my_interrupt_handler(int irq, void *dev_id) {struct my_device *dev = (struct my_device *)dev_id;// 处理中断逻辑return IRQ_HANDLED; }
-
共享中断:需通过
dev_id
区分不同设备。
3. 驱动卸载(Remove函数)
-
禁用中断:调用
disable_irq
确保中断不再触发。 -
释放中断:通过
free_irq
解除注册。 -
清理资源:释放与
dev_id
关联的内存。
三、注意事项与最佳实践
1. 内存与资源管理
-
使用
devm_request_irq
:自动管理资源,避免free_irq
遗漏。int devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev_id);
-
错误处理:检查
request_irq
和platform_get_irq
的返回值。
2. 共享中断处理
-
标志设置:必须使用
IRQF_SHARED
。 -
唯一
dev_id
:每个设备需提供唯一的标识符(如设备结构体指针)。
3. 中断禁用与同步
-
避免长时间禁用:可能导致中断丢失或系统延迟。
-
自旋锁保护:在中断处理函数中使用自旋锁(
spin_lock
)保护共享数据。
四、完整示例代码
平台设备驱动示例
#include <linux/interrupt.h>
#include <linux/platform_device.h>struct my_device {struct device *dev;int irq;
};static irqreturn_t my_interrupt_handler(int irq, void *dev_id) {struct my_device *dev = dev_id;// 处理中断return IRQ_HANDLED;
}static int my_probe(struct platform_device *pdev) {struct my_device *dev;int irq, ret;dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);if (!dev) return -ENOMEM;irq = platform_get_irq(pdev, 0);if (irq < 0) return irq;ret = devm_request_irq(&pdev->dev, irq, my_interrupt_handler,IRQF_SHARED, "my_device", dev);if (ret) return ret;dev->irq = irq;platform_set_drvdata(pdev, dev);return 0;
}static int my_remove(struct platform_device *pdev) {struct my_device *dev = platform_get_drvdata(pdev);disable_irq(dev->irq);// free_irq 由 devm_request_irq 自动处理return 0;
}
五、常见问题解答
1. 为何free_irq
需要dev_id
参数?
-
唯一标识:确保释放正确的中断处理程序,尤其在共享中断时。
2. disable_irq
与disable_irq_nosync
的区别?
-
同步性:
disable_irq
等待当前中断处理完成;disable_irq_nosync
立即禁用。
3. 多次调用disable_irq
的影响?
-
计数机制:内核维护禁用计数,需相同次数的
enable_irq
重新启用中断。
通过合理使用上述函数,开发者能够高效管理中断资源,确保驱动程序的稳定性和响应能力。实际开发中需结合具体硬件和内核版本调整实现细节。