/* static char rcsid[] = "$Header$"; */
/************************************************************************
 *									*
 *				Copyright 1985				*
 *			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 		*
 *	Incoroporated.							*
 *									*
 *	The copyright notice appearing above is included to provide	*
 *	statutory protection in the event of unauthorized or 		*
 *	unintentional public disclosure.				*
 *									*
 ************************************************************************/

#include "../h/param.h"
#include "../h/fs.h"
#include "../h/inode.h"
#include "../s32/vectors.h"
#include "../s32/cpu.h"
#include "../s32/setjmp.h"
#include "../s32/dkio.h"
#include "../h/errno.h"
#include "saio.h"
#include "sas32.h"
#include "rfreg.h"

extern struct rf_controller *controller;
extern char rfcstatustbl[], rfdstatustbl[], rf45tstatustbl[];
extern char rf44tstatustbl[], rfmcstatus[];

int	rtdebug = 0;

/*
 * rftcmd -- Execute one Rimfire tape command
 */
rftcmd(rf, command, page, bufsiz, records, address, trw)
{
	rftlcmd(rf, command, page, bufsiz, records, address, trw, CTL_NORMAL);
}

/*
 * rftlcmd -- Execute one long Rimfire tape command
 */
rftlcmd(rf, command, page, bufsiz, records, address, trw, ctl)
register struct rf_controller	*rf;
	u_short		command;
	u_char		page;
	u_short		bufsiz;
	u_short		records;
	u_long		address;
	short		trw; 	/* flag for tape read/write commands */
	u_short ctl;		/* command control information */
{
	register struct rf_tp *rf_tpb	= rf->rf_tp;

	rfsetaddr(rf->rf_ccb->ccb_addr, rf->rf_tp);
	rf_tpb->tp_cmd		= command;
	rf_tpb->tp_page		= page;
	rf_tpb->tp_ctl		= ctl;
	if (!rf->rf_cst)
		rf_tpb->tp_ctl	|= CTL_SD;
	rf_tpb->tp_bufsiz	= bufsiz;
	rf_tpb->tp_nrec 	= records;
	rf_tpb->tp_link		= 0;
	rfsetaddr(rf_tpb->tp_addr, address);

	/*
	 * Do special stuff for cassette drives...
	 */
	if (rf->rf_cst) {
		register struct rf_rb *rb1 = rf->rf_rb1;
		register struct rf_rb *rb2 = rf->rf_rb2;

		if (!rb1 || !rb2) {
			printf("\nRing buffer ptr null.");
			return;
		}
		rb1->rb_page = 0;
		rb2->rb_page = 0;
		rb1->rb_gate = (RBGATE_DETACH | RBGATE_DONE);
		rb2->rb_gate = (RBGATE_DETACH | RBGATE_DONE);
#ifdef NOTYET
		rb1->rb_bcount = 0; /* you may have a clrb problem here */
		rb2->rb_bcount = 0; /* you may have a clrb problem here */
#endif NOTYET
		rfsetaddr(rb1->rb_link, rb2);
		rfsetaddr(rb2->rb_link, rb1);

	}

	/*
	 * A kludge: if this is a casette tape read/write operation,
	 * return without executing the Rimfire command.  This is
	 * beause we want to play the ring-buffer shuffle game inside
	 * of rftrw() for cassette drives (R44)
	 */
	if (rf->rf_cst && trw) 
		return;
	else
		rfdocmd(rf);
}

rftscmd(rf, cmd) /* simple rftcmd */
register struct rf_controller	*rf;
{
	return rftcmd(rf, cmd, 0, 0, 0, 0, 0);
}

/*
 * rftonline -- checks to see if the tape device is on line
 *		return 1 if on line, 0 if off line.
 */
rftonline(rf)
register struct rf_controller	*rf;
{
	register u_char mask;

	rftnop(rf);

	if (!(rf->rf_tp->tp_dst &
		(rf->rf_cst ? CPDST_HASTAPE : TPDST_ONLINE))) {
			rftstatus (rf, 1);
			return 0;
	}
	return 1;
}

