V9/sys/dev.old/ds.c

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

/* 10-26-86 put in splx(ps)'s on error returns from dsstart loop */
/* 9-5-86 re-fixed infamous vstodb-on-close bug */
/* 11-25-85 fixed buffer initialization bug */
/* 9-5-85 simulated DSWAIT ioctl inside dsclose */
/* 4-22-85 added check for partial buffer in dscleanup */
/* 3-27-85 cleaned up handling of signals; added DSSTOP, DSFILTER;
	made DSMONO, DSSTEREO sleep; added check for 0 in DSRATE */
/* 1-18-85 restricted use of priority-raising; check for offline in probe() */
/* 5-29-84  dsrelease checks for zombie state or even death */
/* dsclose() shuts down device */
/* DSWAIT added */
/* 5-25-84  process pointer saved in dsopen for dsintr() */
/*	vsunlock must then be duplicated using correct process structure */
/* the interrelation of dsstart-sleeping, dsintr, and dscleanup
	needs to be cleaned up 
	Probably dscleanup should not call wakeup */


#include "ds.h"
# if NDS > 0

/*
 * DSC System 200 driver
 * via DSC dma11.
 * vax 4.1bsd version
 */

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/mount.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/conf.h"
#include "../h/proc.h"
#include "../h/file.h"
#include "../h/vmmac.h"

#include "../h/dsreg.h"
#include "../h/dsc.h"

/*
 * THESE ARE SITE-SPECIFIC
 *
 * starting base for the
 * d/a and a/d converters,
 * for setting up sequence ram.
 */

/*
 * reset device
 */
# define RESETDEV	bit(4)
# define dsseq(sc, reg, conv, dir) \
	(sc->c_ascseq[reg] = conv | ((dir & 01) << 6))

/*
 * ds flags
 */
# define DS_CLOSED	0
# define DS_OPEN	bit(0)
# define DS_IDLE	bit(1)
# define DS_NDSK	bit(2)
# define DS_MON		bit(3)
# define DS_BRD		bit(4) 
# define DS_READ	bit(5)
# define DS_WRITE	bit(6)


# define DSPRI		(PZERO+1)
# define SPL_DS()	spl6()
/*
 * Redundant book-keeping
 * to help avoid page-wise overlap of buffers
 *	remember that this device is both asynchronous and
 *	uses user-supplied buffers
 */
struct dspageinfo {
	int firstunit,
		lastunit;	/* in units of CLSIZE*NBPG, i.e. 1024 */
};
/*
 * relevant information
 * about the dma and asc
 */
struct ds_softc {
	short		c_dmacsr;	/* copy of dma csr */
	short		c_asccsr;	/* copy of asc csr */
	short		c_ascflags;	/* initial asc flags */
	short		c_ascsrt;	/* sampling rate */
	short		c_ascseq[8];
	int		c_flags;	/* internal flags */
	int		c_errs;		/* errors, returned via ioctl */
	int		c_bufno;	/* dsubinfo/buffer */
	int		c_outbufno;
	int		c_uid;		/* user id */
	short		c_pid;		/* process id */
	struct proc	*c_procp;	/* process structure pointer */
	int		c_ubinfo[NDSB];	/* uba info */
	struct dspageinfo	c_pageinfo[NDSB];
	struct buf	c_dsb[NDSB];	/* buffer list */
	struct buf	*c_ibp, *c_obp;	/* buffer list pointers */
	int		c_nbytes;	/* total # of bytes xferred since reset */
	int		c_to_idle;	/* total # times idle flag set */
	int		c_to_active;	/* total # times idle flag cleared */
} ds_softc[NDS];

int dsprobe(), dsattach(), dsintr();

struct uba_device *dsdinfo[NDS];
struct uba_ctlr dsctlr[NDS], *dscinfo[NDS];	/* dsattach() sets dscinfo to point to this */

u_short dsstd[] = {
	0165400, 0
};

struct uba_driver dsdriver = {
	dsprobe, 0, dsattach, 0, dsstd, "ds", dsdinfo, "ds", dscinfo
};
int	dsdebug;

/*
 * all this just to generate
 * an interrupt; rather
 * involved isn't it?
 */
