閱讀847 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Linux中斷內核編程

轉自:https://blog.csdn.net/tigerjb/article/details/6069516

在前麵分析了中斷的基本原理後,就可以寫一個內核中斷程序來體驗以下,也可以借此程序繼續深入來了解內核中斷的執行過程

一.內核中斷程序 :

我們還是來看一看成程序:

在看程序之前,要熟悉如何進行模塊編程,和了解module_pararm()的用法。如果不熟悉的話請大家看,module_param()的學習 和Linux內核模塊編程,在此不作解釋。

1.程序interrupt.c

[c-sharp] view plaincopy
  1. /* 
  2.  2 *file name :interrupt.c 
  3.  3 *atuthor   : john  
  4.  4 */  
  5.  5 #include<linux/init.h>  
  6.  6 #include<linux/module.h>  
  7.  7 #include<linux/kernel.h>  
  8.  8 #include<linux/interrupt.h>  
  9.  9   
  10. 10 MODULE_LICENSE("GPL");  
  11. 11 static int irq;  
  12. 12 char *interface;  
  13. 13 static irqreturn_t myirq_handler(int irq,void *dev);  
  14. 14   
  15. 15 static int __init myirq_init(void)  
  16. 16 {  
  17. 17         printk("the module is working!/n");  
  18. 18         printk("the irq is ready for working!/n");  
  19. 19         if(request_irq(irq,myirq_handler,IRQF_SHARED,interface,&irq)){  
  20. 20         printk(KERN_ERR "%s interrrupt can't register %d IRQ /n",interface,irq);  
  21. 21         return -EIO;  
  22. 22         }  
  23. 23         printk("%s request %d IRQ/n",interface,irq);  
  24. 24         return 0;  
  25. 25 }  
  26. 26 static irqreturn_t myirq_handler(int irq,void *dev)  
  27. 27 {  
  28. 28         printk("%d IRQ is working/n",irq);  
  29. 29         return IRQ_NONE;  
  30. 30 }  
  31. 31 static void  __exit myirq_exit(void)  
  32. 32 {  
  33. 33         printk("the module is leaving!/n");  
  34. 34         printk("the irq is bye bye!/n");  
  35. 35         free_irq(irq,&irq);  
  36. 36         printk("%s interrupt free %d IRQ/n",interface,irq);  
  37. 37   
  38. 38 }  
  39. 39 module_init(myirq_init);  
  40. 0 module_exit(myirq_exit);  
  41. 41 module_param(interface,charp,0644);  
  42. 42 module_param(irq,int,0644);  
  43. 43   
 

2.Makefile的編寫

[c-sharp] view plaincopy
  1.  1 obj-m:=tiger.o  
  2.  2   
  3.  3 CURRENT_PATH:=$(shell pwd)  
  4.  4 VERSION_NUM:=$(shell uname -r)  
  5.  5 LINUX_PATH:=/usr/src/linux-headers-$(VERSION_NUM)  
  6.  6   
  7.  7   
  8.  8 all :  
  9.  9         make -C $(LINUX_PATH) M=$(CURRENT_PATH) modules  
  10. 10 clean:  
  11. 11         make -C $(LINUX_PATH) M=$(CURRENT_PATH) clean  
 

(程序的調試,加載和運行,在此不進行說明)

3.首先我們來分析下內核加載模塊

在內核加載模塊中最重要的的action就是注冊中斷處理程序。很明顯,這一動作是通過request_irq()函數來完成的。

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

A.先來分析形參:

第一個參數irq: 表示要分配的中斷號。對於一些設備(係統時鍾或鍵盤)它的值是預先固定的,而對於大多數設備來說,這個值是動態確定的。

第二個參數 handler: 表示要掛入到中斷請求對列中的中斷服務例程, 這個中斷服務函數的原型是static irqreturn_t handler(int , void *);

中斷處理程序的前綴為static,因為它從來不會被別的文件中的代碼直接調用。

第三個參數flags:為標誌位。可以取IRQF_DISABLED、IRQF_SHARED和IRQF_SAMPLE_RANDOM之一。在本實例程序中取 IRQF_SHARED,該標誌表示多個中斷處理程序共享irq中斷線。一般某個中斷線上的中斷服務程序在執行時會屏蔽請求該線的其他中斷,如果取 IRQF_DISABLED標誌,則在執行該中斷服務程序時會屏蔽所有其他的中斷。取IRQF_SAMPLE_RANDOM則表示設備可以被看做是事件隨見的發生源。

