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

#include "dnet.h"

#define WPEND	4		    /*	must be at least 2	    */

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

static IOSER *CNet;		    /*	control-req (synchro)        */
static IOSER *RNet;		    /*	read-request		     */
static ubyte RNetInProg;    /*	read request in progress     */
static ubyte SerialIsOpen;  /*	serial device is open	     */
static ubyte RecvEnabled = 1;
static char *DevName;
static long UnitNum;

extern void *CheckIO();

NetOpen(iosink, devname, unitnum, pbaud)
PORT *iosink;
char *devname;
long *pbaud;
{
    IOSER ios;
    short i;
    long baud = *pbaud;

    DevName = devname;
    UnitNum = unitnum;

    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);
    ios.IOSer.io_Message.mn_ReplyPort = iosink;
    SerialIsOpen = 1;

    RNet = (IOSER *)AllocMem(sizeof(IOSER), MEMF_PUBLIC);
    CNet = (IOSER *)AllocMem(sizeof(IOSER), MEMF_PUBLIC);
    *RNet = ios;
    *CNet = ios;

    {
	register IOSER *ioc = CNet;

	ioc->IOSer.io_Command = SDCMD_QUERY;
	DoIO(ioc);
	ioc->IOSer.io_Command = SDCMD_SETPARAMS;
	ioc->io_SerFlags = SERF_SHARED|SERF_XDISABLED|SERF_RAD_BOOGIE;
	ioc->io_ExtFlags = 0;
	ioc->io_ReadLen = ioc->io_WriteLen = 8;
	ioc->io_CtlChar = 0x01020304;
	if (baud)
	    ioc->io_Baud = baud;
	*pbaud = Baud = ioc->io_Baud;
	if (DoIO(ioc) != 0)
	    dneterror("Unable to set serial parameters");
    }
    RNet->IOSer.io_Command = CMD_READ;
    RNet->IOSer.io_Message.mn_Node.ln_Name = (char *)RNET_REQ;
    CNet->IOSer.io_Command = CMD_WRITE;
    CNet->IOSer.io_Message.mn_Node.ln_Name = (char *)WNET_REQ;

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

    CNet->IOSer.io_Message.mn_Node.ln_Name = (char *)CNET_REQ;
}

/*
 *  Closedown the network.
 */

NetClose()
{
    NetAbortRead();
    NetWaitWrite();
    if (SerialIsOpen)
	CloseDevice(RNet);
    if (RNet)
	FreeMem(RNet, sizeof(IOSER));
    if (CNet)
	FreeMem(CNet, sizeof(IOSER));
    SerialIsOpen = 0;
    RNet = CNet = 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 < 0 || i >= WPEND) {
	printf("NetClWrite: Software error %ld\n", i);
    } else {
	Iowact[i] = 0;
    }
}

/*
 *  NETWRITE()
 *
 *  Write data to the network.	Only one standard request (for which we
 *  expect a reply) is allowed queued at one time, while several (WPEND-1)
 *  expect-no-reply requests may be queued.
 */

void
NetWrite(buf, bytes, expectreply)
APTR buf;
{
    register short i;

    if (PDebug)
	printf("SEND %02x %4ld bytes\n", ((ubyte *)buf)[1], bytes);

    if (!SerialIsOpen)
	return;

    if (expectreply) {
	if (Iowact[0]) {
	    WaitIO(&Iowq[0]);
	    Iowact[0] = 0;
	}
	i = 0;
    } else {
	for (i = 1; i < WPEND; ++i) {
	    if (!Iowact[i])
		break;
	    if (CheckIO(&Iowq[i])) {
		WaitIO(&Iowq[i]);
		Iowact[i] = 0;
		break;
	    }
	}
	if (i == WPEND) {
	    WaitIO(&Iowq[1]);
	    Iowact[1] = 0;
	    i = 1;
	}
    }

    if (bytes) {
	register IOSER *iow = &Iowq[i];
	iow->IOSer.io_Message.mn_Node.ln_Name = (char *)((expectreply) ? WNET_REQ : IGWNET_REQ);
	iow->IOSer.io_Data = buf;
	iow->IOSer.io_Length = bytes;
	SendIO(iow);
	Iowact[i] = 1;
    }
    fixsignal(Iowq[0].IOSer.io_Message.mn_ReplyPort);
}

NetBreak()
{
    if (SerialIsOpen) {
	CNet->IOSer.io_Command = SDCMD_BREAK;
	DoIO(CNet);
	fixsignal(Iowq[0].IOSer.io_Message.mn_ReplyPort);
    }
}

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

