V8/usr/sys/dev/pk.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Packet protocol processor
 */

#include "../h/param.h"
#include "../h/stream.h"
#include "../h/conf.h"
#include "../h/ioctl.h"
#include "../h/ttyld.h"
#include "pk.h"

struct pack {
	u_char	istate;
	u_char	ostate;
	u_char	xlogseg;	/* code for xmit segment size */
	u_char	S;		/* current output seqno */
	u_char	SN;		/* next output seqno */
	u_char	XW;		/* size of output window */
	u_char	R;		/* current input seqno */
	u_char	timer;
	short	xsegment;	/* output segment size */
	short	iwant;		/* # chars needed before i q enable */
	struct	block *xpacks[8];	/* waiting output blocks */
	struct {
		u_char	k;	/* packet size */
		u_char	c0;	/* low checksum byte */
		u_char	c1;	/* high checksum byte */
		u_char	C;	/* control byte */
		u_char	x;	/* header check */
	} hdr;			/* copy of header of received segment */
	struct	queue	*rdq;	/* associated read queue */
};

#define	PK_TYPE	0xC0
#define	PKCNTL	0x00
#define	PKDATA	0x80
#define	PKSDATA 0xC0

#define	PK_CLOSE 010
#define	PK_RJ	 020
#define	PK_RR	 040
#define	PK_INITC 050
#define	PK_INITB 060
#define	PK_INITA 070

#define	CHECK	0125252

#define	WDATA	01
#define	PKDELIM	02
#define	PKREJ	04

#define	SYN	020		/* sic */

#define	SINITA	1
#define	SINITB	2
#define	SINITC	3
#define	OPEN	4
#define	LCLOSE	5
#define	RCLOSE	6

#define	PKPRI	28
#define	DKDATA	0400

#define	RWINSIZE 3
#define	RSEGSIZE 1		/* 64 bytes */

#define	PKTIME	5

struct	pack	packs[NPK];
short	pksizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
u_short	pkc_sum;
u_short	pkc_x;
int	pkc_size;

int	pkiput(), pkisrv(), pkoput(), pkosrv(), pkopen(), pkclose();
static	struct qinit pkrinit = { pkiput, pkisrv, pkopen, pkclose, 512, 64 };
static	struct qinit pkwinit = { pkoput, pkosrv, pkopen, pkclose, 128, 65 };
struct	streamtab pkinfo = { &pkrinit, &pkwinit };

pkopen(q)
register struct queue *q;
{
	register struct pack *pkp;
	register s;
	static timer = 0;
	int pktimer();

	if (q->ptr) {
		pkp = (struct pack *)q->ptr;
		if (pkp->ostate != OPEN)
			return(0);	/* don't reopen half-open chans */
		return(1);
	} else {
		for (pkp = packs; pkp->ostate!=0; pkp++)
			if (pkp >= &packs[NPK])
				return(0);
		pkp->rdq = q;
		q->ptr = (caddr_t)pkp;
		WR(q)->ptr = (caddr_t)pkp;
		pkp->S = 1;
		pkp->SN = 1;
		pkp->R = 0;
		putctl(q->next, M_FLUSH);
		pkp->ostate = SINITA;
		pkp->istate = 0;
		pkp->timer = 2;
		pkscntl(pkp, PK_INITA+RWINSIZE);
	}
	if (timer == 0) {
		timer = 1;
		timeout(pktimer, (caddr_t)NULL, 60);
	}
	s = spl5();
	while (pkp->ostate != OPEN) {
		switch (tsleep((caddr_t)pkp, PKPRI, 15)) {
		case TS_OK:
			continue;
		case TS_SIG:
		case TS_TIME:
			pkp->ostate = 0;
			splx(s);
			return(0);
		}
	}
	q->flag |= QDELIM;
	splx(s);
	return(1);
}

/*
 * Shut it down.
 *  The problem is to dispose of unacked stuff in the window.
 *   -- no real solution; the receiver might hang on for hours.
 *   Give it 15 seconds.
 */
pkclose(q)
register struct queue *q;
{
	register struct pack *pkp;
	register s = spl5();
	register i;

	pkp = (struct pack *)q->ptr;
	pkp->ostate = LCLOSE;
	flushq(q, 1);
	for (i=0; pkp->S < pkp->SN && i<15; i++)
		tsleep((caddr_t)pkp, PKPRI, 1);
	splx(s);
	flushq(WR(q), 1);
	pkrack(pkp, pkp->SN-1, 0);
	pkscntl(pkp, PK_CLOSE);
	pkp->ostate = 0;
}


