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");

运行结果:

时间: 2024-10-30 22:27:55

Linux内核中断和异常分析(下)的相关文章

Linux内核中断和异常分析(中)

在linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线.所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,上次讲到单片机的时候,我就讲到了单片机中断的一些概念.我们现在来看一幅图,更好说明一个问题:      这下面的这幅图是51单片机的一个关于矩阵键盘的学习的一个proteus的仿真电路图. 其中P3.2和P3.3为外部中断引脚,当可编程控制器(51MCU)收到外部中断响应的时候,会执行一些特定的操作,当然这需要开发者去编写一个中断初始化

Linux内核中断和异常分析(上)

中断,通常被定义为一个事件.打个比方,你烧热水,水沸腾了,这时候你要去关掉烧热水的电磁炉,然后再去办之前手中停不下来的事情.那么热水沸腾就是打断你正常工作的一个信号机制.当然,还有其它的情况,我们以后再做分析.       中断也就是这样产生的,中断分为同步中断还有异步中断.       同步中断在Intel的手册中被称为异常,而异步中断被称作中断.打个比方在ARM处理器的异常种类就有不少,有未定义指令异常,软中断异常,快中断异常等等.异常是由程序错误产生的,或者是内核必须处理的异常条件产生的.

Linux的中断和异常扫盲笔记

广义的中断: 什么是中断: 所谓中断是指CPU对系统发生的某个事件作出的一种反应,让CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程:CPU在遇到某个事件时,暂停正在执行的程序,保留现场后自动地转去执行相应的处理程序,处理完该事件后再返回断点继续执行被"打断"的程序.事实上CPU对于中断和异常的具体处理机制本质上是完全一致的 即在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,此时,CPU暂时中止程序的执行转而处理这个新的情况的过程就叫做中断. 广义的中断包含异

Linux内核抢占实现机制分析【转】

Linux内核抢占实现机制分析  转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介绍了内核抢占和用户抢占的概念和区别,接着分析了不可抢占内核的特点及实时系统中实现内核抢占的必要性.然后分析了禁止内核抢占的情况和内核抢占的时机,最后介绍了实现抢占内核所做的改动以及何时需要重新调度.   [关键字]内核抢占,用户抢占,中断, 实时性,自旋锁,抢占时机,调度时机,schedule,p

Linux 内核中断内幕【转】

转自:https://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/index.html 本文对中断系统进行了全面的分析与探讨,主要包括中断控制器.中断分类.中断亲和力.中断线程化与 SMP 中的中断迁徙等.首先对中断工作原理进行了简要分析,接着详细探讨了中断亲和力的实现原理,最后对中断线程化与非线程化中断之间的实现机理进行了对比分析. 3评论 苏 春艳, 在读研究生 杨 小华 (normalnotebook@126.com),

【内核】linux内核启动流程详细分析【转】

转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程  arch/arm/kernel/head-armv.S  该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用是检查CPU ID, Architecture Type,初始化BSS等操作,并跳到start_kernel函数.在执行前,处理器应满足以下状态:  r0 - should be 0 r1 - unique

Linux内核协议栈-初始化流程分析

本文主要针对Linux-3.19.3版本的内核简单分析内核协议栈初始化涉及到的主要步骤和关键函数,不针对协议的解析以及数据包的处理流程做具体分析,后续有机会再详细分析 1.准备 Linux内核协议栈本身构建在虚拟文件系统之上,所以对Linux VFS不太了解的可以参考内核源码根目录下Documentation/filesystems/vfs.txt,另外,socket接口层,协议层,设备层的许多数据结构涉及到内存管理,所以对基本虚拟内存管理,slab缓存,页高速缓存不太了解的也可以查阅相关文档.

Linux内核态抢占机制分析

本文首先介绍非抢占式内核(Non-Preemptive Kernel)和可抢占式内核(Preemptive Kernel)的区别.接着分析Linux下有两种抢占:用户态抢占(User Preemption).内核态抢占(Kernel Preemption).然后分析了在内核态下:如何判断能否抢占内核(什么是可抢占的条件);何时触发重新调度(何时设置可抢占条件);抢占发生的时机(何时检查可抢占的条件);什么时候不能抢占内核.最后分析了2.6kernel中如何支持抢占内核. 1. 非抢占式和可抢占式

Linux内核中断引入用户空间(异步通知机制)【转】

转自:http://blog.csdn.net/kingdragonfly120/article/details/10858647 版权声明:本文为博主原创文章,未经博主允许不得转载.         当Linux内核空间发生中断后怎么使用户空间的应用程序运行相应的函数呢,当芯片有数据到来时内核会产生一个中断,但是怎样通知应用程序来取数据,以前这个问题一直困扰我很长时间,后来发现linux中有异步通知机制,在用户程序中用signal注册一个响应SIGIO信号的回调函数,然后在驱动程序中向该进程发