imx6ull-驱动开发篇24——Linux 中断API函数
目录
Linux 中断 API 函数
中断号
request_irq 函数
函数参数
独占中断(非共享)
共享中断
free_irq 函数
中断处理函数
函数格式
示例代码
中断使能与禁止函数
函数介绍
示例代码
在裸机实验里面中断的处理方法:GPIO中断实验
- 使能中断,初始化相应的寄存器。
- 注册中断服务函数,也就是向 irqTable 数组的指定标号处写入中断服务函数。
- 中断发生以后进入 IRQ 中断服务函数,在 IRQ 中断服务函数在数组 irqTable 里面查找具体的中断处理函数,找到以后执行相应的中断处理函数。
那么,linux下又是如何使用中断的呢?让我们一起学习一下linux中断的API函数吧~
Linux 中断 API 函数
中断号
每个中断都有一个中断号,通过中断号即可区分不同的中断,有的资料也把中断号叫做中断线。
在 Linux 内核中使用一个 int 变量表示中断号:
中断号范围:
-
传统 IRQ:0~15(x86 PIC),0~NR_IRQS(现代系统可能扩展至数千)。
-
虚拟中断(如 MSI-X):动态分配,可能远超物理 IRQ 数量。
request_irq 函数
在 Linux 内核中,使用request_irq 函数用于申请中断, request_irq函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函数。
函数参数
request_irq 函数会激活(使能)中断,函数原型如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);
其中,flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志。常见的中断标志如下:
request_irq函数的返回值: 0 中断申请成功,其他负值:中断申请失败,如果返回-EBUSY 的话表示中断已经被申请了。
示例代码:
独占中断(非共享)
irqreturn_t my_handler(int irq, void *dev_id) {printk("Interrupt %d handled by device %s\n", irq, (char *)dev_id);return IRQ_HANDLED;
}int setup_irq(void) {int irq_num = 5; // 假设硬件中断号为 5char *dev_name = "my_device";if (request_irq(irq_num, my_handler, IRQF_TRIGGER_RISING, dev_name, NULL)) {printk("Failed to register IRQ %d\n", irq_num);return -EIO;}return 0;
}
共享中断
struct my_device {char *name;int data;
};irqreturn_t shared_handler(int irq, void *dev_id) {struct my_device *dev = dev_id;printk("IRQ %d by %s (data=%d)\n", irq, dev->name, dev->data);return IRQ_HANDLED;
}int setup_shared_irq(void) {struct my_device dev1 = { .name = "dev1", .data = 42 };int irq_num = 16;if (request_irq(irq_num, shared_handler, IRQF_SHARED | IRQF_TRIGGER_HIGH, "shared_irq", &dev1)) {printk("Failed to register shared IRQ\n");return -EIO;}return 0;
}
free_irq 函数
通过 request_irq 函数申请中断,通过 free_irq 函数释放掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。
free_irq函数原型如下所示:
void free_irq(unsigned int irq, void *dev)
示例代码如下:
//1. 释放独占中断
void cleanup_irq(void) {int irq_num = 5; // 假设之前注册的中断号为 5free_irq(irq_num, NULL); // 非共享中断时 dev 可为 NULL
}// 2. 释放共享中断
struct my_device {char *name;int data;
};void cleanup_shared_irq(void) {static struct my_device dev1 = { .name = "dev1", .data = 42 };int irq_num = 16;free_irq(irq_num, &dev1); // 必须传递与注册时相同的 dev 指针
}
中断处理函数
使用 request_irq 函数申请中断的时候需要设置中断处理函数。
函数格式
中断处理函数格式如下所示:
irqreturn_t (*irq_handler_t) (int, void *)
中断处理函数的返回值为 irqreturn_t 类型, irqreturn_t 类型定义如下所示:
enum irqreturn {IRQ_NONE = (0 << 0),IRQ_HANDLED = (1 << 0),IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
可以看出 irqreturn_t 是个枚举类型,一共有三种返回值。
示例代码
// 1. 基础中断处理irqreturn_t my_interrupt_handler(int irq_num, void *dev_id) {struct my_device *dev = dev_id;if (!check_device_irq(dev)) // 检查是否为本设备中断return IRQ_NONE; // 非本设备中断,不处理process_hardware_irq(dev); // 处理中断return IRQ_HANDLED; // 确认已处理
}// 2. 共享中断处理
irqreturn_t shared_handler(int irq_num, void *dev_id) {struct my_device *dev = dev_id;if (read_device_status(dev) != IRQ_TRIGGERED)return IRQ_NONE; // 非本设备中断handle_irq_logic(dev);return IRQ_HANDLED;
}// 注册共享中断
request_irq(irq_num, shared_handler, IRQF_SHARED, "shared_irq", &device1);
request_irq(irq_num, shared_handler, IRQF_SHARED, "shared_irq", &device2);// 3. 线程化中断
irqreturn_t threaded_handler(int irq_num, void *dev_id) {// 快速处理硬件操作(在硬中断上下文中)return IRQ_WAKE_THREAD; // 唤醒关联线程
}static irqreturn_t threaded_part(int irq_num, void *dev_id) {// 耗时的处理(在进程上下文中)return IRQ_HANDLED;
}// 注册线程化中断
request_threaded_irq(irq_num, threaded_handler, threaded_part, IRQF_ONESHOT, "threaded_irq", NULL);
中断使能与禁止函数
函数介绍
常用的中断使用和禁止函数如下所示:
void enable_irq(unsigned int irq); // 启用中断
void disable_irq(unsigned int irq); // 禁用中断
enable_irq 和 disable_irq 用于使能和禁止指定的中断, irq 就是要禁止的中断号。
disable_irq函数要等到当前正在执行的中断处理函数执行完才返回,因此使用者需要保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出。
还有一个中断禁止函数:
void disable_irq_nosync(unsigned int irq)
disable_irq_nosync 函数调用以后立即返回,不会等待当前中断处理程序执行完毕。
使能和禁止全局中断函数如下:
local_irq_enable()
local_irq_disable()
local_irq_save(flags)
local_irq_restore(flags)
示例代码
// 1. 保护短临界区(如自旋锁)
spinlock_t lock;
unsigned long flags;local_irq_save(flags); // 禁用中断并保存状态
spin_lock(&lock); // 获取自旋锁
// 操作共享数据...
spin_unlock(&lock); // 释放锁
local_irq_restore(flags); // 恢复中断状态// 2. 中断上下文中的原子操作
irqreturn_t irq_handler(int irq, void *dev_id) {unsigned long flags;local_irq_save(flags); // 防止中断嵌套// 修改硬件寄存器或全局变量local_irq_restore(flags);return IRQ_HANDLED;
}// 3. 内核定时器回调
void timer_callback(struct timer_list *t) {unsigned long flags;local_irq_save(flags);// 处理时间敏感任务(避免被中断打断)local_irq_restore(flags);
}