4.3BSD-Tahoe/usr/src/sys/tahoevba/dr.c

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

/*
 *	@(#)dr.c	7.1 (Berkeley) 5/21/88
 */

#include "dr.h"
#if NDR > 0
/*
 * DRV11-W DMA interface driver.
 *
 * UNTESTED WITH 4.3
 */
#include "../machine/mtpr.h"
#include "../machine/pte.h"

#include "param.h"
#include "conf.h"
#include "dir.h"
#include "user.h"
#include "proc.h"
#include "map.h"
#include "ioctl.h"
#include "buf.h"
#include "vm.h"
#include "uio.h"
#include "kernel.h"

#include "../tahoevba/vbavar.h"
#include "../tahoevba/drreg.h"

#define YES 1
#define NO  0

struct  vba_device  *drinfo[NDR];
struct  dr_aux dr_aux[NDR];

unsigned drminphys();
int	 drprobe(), drintr(), drattach(), drtimo(), drrwtimo();
int	 drstrategy();
extern	struct  vba_device  *drinfo[];
static	long drstd[] = { 0 };
struct  vba_driver drdriver =
    { drprobe, 0, drattach, 0, drstd, "rs", drinfo };

#define RSUNIT(dev) (minor(dev) & 7)
#define SPL_UP spl5

/* -------- Per-unit data -------- */

extern struct dr_aux dr_aux[];

#ifdef DR_DEBUG
long	DR11 = 0;
#endif

drprobe(reg, vi)
	caddr_t reg;
	struct vba_device *vi;
{
	register int br, cvec;		/* must be r12, r11 */
	struct rsdevice *dr;

#ifdef lint
	br = 0; cvec = br; br = cvec;
	drintr(0);
#endif
	if (badaddr(reg, 2))
		return (0);
	dr = (struct rsdevice *)reg;
	dr->dr_intvect = --vi->ui_hd->vh_lastiv;
#ifdef DR_DEBUG
	printf("dprobe: Set interrupt vector %lx and init\n",dr->dr_intvec);
#endif
	/* generate interrupt here for autoconfig */
	dr->dr_cstat = MCLR;		/* init board and device */
#ifdef DR_DEBUG
	printf("drprobe: Initial status %lx\n", dr->dr_cstat);
#endif
	br = 0x18, cvec = dr->dr_intvect;	/* XXX */
	return (sizeof (struct rsdevice));		/* DR11 exist */
}

/* ARGSUSED */
drattach(ui)
	struct vba_device *ui;
{
	register struct dr_aux *rsd;

	rsd = &dr_aux[ui->ui_unit];
	rsd->dr_flags = DR_PRES;		/* This dr11 is present */
	rsd->dr_addr = (struct rsdevice *)ui->ui_addr; /* Save addr of this dr11 */
	rsd->dr_istat = 0;
	rsd->dr_bycnt = 0;
	rsd->dr_cmd = 0;
	rsd->currenttimo = 0;
}

/*ARGSUSED*/
dropen(dev, flag)
	dev_t dev;
	int flag;
{
	register int unit = RSUNIT(dev);
	register struct rsdevice *dr;
	register struct dr_aux *rsd;

	if (drinfo[unit] == 0 || !drinfo[unit]->ui_alive)
		return (ENXIO);
	dr = RSADDR(unit);
	rsd = &dr_aux[unit];
	if (rsd->dr_flags & DR_OPEN) {
#ifdef DR_DEBUG
		printf("\ndropen: dr11 unit %ld already open",unit);
#endif
		return (ENXIO);	  		/* DR11 already open */
	}
	rsd->dr_flags |= DR_OPEN;	/* Mark it OPEN */
	rsd->dr_istat = 0;		/* Clear status of previous interrupt */
	rsd->rtimoticks = hz;		/* Set read no stall timout to 1 sec */
	rsd->wtimoticks = hz*60;	/* Set write no stall timout to 1 min */
	dr->dr_cstat = DR_ZERO;		/* Clear function & latches */
	dr->dr_pulse = (RDMA | RATN);	/* clear leftover attn & e-o-r flags */
	drtimo(dev);			/* start the self kicker */
	return (0);
}

drclose (dev)
	dev_t dev;
{
	register int unit = RSUNIT(dev);
	register struct dr_aux *dra;
	register struct rsdevice *rs;
	register short s;

	dra = &dr_aux[unit];
	if ((dra->dr_flags & DR_OPEN) == 0) {
#ifdef DR_DEBUG
		printf("\ndrclose: DR11 device %ld not open",unit);
#endif
		return;
	}
	dra->dr_flags &= ~(DR_OPEN|DR_ACTV);
	rs = dra->dr_addr;
	s = SPL_UP();
	rs->dr_cstat = DR_ZERO;
	if (dra->dr_buf.b_flags & B_BUSY) {
		dra->dr_buf.b_flags &= ~B_BUSY;
		wakeup((caddr_t)&dra->dr_buf.b_flags);
	}
	splx(s);
}


