/* *****************************************************************
 * (C) Copyright 2007 Benjamin Krill <ben@codiert.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
 * MA 02110-1301 USA
 * *****************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/mm.h>
#include "fpgafs.h"

#define MEM_SIZE 255
/* some test memory */
static char *mem;
static unsigned int stat;

static int do_cmd(u32 cmd, u32 data)
{
	switch(cmd) {
		case BLA:
			stat = data;
			break;
		default:
			return -EFAULT;
	}
	return 0;
}

static int fpgafs_stat_dbg(struct fpga_context *ctx, unsigned char *buf, int len)
{
	//struct fpga_context *ctx = file->private_data;
	u32 data;
	data=stat;

	if (copy_to_user(buf, &data, sizeof(data)))
		return -EFAULT;

	return 4;
}

static int fpgafs_cmd_dbg(struct fpga_context *ctx, const char __user *buf, int len)
{
	u32 data, __user *udata;

	if (len < 4)
		return -EINVAL;

	udata = (void __user *)buf;
	if (!access_ok(VERIFY_READ, buf, len))
		return -EFAULT;

	if (__get_user(data, udata))
		return -EFAULT;

	do_cmd(BLA,data);
	return 4;
}

static int fpgafs_send_data_dbg(struct fpga_context *ctx, const char __user *buf, int len)
{
	u32 cp = 0;
	u8 __user *usr;

	if (mem == NULL)
		return -EBUSY;

	len = (len > MEM_SIZE)?MEM_SIZE:len;

	if (len < 2)
		return -EINVAL;

	if (!access_ok(VERIFY_READ, buf, len))
		return -EFAULT;

	while (cp < len) {
		usr = (u8*)&buf[cp];
		if (__get_user(mem[cp], usr))
			return -EFAULT;
		cp++;
	}

	return len;
}

static int fpgafs_recv_data_dbg(struct fpga_context *ctx, unsigned char *buf, int len)
{
	printk("fpgafs: receive data DEBUG\n");
	len = (len > MEM_SIZE)?MEM_SIZE:len;
	if (copy_to_user(buf, mem, len))
		return -EFAULT;

	return len;
}

static int fpgafs_read_load_dbg(struct fpga_context *ctx, unsigned char *buf, int len)
{
	if (ctx->load_buf == NULL)
		return -EBUSY;

	len = len > sizeof(ctx->load_buf)?sizeof(ctx->load_buf):len;

	if (copy_to_user(buf, ctx->load_buf, len))
		return -EFAULT;

	return len;
}

static int fpgafs_write_load_dbg(struct fpga_context *ctx, const char __user *buf, int len)
{
	u32 cp = 0;
	u8 __user *usr;

	if (ctx->load_buf != NULL)
		return -EBUSY;

	if (len < 2)
		return -EINVAL;

	if (!access_ok(VERIFY_READ, buf, len))
		return -EFAULT;

	ctx->load_buf = kmalloc(len,GFP_USER);

	while (cp < len) {
		usr = (u8*)&buf[cp];
		if (__get_user(ctx->load_buf[cp], usr))
			return -EFAULT;
		cp++;
	}

	return len;
}


static int fpgafs_init_dbg(void)
{
	mem = kmalloc(MEM_SIZE,GFP_USER);
	return 0;
}

static int fpgafs_exit_dbg(void)
{
	kfree(mem);
	return 0;
}

static struct fpgafs_lldrv fpgafs_lldrv_dbg = {
	.name = "dbg",
	.init = fpgafs_init_dbg,
	.exit = fpgafs_exit_dbg,
	.send = &fpgafs_send_data_dbg,
	.recv = &fpgafs_recv_data_dbg,
	.stat = &fpgafs_stat_dbg,
	.cmd  = &fpgafs_cmd_dbg,
	.read_load = &fpgafs_read_load_dbg,
	.write_load = &fpgafs_write_load_dbg,
};

/* init exit functions ... */
int __init fpgafs_lldrv_dbg_init(void)
{
	return fpgafs_register_lldrv(&fpgafs_lldrv_dbg);
}

void __exit fpgafs_lldrv_dbg_exit(void)
{
	fpgafs_unregister_lldrv(&fpgafs_lldrv_dbg);
}

module_init(fpgafs_lldrv_dbg_init);
module_exit(fpgafs_lldrv_dbg_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Benjamin Krill <ben@codiert.org>");
