/* Global variables and such for terminal driver
   Copyright (C) 1993, 1994 Free Software Foundation

This file is part of the GNU Hurd.

The GNU Hurd 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.

The GNU Hurd 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 the GNU Hurd; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Written by Michael I. Bushnell.  */

#include <mach.h>
#include <hurd.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <unistd.h>
#include <hurd/ports.h>

#undef MDMBUF
#undef ECHO
#undef TOSTOP
#undef FLUSHO
#undef PENDIN
#undef NOFLSH
#include <termios.h>

/* Interesting characters */
#define CHAR_CTRL_A '\001'	/* C-a */
#define CHAR_EOT '\004'		/* C-d */
#define CHAR_RPT '\022'		/* C-r */
#define CHAR_USER_QUOTE '\377'	/* used for break quoting, etc. */

/* XXX These belong in <termios.h> */
#define ILCASE (1 << 14)
#define OLCASE (1 << 9)
#define OTILDE (1 << 10)
   
/* State of terminal as set by TIOCSETA et. al. */
struct termios termstate;


/*
   The terminal driver is divided into three "halves". 

   The top half is responsible for implementing the Hurd fs, term, and
   io interfaces for terminal ports.  It is implemented in the file
   users.c.  

   The middle half is responsible for frobbing characters on input
   and output, implementing the various bits and frobs in the termios
   struct.  It is implemented in the files input.c and output.c.

   The bottom half is responsible for interacting with the "physical"
   device.  There are three bottom halves supported, found in
   hurdio.c, devio.c, and ptyio.c  */


/* Interaction between the top half and the middle half: */

/* The middle half puts characters on this queue when they are ready to 
   be read.  The top half takes them off and hands them directly to
   users to satisfy read requests.  The top half is reponsible for
   special EOF and break character processing, as well as VDSUSP. */
struct queue *inputq;

void initialize_input ();

/* When new data is put on INPUTQ, the middle half calls this 
   upper half routine which tries to satisfy outstanding read requests. */
void wakeup_readers ();
   
/* When the middle half wants output to stop (because the user type XOFF 
   or some such) then call user_suspend_writing; when output should resum
   again, call user_resume writing.  The top half will do whatever
   further queue flushing is necessary. */
void user_suspend_writing ();
void user_resume_writing ();
   
/* The middle half calls this routine to cause the top half to send
   signal SIGNO to the forground process group of the terminal. */
void send_signal (int signo); 

/* The top half calls this middle half routine to output single characters
   that the user has written. */
void output_character (int c);

/* After doing any output, the top half should call this middle-half
   routine.  */
void nonecho_output ();

/* Cause all pending operations on ctty ports whose pid is PID or
   whose pgrp is -PID to return EINTR. */
void interrupt_pid (pid_t pid);

/* The middle half calls this routine to notify the top half that
   all writes should be discarded (but the RPC's should return
   successfully). */
void discard_output ();

/* The middle half calls this routine to cancel the effect of
   discard_output.  */
void stop_discarding_output ();

/* Copy the characters in the raw queue to the end of INPUTQ and clear 
   the raw queue. */
void copy_rawq ();

/* Remove everything in INPUTQ; then process each character as if 
   it had just been typed. */
void rescan_inputq ();


/* Interaction between the bottom half and the top and middle halves: */

/* The bottom half should call this top half routine whenever the bottom
   half becomes connected.  */
void report_connect ();

/* The bottom half should call this top half routine whenever the bottom
   half loses its connection. */
void report_hangup ();

/* The bottom half should call this top half routine whenever dtron
   encounters an error after it has returned successfully. */
void report_open_error (error_t);
   
/* The bottom half should call stop_writing whenever there is
   too much pending output and the top half should stop writing. 
   Then call start_writing when room becomes free.  */
void stop_writing ();
void start_writing ();

/* The bottom half should call this for each character received.
   The bottom half should not check parity.  If this function
   returns non-zero, then all remaining pending input characters
   should be dropped. */
int input_character (int c);

/* The bottom half should call this whenever a break is received */
void input_break ();

/* The bottom half should call this whenever a character is
   received with a framing error. */
void input_framing_error (int c);

/* The bottom half should call this in response to a drain request when
   the drain has completed. */
void drain_finished (int code);

/* Function table for bottom halves */
struct lowerhalf
{
  /* Send character C out. */
  void (*transmit)(int c);

  /* Set a break condition */
  void (*set_break) ();
  
  /* Clear a break condition */
  void (*clear_break) ();

  /* Call drain_finished with CODE when all currently pending output
     is finished */
  void (*drain_output)(int code);
  
  /* Drop all currently pending output. */
  void (*drop_output)();

