static char rcsid[] = "$Header: it.c,v 820.1 86/12/04 19:55:49 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * Interphase Storager QIC II tape controller.
 *
 */

#include "it.h"
#if NIT > 0
#include "../h/param.h"
#include "../h/buf.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/file.h"
#include "../h/inode.h"
#include "../h/ioctl.h"
#include "../h/user.h"
#include "../h/uio.h"
#include "../s32/dkio.h"
#include "../h/mtio.h"
#include "../s32/cpu.h"
#include "../s32dev/mbvar.h"
#include "../s32dev/isreg.h"
#include "../s32dev/isvar.h"

#include "../s32dev/is_ioctl.h"


	/* debug flags */

#ifdef DEBUG
char itdebug = 0;
char itidebug = 0;		/* print itintr() actions */
char itsdebug = 0;		/* print operation in itustart() */
char itrdebug = 0;
char itcdebug = 0;
char itudebug = 0;
#else
#define itdebug 0
#define itidebug 0
#define itsdebug 0
#define itrdebug 0
#define itcdebug 0
#define itudebug 0
#endif



	/* tunable constants */

#define IT_TAPETIMEOUT	18		/* timeout in 10s incrs for tape ops */

#define IT_NOREWIND 	0x08		/* PUT ME in an include.. */


int itretrycnt = 2;			/* software retry count */

#define itunit(d) (minor(d) & 0x3)
#define itmkdev(u) ((u) & 0x3)


#define INF 10000000			/* big num for eot */

long it_ioctl_mutex;			/* b_flags-like variable for mutual
					 * exclusion on it_ioctl().
					 */
struct is_tape is_tape[NIT];		/* per tape structures */
struct mb_device *itinfo[NIT];

struct buf itcbuf;			/* command buffer for itcmd() */
u_char it_tape_inited;


/*
 * Check to see if a slave is there 
 * (if a device is connected to controller).
 * Note that we need to check to see if the controller is a Storager.
 */

itslave(md, mc)
	struct mb_device *md;
	struct mb_ctlr *mc;
{
	register slave = md->md_slave;
	register struct is_ctlr *ic = is_ctlr + md->md_ctlr;
	int unit = itunit(slave);
	register struct is_tape *it = is_tape + unit;
	int i;
	u_char intunit, csr, tprdy;
	u_char stat;

	if ((unit < 0) || (unit >= NIT) || (!ic->ic_storager)) {
		if (itdebug)
			printf("itslave: out of bounds.\n");
		return 0;
	}


	if (isaltdebug) cdebugger("itslave");

	/*
	 * We must configure every unit before we send any other commands.
	 * This command probably fails if there is no unit there, but
	 * I'm not sure.  Anyhow, we'll catch the error after looking
	 * at the ready bits.
	 */

	if (! it_tape_inited++) {
		stat = iticmd(ic, it, IPCMD_TAPECONF, IPTAPEOPT_NORMAL, 
				0, 0, 0, 0);

		/* 
		 * Need to delay for possible status change from
		 * tape drive here - approx 20ms should suffice.
		 */

		i = (chipType == CHIPTYPE_68020) ? 10000 * 10: 10000;
		while (i--)
			;

		if (ic->ic_addr1->iscsr & (ISCSR_SI|ISCSR_CI)) {
			/* got a late interrupt */

#ifdef ISNOISY
			printf("it%d: late interrupt.\n",unit);
#endif ISNOISY
			ic->ic_addr1->iscsr = ISCSR_CLRI;

			/*
			 * Explanation: any change in any of these fields
			 * means that the clri was acked.
			 * Example: intunit changes, which means that we have
			 * the same int type on a different unit.
			 *
			 * bog: where is the explanation behind this 
			 * explanation?  Where is the logic documented?  
			 * It seems all we're doing here is providing 
			 * more complexity and slower speeds.
			 */
			i = ISTIMEOUTDELAY;
			while (
				/*
				* 1. CSR is busy, I.E. something is happening
				*/
			       (ic->ic_addr1->iscsr & ~ISCSR_BUSY) == 
					(csr & ~ISCSR_BUSY) &&
				/*
				 * 2. The interrupting unit is ours
				 * (note since we're CLRI'ing, the interrupt
				 * should disappear. Right? RIGHT?)
				 */
				ic->ic_addr1->isintunit == intunit &&
				/*
				 * 3. The ready tape drive is ours.
				 */
				ic->ic_addr1->istprdy == tprdy &&
				/*
				 * 4. Timeout in case of brane deth.
				 */
			       i-- )
				;
		}
	}

	/*
	 * Looking at the tape ready bit after doing a TAPECONF
	 * tells us whether the drive is there.
	 */

