
/*
 *  DNET.C
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 */

#include "dnet.h"
#include <local/deemu.h>
#include <local/ipc.h>

short Deemu[] = {
    DMSTRT, 0, 0,
    DMNW  , 0,10, 50, 50, 320, 100, 0xFFFF,
    DMEND , 0, 0
};


extern int Enable_Abort;
long DResBase;
char PortName[sizeof(DNETPORTNAME)+32];

main(ac,av)
char *av[];
{
    long sink_mask, dnet_mask, ipc_mask;
    long baud = 0;
    char *netdevice = "serial.device";
    long netunit = 0;
    char *autoclient = "FTERM";

    unlink("ENV:DNET_NORUNCLIENT");
    Enable_Abort = 0;
    bzero(Pkts,sizeof(Pkts));
    if ((DResBase = (long)OpenLibrary("dres.library", 0)) == NULL) {
	puts("Unable to open DRES.LIBRARY");
	exit(1);
    }
    ac = DoOption(ac, av, "p,b%ld,d%d,n%s,s%s,a,h%d,U%ld,D%s,N%d,8X%d,B%ld",
	&PDebug, &baud, &DDebug, &HostName, &autoclient, &AutoAnswer,
	&AutoHangup, &netunit, &netdevice, &NetworkNum, &Master8, &DialOut,
	&TOBaud
    );
    if (ac < 0) {
	puts("Unknown option, valid options:\n");
	puts("dnet -pb#B#d#n<hostname>s<autorunclient>aU#D<device>N<netid>8");
	dneterror("Unknown switch");
    }
    if (AutoAnswer) {
	AutoHangup = 1;
	DialOut = 0;
    }
    sprintf(PortName, "%s%d", DNETPORTNAME, NetworkNum);
    {
	PORT *port;
	if (port = (PORT *)FindPort(PortName)) {
	    puts("DNET: Network number in use");
	    CloseLibrary(DResBase);
	    exit(1);
	}
    }
    DNetPort = (PORT *)CreatePort(PortName, 0);
    IOSink   = (PORT *)CreatePort(NULL,0);
    IPCPort  = (PORT *)OpenIPC("dnet.CMD", 0);
    if (!DNetPort || !IOSink)
	dneterror("CreatePort");

    NewList(&TxList);
    NewList(&SvList);
    Rto_act = Wto_act = Cto_act = 0;
    NetOpen(IOSink, netdevice, netunit, &baud);
    TimerOpen(&Rto, IOSink);
    TimerOpen(&Wto, IOSink);
    TimerOpen(&Cto, IOSink);

    if (TOBaud)
	SetTimeouts(TOBaud);
    else
	SetTimeouts(Baud);

    NetStartRead(&Raux->sync, 3);

    do_dnetwindow(baud);
    if (Quit)
	dneterror(NULL);

    Rto.tr_node.io_Message.mn_Node.ln_Name = (char *)RTO_REQ;
    Wto.tr_node.io_Message.mn_Node.ln_Name = (char *)WTO_REQ;
    Cto.tr_node.io_Message.mn_Node.ln_Name = (char *)CTO_REQ;

    sink_mask = 1 << IOSink->mp_SigBit;
    dnet_mask = 1 << DNetPort->mp_SigBit;
    ipc_mask = 1 << IPCPort->mp_SigBit;

    do_netreset();
loop:
    if (strcmp(autoclient, "-") != 0) {
	char buf[64];
	char *tmp;

	if ((tmp = GetDEnv("DNET_NORUNCLIENT")) == NULL) {
	    sprintf(buf, "RUN <NIL: >NIL: %s -N%ld", autoclient, NetworkNum);
	    if (Execute(buf, NULL, NULL) == 0) {
		puts("Unable to RUN FTERM (path not setup?)");
		puts("You can do it yourself");
	    }
	} else {
	    free(tmp);
	}
    }
    NetWrite(RestartPkt, 3, 1);
    Restart = 1;
    OnLine = 1;

    /*
     *	NOTE:	Routines must be particularly careful not to clear the
     *		signal mask unless it doesn't matter.  Specifically,
     *		routines in the dnet_mask section cannot arbitrarily
     *		clear the signal associated with the sink_mask section.
     *
     *		If you look at NetWrite(), you will note that the signal
     *		is restored if it must do a WaitIO().
     */

    /*
     *	Immediate return from initial Wait() ... due to looping it is
     *	possible one or more requests is ready but the signal bit has
     *	already been cleared.
     */

    Signal(FindTask(NULL), ipc_mask|sink_mask|dnet_mask);

    while (!Quit && OnLine) {
	long mask = Wait(ipc_mask|sink_mask|dnet_mask|SIGBREAKF_CTRL_C);

	if (mask & ipc_mask)
	    handle_ipc();

	if (mask & sink_mask) {     /*  IOSink returns      */
	    register IOR *ior;
	    while (ior = (IOR *)GetMsg(IOSink)) {
		switch((long)ior->io_Message.mn_Node.ln_Name) {
		case PKT_REQ:
		    --NumCon;
		    if (ior->io_Length)
			FreeMem(ior->io_Data, ior->io_Length);
		    FreeMem(ior, sizeof(IOR));
		    break;
		case CTO_REQ:	/*  Only when line idle 	    */
		    Cto_act = 0;
		    do_cto(ior);
		    if (Cd == 0 && AutoHangup)
			OnLine = 0;
		    break;
		case RTO_REQ:	/*  Read timeout, reset READ state  */
		    Rto_act = 0;
		    do_rto(ior);
		    break;
		case WTO_REQ:	/*  Write-Ack timeout, send CHECK   */
		    Wto_act = 0;
		    do_wto(ior);
		    break;
		case RNET_REQ:	/*  Receive data ready, interpret   */
		    NetReadReturned();
		    do_rnet(ior);
		    if (Cd == 0 && AutoHangup)
			OnLine = 0;
		    break;
		case WNET_REQ:	/*  Write data sent, start WTO	    */
		    NetClWrite(ior);
		    do_wnet(ior);
		    break;
		case IGWNET_REQ:
		    NetClWrite(ior);
		    break;
		}
	    }
	}
	if (mask & dnet_mask) {     /*  Receive commands    */
	    register IOR *ior;
	    while (ior = (IOR *)GetMsg(DNetPort)) {
		ior->io_Actual = 0;
		switch(ior->io_Command) {
		case DNCMD_WRITE:	/*  write data to net	    */
		    {
			uword chan = (ulong)ior->io_Unit;
			if (Chan[chan].state != CHAN_OPEN) {
			    ior->io_Error = 1;
			    break;
			}
			ior->io_Error = 0;
			ior->io_Command = SCMD_DATA;
			ior->io_Message.mn_Node.ln_Pri = Chan[chan].pri;
			Enqueue(&TxList, ior);
			ior = NULL;
		    }
		    break;
		case DNCMD_SOPEN:   /*	Reply from server port on remote    */
				    /*	open request			    */
		    {
			CACKCMD ack;
			uword chan = (ulong)ior->io_Unit;


			ack.chanh = chan >> 8;
			ack.chanl = chan;
			ack.error = ior->io_Error;
			WriteStream(SCMD_ACKCMD, &ack, sizeof(ack), chan);
			if (ack.error) {
			    Chan[chan].state = CHAN_FREE;
			    --NumCon;
			} else {
			    if (Chan[chan].state == CHAN_CLOSE && !ack.error) {
				WritePort(Chan[chan].port, DNCMD_CLOSE, NULL, 0, PKT_REQ, chan);
				goto sopenbrk;
			    }
			    Chan[chan].state = CHAN_OPEN;
			    Chan[chan].port  = (PORT *)ior->io_Offset;
			    Chan[chan].flags = CHANF_ROK|CHANF_WOK;
			}
sopenbrk:
			if (ior->io_Length)
			    FreeMem(ior->io_Data, ior->io_Length);
			FreeMem(ior, sizeof(IOR));
			ior = NULL;
		    }
		    break;
		case DNCMD_EOF:
		    {
			CEOFCMD eof;
			uword chan = (ulong)ior->io_Unit;

			ior->io_Error = 0;
			eof.chanh = chan >> 8;
			eof.chanl = chan;
			eof.flags = CHANF_ROK;
			WriteStream(SCMD_EOFCMD, &eof, sizeof(CEOFCMD), chan);
			Chan[chan].flags &= ~CHANF_WOK;
		    }
		    break;
		case DNCMD_IOCTL:
		    {
			CIOCTL cio;
			uword chan = (ulong)ior->io_Unit;

			ior->io_Error = 0;
			cio.chanh = chan >> 8;
			cio.chanl = chan;
			cio.valh   = (ubyte)((ulong)ior->io_Data >> 24);
			cio.vall   = (ubyte)((ulong)ior->io_Data >> 16);
			cio.valaux = (ubyte)((ulong)ior->io_Data >> 8);
			cio.cmd = (ubyte)(ulong)ior->io_Data;
			WriteStream(SCMD_IOCTL, &cio, sizeof(CIOCTL), chan);
		    }
		    break;
		case DNCMD_QUIT:
		    {
			char dummy;

			WriteStream(SCMD_QUIT, &dummy, 1, -1);
		    }
		    break;
		case DNCMD_INFO:
		    {
			char *ptr = (char *)ior->io_Data;
			sprintf(ptr, "         Bytes  Packets   Errors\n");
			ptr += strlen(ptr);
			sprintf(ptr, "OUT:  %8ld %8ld %8ld\n", BytesOut, PacketsOut, PacketsResent);
			ptr += strlen(ptr);
			sprintf(ptr, "IN :  %8ld %8ld %8ld\n", BytesIn, PacketsIn, PacketsNakd);
			ptr += strlen(ptr);
			sprintf(ptr, "Garbage Bytes In: %ld\n", GarbageIn);
		    }
		    break;
		case DNCMD_OPEN:
		    ior->io_Error = 0;
		    {
			uword chan = alloc_channel();
			COPEN co;
			if (chan >= MAXCHAN) {
			    ior->io_Error = 1;
			    break;
			}
			co.chanh = chan >> 8;
			co.chanl = chan;
			co.porth = (ulong)ior->io_Unit >> 8;  /* port #   */
			co.portl = (ulong)ior->io_Unit;
			co.error= 0;
			co.pri = (char)(long)ior->io_Message.mn_Node.ln_Name;
			Chan[chan].ior = ior;
			Chan[chan].port= (PORT *)ior->io_Offset;
			Chan[chan].state = CHAN_LOPEN;
			Chan[chan].flags = 0;
			Chan[chan].pri = ior->io_Message.mn_Node.ln_Pri;
			WriteStream(SCMD_OPEN, &co, sizeof(COPEN), chan);
			ior = NULL;
		    }
		    break;
		case DNCMD_CLOSE:	/*  same io_Command for CCTL_?	*/
		    ior->io_Error = 0;
		    {
			CCLOSE cc;
			uword chan = (ulong)ior->io_Unit;

			cc.chanh = chan >> 8;
			cc.chanl = chan;
			WriteStream(SCMD_CLOSE, &cc, sizeof(CCLOSE), chan);
			Chan[chan].ior = ior;
			Chan[chan].state = CHAN_CLOSE;
			Chan[chan].flags |= CHANF_LCLOSE;
			if (Chan[chan].flags & CHANF_RCLOSE) {
			    Chan[chan].state = CHAN_FREE;
			    Chan[chan].ior = NULL;
			} else {
			    ior = NULL;
			}
		    }
		    break;
		}
		if (ior)
		    ReplyMsg(ior);
	    }
	}
	if (mask & SIGBREAKF_CTRL_C)
	    OnLine = 0;
	do_wupdate();
    }
    do_netreset();
    if (!Cd) {
	ResetConnect();
	ResetIdle();
    }
    if (!Quit)
	do_dnetwindow(baud);
    if (!Cd) {
	ResetConnect();
	ResetIdle();
    }
    if (!Quit)
	goto loop;
    dneterror(NULL);
}

