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

#include "dnet.h"
#include <stdio.h>

/*
 *  Add the high-level command to the queue of commands to be sent to
 *  the remote DNET.
 */

WriteStream(sdcmd, buf, len, chan)
uword chan;
{
    register IOR *ior = (IOR *)AllocMem(sizeof(IOR), MEMF_PUBLIC);

    if (DDebug)
	printf("WriteStream: cmd %ld  buflen %ld  chan %ld\n", sdcmd, len, chan);
    ior->io_Message.mn_Node.ln_Name = (char *)PKT_REQ;
    ior->io_Message.mn_ReplyPort = IOSink;
    ior->io_Data = (APTR)AllocMem(len, MEMF_PUBLIC);
    ior->io_Length = len;
    ior->io_Actual = 0;
    ior->io_Command = sdcmd;
    ior->io_Error = 0;
    ior->io_Message.mn_Node.ln_Pri = (chan > MAXCHAN) ? 126 : Chan[chan].pri;
    CopyMem(buf, ior->io_Data, len);
    Enqueue(&TxList, ior);
    do_wupdate();
}

/*
 *  Send a packet to a port.  Used to send data/eof/close to channel
 *  ports, and to relay open requests to server ports.
 */

WritePort(port, scmd, buf, len, rettype, unit)
PORT *port;
ubyte *buf;
{
    register IOR *ior = (IOR *)AllocMem(sizeof(IOR), MEMF_PUBLIC);

    if (DDebug)
	printf("WritePort: cmd %ld  buflen %ld  rt %ld unit %ld\n", scmd, len, rettype, unit);
    ior->io_Message.mn_Node.ln_Name = (char *)rettype;
    ior->io_Message.mn_ReplyPort = (rettype) ? IOSink : DNetPort;
    ior->io_Unit = (struct Unit *)unit;
    ior->io_Command = scmd;
    ior->io_Error = 0;
    ior->io_Data  = NULL;
    ior->io_Length = 0;
    ior->io_Actual = 0;
    if (buf) {
	ior->io_Data = (APTR)AllocMem(len, MEMF_PUBLIC);
	ior->io_Length = len;
	CopyMem(buf, ior->io_Data, len);
    }
    PutMsg(port, ior);
    if (rettype == PKT_REQ)
	++NumCon;
}

/*
 *  ALLOC_CHANNEL()
 *
 *	Allocate a channel.  Starting at a random point, find the first
 *	free channel.  Return -1 if no free channels found.
 */

alloc_channel()
{
    static ulong ran = 13;
    long stamp[3];
    register uword i;

    DateStamp(stamp);
    ran = ((ran * 13) + 1) ^ (ran >> 9) + stamp[0] + stamp[1] + stamp[2];
    for (i = ran % MAXCHAN; i < MAXCHAN; ++i) {
	if (Chan[i].state == 0) {
	    return(i);
	}
    }
    for (i = ran % MAXCHAN; i != 0xFFFF; --i) {  /*  Fixed 28 September 1988 */
	if (Chan[i].state == 0) {
	    return(i);
	}
    }
    return(-1);
}

/*
 *  Get next node in a linked list.  If a pointer to a list base
 *  is passed, gets the first node in the linked list.	Returns
 *  NULL if no further nodes in the list.
 */

GetNext(node)
NODE *node;
{
    register NODE *next = node->ln_Succ;
    if (*(long *)next)
	return((long)next);
    return(NULL);
}

/*
 *  CHKBUF
 *
 *  Generate a two-byte checksum for data.  Uses a very simple, but
 *  effective algorithm.
 */

chkbuf(buf, bytes)
register ubyte *buf;
register uword bytes;
{
    register uword i;
    register ubyte c1,c2;

    for (i = c1 = c2 = 0; i < bytes; ++i) {
	c1 += buf[i];
	c2 += c1;
    }
    c1 = -(c1 + c2);
    return((c1<<8)|c2);
}


TimerOpen(req, sink)
IOT *req;
PORT *sink;
{
    if (OpenDevice("timer.device", UNIT_VBLANK, req, 0))
	dneterror("timer.device");
    req->tr_node.io_Message.mn_ReplyPort = sink;
    req->tr_node.io_Command = TR_ADDREQUEST;
}

TimerClose(req)
IOT *req;
{
    CloseDevice(req);
    req->tr_node.io_Device = NULL;
}


/*
 *  RUNSERVER
 *
 *	Search the file [S:]DNET.SERVERS for the specified server and
 *	attempt to LoadSeg()/CreateProc() it.  The port number must be
 *	non-zero (allows commening out lines in DNET.SERVERS), and
 *	regardless of whether the server is able to DListen(), it must
 *	perform a small handshake sequence with DNET.
 */

