/*
 * Copyright (C) 2007 Luca Bigliardi (shammash@artha.org).
 * Licensed under the GPL.
 */

#include <stddef.h>
#include <errno.h>
#include <libvdeplug.h>
#include <net_user.h>
#include <um_malloc.h>
#include "vde.h"

static int vde_user_init(void *data, void *dev)
{
	struct vde_data *pri = data;
	VDECONN *conn = NULL;
	int err = -EINVAL;

	pri->dev = dev;

	conn = vde_open(pri->vde_switch, pri->descr, pri->args);

	if (conn == NULL) {
		err = -errno;
		printk(UM_KERN_ERR "vde_user_init: vde_open failed, "
		       "errno = %d\n", errno);
		return err;
	}

	printk(UM_KERN_INFO "vde backend - connection opened\n");

	pri->conn = conn;

	return 0;
}

static int vde_user_open(void *data)
{
	struct vde_data *pri = data;

	if (pri->conn != NULL)
		return vde_datafd(pri->conn);

	printk(UM_KERN_WARNING "vde_open - we have no VDECONN to open");
	return -EINVAL;
}

static void vde_remove(void *data)
{
	struct vde_data *pri = data;

	if (pri->conn != NULL) {
		printk(UM_KERN_INFO "vde backend - closing connection\n");
		vde_close(pri->conn);
		pri->conn = NULL;
		kfree(pri->args);
		pri->args = NULL;
		return;
	}

	printk(UM_KERN_WARNING "vde_remove - we have no VDECONN to remove");
}

const struct net_user_info vde_user_info = {
	.init		= vde_user_init,
	.open		= vde_user_open,
	.close	 	= NULL,
	.remove	 	= vde_remove,
	.add_address	= NULL,
	.delete_address = NULL,
	.mtu		= ETH_MAX_PACKET,
	.max_packet	= ETH_MAX_PACKET + ETH_HEADER_OTHER,
};

void vde_init_libstuff(struct vde_data *vpri, struct vde_init *init)
{
	struct vde_open_args *args;

	vpri->args = uml_kmalloc(sizeof(struct vde_open_args), UM_GFP_KERNEL);
	if (vpri->args == NULL) {
		printk(UM_KERN_ERR "vde_init_libstuff - vde_open_args "
		       "allocation failed");
		return;
	}

	args = vpri->args;

	args->port = init->port;
	args->group = init->group;
	args->mode = init->mode ? init->mode : 0700;

	args->port ?  printk("port %d", args->port) :
		printk("undefined port");
}

int vde_user_read(void *conn, void *buf, int len)
{
	VDECONN *vconn = conn;
	int rv;

	if (vconn == NULL)
		return 0;

	rv = vde_recv(vconn, buf, len, 0);
	if (rv < 0) {
		if (errno == EAGAIN)
			return 0;
		return -errno;
	}
	else if (rv == 0)
		return -ENOTCONN;

	return rv;
}

int vde_user_write(void *conn, void *buf, int len)
{
	VDECONN *vconn = conn;

	if (vconn == NULL)
		return 0;

	return vde_send(vconn, buf, len, 0);
}