linux中斷機制及中斷註冊1(韋東山的視頻總結及針對linux-2.6.30.4)

自己的總結有錯誤請評論,我們共同進步。

下面的以天嵌 用戶模式下 按下按鍵k1 產生中斷EINT1爲例進行分析的,內核代碼只是摘錄中斷相關的。

下面爲流程圖,



traps.c中early_trap_init(void)被用來設置各種異常向量,通俗的說就是把有關異常代碼放到固定位置,當發生異常時,CPU會自動找到相關異常的代碼進行執行。

void __init early_trap_init(void){

unsigned long vectors = CONFIG_VECTORS_BASE; 

/*CONFIG_VECTORS_BASE配置項,vi .config可以查看其值,此值爲異常向量基址*/

memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

/*從__vectors_start到 __vectors_end 複製到vectors中去*/

}
__vectors_start異常代碼,(在kernel\entry-armv.S中,下面只是分析中斷)
__vectors_start:
b vector_irq + stubs_offset  /*vector_irq是宏,就在此文件中stubs_offset   */


/*與vector_irq 相關代碼*/

__stubs_start:
/*
 * Interrupt dispatcher
 */
vector_stub irq, IRQ_MODE, 4                                 (1)
.long __irq_usr@  0  (USR_26 / USR_32)/*用戶模式下產生中斷*/
.long __irq_invalid@  1  (FIQ_26 / FIQ_32)
.long __irq_invalid@  2  (IRQ_26 / IRQ_32)
.long __irq_svc@  3  (SVC_26 / SVC_32)/*管理模式下產生中斷*/
.long __irq_invalid@  4
.long __irq_invalid@  5
.long __irq_invalid@  6
.long __irq_invalid@  7
.long __irq_invalid@  8
.long __irq_invalid@  9
.long __irq_invalid@  a
.long __irq_invalid@  b
.long __irq_invalid@  c
.long __irq_invalid@  d
.long __irq_invalid@  e

.long__irq_invalid@  f


/*
 *Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 */
在此文件中有上面一句話,意思大概是宏的共同的入口函數(英語不怎麼地,只能知道大概意思)
           /*上面的 vector_stub irq, IRQ_MODE, 4  跟這個結構好像呢                    
          
.macro vector_stub, name, mode, correction=0             (2)
.align 5
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif


@
@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
@ (parent CPSR)
@
stmia sp, {r0, lr}@ save r0, lr
mrs lr, spsr
str lr, [sp, #8]@ save spsr


@
@ Prepare for SVC32 mode.  IRQs remain disabled.
@
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE)
msr spsr_cxsf, r0


@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]

movspc, lr@ branch to handler in SVC mode

ENDPROC(vector_\name)

.endm



有(1)和(2)就可以把宏vector_irq展開(更改的用紅色字體已標出)。
.macro vector_stub, irq, IRQ_MODE, 4             
.align 5
vector_irq:
.if 4
sub lr, lr, #4/*計算返回值*/
.endif


@
@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
@ (parent CPSR)
@
stmia sp, {r0, lr}@ save r0, lr
mrs lr, spsr
str lr, [sp, #8]@ save spsr


@
@ Prepare for SVC32 mode.  IRQs remain disabled.
@
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE)
msr spsr_cxsf, r0


@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]
movs pc, lr@ branch to handler in SVC mode
ENDPROC(vector_irq)

.endm



用戶模式下發生中斷進入__irq_usr
__irq_usr:
usr_entry /*主要是保存返回的寄存器的值*/

irq_handler/*在kernel\entry-armv.S定義的宏*/


  /*********************展開irq_handler****************/
/*
   *Interrupt handling.  Preserves r7, r8, r9
   */
  .macroirq_handler//最終調用asm_do_IRQ(中斷處理)
  bneasm_do_IRQ

  /****************************************************/