/*
 * Open up tape device.  Returns 0 if successful, and -1 otherwise.
 *
 * io->i_unit tells us which tape we're opening.
 * io->i_boff is the file number on the tape;  it tells us how many
 *	file marks to seek in on the tape. (0 ==> stay at BOM)
 *
 * The command positions the tape at BOM, then seeks in past the requisite
 * number of file marks.
 */
rtopen (io, flag)
register struct iob *io;
{
	/*
	 * Interpolate controller and unit number from ID_NSLAVE.
	 */
	int ctlr = rtctlr(io->i_unit);
	int unit = rtunit(io->i_unit);	/* Hmm.  What do we get here? 4? 0? */

	register struct rf_controller *rf;
	register struct rf_device *rf_addr = (struct rf_device *)
						makeioaddr(rf->rf_device);
	register cmd;
	
	if (rtdebug) {
		printf("rtopen: ctlr %d unit %d\n", ctlr, unit);
#ifdef notdef
		printf("   io->i_flgs= 0x%x\n",io->i_flgs);
		printf("   io->i_unit= 0x%x\n",io->i_unit);
		printf("   io->i_boff= 0x%x\n",io->i_boff);
		printf("   io->i_cyloff= 0x%x\n",io->i_cyloff);
		printf("   io->i_offset= 0x%x\n",io->i_offset);
		printf("   io->i_bn= 0x%x\n",io->i_bn);
		printf("   io->i_ma= 0x%x\n",io->i_ma);
		printf("   io->i_cc= 0x%x\n",io->i_cc);
		printf("   io->i_error= 0x%x\n",io->i_error);
		printf("   io->i_errcnt= 0x%x\n",io->i_errcnt);
		printf("   io->i_errblk= 0x%x\n",io->i_errblk);
#endif notdef
	}

	/*
	 * Check for brane damij.
	 */
	if ((ctlr != 0) || (unit != 0))
	{
		return(rfSetErrorAndBitch(io,ENXIO,
			"Bad ctlr (0x%x) or unit (0x%x)\n",ctlr, unit));
	}

	/*
	 * Make sure controller is home.
	 */
	if (!rfopen(rf, ctlr))
		_stop("rt: can't open ctlr");

	if (rtdebug)
		printf("rtopen: got back from rfopen\n");
	rf = controller;
	/* Reset tape. */
	if (!rftscmd(rf, TPCMD_RESET))
		return(rfSetErrorAndBitch(io,ENXIO,
			(unit == 0) ? 
			"Can't configure tape 0\n" : "Bad unit number 0x%x\n",
			unit));
	if (rtdebug)
		printf ("After tape reset\n");

	/*
	 * Rewind tape.
	 */
	if (rfrew (rf) < 0)
		return(rfSetErrorAndBitch(io,EIO,"rtopen: couldn't rewind\n"));
	if (rtdebug)
		printf ("After Rewind\n");

	/*
	 * Seek past io->i_boff filemarks.
	 */
	if (io->i_boff) {
		if (rtdebug)
			printf("rtopen: seeking past 0x%x filemarks\n",
				io->i_boff);
		if (rf->rf_cst)  {
			if (!rftcmd(rf, TPCMD_SPACEFM, 0, io->i_boff, 0, 0, 0))
				return(rfSetErrorAndBitch(io,EIO,
					"rtopen: big seek failed.\n"));
		} else while (io->i_boff --)
			if (!rftcmd(rf, TPCMD_SEARCHFM, 0, 0, 0, 0, 0))
				return(rfSetErrorAndBitch(io,EIO,
					"rtopen: big seek failed.\n"));
	}
	if (rtdebug)
		printf ("After seek, returning\n");
	io->i_flgs |= F_TAPE;
	return 0;
}

rtioctl(io, cmd, opt)
struct iob *io;
{
	register struct rf_controller *rf = controller;

	if (rtdebug)
		printf("rtioctl\n");
	switch (cmd) {
	default:
#ifdef LATER
	    return rtxioctl(io,cmd,opt);
#else LATER
	    printf("Command not implemented\n");
	    return 1;
#endif LATER
	case SAIOTREW:
		rftxandwait(rf, TPCMD_REWIND);
		break;
	case SAIOTWFM:
		rftscmd(rf, TPCMD_WRITEFM);
		break;
	}
	return rftstatus(rf,0);
}

