#include "../chunix/chsys.h" #include "../chunix/chconf.h" #include "../chaos/chaos.h" #include "../chaos/dev.h" #include "../h/inode.h" #include "../h/file.h" #include "../h/tty.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/conf.h" /* * This file contains functions for treating a chaos channel as a UNIX tty */ #include "cht.h" struct tty cht_tty[NCHT * 16]; int cht_cnt = NCHT * 16; /* * Do the additional work necessary to open a channel as a UNIX tty. * Basically the existence of an associated connection is much like * the existence of a carrier. */ /* ARGSUSED */ chtopen(dev, flag) dev_t dev; { register struct tty *tp; register int unit; int chtstart(), chtinput(); unit = minor(dev); if (unit >= cht_cnt) { u.u_error = ENXIO; return; } tp = &cht_tty[unit]; if (tp->t_state & XCLUDE && u.u_uid != 0) { u.u_error = EBUSY; return; } tp->t_state |= WOPEN; tp->t_oproc = chtstart; tp->t_iproc = chtinput; if ((tp->t_state&ISOPEN) == 0) { ttychars(tp); tp->t_ispeed = B9600; tp->t_ospeed = B9600; tp->t_flags = ODDP|EVENP|ECHO; tp->t_state |= HUPCLS; } tp->t_lstate |= LSCHAOS; LOCK; /* * Since ttyclose forces CARR_ON off, we turn it on again if * the connection is still around. */ if (tp->t_addr) tp->t_state |= CARR_ON; else while ((tp->t_state & CARR_ON) == 0) sleep((caddr_t)&tp->t_rawq, TTIPRI); UNLOCK; tp->t_line = NTTYDISC; (*linesw[tp->t_line].l_open)(dev, tp); } /* * Close the tty associated with the given connection. */ chtclose(dev, flag) dev_t dev; int flag; { register struct connection *conn; register struct tty *tp; tp = &cht_tty[minor(dev)]; (*linesw[tp->t_line].l_close)(tp); conn = (struct connection *)tp->t_addr; if (tp->t_state & HUPCLS || conn->cn_state != CSOPEN) { /* * We call the main close routine in RECORD mode, which * closes the connection directly. */ conn->cn_mode = CHRECORD; chclose(conn, flag); tp->t_addr = 0; tp->t_state &= ~CARR_ON; tp->t_lstate &= ~LSCHAOS; } ttyclose(tp); } /* * Read from a chaos channel that is a tty. */ chtread(dev) dev_t dev; { register struct tty *tp = &cht_tty[minor(dev)]; register struct connection *conn = (struct connection *)tp->t_addr; /* * Since ttys are quite possibly interactive, be sure * to flush any output when input is desired. It would * be soon anyway due to timeouts. */ spl6(); if (conn->cn_toutput != NOPKT && !chtfull(conn)) ch_sflush(conn); spl0(); (*linesw[tp->t_line].l_read)(tp); } int chttyraw; /* * Write to a chaos channel that is a tty. */ chtwrite(dev) dev_t dev; { register struct tty *tp = &cht_tty[minor(dev)]; if (tp->t_flags & RAW) { chttyraw++; chwrite((struct connection *)tp->t_addr); } else (*linesw[tp->t_line].l_write)(tp); } /* * We only allow tty ioctl's for a chaos tty. * We could also allow chaos ioctl's if needed. */ chtioctl(dev, cmd, addr, flag) dev_t dev; caddr_t addr; { register struct tty *tp = &cht_tty[minor(dev)]; cmd = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr); if (cmd == 0) return; if (ttioctl(tp, cmd, addr, flag)) { if (cmd==TIOCSETP || cmd==TIOCSETN) { /* * Send virtual terminal codes here? */ /* chparam(conn); */ } } else switch(cmd) { /* * We need a remote terminal protocol here. - Yick. */ case TIOCSBRK: /* maybe someday a code for break on output ? */ break; case TIOCCBRK: break; case TIOCSDTR: break; case TIOCCDTR: break; default: u.u_error = ENOTTY; } } /* * Interrupt routine called when a new packet is available for a tty channel * Basically empty the packet into the tty system. * Called both from interrupt level when the read packet queue becomes * non-empty and also (at high priority) from top level. THe top level * call is needed to retrieve data not queued at interrupt time due to * input queue high water mark reached. */ chtrint(conn) struct connection *conn; { register struct packet *pkt; register struct tty *tp = conn->cn_ttyp; register char *cp; while ((pkt = conn->cn_rhead) != NOPKT) { if (ISDATOP(pkt) && conn->cn_state == CSOPEN) for (cp = &pkt->pk_cdata[conn->cn_roffset]; pkt->pk_len != 0; pkt->pk_len--) if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG) { conn->cn_roffset = cp - pkt->pk_cdata; return; } else (*linesw[tp->t_line].l_rint) (*cp++ & 0377, tp); ch_read(conn); } /* * Flush any output since we might have echoed something at * interrupt level. */ if (conn->cn_toutput != NOPKT) ch_sflush(conn); } /* * Get more data if possible. This is called from ttread (ntread) to * see if more data can be read from the connection. * It is only needed to account for the case where packets are not * completely emptied at interrupt level due to input clist buffer * overflow (>TTYHOG). In this respect, chaos connections win better * than ttys, which just throw the data away. */ chtinput(tp) register struct tty *tp; { register int opl = spl6(); chtrint((struct connection *)tp->t_addr); splx(opl); } /* * Interrupt routine called when more packets can again be sent after things * block due to window full. */ chtxint(conn) register struct connection *conn; { register struct tty *tp = conn->cn_ttyp; register int s = spl6(); if (tp->t_state & ASLEEP) { tp->t_state &= ~ASLEEP; wakeup((caddr_t)&tp->t_outq); } if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else chtstart(tp); splx(s); } /* * Are we output flow controlled? */ chtblocked(tp) struct tty *tp; { register struct connection *conn = (struct connection *)tp->t_addr; return chtfull(conn); } /* * Are we empty on output? */ chtnobuf(tp) struct tty *tp; { register struct connection *conn = (struct connection *)tp->t_addr; return !conn->cn_toutput; } /* * Flush any buffered output that we can. * Called from high priority. */ chtflush(tp) struct tty *tp; { register struct connection *conn = (struct connection *)tp->t_addr; if (conn->cn_toutput) { ch_free((caddr_t)conn->cn_toutput); conn->cn_toutput = NOPKT; } } /* * Start sending any buffered output. * We just start sending any packet that is partially full. */ chtstart(tp) struct tty *tp; { ch_sflush((struct connection *)tp->t_addr); } /* * Put out one character and return non-zero if we couldn't */ chtputc(c, tp) char c; struct tty *tp; { return chtout(&c, 1, tp); } /* * Send a contiguous array of bytes. * Return the number we can't accept now. * Packet allocation strategy: * We allocate a packet that is at least large enough to hold all bytes * remaining to be sent in this system call. We rely on the fact that * packets are really powers of two in size and at least 16 bytes of data, * since we don't round up our request at all. * If our "clever" request fails, we try for a small packet. */ chtout(cp, cc, tp) register char *cp; struct tty *tp; { register struct connection *conn = (struct connection *)tp->t_addr; register struct packet *pkt; register int n; int sps = spl6(); extern int ttrstrt(); if (conn->cn_state == CSOPEN && (tp->t_state & (TIMEOUT|BUSY|TTSTOP)) == 0) { while (cc != 0) { if ((pkt = conn->cn_toutput) == NOPKT) { n = chtfull(conn) ? CHMAXDATA : chroundup(u.u_count + cc); if ((pkt = pkalloc(n, 1)) == NOPKT) { n = CHMINDATA; if ((pkt = pkalloc(n, 1)) == NOPKT) break; } pkt->pk_op = DATOP; pkt->pk_type = 0; pkt->pk_lenword = 0; conn->cn_troom = n; conn->cn_toutput = pkt; } if ((n = cc) > conn->cn_troom) n = conn->cn_troom; if (n != 0) { chmove(cp, &pkt->pk_cdata[pkt->pk_lenword], n); pkt->pk_time = Chclock; pkt->pk_lenword += n; cp += n; } cc -= n; if ((conn->cn_troom -= n) == 0) if (chtfull(conn)) break; else ch_sflush(conn); } } splx(sps); return (cc); } /* * Process a connection state change for a tty. */ chtnstate(conn) register struct connection *conn; { register struct tty *tp; tp = conn->cn_ttyp; switch (conn->cn_state) { /* * This shouldn't really happen since the connection shouldn't * become a tty until it is open. */ case CSOPEN: if ((tp->t_state & CARR_ON) == 0) { wakeup((caddr_t)&tp->t_rawq); tp->t_state |= CARR_ON; } break; case CSCLOSED: case CSINCT: case CSLOST: if (tp->t_state & CARR_ON) { if ((tp->t_local & LNOHANG) == 0 && tp->t_state & ISOPEN) { gsignal(tp->t_pgrp, SIGHUP); #ifdef SIGCONT gsignal(tp->t_pgrp, SIGCONT); #endif flushtty(tp, FREAD|FWRITE); } tp->t_state &= ~CARR_ON; } else if (!(tp->t_state & ISOPEN)) { tp->t_addr = 0; ch_close(conn, NOPKT, 1); ch_buffree(); } break; default: panic("chtnstate"); } }