字符設備阻塞式輪詢實現

在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函數兩個要點:

  1. 無數據時,poll_wait加入等待隊列,函數返回0,會自動阻塞
  2. 有數據時喚醒,系統會再調用驅動的poll函數,返回POLLIN即可
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章