static char rcsid[] = "$Header: id.c,v 86.1 86/07/21 14:56:21 bog Exp $";
/************************************************************************
 *									*
 *				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 		*
 *	Incoroporated.							*
 *									*
 *	The copyright notice appearing above is included to provide	*
 *	statutory protection in the event of unauthorized or 		*
 *	unintentional public disclosure.				*
 *									*
 ************************************************************************/

/*
 * Interphase 2190/Storager disk driver routines.
 * Sam Cramer.
 */

#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 "../h/errno.h"
#include "saio.h"
#include "isreg.h"		/* Interphase 2190/Storager registers */
#include "isvar.h"		/* 2190/Storager data structure definitions */

struct is_ctlr is_ctlr;		/* Per controller info */
extern u_char idebug;

/*
 * Possible drive descriptor bytes for Storager.
 * Note that the entry for a Fuji ESDI drive (ESDI_SECTPULSE) must
 * come first, as the Fuji drive does not like to be misconfigured...
 */

u_char id_ddesc[] = { 
		/* Kernel Order:
		 *	IUDDESC_ESDI_SECTPULSE,
		 *	IUDDESC_ST506, 
		 *	IUDDESC_ESDI_ADDRMK, 
		 *	IUDDESC_ESDI_BYTECLK
		 */
		 IUDDESC_ESDI_SECTPULSE,
		 IUDDESC_ST506,
		 IUDDESC_ESDI_ADDRMK, 
		 IUDDESC_ESDI_BYTECLK	/* No longer part of ESDI spec??? */
};
#define NDDESC		(sizeof(id_ddesc) / sizeof(id_ddesc[0]))



/*
 * Opens a disk.
 * Returns ZERO if successful.
 */

