linux设备驱动归纳总结(六):2.分享中断号
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
上一节介绍的内容是,调用接口request_irq(),使中断号与中断处理函数对应。但是,有时候会有这样的情况,如果开发板上按键的中断已经被另外的驱动程序注册中断了,而我现在又想再注册一次这个中断,这就出现了一个中断号不止对应一个中断函数的情况。注意,这里与硬件上的共享中断不一样,这里是指,当一个中断信号来了,基于操作系统,一个中断的到来可以调用多个中断处理程序,与硬件无关。
接下来从错误的代码开始讲解。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、错误的产生
以下的代码在“6th_irq_2/1st”中。
假设有这样的情况,有一个人,加载了模块test.ko,模块注册了中断号EINT1。接着,我编写代码(我并不知道中断号已经被使用了),加载模块test1.c(在err目录下),模块同样注册了中断号EINT1。但这样的注册不成功的。
看效果:
[root:1st]# insmod test.ko //某处加载第一个时成功
helloirq
[root:1st]# key down
keydown
[root:1st]# insmod err/test1.ko //假设我并不知道已经记载了一次,加载第二次时失败
<kernel>[test_init]requestirq failed!
insmod:cannot insert 'err/test1.ko': Deviceor resource busy
[root:1st]# cat /proc/interrupts //查看proc时发现,原来早就有人注册了EINT1中断号
CPU0
17: 2 s3c-ext0 key INT_EINT1
30: 20429 s3c S3C2410 Timer Tick
32: 0 s3c s3c2410-lcd
51: 3032 s3c-ext eth0
70: 252 s3c-uart0 s3c2440-uart
71: 277 s3c-uart0 s3c2440-uart
79: 0 s3c-adc s3c2410_action
80: 0 s3c-adc adc, s3c2410_action
83: 0 - s3c2410-wdt
Err: 0
这个就是两男争一妞的情况了,解决办法有两个:
1、动物界的规矩,干掉其中一个,谁赢谁说了算。
2、邪恶做法,和平解决,实现共享。
第一个解决办法很简单,查阅内核代码,找到注册该中断的模块,并且想办法卸载该模块。但是,如果那个模块实在是太重要的,不能卸载,那只能共享了。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、共享中断号的标记
在上一节的内容,注册中断处理函数接口request_irq()的参数irqflags还没完全介绍完毕,除了IRQ_TIRGGER_FALLING这类指明中断触发的条件的标志外,下面还介绍三个:
SA_INTERRUPT:这个标志表明该中断处理程序是一个快速中断处理程序。过去,linux系统会区分快速我慢速中断,但现在这个标志只有这样的效果:当响应这个中断时,禁止所有的中断,是该中断处理函数不会被其他中断打断,迅速执行。
SA_SAMPLE_RANDOM:这个标志表明产生的中断会对内核的entropypool有贡献。Entropypool负责产生随机数。
SA_SHIRQ:这个标志表明多个中断处理程序可以共享一个中断号。
相对其他两个,SA_SHIRQ是常用的标记。也就是说,我的中断注册失败,原因是我没有共享标记。也就是说,我需要在我的注册中断函数添加共享标记。但再回想一下两男争一妞的场景,需要共享前提是两个男的都同意共享,所以,原来的中断注册函数也需要共享标记。需要修改原来的函数test.c和我新写的test1.c,都加上共享标记SA_SHIRQ,表示它们两都同意共享。
在ARM下SA_SHIRQ相当于标志IRQF_SHARED:
/*iclude/linux/interrupt.h*/
53#define IRQF_DISABLED 0x00000020 //SA_INTERRUPT
54#define IRQF_SAMPLE_RANDOM 0x00000040 //SA_SAMPLE_RANDOM
55#define IRQF_SHARED 0x00000080 //SA_SHIRQ
看修改后的代码:
/*6th_irq_2/1st/test.c*/
30 ret = request_irq(IRQ_EINT1, irq_handler,
31 IRQF_TRIGGER_FALLING | IRQF_SHARED,"key INT_EINT1", NULL);
32 if(ret){
33 P_DEBUG("request irq failed!\n");
34 return ret;
35 }
另外一个一模一样
/*6th_irq_2/1st/err/test1.c*/
30 ret = request_irq(IRQ_EINT1, irq_handler,
31 IRQF_TRIGGER_FALLING | IRQF_SHARED,"key INT_EINT1", NULL);
32 if(ret){
33 P_DEBUG("request irq failed!\n");
34 return ret;
35 }
再加载一次,发现还是不行。
[root:/]# cd review_driver/6th_irq/6th_irq_2/2nd/
[root:2nd]# insmod test.ko //加载第一个时就已经不行了
<kernel>[test_init]requestirq failed!
insmod:cannot insert 'test.ko': invalidparameter//提示参数错误
[root:2nd]# insmod err/test1.ko
<kernel>[test_init]requestirq failed!
insmod:cannot insert 'err/test1.ko': invalid parameter
那到底是哪个参数错了?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、设备号ID——dev_id
话说两个难得已经同意共享,但为什么还是只能加载一个呢?女的说:“你们是同意了,但我不能分辨不你们吖!”
中断函数同时一样的道理,实现共享中断号的情况下,在调用free_irq()时,通过对应的标记,内核才会知道该释放哪个中断处理函数。
此时,最有一个没讲的参数dev_id就有他的用处了——内核通过dev_id对应中断处理函数handler。另外,也可以通过它来传参给中断处理函数。
再次修改两个程序,给每个程序就加上一个不同的dev_id:
/*6h_irq_2/1st/test.c*/
13irqreturn_t irq_handler(int irqno, void *dev_id) //中断处理函数
14{
15 printk("key down, dev_id[%d]\n", *(int *)dev_id);
16 return IRQ_HANDLED;
17}
18
19int id = 321;
20
。。。。
32 ret = request_irq(IRQ_EINT1, irq_handler,
33 IRQF_TRIGGER_FALLING | IRQF_SHARED, "key INT_EINT1",&id);
。。。。
44 free_irq(IRQ_EINT1,&id);
另外一个一模一样
6th_irq_2/1st/err/test.c
/*6h_irq_2/1st/test.c*/
13irqreturn_t irq_handler(int irqno, void *dev_id) //中断处理函数
14{
15 printk("hello xiaobai!, dev_id[%d]\n", *(int *)dev_id);
16 return IRQ_HANDLED;
17}
18
19int id = 123;
20
。。。。
32 ret = request_irq(IRQ_EINT1, irq_handler,
33 IRQF_TRIGGER_FALLING | IRQF_SHARED, "key INT_EINT1",&id);
。。。。
44 free_irq(IRQ_EINT1,&id);
验证一下,共享成功!
[root: 3rd]# insmod test.ko//加载第一个成功
hello irq
[root: 3rd]# key down, dev_id[321]
key down, dev_id[321]
key down, dev_id[321]
[root: 3rd]# insmod err/test1.ko //加载第二个也成功
hello irq
[root: 3rd]# key down,dev_id[321]//当我按下按键时,两个中断处理函数都调用了。
hello xiaobai!, dev_id[123]
key down, dev_id[321]
hello xiaobai!, dev_id[123]
key down, dev_id[321]
hello xiaobai!, dev_id[123]
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、介绍完了中断接口函数,下面简单讲一下一个中断产生后的流程:
以下的图是《linux内核设计与实现》上的插图,基于x86体系的,所以有些函数我在ARM下找不到。
先看前三步,这三步是我在《linux设备驱动归纳总结(六):1.中断的实现》的“从硬件角度看中断”有详细描述,当硬件(Hardware)产生中断,传给中断处理器(Interruptcontroller),经过中断处理器的一轮筛选后,把中断传给处理器(Processer)。
接下来就要讲解处理器接收到中断之后干什么:
1、处理器中断内核(processorinterrupts the kernel):这步骤如下:
1.1、处理器会立即停止它正在进行的事情。
1.2、处理器关闭中断。
1.3、保存原来寄存器的值(这些值属于被中断的任务),切换工作模式至IRQ(S3C2440有7种工作模式,芯片手册有讲解,切换工作模式之前需要保存原来部分寄存器上的值,具体请看S3C2440芯片手册)。
2、do_IRQ:(这个部分说得可能有错,因为我把内核的源代码仔细看过,这部分主要是为了引出下一个的函数handle_IRQ_event())
在ARM相关的内核代码我没找到do_IRQ函数,但我找到一个相关的——asm_do_IRQ。看看大概做了些什么事情:
2.1、把中断号和一个在内核中存放对应的相关数据的结构体(这个结构体就是存放处理器中寄存器的值)作为参数,传参给asm_do_IRQ。
/*linux-2.6.29/arch/arm/kernel/entry-armv.S*/
29 .macro irq_handler
30 get_irqnr_preamble r5, lr
311: get_irqnr_and_base r0, r6, r5, lr
32 movne r1, sp
33 @
34 @ routine called withr0 = irq number, r1 = struct pt_regs *
35 @ //获得中断号和一个结构体,作为参数传给asm_do_IRQ
36 adrne lr, 1b
37 bne asm_do_IRQ
2.2、asm_do_IRQ进行一系列的准备工作之后,调用函数generic_handle_irq():
/*linux-2.6.29/arch/arm/kernel/irq.c*/
112asmlinkage void __exception asm_do_IRQ(unsigned int irq, structpt_regs *regs)
113{
114 struct pt_regs *old_regs = set_irq_regs(regs);//保存并设置寄存器上的值,还没弄懂操作的原因
115
116 irq_enter(); //一系列准备操作,没细看
117
118 /*
119 * Some hardware gives randomly wrong interrupts. Rather
120 * than crashing, do something sensible.
121 */
122 if (irq >= NR_IRQS)
123 handle_bad_irq(irq, &bad_irq_desc);
124 else
125 generic_handle_irq(irq);//调用该函数,开始处理中断。
126
127 /* AT91 specific workaround */
128 irq_finish(irq);
129
130 irq_exit();
131 set_irq_regs(old_regs);
132}
2.3、generic_handle_irq中调用函数_do_IRQ:
/*linux-2.6.29/include/linux/irq.h*/
309 static inline voidgeneric_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
310 {
311 #ifdefCONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
312 desc->handle_irq(irq,desc);
313 #else
314 if(likely(desc->handle_irq))
315 desc->handle_irq(irq,desc);
316 else
317 __do_IRQ(irq);
318 #endif
319 }
320
321 static inline voidgeneric_handle_irq(unsigned int irq)
322 {
323 generic_handle_irq_desc(irq,irq_to_desc(irq));
324 }
2.4、在__do_IRQ中,会判断该中断号是否已经注册了中断处理函数,如果没有则退出中断,切换至原来的工作模式。如果有,__do_IRQ会调用函数handle_IRQ_event()。
3、irqreturn_thandle_IRQ_event(unsigned int irq, struct irqaction *action)干了些什么:
上面说的内容可能都不太详细,因为我也不太明白有些函数的作用和具体的内核代码,但下面的函数大家应该都会看得懂:
/*linux-2.6.29/kernel/irq/handle.c*/
326irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction*action)
327{
328 irqreturn_t ret, retval = IRQ_NONE;
329 unsigned int status = 0;
330
331 if (!(action->flags &IRQF_DISABLED))//在切换工作模式时内核是禁止了中断的,如果
332 local_irq_enable_in_hardirq(); //注册时使用标记IRQF_DISABLED,则开启中断
333
334 do {
335 ret = action->handler(irq, action->dev_id);//调用我们注册的中断处理函数
336 if (ret == IRQ_HANDLED)
337 status |= action->flags;
338 retval |= ret;
339 action = action->next;
340 } while (action); //这是个循环,那就说,如果我们使用的IRQF_SHARED标识,
341//它会轮流执行该中断号对应的所有注册的中断处理函数
342 if (status & IRQF_SAMPLE_RANDOM)//如果使用该标记时相应的操作
343 add_interrupt_randomness(irq);
344 local_irq_disable();//再次关上中断
345
346 return retval;
347}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、总结
总结一下中断注册的几个注意事项:
1、调用request_irq必须通过返回值判断是否成功。
2、共享中断号时,所有共享这个中断号的request_irq都必须添加标记IRQF_SHARED,另外还需要使用一个独特的设备好dev_id,让内核能够通过dev_id对应注册时的中断处理函数。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
转载自:http://my.chinaunix.net/space.php?uid=25014876&do=blog&id=90837
分享到:
相关推荐
此指令详细的描述了S7-200中断指令的各种用法,很好用, 好的东西大家拿出共同分享,希望大家喜欢!谢谢!
(增强)后台淘宝客采集增加中断继续功能 (修正)修正淘宝评论采集的一个BUG,增强稳定性 (增强)后台淘宝客采集增加API调用超限错误提示 (增强)后台增加URL后缀设置,如.html (增强)淘宝评论采集增加...
——S1与S2连接单片机上的S(我这里用的2号和3号,是中断引脚); ——G与V连接单片机上的G与V(对着接就行); ——VM与GM接航模电池的正极与负极; 测速原理 这里需要用到一些基本函数,大家可以去相关网站去找...
信誉至上,分享光荣,所有资源,全部一分 第1章 预备知识 1. 1 Linux内核简介 1. 2 Intel X86 CPU系列的寻址方式 1. 3 i386的页式内存管理机制 1. 4 Linux内核源代码中的C语言代码 1.5 Linux内核源代码中的汇编...
程序丢了可惜,分享下。 执行存储过程,通过指定目标数据库登录信息,动态生成dblink,打开游标,按批次插入本地指定表中,每批次1万条数据。同步记录和日志信息写在自动生成的sync_log表中。同步完成后自动删除动态...
程序具有良好的用户体验,适合美工人员快速建立站点,您也可以根据...2.修复资源站不稳定导致采集中断 3.修复推广连接需要登录错误 4.修复QQ登录插件回调地址?号错误 5.修复采集入库的时候同名类似演员的影片无法更新
分享给大家供大家参考,具体如下: php的多进程处理依赖于pcntl扩展,通过pcntl_fork创建子进程来进行并行处理。 例1如下: <?php $pid = pcntl_fork(); if($pid == -1) { //错误处理:创建子进程失败时返回-1....
电容器在光线短暂的中断期间保持电路运行,例如鸟在PV面板上飞行。使用78L09调节器IC将18V调节至9V,为其余电路提供稳定的9V电源。如上图所示,电路只能在阳光直射在面板上时起作用。一个较大的面板,在多云条件下...
除对操作系统常识性的了解(比如知道中断、进程等概念)之外,本书不假定读者具备其他任何经验。 如果你学习过操作系统的理论课程,你会发现本书是对于理论的吻合和补充。它是从实践的角度为你展现一幅操作系统画面...
除对操作系统常识性的了解(比如知道中断、进程等概念)之外,本书不假定读者具备其他任何经验。 如果你学习过操作系统的理论课程,你会发现本书是对于理论的吻合和补充。它是从实践的角度为你展现一幅操作系统画面...
网络安全知识入门 近日,因为工作需要,对于网络安全的一些基础的知识做了一些简单的了解,并整理成总结文档以便于学习和分享。 网络安全的知识体系非常庞大,想要系统的完成学习非简单的几天就可以完成的。所以这...
看着差不多完善了,主控板都稳定运行两个月不用重启,赶紧分享给大家。 硬件用的是stm32F030C8T6,开始用的f103c8发现太浪费了,就改F030C8。本来还有预留F030F4P6的部分,可惜pcb设计功力不够,nsf24l01要飞线出来...
这个作品是可以作为送给女友的DIY礼物,在这里分享出来可以帮助广大工程师、工科男在用于追求自己的女神,送给她不一样的礼物,比起那些到处都能买到的礼物,自己DIY的礼物更有诚意! 事业有成的工程师们,不要忘了...
并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之三:Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: ...
并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之三:Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: ...
并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之三:Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: ...
并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之三:Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: ...
并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之三:Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: ...
并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之三:Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: ...
并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之三:Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: ...