以下是官方解釋:

[c-sharp] view plaincopy
  1. /*  
  2. * These flags used only by the kernel as part of the  
  3. * irq handling routines.  
  4.  
  5. * IRQF_DISABLED - keep irqs disabled when calling the action handler  
  6. * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator  
  7. * IRQF_SHARED - allow sharing the irq among several devices  
  8. * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur  
  9. * IRQF_TIMER - Flag to mark this interrupt as timer interrupt  
  10. * IRQF_PERCPU - Interrupt is per cpu  
  11. * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing  
  12. * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is  
  13. *                registered first in an shared interrupt is considered for  
  14. *                performance reasons)  
  15. */   
  16. #define IRQF_DISABLED           0x00000020   
  17. #define IRQF_SAMPLE_RANDOM       0x00000040   
  18. #define IRQF_SHARED             0x00000080   
  19. #define IRQF_PROBE_SHARED       0x00000100   
  20. #define IRQF_TIMER               0x00000200   
  21. #define IRQF_PERCPU             0x00000400   
  22. #define IRQF_NOBALANCING         0x00000800   
  23. #define IRQF_IRQPOLL             0x00001000    

第四個參數devname:是請求中斷的設備的名稱。當你加載模塊成功後可以在/proc/interrupts中查看到具體設備的名稱,與此同時也可以看到這個設備對應的中斷號以及請求次數。

第五個參數dev_id:為一個指針型變量。注意該參數為void型,也就是說通過強製轉換可以轉換為任意類型。dev_id主要用於共享中斷線,對每個注冊的中斷處理程序來說,( Dev_id must be globally unique. Normally the address of the  device data structure is used as the cookie.)dev_id參數必須唯一(指向任一設備結構的指針就可以滿足此要求,選擇設備結構因為它是唯一的,而且中斷處理程序可能會用到它)如果無需共享中斷線,則將該參數賦值為NULL。

B:函數返回值

requset_irq()函數成功執行後返回0。如果返回非0值,就表示錯誤發生。此時,指定的中斷處理程序不會被注冊。

這裏麵有幾個疑問:

為什麼要注冊中斷函數

共享中斷線的概念,參數dev_id的作用是什麼

看一個圖進行說明 :


1>由圖可知:有16個中斷線。要使用中斷線,就要進行中斷線的 申請 ,也常把申請一條中斷線稱為申請一個中斷號,這就   與request_irq()函數中的第一個形參 irq 有關係 

2>Linux有256個中斷向量,而外部中中斷向量隻有16個(32~47)。由於硬件上的限製,很多外部設備不得不共享中斷線。

(例如:一些PC機所用的網卡和圖形卡可以把它們分配到一條中斷線上)

讓每個中斷源獨自占用一條中斷線是不實現的。

3>共享中斷線的話雖然解決了中斷資源的問題,但是,此時引出了另一個問題( 任何事物都有其兩麵性 ),此時僅僅用中斷描述符並不能提供中斷產生的所有信息。為了解決這個問題,內核必須對中斷線給出近一步的描述,所以在Linux設計中,為每個中斷請求IRQ設置了一個專用隊列(中斷請求隊列)
4>中斷服例程序和中斷處理程序的區別:
a.中斷服務例程(interrupt service routine):

Linux中,15條中斷線對應15個中斷處理程序,依次命名是IRQ0x00_interrupt(),IRQ0x01_interrupt()..... IRQ0X1f_interrupt().

中斷處理程序相當於某個中斷向量的總處理程序。 
eg:IRQ0X05_interupt()是5號中斷(向量為37)的總處理程序。

b.中斷服務例程是針對一個具體設備的中斷。
5>.注冊中斷服務例程:
在IDT表完成初始化時,每個中斷服務隊列還為空。此時即使打開中斷且某個外設的中斷真的發生了,也得不到實際的服務。因為CPU雖然通過中斷門進入了某個中斷向量的總處理程序。但是,具體的中斷服務例程還沒有掛入中斷請求隊列。所以,在設備驅動程序的初始化階段,必須通過request_irq()函數將響應的中斷服務例程掛入中斷請求隊列,也就是進行注冊。
 

