
/*
 *  GIO.C	WINDOWED G PROTOCOL
 *
 *  Rewritten from scratch, except the checksum routine which was
 *  rewritten but based on the old one.
 *
 *  WINDOW SIZE:    When changing the window size be sure there
 *		    are enough buffers available for pending txs.
 *
 *  GIO.C (c)Copyright 1989, Matthew Dillon, All Rights Reserved
 */

#include <stdio.h>
#include <stdlib.h>
#include <exec/types.h>
#include <proto/all.h>

void	FrameToPacket();

extern int debug;
extern int WindowOne;

#define SUCCESS 0
#define FAIL	1

#define MAGIC	    0125252
#define DLE	    0x10
#define WINDOWSIZE  2		/*  WindowSize = 1 or 2  */
#define SEGSIZEK    2		/*  64 bytes ??     */

#define SUB1(var)   (((var)-1)&7)

typedef unsigned char	ubyte;
typedef unsigned short	uword;

typedef struct {
    ubyte   Dle;	/*  Literal ASCII DLE				*/
    ubyte   K;		/*  data size 2^(K+4) except K=0 means no data  */
    ubyte   CL,CH;	/*  MAGIC - chksum(data) ^ C        */
    ubyte   C;
    ubyte   X;		/*  K ^ C0 ^ C1 ^ C		    */
} Frame;

typedef struct {
    ubyte   CType;	/*  bits 7-6 of C	*/
    ubyte   CCmd;	/*  bits 5-3 of C	*/
    ubyte   CSeq;	/*  bits 2-0 of C	*/
    ubyte   PLen;	/*  pre-data (2 bytes) for SHORTDATA type */
    ubyte   *Data;	/*  implies CType=LONGDATA or SHORTDATA */
    uword   DLen;	/*  length of data	*/
} Packet;

#define CT_CONTROL	(0 << 6)
#define CT_ALTCHN	(1 << 6)
#define CT_LONGDATA	(2 << 6)
#define CT_SHORTDATA	(3 << 6)
#define CT_MASK 	(3 << 6)

#define CC_CLOSE	(1 << 3)
#define CC_RJ		(2 << 3)
#define CC_SRJ		(3 << 3)
#define CC_RR		(4 << 3)
#define CC_INITC	(5 << 3)
#define CC_INITB	(6 << 3)
#define CC_INITA	(7 << 3)
#define CC_MASK 	(7 << 3)

#define SEQ_MASK	7

#define WAIT_ACK	1
#define WAIT_DATA	2
#define WAIT_CONTROL	3

#define MaxPktSize	4096

char	RxBuf[MaxPktSize+4];
char	TxBuf[4][MaxPktSize+4];

				/*  TRANSMIT STAGE	    */
char	TxSeq = 0;		/*  Last send packet	    */
char	TxPend= 0;		/*  hasn't been acked yet   */
uword	TxWinSize;		/*  1 or 2		    */
uword	TxSegSize;		/*  maximum segment size    */
char	TxSegK; 		/*  K for TxSegSize	    */
Packet	TxPacket[8];		/*  contents of last packet */

				/*  RECEIVE STAGE	    */
char	RxSeq = 1;		/*  Next expected recv pkt  */
char	RxRdy = 0;		/*  Has come in..	    */
char	RxRetry = 4;
char	RxNotAcked = 0; 	/*  We have yet to ack it   */
Packet	RxPacket;		/*  The packet that has come in */

/*
 *  Get Transmit Buffer.  Note that some space to the left is available
 */

char *
GetTxBuf()
{
    static int index = 0;

    index = (index + 1) & 3;
    return(TxBuf[index] + 2);
}

int
gwrdata(fi)
FILE *fi;
{
    int bytes;
    char *buf = GetTxBuf();

    if (debug > 7)
	printf("GWRDATA: ");

    while ((bytes = fread(buf, 1, TxSegSize, fi)) >= 0) {
	if (SendDataPacket(buf, bytes) != SUCCESS) {
	    if (debug > 7)
		printf("GWR Failed\n");
	    return(FAIL);
	}
	if (debug > 7)
	    printf("\nGWROK ");
	if (bytes == 0)
	    break;
	buf = GetTxBuf();
    }
    if (debug > 7)
	printf("\nGWFile Last Ack\n");
    while (TxPend) {
	if (GetData(WAIT_ACK, NULL) == FAIL)
	    return(FAIL);
    }
    return (SUCCESS);
}

/*
 *  Write message to the other guy.
 *
 *  NOTE: LONGDATA packets used exclusively and \0 fill to end.
 */

int
gwrmsg(str)
char *str;
{
    int len = strlen(str) + 1;      /*  type + str + \0  */

    if (debug > 8)
	printf("GWRMSG: %s\n", str);

    while (len > TxSegSize) {
	char *buf = GetTxBuf();
	movmem(str, buf, TxSegSize);
	if (debug > 8)
	    printf("GWR-SEND %d\n", TxSegSize);
	if (SendDataPacket(buf, TxSegSize) != SUCCESS)
	    return(FAIL);
	len -= TxSegSize;
	str += TxSegSize;
    }

    /*
     *	Find optimal packet size (remember, we must force LONGDATA
     *
     *	Apparently packet sizes less than the agreed upon size are
     *	not allowed ???
     */

    {
	short siz = TxSegSize;
	char *buf = GetTxBuf();
#ifdef NOTDEF
	short k = TxSegK;

	while (k > 1 && (siz >> 1) >= len) {
	    --k;
	    siz >>= 1;
	}
#endif
	if (debug > 8)
	    printf("GWR-FNL %d %d\n", len, siz);

	movmem(str, buf, len);
	setmem(buf + len, siz - len, 0);
	if (SendDataPacket(buf, siz) != SUCCESS) {
	    if (debug > 8)
		printf("GWR-FAIL\n");
	    return(FAIL);
	}
    }
    if (debug > 8)
	printf("GWR Last Ack\n");
    while (TxPend) {
	if (GetData(WAIT_ACK, NULL) == FAIL)
	    return(FAIL);
    }
    return (SUCCESS);
}

int
grddata(fi)
FILE *fi;
{
    int bytes;
    char *buf;

    if (debug > 7)
	printf("GRDDATA\n");
    while ((bytes = RecvDataPacket(&buf)) > 0) {
	if (debug > 7)
	    printf("GRDDATA blk %d\n", bytes);
	fwrite(buf, 1, bytes, fi);
    }
    if (debug > 7)
	printf("GRDDATA end %d\n", bytes);
    if (bytes < 0)
	return(FAIL);
    else
	return(SUCCESS);
}

grdmsg(buf, maxlen)
char *buf;
{
    short i;
    short n;
    short slen;
    char *ptr;

    i = 0;
    if (debug > 8)
	printf("GRDMSG\n");
    while ((n = RecvDataPacket(&ptr)) > 0) {
	ptr[n] = 0;
	slen = strlen(ptr);
	if (slen > maxlen - 1) {
	    printf("GRDMSG: Buffer overflow!\n");
	    return (FAIL);
	}
	movmem(ptr, buf + i, slen);
	buf[i + slen] = 0;
	if (slen != n)
	    break;
	i += slen;
	maxlen -= slen;
    }
    if (debug > 8)
	printf("GRDMSGEND %d (%d) %s\n", n, i, buf);
    if (n < 0) {
	buf[0] = 0;
	return(FAIL);
    }
    return(SUCCESS);
}

