linux按键驱动之poll
上一节应用程序的死循环里的读函数是一直在读的;在实际的应用场所里,有没有那么一种情况,偶尔有数据、偶尔没有数据,答案当然是有的。--》poll机制:Poll机制实现的是一定时间如果没有按键的话就返回。以前是如果没有按键不返回 一直处于休眠状态。
poll调用(应用层) :
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
struct pollfd结构如下:【在源码文件poll.h文件中】
struct pollfd {
int fd;
short events;
short revents;
};
这个结构中fd表示文件描述符,events表示请求检测的事件,revents表示检测之后返回的事件,如果当某个文件描述符有状态变化时,revents的值就不为空。
- fds:存放需要被检测状态的Socket描述符;与select不同(select函数在调用之后,会清空检测socket描述符的数组),每当调用这个函数之后,系统不会清空这个数组,而是将有状态变化的描述符结构的revents变量状态变化,操作起来比较方便;
- nfds:用于标记数组fds中的struct pollfd结构元素的总数量;
- timeout:poll函数调用阻塞的时间,单位是MS(毫秒)
return value:
- 大于0:表示数组fds中有socket描述符的状态发生变化,或可以读取、或可以写入、或出错。并且返回的值表示这些状态有变化的socket描述符的总数量;此时可以对fds数组进行遍历,以寻找那些revents不空的socket描述符,然后判断这个里面有哪些事件以读取数据。
- 等于0:表示没有socket描述符有状态变化,并且调用超时。
- 小于0:此时表示有错误发生,此时全局变量errno保存错误码。
内核实现:
poll机制总结:
1. poll > sys_poll > do_sys_poll >poll_initwait,poll_initwait函数注册一下回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数。
2. 接下来执行file->f_op->poll,即我们驱动程序里自己实现的poll函数
它会调用poll_wait把自己挂入某个队列,这个队列也是我们的驱动自己定义的;
它还判断一下设备是否就绪。
3. 如果设备未就绪,do_sys_poll里会让进程休眠一定时间
4. 进程被唤醒的条件有2:一是上面说的“一定时间”到了,二是被驱动程序唤醒。驱动程序发现条件就绪时,就把“某个队列”上挂着的进程唤醒,这个队列,就是前面通过poll_wait把本进程挂过去的队列。
5. 如果驱动程序没有去唤醒进程,那么chedule_timeout(__timeou)超时后,会重复2、3动作,直到应用程序的poll调用传入的时间到达。
博客中http://blog.csdn.net/u012719256/article/details/52663292提到:
内核中poll实现非常固定,就以下两个步骤
static unsigned forth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0; // 1. 将button_waitq添加到等待队列
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press) // 如果有按键按下了,返回的掩码表示不休眠
mask |= POLLIN | POLLRDNORM; // 2. 返回掩码
return mask;
}
暂时做个标记吧,现在只知道这个格式
代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#define DEVICE_NAME "mybutton" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
static struct class *button_class;
static struct class_device *button_dev_class;
int major; static volatile int press_cnt=;/* 按键被按下的次数(准确地说,是发生中断的次数) */ static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//定义等待队列 /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
static volatile int ev_press = ; static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
// volatile int *press_cnt = (volatile int *)dev_id;
press_cnt =press_cnt + ; /* 按键计数加1 */
ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
return IRQ_RETVAL(IRQ_HANDLED);//中断处理程序应该返回一个值,用来表明是否真正处理了一个中断,如果中断例程发现其设备的确要处理,则应该返回IRQ_HANDLED, //否则应该返回IRQ_NONE,我们可以通过这个宏来产生返回值,不是本设备的中断应该返回IRQ_NONE }
/* 应用程序对设备文件/dev/xxx 执行open(...)时,
* 就会调用button_open函数
* 就会调用button_open函数
*/
static int button_open (struct inode *inode, struct file *filep)
{
int err;
err=request_irq(IRQ_EINT2,buttons_interrupt,IRQF_TRIGGER_FALLING,"KEY3",NULL); if (err) {
// 释放已经注册的中断
free_irq(IRQ_EINT2, NULL);
return -EBUSY;
} return ;
} /* 应用程序对设备文件/dev/buttons执行close(...)时,
* 就会调用buttons_close函数
*/
static int buttons_close(struct inode *inode, struct file *file)
{ free_irq(IRQ_EINT2, NULL);
return ;
} ssize_t button_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long err; /* 如果ev_press等于0,休眠 */
wait_event_interruptible(button_waitq, ev_press);//阻塞
/* 执行到这里时,ev_press等于1,将它清0 */
ev_press = ;
/* 将按键状态复制给用户,并清0 */
err = copy_to_user(buf, (const void *)&press_cnt, count);
//memset((void *)&press_cnt, 0, sizeof(press_cnt));
return err ? -EFAULT : ;
} static unsigned buttons_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 将进程挂接到button_waitq队列中
/* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0
* 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1
*/
if(ev_press)
{
mask |= POLLIN | POLLRDNORM; /* POLLIN表示有数据可读 POLLRDNORM表示有普通数据可读*/
}
/* 如果有按键按下时,mask |= POLLIN | POLLRDNORM,否则mask = 0 */
return mask;
} /* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的open、read、write等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations button_ops=
{
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = button_open,
.read = button_read,
.release = buttons_close,
.poll = buttons_poll,
}; /*
* 执行insmod命令时就会调用这个函数
*/ static int button_init(void)
{ /* 注册字符设备
* 参数为主设备号、设备名字、file_operations结构;
* 这样,主设备号就和具体的file_operations结构联系起来了,
* 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
* LED_MAJOR可以设为0,表示由内核自动分配主设备号
*/
major = register_chrdev(, DEVICE_NAME, &button_ops);
if (major < )
{
printk(DEVICE_NAME " can't register major number number::%d\n",major);
return ;
}
printk(DEVICE_NAME " initialized1\n");
button_class = class_create(THIS_MODULE, "button");
if (IS_ERR(button_class))
return PTR_ERR(button_class);
button_dev_class = class_device_create(button_class, NULL, MKDEV(major, ), NULL, "my_button"); /* /dev/my_button */ return ; } /*
* 执行rmmod命令时就会调用这个函数
*/
static void button_exit(void)
{
class_device_unregister(button_dev_class);
class_destroy(button_class);
/* 卸载驱动程序 */
unregister_chrdev(major, DEVICE_NAME);
} /* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(button_init);
module_exit(button_exit); /* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("http://www.100ask.net");// 驱动程序的作者
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");// 一些描述信息
MODULE_LICENSE("GPL"); // 遵循的协议
test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h> int main(int argc,char**argv)
{
int fd;
int cnt = 0;
int ret; struct pollfd fds[1];/*每一个pollfd结构体指定了一个被监视的文件描述符*/ //打开设备
fd = open("/dev/my_button",O_RDWR); if(fd<0)
{
perror("open fail \n");
return -1;
} fds[0].fd=fd;
fds[0].events=POLLIN;
while(1)
{
/*poll函数返回0时,表示5s时间到了,而这段时间里,没有事件发生"数据可读" */
ret=poll(fds,1,5000); if (ret==0)
{
printf("time out\n");
}
else /* 如果没有超时,则读出按键值 */
{
read(fd,&cnt, sizeof(cnt)); //read()
printf("button has been pressed %d timea\n",cnt);
}
}
close(fd);
return 0;
}
结果:
参考:
http://blog.csdn.net/lwj103862095/article/details/17536069
http://blog.csdn.net/u012719256/article/details/52663292
http://www.cnblogs.com/mylinux/p/5090264.html
http://blog.csdn.net/ruoyunliufeng/article/details/24188693
linux按键驱动之poll的更多相关文章
- linux字符驱动之poll机制按键驱动
在上一节中,我们讲解了如何自动创建设备节点,实现一个中断方式的按键驱动.虽然中断式的驱动,效率是蛮高的,但是大家有没有发现,应用程序的死循环里的读函数是一直在读的:在实际的应用场所里,有没有那么一种情 ...
- Linux按键驱动程序设计--从简单到不简单【转】
本文转载自:http://blog.csdn.net/coding__madman/article/details/51399353 混杂设备驱动模型: 1. 混杂设备描述 在Linux系统中,存在一 ...
- Linux按键驱动程序设计详解---从简单到不简单【转】
转自:http://blog.csdn.net/coding__madman/article/details/51399353 版权声明:本文为博主原创文章,未经博主允许不得转载. 混杂设备驱动模型: ...
- (十二)Linux内核驱动之poll和select
使用非阻塞 I/O 的应用程序常常使用 poll, select, 每个允许一个进程来决定它是否可读或者写一个或多个文件而不阻塞. 这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来读或写. ...
- 嵌入式Linux驱动学习之路(十二)按键驱动-poll机制
实现的功能是在读取按键信息的时候,如果没有产生按键,则程序休眠在read函数中,利用poll机制,可以在没有退出的情况下让程序自动退出. 下面的程序就是在读取按键信息的时候,如果5000ms内没有按键 ...
- 入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖
文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键, ...
- 基于等待队列及poll机制的按键驱动代码分析和测试代码
按键驱动分析: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> ...
- Linux驱动之poll机制的理解与简单使用
之前在Linux驱动之按键驱动编写(中断方式)中编写的驱动程序,如果没有按键按下.read函数是永远没有返回值的,现在想要做到即使没有按键按下,在一定时间之后也会有返回值.要做到这种功能,可以使用po ...
- linux输入子系统之按键驱动
上一节中,我们讲解了Linux input子系统的框架,到内核源码里详细分析了输入子系统的分离分层的框架等. 上一节文章链接:http://blog.csdn.net/lwj103862095/ar ...
随机推荐
- ByteArrayBuilder
public class ByteArrayBuilder : IDisposable { #region Constants /// <summary> /// True in a by ...
- 个人对sort()排序方法中比较函数一直很混乱,今日理清
需求:使用随机数来打印出0-10,并排序. 代码: var a = new Array();var testArray = function() { while (1) { var b = parse ...
- Parse xml/json[xpath/jpath]
import groovy.util.XmlSlurper import groovy.util.XmlParser import com.eviware.soapui.support.GroovyU ...
- c# UrlEncode,UrlDecode
用 C# winform 处理 utf-8,gb2312编码转换方法 首先,在项目属性 的 应用程序——目标框架中,选择 .NET Framework 4 然后再添加引用——.NET 中选择 ...
- sqlserver查看所有的外键约束
select a.name as 约束名, object_name(b.parent_object_id) as 外键表, d.name as 外键列, object_name(b.reference ...
- (转) 浅析HTML5在移动应用开发中的使用
(转)浅析HTML5在移动应用开发中的使用 (原)http://www.iteye.com/magazines/67 2012-03-07 来自 UECD.163.com 编辑 wangguo ...
- 第一个structs+spring+hibernate的web程序
1. 数据库: Column Type Comment id int(11) Auto Increment name varchar(50) NULL url varchar(255) NUL ...
- NSDateFormatter遇到无法转换的问题
NSDateFormatter并不是万能的,并不是给出什么字符串都能转遍为NSDate类型,所转换的格式必须必须和你给出的格式想对应 比如说:NSString *dateStr = @"20 ...
- BaseDao代码,用于连接数据库实行增删改查等操作
在学习JavaWeb时会用到此代码,用于实行增删改查操作 1 package com.bdqn.dao; import java.sql.Connection; import java.sql.Dri ...
- VIm 一些常用的设置
一些常用的vim设置 以下内容皆来源于网络,感谢原作者.如果引用出处错误,请告知以便修改. 1. vim的几种模式和按键映射 转载自:[1] Map是Vim强大的一个重要原因,可以自定义各种快捷键 ...