4.3BSD-UWisc/src/sys/vaxuba/vs.c

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

/* @(#)vs.c	7.1 (MIT) 6/5/86 */
#ifndef lint
static char rcs_id[] = {"$Header: vs.c,v 3.1 86/10/22 14:11:07 tadl Exp $"};
#endif not lint
/*
 * RCS Info
 *	$Locker:  $
 */
 /****************************************************************************
 *									    *
 *  Copyright (c) 1983, 1984 by						    *
 *  DIGITAL EQUIPMENT CORPORATION, Maynard, Massachusetts.		    *
 *  All rights reserved.						    *
 * 									    *
 *  This software is furnished on an as-is basis and may be used and copied *
 *  only with inclusion of the above copyright notice. This software or any *
 *  other copies thereof may be provided or otherwise made available to     *
 *  others only for non-commercial purposes.  No title to or ownership of   *
 *  the software is hereby transferred.					    *
 * 									    *
 *  The information in this software is  subject to change without notice   *
 *  and  should  not  be  construed as  a commitment by DIGITAL EQUIPMENT   *
 *  CORPORATION.							    *
 * 									    *
 *  DIGITAL assumes no responsibility for the use  or  reliability of its   *
 *  software on equipment which is not supplied by DIGITAL.		    *
 * 									    *
 *									    *
 ****************************************************************************/

#include "vs.h"
#if NVS > 0

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

#include "param.h"
#include "dir.h"
#include "user.h"
#include "buf.h"
#include "systm.h"
#include "map.h"
#include "kernel.h"
#include "ioctl.h"

#include "vsio.h" 

#include "proc.h"
#include "uio.h"
#include "vmmac.h"
#include "file.h"

#include "ubareg.h"
#include "ubavar.h"
#include "vsreg.h"

#include "../vax/mtpr.h"

#define	VSWAITPRI	(PZERO+1)
#define	VSMAXEVQ	64	/* must be power of 2 */
#define EVROUND(x)	((x) & (VSMAXEVQ - 1))


#define VSBUFFSIZE	3072
struct vsBuffArea {
	vsIoAddr vsioa;
	char	obuff[VSBUFFSIZE];
	vsEvent ibuff[VSMAXEVQ];
};
struct vsBuffArea vsBuff[NVS];


int vsprobe(), vsattach();
struct uba_device *vsdinfo[NVS];
u_short vsstd[] = { 0 };
struct uba_driver vsdriver =
	{ vsprobe, 0, vsattach, 0, vsstd, "vs", vsdinfo, 0, 0 };

#define	VSUNIT(dev)	(minor(dev))

struct vs_softc {
	unsigned inited : 1;		/* has this ever been inited? */
	unsigned open : 1;		/* only one open, please */
	unsigned linkAvail : 1;		/* link is up */
	short	pgrp;			/* process group for SIGHUP */
	int	romVersion;		/* rom version */
	struct vs_fparm	offset;		/* address base */
	struct vs_csr	csr;		/* saved csr0 */
	struct vs_intr	irr;		/* saved interrupt reason */
	struct vs_kbd	krr;		/* saved keyboard */
	struct vs_fparm	pr;		/* saved parameter regs */
	struct proc *rsel;		/* process waiting for select */
	struct vs_fparm vs_nextgo;	/* next packet to go */
	short vs_status;		/* status from previous packet */
	vsStats stats;			/* statistics */
	int vsBuff_ubinfo;		/* ubinfo for vsBuff */
}vs_softc[NVS];

#define	TRUE	1
#define	FALSE	0

#define	printI	if (vsIntrPrintfs)printf
#define	printD	if (vsDebugPrintfs)printf
#define	printM	if (vsMlpPrintfs) vsMlpPrintfs--,printf
int	vsIntrPrintfs = 0;
int	vsDebugPrintfs = 0;
int	vsMlpPrintfs = 0;

