/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

/* $Header:abios_fd.c 12.0$ */
/* $ACIS:abios_fd.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/pc_code/RCS/abios_fd.c,v $ */

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

#include <dos.h>
#include <stdio.h>
#include <sys/types.h>
#include "pcparam.h"
#include "rb.h"
#include "abios_st.h"
#include "dio.h"
#include "trace.h"

#ifdef ABIOS
/* #undef	DISKDEBUG
#define DISKDEBUG ABIOSDEBUG */

extern u_short *dbugptr;
extern char far *buff_fdio;
extern struct Device_info device_info[];
extern void fdirq();

u_short	fdop_map();
u_short  fderror_map();
u_short	fdstart();

/*
 * Characteristics of drives attached to system
 *   Used by: loadboot(), fddiskgeometry()
 */
struct drvparm  fdparm[NFD] = {0};


/*
 * The return code of each disk operation is placed in the one of the
 * following arrays. The address of each of these data items is
 * placed into the cbcb.cb_ent[XXX].pc_cb field so that the Unix
 * code can get to the data.
 */

static char     fdr_buff[64];
struct dio_rtn_codes *fd_return_code = (struct dio_rtn_codes *) fdr_buff;
#define MIN(x,y) (((x) < (y)) ? (x) : (y))


pcdpl_t		fddpl;
pcdpl_t		*fddptr = 0;

struct		fdsoftc {
	u_short		status;
#define	IDLE		0
#define	IN_TRANSFER	1
#define BAD_BLOCK_RECOVER 2
#define PIO_TRANSFER	3
#define GET_PARAMETERS	4
	u_short		retries;
	u_short		max_retry;
	u_short		fdtimeout;
	u_short		fddelay;
	u_short		byte_per_block;
	u_short		fdmotordelay;	/* time to delay to turn motor off */
	u_short		fdmotoroff;	/* variable to set the start it */
	u_short		blocks_left;
	u_short		max_transfer;
	u_short		current_transfer;
	u_char		irq;
} fd_softc;

struct Fdrequest	fdrequest;
struct Fdrequest 	fdtimereq;	/* request blck to turn off the motor */
#define FD_TIMEOUT	2

/*************************************************************
 * fddiskop(dplqe) - Do a diskette operation
 *
 *  This routine is called from the main program to do a disk 
 *  operation. It build the proper request structure, then calls
 *  ABIOS start.
 *
 */
