V10/sys/io/archosld.c

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

/*
 *  Raw line discipline and listener stuff for talking to controller
 */
#include "sys/param.h"
#include "sys/stream.h"
#include "sys/dkio.h"
#include "sys/conf.h"
#include "sys/dkstat.h"
#include "sys/dkmod.h"

/*
 * CMC/Listener message structure
 */
struct dialin {
	char	type;
	char	srv;
	short	param0;
	short	param1;
	short	param2;
	short	param3;
	short	param4;
	short	param5;
};

struct dialout {
	short	chan;
	struct	dialin d;
};

/*
 * Message codes
 */
#define T_CALL	'0'
#define	T_LSTNR	4
#define	T_CHG	3
#define	T_REPLY	10
#define	D_CLOSE	1
#define	D_ISCLOSED 2
#define	D_ENQ	3
#define	D_ENQANS 4

/*
 * Raw-mode line discipline for DK (only)
 */
long	adkopen();
int	adkclose(), adkiput(), adkoput(), adkosrv(), adkisrv();
static	struct qinit adkrinit = { adkiput, adkisrv, adkopen, adkclose, 72, 36};
static	struct qinit adkwinit = { adkoput, adkosrv, adkopen, nulldev, 72, 36};
struct	streamtab adkstream = { &adkrinit, &adkwinit };

extern	struct	dkstat dkstat;
extern	struct	dkmodule dkmod[];
extern	int dkmodcnt;

#define	NDKRAW	32
struct	adk {
	struct	queue *q;
	struct	dkmodule *module;
	short	chan;
} adk[NDKRAW];

long
adkopen(q, dev)
register struct queue *q;
{
	static timer_on = 0;
	register struct adk *dkp, *edkp;
	register struct dkmodule *dkm;

	if ((dkm = getdkmod(dev)) == NULL)
		return (0);
	edkp = NULL;
	for (dkp=adk; dkp < &adk[NDKRAW]; dkp++) {
		if (dkp->q == q) {
			if (dkp->chan != minor(dev) - dkm->lo || WR(q)->ptr==NULL) {
				printf("q %x dkp %x\n", q, dkp);
				panic("adkopen");
			}
			return(1);
		}
		if (dkp->q == NULL && edkp==NULL)
			edkp = dkp;
	}
	if (edkp==NULL)
		return(0);
	edkp->q = q;
	edkp->module = dkm;
	edkp->chan = minor(dev) - dkm->lo;
	q->flag |= QDELIM;
	q->ptr = 0;
	WR(q)->ptr = (caddr_t)edkp;
	if (timer_on==0) {
		adktimer();
		timer_on = 1;
	}
	return(1);
}

adkclose(q)
register struct queue *q;
{
	register struct block *bp;
	register struct adk *dkp = (struct adk *)WR(q)->ptr;

	if (WR(q)==dkp->module->listnrq) {
		dkp->module->listnrq = NULL;
		dkp->module->type = 0;
	}
	while (bp = getq(q))
		(*q->next->qinfo->putp)(q->next, bp);
	dkp->q = NULL;
	dkp->module = NULL;
}

/*
 * output put procedure.
 */
adkoput(q, bp)
register struct queue *q;
register struct block *bp;
{
	register struct adk *dkp = (struct adk *)q->ptr;
	register struct dkmodule *modp = dkp->module;
	register i;

	switch (bp->type) {

	case M_IOCTL:
		switch (stiocom(bp)) {

		/* hang everybody up */
		case DIOCHUP:
			/* must be on listener chan */
			if (q == modp->listnrq) {
				adkmesg(modp, T_LSTNR, 0, 0, 0, 1);
				bp->type = M_IOCACK;
				bp->rptr = bp->wptr;
				qreply(q, bp);
				return;
			}
			break;

		/* announce listener channel */
		case DIOCLHN:
			if (modp->listnrq == NULL) {
				modp->listnrq = q;
				RD(q)->flag |= QNOENB;
				modp->type = CMCLD;
				adkmesg(modp, T_LSTNR, 0, 0, 0, 0);
				bp->type = M_IOCACK;
				bp->rptr = bp->wptr;
				qreply(q, bp);
				return;
			}
			break;

		/* delay input */
		case DIOCSTOP:
			RD(q)->ptr = (caddr_t)1;
			bp->type = M_IOCACK;
			bp->rptr = bp->wptr;
			qreply(q, bp);
			return;

		/* suggest a free outgoing channel */
		case DIOCCHAN:
			for (i=modp->lo + 3; i<modp->hi; i+=2) {
				if (modp->dkstate[i]==DKCLOSED) {
					stiodata(bp)[0] = i;
					for (i = 1; i < sizeof(int); i++)
						stiodata(bp)[i] = 0;	/* silly */
					bp->wptr = bp->rptr+STIOCHDR+sizeof(int);
					bp->type = M_IOCACK;
					break;
				}
			}
			qreply(q, bp);
			return;

		default:
			(*q->next->qinfo->putp)(q->next, bp);
			return;
		}
		bp->type = M_IOCNAK;
		bp->wptr = bp->rptr;
		qreply(q, bp);
		return;
	}
	putq(q, bp);
}

