2.11BSD/sys/OTHERS/dig_anal/ds.c

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

/* %M%	%I%	(CARL)	%G%	%U% */
static char RCSid[] = "$Header: ds.c,v 1.8 84/10/03 04:44:51 lepreau Exp $";

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

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

/*
 * *=*=*=* BUGS *=*=*=*
 *
 * 1. if you #define ZEROBUF it has unpleasant
 *    side effects: it crashes the vax.
 */

/*
 * !!! WARNING !!! WARNING !!! WARNING !!!
 *
 * since the disk driver strategy routine
 * is called from the converters' interrupt
 * level the processor level must not be set
 * to 0 in the disk driver. also where the
 * processor level is raised before fiddling
 * with the buffer list it must not be "raised"
 * to lower than the dma11's interrupt level.
 * change the code in the strategy routine to
 * look something like this:
 *
 * (* the following #define causes data lates and was taken out *)
 * (* # define spl5 spl6	(* converters run at level 6 *)
 *
 *	opl = spl5();		(* CHANGED *)
 *	dp = &xxutab[ui->ui_unit];
 *	disksort(dp, bp);
 *	if (dp->b_active == 0) {
 *		xxustart(ui);
 *		bp = &ui->ui_mi->um_tab;
 *		if ((bp->b_actf != NULL) && (bp->b_active == 0))
 *			xxstart(ui->ui_mi);
 *	}
 *	(void) splx(opl);	(* CHANGED *)
 *
 * i suspect that you should also bracket the entire disk
 * interrupt routine with "opl = spl6() ... splx(opl)" but
 * we get data lates when this is done. the next paragraph
 * should be read with this in mind.
 *
 * the other alternative is to fix your dma11 so that it
 * interrupts at level 5 instead of level 6. dsc can give
 * you the information on how to do this. in either event
 * the strategy routine must be fixed as shown above.
 */

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/mount.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../machine/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/kernel.h"
#include "../vaxuba/ubavar.h"
#include "../h/conf.h"
#include "../h/proc.h"
#include "../h/uio.h"
#include "../h/file.h"

#include "../vaxuba/dsc.h"

#include "../vaxuba/dsreg.h"

/*
 * THESE ARE SITE-SPECIFIC
 *
 * bulk storage devices
 *
 * these can be tape, disk, bubble
 * or whatever.
 */

#include "up.h"
#include "hp.h"
#include "tu.h"
#include "te.h"
#include "ra.h"

#if NSC > 0
extern int upstrategy();
#endif NSC

#if NRA > 0
extern int udstrategy();
#endif NRA

#if NHT > 0
extern int htstrategy();
#endif NHT

#if NHP > 0
extern int hpstrategy();
#endif NHP

#if NTE > 0
extern int tmstrategy();
#endif NTE

struct bs {
	dev_t	d_cdev;		/* chrdev; major+minor */
	dev_t	d_bdev;		/* blkdev; major+minor */
	int	d_flags;	/* buffer flags */
	int	(*d_io)();	/* strategy routine */
} bs[] = {

#if NHP > 0
	/* rhp2b */		/* hp2b */
	{ makedev(4, 17),	makedev(0, 17),	0,	hpstrategy },
#endif NHP

#if NTE > 0
	/* rmt8 */		/* mt8 */
	{ makedev(14,8),	makedev(5,8),	B_TAPE,	tmstrategy }
#endif NTE

#ifdef NOTDEFED
#if NUP > 0
	/* rup0c */		/* up0c */
	{ makedev(13, 2),	makedev(2, 2),	0,	upstrategy },
	/* rup1f */		/* up1f */
	{ makedev(13, 13),	makedev(2, 13),	0,	upstrategy },
#endif NUP

#if NRA > 0
	/* rra0f */		/* ra0f */
	{ makedev(9, 5),	makedev(9, 5),	0,	udstrategy },
	/* rra1f */		/* ra1f */
	{ makedev(9, 13),	makedev(9, 13),	0,	udstrategy },
	/* rra2f */		/* ra1f */
	{ makedev(9, 21),	makedev(9, 21),	0,	udstrategy },
#endif NRA

#if NHT > 0
	/* rmt8 */		/* mt8 */
	{ makedev(14, 8),	makedev(5, 8),	B_TAPE,	htstrategy },
#endif NHT
#endif NOTDEFED

};

# define NBS		(sizeof(bs) / sizeof(bs[0]))

/*
 * THESE ARE SITE-SPECIFIC
 *
 * starting base for the
 * d/a and a/d converters,
 * for setting up sequence ram.
 */
# define ADBASE		040
# define DABASE		050

/*
 * reset device
 */
# define RESETDEV	bit(4)

/*
 * used to be a macro
 *
 * # define dsblock(sf) \
 * ((((sf)->f_todo - (sf)->f_dcnt) / 512) + (sf)->f_bno)
 */
extern daddr_t	dsblock();

# define size(sf, cnt) \
((cnt > (sf)->f_bsize) ? (sf)->f_bsize : cnt)
# define dsseq(ui, reg, conv, dir) \
((struct dsdevice *) (ui)->ui_addr)->ascseq[reg] = conv | ((dir & 01) << 6)