/*	drread() works exactly like drwrite() except that the
	B_READ flag is used when physio() is called
*/
drread (dev, uio)
	dev_t dev;
	struct uio *uio;
{	register struct dr_aux *dra;
	register struct buf *bp;
	register int spl, err;
	register int unit = RSUNIT(dev);

	if (uio->uio_iov->iov_len <= 0 ||	/* Negative count */
	    uio->uio_iov->iov_len & 1 ||	/* odd count */
	    (int)uio->uio_iov->iov_base & 1)	/* odd destination address */
		return (EINVAL);
#ifdef DR_DEBUG
	if (DR11 & 8)
		printf("\ndrread: (len:%ld)(base:%lx)",
		    uio->uio_iov->iov_len,(int)uio->uio_iov->iov_base); 
#endif
	dra = &dr_aux[RSUNIT(dev)];
	dra->dr_op = DR_READ;
	bp =  &dra->dr_buf;
	bp->b_resid = 0;
	if (dra->dr_flags & DR_NORSTALL) {
		/*
		 * We are in no stall mode, start the timer,
		 * raise IPL so nothing can stop us once the
		 * timer's running
		 */
		spl = SPL_UP();
		timeout(drrwtimo, (caddr_t)((dra->currenttimo<<8) | unit),
		    (int)dra->rtimoticks);
		err = physio(drstrategy, bp, dev,B_READ, drminphys, uio);
		splx(spl);
		if (err)
			return (err);
		dra->currenttimo++;	/* Update current timeout number */
		/* Did we timeout */
		if (dra->dr_flags & DR_TMDM) {
			dra->dr_flags &= ~DR_TMDM; /* Clear timeout flag */
			u.u_error = 0;	/* Made the error ourself, ignore it */
		}
		return (err);
	}
	return (physio(drstrategy, bp, dev,B_READ, drminphys, uio));
}

drwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{	register struct dr_aux *dra;
	register struct buf *bp;
	register int unit = RSUNIT(dev);
	int spl, err;

	if (uio->uio_iov->iov_len <= 0 || uio->uio_iov->iov_len & 1 ||
	    (int)uio->uio_iov->iov_base & 1)
		return (EINVAL);
#ifdef DR_DEBUG
	if (DR11 & 4)
		printf("\ndrwrite: (len:%ld)(base:%lx)",
		    uio->uio_iov->iov_len,(int)uio->uio_iov->iov_base); 
#endif
	dra = &dr_aux[RSUNIT(dev)];
	dra->dr_op = DR_WRITE;
	bp =  &dra->dr_buf;
	bp->b_resid = 0;
	if (dra->dr_flags & DR_NOWSTALL) {
		/*
		 * We are in no stall mode, start the timer,
		 * raise IPL so nothing can stop us once the
		 * timer's running
		 */
		spl = SPL_UP();
		timeout(drrwtimo,(caddr_t)((dra->currenttimo<<8) | unit),
		    (int)dra->wtimoticks);
		err = physio (drstrategy, bp, dev,B_WRITE, drminphys, uio);
		splx(spl);
		if (err)
			return (err);
		dra->currenttimo++;	/* Update current timeout number */
		/* Did we timeout */
		if (dra->dr_flags & DR_TMDM) {
			dra->dr_flags &= ~DR_TMDM;	/* Clear timeout flag */
			u.u_error = 0;	/* Made the error ourself, ignore it */
		}
		return (err);
	}
	return (physio(drstrategy, bp, dev,B_WRITE, drminphys, uio));
}

/*
 * Routine used by calling program to issue commands to dr11 driver and 
 * through it to the device.
 * It is also used to read status from the device and driver and to wait 
 * for attention interrupts.
 * Status is returned in an 8 elements unsigned short integer array, the 
 * first two elements of the array are also used to pass arguments to 
 * drioctl() if required.
 * The function bits to be written to the dr11 are included in the cmd
 * argument. Even if they are not being written to the dr11 in a particular
 * drioctl() call, they will update the copy of cmd that is stored in the
 * driver. When drstrategy() is called, this updated copy is used if a 
 * deferred function bit write has been specified. The "side effect" of
 * calls to the drioctl() requires that the last call prior to a read or
 * write has an appropriate copy of the function bits in cmd if they are
 * to be used in drstrategy().
 * When used as command value, the contents of data[0] is the command
 * parameter.
 */
