/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  __tioctl.c,v 1.1.1.1 1994/04/04 04:30:13 amiga Exp
 *
 *  __tioctl.c,v
 * Revision 1.1.1.1  1994/04/04  04:30:13  amiga
 * Initial CVS check in.
 *
 *  Revision 1.5  1993/11/05  21:51:08  mw
 *  seems I got oldstyle tty handling oposite way..
 *
 *  Revision 1.4  1992/08/09  20:39:01  amiga
 *  add a cast to get rid of a warning
 *
 *  Revision 1.3  1992/07/04  19:07:25  mwild
 *  send DISK_INFO packet with synchronous port, async delivery seems to be
 *  broken with CNC:
 *
 * Revision 1.2  1992/06/08  02:36:00  mwild
 * fix TIOCGWINSZ, row/column was off by one
 *
 * Revision 1.1  1992/05/14  19:55:40  mwild
 * Initial revision
 *
 */

#define KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <sgtty.h>
#define OB0	B0	
#define OB50	B50	
#define OB75	B75	
#define OB110	B110	
#define OB134	B134	
#define OB150	B150	
#define OB200	B200	
#define OB300	B300	
#define OB600	B600	
#define OB1200	B1200	
#define	OB1800	B1800	
#define OB2400	B2400	
#define OB4800	B4800	
#define OB9600	B9600	
#define OEXTA	EXTA	
#define OEXTB	EXTB	
#undef B0	
#undef B50	
#undef B75	
#undef B110	
#undef B134	
#undef B150	
#undef B200	
#undef B300	
#undef B600	
#undef B1200	
#undef B1800	
#undef B2400	
#undef B4800	
#undef B9600	
#undef EXTA	
#undef EXTB	
#include <sys/termios.h>
#include <ctype.h>
#include <devices/conunit.h>

#if __GNUC__ != 2
#define alloca __builtin_alloca
#endif

/* IOCTLs on "interactive" files */

