linux下的中断处理,都是在asm_do_IRQ函数中:
desc->handle_irq
上面有irq_desc[irq] = desc
在__irq_set_handler中设置了desc->handler_irq
irq_desc[irq]
{
handle_irq = handle_edge_irq;
chip = s3cirq_eint0t4;
...
}
(一)汇编中断向量部分
irq.c
handle_IRQ
generic_handle_irq(irq); //
struct irq_desc *desc = irq_to_desc(irq);
**************
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
**************
//返回irq_desc数组中的irq_desc[irq]项
//即desc = irq_desc[irq]
...
generic_handle_irq_desc(irq, desc)
desc->handle_irq(irq, desc);//根据传入的irq(中断号),irq_desc[irq]两参数
//上面给irq_desc[irq]->handle_irq传入两个参数
//那么必定这个desc_irq[irq]->handle_irq成员有被某个函数赋值
(二)irq_desc初始化部分(下面举例按键中断初始化)
s3c24xx_init_irq
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++)
irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,handle_edge_irq);
//设置按键0的irqno=16
// irq_chip=s3c_irq_eint0t4
// irq_flow_handler_t = handle_edge_irq
//static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle)
irq_set_chip_and_handler_name(irq, chip, handle, NULL)
irq_set_chip(irq, chip); //设置chip
__set_irq_handler(irq, handle, 0, name)
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
//从buslock中取出发生中断的desc = irq_desc[irq]
desc->handle_irq = handle; //handle_irq在这里被设置
***********************
//最后就是把irq_desc[irq]->hand_irq = handle_edge_irq
//这是在按键被初始化就对irq_desc[]数组第irq个成员的handle_irq初始化
//了中断处理函数,同时还初始化了chip
***********************
上述代码s3c4xx_init_irq()分析的结果如下:
irq_desc[IRQ_EINT0(16)]
{
.handl_irq = handle_edge_irq;
.chip = &s3cirq_eint0to4;
...
}
irq_desc[IRQ_EINT1(17)]
{
.handl_irq = handle_edge_irq;
.chip = &s3cirq_eintto4;
...
}
...
(三)调用响应处理函数(还是举上例)
handle_edge_irq //处理边缘触发中断
desc->irq_data.chip->irq_ack(&desc->irq_data); //清中断
handle_irq_event(desc); //取出action链表中的成员,执行action->handler
handle_irq_event_percpu
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id); //
trace_irq_handler_exit(irq, action, res);
总结:1.(一)部分在硬件产生中断,根据中断号,会找到由(二)部分已经存放在irq_desc[]数组
初始化好的handler_irq中断服务程序。
2.
按下按键
cpu自动进入异常模式 b vector_irq,...
__irq_usr:
usr_entry
irq_handler
asm_do_IRQ
irq_desc[irq]->handler.irq ()
假设对应这个中断函数: handle_edge_irq
desc->chip->ack(irq) //清中断
handle_IRQ_event //处理中断
取出action链表中的成员
执行: action_handl
(四)在之前handle_irq会根据中断服务程序去调用相对应的函数,如handle_edge_irq,
它将先清除中断,然后执行action。而action到底是什么?action也是irq_desc结构
体中的成员:irq_desc.action = (中断行为链表)
struct irqaction {
irq_handler_t handler;
unsigned long flags;
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
之前的步骤都是系统中断架构,而action->handler才是用户自身用来使用的。如果
想让系统注册某个中断handler,使用request_irq()函数。
request_threaded_irq
->desc = irq_to_desc(irq);
->action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
***** a. 分配irqation结构 ******
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
->retval = __setup_irq(irq, desc, action);
old_ptr = &desc->action;
if(old)
if (!((old->flags & new->flags) & IRQF_SHARED)
...
//判断是不是共享中断
//不能共享同一个引脚就不能添加到队列中
do {
/*
* Or all existing action->thread_mask bits,
* so we can find the next zero bit for this
* new action.
*/
thread_mask |= old->thread_mask;
old_ptr = &old->next;
old = *old_ptr;
} while (old);
***** b. 添加到新的队列中去 *****
__irq_set_trigger
chip->irq_set_type ***** c. 把某个引脚设置为中断引脚**
//对于s3c_irqext_chip的irq_set_type=s3c_irqext_type
//对于s3c_irq_eint0t4的irq_set_type=s3c_irqext_type
irq_startup ***** d. 开启中断 *****
desc->irq_data.chip->irq_startup(&desc->irq_data);
register_handler_proc(irq, new)
总结:
request_irq(irq,handler,flags,name,dev_id)
a.分配一个irqaction
b.把这个结构放入irq_desc[irq]的action链表
c.设置引脚
d.使能中断
重要的几个数据结构:
****************
中断描述符
****************
//include\linux\irq.h
irq_desc {
...
irq_flow_handler_t handle_irq; //高层次的中断事件处理函数
struct irq_chip *chip; //底层次的硬件操作
struct msi_desc *msi_desc;
void *handler_data; //chip方法使用的数据
void chip_data;
struct irqaction *action; //IRQ action list 行为链表
unsigned int status; //IRQ status
unsigned int depth; //关中断次数
unsigned int wake_depth; //唤醒次数
unsigned int irq_count; //发生中断次数
unsigned long last_unhandled; //滞留时间
...
spinlock_t lock; //自旋锁
...
struct proc_dir_entry *dir; //在proc文件系统中的目录
}
*****************
irq_data存储了会传递给芯片相关处理函数irq相关数据以及和irq相关的芯片信息
(包括一个struct irq_chip结构)
*****************
struct irq_data {
unsigned int irq;
unsigned long hwirq;
unsigned int node;
unsigned int state_use_accessors;
struct irq_chip *chip;
struct irq_domain *domain;
void *handler_data;
void *chip_data;
struct msi_desc *msi_desc;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
#endif
};
******************
irqaction存储了中断服务函数描述符,存储了中断服务函数相关的信息
******************
//include\linux\irqaction
struct irqaction {
irq_handler_t handler;
unsigned long flags;
}
*******************
struct irq_chip
该数据结构存储了最低层的和中断相关的芯片等级的函数指针(包括了初始化
、使能、关闭、确认、中断结束(eoi)、设置中断亲和性等函数指针)。是一
个芯片等级的硬件中断描述符。该结构考虑到了可能遇到的所有IRQ的特性,
因而对于一个特定的IRQ来说,它可能只实现了其中的一部分操作。它表示一
个IRQ芯片,它提供的都是芯片级的操作处理函数。
*******************
//include\linux\irq.h
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); //响应中断,清中断
....
}
*******************
9. low level API
系统包含了NR_IRQS个中断描述符的数组,在early_irq_init中,数组元素会被默认
初始化为关闭中断,中断处理函数为handle_bad_irq,irq_chip会被设置为no_irq_chip。
中断框架提供了几个low level的API用于处理和中断控制器相关的事情:
(1).int irq_set_chip(unsigned int irq, struct irq_chip *chip):用于设计irq对应的irq chip。
(2).int irq_set_handler_data(unsigned int irq, void *data)和irq_set_chip_data(unsigned int irq, void *data):
设置irq chip的处理函数所需要的data
(3).irq_set_irq_type(unsigned int irq, unsigned int type):设置irq对中的中断的触发机制
(4).void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle)
设置irq对应的irq chip以及中断处理函数,它设置的是irq_desc中的handle_irq,而不是irqaction类型的
action。这二者的区别在于前者一般使用的是一个系统已经定义好的处理函数,比如handle_level_irq、
handle_percpu_irq、handle_fasteoi_irq、handle_edge_irq,它需要调用irq chip中的函数完成芯片级的中断处理,比如确认中断
,在中断处理完后做EOI,同时这些处理函数还需要做的一个重要的工作就是调用与该irq关联的irqaction类型的action;
irqaction类型的中断处理函数是完成中断处理的函数,即对中断进行响应的函数,它是通过requst_irq注册到irq对
应的irq_desc中从而和irq关联起来的。
(5).void irq_set_handler(unsigned int irq, irq_flow_handler_t handle):该函数用来设置irq对应的中断处理函数,
注意它设置的handler也是irq_desc中的handle_irq域
(6).void irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle):该函数用来设置irq对应的中断
处理函数,注意它设置的handler也是irq_desc中的handle_irq域,它的特殊之处在于,用它设置完一个irq后,该irq
会被标记为IRQ_NOREQUEST, IRQ_NOPROBE, and IRQ_NOTHREAD,也就是说该irq变为不可request,不可probe,不可线
程化。
********************************************
desc->handle_irq
上面有irq_desc[irq] = desc
在__irq_set_handler中设置了desc->handler_irq
irq_desc[irq]
{
handle_irq = handle_edge_irq;
chip = s3cirq_eint0t4;
...
}
(一)汇编中断向量部分
irq.c
handle_IRQ
generic_handle_irq(irq); //
struct irq_desc *desc = irq_to_desc(irq);
**************
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
**************
//返回irq_desc数组中的irq_desc[irq]项
//即desc = irq_desc[irq]
...
generic_handle_irq_desc(irq, desc)
desc->handle_irq(irq, desc);//根据传入的irq(中断号),irq_desc[irq]两参数
//上面给irq_desc[irq]->handle_irq传入两个参数
//那么必定这个desc_irq[irq]->handle_irq成员有被某个函数赋值
(二)irq_desc初始化部分(下面举例按键中断初始化)
s3c24xx_init_irq
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++)
irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,handle_edge_irq);
//设置按键0的irqno=16
// irq_chip=s3c_irq_eint0t4
// irq_flow_handler_t = handle_edge_irq
//static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle)
irq_set_chip_and_handler_name(irq, chip, handle, NULL)
irq_set_chip(irq, chip); //设置chip
__set_irq_handler(irq, handle, 0, name)
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
//从buslock中取出发生中断的desc = irq_desc[irq]
desc->handle_irq = handle; //handle_irq在这里被设置
***********************
//最后就是把irq_desc[irq]->hand_irq = handle_edge_irq
//这是在按键被初始化就对irq_desc[]数组第irq个成员的handle_irq初始化
//了中断处理函数,同时还初始化了chip
***********************
上述代码s3c4xx_init_irq()分析的结果如下:
irq_desc[IRQ_EINT0(16)]
{
.handl_irq = handle_edge_irq;
.chip = &s3cirq_eint0to4;
...
}
irq_desc[IRQ_EINT1(17)]
{
.handl_irq = handle_edge_irq;
.chip = &s3cirq_eintto4;
...
}
...
(三)调用响应处理函数(还是举上例)
handle_edge_irq //处理边缘触发中断
desc->irq_data.chip->irq_ack(&desc->irq_data); //清中断
handle_irq_event(desc); //取出action链表中的成员,执行action->handler
handle_irq_event_percpu
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id); //
trace_irq_handler_exit(irq, action, res);
总结:1.(一)部分在硬件产生中断,根据中断号,会找到由(二)部分已经存放在irq_desc[]数组
初始化好的handler_irq中断服务程序。
2.
按下按键
cpu自动进入异常模式 b vector_irq,...
__irq_usr:
usr_entry
irq_handler
asm_do_IRQ
irq_desc[irq]->handler.irq ()
假设对应这个中断函数: handle_edge_irq
desc->chip->ack(irq) //清中断
handle_IRQ_event //处理中断
取出action链表中的成员
执行: action_handl
(四)在之前handle_irq会根据中断服务程序去调用相对应的函数,如handle_edge_irq,
它将先清除中断,然后执行action。而action到底是什么?action也是irq_desc结构
体中的成员:irq_desc.action = (中断行为链表)
struct irqaction {
irq_handler_t handler;
unsigned long flags;
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
之前的步骤都是系统中断架构,而action->handler才是用户自身用来使用的。如果
想让系统注册某个中断handler,使用request_irq()函数。
request_threaded_irq
->desc = irq_to_desc(irq);
->action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
***** a. 分配irqation结构 ******
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
->retval = __setup_irq(irq, desc, action);
old_ptr = &desc->action;
if(old)
if (!((old->flags & new->flags) & IRQF_SHARED)
...
//判断是不是共享中断
//不能共享同一个引脚就不能添加到队列中
do {
/*
* Or all existing action->thread_mask bits,
* so we can find the next zero bit for this
* new action.
*/
thread_mask |= old->thread_mask;
old_ptr = &old->next;
old = *old_ptr;
} while (old);
***** b. 添加到新的队列中去 *****
__irq_set_trigger
chip->irq_set_type ***** c. 把某个引脚设置为中断引脚**
//对于s3c_irqext_chip的irq_set_type=s3c_irqext_type
//对于s3c_irq_eint0t4的irq_set_type=s3c_irqext_type
irq_startup ***** d. 开启中断 *****
desc->irq_data.chip->irq_startup(&desc->irq_data);
register_handler_proc(irq, new)
总结:
request_irq(irq,handler,flags,name,dev_id)
a.分配一个irqaction
b.把这个结构放入irq_desc[irq]的action链表
c.设置引脚
d.使能中断
重要的几个数据结构:
****************
中断描述符
****************
//include\linux\irq.h
irq_desc {
...
irq_flow_handler_t handle_irq; //高层次的中断事件处理函数
struct irq_chip *chip; //底层次的硬件操作
struct msi_desc *msi_desc;
void *handler_data; //chip方法使用的数据
void chip_data;
struct irqaction *action; //IRQ action list 行为链表
unsigned int status; //IRQ status
unsigned int depth; //关中断次数
unsigned int wake_depth; //唤醒次数
unsigned int irq_count; //发生中断次数
unsigned long last_unhandled; //滞留时间
...
spinlock_t lock; //自旋锁
...
struct proc_dir_entry *dir; //在proc文件系统中的目录
}
*****************
irq_data存储了会传递给芯片相关处理函数irq相关数据以及和irq相关的芯片信息
(包括一个struct irq_chip结构)
*****************
struct irq_data {
unsigned int irq;
unsigned long hwirq;
unsigned int node;
unsigned int state_use_accessors;
struct irq_chip *chip;
struct irq_domain *domain;
void *handler_data;
void *chip_data;
struct msi_desc *msi_desc;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
#endif
};
******************
irqaction存储了中断服务函数描述符,存储了中断服务函数相关的信息
******************
//include\linux\irqaction
struct irqaction {
irq_handler_t handler;
unsigned long flags;
}
*******************
struct irq_chip
该数据结构存储了最低层的和中断相关的芯片等级的函数指针(包括了初始化
、使能、关闭、确认、中断结束(eoi)、设置中断亲和性等函数指针)。是一
个芯片等级的硬件中断描述符。该结构考虑到了可能遇到的所有IRQ的特性,
因而对于一个特定的IRQ来说,它可能只实现了其中的一部分操作。它表示一
个IRQ芯片,它提供的都是芯片级的操作处理函数。
*******************
//include\linux\irq.h
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); //响应中断,清中断
....
}
*******************
9. low level API
系统包含了NR_IRQS个中断描述符的数组,在early_irq_init中,数组元素会被默认
初始化为关闭中断,中断处理函数为handle_bad_irq,irq_chip会被设置为no_irq_chip。
中断框架提供了几个low level的API用于处理和中断控制器相关的事情:
(1).int irq_set_chip(unsigned int irq, struct irq_chip *chip):用于设计irq对应的irq chip。
(2).int irq_set_handler_data(unsigned int irq, void *data)和irq_set_chip_data(unsigned int irq, void *data):
设置irq chip的处理函数所需要的data
(3).irq_set_irq_type(unsigned int irq, unsigned int type):设置irq对中的中断的触发机制
(4).void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle)
设置irq对应的irq chip以及中断处理函数,它设置的是irq_desc中的handle_irq,而不是irqaction类型的
action。这二者的区别在于前者一般使用的是一个系统已经定义好的处理函数,比如handle_level_irq、
handle_percpu_irq、handle_fasteoi_irq、handle_edge_irq,它需要调用irq chip中的函数完成芯片级的中断处理,比如确认中断
,在中断处理完后做EOI,同时这些处理函数还需要做的一个重要的工作就是调用与该irq关联的irqaction类型的action;
irqaction类型的中断处理函数是完成中断处理的函数,即对中断进行响应的函数,它是通过requst_irq注册到irq对
应的irq_desc中从而和irq关联起来的。
(5).void irq_set_handler(unsigned int irq, irq_flow_handler_t handle):该函数用来设置irq对应的中断处理函数,
注意它设置的handler也是irq_desc中的handle_irq域
(6).void irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle):该函数用来设置irq对应的中断
处理函数,注意它设置的handler也是irq_desc中的handle_irq域,它的特殊之处在于,用它设置完一个irq后,该irq
会被标记为IRQ_NOREQUEST, IRQ_NOPROBE, and IRQ_NOTHREAD,也就是说该irq变为不可request,不可probe,不可线
程化。
********************************************