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

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

#define MAXTRANS (32 * 1024)	/* 32K max tranfser	*/
#define	BSIZE	DEV_BSIZE

#define NOP	32
#define NOPC	2

struct pc_dpl op_dpl[NOP];

u_short optype;			
long	op_pc_cb;
#define UNIT_SHIFT	5
#define BOFF_SHIFT	28		/* for encloding partition in block number */

opopen(io)
	register struct iob *io;
{
	register struct opst *st;
	register int boff = io->i_boff;
	register int unit = io->i_unit;
	struct pc_stat *op_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("OP%d: BAD DRIVE NUMBER OR INVALID PARTITION\n",unit);
       	  goto done;
	  }


	unit = (unit << UNIT_SHIFT) + io->i_boff;
	op_pc_cb = get_pc_cb(OPENT);
	op_stat = (struct pc_stat *)(set_512_window(op_pc_cb)+pcif_512_fw);
	op_stat += unit;

	op_dpl[unit].op_code = DISKOP_OPEN;	/* initialized yet, then */
	op_dpl[unit].drive = unit;	        /* send a reset to the drive */
	op_dpl[unit].number = (io->i_flgs&F_WRITE) != 0; /* flag if writing */

	op_stat->return_code = 0;
	if(pc_req(CB_OPREQ,&op_dpl[unit],OPENT) < 0) 
	  goto done;				/* request, return an error */

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

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


	io->i_boff = io->i_boff << BOFF_SHIFT;		/* remember boff for later */

	result = 0;

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

}

opclose(io)
	register struct iob *io;
{
	int unit = (io->i_unit << UNIT_SHIFT) + (io->i_boff >> BOFF_SHIFT);
	int old_window = get_512_window(); 		/* Save the current window */
	int result = -1;
	struct pc_stat *op_stat;

	op_stat = (struct pc_stat *)(set_512_window(op_pc_cb)+pcif_512_fw);
	op_stat += unit;

	op_dpl[unit].op_code = DISKOP_CLOSE;	
	op_dpl[unit].drive = unit;

	op_stat->return_code = 0;
	if(pc_req(CB_OPREQ,&op_dpl[unit],OPENT) < 0) 
	  goto done;				/* request, return an error */

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

	result = 0;

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


opioctl(opesc, cmd, arg)
	int opesc, cmd;
	char *arg;
{

	switch (cmd) {

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

}


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


opstrategy(io)
	register struct iob *io;
{
	int unit = (io->i_unit << UNIT_SHIFT) + (io->i_boff >> BOFF_SHIFT);
	register short sector,cylinder,head;
	register int maxcount,xfercount,blockno;
	register char *memaddr;
	struct pc_stat *op_stat;
	register int old_window;
	int result = -1;

	old_window = get_512_window(); 		/* Save the current window */
	op_stat = (struct pc_stat *)(set_512_window(op_pc_cb)+pcif_512_fw);
	op_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 = blockno;
	     head = blockno >> 16;
	
	     if(xfercount > MAXTRANS)	/* 32K maximum transfer */
	       maxcount = MAXTRANS;
	     else
	       maxcount = xfercount; 

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

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

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

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

	     op_stat->return_code = 0;

	     if(pc_req(CB_OPREQ,&op_dpl[unit],OPENT) < 0) 
	       goto done;				/* error from BIOS */
	       

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

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

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

	}  /* end of while */

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


