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

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

#include        "../h/param.h"
#include        "../h/inode.h"
#include        "../h/fs.h"
#include        "../h/time.h"
#include        "../h/proc.h"
#include 	"../ca_atr/pcif.h"
#include	"../pc_code/dio.h"

#include 	"saio.h"
#include	"sa.h"
#include	"fd.h"

u_short fdtype;			
long	fd_pc_cb;

fdopen(io)
	register struct iob *io;
{
	register struct fdst *st;
	register int boff = io->i_boff;
	register int unit = io->i_unit;
	struct pc_stat *fd_stat;
	register int old_window;
	int result = -1;

	old_window = get_512_window(); 		/* Save the current window */

	if( unit > 1 || boff < 0 || boff > 7 ) {
	  printf("FD%d: BAD DRIVE NUMBER OR INVALID PARTITION\n",unit);
       	  goto done;
	  }


	fd_pc_cb = get_pc_cb(FDENT);
	fd_stat = (struct pc_stat *)(set_512_window(fd_pc_cb)+pcif_512_fw);
	fd_stat += unit;

	fd_dpl[unit].op_code = DISKOP_STATUS;	/* initialized yet, then */
	fd_dpl[unit].drive = unit;	        /* send a reset to the drive */

	fd_stat->return_code = 0;
	if(pc_req(CB_FDREQ,&fd_dpl[unit],FDENT) < 0) 
	  goto done;				/* request, return an error */

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

	if(fd_stat->return_code & BIOS_ERROR) {
	    printf("FD%d: DOOR OPEN OR HARDWARE FAULT\n",unit);
	    printf("FD%d: BIOS ERROR CODE = %x\n",unit,(char)(fd_stat->return_code & 0x00ff));
	    goto done;		 /* return error if no disk */
	  } 

	if((fd_stat->return_code & FDWP) && (io->i_flgs & F_WRITE)) {
	    printf("FD%d: WRITE PROTECTED\n",unit);
	    goto done;		
	  } 

	fdtype = fd_stat->return_code & 0x00ff;

	st = &fdst[fdtype];
	io->i_boff = st->off[boff].start * (st->nspc);

	result = 0;

done:
        set_512_window(old_window);	
	return(result);

}

fdclose(io)
	register struct iob *io;
{
	return(0);
}


fdioctl(fdesc, cmd, arg)
	int fdesc, cmd;
	char *arg;
{

	switch (cmd) {

	       case SAIODEVDATA:       /* Return Drive description table. */
		        {
			struct st *st = (struct st *)arg;
			
			st->nsect = (short)fdst[fdtype].nsect;
			st->ntrak = (short)fdst[fdtype].ntrak;
			st->nspc = (short)fdst[fdtype].nspc;
			st->ncyl = (short)fdst[fdtype].ncyl;
			st->steprate = 0;
			st->precompcyl = 0;
			st->off = (struct partab *)fdst[fdtype].off;
		        }
			return(0);
        	default:
		  	return (ENXIO);
	}

}


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


fdstrategy(io)
	register struct iob *io;
{
	register struct fdst *st = &fdst[fdtype];
	register int unit = io->i_unit;
	register short sector,cylinder,head;
	register int maxcount,xfercount,blockno;
	register char *memaddr;
	struct pc_stat *fd_stat;
	register int old_window;
	int result = -1;

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

	xfercount = io->i_cc;			/* save the count requested */
	memaddr = io->i_ma;			/* save the memory address */
	blockno = io->i_bn;

	while(xfercount) {
	     sector = blockno;			/* calc the track, head and */	
	     cylinder = sector / (unsigned)(st->nspc); /* sector from blockno */
	     sector %= st->nspc;
	     head = (sector / st->nsect) & 1;
	     sector %= (st->nsect);
	
	     if((cylinder >= st->ncyl) || (sector > st->nspc)) {
	       goto done;   				/* return error */
	       }

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

	     fd_dpl[unit].drive = (u_short)unit;
	     fd_dpl[unit].sector = (short)sector + 1;
	     fd_dpl[unit].cyl = (short)cylinder;
	     fd_dpl[unit].head = (short)head;
	     fd_dpl[unit].number = (short)((maxcount + BSIZE - 1)/ BSIZE);

	     if (io->i_flgs & F_RDDATA)          	/* READ */
		fd_dpl[unit].op_code = DISKOP_READ;
	     else 			  	 	/* WRITE */
		fd_dpl[unit].op_code = DISKOP_WRITE; 

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

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

	     fd_stat->return_code = 0;

	     if(pc_req(CB_FDREQ,&fd_dpl[unit],FDENT) < 0) 
	       goto done;				/* error from BIOS */
	       

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

	     if(fd_stat->return_code & BIOS_ERROR) {
	       printf("FD%d: BIOS ERROR CODE = %x\n",unit,(char)(fd_stat->return_code & 0x00ff));
	       goto done;		 /* return error if no disk */
	       } 

	     memaddr += maxcount;
	     blockno += fd_dpl[unit].number;
             xfercount -= maxcount;

	}  /* end of while */

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


