4.4BSD/usr/src/sys/tahoe/vba/vx.c

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

/*
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Computer Consoles Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)vx.c	7.13 (Berkeley) 5/16/91
 */

#include "vx.h"
#if NVX > 0
/*
 * VIOC-X driver
 */
#ifdef VXPERF
#define	DOSCOPE
#endif

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

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

#include "../vba/vbavar.h"
#include "../vba/vbaparam.h"
#include "../vba/vxreg.h"
#include "../vba/scope.h"

#ifdef VX_DEBUG
long	vxintr4 = 0;
#define	VXERR4	1
#define	VXNOBUF	2
long	vxdebug = 0;
#define	VXVCM	1
#define	VXVCC	2
#define	VXVCX	4
#endif

/*
 * Interrupt type bits passed to vinthandl().
 */
#define	CMDquals 0		/* command completed interrupt */
#define	RSPquals 1		/* command response interrupt */
#define	UNSquals 2		/* unsolicited interrupt */

#define	VXUNIT(n)	((n) >> 4)
#define	VXPORT(n)	((n) & 0xf)

struct	tty vx_tty[NVX*16];
#ifndef lint
int	nvx = NVX*16;
#endif
int	vxstart(), ttrstrt();
struct	vxcmd *vobtain(), *nextcmd();

/*
 * Driver information for auto-configuration stuff.
 */
int	vxprobe(), vxattach(), vxrint();
struct	vba_device *vxinfo[NVX];
long	vxstd[] = { 0 };
struct	vba_driver vxdriver =
    { vxprobe, 0, vxattach, 0, vxstd, "vx", vxinfo };

struct	vx_softc {
	struct	vxdevice *vs_addr;	/* H/W address */
	u_char	vs_type;	/* 0: viox-x/vioc-b, 1: vioc-bop */
	u_char	vs_bop;		/* bop board # for vioc-bop's */
	u_char	vs_loport;	/* low port nbr */
	u_char	vs_hiport;	/* high port nbr */
	u_short	vs_nbr;		/* viocx number */
	u_short	vs_maxcmd;	/* max number of concurrent cmds */
	u_short	vs_silosiz;	/* silo size */
	short	vs_vers;	/* vioc/pvioc version */
#define	VXV_OLD	0		/* PVIOCX | VIOCX */
#define	VXV_NEW	1		/* NPVIOCX | NVIOCX */
	short 	vs_state;	/* controller state */
#define	VXS_READY	0	/* ready for commands */
#define	VXS_RESET	1	/* in process of reseting */
	u_short	vs_softCAR;	/* soft carrier */
	u_int	vs_ivec;	/* interrupt vector base */
	caddr_t vs_mricmd;	/* most recent issued cmd */
	/* The remaining fields are zeroed on reset... */
#define vs_zero vs_xmtcnt
	int	vs_xmtcnt;	/* xmit commands pending */
	struct	vxcmd *vs_avail;/* next available command buffer */
	struct	vxcmd *vs_build;
	struct	vxcmd vs_lst[NVCXBUFS];
	struct	vcmds vs_cmds;
} vx_softc[NVX];

struct speedtab vxspeedtab[] = {
	EXTA,	V19200,
	EXTB,	V19200,
	19200,	V19200,
	9600,	13,
	4800,	12,
	2400,	11,
	1800,	10,
	1200,	9,
	600,	8,
	300,	7,
	200,	6,
	150,	5,
	134,	4,
	110,	3,
	75,	2,
	50,	1,
	0,	0,
	-1,	-1,
};

vxprobe(reg, vi)
	caddr_t reg;
	struct vba_device *vi;
{
	register int br, cvec;			/* must be r12, r11 */
	register struct vxdevice *vp;
	register struct vx_softc *vs;
	struct pte *dummypte;

#ifdef lint
	br = 0; cvec = br; br = cvec;
	vackint(0); vunsol(0); vcmdrsp(0);
#ifdef VX_DEBUG
	vxfreset(0);
#endif
#endif /* lint */
	/*
	 * If on an HCX-9, the device has a 32-bit address,
	 * and we receive that address so we can set up a map.
	 * On VERSAbus devices, the address is 24-bit, and is
	 * already mapped (into vmem[]) by autoconf.
	 */
	if (!(reg >= vmem && reg < &vmem[ctob(VBIOSIZE)]) &&	/* XXX */
	    !vbmemalloc(16, reg, &dummypte, &reg)) {
		printf("vx%d: vbmemalloc failed.\n", vi->ui_unit);
		return(0);
	}
	vp = (struct vxdevice *)reg;
	if (badaddr((caddr_t)vp, 1))
		return (0);
	vp->v_fault = 0;
	vp->v_vioc = V_BSY;
	vp->v_hdwre = V_RESET;		/* reset interrupt */
	DELAY(4000000);
	if (vp->v_fault != VXF_READY)
		return (0);
	vs = &vx_softc[vi->ui_unit];
#ifdef notdef
	/*
	 * Align vioc interrupt vector base to 4 vector
	 * boundary and fitting in 8 bits (is this necessary,
	 * wish we had documentation).
	 */
	if ((vi->ui_hd->vh_lastiv -= 3) > 0xff)
		vi->ui_hd->vh_lastiv = 0xff;
	vs->vs_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x3;
#else
	vs->vs_ivec = 0x40+vi->ui_unit*4;
#endif
	br = 0x18, cvec = vs->vs_ivec;	/* XXX */
	return (sizeof (struct vxdevice));
}