  /* Return output queue size */
  int (*outsize)();

  /* Stop sending output. */
  void (*stop_sending)();

  /* Start sending output. */
  void (*start_sending)();

  /* Stop reading input. */
  void (*stop_reading)();
 
  /* Start reading input. */
  void (*start_reading)();

  /* Batch up output characters until stop_batch is called;
     it's not important to transmit characters until then,
     but don't delay sending if the queue is getting full. */
  void (*start_batch)();
  void (*stop_batch)();

  /* Set number of transmit bits, parity state, and speed appropriately
     for termstate.  (This should enable parity *detection* if appropriate,
     but not parity *checking*.  Received bad-parity characters should be
     sent up to input_character normally.) */
  void (*set_bits)();

  /* Drop association with lowerhalf.  A drain request has just finished;
     and stop_reading has been called. */
  void (*shutdown)();

  /* Change modem state. Values for BITS are TIOCM_* in <ioctls.h> */
  void (*mdmctl)(int how, int bits);
  /* Values for HOW */
#define MDMCTL_BIS 0
#define MDMCTL_BIC 1
#define MDMCTL_SET 2

  /* Fetch modem state. */
  int (*mdmstate)();

  /* Cause a DTR flicker to signal modems to hangup. */
  void (*dtrflicker)();

  /* Assert DTR so that modem will answer phone. */
  error_t (*dtron)();
};
struct lowerhalf *lowerhalf;

/* These functions are called to set up a particular lower half.
   The previous lower half should have already been shutdown.  These
   functions may set bits in termstate, but should not othewise
   mung it. */
error_t devio_open (char *devname, device_t master_device);
   

/* Communication between input.c and output.c. */
/* Physical position of cursor on output line */
int output_psize;
   
/* Write character without any frobbing */
void poutput (int c);

/* Report the width of character C as printed by output_character. */
int output_width (int c);


/* Communication between main.c and users.c. */

/*  Initializes termstate. */
void init_user_state ();



/* Character queues */
struct quote
{
  int charno;
  struct quote *next;
};

struct queue 
{
  char *qs;			/* beginning of useful data */
  char *qp;			/* end of useful data */
  int chars_alloced;		/* size of chars array */
  struct quote *quoted;	/* list of quoted chars, offset from chars */
  char chars[0];
};

#define qsize(q) ((q)->qp - (q)->qs)

/* Make Q larger; it may be freed; use the return value 
   in place of Q in the future.  */
struct queue *reallocate (struct queue *q);
   
/* Remove all the characters in Q. */
void clear_queue (struct queue *q);

/* Create and return a new queue */
struct queue *create_queue ();
   
/* Add character C to the end of *Q. */
static inline void
enqueue_char (char c, struct queue **q)
{
  assert ((*q)->qp <= (*q)->chars + (*q)->chars_alloced);
  
  if ((*q)->qp == (*q)->chars + (*q)->chars_alloced)
    *q = reallocate (*q);
  *(*q)->qp++ = c;
}

/* Add character C to the end of *Q; mark it quoted. */
static inline void
quoted_enqueue_char (char c, struct queue **q)
{
  struct quote *qt = malloc (sizeof (struct quote));

  enqueue_char (c, q);
  qt->charno = (*q)->qp - (*q)->chars;
  qt->next = (*q)->quoted;
}

/* Pull a character out of the queue; tell if it was quoted or not as well. */
static inline int
remove_char (struct queue *q)
{
  int offset = q->qp - q->chars;
  struct quote *qt, **lastp;
  
  --q->qp;

  for (qt = q->quoted, lastp = &q->quoted; 
       qt; 
       qt = qt->next, lastp = &qt->next)
    if (qt->charno == offset)
      {
	*lastp = qt->next;
	free (qt);
	return 1;
      }
  return 0;
}

struct trivfs_control *termctl;

/* A reasonable amount to keep in a queue without blocking people. */
#define HIGH_WATER_MARK 1024

/* ports library port types */
enum port_type
{
  PT_TTYCNTL,		/* control port for tty node [users.c] */
  PT_TTY,  		/* trivfs port for tty port [users.c] */
  PT_PTYCNTL, 		/* control port for pty node [ptyio.c] */
  PT_PTY,     		/* trivfs port for pty node [ptyio.c] */
  PT_DEVRD,	   	/* replies to device_read [devio.c] */
  PT_DEVWR,		/* replies to device_write [devio.c] */
  PT_DEVOP,		/* replies to dovice_open [devio.c] */
  PT_IOREPLY,		/* replies to io_* [hurdio.c] */
  PT_CTTYID,		/* controlling terminal id [users.c] */
};

  