	if (itdebug)
		printf("itslave: tape ready bits 0x%x\n", 
					ic->ic_addr1->istprdy);

#ifdef IS_TAPEFIXES
	/*
	 * Allow freedom of public gathering for the downtrodden 1/4-inchers.
	 */
	return(ic->ic_addr1->istprdy & ISTPRDY_BITS & (1 << unit));
#else  IS_TAPEFIXES
	/*
	 * Hardwire support for only one tape drive.
	 */
	return(unit == 0 && ic->ic_addr1->istprdy & ISTPRDY_BITS & 1);
#endif IS_TAPEFIXES
}


/*
 * Attach tape device to controller.
 */

itattach(md)
	register struct mb_device *md;
{
	register struct is_ctlr *ic = is_ctlr + md->md_ctlr;
	register struct is_tape *it = is_tape + md->md_unit;

	if (isaltdebug) printf("->itattach");

	/* initialize the controller for this drive */

	if (iticmd(ic, it, IPCMD_TAPECONF, IPTAPEOPT_NORMAL, 0, 0, 0, 
				"tape drive initialization failed") < 0) {
		return;
	}

	/* set up tape struct and link into list of drives on controller */

	it->it_md = md;
	it->it_ic = ic;
	it->it_unit = md->md_unit;
	it->it_link = ic->ic_tape;
	ic->ic_tape = it;
	ic->ic_ntape++;

	if (isaltdebug) printf("itattach->\n");
#ifdef ISNOISY
	printf("it%d.\n", it->it_unit);
#endif ISNOISY
}


/*
 *  Open the tape.  
 *  Check relevant status fields.
 */
itopen(dev, flag)
	dev_t dev;
{
	int unit = itunit(dev);
	struct mb_device *md;
	register struct is_tape *it;
	register struct is_ctlr *ic;
	struct is_tapestat tpstat;

	if (unit >= NIT || (md = itinfo[unit]) == 0 || !md->md_alive)
		return ENXIO;

	it = is_tape + md->md_unit;
	ic = is_ctlr + md->md_ctlr;

	if (it->it_open) {
		/* if (itdebug) */
			printf("itopen: already opened, it->it_open %d\n", 
				it->it_open);
		return EIO;
	}

	it->it_open = 1;
	it->it_lasterr = 0;
	it->it_errcnt = 0;
	it->it_active = 0;
	it->it_err = 0;
	it->it_blkno = 0;
	it->it_lastiow = 0;
	it->it_nxrec = INF;

	/* 
	 * Check the drive status bits.
	 */

#ifdef notdef
	if (itcmd(it, IPCMD_TAPESTAT, 0, 0, 0, 0, (int)vtop(&tpstat), 
/************
	if (iticmd(ic, it, IPCMD_TAPESTAT, 0, 0, 0, (int)vtop(&tpstat), 
*************/
						"can't read tape status") < 0)
		goto err;
#else  notdef
	if (itcmd(it, IPCMD_TAPESTAT, 0, 0, 0, 0, (int)vtop(&tpstat), 
						"can't read tape status") >= 0)
	{
#endif notdef

	   if (itdebug)
		   printf("itopen: status byte 0 0x%x\n", tpstat.it_byte0);

	   if (tpstat.it_byte0 & IPBYTE0_EXBYTE0) {
		   if (tpstat.it_byte0 & IPBYTE0_CART)
			   printf("it%d: cartridge not in place\n.", unit);
		   else if (tpstat.it_byte0 & IPBYTE0_OFFLINE)
			   printf("it%d: drive off line\n.", unit);
		   else if (tpstat.it_byte0 & IPBYTE0_WRTPROT)
		   {
			   if (flag & FWRITE)
				   printf("it%d: write protected.\n", unit);
			   else
				   return 0;
		   }
		   else
			   printf("it%d: unknown error, ex0 %b.\n", unit,
				   tpstat.it_byte0, ISTPSTAT0_BITS);
	   }
	   else
		   return 0;
#ifdef notdef
err:
#else  notdef
	}
#endif notdef
	it->it_open = 0;
	/* does u.u_error get updated properly here? */
	return u.u_error ? u.u_error : EIO;
}


itstrategy(bp)
	register struct buf *bp;
{
	register struct is_tape *it = is_tape + itunit(bp->b_dev);
	register struct is_ctlr *ic = it->it_ic;
	int nblk;
	int s;

	if (bp != &itcbuf && bp->b_blkno < 0) {
		if (itdebug)
			printf("it%d: bad blkno, bn %d.\n",
				it->it_unit, bp->b_blkno);
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	s = splIS();
	if (it->it_actf == 0)
		it->it_actf = bp;
	else
		it->it_actl->av_forw = bp;
	it->it_actl = bp;
	if (!ic->ic_busy1)
		itstart(it);
	splx(s);
}


itread(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	return physio(itstrategy, &is_tape[itunit(dev)].it_rbuf,
		dev, B_READ, minphys, uio);
}

itwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	return physio(itstrategy, &is_tape[itunit(dev)].it_rbuf,
		dev, B_WRITE, minphys, uio);
}