vxattach(vi)
	register struct vba_device *vi;
{
	register struct vx_softc *vs = &vx_softc[vi->ui_unit];

	vs->vs_softCAR = vi->ui_flags;
	vs->vs_addr = (struct vxdevice *)vi->ui_addr;
	vxinit(vi->ui_unit, 1);
}

/*
 * Open a VX line.
 */
/*ARGSUSED*/
vxopen(dev, flag)
	dev_t dev;
	int flag;
{
	register struct tty *tp;	/* pointer to tty struct for port */
	register struct vx_softc *vs;
	register struct vba_device *vi;
	int unit, vx, s, error = 0;
	int vxparam();

	unit = minor(dev);
	vx = VXUNIT(unit);
	if (vx >= NVX || (vi = vxinfo[vx])== 0 || vi->ui_alive == 0)
		return (ENXIO);
	vs = &vx_softc[vx];
	tp = &vx_tty[unit];
	unit = VXPORT(unit);
	if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
		return (EBUSY);
	if (unit < vs->vs_loport || unit > vs->vs_hiport)
		return (ENXIO);
	tp->t_addr = (caddr_t)vs;
	tp->t_oproc = vxstart;
	tp->t_param = vxparam;
	tp->t_dev = dev;
	s = spl8();
	if ((tp->t_state&TS_ISOPEN) == 0) {
		tp->t_state |= TS_WOPEN;
		ttychars(tp);
		if (tp->t_ispeed == 0) {
			tp->t_iflag = TTYDEF_IFLAG;
			tp->t_oflag = TTYDEF_OFLAG;
			tp->t_lflag = TTYDEF_LFLAG;
			tp->t_cflag = TTYDEF_CFLAG;
			tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
		}
		vxparam(tp, &tp->t_termios);
		ttsetwater(tp);
	}
	vcmodem(dev, VMOD_ON);
	while (!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) && 
	      (tp->t_state&TS_CARR_ON) == 0) {
		tp->t_state |= TS_WOPEN;
		if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
		    ttopen, 0))
			break;
	}
	if (error == 0)
		error = (*linesw[tp->t_line].l_open)(dev,tp);
	splx(s);
	return (error);
}

/*
 * Close a VX line.
 */
/*ARGSUSED*/
vxclose(dev, flag, mode, p)
	dev_t dev;
	int flag, mode;
	struct proc *p;
{
	register struct tty *tp;
	int unit, s, error = 0;

	unit = minor(dev);
	tp = &vx_tty[unit];
	s = spl8();
	(*linesw[tp->t_line].l_close)(tp, flag);
	if (tp->t_cflag & HUPCL || (tp->t_state & TS_ISOPEN) == 0)
		vcmodem(dev, VMOD_OFF);
	/* wait for the last response */
	while (tp->t_state&TS_FLUSH && error == 0)
		error = tsleep((caddr_t)&tp->t_state, TTOPRI | PCATCH,
		    ttclos, 0);
	splx(s);
	if (error)
		return (error);
	return (ttyclose(tp));
}

/*
 * Read from a VX line.
 */
vxread(dev, uio, flag)
	dev_t dev;
	struct uio *uio;
{
	struct tty *tp = &vx_tty[minor(dev)];

	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}

/*
 * write on a VX line
 */
vxwrite(dev, uio, flag)
	dev_t dev;
	struct uio *uio;
{
	register struct tty *tp = &vx_tty[minor(dev)];

	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}

/*
 * VIOCX unsolicited interrupt.
 */
vxrint(vx)
	register vx;
{
	register struct tty *tp, *tp0;
	register struct vxdevice *addr;
	register struct vx_softc *vs;
	struct vba_device *vi;
	register int nc, c;
	register struct silo {
		u_char	data, port;
	} *sp;
	short *osp;
	int overrun = 0;

	vi = vxinfo[vx];
	if (vi == 0 || vi->ui_alive == 0)
		return;
	addr = (struct vxdevice *)vi->ui_addr;
	switch (addr->v_uqual&037) {
	case 0:
		break;
	case 2:
		if (addr->v_ustat == VP_SILO_OFLOW)
			log(LOG_ERR, "vx%d: input silo overflow\n", vx);
		else {
			printf("vx%d: vc proc err, ustat %x\n",
			    vx, addr->v_ustat);
			vxstreset(vx);
		}
		return;
	case 3:
		vcmintr(vx);
		return;
	case 4:
		return;
	default:
		printf("vx%d: vc uqual err, uqual %x\n", vx, addr->v_uqual);
		vxstreset(vx);
		return;
	}
	vs = &vx_softc[vx];
	if (vs->vs_vers == VXV_NEW)
		sp = (struct silo *)((caddr_t)addr + *(short *)addr->v_usdata);
	else
		sp = (struct silo *)((caddr_t)addr+VX_SILO+(addr->v_usdata[0]<<6));
	nc = *(osp = (short *)sp);
	if (nc == 0)
		return;
	if (vs->vs_vers == VXV_NEW && nc > vs->vs_silosiz) {
		printf("vx%d: %d exceeds silo size\n", nc);
		nc = vs->vs_silosiz;
	}
	tp0 = &vx_tty[vx*16];
	sp = (struct silo *)(((short *)sp)+1);
	for (; nc > 0; nc--, sp = (struct silo *)(((short *)sp)+1)) {
		c = sp->port & 017;
		if (vs->vs_loport > c || c > vs->vs_hiport)
			continue;
		tp = tp0 + c;
		if( (tp->t_state&TS_ISOPEN) == 0) {
			wakeup((caddr_t)&tp->t_rawq);
			continue;
		}
		c = sp->data&((tp->t_cflag&CSIZE)==CS8 ? 0xff : 0x7f);
		if ((sp->port&VX_RO) == VX_RO && !overrun) {
			log(LOG_ERR, "vx%d: receiver overrun\n", vi->ui_unit);
			overrun = 1;
			continue;
		}
		if (sp->port&VX_PE)
			c |= TTY_PE;
		if (sp->port&VX_FE) 
			c |= TTY_FE;
		(*linesw[tp->t_line].l_rint)(c, tp);
	}
	*osp = 0;
}

