sub
sp, sp, #S_FRAME_SIZE //S_FRAME_SIZE实际值为72
stmib
sp, {r1 - r12} //将r1-r12入栈,每次传送前地址加一
ldmia
r0, {r1 - r3} //将r0指示的内存单元数据送往r1-r3寄存器
add
r0, sp, #S_PC
@ here for interlock avoidance
mov
r4, #-1
@ "" "" "" ""
str
r1, [sp]
@ save the "real" r0 copied
@ from the exception stack
@
@ We are now ready to fill in the remaining blanks on the stack:
@
@ r2 - lr_<exception>, already fixed up for correct return/restart
@ r3 - spsr_<exception>
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
@
@ Also, separately save sp_usr and lr_usr
@
stmia
r0, {r2 - r4}
stmdb
r0, {sp, lr}^
@
@ Enable the alignment trap while in kernel mode
@
alignment_trap r0
@
@ Clear FP to mark the first stack frame
@
zero_fp
.endm
注释2:irq_handler是一个宏,展开如下:
.macro
irq_handler
get_irqnr_preamble r5, lr
get_irqnr_and_base r0, r6, r5, lr //判断中断号,通过r0返回。关于这个函数的定义在
include/asm/arch-s3c2410/entry- //macro.s中。具体分析见注释2-1
movne
r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne
lr, 1b
bne
asm_do_IRQ //
进入中断处理,关于这函数的实现在(5)中讲解
#ifdef CONFIG_SMP
/*
* XXX
*
* this macro assumes that irqstat (r6) and base (r5) are
* preserved from get_irqnr_and_base above
*/
test_for_ipi r0, r6, r5, lr
movne
r0, sp
adrne
lr, 1b
bne
do_IPI
#ifdef CONFIG_LOCAL_TIMERS
test_for_ltirq r0, r6, r5, lr
movne
r0, sp
adrne
lr, 1b
bne
do_local_timer
#endif
#endif
.endm
注释2-1:get_irqnr_and_base是一个宏,其展开如下:
.macro
get_irqnr_and_base, irqnr, irqstat, base, tmp
mov
\base, #S3C24XX_VA_IRQ
@@ try the interrupt offset register, since it is there
ldr
\irqstat, [ \base, #INTPND ]
teq
\irqstat, #0
beq
1002f
ldr
\irqnr, [ \base, #INTOFFSET ] //
通过判断INTOFFSET寄存器得到中断位置
mov
\tmp, #1
tst
\irqstat, \tmp, lsl \irqnr
bne
1001f
@@ the number specified is not a valid irq, so try
@@ and work it out for ourselves
mov
\irqnr, #0
@@ start here
@@ work out which irq (if any) we got
movs
\tmp, \irqstat, lsl#16
addeq
\irqnr, \irqnr, #16
moveq
\irqstat, \irqstat, lsr#16
tst
\irqstat, #0xff
addeq
\irqnr, \irqnr, #8
moveq
\irqstat, \irqstat, lsr#8
tst
\irqstat, #0xf
addeq
\irqnr, \irqnr, #4
moveq
\irqstat, \irqstat, lsr#4
tst
\irqstat, #0x3
addeq
\irqnr, \irqnr, #2
moveq
\irqstat, \irqstat, lsr#2
tst
\irqstat, #0x1
addeq
\irqnr, \irqnr, #1
@@ we have the value
adds
\irqnr, \irqnr, #IRQ_EINT0//
加上中断号的基准数值,得到最终的中断号,
IRQ_EINT0在include/asm/arch- s3c2410/irqs.h中定义.从这里可以看出,中断号的具体值是有平台相关的代码决定的,和硬件中断挂起寄存器中的中断号是不 等的
@@ exit here, Z flag unset if IRQ
.endm
(5)到这一步为止,是不是有点晕了呢,好吧,让我们来回忆一下,现在我们到那一步了,我们进入中断处理函数,在中断处理函数中发现了一条语句:bne
asm_do_IRQ,意思是程序将跳转到asm_do_IRQ()函数处去执行。那么好,我们就来找找这个函数,它在
arch/arm/kernel/irq.c中,尼玛,是个c代码,哥终于见到亲人了,下面是它的源代码:
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter(); //没做什么特别的工作,可以跳过不看
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (irq >= NR_IRQS)
handle_bad_irq(irq, &bad_irq_desc);
else
generic_handle_irq(irq); //进入处理函数,在include/linux/irq.h中定义,详见注释1
/* AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
注释1:
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
desc->handle_irq(irq, desc);//关于handle_irq的介绍见注释1-2
#else
if (likely(desc->handle_irq))
desc->handle_irq(irq, desc);
else
__do_IRQ(irq);
#endif
}
static inline void generic_handle_irq(unsigned int irq)
{
generic_handle_irq_desc(irq, irq_to_desc(irq));//
irq_to_desc定义在include/linux/irq.h中,具体代码如注释1-1所示
}
static inline struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < nr_irqs) ? irq_desc + irq : NULL;//irq_desc为一个数组,也在本文件中定义,代码是:extern struct irq_desc //irq_desc[NR_IRQS];其中struct irq_desc是一个结构体,其定义如注释1-1- //1所示,一次本函数的返回至使&irq_desc[irq]
}
注释1-2:
对
desc->handle_irq的赋值出现在kernel/irq/chip.c中,代码如下:desc->handle_irq = handle;
这段代码在函数__set_irq_handler中,而函数__set_irq_handler又被include/linux/irq.h中的set_irq_handler函数调用,代码如下:__set_irq_handler(irq, handle, 0, NULL);
而set_irq_handler又被arch/arm/plat-s3c24xx/irq.c下的s3c24xx_init_irq调用,这下我们就找到源头了,我们贴出一部分代码来分析一下:
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_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
对于外部中断0,即irqno=IRQ_EINT0,调用set_irq_handler(irqno, handle_edge_irq)-->_set_irq_handler(irq, handle, 0, NULL);-->desc->handle_irq = handle;如此就就把函数handle_edge_irq赋值给了desc->handle_irq ,即desc[16]->handle_irq =handle_edge_irq.那么回到注释1的代码desc->handle_irq(irq, desc);处,我们就知道,这行代码就可以写成handle_edge_irq(irq,desc);handle_edge_irq的源代码在kernel/irq/chip.c中,源码在 注释1-2-1中贴出来,这里只给出其原型:handle_edge_irq(unsigned int irq, struct irq_desc *desc);
对于set_irq_chip也一样,set_irq_chip(irqno, &s3c_irq_eint0t4);--> set_irq_chip(unsigned int irq, struct irq_chip *chip)(在kernel/irq/chip.c中)-->desc->chip = chip;最终desc->chip =s3c_irq_eint0t4;
注释1-1-1:
struct irq_desc {
unsigned int
irq;
irq_flow_handler_t
handle_irq;
struct irq_chip
*chip;
struct msi_desc
*msi_desc;
void
*handler_data;
void
*chip_data;
struct irqaction
*action;
/* IRQ action list */
unsigned int
status;
/* IRQ status */
unsigned int
depth;
/* nested irq disables */
unsigned int
wake_depth;
/* nested wake enables */
unsigned int
irq_count;
/* For detecting broken IRQs */
unsigned int
irqs_unhandled;
unsigned long
last_unhandled;
/* Aging timer for unhandled count */
spinlock_t
lock;
#ifdef CONFIG_SMP
cpumask_t
affinity;
unsigned int
cpu;
#endif
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_t
pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry
*dir;
#endif
const char
*name;
} ____cacheline_internodealigned_in_smp;
注释1-2-1:
void
handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
spin_lock(&desc->lock);
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
/*
* If we're currently running this IRQ, or its disabled,
* we shouldn't process the IRQ. Mark it pending, handle
* the necessary masking and go out
*/
/*出错处理*/
if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
!desc->action)) {
desc->status |= (IRQ_PENDING | IRQ_MASKED);
mask_ack_irq(desc, irq);
goto out_unlock;
}
kstat_incr_irqs_this_cpu(irq, desc);//中断计数
/* Start handling the irq */
desc->chip->ack(irq);
/* Mark the IRQ currently in progress.*/
desc->status |= IRQ_INPROGRESS;
do {
struct irqaction *action = desc->action;
irqreturn_t action_ret;
if (unlikely(!action)) {
desc->chip->mask(irq);
goto out_unlock;
}
/*
* When another irq arrived while we were handling
* one, we could have masked the irq.
* Renable it, if it was not disabled in meantime.
*/
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);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
spin_lock(&desc->lock);
} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
对于个函数我们主要分析两个地方,这两个地方都用橙色标记了出来,首先我们来分析
desc->chip->ack(irq)
当为外部中断0时,desc->chip =s3c_irq_eint0t4的定义为:
static struct irq_chip s3c_irq_eint0t4 = {
.name
= "s3c-ext0",
.ack
= s3c_irq_ack,
.mask
= s3c_irq_mask,
.unmask
= s3c_irq_unmask,
.set_wake
= s3c_irq_wake,
.set_type
= s3c_irqext_type,
};
其中.ack=s3c_irq_ack的定义又为:
s3c_irq_ack(unsigned int irqno)
{
unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
__raw_writel(bitval, S3C2410_SRCPND);//清中断
__raw_writel(bitval, S3C2410_INTPND);
}
因此
desc->chip->ack(irq);这行代码很简单,就是清中断。
接下来我们再来分析一下
action_ret = handle_IRQ_event(irq, action);其代码如下所示:
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
irqreturn_t ret, retval = IRQ_NONE;
unsigned int status = 0;
if (!(action->flags & IRQF_DISABLED))
local_irq_enable_in_hardirq();
do {
ret = action->handler(irq, action->dev_id);
if (ret == IRQ_HANDLED)
status |= action->flags;
retval |= ret;
action = action->next;
} while (action);
if (status & IRQF_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
local_irq_disable();
return retval;
}
在分析这段代码之前,先让我们来了解一下action。
Irq_desc_t结构体中的成员action指向该中断号对应的irqaction结构体链表。Irqaction结构体定义在include/linux/interrupt.h中,如下:
truct irqaction {
irq_handler_t handler; //中断处理函数,注册时提供
unsigned long flags; //中断标志,注册时提供
cpumask_t mask; //中断掩码
const char *name; //中断名称
void *dev_id; //设备id,本文后面部分介绍中断共享时会详细说明这个参数的作用
struct irqaction *next; //如果有中断共享,则继续执行,
int irq; //中断号,注册时提供
struct proc_dir_entry *dir; //指向IRQn相关的/proc/irq/n目录的描述符
};
irqreturn_t handle_IRQ_event函数所做的就是不断从action链表中去成员,并执行action->handler。那么现在我们想一想,从发生中断开时,经过一系列的过程,最终中断处理函数对应到了handle_edge_irq,而handle_edge_irq所执行的函数时desc->action->handler,我们很自然的就想到,如果要中断处理我们自己写的程序,就要让handler指向我们所写的函数。关于如何让我们写的程序与中断服务程序关联起来,我们在(6)里面仔细分析。
(6)下面我们来分析一个比较靠近上层的中断注册函数——request_irq(),它定义在kernel/irq/manage.c中,源代码如下:
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
#ifdef CONFIG_LOCKDEP
/*
* Lockdep wants atomic interrupt handlers:
*/
irqflags |= IRQF_DISABLED;
#endif
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq);//利用irq构造出了desc
if (!desc)
return -EINVAL;
if (desc->status & IRQ_NOREQUEST)
return -EINVAL;
if (!handler)
return -EINVAL;
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
if (!action)
return -ENOMEM;
action->handler = handler;//一下几句话很重要,不过应该不用解释了
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
retval = __setup_irq(irq, desc, action);//然后主要工作在这里做了,由于本人水平太差,对这个函数是在分析不出,所以只将这个函数 //所做的工作写下了,具体分析就不做了,如注释1所示。
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ
if (irqflags & IRQF_SHARED) {
/*
* It's a shared IRQ -- the driver ought to be prepared for it
* to happen immediately, so let's make sure....
* We disable the irq to make sure that a 'real' IRQ doesn't
* run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
注释1:在注册中断号为irq的中断服务程序时,系统会根据注册参数封装相应的irqaction结构体。并把中断号为irq的irqaction结构体写入irq_desc [irq]->action。这样就把设备的中断请求号与该设备的中断服务例程irqaction联系在一起了。样当CPU接收到中断请求后,就可以根据中断号通过irq_desc []找到该设备的中断服务程序。
总结:我们现在在来总结一下我们所分析的,这一次我们从request_irq来分析,首先在注册中断的时候根据irq构造出了irq_desc [irq],然后将根据handler,flags等参数将action填充,之后将action填入irq_desc[irq],构造出irq_desc [irq]->action,这样中断与其对应的处理函数的关系就对应起来了。然后从头分析,从发生中断开时,经过一系列的过程,最终中断处理函数对应到了handle_edge_irq,而handle_edge_irq所执行的函数时desc->action->handler,这样就找到了相应的处理函数。
分析完毕,本人也知道这篇博客分析的很混乱,一开始还信心满满,但到后来就分析不下去了,不过终究还是咬着牙分析完了,希望看到这篇日志的人不要骂我