adkosrv(q)
register struct queue *q;
{
	register struct block *bp;

	while ((q->next->flag&QFULL)==0 && (bp = getq(q)))
		(*q->next->qinfo->putp)(q->next, bp);
}

/*
 * input put procedure
 *   -- ignores all control bytes
 */
adkiput(q, bp)
register struct queue *q;
register struct block *bp;
{
	register struct adk *dkp = (struct adk *)WR(q)->ptr;
	register int delim;

	switch (bp->type) {

	case M_DATA:
		if (WR(q) == dkp->module->listnrq) {
			delim = bp->class & S_DELIM;
			putq(q, bp);
			if (delim)
				qenable(q);
			return;
		}
		if (q->next->flag&QFULL) { /* you lose */
			freeb(bp);
			return;
		}
	/*	bp->class |= S_DELIM; always push through; this seems wrong */
		if (q->ptr) {	/* input delayed */
			putq(q, bp);
			return;
		}
		(*q->next->qinfo->putp)(q->next, bp);
		return;

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

	case M_PRICTL:
		switch (*bp->rptr) {
		case DKMCLOSE:
			if (dkp->module->dkstate[bp->rptr[1]] == DKRCLOSE)
				adkmesg(dkp->module, T_CHG, D_ISCLOSED, 0, bp->rptr[1], 0);
			else
				adkmesg(dkp->module, T_CHG, D_CLOSE, 0, bp->rptr[1], 0);
			freeb(bp);
			return;

		default:
			freeb(bp);
			return;
		}

	default:
		freeb(bp);
	}
}

/*
 * listener sends a message to CMC
 */
adkmesg(modp, type, srv, p0, p1, p2)
register struct dkmodule *modp;
{
	register struct dialout *dp;
	register struct block *bp;

	if (modp->listnrq==NULL || modp->listnrq->next->flag&QFULL)
		return;
	if ((bp = allocb(sizeof(struct dialout))) == NULL)
		return;		/* hope it will be retried later */
	dp = (struct dialout *)bp->wptr;
	dp->chan = ((struct adk *)modp->listnrq->ptr)->chan;
	dp->d.type = type;
	dp->d.srv = srv;
	dp->d.param0 = p0;
	dp->d.param1 = p1;
	dp->d.param2 = p2;
	bp->wptr += sizeof(struct dialout);
	bp->class |= S_DELIM;
	(*modp->listnrq->next->qinfo->putp)(modp->listnrq->next, bp);
}


/* try to construct a struct dialin from the message */
struct block *
adkdialin(q, bp)
register struct queue *q;
register struct block *bp;
{
	register struct block *nbp;
	register int len;
	register int delim;
	register int n;
	register int over;

	if ((len = bp->wptr - bp->rptr) == sizeof(struct dialin))
		return bp;
	n = sizeof(struct dialin) - len;
	delim = bp->class & S_DELIM;

	if (bp->lim - bp->rptr >= sizeof(struct dialin))
		nbp = bp;
	else if ((nbp = allocb(sizeof(struct dialin))) == NULL) {
		freeb(bp);
		return NULL;
	} else {
		bcopy(bp->rptr, nbp->wptr, len);
		nbp->wptr += len;
		freeb(bp);
	}

	over = 0;
	while (!delim && (bp = getq(q))) {
		len = bp->wptr - bp->rptr <= n ? bp->wptr - bp->rptr : (over = 1, n);
		bcopy(bp->rptr, nbp->wptr, len);
		nbp->wptr += len;
		n -= len;
		delim = bp->class & S_DELIM;
		freeb(bp);
	}
	if (n == 0 && !over && delim)
		return nbp;
	freeb(nbp);
	return NULL;
}