/*
 * Ioctl for VX.
 */
vxioctl(dev, cmd, data, flag)
	dev_t dev;
	caddr_t	data;
{
	register struct tty *tp;
	int error;

	tp = &vx_tty[minor(dev)];
	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);
	return (ENOTTY);
}

vxparam(tp, t)
	struct tty *tp;
	struct termios *t;
{

	return (vxcparam(tp, t, 1));
}

/*
 * Set parameters from open or stty into the VX hardware
 * registers.
 */
vxcparam(tp, t, wait)
	struct tty *tp;
	struct termios *t;
	int wait;
{
	register struct vx_softc *vs;
	register struct vxcmd *cp;
	int s, error = 0;
	int speedcode = ttspeedtab(t->c_ospeed, vxspeedtab);

	if (speedcode < 0 || (t->c_ispeed != t->c_ospeed && t->c_ispeed))
		return (EINVAL);
	vs = (struct vx_softc *)tp->t_addr;
	cp = vobtain(vs);
	s = spl8();
	/*
	 * Construct ``load parameters'' command block
	 * to setup baud rates, xon-xoff chars, parity,
	 * and stop bits for the specified port.
	 */
	cp->cmd = VXC_LPARAX;
	cp->par[1] = VXPORT(minor(tp->t_dev));
	/*
	 * note: if the hardware does flow control, ^V doesn't work
	 * to escape ^S
	 */
	if (t->c_iflag&IXON) {
		if (t->c_cc[VSTART] == _POSIX_VDISABLE)
			cp->par[2] = 0;
		else
			cp->par[2] = t->c_cc[VSTART];
		if (t->c_cc[VSTOP] == _POSIX_VDISABLE)
			cp->par[3] = 0;
		else
			cp->par[3] = t->c_cc[VSTOP];
	} else 
		cp->par[2] = cp->par[3] = 0;
#ifdef notnow
	switch (t->c_cflag & CSIZE) {	/* XXX */
	case CS8:
#endif
		cp->par[4] = BITS8;		/* 8 bits of data */
#ifdef notnow
		break;
	case CS7:
		cp->par[4] = BITS7;		/* 7 bits of data */
		break;
	case CS6:
		cp->par[4] = BITS6;		/* 6 bits of data */
		break;
	case CS5:
		cp->par[4] = BITS5;		/* 5 bits of data */
		break;
	}
	if ((t->c_cflag & PARENB) == 0)		/* XXX */
#endif
		cp->par[7] = VNOPARITY;		/* no parity */
#ifdef notnow
	else if (t->c_cflag&PARODD)
		cp->par[7] = VODDP;	/* odd parity */
	else
		cp->par[7] = VEVENP;	/* even parity */
#endif
	cp->par[5] = (t->c_cflag&CSTOPB) ? VSTOP2 : VSTOP1;
	cp->par[6] = speedcode;
	if (vcmd((int)vs->vs_nbr, (caddr_t)&cp->cmd) && wait)
		error = tsleep((caddr_t)cp, TTIPRI | PCATCH, ttyout, 0);
	if ((t->c_ospeed)==0) {
		tp->t_cflag |= HUPCL;
		vcmodem(tp->t_dev, VMOD_OFF);
	}
	splx(s);
	return (error);
}

/*
 * VIOCX command response interrupt.
 * For transmission, restart output to any active port.
 * For all other commands, just clean up.
 */
vxxint(vx, cp)
	register int vx;
	register struct vxcmd *cp;
{
	register struct vxmit *vp;
	register struct tty *tp, *tp0;
	register struct vx_softc *vs;

	vs = &vx_softc[vx];
	cp = (struct vxcmd *)((long *)cp-1);

	switch (cp->cmd&0xff00) {

	case VXC_LIDENT:	/* initialization complete */
		if (vs->vs_state == VXS_RESET) {
			vxfnreset(vx, cp);
			vinthandl(vx, ((V_BSY|RSPquals) << 8)|V_INTR);
		}
		cp->cmd++;
		return;

	case VXC_XMITDTA:
	case VXC_XMITIMM:
		break;

	case VXC_LPARAX:
		wakeup((caddr_t)cp);
		/* fall thru... */
	default:	/* VXC_MDMCTL or VXC_FDTATOX */
		vrelease(vs, cp);
		if (vs->vs_state == VXS_RESET)
			vinthandl(vx, ((V_BSY|RSPquals) << 8)|V_INTR);
		return;
	}
	tp0 = &vx_tty[vx*16];
	vp = (struct vxmit *)(cp->par + (cp->cmd & 07)*sizeof (struct vxmit));
	for (; vp >= (struct vxmit *)cp->par; vp--) {
		tp = tp0 + (vp->line & 017);
		tp->t_state &= ~TS_BUSY;
		if (tp->t_state & TS_FLUSH) {
			tp->t_state &= ~TS_FLUSH;
			wakeup((caddr_t)&tp->t_state);
		} else
		 	ndflush(&tp->t_outq, vp->bcount+1);
	}
	vrelease(vs, cp);
	if (vs->vs_vers == VXV_NEW)
		(*linesw[tp->t_line].l_start)(tp);
	else {
		tp0 = &vx_tty[vx*16 + vs->vs_hiport];
		for(tp = &vx_tty[vx*16 + vs->vs_loport]; tp <= tp0; tp++)
			(*linesw[tp->t_line].l_start)(tp);
		if ((cp = nextcmd(vs)) != NULL) {	/* command to send? */
			vs->vs_xmtcnt++;
			(void) vcmd(vx, (caddr_t)&cp->cmd);
		}
	}
	vs->vs_xmtcnt--;
}

