/* @(#)tt1.c 1.4 */ /* * Line discipline 0 * No Virtual Terminal Handling */ #include "sys/param.h" #include "sys/types.h" #include "sys/systm.h" #include "sys/conf.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/errno.h" #include "sys/proc.h" #include "sys/file.h" #include "sys/tty.h" #include "sys/termio.h" #include "sys/sysinfo.h" #include "sys/var.h" #include "sys/reg.h" extern char partab[]; /* * 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->t_pgrp; tp->t_pgrp = pp->p_pgrp; } ttioctl(tp, LDOPEN, 0, 0); tp->t_state &= ~WOPEN; tp->t_state |= ISOPEN; } ttclose(tp) register struct tty *tp; { if ((tp->t_state&ISOPEN) == 0) return; tp->t_state &= ~ISOPEN; tp->t_pgrp = 0; ttioctl(tp, LDCLOSE, 0, 0); } /* * Called from device's read routine after it has * calculated the tty-structure given as argument. */ ttread(tp) register struct tty *tp; { register struct user *up; register struct clist *tq; up = &u; tq = &tp->t_canq; if (tq->c_cc == 0) canon(tp); while (up->u_count!=0 && up->u_error==0) { if (up->u_count >= CLSIZE) { register n; register struct cblock *cp; if ((cp = getcb(tq)) == NULL) break; n = MIN(up->u_count, (unsigned)(cp->c_last - cp->c_first)); if (copyout((caddr_t)&cp->c_data[cp->c_first], (caddr_t)up->u_base, n)) up->u_error = EFAULT; putcf((struct cblock *)cp); up->u_base += n; up->u_count -= n; } else { register c; if ((c = getc(tq)) < 0) break; if (subyte(up->u_base++, c)) up->u_error = EFAULT; up->u_count--; } } if (tp->t_state&TBLOCK) { if (tp->t_rawq.c_cct_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; { register struct cblock *cp; register struct user *up; register a, n; if (!(tp->t_state&CARR_ON)) return; up = &u; a = tthiwat[tp->t_cflag&CBAUD]; if (up->u_count <= 4 && up->u_ar0[R0] & 0x80000000) { n = up->u_arg[3]; SPL6(); while (tp->t_outq.c_cc > a) { (*tp->t_proc)(tp, T_OUTPUT); /* * For non-interrupting output devices sleep * only when characters are still pending. */ if (tp->t_state&(TIMEOUT|TTSTOP|BUSY)) { tp->t_state |= OASLP; (void) sleep((caddr_t)&tp->t_outq, TTOPRI); } } SPL0(); while (up->u_count) { ttxput(tp, n&0xFF, 0); up->u_base++; up->u_count--; n >>= 8; } } else while (up->u_count) { SPL6(); while (tp->t_outq.c_cc > a) { (*tp->t_proc)(tp, T_OUTPUT); /* * For non-interrupting output devices sleep * only when characters are still pending. */ if (tp->t_state&(TIMEOUT|TTSTOP|BUSY)) { tp->t_state |= OASLP; (void) sleep((caddr_t)&tp->t_outq, TTOPRI); } } SPL0(); if (up->u_count >= (CLSIZE/4)) { if ((cp = getcf()) == NULL) break; n = MIN(up->u_count, (unsigned)cp->c_last); if (copyin((caddr_t)up->u_base, (caddr_t)cp->c_data, n)) { up->u_error = EFAULT; putcf((struct cblock *)cp); break; } /* * Put trailing '\n' in a separate cblock */ if (n==up->u_count && cp->c_data[n-1]=='\n' && n>=3) n--; up->u_base += n; up->u_count -= n; cp->c_last = n; ttxput(tp, cp, n); } else { n = fubyte(up->u_base++); if (n<0) { up->u_error = EFAULT; break; } up->u_count--; ttxput(tp, n, 0); } } spltty(); if (!(tp->t_state&BUSY)) (*tp->t_proc)(tp, T_OUTPUT); SPL0(); } /* * Place a character on raw TTY input queue, putting in delimiters * and waking up top half as needed. * Also echo if required. */ #define LCLESC 0400 ttin(tp) register struct tty *tp; { register c; register flg; register char *cp; ushort nchar, nc; nchar = tp->t_rbuf.c_size - tp->t_rbuf.c_count; /* reinit rx control block */ tp->t_rbuf.c_count = tp->t_rbuf.c_size; if (nchar==0) return; flg = tp->t_iflag; nc = nchar; cp = tp->t_rbuf.c_ptr; if (nc < cfreelist.c_size || (flg & (INLCR|IGNCR|ICRNL|IUCLC))) { /* must do per character processing */ for ( ;nc--; cp++) { c = *cp; if (c == '\n' && flg&INLCR) *cp = c = '\r'; else if (c == '\r') if (flg&IGNCR) continue; else if (flg&ICRNL) *cp = c = '\n'; if (flg&IUCLC && 'A' <= c && c <= 'Z') c += 'a' - 'A'; if (putc(c, &tp->t_rawq)) continue; sysinfo.rawch++; } cp = tp->t_rbuf.c_ptr; } else { /* may do block processing */ putcb(CMATCH((struct cblock *)cp), &tp->t_rawq); sysinfo.rawch += nc; /* allocate new rx buffer */ if ((tp->t_rbuf.c_ptr = getcf()->c_data) == ((struct cblock *)NULL)->c_data) { tp->t_rbuf.c_ptr = NULL; return; } tp->t_rbuf.c_count = cfreelist.c_size; tp->t_rbuf.c_size = cfreelist.c_size; } if (tp->t_rawq.c_cc > TTXOHI) { if (flg&IXOFF && !(tp->t_state&TBLOCK)) (*tp->t_proc)(tp, T_BLOCK); if (tp->t_rawq.c_cc > TTYHOG) { ttyflush(tp, FREAD); return; } } flg = lobyte(tp->t_lflag); if (tp->t_outq.c_cc > (tthiwat[tp->t_cflag&CBAUD] + TTECHI)) flg &= ~(ECHO|ECHOK|ECHONL|ECHOE); if (flg) while (nchar--) { c = *cp++; if (flg&ISIG) { if (c == tp->t_cc[VINTR]) { signal(tp->t_pgrp, SIGINT); if (!(flg&NOFLSH)) ttyflush(tp, (FREAD|FWRITE)); continue; } if (c == tp->t_cc[VQUIT]) { signal(tp->t_pgrp, SIGQUIT); if (!(flg&NOFLSH)) ttyflush(tp, (FREAD|FWRITE)); continue; } } #ifdef notdef if (flg&ICANON) { if (tp->t_state&CLESC) { flg |= LCLESC; tp->t_state &= ~CLESC; } if (c == '\n') { if (flg&ECHONL) flg |= ECHO; tp->t_delct++; } else if (c == '\\') { tp->t_state |= CLESC; if (flg&XCASE) { c |= QESC; if (flg&LCLESC) tp->t_state &= ~CLESC; } } else if (c == tp->t_cc[VEOL] || c == tp->t_cc[VEOL2]) tp->t_delct++; else if (!(flg&LCLESC)) { if (c == tp->t_cc[VERASE] && flg&ECHOE) { if (flg&ECHO) ttxput(tp, '\b', 0); flg |= ECHO; ttxput(tp, ' ', 0); c = '\b'; } else if (c == tp->t_cc[VKILL] && flg&ECHOK) { if (flg&ECHO) ttxput(tp, c, 0); flg |= ECHO; c = '\n'; } else if (c == tp->t_cc[VEOF]) { flg &= ~ECHO; tp->t_delct++; } } } if (flg&ECHO) { ttxput(tp, c, 0); (*tp->t_proc)(tp, T_OUTPUT); } #else if (flg&ICANON) { if (c == '\n') { if (flg&ECHONL) flg |= ECHO; tp->t_delct++; } else if (c == tp->t_cc[VEOL] || c == tp->t_cc[VEOL2]) tp->t_delct++; if (!(tp->t_state&CLESC)) { if (c == '\\') tp->t_state |= CLESC; if (c == tp->t_cc[VERASE] && flg&ECHOE) { if (flg&ECHO) ttxput(tp, '\b', 0); flg |= ECHO; ttxput(tp, ' ', 0); c = '\b'; } else if (c == tp->t_cc[VKILL] && flg&ECHOK) { if (flg&ECHO) ttxput(tp, c, 0); flg |= ECHO; c = '\n'; } else if (c == tp->t_cc[VEOF]) { flg &= ~ECHO; tp->t_delct++; } } else { if (c != '\\' || (flg&XCASE)) tp->t_state &= ~CLESC; } } if (flg&ECHO) { ttxput(tp, c, 0); (*tp->t_proc)(tp, T_OUTPUT); } #endif } if (!(flg&ICANON)) { tp->t_state &= ~RTO; if (tp->t_rawq.c_cc >= tp->t_cc[VMIN]) tp->t_delct = 1; else if (tp->t_cc[VTIME]) { if (!(tp->t_state&TACT)) tttimeo(tp); } } if (tp->t_delct && (tp->t_state&IASLP)) { tp->t_state &= ~IASLP; wakeup((caddr_t)&tp->t_rawq); } } /* * Scan a list of characters and assure that they require no * post processing */ ttxchk(ncode, cp) register short ncode; register unsigned char *cp; { register c, n; n = 0; ncode--; do { c = *cp++; if (c & 0200) return(-1); c = partab[c] & 077; if (c == 0) n++; else if (c != 1) return(-1); } while (--ncode != -1); return(n); } /* * Put character(s) on TTY output queue, adding delays, * expanding tabs, and handling the CR/NL bit. * It is called both from the base level for output, and from * interrupt level for echoing. */ /* VARARGS1 */ ttxput(tp, ucp, ncode) register struct tty *tp; register ncode; union { struct ch { /* machine dependent union */ char dum[3]; unsigned char theaddr; } ch; int thechar; struct cblock *ptr; } ucp; { register struct clist *outqp; register unsigned char *cp; register c, flg, ctype; register char *colp; struct cblock *scf; int cs; flg = tp->t_oflag; outqp = &tp->t_outq; if (ncode == 0) { if (!(flg&OPOST)) { sysinfo.outch++; (void) putc(ucp.thechar, outqp); return; } ncode++; cp = (unsigned char *)&ucp.ch.theaddr; scf = NULL; } else { if (!(flg&OPOST)) { sysinfo.outch += ncode; putcb(ucp.ptr, outqp); return; } cp = (unsigned char *)&ucp.ptr->c_data[ucp.ptr->c_first]; scf = ucp.ptr; } if ((tp->t_lflag&XCASE)==0 && (flg&OLCUC)==0) { colp = &tp->t_col; if (ncode > 1 && (c = ttxchk(ncode, cp)) >= 0) { (*colp) += c; sysinfo.outch += ncode; putcb(ucp.ptr, outqp); return; } while (ncode--) { ctype = partab[c = *cp++] & 077; if (ctype==0) { (*colp)++; sysinfo.outch++; (void) putc(c, outqp); continue; } else if (ctype==1) { sysinfo.outch++; (void) putc(c, outqp); continue; } if (c >= 0200) { if (c == QESC) (void) putc(QESC, outqp); sysinfo.outch++; (void) putc(c, outqp); continue; } cs = c; /* * Calculate delays. * The numbers here represent clock ticks * and are not necessarily optimal for all terminals. * The delays are indicated by characters above 0200. */ c = 0; switch (ctype) { case 0: /* ordinary */ (*colp)++; case 1: /* non-printing */ break; case 2: /* backspace */ if (flg&BSDLY) c = 2; if (*colp) (*colp)--; break; case 3: /* line feed */ if (flg&ONLRET) goto qcr; if (flg&ONLCR) { if (!(flg&ONOCR && *colp==0)) { sysinfo.outch++; (void) putc('\r', outqp); } goto qcr; } qnl: if (flg&NLDLY) c = 5; break; case 4: /* tab */ c = 8 - ((*colp)&07); *colp += c; ctype = flg&TABDLY; if (ctype == TAB0) { c = 0; } else if (ctype == TAB1) { if (c < 5) c = 0; } else if (ctype == TAB2) { c = 2; } else if (ctype == TAB3) { sysinfo.outch += c; do (void) putc(' ', outqp); while (--c); continue; } break; case 5: /* vertical tab */ if (flg&VTDLY) c = 0177; break; case 6: /* carriage return */ if (flg&OCRNL) { cs = '\n'; goto qnl; } if (flg&ONOCR && *colp == 0) continue; qcr: ctype = flg&CRDLY; if (ctype == CR1) { if (*colp) c = max((unsigned)((*colp>>4) + 3), 6); } else if (ctype == CR2) { c = 5; } else if (ctype == CR3) { c = 9; } *colp = 0; break; case 7: /* form feed */ if (flg&FFDLY) c = 0177; break; } sysinfo.outch++; (void) putc(cs, outqp); if (c) { if ((c < 32) && flg&OFILL) { if (flg&OFDEL) cs = 0177; else cs = 0; (void) putc(cs, outqp); if (c > 3) (void) putc(cs, outqp); } else { (void) putc(QESC, outqp); (void) putc(c|0200, outqp); } } } } else while (ncode--) { c = *cp++; if (c >= 0200) { /* spl5-0 */ if (c == QESC) (void) putc(QESC, outqp); sysinfo.outch++; (void) putc(c, outqp); continue; } /* * Generate escapes for upper-case-only terminals. */ if (tp->t_lflag&XCASE) { colp = "({)}!|^~'`\\\\"; while(*colp++) if (c == *colp++) { ttxput(tp, '\\'|0200, 0); c = colp[-2]; break; } if ('A' <= c && c <= 'Z') ttxput(tp, '\\'|0200, 0); } if (flg&OLCUC && 'a' <= c && c <= 'z') c += 'A' - 'a'; cs = c; /* * Calculate delays. * The numbers here represent clock ticks * and are not necessarily optimal for all terminals. * The delays are indicated by characters above 0200. */ ctype = partab[c]; colp = &tp->t_col; c = 0; switch (ctype&077) { case 0: /* ordinary */ (*colp)++; case 1: /* non-printing */ break; case 2: /* backspace */ if (flg&BSDLY) c = 2; if (*colp) (*colp)--; break; case 3: /* line feed */ if (flg&ONLRET) goto cr; if (flg&ONLCR) { if (!(flg&ONOCR && *colp==0)) { sysinfo.outch++; (void) putc('\r', outqp); } goto cr; } nl: if (flg&NLDLY) c = 5; break; case 4: /* tab */ c = 8 - ((*colp)&07); *colp += c; ctype = flg&TABDLY; if (ctype == TAB0) { c = 0; } else if (ctype == TAB1) { if (c < 5) c = 0; } else if (ctype == TAB2) { c = 2; } else if (ctype == TAB3) { sysinfo.outch += c; do (void) putc(' ', outqp); while (--c); continue; } break; case 5: /* vertical tab */ if (flg&VTDLY) c = 0177; break; case 6: /* carriage return */ if (flg&OCRNL) { cs = '\n'; goto nl; } if (flg&ONOCR && *colp == 0) continue; cr: ctype = flg&CRDLY; if (ctype == CR1) { if (*colp) c = max((unsigned)((*colp>>4) + 3), 6); } else if (ctype == CR2) { c = 5; } else if (ctype == CR3) { c = 9; } *colp = 0; break; case 7: /* form feed */ if (flg&FFDLY) c = 0177; break; } sysinfo.outch++; (void) putc(cs, outqp); if (c) { if ((c < 32) && flg&OFILL) { if (flg&OFDEL) cs = 0177; else cs = 0; (void) putc(cs, outqp); if (c > 3) (void) putc(cs, outqp); } else { (void) putc(QESC, outqp); (void) putc(c|0200, outqp); } } } if (scf != NULL) putcf(scf); } /* * Get next packet from output queue. * Called from xmit interrupt complete. */ ttout(tp) register struct tty *tp; { register struct ccblock *tbuf; register c; register char *cptr; register retval; register struct clist *outqp; extern ttrstrt(); outqp = &tp->t_outq; if (tp->t_state&TTIOW && outqp->c_cc==0) { tp->t_state &= ~TTIOW; wakeup((caddr_t)&tp->t_oflag); } delay: tbuf = &tp->t_tbuf; if (hibyte(tp->t_lflag)) { if (tbuf->c_ptr) { putcf(CMATCH((struct cblock *)tbuf->c_ptr)); tbuf->c_ptr = NULL; } tp->t_state |= TIMEOUT; timeout(ttrstrt, (caddr_t)tp, (int)((hibyte(tp->t_lflag)&0177)+6)); hibyte(tp->t_lflag) = 0; return(0); } retval = 0; if (!(tp->t_oflag&OPOST)) { if (tbuf->c_ptr) putcf(CMATCH((struct cblock *)tbuf->c_ptr)); if ((tbuf->c_ptr = (char *)getcb(outqp)) == NULL) goto out; /* return(0); */ tbuf->c_count = ((struct cblock *)tbuf->c_ptr)->c_last - ((struct cblock *)tbuf->c_ptr)->c_first; tbuf->c_size = tbuf->c_count; tbuf->c_ptr = &((struct cblock *)tbuf->c_ptr)->c_data [((struct cblock *)tbuf->c_ptr)->c_first]; retval = CPRES; } else { /* watch for timing */ if (tbuf->c_ptr == NULL) { if ((tbuf->c_ptr = getcf()->c_data) == ((struct cblock *)NULL)->c_data) { tbuf->c_ptr = NULL; goto out; /* return(0); /* Add restart? */ } } tbuf->c_count = 0; cptr = tbuf->c_ptr; while ((c=getc(outqp)) >= 0) { if (c == QESC) { if ((c = getc(outqp)) < 0) break; if (c > 0200) { hibyte(tp->t_lflag) = c; if (!retval) goto delay; break; } } retval = CPRES; *cptr++ = c; tbuf->c_count++; if (tbuf->c_count >= cfreelist.c_size) break; } tbuf->c_size = tbuf->c_count; } out: if (tp->t_state&OASLP && outqp->c_cc<=ttlowat[tp->t_cflag&CBAUD]) { tp->t_state &= ~OASLP; wakeup((caddr_t)outqp); } return(retval); } tttimeo(tp) register struct tty *tp; { tp->t_state &= ~TACT; if (tp->t_lflag&ICANON || tp->t_cc[VTIME] == 0) return; if (tp->t_rawq.c_cc == 0 && tp->t_cc[VMIN]) return; if (tp->t_state&RTO) { tp->t_delct = 1; if (tp->t_state&IASLP) { tp->t_state &= ~IASLP; wakeup((caddr_t)&tp->t_rawq); } } else { tp->t_state |= RTO|TACT; timeout(tttimeo, (caddr_t)tp, (int)(tp->t_cc[VTIME]*(short)((short)v.v_hz/10))); } } /* * I/O control interface */ /* ARGSUSED */ ttioctl(tp, cmd, arg, mode) register struct tty *tp; { ushort chg; switch(cmd) { case LDOPEN: if (tp->t_rbuf.c_ptr == NULL) { /* allocate RX buffer */ while((tp->t_rbuf.c_ptr = getcf()->c_data) == ((struct cblock *)NULL)->c_data) { tp->t_rbuf.c_ptr = NULL; cfreelist.c_flag = 1; (void) sleep((caddr_t)&cfreelist, TTOPRI); } tp->t_rbuf.c_count = cfreelist.c_size; tp->t_rbuf.c_size = cfreelist.c_size; (*tp->t_proc)(tp, T_INPUT); } break; case LDCLOSE: spltty(); (*tp->t_proc)(tp, T_RESUME); SPL0(); ttywait(tp); ttyflush(tp, (FREAD|FWRITE)); if (tp->t_tbuf.c_ptr) { putcf(CMATCH((struct cblock *)tp->t_tbuf.c_ptr)); tp->t_tbuf.c_ptr = NULL; tp->t_tbuf.c_count = 0; tp->t_tbuf.c_size = 0; } if (tp->t_rbuf.c_ptr) { putcf(CMATCH((struct cblock *)tp->t_rbuf.c_ptr)); tp->t_rbuf.c_ptr = NULL; tp->t_rbuf.c_count = 0; tp->t_rbuf.c_size = 0; } tp->t_tmflag = 0; break; case LDCHG: chg = tp->t_lflag^arg; if (!(chg&ICANON)) break; spltty(); if (tp->t_canq.c_cc) { if (tp->t_rawq.c_cc) { tp->t_canq.c_cc += tp->t_rawq.c_cc; tp->t_canq.c_cl->c_next = tp->t_rawq.c_cf; tp->t_canq.c_cl = tp->t_rawq.c_cl; } tp->t_rawq = tp->t_canq; tp->t_canq = ttnulq; } tp->t_delct = tp->t_rawq.c_cc; SPL0(); break; default: break; } }