V9/sys/dev.old/ads.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 */
/* 5-3-86 fixed infamous vstodb-on-close bug */
/* 5-1-86 converted to work with BSD4.2 */
/* 11-25-85 fixed buffer initialization bug */
/* 9-5-85 simulated DSWAIT ioctl inside adsclose */
/* 4-22-85 added check for partial buffer in adscleanup */
/* 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  adsrelease checks for zombie state or even death */
/* adsclose() shuts down device */
/* DSWAIT added */
/* 5-25-84  process pointer saved in adsopen for adsintr() */
/*	vsunlock must then be duplicated using correct process structure */
/* the interrelation of adsstart-sleeping, adsintr, and adscleanup
	needs to be cleaned up 
	Probably adscleanup should not call wakeup */


#include "ads.h"
# if NADS > 0

/*
 * DSC System 200 driver
 * via DSC dma11.
 * Combined Eighth Edition & BSD 4.2 Unibus version
 */

/* NOTE: This requires BSD42 to be defined somehow, if BSD 4.2 is being used */

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/mount.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/buf.h"
#include "../h/map.h"
#include "../h/conf.h"
#include "../h/proc.h"
#include "../h/file.h"
#include "../h/vmmac.h"
#include "../h/ioctl.h"
/* Since "make depend" would otherwise get confused by the #ifdef'ed #includes,
     we fake it out, so it ignores all of these #include's */
#ifdef BSD42
# include "../h/uio.h"
# include "../h/kernel.h"
# include "../h/tsleep.h"
# include "../machine/pte.h"
# include "../vaxuba/ubareg.h"
# include "../vaxuba/ubavar.h"
# include "../vaxuba/adsreg.h"
# include "../vaxuba/adsvar.h"
#else
# include "../h/pte.h"
# include "../h/ubareg.h"
# include "../h/ubavar.h"
# include "../h/adsreg.h"
# include "../h/adsvar.h"
#endif

#ifdef BSD42
# define ERR(err)   return err
# define IOMOVE(buf,count,dir)	   uiomove(buf, count, dir, uio)
# define PHYSIO(strat,buf,dev,dir,minph) \
	       physio(strat, buf, dev, dir, minph, uio)
# define COUNT	    uio->uio_iov->iov_len
# define RESID	    uio->uio_resid
# define BASE	    uio->uio_iov->iov_base
# define IO_READ    UIO_READ
# define IO_WRITE   UIO_WRITE
# define UBA_WANTBDP	0
#else
# define ERR(err)   { u.u_error = (err);  return; }
# define IOMOVE(buf,count,dir)	   (iomove(buf, count, dir), u.u_error)
# define PHYSIO(strat,buf,dev,dir,minph) \
	       (physio(strat, buf, dev, dir, minph), u.u_error)
# define COUNT	    u.u_count
# define RESID	    u.u_count
# define BASE	    u.u_base
# define IO_READ    B_READ
# define IO_WRITE   B_WRITE
#endif

/*
 * 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_OPEN	bit(0)
# define DS_IDLE	bit(1)
# 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 ads_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[NADSB];	/* uba info */
	struct dspageinfo	c_pageinfo[NADSB];
	struct buf	c_dsb[NADSB];	/* 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 */
} ads_softc[NADS];

int adsprobe(), adsattach(), adsintr();

struct uba_device *adsdinfo[NADS];
struct uba_ctlr *adscinfo[NADS];  /* attach() sets adscinfo to point to ctlr struct */

u_short adsstd[] = {
	0164400, 0
};

struct uba_driver adsdriver = {
	adsprobe, 0, adsattach, 0, adsstd, "ads", adsdinfo, "ads", adscinfo
};
int	adsdebug;

/*
 * all this just to generate
 * an interrupt; rather
 * involved isn't it?
 */
