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

/*
 * Interphase 2190/Storager ioctl and support 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 "dk_types.h"		/* includes "../s32/dkio.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 "s32_config.h"

/*
 * Debug flag {en,dis}ables debugging statements.
 */
extern u_char	idebug;

idioctl(io, cmd, opt)
register struct iob	*io;
register int		cmd, opt;
{
	register struct is_ctlr *ic = &(i_templates[ISCTLR(io->i_unit)]);
	register struct is_disk *id = &(ic->ic_disk[ISUNIT(io->i_unit)]);
	register struct is_param *ip = ic->ic_param;

	if (idebug)
		printf("idioctl(0x%x, 0x%x, 0x%x)\n", io, cmd, opt);

	switch (cmd)
	{
		default:
			printf("Command 0x%x not implemented\n",cmd);
			break;
		case SAIOGPARM:	/* get the disk's parameters */
		{
			register model_t	*mp = (model_t *) opt;
			register int		i;

			if (idebug) printf("Cmd SAIOGPARM\n");
			mp->m_nCyl = id->id_ncyl + id->id_acyl;
			mp->m_nHead = id->id_nhead;
			mp->m_nSectors = id->id_nsec;
			mp->m_aCyl = id->id_acyl;
			mp->m_Gap1 = id->id_gap1;
			mp->m_Gap2 = id->id_gap2;
			mp->m_Interleave = id->id_intrlv;
			mp->m_Grouping = id->id_group;
			mp->m_Skew = id->id_skew;
			mp->m_Cache = id->id_cache;
			mp->m_ZeroLat = id->id_zerolatency;
			mp->m_DiskType = id->id_drivedesc;
			mp->m_Gap3 = id->id_gap3;
			mp->m_BufStep = id->id_bufstep;	
			mp->m_StepWidth = id->id_stepwidth;
			mp->m_StepInt = id->id_stepinter;
			mp->m_HeadLoad = id->id_headloadtime;
			mp->m_SeekTime = id->id_seektime;
			mp->m_RedWrite = id->id_redwrite;
			mp->m_Precomp = id->id_precomp;
			for (i=0; i < DK_NSIZES; i++) {
				mp->m_PartSizes[i].sz_nblk =
					id->id_sizes[i].sz_nblk;
				mp->m_PartSizes[i].sz_cyloff =
					id->id_sizes[i].sz_cyloff;
			}
			break;
		}
		case SAIOSPARM:	/* set the disk's parameters */
		{
			register model_t	*mp = (model_t *) opt;
			register int		i;

			if (idebug) printf("Cmd SAIOSPARM\n");
			id->id_ncyl = mp->m_nCyl;
			id->id_nhead = mp->m_nHead;
			id->id_nsec = mp->m_nSectors;
			id->id_nsecpercyl = id->id_nsec * id->id_nhead;
			id->id_acyl = mp->m_aCyl;
			id->id_gap1 = mp->m_Gap1;
			id->id_gap2 = mp->m_Gap2;
			id->id_intrlv = mp->m_Interleave;
			id->id_group = mp->m_Grouping;
			id->id_skew = mp->m_Skew;
			id->id_cache = mp->m_Cache;
			id->id_zerolatency = mp->m_ZeroLat;
			id->id_drivedesc = mp->m_DiskType;
			id->id_gap3 = mp->m_Gap3;
			id->id_bufstep;	mp->m_BufStep;
			id->id_stepwidth = mp->m_StepWidth;
			id->id_stepinter = mp->m_StepInt;
			id->id_headloadtime = mp->m_HeadLoad;
			id->id_seektime = mp->m_SeekTime;
			id->id_redwrite = mp->m_RedWrite;
			id->id_precomp = mp->m_Precomp;
			for (i=0; i < DK_NSIZES; i++) {
				id->id_sizes[i].sz_nblk =
					mp->m_PartSizes[i].sz_nblk;
				id->id_sizes[i].sz_cyloff =
					mp->m_PartSizes[i].sz_cyloff;
			}
			return (!iscmd(ic,id->id_unit,IPCMD_INIT,0,0,0,0,is_uib));
		}
		case SAIORLAB:	/* read the disk label */
			if (idebug) printf("Cmd SAIORLAB\n");
			return (!idreadlabel(ic, id->id_unit, opt));
		case SAIOWLAB:	/* write the disk label */
			if (idebug) printf("Cmd SAIOWLAB\n");
			return (!idwritelabel(ic, id->id_unit, opt));
		case SAIOFMTTRK:	/* format a specified track */
			if (idebug) printf("Cmd SAIOFMTTRK\n");
			return (!idfmttrk(ic, id->id_unit, opt));
		case SAIOFMTDSK:	/* format the whole disk */
			if (idebug) printf("Cmd SAIOFMTDISK\n");
			return (!idfmtdsk(ic, id->id_unit, opt));
		case SAIOMAP:	/* map spcified bad track */
			if (idebug) printf("Cmd SAIOMAP\n");
			return (!idmaptrk(ic, id->id_unit, opt));
		case SAIOMAPLST:	/* map out bad tracks in list */
			if (idebug) printf("Cmd SAIOMAPLST\n");
			return (!idmaplst(ic, id->id_unit, opt));
		case SAIODEBUG:	/* toggle debug flag */
		{
			if (idebug) printf("Cmd SAIODEBUG\n");
			idebug = !idebug;
			break;
		}
	}
	return 0;
}

