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

#include "dnet.h"

#ifdef __GNUC__
#include <ctype.h>
#endif

IOSER *NetAbortRead();
void NetWaitWrite();
void SaveState();
void RestoreState();
void NetSerialOff();
void NetSerialOn();

static IOSER Iosw;        /* Serial IO write request */
static ubyte IoswIP = 0;  /* is a write request pending? */
static ubyte LastWasWrite = 0;   /* was the last thing mentioned a write? */

static ubyte IosrIP = 0;	/* Serio IO read request is pending. ? */
static ubyte RBuf[MAXPACKET*2];     /*	Recv Buf.		    */

static IOSER *CNet;		    /*	control-req (synchro)       */
static IOSER *RNet;		    /*	read-request		    */
static ubyte SerialIsOpen;	    /*	serial device is open	     */
static ubyte RecvEnabled = 1;       /* is reading enabled? */
static char *DevName = NULL;
static long UnitNum = 0;

/* open (serial) device and do related setup */
void
NetOpen(iosink, devname, unitnum, pbaud)
PORT *iosink;
char *devname;
long *pbaud;
{
    IOSER ios;
    long baud = *pbaud;

    DevName = devname;
    UnitNum = unitnum;

    if(DDebug)printf("netopen %s %d\n", devname, unitnum);

    BZero(&ios, sizeof(ios));
    ios.io_CtlChar = 0x11130102;
    ios.io_SerFlags = SERF_SHARED;
    if (OpenDevice(devname, unitnum, (IOR *)&ios, 0))
	dneterror(devname);
    ios.IOSer.io_Message.mn_ReplyPort = iosink;
    SerialIsOpen = 1;

    RNet = (IOSER *)AllocMem(sizeof(IOSER), MEMF_PUBLIC);
    if(RNet == NULL ) printf("Ack, AllocMemfailure for RNet!!\n");
    *RNet = ios;

    CNet = (IOSER *)AllocMem(sizeof(IOSER), MEMF_PUBLIC);
    if(CNet == NULL ) printf("Ack, AllocMemfailure for CNet!!\n");
    *CNet = ios;

    {
	IOSER *ioc = CNet;

	ioc->IOSer.io_Command = SDCMD_QUERY;
	DoIO((IOR *)ioc);

	if (Getty == 0) {
	    ioc->IOSer.io_Command = SDCMD_SETPARAMS;

	    if (baud)
		ioc->io_Baud = baud;

	    ioc->io_SerFlags |= SERF_SHARED;   /* share device with others */
	    ioc->io_CtlChar = 0x01020304;      /* device stop characters? */

	    if (Protocol >= 0) {  /* always true? */
		ioc->io_SerFlags |= SERF_XDISABLED|SERF_RAD_BOOGIE;

		switch(Protocol) {
		case 0:
		    break;
		case 1:     /*	xon-xoff    */
		    Mode7 = 1;
		    ioc->io_SerFlags &= ~(SERF_XDISABLED | SERF_RAD_BOOGIE);
		    break;
		case 2:
		case 7:
		    ioc->io_SerFlags |= SERF_7WIRE;
		    break;
		}
	    }

	    if (Parity >= 0) { /* always true? */
		ioc->io_ReadLen = ioc->io_WriteLen = 7;
		ioc->io_SerFlags &= ~(SERF_PARTY_ON|SERF_PARTY_ODD);
		ioc->io_ExtFlags &= ~(SEXTF_MSPON|SEXTF_MARK);
		ioc->io_SerFlags &= ~SERF_RAD_BOOGIE;
		switch(Parity) {
		case 0:
		    ioc->io_ReadLen = ioc->io_WriteLen = 8;
		    if (Protocol != 1)
			ioc->io_SerFlags |= SERF_RAD_BOOGIE;
		    break;
		case 1:
		    ioc->io_SerFlags |= SERF_PARTY_ON | SERF_PARTY_ODD;
		    break;
		case 2:
		    ioc->io_SerFlags |= SERF_PARTY_ON;
		    break;
		case 3:
		    ioc->io_SerFlags |= SERF_PARTY_ON;
		    ioc->io_ExtFlags |= SEXTF_MSPON | SEXTF_MARK;
		    break;
		case 4:
		    ioc->io_SerFlags |= SERF_PARTY_ON;
		    ioc->io_ExtFlags |= SEXTF_MSPON;
		    break;
		}
	    }
	    if(DDebug){
		printf("parity is ");
		if (ioc->io_SerFlags & SERF_PARTY_ON) {
		    if (ioc->io_ExtFlags & SEXTF_MSPON) {
			if (ioc->io_ExtFlags & SEXTF_MARK)
			    puts("mark");
			else
			    puts("space");
		    } else {
			if (ioc->io_SerFlags & SERF_PARTY_ODD)
			    puts("odd");
			else
			    puts("even");
		    }
		} else {
		    puts("off");
		}
		printf("protocol is ");
		if (ioc->io_SerFlags & SERF_7WIRE)
		    printf("7wire ");
		if ((ioc->io_SerFlags & SERF_XDISABLED) == 0)
		    printf("xon-xoff");
		if ((ioc->io_SerFlags & SERF_7WIRE) == 0 && (ioc->io_SerFlags & SERF_XDISABLED))
		    printf("none");
		puts("");
		fflush(stdout);
	    }

	    *pbaud = Baud = ioc->io_Baud;
	    if (DoIO((IOR *)ioc) != 0){
		/*  This needen't be fatal 
		dneterror("Unable to set serial parameters");
		*/
		if(DDebug)printf("Unable to set serial parameters");
		/* check device error channel?  Naah... */
	    }
	}
    }

    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;

    Iosw = *CNet;

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

    if(DDebug){puts("net open"); fflush(stdout);}
}

