Linux Kernel threaded irq

1.How-to

request_threaded_irq()
{
action->handler = handler;
action->thread_fn = thread_fn;
->__setup_irq()
{
/*
* Create a handler thread when a thread function is supplied
* and the interrupt does not nest into another interrupt
* thread.
*/
if (new->thread_fn && !nested) {
struct task_struct *t;


t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
  new->name);

adb shell ps -P -p
可以看到创建的 irq thread:
USER     PID   PPID  VSIZE  RSS   PRIO  NICE  RTPRI SCHED  PCY  WCHAN    PC        NAME
root      57    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/102-msm_iom
root      58    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/102-msm_iom
root      59    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/102-msm_iom
root      60    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/79-msm_iomm
root      61    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/78-msm_iomm
root      62    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/78-msm_iomm
root      63    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/74-msm_iomm
root      64    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/75-msm_iomm
root      65    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/75-msm_iomm
root      66    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/273-msm_iom
root      67    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/273-msm_iom
root      68    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/273-msm_iom
root      69    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/97-msm_iomm
root      70    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/97-msm_iomm
root      71    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/97-msm_iomm
root      77    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/402-mba
root      78    2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/530-wcnss
root      134   2     0      0     -51   0     50    1     fg  c0100ac8 00000000 S irq/335-main_tt

2.什么时候执行

产生中断之后先执行中断对应的 hard handler, 之后根据hard handler中断服务程序是否返回 IRQ_WAKE_THREAD flag决定是否执行中断对应的thread_fn。
(下半部tasklet 也是在hard handler 中断服务程序中执行tasklet_schedule()触发的)
执行流程是下面这个样子的:
 

handle_irq_event_percpu()
->
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id);
trace_irq_handler_exit(irq, action, res);
->
switch (res) {
case IRQ_WAKE_THREAD:
/*
* Catch drivers which return WAKE_THREAD but
* did not set up a thread function
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}


irq_wake_thread(desc, action);


->static void irq_wake_thread(struct irq_desc *desc, struct irqaction *action)
{
wake_up_process(action->thread);




->
/*
 * Interrupt handler thread
 */
static int irq_thread(void *data)
{
static const struct sched_param param = {
.sched_priority = MAX_USER_RT_PRIO/2,
};
sched_setscheduler(current, SCHED_FIFO, &param);//irq thread 优先级比ksoftirqd/N 高很多
->
ret = action->thread_fn(action->irq, action->dev_id);

 

3. 一个threaded irq 典型的例子

static irqreturn_t ads7846_hard_irq(int irq, void *handle)
{
	struct ads7846 *ts = handle;

	return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
}


static irqreturn_t ads7846_irq(int irq, void *handle)
{
	struct ads7846 *ts = handle;

	/* Start with a small delay before checking pendown state */
	msleep(TS_POLL_DELAY);

	while (!ts->stopped && get_pendown_state(ts)) {

		/* pen is down, continue with the measurement */
		ads7846_read_state(ts);

		if (!ts->stopped)
			ads7846_report_state(ts);

		wait_event_timeout(ts->wait, ts->stopped,
				   msecs_to_jiffies(TS_POLL_PERIOD));
	}

	if (ts->pendown) {
		struct input_dev *input = ts->input;

		input_report_key(input, BTN_TOUCH, 0);
		input_report_abs(input, ABS_PRESSURE, 0);
		input_sync(input);

		ts->pendown = false;
		dev_vdbg(&ts->spi->dev, "UP\n");
	}

	return IRQ_HANDLED;
}


static int __devinit ads7846_probe(struct spi_device *spi)
{
err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq,
  irq_flags, spi->dev.driver->name, ts);

这个驱动既申请了 hard handler(ads7846_hard_irq) 也申请了thread_fn(ads7846_irq)。

### Linux AMP 架构下的 GPIO 中断配置与问题排查 在 Linux 环境下,特别是在不对称多处理 (Asymmetric Multiprocessing, AMP) 的架构中,GPIO 中断的配置和调试是一个复杂的过程。以下是关于如何实现 GPIO 中断配置以及可能遇到的问题及其解决方案。 #### 1. GPIO 中断的基础概念 GPIO(General Purpose Input/Output)通常用于连接外部设备并触发中断信号。在 AMP 架构中,不同处理器核心之间可能存在独立的操作系统实例或实时操作系统(RTOS),这使得 GPIO 配置更加具有挑战性[^1]。 #### 2. 配置 GPIO 中断的关键步骤 为了成功配置 GPIO 中断,在 Linux 下需要完成以下几个方面的工作: - **定义平台数据** 在设备树(Device Tree)文件中明确定义 GPIO 和其对应的中断控制器。例如,通过 `interrupt-parent` 属性指定父级中断控制器,并设置具体的中断号和触发模式[^2]。 ```dts gpio_keys { compatible = "gpio-keys"; button@0 { label = "User Button"; gpios = <&gpio1 17 GPIO_ACTIVE_LOW>; linux,code = <KEY_ENTER>; interrupt-controller; interrupts = <17 IRQ_TYPE_EDGE_FALLING>; }; } ``` - **注册中断处理程序** 使用标准的 Linux API 来请求和释放中断资源。可以通过调用函数 `request_irq()` 或更现代的方式使用 `devm_request_threaded_irq()` 注册中断服务例程(ISR)。这种方式可以确保驱动卸载时自动清理资源[^3]。 ```c static irqreturn_t gpio_interrupt_handler(int irq, void *data) { struct my_device_data *my_dev = data; // 处理中断逻辑 dev_info(my_dev->dev, "Interrupt triggered on GPIO\n"); return IRQ_HANDLED; } int register_gpio_interrupt(struct platform_device *pdev) { int ret, irq; struct my_device_data *my_dev = platform_get_drvdata(pdev); irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, /* Hard IRQ handler */ gpio_interrupt_handler, /* Threaded IRQ handler */ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "gpio-interrupt", my_dev); if (ret) { dev_err(&pdev->dev, "Failed to request threaded IRQ: %d\n", ret); return ret; } return 0; } ``` #### 3. 常见问题及解决方法 在实际开发过程中可能会面临一些常见问题,下面列举了一些典型情况及其应对策略: - **中断未被触发** 如果发现硬件操作正常但软件层面无法捕获到中断事件,则需检查以下几点: - 设备树中的 `interrupts` 定义是否正确匹配目标硬件; - 是否启用了相应的内核选项支持该类型的中断源[^4]。 - **共享中断冲突** 当多个外设共用同一个物理IRQ线时容易引发竞争条件。此时应考虑采用嵌套矢量中断控制器(NVIC)或者调整设计避免资源共享[^5]。 - **性能瓶颈** 对于高频发生的短脉冲输入场景,默认轮询机制可能导致延迟过高影响用户体验。建议优化代码结构引入工作队列(workqueue)异步执行耗时任务减轻主线负担[^6]。 #### 4. 调试技巧 利用丰富的工具链辅助定位潜在缺陷至关重要。推荐的方法包括但不限于启用 kernel log 输出跟踪具体流程走向;借助 perf 工具分析热点路径消耗时间占比等等[^7]。 ```bash # 查看当前已分配的所有中断状态信息 cat /proc/interrupts # 动态加载模块前先打印日志开关参数 echo 'options my_module debug=verbose' > /sys/module/my_module/parameters/debug_level ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值