adsprobe(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;
	}

	printf("dsc interrupt vector at 0%o\n", dsaddr->dmaiva & 0776);
	dsaddr->dmacsr = 0;
	dsaddr->dmacls = 0;
	dsaddr->ascseq[0] = DABASE+0 | (DA << 6) | LAST_SEQ;
	dsaddr->ascsrt = 0100;
	dsaddr->dmablr = ~1;
	dsaddr->dmacsr = DMA_IE;
	dsaddr->asccsr = ASC_RUN | ASC_IE;

	DELAY(40000);

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

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

	return sizeof(struct dsdevice);
}

adsattach(ui)
struct uba_device *ui;
{
	register int unit;
	register int i;

	unit = ui->ui_unit;

	ads_softc[unit].c_flags = DS_IDLE;
	for (i = 0; i < NADSB; i++)
		ads_softc[unit].c_dsb[i].b_flags = 0;
}

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

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

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

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

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

	if(adsdebug)printf("adsopen: unit %d, flag %x\n",unit,flag);
	if (dsaddr->dmacsr & DMA_OFL)
		ERR(ENXIO);

	/*
	 * 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))
				ERR(ENXIO);
		}
		adscleanup(unit);
		sc->c_flags = DS_IDLE;
		printf("ads%d: reset\n",unit);
		return 0;
	}

	/*
	 * 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:		ERR(ENXIO);

	/*
	 * initialize
	 */


	/* set defaults and initial conditions in ads_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 < NADSB; i++)
		sc->c_dsb[i].b_flags = 0;
	sc->c_ibp = sc->c_obp = &sc->c_dsb[0];
	sc->c_outbufno = sc->c_bufno = 0;

	if(adsdebug)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;

	return 0;
}

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

	unit = minor(dev) & ~RESETDEV;
	sc = &ads_softc[unit];
	ui = adsdinfo[unit];
	dsaddr = (struct dsdevice *)ui->ui_addr;
	if(adsdebug)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)sc, DSPRI, 5) != TS_OK)
			break;
	}

	adscleanup(unit);
	sc->c_flags &= ~DS_OPEN;
	u.u_procp->p_flag &= ~SPHYSIO;
	if(adsdebug){
		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]);
		adsdb(dsaddr);
	}
	return 0;
}

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

	if(adsdebug)printf("S");

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

	/*
	 * check user access rights to buffer
	 */
	if (useracc(BASE, COUNT, rw==B_READ?B_WRITE:B_READ) == NULL)
		ERR(EFAULT);

	if (sc->c_errs)
		ERR(EIO);


	/*
	 * 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 = BASE;
	count = COUNT;
	while(count > 0) {

		if(adsdebug)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(adsdebug)printf("s");
			ts = tsleep((caddr_t)sc, DSPRI, 0);
			if(adsdebug)printf("w");
#ifdef RESTRICT
			splx(ps);
#endif
			switch(ts) {
				case TS_SIG:
#ifndef RESTRICT
					splx(ps);
#endif
					ERR(EINTR);
				case TS_OK:
					if ((sc->c_dmacsr = dsaddr->dmacsr) & DMA_SFL) {
#ifndef RESTRICT
						splx(ps);
#endif
						printf("ads%d  still full after tsleep\n",unit);
						adscleanup(unit);
						ERR(EIO); 
					}
			/* This is not necessary */
					if (!(sc->c_flags & DS_OPEN)) {
#ifndef RESTRICT
						splx(ps);
#endif
						printf("ads: wakeup unopen\n");
						adscleanup(unit);
						ERR(EIO);
					}
					break;
				default:
#ifndef RESTRICT
					splx(ps);
#endif
					printf("ads: %x from tsleep\n",ts);
					adscleanup(unit);
					ERR(EIO);
			}
#ifdef RESTRICT
		} else
			splx(ps);
