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

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

#ifndef lint
static char *rcsid = "$Header:abios_hd.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

#define MIN(x,y)	((x)>(y)?(y):(x))
extern u_short *dbugptr;
extern char far *buff_dio;
extern struct Device_info device_info[];
extern void hdirq();
extern struct rb_part rb_part[];
#define TICKS 182

u_short	hdop_map();
u_short hderror_map();
u_short	hdstart();

/*
 * Characteristics of drives attached to system
 *   Used by: loadboot(), hddiskgeometry()
 */
struct drvparm  hdparm[NHD] = {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     hdr_buff[64];
struct dio_rtn_codes *hd_return_code = (struct dio_rtn_codes *) hdr_buff;

pcdpl_t		hddpl;
pcdpl_t		*hddptr = 0;

struct		hdsoftc {
	u_short		status;
#define	IDLE		0
#define	IN_TRANSFER	1
#define BAD_BLOCK_RECOVER 2
#define PIO_TRANSFER	3
#define GET_PARAMETERS	4
#define GET_BAD_BLOCK	5
	u_short		retries;
	u_short		max_retry;
	u_short		max_transfer;
	u_short		blocks_left;
	u_short		hdtimeout;
	u_short		hddelay;
	u_short		byte_per_block;
	u_long		last_block;
	u_char		irq;
} hd_softc;

struct Hdrequest	hdrequest;
#define HD_TIMEOUT	2

/*************************************************************
 * hddiskop(dplqe) - Do a harddisk 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 
hddiskop(pcplqe)
	pcplqaddr_t     pcplqe;
{
	register int    i;
	u_short         s;
	int             rc = 0, drive;
	int		fdstat;

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

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

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

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

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

	/*
	 * Change byte order 
	 */

	hddpl.op_code = exchw(hddpl.op_code);
	drive = hddpl.drive = (exchw(hddpl.drive) & 0x00ff) - 0x80;
	hddpl.head = exchw(hddpl.head);
	hddpl.cyl = exchw(hddpl.cyl);
	hddpl.sector = exchw(hddpl.sector);
	hddpl.number = exchw(hddpl.number);

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

	/* now fill out the request field for the first transfer */
	hdrequest.r_logical_id = device_info[HD_ID].logical_id;
	hdrequest.r_unit = drive;
	hdrequest.r_function = hdop_map(hddpl.op_code);
	hdrequest.r_return_code = ABIOS_UNDEFINED;
	hdrequest.hr_block = (((u_long)hddpl.cyl * 
			(hdparm[drive].maxhd+1)) + hddpl.head)*
				hdparm[drive].maxsec + hddpl.sector - 1;
	hdrequest.hr_number_of_blocks = MIN(hddpl.number,hd_softc.max_transfer);
	hdrequest.hr_log_addr = buff_dio;
	hdrequest.hr_phys_addr = (char far *) physaddr((u_long)buff_dio,
					     (u_short far *)0,(u_short far *)0);
	hdrequest.hr_transfer_flags = HD_NO_CACHE;

	/* initialize the soft c structure */
	hd_softc.blocks_left = hddpl.number;
	hd_softc.retries = 0;
	hd_softc.status = IN_TRANSFER;

	DEBUGF(ldebug,
	printf("transfering %d blocks from %ld (%d,%d,%d) to %lx (%lx)\n",
		hdrequest.hr_number_of_blocks,hdrequest.hr_block,hddpl.cyl,
		hddpl.head,hddpl.sector,hdrequest.hr_phys_addr,
		hdrequest.hr_log_addr);
	);

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

		while ((cnt = hddpl.atr_cnt[i]) > 0) {
			rc = movein(hddpl.atr_addr[i], pcaddr, cnt);
			if (rc < 0)
				break;
			pcaddr += cnt;
			i++;
		}
	}
	if (hdrequest.r_function == HD_INV_FUNCTION) {
		hddone(INV_FUNCTION);
		return;
	}
	if (rc == 0) {
		if ((rc = hdstart()) == HDSTAGED) {
			return;
		}
	}

	hddone(rc);
}


/*
 * hdio()   - Send a request off to ABIOS, wait for response
 *
 */

