4.4BSD/usr/src/sys/vax/uba/dmx.c

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

/*
 * Copyright (c) 1982, 1986 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)dmx.c	7.7 (Berkeley) 5/16/91
 */

/*
 * Common code for DMF32 and DMZ32 drivers
 */
#include "dmf.h"
#include "dmz.h"
#if NDMF + NDMZ > 0

#include "../include/pte.h"

#include "sys/param.h"
#include "uba.h"
#include "sys/conf.h"
#include "sys/user.h"
#include "sys/proc.h"
#include "sys/ioctl.h"
#include "sys/tty.h"
#include "sys/map.h"
#include "sys/buf.h"
#include "sys/vm.h"
#include "sys/clist.h"
#include "sys/file.h"
#include "sys/uio.h"
#include "sys/kernel.h"
#include "sys/syslog.h"

#include "dmx.h"
#include "ubareg.h"
#include "ubavar.h"
#include "dmxreg.h"
#include "dmreg.h"

#ifndef	PORTSELECTOR
#define	ISPEED	TTYDEF_SPEED
#define	LFLAG	TTYDEF_LFLAG
#else
#define	ISPEED	B4800
#define	IFLAGS	(TTYDEF_LFLAG&~ECHO)
#endif

#ifndef DMX_TIMEOUT
#define DMX_TIMEOUT	10
#endif
int	dmx_timeout = DMX_TIMEOUT;		/* silo timeout, in ms */
int	dmx_mindma = 4;			/* don't dma below this point */

struct speedtab dmxspeedtab[] = {
	0,	0,
	75,	1,
	110,	2,
	134,	3,
	150,	4,
	300,	5,
	600,	6,
	1200,	7,
	1800,	010,
	2400,	012,
	4800,	014,
	9600,	016,
	19200,	017,
	EXTA,	017,
	-1,	-1
};
/*
 * The clist space is mapped by the drivers onto each UNIBUS.
 * The UBACVT macro converts a clist space address for unibus uban
 * into an I/O space address for the DMA routine.
 */
int	cbase[NUBA];			/* base address in unibus map */
#define	UBACVT(x, uban)		(cbase[uban] + ((x)-(char *)cfree))

int	ttrstrt();

/*
 * DMF/DMZ open common code
 */
dmxopen(tp, sc, flag)
	register struct tty *tp;
	register struct dmx_softc *sc;
{
	int s, unit, error = 0;
	int dmxparam();

	s = spltty();
	if ((sc->dmx_flags & DMX_ACTIVE) == 0) {
		sc->dmx_octet->csr |= DMF_IE;
		sc->dmx_flags |= DMX_ACTIVE;
		sc->dmx_octet->rsp = dmx_timeout;
	}
	splx(s);
	if (tp->t_state & TS_XCLUDE && u.u_uid != 0)
		return (EBUSY);
	/*
	 * If this is first open, initialize tty state to default.
	 */
	if ((tp->t_state&TS_ISOPEN) == 0) {
		tp->t_state |= TS_WOPEN;
		ttychars(tp);
#ifndef PORTSELECTOR
		if (tp->t_ispeed == 0) {
#endif
			tp->t_iflag = TTYDEF_IFLAG;
                        tp->t_oflag = TTYDEF_OFLAG;
                        tp->t_cflag = TTYDEF_CFLAG;
                        tp->t_lflag = LFLAG;
                        tp->t_ispeed = tp->t_ospeed = ISPEED;
#ifdef PORTSELECTOR
			tp->t_cflag |= HUPCL;
#else
		}
#endif
	}
	dmxparam(tp, &tp->t_termios);

	unit = minor(tp->t_dev) & 07;
	/*
	 * Wait for carrier, then process line discipline specific open.
	 */
	s = spltty();
	for (;;) {
		if ((dmxmctl(tp, DMF_ON, DMSET) & DMF_CAR) ||
		    (sc->dmx_softCAR & (1 << unit)))
			tp->t_state |= TS_CARR_ON;
		if (tp->t_state&TS_CARR_ON || flag&O_NONBLOCK ||
		    tp->t_cflag&CLOCAL)
			break;
		tp->t_state |= TS_WOPEN;
		if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
		    ttopen, 0))
			break;
	}
	splx(s);
	if (error)
		return (error);
	return ((*linesw[tp->t_line].l_open)(tp->t_dev, tp));
}

