threaded irq和irqreturn
irqreturn有以下几种:
enum irqreturn { IRQ_NONE = (0 << 0), // 表示中断处理程序未处理该中断,或者没有发生中断 IRQ_HANDLED = (1 << 0), // 表示中断已经被处理 IRQ_WAKE_THREAD = (1 << 1), // 表示中断处理程序通知内核唤醒一个中断线程来处理中断 };
request_threaded_irq 是 Linux 内核中用于注册中断处理程序的函数之一。它允许在中断处理过程中执行较长时间的操作,而不会阻塞其他重要的内核功能。以下是对
request_threaded_irq 函数如下:
int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id); // 参数和返回值说明 unsigned int irq:要注册的中断号。 irq_handler_t handler:中断服务函数,即中断被触发时所调用的函数。这个函数应该返回一个 irqreturn_t 类型的值。 irq_handler_t thread_fn:中断线程服务函数,用于执行较长时间的操作。中断线程服务函数在中断服务函数之后执行。这个函数也应该返回一个 irqreturn_t 类型的值。 unsigned long irqflags:中断标志,控制中断的行为。常用的标志包括 IRQF_SHARED(允许共享中断)、IRQF_TRIGGER_*(中断触发方式,例如上升沿触发、下降沿触发等)等。 const char *devname:设备名称,用于标识中断请求所属的设备。 void *dev_id:设备标识符,将传递给中断服务函数和中断线程服务函数作为参数。 函数返回值为 0 表示注册成功,否则表示注册失败,返回的值是一个负数,表示出错原因。
request_threaded_irq 函数的工作流程如下:
- 检查中断号是否有效,以及指定的中断服务函数和中断线程服务函数是否为有效函数。
- 分配中断描述符,并为中断分配中断处理程序和中断线程处理程序。
- 将中断处理程序注册到内核,以便在中断被触发时调用。
- 将中断线程处理程序注册到内核,以便在中断被触发时调用。
在成功注册中断后,中断被触发时,首先会调用中断服务函数,然后再调用中断线程服务函数。这样可以确保中断服务函数尽快返回,而不会阻塞其他重要的内核功能,而长时间的处理操作可以在中断线程服务函数中执行。
request_threaded_irq driver demo:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/kthread.h> // 包含内核线程相关头文件 #define DEMO_IRQ_NUMBER 1 // 定义一个结构体,用于传递给中断处理函数和中断线程处理函数 struct demo_data { irq_handler_t irq_handler; struct task_struct *thread_task; }; static irqreturn_t demo_interrupt_handler(int irq, void *dev_id) { struct demo_data *data = (struct demo_data *)dev_id; // 处理中断 printk(KERN_INFO "Demo interrupt handled\n"); // 如果有中断线程处理函数,则唤醒线程 if (data->thread_task) { wake_up_process(data->thread_task); } // 返回中断处理结果 return IRQ_HANDLED; } static irqreturn_t demo_threaded_handler(int irq, void *dev_id) { // 处理中断 printk(KERN_INFO "Demo threaded interrupt handled\n"); // 返回中断处理结果 return IRQ_HANDLED; } static int demo_thread_fn(void *data) { // 执行长时间的操作,例如复杂计算、文件操作等 printk(KERN_INFO "Demo threaded handler executing long operation\n"); msleep(1000); // 模拟长时间操作 return 0; } static int __init demo_init(void) { int ret; struct demo_data *data; // 分配内存并初始化数据结构 data = kmalloc(sizeof(struct demo_data), GFP_KERNEL); if (!data) { printk(KERN_ERR "Failed to allocate memory for demo data\n"); return -ENOMEM; } data->irq_handler = demo_interrupt_handler; data->thread_task = kthread_create(demo_thread_fn, NULL, "demo_thread_task"); if (IS_ERR(data->thread_task)) { kfree(data); printk(KERN_ERR "Failed to create demo thread\n"); return PTR_ERR(data->thread_task); } // 注册中断处理程序和中断线程处理程序 ret = request_threaded_irq(DEMO_IRQ_NUMBER, demo_interrupt_handler, demo_threaded_handler, IRQF_SHARED, "demo_interrupt_threaded", (void *)data); if (ret) { printk(KERN_ERR "Failed to register threaded interrupt handler\n"); kfree(data); return ret; } printk(KERN_INFO "Demo module initialized\n"); return 0; } static void __exit demo_exit(void) { struct demo_data *data = (struct demo_data *)free_irq(DEMO_IRQ_NUMBER, (void *)demo_interrupt_handler); // 释放中断资源 if (data) { kfree(data); } printk(KERN_INFO "Demo module exited\n"); } module_init(demo_init); module_exit(demo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("lethe1203"); MODULE_DESCRIPTION("request_threaded_irq demo");