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

#include "dnet.h"

typedef struct IOExtSer IOSER;

#define WPEND	1
#define WONE	1		/*  defined if WPEND == 1   */

IOSER Iowq[WPEND];
ubyte Iowact[WPEND];


NetOpen(pior, piow, iosink, devname, unitnum, baud)
IOSER **pior, **piow;
PORT *iosink;
char *devname;
{
    IOSER ios;
    IOSER *ior, *iow;
    short i;

    bzero(&ios, sizeof(ios));
    ios.io_CtlChar = 0x11130102;
    ios.io_SerFlags = SERF_SHARED|SERF_XDISABLED|SERF_RAD_BOOGIE;
    if (OpenDevice(devname, unitnum, &ios, 0))
	dneterror(devname);
    *pior = ior = (IOSER *)AllocMem(sizeof(IOSER), MEMF_PUBLIC);
    *piow = iow = (IOSER *)AllocMem(sizeof(IOSER), MEMF_PUBLIC);
    *ior = ios;
    *iow = ios;
    ior->IOSer.io_Command = SDCMD_QUERY;
    DoIO(ior);
    ior->IOSer.io_Command = SDCMD_SETPARAMS;
    ior->io_SerFlags = SERF_SHARED|SERF_XDISABLED|SERF_RAD_BOOGIE;
    ior->io_ExtFlags = 0;
    ior->io_ReadLen = ior->io_WriteLen = 8;
    ior->io_CtlChar = 0x01020304;
    if (baud)
	ior->io_Baud = baud;
    Baud = ior->io_Baud;
    if (DoIO(ior) != 0)
	dneterror("Unable to set serial parameters");
    ior->IOSer.io_Command = CMD_READ;
    iow->IOSer.io_Command = CMD_WRITE;
    ior->IOSer.io_Message.mn_ReplyPort = iosink;
    iow->IOSer.io_Message.mn_ReplyPort = iosink;
    iow->IOSer.io_Message.mn_Node.ln_Name = (char *)WNET_REQ;
    ior->IOSer.io_Message.mn_Node.ln_Name = (char *)RNET_REQ;

    for (i = 0; i < WPEND; ++i)
	Iowq[i] = *iow;
}

/*
 *  Closedown the network.
 */

NetClose(pior, piow)
IOSER **pior, **piow;
{
    IOSER *ior = *pior;
    IOSER *iow = *piow;

    if (ior) {
	CloseDevice(ior);
	FreeMem(ior, sizeof(IOSER));
	FreeMem(iow, sizeof(IOSER));
    }
    *pior = NULL;
    *piow = NULL;
}

/*
 *  NETCLWRITE()
 *
 *  Clear write request which was GetMsg()'d in DNET.C instead of
 *  WaitIO()'d here.
 */

NetClWrite(ior)
IOSER *ior;
{
    short i = ior - &Iowq[0];
    if (i >= WPEND) {
	printf("NetClWrite: Software error %ld\n", i);
    } else {
	Iowact[i] = 0;
    }
}

/*
 *  NETWRITE()
 *
 *  Write data to the network.	Up to WPEND requests may be made pending
 *  before this call blocks.  This call also controls whether the last
 *  sent packet is to start a timeout sequence or not (by being returned
 *  as WNET_REQ or not).
 */

void
NetWrite(buf, bytes, expectreply)
APTR buf;
{
    short i;
    static short j;
#ifdef WONE
    static short last = -1;
#else
    static short last;
#endif

    if (buf == NULL) {
	for (i = 0; i < WPEND; ++i) {
	    if (Iowact[i]) {
		WaitIO(&Iowq[i]);
		Iowact[i] = 0;
	    }
	}
	fixsignal(Iowq[0].IOSer.io_Message.mn_ReplyPort);
	return;
    }

    /*
     *	Find an open slot.  Do not use the last timeout-ok used slot.
     *	Remove requests that are done.
     */

    for (i = 0; i < WPEND; ++i) {
	if (Iowact[i] && CheckIO(&Iowq[i]) && last != i) {
	    WaitIO(&Iowq[i]);
	    Iowact[i] = 0;
	}
	if (!Iowact[i])
	    break;
    }
    if (bytes) {
	if (i == WPEND) {       /*  all requests active, wait for one   */
	    if (j == last)      /*  but not the WNET_REQ one (if any)   */
		j = (j + 1) % WPEND;
	    WaitIO(&Iowq[i=j]);
	    Iowact[i] = 0;
	    j = (j + 1) % WPEND;
	}

	/*
	 *  If we expect a reply to this packet, name it WNET_REQ (when
	 *  returned starts a timeout sequence), else name it IGWNET_REQ.
	 */

#ifndef WONE
	if (expectreply) {
	    Iowq[last].IOSer.io_Message.mn_Node.ln_Name = (char *)IGWNET_REQ;
	    Iowq[i].IOSer.io_Message.mn_Node.ln_Name = (char *)WNET_REQ;
	    last = i;
	} else {
	    Iowq[i].IOSer.io_Message.mn_Node.ln_Name = (char *)IGWNET_REQ;
	}
#endif
	Iowq[i].IOSer.io_Data = buf;
	Iowq[i].IOSer.io_Length = bytes;
	SendIO(&Iowq[i]);
	Iowact[i] = 1;
    }
    fixsignal(Iowq[0].IOSer.io_Message.mn_ReplyPort);
}

NetBreak()
{
    NetWrite(NULL,0,0);
    Iowq[0].IOSer.io_Command = SDCMD_BREAK;
    DoIO(&Iowq[0]);
    Iowq[0].IOSer.io_Command = CMD_WRITE;
    fixsignal(Iowq[0].IOSer.io_Message.mn_ReplyPort);
}

/*
 *  NETREADY()
 *
 *  Using the specified inactive request, return the # bytes ready and
 *  the carrier status.
 */

NetReady(ior, cd)
IOSER *ior;
ubyte *cd;
{
    register ubyte oldcmd = ior->IOSer.io_Command;
    register long n = 0;
    ior->IOSer.io_Command = SDCMD_QUERY;
    if (DoIO(ior) == 0)
	n = ior->IOSer.io_Actual;
    ior->IOSer.io_Command = oldcmd;
    *cd = !(ior->io_Status & (1 << 5));
    return(n);
}

