static char rcsid[] = "$Header: rt.c,v 820.1 86/12/04 19:56:22 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.				*
*									*
************************************************************************/

/*
 * Rimfire 44/45 controller
 *
 * Tape driver
 */

#include "rt.h"
#if NRT > 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 "../s32dev/rim_ioctl.h"
#include "../h/user.h"
#include "../h/uio.h"
#include "../h/mtio.h"			/* mag tape ioctl defs */
#include "../h/kernel.h"
#include "../s32/dkio.h"
#include "../s32dev/mbvar.h"
#include "../s32dev/rfreg.h"
#include "../s32dev/rfvar.h"

struct mb_device *rtinfo[NRT];

struct rf_info rf_tape[NRT];

long rt_ioctl_mutex;			/* b_flags-like variable for mutual
					 * exclusion on rt_ioctl().
					 */

rtslave(md, mc)
	struct mb_device *md;
	struct mb_ctlr *mc;
{
	register struct rf_ctlr *rc = rf_ctlr + mc->mc_ctlr;
	struct rf_info ri;

	if (rtdebug) {
		printf("rtslave: probing for unit %d\n", md->md_unit);
	}

	ri.ri_istape = 1;
	ri.ri_md = md;

	/*
	 * The reset always succeeds on a mag tape whether it
	 * exists or not.  But nothing else seems to work, either.
	 * So we just pretend.
	 */
	if (rc->rc_ctape) {
		rficmd(rc, &ri, TPCMD_RESET, CTL_NORMAL, 0, 0, 0, 0, 0, 0);
	}

	rficmd(rc, &ri, TPCMD_STATUS, CTL_NORMAL, 0, 0, 0, 0, 0, 0);

	if (rc->rc_ctape) { 	/* rim44 */
		/*
		 * Now this is what you call a crock.
		 *
		 * The brain-damaged Rim44 controller can't tell if
		 * the cartridge drive is there or not.
		 * We coerce it into seeing one at unit 4 (1st
		 * non-magtape unit).
		 */
		if (md->md_unit == 4) {
			if (rtdebug)
				printf("rt: forced find of cartridge tape\n");
			return(1);
		} else {
			if (rtdebug)
				printf("rt: forced miss of cartridge tape\n");
			return(0);
		}
	} else { 		/* rim45 */
		return(md->md_unit == 0 || md->md_unit == 1);
	}
}

rtattach(md)
	register struct mb_device *md;
{
	register struct rf_ctlr *rc = rf_ctlr + md->md_ctlr;
	register struct rf_info *ri = rf_tape + md->md_unit;

	if (rtdebug) {
		printf("rtattach: md->md_unit = %d, ri = 0x%x md = 0x%x\n",
				md->md_unit, (int) ri, (int) md);
	}

	ri->ri_istape = 1;
	ri->rt_1600bpi = 1;	/* 1600 bpi default - fixes the XTW., tar bug */
	ri->ri_md = md;
	ri->ri_link = rc->rc_tape;
	rc->rc_tape = ri;
	rc->rc_tact = ri;
	rc->rc_ntape++;
	rfdriver.mdr_dname[1] = 'd';	/* for iostat */
}

/*
 * When the tape is rewinding, all we get is a tape busy status.
 * So we kludge it by saving the time of the last rewind operation,
 * and give ourselves 'rtrewtime' seconds for it to complete.
 * This is better than always waiting a fixed amount of time for the tape
 * to become ready, whether or not it has been active recently.
 */