dsprobe(reg)
caddr_t reg;
{
	register int br, cvec;		/* value-result */
	register struct dsdevice *dsaddr;
	int dummy;

# ifdef lint	
	br = 0; cvec = br; br = cvec;
# endif lint

	dsaddr = (struct dsdevice *) reg;
	if ((dummy = dsaddr->dmacsr) & DMA_OFL) {
		printf("dsc offline\n");
		return 0;
	}

	dsaddr->dmacsr = 0;
	dsaddr->dmacls = 0;
	dsaddr->ascseq[0] = DABASE+0 | ((DA & 01) << 6) | LAST_SEQ;
	dsaddr->ascsrt = 0100;
	dsaddr->dmacsr = DMA_IE;
	dsaddr->asccsr = ASC_RUN | ASC_IE;

	DELAY(40000);

	/*
	 * now shut everything down.
	 */
	dsaddr->dmacls = 0;
	dummy = dsaddr->dmasar;			/* clears sar flag */
	dsaddr->dmaclr = 0;

	dummy = dsaddr->ascrst;
	dsaddr->ascrst = 0; 

	return(1);
}

dsattach(ui)
struct uba_device *ui;
{
	register struct uba_ctlr *um;
	register int unit;
	register int i;

	unit = ui->ui_unit;

	um = &dsctlr[unit];
	dscinfo[unit] = um;
	ui->ui_ctlr = unit;
	ui->ui_mi = um;
	um->um_driver = ui->ui_driver;
	um->um_ctlr = unit;
	um->um_ubanum = ui->ui_ubanum;
	um->um_alive = 1;
	um->um_intr = ui->ui_intr;
	um->um_addr = ui->ui_addr;
	um->um_hd = ui->ui_hd;
	ds_softc[unit].c_flags = DS_IDLE;
	for (i = 0; i < NDSB; i++)
		ds_softc[unit].c_dsb[i].b_flags = 0;
}

/* ARGSUSED */
dsopen(dev, flag)
dev_t dev;
{
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register int unit, i;
	register struct buf *bp;
	int dummy;

	if ((unit = (minor(dev) & ~RESETDEV)) >= NDS)
		goto bad;

	if ((ui = dsdinfo[unit]) == NULL)
		goto bad;

	if (ui->ui_alive == 0)
		goto bad;

	sc = &ds_softc[ui->ui_unit];
	dsaddr = (struct dsdevice *)ui->ui_addr;

	if(dsdebug)printf("dsopen: unit %d, flag %x\n",unit,flag);
	if (dsaddr->dmacsr & DMA_OFL) {
		u.u_error = ENXIO;
		return;
	}

	/*
	 * if this is the reset device
	 * then just do a reset and return.
	 */
	if (minor(dev) & RESETDEV) {
		/*
		 * if the converters are in use then
		 * only the current user or root can
		 * do a reset.
		 */
		if (sc->c_flags & DS_OPEN) {
			if ((sc->c_uid != u.u_ruid) && (u.u_uid != 0)) {
				u.u_error = ENXIO;
				return;
			}
		}
		dscleanup(unit);
		sc->c_flags = DS_CLOSED | DS_IDLE;
		printf("ds%d: reset\n",unit);
		return;
	}

	/*
	 * only one person can use it at a time
	 * and it can't be opened for both reading and writing
	 */
	if ((sc->c_flags & DS_OPEN) || (flag & (FREAD|FWRITE)) == (FREAD|FWRITE)) {
bad:		u.u_error = ENXIO;
		return;
	}

	/*
	 * initialize
	 */


	/* set defaults and initial conditions in ds_softc */
	sc->c_flags = DS_IDLE;
	sc->c_errs = 0;

	sc->c_nbytes = sc->c_to_idle = sc->c_to_active = 0;

	sc->c_ascflags = (flag & FWRITE ? ASC_PLAY : ASC_RECORD) | ASC_HZ04;
	sc->c_ascseq[0] = (
		(flag & FWRITE) ?
			(DABASE+0 | (DA << 6)) :  (ADBASE+0 | (AD << 6))
						) | LAST_SEQ;
	sc->c_ascsrt = 399;	/* 10 kHz */

	sc->c_uid = u.u_ruid;
	sc->c_pid = u.u_procp->p_pid;
	sc->c_procp = u.u_procp;
	for (i = 0; i < NDSB; i++)
		ds_softc[unit].c_dsb[i].b_flags = 0;
	sc->c_ibp = sc->c_obp = &sc->c_dsb[0];
	sc->c_outbufno = sc->c_bufno = 0;

	if(dsdebug)printf("pid %d %d, flag %x stat %x\n",
			      sc->c_pid,sc->c_procp->p_pid,
			      u.u_procp->p_flag,u.u_procp->p_stat);
	sc->c_flags |= DS_OPEN | (flag & FREAD ? DS_READ : DS_WRITE);
	u.u_procp->p_flag |= SPHYSIO;
	dsaddr->dmacsr = 0;
	dummy = dsaddr->dmasar;
}