/*ARGSUSED*/
itioctl(dev, cmd, addr, flag)
	dev_t dev;
	caddr_t addr;
{
	register struct is_tape *it = is_tape + itunit(dev);
	register callcount;
	register int tpcmd;
	struct buf *bp = &itcbuf;
	int fcount;
	int s;
	int tapenotrdy;
	struct is_tapestat tpstat;
	struct mtop *mtop;
	struct mtget *mtget;
	/*
	 * We depend on the values and order of the MT codes here.
	 * A code of 0 means illegal command.
	 */
	static mtops[] =
	 	{IPCMD_WRITEFM, IPCMD_READFM, 0, IPCMD_SEEKTAPE, 
	 	 0, IPCMD_REWIND, IPCMD_REWIND, IPCMD_TAPESTAT};

	u.u_error = 0;

	if (itdebug)
		printf("it: ioctl cmd 0x%x\n", (u_char) cmd);

	/*
	 * Get the cookie.
	 */
	
	s = splIS();
	while (it_ioctl_mutex & B_BUSY) {
		it_ioctl_mutex |= B_WANTED;
		if (itdebug)
			printf("it: sleeping for cookie\n");
		sleep((caddr_t)&it_ioctl_mutex, PRIBIO+1);
	}
	it_ioctl_mutex |= B_BUSY;
	if (itdebug)
		printf("it: got cookie\n");
	splx(s);

	/*
	 * Now we can safely play.
	 */

	switch (cmd) {
	 case MTIOCTOP:	/* tape operation */
		mtop = (struct mtop *)addr;

		if (itdebug) {
			printf("it: ioctl mtop 0x%x\n", (u_char) mtop->mt_op);
		}

		switch (mtop->mt_op) {
		 case MTWEOF:
			callcount = mtop->mt_count;
			fcount = 1;
			break;

		 case MTFSF: 
			callcount = 1;
			fcount = mtop->mt_count;
			break;

		 case MTBSF:
		 case MTBSR:
		 case MTFSR: 
			u.u_error = EINVAL;
			goto done;
			break;

		 case MTREW: 
		 case MTOFFL:
			callcount = 1;
			fcount = 1;
			break;

		 default:
			u.u_error = EINVAL;
			goto done;
		} /* switch (mtop->mt_op) */

		if (callcount <= 0 || fcount <= 0) {
			u.u_error = EINVAL;
			goto done;
		}

		tpcmd = mtops[mtop->mt_op];

		while (--callcount >= 0) {
			register int n;

			do {
				n = MIN(fcount, 0xffff);
				if (tpcmd == IPCMD_READFM) {
					if(itcmd(it,tpcmd,0,0,0,n,0,0) < 0)
						break;
				} else {
					if(itcmd(it,tpcmd,0,0,n,0,0,0) < 0)
						break;
				}
				fcount -= n;
			} while (fcount);

			if ((tpcmd == IPCMD_READFM || tpcmd == IPCMD_SEEKTAPE) 
					&& bp->b_resid) {
				u.u_error = EIO;
				goto done;
			}
			if (bp->b_flags & B_ERROR)
				break;
		}
		u.u_error = geterror(bp);
		break;

	 case MTIOCGET:
		/* error field gets ctlr status, for want of anything better */
		mtget = (struct mtget *) addr;
		if (itcmd(it, IPCMD_TAPESTAT, 0, 0, 0, 0, &tpstat, 0) == 0) {
			mtget->mt_dsreg = 0;	/* should be csr */
			mtget->mt_erreg = tpstat.it_byte1 << NBBY 
						+ tpstat.it_byte0;
			mtget->mt_resid = bp->b_resid;
		}
		break;

	 default:
		u.u_error = EINVAL;
		break;
	} /* switch(cmd) */
done:
	/*
	 * Give back the cookie.
	 */
	s =splIS();
	if (it_ioctl_mutex & B_WANTED) {
		if (itdebug)
			printf("waking up cookie\n");
		wakeup((caddr_t) &it_ioctl_mutex);
	}
	it_ioctl_mutex = 0;
	if (itdebug)
		printf("gave back cookie\n");
	splx(s);

	return u.u_error;
}


/*
 * Start up an operation on the drive.
 */