/*
 *  Closedown the network.
 */
void
NetClose()
{
    NetAbortRead();
    NetWaitWrite();
    if (SerialIsOpen)
	CloseDevice((IOR *)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.
 */

void
NetClWrite(ior)
IOSER *ior;
{
    if (ior == &Iosw){
	IoswIP = 0;
	if(DDebug)printf("DoingNetClWrite\n");
    }
}

/* write 'bytes' of data from 'buf' to the (serial) device */
void
NetWrite(buf, bytes)
void *buf;
int bytes;
{
    long sigMask;
    if (PDebug && !OnLine ) {
	if(LastWasWrite != 1 ){
	    printf("WRITE:", bytes);
	    LastWasWrite = 1;
	}
	HexDump(buf, bytes);
    }

    if (!SerialIsOpen){
	if(DDebug) printf("Oops, can't write data before SerialIsOpen!\n");
	return;
    }

    if (IoswIP) {
#ifdef TRAPNET
	long mask, iOMask, newmask;
bogus:
        iOMask = (1<<Iosw.IOSer.io_Message.mn_ReplyPort->mp_SigBit) ;
	mask = iOMask | SIGBREAKF_CTRL_C;
	newmask = Wait(mask);
	if(newmask & SIGBREAKF_CTRL_C){
	    if(DDebug)printf("^C caught in wait for Iosw\n");
	    /* goto bogus; */
	} 
	if(newmask & iOMask){
	    /* Have to deal with the replied messages on the port! */
	    /* uh oh, both reads and writes reply to this port? */
	    /* could handle too many things off it! */
	    while(GetMsg( Iosw.IOSer.io_Message.mn_ReplyPort) != NULL);
	    /* dammit, why couldn't WaitIO handle breaks nicely */
	}
	fixsignal(Iosw.IOSer.io_Message.mn_ReplyPort);
#else
	WaitIO( (IOR *) &Iosw);
#endif
	IoswIP = 0;
    }

    if (bytes) {
	Iosw.IOSer.io_Message.mn_Node.ln_Name = (char *)WNET_REQ;
	Iosw.IOSer.io_Data = (APTR)buf;
	Iosw.IOSer.io_Length = bytes;
	SendIO((IOR *)&Iosw);
	IoswIP = 1;
    }
    fixsignal(Iosw.IOSer.io_Message.mn_ReplyPort);
}

/* tell the device to send a break */
void
NetBreak()
{
    if (SerialIsOpen) {
	CNet->IOSer.io_Command = SDCMD_BREAK;
	DoIO((IOR *)CNet);
	fixsignal(Iosw.IOSer.io_Message.mn_ReplyPort);
    }
}

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

NetReady()
{
    long n = 0;

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

/*
 *  SetBaudRate()
 */

SetBaudRate(baud)
long baud;
{
    long result;

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

    if (result)
	printf("Unable to set baud rate to %d\n", baud);

    if (!result && DDebug)
	printf("Baud rate set to %d\n", baud);

    return(0);  /*  don't abort whether we can or not   */
}

/*
 *  Assumes CNet holds current settings
 */

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

/* abort serial read */
IOSER *
NetAbortRead()
{
    if (IosrIP) {

	if (SerialIsOpen && RecvEnabled) {
	    AbortIO((IOR *)RNet);
#ifdef TRAPNET
	    {
		long waitMask,iOMask, returnmask=0;
    Bogus:
		iOMask = 1 << RNet->IOSer.io_Message.mn_ReplyPort->mp_SigBit;
		waitMask = SIGBREAKF_CTRL_C | iOMask;
		returnmask = Wait(waitMask);
		if(returnmask & SIGBREAKF_CTRL_C){
		    printf("^C trapped in wait in NetAbortRead\n");
		    /* goto Bogus; */
		}
		if(returnmask & iOMask){
		    /* Have to deal with the replied messages on the port! */
		    /* this would ordinarily be done if we were using WaitIO() */
		    while(GetMsg( Iosw.IOSer.io_Message.mn_ReplyPort) != NULL);
		}
	    }
#else
	    WaitIO((IOR *) RNet);
#endif
	}
	IosrIP = 0;
    }
    return(RNet);
}

/*
 *  NetStartRead(bytes)
 * read (bytes) from the (serial) device.
 */

static long  SaveLen;

void
NetStartRead(len)
long len;
{
    if (len > sizeof(RBuf)){
        if(DDebug) 
	    printf("Warning: called NetstartRead for %d, buf only %d!\n",
	    	len, sizeof(RBuf));
	len = sizeof(RBuf);
    }

    if(DDebug>3) printf("Starting NetStartRead()\n");
    if (SerialIsOpen && RecvEnabled) {
	if (IosrIP){
	    if( DDebug ) printf("NetAbortRead()\n");
	    NetAbortRead();
	}
	RNet->IOSer.io_Data = (APTR)RBuf;
	SaveLen = RNet->IOSer.io_Length = len;
	SendIO((IOR *)RNet);   /* non blocking */
        IosrIP = 1;
	if(DDebug >2 ) printf("RNet sent\n");
    } else {
	SaveLen = len;
	RNet->IOSer.io_Actual = 0;
    }
}

/* return pointer to IOSER structure if it's ready to be read
** return NULL otherwise 
*/
IOSER *
NetReadReady()   
{
    if( IosrIP == 0) return NULL; /* no 'pending' request to be ready! */
    if( CheckIO( ((IOR *) RNet))){
	return( (IOSER *) RNet);
    } else {
	return( (IOSER *) NULL );
    }
}

NetReadReturned(pptr)
ubyte **pptr;
{
    long bytes = 0;

    IosrIP = 0;  /* Odd that this is cleared here, considering that
    		 ** it doesn't actually clear the io read request */

    bytes = RNet->IOSer.io_Actual;
    *pptr = RBuf;

    if ( PDebug && !OnLine ) {
	if (LastWasWrite) {
	    LastWasWrite = 0;
	    printf(" READ:");
	}
	HexDump(RBuf, bytes);
    }
    return(bytes);
}

void
NetWaitWrite()
{
    if (IoswIP) {
#ifdef TRAPNET
        ulong waitMask, returnMask, iOMask; 
	iOMask = 1<< Iosw.IOSer.io_Message.mn_ReplyPort->mp_SigBit;
        waitMask = iOMask |SIGBREAKF_CTRL_C;
Waitsomemore:
	returnMask = Wait(waitMask);
	if(returnMask & SIGBREAKF_CTRL_C ){
	     printf("^C caught in NetWaitWrite!\n");
	     /* goto Waitsomemore; */
	}
	if(returnMask & iOMask){
	    /* Have to deal with the replied messages on the port! */
	    /* this would ordinarily be done if we were using WaitIO() */
	    while( GetMsg( Iosw.IOSer.io_Message.mn_ReplyPort) != NULL);
	}
#else
        WaitIO( (IOR *) &Iosw );
#endif
	IoswIP = 0;
	fixsignal(Iosw.IOSer.io_Message.mn_ReplyPort); /* is this necessary? */
    }
}

static char SaveIosrIP;

void
SaveState()
{
    SaveIosrIP = IosrIP;
    if (IosrIP)
	NetAbortRead();
    NetWaitWrite();
}

void
RestoreState()
{
    if (SaveIosrIP)
	NetStartRead(SaveLen);
}

void
NetRecvOff()
{
    if (RecvEnabled) {
	SaveState();
	if(DDebug) printf("Recv Disabled. \n");
	RecvEnabled = 0;
    }
}

void
NetRecvOn()
{
    if (!RecvEnabled) {
	RecvEnabled = 1;
	if(DDebug) printf("Recv Re-Enabled. \n");
	RestoreState();
    }
}

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

void
NetSerialOn()
{
    if (!SerialIsOpen) {
	if (OpenDevice(DevName, UnitNum, (IOR *)RNet, 0))      /* OH HELL! */
	    return;

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

	SerialIsOpen = 1;
	Iosw.IOSer.io_Device = RNet->IOSer.io_Device;
	Iosw.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((IOR *)CNet);
	RestoreState();         /*  restore pending read req.       */
    }
    return;
}

