/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:hd.c 12.0$ */
/* $ACIS:hd.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/standatr/RCS/hd.c,v $ */

#ifndef lint
static char    *rcsid = "$Header:hd.c 12.0$";
#endif

#include "param.h"
#include "inode.h"
#include "fs.h"
#include "time.h"
#include "proc.h"
#include "../machineio/hdreg.h"
#include "../machineio/hdconfig.h"
#include "../ca_atr/pcif.h"
#include "../ca_atr/part.h"
#include "../machine/dkio.h"
#include "../pc_code/dio.h"

#include "saio.h"
#include "sa.h"
#include "hd.h"

extern struct hdinfo *hdinfo[2];

int             hddebug = 0;
int             hdquiet = 0;
int		hd_any_ucode = 0; /* ignored, but needed to compile format */
int             hdquietinit = 0;/* set by format.c (sigh) */
int		hd_pc_cb;		/* the PC control block addr */

hdopen(io)
	register struct iob *io;
{
	register int    boff = io->i_boff;	/* partition # */
	register int    unit = io->i_unit;
	register struct partab *off;

	if ((unit > 1) || boff < 0 || boff >= NPART) {
		printf("HD%d: BAD DRIVE NUMBER OR INVALID PARTITION\n", unit);
		return (-ENXIO);
	}
	hd_pc_cb = get_pc_cb(HDENT);
	if (hdsetup(unit) < 0)
		return (-ENXIO);

	HDDEBUG(SHOW_INIT, printf("HD: HDOPEN -- INITIALIZATION COMPLETE\n\n"));


	HDDEBUG(SHOW_INIT, printf("HDINFO ********\n\n"));
	HDDEBUG(SHOW_INIT, printf("DRIVE 0 ********\n\n"));
	HDDEBUG(SHOW_INIT, printf("NCPD = %x  NTPC = %x NSPT = %x PSTART = %x PLEN = %x\n", hdinfo[0]->ncpd, hdinfo[0]->ntpc, hdinfo[0]->nspt, hdinfo[0]->pstart, hdinfo[0]->plen));


	HDDEBUG(SHOW_INIT, printf("DRIVE 1 ********\n\n"));
	HDDEBUG(SHOW_INIT, printf("NCPD = %x  NTPC = %x NSPT = %x PSTART = %x PLEN = %x\n", hdinfo[1]->ncpd, hdinfo[1]->ntpc, hdinfo[1]->nspt, hdinfo[1]->pstart, hdinfo[1]->plen));

	off = hdoff[unit];	/* get partition table entry */

	if (off[boff].len == 0)
		return (-ENXIO);

	io->i_boff = off[boff].start * hdinfo[unit]->nspt * hdinfo[unit]->ntpc;

	HDDEBUG(SHOW_ORDWR,
		printf("HD: boff=%d ==>%d\n", boff, io->i_boff));

	return (0);

}


/*
 *
 *       PERFORM SPECIAL DISK FUNCTIONS
 *
 */

hdioctl(io, cmd, arg)
	register struct iob *io;
	register int    cmd;
	register char  *arg;
{
	register int    unit = io->i_unit;
	int             flag;

	HDDEBUG(SHOW_IOCTL,
		printf("unit=%x type=%x hdioctl(%x,%x,%x)\n", unit, hd_type[unit], io, cmd, arg));
	switch (cmd) {

	case (SAIODEBUG):

		flag = (int) arg;
		if (flag > 0)
			hddebug |= flag;
		else
			hddebug &= ~flag;
		return (0);

	case (SAIODEVDATA):

		{
			register struct st *tmp = (struct st *) arg;

			tmp->nsect = (short) hdinfo[unit]->nspt;
			tmp->ntrak = (short) hdinfo[unit]->ntpc;
			tmp->nspc = (short) hdinfo[unit]->nspt * hdinfo[unit]->ntpc;
			tmp->nbps = BSIZE;
			tmp->ncyl = (short) hdinfo[unit]->plen - BOOTCYLS;
			tmp->steprate = 0;
			tmp->precompcyl = 0;
			tmp->off = (struct partab *) hdoff[unit];
			return (0);
		}

	case (SAIOECCMSGS):

		hd_ecc_msgs = *arg;
		return (0);

	case (SAIOGETPART):

		{
			register struct dkpart *dk = (struct dkpart *) arg;

			dk->dk_size = hdoff[unit][io->i_boff].len;	/* size */
			dk->dk_start = hdoff[unit][io->i_boff].start * hdinfo[unit]->nspt * hdinfo[unit]->ntpc;
			dk->dk_blocksize = BSIZE;
			dk->dk_ntrack = hdinfo[unit]->ntpc;
			dk->dk_nsector = hdinfo[unit]->nspt;
			dk->dk_ncyl = hdinfo[unit]->ncpd;
			return (0);
		}
	}
	io->i_error = ECMD;
	return (-ECMD);
}


