static char *rcsid="$Header$";
/************************************************************************
 *									*
 *			Copyright 1985 - 1986				*
 *			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.				*
 *									*
 ************************************************************************/

/*
 * Interphase 2190/Storager tape driver routines.
 * Sam Cramer.
 *
 * Modified for use in standalone library.  Bill O. Gallmeister 072286.
 *
 * This module is completely untested and does not work.  Believe it.
 */

#include "../h/types.h"		/* handy types */
#include "../h/param.h"		/* NBBY, DEV_BSIZE */
#include "../h/fs.h"
#include "../h/inode.h"
#include "../s32/vectors.h"
#include "../s32/cpu.h"
#include "../s32/setjmp.h"
#include "../s32/dkio.h"	/* to help dklabel.h */
#include "../s32/dklabel.h"	/* smd label definition */
#include "saio.h"
#include "isreg.h"		/* Interphase 2190/Storager registers */
#include "isvar.h"		/* 2190/Storager data structure definitions */
#include "rfreg.h"
#include "../h/errno.h"

/*
 * Debugging frag.
 */
#ifdef ITDEBUG
int itdebug = 1;
#else  ITDEBUG
int itdebug = 0;
#endif ITDEBUG

/*
 * Set up an IS command block and execute it for tape.
 * Calls generic iscmd() routine.
 *
 *	    returns 1 if successful, zero otherwise.
 *
 * Deals with only one tape drive: unit 4 on controller 0.
 */
itcmd(ic, cmd, tapeopt, nblk, addr)
	struct is_ctlr *ic;
	int cmd, tapeopt, nblk, addr;
{
	if (itdebug)
		printf ("itcmd (0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
			ic, cmd, tapeopt, nblk, addr);
	return iscmd(ic, 4, cmd, tapeopt, 0, 0, nblk, addr);
}

/*
 * Open up tape device.  Returns 0 if successful, and -1 otherwise.
 *
 * Code ripped off from idopen().  Some duplication,
 * maybe to be consolidated later.
 *
 * io->i_unit tells us which tape we're opening.
 * 	Only unit 4 on ctlr 0 is supported right now.
 * 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.
 */
itopen(io)
	register struct iob *io;
{
	/*
	 * Interpolate controller and unit number from ID_NSLAVE.
	 */
	int ctlr = ISCTLR(io->i_unit);
	int unit = ISUNIT(io->i_unit);	/* Hmm.  What do we get here? 4? 0? */

	register struct is_ctlr *ic = i_templates;
	register struct is_device *ic_addr = (struct is_device *)
						makeioaddr(ic->ic_addr);
	struct is_tape *it;
	
	if (itdebug) {
		printf("itopen: 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(isSetErrorAndBitch(io,ENXIO,
			"Bad ctlr (0x%x) or unit (0x%x)\n",ctlr, unit));
	}

	/*
	 * Make sure controller is home. (Sets ic_storager)
	 */
	if (!isopen(ic, ctlr))
		_stop("id: can't open ctlr");

	if (itdebug) {
		printf("itopen: got back from isopen\n");
	}

	/*
	 * Initialize the controllers with values that will allow us
	 * to read the label.
	 * Read the disk label, in order to extract the real disk parameters.
	 * Re-initialize the controller to real values.
	 */

	if (itdebug)
		printf("idopen: %s controller\n",
			ic->ic_storager ? "Storager" : "SMD 2190");

	if (!(ic->ic_storager))
		return(isSetErrorAndBitch(io,ENXIO,
			"it: no tape on 2190 controller!\n"));

	/* Reset tape. */
	if (!itrst(ic)) {
		return(isSetErrorAndBitch(io,ENXIO,
			(unit == 0) ? 
			"Can't configure tape 0\n" : "Bad unit number 0x%x\n",
			unit));
	}

	/*
	 * Initialize it structure.
	 */
	it = ic->ic_tape;

	it->it_unit = unit;		/* unit number */
	it->it_open    = 1;		/* is open */
	it->it_err     = 0;		/* can't recover from last error */
	it->it_lastiow = 0;		/* last io was a write */
	it->it_lasterr = 0;		/* error field of last command run */
	it->it_blkno   = 0;		/* current block number */
	it->it_nxrec   = 0;		/* eof position, if known */
	it->it_filemark= 0;		/* No filemarks yet! */

	/*
	 * Rewind tape.
	 */
	if (itxandwait(ic,IPCMD_REWIND) < 0)
		return(isSetErrorAndBitch(io,EIO,"itopen: couldn't rewind\n"));

	/*
	 * Seek past io->i_boff filemarks.
	 */
	if (io->i_boff)
	{
		if (itdebug)
			printf("itopen: seeking past 0x%x filemarks\n",
				io->i_boff);
		if (!iscmd(ic, 4, IPCMD_READFM, 0, io->i_boff, 0, 0, 0))
		{
			return(isSetErrorAndBitch(io,EIO,
				"itopen: big seek failed.\n"));
		}
	}
	io->i_flgs |= F_TAPE;
	return 0;
}