SendDataPacket(buf, bytes)
ubyte *buf;
int bytes;
{
    Packet  P;

    /*
     *	If window exhausted we must wait for at least one ack.
     */

    if (TxPend == TxWinSize) {
	if (GetData(WAIT_ACK, NULL) == FAIL)
	    return(FAIL);
    }

    TxSeq = (TxSeq + 1) & 7;        /*  next Tx packet  */

    /*
     *	Figure out best fit packet size.  Apparently packets smaller
     *	then the agreed upon size are not allowed ???
     */

#ifdef NOTDEF
    {
	short k = TxSegK;
	P.DLen = TxSegSize;
	while (k > 1 && P.DLen && (P.DLen >> 1) >= bytes) {
	    --k;
	    P.DLen >>= 1;
	}
    }
#else
    P.DLen = TxSegSize;
#endif

    if (bytes < P.DLen) {
	uword extra = P.DLen - bytes;
	setmem(buf + bytes, extra, 0);
	if (extra <= 127) {
	    P.PLen = 1;
	    buf[-1] = extra;
	} else {
	    P.PLen = 2;
	    buf[-2] = 0x80 | extra;
	    buf[-1] = (extra >> 7);
	}
	P.CType = CT_SHORTDATA;
    } else {
	P.PLen = 0;
	P.CType = CT_LONGDATA;
    }
    P.CCmd = TxSeq << 3;	/*  transmit sequence number */
    P.CSeq = (RxSeq - 1) & 7;   /*  last valid received pkt  */
    P.Data = buf;

    if (debug > 8)
	printf("WRITE PACKET %d %d\n", TxSeq, P.CSeq);

    if (RxNotAcked)             /*  We've acked the rx packet */
	RxNotAcked = 0;

    TxPacket[TxSeq & 7] = P;
    ++TxPend;

    WritePacket(&TxPacket[TxSeq & 7], 1);

    return(SUCCESS);
}

RecvDataPacket(pbuf)
char **pbuf;
{
    if (RxRdy == 0) {
	if (GetData(WAIT_DATA, NULL) != SUCCESS)
	    return(-1);
    }
    *pbuf = RxPacket.Data;
    RxRdy = 0;
    return((int)RxPacket.DLen);
}

gturnon(master)
{
    Packet P;
    short retry = 5;
    short windowsize = WINDOWSIZE;	/*  our prefered window size */
    short segsize = SEGSIZEK;		/*  our prefered segment size */

    if (WindowOne)
	windowsize = 1;

    if (master) {
	while (retry > 0) {
	    WriteCtlPacket(CC_INITA | windowsize);
	    if (GetData(WAIT_CONTROL, &P) == SUCCESS && P.CCmd == CC_INITA) {
		if (P.CSeq && P.CSeq < windowsize)
		    windowsize = P.CSeq;
		break;
	    }
	    --retry;
	}
	while (retry > 0) {
	    WriteCtlPacket(CC_INITB | (segsize - 1));
	    if (GetData(WAIT_CONTROL, &P) == SUCCESS && P.CCmd == CC_INITB) {
		if (P.CSeq < segsize - 1)
		    segsize = P.CSeq + 1;
		break;
	    }
	    --retry;
	}
	while (retry > 0) {
	    WriteCtlPacket(CC_INITC | windowsize);
	    if (GetData(WAIT_CONTROL, &P) == SUCCESS && P.CCmd == CC_INITC) {
		if (P.CSeq && P.CSeq < windowsize)
		    windowsize = P.CSeq;
		break;
	    }
	    --retry;
	}
    } else {
	while (retry > 0) {
	    if (GetData(WAIT_CONTROL, &P) == SUCCESS && P.CCmd == CC_INITA) {
		WriteCtlPacket(CC_INITA | windowsize);
		if (P.CSeq && windowsize > P.CSeq)
		    windowsize = P.CSeq;
		break;
	    }
	    --retry;
	}
	while (retry > 0) {
	    if (GetData(WAIT_CONTROL, &P) == SUCCESS && P.CCmd == CC_INITB) {
		WriteCtlPacket(CC_INITB | (segsize - 1));
		if (P.CSeq < segsize - 1)
		    segsize = P.CSeq + 1;
		break;
	    }
	    --retry;
	}
	while (retry > 0) {
	    if (GetData(WAIT_CONTROL, &P) == SUCCESS && P.CCmd == CC_INITC) {
		WriteCtlPacket(CC_INITC | windowsize);
		if (P.CSeq && windowsize > P.CSeq)
		    windowsize = P.CSeq;
		break;
	    }
	    --retry;
	}
    }
    TxSegK = segsize;
    TxSegSize = 1 << (TxSegK + 4);
    TxWinSize = windowsize;
    if (debug > 0)
	printf("Window Size is %d\n", TxWinSize);
    if (retry == 0)
	return(FAIL);
    return(SUCCESS);
}

