Ultrix-3.1/sys/dev/uh.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: @(#)uh.c	3.0	4/21/86
 */
/*
 *
 *	ULTRIX-11 DHU11/DHV11 Driver
 *	
 *	Bill Burns	March 1984
 *
 *	Added code to send break if arg to ioctl cmd TCSBRK is 0
 *	(System V) George Mathew: 7/10/85
 *
 */
#include <sys/param.h>
#include <sys/systm.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 & FWRITE */
#include <sys/uba.h>
#ifdef	UCB_CLIST
#include <sys/seg.h>
#endif

#define	q3	tp->t_outq
#define DHUTIME 	3
#define SSPEED	7		/* 300 baud */
extern	int 	nuh11;
extern	char	uhcc[];
extern	struct tty *uh11[];
extern	int	uhchars[];
extern	char	uh_shft;		/* dhu11 = 4, dhv11 = 3 */
extern	char	uh_mask;		/* dhu11 = 017, dhv11 = 07 */
char	uh_spds[] = {
	0, 0, 01, 02, 03, 04, 0, 05,		/* illegal speeds: B50 B200 */
	06, 07, 010, 012, 013, 015, 016, 0,	/* exta = 19200 */
	};					/* extb = illegal */
int	uhtimer();
int	uh_local[];
int	uhstart();
int	io_csr[];
int	ttrstrt();

/* register definitions */
/* csr lo byte definitions */
#define RDA	0200		/* receiver data available */
#define RINT	0100		/* receiver interrupt enable */
#define MR	040		/* master reset */
/* csr hi byte definitions */
#define TA	0200		/* transmitter action */
#define XINT	0100		/* transmit interrupt enable */
#define DIAG	040		/* diagnostic failure */
#define	DMAERR	020		/* DMA error */
#define XLINE	017		/* transmit line number */

/* rbuf bits */
#define VALID	0100000		/* data valid */
#define ERR	070000		/* error bits */
#define OVERR	040000		/* overrun error */
#define FERR	020000		/* framing error */
#define PERR	010000		/* parity error */

/* rit (receive interrupt timer) bits (dhu11 only) */

#define NODLY	01		/* no delay - interrupt immediatly */

/* lpr bits */
#define EPAR	0100		/* even parity - (opposite of old dh) */
#define TWOSB	0200		/* two stop bits */
#define PENABLE	040		/* parity enable */
#define BITS8	030		/* eight data bits */
#define BITS7	020		/* seven data bits */
#define BITS6	010		/* six data bits */

/* line status register */
#define DSR	0200		/* data set ready */
#define RING	040		/* ring indicator */
#define CD	020		/* carrier detect */
#define CTS	010		/* clear to send */
#define DHU11	01		/* on = DHU11, off = DHV11 */

/* line control register */
#define RTS	010000		/* request to send */
#define DTR	01000		/* data terminal ready */
#define LINK	0400		/* set for data set change in rbuf */
#define MAINT	0200		/* loopback mode */
#define XXOFF	040		/* transmit XOFF */
#define XFLOW	020		/* transmit flow control */
#define BREAK	010		/* transmit break */
#define RENA	04		/* reciever enable */
#define RFLOW	02		/* receiver flow control */
#define XABT	01		/* abort transmit */

/* transmit buffer address 2 */
#define TENA	0200		/* transmitter enable (hi byte) */
#define START	0200		/* start dma transfer (lo byte) */


/*
 * DHU11/DHV11 device registers
 */

struct device
{
	char	uhcsrl;		/* csr register lo byte */
	char	uhcsrh;		/* csr register hi byte */
	union {
		int	uhrbuf; /* receive buffer reg. */
		char	uhrit;	/* aka: recieve interrupt delay timer (dhu)*/
	} uh2;
	int	uhlpr;		/* line parameter reg. */
	union {
		int	uhdata;		/* xmit fifo data */
		struct 
		{
			char	uhsize;		/* fifo size (dhu) */
			char	uhstat; 	/* line status reg. */
		} uh6b;
	} uh6;
	int	uhctl;		/* line control reg. */
	int	uhxba;		/* transmit buffer address */
	char	uhxba2l;	/* transmit buffer address ext. lo byte */
	char	uhxba2h;	/* transmit buffer address ext. hi byte */
	int	uhxcnt;		/* transmit char count */
};

/*
 * open a DHU11/DHV11 line
 */

uhopen(dev, flag)
{
	register struct tty *tp;
	register d;
	register struct device *addr;
	static timer_on;
	int s, c, i;

	d = minor(dev);
/*
 * If no tty struct...
 */
	if ((d >= nuh11) || ((tp = uh11[d]) == 0)) {
		u.u_error = ENXIO;
		return;
	}
	addr = io_csr[UH_RMAJ];
/*
 * adjust address
 */
	addr += (d >> uh_shft);
/*
 * uhopen can be called from uhioctl
 * to set maint. loop back mode.
 */
	if(flag == TIOCSMLB) {
		spl5();
		addr->uhcsrl = (RINT|(d & uh_mask));
/* below used for hardware flow control */
		/* addr->uhctl &= ~(XFLOW); */
		addr->uhctl |= (MAINT);
		spl0();
		return;
	}
	if(flag == TIOCCMLB) {
		spl5();
		addr->uhcsrl = (RINT|(d & uh_mask));
		addr->uhctl &= ~(MAINT);
		spl0();
		return;
	}

/*
 * set up tty struct
 */
	tp->t_addr = (caddr_t)addr;
	tp->t_oproc = uhstart;
	tp->t_iproc = NULL;
	tp->t_state |= WOPEN;
	/* timer routine not used
	if(addr->uh6.uhstat & 01) {
		s = spl6();
		if(!timer_on) {
			timer_on++;
			timeout(uhtimer, (caddr_t)0, DHUTIME);
		} 
		splx(s);
	}
	*/
	addr->uhcsrl = RINT;
	if((tp->t_state & ISOPEN) == 0) {
		ttychars(tp);			/* set control chars */
		tp->t_ispeed = SSPEED;
		tp->t_ospeed = SSPEED;
		tp->t_flags = ODDP|EVENP|ECHO;
		uhparam(d);
	}
	if(tp->t_state&XCLUDE && u.u_uid != 0) {
		u.u_error = EBUSY;
		return;
	}
/*
 * set no interrupt delay for DHU11
 */
	if(addr->uh6.uh6b.uhstat & 01) {
		spl5();
		addr->uhcsrl = RINT;	/* must select line zero */
		addr->uh2.uhrit = NODLY;
		spl0();
	}
/*
 * only set DTR on dialup lines 
 */
	if(uh_local[(d>>uh_shft) & uh_mask] & (1 << (d & uh_mask))) {
		spl5();
		addr->uhcsrl = (RINT|(d & uh_mask));
		addr->uhctl &= ~(RTS|DTR|LINK);
		addr->uhctl |= RENA;
		tp->t_state |= CARR_ON;
		spl0();
	} else {
		spl5();
		addr->uhcsrl = (RINT|(d & uh_mask));
		addr->uhctl |= (RTS|DTR|RENA|LINK);
		if (addr->uh6.uh6b.uhstat & CD)
			tp->t_state |= CARR_ON;
		if((flag&FNDELAY) == 0)
			while ((tp->t_state & CARR_ON) == 0)
				sleep((caddr_t)&tp->t_rawq, TTIPRI);
		spl0();
	}
	ttyopen(dev, tp);
}

/*
 * close a DHU11/DHV11 line
 */

uhclose(dev, flag)
dev_t dev;
int flag;
{
	register struct tty *tp;
	register struct device *addr;
	register d;

	d = minor(dev);
	tp = uh11[d];
	(*linesw[tp->t_line].l_close)(tp);
	if((tp->t_state&HUPCLS) || (tp->t_state&CARR_ON) == 0) {
		addr = tp->t_addr;
		spl5();
		addr->uhcsrl = (RINT|(d & uh_mask));
		addr->uhctl = NULL;
		spl0();
	}
	ttyclose(tp);
}

/*
 * Read from a DHU11/DHV11 line
 */

uhread(dev)
{
	register struct tty *tp;

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

/*
 * Write to a DHU11/DHV11 line. 
 */

uhwrite(dev)
{
	register struct tty *tp;

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

/*
 * DHU11/DHV11 reciever interrupt
 */
	
uhrint(dev)
{
	register struct tty *tp;
	register int c;
	register struct device *addr;
	int ln, d;

	d = minor(dev);
	addr = io_csr[UH_RMAJ];
	addr =+ minor(dev);

	while((c = addr->uh2.uhrbuf) < 0) {	/* process characters */
/*
 * construct a minor device number from the dev code
 */
		ln = (d<<uh_shft) + ((c>>8)&uh_mask);

		uhchars[minor(dev)]++;
		if((ln >= nuh11) || ((tp = uh11[ln]) == 0))
				continue;
		if((c&ERR) == ERR) {  /* &&((c&0377)!= 0))/* data set change */
			if(c&01) {			  /* or diag code */
				continue;
			} else {		/* modem status change */
				if ((ln < nuh11) && tp) {
					wakeup((caddr_t)&tp->t_rawq);
					if ((( c & CD) == 0) &&
					    (uh_local[d] & (1 << (ln&uh_mask)))==0) {
						if ((tp->t_state & WOPEN)==0) {
							gsignal(tp->t_pgrp, SIGHUP);
							addr->uhcsrl = (RINT|(ln&uh_mask));
							addr->uhctl = NULL;
							flushtty(tp, FREAD|FWRITE);
						}
						tp->t_state &= ~CARR_ON;
					} else {
						tp->t_state |= CARR_ON;
					}
				}
			}
		continue;
		}
		if((tp->t_state&ISOPEN)==0) {
			wakeup((caddr_t)tp);
			continue;
		}
		if(c & PERR) {
			if((tp->t_flags&(EVENP|ODDP)) == EVENP
			   || (tp->t_flags&(EVENP|ODDP)) == ODDP ){
				tp->t_errcnt++;
				tp->t_lastec = c;
				continue;
			} else
				c &= ~(PERR);
		}
		if(c & FERR)			/* break */
			if(tp->t_flags&RAW)
				c = 0;		/* null (for getty) */
			else
				c = tun.t_intrc;	/* DEL (intr) */
		if(c & ERR) {
			tp->t_errcnt++;		/* count errors on each line */
			tp->t_lastec = c;	/* save last error character */
		}
		(*linesw[tp->t_line].l_rint)(c, tp);
	}
}

/*
 * stty/gtty for DHU11/DHV11
 */

uhioctl(dev, cmd, addr, flag)
caddr_t addr;
{
	register struct tty *tp;
	register int i;
	int s;
	extern wakeup();
	extern int hz;

	tp = uh11[minor(dev)];
	cmd = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr, flag);
	if (cmd == 0)
		return;
	if (cmd == TIOCSETP || cmd == TIOCSETN)
		uhparam(dev);
	else if(cmd == TIOCLOCAL) {
		if (u.u_uid)
			u.u_error = EPERM;
		else if(minor(dev) < nuh11) {
			i = (dev >> uh_shft) & uh_mask;
			uh_local[i] &= ~(1 << (dev & uh_mask));
			uh_local[i] |= (flag << (dev & uh_mask));
		}
	} else if (cmd == TIOCSBRK) {
		spl5();
		((struct device *)tp->t_addr)->uhcsrl = (RINT|(dev & uh_mask));
		((struct device *)tp->t_addr)->uhctl |= BREAK;
		spl0();
	} else if (cmd == TIOCCBRK) {
		spl5();
		((struct device *)tp->t_addr)->uhcsrl = (RINT|(dev & uh_mask));
		((struct device *)tp->t_addr)->uhctl &= ~(BREAK);
		spl0();
	} else if (cmd == TIOTCSBRK) {    /* if arg is 0 for TCSBRK cmd 
					   * George Mathew: 7/11/85 */
		((struct device *)tp->t_addr)->uhcsrl = (RINT|(dev & uh_mask));
		((struct device *)tp->t_addr)->uhctl |= BREAK;
		/*tp->t_state |= TIMEOUT; is this required??? */
		s = spl5();
		timeout(wakeup,(caddr_t)tp,hz/4);
		sleep((caddr_t)tp,PZERO-1);
		splx(s);
		((struct device *)tp->t_addr)->uhcsrl = (RINT|(dev & uh_mask));
		((struct device *)tp->t_addr)->uhctl &= ~(BREAK);
	} else if (cmd == TIOCSDTR) {
		spl5();
		((struct device *)tp->t_addr)->uhcsrl = (RINT|(dev & uh_mask));
		((struct device *)tp->t_addr)->uhctl |= (DTR|RTS);
		spl0();
	} else if (cmd == TIOCCDTR) {
		spl5();
		((struct device *)tp->t_addr)->uhcsrl = (RINT|(dev & uh_mask));
		((struct device *)tp->t_addr)->uhctl &= ~(DTR|RTS);
		spl0();
	} else if (cmd == TIOCSMLB || cmd == TIOCCMLB) {
		if (u.u_uid)
			u.u_error = EPERM;
		else
			uhopen(dev, cmd);
	} else {
		u.u_error = ENOTTY;
	}
}

static
uhparam(dev)
{
	register struct tty *tp;
	register struct device *addr;
	register d;

	d = minor(dev);
	tp = uh11[d];
	addr = ((struct device *)tp->t_addr);
	spl5();
	addr->uhcsrl = (RINT|(d&uh_mask));
	if((tp->t_ispeed)==0) {
		tp->t_state |= HUPCLS;
		((struct device *)tp->t_addr)->uhctl &= ~(LINK|DTR|RTS);
		spl0();
		return;
	}
/*
 * set up line parameter reg 
 */
	d = (uh_spds[tp->t_ospeed] << 12) | (uh_spds[tp->t_ispeed] << 8);
	if((tp->t_ispeed) == 4)		/* 134.5 baud */
		d |= (BITS6|PENABLE);	/* HDUPLX in stock dh */
	else if(tp->t_flags&RAW)
		d |= BITS8;
	else
		d |= (BITS7|PENABLE);
	if(tp->t_flags&EVENP)
		d |= EPAR;
	if((tp->t_ospeed) == 3)		/* 110 baud */
		d |= TWOSB;
	(struct device *)addr->uhlpr = d;
/* below was used for hardware flow control
	 if((tun.t_startc == CSTART) && (tun.t_stopc == CSTOP))
		addr->uhctl |= (XFLOW);
	else
		addr->uhctl &= ~(XFLOW);
*/
	spl0();
}

/*
 * DHU11/DHV11 transmitter interrupt
 */

uhxint(dev)
{
	register struct tty *tp;
	register struct device *addr;
	register d;
	char csrsv;
	char	*p;	/* address offset (if UB map used) */

#ifndef	UCB_CLIST
	if(ubmaps)
		p = &cfree;
	else
		p = 0;
#endif	UCB_CLIST
	d = minor(dev);
	addr = io_csr[UH_RMAJ];
	addr += d;
	while((csrsv = addr->uhcsrh) < 0) {
		/*
		if (csrsv & DMAERR)
			printf("dhuv: dma error\n");
		if (csrsv & DIAG)
			printf("dhuv: diag failure\n");
		*/
		d = minor(dev);
		d <<= uh_shft;
		d |= (csrsv & uh_mask);
		tp = uh11[d];
		addr->uhcsrl = ((d&uh_mask)|RINT);
		if(tp->t_state&FLUSH)
			tp->t_state &= ~FLUSH;
		else {
#ifndef	UCB_CLIST
			ndflush(&q3, ((char *)addr->uhxba+p)-q3.c_cf);
#else	UCB_CLIST
			if(ubmaps)
			    p = (char *)addr->uhxba + (char *)cfree -
				CLIST_UBADDR - q3.c_cf;
			else
			    p = (char *)(((((long)(addr->uhxba2l&077))<<16L) +
				(((long)addr->uhxba)&0xffffL)) - clstaddr -
				(long)(q3.c_cf - SEG5));
			ndflush(&q3, p);
#endif	UCB_CLIST
		}
		tp->t_state &= ~BUSY;
		if(tp->t_line)
			(*linesw[tp->t_line].l_start)(tp);
		else
			uhstart(tp);
	}
}

/*
 * DHU11/DHV11 start (restart) routine
 */

uhstart(tp)
register struct tty *tp;
{
	register struct device *addr;
	register nch;
	int s, d;

/* fifo code - not used
	int free;
	char *cp = tp->t_outq.c_cf;
	union {
		int x;
		char y[2];
	} wdbuf;
*/

/*
 * Find the minor device number from the TTY pointer,
 * the old way will not work because the TTY structure
 * assignments may not be in order.
 */
	s = spl5();
	for(d=0; d<nuh11; d++)
		if(uh11[d] == tp)
			break;
	addr = (struct device *)tp->t_addr;
	/*
	 * If it's currently active, or delaying,
	 * no need to do anything.
	 */
	if (tp->t_state&(TIMEOUT|BUSY|TTSTOP))
		goto out;

	/*
	 * If the writer was sleeping on output overflow,
	 * wake him when low tide is reached.
	 */
	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	SELECT
	}

	if (tp->t_outq.c_cc == 0)
		goto out;

	/*
	 * Find number of characters to transfer.
	 */
	if (tp->t_flags & RAW) {
		nch = ndqb(&tp->t_outq, 0);
	} else {
		nch = ndqb(&tp->t_outq, 0200);
		if (nch == 0) {
			nch = getc(&tp->t_outq);
			timeout(ttrstrt, (caddr_t)tp, (nch&0177)+6);
			tp->t_state |= TIMEOUT;
			goto out;
		}
	}
	/*
	 * If any characters were set up, start transmission;
	 */
	if (nch) {		/* dma transfer */
		addr->uhcsrl = ((d & uh_mask)|RINT);
		addr->uhcsrh = XINT;
		if (addr->uhctl & XABT)
			addr->uhctl &= ~(XABT);
		uhcc[d] = nch;		/* What is this used for ? */
#ifndef	UCB_CLIST
		if(ubmaps)	/* must offset address if UB map used */
			addr->uhxba = (char *)tp->t_outq.c_cf - (char *)&cfree;
		else
			addr->uhxba = tp->t_outq.c_cf;
		addr->uhxcnt = nch;
		addr->uhxba2h = TENA;
		addr->uhxba2l = START;
#else	UCB_CLIST
		if(ubmaps) {	/* must offset address if UB map used */
			addr->uhxba = CLIST_UBADDR + tp->t_outq.c_cf - (char *)cfree;
			addr->uhxcnt = nch;
			addr->uhxba2h = TENA;
			addr->uhxba2l = START;
		} else {
			long paddr;
			paddr = clstaddr + (long)(tp->t_outq.c_cf - SEG5);
			addr->uhxba = loint(paddr);
			addr->uhxcnt = nch;
			addr->uhxba2h = TENA;
			addr->uhxba2l = START|(hiint(paddr)&077);
		}
#endif	UCB_CLIST
		tp->t_state |= BUSY;
	}
/* fifo code - not used 
	if (nch) {
		addr->uhcsrl = ((d & uh_mask)|RINT);
		addr->uhcsrh = XINT;
		free = addr->uh6.uh6b.uhsize;
		if (nch > free)
			nch = free;
		else
			free = nch;
		while (free > 1) {
			wdbuf.y[0] = *cp++;
			wdbuf.y[1] = *cp++;
			addr->uh6.uhdata = wdbuf.x;
			free -= 2;
		}
		if (free == 1)
			addr->uh6.uhdata = *cp++;
		ndflush(&tp->t_outq, nch);
		tp->t_state |= BUSY;
	}
 end of not used fifo code */
    out:
	splx(s);
}

/*
 * Stop output on a line
 */

uhstop(tp, flag)
register struct tty *tp;
{
	register struct device *addr;
	register d, s;

	addr = (struct device *)tp->t_addr;
	s = spl6();
	if(tp->t_state & BUSY) {
		d = minor(tp->t_dev);
		addr->uhcsrl = ((d & uh_mask) | RINT);
		if((tp->t_state & TTSTOP)==0) {
			tp->t_state |= FLUSH;
		}
/* below was for hardware flow control 
		if(!(addr->uhctl & XFLOW))
			addr->uhctl |= XABT;
*/
		addr->uhctl |= XABT;
	}
	splx(s);
}

/*
 * timer routine
 *
 * This routine is only used for the dhu11
 *
 */

uhtimer(dev)
{
	register d, cc;
	register struct device *addr;
	int x;

	addr = io_csr[UH_RMAJ];
	d = 0;
	do {
		cc = uhchars[d];
		uhchars[d] = 0;
		if(cc > 50)
			x = 10;
		else if(cc >16)
				x = 5;
			else
				x = 1;
		addr->uh2.uhrit = x;
		addr += 1;
		uhrint(d++);
	} while (d < (nuh11+15)/16);
	timeout(uhtimer, (caddr_t)0, DHUTIME);
}