6>分析一下中斷服務程序,即request_irq()函數中第二個參數所對應的函數

static irqreturn_t myirq_handler(int irq,void *dev_id)
{
 
         printk("ISR is Working/n");
         return IRQ_HANDLED;

}

中斷服務例程的形參:

a.int irq :中斷號。
b.void *dev_id :與request_irq()的參數dev_id一致,可以根據這個設備id號得到相應設備的數據結構,進而得到相應設備的信息和相關數據。
c.返回值:中斷程序的返回值是一個特殊類型 rqreturn_t。但是中斷程序的返回值卻隻有兩個值IRQ_NONE和IRQ_HANDLED。
IRQ_NONE:中斷程序接收到中斷信號後發現這並不是注冊時指定的中斷原發出的中斷信號。
IRQ_HANDLED:接收到了準確的中斷信號,並且作了相應正確的處理。

一般 中斷處理程序要做什麼service,主要取決於產生的設備和該設備為什麼要發送中斷。

John哥說明:

1.當一個給定的中斷處理程序正在執行時,這條中斷線上的其它中斷都會被屏蔽。but,所有其他中斷線上的中斷都是打開的。因此這些不同中斷線上的其他中斷都能被處理。

2.request_irq()函數可能會睡眠,所以,不能在中斷上下文或其它不允許阻塞的代碼中調用該函數。

4.在深入分析request_irq()函數之前,先來看幾個重要的數據結構。

A.irqaction的數據結構(用irqaction結構體來描述一個具體的中斷服務例程)

[c-sharp] view plaincopy
  1. 113struct irqaction {  
  2. 114        irq_handler_t handler;  
  3. 115        unsigned long flags;  
  4. 116        const char *name;  
  5. 117        void *dev_id;  
  6. 118        struct irqaction *next;  
  7. 119        int irq;  
  8. 120        struct proc_dir_entry *dir;  
  9. 121        irq_handler_t thread_fn;  
  10. 122        struct task_struct *thread;  
  11. 123        unsigned long thread_flags;  
  12. 124};  
  13. 125  

1>handler:指向具體的一個中斷服務例程。

2>flags:表示中斷標誌位,對應於request_irq()函數中所傳遞的第三個參數,可取IRQF_DISABLED、IRQF_SAMPLE_RANDOM和IRQF_SHARED其中之一。

3>name:請求中斷的設備名稱,對應request_irq()函數中所傳遞的第四個參數

4>dev_id: 共享中斷時有用。 對應於request_irq()函數中所傳遞的第五個參數,可取任意值,但必須唯一能夠代表發出中斷請求的設備,通常取描述該設備的結構體。

5>strct irqaction *next:指向irqaction描述符的下一個元素。用一條鏈表將共享同一條中斷線上的中斷服務例程鏈接起來。

6>irq:所申請的中斷號

7>dir:指向proc/irq/NN/name entry

8>thread_fn:指向具體的一個線程化的中斷。

9>thread:指向線程中斷的指針。

10>thread_flags:線程中斷的標誌。

B.irq_desc的數據結構體

每個中斷向量都有它自己的irq_desc 描述符。即用irq_desc來描述中斷向量。所有的這些中斷描述符組織在一起就形成了irq_desc irq_desc[NR_IRQS]數組

