#include <linux/config.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/net.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/netprotocol.h>
#include <linux/stream.h>

static struct file_operations stream_file_ops= {
	stream_lseek,
	stream_read,
	stream_write,
	stream_readdir,
	stream_select,
	stream_ioctl,
	NULL,	/* mmap */
	stream_open,
	stream_close
};

static struct file_operations clone_file_ops= {
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,	/* mmap */
	clone_open,
	NULL
};

static struct file_operations stream_hup_file_ops= {
	stream_lseek,
	stream_hupped_read,
	stream_hupped_read,	/* Write is the same */
	stream_readdir,
	stream_select,
	stream_ioctl,
	NULL,	/* mmap */
	stream_open,
	stream_close
};

/*
 *	The clone driver takes its minor as a major, looks it up and if
 *	its a valid stream driver generates a clone of the driver (ie new
 *	inode). This is then used. This turns out to be rather easy.
 */

static int clone_open(struct inode *i, struct file *f)
{
	int major=MINOR(i->i_dev);
	struct inode *ni;
	struct inode *tmp;
	if(stream_table[i]==NULL)
		return -ENXIO;
	if(stream_table[i]->clone_open==NULL)
		return -ENXIO;
	ni=stream_table[i]->clone_open(f,stream_table[i]);
	if(ni==NULL)
		return -ENXIO;
	cli();
	tmp=f->inode;
	f->inode=ni;
	sti();
	iput(tmp);	/* Free the old inode */
	return 0;
}

/*
 *	Generic streams functions
 */
 
static int stream_lseek(struct inode *i, struct file *f, off_t t, int dir)
{
	return -ESPIPE;
}

static int stream_hupped_read(struct inode *i, struct file *f, char *buf, int len)
{
	return -ENXIO;
}

static int stream_readdir(struct inode *i, struct file *f, struct dirent *d, int n)
{
	return -EBADF;
}

/*
 *	Start with the basic streams ioctl calls!
 */

static int stream_op_pop(struct stream *s)
{
	struct protocol *p;
	cli();	/* Don't multithread this lot... Semaphore would be cleaner */
	
	/* The second case here is a 'stream tail' which we don't and can't
	   discard as its the device interface itself */
	if(s->proto_chain==NULL || s->proto_chain->stream_up==NULL)
	{
		sti();
		return -EINVAL; 
	}
	/*
	 *	Unhook us from the stream_down queue
	 */
	protocol_disconnect_stream(s->proto_chain,s);
	/*
	 *	Who is next ?
	 */
	p=s->proto_chain->stream_up;
	/*
	 *	Unbind the protocol we are discarding
	 */
	protocol_pop(s->proto_chain);
	/*
	 *	Set up our new head
	 */
	s->proto_chain=p;
	protocol_connect_stream(p,s);
	sti();
	return 0;
}

/*
 *	Stream I_PUSH. Find a module and insert it.
 */
 
static int stream_push(struct stream *s, char *name)
{
	struct protocol *p=protocol_find(name);
	int err;
	if(p==NULL)
		return -ENXIO;
	if(!(p->flags&PFL_STREAM))	/* Can't push me onto a stream */
		return -ENXIO;
		
	p=protocol_clone(&p);		/* Create an instance */
	if(p==NULL)
		return -ENOBUFS;
	protocol_disconnect_stream(s->proto_chain,s);	/* Unplug */
	err=protocol_bind(s->proto_chain,p);		/* Glue protocols together */
	if(p)
	{
		protocol_delete(p);			/* Fail: Discard and reconnect */
		protocol_connect_stream(s->proto_chain,s);
		return err;
	}
	s->proto_chain=p;				/* Connect up the new set */
	protcol_connect_stream(p,s);
	return 0;
}


