/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1995 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Do not include sys/conf.h- it isn't in the compatibility include dirs.
 */
#ifdef	THIS_IS_AVAIL
#include <sys/conf.h>
#endif
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ioccom.h>
#include <sys/stropts.h>
#include <sys/des.h>
#include <sys/fcntl.h>
#include <sys/filio.h>
#include <sys/termios.h>
#include <sys/termio.h>
#include <sys/ttold.h>
#include <sys/ttycom.h>
#include <sys/msio.h>
#include <sys/errno.h>
#include <nettli/tihdr.h>
#include <nettli/timod.h>
#include <nettli/tiuser.h>
#include <sun/dkio.h>
#include <scsi/impl/uscsi.h>
#include "cdioctl.h"
#include "s5dkio.h"
#include "s5fdio.h"

/*
 * parameter for windows ioctls 
 */
struct winclip {
        int     wc_blockbytes;          /* size of wc_block */
        int     wc_clipid;              /* Current clip id of clipping */
        short   wc_screenrect[4];       /* Screen relatived (used when paint) */
        char    *wc_block;              /* Block where RectList is copied. */
};

/*
 * Ioctl control packet
 */
struct s5termios {
	tcflag_t	c_iflag;	/* input modes */
	tcflag_t	c_oflag;	/* output modes */
	tcflag_t	c_cflag;	/* control modes */
	tcflag_t	c_lflag;	/* line discipline modes */
	cc_t		c_cc[19];	/* control chars */
};

#define N_ENOMSG	35	
#define N_I_FIND	('S'<<8)|013
#define N_I_PUSH	('S'<<8)|02
#define WINGETEXPOSEDRL	_IOWR('g',31,struct winclip)
#define WINGETDAMAGEDRL _IOWR('g',32,struct winclip)

struct n_sgttyb {
	char    sg_ispeed;              /* input speed */
	char    sg_ospeed;              /* output speed */
	char    sg_erase;               /* erase character */
	char    sg_kill;                /* kill character */
	int     sg_flags;               /* mode flags */
};

static int handle_dkio_partitions(int, int, int);
static int tcget(int, int, int);
static int tcset(int, int, int);
static int _bc_ioctl(int, int, int);

int
ioctl(int des, int request, int arg)
{
	int ret;

	if ((ret = _bc_ioctl(des, request, arg)) == -1)
		maperror();
	return (ret);
}

int
bc_ioctl(int des, int request, int arg)
{
	int ret;

	if ((ret = _bc_ioctl(des, request, arg)) == -1)
		maperror();
	return (ret);
}

