用今天写的一个按键驱动程序,来说明中断的处理过程:
代码如下:
#define GPH0CON 0xE0300C00
int major = 250;
int minor = 0;
int devno;
void *gphcon=NULL;
struct key_device{
char val;
struct cdev cdev;
struct semaphore sem;
wait_queue_head_t rwait;
struct fasync_struct *async_queue;
}key_device;
int key_open(struct inode *inode,struct file *file)
{
file->private_data = container_of(inode->i_cdev,struct key_device,cdev);
printk(KERN_INFO"opened\n");
return 0;
}
int key_fasync(int fd,struct file *file,int mode)
{
printk("%d\n",mode);
struct key_device *device = file->private_data;
return fasync_helper(fd,file,mode,&device->async_queue);
}
int key_release(struct inode *inode,struct file *file)
{
printk(KERN_INFO"closed\n");
key_fasync(-1,file,0);
return 0;
}
ssize_t key_read(struct file *file,char *__user buf,size_t count,loff_t *loff)
{
struct key_device *device;
device = file->private_data;
down(&device->sem);
while(device->val==0)
{
up(&device->sem);
if(file->f_flags & O_NONBLOCK)
return -EAGAIN;
if(wait_event_interruptible(device->rwait,(device->val>0)))
return -EAGAIN;
down(&device->sem);
}
if(copy_to_user(buf,&(device->val),count))
{
up(&device->sem);
return -EFAULT;
}
device->val = 0;
up(&device->sem);
return 1;
}
struct file_operations fops={
.open = key_open,
.release = key_release,
.read = key_read,
.fasync = key_fasync,
};
irqreturn_t handler(int irqno,void *dev) //中断处理
{
static long old_jiffies;
if((jiffies - old_jiffies) < 20)
return IRQ_HANDLED;
old_jiffies = jiffies;
switch (irqno)
{
case IRQ_EINT(1):
((struct key_device *)dev)->val = 1;
break;
case IRQ_EINT(2):
((struct key_device *)dev)->val = 2;
break;
case IRQ_EINT(3):
((struct key_device *)dev)->val = 3;
break;
case IRQ_EINT(4):
((struct key_device *)dev)->val = 4;
break;
case IRQ_EINT(6):
((struct key_device *)dev)->val = 5;
break;
default:
((struct key_device *)dev)->val = 6;
}
wake_up(&((struct key_device *)dev)->rwait );
if(((struct key_device *)dev)->async_queue)
kill_fasync(&((struct key_device*)dev)->async_queue,SIGIO,POLL_IN);
return IRQ_HANDLED;
}
int key_request_irq(void)//注册中断
{
int ret;
ret = request_irq(IRQ_EINT(1),handler,IRQF_DISABLED|IRQF_TRIGGER_FALLING,"key",&key_device);
if(ret) return ret;
ret = request_irq(IRQ_EINT(2),handler,IRQF_DISABLED|IRQF_TRIGGER_FALLING,"key",&key_device);
if(ret) goto _errno1;
ret = request_irq(IRQ_EINT(3),handler,IRQF_DISABLED|IRQF_TRIGGER_FALLING,"key",&key_device);
if(ret) goto _errno2;
ret = request_irq(IRQ_EINT(4),handler,IRQF_DISABLED|IRQF_TRIGGER_FALLING,"key",&key_device);
if(ret) goto _errno3;
ret = request_irq(IRQ_EINT(6),handler,IRQF_DISABLED|IRQF_TRIGGER_FALLING,"key",&key_device);
if(ret) goto _errno4;
ret = request_irq(IRQ_EINT(7),handler,IRQF_DISABLED|IRQF_TRIGGER_FALLING,"key",&key_device);
if(ret) goto _errno5;
printk(KERN_INFO"irq_register\n");
return ret;
_errno5: free_irq(IRQ_EINT(6),&key_device);
_errno4: free_irq(IRQ_EINT(4),&key_device);
_errno3: free_irq(IRQ_EINT(3),&key_device);
_errno2: free_irq(IRQ_EINT(2),&key_device);
_errno1: free_irq(IRQ_EINT(1),&key_device);
printk("irq_register\n");
return -1;
}
static int key_int(void)
{
devno = MKDEV(major,minor);
cdev_init(&key_device.cdev,&fops);
key_device.cdev.owner = THIS_MODULE;
cdev_add(&key_device.cdev,devno,1);
gphcon = ioremap(GPH0CON,4);
writel((readl(gphcon)&0x00f0000f)|0x22022220,gphcon);
printk(KERN_INFO"ready for irq\n");
key_request_irq();
init_waitqueue_head(&key_device.rwait);
init_MUTEX(&key_device.sem);
key_device.val = 0;
return 0;
}
static void key__exit()
{
iounmap(gphcon);
free_irq(IRQ_EINT(7),&key_device);
free_irq(IRQ_EINT(6),&key_device);
free_irq(IRQ_EINT(4),&key_device);
free_irq(IRQ_EINT(3),&key_device);
free_irq(IRQ_EINT(2),&key_device);
free_irq(IRQ_EINT(1),&key_device);
cdev_del(&key_device.cdev);
unregister_chrdev_region(devno,1);
}
module_init(key_int);
module_exit(key__exit);
MODULE_LICENSE("GPL");
(1)注册中断:
request_irq(irqno,handler,flag,"",char *)
此函数把中断号irqno,和中断处理函数handler结合起来。flag是标志位,说明了中断是否共用,中断方式(上升、下降、电平)
我认为最重要的是后面的char *,它可以做为参数传递为中断处理程序,一般传的是设备结构体。
返回值0代表成功,负数代表失败
(2)中断处理函数
irqreturn_t handler(int irqno,void *)
irqno是发生中断的中断号,void * 就是由此irqno注册时的传参(request_irq的参数char *),传参一般是设备结构体指针,所以还需要对他进行强转。
有中断号,有设备结构体,我们就能完成对设备结构体相应的操作。
返回值是枚举型。
在这个函数中,用到了前面说的异步通知,就是说,当发生按键时,在中断处理函数中会去kill_fasync,给应用进程发送SIGIO信号,在应用程序中,就回去读取按键值,这时候不会发生阻塞。
另一种方法是应用程序读写时,若没有按键按下,就加入等待队列,有按键按下时再去唤醒等待队列。
这两种方法驱动都有所实现。