linux设备驱动归纳总结(六):2.分享中断号【转】
?轉自:http://blog.chinaunix.net/uid-25014876-id-90837.html
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
上一節介紹的內容是,調用接口request_irq(),使中斷號與中斷處理函數對應。但是,有時候會有這樣的情況,如果開發板上按鍵的中斷已經被另外的驅動程序注冊中斷了,而我現在又想再注冊一次這個中斷,這就出現了一個中斷號不止對應一個中斷函數的情況。注意,這里與硬件上的共享中斷不一樣,這里是指,當一個中斷信號來了,基于操作系統,一個中斷的到來可以調用多個中斷處理程序,與硬件無關。
接下來從錯誤的代碼開始講解。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
一、錯誤的產生
?
以下的代碼在“6th_irq_2/1st”中。
假設有這樣的情況,有一個人,加載了模塊test.ko,模塊注冊了中斷號EINT1。接著,我編寫代碼(我并不知道中斷號已經被使用了),加載模塊test1.c(在err目錄下),模塊同樣注冊了中斷號EINT1。但這樣的注冊不成功的。
看效果:
[root: 1st]# insmod test.ko //某處加載第一個時成功
hello irq
[root: 1st]# key down
key down
[root: 1st]# insmod err/test1.ko //假設我并不知道已經記載了一次,加載第二次時失敗
[test_init]request irq failed!
insmod: cannot insert 'err/test1.ko':?Device or 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:這個標志表明產生的中斷會對內核的entropy pool有貢獻。Entropy pool負責產生隨機數。
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 //加載第一個時就已經不行了
[test_init]request irq failed!
insmod: cannot insert 'test.ko':?invalid parameter //提示參數錯誤
[root: 2nd]# insmod err/test1.ko
[test_init]request irq 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*/
13 irqreturn_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
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);
另外一個一模一樣
6th_irq_2/1st/err/test.c
/*6h_irq_2/1st/test.c*/
13 irqreturn_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
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);
?
驗證一下,共享成功!
[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)產生中斷,傳給中斷處理器(Interrupt controller),經過中斷處理器的一輪篩選后,把中斷傳給處理器(Processer)。
?
接下來就要講解處理器接收到中斷之后干什么:
1、處理器中斷內核(processor interrupts 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
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 @?//獲得中斷號和一個結構體,作為參數傳給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*/
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);?//保存并設置寄存器上的值,還沒弄懂操作的原因
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 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中,會判斷該中斷號是否已經注冊了中斷處理函數,如果沒有則退出中斷,切換至原來的工作模式。如果有,__do_IRQ會調用函數handle_IRQ_event()。
?
3、irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)?干了些什么:
上面說的內容可能都不太詳細,因為我也不太明白有些函數的作用和具體的內核代碼,但下面的函數大家應該都會看得懂:
/*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)) //在切換工作模式時內核是禁止了中斷的,如果
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
源代碼:?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 【我的作品---旋轉倒立擺】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【我的作品---自平衡自動循跡車】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【新浪微博】 張昺華--sky 【twitter】 @sky2030_ 【facebook】 張昺華 zhangbinghua 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利.總結
以上是生活随笔為你收集整理的linux设备驱动归纳总结(六):2.分享中断号【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: abap常用函数
- 下一篇: 数据库设计(6/9):存储过程主体