itstart(it)
	register struct is_tape *it;
{
	register struct is_ctlr *ic;
	register struct buf *bp;
	register i;
	u_char csr;
	daddr_t blkno;
	int nblk;

again:
	if ((bp = it->it_actf) == 0)		/* null buffer - bogus */
		return;

	/* get the controller for this drive */

	ic = it->it_ic;
	if (ic->ic_busy1)		/* controller busy, can't do is */
		return;

	if ((csr = ic->ic_addr1->iscsr) & ISCSR_BUSY) {
		/*
		 * This should never happen.
		 * So don't try to recover nicely, just drop is on the floor.
		 */
		printf("ic%d: itstart, controller busy, csr 0x%b.\n",
			ic->ic_ctlr, csr, ISCSR_BITS);
		it->it_active = 0;
		it->it_actf = bp->av_forw;
		bp->b_flags |= B_ERROR;
		iodone(bp);
		ISLOG('B', it->it_unit, csr);
		goto again;
	}

	blkno = bp->b_blkno;
	nblk = bp->b_bcount / DEV_BSIZE;

	if (itdebug)
		printf("itstart: blkno %d nblk %d (b_blkno %d, b_bcount %d)\n",
			blkno, nblk, bp->b_blkno, bp->b_bcount);

	/* check to see if we are responding to an I/O from itcmd */

	if (bp == &itcbuf) {
		IP1 = *(struct is_param *)bp->b_un.b_addr;	/* get iopb */
		if (itdebug)
			printf("it%d: cmd 0x%x.\n", it->it_unit, IP1.ip_cmd);
		it->it_active = ACTIVE_TCMD;
	} else {
		/* data xfer */
		if (itdebug)
			printf("is%d: itstart, io.\n", it->it_unit);

		/*
		 * Reading at eof returns 0 bytes.
		 * Reading past eof returns error.
		 * Writing sets eof.
		 */
		if (bp->b_flags & B_READ) {
			if (bp->b_blkno == it->it_nxrec) { 		
				/* reading at eof */
				bp->b_resid = bp->b_bcount;
				it->it_actf = bp->av_forw;
				iodone(bp);
				goto again;
			} else if (bp->b_blkno > it->it_nxrec) {
				/* can't read past eof */
				bp->b_flags |= B_ERROR;
				bp->b_resid = bp->b_bcount;
				it->it_actf = bp->av_forw;
				iodone(bp);
				goto again;
			}
		} else
			it->it_nxrec = bp->b_blkno + 1;

		if (bp->b_blkno != it->it_blkno) {
			printf("it%d: can't seek on cartridge tape.\n",
				it->it_unit);
			if (itdebug) printf("it_blkno %d\n", it->it_blkno);
			bp->b_flags |= B_ERROR;
			bp->b_resid = bp->b_bcount;
			it->it_actf = bp->av_forw;
			iodone(bp);
			goto again;
		}

		if (bp->b_flags & B_READ) {
			IP1.ip_cmd = IPCMD_READTAPE;
			it->it_lastiow = 0;
		} else {
			IP1.ip_cmd = IPCMD_WRITETAPE;
			it->it_lastiow = 1;
		}

		it->it_active = ACTIVE_TIO;

		/* fill in the iopb */

		IP1.ip_nsec0 = nblk;
		IP1.ip_nsec1 = nblk >> NBBY;
		i = bp->b_flags & B_PHYS
			? (int) bp->b_un.b_addr : (int) vtop(bp->b_un.b_addr);
		IP1.ip_buf0 = i;
		IP1.ip_buf1 = i >> NBBY;
		IP1.ip_buf2 = i >> NBBY * 2;
		IP1.ip_tapeopt = 0;
		IP1.ip_opt = IPOPT_NORMAL;
		IP1.ip_stat = 0;
		IP1.ip_err = 0;
		IP1.ip_drive = it->it_unit + 4;	/* phys unit = logical + 4 */
		IP1.ip_dma = IS_DMA;
		IP1.ip_reladdr0 = 0;
		IP1.ip_reladdr1 = 0;
		i = (int)ic->ic_addr1 - MBIO_VA;
		IP1.ip_ioaddr0 = i;
		IP1.ip_ioaddr1 = i >> NBBY;

		ISLOG('X', it->it_unit, csr);
	}

	ic->ic_busy1 = 1;
	ic->ic_unit1 = it->it_unit;

	/* start up command */

#ifdef M68020
	flushWriteQueue();
#endif M68020
	ic->ic_addr1->iscsr = ISCSR_GO | ISCSR_BUS ;
}


/* 
 * We return 0 if was spurious, one if legit interrupt.
 */
