/* pty.c 4.21 82/03/23 */ /* * Pseudo-teletype Driver * (Actually two drivers, requiring two entries in 'cdevsw') */ /* * billn -- 12/15/82. Mercilessly hacked for system 3. To do * Remote input editing, etc, need to rework it again * from the original. */ #include "net/pty.h" #if NPTY > 0 #include "sys/param.h" #include "sys/config.h" #include "sys/types.h" #include "sys/mmu.h" #include "sys/sysmacros.h" #include "sys/systm.h" #include "sys/tty.h" #include "sys/ttold.h" #include "sys/termio.h" #include "sys/ioctl.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/errno.h" #include "sys/user.h" #include "sys/conf.h" #include "sys/buf.h" #include "sys/file.h" #include "sys/proc.h" #include "sys/var.h" #include "net/misc.h" #define BUFSIZ 100 /* Chunk size iomoved from user */ /* * pts == /dev/tty[pP]? * ptc == /dev/pty[pP]? */ struct tty pt_tty[NPTY]; struct pt_ioctl { int pt_flags; int pt_gensym; struct proc *pt_selr, *pt_selw; int pt_send; } pt_ioctl[NPTY]; #define PF_RCOLL 0x01 #define PF_WCOLL 0x02 #define PF_NBIO 0x04 #define PF_PKT 0x08 /* packet mode */ #define PF_STOPPED 0x10 /* user told stopped */ #define PF_REMOTE 0x20 /* remote and flow controlled input */ #define PF_NOSTOP 0x40 #define PF_WTIMER 0x80 /* waiting for timer to flush */ /* billn -- kludge */ #define PF_PTSOPEN 0x100 /* pts side is open */ #define PF_PTCOPEN 0x200 /* ptc side is open */ /*ARGSUSED*/ ptsopen(dev, flag) dev_t dev; { register struct tty *tp; register struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; if (dev >= NPTY) { u.u_error = ENXIO; return; } tp = &pt_tty[dev]; if ((tp->t_state & ISOPEN) == 0) { /* No features (nor raw mode) */ tp->t_cflag = 0; tp->t_oflag = 0; tp->t_iflag = 0; tp->t_lflag = 0; ttinit(tp); /* Set up default chars */ } if (tp->t_proc) /* Ctrlr still around. */ tp->t_state |= CARR_ON; while ((tp->t_state & CARR_ON) == 0) { tp->t_state |= WOPEN; (void) sleep((caddr_t)&tp->t_rawq, TTIPRI); } (*linesw[tp->t_line].l_open)(tp); pti->pt_flags |= PF_PTSOPEN; } ptsclose(dev) dev_t dev; { register struct tty *tp; register struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; tp = &pt_tty[dev]; (*linesw[tp->t_line].l_close)(tp); pti->pt_flags &= ~PF_PTSOPEN; if ((pti->pt_flags & PF_PTCOPEN) == 0) /* other end already gone? */ tp->t_proc = 0; } ptsread(dev) dev_t dev; { register struct tty *tp = &pt_tty[dev]; register struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; if (tp->t_proc) (*linesw[tp->t_line].l_read)(tp); wakeup((caddr_t)&tp->t_rawq.c_cf); if (pti->pt_selw) { selwakeup(pti->pt_selw, pti->pt_flags & PF_WCOLL); pti->pt_selw = 0; pti->pt_flags &= ~PF_WCOLL; } } /* * Write to pseudo-tty. * Wakeups of controlling tty will happen * indirectly, when tty driver calls ptsstart. */ ptswrite(dev) dev_t dev; { register struct tty *tp; tp = &pt_tty[dev]; if (tp->t_proc) (*linesw[tp->t_line].l_write)(tp); } ptcwakeup(tp) struct tty *tp; { struct pt_ioctl *pti = &pt_ioctl[tp - &pt_tty[0]]; int s = spl5(); /* any NZ spl will lockout ptctimer */ if (pti->pt_selr) { selwakeup(pti->pt_selr, pti->pt_flags & PF_RCOLL); pti->pt_selr = 0; pti->pt_flags &= ~PF_RCOLL; } if (pti->pt_selw) { selwakeup(pti->pt_selw, pti->pt_flags & PF_WCOLL); pti->pt_selw = 0; pti->pt_flags &= ~PF_WCOLL; } wakeup((caddr_t)&tp->t_outq.c_cf); splx(s); } ptctimer() { register struct tty *tp = &pt_tty[0]; register struct pt_ioctl *pti = &pt_ioctl[0]; register i; timeout(ptctimer, (caddr_t)0, v.v_hz >> 2); for (i=0; ipt_flags & PF_WTIMER) == 0) continue; pti->pt_flags &= ~PF_WTIMER; if (tp->t_proc == 0) continue; ptcwakeup(tp); } } /*ARGSUSED*/ ptcopen(dev, flag) dev_t dev; int flag; { register struct tty *tp; struct pt_ioctl *pti; static first; extern int ptsstart(); if (first == 0) { first++; ptctimer(); } if (dev >= NPTY) { u.u_error = ENXIO; return; } tp = &pt_tty[dev]; if (tp->t_proc) { u.u_error = EIO; return; } tp->t_iflag = ICRNL|ISTRIP|IGNPAR; tp->t_oflag = OPOST|ONLCR|TAB3; tp->t_lflag = ISIG|ICANON; /* no echo */ tp->t_proc = ptsstart; if (tp->t_state & WOPEN) wakeup((caddr_t)&tp->t_rawq); tp->t_state |= CARR_ON; pti = &pt_ioctl[dev]; pti->pt_flags = 0; pti->pt_send = 0; pti->pt_flags |= PF_PTCOPEN; } ptcclose(dev) dev_t dev; { register struct tty *tp; register struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; tp = &pt_tty[dev]; if (tp->t_state & ISOPEN) signal(tp->t_pgrp, SIGHUP); tp->t_state &= ~CARR_ON; /* virtual carrier gone */ ttyflush(tp, FREAD|FWRITE); pti->pt_flags &= ~PF_PTCOPEN; if ((pti->pt_flags & PF_PTSOPEN) == 0) /* other end already gone? */ tp->t_proc = 0; /* mark closed */ } ptcread(dev) dev_t dev; { register struct tty *tp; register struct pt_ioctl *pti; register c; tp = &pt_tty[dev]; if ((tp->t_state&(CARR_ON|ISOPEN)) == 0) return; pti = &pt_ioctl[dev]; if (pti->pt_flags & PF_PKT) { if (pti->pt_send) { (void) passc(pti->pt_send); pti->pt_send = 0; return; } (void) passc(0); } while (tp->t_outq.c_cc == 0 || (tp->t_state&TTSTOP)) { if (pti->pt_flags&PF_NBIO) { u.u_error = EWOULDBLOCK; return; } (void) sleep((caddr_t)&tp->t_outq.c_cf, TTIPRI); } while (tp->t_outq.c_cc && (c = getc(&tp->t_outq)) >= 0) if (passc(c) < 0) break; tp->t_state &= ~BUSY; if (tp->t_state&OASLP) { tp->t_state &= ~OASLP; wakeup((caddr_t)&tp->t_outq); } if (tp->t_state&TTIOW && tp->t_outq.c_cc==0) { tp->t_state &= ~TTIOW; wakeup((caddr_t)&tp->t_oflag); } } /* * System 5 does not have nbio normally */ /* #define IF_NBIO */ ptcwrite(dev) dev_t dev; { register struct tty *tp; register char *cp, *ce; register int cc, c, ctmp; char locbuf[BUFSIZ]; #ifdef IF_NBIO int cnt = 0; #endif tp = &pt_tty[dev]; if ((tp->t_state&(CARR_ON|ISOPEN)) == 0) return; do { cc = MIN(u.u_count, BUFSIZ); cp = locbuf; iomove(cp, cc, B_WRITE); if (u.u_error) break; ce = cp + cc; again: while (cp < ce) { while (tp->t_delct && tp->t_rawq.c_cc >= TTYHOG - 2) { wakeup((caddr_t)&tp->t_rawq); #ifdef IF_NBIO if (tp->t_state & TS_NBIO) { u.u_count += ce - cp; if (cnt == 0) u.u_error = EWOULDBLOCK; return; } #endif /* Better than just flushing it! */ /* Wait for something to be read */ (void) sleep((caddr_t)&tp->t_rawq.c_cf, TTOPRI); goto again; } c = *cp++; if (tp->t_iflag & IXON) { ctmp = c & 0177; if (tp->t_state & TTSTOP) { if (c == CSTART || tp->t_iflag & IXANY) (*tp->t_proc)(tp, T_RESUME); } else if (c == CSTOP) (*tp->t_proc)(tp, T_SUSPEND); if (c == CSTART || c == CSTOP) continue; } if (tp->t_rbuf.c_ptr != NULL) { if (tp->t_iflag&ISTRIP) c &= 0177; *tp->t_rbuf.c_ptr = c; tp->t_rbuf.c_count--; (*linesw[tp->t_line].l_input)(tp); } #ifdef IF_NBIO cnt++; #endif } } while (u.u_count); } ptcioctl(dev, cmd, addr, flag) caddr_t addr; dev_t dev; { register struct tty *tp = &pt_tty[minor(dev)]; register struct pt_ioctl *pti = &pt_ioctl[dev]; if (cmd == TIOCPKT) { int packet; if (copyin((caddr_t)addr, (caddr_t)&packet, sizeof (packet))) { u.u_error = EFAULT; return; } if (packet) pti->pt_flags |= PF_PKT; else pti->pt_flags &= ~PF_PKT; return; } if (cmd == FIONBIO) { int nbio; if (copyin((caddr_t)addr, (caddr_t)&nbio, sizeof (nbio))) { u.u_error = EFAULT; return; } if (nbio) pti->pt_flags |= PF_NBIO; else pti->pt_flags &= ~PF_NBIO; return; } /* IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG ??? */ if ((cmd==TIOCSETP)||(cmd==TCSETAW)) { while (getc(&tp->t_outq) >= 0); tp->t_state &= ~BUSY; } ptsioctl(dev, cmd, addr, flag); } /*ARGSUSED*/ ptsioctl(dev, cmd, addr, flag) register caddr_t addr; register dev_t dev; { register struct tty *tp = &pt_tty[dev]; register struct pt_ioctl *pti = &pt_ioctl[dev]; register int stop; if (ttiocom(tp, cmd, (int)addr, dev) == 0) ; /* else...?? */ stop = tp->t_iflag&IXON; if (pti->pt_flags & PF_NOSTOP) { if (stop) { pti->pt_send &= TIOCPKT_NOSTOP; pti->pt_send |= TIOCPKT_DOSTOP; pti->pt_flags &= ~PF_NOSTOP; ptcwakeup(tp); } } else { if (stop == 0) { pti->pt_send &= ~TIOCPKT_DOSTOP; pti->pt_send |= TIOCPKT_NOSTOP; pti->pt_flags |= PF_NOSTOP; ptcwakeup(tp); } } } ptsstart(tp, cmd) register struct tty *tp; { register struct pt_ioctl *pti = &pt_ioctl[tp - &pt_tty[0]]; extern ttrstrt(); switch(cmd) { case T_TIME: tp->t_state &= ~TIMEOUT; goto start; case T_WFLUSH: if (tp->t_outq.c_cc) { while (getc(&tp->t_outq) >= 0) ; tp->t_state &= ~BUSY; } /* fall through */ case T_RESUME: tp->t_state &= ~TTSTOP; wakeup((caddr_t)&tp->t_outq.c_cf); /* fall through */ case T_OUTPUT: start: if (tp->t_state&(TIMEOUT|TTSTOP|BUSY)) break; if (tp->t_state&TTIOW && tp->t_outq.c_cc==0) { tp->t_state &= ~TTIOW; wakeup((caddr_t)&tp->t_oflag); } if (pti->pt_flags & PF_STOPPED) { pti->pt_flags &= ~PF_STOPPED; pti->pt_send = TIOCPKT_START; } if (tp->t_outq.c_cc < 200) { pti->pt_flags |= PF_WTIMER; return; } pti->pt_flags &= ~PF_WTIMER; tp->t_state |= BUSY; ptcwakeup(tp); if (tp->t_state&OASLP && tp->t_outq.c_cc <= ttlowat[tp->t_cflag&CBAUD]) { tp->t_state &= ~OASLP; wakeup((caddr_t)&tp->t_outq); } break; case T_SUSPEND: tp->t_state |= TTSTOP; pti->pt_flags |= PF_STOPPED; pti->pt_send |= TIOCPKT_STOP; break; case T_BLOCK: tp->t_state |= TBLOCK; tp->t_state &= ~TTXON; if(tp->t_outq.c_cc > 0) wakeup((caddr_t)&tp->t_outq.c_cf); break; case T_RFLUSH: if (!(tp->t_state&TBLOCK)) break; case T_UNBLOCK: tp->t_state &= ~(TTXOFF|TBLOCK); if(tp->t_outq.c_cc > 0) wakeup((caddr_t)&tp->t_outq.c_cf); break; case T_BREAK: tp->t_state |= TIMEOUT; timeout(ttrstrt, (caddr_t)tp, v.v_hz/4); break; } } ptcselect(dev, rw) dev_t dev; int rw; { register struct tty *tp = &pt_tty[minor(dev)]; struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; struct proc *p; int s; if ((tp->t_state&(CARR_ON|ISOPEN)) == 0) return (1); /* ??? billn */ s = spl7(); switch (rw) { case FREAD: if (tp->t_outq.c_cc && (tp->t_state&TTSTOP) == 0) { splx(s); return (1); } if ((p = pti->pt_selr) && p->p_wchan == (caddr_t)&selwait) pti->pt_flags |= PF_RCOLL; else pti->pt_selr = u.u_procp; break; case FWRITE: splx(s); return 1; #ifdef notdef /* * only do this if using "PF_REMOTE" ala 4.1a . So why keep * it? -- history... */ if (tp->t_rawq.c_cc == 0) { splx(s); return (1); } if ((p = pti->pt_selw) && p->p_wchan == (caddr_t)&selwait) pti->pt_flags |= PF_WCOLL; else pti->pt_selw = u.u_procp; #endif break; } splx(s); return (0); } #endif