1. 程式人生 > >request_irq() | 註冊中斷服務

request_irq() | 註冊中斷服務

when reason blank ini per ces inode his lis

一、中斷註冊方法

在linux內核中用於申請中斷的函數是request_irq(),函數原型在Kernel/irq/manage.c中定義:

int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)

irq是要申請的硬件中斷號

handler是向系統註冊的中斷處理函數,是一個回調函數,中斷發生時,系統調用這個函數,dev_id參數將被傳遞給它。

irqflags是中斷處理的屬性

,若設置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已經不支持了),則表示中斷處理程序是快速處理程序,快速處理程序被調用時屏蔽所有中斷,慢速處理程序不屏蔽;若設置了IRQF_SHARED (老版本中的SA_SHIRQ),則表示多個設備共享中斷,若設置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示對系統熵有貢獻,對系統獲取隨機數有好處。(這幾個flag是可以通過或的方式同時使用的)

devname設置中斷名稱,通常是設備驅動程序的名稱 在cat /proc/interrupts中可以看到此名稱

dev_id在中斷共享時會用到

,一般設置為這個設備的設備結構體或者NULL。

request_irq()返回0表示成功,返回-INVAL表示中斷號無效或處理函數指針為NULL,返回-EBUSY表示中斷已經被占用且不能共享。

==================================================================================================================================

request_irq() | 註冊中斷服務

在 2.4 內核和 2.6內核中都使用 request_irq() 函數來註冊中斷服務函數。在 2.4 內核中,需要包含的頭文件是 #include <linux/sched.h> ,2.6 內核中需要包含的頭文件則是
#include <linux/interrupt.h> 。函數原型如下:

  • 2.4 內核
int request_irq (unsignedintirq,void (*handler)(int,void*,structpt_regs*),unsignedlongfrags,constchar*device,void*dev_id);

  • 2.6 內核
request_irq(unsignedintirq,irq_handler_thandler,unsignedlongflags,constchar*name,void*dev);

參數說明

在發生對應於第 1個參數 irq 的中斷時,則調用第 2 個參數handler 為要註冊的中斷服務函數(也就是把 handler() 中斷服務函數註冊到內核中 )。

第 3 個參數 flags 指定了快速中斷或中斷共享等中斷處理屬性。在 2.6 教新的內核裏(我的是 2.6.27 ~ 2.6.31 ),在 linux/interrupt.h 中定義操作這個參數的宏如下:
引用 /*
* These correspond to the IORESOURCE_IRQ_* defines in
* linux/ioport.h to select the interrupt line behaviour. When
* requesting an interrupt without specifying a IRQF_TRIGGER, the
* setting should be assumed to be "as already configured", which
* may be as per machine or firmware initialisation.

#define IRQF_TRIGGER_NONE0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
指定中斷觸發類型:高電平有效。新增加的標誌
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
/*
* These flags used only by the kernel as part of the irq handling routines.
* registered first in an shared interrupt is considered for
* performance reasons)
*/
#define IRQF_DISABLED 0x00000020 * IRQF_DISABLED - keep irqs disabled when calling the action handler
#define IRQF_SAMPLE_RANDOM 0x00000040* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
#define IRQF_SHARED 0x00000080* IRQF_SHARED - allow sharing the irq among several devices #define IRQF_PROBE_SHARED 0x00000100* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
#define IRQF_TIMER 0x00000200* IRQF_TIMER - Flag to mark this interrupt as timer interrupt #define IRQF_PERCPU 0x00000400* IRQF_PERCPU - Interrupt is per cpu
#define IRQF_NOBALANCING 0x00000800* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
#define IRQF_IRQPOLL 0x00001000* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
早期一點的 2.6 內核這裏一般以 SA_ 前綴開頭,如:
SA_INTERRUPT 表示禁止其他中斷;(對應於 IRQF_DISABLED )
SA_SHIRQ 表示共享相同的中斷號 (對應於 IRQF_SHARED )
SA_SAMPLE_RANDOM 此宏會影響到 RANDOM 的處理( 對應於 IRQF_SAMPLE_RANDOM )。

第 4 個參數 name,通常是設備驅動程序的名稱。改值用在 /proc/interrupt 系統 (虛擬) 文件上,或內核發生中斷錯誤時使用。

第 5 個參數 dev_id 中斷名稱 可作為共享中斷時的中斷區別參數,也可以用來指定中斷服務函數需要參考的數據地址。建議將設備結構指針作為dev_id參數

int request_irq(unsigned int irq, irq_handler_t handler,
IRQF_SHARED, const char *devname, void *dev_id)

很多權威資料中都提到,中斷共享註冊時的註冊函數中的dev_id參數是必不可少的,並且dev_id的值必須唯一。那麽這裏提供唯一的dev_id值的究竟是做什麽用的?

根據我們前面中斷模型的知識,可以看出發生中斷時,內核並不判斷究竟是共享中斷線上的哪個設備產生了中斷,它會循環執行所有該中斷線上註冊的中斷處理函數(即irqaction->handler函數)。因此irqaction->handler函數有責任識別出是否是自己的硬件設備產生了中斷,然後再執行該中斷處理函數。通常是通過讀取該硬件設備提供的中斷flag標誌位進行判斷。那既然kernel循環執行該中斷線上註冊的所有irqaction->handler函數,把識別究竟是哪個硬件設備產生了中斷這件事交給中斷處理函數本身去做,那request_irq的dev_id參數究竟是做什麽用的?

很多資料中都建議將設備結構指針作為dev_id參數。在中斷到來時,迅速地根據硬件寄存器中的信息比照傳入的dev_id參數判斷是否是本設備的中斷,若不是,應迅速返回。這樣的說法沒有問題,也是我們編程時都遵循的方法。但事實上並不能夠說明為什麽中斷共享必須要設置dev_id。

下面解釋一下dev_id參數為什麽必須的,而且是必須唯一的。

當調用free_irq註銷中斷處理函數時(通常卸載驅動時其中斷處理函數也會被註銷掉),因為dev_id是唯一的,所以可以通過它來判斷從共享中斷線上的多個中斷處理程序中刪除指定的一個。如果沒有這個參數,那麽kernel不可能知道給定的中斷線上到底要刪除哪一個處理程序。

註銷函數定義在Kernel/irq/manage.c中定義:
void free_irq(unsigned int irq, void *dev_id)


返回值
函數運行正常時返回 0 ,否則返回對應錯誤的負值。

示例代碼片段
引用

irqreturn_t xxx_interrupt (intirq,void*dev_id)
{
...

return (IRQ_HANDLED);
}

int xxx_open (struct inode *inode,structfile*filp)
{
if (!request_irq (XXX_IRQ,xxx_interruppt,IRQF_DISABLED,"xxx",NULL)){

/*正常註冊*/
}

return (0);
}

request_irq() | 註冊中斷服務