idopen(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);

	register off = io->i_boff;	/* Meaning for tape? */
	register struct is_ctlr *ic = &is_ctlr;
	register struct is_device *ic_addr = (struct is_device *)
						makeioaddr(ic->ic_addr);
	struct is_disk *id;
	u_char storagerflag;
	int ret_val = 0;
	static char badunitmsg[] = "id: bad unit";
	
	if (idebug) {
		printf("idopen: 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
	}

	if (!isopen(ic, ctlr))
		_stop("id: can't open ctlr");

	if (idebug) {
		printf("idopen: got back from isopen\n", ctlr, unit);
	}

	/*
	 * 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.
	 */

	storagerflag = ic->ic_storager;
	if (idebug)
		printf("idopen: %s controller\n",
			storagerflag ? "Storager" : "SMD 2190");
	idloadfakeuib(is_uib, storagerflag);

	if (!storagerflag) {
		if (!iscmd(ic, unit, IPCMD_INIT, 0, 0, 0, 0, is_uib))
			_stop("id: failed fake init");
		if (idreadlabel(ic, unit, label)) {
			if (idebug)
				printf("idopen: label was read\n");
		}
		else 
		{
			if (idebug)
				printf("idopen: label not read\n");
			_stop(badunitmsg);
		}
	} else {
		/*
		 * We have to try several different drive descriptor
		 * bytes for the various flavors of 5 1/4" drives:
		 * ST506, ESDI with address mark, ESDI with sector
		 * pulse, etc.
		 */

#define IDSLAVE_RDY(csr, unit) (csr & ISCSR_RDYBIT<<unit)

		int i, x;
		u_char found_it = 0;

		for (i = 0; i < NDDESC ; i++ ) {
			is_uib->iu_drivedesc = id_ddesc[i]; 

			/* if it's a Siemens drive, spin it up */
			if (is_uib->iu_drivedesc == IUDDESC_ESDI_BYTECLK) {
				iscmd(ic, unit, IPCMD_MOTORCTL, 0, 1, 0, 0,
					is_uib);
			}
			if (!iscmd(ic, unit, IPCMD_INIT, 0, 0, 0, 0, is_uib)) {
				if (idebug)
				   printf("idopen: INIT ddesc 0x%x failed\n",
					id_ddesc[i]);
				continue;	/* should never happen */
			}

			if (idreadlabel(ic, unit, label)) {
				if (idebug)
					printf("idopen: label was read\n");
				found_it = 1;
				break;
			} else {
				if (idebug)
					printf("idopen: label not read\n");
			}

		}
		if (idebug)
			printf("Out of loop found it is %d ...\n",found_it);
		if (!found_it) {
			if (idebug)
				printf("Bad Unit. Aaak.\n");
			_stop(badunitmsg);
		} 
	}

	if (idebug) {
		printf("idopen: going to load uib\n", ctlr, unit);
	}

	idloaduib(is_uib, label, storagerflag);

	if (idebug) {
		printf("idopen: loaded uib\n", ctlr, unit);
	}

	/*
	 * Load up the info from the label into the is_disk[]
	 * array.
	 */
	/* label was good, fill in disk structure with real params */

	id = &ic->ic_disk[unit];

	id->id_ncyl = label->dkl_ncyl;
	id->id_acyl = label->dkl_acyl;
	id->id_a_alloc = label->dkl_a_alloc;
	id->id_nhead = label->dkl_nhead;
	id->id_nsec = label->dkl_nsec;
	id->id_gap1 = label->dkl_gap1;
	id->id_gap2 = label->dkl_gap2;
	id->id_intrlv = label->dkl_intrlv;
	id->id_skew = label->dkl_skew;
	id->id_group = label->dkl_group;
	id->id_cache = label->dkl_cache;
	id->id_nsecpercyl = id->id_nsec * id->id_nhead;

	if (ic->ic_storager) {
		id->id_zerolatency = label->dkl_zerolatency;
		id->id_drivedesc = label->dkl_drivedesc;
		id->id_gap3 = label->dkl_gap3;
		id->id_bufstep = label->dkl_bufstep;
		id->id_stepwidth = label->dkl_stepwidth;
		id->id_stepinter = label->dkl_stepinter;
		id->id_headloadtime = label->dkl_headloadtime;
		id->id_seektime = label->dkl_seektime;
		id->id_precomp = label->dkl_precomp;
		id->id_redwrite = label->dkl_redwrite;
	}

	/* copy the partition table from the labelabel */
	bcopy(label->dkl_sizes, id->id_sizes, sizeof id->id_sizes);
	label->dkl_asciilabel[sizeof label->dkl_asciilabel - 1] = 0;

	if (idebug) {
		printf("id_ncyl %d id_nhead %d id_nsec %d id_nsecpercyl %d\n",
			id->id_ncyl, id->id_nhead, id->id_nsec,
			id->id_nsecpercyl);
	}

	if (!iscmd(ic, unit, IPCMD_INIT, 0, 0, 0, 0, is_uib))
		_stop("id: can't re-init drive");
	
	/*
	 * Figure out the partition number.
	 * If off < 0, pretend it is an absolute offset
	 */

	if (off < 0) {				/* Absolute offset */
		io->i_boff = -off;		/* Make it positive */
		if (io->i_boff >= (id->id_ncyl * id->id_nsecpercyl))
			_stop(badunitmsg);		/* off the end */

	} else if (off >= DK_NSIZES) {		/* Must be a partition num */
		_stop (badunitmsg);			/* Too big */

	} else {
		/* Replace with block offset of start of partition */

		io->i_boff = id->id_sizes[io->i_boff].sz_cyloff 
					* id->id_nsecpercyl;
	}

	return 0;
}


/*
 * idreadlabel -- read the label and see if it is good.
 * Returns true if label ok, false otherwise.
 *
 * Complains if the label is wrong.
 */
idreadlabel(ic, unit, l)
	struct is_ctlr *ic;
	struct dk_label *l;
{
	register u_short *sp;        /* pointer for checksum calculation */
	register u_short sum = 0;    /* label checksum */