void
RunServer(portnum)
uword portnum;
{
    FILE *fi;
    char buf[128];
    char s1[64], s2[64];
    long v1;
    long seg;

    if (!portnum)
	return;
    if ((fi = fopen("dnet.servers", "r")) == NULL)
	fi = fopen("s:dnet.servers", "r");
    if (fi) {
	while (fgets(buf, 128, fi)) {
	    if (sscanf(buf, "%ld %s %s", &v1, s1, s2) == 3 && v1 == portnum)
		goto success;
	}
	fclose(fi);
    }
    return;
success:
    fclose(fi);
    if (seg = LoadSeg(s1)) {
	PNODE	*pnode;
	PROC	*myproc;
	PROC	*proc;
	char	*name;
	MSG	msg;

	myproc = (PROC *)FindTask(NULL);
	pnode = AllocMem(sizeof(PNODE), MEMF_PUBLIC);
	sprintf(pnode->name, "DNET.SERVER.%ld", portnum);
	pnode->seg = seg;
	if (proc = CreateProc(pnode->name, 0, pnode->seg, 4096)) {
	    proc = (PROC *)((char *)proc - OFFSET(proc,pr_MsgPort));
	    msg.mn_ReplyPort = &myproc->pr_MsgPort;
	    msg.mn_Node.ln_Name = s2;
	    printf("Starting Server: %s %s\n", s1, pnode->name);
	    PutMsg(&proc->pr_MsgPort, &msg);        /*  startup handshake */
	    WaitPort(&myproc->pr_MsgPort);
	    GetMsg(&myproc->pr_MsgPort);
	    AddTail(&SvList, pnode);
	} else {
	    printf("Unable to start server: %s %s\n", s1, pnode->name);
	    FreeMem(pnode, sizeof(PNODE));
	}
    } else {
	printf("Server not found: %s\n", s1);
    }
}

/*
 *  This cleans up as best as possible, but if there are still ACTIVE
 *  servers or any CLIENTS, this call will not completely get rid of
 *  them...
 *
 *  Note that this call will KILL any servers automatically started up
 *  by DNET and then UnLoadSeg() them.  If the server has already exited,
 *  it simply unloads the server's segment.
 */

dneterror(str)
char *str;
{
    register short i;
    if (str)
	puts(str);
    if (DNetPort) {
	register IOR *ior;
	Forbid();
	while ((ior = (IOR *)RemHead(&TxList)) || (ior = (IOR *)GetMsg(DNetPort))) {
	    ior->io_Error = -1;
	    ReplyMsg(ior);
	}
	DeletePort(DNetPort);
	Permit();
	DNetPort = NULL;
    }
    if (Wto.tr_node.io_Device) {
	if (Wto_act) {
	    AbortIO(&Wto);
	    WaitIO(&Wto);
	    Wto_act = 0;
	}
	TimerClose(&Wto);
    }
    if (Rto.tr_node.io_Device) {
	if (Rto_act) {
	    AbortIO(&Rto);
	    WaitIO(&Rto);
	    Rto_act = 0;
	}
	TimerClose(&Rto);
    }
    if (Cto.tr_node.io_Device) {
	if (Cto_act) {
	    AbortIO(&Cto);
	    WaitIO(&Cto);
	    Cto_act = 0;
	}
	TimerClose(&Cto);
    }

    NetClose();
    if (IOSink)
	DeletePort(IOSink);
    IOSink = NULL;
    for (i = 0; i < MAXCHAN; ++i) {
	if (Chan[i].state != CHAN_FREE && Chan[i].ior) {
	    Chan[i].ior->io_Error = -1;
	    Chan[i].state = CHAN_FREE;
	    ReplyMsg(Chan[i].ior);
	}
    }
    {
	register PNODE *pnode;
	register PROC *proc;
	while (pnode = (PNODE *)RemHead(&SvList)) {
	    Forbid();
	    while (proc = (PROC *)FindTask(pnode->name)) {
		Signal(proc, SIGBREAKF_CTRL_C);
		Permit();
		printf("Waiting for server %s to exit\n", pnode->name);
		Forbid();
	    }
	    Permit();
	    UnLoadSeg(pnode->seg);
	    FreeMem(pnode, sizeof(PNODE));
	}
    }
    if (IPCPort)
	CloseIPC(IPCPort);
    if (DResBase)
	CloseLibrary(DResBase);
    exit(1);
}

bzero(ptr, bytes)
register ubyte *ptr;
register long bytes;
{
    while (bytes--)
	*ptr++ = 0;
}

fixsignal(port)
register PORT *port;
{
    if ((long)port->mp_MsgList.lh_Head != (long)&port->mp_MsgList.lh_Tail)
	Signal(port->mp_SigTask, 1 << port->mp_SigBit);
}

SetTimeouts(baud)
{
    WTimeoutVal = ((MAXPKT * 2) * 1000 / (baud / 10 + 1));
    RTimeoutVal = ((MAXPKT + 50) * 1000 / (baud / 10 + 1));
    WTimeoutVal *= 1000;
    RTimeoutVal *= 1000;
}

