天天看點

linux驅動中斷号配置設定,linux裝置驅動歸納總結(六):2.分享中斷号

linux裝置驅動歸納總結(六):2.分享中斷号

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:

這個就是兩男争一妞的情況了,解決辦法有兩個:

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:

53

#define IRQF_DISABLED 0x00000020 //SA_INTERRUPT

54

#define IRQF_SAMPLE_RANDOM 0x00000040 //SA_SAMPLE_RANDOM

55

#define IRQF_SHARED 0x00000080 //SA_SHIRQ

看修改後的代碼:

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

}

另外一個一模一樣

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:

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

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。

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 withr0 = irq number, r1 = struct pt_regs *

35

@//獲得中斷号和一個結構體,作為參數傳給asm_do_IRQ

36

adrne lr, 1b

37

bneasm_do_IRQ

2.2、asm_do_IRQ進行一系列的準備工作之後,調用函數generic_handle_irq():

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

122

if (irq >= NR_IRQS)

123

handle_bad_irq(irq, &bad_irq_desc);

124

else

125generic_handle_irq(irq);

//調用該函數,開始進行中斷。

126

127

128

irq_finish(irq);

129

130

irq_exit();

131

set_irq_regs(old_regs);

132

}

2.3、generic_handle_irq中調用函數_do_IRQ:

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)幹了些什麼:

上面說的内容可能都不太詳細,因為我也不太明白有些函數的作用和具體的核心代碼,但下面的函數大家應該都會看得懂:

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);

344local_irq_disable();

//再次關上中斷

345

346

return retval;

347

}

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

五、總結

總結一下中斷注冊的幾個注意事項:

1、調用request_irq必須通過傳回值判斷是否成功。

2、共享中斷号時,所有共享這個中斷号的request_irq都必須添加标記IRQF_SHARED,另外還需要使用一個獨特的裝置好dev_id,讓核心能夠通過dev_id對應注冊時的中斷處理函數。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx