V8/usr/sys/chunix/chtty.c

Compare this file to the similar file:
Show the results in this format:

#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");
	}
}