void 
fddiskop(pcplqe)
	pcplqaddr_t     pcplqe;
{
	register int    i;
	u_short         s;
	u_short		max_t;
	int             rc = 0, drive;
	int		fdstat;

#ifdef DEBUG
	int             ldebug = *dbugptr & DISKDEBUG;
#endif /* DEBUG */

	/*
	 * First read the DPL down from Unix 
	 */
	DEBUGF(ldebug, printf("fddiskop: pcplqe = %#lx\n", (u_long) pcplqe));
	DEBUGF(ldebug, printf("fddiskop: Copy the dpl from Unix\n"));

	rc = movein((u_long) pcplqe->unix_cb, (char far *)&fddpl, sizeof(pcdpl_t));

	if (rc < 0)
	{
		DEBUGF(ldebug, printf("fd:pc code: Can't read DPL\n"));
		fddone(rc);
		return;
	}

	TRACE(TRACE_DISK, (char far *)&fddpl, sizeof (pcdpl_t));

	/*
	 * Change byte order 
	 */

	fddpl.op_code = exchw(fddpl.op_code);
	drive = fddpl.drive = (exchw(fddpl.drive) & 0x00ff);
	fddpl.head = exchw(fddpl.head);
	fddpl.cyl = exchw(fddpl.cyl);
	fddpl.sector = exchw(fddpl.sector);
	fddpl.number = exchw(fddpl.number);

	i = 0;
	while ((fddpl.atr_cnt[i] = exchw(fddpl.atr_cnt[i])) > 0)
	{
		fddpl.atr_addr[i] = exchl(fddpl.atr_addr[i]);
		DEBUGF(ldebug, printf("fddiskop: %d: addr %#lx cnt %#x\n",
			       i, fddpl.atr_addr[i], fddpl.atr_cnt[i]));
		if (++i >= 32)
			break;
	}
	if (fddpl.op_code == DISKOP_STATUS) {
		fdstat = 0;
		rc = fddiskgeometry(drive, &fdstat);
		if (rc >= 0)
			rc = fdstat;
		fdrequest.r_unit = drive;
		fddone(rc);
		return;
	}

	/* now fill out the request field for the first transfer */
	fdrequest.r_logical_id = device_info[FD_ID].logical_id;
	fdrequest.r_unit = drive;
	fdrequest.r_function = fdop_map(fddpl.op_code);
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fdrequest.fr_cylinder = fddpl.cyl;
	fdrequest.fr_head = fddpl.head;
	fdrequest.fr_sector =fddpl.sector;
	max_t = (fdparm[drive].maxsec - fddpl.sector) +
		(fdparm[drive].maxhd - fddpl.head)*fdparm[drive].maxsec + 1;
	fdrequest.fr_number_of_blocks = MIN(max_t,fddpl.number);
	fdrequest.fr_log_addr = buff_fdio;
	fdrequest.fr_phys_addr = (char far *) physaddr((u_long)buff_fdio,
					     (u_short far *)0,(u_short far *)0);

	/* initialize the soft c structure */
	fd_softc.retries = 0;
	fd_softc.status = IN_TRANSFER;
	fd_softc.blocks_left = fddpl.number;
	if (fddpl.op_code == DISKOP_FORMAT) {
		fdrequest.fr_sub_function == 0;
		fd_softc.blocks_left = 0;
	}
	fd_softc.current_transfer = fdrequest.fr_number_of_blocks;

	DEBUGF(ldebug,
	 printf("fd:transfering %d blocks from (%d,%d,%d) to %lx (%lx)\n",
		fdrequest.fr_number_of_blocks,fddpl.cyl,
		fddpl.head,fddpl.sector,fdrequest.fr_phys_addr,
		fdrequest.fr_log_addr);
	);

	/* copy in the unix stuff for writes */
	if ((fddpl.op_code == DISKOP_WRITE) || (fddpl.op_code == DISKOP_FORMAT))
	{
		u_short	i=0,cnt;
		char far *pcaddr = buff_fdio;

		while ((cnt = fddpl.atr_cnt[i]) > 0) {
			rc = movein(fddpl.atr_addr[i], pcaddr, cnt);
			if (rc < 0)
				break;
			pcaddr += cnt;
			i++;
		}
	}
	if (fdrequest.r_function == FD_INV_FUNCTION) {
		fddone(INV_FUNCTION);
		return;
	}
	if (rc == 0) {
		if ((rc = fdstart()) == FDSTAGED) {
			return;
		}
	}

	fddone(rc);
}


/*
 * fddiskgeometry() - Find out what type of drive we have.
 *
 * Input: drive - Bios drive number of disk
 *        fdstat - Place status information here.
 *
 * Global Data Areas:
 *
 *       Fills in either fdparm or fdparm based on drive number.
 *
 */

