6.分析request_irq和free_irq函数如何注册注销中断
上一节讲了如何实现运行中断,这些都是系统给做好的,当我们想自己写个中断处理程序,去执行自己的代码,就需要写irq_desc->action->handler,然后通过request_irq()来向内核申请注册中断
本节目标:
分析request_irq()如何申请注册中断,free_irq()如何注销中断
1.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)
参数说明:
unsigned int irq:为要注册中断服务函数的中断号,比如外部中断0就是16,定义在mach/irqs.h
irq_handler_t handler:为要注册的中断服务函数,就是(irq_desc+ irq )->action->handler
unsigned long irqflags: 触发中断的参数,比如边沿触发, 定义在linux/interrupt.h。
const char *devname:中断程序的名字,使用cat /proc/interrupt 可以查看中断程序名字
void *dev_id:传入中断处理程序的参数,注册共享中断时不能为NULL,因为卸载时需要这个做参数,避免卸载其它中断服务函数
1.1request_irq代码如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
... ...
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); //注册irqaction结构体类型的action
if (!action)
return -ENOMEM; /* 将带进来的参数赋给action */
action->handler = handler;
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id; select_smp_affinity(irq);
... ...
retval = setup_irq(irq, action); // 进入setup_irq(irq, action),设置irq_ desc[irq]->action
if (retval)
kfree(action); return retval;
}
从上面分析,request_irq()函数主要注册了一个irqaction型action,然后把参数都赋给这个action,最后进入setup_irq(irq, action)设置irq_ desc[irq]->action
1.2我们来看看setup_irq(irq, action)如何设置irq_ desc[irq]->action的:
int setup_irq(unsigned int irq, struct irqaction *new)
{
struct irq_desc *desc = irq_desc + irq; //根据中断号找到irq_ desc[irq]
... ...
p = &desc->action; //指向desc->action
old = *p;
if (old) { //判断action是否为空
/*判断这个中断是否支持共享 (IRQF_SHARED)*/
if (!((old->flags & new->flags) & IRQF_SHARED) ||
((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
old_name = old->name;
goto mismatch; //不支持,则跳转
} #if defined(CONFIG_IRQ_PER_CPU)
/* All handlers must agree on per-cpuness */
if ((old->flags & IRQF_PERCPU) !=
(new->flags & IRQF_PERCPU))
goto mismatch;
#endif /*找到action链表尾处,后面用于添加 新的中断服务函数(*new) */
do {
p = &old->next;
old = *p;
} while (old);
shared = ; //表示该中断支持共享,添加新的action,否则直接赋值新的action
} *p = new; //指向新的action ... ... if (!shared) { //若该中断不支持共享
irq_chip_set_defaults(desc->chip); //更新desc->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设置为中断引脚
desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK);
else
printk(KERN_WARNING "No IRQF_TRIGGER set_type " "function for IRQ %d (%s)\n", irq, desc->chip ? desc->chip->name : "unknown");
} else
compat_irq_chip_set_default_handler(desc); desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
IRQ_INPROGRESS); 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 = ;
}
从上面可以看出setup_irq(irq, action)主要是将action中断服务函数放在irq_ desc[irq]->action中,
然后设置中断引脚:
desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK);
最后[开启/使能]中断:
desc->chip->[startup(irq) /enable(irq)]; //[开启/使能]中断
我们以外部中断0的desc[16]->chip->set_type为例,来看看它是如何初始化中断引脚的:
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 = , value;
if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) //找到寄存器
{
gpcon_reg = S3C2410_GPFCON;
extint_reg = S3C24XX_EXTINT0; // EXTINT0对应中断0~中断7
gpcon_offset = (irq - IRQ_EINT0) * ; //找到gpcon寄存器的相应位偏移量
extint_offset = (irq - IRQ_EINT0) * ; //找到extint寄存器的相应位偏移量
}
else if(... ...) //找到其它的EINT4~23的寄存器 /*将GPIO引脚设为中断引脚*/
value = __raw_readl(gpcon_reg);
value = (value & ~( << gpcon_offset)) | (0x02 << gpcon_offset); //相应位设置0x02
switch (type) //设置EXTINT0中断模式
{
case IRQT_NOEDGE: //未指定的中断模式
printk(KERN_WARNING "No edge setting!\n");
break; case IRQT_RISING: //上升沿触发,设置EXTINT0相应位为0x04
newvalue = S3C2410_EXTINT_RISEEDGE;
break; case IRQT_FALLING: //下降沿触发,设置EXTINT0相应位为0x02
newvalue = S3C2410_EXTINT_FALLEDGE;
break; case IRQT_BOTHEDGE: //双边沿触发,设置EXTINT0相应位为0x06
newvalue = S3C2410_EXTINT_BOTHEDGE;
break; case IRQT_LOW: //低电平触发,设置EXTINT0相应位为0x00
newvalue = S3C2410_EXTINT_LOWLEV;
break; case IRQT_HIGH: //高电平触发,设置EXTINT0相应位为0x01
newvalue = S3C2410_EXTINT_HILEV;
break;
default:
} /*更新EXTINT0相应位*/
value = __raw_readl(extint_reg);
value = (value & ~( << extint_offset)) | (newvalue << extint_offset); //相应位设置
__raw_writel(value, extint_reg); //向extint_reg写入value值
return ;
}
通过上面分析,就是将action->flags带入到desc[16]->chip->set_type里面,根据不同的中断来设置寄存器模式
2.request_irq()是注册中断,同样的卸载中断的函数是free_irq()
free_irq()也位于kernel/irq/ manage .c,函数原型如下:
free_irq(unsigned int irq, void *dev_id);
参数说明:
unsigned int irq:要卸载的中断号
void *dev_id:这个是要卸载的中断action下的哪个服务函数,
2.1 free_irq()代码如下:
void free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc;
struct irqaction **p;
unsigned long flags;
irqreturn_t (*handler)(int, void *) = NULL; WARN_ON(in_interrupt());
if (irq >= NR_IRQS)
return; desc = irq_desc + irq; //根据中断号,找到数组
spin_lock_irqsave(&desc->lock, flags);
p = &desc->action; //p指向中断里的action链表 for (;;) {
struct irqaction *action = *p; if (action) { //在action链表中找到与参数dev_id相等的中断服务函数
struct irqaction **pp = p;
p = &action->next;
if (action->dev_id != dev_id) //直到找dev_id才执行下面,进行卸载
continue;
*pp = action->next; //指向下个action成员,将当前的action释放掉
#ifdef CONFIG_IRQ_RELEASE_METHOD
if (desc->chip->release) //执行chip->release释放中断服务函数相关的东西
desc->chip->release(irq, dev_id);
#endif
if (!desc->action) { //判断当前action成员是否为空,表示没有中断服务函数
desc->status |= IRQ_DISABLED;
if (desc->chip->shutdown) //执行chip->shutdown关闭中断
desc->chip->shutdown(irq);
else //执行chip-> disable禁止中断
desc->chip->disable(irq);
} spin_unlock_irqrestore(&desc->lock, flags);
unregister_handler_proc(irq, action);
synchronize_irq(irq);
if (action->flags & IRQF_SHARED)
handler = action->handler;
kfree(action);
return; } printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);//没有找到要卸载的action成员 spin_unlock_irqrestore(&desc->lock, flags); return; } #ifdef CONFIG_DEBUG_SHIRQ
if (handler) { /* * It's a shared IRQ -- the driver ought to be prepared for it * to happen even now it's being freed, so let's make sure.... * We do this after actually deregistering it, to make sure that * a 'real' IRQ doesn't run in parallel with our fake */
handler(irq, dev_id); }
#endif
}
从上面分析,free_irq()函数主要通过irq和dev_id来找要释放的中断action
若释放的中断action不是共享的中断(为空),则执行:
*pp = action->next; //指向下个action成员,将当前的action释放掉
desc->chip->release(irq, dev_id); //执行chip->release释放中断服务函数相关的东西 desc->status |= IRQ_DISABLED; //设置desc[irq]->status标志位
desc->chip->[shutdown(irq)/ desible(irq)]; //关闭/禁止中断
若释放的中断action是共享的中断(还有其它中断服务函数)的话就只执行:
*pp = action->next; //指向下个action成员,将当前的action释放掉
desc->chip->release(irq, dev_id); //执行chip->release释放中断服务函数相关的东西
request_irq()和free_irq()分析完毕后,接下来开始编写中断方式的按键驱动
6.分析request_irq和free_irq函数如何注册注销中断的更多相关文章
- 6.分析request_irq和free_irq函数如何注册注销中断(详解)
上一节讲了如何实现运行中断,这些都是系统给做好的,当我们想自己写个中断处理程序,去执行自己的代码,就需要写irq_desc->action->handler,然后通过request_irq ...
- 第3阶段——内核启动分析之start_kernel初始化函数(5)
内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只是浅尝辄止的描述一下函数的功能,很多函数 ...
- 插件开发之360 DroidPlugin源码分析(五)Service预注册占坑
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52264977 在了解系统的activity,service,broa ...
- MediaInfo源代码分析 2:API函数
本文主要分析MediaInfo的API函数.它的API函数位于MediaInfo.h文件中的一个叫做MediaInfo的类中. 该类如下所示,部分重要的方法已经加上了注释: //MediaInfo类 ...
- 实验作业:使gdb跟踪分析一个系统调用内核函数
实验作业:使gdb跟踪分析一个系统调用内核函数(我使用的是getuid) 20135313吴子怡.北京电子科技学院 [第一部分] 根据视频演示的步骤,先做第一部分,步骤如下 ①更新menu代码到最新版 ...
- (转)分析kernel的initcall函数
分析kernel的initcall函数 来源: ChinaUnix博客 日期: 2008.07.19 21:24 (共有条评论) 我要评论 分析kernel的initcall函数Autho ...
- LInux设备驱动分析—— kmalloc和kzalloc函数
今晚在研究EVM5728开发板上面Linux系统的IIC设备驱动程序,偶然之间看到驱动程序中有一处使用了kzalloc函数,本人之前都是使用Linux内核提供的kmalloc / kfree函数来给设 ...
- day10作业(函数实现注册''')
在猜年龄的基础上编写登录.注册方法,并且把猜年龄游戏分函数处理,如 登录函数 注册函数 猜年龄函数 选择奖品函数 '''在猜年龄的基础上编写登录.注册方法,并且把猜年龄游戏分函数处理,如 2. 登录函 ...
- windowsAPI函数操作注册表实现软件开机自启
注册表的结构 注册表是一个数据库,它的结构同逻辑磁盘类似.注册表包含键(Key),它类似磁盘中的目录,注册表还包含键值(Value),它类似磁盘中的文件.一个键可以包含多个子健和键值,其中键值用于存储 ...
随机推荐
- tomcat 日志 按天自动分割 设定时任务定时清除
一.日志分割所需jar包 1.下载tomcat apache-tomcat-7.0.79.tar.gz 地址:http://www.apache.org/dist/tomcat/tomcat-7/ ...
- R读取excel文件乱码 read.xlsx() 解决方法
1. 参考[R语言]R读取含中文excel文件,read.xlsx乱码问题 该文章总结得很好,可以直接跳到最后看博主的总结. 2. 如果依旧是乱码那么用read.xlsx2()去读取excel文件, ...
- Hibernate 中Criteria Query查询详解【转】
当查询数据时,人们往往需要设置查询条件.在SQL或HQL语句中,查询条件常常放在where子句中.此外,Hibernate还支持Criteria查询(Criteria Query),这种查询方式把查询 ...
- 4.写一个控制台应用程序,接收一个长度大于3的字符串,完成下列功能: 1)输出字符串的长度。 2)输出字符串中第一个出现字母a的位置。 3)在字符串的第3个字符后面插入子串“hello”,输出新字符串。 4)将字符串“hello”替换为“me”,输出新字符串。 5)以字符“m”为分隔符,将字符串分离,并输出分离后的字符串。 */
namespace test4 {/* 4.写一个控制台应用程序,接收一个长度大于3的字符串,完成下列功能: 1)输出字符串的长度. 2)输出字符串中第一个出现字母a的位置. 3)在字符串的第3个字符 ...
- Eclipse中的Debug
一.Debug的基本过程 设置断点(双击待设断点左边行号处) 进入Debug模式(在待调试类上右键>调试方式,根据需求选择) 开始调试 二.Debug中的常用操作 继续执行[F8]:继续运行程序 ...
- SpringBoot集成MyBatis的分页插件PageHelper(回头草)
俗话说:好
- hashlib,configparser,logging模块
一.常用模块二 hashlib模块 hashlib提供了常见的摘要算法,如md5和sha1等等. 那么什么是摘要算法呢?摘要算法又称为哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长度 ...
- JMeter请求执行次数设置
今天介绍下JMeter如何控制请求执行次数 主要有两种方式: 方式一:通过循环控制器控制每个请求的执行次数 例如:脚本执行规律是这样的,login-->customerPage-->sea ...
- 一些方便的bash命令
1.文件名大小写转换: (1)大写转小写: ls | awk '{printf("mv %s %s\n", $0, tolower($0))|"sh"}' (2 ...
- 设计模式原则(3)--Dependency Inversion Principle(DIP)--依赖倒转原则
1.定义: 高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 抽象不应该依赖于细节,细节应当依赖于抽象.换言之,要针对接口编程,而不是针对实现编程. 2.使用场 ...