static int
_bc_ioctl(int des, int request, int arg)
{
	int ret;
	int nreq = (request >> 8) & 0xFF;
	struct n_sgttyb nsg;
	struct s5_dk_cinfo newArgs;
	struct dk_info *infoArgs;
	struct dk_conf *confArgs;
	extern int errno;

	/* not all mappings for 'm' have been performed */
	switch (nreq) {
		case ((int) 't'):
			if (_ioctl(des, N_I_FIND, "ttcompat") == 0)
				if (_ioctl(des, N_I_PUSH, "ttcompat") == -1)
					perror("ioctl/I_PUSH");
			switch(request) {
				case TIOCSETD:
					     /* added for sunview */
					     return(0);
				case TIOCREMOTE: request = ('t'<<8)|30;
					     break;
				case TIOCNOTTY:
					     bc_setsid();
					     return(0);
				case TIOCGPGRP: request = ('t'<<8)|20;
					     break;
				case TIOCSPGRP:
				    {
					pid_t pgid;
					sigset_t set, oset;

					request = ('t'<<8)|21;
					ret = _ioctl(des, request, arg);

					/*
					 * SunOS4.x allows this to succeed
					 * even if the process group does
					 * not exist yet.  We emulate the 4.x
					 * bug by creating the process group
					 * and reissuing the ioctl().
					 * See bugid 1175044.
					 */
					if (ret != 0 && errno == EPERM &&
					    (pgid = *((pid_t *)arg)) != 0 &&
					    pgid == getpid() &&
					    setpgid(0, pgid) == 0) {
						sigemptyset(&set);
						sigaddset(&set, SIGTSTP);
						sigaddset(&set, SIGTTIN);
						sigaddset(&set, SIGTTOU);
						sigprocmask(SIG_BLOCK,
							&set, &oset);
						ret = _ioctl(des,
							request, arg);
						sigprocmask(SIG_SETMASK,
							&oset, NULL);
					}
					return(ret);
				    }
				case TIOCSTI: request = ('t'<<8)|23;
					     break;
				case TIOCSIGNAL: request = ('t'<<8)|31;
					     break;
				case TIOCCONS: request = ('t'<<8)|36;
					     break;
				case TIOCSWINSZ: request = ('T'<<8)|103;
					     break;
				case TIOCGWINSZ: request = ('T'<<8)|104;
					     break;
				case TIOCSETP:
				case TIOCSETN:
			  	    {
					struct sgttyb *sg = (struct sgttyb *)arg;
					nsg.sg_ispeed = sg->sg_ispeed;
					nsg.sg_ospeed = sg->sg_ospeed;
					nsg.sg_erase = sg->sg_erase;
					nsg.sg_kill = sg->sg_kill;
					nsg.sg_flags = (int)sg->sg_flags;
					arg = (int)&nsg;
				        request = request & 0x0FFFF;
					break;
				    }
				
				case TIOCGETP:			
				    {
					struct sgttyb *sg = (struct sgttyb *)arg;

					ret = _ioctl(des, request&0xFFFF, &nsg);
					if (ret != -1) {
						sg->sg_ispeed = nsg.sg_ispeed;
						sg->sg_ospeed = nsg.sg_ospeed;
						sg->sg_erase = nsg.sg_erase;
						sg->sg_kill = nsg.sg_kill;
						sg->sg_flags = (short)nsg.sg_flags & 0x0FFFF;
					}
					return(ret);
				    }
				case TIOCPKT:
				case TIOCUCNTL:
				case TIOCTCNTL:
				case TIOCSSOFTCAR:
				case TIOCGSOFTCAR:
				case TIOCISPACE:
				case TIOCISIZE:
				case TIOCSSIZE:
				case TIOCGSIZE:
				    	     break;
				default:     request = request & 0x0FFFF;
				 	     break;
			}
			break;
		case ((int) 'T'):
			switch(request) {
				case TCGETS:
					request = ('T'<<8)|13;
					return(tcget(des, request, arg));
					break;
				case TCSETS:
					request = ('T'<<8)|14;
					return(tcset(des, request, arg));
					break;
				case TCSETSW:
					request = ('T'<<8)|15;
					return(tcset(des, request, arg));
					break;
				case TCSETSF:
					request = ('T'<<8)|16;
					return(tcset(des, request, arg));
					break;
				case TCGETA:
				case TCSETA:
				case TCSETAW:
				case TCSETAF:
				default:
					request = request & 0x0FFFF;
					break;
			}
			break;
		case ((int) 'S'):
			switch (request) {
 				case I_PLINK: request = ('S'<<8)|026;
					      break;
 				case I_PUNLINK: request = ('S'<<8)|027;
					      break;
 				case I_STR: {
					struct strioctl *iarg =
					    (struct strioctl *)arg;
					int cmd = iarg->ic_cmd;

					switch (cmd) {
					case TI_GETINFO: {
						/*
						 * The T_info_ack structure
						 * has one additional word
						 * added to it in 5.x.
						 * To prevent the module from
						 * overwritting user memory we
						 * use an internal buffer for
						 * the transfer and copy out
						 * the results to the caller.
						 */
						struct {
							struct T_info_ack info;
							long		pad[16];
						} args;
						char *dp = iarg->ic_dp;

						memcpy(&args.info, iarg->ic_dp,
						    sizeof(struct T_info_ack));
						iarg->ic_dp =
						    (char *) &args.info;
						iarg->ic_cmd = (TIMOD | 140);
						ret = _ioctl(des,
						    request & 0xffff, arg);
						iarg->ic_cmd = cmd;
						iarg->ic_dp = dp;
						iarg->ic_len =
						    sizeof(struct T_info_ack);
						memcpy(iarg->ic_dp, &args.info,
						    iarg->ic_len);
						return (ret);
						break;
					}
					case TI_OPTMGMT:
						iarg->ic_cmd = (TIMOD | 141);
						break;
					case TI_BIND:
						iarg->ic_cmd = (TIMOD | 142);
						break;
					case TI_UNBIND:
						iarg->ic_cmd = (TIMOD | 143);
						break;
					}
					ret = _ioctl(des,
					    request & 0xffff, arg);
					iarg->ic_cmd = cmd;
					return ret;
				}
				default:      request = request & 0x0FFFF;
				  	      break;
			}
			break;
		case ((int) 'm'):
			switch (request) {
				case MSIOGETPARMS: request = ('m'<<8)|1;
					      break;
				case MSIOSETPARMS: request = ('m'<<8)|2;
					      break;
				default:      request = request & 0x0FFFF;
				  	      break;
			}	
			break;
		case ((int) 'd'):
			switch (request) {
				case DKIOCGGEOM:
					request = S5DKIOCGGEOM;
					break;
				case DKIOCSGEOM:
					request = S5DKIOCSGEOM;
					break;
				case DKIOCSAPART:
					request = S5DKIOCSAPART;
					break;
				case DKIOCGAPART:
					request = S5DKIOCGAPART;
					break;
				case DKIOCSTYPE:
					request = S5HDKIOCSTYPE;
					break;
				case DKIOCGTYPE:
					request = S5HDKIOCGTYPE;
					break;
				case DKIOCSBAD:
					request = S5HDKIOCSBAD;
					break;
				case DKIOCGBAD:
					request = S5HDKIOCGBAD;
					break;
				case DKIOCSCMD:
					request = S5HDKIOCSCMD;
					break;
				case DKIOCGDIAG:
					request = S5HDKIOCGDIAG;
					break;
				case FDKIOGCHAR:
					request = S5FDIOGCHAR;
					break;
				case FDKIOSCHAR:
					request = S5FDIOSCHAR;
					break;
				case FDKEJECT:
					request = S5FDEJECT;
					break;
				case FDKGETCHANGE:
					request = S5FDGETCHANGE;
					break;
				case FDKGETDRIVECHAR:
					request = S5FDGETDRIVECHAR;
					break;
				case FDKSETDRIVECHAR:
					request = S5FDSETDRIVECHAR;
					break;
				case FDKGETSEARCH:
					request = S5FDGETSEARCH;
					break;
				case FDKSETSEARCH:
					request = S5FDSETSEARCH;
					break;
				case FDKIOCSCMD:
					request = S5FDIOCMD;
					break;
				case F_RAW:
					request = S5FDRAW;
					break;
				case DKIOCINFO:
					ret = _ioctl(des, S5DKIOCINFO, &newArgs);
					if (ret != -1) {
						infoArgs = (struct dk_info *)arg;
						infoArgs->dki_ctlr =
							newArgs.dki_addr;
						infoArgs->dki_unit =
							newArgs.dki_unit;
						infoArgs->dki_ctype =
							newArgs.dki_ctype;
						infoArgs->dki_flags =
							newArgs.dki_flags;
					}
					return ret;
					break;
				case DKIOCGCONF:
					ret = _ioctl(des, S5DKIOCINFO, &newArgs);
					if (ret != -1) {
						confArgs = (struct dk_conf *)arg;
						strncpy(confArgs->dkc_cname,
							newArgs.dki_cname,
							DK_DEVLEN);
						strncpy(confArgs->dkc_dname,
							newArgs.dki_dname,
							DK_DEVLEN);
						confArgs->dkc_ctype =
							(u_short)newArgs.dki_ctype;
						confArgs->dkc_flags =
							(u_short)newArgs.dki_flags;
						confArgs->dkc_cnum =
							newArgs.dki_cnum;
						confArgs->dkc_addr =
							newArgs.dki_addr;
						confArgs->dkc_space =
							(u_int)newArgs.dki_space;
						confArgs->dkc_prio =
							newArgs.dki_prio;
						confArgs->dkc_vec =
							newArgs.dki_vec;
						confArgs->dkc_unit =
							newArgs.dki_unit;
						confArgs->dkc_slave =
							newArgs.dki_slave;
					}
					return ret;
					break;
				case DKIOCWCHK:
					/*
					 * This is unsupported in SVR4. It
					 * turns on verify-after-write for
					 * the floppy. I don't think the
					 * system call should fail, however.
					 */
					return 0;
					break;
				case DKIOCGPART:
				case DKIOCSPART:
					return (handle_dkio_partitions(des,
					       request, arg));
				case DKIOCGLOG:
					/* unsupported */
					errno = EINVAL;
					return -1;
					break;
				case DESIOCBLOCK:
				case DESIOCQUICK:
					break; /* no change for these two */
				default:
					request = request & 0x0FFFF; /* try */
					break;
			}
			break;
		case ((int) 'c'):
			switch (request) {
				case CDROMPAUSE:
					request = S5CDROMPAUSE;
					break;
				case CDROMRESUME:
					request = S5CDROMRESUME;
					break;
				case CDROMPLAYMSF:
					request = S5CDROMPLAYMSF;
					break;
				case CDROMPLAYTRKIND:
					request = S5CDROMPLAYTRKIND;
					break;
				case CDROMREADTOCHDR:
					request = S5CDROMREADTOCHDR;
					break;
				case CDROMREADTOCENTRY:
					request = S5CDROMREADTOCENTRY;
					break;
				case CDROMSTOP:
					request = S5CDROMSTOP;
					break;
				case CDROMSTART:
					request = S5CDROMSTART;
					break;
				case CDROMEJECT:
					request = S5CDROMEJECT;
					break;
				case CDROMVOLCTRL:
					request = S5CDROMVOLCTRL;
					break;
				case CDROMSUBCHNL:
					request = S5CDROMSUBCHNL;
					break;
				case CDROMREADMODE1:
					request = S5CDROMREADMODE1;
					break;
				case CDROMREADMODE2:
					request = S5CDROMREADMODE2;
					break;
			}
			break;
		case ((int) 'u'):
			switch (request) {
				case USCSICMD:
				    {
					struct s5_uscsi_cmd s5_cmd;
					struct uscsi_cmd *cmd =
						(struct uscsi_cmd *) arg;
					request = S5USCSICMD;
					s5_cmd.uscsi_cdb = cmd->uscsi_cdb;
					s5_cmd.uscsi_cdblen =
						cmd->uscsi_cdblen;
					s5_cmd.uscsi_bufaddr =
						cmd->uscsi_bufaddr;
					s5_cmd.uscsi_buflen = 
						cmd->uscsi_buflen;
					s5_cmd.uscsi_flags =
						cmd->uscsi_flags;
					ret = _ioctl(des, request, &s5_cmd);
					cmd->uscsi_status = s5_cmd.uscsi_status;
					return(ret);
				    }
			}
			break;
		case ((int) 'k'):
		case ((int) 'v'):
		case ((int) 'F'):
		case ((int) 'G'):
		case ((int) 'X'):
		case ((int) 'L'):
			request = request & 0x0FFFF;
			break;
		case ((int) 'f'):
			if ((request == FIOCLEX) || (request == FIONCLEX))
				return(fcntl(des, F_SETFD,
				    ((request == FIOCLEX) ? 1 : 0)));
			break;
		case ((int) 'g'):
			/* Treat the following 2 ioctls specially for
			 * sunview. */
			if (request == WINGETEXPOSEDRL || 
				request == WINGETDAMAGEDRL) {
				ret = _ioctl(des, request, arg);
				if (errno == N_ENOMSG)
					errno = EFBIG;
				return(ret);	
			}
			break;
	}
	return (_ioctl(des, request, arg));
}