[c-sharp] view plaincopy
  1. 175struct irq_desc {  
  2. 176        unsigned int            irq;  
  3. 177        struct timer_rand_state *timer_rand_state;  
  4. 178        unsigned int            *kstat_irqs;  
  5. 179#ifdef CONFIG_INTR_REMAP  
  6. 180        struct irq_2_iommu      *irq_2_iommu;  
  7. 181#endif  
  8. 182        irq_flow_handler_t      handle_irq;  
  9. 183        struct irq_chip         *chip;  
  10. 184        struct msi_desc         *msi_desc;  
  11. 185        void                    *handler_data;  
  12. 186        void                    *chip_data;  
  13. 187        struct irqaction        *action;        /* IRQ action list */  
  14. 188        unsigned int            status;         /* IRQ status */  
  15. 189  
  16. 190        unsigned int            depth;          /* nested irq disables */  
  17. 191        unsigned int            wake_depth;     /* nested wake enables */  
  18. 192        unsigned int            irq_count;      /* For detecting broken IRQs */  
  19. 193        unsigned long           last_unhandled; /* Aging timer for unhandled count */  
  20. 194        unsigned int            irqs_unhandled;  
  21. 195        raw_spinlock_t          lock;  
  22. 196#ifdef CONFIG_SMP  
  23. 197        cpumask_var_t           affinity;  
  24. 198        const struct cpumask    *affinity_hint;  
  25. 199        unsigned int            node;  
  26. 200#ifdef CONFIG_GENERIC_PENDING_IRQ  
  27. 201        cpumask_var_t           pending_mask;  
  28. 202#endif  
  29. 203#endif  
  30. 204        atomic_t                threads_active;  
  31. 205        wait_queue_head_t       wait_for_threads;  
  32. 206#ifdef CONFIG_PROC_FS  
  33. 207        struct proc_dir_entry   *dir;  
  34. 208#endif  
  35. 209        const char              *name;  
  36. 210} ____cacheline_internodealigned_in_smp;  
  37. 211  
  38. 212extern void arch_init_copy_chip_data(struct irq_desc *old_desc,  
  39. 213                                        struct irq_desc *desc, int node);  
  40. 214extern void arch_free_chip_data(struct irq_desc *old_desc, struct irq_desc *desc);  
  41. 215  
  42. 216#ifndef CONFIG_SPARSE_IRQ  
  43. 217extern struct irq_desc irq_desc[NR_IRQS];  
 

1>irq:表示這個描述符所對應的中斷號。

2>handle_irq:指向該IRQ線的公共服務程序(即該IRQ所對應的中斷處理程序。

3>chip:它是一個struct irq_chip類型的指針,是中斷控製器的描述符 。在2.6以前的版本中它是hw_irq_controller。
4>handler_data:是handler_irq的參數。
5>chip_data:是指向irq_chip的指針。
6>atcion:一個struct irqaction類型的指針,它指向一個單鏈表。該鏈表是由該中斷線上所有中斷服務例程鏈接起來的。
7>status:表示中斷線當前的狀態。
8>depth:中斷線被激活時,值為0;當值為正數時,表示被禁止的次數。
9>irq_count:表示該中斷線上發生中斷的次數
10>irqs_unhandled:該IRQ線上未處理中斷發生的次數
11>name:申請中斷設備的名字。

C.struct irq_chip結構體:

struct irq_chip是一個中斷控製器的描述符。Linux支持N種可編程中斷控製器PIC(中斷控製器),通常不同的體係結構就有一套自己的中斷處理方式。內核為了統一的處理中斷,提供了底層的中斷處理抽象接口,對於每個平台都需要實現底層的接口函數。這樣對於上層的中斷通用處理程序就無需任何改動。

struct irq_chip的具體代碼如下:

[c-sharp] view plaincopy
  1. 111struct irq_chip {  
  2. 112        const char      *name;  
  3. 113        unsigned int    (*startup)(unsigned int irq);  
  4. 114        void            (*shutdown)(unsigned int irq);  
  5. 115        void            (*enable)(unsigned int irq);  
  6. 116        void            (*disable)(unsigned int irq);  
  7. 117  
  8. 118        void            (*ack)(unsigned int irq);  
  9. 119        void            (*mask)(unsigned int irq);  
  10. 120        void            (*mask_ack)(unsigned int irq);  
  11. 121        void            (*unmask)(unsigned int irq);  
  12. 122        void            (*eoi)(unsigned int irq);  
  13. 123  
  14. 124        void            (*end)(unsigned int irq);  
  15. 125        int             (*set_affinity)(unsigned int irq,  
  16. 126                                        const struct cpumask *dest);  
  17. 127        int             (*retrigger)(unsigned int irq);  
  18. 128        int             (*set_type)(unsigned int irq, unsigned int flow_type);  
  19. 129        int             (*set_wake)(unsigned int irq, unsigned int on);  
  20. 130  
  21. 131        void            (*bus_lock)(unsigned int irq);  
  22. 132        void            (*bus_sync_unlock)(unsigned int irq);  
  23. 133  
  24. 134        /* Currently used only by UML, might disappear one day.*/  
  25. 135#ifdef CONFIG_IRQ_RELEASE_METHOD  
  26. 136        void            (*release)(unsigned int irq, void *dev_id);  
  27. 137#endif  
  28. 138        /* 
  29. 139         * For compatibility, ->typename is copied into ->name. 
  30. 140         * Will disappear. 
  31. 141         */  
  32. 142        const char      *typename;  
  33. 143};  
  34. 144  

name:中斷控製器的名字;
Startup:啟動中斷線;
Shutdown:關閉中斷線;
Enable:允許中斷;
Disable:禁止中斷;

分析了struct irq_desc,struct irq_chip和irqaction的數據結構之後我們來看看他們之間的關係 。

現在深入分析request_irq()內部是如何實現的。

[c-sharp] view plaincopy
  1. 135request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,  
  2. 136            const char *name, void *dev)  
  3. 137{  
  4. 138        return request_threaded_irq(irq, handler, NULL, flags, name, dev);  
  5. 139}  
  6. 140  