int
hdio(op, drive, block, num, buff)
	int             op;
	int             drive;
	u_long		block;
	int             num;
	char far       *buff;	/* Data buffer for operation */
{
	int             cnt = 0, rc, retry, floppy = (drive < 2);


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

	DEBUGF(ldebug, 
	 printf("hdio:op=0x%x,drive=0x%x,block=0x%lx,num=0x%x,buff = 0x%lx\n",
		   op, drive, (u_long)block, num, (char far *) buff));

	/*
	 * build the request block
	 */
	hdrequest.r_logical_id = device_info[HD_ID].logical_id;
	hdrequest.r_unit = drive;
	hdrequest.r_function = hdop_map(op);
	hdrequest.hr_block = block;
	hdrequest.hr_number_of_blocks = num;
	hdrequest.hr_log_addr = buff;
	hdrequest.hr_phys_addr = (char far *) physaddr((u_long)buff,
					   (u_short far *)0,(u_short far *)0);
	hdrequest.hr_transfer_flags = HD_NO_CACHE;
	hdrequest.r_return_code = ABIOS_UNDEFINED;
	hd_softc.blocks_left = num;
	hd_softc.status = PIO_TRANSFER;

	rc = INV_FUNCTION;
	DEBUGF(ldebug,printf(
	    "starting transfer type=%d, block=%ld, cnt=%d, raddr=%lx (%lx)\n",
		hdrequest.r_function,hdrequest.hr_block,
			hdrequest.hr_number_of_blocks,hdrequest.hr_phys_addr,
							hdrequest.hr_log_addr);
	);
	DEBUGF(ldebug,printf("hd_softc.status = %d\n",hd_softc.status););
	if (hdrequest.r_function != HD_INV_FUNCTION) {
		if (hdstart() == HDSTAGED) {
			DEBUGF(ldebug,printf("hdio:staged! (%x)\n",
						hdrequest.r_return_code););
			while (hd_softc.status != IDLE);
		}
		rc = hderror_map(hdrequest.r_return_code);
	}

	DEBUGF(ldebug,printf("hd_softc.status = %d\n",hd_softc.status););
	DEBUGF(ldebug,printf("transfer completed code=%x (BIOS %x)\n",
		hdrequest.r_return_code,rc); );
	hd_softc.status = IDLE;
	return (rc);
}



/*
 * hddiskgeometry() - 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 hdparm or fdparm based on drive number.
 *
 */

int hddiskgeometry(drive, fdstat)
	int             drive, *fdstat;
{

	register int    i;

	int             rc;

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


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

	if (fdstat != (int *) 0)
		*fdstat = 0;

	/*
	 * build the request block
	 */
	hdrequest.r_logical_id = device_info[HD_ID].logical_id;
	hdrequest.r_unit = drive;
	hdrequest.r_function = ABIOS_READ_PARAMETER;
	hdrequest.r_return_code = ABIOS_UNDEFINED;
	hd_softc.status = GET_PARAMETERS;

	/*
	 * call abios
	 */
	if (hdstart() == HDSTAGED) {
		while (hd_softc.status != IDLE);
	}
	if (hdrequest.r_return_code != 0)
	{
		DEBUGF(ldebug, printf(
			"Drive %#x not present, or failed! ax = %#x\n", drive,
						    hdrequest.r_return_code));
		return (-1);
	}

	hd_softc.status = IDLE;

	hdparm[drive].maxsec = hdrequest.hr_sectors_per_track;
	hdparm[drive].maxhd = hdrequest.hr_number_heads - 1;
	hdparm[drive].maxcyl = hdrequest.hr_number_cylinders - 1;
	hd_softc.max_retry = hdrequest.hr_max_retries;
	hd_softc.max_transfer = hdrequest.hr_max_transfer? 
			hdrequest.hr_max_transfer: 0xffff;
	return(0);
}

/*
 * field hard disk interupts
 */
