Path: utzoo!attcan!uunet!munnari!uqcspe!elec!marks
From: marks@elec.uq.oz (Mark Schulz)
Newsgroups: comp.os.minix
Subject: new tty driver (2 of 3)
Keywords: tty MINIX rs232
Message-ID: <26@elec.uq.oz>
Date: 22 Sep 88 07:23:39 GMT
Organization: Elec Eng, Queensland Uni, Australia
Lines: 1829


Please reply to hannam.uq.oz . I'm using someone elses account to send
this.

echo x - ioctl.c
sed 's/^X//' >ioctl.c <<'*-*-END-of-ioctl.c-*-*'
X/* ioctl.c - get/set character device modes
X *
X * Modifications:
X * 18/9/88  -	Modified for new tty driver & improved efficiency of
X *			code produced.
X */
X
X#include "lib.h"
X#include 
X#include 
X
Xtypedef union {
X	long				l;
X	struct {int i0, i1;}		i;
X	struct {char c0,c1,c2,c3;}	c;
X	} olong;
X
Xunion ioctl_struct {
X  struct sgttyb *argp;
X  struct tchars *argt;
X  int		*argi;
X  char		*argc;
X};
X
XPUBLIC int ioctl(fd, request, u)
Xint fd;
Xint request;
Xunion ioctl_struct u;
X{
X  olong tmp;
X  int n;
X
X  M.TTY_REQUEST = request;
X  M.TTY_LINE = fd;
X
X  switch(request) {
X     case TIOCGETP:
X  	n = callx(FS, IOCTL);
X	tmp.l = M.TTY_FLAGS;
X  	u.argp->sg_flags  = tmp.i.i0;
X  	tmp.l = M.TTY_SPEK;
X	u.argp->sg_ospeed = tmp.c.c3;		/* Non - compat with old */
X	u.argp->sg_ispeed = tmp.c.c2;
X	u.argp->sg_erase  = tmp.c.c1;
X	u.argp->sg_kill   = tmp.c.c0;
X  	return n;
X
X     case TIOCSETN:
X     case TIOCSETP:
X	tmp.i.i1 = 0;
X	tmp.i.i0 = u.argp->sg_flags;
X	M.TTY_FLAGS = tmp.l;
X	tmp.c.c3 = u.argp->sg_ospeed;
X	tmp.c.c2 = u.argp->sg_ispeed;
X	tmp.c.c1 = u.argp->sg_erase;
X	tmp.c.c0 = u.argp->sg_kill;
X	M.TTY_SPEK = tmp.l;
X	break;
X 
X     case TIOCGETC:
X  	n = callx(FS, IOCTL);
X  	tmp.l = M.TTY_SPEK;
X  	u.argt->t_intrc  = tmp.c.c3;
X  	u.argt->t_quitc  = tmp.c.c2;
X  	u.argt->t_startc = tmp.c.c1;
X  	u.argt->t_stopc  = tmp.c.c0;
X  	tmp.l = M.TTY_FLAGS;
X  	u.argt->t_eofc   = tmp.c.c1;
X  	u.argt->t_brkc   = tmp.c.c0;
X  	return n;
X
X     case TIOCSETC:
X	tmp.c.c3 = u.argt->t_intrc;
X	tmp.c.c2 = u.argt->t_quitc;
X	tmp.c.c1 = u.argt->t_startc;
X	tmp.c.c0 = u.argt->t_stopc;
X  	M.TTY_SPEK = tmp.l;
X  	tmp.i.i1 = 0;
X  	tmp.c.c1 = u.argt->t_eofc;
X  	tmp.c.c0 = u.argt->t_brkc;
X  	M.TTY_FLAGS = tmp.l;
X  	break;
X
X     case FIONREAD:
X     case TIOCMODG:
X     case TIOCGETM:
X	n= callx(FS, IOCTL);
X	*u.argi = (unsigned) M.TTY_FLAGS;
X	return n;
X
X     case TIOCMODS:
X     case TIOCSETM:
X	M.TTY_FLAGS = (unsigned long) *u.argi;
X	break;
X
X     case TIOCSTI:
X	M.TTY_FLAGS = (unsigned long) *u.argc;
X	break;
X
X     case TIOCSBRK:
X     case TIOCCBRK:
X     case TIOCSDTR:
X     case TIOCCDTR:
X     case TIOCSMLB:
X     case TIOCCMLB:
X     case TIOCSTART:
X     case TIOCSTOP:
X	break;
X
X     default:
X	errno = -(EINVAL);
X	return -1;
X  }
X  return callx(FS, IOCTL);
X}
*-*-END-of-ioctl.c-*-*
echo x - login.c
sed 's/^X//' >login.c <<'*-*-END-of-login.c-*-*'
X/* login - log into the system		Author: Andrew Hannam
X *
X * Modifications:
X * 21/9/88   -	Added wtmp support as in V1.3
X *
X * BUGS:
X * Doesn't handle LCASE detection.
X * Doesn't kill parent process's.
X */
X
X#include 
X#include 
X#include 
X#include 
X
X#define NULL (char *) 0
X
X/* default files */
X#define DEFSH		"/bin/sh"
X#define WTMPFILE	"/usr/adm/wtmp"
X
X/* default INT, QUIT, START, STOP, EOF, BRK chars (for the login process) */
Xstruct tchars tchars = {-1, -1, 021, 023, 004, -1};
X#define DEFKILL		'@'
X#define DEFERASE	'\b'
X
X/* The following chars need to be changed before exec'ing the shell */
X#define DEFINT		0177
X#define DEFQUIT		034
X
X/* The following flags are used by login, passwd & the user process */
X#define FLAGMASK	(ANYP|ODDP|EVENP)
X#define LOGINFLAGS	ECHO|XTABS|CRTERA|DECCTQ|CRMOD|CRTKIL
X#define PASSWDFLAGS	CRMOD|DECCTQ
X#define USERFLAGS	ECHO|XTABS|CRTERA|DECCTQ|CRMOD|CRTKIL|CTLECH
X
Xchar Aname[16]	=	"-";
Xchar Epath[]	=	"PATH=:/bin:/usr/bin";
Xchar Ehome[64]	=	"HOME=";
Xchar Euser[24]	=	"USER=";
Xchar Eterm[]	=	"TERM=minix";
Xchar Eshell[64] =	"SHELL=";
X
Xchar *arge[]= { Epath, Ehome, Euser, Eterm, Eshell, (char *) 0 };
Xchar *argp[]= { Aname, (char *)0 };
Xchar ttyname[] = "tty?";
X#define TDIGIT 3
Xchar *wtmpfile = WTMPFILE;
X#define WTMPSIZE 8
X
Xchar *crypt();
Xstruct passwd *getpwnam();
X
Xmain() 
X{
Xstatic	char    buf[30],
X		buf1[30];
X	int     n, n1, bad;
Xstatic	struct stat statbuf;
Xstatic	struct sgttyb args;
Xstatic	struct passwd *pwd;
X
X	/* Make sure the file system is valid (in case we wish to reboot) */
X	sync();
X
X	/* Get current modes and set default chars for login */
X	ioctl(0, TIOCGETP, &args);
X	ioctl(0, TIOCSETC, &tchars);
X	args.sg_kill = DEFKILL;
X	args.sg_erase = DEFERASE;
X
X	/* Set up signals to defaults */
X	for(n = 1; n <= NR_SIGS; ++n) signal(n, SIG_DFL);
X
X	/* Look up /dev/tty number. */
X	fstat(0, &statbuf);
X	ttyname[TDIGIT] = '0' + statbuf.st_rdev & 0377;
X
X	/* Check for login, passwd until sucessful */
X	for (;;) {
X	/* Get login name */
X		bad = 0;
X		args.sg_flags &= FLAGMASK;
X		args.sg_flags |= LOGINFLAGS;
X		ioctl (0, TIOCSETP, &args);
X		do {
X			write(1,"login: ",7);
X			n = read (0, buf, 30);
X		} while (n < 2);
X		buf[n - 1] = 0;
X	/* Look up login/passwd. */
X		if ((pwd = getpwnam (buf)) == 0)
X			bad++;
X	/* Get passwd */
X		if (bad || strlen (pwd->pw_passwd) != 0) {
X			args.sg_flags &= FLAGMASK;
X			args.sg_flags |= CRMOD|DECCTQ;
X			ioctl (0, TIOCSETP, &args);
X			write(1,"Password: ",10);
X			n1 = read (0, buf1, 30);
X			buf1[n1 - 1] = 0;
X			write(1,"\n",1);
X			if (bad) crypt(buf1,"*");
X			if (bad || strcmp (pwd->pw_passwd, crypt(buf1, pwd->pw_passwd))) {
X				write (1,"Login incorrect\n",16);
X				continue;
X			}
X		}
X
X/* Successful login. */
X	/* Creat wmtp entry. */
X		wtmp(ttyname, pwd->pw_name);
X	/* Set up directories and uid,gid */
X		chdir("/");
X		setgid (pwd->pw_gid);
X		setuid (pwd->pw_uid);
X		chdir (pwd->pw_dir);
X	/* Set up environment, params & shell */
X		strcat(Ehome,pwd->pw_dir);
X		strcat(Euser,pwd->pw_name);
X		if (!pwd->pw_shell || !*pwd->pw_shell)
X			pwd->pw_shell = DEFSH;
X		strcat(Eshell,pwd->pw_shell);
X		strcat(Aname,rindex(pwd->pw_shell,'/')+1);
X	/* Set up tty modes */
X		args.sg_flags &= FLAGMASK;
X		args.sg_flags |= USERFLAGS;
X		ioctl(0, TIOCSETP, &args);
X		tchars.t_intrc = DEFINT;
X		tchars.t_quitc = DEFQUIT;
X		ioctl(0, TIOCSETC, &tchars);
X	/* Do the exec */
X		execve(pwd->pw_shell, argp, arge);
X		write(1,"exec failure\n",13);
X	}
X}
X
Xwtmp(tty, name)
X{
X/* Make an entry in wtmp file. */
X
X  int i, fd;
X  long t, time();
X  char ttybuff[WTMPSIZE], namebuff[WTMPSIZE];
X
X  fd = open(wtmpfile, 2);
X  if (fd < 0) return;		/* if wtmp does not exist, no accounting */
X  lseek(fd, 0L, 2);		/* append to file */
X
X  for (i = 0; i < WTMPSIZE; i++) {
X	ttybuff[i] = 0;
X	namebuff[i] = 0;
X  }
X  strncpy(ttybuff, tty, 8);
X  strncpy(namebuff, name, 8);
X  time(&t);
X  write(fd, ttybuff, WTMPSIZE);
X  write(fd, namebuff, WTMPSIZE);
X  write(fd, &t, sizeof(t));
X  close(fd);
X}
*-*-END-of-login.c-*-*
echo x - stty.c
sed 's/^X//' >stty.c <<'*-*-END-of-stty.c-*-*'
X/* stty - set terminal mode  	Author: Andy Tanenbaum
X * Modifications:
X * 21/9/88  -	Added support for new tty driver
X */
X
X#include 
X
Xchar *on[] = {"-tabs",  "cbreak",  "raw",  "-nl",  "echo"
X		,"prterase", "crtbs", "crterase", "crtkill"
X		,"ctlecho", "decctq", "lcase"};
Xchar *off[]= {"tabs", "", "", "nl", "-echo", "", "", ""
X		,"-crtkill", "-ctlecho", "-decctq", ""};
Xchar *speed[]= {"45.5", "50", "75", "110", "134.5", "150", "200"
X		,"300", "600", "1200", "1800", "2000", "2400"
X		,"4800", "9600", "19.2K", "38.4K", "57.6K", "115.2K"};
Xchar *speed2[]= {"19200", "38400", "57600", "115200"};
X
X#define SPEED2 15
X#define NO_SPEEDS NO_BAUDS
X
X#define match(s1,s2)  (!strcmp(s1,s2))
X
Xstruct sgttyb args;
Xstruct tchars tch;
Xunsigned term_mod;
X
X/* Chars for reset */
X#define KILL	 '@'		/* @ */
X#define ERASE	'\b'		/* CTRL-H */
X#define STARTC	 021		/* CTRL-Q */
X#define STOPC	 023		/* CTRL-S */
X#define QUITC	 034		/* CTRL-\ */
X#define EOFC	 004		/* CTRL-D */
X#define DELC	0177		/* DEL */
X
X#define FD	   0		/* what file descriptor to use */
X
Xint k;
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X
X  /* stty with no arguments just reports on current status. */
X  ioctl(FD, TIOCGETP, &args);
X  ioctl(FD, TIOCGETC, &tch);
X  ioctl(FD, TIOCGETM, &term_mod);
X  if (argc == 1) {
X	report();
X	exit(0);
X  }
X
X  /* Process the options specified. */
X  k = 1;
X  while (k < argc) {
X	option(argv[k], k+1 < argc ? argv[k+1] : "");
X	k++;
X  }
X  ioctl(FD, TIOCSETP, &args);
X  ioctl(FD, TIOCSETC, &tch);
X  ioctl(FD, TIOCSETM, &term_mod);
X  exit(0);
X}
X
X
X
Xreport()
X{
X  int mode;
X
X
X  mode = args.sg_flags;
X  pr(mode&XTABS, 0);
X  pr(mode&CBREAK, 1);
X  pr(mode&RAW, 2);
X  pr(mode&CRMOD,3);
X  pr(mode&ECHO,4);
X  pr(mode&PRTERA,5);
X  pr(mode&CRTBS,6);
X  pr(mode&CRTERA,7);
X  pr(mode&CRTKIL,8);
X  pr(mode&CTLECH,9);
X  pr(mode&DECCTQ,10);
X  pr(mode&LCASE,11);
X  prints("\n");
X  if (term_mod&D_SERIAL)
X  	prints("serial: ");
X  else if (term_mod&D_CONSOLE)
X  	prints("console: ");
X  else	prints("tty type unknown: ");
X  prints(" input @ %s",args.sg_ispeed ? speed[args.sg_ispeed - 1]:"????");
X  if (term_mod&D_SERIAL) {
X  	if (term_mod&RX_XONXOFF)
X  		prints(" %c xonxoff ", '5' + (term_mod&SER_DATABITS));
X  	else
X		prints(" %c hardware", '5' + (term_mod&SER_DATABITS));
X  }
X  prints("\toutput @ %s",args.sg_ispeed ? speed[args.sg_ospeed - 1]:"????");
X  if (term_mod&D_SERIAL) {
X  	if (term_mod&TX_XONXOFF)
X  		prints(" %c xonxoff", '5' + (term_mod&SER_DATABITS));
X  	else
X		prints(" %c hardware", '5' + (term_mod&SER_DATABITS));
X  }
X  prints("\n\nkill = ");prctl(args.sg_kill);
X  prints("\nerase = ");	prctl(args.sg_erase);
X  prints("\nint = "); 	prctl(tch.t_intrc);
X  prints("\nquit = "); 	prctl(tch.t_quitc);
X  prints("\nstop = "); 	prctl(tch.t_stopc);
X  prints("\nstart = "); prctl(tch.t_startc);
X  prints("\neof = "); 	prctl(tch.t_eofc);
X  prints("\n");
X}
X
Xpr(f, n)
Xint f,n;
X{
X  if (f)
X	prints("%s ",on[n]);
X  else
X	prints("%s ",off[n]);
X}
X
Xoption(opt, next)
Xchar *opt, *next;
X{
X	register spe;
X	static char inflag= 0, outflag= 0;
X  if (match(opt, "-tabs"))	{args.sg_flags |= XTABS; return;}
X  if (match(opt, "-raw"))	{args.sg_flags &= ~RAW; return;}
X  if (match(opt, "-cbreak"))	{args.sg_flags &= ~CBREAK; return;}
X  if (match(opt, "-echo"))	{args.sg_flags &= ~ECHO; return;}
X  if (match(opt, "-nl"))	{args.sg_flags |= CRMOD; return;}
X  if (match(opt, "-prterase"))	{args.sg_flags &= ~PRTERA; return;}
X  if (match(opt, "-crtbs"))	{args.sg_flags &= ~CRTBS; return;}
X  if (match(opt, "-crterase"))	{args.sg_flags &= ~CRTERA; return;}
X  if (match(opt, "-crtkill"))	{args.sg_flags &= ~CRTKIL; return;}
X  if (match(opt, "-ctlecho"))	{args.sg_flags &= ~CTLECH; return;}
X  if (match(opt, "-decctq"))	{args.sg_flags &= ~DECCTQ; return;}
X  if (match(opt, "-lcase"))	{args.sg_flags &= ~LCASE; return;}
X  if (match(opt, "tabs"))	{args.sg_flags &= ~XTABS; return;}
X  if (match(opt, "raw"))	{args.sg_flags |= RAW; return;}
X  if (match(opt, "cbreak"))	{args.sg_flags |= CBREAK; return;}
X  if (match(opt, "echo"))	{args.sg_flags |= ECHO; return;}
X  if (match(opt, "nl"))		{args.sg_flags &= ~CRMOD; return;}
X  if (match(opt, "prterase"))	{args.sg_flags &= ~(CRTBS|CRTERA);
X  				 args.sg_flags |= PRTERA; return;}
X  if (match(opt, "crtbs"))	{args.sg_flags &= ~(PRTERA|CRTERA);
X  				 args.sg_flags |= CRTBS; return;}
X  if (match(opt, "crterase"))	{args.sg_flags &= ~(PRTERA|CRTBS);
X  				 args.sg_flags |= CRTERA; return;}
X  if (match(opt, "crtkill"))	{args.sg_flags |= CRTKIL; return;}
X  if (match(opt, "ctlecho"))	{args.sg_flags |= CTLECH; return;}
X  if (match(opt, "decctq"))	{args.sg_flags |= DECCTQ; return;}
X  if (match(opt, "lcase"))	{args.sg_flags |= LCASE; return;}
X  if (match(opt, "kill"))	{args.sg_kill = *next; k++; return;}
X  if (match(opt, "erase"))	{args.sg_erase = *next; k++; return;}
X  if (match(opt, "int"))	{tch.t_intrc = *next; k++; return;}
X  if (match(opt, "quit"))	{tch.t_quitc = *next; k++; return;}
X  if (match(opt, "start"))	{tch.t_startc = *next; k++; return;}
X  if (match(opt, "stop"))	{tch.t_stopc = *next; k++; return;}
X  if (match(opt, "eof"))	{tch.t_eofc = *next; k++; return;}
X
X  if (match(opt, "default") || match(opt,"reset"))	{
X	args.sg_flags = ECHO|CRMOD|XTABS|CRTERA|CTLECH|DECCTQ|CRTKIL;
X	args.sg_kill = KILL;
X	args.sg_erase = ERASE;
X  	tch.t_intrc = DELC;
X  	tch.t_quitc = QUITC;
X  	tch.t_startc = STARTC;
X  	tch.t_stopc = STOPC;
X  	tch.t_eofc = EOFC;
X  	return;
X  }
X
X  if (match(opt,"input")) {inflag++ ; outflag = 0; return;}
X  if (match(opt,"output")) {outflag++; inflag = 0; return;}
X  if (term_mod&D_SERIAL) {
X  	if (atoi(opt) >= 5 && atoi(opt) <= 8) {
X  		term_mod &= ~SER_DATABITS;
X  		term_mod |= atoi(opt) - 5;	return;
X	}
X	if (match(opt,"xonxoff") || match(opt,"-hardware")) {
X		if (!outflag) term_mod |= RX_XONXOFF;
X		if (!inflag)  term_mod |= TX_XONXOFF;
X		return;
X	}
X	if (match(opt,"-xonxoff") || match(opt,"hardware")) {
X		if (!outflag) term_mod &= ~RX_XONXOFF;
X		if (!inflag)  term_mod &= ~TX_XONXOFF;
X		return;
X	}
X  }
X
X  for(spe=0;spe < NO_SPEEDS;spe++)
X  	if (match(opt,speed[spe])) break;
X  if (spe >= NO_SPEEDS)
X  	for (spe=SPEED2;spe < NO_SPEEDS;spe++)
X  		if (match(opt,speed2[spe-SPEED2])) break;
X  if (spe < NO_SPEEDS) {
X  	if (!outflag) args.sg_ispeed= spe+1;
X  	if (!inflag)  args.sg_ospeed= spe+1;
X  	return;
X  }
X
X  std_err("unknown mode: ");
X  std_err(opt);
X  std_err("\n");
X
X}
X
Xprctl(c)
Xchar c;
X{
X  if (c < ' ')
X	prints("^%c", 'A' - 1 + c);
X  else if (c == 0177)
X	prints("DEL");
X  else
X	prints("%c", c);
X}
*-*-END-of-stty.c-*-*
echo x - term.c
sed 's/^X//' >term.c <<'*-*-END-of-term.c-*-*'
X/* term - terminal simulator		Author: Andy Tanenbaum
X * Modififications:
X * 21/9/88  -	Added support for new tty driver
X */
X
X/* This program allows the user to turn a MINIX system into a dumb
X * terminal to communicate with a remote computer over a modem.  It
X * forks into two processes.  The parent sits in a tight loop copying
X * from the keyboard to the modem.  The child sits in a tight loop
X * copying from the modem to the screen.
X *
X * Example usage:
X *	term			: 1200 baud, 8 bits/char, no parity
X *	term 9600 7 even	: 9600 baud, 7 bits/char, even parity
X *	term odd 300 7		:  300 baud, 7 bits/char, odd parity
X */
X
X#include 
X#include 
X
X#define NCHECKS 10
X#define BAD -1
X#define GOOD 1
X#define MODEM "/dev/tty1"	/* special file attached to the modem */
X#define ESC 033			/* character to hit to leave simulator */
X#define LIMIT 3			/* how often do you have to hit  ESC to exit*/
X#define CHUNK 1024		/* how much to read at once */
X
Xint modem, pid;			/* file descriptor for modem */
X
Xchar *speed1[]= {"45.5", "50", "75", "110", "134.5", "150", "200"
X		,"300", "600", "1200", "1800", "2000", "2400"
X		,"4800", "9600", "19.2K", "38.4K", "57.6K", "115.2K"};
Xchar *speed2[]= {"19200", "38400", "57600", "115200"};
X
X#define SPEED2 15
X#define NO_SPEEDS NO_BAUDS
X#define match(s1,s2)	(!strcmp(s1,s2))
X
Xstruct sgttyb sgtty, sgsave1, sgsave2;
Xunsigned modsave1;
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X
X  sync();
X  modem = open(MODEM, 2);
X  if (modem < 0) {
X	printf("Can't open modem on %s\n", MODEM);
X	exit(1);
X  }
X  set_uart(argc, argv);
X
X  /* Main body of the terminal simulator. */
X  if ( (pid = fork()))
X	copy(0, modem, ESC);	/* copy from stdin to modem */
X  else
X	copy(modem, 1, -1);	/* copy from modem to stdout */
X}
X
Xset_uart(argc, argv)
Xint argc;
Xchar *argv[];
X{
X/* Set up the UART parameters. */
X
X  int i, k, modstat
X	, speed, parity, bits, handsh;
X
X  ioctl(modem, TIOCGETM, &modstat);
X  modsave1 = modstat;
X  if (!(modstat&D_SERIAL))
X  	error("term will only run on serial lines\n");
X
X  ioctl(modem, TIOCGETP, &sgtty);
X  sgsave1 = sgtty;		/* modem parameters saved */
X  	/* Get defaults */
X  parity = sgtty.sg_flags & (ODDP|EVENP|ANYP|NONEP);
X  bits = modstat&SER_DATABITS;
X  handsh = modstat&(RX_XONXOFF|TX_XONXOFF);
X  speed = sgtty.sg_ispeed;
X
X  /* Examine all the parameters and check for validity. */
X  for (i = 1; i < argc; i++) {
X	if (match(argv[i], "even")) {parity = EVENP; continue;}
X	if (match(argv[i], "odd"))  {parity = ODDP; continue;}
X	if (match(argv[i], "any"))  {parity = ANYP; continue;}
X	if (match(argv[i], "none")) {parity = NONEP; continue;}
X
X	if (match(argv[i], "xonxoff")) {handsh = TX_XONXOFF|RX_XONXOFF;
X					continue;}
X	if (match(argv[i], "hardware")) {handsh = 0; continue;}
X
X	k = atoi(argv[i]);
X	if (k >= 5 && k <= 8) {
X		bits = k - 5;
X		continue;
X	}
X
X	speed = validity(argv[i]);
X	if (speed == BAD) {
X		printf("Invalid parameter: %s\n", argv[i]);
X		error("Usage: term [baudrate] [data_bits] [parity] [handshaking]\n");
X	}
X  }
X
X  /* Set the modem parameters. */
X  modstat &= ~(SER_DATABITS|TX_XONXOFF|RX_XONXOFF);
X  modstat |= bits | handsh;
X  ioctl(modem, TIOCSETM, &modstat);
X  sgtty.sg_ispeed = sgtty.sg_ospeed = speed;
X  sgtty.sg_flags = RAW | parity;
X  ioctl(modem, TIOCSETP, &sgtty);
X  
X  /* Fetch the keyboard parameters, save them, and set new ones. */
X  ioctl(0, TIOCGETP, &sgtty);
X  sgsave2 = sgtty;		/* modem parameters */
X  sgtty.sg_flags = (sgtty.sg_flags&(ANYP|EVENP|ODDP|NONEP)) | RAW;
X  ioctl(0, TIOCSETP, &sgtty);
X}
X
X
Xint validity(s)
Xchar *s;
X{
X/* Check speed parameter for legality. */
X
X  int i;
X
X  for (i = 0; i < NO_BAUDS; i++) {
X	if (match(s, speed1[i])) return(i+1);
X  }
X  for (i = 0; i < NO_BAUDS - SPEED2 ; i++) {
X  	if (match(s, speed2[i])) return(i+SPEED2+1);
X  }
X  return(BAD);
X}
X 
X
Xcopy(in, out, end)
Xint in, out, end;
X{
X/* Copy from the keyboard to the modem or vice versa. If the end character
X * is seen LIMIT times in a row, quit.  For the traffic from the modem, the
X * end character is -1, which cannot occur since the characters from the
X * modem are unsigned integers in the range 0 to 255.
X */
X
X  int t, count, state = 0;
X  char buf[CHUNK], *p;
X
X  while (1) {
X	if ( (count = read(in, buf, CHUNK)) < 0) {
X		printf("Can't read from modem\r\n");
X		quit();
X	}
X
X	if (end > 0) {
X		for (p = &buf[0]; p < &buf[count]; p++) {
X			t = *p & 0377;		/* t is unsigned int 0 - 255 */
X			if (t == end) {
X				if (++state == LIMIT) quit();
X			} else {
X				state = 0;
X			}
X		}
X	}
X	write(out, buf, count);
X  }
X}
X
Xerror(s)
Xchar *s;
X{
X  printf("%s", s);
X  exit(1);
X}
X
Xquit()
X{
X  ioctl(modem, TIOCSETP, &sgsave1);
X  ioctl(modem, TIOCSETM, &modsave1);
X  ioctl(0, TIOCSETP, &sgsave2);
X  if (getpid() != pid) kill(pid, SIGINT);
X  exit(0);
X}
X
*-*-END-of-term.c-*-*
echo x - xonxoff.c
sed 's/^X//' >xonxoff.c <<'*-*-END-of-xonxoff.c-*-*'
X#include 
X
Xmain(argc,argv)
Xchar *argv[];
X{
Xint i;
X
X	if (argc > 2 || (argc == 2 && *argv[1] != 't' && *argv[1] != 'r')) {
X		printf("Usage: %s [t|r]\n",*argv);
X		exit(1);
X	}
X	ioctl(0,TIOCGETM, &i);
X	if (!(i&D_SERIAL)) {
X		printf("This is not a serial line: device = 0x%x mode = 0x%x\n"
X			,i>>8, i&0xFF);
X		exit(1);
X	}
X
X	if (argc == 2) {
X		if (*argv[1] == 't')	i |= TX_XONXOFF;
X		else			i |= RX_XONXOFF;
X	} else				i ^= (TX_XONXOFF|RX_XONXOFF);
X
X	ioctl(0,TIOCSETM, &i);
X	ioctl(0,TIOCGETM, &i);
X	if (i&TX_XONXOFF)
X		printf("TX now in XONXOFF handshaking\n");
X	else
X		printf("TX now in HARDWARE handshaking\n");
X	if (i&RX_XONXOFF)
X		printf("RX now in XONXOFF handshaking\n");
X	else
X		printf("RX now in HARDWARE handshaking\n");
X}
*-*-END-of-xonxoff.c-*-*
echo x - tty1.c
sed 's/^X//' >tty1.c <<'*-*-END-of-tty1.c-*-*'
X/* tty1.c - device independant code for tty driver
X *
X * Written by:  A.Hannam	(Feb 1988)
X *
X * Modifications:
X * 21/8/88  -	Modified for MINIX V1.3 on IBM-PC
X */
X
X/* This file contains the high level terminal driver and is device independant.
X * It accepts characters to be printed from programs, process's them and then
X * passes them on to the raw (device dependant) output routines. It also
X * accepts input from raw (device dependant) routines, process's them acording
X * to the modes set by ioctl, and queues it for programs.
X * This file contains 3 main entry points: tty_task(), sigchar() and putc().
X * tty_task - takes messages to do work from readers, writers and interrupt
X *	service routines.
X * sigchar - used to send signals from the keyboard. (Special for F10)
X * putc - used in the printf routine for the kernel.
X *
X * The valid messages and their parameters are:
X *
X *   TTY_CHAR_INT: interrupt routines have produced char(s)
X *   TTY_O_DONE:   interrupt routines need more charactors to output
X *   TTY_READ:     a process wants to read from a terminal
X *   TTY_WRITE:    a process wants to write on a terminal
X *   TTY_IOCTL:    a process wants to change a terminal's parameters
X *   TTY_SETPGRP:  a process wants to change a terminal's process group
X *   CANCEL:       terminate a previous incomplete system call immediately
X *
X *    m_type      TTY_LINE   PROC_NR    COUNT   TTY_SPEK  TTY_FLAGS  ADDRESS
X * ---------------------------------------------------------------------------
X * | TTY_CHAR_INT|minor dev|         |         |         |         |array ptr|
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_O_DONE  |minor dev|         |         |         |         |         |
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_READ    |minor dev| proc nr |  count  |         |         | buf ptr |
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_WRITE   |minor dev| proc nr |  count  |         |         | buf ptr |
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_IOCTL   |minor dev| proc nr |func code|erase etc|  flags  |         |
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | TTY_SETGPRP |minor dev| proc nr |   pgrp  |         |         |         |
X * |-------------+---------+---------+---------+---------+---------+---------|
X * | CANCEL      |minor dev| proc nr |         |         |         |         |
X * ---------------------------------------------------------------------------
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "../h/sgtty.h"
X#include "../h/signal.h"
X#include "../h/callnr.h"
X#include "type.h"
X#include "const.h"
X#include "proc.h"
X
X#define HIGH_LEVEL
X#include "tty.h"
X
XPUBLIC tty_entry tty_struct[NR_TTYS];		/* The tty tables */
XPUBLIC void tty_task(), putc();			/* Public routines */
XPUBLIC void do_nothing(), sigchar(), rs_flush();
X
Xextern int lock();
Xextern void restore();
Xextern char get_byte();
X
X
X#define TAB_SIZE	8	/* distance between tabs (must be pwr of 2) */
X
X#define ERASE_CHAR	'\b'	/* default erase character */
X#define KILL_CHAR	'@'	/* default kill character */
X#define INTR_CHAR	0177	/* default interrupt character */
X#define QUIT_CHAR	034	/* default quit characterc (CTRL-\) */
X#define XOFF_CHAR	023	/* default x-off character (CTRL-S) */
X#define XON_CHAR	021	/* default x-on character  (CTRL-Q) */
X#define EOT_CHAR	004	/* default EOF character   (CTRL-D) */
X#define BRK_CHAR	'\n'	/* default delimiter (\n) (not used) */
X#define WORD_MASK     0xFFFF	/* mask for 16 bits */
X#define OFF_MASK      0x000F	/* mask for  4 bits */
X
X		/* Our private routines */
XPRIVATE void tty_bad(), tty_init(), do_charint(), proc_ints()
X		,echo(), do_read(), do_ioctl(), do_cancel(), do_write()
X		,tty_reply(), out_chars(), do_setpgrp();
XPRIVATE bool do_cooked(), in_char();
XPRIVATE unsigned chuck();
XPRIVATE int rd_chars();
X
XPRIVATE charfbuf tty_copy_buf;		/* copy buf used to avoid races */
XPRIVATE message c_mess;			/* used by the clock in flushing */
X
X/*===========================================================================*
X *				tty_task				     *
X *===========================================================================*/
XPUBLIC void tty_task()
X{
X/* Main routine of the terminal task. */
X
X  message tty_mess;		/* buffer for all incoming messages */
X  register tty_entry *tp;
X
X  tty_init();			/* initialize */
X  while (TRUE) {
X	receive(ANY, &tty_mess);
X	if (tty_mess.TTY_LINE < 0 || tty_mess.TTY_LINE > NR_TTYS-1) {
X		tty_bad(&tty_mess);
X		continue;
X		}
X	tp = &tty_struct[tty_mess.TTY_LINE];
X	switch(tty_mess.m_type) {
X
X			/* Hardware Interrupts */
X	    case TTY_CHAR_INT:
X	    case TTY_O_DONE:	proc_ints();			break;
X
X			/* Software Interrupts */
X	    case TTY_READ:	do_read(tp, &tty_mess);		break;
X	    case TTY_WRITE:	do_write(tp, &tty_mess);	break;
X	    case TTY_IOCTL:	do_ioctl(tp, &tty_mess);	break;
X	    case TTY_SETPGRP:	do_setpgrp(tp, &tty_mess);	break;
X	    case CANCEL   :	do_cancel(tp, &tty_mess);	break;
X	    default:		tty_bad(&tty_mess);
X	}
X  }
X}
X
X
X/*===========================================================================*
X *				tty_init				     *
X *===========================================================================*/
XPRIVATE void tty_init()
X{
X/* Initialize all the terminals and tty_tables */
X
X  register tty_entry *tp;
X  register i;
X
X  /* Prepare the clock tasks message (so it is a legal message) */
X  c_mess.TTY_LINE = 0;			/* Has to point to a valid tty (any) */
X  c_mess.m_type = TTY_CHAR_INT;		/* Could also have been TTY_O_DONE */
X
X  /* Initialize the TTY structure */
X  for (tp = &tty_struct[0],i=0; i < NR_TTYS; tp++,i++) {
X	bufinit(tp->tty_inbuf);
X	tp->tty_lfct = 0;
X	tp->tty_writers = 0;
X	tp->tty_state = 0;
X	tp->tty_ddmod = 0;
X	tp->tty_mode = CRMOD|XTABS|ECHO|CRTERA|CTLECH|CRTKIL|DECCTQ;
X	tp->tty_erase = ERASE_CHAR;
X	tp->tty_kill  = KILL_CHAR;
X	tp->tty_intr  = INTR_CHAR;
X	tp->tty_quit  = QUIT_CHAR;
X	tp->tty_xon   = XON_CHAR;
X	tp->tty_xoff  = XOFF_CHAR;
X	tp->tty_eof   = EOT_CHAR;
X	tp->tty_brk   = BRK_CHAR;
X	(*dev_init[i])(tp);
X  }
X}
X
X/*===========================================================================*
X *				tty_bad					     *
X *===========================================================================*/
XPRIVATE void tty_bad(p)
Xmessage *p;
X{
X	/* dump out bad tty messages */
X#ifdef DEBUG
X	register char *q= (char *)p;
X	register x;
X
X	printf("\nIllegal TTY message:\n");
X#ifdef DEBUG2
X	for(x=1; x <= sizeof(*p); x++) {
X		printf("0x%x ", (*q++) & 0xFF);
X		if( x%8 == 0) printf("\n");
X		}
X	printf("\n");
X	printf("m_type= %d, source= %d, TTY_LINE= %d, PROC_NR= %d\n"
X		,p->m_type,p->m_source,p->TTY_LINE,p->PROC_NR);
X	printf("COUNT= %d, ADDRESS= 0x%x\n",p->COUNT,p->ADDRESS);
X	printf("\tor ...\n");
X#endif DEBUG2
X	printf("m_type= %d, dest= %d, PROC_NR= %d, STATUS= %d\n"
X		,p->m_type,p->m_source,p->REP_PROC_NR,p->REP_STATUS);
X	printf("flags= %D, spek= %D\n",p->TTY_FLAGS,p->TTY_SPEK);
X#else
X	printf("m_type = %d\n",p->m_type);
X	panic("TTY",p->TTY_LINE);
X#endif DEBUG
X}
X
X/*===========================================================================*
X *				rs_flush				     *
X *===========================================================================*/
XPUBLIC void rs_flush()
X{
X/* The clock task has recieved notification that input or output flushing is
X * needed. This is a general routine unlike the one in the original (V1.3)
X * tty which was specific to the rs232 lines. This should probably be
X * renamed to something more meaningful but will suffice at present.
X * Simply send an interrupt message for the tty.
X */
X	interrupt(TTY, &c_mess);
X	flush_flag = 0;
X}
X
X/*===========================================================================*
X *				proc_ints				     *
X *===========================================================================*/
XPRIVATE void proc_ints()
X{
X/* This routine process's hardware interrupts. It is needed as MINIX can only
X * handle one interrupt per task at a time. This means interrupts can be lost.
X * This solution simply says that an interrupt has occured (it doesn't matter
X * whose) and the state of the work needed is stored else where (in the
X * tty_struct). It is a bad problem with a horrible solution. Still it works.
X */
Xregister tty_entry *tp;
Xregister flags;
X
X  /* Some one needs some flushing - find out who and do it. */
X  for(tp= &tty_struct[0]; tp < &tty_struct[NR_TTYS]; tp++) {
X	flags = lock();
X	if (tp->tty_state & IN_FLUSH) {
X		tp->tty_state &= ~IN_FLUSH;
X		restore(flags);
X		do_charint(tp);		/* This tty has some input */
X		flags = lock();
X		}
X	if (tp->tty_state & OUT_FLUSH) {
X		tp->tty_state &= ~OUT_FLUSH;
X		restore(flags);
X		out_chars(tp);		/* This tty needs some output */
X		continue;
X		}
X	restore(flags);
X	}
X}
X
X/*===========================================================================*
X *				do_charint				     *
X *===========================================================================*/
XPRIVATE void do_charint(tp)
Xregister tty_entry *tp;
X{
X/* A character has been typed.  If a character is typed and the tty task is
X * not able to service it immediately, the character is accumulated within
X * the low level tty driver. A call to this routine may thus have to process
X * several characters.
X */
X
X  register unsigned char *count;
X  register char *copy_ptr;
X  int r_val;
X
X  
X/* We have to copy the chars in the device fbuf so we can process them */
X  r_val= lock();			/* prevent races by disabling int's */
X  count = &(tp->tty_fbuf->count);	/* how many chars to transfer */
X  if (*count == 0) {			/* check the count is valid */
X	restore(r_val);
X	return;
X	}
X		/* copy using bcopy(from,to,count) */
X  bcopy(tp->tty_fbuf,&tty_copy_buf, *count);
X  tty_copy_buf.count= *count;		/* copy the no of chars */
X  *count= 0;				/* clear the device fbuf */
X  restore(r_val);			/* re-enable interrupts */
X
X  /* notify the low level routine for flow control reasons */
X  (*tp->tty_devempty)(tp);
X
X  /* Loop on the accumulated characters, processing each in turn. */
X  copy_ptr = tty_copy_buf.b;
X	 /* process the char, queue and echo it */
X  while (tty_copy_buf.count--)
X	if (in_char(tp, *copy_ptr++) && tp->tty_inleft) {
X		/* TTY buffer is full, try transfering to user. */
X		r_val= rd_chars(tp,1);	/* force the reader to accept chars */
X		if (r_val != SUSPEND)	/* reader could be satisfied */
X			tty_reply(REVIVE, (int) tp->tty_incaller
X				, (int) tp->tty_inproc, r_val);
X		tty_copy_buf.count++;	/* try the char again */
X		copy_ptr--;
X	}
X
X	/* See if user can be satisfied. */
X  if (tp->tty_inleft) {
X	r_val= rd_chars(tp,0);		/* don't force reader to take chars */
X	if (r_val != SUSPEND)		/* reader could be satisfied */
X		tty_reply(REVIVE, (int) tp->tty_incaller
X			, (int) tp->tty_inproc, r_val);
X  }
X}
X
X
X/*===========================================================================*
X *				in_char					     *
X *===========================================================================*/
XPRIVATE bool in_char(tp, ch)
Xregister tty_entry *tp;		/* tty on which the char arrived */
Xint ch;				/* code for character that arrived */
X{
X/* A character has just been typed in.  Process, save, and echo it.
X	Returns TRUE if buffers full else FALSE */
X
X  register mode;
X  char c;
X  unsigned col;
X  charpos chp;
X
X  ch &= 0xFF;				/* make sure only 8 bits */
X  mode = tp->tty_mode;		/* speed up access to mode bits */
X
X  do {
X    /* The translate routine returns unsigned. The lower byte is the
X	ascii char, the upper byte (except for the MARKER (top) bit)
X	may be used for the translate routines use.
X	If the top byte == 0 then this is a single char,
X		else it is the start of a sequence.
X	The next call of a sequence is passed the previous result
X	as a parameter. The first call to translate always has the
X	top byte set to 0.
X	If the MARKER is set then this char is to be ignored and
X	if in a sequence then the sequence ends.
X    */
X    if (tp->tty_state & TRANSLATE)	/* Translate the char to ascii */
X	if( (ch = (*tp->tty_devtrans)(ch))&MARKER) break;
X
X    c= ch & 0xFF;			/* We now have an 8-bit ascii char */
X
X	/* From now on the MARKER bit is used to indicate EOF */
X
X
X    /* Processing for COOKED and CBREAK mode contains special checks. */
X    if (!(mode&RAW)) {
X
X	/* 7-bit chars except in raw mode or if an 8 bit terminal */
X	if (!(tp->tty_state&COK8BIT)) c &= 0177;
X
X		/* LCASE processing must come first */
X	if ((mode&LCASE) && c >= 'A' && c <= 'Z') c += 'a'-'A';
X
X	/* Handle erase, kill and escape processing etc. (cooked mode only) */
X	if (!(mode&CBREAK)) {
X
X	/* See if the last char was escaped */
X	    if (tp->tty_state&ESCAPED) {
X
X		/* Previous character was backslash. */
X		tp->tty_state &= ~ESCAPED;	/* turn escaping off */
X
X		/* If necessary store the escape previously skipped over */
X		if (c != tp->tty_erase && c != tp->tty_kill && c != '\\' &&
X		    c != tp->tty_eof && bufnfull(tp->tty_inbuf)) {
X			chp.ch= '\\'; chp.col= tp->tty_column-1;
X			putobj(tp->tty_inbuf, chp);
X			}
X
X	/* Last char wasn't escaped - do erase, kill etc processing ...
X	 * Care must be taken as whether to use echo() or do_cooked()
X	 * as they have different results in the CTLECH case. Where it
X	 * doesn't matter use echo() as it can be considered safer.
X	 */
X	    } else {
X
X		/* ERASE processing (rub out of last character). */
X		if (c == tp->tty_erase) {
X			/* del last char, doing echo if possible */
X			if ((col=chuck(tp)) != 0xFFFF && mode&ECHO) {
X			    if (mode&(CRTBS|CRTERA)) {
X				col= col >>8; /* get column */
X				do {
X				    do_cooked(tp,'\b',ECHO_OUT);
X				    if (mode&CRTERA) {
X					echo(tp, ' ');
X					do_cooked(tp, '\b', ECHO_OUT);
X				    }
X				} while(tp->tty_column > col);
X			    } else if (tp->tty_mode&PRTERA) {
X				echo(tp,'\\');
X				echo(tp,(char)ch);
X				echo(tp,'/');
X			    } else echo(tp,tp->tty_erase);
X			}
X			continue;	/* Don't store a char */
X		} else			/* End of ERASE processing */
X
X		/* KILL processing (remove current line). */
X		if (c == tp->tty_kill) {
X			/* Only do fancy kill if echo on */
X			if (tp->tty_mode&CRTKIL && mode&ECHO) {
X				while((col= chuck(tp)) != 0xFFFF) {
X					col= col >>8;
X					do {
X						do_cooked(tp,'\b',ECHO_OUT);
X						echo(tp,' ');
X						do_cooked(tp,'\b',ECHO_OUT);
X					} while(tp->tty_column > col);
X				}
X			} else {	/* Simple kill */
X				while(chuck(tp) != 0xFFFF);
X				echo(tp, tp->tty_kill);
X				echo (tp, '\n');
X			}
X			continue;	/* Don't store a char */
X		} else			/* End of KILL processing */
X
X		/* ESCAPE processing (backslash). */
X		if (c == '\\') {
X			tp->tty_state |= ESCAPED;
X			echo(tp, c);
X			continue;	/* Don't store the '\' */
X		} else			/* End of ESCAPE processing */
X
X		/* EOF processing.
X		 * It is stored in the text as MARKER, and counts as a
X		 * line feed in terms of knowing whether a full line
X		 * has been typed already.
X		 */
X		if (c == tp->tty_eof)
X			ch |= MARKER;	/* End of EOF processing */
X
X	    }		/* End of Escape processing */
X	}		/* End of Cooked processing */
X
X	/* Both COOKED and CBREAK modes come here; first map CR to LF. */
X	if (c == '\r' && (mode&CRMOD)) c = '\n';
X
X	/* Check for interrupt character. */
X	if (c == tp->tty_intr) {
X		sigchar(tp, SIGINT);
X		continue;		/* Don't store a char */
X	}
X
X	/* Check for quit character. */
X	if (c == tp->tty_quit) {
X		sigchar(tp, SIGQUIT);
X		continue;		/* Don't store a char */
X	}
X
X	/* Check for and process CTRL-Q (terminal start). */
X	if (c == tp->tty_xon
X	    || (tp->tty_state&INHIBITED && !(mode&DECCTQ))) {
X		tp->tty_state &= ~INHIBITED;
X		out_chars(tp);		/* resume output */
X		continue;		/* Don't store a char */
X	}
X
X	/* Check for and process CTRL-S (terminal stop). */
X	if (c == tp->tty_xoff) {
X		tp->tty_state |= INHIBITED;
X		continue;		/* Don't store a char */
X	}
X    }			/* End of Cooked, Cbreak processing */
X
X  /* All 3 modes come here. */
X    if (bufnfull(tp->tty_inbuf)) {	/* discard char if buffer full */
X	if (c == '\n' || (ch&MARKER))	/* count line feeds */
X		tp->tty_lfct++;
X		/* save the character in the input queue */
X	if (ch&MARKER) {
X  		chp.ch= chp.col= 0xFF;
X	  	putobj(tp->tty_inbuf,chp);
X	} else {
X		chp.ch= c; chp.col= tp->tty_column;
X		putobj(tp->tty_inbuf,chp);
X		echo(tp, c);
X	}
X    }
X	
X  /* Continue with macro type keys (translate on) */
X  } while( (ch &= ~MARKER) & 0xFF00 );
X  return buffull(tp->tty_inbuf);
X}
X
X/*===========================================================================*
X *				sigchar					     *
X *===========================================================================*/
XPUBLIC void sigchar(tp, sig)
Xregister tty_entry *tp;		/* pointer to tty_struct */
Xint sig;				/* SIGNAL to send */
X{
X/* Process a signal generated by the tty (key or otherwise) */
X
X	tp->tty_state &= ~INHIBITED;		/* do implied CRTL-Q */
X	bufinit(tp->tty_inbuf);			/* discard input */
X	tp->tty_lfct = 0;
X	(*tp->tty_devclr)(tp);			/* discard output */
X
X	/* Send signal if the process group is valid - (or console KILL all) */
X	/* At the moment tty_pgrp stores a slot num, not a proc group num. */
X	if (tp->tty_pgrp || sig == SIGKILL)
X		cause_sig(tp->tty_pgrp, sig);
X}
X
X
X/*===========================================================================*
X *				echo					     *
X *===========================================================================*/
XPRIVATE void echo(tp, c)
Xregister tty_entry *tp;	/* terminal on which to echo */
Xregister char c;		/* character to echo */
X{
X/* Echo a character on the terminal. */
X
X  if ( (tp->tty_mode & ECHO) == 0) return;	/* if no echoing, don't echo */
X
X  if ((tp->tty_mode&(CTLECH|RAW))==CTLECH) {
X	if (c&128) {
X		echo(tp,'|');
X		c -= 128;
X	}
X	if (c<' ' && c!='\n' && c!='\t' && c!='\r') {
X		echo(tp,'^');
X		c += '@';
X	} else if (c==127) {
X		echo(tp,'^');
X		c= '?';
X	}
X  }
X  do_cooked(tp,(char) c,ECHO_OUT);
X}
X
X
X/*===========================================================================*
X *				chuck					     *
X *===========================================================================*/
XPRIVATE unsigned chuck(tp)
Xregister tty_entry *tp;	/* from which tty should chars be removed */
X{
X/* Delete one character from the input queue.  Used for erase and kill.
X * Return 0xFFFF if there are no more chars to return
X *	else return char in lower byte and its screen column in upper byte.
X */
X
X  int prev;
X  charpos val;
X
X  /* If input queue is empty, don't delete anything. */
X  if (bufempty(tp->tty_inbuf))
X	return 0xFFFF;
X
X  /* Don't delete '\n' or MARKER (eof). */
X  prev = (tp->tty_inbuf.in ? tp->tty_inbuf.in-1 : bufsize(tp->tty_inbuf)-1);
X  val= tp->tty_inbuf.b[prev];
X  if (val.ch == '\n' || (val.ch== 0xFF && val.col== 0xFF))
X	return 0xFFFF;
X
X		/* char erasure was possible */
X  tp->tty_inbuf.in = prev;
X  tp->tty_inbuf.count--;
X  return( (unsigned)val.ch | (((unsigned)val.col)<<8));
X}
X
X
X/*===========================================================================*
X *				do_read					     *
X *===========================================================================*/
XPRIVATE void do_read(tp, m_ptr)
Xregister tty_entry *tp;	/* pointer to tty struct */
Xmessage *m_ptr;			/* pointer to message sent to the task */
X{
X/* A process wants to read from a terminal. */
X
Xphys_bytes phys_add;
Xextern phys_bytes umap();
X
X	  /* Perform Validity checks on the read request */
X
X  /* Must have between 1 and MAXINT chars */
X  if (m_ptr->COUNT & 0x8000 || m_ptr->COUNT == 0) {
X  	tty_reply(TASK_REPLY,m_ptr->m_source,m_ptr->PROC_NR, E_BAD_ADDR);
X  	return;
X  	}
X
X  /* Check the address given to see if it is valid */
X  if ( (phys_add= umap(proc_addr(m_ptr->PROC_NR),D,(vir_bytes) m_ptr->ADDRESS
X  		,(vir_bytes) m_ptr->COUNT)) == 0) {
X  	tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, E_BAD_ADDR);
X	return;
X  }
X
X  /* if someone else is hanging, give up */
X  if (tp->tty_inleft > 0) {
X	tty_reply(TASK_REPLY,m_ptr->m_source,m_ptr->PROC_NR, E_TRY_AGAIN);
X	return;
X  }
X
X	/* Read is valid - Set up the read */
X
X  /* Copy information from the message to the tty struct. */
X  tp->tty_incaller = m_ptr->m_source;
X  tp->tty_inproc = m_ptr->PROC_NR;
X  tp->tty_inleft = m_ptr->COUNT;
X  tp->tty_inoffset = phys_add & OFF_MASK;
X  tp->tty_inseg = (phys_add >> 4) & WORD_MASK;
X  tp->tty_incum = 0;
X
X  /* Flush any chars in the output routine buffers */
X  (*tp->tty_devflush)(tp);
X
X  /* Try to get chars.  This call either gets enough, or gets nothing. */
X  tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, rd_chars(tp,0));
X}
X
X
X/*===========================================================================*
X *				rd_chars				     *
X *===========================================================================*/
XPRIVATE int rd_chars(tp,force)
Xregister tty_entry *tp;	/* pointer to terminal to read from */
Xint force;		/* the user must accept as many chars as he can */
X{
X/* Try to send some data from the tty buffers to the user.
X *	Returns SUSPEND if more chars have to be read to complete the
X *	read request else returns the total number of chars transfered to the
X *	user.
X */
X
X  register buf_ct;
X  unsigned char enough;
X  bool cooked;
X  charpos chp;
X
X  if (bufempty(tp->tty_inbuf))		/* Only put chars if there are some */
X  	return(SUSPEND);
X
X  cooked = !(tp->tty_mode & (RAW|CBREAK));	/* TRUE iff COOKED mode */
X
X  if (!force && !tp->tty_lfct && cooked)	/* Only transfer chars if */
X  	return(SUSPEND);			/* read finishable or forced */
X
X	/* We can try to complete this read request now */
X
X  enough = 0;
X
X  /* What is the maximum chars we can transfer at the moment */
X  buf_ct = MIN(tp->tty_inleft, bufcount(tp->tty_inbuf));
X
X  while(buf_ct--) {
X  		/* Get a char from the buffer */
X	chp = getobj(tp->tty_inbuf);
X	advgetobj(tp->tty_inbuf);
X		/* Check for EOF or lf char */
X	if (chp.ch == '\n' || (chp.ch==0xFF && chp.col==0xFF)) {
X		tp->tty_lfct--;
X			/* Finish looping if not nl in uncooked mode */
X		if (cooked || chp.ch == 0xFF) {
X			enough = chp.ch;
X			buf_ct= 0;
X			}
X	}
X
X		/* Send it if it isn't an EOF */
X	if (enough != 0xFF) {
X		put_byte(tp->tty_inseg,tp->tty_inoffset++,chp.ch);
X		tp->tty_inleft--;
X		tp->tty_incum++;
X	}
X  }
X
X		/* Does that satisfy the read request ? */
X  if (enough || !cooked || tp->tty_inleft==0) {
X  	tp->tty_inleft= 0;		/* No more chars needed */
X  	return tp->tty_incum;
X  	}
X  return(SUSPEND);
X}
X
X/*===========================================================================*
X *				do_write				     *
X *===========================================================================*/
XPRIVATE void do_write(tp, m_ptr)
Xregister tty_entry *tp;	/* pointer to tty struct */
Xmessage *m_ptr;			/* pointer to message sent to the task */
X{
X/* A process wants to write on a terminal. */
X
X  register struct writers *tpw;
X  phys_bytes phys_add;
X  int wr;
X  extern phys_bytes umap();
X
X  /* Check the address given to see if it is valid */
X  if ( (phys_add= umap(proc_addr(m_ptr->PROC_NR),D,(vir_bytes) m_ptr->ADDRESS
X  		,(vir_bytes) m_ptr->COUNT)) == 0) {
X  	tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, E_BAD_ADDR);
X	return;
X  }
X
X  /* Check that we have room for more writers */
X  if ( (wr=tp->tty_writers) >= MAX_WRITERS) {
X  	tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, E_TRY_AGAIN);
X	return;
X  }
X
X  /* Copy message parameters to the tty structure. */
X  tpw= &tp->w[tp->tty_writers++];
X  tpw->otcaller = m_ptr->m_source;
X  tpw->outproc = m_ptr->PROC_NR;
X  tpw->offset = phys_add & OFF_MASK;
X  tpw->seg = (phys_add >> 4) & WORD_MASK;
X  tpw->outleft = m_ptr->COUNT;
X  if (wr == 0) {
X  	tp->tty_state |= WAITING;	/* a new writer always needs a reply */
X  	tp->tty_cum= 0;
X  }
X
X  /* O/S writes are already suspended waiting for reply, so don't
X   *  send message telling them to suspend.
X   */
X  if (tpw->outproc >= LOW_USER)
X	tty_reply(TASK_REPLY,tpw->otcaller,tpw->outproc,SUSPEND);
X  if (wr == 0) out_chars(tp);
X}
X
Xtypedef union {
X	long				l;
X	struct {int i0, i1;}		i;
X	struct {char c0,c1,c2,c3;}	c;
X	} olong;
X
X/*===========================================================================*
X *				do_ioctl				     *
X *===========================================================================*/
XPRIVATE void do_ioctl(tp, m_ptr)
Xregister tty_entry *tp;		/* pointer to tty_struct */
Xmessage *m_ptr;			/* pointer to message sent to task */
X{
X/* Perform IOCTL on this terminal.
X * The V1.3 ioctl was inconsistant in the way it got and put fields,
X * particularly with sg_i/ospeed. The new is consistant.
X * Programs that used the old ioctl will not work as the ioctl calls have
X * different values for TIOCSETP etc. The old calls will return EINVAL.
X * Any programs that set the number of bits in V1.3 will have to be
X * rewritten (not just recompiled) as those fields are being used for other
X * purposes and this function has been shifted to the TIOC(SET/GET)M call.
X */
X
X  olong flags, erki;
X  register r = m_ptr->TTY_REQUEST;
X  int stat = OK;
X
X  if (r&IOC_IN) {
X    flags.l = m_ptr->TTY_FLAGS;
X    erki.l  = m_ptr->TTY_SPEK;
X    switch(r) {
X    	case TIOCSETP:		/* Set erase, kill, and flags. */
X	case TIOCSETN:
X		tp->tty_mode  = flags.i.i0;	/* 16 bit flags at present */
X			/* Take care when setting speed fields */
X		if (erki.c.c3)
X			tp->tty_ospeed = erki.c.c3;
X		if (erki.c.c2)
X			tp->tty_ispeed = erki.c.c2;
X		tp->tty_erase = erki.c.c1;
X		tp->tty_kill  = erki.c.c0;
X			/* Any device dependant stuff */
X		(*tp->tty_devioctl)(tp,m_ptr);
X		break;
X
X	case TIOCSETC:	/* Set intr, quit, xon, xoff, eof (brk not used). */
X		tp->tty_intr = erki.c.c3;
X		tp->tty_quit = erki.c.c2;
X		tp->tty_xon  = erki.c.c1;
X		tp->tty_xoff = erki.c.c0;
X		tp->tty_eof  = flags.c.c1;
X		tp->tty_brk  = flags.c.c0;
X		break;
X
X	case TIOCSETM:	/* Set device dependant flags */
X		tp->tty_ddmod = flags.i.i0;
X			/* Any device dependant stuff */
X		(*tp->tty_devioctl)(tp, m_ptr);
X		break;
X
X	case TIOCSTI:		/* Simulate terminal input */
X		erki.c.c0 = tp->tty_state & TRANSLATE;	/* save trns flag */
X		tp->tty_state &= ~TRANSLATE;		/* turn off inp trns */
X		in_char(tp, flags.c.c0);		/* put char in the Q */
X		tp->tty_state |= erki.c.c0;		/* restore trns flag */
X		break;
X
X	case TIOCMODS:		/* raw device hanles these */
X		(*tp->tty_devioctl)(tp, m_ptr);
X		break;
X
X	default:
X		stat = EINVAL;
X		break;
X    }
X  }
X
X  if (r&IOC_OUT) {
X    flags.l = erki.l = 0;
X    switch(r) {
X	case TIOCGETP:		/* Get erase, kill, and flags. */
X		flags.i.i0 = tp->tty_mode;
X		erki.c.c3  = tp->tty_ospeed;
X		erki.c.c2  = tp->tty_ispeed;
X		erki.c.c1  = tp->tty_erase;
X		erki.c.c0  = tp->tty_kill;
X		break;
X
X	case TIOCGETC:		/* Get intr, quit, xon, xoff, eof. */
X		erki.c.c3 = tp->tty_intr;
X		erki.c.c2 = tp->tty_quit;
X		erki.c.c1 = tp->tty_xon;
X		erki.c.c0 = tp->tty_xoff;
X		flags.c.c1 = tp->tty_eof;
X		flags.c.c0 = tp->tty_brk;
X		break;
X
X	case TIOCGETM:		/* Set device dependant flags */
X		flags.i.i0 = tp->tty_ddmod;
X		break;
X
X	case FIONREAD:		/* Returns number of chars to read */
X		flags.i.i0 = (unsigned) tp->tty_inbuf.count;
X		break;
X
X	case TIOCMODG:		/* raw device handles these */
X		(*tp->tty_devioctl)(tp, &m_ptr);
X		flags.l = m_ptr->TTY_FLAGS;
X		erki.l = m_ptr->TTY_SPEK;
X		break;
X
X	default:
X		/* Check for device dependant handling */
X		stat = EINVAL;
X		break;
X    }
X  }
X
X  if ((r&(IOC_IN|IOC_OUT)) == IOC_VOID)
X    switch (r) {
X	case TIOCSTOP:		/* stop output, like ^S */
X		tp->tty_state |= INHIBITED;
X		break;
X
X	case TIOCSTART:		/* start output, like ^Q */
X		tp->tty_state &= ~INHIBITED;
X		out_chars(tp);			/* restart output */
X		break;
X
X	case TIOCSBRK:		/* the raw devices handle these */
X	case TIOCCBRK:
X	case TIOCSDTR:
X	case TIOCCDTR:
X	case TIOCSMLB:
X	case TIOCCMLB:
X		(*tp->tty_devioctl)(tp, m_ptr);
X		break;
X
X	default:
X		stat = EINVAL;
X		break;
X    }
X
X  /* Send a reply */
X  tty_reply(TASK_REPLY,m_ptr->m_source, m_ptr->PROC_NR, stat, flags.l, erki.l);
X}
X
X
X/*===========================================================================*
X *				do_cancel				     *
X *===========================================================================*/
XPRIVATE void do_cancel(tp, m_ptr)
Xregister tty_entry *tp;	/* pointer to tty_struct */
Xmessage *m_ptr;			/* pointer to message sent to task */
X{
X/* A signal has been sent to a process that is hanging trying to read or write.
X * The pending read or write must be finished off immediately.
X */
X	register writer,reader;
X
X  /* Find out if it is trying to write */
X
X  for(writer=0; writertty_writers ; writer++)
X	if (m_ptr->PROC_NR == tp->w[writer].outproc) break;
X
X  /* Find out if it is the current reader */
X  reader= tp->tty_inproc==m_ptr->PROC_NR && tp->tty_inleft != 0;
X
X  /* First check to see if the process is a reader or writer with an unfinished
X   * request.  If it is not, don't reply (to avoid race conditions).
X   */
X  if ( !reader && writer>=tp->tty_writers ) return;
X
X	/* If it is a reader then ... */
X  if (reader) {
X  	bufinit(tp->tty_inbuf);		/* discard all input */
X  	tp->tty_inleft= 0;
X  	}
X
X	/* If it the current writer then discard output & complete IO */
X  if (writer == 0) {
X  	(*tp->tty_devclr)(tp);			/* clear the output stream */
X	tp->w[0].outleft = 0;			/* simulate end of write */
X	tp->tty_state &= ~WAITING;		/*   but don't reply. */
X	tp->tty_state &= ~INHIBITED;		/* do an implied ^Q */
X			/* now send real reply */
X	tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR);
X	out_chars(tp);				/* restart output */
X	return;
X  }
X	/* If a writer, but not current then just dequeue it */
X  if (writer < tp->tty_writers)
X	for(tp->tty_writers-- ; writer < tp->tty_writers ; writer++)
X		tp->w[writer]=tp->w[writer+1];
X  		
X  			/* send EINTR responce to writer */
X  tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR);
X}
X
X
X/*===========================================================================*
X *				out_chars				     *
X *===========================================================================*/
XPRIVATE void out_chars(tp)
Xregister tty_entry *tp;
X{
X/* Get chars from a process and insert into the cooked stream.
X *  Also arrange for next writer when this one completes.
X */
X	register i;
X
X		/* Safety line: check that there are writers */
X  if (tp->tty_writers == 0) return;
X		/* While there are writers remaining ... */
X  while(TRUE) {
X  	/* Try to send all the chars if the terminal is not inhibited */
X	if (!(tp->tty_state&INHIBITED)) while(tp->w[0].outleft) {
X		tp->w[0].outleft--;
X		tp->tty_cum++;
X		/* Stop when raw devices have enough chars - ^S processing
X		 * is in the low level output routines so the stop occurs
X		 * immediately and not when its buffer finally empties.
X		 */
X		if (do_cooked(tp
X		   ,(char) get_byte(tp->w[0].seg,tp->w[0].offset++),WRITE_OUT))
X			break;
X	}
X
X		/* Return if there is nothing more to do at present */
X	if (tp->w[0].outleft) return;
X
X		/* Write is finished. Flush output buffers. */
X	(*tp->tty_devflush)(tp);
X
X		/* Wake up this process */
X	if (tp->tty_state & WAITING)		/* send reply if needed */
X	    tty_reply(REVIVE,tp->w[0].otcaller,tp->w[0].outproc,tp->tty_cum);
X
X		/* Start next writer (if any) */
X	for(i=1; i < tp->tty_writers ; i++)
X		tp->w[i-1]= tp->w[i];
X	if (--tp->tty_writers == 0) {
X		tp->tty_state &= ~WAITING;		/* no more writers */
X		return;
X	}
X	tp->tty_state |= WAITING;		/* new writer wants reply */
X    }
X}
X
X/*===========================================================================*
X *				do_cooked				     *
X *===========================================================================*/
XPRIVATE bool do_cooked(tp,c,prio)
Xregister tty_entry *tp;
Xchar c;
X{
X/* Process a char according to COOKED flags etc.
X * 	prio = ECHO_OUT for non stopable stream & WRITE_OUT for normal IO.
X * Returns status as given by the low level routine.
X */
Xregister val;
X
X  /* Maintain some estimation of the current column (for XTAB,CTLECH etc).
X   * Do XTAB, CRMOD, LCASE output processing (if not in RAW mode).
X   * The current column must be estimated even in RAW mode as the console
X   * uses this estimation to calculate real locations.
X   * Set a special flag so the console can tell when to do backspace wrap.
X   */
X  if (c < ' ')
X	switch(c) {
X 	case '\t':		/* TAB - do XTAB processing */
X		if ((tp->tty_mode&(XTABS|RAW))==XTABS) {
X			do {
X				val= do_cooked(tp,' ',prio);
X			} while (tp->tty_column & (TAB_SIZE-1));
X			return val;
X		}
X			/* Assume the terminal does tabs of TAB_SIZE */
X		tp->tty_column = (tp->tty_column + TAB_SIZE) & ~(TAB_SIZE-1);
X		break;
X	case '\n':		/* LINEFEED - do CRMOD processing */
X		if ((tp->tty_mode&(CRMOD|RAW))==CRMOD)
X			do_cooked(tp,'\r',prio);
X		break;
X	case '\r':		/* CR - set column = 0 */
X		tp->tty_column = 0;
X		break;
X	case '\b':		/* BS - decr column if valid, check BS wrap */
X		if (tp->tty_column) tp->tty_column--;
X		else tp->tty_state |= BS_WRAP;
X		break;
X	}
X  else {			/* Normal char (incr column) */
X	tp->tty_column++;
X				/* LCASE processing */
X	if ((tp->tty_mode&(LCASE|RAW))==LCASE && c >= 'a' && c <= 'z')
X		c -= 'a' - 'A';
X  }
X
X	/* Now do raw device output */
X  return (*tp->tty_devraw)(tp,c,prio);
X}
X
X/*===========================================================================*
X *				tty_reply				     *
X *===========================================================================*/
XPRIVATE void tty_reply(code, replyee, proc_nr, status, extra, other)
Xint code;			/* TASK_REPLY or REVIVE */
Xint replyee;			/* destination address for the reply */
Xint proc_nr;			/* to whom should the reply go? */
Xint status;			/* reply code */
Xlong extra;			/* extra value */
Xlong other;			/* used for IOCTL replies */
X{
X/* Send a reply to a process that wanted to read or write data. */
X
X  message tty_mess;
X
X  tty_mess.m_type = code;
X  tty_mess.REP_PROC_NR = proc_nr;
X  tty_mess.REP_STATUS = status;
X  tty_mess.TTY_FLAGS = extra;	/* used by IOCTL for flags (mode) */
X  tty_mess.TTY_SPEK = other;	/* used by IOCTL for erase and kill chars */
X  send(replyee, &tty_mess);
X}
X
X
X/*===========================================================================*
X *				do_nothing				     *
X *===========================================================================*/
XPUBLIC void do_nothing()
X{
X}
X
X
X/*===========================================================================*
X *				putc					     *
X *===========================================================================*/
XPUBLIC void putc(c)
Xchar c;				/* character to print */
X{
X/* This procedure is used by the version of printf() that is linked with
X * the kernel itself.  The one in the library sends a message to FS, which is
X * not what is needed for printing within the kernel.  This version just queues
X * the character and starts the output.
X */
X
X  do_cooked(&tty_struct[0], c, WRITE_OUT);
X}
X
X/*===========================================================================*
X *				do_setpgrp				     *
X *===========================================================================*/
XPRIVATE void do_setpgrp(tp, m_ptr)
Xregister tty_entry *tp; /* pointer to tty struct */
Xmessage *m_ptr;			/* pointer to message sent to task */
X{
X/* A control process group has changed.
X * At the moment this is really a slot number not a process number */
X
X   tp->tty_pgrp = m_ptr->TTY_PGRP;
X   tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, OK);
X}
*-*-END-of-tty1.c-*-*
exit