1. 中斷處理常式架構
中斷服務程式的執行不存在於進程上下文,要求中斷服務程式的時間短,核心中對時鐘的處理也採用中斷方式
中斷機制提供了硬體和軟體之間非同步傳遞資訊的方式,硬體裝置在發生某個事件時通過中斷通知軟體進行處理
中斷實現了硬體裝置按需獲得處理器關注的機制,與查詢方式相比可以大大節省CPU時間
中斷處理常式可以分成兩部分上半部是實際ISR,在中斷髮生時被調用。
下半部是tasklet或work_queue,被上半部調度,在稍後的安全時間被調用
頂半部完成比較緊急的功能,(簡單地讀取寄存器中的中斷狀態並清除中斷標誌),執行速度快。
底半部可以被中斷打斷,完成絕大多數費時的任務(底半部機制有tasklet, work_queue)
2. 申請IRQ
request_irq()函數來註冊中斷服務函數,Linux2.6核心所需包含的標頭檔是#include <linux/interrupt.h>
定義如下
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
參數:
@irq:是要申請的硬體中斷號
@handler:向系統登記的中斷處理函數(頂半部),是一個回呼函數,中斷髮生時,系統調用這個函數
dev參數將傳給它。
@flags:中斷處理屬性在<linux/interrupt.h>中有定義
#define IRQF_DISABLED 0x00000020
表明中斷處理常式是快速處理常式,被調用時屏掉所有中斷
#define IRQF_SHARED 0x00000080
表示多個裝置共用中斷,參數dev在中斷共用時會用到。
@name:裝置驅動的名稱用在/proc/interrupt系統檔案上
3. 釋放IRQ
void free_irq(unsigned int irq, void *dev_id)
4. 中斷底半部機制
Linux實現底半部的機制主要有tasklet、workqueue、soft_irq
1)tasklet
每個tasklet都和一個函數相關聯。當tasklet被運行時,該函數就被調用,並且tasklet可以調度自己
I)定義一個tasklet 結構
struct tasklet_struct keyboard_tasklet;
II)定義一個tasklet處理函數
static void keyboard_func(unsigned long);
III)tasklet結構與函數綁定
static DECLARE_TASKLET(keyboard_tasklet, keyboard_func, data);
傳入這個函數的參數為data
IV)調度tasklet
tasklet_schedule(&keyboard_tasklet);
tasklet使用模板/**定義tasklet和底半部函數並關聯*/struct tasklet_struct xxx_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){...taklet_schedule(&xxx_tasklet);...return IRQ_HANDLED;}/**裝置驅動模組載入函數*/int __init xxx_init(void){.../**申請中斷*/result = request_irq(xxx_irq, xxx_interrupt, IRQF_DISABLED, "xxx", NULL);...}/**裝置驅移除函數*/void __exit xxx_exit(void){.../**釋放中斷*/free_irq(dev->irq, dev);...}
2)工作隊列tasklet運行於中斷上下文 工作隊列運行於進程上下文 tasklet處理函數中不能睡眠,而工作隊列處理函數中允許睡眠 I)定義一個工作隊列結構struct work_struct my_wq;II)定義一個處理函數void my_wq_func(unsigned long);III)初始化這個工作隊列並將工作隊列與函數綁定INIT_WORK(&my_wq, (void(*)(void *))my_wq_func, NULL);IV)調度工作隊列執行schedule_work()schedule_work(&my_wq);-----------------------------------------------------工作隊列使用模板/**定義工作隊列和關聯函數*/struct work_struct xxx_wq;void xxx_do_work(unsigned long);/**中斷處理底半部*/void xxx_do_work(unsigned long){...}/**中斷處理頂半部*/irqreturn_t xxx_interrupt(int irq, void *dev_id){...schedule_work(&xxx_wq);...return IRQ_HANDLED;}/**裝置驅動模組載入函數*/int __init xxx_init(void){.../**申請中斷*/result = request_irq(xxx_irq, xxx_interrupt, IRQF_DISABLED, "xxx", NULL);.../**初始化工作隊列*/INIT_WORK(&xxx_wq, (void(*)(void*))xxx_do_work, NULL);...}/**裝置驅動移除函數*/void xxx_exit(void){.../**釋放中斷*/free_irq(&dev->irq, dev);...}
5. 中斷例子/***interrupt key kernel 2.6.35.7*/#include <linux/module.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/fs.h>#include <mach/gpio.h>#define KEY_MAJOR 233#define DEVICE_NAME "gpio_key1"struct work_struct work;static void gpio_key_work_func(struct work_struct *work){printk("the code is 101\n");}static irqreturn_t gpio_key_isr(int irq, void *dev_id){schedule_work(&work);return IRQ_HANDLED;}int key_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){return 0;}static const struct file_operations key_ops = {.owner = THIS_MODULE,.ioctl = key_ioctl,};static int __init gpio_keys_init(void){int ret = 0;int irq;//中斷號unsigned long irqflags;ret = register_chrdev(KEY_MAJOR, DEVICE_NAME, &key_ops);if (ret < 0) {printk("can't register gpio_keys_number\n");return -1;}//申請管腳gpio_request(S3C64XX_GPN(0), "HOME");//設定為輸入gpio_direction_input(S3C64XX_GPN(0));irq = gpio_to_irq(S3C64XX_GPN(0));printk("the gpio_key irq is [%d]\n ", irq);irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;ret = request_irq(irq, gpio_key_isr, irqflags, "HOME", NULL);if (ret) {printk("can't get gpio irq\n");return -1;}INIT_WORK(&work, gpio_key_work_func);printk("gpio_keys init\n");return 0;}static void __exit gpio_keys_exit(void){unregister_chrdev(KEY_MAJOR, DEVICE_NAME);}module_init(gpio_keys_init);module_exit(gpio_keys_exit);MODULE_LICENSE("GPL");-------------------------------測試,按鍵# the code is 101# cat /proc/interrupts 101: 6 s3c-eint HOME中斷被用了6次