/* ARGSUSED */
dsclose(dev, flag) {
	register int unit;
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ds_softc *sc;

	unit = minor(dev) & ~RESETDEV;
	sc = &ds_softc[unit];
	ui = dsdinfo[unit];
	dsaddr = (struct dsdevice *)ui->ui_addr;
	if(dsdebug)printf("X");

	/*  In case user doesn't use the DSWAIT ioctl */
	while (sc->c_outbufno != sc->c_bufno && !(sc->c_flags & DS_IDLE)) {
		if (tsleep((caddr_t)&ds_softc[unit], DSPRI, 5) != TS_OK)
			break;
	}

	dscleanup(unit);
	sc->c_flags = DS_CLOSED | DS_IDLE;
	u.u_procp->p_flag &= ~SPHYSIO;
	if(dsdebug){
		printf("to_active %d, to_idle %d, c_nbytes %d\n",
		sc->c_to_active,
		sc->c_to_idle,
		sc->c_nbytes);
		printf("c_ascseq[0] %x\n",sc->c_ascseq[0]);
		dsdb(dsaddr);
	}
}

/*
 *
 * Using u.u_count, u.u_base, each buffer header is set up.
 * The converters only need the base address of the buffer and the word
 * count.
 *
 */
dsstart(dev, rw)
register dev_t dev;
{
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register struct buf *bp;
	register int	unit, c, bufno, ps;
	int dummy,i, count;
	caddr_t base;
	int bits;

	if(dsdebug)printf("S");

	unit = minor(dev) & ~RESETDEV;
	sc = &ds_softc[unit];
	ui = dsdinfo[unit];
	dsaddr = (struct dsdevice *)ui->ui_addr;

	/*
	 * check user access rights to buffer
	 */
	if (useracc(u.u_base, u.u_count, rw==B_READ?B_WRITE:B_READ) == NULL) {
		u.u_error = EFAULT;
		return;
	}

	if (sc->c_errs) {
		u.u_error = EIO;
		return;
	}


	/*
	 * Get a buffer for each 64K block
	 * point each device buffer somewhere into
	 * the user's buffer
	 */
	/* NOTE that in order to get reasonable performance,
	 * buffer lengths (initial u.u_count) must be either
	 * less than 64K or sufficiently greater than a multiple
	 * of 64K.
	 */
	base = u.u_base;
	count = u.u_count;
	while(count > 0) {

		if(dsdebug)printf("LT ");

		ps = SPL_DS();
		if ((sc->c_dmacsr = dsaddr->dmacsr) & DMA_SFL) {
			register	ts;

			/*
			 * no hardware buffers left, sleep till interrupt
			 * wakes us up
			 */
			if(dsdebug)printf("s");
			ts = tsleep((caddr_t) &ds_softc[unit], DSPRI, 0);
			if(dsdebug)printf("w");
#ifdef RESTRICT
			splx(ps);
#endif
			switch(ts) {
				case TS_SIG:
#ifndef RESTRICT
					splx(ps);
#endif
					u.u_error = EINTR;
					return;
				case TS_OK:
					if ((sc->c_dmacsr = dsaddr->dmacsr) & DMA_SFL) {
#ifndef RESTRICT
						splx(ps);
#endif
						u.u_error = EIO;
						printf("ds%d  still full after tsleep\n",unit);
						dscleanup(unit);
						return; 
					}
			/* This is not necessary */
					if((sc->c_flags & DS_CLOSED) ||
					  !(sc->c_flags & DS_OPEN)){
#ifndef RESTRICT
						splx(ps);
#endif
						if(dsdebug)
					  printf("ds: wakeup unopen\n");
						u.u_error = EIO;
						dscleanup(unit);
						return;
					}
					break;
				default:
#ifndef RESTRICT
					splx(ps);
#endif
					printf("ds: %x from tsleep\n",ts);
					dscleanup(unit);
					u.u_error = EIO;
					return;
			}
#ifdef RESTRICT
		} else
			splx(ps);
#else
		}

		splx(ps);
#endif
		/*
		 * If device is IDLE (initial read/write, or datalate condition
		 *  has been detected in dsintr()), (re)initialize dsc
		 */

		ps = SPL_DS();

		if (sc->c_flags & DS_IDLE) {

			if(dsdebug) printf("I1 ");
			dsaddr->asccsr = sc->c_asccsr = sc->c_ascflags;
			dummy = dsaddr->ascrst;
			dsaddr->asccsr = (sc->c_asccsr |= ASC_IE);
			dsaddr->ascsrt = sc->c_ascsrt;
			{register	j, i = 0;
				do {
					dsaddr->ascseq[i] = sc->c_ascseq[i];
					j = i++;
				} while(!(sc->c_ascseq[j] & LAST_SEQ) && i<8);
			}
			dsaddr->dmacsr = sc->c_dmacsr = 0;
			dsaddr->dmacls = 0;
			dummy = dsaddr->dmasar;			/* clears sfl flag */
			dsaddr->dmawc = -1;
			dsaddr->dmacsr = sc->c_dmacsr = (DMA_IE | DMA_CHN
					| (rw == B_READ ? DMA_W2M : 0) );

			sc->c_bufno = sc->c_outbufno = 0;
			sc->c_ibp = sc->c_obp = &sc->c_dsb[0];	/* correct?  */

			sc->c_flags &= ~DS_IDLE;
			++sc->c_to_active;
		}
#ifdef RESTRICT
		splx(ps);
#endif
		/* get next device buffer and set it up */
		bp = sc->c_ibp++;
		if (sc->c_ibp > &sc->c_dsb[NDSB-1])
			sc->c_ibp = &sc->c_dsb[0];

		/* hopefully redundant check against going too fast */
		if(bp->b_flags & B_BUSY){
			printf("dsstart: about to set up BUSY buffer!\n");
			u.u_error = EIO;
			return;
		}

		c = MIN(count, 1024*64);
		bp->b_un.b_addr = base;
		bp->b_error = 0;
		bp->b_proc = u.u_procp;
		bp->b_dev = dev;
		bp->b_blkno = sc->c_bufno;
		bp->b_bcount = c;
		bufno = sc->c_bufno % NDSB;

		/* the page computations should be relative to param.h defs */

		sc->c_pageinfo[bufno].firstunit = (int)base >> BSHIFT(0);
		sc->c_pageinfo[bufno].lastunit =
				(((int)base + c + BMASK(0)) >> BSHIFT(0)) - 1;

		for (i = 0; i < NDSB; i++) {
			if (!(sc->c_dsb[i].b_flags & B_BUSY))
				continue;
			if (sc->c_pageinfo[bufno].firstunit <=
						sc->c_pageinfo[i].lastunit
					&& sc->c_pageinfo[bufno].lastunit >=
						sc->c_pageinfo[i].firstunit) {
				printf("ds: buf %d overlaps %d\n",i,bufno);
				u.u_error=EIO;
				return;
			}
		}

		/* lock the buffer and send it off */

		bp->b_flags = B_BUSY | B_PHYS  | rw;
		if(dsdebug)printf("lk%d %x %d ",bufno,base,c);
		dslock(base, (int) c);	/* fasten seat belts */

		sc->c_ubinfo[bufno] = ubasetup(ui->ui_ubanum, bp, UBA_WANTBDP);

#ifdef RESTRICT
		ps = SPL_DS();
#endif
		dsaddr->dmablr = ~ (c >> 1);
		dsaddr->dmasax = (sc->c_ubinfo[bufno] >> 16) & 03;
		dsaddr->dmasar = sc->c_ubinfo[bufno];
			
		sc->c_bufno++;

		/*
		 * make sure the ASC is running
		 */
		dsaddr->asccsr = (sc->c_asccsr |= ASC_RUN);

		splx(ps);

		base += c;
		count -= c;
		if(dsdebug)printf("LB%x",u.u_error);
	}
		
	u.u_count = count;
}
dsdb(dsaddr)
register struct dsdevice *dsaddr;
{
	printf("dmacsr %b\n", dsaddr->dmacsr, DMA_BITS);
	printf("asccsr %b\n", dsaddr->asccsr, ASC_BITS);
	printf("dmawc %o\t", dsaddr->dmawc);
	printf("dmablr %o\n", dsaddr->dmablr);
	printf("dmaac %o\t", dsaddr->dmaac);
	printf("dmasar %o\n", dsaddr->dmasar);
	printf("dmaacx %o\t", dsaddr->dmaacx);
	printf("dmasax %o\n", dsaddr->dmasax);
	printf("ascsrt(decimal) %d\n", dsaddr->ascsrt);
	printf("ascseq[0] %o\n", dsaddr->ascseq[0]);
}
/*
 * this is where the real work is done. we copy any device registers that
 * we will be looking at to decrease the amount of traffic on the ds 200
 * bus.
 *
 */
