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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章