当前位置: 代码迷 >> Linux/Unix >> Linux内核中断和错误分析(下)
  详细解决方案

Linux内核中断和错误分析(下)

热度:735   发布时间:2016-04-29 12:25:19.0
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");
运行结果: