~kris/9p

9hist

b937ce47ef8b34262825b478d2448ab5fc021f33 — David du Colombier 34 years ago 9d0888e
Plan 9 from Bell Labs 1992-06-09
M boot/aux.c => boot/aux.c +3 -1
@@ 131,12 131,14 @@ outin(char *prompt, char *def, int len)
	int n;
	char buf[256];

	alarm(60*1000);
	do{
		print("%s[%s]: ", prompt, *def ? def : "no default");
		n = read(0, buf, len);
	}while(n==0);
	alarm(0);
	if(n < 0)
		fatal("can't read #c/cons; please reboot");
		fatal("can't read #c/cons or timeout; please reboot");
	if(n != 1){
		buf[n-1] = 0;
		strcpy(def, buf);

M boot/boot.c => boot/boot.c +2 -1
@@ 104,7 104,8 @@ boot(int argc, char *argv[])
			fatal("mount");
	}
	close(fd);
	newkernel();
	if(cpuflag == 0)
		newkernel();

	/*
	 *  if a local file server exists and it's not the

M gnot/dat.h => gnot/dat.h +2 -0
@@ 102,12 102,14 @@ struct Conf
	int	npipe;		/* number of pipes */
	int	nservice;	/* number of services */
	int	nfsyschan;	/* number of filsys open channels */
	int	nisdn;		/* number of isdn interfaces */
	int	nlapd;		/* number of dragnet protocol modules */
	ulong	maxialloc;	/* maximum bytes used by ialloc */
	int	copymode;	/* 0 is copy on write, 1 is copy on reference */
	int	portispaged;	/* ??? */
	int	cntrlp;		/* panic on ^P */
	int	dkif;		/* number of datakit interfaces */
	int	nconc;		/* number of datakit concentrators */
};

#include "../port/portdat.h"

A gnot/devhifi.c => gnot/devhifi.c +753 -0
@@ 0,0 1,753 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"devtab.h"

#include	"io.h"
#ifdef	SET
#undef	SET
#endif
#include	"isdn.h"
#include	"hifi.h"

#define DPRINT 	if(hifidebug)kprint

#define	BSIZE	4096

typedef struct Hifichan
{
	Isdn *	udev;		/* unite interface */
	Hifi *	hdev;		/* pointer to device */
	uchar	imask;		/* interrupt bit */
	uchar	opreg;		/* sticky bits */
	uchar	tctl;		/* more sticky bits */
	uchar	devaddr;	/* debug access */

	Lock	kctl;		/* kproc start */
	Rendez	kr;		/* kernel process sleep/wakeup */
	QLock;			/* access to struct */
	int	hdlc;		/* hdlc mode enabled */
	int	hangup;		/* hangup pending */

	int	renable;	/* reading enabled */
	Block *	inb[NB];	/* input buffer */
	int	ri;		/* input read pointer */
	int	wi;		/* input write pointer */
	Queue *	rq;		/* read queue */

	int	wenable;	/* writing enabled */
	int	wactive;	/* writing in progress */
	QLock	wlock;		/* write's are atomic */
	Block *	outb[NB];	/* output buffer */
	int	so;		/* output scavenge pointer */
	int	ro;		/* output read pointer */
	int	wo;		/* output write pointer */
	Block *	out;		/* current output block */
	Rendez	tr;		/* if transmitter must wait */
	Rendez	cr;		/* waiting to close */

	/* statistics */

	ulong	inchars;
	ulong	badcrc;
	ulong	abort;
	ulong	overrun;
	ulong	badcount;
	ulong	overflow;
	ulong	toolong;
	ulong	underrun;
	ulong	outchars;
} Hifichan;

Hifichan *	hifichan;
Hifichan *	hifichanN;

static void	devinit(int);
static int	hifiintr(void);
static void	hifikproc(void*);
static void	hifiloopctl(Hifichan*, int);
static int	hifirecv(Hifichan*);
static void	hifirecven(Hifichan*, int);
static int	hifixmit(Hifichan*);
static void	hifixmitdis(Hifichan*, int);
static void	hifixmiten(Hifichan*, int);

enum {
	Qdir, Qdev, Qstats
};

Dirtab hifidir[]={
	"dev",		{Qdev},		0,	0666,
	"stats",	{Qstats},	0,	0444,
};
#define	NHIFI	(sizeof hifidir/sizeof(Dirtab))

/*
 *  stream module definition
 */
static void hifiiput(Queue*, Block*);
static void hifioput(Queue*, Block*);
static void hifistopen(Queue*, Stream*);
static void hifistclose(Queue*);
Qinfo hifiinfo = { hifiiput, hifioput, hifistopen, hifistclose, "hifi" };

int	hifidebug = 1;

void
hifireset(void)
{
	hifichan = ialloc(2*conf.nisdn*sizeof(Hifichan), 0);
	hifichanN = &hifichan[2*conf.nisdn];
}

void
hifiinit(void)
{}

Chan*
hifiattach(char *spec)
{
	int dev;
	Chan *c;
	Hifichan *hp;
	static int intrinited;

	dev = strtoul(spec, 0, 0);
	if(dev >= 2*conf.nisdn)
		error(Enonexist);
		
	c = devattach('H', spec);
	c->dev = dev;
	hp = &hifichan[dev];
	if(!hp->udev){
		DPRINT("hifiattach: init dev=%d\n", c->dev);
		if(!intrinited){
			intrinited = 1;
			DPRINT("addportintr(hifiintr)\n");
			addportintr(hifiintr);
		}
		devinit(c->dev);
	}
	return c;
}

Chan*
hificlone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int	 
hifiwalk(Chan *c, char *name)
{
	return devwalk(c, name, hifidir, NHIFI, streamgen);
}

void	 
hifistat(Chan *c, char *dp)
{
	devstat(c, dp, hifidir, NHIFI, streamgen);
}

Chan*
hifiopen(Chan *c, int omode)
{
	Hifichan *hp = &hifichan[c->dev];

	if(c->qid.path == CHDIR){
		if(omode != OREAD)
			error(Eperm);
	}else switch(STREAMTYPE(c->qid.path)){
	case Qdev:
	case Qstats:
		break;
	default:
		DPRINT("hifiopen dev=%d\n", c->dev);
		streamopen(c, &hifiinfo);
	}
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void	 
hificreate(Chan *c, char *name, int omode, ulong perm)
{
	error(Eperm);
}

void	 
hificlose(Chan *c)
{
	Hifichan *hp = &hifichan[c->dev];

	if(c->qid.path != CHDIR)
		streamclose(c);
}

long	 
hifiread(Chan *c, void *buf, long n, ulong offset)
{
	Hifichan *hp = &hifichan[c->dev];
	char nbuf[512], *p; int k;

	if(n <= 0)
		return 0;
	if(c->qid.path == CHDIR){
		return devdirread(c, buf, n, hifidir, NHIFI, streamgen);
	}else switch(STREAMTYPE(c->qid.path)){
	case Qdev:
		p = nbuf;
		p += sprint(p, "0x%2.2ux = 0x%2.2ux\n",
			hp->devaddr, isdnpeek(hp->udev, (void *)hp->devaddr));
		goto Readnbuf;

	case Qstats:
		p = nbuf;
		p += sprint(p, "inchars  %10lud\n", hp->inchars);
		p += sprint(p, "badcrc   %10lud\n", hp->badcrc);
		p += sprint(p, "abort    %10lud\n", hp->abort);
		p += sprint(p, "overrun  %10lud\n", hp->overrun);
		p += sprint(p, "badcount %10lud\n", hp->badcount);
		p += sprint(p, "overflow %10lud\n", hp->overflow);
		p += sprint(p, "toolong  %10lud\n", hp->toolong);
		p += sprint(p, "underrun %10lud\n", hp->underrun);
		p += sprint(p, "outchars %10lud\n", hp->outchars);
	Readnbuf:
		k = p - nbuf;
		if (offset >= k)
			return 0;
		if (offset+n > k)
			n = k - offset;
		memmove(buf, &nbuf[offset], n);
		return n;
	}
	if(!hp->renable)
		hifirecven(hp, 1);
	return streamread(c, buf, n);
}

long	 
hifiwrite(Chan *c, void *buf, long n, ulong offset)
{
	Hifichan *hp = &hifichan[c->dev];
	Hifi *hifi = hp->hdev;
	char nbuf[32], *p;

	if(n < 0)
		return 0;
	switch(STREAMTYPE(c->qid.path)){
	case Qdev:
		if(n>sizeof nbuf-1)
			n = sizeof nbuf-1;
		memmove(nbuf, buf, n);
		nbuf[n-1] = 0;
		hp->devaddr = (int)hp->hdev + strtoul(nbuf,0,0);
		if(p = strchr(nbuf, '='))	/* assign = */
			isdnpoke(hp->udev, (void *)hp->devaddr, strtoul(++p,0,0));
		return n;
	}
	return streamwrite(c, buf, n, 0);
}

void	 
hifiremove(Chan *c)
{
	error(Eperm);
}

void	 
hifiwstat(Chan *c, char *dp)
{
	error(Eperm);
}

/*
 *	the stream routines
 */

#define	Predicate(name, expr)	static int name(void *arg) { return expr; }
#define	hp	((Hifichan *)arg)

Predicate(kRun, hp->ri != hp->wi || hp->so != hp->ro)

Predicate(tActive, hp->wenable)
Predicate(tDead, hp->wenable == 0)
Predicate(tEmpty, hp->ro == hp->wo)
Predicate(tFull, NEXT(hp->wo) == hp->so)
Predicate(tNotFull, NEXT(hp->wo) != hp->so)

#undef	hp

static void
hifistopen(Queue *q, Stream *s)
{
	Hifichan *hp = &hifichan[s->dev];
	char name[32];

	DPRINT("hifistopen %d\n", s->dev);
	qlock(hp);
	q->ptr = q->other->ptr = hp;
	hp->rq = q;
	if(canlock(&hp->kctl)){
		sprint(name, "hifi%d", s->dev);
		kproc(name, hifikproc, hp);
	}
	qunlock(hp);
}

static void
hifistclose(Queue * q)
{
	Hifichan *hp = (Hifichan *)q->ptr;

	DPRINT("hifistclose %d...\n", hp-hifichan);
	hifirecven(hp, -1);
	qlock(hp);
	hp->rq = 0;
	hp->hangup = 0;
	if(tActive(hp)){
		if(!hp->wactive || waserror()){
			hifixmitdis(hp, 1);
			wakeup(&hp->kr);
		}else{
			sleep(&hp->cr, tDead, hp);
			poperror();
		}
	}
	qunlock(hp);
	DPRINT("hifistclose %d OK\n", hp-hifichan);
}

/*
 *  this is only called by hangup
 */
static void
hifiiput(Queue *q, Block *bp)
{
	Hifichan *hp = (Hifichan *)q->ptr;

	if(bp->type == M_HANGUP){
		DPRINT("hifiiput %d M_HANGUP\n", hp-hifichan);
		hifirecven(hp, 0);
	}
	freeb(bp);
}

void
hifioput(Queue *q, Block *bp)
{
	Hifichan *hp = (Hifichan *)q->ptr;

	if(bp->type != M_DATA){
		if(hifidebug){
			char fmt[32];
			sprint(fmt, "hifioput %%d: %%.%ds\n", bp->wptr-bp->rptr);
			kprint(fmt, hp-hifichan, bp->rptr);
		}
		if(streamparse("hdlc", bp)){
			if(streamparse("on", bp)){
				hp->hdlc = 1;
				hifixmiten(hp, 1);
			}else{
				hp->hdlc = 0;
				if(hp->wenable)
					hifixmiten(hp, 1);
			}
		}else if(streamparse("loop", bp)){
			if(streamparse("on", bp))
				hifiloopctl(hp, 1);
			else
				hifiloopctl(hp, 0);
		}else if(streamparse("click!", bp)){
			hifirecven(hp, 0);
		}else if(streamparse("recven", bp)){
			hifirecven(hp, 1);
		}
		freeb(bp);
		return;
	}
	qlock(&hp->wlock);
	if(!putq(q, bp)){	/* send only whole messages */
		qunlock(&hp->wlock);
		return;
	}
	DPRINT("hifioput %d: [%d] len=%d\n",
		hp-hifichan, TK2MS(MACHP(0)->ticks), q->len);
	if(tFull(hp))
		sleep(&hp->tr, tNotFull, hp);
	hp->outb[hp->wo] = grabq(q);
	hp->wo = NEXT(hp->wo);
	qunlock(&hp->wlock);

	isdnlock(hp->udev);
	if(hifixmit(hp))
		wakeup(&hp->kr);
	isdnunlock(hp->udev);
}

static void
hifirecven(Hifichan *hp, int r)
{
	Hifi *hifi = hp->hdev;
	int i;

	qlock(hp);
	if(!hp->renable && r > 0){
		hp->ri = hp->wi;
		for (i=0; i<NB; i++)
			if(!hp->inb[i])
				hp->inb[i] = allocb(BSIZE);
		hp->renable = 1;
		isdnpoke(hp->udev, &hifi->opreg, hp->opreg |= Enr);
		DPRINT("hifirecven %d: enabled\n", hp-hifichan);
	}else if(hp->renable && r <= 0){
		isdnpoke(hp->udev, &hifi->opreg, hp->opreg &= ~Enr);
		hp->renable = 0;
		hp->hangup = 1;
		wakeup(&hp->kr);
		DPRINT("hifirecven %d: disabled\n", hp-hifichan);
	}
	if(r < 0){
		hp->ri = hp->wi;
		for (i=0; i<NB; i++)
			if(hp->inb[i]){
				freeb(hp->inb[i]);
				hp->inb[i] = 0;
			}
		DPRINT("hifirecven %d: flushed\n", hp-hifichan);
	}
	qunlock(hp);
}

static void
hifikproc(void *arg)
{
	Hifichan *hp = arg;
	Block *bp;
	int i;
Top:
	if(waserror()){
		print("hifikproc: error\n");
		goto Top;
	}
	for(;;){
		qlock(hp);
		while(hp->ri != hp->wi){
			if(hp->rq)
				PUTNEXT(hp->rq, hp->inb[hp->ri]);
			else
				freeb(hp->inb[hp->ri]);
			if(hp->renable)
				hp->inb[hp->ri] = allocb(BSIZE);
			else
				hp->inb[hp->ri] = 0;
			hp->ri = NEXT(hp->ri);
		}
		if(hp->hangup){
			hp->hangup = 0;
			if(hp->rq){
				bp = allocb(0);
				bp->type = M_HANGUP;
				PUTNEXT(hp->rq, bp);
			}
		}
		i = 0;
		while(hp->so != hp->ro){
			freeb(hp->outb[hp->so]);
			hp->so = NEXT(hp->so);
			i++;
		}
		qunlock(hp);
		if(i)
			wakeup(&hp->tr);
		sleep(&hp->kr, kRun, hp);
	}
}

static void
devinit(int h)
{
	Hifichan *hp = &hifichan[h];
	Hifi *hifi = (h&1) ? hifi1 : hifi0;
	Isdn *ip = &isdndev[h>>1];

	isdnlock(ip);

	hp->udev = ip;
	hp->hdev = hifi;
	hp->imask = (h&1) ? Hint1 : Hint0;

	hp->opreg = 0;
	SET(hifi->opreg, Rres|Tres);	/* master reset */
	SET(hifi->config, Fsen);	/* frame sync enable */
	hp->tctl = 30;
	SET(hifi->tctl, hp->tctl);	/* transmitter interrupt level */
	SET(hifi->rctl, 20);		/* receiver interrupt level */
	SET(hifi->bofctl, Clkxi);	/* transmit in rising edge of CLKX */
	/* SET(hifi->tofctl, Dxi);	/* transmit inverted data */
	SET(hifi->tofctl, 0);		/* transmit normal data */
	SET(hifi->rof_tm, Dri);		/* receive inverted data */
	SET(hifi->config, Fsen|Alt);	/* alternate reg's... */
	SET(hifi->rof_tm, Trans);	/* transparent mode */
	SET(hifi->config, Fsen);	/* normal reg's... */
	SET(hifi->tmask, 0xff);		/* transmit all bits */
	SET(hifi->imask, Undie|Tdie|Teie|Rfie|Reofie); /* interrupt enables */

	isdnunlock(ip);
}

static void
hifiloopctl(Hifichan *hp, int loop)
{
	Isdn *ip = hp->udev;
	Hifi *hifi = hp->hdev;

	DPRINT("hifiloopctl %d: %d\n", hp-hifichan, loop);
	isdnlock(ip);
	if(loop)
		SET(hifi->opreg, hp->opreg |= Lloop);
	else
		SET(hifi->opreg, hp->opreg &= ~Lloop);
	isdnunlock(ip);
}

static int
hifiintr(void)
{
	Isdn *ip;
	Hifichan *hp;
	Hifi *hifi;
	int intr = 0, status, n, c, wake = 0, w = 0;
	uchar *p;

	for(hp=hifichan; hp<hifichanN; hp++){
		if(!(ip = hp->udev))	/* assign = */
			continue;
		if (!(*(ip->asr)&hp->imask))
			continue;
		++intr;
		hifi = hp->hdev;
		status = GET(hifi->istat);
		if(status&(Rf|Reof)){
			status &= ~(Rf|Reof);
			wake += hifirecv(hp);
		}
		if(status&Te){
			status &= ~Te;
			wake += w = hifixmit(hp);
		}
		if(status&Undabt){
			status &= ~Undabt;
			DPRINT("hifiintr: hdlc %d under run\n", hp-hifichan);
			++hp->underrun;
		}
		if(status&Tdone){
			status &= ~Tdone;
			hp->wactive = 0;
			if(!hp->hdlc && (w || hp->out || hp->ro != hp->wo)){
				DPRINT("hifiintr: trans %d under run\n",
					hp-hifichan);
				++hp->underrun;
			}else if(!hp->hdlc || !hp->rq){
				DPRINT("hifiintr: xmtr %d done\n", hp-hifichan);
				hifixmitdis(hp, 0);
				++wake;
				wakeup(&hp->cr);
			}
		}
		if(wake)
			wakeup(&hp->kr);
		if(status)
			DPRINT("hifiintr: status %d 0x%2.2x\n",
				status, hp-hifichan);
	}
	return intr;
}

static int
hifirecv(Hifichan *hp)
{
	Isdn *ip = hp->udev;
	Hifi *hifi = hp->hdev;
	Block *bp = hp->inb[hp->wi];
	uchar *p;
	int status, i, n, m, wake = 0;
Loop:
	status = GET(hifi->rstat);
	if(status == 0)
		return wake;
	n = status&0x7f;
	p = bp->wptr;
	m = bp->lim - p;
	if(hp->hdlc){
		if(n > m){
			hp->toolong++;
			bp->wptr = bp->base;
			SET(hifi->opreg, hp->opreg | Rres);
			return wake;
		}
		hp->inchars += n;
		SETADDR(&hifi->data);
		while(--n >= 0)
			*p++ = *(ip->data);
		if(status&Eof){
			if(*--p == 0){
				i = NEXT(hp->wi);
				if(i == hp->ri)
					hp->overflow++;
				else{
					bp->wptr = p;
					bp->flags |= S_DELIM;
					hp->wi = i;
					bp = hp->inb[i];
					++wake;
				}
			}else{
				if(*p & 0x80)
					hp->badcrc++;
				if(*p & 0x40)
					hp->abort++;
				if(*p & 0x20)
					hp->overrun++;
				if(*p & 0x10)
					hp->badcount++;
			}
			bp->wptr = bp->base;
		}else
			bp->wptr = p;
	}else{
		if(n > m)
			n = m;
		hp->inchars += n;
		SETADDR(&hifi->data);
		while(--n >= 0)
			*p++ = *(ip->data);
		bp->wptr = p;
		if(p >= bp->lim){
			i = NEXT(hp->wi);
			if(i == hp->ri){
				bp->wptr = bp->base;
				return wake;
			}else{
				bp->flags |= S_DELIM;
				hp->wi = i;
				bp = hp->inb[i];
				++wake;
			}
		}
	}
	goto Loop;
}

static int
hifixmit(Hifichan *hp)
{
	Isdn *ip = hp->udev;
	Hifi *hifi = hp->hdev;
	Block *bp;
	uchar *p;
	int n, k, wake = 0;

Loop:
	n = GET(hifi->tstat)&0x7f;
	if(n == 0)
		return wake;
	if(!(bp = hp->out)){	/* assign = */
		if(hp->ro == hp->wo){
			DPRINT("hifixmit %d: [%d] empty\n",
				TK2MS(MACHP(0)->ticks), hp-hifichan);
			return wake;
		}
		hp->out = bp = hp->outb[hp->ro];
	}
	p = bp->rptr;
	k = bp->wptr - p;
	if (n > k)
		n = k;
	hp->outchars += n;
	SETADDR(&hifi->data);
	if(hp->hdlc){
		while(--n >= 0)
			*(ip->data) = *p++;
	}else{
		while(--n >= 0)
			*(ip->data) = ~*p++;
	}
	bp->rptr = p;
	hp->wactive = 1;
	if(!hp->wenable)
		hifixmiten(hp, 0);
	if(bp->rptr == bp->wptr){
		if(hp->hdlc && (bp->flags & S_DELIM))
			SET(hifi->tctl, hp->tctl|Tfc);
		if(!(hp->out = bp = bp->next)){	/* assign = */
			hp->ro = NEXT(hp->ro);
			++wake;
		}
	}
	goto Loop;
}

static void
hifixmiten(Hifichan *hp, int dolock)
{
	Isdn *ip = hp->udev;
	Hifi *hifi = hp->hdev;

	DPRINT("hifixmiten %d: %s\n", hp-hifichan, hp->hdlc?"hdlc":"trans");
	if(dolock)
		isdnlock(ip);
	if(hp->hdlc){
		SET(hifi->tofctl, Dxi|Tlbit);	/* xmit ~data, lsb first */
		SET(hifi->rof_tm, Dri|Rlbit);	/* rcv ~data, lsb first */
		SET(hifi->config, Fsen|Alt);	/* alternate reg's... */
		SET(hifi->rof_tm, 0);		/* hdlc mode */
		SET(hifi->config, Fsen|Flags);	/* normal reg's, send flags */
	}else{
		SET(hifi->tofctl, 0);		/* xmit data, msb first */
		SET(hifi->rof_tm, Dri);		/* rcv ~data, msb first */
		SET(hifi->config, Fsen|Alt);	/* alternate reg's... */
		SET(hifi->rof_tm, Trans);	/* transparent mode */
		SET(hifi->config, Fsen);	/* normal reg's, send ones */
	}
	hp->wenable = 1;
	if(hp->hdev == hifi0){
		ip->venval &= ~DXCODEC;
		if(hp->hdlc)
			ip->venval &= ~DRCODEC;
	}else if(hp->hdlc)
		SET(hifi->config, Flags);	/* no fs on chan. 2 */
	SET(hifi->ttsctl, Dxac);
	SET(hifi->opreg, hp->opreg |= Ent);
	if(dolock)
		isdnunlock(ip);
}

static void
hifixmitdis(Hifichan *hp, int dolock)
{
	Isdn *ip = hp->udev;
	Hifi *hifi = hp->hdev;

	DPRINT("hifixmitdis %d\n", hp-hifichan);
	if(dolock)
		isdnlock(ip);
	SET(hifi->ttsctl, 0);
	if(hp->hdev == hifi0){
		ip->venval |= DXCODEC;
		if(hp->hdlc)
			ip->venval |= DRCODEC;
	}
	SET(hifi->opreg, hp->opreg &= ~Ent);
	hp->out = 0;
	hp->ro = hp->wo;
	hp->wenable = 0;
	/*SET(hifi->opreg, hp->opreg | Tres);*/
	if(dolock)
		isdnunlock(ip);
}

A gnot/devisdn.c => gnot/devisdn.c +711 -0
@@ 0,0 1,711 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"devtab.h"

#include	"io.h"
#ifdef	SET
#undef	SET
#endif
#include	"isdn.h"
#include	"unite.h"

#define DPRINT 	if(isdndebug)kprint

#define	BSIZE	260

Isdn *	isdndev;
Isdn *	isdndevN;

static void	devinit(int);
static void	devctl(Isdn*, int, int);
static void	devlctl(Isdn*, int);
static void	isdnkproc(void*);
static int	isdnintr(void);
static int	isdnInfo(Isdn*);
static int	isdnrecv(Isdn*);
static int	isdnxmit(Isdn*);

enum {
	Qdir, Qloop, QInfo, Qstats, Qdev, Qdebug
};

Dirtab isdndir[]={
	"loop",		{Qloop},	5,	0666,
	"info",		{QInfo},	1,	0666,
	"stats",	{Qstats},	0,	0444,
	"dev",		{Qdev},		0,	0666,
	"debug",	{Qdebug},	0,	0666,
};
#define	NISDN	(sizeof isdndir/sizeof(Dirtab))

/*
 *  I.430 stream module definition
 */
static void isdniput(Queue*, Block*);
static void isdnoput(Queue*, Block*);
static void isdnstopen(Queue*, Stream*);
static void isdnstclose(Queue*);
Qinfo isdninfo = { isdniput, isdnoput, isdnstopen, isdnstclose, "isdn" };

int	venval;
int	isdndebug;

void
isdnreset(void)
{
	isdndev = ialloc(conf.nisdn*sizeof(Isdn), 0);
	isdndevN = &isdndev[conf.nisdn];
}

void
isdninit(void)
{}

Chan*
isdnattach(char *spec)
{
	Chan *c;
	int dev;

	if(spec && *spec){
		if(*spec < '0' || *spec > '9')
			error(Enonexist);
		dev = strtoul(spec, 0, 0);
	}else
		dev = 0;
	if(dev >= conf.nisdn)
		error(Enonexist);
	if(!isdndev[dev].asr){
		addportintr(isdnintr);
		devinit(dev);
	}
	c = devattach('I', spec);
	c->dev = dev;
	return c;
}

Chan*
isdnclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int	 
isdnwalk(Chan *c, char *name)
{
	return devwalk(c, name, isdndir, NISDN, streamgen);
}

void	 
isdnstat(Chan *c, char *dp)
{
	devstat(c, dp, isdndir, NISDN, streamgen);
}

Chan*
isdnopen(Chan *c, int omode)
{
	if(c->qid.path == CHDIR){
		if(omode != OREAD)
			error(Eperm);
	}else switch(STREAMTYPE(c->qid.path)){
	case Qloop:
	case QInfo:
	case Qstats:
	case Qdev:
	case Qdebug:
		break;
	default:
		DPRINT("isdnopen dev=%d\n", c->dev);
		streamopen(c, &isdninfo);
	}
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void	 
isdncreate(Chan *c, char *name, int omode, ulong perm)
{
	error(Eperm);
}

void	 
isdnclose(Chan *c)
{
	if(c->qid.path != CHDIR)
		streamclose(c);
}

long	 
isdnread(Chan *c, void *buf, long n, ulong offset)
{
	Isdn *ip = &isdndev[c->dev];
	char nbuf[512]; int k;

	if(n <= 0)
		return 0;
	if(c->qid.path == CHDIR){
		return devdirread(c, buf, n, isdndir, NISDN, streamgen);
	}else switch(STREAMTYPE(c->qid.path)){
	case Qloop:
		k = sprint(nbuf, "0x%2.2ux ", ip->lctl);
		goto Readnbuf;
	case QInfo:
		if(offset>0)
			return 0;
		k = isdnpeek(ip, &unite->listat)&Rss;
		*(char *)buf = "024L"[k>>2];
		return 1;
	case Qstats:
		k = 0;
		k += sprint(&nbuf[k], "inchars  %10lud\n", ip->inchars);
		k += sprint(&nbuf[k], "badcrc   %10lud\n", ip->badcrc);
		k += sprint(&nbuf[k], "abort    %10lud\n", ip->abort);
		k += sprint(&nbuf[k], "overrun  %10lud\n", ip->overrun);
		k += sprint(&nbuf[k], "badcount %10lud\n", ip->badcount);
		k += sprint(&nbuf[k], "overflow %10lud\n", ip->overflow);
		k += sprint(&nbuf[k], "toolong  %10lud\n", ip->toolong);
		k += sprint(&nbuf[k], "underrun %10lud\n", ip->underrun);
		k += sprint(&nbuf[k], "outchars %10lud\n", ip->outchars);
		goto Readnbuf;
	case Qdev:
		k = isdnpeek(ip, (void *)ip->devaddr);
		k = sprint(nbuf, "0x%2.2ux = 0x%2.2ux\n", ip->devaddr, k);
		goto Readnbuf;
	case Qdebug:
		k = sprint(nbuf, "%d\n", isdndebug);
	Readnbuf:
		if (offset >= k)
			return 0;
		if (offset+n > k)
			n = k - offset;
		memmove(buf, nbuf+offset, n);
		return n;
	}
	return streamread(c, buf, n);
}

long	 
isdnwrite(Chan *c, void *buf, long n, ulong offset)
{
	Isdn *ip = &isdndev[c->dev];
	char nbuf[32], *p;

	if(n <= 0)
		return 0;
	switch(STREAMTYPE(c->qid.path)){
	case Qloop:
		if(offset>0)
			return 0;
		if (n>sizeof nbuf)
			n = sizeof nbuf;
		memmove(nbuf, buf, n);
		nbuf[n-1] = 0;
		devlctl(ip, strtoul(nbuf,0,0));
		return n;
	case QInfo:
		if(offset>0)
			return 0;
		switch(*(char *)buf){
		case '0':
		case '1':
		case '3':
			devctl(ip, Tss, *(char *)buf - '0');
			break;
		default:
			error(Ebadarg);
		}
		return 1;
	case Qdev:
		if (n>sizeof nbuf)
			n = sizeof nbuf;
		memmove(nbuf, buf, n);
		nbuf[n-1] = 0;
		ip->devaddr = strtoul(nbuf,0,0);
		if (p = strchr(nbuf, '='))	/* assign = */
			isdnpoke(ip, (void *)ip->devaddr, strtoul(++p,0,0));
		return n;
	case Qdebug:
		if (n>sizeof nbuf)
			n = sizeof nbuf;
		memmove(nbuf, buf, n);
		nbuf[n-1] = 0;
		isdndebug = strtoul(nbuf,0,0);
		return n;
	}
	return streamwrite(c, buf, n, 0);
}

void	 
isdnremove(Chan *c)
{
	error(Eperm);
}

void	 
isdnwstat(Chan *c, char *dp)
{
	error(Eperm);
}

/*
 *	to sleep, perchance to dream...
 */

#define	Predicate(name, expr)	static int name(void *arg) { return expr; }
#define	ip	((Isdn *)arg)

Predicate(kLive, ip->kstart != 0)
Predicate(kDead, ip->kstart == 0)

Predicate(kRun, !ip->rq || ip->ri != ip->wi || ip->so != ip->ro)

Predicate(lActive, (ip->listat&Rss)>>1 == 4)

Predicate(tEmpty, ip->ro == ip->wo)
Predicate(tFull, NEXT(ip->wo) == ip->so)
Predicate(tNotFull, NEXT(ip->wo) != ip->so)

#undef	ip

/*
 *	the stream routines
 */

static void
isdnstopen(Queue *q, Stream *s)
{
	Isdn *ip = &isdndev[s->dev];
	char name[32];
	int rss;

	DPRINT("isdnstopen %d...", s->dev);
	qlock(&ip->kctl);
	if (ip->kstart) {
		qunlock(&ip->kctl);
		DPRINT("active\n", s->dev);
		return;
	}
	q->ptr = q->other->ptr = ip;
	ip->rq = q;
	rss = (isdnpeek(ip, &unite->listat)&Rss)>>1;
	if (ip->lctl & Lld)		/* if loopback D, */
		devctl(ip, Tss, 0);	/* transmit INFO 0 */
	else if (rss == 2 || rss ==4)	/* else if line active */
		devctl(ip, Tss, 3);	/* transmit INFO 3 */
	else
		devctl(ip, Tss, 1);	/* transmit INFO 1 */
	sprint(name, "isdn%d", s->dev);
	kproc(name, isdnkproc, ip);
	if (waserror()) {
		qunlock(&ip->kctl);
		DPRINT("aborted\n");
		nexterror();
	}
	sleep(&ip->kctlr, kLive, ip);
	qunlock(&ip->kctl);
	poperror();
	DPRINT("started\n");
	if (!(ip->lctl & Lld)) {
		DPRINT("waiting for INFO 4\n");
		sleep(&ip->ar, lActive, ip);
		DPRINT("got INFO 4\n");
	}
}

static void
isdnstclose(Queue * q)
{
	Isdn *ip = (Isdn *)q->ptr;
	DPRINT("isdnstclose %d...", ip-isdndev);
	qlock(&ip->kctl);
	qlock(ip);
	ip->rq = 0;
	qunlock(ip);
	wakeup(&ip->kr);
	if (waserror()) {
		qunlock(&ip->kctl);
		DPRINT("aborted\n");
		nexterror();
	}
	sleep(&ip->kctlr, kDead, ip);
	qunlock(&ip->kctl);
	poperror();
	DPRINT("stopped\n");
}

static void
showframe(char *t, uchar *buf, int n)
{
	kprint("I %s [", t);
	while (--n >= 0)
		kprint(" %2.2ux", *buf++);
	kprint(" ]\n");
}

/*
 *  this is only called by hangup
 */
static void
isdniput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

/*
 *  output a block
 */
static void
isdnoput(Queue *q, Block *bp)
{
	Isdn *ip = (Isdn *)q->ptr;
	uchar set, clr;

	if(bp->type != M_DATA){
		set = clr = 0;
		if(streamparse("ear", bp)){
			if(streamparse("on", bp))
				set = DRCODEC;
			else if(streamparse("off", bp))
				clr = DRCODEC;
		}else if(streamparse("mic", bp)){
			if(streamparse("on", bp))
				set = DXCODEC;
			else if(streamparse("off", bp))
				clr = DXCODEC;
		}
		freeb(bp);
		isdnlock(ip);
		ip->venval &= ~clr;
		ip->venval |= set;
		*(ip->asr) = ip->venval;
		isdnunlock(ip);
		return;
	}
	qlock(&ip->wlock);
	if(!putq(q, bp)){	/* send only whole messages */
		qunlock(&ip->wlock);
		return;
	}
	DPRINT("isdnoput: len=%d\n", q->len);
	if (tFull(ip))
		sleep(&ip->tr, tNotFull, ip);
	ip->outb[ip->wo] = grabq(q);
	ip->wo = NEXT(ip->wo);
	qunlock(&ip->wlock);

	isdnlock(ip);
	if (isdnxmit(ip))
		wakeup(&ip->kr);
	isdnunlock(ip);
}

static void
isdnkproc(void *arg)
{
	Isdn *ip = (Isdn *)arg;
	int i;

	if (waserror()) {
		devctl(ip, Enr, 0);
		ip->kstart = 0;
		wakeup(&ip->kctlr);
		return;
	}
	/*
	 *  create a number of blocks for input
	 */
	for (i=0; i<NB; i++)
		if (!ip->inb[i])
			ip->inb[i] = allocb(BSIZE);
	ip->ri = 0;
	ip->wi = 0;
	devctl(ip, 0, Enr);
	ip->kstart = 1;
	wakeup(&ip->kctlr);
	for (;;) {
		qlock(ip);
		if (!ip->rq) {
			qunlock(ip);
			break;
		}
		while (ip->ri != ip->wi) {
			PUTNEXT(ip->rq, ip->inb[ip->ri]);
			ip->inb[ip->ri] = allocb(BSIZE);
			ip->ri = NEXT(ip->ri);
		}
		i = 0;
		while (ip->so != ip->ro) {
			freeb(ip->outb[ip->so]);
			ip->so = NEXT(ip->so);
			i++;
		}
		qunlock(ip);
		if (i)
			wakeup(&ip->tr);
		sleep(&ip->kr, kRun, ip);
	}
	devctl(ip, Enr, 0);
	sleep(&ip->kr, tEmpty, ip);
	while (ip->so != ip->ro) {
		freeb(ip->outb[ip->so]);
		ip->so = NEXT(ip->so);
	}
	poperror();
	ip->kstart = 0;
	wakeup(&ip->kctlr);
}

void
isdnlock(Isdn *ip)
{
	int s;

	s = spl2();
	lock(&ip->dev);
	ip->pri = s;
}

void
isdnunlock(Isdn *ip)
{
	int s;

	s = ip->pri;
	unlock(&ip->dev);
	splx(s);
}

static void
devinit(int h)
{
	Isdn *ip = &isdndev[h];

	isdnlock(ip);
	ip->asr = ISDNDEV0+2*h;
	ip->data = ip->asr+1;
	ip->venval = DRCODEC | DXCODEC;
	SET(unite->reset, Mres);
	ip->lctl = 0;		/* no loopback or 1's transmission */
	SET(unite->lctl, ip->lctl);
	SET(unite->stictl, Prye); /* S/T interface normal */
	SET(unite->itl, 0x88);	/* interrupt on 8 rcv/xmit char's */
	SET(unite->hcr, Tpol|Ipol|Clkmux|B2f|B1f);
	ip->tctl = Ent;		/* enable transmitter, send INFO 0 */
	ip->imask = Richgie|Teie;
	SET(unite->tctl, ip->tctl);
	SET(unite->imask, ip->imask);
	ip->listat = GET(unite->listat);
	isdnunlock(ip);
}

static void
devctl(Isdn *ip, int clear, int set)
{
	isdnlock(ip);
	ip->tctl &= ~clear;
	ip->tctl |= set;
	if (clear & Ent)
		ip->imask &= ~Teie;
	if (set & Ent)
		ip->imask |= Teie;
	if (clear & Enr)
		ip->imask &= ~(Rfie|Reofie);
	if (set & Enr)
		ip->imask |= (Rfie|Reofie);
	ip->imask |= Richgie;
	SET(unite->tctl, ip->tctl);
	SET(unite->imask, ip->imask);
	isdnunlock(ip);
}

int
isdnpeek(Isdn *ip, void *arg)
{
	int r;

	isdnlock(ip);
	SETADDR(arg);
	r = *(ip->data);
	isdnunlock(ip);
	return r;
}

void
isdnpoke(Isdn *ip, void *arg, int data)
{
	isdnlock(ip);
	SETADDR(arg);
	*(ip->data) = data;
	isdnunlock(ip);
}

static void
devlctl(Isdn *ip, int val)
{
	isdnlock(ip);
	ip->lctl = val;
	SET(unite->lctl, val);
	if (val & Lld)
		SET(unite->stictl, Clrmm);
	else
		SET(unite->stictl, Prye);
	isdnunlock(ip);
}

static int
isdnintr(void)
{
	int status, wake = 0, intr = 0;
	Isdn *ip;

	for(ip=isdndev; ip<isdndevN; ip++){
		if (!ip->asr)
			continue;
		if (!(*(ip->asr)&Uint))
			continue;
		++intr;
		status = GET(unite->istat);
		if (status & Richg)
			wake += isdnInfo(ip);
		if (status & (Rfull|Reof))
			wake += isdnrecv(ip);
		if (status & Tempty)
			wake += isdnxmit(ip);
		if (wake)
			wakeup(&ip->kr);
	}
	return intr;
}

static int
isdnInfo(Isdn *ip)
{
	int newstat;

	newstat = GET(unite->listat);
	DPRINT("isdnInfo: old=0x%2.2ux, new=0x%2.2ux\n",
		ip->listat, newstat);
	ip->listat = newstat;
	if (!ip->rq) {
		ip->imask &= ~Richgie;
		SET(unite->imask, ip->imask);
		return 0;
	}
	newstat = (newstat&Rss)>>1;
	ip->tctl &= ~Tss;
	if (newstat == 2 || newstat == 4)
		ip->tctl |= 3;	/* transmit INFO 3 */
	else
		ip->tctl |= 1;	/* transmit INFO 1 */
	SET(unite->tctl, ip->tctl);
	DPRINT("isdnInfo: tctl=0x%2.2ux\n", ip->tctl);
	if (newstat == 4)
		wakeup(&ip->ar);
	return 0;
}

static int
isdnrecv(Isdn *ip)
{
	Block *bp = ip->inb[ip->wi];
	uchar status, *p;
	int i, n, wake = 0;
Loop:
	status = GET(unite->rstat);
	if (status&Overrun) {
		ip->overrun++;
		goto Reset;
	}
	if (!(n = status&Rqs))
		return wake;
	p = bp->wptr;
	if (p+n > bp->lim) {
		ip->toolong++;
		goto Reset;
	}
	ip->inchars += n;
	SETADDR(&unite->data);
	while (--n >= 0)
		*p++ = *(ip->data);
	if (status&Eof) {
		if (*--p == 0) {
			i = NEXT(ip->wi);
			if (i == ip->ri)
				ip->overflow++;
			else {
				bp->wptr = p;
				bp->flags |= S_DELIM;
				ip->wi = i;
				bp = ip->inb[i];
				++wake;
			}
		} else {
			if (*p & 0x80)
				ip->badcrc++;
			if (*p & 0x40)
				ip->abort++;
			if (*p & 0x20)
				ip->overrun++;
			if (*p & 0x10)
				ip->badcount++;
		}
		bp->wptr = bp->base;
	} else
		bp->wptr = p;
	goto Loop;
Reset:
	bp->wptr = bp->base;
	SET(unite->reset, Rres);
	return wake;
}

static int
isdnxmit(Isdn *ip)
{
	Block *bp;
	uchar status, *p;
	int n, k, wake = 0;
Loop:
	status = GET(unite->tstat);
	if (status&Undabt) {
		ip->underrun++;
		SET(unite->reset, Tres);
		if (ip->out) {
			ip->ro = NEXT(ip->ro);
			ip->out = 0;
			++wake;
		}
	}
	if (!(n = status&Tqs))
		return wake;
	if (!(bp = ip->out)) {
		if (ip->ro == ip->wo)
			return wake;
		ip->out = bp = ip->outb[ip->ro];
	}
	p = bp->rptr;
	k = bp->wptr - p;
	if (n > k)
		n = k;
	ip->outchars += n;
	SETADDR(&unite->data);
	while (--n >= 0)
		*(ip->data) = *p++;
	bp->rptr = p;
	if (bp->rptr == bp->wptr) {
		if (bp->flags & S_DELIM)
			SET(unite->tctl, ip->tctl|Tfc);
		if (!(ip->out = bp = bp->next)) {
			ip->ro = NEXT(ip->ro);
			++wake;
		}
	}
	goto Loop;
}

A gnot/hifi.h => gnot/hifi.h +106 -0
@@ 0,0 1,106 @@
/* AT&T T7121 HDLC Interface for ISDN (HIFI-64) */

enum Hifi_r0 {		/* Chip Configuration Register */
	Dint	= Bit(0),
	Ipol	= Bit(1),
	Flags	= Bit(2),
	Bm	= Bit(3),
	Alt	= Bit(4),
	Fe	= Bit(5),
	Fspol	= Bit(6),
	Fsen	= Bit(7)
};

enum Hifi_r1 {		/* Transmitter Control Register */
	Til	= Bits(0,5),
	Tabt	= Bit(6),
	Tfc	= Bit(7)
};

enum Hifi_r2 {		/* Transmitter Status Register */
	Tqs	= Bits(0,6),
	Ted	= Bit(7)
};

enum Hifi_r4 {		/* Receiver Status Register */
	Rqs	= Bits(0,6),
	Eof	= Bit(7)
};

enum Hifi_r5 {		/* Receiver Control Register */
	Ril	= Bits(0,5),
	P21ctl	= Bit(6),
	P17ctl	= Bit(7)
};

enum Hifi_r6 {		/* Operation Control Register */
	Rloop	= Bit(0),
	Lloop	= Bit(1),
	Enr	= Bit(2),
	Ent	= Bit(3),
	Rres	= Bit(4),
	Tres	= Bit(5),
	Tristate= Bit(6),
	Pdwn	= Bit(7)
};

enum Hifi_r7 {		/* Transmit Time Slot Control Register */
	Tslt	= Bits(0,5),
	Dxbc	= Bit(6),
	Dxac	= Bit(7)
};

enum Hifi_r8 {		/* Receiver Time Slot Control Register */
	Rslt	= Bits(0,5),
	Iom2	= Bit(6),
	Drab	= Bit(7)
};

enum Hifi_r9 {		/* Bit Offset Control Register */
	Clkri	= Bit(0),
	Rbof	= Bits(1,3),
	Clkxi	= Bit(4),
	Tbof	= Bits(5,7),
};

enum Hifi_r10 {		/* Transmitter Time Slot Offset Control Register */
	Ttsof	= Bits(0,5),
	Tlbit	= Bit(6),
	Dxi	= Bit(7)
};

enum Hifi_r11 {		/* Receiver Time Slot Offset Control Register */
	Rtsof	= Bits(0,5),
	Rlbit	= Bit(6),
	Dri	= Bit(7)
};

enum Hifi_ar11 {	/* Transparent Mode Control Register */
	Octof	= Bits(0,2),
	Mstat	= Bit(3),
	Aloct	= Bit(4),
	Match	= Bit(5),
	Trans	= Bit(6),
	Test	= Bit(7)
};

enum Hifi_r14 {		/* Interrupt Mask Register */
	Tdie	= Bit(0),
	Teie	= Bit(1),
	Undie	= Bit(2),
	Rfie	= Bit(3),
	Reofie	= Bit(4),
	Rovie	= Bit(5),
	Riie	= Bit(6),
	Tbcrc	= Bit(7)
};

enum Hifi_r15 {		/* Interrupt Status Register */
	Tdone	= Bit(0),
	Te	= Bit(1),
	Undabt	= Bit(2),
	Rf	= Bit(3),
	Reof	= Bit(4),
	Overun	= Bit(5),
	Ridl	= Bit(6)
};

A gnot/isdn.h => gnot/isdn.h +137 -0
@@ 0,0 1,137 @@
typedef struct Isdn	Isdn;
typedef struct Unite	Unite;
typedef struct Hifi	Hifi;

/*
struct Isdndev {
	uchar	asr;
	uchar	data;
};
*/

#define	ISDNDEV0	((uchar *)&PORT[52])	/* the first one */

#define	Bit(n)		(1<<(n))

#define	Bits(m,n)	(((1<<((n)-(m)+1))-1)<<(m))

enum Isdn_asr {		/* Board status */
	Uint	= Bit(0),
	Hint0	= Bit(1),
	Hint1	= Bit(2),
	DRCODEC = Bit(6),	/* enable codec receiver */
	DXCODEC = Bit(7)	/* enable codec transmitter */
};

/*
 * Board addresses
 */

#define	unite	((Unite *)(0*Bit(4)))
#define	hifi0	((Hifi *) (2*Bit(4)))
#define	hifi1	((Hifi *) (3*Bit(4)))

struct Unite {
	uchar	config;	/* Chip/Hardware Configuration Register */
	uchar	listat;	/* Line Interface Status */
	uchar	tctl;	/* Transmitter Control Register */
	uchar	data;	/* D-channel data register */
	uchar	hcr;	/* Hardware Configuration Register */
	uchar	rstat;	/* HDLC Receiver Status */
	uchar	itl;	/* Interrupt Trigger Levels for D-Channel Queues */
	uchar	mfstat;	/* Multiframe Status and S-Channel Queues */
	uchar	stictl;	/* S/T Interface control */
	uchar	imask;	/* Interrupt masks */
	uchar	istat;	/* Interrupt Status */
	uchar	qstat;	/* Q-Channel Data and Status */
	uchar	lctl;	/* Loopback and Transmit 1's Control */
	uchar	timctl;	/* Timer Configuration Control */
	uchar	tstat;	/* Transmitter Queue Status */
	uchar	reset;	/* Software Resets */
};

struct Hifi {
	uchar	config;	/* Chip Configuration Register */
	uchar	tctl;	/* Transmitter Control Register */
	uchar	tstat;	/* Transmitter Status Register */
	uchar	data;	/* r3 is the Data Byte Register. */
	uchar	rstat;	/* Receiver Status Register */
	uchar	rctl;	/* Receiver Control Register */
	uchar	opreg;	/* Operation Register */
	uchar	ttsctl;	/* Transmitter Time Slot Control Register */
	uchar	rtsctl;	/* Receiver Time Slot Control Register */
	uchar	bofctl;	/* Bit Offset Control Register */
	uchar	tofctl;	/* Transmitter Time Slot Offset Control Register */
	uchar	rof_tm;	/* Rcvr Time Slot Offset/Trans. Mode Control Register */
	uchar	rmask;	/* Receiver Mask/Character Register. */
	uchar	tmask;	/* Transmitter Mask/Idle Char. Register. */
	uchar	imask;	/* Interrupt Mask Register */
	uchar	istat;	/* Interrupt Status Register */
};

/*
 *	usage: SET(unite->chcr, C2pol);
 */

#define	SETADDR(addr)	(*(ip->asr) = ip->venval | ((int)(addr)))

#define	GET(lval)	(SETADDR(&(lval)), *(ip->data))

#define	SET(lval, rval)	(SETADDR(&(lval)), *(ip->data)=(rval))

#define	NBMASK	0x0f
#define	NB	(NBMASK+1)
#define	NEXT(a)	(((a)+1)&NBMASK)

struct Isdn
{
	QLock;			/* access to struct */
	QLock	kctl;		/* kproc start/stop */
	Rendez	kctlr;
	int	kstart;		/* set if kernel process started */
	Rendez	kr;		/* kernel process sleep/wakeup */
	Rendez	ar;		/* wait for active link (INFO 4) */

	int	pri;		/* priority at time of lock(&dev) */
	Lock	dev;		/* access to device */
	uchar	venval;
	uchar *	asr;
	uchar *	data;
	uchar	listat;		/* previous line interface status */
	uchar	tctl;		/* transmitter control */
	uchar	imask;		/* interrupt mask */
	uchar	lctl;		/* loopback control */
	uchar	devaddr;	/* debug access */

	Block *	inb[NB];	/* input buffer */
	int	ri;		/* input read pointer */
	int	wi;		/* input write pointer */
	Queue *	rq;		/* read queue */

	QLock	wlock;		/* write's are atomic */
	Block *	outb[NB];	/* output buffer */
	int	so;		/* output scavenge pointer */
	int	ro;		/* output read pointer */
	int	wo;		/* output write pointer */
	Block *	out;		/* current output block */
	Rendez	tr;		/* if transmitter must wait */

	/* statistics */

	ulong	inchars;
	ulong	badcrc;
	ulong	abort;
	ulong	overrun;
	ulong	badcount;
	ulong	overflow;
	ulong	toolong;
	ulong	underrun;
	ulong	outchars;
};

extern Isdn *	isdndev;
extern void	isdnlock(Isdn*);
extern int	isdnpeek(Isdn*, void*);
extern void	isdnpoke(Isdn*, void*, int);
extern void	isdnunlock(Isdn*);
extern int	spl2(void);

A gnot/unite.h => gnot/unite.h +120 -0
@@ 0,0 1,120 @@
/* AT&T T7250A User Network Interface for Terminal Equipment (UNITE) */

enum Unite_r0 {		/* Chip/Hardware Configuration Register */
	Ckdm	= Bit(1),
	C2pol	= Bit(2),
	C1pol	= Bit(3),
	Dynrd	= Bit(4),
};

enum Unite_r1 {		/* Line Interface Status */
	Prss	= Bits(0,1),
	Rss	= Bits(2,3),
};

enum Unite_r2 {		/* Transmitter Control Register */
	Tss	= Bits(0,1),
	Tabt	= Bit(2),
	Tfc	= Bit(3),
	Tinv	= Bit(4),
	Rinv	= Bit(5),
	Ent	= Bit(6),
	Enr	= Bit(7)
};

enum Unite_r4 {		/* Hardware Configuration Register */
	Tpol	= Bit(0),
	Ipol	= Bit(1),
	Clkmux	= Bit(2),
	B2f	= Bit(3),
	B1f	= Bit(4),
	Syscko	= Bit(5),
	Codec2	= Bit(6),
	Codec1	= Bit(7)
};

enum Unite_r5 {		/* HDLC Receiver Status */
	Rqs	= Bits(0,4),
	Overrun	= Bit(5),
	Ridl	= Bit(6),
	Eof	= Bit(7)
};

enum Unite_r6 {		/* Interrupt Trigger Levels for D-Channel Queues */
	Tel	= Bits(0,3),
	Rfl	= Bits(4,7)
};

enum Unite_r7 {		/* Multiframe Status and S-Channel Queues */
	Sdata	= Bits(0,4),
	Mstate	= Bits(5,6),
	Mse	= Bit(7)
};

enum Unite_r8 {		/* S/T Interface control */
	Clrmm	= Bit(0),
	B1xb2	= Bit(1),
	Pry	= Bit(2),
	Prye	= Bit(3),
	Lpry	= Bit(4),
	Ssm	= Bit(5),
	B2i	= Bit(6),
	B1i	= Bit(7)
};

enum Unite_r9 {		/* Interrupt masks */
	Mmie	= Bit(0),
	Richgie	= Bit(1),
	Qdie	= Bit(2),
	Ssie	= Bit(3),
	Tdie	= Bit(4),
	Teie	= Bit(5),
	Rfie	= Bit(6),
	Reofie	= Bit(7)
};

enum Unite_r10 {	/* Interrupt Status */
	Mm	= Bit(0),
	Richg	= Bit(1),
	Qdone	= Bit(2),
	Ss	= Bit(3),
	Tdone	= Bit(4),
	Tempty	= Bit(5),
	Rfull	= Bit(6),
	Reof	= Bit(7)
};

enum Unite_r11 {	/* Q-Channel Data and Status */
	Qdata	= Bits(0,3),
	Enflg	= Bit(4),
	Doneq	= Bit(5),
	Qse	= Bit(7)
};

enum Unite_r12 {	/* Loopback and Transmit 1's Control */
	Rlb2	= Bit(0),
	Rlb1	= Bit(1),
	Rld	= Bit(2),
	Llb2	= Bit(3),
	Llb1	= Bit(4),
	Lld	= Bit(5),
	T1b2	= Bit(6),
	T1b1	= Bit(7)
};

enum Unite_r13 {	/* Timer Configuration Control */
	Tim	= Bits(0,3),
	Tm	= Bits(4,7)
};

enum Unite_r14 {	/* Transmitter Queue Status */
	Tqs	= Bits(0,4),
	Undabt	= Bit(5),
};

enum Unite_r15 {	/* Software Resets */
	Mres	= Bit(0),
	Rres	= Bit(1),
	Tres	= Bit(2),
	Tcrcb	= Bit(4),
};

M pc/fns.h => pc/fns.h +0 -1
@@ 7,7 7,6 @@ void	bootargs(ulong);
void	clock(Ureg*);
void	clockinit(void);
void	config(int);
void	confinit1(void);
int	cpuspeed(int);
void	delay(int);
void	dmaend(int);

M pc/main.c => pc/main.c +1 -1
@@ 278,7 278,7 @@ confinit(void)
	conf.nfloppy = 2;
	conf.nhard = 1;
	conf.dkif = 1;
	confinit1();
	confinit1(mul);
}

char *mathmsg[] =

M pc/trap.c => pc/trap.c +12 -6
@@ 343,7 343,15 @@ syscall(Ureg *ur)

	u->p->insyscall = 0;
	u->p->psstate = 0;

	/*
	 *  Put return value in frame.  On the safari the syscall is
	 *  just another trap and the return value from syscall is
	 *  ignored.  On other machines the return value is put into
	 *  the results register by caller of syscall.
	 */
	ur->ax = ret;

	if(ax == NOTED)
		noted(ur, *(ulong*)(sp+BY2WD));



@@ 432,9 440,7 @@ noted(Ureg *ur, ulong arg0)
{
	Ureg *nur;

	nur = u->ureg;
	validaddr(nur->pc, 1, 0);
	validaddr(nur->usp, BY2WD, 0);
	nur = u->ureg;		/* pointer to user returned Ureg struct */
	if(nur->cs!=u->svcs || nur->ss!=u->svss
	|| (nur->flags&0xff00)!=(u->svflags&0xff00)){
		pprint("bad noted ureg cs %ux ss %ux flags %ux\n", nur->cs, nur->ss,


@@ 449,8 455,8 @@ noted(Ureg *ur, ulong arg0)
		return;
	}
	u->notified = 0;
	nur->flags = (u->svflags&0xffffff00) | (ur->flags&0xff);
	memmove(ur, u->ureg, sizeof(Ureg));
	nur->flags = (u->svflags&0xffffff00) | (nur->flags&0xff);
	memmove(ur, nur, sizeof(Ureg));
	switch(arg0){
	case NCONT:
		if(waserror()){


@@ 458,7 464,7 @@ noted(Ureg *ur, ulong arg0)
			qunlock(&u->p->debug);
			goto Die;
		}
		validaddr(nur->pc, 1, 0);
		validaddr(nur->pc, 1, 0);	/* check for valid pc & usp */
		validaddr(nur->usp, BY2WD, 0);
		poperror();
		qunlock(&u->p->debug);

A port/devconc.c => port/devconc.c +489 -0
@@ 0,0 1,489 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#include	"fcall.h"

#define	concdebug	1

#define	DPRINT	if(concdebug)kprint

typedef struct Conc	Conc;
typedef struct Subdev	Subdev;

/*
 *  A concentrator.  One exists for every stream that a 
 *  conc line discipline is pushed onto.
 */
struct Conc
{
	QLock;
	Stream *s;
	Queue *	wq;
	char	name[64];
	int	ndev;
	Subdev *dev;
	Block *	bp;
};

struct Subdev
{
	Conc *	cp;
	int	chan0;
	Queue *	rq;
};

static Lock	conclock;
static Conc	*concs;

/*
 * Concentrator line discipline (push here).
 */

static void concstopen(Queue*, Stream*);
static void concstclose(Queue*);
static void concstoput(Queue*, Block*);
static void concstiput(Queue*, Block*);
Qinfo concinfo =
{
	concstiput,
	concstoput,
	concstopen,
	concstclose,
	"conc"
};

/*
 * Stream interface to concentrator device (open here).
 */

static void concdevstopen(Queue*, Stream*);
static void concdevstclose(Queue*);
static void concdevstoput(Queue*, Block*);
static void concdevstiput(Queue*, Block*);
Qinfo concdevinfo =
{
	concdevstiput,
	concdevstoput,
	concdevstopen,
	concdevstclose,
	"concdev"
};

static void
concstopen(Queue *q, Stream *s)
{
	DPRINT("concstopen q=0x%ux s=0x%ux\n", q, s);

	RD(q)->ptr = 0;		/* (Conc *) for concstiput */
	WR(q)->ptr = s;

	s->opens++;		/* Hold this queue in place */
	s->inuse++;
}

static void
concstclose(Queue *q)
{
	Conc *cp = q->ptr;
	Subdev *dp;
	Block *bp;
	int ndev;

	if(RD(q)->ptr == 0)
		return;
	DPRINT("concstclose \"%s\"\n", cp->name);

	qlock(cp);
	ndev = cp->ndev;
	cp->ndev = 0;
	for(dp=cp->dev; dp<&cp->dev[ndev]; dp++){
			continue;
		bp = allocb(0);
		bp->type = M_HANGUP;
		PUTNEXT(dp->rq, bp);
	}
	qunlock(cp);
	cp->name[0] = 0;
}

static Conc *
concalloc(char *name, int aflag)
{
	Conc *free, *cp;

	DPRINT("concalloc \"%s\" %d...", name, aflag);
	lock(&conclock);
	free = 0;
	for(cp = concs; cp < &concs[conf.nconc]; cp++){
		if(strcmp(name, cp->name) == 0){
			unlock(&conclock);
			DPRINT("found\n");
			return cp;
		}
		if(cp->name[0] == 0)
			free = cp;
	}
	if(!aflag || free == 0){
		unlock(&conclock);
		DPRINT("not found/none free\n");
		return 0;
	}
	strncpy(free->name, name, sizeof free->name-1);
	unlock(&conclock);
	DPRINT("alloc'd\n");
	return free;
}

static void
concstoput(Queue *q, Block *bp)
{
	Conc *cp = 0;
	Subdev *dp;
	char *p, *err = 0;
	int n, ndev, k;

	if(bp->type != M_CTL || streamparse("config", bp) == 0){
		PUTNEXT(q, bp);
		return;
	}
	if(concdebug){
		char fmt[32];
		sprint(fmt, "concstoput config: %%.%ds\n", bp->wptr-bp->rptr);
		kprint(fmt, bp->rptr);
	}
	if(RD(q)->ptr){
		err = "stream already configured";
		goto out;
	}
	n = BLEN(bp);
	if(bp->wptr >= bp->lim){
		memmove(bp->base, bp->rptr, n);
		bp->rptr = bp->base;
		bp->wptr = bp->rptr+n;
	}
	*bp->wptr++ = 0;
	p = strchr((char *)bp->rptr, ' ');
	if(p == 0){
		err = "bad config command";
		goto out;
	}
	*p++ = 0;
	cp = concalloc((char *)bp->rptr, 1);
	bp->rptr = (uchar *)p;
	if(cp == 0){
		err = "no free conc's";
		goto out;
	}
	qlock(cp);
	if(cp->ndev){
		err = "conc name in use";
		goto out;
	}
	p = (char *)bp->rptr;
	for(ndev=0; strtoul(p, &p, 0); ndev++) ;
	DPRINT("concstoput: ndev=%d\n", ndev);
	cp->bp = allocb(ndev*sizeof(Subdev));
	if(cp->bp == 0){
		err = "can't allocb for subdevices";
		cp->name[0] = 0;
		goto out;
	}
	cp->dev = (Subdev *)cp->bp->base;
	p = (char *)bp->rptr;
	k = 0;
	DPRINT("concstoput: chan0=");
	for(dp=cp->dev; dp<&cp->dev[ndev]; dp++){
		dp->cp = cp;
		dp->chan0 = k;
		DPRINT(" %d", dp->chan0);
		k += strtoul(p, &p, 0);
		dp->rq = 0;
	}
	DPRINT("\n");
	cp->s = q->ptr;
	cp->wq = q;
	cp->ndev = ndev;
	q->ptr = cp;
	q->other->ptr = cp;
out:
	freeb(bp);
	if(cp)
		qunlock(cp);
	if(err){
		DPRINT("concstoput: err=\"%s\"\n", err);
		error(err);
	}
}

/*
 *  Simplifying assumption:  one put == one message && the channel number
 *	is in the first block (cf. dkmuxiput).
 */
static void
concstiput(Queue *q, Block *bp)
{
	Conc *cp = q->ptr;
	Subdev *dp;
	int chan;

	if(bp->type != M_DATA){
		PUTNEXT(q, bp);
		return;
	}
	if(BLEN(bp) < 2 || cp == 0)
		goto Drop;
	chan = bp->rptr[0] | (bp->rptr[1]<<8);
	for(dp=&cp->dev[cp->ndev-1]; dp>=cp->dev; dp--)
		if(chan >= dp->chan0)
			break;
	if(dp < cp->dev || dp->rq == 0)
		goto Drop;
	chan -= dp->chan0;
	bp->rptr[0] = chan;
	bp->rptr[1] = chan>>8;
	bp->flags |= S_DELIM;	/* a process is probably waiting for this */
	PUTNEXT(dp->rq, bp);
	return;
	
Drop:
	freeb(bp);
	return;
}

static void
concdevstopen(Queue *q, Stream *s)
{
	Conc *cp;
	Subdev *dp;

	cp = &concs[s->dev];
	qlock(cp);
	if(s->id > cp->ndev){
		qunlock(cp);
		error("no such subdevice");
	}
	dp = &cp->dev[s->id];
	q->ptr = dp;
	q->other->ptr = dp;
	dp->rq = RD(q);
	streamenter(cp->s);
	qunlock(cp);
}

static void
concdevstclose(Queue *q)
{
	Subdev *dp = q->ptr;
	Conc *cp = dp->cp;

	qlock(cp);
	q->ptr = 0;
	q->other->ptr = 0;
	dp->rq = 0;
	streamexit(cp->s, 0);
	qunlock(cp);
}

/*
 *  this is only called by concstclose (hangup)
 */
static void
concdevstiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

static void
concdevstoput(Queue *q, Block *bp)
{
	Subdev *dp = q->ptr;
	Conc *cp;
	int chan;

	if(dp == 0){
		DPRINT("concdevstoput: not config'd, dropped %d\n", BLEN(bp));
		freeb(bp);
		return;
	}
	cp = dp->cp;
	qlock(cp);
	if(!putq(q, bp)){	/* send only whole messages */
		qunlock(cp);
		return;
	}
	bp = grabq(q);
	qunlock(cp);
	if(BLEN(bp) < 2 || bp->type != M_DATA){
		DPRINT("concdevstoput: dropped %d\n", BLEN(bp));
		freeb(bp);
		return;
	}
	chan = bp->rptr[0] | (bp->rptr[1]<<8);
	chan += dp->chan0;
	bp->rptr[0] = chan;
	bp->rptr[1] = chan>>8;
	PUTNEXT(cp->wq, bp);
}

void
concreset(void)
{
	concs = ialloc(conf.nconc*sizeof(Conc), 0);
	newqinfo(&concinfo);
}

void
concinit(void)
{
}

Chan *
concattach(char *spec)
{
	Chan *c;
	Conc *cp;

	/*
	 *  find a concentrator with the same name (default conc)
	 */
	if(*spec == 0)
		spec = "conc";
	cp = concalloc(spec, 0);
	if(cp == 0 || cp->ndev == 0)
		error("device not configured");

	c = devattach('K', spec);
	c->dev = cp-concs;
	c->qid = (Qid){CHDIR, 0};
	return c;
}

Chan *
concclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

#define	TOPDIR		(CHDIR+1)
#define	CHANDIR		(CHDIR+2)

static int
concgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp)
{
	Conc *cp;
	char buf[16];

	cp = &concs[c->dev];
	switch(c->qid.path){
	case CHDIR:
		devdir(c, (Qid){TOPDIR,0}, concs[c->dev].name, 0, eve, 0555, dp);
		return i==0 ? 1 : -1;
	case TOPDIR:
		if(i<0 || i >= cp->ndev)
			return -1;
		sprint(buf, "%d", i);
		devdir(c, (Qid){STREAMQID(i,CHANDIR),0}, buf, 0, eve, 0555, dp);
		return 1;
	}
	return streamgen(c, 0, 0, i, dp);
}

int	 
concwalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, concgen);
}

void	 
concstat(Chan *c, char *dp)
{
	char buf[16], *name;
	Dir dir;

	if(c->qid.path&CHDIR)switch(c->qid.path){
	case CHDIR:
		name = ".";
		break;
	case TOPDIR:
		name = concs[c->dev].name;
		break;
	default:
		sprint(name = buf, "%d", STREAMID(c->qid.path));
		break;
	}else switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
		name = "data";
		break;
	case Sctlqid:
		name = "ctl";
		break;
	default:
		name = "panic";
		break;
	}
	devdir(c, c->qid, name, 0, eve, (c->qid.path&CHDIR)?0555:0600, &dir);
	convD2M(&dir, dp);
}

Chan *
concopen(Chan *c, int omode)
{
	if(c->qid.path & CHDIR){
		if(omode != OREAD)
			error(Eperm);
	}else
		streamopen(c, &concdevinfo);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void	 
conccreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c);
	error(Eperm);
}

void	 
concclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
}

long	 
concread(Chan *c, void *a, long n, ulong offset)
{
	if(c->stream)
		return streamread(c, a, n);
	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, 0, 0, concgen);
	error(Eio);
}

long	 
concwrite(Chan *c, void *a, long n, ulong offset)
{
	return streamwrite(c, a, n, 0);
}

void	 
concremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void	 
concwstat(Chan *c, char *dp)
{
	USED(c);
	error(Eperm);
}

M port/portfns.h => port/portfns.h +2 -0
@@ 26,6 26,7 @@ void	closemount(Mount*);
void	closepgrp(Pgrp*);
long	clrfpintr(void);
void	confinit(void);
void	confinit1(int mul);
int	consactive(void);
void	consdebug(void);
Block	*copyb(Block*, int);


@@ 83,6 84,7 @@ int	getfields(char*, char**, int, char);
Block*	getq(Queue*);
int	gets(IOQ*, void*, int);
void	gotolabel(Label*);
Block*	grabq(Queue*);
void	grpinit(void);
int	hwcursmove(int, int);
int	hwcursset(uchar*, uchar*, int, int);

M port/proc.c => port/proc.c +7 -1
@@ 730,6 730,10 @@ kproc(char *name, void (*func)(void *), void *arg)
	flushmmu();
}

/*
 *  called splhi() by notify().  See comment in notify for the
 *  reasoning.
 */
void
procctl(Proc *p)
{


@@ 737,6 741,7 @@ procctl(Proc *p)

	switch(p->procctl) {
	case Proc_exitme:
		spllo();	/* pexit has locks in it */
		pexit("Killed", 1);
	case Proc_traceme:
		if(u->nnote == 0)


@@ 754,7 759,8 @@ procctl(Proc *p)
		}
		qunlock(&p->debug);
		p->state = Stopped;
		sched();
		sched();		/* sched returns spllo() */
		splhi();
		p->psstate = state;
		return;
	}

A port/stlapd.c => port/stlapd.c +768 -0
@@ 0,0 1,768 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#define DPRINT if(q->flag&QDEBUG)kprint

typedef struct Lapd	Lapd;

#define NOW	TK2MS(MACHP(0)->ticks)
#define	T200	(NOW+2500)

	/* C/R bits */

#define	UCmd	0	/* command, user to network */
#define	UResp	2	/* response, user to network */
#define	NCmd	2	/* command, network to user */
#define	NResp	0	/* response, network to user */

#define	SAPI(s, cr)	(((s)<<2)|cr)
#define	TEI(t)		(((t)<<1)|1)

/*
 *  Commands and responses
 */

#define	RR	0x01	/* Receiver Ready */
#define	RNR	0x05	/* Receiver Not Ready */
#define	REJ	0x09	/* Reject */
#define	SABME	0x6f	/* Set Asynchronous Balanced Mode Extended */
#define	DM	0x0f	/* Disconnect Mode */
#define	UI	0x03	/* Unnumbered Information */
#define	DISC	0x43	/* Disconnect */
#define	UA	0x63	/* Unnumbered Acknowledgement */
#define	FRMR	0x87	/* Frame Reject */
#define	XID	0xaf	/* Exchange Identification */

enum States {
	Unused = 0,
	NoTEI,
	WaitTEI,
	TEIassigned,
	WaitEst,
	Multiframe,
	WaitRel,
};

enum Flags {
	Ownbusy = 0x01,
	Peerbusy = 0x02,
	Rejecting = 0x04,
	Timeout = 0x08,
	Hungup = 0x80
};

#define	SMASK		127
#define	NSEQ		(SMASK+1)
#define	THISS(i)	((i)&SMASK)
#define	NEXTS(i)	(((i)+1)&SMASK)

#define	OMASK		7
#define	NOBUF		(OMASK+1)
#define	THISO(i)	((i)&OMASK)
#define	NEXTO(i)	(((i)+1)&OMASK)

#define	RSTATE(lp)	((lp->flags&Ownbusy) ? RNR : \
			(lp->flags&Rejecting) ? REJ : RR)

struct Lapd {
	QLock;		/* access, state change */
	Rendez;		/* waiting for state change */
	int	state;
	int	flags;
	Queue *	rq;
	Queue *	wq;
	ushort	refnum;
	int	tei;
	int	kout;	/* maximum allowed number of outstanding I frames */
	int	vs;	/* send state variable (next seq. no. to send) */
	int	va;	/* acknowledge state variable (next expected ack) */
	int	vr;	/* receive state variable (next expected input) */
	Rendez	tr;	/* so transmit writer can wait */
	Block *	outb[NOBUF]; /* transmitter circular buffer */
	int	vt;	/* input pointer for outb */
	int	t200;
	Rendez	timer;
	int	kstarted;
	int	error;
};

enum {
	LAPD_OK,
	Address,
	BadUA,
	Badassign,
	Badmanage,
	Tooshort,
	Unimpmanage,
	Unkdlci,
	Egates,
};

char *lapderrors[] = {
	"LAPD OK",
	"Address",
	"BadUA",
	"Badassign",
	"Badmanage",
	"Tooshort",
	"Unimpmanage",
	"Egates",
};

Lapd	*lapd;

/*
 *  predeclared
 */
static void	assigntei(Lapd *);
static void	dl_establish(Lapd *);
static void	dl_release(Lapd *);
static void	lapdreset(void);
static void	lapdclose(Queue *);
static void	lapdiput(Queue*, Block*);
static void	lapdkproc(void *);
static void	lapdopen(Queue*, Stream*);
static void	lapdoput(Queue*, Block*);
static int	lapdxmit(Lapd *);
static void	mdl_assign(Lapd *);
static void	mdl_iput(Queue *, Block *);
static int	recvack(Lapd *, int);
static void	sendctl(Lapd *, int, int, int);

Qinfo lapdinfo = { lapdiput, lapdoput, lapdopen, lapdclose, "lapd", lapdreset };

#define	Predicate(name, expr)	static int name(void *arg) { return expr; }
#define	lp	((Lapd *)arg)

Predicate(pTEIassigned, lp->state == TEIassigned);
Predicate(pMultiframe, lp->state == Multiframe);

Predicate(tEmpty, lp->vs == lp->va)
Predicate(tFull, NEXTO(lp->vt) == THISO(lp->va))
Predicate(tNotFull, NEXTO(lp->vt) != THISO(lp->va))

#undef	lp

static void
lapderror(Lapd *lp, int code)
{
	lp->error = code;
	nexterror();
}

static void
lapdreset(void)
{
	lapd = ialloc(conf.nlapd*sizeof(Lapd), 0);
}

static void
lapdopen(Queue *q, Stream *s)
{
	Lapd *lp;
	char name[32];

	for(lp=lapd; lp<&lapd[conf.nlapd]; lp++){
		qlock(lp);
		if(lp->state == 0)
			break;
		qunlock(lp);
	}
	if(lp>=&lapd[conf.nlapd])
		error(Enoifc);

	/*q->flag |= QDEBUG;
	q->other->flag |= QDEBUG;*/
	DPRINT("lapdopen: lapd %d\n", lp - lapd);
	q->ptr = q->other->ptr = lp;
	lp->rq = q;
	lp->wq = q->other;
	lp->state = NoTEI;
	lp->flags = 0;
	lp->kout = 1;
	qunlock(lp);
	sprint(name, "lapd%d", lp - lapd);
	kproc(name, lapdkproc, lp);
	DPRINT("lapdopen: mdl_assign\n");
	mdl_assign(lp);
	DPRINT("lapdopen: dl_establish\n");
	dl_establish(lp);
}

static void
mdl_assign(Lapd *lp)
{
	Block *b; uchar *p;

	qlock(lp);
	if (lp->state != NoTEI) {
		qunlock(lp);
		error(Egreg);
	}
	lp->state = WaitTEI;
	assigntei(lp);
	lp->t200 = T200;
	qunlock(lp);
	sleep(lp, pTEIassigned, lp);
	if (lp->state != TEIassigned){
		lapdclose(lp->rq);
		error(Egreg);
	}
}

static void
assigntei(Lapd *lp)
{
	Queue *q = lp->rq;
	Block *b = allocb(16);
	uchar *p = b->wptr;

	lp->refnum = NOW;
	*p++ = SAPI(63, UCmd);
	*p++ = TEI(127);
	*p++ = UI;
	*p++ = 0x0f;
	*p++ = lp->refnum>>8;
	*p++ = lp->refnum;
	*p++ = 0x01;
	*p++ = 0xff;
	b->wptr = p;
	b->flags |= S_DELIM;
	DPRINT("assigntei: refnum=0x%4.4ux\n", lp->refnum);
	PUTNEXT(lp->wq, b);
}

static void
dl_establish(Lapd *lp)
{
	Block *b; uchar *p;

	qlock(lp);
	if (lp->state != TEIassigned) {
		qunlock(lp);
		error(Egreg);
	}
	lp->state = WaitEst;
	sendctl(lp, UCmd, SABME, 0);
	lp->t200 = T200;
	qunlock(lp);
	sleep(lp, pMultiframe, lp);
	if (lp->state != Multiframe){
		lapdclose(lp->rq);
		error(Egreg);
	}
}

static void
dl_release(Lapd *lp)
{
	Block *b; uchar *p;

	qlock(lp);
	if (lp->state != Multiframe) {
		qunlock(lp);
		error(Egreg);
	}
	lp->state = WaitRel;
	sendctl(lp, UCmd, DISC, 0);
	lp->t200 = T200;
	qunlock(lp);
	tsleep(lp, pTEIassigned, lp, 30*1000);
	qlock(lp);
	if(lp->state > TEIassigned)
		lp->state = TEIassigned;
	qunlock(lp);
}

static void
lapdclose(Queue *q)
{
	Lapd *lp = (Lapd *)q->ptr;
	int i;

	if(lp == 0)
		return;

	if (lp->state == Multiframe) {
		for(i=0; i<NOBUF && !tEmpty(lp); i++)
			tsleep(&lp->tr, tEmpty, lp, 2500);	/* drain */
		dl_release(lp);					/* DISC to NT */
		for(i=0; i<NOBUF; i++)
			if(lp->outb[i]){
				freeb(lp->outb[i]);
				lp->outb[i] = 0;
			}
	}

	lp->flags |= Hungup;			/* tell kernel proc */
	lp->t200 = NOW;
	wakeup(&lp->timer);
	for(i=0; i<10 && lp->kstarted; i++)
		tsleep(lp, return0, 0, 500);

	lp->state = 0;
}

/*
 *  upstream control messages
 */
static void
lapdctliput(Lapd *lp, Queue *q, Block *bp)
{
	switch(bp->type){
	case M_HANGUP:
		lp->flags |= Hungup;
		wakeup(lp);
		break;
	}
	PUTNEXT(q, bp);
}

void
lapdiput(Queue *q, Block *bp)
{
	Lapd *lp = (Lapd *)q->ptr;
	int sapi, cr, tei, ctl, ns, nr = 0, pf;
	uchar *start, *p;

	DPRINT("lapdiput: %d byte(s)\n", bp->wptr-bp->rptr);
	qlock(lp);
	lp->error = 0;
	p = start = bp->rptr;
	if (waserror()) {
		qunlock(lp);
		kprint("lapdiput: %s:", lapderrors[lp->error]);
		for(p=start; p<bp->wptr; p++)
			kprint(" %2.2ux", *p);
		kprint("\n");
		freeb(bp);
		return;
	}

	if(bp->type != M_DATA){
		lapdctliput(lp, q, bp);
		bp = 0;
		goto out;
	}
	if (bp->wptr-p < 3)
		lapderror(lp, Tooshort);
	if ((p[0] & 0x01) || !(p[1] & 0x01))
		lapderror(lp, Address);
	sapi = p[0] >> 2;
	cr = p[0] & 0x02;
	tei = p[1] >> 1;
	ctl = p[2];
	if ((ctl&0x03) != 0x03) {
		if (bp->wptr-p < 4)
			lapderror(lp, Tooshort);
		pf = p[3]&0x01;
		nr = p[3]>>1;
		p += 4;
	} else {
		pf = ctl&0x10;
		ctl &= ~0x10;
		p += 3;
	}
	bp->rptr = p;
	DPRINT("lapdiput: cr=%d sapi=%d tei=%d ctl=0x%2.2x pf=%d\n",
		cr, sapi, tei, ctl, pf);
	if (cr==NCmd && tei==127 && ctl==UI) {
		switch (sapi) {
		case 0:
			PUTNEXT(q, bp);
			bp = 0;
			break;
		case 63:
			mdl_iput(q, bp);
			bp = 0;
			break;
		default:
			lapderror(lp, Unkdlci);
			break;
		}
		goto out;
	}
	if (sapi != 0 || lp->state < TEIassigned || tei != lp->tei)
		lapderror(lp, Unkdlci);
	if (!(ctl&0x01)) {	/* Information */
		if (recvack(lp, nr)>0 && !(lp->flags&(Peerbusy|Timeout)))
			lp->t200 = 0;
		if (lp->flags&Ownbusy) {
			if (pf)
				sendctl(lp, cr, RNR, pf);
			lapdxmit(lp);
			goto out;
		}
		ns = ctl>>1;
		if (ns != lp->vr) {
			lp->flags |= Rejecting;
			sendctl(lp, cr, REJ, pf);
			lapdxmit(lp);
			goto out;
		}
		PUTNEXT(q, bp);
		bp = 0;
		lp->flags &= ~Rejecting;
		lp->vr = NEXTS(lp->vr);
		if (QFULL(q->next))
			lp->flags |= Ownbusy;
		if (pf || (lp->flags&Ownbusy)) {
			sendctl(lp, cr, RSTATE(lp), pf);
			lapdxmit(lp);
		} else if (!lapdxmit(lp))
			sendctl(lp, cr, RR, pf);
		goto out;
	}
	switch (ctl) {
	case RR:
		if (recvack(lp, nr)<0)
			goto out;
		lp->flags &= ~Peerbusy;
		if (cr==NCmd && pf)
			sendctl(lp, cr, RSTATE(lp), pf);
		if (!(lp->flags&Timeout)) {
			lp->t200 = 0;
			if (cr==NResp && pf)
				DPRINT("lapdiput: RR response with F==1\n");
			lapdxmit(lp);
		} else if (cr==NResp && pf) {
			lp->flags &= ~Timeout;
			lp->vs = nr;
			lp->t200 = 0;
			lapdxmit(lp);
		}
		break;
	case RNR:
		if (recvack(lp, nr)<0)
			goto out;
		lp->flags |= Peerbusy;
		if (cr==NCmd && pf)
			sendctl(lp, cr, RSTATE(lp), pf);
		if (!(lp->flags&Timeout)) {
			lp->t200 = T200;
			if (cr==NResp && pf)
				DPRINT("lapdiput: RNR response with F==1\n");
		} else if (cr==NResp && pf) {
			lp->flags &= ~Timeout;
			lp->vs = nr;
			lp->t200 = T200;
		}
		break;
	case REJ:
		if (recvack(lp, nr)<0)
			goto out;
		lp->flags &= ~Peerbusy;
		if (cr==NCmd && pf)
			sendctl(lp, cr, RSTATE(lp), pf);
		if (!(lp->flags&Timeout)) {
			lp->vs = nr;
			lp->t200 = 0;
			if (cr==NResp && pf)
				DPRINT("lapdiput: REJ response with F==1\n");
			lapdxmit(lp);
		} else if (cr==NResp && pf) {
			lp->flags &= ~Timeout;
			lp->vs = nr;
			lp->t200 = 0;
			lapdxmit(lp);
		}
		break;
	case DM:
		lp->state = TEIassigned;
		lp->flags = 0;
		lp->t200 = 0;
		wakeup(lp);
		break;
	case UA:
		if (lp->state == WaitEst) {
			lp->state = Multiframe;
			lp->flags = 0;
			lp->vs = 0;
			lp->va = 0;
			lp->vr = 0;
			lp->vt = 0;
			lp->t200 = 0;
			wakeup(lp);
		} else if (lp->state == WaitRel) {
			lp->state = TEIassigned;
			lp->flags = 0;
			lp->t200 = 0;
			wakeup(lp);
		} else
			lapderror(lp, BadUA);
		break;
	case SABME:
	case UI:
	case DISC:
	case FRMR:
	case XID:
		lapderror(lp, Egates);
		break;
	}
out:
	if (bp)
		freeb(bp);
	poperror();
	qunlock(lp);
}

static void
mdl_iput(Queue *q, Block *bp)
{
	Lapd *lp = (Lapd *)q->ptr;
	uchar *p = bp->rptr;
	ushort refnum;
	int ai;

	if (bp->wptr-p != 5 || p[0] != 0x0f || !(p[4]&0x01))
		lapderror(lp, Badmanage);
	refnum = (p[1]<<8)|p[2];
	ai = p[4]>>1;
	switch (p[3]) {
	case 2:		/* identity assigned */
		if (lp->state == WaitTEI && lp->refnum == refnum) {
			lp->state = TEIassigned;
			lp->tei = ai;
			lp->t200 = 0;
			DPRINT("assigned tei = %d\n", lp->tei);
			wakeup(lp);
		} else
			lapderror(lp, Badassign);
		break;
	case 3:		/* identity denied */
		if (lp->state == WaitTEI && lp->refnum == refnum) {
			lp->t200 = 0;
			DPRINT("denied tei = %d\n", ai);
			wakeup(lp);
		} else
			lapderror(lp, Badassign);
		break;
	case 4:		/* identity check request */
		if (lp->state >= TEIassigned && (ai==127||ai==lp->tei)) {
			Block *b = allocb(16);
			uchar *p = b->wptr;
		
			*p++ = SAPI(63, UCmd);
			*p++ = TEI(127);
			*p++ = UI;
			*p++ = 0x0f;
			*p++ = NOW>>8;
			*p++ = NOW;
			*p++ = 0x05;
			*p++ = (lp->tei<<1)|1;
			b->wptr = p;
			b->flags |= S_DELIM;
			DPRINT("id check response: tei=%d\n", lp->tei);
			PUTNEXT(lp->wq, b);
		} else
			lapderror(lp, Badassign);
		break;
	default:
		{
			Block *b = allocb(0);
			b->type = M_HANGUP;
			PUTNEXT(lp->rq, b);
		}
		lapderror(lp, Unimpmanage);
		break;
	}
	freeb(bp);
}

static int
recvack(Lapd *lp, int nr)
{
	Queue *q = lp->wq;
	int i;

	for(i=lp->va; i!=lp->vs; i=NEXTS(i)){
		if(i==nr)
			break;
	}
	if(i!=nr) {
		DPRINT("lapd recvack: va,nr,vs = %d,%d,%d\n",
			lp->va, nr, lp->vs);
		return -1;
	}
	i = 0;
	while (lp->va != nr) {
		freeb(lp->outb[THISO(lp->va)]);
		lp->outb[THISO(lp->va)] = 0;
		lp->va = NEXTS(lp->va);
		i++;
	}
	if (i > 0)
		wakeup(&lp->tr);
	return i;
}

/*
 *  downstream control
 */
static void
lapdctloput(Lapd *lp, Queue *q, Block *bp)
{
	char *fields[2];

	switch(bp->type){
	case M_CTL:
		if(streamparse("debug", bp)){
			switch(getfields((char *)bp->rptr, fields, 2, ' ')){
			case 1:
				if (strcmp(fields[0], "on") == 0) {
					q->flag |= QDEBUG;
					q->other->flag |= QDEBUG;
				}
				if (strcmp(fields[0], "off") == 0) {
					q->flag &= ~QDEBUG;
					q->other->flag &= ~QDEBUG;
				}
			}
			freeb(bp);
			return;
		}
	}
	PUTNEXT(q, bp);
}

/*
 *  accept data from a writer
 */
static void
lapdoput(Queue *q, Block *bp)
{
	Lapd *lp = (Lapd *)q->ptr;

	if(bp->type != M_DATA){
		lapdctloput(lp, q, bp);
		return;
	}
	if(!putq(q, bp))	/* send only whole messages */
		return;
	DPRINT("lapdoput: len=%d\n", q->len);
	if (tFull(lp))
		sleep(&lp->tr, tNotFull, lp);
	qlock(lp);
	if (waserror()) {
		qunlock(lp);
		nexterror();
	}
	lp->outb[lp->vt] = grabq(q);
	lp->vt = NEXTO(lp->vt);

	lapdxmit(lp);
	poperror();
	qunlock(lp);
}

static int
lapdxmit(Lapd *lp)
{
	Block *bp, *db;
	int i; uchar *p;

	for (i=0;;i++) {
		if (THISO(lp->vs) == lp->vt)		/* buffer empty */
			break;
		if (THISS(lp->va+lp->kout) == lp->vs)	/* window full */
			break;
		if (lp->flags & Peerbusy)		/* flow-controlled */
			break;
		bp = allocb(16);
		p = bp->wptr;
		*p++ = SAPI(0, UCmd);
		*p++ = TEI(lp->tei);
		*p++ = lp->vs<<1;
		*p++ = lp->vr<<1;
		bp->wptr = p;
		PUTNEXT(lp->wq, bp);
		for(bp=lp->outb[THISO(lp->vs)]; bp; bp=bp->next){
			db = allocb(0);
			db->rptr = bp->rptr;
			db->wptr = bp->wptr;
			db->flags |= (bp->flags&S_DELIM);
			PUTNEXT(lp->wq, db);
		}
		lp->vs = NEXTS(lp->vs);
		lp->t200 = T200;
	}
	return i;
}

/*
 *  send a control frame.
 */
static void
sendctl(Lapd *lp, int cmd, int ctl, int pf)
{
	Block *bp = allocb(16);
	uchar *p = bp->wptr;

	*p++ = SAPI(0, cmd);
	*p++ = TEI(lp->tei);
	if (ctl & 0x02) {
		*p++ = ctl|(pf?0x10:0);
	} else {
		*p++ = ctl;
		*p++ = (lp->vr<<1)|(pf?0x01:0);
	}
	bp->wptr = p;
	bp->flags |= S_DELIM;
	PUTNEXT(lp->wq, bp);
}

static void
lapdkproc(void *arg)
{
	Lapd *lp = (Lapd *)arg;
	int locked = 0;
	lp->kstarted = 1;

	if(waserror()){
		if (locked)
			qunlock(lp);
		print("lapdkproc %d error\n", lp-lapd);
		lp->kstarted = 0;
		wakeup(lp);
		return;
	}
	for(;;){
		tsleep(&lp->timer, return0, 0, 500);
		if(!lp->t200 || NOW < lp->t200)
			continue;
		qlock(lp);
		locked = 1;
		USED(locked);
		if(lp->flags & Hungup){
			qunlock(lp);
			locked = 0;
			USED(locked);
			break;
		}
		switch(lp->state){
		case WaitTEI:
			assigntei(lp);
			lp->t200 = T200;
			break;
		case WaitEst:
			sendctl(lp, UCmd, SABME, 0);
			break;
		case Multiframe:
			sendctl(lp, UCmd, RSTATE(lp), 1);
			lp->flags |= Timeout;
			lp->t200 = T200;
			break;
		}
		qunlock(lp);
		locked = 0;
		USED(locked);
	}
	lp->kstarted = 0;
	wakeup(lp);
	poperror();
}

M port/stream.c => port/stream.c +24 -0
@@ 543,6 543,30 @@ getq(Queue *q)
}

/*
 *  grab all the blocks in a queue
 */
Block *
grabq(Queue *q)
{
	Block *bp;

	lock(q);
	bp = q->first;
	if(bp){
		q->first = 0;
		q->last = 0;
		q->len = 0;
		q->nb = 0;
		if(q->flag&QHIWAT){
			wakeup(q->other->next->other->rp);
			q->flag &= ~QHIWAT;
		}
	}
	unlock(q);
	return bp;
}

/*
 *  remove the first block from a list of blocks
 */
Block *

M power/devhotrod.c => power/devhotrod.c +2 -5
@@ 75,9 75,9 @@ hotwait(Hotmsg **mp)
	l = 0;
	while(*mp){
		delay(0);	/* just a subroutine call; stay off VME */
		if(++l > 1000*1000){
		if(++l > 10*1000*1000){
			l = 0;
			print("hotsend blocked\n");
			panic("hotsend blocked");
		}
	}
	return;


@@ 412,11 412,9 @@ hotrodintr(int vec)
	Hotmsg *hm;
	ulong l;

	LEDON(LEDhotintr);
	h = &hotrod[vec - Vmevec];
	if(h<hotrod || h>&hotrod[Nhotrod]){
		print("bad hotrod vec\n");
		LEDOFF(LEDhotintr);
		return;
	}
	while(l = h->addr->replyq[h->ri]){	/* assign = */


@@ 429,5 427,4 @@ hotrodintr(int vec)
		if(!hm->abort)
			wakeup(&hm->r);
	}
	LEDOFF(LEDhotintr);
}

M power/faultmips.c => power/faultmips.c +2 -0
@@ 21,6 21,8 @@ faultmips(Ureg *ur, int user, int code)

	LEDON(LEDfault);
	addr = ur->badvaddr;
	if(addr & KZERO)
		LEDON(LEDkfault);
	addr &= ~(BY2PG-1);
	read = !(code==CTLBM || code==CTLBS);


M power/io.h => power/io.h +3 -3
@@ 10,13 10,13 @@
enum
{
	LEDtrapmask=	0xf<<0,
	LEDhotintr=	1<<4,
	LEDkfault=	1<<4,
	LEDclock=	1<<5,
	LEDfault=	1<<6,
	LEDpulse=	1<<7,
};
#define LEDON(x) 	(m->ledval &= ~x, *LED = m->ledval)
#define LEDOFF(x) 	(m->ledval |= x, *LED = m->ledval)
#define LEDON(x) 	(m->ledval &= ~(x), *LED = m->ledval)
#define LEDOFF(x) 	(m->ledval |= (x), *LED = m->ledval)

typedef struct SBCC	SBCC;
typedef struct Timer	Timer;