drioctl(dev, cmd, data)
	dev_t dev;
	int cmd;
	long *data;
{
	register int unit = RSUNIT(dev);
	register struct dr_aux *dra;
	register struct rsdevice *rsaddr = RSADDR(unit);
	int s;
	u_short status;
	long temp;

#ifdef DR_DEBUG
	if (DR11 & 0x10)
		printf("\ndrioctl: (dev:%lx)(cmd:%lx)(data:%lx)(data[0]:%lx)",
		    dev,cmd,data,data[0]);
#endif
	dra = &dr_aux[unit];
	dra->dr_cmd = 0;	/* Fresh copy; clear all previous flags */
	switch (cmd) {

	case DRWAIT:		/* Wait for attention interrupt */
#ifdef DR_DEBUG
		printf("\ndrioctl: wait for attention interrupt");
#endif
		s = SPL_UP();
		/* 
		 * If the attention flag in dr_flags is set, it probably
		 * means that an attention has arrived by the time a
		 * previous DMA end-of-range interrupt was serviced. If
		 * ATRX is set, we will return with out sleeping, since
		 * we have received an attention since the last call to
		 * wait on attention.  This may not be appropriate for
		 * some applications.
		 */
		if ((dra->dr_flags & DR_ATRX) == 0) {
			dra->dr_flags |= DR_ATWT;	/* Set waiting flag */
			/*
			 * Enable interrupt; use pulse reg.
			 * so function bits are not changed
			 */
			rsaddr->dr_pulse = IENB;
			sleep((caddr_t)&dra->dr_cmd, DRPRI);
		}
		splx(s);
		break;

	case DRPIOW:			/* Write to p-i/o register */
		rsaddr->dr_data = data[0];
		break;

	case DRPACL:			/* Send pulse to device */
		rsaddr->dr_pulse = FCN2;
		break;

	case DRDACL:			/* Defer alco pulse until go */
		dra->dr_cmd |= DR_DACL;
		break;

	case DRPCYL:			/* Set cycle with next go */
		dra->dr_cmd |= DR_PCYL;
		break;

	case DRDFCN:			/* Update function with next go */
		dra->dr_cmd |= DR_DFCN;
		break;

	case DRRATN:			/* Reset attention flag */
		rsaddr->dr_pulse = RATN;
		break;

	case DRRDMA:			/* Reset DMA e-o-r flag */
		rsaddr->dr_pulse = RDMA;
		break;

	case DRSFCN:			/* Set function bits */
		temp = data[0] & DR_FMSK;
		/*
		 * This has a very important side effect -- It clears
		 * the interrupt enable flag. That is fine for this driver,
		 * but if it is desired to leave interrupt enable at all
		 * times, it will be necessary to read the status register
		 * first to get IENB, or carry a software flag that indicates
		 * whether interrupts are set, and or this into the control
		 * register value being written.
		 */
		rsaddr->dr_cstat = temp;
		break;

	case DRRPER:			/* Clear parity flag */
		rsaddr->dr_pulse = RPER;
		break;

	case DRSETRSTALL:		/* Set read stall mode. */
		dra->dr_flags &= (~DR_NORSTALL);
		break;

	case DRSETNORSTALL:		/* Set no stall read  mode. */
		dra->dr_flags |= DR_NORSTALL;
		break;

	case DRGETRSTALL:		/* Returns true if in read stall mode */
		data[0]  = (dra->dr_flags & DR_NORSTALL)? 0 : 1;
		break;

	case DRSETRTIMEOUT:		/* Set read stall timeout (1/10 secs) */
		if (data[0] < 1) {
			u.u_error = EINVAL;
			temp = 1;
		}
		dra->rtimoticks = (data[0] * hz )/10;
		break;

	case DRGETRTIMEOUT:		/* Return read stall timeout */
		data[0] = ((dra->rtimoticks)*10)/hz;
		break;

	case DRSETWSTALL:		/* Set write stall mode. */
		dra->dr_flags &= (~DR_NOWSTALL);
		break;

	case DRSETNOWSTALL:		/* Set write stall mode. */
		dra->dr_flags |= DR_NOWSTALL;
		break;

	case DRGETWSTALL:		/* Return true if in write stall mode */
		data[0] = (dra->dr_flags & DR_NOWSTALL)? 0 : 1;
		break;

	case DRSETWTIMEOUT:		/* Set write stall timeout (1/10's) */
		if (data[0] < 1) {
			u.u_error = EINVAL;
			temp = 1;
		}
		dra->wtimoticks = (data[0] * hz )/10;
		break;

	case DRGETWTIMEOUT:		/* Return write stall timeout */
		data[0] = ((dra->wtimoticks)*10)/hz;
		break;

	case DRWRITEREADY:		/* Return true if can write data */
		data[0] = (rsaddr->dr_cstat & STTA)? 1 : 0;
		break;

	case DRREADREADY:		/* Return true if data to be read */
		data[0] = (rsaddr->dr_cstat & STTB)? 1 : 0;
		break;

	case DRBUSY:			/* Return true if device busy */
		/*
		 * Internally this is the DR11-W
		 * STAT C bit, but there is a bug in the Omega 500/FIFO
		 * interface board that it cannot drive this signal low
		 * for certain DR11-W ctlr such as the Ikon. We use the
		 * REDY signal of the CSR on the Ikon DR11-W instead. 
		 */
#ifdef notdef
		data[0] = (rsaddr->dr_cstat & STTC)? 1 : 0;
#else
		data[0] = ((rsaddr->dr_cstat & REDY)? 0 : 1);
#endif
		break;

	case DRRESET:			/* Reset device */
		/* Reset DMA ATN RPER flag */
		rsaddr->dr_pulse = (MCLR|RDMA|RATN|RPER);
		DELAY(0x1f000);
		while ((rsaddr->dr_cstat & REDY) == 0)
			sleep((caddr_t)dra, DRPRI);	/* Wakeup by drtimo() */
		dra->dr_istat = 0;
		dra->dr_cmd = 0;
		dra->currenttimo = 0;
		break;

	case DR11STAT: {		/* Copy back dr11 status to user */
		register struct dr11io *dr = (struct dr11io *)data;
		dr->arg[0] = dra->dr_flags;
		dr->arg[1] = rsaddr->dr_cstat;
		dr->arg[2] = dra->dr_istat;	/* Status at last interrupt */
		dr->arg[3] = rsaddr->dr_data;	/* P-i/o input data */
		status = (u_short)((rsaddr->dr_addmod << 8) & 0xff00);
		dr->arg[4] = status | (u_short)(rsaddr->dr_intvect & 0xff);
		dr->arg[5] = rsaddr->dr_range;
		dr->arg[6] = rsaddr->dr_rahi;
		dr->arg[7] = rsaddr->dr_ralo;
		break;
	}
	case DR11LOOP:			/* Perform loopback test */
		/*
		 * NB: MUST HAVE LOOPBACK CABLE ATTACHED --
		 * Test results are printed on system console
		 */
		if (suser())
			dr11loop(rsaddr, dra, unit);
		break;

	default:
		return (EINVAL);
	}
#ifdef DR_DEBUG
	if (DR11 & 0x10)
		printf("**** (data[0]:%lx)",data[0]);
#endif
	return (0);
}

