/*
 *	Generic stream/socket interface for MAC protocols.
 *	
 *	Authors:	Alan Cox, <iialan@iifeak.swan.ac.uk>
 *
 *	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.
 */

#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/config.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/termios.h>
#include <linux/errno.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include "sock.h"
#include "sockgeneric.h"

#define min(a,b)		((a<b)?(a):(b))

static void gs_callback1(struct sock *sk)
{
	if(!sk->dead)
		wake_up_interruptible(sk->sleep);
}

static void gs_callback2(struct sock *sk, int len)
{
	if(!sk->dead)
		wake_up_interruptible(sk->sleep);
}

int gs_create(struct socket *sock, int protocol)
{
	struct sock *sk;
	sk=(struct sock *)kmalloc(sizeof(*sk), GFP_KERNEL);
	if(sk==NULL)
		return -ENOBUFS;
	sk->num=protocol;
	sk->reuse=0;
	if(!suser())
	{
		kfree_s((void *)sk, sizeof(*sk));
		return -EPERM;
	}
	
	switch(sock->type)
	{
		case SOCK_DGRAM:
		case SOCK_RAW:
			break;
		default:
			kfree_s((void *)sk,sizeof(*sk));
			return -ESOCKTNOSUPPORT;
	}
	sk->dead=0;
	sk->next=NULL;
	sk->broadcast=0;
	sk->rcvbuf=SK_RMEM_MAX;
	sk->sndbuf=SK_WMEM_MAX;
	sk->wmem_alloc=0;
	sk->rmem_alloc=0;
	sk->inuse=0;
	sk->shutdown=0;
	sk->prot=NULL;	/* So we use default free mechanisms */
	sk->err=0;
	skb_queue_head_init(&sk->receive_queue);
	skb_queue_head_init(&sk->write_queue);
	sk->send_head=NULL;
	skb_queue_head_init(&sk->back_log);
	sk->state=TCP_CLOSE;
	sk->type=sock->type;
	sk->socket=sock;
	sk->type=sock->type;
	sk->debug=0;
	sk->mtu=0;
	sk->state_change=gs_callback1;
	sk->data_ready=gs_callback2;
	sk->write_space=gs_callback1;
	sk->error_report=gs_callback1;
	sk->sleep=sock->wait;
	sk->zapped=1;
	sk->opt.generic.family=AF_UNSPEC;
	sk->opt.generic.device[0]=0;
	sock->protocol=sk;
	return(0);
}

int gs_dup(struct socket *newsock, struct socket *oldsock)
{
	return gs_create(newsock, oldsock->type);
}

int gs_release(struct socket *sock, struct socket *peer)
{
	struct sock *sk=sock->protocol;
	if(sk==NULL)
		return 0;
	if(!sk->dead)
		sk->state_change(sk);
	sk->dead=1;
	kfree_s(sk,sizeof(*sk));
	sock->protocol=NULL;
	return 0;
}

/*
 *	Generic mac protocol sockets are bound to physical device layers.
 */

int gs_bind(struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len)
{
	struct sock *sk=sock->protocol;
	if(sockaddr_len>=sizeof(short)+sizeof(sk->opt.generic.device))
		return -EINVAL;
	if(sk->opt.generic.bound)
		return -EINVAL;
	memcpy(sk->opt.generic.device,umyaddr->sa_data,sizeof(sk->opt.generic.device));
	sk->opt.generic.device[sizeof(sk->opt.generic.device)-1]=0;
	if(dev_get(sk->opt.generic.device)==NULL)
		return -ENODEV;
	sk->opt.generic.bound=1;
	sk->state=TCP_ESTABLISHED;
	return 0;
}
	
/*
 *	Connect is protocol specific - a totally generic layer just doesn't support it.
 */
 
int gs_connect(struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len, int flags)
{
	return -EOPNOTSUPP;
}


/*
 *	Generic sockets don't pair. Use streams loopback or something else pal.
 */
 
int gs_socketpair(struct socket *sock1, struct socket *sock2)
{
	return -EOPNOTSUPP;
}

/*
 *	Datagram only - no accepts
 */

int gs_accept(struct socket *sock, struct socket *newsock, int flags)
{
	return -EOPNOTSUPP;
}

/*
 *	Get the address name of our protocol layer.
 */
 
int gs_getname(struct socket *sock, struct sockaddr *uaddr, int *usockaddr_len, int peer)
{	
	struct sock *sk=sock->protocol;
	if(peer)
		return -ENOTCONN;
	uaddr->sa_family=AF_UNSPEC;	/* Hardware address */
	memcpy(uaddr->sa_data,sk->opt.generic.device, sizeof(uaddr->sa_data));
	*usockaddr_len=sizeof(struct sockaddr);
	return 0;
}