/*
 * ds flags
 */
# define DS_CLOSED	0
# define DS_OPEN	bit(0)
# define DS_BSY		bit(1)
# define DS_NDSK	bit(2)
# define DS_MON		bit(3)
# define DS_BRD		bit(4)

/*
 * params to driver
 */
# define A_TODO		bit(0)
# define A_BNO		bit(1)
# define A_CNT		bit(2)
# define A_SEQ		bit(3)
# define A_DEV		bit(4)
# define A_NBLKS	bit(5)
# define A_BLIST	bit(6)

# define MINARG 	(A_BNO | A_CNT | A_SEQ | A_DEV)
# define DSPRI		(PZERO-1)

/* size of the buffer holding the block list */
# define LISTSIZE	MAXBSIZE

/* number of disk addresses per buffer */
# define NDADDRS	(LISTSIZE / sizeof(daddr_t))

/*
 * relevant information
 * about the dma and asc
 */
struct ds_softc {
	int		c_dmacsr;	/* copy of dma csr on error */
	int		c_asccsr;	/* copy of asc csr on error */
	int		c_flags;	/* internal flags */
	int		c_errs;		/* errors, returned via ioctl */
	int		c_bufno;	/* dsubinfo/buffer */
	int		c_uid;		/* user id */
	int		c_args;		/* args received from user */
	int		c_wticks;	/* watch dog */
	int		c_ubinfo[NDSB];	/* uba info */
	int		c_bubinfo;	/* uba info, 1st buffer, once only */
	int		c_nblist;	/* length of block list */
	int		c_blkno;	/* current block in blist */
	int		c_mode;		/* mode opened for */
	struct buf	c_dsb[NDSB];	/* bs buffers */
	struct buf	c_blist;	/* block list */
	struct buf	*c_curb;	/* current buffer in block list */
} ds_softc[NDS];

/*
 * relevant information
 * about the disking and
 * other truck
 */
struct ds_softf {
	daddr_t	f_bno;			/* starting block */
	off_t	f_todo;			/* amnt. of file to convert */
	off_t	f_ccnt;			/* amnt. of data not converted */
	off_t	f_dcnt;			/* amnt. of file not diskio'd */
	off_t	f_icnt;			/* amnt. of file not seen by dsintr */
	int	f_bsize;		/* size of each buffer */
	int	f_boff;			/* offset into buffer of first i/o */
	int	f_dev;			/* bs device to use */
} ds_softf[NDS];

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

struct uba_device *dsdinfo[NDS];

u_short dsstd[] = {
	0165400, 0
};

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

/* ARGSUSED */
dsopen(dev, mode)
	dev_t dev;
{
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register struct ds_softf *sf;
	register int unit;

	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];
	sf = &ds_softf[ui->ui_unit];

	/*
	 * 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)) {
				return(ENXIO);
			}
		}

		if (dsinit(unit)) {
			uprintf("ds%d: asc offline\n", ui->ui_unit);
			return(EIO);
		}

		return(0);
	}

	/*
	 * only one person can use it
	 * at a time
	 */
	if (sc->c_flags & DS_OPEN)
bad:		return(ENXIO);

	sc->c_mode = mode;

	/*
	 * initialize
	 */
	if (dsinit(unit)) {
		uprintf("ds%d: asc offline\n", ui->ui_unit);
		return(EIO);
	}

	sc->c_uid = u.u_ruid;

	sc->c_flags = DS_OPEN;

	return(0);
}

/* ARGSUSED */
dsclose(dev, flag) {
	register int unit;

	unit = minor(dev) & ~RESETDEV;
	ds_softc[unit].c_flags = DS_CLOSED;
	(void) dsinit(unit);

	return(0);
}