static int stream_ioctl(struct inode *i, struct file *f, unsigned int opt, unsigned long arg)
{
	struct stream *s=(struct stream *)i->i_stream;
	char *sarg=(char *)arg;
	char name[17];
	int err;
	int size,count;
	sk_buff *skb;
	
	if(s==NULL)
	{
		printk("stream_ioctl: Impossible NULL pointer!\n");
		return -EINVAL;
	}
	switch(opt)
	{
		case I_PUSH:
			/*
			 *	Pile on another new exciting layer
			 */
			err=verify_area(VERIFY_READ,sarg,16);
			if(err)
				return err;
			memcpy_fromfs(name,sarg,16);
			name[16]=0;
			return stream_op_push(s,name);
		case I_POP:
			/*
			 *	Take off a layer. Not allowed to fail if
			 *	such a layer exists. (see the close code for 
			 *	why).
			 */
			if(s->flags&FL_HANGUP)
				return -ENXIO;	/* Hupped */
			return stream_op_pop(s);
		case I_LOOK:
			err=verify_area(VERIFY_WRITE, sarg,16);
			if(err)
				return err;
			if(s->proto_chain==NULL)
				return -EINVAL;
			memcpy_tofs(sarg,proto_chain->name,16);
			return 0;
		case I_FLUSH:
			if(arg==0||arg&~FLUSHRW)
				return -EINVAL;
			if(s->flags&FL_HANGUP)
				return -ENXIO;
			/*
			 *	Throw a flush message down the control chain
			 *	(immediate (ish))
			 */
			protocol_inform_lower(proto_chain, CTL_FLUSH_REQ, arg);
			/*
			 *	Did a hangup sneak in - eg off an interrupt
			 */
			if(s->flags&FL_HANGUP)
				return -ENXIO;
			return 0;
		/*
		 *	These are hard (save for later 8) )
		 */
		case I_SETSIG:
		case I_GETSIG:
			return -EINVAL;
		case I_FIND:
			err=verify_area(VERIFY_READ,sarg,16);
			if(err)
				return err;
			memcpy_fromfs(name,sarg,16);
			name[16]=0;
			/* This check is only needed to keep SVID error codes */
			if(protocol_find(name)==NULL)
				return -EINVAL;
				
			if(s->protocol_chain==NULL)
				return 0;
				
			return protocol_inform_lower(s->protocol_chain, CTL_ARE_YOU_THERE,name);
		case I_PEEK:
			return stream_peek(s,arg);
		case I_SRDOPT
			if(arg<RNORM || arg>RMSGN)
				return -EINVAL;
			s->rdopt=arg;
			return 0;
		case I_GRDOPT:
			err=verify_area(VERIFY_WRITE, sarg,sizeof(int));
			if(err)
				return err;
			/* DEC alpha watch these... */
			put_fs_long(s->rdopt, (long *)sarg);
			return 0;
		case I_NREAD:
			err=verify_area(VERIFY_WRITE, sarg,sizeof(int));
			if(err)
				return err;
			if(s->flags&FL_HUNGUP)
				return -ENXIO;
			cli();
			skb=skb_peek(&s->rcv_queue);
			if(skb==NULL)
			{
				sti();
				put_fs_long(0,(long *)sarg);
				return 0;
			}
			size=skb->len;
			count=0;
			do
			{
				count++;
				skb=skb->next;
			}
			while(skb!=skb_peek(skb->rcv_queue));
			sti();
			put_fs_long(size,(long *)sarg);
			return count;
		case I_FDINSERT:
			return -EINVAL;	/* Very hard */
		case I_STR:
			return stream_pass_ioctl(s,sarg);
		
		/* These are mostly easy - lock and pass the file pointer in
		   the sk_buff. Keep the file ptr in a known place so kfree_skb
		   can clean up in emergencies. Also ban it from network layers
		   that go host to host!!! */
		case I_SENDFD:
		case I_RECVFD:
			return -EINVAL;
		/*
		 *	These two are real fun. We probably ought to do 5.4 PLINK PUNLINK too.
		 */
		case I_LINK:
		case I_UNLINK:
			return -EINVAL;
			
		default:
			return -EINVAL;
	}
}

/*
 *	Read data from a stream in the appropriate mode allowing multiple
 *	overlapping channels without races (we hope).
 *
 *	Race avoidance:
 *		Interrupts are off during the initial peek so we are sure
 *	we have safe access to skb. We increment the user count and remove
 *	the bytes we need (actually we move up the data pointer). 
 *		We copy the data with interrupts on safe and sound in that
 *	nobody will free the buffer (which our data is still in).
 *		When finished we cli,decrement the user count. If we are last 
 *	user we free the buffer.
 *
 */
 
