1.uboot启动kernel入口函数是start_kernel()
start_kernel()
--->early_trap_init()
--->unsigned long vectors = CONFIG_VECTORS_BASE;
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); //初始化阶段完成
在arch/arm/configs/s3c2410_defconfig 中定义 CONFIG_VECTORS_BASE=0xffff0000
__vectors_start和__stubs_start处的代码在内核初始化过程中从原来的内核代码段拷贝到了0xffff0000和0xffff0200
2.当中断产生直接跳转到中断跳转的一行
__vectors_start定义如下:
arch/arm/kernel/entry-armv.S
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start。
.globl __vectors_start
__vectors_start:
swi SYS_ERROR0
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset @<------中断发生后跳转到此处
b vector_fiq + stubs_offset
.globl __vectors_end
__vectors_start和__stubs_start搬迁后的地址分别是0xffff0000和0xffff0200,相差0x200,搬迁前是stubs_start - vectors_start,
注意__vectors_start和__stubs_start均为搬迁前地址。搬迁前的跳转只需要跳转长度为vector_irq,但由于映射搬迁后两个地址对应的新地址
又偏移了stubs_offset[0x200 - (stubs_start - vectors_start)],所以欲使搬迁后仍然能够跳转到相应位置,需要在vector_irq的基础上再跳转
stubs_offset,也就是跳转vector_irq+stubs_offset。
3 vector_irq 为宏定义具体如下: arch/arm/kernel/entry-armv.S
.macro vector_stub, name, mode, correction=0
.align 5
@ 发生中断后处理器响应中断的伪代码如下
@ 1. lr_<exception> = return link
@ 2. spsr_<exception> = cpsr
@ 3. cpsr[4:0] = exception mode number
@ 4. cpsr[5] = 0 @ if run in ARM
@ 5. if (fiq or reset) then cpsr[6] = 1 @ disable F
@ 6. cpsr[7] = 1 @ disable I
@ 7. pc = exception vector address
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]
movs pc, lr @ branch to handler in SVC mode
.endm
.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4
.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)
(省略后12个__irq_invalid)
带入具体参数:
vector_irq:
@ 根据进入irq中断前处理器所处的模式(IRQ_MODE),将紧接着其下边的16个地址池中对应位置的处理向量,
@ 取出来赋给PC,完成进一步跳转。这里我们选择让程序跳转到__irq_usr代码段继续执行
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
mrs r0, cpsr
eor r0, r0, #(IRQ_MODE ^ SVC_MODE)
msr spsr_cxsf, r0
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]
movs pc, lr @ branch to handler in SVC mode
.endm
@ 16个地址池中对应位置的处理向量
.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)
(省略后12个__irq_invalid)
4 然后__irq_usr 如下:
#暂时不执行的代码没有贴出来
__irq_usr:
usr_entry @将usr模式下的寄存器、中断返回地址保存到堆栈中 @save register
get_thread_info tsk @获取当前进程的进程描述符中的成员变量thread_info的地址,并将该地址保存到寄存器tsk等于r9
irq_handler @中断处理
mov why, #0
b ret_to_user @中断处理完成,返回中断产生的位置
5 irq_handler @中断处理函数的主函数
/*
* Interrupt handling. Preserves r7, r8, r9
*/
.macro irq_handler
get_irqnr_preamble r5, lr
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
@ routine called with r0 = irq number, r1 = struct pt_regs *
adrne lr, 1b
bne asm_do_IRQ @接下来执行的函数是用C编写的 asm_do_IRQ是中断的C语言总入口函数
6 asm_do_IRQ
带的第一个参数就是对应的中断号
中断号对应着发生了什么样的中断事件,就可以采取什么样的中断服务程序进行处理。
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) //
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc *desc = irq_desc + irq; //中断数组第irq个中断 也就是irq_desc[irq]
/*Some hardware gives randomly wrong interrupts. Ratherthan crashing, do something sensible.*/
if (irq >= NR_IRQS)
desc = &bad_irq_desc;
irq_enter(); //进中断
desc_handle_irq(irq, desc);
/* AT91 specific workaround */
irq_finish(irq);
irq_exit(); //退出中断
set_irq_regs(old_regs);
}
7.
desc_handle_irq(irq,desc)
----> desc->handle_irq(irq, desc); //此处为回调函数
对应注册函数为:
s3c24xx_init_irq() (arch/arm/plat-s3c24xx/irq.c)
---->
set_irq_handler(irqno,
handle_edge_irq)
---->
__
set_irq_handler
(irq, handle, 0, NULL)
----> desc->handle_irq = handle;
---->
set_irq_chip(irqno, &
s3c_irq_chip)
----> desc->chip = chip;
也就是说,在初始化irq中断时,desc->handle_irq和desc->chip已经分别指向了
handle_edge_irq和
s3c_irq_chip。
中断号为 EINT4t7 的中断触发后直接转到了相应的函数。
另外 通过 set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7)
使s3c_irq_demux_extint4t7()对应
__
set_irq_handler中的handle。
8 handle_edge_irq(irq,desc) //清除中断,禁止中断,重新开启中断在此
void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
......
kstat_cpu(cpu).irqs[irq]++; //对应中断次数加1
/* Start handling the irq */
desc->chip->ack(irq);
......
do {
struct irqaction *action = desc->action;
irqreturn_t action_ret;
......
desc->status &= ~IRQ_PENDING;
action_ret = handle_IRQ_event(irq, action);
......
} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
......
}
static struct irq_chip s3c_irq_chip = {
.name = "s3c",
.ack = s3c_irq_ack,
.mask = s3c_irq_mask,
.unmask = s3c_irq_unmask,
.set_wake = s3c_irq_wake
};
由第7步知道
desc->chip
对应的是结构体
s3c_irq_chip
desc->chip->ack 指向函数
s3c_irq_ack()
9 s3c_irq_ack() 对应寄存器值的修改
static inline void s3c_irq_ack(unsigned int irqno)
{
unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
__raw_writel(bitval, S3C2410_SRCPND);
__raw_writel(bitval, S3C2410_INTPND);
}
10
handle_IRQ_event
(irq, action) 这个函数就是将中断号为irq的中断响应函数desc->action放入到对应的中断链表中
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
irqreturn_t ret, retval = IRQ_NONE;
unsigned int status = 0;
handle_dynamic_tick(action);
if (!(action->flags & IRQF_DISABLED)) //当调用action函数时中断不接收
local_irq_enable_in_hardirq(); //local_irq_enable() 和ocal_irq_disable()对应
do {
ret = action->handler(irq, action->dev_id); /* 调用action->handler,找对应注册函数。即request_irq 时注册的handler 函数 */
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;
}
11 action->handler 的注册函数 搜索结构体 irq_handler_t 找到
request_irq
request_irq( irq, handler,
irqflags, *devname, *dev_id) //注册中断处理函数
对应 free_irq(irq, *dev_id) //释放中断处理函数
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001 //上升沿
#define IRQF_TRIGGER_FALLING 0x00000002 //下降沿
#define IRQF_TRIGGER_HIGH 0x00000004 //高电平
#define IRQF_TRIGGER_LOW 0x00000008 //低电平
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_DISABLED - keep irqs disabled when calling the action handler
* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for performance reasons)
*/
#define IRQF_DISABLED 0x00000020
#define IRQF_SAMPLE_RANDOM 0x00000040
#define IRQF_SHARED 0x00000080
#define IRQF_PROBE_SHARED 0x00000100
#define IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
int retval;
......
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); //分配要保存的中断处理函数空间
action->handler = handler; //对应handle的注册
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id; //最主要是用于注销对应中断处理函数
select_smp_affinity(irq); //中断分配到不同的CPU
#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 do this before actually registering it, to make sure that
* a 'real' IRQ doesn't run in parallel with our fake
*/
if (irqflags & IRQF_DISABLED) {
unsigned long flags;
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
} else
handler(irq, dev_id);
}
#endif
retval = setup_irq(irq, action);
if (retval)
kfree(action); //对应上文kmalloc
return retval;
}
12 setup_irq (irq,action) Internal function to register an irqaction
int setup_irq(unsigned int irq, struct irqaction *new)
{
struct irq_desc *desc = irq_desc + irq;
struct irqaction *old, **p;
const char *old_name = NULL;
unsigned long flags;
int shared = 0;
/*The following block of code has to be executed atomically*/
spin_lock_irqsave(&desc->lock, flags);
p = &desc->action;
old = *p;
if (old) // shared = 1;
{
/*
* Can't share interrupts unless both agree to and are
* the same type (level, edge, polarity). So both flag
* fields must have IRQF_SHARED set and the bits which
* set the trigger type must match.
*/
if (!((old->flags & new->flags) & IRQF_SHARED) ||
((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
old_name = old->name;
goto mismatch;
}
#if defined(CONFIG_IRQ_PER_CPU)
/* All handlers must agree on per-cpuness */
if ((old->flags & IRQF_PERCPU) !=
(new->flags & IRQF_PERCPU))
goto mismatch;
#endif
/* add new interrupt at end of irq queue */
do {
p = &old->next; //中断添加到结构体irqaction
old = *p;
} while (old);
shared = 1;
}
*p = new;
/* Exclude IRQ from balancing */
if (new->flags & IRQF_NOBALANCING)
desc->status |= IRQ_NO_BALANCING;
if (!shared)
{
irq_chip_set_defaults(desc->chip); //按默认的写入
#if defined(CONFIG_IRQ_PER_CPU)
if (new->flags & IRQF_PERCPU)
desc->status |= IRQ_PER_CPU;
#endif
/* Setup the type (level, edge polarity) if configured: */
if (new->flags & IRQF_TRIGGER_MASK)
{
if (desc->chip && desc->chip->set_type)
desc->chip->set_type(irq, new->flags & IRQF_TRIGGER_MASK); //设置type
else
/* IRQF_TRIGGER_* but the PIC does not support multiple flow-types?*/
printk(KERN_WARNING "No IRQF_TRIGGER set_type" "function for IRQ %d (%s)\n", irq, desc->chip ? desc->chip->name :"unknown");
}
else
compat_irq_chip_set_default_handler(desc);
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);
if (!(desc->status & IRQ_NOAUTOEN))
{
desc->depth = 0;
desc->status &= ~IRQ_DISABLED;
if (desc->chip->startup)
desc->chip->startup(irq); //使能或者启动
else
desc->chip->enable(irq);
}
else
desc->depth = 1; /* Undo nested disables: */
}
/* Reset broken irq detection when installing new handler */
desc->irq_count = 0;
desc->irqs_unhandled = 0;
spin_unlock_irqrestore(&desc->lock, flags);
new->irq = irq;
register_irq_proc(irq);
new->dir = NULL;
register_handler_proc(irq, new); //创建文件mkdir
return 0;
mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
if (!(new->flags & IRQF_PROBE_SHARED))
{
printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
if (old_name)
printk(KERN_ERR "current handler: %s\n", old_name);
dump_stack();
}
#endif
spin_unlock_irqrestore(&desc->lock, flags);
return -EBUSY;
}
中断体系结构的初始化,就是构造irq_desc[NR_IRQS]这个数据结构;用户注册中断就是构造action链表;用户卸载中断就是从action链表中去除对应的项
![](https://img-blog.csdnimg.cn/20201211095225545.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l5eWxqdw==,size_16,color_FFFFFF,t_70)