/*
 * Process a bunch of input
 */
pkisrv(q)
register struct queue *q;
{
	register struct pack *pkp = (struct pack *)q->ptr;
	register struct block *bp;
	register segsize, msgsize;
	int R, OK;
	u_char c;

more:
	while ((pkp->istate & WDATA) == 0) {
		if (pkwant(pkp, 6)==0)
			return;
		pkgetb(q, &c, 1);
		if (c != SYN)
			continue;
		pkgetb(q, (u_char *)&pkp->hdr, 5);
		if (pkp->hdr.x != (pkp->hdr.k ^ pkp->hdr.c0 ^
		    pkp->hdr.c1 ^ pkp->hdr.C)) {	/* bad header? */
			continue;
		}
		switch (pkp->hdr.C & PK_TYPE) {
		case PKCNTL:
			pkp->istate &= ~PKREJ;
			pkcntl(pkp);
			continue;
	
		case PKDATA:
		case PKSDATA:
			if (pkp->ostate==SINITC) {
				pkp->ostate = OPEN;
				wakeup((caddr_t)pkp);
			}
			if (pkp->S != pkp->SN)	/* if need an ack */
				pkrack(pkp, pkp->hdr.C&07, 0);
			if (pkp->hdr.k<1 || pkp->hdr.k>8) {
				continue;
			}
			pkp->istate |= WDATA;
			break;

		default:
			continue;
		}
		break;
	}
	if (q->next->flag&QFULL)
		return;
	msgsize = segsize = pksizes[pkp->hdr.k];
	if (pkwant(pkp, segsize) == 0)
		return;
	/*
 	 * Now have a segment's worth of data; if in window, compute checksum
	 * and accept if all is OK
	 */
	R = (pkp->hdr.C>>3) & 07;
	if (R <= pkp->R)
		R += 8;
	/* compute checksum */
	OK = 0;
	pkc_sum = -1;
	pkc_x = 0;
	pkc_size = segsize;
	for (bp = q->first; segsize>0; bp = bp->next) {
		register sz = bp->wptr - bp->rptr;
		pkcksum(bp->rptr, sz);
		segsize -= sz;
	}
	segsize = msgsize;
	pkc_sum ^= pkp->hdr.C;
	pkc_sum += (pkp->hdr.c1<<8) + pkp->hdr.c0;
	if (pkc_sum != CHECK || R != pkp->R+1) {	/* Bad? */
		if (R < pkp->R+RWINSIZE) {
			flushq(q, 0);
			if ((pkp->istate & PKREJ) == 0)
				pkscntl(pkp, PK_RJ+pkp->R);
			pkp->istate &= ~WDATA;
			pkp->istate |= PKREJ;
			goto more;
		}
		msgsize = -1;		/* duplicate; just ignore */
	} else {	/* It's good */
		pkp->R = (pkp->R+1)&07;
		OK = 1;
		if ((pkp->hdr.C&PK_TYPE) == PKSDATA) {
			pkgetb(q, &c, 1);
			msgsize -= c & 0x7F;
			segsize -= 1;
			if (c&0x80) {
				pkgetb(q, &c, 1);
				msgsize -= c << 7;
				segsize -= 1;
			}
		}
	}
	pkp->istate &= ~PKREJ;
	pkscntl(pkp, PK_RR+pkp->R);
	while (segsize > 0) {
		register i;
		bp = getq(q);
		i = bp->wptr - bp->rptr;
		if (i <= msgsize) {
			(*q->next->qinfo->putp)(q->next, bp);
			msgsize -= i;
			segsize -= i;
			continue;
		}
		if (msgsize>0) {
			register struct block *bp1 = allocb(msgsize);
			if (bp1) {
				bcopy(bp->rptr, bp1->wptr, msgsize);
				bp->rptr += msgsize;
				bp1->wptr += msgsize;
				(*q->next->qinfo->putp)(q->next, bp1);
				segsize -= msgsize;
				msgsize = 0;
				putbq(q, bp);
				continue;
			} else
				panic("pk alloc\n");
		}
		if (i <= segsize) {
			freeb(bp);
			segsize -= i;
			continue;
		}
		bp->rptr += segsize;
		putbq(q, bp);
		break;
	}
	if (OK)
		putctl(q->next, M_DELIM);
	pkp->istate &= ~WDATA;
	goto more;
}