hdint(lvl)
	int lvl;
{
	int	rc = 0;
	u_long	goodblock;
	u_long	block;
	int	found;

	/*
	 * No one active
	 */
	rbprintf("hdinit: ");
	switch (lvl) {
	case 1:
		rbprintf("hardware ");
		break;
	case 0:
		rbprintf("delay ");
		break;
	case HD_TIMEOUT:
		rbprintf("time ");
		break;
	default:
		rbprintf("invalid ");
	}
	rbprintf("func=%d\n",hdrequest.r_function);
		
	if ((hd_softc.status == IDLE) || 
			(hdrequest.r_return_code == ABIOS_UNDEFINED) || 
						(hdrequest.r_return_code == 0)){
		DEBUGF(*dbugptr & PCINTDEBUG,
				rbprintf("hdint:stray interupt (%d)\n",lvl););
		hdeoi(lvl);
		return(1);
	}

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

	/*
	 * continue to stage this
	 */
	if (ABIOS_STAGED(hdrequest.r_return_code)) {
		if (hdrequest.r_return_code & ABIOS_STAGE_ON_TIME) {
			hd_softc.hddelay = (u_short)(((u_long)
			     (hdrequest.hr_delay_time * TICKS)+9999999)/(10000000));
		}
		/* timeout is in seconds * 8 */
		hd_softc.hdtimeout = ((hdrequest.r_time_out * TICKS)+79)/(80);
		if ((hd_softc.hdtimeout) && 
				(hd_softc.hddelay > hd_softc.hdtimeout)) {
			hd_softc.hdtimeout = (hd_softc.hddelay+TICKS/10);
		}
		hdeoi(lvl);
		return((hdrequest.r_return_code & ABIOS_NOT_MY_INT) != 0);
	}
	/*
	 * turn off timers
	 */
	hd_softc.hdtimeout = 0;
	hd_softc.hddelay = 0;

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


	/*
	 * start the next leg if necessary
	 */
hd_restart:
	if (hdrequest.r_return_code == 0) {
		/* check for more stuff to transfer */
		hd_softc.blocks_left -= hdrequest.hr_blocks_transfered;
		if (hd_softc.blocks_left > 0) {
			if (hd_softc.status == BAD_BLOCK_RECOVER) {
				hdrequest.hr_block = hd_softc.last_block+
						 hdrequest.hr_blocks_transfered;
				hd_softc.status = IN_TRANSFER;
			} else {
				hdrequest.hr_block+=
						 hdrequest.hr_blocks_transfered;
			}
			hdrequest.hr_phys_addr +=(hdrequest.hr_blocks_transfered
						    * hd_softc.byte_per_block);
			hdrequest.hr_log_addr = (char far *) segaddr((u_long)
							hdrequest.hr_phys_addr);
			hdrequest.hr_number_of_blocks =MIN(hd_softc.blocks_left,
							hd_softc.max_transfer);
			hdrequest.r_return_code = ABIOS_UNDEFINED;

			DEBUGF(*dbugptr & PCINTDEBUG,rbprintf("2nd-half\n"););
			if (hdstart() != HDSTAGED) {
				goto hd_restart;
			}
			hdeoi(lvl);
			return(0);
		}

		/*
		 * if this is a transfer for unix, tell unix that it is done
		 */
		if ((hd_softc.status == IN_TRANSFER) || 
				   (hd_softc.status == BAD_BLOCK_RECOVER)) {
			/*
			 * well a read is almost done...
			 */
			if (hddpl.op_code == DISKOP_READ) {
				hddptr = &hddpl;
				hdeoi(lvl);
				return(0);
			}
			hddone(0);
		}
		if (hd_softc.status == GET_BAD_BLOCK) {
			bbtinit(hdrequest.r_unit,buff_dio);
		}
		hd_softc.status = IDLE;
		hdeoi(lvl);
		return(0);
	}

	/*
	 * Ooops, something is wrong??
	 */
	/* is it retryable? */
	if (ABIOS_RETRY(hdrequest.r_return_code) && (hd_softc.retries++ <
							 hd_softc.max_retry)) {
			hdrequest.r_return_code = ABIOS_UNDEFINED;
			if (hd_softc.status == BAD_BLOCK_RECOVER) {
				hdrequest.hr_number_of_blocks = 1;
			} else {
				hdrequest.hr_number_of_blocks = 
							hd_softc.blocks_left;
			}

			DEBUGF(*dbugptr & PCINTDEBUG,rbprintf("retry\n"););
			if (hdstart() != HDSTAGED) {
				goto hd_restart;
			}
			hdeoi(lvl);
			return(0);
	}

	/* was it on a bad block?? */
	if (hd_softc.status == BAD_BLOCK_RECOVER) {
		/*
		 * that's it. we've done all we can
		 */
		rbprintf("bad bad block\n");
		hddone(hderror_map(hdrequest.r_return_code));
		hdeoi(lvl);
		return(0);
	}
	/* don't attempt forwarding on pc transfers */
	if (hd_softc.status == PIO_TRANSFER)  {
		/* PIO routines read the request.return_code field themselves */
		rbprintf("pio error\n");
		hd_softc.status = IDLE;
		hdeoi(lvl);
		return(0);
	}

	/* 
	 * well it looks like we have hit a real bad block
	 */
	found = 0;
	for (block = 0; block <= hdrequest.hr_blocks_transfered ; block++ ) {
	    if (badblklookup(hdrequest.r_unit,hdrequest.hr_block+block,
							&goodblock)) {
		found++;
		break;
	   }
	}

	if (found == 0) {
	/* block = hdrequest.hr_blocks_transfered;
	if (badblklookup(hdrequest.r_unit,hdrequest.hr_block+block,
							&goodblock) == 0) {
		/* can't find bad block  to forward to */
		DEBUGF(*dbugptr & PCINTDEBUG,
		   rbprintf("no bad block in table %x %ld + %d (out of %d)\n",
		      hdrequest.r_return_code,hdrequest.hr_block,
			hdrequest.hr_blocks_transfered,hd_softc.blocks_left););
		hddone(hderror_map(hdrequest.r_return_code));
		hdeoi(lvl);
		return(0);
	}
	

	/*
	 * grab the good block
	 */
	 DEBUGF(*dbugptr & PCINTDEBUG,
		rbprintf("badblk %ld->%ld (%d)\n",hdrequest.hr_block+block,
		goodblock,hdrequest.hr_blocks_transfered);
	 );
	hd_softc.last_block = hdrequest.hr_block+block;
	hdrequest.hr_block = goodblock;
	hdrequest.hr_phys_addr += block * hd_softc.byte_per_block;
	hd_softc.blocks_left -= block;

	/*
	 * note: hr_number_of_blocks and hr_blocks_transfered are the same 
	 * fields....
 	 */
	hdrequest.hr_number_of_blocks = 1;
	hdrequest.hr_log_addr = (char far *)segaddr(
					(u_long)hdrequest.hr_phys_addr);
	hd_softc.status = BAD_BLOCK_RECOVER;
	hdrequest.r_return_code = ABIOS_UNDEFINED;

	if (hdstart() != HDSTAGED) {
		goto	hd_restart;
	}
	hdeoi(lvl);
	return(0);
}

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