dmxclose(tp, flag)
	struct tty *tp;
	int flag;
{

	(*linesw[tp->t_line].l_close)(tp, flag);
	(void) dmxmctl(tp, DMF_BRK, DMBIC);
	if (tp->t_cflag & HUPCL || (tp->t_state & TS_ISOPEN) == 0)
		(void) dmxmctl(tp, DMF_OFF, DMSET);
	return (ttyclose(tp));
}

dmxrint(sc)
	register struct dmx_softc *sc;
{
	register c, cc;
	register struct tty *tp;
	register struct dmx_octet *addr;
	int unit;
	int overrun = 0;

	addr = (struct dmx_octet *)sc->dmx_octet;
	/*
	 * Loop fetching characters from the silo for this
	 * octet until there are no more in the silo.
	 */
	while ((c = addr->rbuf) < 0) {
		cc = c&0xff;
		unit = (c >> 8) & 07;
		tp = sc->dmx_tty + unit;
		if (c & DMF_DSC) {
			addr->csr = DMF_IE | DMFIR_RMSTSC | unit;
			if (addr->rmstsc & DMF_CAR)
				(void)(*linesw[tp->t_line].l_modem)(tp, 1);
			else if ((sc->dmx_softCAR & (1 << unit)) == 0 &&
			    (*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
				addr->csr = DMF_IE | DMFIR_LCR | unit;
				addr->lctms = DMF_ENA;
			}
			continue;
		}
		if ((tp->t_state&TS_ISOPEN) == 0) {
			wakeup((caddr_t)&tp->t_rawq);
#ifdef PORTSELECTOR
			if ((tp->t_state & TS_WOPEN) == 0)
#endif
				continue;
		}
		if (c & (DMF_PE|DMF_DO|DMF_FE)) {
			if (c & DMF_PE)
				cc |= TTY_PE;
			if ((c & DMF_DO) && overrun == 0) {
				log(LOG_WARNING,
				    "dm%c%d: silo overflow, line %d\n",
				    sc->dmx_type, sc->dmx_unit,
				    sc->dmx_unit0 + unit);
				overrun = 1;
			}
			if (c & DMF_FE)
				cc |= TTY_FE;
		}
		(*linesw[tp->t_line].l_rint)(cc, tp);
	}
}

dmxioctl(tp, cmd, data, flag)
	register struct tty *tp;
	caddr_t data;
{
	int error;

	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)
		return (error);

	switch (cmd) {

	case TIOCSBRK:
		(void) dmxmctl(tp, DMF_BRK, DMBIS);
		break;

	case TIOCCBRK:
		(void) dmxmctl(tp, DMF_BRK, DMBIC);
		break;

	case TIOCSDTR:
		(void) dmxmctl(tp, DMF_DTR|DMF_RTS, DMBIS);
		break;

	case TIOCCDTR:
		(void) dmxmctl(tp, DMF_DTR|DMF_RTS, DMBIC);
		break;

	case TIOCMSET:
		(void) dmxmctl(tp, dmtodmx(*(int *)data), DMSET);
		break;

	case TIOCMBIS:
		(void) dmxmctl(tp, dmtodmx(*(int *)data), DMBIS);
		break;

	case TIOCMBIC:
		(void) dmxmctl(tp, dmtodmx(*(int *)data), DMBIC);
		break;

	case TIOCMGET:
		*(int *)data = dmxmctl(tp, 0, DMGET);
		break;

	default:
		return (ENOTTY);
	}
	return (0);
}

/*
 * modem control
 * "bits" are dmf/dmz lcr format;
 * return of DMGET is DM11 format.
 */
dmxmctl(tp, bits, how)
	struct tty *tp;
	int bits, how;
{
	register struct dmx_octet *addr;
	register int unit, mbits, lcr;
	int s;

	unit = minor(tp->t_dev) & 07;
	addr = (struct dmx_octet *)(tp->t_addr);

	s = spltty();
	addr->csr = DMF_IE | DMFIR_RMSTSC | unit;
	mbits = addr->rmstsc & 0xff00;
	addr->csr = DMF_IE | DMFIR_LCR | unit;
	lcr = addr->lctms;

	switch (how) {
	case DMSET:
		lcr = bits;
		break;

	case DMBIS:
		lcr |= bits;
		break;

	case DMBIC:
		lcr &= ~bits;
		break;

	case DMGET:
		splx(s);
		return (dmxtodm(mbits, lcr));
	}
	addr->lctms = lcr;
	(void) splx(s);
	return (mbits);
}

/*
 * Routine to convert modem status from dm to dmf/dmz lctmr format.
 */