dsinit(unit) {
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register struct ds_softf *sf;
	register short dummy;
	register int offl;
	int flts;
	int odd;

	if (unit >= NDS)
		return(1);

	if ((ui = dsdinfo[unit]) == NULL)
		return(1);

	dsaddr = (struct dsdevice *) ui->ui_addr;

	if (dsaddr->dmacsr & DMA_OFL)
		offl = 1;
	else {
		flts = dsaddr->asccsr & ASC_HZMSK;	/* save filters */
		dsaddr->asccsr = flts;
		dsaddr->ascrst = 0;
		offl = 0;
	}

	sc = &ds_softc[ui->ui_unit];
	sf = &ds_softf[ui->ui_unit];

	odd = 0;

	/*
	 * flush out last remaining buffer
	 */
	if ((sc->c_mode & FREAD)	&&
	   (sf->f_dcnt > 0)		&&
	   (sc->c_flags & DS_BSY)	&&
	   (! (sc->c_flags & DS_NDSK)))	{
		register struct buf *bp;

		bp = &sc->c_dsb[sc->c_bufno % NDSB];

		/* BEGIN DEBUG */
		if (sf->f_bsize == 0) {
			uprintf("dsinit: zero bsize\n");
			sc->c_errs |= EDS_CERR;
			goto out;
		}
		/* END DEBUG */

		/* sometimes dmawc is odd? */
		bp->b_bcount = ~ dsaddr->dmawc;
		if ((bp->b_bcount % sizeof(short)) != 0)
			odd = bp->b_bcount;
		bp->b_bcount -= bp->b_bcount % sizeof(short);

		bp->b_blkno = dsblock(sf, sc);
		bp->b_flags &= ~ (B_DONE | B_ERROR);
		(*bs[sf->f_dev].d_io)(bp);

		sf->f_dcnt -= sf->f_bsize;

		out:;
	}

	dsfreeall(&sc->c_blist);

	dsaddr->dmablr = 0;
	dsaddr->dmasax = 0;
	dsaddr->dmasar = 0;
	dsaddr->dmacsr = 0;
	dsaddr->dmawc = 0;
	dsaddr->dmaacx = 0;
	dsaddr->dmaac = 0;
	dsaddr->dmadr = 0;
	dsaddr->dmaiva = 0;
	dsaddr->dmaclr = 0;
	dsaddr->dmacls = 0;
	dummy = dsaddr->dmasar;			/* clears sar flag */

#ifdef notdef
	/*
	 * put converters in monitor mode
	 */
	if (offl == 0) {
		dsseq(ui, 0, ADBASE+0, AD);
		dsseq(ui, 1, ADBASE+1, AD);
		dsaddr->ascseq[1] |= bit(7);	/* last sequence reg */
		flts = dsaddr->asccsr & ASC_HZMSK;	/* save filters */
		dsaddr->asccsr = flts | ASC_RUN | ASC_MON;
	}
#endif notdef

	/* reset ds_softc */
	sc->c_dmacsr = 0;
	sc->c_asccsr = 0;
	sc->c_flags = 0;
	sc->c_errs = 0;
	sc->c_bufno = 0;
	sc->c_args = 0;
	sc->c_blkno = 0;
	sc->c_mode = 0;

	/* reset ds_softf */
	sf = &ds_softf[ui->ui_unit];
	sf->f_bno = 0;
	sf->f_todo = 0;
	sf->f_ccnt = 0;
	sf->f_dcnt = 0;
	sf->f_icnt = 0;
	sf->f_bsize = 0;
	sf->f_boff = 0;
	sf->f_dev = 0;

	/*
	 * terminate current run
	 */
	sc->c_flags &= ~ DS_BSY;
	wakeup((caddr_t) &ds_softc[ui->ui_unit]);
	if (sc->c_flags & DS_BSY)
		sc->c_errs |= EDS_RST;

	if (odd)
		printf("ds%d: dsinit: odd dmawc (%d)\n", ui->ui_unit, odd);

	return(offl);
}

/*
 * f_bsize is the size of the buffers used for the i/o. the buffers are
 * obtained by taking the user buffer and splitting it into NDSB buffers.
 * the user buffer should be a multiple of the track size of the disk.
 * this is for efficiency of disk i/o.
 *
 * using uio_resid, iov_base, and f_bsize each buffer header is set up.
 * the converters only need the base address of the buffer and the word
 * count. the disk needs these in addition to the block number. if we
 * are doing d/a conversions then we start the disk up to fill up the
 * buffers.
 *
 * after everything is ready to go we turn on the RUN bit and let 'em rip.
 */
dsstart(unit, rw, uio)
	struct uio *uio;
{
	register struct dsdevice *dsaddr;
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register struct ds_softf *sf;
	register struct iovec *iov;
	register struct buf *bp;
	int dummy;
	int bits;
	int blr;
	int opl;
	int i;

	sc = &ds_softc[unit];
	sf = &ds_softf[unit];

	iov = uio->uio_iov;

	sf->f_bsize = (iov->iov_len / NDSB);
/* begin debug */
	if (sf->f_bsize == 0) {
		uprintf("dsstart: zero bsize (1st check)\n");
		goto bad;
	}
/* end debug */

	/*
	 * make sure we have all
	 * necessary info.
	 */
	if ((sc->c_args & MINARG) != MINARG) {
		sc->c_errs |= EDS_ARGS;
		goto bad;
	}

	/*
	 * either the converters will be writing
	 * to memory or the disk will be.
	 */
	if (useracc(iov->iov_base, iov->iov_len, B_WRITE) == NULL) {
		sc->c_errs |= EDS_ACC;
		return(EFAULT);
	}

	/*
	 * check for misaligned buffer.
	 * 
	 * the "% DEV_BSIZE" check isn't correct. it doesn't hurt
	 * though. it probably should be deleted.
	 */
	if (((iov->iov_len % DEV_BSIZE) != 0) || (iov->iov_len & 01)) {
		sc->c_errs |= EDS_MOD;
		goto bad;
	}

	/*
	 * check for tiny buffer
	 */
	if (sf->f_bsize < DEV_BSIZE) {
		sc->c_errs |= EDS_SIZE;
		goto bad;
	}

	/*
	 * check for unreasonable buffer
	 * offset for first buffer
	 */
	if (sf->f_boff >= sf->f_bsize) {
		sc->c_errs |= EDS_ARGS;
		goto bad;
	}

	u.u_procp->p_flag |= SPHYSIO;

	sc->c_flags |= DS_BSY;

	vslock(iov->iov_base, (int) iov->iov_len);	/* fasten seat belts */

