Linux内核中断和异常分析(下)
这节,我们继续上,中(以前的日志有)篇目进行分析,结合一个真实的驱动案例来描述linux内核中驱动的中断机制,首先我们先了解一下linux内核中提供的中断接口。
这个接口我们需要包含一个头文件:#include <linux/interrupt.h>
在中断接口中,最重要的是以下的接口函数:
1、这个是请求中断函数
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) irq: 中断号 arch/arm/plat-s3c64xx/include/plat/irqs.h handler: 中断处理函数 irqreturn_t handler(int irq, void *dev_id); irqreturn_t: See include/linux/irqreturn.h irqflags: See line 21-59 in include/linux/interrupt.h 使用IRQF_SHARED共享irq时, irqflags必须相同 如: request_irq(IRQ_EINT(0), handler1, IRQF_TRIGGER_FALLING | IRQF_SHARED, "dev1", &dev1); request_irq(IRQ_EINT(0), handler2, IRQF_TRIGGER_FALLING | IRQF_SHARED, "dev2", &dev2); devname: 设备名, cat /proc/interrupts dev_id: 发生中断时将dev_id传递给handler函数, irqflags含有IRQF_SHARED时dev_id不能为NULL, 并且要保证唯一 dev_id一般采用当前设备的结构体指针
2、释放中断
void free_irq ( unsigned int irq, void * dev_id); 释放匹配irq和dev_id的中断, 如果irq有多个相同的dev_id, 将释放第一个 So, 共享中断的dev_id不是唯一时, 可能会释放到其它设备的中断
3、开启中断
void enable_irq(unsigned int irq); 开启irq号中断
4、关闭中断
void disable_irq(unsigned int irq); 关闭irq号中断
5、关闭当前CPU中断并保存在flag中去
void local_irq_save(unsigned long flags);
6、恢复flag到CPU中去
void local_irq_restore(unsigned long flags); 恢复flags到当前CPU
7、关闭当前的CPU中断
void local_irq_disable(void);
8、开始当前的CPU中断
void local_irq_enable(void);
接下来我们来看一个按键中断的例子,这个例子是基于Tiny4412按键驱动的源码:
#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/sched.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 <linux/gpio.h> #include <mach/map.h> #include <mach/gpio.h> #include <mach/regs-clock.h> #include <mach/regs-gpio.h> //设备名称 #define DEVICE_NAME "buttons" struct button_desc { int gpio; int number; char *name; struct timer_list timer; }; //定义按键相关的寄存器 static struct button_desc buttons[] = { { EXYNOS4_GPX3(2), 0, "KEY0" }, { EXYNOS4_GPX3(3), 1, "KEY1" }, { EXYNOS4_GPX3(4), 2, "KEY2" }, { EXYNOS4_GPX3(5), 3, "KEY3" }, }; //存储按键的键值 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 void tiny4412_buttons_timer(unsigned long _data) { struct button_desc *bdata = (struct button_desc *)_data; int down; int number; unsigned tmp; //获取按键的值 tmp = gpio_get_value(bdata->gpio); //判断是否为低电平 down = !tmp; printk(KERN_DEBUG "KEY %d: %08x\n", bdata->number, down); number = bdata->number; //如果此时不为低电平,中断处理进入休眠状态,一般有事件产生就会立即被唤醒 if (down != (key_values[number] & 1)) { key_values[number] = '0' + down; ev_press = 1; //中断休眠 wake_up_interruptible(&button_waitq); } } //按键中断处理函数 //irq:中断号 //dev_id:设备ID号 static irqreturn_t button_interrupt(int irq, void *dev_id) { struct button_desc *bdata = (struct button_desc *)dev_id; //注册一个定时器 mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(40)); //返回一个中断句柄 return IRQ_HANDLED; } //按键打开函数 //inode : 节点 //file : 打开文件的形式 static int tiny4412_buttons_open(struct inode *inode, struct file *file) { int irq; int i; int err = 0; //循环遍历四个IO口,看看有哪个按键被按下了 for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; //初始化定时器 setup_timer(&buttons[i].timer, tiny4412_buttons_timer, (unsigned long)&buttons[i]); //设置GPIO为中断引脚,也就是对应那四个按键 irq = gpio_to_irq(buttons[i].gpio); err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, //请求中断处理函数 buttons[i].name, (void *)&buttons[i]); if (err) break; } if (err) { i--; for (; i >= 0; i--) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); disable_irq(irq); //关中断 free_irq(irq, (void *)&buttons[i]);//释放中断 del_timer_sync(&buttons[i].timer);//删除一个定时器 } return -EBUSY; } ev_press = 1; return 0; } //按键关闭处理函数 static int tiny4412_buttons_close(struct inode *inode, struct file *file) { int irq, i; for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; //同样的,这里也是释放 irq = gpio_to_irq(buttons[i].gpio); free_irq(irq, (void *)&buttons[i]); <span style="white-space:pre"> </span>//删除一个定时器 del_timer_sync(&buttons[i].timer); } return 0; } //读取按键的键值函数 static int tiny4412_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 tiny4412_buttons_poll( struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; <span style="white-space:pre"> </span>//非阻塞型等待 poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } //驱动文件操作结构体成员初始化 static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = tiny4412_buttons_open, .release = tiny4412_buttons_close, .read = tiny4412_buttons_read, .poll = tiny4412_buttons_poll, }; //注册杂类设备的结构体成员初始化 static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, //这里就是把上面那个文件操作结构体的成员注册到杂类操作这里 }; //按键驱动初始化 static int __init button_dev_init(void) { int ret; //先注册一个杂类设备 //这相当于让misc去管理open ,read,write,close这些接口 ret = misc_register(&misc); // printk(DEVICE_NAME"\tinitialized\n"); return ret; } //按键驱动注销 static void __exit button_dev_exit(void) { //注销一个杂类设备驱动 misc_deregister(&misc); } module_init(button_dev_init); module_exit(button_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yang.yuanxin");
运行结果:
Linux内核中断和异常分析(下)的更多相关文章
- Linux内核中断和异常分析(中)
在linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线.所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,上次讲到单片机的时候,我就讲到了单片机 ...
- Linux内核中断和异常分析(上)
中断,通常被定义为一个事件.打个比方,你烧热水,水沸腾了,这时候你要去关掉烧热水的电磁炉,然后再去办之前手中停不下来的事情.那么热水沸腾就是打断你正常工作的一个信号机制.当然,还有其它的情况,我们以后 ...
- Linux内核--网络栈实现分析(十一)--驱动程序层(下)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7555870 更多请查看专栏,地 ...
- Linux内核--网络栈实现分析(七)--数据包的传递过程(下)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地 ...
- Linux 内核中断内幕
转自:http://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/index.html#resources Linux 内核中断内幕 ...
- Linux内核--网络栈实现分析(二)--数据包的传递过程--转
转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的”(上 ...
- Linux内核态抢占机制分析(转)
Linux内核态抢占机制分析 http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...
- Linux内核哈希表分析与应用
目录(?)[+] Linux内核哈希表分析与应用 Author:tiger-johnTime:2012-12-20mail:jibo.tiger@gmail.comBlog:http:// ...
- Linux内核抢占实现机制分析【转】
Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...
随机推荐
- 15 ActionBar 总结
ActionBar 一, 说明 是一个动作栏 是窗口特性 提供给用户动作 导航模式 可以适配不同的屏幕 二, ActionBar 提供的功能 1. 显示菜单项 always:总是展示到ActionBa ...
- Dynamics CRM2015 The plug-in type does not exist in the specified assembly问题的解决方法
在用插件工具PluginProfiler调试时,报"The plug-in type xxxx does not exist in the specified assembly" ...
- GCD API 理解 (一)
资料先行 GCD 深入理解:第一部分 GCD 深入理解:第二部分 以上两篇文章是关于GCD讲的比较好的文章,翻译自raywenderlich,该网站有很多关于iOS 开发的优秀文章. 引子 iOS 开 ...
- ExpandableListView简单应用及listview模拟ExpandableListView
首先我们还是来看一些案例,还是拿搜狐新闻客户端,因为我天天上下班没事爱看这个东东,上班又没时间看新闻,上下班路途之余浏览下新闻打发时间嘛. 看这个效果挺棒吧,其实实现起来也不难,我 ...
- JSP自定义标签之简单标签入门
在sun官方文档上有下面这样一段话. 官方文档声明 public interface SimpleTag extends JspTag Interface for defining Simple Ta ...
- Uva - 1589 - Xiangqi
Xiangqi is one of the most popular two-player board games in China. The game represents a battle bet ...
- UNIX环境高级编程——网络编程常用函数及结构
IP地址的转换 #include <arpa/inet.h> int inet_aton(const char *strptr, struct i ...
- 最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)
===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...
- C# 运行时序列化
一. 序列化与反序列的作用 为什么要有序列化呢,考虑下面这种情况,在WINFORM或者更为方便的WPF工程中,当我们进行UI设计时,可以随意的将一个控件剪切/张贴到另外一个地方.操作方便的背后是什么在 ...
- 我眼中的Linux设备树(三 属性)
三 属性(property)device_type = "memory"就是一个属性,等号前边是属性,后边是值.节点是一个逻辑上相对独立的实体,属性是用来描述节点特性的,根据需要一 ...