/*
 * (C) Finite State Machine Labs Inc. 2000 business@fsmlabs.com
 *
 * Released under the terms of GPL 2.
 * Open RTLinux makes use of a patented process described in
 * US Patent 5,995,745. Use of this process is governed
 * by the Open RTLinux Patent License which can be obtained from
 * www.fsmlabs.com/PATENT or by sending email to
 * licensequestions@fsmlabs.com
 */

#include <rtl.h>
#include <linux/types.h>
#include <linux/random.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/slab.h>

#include <rtl_fifo.h>

#define MODULE_NAME	"fifo_module"


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nathan Paul Simons <npsimons@fsmlabs.com>");
MODULE_DESCRIPTION("RTLinux FIFO regression test kernel module");
MODULE_SUPPORTED_DEVICE("/dev/rtf*");

MODULE_PARM(fifo_size, "i");
MODULE_PARM_DESC(fifo_size, "Size of RTL-FIFO to create in kernel");
MODULE_PARM(fifo_nr, "i");
MODULE_PARM_DESC(fifo_nr,
		 "RTL-FIFO number to create (ie, 0 -> /dev/rtf0)");

/* module parameter variables */
int fifo_size;
int fifo_nr;

/* global buffer for storing data read from the FIFO */
char *buf;

/* number of bytes read from the FIFO */
int buf_size;

extern void get_random_bytes(void *, int);
char *rtl_strerr(int thiserr);

/* simple handler to let us test user space writes and reads */
int dummy_handler(unsigned int fifo)
{
	return (0);
}				/* dummy_handler (fifo) */

/* Simple buffer handler; any writes from user space to the FIFO we are setup
 * to handle will be read into a buffer as big as the FIFO; any reads will
 * trigger us to write everything in the buffer back to the FIFO */

/* NOTE: this should work if this handler gets called for writes _and_ reads
 * of the FIFO.  Unfortunately it doesn't, so the test will fail with this
 * handler.  Use the dummy handler above. */
int fifo_handler(unsigned int fifo)
{
	int retval;

	rtl_printf("%s: fifo_handler called\n", MODULE_NAME);

	if ((retval = rtf_get(fifo, buf + buf_size, fifo_size)) < 0) {
		rtl_printf("%s: rtf_get (%d, buf, %d): %s\n",
			   MODULE_NAME, fifo, fifo_size,
			   rtl_strerr(retval));
		return (retval);
	} else if (retval > 0) {
		buf_size += retval;
	} else if (retval == 0) {
		if ((retval = rtf_put(fifo, buf, buf_size)) < 0) {
			rtl_printf("%s: rtf_put (%d, %s, %d): %s\n",
				   MODULE_NAME, fifo, buf, fifo_size,
				   rtl_strerr(retval));
			return (retval);
		}
		buf_size = 0;
	}

	return (0);
}				/* fifo_handler (fifo) */

int init_module(void)
{
	int retval;
	char *big_buf;

	buf_size = 0;

	if ((buf = kmalloc(fifo_size, GFP_ATOMIC)) == NULL) {
		rtl_printf("%s: kmalloc (%d, GFP_ATOMIC) failed\n",
			   MODULE_NAME, fifo_size);
		return (-1);
	}

	if ((retval = rtf_create(fifo_nr, fifo_size)) != 0) {
		rtl_printf("%s: rtf_create (%d, %d): %s\n", MODULE_NAME,
			   fifo_nr, fifo_size, rtl_strerr(retval));
		return (retval);
	}

	if ((big_buf = kmalloc(fifo_size + 1, GFP_ATOMIC)) == NULL) {
		rtl_printf("%s: kmalloc (%d, GFP_ATOMIC) failed\n",
			   MODULE_NAME, fifo_size);
		return (-1);
	}

	if ((retval = rtf_put(fifo_nr, big_buf, fifo_size + 1)) != -ENOSPC) {
		rtl_printf("%s: wrote %d bytes to %d size FIFO!\n",
			   MODULE_NAME, retval, fifo_size);
		return (retval);
	}

	kfree(big_buf);

	if ((retval = rtf_create_handler(fifo_nr, &dummy_handler)) != 0) {
		rtl_printf
		    ("%s: rtf_create_handler (%d, &fifo_handler): %s\n",
		     MODULE_NAME, fifo_nr, rtl_strerr(retval));
		return (retval);
	}
	return (0);
}

void cleanup_module(void)
{
	int retval;

	kfree(buf);

	if ((retval = rtf_destroy(fifo_nr)) != 0) {
		rtl_printf("%s: rtf_destroy (%d): %s\n", MODULE_NAME,
			   fifo_nr, rtl_strerr(retval));
	}
}

/* Error number mapping function. */
char *rtl_strerr(int thiserr)
{
	switch (thiserr) {
	case -ENODEV:
		return ("fifo is greater than or equal to RTF_NO");
	case -EINVAL:
		return
		    ("fifo is not a valid RT-FIFO identifier or handler is NULL");
	case -EBUSY:
		return ("fifo is already in use");
	case -ENOMEM:
		return
		    ("size bytes could not be allocated for the RT-FIFO");
	case -ENOSPC:
		return
		    ("insufficient space is available in the RT-FIFO for count bytes");
	default:
		return ("unknown error");
	}
}
