在kernel中有一个数据buffer,在用户空间需要轮询读取,一般情况都会采用select、poll。因此需要在驱动中实现对select、poll的支持。在驱动中,对这两种的支持都只需实现一个poll函数即可。
实例如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mod_devicetable.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include "fm17550_drv.h"
#include "data_queue.h"
#define LOG_TAG "rfid:"
#define print_dbg(fmt, ...) printk(KERN_DEBUG LOG_TAG "%s:%d->" fmt "\n", \
__func__, __LINE__, ##__VA_ARGS__)
#define print_err(fmt, ...) printk(KERN_ERR LOG_TAG "%s:%d->" fmt "\n", \
__func__, __LINE__, ##__VA_ARGS__)
#define print_info(fmt, ...) printk(KERN_INFO LOG_TAG "%s:%d->" fmt "\n", \
__func__, __LINE__, ##__VA_ARGS__)
static rfid_dev_init g_dev_init = fm17550_init;
static rfid_dev_ops g_dev_ops;
static struct mutex g_mutex;
DECLARE_DATA_QUEUE(rfid_data, 100);
// 定义等待队列
static DECLARE_WAIT_QUEUE_HEAD(read_waitq);
static volatile bool card_touch = false;
static void notify_have_data(void)
{
print_dbg("enter.");
// mutex_lock(&g_mutex);
if(!card_touch) {
// 唤醒阻塞
wake_up_interruptible(&read_waitq);
card_touch = true;
}
// mutex_unlock(&g_mutex);
}
static int _open (struct inode * node, struct file *file)
{
print_dbg("enter.");
g_dev_ops.set_notify(notify_have_data);
return g_dev_ops.open_dev();
}
static int _release(struct inode * node, struct file * file)
{
print_dbg("enter.");
g_dev_ops.set_notify(NULL);
return g_dev_ops.close_dev();
}
static ssize_t _read (struct file *file, char __user * buff, size_t length, loff_t *offset)
{
// 该函数未实现阻塞
print_dbg("enter.");
if(!rfid_data.exist_data(&rfid_data.common)) return 0;
return rfid_data.dequeue(&rfid_data.common, buff, (length/MAX_VALID_UUID_LEN)*MAX_VALID_UUID_LEN);
}
static ssize_t _write (struct file *file, const char __user * buff, size_t length, loff_t *offset)
{
print_dbg("enter");
return 0;
}
unsigned int _poll (struct file *file, struct poll_table_struct * wait)
{
bool have_data = rfid_data.exist_data(&rfid_data.common);
print_dbg("have_data = %d", have_data);
if(!have_data) {
// mutex_lock(&g_mutex);
card_touch = false;
// mutex_unlock(&g_mutex);
print_dbg("wait ... ");
poll_wait(file, &read_waitq, wait); //不会立刻休眠,先加入休眠队列
// 如果没有数据,返回0即可,会自动阻塞
return 0;
}
// 有数据时返回该值
return POLLIN;
}
static long _ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
print_dbg("enter");
return 0;
}
/*-------------------------------------------------------------------------*/
#define DEV_NAME "rfid"
struct rfid_cdev {
dev_t dev;
struct cdev cdev;
struct class* class;
};
static struct rfid_cdev rfidCdev;
static struct file_operations rfid_fops = {
.owner = THIS_MODULE,
.open = _open,
.release = _release,
.read = _read,
.write = _write,
.poll = _poll,
.unlocked_ioctl = _ioctl,
};
static int __init rfid_init(void)
{
int res = -1;
struct device* device = NULL;
res = alloc_chrdev_region(&rfidCdev.dev, 0, 1, DEV_NAME);
if (res)
goto out;
cdev_init(&rfidCdev.cdev, &rfid_fops);
rfidCdev.cdev.owner = THIS_MODULE;
res = cdev_add(&rfidCdev.cdev, rfidCdev.dev, 1);
if (res)
goto out_unreg_chrdev;
rfidCdev.class = class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(rfidCdev.class)) {
res = PTR_ERR(rfidCdev.class);
goto out_cdev_del;
}
device = device_create(rfidCdev.class, NULL, rfidCdev.dev,
NULL, DEV_NAME);
if (IS_ERR(device)) {
res = PTR_ERR(device);
goto out_class_destroy;
}
res = g_dev_init(&g_dev_ops, &rfid_data);
if(!res) {
print_info("Rfid Driver Initialisation succeed.");
return res;
}
device_destroy(rfidCdev.class, rfidCdev.dev);
out_class_destroy:
class_destroy(rfidCdev.class);
out_cdev_del:
cdev_del(&rfidCdev.cdev);
out_unreg_chrdev:
unregister_chrdev_region(rfidCdev.dev, 1);
out:
print_err("Rfid Driver Initialisation failed.");
return res;
}
static void __exit rfid_exit(void)
{
print_dbg("enter");
device_destroy(rfidCdev.class, rfidCdev.dev);
class_destroy(rfidCdev.class);
cdev_del(&rfidCdev.cdev);
unregister_chrdev_region(rfidCdev.dev, 1);
g_dev_ops.dev_deinit();
}
module_init(rfid_init);
module_exit(rfid_exit);
MODULE_DESCRIPTION("fm17550 deivce driver");
MODULE_AUTHOR("binn.chen");
MODULE_LICENSE("GPL");
总结
实现驱动中poll函数两个要点:
- 无数据时,poll_wait加入等待队列,函数返回0,会自动阻塞
- 有数据时唤醒,系统会再调用驱动的poll函数,返回POLLIN即可