[net.sources] dmf.32 driver with working lines 0 and 1

perry@sbcs.UUCP (Perry Kivolowitz) (09/21/83)

many people who have the original version of this driver can't use
0 and 1 for local (hardwired) use - this is because the modem control
routines just didn't make any sense.

You MUST jumper dsr to dtr on 0 and 1


-------------------------------------------------------------------

/*	dmf.c	4.3	82/05/27	*/

#include "dmf.h"
#if NDMF > 0

#define	DMFDMA

/*
 * DMF32 driver
 *
 * TODO:
 *	test with modem
 *	load as much as possible into silo
 *	get correct numbers for receive silo parameter timeout
 *	use auto XON/XOFF
 *	test reset code
 *	optimize for efficient DMA and dynamically
 *	  decide between silo and DMA mode
 *
 * modified by RJ Spanbauer, SUNY/Stony Brook to operate correctly
 * on "modem" ports (ttybase+ [0,1]) without a full complement of RS232
 * handshake lines.
 */

#include "bk.h"
#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/tty.h"
#include "../h/map.h"
#include "../h/pte.h"
#include "../h/buf.h"
#include "../h/vm.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/bk.h"
#include "../h/clist.h"
#include "../h/file.h"

/*
 * Definition of the driver for the auto-configuration program.
 */
int	dmfprobe(), dmfattach(), dmfrint(), dmfxint();
struct	uba_device *dmfinfo[NDMF];
u_short	dmfstd[] = { 0 };
struct	uba_driver dmfdriver =
	{ dmfprobe, 0, dmfattach, 0, dmfstd, "dmf", dmfinfo };

/*
 * In this driver, "dmf" (unqualified) refers to the async portion
 * of the dmf32, "dmfc" to the combo portion, "dmfs" to the sync
 * portion, "dmfl" to the lp portion, and "dmfd" to the dr portion.
 */
struct dmfdevice
{
	short	dmfccsr0;		/* combo csr 0 */
	short	dmfccsr1;		/* combo csr 1 */
	short	dmfs[4];
	short	dmfcsr;			/* control-status register */
	short	dmflpr;			/* line parameter register */
	short	dmfrbuf;		/* receiver buffer (ro) */
	union {
		u_short	dmfirw;		/* indirect register word */
		u_char	dmfirc[2];	/*    "         "    bytes */
	} dmfun;
	short	dmfl[2];
	short	dmfd[4];
};

#define	dmfrsp	dmfrbuf		/* receive silo parameter register (wo) */
#define	dmftbuf	dmfun.dmfirc[0]	/* transmit buffer */
#define	dmftsc	dmfun.dmfirc[0]	/* transmit silo count */
#define	dmfrms	dmfun.dmfirc[1]	/* receive modem status */
#define	dmflcr	dmfun.dmfirc[0]	/* line control register */
#define	dmftms	dmfun.dmfirc[1]	/* transmit modem status */
#define	dmftba	dmfun.dmfirw	/* transmit buffer address */
#define	dmftcc	dmfun.dmfirw	/* transmit character count */

/* bits in dmfcsr */
#define	DMF_TI	0100000		/* transmit interrupt */
#define	DMF_TIE	0040000		/* transmit interrupt enable */
#define	DMF_NXM	0020000		/* non-existant memory */
#define	DMF_LIN	0003400		/* transmit line number */
#define	DMF_RI	0000200		/* receiver interrupt */
#define	DMF_RIE	0000100		/* receiver interrupt enable */
#define	DMF_CLR	0000040		/* master reset */
#define	DMF_IAD	0000037		/* indirect address register */

#define	DMFIR_TBUF	000	/* select tbuf indirect register */
#define	DMFIR_LCR	010	/* select lcr indirect register */
#define	DMFIR_TBA	020	/* select tba indirect register */
#define	DMFIR_TCC	030	/* select tcc indirect register */