gturnoff()
{
    Packet P;

    WriteCtlPacket(CC_CLOSE);
    if (GetData(WAIT_CONTROL, &P) == SUCCESS && P.CCmd == CC_CLOSE) {
	WriteCtlPacket(CC_RR | ((RxSeq - 1) & 7));
	return (SUCCESS);
    }
    return (FAIL);
}

GetData(waitfor, p)
Packet *p;
{
    Packet P;
    short timeout;

    /*
     *	If we haven't acked the last packet we received we must do
     *	so now before we can expect to receive another one!
     */

    if (waitfor == WAIT_ACK)
	timeout = 20;
    else if (waitfor == WAIT_DATA)
	timeout = 40;
    else
	timeout = 40;

    for (;;) {
	if (RxNotAcked) {
	    WriteCtlPacket(CC_RR | SUB1(RxSeq));
	    RxNotAcked = 0;
	}
	if (ReadPacket(&P, timeout) == FAIL) {
	    if (waitfor == WAIT_DATA) {
		if (RxRetry) {
		    --RxRetry;
		    if (debug > 8)
			printf("\nWAIT-DATA, TIMEOUT, SEND RJ\n");
		    WriteCtlPacket(CC_RJ | SUB1(RxSeq));
		    continue;
		}
	    }
	    if (waitfor == WAIT_ACK) {
		if (RxRetry) {
		    --RxRetry;
		    if (debug > 8)
			printf("\nWAIT-ACK, TIMEOUT, RETRY\n");
		    WritePacket(&TxPacket[(TxSeq - TxPend + 1) & 7], 1);
		    continue;
		}
	    }
	    if (debug > 8)
		printf("COMPLETE FAILURE RxRetry = %d\n", RxRetry);
	    return(FAIL);
	}
	RxRetry = 4;
	switch(P.CType) {
	case CT_CONTROL:
	    switch(P.CCmd) {
	    case CC_CLOSE:  /*	End of communication ... not an ACK!	   */
		if (waitfor == WAIT_CONTROL) {
		    *p = P;
		    return(SUCCESS);
		}
		return(FAIL);
	    case CC_RJ:     /*	Reject packet (P.CSeq == last good packet) */
			    /*	resend last packet  */

		WritePacket(&TxPacket[(P.CSeq + 1) & 7], 1);
		{
		    short i;
		    for (i = 0; i < TxPend; ++i) {
			if (P.CSeq == ((TxSeq - i) & 7))
			    break;
		    }
		    if (i == TxPend)
			printf("RJ, bad seq no: %d expected %d-%d\n", P.CSeq, (TxSeq - TxPend + 1) & 7, TxSeq);
		}
		break;
	    case CC_SRJ:    /*	Selective Reject (P.CSeq == bad packet # ) */
		return(FAIL);
	    case CC_RR:     /*	Ack to packet  (P.CSeq == packet # acked)   */
		if (RecvdAck(P.CSeq) == SUCCESS && waitfor == WAIT_ACK)
		    return(SUCCESS);
		break;
	    case CC_INITC:
	    case CC_INITB:
	    case CC_INITA:
		if (waitfor == WAIT_CONTROL) {
		    *p = P;
		    return(SUCCESS);
		}
		return(FAIL);
	    }
	    break;
	case CT_ALTCHN:
	    printf("ALTCHN packet ??\n");
	    break;
	case CT_LONGDATA:
	case CT_SHORTDATA:
	    {
		char rxseq = P.CCmd >> 3;
		char txack = P.CSeq;

		if (RxRdy == 1) {
		    printf("Got two receive packets without me acking!\n");
		}
		if (rxseq == SUB1(RxSeq)) {
		    RxNotAcked = 1;
		    continue;	    /*	ignore resent pkts we already have */
		} else if (rxseq != RxSeq) {
		    if (rxseq == ((RxSeq + 1) & 7))     /*  missed one (windowed)   */
			WriteCtlPacket(CC_RJ | SUB1(RxSeq));
		    printf("Received sequence %d, expected %d\n", rxseq, RxSeq);
		    break;
		} else {
		    if (debug > 6)
			printf("RECV SEQUENCE %d\n", rxseq);
		}

		RecvdAck(txack);

		/*
		 *  Delay sending the ACK back in case we can combine
		 *  it with the next transmitted packet.
		 */

		RxNotAcked = 1; 	/*  we haven't ack'd the rx packet */

		RxSeq = (RxSeq + 1) & 7;
		RxRdy = 1;
		RxPacket = P;
		if (waitfor == WAIT_DATA)
		    return(SUCCESS);
		if (TxPend == 0 && waitfor == WAIT_ACK)
		    return(SUCCESS);
	    }
	    break;
	}
    }
}