/*
 *	Strategy Routine:
 *	Arguments:
 *	  Pointer to  I/O buffer structure
 *	  R/W function flag
 *	Function:
 *	  Start up the device
 */


hdstrategy(io)
	register struct iob *io;
{
	register int    unit = io->i_unit;
	register int    sector, cylinder, head;
	register int    maxcount, xfercount, blockno;
	register char  *memaddr;
	register u_short nspc, nspt, ntpc;
	register int    old_window;
	struct pc_stat *hd_stat;

	if ((hdinfo[unit]->flag & HDINFO_PRESENT) == 0)
		return (-ENXIO);

	old_window = get_512_window();	/* Save the current window */
	hd_stat = (struct pc_stat *) (set_512_window(hd_pc_cb) + pcif_512_fw);
	hd_stat += unit;

	xfercount = io->i_cc;	/* save the count requested */
	memaddr = io->i_ma;	/* save the memory address */
	blockno = io->i_bn;
	ntpc = hdinfo[unit]->ntpc;	/* XXXXXX  temp fix for compiler bug */
	nspt = hdinfo[unit]->nspt;
	nspc = ntpc * nspt;

	while (xfercount) {
		sector = blockno;	/* calc the cyl, head and */
		cylinder = sector / nspc;
		sector %= nspc;
		head = (sector / hdinfo[unit]->nspt);
		sector = (sector % hdinfo[unit]->nspt) + 1;

		if (xfercount > MAXTRANS)	/* 32K maximum transfer */
			maxcount = MAXTRANS;
		else
			maxcount = xfercount;

		hd_dpl[unit].drive = unit + 0x80;	/* load up the dpl */
		hd_dpl[unit].sector = (u_short) sector;
		hd_dpl[unit].cyl = (u_short) cylinder;
		hd_dpl[unit].head = (u_short) head;
		hd_dpl[unit].number = (u_short) ((maxcount + BSIZE - 1) / BSIZE);

		/* Check for FORMAT flags here */

		switch (io->i_flgs & F_TYPEMASK) {

		case (F_RDDATA):
			hd_dpl[unit].op_code = DISKOP_READ;
			break;

		case (F_RDDATA | F_VERIFY):
			hd_dpl[unit].op_code = DISKOP_VERIFY;
			break;

		case (F_WRDATA):
			hd_dpl[unit].op_code = DISKOP_WRITE;
			break;

		case (F_FORMAT | F_WRDATA):
			hd_dpl[unit].op_code = DISKOP_FORMAT;
			hd_dpl[unit].number = hdinfo[unit]->nspt;
			break;

		default:
			io->i_error = ECMD;	/* mark error type */
			set_512_window(old_window);
			return (-ECMD);
		}

		hd_dpl[unit].atr_addr[0] = (u_long) memaddr;
		hd_dpl[unit].atr_cnt[0] = maxcount;

		hd_dpl[unit].atr_addr[1] = 0;
		hd_dpl[unit].atr_cnt[1] = 0;

		HDDEBUG(SHOW_INIT, printf("HD: STRATEGY CALLING PC_REQ\nOP=%x DR=%x SEC=%x CYL=%x HEAD=%x NUM=%x\n", hd_dpl[unit].op_code, hd_dpl[unit].drive, hd_dpl[unit].sector, hd_dpl[unit].cyl, hd_dpl[unit].head, hd_dpl[unit].number));

		hd_stat->return_code = 0;
		if (pc_req(CB_HDREQ, &hd_dpl[unit], HDENT) < 0) {
			set_512_window(old_window);
			return (-EIO);
		}

		if (pc_poll(&hd_stat->return_code, BIOS_ERROR | BIOS_RET_OK) < 0) {
			set_512_window(old_window);
			return (-EIO);
		}

		if (hd_stat->return_code & BIOS_ERROR) {
			printf("HD%d: BIOS ERROR CODE = %x\n", unit, (char) (hd_stat->return_code & 0x00ff));
			io->i_errblk = hd_stat->badblock - (nspc * hdinfo[unit]->pstart);

			switch (hd_stat->return_code & 0x00ff) {

			case BIOS_WR_FAULT:
			case BIOS_UNDEF_ERR:
			case BIOS_CRC_ERR:
			case BIOS_BAD_SECTOR:
				io->i_error = EECC;
				set_512_window(old_window);
				return (-EECC);
			default:
				io->i_error = EIO;
				set_512_window(old_window);
				return (-EIO);
			}
		}
		memaddr += maxcount;
		blockno += hd_dpl[unit].number;
		xfercount -= maxcount;

	}			/* end of while */


	set_512_window(old_window);
	return (io->i_cc - xfercount);	/* everything is ok */
}



