Captain's Universe Home
Captain's Universe Home
Cosmic Ray Muon DetectorTeleGarden Pages
Time on MarsBryophyllum Plants
Jupiter Radio AstronomyAncient Pages
Salzburg Tourist GuideEarth Magnetometer
  H O M E     AJAX & MORE     LINUX & MORE     RTAI     XENOMAI     ADEOS IPIPE      
    JAVA & BROWSERS     *NIX     ELECTRONICS     REVIEWS     ARTEMIA     FAIRY SHRIMP      


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

Google
 
Web www.captain.at
go to top
© 1996-2010 . All rights reserved.
No reproduction, distribution, publishing or transmission of the copyrighted materials at this site is permitted. Policy
go to top