/*
 * Packet arrives.
 */
pkiput(q, bp)
struct queue *q;
register struct block *bp;
{
	register struct pack *pkp;
	register c;

	if ((pkp = (struct pack *)q->ptr)==NULL || pkp->ostate==LCLOSE) {
		freeb(bp);
		return;
	}
	switch (bp->type) {

	case M_DATA:
		if (q->flag&QFULL) {
			freeb(bp);
			return;
		}
		if ((pkp->iwant -= bp->wptr - bp->rptr) <= 0) {
			pkp->iwant = 0;
			qenable(q);
		}
		putq(q, bp);
		return;

	case M_HANGUP:
		pkp->ostate = RCLOSE;
		flushq(WR(q), 0);
		pkrack(pkp, pkp->SN-1, 0);
		qenable(q);
		freeb(bp);
		return;

	case M_IOCACK:
	case M_IOCNAK:
		(*q->next->qinfo->putp)(q->next, bp);
		return;

	default:
		freeb(bp);
		return;
	}
}

/*
 * --- Output processor
 */

/*
 * accept data from writer
 *  -- handle all non-data messages
 */
pkoput(q, bp)
register struct queue *q;
register struct block *bp;
{
	register struct pack *pkp = (struct pack *)q->ptr;
	register union stmsg *sp;

	if (pkp->ostate == RCLOSE) {
		freeb(bp);
		return;
	}
	switch (bp->type) {

	case M_DELIM:
		if (pkp->istate & PKDELIM)
			goto isdata;
		pkp->istate |= PKDELIM;
		/* flow through */
	case M_FLUSH:
		freeb(bp);
		return;

	case M_IOCTL:
		sp = (union stmsg *)bp->rptr;
		switch (sp->ioc0.com) {

		case TIOCGETP:
			sp->ioc1.sb.sg_erase = 0;
			sp->ioc1.sb.sg_kill = 0;
			sp->ioc1.sb.sg_flags = RAW;
			bp->wptr = bp->rptr+sizeof(struct ioc1);
			bp->type = M_IOCACK;
			qreply(q, bp);
			break;

		default:
			(*q->next->qinfo->putp)(q->next, bp);
			break;
		}
		return;

	default:
		freeb(bp);
		return;

	case M_DATA:
		pkp->istate &= ~PKDELIM;
	isdata:
		putq(q, bp);
		if (pkp->SN < pkp->S + pkp->XW)
			qenable(q);
		return;
	}
}

/*
 * PK out server:
 *  if space in window, process queue
 *    take care of splitting packets bigger than segment size
 *    if timer has expired, retransmit
 */
pkosrv(q)
register struct queue *q;
{
	register struct pack *pkp = (struct pack *)q->ptr;
	register struct block *bp, *xbp;

	if (pkp->timer==0 && pkp->S != pkp->SN)
		pkxmit(q, pkp->xpacks[pkp->S], pkp->S);
	while (pkp->SN < pkp->S + pkp->XW) {
		if ((bp = getq(q)) == NULL)
			break;
		if (bp->wptr-bp->rptr > pkp->xsegment) {
			if ((xbp = allocb(pkp->xsegment)) == NULL) {
				putbq(q, bp);
				return;
			}
			bcopy(bp->rptr, xbp->wptr, pkp->xsegment);
			bp->rptr += pkp->xsegment;
			xbp->wptr += pkp->xsegment;
			putbq(q, bp);
			bp = xbp;
		}
		bp->type = M_DATA;	/* M_DELIM -> 0-length write */
		pkp->xpacks[pkp->SN & 07] = bp;
		pkxmit(q, bp, pkp->SN);
		pkp->SN++;
	}
}

/*
 *  Send out a packet, with header.
 *    -- only a "pointer" to the data is sent; the original
 *    is kept.
 */