/*
 * Force out partial XMIT command after timeout
 */
vxforce(vs)
	register struct vx_softc *vs;
{
	register struct vxcmd *cp;
	int s;

	s = spl8();
	if ((cp = nextcmd(vs)) != NULL) {
		vs->vs_xmtcnt++;
		(void) vcmd((int)vs->vs_nbr, (caddr_t)&cp->cmd);
	}
	splx(s);
}

/*
 * Start (restart) transmission on the given VX line.
 */
vxstart(tp)
	register struct tty *tp;
{
	register short n;
	register struct vx_softc *vs;
	int s, port;

	s = spl8();
	port = VXPORT(minor(tp->t_dev));
	vs = (struct vx_softc *)tp->t_addr;
	if ((tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) == 0) {
		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;
			}
		}
		if (tp->t_outq.c_cc == 0) {
			splx(s);
			return;
		}
		scope_out(3);
		if (1 || !(tp->t_oflag&OPOST))	/* XXX */
			n = ndqb(&tp->t_outq, 0);
		else {
			n = ndqb(&tp->t_outq, 0200);
			if (n == 0) {
				n = getc(&tp->t_outq);
				timeout(ttrstrt, (caddr_t)tp, (n&0177)+6);
				tp->t_state |= TS_TIMEOUT;
				n = 0;
			}
		}
		if (n) {
			tp->t_state |= TS_BUSY;
			vsetq(vs, port, (char *)tp->t_outq.c_cf, n);
		}
	}
	splx(s);
}

/*
 * Stop output on a line.
 */
vxstop(tp)
	register struct tty *tp;
{
	int s;

	s = spl8();
	if (tp->t_state&TS_BUSY)
		if ((tp->t_state&TS_TTSTOP) == 0)
			tp->t_state |= TS_FLUSH;
	splx(s);
}

static	int vxbbno = -1;
/*
 * VIOCX Initialization.  Makes free lists of command buffers.
 * Resets all viocx's.  Issues a LIDENT command to each
 * viocx to establish interrupt vectors and logical port numbers.
 */
vxinit(vx, wait)
	register int vx;
	int wait;
{
	register struct vx_softc *vs;
	register struct vxdevice *addr;
	register struct vxcmd *cp;
	register char *resp;
	register int j;
	char type, *typestring;

	vs = &vx_softc[vx];
	addr = vs->vs_addr;
	type = addr->v_ident;
	vs->vs_vers = (type&VXT_NEW) ? VXV_NEW : VXV_OLD;
	if (vs->vs_vers == VXV_NEW)
		vs->vs_silosiz = addr->v_maxsilo;
	switch (type) {

	case VXT_VIOCX:
	case VXT_VIOCX|VXT_NEW:
		typestring = "VIOC-X";
		/* set soft carrier for printer ports */
		for (j = 0; j < 16; j++)
			if (vs->vs_softCAR & (1 << j) ||
			    addr->v_portyp[j] == VXT_PARALLEL) {
				vs->vs_softCAR |= 1 << j;
				addr->v_dcd |= 1 << j;
			}
		break;

	case VXT_PVIOCX:
	case VXT_PVIOCX|VXT_NEW:
		typestring = "VIOC-X (old connector panel)";
		break;
	case VXT_VIOCBOP:		/* VIOC-BOP */
		vs->vs_type = 1;
		vs->vs_bop = ++vxbbno;
		printf("VIOC-BOP no. %d at %x\n", vs->vs_bop, addr);
		goto unsup;
	default:
		printf("vx%d: unknown type %x\n", vx, type);
	unsup:
		vxinfo[vx]->ui_alive = 0;
		return;
	}
	vs->vs_nbr = vx;		/* assign board number */
	vs->vs_maxcmd = (vs->vs_vers == VXV_NEW) ? 24 : 4;
	/*
	 * Initialize all cmd buffers by linking them
	 * into a free list.
	 */
	for (j = 0; j < NVCXBUFS; j++) {
		cp = &vs->vs_lst[j];
		cp->c_fwd = &vs->vs_lst[j+1];
	}
	vs->vs_avail = &vs->vs_lst[0];	/* set idx to 1st free buf */
	cp->c_fwd = (struct vxcmd *)0;	/* mark last buf in free list */

	/*
	 * Establish the interrupt vectors and define the port numbers.
	 */
	cp = vobtain(vs);
	cp->cmd = VXC_LIDENT;
	cp->par[0] = vs->vs_ivec; 	/* ack vector */
	cp->par[1] = cp->par[0]+1;	/* cmd resp vector */
	cp->par[3] = cp->par[0]+2;	/* unsol intr vector */
	cp->par[4] = 15;		/* max ports, no longer used */
	cp->par[5] = 0;			/* set 1st port number */
	(void) vcmd(vx, (caddr_t)&cp->cmd);
	if (!wait)
		return;

	for (j = 0; cp->cmd == VXC_LIDENT && j < 4000000; j++)
		;
	if (j >= 4000000)
		printf("vx%d: didn't respond to LIDENT\n", vx); 

 	/* calculate address of response buffer */
 	resp = (char *)addr + (addr->v_rspoff&0x3fff);
	if (resp[0] != 0 && (resp[0]&0177) != 3) {
		vrelease(vs, cp);	/* init failed */
		return;
	}
	vs->vs_loport = cp->par[5];
	vs->vs_hiport = cp->par[7];
	printf("vx%d: %s%s, ports %d-%d\n", vx,
	    (vs->vs_vers == VXV_NEW) ? "" : "old ", typestring,
	    vs->vs_loport, vs->vs_hiport);
	vrelease(vs, cp);
}

