- /* *****************************************************************************
- 1.申請和釋放中斷
- ***************************************************************************** */
- //*----------------------------------------------------------------------------
- //* \fn request_irq()
- //* \brief 該函數用來申請一個IRQ,其中irq是要申請的硬件中斷號,hander是向系統登記的中斷處理函數,
- //*中斷髮生時系統調用這個函數,dev_id參數將被傳遞給它。irqflags是中斷處理的屬性,若設置爲
- //* SA_INTERRUPT,則表示中斷處理程序爲快速處理程序,快速處理程序在調用時屏蔽所有中斷,慢速處理程
- //*序不屏蔽;若設置爲SA_SHIRQ,則表示多個設備共享中斷,dev_id在中斷共享時會用到,一般設置爲這個設
- //*備的設備結構體或者NULL.
- //* \return value:
- //* 0 : 表示成功
- //* -INVAL: 表示中斷號無效或者是處理函數指針爲NULL
- //* -EBUSY : 表示中斷已經被佔用且不能共享
- //*----------------------------------------------------------------------------
- int request_irq(unsigned int irq,void (*handler)(int irq,void *dev_id,struct pt_regs *regs),
- unsigned long irqflags,const char *devname,void *dev_id);
- //*----------------------------------------------------------------------------
- //* \fn free_irq()
- //* \brief 該函數用來釋放一個IRQ,參數與request_irq()相同
- //*----------------------------------------------------------------------------
- void free_irq(unsigned int irq,void *dev_id);
- /* *****************************************************************************
- 2.使能和屏蔽中斷
- ***************************************************************************** */
- //*----------------------------------------------------------------------------
- //* \fn disable_irq(),disable_irq_nosync(),enable_irq()
- //* \brief 這三個函數用於屏蔽一箇中斷源,其中disable_irq_nosync()立即返回;
- //* disable_irq()等待目前的中斷處理完成後返回.說明:這三個函數作用於可編程控制器,因此
- //*對系統內的所有CPU都生效。
- //*----------------------------------------------------------------------------
- void disable_irq(int irq);
- void disable_irq_nosync(int irq);
- void enable_irq(int irq);
- //*----------------------------------------------------------------------------
- //* \fn local_irq_save(),local_irq_disable()
- //* \brief 這2個函數將屏蔽cpu內的所有中斷,其中local_irq_save()將目前的中斷狀態保存在
- //* flags中.說明:flags被直接傳遞而不是通過指針傳遞;local_irq_disable()直接禁止中斷。
- //*----------------------------------------------------------------------------
- void local_irq_save(unsigned long flags);
- void local_irq_disable(void);
- //*----------------------------------------------------------------------------
- //* \fn local_irq_restore(),local_irq_restore()
- //* \brief 這2個函數將上述倆函數(local_irq_save(),local_irq_disable())禁止的中斷恢復
- //*----------------------------------------------------------------------------
- void local_irq_restore(unsigned long flags);
- void local_irq_restore(void);
- /* *****************************************************************************
- 3.底半部機制
- Linux系統實現底半部機制主要有tasklet、工作隊列和軟中斷。
- 3.1 tasklet
- tasklet的使用比較簡單,我們只需要定義tasklet及其處理函數並將二者關聯,例如
- void my_tasklet_func(unsigned long);//定義一個處理函數
- DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);//定義一個tasklet結構my_tasklet,與
- my_tasklet_func(data)函數相關聯。
- 在需要調度tasklet的時候引用一個task_schedule()函數就能使系統在適當的時候進行調度運行,如下所示:
- task_schedule(&my_tasklet);
- 使用tasklet作爲底半部處理中斷的設備驅動程序模板如下:
- ***************************************************************************** */
- /*定義tasklet和底半部函數並關聯*/
- void xxx_do_tasklet(unsigned long);
- DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);
- /*中斷處理底半部*/
- void xxx_do_tasklet(unsigned long)
- {
- ...
- }
- /*中斷處理頂半部*/
- irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
- {
- ...
- tasklet_schedule(&xxx_tasklet);
- ...
- }
- /*設備驅動加載模塊*/
- void _init xxx_init(void)
- {
- ...
- //申請中斷
- result = request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,"XXX",NULL);
- ...
- }
- /*設備驅動卸載模塊*/
- void _exit xxx_exit(void)
- {
- ...
- //釋放中斷
- free_irq(xxx_irq,NULL);
- ...
- }
- /* *****************************************************************************
- 3.2 工作隊列
- 工作隊列與tasklet的使用非常類似,我們只需要定義工作隊列及其處理函數並將二者關聯,例如
- struct work_struct my_wq;//定義一個工作隊列
- void my_wq_func(unsigned long);//定義一個處理函數
- INIT_WORK(&my_wq,(void(*)(void *))my_wq_func,NULL);//初始化工作隊列並將它與處理函數綁定。
- 在需要調度tasklet的時候引用一個schedule_work()函數就能使系統在適當的時候進行調度運行,如下所示:
- schedule_work(&my_wq);
- 使用工作隊列作爲底半部處理中斷的設備驅動程序模板如下:
- ***************************************************************************** */
- /*定義工作隊列和底半部函數*/
- struct work_struct xxx_wq;
- void xxx_do_work(unsigned long);
- // DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);
- /*中斷處理底半部*/
- void xxx_do_work(unsigned long)
- {
- ...
- }
- /*中斷處理頂半部*/
- irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
- {
- ...
- schedule_work(&xxx_wq);
- ...
- }
- /*設備驅動加載模塊*/
- void _init xxx_init(void)
- {
- ...
- //申請中斷
- result = request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,"XXX",NULL);
- ...
- //初始化工作隊列
- INIT_WORK(&xxx_wq,void(*)(void *))xxx_do_work,NULL);
- ...
- }
- /*設備驅動卸載模塊*/
- void _exit xxx_exit(void)
- {
- ...
- //釋放中斷
- free_irq(xxx_irq,NULL);
- ...
- }
- /* *****************************************************************************
- 3.3 軟中斷
- 軟中斷是用軟件方式模擬硬件中斷的概念,實現宏觀上的異步執行效果,tasklet也是基於軟中斷實現的
- 基於信號的異步通知也類似於中斷。硬中斷是外部設備對CPU的中斷,軟中斷是硬中斷服務程序對內核的中斷。而信號
- 是由內核給某個進程的中斷。
- linux內核中,用softirq_action結構體表徵一個軟中斷,這個結構體中包含軟中斷處理函數指針和傳遞給該函數的
- 參數。使用open_softirq()函數可以註冊軟中斷對應的處理函數,使用raise_action()函數可以出發一個軟中斷。
- 軟中斷和tasklet仍然運行於中斷上下文,而工作隊列運行於進程上下文。因此,軟中斷和tasklet處理函數中不能睡眠,
- 而工作隊列處理函數中可以睡眠。
- local_bh_disable()和local_bh_enable()是內核中用於禁止和使能軟中斷和tasklet底半部機制的函數。
- ***************************************************************************** */