dsintr(dev)
dev_t dev;
{
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register struct buf *bp;
	register int bufno;
	register int i;
	int unit, dummy;

	unit = minor(dev) & ~RESETDEV;
	sc = &ds_softc[unit];
	ui = dsdinfo[unit];

	dsaddr = (struct dsdevice *) dsdinfo[unit]->ui_addr;
	
	sc->c_dmacsr = dsaddr->dmacsr;
	dsaddr->dmacls = 0;

	if(dsdebug)printf("i");
	if (sc->c_flags & DS_IDLE) {
		printf("ds%d: interrupt while idle\n", unit);
		return;
	}

	if (sc->c_dmacsr & DMA_ERR) {
		if(dsdebug)printf("e");
		sc->c_asccsr = (sc->c_dmacsr & DMA_XIN ? dsaddr->asccsr : 0);
		dsaddr->asccsr = dummy = 0;

		dsaddr->dmacsr = (sc->c_dmacsr &= ~DMA_CHN);
		if ((sc->c_dmacsr & (DMA_XER|DMA_UBA|DMA_AMPE|DMA_XBA))
				|| (sc->c_asccsr & (ASC_BA|ASC_DCN|ASC_DNP))) {
			++sc->c_errs;
			printf("ds%d error: asccsr=%b dmacsr=%b\n",
				unit, sc->c_asccsr, ASC_BITS,
				sc->c_dmacsr, DMA_BITS);
		}
		dsaddr->dmablr = 0;
		dscleanup(unit);
		return;
	}
		
	/*
	 * get current buffer and release it
	 */

	bp = sc->c_obp++;
	if (sc->c_obp > &sc->c_dsb[NDSB-1])
		sc->c_obp = &sc->c_dsb[0];

	sc->c_outbufno++;
	if(dsdebug)printf("b ");
	dsrelease(sc, ui, bp);

	/*
	 * update byte count.
	 */

	sc->c_nbytes += bp->b_bcount;

	wakeup((caddr_t) &ds_softc[unit]);
}

