设备驱动中的中断问题及实例解析
1、关于设备驱动中的中断问题
操作系统为了使得快速设备和慢速设备合适工作,需要中断来提高效率,一个外设要使用一个中断就必须注册中断号,获得跟这个中断号相关的一些资源,并且在中断发生的时候内核可以进行一些处理,例如:调用中断处理例程来真正的处理设备中断。Linux处理中断的方式很大程度上与它在用户空间处理信号的方式是一样的。
我们知道,从本质上讲,中断处理例程会和其他代码并发运行,这就会涉及到竞态和并发的问题。
接下来我们就来讲讲有关中断的实现和使用:
首先,我们需要注册一个中断,可如下注册,在
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
参数说明:
第一个参数:要申请的中断号,主要看硬件的连接方式决定;
第二个参数:中断处理例程,自己实现,在发生中断的时候调用,稍候详细说明;
第三个参数:中断管理的标志,是一个位掩码选项,例如可设置一个中断号在几个设备间共享,常见的就 是开发板上的ADC和Touch Screen共享ADC中断;
第四个参数:用来标示中断拥有者的名称,可自己设定;
第五个参数:用于共享的中断信号线,稍候详细说明。
调用request_irq的正确位置应该是设备第一次打开、硬件被告知产生中断之前。
接下来,注册后怎么注销呢?调用如下函数即可:
void free_irq(unsigned int irq, void *dev);
这里参数的意义和上面是一样的。
调用free_irq的位置应该是最后一次关闭设备、硬件被告知不用再中断处理器后。
有关中断处理例程:
首先看看irq_handler_t的定义,显然它应该是一个自定义类型,定义在:include/linux/interrupt.h中:
typedef irqreturn_t (*irq_handler_t)(int, void *);
确实是一个类型定义,是一个函数指针类型,指向的函数有两个参数,一个irqreturn_t类型的返回值,这也是一个自定义类型,定义在include/linux/irqreturn.h中:
typedef enum irqreturn irqreturn_t;
确实是一个自定义类型,看看typedef就知道了,而且是一个枚举类型,接着看看这个枚举类型
/*** enum irqreturn* @IRQ_NONE interrupt was not from this device* @IRQ_HANDLED interrupt was handled by this device* @IRQ_WAKE_THREAD handler requests to wake the handler thread*/
enum irqreturn {IRQ_NONE,IRQ_HANDLED,IRQ_WAKE_THREAD,
};
这个枚举类型里面的值代表着中断处理例程的处理结果,也就是中断程序的返回值。OK,这下就清除多了!
2、中断处理的示例
这里是友善之臂的按键设备驱动程序,做了一些注释,对比这上面的理论部分就比较好理解了。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>#define DEVICE_NAME "buttons"/*用于描述每个按键中断的结构体*/
struct button_irq_desc {int irq;int number;char *name;
};static struct button_irq_desc button_irqs [] = {{IRQ_EINT( 0), 0, "KEY0"},{IRQ_EINT( 1), 1, "KEY1"},{IRQ_EINT( 2), 2, "KEY2"},{IRQ_EINT( 3), 3, "KEY3"},{IRQ_EINT( 4), 4, "KEY4"},{IRQ_EINT( 5), 5, "KEY5"},{IRQ_EINT(19), 6, "KEY6"},{IRQ_EINT(20), 7, "KEY7"},
};
static volatile char key_values [] = {'0', '0', '0', '0', '0', '0', '0', '0'};/*涉及到中断处理例程,所以就需要注意竟态和并发问题*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);/*唤醒等待队列的条件,可以是任意的布尔表达式*/
static volatile int ev_press = 0;/*中断处理例程*/
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;int down;int number;unsigned tmp;udelay(0);number = button_irqs->number;switch(number) {/*key0~key5使用的是EINT0~EINT5,看芯片手册就知道是GPN口上的功能*/case 0: case 1: case 2: case 3: case 4: case 5:tmp = readl(S3C64XX_GPNDAT);down = !(tmp & (1<<number));break;/*key6~key7使用EINT19~EINT20,使用GPL引脚*/case 6: case 7:tmp = readl(S3C64XX_GPLDAT);down = !(tmp & (1 << (number + 5)));break;default:down = 0;}/*判断是否有按键按下*/if (down != (key_values[number] & 1)) {key_values[number] = '0' + down;/*唤醒等待队列,此刻实际上是唤醒读进程*/ev_press = 1;wake_up_interruptible(&button_waitq);}/*该返回值代表中断确实真的处理了该中断*/return IRQ_RETVAL(IRQ_HANDLED);
}static int s3c64xx_buttons_open(struct inode *inode, struct file *file)
{int i;int err = 0;for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {continue;}/*申请中断号*/err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]);if (err)break;}/*如果在申请中断的时候发生错误,那么就释放掉已申请的中断号*/if (err) {i--;for (; i >= 0; i--) {if (button_irqs[i].irq < 0) {continue;}disable_irq(button_irqs[i].irq);free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);}return -EBUSY;}ev_press = 1;return 0;
}static int s3c64xx_buttons_close(struct inode *inode, struct file *file)
{int i;/*释放获得的中断资源*/for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {continue;}free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);}return 0;
}static int s3c64xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{unsigned long err;if (!ev_press) {if (filp->f_flags & O_NONBLOCK) //非阻塞读取return -EAGAIN;else wait_event_interruptible(button_waitq, ev_press); //阻塞读取,则进入休眠,等待发生中断时由中断例程唤醒}ev_press = 0;err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count));return err ? -EFAULT : min(sizeof(key_values), count);
}/*用于轮询操作*/
static unsigned int s3c64xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{unsigned int mask = 0;poll_wait(file, &button_waitq, wait);if (ev_press)mask |= POLLIN | POLLRDNORM;/*返回标示是否可立即无阻塞执行的位掩码*/return mask;
}static struct file_operations dev_fops = {.owner = THIS_MODULE,.open = s3c64xx_buttons_open,.release = s3c64xx_buttons_close, .read = s3c64xx_buttons_read,.poll = s3c64xx_buttons_poll,
};/*定义misc设备*/
static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,
};static int __init dev_init(void)
{int ret;ret = misc_register(&misc);printk (DEVICE_NAME"\tinitialized\n");return ret;
}static void __exit dev_exit(void)
{misc_deregister(&misc);
}module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
设备驱动中的中断问题及实例解析相关推荐
- Linux设备驱动中的并发控制总结
并发(concurrency)指的是多个执行单元同时.并行被执行.而并发的执行单元对共享资源(硬件资源和软件上的全局.静态变量)的访问则容易导致竞态(race conditions). SMP是一 ...
- Linux 设备驱动中的 I/O模型(一)—— 阻塞和非阻塞I/O
在前面学习网络编程时,曾经学过I/O模型 Linux 系统应用编程--网络编程(I/O模型),下面学习一下I/O模型在设备驱动中的应用. 回顾一下在Unix/Linux下共有五种I/O模型,分别是: ...
- Linux 设备驱动中的 I/O模型(二)—— 异步通知和异步I/O
阻塞和非阻塞访问.poll() 函数提供了较多地解决设备访问的机制,但是如果有了异步通知整套机制就更加完善了. 异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态 ...
- linux编写驱动后write已杀死_《Linux4.0设备驱动开发详解》笔记--第九章:Linux设备驱动中的异步通知与同步I/O...
在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问.因此,使用无阻塞I/O的应用程序无需轮询设备是否可访问,而阻塞访问也可以被类似"中断"的异步通 ...
- Linux 设备驱动中的阻塞与非阻塞 I/O
阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作.被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足.而非阻塞操作的进程在不能进行设备操作时 ...
- linux 两个驱动 竞态,第7章 Linux设备驱动中的并发控制之一(并发与竞态)
本章导读 Linux设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发的访问会导致竞态(竞争状态). Linux提供了多种解决竞态问题的方式,这些方式适合不同的应用场景. 7.1讲解了并 ...
- Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用
关与设备树的概念,我们在Exynos4412 内核移植(六)-- 设备树解析 里面已经学习过,下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构,设备树源(De ...
- 字符设备驱动(四)按键中断
目录 字符设备驱动(四)按键中断 硬件IO 程序设计 中断配置 中断关闭 中断函数 共享中断号 测试 完整的程序 title: 字符设备驱动(四)按键中断 tags: linux date: 2018 ...
- Linux字符设备驱动中container_of宏的作用
Linux字符设备驱动中container_of宏的作用 首先看看这个宏的原型: container_of(ptr,type,member) 功能:根据一个结构体变量中的一个成员变量的指针来获取指向整 ...
最新文章
- Adam又要“退休”了?耶鲁大学团队提出AdaBelief,却引来网友质疑
- ios 替换数组中元素_ios可变数组的所有操作
- Java对MySQL数据库进行连接、查询和修改【转载】
- opengl 纹理贴到对应的位置_一步步学OpenGL(27) -《公告牌技术与几何着色器》
- jQuery 源码解析笔记(一)
- 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn
- 纯CSS3实现打火机火焰动画
- c语言宽度限定词,C语言中限定词的深层分析
- 【Objective-C】Objective-C语言的动态性
- 不可见的unicode字符
- 软件测试面试题:Jmeter怎么录制脚本,怎么过滤,线程组有哪些内容?
- 用puttygen工具把私钥id_rsa转换成公钥id_rsa.ppk
- 二路归并排序的C++实现
- java 分布式系统架构_什么是分布式系统!以及分布式系统架构的优缺点
- JavaScript分解质因数
- 多种方法教你如何让手机免费上网
- python3 时区 时间戳 指定输入时间为东八区时间、北京时间
- 如何查找qq看点里用户的qq号
- 对浏览器村的第二次采访
- 计算机毕业设计Java藏宝阁游戏交易系统(源码+系统+mysql数据库+lw文档)
热门文章
- 多益网络提前批笔试题
- 用计算机做路由器,附录:如何用PC主机当作路由器?_华硕 Maximus VI Impact_主板导购-中关村在线...
- java 面试 sql_sql 面试问题
- 拓扑学 -- from BBS 水木清华站
- 计算机软件快速启动,效率神器!1秒打开电脑任何软件,推荐2款强大的快速启动器...
- Transformer自监督学习(2021) - SiT: Self-supervised vIsion Transformer
- 东芝核电及半导体去向令日美政府忧心忡忡
- 【Go语言写界面】一、使用xcgui完成go语言第一个软件界面
- ansible安装Mysql 5.7.30
- win10 记事本居然默认改成 Unix (LF)