可以看到request_irq()函數裏麵有封裝了request_threaded_irq(irq, handler, NULL, flags, name, dev)函數。

先看一下官方的解釋

[c-sharp] view plaincopy
  1. 1006/** 
  2. 1007 *      request_threaded_irq - allocate an interrupt line 
  3. 1008 *      @irq: Interrupt line to allocate 
  4. 1009 *      @handler: Function to be called when the IRQ occurs. 
  5. 1010 *                Primary handler for threaded interrupts 
  6. 1011 *                If NULL and thread_fn != NULL the default 
  7. 1012 *                primary handler is installed 
  8. 1013 *      @thread_fn: Function called from the irq handler thread 
  9. 1014 *                  If NULL, no irq thread is created 
  10. 1015 *      @irqflags: Interrupt type flags 
  11. 1016 *      @devname: An ascii name for the claiming device 
  12. 1017 *      @dev_id: A cookie passed back to the handler function 
  13. 1018 * 
  14. 1019 *      This call allocates interrupt resources and enables the 
  15. 1020 *      interrupt line and IRQ handling. From the point this 
  16. 1021 *      call is made your handler function may be invoked. Since 
  17. 1022 *      your handler function must clear any interrupt the board 
  18. 1023 *      raises, you must take care both to initialise your hardware 
  19. 1024 *      and to set up the interrupt handler in the right order. 
  20. 1025 * 
  21. 1026 *      If you want to set up a threaded irq handler for your device 
  22. 1027 *      then you need to supply @handler and @thread_fn. @handler ist 
  23. 1028 *      still called in hard interrupt context and has to check 
  24. 1029 *      whether the interrupt originates from the device. If yes it 
  25. 1030 *      needs to disable the interrupt on the device and return 
  26. 1031 *      IRQ_WAKE_THREAD which will wake up the handler thread and run 
  27. 1032 *      @thread_fn. This split handler design is necessary to support 
  28. 1033 *      shared interrupts. 
  29. 1034 * 
  30. 1035 *      Dev_id must be globally unique. Normally the address of the 
  31. 1036 *      device data structure is used as the cookie. Since the handler 
  32. 1037 *      receives this value it makes sense to use it. 
  33. 1038 * 
  34. 1039 *      If your interrupt is shared you must pass a non NULL dev_id 
  35. 1040 *      as this is required when freeing the interrupt. 
  36. 1041 * 
  37. 1042 *      Flags: 
  38. 1043 * 
  39. 1044 *      IRQF_SHARED             Interrupt is shared 
  40. 1045 *      IRQF_SAMPLE_RANDOM      The interrupt can be used for entropy 
  41. 1046 *      IRQF_TRIGGER_*          Specify active edge(s) or level 
  42. 1047 * 
  43. 1048 */  

