注:图片取自:http://blog.chinaunix.net/uid-23769728-id-3079164.html
先简单介绍一下硬件中断的软件流程:handle_int ->plat_irq_dispatch->malta_hw0_irqdispatch->do_IRQ(irq)->generic_handle_irq -> __do_IRQ() -> irq_exit
1、中断初始化:
1、main.c中的start_kernel函数中,trap_init()函数,用来初始化handle_init函数,使其指向中断响应。
void __init trap_init(void){
.......
set_except_vector(0, handle_int); //中断异常
.........
}
handle_init函数到下面的函数响应中再进行分析。
2、init_IRQ();//同样是在start_kernel函数中int_IRQ调用函数arch_init_irq,用来初始化中断全局参数irq_desc[irq]。
for:
void __init mips_cpu_irq_init(void)
{
int irq_base = MIPS_CPU_IRQ_BASE;
int i;
/* Mask interrupts. */
clear_c0_status(ST0_IM);
clear_c0_cause(CAUSEF_IP);
/*
* Only MT is using the software interrupts currently, so we just
* leave them uninitialized for other processors.
*/
if (cpu_has_mipsmt)
for (i = irq_base; i < irq_base + 2; i++)
set_irq_chip(i, &mips_mt_cpu_irq_controller);
for (i = irq_base + 2; i < irq_base + 8; i++)
set_irq_chip_and_handler(i, &mips_cpu_irq_controller,
handle_level_irq);
}
这个是mips_cpu的中断,也可以根据自己的CPU注册中断。
for:
static void init_my_irqs(void)
{
int i;
for(i = 0;i<32;i++){
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = 0;
irq_desc[i].depth = 1;
irq_desc[i].chip = &my_irq_type;
}
/* enable the interrupt line */
IntEnable();
}
至此硬件中断初始化结束。
2、中断注册:
1、request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);这个用来注册中断
irq:中断号
handler:中断函数句柄
flag:中断处理属性flag定义在include/linux/interrupt.h
/*
* 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 //独享中断号相当于早期的SA_INTERRUPT
#define IRQF_SAMPLE_RANDOM 0x00000040
#define IRQF_SHARED 0x00000080 //共享中断号相当于早期的SA_SHIRE
#define IRQF_PROBE_SHARED 0x00000100
#define IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
name:中断名字
dev:设备,这个跟共享中断有关系。独享的话为NULLfor:
if(request_irq(OPL_UART1_IRQ, &uartInterrupt,IRQF_DISABLED, "uart1", NULL)) {
unregister_chrdev(UART1_MAJOR, UFILE_NAME);
return -1;
}
注册串口1中断,其中中断处理函数为uartInterrupt,独享中断。
2、int setup_irq(unsigned int irq, struct irqaction *new)//这是另外一个注册中断函数
其实在request_irq中也调用了这个函数,在request_irq中其实就多了一个action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);以及一些安全判断。
我们注意到timer中断使用setup_irq(mips_cpu_timer_irq, irq);那是因为在调用这个函数的时候,还没发调用kmalloc函数,否则会导致内核挂起,kmalloc函数需要mem_init();kmem_cache_init();这两个函数被调用后才行。在此函数的最后几行,会注册此中断相对应的/proc/irq/(num)/(name)的虚拟文件系统,方便在应用层观察中断信息
3、中断响应:
好了,现在重头戏来了,中断响应。
handle_int ->plat_irq_dispatch->malta_hw0_irqdispatch->do_IRQ(irq)->generic_handle_irq -> __do_IRQ() ->hanle_IRQ_event-> irq_exit
1、handle_init
NESTED(handle_int, PT_SIZE, sp)
#ifdef CONFIG_TRACE_IRQFLAGS
/*
* Check to see if the interrupted code has just disabled
* interrupts and ignore this interrupt for now if so.
*
* local_irq_disable() disables interrupts and then calls
* trace_hardirqs_off() to track the state. If an interrupt is taken
* after interrupts are disabled but before the state is updated
* it will appear to restore_all that it is incorrectly returning with
* interrupts disabled
*/
.set push
.set noat
mfc0 k0, CP0_STATUS
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
and k0, ST0_IEP
bnez k0, 1f
mfc0 k0, EP0_EPC
.set noreorder
j k0
rfe
#else
and k0, ST0_IE
bnez k0, 1f
eret
#endif
1:
.set pop
#endif
SAVE_ALL
CLI
TRACE_IRQS_OFF
LONG_L s0, TI_REGS($28)
LONG_S sp, TI_REGS($28)
PTR_LA ra, ret_from_irq
j plat_irq_dispatch //跳入plat_irq_patch
END(handle_int)
__INIT
plat_irq_patch
asmlinkage void plat_irq_dispatch()
{
int i;
int pending = read_c0_status() & read_c0_cause();
//printk("%s, %s\n", __FILE__, __FUNCTION__);
if(pending & CAUSEF_IP2) /*IP2:处理芯片内部中断*/
malta_hw0_irqdispatch();
else {
for(i = 7; i>0; i--)
if(pending & (0x100<<i)) do_IRQ(i);
}
spurious_interrupt();
}
在malta-hw0_irqdispatch函数中根据中断状态寄存器来判断irq号,进入do_IRQ(irq);
#define do_IRQ(irq) \
do { \
irq_enter(); \
__DO_IRQ_SMTC_HOOK(irq); \
generic_handle_irq(irq); \
irq_exit(); \
} while (0)
generic_handle_irq(irq)为处理中断上半部分的顶部中断,进入__do_IRQ(irq)
fastcall unsigned int __do_IRQ(unsigned int irq)
{
kstat_this_cpu.irqs[irq]++; //这个变量在/proc/interrupt文件中显示成为进入中断次数统计
...........
for (;;) {
irqreturn_t action_ret;
spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, action); //中断处理函数
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
spin_lock(&desc->lock);
if (likely(!(desc->status & IRQ_PENDING)))
break;
desc->status &= ~IRQ_PENDING;
}
..........
}
接下来进入hanle_IRQ_event
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);
//printk("%s,%s\n",__FILE__,__FUNCTION__,__LINE__);
if (!(action->flags & IRQF_DISABLED))
local_irq_enable_in_hardirq(); //如果为共享中断,则开启全局中断
do {
//printk("%s,%s,%s,irq=%d,handler=0x%x\n",__FILE__,__FUNCTION__,__LINE__, action->name,
// action->irq);
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(); //在返回__do_IRQ前必须关中断,前面共享中断有可能开启全局中断,
//且action->flag有可能在中途改变,故这里强行关会比较安全
return retval;
}
至此上半部分中断已经处理完毕irq_exit为下半部分中断即为软中断,这里不多加说明了,以后有时间在分析。
注:由于水平有限,可能有些地方有错误,或不清楚,还望多多见谅、指出。