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

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

/*
 * Copyright University of Southern California, 1988
 */

#include <stdio.h>
#include <subcalls.h>
#include <doscalls.h>
#include <dos.h>
#include "pctype.h"
#include "pcparam.h"
#include "rb.h"
#include "dio.h"
#include "os2data.h"
#include "thread.h"

#ifdef PROFILING
#include "profile.h"
#endif


#define OS2_VERSION(major,minor) ((major) + ((minor)<<8))

static u_long	hd_io_unixaddr;  /* cbcb unix address for hard disk op */
u_int	hd_io_thread_id; /* id of the hard disk thread */
static u_long	fd_io_unixaddr;  /* cbcb unix address for floppy disk op */
u_int	fd_io_thread_id; /* id of the floppy disk thread */
extern u_long option_flag;
extern int os2_version;		/* version of OS/2 we're running on */

/*
 * Function to map OS/2 hd/fd Errors to BIOS errors.
 * We map read and write faults etc. to BIOS bad sector
 * errors. This is because OS/2 gives us a much larger error space - To
 * enforce bbt processing, this has to be done.
 */

int
os2bios_disk_error(rc)
int	rc;	/* OS/2 return val */

{	int bios_err;

	switch ( rc ) {
	case 0:
		return 0;

	case EINVALIDDRIVE:
	case EBADUNIT:
	case ENOTREADY:
			bios_err = BIOS_NOT_RDY; break;

	case EWRITEPROTECT: bios_err = BIOS_WRT_PROT; break;

	case EBADCOMMAND: bios_err = BIOS_BAD_CMD; break;

	case ECRC:  bios_err = BIOS_CRC_ERR; break;

	case ESEEK: bios_err = BIOS_SEEK_FAIL; break;

	case ENOTDOSDISK: bios_err = BIOS_INVAL_MED; break;

	case ESECTORNOTFOUND:
	case EWRITEFAULT: 
	case EREADFAULT:
	case EINVALIDDATA:
			bios_err = BIOS_BAD_SECTOR; break;

	case EWRONGDISK: bios_err = BIOS_DSKT_CHG; break;
	
	default:
		bios_err = BIOS_UNDEF_ERR; break;
	}

	/* OR with BIOS_ERROR to make it negative */
	return (bios_err | BIOS_ERROR);
}


/*
 ****************************************************************************
 * FLOPPY DISK STUFF
 * Floppy and Hard disk stuff are done separately since different ioctls
 * are used. In many cases, however, the code is similar. The UNIX interface
 * part is common.
 ****************************************************************************
 */

/* Category 8 IOCTL definitions */

#define C8_LOCK_DRIVE	0x00
#define C8_UNLOCK_DRIVE	0x01
#define C8_REDET_MEDIA	0x02
#define C8_SET_LOG_MAP	0x03
#define C8_BLOCK_REM	0x20
#define C8_GET_LOG_MAP	0x21
#define C8_SET_DEV_P	0x43
#define C8_WRITE_TRACK	0x44
#define C8_FORMAT_TRACK	0x45
#define C8_GET_DEV_P	0x63
#define C8_READ_TRACK	0x64
#define C8_VERIFY_TRACK	0x65

/* defines for the device type field */

#define C8DEVT_FD48TPI	0
#define C8DEVT_FD96TPI	1
#define C8DEVT_FD35LD	2
#define C8DEVT_FD8SD	3
#define C8DEVT_FD8DD	4
#define C8DEVT_HDISK	5
#define C8DEVT_TAPE	6
#define C8DEVT_OTHER	7

/* Note that these structures cannot be passed in an ioctl, as the compiler
 * will pad fields to maintain alignment.
 */

#define SIZEOF_EXT_BPB	31

struct ext_bpb {
	u_int	bytes_p_sec;
	char	sec_p_cluster;
	u_int	resv_sec;
	char	no_fats;
	u_int	rdir;
	u_int	tot_sec;
	char	media;
	u_int	sec_p_fat;
	u_int	sec_p_track;
	u_int	no_heads;
	u_long	hidden_secs;
	u_long	large_tot_secs;
	char	resv[6];
};

#define SIZEOF_C8_DEV_P	36

struct c8_dev_p {
	struct ext_bpb bpb;
	u_int	no_cyls;
	char	dev_type;
	u_int	dev_attr;
};

/* struct for read/write/verify parameters. The first field is kept as
 * a pad. The address of the second field must be used instead of the 
 * first.
 */

#define MAX_FD_SECTORS	24		/* arbitrary (>18) */

struct fd_track {
	u_int	sec_num;
	u_int	sec_size;
};

struct fd_rwv_params {
	char	pad;
	char	command;
	u_int	head;
	u_int	cyl;
	u_int	first_sec;	/* 0 based */
	u_int	no_sec;
struct fd_track track_layout[MAX_FD_SECTORS];
};