5.首先分析request_threaded_irq()函數中的各個形參
1>:irq:表示申請的中斷號。
2>:handler:表示中斷服務例程
3.> thread_fn:中斷線程化,此處傳遞的是NULL。NULL表示沒有中斷線程化。
此參數是最新版本中才出現的。為什麼要提出中斷線程化?
在 Linux 中,中斷具有最高的優先級。不論在任何時刻,隻要產生中斷事件,內核將立即執行相應的中斷
處理程序,等到所有掛起的中斷和軟中斷處理完畢後才能執行正常的任務,因此有可能造成實時任務得不
到及時的處理。中斷線程化之後,中斷將作為內核線程運行而且被賦予不同的實時優先級,實時任務可以
有比中斷線程更高的優先級。這樣,具有最高優先級的實時任務就能得到優先處理,即使在嚴重負載下仍
有實時性保證。but,並不是所有的中斷都可以被線程化,比如時鍾中斷,主要用來維護係統時間以及定時器
等,其中定時器是操作係統的脈搏,一旦被線程化,就有可能被掛起,這樣後果將不堪設想,所以不應當
被線程化。
 
4>.irqflags:表示中斷標誌位。
5>.devname:表示請求中斷的設備的名稱。

6>.dev_id: 對應於request_irq()函數中所傳遞的第五個參數,可取任意值,但必須唯一能夠代表發出中斷請求的設備,通常取描述該設備的結構體。 共享中斷時所用。

現在繼續迭代深入 request_threaded_irq()內部是如何實現的。

[c-sharp] view plaincopy
  1. 1049int request_threaded_irq(unsigned int irq, irq_handler_t handler,  
  2. 1050                         irq_handler_t thread_fn, unsigned long irqflags,  
  3. 1051                         const char *devname, void *dev_id)  
  4. 1052{  
  5. 1053        struct irqaction *action;  
  6. 1054        struct irq_desc *desc;  
  7. 1055        int retval;  
  8. 1056  
  9. 1057        /* 
  10. 1058         * Sanity-check: shared interrupts must pass in a real dev-ID, 
  11. 1059         * otherwise we'll have trouble later trying to figure out 
  12. 1060         * which interrupt is which (messes up the interrupt freeing 
  13. 1061         * logic etc). 
  14. 1062         */  
  15. 1063        if ((irqflags & IRQF_SHARED) && !dev_id)  
  16. 1064                return -EINVAL;  
  17. 1065  
  18. 1066        desc = irq_to_desc(irq);  
  19. 1067        if (!desc)  
  20. 1068                return -EINVAL;  
  21. 1069  
  22. 1070        if (desc->status & IRQ_NOREQUEST)  
  23. 1071                return -EINVAL;  
  24. 1072  
  25. 1073        if (!handler) {  
  26. 1074                if (!thread_fn)  
  27. 1075                        return -EINVAL;  
  28. 1076                handler = irq_default_primary_handler;  
  29. 1077        }  
  30. 1078  
  31. 1079        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);  
  32. 1080        if (!action)  
  33. 1081                return -ENOMEM;  
  34. 1082  
  35. 1083        action->handler = handler;  
  36. 1084        action->thread_fn = thread_fn;  
  37. 1085        action->flags = irqflags;  
  38. 1086        action->name = devname;  
  39. 1087        action->dev_id = dev_id;  
  40. 1088  
  41. 1089        chip_bus_lock(irq, desc);  
  42. 1090        retval = __setup_irq(irq, desc, action);  
  43. 1091        chip_bus_sync_unlock(irq, desc);  
  44. 1092  
  45. 1093        if (retval)  
  46. 1094                kfree(action);  
  47. 1095  
  48. 1096#ifdef CONFIG_DEBUG_SHIRQ  
  49. 1097        if (!retval && (irqflags & IRQF_SHARED)) {  
  50. 1098                /* 
  51. 1099                 * It's a shared IRQ -- the driver ought to be prepared for it 
  52. 1100                 * to happen immediately, so let's make sure.... 
  53. 1101                 * We disable the irq to make sure that a 'real' IRQ doesn't 
  54. 1102                 * run in parallel with our fake. 
  55. 1103                 */  
  56. 1104                unsigned long flags;  
  57. 1105  
  58. 1106                disable_irq(irq);  
  59. 1107                local_irq_save(flags);  
  60. 1108  
  61. 1109                handler(irq, dev_id);  
  62. 1110  
  63. 1111                local_irq_restore(flags);  
  64. 1112                enable_irq(irq);  
  65. 1113        }  
  66. 1114#endif  
  67. 1115        return retval;  
  68. 1116}  

