一,认识几个重要结构体:
1.中断描述符
对于每一条中断线都由一个irq_desc结构来描述。
//在include/linux/irq.h中
struct irq_desc {
unsignedint irq;//中断号
struct timer_rand_state*timer_rand_state;
unsignedint *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
structirq_2_iommu *irq_2_iommu;
#endif
irq_flow_handler_t handle_irq;//上层中断处理函数,
structirq_chip *chip;//底层硬件操作
structmsi_desc *msi_desc;
void *handler_data;//附加参数,用于handle_irq
void *chip_data;//平台相关附加参数,用于chip
structirqaction *action; //中断服务例程链表
unsignedint status; //中断当前的状态
//中断关闭打开层数调用一次disable_irq( ),depth加1;调用一次enable_irq( )该值减1,
//如果depth等于0就开启这条中断线,如果depth大于0就关闭中断线。
unsignedint depth;
unsignedint wake_depth; ////* 唤醒次数*/
unsignedint irq_count; /* 发生的中断次数*/
unsignedlong last_unhandled;
unsignedint irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
unsignedint cpu;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
structproc_dir_entry *dir;///proc/irq/ 入口
#endif
constchar *name;///proc/interrupts中显示的中断名称
} ____cacheline_internodealigned_in_smp;
2. 中断硬件操作函数集
//在include/linux/irq.h中定义
//该结构体中各函数在文件linux/arch/arm/plat-s3c24xx/irq.c中实现
struct irq_chip {
constchar *name; //用于 /proc/interrupts
unsignedint (*startup)(unsigned intirq); //默认为 enable 如果为NULL
void (*shutdown)(unsignedint irq); //默认为 disable 如果为NULL
void (*enable)(unsignedint irq); //允许中断,默认为 unmask 如果为NULL
void (*disable)(unsignedint irq); //禁止中断,默认为 mask 如果为NULL
void (*ack)(unsignedint irq); //响应一个中断,清除中断标志
void (*mask)(unsignedint irq); //mask 一个中断源,通常是关闭中断
void (*mask_ack)(unsignedint irq); //响应并 mask 中断源
void (*unmask)(unsignedint irq); //unmask 中断源
void (*eoi)(unsignedint irq);
void (*end)(unsignedint irq);
void (*set_affinity)(unsignedint irq,
conststruct cpumask *dest);
int (*retrigger)(unsignedint irq);
int (*set_type)(unsignedint irq, unsigned int flow_type); //设置中断触发方式IRQ_TYPE_LEVEL
int (*set_wake)(unsignedint irq, unsigned int on);
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsignedint irq, void *dev_id);
#endif
const char *typename;
};
3.中断处理例程描述符
//在include/linux/interrupt.h中
struct irqaction {
irq_handler_thandler;
unsigned longflags; //用一组标志描述中断线与 I/O 设备之间的关系。
cpumask_t mask;
const char *name;
void*dev_id;
struct irqaction*next;
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};
这三个结构体间的关系表示如下
二,中断初始化过程
中断机制的初始化通过两个函数完成:early_trap_init()和init_IRQ(),在此我们先讨论函数init_IRQ()。
//函数init_IRQ在文件linux/arch/arm/kernel/irq.c中实现。
void __initinit_IRQ(void)
{
int irq;
for (irq = 0; irq< NR_IRQS; irq++)
irq_desc[irq].status |=IRQ_NOREQUEST | IRQ_NOPROBE;
#ifdef CONFIG_SMP
cpumask_setall(bad_irq_desc.affinity);
bad_irq_desc.cpu = smp_processor_id();
#endif
init_arch_irq();
}
//函数s3c24xx_init_irq在文件linux/arch/arm/plat-s3c24xx/irq.c中实现
void __inits3c24xx_init_irq(void)
{
unsigned long pend;
unsigned long last;
int irqno;
int i;
irqdbf("s3c2410_init_irq: clearing interruptstatus flags\n");
last =0;
for (i = 0; i < 4; i++){
pend =__raw_readl(S3C24XX_EINTPEND);
if (pend == 0 || pend ==last)
break;
__raw_writel(pend,S3C24XX_EINTPEND); //清除外部中断寄存器EINTPEND中的请求标志,
printk("irq: clearing pendingext status x\n", (int)pend);
last = pend;
}
last = 0;
。。。。。。
//设置各中断的底层硬件操作函数集desc->chip,中断上层处理函数desc->handle_irq
for (irqno =IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++){
switch (irqno) {
case IRQ_EINT4t7:
case IRQ_EINT8t23:
case IRQ_UART0:
case IRQ_UART1:
case IRQ_UART2:
case IRQ_ADCPARENT:
set_irq_chip(irqno,&s3c_irq_level_chip);
set_irq_handler(irqno,handle_level_irq);
break;
case IRQ_RESERVED6:
case IRQ_RESERVED24:
break;
default:
//irqdbf("registeringirq %d (s3c irq)\n", irqno);
set_irq_chip(irqno,&s3c_irq_chip);
set_irq_handler(irqno,handle_edge_irq);
set_irq_flags(irqno,IRQF_VALID);
}
}
//以下几个中断都有多个中断源,每一个中断源也都有各自的中断号,它们的多个中断源中任意一个产生中断
//该中断都会被触发,而不是直接出发子中断。这几个中断并不处理中断函数,它们的中作是计算子中断的中断号,
//并根据子中断的中断号在数组irq_desc[NR_IRQS]中去找出该中断号对应的irq_desc结构,并调用该结构中的中断处理函数。
set_irq_chained_handler(IRQ_EINT4t7,s3c_irq_demux_extint4t7);
set_irq_chained_handler(IRQ_EINT8t23,s3c_irq_demux_extint8);
set_irq_chained_handler(IRQ_UART0,s3c_irq_demux_uart0);
set_irq_chained_handler(IRQ_UART1,s3c_irq_demux_uart1);
set_irq_chained_handler(IRQ_UART2,s3c_irq_demux_uart2);
set_irq_chained_handler(IRQ_ADCPARENT,s3c_irq_demux_adc);
。。。。。。
for (irqno =IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irqdbf("registering irq %d(extended s3c irq)\n", irqno);
set_irq_chip(irqno,&s3c_irqext_chip); //设置子中断的硬件操作函数集
set_irq_handler(irqno,handle_edge_irq); //设置子中断的上层处理函数
set_irq_flags(irqno,IRQF_VALID);
}
。。。。。。
}
比如此时外部中断10产生了中断,中断号为IRQ_EINT8t23的中断被触发,执行函数s3c_irq_demux_extint8()。
static void
s3c_irq_demux_extint8(unsigned int irq,
struct irq_desc *desc)
{
unsigned long eintpnd =__raw_readl(S3C24XX_EINTPEND);
unsigned long eintmsk =__raw_readl(S3C24XX_EINTMASK);
eintpnd &= ~eintmsk;
eintpnd &=~0xff;
while (eintpnd) {
irq =__ffs(eintpnd); //计算该中断在外部中断寄存器EINTPEND中的偏移量
eintpnd &=~(1<<irq);
irq += (IRQ_EINT4 -4); //根据这个偏移量重新计算中断号
generic_handle_irq(irq); //根据重新计算的中断号获取对应的结构体irq_desc,并调用它的上层中断处理函数。
}
}
static inline void generic_handle_irq_desc(unsigned int irq, structirq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
desc->handle_irq(irq,desc); //直接调用上层中断处理函数
#else
if(likely(desc->handle_irq))
desc->handle_irq(irq,desc);
else
__do_IRQ(irq); //通用中断处理函数,该函数最终调用desc->handle_irq(irq,desc);
#endif
}
上层中断处理函数
上层中断处理函数有5个分别为:handle_simple_irq(),handle_level_irq(),
handle_edge_irq(),handle_fasteoi_irq()以及handle_percpu_irq()。
这几个函数在文件kernel/irq/chip.c中实现。常用的有两个handle_level_irq(),和handle_edge_irq()。
这5个上层中断处理函数都是通过调用函数handle_IRQ_event()来做进一步处理。
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); //执行中断处理函数。
。。。。。。
retval |= ret;
action =action->next;
} while (action); //调用该中断线上的所有例程
if (status &IRQF_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
local_irq_disable();
return retval;
}
void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
struct irqaction *action;
irqreturn_t action_ret;
。。。。。。
desc = irq_remap_to_desc(irq, desc);
。。。。。。
action =desc->action;
action_ret =handle_IRQ_event(irq, action);
。。。。。。
}
void
handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
spin_lock(&desc->lock);
。。。。。。
desc = irq_remap_to_desc(irq,desc);
。。。。。。
desc->status |=IRQ_INPROGRESS;
do {
struct irqaction *action =desc->action;
。。。。。。
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);
//该函数与函数handle_level_irq不太一样的是,该函数多了一个循环。即如果在本次中断
//的处理过程中该中断线上又有中断产生,则再次执行该中断线上的处理例程
#define IRQ_DISABLED 2
#defineIRQ_PENDING 4
#define IRQ_REPLAY 8
#define IRQ_WAITING 32
*/
} while ((desc->status& (IRQ_PENDING | IRQ_DISABLED)) ==IRQ_PENDING);
desc->status &=~IRQ_INPROGRESS;
out_unlock:
spin_unlock(&desc->lock);
}