1. 程式人生 > >LINUX系統中斷處理結構及中斷函式的實現

LINUX系統中斷處理結構及中斷函式的實現

中斷系統流程解析: asm_do_IRQ(unsigned int irq, struct pt_regs *regs)      handle_IRQ(irq, regs); generic_handle_irq(irq);/*Garmen:進行一般的中斷處理*/       struct irq_desc *desc = irq_to_desc(irq);  /*#define irq_to_desc(irq)    (&irq_desc[irq])      Garmen:他是以irq為下標的一個全域性陣列項*/               generic_handle_irq_desc(irq, desc);                                      desc->handle_irq(irq, desc);  
/*Garmen : 那麼究竟是誰呼叫handle_irq ???  下面進行分析*/
---> 問:所以是誰呼叫handle_irq ?        答:進行搜尋handle_irq然後進入kernel\irq\Chip.c                __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,                         struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);/*Garmen : 以irq為索引,找到一項*/
                        desc->handle_irq = handle;          /*Garmen : 這裡進行設定,把上面以irq為索引找到的那項的handle_irq指定為傳進來的引數handle*/                然後我們再搜尋__irq_set_handler被誰呼叫: irq_set_handler(unsigned int irq, irq_flow_handler_t handle)     __irq_set_handler(irq, handle, 0, NULL);                而且我們在linux-3.4.2\arch\arm\plat-s3c24xx\Irq.c 發現:                                    void __init s3c24xx_init_irq(void)     /*Garmen: 其實下面這些就相當於初始化函式*/
                                        for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++)                                          irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,handle_edge_irq);     /*Garmen : 再跟進去*/                                                   /*Garmen:irqno對應的就是上面irq_desc的irq索引                                                     *然後你會驚人的發現這個handle_edge_irq 就是handle就是上面desc->handle_irq = handle 中斷處理函式                                                     */                                                  irq_set_chip_and_handler_name(irq, chip,handle, NULL);                                                                  irq_set_chip(irq, chip);                                                                       desc->irq_data.chip = chip;   /*Garmen:chip函式也是一樣,有跟進來的irq索引和chip*/                                                           __irq_set_handler(irq, handle, 0, name);     /*Garmen : 於是我們驚人的發現這個函式被呼叫了*/ 再研究一下handle_edge_irq handle_edge_irq:      /* Start handling the irq */      desc->irq_data.chip->irq_ack(&desc->irq_data);      handle_irq_event(desc);               struct irqaction *action = desc->action;      /*Garmen : 取出desc中action成員*/               ret = handle_irq_event_percpu(desc, action);          /*Garemn : 執行action相關函式*/ 小結:問:按下按鍵之後怎麼處理          答:1、進入異常模式:                          b vector_irq + 偏移值                2、呼叫列表,比如呼叫到irq_usr,保持現場什麼的工作                3、呼叫asm_do_irq                 4、呼叫irq_desc[irq]->handle_irq /*Garmen: 以中斷號為下標,取出一項處理函式*/                5、上面的handle_irq 就是一箇中斷處理函式 , 好比如handle_edge_irq                     裡面做了什麼事情呢?參考上面handle_edge_irq的研究 irq_desc結構體定義及其說明:他是一個全域性陣列 ①:chip是一些晶片底層硬體相關的操作(比如響應中斷、清中斷等等),在linux3.4.2核心。他在irq_data這個數組裡面,這裡沒有列出來 ②:handle_irq是中斷處理函式,將aciton連結串列中的成員一一取出來,然後執行action->handler /**  * struct irq_desc - interrupt descriptor  * @irq_data:        per irq and chip data passed down to chip functions  * @timer_rand_state:    pointer to timer rand state struct  * @kstat_irqs:        irq stats per cpu  * @handle_irq:        highlevel irq-events handler  * @preflow_handler:    handler called before the flow handler (currently used by sparc)  * @action:        the irq action chain  * @status:        status information  * @core_internal_state__do_not_mess_with_it: core internal status information  * @depth:        disable-depth, for nested irq_disable() calls  * @wake_depth:        enable depth, for multiple irq_set_irq_wake() callers  * @irq_count:        stats field to detect stalled irqs  * @last_unhandled:    aging timer for unhandled count  * @irqs_unhandled:    stats field for spurious unhandled interrupts  * @lock:        locking for SMP  * @affinity_hint:    hint to user space for preferred irq affinity  * @affinity_notify:    context for notification of affinity changes  * @pending_mask:    pending rebalanced interrupts  * @threads_oneshot:    bitfield to handle shared oneshot threads  * @threads_active:    number of irqaction threads currently running  * @wait_for_threads:    wait queue for sync_irq to wait for threaded handlers  * @dir:        /proc/irq/ procfs entry  * @name:        flow handler name for /proc/interrupts output  */ struct irq_desc {     struct irq_data        irq_data;     struct timer_rand_state *timer_rand_state;     unsigned int __percpu    *kstat_irqs;     irq_flow_handler_t    handle_irq; #ifdef CONFIG_IRQ_PREFLOW_FASTEOI     irq_preflow_handler_t    preflow_handler; #endif     struct irqaction    *action;    /* IRQ action list */     unsigned int        status_use_accessors;     unsigned int        core_internal_state__do_not_mess_with_it;     unsigned int        depth;        /* nested irq disables */     unsigned int        wake_depth;    /* nested wake enables */     unsigned int        irq_count;    /* For detecting broken IRQs */     unsigned long        last_unhandled;    /* Aging timer for unhandled count */     unsigned int        irqs_unhandled;     raw_spinlock_t        lock;     struct cpumask        *percpu_enabled; #ifdef CONFIG_SMP     const struct cpumask    *affinity_hint;     struct irq_affinity_notify *affinity_notify; #ifdef CONFIG_GENERIC_PENDING_IRQ     cpumask_var_t        pending_mask; #endif #endif     unsigned long        threads_oneshot;     atomic_t        threads_active;     wait_queue_head_t       wait_for_threads; #ifdef CONFIG_PROC_FS     struct proc_dir_entry    *dir; #endif     struct module        *owner;     const char        *name; } ____cacheline_internodealigned_in_smp; struct irqaction {     irq_handler_t        handler;     unsigned long        flags;     void            *dev_id;     void __percpu        *percpu_dev_id;     struct irqaction    *next;     int            irq;     irq_handler_t        thread_fn;     struct task_struct    *thread;     unsigned long        thread_flags;     unsigned long        thread_mask;     const char        *name;     struct proc_dir_entry    *dir; } ____cacheline_internodealigned_in_smp;
分析request_irq函式 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)     return request_threaded_irq(irq, handler, NULL, flags, name, dev);           int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)                struct irqaction *action;                struct irq_desc *desc; action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);    /*Garmen : 分配設定了action結構*/ if (!action)         return -ENOMEM; action->handler = handler; action->thread_fn = thread_fn; action->flags = irqflags; action->name = devname; action->dev_id = dev_id;        desc = irq_to_desc(irq);     /*Garmen : 以中斷號為下標找到了這個desc全域性陣列項,將這個desc傳遞給下面的setup_irq */                    retval = __setup_irq(irq, desc, action);     /*Garmen : 然後呼叫__setup_irq 設定中斷*/                         struct irqaction *old, **old_ptr; /*定義old指標做判斷*/                         old_ptr = &desc->action;                         old = *old_ptr                         /*Garmen : 你知道什麼是共享中斷嗎?*/                         ...                         /*Garmen : 對action做一系列判斷,比如是否是共享中斷啊等等,假如不是,就將我們傳進來的action加入aciton連結串列(具體看上面的圖)                          *然後desc->chip->settype       將引腳配置中斷引腳,還有那些flag什麼進行定義                          *然後desc->->startup/enable    使能引腳                          */                         ... 插一小部分相關操作 開啟裝置: exec 5</dev/buttons     /*Garmen : 開啟這個裝置,把它定位到5去*/ 檢視裝置: ps知道當前裝置 -sh是771 然後 ls -l /proc/771/fd 關閉裝置: exec 5<&- 刪除程序:kill -0 PID號 退出程序:kill -9 PID號  1、中斷申請函式:      request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);      函式原型:      int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)      第一個引數:unsigned int irq 可例 : IRQ_EINT0                         在Irq.h進行巨集定義(include\asm-arm)                         在Irq.c 的s3c24xx_init_irq函式中有進行初始化;                         中斷號是去Irq.h 將該巨集展開得到的一個數字      第二個引數:irq_handler_t handler 可例 : buttons_irq 是一個處理函式      第三個引數:unsigned long irqflags 可例:IRQT_BOTHEDGE 雙邊緣觸發                         在Irq.h進行巨集定義                         在Irq.c 的s3c_irqext_type中進行初始化      第四個引數:隨意      第五個引數:void *dev_id                          它是void型的指標。所以不必顧慮太多;                         它只不過是用來在free_irq解除安裝時候,與fre_irq的引數結合在一起,確定解除安裝哪一個irq_action 結構 小結:當發生IRQ_EINT0這個中斷的時候,就去呼叫這個buttons_irq中斷處理函式,           這個函式static irqreturn_t buttons_irq(int irq, void *dev_id),有兩個引數           第一個引數是中斷號等於IRQ_EINT0 ,第二個引數是dev_id 等於pins_desc[0] 敲黑板:      後面引入這樣的一個方法:將irq、*name、pin、key_val寫成一個結構體dev_id      例如 定義一個pin_desc結構體原型: struct pin_desc{     int irq;     char *name;     unsigned int pin;     unsigned int key_val; }; 編寫我們自己想要的結構體: struct pin_desc pins_desc[4] = {     {IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},     {IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},     {IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},     {IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT}, }; 我們的註冊函式就可以這麼寫了:     for (i = 0; i < 4; i++)     {         request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);     }      裡面所做的事情:      ①:分配一個irqaction      ②:把這個結構體放入irq_des[irq] <==action連結串列      ③:設定引腳      ④:中斷使能 2、中斷清除函式      free_irq(irq, dev_id)      函式原型:void free_irq(unsigned int irq, void *dev_id)      裡面所做的事情      ①:出鏈      ②:禁止中斷 3、系統函式:s3c2410_gpio_getpin      如何使用:           直接拿GPIO口來判斷就行!           if (s3c2410_gpio_getpin (s3c2410_GPG10) == 0)      對暫存器的讀取和判斷 4、休眠函式! 休眠函式:      函式原型: #define wait_event_interruptible(wq, condition)                \ ({                                    \     int __ret = 0;                            \     if (!(condition))                        \         __wait_event_interruptible(wq, condition, __ret);    \     __ret;                                \ })      裡面所做的事情:           先判斷condition,假如condition為0的話,就呼叫__wait_event_interruptible(wq, condition, __ret);  讓應用程式進行休眠;           假如!condition,不等於0的話,函式就跳過wait_event_interruptible,向下執行__wait_event_interruptible了!!!           所以我們運用該休眠函式的時候,要在wait_event_interruptible 後面,將condition標誌位置1;      例函:        /* 中斷事件標誌, 中斷服務程式將它置1,forth_drv_read將它清0 */      wait_event_interruptible(button_waitq, ev_press);      還要定義static DECLARE_WAIT_QUEUE_HEAD(button_waitq);                意思是:定義一個等待佇列頭結構體; 喚醒函式:     ev_press = 1;               Garmen: 表示中斷髮生了      wake_up_interruptible(&button_waitq);   Garmen::喚醒休眠的程序  參考驅動函式: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static struct class *thirddrv_class; static struct class_device    *thirddrv_class_dev; volatile unsigned long *gpfcon; volatile unsigned long *gpfdat; volatile unsigned long *gpgcon; volatile unsigned long *gpgdat; /*Garmen : 中斷佇列頭*/ static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中斷事件標誌, 中斷服務程式將它置1,third_drv_read將它清0 */ static volatile int ev_press = 0; struct pin_desc{     unsigned int pin;     unsigned int key_val; }; /* 鍵值: 按下時, 0x01, 0x02, 0x03, 0x04 */ /* 鍵值: 鬆開時, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; struct pin_desc pins_desc[4] = {     {S3C2410_GPF0, 0x01},     {S3C2410_GPF2, 0x02},     {S3C2410_GPG3, 0x03},     {S3C2410_GPG11, 0x04}, }; /*   * 確定按鍵值   */ static irqreturn_t buttons_irq(int irq, void *dev_id) {     struct pin_desc * pindesc = (struct pin_desc *)dev_id;     unsigned int pinval;     pinval = s3c2410_gpio_getpin(pindesc->pin);     if (pinval)     {         /* 鬆開 */         key_val = 0x80 | pindesc->key_val;     }     else     {         /* 按下 */         key_val = pindesc->key_val;     }   ev_press = 1;                  /* 表示中斷髮生了 */     wake_up_interruptible(&button_waitq);   /* 喚醒休眠的程序 */     return IRQ_RETVAL(IRQ_HANDLED); } static int third_drv_open(struct inode *inode, struct file *file) {     /* 配置GPF0,2為輸入引腳 */     /* 配置GPG3,11為輸入引腳 */     request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);     request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);     request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);     request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);        return 0; } ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) {     if (size != 1)         return -EINVAL; /* 如果沒有按鍵動作, 休眠 */     wait_event_interruptible(button_waitq, ev_press); /* 如果有按鍵動作, 返回鍵值 */     copy_to_user(buf, &key_val, 1);   /*Garmen : 同時將休眠標誌位至0 這樣又可以繼續休眠*/     ev_press = 0;     return 1; } int third_drv_close(struct inode *inode, struct file *file) {     free_irq(IRQ_EINT0, &pins_desc[0]);     free_irq(IRQ_EINT2, &pins_desc[1]);     free_irq(IRQ_EINT11, &pins_desc[2]);     free_irq(IRQ_EINT19, &pins_desc[3]);     return 0; } static struct file_operations sencod_drv_fops = {     .owner    =  THIS_MODULE,    /* 這是一個巨集,推向編譯模組時自動建立的__this_module變數 */     .open     =  third_drv_open,          .read     =    third_drv_read,            .release  =  third_drv_close,        }; int major; static int third_drv_init(void) {     major = register_chrdev(0, "third_drv", &sencod_drv_fops);     thirddrv_class = class_create(THIS_MODULE, "third_drv");     thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */     gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);     gpfdat = gpfcon + 1;     gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);     gpgdat = gpgcon + 1;     return 0; } static void third_drv_exit(void) {     unregister_chrdev(major, "third_drv");     class_device_unregister(thirddrv_class_dev);     class_destroy(thirddrv_class);     iounmap(gpfcon);     iounmap(gpgcon);     return 0; } module_init(third_drv_init); module_exit(third_drv_exit); MODULE_LICENSE("GPL"); 在測試程式中呼叫: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* thirddrvtest   */ int main(int argc, char **argv) {     int fd;     unsigned char key_val;     fd = open("/dev/buttons", O_RDWR);     if (fd < 0)     {         printf("can't open!\n");     }     while (1)     {         read(fd, &key_val, 1);         printf("key_val = 0x%x\n", key_val);     }     return 0; }