/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: sys_generic.c 1.26 90/04/05 $"

/*
 * sys_generic.c
 *	File-type independent IO system-calls.
 */

/* $Log:	sys_generic.c,v $
 */

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/sysmacros.h"
#include "sys/signal.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/systm.h"
#include "sys/mutex.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/ioctl.h"
#include "sys/file.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/uio.h"
#include "sys/ttold.h"

#include "sys/vm.h"
#include "sys/pte.h"
#include "sys/engine.h"
#include "sys/plocal.h"

#include "sys/vnode.h"
#include "sys/session.h"
#include "sys/stream.h"
#include "sys/snode.h"

extern struct vnodeops spec_vnodeops;

/*
 * Read system call.
 */
read()
{
	register struct a {
		int	fdes;
		char	*cbuf;
		int	count;
	} *uap = (struct a *)u.u_ap;
	register struct file *fp;
	struct uio auio;
	struct iovec aiov;

	l.cnt.v_sysread++;
	GETF(fp, uap->fdes);
	if ((fp->f_flag&FREAD) == 0) {
		u.u_error = EBADF;
		return;
	}
	/*
	 * return as if a modem disconnect had occured. FHUPED
	 * is set by vhangup().
	 */
	if (fp->f_flag&FHUPED) {
		return;
	}
	if (uap->count < 0) {
		u.u_error = EINVAL;
		return;
	}

	aiov.iov_base = (caddr_t)uap->cbuf;
	auio.uio_resid = aiov.iov_len = uap->count;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_segflg = UIOSEG_USER;
	u.u_error = vno_rw(fp, UIO_READ, &auio);

	/*
	 * When attempting to read from a pipe or tty and O_NONBLOCK
	 * is set and there is no data available, POSIX specifies that
	 * a -1 be returned and that ERRNO be set to EAGAIN.  SYSV
	 * returns 0 (unless it is a streams device or a regular file
	 * with mandatory record locking enabled).  Here we are 
	 * adopting the POSIX way of life.
	 */
	if( u.u_error == EAGAIN && (fp->f_flag & FNDELAY) ) {
             u.u_r.r_val1 = -1;  /* assume the POSIX symantic */
	     if (u.u_ptype == BCS_PROC) {
		  /*
		   * BCS compliance insists that we return 0 here in all
		   * cases except when O_NDELAY is set and a
		   * read from a streams device would have blocked or
		   * a read from a regular file with mandatory record
		   * locking enabled would have blocked due to an enforced
		   * lock.  Since EAGAIN is never returned by a read of
		   * a VREG file except when mandatory locking is enforced,
		   * we feel justified in only checking the v_type of the
		   * file.
		   */
		  if (!V_STREAM(fp->f_vnode) && 
					((fp->f_vnode)->v_type != VREG)) {
		  	  
		          u.u_error = 0;
			  u.u_r.r_val1 = 0;
		  }
	     }
	} else {
	     u.u_r.r_val1 = uap->count - auio.uio_resid;
	     u.u_ioch += u.u_r.r_val1;
	     l.cnt.v_readch += u.u_r.r_val1;
        }
}

/*
 * Write system call
 */
write()
{
	register struct a {
		int	fdes;
		char	*cbuf;
		int	count;
	} *uap = (struct a *)u.u_ap;
	register struct file *fp;
	struct uio auio;
	struct iovec aiov;

	l.cnt.v_syswrite++;
	GETF(fp, uap->fdes);
	if ((fp->f_flag&FWRITE) == 0) {
		u.u_error = EBADF;
		return;
	}
	/*
	 * return as if a modem disconnect had occured. FHUPED
	 * is set by vhangup().
	 */
	if (fp->f_flag&FHUPED) {
		u.u_error = ENXIO;
		return;
	}
	if (uap->count < 0) {
		u.u_error = EINVAL;
		return;
	}

	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	aiov.iov_base = uap->cbuf;
	auio.uio_resid = aiov.iov_len = uap->count;
	auio.uio_segflg = UIOSEG_USER;
	u.u_error = vno_rw(fp, UIO_WRITE, &auio);

	/*
	 * When attempting to write to a pipe or tty and O_NONBLOCK
	 * (alias O_NDELAY) is set and the device cannot accept more
	 * data, POSIX specifies that a -1 be returned and that ERRNO
	 * be set to EAGAIN.  SYSV simply returns 0 (except when writing
	 * to a streams device or to a regular file with mandatory record
	 * locking enabled).  Here we are adopting the POSIX way of life.
	 */
	if( u.u_error == EAGAIN && (fp->f_flag & FNDELAY) ) {
	     u.u_r.r_val1 = -1;  /* assume the POSIX semantic */
	     if (u.u_ptype == BCS_PROC) {
		  /*
		   * BCS compliance insists that we return 0 here in all
		   * cases except when O_NDELAY is set and a
		   * write to a streams device would have blocked or
		   * a write to a regular file with mandatory record
		   * locking enabled would have blocked due to an enforced
		   * lock.  Since EAGAIN is never returned by a write to
		   * a VREG file except when mandatory locking is enforced,
		   * we feel justified in only checking the v_type of the
		   * file.
		   */
		  if (!V_STREAM(fp->f_vnode) && 
					((fp->f_vnode)->v_type != VREG)) {
		          u.u_error = 0;
			  u.u_r.r_val1 = 0;
		  }
	     }
        } else {
	     u.u_r.r_val1 = uap->count - auio.uio_resid;
	     u.u_ioch += u.u_r.r_val1;
	     l.cnt.v_writech += u.u_r.r_val1;
        }
}