static struct fd_rwv_params fdp;	/* structure for floppy operations */
static	char fd_name[] = "a:";		/* only a: and b: are permitted */
static	struct drvparm fdparm[NFD];	/* floppy disk info */

/* function to return a handle to the floppy drive. A -1 indicates error */

static int
open_floppy_drive(drive)
int drive;	/* 0 - 1 */

{
	u_int	handle;
	u_int	action;
	u_long	size=0;
	u_int	attr=0;
	u_int	flag=0x01;
	u_int	mode= 0xe092;
	u_long	resv=0;
	int	rc;

	if ( (drive < 0) || (drive > 1) )
		return os2bios_disk_error(EINVALIDDRIVE);
	fd_name[0] = 'a' + (char) drive;
	if ( rc = DOSOPEN((char far *) fd_name, (unsigned far *)&handle,
			(unsigned far *)&action, size,attr,flag,mode,resv)){
#ifdef FD_DEBUG
		printf("DOSOPEN returned %d\n",rc);
#endif
		return os2bios_disk_error(rc);
	}
	return handle;
}

#define close_floppy_drive(fh)	DOSCLOSE(fh)

/* conversion routines */

/* character array to int (2 bytes) */

static u_int
ctoi(cp)
register char *cp;

{	register unsigned i=0;

	i = (u_int) *(cp+1);	/* higher byte */
	i <<= 8;
	i += (u_int) *cp;	/* lower byte */
	return i;
}

/* character array to long (4 bytes) */

static u_long
ctol(cp)
register char *cp;

{	register u_long l=0;
	l += (u_long) ctoi(cp+2);	/* higher word */
	l <<= 16;
	l += (u_long) ctoi(cp);		/* lower word */
	return l;
}

/* convert the ext bpb structure returned from an ioctl to the C accessible
 * structure.
 */

static void
buf_to_ext_bpb(bp,bpbp)
char	*bp;		/* buffer pointer */
register struct ext_bpb *bpbp;	/* structure to fill in */

{
	bpbp->bytes_p_sec = ctoi(&bp[0]);
	bpbp->sec_p_cluster = bp[2];
	bpbp->resv_sec = ctoi(&bp[3]);
	bpbp->no_fats = bp[5];
	bpbp->rdir = ctoi(&bp[6]);
	bpbp->tot_sec = ctoi(&bp[8]);
	bpbp->media = bp[10];
	bpbp->sec_p_fat = ctoi(&bp[11]);
	bpbp->sec_p_track = ctoi(&bp[13]);
	bpbp->no_heads = ctoi(&bp[15]);
	bpbp->hidden_secs = ctol(&bp[17]);
	bpbp->large_tot_secs = ctol(&bp[21]);
}

/* convert the device param structure from the result of an ioctl 63 */

static void
buf_to_dev_p(bp,dp)
char 	*bp;			/* character buffer */
register struct	c8_dev_p *dp;	/* structure to fill in */

{
	buf_to_ext_bpb(bp,&dp->bpb);
	dp->no_cyls = ctoi(&bp[SIZEOF_EXT_BPB+0]);
	dp->dev_type = bp[SIZEOF_EXT_BPB+2];
	dp->dev_attr = ctoi(&bp[SIZEOF_EXT_BPB+3]);
}

/* get a floppy device parameters */

static int
get_fd_params(fh,dp)
int	fh;		/* handle */
struct c8_dev_p *dp;	/* structure to return */

{	int rc;
	char data_buf[SIZEOF_C8_DEV_P];
	char	command = 1;	/* 0 = drive, 1 = disk */

try_again:
	if ( rc = DOSDEVIOCTL((char far *)data_buf,(char far *)&command,
		0x63,0x08,fh)) {
		if ( command == 1 ) {
			/* If we fail to read the parameters of the disk
			 * in the drive, then we'll settle for the default
			 * parameters of the drive.
			 */
#ifdef FD_DEBUG
			printf("get_fd_params:media parameters unreadable\n");
#endif
			command = 0;
			goto try_again;
		}
#ifdef FD_DEBUG
		printf("get_fd_params: IOCTL returned %d\n",rc);
#endif
		close_floppy_drive(fh);
		return -1;
	}
	buf_to_dev_p(data_buf,dp);
	return 1;
}

/* fills in the track layout table with consecutive sectors starting from
 * 0.
 */

static void
fill_fd_track_table(no_sec,sec_size,track_tab)
u_int	no_sec;
u_int	sec_size;
register struct fd_track *track_tab;

{	register int 	i;

	for ( i=0; i < no_sec; i++,track_tab++ ) {
		track_tab->sec_num = i+1;
		track_tab->sec_size = sec_size;
	}
}

/* initialize the internal data structures. */

int
fd_init()