	/*
	 * point each c_dsb somewhere into
	 * the user's buffer, set up each c_dsb.
	 * the buffer's rw flag is set to the
	 * inverse of our operation since it
	 * applies to the bulk storage device
	 * i/o to be done.
	 */
	for (i = 0; i < NDSB; i++) {
		bp = &sc->c_dsb[i];
		bp->b_un.b_addr = iov->iov_base + (i * sf->f_bsize);
		bp->b_error = 0;
		bp->b_proc = u.u_procp;
		bp->b_flags = bs[sf->f_dev].d_flags | B_PHYS | ((rw == B_READ) ? B_WRITE : B_READ);
		bp->b_dev = bs[sf->f_dev].d_cdev;
		bp->b_bcount = sf->f_bsize;
		bp->b_flags |= B_BUSY;
		if (rw == B_WRITE) {
			if ((sc->c_flags & DS_NDSK) == 0) {
/* begin debug */
				if (sf->f_bsize == 0) {
					uprintf("dsstart: zero bsize");
					uprintf(" i=%d, unit=%d\n", i, unit);

					/* for printf in wait loop below */
					ui = dsdinfo[unit];
					sc->c_errs |= EDS_ARGS;
					goto out;
				}
/* end debug */
				bp->b_blkno = dsblock(sf, sc);
				sc->c_blkno++;

/* uprintf("dsstart: blk=%ld, count=%d\n", bp->b_blkno, bp->b_bcount); */
				(*bs[sf->f_dev].d_io)(bp);

				/* iodone will wake us up */
				while ((bp->b_flags & B_DONE) == 0)
					sleep((caddr_t) bp, DSPRI);

				/* oops */
				if (bp->b_flags & B_ERROR) {
/* printf("dsstart: disk error (block=%ld)\n", bp->b_blkno); */
					sc->c_errs |= EDS_DISK;
					goto out;
				}
			}
			else
				bp->b_flags |= B_DONE;

			sf->f_dcnt -= sf->f_bsize;
		}
		else
			bp->b_flags |= B_DONE;
	}

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

	opl = spl6();
	/*
	 * if reading then going to
	 * memory so set W2M
	 */
	if (rw == B_WRITE)
		dsaddr->asccsr |= ASC_PLAY;
	else {
		dsaddr->asccsr |= ASC_RECORD;
		dsaddr->dmacsr |= DMA_W2M;
	}

	dsaddr->dmacsr |= DMA_CHN | DMA_SFL;
	dummy = dsaddr->dmasar;
	dsaddr->asccsr &= ~ ASC_MON;
	dummy = dsaddr->ascrst;
 	dsaddr->asccsr |= ASC_IE;
	dsaddr->dmacls = 0;
	dsaddr->dmacsr |= DMA_IE;
	dsaddr->dmawc = -1;

	/*
	 * if there is a buffer offset
	 * then adjust b_addr temporarily.
	 * this fudging around is done
	 * unconditionally since f_boff
	 * is zeroed in dsopen so if the user
	 * hasn't specified a boff then this
	 * shouldn't affect b_addr.
	 */
	if ((blr = size(sf, sf->f_ccnt) - sf->f_boff) <= 0) {
		sc->c_errs |= EDS_ARGS;
		goto bad;
	}
	sc->c_ubinfo[0] = ubasetup(ui->ui_ubanum, &sc->c_dsb[0], UBA_NEEDBDP);
	sc->c_bubinfo = sc->c_ubinfo[0] + sf->f_boff;
	dsaddr->dmablr = ~ (blr >> 1);
	dsaddr->dmasax = (sc->c_bubinfo >> 16) & 03;
	dsaddr->dmasar = sc->c_bubinfo;
	sf->f_ccnt -= blr;
#ifdef debug
	if (sf->f_ccnt <= 0)
		uprintf("dsstart: dsb0: f_ccnt=%d\n", sf->f_ccnt);
#endif debug

	sc->c_ubinfo[1] = ubasetup(ui->ui_ubanum, &sc->c_dsb[1], UBA_NEEDBDP);

	/*
	 * set monitor mode or
	 * broadcast mode.
	 */
	bits = 0;
	if (sc->c_flags & DS_MON)
		bits |= ASC_MON;
	if (sc->c_flags & DS_BRD)
		bits |= ASC_BRD;

	/*
	 * as they say in california,
	 * ``go for it''
	 */
	dsaddr->asccsr |= ASC_RUN | bits;

	/*
	 * if the file is so small and only uses the first
	 * buffer we would load dmablr with 0 but we can't
	 * do that because we get errors (data late, external
	 * interrupt), so we fudge and give it f_bsize. dsintr
	 * will shut down the converters before it tries to play
	 * the 2nd (bogus) block.
	 */
	if ((blr = size(sf, sf->f_ccnt)) <= 0) {
#ifdef debug
		uprintf("dsstart: dsb1: blr=%d\n", blr);
#endif debug
		blr = sf->f_bsize;
	}

	dsaddr->dmablr = ~ (blr >> 1);
	dsaddr->dmasax = (sc->c_ubinfo[1] >> 16) & 03;
	dsaddr->dmasar = sc->c_ubinfo[1];
	sf->f_ccnt -= blr;
#ifdef debug
	if (sf->f_ccnt <= 0)
		uprintf("dsstart: dsb1: f_ccnt=%d\n", sf->f_ccnt);
#endif debug
	splx(opl);

