一 单片机下的中断处理:
①分辨是哪个中断;
②调用处理函数;
③清中断。
下面结合代码查看
#include "s3c24xx.h"
void EINT_Handle()
{
unsigned long oft = INTOFFSET;
unsigned long val;
switch( oft )
{
// 分辨是哪个中断;
// S2被按下
case 0:
{
GPFDAT |= (0x7<<4); // 所有LED熄灭
GPFDAT &= ~(1<<4); // LED1点亮
break;
}
// S3被按下
case 2:
{
GPFDAT |= (0x7<<4); // 所有LED熄灭
GPFDAT &= ~(1<<5); // LED2点亮
break;
}
// K4被按下
case 5:
{
GPFDAT |= (0x7<<4); // 所有LED熄灭
GPFDAT &= ~(1<<6); // LED4点亮
break;
}
default:
break;
}
//清中断
if( oft == 5 )
EINTPEND = (1<<11); // EINT8_23合用IRQ5
SRCPND = 1<<oft;
INTPND = 1<<oft;
}
二 LINUX下的中断处理:
总结:
①按下按键
②CPU自动进入异常模式
//arch/arm/kernel/entry-armv.s
b vector_irq + stubs_offset
||
||调用
||
∨
.long __irq_usr @ 0 (USR_26 / USR_32)
||
||调用
||
∨
__irq_usr:
usr_entry
...
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
1. 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);
//注释1:
//irq_desc是个数组,定义于kernel/irq,参见下文
//这里根据中断编号找出数组的一个元素
struct irq_desc *desc = irq_desc + irq;
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (irq >= NR_IRQS)
desc = &bad_irq_desc;
irq_enter();
//注释2:
//该函数实现了这三个功能:
//①分辨是哪个中断;②调用处理函数;③清中断
//函数原型参见注释2
desc_handle_irq(irq, desc);
/* AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
注释1: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),
#ifdef CONFIG_SMP
.affinity = CPU_MASK_ALL
#endif
}
};
注释2:desc_handle_irq
函数原型
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
//注释3:函数定义在kernel/irq/chip.c
//desc->handle_irq = handle_edge_irq
desc->handle_irq(irq, desc);
}
2.irq_desc数组元素初始化
irq_desc.handle_irq = handle_edge_irq;
chip = s3c_irqext_chip;
(1) irq_desc.handle_irq
函数原型的初始化过程:
//arch/arm/plat-s3c24xx
void __init s3c24xx_init_irq(void)
{
//...
/* external interrupts */
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-----------
set_irq_handler(irqno, handle_edge_irq);
//--------------------------------------
set_irq_flags(irqno, IRQF_VALID);
}
//...
}
||
||调用
||
∨
//include/linux/irq.h
//irq = irqno = IRQ_EINT4
//handle = handle_edge_irq
static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
__set_irq_handler(irq, handle, 0, NULL);
}
||
||调用
||
∨
//kernel/irq/chip.c
//irq = IRQ_EINT4
//handle = handle_edge_irq
//is_chained = 0
//*name = NULL
void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name)
{
struct irq_desc *desc;
unsigned long flags;
//根据注释1,这里根据中断编号找出数组的一个元素
desc = irq_desc + irq;
//这里把handle_edge_irq注册进来!对应于某个irq
desc->handle_irq = handle;
desc->name = name;
}
(2) irq_desc.chip
原型的初始化过程:
//arch/arm/plat-s3c24xx
void __init s3c24xx_init_irq(void)
{
//...
/* external interrupts */
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);
}
//...
}
||
||调用
||
∨
//kernel/irq/chip.c
//irq=IRQ_EINT4
//*chip=s3c_irqext_chip
int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
struct irq_desc *desc;
desc = irq_desc + irq;
desc->chip = chip;
}
3.handle_edge_irq函数处理过程
根据前面的分析,这是外部边沿触发中断处理函数
void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
/*
* If we're currently running this IRQ, or its disabled,
* we shouldn't process the IRQ. Mark it pending, handle
* the necessary masking and go out
* 出错处理
*/
if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
!desc->action)) {
desc->status |= (IRQ_PENDING | IRQ_MASKED);
mask_ack_irq(desc, irq);
goto out_unlock;
}
/* 发生中断计数值自加 */
kstat_cpu(cpu).irqs[irq]++;
//-------------第一步-----------------
/* Start handling the irq开始处理中断*/
desc->chip->ack(irq);
/* Mark the IRQ currently in progress.*/
desc->status |= IRQ_INPROGRESS;
do {
struct irqaction *action = desc->action;
irqreturn_t action_ret;
/*错误处理,action是个链表,如果俩表为空,则...*/
if (unlikely(!action)) {
desc->chip->mask(irq);
goto out_unlock;
}
/* 中断嵌套:如果新的中断到来,而当前中断还没有处理完成
* 必须把新的中断屏蔽掉
* When another irq arrived while we were handling
* one, we could have masked the irq.
* Renable it, if it was not disabled in meantime.
*/
if (unlikely((desc->status &
(IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
(IRQ_PENDING | IRQ_MASKED))) {
desc->chip->unmask(irq);
desc->status &= ~IRQ_MASKED;
}
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);
} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
desc->status &= ~IRQ_INPROGRESS;
out_unlock:
spin_unlock(&desc->lock);
}
把以上函数精简一下:
void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
//-------------第一步-----------------
/* Start handling the irq开始处理中断*/
desc->chip->ack(irq);
do {
//-------------第二步-----------------
/* 真正处理过程*/
action_ret = handle_IRQ_event(irq, action);
} while (..);
}
根据前面的分析:
irq_desc.handle_irq = handle_edge_irq;
chip = s3c_irqext_chip;
//arch/arm/plat-s3c24xx
static struct irq_chip s3c_irqext_chip = {
.name = "s3c-ext",
.mask = s3c_irqext_mask,
.unmask = s3c_irqext_unmask,
.ack = s3c_irqext_ack,
.set_type = s3c_irqext_type,
.set_wake = s3c_irqext_wake
};
- 第一步
desc->chip->ack(irq)
即s3c_irqext_ack
清中断
static void s3c_irqext_ack(unsigned int irqno)
{
unsigned long req;
unsigned long bit;
unsigned long mask;
bit = 1UL << (irqno - EXTINT_OFF);
//
mask = __raw_readl(S3C24XX_EINTMASK);
__raw_writel(bit, S3C24XX_EINTPEND);
req = __raw_readl(S3C24XX_EINTPEND);
req &= ~mask;
/* not sure if we should be acking the parent irq... */
if (irqno <= IRQ_EINT7 ) {
if ((req & 0xf0) == 0)
s3c_irq_ack(IRQ_EINT4t7);
} else {
if ((req >> 8) == 0)
s3c_irq_ack(IRQ_EINT8t23);
}
}
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);
}
- 处理中断
action_ret = handle_IRQ_event(irq, action);
//kernel/irq/handle.c
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
do {
ret = action->handler(irq, action->dev_id); //执行链表中的每一个函数
if (ret == IRQ_HANDLED)
status |= action->flags;
retval |= ret;
action = action->next; //依次取出链表每一个成员
} while (action);
}
如何向action
链表中添加注册的函数呢?
使用request_irq函数
插叙:struct irq_desc *desc结构定义分析
void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
分析struct irq_desc *desc
结构体
//include/linux/irq.h
struct irq_desc {
irq_flow_handler_t handle_irq;//根据2中的分析:handle_edge_irq
struct irq_chip *chip; //与芯片相关的底层处理函数--根据2中的分析:s3c_irqext_chip,参见下面的注释1
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* 中断函数执行列表 IRQ action list,参见注释2*/
unsigned int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
- 注释1 irq_chip
/**
* struct irq_chip - hardware interrupt chip descriptor
* 硬件中断处理,与具体芯片相关的一些操作
* @name: name for /proc/interrupts
* @startup: start up the interrupt (defaults to ->enable if NULL) 启动中断
* @shutdown: shut down the interrupt (defaults to ->disable if NULL) 关闭中断
* @enable: enable the interrupt (defaults to chip->unmask if NULL) 使能中断
* @disable: disable the interrupt (defaults to chip->mask if NULL) 禁止中断
* @ack: start of a new interrupt 开启新的中断(清除当前中断)
* @mask: mask an interrupt source 屏蔽中断源
* @mask_ack: ack and mask an interrupt source
* @unmask: unmask an interrupt source
* @eoi: end of interrupt - chip level
* @end: end of interrupt - flow level
* @set_affinity: set the CPU affinity on SMP machines
* @retrigger: resend an IRQ to the CPU
* @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
* @set_wake: enable/disable power-management wake-on of an IRQ
*
* @release: release function solely used by UML
* @typename: obsoleted by name, kept as migration helper
*/
struct irq_chip {
const char *name;
unsigned int (*startup)(unsigned int irq);
void (*shutdown)(unsigned int irq);
void (*enable)(unsigned int irq);
void (*disable)(unsigned int irq);
void (*ack)(unsigned int irq);
void (*mask)(unsigned int irq);
void (*mask_ack)(unsigned int irq);
void (*unmask)(unsigned int irq);
void (*eoi)(unsigned int irq);
void (*end)(unsigned int irq);
void (*set_affinity)(unsigned int irq, cpumask_t dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type);
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;
};
- 注释2 struct irqaction *action分析-中断函数执行链表
struct irqaction {
irq_handler_t handler;
unsigned long flags;
cpumask_t mask;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
};
4.request_irq函数分析-注册中断程序使用
(1)函数声明:
/**
* request_irq - allocate an interrupt line
* @irq: Interrupt line to allocate 中断号
* @handler: Function to be called when the IRQ occurs 处理函数
* @irqflags: Interrupt type flags 中断类型:上升沿/下降沿/
* @devname: An ascii name for the claiming device 中断名字
* @dev_id: A cookie passed back to the handler function 卸载irq的时候需要用到本参数
* */
int request_irq(
unsigned int irq,
irq_handler_t handler,
unsigned long irqflags,
const char *devname,
void *dev_id)
函数实现
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
int retval;
//1.分配一个结构
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
//使用输入参数来初始化这个action结构
action->handler = handler;
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
//2.使用分配的这个action结构
retval = setup_irq(irq, action);
if (retval)
kfree(action);
return retval;
}
(2)setup_irq(irq, action)
函数分析
int setup_irq(unsigned int irq, struct irqaction *new)
{
struct irq_desc *desc = irq_desc + irq;
p = &desc->action;
old = *p;
/* add new interrupt at end of irq queue
把新的函数加到链表队尾*/
do {
p = &old->next;
old = *p;
} while (old);
/* Setup the type (level, edge polarity) if configured:
设置中断引脚触发类型,参见注释1*/
desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK);
/* 使能中断*/
if (desc->chip->startup)
desc->chip->startup(irq);
else
desc->chip->enable(irq);
}
注释1:
desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK)
函数分析:调用与芯片相匹配的具体代码初始化中断引脚,中断类型等
根据插叙的分析desc->chip=s3c_irqext_chip
,
static struct irq_chip s3c_irqext_chip = {
.name = "s3c-ext",
.mask = s3c_irqext_mask,
.unmask = s3c_irqext_unmask,
.ack = s3c_irqext_ack,
.set_type = s3c_irqext_type,
.set_wake = s3c_irqext_wake
};
所以调用
desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK)
相当于调用s3c_irqext_type(unsigned int irq, unsigned int type)
int s3c_irqext_type(unsigned int irq, unsigned int type)
{
void __iomem *extint_reg;
void __iomem *gpcon_reg;
unsigned long gpcon_offset, extint_offset;
unsigned long newvalue = 0, value;
if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
{
gpcon_reg = S3C2410_GPFCON;
extint_reg = S3C24XX_EXTINT0;
gpcon_offset = (irq - IRQ_EINT0) * 2;
extint_offset = (irq - IRQ_EINT0) * 4;
}
...
/* Set the GPIO to external interrupt mode */
value = __raw_readl(gpcon_reg);
value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
__raw_writel(value, gpcon_reg);
/* Set the external interrupt to pointed trigger type */
switch (type)
{
case IRQT_NOEDGE:
printk(KERN_WARNING "No edge setting!\n");
break;
case IRQT_RISING:
newvalue = S3C2410_EXTINT_RISEEDGE;
break;
case IRQT_FALLING:
newvalue = S3C2410_EXTINT_FALLEDGE;
break;
case IRQT_BOTHEDGE:
newvalue = S3C2410_EXTINT_BOTHEDGE;
break;
...
}
value = __raw_readl(extint_reg);
value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
__raw_writel(value, extint_reg);
return 0;
}
5.free_irq函数分析-释放irq
①把函数移出链表;
②禁止中断
/**
* free_irq - free an interrupt 释放中断
* @irq: Interrupt line to free 中断标号
* @dev_id: Device identity to free 设备号
*/
void free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc;
struct irqaction **p;
/*查找对应的中断号*/
desc = irq_desc + irq;
p = &desc->action;
/*循环查找action链表*/
for (;;) {
struct irqaction *action = *p;
if (action) {
struct irqaction **pp = p;
p = &action->next;
/*判断这个action的dev_id是否等于传进来的dev_id,
若相等,则卸载掉;
若不想等,则查找下一个。*/
if (action->dev_id != dev_id)
continue;
/* Found it - now remove it from the list of entries 卸载掉*/
*pp = action->next;
desc->chip->release(irq, dev_id);
/* 若链表已空,屏蔽或者禁止该中断*/
if (!desc->action) {
if (desc->chip->shutdown)
desc->chip->shutdown(irq);
else
desc->chip->disable(irq);
}
}