Request_irq参数dev_id的真正作用

本文深入探讨了Linux内核中断处理机制中的request_irq函数及其参数dev_id的作用,解释了为何在共享中断时需要提供唯一dev_id以及其在中断处理程序释放和设备结构体传递中的应用。
 

Request_irq参数dev_id的真正作用
转自:http://www.diybl.com/course/6_system/linux/Linuxjs/2008720/133589.html 
注:若对kernel中断处理模型不是很清楚的话(如:irqaction的作用)可以先参考一下这篇文档:
http://blog.youkuaiyun.com/mxslgyb/article/details/6700586

这里主要讲request_irq的参数dev_id的作用,内容会涉及到少许上面文档提到的内容。
 
Request_irq的作用是申请使用IRQ并注册中断处理程序。
request_irq()函数的原型如下:

显示代码打印
1 /* kernel/irq/manage.c */ 

2 int request_irq(unsigned int irq,  

3     irqreturn_t (*handler)(int, void *, struct pt_regs *),  

4     unsigned long irqflags,   

5     const char *devname,   

6     void *dev_id);

 

我们知道,当使用内核共享中断时,request_irq必须要提供dev_id参数,并且dev_id的值必须唯一。那么这里提供唯一的dev_id值的究竟是做什么用的?
 
起先我以为dev_id的值是提供给kernel进行判断共享中断线上的哪一个设备产生了中断(即哪个irqaction产生中断),然后执行相应的中断处理函数(irqaction->handler)。实际上不是的,我们来看看《Linux Kernel Development – Second Edition》第六章中Shared Handlers这一节,其中有段总结性的文字如下:
 
When the kernel receives an interrupt, it invokes sequentially each registered handler on the line.

Therefore, it is important that the handler be capable of distinguishing

whether it generated a given interrupt. The handler must quickly exit if its associated device did not

 generate the interrupt. This requires the hardware device to have a status register (or similar mechanism)

that the handler can check. Most hardware does indeed have such a feature.
 
这段话的大概意思是,发生中断时,内核并不判断究竟是共享中断线上的哪个设备产生了中断,它会循环执行所有该中断线上注册的中断处理函数(即irqaction->handler函数)。因此irqaction->handler函数有责任识别出是否是自己的硬件设备产生了中断,然后再执行该中断处理函数。通常是通过读取该硬件设备提供的中断flag标志位进行判断。
 
那既然kernel循环执行该中断线上注册的所有irqaction->handler函数,把识别究竟是哪个硬件设备产生了中断这件事交给中断处理函数本身去做,那request_irq的dev_id参数究竟是做什么用的?
 
我总结了一下,实际上dev_id作用主要有两点:
一.在中断处理程序释放时使用到dev_id
When your driver unloads, you need to unregister your interrupt handler and potentially disable the interrupt line. To do this, call
void free_irq(unsigned int irq, void *dev_id)
……
The fifth parameter, dev_id, is used primarily for shared interrupt lines. When an interrupt handler is freed (discussed later), dev_id provides a unique cookie to allow the removal of only the desired interrupt handler from the interrupt line. Without this parameter, it would be impossible for the kernel to know which handler to remove on a given interrupt line.
 
这里《LKD2》讲了很清楚,当调用free_irq注销中断处理函数时(通常卸载驱动时其中断处理函数也会被注销掉),因为dev_id是唯一的,所以可以通过它来判断从共享中断线上的多个中断处理程序中删除指定的一个。如果没有这个参数,那么kernel不可能知道给定的中断线上到底要删除哪一个处理程序。
 
下面我们来看一下free_irq的代码:


显示代码打印
01 void free_irq(unsigned int irq, void *dev_id)  

02 {  

03     struct irq_desc *desc;  

04     struct irqaction **p;  

05     unsigned long flags;  

06   

07     WARN_ON(in_interrupt());  

08     if (irq >= NR_IRQS)  

09         return;  

10   

11     desc = irq_desc + irq;    /* 获取该中断号对应的irq_desc */ 

12     spin_lock_irqsave(&desc->lock, flags);  

13     p = &desc->action;        /* 找到该irq的irqaction链表 */ 

14     for (;;) {  

15         struct irqaction *action = *p;  

16   

17         if (action) {  

18             struct irqaction **pp = p;  

19   

20             p = &action->next;  

21             if (action->dev_id != dev_id)   

22                 continue; /* 这两句进行循环判断,直到找到相应dev_id设备的irqaction */ 

23   

24             /* Found it - now remove it from the list of entries */ 

25             *pp = action->next; /* 指向下一个irqaction */ 

26   

27             /* Currently used only by UML, might disappear one day.*/ 

28 #ifdef CONFIG_IRQ_RELEASE_METHOD  

29             if (desc->chip->release)  

30                 desc->chip->release(irq, dev_id);  

31 #endif  

32   

33             if (!desc->action) {  

34                 desc->status |= IRQ_DISABLED;  

35                 if (desc->chip->shutdown)  

36                     desc->chip->shutdown(irq);  

37                 else 

38                     desc->chip->disable(irq);  

39             }  

40             recalculate_desc_flags(desc);  

41             spin_unlock_irqrestore(&desc->lock, flags);  

42             unregister_handler_proc(irq, action);  

43   

44             /* Make sure it’s not being used on another CPU */ 

45             synchronize_irq(irq);  

46             kfree(action);        /* 删除该设备(dev_id)的irqaction */ 

47             return;  

48         }  

49         printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);  

50         spin_unlock_irqrestore(&desc->lock, flags);  

51         return;  

52     }  