/*
 * release resources, if any, associated with a buffer
 */
dsrelease(sc, ui, bp)
register struct ds_softc *sc;
struct uba_device *ui;
register struct buf *bp;
{
	register int	ps;
	register struct pte *pte;
	register int npf;
	int	bufno;

	bufno = bp->b_blkno % NDSB;
	if(dsdebug)printf("R%d ",bufno);
	/*
	 * release uba resources
	 * and memory resources if process seems healthy
	 */
	ps = SPL_DS();

	if (bp->b_flags & B_BUSY) {
		if(dsdebug) printf("%x %d ",bp->b_un.b_addr,bp->b_bcount);
		ubarelse(ui->ui_ubanum, &sc->c_ubinfo[bufno]);

		if (sc->c_pid != sc->c_procp->p_pid ||
			 (sc->c_procp->p_flag & SWEXIT) ||
			 sc->c_procp->p_stat == SZOMB){
				/* process is dying or dead - exit() will
				    release memory (does it unlock???) but
				    will not release uba resources */
			printf(" REL pid %d %d flag %x stat %x ",sc->c_pid,
					sc->c_procp->p_pid,sc->c_procp->p_flag,
						sc->c_procp->p_stat);
			sc->c_flags |= DS_CLOSED;
		}
		else {
			/* we can't call vsunlock() because u.u_procp may be
				different */
			pte = vtopte(sc->c_procp,btop(bp->b_un.b_addr));
			npf = btoc(bp->b_bcount + ((int)(bp->b_un.b_addr)&CLOFSET));
			if(dsdebug)printf("U%x %d ",pte->pg_pfnum,npf);
			while(npf > 0) {
				munlock(pte->pg_pfnum);
				if(sc->c_ascflags & ASC_RECORD)
					pte->pg_m = 1;	/* memory written */
				pte += CLSIZE;
				npf -= CLSIZE;
			}
		}

		bp->b_flags = 0;  /* better than resetting B_BUSY */
	}

	splx(ps);
}

