linux设备驱动归纳总结(六):2.分享中断号【转】
?轉(zhuǎn)自:http://blog.chinaunix.net/uid-25014876-id-90837.html
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
上一節(jié)介紹的內(nèi)容是,調(diào)用接口request_irq(),使中斷號(hào)與中斷處理函數(shù)對(duì)應(yīng)。但是,有時(shí)候會(huì)有這樣的情況,如果開發(fā)板上按鍵的中斷已經(jīng)被另外的驅(qū)動(dòng)程序注冊(cè)中斷了,而我現(xiàn)在又想再注冊(cè)一次這個(gè)中斷,這就出現(xiàn)了一個(gè)中斷號(hào)不止對(duì)應(yīng)一個(gè)中斷函數(shù)的情況。注意,這里與硬件上的共享中斷不一樣,這里是指,當(dāng)一個(gè)中斷信號(hào)來了,基于操作系統(tǒng),一個(gè)中斷的到來可以調(diào)用多個(gè)中斷處理程序,與硬件無關(guān)。
接下來從錯(cuò)誤的代碼開始講解。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
一、錯(cuò)誤的產(chǎn)生
?
以下的代碼在“6th_irq_2/1st”中。
假設(shè)有這樣的情況,有一個(gè)人,加載了模塊test.ko,模塊注冊(cè)了中斷號(hào)EINT1。接著,我編寫代碼(我并不知道中斷號(hào)已經(jīng)被使用了),加載模塊test1.c(在err目錄下),模塊同樣注冊(cè)了中斷號(hào)EINT1。但這樣的注冊(cè)不成功的。
看效果:
[root: 1st]# insmod test.ko //某處加載第一個(gè)時(shí)成功
hello irq
[root: 1st]# key down
key down
[root: 1st]# insmod err/test1.ko //假設(shè)我并不知道已經(jīng)記載了一次,加載第二次時(shí)失敗
[test_init]request irq failed!
insmod: cannot insert 'err/test1.ko':?Device or resource busy
[root: 1st]# cat /proc/interrupts //查看proc時(shí)發(fā)現(xiàn),原來早就有人注冊(cè)了EINT1中斷號(hào)
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
?
這個(gè)就是兩男爭(zhēng)一妞的情況了,解決辦法有兩個(gè):
1、動(dòng)物界的規(guī)矩,干掉其中一個(gè),誰贏誰說了算。
2、邪惡做法,和平解決,實(shí)現(xiàn)共享。
?
第一個(gè)解決辦法很簡(jiǎn)單,查閱內(nèi)核代碼,找到注冊(cè)該中斷的模塊,并且想辦法卸載該模塊。但是,如果那個(gè)模塊實(shí)在是太重要的,不能卸載,那只能共享了。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
二、共享中斷號(hào)的標(biāo)記
?
在上一節(jié)的內(nèi)容,注冊(cè)中斷處理函數(shù)接口request_irq()的參數(shù)irqflags還沒完全介紹完畢,除了IRQ_TIRGGER_FALLING這類指明中斷觸發(fā)的條件的標(biāo)志外,下面還介紹三個(gè):
SA_INTERRUPT:這個(gè)標(biāo)志表明該中斷處理程序是一個(gè)快速中斷處理程序。過去,linux系統(tǒng)會(huì)區(qū)分快速我慢速中斷,但現(xiàn)在這個(gè)標(biāo)志只有這樣的效果:當(dāng)響應(yīng)這個(gè)中斷時(shí),禁止所有的中斷,是該中斷處理函數(shù)不會(huì)被其他中斷打斷,迅速執(zhí)行。
SA_SAMPLE_RANDOM:這個(gè)標(biāo)志表明產(chǎn)生的中斷會(huì)對(duì)內(nèi)核的entropy pool有貢獻(xiàn)。Entropy pool負(fù)責(zé)產(chǎn)生隨機(jī)數(shù)。
SA_SHIRQ:這個(gè)標(biāo)志表明多個(gè)中斷處理程序可以共享一個(gè)中斷號(hào)。
?
相對(duì)其他兩個(gè),SA_SHIRQ是常用的標(biāo)記。也就是說,我的中斷注冊(cè)失敗,原因是我沒有共享標(biāo)記。也就是說,我需要在我的注冊(cè)中斷函數(shù)添加共享標(biāo)記。但再回想一下兩男爭(zhēng)一妞的場(chǎng)景,需要共享前提是兩個(gè)男的都同意共享,所以,原來的中斷注冊(cè)函數(shù)也需要共享標(biāo)記。需要修改原來的函數(shù)test.c和我新寫的test1.c,都加上共享標(biāo)記SA_SHIRQ,表示它們兩都同意共享。
?
在ARM下SA_SHIRQ相當(dāng)于標(biāo)志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 }
另外一個(gè)一模一樣
/*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 }
?
再加載一次,發(fā)現(xiàn)還是不行。
[root: /]# cd review_driver/6th_irq/6th_irq_2/2nd/
[root: 2nd]# insmod test.ko //加載第一個(gè)時(shí)就已經(jīng)不行了
[test_init]request irq failed!
insmod: cannot insert 'test.ko':?invalid parameter //提示參數(shù)錯(cuò)誤
[root: 2nd]# insmod err/test1.ko
[test_init]request irq failed!
insmod: cannot insert 'err/test1.ko': invalid parameter
?
那到底是哪個(gè)參數(shù)錯(cuò)了?
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
三、設(shè)備號(hào)ID——dev_id
?
話說兩個(gè)難得已經(jīng)同意共享,但為什么還是只能加載一個(gè)呢?女的說:“你們是同意了,但我不能分辨不你們吖!”
中斷函數(shù)同時(shí)一樣的道理,實(shí)現(xiàn)共享中斷號(hào)的情況下,在調(diào)用free_irq()時(shí),通過對(duì)應(yīng)的標(biāo)記,內(nèi)核才會(huì)知道該釋放哪個(gè)中斷處理函數(shù)。
此時(shí),最有一個(gè)沒講的參數(shù)dev_id就有他的用處了——內(nèi)核通過dev_id對(duì)應(yīng)中斷處理函數(shù)handler。另外,也可以通過它來傳參給中斷處理函數(shù)。
?
再次修改兩個(gè)程序,給每個(gè)程序就加上一個(gè)不同的dev_id:
/*6h_irq_2/1st/test.c*/
13 irqreturn_t irq_handler(int irqno, void *dev_id) //中斷處理函數(shù)
14 {
15 printk("key down, dev_id[%d]\n", *(int *)dev_id);
16 return IRQ_HANDLED;
17 }
18
19 int 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);
另外一個(gè)一模一樣
6th_irq_2/1st/err/test.c
/*6h_irq_2/1st/test.c*/
13 irqreturn_t irq_handler(int irqno, void *dev_id) //中斷處理函數(shù)
14 {
15 printk("hello xiaobai!, dev_id[%d]\n", *(int *)dev_id);
16 return IRQ_HANDLED;
17 }
18
19 int 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);
?
驗(yàn)證一下,共享成功!
[root: 3rd]# insmod test.ko //加載第一個(gè)成功
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 //加載第二個(gè)也成功
hello irq
[root: 3rd]# key down, dev_id[321] //當(dāng)我按下按鍵時(shí),兩個(gè)中斷處理函數(shù)都調(diào)用了。
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
?
四、介紹完了中斷接口函數(shù),下面簡(jiǎn)單講一下一個(gè)中斷產(chǎn)生后的流程:
?
以下的圖是《linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》上的插圖,基于x86體系的,所以有些函數(shù)我在ARM下找不到。
?
先看前三步,這三步是我在《linux設(shè)備驅(qū)動(dòng)歸納總結(jié)(六):1.中斷的實(shí)現(xiàn)》的“從硬件角度看中斷”有詳細(xì)描述,當(dāng)硬件(Hardware)產(chǎn)生中斷,傳給中斷處理器(Interrupt controller),經(jīng)過中斷處理器的一輪篩選后,把中斷傳給處理器(Processer)。
?
接下來就要講解處理器接收到中斷之后干什么:
1、處理器中斷內(nèi)核(processor interrupts the kernel):這步驟如下:
1.1、處理器會(huì)立即停止它正在進(jìn)行的事情。
1.2、處理器關(guān)閉中斷。
1.3、保存原來寄存器的值(這些值屬于被中斷的任務(wù)),切換工作模式至IRQ(S3C2440有7種工作模式,芯片手冊(cè)有講解,切換工作模式之前需要保存原來部分寄存器上的值,具體請(qǐng)看S3C2440芯片手冊(cè))。
?
2、do_IRQ:(這個(gè)部分說得可能有錯(cuò),因?yàn)槲野褍?nèi)核的源代碼仔細(xì)看過,這部分主要是為了引出下一個(gè)的函數(shù)handle_IRQ_event())
在ARM相關(guān)的內(nèi)核代碼我沒找到do_IRQ函數(shù),但我找到一個(gè)相關(guān)的——asm_do_IRQ。看看大概做了些什么事情:
2.1、把中斷號(hào)和一個(gè)在內(nèi)核中存放對(duì)應(yīng)的相關(guān)數(shù)據(jù)的結(jié)構(gòu)體(這個(gè)結(jié)構(gòu)體就是存放處理器中寄存器的值)作為參數(shù),傳參給asm_do_IRQ。
/*linux-2.6.29/arch/arm/kernel/entry-armv.S*/
29 .macro irq_handler
30 get_irqnr_preamble r5, lr
31 1: get_irqnr_and_base r0, r6, r5, lr
32 movne r1, sp
33 @
34 @ routine called with?r0 = irq number, r1 = struct pt_regs *
35 @?//獲得中斷號(hào)和一個(gè)結(jié)構(gòu)體,作為參數(shù)傳給asm_do_IRQ
36 adrne lr, 1b
37 bne?asm_do_IRQ
2.2、asm_do_IRQ進(jìn)行一系列的準(zhǔn)備工作之后,調(diào)用函數(shù)generic_handle_irq():
/*linux-2.6.29/arch/arm/kernel/irq.c*/
112 asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
113 {
114 struct pt_regs *old_regs = set_irq_regs(regs);?//保存并設(shè)置寄存器上的值,還沒弄懂操作的原因
115
116 irq_enter(); //一系列準(zhǔn)備操作,沒細(xì)看
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); //調(diào)用該函數(shù),開始處理中斷。
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中調(diào)用函數(shù)_do_IRQ:
/*linux-2.6.29/include/linux/irq.h*/
309 static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
310 {
311 #ifdef CONFIG_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 void generic_handle_irq(unsigned int irq)
322 {
323 generic_handle_irq_desc(irq, irq_to_desc(irq));
324 }
2.4、在__do_IRQ中,會(huì)判斷該中斷號(hào)是否已經(jīng)注冊(cè)了中斷處理函數(shù),如果沒有則退出中斷,切換至原來的工作模式。如果有,__do_IRQ會(huì)調(diào)用函數(shù)handle_IRQ_event()。
?
3、irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)?干了些什么:
上面說的內(nèi)容可能都不太詳細(xì),因?yàn)槲乙膊惶靼子行┖瘮?shù)的作用和具體的內(nèi)核代碼,但下面的函數(shù)大家應(yīng)該都會(huì)看得懂:
/*linux-2.6.29/kernel/irq/handle.c*/
326 irqreturn_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)) //在切換工作模式時(shí)內(nèi)核是禁止了中斷的,如果
332 local_irq_enable_in_hardirq(); //注冊(cè)時(shí)使用標(biāo)記IRQF_DISABLED,則開啟中斷
333
334 do {
335 ret = action->handler(irq, action->dev_id); //調(diào)用我們注冊(cè)的中斷處理函數(shù)
336 if (ret == IRQ_HANDLED)
337 status |= action->flags;
338 retval |= ret;
339 action = action->next;
340 } while (action); //這是個(gè)循環(huán),那就說,如果我們使用的IRQF_SHARED標(biāo)識(shí),
341 //它會(huì)輪流執(zhí)行該中斷號(hào)對(duì)應(yīng)的所有注冊(cè)的中斷處理函數(shù)
342 if (status & IRQF_SAMPLE_RANDOM) //如果使用該標(biāo)記時(shí)相應(yīng)的操作
343 add_interrupt_randomness(irq);
344?local_irq_disable(); //再次關(guān)上中斷
345
346 return retval;
347 }
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
五、總結(jié)
?
總結(jié)一下中斷注冊(cè)的幾個(gè)注意事項(xiàng):
1、調(diào)用request_irq必須通過返回值判斷是否成功。
2、共享中斷號(hào)時(shí),所有共享這個(gè)中斷號(hào)的request_irq都必須添加標(biāo)記IRQF_SHARED,另外還需要使用一個(gè)獨(dú)特的設(shè)備好dev_id,讓內(nèi)核能夠通過dev_id對(duì)應(yīng)注冊(cè)時(shí)的中斷處理函數(shù)。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
源代碼:?6th_irq_2.rar???
【作者】張昺華 【出處】http://www.cnblogs.com/sky-heaven/ 【博客園】 http://www.cnblogs.com/sky-heaven/ 【新浪博客】 http://blog.sina.com.cn/u/2049150530 【知乎】 http://www.zhihu.com/people/zhang-bing-hua 【我的作品---旋轉(zhuǎn)倒立擺】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【我的作品---自平衡自動(dòng)循跡車】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【新浪微博】 張昺華--sky 【twitter】 @sky2030_ 【facebook】 張昺華 zhangbinghua 本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利.總結(jié)
以上是生活随笔為你收集整理的linux设备驱动归纳总结(六):2.分享中断号【转】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: abap常用函数
- 下一篇: linux下调试core的命令