4.4BSD/usr/src/sys/vax/datakit/new/dktty.c.save

/*
 *  Datakit terminal driver
 *	SCCSID[] = "@(#)dktty.c	1.8 Garage 84/05/14"
 */

#include "dktty.h"
#if NDKTTY>0
#include "datakit.h"

#include "param.h"
#include "../machine/pte.h"
#include "syslog.h"
#include "errno.h"
#include "signal.h"
#include "conf.h"
#include "dir.h"
#include "user.h"
#include "proc.h"
#include "ioctl.h"
#include "tty.h"
#include "file.h"
#include "mbuf.h"
#include "uio.h"
#include "kernel.h"
#include "dkit.h"
#include "dk.h"
#include "dkdev.h"

extern int		dk_nchan;
extern struct dkdev	dkdev[];

struct tty	dkt[NDATAKIT];
caddr_t	dktibuf[NDATAKIT];	/* Input buffer pointers */
int	dktpaused[NDATAKIT];	/* delays for no output mbuf */
int	dktdelay[] = {		/* Time to wait on close before dropping line */
	4, 15, 15, 15, 15, 15, 15, 8,		/* B0-B300 */
	4, 2, 2, 2, 1, 1, 1, 1
};

int	dktstart();

static char dkt_tmr[16] = {
	15, 15, 15, 15, 15, 15, 15, 15,
	15, 9, 6, 4, 2, 1, 15, 15
} ;


/*
 * DKT control messages
 */
#define	D_BREAK	0110
#define	D_DELAY	0100

#define	DKTSPEED	B9600
#define	DKTFLAGS	(EVENP|ODDP|ECHO)

extern int dkdebug ;

#define	DEBUG	(dkdebug < 512)
#define devDEBUG 	(minor(dev) >= dkdebug)
#define chanDEBUG 	(chan >= dkdebug)
#define tpDEBUG 	((tp - dkt) >= dkdebug)

/*
 * Open a DKT line.
 */
dktopen(dev, flag)
{
	register struct tty	*tp;
	register struct dkdev	*dv;
	register		d;
	int chan;

	d = minor(dev);
	if (d >= dk_nchan) {
		if (DEBUG) log(LOG_ERR, "dkt_open(%d) error\n", dev);
		return ENXIO;
	}
	tp = &dkt[d];
	if ((tp->t_state&TS_XCLUDE) && u.u_uid!=0)
		return (EBUSY);
	if (!dktibuf[d]) {
		struct mbuf *mb;
		mb = m_get(M_WAIT, DKMT_ITTY);
		if (mb == NULL) return ENOBUFS;
		dktibuf[d] = mtod(mb, caddr_t);
	}
	if ((chan = dk_open(d, (int (*)()) NULL)) < 0) {
		return -chan;
	}

	tp->t_oproc = dktstart;
	tp->t_state |= (TS_WOPEN|TS_CARR_ON);
	dv = &dkdev[d];
	if ((tp->t_state&TS_ISOPEN) == 0) {
		ttychars(tp) ;
		if (tp->t_ispeed == 0) {
			tp->t_ispeed = tp->t_ospeed = DKTSPEED;
			tp->t_flags = DKTFLAGS;
		}
		if (devDEBUG) log(LOG_ERR, "DKT_open(%x,%o)\n",dev,flag);
	}
	dktfcon(tp);
	if (devDEBUG) log(LOG_ERR, "DKT_open(%x, %x) ok\n", dev, tp);
	dv->d_prot |= DpTTY;
	return (*linesw[tp->t_line].l_open)(dev, tp);
}

/*
 * Close a DKT line.
 */
/*ARGSUSED*/
dktclose(dev, flag)
dev_t dev;
int  flag;
{
	register struct tty	*tp;
	register struct dkdev	*dv;
	register int	d, s;
	extern int dktcflush(), dktrcv(), wakeup();

	d = minor(dev);
	tp = &dkt[d];
	dv = &dkdev[d];
	/*
	 * If called from exit(), give output 30 seconds to drain.
	 * Otherwise let output drain first.
	 */
	if(u.u_signal[SIGKILL] == SIG_IGN){
		s = spl5();
		timeout(dktcflush, (caddr_t) tp, 30*hz);
		ttywflush(tp) ;
		untimeout(dktcflush, (caddr_t) tp);
		tp->t_state &= ~TS_CARR_ON;
		if(dv->d_prot == DpTTY)	/* no other protocols open */
			dk_reset(d);
		splx(s);
	}

	(*linesw[tp->t_line].l_close)(tp);
	if (devDEBUG) log(LOG_ERR, "DKT_clos(%x)\n",dev);
	dv->d_prot &= ~DpTTY;
	tp->t_state &= ~TS_CARR_ON;
	/* Wait for output to drain on far end */
	if (dktdelay[tp->t_ispeed] > 0) {
		timeout(wakeup, (caddr_t) tp, dktdelay[tp->t_ispeed] * hz);
		sleep((caddr_t) tp, TTIPRI);
	}
	if(!dv->d_prot){
		(void) dk_close(d);
		(void) dk_takedown(d);
		dv->d_state = 0;
	}
	else (void) dk_rabort(d, dktrcv, (caddr_t) tp);
	ttyclose(tp);
	s = spl5();
	if (dktibuf[d]) {
		(void) m_free(dtom(dktibuf[d]));
		dktibuf[d] = NULL;
	}
	splx(s);
}

