linux按键驱动之poll

时间:2023-03-09 08:39:15
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的值就不为空。

  1. fds:存放需要被检测状态的Socket描述符;与select不同(select函数在调用之后,会清空检测socket描述符的数组),每当调用这个函数之后,系统不会清空这个数组,而是将有状态变化的描述符结构的revents变量状态变化,操作起来比较方便;
  2. nfds:用于标记数组fds中的struct pollfd结构元素的总数量;
  3. timeout:poll函数调用阻塞的时间,单位是MS(毫秒)

return value:

  1. 大于0:表示数组fds中有socket描述符的状态发生变化,或可以读取、或可以写入、或出错。并且返回的值表示这些状态有变化的socket描述符的总数量;此时可以对fds数组进行遍历,以寻找那些revents不空的socket描述符,然后判断这个里面有哪些事件以读取数据。
  2. 等于0:表示没有socket描述符有状态变化,并且调用超时。
  3. 小于0:此时表示有错误发生,此时全局变量errno保存错误码。

内核实现:

poll机制总结:

linux按键驱动之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;
}

  

结果:

linux按键驱动之poll

参考:

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