#else
		}

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

		ps = SPL_DS();

		if (sc->c_flags & DS_IDLE) {

			if(adsdebug) 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[NADSB-1])
			sc->c_ibp = &sc->c_dsb[0];

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

		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 % NADSB;

		sc->c_pageinfo[bufno].firstunit = (int)base / CLSIZE;
		sc->c_pageinfo[bufno].lastunit =
				(((int)base + c + CLOFSET) / CLSIZE) - 1;

		for (i = 0; i < NADSB; 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("ads: buf %d overlaps %d\n",i,bufno);
				ERR(EIO);
			}
		}

		/* lock the buffer and send it off */

		bp->b_flags = B_BUSY | B_PHYS  | rw;
		if(adsdebug)printf("lk%d %x %d ",bufno,base,c);
		adslock(base, 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(adsdebug)printf("LB\n");
	}
		
	RESID -= COUNT;	 /* Doesn't handle multiple iovecs */
	return 0;
}

adsdb(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);
}
/*
 * 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.
 *
 */
adsintr(dev)
dev_t dev;
{
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ads_softc *sc;
	register struct buf *bp;
	register int bufno;
	register int i;
	int unit, dummy;

	unit = minor(dev) & ~RESETDEV;
	sc = &ads_softc[unit];
	ui = adsdinfo[unit];

	dsaddr = (struct dsdevice *)ui->ui_addr;
	
	sc->c_dmacsr = dsaddr->dmacsr;
	dsaddr->dmacls = dummy = 0;

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

	if (sc->c_dmacsr & DMA_ERR) {
		if(adsdebug)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("ads%d error: asccsr=%b dmacsr=%b\n",
				unit, sc->c_asccsr, ASC_BITS,
				sc->c_dmacsr, DMA_BITS);
		}
		dsaddr->dmablr = ~0;
		adscleanup(unit);
		return;
	}
		
	/*
	 * get current buffer and release it
	 */

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

	sc->c_outbufno++;
	if(adsdebug)printf("b ");
	adsrelease(sc, ui, bp);

	/*
	 * update byte count.
	 */

	sc->c_nbytes += bp->b_bcount;

	wakeup((caddr_t)sc);
	if (adsdebug) printf(" upi ");
}

/*
 * release resources, if any, associated with a buffer
 */
adsrelease(sc, ui, bp)
register struct ads_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 % NADSB;
	if(adsdebug)printf("R%d ",bufno);
	/*
	 * release uba resources
	 * and memory resources if process seems healthy
	 */
	ps = SPL_DS();

	if (bp->b_flags & B_BUSY) {
		if(adsdebug) 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("\nDSC exit release 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_OPEN;
		}
		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(adsdebug)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
 */
adscleanup(unit)
int unit;
{
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ads_softc *sc;
	register struct buf *bp;
	int	dummy;

	unit = (unit & ~RESETDEV);
	sc = &ads_softc[unit];
	ui = adsdinfo[unit];
	dsaddr = (struct dsdevice *)ui->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(adsdebug)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[NADSB]); ++bp)
		adsrelease(sc, ui, bp);


	/* in case called from adsintr while adsstart is sleeping */

	wakeup((caddr_t)sc);
	if (adsdebug) printf(" upc ");
}

/*
 * a/d conversion
 */
adsread(dev, uio)
dev_t dev;
struct uio *uio;
{
	return adsstart(dev, B_READ, uio);	/* dsc => memory */
}

/*
 * d/a conversion
 */
adswrite(dev, uio)
dev_t dev;
struct uio *uio;
{
	return adsstart(dev, B_WRITE, uio);	/* memory => dsc */
}