asm_do_IRQ主要的功能1.分辨中斷,2.調用中斷處理函數,3.清中斷。
下面分析asm_do_IRQ函數(在\kernel\irq.c中定義)
 asm_do_IRQ(unsigned int irq, struct pt_regs *regs)  
{
            generic_handle_irq(irq);   /*主要的中斷都是調用它,在linux\irq.h中定義*/

}


  /************把generic_handle_irq(irq)展開*********************/   
   generic_handle_irq( irq)
   {

            generic_handle_irq_desc(irq, irq_to_desc(irq));/*被調用也在linux   \irq.h中定義*/


 /********irq_to_desc(irq)展開(在irq\handle.c中定義)*********/
        struct irq_desc *irq_to_desc(unsigned int irq)
     {
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
        }
   返回以中斷號irq爲下標的中斷描述項的指針 即:dev=irq_desc+irq
      中斷描述的數組(在irq\handle.c中定義)爲:
       struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
        [0 ... NR_IRQS-1] = {
    .status = IRQ_DISABLED,
      .chip = &no_irq_chip,
      .handle_irq = handle_bad_irq,
     .depth = 1,
     .lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
   }    
     /**************************************************/
    

}

  /*******************************************************************/

  
      


    /************把generic_handle_irq_desc展開************/
    /*irq_desc + irq 分辨出中斷,就是確定了中斷源,調用->handle_irq()*/
    generic_handle_irq_desc(unsigned int irq, irq_desc + irq )
    {  
          desc->handle_irq(irq, desc);
     }

   /*****************************************************************/


    /****desc->handle_irq(irq, desc);相關的*****/

   /*在irq/chip.c中定義*/

  __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,const char *name)

{

       desc->handle_irq = handle; 
}


/*
 * Set a highlevel flow handler for a given IRQ:

 */

 /*linux\irq.h中定義*/

set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
       __set_irq_handler(irq, handle, 0, NULL);
}


/* s3c24xx_init_irq
 *
 * Initialise S3C2410 IRQ system
*/
/*在plat-s3c24xx\irq.c中定義*/

void __init   s3c24xx_init_irq(void)
{
     for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
     irqdbf("registering irq %d (ext int)\n", irqno);

     set_irq_chip(irqno, &s3c_irq_eint0t4);     

             

    /******set_irq_chip(irqno, &s3c_irq_eint0t4)展開***/

     int set_irq_chip(irq, &s3c_irq_eint0t4)

{

              struct irq_desc *desc = irq_to_desc(irq);
              irq_chip_set_defaults(chip);
              desc->chip = chip;
     }
      結果爲設置了desc中的chip域

     /*************************************************/


       set_irq_handler(irqno, handle_edge_irq);  /*設置中斷事件處理函數 即dev->handle_irqhandle_edge_irq*/
       set_irq_flags(irqno, IRQF_VALID);
    }

}



/************ handle_edge_irq展開****************/
handle_edge_irq(unsigned int irq, struct irq_desc *desc){
/* Start handling the irq */
if (desc->chip->ack)
desc->chip->ack(irq); /*清中斷,查看s3c_irq_eint0t4可查看ack*/
action_ret = handle_IRQ_event(irq, action); /*中斷處理*/ 
}

/***********************************************/


/****************** handle_IRQ_event(irq, action)展開*********/
/**
 * handle_IRQ_event - irq action chain handler
 * @irq: the interrupt number
 * @action: the interrupt action chain for this irq
 *
 * Handles the action chain of an irq event
 */
irqreturn_t handle_IRQ_event(irqirq, action)
{
do {
trace_irq_handler_entry(irq, action);
ret = action->handler(irq, action->dev_id); /*我們定義的中斷處理函數執行*/
trace_irq_handler_exit(irq, action, ret);


switch (ret) {
case IRQ_WAKE_THREAD:
/*
* Set result to handled so the spurious check
* does not trigger.
*/
ret = IRQ_HANDLED;


/*
* Catch drivers which return WAKE_THREAD but
* did not set up a thread function
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}


/*
* Wake up the handler thread for this
* action. In case the thread crashed and was
* killed we just pretend that we handled the
* interrupt. The hardirq handler above has
* disabled the device interrupt, so no irq
* storm is lurking.
*/
if (likely(!test_bit(IRQTF_DIED,
    &action->thread_flags))) {
set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
wake_up_process(action->thread);
}


/* Fall through to add to randomness */
case IRQ_HANDLED:
status |= action->flags;
break;


default:
break;
}


retval |= ret;
action = action->next;   //爲NULL跳出,
} while (action);
return retval;
}
結果爲調用action->handler(irq, action->dev_id)處理中斷,就是我們定義在驅動中的中斷處理函數
/************************************************************************/
發佈了36 篇原創文章 · 獲贊 7 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章