/*
 * rffeedring -- feed ring-buffer blocks to the Rimfire (44)
 *	SORRY ABOUT LOUSY CODING STYLE -- SHOULD CLEAN THIS UP!!!
 */
int
rffeedring(rf, startaddr, endaddr, t_buffsize)
register struct rf_controller *rf;
long startaddr;
register long endaddr;
{
	register struct rf_rb *rb1 = rf->rf_rb1;
	register struct rf_rb *rb2 = rf->rf_rb2;
	register long a = startaddr;
	register long b = a;
	long error;
	char *error_msg = "\nRT: ERROR %x\n";

frl1:
	if (rtdebug)
		printf("rfwaitblk(1), ");
	rfwaitblk(rf, rb2);
	if (rb2->rb_gate & RBGATE_FAULT) {
		if (rtdebug)
			printf("fault.\n");
		rfwaitblk(rf, rb2);
		return b;
	}
	if (rtdebug)
		printf("rfdoblk(1): a=%6x, endaddr=%6x ", a, endaddr);
	if (error = (rf->rf_tp->tp_cst & CST_ERROR)) {
		printf(error_msg, error);
		if (rtdebug)
			printf("..error out\n");
		return b;
	}
	b = a;
	a = rfdoblk(rb2, a, endaddr, t_buffsize);
	if (rtdebug)
		printf("result=%6x\n", a);
	if (a >= endaddr) {
		rfwaitblk(rf, rb2);
		return a;
	}
	if (rtdebug)
		printf("rfwaitblk(2), ");
	rfwaitblk(rf, rb1);
	if (rb1->rb_gate & RBGATE_FAULT) {
		if (rtdebug)
			printf(" fault.\n");
		rfwaitblk(rf, rb1);
		return b;
	}
	if (rtdebug)
		printf("\nrfdoblk(2): a=%6x, endaddr=%6x ", a, endaddr);
	if (error = (rf->rf_tp->tp_cst & CST_ERROR)) {
		printf(error_msg, error);
		if (rtdebug)
			printf("..error out\n");
		return b;
	}
	b = a;
	a = rfdoblk(rb1, a, endaddr, t_buffsize);
	if (rtdebug)
		printf("result=%6x\n", a);
	if (a >= endaddr) {
		rfwaitblk(rf, rb1);
		return a;
	}
	goto frl1;

}

/*
** function rfdoblk extracted from mike/rft.c
*/


/*
 * rfdoblk -- do the tape data block pointer update
 */
rfdoblk(rb, startaddr, endaddr, t_buffsize)
register struct rf_rb *rb;
register long startaddr;
register long endaddr;
{
	register long blocksize;

	rb->rb_gate = RBGATE_DETACH;
	rb->rb_page = rfpage(startaddr);
	if ((blocksize = endaddr - startaddr) <= t_buffsize) {
		if (rtdebug)
			printf(" LAST ");
		rb->rb_gate |= RBGATE_LAST;
	} else
		blocksize = t_buffsize;
	rb->rb_bcount = blocksize;
	rfsetaddr(rb->rb_addr, startaddr);
	rb->rb_gate |= RBGATE_RDY;

	return(startaddr + blocksize);
}

/*
** function rfwaitblk extracted from mike/rft.c
*/

/*
 * rfwaitblk -- wait for a data block pointer to become available
 */
