Ultrix-3.1/sys/dev/dz.c

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


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * SCCSID: @(#)dz.c	3.0	4/21/86
 */
/*
 *	Unix/v7m DZ11/DZV11 driver
 *
 *	Modified for local/remote terminal operation
 *	and DZV11 support.
 *
 *	Fred Canter 11/12/82
 *
 *	Added check for exclusive bit, and made it call ttyopen through
 *	the linesw table. -Dave Borman, 1/30/84
 *
 *	Added code to send break if arg to ioctl cmd TCSBRK is 0
 *	(system V). George Mathew: 7/10/85
 *	
 *	Changes for O_NDELAY for open().  George Mathew: 7/24/85
 */

#include <sys/param.h>
#include <sys/conf.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/tty.h>
#include <sys/devmaj.h>
#include <sys/file.h>		/* needed for FREAD and FWRITE */

int	io_csr[];	/* CSR address now in config file (c.c) */
int	dz_cnt;		/* number of dz11 or dzv11 lines, see c.c */
			/* MUST BE A MULTIPLE OF THE NUMBER OF LINES PER UNIT */
			/* DZ11 has 8 lines per unit, DZV11 has 4 */
char	dz_shft;	/* DZ SHIFT - 3 for DZ11, 2 for DZV11 */
char	dz_mask;	/* DZ MASK  - 7 for DZ11, 3 for DZV11 */

extern struct tty *dz_tty[];
char	dz_stat;
char	dz_local[];	/* bit per line, 1 = local tty */
char	dz_brk[];	/* soft copy of brk reg (write only) */

char	dz_speeds[] = {
	0, 020, 021, 022, 023, 024, 0, 025,
	026, 027, 030, 032, 034, 036, 0, 0,
	};

#define	MAINT	010
#define	BITS7	020
#define	BITS8	030
#define	TWOSB	040
#define	PENABLE	0100
#define	OPAR	0200
#define	RCVENA	010000

#define	TXIE	040000		/* Transmitter interrupt enable */
#define	RXIE	000100		/* Receiver interrupt enable */
#define	MSE	000040		/* Master Scan enable */
#define	IE	(TXIE|RXIE|MSE)	/* Enable all interrupts */

#define	PERROR	010000		/* Parity error */
#define	FRERROR	020000		/* Framing error */
#define	ORERROR	040000		/* silo overrun error */
#define	ERR	(PERROR|FRERROR|ORERROR) /* Any error */

#define	SSPEED	7		/* standard speed: 300 baud */

#define	DZTIMEOUT	0	/* call timeout at end of dzscan */
#define	NODZTIMEOUT	1	/* don't call timeout at end of dzscan */

struct device {
	int	dzcsr, dzrbuf;
	char	dztcr, dzdtr;
	char	dztbuf, dzbrk;
};
#define	dzlpr	dzrbuf
#define	dzmsr	dzbrk

#define	ON	1
#define	OFF	0

dzopen(dev, flag)
{
	register struct tty *tp;
	register struct device *dzaddr;
	register int d;
	extern dzstart(), dzscan();

	d = minor(dev);
	if ((d >= dz_cnt) || ((tp = dz_tty[d]) == 0)) {
		u.u_error = ENXIO;
		return;
	}
	dzaddr = io_csr[DZ_RMAJ];
	dzaddr += d>>dz_shft;
/*
 * dzopen can now be called from dzioctl
 * to set maint. loop back mode.
 */
	if(flag == TIOCSMLB) {
		dzaddr->dzcsr |= MAINT;
		return;
	}
	if(flag == TIOCCMLB) {
		dzaddr->dzcsr &= ~MAINT;
		return;
	}
	tp->t_addr = (caddr_t)dzaddr;
	if ((tp->t_state&(ISOPEN|WOPEN)) == 0) {
		tp->t_oproc = dzstart;
		tp->t_iproc = NULL;
		ttychars(tp);
		tp->t_ispeed = SSPEED;
		tp->t_ospeed = SSPEED;
		tp->t_flags = ODDP|EVENP|ECHO;
		dzparam(d);
	} else if (tp->t_state&XCLUDE && u.u_uid!=0) {
		u.u_error = EBUSY;
		return;
	}
/* only set DTR on dialup lines */
	if((dz_local[(d>>dz_shft)&dz_mask] & (1 << (d&dz_mask))) == 0)
		dzmodem(d, ON);
	spl6();
	if((flag&FNDELAY) == 0)
		while ((tp->t_state&CARR_ON)==0) {
			tp->t_state |= WOPEN;
			sleep((caddr_t)&tp->t_rawq, TTIPRI);
		}
	ttyopen(dev, tp);
	spl0();
}

dzclose(dev)
{
	register struct tty *tp;
	register int unit;

	dev = minor(dev);
	unit = (dev >> dz_shft) & dz_mask;
	tp = dz_tty[dev];
	(*linesw[tp->t_line].l_close)(tp);
	dz_brk[unit] &= ~(1 << (dev & dz_mask));
	((struct device *)tp->t_addr)->dzbrk = dz_brk[unit];
	if ((tp->t_state&HUPCLS) || (tp->t_state&CARR_ON) == 0) {
		dzmodem(dev, OFF);
	}
	ttyclose(tp);
}

dzread(dev)
{
	register struct tty *tp;

	tp = dz_tty[minor(dev)];
	(*linesw[tp->t_line].l_read)(tp);
}

dzwrite(dev)
{
	register struct tty *tp;

	tp = dz_tty[minor(dev)];
	(*linesw[tp->t_line].l_write)(tp);
}

dzioctl(dev, cmd, addr, flag)
{
	register struct tty *tp;
	register int unit;
	int s;
	extern int hz;
	extern wakeup();

	unit = (dev >> dz_shft) & dz_mask;
	tp = dz_tty[minor(dev)];
	cmd = (*linesw[tp->t_line].l_ioctl)(tp, cmd, (caddr_t)addr, flag);
	if (cmd==0) {
		return;
	} else if (cmd == TIOCLOCAL) {
		if (u.u_uid)
			u.u_error = EPERM;
		else if(minor(dev) < dz_cnt) {
			dz_local[unit] &= ~(1 << (dev & dz_mask));
			dz_local[unit] |= (flag << (dev & dz_mask));
			/*
			 * Call the scan routine directly to
			 * make the change take effect now.
			 * This keeps init from waiting 2 seconds on
			 * each line it enables for logins.
			 */
			dzscan(NODZTIMEOUT);
		}
	} else if (cmd == TIOCSETP || cmd == TIOCSETN) {
		dzparam(minor(dev));
	} else if (cmd == TIOCSDTR) {
		dzmodem(minor(dev), ON);
	} else if (cmd == TIOCCDTR) {
		dzmodem(minor(dev), OFF);
	} else if (cmd == TIOCSBRK) {
		dz_brk[unit] |= (1 << (dev & dz_mask));
		((struct device *)tp->t_addr)->dzbrk = dz_brk[unit];
	} else if (cmd == TIOCCBRK) {
		dz_brk[unit] &= ~(1 << (dev & dz_mask));
		((struct device *)tp->t_addr)->dzbrk = dz_brk[unit];
	} else if (cmd == TIOCSMLB || cmd == TIOCCMLB) {
		if (u.u_uid)
			u.u_error = EPERM;
		else
			dzopen(dev, cmd);
	} else if (cmd == TIOTCSBRK) {    /* if arg is 0 for TCSBRK cmd 
					   * George Mathew: 7/10/85 */
		dz_brk[unit] |= (1 << (dev & dz_mask));
		((struct device *)tp->t_addr)->dzbrk = dz_brk[unit];
		/*tp->t_state |= TIMEOUT; is this required??? */
		s = spl5();
		timeout(wakeup,(caddr_t)tp,hz/4);
		sleep((caddr_t)tp,PZERO-1);
		splx(s);
		dz_brk[unit] &= ~(1 << (dev & dz_mask));
		((struct device *)tp->t_addr)->dzbrk = dz_brk[unit];
	} else {
		u.u_error = ENOTTY;
	}
}

static
dzparam(dev)
{
	register struct tty *tp;
	register struct device *dzaddr;
	register lpr;

	tp = dz_tty[dev];
	dzaddr = io_csr[DZ_RMAJ];
	dzaddr += dev>>dz_shft;
/*
 * The following three lines of code were inserted
 * to allow the comm. mux. exerciser (cmx) to run
 * the DZ11 in maintenance loop back mode.
 */
	if(dzaddr->dzcsr&MAINT)
		dzaddr->dzcsr |= IE;
	else
		dzaddr->dzcsr = IE;
	if (dz_stat==0) {
		dzscan(DZTIMEOUT);
		dz_stat++;
	}
	if (tp->t_ispeed==0) {	/* Hang up line */
		dzmodem(dev, OFF);
		return;
	}
	lpr = (dz_speeds[tp->t_ispeed]<<8)|(dev&dz_mask);
	if (tp->t_flags&RAW)
		lpr |= BITS8;
	else
		lpr |= BITS7|PENABLE;
	if ((tp->t_flags&EVENP)==0)
		lpr |= OPAR;
	if (tp->t_ispeed == 3)	/* 110 baud */
		lpr |= TWOSB;
	dzaddr->dzlpr = lpr;
}

dzrint(dev)
{
	register struct device *dzaddr;
	register struct tty *tp;
	register c;
	int	ln;

	dzaddr = io_csr[DZ_RMAJ];
	dzaddr += minor(dev);
	while ((c = dzaddr->dzrbuf) < 0) {	/* char. present */
		ln = ((c>>8)&dz_mask)|(dev<<dz_shft);
		if ((ln >= dz_cnt) || ((tp = dz_tty[ln]) == 0))
			continue;
		if((tp->t_state&ISOPEN)==0) {
			wakeup((caddr_t)&tp->t_rawq);
			continue;
		}
		if (c & ERR) {
			if (c&FRERROR) {
				/* framing error, probably a break */
				if (tp->t_flags&RAW)
					c = 0;		/* null (for getty) */
				else
					c = tun.t_intrc;;	/* DEL (intr) */
			} else {
				if (c&PERROR){
					if ((tp->t_flags&(EVENP|ODDP))==EVENP
					 || (tp->t_flags&(EVENP|ODDP))==ODDP ){
						/* parity error */
						tp->t_errcnt++;
						tp->t_lastec = c;
						continue;
					} else
						c &= ~PERROR;
				}
				if(c & ORERROR) {
					/* count errors on each line */
					tp->t_errcnt++;
					/* save last error character */
					tp->t_lastec = c;
				}
			}
		}
		(*linesw[tp->t_line].l_rint)(c, tp);
	}
}

dzxint(dev)
{
	register struct tty *tp;
	register struct device *dzaddr;
	register int csrsav;

	dzaddr = io_csr[DZ_RMAJ];
	dzaddr += minor(dev);
	while((csrsav = dzaddr->dzcsr) < 0) {	/* TX rdy */
		tp = dz_tty[((dev<<dz_shft)|(csrsav>>8)&dz_mask)];
/* below is fix for dzv11 multiple character problem  OHMS */
		if(tp->t_state & BUSY) {
			dzaddr->dztbuf = tp->t_char;
			tp->t_state &= ~BUSY;
		} else
			dzaddr->dztcr &= ~(1<<((csrsav>>8)&dz_mask));
		dzstart(tp);
	}
}

dzstart(tp)
register struct tty *tp;
{
	register struct device *dzaddr;
	register c;
	int s, unit;
	extern ttrstrt();

/*
 * Find the minor device number from the TTY pointer,
 * the old method will not work because the TTY
 * structure assignments may not be in order.
 */
	for(unit=0; unit<dz_cnt; unit++)
		if(dz_tty[unit] == tp)
			break;
	dzaddr = io_csr[DZ_RMAJ];
	dzaddr += unit>>dz_shft;
	unit = 1<<(unit&dz_mask);
	s = spl5();
	if (tp->t_state&(TIMEOUT|BUSY)) {
		splx(s);
		return;
	}
	if (tp->t_state&TTSTOP) {
		dzaddr->dztcr &= ~unit;
		splx(s);
		return;
	}
	if ((c=getc(&tp->t_outq)) >= 0) {
		if (c>=0200 && (tp->t_flags&RAW)==0) {
			dzaddr->dztcr &= ~unit;
			tp->t_state |= TIMEOUT;
			timeout(ttrstrt, (caddr_t)tp, (c&0177)+6);
		} else {
			tp->t_char = c;
			tp->t_state |= BUSY;
			dzaddr->dztcr |= unit;
		}
		if (tp->t_outq.c_cc<=TTLOWAT(tp)) {
			if (tp->t_state&ASLEEP) {
				tp->t_state &= ~ASLEEP;
				wakeup((caddr_t)&tp->t_outq);
			}
#ifdef	SELECT
			if (tp->t_wsel) {
				selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
				tp->t_wsel = 0;
				tp->t_state &= ~TS_WCOLL;
			}
#endif
		}
	}
	splx(s);
}

static
dzmodem(dev, flag)
{
	register struct device *dzaddr;
	register bit;

	dzaddr = io_csr[DZ_RMAJ];
	dzaddr += minor(dev)>>dz_shft;
	bit = 1<<(minor(dev)&dz_mask);
	if (flag==OFF)
		dzaddr->dzdtr &= ~bit;
	else	dzaddr->dzdtr |= bit;
}

dzscan(flag)
int flag;
{
	register i;
	register struct device *dzaddr;
	register struct tty *tp;
	char	bit;

	for (i=0; i<dz_cnt; i++) {
		dzaddr = io_csr[DZ_RMAJ];
		dzaddr += i>>dz_shft;
		if((tp = dz_tty[i]) == 0)
			continue;
		bit = 1 << (i&dz_mask);
		if (dzaddr->dzmsr&bit || (dz_local[i>>dz_shft] & bit)) {
			if ((tp->t_state&CARR_ON)==0) {
				wakeup((caddr_t)&tp->t_rawq);
				tp->t_state |= CARR_ON;
			}
		} else {
			if (tp->t_state&CARR_ON) {
				if (tp->t_state&ISOPEN) {
					gsignal(tp->t_pgrp, SIGHUP);
					dzaddr->dzdtr &= ~bit;
					flushtty(tp, FREAD|FWRITE);
				}
				tp->t_state &= ~CARR_ON;
			}
		}
	}
	if (flag == DZTIMEOUT)
		timeout(dzscan, (caddr_t)DZTIMEOUT, 120);
}