
/*
 *  GIO.C	WINDOWED G PROTOCOL
 *
 *  written 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
 *  extended to window size of 7 by Jack J. Rouse
 */

#include "includes.h"


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;

Prototype int gwrdata(FILE *);
Prototype int gwrmsg(char *);
Prototype int grddata(FILE *);
Prototype int grdmsg(char *, int);
Prototype int gturnon(int);
Prototype int gturnoff(void);
Prototype void ResetGIO(void);

Prototype short ReducedTimeout;

short ReducedTimeout;

Local	  char *GetTxBuf(void);
Local	  int SendDataPacket(unsigned char *, int);
Local	  int RecvDataPacket(char **);
Local	  int GetData(int, Packet *);
Local	  int RecvdAck(char);
Local	  int WriteCtlPacket(int);
Local	  int WritePacket(Packet *, int);
Local	  int ReadPacket(Packet *, int);
Local	  int CheckSum(unsigned char *, int);
Local	  void FrameToPacket(Frame *, Packet *, unsigned char *);
Local	  int LenToK(unsigned short);

extern int debug;
extern int WindowSize;

#define SUCCESS 0
#define FAIL	1

#define MAGIC	    0125252
#define DLE	    0x10
#define WINDOWSIZE  7
#define SEGSIZEK    2		/*  64 bytes ??     */

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


#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[8];

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

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

void
ResetGIO()
{
    TxSeq = 0;
    TxPend= 0;
    TxWinSize = 0;
    TxSegSize = 0;
    TxSegK = 0;
    RxSeq = 0;
    RxRdy = 0;
    RxRetry = 8;
    RxNotAcked = 0;
}

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

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

    if (++index > TxWinSize)
	index = 0;
    if (TxBuf[index] == NULL
    &&	(TxBuf[index] = (char *)malloc(MaxPktSize+4)) == NULL)
    {
	printf("Out of memory in GetTxBuf.");
	/* could handle this case better, but it should be rare */
	xexit(1);
    }
    return(TxBuf[index] + 2);
}

int
gwrdata(fi)
FILE *fi;
{
    int bytes;

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

    for (;;) {
	char *buf = GetTxBuf();
	if ((bytes = fread(buf, 1, TxSegSize, fi)) <= 0)
	    break;
	if (SendDataPacket(buf, bytes) != SUCCESS) {
	    if (debug > 7)
		printf("GWR Failed\n");
	    return(FAIL);
	}
	if (debug > 7)
	    printf("\nGWROK ");
    }
    {
	char *buf = GetTxBuf();
	if (SendDataPacket(buf, 0) != SUCCESS) {
	    if (debug > 7)
		printf("GWR Failed (last)\n");
	    return(FAIL);
	}
    }
    if (debug > 7)
	printf("\nGWFile Last Ack\n");
    while (TxPend) {
	if (GetData(WAIT_ACK, NULL) == FAIL)
	    return(FAIL);
    }
    if (debug > 7)
	printf("success\n");
    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 > 7)
	printf("GWRMSG: %s\n", str);

    while (len > TxSegSize) {
	char *buf = GetTxBuf();
	movmem(str, buf, TxSegSize);
	if (debug > 7)
	    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 > 7)
	    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 > 7)
		printf("GWR-FAIL\n");
	    return(FAIL);
	}
    }
    if (debug > 7)
	printf("GWR Last Ack\n");
    while (TxPend) {
	if (GetData(WAIT_ACK, NULL) == FAIL)
	    return(FAIL);
    }
    if (debug > 7)
	printf("success\n");
    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);
}

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

    i = 0;
    if (debug > 7)
	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 > 7)
	printf("GRDMSGEND %d (%d) %s\n", n, i, buf);
    if (n < 0) {
	buf[0] = 0;
	return(FAIL);
    }
    return(SUCCESS);
}

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

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

    while (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;		/*  last valid received pkt  */
    P.Data = buf;

    if (debug > 7)
	printf("WRITE DATA PACKET txseq=%d rxack=%d\n", TxSeq, P.CSeq);

#ifdef NOTDEF
    RxNotAcked = 0;		/*  We've acked the rx packet */
#else
    if (RxNotAcked) {           /*  always send CC_RR packets */
	WriteCtlPacket(CC_RR | RxSeq);
	RxNotAcked = 0;
    }
#endif

    TxPacket[TxSeq] = P;
    ++TxPend;

    WritePacket(&TxPacket[TxSeq], 1);

    return(SUCCESS);
}

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

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

    if (WindowSize < windowsize)        /*  command line override       */
	windowsize = WindowSize;

    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);
}