int fddiskgeometry(drive, fdstat)
	int             drive, *fdstat;
{
	int		media;
	int             rc;

#ifdef DEBUG
	int             ldebug = *dbugptr & DISKDEBUG;
#endif /* DEBUG */


	DEBUGF(ldebug, printf("fddiskgeometry: drive %#x\n", drive));

	/*
	 * Get the device paramter information (media type).
	 */
	fdrequest.r_logical_id = device_info[FD_ID].logical_id;
	fdrequest.r_unit = drive;
	fdrequest.r_function = ABIOS_READ_PARAMETER;
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fd_softc.status = GET_PARAMETERS;

	/*
	 * call abios
	 */
	if (fdstart() == FDSTAGED) {
		while (fd_softc.status != IDLE);
	}
	fd_softc.status = IDLE;
	if (fdrequest.r_return_code != 0)
	{
		DEBUGF(ldebug, printf(
			"fd drive %#x not present, or failed! ax = %#x\n",
			 	drive, fdrequest.r_return_code));
		return (-1);
	}

	fd_softc.max_retry = fdrequest.fr_max_retries;
	fd_softc.fdmotordelay = (u_short)(((u_long)
			    (fdrequest.fr_motor_off*182)+9999999)/(10000000));
	media = fdrequest.fr_media_type;

	/*
	 * reset the adapter
	 */

	fdrequest.r_unit = 0;
	fdrequest.r_function = ABIOS_RESET;
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fd_softc.status = GET_PARAMETERS;

	if (fdstart() == FDSTAGED) {
		while (fd_softc.status != IDLE);
	}

	if (fdrequest.r_return_code != 0)
	{
		DEBUGF(*dbugptr & DISKDEBUG,printf(
			"fd:drive not present, or failed! ax = %#x\n",
					          fdrequest.r_return_code));
	}
	fd_softc.status = IDLE;

	/*
	 * read the media specific parameters
	 */
	fdrequest.r_function = FD_READ_MEDIA;
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fd_softc.status = GET_PARAMETERS;

	/*
	 * call abios
	 */
	if (fdstart() == FDSTAGED) {
		while (fd_softc.status != IDLE);
	}
	fd_softc.status = IDLE;
	if (fdrequest.r_return_code != 0)
	{
		DEBUGF(ldebug, printf(
			"fd drive %#x not present, or failed! ax = %#x\n",
			 	drive, fdrequest.r_return_code));
		fd_softc.fdmotoroff = fd_softc.fdmotordelay;
		return (-1);
	}

	fdparm[drive].maxsec = fdrequest.fr_sectors_per_track;
	fdparm[drive].maxhd = fdrequest.fr_number_heads - 1;
	fdparm[drive].maxcyl = fdrequest.fr_number_cylinders - 1;
	fd_softc.max_transfer = fdrequest.fr_number_heads 
					* fdrequest.fr_sectors_per_track;

	/* the rest is just to get status info */
	if (fdstat == (int *) 0) {
		return(0);
	}

	/*
	 * determine the media type
	 */
	switch (media) {
		case FD_3_1440K:
			if (fdparm[drive].maxsec == 9) {
				*fdstat = FD3LO;
			} else {
				*fdstat = FD3HI;
			}
			break;
		case FD_5_360K:
			*fdstat = FD5LO;
			break;
		default:
			printf("fdabios: Unknown media %d, default to 720K\n",
					media);
			*fdstat = FD3LO;
			break;
	}

	/*
	 * Check for write protect. 1) read block 0 into a buffer. 
	 * 2) try to write it back out. If it fails, the drive is write
	 * protected. 
	 *  First read the block.
	 */
	fdrequest.r_function = ABIOS_READ;
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fdrequest.fr_cylinder = 0;
	fdrequest.fr_head = 0;
	fdrequest.fr_sector = (u_short) 1;
	fdrequest.fr_number_of_blocks = 1;
	fd_softc.current_transfer = fdrequest.fr_number_of_blocks;
	fdrequest.fr_log_addr = buff_fdio;
	fdrequest.fr_phys_addr = (char far *) physaddr((u_long)buff_fdio,
					     (u_short far *)0,(u_short far *)0);

	/* initialize the soft c structure */
	fd_softc.retries = 0;
	fd_softc.blocks_left = 1;
	fd_softc.status = PIO_TRANSFER;

	DEBUGF(ldebug,
	   printf("transfering (%d,%d,%d) go %ld (%ld)\n",
		fdrequest.fr_cylinder,fdrequest.fr_head,fdrequest.fr_sector,
		fdrequest.fr_phys_addr,fdrequest.fr_log_addr);
	);
	/*
	 * call abios
	 */
	if (fdstart() == FDSTAGED) {
		while (fd_softc.status != IDLE);
	}
	fd_softc.status = IDLE;
	if (fdrequest.r_return_code != 0)
	{
		DEBUGF(ldebug, printf(
			"fd drive %#x not present, or failed! ax = %#x\n",
			 	drive, fdrequest.r_return_code));
		fd_softc.fdmotoroff = fd_softc.fdmotordelay;
		return (-1);
	}

	/*
	 * now write it
	 */
	fdrequest.r_function = ABIOS_WRITE;
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fdrequest.fr_number_of_blocks = 1;
	fd_softc.current_transfer = fdrequest.fr_number_of_blocks;

	/* initialize the soft c structure */
	fd_softc.retries = 0;
	fd_softc.blocks_left = 1;
	fd_softc.status = PIO_TRANSFER;

	/*
	 * call abios
	 */
	if (fdstart() == FDSTAGED) {
		while (fd_softc.status != IDLE);
	}
	fd_softc.status = IDLE;
	if (fdrequest.r_return_code != 0)
	{
		DEBUGF(ldebug, printf("write protected\n"););
		*fdstat |= FDWP;
	}

	fd_softc.fdmotoroff = fd_softc.fdmotordelay;
	return(0);
}

