/* @(#)tty.c 1.2 */ /* * general TTY subroutines */ #include "sys/param.h" #include "sys/types.h" #include "sys/systm.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/errno.h" #include "sys/tty.h" #include "sys/ttold.h" #include "sys/proc.h" #include "sys/file.h" #include "sys/conf.h" #include "sys/termio.h" #include "sys/sysinfo.h" #include "sys/var.h" extern int sspeed; extern int tthiwat[]; extern int ttlowat[]; extern char ttcchar[]; /* 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, }; /* * common ioctl tty code */ ttiocom(tp, cmd, arg, mode) register struct tty *tp; { register struct user *up; register short flag; register struct sgttyb *tbp; register struct termio *cbp; struct termio cb; struct sgttyb tb; up = &u; switch(cmd) { case IOCTYPE: up->u_rval1 = TIOC; break; case TCSETAW: case TCSETAF: ttywait(tp); if (cmd == TCSETAF) ttyflush(tp, (FREAD|FWRITE)); case TCSETA: cbp = &cb; if (copyin((caddr_t)arg, (caddr_t)cbp, sizeof cb)) { up->u_error = EFAULT; break; } if (tp->t_line != cbp->c_line) { if (cbp->c_line < 0 || cbp->c_line >= linecnt) { up->u_error = EINVAL; break; } (*linesw[tp->t_line].l_ioctl)(tp, LDCLOSE, 0, mode); } flag = tp->t_lflag; tp->t_iflag = cbp->c_iflag; tp->t_oflag = cbp->c_oflag; tp->t_cflag = cbp->c_cflag; tp->t_lflag = cbp->c_lflag; bcopy((caddr_t)cbp->c_cc, (caddr_t)tp->t_cc, NCC); if (tp->t_line != cbp->c_line) { tp->t_line = cbp->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: cbp = &cb; cbp->c_iflag = tp->t_iflag; cbp->c_oflag = tp->t_oflag; cbp->c_cflag = tp->t_cflag; cbp->c_lflag = tp->t_lflag; cbp->c_line = tp->t_line; bcopy((caddr_t)tp->t_cc, (caddr_t)cbp->c_cc, NCC); if (copyout((caddr_t)cbp, (caddr_t)arg, sizeof cb)) up->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: up->u_error = EINVAL; } break; case TCFLSH: switch (arg) { case 0: case 1: case 2: ttyflush(tp, (arg - FOPEN)&(FREAD|FWRITE)); break; default: up->u_error = EINVAL; } break; /* conversion aide only */ case TIOCSETP: tbp = &tb; ttywait(tp); ttyflush(tp, (FREAD|FWRITE)); if (copyin((caddr_t)arg, (caddr_t)tbp, sizeof(tb))) { up->u_error = EFAULT; break; } tp->t_iflag = 0; tp->t_oflag = 0; tp->t_lflag = 0; tp->t_cflag = (tbp->sg_ispeed&CBAUD)|CREAD; if ((tbp->sg_ispeed&CBAUD)==B110) tp->t_cflag |= CSTOPB; tp->t_cc[VERASE] = tbp->sg_erase; tp->t_cc[VKILL] = tbp->sg_kill; flag = tbp->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_cc[VEOL2] = 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: tbp = &tb; tbp->sg_ispeed = tp->t_cflag&CBAUD; tbp->sg_ospeed = tbp->sg_ispeed; tbp->sg_erase = tp->t_cc[VERASE]; tbp->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; tbp->sg_flags = flag; if (copyout((caddr_t)tbp, (caddr_t)arg, sizeof(tb))) up->u_error = EFAULT; break; /* * The following ioctls were added by UniSoft */ /* * Return number of characters immediately available. */ case FIONREAD: { off_t nread; SPL6(); while (tp->t_rawq.c_cc && tp->t_delct) canon(tp); SPL0(); nread = tp->t_canq.c_cc; if (!(tp->t_lflag &ICANON)) nread += tp->t_rawq.c_cc; if (copyout((caddr_t)&nread, (caddr_t)arg, sizeof (off_t))) up->u_error = EFAULT; break; } default: if ((cmd&IOCTYPE) == LDIOC) (*linesw[tp->t_line].l_ioctl)(tp, cmd, arg, mode); else up->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((caddr_t)ttcchar, (caddr_t)tp->t_cc, NCC); } ttywait(tp) register struct tty *tp; { spltty(); while (tp->t_outq.c_cc || (tp->t_state&(BUSY|TIMEOUT))) { tp->t_state |= TTIOW; (void) sleep((caddr_t)&tp->t_oflag, TTOPRI); } SPL0(); delay(v.v_hz>>4); } /* * 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 = spltty(); 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; spltty(); if (tp->t_rawq.c_cc == 0) tp->t_delct = 0; while (tp->t_delct == 0) { if (!(tp->t_state&CARR_ON) || (u.u_fmode&FNDELAY)) { SPL0(); return; } if (!(tp->t_lflag&ICANON) && tp->t_cc[VMIN]==0) { if (tp->t_cc[VTIME]==0) break; tp->t_state &= ~RTO; if (!(tp->t_state&TACT)) tttimeo(tp); } tp->t_state |= IASLP; (void) sleep((caddr_t)&tp->t_rawq, TTIPRI); } if (!(tp->t_lflag&ICANON)) { if (tp->t_canq.c_cc == 0) { tp->t_canq = tp->t_rawq; tp->t_rawq = ttnulq; } else while (tp->t_rawq.c_cc) (void) putc(getc(&tp->t_rawq), &tp->t_canq); 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] || c == tp->t_cc[VEOL2]) break; if (bp >= &canonb[CANBSIZ]) bp--; } tp->t_delct--; c = bp - canonb; sysinfo.canch += c; bp = canonb; /* faster copy ? */ while (c--) (void) 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); }