/* bits in dmflpr */
#define	BITS6	(01<<3)
#define	BITS7	(02<<3)
#define	BITS8	(03<<3)
#define	TWOSB	0200
#define	PENABLE	040
/* DEC manuals incorrectly say this bit causes generation of even parity. */
#define	OPAR	0100

#define	DMF_IE	(DMF_TIE|DMF_RIE)

#define	DMF_SILOCNT	32	/* size of DMF output silo (per line) */

/* bits in dmfrbuf */
#define	DMF_DSC		0004000		/* data set change */
#define	DMF_PE		0010000		/* parity error */
#define	DMF_FE		0020000		/* framing error */
#define	DMF_DO		0040000		/* data overrun */

/* bits in dmfrms */
#define	DMF_USRR	0004		/* user modem signal (pin 25) */
#define	DMF_SR		0010		/* secondary receive */
#define	DMF_CTS		0020		/* clear to send */
#define	DMF_CAR		0040		/* carrier detect */
#define	DMF_RNG		0100		/* ring */
#define	DMF_DSR		0200		/* data set ready */

/* bits in dmftms */
#define	DMF_USRW	0001		/* user modem signal (pin 18) */
#define	DMF_DTR		0002		/* data terminal ready */
#define	DMF_RATE	0004		/* data signal rate select */
#define	DMF_ST		0010		/* secondary transmit */
#define	DMF_RTS		0020		/* request to send */
#define	DMF_BRK		0040		/* pseudo break bit */
#define	DMF_PREEMPT	0200		/* preempt output */

/* flags for modem control */
#define	DMF_ON	(DMF_DTR|DMF_RTS)
#define	DMF_OFF	0

/* bits in dmflcr */
#define	DMF_MIE		0040		/* modem interrupt enable */
#define	DMF_FLUSH	0020		/* flush transmit silo */
#define	DMF_RBRK	0010		/* real break bit */
#define	DMF_RE		0004		/* receive enable */
#define	DMF_AUTOX	0002		/* auto XON/XOFF */
#define	DMF_TE		0001		/* transmit enable */

#define	DMFLCR_ENA	(DMF_RE|DMF_TE) 

/* bits in dm lsr, copied from dh.c */
#define	DML_USR		0001000		/* usr modem sig, not a real DM bit */
#define	DML_DSR		0000400		/* data set ready, not a real DM bit */
#define	DML_RNG		0000200		/* ring */
#define	DML_CAR		0000100		/* carrier detect */
#define	DML_CTS		0000040		/* clear to send */
#define	DML_SR		0000020		/* secondary receive */
#define	DML_ST		0000010		/* secondary transmit */
#define	DML_RTS		0000004		/* request to send */
#define	DML_DTR		0000002		/* data terminal ready */
#define	DML_LE		0000001		/* line enable */

/*
 * Local variables for the driver
 */
char	dmf_speeds[] =
	{ 0, 0, 1, 2, 3, 4, 0, 5, 6, 7, 010, 012, 014, 016, 017, 0 };

struct	tty dmf_tty[NDMF*8];
char	dmfsoftCAR[NDMF];
int	ndmf = NDMF*8;
int	dmfact;				/* mask of active dmf's */
int	dmfstart(), ttrstrt();

#ifdef DMFDMA
/*
 * The clist space is mapped by the driver 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	dmf_ubinfo[MAXNUBA];		/* info about allocated unibus map */
static int cbase[MAXNUBA];		/* base address in unibus map */
#define	UBACVT(x, uban)		(cbase[uban] + ((x)-(char *)cfree))
#endif

/*
 * Routine for configuration to set dmf interrupt.
 */
/*ARGSUSED*/
dmfprobe(reg, ctlr)
	caddr_t reg;
	int ctlr;
{
	register int br, cvec;		/* these are ``value-result'' */
	register struct dmfdevice *dmfaddr = (struct dmfdevice *)reg;

#ifdef lint
	br = 0; cvec = br; br = cvec;
#endif
	br = 0x15;
	cvec = (uba_hd[numuba].uh_lastiv -= 4*8);
	dmfaddr->dmfccsr0 = cvec >> 2;
	/* NEED TO SAVE IT SOMEWHERE FOR OTHER DEVICES */
	return (1);
}