dmtodmx(bits)
	register int bits;
{
	register int lcr = DMF_ENA;

	if (bits & DML_DTR)
		lcr |= DMF_DTR;
	if (bits & DML_RTS)
		lcr |= DMF_RTS;
	if (bits & DML_ST)
		lcr |= DMF_SRTS;
	if (bits & DML_USR)
		lcr |= DMF_USRW;
	return (lcr);
}

/*
 * Routine to convert modem status from dmf/dmz receive modem status
 * and line control register to dm format.
 * If dmf/dmz user modem read bit set, set DML_USR.
 */
dmxtodm(mstat, lcr)
	register int mstat, lcr;
{

	mstat = ((mstat & (DMF_DSR|DMF_RNG|DMF_CAR|DMF_CTS|DMF_SR)) >> 7) | 
		((mstat & DMF_USRR) >> 1) | DML_LE;
	if (lcr & DMF_DTR)
		mstat |= DML_DTR;
	if (lcr & DMF_SRTS)
		mstat |= DML_ST;
	if (lcr & DMF_RTS)
		mstat |= DML_RTS;
	return (mstat);
}
 

/*
 * Set parameters from open or ioctl into the hardware registers.
 */
dmxparam(tp, t)
	register struct tty *tp;
	register struct termios *t;
{
	register struct dmx_octet *addr;
	register int lpar, lcr;
	register int cflag = t->c_cflag;
	int s, unit;
	int ispeed = ttspeedtab(t->c_ispeed, dmxspeedtab);
        int ospeed = ttspeedtab(t->c_ospeed, dmxspeedtab);

        /* check requested parameters */
        if (ospeed < 0 || ispeed < 0 || (cflag&CSIZE) == CS5)
                return(EINVAL);
        if (ispeed == 0)
                ispeed = ospeed;
        /* and copy to tty */
        tp->t_ispeed = t->c_ispeed;
        tp->t_ospeed = t->c_ospeed;
        tp->t_cflag = cflag;

	addr = (struct dmx_octet *)tp->t_addr;
	unit = minor(tp->t_dev) & 07;
	/*
	 * Block interrupts so parameters will be set
	 * before the line interrupts.
	 */
	s = spltty();
	addr->csr = unit | DMFIR_LCR | DMF_IE;
	if (ospeed == 0) {
		tp->t_cflag |= HUPCL;
		(void) dmxmctl(tp, DMF_OFF, DMSET);
		splx(s);
		return;
	}
	lpar = (ospeed<<12) | (ispeed<<8);
	lcr = DMF_ENA;
	switch (cflag&CSIZE) {
        case CS6:       lpar |= BITS6; break;
        case CS7:       lpar |= BITS7; break;
        case CS8:       lpar |= BITS8; break;
        }
        if (cflag&PARENB)
                lpar |= PENABLE;
        if (!(cflag&PARODD))
                lpar |= EPAR;
        if (cflag&CSTOPB)
                lpar |= TWOSB;

	lpar |= (unit&07);
	addr->lpr = lpar;
	addr->lctms = (addr->lctms &~ 0xff) | lcr;
	splx(s);
	return 0;
}

/*
 * Process a transmit interrupt on an octet.
 */
dmxxint(sc)
	register struct dmx_softc *sc;
{
	register struct tty *tp;
	register struct dmx_octet *addr;
	register int t;

	addr = (struct dmx_octet *)sc->dmx_octet;
	while ((t = addr->csr) & DMF_TI) {
		if (t & DMF_NXM)
			/* SHOULD RESTART OR SOMETHING... */
			printf("dm%c%d: NXM line %d\n", sc->dmx_type,
			    sc->dmx_unit, sc->dmx_unit0 + (t >> 8 & 7));
		t = t >> 8 & 7;
		tp = sc->dmx_tty + t;
		tp->t_state &= ~TS_BUSY;
		if (tp->t_state & TS_FLUSH)
			tp->t_state &= ~TS_FLUSH;
#define new
#ifndef new
		else if (sc->dmx_dmacount[t]) {
			short cntr;

			/*
			 * Do arithmetic in a short to make up
			 * for lost 16&17 bits.
			 */
			addr->csr = DMFIR_TBA | DMF_IE | t;
			cntr = addr->tba -
			    UBACVT(tp->t_outq.c_cf, sc->dmx_ubanum);
			ndflush(&tp->t_outq, (int)cntr);
		}
#else
		else if (sc->dmx_dmacount[t])
			ndflush(&tp->t_outq, sc->dmx_dmacount[t]);
		sc->dmx_dmacount[t] = 0;
#endif
		(*linesw[tp->t_line].l_start)(tp);
	}
}