/*
 * Obtain a command buffer
 */
struct vxcmd *
vobtain(vs)
	register struct vx_softc *vs;
{
	register struct vxcmd *p;
	int s;

	s = spl8();
	p = vs->vs_avail;
	if (p == (struct vxcmd *)0) {
#ifdef VX_DEBUG
		if (vxintr4&VXNOBUF)
			vxintr4 &= ~VXNOBUF;
#endif
		printf("vx%d: no buffers\n", vs->vs_nbr);
		vxstreset(vs->vs_nbr);
		splx(s);
		return (vobtain(vs));
	}
	vs->vs_avail = p->c_fwd;
	splx(s);
	return ((struct vxcmd *)p);
}

/*
 * Release a command buffer
 */
vrelease(vs, cp)
	register struct vx_softc *vs;
	register struct vxcmd *cp;
{
	int s;

#ifdef VX_DEBUG
	if (vxintr4&VXNOBUF)
		return;
#endif
	s = spl8();
	cp->c_fwd = vs->vs_avail;
	vs->vs_avail = cp;
	splx(s);
}

struct vxcmd *
nextcmd(vs)
	register struct vx_softc *vs;
{
	register struct vxcmd *cp;
	int s;

	s = spl8();
	cp = vs->vs_build;
	vs->vs_build = (struct vxcmd *)0;
	splx(s);
	return (cp);
}

/*
 * Assemble transmits into a multiple command;
 * up to 8 transmits to 8 lines can be assembled together
 * (on PVIOCX only).
 */
vsetq(vs, line, addr, n)
	register struct vx_softc *vs;
	caddr_t	addr;
{
	register struct vxcmd *cp;
	register struct vxmit *mp;

	/*
	 * Grab a new command buffer or append
	 * to the current one being built.
	 */
	cp = vs->vs_build;
	if (cp == (struct vxcmd *)0) {
		cp = vobtain(vs);
		vs->vs_build = cp;
		cp->cmd = VXC_XMITDTA;
	} else {
		if ((cp->cmd & 07) == 07 || vs->vs_vers == VXV_NEW) {
			printf("vx%d: setq overflow\n", vs-vx_softc);
			vxstreset((int)vs->vs_nbr);
			return;
		}
		cp->cmd++;
	}
	/*
	 * Select the next vxmit buffer and copy the
	 * characters into the buffer (if there's room
	 * and the device supports ``immediate mode'',
	 * or store an indirect pointer to the data.
	 */
	mp = (struct vxmit *)(cp->par + (cp->cmd & 07)*sizeof (struct vxmit));
	mp->bcount = n-1;
	mp->line = line;
	if (vs->vs_vers == VXV_NEW && n <= sizeof (mp->ostream)) {
		cp->cmd = VXC_XMITIMM;
		bcopy(addr, mp->ostream, (unsigned)n);
	} else {
		/* get system address of clist block */
		addr = (caddr_t)vtoph((struct proc *)0, (unsigned)addr);
		bcopy((caddr_t)&addr, mp->ostream, sizeof (addr));
	}
	/*
	 * We send the data immediately if a VIOCX,
	 * the command buffer is full, or if we've nothing
	 * currently outstanding.  If we don't send it,
	 * set a timeout to force the data to be sent soon.
	 */
	if (vs->vs_vers == VXV_NEW || (cp->cmd & 07) == 7 ||
	    vs->vs_xmtcnt == 0) {
		vs->vs_xmtcnt++;
		(void) vcmd((int)vs->vs_nbr, (char *)&cp->cmd);
		vs->vs_build = 0;
	} else
		timeout(vxforce, (caddr_t)vs, 3);
}

/*
 * Write a command out to the VIOC
 */
vcmd(vx, cmdad)
	register int vx;
	register caddr_t cmdad;
{
	register struct vcmds *cp;
	register struct vx_softc *vs = &vx_softc[vx];
	int s;

	s = spl8();
	/*
	 * When the vioc is resetting, don't process
	 * anything other than VXC_LIDENT commands.
	 */
	if (vs->vs_state == VXS_RESET && cmdad != NULL) {
		struct vxcmd *vcp = (struct vxcmd *)(cmdad-sizeof (vcp->c_fwd));

		if (vcp->cmd != VXC_LIDENT) {
			vrelease(vs, vcp);
			return (0);
		}
	}
	cp = &vs->vs_cmds;
	if (cmdad != (caddr_t)0) {
		cp->cmdbuf[cp->v_fill] = cmdad;
		if (++cp->v_fill >= VC_CMDBUFL)
			cp->v_fill = 0;
		if (cp->v_fill == cp->v_empty) {
			printf("vx%d: cmd q overflow\n", vx);
			vxstreset(vx);
			splx(s);
			return (0);
		}
		cp->v_cmdsem++;
	}
	if (cp->v_cmdsem && cp->v_curcnt < vs->vs_maxcmd) {
		cp->v_cmdsem--;
		cp->v_curcnt++;
		vinthandl(vx, ((V_BSY|CMDquals) << 8)|V_INTR);
	}
	splx(s);
	return (1);
}

