/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
#ifndef lint
static char    *rcsid = "$Header:ub.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 "bios.h"
#include "trace.h"
#include "vars.h"

/*
 * ubop - finds out what command we are doing and for what
 * unit.
 */

UB_SOFTC        ub_softc[MAX_UBS];

u_long          ubcode_addr = 0;/* Address of the RAM for the unit we are
				 * working with */

char	ub_buff[MAX_UBS*sizeof(struct ubpc)+16] = { 0 };
struct ubpc *ub_pc = (struct ubpc *) ub_buff;

ubop(ubqp)
	ubq_t           ubqp;
{
	u_long          zcbp = ubqp.unix_zcb;
	register int    rc;
	int             zpseg, zpoff;
	char            tempresult;
	int             x = 0;
	char            command;
	/*
	 * We have to use the 3 ZCB far ptr's because FP_SEG and FP_OFF below
	 * will not let us pass the addresses of the zcb's. 
	 */
	struct ZCB far *pczcbp;
	struct ZCB far *rcvzcbp;
	struct ZCB far *xmtzcbp;
	register struct ubdevice *addr;
	register UB_SOFTC *us;
	short           unit = 0;

	sti();

	rc = movein((u_long) (zcbp + 4), (char far *) &unit,
		    (u_short) 2);
	if (rc < 0)
	{
		printf("pc code: Can't read unit\n");
		cli();
		return (rc);
	}
	if ((unsigned) unit >= MAX_UBS)
	{
		printf("pc code: invalid ub unit (%d)\n",unit);
		cli();
		return (rc);
	}
	us = &ub_softc[unit];
	pczcbp = &us->tmpzcb;
	rc = movein((u_long) zcbp, (char far *) pczcbp,
		    (u_short) sizeof(ZCB));
	if (rc < 0)
	{
		printf("pc code: Can't read ZCB\n");
		cli();
		return (rc);
	}

	TRACE(TRACE_UB, pczcbp, sizeof (ZCB));

	pczcbp->header.ZCB_Options = 0;	/* clear options field */
	command = pczcbp->header.ZCB_Command;
	DEBUGF(*dbugptr&UBDEBUG, printf("ub%d: command=%x\n",unit,command));
	switch (command)
	{
	case UB_RCV:
		rcvzcbp = &us->rcvzcb;
		us->rcvzcb = us->tmpzcb;
		us->rcv_zcb = zcbp;
		us->newrcv = 1;
		us->urcvbuff = us->rcvzcb.zcbr.ZCB_Rcv_Bfr_Address;
		us->rcvzcb.zcbr.ZCB_Rcv_Bfr_Address = (long) us->pcrcvbuff;
		us->rcvzcb.zcbr.ZCB_Bfr_Size = (short) MAX_PAC_LEN;
		us->rcvzcb.header.ZCB_Post_Routine = (long) 0;
		zpseg = FP_SEG(rcvzcbp);
		zpoff = FP_OFF(rcvzcbp);
		break;
	case UB_INIT:
		/* We use the ZCB_Acq_Modes field temporarily */
		addr = (struct ubdevice *) pczcbp->zcbi.ZCB_Acq_Modes;
		us->addr = addr;
		pczcbp->zcbi.ZCB_Acq_Modes = 0;	/* Set this field back to 0 */
		us->ram_addr = (long) ((IOIN(&addr->ub_ca) << 8) |
				 ((IOIN(&addr->ub_pos5) & 0x3) << 6)) << 20;
		/* see if we have a valid RAM addr */
		if (us->ram_addr != ADDR1 && us->ram_addr != ADDR2 &&
		    us->ram_addr != ADDR3 && us->ram_addr != ADDR4)
		{
			printf("pc code: invalid ub ram addr (%lx)\n",us->ram_addr);
			us->ram_addr = 0;	/* forget it now */
			cli();
			return (1);
		}
		ubcopyram((char far *) us->ram_addr, (char far *) us->ram_save,
			sizeof us->ram_save);	/* save the ram contents */
		zpseg = FP_SEG(pczcbp);
		zpoff = FP_OFF(pczcbp);
		break;
	case UB_STAT:
		zpseg = FP_SEG(pczcbp);
		zpoff = FP_OFF(pczcbp);
		break;
	case UB_CAN_RCV:
		zpseg = FP_SEG(pczcbp);
		zpoff = FP_OFF(pczcbp);
		break;
	case UB_XMT_FR:
		xmtzcbp = &us->xmtzcb;
		us->xmtzcb = us->tmpzcb;
		us->xmt_zcb = zcbp;
		us->newxmt = 1;
		rc = movein((u_long) us->xmtzcb.zcbt.ZCB_Xmt_Data_Address,
			    (char far *) (us->pcxmtbuff),
		    (u_short) (((us->xmtzcb.zcbt.ZCB_Xmt_Data_Length + 1) >>
				1) << 1));
		us->xmtzcb.zcbt.ZCB_Xmt_Data_Address = (long) us->pcxmtbuff;
		us->xmtzcb.header.ZCB_Post_Routine = (long) 0;
		zpseg = FP_SEG(xmtzcbp);
		zpoff = FP_OFF(xmtzcbp);
		break;
	default:
		rbprintf("pc code: invalid ub command (%d)\n",command);
		cli();
		return (rc);
	}
	if (ubcode_addr = us->ram_addr)
		fci(zpseg, zpoff);	/* call ram if we know where it is */
	if ((pczcbp->header.ZCB_Command != UB_RCV) &&
	    (pczcbp->header.ZCB_Command != UB_XMT_FR))
	{
		int i;
		while ((pczcbp->header.ZCB_Status&0xff) == 0xff)
		{
			if (x++ < 200)
			{
				delay(1);	/* wait for a bit (200ms) */
			}
			else
			{
				DEBUGF(*dbugptr&UBDEBUG, printf("ub%d: timeout; result=%x status=%x\n",unit,pczcbp->header.ZCB_Result,pczcbp->header.ZCB_Status));
				cli();
				return (1);
			}
		}
		for (i=0; i < 6; i++) {
			ub_pc[unit].ub_addr[i]=
					pczcbp->zcbs.ZCB_Stat_Unique_ID[i];
		}

		tempresult = pczcbp->header.ZCB_Result;
		if (!ub_pc[unit].new) {	/* if unix side is old */
			pczcbp->header.ZCB_Result = 0;
			moveout((u_long) zcbp, (char far *) pczcbp,
				(u_short) sizeof(ZCB));
			pczcbp->header.ZCB_Result = tempresult;
			moveout((u_long) zcbp, (char far *) pczcbp,
				(u_short) sizeof(ZCB));
		} else {
			ub_pc[unit].tmp_status = pczcbp->header.ZCB_Result; 
		}
		DEBUGF(*dbugptr&UBDEBUG, printf("ub%d: result=%x status=%x\n",unit,tempresult,pczcbp->header.ZCB_Status));
	}
	cli();
	return (1);
}

