4.3BSD-UWisc/src/sys/vaxuba/dmf.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.
 *
 *	@(#)dmf.c	7.1 (Berkeley) 6/5/86
 */
#ifndef lint
static char rcs_id[] = {"$Header: dmf.c,v 3.1 86/10/22 14:02:36 tadl Exp $"};
#endif not lint
/*
 * RCS Info
 *	$Locker:  $
 */

#include "dmf.h"
#if NDMF > 0
/*
 * DMF32 driver
 *
 *
 * TODO:
 *	test with modem
 *	load as much as possible into silo
 *	use auto XON/XOFF
 *	test reset code
 ****************************
 * DMF32 line printer driver
 *
 * the line printer on dmfx is indicated by a minor device code of 128+x
 *
 * the flags field of the config file is interpreted like so:
 * bits		meaning
 * ----		-------
 * 0-7		soft carrier bits for ttys part of dmf32
 * 8-15		number of cols/line on the line printer
 *			if 0, 132 will be used.
 * 16-23	number of lines/page on the line printer
 *			if 0, 66 will be used.
 * 24		if 1 DO NOT use the auto format mode of the
 *			line printer parallel port
 */
#include "../machine/pte.h"

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

#include "ubareg.h"
#include "ubavar.h"
#include "dmfreg.h"

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

int	dmf_timeout = 10;		/* silo timeout, in ms */
int	dmf_mindma = 4;			/* don't dma below this point */

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

#ifndef	PORTSELECTOR
#define	ISPEED	B9600
#define	IFLAGS	(EVENP|ODDP|ECHO)
#else
#define	ISPEED	B4800
#define	IFLAGS	(EVENP|ODDP)
#endif

struct	tty dmf_tty[NDMF*8];
char	dmfsoftCAR[NDMF];

struct dmfl_softc {
	u_int	dmfl_state; 		/* soft state bits */
	int	dmfl_info;		/* uba info */
	u_short	dmfl_lines;		/* lines per page (66 def.) */
	u_short	dmfl_cols; 		/* cols per line (132 def.) */
	u_short	dmfl_format;		/* fflag for auto form feed */
	char	dmfl_buf[DMFL_BUFSIZ];
} dmfl_softc[NDMF];

/*
 * convert device number into DMF line printer unit number
 */
#define	DMFL_UNIT(d)	(minor(d)&0xF)	/* up to 16 DMFs */

#define ASLP 1		/* waiting for interrupt from dmf */
#define OPEN 2		/* line printer is open */
#define ERROR 4		/* error while printing, driver
			 refuses to do anything till closed */
#define MOREIO 8	/* more data for printer */

#ifndef lint
int	ndmf = NDMF*8;			/* used by iostat */
#endif
int	dmfact;				/* mask of active dmf's */
int	dmfstart(), ttrstrt();

/*
 * 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[NUBA];		/* info about allocated unibus map */
int	cbase[NUBA];			/* base address in unibus map */
#define	UBACVT(x, uban)		(cbase[uban] + ((x)-(char *)cfree))
char	dmf_dma[NDMF*8];

/*
 * Routine for configuration to set dmf interrupt.
 */
/*ARGSUSED*/
dmfprobe(reg, ctlr)
	caddr_t reg;
	struct uba_device *ctlr;
{
	register int br, cvec;		/* these are ``value-result'' */
	register struct dmfdevice *dmfaddr = (struct dmfdevice *)reg;
	register int i;
	register unsigned int a;
	static char *dmfdevs[]=
		{"parallel","printer","synch","asynch"};
	unsigned int dmfoptions;
	static int (*intrv[3])() = { (int (*)())0, (int (*)())0, (int (*)())0 };

#ifdef lint
	br = 0; cvec = br; br = cvec;
	dmfxint(0); dmfrint(0);
	dmfsrint(); dmfsxint(); dmfdaint(); dmfdbint(); dmflint(0);
#endif
	/*
	 * Pick the usual size DMF vector here (don't decrement it here).
	 * grab configuration; note that the DMF32
	 * doesn't seem to put the right bits in this
	 * register until AFTER the interrupt vector is set.
	 */
	br = 0x15;
	cvec = (uba_hd[numuba].uh_lastiv - 4*8);
	dmfaddr->dmfccsr0 = (cvec >> 2);
	dmfoptions = dmfaddr->dmfccsr0 & DMFC_CONFMASK;

	/* catch a couple of special cases:  Able vmz/32n and vmz/lp	*/
	if (dmfoptions == DMFC_ASYNC) {
		/* Async portion only */

		cvec = (uba_hd[numuba].uh_lastiv -= 8);
		dmfaddr->dmfccsr0 = (cvec - 2*8) >> 2;
		intrv[0] = ctlr->ui_intr[4];
		intrv[1] = ctlr->ui_intr[5];
		ctlr->ui_intr = intrv;
	} else if (dmfoptions == DMFC_LP) {
		/* LP portion only */

		cvec = (uba_hd[numuba].uh_lastiv -= 8);
		ctlr->ui_intr = &ctlr->ui_intr[6];
	} else if (dmfoptions == (DMFC_LP|DMFC_ASYNC)) {
		/* LP ans Async portions only */

		cvec = (uba_hd[numuba].uh_lastiv -= 2*8);
		ctlr->ui_intr = &ctlr->ui_intr[4];
	} else {
		/* All other configurations get everything */

		cvec = (uba_hd[numuba].uh_lastiv -= 4*8);
	}
	a = (dmfoptions >> 12) & 0xf;
	printf("dmf%d:", ctlr->ui_unit);
	for (i = 0; a != 0; ++i, a >>= 1) {
		if (a & 1)
			printf(" %s",dmfdevs[i]);
	}
	printf(".\n");

	if (dmfoptions & DMFC_LP)
		dmfaddr->dmfl_ctrl = DMFL_RESET;
	return (sizeof (struct dmfdevice));
}

/*
 * Routine called to attach a dmf.
 */
dmfattach(ui)
	struct uba_device *ui;
{
	register int cols = (ui->ui_flags>>8) & 0xff;
	register int lines = (ui->ui_flags>>16) & 0xff;

	dmfsoftCAR[ui->ui_unit] = ui->ui_flags & 0xff;
	dmfl_softc[ui->ui_unit].dmfl_cols = cols == 0 ? DMFL_DEFCOLS : cols;
	dmfl_softc[ui->ui_unit].dmfl_lines = lines == 0 ? DMFL_DEFLINES : lines;
 	if ((ui->ui_flags >> 24) & 0x1)
 		dmfl_softc[ui->ui_unit].dmfl_format = (2 << 8);
 	else
 		dmfl_softc[ui->ui_unit].dmfl_format = (2 << 8) | DMFL_FORMAT;
	cbase[ui->ui_ubanum] = -1;
}


/*
 * 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);
	if (unit & 0200)
		return (dmflopen(dev,flag));
	dmf = unit >> 3;
	if (unit >= NDMF*8 || (ui = dmfinfo[dmf])== 0 || ui->ui_alive == 0)
		return (ENXIO);
	tp = &dmf_tty[unit];
	if (tp->t_state&TS_XCLUDE && u.u_uid!=0)
		return (EBUSY);
	addr = (struct dmfdevice *)ui->ui_addr;
	tp->t_addr = (caddr_t)addr;
	tp->t_oproc = dmfstart;
	tp->t_state |= TS_WOPEN;
	/*
	 * While setting up state for this uba and this dmf,
	 * block uba resets which can clear the state.
	 */
	s = spltty();
	if (cbase[ui->ui_ubanum] == -1) {
		dmf_ubinfo[ui->ui_ubanum] =
		    uballoc(ui->ui_ubanum, (caddr_t)cfree,
			nclist*sizeof(struct cblock), 0);
		cbase[ui->ui_ubanum] = UBAI_ADDR(dmf_ubinfo[ui->ui_ubanum]);
	}
	if ((dmfact&(1<<dmf)) == 0) {
		addr->dmfcsr |= DMF_IE;
		dmfact |= (1<<dmf);
		addr->dmfrsp = dmf_timeout;
	}
	splx(s);
	/*
	 * If this is first open, initialize tty state to default.
	 */
	if ((tp->t_state&TS_ISOPEN) == 0) {
		ttychars(tp);
#ifndef PORTSELECTOR
		if (tp->t_ispeed == 0) {
#else
			tp->t_state |= TS_HUPCLS;
#endif PORTSELECTOR
			tp->t_ispeed = ISPEED;
			tp->t_ospeed = ISPEED;
			tp->t_flags = IFLAGS;
#ifndef PORTSELECTOR
		}
#endif PORTSELECTOR
		dmfparam(unit);
	}
	/*
	 * Wait for carrier, then process line discipline specific open.
	 */
	s = spltty();
	for (;;) {
		if ((dmfmctl(dev, DMF_ON, DMSET) & (DMF_CAR<<8)) ||
		    (dmfsoftCAR[dmf] & (1<<(unit&07))))
			tp->t_state |= TS_CARR_ON;
		if (tp->t_state & TS_CARR_ON)
			break;
		tp->t_state |= TS_WOPEN;
		sleep((caddr_t)&tp->t_rawq, TTIPRI);
	}
	splx(s);
	return ((*linesw[tp->t_line].l_open)(dev, tp));
}

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

	unit = minor(dev);
	if (unit & 0200) {
		dmflclose(dev,flag);
		return;
	}
		
	tp = &dmf_tty[unit];
	(*linesw[tp->t_line].l_close)(tp);
	(void) dmfmctl(unit, DMF_BRK, DMBIC);
	if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0)
		(void) dmfmctl(unit, DMF_OFF, DMSET);
	ttyclose(tp);
}

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

	if (minor(dev) & 0200)
		return(ENXIO);
	tp = &dmf_tty[minor(dev)];
	return ((*linesw[tp->t_line].l_read)(tp, uio));
}

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

	if (minor(dev) & 0200)
		return (dmflwrite(dev,uio));
	tp = &dmf_tty[minor(dev)];
	return ((*linesw[tp->t_line].l_write)(tp, uio));
}

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

	ui = dmfinfo[dmf];
	if (ui == 0 || ui->ui_alive == 0)
		return;
	addr = (struct dmfdevice *)ui->ui_addr;
	tp0 = &dmf_tty[dmf * 8];
	/*
	 * Loop fetching characters from the silo for this
	 * dmf until there are no more in the silo.
	 */
	while ((c = addr->dmfrbuf) < 0) {

		unit = (c >> 8) & 07;
		tp = tp0 + unit;
		if (c & DMF_DSC) {
			addr->dmfcsr = DMF_IE | DMFIR_TBUF | unit;
			if (addr->dmfrms & DMF_CAR)
				(void)(*linesw[tp->t_line].l_modem)(tp, 1);
			else if ((dmfsoftCAR[dmf] & (1 << unit)) == 0 &&
			    (*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
				addr->dmfcsr = DMF_IE | DMFIR_LCR | unit;
				addr->dmflctms = DMFLCR_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)
				if ((tp->t_flags & (EVENP|ODDP)) == EVENP
			 	|| (tp->t_flags & (EVENP|ODDP)) == ODDP)
					continue;
			if ((c & DMF_DO) && overrun == 0) {
				log(LOG_WARNING, "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 = tp->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, data, flag)
	dev_t dev;
	caddr_t data;
{
	register struct tty *tp;
	register int unit = minor(dev);
	int error;
 
	if (unit & 0200)
		return (ENOTTY);
	tp = &dmf_tty[unit];
	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) {
		if (cmd == TIOCSETP || cmd == TIOCSETN || cmd == TIOCLBIS ||
		    cmd == TIOCLBIC || cmd == TIOCLSET)
			dmfparam(unit);
		return (error);
	}
	switch (cmd) {

	case TIOCSBRK:
		(void) dmfmctl(dev, DMF_BRK, DMBIS);
		break;

	case TIOCCBRK:
		(void) dmfmctl(dev, DMF_BRK, DMBIC);
		break;

	case TIOCSDTR:
		(void) dmfmctl(dev, DMF_DTR|DMF_RTS, DMBIS);
		break;

	case TIOCCDTR:
		(void) dmfmctl(dev, DMF_DTR|DMF_RTS, DMBIC);
		break;

	case TIOCMSET:
		(void) dmfmctl(dev, dmtodmf(*(int *)data), DMSET);
		break;

	case TIOCMBIS:
		(void) dmfmctl(dev, dmtodmf(*(int *)data), DMBIS);
		break;

	case TIOCMBIC:
		(void) dmfmctl(dev, dmtodmf(*(int *)data), DMBIC);
		break;

	case TIOCMGET:
		*(int *)data = dmftodm(dmfmctl(dev, 0, DMGET));
		break;

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

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 = spltty();
	addr->dmfcsr = (unit&07) | DMFIR_LCR | DMF_IE;
	if ((tp->t_ispeed)==0) {
		tp->t_state |= TS_HUPCLS;
		(void) dmfmctl(unit, DMF_OFF, DMSET);
		splx(s);
		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|LITOUT|PASS8))
		lpar |= BITS8;
	else {
		lpar |= BITS7|PENABLE;
		/* CHECK FOR XON/XOFF AND SET lcr |= DMF_AUTOX; */
	}
	if (tp->t_flags&EVENP)
		lpar |= EPAR;
	if ((tp->t_ospeed) == B110)
		lpar |= TWOSB;
	lpar |= (unit&07);
	addr->dmflpr = lpar;
	addr->dmflctms = (addr->dmflctms &~ 0xff) | lcr;
	splx(s);
}

/*
 * DMF32 transmitter interrupt.
 * Restart the idle line.
 */
dmfxint(dmf)
	int dmf;
{
	int unit0 = dmf * 8;
	struct tty *tp0 = &dmf_tty[unit0];
	register struct tty *tp;
	register struct dmfdevice *addr;
	register struct uba_device *ui;
	register int t;
	short cntr;

	ui = dmfinfo[dmf];
	addr = (struct dmfdevice *)ui->ui_addr;
	while ((t = addr->dmfcsr) & DMF_TI) {
		if (t & DMF_NXM)
			/* SHOULD RESTART OR SOMETHING... */
			printf("dmf%d: NXM line %d\n", dmf, t >> 8 & 7);
		t = t >> 8 & 7;
		tp = tp0 + t;
		tp->t_state &= ~TS_BUSY;
		if (tp->t_state&TS_FLUSH)
			tp->t_state &= ~TS_FLUSH;
		else if (dmf_dma[unit0 + t]) {
			/*
			 * Do arithmetic in a short to make up
			 * for lost 16&17 bits.
			 */
			addr->dmfcsr = DMFIR_TBA | DMF_IE | t;
			cntr = addr->dmftba -
			    UBACVT(tp->t_outq.c_cf, ui->ui_ubanum);
			ndflush(&tp->t_outq, (int)cntr);
		}
		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 unit, nch;
	int s;
	register int dmf;

	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 = 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 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->dmflctms = addr->dmflctms | 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<=TTLOWAT(tp)) {
		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 (tp->t_flags & (RAW|LITOUT))
		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 >= dmf_mindma) {
		register car;

		dmf_dma[minor(tp->t_dev)] = 1;
		addr->dmfcsr = DMF_IE | DMFIR_LCR | unit;
		addr->dmflctms = addr->dmflctms | 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;
		tp->t_state |= TS_BUSY;
	} else if (nch) {
		register char *cp = tp->t_outq.c_cf;
		register int i;

		dmf_dma[minor(tp->t_dev)] = 0;
		nch = MIN(nch, DMF_SILOCNT);
		addr->dmfcsr = DMF_IE | DMFIR_LCR | unit;
		addr->dmflctms = addr->dmflctms | DMF_TE;
		addr->dmfcsr = DMF_IE | DMFIR_TBUF | unit;
		for (i = 0; i < nch; i++)
			addr->dmftbuf = *cp++;
		ndflush(&tp->t_outq, nch);
		tp->t_state |= TS_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 unit = minor(tp->t_dev) & 7;
	int s;

	addr = (struct dmfdevice *)tp->t_addr;
	/*
	 * Block input/output interrupts while messing with state.
	 */
	s = spltty();
	if (flag) {
		addr->dmfcsr = DMF_IE | DMFIR_TBUF | unit;
		if (addr->dmftsc) {
			/*
			 * Flush regardless of whether we're transmitting
			 * (TS_BUSY), if the silo contains untransmitted
			 * characters.
			 */
			addr->dmfcsr = DMFIR_LCR | unit | DMF_IE;
			addr->dmflctms = addr->dmflctms | DMF_TE | DMF_FLUSH;
			/* this will interrupt so let dmfxint handle the rest */
			tp->t_state |= TS_FLUSH|TS_BUSY;
		}
	} else {
		if (tp->t_state & TS_BUSY) {
			/*
			 * Stop transmission by disabling
			 * the transmitter.  We'll pick up where we
			 * left off by reenabling in dmfstart.
			 */
			addr->dmfcsr = DMFIR_LCR | unit | DMF_IE;
			addr->dmflctms = addr->dmflctms &~ DMF_TE;
			/* no interrupt here */
			tp->t_state &= ~TS_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 = spltty();
	dmfaddr->dmfcsr = DMF_IE | DMFIR_TBUF | unit;
	mbits = dmfaddr->dmfrms << 8;
	dmfaddr->dmfcsr = DMF_IE | DMFIR_LCR | unit;
	lcr = dmfaddr->dmflctms;
	mbits |= (lcr & 0xff00) >> 8;
	switch (how) {
	case DMSET:
		mbits = (mbits &0xff00) | 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;
	dmfaddr->dmflctms = ((mbits & 037) << 8) | (lcr & 0xff);
	(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;

	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);
		if (dmf_ubinfo[uban]) {
			dmf_ubinfo[uban] = uballoc(uban, (caddr_t)cfree,
			    nclist*sizeof (struct cblock), 0);
			cbase[uban] = UBAI_ADDR(dmf_ubinfo[uban]);
		}
		addr = (struct dmfdevice *)ui->ui_addr;
		addr->dmfcsr = DMF_IE;
		addr->dmfrsp = dmf_timeout;
		unit = dmf * 8;
		for (i = 0; i < 8; i++) {
			tp = &dmf_tty[unit];
			if (tp->t_state & (TS_ISOPEN|TS_WOPEN)) {
				dmfparam(unit);
				(void) dmfmctl(unit, DMF_ON, DMSET);
				tp->t_state &= ~TS_BUSY;
				dmfstart(tp);
			}
			unit++;
		}
	}
}

/*
 * dmflopen -- open the line printer port on a dmf32
 */
/* ARGSUSED */
dmflopen(dev, flag)
	dev_t dev;
	int flag;
{
	register int dmf;
	register struct dmfl_softc *sc;
	register struct uba_device *ui;
	register struct dmfdevice *addr;

	dmf = DMFL_UNIT(dev);
	if (dmf >= NDMF || (ui = dmfinfo[dmf]) == 0 || ui->ui_alive == 0)
		return (ENXIO);
	sc = &dmfl_softc[dmf];
	if (sc->dmfl_state & OPEN)
		return (EBUSY);
	addr = (struct dmfdevice *)ui->ui_addr;
	if (addr->dmfl_ctrl & DMFL_OFFLINE) {
#ifdef notdef
		log(LOG_WARNING, "dmf%d: line printer offline/jammed\n",
			dmf);
#endif
		return (EIO);
	}
	if ((addr->dmfl_ctrl & DMFL_CONV)) {
		log(LOG_WARNING, "dmf%d: line printer disconnected\n", dmf);
		return (EIO);
	}

	addr->dmfl_ctrl = 0;
	sc->dmfl_state |= OPEN;
	return (0);
}

/* ARGSUSED */
dmflclose(dev, flag)
	dev_t dev;
	int flag;
{
	register int dmf = DMFL_UNIT(dev);
	register struct dmfl_softc *sc = &dmfl_softc[dmf];
	register struct uba_device *ui = dmfinfo[dmf];

	sc->dmfl_state = 0;
	if (sc->dmfl_info != 0)
		ubarelse((int)ui->ui_ubanum, &sc->dmfl_info);

	((struct dmfdevice *)ui->ui_addr)->dmfl_ctrl = 0;
}

dmflwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register int n;
	register int error;
	register struct dmfl_softc *sc;

	sc = &dmfl_softc[DMFL_UNIT(dev)];
	if (sc->dmfl_state & ERROR)
		return (EIO);
	while (n = (unsigned)uio->uio_resid) {
		if (n > DMFL_BUFSIZ) {
			n = DMFL_BUFSIZ;
			sc->dmfl_state |= MOREIO;
		} else
			sc->dmfl_state &= ~MOREIO;
		if (error = uiomove(sc->dmfl_buf, (int)n, UIO_WRITE, uio))
			return (error);
		if (error = dmflout(dev, sc->dmfl_buf, n))
			return (error);
	}
	return (0);
}


/*
 * dmflout -- start io operation to dmf line printer
 *		cp is addr of buf of n chars to be sent.
 *
 *	-- dmf will be put in formatted output mode, this will
 *		be selectable from an ioctl if the
 *		need ever arises.
 */
dmflout(dev, cp, n)
	dev_t dev;
	char *cp;
	int n;
{
	register struct dmfl_softc *sc;
	register int dmf;
	register struct uba_device *ui;
	register struct dmfdevice *d;
	int s;

	dmf = DMFL_UNIT(dev);
	sc = &dmfl_softc[dmf];
	if (sc->dmfl_state & ERROR)
		return (EIO);
	ui = dmfinfo[dmf];
	/*
	 * allocate unibus resources, will be released when io
	 * operation is done.
	 */
	if (sc->dmfl_info == 0)
		sc->dmfl_info = uballoc(ui->ui_ubanum, cp, n, 0);
	d = (struct dmfdevice *)ui->ui_addr;
	d->dmfl_ctrl = sc->dmfl_format;		/* indir reg 2 */
	/* indir reg auto increments on r/w */
	/* SO DON'T CHANGE THE ORDER OF THIS CODE */
	d->dmfl_indrct = 0;			/* prefix chars & num */
	d->dmfl_indrct = 0;			/* suffix chars & num */
	d->dmfl_indrct = sc->dmfl_info; 	/* dma lo 16 bits addr */
	d->dmfl_indrct = -n;			/* number of chars */

	d->dmfl_indrct = ((sc->dmfl_info>>16)&3) | DMFL_OPTIONS;
						/* dma hi 2 bits addr */
	d->dmfl_indrct = sc->dmfl_lines 	/* lines per page */
		| (sc->dmfl_cols<<8);		/* carriage width */
	sc->dmfl_state |= ASLP;
	s = spltty();
	d->dmfl_ctrl |= DMFL_PEN | DMFL_IE;
	while (sc->dmfl_state & ASLP) {
		sleep(sc->dmfl_buf, PZERO + 8);
		while (sc->dmfl_state & ERROR) {
			timeout(dmflint, (caddr_t)dmf, 10 * hz);
			sleep((caddr_t)&sc->dmfl_state, PZERO + 8);
		}
	}
	splx(s);
	return (0);
}

/*
 * dmflint -- handle an interrupt from the line printer part of the dmf32
 */
dmflint(dmf)
	int dmf;
{
	register struct uba_device *ui;
	register struct dmfl_softc *sc;
	register struct dmfdevice *d;
	short dmfl_stats;

	ui = dmfinfo[dmf];
	sc = &dmfl_softc[dmf];
	d = (struct dmfdevice *)ui->ui_addr;

	d->dmfl_ctrl &= ~DMFL_IE;
	dmfl_stats = d->dmfl_ctrl;
	if (sc->dmfl_state & ERROR) {
		if ((dmfl_stats & DMFL_OFFLINE) == 0)
			sc->dmfl_state &= ~ERROR;
		wakeup((caddr_t)&sc->dmfl_state);
		return;
	}
	if (dmfl_stats & DMFL_DMAERR)
		log(LOG_WARNING, "dmf%d: NXM\n", dmf);
	if (dmfl_stats & DMFL_OFFLINE) {
		log(LOG_WARNING, "dmf%d: printer error\n", dmf);
		sc->dmfl_state |= ERROR;
	}
#ifdef notdef
	if (dmfl_stats & DMFL_PDONE) {
		printf("bytes= %d\n", d->dmfl_indrct);
		printf("lines= %d\n", d->dmfl_indrct);
	}
#endif
	sc->dmfl_state &= ~ASLP;
	wakeup((caddr_t)sc->dmfl_buf);
	if (sc->dmfl_info && (sc->dmfl_state & MOREIO) == 0)
		ubarelse(ui->ui_ubanum, &sc->dmfl_info);
}

/* 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");
}
#endif NDMF