marks@elec.uq.oz (Mark Schulz) (09/22/88)
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 <minix/com.h> X#include <sgtty.h> 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 <signal.h> X#include <sgtty.h> X#include <pwd.h> X#include <sys/stat.h> 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 <sgtty.h> 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 <signal.h> X#include <sgtty.h> 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 <sgtty.h> 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; writer<tp->tty_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
marks@elec.uq.oz (Mark Schulz) (09/22/88)
echo x - tty2.c sed 's/^X//' >tty2.c <<'*-*-END-of-tty2.c-*-*' X/* tty2.c - device dependant code for console tty driver (IBM-PC) X * X * Written by: A.Hannam (Feb 1988) X * X * Modifications: X * 28/8/88 - Modified for MINIX V1.3 on IBM-PC X * Added scan-code translation & macro sequences X * 5/9/88 - Added a significant portion of ANSI sequences X * Fixed EGA code X */ X X#include "../h/const.h" X#include "../h/sgtty.h" X#include "../h/type.h" X#include "../h/signal.h" X#include "../h/com.h" X#include "const.h" X X#ifdef HIGH_LEVEL X#undef HIGH_LEVEL X#endif X X#include "tty.h" X X/* Now begins the code and data for the device-dependent tty drivers. */ X X/*****************************************************************************/ X/******************************* CONSOLE DRIVER ******************************/ X/*****************************************************************************/ X XPUBLIC int scan_code; /* scancode for '=' to test if olivetti */ XPUBLIC int color; /* 1 if console is color, 0 if it is mono */ XPUBLIC int vid_mask; /* 037777 for color (16K) or 07777 for mono */ XPUBLIC int vid_retrace; /* how many words to display per burst */ XPUBLIC unsigned vid_base; /* base of video ram (0xB000 or 0xB800) */ X XPUBLIC void int_con(), init_con(); X Xextern int pc_at; Xextern int lock(); Xextern void do_nothing(), sigchar(), reboot(), wreboot(), restore() X , vid_write(), vid_fill(), vid_fmove(), vid_bmove(); X XPRIVATE bool rout_con(); XPRIVATE unsigned trans_con(); XPRIVATE void ofl_con(), scroll_screen(), move_to(), do_escape() X , set_6845(), beep_on(), dset_con(), parse_escape(), set_leds() X , beep_off(), check_reboot(); X X X/* Definitions used by the console driver. */ X#define COLOR_BASE 0xB800 /* video ram paragraph for color display */ X#define MONO_BASE 0xB000 /* video ram address for mono display */ X#define C_VID_MASK 0x3FFF /* mask for 16K video RAM */ X#define M_VID_MASK 0x0FFF /* mask for 4K video RAM */ X#define C_RETRACE 0x0180 /* maximum words to display at once (cga) */ X#define M_RETRACE 0x7FFF /* maximum words to display at once (mono) */ X#define BEEP_FREQ 0x0533 /* value to put into timer to set beep freq */ X#define B_TIME 3 /* length of CTRL-G beep in ticks */ X#define BLANK 0x0700 /* default blank color */ X#define GO_FORWARD 0 /* scroll forward */ X#define GO_BACKWARD 1 /* scroll backward */ X#define TIMER2 0x42 /* I/O port for timer channel 2 */ X#define TIMER3 0x43 /* I/O port for timer channel 3 */ X#define KEYBD 0x60 /* I/O port for keyboard data */ X#define PORT_B 0x61 /* I/O port for 8255 port B */ X#define KBIT 0x80 /* bit used to ack characters to keyboard */ X#define LED_CODE 0xED /* command to keyboard to set LEDs */ X#define LED_DELAY 0x80 /* device dependent delay needed */ X#define MAX_ESC_PARMS 2 /* no. of escape sequence parameters allowed */ X#define FAST_BAUD B115200 /* console looks like a term at this speed */ X#define BEEPING DD_FLAG1 /* flag in tty struct when console beeping */ X#define REBOOT DD_FLAG2 /* flag in tty struct when about to reboot */ X X#define LINE_WIDTH 80 /* # characters on a line */ X#define SCR_LINES 25 /* # lines on the screen */ X X#define ESC 033 /* ESC for use in ansi term sequences */ X#define XOFF_CHAR 0x13 /* xoff char that these routines can decode */ X X#define OLIVETTI_EQUAL 12 /* scan code for '=' on olivetti (13 on IBM) */ X#define CTRL_S 31 /* scan code for letter S (for CRTL-S) */ X#define DEL_CODE 83 /* code for DEL for in CTRL-ALT-DEL reboot */ X#define FUNC_KEY 58 /* codes above this belong to function keys */ X#define F10 68 /* scan code for function key F10 */ X#define TOP_ROW 14 /* codes below this are shifted if CTRL */ X#define NUM_PAD 70 /* codes above this belong to the number pad */ X#define NUM_PGDN 81 /* code for pgdn key on number pad */ X X/* Constants relating to the video RAM and 6845. */ X#define M_6845 0x3B0 /* port for 6845 mono */ X#define C_6845 0x3D0 /* port for 6845 color */ X#define EGA 0x3C0 /* port for EGA card */ X#define INDEX 4 /* 6845's index register */ X#define DATA 5 /* 6845's data register */ X#define CUR_SIZE 10 /* 6845's cursor size register */ X#define VID_ORG 12 /* 6845's origin register */ X#define CURSOR 14 /* 6845's cursor register */ X X/* Global variables used by the console driver. */ XPRIVATE message c_mess; /* message used for console input chars */ X XPRIVATE bool shift1, shift2; /* keep track of key statii */ XPRIVATE bool capslock, numlock; XPRIVATE bool control, alt; X XPRIVATE char c_estate; /* 0=normal, 1=ESC, 2=ESC[ etc */ XPRIVATE int c_enum[MAX_ESC_PARMS]; /* list of escape parameters */ XPRIVATE int * c_p_enum; /* pointer to current escape param */ XPRIVATE int c_row; /* the current row for the console */ XPRIVATE int c_attr; /* the current character atrribute */ XPRIVATE unsigned c_blank; /* blanking character and attribute */ XPRIVATE int c_org; /* origin of the screen in video ram */ XPRIVATE int c_vid; /* current cursor pos in video ram */ XPRIVATE int c_port; /* I/O port for accessing 6845 */ XPRIVATE char * c_key_sh; /* translation tables - shifted keys */ XPRIVATE char * c_key_ush; /* - unshifted keys */ XPRIVATE char c_s_col; /* saved cursor column */ XPRIVATE char c_s_row; /* saved cursor row */ X X/* Scan codes to ASCII for unshifted keys */ XPRIVATE char unsh[] = { X 0,ESC,'1','2','3','4','5','6','7','8','9','0','-','=','\b','\t', X 'q','w','e','r','t','y','u','i','o','p','[',']',015,0202,'a','s', X 'd','f','g','h','j','k','l',';',047,0140,0200,0134,'z','x','c','v', X 'b','n','m',',','.','/',0201,'*',0203,' ',0204,0241,0242,0243,0244,0245, X 0246,0247,0250,0251,0252,0205,0210,0267,0270,0271,0211,0264,0265,0266,0214, X 0261,0262,0263,0260,0177 X}; X X/* Scan codes to ASCII for shifted keys */ XPRIVATE char sh[] = { X 0,ESC,'!','@','#','$','%','^','&','*','(',')','_','+','\b','\t', X 'Q','W','E','R','T','Y','U','I','O','P','{','}',015,0202,'A','S', X 'D','F','G','H','J','K','L',':',042,'~',0200,'|','Z','X','C','V', X 'B','N','M','<','>','?',0201,'*',0203,' ',0204,0221,0222,0223,0224,0225, X 0226,0227,0230,0231,0232,0204,0213,'7','8','9','-','4','5','6','+','1', X '2','3','0','.' X}; X X/* Scan codes to ASCII for Olivetti M24 for unshifted keys. */ XPRIVATE char unm24[] = { X 0,ESC,'1','2','3','4','5','6','7','8','9','0','-','^','\b','\t', X 'q','w','e','r','t','y','u','i','o','p','@','[','\r',0202,'a','s', X 'd','f','g','h','j','k','l',';',':',']',0200,'\\','z','x','c','v', X 'b','n','m',',','.','/',0201,'*',0203,' ',0204,0241,0242,0243,0244,0245, X 0246,0247,0250,0251,0252,023,0210,0267,0270,0271,0211,0264,0265,0266,0214,0261, X 0262,0263,0207,0177,0271,014,0212,'\r',0264,0262,0266,0270,032,0213,0274,'/', X 0253,0254,0255,0256,0257,0215,0216,0217 X}; X X/* Scan codes to ASCII for Olivetti M24 for shifted keys. */ XPRIVATE char m24[] = { X 0,ESC,'!','"','#','$','%','&',047,'(',')','_','=','~','\b','\t', X 'Q','W','E','R' ,'T','Y','U','I','O','P',0140,'{','\r',0202,'A','S', X 'D','F','G','H','J','K','L','+','*','}',0200,'|','Z','X','C','V', X 'B','N','M','<','>','?',0201,'*',0203,' ',0204,0221,0222,0223,0224,0225, X 0226,0227,0230,0231,0232,0270,023,'7','8','9',0211,'4','5','6',0214,'1', X '2','3','0','.',' ',014,0272,'\r','\b','\n','\f',036,032,0273,' ','/', X 0233,0234,0235,0236,0237,0275,0276,0277 X}; X XPRIVATE char *fn_macros[4][10]= { X { /* no shift, control or alt - ANSI Fn Keys */ X "\033OS","\033OT","\033OU","\033OV","\033OW", X "\033OP","\033OQ","\033OR","\033OX","\033OY" X },{ /* shift, no control or alt - (empty) */ X 0,0,0,0,0,0,0,0,0,0 X },{ /* control, no shift or alt - (empty) */ X 0,0,0,0,0,0,0,0,0,0 X },{ /* control & shift, no alt - (empty) */ X 0,0,0,0,0,0,0,0,0,0 X}}; X XPRIVATE char *fn_a_macros[2][10]= { X { /* alt, no shift or control - Useful Strings */ X "ls -al ","ls -al\n","mount /dev/","umount /dev/", X "dosdir -l ","dosdir -l 1\n","doswrite -a 1 ","doswrite 1 ", X "dosread -a 1 ","dosread 1 " X },{ /* alt & shift, no control - Useful Strings */ X 0,0,0,0,0,0,0,0,0,"\nexit\n" X}}; X XPRIVATE char *arrow_macros[]= { X "[H","[A","[V","[S","[D","[G","[C","[T","[Y","[B","[U" X }; X XPRIVATE charfbuf con_inbuf; X X#define CONS_RAM_WORDS 256 XPRIVATE int cons_ramqueue[CONS_RAM_WORDS]; XPRIVATE int cons_rwords; X X/*===========================================================================* X * init_con * X *===========================================================================*/ XPUBLIC void init_con(tp) Xregister tty_entry *tp; X{ X X c_mess.TTY_LINE= CON1; /* The interrupt message */ X c_mess.m_type= TTY_CHAR_INT; /* Could have been TTY_O_DONE */ X X /* Tell the EGA card, if any, to simulate a 16K CGA card. */ X port_out(EGA + INDEX, 4); /* register select */ X port_out(EGA + DATA, 1); /* no extended memory to be used */ X X X tp->tty_devraw= rout_con; X tp->tty_devclr= ofl_con; X tp->tty_devflush= ofl_con; X tp->tty_devioctl= dset_con; X tp->tty_devtrans= trans_con; X tp->tty_devempty= check_reboot; X X /* console requires translation and always gives 8 bits */ X tp->tty_state |= TRANSLATE|COK8BIT; X tp->tty_ddmod |= CON_COOK8|CON_WRAP|D_CONSOLE; X tp->tty_ispeed= tp->tty_ospeed = FAST_BAUD; X X tp->tty_fbuf = &con_inbuf; X fbufinit(con_inbuf); X X /* Set parameters based on this particular machine */ X if (color&1) { /* What mode is the console in ? */ X vid_base = COLOR_BASE; X vid_mask = C_VID_MASK; X c_port = C_6845; X vid_retrace = C_RETRACE; X } else { X vid_base = MONO_BASE; X vid_mask = M_VID_MASK; X c_port = M_6845; X vid_retrace = M_RETRACE; X } X if (scan_code == OLIVETTI_EQUAL) { X c_key_sh= m24; X c_key_ush= unm24; X } else { X c_key_sh= sh; X c_key_ush = unsh; X } X X c_blank = c_attr = BLANK; /* attribute byte for screen */ X cons_rwords = 0; /* initialize buffer */ X c_estate = 0; /* ESC state is 0 */ X c_s_col = c_s_row = 0; /* The saved cursor pos is home */ X set_6845(CUR_SIZE, 23); /* set cursor shape (old = 31) */ X set_6845(VID_ORG, c_org = 0); /* use page 0 of video ram */ X move_to(0,SCR_LINES-1); /* move cursor to bottom left corner */ X} X X X/*===========================================================================* X * int_con * X *===========================================================================*/ XPUBLIC void int_con() X{ X/* A keyboard interrupt has occurred. Process it. (Interrupts are off) */ X X int val, c; X X /* Fetch the character from the keyboard hardware and acknowledge it. */ X port_in(KEYBD, &c); /* get the scan code for the key struck */ X port_in(PORT_B, &val); /* strobe the keyboard to ack the char */ X port_out(PORT_B, val | KBIT); /* strobe the bit high */ X port_out(PORT_B, val); /* now strobe it low */ X X /* If Keyboard Locked then discard char */ X if (tty_struct[CON1].tty_ddmod&CON_LOCK) { X port_out(INT_CTL, ENABLE); X return; X } X X /* The IBM keyboard interrupts twice per key, once when depressed, once when X * released. Filter out the latter, ignoring all but the shift-type keys. X * The shift-type keys, 29, 42, 54, 56, 58, and 69 must be processed normally. X */ X if (c > 0200) X switch(c) { X case 29 + 0200: case 42 + 0200: X case 54 + 0200: case 56 + 0200: X case 58 + 0200: case 69 + 0200: X break; X default: /* re-enable interrupts */ X port_out(INT_CTL, ENABLE); X return; /* don't call tty_task() */ X } X X /* Check for CTRL-ALT-DEL, and if found set a flag. */ X if (control && alt && c == DEL_CODE) X tty_struct[CON1].tty_state |= REBOOT; X X/* Check to see if character is CTRL-S, to stop output. Setting xoff X * to anything other than CTRL-S will not be detected here, but will X * be detected later, in the driver. A general routine to detect any X * xoff character here would be complicated since we only have the X * scan code here, not the ASCII character. This is just done to X * improve responce. X */ X if ( !(tty_struct[CON1].tty_mode & RAW) X && tty_struct[CON1].tty_xoff == XOFF_CHAR X && control && c == CTRL_S ) { X tty_struct[CON1].tty_state |= INHIBITED; X port_out(INT_CTL, ENABLE); X return; X } X X X /* Store the character in memory so the task can get at it later. */ X if ( bufnfull(con_inbuf) ) { X putfobj(con_inbuf,c); /* Enough room so store the char */ X } X X /* Inform other routines that we have chars available */ X tty_struct[CON1].tty_state |= IN_FLUSH; X flush_flag |= IN_FLUSH; X X /* Build and send the int message to tty task only if absolutly necessary */ X if (bufcount(con_inbuf) >= CHAR_BUF_THRESHOLD) { X interrupt(TTY, &c_mess); X } X X port_out(INT_CTL, ENABLE); /* re-enable 8259A controller */ X} X X/*===========================================================================* X * dset_con * X *===========================================================================*/ XPRIVATE void dset_con(tp, m) Xregister tty_entry *tp; Xregister message *m; X{ X switch (m->TTY_REQUEST) { X case TIOCSETN: X case TIOCSETP: X /* Console baud rate is always fast */ X tp->tty_ispeed= tp->tty_ospeed= FAST_BAUD; X break; X X case TIOCSETM: X /* Ensure modes are valid - if something looks X * suspicious then don't allow keyboard lock. X */ X if (!(tp->tty_ddmod & D_CONSOLE)) X tp->tty_ddmod &= ~CON_LOCK; X tp->tty_ddmod &= (CON_COOK8|CON_LOCK|CON_WRAP|CON_INSERT); X tp->tty_ddmod |= D_CONSOLE; X /* Process the device dependant modes */ X if (tp->tty_ddmod & CON_COOK8) X tp->tty_state |= COK8BIT; X else X tp->tty_state &= ~COK8BIT; X break; X X case TIOCMODG: X /* This looks like an operating terminal */ X m->TTY_FLAGS = TIOCM_LE|TIOCM_DTR|TIOCM_RTS|TIOCM_CTS X |TIOCM_CAR|TIOCM_DSR; X m->TTY_SPEK = 0; X break; X X /* Ignore all other ioctl calls */ X } X} X X X/*===========================================================================* X * rout_con * X *===========================================================================*/ XPRIVATE bool rout_con(tp, c, prio) Xregister tty_entry *tp; /* pointer to tty struct */ Xchar c; /* character to be output */ X{ X/* Output a character on the console. Priority order is ... X * Handle control codes, X * Handle escape sequences X * Handle normal chars X * X * This allows control code processing to occur without disturbing X * escape sequences. X */ X X if (c < ' ') switch(c) { /* Handle control codes */ X X /* Cancel a escape sequence and beep */ X case 0x18: /* CAN & SUB */ X case 0x1A: X if (!c_estate) break; X c_estate = 0; X X /* ring the bell */ X case 007: /* BEL */ X ofl_con(); X beep_on(BEEP_FREQ,B_TIME); X break; X X /* BackSpace - with EOL wrap - (left) */ X case '\b': /* BS */ X if (tp->tty_state & BS_WRAP) { X tp->tty_state &= ~BS_WRAP; X if (c_row != 0 && tp->tty_ddmod&CON_WRAP) { X c_row--; X tp->tty_column= LINE_WIDTH - 1; X } X } X move_to(tp->tty_column, c_row); X break; X X /* Tab Handling - don't bother clearing space */ X case '\t': /* HT */ X move_to(tp->tty_column, c_row); X break; X X /* line feed (down) */ X case '\n': /* NL */ X if (c_row >= SCR_LINES-1) X scroll_screen(GO_FORWARD); X else X c_row++; X move_to(tp->tty_column, c_row); X break; X X /* vertical tab - (up) */ X case 0x0B: /* VT */ X c_row--; X move_to(tp->tty_column, c_row); X break; X X /* form feed - (right) */ X case 0x0C: /* FF */ X move_to(tp->tty_column+1, c_row); X break; X X /* carriage return */ X case '\r': /* CR */ X move_to(0, c_row); X break; X X /* ESC - start of an escape sequence */ X case ESC: /* ESC */ X c_estate = 0; X parse_escape(c); X break; X X /* All other control codes are ignored */ X X } else if (c_estate != 0) { /* Handle Escape Sequences */ X parse_escape(c); X } else { /* Normal Chars */ X /* printable chars are stored in outqueue */ X /* They are being used as words (not bytes) */ X if (cons_rwords >= CONS_RAM_WORDS) ofl_con(); X X if (tp->tty_column < LINE_WIDTH) X cons_ramqueue[cons_rwords++] = c_attr | (unsigned)c; X else if (tp->tty_ddmod&CON_WRAP) { /* Long Lines */ X cons_ramqueue[cons_rwords++] = c_attr | (unsigned)c; X move_to(0, c_row); /* Implied \r */ X rout_con(tp,'\n',prio); /* Implied \n */ X } X } X X /* Handle ^S processing and ECHO_OUT priority */ X if (tp->tty_state&INHIBITED || prio==ECHO_OUT) ofl_con(); X return (tp->tty_state&INHIBITED); X} X X/*===========================================================================* X * scroll_screen * X *===========================================================================*/ XPRIVATE void scroll_screen(dir) Xint dir; /* GO_FORWARD or GO_BACKWARD */ X{ X register offset; X X /* Hardware Scrolling. It is up to the video routines to ensure the screen is X * written in such a way that scrolling over the ram boundary works on X * both CGA,MONO (which support ram wrap) and EGA (which may not support X * ram wrap). X */ X ofl_con(); /* Flush the output stream */ X if (dir == GO_FORWARD) { /* Calculate the new video origin */ X c_org += 2 * LINE_WIDTH; X c_org &= vid_mask; X offset = c_org + 2*(SCR_LINES-1)*LINE_WIDTH; X } else { X c_org -= 2 * LINE_WIDTH; X c_org &= vid_mask; X offset = c_org; X } X X /* Blank the new line at top or bottom. */ X vid_fill(c_blank, offset, LINE_WIDTH); X set_6845(VID_ORG, c_org >> 1); /* 6845 thinks in words */ X} X X/*===========================================================================* X * ofl_con * X *===========================================================================*/ XPRIVATE void ofl_con() X{ X/* Have the characters in 'outqueue' transferred to the screen. */ Xregister unsigned i; X X if (cons_rwords == 0) return; X X /* Do insert mode processing */ X if (tty_struct[CON1].tty_ddmod&CON_INSERT) { X i = c_org + c_row*2*LINE_WIDTH + (2*LINE_WIDTH-2); /* end of line */ X vid_bmove(i - cons_rwords*2, i X , LINE_WIDTH - MIN(tty_struct[CON1].tty_column,LINE_WIDTH)); X } X X /* Write the chars to the screen */ X vid_write(cons_ramqueue, c_vid, cons_rwords); X X /* Update the video parameters and cursor. */ X c_vid += 2*cons_rwords; X set_6845(CURSOR, c_vid >> 1); /* cursor counts in words */ X cons_rwords = 0; X} X X X/*===========================================================================* X * move_to * X *===========================================================================*/ XPRIVATE void move_to(x, y) Xint x; /* column (0 <= x <= 79) */ Xint y; /* c_row (0 <= y <= 24, 0 at top) */ X{ X/* Move the cursor to (x, y). */ X X ofl_con(); /* flush any pending characters */ X x = between(0, x, LINE_WIDTH-1); /* ensure x & y are valid */ X y = between(0, y, SCR_LINES -1); X tty_struct[CON1].tty_column = x; /* set x co-ordinate */ X c_row = y; /* set y co-ordinate */ X c_vid = c_org + y*2*LINE_WIDTH + 2*x; X set_6845(CURSOR, c_vid >> 1); /* cursor counts in words */ X} X X/*===========================================================================* X * set_6845 * X *===========================================================================*/ XPRIVATE void set_6845(reg, val) Xregister reg; /* which register pair to set */ Xregister val; /* 16-bit value to set it to */ X{ X/* Set a register pair inside the 6845. X * Registers 10-11 control the format of the cursor (how high it is, etc). X * Registers 12-13 tell the 6845 where in video ram to start (in WORDS) X * Registers 14-15 tell the 6845 where to put the cursor (in WORDS) X * X * Note that registers 12-15 work in words, i.e. 0x0000 is the top left X * character, but 0x0001 (not 0x0002) is the next character. This addressing X * is different from the way the 8088 addresses the video ram, where 0x0002 X * is the address of the next character. X */ X port_out(c_port + INDEX, reg); /* set the index register */ X port_out(c_port + DATA, (val>>8) & BYTE); /* output high byte */ X port_out(c_port + INDEX, reg + 1); /* again */ X port_out(c_port + DATA, val&BYTE); /* output low byte */ X} X X X/*===========================================================================* X * beep_on * X *===========================================================================*/ XPRIVATE void beep_on(f,d) Xregister f; /* this value determines beep frequency */ Xregister d; /* this value is the time to sound in ticks */ X{ X/* Making a beeping sound on the speaker (output for CRTL-G). This routine X * works by turning on the bits in port B of the 8255 chip that drive the X * speaker and sending a clock message to time the beep. X */ X int x, s; X message m; X X if (tty_struct[CON1].tty_state & BEEPING) return; X s = lock(); /* disable interrupts */ X port_out(TIMER3,0xB6); /* set up timer channel 2 mode */ X port_out(TIMER2, f&BYTE); /* load low-order bits of frequency in timer */ X port_out(TIMER2,(f>>8)&BYTE); /* now high-order bits of frequency in timer */ X port_in(PORT_B,&x); /* acquire status of port B */ X port_out(PORT_B, x|3); /* turn bits 0 and 1 on to beep */ X tty_struct[CON1].tty_state |= BEEPING; X restore(s); /* re-enable interrupts */ X X m.m_type = SET_ALARM; /* set up the duration */ X m.CLOCK_PROC_NR = TTY; X m.DELTA_TICKS = d; /* beep for d ticks */ X m.FUNC_TO_CALL = (int (*)()) beep_off; /* func to switch off beep */ X sendrec(CLOCK, &m); X} X X/*===========================================================================* X * beep_off * X *===========================================================================*/ XPRIVATE void beep_off() X{ X/* Turn off the beep on the speaker (output for CRTL-G). This routine X * is called only by the CLOCK task. X */ Xint s,x; X X s = lock(); /* disable interrupts */ X port_in(PORT_B, &x); /* get status of port B */ X port_out(PORT_B, x & 0xFFFC); /* turn bits 0 and 1 off to stop beep */ X tty_struct[CON1].tty_state &= ~BEEPING; X restore(s); /* restore interrupts */ X} X X X/*===========================================================================* X * func_key * X *===========================================================================*/ XPRIVATE func_key(ch) Xchar ch; /* scan code for a function key */ X{ X/* This procedure traps function keys for debugging purposes. When MINIX is X * fully debugged, it should be removed. X */ X X switch (ch) { X X case FUNC_KEY+1: /* print process table */ X p_dmp(); X break; X X case FUNC_KEY+2: /* print memory map */ X map_dmp(); X break; X X case FUNC_KEY+3: /* Switch between EGA (with no */ X color ^= 2; /* retrace checking) and CGA/MONO. */ X printf("\n\rVideo Card = %s\n\r" X ,(color&2 ? "EGA": (color&1 ? "CGA":"MONO"))); X break; /* This is useful to control the */ X /* scrolling speed of EGA cards. */ X X#ifdef AM_KERNEL X#ifndef NONET X case FUNC_KEY+4: /* re-initialise the ethernet card */ X net_init(); X break; X#endif NONET X#endif AM_KERNEL X X case FUNC_KEY+9: /* kill all process */ X sigchar(&tty_struct[CON1], SIGKILL); X break; X } X} X X/*===========================================================================* X * trans_con * X *===========================================================================*/ XPRIVATE unsigned trans_con(c) Xunsigned c; /* scan code of key just struck or released */ X{ X/* This routine handles the console keyboard scan-code to ascii conversion. X * It has some ANSI sequences and various macros hardwired to certain keys. X * E.g. Arrow keys and Function Keys. At the moment there is no way to X * change the macros at run-time but lator the console output routines X * may define ANSI sequences to do this. X * This is a general translation routine with no hardware dependancies X * except for keyboard leds (on an AT). X * Note: The type of scan-code translation (Olivetti or IBM) is determined X * during initialization (not here). X */ X X unsigned char code; X bool make; X static char *seq_str= ""; X X /* Do Macro Key Sequences (including ANSI keys) */ X switch (c>>8) { X case 0: /* Non Macro Key */ X break; X case 1: /* Macro String */ X if (*seq_str) X return *seq_str++ | 0x100; X return MARKER; X default: /* Unknown - ignore */ X return MARKER; X } X X /* Start a Arrow Key ANSI macro */ X if (c > NUM_PAD && c <= NUM_PGDN && !((shift1|shift2)^numlock)) { X seq_str= arrow_macros[c - (NUM_PAD+1)]; X return ESC | 0x100; /* Send the escape */ X } X X /* Start a Funtion Key macro */ X if(c > FUNC_KEY && c <= F10) { X if (control && alt) { /* Special CONSOLE DEBUG routines */ X func_key(c); X return MARKER; X } X X /* Handle other combinations of shift, control & alt */ X /* There is no possiblility of control when alt is on */ X seq_str= (alt ? fn_a_macros[shift1|shift2] X : fn_macros[shift1|shift2|(control<<1)]) [c - (FUNC_KEY+1)]; X if (*seq_str) X return *seq_str++ | 0x100; X return MARKER; X } X X make = !(c & 0200); /* 1 when key depressed, 0 when key released */ X c &= 0177; /* high-order bit set on key release */ X X /* Standard IBM keyboard. Do shift processing */ X code = ((shift1 | shift2)^ ( X /* Check capslock for alpha */ X (capslock && c_key_ush[c] >= 'a' && c_key_ush[c] <= 'z') || X /* Check numlock for number pad */ X (c > NUM_PAD && numlock) )) X ? c_key_sh[c] : c_key_ush[c]; X if (control && c < TOP_ROW) code = c_key_sh[c]; /* CTRL-(top row) */ X X /* Process ordinary keys, i.e. not shift, control, alt, etc. */ X if (code < 0200 || code > 0205) { X if (!make) return MARKER; /* key release */ X if (control) code &= 037; X if (alt) code |= 0200; /* alt key ORs 0200 into code */ X return code; X } X X /* Table entries 0200 - 0205 denote special actions. */ X switch(code - 0200) { X case 0: shift1 = make; break; /* shift key on left */ X case 1: shift2 = make; break; /* shift key on right*/ X case 2: control = make; break; /* control */ X case 3: alt = make; break; /* alt key */ X case 4: if (make) capslock = 1-capslock; X set_leds(); break; /* caps lock */ X case 5: if (make) numlock = 1-numlock; X set_leds(); break; /* num lock */ X } X return MARKER; /* No high level processing to do */ X} X X/*===========================================================================* X * escape * X *===========================================================================*/ XPRIVATE void parse_escape(c) Xchar c; /* next character in escape sequence */ X{ X/* Parse and build an escape sequence. X * Formats understood ... X * X * ESC char : state = 0x01 X * ESC '[' [num] char : state = 0x02 X * X * where num is from 1 to MAX_ESC_PARMS decimal strings seperated by ';'s. X * more than MAX_ESC_PARMS decimal strings are ignored. X */ Xregister i; X X /* This char is not printable but the column has been incremented */ X /* fix this by decrementing it */ X tty_struct[CON1].tty_column--; X X if (c_estate == 0) { /* ESC seen - initialize sequence */ X c_estate = 1; X c_p_enum = c_enum; X for(i=0; i<MAX_ESC_PARMS ; i++) X c_enum[i] = 0; X return; X } X X if (c_estate == 2) { /* Only allow numbers in "ESC [" seq */ X if (c >= '0' && c <= '9') { X if (c_p_enum < &c_enum[MAX_ESC_PARMS]) X *c_p_enum = *c_p_enum * 10 + (c - '0'); X return; X } X /* Start a new number in sequence */ X if (c == ';') { X if (c_p_enum < &c_enum[MAX_ESC_PARMS]) X c_p_enum++; X return; X } X } X X /* A char has arrived - process it */ X switch (c_estate) { X case 0x01: /* "ESC char" sequence */ X if (c == '[') { /* "ESC '['" sequence is starting */ X c_estate = 0x02; X return; X } X break; X X case 0x02: /* "ESC '[' [num] char" sequence */ X break; X X default: /* illegal state */ X c_estate = 0; X return; X } X X /* A syntacticly valid ESC sequence has arrived - process it */ X do_escape(c); X c_estate = 0; /* The sequence is finished */ X} X X X/*===========================================================================* X * do_escape * X *===========================================================================*/ XPRIVATE void do_escape(c) Xchar c; /* next character in escape sequence */ X{ X/* The following ANSI escape sequences are currently supported: X * ESC D index the screen X * ESC M reverse index the screen X * ESC E next line X * ESC 7 save cursor position X * ESC 8 restore cursor position X * ESC [ n A Cursor Up [default 1] X * ESC [ n B Cursor Down [default 1] X * ESC [ n C Cursor Right [default 1] X * ESC [ n D Cursor Left [default 1] X * ESC [ y ; x f move cursor to (x, y) [default (1,1)] X * ESC [ y ; x H move cursor to (x, y) [default (1,1)] X * ESC [ n J clear a section of screen [default 0] X * n: 0 = cursor to bottom of screen X * 1 = cursor to top of screen X * 2 = top to bottom of screen X * ESC [ n K clear a section of line [default 0] X * n: 0 = cursor to end of line X * 1 = cursor to start of line X * 2 = start to end of line X * ESC [ n L insert n lines at cursor [default 1] X * ESC [ n M delete n lines at cursor [default 1] X * ESC [ n @ insert n chars at cursor [default 1] X * ESC [ n P delete n chars at cursor [default 1] X * ESC [ n h set mode [default 0] X * ESC [ n l reset mode [default 0] X * n: 2 = Keyboard Lock [pwr up Off] X * 4 = Insert Mode [pwr up Off] X * 7 = Auto Wrap [pwr up ON] X * ESC [ n m set the screen rendition [default 0] X * n: 0 = normal X * 1 = bold X * 4 = underline X * 5 = blink X * 7 = reverse X */ Xregister unsigned i, j; X X switch (c_estate) { X case 0x01: /* ESC char */ X switch (c) { X case 'M': /* Reverse Index */ X if (c_row == 0) X scroll_screen(GO_BACKWARD); X else { X c_row--; X move_to(tty_struct[CON1].tty_column, c_row); X } X return; X X case '7': X c_s_row = c_row; X c_s_col = tty_struct[CON1].tty_column; X return; X X case '8': X move_to(c_s_col, c_s_row); X return; X X case 'E': X move_to(0, c_row); X case 'D': X rout_con(&tty_struct[CON1],'\n',ECHO_OUT); X return; X X default: /* Illegal ESC char sequence */ X return; X } X X case 0x02: /* ESC '[' [num] char */ X switch (c) { X case 'A': /* Cursor Up */ X move_to(tty_struct[CON1].tty_column X , c_row - MAX(1,c_enum[0])); X return; X case 'B': /* Cursor Down */ X move_to(tty_struct[CON1].tty_column X , c_row + MAX(1,c_enum[0])); X return; X case 'C': /* Cursor Right */ X move_to(tty_struct[CON1].tty_column + MAX(1,c_enum[0]) X , c_row); X return; X case 'D': /* Cursor Left */ X move_to(tty_struct[CON1].tty_column + MAX(1,c_enum[0]) X , c_row); X return; X X case 'f': /* Position cursor */ X case 'H': X move_to(c_enum[1]-1, c_enum[0]-1); X return; X X case 'J': X ofl_con(); /* Flush pending chars */ X switch (c_enum[0]) { X case 1: /* Clear start of screen to cursor */ X vid_fill(c_blank, c_org X , c_row * LINE_WIDTH X + tty_struct[CON1].tty_column + 1); X return; X case 2: /* Clear entire screen */ X vid_fill(c_blank, c_org, SCR_LINES*LINE_WIDTH); X return; X case 0: /* Clear cursor to end of screen*/ X vid_fill(c_blank, c_vid X , (SCR_LINES - c_row) * LINE_WIDTH X - tty_struct[CON1].tty_column); X return; X } X return; X X case 'K': X ofl_con(); /* Flush pending chars */ X switch(c_enum[0]) { X case 0: /* Clear cursor to end of line */ X vid_fill(c_blank, c_vid X ,LINE_WIDTH - tty_struct[CON1].tty_column); X return; X case 1: /* Clear start of line to cursor */ X vid_fill(c_blank, c_org + c_row*2*LINE_WIDTH X , tty_struct[CON1].tty_column + 1); X return; X case 2: /* Clear entire line */ X vid_fill(c_blank, c_org + c_row*2*LINE_WIDTH X , LINE_WIDTH); X return; X } X return; X X case 'L': /* Insert Lines */ X ofl_con(); /* Flush pending chars */ X X i = between(1,c_enum[0],SCR_LINES - c_row); /* No. lines */ X j = c_org + (SCR_LINES*LINE_WIDTH*2-2); /* end of screen */ X X /* Do the line moving */ X vid_bmove(j - i*2*LINE_WIDTH, j X , (SCR_LINES - c_row - i)*LINE_WIDTH); X X /* Clear the inserted lines */ X vid_fill(c_blank, c_org + c_row*2*LINE_WIDTH X , i*LINE_WIDTH); X return; X X case 'M': /* Delete Lines */ X ofl_con(); /* Flush pending chars */ X X i = between(1,c_enum[0],SCR_LINES - c_row); /* No. lines */ X j = c_org + c_row*2*LINE_WIDTH; /* Destination */ X X /* Do the line moving */ X vid_fmove(j + i*2*LINE_WIDTH, j X , (SCR_LINES - c_row - i)*LINE_WIDTH); X X /* Clear the new lines */ X vid_fill(c_blank, c_org + (SCR_LINES - i)*LINE_WIDTH*2 X , i*LINE_WIDTH); X return; X X case '@': /* Insert Chars */ X ofl_con(); /* Flush pending chars */ X X i = between(1,c_enum[0] /* No. chars */ X ,LINE_WIDTH - tty_struct[CON1].tty_column); X j = c_org + c_row*2*LINE_WIDTH /* end of line */ X + (2*LINE_WIDTH-2); X X /* Do the char moving */ X vid_bmove(j - i*2, j X , LINE_WIDTH - tty_struct[CON1].tty_column); X X /* Clear the new chars */ X vid_fill(c_blank, c_vid, i); X return; X X case 'P': /* Delete Chars */ X ofl_con(); /* Flush pending chars */ X X i = between(1,c_enum[0] /* No. chars */ X ,LINE_WIDTH - tty_struct[CON1].tty_column); X j = c_org + c_row*2*LINE_WIDTH /* Destination */ X + tty_struct[CON1].tty_column; X X /* Do the char moving */ X vid_fmove(j + i*2, j X , LINE_WIDTH - tty_struct[CON1].tty_column - i); X X /* Clear the new chars */ X vid_fill(c_blank, c_org + (c_row+1)*LINE_WIDTH*2 - i*2, i); X return; X X case 'h': /* Set mode */ X switch (c_enum[0]) { X case 2: /* Keyboard Lock */ X tty_struct[CON1].tty_ddmod |= CON_LOCK; X return; X case 4: /* Insert Mode */ X tty_struct[CON1].tty_ddmod |= CON_INSERT; X return; X case 7: /* Auto Wrap */ X tty_struct[CON1].tty_ddmod |= CON_WRAP; X return; X } X return; X X case 'l': /* Reset mode */ X switch (c_enum[0]) { X case 2: /* Keybaord Unlock */ X tty_struct[CON1].tty_ddmod &= ~CON_LOCK; X return; X case 4: /* Overwrite Mode */ X tty_struct[CON1].tty_ddmod &= ~CON_INSERT; X return; X case 7: /* No Auto Wrap */ X tty_struct[CON1].tty_ddmod &= ~CON_WRAP; X return; X } X return; X X case 'm': /* Set graphic rendition */ X switch (c_enum[0]) { X case 1: /* BOLD (light green on black) */ X c_attr = 0x0A00; X return; X X case 4: /* UNDERLINE (blue on red) */ X c_attr = 0x4100; X return; X X case 5: /* BLINKING (light grey on black) */ X c_attr = 0x8700; X return; X X case 7: /* REVERSE (black on light grey) */ X c_attr = 0x7000; X return; X X default: c_attr = 0x0700; X return; X } X X default: /* Illegal ESC [ sequence */ X return; X } X X default: /* Illegal state */ X return; X } X} X X/*===========================================================================* X * set_leds * X *===========================================================================*/ XPRIVATE void set_leds() X{ X/* Set the LEDs on the caps lock and num lock keys */ X X int leds, dummy, i; X X if (pc_at == 0) return; /* PC/XT doesn't have LEDs */ X leds = (numlock<<1) | (capslock<<2); /* encode LED bits */ X port_out(KEYBD, LED_CODE); /* prepare keyboard to accept LED values */ X port_in(KEYBD, &dummy); /* keyboard sends ack; accept it */ X for (i = 0; i < LED_DELAY; i++) ; /* delay needed */ X port_out(KEYBD, leds); /* give keyboard LED values */ X port_in(KEYBD, &dummy); /* keyboard sends ack; accept it */ X} X X/*===========================================================================* X * check_reboot * X *===========================================================================*/ XPRIVATE void check_reboot() X{ X/* High Level Reboot (Here because int routines can't print) */ X X if (tty_struct[CON1].tty_state & REBOOT) { X printf("\r\n\033[H\033[J\033[12;27HPress any key to Reboot :"); X ofl_con(); /* Flush the string */ X wreboot(); X } X} *-*-END-of-tty2.c-*-* echo x - tty3.c sed 's/^X//' >tty3.c <<'*-*-END-of-tty3.c-*-*' X/* tty3.c - device dependant code for RS232 tty driver (using 8250) X * X * Written by: A.Hannam (Feb 1988) X * X * Modifications: X * 7/9/88 - Modified for MINIX V1.3 on IBM-PC X * Improved interrupt handling X * Improved efficiency of compiled code X * 19/9/88 - Added flow control (handshaking) & many ioctl calls X */ X X#include "../h/const.h" X#include "../h/sgtty.h" X#include "../h/type.h" X#include "../h/signal.h" X#include "../h/com.h" X#include "const.h" X X#ifdef HIGH_LEVEL X#undef HIGH_LEVEL X#endif X X#include "tty.h" X X/* Now begins the code and data for the device-dependent tty drivers. */ X X/*****************************************************************************/ X/**************************** 8250 SERIAL DRIVER *****************************/ X/*****************************************************************************/ X X#if NR_8250S < 1 XError:- 8250 Driver: "NR_8250S" < 1 X#endif X XPUBLIC void int_8250(), init_8250(); X Xextern void do_nothing(), restore(); Xextern int lock(); X XPRIVATE bool rout_8250(); XPRIVATE void dset_8250(), oclr_8250(), iflow_8250(); X X /* Some space and variables for the raw Output routines */ XGEN_S_BUF_TYPE(char,128) XGEN_S_BUF_TYPE(char,32) X X#define BUSY DD_FLAG1 /* 1 when tty busy on output */ X#define RX_FLOW DD_FLAG2 /* 1 when input doing flow cntrl */ X#define TX_FLOW DD_FLAG3 /* 1 when output doing flow cntrl */ X X#define WOUT_THRESHOLD 4 /* when to start trying to get more chars */ X#define XONCHAR 17 /* ^Q to restart with XONXOFF control */ X#define XOFFCHAR 19 /* ^S to stop with XONXOFF control */ X X /* interrupt identification register bits */ X#define IPENDING 1 /* 1 when no interrupt pending */ X#define ITYPEMASK 6 /* mask to isolate interrupt causes */ X#define RXREADY 4 /* reciever is ready interrupt */ X#define TXREADY 2 /* transmitter ready interrupt */ X#define LSTATUS 6 /* line status event interrupt */ X#define MSTATUS 0 /* modem status event interrupt */ X /* line control register bits */ X/* bits 0,1 are number of databits as defined in sgtty.h */ X/* bit 2 is the number of stopbits as defined in sgtty.h */ X#define P_ENABLE 8 /* enable parity bit */ X#define P_EVEN 16 /* even parity bit */ X#define P_STICK 32 /* the parity sticks to 1 on P_EVEN else 0 */ X#define P_BRK 64 /* break enable bit */ X#define P_BAUD 128 /* set up for baud divisor input */ X /* line status register bits */ X#define L_DR 1 /* data recieved status bit */ X#define L_OE 2 /* overrun error status bit */ X#define L_PE 4 /* parity error status bit */ X#define L_FE 8 /* framing error status bit */ X#define L_BI 16 /* break detected status bit */ X#define L_HRE 32 /* holding reg empty status bit */ X#define L_TE 64 /* transmitter empty status bit */ X /* interrupt enable register bits */ X#define I_DR 1 /* enable recieved data interrupt */ X#define I_TE 2 /* enable transmit reg empty interrupt */ X#define I_LS 4 /* reciever line status interrupt */ X#define I_MS 8 /* modem status interrupt */ X /* modem control register bits */ X#define M_DTR 1 /* DTR output bit */ X#define M_RTS 2 /* RTS output bit */ X#define M_OUT1 4 /* OUT1 output bit */ X#define M_OUT2 8 /* OUT2 output bit */ X#define M_LOOP 16 /* enable loopback mode bit */ X /* modem status register bits */ X#define M_CTSC 1 /* CTS input has changed bit */ X#define M_DSRC 2 /* DSR input has changed bit */ X#define M_RIT 4 /* RI input (low to high transition) bit */ X#define M_DCDC 8 /* DCD input has changed bit */ X#define M_CTS 16 /* CTS input bit */ X#define M_DSR 32 /* DSR input bit */ X#define M_RI 64 /* RI input bit */ X#define M_DCD 128 /* DCD input bit */ X XPRIVATE message mess_8250; /* our message buffer */ XPRIVATE char modem_bits; /* bits for RTS, CTS status etc. */ X XPRIVATE struct rs_struct { X int ldata; /* Tx/Rx data register */ X int lreg; /* Line control register */ X int intreg; /* Interrupt control register */ X int intid; /* Interrupt status register */ X int baud; /* Register (& +1) for baud */ X int lstat; /* Line status register */ X int modreg; /* Modem line control register */ X int mstat; /* Modem status register */ X char32 eout; /* Pointer to the echo out buffer */ X char128 wout; /* Pointer to the write out buffer */ X charfbuf in; /* Pointer to the input buffer */ X } rs[NR_8250S] = {{ X 0x3F8, 0x3FB, 0x3F9, 0x3FA, 0x3F8, 0x3FD, 0x3FC, 0x3FE X , {"",0,0,0}, {"",0,0,0}, {"",0} X#if NR_8250S >= 2 X },{ X 0x2F8, 0x2FB, 0x2F9, 0x2FA, 0x2F8, 0x2FD, 0x2FC, 0x2FE X , {"",0,0,0}, {"",0,0,0}, {"",0} X#endif X#if NR_8250S >= 3 XError:- Need Extra port tables for COM3 ... X#endif X }}; X X/*===========================================================================* X * init_8250 * X *===========================================================================*/ XPUBLIC void init_8250(tp) Xregister tty_entry *tp; X{ Xregister i = tp - &tty_struct[COM1]; X X mess_8250.TTY_LINE= COM1; /* The interrupt message */ X mess_8250.m_type= TTY_CHAR_INT; /* Could have been TTY_O_DONE */ X X tp->tty_devraw= rout_8250; X tp->tty_devclr= oclr_8250; X tp->tty_devflush= do_nothing; X tp->tty_devioctl= dset_8250; X tp->tty_devtrans= (unsigned (*)()) do_nothing; X tp->tty_devempty= iflow_8250; X X#ifdef NXONXOFF X /* 8 Data, 1 Stop bits, No Parity, Hardware handshaking */ X tp->tty_ddmod= D_SERIAL | SER_8DATA; X#else X /* 8 Data, 1 Stop bits, No Parity, XONXOFF handshaking */ X tp->tty_ddmod = D_SERIAL | SER_8DATA | TX_XONXOFF | RX_XONXOFF; X#endif X tp->tty_mode &= ~(EVENP|ODDP); X X#ifdef SLOW X tp->tty_ispeed = tp->tty_ospeed = B1200; X#else X tp->tty_ispeed = tp->tty_ospeed = B4800; X#endif X X tp->tty_fbuf = &rs[i].in; X fbufinit(rs[i].in); X bufinit(rs[i].wout); X bufinit(rs[i].eout); X modem_bits = (M_DTR|M_RTS|M_OUT2); /* DTR, RTS on, ints enabled */ X X dset_8250(tp, 0); X} X X X X/*===========================================================================* X * int_8250 * X *===========================================================================*/ XPUBLIC void int_8250(unit) X{ /* Interrupts are off */ X /* Called when an interrupt occurs on the RS232 a port */ Xchar c; Xregister tty_entry *tp= &tty_struct[COM1+unit]; Xregister struct rs_struct *r = &rs[unit]; X X /* Ignore any interrupts on lines we don't know about */ X if (unit >= NR_8250S) return; X X while(TRUE) { X port_in(r->intid, &c); X X if (c&IPENDING) break; /* no interrupt to process */ X X switch (c & ITYPEMASK) { X case RXREADY: /* Get a character */ X port_in(r->ldata,&c); X X /* Store the char if possible */ X if ( bufnfull(r->in) ) { X /* Enough room so store the char */ X putfobj(r->in,c); X } X X /* Tell other routines we have chars available */ X tp->tty_state |= IN_FLUSH; X flush_flag |= IN_FLUSH; X X /* Send the int message to tty task only if abs. needed */ X if (bufcount(r->in) >= CHAR_BUF_THRESHOLD) { X /* Do Flow Control */ X tp->tty_state |= RX_FLOW; X if (tp->tty_ddmod & RX_XONXOFF) X /* jam a ^S into the output stream */ X port_out(r->ldata,XOFFCHAR); X else { X /* hardware hanshaking */ X modem_bits &= ~M_DTR; X port_out(r->modreg,modem_bits); X } X interrupt(TTY, &mess_8250); X } X /* The high level routine must turn back on the flow control */ X break; X X case TXREADY: /* Transmit more data */ X /* XT and some clones generate spurious ints */ X port_in(r->lstat,&c); X if (!(c&L_HRE)) break; X X tp->tty_state &= ~BUSY; /* Assume port now free */ X X /* Get data from the echo stream */ X if (bufnempty(r->eout)) { X port_out(r->ldata,getobj(r->eout)); X advgetobj(r->eout); X tp->tty_state |= BUSY; X break; X } X X /* If inhibited don't get data from write stream */ X if (tp->tty_state&(INHIBITED|TX_FLOW)) break; X X /* get data from write stream */ X if (bufnempty(r->wout)) { X port_out(r->ldata,getobj(r->wout)); X advgetobj(r->wout); X tp->tty_state |= BUSY; X } X X /* If nearly empty make an attempt at getting more */ X if (bufcount(r->wout) <= WOUT_THRESHOLD X && tp->tty_writers) { X tp->tty_state |= OUT_FLUSH; X flush_flag |= OUT_FLUSH; X } X X /* No more data available, urgently try to get more */ X if (bufempty(r->wout) && tp->tty_writers) { X tp->tty_state |= OUT_FLUSH; X interrupt(TTY,&mess_8250); X } X break; X X case LSTATUS: /* line status event */ X port_in(r->lstat, &c); /* disabled */ X break; X X case MSTATUS: /* modem status event */ X port_in(r->mstat, &c); X /* XONXOFF output flow cntrl in device indep driver */ X if (tp->tty_ddmod & TX_XONXOFF) break; X X if ((c&(M_CTS|M_DSR)) == (M_CTS|M_DSR)) X tp->tty_state &= ~TX_FLOW; X else X tp->tty_state |= TX_FLOW; X break; X } X } X port_out(INT_CTL,ENABLE); X} X X X/*===========================================================================* X * rout_8250 * X *===========================================================================*/ XPRIVATE bool rout_8250(tp,c,prio) Xregister tty_entry *tp; Xchar c; X{ X/* Fill up the buffers in FIFO manner according to the priority */ Xint save_st; Xregister struct rs_struct *r = &rs[tp - &tty_struct[COM1]]; X X save_st = lock(); X /* If not busy then send echo's straight to the hardware */ X if (!(tp->tty_state&BUSY) && (prio==ECHO_OUT)) { X port_out(r->ldata,c); X tp->tty_state |= BUSY; X restore(save_st); X return 0; X } X X /* Transmitter currently busy or char in write stream. Queue it FIFO */ X /* If ECHO_OUT and not enough space then try in write stream */ X if (prio==ECHO_OUT && bufnfull(r->eout)) { X putobj(r->eout,c); X restore(save_st); X return 0; X } X X /* Place in write stream if there is enough room */ X if (bufnfull(r->wout)) { X putobj(r->wout,c); X } X X /* If still not busy try to restart IO. X * This is subject to characters being writable (^S processing). X */ X if (!(tp->tty_state&(INHIBITED|BUSY|TX_FLOW)) && bufnempty(r->wout)) { X port_out(r->ldata,getobj(r->wout)); X advgetobj(r->wout); X tp->tty_state |= BUSY; X } X X /* Warn the user if the stream is filling up */ X restore(save_st); X return (prio==ECHO_OUT X || (int)bufcount(r->wout) >= (int)bufsize(r->wout)*7/8); X} X Xstatic unsigned char SBHigh[]= {0,9,9,6,4,3,3,2,1,0,0,0,0,0,0,0,0,0,0,0} X ,SBLow[]={0,0xE4,0,0,0x17,0x59,0,0x40,0x80,0xC0 X ,0x60,0x40,0x3A,0x30,0x18,0xC,6,3,2,1}; X X/*===========================================================================* X * dset_8250 * X *===========================================================================*/ XPRIVATE void dset_8250(tp, m) Xregister tty_entry *tp; Xmessage *m; X{ Xint c, save_st; Xregister struct rs_struct *r = &rs[tp - &tty_struct[COM1]]; X X/* set baud rate, bits/char, etc X */ X if (m) X switch(m->TTY_REQUEST) { X case TIOCSETN: X case TIOCSETP: X /* Don't support split baud */ X tp->tty_ospeed= tp->tty_ispeed; X break; X X case TIOCSETM: X /* Only keep Valid Mode bits */ X tp->tty_ddmod &= SER_2STOP|SER_DATABITS|RX_XONXOFF|TX_XONXOFF; X tp->tty_ddmod |= D_SERIAL; X break; X X case TIOCMODS: X port_in(r->modreg, &c); X c &= ~(M_DTR|M_RTS); X if (((int)m->TTY_FLAGS) & TIOCM_DTR) c |= M_DTR; X if (((int)m->TTY_FLAGS) & TIOCM_RTS) c |= M_RTS; X port_out(r->modreg, c); X return; X X case TIOCMODG: X port_in(r->modreg, &c); X m->TTY_FLAGS = (long)((unsigned) TIOCM_LE X | ((c&M_DTR) ? TIOCM_DTR : 0) X | ((c&M_RTS) ? TIOCM_RTS : 0)); X port_in(r->mstat, &c); X m->TTY_FLAGS |= (long)((unsigned) ((c&M_CTS) ? TIOCM_CTS : 0) X | ((c&M_DCD) ? TIOCM_CAR : 0) X | ((c&M_DSR) ? TIOCM_DSR : 0) X | ((c&M_RI ) ? TIOCM_RNG : 0)); X m->TTY_SPEK = 0; X return; X X case TIOCSBRK: X port_in(r->lreg, &c); X c |= P_BRK; X port_out(r->lreg, c); X return; X X case TIOCCBRK: X port_in(r->lreg, &c); X c &= ~P_BRK; X port_out(r->lreg, c); X return; X X case TIOCSDTR: X port_in(r->modreg, &c); X c |= M_DTR; X port_out(r->modreg, c); X return; X X case TIOCCDTR: X port_in(r->modreg, &c); X c &= ~M_DTR; X port_out(r->modreg, c); X return; X X case TIOCSMLB: X port_in(r->modreg, &c); X c |= M_LOOP; X port_out(r->modreg, c); X return; X X case TIOCCMLB: X port_in(r->modreg, &c); X c &= ~M_LOOP; X port_out(r->modreg, c); X return; X X default: /* Ignore all other ioctl calls */ X return; X } X X /* set up c with line control params i.e. data,stop,parity */ X c = tp->tty_ddmod & (SER_2STOP|SER_DATABITS); X if (tp->tty_mode&ANYP) { X c |= P_ENABLE; X if (tp->tty_mode&EVENP) c |= P_EVEN; X } X X /* Now set according to the modes */ X save_st = lock(); X X port_out(r->lreg,P_BAUD); /* Set up for baud */ X port_out(r->baud+1,SBHigh[tp->tty_ospeed]); /* set Baud rate */ X port_out(r->baud,SBLow[tp->tty_ospeed]); X X port_out(r->lreg,c); /* set data, stop & parity */ X X port_in(r->lstat, &c); /* Clear any data from controller */ X if (c & L_DR) port_in(r->ldata,&c); X X port_in(r->mstat,&c); /* Check output flow control */ X tp->tty_state &= ~TX_FLOW; X if (!(tp->tty_ddmod&TX_XONXOFF) && (c&(M_CTS|M_DSR)) != (M_CTS|M_DSR)) X tp->tty_state |= TX_FLOW; X X if (tp->tty_state & RX_FLOW) { /* cancel input flow control */ X modem_bits |= M_DTR; /* set up DTR etc */ X port_out(r->ldata,XONCHAR); /* jam ^Q in output */ X tp->tty_state &= ~RX_FLOW; X } X port_out(r->modreg,modem_bits); X if (tp->tty_ddmod&TX_XONXOFF) X port_out(r->intreg, I_DR|I_TE ); /* TX & RX ints */ X else X port_out(r->intreg, I_DR|I_TE|I_MS); /* TX, RX & MS ints */ X X restore(save_st); X} X X/*===========================================================================* X * oclr_8250 * X *===========================================================================*/ XPRIVATE void oclr_8250(tp) Xregister tty_entry *tp; X{ Xint save_st; Xregister struct rs_struct *r = &rs[tp - &tty_struct[COM1]]; X X save_st = lock(); X bufinit(r->eout); X bufinit(r->wout); X restore(save_st); X} X X/*===========================================================================* X * iflow_8250 * X *===========================================================================*/ XPRIVATE void iflow_8250(tp) Xregister tty_entry *tp; X{ X/* The input buffers have been cleared ... X * Handle flow control for the input stream X */ X if (tp->tty_state & RX_FLOW) { X if (tp->tty_ddmod & RX_XONXOFF) /* XON-XOFF handshaking */ X rout_8250(tp, XONCHAR, ECHO_OUT); X else { /* Hardware handshaking */ X modem_bits |= M_DTR; X port_out(rs[tp - &tty_struct[COM1]].modreg,modem_bits); X } X tp->tty_state &= ~RX_FLOW; X } X} *-*-END-of-tty3.c-*-* exit