/* ARGSUSED */
adsioctl(dev, cmd, addr, flag)
dev_t dev;
caddr_t addr;
{
	register struct dsdevice *dsaddr;
	register struct ads_softc *sc;
	register struct ds_seq *dq;
	register struct ds_err *de;
	register struct ds_trans *dt;
	struct uba_device *ui;
	int	unit, flts, i;
#ifdef BSD42
	int	*iarg;
#else
	int	iarg[2];
	struct ds_seq	ds_seq;
	struct ds_err	ds_err;
	struct ds_trans	ds_trans;
#endif

	if (minor(dev) & RESETDEV)
		ERR(EINVAL);
	unit = minor(dev);
	if (adsdebug) printf("IOC");

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

#ifdef BSD42
	iarg = (int *)addr;
#endif

	switch (cmd) {
		/* set sample rate */
		case DSRATE:
#ifndef BSD42
			if (copyin(addr, (caddr_t)iarg, sizeof(int)))
				ERR(EFAULT);
#endif
			if (iarg[0] == 0)
				ERR(EINVAL);
			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:
#ifndef BSD42
			if (copyin(addr, (caddr_t)iarg, sizeof(int)))
				ERR(EFAULT);
#endif
			sc->c_ascflags &= ~ ASC_HZMSK;
			sc->c_ascflags |= (iarg[0] << ASC_HZSHIFT) & ASC_HZMSK;
			break;

		/* fetch errors */
		case DSERRS:
#ifdef BSD42
			de = (struct ds_err *)addr;
#else
			de = &ds_err;
#endif
			de->dma_csr = sc->c_dmacsr;
			de->asc_csr = sc->c_asccsr;
			de->errors = sc->c_errs;
#ifndef BSD42
			if (copyout((caddr_t)de, addr, sizeof(ds_err)))
				ERR(EFAULT);
#endif
			break;

		/* fetch transition counts */
		case DSTRANS:
#ifdef BSD42
			dt = (struct ds_trans *)addr;
#else
			dt = &ds_trans;
#endif
			dt->to_idle = sc->c_to_idle;
			dt->to_active = sc->c_to_active;
#ifndef BSD42
			if (copyout((caddr_t)dt, addr, sizeof(ds_trans)))
				ERR(EFAULT);
#endif
			break;

		/* how many samples actually converted */
		case DSDONE:
#ifdef BSD42
			*(int *)addr = sc->c_nbytes;
#else
			if (copyout((caddr_t)&sc->c_nbytes, addr, sizeof(int)))
				ERR(EFAULT);
#endif
			break;

		case DSSTEREO:
			while (!sc->c_flags & DS_IDLE) {
				if (tsleep(sc, DSPRI, 0) == TS_SIG)
					ERR(EINTR);
			}
			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(sc, DSPRI, 0) == TS_SIG)
					ERR(EINTR);
			}
			if (sc->c_flags & DS_READ)
				dsseq(sc, 0, ADBASE | LAST_SEQ, AD);
			else
				dsseq(sc, 0, DABASE | LAST_SEQ, DA);
			break;

		case DSRESET:
			adscleanup(unit);	/* not adequate (?) */
			printf("adsreset\n");
			break;
		case DSDEBUG:
 			adsdebug = !adsdebug;
			break;

		case DSWAIT:
			if (adsdebug) printf("W%d %d ", sc->c_outbufno, sc->c_bufno);
			while (sc->c_outbufno != sc->c_bufno
					&& !(sc->c_flags & DS_IDLE)) {
				if (tsleep(sc, DSPRI, 0) == TS_SIG) {
					adscleanup(unit);
					ERR(EINTR);
				}
			}
			break;

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

		default:
			ERR(ENOTTY);
	}

	return 0;
}


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

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

		printf(" ads%d", ds);

		/*
		 * release unibus resources
		 */
		ads_softc[ds].c_flags = DS_IDLE;
		adscleanup(ds);
	}
}
/*
 equivalent to vslock() from vmmem.c
  inserted differently here for debugging convenience only.
 */
adslock(base, count)
	caddr_t base;
{
#if 0
	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(adsdebug)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);
#else
	if (adsdebug) printf("Lk %x %d ",
				vtopte(u.u_procp, btop(base))->pg_pfnum,
				btoc(count + ((int)base & CLOFSET)));
	vslock(base, count);
#endif
}
# endif