#define NPAT	2
#define DMATBL	20
u_short	tstpat[DMATBL] = { 0xAAAA, 0x5555};
long	DMAin = 0;

/*
 * Perform loopback test -- MUST HAVE LOOPBACK CABLE ATTACHED
 * Test results are printed on system console
 */
dr11loop(dr, dra, unit)
	struct rsdevice *dr;
	struct dr_aux *dra;
	int unit;
{
	register long result, ix;
	long addr, wait;

	dr->dr_cstat = MCLR;		/* Clear board & device, disable intr */
	printf("\n\t ----- DR11 unit %ld loopback test -----", unit);
	printf("\n\t Program I/O ...");
	for (ix=0;ix<NPAT;ix++) {
		dr->dr_data = tstpat[ix];	/* Write to Data out register */
		result = dr->dr_data & 0xFFFF;	/* Read it back */
		if (result != tstpat[ix]) {
			printf("Failed, expected : %lx --- actual : %lx",
				tstpat[ix], result);
			return;
		}
	}
	printf("OK\n\t Functions & Status Bits ...");
	dr->dr_cstat = (FCN1 | FCN3);
	result = dr->dr_cstat & 0xffff;		/* Read them back */
	if ((result & (STTC | STTA)) != (STTC |STTA)) {
		printf("Failed, expected : %lx --- actual : %lx, ISR:%lx",
			(STTA|STTC), (result & (STTA|STTC)), result);
		return;
	}
	dr->dr_cstat = FCN2;
	result = dr->dr_cstat & 0xffff;		/* Read them back */
	if ((result & STTB) != STTB) {
		printf("Failed, expected : %lx --- actual : %lx, ISR:%lx",
			STTB, (result & STTB), result);
		return;
	}
	printf("OK\n\t DMA output ...");
	if (DMAin)
		goto dmain;
	/* Initialize DMA data buffer */
	for (ix=0; ix<DMATBL; ix++)
		tstpat[ix] = 0xCCCC + ix;
	tstpat[DMATBL-1] = 0xCCCC;	/* Last word output */
	/* Setup normal DMA */
	addr = (long)vtoph((struct proc *)0, (unsigned)tstpat);
	dr->dr_walo = (addr >> 1) & 0xffff;
	dr->dr_wahi = (addr >> 17) & 0x7fff;
	/* Set DMA range count: (number of words - 1) */
	dr->dr_range = DMATBL - 1;
	/* Set address modifier code to be used for DMA access to memory */
	dr->dr_addmod = DRADDMOD;

	/*
	 * Clear dmaf and attf to assure a clean dma start, also disable
	 * attention interrupt
	 */
	dr->dr_pulse = RDMA|RATN|RMSK;  /* Use pulse register */
	dr->dr_cstat = GO|CYCL;		  /* GO...... */

	/* Wait for DMA complete; REDY and DMAF are true in ISR */
	wait = 0;
	while ((result=(dr->dr_cstat & (REDY|DMAF))) != (REDY|DMAF)) {
		printf("\n\tWait for DMA complete...ISR : %lx", result);
		if (++wait > 5) {
			printf("\n\t DMA output fails...timeout!!, ISR:%lx",
				result);
			return;
		}
	}
	result = dr->dr_data & 0xffff;		/* Read last word output */	
	if (result != 0xCCCC) {
		printf("\n\t Fails, expected : %lx --- actual : %lx",
			0xCCCC, result);
		return;
	}
	printf("OK\n\t DMA input ...");
dmain:
	dr->dr_data = 0x1111;		/* DMA input data */
	/* Setup normal DMA */
	addr = (long)vtoph((struct proc *)0, (unsigned)tstpat);
	dr->dr_walo = (addr >> 1) & 0xffff;
	dr->dr_wahi = (addr >> 17) & 0x7fff;
	dr->dr_range = DMATBL - 1;
	dr->dr_addmod = (char)DRADDMOD;
	dr->dr_cstat = FCN1;		/* Set FCN1 in ICR to DMA in*/
	if ((dra->dr_flags & DR_LOOPTST) == 0) {
		/* Use pulse reg */  
		dr->dr_pulse = RDMA|RATN|RMSK|CYCL|GO;
		/* Wait for DMA complete; REDY and DMAF are true in ISR */
		wait = 0;
		while ((result=(dr->dr_cstat & (REDY|DMAF))) != (REDY|DMAF)) {
			printf("\n\tWait for DMA to complete...ISR:%lx",result);
			if (++wait > 5) {
				printf("\n\t DMA input timeout!!, ISR:%lx",
					result);
				return;
			}
		}
	} else  {
		/* Enable DMA e-o-r interrupt */
		dr->dr_pulse = IENB|RDMA|RATN|CYCL|GO;
		/* Wait for DMA complete; DR_LOOPTST is false in dra->dr_flags*/
		wait = 0;
		while (dra->dr_flags & DR_LOOPTST) { 
			result = dr->dr_cstat & 0xffff;
			printf("\n\tWait for DMA e-o-r intr...ISR:%lx", result);
			if (++wait > 7) {
				printf("\n\t DMA e-o-r timeout!!, ISR:%lx",
					result);
				dra->dr_flags &= ~DR_LOOPTST;
				return;
			}
		}
		dra->dr_flags |= DR_LOOPTST;
	}
	mtpr(P1DC, tstpat);			/* Purge cache */
	mtpr(P1DC, 0x3ff+tstpat);
	for (ix=0; ix<DMATBL; ix++) {
		if (tstpat[ix] != 0x1111) {
			printf("\n\t Fails, ix:%d, expected:%x --- actual:%x",
				ix, 0x1111, tstpat[ix]);
			return;
		}
	}
	if ((dra->dr_flags & DR_LOOPTST) == 0) {
		dra->dr_flags |= DR_LOOPTST;
		printf(" OK..\n\tDMA end of range interrupt...");
		goto dmain;
	}
	printf(" OK..\n\tAttention interrupt....");
	dr->dr_pulse = IENB|RDMA;
	dr->dr_pulse = FCN2;
	/* Wait for ATTN interrupt; DR_LOOPTST is false in dra->dr_flags*/
	wait = 0;
	while (dra->dr_flags & DR_LOOPTST) { 
		result = dr->dr_cstat & 0xffff;
		printf("\n\tWait for Attention intr...ISR:%lx",result);
		if (++wait > 7) {
			printf("\n\t Attention interrupt timeout!!, ISR:%lx",
				result);
			dra->dr_flags &= ~DR_LOOPTST;
			return;
		}
	}
	dra->dr_flags &= ~DR_LOOPTST;
	printf(" OK..\n\tDone...");
}