程序的第一行和第二行分別定義了:

(1) struct irqaction *action;

(2)2struct irq_desc *desc;

兩個指針action和desc,它們分別指向了結構體irqaction和 irq_desc。

(3)    if ((irqflags & IRQF_SHARED) && !dev_id)
              return -EINVAL;

作用是:判斷中斷標誌位,如果是共享中斷的話就必須要有一個唯一的dev_id,否則返回一個錯誤。

(4)      desc = irq_to_desc(irq);

irq_to_desc(irq):根據中斷號irq在 irq_desc[NR_IRQS]數組中 返回一個具體的irq_desc。即根據irq找到它的中斷處理程序。

(5)    if (!desc)

        return -EINVAL;

當返回一個空值時返回一個錯誤。說明申請中斷號失敗。

(6)if (desc->status & IRQ_NOREQUEST)
               return -EINVAL;

判斷中斷線的狀態,若為IRQ_NOREQUEST時( IRQ_NOREQUEST表示 IRQ 不能被申請)

(7)        if (!handler) {
                        if (!thread_fn)
                        return -EINVAL;
               handler = irq_default_primary_handler;
              }

判斷中斷服務例程是否為空,如果handler為空,則判斷線程中斷服務例程,若線程中斷服務例程也為空,則返回一個錯誤值。否則中斷服務例程指向: rq_default_primary_handler。

(8)

1079        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
1080        if (!action)
1081                return -ENOMEM;
1082
1083        action->handler = handler;
1084        action->thread_fn = thread_fn;
1085        action->flags = irqflags;
1086        action->name = devname;
1087        action->dev_id = dev_id;

從1079~1087:根據requst_irq()函數中傳遞的參數生成一個irqaction.

1097        if (!retval && (irqflags & IRQF_SHARED)) {
1098                /*
1099                 * It's a shared IRQ -- the driver ought to be prepared for it
1100                 * to happen immediately, so let's make sure....
1101                 * We disable the irq to make sure that a 'real' IRQ doesn't
1102                 * run in parallel with our fake.
1103                 */
1104                unsigned long flags;
1105
1106                disable_irq(irq);
1107                local_irq_save(flags);
1108
1109                handler(irq, dev_id);
1110
1111                local_irq_restore(flags);
1112                enable_irq(irq);
1113        }

1097~1113:如果為共享中斷的話,在執行中斷服務例程之前,要先把這條中斷線上的中斷屏蔽,讓後在執行,執行完之後打開中斷。

6.有注冊中斷服務函數,那必然有相應的釋放中斷函數。

可以調用void free_irq(unsigned int irq, void *dev_id)來釋放我們申請的中斷線。

函數形參:

1>unsigned int riq:表示申請的中斷號與request_irq()函數中的第一個形參對應。

2>void *dev_id:與request_irq()函數中的最後一個形參含義和用法相同,在此不再說明。

函數功能:

如果指定的中斷線不是共享的,那麼,該函數刪除處理程序的同時將禁用這條中斷線。如果中斷線是共享的,則僅刪除dev_id所對應的處理程序,而這條中斷線本省隻有在刪除了最後一個處理程序時才會被禁止。

切記:This function must not be called from interrupt context

freee_irq()函數不能在中斷上下文中被調用。

3>深入分析下free_irq()函數內部是如何實現的

[c-sharp] view plaincopy
  1.  993void free_irq(unsigned int irq, void *dev_id)  
  2.  994{  
  3.  995        struct irq_desc *desc = irq_to_desc(irq);  
  4.  996  
  5.  997        if (!desc)  
  6.  998                return;  
  7.  999  
  8. 1000        chip_bus_lock(irq, desc);  
  9. 1001        kfree(__free_irq(irq, dev_id));  
  10. 1002        chip_bus_sync_unlock(irq, desc);  
  11. 1003}  

可以看到free_irq()函數了封裝了_free_irq(irq,dev_id)函數。

free_irq()調用_free_irq()把每一個具體的中斷服務例程()釋放。

最後更新:2017-04-03 19:13:18

  上一篇:go 那些優秀的開源係統恢複軟件
  下一篇:go 遞歸算法時間複雜度