/*
 * Routine called to attach a dmf.
 */
dmfattach(ui)
	struct uba_device *ui;
{
	printf("dmf flags: %x\n" , ui->ui_flags);
	dmfsoftCAR[ui->ui_unit] = ui->ui_flags;
}


/*
 * Open a DMF32 line, mapping the clist onto the uba if this
 * is the first dmf on this uba.  Turn on this dmf if this is
 * the first use of it.
 */
/*ARGSUSED*/
dmfopen(dev, flag)
	dev_t dev;
{
	register struct tty *tp;
	register int unit, dmf;
	register struct dmfdevice *addr;
	register struct uba_device *ui;
	int s;

	unit = minor(dev);
	dmf = unit >> 3;
	if (unit >= NDMF*8 || (ui = dmfinfo[dmf])== 0 || ui->ui_alive == 0) {
		u.u_error = ENXIO;
		return;
	}
#ifdef	DEBUG
	printf("dmf%d,%d opening\n", dmf, unit);
#endif
	tp = &dmf_tty[unit];
	if (tp->t_state&XCLUDE && u.u_uid!=0) {
		u.u_error = EBUSY;
		return;
	}
	addr = (struct dmfdevice *)ui->ui_addr;
	tp->t_addr = (caddr_t)addr;
	tp->t_oproc = dmfstart;
	tp->t_state |= WOPEN;
	/*
	 * While setting up state for this uba and this dmf,
	 * block uba resets which can clear the state.
	 */
	s = spl5();
#ifdef DMFDMA
	if (dmf_ubinfo[ui->ui_ubanum] == 0) {
		dmf_ubinfo[ui->ui_ubanum] =
		    uballoc(ui->ui_ubanum, (caddr_t)cfree,
			nclist*sizeof(struct cblock), 0);
		cbase[ui->ui_ubanum] = dmf_ubinfo[ui->ui_ubanum]&0x3ffff;
	}
#endif
	if ((dmfact&(1<<dmf)) == 0) {
		addr->dmfcsr |= DMF_IE;
		dmfact |= (1<<dmf);
		addr->dmfrsp = 1;	/* DON'T KNOW WHAT TO SET IT TO YET */
	}
	splx(s);
	/*
	 * If this is first open, initialze tty state to default.
	 */
	if ((tp->t_state&ISOPEN) == 0) {
		ttychars(tp);
		if (tp->t_ispeed == 0) {
			tp->t_ispeed = B300;
			tp->t_ospeed = B300;
			tp->t_flags = ODDP|EVENP|ECHO;
		}
		dmfparam(unit);
	}
	/*
	 * Wait for carrier, then process line discipline specific open.
	 */
	if ((dmfmctl(dev, DMF_ON, DMSET) & (DMF_CAR<<8)) ||
	    (dmfsoftCAR[dmf] & (1<<(unit&07))))
		tp->t_state |= CARR_ON;
	s = spl5();
	while ((tp->t_state & CARR_ON) == 0) { 
		tp->t_state |= WOPEN;
		sleep((caddr_t)&tp->t_rawq, TTIPRI);
	}

	splx(s);
	(*linesw[tp->t_line].l_open)(dev, tp);
#ifdef	DEBUG
	printf("exit open\n");
#endif
}

/*
 * Close a DMF32 line.
 */
/*ARGSUSED*/
dmfclose(dev, flag)
	dev_t dev;
	int flag;
{
	register struct tty *tp;
	register unit;

	unit = minor(dev);
	tp = &dmf_tty[unit];
#ifdef	DEBUG
	printf("dmf%d,%d close\n", unit>>3, unit);
#endif
	(*linesw[tp->t_line].l_close)(tp);
	dmfmctl(unit, DMF_BRK, DMBIC);
	if (tp->t_state&HUPCLS || (tp->t_state&ISOPEN)==0)
		dmfmctl(unit, DMF_OFF, DMSET);
	ttyclose(tp);
}