/*
 * release all buffers and do general cleanup
 */
dscleanup(unit)
int unit;
{
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register struct buf *bp;
	int	dummy;

	unit = (unit & ~RESETDEV);
	sc = &ds_softc[unit];
	ui = dsdinfo[unit];
	dsaddr = (struct dsdevice *) dsdinfo[unit]->ui_addr;

	/* Check for partial buffer */
	if ((dummy = dsaddr->dmawc) != ~0)
		sc->c_nbytes += sc->c_obp->b_bcount - ((~dummy) << 1);

	dsaddr->asccsr = dummy = 0;
	dummy = dsaddr->ascrst;
	dsaddr->dmacsr = dummy = 0;
	dummy = dsaddr->dmasar;
	
	if(dsdebug)printf("C ");
	if (!(sc->c_flags & DS_IDLE)) {
		sc->c_flags |= DS_IDLE;
		++sc->c_to_idle;
	}

	for (bp = &(sc->c_dsb[0]); bp < &(sc->c_dsb[NDSB]); ++bp)
		dsrelease(sc, ui, bp);


	/* in case called from dsintr while dsstart is sleeping */

	wakeup((caddr_t) &ds_softc[unit]);
}

/*
 * a/d conversion
 */
dsread(dev)
dev_t dev;
{
	dsstart(dev, B_READ);		/* writes on disk */
}

/*
 * d/a conversion
 */
dswrite(dev)
dev_t dev;
{
	dsstart(dev, B_WRITE);		/* reads from disk */
}