pkxmit(q, bp, pkno)
struct queue *q;
register struct block *bp;
unsigned pkno;
{
	register struct block *hbp;
	register size;
	register seg;
	register struct pack *pkp = (struct pack *)q->ptr;
	register struct block *sbp = NULL;
	register sizeadjust = 0;
	static u_char zero[64];

	if (bp == (struct block *)NULL) {
		printf("0 bp in pkxmit\n");
		return;
	}
	pkp->timer = PKTIME;
	if ((hbp = allocb(8)) == NULL)
		return;		/* hope timeouts will recover */
	pkno &= 07;
	size = bp->wptr - bp->rptr;
	/* compute header */
	hbp->wptr += 6;		/* header size */
	hbp->rptr[0] = SYN;
	hbp->rptr[1] = pkp->xlogseg;
	seg = pkp->xsegment;
	pkc_sum = -1;
	pkc_x = 0;
	pkc_size = seg;
	if (size < seg) {
		hbp->rptr[4] = PKSDATA | (pkno << 3) | pkp->R;
		if ((sbp = allocb(2)) == NULL) {
			freeb(hbp);
			return;
		}
		/* hack in the size bytes */
		if ((seg - size) <= 127) {
			*sbp->wptr++ = seg - size;
			sizeadjust = 1;
		} else {
			*sbp->wptr++ = 0x80 + ((seg - size) & 0x7F);
			*sbp->wptr++ = (seg - size) >> 7;
			sizeadjust = 2;
		}
		pkcksum(sbp->rptr, sizeadjust);
		pkcksum(bp->rptr, size);
		seg -= size;
		while (seg>0) {
			pkcksum(zero, 64);
			seg -= 64;
		}
	} else {
		if (size > seg) {
			printf("pk size botch\n");
			return;
		}
		hbp->rptr[4] = PKDATA | (pkno<<3) | pkp->R;
		pkcksum(bp->rptr, size);
	}
	pkc_sum = CHECK - (pkc_sum ^ hbp->rptr[4]);
	hbp->rptr[2] = pkc_sum;
	hbp->rptr[3] = pkc_sum>>8;
	hbp->rptr[5] = hbp->rptr[1]^hbp->rptr[2]^hbp->rptr[3]^hbp->rptr[4];
	/* send header */
	(*q->next->qinfo->putp)(q->next, hbp);
	/* send size */
	if (sbp)
		(*q->next->qinfo->putp)(q->next, sbp);
	/* send data ptr */
	if (hbp = allocb(0)) {		/* if fail, pray for timeout */
		hbp->rptr = bp->rptr;	/* copy ptrs */
		hbp->wptr = bp->wptr;
		(*q->next->qinfo->putp)(q->next, hbp);	/* send data */
	}
	/* send padding */
	seg = pkp->xsegment - sizeadjust;
	while (size < seg) {
		register deficit = seg - size;
		if (deficit > 64)
			deficit = 64;
		if (hbp = allocb(deficit)) {
			hbp->rptr = zero;
			hbp->wptr = zero + deficit;
			(*q->next->qinfo->putp)(q->next, hbp);
		}
		size += deficit;
	}
}

/*
 * Receive an ack for a transmitted packet.
 *  Advance the window.  Retransmit stacked up stuff if required.
 */
pkrack(pkp, packno, rxmit)
register unsigned packno;
register struct pack *pkp;
{
	register struct block **bpp;
	register s = spl5();

	packno &= 07;
	if (packno < pkp->S)
		packno += 8;
	pkp->timer = PKTIME;
	if (packno < pkp->SN)
		while (pkp->S <= packno) {
			bpp = &pkp->xpacks[pkp->S & 07];
			if (*bpp) {
				freeb(*bpp);
				*bpp = NULL;
			}
			pkp->S++;
		}
	if (WR(pkp->rdq)->count)
		qenable(WR(pkp->rdq));
	if (rxmit) {
		for (packno = pkp->S; packno < pkp->SN; packno++)
			pkxmit(WR(pkp->rdq), pkp->xpacks[packno&07], packno);
	}
	if (pkp->S >= 8) {
		pkp->S -= 8;
		pkp->SN -= 8;
	}
	splx(s);
}

/*
 * If n bytes are available, return 1, else 0.
 */
pkwant(pkp, n)
register n;
register struct pack *pkp;
{
	register int navail = 0;
	register struct block *bp;
	int s = spl5();

	for (bp = pkp->rdq->first; bp; bp = bp->next) {
		navail += bp->wptr - bp->rptr;
		if (navail >= n) {
			splx(s);
			return(1);
		}
	}
	if (pkp->ostate==RCLOSE)
		putctl(pkp->rdq->next, M_HANGUP);
	pkp->iwant = n - navail;
	splx(s);
	return(0);
}