static
dktcflush(tp)
	struct tty *tp;
{
	ttyflush(tp, (FREAD|FWRITE)) ;
}

/*
 * Read from a DKT line.
 */
dktread(dev, uio)
struct uio *uio;
{
	register struct tty *tp;
	int err;

	if (devDEBUG) log(LOG_ERR, "dktread(%x) %d\n", dev, uio->uio_resid) ;
	tp = &dkt[minor(dev)];
	err = (*linesw[tp->t_line].l_read)(tp, uio);
	if (devDEBUG)
		log(LOG_ERR, "dktread done(%x) %d err=%d\n", dev, uio->uio_resid, err) ;
	dktfcon(tp);
	return err;
}

/*
 * Write on a DKT line
 */
dktwrite(dev, uio)
struct uio *uio;
{
	register struct tty *tp;

	if (devDEBUG) log(LOG_ERR, "dktwrite(%x)\n",dev);
	tp = &dkt[minor(dev)];
	return (*linesw[tp->t_line].l_write)(tp, uio);
}

/*
 * Receive a packet
 */
/*ARGSUSED*/
dktrcv(tp, chan, resid, rmode, rctl)
register struct tty *tp ;
{
	register c ;
	register char *cp ;
	register count ;

	if ((rmode & DKR_ABORT) || (dk_status(chan) & DK_RESET)) {
		dktshut(tp) ;
		return ;
	}
	/* Process input data */
	if (tp->t_state&TS_ISOPEN) {
		cp = dktibuf[tp-dkt];
		count = MLEN - resid ;
		if (count) {
			do {
				/* Should really do parity checking... */
				(*linesw[tp->t_line].l_rint)((*cp++)&0377, tp) ;
			} while (--count);
		}
		if ((c = (rctl & 0377)) != 0) {
			if (chanDEBUG) log(LOG_ERR, "DKT_ctl 0%o on %d\n",c,chan);
			if (c==D_BREAK) {
				/*
				 * At framing error (break) generate
				 * a null (in raw mode, for getty), or a
				 * interrupt (in cooked/cbreak mode).
				 */
				if (tp->t_flags&RAW)
					c = 0;
				else
					c = tp->t_intrc;
				(*linesw[tp->t_line].l_rint)(c, tp) ;
			}
		}
	}
	dktfcon(tp) ;
}


/*
 * Input flow control:  queue another receive unless to many chars waiting
 */
dktfcon(tp)
register struct tty *tp;
{
	register int d = tp - dkt;
	register x;

	if ((dk_status(d) & (DK_RCV|DK_OPEN)) != DK_OPEN)
		return ;
	if (dktibuf[d] == NULL) return;
	x = tp->t_rawq.c_cc + tp->t_canq.c_cc;
	if (x >= TTYHOG/2 && (tp->t_delct>0 || (tp->t_flags&(RAW|CBREAK))))
		return;
	(void) dk_recv(d, dktibuf[d], MLEN,
	    DKR_BLOCK | DKR_TIME | (dkt_tmr[tp->t_ispeed]<<8),
	    dktrcv, (caddr_t) tp) ;
}

/*
 * stty/gtty for DKT
 */
dktioctl(dev, cmd, data, flag)
caddr_t data;
{
	register struct tty *tp;
	int error;

	tp = &dkt[minor(dev)];
	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
	if (error >= 0)
		return error;
	error = ttioctl(tp, cmd, data, flag);
	if (error >= 0) {
		if (tp->t_ispeed == 0) {
			tp->t_state &= ~TS_CARR_ON;
			if (devDEBUG) log(LOG_ERR, "DKT_ioctl carr off\n");
			gsignal(tp->t_pgrp, SIGHUP);
			gsignal(tp->t_pgrp, SIGCONT);
		}
		return (error);
	}

	switch(cmd) {
		case TIOCSBRK:
			dktxpack(tp-dkt, D_BREAK) ;
			return 0;
		case TIOCCBRK:
			return 0;
	}
	return ENOTTY;
}