/* 
 * Tell the system that it's out there, and set up the device's interrupt
 * vector. Since we are supporting vs100s and vs125s,
 * this is a bit kludgey. The vs100 works much
 * as one expects, but the vs125 tries to set all the fiber link
 * related bits when you hit VS_IE, ignoring the way the 100 works.
 * Also, the vs100 will let you set the interrupt vector, but
 * the vs125 ignores this and uses its hard-wired value.
 * And there's no sure fire to tell which variant it is.
 * Ugh. Ugh. Ugh.
 */

vsprobe(reg)
caddr_t reg;
{
	register int br, cvec;		/* value-result */
	register struct vsdevice *vsaddr = (struct vsdevice *)reg;

#ifdef	lint
	br = 0; cvec = br; br = cvec;
	vsintr(0);
#endif
	br = 0x15;
	cvec = (uba_hd[numuba].uh_lastiv -= 4*8);
	/* 
	 * uh_lastiv is the last free interrupt vector in the
	 * unibus addapter header (uba_hd).
	 */

	vsaddr->vs_csr0 = cvec >> 2;	/* Save the vector for use on next device */
	vsaddr->vs_irr = 0;		/* Csr will only be read if irr == 0 */
	vsaddr->vs_irr = 0;		/* Clear interrupt reason register */
	vsaddr->vs_pr1  = 0;		/* Clear function parameter */
	vsaddr->vs_pr2  = 0;		/* Clear function parameter */
	vsaddr->vs_ivr = cvec;		/* set up vector (no-op for vs125) */

	DELAY(100000);
	if (vsaddr->vs_csr0 & VS_LNK_AVL)
		return(0);	/* light won't go off! */
	vsaddr->vs_csr0 &= ~VS_LNK_TRNS;
	vsaddr->vs_csr0 |= VS_IE;	/* enable interrupts */
	DELAY(200000);

	return sizeof(struct vsdevice);
}

vsattach(uip)
struct uba_device *uip;
{
	register struct vs_softc *vsp;
	register struct vsdevice *vsaddr;

	vsp = &vs_softc[VSUNIT(uip->ui_unit)];
	vsp->inited  = FALSE;
	vsp->open = FALSE;
	vsBuff[VSUNIT(uip->ui_unit)].vsioa.mbox.bottom = 0;
	vsp->linkAvail = FALSE;
	vsp->romVersion = 0;
	vsp->vs_nextgo.fparm_all = NULL;
	
	vsaddr = (struct vsdevice *) uip->ui_addr;
	vsaddr->vs_csr0 |= (VS_IE | VS_XMIT_ON);
}