/*
 * VIOC acknowledge interrupt.  The VIOC has received the new
 * command.  If no errors, the new command becomes one of 16 (max)
 * current commands being executed.
 */
vackint(vx)
	register vx;
{
	register struct vxdevice *vp;
	register struct vcmds *cp;
	struct vx_softc *vs;
	int s;

	scope_out(5);
	vs = &vx_softc[vx];
	if (vs->vs_type)	/* Its a BOP */
		return;
	s = spl8();
	vp = vs->vs_addr;
	cp = &vs->vs_cmds;
	if (vp->v_vcid&V_ERR) {
		register char *resp;
		register i;

		printf("vx%d: ackint error type %x v_dcd %x\n", vx,
		    vp->v_vcid & 07, vp->v_dcd & 0xff);
		resp = (char *)vs->vs_mricmd;
		for (i = 0; i < 16; i++)
			printf("%x ", resp[i]&0xff);
		printf("\n");
		splx(s);
		vxstreset(vx);
		return;
	}
	if ((vp->v_hdwre&017) == CMDquals) {
#ifdef VX_DEBUG
		if (vxintr4 & VXERR4) {	/* causes VIOC INTR ERR 4 */
			struct vxcmd *cp1, *cp0;

			cp0 = (struct vxcmd *)
			    ((caddr_t)cp->cmdbuf[cp->v_empty]-sizeof (cp0->c_fwd));
			if (cp0->cmd == VXC_XMITDTA || cp0->cmd == VXC_XMITIMM) {
				cp1 = vobtain(vs);
				*cp1 = *cp0;
				vxintr4 &= ~VXERR4;
				(void) vcmd(vx, &cp1->cmd);
			}
		}
#endif
		cp->v_curcmd[vp->v_vcid & VCMDLEN-1] = cp->cmdbuf[cp->v_empty];
		if (++cp->v_empty >= VC_CMDBUFL)
			cp->v_empty = 0;
	}
	if (++cp->v_itrempt >= VC_IQLEN)
		cp->v_itrempt = 0;
	vintempt(vx);
	splx(s);
	(void) vcmd(vx, (caddr_t)0);	/* queue next cmd, if any */
}

/*
 * Command Response interrupt.  The Vioc has completed
 * a command.  The command may now be returned to
 * the appropriate device driver.
 */
vcmdrsp(vx)
	register vx;
{
	register struct vxdevice *vp;
	register struct vcmds *cp;
	register caddr_t cmd;
	register struct vx_softc *vs;
	register char *resp;
	register k;
	register int s;

	scope_out(6);
	vs = &vx_softc[vx];
	if (vs->vs_type) {	/* Its a BOP */
		printf("vx%d: vcmdrsp interrupt\n", vx);
		return;
	}
	s = spl8();
	vp = vs->vs_addr;
	cp = &vs->vs_cmds;
	resp = (char *)vp + (vp->v_rspoff&0x7fff);
	if (((k = resp[1])&V_UNBSY) == 0) {
		printf("vx%d: cmdresp debug\n", vx);
		splx(s);
		vxstreset(vx);
		return;
	}
	k &= VCMDLEN-1;
	cmd = cp->v_curcmd[k];
	cp->v_curcmd[k] = (caddr_t)0;
	cp->v_curcnt--;
	k = *((short *)&resp[4]);	/* cmd operation code */
	if ((k&0xff00) == VXC_LIDENT)	/* want hiport number */
		for (k = 0; k < VRESPLEN; k++)
			cmd[k] = resp[k+4];
	resp[1] = 0;
	vxxint(vx, (struct vxcmd *)cmd);
	if (vs->vs_state == VXS_READY)
		vinthandl(vx, ((V_BSY|RSPquals) << 8)|V_INTR);
	splx(s);
}

/*
 * Unsolicited interrupt.
 */
vunsol(vx)
	register vx;
{
	register struct vxdevice *vp;
	struct vx_softc *vs;
	int s;

	scope_out(1);
	vs = &vx_softc[vx];
	if (vs->vs_type) {	/* Its a BOP */
		printf("vx%d: vunsol from BOP\n", vx);
		return;
	}
	s = spl8();
	vp = vs->vs_addr;
	if (vp->v_uqual&V_UNBSY) {
		vxrint(vx);
		vinthandl(vx, ((V_BSY|UNSquals) << 8)|V_INTR);
#ifdef notdef
	} else {
		printf("vx%d: unsolicited interrupt error\n", vx);
		splx(s);
		vxstreset(vx);
#endif
	}
	splx(s);
}

/*
 * Enqueue an interrupt.
 */
vinthandl(vx, item)
	register int vx;
	register item;
{
	register struct vcmds *cp;
	int empty;

	cp = &vx_softc[vx].vs_cmds;
	empty = (cp->v_itrfill == cp->v_itrempt);
	cp->v_itrqueu[cp->v_itrfill] = item;
	if (++cp->v_itrfill >= VC_IQLEN)
		cp->v_itrfill = 0;
	if (cp->v_itrfill == cp->v_itrempt) {
		printf("vx%d: interrupt q overflow\n", vx);
		vxstreset(vx);
	} else if (empty)
		vintempt(vx);
}

