
/*
 *  PACKET.C
 */

#include "dnet.h"

void BuildPacket();

#define Ascize(foo) (((foo) & 0x3F) | 0x40)
/* strip low bits and make sure < 0x40 */

ubyte	RxTmp[MAXPACKET];

void
BuildDataPacket(pkt, win, dbuf, actlen)
PKT	*pkt;	/* pointer to start of packet 	*/
ubyte	win;	/* 'channel' packet goes on?  	*/
ubyte	*dbuf;	/* pointer to data buffer 	*/
uword	actlen;	/* length of data buffer 	*/
{
    ubyte *ptr;
    ubyte *pend;
    ubyte range = 0;

    /* 'bad characters' which should be translated out */
    static ubyte BadCtl[32] = { 
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1
    };

    ptr = dbuf;			/* point to start of data buffer */
    pend= dbuf + actlen;	/* point to end of data buffer */

    if(Fast8){
	/* an 8 bit line and we don't care a bit about translating
	** out 'bad' characters */
	BuildPacket(pkt, PKCMD_WRITE, win, dbuf, actlen, actlen);
	return;
    }

    /* scan data, set flags if its got characters < 0x20 (Ascii 32) or
    ** got characters > 0x80 (Ascii 128)
    ** these bits will affect how the data has to be 'altered to go over the'
    ** stream
    */
    while (ptr < pend && range < 3 ) { 
	if (*ptr < 0x20 && BadCtl[*ptr] )
	    range |= 1;
	if (*ptr >= 0x80)
	    range |= 2;
	++ptr;
    }

    if(DDebug){
    	printf("BuildDataPkt: win %d len %d(%d): ", win, dbuf, range);
	HexDump( dbuf, actlen);
    }

    if (Mode7 && range) {
	/* have to translate full 8 bit binary data into a 6 bit space */
	uword buflen = Expand6(dbuf, pkt->data, actlen);
	BuildPacket(pkt, PKCMD_WRITE6, win, pkt->data, actlen, buflen);
    } else {
	if (Mode7 || (range & 2)) {
	    /*   Data needs no translation:
	    ** either all ascii (in 7 bit mode )
	    ** or uses 8th bit and we're in 8 bit mode */
	    BuildPacket(pkt, PKCMD_WRITE, win, dbuf, actlen, actlen);
	} else {
	    /* compress all ascii data to make use of the 8th bit and hence
	    ** make the data packet smaller! */
	    uword buflen = Compress7(dbuf, pkt->data, actlen);
	    BuildPacket(pkt, PKCMD_WRITE7, win, pkt->data, actlen, buflen);
	}
    }
}

PKT *
BuildRestartAckPacket(dbuf, bytes)
ubyte *dbuf;
ubyte bytes;
{
    static PKT pkt;
    BuildPacket(&pkt, PKCMD_ACKRSTART, 0, dbuf, bytes, bytes);
    return(&pkt);
}

void
BuildPacket(pkt, ctl, win, dbuf, actlen, buflen)
PKT	*pkt; 	/* pointer to packet structure */
ubyte	ctl;	/* type of packet that this is */
ubyte	*dbuf;	/* pointer to data packet should carry */
uword	actlen; /* pointer to actual data length */
uword	buflen; /* pointer to number of characters in data after encoding for
		** data (serial) stream bitwidth */
{
    pkt->buflen = buflen;
    pkt->sync = SYNC;
    pkt->ctl = ctl | win;
    pkt->cchk = Ascize((pkt->sync << 1) ^ pkt->ctl);
    if (actlen) {
	uword chk = chkbuf(dbuf, buflen);
	ubyte dchkh = chk >> 8;
	ubyte dchkl = chk;

	pkt->lenh = Ascize(actlen >> 6);
	pkt->lenl = Ascize(actlen);
	pkt->dchkh= Ascize(dchkh);
	pkt->dchkl= Ascize(dchkl);
	if (dbuf != pkt->data)
	    BMov(dbuf, pkt->data, buflen);
    }
}

void
WritePacket(pkt)
PKT *pkt;
{
    if (DDebug)
	printf("SEND-PKT: %02x %d\n", pkt->ctl, pkt->buflen);

    switch(pkt->ctl & PKF_MASK) {
    case PKCMD_WRITE:
	++Packets8Out;
	break;
    case PKCMD_WRITE6:
	++Packets6Out;
	break;
    case PKCMD_WRITE7:
	++Packets7Out;
	break;
    }

    if(PDebug && OnLine){
	if(pkt->buflen)
	    PacketDump( pkt->ctl, pkt->data, pkt->buflen);
	else 
	    PacketDump( pkt->ctl, 0, 0);
    }

    if (pkt->buflen)
	NetWrite(&pkt->sync, 7 + pkt->buflen);
    else
	NetWrite(&pkt->sync, 3);
}

void
WriteCtlPacket(ctl, win)
{
    static CTLPKT pkt;

    NetWrite(NULL, 0);
    BuildPacket(&pkt, ctl, win, NULL, 0, 0);
    WritePacket(&pkt);
}

void
WriteNak(win)
{
    WriteCtlPacket(PKCMD_NAK, win);
}

void
WriteAck(win)
{
    WriteCtlPacket(PKCMD_ACK, win);
}

void
WriteChk(win)
{
    WriteCtlPacket(PKCMD_CHECK, win);
}

void
WriteRestart()
{
    WriteCtlPacket(PKCMD_RESTART, 0);
}


/*
 *	RECEIVE AND PROCESS DATA
 *
 * return number of characters needed for next packet
 * if data sent doesn't complete the last one 
 */
int
RecvPacket(ptr, len)
ubyte *ptr; 	/* actual character data to translate */
long len; 	/* number of characters to 'read' */
{
    static uword ActLen;    /*	actual # bytes after decoding */
    static uword BufLen;    /*	length of input data buffer   */
    static uword DBLen;     /*	# bytes already in i.d.buf    */
    static ubyte RState;    /* state of reception: how many characters into
    			    ** a packet the read has recognized */
    static PKT	 Pkt;	    /* store current packet being examined in this */
    ubyte  *dptr;
    short expect;

    if (ptr == NULL) {
	RState = 0;
	return(3);
    }

    /* scan through the data for packets, process them */
    while (len) {
	switch(RState) {
	case 0:
	    --len;
	    Pkt.sync = *ptr++;

	    if (Pkt.sync == SYNC){  /* start of packet marked by SYNC char */
		++RState;
	    } /* else { printf"Junk character:%c ?\n", *(ptr-1) );} */
	    break;
	case 1: 	/*  CTL */
	    --len;
	    Pkt.ctl = *ptr++;

	    /* make sure it's a legal packet control type */
	    if (Pkt.ctl < 0x20 || Pkt.ctl > 0x7F) {
		RState = 0;
		break;
	    }
	    ++RState;
	    break;
	case 2: 	/*  CCHK    */
	    --len;
	    Pkt.cchk = *ptr++;

	    /* check character to make sure the SYNC was proper */
	    if (Ascize((SYNC<<1) ^ Pkt.ctl) != Pkt.cchk) {
		RState = 0;
		break;
	    }

	    switch(Pkt.ctl & PKF_MASK) {
	      case PKCMD_ACKRSTART:
	      case PKCMD_WRITE:
	      case PKCMD_WRITE6:
	      case PKCMD_WRITE7:
		++RState;
		break;
	      /* 3 byte packets, process them and start next packet */
	      default:
		do_cmd(Pkt.ctl, NULL, 0);
		RState = 0;
		break;
	    }
	    break;
	case 3: 	/*  LENH    */
	    --len;
	    Pkt.lenh = *ptr++;

	    ++RState;
	    if (Pkt.lenh < 0x20 || Pkt.lenh > 0x7F)
		RState = 0;
	    break;
	case 4: 	/*  LENL    */
	    --len;
	    Pkt.lenl = *ptr++;

	    if (Pkt.lenl < 0x20 || Pkt.lenl > 0x7F) {
		RState = 0;
		break;
	    }

	    ++RState;

	    ActLen = ((Pkt.lenh & 0x3F) << 6) | (Pkt.lenl & 0x3F);
	    DBLen = 0;

	    switch(Pkt.ctl & PKF_MASK) {
	      case PKCMD_ACKRSTART:
	      case PKCMD_WRITE:
		BufLen = ActLen;
		break;
	      case PKCMD_WRITE6:
		BufLen = (ActLen * 8 + 5) / 6;
		break;
	      case PKCMD_WRITE7:
		BufLen = (ActLen * 7 + 7) / 8;
		break;
	      default:
		puts(" BaD pkt");
		break;
	    }

	    if (ActLen > MAXPKT || BufLen > MAXPACKET) {
		if (PDebug || DDebug)
		    printf("Packet Length Error %d %d\n", ActLen, BufLen);
		RState = 0;
	    }
	    break;
	case 5: 	/*  DChkH   */
	    --len;
	    Pkt.dchkh = *ptr++;

	    ++RState;
	    if (Pkt.dchkh < 0x20 || Pkt.dchkh > 0x7F)
		RState = 0;
	    break;
	case 6: 	/*  DCHKL   */
	    --len;
	    Pkt.dchkl = *ptr++;

	    ++RState;
	    if (Pkt.dchkl < 0x20 || Pkt.dchkl > 0x7F)
		RState = 0;
	    break;
	case 7: 	/*  -DATA-  */
	    if (DBLen + len < BufLen) {     /*  not enough  */
		BMov(ptr, Pkt.data + DBLen, len);
		DBLen += len;
		len = 0;
		break;
	    }

	    /*
	     *	Enough data, check chk checksum
	     */

	    BMov(ptr, Pkt.data + DBLen, BufLen - DBLen);
	    len -= BufLen - DBLen;
	    ptr += BufLen - DBLen;

	    {
		uword chk;
		ubyte chkh;
		ubyte chkl;

		chk = chkbuf(Pkt.data, BufLen);
		chkh = Ascize(chk >> 8);
		chkl = Ascize(chk);

		if (Pkt.dchkh != chkh || Pkt.dchkl != chkl) {
		    printf("Chksum failure %02x %02x %02x %02x\n",
			Pkt.dchkh, chkh, Pkt.dchkl, chkl
		    );
		    RState = 0;
		    break;
		}
	    }

	    switch(Pkt.ctl & PKF_MASK) {
	    case PKCMD_ACKRSTART:
		dptr = Pkt.data;
		break;
	    case PKCMD_WRITE:
		dptr = Pkt.data;
		++Packets8In;
		break;
	    case PKCMD_WRITE6:
		UnExpand6(Pkt.data, RxTmp, ActLen);
		dptr = RxTmp;
		++Packets6In;
		break;
	    case PKCMD_WRITE7:
		UnCompress7(Pkt.data, RxTmp, ActLen);
		dptr = RxTmp;
		++Packets7In;
		break;
	    default:
		puts("BaD2");
		dptr = Pkt.data;
		break;
	    }

	    do_cmd(Pkt.ctl, dptr, ActLen);

	    RState = 0;
	    break;
	}
    }

    /* figure out how many characters are needed to complete a packet */
    {
	static short ExpAry[] = { 3, 2, 1, 4, 3, 2, 1, 0 };
	expect = ExpAry[RState];

	if (RState == 7)
	    expect = BufLen - DBLen;
    }

    if (Rto_act) { /* abort any active read time out */
	AbortIO((IOR *)&Rto);
	WaitIO((IOR *)&Rto);
	Rto_act = 0;
    }

    /* Got an incomplete packet, set a 'read timeout' for it */
    if (RState == 7) {
	Rto.tr_time.tv_secs = 8;
	Rto.tr_time.tv_micro= 0;
	SendIO((IOR *)&Rto);
	Rto_act = 1;
    }

    do_cmd( (uword) 0xFFFF , NULL, 0);

    return((int)expect);
}
