1.中断描述符
对于每一条中断线都由一个irq_desc结构来描述。
//在include/linux/irq.h中
struct irq_desc {
unsigned int irq;//中断号
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu;
#endif
/*
在kernel/irq/chip.c中实现了5个函数:handle_simple_irq(),handle_level_irq(),
handle_edge_irq(),handle_fasteoi_irq()以及handle_percpu_irq()。 handle_irq指针可
以指向这5个函数中的一个, 选择一种中断事件处理策略, 这是通过函数set_irq_handler()
完成的
*/
irq_flow_handler_t handle_irq;//上层中断处理函数,
struct irq_chip *chip;//底层硬件操作
struct msi_desc *msi_desc;
void *handler_data;//附加参数,用于handle_irq
void *chip_data;//平台相关附加参数,用于chip
struct irqaction *action; /* IRQ action list *///中断服务例程链表
unsigned int status; /* IRQ status *///中断当前的状态
//中断关闭打开层数调用一次disable_irq( ) ,depth加1;调用一次enable_irq( )该值减1,
//如果depth等于0就开启这条中断线,如果depth大于0就关闭中断线。
unsigned int depth;
unsigned int wake_depth; ////* 唤醒次数 */
unsigned int irq_count; /* 发生的中断次数 */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
unsigned int 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
struct proc_dir_entry *dir;///proc/irq/ 入口
#endif
const char *name;///proc/interrupts 中显示的中断名称
} ____cacheline_internodealigned_in_smp;
/*在kernel/irq/handle.c中有个全局irq_desc数组,描述了系统中所有的中断线:
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
NR_IRQS为最大中断数对于s3c2410芯片,在文件arch/arm/mach-s3c2410/include/mach/irqs.h中定义如下:
#ifdef CONFIG_CPU_S3C2443
#define NR_IRQS (IRQ_S3C2443_AC97+1)
#else
#define NR_IRQS (IRQ_S3C2440_AC97+1)//每一个中断源都对应一个irq_desc结构体。
#endif
*/
2. 中断硬件操作函数集
//在include/linux/irq.h中定义
//该结构体中各函数在文件linux/arch/arm/plat-s3c24xx/irq.c中实现
struct irq_chip {
const char *name; //用于 /proc/interrupts
unsigned int (*startup)(unsigned int irq); //默认为 enable 如果为NULL
void (*shutdown)(unsigned int irq); //默认为 disable 如果为NULL
void (*enable)(unsigned int irq); //允许中断,默认为 unmask 如果为NULL
void (*disable)(unsigned int irq); //禁止中断,默认为 mask 如果为 NULL
void (*ack)(unsigned int irq); //响应一个中断,清除中断标志
void (*mask)(unsigned int irq); //mask 一个中断源,通常是关闭中断
void (*mask_ack)(unsigned int irq); //响应并 mask 中断源
void (*unmask)(unsigned int irq); //unmask 中断源
void (*eoi)(unsigned int irq);
void (*end)(unsigned int irq);
void (*set_affinity)(unsigned int irq,
const struct cpumask *dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type); //设置中断触发方式 IRQ_TYPE_LEVEL
int (*set_wake)(unsigned int irq, unsigned int on);
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char *typename;
};
3.中断处理例程描述符
//在include/linux/interrupt.h中
struct irqaction {
irq_handler_t handler; /* 具体的中断处理程序 */
unsigned long flags; //用一组标志描述中断线与 I/O 设备之间的关系。
cpumask_t mask;
const char *name; /* 名称,会显示在/proc/interreupts中 */
void *dev_id; /* 设备ID,用于区分共享一条中断线的多个处理程序 ,以便从共享中断线的诸多中断处理程序中删除指定的那一个*/
struct irqaction *next; /* 指向下一个irq_action结构 */
int irq; /* 中断通道号 */
struct proc_dir_entry *dir; /* procfs目录 */
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};
这三个结构体间的关系表示如下
比如此时外部中断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; /* ignore lower irqs */
/* we may as well handle all the pending IRQs here */
while (eintpnd) {
irq = __ffs(eintpnd); //计算该中断在外部中断寄存器EINTPEND中的偏移量
eintpnd &= ~(1<
irq += (IRQ_EINT4 - 4); //根据这个偏移量重新计算中断号
generic_handle_irq(irq); //根据重新计算的中断号获取对应的结构体irq_desc,并调用它的上层中断处理函数。
}
}
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); //直接调用上层中断处理函数
#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不太一样的是,该函数多了一个循环。即如果在本次中断
//的处理过程中该中断线上又有中断产生,则再次执行该中断线上的处理例程
/*
以下是5个常用的中断线状态。
#define IRQ_INPROGRESS 1 /* 正在执行这个 IRQ 的一个处理程序 */
#define IRQ_DISABLED 2 /* 由设备驱动程序已经禁用了这条 IRQ 中断线 */
#define IRQ_PENDING 4 /* 一个 IRQ 已经出现在中断线上,且被应答,但还没有
为它提供服务 */
#define IRQ_REPLAY 8 /* 当 Linux 重新发送一个已被删除的 IRQ 时 */
#define IRQ_WAITING 32 /* 当对硬件设备进行探测时,设置这个状态以标记正在被
测试的 irq */
*/
} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
desc->status &= ~IRQ_INPROGRESS;
out_unlock:
spin_unlock(&desc->lock);
}
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』