	/*
	 * wait for interrupt routine to signal
	 * end of conversions.
	 */
	while (sc->c_flags & DS_BSY)
		sleep((caddr_t) &ds_softc[ui->ui_unit], DSPRI);

	/*
	 * wait for disk i/o to complete.
	 * release unibus resources.
	 */
out:	for (i = 0; i < NDSB; i++) {
		if ((sc->c_dsb[i].b_flags & B_DONE) == 0) {
			int waittime;
			for (waittime = 0; waittime < 7; waittime++) {
				sleep((caddr_t) &lbolt, DSPRI);
				if (sc->c_dsb[i].b_flags & B_DONE)
					goto done;
			}

			printf("ds%d: dsb%d: wait timeout\n", ui->ui_unit, i);
		}

done:		if (sc->c_ubinfo[i] != 0) {
			ubarelse(ui->ui_ubanum, &sc->c_ubinfo[i]);
			sc->c_ubinfo[i] = 0;
		}
	}

	/*
	 * either the disk or the
	 * converters were writing
	 * to memory, thus the B_READ.
	 */
	vsunlock(iov->iov_base, (int) iov->iov_len, B_READ);

	u.u_procp->p_flag &= ~ SPHYSIO;

	if (sc->c_errs)
bad:		return(EIO);

	return(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. also you can't do a "read-modify-write" (I think that this is called
 * a DATIP followed by a DATO in DEC manuals) on any of the ds registers
 * while the converters are running.
 *
 * note that you must write something to the dma11 clear status register
 * or you'll never get any more interrupts.
 *
 * c_wticks gets cleared on each interrupt. dswatch() increments
 * c_wticks and if c_wticks gets over a threshold then the
 * addacs are assumed to have hung and we do a reset.
 */
dsintr(dev)
	dev_t dev;
{
	register struct dsdevice *dsaddr;
	register struct ds_softc *sc;
	register struct ds_softf *sf;
	register struct buf *bp;
	register int bufno;
	register int i;
	int unit;
	int blr;

	unit = minor(dev) & ~RESETDEV;
	sc = &ds_softc[unit];
	sf = &ds_softf[unit];

	/*
	 * reset c_wticks
	 */
	sc->c_wticks = 0;

	dsaddr = (struct dsdevice *) dsdinfo[unit]->ui_addr;

	sc->c_asccsr = dsaddr->asccsr;
	sc->c_dmacsr = dsaddr->dmacsr;

	/*
	 * get current buffer
	 */
	bufno = sc->c_bufno % NDSB;
	bp = &sc->c_dsb[bufno];

	/*
	 * check to see if any disking
	 * needs to be done
	 */
	if (sf->f_dcnt > 0) {
		if ((bp->b_flags & B_DONE) == 0) {
/* printf("dsintr: block not done\n"); */
			sc->c_errs |= EDS_DISK;
			goto out;
		}

		if ((sc->c_flags & DS_NDSK) == 0) {
/* begin debug */
			if (sf->f_bsize == 0) {
				uprintf("dsintr: zero bsize\n");
				sc->c_errs |= EDS_CERR;
				goto out;
			}
/* end debug */

			bp->b_blkno = dsblock(sf, sc);
			sc->c_blkno++;

			bp->b_flags &= ~ (B_DONE | B_ERROR);
/* uprintf("dsintr: blk=%d, count=%d\n", bp->b_blkno, bp->b_bcount); */
			(*bs[sf->f_dev].d_io)(bp);
		}
		else
			bp->b_flags |= B_DONE;

		sf->f_dcnt -= sf->f_bsize;
	}

	/*
	 * check to see if converting
	 * is finished
	 */
	if ((sf->f_icnt -= sf->f_bsize) <= 0)
		goto out;

	/*
	 * check for converter error.
	 * all errors and normal termination
	 * come here.
	 */
	if ((sc->c_dmacsr & (DMA_ERR | DMA_BSY)) != DMA_BSY) {
		int flts;
		sc->c_errs |= EDS_CERR;
		printf("ds%d error: asccsr=%b, dmacsr=%b\n", unit, sc->c_asccsr, ASC_BITS, sc->c_dmacsr, DMA_BITS);

out:		sc->c_flags &= ~ DS_BSY;
		flts = dsaddr->asccsr & ASC_HZMSK;	/* save filters */
		dsaddr->asccsr = flts;			/* clears run */
		dsaddr->dmacsr = 0;			/* turn off dma11 */
		wakeup((caddr_t) &ds_softc[unit]);
		return(0);
	}

	/*
	 * reinitialize dma11
	 */
	dsaddr->dmacls = 0;

	/*
	 * load dma11 registers
	 */
	blr = size(sf, sf->f_ccnt);
	if (sf->f_ccnt > 0) {
		dsaddr->dmablr = ~ (blr >> 1);
		dsaddr->dmasax = (sc->c_ubinfo[bufno] >> 16) & 03;
		dsaddr->dmasar = sc->c_ubinfo[bufno];
	}

	/*
	 * try to catch disk errors
	 */
	for (i = 0; i < NDSB; i++) {
		if (sc->c_dsb[i].b_flags & B_ERROR) {
/* printf("dsintr: disk error (block=%ld)\n", sc->c_dsb[i].b_blkno); */
			sc->c_errs |= EDS_DISK;
			goto out;
		}
	}

	/*
	 * update converted byte count.
	 * update buffers converted count.
	 */
	sf->f_ccnt -= blr;
	sc->c_bufno++;

#ifdef ZEROBUF
	/*
	 * last buffer is full of
	 * zeroes
	 */
	if (sf->f_ccnt <= sf->f_bsize) {
		bufno = sc->c_bufno % NDSB;
		dsbclr(sc->c_dsb[bufno].b_un.b_addr, sc->c_dsb[bufno].b_bcount);
	}
#endif ZEROBUF

	return(0);
}

/*
 * last time i checked, this
 * didn't work
 */
dsbclr(base, bcnt)
	caddr_t base;
	unsigned int bcnt;
{
	/* debugging stuff */
	if (! kernacc(base, bcnt, B_READ)) {
		uprintf("can't dsbclr\n");
		return;
	}
	;				/* Avoid asm() label botch */
	/* end debugging stuff */

	{ asm("	movc5	$0,(sp),$0,8(ap),*4(ap)"); }
}

/*
 * a/d conversion
 */
dsread(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	int unit;

	unit = minor(dev) & ~RESETDEV;
	return(dsstart(unit, B_READ, uio));	/* writes on disk */
}

/*
 * d/a conversion
 */
dswrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	int unit;