/*
 * idwritelabel -- write the label
 * Returns true if label ok, false otherwise.
 *
 * Complains if the label is wrong.
 */
idwritelabel(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 */
	struct dk_label localbuf;

	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);
	}

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

	l->dkl_cksum = sum;

	/* Formatting one track */
	iscmd(ic, unit, IPCMD_FORMAT, 0, 0, 0, 0, 0x61);
	if (!iscmd(ic, unit, IPCMD_READ, 0, 0, 0, 1, &localbuf)) {
		printf("TRACK 0 FORMAT FAILED!\n");
		return (0);
	}

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

	return (1);
}

idfmttrk(ic, unit, trkno)
register struct is_ctlr *ic;
register u_short unit;
register struct cyl_hd trkno;
{
	return iscmd(ic, unit, IPCMD_FORMAT, trkno.head, trkno.cyl, 0, 0, 0x61);
}

idfmtdsk(ic, unit)
register struct is_ctlr *ic;
{
	char buffer[80];
	register struct is_disk *id = &(ic->ic_disk[ISUNIT(unit)]);
	register u_long cyls = id->id_ncyl;
	register u_long tpc = id->id_nhead;
	register u_long cyl, head;
	register spt = id->id_nsec;
	u_long errors = 0;

	for (cyl = 0; cyl <= cyls; ++cyl) {
		printf("\nCYL %X(%d):", cyl, cyl);
		for (head = 0; head < tpc; ++head) {
			printf(" %d", head);
			if (!iscmd(ic, unit, IPCMD_FORMAT, head,
			    cyl, 0, 0, 0x61))
				errors ++;
		}
	}
	printf("\n");
	if (errors) {
		printf("%d errors during formatting, format invalid\n", errors);
		return (0);
	}
	return (1);
}

idmaplst(ic, unit, list)
register struct is_ctlr *ic;
struct s32_badtrks *list;
{
	struct dk_label l;
	register struct s32_badtrks *pbt = list;

	if (!idreadlabel(ic, unit, &l)) {
		printf ("Label botched, reformat\n");
		return (0);
	}
	/*
	 * Spare bad alternates first
	 */
	while (pbt->sb_cyl || pbt->sb_head) {
		if (pbt->sb_cyl >= l.dkl_ncyl)
			if (id_domap(ic, unit, pbt->sb_cyl, pbt->sb_head, &l))
				return 1;
		pbt++;
	}

	/*
	 * Now map the useful part of the disk
	 */
	pbt = list;
	while (pbt->sb_cyl || pbt->sb_head) {
		if (pbt->sb_cyl < l.dkl_ncyl)
			if (id_domap(ic, unit, pbt->sb_cyl, pbt->sb_head, &l))
				return 1;
		pbt++;
	}

}

idmaptrk(ic, unit, trkno)
register struct is_ctlr *ic;
register struct cyl_hd trkno;
{
	struct dk_label l;

	if (!idreadlabel(ic, unit, &l)) {
		printf ("Label botched, reformat\n");
		return (0);
	}
	return id_domap(ic, unit, trkno.cyl, trkno.head, &l);
}

id_domap(ic, unit, cyl, head, l)
register struct is_ctlr *ic;
register struct dk_label *l;
{
	register u_long althead, altcyl;
	register u_long tpc;
		 u_long spt;
	register u_long retries;
	register u_long a_alloc_wrong = 0;
	register u_long	i = 0;
		 char	buffer[DEV_BSIZE];

	tpc = l->dkl_nhead;
	spt = l->dkl_nsec;
	altcyl = l->dkl_ncyl;
	althead = l->dkl_a_alloc;

	while (althead >= tpc) {
		++altcyl;
		althead -= tpc;
	}

	/*	Where is the buffer for this command gonna live ???? */

	while (i != spt) {
		for (i = 0; i < spt; i++)
			if (iscmd(ic, unit, IPCMD_READ, althead, altcyl, 0,
			    1, buffer)) {

				++a_alloc_wrong;
				++althead;
				++l->dkl_a_alloc;

				while (althead >= tpc) {
					++altcyl;
					althead -= tpc;
				}
			}
	}

	if (a_alloc_wrong)
		idwritelabel(ic, unit, l);

	if (cyl >= l->dkl_ncyl) {
		printf("Mapping alternate cyl %X(%d) head %X(%d)\n",
			cyl, cyl, head, head);
		/* Map the last alt track to the bad alt */
		iscmd(ic, unit, IPCMD_MAP,
			tpc -1, l->dkl_ncyl + l->dkl_acyl - 1, head, cyl, 0);
		return;
	}

	printf("Mapping cyl %X(%d) head %X(%d) to %X(%d), %X(%d)\n",
		cyl, cyl, head, head, altcyl, altcyl, althead, althead);

	iscmd(ic, unit, IPCMD_MAP, head, cyl, althead, altcyl, 0);
	++l->dkl_a_alloc;
	idwritelabel(ic, unit, l);
	++althead;
}