/*
 * Start (restart) transmission on the given DKT line.
 */
dktstart(tp)
register struct tty *tp;
{
	register d;
	char delay;
	extern dktxdun() ;
	int s, c;
	register int nch;
	register struct mbuf *m;
	extern ttrstrt();

	d = tp - dkt;
	s = spl5() ;

#ifdef notdef
	if (dk_status(d) & DK_SPND)
		dk_cmd(d, DKC_RSME) ;
#endif

	if (tp->t_state & (TS_BUSY|TS_TTSTOP|TS_TIMEOUT))
		goto out;

	/*
	 * If the writer was sleeping on output overflow,
	 * wake the process when low tide is reached.
	 */
	if (tp->t_outq.c_cc<=TTLOWAT(tp)) {
		if (tp->t_state&TS_ASLEEP) {
			tp->t_state &= ~TS_ASLEEP;
			wakeup((caddr_t)&tp->t_outq);
		}
		if (tp->t_wsel) {
			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
			tp->t_wsel = 0;
			tp->t_state &= ~TS_WCOLL;
		}
	}
	/*
	 * Now restart transmission unless the output queue is
	 * empty.
	 */
	if (tp->t_outq.c_cc == 0)
		goto out;

	m = m_get(M_DONTWAIT, DKMT_OTTY);
	if (m == NULL) {
		/* No buffers; arrange to retry in .5 seconds */
		dktpaused[d]++;
		tp->t_state |= TS_TIMEOUT;
		timeout(ttrstrt, (caddr_t) tp, hz/2);
		goto out;
	}
	if (tp->t_flags & (RAW|LITOUT))
		nch = ndqb(&tp->t_outq, 0);
	else {
		nch = ndqb(&tp->t_outq, 0200);
		/*
		 * If first thing on queue is a delay process it.
		 */
		if (nch == 0) {
			nch = getc(&tp->t_outq);
			c = MIN((nch & 0xff) + 6, 0x7f);
			delay = D_DELAY;
			if (tpDEBUG) 
				log(LOG_ERR, "DKT_delay %d\n", c) ;
			while (c) {
				delay++;
				c >>= 1;
			}
			if (dk_xmit(d, (struct mbuf *) NULL, 1, delay, dktxdun, (caddr_t) 0))
				tp->t_state |= TS_BUSY;
			(void) m_free(m);
			goto out;
		}
	}
	/*
	 * If characters to transmit, restart transmission.
	 */
	if (nch) {
		bcopy((caddr_t)tp->t_outq.c_cf, mtod(m, caddr_t), (unsigned) nch);
		m->m_len = nch;
		if (dk_xmit(d, m, 1, 0, dktxdun, (caddr_t) nch))
			tp->t_state |= TS_BUSY;
	}
	else (void) m_free(m);
out: ;
	splx(s) ;
}

dktxpack(chan, cmd)
char cmd;
{
	(void) dk_xmit(chan, (struct mbuf *) NULL, 1, cmd, (int (*)()) 0, (caddr_t) 0);
	if (chanDEBUG) log(LOG_ERR, "DKT_sent %o on %d\n",cmd&0377,chan);
}

/*ARGSUSED*/
dktstop(tp, rw)
register struct tty *tp;
{
	register int s, d;

	d = tp - dkt;
	s = spl5();
	if (tp->t_state & TS_BUSY) {
#ifdef notdef
		dk_cmd(d, DKC_SPND);
#endif
		if ((tp->t_state & TS_TTSTOP) == 0) {
			tp->t_state |= TS_FLUSH;
			dk_cmd(d, DKC_FLUSH);
		}
}
	splx(s);
}

dktshut(tp)
register struct tty *tp;
{
	if (tpDEBUG) log(LOG_ERR, "dktshut %d\n", tp-dkt);
	if ((tp->t_state&TS_ISOPEN) && (tp->t_state&TS_CARR_ON)) {
		if (tpDEBUG) log(LOG_ERR, "DKT_sighup %d\n",tp->t_pgrp);
		gsignal(tp->t_pgrp, SIGHUP);
		gsignal(tp->t_pgrp, SIGCONT);
	}
	tp->t_state &= ~TS_CARR_ON;
	ttyflush(tp, (FREAD|FWRITE)) ;
	dk_cmd((tp - dkt), DKC_FLUSH);
}


dktxdun(cnt, chan)
{
	register struct tty *tp ;

	tp = &dkt[chan];
	if (tp->t_state & TS_FLUSH) tp->t_state &= ~TS_FLUSH;
	else ndflush(&tp->t_outq, cnt);
	tp->t_state &= ~TS_BUSY;
	if (tp->t_line)
		(*linesw[tp->t_line].l_start)(tp);
	else
		dktstart(tp);
}
#endif