	unit = minor(dev) & ~RESETDEV;
	return(dsstart(unit, B_WRITE, uio));	/* 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_softf *sf;
	register struct ds_seq *dq;
	register struct ds_err *de;
	register struct ds_fs *df;
	register struct bs *bsp;
	struct uba_device *ui;
	struct ds_seq ds_seq;
	struct ds_err ds_err;
	struct ds_fs ds_fs;
	int unit;
	int flts;
	int i;

	unit = minor(dev) & ~RESETDEV;

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

	switch (cmd) {
		/* put in broadcast mode */
		case DSBRD:
			if (! (sc->c_args & A_SEQ)) {
				sc->c_errs |= EDS_ARGS;
				return(EINVAL);
			}

			sc->c_flags |= DS_BRD;
			break;

		/* put in monitor mode */
		case DSMON:
			if (! (sc->c_args & A_SEQ)) {
				sc->c_errs |= EDS_ARGS;
				return(EINVAL);
			}

			sc->c_flags |= DS_MON;

			break;

		/* set sequence */
		case DSSEQ:
			dq = &ds_seq;
			bcopy(addr, (caddr_t) dq, sizeof(struct ds_seq));
			if ((dq->reg > 15) || (dq->reg < 0))
				return(EINVAL);

			dsseq(ui, (int) dq->reg, (int) dq->conv, (int) dq->dirt);
			break;

		/* mark last sequence register */
		case DSLAST:
			dq = &ds_seq;
			bcopy(addr, (caddr_t) dq, sizeof(struct ds_seq));
			if ((dq->reg > 15) || (dq->reg < 0))
				return(EINVAL);

			dsaddr->ascseq[dq->reg] |= bit(7);
			sc->c_args |= A_SEQ;
			break;

		/* starting block number */
		case DSBNO:
			df = &ds_fs;
			bcopy(addr, (caddr_t) df, sizeof(struct ds_fs));
			if (df->bnosiz < 0)
				return(EINVAL);

			sf->f_bno = df->bnosiz;
			sc->c_args |= A_BNO;
			break;

		/* no. of bytes to convert */
		case DSCOUNT:
			df = &ds_fs;
			bcopy(addr, (caddr_t) df, sizeof(struct ds_fs));

			/*
			 * DSCOUNT is in bytes so
			 * must be modulo sizeof(short)
			 */
			if ((df->bnosiz & 01) || (df->bnosiz <= 0))
				return(EINVAL);

			sf->f_todo = sf->f_dcnt = sf->f_ccnt = sf->f_icnt = df->bnosiz;
			sc->c_args |= A_CNT;
			break;

		/* bs device to use */
		case DSDEV:
			dq = &ds_seq;
			bcopy(addr, (caddr_t) dq, sizeof(struct ds_seq));

#ifdef MAJMIN
			for (bsp = &bs[0]; bsp < &bs[NBS]; bsp++) {
				if (dq->dirt == bsp->d_cdev)
					break;
			}

			if (bsp == &bs[NBS])
				return(EINVAL);

			if (bsdevchk(dq->dirt) == -1)
				return(EBUSY);
#else MAJMIN
			if ((dq->dirt >= NBS) || (dq->dirt < 0))
				return(EINVAL);

			if (bsdevchk(bs[dq->dirt].d_bdev) == -1)
				return(EBUSY);
#endif MAJMIN

			sf->f_dev = dq->dirt;
			sc->c_args |= A_DEV;
			break;

		/* set sample rate */
		case DSRATE:
			dq = &ds_seq;
			bcopy(addr, (caddr_t) dq, sizeof(struct ds_seq));
			dsaddr->ascsrt = dq->dirt;
			break;