/*
 * Receive message for listener
 */
adkisrv(q)
register struct queue *q;
{
	register struct adk *dkp = (struct adk *)WR(q)->ptr;
	struct dkmodule *modp = dkp->module;
	register struct queue *listnrq = modp->listnrq;
	register struct block *bp;
	register struct dialin *dialp;
	register int i;

	if (WR(q) != modp->listnrq)
		return;

	while (bp = getq(q)) {
	
		switch (*bp->rptr) {
	
		case T_CALL:
			bp->rptr++;
			if (bp->wptr - bp->rptr <= 0 &&
			   ((bp->class & S_DELIM) == 0 || (bp = getq(q)) == NULL))
				return;
			do {
				i = bp->class & S_DELIM;
				if (q->next->flag & QFULL)
					free(bp);
				else
					(*q->next->qinfo->putp)(q->next, bp);
			} while (i == 0 && (bp = getq(q)));
			break;
		case T_CHG:	
			if ((bp = adkdialin(q, bp)) == NULL)
				break;
			dialp = (struct dialin *)bp->rptr;
			i = dialp->param1;
			if (i <= 0 || i >= modp->hi - modp->lo) {
				dkstat.chgstrange++;
				if (dialp->srv)
					adkmesg(modp, T_CHG, D_ISCLOSED, 0, i, 0);
				freeb(bp);
				break;
			}
			switch (dialp->srv) {
	
			case D_CLOSE:		/* remote shutdown */
				switch (modp->dkstate[i]) {
	
				case DKRCLOSE:
					dkstat.notclosed++;
				case DKOPEN:
					putctl2(listnrq->next, M_PRICTL, DKMCLOSE, i);
					break;
	
				case DKLCLOSE:
				case DKCLOSED:
					adkmesg(modp, T_CHG, D_ISCLOSED, 0, i, 0);
					putctl2(listnrq->next, M_PRICTL, DKMCLOSE, i);
					break;
				}
				break;
			
			case D_ISCLOSED:
				switch (modp->dkstate[i]) {
	
				case DKLCLOSE:
				case DKCLOSED:
					putctl2(listnrq->next, M_PRICTL, DKMCLOSE, i);
					break;
	
				case DKOPEN:
				case DKRCLOSE:
					dkstat.isclosed++;
					break;
				}
				break;
	
			case D_ENQ:
				adkmesg(modp, T_CHG, D_ENQANS, 0, i, modp->dkstate[i]);
				break;
			}
			freeb(bp);
			break;
	
		case T_REPLY:	/* CMC sends reply; find, and hand to process */
			if ((bp = adkdialin(q, bp)) == NULL)
				break;
			dialp = (struct dialin *)bp->rptr;
			i = dialp->param0;
			if (i < 0 || i >= modp->hi - modp->lo) {
				free(bp);
				break;
			}
			for (dkp=adk; dkp<&adk[NDKRAW]; dkp++) {
				if (dkp->module==modp && dkp->chan==i) {
					bp->class |= S_DELIM;
					(*dkp->q->next->qinfo->putp)(dkp->q->next, bp);
					break;
				}
			}
			if (dkp == &adk[NDKRAW])
				free(bp);
			break;
	
		default:
			free(bp);
			break;
		}
	}
}

/*
 * 15-second timer
 * -- should look at dkmod->type, so different
 * listeners can coexist
 */
adktimer()
{
	register i;
	register struct dkmodule *dkp;

	for (dkp = dkmod; dkp < &dkmod[dkmodcnt]; dkp++) {
		if (dkp->listnrq && dkp->type == CMCLD) {
			adkmesg(dkp, T_LSTNR, 0, 0, 0, 0);
			for (i=dkp->hi - dkp->lo - 1; i >= 0; --i)
				if (dkp->dkstate[i] == DKLCLOSE)
					adkmesg(dkp, T_CHG, D_CLOSE, 0, i, 0);
		}
	}
	timeout(adktimer, (caddr_t)NULL, 15*HZ);
}