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

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

void do_netreset();
char *GetDEnv();

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];

#ifdef LATTICE

int __stdargs CXBRK(void);      /*  get around a bug in lcr.lib : onbreak() */

int __stdargs
CXBRK(void)
{
    return(0);
}

#endif

void main ARGS((int, char **));

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

    unlink("ENV:DNET_NORUNCLIENT");
#ifndef LATTICE
    Enable_Abort = 0;
#endif
    BZero(Pkts,sizeof(Pkts));
    InitServers();

    for (i = 1; i < ac; ++i) {
	if (strcmp(av[i], "-Getty") == 0) {
	    Getty = 1;
	    AutoHangup = 1;
	    av[i] = "";
	}
	if (strcmp(av[i], "-DEVICE") == 0) {
	    netdevice = av[i + 1];
	    av[i] = "";
	    av[i+1] = "";
	}
	if (strcmp(av[i], "-UNIT") == 0) {
	    netunit = atoi(av[i+1]);
	    av[i] = "";
	    av[i+1] = "";
	}
    }

    ac = DoOption(ac, av, "p,m%d,b%ld,d%d,n%s,s%s,a,h%d,U%ld,D%s,N%d,8X%d,B%ld,P%d",
	&PDebug, &Mode7, &baud, &DDebug, &HostName, &autoclient, &AutoAnswer,
	&AutoHangup, &netunit, &netdevice, &NetworkNum, &Master8, &DialOut,
	&TOBaud, &Protocol
    );
    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((LIST *)&TxList);
    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(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) {
	FILE *fi = fopen("T:dscr", "w");
	if (fi) {
	    fprintf(fi, "Wait 5\n");
	    fprintf(fi, "%s -N%ld\n", autoclient, NetworkNum);
	    fclose(fi);
	    Execute("RUN <NIL: >NIL: execute T:dscr", NULL, NULL);
	}
    }

    WriteRestart();
    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      */
	    IOSTD *ior;
	    while (ior = (IOSTD *)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(IOSTD));
		    break;
		case CTO_REQ:	/*  Only when line idle 	    */
		    Cto_act = 0;
		    do_cto((IOT *)ior);
		    if (Cd == 0 && AutoHangup)
			OnLine = 0;
		    break;
		case RTO_REQ:	/*  Read timeout, reset READ state  */
		    Rto_act = 0;
		    do_rto((IOT *)ior);
		    break;
		case WTO_REQ:	/*  Write-Ack timeout, send CHECK   */
		    Wto_act = 0;
		    do_wto((IOT *)ior);
		    break;
		case RNET_REQ:	/*  Receive data ready, interpret   */
		    {
			char *ptr;
			long bytes = NetReadReturned(&ptr);
			do_rnet(ptr, bytes);
		    }
		    if (Cd == 0 && AutoHangup)
			OnLine = 0;
		    break;
		case WNET_REQ:
		    NetClWrite((IOSER *)ior);
		    break;
		}
	    }
	}
	if (mask & dnet_mask) {     /*  Receive commands    */
	    IOSTD *ior;
	    while (ior = (IOSTD *)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, (NODE *)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(IOSTD));
			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, (uword)-1);
		    }
		    break;
		case DNCMD_INFO:
		    {
			char *ptr = (char *)ior->io_Data;
			sprintf(ptr, "         Bytes  Packets   Errors\n"
				     "OUT:  %8ld %8ld %8ld\n"
				     "IN :  %8ld %8ld %8ld\n"
				     "Garbage Bytes In: %ld\n\n"
				     "                        6-expn   7-comp   8-bin\n"
				     "Packet Breakdown Send: %8ld %8ld %8ld\n"
				     "Packet Breakdown Recv: %8ld %8ld %8ld\n",

				    BytesOut, PacketsOut, PacketsResent,
				    BytesIn,  PacketsIn,  PacketsNakd,
				    GarbageIn,
				    Packets6Out, Packets7Out, Packets8Out,
				    Packets6In,  Packets7In,  Packets8In
			);
		    }
		    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;
		case DNCMD_EXEC:
		    Execute((char *)ior->io_Offset, NULL, NULL);
		    break;
		}
		if (ior)
		    ReplyMsg((MSG *)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);
}

void
do_netreset()
{
    short i;
    CHAN *ch;
    IOSTD *ior;

    while (ior = (IOSTD *)RemHead((LIST *)&TxList)) {
	ior->io_Error = 1;
	ReplyMsg((MSG *)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((MSG *)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((MSG *)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((IOR *)&Cto);
	Cto_act = 1;
    }
}

