Linux Device Driver Kernel Module FIFO (circular buffer)
This is a simple implementation of a kernel module / device driver circular buffer (FIFO) suitable for
caching data in kernel space. Data is read from the kernel module via a user-space application via
a /proc file entry.
Also see:
Developed and tested with kernel 2.4.31 and 2.6.12.
For further details see the comments in the source code.
simplefifo.c
/*
Simple kernel module with circular buffer (FIFO)
We create a proc file entry, put some data in the buffer and
read from the proc file in user-space with user.c
Module (C) 2005 www.captain.at
FIFO (C) 2005 www.opersys.com (LRTBF http://www.opersys.com/lrtbf/)
USAGE:
* Compile the kernel module with "make"
* Load it with # insmod ./simplefifo.ko or # insmod ./simplefifo.o
* Start "user" - end it with CTRL-C; see "mydata" for the data read from
kernel space
* Test writing to the kernel module via the proc file with:
# echo "test" > /proc/simplefifo/data
(check the kernel log for the message)
Since writing to the buffer is prohibited when the buffer is read, this
method is only suitable for rather long intervals between subsequent
write actions.
It was tested successfully with a max. frequency of write actions of
5kHz on a PIII/450MHz machine. So if you have to log temperature or other
environmental data, or capture "seldom" events, this solution is OK.
I.e. for sampling audio data, double buffering is needed.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/apic.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
struct proc_dir_entry *proc_simplefifo;
struct proc_dir_entry *proc_simplefifo_data;
int noproc = 0;
#define BUFFER_SIZE 512000;
// COPYSTRING switches between 2 modes:
// If it is defined, a string is read from the proc file and we just have
// to write this human readable string into the output file
// If it's not defined, an integer value is read from the proc file and we
// have to convert this int value to a human readable format in user space
//#define COPYSTRING
#ifdef COPYSTRING
#define MAX_STR_LEN 32
unsigned long long buf[512000];
#else
#define MAX_STR_LEN (sizeof(int))
int buf[512000];
#endif
int disable_writing = 0;
volatile static unsigned int read_pos = 0, write_pos = 0;
static int read_proc_buf_pos = 0;
static inline int inc_read_pos (void) {
read_pos = (read_pos + 1) % BUFFER_SIZE;
return read_pos;
}
static inline int inc_write_pos (void) {
unsigned int newpos = (write_pos + 1) % BUFFER_SIZE;
if (read_pos == newpos) { // buffer full ?
inc_read_pos(); // yes, move read_pos one position ahead of write_pos
}
write_pos = newpos;
return write_pos;
}
static int write_data(struct file *file, const char *buffer,
unsigned long count, void *data) {
char buf[10];
int i;
for (i=0; i<10; i++) buf[i] = 0;
if (count > sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, buffer, count))
return -EFAULT;
printk("write_data: %.9s\n", buf);
return count;
}
// read_proc: read from circular buffer (FIFO)
// From: LRTBF http://www.opersys.com/lrtbf/
static int read_proc (char *page, char **start, off_t offset,
int count, int *eof, void *data) {
// offset: 0 if fresh read ; !=0 if *eof was not set to 1 in previous read
// if *eof was 0 previously, there is still old data available
// count: want to read count bytes
int len = 0;
*start = page;
*eof = 0;
// disable writing -> otherwise read_pos and write_pos are modified when
// writing to the FIFO and disturbs reading
disable_writing = 1;
while (read_pos != write_pos && len <= (count - MAX_STR_LEN )) {
// len <= (count - MAX_STR_LEN ) :
// -> assure that we want to read at least MAX_STR_LEN bytes
// MAX_STR_LEN = length of full single data record
// stop reading if we have written a max. of count bytes -or-
// there is no data in the buffer (read_pos != write_pos)
#ifdef COPYSTRING
len += sprintf ((*start)+len, "%llu\n", buf[read_pos]);
#else
memcpy((*start)+len, &buf[read_pos], MAX_STR_LEN);
len = len + MAX_STR_LEN;
#endif
inc_read_pos ();
}
if (read_pos == write_pos) {
*eof = 1; // we have read all data (whole file) -> signal EOF
}
// allow writing to the buffer again
disable_writing = 0;
return len;
}
static int __init mod_init (void) {
int i;
proc_simplefifo = proc_mkdir("simplefifo", 0);
if (!proc_simplefifo) {
printk (KERN_ERR "cannot create /proc/simplefifo\n");
noproc = 1;
}
proc_simplefifo_data = create_proc_read_entry ( "simplefifo/data",
0,
NULL,
read_proc,
(void *)&read_proc_buf_pos );
if (!proc_simplefifo_data) {
printk (KERN_ERR "cannot create /proc/simplefifo/data\n");
remove_proc_entry("simplefifo", 0);
noproc = 1;
} else {
proc_simplefifo_data->write_proc = write_data;
}
// write some dummy data to the buffer
for (i = 0; i < 10000; i++) {
if (!disable_writing) {
#ifdef COPYSTRING
buf[write_pos] = (long long)i;
#else
buf[write_pos] = (int)i;
#endif
inc_write_pos();
}
}
return 0;
}
static void __exit mod_exit (void) {
if (noproc == 0) {
remove_proc_entry("simplefifo/data", 0);
remove_proc_entry("simplefifo", 0);
}
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");
user.c
/*
Simple kernel module with circulaar buffer (FIFO)
We create a proc file entry, put some data in the buffer and
read from the proc file in user-space with user.c
(C) 2005 www.captain.at
USAGE: see simplefifo.c
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#define BUFSIZE 1024*100
#define MAXDATALEN (sizeof(int))
//#define COPYSTRING
struct timespec ts;
int end = 0;
void clean_exit(int dummy) {
end = 1;
}
main() {
int fd, fd2, len, wlen;
#ifdef COPYSTRING
char receive[BUFSIZE];
#else
int receive[BUFSIZE];
char writestr[32*BUFSIZE];
#endif
int tmp = sizeof(int);
int tmp2, k, i, o;
signal(SIGTERM, clean_exit);
signal(SIGINT, clean_exit);
fd = open("/proc/simplefifo/data", O_RDWR);
fd2 = open("mydata", O_RDWR | O_CREAT, 0600);
if( fd == -1) {
printf("open error...\n");
exit(0);
}
if( fd2 == -1) {
printf("open error2...\n");
exit(0);
}
#ifdef COPYSTRING
while(!end) {
len = read(fd, receive, BUFSIZE);
write(fd2, receive,len);
usleep(100); // avoid 100% CPU usage
}
#else
wlen = 0;
while(!end) {
len = read(fd, receive, BUFSIZE * MAXDATALEN);
for( i = 0; i < (len/tmp); i = i + 1) {
tmp2 = (int)receive[i];
wlen += sprintf (writestr+wlen, "%d\n", tmp2);
}
write(fd2, writestr, wlen);
wlen = 0;
usleep(100); // avoid 100% CPU usage
}
#endif
close(fd);
close(fd2);
}
Makefile
UNAME := $(shell uname -r)
KERNEL26 := 2.6
KERNELVERSION := $(findstring $(KERNEL26),$(UNAME))
all:: user
ifeq ($(KERNELVERSION),2.6)
obj-m := simplefifo.o
INCLUDE := -I/usr/include/asm/mach-default/
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all::
$(MAKE) -C $(KDIR) $(INCLUDE) SUBDIRS=$(PWD) modules
else
TARGET := simplefifo
INCLUDE := -I/lib/modules/`uname -r`/build/include -I/usr/include/asm/mach-default/
CFLAGS := -O2 -Wall -DMODULE -D__KERNEL__ -DLINUX
CC := gcc
all:: ${TARGET}.o
${TARGET}.o: ${TARGET}.c
$(CC) $(CFLAGS) ${INCLUDE} -c ${TARGET}.c
endif
user: user.c
gcc -o $@ $<
Last-Modified: Sat, 04 Feb 2006 16:03:07 GMT