/*
 * Tape read/write.  
 * Note that the xfer must be a multiple of DEV_BSIZE.
 * Returns number of bytes transferred.
 */
itstrategy(io)
	register struct iob *io;
{
	u_int	rw = io->i_flgs & F_RDDATA;	/* read/write flag */
	register struct is_ctlr *ic = i_templates;
	register struct is_tape *it = &ic->ic_tape[ISUNIT(io->i_unit)]; /* 4? */

	/* Where do start, limit addr come from? */
	int nblk;
	register int nbytes = io->i_cc;
	u_char *ready = (u_char *)makeioaddr(&(ic->ic_addr->istprdy));
	long leftover;

	if (itdebug)
		printf("itstrategy\n");

	if (it->it_filemark) {
		it->it_filemark = 0;
		return 0;
	}
	if (nbytes % DEV_BSIZE)
	{
		return(isSetErrorAndBitch(io,EINVAL,
			"itstrat: i_cc 0x%x not a multiple of DEV_BSIZE 0x%x\n",
				nbytes, DEV_BSIZE));
	}
	if ((nblk = nbytes / DEV_BSIZE) == 0)
		if (itdebug)
			printf("itstrategy: count == 0, read til FM...\n");

	if (berrfound)
		/* Tape not ready or controller scrogged */
		return isSetErrorAndBitch(io,EIO,"it: tape not ready\n");

#ifdef	LATER
	/* Why don't we do this? */
	/*
	 * Return immediately if the tape drive isn't on line
	 */
	if (istapemst(ic) & (TPMST_NOTAPE | TPMST_OFFLINE | TPMST_EOM)) {
		return isSetErrorAndBitch(io,EIO,"it: tape not ready\n");
	}
#endif	LATER

	if (!itcmd(ic, rw ? IPCMD_READTAPE : IPCMD_WRITETAPE,0,nblk,io->i_ma)) {
		if (ic->ic_param->ip_err == IPERR_FILEMARK || 
		    ic->ic_param->ip_err == IPERR_FM) 
			it->it_filemark ++;
		else
			return isSetErrorAndBitch(io,EIO,"it: %s error %x\n",
				rw ? "Read":"Write", ic->ic_param->ip_err);
	}
	else if (ic->ic_param->ip_err == IPERR_FILEMARK) 
			it->it_filemark ++;

	leftover = (ic->ic_param->ip_nsec0
	    + (ic->ic_param->ip_nsec1<<NBBY));

	if (itdebug)
		printf("itstrategy just transfered 0x%x bytes\n",
			DEV_BSIZE * (nblk - leftover));

	return nbytes - (leftover * DEV_BSIZE);
}

itioctl(io, cmd, opt)
struct iob *io;
{
	register struct is_ctlr *ic = &(i_templates[ISCTLR(io->i_unit)]);
	/* unit 4? */
	register struct is_tape *it = &(ic->ic_tape[ISUNIT(io->i_unit)]);
	register struct is_param *ip = ic->ic_param;

	if (itdebug)
		printf("itioctl(0x%x, 0x%x, 0x%x)\n", io, cmd, opt);

