Linux 设备驱动poll轮询编程

来源:互联网 发布:idm mac版 知乎 编辑:程序博客网 时间:2024/06/11 05:00

Linux系统下网络模型数select最为常用,当然,select只是检测文件系统数据状态,并不只局限于网络编程,select的功能需要底层驱动提供支持,其中核心应用即为等待队列,其他模型,如poll和epoll,对驱动来说并无区别,驱动只是返回数据状态而已。驱动支持select,需要实现file_operations结构中的poll函数指针,其实现也非常简单,只是poll_wait函数的调用,原型如下:

unsigned int (*poll) (struct file *, struct poll_table_struct *);void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table * wait);

驱动程序poll实现方法:
unsigned int (*poll)(struct file *filp,poll_table *wait)
1、使用poll_wait将等待队列添加到poll_table中
2、返回描述设备是否可读或可写的掩码
位掩码:
POLLIN 设备可读
POLLRDNORM 数据可读
POLLOUT 设备可写
POLLWRNORM 数据可写
设备可读通常返回(POLLIN|POLLRDNORM)
设备可写通常返回(POLLOUT|POLLWRNONRM

需要注意的是poll_wait函数不会像它名字一样处于wait状态,仅供上层查询之用。其编程基本框架也比较固定,现修改simple等待队列驱动之中如下:


unsigned int simple_poll (struct file * filp, struct poll_table_struct * wait)
{
unsigned int mask = 0;
poll_wait(filp, &simple_queue, wait);


if (len > 0)
{
mask |= POLLIN | POLLRDNORM;
}


return mask;
}
在每次read之后都把len赋值为0,调用write把len赋值为数据长,poll中只需判断len,如果len大于0,则返回可读,整理之后,整个程序代码如下:


#include <linux/init.h>
#include <linux/module.h>


#include <linux/fs.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>


#include <linux/device.h>
#include <linux/poll.h>


dev_t devno;
struct class * simple_class;
static struct cdev cdev;


wait_queue_head_t simple_queue;


char test_data[255];
int len = 0;


unsigned int simple_poll (struct file * filp, struct poll_table_struct * wait)
{
unsigned int mask = 0;
poll_wait(filp, &simple_queue, wait);


if (len > 0)
{
mask |= POLLIN | POLLRDNORM;
}


return mask;
}


ssize_t simple_read(struct file * pfile,
char __user * buf, size_t size, loff_t * ppos)
{
int ret = len;
len = 0;
if (copy_to_user(buf, test_data, ret))
return -EFAULT;
else
return ret;
}


ssize_t simple_write(struct file * pfile, const char __user * buf, size_t count, loff_t * ppos)
{
if (count > 255)
{
return -EFAULT;
}


if (!copy_from_user(test_data, buf, count))
{
len = count;
wake_up(&simple_queue);
}
return len;
}


int simple_open(struct inode * pnode, struct file * pfile)
{
printk(KERN_INFO "open simple\n");
return 0;
}


int simple_release(struct inode * pnode, struct file * pfile)
{
printk(KERN_INFO "close simple\n");
return 0;
}


static struct file_operations simple_op = 
{
.owner = THIS_MODULE,
.read = simple_read,
.open = simple_open,
.release = simple_release,
.write = simple_write,
.poll = simple_poll,
};


static int __init initialization(void)
{
int result;


result = alloc_chrdev_region(&devno, 0, 1, "simple");
if (result < 0)
return result;


cdev_init(&cdev, &simple_op);
result = cdev_add(&cdev, devno, 1);


simple_class = class_create(THIS_MODULE, "simple");
device_create(simple_class, NULL, devno, NULL, "simple");


printk(KERN_INFO " init simple\n");


init_waitqueue_head(&simple_queue);


return result;
}


static void __exit cleanup(void)
{
device_destroy(simple_class, devno);
class_destroy(simple_class);


cdev_del(&cdev);
unregister_chrdev_region(devno, 1);
printk(KERN_INFO " cleanup simple\n");
}


module_init(initialization);
module_exit(cleanup);


MODULE_AUTHOR("alloc cppbreak@gmail.com");
MODULE_DESCRIPTION("A simple linux kernel module");
MODULE_VERSION("V0.1");
MODULE_LICENSE("Dual BSD/GPL");
0 0