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重新启用中断。
通过合理使用上述函数,开发者能够高效管理中断资源,确保驱动程序的稳定性和响应能力。实际开发中需结合具体硬件和内核版本调整实现细节。