/*
 * Copy  n  bytes from the front of the queue.
 */
pkgetb(q, cp, n)
register struct queue *q;
register u_char *cp;
register n;
{
	register struct block *bp;

	while (n) {
		if ((bp = q->first) == NULL) {
			printf("pkgetb fail\n");
			return;
		}
		if (bp->rptr >= bp->wptr) {
			freeb(getq(q));
			continue;
		}
		*cp++ = *bp->rptr++;
		--n;
	}
	if (bp->rptr >= bp->wptr)
		freeb(getq(q));
}

/*
 * The very strange checksum
 */
pkcksum(cp, nbytes)
register u_char *cp;
register nbytes;
{
	register sum, t, x;

	if (nbytes > pkc_size)
		nbytes = pkc_size;
	sum = pkc_sum;
	x = pkc_x;
	while (--nbytes >= 0) {
		sum <<= 1;
		if (sum & 0x10000)
			sum++;
		t = sum & 0xFFFF;
		sum += *cp++;
		sum &= 0xFFFF;
		x += sum ^ pkc_size;
		pkc_size--;
		if (sum <= t)
			sum ^= x;
	}
	pkc_x = x;
	pkc_sum = sum;
}

/*
 * Send a control packet
 */
pkscntl(pkp, cmd)
struct pack *pkp;
{
	register struct block *bp;
	register u_char *cp;
	register struct queue *q;

	if ((bp = allocb(6)) == NULL)
		return;		/* what else to do? */
	bp->wptr += 6;
	cp = bp->rptr;
	*cp++ = SYN;
	*cp++ = 9;		/* ctl pack marker */
	*cp++ = cmd;
	*cp++ = 0;
	*cp++ = cmd;
	*cp++ = 9;		/* xor of hdr[1..4] */
	q = WR(pkp->rdq);
	(*q->next->qinfo->putp)(q->next, bp);
}

/*
 * Receive a control packet
 */
pkcntl(pkp)
register struct pack *pkp;
{
	register val;
	register reject = 0;

	if (pkp->hdr.k != 9) {
		return;
	}
	val = pkp->hdr.C & 07;
	switch (pkp->hdr.C & 0370) {

	case PK_INITA:
		pkp->timer = 2;
		pkp->XW = val;
		if (pkp->ostate==SINITA || pkp->ostate==SINITB) {
			pkscntl(pkp, PK_INITA+RWINSIZE);
			pkscntl(pkp, PK_INITB+RSEGSIZE);
			pkp->ostate = SINITB;
			return;
		}
		return;

	case PK_INITB:
		pkp->timer = 2;
		pkp->xsegment = pksizes[val+1];
		pkp->xlogseg = val + 1;
		if (pkp->ostate==SINITB) {
			pkp->ostate = SINITC;
			pkscntl(pkp, PK_INITB+RSEGSIZE);
			pkscntl(pkp, PK_INITC+RWINSIZE);
			return;
		}
		return;

	case PK_INITC:
		if (pkp->ostate>=SINITB) {
			if (pkp->ostate==SINITB)
				pkscntl(pkp, PK_INITC+RWINSIZE);
			pkp->ostate = OPEN;
			wakeup((caddr_t)pkp);
			return;
		}
		return;

	case PK_RJ:
		reject = 1;
	case PK_RR:
		pkrack(pkp, val, reject);
		return;

	case PK_CLOSE:
		pkp->ostate = RCLOSE;
		flushq(WR(pkp->rdq), 0);
		pkrack(pkp, pkp->SN-1, 0);
		qenable(pkp->rdq);
		return;

	default:
		return;
	}
}

pktimer()
{
	register struct pack *pkp;

	for (pkp = packs; pkp < &packs[NPK]; pkp++) {
		if (pkp->timer) {
			pkp->timer--;
			continue;
		}
		switch (pkp->ostate) {

		case 0:
			continue;

		case OPEN:
		case LCLOSE:
			qenable(WR(pkp->rdq));
			continue;

		case SINITA:
			pkscntl(pkp, PK_INITA+RWINSIZE);
			pkp->timer = 2;
			continue;

		case SINITB:
			pkscntl(pkp, PK_INITA+RWINSIZE);
			pkscntl(pkp, PK_INITB+RSEGSIZE);
			pkp->timer = 2;
			continue;
		}
	}
	timeout(pktimer, (caddr_t)NULL, 60);
}