/*
 * field hard disk interupts
 */
fdint(lvl)
	int lvl;
{
	int	rc = 0;

	/*
	 * No one active
	 */
	if ((fd_softc.status == IDLE) || 
			(fdrequest.r_return_code == ABIOS_UNDEFINED) || 
						(fdrequest.r_return_code == 0)){
		DEBUGF(*dbugptr & PCINTDEBUG,
			rbprintf("fdint:stray interupt %d %x\n",fd_softc.status,
						       fdrequest.r_return_code);
		);
		fdeoi(lvl);
		return(1);
	}

	/*
	 * ask abios to deal with this
	 */
	if (lvl != FD_TIMEOUT) {
		abios_int(&fdrequest);
	} else {
		abios_timeout(&fdrequest);
	}

	/*
	 * continue to stage this
	 */
	if (ABIOS_STAGED(fdrequest.r_return_code)) {
		if (fdrequest.r_return_code & ABIOS_STAGE_ON_TIME) {
			fd_softc.fddelay = (u_short)(((u_long)
			     (fdrequest.fr_delay_time * 182)+9999999)/(10000000));
		}
		/* timeout is in seconds * 8 */
		fd_softc.fdtimeout = (fdrequest.r_time_out * 182)/(80);
		fdeoi(lvl);
		return((fdrequest.r_return_code & ABIOS_NOT_MY_INT) != 0);
	}
	/*
	 * turn off timer
	 */
	fd_softc.fdtimeout = 0;

	/*
	 * get param request is all done at this point
 	 */
	if (fd_softc.status == GET_PARAMETERS) {
		fd_softc.status = IDLE;
		fdeoi(lvl);
		return(0);
	}


	/*
	 * start the next leg if necessary
	 */
fd_restart:
	if (fdrequest.r_return_code == 0) {
		/* check for more stuff to transfer */
		fd_softc.blocks_left -= fdrequest.fr_blocks_transfered;
		if (fd_softc.blocks_left > 0) {
			DEBUGF(*dbugptr & PCINTDEBUG,
			  rbprintf("fd_2nd-half:: transfered %d\n",
					fdrequest.fr_blocks_transfered);
			);
			if (fdrequest.fr_blocks_transfered) {
				fdrequest.fr_cylinder += 1;
				fdrequest.fr_sector = 1;
				fdrequest.fr_head = 0;
			}
			fdrequest.fr_phys_addr +=(fdrequest.fr_blocks_transfered
						    * fd_softc.byte_per_block);
			fdrequest.fr_log_addr = (char far *) segaddr((u_long)
							fdrequest.fr_phys_addr);
			fdrequest.fr_number_of_blocks =MIN(fd_softc.blocks_left,
							fd_softc.max_transfer);
			fd_softc.current_transfer = 
						fdrequest.fr_number_of_blocks;
			fdrequest.r_return_code = ABIOS_UNDEFINED;
			fd_softc.retries = 0;

			if (fdstart() != HDSTAGED) {
				goto fd_restart;
			}
			fdeoi(lvl);
			return(0);
		}
		/*
		 * if this is a transfer for unix, tell unix that it is done
		 */
		if (fd_softc.status == IN_TRANSFER)  {
			/*
			 * well a read is almost done...
			 */
			if (fddpl.op_code == DISKOP_READ) {
				DEBUGF(*dbugptr & DISKDEBUG,
					rbprintf("read complete\n");
				);
				fddptr = &fddpl;
				fdeoi(lvl);
				return(0);
			}
			fddone(0);
		}
		fd_softc.status = IDLE;
		fdeoi(lvl);
		return(0);
	}

	/*
	 * Ooops, something is wrong??
	 */
	/* is it retryable? */
	if (ABIOS_RETRY(fdrequest.r_return_code) && (fd_softc.retries++ <
							 fd_softc.max_retry)) {
			DEBUGF(*dbugptr & PCINTDEBUG,
			 rbprintf("fdretry,error=%x\n",fdrequest.r_return_code);
			);

			fdrequest.r_return_code = ABIOS_UNDEFINED;
			fdrequest.fr_number_of_blocks = 
						     fd_softc.current_transfer;
			if (fdstart() != FDSTAGED) {
				goto fd_restart;
			}
			fdeoi(lvl);
			return(0);
	}

	/* can't recover from the error */
	if (fd_softc.status == IN_TRANSFER)  {
		fddone(fderror_map(fdrequest.r_return_code));
	} else {
		fd_softc.status = IDLE;
	}
	fdeoi(lvl);
	return(0);
}