/* Reset state on Unibus reset */
/*ARGSUSED*/
drreset(uban)
	int uban;
{

}

/*
 * An interrupt is caused either by an error,
 * base address overflow, or transfer complete
 */
drintr(dr11)
	int dr11;
{
	register struct dr_aux *dra = &dr_aux[dr11];
	register struct rsdevice *rsaddr = RSADDR(dr11);
	register struct buf *bp;
	register short status;

	status = rsaddr->dr_cstat & 0xffff;	/* get board status register */
	dra->dr_istat = status;
#ifdef DR_DEBUG
	if (DR11 & 2)
		printf("\ndrintr: dr11 status : %lx",status & 0xffff);
#endif
	if (dra->dr_flags & DR_LOOPTST) {	/* doing loopback test */
		dra->dr_flags &= ~DR_LOOPTST;
		return;
	}
	/*
	 * Make sure this is not a stray interrupt; at least one of dmaf or attf
	 * must be set. Note that if the dr11 interrupt enable latch is reset 
	 * during a hardware interrupt ack sequence, and by the we get to this 
	 * point in the interrupt code it will be 0. This is done to give the
	 * programmer some control over how the two more-or-less independent
	 * interrupt sources on the board are handled.
	 * If the attention flag is set when drstrategy() is called to start a
	 * dma read or write an interrupt will be generated as soon as the
	 * strategy routine enables interrupts for dma end-of-range. This will
	 * cause execution of the interrupt routine (not necessarily bad) and
	 * will cause the interrupt enable mask to be reset (very bad since the
	 * dma end-of-range condition will not be able to generate an interrupt
	 * when it occurs) causing the dma operation to time-out (even though
	 * the dma transfer will be done successfully) or hang the process if a
	 * software time-out capability is not implemented. One way to avoid 
	 * this situation is to check for a pending attention interrupt (attf
	 * set) by calling drioctl() before doing a read or a write. For the
	 * time being this driver will solve the problem by clearing the attf
	 * flag in the status register before enabling interrupts in
	 * drstrategy().
	 *
	 * **** The IKON 10084 for which this driver is written will set both
	 * attf and dmaf if dma is terminated by an attention pulse. This will
	 * cause a wakeup(&dr_aux), which will be ignored since it is not being 
	 * waited on, and an iodone(bp) which is the desired action. Some other
	 * dr11 emulators, in particular the IKON 10077 for the Multibus, donot
	 * dmaf in this case. This may require some addtional code in the inter-
	 * rupt routine to ensure that en iodone(bp) is issued when dma is term-
	 * inated by attention.
	 */
	bp = dra->dr_actf;
	if ((status & (ATTF | DMAF)) == 0) {
		printf("dr%d: stray interrupt, status=%x", dr11, status);
		return;
	}
	if (status & DMAF) {		/* End-of-range interrupt */
		dra->dr_flags |= DR_DMAX;

#ifdef DR_DEBUG
		if (DR11 & 2)
		printf("\ndrintr: e-o-r interrupt,cstat:%lx,dr_flags:%lx",
			status&0xffff, dra->dr_flags & DR_ACTV);
#endif
		if ((dra->dr_flags & DR_ACTV) == 0) {
			/* We are not doing DMA !! */
			bp->b_flags |= B_ERROR;
		} else {
			if (dra->dr_op == DR_READ)
				mtpr(P1DC, bp->b_un.b_addr);
			dra->dr_bycnt -= bp->b_bcount;
			if (dra->dr_bycnt >0) {
				bp->b_un.b_addr += bp->b_bcount;
				bp->b_bcount = (dra->dr_bycnt > NBPG) ? NBPG:
					dra->dr_bycnt;
				drstart(rsaddr, dra, bp);
				return;
			}
		}
		dra->dr_flags &= ~DR_ACTV;
		wakeup((caddr_t)dra);		/* Wakeup waiting in drwait() */
		rsaddr->dr_pulse = (RPER|RDMA|RATN); /* reset dma e-o-r flag */
	}
	/*
	 * Now test for attention interrupt -- It may be set in addition to 
	 * the dma e-o-r interrupt. If we get one we will issue a wakeup to
	 * the drioctl() routine which is presumable waiting for one.
	 * The program may have to monitor the attention interrupt received
	 * flag in addition to doing waits for the interrupt. Futhermore, 
	 * interrupts are not enabled unless dma is in progress or drioctl()
	 * has been called to wait for attention -- this may produce some
	 * strange results if attf is set on the dr11 when a read or a write
	 * is initiated, since that will enables interrupts.
	 * **** The appropriate code for this interrupt routine will probably
	 * be rather application dependent.
	 */
	if (status & ATTF) {
		dra->dr_flags |= DR_ATRX;
		dra->dr_flags &= ~DR_ATWT;
		rsaddr->dr_cstat = RATN;	/* reset attention flag */
		/*
		 * Some applications which use attention to terminate
		 * dma may also want to issue an iodone() here to
		 * wakeup physio().
		 */
		wakeup((caddr_t)&dra->dr_cmd);
	}
}

