Linux驱动之中断处理体系结构简析
S3C2440中的中断处理最终是通过IRQ实现的,在Linux驱动之异常处理体系结构简析已经介绍了IRQ异常的处理过程,最终分析到了一个C函数asm_do_IRQ,接下来继续分析asm_do_IRQ,目标是推导出中断的处理过程。
看到asm_do_irq函数,它位于arch\arm\kernel\Irq.c中。它先根据irq中断号从irq_desc 数组中取出这个中断对应的desc结构体,irq中断号是根据INTOFFSET寄存器的值来确定的,这个寄存器里的值根据中断的来源不同会置位相应的位,它在调用C函数asm_do_IRQ之前被存放在r0中,在C函数中即是irq。
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc *desc = irq_desc + irq;//根据irq中断号求出当前中断的desc结构 /*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (irq >= NR_IRQS)
desc = &bad_irq_desc; irq_enter();//中断次数的计数 desc_handle_irq(irq, desc);//处理函数 /* AT91 specific workaround */
irq_finish(irq); irq_exit();//退出中断
set_irq_regs(old_regs);
}
然后再调用中断处理函数为desc_handle_irq,接着看到desc_handle_irq函数,它是一个内联函数,它位于include\asm-arm\mach\irq.h
/*
* Obsolete inline function for calling irq descriptor handlers.
*/
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);//调用handle_irq处理中断
}
从中可以得出一个很重要的数据结构irq_desc ,它的结构如下
struct irq_desc {
irq_flow_handler_t handle_irq;//handle_irq处理函数
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */action链表
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;
接着搜索handle_irq函数看看它是在哪里被定义的,经过层层分析,最终找到了调用它的顶层函数init_IRQ,下面列出调用过程:其中init_arch_irq函数是由MACHINE_START这个宏定义的,它在start_kernel->setup_arch(init\Main.c)时已经初始化。它是跟单板结构相关的,详情参考Linux移植之内核启动过程引导阶段分析
init_IRQ
init_arch_irq();//init_arch_irq = mdesc->init_irq,这个是在machine_desc结构中定义的
s3c24xx_init_irq
set_irq_handler
__set_irq_handler
desc->handle_irq = handle;
接着看到init_arch_irq,即s3c24xx_init_irq(arch\arm\plat-s3c24xx\Irq.c)函数:这个函数设置了irq_desc的一些变量,后面就可以调用这些变量。比如说handle_edge_irq即是最终的中断处理函数
void __init s3c24xx_init_irq(void)
{
...
...
/* external interrupts */ for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int)\n", irqno);
set_irq_chip(irqno, &s3c_irq_eint0t4); //设置irq_desc->chip = &s3c_irq_eint0t4
set_irq_handler(irqno, handle_edge_irq);//设置irq_desc->handler = handle_edge_irq,处理入口函数
set_irq_flags(irqno, IRQF_VALID); //设置irq_desc->status为IRQF_VALID表示可以使用前面定义的这几个函数
}
...
...
}
接着看到handle_edge_irq,它位于kernel\irq\Chip.c中,它的主要功能是首先是清0中断标志,然后运行中断处理函数handle_IRQ_event
void fastcall
handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
...
...
/* Start handling the irq */
desc->chip->ack(irq);//清0中断标志,响应中断
...
...
if (unlikely((desc->status &
(IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
(IRQ_PENDING | IRQ_MASKED))) {
desc->chip->unmask(irq);//开启中断
desc->status &= ~IRQ_MASKED;
} action_ret = handle_IRQ_event(irq, action);//中断处理函数 ...
...
}
继续往下看handle_IRQ_event,它位于kernel\irq\Handle.c,它会不断的检查desc结构里的action结构链表,然后调用里面的处理函数action->handler,函数的参数为irq、atcion->dev_id,这个函数就是真正的处理函数,而这个函数可以由用户定义。
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
...
... do {
ret = action->handler(irq, action->dev_id);//运行handler函数,这个函数是自己构建的
if (ret == IRQ_HANDLED)
status |= action->flags;//设置状态
retval |= ret;
action = action->next;
} while (action);//检查action链表上是否还有函数未执行 ...
...
}
那么action->handler这个函数在哪里被定义呢,接着搜索action->handler被定义的地方,最终发现在request_irq,这个函数位于kernel\irq\Manage.c,
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
...
...
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);//分配一个action结构体
if (!action)
return -ENOMEM; action->handler = handler;//设置处理函数handler
action->flags = irqflags; //设置标志irqflags。中断方式
cpus_clear(action->mask);
action->name = devname; //设置设备名称
action->next = NULL; //设置下一个action为空
action->dev_id = dev_id; //设置设备id
...
... retval = setup_irq(irq, action);//将上面设备的action结构放入action链表
if (retval)
kfree(action); return retval;
}
这个函数的主要作用是构建一个ation结构,然后用request_irq传入的四个参数初始化它,ation结构如下:
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;
};
最终会调用setup_irq,将初始化的内容放入action链表,这个函数同样位于kernel\irq\Manage.c。它的功能简述为
1、将新的action放入链表
2、设置中断触发方式
3、启动中断
int setup_irq(unsigned int irq, struct irqaction *new)
{
...
...
/*
286 * The following block of code has to be executed atomically
287 */
spin_lock_irqsave(&desc->lock, flags);
p = &desc->action;//获得当前desc结构的action结构
old = *p;
if (old) {//如果已经存在了action结构
if (!((old->flags & new->flags) & IRQF_SHARED) ||
((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {//判断新的action结构与现有的action结构是否使用相同的触发方式,是否是可共享的
old_name = old->name;
goto mismatch;//如果不一样,则跳转到mismatch
} ...
... /* add new interrupt at end of irq queue */
do {//腾出空间,准备将新的结构放入当前的action链表
p = &old->next;
old = *p;
} while (old);
shared = ;
} *p = new;//将新的结构放入当前的action链表 ...
... if (!shared) {
irq_chip_set_defaults(desc->chip);//设置一些chip结构还没设置的指针 #if defined(CONFIG_IRQ_PER_CPU)
if (new->flags & IRQF_PERCPU)
desc->status |= IRQ_PER_CPU;
#endif /* Setup the type (level, edge polarity) if configured: */
if (new->flags & IRQF_TRIGGER_MASK) {
if (desc->chip && desc->chip->set_type)
desc->chip->set_type(irq,
new->flags & IRQF_TRIGGER_MASK);//设置中断的触发方式,配置EXTINTX寄存器以及GPXCON寄存器
...
... if (!(desc->status & IRQ_NOAUTOEN)) {
desc->depth = ;
desc->status &= ~IRQ_DISABLED;
if (desc->chip->startup)
desc->chip->startup(irq);//开始中断
else
desc->chip->enable(irq);//开始中断
} else
/* Undo nested disables: */
desc->depth = ;
}
...
...
}
所有如果要注册一个新的中断,那么可以调用request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)来注册。
同样的,如果要消除已经注册的中断,需要调用free_irq(unsigned int irq,void *dev_id)函数来注销,它的执行过程与request刚好相反
1、根据中断号irq、dev_iq从action链表中找到表项,然后移除它
2、如果它是唯一的表项,,则调用desc->chip->shutdown(irq);或desc->chip->disable(irq);来关闭irq号的中断。
以上就是整个linux的中断体系结构的简单描述,更复杂的下半部机制后面深入了解后再去描述。
Linux驱动之中断处理体系结构简析的更多相关文章
- Linux驱动之异常处理体系结构简析
异常的概念在单片机中也接触过,它的意思是让CPU可以暂停当前的事情,跳到异常处理程序去执行.以前写单片机裸机程序属于前后台程序,前台指的就是mian函数里的while(1)大循环,后台指的就是产生异常 ...
- Linux驱动之输入子系统简析
输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动.键盘按下等通过Driver->Inputcore->Event handler->userspac ...
- Linux网络性能优化方法简析
Linux网络性能优化方法简析 2010-12-20 10:56 赵军 IBMDW 字号:T | T 性能问题永远是永恒的主题之一,而Linux在网络性能方面的优势则显而易见,这篇文章是对于Linux ...
- Linux 目录结构学习与简析 Part2
linux目录结构学习与简析 by:授客 QQ:1033553122 ---------------接Part 1-------------- #1.查看CPU信息 #cat /proc/cpuinf ...
- Linux 目录结构学习与简析 Part1
linux目录结构学习与简析 by:授客 QQ:1033553122 说明: / linux系统目录树的起点 =============== /bin User Bi ...
- linux驱动之中断处理过程C程序部分
当发生中断之后,linux系统在汇编阶段经过一系列跳转,最终跳转到asm_do_IRQ()函数,开始C程序阶段的处理.在汇编阶段,程序已经计算出发生中断的中断号irq,这个关键参数最终传递给asm_d ...
- linux驱动之中断处理过程汇编部分
linux系统下驱动中,中断异常的处理过程,与裸机开发中断处理过程非常类似.通过简单的回顾裸机开发中断处理部分,来参考学习linux系统下中断处理流程. 一.ARM裸机开发中断处理过程 以S3C244 ...
- Linux内核poll/select机制简析
0 I/O多路复用机制 I/O多路复用 (I/O multiplexing),提供了同时监测若干个文件描述符是否可以执行IO操作的能力. select/poll/epoll 函数都提供了这样的机制,能 ...
- Linux Hugetlbfs内核源码简析-----(二)Hugetlbfs挂载
本文只讨论执行"mount none /mnt/huge -t hugetlbfs"命令后,mount系统调用的执行过程(基于Linux-3.4.51),不涉及进程相关的细节. m ...
随机推荐
- Centos7更改yum源
每次都要百度一番,还不如自己做个记录,简单粗暴,哈哈哈哈 cd /etc/yum.repos.d mv CentOS-Base.repo CentOS-Base.repo.old wget http: ...
- Python——列表、元祖、字典、集合的基本操作
列表 1. 列表——增 (1)append li = ['xcsd', 'cdc', [1, 5, 2], 'eht', '辛辰'] li.append('nihao') print(li) #['x ...
- nginx 信号
来源:nginx.cn Nginx信号控制 Nginx控制信号 TERM, INT 快速关闭 QUIT 从容关闭 HUP 重新加载,用新的配置开始新的工作进程 USER1 重新打开日志文件 USER2 ...
- gdb调试嵌入式环境搭建
1.下载gdb源代码 http://ftp.gnu.org/gnu/gdb/ 2.编译 解压#tar zxvf gdb-7.9.1.tar.gz,cd到解压的目录中. 2.1编译arm-linux-g ...
- python3学习笔记六(元组)
元组 创建空元组 tup1 = () #空元组print(type(tup1))print(tup1) tup2 = (10)tup3 = (10,)print(type(tup2)) #不加逗号,类 ...
- 20175311 2018-2019-2 《Java程序设计》第1周学习总结
教材学习内容总结 第一周我们主要尝试了怎么安装各种以后可能需要用到的软件,根据老师提供的博客教程进行自主学习安装软件,然后编写一些简单的语言程序. 教材学习中的问题和解决过程 问题1:在学习过程中主要 ...
- 源码:Java集合源码之:数组与链表(一)
数组和链表是数据结构中最基本的部分. 数组 在java中,数组定义为一种基本类型,其可以通过下标获取到对应位置的数据.那么这种结构的数据,在内存中是怎么存放的呢? 数组在内存中是一段连续的存储单元,每 ...
- 1-hadoop、mr
1.HDFS的优缺点: 优点: ① 高容错 ② 可扩展 ③ 适合大文件存储 ④ 可构建在廉价的机器上 缺点: ① 高延迟 ② 文件不能修改 ③ 不适合小文件存储 2.HDFS架构(类似于文件系统): ...
- python3文件的读写操作
open函数:对文件进行读写操作前,先打开文件,获取文件的句柄: open(file, mode, encoding, buffering) 参数说明 file_name:一个包含了你要访问的文件路径 ...
- Error creating bean with name 'transactionManager'
查看数据库是否连通,看错误的具体信息 看ssm配置文件是否被正确加载,上次我的错误是beans之类的错误,就是spring文件没有被加载,因为 而文件是applicationConfig.xml