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