unsigned
drminphys(bp)
	struct buf *bp;
{

	if (bp->b_bcount > 65536)
		bp->b_bcount = 65536;
}

/*
 * This routine performs the device unique operations on the DR11W
 * it is passed as an argument to and invoked by physio
 */
drstrategy (bp)
	register struct buf *bp;
{
	register int s;
	int unit = RSUNIT(bp->b_dev);
	register struct rsdevice *rsaddr = RSADDR(unit);
	register struct dr_aux *dra = &dr_aux[unit];
	register int ok;
#ifdef DR_DEBUG
	register char *caddr;
	long drva();
#endif

	if ((dra->dr_flags & DR_OPEN) == 0) {	/* Device not open */
		bp->b_error = ENXIO;
		bp->b_flags |= B_ERROR;
		iodone (bp);
		return;
	}
	while (dra->dr_flags & DR_ACTV)
		/* Device is active; should never be in here... */
		sleep((caddr_t)&dra->dr_flags,DRPRI);
	dra->dr_actf = bp;
#ifdef DR_DEBUG
	drva(dra, bp->b_proc, bp->b_un.b_addr, bp->b_bcount);
#endif
	dra->dr_oba = bp->b_un.b_addr;	/* Save original addr, count */
	dra->dr_obc = bp->b_bcount;
	dra->dr_bycnt = bp->b_bcount;	/* Save xfer count used by drintr() */
	if ((((long)bp->b_un.b_addr & 0x3fffffff) >> PGSHIFT) !=
	    ((((long)bp->b_un.b_addr & 0x3fffffff) + bp->b_bcount) >> PGSHIFT))
		bp->b_bcount = NBPG - (((long)bp->b_un.b_addr) & PGOFSET);
	dra->dr_flags |= DR_ACTV;	/* Mark active (use in intr handler) */
	s = SPL_UP();
	drstart(rsaddr,dra,bp);
	splx(s);
	ok = drwait(rsaddr,dra);
#ifdef DR_DEBUG
	if (DR11 & 0x40) {
		caddr = (char *)dra->dr_oba;
		if (dra->dr_op == DR_READ)
			printf("\nAfter read: (%lx)(%lx)",
			    caddr[0]&0xff, caddr[1]&0xff);
	}
#endif
	dra->dr_flags &= ~DR_ACTV;		/* Clear active flag */
	bp->b_un.b_addr = dra->dr_oba;	/* Restore original addr, count */
	bp->b_bcount = dra->dr_obc;
	if (!ok)
		bp->b_flags |= B_ERROR;
	/* Mark buffer B_DONE,so physstrat() in ml/machdep.c won't sleep */
	iodone(bp);			
	wakeup((caddr_t)&dra->dr_flags);
	/*
	 * Return to the calling program (physio()). Physio() will sleep
	 * until awaken by a call to iodone() in the interupt handler --
	 * which will be called by the dispatcher when it receives dma
	 * end-of-range interrupt.
	 */
}