dmfread(dev)
	dev_t dev;
{
	register struct tty *tp;


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

dmfwrite(dev)
	dev_t dev;
{
	register struct tty *tp;

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

/*
 * DMF32 receiver interrupt.
 */
dmfrint(dmf)
	int dmf;
{
	register struct tty *tp;
	register c;
	register struct dmfdevice *addr;
	register struct tty *tp0;
	register struct uba_device *ui;
	int overrun = 0;

	ui = dmfinfo[dmf];
	if (ui == 0 || ui->ui_alive == 0)
		return;
	addr = (struct dmfdevice *)ui->ui_addr;
	tp0 = &dmf_tty[dmf<<3];
	/*
	 * Loop fetching characters from the silo for this
	 * dmf until there are no more in the silo.
	 */
	while ((c = addr->dmfrbuf) < 0) {
		tp = tp0 + ((c>>8)&07);
		if (c & DMF_DSC) {
			addr->dmfcsr = DMF_IE | DMFIR_TBUF | ((c>>8)&07);
			if (addr->dmfrms & DMF_CAR) {
				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) {
					gsignal(tp->t_pgrp, SIGHUP);
					gsignal(tp->t_pgrp, SIGCONT);
					addr->dmfcsr = DMF_IE | DMFIR_LCR |
						((c>>8)&07);
					addr->dmfun.dmfirw &= 0xff;
					flushtty(tp, FREAD|FWRITE);
				}
				tp->t_state &= ~CARR_ON;
			}
#ifdef	DEBUG
			printf("data set change\n");
#endif
			continue;
		}
		if ((tp->t_state&ISOPEN)==0) {
			wakeup((caddr_t)tp);
#ifdef	DEBUG
			printf("wasn't open\n");
#endif
			continue;
		}
		if (c & DMF_PE)
			if ((tp->t_flags&(EVENP|ODDP))==EVENP
			 || (tp->t_flags&(EVENP|ODDP))==ODDP ) {
#ifdef	DEBUG
				printf("parity error\n");
#endif
				continue;
		}
		if ((c & DMF_DO) && overrun == 0) {
			printf("dmf%d: silo overflow\n", dmf);
			overrun = 1;
		}
		if (c & DMF_FE)
			/*
			 * 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 = tun.t_intrc;
#if NBK > 0
		if (tp->t_line == NETLDISC) {
			c &= 0177;
			BKINPUT(c, tp);
		} else
#endif
			(*linesw[tp->t_line].l_rint)(c, tp);
	}
}

/*
 * Ioctl for DMF32.
 */
/*ARGSUSED*/
dmfioctl(dev, cmd, addr, flag)
	dev_t dev;
	caddr_t addr;
{
	register struct tty *tp;
	register int unit = minor(dev);
	register int dmf = unit >> 3;
	register struct device *dmfaddr;
	int temp;
 
	tp = &dmf_tty[unit];
	cmd = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr);
	if (cmd == 0)
		return;
	if (ttioctl(tp, cmd, addr, flag)) {
		if (cmd==TIOCSETP || cmd==TIOCSETN)
			dmfparam(unit);
	} else switch(cmd) {

	case TIOCSBRK:
		dmfmctl(dev, DMF_BRK, DMBIS);
		break;
	case TIOCCBRK:
		dmfmctl(dev, DMF_BRK, DMBIC);
		break;
	case TIOCSDTR:
		dmfmctl(dev, DMF_DTR|DMF_RTS, DMBIS);
		break;
	case TIOCCDTR:
		dmfmctl(dev, DMF_DTR|DMF_RTS, DMBIC);
		break;
	case TIOCMSET:
		if (copyin(addr, (caddr_t) &temp, sizeof(temp)))
			u.u_error = EFAULT;
		else
			dmfmctl(dev, dmtodmf(temp), DMSET);
		break;
	case TIOCMBIS:
		if (copyin(addr, (caddr_t) &temp, sizeof(temp)))
			u.u_error = EFAULT;
		else
			dmfmctl(dev, dmtodmf(temp), DMBIS);
		break;
	case TIOCMBIC:
		if (copyin(addr, (caddr_t) &temp, sizeof(temp)))
			u.u_error = EFAULT;
		else
			dmfmctl(dev, dmtodmf(temp), DMBIC);
		break;
	case TIOCMGET:
		temp = dmftodm(dmfmctl(dev, 0, DMGET));
		if (copyout((caddr_t) &temp, addr, sizeof(temp)))
			u.u_error = EFAULT;
		break;
	default:
		u.u_error = ENOTTY;
	}
}

dmtodmf(bits)
	register int bits;
{
	register int b;

	b = bits & 012;
	if (bits & DML_ST) b |= DMF_RATE;
	if (bits & DML_RTS) b |= DMF_RTS;
	if (bits & DML_USR) b |= DMF_USRW;
	return(b);
}

dmftodm(bits)
	register int bits;
{
	register int b;

	b = (bits & 012) | ((bits >> 7) & 0760) | DML_LE;
	if (bits & DMF_USRR) b |= DML_USR;
	if (bits & DMF_RTS) b |= DML_RTS;
	return(b);
}
 

/*
 * Set parameters from open or stty into the DMF hardware
 * registers.
 */
dmfparam(unit)
	register int unit;
{
	register struct tty *tp;
	register struct dmfdevice *addr;
	register int lpar, lcr;
	int s;

	tp = &dmf_tty[unit];
	addr = (struct dmfdevice *)tp->t_addr;
	/*
	 * Block interrupts so parameters will be set
	 * before the line interrupts.
	 */
	s = spl5();
	addr->dmfcsr = (unit&07) | DMFIR_LCR | DMF_IE;
	if ((tp->t_ispeed)==0) {
		tp->t_state |= HUPCLS;
		dmfmctl(unit, DMF_OFF, DMSET);
		return;
	}
	lpar = (dmf_speeds[tp->t_ospeed]<<12) | (dmf_speeds[tp->t_ispeed]<<8);
	lcr = DMFLCR_ENA;
	if ((tp->t_ispeed) == B134)
		lpar |= BITS6|PENABLE;
	else if ((tp->t_flags&RAW) || (tp->t_local&LLITOUT))
		lpar |= BITS8;
	else {
		lpar |= BITS7|PENABLE;
		/* CHECK FOR XON/XOFF AND SET lcr |= DMF_AUTOX; */
	}
	if ((tp->t_flags&EVENP) == 0)
		lpar |= OPAR;
	if ((tp->t_ospeed) == B110)
		lpar |= TWOSB;
	lpar |= (unit&07);
	addr->dmflpr = lpar;
	addr->dmfun.dmfirw = lcr | (addr->dmftms << 8);
	splx(s);
}

/*
 * DMF32 transmitter interrupt.
 * Restart the idle line.
 */
dmfxint(dmf)
	int dmf;
{
	register struct tty *tp;
	register struct dmfdevice *addr;
	register struct uba_device *ui;
	register int unit, t;
#ifdef DMFDMA
	short cntr;
#endif

	ui = dmfinfo[dmf];
	addr = (struct dmfdevice *)ui->ui_addr;
	while ((t = addr->dmfcsr) & DMF_TI) {
		unit = dmf*8 + ((t>>8)&07);
		tp = &dmf_tty[unit];
		tp->t_state &= ~BUSY;
		if (t & DMF_NXM) {
			printf("dmf%d: NXM line %d\n", dmf, unit&7);
			/* SHOULD RESTART OR SOMETHING... */
		}
		if (tp->t_state&FLUSH)
			tp->t_state &= ~FLUSH;
#ifdef DMFDMA
		else {
			addr->dmfcsr = DMFIR_TBUF | DMF_IE | (unit&07);
			if (addr->dmftsc == 0) {
				/*
				 * Do arithmetic in a short to make up
				 * for lost 16&17 bits.
				 */
				addr->dmfcsr = DMFIR_TBA | DMF_IE | (unit&07);
				cntr = addr->dmftba -
				    UBACVT(tp->t_outq.c_cf, ui->ui_ubanum);
				ndflush(&tp->t_outq, (int)cntr);
			}
		}
#endif
		if (tp->t_line)
			(*linesw[tp->t_line].l_start)(tp);
		else
			dmfstart(tp);
	}
}

/*
 * Start (restart) transmission on the given DMF32 line.
 */
dmfstart(tp)
	register struct tty *tp;
{
	register struct dmfdevice *addr;
	register int car, dmf, unit, nch;
	int s;

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

	/*
	 * Must hold interrupts in following code to prevent
	 * state of the tp from changing.
	 */
	s = spl5();
	/*
	 * If it's currently active, or delaying, no need to do anything.
	 */
	if (tp->t_state&(TIMEOUT|BUSY|TTSTOP))
		goto out;
	/*
	 * If there are still characters in the silo,
	 * just reenable the transmitter.
	 */
	addr->dmfcsr = DMF_IE | DMFIR_TBUF | unit;
	if (addr->dmftsc) {
		addr->dmfcsr = DMF_IE | DMFIR_LCR | unit;
		addr->dmfun.dmfirw |= DMF_TE;
		tp->t_state |= BUSY;
		goto out;
	}
	/*
	 * If there are sleepers, and output has drained below low
	 * water mark, wake up the sleepers.
	 */
	if ((tp->t_state&ASLEEP) && tp->t_outq.c_cc<=TTLOWAT(tp)) {
		tp->t_state &= ~ASLEEP;
		wakeup((caddr_t)&tp->t_outq);
	}
	/*
	 * Now restart transmission unless the output queue is
	 * empty.
	 */
	if (tp->t_outq.c_cc == 0)
		goto out;
	if (tp->t_flags&RAW || tp->t_local&LLITOUT)
		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);
			timeout(ttrstrt, (caddr_t)tp, (nch&0x7f)+6);
			tp->t_state |= TIMEOUT;
			goto out;
		}
	}
	/*
	 * If characters to transmit, restart transmission.
	 */
	if (nch) {
#ifdef DMFDMA
		addr->dmfcsr = DMF_IE | DMFIR_LCR | unit;
		addr->dmfun.dmfirw |= DMF_TE;
		car = UBACVT(tp->t_outq.c_cf, dmfinfo[dmf]->ui_ubanum);
		addr->dmfcsr = DMF_IE | DMFIR_TBA | unit;
		addr->dmftba = car;
		addr->dmftcc = ((car>>2)&0xc000) | nch;
#else
		register char *cp = tp->t_outq.c_cf;
		register int i;

		nch = MIN(nch, DMF_SILOCNT);
		addr->dmfcsr = DMF_IE | DMFIR_LCR | unit;
		addr->dmfun.dmfirw |= DMF_TE;
		addr->dmfcsr = DMF_IE | DMFIR_TBUF | unit;
		for (i = 0; i < nch; i++)
			addr->dmftbuf = *cp++;
		ndflush(&tp->t_outq, nch);
#endif
		tp->t_state |= BUSY;
	}
