// 中斷電流類型: // 邊沿型(edge); // 經過電位變化觸發中斷(上升沿/降低沿),若是外設但願觸發一箇中斷,它在irq line上發送一個脈衝, // 而後釋放irq line恢復到inactive狀態。cpu經過檢測irq line上的脈衝來觸發中斷處理函數的執行。 // 電平型(level): // 經過特定電位觸發中斷,若是外設但願觸發一箇中斷,它會將irq line設置到active level(高電平/低電平), // 而後一直保持此irq line爲active level,直到中斷被處理。cpu經過週期性採樣irq line的電平來觸發中斷處理函數的執行。 // 在2.6.x版本後,中斷描述符irq_desc->handle_irq封裝對不一樣電流類型的處理。 // struct irq_desc // { // irq_flow_handler_t handle_irq; // ... // } // 電流處理入口: // 系統中全部中斷統一通過do_IRQ處理: // 1.irq_desc提供電流處理例程(irq_desc->handle_irq != NULL),則調用; // 2.不然,經過__do_IRQ處理全部電流類型。 // do_IRQ(regs) // { // .... // if(irq_desc->handle_irq) // { // irq_desc->handle_irq(irq, irq_desc); // }else // { // __do_IRQ(irq, regs); // } // .... // } // // 設置irq_desc的電流處理例程 // 參數: // typedef void (*irq_flow_handler_t)(unsigned int irq, struct irq_desc *desc); 1.1 static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handler) { struct irq_desc *desc; desc = irq_to_desc(irq); desc->handle_irq = handler; } // 邊沿型中斷處理 // 函數主要任務: // 1.檢查中斷是否被禁用,或者中斷處理函數是否在運行過程當中 // 1.1 若是是,向芯片屏蔽並確認此中斷,設置標誌表示有待處理的中斷,退出 // 2.向芯片確認這次中斷 // 3.設置中斷處理函數在運行中標誌 // 4.運行中斷處理函數 // 5.檢查在運行中斷處理函數過程當中,是否有新中斷(步驟1中會被設置) // 5.1 若是有,清除標誌,轉去步驟4 // 6.清除中斷處理函數在運行中標誌,表示全部已經發生的中斷都被處理完畢 // 7.退出 2.1 void handle_edge_irq(unsigned int irq, struct irq_desc *desc) { const unsigned int cpu = smp_processor_id(); spin_lock(&desc->lock); desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); //只有設備的中斷請求引腳(中斷線)的電平發生跳變時(由高變低或者有低變高),纔會發出中斷請求, //由於跳變是一瞬間,並且不會像電平中斷能保持住電平,因此處理不當就特別容易漏掉一次中斷請求, //爲了不這種狀況,屏蔽中斷的時間必須越短越好。內核的開發者們顯然意識到這一點,在正是處理中斷前, //判斷IRQ_INPROGRESS標誌沒有被設置的狀況下,只是ack irq,並無mask irq,以便復位設備的中斷請求引腳, //在這以後的中斷處理期間,另外的cpu能夠再次響應同一個irq請求,若是IRQ_INPROGRESS已經置位, //代表另外一個CPU正在處理該irq的上一次請求,這種狀況下,他只是簡單地設置IRQ_PENDING標誌,而後mask_ack_irq後退出, //中斷請求交由原來的CPU繼續處理。由於是mask_ack_irq,因此係統實際上只容許掛起一次中斷。 if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) || !desc->action)) { desc->status |= (IRQ_PENDING | IRQ_MASKED); mask_ack_irq(desc, irq); goto out_unlock; } //向芯片確認此irq desc->chip->ack(irq); //中斷處理函數運行 desc->status |= IRQ_INPROGRESS; do { struct irqaction *action = desc->action; irqreturn_t action_ret; //若是沒有中斷處理函數,屏蔽此中斷 if (unlikely(!action)) { desc->chip->mask(irq); goto out_unlock; } //處理中斷期間,另外一次請求可能由另外一個cpu響應後掛起,因此在處理完本次請求後還要判斷IRQ_PENDING標誌, //若是被置位,當前cpu要接着處理被另外一個cpu「委託」的請求。內核在這裏設置了一個循環來處理這種狀況, //直到IRQ_PENDING標誌無效爲止,並且由於另外一個cpu在響應並掛起irq時,會mask irq, //因此在循環中要再次unmask irq,以便另外一個cpu能夠再次響應並掛起irq。 if (unlikely((desc->status & (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) == (IRQ_PENDING | IRQ_MASKED))) { //解除屏蔽,從新容許中斷 desc->chip->unmask(irq); desc->status &= ~IRQ_MASKED; } //處理在運行中斷處理函數過程當中發生的中斷 desc->status &= ~IRQ_PENDING; spin_unlock(&desc->lock); //調用中斷處理函數 action_ret = handle_IRQ_event(irq, action); spin_lock(&desc->lock); } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING); //中斷所有被處理 desc->status &= ~IRQ_INPROGRESS; out_unlock: spin_unlock(&desc->lock); } // 電平型中斷 // 只要設備的中斷請求引腳(中斷線)保持在預設的觸發電平,中斷就會一直被請求, // 因此,爲了不同一中斷被重複響應,必須在處理中斷前先把mask irq,而後ack irq, // 以便復位設備的中斷請求引腳,響應完成後再unmask irq。 // 函數主要任務: // 1.向芯片屏蔽並確認此中斷 // 2.運行中斷處理函數 // 3.解除屏蔽 // 4.退出 2.2 void handle_level_irq(unsigned int irq, struct irq_desc *desc) { raw_spin_lock(&desc->lock); //確認並屏蔽此中斷 mask_ack_irq(desc); desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); //沒有中斷處理函數,或者中斷被禁止,退出 if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) goto out_unlock; //調用中斷處理函數 handle_irq_event(desc); //解除屏蔽 cond_unmask_irq(desc); out_unlock: raw_spin_unlock(&desc->lock); } // 雖然handle_level_irq對電平中斷的流控進行了必要的處理,由於電平中斷的特性:只要沒有ack irq,中斷線會一直有效, // 因此咱們不會錯過某次中斷請求,可是驅動程序的開發人員若是對該過程理解不透徹,特別容易發生某次中斷被屢次處理的狀況。 // 特別是使用了中斷線程(action->thread_fn)來響應中斷的時候:一般mask_ack_irq只會清除中斷控制器的pending狀態, // 不少慢速設備(例如經過i2c或spi控制的設備)須要在中斷線程中清除中斷線的pending狀態,可是未等到中斷線程被調度執行的時候, // handle_level_irq早就返回了,這時已經執行過unmask_irq,設備的中斷線pending處於有效狀態,中斷控制器會再次發出中斷請求, // 結果是設備的一次中斷請求,產生了兩次中斷響應。要避免這種狀況,最好的辦法就是不要單獨使用中斷線程處理中斷, // 而是要實現request_threaded_irq()的第二個參數irq_handler_t:handler,在handle回調中使用disable_irq() // 關閉該irq,而後在退出中斷線程回調前再enable_irq()。 // 參考: // http://en.wikipedia.org/wiki/Interrupt // http://blog.csdn.net/xiaoxiaomuyu2010/article/details/12162599 // http://blog.csdn.net/droidphone/article/details/7489756