rfwaitblk(rf, rb)
register struct rf_controller *rf;
register struct rf_rb *rb;
{

retest:
	if (rb->rb_gate & RBGATE_DONE) {
		if (rtdebug) {
			printf("\nleaving rfwaitblk through the front door...");
			printf("\nrb->rb_gate =  %2X:", rb->rb_gate);
			printf("\nrf->rf_tp->tp_cst =  %2X:\n		",
				rf->rf_tp->tp_cst);
		}
		return;
	}
	if (watchcuart()) {
		printf("\nrb->rb_gate =  %x:", rb->rb_gate);
		printf("\nrf->rf_tp->tp_cst =  %x:\n		",
			rf->rf_tp->tp_cst);
		_stop("RF RB gate not done!\n");
	}
	if ((rf->rf_tp->tp_cst & (CST_DONE | CST_ERROR))) {
		if (rtdebug) {
			printf("\nleaving rfwaitblk through the back door...");
			printf("\nrb->rb_gate =  %2X:", rb->rb_gate);
			printf("\nrf->rf_tp->tp_cst =  %2X:\n		",
				rf->rf_tp->tp_cst);
		}
		return;
	}
	goto retest;
}

/*
**  rfrew -- rewind the tape
*/
rfrew(rf)
register struct rf_controller *rf;
{
	rftscmd(rf, TPCMD_RESET);
	rftstatus(rf,0);
	rftscmd(rf, TPCMD_REWIND);
	return rtwaitforbom(rf);
}

rftxandwait(rf, cmd)
register struct rf_controller *rf;
{
	rftscmd(rf, TPCMD_RESET);
	rftstatus(rf,0);
	rftscmd(rf, cmd);
	return rtwaitforbom(rf);
}

rtwaitforbom(rf)
register struct rf_controller *rf;
{
busywait:
	if (watchcuart())
		_stop ("Wating for BOM");

	rftscmd(rf, TPCMD_STATUS);

	if (rf->rf_cst) {
		if(!(rf->rf_tp->tp_mst & (TPMST_BOM | TPMST_NOTAPE)))
			goto busywait;
	} else {
		if(!(rf->rf_tp->tp_dst & TPDST_LOADPT)) goto busywait;
	}

	return 0;
}

/*
** function rftready extracted from rfcomm.c
*/

rftready(rf, rw)
struct rf_controller *rf;
register rw;
{
	if (!rftonline(rf)) {
		printf("Tape off line\n");
		return 1; /* tape is offline */
	}

	if (rw == READ)
		return 0; /* tape is ready for read */

	if ((rf->rf_cst) &&
	    ((rf->rf_tp->tp_dst & CPDST_WRTPROT) == 0))
		return 0;

	if ((rf->rf_cst == 0) &&
	    ((rf->rf_tp->tp_dst & TPDST_WRTPROT) == 0))
		return 0;

	printf("Tape is write protected\n");
	return 2; /* tape is write ready */
}

/*
** function rftstatus extracted from rfcomm.c
*/

/*
* rftstatus  -- print out the Rimfire tape status
*/
rftstatus(rf, print)
register struct rf_controller *rf;
int print;
{
	register struct rf_tp *rf_tp = rf->rf_tp;
	register u_char i;
	register short j;
	register u_char k;
	register u_char   cst = rf_tp->tp_cst;
	register u_char   *dst = &rf_tp->tp_dst;
	register char *tstatus;

	if (rf->rf_cst)
		tstatus = rf44tstatustbl;
	else
		tstatus = rf45tstatustbl;

	k = cst & CST_ERROR;

	if (print) {
		printf("Tape status = %b %b", cst&0xC0, rfcstatustbl,
			*dst, tstatus);

		if (k)
			printf(" ERR %x", k);

		j = rf_tp->tp_retcnt;
		if (rf->rf_cst) {
			printf(" %b", rf->rf_tp->tp_mst, rfmcstatus);
		}
		else if (j)
			printf(" (COUNT = %x )", j);
		printf("\n");
	}
	return(k);
}

/*
** function rtstrategy extracted from rfcomm.c
*/

/*
 * rtstrategy -- read/write tape;
 * returns the number of bytes transfered
 */