	switch (cmd)
	{
		default:
			printf("itioctl: cmd 0x%x not implemented\n",cmd);
			break;
		case SAIODEBUG:	/* Toggle debugging flag. */
			if (itdebug) printf("it: turning debugging off\n");
			itdebug = !itdebug;
			if (itdebug) printf("it: debugging on\n");
			break;
		case SAIOTREW:
			itxandwait(ic, IPCMD_REWIND);
			break;
		case SAIOTWFM:
			itcmd(ic, IPCMD_WRITEFM, 0, 0, 0);
			break;
#ifdef notdef
		case SAIOTRST:
			itrst(ic);
			break;
		case IO_CMD_STATUS:
			itrst(ic);
			break;
		case IO_CMD_RETENSION:
			return itxandwait(ic, IPCMD_RETENTION);
		case IO_CMD_LOCATE:
		case IO_CMD_LABEL_PROTECT:
			return 0;
#endif notdef
	}
	if (errno) return -1;
	return 0;
}

itxandwait(ic, cmd)
	struct is_ctlr *ic;
{
	register struct is_device *is = (struct is_device *)
						makeioaddr(ic->ic_addr);

/* Name three. */
	if ((istapemst(ic) & TPMST_NOTAPE)) {
		printf("Tape drive off line\n");
		return (-1);
	} 

	/* Do the command. */
	if (!itcmd(ic, cmd, 0, 0, 0)) {
		printf("Tape drive off line\n");
		return (-1);
	}

	if (itdebug)
		printf("\nreturned from itcmd. ISCSR = %x\n", is->iscsr);

	while (!(is->istprdy & 1)) /* tape ready status */
		if (watchcuart()) {
			printf("I die while waiting for status change ints!\n");
			_stop("^C");
		}

	if (itdebug)
		printf("\nClearing another interrupt...\n");

	is->iscsr = ISCSR_CLRI;    /* Clear interrupt */

	while(!(istapemst(ic) & (TPMST_BOM | TPMST_NOTAPE)))
		if (watchcuart()) {
			printf("I die while waiting for the BOM!\n");
			_stop("^C");
		}

	return 0;
}

#define ISTIMEOUTDELAY 100000000	/* how long delay before timing out */

itrst(ic)
register struct is_ctlr *ic;
{
	register i = ISTIMEOUTDELAY;
	register struct is_device *ic_addr = 
				(struct is_device *) makeioaddr(ic->ic_addr);

	if (!itcmd(ic, IPCMD_TAPECONF, IPTAPEOPT_UPDAT, 0, 0))
		return 0;

	while (i-- && (ic_addr->istprdy & ISTPRDY_BITS & 1) == 0)
		if (itdebug)
			printf ("csr=%x, intunit=%x, tprdy=%x\n",
				ic_addr->iscsr, ic_addr->isintunit,
				ic_addr->istprdy);
	if (itdebug)
		printf ("csr=%x, intunit=%x, tprdy=%x\n",
			ic_addr->iscsr, ic_addr->isintunit,
			ic_addr->istprdy);
	return(ic_addr->istprdy & ISTPRDY_BITS & 1);
}

#ifdef notdef
/*
** function itinit extracted from is.c
*/
itinit(ic, absunit)
register struct is_ctlr *ic;
{
	long returnval = 0;

	if (itdebug)
		printf("itinit...\n");

	/* Storager barfs if you access units 5-7! */
	if (itrst(ic)) {
		if (absunit == 4)
		{
			printf("Can't configure tape 0\n");
			++returnval;
		}
		else
		{
			printf("Bad unit number 0x%x\n");
		}
	}
	return returnval;
}
#endif notdef

istapemst(ic)
register struct is_ctlr *ic;
{
	struct is_tapestat it_stat;
	static u_short it_mst;
	
	/* Reselect drive 0: 
	 * 
	 * This command will hose the average Tandberg 3400, so since
	 * there is no other reason for it being there, we comment it
	 * out - ELP 10/10/86
	 *
	 * itcmd(ic, IPCMD_PASSTHRU, 0, 0, 0);
	 */
	itcmd(ic, IPCMD_TAPESTAT, 0, 0, &it_stat);
	it_mst =  *((u_short *)&it_stat);
	return it_mst;
}

/*
 * Code consolidation.  We do this a lot.  Returns -1 always.
 */
isSetErrorAndBitch(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);
}