#define SEOI	0x60
fdeoi(lvl)
	int lvl;
{
	/* don't eoi if there is not interupt */
	if (lvl != 1)
		return;
	if (fd_softc.irq == 0)
		return;
	if (fd_softc.irq >= 8) {
		eoi_slave((fd_softc.irq & 0x7)+SEOI);
	} else {
		eoi_master(fd_softc.irq+SEOI);
	}
}

fdinit()
{
	/*
	 * Get logical device info
	 */
	fdrequest.r_logical_id = device_info[FD_ID].logical_id;
	fdrequest.r_current_req_blck_len = sizeof(struct Fdrequest);
	fdrequest.r_unit = 0;
	fdrequest.r_function = ABIOS_LOGICAL_PARAMETER;
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fdrequest.fr_reserved_1 = 0;
	abios_start(&fdrequest);
	if ( fdrequest.r_return_code !=0 ) {
		pcpanic("bad fd abios");
	}

#ifdef DEBUG
	if (*dbugptr & DISKDEBUG) {
		u_short flags = fdrequest.r_logical_id_flags;
		printf(
		 "fd abios: %s %s%s%sintr=%d arb=%d block=%d id=%d rev=%d\n",
		   flags & OVERLAP_IO?"<Overlap>":"<Single Thread>",
		    flags & LOG_POINTER?"<Log Ptr> ":"",
		     flags & PHYS_POINTER?"<Phys Ptr> ":"",
		      GET_POINTER_TYPE(flags)?"":"<No Ptrs> ",
			fdrequest.r_hardware_intr,fdrequest.r_arbitration_level,
			fdrequest.r_request_block_length,
		          fdrequest.r_secondary_id,fdrequest.r_revision_level);
	}
#endif /* DEBUG */

	/*
	 * use that info to allocate the interupt level
	 */
	if (fdrequest.r_hardware_intr != ABIOS_NO_INT) {
		chain_int(fdrequest.r_hardware_intr,(u_long)fdirq);
		fd_softc.irq = fdrequest.r_hardware_intr;
	}

	if (fdrequest.r_request_block_length > sizeof(struct Fdrequest)) {
		pcpanic("fd request block is too small");
	}

	/*
	 * reset the adapter
	 */

	fdrequest.r_unit = 0;
	fdrequest.r_function = ABIOS_RESET;
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fd_softc.status = GET_PARAMETERS;

	if (fdstart() == FDSTAGED) {
		while (fd_softc.status != IDLE);
	}

	if (fdrequest.r_return_code != 0)
	{
		DEBUGF(*dbugptr & DISKDEBUG,printf(
			"fd:drive not present, or failed! ax = %#x\n",
					          fdrequest.r_return_code));
	}
	fd_softc.status = IDLE;

	/*
	 * now read the device info
	 */
	fdrequest.r_unit = 0;
	fdrequest.r_function = ABIOS_READ_PARAMETER;
	fdrequest.r_return_code = ABIOS_UNDEFINED;
	fd_softc.status = GET_PARAMETERS;

	/*
	 * call abios
	 */
	if (fdstart() == FDSTAGED) {
		while (fd_softc.status != IDLE);
	}
	if (fdrequest.r_return_code != 0)
	{
		DEBUGF(*dbugptr & DISKDEBUG,printf(
			"fd drive %#x not present, or failed! ax = %#x\n", 0,
					          fdrequest.r_return_code));
		return;
	}

#ifdef DEBUG
	if (*dbugptr & DISKDEBUG) {
	 u_short flags = fdrequest.fr_device_control;
	 printf("FD ABIOS: %s%s%s%sS/T=%d T/C=%d C/D=%d sec_size=%d media=%d\n",
	  (flags & FD_RECALIBRATE? "<recal needed> ": ""),
	   (flags & FD_CONCURRENT ? "<concurrent> ":""),
	    (flags & FD_UNIT_FORMAT ? "<format unit> " :""),
	     (flags & FD_CHANGE_SUPPORT ? "<Change> ": ""),
	     fdrequest.fr_sectors_per_track,fdrequest.fr_number_heads,
	     fdrequest.fr_number_cylinders,fdrequest.fr_bytes_per_sector,
						     fdrequest.fr_media_type);
	}
#endif /* DEBUG */
	fd_softc.status = IDLE;

	fd_softc.fdmotordelay = (u_short)(((u_long)
			    (fdrequest.fr_motor_off*182)+9999999)/(10000000));
	fd_softc.max_retry = fdrequest.fr_max_retries;
	fd_softc.byte_per_block = 512;	/* need to use define */
	/* turn off the motor */
	fdtimereq.r_logical_id = device_info[FD_ID].logical_id;
	fdtimereq.r_current_req_blck_len = sizeof(struct Fdrequest);
	fdtimereq.r_function = FD_MOTOR_OFF;
	fd_softc.fdmotoroff = fd_softc.fdmotordelay;

}