{
	fill_fd_track_table(MAX_FD_SECTORS,512,fdp.track_layout);
	return 0;
}

static int
getfdgeometry(drive, fdstat)
u_int	drive;
u_int	*fdstat;

{	u_int	fh;
	u_int	i;
	struct c8_dev_p fdev_p;
	struct drvparm *fdparmp;	/* pointer to parameter structure */

	if ( (fh = open_floppy_drive(drive)) < 0 )
		return -1;
	if ( get_fd_params(fh,&fdev_p) < 0 ){
		close_floppy_drive(fh);
		return -1;
	}

	fdparmp = &fdparm[drive];
	fdparmp->maxcyl = fdev_p.no_cyls;
	fdparmp->maxhd = fdev_p.bpb.no_heads;
	fdparmp->maxsec = fdev_p.bpb.sec_p_track;

	/* determine the media */
	switch ( fdev_p.dev_type ) {
	case C8DEVT_FD48TPI:
		i = FD5LO;	/* 5in low density */
		break;
	case C8DEVT_FD96TPI:
		i = FD5HI;	/* 5in high density */
		break;
	case C8DEVT_FD35LD:
		i = FD3LO;	/* 3.5 in 720Kb */
		break;
	case C8DEVT_OTHER:
				/* OS/2 does not have a device type for
				 * the 3.5 2Mb (135TPI) disk. It also confuses
				 * the 3.5 1Mb disk. We are forced to use
				 * the physical properties of the disk to
				 * figure out the type. The media byte is
				 * undecipherable (at least to me!).
				 */

		if ( fdparmp->maxsec == 9 )
			i = FD3LO;	/* 3.5 in 1Mb */
		else	/* has to be 18 */
			i = FD3HI;	/* 3.5 in 2Mb */
		break;
	default:
		close_floppy_drive(fh);
		return -1;	/* some other stuff */
	}
	if ( fdstat != (u_int *) 0 )
		*fdstat |= i;
	
#ifdef FD_DEBUG
	printf("getfdgeometry: h %d c %d s %d type: %d\n",
		fdparmp->maxhd,fdparmp->maxcyl,fdparmp->maxsec,i);
#endif

	/* test if drive is write protected:
	 * Read and write back first track
	 */

	if ( fd_op(C8_READ_TRACK, fh, (char far *)fdbuf, 0, 0, 0,
			fdparmp->maxsec) < 0 ) {
		close_floppy_drive(fh);
		return -1;	/* read error */
	}
	if ( fd_op(C8_WRITE_TRACK, fh, (char far *)fdbuf, 0, 0, 0,
			fdparmp->maxsec) < 0 ){
		/* write protected */
#ifdef FD_DEBUG
		printf("getfdgeometry: drive write protected\n");
#endif
		if ( fdstat != (u_int *) 0 )
			*fdstat |= FDWP;
	}
	close_floppy_drive(fh);
	return 1;
}

static int
fd_op(op, fh, buf, head, cyl, fsec, no_sec)
u_int	op;
u_int	fh;
char	far *buf;
u_int	head,cyl,fsec,no_sec;

{	int 	rc;

	fdp.command = 1;	/* consecutive sectors only */
	fdp.head = head;
	fdp.cyl = cyl;
	fdp.first_sec = fsec;
	fdp.no_sec = no_sec;
#ifdef FD_DEBUG
	printf("fd_op: 0x%x: (h %d,c %d,s %d,\# %d)\n",op,head,cyl,fsec,
						no_sec);
#endif
	if ( rc = DOSDEVIOCTL(buf,(char far *)&fdp.command, op, 0x08, fh)) {
#ifdef FD_DEBUG
		printf("fd_op: op 0x%x returned %d\n", op, rc);
#endif
		close_floppy_drive(fh);
		return os2bios_disk_error(rc);
	}
	return 1;
}


/*
 * Floppy disk io handler
 */


int
fdio(op,drive,head,track,sector,num,buff)
int             op;
int             drive;
int             head;
int             track;
int             sector;
int             num;
char far        *buff;	/* Data buffer for operation */

