
/*
 *  DNET.C
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *
 *  V <1.05 Alpha Release
 *  V <1.10 Beta  Release (Internal)
 *  V1.10   First Release
 */

#include "dnet.h"

extern int Enable_Abort;

main(ac,av)
char *av[];
{
    long sink_mask, dnet_mask;
    long baud = 0;
    short networknum = 0;
    char *netdevice = "serial.device";
    long netunit = 0;
    ubyte notdone;
    ubyte autofterm = 1;
    char buf[sizeof(DNETPORTNAME)+32];

    Enable_Abort = 0;
    bzero(Pkts,sizeof(Pkts));
    {
	register short i;
	register char *ptr;
	for (i = 1; i < ac; ++i) {
	    if (*(ptr = av[i]) == '-') {
		while (*++ptr) {
		    switch(*ptr) {
		    case 'b':
			baud = atoi(ptr+1);
			ptr = "\0\0";
			break;
		    case 'd':
			DDebug = 1;
			puts("Debugger on");
			break;
		    case 'n':
			HostName = ptr+1;
			ptr = "\0\0";
			break;
		    case 's':
			autofterm = 0;
			break;
		    case 'h':
			AutoHangup = 1;
			break;
		    case 'U':
			netunit = atoi(ptr+1);
			ptr = "\0\0";
			break;
		    case 'D':
			netdevice = ptr + 1;
			ptr = "\0\0";
			break;
		    case 'N':
			networknum = atoi(ptr+1);
			ptr = "\0\0";
			break;
		    default:
			printf("Unknown option: %c\n", *ptr);
			dneterror("Unknown switch");
			break;	/*  not reached */
		    }
		}
	    }
	}
    }
    sprintf(buf, "%s%d", DNETPORTNAME, networknum);
    {
	PORT *port;
	if (port = (PORT *)FindPort(buf)) {
	    puts("DNET: Network number in use");
	    exit(1);
	}
    }
    DNetPort = (PORT *)CreatePort(buf, 0);
    IOSink   = (PORT *)CreatePort(NULL,0);
    if (!DNetPort || !IOSink)
	dneterror("CreatePort");
    NewList(&TxList);
    NewList(&SvList);
    Rto_act = Wto_act = 0;
    NetOpen(&RNet,&WNet, IOSink, netdevice, netunit, baud);
    TimerOpen(&Rto, IOSink);
    TimerOpen(&Wto, IOSink);

    WTimeoutVal = ((MAXPKT * 2) * 1000 / (Baud / 10 + 1));
    RTimeoutVal = ((MAXPKT + 50) * 1000 / (Baud / 10 + 1));
    WTimeoutVal *= 1000;
    RTimeoutVal *= 1000;

    RNet->io_Data   = (APTR)&Raux->sync; /*  Startup the network read    */
    RNet->io_Length = 3;
    SendIO(RNet);

    if (do_dnetwindow() < 0)
	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;

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

    do_netreset();
loop:
    if (autofterm) {
	char buf[32];
	sprintf(buf, "RUN <NIL: >NIL: FTERM -N%ld", networknum);
	if (Execute(buf, NULL, NULL) == 0) {
	    puts("Unable to RUN FTERM (path not setup?)");
	    puts("You can do it yourself");
	}
    }
    NetWrite(RestartPkt, 3, 1);
    Restart = 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().
     */

    notdone = 1;
    while (notdone) {
	long mask = Wait(sink_mask|dnet_mask|SIGBREAKF_CTRL_C);
	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 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   */
		    if (do_rnet(ior) < 0 && AutoHangup) /*  handle receive data */
			notdone = 0;
		    SendIO(ior);            /*  restart receive     */
		    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);
			do_wupdate();
			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;
			}
			do_wupdate();
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;
			do_wupdate();
		    }
		    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);
			do_wupdate();
		    }
		    break;
		case DNCMD_QUIT:
		    {
			char dummy;

			DeldQuit = 1;
			WriteStream(SCMD_QUIT, &dummy, 1, -1);
			do_wupdate();
		    }
		    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;
			do_wupdate();
		    }
		    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;
			}
			do_wupdate();
		    }
		    break;
		}
		if (ior)
		    ReplyMsg(ior);
	    }
	}
	if (mask & SIGBREAKF_CTRL_C)
	    notdone = 0;
    }
    do_netreset();
    if (do_dnetwindow() > 0)
	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;
}