rtopen(dev, flag)
	dev_t dev;
{
	int unit = rtunit(dev);
	register struct rf_info *ri;
	register struct rf_ctlr *rc;
	char is1600bpi = minor(dev) & RT_1600BPI ? 1 : 0;
	char except = 0;
	char hasnotape = 0;

	if (rtdebug) {
		printf("rtopen: unit %d\n", unit);
	}

	if (unit >= NRT) {
		if (rtdebug)
			printf("rtopen: unit %d too big\n");
		return ENXIO;
	}

	ri = rf_tape + unit;

	if (ri->ri_md == 0 || !ri->ri_alive) {
		if (rtdebug)
			printf("rtopen: ri 0x%x ri->ri_md %d ri->ri_alive %d\n",
				(int) ri, ri->ri_md, ri->ri_alive);
		return ENXIO;
	}
	rc = rf_ctlr + ri->ri_ctlr;
	if (ri->rt_open) {
		if (rtdebug)
			printf("rtopen: already opened, ri->rt_open %d\n", 
				ri->rt_open);
		return EIO;
	}
	ri->rt_open = 1;
	ri->rt_err = 0;
	ri->rt_blkno = 0;
	ri->rt_lastiow = 0;
	ri->rt_nxrec = INF;
again:
	rfcmd(ri, TPCMD_STATUS, CTL_NORMAL, 0, 0, 0, 0, 0);
	if (u.u_error) {
		if (rtdebug)
			printf("rtopen: status failed\n");
		goto err;
	}

	/*
	 * For speed, don't reset the drive unless we need to.
	 * But also take care not to loop infinitely.
	 */

	/* 
	 * RIM44 fix
	 *
	 * We need to reset the drive whenever a new cartridge is inserted.
	 * If a tape operation results in error, e.g. an open on an empty drive,
	 * then presumably a new cartridge will be inserted and we will have to
	 * reset the drive. Unfortunately the CPDST_EXCEPT flag is NOT set on 
	 * the open following the error, at least on the cartridge tape drive 
	 * that I am using. 
	 * So we force the reset when the error is detected, i.e. by 
	 * examining the drive status flag "dst" and reseting the drive if both 
	 * the drive ready and hastape bits ( CPDST_RDY and CPDST_HASTAPE ) are
	 * not set. 
	 *
	 * 26 Feb, 1987							BW
	 */

	if( rc->rc_ctape && ( ( ri->rt_dst & CPDST_EXCEPT ) || 
	  		     !( ri->rt_dst & CPDST_RDY ) || 
			     !( ri->rt_dst & CPDST_HASTAPE ) ) )
	{
		if( ( ( ri->rt_dst & CPDST_HASTAPE ) == 0 ) && hasnotape++ )
		{
			printf("rt%d: no tape.\n", unit);
			goto err;
		}
		if( ( ri->rt_dst & CPDST_EXCEPT ) &&  except++ ) 
		{
			printf("rt%d: can't clear exception.\n", unit);
			goto err;
		}

		if (rtdebug)
			printf("rtopen: about to reset tape drive\n");

		rfcmd(ri, TPCMD_RESET, CTL_NORMAL, 0, 0, 0, 0, 0);

		if (u.u_error) {
			if (rtdebug)
				printf("rtopen: reset failed\n");
			goto err;
		}
		goto again;
	}
	if (!rc->rc_ctape && (ri->rt_dst & TPDST_ONLINE) == 0) {
		printf("rt%d: off line.\n", unit);
		goto err;
	}
	if (! rtwaitrewind(rc, ri)) {
		printf("rt%d: not ready.\n", unit);
		goto err;
	}
	if (flag & FWRITE &&
	    ri->rt_dst & (rc->rc_ctape ? CPDST_WRTPROT : TPDST_WRTPROT)) {
		printf("rt%d: write protected.\n", unit);
		goto err;
	}
	if (!rc->rc_ctape && (ri->rt_dst & TPDST_LOADPT) == 0 &&
	    is1600bpi != ri->rt_1600bpi) {
		printf("rt%d: can't change desity in mid tape.\n", unit);
		goto err;
	}
	if (rc->rc_ctape) {
		if (minor(dev) & RT_QIC24) {
			rfcmd(ri, TPCMD_PASS, CTL_NORMAL, 0, 0, CP_QIC24, 0, 0);
			if (u.u_error) {
				printf("rtopen: upgrade Rimfire 44 NOW!\n");
				goto err;
			}
		}
	}
	ri->rt_1600bpi = is1600bpi;
	return 0;
err:
	ri->rt_open = 0;
	return u.u_error ? u.u_error : EIO;
}

