#define	KERNEL	1
#include <sys/pk.p>

#ifdef	UCB_SCCSID
static	char sccs_id[] = "@(#)pk2.c	3.1";
#endif


/*
 * input framing and block checking.
 * frame layout for most devices is:
 *	
 *	S|K|X|Y|C|Z|  ... data ... |
 *
 *	where 	S	== initial synch byte
 *		K	== encoded frame size (indexes pksizes[])
 *		X, Y	== block check bytes
 *		C	== control byte
 *		Z	== XOR of header (K^X^Y^C)
 *		data	== 0 or more data bytes
 *
 * device driver interfaces on input are:
 *	pkrint  - byte level
 *	pkrend  - dma or pseudo-dma transfer level
 *	pkdata - packet level
 */

int pksizes[] ={
	1, 32, 64, 128, 256, 512, 1024, 2048, 4096, 1
};

/*
 * Pseudo-dma byte collection.
 * This code can be put in the device driver
 * interrupt routine to eliminate the per byte
 * subroutine call.
 */
pkrint(c, tp)
register c;
register struct tty *tp;
{

	if (q1.c_cc<0) {
		if (q1.c_cf != NULL) {
			tp->t_erase = 0;
			*q1.c_cf++ = c;
		}
		if (++q1.c_cc)
			return;
		pkrend(tp);
		return;
	}
}



/*
 * End of input transfer.
 */
pkrend(tp)
register struct tty *tp;
{
register char *p;
struct pack *pk;
struct header *h;
register x;
char	cntl, hdcheck;
unsigned short sum;
int i,j,k;
char **bp;

	p = q1.c_cl;
	x = (int)q1.c_cf - (int)p;
	pk = (struct pack *)tp->t_linep;
	h = (struct header * )&pk->p_ihbuf;
	if (x == HDRSIZ) {
		if (*p++ == SYN ) {
			hdcheck = k = *p++;
			sum = (unsigned)*p;
			hdcheck ^= *p++;
			sum |= (unsigned)*p << 8;
			hdcheck ^= *p++;
			hdcheck ^= cntl = *p++;
			if (hdcheck != *p) {
				goto bad;
			}
			if (k == 9) {
				pkcntl(cntl, pk);
				q1.c_cf = q1.c_cl;
				q1.c_cc = -HDRSIZ;
				goto istart1;
			}
			if (k && pksizes[k]==pk->p_rsize) {
				pk->p_rpr = cntl&MOD8;
				pksack(pk);
				bp = pk->p_ipool;
				if (bp) {
					pk->p_ipool = (char **)*bp;
					pk->p_io = bp;
				} else {
				}
				q1.c_cf = (char *)bp;
				q1.c_cc = -pk->p_rsize;
				h->sum = sum;
				h->cntl = cntl;
				goto istart1;
			}
bad:
			pkbadframe(pk);
		}
scan:
		x = HDRSIZ;
		j = 0;
		p = (caddr_t)h;
		for (i = 1; i < HDRSIZ; i++)
			if (p[i] == SYN)
				for(x=i; i<HDRSIZ; i++,j++)
					p[j] = p[i];

		q1.c_cc = -x;
		q1.c_cf = (caddr_t)((int)p + j);
		goto istart2;
	}
	if (x == pk->p_rsize) {
		pkdata(h->cntl, h->sum, pk, q1.c_cl);
		pk->p_io = NULL;
		q1.c_cf = (char *)h;
		q1.c_cc = -HDRSIZ;
		goto istart1;
	}
	if (x == 0) {
		q1.c_cf = (char *)h;
		q1.c_cc = -HDRSIZ;
		pkbadframe(pk);
	} else {
		pkbadframe(pk);
		goto scan;
	}
istart1:
	q1.c_cl = q1.c_cf;
istart2:
	if (tp->t_iproc != NULL)
		(*tp->t_iproc)(tp);
}



/*
 * Put packet located at address bp
 * in an input slot for further processing.
 */
pkdata(c, sum, pk, cp)
char c;
unsigned short sum;
register struct pack *pk;
char *cp;
{
register struct tty *tp;
register x;
char **bp;
int t;

	pk->p_state &= ~BADFRAME;
	bp = (char **)cp;
	tp = pk->p_ttyp;
	if (pk->p_state&DRAINO || !(pk->p_state&LIVE)) {
		pk->p_msg |= pk->p_rmsg;
		pkoutput(pk);
		goto drop;
	}
	t = next[pk->p_pr];
	for(x=pk->p_pr; x!=t; x = (x-1)&7) {
		if (pk->p_is[x] == 0)
			goto slot;
	}
	/*
	 * this can't happen
	 */
	printf("no slot\n");
drop:
	*bp = (char *)pk->p_ipool;
	pk->p_ipool = bp;
	return;

slot:
	pk->p_imap |= mask[x];
	pk->p_is[x] = c;
	pk->p_isum[x] = sum;
	pk->p_ib[x] = cp;
	if (tp->t_chan)
		sdata(tp->t_chan); else
		wakeup(&pk->p_pr);
}


/*
 * Start transmission on output device associated with pk.
 * For asynch devices (t_line==1) framing is
 * imposed.  For devices with framing and crc
 * in the driver (t_line==2) the transfer is
 * passed on to the driver.
 */
pkxstart(pk, cntl, x)
struct pack *pk;
char cntl;
register x;
{
struct tty *tp;
register char *p;
short checkword;
char hdcheck;

	p = (caddr_t)&pk->p_ohbuf;
	tp = pk->p_ttyp;

	if (tp->t_line==1) {
		*p++ = SYN;
		if (x < 0) {
			*p = 9;
			checkword = cntl;
			q3.c_cl = NULL;
		} else {
			*p = pk->p_lpsize;
			checkword = pk->p_osum[x] ^ (unsigned)cntl;
			q3.c_cl = pk->p_ob[x];
		}
		checkword = CHECK - checkword;
		hdcheck = *p++;
		hdcheck ^= *p++ = checkword;
		hdcheck ^= *p++ = checkword>>8;
		q3.c_cc = -HDRSIZ;
	} else {
		q3.c_cc = -1;
	}

	hdcheck ^= *p++ = cntl;
	*p = hdcheck;
	q3.c_cf = (caddr_t)&pk->p_ohbuf;
/*
	pk->p_srxmit++;
*/
	(*tp->t_oproc)(tp);
}

/*
 * transmitter interrupt.
 */
int	pkdelay	= 2;

pkxint(tp)
register struct tty *tp;
{
register struct pack *pk;
register s;
extern int pkoutput();

	pk = (struct pack *)tp->t_linep;
	s = spl6();
	tp->t_state &= ~BUSY;
	if (q3.c_cl == NULL) {
			pkoutput(pk);
	} else {
		q3.c_cf = q3.c_cl;
		q3.c_cl = NULL;
		q3.c_cc = -pk->p_xsize;
		(*tp->t_oproc)(tp);
	}
	splx(s);
}