vsopen(dev, flag)
dev_t dev;
int flag;
{
	register struct vs_softc *vsp;
	register struct uba_device *uip;
	register struct vsdevice *vsaddr;
	int s;
	int ret;
	struct buf vsbuf;
	struct vsBuffArea *vsb;
	caddr_t vsBuffpage;
	int vsBuffnpages;

	if (VSUNIT(dev) >= NVS || (vsp = &vs_softc[VSUNIT(dev)])->open ||
	    (uip = vsdinfo[VSUNIT(dev)]) == 0 || uip->ui_alive == 0)
		return (ENXIO);

	vsaddr = (struct vsdevice *) uip->ui_addr;
	vsb = &vsBuff[VSUNIT(dev)];
	printM("vsopen csr0=%x, csr1=%x, csr2=%x, csr3=%x, csr4=%x, csr5=%x, csr6=%x, csr7=%x\n",
		vsaddr->vs_csr0, vsaddr->vs_csr1, vsaddr->vs_csr2, vsaddr->vs_csr3,
		vsaddr->vs_csr4, vsaddr->vs_csr5, vsaddr->vs_csr6, vsaddr->vs_csr7);

	/* 
	 * Finally! We can now set up the device.
	 */

	if (!vsp->inited && !(flag & FNDELAY)) {
		vsInitDev(dev, TRUE);
		if (ret = vsError(vsp))
			return(ret);
	}

	vsp->open = TRUE;		/* we're open */
	vsp->pgrp = u.u_procp->p_pgrp;

	/* reset statistics */
	bzero((caddr_t) &vsp->stats, sizeof(vsStats));

	/* initialize user I/O addresses */
	vsb->vsioa.ioreg = (short *)vsaddr;
	vsb->vsioa.status = 0;
	vsb->vsioa.obuff = vsb->obuff;
	vsb->vsioa.obufflen = VSBUFFSIZE;
	vsb->vsioa.ibuff = vsb->ibuff;
	vsb->vsioa.ihead = 0;
	vsb->vsioa.itail = 0;
	vsb->vsioa.iqsize = VSMAXEVQ;
	/* map io regs into user address space (assume they don't cross a page) */
	maptouser(vsaddr);
	/* map vsBuff into user address space */
	vsBuffpage = (caddr_t)((int)vsb & ~PGOFSET);
	vsBuffnpages = (((int)vsb & PGOFSET) +
			 (NBPG-1) + sizeof(struct vsBuffArea)) >> PGSHIFT;
	while (vsBuffnpages>0) {
	    maptouser(vsBuffpage);
	    vsBuffpage += NBPG;
	    vsBuffnpages--;
	}
	/* lock in the buffer */
	vsbuf.b_error = 0;
	vsbuf.b_proc = u.u_procp;
	vsbuf.b_un.b_addr = vsb->obuff;
	vsbuf.b_flags = B_BUSY;
	vsbuf.b_bcount = VSBUFFSIZE;
	vsp->vsBuff_ubinfo = ubasetup(uip->ui_ubanum, &vsbuf, UBA_CANTWAIT);

	vsb->vsioa.reloc = (int) (vsp->offset.fparm_all
			+ (vsp->vsBuff_ubinfo & 0x3ffff));
	return(0);
}

vsclose(dev)
dev_t dev;
{
	register struct uba_device *uip = vsdinfo[VSUNIT(dev)];
	register struct vs_softc *vsp = &vs_softc[VSUNIT(dev)];
	int s, i;
	struct vsdevice *vsaddr;
	struct vsBuffArea *vsb;
	caddr_t vsBuffpage;
	int vsBuffnpages;
	
	vsaddr = (struct vsdevice *) uip->ui_addr;
	printM("vsclose csr0=%x, csr1=%x, csr2=%x, csr3=%x, csr4=%x, csr5=%x, csr6=%x, csr7=%x\n",
		vsaddr->vs_csr0, vsaddr->vs_csr1, vsaddr->vs_csr2, vsaddr->vs_csr3,
		vsaddr->vs_csr4, vsaddr->vs_csr5, vsaddr->vs_csr6, vsaddr->vs_csr7);
		vsb = &vsBuff[VSUNIT(dev)];
	if (vsDebugPrintfs) {
		printf("vs%d: %d errors, %d unsolicited interrupts",
			VSUNIT(dev), vsp->stats.errors, vsp->stats.unsolIntr);
		printf(", %d link errors", vsp->stats.linkErrors);
		printf(", %d overruns", vsp->stats.overruns);
		printf(", csr0 %x, csr1 %x", vsaddr->vs_csr0, vsaddr->vs_csr1);
		printf("\n");
	}

	vsp->open = FALSE;
	vsp->inited = FALSE;		/* init on every open */
	vsp->vs_nextgo.fparm_all = NULL;
	vsb->vsioa.mbox.bottom = 0;
	/* release the buffer */
	if (vsp->vsBuff_ubinfo!=0) {
		ubarelse(uip->ui_ubanum, &vsp->vsBuff_ubinfo);
	}

#ifdef notdef
	/* unmap io regs into user address space (assume they don't cross a page) */
	unmaptouser(vsaddr);
	/* unmap vsBuff into user address space */
	vsBuffpage = (caddr_t)((int)vsb & ~PGOFSET);
	vsBuffnpages = (((int)vsb&PGOFSET) +
			 (NBPG-1)+ sizeof(struct vsBuffArea)) >> PGSHIFT;
	while (vsBuffnpages>0) {
	    unmaptouser(vsBuffpage);
	    vsBuffpage += NBPG;
	    vsBuffnpages--;
	}
#endif
}

