十、【等待队列】按键中断程序更新
一、概述
linux驱动中,阻塞一般就是用等待队列来实现,将进程停止在此处并睡眠下,直到条件满足时,才可通过此处,继续运行。在睡眠等待期间,wake up时,唤起来检查条件,条件满足解除阻塞,不满足继续睡下去。
上一个按键中断程序中,在读取按键键值时,应用层在read时一直返回键值,并不会阻塞在read,等待有按键按下的时候再返回,在这里我们使用等待队列来进行阻塞。
二、等待队列相关接口
1、等待队列的定义
(1)静态定义
#define DECLARE_WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
(2)动态定义
wait_queue_head_t key_wq;
init_waitqueue_head(key_wq);
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
\
__init_waitqueue_head((q), #q, &__key); \
} while (0)
2、等待
wait_event(wq, condition) -----------------深睡眠,不可被信号打断
wait_event_interruptible(wq, condition)----浅睡眠,可以被信号打断
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
参数:
- wq:等待队列名字
- condition:等待的条件,condition假时一直阻塞等待,为真时执行。
3、唤醒
wake_up(x) //和wait_event配套使用
wake_up_interruptible(x) //和wait_event_interruptible配套使用
三、按键中断中使用等待队列
key_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/sched.h>
struct key_gpio_t{
unsigned int irq;
char irqname[20];
unsigned char keyvalue;
};
static DECLARE_WAIT_QUEUE_HEAD(key_wq) ; //静态定义等待队列
static bool flag = false;
static struct key_gpio_t key_gpio[]=
{
{IRQ_GPIO_A_START+28,"KEY2_GPIOA28",2},
{IRQ_GPIO_B_START+30,"KEY3_GPIOB30",3},
{IRQ_GPIO_B_START+31,"KEY4_GPIOB31",4},
{IRQ_GPIO_B_START+9, "KEY6_GPIOB9",6},
};
static char keyvalue = 0;
ssize_t gec6818_key_read(struct file *filp, char __user * buf, size_t size, loff_t *oft)
{
int ret;
wait_event_interruptible(key_wq, flag);
flag = false; //唤醒一次队列后要复位flag的值
if(size !=1)
{
return -EINVAL;
}
ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
if(ret != 0)
{
return (size -ret);
}
keyvalue=0;
return size;
}
static irqreturn_t gec6818_key_handler(int irq, void * dev)
{
struct key_gpio_t keytmp=*(struct key_gpio_t *)dev;
keyvalue =keytmp.keyvalue;
mdelay(400); //按键防抖
flag = true; //设置flag为true
wake_up_interruptible(&key_wq); //按键按下时,唤醒等待队列
return IRQ_HANDLED;
}
struct file_operations key_misc_fops=
{
.read = gec6818_key_read,
};
static struct miscdevice key_misc={
.minor = MISC_DYNAMIC_MINOR,
.name = "key_misc",
.fops = &key_misc_fops,
};
static int __init gec6818_key_init(void)
{
int ret,i;
printk(KERN_INFO"gec6818_key_init\n");
ret = misc_register(&key_misc);
if(ret < 0)
{
printk(KERN_INFO"key misc register fail.\n");
goto misc_register_err;
}
for(i=0;i<4;i++)
{
ret = request_irq(key_gpio[i].irq, gec6818_key_handler,IRQF_TRIGGER_FALLING,key_gpio[i].irqname,(void*)&key_gpio[i]);
if(ret < 0)
{
printk(KERN_INFO"request_irq fail.\n");
goto irq_request_err;
}
}
return 0;
irq_request_err:
while(i--)
{
free_irq(key_gpio[i].irq,NULL);
}
misc_register_err:
return 0;
}
static void __exit gec6818_key_exit(void)
{
int i;
printk(KERN_INFO"gec6818_key_exit\n");
misc_deregister(&key_misc);
for(i=0;i<4;i++)
{
free_irq(key_gpio[i].irq,(void *)&key_gpio[i]);
}
}
module_init(gec6818_key_init);
module_exit(gec6818_key_exit);
MODULE_LICENSE("GPL");
main.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
int main()
{
int fd,ret;
char keyvalue=0;
fd = open("/dev/key_misc",O_RDWR);
if(fd<0)
{
perror("open key_misc error!");
}
while(1)
{
ret=read(fd,&keyvalue,1);
if(ret !=1)
{
perror("read error");
continue;
}
printf("keyvalue=key%d\n",keyvalue);
}
close(fd);
}
相关链接:linux中的阻塞机制和等待队列