中断处理
1:外设的处理速度一般慢于CPU。2:CPU不能一直等待外部事件,所以设备必须有一种方法来通知CPU它的工作进度,这个方法就是中断,外设与CPU信息交互的机制,提高CPU利用率。处理之外还有查询,但是查询会一直占有CPU资源,导致CPU低利用率,好处是实现简单。
Linux系统中为设备实现中断处理程序,需要做两件事,1:向内核注册中断,让Linux内核知道中断的存在。2:实现中断处理函数。
一:中断的注册:int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *devname, void *dev_id) 返回0表示成功,让内核找到中断函数调用。
参数:irq:中断号, irq_handler_t handler:中断处理函数,flags: 中断标志
Devname:设备名,中断处理程序属于的设备,dev_id:共享中断时使用
flag中断标志:1:IRQF_DISABLED 如果设置这个标志,表示是一个“快中断”处理程序,如果没有设置,那么是一个“慢中断”处理程序。
2:IRQF_SHARED该位表示中断可在设备间共享。
快速/慢速中断:
快速中断处理程序是不允许发生中断嵌套的,慢速中断允许嵌套。举例:在CPU运行过程中突然某个点产生串口中断,进入串口中断处理程序,处理到某个时间点后产生并口中断,这时会暂停串口中断处理函数处理,转向处理并口中断,执行完后,继续处理串口中断,结束后退回CPU,这就是嵌套情况,在串口执行过程中嵌入并口中断。如果中断嵌套发生就是慢速中断,如果是快速中断情况,在产生并口中断时,丢掉并口中断。默认慢速中断。
共享中断:早期中断源不同的情况,讲不同中断设备挂到同一个中断线上,有相同中断号,通过request_irq函数注册,flags指定IRQF_SHARED,dev_id必须是唯一的,不能使用中断的处理程序disable_irq(unsigned int irq)。
二:中断处理函数:中断处理程序是在中断上下文中运行。流程
void short_sh_interrupt (int irq, void *dev_id, struct pt_regs *reg) {
//判断该设备是否产生中断,避免共享中断产生的误调用其他设备
value = inb(short_base);
if (!(value & 0x80 )) return ;
//清除中断位,中断位表明该设备是否发生中断
Outb(value & 0x7f short_base) ;
//中断处理,通常是接受数据
。。。。。。。。。
//唤醒等待数据的进程,可能有进程因发生中断而阻塞
wake_up_interruptible (&short_queue) ;
}
三:释放中断 void free_irq (unsigned int irq,,void *dev_id)
按键中断驱动程序
S3c2440的GPIO_G0,GPIO_G3,GPIO_G5,GPIO_G6,GPIO_G7,GPIO_G11作为输入口,读取按键状态,这6个I/O口分别使用外部中断EINT8,EINT11, EINT13,EINT14, EINT15,EINT19。当按键松开时,I/O处于高电平,当按键按下时,I/O处于低电平,
#define DEVICE_NAME "buttons"
struct button_irq_desc {
int irq;
int pin;
int pin_setting;
int number;
char *name;
};
#if !defined (CONFIG_QQ2440_BUTTONS)
static struct button_irq_desc buttons_irqs[] = {
{IRQ_EINT8,s3c2410_GPG0,s3c2410_GPG0_EINT8,0,"KEY0"},
{IRQ_EINT11,s3c2410_GPG3,s3c2410_GPG3_EINT11,1,"KEY1"},
{IRQ_EINT13,s3c2410_GPG5,s3c2410_GPG5_EINT13,2,"KEY2"},
{IRQ_EINT15,s3c2410_GPG7,s3c2410_GPG7_EINT15,3,"KEY3"},
{IRQ_EINT14,s3c2410_GPG6,s3c2410_GPG6_EINT14,4,"KEY4"},
{IRQ_EINT19,s3c2410_GPG11,s3c2410_GPG11_EINT19,5,"KEY5"},
};
#else
static struct button_irq_desc button_irqs[] = {
{IRQ_EINT8,s3c2410_GPG0,s3c2410_GPG0_EINT8,0,"KEY0"},
{IRQ_EINT11,s3c2410_GPG3,s3c2410_GPG3_EINT11,1,"KEY1"},
{IRQ_EINT13,s3c2410_GPG5,s3c2410_GPG5_EINT13,2,"KEY2"},
{IRQ_EINT15,s3c2410_GPG7,s3c2410_GPG7_EINT15,3,"KEY3"},
{IRQ_EINT14,s3c2410_GPG6,s3c2410_GPG6_EINT14,4,"KEY4"},
{IRQ_EINT19,s3c2410_GPG11,s3c2410_GPG11_EINT19,5,"KEY5"},
};
#endif
static volatile char key_values[] = {'0','0','0','0','0','0'};
static DECLARE_WAIT_QUEUE_HEAD (button_waitq);
static volatile int env_press = 0;
static int s3c24xx_button_read (struct file *filp, char __user *buff, size_t count, loff_t *offp)
{//从按键中读数据。
unsigned long err;
if(!ev_press) {
//ev_press为0,无数据可读,驱动程序默认按照阻塞方式处理
if(filp->flag & O_NONBLOCK)
//O_NONBLOCK标志用户指定不阻塞,如果有O_NONBLOCK,返回
return -EAGAIN;
else
wait_event_interrupt(button_waitq, ev_press);
//如果没设置O_NONBLOCK,程序阻塞在等待队列button_waitq
}
//有数据往下走
ev_press = 0;
err = copy_to_user(buf, (const void *)key_values, min(sizeof(key_value), count));
//通过按键的键值key_values判断哪个键,读走键值
return err? = -EFAULT:min(sizeof(key_values),count);
}
static unsigned int s3c24xx_buttons_poll (struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait);//用poll_wait指明使用等待队列button_waitq
if(ev_press) //ev_press为1时按键按下,有数据可读
mask |= POLLIN | POLLRDNORM; //按键可读掩码
return mask;
}
static irqretum_t buttons_interrupt (int irq, void *dev_id)
{
struct button_irq_desc *button_irqs = (struct button_irq_desc*)dev_id;
int down;
down = !s3c2410_gpio_getpin(button_irq_pin);
//获取gpio数据寄存器的值,按下时低电平取反down=1
if(down != (key_values[button_irq->number] & 1)) { //当按下时 1 != 0, 继续
key_values[button_irq->number] = '0' + down;
ev_press = 1;
wake_up_interruptable(&button_waitq);
//不是ev_press改变就唤醒,改变是唤醒条件,真正唤醒的是wake_up_interruptable
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static int s3c24xx_buttons_open (struct inode *inode, struct file *file)
{
int i;
int err;
for(i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
if(button_irq[i] < 0) {
continue;
}//按键按下高->低电平
err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
//中断双沿触发,按键按下弹起都会触发,按键按下和弹起都有数据读。
button_irq[i].name, (void*)&button_irqs[i]); //为6个按键分别中断处理程序
if(err)
break;
}
if(err){
i--;
for(; i>=0; i--){
if(button_irqs[i].irq < 0) {
continue;
}
disable_irq(button_irq[i].irq);
free_irq(button_irq[i].irq, (void *)&button_irq[i]);
}
return -EBUSY;
}
ev_press = 1;
return 0;
}
static int s3c24xx_buttons_close (struct inode *inode, struct file *file)
{
int i;
int err;
for(i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
if(button_irq[i] < 0) {
continue;
}
free_irq(button_irq[i].irq, (void *)&button_irq[i]);
}
return 0;
}
static int s3c24xx_buttons_op[(struct inode *inode, struct file *file)
{
int i;
for()
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = s3c24xx_button_open,
.release = s3c24xx_button_close,
.read = s3c24xx_button_read,
.poll = s3c24xx_button_poll, //进行多路监控
}
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR;
.name = DEVICE_NAME;
.fops = &dev_fops; //字符设备需要file_operation
}
static int __init dev_init (void)
{
int ret;
ret = misc_register(&misc); //主设备号为10,一类字符设备
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);