BBN-V6/newtty/pty.c

Compare this file to the similar file:
Show the results in this format:

#
/*
 * Pseudo-teletype Driver
 * (Actually two drivers, requiring two entries in 'cdevsw')
 */

#include "../h/param.h"
#include "../h/conf.h"
#include "../h/inode.h"
#include "../h/user.h"
#include "../h/tty.h"

#define NPTY 16      /* Number of pseudo-teletypes */

/*
 * A pseudo-teletype is a special device which is not unlike a pipe.
 * It is used to communicate between two processes.  However, it allows
 * one to simulate a teletype, including mode setting, interrupt, and
 * multiple end of files (all not possible on a pipe).  There are two
 * really two drivers here.  One is the device which looks like a TTY
 * and can be thought of as the slave device, and hence its routines are
 * are prefixed with 'pts' (PTY slave).  The other driver can be
 * thought of as the controlling device, and its routines are prefixed
 * by 'ptc' (PTY controller).  To type on the simulated keyboard of the
 * PTY, one does a 'write' to the controlling device.  To get the
 * simulated printout from the PTY, one does a 'read' on the controlling
 * device.  Normally, the controlling device is called 'ptyx' and the
 * slave device is called 'ttyx' (to make programs like 'who' happy).
 *
 * Modified by Dan Franklin:
 *    Removed commented-out ptcwrite code; it will block RUBOUT
 *    after TTHIWAT chars typed, which is undesirable.
 * 9/11/78 made ptcopen turn on virtual carrier. Otherwise the sequence
 *    ptsopen - ptsopen - ptcclose - ptcopen leaves pty open with carrier off,
 *    which causes slave reads to return EOF, which causes getty to go away
 *    as soon as init forks it -- repeatedly!
 *    Also removed wakeup on rawq.c_cf, since ptcwrite no longer sleeps on it
 *    Added await and capacity stuff.
 * 9/27/78 ptsopen now sleeps on virtual carrier.  ptsclose, ptsread, ptswrite,
 *    ptcopen simplified accordingly. CHANGE: ptswrite now returns 0 on attempt
 *    to write when controller side closed. This is consistent with handling
 *    of "carrier" in other tty drivers, and means that random garbage from one
 *    (server telnet) session will not get received by the next. Changed await
 *    stuff to give controller side its own slot.
 * 1/4/78 BBN(dan) to fix stty bug: an stty on slave after
 *    controller closed called ttstart when t_addr == 0. Changed to not
 *    clear t_addr in ptcclose. Also changed ptsstart to check if controller
 *    closed and flushtty if so -- this assures that after controller closed,
 *    sleeping ttwrites will still complete.
 * 1/16/78 BBN(dan) to fix stty bug: stty on controller could lead to deadlock
 *    waiting for output to drain if the process doing the stty is the one
 *    that would drain the output. Fixed by imitating stty code in ttystty
 *    without wflushtty call. Also changed ptccap to return half of what it
 *    what it was before -- this allows for every char input being followed
 *    by \377 in ttyinput.
 * 3/22/79 BBN(dan) feature: ctrller can now tell whether slave open
 *    by examining speeds. Nonzero speed means open, zero speed means closed.
 * 4/9/79 BBN(dan): made use new await interface.
 *    Not very reliable, but sufficient for some applications..
 * 3/26/79 BBN(dan): Made empty() work on pty ctrller (ptcsgtty).
 * 4/25/79 BBN(dan): Deleted empty() line from ptcsgtty.
 * 5/4/79  BBN(dan): Made ptcopen set TS_SSTART bit (it already set t_addr).
 *    Otherwise stty on ctrller with slave closed can crash system.
 *    Also made ptsopen set t_addr (it already set TS_SSTART).
 * 5/13/79 converted to new tty driver. This included modifying ptcsgtty
 *    to just turn off M_OWAIT to duplicate the effect formerly achieved by
 *    by having a separate copy of the wflushtty code.
 * 6/22/79 Implemented Page-length processing.
 */

struct
   {
   struct tty _XXXX;
   int t_itpc;          /* For await on controller side */
   } pty[NPTY];         /* TTY headers for PTYs */

/* ----------------------------- P T S O P E N ---------------------- */