		case DS20KHZ:
			dsaddr->asccsr &= ~ ASC_HZMSK;
			dsaddr->asccsr |= ASC_HZ20;	/* set 20kHz filter */
			break;

		case DS10KHZ:
			dsaddr->asccsr &= ~ ASC_HZMSK;
			dsaddr->asccsr |= ASC_HZ10;	/* set 10kHz filter */
			break;

		case DS5KHZ:
			dsaddr->asccsr &= ~ ASC_HZMSK;
			dsaddr->asccsr |= ASC_HZ05;	/* set 5kHz filter */
			break;

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

		/* no bs device i/o */
		case DSNODSK:
			sc->c_flags |= DS_NDSK;
			sc->c_args |= A_DEV | A_BNO;
			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;
			bcopy((caddr_t) de, addr, sizeof(struct ds_err));
			break;

		/* byte offset into 1st buffer */
		case DSBOFF:
			df = &ds_fs;
			bcopy(addr, (caddr_t) df, sizeof(struct ds_fs));

			/*
			 * DSBOFF is in bytes so
			 * must be modulo sizeof(short)
			 */
			if ((df->bnosiz & 01) || (df->bnosiz < 0))
				return(EINVAL);

			sf->f_boff = df->bnosiz;
			break;

		/* how many samples actually converted */
		case DSDONE:
			df = &ds_fs;
			df->bnosiz = sf->f_todo - sf->f_icnt;
			df->bnosiz += ~ dsaddr->dmawc;
			bcopy((caddr_t) df, addr, sizeof(struct ds_fs));
			break;

		case DSNBLKS:
			df = &ds_fs;
			bcopy(addr, (caddr_t) df, sizeof(struct ds_fs));

			if (df->bnosiz < 1)
				return(EINVAL);

			sc->c_nblist = df->bnosiz;
			sc->c_args |= A_NBLKS;
			break;

		case DSBLKS:
			if ((sc->c_args & A_NBLKS) == 0) {
				sc->c_errs |= EDS_ARGS;
				return(EINVAL);
			}

			for (i = sc->c_nblist * sizeof(daddr_t); i > 0; i -= LISTSIZE) {
				struct buf	*bp;
				unsigned int	amt;

				bp = geteblk(LISTSIZE);
				clrbuf(bp);

				amt = i > LISTSIZE ? LISTSIZE : i;

				if (copyin(*(caddr_t *)addr, bp->b_un.b_addr, amt)) {
					dsfreeall(&sc->c_blist);
					return(EFAULT);
				}

				*(caddr_t *)addr += amt;

				dslink(&sc->c_blist, bp);
			}
			sc->c_curb = sc->c_blist.av_forw;

			sc->c_args |= A_BNO | A_BLIST;
			break;

		default:
			return(ENOTTY);
			break;
	}

	return(0);
}

/*
 * link a buffer onto the buffer list
 * of blocks
 */
dslink(dp, bp)
	register struct buf	*dp, *bp;
{
	dp->av_back->av_forw = bp;
	bp->av_back = dp->av_back;
	dp->av_back = bp;
	bp->av_forw = dp;
}

/*
 * return all of the buffers to
 * the system
 */
dsfreeall(dp)
	register struct buf	*dp;
{
	register struct buf	*bp;

	for (bp = dp->av_forw; bp != dp; bp = dp->av_forw) {
		bp->av_back->av_forw = bp->av_forw;
		bp->av_forw->av_back = bp->av_back;
		brelse(bp);
	}
}

daddr_t
dsblock(sf, sc)
	register struct ds_softf	*sf;
	register struct ds_softc	*sc;
{
	daddr_t				blkno;
	daddr_t				*daddrs;
	register struct buf		*bp;

	if (sc->c_args & A_BLIST) {
		/* circular block list */
		blkno = sc->c_blkno % sc->c_nblist;

		/*
		 * if we're at the end of this buffer then
		 * move on to the next one.
		 */
		if ((blkno % NDADDRS) == 0) {
			/* special case for first time only */
			if (sc->c_bufno != 0)
				sc->c_curb = sc->c_curb->av_forw;
		}

		/*
		 * convert the buffer data from an array of
		 * bytes to an array of longs, then index
		 * into that to get the block.
		 */
		daddrs = (daddr_t *) sc->c_curb->b_un.b_addr;
		blkno = daddrs[blkno % NDADDRS];
	}
	else
		blkno = ((sf->f_todo - sf->f_dcnt) / 512) + sf->f_bno;

	return(blkno);
}

/*
 * user level entry to dsinit. check that
 * unit is valid and call dsinit. this is
 * used by user programs via the dsinit
 * system call to reset the dacs.
 */
dsuinit() {
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register int unit;
	register struct a {
		int dsunit;
	} *uap;

	uap = (struct a *) u.u_ap;

	if ((unit = uap->dsunit) >= NDS)
		return(ENXIO);

	if ((ui = dsdinfo[unit]) == NULL)
		return(ENXIO);

	sc = &ds_softc[ui->ui_unit];

	/*
	 * possible abnormal termination;
	 * only the current user or root can
	 * terminate an active run
	 */
	if (sc->c_flags & DS_OPEN) {
		if (sc->c_uid != u.u_ruid) {
			if (u.u_uid != 0)
				return(ENXIO);
		}
	}

	if (dsinit(ui->ui_unit)) {
		uprintf("ds%d: asc offline\n", ui->ui_unit);
		return(EIO);
	}

	return(0);
}