53 }
 

 


二.将使用该中断处理程序的设备结构体传递给该中断处理程序
首先看两个基础条件:
1)  内核中的各个设备结构体肯定是唯一的,因此满足dev_id唯一这个要求
2)  dev_id参数会在发生中断时传递给该中断的服务程序。
典型的中断服务程序定义如下:


显示代码打印
1 static irqreturn_t intr_handler(int irq, void *dev_id, struct pt_regs *regs);


即request_irq的dev_id参数会传递给该中断服务程序的dev_id。因此也可以将驱动程序的设备结构体通过dev_id传递给中断服务程序。
这样做作用主要有两个:
1) 中断服务程序可能使用到设备结构体中的信息
如s3c2410的iis音频驱动,它是将DMA Channel结构体通过dev_id传递给中断服务程序


显示代码打印
1 /* arch/arm/mach-s3c2410/dma.c */ 

2 int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client,void *dev)  

3 {  

4 err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED,  

5                  client->name, (void *)chan);  

6 ……  

7 }

 

目的是中断处理函数s3c2410_dma_irq内需要用到chan结构体的duf成员和load_state成员等。
使用方法如下:


显示代码打印
1 /* arch/arm/mach-s3c2410/dma.c */ 

2 static irqreturn_t  

3 s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs)  

4 {  

5     struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; /* 这里强制转化成request_irq时传给dev_id的设备类型,此处即s3c2410_dma_chan ,下面便可以使用它*/ 

6     struct s3c2410_dma_buf *buf;  

7     buf = chan->curr;  

8     ……  

9 }


 
因此,我们可以通过将设备结构传递给request_irq的dev_id参数这种机制,在中断处理函数中使用该设备结构体,这是大部分驱动程序通用的一种手法。
 
2)前面我们讲了若使用共享中断,那么中断处理函数自身需要能识别是否是自己的设备产
生了中断。通常这是通过读取该硬件设备提供的中断flag标志位进行判断的。
而往往驱动程序里定义的设备结构体通常包含了该设备的IO地址,加上偏移就可以算出中断状态寄存器及中断flag标志位的IO地址信息。因此将设备结构体通过dev_id传递给设备中断处理程序的另一个作用就是使用共享中断时,可以在中断处理函数内通过读取该设备结构 (dev_id) 中提供的中断Flag标志位地址信息进行判断,是否是该设备产生了中断,然后再进一步判断是否继续往下执行还是跳到下一个irqaction->handler函数再判断执行。当然,如果本来就知道该设备中断状态寄存器的IO地址,也可以选择直接readl该地址信息进行判断。

 

文章出处:飞诺网(www.diybl.com):http://www.diybl.com/course/6_system/linux/Linuxjs/2008720/133589.html