/*ARGSUSED*/
rtclose(dev, flag)
	dev_t dev;
{
	register struct rf_info *ri = rf_tape + rtunit(dev);
	register struct rf_ctlr *rc  = rf_ctlr + ri->ri_ctlr;
	char rewound = 0;

	if (rc->rc_ctape && ri->rt_blkno > 0) {
		/*
		 * We're in an open ended sequence:
		 *
		 * If we did a write, than use OETRM to terminate
		 * the open ended sequence, which also writes a file mark.
		 *
		 * If not writing, then [TPCMD_OETRM] causes a forward
		 * search for the file mark.  So if we are going to rewind,
		 * then use a reset to terminate the sequence instead.
		 */
		if (ri->rt_lastiow || minor(dev) & RT_NOREWIND)
			rfcmd(ri, TPCMD_OETRM, CTL_NORMAL, 0, 0, 0, 0, 0);
		else {
			rfcmd(ri, TPCMD_RESET, CTL_NORMAL, 0, 0, 0, 0, 0);
			rewound = 1;
		}
		if (u.u_error)
			goto out;
	} else {
		/*
		 * A mag tape, or a cartridge but not in open ended sequence:
		 *
		 * If we're opened for writing, or reading and writing and
		 * the last operation was a write, then write the standard
		 * two file marks and back space over the second,
		 *
		 * If this is a cartridge tape, then we can't back up,
		 * so just write a single file mark.
		 */
		if (flag == FWRITE || ri->rt_lastiow) {
			rfcmd(ri, TPCMD_WRITEFM, CTL_NORMAL, 0, 0, 0, 0, 0);
			if (!rc->rc_ctape) {
				rfcmd(ri, TPCMD_WRITEFM, CTL_NORMAL,
					0, 0, 0, 0, 0);
				rfcmd(ri, TPCMD_SPACE, CTL_NORMAL|CTL_REVERSE,
					0, 0, 0, 1, 0);
			}
			if (u.u_error)
				goto out;
		}
	}
	if ((minor(dev) & RT_NOREWIND) == 0) {
		if (!rewound)
			rfcmd(ri, TPCMD_REWIND, CTL_NORMAL, 0, 0, 0, 0, 0);
		ri->rt_rewtime = time.tv_sec;
	}
out:
	ri->rt_open = 0;
	return u.u_error;
}

rtstrategy(bp)
	struct buf *bp;
{
	struct rf_info *ri = rf_tape + rtunit(bp->b_dev);
	struct rf_ctlr *rc = rf_ctlr + ri->ri_ctlr;
	int s;

	if (bp != &rfcbuf && bp->b_blkno < 0) {
		if (rfdebug || rtdebug)
			printf("rt%d: bad blkno, bn %d.\n",
				ri->ri_unit, bp->b_blkno);
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	s = spl5();
	if (ri->rt_actf == 0)
		ri->rt_actf = bp;
	else
		ri->rt_actl->av_forw = bp;
	ri->rt_actl = bp;
	if (!rc->rc_busy)
		rfstart(rc);
	splx(s);
}

rtread(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	rtphys(dev, uio);
	return physio(rtstrategy, &rf_tape[rtunit(dev)].ri_rbuf,
		dev, B_READ, minphys, uio);
}

rtwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	rtphys(dev, uio);
	return physio(rtstrategy, &rf_tape[rtunit(dev)].ri_rbuf,
		dev, B_WRITE, minphys, uio);
}

/*
 * Before a raw transfer, set up block number and nxrec so rfstart()
 * won't do any seeks.  However, if we get an error, the tape will
 * be backed up before retrying, which is what we want.
 */
rtphys(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	struct rf_info *ri = rf_tape + rtunit(dev);
	int blkno = bdbtofsb(uio->uio_offset >> DEV_BSHIFT);

	if (rtdebug) 
		printf("rtphys: unit %d ri 0x%x blkno %d\n",
				rtunit(dev), (u_long) ri, blkno);
	ri->rt_blkno = blkno;
	ri->rt_nxrec = blkno + 1;
}