itintr(ctlr, csr)
	u_char csr;
{
	register struct is_ctlr *ic = is_ctlr + ctlr;
	register struct is_tape *it = is_tape + ic->ic_unit1;
	register struct buf *bp;
	register struct is_device *ic_reg = ic->ic_addr1;	/* our regs */
	u_char  intunit;			/* unit interrupting us */
	u_char  tprdy;				/* tape ready bits */
	int i;
	u_char stat;

	ISLOG('I', ic->ic_ctlr, csr);
	if (itdebug)
		printf("ic%d: itintr int, csr 0x%b.\n",
				ic->ic_ctlr, csr, ISCSR_BITS);


	/* get 'em before they go way from the CLRI */

	intunit = ic_reg->isintunit & ISINTUNIT_BITS;
	tprdy = ic_reg->istprdy;
	stat = IP1.ip_stat;

	/* sanity check - may not want to keep in */
	if (intunit < 4 && itdebug)
		/* wasn't tape unit that interrupted */
		cdebugger("tape unit didn't int");

	/*
	 * Clear that noisy interrupt.
	 */

	ic_reg->iscsr = ISCSR_CLRI;

	/*
	 * Wait until the CLRI is seen.
	 * Any change in any of these fields means that the clri was acked.
	 * Example: intunit changes, which means that we have
	 * the same int type on a different unit.
	 */

	i = 10000;
	while ((ic_reg->iscsr & ~ISCSR_BUSY) == (csr & ~ISCSR_BUSY) &&
	       ic_reg->isintunit == intunit &&
	       ic_reg->istprdy == tprdy && i-- )	 /* wait for done */
		;

	bp = it->it_actf;
	if ((bp != &itcbuf) && (csr & ISCSR_SI)) {
		/*
		 * Were running a data xfer command: don't expect SI.
		 *
		 * May want to check to see if cartridge has gone offline
		 * here.
		 */
		printf("ic%d: spurious interrupt - drive offline?\n", 
				ic->ic_ctlr);
		return(0);
	}


	/*
	 * CI interrupt or SI from a legit SI command - how did we do?
	 */

	switch (it->it_active) {
	 case ACTIVE_TCMD:

		/* non-data transfer command */

		if (bp != &itcbuf) {	/* sanity check */
			printf("it%d: ACTIVE_TCMD consistency\n",it->it_unit);
			cdebugger(0);
			return 1;
		}

		/* 
		 * Could be a status change too early - indicates
		 * drive offline.
		 */

		if (csr & ISCSR_SI) {
			printf("drive offline!\n");
			return 1;
		}

		/*
		 * If it's a command that only really completes when
		 * we recieve a status change interrupt (SI), make
		 * sure that we have no error, and wait for the SI.
		 *
		 * Note that if there was an error we fall through
		 * and handle it by looking at ip_stat.
		 */

		if (IP1.ip_cmd == IPCMD_RETENTION || IP1.ip_cmd == IPCMD_REWIND
				|| IP1.ip_cmd == IPCMD_SEEKTAPE) {
			if (stat == IPSTAT_DONE) {
				/* cool */
				it->it_active = ACTIVE_TSICMD;
				return(1);		/* wait for SI */
			}
		}

		/*
		 * At this point we are looking at non-SI commands
		 * or SI commands that failed initially
		 * or we have SI interrupt for an SI command.
		 */

		/*
		 * Copy back updated iopb.
		 */

		*(struct is_param *)bp->b_un.b_addr = IP1;
		
		switch (stat) {

		 case IPSTAT_DONE:
			/*
			 * Command was successful.
			 */
			bp->b_resid = bp->b_bcount & DEV_BMASK;

			if (itdebug) {
				printf("it: updated count %d\n",
					IP1.ip_nsec1 << NBBY + IP1.ip_nsec0);
				itprint(bp->b_dev, bp, &IP, 0);
			}
			break;

		  case IPSTAT_ERR:
		  default:
			/*
			 * Oops.
			 */
			
			/*
			 * Could be that we want to update b_resid here 
			 * if we detected a FM, EOT, etc.
			 */
			if (itdebug)
				itprint(bp->b_dev, bp, &IP1, "tape cmd failed");

			bp->b_flags |= B_ERROR;
			break;
		}/* switch (IP1.ip_stat) */

		break;

	 case ACTIVE_TSICMD:

		/* non-data transfer command waiting for status change int */

		if (!(csr & ISCSR_SI)) {		/* sanity check */
			itprint(bp->b_dev, bp, &IP1, "TSICMD botch");
			return(1);
		}

		/*
		 * Copy back updated iopb.
		 */

		*(struct is_param *)bp->b_un.b_addr = IP1;

		switch (stat) {

		 case IPSTAT_DONE:
			/*
			 * Command was successful.
			 */
			bp->b_resid = bp->b_bcount & DEV_BMASK;
			if (itdebug) {
				printf("it: updated count %d\n",
					IP1.ip_nsec1 << NBBY + IP1.ip_nsec0);
				itprint(bp->b_dev, bp, &IP, 0);
			}

			/* adjust software notion of current block number */

			switch (IP1.ip_cmd) {
			 case IPCMD_RETENTION:
			 case IPCMD_REWIND:
				it->it_blkno = 0;
				break;

			 case IPCMD_SEEKTAPE:
				it->it_blkno +=  (bp->b_bcount / DEV_BSIZE);
				break;

			 default:
				/*NOTREACHED*/
				printf("itintr: ACTIVE_TSICMD botch\n");
				break;
			}
			break;

		  case IPSTAT_ERR:
		  default:
			/*
			 * Oops.
			 */
			
			/*
			 * Could be that we want to update b_resid here 
			 * if we detected a FM, EOT, etc.
			 */
			itprint(bp->b_dev, bp, &IP, "tape command fails");
			bp->b_flags |= B_ERROR;
			break;

		} /* switch (IP1.ip_stat) */

		break;

	 case ACTIVE_TIO:
		/* data transfer */

		switch (IP1.ip_stat) {

		 case IPSTAT_DONE:
			/*
			 * command was successful
			 */
			bp->b_resid = bp->b_bcount & DEV_BMASK;
			if (itdebug)
				itprint(bp->b_dev, bp, &IP,
					bp->b_flags & B_READ ?
					"read" : "write");
			break;

		  case IPSTAT_ERR:
		  default:
			/*
			 * Oops.
			 */

			switch (IP1.ip_err) {

			 case IPERR_FILEMARK:
				/*
				 * This says that there is a file mark
				 * following the block just read.
				 * Not an error, as far as we're concerned.
				 * However, we note the fact that the next
				 * block is EOF.
				 */
				bp->b_resid = bp->b_bcount & DEV_BMASK;
				if (itdebug)
					itprint(bp->b_dev, bp, &IP,
						bp->b_flags & B_READ ?
						"read" : "write");
				it->it_nxrec = it->it_blkno + 1;
				break;

			 case IPERR_EOT:
			 case IPERR_FM:
			 case IPERR_NODATA:
				/*
				 * Does the updated count really tell us
				 * the truth here?  Check.
				 */
				/* if (itdebug) */
					printf("TIO/EOT/FM: didn't xfer %d\n",
					 (IP1.ip_nsec1 << NBBY) + IP1.ip_nsec0);
				bp->b_resid = (IP1.ip_nsec1 << NBBY) 
							+ IP1.ip_nsec0;
				break;
			
			 default:
				bp->b_flags |= B_ERROR;
				break;
			}/* switch (IP1.ip_err) */

			break;
		}/* switch (IP1.ip_stat) */

		it->it_blkno += (bp->b_bcount - bp->b_resid) / DEV_BSIZE;
		if (itdebug) printf("new blkno %d\n", it->it_blkno);
		break;

	}/* switch (it->it_active) */

	it->it_actf = bp->av_forw;
	it->it_active = 0;
	ic->ic_busy1 = 0;
	iodone(bp);

start_new_op:
	/*
	 * If controller not busy, start next operation.
	 */

	for (it = ic->ic_tape; !ic->ic_busy1 && it; it = it->it_link)
		if (it->it_md->md_alive && !it->it_active && it->it_actf != 0)
			itstart(it);

	return 1;
}