	/*
	 * Read label area on disk -- cylinder 0, track 0, sector 0.
	 */
	if (!iscmd(ic, unit, IPCMD_READ, 0, 0, 0, 1, l))
		/* 
		 * No-Can-Read, Bahzz... 
		 */
		return (0); 

	for (sp = (u_short *)l ; sp < (u_short *)&(l->dkl_cksum) ; sum ^= *sp++)
		;

	if (l->dkl_magic != DKL_MAGIC) {
		printf("bad label magic number 0x%x should be 0x%x\n", 
			l->dkl_magic, DKL_MAGIC);
		return (0);
	}

	if (sum != l->dkl_cksum) {
		printf("bad checksum 0x%x should be 0x%x\n", 
			l->dkl_cksum, sum);
		return (0);
	}

	return (1);
}


/*
 * Load the uib structure with predetermined
 * values that will allow us to read the label.
 */
idloadfakeuib(uib, storagerflag)
	register struct is_uib *uib;
	int storagerflag;			/* 1 ==> ctlr is a storager */
{
	bclear((char *)uib, sizeof(struct is_uib));

	if (storagerflag) {
		/* storager only */
		uib->iu_ncyl0 = 1024;
		uib->iu_ncyl1 = 1024 >> NBBY;
		uib->iu_nsec = 17;
		uib->iu_gap1 = 13;
		uib->iu_gap2 = 17;
		uib->iu_gap3 = 20;
		uib->iu_il = 1;
		uib->iu_opoption = IUOPT_NORMAL|IUOPT_ZEROLATENCY;
		uib->iu_drivedesc = 0x6;
		uib->iu_stepwidth = 1;
		uib->iu_stepinter0 = 1;
		uib->iu_seektime = 6;
		uib->iu_precomp0 = 0xff;
		uib->iu_precomp1 = 0xff;
		uib->iu_redwrite0 = 0xff;
		uib->iu_redwrite1 = 0xff;
		uib->iu_il = 1 & IUIL_IL;
	} else {
		/* 2190 only */
		uib->iu_nsec = 44;
		uib->iu_gap1 = 20;
		uib->iu_gap2 = 30;
		uib->iu_il = 3 & IUIL_IL;
	}

	/* common to both */

	uib->iu_nhead = 5;
	uib->iu_secsiz0 = DEV_BSIZE;
	uib->iu_secsiz1 = DEV_BSIZE >> NBBY;
	uib->iu_retry = 10;
	uib->iu_ec = 1;
	uib->iu_rs = 1;
	uib->iu_bd = 0;
	uib->iu_ih = 1;
	uib->iu_2port = 0;
	uib->iu_si = 0;		/* no status change interrupts, please */
}

idloadimmutable(uib)
	register struct is_uib *uib;
{
	uib->iu_secsiz0 = DEV_BSIZE;
	uib->iu_secsiz1 = DEV_BSIZE >> NBBY;
	uib->iu_retry = 10;
	uib->iu_ec = 1;
	uib->iu_rs = 1;
	uib->iu_bd = 0;
	uib->iu_ih = 1;
	uib->iu_2port = 0;
	uib->iu_si = 0;		/* no status change interrupts, please */
}


/*
 * isloaduib -- load the uib structure with the values from the label
 */