{	int	func;
	int	j;
	u_int	fd_handle;
	u_int	maxcyl, maxhd, maxsec;
	int	rc;

	switch	(op) {

	case	DISKOP_READ:
			func = C8_READ_TRACK;
/*	 printf("fd read sector:drive=%x head=%d track=%d sec=%d num=%d\n",
		drive,head,track,sector,num); */
			break;
	case	DISKOP_WRITE:
			func = C8_WRITE_TRACK;
/*	 printf("fd write sector:drive=%x head=%d track=%d sec=%d num=%d\n",
		drive,head,track,sector,num); */
			break;
	case	DISKOP_FORMAT:
			func = C8_FORMAT_TRACK;
			printf("fdio: format request not supported\n");
			return -1;
	case	DISKOP_VERIFY:
/* printf("fd verify sector:drive=%x head=%d track=%d sec=%d num=%d\n",
		drive,head,track,sector,num); */
			func = C8_VERIFY_TRACK;
			break;
	default:
		printf("fdio: non-supported disk operation %d\n",op);
		return os2bios_disk_error(EBADCOMMAND);
	}

	/* initialize local variables */
	if ( (fd_handle = open_floppy_drive(drive)) < 0 )
		return fd_handle;

	maxhd  = fdparm[drive].maxhd;
	maxcyl = fdparm[drive].maxcyl;
	maxsec = fdparm[drive].maxsec;

	while(num > 0) {
		/* check to see if cross the track boundary */
		j = ((num + sector - 1) > maxsec) ? maxsec - sector + 1 : num;
		if ( (rc = fd_op(func, fd_handle, (char far *)buff,head,track,
			sector-1, j)) < 0 ) {
			close_floppy_drive(fd_handle);
			return rc;
		}
		/* adjust parameters for another read if any */
		sector = 1 ;	/* sector will now start from the beginning */
		/* increment the head and check. head starts from 0 */
		if( ++head >= maxhd) {
			/* increment the track and check. cyl starts from 0 */
			if( ++track >= maxcyl ) {
				printf("fdio: over the cyl limit\n");
				close_floppy_drive(fd_handle);
				return (BIOS_BAD_TRACK|BIOS_ERROR);
			} 
			head = 0; /* go to the top */
		};
		num -= j;
		buff += j * 512;
	}
	close_floppy_drive(fd_handle);
	return 0;
}


static int
fd_io(op, drive, head, track, sector, num, buff)
int             op;
int             drive;
int             head;
int             track;
int             sector;
int             num;
char far       *buff;	/* Data buffer for operation */
{
	int    rc;

	/* first check if we have allocated buffer space.
	 * if not, then return an error
	 */
	if ( fdbuf == 0 )
		return os2bios_disk_error(ENOTREADY);

	/*
	 * Send the request off to OS/2.
	 * Under OS/2 there is no need to reset or retry. The driver should
	 * do that.
	 */

	rc = fdio(op,drive,head,track,sector,num,buff);

	return rc;
}

/*
 ****************************************************************************
 * HARD DISK STUFF
 ****************************************************************************
 */

/* function codes for category 09 DEVIOCTL */

#define LOCK	0x00
#define UNLOCK	0x01
#define WTRACK	0x44
#define GPARAM	0x63
#define RTRACK	0x64
#define VTRACK	0x65

#define MAXSEC	50		/* max nspc supported is 50 */

struct	sectab {
	int	sectnum;
	int	sectsize;
};

struct	hdparam {
	char	fill;	    /* pad to avoid alignment by compiler */
	char	cominfo;
	int	head;
	int	cyl;
	int	ssector;
	int	nsector;
	struct	sectab	sectab[MAXSEC];
};

extern	struct	drvparm	hdparm[];	/* defined in wconfig.c */

/* The following table keeps data for each hard disk in the system. */

static struct	hard_disk {
	int	valid;			/* valid or not */
	u_int	handle;			/* handle to access the disk */
	struct	hdparam	hd_param;	/* parameters to give OS/2 */
} hard_disk[NHD];

static char	diskname[] = "1:";   /* drive starts from 1, not 0. */


/* 
 * get_hd_handle returns a handle for a physical hard disk.
 * NOTE: the drive number is an integer ranging from 0 to NHD-1
 */

static int
get_hd_handle(drive)
u_int	drive;
{
    int	 	func,parmlen;
    int 	ecode;
    u_int	hd_handle;

    diskname[0] = (u_char)drive + '1';

    func = 2;
    parmlen = strlen(diskname) + 1;	  /* must count null byte */
    ecode=DOSPHYSICALDISK(func,(char far *)&hd_handle,2,(char far *)diskname,
					      parmlen);
    if(ecode != 0) { 
    		printf("get_hd_handle: drive %d open error code = %d\n",ecode,
			drive);
		return -1;
    }
    return hd_handle;
}

/*
 * This function releases the handle to a hard disk.
 */

static void
release_hd_handle(drive)
int	drive;

{	int	func;
	u_int	hd_handle;

	if ( hard_disk[drive].valid == 0 ) return;
	hd_handle = hard_disk[drive].handle;
    	func = 3;
	DOSPHYSICALDISK(func,0,0,(char far *)&hd_handle, 2);
	hard_disk[drive].valid = 0;
}


/* This function gets the geometry of the hard disk */

static int
getdiskgeometry(hd_handle,dparm)
u_int	hd_handle;
struct	drvparm	*dparm;
{
    int 	ecode;
    char	gparam;
    int 	gdata[8];

