/* * general TTY subroutines */ #include "sys/param.h" #include "sys/systm.h" #include "sys/dir.h" #include "sys/user.h" #include "sys/tty.h" #include "sys/ttold.h" #include "sys/proc.h" #include "sys/file.h" #include "sys/conf.h" #include "sys/ioctl.h" #include "sys/sysinfo.h" /* * tty low and high water marks * high < TTYHOG */ int tthiwat[16] = { 0, 60, 60, 60, 60, 60, 60, 120, 120, 180, 180, 240, 240, 240, 100, 100, }; int ttlowat[16] = { 0, 20, 20, 20, 20, 20, 20, 40, 40, 60, 60, 80, 80, 80, 50, 50, }; char ttcchar[NCC] = { CINTR, CQUIT, CERASE, CKILL, CEOF, 0, 0, 0 }; /* null clist header */ struct clist ttnulq; /* canon buffer */ char canonb[CANBSIZ]; /* * Input mapping table-- if an entry is non-zero, when the * corresponding character is typed preceded by "\" the escape * sequence is replaced by the table value. Mostly used for * upper-case only terminals. */ char maptab[] = { 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,'|',000,000,000,000,000,'`', '{','}',000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,'~',000, 000,'A','B','C','D','E','F','G', 'H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W', 'X','Y','Z',000,000,000,000,000, }; /* * routine called on first teletype open. * establishes a process group for distribution * of quits and interrupts from the tty. */ ttopen(tp) register struct tty *tp; { register struct proc *pp; pp = u.u_procp; if ((pp->p_pid == pp->p_pgrp) && (u.u_ttyp == NULL) && (tp->t_pgrp == 0)) { u.u_ttyp = tp; tp->t_pgrp = pp->p_pgrp; } tp->t_state &= ~WOPEN; tp->t_state |= ISOPEN; } ttclose(tp) register struct tty *tp; { spl5(); (*tp->t_proc)(tp, T_RESUME); spl0(); ttyflush(tp, FREAD); ttywait(tp); tp->t_state &= ~(ISOPEN|WOPEN); } /* * common ioctl tty code */ ttiocom(tp, cmd, arg, mode) register struct tty *tp; { register flag; struct termio cb; struct sgttyb tb; switch(cmd) { case IOCTYPE: u.u_rval1 = TIOC; break; case TCSETAW: case TCSETAF: ttywait(tp); if (cmd == TCSETAF) ttyflush(tp, (FREAD|FWRITE)); case TCSETA: if (copyin(arg, &cb, sizeof cb)) { u.u_error = EFAULT; break; } if (tp->t_line != cb.c_line) { if (cb.c_line < 0 || cb.c_line >= linecnt) { u.u_error = EINVAL; break; } (*linesw[tp->t_line].l_ioctl)(tp, LDCLOSE, 0, mode); } flag = tp->t_lflag; tp->t_iflag = cb.c_iflag; tp->t_oflag = cb.c_oflag; tp->t_cflag = cb.c_cflag; tp->t_lflag = cb.c_lflag; bcopy(cb.c_cc, tp->t_cc, NCC); if (tp->t_line != cb.c_line) { tp->t_line = cb.c_line; (*linesw[tp->t_line].l_ioctl)(tp, LDOPEN, 0, mode); } else if (tp->t_lflag != flag) { (*linesw[tp->t_line].l_ioctl)(tp, LDCHG, flag, mode); } return(1); case TCGETA: cb.c_iflag = tp->t_iflag; cb.c_oflag = tp->t_oflag; cb.c_cflag = tp->t_cflag; cb.c_lflag = tp->t_lflag; cb.c_line = tp->t_line; bcopy(tp->t_cc, cb.c_cc, NCC); if (copyout(&cb, arg, sizeof cb)) u.u_error = EFAULT; break; case TCSBRK: ttywait(tp); if (arg == 0) (*tp->t_proc)(tp, T_BREAK); break; case TCXONC: switch (arg) { case 0: (*tp->t_proc)(tp, T_SUSPEND); break; case 1: (*tp->t_proc)(tp, T_RESUME); break; case 2: (*tp->t_proc)(tp, T_BLOCK); break; case 3: (*tp->t_proc)(tp, T_UNBLOCK); break; default: u.u_error = EINVAL; } break; case TCFLSH: switch (arg) { case 0: case 1: case 2: ttyflush(tp, (arg - FOPEN)&(FREAD|FWRITE)); break; default: u.u_error = EINVAL; } break; /* conversion aide only */ case TIOCSETP: ttywait(tp); ttyflush(tp, (FREAD|FWRITE)); if (copyin(arg, &tb, sizeof(tb))) { u.u_error = EFAULT; break; } tp->t_iflag = 0; tp->t_oflag = 0; tp->t_lflag = 0; tp->t_cflag = (tb.sg_ispeed&CBAUD)|CREAD; if ((tb.sg_ispeed&CBAUD)==B110) tp->t_cflag |= CSTOPB; tp->t_cc[VERASE] = tb.sg_erase; tp->t_cc[VKILL] = tb.sg_kill; flag = tb.sg_flags; if (flag&O_HUPCL) tp->t_cflag |= HUPCL; if (flag&O_XTABS) tp->t_oflag |= TAB3; else if (flag&O_TBDELAY) tp->t_oflag |= TAB1; if (flag&O_LCASE) { tp->t_iflag |= IUCLC; tp->t_oflag |= OLCUC; tp->t_lflag |= XCASE; } if (flag&O_ECHO) tp->t_lflag |= ECHO; if (!(flag&O_NOAL)) tp->t_lflag |= ECHOK; if (flag&O_CRMOD) { tp->t_iflag |= ICRNL; tp->t_oflag |= ONLCR; if (flag&O_CR1) tp->t_oflag |= CR1; if (flag&O_CR2) tp->t_oflag |= ONOCR|CR2; } else { tp->t_oflag |= ONLRET; if (flag&O_NL1) tp->t_oflag |= CR1; if (flag&O_NL2) tp->t_oflag |= CR2; } if (flag&O_RAW) { tp->t_cc[VTIME] = 1; tp->t_cc[VMIN] = 6; tp->t_iflag &= ~(ICRNL|IUCLC); tp->t_cflag |= CS8; } else { tp->t_cc[VEOF] = CEOF; tp->t_cc[VEOL] = 0; tp->t_iflag |= BRKINT|IGNPAR|ISTRIP|IXON|IXANY; tp->t_oflag |= OPOST; tp->t_cflag |= CS7|PARENB; tp->t_lflag |= ICANON|ISIG; } tp->t_iflag |= INPCK; if (flag&O_ODDP) if (flag&O_EVENP) tp->t_iflag &= ~INPCK; else tp->t_cflag |= PARODD; if (flag&O_VTDELAY) tp->t_oflag |= FFDLY; if (flag&O_BSDELAY) tp->t_oflag |= BSDLY; return(1); case TIOCGETP: tb.sg_ispeed = tp->t_cflag&CBAUD; tb.sg_ospeed = tb.sg_ispeed; tb.sg_erase = tp->t_cc[VERASE]; tb.sg_kill = tp->t_cc[VKILL]; flag = 0; if (tp->t_cflag&HUPCL) flag |= O_HUPCL; if (!(tp->t_lflag&ICANON)) flag |= O_RAW; if (tp->t_lflag&XCASE) flag |= O_LCASE; if (tp->t_lflag&ECHO) flag |= O_ECHO; if (!(tp->t_lflag&ECHOK)) flag |= O_NOAL; if (tp->t_cflag&PARODD) flag |= O_ODDP; else if (tp->t_iflag&INPCK) flag |= O_EVENP; else flag |= O_ODDP|O_EVENP; if (tp->t_oflag&ONLCR) { flag |= O_CRMOD; if (tp->t_oflag&CR1) flag |= O_CR1; if (tp->t_oflag&CR2) flag |= O_CR2; } else { if (tp->t_oflag&CR1) flag |= O_NL1; if (tp->t_oflag&CR2) flag |= O_NL2; } if ((tp->t_oflag&TABDLY)==TAB3) flag |= O_XTABS; else if (tp->t_oflag&TAB1) flag |= O_TBDELAY; if (tp->t_oflag&FFDLY) flag |= O_VTDELAY; if (tp->t_oflag&BSDLY) flag |= O_BSDELAY; tb.sg_flags = flag; if (copyout(&tb, arg, sizeof(tb))) u.u_error = EFAULT; break; default: if ((cmd&IOCTYPE) == LDIOC) (*linesw[tp->t_line].l_ioctl)(tp, cmd, arg, mode); else u.u_error = EINVAL; break; } return(0); } ttinit(tp) register struct tty *tp; { tp->t_line = 0; tp->t_iflag = 0; tp->t_oflag = 0; tp->t_cflag = SSPEED|CS8|CREAD|HUPCL; tp->t_lflag = 0; bcopy(ttcchar, tp->t_cc, NCC); } ttywait(tp) register struct tty *tp; { spl5(); while (tp->t_outq.c_cc || (tp->t_state&(BUSY|TIMEOUT))) { tp->t_state |= TTIOW; sleep((caddr_t)&tp->t_oflag, TTOPRI); } spl0(); delay(HZ/15); } /* * flush TTY queues */ ttyflush(tp, cmd) register struct tty *tp; { register struct cblock *cp; register s; if (cmd&FWRITE) { while ((cp = getcb(&tp->t_outq)) != NULL) putcf(cp); (*tp->t_proc)(tp, T_WFLUSH); if (tp->t_state&OASLP) { tp->t_state &= ~OASLP; wakeup((caddr_t)&tp->t_outq); } if (tp->t_state&TTIOW) { tp->t_state &= ~TTIOW; wakeup((caddr_t)&tp->t_oflag); } } if (cmd&FREAD) { while ((cp = getcb(&tp->t_canq)) != NULL) putcf(cp); s = spl5(); while ((cp = getcb(&tp->t_rawq)) != NULL) putcf(cp); tp->t_delct = 0; splx(s); (*tp->t_proc)(tp, T_RFLUSH); if (tp->t_state&IASLP) { tp->t_state &= ~IASLP; wakeup((caddr_t)&tp->t_rawq); } } } /* * Transfer raw input list to canonical list, * doing erase-kill processing and handling escapes. */ canon(tp) register struct tty *tp; { register char *bp; register c, esc; spl5(); if (tp->t_rawq.c_cc == 0) tp->t_delct = 0; while (tp->t_delct == 0) { if ((tp->t_state&CARR_ON)==0) { spl0(); return; } tp->t_state |= IASLP; sleep((caddr_t)&tp->t_rawq, TTIPRI); } if (!(tp->t_lflag&ICANON)) { tp->t_canq = tp->t_rawq; tp->t_rawq = ttnulq; tp->t_delct = 0; spl0(); return; } spl0(); bp = canonb; esc = 0; while ((c=getc(&tp->t_rawq)) >= 0) { if (!esc) { if (c == '\\') { esc++; } else if (c == tp->t_cc[VERASE]) { if (bp > canonb) bp--; continue; } else if (c == tp->t_cc[VKILL]) { bp = canonb; continue; } else if (c == tp->t_cc[VEOF]) { break; } } else { esc = 0; if (c == tp->t_cc[VERASE] || c == tp->t_cc[VKILL] || c == tp->t_cc[VEOF]) bp--; else if (tp->t_lflag&XCASE) { if ((c < 0200) && maptab[c]) { bp--; c = maptab[c]; } else if (c == '\\') continue; } else if (c == '\\') esc++; } *bp++ = c; if (c == '\n' || c == tp->t_cc[VEOL]) break; if (bp >= &canonb[CANBSIZ]) bp--; } tp->t_delct--; c = bp - canonb; sysinfo.canch += c; bp = canonb; /* faster copy ? */ while (c--) putc(*bp++, &tp->t_canq); return; } /* * Restart typewriter output following a delay timeout. * The name of the routine is passed to the timeout * subroutine and it is called during a clock interrupt. */ ttrstrt(tp) register struct tty *tp; { (*tp->t_proc)(tp, T_TIME); } /* * Called from device's read routine after it has * calculated the tty-structure given as argument. */ ttread(tp) register struct tty *tp; { register struct clist *tq; tq = &tp->t_canq; if (tq->c_cc == 0) if (u.u_fmode&FNDELAY) return; else canon(tp); while (u.u_count!=0 && u.u_error==0) { if (u.u_count >= CLSIZE) { register n; register struct cblock *cp; cp = getcb(tq); if (cp == NULL) break; n = min(u.u_count, cp->c_last - cp->c_first); if (copyout(&cp->c_data[cp->c_first], u.u_base, n)) u.u_error = EFAULT; putcf(cp); u.u_base += n; u.u_count -= n; } else { register c; if ((c = getc(tq)) < 0) break; if (subyte(u.u_base++, c)) u.u_error = EFAULT; u.u_count--; } } if (tp->t_state&TBLOCK) { if (tp->t_rawq.c_cc<TTXOLO) { (*tp->t_proc)(tp, T_UNBLOCK); } } } /* * Called from device's write routine after it has * calculated the tty-structure given as argument. */ ttwrite(tp) register struct tty *tp; { if ((tp->t_state&CARR_ON)==0) return; while (u.u_count) { spl5(); while (tp->t_outq.c_cc > tthiwat[tp->t_cflag&CBAUD]) { (*tp->t_proc)(tp, T_OUTPUT); tp->t_state |= OASLP; sleep((caddr_t)&tp->t_outq, TTOPRI); } spl0(); if (u.u_count >= (CLSIZE/2)) { register n; register struct cblock *cp; if ((cp = getcf()) == NULL) break; n = min(u.u_count, cp->c_last); if (copyin(u.u_base, cp->c_data, n)) { u.u_error = EFAULT; putcf(cp); break; } u.u_base += n; u.u_count -= n; cp->c_last = n; ttxput(tp, cp, n); } else { register c; c = fubyte(u.u_base++); if (c<0) { u.u_error = EFAULT; break; } u.u_count--; ttxput(tp, c, 0); } } spl5(); (*tp->t_proc)(tp, T_OUTPUT); spl0(); }