下面的以天嵌 用戶模式下 按下按鍵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
.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:
usr_entry /*主要是保存返回的寄存器的值*/
irq_handler/*在kernel\entry-armv.S定義的宏*/
/*
*Interrupt handling. Preserves r7, r8, r9
*/
.macroirq_handler//最終調用asm_do_IRQ(中斷處理)
bneasm_do_IRQ
/****************************************************/
下面分析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_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_flags(irqno, IRQF_VALID);
}
}
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 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)處理中斷,就是我們定義在驅動中的中斷處理函數
/************************************************************************/