前言
知识点: 中断 等待队列
普通IO中断架接字符设备时,只能轮询的读取io状态,要实现当有按键按下时才进行读取,可以使用等待队列来实现阻塞休眠读写。
一、等待队列
等待队列是一种实现阻塞和唤醒的内核机制,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。
1、定义
定义头文件 #include <linux/wait.h>
(1)、静态定义的宏
#define DECLARE_WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
如
static DECLARE_WAIT_QUEUE_HEAD(wq)
(2)、动态定义的宏
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
\
__init_waitqueue_head((q), #q, &__key); \
} while (0)
如
wait_queue_head_t wq;
init_waitqueue_head(&wq);
2、阻塞接口
wait_event(wq, condition) ;
wait_event_timeout(wq, condition, timeout);
wait_event_interruptible(wq, condition);
wait_event_interruptible_timeout(wq, condition, timeout)
wq:定义的等待队列头、
condition:条件表达式,当值为真时唤醒阻塞休眠的进程,为假继续阻塞休眠
timeout:超时等待时间
①:不可中断 条件不满足一直休眠 kill也无效
②:不可中断 不管条件是否为真,只要达到超时时间就唤醒进程, 此时返回 0 非超时唤醒时返回timeout或1
③:可中断 可被信号中断 当被中断时返回 负值-ERESTARTSYS 唤醒wake up时条件为真返回 0
④ 可中断 加超时等待唤醒 超时返回 0
如
static volatile int ev_press = 0;//设置条件为假
wait_event_interruptible(wq, ev_press);
ev_press = 0;
定义是一个全局变量来维护condition
3、唤醒接口
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
如
ev_press = 1;//设置条件为真
wake_up_interruptible(&wq);
惯例:wake_up 跟 wait_event
wake_up_interruptible 跟 wait_event_interruptible
二、按键驱动
1、自定义结构体
struct button_irq_desc
{
int irq; //获得的中断号
int pin; //引脚
int number; //标号
char *name; //自定义pin名
};
static struct button_irq_desc button_irqs [] = {
{0 , PINID_LCD_D17 , 0, "KEY1"},
{0, PINID_LCD_D18 , 1, "KEY2"},
{0, PINID_SSP0_DATA4 , 2, "KEY3"},
{0, PINID_SSP0_DATA5 , 3, "KEY4"},
{0, PINID_SSP0_DATA6 , 4, "KEY5"},
};
2、申请中断
request_irq(button_irqs[i].irq, buttons_interrupt, IRQF_DISABLED,
button_irqs[i].name, (void *)&button_irqs[i]);
最后一个参数传递结构数组里的一个地址,等下传递到中断处理函数的形参的第二个参数
3、中断服务函数
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; //用来获取那个按键被按下的信息
int down;
down = key_geT_valune(button_irqs->pin); //得到io状态
key_values[button_irqs->number] = '0' + down;
ev_press = 1; //设置条件有效值为1,才可唤醒进程
wake_up_interruptible(&button_waitq); //唤醒休眠的进程
return IRQ_RETVAL(IRQ_HANDLED);
}
4、read
static int 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) //在用户层read(,flag)中可以设置为阻塞或非阻塞的实现代码
return -EAGAIN;
else
wait_event_interruptible(button_waitq, ev_press);
}
ev_press = 0; //唤醒完条件应该立即被为0
err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
return err ? -EFAULT : min(sizeof(key_values), count);
}
总结
进程被置为休眠,意味着它被标识为处于一个特殊的状态并且从调度器的运行队列中移走。这个进程将不被在任何 CPU 上调度,即将不会运行。 直到发生某些事情改变了那个状态安全地进入阻塞休眠的3条规则:
1、永远不要在原子上下文中进入休眠,即当驱动在持有一个自旋锁、seqlock或者 RCU 锁时不能睡眠;
2、当进程(如进程A)被唤醒,它并不知道休眠了多长时间以及休眠时发生什么;也不知道是否另有进程(如进程B)也在休眠等待同一事件,且那个进程(如进程B)可能在它(如进程A)之前醒来并获取了所等待的资源。所以不能对唤醒后的系统状态做任何的假设,必须重新检查等待条件来确保正确的响应。
3、除非确信其他进程会在其他地方唤醒休眠的进程,否则也不能睡眠
驱动源码
#include<linux/init.h>
#include<linux/module.h>
#include<mach/gpio.h>
#include<asm/io.h>
#include"mach/../../mx28_pins.h"
#include <mach/pinctrl.h>
#include "mach/mx28.h"
#include<linux/fs.h>
#include <linux/io.h>
#include<asm/uaccess.h>
#include<linux/miscdevice.h>
#include<linux/irq.h>
#include<linux/sched.h>
#include<linux/interrupt.h>
#include<linux/timer.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define DEVICE_NAME "imx283_key"
struct button_irq_desc {
int irq;
int pin;
int number;
char *name;
};
static struct button_irq_desc button_irqs [] = {
{0 , PINID_LCD_D17 , 1, "KEY1"},
{0, PINID_LCD_D18 , 2, "KEY2"},
{0, PINID_SSP0_DATA4 , 3, "KEY3"},
{0, PINID_SSP0_DATA5 , 4, "KEY4"},
{0, PINID_SSP0_DATA6 , 5, "KEY5"},
};
static volatile char key_values [] = {'0', '0', '0', '0', '0','0'};
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;
int key_geT_valune(int pin)
{
int sta[2];
int irq_no,i;
for(i=0;i<2;i++)
{
sta[i] = gpio_get_value(MXS_PIN_TO_GPIO(pin));
sta[i] = (sta[i]>>(pin&0xFF)&0x01);
printk("sta[%d] = %d\r\n",i,sta[i]);
}
if(sta[0]!=sta[1])
{
return -1;
}
irq_no = gpio_to_irq(MXS_PIN_TO_GPIO(pin));
if(sta[0] == 1)
{
set_irq_type(irq_no, IRQF_TRIGGER_FALLING);
}else
{
set_irq_type(irq_no, IRQF_TRIGGER_RISING);
}
return !sta[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;
down = key_geT_valune(button_irqs->pin);
key_values[button_irqs->number] = '0' + down;
ev_press = 1;
wake_up_interruptible(&button_waitq);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int buttons_open(struct inode *inode, struct file *file)
{
int i;
int err = 0;
int ret;
for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
{
if (button_irqs[i].irq < 0)
{
continue;
}
gpio_free(MXS_PIN_TO_GPIO(button_irqs[i].pin));
ret = gpio_request(MXS_PIN_TO_GPIO(button_irqs[i].pin), button_irqs[i].name);
if(ret!= 0)
{
printk("gpio request fail!\r\n");
return -EBUSY;
}
gpio_direction_input(MXS_PIN_TO_GPIO(button_irqs[i].pin));
button_irqs[i].irq = gpio_to_irq(MXS_PIN_TO_GPIO(button_irqs[i].pin));
set_irq_type(button_irqs[i].irq, IRQF_TRIGGER_FALLING);
err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQF_DISABLED,
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 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 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(buff, (const void *)key_values, min(sizeof(key_values), count));
return err ? -EFAULT : min(sizeof(key_values), count);
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = buttons_open,
.release = buttons_close,
.read = buttons_read,
};
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.");
用户层
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <limits.h>
#include <sys/ioctl.h>
#include <time.h>
#include <pthread.h>
int main(void)
{
int ret;
int button_fd = -1;
char key[6];
int i;
button_fd = open("/dev/imx283_key",O_RDWR);
if ( button_fd == -1 )
{
printf("open dev/imx283_key fail=%d\n",button_fd);
return -1;
}
printf("open dev/imx283_key success =%d\n",button_fd);
while(1)
{
ret = read(button_fd, key, 6);
for(i=1; i<ret; i++)
printf("key[%d]=%c ",i, key[i]);
printf("\r\n ");
}
close(button_fd);
printf("close dev/button_fd success \n");
return 0;
}