<think> 首先,用户的问题是:“我想查找devm_request_threaded_irq函数的参数表 请问devm_request_threaded_irq函数参数表”。这是一个直接的查询,要求提供这个Linux内核函数的参数表。 从系统级指令中,我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户可见层要求:回答结构清晰,帮助用户逐步解决问题,尽量保证回答真实可靠。 参考提供的引用: - 引用[1]:提到调用devm_request_threaded_irq()时返回-22,可能由于无效的中断号。参数包括:struct device *dev, unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags, const char *name, void *dev_id。 - 引用[2]:提供了函数原型:int devm_request_threaded_irq(struct device *dev, unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags, const char *name, void *dev_id)。 - 引用[3]:类似,提及request_threaded_irq参数。 - 引用[4]:详细说明每个参数的含义:struct device *dev (设备指针), unsigned int irq (中断号), irq_handler_t handler (主处理函数), irq_handler_t thread_fn (线程处理函数), unsigned long irqflags (中断标志), const char *devname (设备名称), void *dev_id (设备标识符)。还解释了函数的作用和返回值。 - 引用[5]:讨论另一个函数,但可能与devm_request_threaded_irq相关。 用户指定要查找参数表,所以我应该基于引用[4]来提供详细参数说明,因为它最全面。 我的回答结构: 1. **清晰介绍函数**:简要解释devm_request_threaded_irq函数的作用,基于引用。 2. **提供参数表**:列出所有参数,包括类型、名称和描述。使用表格或列表格式使其清晰。既然指令要求使用LaTeX,但我需要保持文本可读,我应该用Markdown列表或类似方式,但确保在文本中描述。 - 指令说“独立公式必须使用$$...$$格式”,但参数表不是数学公式,所以我应该用文本描述。 - 我可以创建一个表格或列表。 3. **添加引用标识**:在描述参数时,自然地添加引用,如[^4]。 4. **生成相关问题**:在回答末尾,添加§§相关问题§§部分。 5. **使用中文**:整个回答用中文。 步骤: - 开头:重述用户问题并简要介绍函数。 - 主体:列出参数表。每个参数一行,解释其含义。 - 结尾:总结或添加注意事项。 - 相关问题:生成2-3个相关问题。 参数表基于引用[4]: - struct device *dev: 指向表示设备的 struct device 结构体的指针。 - unsigned int irq: 中断号。 - irq_handler_t handler: 中断处理程序的主处理函数。 - irq_handler_t thread_fn: 线程处理函数。 - unsigned long irqflags: 中断标志。 - const char *devname: 设备名称。 - void *dev_id: 设备标识符。 返回值:int,0表示成功,负数错误码。 在回答中,我需要添加引用标识。例如,在解释后说[^4]。 相关问题建议: 1. devm_request_threaded_irq与其他中断注册函数如request_irq的区别是什么? 2. 在Linux驱动中如何正确使用devm_request_threaded_irq? 3. 处理中断时常见错误如返回-22如何调试? 确保回答真实可靠:基于提供的引用,不要添加虚构信息。 最终回答结构: 1. 标题或介绍。 2. 参数列表。 3. 注意事项(如返回值)。 4. 相关问题。 </think>### devm_request_threaded_irq 函数参数表 `devm_request_threaded_irq` 是 Linux 内核中用于注册线程化中断处理程序的函数,它允许在主中断处理程序外执行耗时操作,避免阻塞中断上下文。该函数通过 `devm`(设备资源管理)机制自动管理资源,在设备卸载时自动释放资源,避免内存泄漏[^4]。以下是完整的参数表,基于 Linux 内核文档和相关引用整理而成[^2][^4]。 #### 参数表 以下是 `devm_request_threaded_irq` 函数的参数列表,每个参数包括类型、名称和详细描述: 1. **struct device *dev** - **类型**:指向 `struct device` 结构体的指针。 - **描述**:表示请求中断的设备实例。通常从 `platform_device` 或类似结构中获取,用于资源管理。该指针确保中断资源与设备生命周期绑定[^4]。 2. **unsigned int irq** - **类型**:无符号整数。 - **描述**:中断号,标识要注册的硬件中断线。必须是有效的系统中断号(例如通过 `platform_get_irq()` 获取),否则函数可能返回错误(如 -EINVAL)[^1][^4]。 3. **irq_handler_t handler** - **类型**:中断处理函数指针(`irq_handler_t`)。 - **描述**:主中断处理程序,在中断发生时立即执行(在中断上下文中)。通常用于快速操作,如清除中断标志或保存数据。如果设置为 `NULL`,线程处理函数将直接执行[^4]。 4. **irq_handler_t thread_fn** - **类型**:中断处理函数指针(`irq_handler_t`)。 - **描述**:线程处理函数,在主处理程序后以线程化方式执行(在进程上下文中)。适用于耗时任务(如 I/O 或数据处理),避免阻塞中断。必须提供此函数或主处理函数[^4]。 5. **unsigned long irqflags** - **类型**:无符号长整型。 - **描述**:中断标志,指定中断属性和行为。常用标志包括: - `IRQF_TRIGGER_RISING`:上升沿触发。 - `IRQF_TRIGGER_FALLING`:下降沿触发。 - `IRQF_ONESHOT`:中断处理完成后重新使能中断(常用于线程化模式)。 - `IRQF_SHARED`:允许多个设备共享同一中断线。 标志可组合使用,例如 `IRQF_TRIGGER_RISING | IRQF_ONESHOT`[^4]。 6. **const char *devname** - **类型**:指向常量字符的指针(字符串)。 - **描述**:设备名称,用于标识中断请求的设备(例如 `"touchscreen"`)。在系统日志或调试信息中显示,帮助识别中断来源[^4]。 7. **void *dev_id** - **类型**:`void` 指针。 - **描述**:设备标识符,通常是设备结构体指针或其他唯一标识。用于共享中断时区分设备;非共享时可设为 `NULL`。在中断处理函数中作为参数传递[^4]。 #### 返回值 - **类型**:整数(`int`)。 - **描述**: - 返回 `0` 表示成功注册中断。 - 返回负值表示错误,常见错误码: - `-EINVAL`(-22):无效参数(如中断号无效)[^1]。 - `-EBUSY`:中断线已被占用(未使用 `IRQF_SHARED`)。 - `-ENOMEM`:内存分配失败。 错误码可通过内核日志调试。 #### 注意事项 - 该函数是 `request_threaded_irq` 的 `devm` 版本,自动处理资源释放,减少驱动代码复杂性[^4]。 - 使用前需确保中断号有效(例如通过设备树或平台数据获取)。 - 典型用法示例(伪代码): ```c ret = devm_request_threaded_irq(dev, irq_num, top_half, bottom_half, IRQF_ONESHOT, "example_device", dev_id); if (ret < 0) { printk("中断注册失败: %d\n", ret); // 调试错误 } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值