vsread(dev,uio)
dev_t   dev;
struct uio      *uio;
{
        return(-1);
}

vswrite(dev, uio)
dev_t   dev;
struct uio      *uio;
{
        return(-1);
}

/*ARGSUSED*/
vsioctl(dev, cmd, addr, flag)
dev_t dev;
register caddr_t addr;
{
	register struct uba_device *uip = vsdinfo[VSUNIT(dev)];
	register struct vs_softc *vsp = &vs_softc[VSUNIT(dev)];
	register struct vsdevice *vsaddr = (struct vsdevice *) uip->ui_addr;
	register struct vsBuffArea *vsb = &vsBuff[VSUNIT(dev)];
	struct vs_fparm vsAddr;
	int s;
	int func;
	int ret;

	switch(cmd) {			/* things that don't need the device */
	case VSIOWAITGO:
		/* wait for user I/O operation to complete, then go */
		s = spl5();
		if ((ret = vsb->vsioa.status) == 0) {
			vsp->vs_nextgo.fparm_all = ((struct vs_fparm *) addr)->fparm_all;
			do {
				sleep((caddr_t) vsp, VSWAITPRI);
			} while (vsp->vs_nextgo.fparm_all);
			ret = vsp->vs_status;
		} else {
			vsaddr->vs_pr1 = ((struct vs_fparm *)addr)->fparm_low;
			vsaddr->vs_pr2 = ((struct vs_fparm *)addr)->fparm_high;
			vsb->vsioa.status = 0;
			vsaddr->vs_csr0 &= ~VS_FCN;	/* clear bits */
			vsaddr->vs_csr0 |= (VS_IE | (VS_SEND << VS_FCSHIFT) | VS_GO);
		}
		splx(s);
		if (ret & VS_ERROR)
			return ((ret & VS_REASON) + 128);
		return(0);

	case VSIOUSERWAIT:
		/* wait for user I/O operation to complete */
		s = spl5();
		while (vsb->vsioa.status == 0) {
			sleep((caddr_t) vsp, VSWAITPRI);
		}
		splx(s);
		return (0);

	case VSIOGETVER:		/* get ROM version */
		if (!vsp->inited)
			return(ENODEV);
		*(int *) addr = vsp->romVersion;
		return(0);

	case VSIOGETSTATS:		/* get statistics block */
		*(vsStats *)addr = vsp->stats;
		return(0);

	case VSIOGETIOA:		/* get io addresses */
		if (vsp->vsBuff_ubinfo==0) {
		    return(EIO);
		}
		*((vsIoAddrAddr *)addr) = &vsb->vsioa;
		return(0);

	default:			/* a command that could block */
		if (ret = vsError(vsp))
			return(ret);
		break;
	}

	switch(cmd) {			/* Commands that cause an interrupt */
	case VSIOINIT:			/* initialize device */
		vsInitDev(dev, FALSE);
		return(vsError(vsp));

	case VSIOSTART:			/* start microcode */
		vsAddr.fparm_all = *(caddr_t *)addr;
		s = spl5();
		vsaddr->vs_pr1 = vsAddr.fparm_low;
		vsaddr->vs_pr2 = vsAddr.fparm_high;
		vsaddr->vs_irr = 0;
		vsaddr->vs_csr0 &= ~VS_FCN;	/* clear bits */
		vsaddr->vs_csr0 |= (VS_IE | (VS_START << VS_FCSHIFT) | VS_GO);
		sleep((caddr_t) vsp, VSWAITPRI);	/* synchronous */
		splx(s);
		return(vsError(vsp));

	case VSIOABORT:			/* abort a command chain */
		s = spl5();
		vsaddr->vs_irr = 0;
		vsaddr->vs_csr0 &= ~VS_FCN;
		vsaddr->vs_csr0 |= (VS_IE | (VS_ABORT << VS_FCSHIFT) | VS_GO);
		sleep((caddr_t) vsp, VSWAITPRI);
		splx(s);
		return(vsError(vsp));

	case VSIOPWRUP:			/* power-up reset */
		s = spl5();
		vsaddr->vs_irr = 0;
		vsaddr->vs_csr0 &= ~VS_FCN;
		vsaddr->vs_csr0 |= (VS_IE | (VS_PWRUP << VS_FCSHIFT) | VS_GO);
		sleep((caddr_t) vsp, VSWAITPRI);
		splx(s);
		return(vsError(vsp));

	case VSIOBBACTL:		/* enable/disable BBA */
		s = spl5();
		vsaddr->vs_irr = 0;
		vsaddr->vs_csr0 &= ~VS_FCN;
		func = *(int *)addr == VSIO_ON ? VS_ENABBA : VS_DISBBA;
		vsaddr->vs_csr0 |= (VS_IE | (func << VS_FCSHIFT) | VS_GO);
		sleep((caddr_t) vsp, VSWAITPRI);
		splx(s);
		return(vsError(vsp));

	case VSIOFIBCTL:		/* turn the fiber lamp on/off */
		s = spl5();
		if (*(int *)addr == VSIO_OFF)
			vsaddr->vs_csr0 &= ~VS_XMIT_ON;
		else
			vsaddr->vs_csr0 |= (VS_IE | VS_XMIT_ON);
		sleep((caddr_t) vsp, VSWAITPRI);
		splx(s);
		return(vsError(vsp));

	case VSIOFIBRETRY:		/* set fiber retries */
		s = spl5();
		vsaddr->vs_irr = 0;
		vsaddr->vs_csr0 &= ~VS_FCN;
		func = *(int *)addr == VS_FIB_FINITE ? VS_FINITE : VS_INFINITE;
		vsaddr->vs_csr0 |= (VS_IE | (func << VS_FCSHIFT) | VS_GO);
		sleep((caddr_t) vsp, VSWAITPRI);
		splx(s);
		return(vsError(vsp));

	case VSIOSYNC:			/* get synchronized with device */
		break;

	default:
		return(ENOTTY);
	}

	return(0);
}