/* ARGSUSED */
dsioctl(dev, cmd, addr, flag)
dev_t dev;
caddr_t addr;
{
	register struct dsdevice *dsaddr;
	register struct ds_softc *sc;
	register struct ds_seq *dq;
	register struct ds_err *de;
	register struct ds_trans *dt;
	struct uba_device *ui;
	struct ds_seq ds_seq;
	struct ds_err ds_err;
	struct ds_trans ds_trans;
	int unit;
	int flts;
	int i;
	int iarg[2];

	if (minor(dev) & RESETDEV) {
		u.u_error = EINVAL;
		return;
	}
	unit = minor(dev);

	sc = &ds_softc[unit];
	ui = dsdinfo[unit];
	dsaddr = (struct dsdevice *) ui->ui_addr;

	switch (cmd) {
		/* set sample rate */
		case DSRATE:
			
			if (copyin(addr, (caddr_t)iarg, sizeof(int))) {
				u.u_error = EFAULT;
				return;
			}
			if (iarg[0] == 0) {
				u.u_error = EINVAL;
				return;
			}
			sc->c_ascsrt = 4000000/iarg[0] - 1;
			break;

		case DS08KHZ:
			sc->c_ascflags &= ~ ASC_HZMSK;
			sc->c_ascflags |= ASC_HZ08;	/* set 8kHz filter */
			break;

		case DS04KHZ:
			sc->c_ascflags &= ~ ASC_HZMSK;
			sc->c_ascflags |= ASC_HZ04;	/* set 4kHz filter */
			break;

		case DSBYPAS:
			sc->c_ascflags &= ~ ASC_HZMSK;
			sc->c_ascflags |= ASC_BYPASS;	/* set bypass */
			break;

		case DSFILTER:
			if (copyin(addr, (caddr_t)iarg, sizeof(int))) {
				u.u_error = EFAULT;
				return;
			}
			sc->c_ascflags &= ~ ASC_HZMSK;
			sc->c_ascflags |= (iarg[0] << ASC_HZSHIFT) & ASC_HZMSK;
			break;

		/* fetch errors */
		case DSERRS:
			de = &ds_err;
			de->dma_csr = sc->c_dmacsr;
			de->asc_csr = sc->c_asccsr;
			de->errors = sc->c_errs;
			if (copyout((caddr_t) de, addr, (unsigned) sizeof(struct ds_err))) {
				u.u_error = EFAULT;
				return;
			}
			break;

		/* fetch transition counts */
		case DSTRANS:
			dt = &ds_trans;
			dt->to_idle = sc->c_to_idle;
			dt->to_active = sc->c_to_active;
			if (copyout((caddr_t) dt, addr, sizeof(struct ds_trans))) {
				u.u_error = EFAULT;
				return;
			}
			break;

		/* how many samples actually converted */
		case DSDONE:
			if (copyout((caddr_t)&sc->c_nbytes, addr, sizeof(int))) {
				u.u_error = EFAULT;
				return;
			}
			break;

		case DSSTEREO:
			while (!sc->c_flags & DS_IDLE) {
				if (tsleep(&ds_softc[unit],DSPRI,0) == TS_SIG) {
					u.u_error = EINTR;
					return;
				}
			}
			if (sc->c_flags & DS_READ) {
				dsseq(sc, 0, ADBASE, AD);
				dsseq(sc, 1, ADBASE+1 | LAST_SEQ, AD);
			} else {
				dsseq(sc, 0, DABASE, DA);
				dsseq(sc, 1, DABASE+1 | LAST_SEQ, DA);
			}
			break;

		case DSMONO:
			while (!sc->c_flags & DS_IDLE) {
				if (tsleep(&ds_softc[unit],DSPRI,0) == TS_SIG) {
					u.u_error = EINTR;
					return;
				}
			}
			if (sc->c_flags & DS_READ)
				dsseq(sc, 0, ADBASE | LAST_SEQ, AD);
			else
				dsseq(sc, 0, DABASE | LAST_SEQ, DA);
			break;

		case DSRESET:
			printf("dsreset\n");
			break;
				/* not adequate */
/*			sc->c_flags = DS_CLOSED | DS_IDLE;
			dscleanup(unit);
			break;
*/
		case DSDEBUG:
 			dsdebug = 1 - dsdebug;
			break;

		case DSWAIT:
			while (sc->c_outbufno != sc->c_bufno
					&& !(sc->c_flags & DS_IDLE)) {
				if (tsleep(&ds_softc[unit],DSPRI,0) == TS_SIG) {
					u.u_error = EINTR;
					return;
				}
			}
			break;

		case DSSTOP:
			/* stop dsc, if active */
			dscleanup(unit);
			break;

		default:
			u.u_error = ENOTTY;
			break;
	}
}


/*
 * zero uba vector.
 * shut off converters.
 * set error bit.
 */
dsreset(uban) {
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register int ds;

	for (ds = 0; ds < NDS; ds++) {
		if ((ui = dsdinfo[ds]) == NULL)
			continue;
		if (ui->ui_alive == 0)
			continue;
		if (ui->ui_ubanum != uban)
			continue;

		printf(" ds%d", ds);

		sc = &ds_softc[ds];

		/*
		 * release unibus resources
		 */
		sc->c_flags = DS_CLOSED | DS_IDLE;
		dscleanup(ds);
	}
}
/*
 equivalent to vslock() from vmmem.c
  inserted differently here for debugging convenience only.
 */
dslock(base, count)
	caddr_t base;
{
	register unsigned v;
	register int npf, ps = spl6();
	register struct pte *pte;

	u.u_procp->p_flag |= SDLYU;
	v = btop(base);
	pte = vtopte(u.u_procp, v);
	npf = btoc(count + ((int)base & CLOFSET));
	if(dsdebug)printf("Lk %x %d ",pte->pg_pfnum,npf);
	while (npf > 0) {
		if (pte->pg_v) 
			mlock(pte->pg_pfnum);
		else
			if (fubyte((caddr_t)ctob(v)) < 0)
				panic("vslock");
		pte += CLSIZE;
		v += CLSIZE;
		npf -= CLSIZE;
	}
	u.u_procp->p_flag &= ~SDLYU;

	splx(ps);
}
# endif