/*
 * map PC REQ op codes to
 * ABIOS opcodes.
 */
u_short
fdop_map(op)
	u_short op;
{
	switch (op) {
	case DISKOP_READ:
		return(ABIOS_READ);
	case DISKOP_WRITE:
		return(ABIOS_WRITE);
	case DISKOP_VERIFY:
		return(FD_VERIFY);
	case DISKOP_RESET:
		return(ABIOS_RESET);
	case DISKOP_FORMAT:
		return(FD_FORMAT);
	default:
		return(FD_INV_FUNCTION);
	}
}

u_short
fderror_map(error_code)
	u_short error_code;
{
	u_short	error;
	/*
	 * no failures detected
	 */
	if ((error_code & ABIOS_FAILED_OP) == 0) {
		return(0);
	}

	/*
	 * parameter errors
	 */
	if (error_code & ABIOS_BAD_PARAM) {
		return(1);
	}

	/*
	 * The following will convert ABIOS errors to
	 * BIOS approx 90 % of the time...
 	 */
	if ((error = (error_code & 0xff)) > 0x10) {
		return( 0x8000 | (error & 0xf0) );
	}
	switch (error) {
	/* general error -> general error controller error*/
	/* reset failed -> general error controller error*/
	/* controler parameter failed -> general controller error */
	case 0:
	case 5:
	case 7:
		return(0x8020);
	/* defective sector -> requested sector not found */
	/* bad track -> requested sector not found */
	/* invalid sector to format -> requested sector not found */
	case 0xa:
	case 0xb:
	case 0xd:
		return(0x8004);
	/* CAM detected during read or verify -> CRC error */
	case 0xe:
	case 0xf:
		return(0x8010);
	default:
		return(0x8000 | error);
	}
}