    /* get hard disk parameter */
    gparam = 0x00;
    ecode = DOSDEVIOCTL((char far *)gdata,(char far *)&gparam,GPARAM,
    					0x09,hd_handle);
    if(ecode != 0) { 
    		printf("getdgeo: error code = %d\n",ecode);
		return -1;
    }

    dparm->maxcyl = gdata[1];
    dparm->maxhd = gdata[2];
    dparm->maxsec = gdata[3];

    return 0;
}


/*
 * hd_init initializes all the hard disk data.
 */

int
hd_init()

{	int	i,k;
	struct hard_disk *hd;
	int	rc;
	int	no_sec;
	struct sectab *sec_tabp;

	for ( i = 0; i < NHD ; i++ ) {
		hd = &hard_disk[i];
		if (option_flag & OPTION_ONE_DISK) {
			if (i == cpu_number) {
				if ( (rc = get_hd_handle(cpu_number)) < 0 ){
					hd->valid = 0;
					continue;
				}
			} else {
				hd->valid = 0;
				continue;
			}
		} else {
			if ( (rc = get_hd_handle(i)) < 0 ){
				hd->valid = 0;
				continue;
			}
		}
		hd->handle = (u_int) rc;
		hd->valid = 1;
		if ( getdiskgeometry(hd->handle,&hdparm[i]) < 0 ){
			release_hd_handle(i);
			continue;
		}
		sec_tabp = &(hard_disk[i].hd_param.sectab[0]);
		no_sec = (hdparm[i].maxsec < MAXSEC)?hdparm[i].maxsec : MAXSEC;
		for ( k=0; k < no_sec; sec_tabp++ ){
			sec_tabp->sectsize = SECSIZE;
			sec_tabp->sectnum  = ++k;
		}
	
	}
}

/*
 * This function releases all the handles to the hard disks.
 */

int
close_hard_disks()

{	int	i;

	for ( i=0; i < NHD; i++ )
		release_hd_handle(i);
}

	
/*
 * This function performs all the operations on hard disks.
 * The return code is 0 (no error) or (the neg of) an OS/2 error.
 */

int
hdio(op,drive,head,track,sector,num,buff)
int             op;
int             drive;
int             head;
int             track;
int             sector;
int             num;
char far        *buff;	/* Data buffer for operation */

{	int	func;
	int	ecode,j;
	register struct hdparam *hdparam;
	u_int	hd_handle;
	u_int	maxcyl, maxhd, maxsec;

	drive -= 0x80; /* hard disk drives start at 0x80 */

	if ( (drive < 0 ) || (hard_disk[drive].valid == 0) ) {
		printf("hdio: invalid drive %d\n", drive);
		return os2bios_disk_error(EINVALIDDRIVE);
	}

	switch	(op) {

	case	DISKOP_READ:
			func = RTRACK;
/*	 printf("read sector: drive=%x head=%d track=%d sector=%d num=%d\n",
		drive,head,track,sector,num); */
			break;
	case	DISKOP_WRITE:
			func = WTRACK;
/*	 printf("write sector: drive=%x head=%d track=%d sector=%d num=%d\n",
		drive,head,track,sector,num); */
			break;
	case	DISKOP_VERIFY:
	 printf("verify sector: drive=%x head=%d track=%d sector=%d num=%d\n",
		drive,head,track,sector,num);
			func = VTRACK;
			break;
	default:
		printf("hdio: non-supported disk operation %d\n",op);
		return os2bios_disk_error(EBADCOMMAND);

	}

	/* initialize pointers and local variables */

	hdparam = &(hard_disk[drive].hd_param);
	hd_handle = hard_disk[drive].handle;
	maxcyl = hdparm[drive].maxcyl;
	maxhd  = hdparm[drive].maxhd;
	maxsec = hdparm[drive].maxsec;

	/* common operation for all the calls */

	/* must be 0 for OS/2 V 1.0 as 1 does not work */
	hdparam->cominfo = (os2_version == OS2_VERSION(1,0)) ? 0 : 1;

	while(num > 0) {
		/* check to see if cross the track boundary */
		j = ((num + sector - 1) > maxsec) ? maxsec - sector + 1 : num;
		hdparam->cyl = track;
		hdparam->head = head;
		hdparam->nsector = j;
		hdparam->ssector = sector - 1;
		ecode = DOSDEVIOCTL((char far *)buff,
		       (char far *)&(hdparam->cominfo),func,0x09,hd_handle);
		if(ecode != 0) { 
#ifdef HD_DEBUG
    			printf("hdio: error code = %d on op %d\n",ecode,func);
#endif
			return os2bios_disk_error(ecode);
		}
		/* adjust parameters for another read if any */
		sector = 1 ;	/* sector will now start from the beginning */
		/* increment the head and check. head starts from 0 */
		if( ++head >= maxhd) {
			/* increment the track and check. cyl starts from 0 */
			if( ++track >= maxcyl ) {
#ifdef HD_DEBUG
				printf("hdio: over the cyl limit\n");
#endif
				return (BIOS_BAD_TRACK|BIOS_ERROR);
			} 
			head = 0; /* go to the top */
		};
		num -= j;
		buff += j * 512;
	}
	return 0;
}

