/* *	Lisa INS8250A device driver * *	Copyright 1984 UniSoft Corporation */#include "sys/param.h"#include "sys/config.h"#include "sys/types.h"#include "sys/systm.h"#include "sys/dir.h"#include "sys/signal.h"#include "sys/user.h"#include "sys/errno.h"#include "sys/file.h"#include "sys/tty.h"#include "sys/termio.h"#include "sys/conf.h"#include "sys/sysinfo.h"#include "sys/var.h"#include "setjmp.h"#include "sys/reg.h"#include "sys/mmu.h"#include "sys/proc.h"int	teproc();/* * structure to access Tecmar device registers */struct tedevice {	char	fill1;	char	te_rbr;		/* +1 data register */#define te_dvlsb te_rbr		/*    lsb of divisor latch */	char	fill2;	char	te_ier;		/* +3 interrupt enable register */#define te_dvmsb te_ier		/*    msb of divisor latch */	char	fill3;	char	te_iir;		/* +5 interrupt id register */	char	fill4;	char	te_lcr;		/* +7 line control register */	char	fill5;	char	te_mcr;		/* +9 modem control register */	char	fill6;	char	te_lsr;		/* +11 line status register */	char	fill7;	char	te_msr;		/* +13 modem status register */	char	fill8;	char	te_scrat;	/* +15 scratch register (8250-A only) */	char	fill[0x200-16];	/* sized to make tedevice be 0x200 long */};/* * structure to access the interrupt reset bit */struct teidevice {	struct tedevice fill[7];/* filler */	char	skip;	char	te_intr;	/* interrupt reset location */};/* * array used to remap ivec interrupt board slot number to tty slot */int te_remap[3];/* * Slot id to interrupt reset address */struct teidevice *te_idevice[3] = {	(struct teidevice *)(STDIO),	(struct teidevice *)(STDIO+0x4000),	(struct teidevice *)(STDIO+0x8000)};extern struct tty te_tty[1];extern struct ttyptr te_ttptr[1];extern char te_dparam[1];extern char te_modem[1];extern int te_cnt;#define MODEM		0x80		/* modem control on bit */#define tedev(d)	((d)&0x7f)	/* from unix device number to device */#define BAUD50		2304#define BAUD75		1356#define BAUD110		1047#define BAUD134		857#define BAUD150		768#define BAUD300		384#define BAUD600		192#define BAUD1200	96#define BAUD1800	64#define BAUD2000	58#define BAUD2400	48#define BAUD3600	32#define BAUD4800	24#define BAUD7200	16#define BAUD9600	12#define BAUD19200	6#define BAUD38400	3#define BAUD56000	2/* -1 means hangup, -2 means invalid */int	tebaudmap[] = {		-1, BAUD50, BAUD75, BAUD110, BAUD134, BAUD150, BAUD134,		BAUD300, BAUD600, BAUD1200, BAUD1800, BAUD2400,		BAUD4800, BAUD9600, BAUD19200, BAUD38400	};	/* Interrupt enable register bits */#define ERBFI	0x01	/* Enable received data available interrupt */#define ETBEI	0x02	/* Enable transmitter enable holding register empty interrupt */#define ELSI	0x04	/* Enable receiver line status interrupt */#define EDSSI	0x08	/* Enable modem status interrupt */	/* Interrupt ident register bits */#define IRQ	0x01	/* Interrupt request, 0 if interrupt pending */#define THE	0x02	/* Transmitter holding register empty */#define IID	0x06	/* Interrupt ID bit mask */#define RID	0x04	/* Interrupt receive bit mask */	/* Line control register bits */#define BITS5	0x00	/* 5 bits */#define BITS6	0x01	/* 6 bits */#define BITS7	0x02	/* 7 bits */#define BITS8	0x03	/* 8 bits */#define STOP1	0x00	/* One stop bit */#define STOP2	0x04	/* Two stop bit */#define PEN	0x08	/* Parity enable */#define EPS	0x10	/* Even parity select */#define SPS	0x20	/* Stick parity */#define SBRK	0x40	/* Set break */#define DLAB	0x80	/* Divisor latch access. i/o direction bit */	/* Modem control register bits */#define DTR	0x01	/* Data terminal ready */#define RTS	0x02	/* Request to send */	/* Line status register bits */#define DATARDY	0x01	/* Data ready */#define OV_ERR	0x02	/* Overrun error on receiver */#define PE_ERR	0x04	/* Parity error */#define FR_ERR	0x08	/* Framing error */#define BR_INT	0x10	/* Break interrupt */#define THRE	0x20	/* Transmitter holding register */#define TEMT	0x40	/* Transmitter empty */	/* Modem status register bits */#define DCTS	0x01	/* Delta clear to send */#define DDSR	0x02	/* Delta data set ready */#define TERI	0x04	/* Trailing edge ring indicator */#define DDCD	0x08	/* Delta data carrier detect */#define CTS	0x10	/* Clear to send */#define DSR	0x20	/* Data set ready */#define RI	0x40	/* Ring indicator */#define DCD	0x80	/* Data carrier detect */int teslotsused = 0;/* * Initialize the baud rate *     slot = 0, 1, or 2 */teinit(slot){	register i, val;	if (teslotsused+4 > te_cnt) {		printf("\n\nSystem only configured for %d tecmar ports\n\n", te_cnt);		return(1);	}	te_remap[slot] = teslotsused;	val = STDIO + slot*0x4000 + 0x200;	for (i = teslotsused; i < teslotsused+4; i++) {		te_ttptr[i].tt_addr = val;		te_ttptr[i].tt_tty = &te_tty[i];		val += 0x200;	}	teslotsused += 4;	return(0);}/* ARGSUSED */teopen(dev, flag)dev_t dev;{	register struct tedevice *addr;	register struct tty *tp;	register d;#ifdef SINGLEUSER	register struct proc *p;#endif SINGLEUSER	d = tedev(dev);	if (d >= te_cnt) {		u.u_error = ENXIO;		return;	}	tp = te_ttptr[d].tt_tty;#ifdef SINGLEUSER	p = u.u_procp;	if ((p->p_pid == p->p_pgrp)	 && (u.u_ttyp == NULL)	 && (tp->t_pgrp == 0)) {		u.u_error = ENOTTY;		return;	}#endif SINGLEUSER	addr = (struct tedevice *)te_ttptr[d].tt_addr;	if (tp == 0 || addr == 0) {		u.u_error = ENXIO;		return;	}	tp->t_index = d;	SPL5();	if ((tp->t_state&(ISOPEN|WOPEN)) == 0) {		tp->t_proc = teproc;		ttinit(tp);		tp->t_iflag = ICRNL | ISTRIP;		tp->t_oflag = OPOST | ONLCR | TAB3;		tp->t_lflag = ISIG | ICANON | ECHO | ECHOK;		tp->t_cflag = sspeed | CS8 | CREAD | HUPCL;		teparam(dev);	}	te_modem[d] = dev & MODEM;	if ((dev&MODEM)==0 || addr->te_msr&DSR)		tp->t_state |= CARR_ON;	else		tp->t_state &= ~CARR_ON;	if (!(flag & FNDELAY))		while ((tp->t_state&CARR_ON) == 0) {			tp->t_state |= WOPEN;			(void) sleep((caddr_t)&tp->t_rawq, TTOPRI);		}	SPL0();	(*linesw[tp->t_line].l_open)(tp);}/* ARGSUSED */teclose(dev, flag)dev_t dev;int flag;{	register struct tedevice *addr;	register struct tty *tp;	register d;	d = tedev(dev);	tp = te_ttptr[d].tt_tty;	(*linesw[tp->t_line].l_close)(tp);	if (tp->t_cflag&HUPCL) {		addr = (struct tedevice *)te_ttptr[d].tt_addr;		d = 0;		addr->te_mcr = d;	}}teread(dev)dev_t dev;{	register struct tty *tp;	tp = te_ttptr[tedev(dev)].tt_tty;	(*linesw[tp->t_line].l_read)(tp);}tewrite(dev)dev_t dev;{	register struct tty *tp;	tp = te_ttptr[tedev(dev)].tt_tty;	(*linesw[tp->t_line].l_write)(tp);}teproc(tp, cmd)register struct tty *tp;{	register struct ccblock *tbuf;	register struct tedevice *addr;	register dev_t dev;	int s;	extern ttrstrt();	s = spltty();	dev = tp->t_index;	addr = (struct tedevice *)te_ttptr[dev].tt_addr;	switch (cmd) {	case T_TIME:		tp->t_state &= ~TIMEOUT;		goto start;	case T_WFLUSH:		tbuf = &tp->t_tbuf;		tbuf->c_size -= tbuf->c_count;		tbuf->c_count = 0;		/* fall through */	case T_RESUME:		tp->t_state &= ~TTSTOP;		goto start;	case T_OUTPUT:start:		if (tp->t_state & (TTSTOP|TIMEOUT|BUSY)) {			/* if ((tp->t_state & BUSY) == 0) {				addr = (struct tedevice *)((int)addr & 0xFCC000);				((struct teidevice *)addr)->te_intr = dev;			} */			break;		}		if (tp->t_state & TTXOFF) {			tp->t_state &= ~TTXOFF;			tp->t_state |= BUSY;			addr->te_rbr = CSTOP;			break;		}		if (tp->t_state & TTXON) {			tp->t_state &= ~TTXON;			tp->t_state |= BUSY;			addr->te_rbr = CSTART;			break;		}		tbuf = &tp->t_tbuf;		if ((tbuf->c_ptr == 0) || (tbuf->c_count == 0)) {			if (tbuf->c_ptr)				tbuf->c_ptr -= tbuf->c_size - tbuf->c_count;			if (!(CPRES & (*linesw[tp->t_line].l_output)(tp))) {				break;			}		}		tp->t_state |= BUSY;		addr->te_ier = ERBFI | ETBEI | ELSI | EDSSI;		addr->te_rbr = *tbuf->c_ptr++;		tbuf->c_count--;		break;	case T_SUSPEND:		tp->t_state |= TTSTOP;		break;	case T_BLOCK:		tp->t_state &= ~TTXON;		tp->t_state |= TBLOCK;		tp->t_state |= TTXOFF;		goto start;	case T_RFLUSH:		if (!(tp->t_state&TBLOCK))			break;		/* fall through */	case T_UNBLOCK:		tp->t_state &= ~(TTXOFF|TBLOCK);		tp->t_state |= TTXON;		goto start;	case T_BREAK:		tp->t_state |= TIMEOUT;		timeout(ttrstrt, (caddr_t)tp, v.v_hz>>2);		break;	}	splx(s);}teioctl(dev, cmd, arg, mode)dev_t dev;{	if (ttiocom(te_ttptr[tedev(dev)].tt_tty, cmd, arg, mode))		teparam(dev);}teparam(dev)register dev_t dev;{	register struct tty *tp;	register struct tedevice *addr;	register int s, speed, oldpri;	char c;	tp = te_ttptr[tedev(dev)].tt_tty;	addr = (struct tedevice *)te_ttptr[tedev(dev)].tt_addr;	/* check for invalid speed */	if ((speed = tebaudmap[tp->t_cflag & CBAUD]) == -2) {		u.u_error = EINVAL;		return;	}	s = 0;	/*	 * hangup the line	 */	if (speed == -1) {		addr->te_mcr = s;		return;	}	if (tp->t_state & BUSY) {		te_dparam[tedev(dev)] = 1;		return;	}	/*	 * set new speed	 */	oldpri = spltty();	addr->te_lcr = DLAB;	addr->te_dvlsb = speed;	addr->te_dvmsb = speed >> 8;	addr->te_lcr = s;	/*	 * set line control information	 */	if ((tp->t_cflag & CSIZE) == CS8)		s |= BITS8;	else if ((tp->t_cflag & CSIZE) == CS7)		s |= BITS7;	else if ((tp->t_cflag & CSIZE) == CS6)		s |= BITS6;	else if ((tp->t_cflag & CSIZE) == CS5)		s |= BITS5;	if (tp->t_cflag & CSTOPB)		s |= STOP2;	if (tp->t_cflag & PARENB)		if ((tp->t_cflag & PARODD) == 0)			s |= PEN|EPS;	addr->te_lcr = s;	/*	 * set modem control information	 */	addr->te_mcr = DTR | RTS;	/*	 * enable interrupts	 */	addr->te_ier = ERBFI | ETBEI | ELSI | EDSSI;	/*	 * reset pending interrupts	 */	c = addr->te_rbr;	c = addr->te_lsr;	c = addr->te_msr;	c = addr->te_iir;	splx(oldpri);}/* VARARGS */teintr(ap)struct args *ap;{	register struct tedevice *addr;	register struct ccblock *cbp;	register struct tty *tp;	register int c, lcnt, flg, iir, lsr;	register char ctmp;	int i, any;	struct teidevice *iaddr;	int index, s;	char lbuf[3];	s = spl5();	index = te_remap[ap->a_dev];	iaddr = te_idevice[ap->a_dev];	c = 0;	iaddr->te_intr = c;	/* reset master interrupt */again:	any = 0;	for (i = index; i < index+4; i++) {		addr = (struct tedevice *)te_ttptr[i].tt_addr;restart:		iir = addr->te_iir;		if (iir & IRQ)			continue;		tp = te_ttptr[i].tt_tty;		lsr = addr->te_lsr;		if (iir & RID) {			sysinfo.rcvint++;			c = addr->te_rbr & 0xFF;			if (tp->t_rbuf.c_ptr == NULL) {				any++;				goto restart;			}			if ((lsr & DATARDY) == 0)				c = 0;			if (tp->t_iflag & IXON) {				ctmp = c & 0177;				if (tp->t_state & TTSTOP) {					if (ctmp == CSTART || tp->t_iflag & IXANY)						(*tp->t_proc)(tp, T_RESUME);				} else {					if (ctmp == CSTOP)						(*tp->t_proc)(tp, T_SUSPEND);				}				if (ctmp == CSTART || ctmp == CSTOP) {					any++;					goto restart;				}			}			/*			 * Check for errors			 */			lcnt = 1;			flg = tp->t_iflag;			if (lsr & (PE_ERR|FR_ERR|OV_ERR|BR_INT)) {				if ((lsr & PE_ERR) && (flg & INPCK))					c |= PERROR;				if (lsr & OV_ERR)					c |= OVERRUN;				if (lsr & FR_ERR)					c |= FRERROR;				if (lsr & BR_INT)					c = OVERRUN;	/* reset char on a break */			}			if (c&(FRERROR|PERROR|OVERRUN)) {				if ((c&0377) == 0) {					if (flg&IGNBRK) {						any++;						goto restart;					}					if (flg&BRKINT) {						signal(tp->t_pgrp, SIGINT);						ttyflush(tp, (FREAD|FWRITE));						any++;						goto restart;					}				} else {					if (flg&IGNPAR) {						any++;						goto restart;					}				}				if (flg&PARMRK) {					lbuf[2] = 0377;					lbuf[1] = 0;					lcnt = 3;					sysinfo.rawch += 2;				} else					c = 0;			} else {				if (flg&ISTRIP)					c &= 0177;				else {					if (c == 0377 && flg&PARMRK) {						lbuf[1] = 0377;						lcnt = 2;					}				}			}			/*			 * Stash character in r_buf			 */			cbp = &tp->t_rbuf;			if (cbp->c_ptr == NULL) {				any++;				goto restart;			}			if (lcnt != 1) {				lbuf[0] = c;				while (lcnt) {					*cbp->c_ptr++ = lbuf[--lcnt];					if (--cbp->c_count == 0) {						cbp->c_ptr -= cbp->c_size;						(*linesw[tp->t_line].l_input)(tp);					}				}				if (cbp->c_size != cbp->c_count) {					cbp->c_ptr -= cbp->c_size - cbp->c_count;					(*linesw[tp->t_line].l_input)(tp);				}			} else {				*cbp->c_ptr = c;				cbp->c_count--;				(*linesw[tp->t_line].l_input)(tp);			}			any++;			goto restart;		}		if (iir&THE) {			sysinfo.xmtint++;			tp->t_state &= ~BUSY;			if (te_dparam[i]) {				te_dparam[i] = 0;				teparam(i);			}			teproc(tp, T_OUTPUT);		} else {			/*			 * must be a modem transition interrupt			 */			temodem(tp, addr->te_msr);		}		any++;		goto restart;	}	if (any != 0)		goto again;	splx(s);}temodem(tp, msr)register struct tty *tp;{	if ((tp->t_state&(ISOPEN|WOPEN))==0)		return;	if (msr & DSR || te_modem[tp->t_index] == 0) {		if ((tp->t_state&CARR_ON) == 0) {			tp->t_state |= CARR_ON;			if (tp->t_state&WOPEN)				wakeup((caddr_t)&tp->t_rawq);		}	} else {		if (tp->t_state&CARR_ON) {			tp->t_state &= ~CARR_ON;			if (tp->t_rbuf.c_ptr != NULL) {				ttyflush(tp, FREAD|FWRITE);				signal(tp->t_pgrp, SIGHUP);			}		}	}}