/*
 * schedule events and delays
 */
fdtimer()
{
	/*
	 * device has timed out
	 */
	if (fd_softc.fdtimeout) {
		if ((--fd_softc.fdtimeout) == 0) {
			fdint(FD_TIMEOUT);
		}
	}

	/*
	 * timer has completed
	 */
	if (fd_softc.fddelay) {
		if ((--fd_softc.fddelay) == 0) {
			fdint(0);
		}
	}
	if (fd_softc.fdmotoroff) {
		if ((--fd_softc.fdmotoroff) == 0) {
			/* OOPS we need to deal with more than one floppy */
			fdtimereq.r_unit = 0;	
			fdtimereq.r_return_code = ABIOS_UNDEFINED;
			fdtimereq.fr_reserved_2 = 0;
			abios_start(&fdtimereq);
		}
	}
}

/*
 * start a transfer
 */
u_short
fdstart()
{
	int	s;

	/* no errors so far, start the transfer */
	fd_softc.fdmotoroff = 0;	/* keep the motor going if possible */
	/*
	 * driver requires all these to be set to zero before callin start
	 */
	fdrequest.fr_reserved_1 = 0;
	fdrequest.fr_reserved_2 = 0;
	fdrequest.fr_reserved_3 = 0;
	fdrequest.fr_reserved_4 = 0;
	abios_start(&fdrequest);
	s=spl();
	if (ABIOS_STAGED(fdrequest.r_return_code)) {
		/* stage on request time is in useconds */
		if (fdrequest.r_return_code & ABIOS_STAGE_ON_TIME) {
			fd_softc.fddelay = (u_short)(((u_long)
			    (fdrequest.fr_delay_time*182)+9999999)/(10000000));
		}
		/* timeout is in seconds * 8 */
		if (fdrequest.r_time_out != 0) {
			fd_softc.fdtimeout = (fdrequest.r_time_out * 182)/(80);
		}
		splx(s);
		return(FDSTAGED);
	}
	splx(s);
	return(fderror_map(fdrequest.r_return_code));
}

/*
 * unix transfer is complete, send interupt
 * and return code to the romp
 */
fddone(rc)
	u_short rc;
{
	int	s;

	DEBUGF(*dbugptr & DISKDEBUG,rbprintf("fddone:rc = %x\n",rc););

	if (rc >= 0)
		rc |= 0x1000;

	rc = exchw(rc);

	fd_softc.status = IDLE;
	s = spl();
	fd_softc.fdmotoroff = fd_softc.fdmotordelay;
	fd_return_code[fdrequest.r_unit].errno = rc;
	rompint3(3,FAKEPOLL(FDIRQ), 0, 0, cur_cbcb);
	splx(s);
}
#else  /* !ABIOS */
fdint(lvl)
{
	rbprintf("Unexpected call to fdint (ABIOS not defined!)\n");
}
#endif /* !ABIOS */