static int stream_read(struct inode *i, struct file *f, char *buf, int len)
{
	int bytes=0;
	int len;
	int err;
	int num;
	sk_buff *skb;
	char *dp;
	struct stream *s=i->i_stream;
	if(len==0)
		return 0;
	err=verify_area(VERIFY_WRITE,buf,len);
	if(err)
		return err;
	if(s->err)
	{
		cli();
		err=s->err;
		s->err=0;
		sti();
		return -err;
	}
	while(bytes<len)
	{
		cli();
		skb=skb_peek(&sk->receive_queue);
		if(skb==NULL)
		{
			if(f->mode&O_NDELAY)
			{
				sti();
				if(bytes)
					return bytes;
				return -EAGAIN;
			}
			interruptible_sleep_on(s->sleep);
			if( current->signal & ~current->blocked)
			{
				sti();
				if(bytes)
					return bytes;
				return -ERESTARTSYS;
			}
			continue;
		}
		skb->users++;

		num=min(skb->len,len-bytes);	
		dp=skb_pull(skb,num);
		
		/*
		 *	We must unlink (but not free) the buffer in case
		 *	it is still on the queue. Nobody else must find
		 *	the buffer if it is now discarded but still being
		 *	copied from...
		 */
		 
		if(skb->len==0 || sk->rdopt==RMSGD)
			skb_unlink(skb);
		/*
		 *	Back to normal state while we copy, secure in the
		 *	lock on skb->users
		 */
		sti();
		memcpy_tofs(buf,dp,len);
		cli();
		/*
		 *	Release our lock, and if appropriate free the 
		 *	buffer.
		 */
		skb->users--;
		if(skb->len==0 || sk->rdopt==RMSGD)
		{
			if(!skb->users)
				kfree_skb(skb, FREE_READ);
		}
		sti();
		/*
		 *	Add up our byte count
		 */
		bytes+=num;
		/*
		 *	RNORM ignores message boundaries and carries on.
		 */
		if(s->rdopt==RNORM)
			continue;	/* Next buffer */
	}
	return bytes;
}


/*
 *	Our intercept for a memory free of one of our buffers
 */
 
static void stream_mem_wake(sk_buff *skb)
{
	struct stream *s=(struct stream *)skb->free_ptr;
	s->wmem-=skb->mem_len;
	/* Wake any blocked writers */
	wake_up_interruptible(s->sleep);
}

static int stream_write(struct inode *i, struct file *f, char *buf, int len)
{
	/* Send a message to a stream */
	sk_buff *skb=NULL;
	int err;
	struct stream *s=i->i_stream;
	
	err=verify_area(VERIFY_READ,buf,len);
	if(err)
		return err;
	
	if(s->proto_chain==NULL)
		return -EINVAL;
	if(s->proto_chain->input==NULL)
		return -EINVAL;
	do
	{
		if(s->err)
		{
			cli();
			err=s->err;
			s->err=0;
			sti();
			return -err;
		}
		
		if(f->f_flags&O_NDELAY)
			return -EAGAIN;
		
		cli();
		/*
		 *	End to end flow. We can only permit so much data
		 *	to flow into a stream head until it pops out the
		 *	other end.
		 */
		if(s->wmem+len>s->wlim)
		{
			interruptible_sleep_on(s->sleep);
			if( current->signal & ~current->blocked)
			{
				sti();
				return -ERESTARTSYS;
			}
			skb=alloc_skb(len, GFP_KERNEL);
			if(skb==NULL)
				return -ENOBUFS;
			break;
		}
		sti();
	}
	while(skb==NULL);
	s->wmem+=skb->mem_len;
	skb->free_ptr=s;
	skb->free_function=stream_mem_wake;
	memcpy_fromfs(skb_put(skb,len),buf,len);
	
	/*
	 *	Feed buffer to stream
	 */
	 
	s->proto_chain->input(s->proto_chain, NULL, skb, NULL, NULL);
	return len;	
}

static void stream_close(struct inode *i, struct file *f)
{
	struct stream *s=(struct stream *)i->i_stream;
	sk_buff *skb;
	s->flags=FL_HANGUP;	/* FL_CLOSING needed to make this clean */
	/*
	 *	Pop all modules.
	 */
	while(!stream_op_pop(s));	
	/*
	 *	Free all buffers.
	 */
	while((skb=skb_dequeue(&s->rcv_queue))!=NULL)
		kfree_skb(skb, FREE_READ);
	/*
	 *	Having looked at the open code you are now wondering when
	 *	the wmem value will get reset. The answer is it doesn't. With
	 *	outstanding buffers taking memory the stream we issued which
	 *	is notionally in some ways both head and tail has resources
	 *	allocated that are still in use. When the prior buffers are
	 *	freed the wmem value will return to 0. You can use the
	 *	wmem==0 check in any modules you do. If we just 'forgot' about
	 *	old usages you could run out of memory by continual re-opening.
	 */
	 
	 /*
	  *	Finally free the stream so people's clone drivers can reissue.
	  */
	 s->flags&=~FL_ALLOC;
}