/*
 * hd_io() - Send request off to OS/2, take care of retries if error
 * 	   - Hard disks only.
 */

static int
hd_io(op, drive, head, track, sector, num, buff)
int            op;
int            drive;
int            head;
int            track;
int            sector;
int            num;
char far       *buff;	/* Data buffer for operation */
{
	int	rc;


	/*
	 * Send the request off to OS/2.
	 * In OS/2 there is no need to reset the drive or retry - the disk
	 * driver should take care of that.
	 */

	rc = hdio(op,drive,head,track,sector,num,buff);
		
	if (rc < 0) {
		if ((rc & 0xff) == BIOS_BAD_SECTOR || (op == DISKOP_VERIFY)){
			if ( (op == DISKOP_READ) || (op == DISKOP_WRITE) 
					|| (op == DISKOP_VERIFY) )
				/* try processing the bad block */
				rc = procbbt(op,drive,head,track,sector,
								num,buff,rc);
		}
	}
	return rc;
}


/*
 * This function is provided for bad block processing. It does simple
 * read, write or verify operations.
 */

int
bbio(op, drive, head, track, sector, num, buff)
int             op;
int             drive;
int             head;
int             track;
int             sector;
int             num;
char far       *buff;	/* Data buffer for operation */
{
	int     rc, func;
	register struct hdparam *hdparam;
	u_int	hd_handle;

#ifdef BBT_DEBUG
	printf("bbio:op=0x%x d=0x%x h=0x%x t=0x%x s=0x%x num=0x%x buff=0x%lx\n",
		   op, drive, head, track, sector, num, (char far *) buff);
#endif

	drive -= 0x80; /* hard disk drives start at 0x80 */

	if ( (drive < 0 ) || (hard_disk[drive].valid == 0) ) {
#ifdef BBT_DEBUG
		printf("bbio: invalid drive %d\n", drive);
#endif
		return os2bios_disk_error(EINVALIDDRIVE);
	}

	switch	(op) {

	case	DISKOP_READ:
			func = RTRACK;
			break;
	case	DISKOP_WRITE:
			func = WTRACK;
			break;
	case	DISKOP_VERIFY:
			func = VTRACK;
			break;
	default:
		printf("bbio: non-supported disk operation %d\n",op);
		return os2bios_disk_error(EBADCOMMAND);

	}
	/* initialize pointers and local variables */

	hdparam = &(hard_disk[drive].hd_param);
	hd_handle = hard_disk[drive].handle;

	/* must be 0 ????? 1 does not work */
	hdparam->cominfo = 0x0;

	hdparam->cyl = track;
	hdparam->head = head;
	hdparam->nsector = num;
	hdparam->ssector = sector - 1;
	rc = DOSDEVIOCTL((char far *)buff,
		       (char far *)&(hdparam->cominfo),func,0x09,hd_handle);

	return os2bios_disk_error(rc);
}

/*
 ************************************************************************
 * THE FOLLOWING OPERATIONS ARE AT A HIGHER LEVEL, DEALING WITH UNIX	*
 * PARAMETERS.								*
 ************************************************************************
 */

/* 
 * This routine invokes the floppy or hard disk handler directly.
 */

static int
diskio(dpl,cptr,hd_fd)
pcdpladdr_t     dpl;
char far *cptr;
int	hd_fd;
{
	register int    num, sec;
	u_short         drive, hd, trk, rc, op;

	hd = dpl->head;
	trk = dpl->cyl;
	sec = dpl->sector;
	num = dpl->number;
	op = dpl->op_code;
	drive = dpl->drive;

	if (hd_fd) {
		/* Hard disk */
		rc = hd_io(op, drive, hd, trk, sec, num, cptr);
	}
	else {
		/* floppy disk */
		rc = fd_io(op, drive, hd, trk, sec, num, cptr);
	}
	return (rc);
}

/*
 *  Do the disk read, then copy the data back to Unix
 */

static int
readop(dpl,hd_fd)
pcdpladdr_t     dpl;
int	hd_fd;
{
	register u_short i = 0, j;
	int             rc;
	u_long          unixaddr;
	char far       *pcaddr;
#ifdef PROFILING
	u_long	r_last_time=0;
#endif

	pcaddr = (hd_fd? buff_dio : fdbuf);

	rc = diskio(dpl,pcaddr,hd_fd);
	if (rc > -1)
	{

		while ((j = dpl->atr_cnt[i]) > 0)
		{
#ifdef PROFILING
			r_last_time = elapsed_time();
#endif
			unixaddr = dpl->atr_addr[i];
			rc = moveout(unixaddr, pcaddr, j);
#ifdef PROFILING
			pt_dmo_cnt += j;
			pt_dmo_time += elapsed_time() - r_last_time;
#endif
			if (rc < 0)
				break;
			pcaddr += j;
			i++;

		}
	}
	return (rc);
}

