內核驅動:中斷機制


A9處理器:EXYNOS4412

中斷:硬件產生(總是需要硬件電路上的一根中斷線):USB線是有VCC、GND、D+、D-四根線組成,所以是不能產生中斷的;

中斷產生的一般過程:中斷源產生中斷------>中斷控制器(根據中斷優先級)------>ARM處理器

對於4412處理器來說,搞清中斷源和中斷號的對應關系是一個很復雜的工作具體可參考arch/arm/mach-exynos/include/mach/irqs.h頭文件

ARM平台查找中斷號:

1.芯片內部外設:利用設備的名字在irqs.h中查找中斷號;

2.芯片外部外設:設備的中斷連接到4412的GPIO引腳,找到GPIO引腳的GPIO號,然后調用函數gpio_to_irq();

中斷函數的注冊:

1.需要包含頭文件<mach/irqs.h>,如果要找4412內部設備的中斷號,只能看文件<mach/irqs.h>

 

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

 

linux的中斷處理機制

GPIO產生中斷,將中斷給GPIO控制器,GPIO控制器再將中斷交給GIC處理,最后GIC交與cpu做處理:如下圖:

 

mask:設備上級組織(GIC或者GPIO控制器)一般在中斷到達的過程中屏蔽中斷;同時,CPU也可以通過控制CPSR寄存器來屏蔽來自GIC的所有中斷; 

中斷處理機制

  當中斷產生后,內核會保護當前進程的執行現場,並進入異常處理,通過異常像量表找到產生中斷的中斷號,並由中斷號,在內核中找到中斷號為100的irq_desc:主要用於描述中斷信息,並執行中斷處理函數(需要用戶有注冊行為)。用戶在處理中斷的過程中,首先要先寫好中斷處理函數,封裝到iqaction並由iqaction想內核進行注冊,從而是handler函數有效;

ps:

1.linux內核在啟動時分配了一個irq_desc的數組,數組中共有NR_IRQS個成員。每個irq_desc中記錄對應中斷的各類信息,比如中斷的處理函數,中斷的發生次數等。irq_desc 結構體定義在<linux/irqdesc.h中>

2.irqaction定義在<linux/interrupt.h>每個irqaction用於封裝一個中斷處理函數。結構體由驅動人員負責分配。irqaction中包含中斷號,中斷處理函數指針,中斷的執行標志,中斷名等。

3.中斷處理函數的返回值為 irq_handler_t類型;定義在<linux/interrupt.h>,如下:

irqreturn_t (*irq_handler_t)(int, void *);

中斷處理函數。由驅動負責實現,記錄在irqaction中。

irqreturn_t只有兩個值,IRQ_NONE/IRQ_HANDLED。如果中斷不是由本設備引起的,則返回IRQ_NONE,否則返回IRQ_HANDLED。
函數參數irq為中斷號,void *為傳遞給中斷處理函數的參數,對應irqaction->dev_id。

 

中斷處理函數的編寫與注冊

驅動人員在設計中斷處理函數時,要遵循的要求是:
(1)可嵌套不可重入
(2)不能睡眠
(3)如果硬件有中斷的狀態寄存器,軟件要負責清除中斷的標志位。一般來說,如果不清除標志位,設備無法再次產生中斷

(4)中斷處理函數的注冊和注銷

0.前提

  需要包含的頭文件

  #include <linux/interrupt.h>
  #include <mach/irqs.h> //片內外設
  #include <linux/gpio.h> //片外外設
  #include <mach/gpio.h>

1.確定中斷號

  正如剛才如上所述:如果是外部IO中斷,那么我們可以通過GPIO號獲取中斷號:

  eg:#define KEY_IRQ     gpio_to_irq(gpio號);

2.中斷處理函數編寫

  中斷處理函數的格式:

  eg:

  //參數1:int irq :中斷號

  //參數2:void * dev_id :為傳遞給中斷處理函數的參數

  //返回值:IRQ_NONE:中斷不是由本設備引起的

                   IRQ_HANDLED:中斷是由本設備引起的

  static irqreturn_t  key_service(int irq, void *dev_id)

  {
  //根據硬件要求完成相應工作
  ...
  return IRQ_HANDLED 或 IRQ_NONE;
  }

3.注冊中斷處理函數(必須檢查返回值)

  u32 flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;//定義中斷的標志:下降沿/上升沿/低電平/高電平觸發中斷

  int ret;
  ret = request_irq(  KEY_IRQ, /* 中斷號 */
             key_service, /* 中斷處理函數 */
             flags, /* 中斷的標志 */
              "xxx", /* 中斷處理函數的名字 */
              dev_id);/* dev_id為傳給中斷處理函數的參數,一般會設置為私有結構體的指針,非共享>中斷可以為NULL */

  if (ret) {
    printk("Cannot register interrupt handler\n");
    return -1;
   }

4.注銷中斷處理函數 

  free_irq(irq, dev_id); //參數為中斷號和dev_id。 dev_id一定要和request_irq中的最后一個參數一致

 

5.屏蔽中斷

  1.可以人為關閉(mask)/打開某個中斷:
    disable_irq(int irq);
    enable_irq(int irq);
    這兩個函數是可以嵌套的,也就是說,如果對一個中斷disable了3次,需要調用enable函數3次才能打開對該中斷的屏蔽;

  2.還可以屏蔽本cpu的中斷:
    local_irq_disable();
    local_irq_enable();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM