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

2019-07-12 15:52发布

中断处理 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");