vintempt(vx)
	int vx;
{
	register struct vcmds *cp;
	register struct vxdevice *vp;
	register struct vx_softc *vs;
	register short item;
	register short *intr;

	vs = &vx_softc[vx];
	vp = vs->vs_addr;
	if (vp->v_vioc&V_BSY)
		return;
	cp = &vs->vs_cmds;
	if (cp->v_itrempt == cp->v_itrfill)
		return;
	item = cp->v_itrqueu[cp->v_itrempt];
	intr = (short *)&vp->v_vioc;
	switch ((item >> 8)&03) {

	case CMDquals: {		/* command */
		int phys;

		if (cp->v_empty == cp->v_fill || vp->v_vcbsy&V_BSY)
			break;
		vs->vs_mricmd = (caddr_t)cp->cmdbuf[cp->v_empty];
		phys = vtoph((struct proc *)0, 
		    (unsigned)cp->cmdbuf[cp->v_empty]);
		vp->v_vcp[0] = ((short *)&phys)[0];
		vp->v_vcp[1] = ((short *)&phys)[1];
		vp->v_vcbsy = V_BSY;
		*intr = item;
		scope_out(4);
		break;
	}

	case RSPquals:		/* command response */
		*intr = item;
		scope_out(7);
		break;

	case UNSquals:		/* unsolicited interrupt */
		vp->v_uqual = 0;
		*intr = item;
		scope_out(2);
		break;
	}
}

/*
 * Start a reset on a vioc after error (hopefully)
 */
vxstreset(vx)
	register int vx;
{
	register struct vx_softc *vs;
	register struct vxdevice *vp;
	register struct vxcmd *cp;
	register int j;
	extern int vxinreset();
	int s;

	vs = &vx_softc[vx];
	s = spl8();
	if (vs->vs_state == VXS_RESET) {	/* avoid recursion */
		splx(s);
		return;
	}
	vp = vs->vs_addr;
	/*
	 * Zero out the vioc structures, mark the vioc as being
	 * reset, reinitialize the free command list, reset the vioc
	 * and start a timer to check on the progress of the reset.
	 */
	bzero((caddr_t)&vs->vs_zero,
	    (unsigned)((caddr_t)(vs + 1) - (caddr_t)&vs->vs_zero));

	/*
	 * Setting VXS_RESET prevents others from issuing
	 * commands while allowing currently queued commands to
	 * be passed to the VIOC.
	 */
	vs->vs_state = VXS_RESET;
	/* init all cmd buffers */
	for (j = 0; j < NVCXBUFS; j++) {
		cp = &vs->vs_lst[j];
		cp->c_fwd = &vs->vs_lst[j+1];
	}
	vs->vs_avail = &vs->vs_lst[0];
	cp->c_fwd = (struct vxcmd *)0;
	printf("vx%d: reset...", vx);
	vp->v_fault = 0;
	vp->v_vioc = V_BSY;
	vp->v_hdwre = V_RESET;		/* generate reset interrupt */
	timeout(vxinreset, (caddr_t)vx, hz*5);
	splx(s);
}

/* continue processing a reset on a vioc after an error (hopefully) */
vxinreset(vx)
	int vx;
{
	register struct vxdevice *vp;
	int s = spl8();

	vp = vx_softc[vx].vs_addr;
	/*
	 * See if the vioc has reset.
	 */
	if (vp->v_fault != VXF_READY) {
		printf(" vxreset failed\n");
		splx(s);
		return;
	}
	/*
	 * Send a LIDENT to the vioc and mess with carrier flags
	 * on parallel printer ports.
	 */
	vxinit(vx, 0);
	splx(s);
}

/*
 * Finish the reset on the vioc after an error (hopefully).
 *
 * Restore modem control, parameters and restart output.
 * Since the vioc can handle no more then 24 commands at a time
 * and we could generate as many as 48 commands, we must do this in
 * phases, issuing no more then 16 commands at a time.
 */
vxfnreset(vx, cp)
	register int vx;
	register struct vxcmd *cp;
{
	register struct vx_softc *vs;
	register struct vxdevice *vp;
	register struct tty *tp, *tp0;
	register int i;
#ifdef notdef
	register int on;
#endif
	extern int vxrestart();
	int s = spl8();

	vs = &vx_softc[vx];
	vrelease(vs, cp);
	vs->vs_state = VXS_READY;

	vp = vs->vs_addr;
	vp->v_vcid = 0;

	/*
	 * Restore modem information and control.
	 */
	tp0 = &vx_tty[vx*16];
	for (i = vs->vs_loport; i <= vs->vs_hiport; i++) {
		tp = tp0 + i;
		if (tp->t_state&(TS_ISOPEN|TS_WOPEN)) {
			tp->t_state &= ~TS_CARR_ON;
			vcmodem(tp->t_dev, VMOD_ON);
			if (tp->t_state&TS_CARR_ON)
				(void)(*linesw[tp->t_line].l_modem)(tp, 1);
			else if (tp->t_state & TS_ISOPEN)
				(void)(*linesw[tp->t_line].l_modem)(tp, 0);
		}
#ifdef notdef
		/*
		 * If carrier has changed while we were resetting,
		 * take appropriate action.
		 */
		on = vp->v_dcd & 1<<i;
		if (on && (tp->t_state&TS_CARR_ON) == 0)
			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
		else if (!on && tp->t_state&TS_CARR_ON)
			(void)(*linesw[tp->t_line].l_modem)(tp, 0);
#endif
	}
	vs->vs_state = VXS_RESET;
	timeout(vxrestart, (caddr_t)vx, hz);
	splx(s);
}