/*
 * Pretty print tape error
 */
rtprint(ri, rp, bp, mesg)
	register struct rf_info *ri;
	register union rf_pb *rp;
	register struct buf *bp;
	char *mesg;
{
	register err = rp->pb_d.dp_cst & CST_ERROR;

	printf("rt%d: %s, ", ri->ri_unit, mesg);
	if (bp == 0 || bp == &rfcbuf)
		printf("cmd 0x%x, ", rp->pb_t.tp_cmd);
	else
		printf("%s, ", rp->pb_t.tp_cmd == TPCMD_SPACEF ? "seek" :
			(bp->b_flags & B_READ ? "read" : "write"));
	if (err != 0 && err != ERR_SOFT)
		printf("error 0x%x \"%s,\"\n     ", err, rferror(err));
	printf("cst 0x%b, ", rp->pb_t.tp_cst, CST_BITS);
	if (rf_ctlr[ri->ri_ctlr].rc_ctape)
		printf("dst 0x%b,\n     mst 0x%b", rp->pb_t.tp_dst, CPDST_BITS,
			rp->pb_t.tp_mst, TPMST_BITS);
	else {
		printf("dst 0x%b", rp->pb_t.tp_dst, TPDST_BITS);
		if ((rfdebug || rtdebug) && rp->pb_t.tp_retcnt != 0)
			printf(",\n     retcnt %d", rp->pb_t.tp_retcnt);
	}
	printf(".\n");
}

/*
 * Pretty print tape error for debug - mag tape only.
 */
rtdebugprint(rp, bp, mesg)
	register union rf_pb *rp;
	register struct buf *bp;
	char *mesg;
{
	register err = rp->pb_d.dp_cst & CST_ERROR;

	printf("rt: %s, ", mesg);
	if (bp == 0 || bp == &rfcbuf)
		printf("cmd 0x%x, ", rp->pb_t.tp_cmd);
	else
		printf("%s, ", rp->pb_t.tp_cmd == TPCMD_SPACEF ? "seek" :
			(bp->b_flags & B_READ ? "read" : "write"));
	if (err != 0 && err != ERR_SOFT)
		printf("error 0x%x \"%s,\"\n     ", err, rferror(err));

	printf("cst 0x%b, ", rp->pb_t.tp_cst, CST_BITS);
	printf("dst 0x%b", rp->pb_t.tp_dst, TPDST_BITS);
	if ((rtdebug || rfdebug) && rp->pb_t.tp_retcnt != 0)
		printf(",\n     retcnt %d", rp->pb_t.tp_retcnt);
	printf(".\n");
}

/* Unfortunately, the network router has used the name rtioctl (jam 8/23/84) */

/*
 * We do not allow all ioctls for the cartridge tape, as there are
 * certain things it just won't do.
 */