/*
 * Reset for the tape half.
 * Assume that the board has already been reset.
 * Need to reconfigure the controller for each active device, and
 * then restart any pending i/o.
 */
itreset(ic)
	struct is_ctlr *ic;
{
	register struct is_tape *it = ic->ic_tape;
	register int i;

	for (it = ic->ic_tape; it; it = it->it_link) {
		if (!it->it_md->md_alive)
			continue;
		/*
		 * Reconfigure the controller for the drive.
		 */
		if (iticmd(ic, it, IPCMD_TAPECONF, IPTAPEOPT_NORMAL, 0, 0, 0, 
				"tape drive initialization failed") < 0) {
			/* this may be a panic-type situation */
			/* else may want to take corrective action here */
			return;
		}

		it->it_active = 0;
		(void) itstart(it);
	}
}



itprint(dev, bp, ip, mesg)
	dev_t dev;
	register struct buf *bp;
	register struct is_param *ip;
	char *mesg;
{
	if (bp == 0 || bp == &itcbuf)
		printf("is%d: ", itunit(dev));
	else
		printf("is%d: ", itunit(dev));
		/***
		printf("is%d%x: ", itunit(dev), itpart(dev));
		***/
	if (mesg)
		printf("%s, ", mesg);
	if (bp == 0 || bp == &itcbuf)
		printf("cmd 0x%x, ", ip->ip_cmd);
	else
		printf("bn %d, ", bp->b_blkno);

	switch (ip->ip_stat) {
	case IPSTAT_DONE:
		if (ip->ip_err != 0) {
			printf("soft error 0x%b, %d retries.\n",
				ip->ip_err, IPERR_BITS,
				ip->ip_err & IPERR_RETRY);
		} else
			printf("no error.\n");
		break;
	case IPSTAT_ERR:
		printf("error 0x%x \"%s.\"\n",
			ip->ip_err, iserror(ip->ip_err));
		printf("Iopb:%x, cmd 0x%x, from it%d\n",
			ip, ip->ip_cmd, itunit(dev));

		break;
	case IPSTAT_BUSY:
		printf("controller busy.\n");
		break;
	default:
		printf("unknown ip_stat 0x%x.\n", ip->ip_stat);
	}
}


/*
 * Run a controller command in asynchronous mode.
 */