ptsopen(dev,flag)
   {
   register struct tty *tp;
   extern int pgrpct;
   extern ptsstart();

   if (dev.d_minor >= NPTY)      /* Verify minor device number */
      {
      u.u_error = ENXIO;         /* No such device */
      return;
      }
   tp = &pty[dev.d_minor];       /* Setup pointer to PTY header */
   tp->t_state =| TS_SSTART;     /* Special start routine */
   tp->t_addr = &ptsstart;
   if ((tp->t_state & TS_ISOPEN) == 0)  /* Not already open? */
      {
/* Mark as open */
      tp->t_state =| TS_ISOPEN;
# ifdef NEWTTY
      ttyinit(tp);
# endif
# ifndef NEWTTY
      tp->t_flags = 0;
      tp->t_erase = CERASE;
      tp->t_kill = CKILL;
# endif
      }
   ttyopen(dev, tp);

   while((tp->t_state & TS_CARR_ON) == 0)
      sleep(&tp->t_addr, TTIPRI);
# ifdef NEWTTY
   tp->t_modes.t_ispeed = tp->t_modes.t_ospeed = B300;
# endif
# ifndef NEWTTY
   tp->t_speeds.lobyte = tp->t_speeds.hibyte = B300;
# endif
   }

/* ----------------------------- P T S C L O S E -------------------- */

ptsclose(dev)
   {
   register struct tty *tp;

   tp = &pty[dev.d_minor];       /* Setup pointer to PTY header */
   if (tp->t_state & TS_CARR_ON)    /* Other side open? */
      wflushtty(tp);		    /* Yes, wait for it */
   else
      flushtty(tp);		    /* No, blast it */
   tp->t_state =& ~TS_ISOPEN;	    /* Mark as closed */
# ifdef NEWTTY
   tp->t_modes.t_ispeed = BHANGUP;	       /* So ctrller can find out */
   tp->t_modes.t_ospeed = BHANGUP;
# endif
# ifndef NEWTTY
   tp->t_speeds.lobyte = tp->t_speeds.hibyte = BHANGUP;
# endif
   }

/* ----------------------------- P T S R E A D ---------------------- */

ptsread(dev) /* Read from PTY, i.e. from data written by controlling device */
   {
   register struct tty *tp;

   tp = &pty[dev.d_minor];     /* Setup pointer to PTY header */
   ttread(tp);
   if (tp-> t_itpc) awake (tp-> t_itpc, -1); /* kahn - wake controller */
   }

/* ----------------------------- P T S W R I T E -------------------- */
/*
 * Write on PTY, chars will be read from controlling device
 */
ptswrite(dev)
   {
   register struct tty *tp;

   tp = &pty[dev.d_minor];      /* Setup pointer to PTY header */
   ttwrite(tp);
   }

/* -------------------------- P T C O P E N -------------------- */

ptcopen(dev,flag) /* Open for PTY Controller */
   {
   register struct tty *tp;
   extern ptsstart();             /* Routine to start write from slave */

   if (dev.d_minor >= NPTY)    /* Verify minor device number */
      {
      u.u_error = ENXIO;       /* No such device */
      return;
      }
   tp = &pty[dev.d_minor];     /* Setup pointer to PTY header */
   if (tp->t_state & TS_CARR_ON)  /* Already open? */
      {
      u.u_error = EIO;         /* I/O error (Is there something better?) */
      return;
      }
   tp->t_state =| TS_SSTART;
   tp->t_addr = &ptsstart;     /* Set address of start routine */
   tp->t_state =| TS_CARR_ON;     /* Turn on virtual carrier */
   tp->t_itpc = 0;             /* No controller side awaiters yet */
   if (tp->t_itp)
      awake(tp->t_itp, -1);    /* Wake up slave's awaiters */
   wakeup(&tp->t_addr);        /* Wake up slave's openers */
   }

/* -------------------------- P T C C L O S E ------------------- */

ptcclose(dev) /* Close controlling part of PTY */
   {
   register struct tty *tp;

   tp = &pty[dev.d_minor];       /* Setup pointer to PTY header */
   if (tp->t_state & TS_ISOPEN)     /* Is it slave open? */
      signal(tp->t_pgrp,SIGHUP); /* Yes, send a HANGUP */
   tp->t_state =& ~TS_CARR_ON;      /* Virtual carrier is gone */
   flushtty(tp);                 /* Clean things up */
   }

/* -------------------------- P T C R E A D ---------------------- */

ptcread(dev) /* Read from PTY's output buffer */
   {
   register struct tty *tp;
   register int c;   /* Unsigned char */
   register int literal;

   tp = &pty[dev.d_minor];
# ifdef NEWTTY
   literal = tp->t_modes.t_oflags & TO_LITERAL;
# endif

/* Wait for something to arrive, or for XOFFHUNG condition to leave */

   while (tp->t_outq.c_cc == 0 || tp->t_state & (TSO_XOFFHNG|TS_ENDPAGE))
      sleep(&tp->t_outq.c_cf,TTIPRI);	/* (Woken by ptsstart) */

/* Copy in what's there, or all that will fit */

   while ((c = getc(&tp->t_outq)) != -1)
      {
# ifdef NEWTTY
      if (literal == 0)
	 if (c == CPAUSE)  /* Page-len char? */
	    {
	    if (tp->t_state & TS_ZOOM) /* In zoom mode, ignore */
	       continue;
            tp->t_state =| TS_ENDPAGE;  /* Otherwise, set TS_ENDPAGE */
	    break;		       /* And be done for now */
	    }
	 else
# endif
	    if (c >= 0200)     /* If delay char, ignore. */
		continue;

      if (passc(c) < 0) /* OK, pass it. If buffer fills, stop */
	 break;
      }

/* Now, decide whether to wake up process waiting on input queue */

   if (tp->t_outq.c_cc <= TTLOWAT)
      {
      if (tp->t_itp)
	 awake(tp->t_itp, -1);		/* Wake up slave side */
      if (tp->t_state & TS_ASLEEP)
	 {
	 tp->t_state =& ~ TS_ASLEEP;
	 wakeup(&tp->t_outq);
	 }
      }
   }

