kernel中实现的循环buffer及使用方式

在kernel中轮询设备数据,用户空间通过阻塞式读取数据,难免在kernel中需要buffer存在,因此实现可配置的循环buffer,供大家参考,待优化部分是buffer加上信号量(如果数据量不是特别大,也没有必要加上信号量,加上保护在性能上或许得不偿失)。代码如下:

data_queue.h

#ifndef __DATA_QUEUE_H__
#define __DATA_QUEUE_H__

#include <linux/types.h>

typedef struct data_queue
{
	int buf_len;
	char* buf;
	int head;  // idle buffer head
	int tail;  // data buffer head
	bool full;
}data_queue;

typedef struct queue
{
	struct data_queue common;
	int (*enqueue)(struct data_queue*,const char* , int , int /* ms */);
	int (*dequeue)(struct data_queue*, char __user *, int );
	bool (*exist_data)(struct data_queue*);
}queue_t;

int enqueue_data(struct data_queue* queue, const char* data, int size, int timeout/* ms */);
int dequeue_data(struct data_queue* queue, char __user *buff, int size);
bool check_exist_data(struct data_queue* queue);

#define DECLARE_DATA_QUEUE(name, size) \
	static char buf_##name[size] = "\0"; \
	static struct queue name = { \
		.common = { \
			.buf_len = size, \
			.buf = buf_##name, \
			.head = 0, \
			.tail = 0, \
			.full = false, \
		}, \
		.enqueue = enqueue_data, \
		.dequeue = dequeue_data, \
		.exist_data = check_exist_data, \
	};


#endif

data_queue.c

#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <linux/spinlock_types.h>
#include <linux/delay.h>

#include "data_queue.h"

#define LOG_TAG	 "data_queue:"
#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_NOTICE LOG_TAG "%s:%d->" fmt "\n", \
					__func__, __LINE__, ##__VA_ARGS__)

DEFINE_SPINLOCK(data_queue_lock);

#define USE_LOCK 0
#define lock_share()  do {if(USE_LOCK) spin_lock(&data_queue_lock);} while(0)
#define unlock_share()  do {if(USE_LOCK) spin_unlock(&data_queue_lock);} while(0)

static int get_idle(struct data_queue* queue)
{
	int idle = 0;
	lock_share();
	print_dbg("---------- data queue debug -----------");
	print_dbg("buf_len = %d", queue->buf_len);
	print_dbg("full = %d", queue->full);
	print_dbg("head = %d", queue->head);
	print_dbg("tail = %d", queue->tail);
	print_dbg("---------------------------------------");
	if(!queue->full && queue->tail == queue->head) idle = queue->buf_len;
	if(queue->head > queue->tail) idle = queue->buf_len - (queue->head-queue->tail);
	if(queue->head < queue->tail) idle = queue->tail - queue->head;
	unlock_share();
	return idle;
}

static void push_data(struct data_queue* queue, const char* data, int size)
{
	memcpy(&queue->buf[queue->head], data, size);
	lock_share();
	queue->head = (queue->head + size)%queue->buf_len;
	if(queue->head == queue->tail) queue->full = true;
	unlock_share();
}

static int pull_data(struct data_queue* queue, char __user *buff, int size)
{
	int buf_len = queue->buf_len;
	if(buf_len - queue->tail >= size) {
		if(copy_to_user((void*)buff, (const void*)&queue->buf[queue->tail], (unsigned long)size) != 0) {
			print_err("copy_to_user failed.");
			return -1;
		}
	} else {
		int first_half = buf_len-queue->tail;
		if(copy_to_user((void*)buff, (const void*)&queue->buf[queue->tail],
			(unsigned long)(first_half))) {
			print_err("copy_to_user failed.");
			return -1;
		}
		if(copy_to_user((void*)(buff+first_half), (const void*)&queue->buf[0],
			(unsigned long)(size-first_half))) {
			print_err("copy_to_user failed.");
			return -1;
		}
	}
	lock_share();
	queue->tail = (queue->tail + size) % buf_len;
	if(queue->head == queue->tail) queue->full = false;
	unlock_share();
	return size;
}

int enqueue_data(struct data_queue* queue, const char* data, int size, int timeout/* ms */)
{
	int buf_len = queue->buf_len;
	int idle = 0;
	if(data == NULL || size > buf_len) {
		print_err("params are not valid.");
		return -1;
	}
	idle = get_idle(queue);

	if(idle >= size) {
		push_data(queue, data, size);
		return size;
	}

	while(timeout > 0) {
		msleep(10);
		timeout -= 10;
		if(get_idle(queue) > size) {
			push_data(queue, data, size);
			return size;
		}
	}
	return 0;
}

int dequeue_data(struct data_queue* queue, char __user *buff, int size)
{
	int valid_size = 0;
	int idle = 0;
	int buf_len = queue->buf_len;
	if(buff == NULL || size <= 0) {
		print_err("params are not valid.");
		return -1;
	}

	if(get_idle(queue) == buf_len) {
		print_info("no data exist.");
		return 0;
	}

	idle = get_idle(queue);
	valid_size = buf_len - idle;
	if(size >= valid_size) return pull_data(queue, buff, valid_size);
	return pull_data(queue, buff, size);
}

bool check_exist_data(struct data_queue* queue)
{
	return (queue->buf_len - get_idle(queue) > 0);
}

使用循环buffer的代码

#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;

// 定义全局循环buffer变量,有100个字节
DECLARE_DATA_QUEUE(rfid_data, 100);

...

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;
	// 从buffer中获取数据
	// 此处读取数据的长度没有特别要求,我的代码中读取数据是以MAX_VALID_UUID_LEN为整数倍的
	return rfid_data.dequeue(&rfid_data.common, buff,  (length/MAX_VALID_UUID_LEN)*MAX_VALID_UUID_LEN);
}

...

存储数据和获取数据方式相同,函数换位queue即可,参数根据函数定义,提供相应的参数。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值