itcmd(it, cmd, opt, sec, nsec, cyl, addr, mesg)
	register struct is_tape *it;
	caddr_t addr;
	char *mesg;
{
	register struct is_ctlr *ic = it->it_ic;
	register struct buf *bp = &itcbuf;
	static struct is_param ip;		/* iopb for commands */
	int i;
	int s;
	u_char stat;
	int b_flags;


	if (itdebug) {
		printf ("itcmd(ic=0x%x, it=0x%x, cmd=0x%x, opt=0x%x, sec=0x%x,\n",
			ic, it, cmd, opt, sec);
		printf ("      nsec=0x%x, cyl=0x%x, addr=0x%x, mesg=0x%x)\n",
			nsec, cyl, addr, mesg);
	}
	s = splIS();
	while (bp->b_flags & B_BUSY) {
		bp->b_flags |= B_WANTED;
		sleep((caddr_t)bp, PRIBIO+1);	/* let iowait() go first */
	}
	bp->b_flags = B_BUSY;
	splx(s);

	ip.ip_cmd = cmd;
	ip.ip_tapeopt = opt;
	ip.ip_cyl0 = cyl;
	ip.ip_cyl1 = cyl >> NBBY;
	ip.ip_sec0 = sec;
	ip.ip_sec1 = sec >> NBBY;
	ip.ip_nsec0 = nsec;
	ip.ip_nsec1 = nsec >> NBBY;
	ip.ip_buf0 = (int)addr;
	ip.ip_buf1 = (int)addr >> NBBY;
	ip.ip_buf2 = (int)addr >> (NBBY * 2);
	if (itdebug)
		printf("addr=%X, b0=%x, b1=%x, b2=%x\n",
			addr, ip.ip_buf0, ip.ip_buf1, ip.ip_buf2);
	ip.ip_opt = IPOPT_NORMAL;
	ip.ip_stat = 0;
	ip.ip_err = 0;
	ip.ip_drive = it->it_unit + 4;	/* physical unit = logical + 4 */
	ip.ip_dma = (cmd == IPCMD_TAPECONF) ? IT_TAPETIMEOUT: IS_DMA;
	ip.ip_reladdr0 = 0;
	ip.ip_reladdr1 = 0;
	i = (u_int) ic->ic_addr1 - MBIO_VA;
	ip.ip_ioaddr0 = i;
	ip.ip_ioaddr1 = i >> NBBY;
	bp->b_un.b_addr = (caddr_t)&ip;
	itstrategy(bp);
	iowait(bp);

	b_flags = bp->b_flags;		/* capture command status */

#ifdef DumbCode
	/* the following is stupid because ip reflects the initial state
	   of the iopb, not the final state (it's copied to the controller's
	   own iopb, and then executed there, so the original does not
	   get the completion code, etc.
	   we could copy the completed iopb back.
	   truthfully, the whole business with copying the iopb in seems
	   bogus. oh well.
	 */

	if (ip.ip_stat != IPSTAT_DONE) {
		if (mesg != 0)
			itprint(itmkdev(it->it_unit), 0,
				&ip, mesg);
		if (ip.ip_stat == IPSTAT_ERR)
			it->it_err = ip.ip_err;
	}
#endif DumbCode

	s = splIS();
	if (bp->b_flags & B_WANTED)
		wakeup((caddr_t)bp);
	bp->b_flags = 0;
	splx(s);

	return(b_flags & B_ERROR ? -1 : 0);
}


/*
 * Run a tape command in polled (synchronous) mode.
 * We poke the controller directly, poll for completion
 * and clear interrupts before returning.
 *
 * This routine does not work when we try to execute an a7 (configure)
 * command - after we clear the completion interrupt, we get a
 * status change interrupt which cannot be cleared.
 *
 * bog: This explains why itattach() calls iticmd to configure its
 * tape drives. Grr.
 */