/* -------------------------- P T S S T A R T ------------------- */

ptsstart(tp) /* Called by 'ttstart' to output a character. */
/* Merely wakes up controlling half, which does actual work */
   {
   if ((tp->t_state & TS_CARR_ON) == 0)   /* Nobody there! */
      flushtty(tp);                 /* To get rid of chars */
   if (tp->t_itpc)
      awake(tp->t_itpc, -1);        /* Wake up controller awaiters */
   wakeup(&tp->t_outq.c_cf);
   }

/* -------------------------- P T C W R I T E ------------------- */

ptcwrite(dev) /* Stuff characters into PTY's input buffer */
   {
   register struct tty *tp;
   register int c;

   tp = &pty[dev.d_minor];     /* Setup pointer to PTY header */
   /* For each character... */
   while ((c = cpass()) >= 0)
      ttyinput(c,tp);
   }

/* -------------------------- P T S S G T T Y ------------------- */
/*
 * Slave sgtty routine. Just call ttystty.
 */
ptssgtty(dev, v1, flag, v2)
   int dev;
   char *v1;
   int flag;
   char *v2;
   {
   register struct tty *tp;

   tp = &pty[dev.d_minor];
   ttystty(tp, v1, flag, v2);
   }

/* -------------------------- P T C S G T T Y ------------------- */
/*
 * Controller sgtty. Differs from slave only in that it does not
 * wait for pending output (it could wait a long time!).
 */
ptcsgtty(dev, v1, flag, v2)
   char *v1;
   int flag;
   char *v2;
   {
   register struct tty *tp;
   register char *v;

   tp = &pty[dev.d_minor];     /* Setup pointer to PTY header */
# ifdef NEWTTY
   ttystty(tp, v1, flag & ~ M_OWAIT, v2);
# endif
# ifndef NEWTTY
   if (v1)  /* gtty? */
      ttystty(tp, v1); /* Normal */
   else
      { /* Monkey ttystty without wflushtty */
      v = u.u_arg;
      tp->t_speeds = *v++;
      tp->t_erase = v->lobyte;
      tp->t_kill = v->hibyte;
      tp->t_flags = v[1];
      }
# endif
   }

/* =================== A W A I T   &   C A P A C I T Y ========== */


/* -------------------------- P T C C A P ----------------------- */

ptccap(ip, v)
   struct inode *ip;
   int *v;
   {
   register struct tty *tp;

   tp = &pty[ip->i_addr[0].d_minor];
   *v = tp->t_outq.c_cc;            /* Can read whatever's in output queue */
   if (tp->t_state & (TSO_XOFFHNG|TS_ENDPAGE)) /* Unless it's hung */
      *v = 0;
   v++;
   *v = TTYHOG - tp->t_rawq.c_cc;   /* After this many, chars thrown away */
# ifdef NEWTTY
   *v =- 2;   /* Allow some slop for T_EOFMARK */
# endif
# ifndef NEWTTY
   *v =/ 2;   /* Allow for every user char being followed by delimiter */
# endif
   }

/* -------------------------- P T S C A P ---------------------- */

ptscap(ip, v)
   struct inode *ip;
   int *v;
   {
   register struct tty *tp;

   tp = &pty[ip->i_addr[0].d_minor];
   ttycap(tp, v);
   }

/* -------------------------- P T S A W T --------------------- */

ptsawt(type, ip, fd)
   char type;
   struct inode *ip;
   int fd;
   {
   extern char *ablei();
   register struct tty *tp;

   tp = &pty[ip->i_addr[0].d_minor];
   tp->t_itp = ablei(type, 0, ip, fd);
   }

/* -------------------------- P T C A W T --------------------- */

ptcawt(type, ip, fd)
   char type;
   struct inode *ip;
   int fd;
   {
   extern char *ablei();
   register struct tty *tp;

   tp = &pty[ip->i_addr[0].d_minor];
   tp->t_itpc = ablei(type, 0, ip, fd);
   }