vsintr(dev)
dev_t dev;
{
	register struct vsdevice *vsaddr;
	register struct vs_softc *vsp;
	register vsEvent *vep;
	struct uba_device *uip;
	register struct vsBuffArea *vsb;
	int i;
	vsCursor cur;

	if (VSUNIT(dev) >= NVS || (uip = vsdinfo[VSUNIT(dev)]) == 0
	    || uip->ui_alive == 0) {
		printI("vs%d stray interrupt\n", VSUNIT(dev));
		return;
	}

	vsaddr = (struct vsdevice *) uip->ui_addr;
	vsp = &vs_softc[VSUNIT(dev)];
	vsb = &vsBuff[VSUNIT(dev)];
#ifdef notdef
	printM("vsintr csr0=%x, csr1=%x, csr2=%x, csr3=%x, csr4=%x, csr5=%x, csr6=%x, csr7=%x\n",
		vsaddr->vs_csr0, vsaddr->vs_csr1, vsaddr->vs_csr2, vsaddr->vs_csr3,
		vsaddr->vs_csr4, vsaddr->vs_csr5, vsaddr->vs_csr6, vsaddr->vs_csr7);

	printI("vs%dintr ", VSUNIT(dev));
#endif

	/* 
	 * get the information out of the soft registers
	 */

	vsp->irr.intr_reg = vsaddr->vs_irr;
	vsp->krr.kbd_reg = vsaddr->vs_krr;
	vsp->pr.fparm_low = vsaddr->vs_pr1;
	vsp->pr.fparm_high = vsaddr->vs_pr2;
	cur.x = vsaddr->vs_cxr;
	cur.y = vsaddr->vs_cyr;
	vsp->csr.csr_reg = vsaddr->vs_csr0;

	if (vsp->irr.intr_reason)
		vsaddr->vs_irr = 0;	/* clear int reason, if any */

	vsaddr->vs_csr0 &= ~VS_OWN;	/* clear owner bit */

	if (vsp->csr.csr_linkTran) {
		vsaddr->vs_csr0 &= ~VS_LNK_TRNS;	/* clear the bit */
		printI("link transition: ");
		if (vsp->csr.csr_linkErr)
			vsp->stats.linkErrors++;

		if (vsp->csr.csr_linkAvail == vsp->linkAvail) {	/* flash */
			vsp->stats.flashes++;
			printI("flash\n");
		} else if (!vsp->csr.csr_linkAvail && vsp->linkAvail) { /* on -> off */
			vsp->stats.douses++;
			printI("douse\n");
			vsp->inited = FALSE;
			if (vsp->open && vsp->pgrp)
				gsignal(vsp->pgrp, SIGHUP);
			wakeup((caddr_t) vsp);
		} else {						/* off -> on */
			vsp->stats.ignites++;
			printI("ignite\n");
			wakeup((caddr_t) vsp);
		}

		i = 200;
		while ((vsaddr->vs_csr0 & VS_LNK_TRNS) && i)
			i--;
		if (i == 0) {		/* bit stuck */
			printI("vs%d: Link Transition bit stuck\n", VSUNIT(dev));
			vsp->inited = FALSE;
			if (vsp->open && vsp->pgrp)
				gsignal(vsp->pgrp, SIGHUP);
			vsaddr->vs_csr0 &= ~VS_XMIT_ON;
			vsp->csr.csr_linkAvail = FALSE;
		}

		vsp->linkAvail = vsp->csr.csr_linkAvail;

		return;
	}

	if (vsp->irr.intr_error) {
		printI("error 0x%x\n", vsp->irr.intr_reg&0xffff);
		vsp->stats.errors++;
		/* set status and wake up user if necessary */
		if (vsp->vs_nextgo.fparm_all) {
			vsp->vs_status = vsp->irr.intr_reg;
			vsaddr->vs_pr1 = vsp->vs_nextgo.fparm_low;
			vsaddr->vs_pr2 = vsp->vs_nextgo.fparm_high;
			vsp->vs_nextgo.fparm_all = NULL;
			vsaddr->vs_csr0 &= ~VS_FCN;	/* clear bits */
			vsaddr->vs_csr0 |= (VS_IE | (VS_SEND << VS_FCSHIFT) | VS_GO);
		} else
			vsb->vsioa.status = vsp->irr.intr_reg;
		wakeup((caddr_t) vsp);
		return;
	}

#ifdef notdef
	printI("reason is %b\n", vsp->irr.intr_reason, VSIRR_BITS);
#endif
	switch(vsp->irr.intr_reason) {
	case VS_INT_CD:			/* command done */
		/* set status and start a new command if necessary */
		if (vsp->vs_nextgo.fparm_all) {
			vsp->vs_status = vsp->irr.intr_reg;
			vsaddr->vs_pr1 = vsp->vs_nextgo.fparm_low;
			vsaddr->vs_pr2 = vsp->vs_nextgo.fparm_high;
			vsp->vs_nextgo.fparm_all = NULL;
			vsaddr->vs_csr0 &= ~VS_FCN;	/* clear bits */
			vsaddr->vs_csr0 |= (VS_IE | (VS_SEND << VS_FCSHIFT) | VS_GO);
		} else
			vsb->vsioa.status = vsp->irr.intr_reg;
		break;

	case VS_INT_MM:			/* mouse moved */

		vsb->vsioa.mouse = cur;

                if (!vsp->open)
                        return;         /* ignore on closed device */

		/* no event if inside box */
		if (cur.y < vsb->vsioa.mbox.bottom &&
		    cur.y >= vsb->vsioa.mbox.top &&
		    cur.x < vsb->vsioa.mbox.right &&
		    cur.x >= vsb->vsioa.mbox.left)
		    return;

		/* trash box */
		vsb->vsioa.mbox.bottom = 0;

		if (EVROUND(vsb->vsioa.itail+1) == vsb->vsioa.ihead)
		    return;
		i = EVROUND(vsb->vsioa.itail-1);
		if ((vsb->vsioa.itail != vsb->vsioa.ihead) &&
		    (i != vsb->vsioa.ihead)) {
		    vep = &vsb->ibuff[i];
		    if (vep->vse_type == VSE_MMOTION) {
			vep->vse_x = cur.x;
			vep->vse_y = cur.y;
			vep->vse_time = mfpr(TODR);
			return;
		    }
		}
		/* put event into queue and do select */
		vep = &vsb->ibuff[vsb->vsioa.itail];
		vep->vse_type = VSE_MMOTION;
		vep->vse_x = cur.x;
		vep->vse_y = cur.y;
		vep->vse_time = mfpr(TODR);
		vsb->vsioa.itail = EVROUND(vsb->vsioa.itail+1);
		if (vsp->rsel) {
			selwakeup(vsp->rsel, 0);
			vsp->rsel = 0;
		}
		break;

	case VS_INT_BE:			/* button event */
		if (!vsp->open)
			return;		/* ignore on closed device */

		if (vsp->krr.kbd_device == VSE_MOUSE) {
		    vsb->vsioa.mouse.x = cur.x;
		    vsb->vsioa.mouse.y = cur.y;
		}
		/* check for room in the queue */
		if ((i = EVROUND(vsb->vsioa.itail+1)) == vsb->vsioa.ihead)
		    return;
		/* put event into queue and do select */
		vep = &vsb->ibuff[vsb->vsioa.itail];
		vep->vse_type = VSE_BUTTON; 
		vep->vse_key = vsp->krr.kbd_key;
		vep->vse_direction = vsp->krr.kbd_transition;
		vep->vse_device = vsp->krr.kbd_device;
	        vep->vse_time = mfpr(TODR);
		vep->vse_x = vsb->vsioa.mouse.x;
		vep->vse_y = vsb->vsioa.mouse.y;
		vsb->vsioa.itail = i;
		if (vsp->rsel) {
			selwakeup(vsp->rsel, 0);
			vsp->rsel = 0;
		}
		break;

	case VS_INT_TM:			/* tablet moved */
		if (!vsp->open)
			return;		/* ignore on closed device */

		if (EVROUND(vsb->vsioa.itail+1) == vsb->vsioa.ihead)
		    return;
		i = EVROUND(vsb->vsioa.itail-1);
		if ((vsb->vsioa.itail != vsb->vsioa.ihead) &&
		    (i != vsb->vsioa.ihead)) {
		    vep = &vsb->ibuff[i];
		    if (vep->vse_type == VSE_TMOTION) {
			vep->vse_x = cur.x;
			vep->vse_y = cur.y;
			vep->vse_time = mfpr(TODR);
			return;
		    }
		}
		/* put event into queue and do select */
		vep = &vsb->ibuff[vsb->vsioa.itail];
		vep->vse_type = VSE_TMOTION;
		vep->vse_x = cur.x;
		vep->vse_y = cur.y;
		vep->vse_time = mfpr(TODR);
		vsb->vsioa.itail = EVROUND(vsb->vsioa.itail+1);
		if (vsp->rsel) {
			selwakeup(vsp->rsel, 0);
			vsp->rsel = 0;
		}
		break;

	case VS_INT_US:			/* unsolicited */
		vsp->stats.unsolIntr++;
		return;

	case VS_INT_ID:			/* Initialization done */
					/* save offset from device */
		vsp->offset.fparm_all = vsp->pr.fparm_all;
					/* save rom version */
		vsp->romVersion = cur.x;
		vsp->inited = TRUE;
		break;

	case VS_INT_SE:			/* ucode started */
		break;

	case VS_INT_PWR:		/* power up complete */
					/* save rom version */
		vsp->romVersion = cur.x;
		vsp->inited = FALSE;
		if (vsp->open && vsp->pgrp)
			gsignal(vsp->pgrp, SIGHUP);
		break;

	default:
		printI("vs%d: unknown interrupt %b\n", VSUNIT(dev),
			vsp->irr.intr_reason, VSIRR_BITS);
		return;
	}
	wakeup((caddr_t) vsp);
}

