/*	$NetBSD: hpux_tty.c,v 1.22 2003/03/27 15:54:02 he Exp $	*/

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * from: Utah $Hdr: hpux_tty.c 1.14 93/08/05$
 *
 *	@(#)hpux_tty.c	8.3 (Berkeley) 1/12/94
 */

/*
 * stty/gtty/termio emulation stuff
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: hpux_tty.c,v 1.22 2003/03/27 15:54:02 he Exp $");

#if defined(_KERNEL_OPT)
#include "opt_compat_43.h"
#endif

#ifndef COMPAT_43
#define COMPAT_43
#endif

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filedesc.h>
#include <sys/ioctl.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/file.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sys/kernel.h>

#include <compat/hpux/hpux.h>
#include <compat/hpux/hpux_termio.h>
#include <compat/hpux/hpux_syscallargs.h>

/*
 * Map BSD/POSIX style termios info to and from SYS5 style termio stuff.
 */
int
hpux_termio(fd, com, data, l)
	int fd, com;
	caddr_t data;
	struct lwp *l;
{
	struct proc *p = l->l_proc;
	struct file *fp;
	struct termios tios;
	struct hpux_termios htios;
	int line, error;
	int newi = 0;
	int (*ioctlrout) __P((struct file *fp, u_long com,
	    void *data, struct proc *p));


	fp = p->p_fd->fd_ofiles[fd];
	ioctlrout = fp->f_ops->fo_ioctl;
	switch (com) {
	case HPUXTCGETATTR:
		newi = 1;
		/* fall into ... */
	case HPUXTCGETA:
		/*
		 * Get BSD terminal state
		 */
		if ((error = (*ioctlrout)(fp, TIOCGETA, (caddr_t)&tios, p)))
			break;
		memset((char *)&htios, 0, sizeof htios);
		/*
		 * Set iflag.
		 * Same through ICRNL, no BSD equivs for IUCLC, IENQAK
		 */
		htios.c_iflag = tios.c_iflag & 0x1ff;
		if (tios.c_iflag & IXON)
			htios.c_iflag |= TIO_IXON;
		if (tios.c_iflag & IXOFF)
			htios.c_iflag |= TIO_IXOFF;
		if (tios.c_iflag & IXANY)
			htios.c_iflag |= TIO_IXANY;
		/*
		 * Set oflag.
		 * No BSD equivs for OLCUC/OCRNL/ONOCR/ONLRET/OFILL/OFDEL
		 * or any of the delays.
		 */
		if (tios.c_oflag & OPOST)
			htios.c_oflag |= TIO_OPOST;
		if (tios.c_oflag & ONLCR)
			htios.c_oflag |= TIO_ONLCR;
		if (tios.c_oflag & OXTABS)
			htios.c_oflag |= TIO_TAB3;
		/*
		 * Set cflag.
		 * Baud from ospeed, rest from cflag.
		 */
		htios.c_cflag = bsdtohpuxbaud(tios.c_ospeed);
		switch (tios.c_cflag & CSIZE) {
		case CS5:
			htios.c_cflag |= TIO_CS5; break;
		case CS6:
			htios.c_cflag |= TIO_CS6; break;
		case CS7:
			htios.c_cflag |= TIO_CS7; break;
		case CS8:
			htios.c_cflag |= TIO_CS8; break;
		}
		if (tios.c_cflag & CSTOPB)
			htios.c_cflag |= TIO_CSTOPB;
		if (tios.c_cflag & CREAD)
			htios.c_cflag |= TIO_CREAD;
		if (tios.c_cflag & PARENB)
			htios.c_cflag |= TIO_PARENB;
		if (tios.c_cflag & PARODD)
			htios.c_cflag |= TIO_PARODD;
		if (tios.c_cflag & HUPCL)
			htios.c_cflag |= TIO_HUPCL;
		if (tios.c_cflag & CLOCAL)
			htios.c_cflag |= TIO_CLOCAL;
		/*
		 * Set lflag.
		 * No BSD equiv for XCASE.
		 */
		if (tios.c_lflag & ECHOE)
			htios.c_lflag |= TIO_ECHOE;
		if (tios.c_lflag & ECHOK)
			htios.c_lflag |= TIO_ECHOK;
		if (tios.c_lflag & ECHO)
			htios.c_lflag |= TIO_ECHO;
		if (tios.c_lflag & ECHONL)
			htios.c_lflag |= TIO_ECHONL;
		if (tios.c_lflag & ISIG)
			htios.c_lflag |= TIO_ISIG;
		if (tios.c_lflag & ICANON)
			htios.c_lflag |= TIO_ICANON;
		if (tios.c_lflag & NOFLSH)
			htios.c_lflag |= TIO_NOFLSH;
		/*
		 * Line discipline
		 */
		if (!newi) {
			line = 0;
			(void) (*ioctlrout)(fp, TIOCGETD, (caddr_t)&line, p);
			htios.c_reserved = line;
		}
		/*
		 * Set editing chars.
		 * No BSD equiv for VSWTCH.
		 */
		htios.c_cc[HPUXVINTR] = tios.c_cc[VINTR];
		htios.c_cc[HPUXVQUIT] = tios.c_cc[VQUIT];
		htios.c_cc[HPUXVERASE] = tios.c_cc[VERASE];
		htios.c_cc[HPUXVKILL] = tios.c_cc[VKILL];
		htios.c_cc[HPUXVEOF] = tios.c_cc[VEOF];
		htios.c_cc[HPUXVEOL] = tios.c_cc[VEOL];
		htios.c_cc[HPUXVEOL2] = tios.c_cc[VEOL2];
		htios.c_cc[HPUXVSWTCH] = 0;
#if 1
		/*
		 * XXX since VMIN and VTIME are not implemented,
		 * we need to return something reasonable.
		 * Otherwise a GETA/SETA combo would always put
		 * the tty in non-blocking mode (since VMIN == VTIME == 0).
		 */
		if (fp->f_flag & FNONBLOCK) {
			htios.c_cc[HPUXVMINS] = 0;
			htios.c_cc[HPUXVTIMES] = 0;
		} else {
			htios.c_cc[HPUXVMINS] = 6;
			htios.c_cc[HPUXVTIMES] = 1;
		}
#else
		htios.c_cc[HPUXVMINS] = tios.c_cc[VMIN];
		htios.c_cc[HPUXVTIMES] = tios.c_cc[VTIME];
#endif
		htios.c_cc[HPUXVSUSP] = tios.c_cc[VSUSP];
		htios.c_cc[HPUXVSTART] = tios.c_cc[VSTART];
		htios.c_cc[HPUXVSTOP] = tios.c_cc[VSTOP];
		if (newi)
			memcpy(data, (char *)&htios, sizeof htios);
		else
			termiostotermio(&htios, (struct hpux_termio *)data);
		break;

	case HPUXTCSETATTR:
	case HPUXTCSETATTRD:
	case HPUXTCSETATTRF:
		newi = 1;
		/* fall into ... */
	case HPUXTCSETA:
	case HPUXTCSETAW:
	case HPUXTCSETAF:
		/*
		 * Get old characteristics and determine if we are a tty.
		 */
		if ((error = (*ioctlrout)(fp, TIOCGETA, (caddr_t)&tios, p)))
			break;
		if (newi)
			memcpy((char *)&htios, data, sizeof htios);
		else
			termiototermios((struct hpux_termio *)data,
			    &htios, &tios);
		/*
		 * Set iflag.
		 * Same through ICRNL, no HP-UX equiv for IMAXBEL
		 */
		tios.c_iflag &= ~(IXON|IXOFF|IXANY|0x1ff);
		tios.c_iflag |= htios.c_iflag & 0x1ff;
		if (htios.c_iflag & TIO_IXON)
			tios.c_iflag |= IXON;
		if (htios.c_iflag & TIO_IXOFF)
			tios.c_iflag |= IXOFF;
		if (htios.c_iflag & TIO_IXANY)
			tios.c_iflag |= IXANY;
		/*
		 * Set oflag.
		 * No HP-UX equiv for ONOEOT
		 */
		tios.c_oflag &= ~(OPOST|ONLCR|OXTABS);
		if (htios.c_oflag & TIO_OPOST)
			tios.c_oflag |= OPOST;
		if (htios.c_oflag & TIO_ONLCR)
			tios.c_oflag |= ONLCR;
		if (htios.c_oflag & TIO_TAB3)
			tios.c_oflag |= OXTABS;
		/*
		 * Set cflag.
		 * No HP-UX equiv for CCTS_OFLOW/CCTS_IFLOW/MDMBUF
		 */
		tios.c_cflag &=
			~(CSIZE|CSTOPB|CREAD|PARENB|PARODD|HUPCL|CLOCAL);
		switch (htios.c_cflag & TIO_CSIZE) {
		case TIO_CS5:
			tios.c_cflag |= CS5; break;
		case TIO_CS6:
			tios.c_cflag |= CS6; break;
		case TIO_CS7:
			tios.c_cflag |= CS7; break;
		case TIO_CS8:
			tios.c_cflag |= CS8; break;
		}
		if (htios.c_cflag & TIO_CSTOPB)
			tios.c_cflag |= CSTOPB;
		if (htios.c_cflag & TIO_CREAD)
			tios.c_cflag |= CREAD;
		if (htios.c_cflag & TIO_PARENB)
			tios.c_cflag |= PARENB;
		if (htios.c_cflag & TIO_PARODD)
			tios.c_cflag |= PARODD;
		if (htios.c_cflag & TIO_HUPCL)
			tios.c_cflag |= HUPCL;
		if (htios.c_cflag & TIO_CLOCAL)
			tios.c_cflag |= CLOCAL;
		/*
		 * Set lflag.
		 * No HP-UX equiv for ECHOKE/ECHOPRT/ECHOCTL
		 * IEXTEN treated as part of ICANON
		 */
		tios.c_lflag &= ~(ECHOE|ECHOK|ECHO|ISIG|ICANON|IEXTEN|NOFLSH);
		if (htios.c_lflag & TIO_ECHOE)
			tios.c_lflag |= ECHOE;
		if (htios.c_lflag & TIO_ECHOK)
			tios.c_lflag |= ECHOK;
		if (htios.c_lflag & TIO_ECHO)
			tios.c_lflag |= ECHO;
		if (htios.c_lflag & TIO_ECHONL)
			tios.c_lflag |= ECHONL;
		if (htios.c_lflag & TIO_ISIG)
			tios.c_lflag |= ISIG;
		if (htios.c_lflag & TIO_ICANON)
			tios.c_lflag |= (ICANON|IEXTEN);
		if (htios.c_lflag & TIO_NOFLSH)
			tios.c_lflag |= NOFLSH;
		/*
		 * Set editing chars.
		 * No HP-UX equivs of VWERASE/VREPRINT/VDSUSP/VLNEXT
		 * /VDISCARD/VSTATUS/VERASE2
		 */
		tios.c_cc[VINTR] = htios.c_cc[HPUXVINTR];
		tios.c_cc[VQUIT] = htios.c_cc[HPUXVQUIT];
		tios.c_cc[VERASE] = htios.c_cc[HPUXVERASE];
		tios.c_cc[VKILL] = htios.c_cc[HPUXVKILL];
		tios.c_cc[VEOF] = htios.c_cc[HPUXVEOF];
		tios.c_cc[VEOL] = htios.c_cc[HPUXVEOL];
		tios.c_cc[VEOL2] = htios.c_cc[HPUXVEOL2];
		tios.c_cc[VMIN] = htios.c_cc[HPUXVMINS];
		tios.c_cc[VTIME] = htios.c_cc[HPUXVTIMES];
		tios.c_cc[VSUSP] = htios.c_cc[HPUXVSUSP];
		tios.c_cc[VSTART] = htios.c_cc[HPUXVSTART];
		tios.c_cc[VSTOP] = htios.c_cc[HPUXVSTOP];

		/*
		 * Set the new stuff
		 */
		if (com == HPUXTCSETA || com == HPUXTCSETATTR)
			com = TIOCSETA;
		else if (com == HPUXTCSETAW || com == HPUXTCSETATTRD)
			com = TIOCSETAW;
		else
			com = TIOCSETAF;
		error = (*ioctlrout)(fp, com, (caddr_t)&tios, p);
		if (error == 0) {
			/*
			 * Set line discipline
			 */
			if (!newi) {
				line = htios.c_reserved;
				(void) (*ioctlrout)(fp, TIOCSETD,
						    (caddr_t)&line, p);
			}
			/*
			 * Set non-blocking IO if VMIN == VTIME == 0, clear
			 * if not.  Should handle the other cases as well.
			 * Note it isn't correct to just turn NBIO off like
			 * we do as it could be on as the result of a fcntl
			 * operation.
			 *
			 * XXX - wouldn't need to do this at all if VMIN/VTIME
			 * were implemented.
			 */
			{
				struct hpux_sys_fcntl_args {
					int fdes, cmd, arg;
				} args;
				int flags, nbio;

				nbio = (htios.c_cc[HPUXVMINS] == 0 &&
					htios.c_cc[HPUXVTIMES] == 0);
				if ((nbio && (fp->f_flag & FNONBLOCK) == 0) ||
				    (!nbio && (fp->f_flag & FNONBLOCK))) {
					args.fdes = fd;
					args.cmd = F_GETFL;
					args.arg = 0;
					(void) hpux_sys_fcntl(l, &args, &flags);
					if (nbio)
						flags |= HPUXNDELAY;
					else
						flags &= ~HPUXNDELAY;
					args.cmd = F_SETFL;
					args.arg = flags;
					(void) hpux_sys_fcntl(l, &args, &flags);
				}
			}
		}
		break;

	default:
		error = EINVAL;
		break;
	}
	return(error);
}

void
termiototermios(tio, tios, bsdtios)
	struct hpux_termio *tio;
	struct hpux_termios *tios;
	struct termios *bsdtios;
{
	int i;

	memset((char *)tios, 0, sizeof *tios);
	tios->c_iflag = tio->c_iflag;
	tios->c_oflag = tio->c_oflag;
	tios->c_cflag = tio->c_cflag;
	tios->c_lflag = tio->c_lflag;
	tios->c_reserved = tio->c_line;
	for (i = 0; i <= HPUXVSWTCH; i++)
		tios->c_cc[i] = tio->c_cc[i];
	if (tios->c_lflag & TIO_ICANON) {
		tios->c_cc[HPUXVEOF] = tio->c_cc[HPUXVEOF];
		tios->c_cc[HPUXVEOL] = tio->c_cc[HPUXVEOL];
		tios->c_cc[HPUXVMINS] = 0;
		tios->c_cc[HPUXVTIMES] = 0;
	} else {
		tios->c_cc[HPUXVEOF] = 0;
		tios->c_cc[HPUXVEOL] = 0;
		tios->c_cc[HPUXVMINS] = tio->c_cc[HPUXVMIN];
		tios->c_cc[HPUXVTIMES] = tio->c_cc[HPUXVTIME];
	}
	tios->c_cc[HPUXVSUSP] = bsdtios->c_cc[VSUSP];
	tios->c_cc[HPUXVSTART] = bsdtios->c_cc[VSTART];
	tios->c_cc[HPUXVSTOP] = bsdtios->c_cc[VSTOP];
}

void
termiostotermio(tios, tio)
	struct hpux_termios *tios;
	struct hpux_termio *tio;
{
	int i;

	tio->c_iflag = tios->c_iflag;
	tio->c_oflag = tios->c_oflag;
	tio->c_cflag = tios->c_cflag;
	tio->c_lflag = tios->c_lflag;
	tio->c_line = tios->c_reserved;
	for (i = 0; i <= HPUXVSWTCH; i++)
		tio->c_cc[i] = tios->c_cc[i];
	if (tios->c_lflag & TIO_ICANON) {
		tio->c_cc[HPUXVEOF] = tios->c_cc[HPUXVEOF];
		tio->c_cc[HPUXVEOL] = tios->c_cc[HPUXVEOL];
	} else {
		tio->c_cc[HPUXVMIN] = tios->c_cc[HPUXVMINS];
		tio->c_cc[HPUXVTIME] = tios->c_cc[HPUXVTIMES];
	}
}

int
bsdtohpuxbaud(bsdspeed)
	long bsdspeed;
{
	switch (bsdspeed) {
	case B0:     return(TIO_B0);
	case B50:    return(TIO_B50);
	case B75:    return(TIO_B75);
	case B110:   return(TIO_B110);
	case B134:   return(TIO_B134);
	case B150:   return(TIO_B150);
	case B200:   return(TIO_B200);
	case B300:   return(TIO_B300);
	case B600:   return(TIO_B600);
	case B1200:  return(TIO_B1200);
	case B1800:  return(TIO_B1800);
	case B2400:  return(TIO_B2400);
	case B4800:  return(TIO_B4800);
	case B9600:  return(TIO_B9600);
	case B19200: return(TIO_B19200);
	case B38400: return(TIO_B38400);
	default:     return(TIO_B0);
	}
}

int
hpuxtobsdbaud(hpux_speed)
	int hpux_speed;
{
	static int hpuxtobsdbaudtab[32] = {
		B0,	B50,	B75,	B110,	B134,	B150,	B200,	B300,
		B600,	B0,	B1200,	B1800,	B2400,	B0,	B4800,	B0,
		B9600,	B19200,	B38400,	B0,	B0,	B0,	B0,	B0,
		B0,	B0,	B0,	B0,	B0,	B0,	EXTA,	EXTB
	};

	return(hpuxtobsdbaudtab[hpux_speed & TIO_CBAUD]);
}

int
hpux_sys_stty_6x(l, v, retval)
	struct lwp *l;
	void *v;
	register_t *retval;
{
	struct hpux_sys_stty_6x_args /* {
		syscallarg(int) fd;
		syscallarg(caddr_t) arg;
	} */ *uap = v;

	return (getsettty(l, SCARG(uap, fd), HPUXTIOCGETP, SCARG(uap, arg)));
}

int
hpux_sys_gtty_6x(l, v, retval)
	struct lwp *l;
	void *v;
	register_t *retval;
{
	struct hpux_sys_gtty_6x_args /* {
		syscallarg(int) fd;
		syscallarg(caddr_t) arg;
	} */ *uap = v;

	return (getsettty(l, SCARG(uap, fd), HPUXTIOCSETP, SCARG(uap, arg)));
}

/*
 * Simplified version of ioctl() for use by
 * gtty/stty and TIOCGETP/TIOCSETP.
 */
int
getsettty(l, fdes, com, cmarg)
	struct lwp *l;
	int fdes, com;
	caddr_t cmarg;
{
	struct proc *p = l->l_proc;
	struct filedesc *fdp = p->p_fd;
	struct file *fp;
	struct hpux_sgttyb hsb;
	struct sgttyb sb;
	int error;

	if ((fp = fd_getfile(fdp, fdes)) == NULL)
		return (EBADF);

	if ((fp->f_flag & (FREAD|FWRITE)) == 0)
		return (EBADF);

	if (com == HPUXTIOCSETP) {
		if ((error = copyin(cmarg, (caddr_t)&hsb, sizeof hsb)))
			return (error);
		sb.sg_ispeed = hsb.sg_ispeed;
		sb.sg_ospeed = hsb.sg_ospeed;
		sb.sg_erase = hsb.sg_erase;
		sb.sg_kill = hsb.sg_kill;
		sb.sg_flags = hsb.sg_flags & ~(V7_HUPCL|V7_XTABS|V7_NOAL);
		if (hsb.sg_flags & V7_XTABS)
			sb.sg_flags |= XTABS;
		if (hsb.sg_flags & V7_HUPCL)
			(void)(*fp->f_ops->fo_ioctl)
				(fp, TIOCHPCL, (caddr_t)0, p);
		com = TIOCSETP;
	} else {
		memset((caddr_t)&hsb, 0, sizeof hsb);
		com = TIOCGETP;
	}
	error = (*fp->f_ops->fo_ioctl)(fp, com, (caddr_t)&sb, p);
	if (error == 0 && com == TIOCGETP) {
		hsb.sg_ispeed = sb.sg_ispeed;
		hsb.sg_ospeed = sb.sg_ospeed;
		hsb.sg_erase = sb.sg_erase;
		hsb.sg_kill = sb.sg_kill;
		hsb.sg_flags = sb.sg_flags & ~(V7_HUPCL|V7_XTABS|V7_NOAL);
		if (sb.sg_flags & XTABS)
			hsb.sg_flags |= V7_XTABS;
		error = copyout((caddr_t)&hsb, cmarg, sizeof hsb);
	}
	return (error);
}