int
gturnoff()
{
    Packet P;

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

/*
 *  GetData()
 *
 *  This is the core of the packet protocol.  As soon as we get a satisfactory
 *  terminating condition we return.  However, on unsatisfactory conditions
 *  such as having to send a reject, we attempt to drain any pending data
 *  (i.e. multiple returned rejects) so we do not do multiple resends.
 *
 *  The resend flag only applies on a failure return.
 */

Local
int
GetData(waitfor, p)
int waitfor;
Packet *p;
{
    Packet P;
    short timeout;
    short drainmode = 0;

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

    if (debug > 7)
	printf("Get-Data waitfor %s\n", (waitfor == WAIT_ACK) ? "WAIT_ACK" : "WAIT_DATA");
    if (waitfor == WAIT_ACK)
	timeout = (PacketTimeout) ? PacketTimeout : 5;
    else if (waitfor == WAIT_DATA)
	timeout = (PacketTimeout) ? PacketTimeout * 4 : 20;
    else
	timeout = (PacketTimeout) ? PacketTimeout * 4 : 20;

    for (;;) {
	/*
	 *  due to confusion on connection termination, we do not wait
	 *  around trying to retry the final termination acknowlege.
	 */

	if (ReducedTimeout) {
	    if (RxRetry > 2)
		RxRetry = 2;
	    if (timeout > 5)
		timeout = 5;
	}
	if (RxNotAcked) {                   /*  good, send ack for rx'd pkt */
	    WriteCtlPacket(CC_RR | RxSeq);
	    RxNotAcked = 0;
	}				    /*	bad, timeout on read	    */
	if (ReadPacket(&P, (drainmode) ? 0 : timeout) == FAIL) {
	    drainmode = 0;

	    /*
	     *	If we have timed out waiting for data then send a reject
	     */

	    if (waitfor == WAIT_DATA) {
		if (RxRetry) {
		    --RxRetry;
		    if (debug > 7)
			printf("\nWAIT-DATA, TIMEOUT, SEND RJ\n");
		    WriteCtlPacket(CC_RJ | RxSeq);
		    continue;
		}
	    }

	    /*
	     *	If we have timed out waiting for an ack send the entire
	     *	window.  We must send the window sometime and it might as
	     *	well be now.
	     */

	    if (waitfor == WAIT_ACK) {
		if (RxRetry) {
		    int i;

		    if (debug > 7)
			printf("RESEND %d packets\n", TxPend);
		    for (i = 1; i <= TxPend; ++i) {
			Packet *p = &TxPacket[(TxSeq - TxPend + i) & 7];
			p->CSeq = RxSeq;
			WritePacket(p, 1);
		    }
		    --RxRetry;
		    continue;
		}
	    }
	    if (debug > 7)
		printf("COMPLETE FAILURE RxRetry = %d\n", RxRetry);
	    return(FAIL);
	}

	/*
	 *  valid packet, terminating condition?
	 */

	RxRetry = 8;
	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) */
		(void)RecvdAck(P.CSeq);
		drainmode = 1;
		break;
	    case CC_SRJ:    /*	Selective Reject (P.CSeq == bad packet # ) */
		return(FAIL);
	    case CC_RR:     /*	Ack to packet  (P.CSeq == packet # acked)   */
#ifdef NOTDEF
		if (P.CSeq == ((TxSeq - TxPend) & 7)) {
		    /*
		     * The CSeq packet was previously acknowledged
		     * The receiver apparently has not seen anything since.
		     * This is the same as a rejection.
		     */
		    goto resend;
		}
#endif
		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 == RxSeq) {   /*  already got this packet */
		    RxNotAcked = 1;
		    continue;
					/*  expected packet?	    */
		} else if (rxseq != ((RxSeq + 1) & 7)) {
		    drainmode = 1;
		    printf("Received sequence %d, expected %d\n", rxseq, (RxSeq + 1) & 7);
		    break;
		}
		if (debug > 6)
		    printf("RECV SEQUENCE %d\n", rxseq);

		RecvdAck(txack);    /*  woa */

		/*
		 *  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;
	}
    }
}

Local
int
RecvdAck(seq)
char seq;
{
    short i;

    /*
     *	which packet was acked?
     */

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

Local
int
WriteCtlPacket(cc)
int 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);
}

Local
int
WritePacket(pk, async)
Packet *pk;
int async;
{
    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 > 7)
	printf("WritePacket: F.K = %d F.C = %d.%d.%d\n", F.K, F.C >> 6, (F.C >> 3) & 7, F.C & 7);

    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);
}

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

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

    if (debug > 7)
	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;
		if (timeout == 0)
		    timeout = 1;
	    }
	    break;
	case 1:
	    F.K = c;
	    ++i;
	    if (c == DLE) { /*  K only valid 1-9    */
		F.Dle = c;
		i = 1;
	    }
	    else if (c == 0 || c > 9)
		i = 0;
	    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 > 7) {
	if (i)
	    printf("RP Hdr i = %d, F.K = %d F.C = %d.%d.%d\n", i, F.K, F.C >> 6, (F.C >> 3) & 7, F.C & 7);
	else
	    printf("RP Hdr <rx timeout>\n");
    }

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

	if (F.K == 9)
	    pktsize = 0;

	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 > 7)
		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);
}

Local
int
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);
}

Local
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;
    }
}

Local
int
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);
}