/*
 * writeop()  - Copy the data down from Unix, then do the disk op
 *
 */

static int
writeop(dpl,hd_fd)
pcdpladdr_t     dpl;
int	hd_fd;
{
	register u_short i = 0, j;
	int             rc;
	u_long          unixaddr;
	char far       *pcaddr;
#ifdef PROFILING
	u_long w_last_time = 0;
#endif

	pcaddr = (hd_fd? buff_dio : fdbuf);


	while ((j = dpl->atr_cnt[i]) > 0)
	{
#ifdef PROFILING
		w_last_time = elapsed_time();
#endif
		unixaddr = dpl->atr_addr[i];
		rc = movein(unixaddr, pcaddr, j);
#ifdef PROFILING
		pt_dmi_cnt += j;
		pt_dmi_time += elapsed_time() - w_last_time;
#endif
		if (rc < 0) {
			return (rc);
		}
		pcaddr += j;
		i++;
	}
	rc = diskio(dpl,(hd_fd ? buff_dio: fdbuf),hd_fd);
	return (rc);
}

/*
 *  Do a disk read-verify
 */

static int
verifyop(dpl,hd_fd)
pcdpladdr_t     dpl;
int	hd_fd;
{
	int             rc;
	char far       *pcaddr;

	pcaddr = (hd_fd? buff_dio : fdbuf);

	rc = diskio(dpl,pcaddr,hd_fd);

	return (rc);
}




/*************************************************************
 * diskop(dplqe) - Do a disk operation
 *
 *  This routine is called from the main program to do a disk (floppy
 *  or fixed) operation. The routine starts off by copying the
 *  disk paramater list (pcdpl) down from Unix. Then it takes care
 *  of any byte swapping that needs to be done on the parameters.
 *  Once all of the parameters are in PC memory we call the appropiate
 *  routine to handle the specific disk operation.
 *
 */

void 
diskop(unixaddr)
u_long	unixaddr;
{
	pcdpl_t         dpl;
	register pcdpladdr_t dplptr = &dpl;
	register int    i;
	int             rc, drive;
	char far       *pcaddr;
	u_short         fdstat=0;
	int		hd_fd = 0;
#ifdef PROFILING
	u_long	d_last_time;
#endif

	/*
	 * First read the DPL down from Unix 
	 */

	pcaddr = (char far *) dplptr;

	rc = movein(unixaddr, pcaddr, sizeof(pcdpl_t));

	if (rc < 0)
	{
#if DISK_DEBUG
		printf("pc code: Can't read DPL\n");
#endif
		goto dio_exit;
	}
	/*
	 * Change byte order 
	 */

	dplptr->op_code = exchw(dplptr->op_code);
	dplptr->drive = exchw(dplptr->drive) & 0x00ff;
	dplptr->head = exchw(dplptr->head);
	dplptr->cyl = exchw(dplptr->cyl);
	dplptr->sector = exchw(dplptr->sector);
	dplptr->number = exchw(dplptr->number);

	i = 0;
	while ((dplptr->atr_cnt[i] = exchw(dplptr->atr_cnt[i])) > 0)
	{
		dplptr->atr_addr[i] = exchl(dplptr->atr_addr[i]);
#ifdef DISK_DEBUG
		printf("diskop: %d: addr %#lx cnt %#x\n",
			       i, dplptr->atr_addr[i], dplptr->atr_cnt[i]);
#endif
		if (++i >= 32)
			break;
	}

	drive = dplptr->drive;
	hd_fd = ( ISFLOPPY(drive) ? 0 : 1);

	switch (dplptr->op_code)
	{
	case DISKOP_RESET:
/*		printf("disk reset\n"); */
		rc = 0;
		if (drive >= 0x80)
			bbtinit(drive);
		break;
	case DISKOP_STATUS:
/*		printf("disk status\n"); */
		/* This should be called only for floppy disks */
		rc = 0;
		if ( hd_fd == 0 ) {
/*			printf("DISKOP_STATUS request for floppy\n"); */
			rc = getfdgeometry(drive,&fdstat);
			if ( rc > 0 )
				rc = fdstat;
		}
		break;

	case DISKOP_READ:
/*		printf("reading hard disk\n"); */
#ifdef PROFILING
		d_last_time = elapsed_time();
#endif
		rc = readop(dplptr,hd_fd);
#ifdef PROFILING
		if ( hd_fd ){
			pt_hdr_cnt++;
			pt_hdr_time += elapsed_time() - d_last_time;
			pt_hdrs_cnt += dplptr->number;
		}
		else {
			pt_fdr_cnt++;
			pt_fdr_time += elapsed_time() - d_last_time;
			pt_fdrs_cnt += dplptr->number;
		}
#endif
		break;

	case DISKOP_WRITE:
/*		printf("writing hard disk\n"); */
#ifdef PROFILING
		d_last_time = elapsed_time();
#endif
		rc = writeop(dplptr,hd_fd);
#ifdef PROFILING
		if ( hd_fd ){
			pt_hdw_cnt++;
			pt_hdw_time += elapsed_time() - d_last_time;
			pt_hdws_cnt += dplptr->number;
		}
		else {
			pt_fdw_cnt++;
			pt_fdw_time += elapsed_time() - d_last_time;
			pt_fdws_cnt += dplptr->number;
		}
#endif
		break;

	case DISKOP_VERIFY:
/*		printf("disk verify\n"); */
		rc = verifyop(dplptr,hd_fd);
		break;

	case DISKOP_SMT:
/*		printf("disk smt\n"); */
		/* set media type - we can afford to ignore this one.
		 * OS/2 presumably is aware of the media types!
		 */
		rc = 0;
		break;

	case DISKOP_FORMAT:
/*		printf("disk format\n"); */
		rc = 0;
		break;

	default:
		printf("unsupported %s disk op (%d)\n",
			 (hd_fd? "hard":"floppy"),dplptr->op_code);
		break;
	}


dio_exit:

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

	rc = exchw(rc);

	if (ISFLOPPY(drive))
	{
		fd_return_code[drive].errno = rc;
		rompint3(FAKEPOLL(FDIRQ));  
	} else
	{
		drive -= 0x80;
		hd_return_code[drive].errno = rc;
		rompint3(FAKEPOLL(HDIRQ));

	}
}