dmxstart(tp, sc)
	register struct tty *tp;
	struct dmx_softc *sc;
{
	register struct dmx_octet *addr;
	register int unit, nch;
	int s;

	unit = minor(tp->t_dev) & 07;
	addr = (struct dmx_octet *)tp->t_addr;

	/*
	 * Must hold interrupts in following code to prevent
	 * state of the tp from changing.
	 */
	s = spltty();
	/*
	 * If it's currently active, or delaying, no need to do anything.
	 */
	if (tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
		goto out;
	/*
	 * If there are still characters to dma or in the silo,
	 * just reenable the transmitter.
	 */
	addr->csr = DMF_IE | DMFIR_TBUF | unit;
#ifdef new
	if (addr->tsc || sc->dmx_dmacount[unit]) {
#else
	if (addr->tsc) {
#endif
		addr->csr = DMF_IE | DMFIR_LCR | unit;
		addr->lctms = addr->lctms | DMF_TE;
		tp->t_state |= TS_BUSY;
		goto out;
	}
	/*
	 * If there are sleepers, and output has drained below low
	 * water mark, wake up the sleepers.
	 */
	if (tp->t_outq.c_cc <= tp->t_lowat) {
		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;
	if (1 || !(tp->t_oflag&OPOST))	/*XXX*/
		nch = ndqb(&tp->t_outq, 0);
	else {
		if ((nch = ndqb(&tp->t_outq, 0200)) == 0) {
			/*
		 	* If first thing on queue is a delay process it.
		 	*/
			nch = getc(&tp->t_outq);
			timeout(ttrstrt, (caddr_t)tp, (nch&0x7f)+6);
			tp->t_state |= TS_TIMEOUT;
			goto out;
		}
	}
	/*
	 * If characters to transmit, restart transmission.
	 */
	if (nch >= dmx_mindma) {
		register car;

		sc->dmx_dmacount[unit] = nch;
		addr->csr = DMF_IE | DMFIR_LCR | unit;
		addr->lctms = addr->lctms | DMF_TE;
		car = UBACVT(tp->t_outq.c_cf, sc->dmx_ubanum);
		addr->csr = DMF_IE | DMFIR_TBA | unit;
		addr->tba = car;
		addr->tcc = ((car >> 2) & 0xc000) | nch;
		tp->t_state |= TS_BUSY;
	} else if (nch) {
		register char *cp = tp->t_outq.c_cf;
		register int i;

#ifndef new
		sc->dmx_dmacount[unit] = 0;
#endif
		nch = MIN(nch, DMF_SILOCNT);
		addr->csr = DMF_IE | DMFIR_LCR | unit;
		addr->lctms = addr->lctms | DMF_TE;
		addr->csr = DMF_IE | DMFIR_TBUF | unit;
		for (i = 0; i < nch; i++)
			addr->tbuf = *cp++;
		ndflush(&tp->t_outq, nch);
		tp->t_state |= TS_BUSY;
	}
out:
	splx(s);
}

dmxstop(tp, sc, flag)
	register struct tty *tp;
	struct dmx_softc *sc;
{
	register struct dmx_octet *addr;
	register unit = minor(tp->t_dev) & 7;
	int s;

	addr = (struct dmx_octet *)tp->t_addr;
	/*
	 * Block input/output interrupts while messing with state.
	 */
	s = spltty();
	if (flag) {
		addr->csr = DMF_IE | DMFIR_TBUF | unit;
		if (addr->tsc) {
			/*
			 * Flush regardless of whether we're transmitting
			 * (TS_BUSY), if the silo contains untransmitted
			 * characters.
			 */
			addr->csr = DMFIR_LCR | unit | DMF_IE;
			addr->lctms = addr->lctms | DMF_TE | DMF_FLUSH;
			/* this will interrupt so let dmxxint handle the rest */
			tp->t_state |= TS_FLUSH|TS_BUSY;
		}
/*#ifdef new*/
		sc->dmx_dmacount[unit] = 0;
/*#endif*/
	} else {
		/*
		 * Stop transmission by disabling
		 * the transmitter.  We'll pick up where we
		 * left off by reenabling in dmxstart.
		 */
		addr->csr = DMFIR_LCR | unit | DMF_IE;
		addr->lctms = addr->lctms &~ DMF_TE;
		/* no interrupt here */
		tp->t_state &= ~TS_BUSY;
	}
	splx(s);
}
#endif NDMF + NDMZ