NetReady()
{
    register long n = 0;

    if (SerialIsOpen && RecvEnabled) {
	CNet->IOSer.io_Command = SDCMD_QUERY;
	if (DoIO(CNet) == 0)
	    n = CNet->IOSer.io_Actual;
	Cd = !(CNet->io_Status & (1 << 5));
    }
    return(n);
}

/*
 *  SetBaudRate()
 */

SetBaudRate(baud)
long baud;
{
    register long result;

    if (TOBaud == 0)
	SetTimeouts(baud);
    if (CNet)
	CNet->io_Baud = baud;
    if (!SerialIsOpen)
	return(1);
    SaveState();
    CNet->IOSer.io_Command = SDCMD_SETPARAMS;
    result = DoIO(CNet);
    RestoreState();
    return(result == 0);
}

/*
 *  Assumes CNet holds current settings
 */

DropDTR()
{
    if (SerialIsOpen) {     /*  only if serial.device currently open    */
	NetSerialOff();
	Delay(50L);
	NetSerialOn();
    }
}

IOSER *
NetAbortRead()
{
    if (RNetInProg) {
	if (SerialIsOpen && RecvEnabled) {
	    AbortIO(RNet);
	    WaitIO(RNet);
	}
	RNetInProg = 0;
    }
    return(RNet);
}

static APTR  SaveBuf;
static long  SaveLen;

NetStartRead(buf, len)
APTR buf;
long len;
{
    if (SerialIsOpen && RecvEnabled) {
	if (RNetInProg)
	    NetAbortRead();
	SaveBuf = RNet->IOSer.io_Data = buf;
	SaveLen = RNet->IOSer.io_Length = len;
	SendIO(RNet);
    } else {
	SaveBuf = buf;		/*  fake it */
	SaveLen = len;
	RNet->IOSer.io_Actual = 0;
    }
    RNetInProg = 1;
}

IOSER *
NetReadReady()
{
    return((IOSER *)CheckIO(RNet));
}

NetReadReturned()
{
    RNetInProg = 0;
}

NetWaitWrite()
{
    register short i;
    register short flag = 0;
    for (i = 0; i < WPEND; ++i) {
	if (Iowact[i]) {
	    WaitIO(&Iowq[i]);
	    Iowact[i] = 0;
	    flag = 1;
	}
    }
    if (flag)
	fixsignal(Iowq[0].IOSer.io_Message.mn_ReplyPort);
}

static char SvRInProg;

SaveState()
{
    if (SvRInProg = RNetInProg)
	NetAbortRead();
    NetWaitWrite();
}

RestoreState()
{
    if (SvRInProg) {
	register long n = RNet->IOSer.io_Actual;
	if (n < 0)
	    n = 0;
	NetStartRead((char *)SaveBuf + n, SaveLen - n);
    }
}

NetRecvOff()
{
    if (RecvEnabled) {
	SaveState();
	RecvEnabled = 0;
    }
}

NetRecvOn()
{
    if (!RecvEnabled) {
	RecvEnabled = 1;
	RestoreState();
    }
}

NetSerialOff()
{
    if (SerialIsOpen) {
	SaveState();            /*  make sure no requests pending   */
	CloseDevice(RNet);      /*  close the device                */
	SerialIsOpen = 0;
    }
}

NetSerialOn()
{
    if (!SerialIsOpen) {
	register short i;
	if (OpenDevice(DevName, UnitNum, RNet, 0))      /* OH HELL! */
	    return(0);

	/*
	 *  Fix the Device / Unit fields and restore the previous
	 *  modes.
	 */

	SerialIsOpen = 1;
	for (i = 0; i < WPEND; ++i) {
	    Iowq[i].IOSer.io_Device = RNet->IOSer.io_Device;
	    Iowq[i].IOSer.io_Unit   = RNet->IOSer.io_Unit;
	}
	CNet->IOSer.io_Device = RNet->IOSer.io_Device;
	CNet->IOSer.io_Unit   = RNet->IOSer.io_Unit;

	RNet->IOSer.io_Command = CMD_READ;
				/*  restore params		    */
	CNet->IOSer.io_Command = SDCMD_SETPARAMS;
	CNet->io_SerFlags = SERF_SHARED|SERF_XDISABLED|SERF_RAD_BOOGIE;
	DoIO(CNet);
	RestoreState();         /*  restore pending read req.       */
    }
    return(1);
}