/* function to signal the hard disk io thread */

int
initiate_hard_disk_op(unixaddr)
u_long unixaddr;

{
	hd_io_unixaddr = unixaddr;
	DOSRESUMETHREAD(hd_io_thread_id);
}
/* function to signal the floppy disk io thread */

int
initiate_floppy_disk_op(unixaddr)
u_long unixaddr;

{
	fd_io_unixaddr = unixaddr;
	DOSRESUMETHREAD(fd_io_thread_id);
}


/* hard disk io thread */

static void far
hd_io_thread(void)

{
#ifdef PROFILING
	u_long	hit_last_time;
#endif
	/* increase thread priority */
	SET_THREAD_PRIORITY(PRI_HD_THREAD);
	while ( 1 ) {
		DOSSUSPENDTHREAD(hd_io_thread_id);	/* suspend self */
#ifdef PROFILING
		pt_hd_cnt++;
		hit_last_time = elapsed_time();
#endif
		diskop(hd_io_unixaddr);	/* do the operation */
#ifdef PROFILING
		pt_hd_time += elapsed_time() - hit_last_time;
#endif
	}
}


/* hard disk io thread */

static void far
fd_io_thread(void)

{
#ifdef PROFILING
	u_long	fit_last_time;
#endif
	/* increase thread priority */
	SET_THREAD_PRIORITY(PRI_FD_THREAD);
	while ( 1 ) {
		DOSSUSPENDTHREAD(fd_io_thread_id);	/* suspend self */
#ifdef PROFILING
		pt_fd_cnt++;
		fit_last_time = elapsed_time();
#endif
		diskop(fd_io_unixaddr);			/* do the operation */
#ifdef PROFILING
		pt_fd_time += elapsed_time() - fit_last_time;
#endif
	}
}

/* This function sets up the disk io threads */

int
spawn_dio_thread()

{	char	*stack;
	char	far *thstk;
	int	rc;
	char	*malloc();

	if ( (stack = malloc(1024)) == 0 ){
		printf("No memory for hard disk io thread\n");
		return -1;
	}
	stack += 1024;
	thstk = (char far *)stack;
	if ( rc = DOSCREATETHREAD(hd_io_thread,(u_int far *)&hd_io_thread_id,
	   thstk) ){
		printf("Unable to create hard disk io thread: %d\n",rc);
		return -1;
	}
	DOSSLEEP(100L);	/* wait for the thread */

	/* now spawn the floppy disk thread - We are not too concerned 
	 * about errors.
	 */

	if ( (stack = malloc(1024)) == 0 ){
		printf("No memory for floppy disk io thread\n");
		return 0;
	}
	stack += 1024;
	thstk = (char far *)stack;
	if ( rc = DOSCREATETHREAD(fd_io_thread,(u_int far *)&fd_io_thread_id,
	   thstk) ){
		printf("Unable to create floppy disk io thread: %d\n",rc);
		return 0;
	}
	DOSSLEEP(100L);	/* wait for the thread */

	return 0;
}