hdsetup(unit)
	register int    unit;
{
	struct iob      io_setup;
	register struct iob *io = &io_setup;
	struct minidirectory minidirectory;
	register struct minidirectory *md = &minidirectory;
	register struct pc_stat *hd_stat;
	register u_short nspc, nspt, ntpc;
	register int    old_window;
	int result = -EIO;

	old_window = get_512_window();	/* Save the current window */
	hd_stat = (struct pc_stat *) (set_512_window(hd_pc_cb) + pcif_512_fw);
	hd_stat += unit;
	set_512_window(old_window);

	ntpc = hdinfo[unit]->ntpc;	/* XXXXXX  temp fix for compiler bug */
	nspt = hdinfo[unit]->nspt;
	nspc = ntpc * nspt;


	if ((hdinfo[unit]->flag & HDINFO_PRESENT) == 0)
		goto done;	/* no drive here */

	hd_dpl[unit].op_code = DISKOP_RESET;	/* reset the drive */
	hd_dpl[unit].drive = unit + 0x80;

	hd_stat->return_code = 0;
	if (pc_req(CB_HDREQ, &hd_dpl[unit], HDENT) < 0)
		goto done;	/* request, return an error */

	if (pc_poll(&hd_stat->return_code, BIOS_ERROR | BIOS_RET_OK) < 0)
		goto done;

	if (hd_stat->return_code & BIOS_ERROR) {
		printf("HD%d: BIOS ERROR CODE = %x\n", unit, (char) (hd_stat->return_code & 0x00ff));
		goto done;	/* return error */
	}
	bzero(hdoff[unit], NPART * sizeof(struct partab));	/* zero the partab */

	hdoff[unit]['h'-'a'].start = 0;	/* h partition starts at 0 */
	hdoff[unit]['h'-'a'].len = hdinfo[unit]->ncpd * hdinfo[unit]->ntpc * hdinfo[unit]->nspt;	/* the whole drive */

	if (hdinfo[unit]->flag & HDINFO_42PART) {	/* if there's a 4.3 part */
		hdoff[unit][2].start = hdinfo[unit]->pstart + BOOTCYLS;	/* c part */
		hdoff[unit][2].len = hdinfo[unit]->plen * nspc;

		/*
		 * read in the minidisk dir and setup the logical partitions 
		 */

		io->i_unit = unit;
		io->i_cc = sizeof(struct minidirectory);
		io->i_ma = (char *) md;
		io->i_flgs = F_RDDATA;
		io->i_bn = (hdoff[unit][2].start * nspc) + MINIDISK_BLOCK;

		if (hdstrategy(io) < 0) {	/* if the read fails, notify
						 * user */
			printf("HD%d: Could not read minidisk information.\n", unit);
			result = 0;
			goto done;
		}
		if (!dkfindpart(md, hdoff[unit], nspc, hdinfo[unit]->pstart + BOOTCYLS, unit, "hd")) {
/*			printf("HD%d: Invalid/empty minidisk table\n", unit); /* /* */
			result = 0;
			goto done;
		}
	}			/* end if there is a 4.3 part */
	result = 0;
done:
	set_512_window(old_window);
	return (result);
}

