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