/* 
   Copyright (C) 1994 Free Software Foundation

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   This program 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
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#include "term.h"
#include <stdio.h>
#include <fcntl.h>
#include <hurd/trivfs.h>
#include <strings.h>
#include <hurd/fsys.h>

#include "term_S.h"

static char *nodename;
static char *machdev;

int
main (int argc, char **argv)
{
  mach_port_t host_priv, device_master;
  file_t file;
  mach_port_t ctlport;
  mach_port_t bootstrap;

  _libports_initialize ();	/* XXX */

  task_get_bootstrap_port (mach_task_self (), &bootstrap);
  if (bootstrap == MACH_PORT_NULL)
    {
      /* For now, only support devio; require the Mach name as
	 argv[1]. */
      if (argc != 3)
	{
	  fprintf (stderr, "Usage: term mach-dev-name ttyname\n");
	  exit (1);
	}
      machdev = argv[1];
      nodename = argv[2];

      /* Install control port in filesystem */
      file = file_name_lookup (nodename, O_CREAT|O_NOTRANS, 0666);
      if (file == MACH_PORT_NULL)
	{
	  perror (nodename);
	  exit (1);
	}
  
      ctlport = trivfs_handle_port (file, PT_TTYCNTL, PT_TTY),
      termctl = ports_check_port_type (ctlport, PT_TTYCNTL);
      assert (termctl);

      errno = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET,
				   0, 0, 0, ctlport, MACH_MSG_TYPE_MAKE_SEND);
      if (errno)
	{
	  perror ("setting translator");
	  exit (1);
	}
    }
  else
    {
      if (argc != 2)
	{
	  fprintf (stderr, "Usage (as translator): term mach-dev-name\n");
	  exit (1);
	}
      machdev = argv[1];
      nodename = "";

      ctlport = trivfs_handle_port (MACH_PORT_NULL, PT_TTYCNTL, PT_TTY);
      errno = fsys_startup (bootstrap, ctlport, MACH_MSG_TYPE_MAKE_SEND,
			    &file);
      if (errno)
	{
	  perror ("Starting translator");
	  exit (1);
	}
      termctl = ports_get_port (ctlport);
      assert (termctl);
      termctl->underlying = file;
    }      

  errno = get_privileged_ports (&host_priv, &device_master);
  if (errno)
    {
      perror ("Getting priviliged ports");
      exit (1);
    }
  mach_port_deallocate (mach_task_self (), host_priv);
  

  bzero (&termstate, sizeof (termstate));

  /* Open Mach device */
  errno = devio_open (machdev, device_master);
  if (errno)
    {
      perror ("Opening device");
      exit (1);
    }
  
  /* Initialize user state */
  inputq = create_queue ();
  initialize_input ();
  init_user_state ();

  /* Launch */
  ports_manage_port_operations_onethread ();
  return 0;
}  

int
ports_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
{
  extern int term_server (mach_msg_header_t *, mach_msg_header_t *);
  extern int device_reply_server (mach_msg_header_t *, mach_msg_header_t *);
  extern int tioctl_server (mach_msg_header_t *, mach_msg_header_t *);
  extern int notify_server (mach_msg_header_t *, mach_msg_header_t *);
  struct port_info *pi;
  int ret;
  
  pi = ports_get_port (inp->msgh_local_port);
  
  switch (pi->type)
    {
    case PT_CTTYID:
      ret = term_server (inp, outp);
      break;

    case PT_TTYCNTL:
      ret = trivfs_demuxer (inp, outp);
      break;
      
    case PT_TTY:
      ret = (trivfs_demuxer (inp, outp) 
	     || term_server (inp, outp)
	     || tioctl_server (inp, outp));
      break;

    case PT_DEVRD:
    case PT_DEVWR:
    case PT_DEVOP:
      ret = (device_reply_server (inp, outp)
	     || notify_server (inp, outp));
      break;
      
    default:
      assert (0);
    }
  
  ports_done_with_port (pi);
  return ret;
}

error_t
trivfs_goaway (int flags,
	       mach_port_t underlying,
	       int ctltype,
	       int protidtypes)
{
  return EBUSY;
}

static void
null_clean (void *arg)
{
}
   
void (*ports_cleanroutines[])(void *) =
{
  [PT_TTYCNTL] = trivfs_clean_cntl,
  [PT_TTY] = trivfs_clean_protid,
  [PT_DEVRD] = null_clean,
  [PT_DEVWR] = null_clean,
  [PT_DEVOP] = null_clean,
  [PT_CTTYID] = null_clean,
};

kern_return_t
S_term_get_nodename (io_t arg,
		     char *name)
{
  struct trivfs_protid *cred = ports_check_port_type (arg, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;
  
  strcpy (name, nodename);
  ports_done_with_port (cred);
  return 0;
}

kern_return_t
S_term_set_nodename (io_t arg,
		     char *name)
{
  struct trivfs_protid *cred = ports_check_port_type (arg, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;
  ports_done_with_port (cred);
  
  if (strcmp (name, nodename))
    return EINVAL;
  
  return 0;
}

kern_return_t
S_term_set_filenode (io_t arg,
		     file_t filenode)
{
  struct trivfs_protid *cred = ports_check_port_type (arg, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;
  ports_done_with_port (cred);

  return EINVAL;
}

kern_return_t
S_term_get_bottom_type (io_t arg,
			int *ttype)
{
  struct trivfs_protid *cred = ports_check_port_type (arg, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;

  ports_done_with_port (cred);
  *ttype = TERM_ON_MACHDEV;
  return 0;
}

kern_return_t
S_term_on_machdev (io_t arg,
		   device_t machdev)
{
  struct trivfs_protid *cred = ports_check_port_type (arg, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;
  ports_done_with_port (cred);
  return EINVAL;
}

kern_return_t
S_term_on_hurddev (io_t arg,
		   io_t hurddev)
{
  return EOPNOTSUPP;
}

kern_return_t
S_term_on_pty (io_t arg,
	       mach_port_t *master)
{
  return EOPNOTSUPP;
}

/* Unused callback from the ports library. */
void
ports_no_hard_ports ()
{
}

/* Unused callback from the ports library. */
void
ports_no_live_ports ()
{
}

/* Unused callback from the ports library. */
void
ports_notice_idle (int hard, int soft)
{
}