out:
	splx(s);
}

/*
 * Stop output on a line, e.g. for ^S/^Q or output flush.
 */
/*ARGSUSED*/
dmfstop(tp, flag)
	register struct tty *tp;
{
	register struct dmfdevice *addr;
	register int unit, s;

	addr = (struct dmfdevice *)tp->t_addr;
	/*
	 * Block input/output interrupts while messing with state.
	 */
	s = spl5();
	if (tp->t_state & BUSY) {
		/*
		 * Device is transmitting; stop output
		 * by selecting the line and disabling
		 * the transmitter.  If this is a flush
		 * request then flush the output silo,
		 * otherwise we will pick up where we
		 * left off by enabling the transmitter.
		 */
		unit = minor(tp->t_dev);
		addr->dmfcsr = DMFIR_LCR | (unit&07) | DMF_IE;
		addr->dmfun.dmfirw &= ~DMF_TE;
		if ((tp->t_state&TTSTOP)==0) {
			tp->t_state |= FLUSH;
			addr->dmflcr |= DMF_FLUSH;
		} else
			tp->t_state &= ~BUSY;
	}
	splx(s);
}

/*
 * DMF32 modem control
 */
dmfmctl(dev, bits, how)
	dev_t dev;
	int bits, how;
{
	register struct dmfdevice *dmfaddr;
	register int unit, mbits, lcr;
	int s;

	unit = minor(dev);
	dmfaddr = (struct dmfdevice *)(dmf_tty[unit].t_addr);
	unit &= 07;
	s = spl5();
	dmfaddr->dmfcsr = DMF_IE | DMFIR_TBUF | unit;
	mbits = dmfaddr->dmfrms << 8;
	dmfaddr->dmfcsr = DMF_IE | DMFIR_LCR | unit;
	mbits |= dmfaddr->dmftms;
	lcr = dmfaddr->dmflcr;
	switch (how) {
	case DMSET:
		mbits = bits;
		break;

	case DMBIS:
		mbits |= bits;
		break;

	case DMBIC:
		mbits &= ~bits;
		break;

	case DMGET:
		(void) splx(s);
		return(mbits);
	}
	if (mbits & DMF_BRK)
		lcr |= DMF_RBRK;
	else
		lcr &= ~DMF_RBRK;
	lcr &= ~DMF_MIE;  /* for local use, this bit causes only problems*/
	dmfaddr->dmfun.dmfirw = ((mbits & 037) << 8) + (lcr & 0377);
	(void) splx(s);
	return(mbits);
}