hdinit()
{
	/*
	 * Get logical device info
	 */
	hdrequest.r_logical_id = device_info[HD_ID].logical_id;
	hdrequest.r_current_req_blck_len = sizeof(struct Hdrequest);
	hdrequest.r_unit = 0;
	hdrequest.r_function = ABIOS_LOGICAL_PARAMETER;
	hdrequest.r_return_code = ABIOS_UNDEFINED;
	abios_start(&hdrequest);
	if ( hdrequest.r_return_code !=0 ) {
		pcpanic("bad hd abios");
	}

#ifdef DEBUG
	if (*dbugptr & DISKDEBUG) {
		u_short flags = hdrequest.r_logical_id_flags;
		printf(
		 "hd 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> ",
			hdrequest.r_hardware_intr,hdrequest.r_arbitration_level,
			hdrequest.r_request_block_length,
		          hdrequest.r_secondary_id,hdrequest.r_revision_level);
	}
#endif /* DEBUG */

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

	if (hdrequest.r_request_block_length > sizeof(struct Hdrequest)) {
		pcpanic("hd request block is too small");
	}

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

	/*
	 * call abios
	 */
	if (hdstart() == HDSTAGED) {
		while (hd_softc.status != IDLE);
	}
	if (hdrequest.r_return_code != 0)
	{
		DEBUGF(*dbugptr & DISKDEBUG,printf(
			"Drive %#x not present, or failed! ax = %#x\n", 0,
					          hdrequest.r_return_code));
		exit(1);
		return;
	}

#ifdef DEBUG
	if (*dbugptr & DISKDEBUG) {
		u_short flags = hdrequest.hr_device_control;
		printf(
"hd abios: (%ld M = %ld blocks) geometry(%ld,%d,%d) MT=%d %s%s%s%s%s%s%s%s%s%s%s%s\n",
		   (hdrequest.hr_max_block_number/2048),
		   hdrequest.hr_max_block_number,
		   hdrequest.hr_number_cylinders,hdrequest.hr_number_heads,
		    hdrequest.hr_sectors_per_track,hdrequest.hr_max_transfer,
		     flags & HD_TRACK_FORMAT?"TrackFormat ":".",
		     flags & HD_UNIT_FORMAT?"UnitFormat ":".",
		     HD_GET_FORMAT(flags)?"":"NoFormat ",
		     flags & HD_ST506?"ST506 ":".",
		     flags & HD_CONCURRENT?"concurrent ":"",
		     flags & HD_EJECTABLE?"ejectable ":"",
		     flags & HD_SEQUENTIAL?"sequential ":"random ",
		     flags & HD_LOCKABLE?"lockable ":"",
		     flags & HD_READABLE?"readable ":"",
		     flags & HD_CACHES?"cache ":"",
		     flags & HD_WRITE_MANY?"WriteMany ":"WriteOnce ",
		     flags & HD_CHANGE_SUPPORT?"ChangeSig ":"");
	}
#endif /* DEBUG */
	hd_softc.status = IDLE;

	hd_softc.max_retry = hdrequest.hr_max_retries;
	hd_softc.max_transfer = hdrequest.hr_max_transfer? 
			hdrequest.hr_max_transfer: 0xffff;
	hd_softc.byte_per_block = 512;	/* need to use define */

	/*
	 * reset the adapter
	 */

	hdrequest.r_unit = 0;
	hdrequest.r_function = ABIOS_RESET;
	hdrequest.r_return_code = ABIOS_UNDEFINED;
	hd_softc.status = PIO_TRANSFER;

	if (hdstart() == HDSTAGED) {
		while (hd_softc.status != IDLE);
	}

	if (hdrequest.r_return_code != 0)
	{
		DEBUGF(*dbugptr & DISKDEBUG,printf(
			"Drive not present, or failed! ax = %#x\n",
					          hdrequest.r_return_code));
	}
	hd_softc.status = IDLE;



}