static int
handle_dkio_partitions(int des, int request, int arg)
{
	struct s5_dk_cinfo	cinfo;
	struct dk_allmap	map;
	struct dk_map		*part;
	int			ret;
	extern int		errno;

	part = (struct dk_map *) arg;
	
	ret = _ioctl(des, S5DKIOCINFO, &cinfo);

	if ((cinfo.dki_partition < 0) || (cinfo.dki_partition >= NDKMAP)) {
		errno = EINVAL;
		return (-1);
	}
	
	if (ret != -1) {
		ret = _ioctl(des, S5DKIOCGAPART, &map);
		if (ret != -1) {
			if (request == DKIOCGPART) {
				part->dkl_cylno =
				    map.dka_map[cinfo.dki_partition].dkl_cylno;
				part->dkl_nblk = 
				    map.dka_map[cinfo.dki_partition].dkl_nblk;
			} else {
				map.dka_map[cinfo.dki_partition].dkl_cylno = 
					part->dkl_cylno;
				map.dka_map[cinfo.dki_partition].dkl_nblk =
					part->dkl_nblk;
				ret = _ioctl(des, S5DKIOCSAPART, &map);
			}
		}
	}
	return (ret);
}

static int
tcset(des, request, arg)
	register int	des;
	register int	request;
	int		arg;
{
	struct s5termios	s5termios;
	struct termios		*termios;

	termios = (struct termios *)arg;

	if (termios != NULL) {
		s5termios.c_iflag = termios->c_iflag;
		s5termios.c_oflag = termios->c_oflag;
		s5termios.c_cflag = termios->c_cflag;
		s5termios.c_lflag = termios->c_lflag;
		memcpy(s5termios.c_cc, termios->c_cc, NCCS);
		return (_ioctl(des, request, &s5termios));
	} else
		return (_ioctl(des, request, NULL));

}

static int
tcget(des, request, arg)
	register int	des;
	register int	request;
	int		arg;
{
	struct s5termios	s5termios;
	struct termios		*termios;
	int			ret;

	termios = (struct termios *)arg;

	ret = _ioctl(des, request, &s5termios);

	if (termios != NULL) {
		termios->c_iflag = s5termios.c_iflag;
		termios->c_oflag = s5termios.c_oflag;
		termios->c_cflag = s5termios.c_cflag;
		termios->c_lflag = s5termios.c_lflag;
		memcpy(termios->c_cc, s5termios.c_cc, NCCS);
	}

	return (ret);
}