1、关于设备驱动中的中断问题
操作系统为了使得快速设备和慢速设备合适工作,需要中断来提高效率,一个外设要使用一个中断就必须注册中断号,获得跟这个中断号相关的一些资源,并且在中断发生的时候内核可以进行一些处理,例如:调用中断处理例程来真正的处理设备中断。Linux处理中断的方式很大程度上与它在用户空间处理信号的方式是一样的。
我们知道,从本质上讲,中断处理例程会和其他代码并发运行,这就会涉及到竞态和并发的问题。
接下来我们就来讲讲有关中断的实现和使用:
首先,我们需要注册一个中断,可如下注册,在
- "FONT-SIZE: 18px">int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
- const char *name, void *dev)
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev) 参数说明:
第一个参数:要申请的中断号,主要看硬件的连接方式决定;
第二个参数:中断处理例程,自己实现,在发生中断的时候调用,稍候详细说明;
第三个参数:中断管理的标志,是一个位掩码选项,例如可设置一个中断号在几个设备间共享,常见的就 是开发板上的ADC和Touch Screen共享ADC中断;
第四个参数:用来标示中断拥有者的名称,可自己设定;
第五个参数:用于共享的中断信号线,稍候详细说明。
调用request_irq的正确位置应该是设备第一次打开、硬件被告知产生中断之前。
接下来,注册后怎么注销呢?调用如下函数即可:
- "FONT-SIZE: 18px">void free_irq(unsigned int irq, void *dev);
void free_irq(unsigned int irq, void *dev); 这里参数的意义和上面是一样的。
调用free_irq的位置应该是最后一次关闭设备、硬件被告知不用再中断处理器后。
有关中断处理例程:
首先看看irq_handler_t的定义,显然它应该是一个自定义类型,定义在:include/linux/interrupt.h中:
- "FONT-SIZE: 18px">typedef irqreturn_t (*irq_handler_t)(int, void *);
typedef irqreturn_t (*irq_handler_t)(int, void *); 确实是一个类型定义,是一个函数指针类型,指向的函数有两个参数,一个irqreturn_t类型的返回值,这也是一个自定义类型,定义在include/linux/irqreturn.h中:
- "FONT-SIZE: 18px">typedef enum irqreturn irqreturn_t;
typedef enum irqreturn irqreturn_t; 确实是一个自定义类型,看看typedef就知道了,而且是一个枚举类型,接着看看这个枚举类型
- "FONT-SIZE: 18px">
-
-
-
-
-
- enum irqreturn {
- IRQ_NONE,
- IRQ_HANDLED,
- IRQ_WAKE_THREAD,
- };
-
/**
* enum irqreturn
* @IRQ_NONE interrupt was not from this device
* @IRQ_HANDLED interrupt was handled by this device
* @IRQ_WAKE_THREAD handler requests to wake the handler thread
*/
enum irqreturn {
IRQ_NONE,
IRQ_HANDLED,
IRQ_WAKE_THREAD,
};
这个枚举类型里面的值代表着中断处理例程的处理结果,也就是中断程序的返回值。OK,这下就清除多了!
2、中断处理的示例
这里是友善之臂的按键设备驱动程序,做了一些注释,对比这上面的理论部分就比较好理解了。
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #include
- #include
- #include
-
- #include
- #include
- #include
-
- #define DEVICE_NAME "buttons"
-
-
- struct button_irq_desc {
- int irq;
- int number;
- char *name;
- };
-
- static struct button_irq_desc button_irqs [] = {
- {IRQ_EINT( 0), 0, "KEY0"},
- {IRQ_EINT( 1), 1, "KEY1"},
- {IRQ_EINT( 2), 2, "KEY2"},
- {IRQ_EINT( 3), 3, "KEY3"},
- {IRQ_EINT( 4), 4, "KEY4"},
- {IRQ_EINT( 5), 5, "KEY5"},
- {IRQ_EINT(19), 6, "KEY6"},
- {IRQ_EINT(20), 7, "KEY7"},
- };
- 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 irqreturn_t buttons_interrupt(int irq, void *dev_id)
- {
- struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
- int down;
- int number;
- unsigned tmp;
-
- udelay(0);
- number = button_irqs->number;
- switch(number) {
-
- case 0: case 1: case 2: case 3: case 4: case 5:
- tmp = readl(S3C64XX_GPNDAT);
- down = !(tmp & (1<
- break;
-
- case 6: case 7:
- tmp = readl(S3C64XX_GPLDAT);
- down = !(tmp & (1 << (number + 5)));
- break;
- default:
- down = 0;
- }
-
-
- if (down != (key_values[number] & 1)) {
- key_values[number] = '0' + down;
-
-
- ev_press = 1;
- wake_up_interruptible(&button_waitq);
- }
-
-
- return IRQ_RETVAL(IRQ_HANDLED);
- }
-
-
- static int s3c64xx_buttons_open(struct inode *inode, struct file *file)
- {
- int i;
- int err = 0;
-
-
- for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
- if (button_irqs[i].irq < 0) {
- continue;
- }
-
- err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
- button_irqs[i].name, (void *)&button_irqs[i]);
- if (err)
- break;
- }
-
-
- if (err) {
- i--;
- for (; i >= 0; i--) {
- if (button_irqs[i].irq < 0) {
- continue;
- }
- disable_irq(button_irqs[i].irq);
- free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
- }
- return -EBUSY;
- }
-
- ev_press = 1;
-
- return 0;
- }
-
-
- static int s3c64xx_buttons_close(struct inode *inode, struct file *file)
- {
- int i;
-
-
- for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
- if (button_irqs[i].irq < 0) {
- continue;
- }
- free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
- }
-
- return 0;
- }
-
-
- static int s3c64xx_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 s3c64xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
- {
- unsigned int mask = 0;
- poll_wait(file, &button_waitq, wait);
- if (ev_press)
- mask |= POLLIN | POLLRDNORM;
-
- return mask;
- }
-
-
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE,
- .open = s3c64xx_buttons_open,
- .release = s3c64xx_buttons_close,
- .read = s3c64xx_buttons_read,
- .poll = s3c64xx_buttons_poll,
- };
-
-
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME,
- .fops = &dev_fops,
- };
-
- static int __init dev_init(void)
- {
- int ret;
-
- ret = misc_register(&misc);
-
- printk (DEVICE_NAME"\tinitialized\n");
-
- return ret;
- }
-
- static void __exit dev_exit(void)
- {
- misc_deregister(&misc);
- }
-
- module_init(dev_init);
- module_exit(dev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("FriendlyARM Inc.");
阅读(1125) | 评论(0) | 转发(0) |