drwait(rs, dr)
	register struct rsdevice *rs;
	register struct dr_aux *dr;
{
	int s;

	s = SPL_UP();
	while (dr->dr_flags & DR_ACTV)
		sleep((caddr_t)dr, DRPRI);
	splx(s);
	if (dr->dr_flags & DR_TMDM) {		/* DMA timed out */
		dr->dr_flags &= ~DR_TMDM;
		return (0);
	}
	if (rs->dr_cstat & (PERR|BERR|TERR)) {
		dr->dr_actf->b_flags |= B_ERROR;
		return (0);
	}
	dr->dr_flags &= ~DR_DMAX;
	return (1);
}

/*
 *
 * The lower 8-bit of tinfo is the minor device number, the
 * remaining higher 8-bit is the current timout number
 */
drrwtimo(tinfo)
	register u_long tinfo;
{
	register long unit = tinfo & 0xff;
	register struct dr_aux *dr = &dr_aux[unit];
	register struct rsdevice *rs = dr->dr_addr;

	/*
	 * If this is not the timeout that drwrite/drread is waiting
	 * for then we should just go away
	 */
	if ((tinfo &~ 0xff) != (dr->currenttimo << 8))
		return;
	/* Mark the device timed out */
	dr->dr_flags |= DR_TMDM;
	dr->dr_flags &= ~DR_ACTV;
	rs->dr_pulse = RMSK;			/* Inihibit interrupt */
	rs->dr_pulse = (RPER|RDMA|RATN|IENB);	/* Clear DMA logic */
	/*
	 * Some applications will not issue a master after dma timeout,
	 * since doing so sends an INIT H pulse to the external device,
	 * which may produce undesirable side-effects.
	 */
	/* Wake up process waiting in drwait() and flag the error */
	dr->dr_actf->b_flags |= B_ERROR;
	wakeup((caddr_t)dr->dr_cmd);
}