sendxmtzcb(unit)
	int             unit;
{
	char            tempresult;
	register UB_SOFTC *us = &ub_softc[unit];

	/* We want to send the result byte last so we make it 0 here */
	if (!ub_pc[unit].new) {	/* if unix side is old */
		tempresult = us->xmtzcb.header.ZCB_Result;
		DEBUGF(*dbugptr&UBDEBUG, printf("ub%d: xmt result=%x\n",unit,tempresult));
		us->xmtzcb.header.ZCB_Result = 0;
		moveout((u_long) us->xmt_zcb, (char far *) &us->xmtzcb,
			(u_short) sizeof(ZCB));
		us->xmtzcb.header.ZCB_Result = tempresult;
		moveout((u_long) us->xmt_zcb, (char far *) &us->xmtzcb,
			(u_short) sizeof(ZCB));
		us->xmtzcb.header.ZCB_Result = 0;
	} else
	if (us->xmtzcb.header.ZCB_Result) {
		ub_pc[unit].xmit_status = us->xmtzcb.header.ZCB_Result;
	} else {
		/* indicate something has happened */
		printf("zero result on xmit\n");
		ub_pc[unit].xmit_status = 1;
	}
	return;
}

sendrcvzcb(unit)
	int             unit;
{
	char            tempresult;
	register UB_SOFTC *us = &ub_softc[unit];

	/* move the bytes from pc memory to romp memory */
	if (!ub_pc[unit].new) {	/* if unix side is old */
		moveout((u_long) us->urcvbuff, (char far *) us->pcrcvbuff,
			(u_short) (((us->rcvzcb.zcbr.ZCB_Rcv_Data_Length + 1) >>
				    1) << 1));
		us->rcvzcb.zcbr.ZCB_Rcv_Bfr_Address = (long) us->urcvbuff;
		/* We want to send the result byte last so we make it 0 here */
		tempresult = us->rcvzcb.header.ZCB_Result;
		DEBUGF(*dbugptr&UBDEBUG, printf("ub%d: rcv result=%x\n",unit,tempresult));
		us->rcvzcb.header.ZCB_Result = 0;
		moveout((u_long) us->rcv_zcb, (char far *) &us->rcvzcb,
			(u_short) sizeof(ZCB));
		us->rcvzcb.header.ZCB_Result = tempresult;
		moveout((u_long) us->rcv_zcb, (char far *) &us->rcvzcb,
			(u_short) sizeof(ZCB));
		us->rcvzcb.header.ZCB_Result = 0;
	} else {

		ub_pc[unit].receive_addr = exchl(physaddr((u_long)
							(char far *)us->pcrcvbuff,0,0));
		ub_pc[unit].receive_length = exchw(us->rcvzcb.zcbr.ZCB_Rcv_Data_Length);
		if (us->rcvzcb.header.ZCB_Result) {
			ub_pc[unit].rcv_status = us->rcvzcb.header.ZCB_Result;
		} else {
			/* indicate something has happened */
			printf("zero result on xmit\n");
			ub_pc[unit].rcv_status = 1;
		}
	}
	return;
}

static
ubcopyram(from, to, length)
char far *from;
char far *to;
{
	if ((option_flag & OPTION_NOSAVEUB) == 0)
		while (--length >= 0)
			*to++ = *from++;
}


/*
 * scan thru the softc structure looking for active ub devices and
 * undo the copy that was done at init time so that we can reuse the
 * device (this is because the device doesn't have a proper reset/shutdown
 * command that gets us back to the initial state).
 */
undo_ub()
{
	int unit;

	for (unit=0; unit<MAX_UBS; ++unit) {
		register UB_SOFTC *us = &ub_softc[unit];
		if (us->ram_addr) {
			DEBUGF(*dbugptr&UBDEBUG, printf("undo_ub: unit=%d\n", unit));
			if ((option_flag & OPTION_NOSAVEUB) == 0) {
				IOOUT(&us->addr->ub_csr, 1);	/* reset */
				DELAY(10);
				IOOUT(&us->addr->ub_csr, 0);	/* stop interrupts */
			}
			ubcopyram((char far *) us->ram_save, (char far *) us->ram_addr,
				sizeof us->ram_save);
		}
	}
}