int gs_select(struct socket *sock, int sel_type, select_table *wait)
{
	return datagram_select(sock->protocol, sel_type, wait);
}

int gs_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	int err;
	long amount=0;
	struct sock *sk=sock->protocol;
	
	switch(cmd)
	{
		case TIOCOUTQ:
			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
			if(err)
				return err;
			amount=sk->sndbuf-sk->wmem_alloc;
			if(amount<0)
				amount=0;
			put_fs_long(amount,(unsigned long *)arg);
			return 0;
		case TIOCINQ:
		{
			sk_buff *skb;
			/* These two are safe on a single CPU system as only user tasks fiddle here */
			if((skb=skb_peek(&sk->receive_queue))!=NULL)
				amount=skb->len;
			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
			put_fs_long(amount,(unsigned long *)arg);
			return 0;
		}
		case SIOCGSTAMP:
			if (sk)
			{
				if(sk->stamp.tv_sec==0)
					return -ENOENT;
				err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval));
				if(err)
					return err;
					memcpy_tofs((void *)arg,&sk->stamp,sizeof(struct timeval));
				return 0;
			}
			return -EINVAL;
	}
	return -EINVAL;
}

int gs_listen(struct socket *sock, int len)
{
	return -EOPNOTSUPP;
}

int gs_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
{
	return -EINVAL;		
}

int gs_recvmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags, int *addr_len)
{
	int copied=0;
	int truesize;
	sk_buff *skb;
	struct sockaddr *addr=(struct sockaddr *)msg->msg_name;
	int er;
	struct sock *sk=sock->protocol;
	/* Easy */
	
	skb=skb_recv_datagram(sk,flags,noblock,&er);
	if(skb==NULL)
		return er;
		
	truesize=skb->len;
	copied=min(len,skb->len);
	skb_copy_datagram_iovec(skb,0,msg->msg_iov,copied);
	sk->stamp=skb->stamp;
	if(addr)
	{
		if(msg->msg_namelen)
			msg->msg_namelen=sizeof(struct sockaddr);
		strcpy(addr->sa_data,skb->dev->name);
	}
	skb_free_datagram(skb);
	return truesize;
}

int gs_shutdown(struct socket *sock, int flags)
{
	return -EOPNOTSUPP;
}

int gs_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{
	struct sock *sk=sock->protocol;
	if(optval==NULL)
		return -EINVAL;
	if(level==SOL_SOCKET)
		return sock_setsockopt(sk, level, optname, optval, optlen);
	return -EOPNOTSUPP;
}

int gs_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
	struct sock *sk=sock->protocol;
	if(level==SOL_SOCKET)
		return sock_getsockopt(sk, level, optname, optval, optlen);
	return -EOPNOTSUPP;
}

int gs_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	return -EINVAL;
}

#if 0

/* Example */

struct proto_ops generic_protocol=
{
	0,
	gs_create,
	gs_dup,
	gs_release,
	gs_bind,
	gs_connect,
	gs_socketpair,
	gs_accept,
	gs_getname,
	gs_select,
	gs_ioctl,
	gs_listen,
	gs_shutdown,
	gs_setsockopt,
	gs_getsockopt,
	gs_fcntl,
	gs_sendmsg,
	gs_recvmsg
};

#endif

/*
 *	A protocol for generic socket bindings.
 *
 *	This allows you to put datagram/raw socket behaviour onto any
 *	arbitary protocol class.
 */

static int gen_input(struct protocol *self, struct protocol *lower, sk_buff *skb, void *saddr, void *daddr)
{
	struct sock *sk=(struct sock *)self->user;
	if(sock_queue_rcv_skb(sk,skb)<0)
	{
		kfree_skb(skb, FREE_READ);
		return 0;
	}
	return 0;
}

static int gen_control(struct protocol *proto, struct protocol *src, int event, void *arg)
{
	if(event==CTL_DISCONNECT_BELOW)	/* Lost our link... */
	{
		struct sock *sk=(struct sock *)proto->user;
		sk->shutdown|=SHUTDOWN_MASK;	/* No more data transfer */
	}
	return default_protocol_control(proto,src,event,arg);
}

static int gen_binding(int protocol, int subid, unsigned char *key)
{
	return -EINVAL;
}

struct protocol proto_generic=
{
	NULL,
	"Generic Socket Queuer",
	0,
	0,
	0,
	0,
	NULL,
	gen_input,
	gen_input,
	gen_control,
	gen_binding,
	NULL,
	NULL,
	0,
};

	