vsreset(uban)
int uban;
{
	register int i;
	register struct uba_device *uip;
	register struct vs_softc *vsp = vs_softc;

	for (i = 0; i < NVS; i++, vsp++) {
		if ((uip = vsdinfo[i]) == 0 || uip->ui_alive == 0 ||
		    uip->ui_ubanum != uban || vsp->open == 0)
			continue;
		printf(" vs%d", i);
		vsp->inited = FALSE;
		if (vsp->open && vsp->pgrp)
			gsignal(vsp->pgrp, SIGHUP);
	}
}

vsselect(dev, rw)
dev_t dev;
{
	register struct vsBuffArea *vsb = &vsBuff[VSUNIT(dev)];
	int s = spl5();

	switch(rw) {
	case FREAD:
		if (vsb->vsioa.ihead != vsb->vsioa.itail) {
		    splx(s);
		    return(1);
		}
		vs_softc[VSUNIT(dev)].rsel = u.u_procp;
		splx(s);
		return(0);

	case FWRITE:
		splx(s);
		return(EACCES);
	}
}

/*
 * Initialize VS100 or SBO.
 * Set XMITON.  VS100 will respond with link available.  SBO won't, so
 * don't wait forever; assume everything is OK and warn user.
 */

vsInitFiber(dev)
dev_t dev;
{
	struct vsdevice *vsaddr = (struct vsdevice *) vsdinfo[VSUNIT(dev)]->ui_addr;
	register struct vs_softc *vsp = &vs_softc[VSUNIT(dev)];
	int s;
#ifdef VSSBO
	int vsFiberNudge();

	timeout(vsFiberNudge, (caddr_t) dev, 2*hz);
#endif
	s = spl5();
	vsaddr->vs_csr0 |= (VS_IE | VS_XMIT_ON);	/* turn link on */
	sleep((caddr_t) vsp, VSWAITPRI);
	splx(s);
#ifdef VSSBO
	if (!vsp->linkAvail) {
		uprintf("\007This had better be a vs125!\n");
		printf("vs%d must be a vs125\n", VSUNIT(dev));
		vsp->linkAvail = TRUE;
	}
#endif
}