/*
 * Restore a particular aspect of the VIOC.
 */
vxrestart(vx)
	int vx;
{
	register struct tty *tp, *tp0;
	register struct vx_softc *vs;
	register int i, count;
	int s = spl8();

	count = vx >> 8;
	vx &= 0xff;
	vs = &vx_softc[vx];
	vs->vs_state = VXS_READY;
	tp0 = &vx_tty[vx*16];
	for (i = vs->vs_loport; i <= vs->vs_hiport; i++) {
		tp = tp0 + i;
		if (count != 0) {
			tp->t_state &= ~(TS_BUSY|TS_TIMEOUT);
			if (tp->t_state&(TS_ISOPEN|TS_WOPEN))
				vxstart(tp);	/* restart pending output */
		} else {
			if (tp->t_state&(TS_WOPEN|TS_ISOPEN))
				vxcparam(tp, &tp->t_termios, 0);
		}
	}
	if (count == 0) {
		vs->vs_state = VXS_RESET;
		timeout(vxrestart, (caddr_t)(vx + 1*256), hz);
	} else
		printf(" vx reset done\n");
	splx(s);
}

vxreset(dev)
	dev_t dev;
{

	vxstreset((int)VXUNIT(minor(dev)));	/* completes asynchronously */
}

#ifdef VX_DEBUG
vxfreset(vx)
	register int vx;
{
	struct vba_device *vi;

	if ((unsigned)vx > NVX || (vi = vxinfo[vx]) == 0 || vi->ui_addr == 0)
		return (ENODEV);
	vx_softc[vx].vs_state = VXS_READY;
	vxstreset(vx);
	return (0);		/* completes asynchronously */
}
#endif

vcmodem(dev, flag)
	dev_t dev;
{
	struct tty *tp;
	register struct vxcmd *cp;
	register struct vx_softc *vs;
	register struct vxdevice *kp;
	register port;
	int unit;

	unit = minor(dev);
	tp = &vx_tty[unit];
	vs = (struct vx_softc *)tp->t_addr;
	if (vs->vs_state != VXS_READY)
		return;
	cp = vobtain(vs);
	kp = vs->vs_addr;

	port = VXPORT(unit);
	/*
	 * Issue MODEM command
	 */
	cp->cmd = VXC_MDMCTL;
	if (flag == VMOD_ON) {
		if (vs->vs_softCAR & (1 << port)) {
			cp->par[0] = V_MANUAL | V_DTR_ON | V_RTS;
			kp->v_dcd |= (1 << port);
		} else
			cp->par[0] = V_AUTO | V_DTR_ON;
	} else
		cp->par[0] = V_DTR_OFF;
	cp->par[1] = port;
	(void) vcmd((int)vs->vs_nbr, (caddr_t)&cp->cmd);
	if ((kp->v_dcd | vs->vs_softCAR) & (1 << port) && flag == VMOD_ON)
		tp->t_state |= TS_CARR_ON;
}

/*
 * VCMINTR called when an unsolicited interrupt occurs signaling
 * some change of modem control state.
 */
vcmintr(vx)
	register vx;
{
	register struct vxdevice *kp;
	register struct tty *tp;
	register port;
	register struct vx_softc *vs;

	vs = &vx_softc[vx];
	kp = vs->vs_addr;
	port = kp->v_usdata[0] & 017;
	tp = &vx_tty[vx*16+port];

	if (kp->v_ustat & DCD_ON)
		(void)(*linesw[tp->t_line].l_modem)(tp, 1);
	else if ((kp->v_ustat & DCD_OFF) &&
	    ((vs->vs_softCAR & (1 << port))) == 0 &&
	    (*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
		register struct vcmds *cp;
		register struct vxcmd *cmdp;

		/* clear all pending transmits */
		if (tp->t_state&(TS_BUSY|TS_FLUSH) &&
		    vs->vs_vers == VXV_NEW) {
			int i, cmdfound = 0;

			cp = &vs->vs_cmds;
			for (i = cp->v_empty; i != cp->v_fill; ) {
				cmdp = (struct vxcmd *)((long *)cp->cmdbuf[i]-1);
				if ((cmdp->cmd == VXC_XMITDTA ||
				    cmdp->cmd == VXC_XMITIMM) &&
				    ((struct vxmit *)cmdp->par)->line == port) {
					cmdfound++;
					cmdp->cmd = VXC_FDTATOX;
					cmdp->par[1] = port;
				}
				if (++i >= VC_CMDBUFL)
					i = 0;
			}
			if (cmdfound)
				tp->t_state &= ~(TS_BUSY|TS_FLUSH);
			/* cmd is already in vioc, have to flush it */
			else {
				cmdp = vobtain(vs);
				cmdp->cmd = VXC_FDTATOX;
				cmdp->par[1] = port;
				(void) vcmd(vx, (caddr_t)&cmdp->cmd);
			}
		}
	} else if ((kp->v_ustat&BRK_CHR) && (tp->t_state&TS_ISOPEN)) {
		(*linesw[tp->t_line].l_rint)(TTY_FE, tp);
		return;
	}
}
#endif