iticmd(ic, it, cmd, opt, sec, nsec, addr, mesg)
	register struct is_ctlr *ic;
	register struct is_tape *it;
	caddr_t addr;
	char *mesg;
{
	register struct buf *bp = &itcbuf;
	register struct is_param *ip;
	register i;
	int s, stat;
	int x;
	char csr = 0;
	char tprdy = 0;
	char intunit = 0;		/* interrupting unit */

	if (isaltdebug)
		printf("iticmd(0x%x, 0x%x, 0x%x, 0x%x, %d, %d, 0x%x, 0x%x\n",
			ic, it, cmd, opt, sec, nsec, addr, mesg);

	if (ic->ic_busy1) {
		printf("id%d: iscmd, controller busy\n",it->it_unit);
		if (isaltdebug) cdebugger(0);
		return -1;
	}
	ic->ic_busy1 = 1;
	ip = &ic->ic_param1;

	/*
	 * Gain access to the controller if its csr shows busy.
	 *
	 * Actually, the controller can only be busy if a command
	 * has gone berserk or we have some kind of bug.
	 * Thus, we should do a controller reset if the command
	 * doesn't time out here.  There may be a recursion problem
	 * here - if we call isreset() we may step on the parameter block
	 * that we are trying to execute here.
	 *
	 * Think about this.
	 */

	for (i=ISTIMEOUTDELAY; --i && (ic->ic_addr1->iscsr & ISCSR_BUSY != 0););
	if (i == 0) {
		/* do this for the time being */
		printf("iscmd: controller is stuck - consider rebooting\n");
		ic->ic_busy1 = 0;
		return -1;
	}

#ifdef IS_TAPEFIXES
	/*
	 * It is probably a bad idea not to initialize the IOPB.
	 */
	bclear(ip, sizeof (*ip));
#endif IS_TAPEFIXES

	ip->ip_cmd = cmd;
	ip->ip_tapeopt = opt;
	ip->ip_sec0 = sec;
	ip->ip_sec1 = sec >> NBBY;
	ip->ip_nsec0 = nsec;
	ip->ip_nsec1 = nsec >> NBBY;
	ip->ip_buf0 = (int)addr;
	ip->ip_buf1 = (int)addr >> NBBY;
	ip->ip_buf2 = (int)addr >> (NBBY * 2);
	if (itdebug)
		printf("addr=%X, b0=%x, b1=%x, b2=%x\n",
			addr, ip->ip_buf0, ip->ip_buf1, ip->ip_buf2);
	ip->ip_opt = IPOPT_NORMAL;
	ip->ip_stat = 0;
	ip->ip_err = 0;
	ip->ip_drive = it->it_unit + 4;	/* physical unit = logical + 4 */
	ip->ip_dma = (cmd == IPCMD_TAPECONF) ? IT_TAPETIMEOUT: IS_DMA;
	ip->ip_reladdr0 = 0;
	ip->ip_reladdr1 = 0;
	i = (u_int) ic->ic_addr1 - MBIO_VA;
	ip->ip_ioaddr0 = i;
	ip->ip_ioaddr1 = i >> NBBY;

	s = splIS(); 		/* wait until the last moment to spl */

#ifdef M68020
	flushWriteQueue();
#endif M68020
	ic->ic_addr1->iscsr = ISCSR_GO | ISCSR_BUS ;

	iswaituntildone(ip, &ic->ic_addr1->iscsr);

	stat = ip->ip_stat;		/* Capture status from iopb */
	csr = ic->ic_addr1->iscsr;		/* stash away for debug */
	tprdy = ic->ic_addr1->istprdy;
	intunit = ic->ic_addr1->isintunit;

	/* 
	 * NOTE: need to watch for SI here.
	 * also need to make sure that the interrupting unit
	 * is the one we ran the command on .
	 */

	ic->ic_addr1->iscsr = ISCSR_CLRI;	/* clear interrupt condition */

	/*
	 * Explanation: any change in any of these fields
	 * means that the clri was acked.
	 * Example: intunit changes, which means that we have
	 * the same int type on a different unit.
	 */

	i = ISTIMEOUTDELAY;
	while ((ic->ic_addr1->iscsr & ~ISCSR_BUSY) == (csr & ~ISCSR_BUSY) &&
	       ic->ic_addr1->isintunit == intunit &&
	       ic->ic_addr1->istprdy == tprdy && --i )	 /* wait for done */
		;

	if (isaltdebug) {
		printf("alternate csr 0x%x\n", (ic->ic_addr + 1)->iscsr);
	}

	it->it_err = ip->ip_err;
	/*
	 * It would be nice if we cleared errors here 
	 * to leave the Storager in a known state...
	 */
	ic->ic_busy1 = 0;
	splx(s);

	/* note: could have a problem with ip/it changing under us here ... */
	if (i == 0) {	
		/* timed out */
		printf("it%t: ", it->it_unit);
		if (mesg != 0)
			printf("%s, ", mesg);
		printf("timeout, csr 0x%b, stat 0x%x.\n", csr, ISCSR_BITS,
			stat);
		return -1;

	} else if (stat != IPSTAT_DONE) {
		if (mesg != 0)
			itprint(itmkdev(it->it_unit), 0,
				ip, mesg);
		return -1;
	}

	return 0;
failure:
	return -1;
}



/*ARGSUSED*/
itclose(dev, flag)
	dev_t dev;
{
	register struct is_tape *it = is_tape + itunit(dev);
	register struct is_ctlr *ic = it->it_ic;
	char rewound = 0;

	/*
	 * If we were opened for writing, must write two file marks.
	 */

	if (flag == FWRITE || it->it_lastiow) {
		if (itcmd(it, IPCMD_WRITEFM, IPTAPEOPT_NORMAL, 0, 2, 0, 0, 
				"can't write closing file mark") < 0)
			goto out;
	}

	/*
	 * Rewind if necessary.
	 */

	if (!(minor(dev) & IT_NOREWIND))
		if (itcmd(it, IPCMD_REWIND, IPTAPEOPT_NORMAL, 0, 1, 0, 0, 
						"can't rewind") < 0)
			goto out;
out:
	it->it_open = 0;
	return u.u_error;
}
#endif NIT > 0