do_netreset()
{
    register short i;
    register CHAN *ch;
    register IOR *ior;

    while (ior = RemHead(&TxList)) {
	ior->io_Error = 1;
	ReplyMsg(ior);
    }
    for (i = 0, ch = Chan; i < MAXCHAN; ++i, ++ch) {
	switch(ch->state) {
	case CHAN_OPEN:
	    WritePort(Chan[i].port, DNCMD_CLOSE, NULL, 0, PKT_REQ, i);
	case CHAN_ROPEN:	/*  pending on listen port  */
	    ch->state = CHAN_CLOSE;
	    ch->flags = CHANF_RCLOSE;
	    ch->ior = NULL;
	    break;
	case CHAN_LOPEN:	/*  pending on network	    */
	    ch->ior->io_Error = 1;
	    ReplyMsg(ch->ior);
	    ch->ior = NULL;
	    ch->state = CHAN_FREE;
	    ch->flags = 0;
	    --NumCon;
	    break;
	case CHAN_CLOSE:
	    if (!(ch->flags & CHANF_LCLOSE))
		break;
	    ch->ior->io_Error = 1;
	    ReplyMsg(ch->ior);
	    ch->ior = NULL;
	    ch->state = CHAN_FREE;
	    ch->flags = 0;
	    --NumCon;
	}
    }
    RPStart = 0;
    WPStart = 0;
    WPUsed  = 0;
    RState  = 0;
    RChan = 0;
    WChan = 0;
    if (!Cto_act) {
	Cto.tr_time.tv_secs = 1;
	Cto.tr_time.tv_micro= 0;
	SendIO(&Cto);
	Cto_act = 1;
    }
}

