中断API之request_irq/request_threaded_irq

本文介绍了Linux内核中的中断请求函数request_irq和request_threaded_irq的使用方法及源码解析。详细解释了函数参数的意义,包括中断号、处理函数、标志位等,并分析了支持的标志类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
这个函数用于为申请一个中断。
其源码分析如下:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
{
	return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
可见这个函数只是request_threaded_irq的一个包装.
形参详细说明如下:
irq:要申请的中断号
handle:中断处理函数
thread_fn:中断线程化。可见这里没有办法通过request_irq 来实现中断线程化,要申请支持中断线程化的
			只能直接调用request_threaded_irq
devname:要申请中断dev的name,其实一个字符串
dev_id:一个void * 变量,主要用于给中断传递参数和共享中断的case
irq_flags:中断支持的flag。
总共支持的flag如下:
 * IRQF_SHARED - allow sharing the irq among several devices
 * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
 * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
 * IRQF_PERCPU - Interrupt is per cpu
 * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
 * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
 *                registered first in an shared interrupt is considered for
 *                performance reasons)
 * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 *                Used by threaded interrupts which need to keep the
 *                irq line disabled until the threaded handler has been run.
 * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend.  Does not guarantee
 *                   that this interrupt will wake the system from a suspended
 *                   state.  See Documentation/power/suspend-and-interrupts.txt
 * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
 * IRQF_NO_THREAD - Interrupt cannot be threaded
 * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
 *                resume time.
 * IRQF_COND_SUSPEND - If the IRQ is shared with a NO_SUSPEND user, execute this
 *                interrupt handler after suspending interrupts. For system
 *                wakeup devices users need to implement wakeup detection in
 *                their interrupt handlers.




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)
用于申请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)
{
	struct irqaction *action;
	struct irq_desc *desc;
	int retval;
	#如果irq number等于#define IRQ_NOTCONNECTED	(1U << 31) 则退出,从这里看出irq number最大就是IRQ_NOTCONNECTED

	if (irq == IRQ_NOTCONNECTED)
		return -ENOTCONN;

	/*
	 * Sanity-check: shared interrupts must pass in a real dev-ID,
	 * otherwise we'll have trouble later trying to figure out
	 * which interrupt is which (messes up the interrupt freeing
	 * logic etc).
	 *
	 * Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
	 * it cannot be set along with IRQF_NO_SUSPEND.
	 */
	 #irq的flag之间有依赖,这理解就是检查依赖。
	if (((irqflags & IRQF_SHARED) && !dev_id) ||
	    (!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
	    ((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
		return -EINVAL;
	#得到中断号对应的中断描述符
	desc = irq_to_desc(irq);
	if (!desc)
		return -EINVAL;
	#检查是否设置_IRQ_NOREQUEST和_IRQ_PER_CPU_DEVID
	if (!irq_settings_can_request(desc) ||
	    WARN_ON(irq_settings_is_per_cpu_devid(desc)))
		return -EINVAL;
	#handler和thread_fn 不能同时为null,如果handle为 null,且thread_fn不为null,则给handler 赋予一个默认函数。
	if (!handler) {
		if (!thread_fn)
			return -EINVAL;
		handler = irq_default_primary_handler;
	}
	#申请一个irqaction 结构体,这个结构体会在free_irq 的时候释放
	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;
	#给irqaction 的个个成员变量赋值
	action->handler = handler;
	action->thread_fn = thread_fn;
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;
	#为这个irq使能power
	retval = irq_chip_pm_get(&desc->irq_data);
	if (retval < 0) {
		kfree(action);
		return retval;
	}
	#内部函数,主要用于注册irqaction。
	retval = __setup_irq(irq, desc, action);

	if (retval) {
		irq_chip_pm_put(&desc->irq_data);
		kfree(action->secondary);
		kfree(action);
	}
#share irq debug,一般情况下不打开这个宏
#ifdef CONFIG_DEBUG_SHIRQ_FIXME
	if (!retval && (irqflags & IRQF_SHARED)) {
		/*
		 * It's a shared IRQ -- the driver ought to be prepared for it
		 * to happen immediately, so let's make sure....
		 * We disable the irq to make sure that a 'real' IRQ doesn't
		 * run in parallel with our fake.
		 */
		unsigned long flags;

		disable_irq(irq);
		local_irq_save(flags);

		handler(irq, dev_id);

		local_irq_restore(flags);
		enable_irq(irq);
	}
#endif
	return retval;
}

<think>我们正在讨论的是Linux内核中的中断处理函数。用户之前询问了关于设备树中配置GPIO中断的pinctrl状态问题,现在转向了解`devm_request_threaded_irq`函数的用法和解释。根据引用内容,我们已知:-`devm_request_threaded_irq`用于请求并注册一个线程化的中断处理程序[^1]。-使用此函数时需要注意传递有效的中断号,否则会返回错误(如-22)[^2]。-该函数是支持中断线程化的接口,与传统的`request_irq`相比,可以简化设计,避免使用tasklet/workqueue等机制[^3]。因此,我们应详细解释该函数的作用、参数、使用方法和注意事项。</think> ### Linux 驱动中 `devm_request_threaded_irq` 函数详解 `devm_request_threaded_irq` 是 Linux 内核中用于管理线程化中断处理的核心函数[^1][^3],与传统 `request_irq` 相比,它实现了两大创新: 1. **资源自动管理**:使用 `devm_` 前缀确保驱动卸载时自动释放中断资源 2. **中断线程化**:支持将耗时中断任务转移到内核线程执行,避免阻塞整个系统 #### 一、函数原型与参数详解 ```c int devm_request_threaded_irq(struct device *dev, // 关联的设备结构体 unsigned int irq, // 硬件中断号[^2] irq_handler_t handler, // 顶半部函数 irq_handler_t thread_fn, // 底半部线程函数 unsigned long irqflags, // 中断标志 const char *devname, // 设备标识名 void *dev_id); // 共享中断ID ``` **关键参数解析**: 1. `handler`(顶半部): - 在硬中断上下文中运行(原子上下文) - 应小于 100μs,仅执行关键操作(如寄存器读取、状态标记) - 返回: - `IRQ_WAKE_THREAD`:唤醒底半部线程 - `IRQ_HANDLED`:无后续处理 ```c irqreturn_t handler(int irq, void *dev_id) { if (check_hardware_status()) return IRQ_WAKE_THREAD; // 触发线程处理 return IRQ_HANDLED; // 处理完成 } ``` 2. `thread_fn`(底半部): - 在内核线程中运行(可睡眠、调度) - 处理耗时任务(如数据传输、复杂计算) ```c irqreturn_t thread_fn(int irq, void *dev_id) { process_data_from_hardware(); // 可调用kmalloc/schedule_timeout等 return IRQ_HANDLED; } ``` 3. `irqflags` 标志组合: | 标志 | 说明 | 适用场景 | |---|---|---| | `IRQF_TRIGGER_RISING` | 上升沿触发 | GPIO按键[^2] | | `IRQF_ONESHOT` | 执行完成后才开中断 | 避免中断嵌套 | | `IRQF_SHARED` | 共享中断线 | 多个设备共享同中断号 | #### 二、典型使用流程 ```c // 驱动探测函数 static int probe(struct platform_device *pdev) { struct my_device *dev = alloc_device(); // 获取设备树中配置的中断号 int irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; // 错误处理[^2] // 注册线程化中断 int ret = devm_request_threaded_irq(&pdev->dev, irq, top_half_handler, bottom_half_thread, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "my-device", dev); if (ret) { dev_err(&pdev->dev, "中断注册失败: %d\n", ret); return ret; } } ``` #### 三、与传统方案的对比 | 方案 | 上下文 | 可睡眠 | 资源管理 | 适用场景 | |---|---|---|---|---| | `request_irq` + Tasklet | 软中断 | ❌ | 手动释放 | 简单实时任务 | | `request_irq` + Workqueue | 进程 | ✅ | 手动释放 | 复杂非实时任务 | | `devm_request_threaded_irq` | 线程 | ✅ | 自动释放 | 推荐通用方案[^3] | #### 四、高频错误处理 1. **返回 -22 (EINVAL)**: - 原因:无效中断号(如 `irq=0` 或超出范围)[^2] - 解决:用 `platform_get_irq()` 替代硬编码 2. **返回 -16 (EBUSY)**: - 原因:共享中断未设置 `IRQF_SHARED` 标志 - 解决:添加共享标志或检查设备树配置 3. **内核崩溃 (oops)**: - 原因:在 `handler` 中使用睡眠函数(如 `msleep()`) - 解决:将耗时操作移至 `thread_fn` > 注意:`IRQF_ONESHOT` 必须与线程化中断配合使用,确保顶半部执行期间不会产生二次中断[^1]。 #### 五、设备树集成示例 ```dts // 设备树配置 my_device { compatible = "vendor,my-device"; interrupt-parent = <&gpio1>; interrupts = <3 IRQ_TYPE_EDGE_RISING>; // GPIO1_IO03上升沿触发 }; ``` 驱动通过 `platform_get_irq()` 自动解析该配置,无需硬编码中断号。 --- ### 相关问题 1. 如何验证 `devm_request_threaded_irq` 的顶半部和底半部执行时间? 2. 共享中断场景下 `dev_id` 参数应如何设置? 3. 线程化中断的实时性如何保障? 4. 设备树配置的 GPIO 中断与标准硬件中断在函数使用上有何区别?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值