idloaduib(uib, l, storagerflag)
	register struct is_uib *uib;
	register struct dk_label *l;
	u_char storagerflag;			/* true if storager */
{
	idloadimmutable(uib);
	uib->iu_nhead = l->dkl_nhead;
	uib->iu_nsec = l->dkl_nsec;
	uib->iu_gap1 = l->dkl_gap1;
	uib->iu_gap2 = l->dkl_gap2;
	uib->iu_il = l->dkl_intrlv;
	uib->iu_skew = l->dkl_skew;

	if (storagerflag) {
		/*
		 * Storager only.
		 */
		uib->iu_opoption = /* IUOPT_NORMAL */ 0x3;
		if (l->dkl_zerolatency)
			uib->iu_opoption |= IUOPT_ZEROLATENCY;
		uib->iu_drivedesc = l->dkl_drivedesc;
		uib->iu_gap3 = l->dkl_gap3;
		if (l->dkl_bufstep)
			uib->iu_stepcontrol = IUSTEPCNTL_BUFSTEP;
		uib->iu_stepwidth = l->dkl_stepwidth;
		uib->iu_stepinter0 = l->dkl_stepinter;
		uib->iu_stepinter1 = l->dkl_stepinter >> NBBY;
		uib->iu_headloadtime = l->dkl_headloadtime;
		uib->iu_seektime = l->dkl_seektime;
		uib->iu_ncyl0 = (l->dkl_ncyl + l->dkl_acyl);
		uib->iu_ncyl1 = (l->dkl_ncyl + l->dkl_acyl) >> NBBY;
		uib->iu_precomp0 = l->dkl_precomp;
		uib->iu_precomp1 = l->dkl_precomp >> NBBY;
		uib->iu_redwrite0 = l->dkl_redwrite;
		uib->iu_redwrite1 = l->dkl_redwrite >> NBBY;
	} else {
		/*
		 * 2190 only.
		 */
		uib->iu_grpsiz = l->dkl_group;
		if (l->dkl_group != 0)
			uib->iu_il |= IUIL_GRP;
		if (l->dkl_cache)
			uib->iu_il |= IUIL_CE;
	}
}


idstrategy(io)
register struct iob *io;
{
	u_int	rw = io->i_flgs & F_RDDATA;	/* read/write flag */
	register struct is_ctlr *ic = &is_ctlr;
	register struct is_disk *id = &(ic->ic_disk[ISUNIT(io->i_unit)]);
	register bno = io->i_bn;
	register u_long	startaddr = (u_long) io -> i_ma;
	register u_long	records;
	register u_char	page;
	register u_short cylinder;
	register u_char head;
	register u_short sector;
	int stat;

#ifdef LATER
	orStatusReg(V_GREENLED);	/* Turn on LED during disk op */
#endif

	if (idebug) {
		printf("io->i_unit %d, io->i_bn %d, id 0x%x\n",
			io->i_unit, io->i_bn, id);
	}

	/*
	 *	Figure out cylinder, head, starting sector, and number of
	 *	records for this operation. This is figured out using the
	 *	paramters for the disk being transferred from in 'rfdtable'.
	 *
	 *	{Blocks per cylinder} = Bytes per cylinder} / 
	 *		{Bytes per sector}
	 *
	 *	cylinder = {block number} / {Blocks per cylinder}
	 *
	 *	head = {block number} % {Blocks per cylinder} /
	 *		{Sectors per track}
	 *
	 *	{starting sector} = ({block number} % {Sectors per track})
	 *
	 *	{# records} = ({transfer count} + {Bytes per sector} - 1)
	 *		/ {Bytes per sector}
	 */


	cylinder = bno / id->id_nsecpercyl;
	head = (bno % id->id_nsecpercyl) / id->id_nsec;
	sector = bno % id->id_nsec;
	records = (io->i_cc + DEV_BSIZE -1) / DEV_BSIZE;

	if (idebug)
		printf("%s cyl %d,head %d,sect %d,cnt %d,addr %x\n",
		rw?"Reading":"Writing", cylinder, head, sector, records,
		startaddr);
#ifdef	USE_ERM
	if (!records) trap15();
#endif	USE_ERM

	/*
	 *	Do the operation and wait for completion 
	 */

	stat = iscmd(ic, ISUNIT(io->i_unit), 
		rw ? IPCMD_READ : IPCMD_WRITE, head, 
		cylinder, sector, records, startaddr);

#ifdef LATER
	xorStatusReg(V_GREENLED);	/* Turn off LED after transfer */
#endif

	if (!stat) {			/* Check for error */
		errno = ENXIO;
		return -1;		/* Return -1 on error */
	} else  
		return io->i_cc;	/* Completed OK, Return count */
}
#ifndef	SA_ADVANCED
idioctl ()
{}
#endif	SA_ADVANCED