RecvdAck(seq)
char seq;
{
    short i;
    short seqbase = TxSeq - TxPend + 1;

	/*
	 *  which packet was acked?
	 */

    for (i = 0; i < TxPend; ++i) {
	if (seq == ((seqbase + i) & 7))
	    break;
    }
    if (i == TxPend) {
	if (TxPend)
	    printf("He acked the wrong packet! %d expected %d\n", seq, TxSeq);
	return(FAIL);
    }
    if (debug > 8)
	printf("TxPend %d ->", TxPend);
    TxPend = TxPend - i - 1;
    if (debug > 8)
	printf(" %d\n", TxPend);
    return(SUCCESS);
}

WriteCtlPacket(cc)
{
    Packet pk;

    pk.CType = CT_CONTROL;
    pk.CCmd  = cc & CC_MASK;
    pk.CSeq  = cc & SEQ_MASK;
    pk.PLen  = 0;
    pk.DLen  = 0;
    WritePacket(&pk, 0);
    return(SUCCESS);
}

WritePacket(pk, async)
Packet *pk;
{
    Frame   F;
    uword   sum;

    F.Dle = DLE;
    F.C   = pk->CType | pk->CCmd | pk->CSeq;
    F.K   = 9;

    if (pk->CType == CT_SHORTDATA || pk->CType == CT_LONGDATA)
	F.K = LenToK(pk->DLen);
    else
	pk->DLen = 0;

    sum = MAGIC - (CheckSum(pk->Data - pk->PLen, pk->DLen) ^ F.C);

    F.CH  = sum >> 8;
    F.CL  = sum;
    F.X   = F.K ^ F.CH ^ F.CL ^ F.C;

    if (debug > 8)
	printf("WritePacket: F.K = %d F.C = %02x\n", F.K, F.C);

    if (async)
	xwritea(&F, sizeof(F));
    else
	xwrite(&F, sizeof(F));      /*  write header    */
    if (pk->DLen) {             /*  write data      */
	if (async)
	    xwritea(pk->Data - pk->PLen, pk->DLen);
	else
	    xwrite(pk->Data - pk->PLen, pk->DLen);
    }
    return(SUCCESS);
}