#ifdef VSSBO
vsFiberNudge(dev)
dev_t dev;
{
	struct vs_softc *vsp = &vs_softc[VSUNIT(dev)];

	if (!vsp->linkAvail)
		wakeup((caddr_t) vsp);
}
#endif VSSBO

vsInitDev(dev, retry)
dev_t dev;
int retry;
{
	register struct vsdevice *vsaddr;
	register struct vs_softc *vsp;
	int s;
	int vsInitNudge();

	vsaddr = (struct vsdevice *) vsdinfo[VSUNIT(dev)]->ui_addr;
	vsp = &vs_softc[VSUNIT(dev)];

	if (!vsp->linkAvail)
		vsInitFiber(dev);
	while (1) {
		if (retry)
			timeout(vsInitNudge, (caddr_t) dev, 10*hz);
		s = spl5();
		vsaddr->vs_irr = 0;
		vsaddr->vs_csr0 &= ~VS_FCN;
		vsaddr->vs_csr0 |= (VS_IE | (VS_INIT << VS_FCSHIFT) | VS_GO);
		sleep((caddr_t) vsp, VSWAITPRI);
		splx(s);
		if (vsp->inited)
			break;
		printM("vs%d: VS_INIT fails\n", VSUNIT(dev));
		uprintf("vsInitDev %x %x\n",vsaddr->vs_csr0, vsaddr->vs_csr1);
	}
}

vsInitNudge(dev)
dev_t dev;
{
	struct vs_softc *vsp = &vs_softc[VSUNIT(dev)];

	if (!vsp->inited)
		wakeup((caddr_t) vsp);
}

vsError(vsp)
	register struct vs_softc *vsp;
{
	if (vsp->irr.intr_error) {
		register int ret = vsp->irr.intr_reg;

		printD("\treturning 0x%x\n", ret);
		vsp->irr.intr_reg = 0;
		return(ret+128);
	}
	return(0);
}
#endif