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


中斷子係統5_電流層處理

    
//  電流類型:
//    1.電平型:handle_level_irq
/*
 *    Level type interrupts are active as long as the hardware line has
 *    the active level. This may require to mask the interrupt and unmask
 *    it after the associated handler has acknowledged the device, so the
 *    interrupt line is back to inactive.
 */
//      譯:當中斷線電平達到激活電平,中斷一直會被激活。 所以需要剛進中斷處理函數就要屏蔽掉中斷,
//          等handler 處理完後再打開中斷(unmask)。
//      問:為什麼要先屏蔽中斷中斷,後確認呢(mask_and_ack)?
//      答:因為cpu周期性采樣中斷線,當發現電平有效時,觸發中斷,因此外部設備為了保證cpu可以檢測到
//          有效電平,需要將電平保持一段時間。當cpu檢測到有效電平後,cpu引腳被置位,如果不先屏蔽中斷,
//          直接確認,當cpu引腳複位時,如果此時電平依然有效,那麼同一個中斷又被觸發,此為不正確的。
//            (注:cpu確認是用於複位cpu引腳,對外設的應答在注冊的irq_action由特定的驅動程序進行)
//      問:這對於編寫ISR有什麼啟示呢?
//      答:由於在handle_level_irq調用ISR過程中,中斷被屏蔽,因此在此期間的新發生的中斷可能會丟失(
//          因為電平型中斷信號不被中斷控製器鎖存,電平恢複之前,如果cpu沒有通過采樣發現此有效電平,
//          則中斷丟失),所以ISR應該盡快執行完畢。
//          

//    2.邊沿型:handle_edge_irq
/*
 *    Interrupt occures on the falling and/or rising edge of a hardware
 *    signal. The occurence is latched into the irq controller hardware
 *    and must be acked in order to be reenabled. After the ack another
 *    interrupt can happen on the same source even before the first one
 *    is handled by the assosiacted event handler. If this happens it
 *    might be necessary to disable (mask) the interrupt depending on the
 *    controller hardware. This requires to reenable the interrupt inside
 *    of the loop which handles the interrupts which have arrived while
 *    the handler was running. If all pending interrupts are handled, the
 *    loop is left.
 */
 //   譯:中斷發生在上升沿/下降沿,它會被中斷控製器鎖存起來,需要ack後才能重新使能。 
 //   ack後新的中斷可以在前一個中斷正在被處理時產生,如果這種情況發生,則需要屏蔽中斷。
 //   同時,需要用一個loop來處理中斷處理過程中又有中斷產生的情況,在這個loop中重新把
 //   中斷屏蔽打開。 如果所有pending的在這個loop中重新把中斷屏蔽打開。 如果所有pending
 //   的中斷都處理完了,loop就可以離開。
 //   問:為什麼不需要先屏蔽中斷,而是直接確認呢(ack)?
 //   答:因為邊沿型中斷在電平的變化沿才有效,所以外設為觸發中斷,會在中斷線上放一個邊沿脈衝
 //       ,一個邊沿脈衝隻能觸發一次中斷,所以不需要防止像電平型中斷,同一個中斷請求的高電平
 //       觸發多次中斷而需先屏蔽中斷的情況。



//  在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已經置位,表明另一個CPU正在處理該irq的上一次請求,
        //這種情況下,隻是簡單地設置IRQ_PENDING標誌,然後mask_ack_irq後退出,
        //中斷請求交由原來的CPU繼續處理。
        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);
}


//  函數主要任務:
//      1.向芯片屏蔽並確認此IRQ
//      2.如果同一個中斷正在被處理,則直接退出
//      3.否則,運行設置IRQ_INPROGRESS標誌,執行ISR
//          3.1.取消IRQ_INPROGRESS標誌,解除IRQ屏蔽
//      4.退出
2.2 void handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
        raw_spin_lock(&desc->lock);
        //確認並屏蔽此中斷
        mask_ack_irq(desc);

        //同一個中斷正在被處理,則直接退出
        if (unlikely(desc->status & IRQ_INPROGRESS))
            goto out_unlock;

        desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
        
        //沒有中斷處理函數,或者中斷被禁止,退出
        if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))
                goto out_unlock;
        desc->status &= ~IRQ_INPROGRESS;
        raw_spin_unlock(&desc->lock);

        //調用中斷處理函數
        handle_irq_event(desc);


        raw_spin_lock(&desc->lock);
        desc->status &= ~IRQ_INPROGRESS;
        //解除屏蔽
        cond_unmask_irq(desc);
out_unlock:
        raw_spin_unlock(&desc->lock);
}

//  參考:
//      https://en.wikipedia.org/wiki/Interrupt
//      https://blog.csdn.net/xiaoxiaomuyu2010/article/details/12162599
//      https://blog.csdn.net/droidphone/article/details/7489756



//  執行驅動注冊的中斷處理例程
//  函數調用路徑:
//      __do_IRQ->handle_IRQ_event
//      handle_level_irq->handle_IRQ_event
//      handle_edge_irq->handle_IRQ_event
//  函數主要任務:
//      1.遍曆驅動注冊的中斷處理例程
//      2.關中斷
3.1 fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
                struct irqaction *action)
{
    int ret, retval = 0, status = 0;

    //遍曆所有已注冊的irqaction
    do {
        //調用驅動注冊的處理程序
        ret = action->handler(irq, action->dev_id, regs);
        if (ret == IRQ_HANDLED)
            status |= action->flags;
        retval |= ret;
        action = action->next;
    } while (action);
    //中斷處理完畢,在中斷返回路徑上先關中斷,iret會恢複eflags(恢複中斷標誌)
    local_irq_disable();
    return retval;
}


//  關於handle_IRQ_event末尾關中斷的思考:
//      do_IRQ->...->handle_IRQ_event,關中斷->ret_from_intr
//      do_IRQ->...->handle_IRQ_event,關中斷->do_softirq 保存中斷狀態,關中斷->...->do_softirq 恢複中斷狀態->ret_from_intr
//      在中斷處理過程中防止丟失中斷,因此開中斷,允許中斷嵌套;
//      在中斷處理結束時,關中斷,避免沒必要的中斷嵌套
//      當iret中斷返回時,會自動恢複中斷狀態(eflags)。

//  參考:
//      https://www.unixresources.net/linux/clf/linuxK/archive/00/00/58/82/588249.html
//      https://blog.csdn.net/normalnotebook/article/details/1649770





最後更新:2017-04-03 14:54:20

  上一篇:go 二叉樹前序、中序、後序遍曆相互求法
  下一篇:go JSP翻頁功能代碼