您的位置:首页 > 运维架构 > Linux

linux中断机制及中断注册1(韦东山的视频总结及针对linux-2.6.30.4)

2013-05-19 16:43 176 查看
自己的总结有错误请评论,我们共同进步。

下面的以天嵌 用户模式下 按下按键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)处理中断,就是我们定义在驱动中的中断处理函数

/************************************************************************/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