linux块设备驱动中断程序,linux设备驱动归纳总结(六):1.中断的实现
linux設(shè)備驅(qū)動(dòng)歸納總結(jié)(六):1.中斷的實(shí)現(xiàn)
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、什么是中斷
中斷分兩種:
1)中斷,又叫外部中斷或異步中斷,它的產(chǎn)生是由于外設(shè)向處理器發(fā)出中斷請(qǐng)求。其中外部中斷也有兩種,這是由配置寄存器設(shè)定的:普通中斷請(qǐng)求(IRQ)和快速中斷請(qǐng)求(FIQ)。一般地,linux下很少使用快速中斷請(qǐng)求。
2)異常,又叫內(nèi)部中斷或同步中斷,它的產(chǎn)生是由于處理器執(zhí)行指令出錯(cuò)。
在以下的內(nèi)容我是要介紹由于外部設(shè)備產(chǎn)生的中斷。
這里我還有兩個(gè)名詞要說清楚
1)中斷請(qǐng)求線:在后面也叫中斷號(hào),每個(gè)中斷都會(huì)通過一個(gè)唯一的數(shù)值來標(biāo)識(shí),而這個(gè)值就稱做中斷請(qǐng)求線
2)在2440芯片中,有些中斷是需要共享一個(gè)中斷寄存器中的一位,如EINT4——EINT7,它們是共享寄存器SRCPEND的第4位。具體可以查看芯片手冊(cè)。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、什么是中斷處理函數(shù)
在相應(yīng)一個(gè)中斷是,內(nèi)核會(huì)執(zhí)行該信號(hào)對(duì)應(yīng)的一個(gè)函數(shù),該函數(shù)就叫做該中斷對(duì)應(yīng)的中斷處理函數(shù)。一般來說,中斷的優(yōu)先級(jí)是最高的,一但接收到中斷,內(nèi)核就會(huì)調(diào)用對(duì)應(yīng)的中斷處理函數(shù)。
中斷處理函數(shù)運(yùn)行在中斷上下文中。中斷上下文與內(nèi)核上下文有一點(diǎn)區(qū)別:
內(nèi)核上下文是指應(yīng)用層調(diào)用系統(tǒng)調(diào)用陷入內(nèi)核執(zhí)行,內(nèi)核代表陷入的進(jìn)程執(zhí)行操作。函數(shù)中可以通過current查看當(dāng)前進(jìn)程(即應(yīng)用層的進(jìn)程)的信息,并且可以睡眠。
中斷上下文中,不能通過current查看調(diào)用它的應(yīng)用層進(jìn)程的信息,同時(shí),處于中斷上下文時(shí),不能睡眠。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、從硬件角度看中斷
中斷的產(chǎn)生到處理器獲得中斷這段過程中,還要通過中斷處理器來篩選信號(hào)。
先溫習(xí)一下S3C2440芯片手冊(cè)的知識(shí):中斷是如何產(chǎn)生的,中斷處理器本身如何處理中斷。先看一下一幅經(jīng)典的圖,這是介紹中斷控制器的工作流程:
從硬件上的分類,有兩種不同的中斷類型:
1)自己占有SORCPND寄存器的一位(without
sub-register)。
2)幾個(gè)中斷共同享用SRCPND寄存器的一位(with
sub-register)。
其實(shí)兩種都差不多,只是多了兩步的檢測。我以自己占用一位的中斷來舉例,如EINT1,在我的開發(fā)板,EINT1上接了一個(gè)按鍵。
1)當(dāng)我按下按鍵產(chǎn)生電平變化,傳到S3C2440的中斷控制器上(即將要進(jìn)入上面圖的流程圖)。
2)首先,信號(hào)要經(jīng)過寄存器SRCPND,SRCPND是用來配置當(dāng)前的處理器要接收什么中斷,如果該寄存器配置成接收EINT1中斷(對(duì)應(yīng)位置一),則允許繼續(xù)下一步。
3)然后,信號(hào)經(jīng)過寄存器MASK,這是用來設(shè)置當(dāng)前系統(tǒng)需要屏蔽的中斷。注意,這里的屏蔽跟上一個(gè)寄存器的不接收中斷是不一樣的。這里的屏蔽是指,中斷是接受了,但是由于某種原因,先暫時(shí)不屏蔽產(chǎn)生的中斷。
4)通過INTPND寄存器,查看當(dāng)前是否有相同的中斷已經(jīng)被請(qǐng)求(如果是,INTPND對(duì)應(yīng)位置一)。
5)如果沒有相同的中斷在請(qǐng)求,中斷處理器才會(huì)把這個(gè)信號(hào)傳給處理器,這時(shí)處理器才會(huì)知道有EINT0的中斷真正來了,要對(duì)信號(hào)進(jìn)行處理了。
注:如果設(shè)定了EINT0是快速中斷模式(FIQ),中斷通過SRCPND寄存器后就會(huì)通過MODE寄存器的判斷,確定是FIQ后,中斷控制器優(yōu)先將該中斷傳給CPU處理。
6)對(duì)應(yīng)傳來的中斷類型(IRQ或FIQ),通過CPSR寄存器切換到對(duì)應(yīng)的工作模式(ARM有七種工作模式)。
7)切換工作模式后,進(jìn)入指定的中斷處理入口執(zhí)行中斷處理函數(shù)。
注意:第6、7步在linux下的實(shí)現(xiàn)相對(duì)復(fù)雜,不像在裸板程序,只需要切換一下工作模式,執(zhí)行相應(yīng)的函數(shù)就可以了。遲點(diǎn)會(huì)介紹linux如何實(shí)現(xiàn)。
來個(gè)流程圖比較只在,同時(shí)來個(gè)類比,處理器是老板,中斷處理器是小秘:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、注冊(cè)和釋放中斷處理函數(shù)
上面的介紹只是講解了一個(gè)設(shè)備產(chǎn)生中斷后要經(jīng)過怎么樣的步驟才能讓處理器接收到中斷信號(hào)。傳入處理器后,接下來的工作就是由內(nèi)核來實(shí)現(xiàn)了,那是一個(gè)復(fù)雜的機(jī)制,我們這里先不說。但是,內(nèi)核提供了相關(guān)的接口給我們,我們只要通過接口告訴內(nèi)核,當(dāng)來了指定中斷時(shí),內(nèi)核你該執(zhí)行哪個(gè)中斷處理函數(shù)。
注冊(cè)中斷處理函數(shù):
/*include
*/
int
request_irq(unsigned int irq, irq_handler_t handler,
unsigned
long irqflags, const char *devname, void *dev_id)
使用:
將中斷號(hào)irq與中斷處理函數(shù)handler對(duì)應(yīng)
參數(shù):
irq:指定要分配的中斷號(hào),中斷號(hào)的定義在“include/mach/irqs.h”中。注意,不管是單獨(dú)占有中斷請(qǐng)求線的中斷,還是共享中斷請(qǐng)求線的每個(gè)中斷,都有一個(gè)對(duì)應(yīng)的中斷號(hào)。,所以,調(diào)用該函數(shù)不需要考慮是哪種中斷(是否共享寄存器),你想哪種中斷響應(yīng),你就填對(duì)應(yīng)的中斷號(hào)。
handler:中斷處理函數(shù)指針。
irqflags:中斷處理標(biāo)記,待會(huì)介紹:
devname:該字符串將顯示在/proc/irq和/pro/interrupt中。
dev_id:ID號(hào),待會(huì)會(huì)介紹。
返回值:成功返回0,失敗返回非0。
注冊(cè)函數(shù)需要注意兩件事:
1)該函數(shù)會(huì)睡眠。
2)必須判斷返回值。
中斷處理標(biāo)志irqflags,這里先介紹幾個(gè)待會(huì)要用的:
/*linux-2.6.29/include/linux/interrupt.h*/
29
#define IRQF_TRIGGER_NONE 0x00000000
30
#define IRQF_TRIGGER_RISING 0x00000001 //上升沿觸發(fā)中斷
31
#define IRQF_TRIGGER_FALLING 0x00000002 //下降沿觸發(fā)中斷
32
#define IRQF_TRIGGER_HIGH 0x00000004 //高電平觸發(fā)中斷
33
#define IRQF_TRIGGER_LOW 0x00000008 //低電平觸發(fā)中斷
34
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
35
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
36
#define IRQF_TRIGGER_PROBE 0x00000010
釋放中斷處理函數(shù):
void
free_irq(unsigned int irq, void *dev_id)
編寫中斷處理函數(shù):
中斷處理函數(shù)聲明如下:
static
irqreturn_t intr_handler(int irq, void *dev_id)
先看第一個(gè)參數(shù)irq,這是調(diào)用中斷處理函數(shù)時(shí)傳給它的中斷號(hào),對(duì)于新版本的內(nèi)核,這個(gè)參數(shù)已經(jīng)用處不大,一般只用于打印。
第二個(gè)參數(shù)dev_id,這個(gè)參數(shù)與request_irq()的參數(shù)dev_id一致,由于待會(huì)的程序我并不需要用這個(gè)參數(shù),所以先不介紹。
再看返回值,中斷處理函數(shù)的返回值有三個(gè):
/*linux-2.6.29/include/linux/interrupt..h*/
21
#define IRQ_NONE (0) //如果產(chǎn)生的中斷并不會(huì)執(zhí)行該中斷處理函數(shù)時(shí)返回該值
22
#define IRQ_HANDLED (1) //中斷處理函數(shù)正確調(diào)用會(huì)返回
23
#define IRQ_RETVAL(x) ((x) != 0) //指定返回的數(shù)值,如果非0,返回IRQ_HADLER,否則
26
#ifndef IRQ_NONE //返回IRQ_NONE。
接下來就要寫函數(shù)了,在我的開發(fā)板中,有一個(gè)按鍵是對(duì)應(yīng)EINT1,我要實(shí)現(xiàn)的操作是,當(dāng)我按下按鍵,終端打印”key
down”。在這個(gè)程序中,我并沒有使用dev_id。這將在會(huì)以后的章節(jié)介紹。
/*6th_irq_1/1st/test.c*/
1
#include
2
#include
3
4
#include
5
。。。省略。。。
13
irqreturn_t irq_handler(int irqno, void *dev_id) //中斷處理函數(shù)
14
{
15
printk("key down\n");
16
return IRQ_HANDLED;
17
}
18
19
static int __init test_init(void) //模塊初始化函數(shù)
20
{
21
int ret;
22
23
/*注冊(cè)中斷處理函數(shù),必須查看返回值
24
* IRQ_EINT1:中斷號(hào),定義在"include/mach/irqs.h"中
25
* irq_handler:中斷處理函數(shù)
26
* IRQ_TIRGGER_FALLING:中斷類型標(biāo)記,下降沿觸發(fā)中斷
27
* ker_INT_EINT1:中斷的名字,顯示在/proc/interrupts等文件中
28
*NULL;現(xiàn)在我不使用dev_id,所以這里不傳參數(shù)
29
*/
30
ret = request_irq(IRQ_EINT1, irq_handler, IRQF_TRIGGER_FALLING,
31
"key INT_EINT1", NULL);
32
if(ret){
33
P_DEBUG("request irq failed!\n");
34
return -1;
35
}
36
printk("hello irq\n");
37
return 0;
38
}
39
40
static void __exit test_exit(void) //模塊卸載函數(shù)
41
{
42
free_irq(IRQ_EINT1, NULL);
43
printk("good bye irq\n");
44
}
45
46
module_init(test_init);
47
module_exit(test_exit);
48
49
MODULE_LICENSE("GPL");
50
MODULE_AUTHOR("xoao bai");
51
MODULE_VERSION("v0.1");
接下來驗(yàn)證一下:
[root:
1st]# insmod test.ko
hello
irq
[root:
1st]# key down //按下按鍵顯示
key
down
key
down
key
down
[root:
1st]# cat /proc/interrupts
CPU0
17:
11 s3c-ext0 key INT_EINT1顯示我注冊(cè)和中斷名字
30:
423482 s3c S3C2410 Timer Tick
32:
0 s3c s3c2410-lcd
51:
2782 s3c-ext eth0
70:
49 s3c-uart0 s3c2440-uart
71:
69 s3c-uart0 s3c2440-uart
79:
0 s3c-adc s3c2410_action
80:
0 s3c-adc adc, s3c2410_action
83:
0 - s3c2410-wdt
Err:
0
[root:
key INT_EINT1]# rmmod test //卸載
good
bye irq
[root:
key INT_EINT1]# cat /proc/interrupts //卸載后,我的中斷名字消失了
CPU0
30:
828977 s3c S3C2410 Timer Tick
32:
0 s3c s3c2410-lcd
51:
3202 s3c-ext eth0
70:
192 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
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、proc/interrupt
接下來,稍稍介紹一下proc/interrupt
[root:
1st]# cat /proc/interrupts
CPU0
17:
11 s3c-ext0 key INT_EINT1顯示我注冊(cè)和中斷名字
首先,第一列是中斷號(hào),之前的程序應(yīng)該有人會(huì)有疑問:
中斷號(hào)在哪里找的?
在S3C2440中,這些中斷號(hào)定義在文件"include/mach/irqs.h"中,在這里,可以找到對(duì)應(yīng)的中斷:
25
/* main cpu interrupts */
26
#define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */
27
#define IRQ_EINT1 S3C2410_IRQ(1)
28
#define IRQ_EINT2 S3C2410_IRQ(2)
29
#define IRQ_EINT3 S3C2410_IRQ(3)
30
#define IRQ_EINT4t7 S3C2410_IRQ(4) /* 20 */
在這里我標(biāo)了兩處紅筆:
第一處:可以看到,S3C2440所有的中斷號(hào)在原來的基值上加了16構(gòu)成中斷號(hào),但不同的芯片或許有不同的定義方法。
第二處:有些中斷號(hào)是共享的。在S3C2440中,EINT4---EINT7是共享寄存器SRCPND中的一位,所以,linux系統(tǒng)給這樣的中斷分配了一個(gè)共享的中斷號(hào)。那就是說,如果你使用IRQ_EINT4t7,當(dāng)收到這些中斷時(shí),都會(huì)調(diào)用對(duì)應(yīng)的中斷處理函數(shù)。這就需要在中斷處理函數(shù)中通過第一個(gè)傳參irq來辨別中斷并執(zhí)行相應(yīng)的操作。如:
13
irqreturn_t irq_handler(int irqno, void *dev_id) //中斷處理函數(shù)
14
{
15switch(irqno){
16。。。。}
17
}
那肯定有人會(huì)說,這太麻煩了吧,有沒有更好的辦法處理共享中斷號(hào)?
那當(dāng)然是有,繼續(xù)看文件"include/mach/irqs.h":
61
/* interrupts generated from the external interrupts sources */
62
#define IRQ_EINT4 S3C2410_IRQ(32) /* 48 */
63
#define IRQ_EINT5 S3C2410_IRQ(33)
64
#define IRQ_EINT6 S3C2410_IRQ(34)
65
#define IRQ_EINT7 S3C2410_IRQ(35)
66
#define IRQ_EINT8 S3C2410_IRQ(36)
看到了吧?內(nèi)核把共享的中斷分離出來,只要使用這些標(biāo)記就可以了。
其實(shí)上面我只是想說明:無論在硬件上ARM是怎么實(shí)現(xiàn)中斷的(是否共享),在內(nèi)核看來所有的中斷都是一樣的,都可以獨(dú)自獲得一個(gè)中斷號(hào)。
第二列“11”是對(duì)應(yīng)處理器響應(yīng)該中斷的次數(shù)。
第三列“s3c-ext0”是處理這個(gè)中斷的中斷控制器
第四列一看就知道調(diào)用irq_request()時(shí)定義的中斷名字。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
六、總結(jié)
其實(shí)要實(shí)現(xiàn)中斷,大部分的工作已經(jīng)給內(nèi)核包了,我們只需要做的就是告訴內(nèi)核,當(dāng)來了什么中斷要執(zhí)行怎么樣的函數(shù),這也是今天介紹的重點(diǎn),其實(shí)步驟很簡單:
1)調(diào)用兩個(gè)函數(shù):requesr_irq和free_irq。
2)實(shí)現(xiàn)中斷處理函數(shù):irq_handler()。
還有沒講的知識(shí):
1)還有幾個(gè)irqflag沒介紹。
2)沒有介紹dev_id。
可能有人會(huì)加載上面的模塊失敗,這也是我今天沒介紹的只是,共享中斷號(hào)。這里說的共享和硬件的共享不一樣性質(zhì),下節(jié)會(huì)介紹。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
總結(jié)
以上是生活随笔為你收集整理的linux块设备驱动中断程序,linux设备驱动归纳总结(六):1.中断的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 冬瓜多少钱啊?
- 下一篇: linux 线程带参数,Linux中多线