/*
 * readv system call
 */
readv()
{
	register struct a {
		int	fdes;
		struct	iovec *iovp;
		int	iovcnt;
	} *uap = (struct a *)u.u_ap;
	register struct file *fp;
	struct uio auio;
	struct iovec aiov[16];
	register struct iovec *iov;
	int	i, count;

	l.cnt.v_sysread++;
	GETF(fp, uap->fdes);
	if ((fp->f_flag&FREAD) == 0) {
		u.u_error = EBADF;
		return;
	}
	/*
	 * return as if a modem disconnect had occured. FHUPED
	 * is set by vhangup().
	 */
	if (fp->f_flag&FHUPED) {
		return;
	}
	if (uap->iovcnt <= 0 || uap->iovcnt > sizeof(aiov)/sizeof(aiov[0])) {
		u.u_error = EINVAL;
		return;
	}
	auio.uio_iov = aiov;
	auio.uio_iovcnt = uap->iovcnt;
	u.u_error = copyin((caddr_t)uap->iovp, (caddr_t)aiov,
	    (unsigned)(uap->iovcnt * sizeof (struct iovec)));
	if (u.u_error)
		return;
	auio.uio_resid = 0;
	auio.uio_segflg = UIOSEG_USER;
	iov = auio.uio_iov;
	for (i = 0; i < auio.uio_iovcnt; i++) {
		if (iov->iov_len < 0) {
			u.u_error = EINVAL;
			return;
		}
		auio.uio_resid += iov->iov_len;
		if (auio.uio_resid < 0) {
			u.u_error = EINVAL;
			return;
		}
		iov++;
	}
	count = auio.uio_resid;
	u.u_error = vno_rw(fp, UIO_READ, &auio);
	u.u_r.r_val1 = count - auio.uio_resid;
	u.u_ioch += u.u_r.r_val1;
	l.cnt.v_readch += u.u_r.r_val1;
}

/*
 * writev system call
 */
writev()
{
	register struct a {
		int	fdes;
		struct	iovec *iovp;
		int	iovcnt;
	} *uap = (struct a *)u.u_ap;
	register struct file *fp;
	struct uio auio;
	struct iovec aiov[16];
	register struct iovec *iov;
	int	i, count;

	l.cnt.v_syswrite++;
	GETF(fp, uap->fdes);
	if ((fp->f_flag&FWRITE) == 0) {
		u.u_error = EBADF;
		return;
	}
	/*
	 * return as if a modem disconnect had occured. FHUPED
	 * is set by vhangup().
	 */
	if (fp->f_flag&FHUPED) {
		u.u_error = ENXIO;
		return;
	}
	if (uap->iovcnt <= 0 || uap->iovcnt > sizeof(aiov)/sizeof(aiov[0])) {
		u.u_error = EINVAL;
		return;
	}
	auio.uio_iov = aiov;
	auio.uio_iovcnt = uap->iovcnt;
	u.u_error = copyin((caddr_t)uap->iovp, (caddr_t)aiov,
	    (unsigned)(uap->iovcnt * sizeof (struct iovec)));
	if (u.u_error)
		return;
	auio.uio_resid = 0;
	auio.uio_segflg = UIOSEG_USER;
	iov = auio.uio_iov;
	for (i = 0; i < auio.uio_iovcnt; i++) {
		if (iov->iov_len < 0) {
			u.u_error = EINVAL;
			return;
		}
		auio.uio_resid += iov->iov_len;
		if (auio.uio_resid < 0) {
			u.u_error = EINVAL;
			return;
		}
		iov++;
	}
	count = auio.uio_resid;
	u.u_error = vno_rw(fp, UIO_WRITE, &auio);
	u.u_r.r_val1 = count - auio.uio_resid;
	u.u_ioch += u.u_r.r_val1;
	l.cnt.v_writech += u.u_r.r_val1;
}

/*
 * Ioctl system call
 */
ioctl()
{
	register struct file *fp;
	struct a {
		int	fdes;
		int	cmd;
		caddr_t	cmarg;
	} *uap = (struct a *) u.u_ap;

	if ((fp = getf(uap->fdes)) == NULL)
		return;
	/*
	 * return as if a modem disconnect had occured. FHUPED
	 * is set by vhangup().
	 */
	if (fp->f_flag&FHUPED) {
		u.u_error = ENXIO;
		return;
	}
	u.u_error = vno_ioctl(fp, uap->cmd, uap->cmarg);
}

stty()
{
	register struct a{
		int fdes;
		int arg;
		int narg;
	} *uap = (struct a *)u.u_ap;

	uap->narg = uap->arg;
	uap->arg = TIOCSETP;
	ioctl();
}

gtty()
{
	register struct a{
		int fdes;
		int arg;
		int narg;
	} *uap = (struct a *)u.u_ap;

	uap->narg = uap->arg;
	uap->arg = TIOCGETP;
	ioctl();
}
