
/*
 * Copyright 1987 Alan Kent
 *
 * Permission is granted to redistribute this code as long
 * as this message is retained in the code and the code is
 * not sold without written permission from the author.
 *
 * UUCP: {seismo,hplabs,mcvax,ukc,nttlab}!munnari!goanna.oz!ajk
 * ACSnet: ajk@goanna.oz
 * ARPA: munnari!goanna.oz!ajk@SEISMO.ARPA
 */

#include "hd.h"


void
perform_io ( ior )
struct IOExtHD *ior;
{
	register struct IOStdReq *sior;
	register int i;
	UBYTE *buf;
	struct hd_unit *unit;
	struct posn posn;
	int cmd;
	LONG num_secs;
	char *p;


	sior = &ior->iohd_TD.iotd_Req;
	unit = (struct hd_unit *) sior->io_Unit;
	sior->io_Error = 0;
	bad_error = 0;
	cmd = sior->io_Command;

	if ( cmd & TDF_EXTCOM ) {
		if ( ior->iohd_TD.iotd_Count < CHANGE_COUNT ) {
			sior->io_Error = TDERR_DiskChanged;
			cmd = TD_LASTCOMM + 1;
		}
		else if ( ior->iohd_TD.iotd_SecLabel != NULL ) {

			/* I didnt think theses were actually used! */

			sior->io_Error = TDERR_NoSecHdr;
			cmd = TD_LASTCOMM + 1;
		}
	}

	switch ( cmd ) {

	default :
		if ( sior->io_Error == 0 )
			sior->io_Error = IOERR_NOCMD;
		break;

	case ETD_MOTOR :
	case TD_MOTOR :
		sior->io_Actual = 1;  /* motor always on - cannot be switched off */
		break;

	case TD_REMOVE :

		/* cannot remove a harddisk, so not need to install interrupt */
		/* thingo */

		break;

	case TD_CHANGENUM :
		sior->io_Actual = CHANGE_COUNT;
		break;

	case TD_CHANGESTATE :
		sior->io_Actual = 0;
		break;

	case TD_PROTSTATUS :
		sior->io_Actual = 0;
		break;

	case ETD_CLEAR :
	case CMD_CLEAR :
		clear_all ();
		break;

	case ETD_UPDATE :
	case CMD_UPDATE :
		flush_all ();
		if ( sior->io_Error == 0 )
			sior->io_Error = bad_error;
		break;

	case ETD_READ :
	case CMD_READ :
		sior->io_Actual = 0;
		if ( calc ( sior , &posn , &num_secs ) ) {
			p = (char *) sior->io_Data;
			while ( num_secs-- > 0 ) {
				if ( posn.cylinder >= first.cylinders ) {
					sior->io_Error = TDERR_SeekError;
					break;
				}
				buf = read_cache ( &posn );
				if ( bad_error != 0  ||  buf == NULL )
					break;
				copy_sector ( buf , p );
				p += HD_SECTOR;
				sior->io_Actual += HD_SECTOR;
				next_posn ( &posn );
			}
		}
		if ( sior->io_Error == 0 )
			sior->io_Error = bad_error;
		break;

	case ETD_WRITE :
	case CMD_WRITE :
		sior->io_Actual = 0;
		if ( calc ( sior , &posn , &num_secs ) ) {
			p = (char *) sior->io_Data;
			while ( num_secs-- > 0 ) {
				if ( posn.cylinder >= first.cylinders ) {
					sior->io_Error = TDERR_SeekError;
					break;
				}
				write_cache ( &posn , p );
				if ( bad_error != 0 )
					break;
				p += HD_SECTOR;
				sior->io_Actual += HD_SECTOR;
				next_posn ( &posn );
			}
		}
		if ( sior->io_Error == 0 )
			sior->io_Error = bad_error;
		break;

	case TD_SEEK :
		if ( calc ( sior , &posn , &num_secs ) )
			sior->io_Error = wd_seek ( &posn );
		break;

	case TD_FORMAT :
		flush_all ();
		clear_all ();
		if ( calc ( sior , &posn , &num_secs ) ) {
			if ( posn.sector != 0 ) {
				sior->io_Error = TDERR_NotSpecified;
			}
			else {
				p = (char *) sior->io_Data;
				while ( num_secs-- > 0 ) {
					if ( posn.cylinder >= first.cylinders ) {
						sior->io_Error = TDERR_SeekError;
						break;
					}
					if ( posn.sector == 0 ) {
						sior->io_Error = wd_format_track ( &posn );
						if ( sior->io_Error != 0 )
							break;
					}
					write_sector ( &posn , p );
					if ( bad_error != 0 ) {
						sior->io_Error = bad_error;
						break;
					}
					p += HD_SECTOR;
					next_posn ( &posn );
					if ( posn.sector == 0 )
						p = (char *) sior->io_Data;
				}
			}
		}
		break;

	case HD_PARK :
		wd_park ();
		break;

	case HD_GETINFO :
		ior->iohd_Info = first;
		break;
	}

	/* if quick io, dont do a reply */

	if ( ( sior->io_Flags & IOF_QUICK ) == 0 )
		ReplyMsg ( ior );
}


next_posn ( posn )
register struct posn *posn;
{
	posn->block++;
	posn->sector++;
	if ( posn->sector >= first.sectors ) {
		posn->sector = 0;
		posn->surface++;
		if ( posn->surface >= first.heads ) {
			posn->surface = 0;
			posn->cylinder++;
		}
	}
}