/*
 * Reset state of driver if UBA reset was necessary.
 * Reset the csr, lpr, and lcr registers on open lines, and
 * restart transmitters.
 */
dmfreset(uban)
	int uban;
{
	register int dmf, unit;
	register struct tty *tp;
	register struct uba_device *ui;
	register struct dmfdevice *addr;
	int i;

#ifdef DMFDMA
	if (dmf_ubinfo[uban] == 0)
		return;
	ubarelse(uban, &dmf_ubinfo[uban]);
	dmf_ubinfo[uban] = uballoc(uban, (caddr_t)cfree,
	    nclist*sizeof (struct cblock), 0);
	cbase[uban] = dmf_ubinfo[uban]&0x3ffff;
#endif
	for (dmf = 0; dmf < NDMF; dmf++) {
		ui = dmfinfo[dmf];
		if (ui == 0 || ui->ui_alive == 0 || ui->ui_ubanum != uban)
			continue;
		printf(" dmf%d", dmf);
		addr = (struct dmfdevice *)ui->ui_addr;
		addr->dmfcsr = DMF_IE;
		addr->dmfrsp = 1;
		unit = dmf * 8;
		for (i = 0; i < 8; i++) {
			tp = &dmf_tty[unit];
			if (tp->t_state & (ISOPEN|WOPEN)) {
				dmfparam(unit);
				dmfmctl(unit, DMF_ON, DMSET);
				tp->t_state &= ~BUSY;
				dmfstart(tp);
			}
			unit++;
		}
	}
}

/* stubs for interrupt routines for devices not yet supported */

dmfsrint() { printf("dmfsrint\n"); }

dmfsxint() { printf("dmfsxint\n"); }

dmfdaint() { printf("dmfdaint\n"); }

dmfdbint() { printf("dmfdbint\n"); }

dmflint() { printf("dmflint\n"); }
#endif