/*
 * map PC REQ op codes to
 * ABIOS opcodes.
 */
u_short
hdop_map(op)
	u_short op;
{
	switch (op) {
	case DISKOP_READ:
		return(ABIOS_READ);
	case DISKOP_WRITE:
		return(ABIOS_WRITE);
	case DISKOP_VERIFY:
		return(HD_VERIFY);
	case DISKOP_RESET:
		return(ABIOS_RESET);
	default:
		return(HD_INV_FUNCTION);
	}
}

u_short
hderror_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( (error & 0xf0) | 0x8000);
	}
	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
 */
hdtimer()
{
	/*
	 * device has timed out
	 */
	if (hd_softc.hdtimeout) {
		if ((--hd_softc.hdtimeout) == 0) {
	 		DEBUGF(*dbugptr & PCINTDEBUG,rbprintf("hdtimeout\n"););
			hdint(HD_TIMEOUT);
		}
	}

	/*
	 * timer has completed
	 */
	if (hd_softc.hddelay) {
		if ((--hd_softc.hddelay) == 0) {
			hdint(0);
		}
	}
}

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

	/* no errors so far, start the transfer */
	abios_start(&hdrequest);
	s=spl();
	if (ABIOS_STAGED(hdrequest.r_return_code)) {
		/* stage on request time is in useconds */
		if (hdrequest.r_return_code & ABIOS_STAGE_ON_TIME) {
			hd_softc.hddelay = (u_short)(((u_long)
			    (hdrequest.hr_delay_time*TICKS)+9999999)/(10000000));
		} 

		/* timeout is in seconds * 8 */
		hd_softc.hdtimeout = ((hdrequest.r_time_out * TICKS) +79)/(80);

		/*
		 * make sure the delay function gets first "shot" at the
		 * transfer.
		 */
		if ((hd_softc.hdtimeout) && 
				(hd_softc.hddelay > hd_softc.hdtimeout)) {
			hd_softc.hdtimeout = (hd_softc.hddelay+TICKS/10);
		}
		splx(s);
		return(HDSTAGED);
	}
	splx(s);
	return(hderror_map(hdrequest.r_return_code));
}

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

	/*
	 * reset needs to load the badblock table first
	 */
	if ((rc >= 0) && (hddpl.op_code == DISKOP_RESET)) {
		u_short drive;
		drive = hdrequest.r_unit;
		hdrequest.r_function = ABIOS_READ;
		hdrequest.r_return_code = ABIOS_UNDEFINED;
		hdrequest.hr_block = ((u_long)rb_part[drive].pstart * 
				(hdparm[drive].maxhd+1)*hdparm[drive].maxsec)+8;
		hdrequest.hr_number_of_blocks = 1;
		hdrequest.hr_log_addr = buff_dio;
		hdrequest.hr_phys_addr = (char far *) physaddr((u_long)buff_dio,
					     (u_short far *)0,(u_short far *)0);
		hdrequest.hr_transfer_flags = HD_NO_CACHE;

		/* initialize the soft c structure */
		hd_softc.blocks_left = 1;
		hd_softc.retries = 0;
		hd_softc.status = GET_BAD_BLOCK;
		hddpl.op_code = 0xff;	/* make sure we only do this once */
	}

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

	rc = exchw(rc);

	hd_softc.status = IDLE;
	s = spl();
	hd_return_code[hdrequest.r_unit].errno = rc;
	rompint3(3,FAKEPOLL(HDIRQ), 0, 0, cur_cbcb);
	splx(s);
}
#else  /* !ABIOS */
hdint(lvl)
{
	rbprintf("Unexpected call to hdint (ABIOS not defined!)\n");
}
#endif /* !ABIOS */
