/*
 * 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:mc.c 12.1$";
#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"
#include "mc.h"


struct mcdev MCFAR mcdev[NCPU];	/* for sharing information */
int mcopen = 0;			/* we we can send to */
static void farmemcpy(short MCFAR *, short MCFAR *, int);

/*
 * pseudo network device between multiple cpu's on a microchannel.
 */

#ifdef DEBUG
static char *mcname[] = 
{ "unknown", "open", "close", "xmit", "recv", "canrcv", "nop" };

static char *mccmdname(cmd)
	int cmd;
{
	if ((unsigned) cmd > MC_CMD_MAX)
		cmd = 0;
	return(mcname[cmd]);
}
#endif

#define TO_ADDR 5		/* where to find the host address */
#define BROADCAST	0xff

mcreq(mcqp)
	pcplqaddr_t mcqp;	/* Pointer into dpl queue */
{

	int unit = cur_cbcb->cpu;		/* our unit number */
	struct mcdev MCFAR *mc = &mcdev[unit];
	int rc = 0;				/* 0 = nothing done */
	int tohost;


	if (mc->cmd == 0)
		mc->cmd = mcqp->unix_cb;	/* pass cmd in unix_cb */
	DEBUGF(*dbugptr & MCDEBUG, printf("mc%d: cmd=%s (%d)\n", unit, mccmdname(mc->cmd),mc->cmd));
	switch(mc->cmd)
	{
	case MC_CMD_OPEN:
		mc->state = MC_OPEN_STATE | MC_XMIT_ENABLE;	/* mark as open */
		mc->unit = unit;
		mc->addr[0] = 0x80;		/* fake an address */
		mc->addr[1] = 0x80;		/* fake an address */
		mc->addr[2] = 0x80;		/* fake an address */
		mc->addr[3] = 0x80;		/* fake an address */
		mc->addr[4] = 0x80;		/* fake an address */
		mc->addr[5] = unit+1;		/* fake an address */
		mc->rstatus = 0;
		mc->xstatus = 0;
		rc = 1;
		break;
	case MC_CMD_NOP:
		rc = 1;
		break;
	case MC_CMD_CLOSE:
		mcopen &= ~(1<<unit);		/* can't send to it */
		mc->state = 0;			/* we're now closed */
		rc = 1;
		break;
	case MC_CMD_XMIT:
		mc->xlen = exchw(mc->xlength);	/* get it byte swapped */
		if (mc->xlen < 12 || mc->xlen > MAXMCLEN)
			printf("mc%d: xlen=%d\n", unit, mc->xlen);
		tohost = mc->xbuff[TO_ADDR];	/* get host address */
		if (tohost == BROADCAST)
			mc->xmask = mcopen;	/* to everybody */
		else
			mc->xmask = mcopen & (1<< (tohost-1));
		mc->xstatus |= MC_XMIT_BUSY;	/* we are now busy */
		break;
	case MC_CMD_RCVEN:
		mc->state |= MC_RECV_ENABLE;	/* can now recieve */
		mc->rstatus = 0;		/* allow new recieve */
		mcopen |= (1<<unit);
		break;
	case MC_CMD_CANRCV:
		mc->state &= ~MC_RECV_ENABLE;	/* can not now recieve */
		mcopen &= ~(1<<unit);		/* prevent waiting for it */
		rc = 1;
		break;
	default:
		printf("mc: invalid command %d\n", mc->cmd);
		break;
	}
			
	mc_scan();		/* scan to see if anything changed */

	if (rc)
	{
		mc->result = rc;
		if ((mc->state&MC_OPEN_STATE) || (mc->cmd==MC_CMD_NOP))	/* */
			rompint4(4, FAKEPOLL((MCIRQ&07)), 0, 0, cur_cbcb);
	}
	mc->cmd = 0;
}

mcclear(cbcb)
struct cbcb *cbcb;
{

	int unit = cbcb->cpu;		/* our unit number */
	struct mcdev MCFAR *mc = &mcdev[unit];

	mcopen &= ~(1<<unit);		/* can't send to it */
	mc->state = 0;			/* we're now closed */
}

/*
 * go thru and see what we can transmit by copying into a free 
 * receive buffer.
 * we send a receive interrupt if that is requested, and send a
 * transmit interrupt when everybody's gotten the packet.
 * we also notice when a transmit is sending to somebody that
 * isn't open and can't receive the packet.
 */
mc_scan()
{
    struct mcdev MCFAR *mc;
    int i;

    for (mc=mcdev; mc < mcdev+ncpu; ++mc)
	if ((mc->state&MC_OPEN_STATE) && (mc->xstatus&MC_XMIT_BUSY))
	{
	    for (i=0; i<ncpu; ++i)
		if (mc->xmask & (1<<i))
		{
		    struct mcdev MCFAR *rmc = &mcdev[i];

		    if ((rmc->state&MC_OPEN_STATE) == 0)
			mc->xmask &= ~(1<<i);		/* not open now */
		    else if ((rmc->rstatus&MC_RECV_BUSY) == 0)
		    {
			DEBUGF(*dbugptr & MCDEBUG, printf("mc%d: sending %d bytes to %d\n", mc->unit, mc->xlen, rmc->unit));
			/* we can send from mc to rmc */
			rmc->rstatus |= MC_RECV_BUSY;
#if NCPU < 3
			memcpy(rmc->rbuff, mc->xbuff, mc->xlen);
#else
			if (mc->xlen)
			{
			    int s = spl();
			    farmemcpy((short MCFAR *) mc->xbuff, (short MCFAR *) rmc->rbuff, mc->xlen);
			    splx(s);
			}
#endif
			rmc->rlen = mc->xlen;
			rmc->rlength = mc->xlength;	/* byte-swapped */
			rmc->rstatus |= MC_RECV_DONE;
			if (rmc->state & MC_RECV_ENABLE)
			{
			    rompint4(4, FAKEPOLL((MCIRQ&7))|IRQ_PUT_INFO(MC_RCV_INTR), 0, 0, cbcbptr+rmc->unit);
			    DEBUGF(*dbugptr & MCDEBUG, printf("mc%d: rcv done intr\n", rmc->unit));
			}
				
			mc->xmask &= ~(1<<i);
		    }
		}

	    if (mc->xmask == 0)
	    {
		mc->xstatus &= ~MC_XMIT_BUSY;
		mc->xstatus |= MC_XMIT_DONE;
		if (mc->state & MC_XMIT_ENABLE)
		{
		    rompint4(4, FAKEPOLL((MCIRQ&7))|IRQ_PUT_INFO(MC_XMIT_INTR), 0, 0, cbcbptr+mc->unit);
		    DEBUGF(*dbugptr & MCDEBUG, printf("mc%d: xmit done intr\n", mc->unit));
		}
	    }
	}
}

#if NCPU > 2
static void farmemcpy(from, to, length)
short MCFAR *to;
short MCFAR *from;
int length;
{
	for (;length > 0; length -= sizeof (short))
		*to++ = *from++;
}
#endif
