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内核抢占实现机制.首先介 ...
随机推荐
- C/C++的mem函数和strcpy函数的区别和应用
mem系列函数是面试的时候常考的知识点,我们需要熟练掌握这三个函数的原理和代码实现,要能准确无误的写出代码. memcpy.memset和memset三个函数在使用过程中,均需包含以下头文件: //在 ...
- Nhibernate系列学习之(一) ORM and Nhibernate入门实例解析
最近框架项目需要,数据层想使用Nhibernate,代替传统的sql语句的写法,更加使用面向对象的思维来维护实体与数据库的这层关系映射(ORM),好在之前接触过Java时学习使用了Hibernate, ...
- Centos6.6上源码安装Nodejs V4版本
本来就是想在vps上装一个Ghost博客,这个博客依赖的是Nodejs,然后推荐的是V4版本.然后我就对着官网的步骤安装,发现根本没有Centos6 i386的资源了(64位的还是有的), 我只能在那 ...
- 剑指Offer——二分查找算法
剑指Offer--二分查找算法 前言 本片博文主要讲解查找算法的相关知识.重点介绍二分查找. 二分查找算法是在有序数组中用到的较为频繁的一种查找算法,在未接触二分查找算法时,最通用的一种做法是,对数组 ...
- JDK8帮助文档生成-笔记
JDK8 出来了,以前习惯了使用.CHM文件来查看API,现在想也这样,这里自己制作了一下,记录一下. 1.需要的工具: ①JD2CHM;②API文档③HTMLlHelper 遇到的问题主要是不知道去 ...
- Collections类解析
最常用的排序: 需要实现Comparable接口 1.什么是Comparable接口 此接口强行对实现它的每个类的对象进行整体排序.此排序被称为该类的自然排序 ,类的 compareTo 方法被称为它 ...
- C++对C的函数拓展 - 默认参数
1 C++中可以在函数声明时为参数提供一个默认值, 当函数调用时没有指定这个参数的值,编译器会自动用默认值代替 void myPrint(int x = 3) { printf("x:%d& ...
- 精通CSS+DIV网页样式与布局--CSS段落效果
在上一篇博文中,小编主要详细的介绍了CSS是如何控制文字的显示效果,随着需求的不断变更,那么我们如何对段落进行相关操作,以达到我们想要的效果呢,接下来,为了需要,小编继续来完善CSS对段落的控制的显示 ...
- Android开发模式之MVC,MVP和MVVM的简单介绍与区别
相信大家对MVC,MVP和MVVM都不陌生,作为三个最耳熟能详的Android框架,它们的应用可以是非常广泛的,但是对于一些新手来说,可能对于区分它们三个都有困难,更别说在实际的项目中应用了,有些时候 ...
- C语言中的内存分配
对于一个C语言程序而言,内存空间主要由以下几个部分组成: 1)程序代码区:用来存储程序的二进制代码 2)全局区/静态存储区 3)BSS段:用来存储未初始化的全局变量和静态变量. 4)栈区:存储局部变量 ...