/*ARGSUSED*/
rt_ioctl(dev, cmd, addr, flag)
	dev_t dev;
	caddr_t addr;
{
	register struct rf_info *ri = rf_tape + rtunit(dev);
	register struct rf_ctlr *rc = rf_ctlr + ri->ri_ctlr;
	register callcount;
	register int op;
	struct buf *bp = &rfcbuf;
	int fcount;
	int s;
	int tapenotrdy;
	struct mtop *mtop;
	struct mtget *mtget;
	/* we depend of the values and order of the MT codes here */
	static mtops[] =
	 	{TPCMD_WRITEFM, TPCMD_SEARCHFM, TPCMD_SEARCHFM, TPCMD_SPACEF, 
	 	 TPCMD_SPACEF, TPCMD_REWIND, TPCMD_OFFLINE, TPCMD_STATUS};
	int ctl = CTL_NORMAL;			/* control byte for tape op */

	u.u_error = 0;

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

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

	/*
	 * Now we can safely play.
	 */

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

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

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

		 case MTBSF:
			ctl |= CTL_REVERSE;
			if (rc->rc_ctape) { 		/* can't do it */
				u.u_error = EINVAL;
				goto done;
			}
		 case MTFSF: 

			/*
			 * Rim45 forward scans file marks one at a time
			 * whereas Rim44 can forward scan many files marks
			 *
			 * e.g.
			 * to scan forward past N files we call rfcmd N times
			 * with the count set to 1 for rim45, but we only
			 * call rfcmd once with the count set to N fo rim44
			 *
			 * 	11/20/86				BW
			 */

			if (rc->rc_ctape)
			{
				callcount = 1;
				fcount = mtop->mt_count;
			}
			else
			{
				callcount = mtop->mt_count;
				fcount = 1;
			}

			break;

		 case MTBSR:
			ctl |= CTL_REVERSE;
		 case MTFSR: 
			if (rc->rc_ctape) { 		/* can't do it */
				u.u_error = EINVAL;
				goto done;
			}
			callcount = 1;
			fcount = mtop->mt_count;
			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;
		}

		op = mtops[mtop->mt_op];
		if (rc->rc_ctape && mtop->mt_op == MTFSF)
			op = TPCMD_SPACEFM;	/* different cmd for 44 */

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

			n = MIN(fcount, 0xffff);

			do {
				/*
				 * The tape parameter block is different for 
				 * rim44 and rim45 - namely the count field 
				 * occurs one position earlier in the rim44 
				 * parameter block than in the rim45 parameter 
				 * block ( i.e. for rim44 the 6th argument to 
				 * rfcmd is the count while for rim45 the 7th 
				 * argument to rfcmd is the count )
				 *
				 *	11/20/86			   BW
				 */

				if (rc->rc_ctape)
					rfcmd(ri, op, ctl, 0, 0, n, 0, 0);
				else
					rfcmd(ri, op, ctl, 0, 0, 0, n, 0);

				if ((op == TPCMD_REWIND || op == TPCMD_OFFLINE) 
				                  && !rtwaitrewind(rc, ri)) {
					printf("rt%d: not ready.\n", 
							ri->ri_unit);
					u.u_error = EIO;
					goto done;
				}
				fcount -= n;
			} while (fcount);

			if ((op == TPCMD_SPACEFM || op == TPCMD_SPACE) 
							&& 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;
		rfcmd(ri, TPCMD_STATUS, ctl, 0, 0, 0, 0, 0);
		mtget->mt_dsreg = rc->rc_pb->pb_t.tp_dst;
		mtget->mt_erreg = rc->rc_pb->pb_t.tp_cst;
		mtget->mt_resid = bp->b_resid;
		break;

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

	return u.u_error;
}

/*
 * Waits for the tape to rewind.
 * Returns true if tape rewound, false otherwise.
 */
rtwaitrewind(rc, ri)
	register struct rf_info *ri;
	register struct rf_ctlr *rc;
{
	register notfirst;

	if (rtdebug) printf("rtwaitrewind\n");
again:
	rfcmd(ri, TPCMD_STATUS, CTL_NORMAL, 0, 0, 0, 0, 0);

	if (rtdebug && rc->rc_ctape)
		printf("dst 0x%x mst 0x%x\n", 
			rc->rc_pb->pb_t.tp_dst, rc->rc_pb->pb_t.tp_mst);

	if (rc->rc_ctape ? !(ri->rt_dst&CPDST_RDY) : !(ri->rt_dst&TPDST_RDY)) {
		if (time.tv_sec < ri->rt_rewtime + rtrewtime) {
			if (rfdebug || rtdebug)
				printf(
		"rt%d: waiting for rewind time %d  rewtime %d rtrewtime %d.\n",
			ri->ri_unit, time.tv_sec, ri->rt_rewtime, rtrewtime);
			sleep((caddr_t)&lbolt, PZERO-1);
			goto again;
		} else 
			return(0);
	} else 
		return(1);
}
#endif
