【嵌入式Linux驱动程序-基础篇】- 中断处理

中断处理

1 中断的请求和释放

当设备需要中断功能时,应当向内核请求中断。如果驱动工程师没有通过请求中断的方式通知Linux内核需要使用中断,那么内核只会简单的应答并且忽略该中断。

1.1 请求中断线

请求中断线可以使内核知道外设应该使用哪一个中断号,哪一个中断处理函数。请求中断线在需要与外部设备交互时发生。Linux内核提供了request_irq()函数请求中断线。该函数如下:

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id);
  • irq表示要请求的中断号,中断号由开发板的硬件原理图决定。
  • handler表示要注册的中断处理函数指针。当中断发生时,内核会自动调用函数来处理中断。
  • irqflags表示关于中断处理的属性。内核通过这个标志可以决定该中断应该如何处理,在中断上半部和下半部机制中,会详细讲解这方面的知识。
  • devname表示设备的名字,该名字会在/proc/interrupts中显示。interrupts记录了设备和中断号之间的对应关系。
  • dev_id这个指针是为了共享中断线而设立的。如果不需要共享中断线,那么只要将该指针设为NULL即可。

request_irq()函数成功返回0,错误时返回-EINVAL或者-ENOMEM。在头文件<include/asm-generic/Errno-base.h>中明确地定义了EINVAL和ENOMEM宏。

#define ENOMEM    12 // 内存溢出
#define EINVAL    22 // 无效参数

ENOMEMbiao表示内存不足。嵌入式系统由于内存资源有限,经常会发生这样的错误。EINVAL宏表示无效的can数。如果出现这个返回值,那么就应该查看传递给request_irq()的参数是否正确。

1.2 释放中断线

当设备不再需要中断线时,我们应当释放中断线,毕竟中断线资源是十分紧缺的,以便供其他设备使用。

释放中断线的实现函数是free_irq()

void free_irq(unsigned int irq, void *dev_id);
  • irq表示释放申请的中断号
  • dev_id这个指针是为了共享中断线而设立的。该参数将在“共享中断”一节中讲述。

需要注意的是,只有中断线被释放了,该中断才能被其他设备使用。

参考例程如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <mach/regs-gpio.h> // include <mahc/gpio-nrs.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
 

int buttons_major = 0; 
static struct cdev buttons_cdev;
static int press_cnt[] = {0,0,0,0};
static volatile int ev_press = 0;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //初始化一个等待队列头key_waitq
struct button_irq_desc{
	int irq;								// 中断号
	unsigned long flags;		// 中断标志,用来定义中断的触发方式
	char *name;							// 中断名字
};
static struct button_irq_desc button_irqs[] =
{
	{IRQ_EINT15, IRQF_TRIGGER_FALLING, "KEY5"},	// K1
	{IRQ_EINT11, IRQF_TRIGGER_FALLING, "KEY2"},	// K2
	{IRQ_EINT13, IRQF_TRIGGER_FALLING, "KEY3"},	// K3
	{IRQ_EINT14, IRQF_TRIGGER_FALLING, "LEY4"},	// K4
};

static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
	volatile int *press_cnt = (volatile int *)dev_id;
	
	*press_cnt = *press_cnt + 1;
	ev_press = 1;
	wake_up_interruptible(&button_waitq);
	return IRQ_RETVAL(IRQ_HANDLED);	
	
}

static int s3c2440_buttons_open(struct inode *inode, struct file *file)
{
	int i;
	int err;
	for(i = 0; i < sizeof(button_irqs) / sizeof(button_irqs[0]);i++)	{
		
		err = request_irq(button_irqs[i].irq, 
											buttons_interrupt, 
											button_irqs[i].flags,
											button_irqs[i].name,
											(void *)&press_cnt[i]);
		if(err)
			break;
		
	}
	// 请求失败,则释放已请求的中断
	if(err){
		i--;
		for(; i >= 0; i--)
			free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
		return -EBUSY;
	}
	return 0;
}


static int s3c2440_buttons_close(struct inode *inode, struct file *file)
{
	int i;
	for(i = 0; i < sizeof(button_irqs) / sizeof(button_irqs[0]); i++)
	{
			free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
	}
	return 0;
}

static int s3c2440_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
	unsigned long err;
	wait_event_interruptible(button_waitq, ev_press);	
	ev_press = 0;
	err = copy_to_user(buff, (const void *)press_cnt, min(sizeof(press_cnt), count));
//	memset((void *)press_cnt, 0, sizeof(press_cnt));
	return err ? -EFAULT:0;
}




static struct file_operations s3c2440_buttons_fops = {
	.owner = THIS_MODULE,
	.open = s3c2440_buttons_open,
	.release = s3c2440_buttons_close,
	.read = s3c2440_buttons_read,
};


static void buttons_setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
	int err, devno = MKDEV(buttons_major, minor);
	
	cdev_init(dev, fops);
	dev->owner = THIS_MODULE;
	dev->ops = fops;
	err = cdev_add(dev, devno, 1);
	if(err)
		printk(KERN_NOTICE "Error %d adding buttons%d\n", err, minor);
}

static int __init s3c2440_buttons_init(void)
{
	int result;
	dev_t  dev = MKDEV(buttons_major, 0);
	char dev_name[] = "button";

	if(buttons_major)
		result = register_chrdev_region(dev, 1, dev_name);
	else{
		result = alloc_chrdev_region(&dev, 0, 1, dev_name);
		buttons_major = MAJOR(dev);
	}
	if(result < 0)
	{
		printk(KERN_WARNING "leds: can't get major %d\n", buttons_major);
		return result;
	}
	buttons_setup_cdev(&buttons_cdev, 0, &s3c2440_buttons_fops);
	printk("buttons deivce installed, with major %d\n", buttons_major);
	printk("The device name is : %s\n", dev_name);	
	return 0;
}


static void __exit s3c2440_buttons_exit(void)
{
	cdev_del(&buttons_cdev);
	unregister_chrdev_region(MKDEV(buttons_major, 0), 1);
	printk("led device unistalled!\n");
}

module_init(s3c2440_buttons_init);
module_exit(s3c2440_buttons_exit);


MODULE_AUTHOR("S");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("s3c2440 button intertupt driver");

猜你喜欢

转载自blog.csdn.net/santapasserby/article/details/81664937