中断处理
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。在头文件
中明确地定义了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
#include
#include
#include
#include
#include // include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
", 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
", buttons_major);
return result;
}
buttons_setup_cdev(&buttons_cdev, 0, &s3c2440_buttons_fops);
printk("buttons deivce installed, with major %d
", buttons_major);
printk("The device name is : %s
", 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!
");
}
module_init(s3c2440_buttons_init);
module_exit(s3c2440_buttons_exit);
MODULE_AUTHOR("S");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("s3c2440 button intertupt driver");