/*
 * set up asc sequence registers.
 * dir == 0 means d/a transfer,
 * dir == 1 means a/d transfer.
 *
 * this has been transformed into
 * a macro
 *
dsseq(ui, reg, conv, dir)
	register struct uba_device *ui;
{
	register struct dsdevice *dsaddr;
	register int i, j;

	dsaddr = (struct dsdevice *) ui->ui_addr;

	dir = ((dir & 01) << 6);

	dsaddr->ascseq[reg] = conv | dir;
}
*/

/*
 * check to see if the indicated bulk storage
 * device is a mounted filesystem.
 */
bsdevchk(bsdev)
	register int bsdev;
{
	register struct mount *mp;

	for (mp = &mount[0]; mp < &mount[NMOUNT]; mp++) {
		if (bsdev == mp->m_dev) {
			return(EBUSY);
		}
	}

	return(0);
}

/*
 * 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;
	int offl;

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

	dsaddr = (struct dsdevice *) reg;

	dsaddr->dmacsr = 0;
	dsaddr->dmacls = 0;
	dsaddr->ascseq[0] = DABASE+0 | ((DA & 01) << 6);
	dsaddr->ascseq[0] |= bit(7);	/* last sequence register */
	dsaddr->ascsrt = 0100;
	dsaddr->dmacsr = DMA_IE;
	dsaddr->asccsr = ASC_RUN | ASC_IE;

	DELAY(40000);

	/*
	 * now shut everything down.
	 * too bad we have to duplicate
	 * the code from dsinit but to
	 * call dsinit we need to give
	 * it a unit.
	 */
	if ((dsaddr->dmacsr & DMA_OFL) == 0) {
		dsaddr->asccsr = 0;
		dsaddr->ascrst = 0;
		offl = 0;
	}
	else
		offl = 1;

	dsaddr->dmablr = 0;
	dsaddr->dmasax = 0;
	dsaddr->dmasar = 0;
	dsaddr->dmacsr = 0;
	dsaddr->dmawc = 0;
	dsaddr->dmaacx = 0;
	dsaddr->dmaac = 0;
	dsaddr->dmadr = 0;
	dsaddr->dmaiva = 0;
	dsaddr->dmaclr = 0;
	dsaddr->dmacls = 0;
	dummy = dsaddr->dmasar;			/* clears sar flag */

#ifdef notdef
	/*
	 * put converters in monitor mode
	 */
	if (offl == 0) {
		dsaddr->ascseq[0] = ADBASE+0 | ((AD & 01) << 6);
		dsaddr->ascseq[1] = ADBASE+1 | ((AD & 01) << 6);
		dsaddr->ascseq[1] |= bit(7);	/* last sequence register */
		dsaddr->asccsr = ASC_RUN | ASC_MON;
	}
#endif notdef

	return(sizeof(struct dsdevice));
}

dsattach(ui)
	struct uba_device *ui;
{
	extern int dswatch();
	static int dswstart = 0;
	register struct ds_softc *sc;

	/*
	 * set up the blist linked list
	 * to the empty list. this must
	 * be done first because dsinit()
	 * calls dsfreeall().
	 */
	sc = &ds_softc[ui->ui_unit];
	sc->c_blist.av_forw = &sc->c_blist;
	sc->c_blist.av_back = &sc->c_blist;

	(void) dsinit(ui->ui_unit);

	/*
	 * start watchdog
	 */
	if (dswstart == 0) {
		timeout(dswatch, (caddr_t) 0, hz);
		dswstart++;
	}

	return(0);
}

/*
 * c_wticks gets cleared on each interrupt. dswatch()
 * increments c_wticks and if c_wticks gets over
 * a threshold then the addacs are assumed to have
 * hung and we do a reset.
 */
dswatch() {
	register struct uba_device *ui;
	register struct ds_softc *sc;
	register int ds;

	/* requeue us */
	timeout(dswatch, (caddr_t) 0, hz);

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

		sc = &ds_softc[ds];
		if ((sc->c_flags & DS_BSY) == 0) {
			sc->c_wticks = 0;
			continue;
		}

		sc->c_wticks++;
		if (sc->c_wticks >= 25) {
			sc->c_wticks = 0;
			printf("ds%d: lost interrupt\n", ds);
			ubareset(ui->ui_ubanum);
		}
	}

	return(0);
}

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

	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];

		/*
		 * this used to be after the ubarelse's,
		 * seems like it should go before them
		 */
		if (dsinit(ds))
			printf("<dsinit error>");

		/*
		 * release unibus resources
		 */
		for (i = 0; i < NDSB; i++) {
			if (sc->c_ubinfo[i] != 0) {
				printf("<%d>", (sc->c_ubinfo[i] >> 28) & 0xf);
				sc->c_ubinfo[i] = 0;
			}
		}
	}

	return(0);
}
#else
dsuinit() {
	return(0);
}
#endif