ReadPacket(pk, timeout)
Packet *pk;
{
    Frame   F;
    short   c;
    short   i = 0;

    pk->Data = RxBuf;
    pk->CType = 0xFF;
    pk->CCmd  = 0;
    pk->CSeq  = 0;

    if (debug > 8)
	printf("ReadPacket\n");
    while ((c = xgetc(timeout)) != EOF) {
	if (debug > 8)
	    printf("RP %d %02x\n", i, c);

	switch(i) {
	case 0:
	    if (c == DLE) {
		F.Dle = c;
		++i;
	    }
	    break;
	case 1:
	    F.K = c;
	    ++i;
	    if (c == DLE) { /*  K only valid 0-9    */
		F.Dle = c;
		i = 1;
	    }
	    break;
	case 2:
	    F.CL = c;
	    ++i;
	    break;
	case 3:
	    F.CH = c;
	    ++i;
	    break;
	case 4:
	    F.C = c;
	    ++i;
	    break;
	case 5:
	    F.X = c;
	    if (F.X != (F.K ^ F.CH ^ F.CL ^ F.C)) {
		printf("F.X failed: %02x %02x\n", F.X, (F.K ^ F.CH ^ F.CL ^ F.C));
		i = 0;
	    } else {	    /*	get data segment if any */
		++i;
	    }
	    break;
	}
	if (i == 6)
	    break;
    }
    if (debug > 8)
	printf("RP Hdr i = %d, F.K = %d F.C = %02x\n", i, F.K, F.C);

    if (i == 6) {       /*  Receive Data Portion    */
	uword pktsize = 1 << (F.K + 4);
	uword n;

	if (F.K == 0 || F.K == 9)
	    pktsize = 0;	    /*	FIXME is K=0 an illegal case? */

	if (pktsize > MaxPktSize) {
	    printf("Protocol failure pktsize %d/%d/%d\n", pktsize, TxSegSize, MaxPktSize);
	    return (FAIL);
	}
	for (n = 0; n < pktsize; ++n) {
	    if ((c = xgetc(4)) == EOF)
		break;
	    pk->Data[n] = c;
	}
	if (c != EOF) {
	    uword hissum;
	    uword oursum;
	    hissum = (F.CH << 8) | F.CL;
	    oursum = MAGIC - (CheckSum(pk->Data, pktsize) ^ F.C);
	    if (debug > 8)
		printf("Got Data, checking: %04x %04x\n", hissum, oursum);
	    if (hissum == oursum) {
		FrameToPacket(&F, pk, pk->Data);
		return (SUCCESS);
	    }
	} else {
	    FrameToPacket(&F, pk, pk->Data);    /* mainly for pk->CType */
	    return (FAIL);
	}
    }
    /*
     *	Timeout, retry?
     */
    return (FAIL);
}

CheckSum(s, n)
ubyte *s;
int n;
{
    uword sum;
    uword x;
    uword t;

    if (n == 0)
	return(0);
    sum = -1;
    x = 0;

    while (n) {
	if (sum & 0x8000)
	    sum = (sum << 1) | 1;
	else
	    sum = (sum << 1);

	t = sum;
	sum += *s++;
	x += sum ^ n;
	if (sum <= t)
	    sum ^= x;
	--n;
    }
    return((int)sum);
}

void
FrameToPacket(fr, pk, data)
Frame *fr;
Packet *pk;
ubyte *data;
{
    pk->CType = fr->C & CT_MASK;
    pk->CCmd  = fr->C & CC_MASK;
    pk->CSeq  = fr->C & SEQ_MASK;
    switch(pk->CType) {
    case CT_LONGDATA:
	pk->DLen = 1 << (fr->K + 4);
	break;
    case CT_SHORTDATA:
	pk->DLen = 1 << (fr->K + 4);
	if (pk->Data[0] & 0x80) {
	    pk->DLen -= (pk->Data[0] & 0x7F) | (pk->Data[1] << 7);
	    if (pk->DLen < 0) {
		printf("DLEN ERROR %d\n", pk->DLen);
		pk->DLen = 0;
	    }
	    pk->Data += 2;
	} else {
	    pk->DLen -= *pk->Data;
	    ++pk->Data;
	}
	break;
    default:
	pk->DLen = 0;
	break;
    }
}

LenToK(bytes)
uword bytes;
{
    uword n = 32;
    uword k = 1;

    while (n < bytes) {
	n <<= 1;
	++k;
    }
    if (k > 8) {
	printf("Soft error, LenToK: %d %d %d\n", bytes, n, k);
	k = 8;
    }
    return((int)k);
}