int
__tioctl(struct file *f, unsigned int cmd, unsigned int inout, 
	 unsigned int arglen, unsigned int arg)
{
  int omask, result, err;

  omask = syscall (SYS_sigsetmask, ~0);
  __get_file (f);
  result = -1;

  if (/*!IsInteractive(CTOBPTR(f->f_fh))*/ ! f->f_fh->fh_Port)
    {
      err = ENOTTY;
      goto ret;
    }

  switch (cmd)
    {
    case TIOCGETA:
      {
        struct termios *t = (struct termios *)arg;
        unsigned char *cp;
        t->c_iflag = IGNBRK|IGNPAR|ICRNL|IXON;
        t->c_oflag = (f->f_flags & FTTYRAW) ? 0 : OPOST|ONLCR;
        t->c_cflag = CS8|CLOCAL;
        t->c_ispeed=
        t->c_ospeed= B38400;
	/* Conman does ECHOCTL, Commo doesn't.. I use Conman:-)) */
        t->c_lflag = ECHOCTL|((f->f_flags & FTTYRAW)?0:ICANON|ECHO);
	/* t->c_line  = 0; */
	cp = t->c_cc;
	cp[VSTART] = 'q' & 31;
	cp[VSTOP] = 's' & 31;
	cp[VSUSP] = 0; /* sneef.. would that be nice... */
	cp[VDSUSP] = 0;
	cp[VREPRINT] = 0;
	cp[VDISCARD] = 'x' & 31;
	cp[VWERASE] = 0;
	cp[VLNEXT] = 0;
	cp[VSTATUS] = 0;
	cp[VINTR] = 3;
	cp[VQUIT] = 0;
	cp[VERASE] = 8;
	cp[VKILL] = 'x' & 31;
	cp[VEOF] = '\\' & 31;
	cp[VEOL] = 10;
	cp[VEOL2] = 0;
	result = 0;
	goto ret;
      }

    case TIOCSETA:
    case TIOCSETAW:
    case TIOCSETAF:
      {
        struct termios *t = (struct termios *)arg;
        int makeraw;
        
        makeraw = (t->c_lflag & (ICANON | ECHO)) != (ICANON | ECHO);
	/* the only thing that counts so far.. if ICANON is disabled,        
	 * we disable ECHO too, no matter what the user wanted, and 
	 * send a RAW-packet.. */
	if (!makeraw && (f->f_flags & FTTYRAW))
          {
	    f->f_flags &= ~FTTYRAW;
	    __wait_packet(&f->f_sp);
	    SendPacket1(f,__rwport,ACTION_SCREEN_MODE,0);
	    __wait_packet(&f->f_sp);
	  }
	else if (makeraw && !(f->f_flags & FTTYRAW))
	  {
	    f->f_flags |= FTTYRAW;
	    __wait_packet(&f->f_sp);
	    SendPacket1(f,__rwport,ACTION_SCREEN_MODE,-1);
	    __wait_packet(&f->f_sp);
	  }
	result = 0;
	goto ret;
      }

    case TIOCGETP:
      {
	struct sgttyb *s = (struct sgttyb *)arg;
	s->sg_erase = 8;
	s->sg_kill = 'x' & 31;
	s->sg_flags = ODDP|EVENP|ANYP|
		      ((f->f_flags&FTTYRAW) ? (CBREAK|RAW) : (ECHO|CRMOD));
	s->sg_ispeed =
	s->sg_ospeed = OEXTB;
	result = 0;
	goto ret;
      }

    case TIOCSETN:
    case TIOCSETP:
      {
	struct sgttyb *s = (struct sgttyb *)arg;
	if (!(s->sg_flags & (RAW|CBREAK)) /*&& (f->f_flags & FTTYRAW)*/)
          {
            f->f_flags &= ~FTTYRAW;
	    __wait_packet(&f->f_sp);
	    SendPacket1(f,__rwport,ACTION_SCREEN_MODE,0);
	    __wait_packet(&f->f_sp);
	  }
        else if ((s->sg_flags & (RAW|CBREAK)) /*&& !(f->f_flags & FTTYRAW)*/)
	  {
	    f->f_flags |= FTTYRAW;
	    SendPacket1(f,__rwport,ACTION_SCREEN_MODE,-1);
	    __wait_packet(&f->f_sp);
	  }
	result = 0;
	goto ret;
      }

    case TIOCGWINSZ:
      {
	struct winsize *ws = (struct winsize *) arg;
	struct ConUnit *cu;
	struct IOStdReq *ios;
	struct InfoData *info;
	struct Window *w;

	err = EINVAL;	/* default */
	
	info = alloca (sizeof (struct InfoData) + 2);
	info = LONG_ALIGN (info);
	bzero (info, sizeof (struct InfoData));
	
	/* VERY! strange.. CNC: won't act on the packet if it sent with the
	   async port as reply port! The error is OBJECT_IN_USE (202)... */

	__wait_packet (&f->f_sp);
	LastResult (f) = 0; LastError (f) = 0;
	SendPacket1 (f, u.u_sync_mp, ACTION_DISK_INFO, CTOBPTR (info));
	__wait_sync_packet (&f->f_sp);
	if (LastResult (f) != -1)
	  {
/*	    syscall (SYS_strncmp, "ACTION_DISK_INFO failed!", "", LastError(f));*/
	    goto ret;
	  }

	w = (struct Window *) info->id_VolumeNode;
	if (! w) 
	  {
/*	    syscall (SYS_index, "No window in InfoData structure!", 0);*/
	    goto ret;
	  }
	/* this information is from DevCon notes, not from the Bantam book */
	ios = (struct IOStdReq *) info->id_InUse;
	if (! ios || ((int)ios & 1)) 
	  {
/*	    syscall (SYS_index, "invalid ConUnit pointer in InfoData!", 0);*/
	    goto ret;
	  }
	cu = (struct ConUnit *) ios->io_Unit;
	if (!cu) 
	  {
/*	    syscall (SYS_index, "no ConUnit pointer in InfoData!", 0);*/
	    goto ret;
	  }

	/* paranoid check.. */
	if (cu->cu_Window != w)
	  {
/*	    syscall (SYS_index, "incompatible ConUnit pointer in InfoData!", 0);*/
	    goto ret;
	  }

    	ws->ws_xpixel = w->Width - w->BorderLeft - w->BorderRight;
    	ws->ws_ypixel = w->Height - w->BorderTop - w->BorderBottom;

	ws->ws_col = cu->cu_XMax + 1;	/* Thanks Rob! those values are off */
	ws->ws_row = cu->cu_YMax + 1;	/* by one! */

	result = 0;
	goto ret;
      }

    case TIOCOUTQ:
      {
	int *count = (int *)arg;
	*count = 0;
	result = 0;
	goto ret;
      }

    case TIOCSWINSZ:
      /* should I really try to resize the window ?? */

    default:
      /* this is no error, but nevertheless we don't take any actions.. */      
      result = 0;
      goto ret;
    }

ret:
    LastResult(f) = 0;
    __release_file (f);
    syscall (SYS_sigsetmask, omask);
    errno = err;
    KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
    return result;
}
