1、设备的阻塞方式访问
应用编程
sd=socke(...)
recv(sd, ....)//无数据阻塞等待 有数据立即返回
希望按键设备 也能达到在用户空间阻塞方式访问
内核中为了实现设备的阻塞方式访问,提供了一套机制: 等待队列
核心数据结构
wait_queue_head_t
实验步骤:
1)定义一个等待队列头变量
wait_queue_head_t btn_wqh;
2) 初始化等待队列头变量
init_waitqueue_head(&btn_wqh);
//等价于步骤1) 2)
DECLARE_WAIT_QUEUE_HEAD(btn_wqh)
3) 驱动程序中,对设备执行读写操作时
如果设备I/O为就绪 可以调用以下函数,
实现进程的阻塞
//该函数会使得调用者进程进入睡眠状态
wait_event(btn_wqh, condition)
//进入的是可中断的睡眠状态
wait_event_interruptible(btn_wqh, condition)
condition,为TRUE 直接返回 不睡眠
为FALSE,进程进入睡眠状态
4)当设备I/O就绪时
唤醒因I/O未就绪而进入睡眠状态的进程
wake_up(&btn_wqh);
wake_up_interruptible(&btn_wqh);
实现原理:
1)内核中管理进程,会为每个进程建立PCB(进程控制块)
linux中PCB在内核中对应的数据结构
struct task_struct
{
//代表了进程的当前状态
volatile long state
...
}
linux内核会为每个进程创建一个task_struct变量
该变量在内核软件中代表着该进程
2)current
始终执行正在CPU中执行的那个进程的数据结构
3) wait_event_interruptible
__wait_event_interruptible(btn_wqh, ....)
{
//创建了一个新的节点
//该节点中保存了当前进程信息(task_struct变量地址)
DEFINE_WAIT(__wait);
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE)
{
//把前边创建的新节点 插入btn_wqh指向的链表中去
__add_wait_queue(q, wait);
//将当前进程的状态改为TASK_INTERRUPTIBLE(可中断的睡眠状态)
set_current_state(state);
}
//从处于running的集合中选出一个进程放入CPU中执行
//当前任务真的就放弃了CPU 进入睡眠状态
schedule();
}
4)唤醒
//btn_wqh指向的链表中有多个节点
//每个节点中记录了因某类事件而睡眠的进程
//当该类事件条件满足时
//内核会唤醒多个节点对应的多个进程
wake_up_interruptible(&btn_wqh)
2、延时去抖
1)timer_list
2) delayed_work
3、按键的按下和释放都关注
1)如何使得上升沿和下降沿都触发中断
requst_irq(..., btn_isr
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
...)
2)btn_timer_func
哪个按键触发的
按下触发还是释放触发 通过读取管脚的电平状态获取
gpio_get_value(...)
练习:
./test
up 按下 输出 0x10
释放 0x11
4、设备的非阻塞方式访问
//默认为阻塞方式访问
fd = open("/dev/mybuttons", O_RDONLY);
//实现设备的非阻塞方式访问
fd = open("/dev/mybuttons", O_RDONLY|O_NONBLOCK);
btn_read(filp, buf, len, offset)
{
要实现非阻塞,关键在于要知道open设备时是否
使用了O_NONBLOCK参数
该参数信息存储在了filp->f_flags
}
struct file//文件表
{
//其中存储了用户空间open设备时的绝大部分参数信息
unsigned int f_flags;
//记录对应的操作函数集合地址
const struct file_operations *f_op;
...
}
#include <stdio.h>
#include <fcntl.h>
int main(void)
{
unsigned char key=0;
int fd = open("/dev/mybuttons", O_RDONLY);
if(fd < 0)
{
perror("open failed:");
return -1;
}
printf("open successed!\n");
while(1)
{
read(fd, &key, sizeof(key));
printf("key=%#x\n", key);
}
close(fd);
return 0;
}
#include "../../global.h"
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <mach/platform.h>
#include <linux/sched.h>
dev_t dev = 0;
struct cdev btn_cdev;
struct class *cls = NULL;
/*定义信号量*/
struct semaphore btn_sem;
/*按键键值缓冲区*/
unsigned char key_buf;
/*记录按键缓冲区中是否有键值
*0,无键值
*1, 有键值
* */
volatile int ev_press = 0;
/*定义等待队列头变量*/
wait_queue_head_t btn_wqh;
static int btn_open(struct inode *inode,
struct file *filp)
{
int ret = 0;
//down(&btn_sem);
ret = down_interruptible(&btn_sem);
if(ret) //被信号打断
{
return -EAGAIN;
}
return 0;
}
static int btn_release(struct inode *inode,
struct file *filp)
{
up(&btn_sem);
return 0;
}
static ssize_t btn_read(struct file *filp,
char __user *buf,
size_t len,
loff_t *off)
{
int ret = 0;
/*
*按键缓冲区中有键值 直接返回
*如果无键值 让test进程进入睡眠状态
* */
wait_event_interruptible(btn_wqh, ev_press);
//*buf = key_buf;//有风险
ret = copy_to_user(buf, &key_buf, len);
if(ret)
{
return -EFAULT;
}
ev_press = 0;
return ret;
}
struct file_operations btn_fops =
{
.owner = THIS_MODULE,
.open = btn_open,
.release = btn_release,
.read = btn_read,
};
irqreturn_t btn_isr(int irq, void *dev)
{
/*保存按键值*/
key_buf = 0x10;
ev_press = 1;//有键值
/*唤醒因无键值而睡眠的进程*/
wake_up_interruptible(&btn_wqh);
return IRQ_HANDLED; //中断处理完毕
}
int __init btn_drv_init(void)
{
if(request_irq(IRQ_GPIO_A_START+28, btn_isr,
IRQF_TRIGGER_FALLING,
"up", NULL))
{
printk("<1>" "request_irq failed!");
return -EAGAIN;
}
/*申请注册设备号*/
alloc_chrdev_region(&dev, 0, 1, "buttons");
/*初始化cdev*/
cdev_init(&btn_cdev, &btn_fops);
/*注册cdev*/
cdev_add(&btn_cdev, dev, 1);
/*自动创建设备文件*/
cls = class_create(THIS_MODULE, "buttons");
device_create(cls, NULL, dev, NULL, "mybuttons");
/*初始化信号量*/
/*该设备可以同时被一个进程访问*/
sema_init(&btn_sem, 1);
/*初始化等待队列头*/
init_waitqueue_head(&btn_wqh);
return 0;
}
void __exit btn_drv_exit(void)
{
/*销毁设备文件*/
device_destroy(cls, dev);
class_destroy(cls);
/*注销cdev*/
cdev_del(&btn_cdev);
/*注销设备号*/
unregister_chrdev_region(dev, 1);
free_irq(IRQ_GPIO_A_START+28, NULL);
}
module_init(btn_drv_init);
module_exit(btn_drv_exit);
#include "../../global.h"
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <mach/platform.h>
#include <linux/sched.h>
dev_t dev = 0;
struct cdev btn_cdev;
struct class *cls = NULL;
/*定义信号量*/
struct semaphore btn_sem;
/*按键键值缓冲区*/
unsigned char key_buf;
/*记录按键缓冲区中是否有键值
*0,无键值
*1, 有键值
* */
volatile int ev_press = 0;
/*定义等待队列头变量*/
wait_queue_head_t btn_wqh;
struct timer_list btn_timer;
typedef struct btn_desc
{
unsigned char code;// 按键编码
int irq;
char *name;
}btn_desc_t;
btn_desc_t buttons[]=
{
{0x10, IRQ_GPIO_A_START+28, "up"},
{0x20, IRQ_GPIO_B_START+30, "down"},
{0x30, IRQ_GPIO_B_START+31, "left"},
{0x40, IRQ_GPIO_B_START+9, "right"},
};
static int btn_open(struct inode *inode,
struct file *filp)
{
int ret = 0;
//down(&btn_sem);
ret = down_interruptible(&btn_sem);
if(ret) //被信号打断
{
return -EAGAIN;
}
return 0;
}
static int btn_release(struct inode *inode,
struct file *filp)
{
up(&btn_sem);
return 0;
}
static ssize_t btn_read(struct file *filp,
char __user *buf,
size_t len,
loff_t *off)
{
int ret = 0;
/*
*按键缓冲区中有键值 直接返回
*如果无键值 让test进程进入睡眠状态
* */
wait_event_interruptible(btn_wqh, ev_press);
//*buf = key_buf;//有风险
ret = copy_to_user(buf, &key_buf, len);
if(ret)
{
return -EFAULT;
}
ev_press = 0;
return ret;
}
struct file_operations btn_fops =
{
.owner = THIS_MODULE,
.open = btn_open,
.release = btn_release,
.read = btn_read,
};
void btn_timer_func(unsigned long data)
{
/*获取按键的描述信息*/
btn_desc_t *pdata = (btn_desc_t *)data;
/*保存按键值*/
key_buf = pdata->code;
ev_press = 1;//有键值
/*唤醒因无键值而睡眠的进程*/
wake_up_interruptible(&btn_wqh);
}
irqreturn_t btn_isr(int irq, void *dev)
{
/*传递按键的描述信息*/
btn_timer.data = (unsigned long)dev;
mod_timer(&btn_timer, jiffies+HZ/100);
return IRQ_HANDLED; //中断处理完毕
}
int __init btn_drv_init(void)
{
int i = 0;
int ret = 0;
/*申请注册设备号*/
alloc_chrdev_region(&dev, 0, 1, "buttons");
/*初始化cdev*/
cdev_init(&btn_cdev, &btn_fops);
/*注册cdev*/
cdev_add(&btn_cdev, dev, 1);
/*自动创建设备文件*/
cls = class_create(THIS_MODULE, "buttons");
device_create(cls, NULL, dev, NULL, "mybuttons");
/*初始化信号量*/
/*该设备可以同时被一个进程访问*/
sema_init(&btn_sem, 1);
/*初始化等待队列头*/
init_waitqueue_head(&btn_wqh);
for(; i<ARRAY_SIZE(buttons);i++)
{
ret = request_irq(buttons[i].irq, btn_isr,
IRQF_TRIGGER_FALLING,
buttons[i].name,
&(buttons[i]));
}
init_timer(&btn_timer);
btn_timer.function = btn_timer_func;
return 0;
}
void __exit btn_drv_exit(void)
{
int i = 0;
for(; i<ARRAY_SIZE(buttons); i++)
{
free_irq(buttons[i].irq, buttons+i);
}
/*销毁设备文件*/
device_destroy(cls, dev);
class_destroy(cls);
/*注销cdev*/
cdev_del(&btn_cdev);
/*注销设备号*/
unregister_chrdev_region(dev, 1);
}
module_init(btn_drv_init);
module_exit(btn_drv_exit);
#include "../../global.h"
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <mach/platform.h>
#include <linux/sched.h>
#include <linux/gpio.h>
dev_t dev = 0;
struct cdev btn_cdev;
struct class *cls = NULL;
/*定义信号量*/
struct semaphore btn_sem;
/*按键键值缓冲区*/
unsigned char key_buf;
/*记录按键缓冲区中是否有键值
*0,无键值
*1, 有键值
* */
volatile int ev_press = 0;
/*定义等待队列头变量*/
wait_queue_head_t btn_wqh;
struct timer_list btn_timer;
typedef struct btn_desc
{
unsigned char code;// 按键编码
int irq;
char *name;
int gpio; //管脚编号
}btn_desc_t;
btn_desc_t buttons[]=
{
{0x10, IRQ_GPIO_A_START+28, "up",PAD_GPIO_A+28},
{0x20, IRQ_GPIO_B_START+30, "down",PAD_GPIO_B+30},
{0x30, IRQ_GPIO_B_START+31, "left",PAD_GPIO_B+31},
{0x40, IRQ_GPIO_B_START+9, "right",PAD_GPIO_B+9},
};
static int btn_open(struct inode *inode,
struct file *filp)
{
int ret = 0;
//down(&btn_sem);
ret = down_interruptible(&btn_sem);
if(ret) //被信号打断
{
return -EAGAIN;
}
return 0;
}
static int btn_release(struct inode *inode,
struct file *filp)
{
up(&btn_sem);
return 0;
}
static ssize_t btn_read(struct file *filp,
char __user *buf,
size_t len,
loff_t *off)
{
int ret = 0;
/*
*按键缓冲区中有键值 直接返回
*如果无键值 让test进程进入睡眠状态
* */
wait_event_interruptible(btn_wqh, ev_press);
//*buf = key_buf;//有风险
ret = copy_to_user(buf, &key_buf, len);
if(ret)
{
return -EFAULT;
}
ev_press = 0;
return ret;
}
struct file_operations btn_fops =
{
.owner = THIS_MODULE,
.open = btn_open,
.release = btn_release,
.read = btn_read,
};
void btn_timer_func(unsigned long data)
{
int stat = 0;
/*获取按键的描述信息*/
btn_desc_t *pdata = (btn_desc_t *)data;
/*判断是按下触发还是释放触发
*按下 返回0
*释放 返回1
* */
stat = gpio_get_value(pdata->gpio);
/*保存按键值*/
key_buf = pdata->code + stat;
ev_press = 1;//有键值
/*唤醒因无键值而睡眠的进程*/
wake_up_interruptible(&btn_wqh);
}
irqreturn_t btn_isr(int irq, void *dev)
{
/*传递按键的描述信息*/
btn_timer.data = (unsigned long)dev;
mod_timer(&btn_timer, jiffies+HZ/100);
return IRQ_HANDLED; //中断处理完毕
}
int __init btn_drv_init(void)
{
int i = 0;
int ret = 0;
/*申请注册设备号*/
alloc_chrdev_region(&dev, 0, 1, "buttons");
/*初始化cdev*/
cdev_init(&btn_cdev, &btn_fops);
/*注册cdev*/
cdev_add(&btn_cdev, dev, 1);
/*自动创建设备文件*/
cls = class_create(THIS_MODULE, "buttons");
device_create(cls, NULL, dev, NULL, "mybuttons");
/*初始化信号量*/
/*该设备可以同时被一个进程访问*/
sema_init(&btn_sem, 1);
/*初始化等待队列头*/
init_waitqueue_head(&btn_wqh);
for(; i<ARRAY_SIZE(buttons);i++)
{
ret = request_irq(buttons[i].irq, btn_isr,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
buttons[i].name,
&(buttons[i]));
}
init_timer(&btn_timer);
btn_timer.function = btn_timer_func;
return 0;
}
void __exit btn_drv_exit(void)
{
int i = 0;
for(; i<ARRAY_SIZE(buttons); i++)
{
free_irq(buttons[i].irq, buttons+i);
}
/*销毁设备文件*/
device_destroy(cls, dev);
class_destroy(cls);
/*注销cdev*/
cdev_del(&btn_cdev);
/*注销设备号*/
unregister_chrdev_region(dev, 1);
}
module_init(btn_drv_init);
module_exit(btn_drv_exit);
#include "../../global.h"
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <mach/platform.h>
#include <linux/sched.h>
#include <linux/gpio.h>
dev_t dev = 0;
struct cdev btn_cdev;
struct class *cls = NULL;
/*定义信号量*/
struct semaphore btn_sem;
/*按键键值缓冲区*/
unsigned char key_buf;
/*记录按键缓冲区中是否有键值
*0,无键值
*1, 有键值
* */
volatile int ev_press = 0;
/*定义等待队列头变量*/
wait_queue_head_t btn_wqh;
struct timer_list btn_timer;
typedef struct btn_desc
{
unsigned char code;// 按键编码
int irq;
char *name;
int gpio; //管脚编号
}btn_desc_t;
btn_desc_t buttons[]=
{
{0x10, IRQ_GPIO_A_START+28, "up",PAD_GPIO_A+28},
{0x20, IRQ_GPIO_B_START+30, "down",PAD_GPIO_B+30},
{0x30, IRQ_GPIO_B_START+31, "left",PAD_GPIO_B+31},
{0x40, IRQ_GPIO_B_START+9, "right",PAD_GPIO_B+9},
};
static int btn_open(struct inode *inode,
struct file *filp)
{
int ret = 0;
//down(&btn_sem);
ret = down_interruptible(&btn_sem);
if(ret) //被信号打断
{
return -EAGAIN;
}
return 0;
}
static int btn_release(struct inode *inode,
struct file *filp)
{
up(&btn_sem);
return 0;
}
static ssize_t btn_read(struct file *filp,
char __user *buf,
size_t len,
loff_t *off)
{
int ret = 0;
//if(用户非阻塞方式访问设备 && ev_press==0)
if((filp->f_flags&O_NONBLOCK) && ev_press==0)
{
return -EAGAIN;
}
/*
*按键缓冲区中有键值 直接返回
*如果无键值 让test进程进入睡眠状态
* */
wait_event_interruptible(btn_wqh, ev_press);
//*buf = key_buf;//有风险
ret = copy_to_user(buf, &key_buf, len);
if(ret)
{
return -EFAULT;
}
ev_press = 0;
return ret;
}
struct file_operations btn_fops =
{
.owner = THIS_MODULE,
.open = btn_open,
.release = btn_release,
.read = btn_read,
};
void btn_timer_func(unsigned long data)
{
int stat = 0;
/*获取按键的描述信息*/
btn_desc_t *pdata = (btn_desc_t *)data;
/*判断是按下触发还是释放触发
*按下 返回0
*释放 返回1
* */
stat = gpio_get_value(pdata->gpio);
/*保存按键值*/
key_buf = pdata->code + stat;
ev_press = 1;//有键值
/*唤醒因无键值而睡眠的进程*/
wake_up_interruptible(&btn_wqh);
}
irqreturn_t btn_isr(int irq, void *dev)
{
/*传递按键的描述信息*/
btn_timer.data = (unsigned long)dev;
mod_timer(&btn_timer, jiffies+HZ/100);
return IRQ_HANDLED; //中断处理完毕
}
int __init btn_drv_init(void)
{
int i = 0;
int ret = 0;
/*申请注册设备号*/
alloc_chrdev_region(&dev, 0, 1, "buttons");
/*初始化cdev*/
cdev_init(&btn_cdev, &btn_fops);
/*注册cdev*/
cdev_add(&btn_cdev, dev, 1);
/*自动创建设备文件*/
cls = class_create(THIS_MODULE, "buttons");
device_create(cls, NULL, dev, NULL, "mybuttons");
/*初始化信号量*/
/*该设备可以同时被一个进程访问*/
sema_init(&btn_sem, 1);
/*初始化等待队列头*/
init_waitqueue_head(&btn_wqh);
for(; i<ARRAY_SIZE(buttons);i++)
{
ret = request_irq(buttons[i].irq, btn_isr,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
buttons[i].name,
&(buttons[i]));
}
init_timer(&btn_timer);
btn_timer.function = btn_timer_func;
return 0;
}
void __exit btn_drv_exit(void)
{
int i = 0;
for(; i<ARRAY_SIZE(buttons); i++)
{
free_irq(buttons[i].irq, buttons+i);
}
/*销毁设备文件*/
device_destroy(cls, dev);
class_destroy(cls);
/*注销cdev*/
cdev_del(&btn_cdev);
/*注销设备号*/
unregister_chrdev_region(dev, 1);
}
module_init(btn_drv_init);
module_exit(btn_drv_exit);