rtstrategy(io, rw)
struct iob *io;
	u_char    rw;        /* read/write flag */
{
	register struct rf_controller *rf = controller;
	register struct rf_tp *rf_tp = rf->rf_tp;
	register u_long thisstart;

	register short        nbytes = io->i_cc;
	register u_char       page;
	register long         diff;
	register short        bufsiz = 0;
	long command;
	int bcount = 0;
	int spcount;

	if (rtdebug)
		printf("rtstrategy\n");
	page = 0;

	/*
	 * Return immediately if the tape drive isn't on line
	 */

	if (rf->rf_cst == 0 && rftready(rf, rw==READ))
		return 0;

	/* 
	 * We have to read in one record at a time.  The block size
	 * sent to the Rimfire should be that given by the user unless
	 * there is a partial buffer (e.g., the block size is 4 bytes
	 * and the user wants to transfer 11 bytes.  We would have 2 buffers
	 * of 4 bytes and 1 partial buffer of 3 bytes)
	 */

	/*
         * This routine does 2 different thing for R44's and R45's.
         * For an R44, we want to call rftcmd() to set up the tape
         * parameter block without calling rfdocmd().  We do this
         * by making the last argument non-zero.  We then grab the RF
         * gate and issue the attn.  Next we feed the ring buff to the
         * 44, then wait on the gate.  For the 45, we just keep sending
         * commands to the RF.
         */
	command = (rw==READ ? TPCMD_READ : TPCMD_WRITE);
	if (rf->rf_cst) {
		/*
                 * Set up the parameter block
                 */
		rftcmd(rf, command, page, bufsiz, 0, rf->rf_rb2,  1);
		if (rtdebug)
			printf("grab, ");
		grabrfgate(rf);
		if (rtdebug)
			printf("issue, ");
		issuerfattn(rf);
		if (rtdebug)
			printf("feed, ");
		bcount = rffeedring(rf, io->i_ma, io->i_ma + io->i_cc, nbytes)
			- (int) io->i_ma;
		if (rtdebug)
			printf("wait\n");
		waitrfgate(rf);
	} else {
		spcount = 0;
		for (thisstart = (int) io->i_ma, diff = nbytes;
		    diff > 0; thisstart += nbytes, diff -= nbytes) {
			page = rfpage(thisstart);
			bufsiz = (diff > nbytes) ? nbytes : diff;
tryagain:
			rftcmd(rf, command, page, bufsiz, 0, thisstart, 0);
			bcount += rf_tp->tp_retcnt;
			switch (rftstatus(rf,0)) {
			case ERR_SOFT:
			case ERR_NORMAL:
			case ERR_FM:
			case ERR_TIMEOUT:	/* Block size too large */
				spcount = 0;
				break;
			case ERR_DATA:
				printf ("Hard error during %s\n",
					rw==READ?"read":"write");
				if (rw == READ) rftstatus (rf, 1);
				if ((spcount > 5) || (rw==READ))
					goto done_xfer;
				if (rtspace(rf, bufsiz, 1 << spcount++))
					goto done_xfer;
				bcount -= rf_tp->tp_retcnt;
				goto tryagain;
			default:
				rftstatus (rf,1);
				goto done_xfer;
			}
		}
	}
done_xfer:

	if (rtdebug)
		printf("rtstrategy just transfered %X bytes\n", bcount);
	return bcount*(rftstatus(rf,0)<=0x01);
}

rtspace(rf, back, fwd)
register struct rf_controller *rf;
{
	if (rftlcmd(rf, TPCMD_SPACE, 0, back, 1, 0, 0, CTL_NORMAL|CTL_REVERSE))
		return 1;

	return rftcmd(rf, TPCMD_ERASE, 0, 0, fwd, 0, 0);
}

/*
** function rftnop extracted from rfcomm.c
*/

/*
* rftnop -- do a Rimfire 45 tape no-op
*/
rftnop(rf)
register struct rf_controller *rf;
{
	rftscmd(rf, TPCMD_STATUS);
}

/*
** function rfterm extracted from rfcomm.c
*/

/*
* rftermcmd -- Terminate an open-ended command
*/
rfterm(rf)
struct rf_controller *rf;
{
	if (rf->rf_cst)
		rftscmd(rf, TPCMD_OETRM);
}

/*
 * Code consolidation.  We do this a lot.  Returns -1 always.
 */
rfSetErrorAndBitch(io,error,string,arg,otherarg)
struct iob *io;
int error;
char *string;
int arg, otherarg;
{
	printf(string, arg, otherarg);
	io->i_error = error;
	return(-1);
}