/*
 * Kick the driver every second
 */
drtimo(dev)
	dev_t dev;
{
	register int unit = RSUNIT(dev);
	register struct dr_aux *dr;

	dr = &dr_aux[unit];
	if (dr->dr_flags & DR_OPEN)
		timeout(drtimo, (caddr_t)dev, hz);
	wakeup((caddr_t)dr);	/* Wakeup any process waiting for interrupt */
}

#ifdef DR_DEBUG
drva(dra, p, va, bcnt)
	struct dr_aux *dra;
	struct proc *p;
	char *va;
	long bcnt;
{
	register long first, last , np;

	if (DR11 & 0x20)  {
		first = ((long)(vtoph(p, (unsigned)va))) >> 10;
		last = ((long)(vtoph(p, (unsigned)va+bcnt))) >> 10;
		np = bcnt / 0x3ff;
		printf("\ndrva: (op:%ld)(first:%ld)(last:%ld)(np:%ld)(cnt:%ld)",
			dra->dr_op,first,last,np,bcnt);
	}
}
#endif

drstart(rsaddr, dra, bp)
	register struct rsdevice *rsaddr;
	register struct dr_aux *dra;
	register struct buf *bp;
{
	register long addr;
	u_short go;

#ifdef DR_DEBUG
	if (dra->dr_op == DR_READ && (DR11 & 8)) {
		char *caddr = (char *)bp->b_un.b_addr;
		printf("\ndrstart: READ, bcnt:%ld",bp->b_bcount);
		printf(",(%lx)(%lx)",caddr[0]&0xff,caddr[1]&0xff);
	}
#endif
	/* we are doing raw IO, bp->b_un.b_addr is user's address */
	addr = (long)vtoph(bp->b_proc, (unsigned)bp->b_un.b_addr);
	/*
	 * Set DMA address into DR11 interace registers: DR11 requires that
	 * the address be right shifted 1 bit position before it is written
	 * to the board (The board will left shift it one bit position before
	 * it places the address on the bus
	 */
	rsaddr->dr_walo = (addr >> 1) & 0xffff;
	rsaddr->dr_wahi = (addr >> 17) & 0x7fff;
	/* Set DMA range count: (number of words - 1) */
	rsaddr->dr_range = (bp->b_bcount >> 1) - 1;
	/* Set address modifier code to be used for DMA access to memory */
	rsaddr->dr_addmod = DRADDMOD;
	/*
	 * Now determine whether this is a read or a write. ***** This is
	 * probably only usefull for link mode operation, since dr11 doesnot
	 * controll the direction of data transfer. The C1 control input 
	 * controls whether the hardware is doing a read or a write. In link
	 * mode this is controlled by function 1 latch (looped back by the
	 * cable) and could be set the program. In the general case, the dr11
	 * doesnot know in advance what the direction of transfer is - although
	 * the program and protocol logic probably is
	 */
#ifdef DR_DEBUG
	if (DR11 & 1)
		printf(
"\ndrstrat: about to GO..,dr_cmd:%lx,drstat:%lx,drcnt:%ld,cdata:%lx,OP:%ld",
		    dra->dr_cmd, rsaddr->dr_cstat, rsaddr->dr_range,
		    rsaddr->dr_data, dra->dr_op);
#endif
	/*
	 * Update function latches may have been done already by drioctl() if
	 * request from drioctl()
	 */
	if (dra->dr_cmd & DR_DFCN) {		/* deferred function write */
		dra->dr_cmd &= ~DR_DFCN;	/* Clear request */
		go = dra->dr_cmd & DR_FMSK;	/* mask out fcn bits */
		rsaddr->dr_cstat = go;		/* Write it to the board */
	}
	/* Clear dmaf and attf to assure a clean dma start */
	rsaddr->dr_pulse = RATN|RDMA|RPER;
	rsaddr->dr_cstat = IENB|GO|CYCL|dra->dr_op; /* GO...... */
	/*
	 * Now check for software cycle request -- usually
	 * by transmitter in link mode.
	 */
	if (dra->dr_cmd & DR_PCYL) {
		dra->dr_cmd &= ~DR_PCYL;	/* Clear request */
		rsaddr->dr_pulse = CYCL;	/* Use pulse register again */
	}
	/*
	 * Now check for deferred ACLO FCNT2 pulse request -- usually to tell
	 * the transmitter (via its attention) that we have enabled dma.
	 */
	if (dra->dr_cmd & DR_DACL) {
		dra->dr_cmd &= ~DR_DACL;	/* Clear request */
		rsaddr->dr_pulse = FCN2;	/* Use pulse register again */
	}
}
#endif  NDR