ARM Linux 中断向量表建立流程
interrupts. Rather
* than crashing, do something sensible.
*/
if (irq >= NR_IRQS)
goto bad_irq;
desc = irq_desc + irq;
spin_lock(&irq_controller_lock);
desc->mask_ack(irq);
/*----------------------------------
void __init init_IRQ(void)
{
extern void init_dma(void);
int irq;
for (irq = 0; irq < NR_IRQS; irq++) {
irq_desc[irq].probe_ok = 0;
irq_desc[irq].valid = 0;
irq_desc[irq].noautoenable = 0;
irq_desc[irq].mask_ack = dummy_mask_unmask_irq;
irq_desc[irq].mask = dummy_mask_unmask_irq;
irq_desc[irq].unmask = dummy_mask_unmask_irq;
}
init_arch_irq();
init_dma();
}
init_arch_irq(); init_dma();最后被指向/mach-s3c2410中的s3c2410_init_irq(void)和s3c2410_init_dma(void), desc->mask_ack(irq);将在那里被填充。
--------------------------------*/
spin_unlock(&irq_controller_lock);
cpu = smp_processor_id(); //#define smp_processor_id() 0
irq_enter(cpu, irq);
kstat.irqs[cpu][irq]++;
desc->triggered = 1;
/* Return with this interrupt masked if no action */
action = desc->action;
/* 这个结构由driver通过request_irq()挂入,包括了具体的中断处理程序入口和flags.一个中断的irq_desc下面可能会挂几个action(一个action队列)来实现中断的复用。也就是说几个driver可以公用一个中断号。*/
if (action) {
int status = 0;
if (desc->nomask) {
spin_lock(&irq_controller_lock);
desc->unmask(irq);
spin_unlock(&irq_controller_lock);
}
if (!(action->flags & SA_INTERRUPT))
/* SA_INTERRUPT Disable local interrupts while processing
SA_SHIRQ is shared
这个flag可以一直追到request irq的action->flags = irq_flags(传递参数);
*/
__sti();//清除cpsr的I_bit,开中断。
/*如果在上面的nomask处判断后,没有执行unmask动作,那么这里的__sti只是允许不同中断通道(即icip上不同的位)上的嵌套*/
do {
status |= action->flags;
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
/*值得注意的是:整个action队列都会被调用,所以在driver里要判定是否是属于自己的中断*/
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
__cli();
if (!desc->nomask && desc->enabled) {
spin_lock(&irq_controller_lock);
desc->unmask(irq);
spin_unlock(&irq_controller_lock);
}
}
unsigned int fixup_irq(int irq) {
unsigned int ret;
unsigned long sub_mask, ext_mask;
if (irq == OS_TIMER)
return irq;
switch (irq) {
case IRQ_UART0:
sub_mask = SUBSRCPND & ~INTSUBMSK;
ret = get_subIRQ(sub_mask, 0, 2, irq);
break;
case IRQ_UART1:
sub_mask = SUBSRCPND & ~INTSUBMSK;
ret = get_subIRQ(sub_mask, 3, 5, irq);
break;
case IRQ_UART2:
sub_mask = SUBSRCPND & ~INTSUBMSK;
ret = get_subIRQ(sub_mask, 6, 8, irq);
break;
case IRQ_ADCTC:
sub_mask = SUBSRCPND & ~INTSUBMSK;
ret = get_subIRQ(sub_mask, 9, 10, irq);
break;
case IRQ_EINT4_7:
ext_mask = EINTPEND & ~EINTMASK;
ret = get_extIRQ(ext_mask, 4, 7, irq);
break;
case IRQ_EINT8_23:
ext_mask = EINTPEND & ~EINTMASK;
ret = get_extIRQ(ext_mask, 8, 23, irq);
break;
default:
ret = irq;
}
这个函数一看就知道是找子中断号的,
inline unsigned int get_subIRQ(int irq, int begin, int end, int fail_irq) {
int i;
for(i=begin; i <= end; i++) {
if (irq & (1 < i))
return (EXT_IRQ_OFFSET + i);
}
return fail_irq;
}
inline unsigned int get_extIRQ(int irq, int begin, int end, int fail_irq) {
int i;
for(i=begin; i <= end; i++) {
if (irq & (1 < i))
return (NORMAL_IRQ_OFFSET - 4 + i);
}
return fail_irq;
}
#define NORMAL_IRQ_OFFSET 32
#define EXT_IRQ_OFFSET (20 +NORMAL_IRQ_OFFSET)
=========================================
申请中断:
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irq_flags, const char * devname, void *dev_id)
{
unsigned long retval;
struct irqaction *action;
if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler ||
(irq_flags & SA_SHIRQ && !dev_id))
return -EINVAL;
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->flags = irq_flags;
action->mask = 0;
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
retval = setup_arm_irq(irq, action); /* 把这个action挂到对应irq的action链表中*/
if (retval)
kfree(action);
return retval;
}
int setup_arm_irq(int irq, struct irqaction * new)
{
int shared = 0;
struct irqaction *old, **p; /*这里的**p 用的太妙了*/
unsigned long flags;
struct irqdesc *desc;
/*
* Some drivers like serial.c use request_irq() heavily,
* so we have to be careful not to interfere with a
* running system.
*/
if (new->flags & SA_SAMPLE_RANDOM) {
/*
* This function might sleep, we want to call it first,
* outside of the atomic block.
* Yes, this might clear the entropy pool if the wrong
* driver is attempted to be loaded, without actually
* installing a new handler, but is this really a problem,
* only the sysadmin is able to do this.
*/
rand_initialize_irq(irq); /*这个函数的作用是利用中断的随机性来产生随机数列*/
}
/*
* The following block of code has to be executed atomically
*/
desc = irq_desc + irq;
spin_lock_irqsave(&irq_controller_lock, flags);
p = &desc->action;
if ((old = *p) != NULL) {
注意/* Cant share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ)) {
spin_unlock_irqrestore(&irq_controller_lock, flags);
return -EBUSY;
}
/* add new interrupt at end of irq queue */
do {
p = &old->next;
old = *p;
} while (old);/*当没有下一个irqaction链表元素时,next就位null*/
shared = 1;
}
*p = new;
if (!shared) {
desc->nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0;
desc->probing = 0;
if (!desc->noautoenable) {
desc->enabled = 1;
desc->unmask(irq);
}
}
spin_unlock_irqrestore(&irq_controller_lock, flags);
return 0;
}
四.ARM Linux中断处理过程分析(3)
在之前的文章中,我分析了进入IRQ之前处理器模式为SVC的情况,在本篇文章中,将要讨论的是进入IRQ之前处理器模式为USR的情形。
843 __irq_usr: sub sp, sp, #S_FRAME_SIZE
844 stmia sp, {r0 - r12} @ save r0 - r12
845 ldr r4, .LCirq
846 add r8, sp, #S_PC
847 ldmia r4, {r5 - r7} @ get saved PC, SPSR
848 stmia r8, {r5 - r7} @ save pc, psr, old_r0
849 stmdb r8, {sp, lr}^
850 alignment_trap r4, r7, __temp_irq
851 zero_fp
852 1: get_irqnr_and_base r0, r6, r5, lr
853 movne r1, sp
854 adrsvc ne, lr, 1b
855 @
856 @ routine called with r0 = irq number, r1 = struct pt_regs *
857 @
858 bne asm_do_IRQ
859 mov why, #0
860 get_current_task tsk
861 b ret_to_user
__irq_usr关于中断处理的过程大体与__irq_svc是一样的,这里我们重点要分析中断处理返回时的不同。
研读过linux内核进程调度的朋友都知道,进程的调度可以自愿的方式随时进行(内核里:schedule、schedule_timeout;用户空间:pause、nanosleep),还可以非自愿的发生,即强制地发生在每次系统调用返回的前夕,以及每次从中断或异常处理返回到用户空间的前夕(只有在用户空间发生的中断或异常才会引起调度)。可参阅毛德操的《Linux内核源代码情景分析》上册的第4章关于进程调度的相关地方。
那我们就来看一下,__irq_usr在返回到usr模式(用户空间)前夕是如何强制进行进程调度的。
Line860,这是中断处理返回后,获取当前进程的task_struct指针,get_current_task是一个宏,它定义于arch/arm/kernel/entry-header.S中:
.macro get_current_task, rd
mov rd, sp, lsr #13
mov rd, rd, lsl #13
.endm
该宏是先将sp的值右移13位,再左移13位,把结果返回给参数,其实也就是只保留sp值的高19位,这代表着把
ARMLinux中断向量 相关文章:
- ARM Linux中断向量表搬移设计过程(11-09)
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)