~kris/9p

9hist

e6a31db53a342e6cf0431b5c8a547b195ba9b0da — David du Colombier 35 years ago 5f9474f
Plan 9 from Bell Labs 1990-10-09
M gnot/dat.h => gnot/dat.h +7 -7
@@ 150,6 150,7 @@ struct Conf
	int	nfont;		/* font structs (devbit.c) */
	int	nurp;		/* max urp conversations */
	int	nasync;		/* number of async protocol modules */
	int	npipe;		/* number of pipes */
};

struct Dev


@@ 457,14 458,13 @@ struct Queue {
 */
struct Stream {
	Lock;			/* structure lock */
	int	inuse;		/* number of processes in stream */
	int	opens;		/* number of processes with stream open */
	int	hread;		/* number of reads after hangup */
	int	type;		/* correclation with Chan */
	int	dev;		/* ... */
	int	id;		/* ... */
	short	inuse;		/* number of processes in stream */
	short	opens;		/* number of processes with stream open */
	ushort	hread;		/* number of reads after hangup */
	ushort	type;		/* correlation with Chan */
	ushort	dev;		/* ... */
	ushort	id;		/* ... */
	QLock	rdlock;		/* read lock */
	QLock	wrlock;		/* write lock */
	Queue	*procq;		/* write queue at process end */
	Queue	*devq;		/* read queue at device end */
};

M gnot/devpipe.c => gnot/devpipe.c +157 -47
@@ 8,80 8,111 @@
#include	"devtab.h"
#include	"fcall.h"

typedef struct Pipe	Pipe;

struct Pipe
{
	Ref;
	int	debug;
	Pipe	*next;
};

struct Pipealloc
{
	Lock;
	Pipe *pipe;
	Pipe *free;
} pipealloc;

static void pipeiput(Queue*, Block*);
static void pipeoput(Queue*, Block*);
static void pipestclose(Queue *);
Qinfo pipeinfo = { pipeiput, pipeoput, 0, pipestclose, "pipe" };

Dirtab pipedir[]={
	"data",		Sdataqid,	0,			0600,
	"ctl",		Sctlqid,	0,			0600,
	"data1",	Sdataqid,	0,			0600,
	"ctl1",		Sctlqid,	0,			0600,
};
#define NPIPEDIR 4

void
pipeinit(void)
{
}

/*
 *  allocate structures for conf.npipe pipes
 */
void
pipereset(void)
{
	Pipe *p, *ep;

	pipealloc.pipe = ialloc(conf.npipe * sizeof(Pipe), 0);
	ep = &pipealloc.pipe[conf.npipe-1];
	for(p = pipealloc.pipe; p < ep; p++)
		p->next = p+1;
	pipealloc.free = pipealloc.pipe;
}

/*
 *  allocate both streams
 *
 *  a subsequent clone will get them the second stream
 *  create a pipe, no streams are created until an open
 */
Chan*
pipeattach(char *spec)
{
	Pipe *p;
	Chan *c;
	int i;

	/*
	 *  make the first stream
	 */
	c = devattach('|', spec);
	c->qid = STREAMQID(0, Sdataqid);
	streamnew(c, &pipeinfo);

	lock(&pipealloc);
	if(pipealloc.free == 0){
		unlock(&pipealloc);
		error(0, Enopipe);
	}
	p = pipealloc.free;
	pipealloc.free = p->next;
	p->ref = 1;
	unlock(&pipealloc);

	c->qid = CHDIR|STREAMQID(2*(p - pipealloc.pipe), 0);
	return c;
}

Chan*
pipeclone(Chan *c, Chan *nc)
{
	/*
	 *  make the second stream 
	 */
	Pipe *p;

	p = &pipealloc.pipe[STREAMID(c->qid)/2];
	nc = devclone(c, nc);
	if(waserror()){
		close(nc);
		nexterror();
	}
	nc->qid = STREAMQID(1, Sdataqid);
	streamnew(nc, &pipeinfo);
	poperror();
	incref(p);
	return nc;
}

	/*
	 *  attach it to the first
	 */
	c->stream->devq->ptr = (Stream *)nc->stream;
	nc->stream->devq->ptr = (Stream *)c->stream;
	c->stream->devq->other->next = nc->stream->devq;
	nc->stream->devq->other->next = c->stream->devq;
int
pipegen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp)
{
	int id;

	/*
	 *  up the inuse count of each stream to reflect the
	 *  pointer from the other stream.
	 */
	if(streamenter(c->stream)<0)
		panic("pipeattach");
	if(streamenter(nc->stream)<0)
		panic("pipeattach");
	return nc;
	id = STREAMID(c->qid);
	if(i > 1)
		id++;
	if(tab==0 || i>=ntab)
		return -1;
	tab += i;
	devdir(c, STREAMQID(id, tab->qid), tab->name, tab->length, tab->perm, dp);
	return 1;
}


int
pipewalk(Chan *c, char *name)
{
	print("pipewalk\n");
	error(0, Egreg);
	return devwalk(c, name, pipedir, NPIPEDIR, pipegen);
}

void


@@ 90,10 121,60 @@ pipestat(Chan *c, char *db)
	streamstat(c, db, "pipe");
}

/*
 *  if the stream doesn't exist, create it
 */
Chan *
pipeopen(Chan *c, int omode)
{
	c->mode = omode;
	Pipe *p;
	Stream *local, *remote;

	if(CHDIR & c->qid){
		if(omode != OREAD)
			error(0, Ebadarg);
		c->mode = omode;
		c->flag |= COPEN;
		c->offset = 0;
		return c;
	}

	p = &pipealloc.pipe[STREAMID(c->qid)/2];
	remote = 0;
	if(waserror()){
		unlock(p);
		if(remote)
			streamclose1(remote);
		nexterror();
	}
	lock(p);
	streamopen(c, &pipeinfo);
	local = c->stream;
	if(local->devq->ptr == 0){
		/*
		 *  First stream opened, create the other end also
		 */
		remote = streamnew(c->type, c->dev, STREAMID(c->qid)^1, &pipeinfo, 1);

		/*
		 *  connect the device ends of both streams
		 */
		local->devq->ptr = remote;
		remote->devq->ptr = local;
		local->devq->other->next = remote->devq;
		remote->devq->other->next = local->devq;

		/*
		 *  increment the inuse count to reflect the
		 *  pointer from the other stream.
		 */
		if(streamenter(local)<0)
			panic("pipeattach");
	}
	unlock(p);
	poperror();

	c->mode = omode&~OTRUNC;
	c->flag |= COPEN;
	c->offset = 0;
	return c;


@@ 118,27 199,56 @@ pipewstat(Chan *c, char *db)
}

void
pipeexit(Pipe *p)
{
	decref(p);
	if(p->ref <= 0){
		lock(&pipealloc);
		p->next = pipealloc.free;
		pipealloc.free = p;
		unlock(&pipealloc);
	}
}

void
pipeclose(Chan *c)
{
	Stream *other;
	Stream *remote;
	Stream *local;
	Pipe *p;

	other = (Stream *)c->stream->devq->ptr;
	p = &pipealloc.pipe[STREAMID(c->qid)/2];

	if(waserror()){
		streamexit(other, 0);
		nexterror();
	/*
	 *  take care of assosiated streams
	 */
	if(local = c->stream){
		remote = (Stream *)c->stream->devq->ptr;
		if(waserror()){
			streamexit(remote, 0);
			pipeexit(p);
			nexterror();
		}
		streamclose(c);		/* close this stream */
		streamexit(remote, 0);	/* release stream for other half of pipe */
		poperror();
	}
	streamclose(c);		/* close this stream */
	streamexit(other, 0);	/* release stream for other half of pipe */
	poperror();
	pipeexit(p);
}

long
piperead(Chan *c, void *va, long n)
{
	return streamread(c, va, n);
	if(CHDIR&c->qid)
		return devdirread(c, va, n, pipedir, NPIPEDIR, pipegen);
	else
		return streamread(c, va, n);
}

/*
 *  a write to a closed pipe causes a note to be sent to
 *  the process.
 */
long
pipewrite(Chan *c, void *va, long n)
{

M gnot/errno.h => gnot/errno.h +3 -2
@@ 36,9 36,9 @@ enum{
	Etoosmall,	/* read or write too small */
	Ehungup,	/* write to hungup stream */
	Ebadnet,	/* illegal network address */
	Enoifc,		/* no free interface slots */
	Enoifc,		/* bad interface or no free interface slots */
	Enodev,		/* no free devices */
	Ebadctl,	/* bad process control request */
	Ebadctl,	/* bad process or stream control request */
	Enonote,	/* note overflow */
	Eintr,		/* interrupted */
	Edestbusy,	/* datakit destination busy */


@@ 57,5 57,6 @@ enum{
	Enofont,	/* out of font descriptors */
	Enovmem,	/* virtual memory allocation failed */
	Enoasync,	/* out of async stream modules */
	Enopipe,	/* out of pipes */
	Egreg,		/* ken hasn't implemented datakit */
};

M gnot/fns.h => gnot/fns.h +2 -1
@@ 153,12 153,13 @@ void	splx(int);
void	spldone(void);
Devgen	streamgen;
void	streamclose(Chan*);
void	streamclose1(Stream*);
int	streamenter(Stream*);
void	streamexit(Stream*, int);
void	streaminit(void);
long	streamread(Chan*, void*, long);
long	streamwrite(Chan*, void*, long, int);
Stream*	streamnew(Chan*, Qinfo*);
Stream*	streamnew(ushort, ushort, ushort, Qinfo*, int);
void	streamopen(Chan*, Qinfo*);
int	streamparse(char*, Block*);
void	streamstat(Chan*, char*, char*);

M gnot/stasync.c => gnot/stasync.c +1 -1
@@ 61,7 61,7 @@ static void asyncclose(Queue*);
static void asyncreset(void);
Qinfo asyncinfo = { asynciput, asyncoput, asyncopen, asyncclose, "async", asyncreset };

int asyncdebug = 10;
int asyncdebug = 0;
int asyncerror;

static ushort crc_table[256] = {

M gnot/stream.c => gnot/stream.c +27 -23
@@ 235,6 235,7 @@ allocq(Qinfo *qi)
	q->info = qi;
	q->put = qi->iput;
	q->len = q->nb = 0;
	q->ptr = 0;
	wq = q->other = q + 1;

	wq->flag = QINUSE;


@@ 242,6 243,7 @@ allocq(Qinfo *qi)
	wq->info = qi;
	wq->put = qi->oput;
	wq->other = q;
	wq->ptr = 0;
	wq->len = wq->nb = 0;

	unlock(q);


@@ 625,10 627,10 @@ streamgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp)
}

/*
 *  create a new stream
 *  create a new stream, if noopen is non-zero, don't increment the open count
 */
Stream *
streamnew(Chan *c, Qinfo *qi)
streamnew(ushort type, ushort dev, ushort id, Qinfo *qi, int noopen)
{
	Stream *s;
	Queue *q;


@@ 651,26 653,25 @@ streamnew(Chan *c, Qinfo *qi)
	}
	if(waserror()){
		unlock(s);
		streamclose(c);
		streamclose1(s);
		nexterror();
	}

	/*
	 *  marry a stream and a channel
	 *  identify the stream
	 */
	if(c){
		c->stream = s;
		s->type = c->type;
		s->dev = c->dev;
		s->id = STREAMID(c->qid);
	} else
		s->type = -1;
	s->type = type;
	s->dev = dev;
	s->id = id;

	/*
 	 *  hang a device and process q off the stream
	 */
	s->inuse = 1;
	s->opens = 1;
	if(noopen)
		s->opens = 0;
	else
		s->opens = 1;
	s->hread = 0;
	q = allocq(&procinfo);
	s->procq = WR(q);


@@ 699,7 700,7 @@ streamopen(Chan *c, Qinfo *qi)
	Queue *q;

	/*
	 *  if the stream already exists, just up the reference count.
	 *  if the stream already exists, just increment the reference counts.
	 */
	for(s = slist; s < &slist[conf.nstream]; s++) {
		if(s->inuse && s->type == c->type && s->dev == c->dev


@@ 721,7 722,7 @@ streamopen(Chan *c, Qinfo *qi)
	/*
	 *  create a new stream
	 */
	streamnew(c, qi);
	c->stream = streamnew(c->type, c->dev, STREAMID(c->qid), qi, 0);
}

/*


@@ 773,17 774,10 @@ streamexit(Stream *s, int locked)
 *  stream release its blocks and call its close routine.
 */
void
streamclose(Chan *c)
streamclose1(Stream *s)
{
	Queue *q, *nq;
	Block *bp;
	Stream *s = c->stream;

	/*
	 *  if not open, ignore it
	 */
	if(!c->stream)
		return;

	/*
	 *  decrement the reference count


@@ 817,6 811,16 @@ streamclose(Chan *c)
	streamexit(s, 1);
	unlock(s);
}
void
streamclose(Chan *c)
{
	/*
	 *  if no stream, ignore it
	 */
	if(!c->stream)
		return;
	streamclose1(c->stream);
}

/*
 *  put a block to be read into the queue.  wakeup any waiting reader


@@ 1174,7 1178,7 @@ dumpblocks(Queue *q, char c)

	lock(q);
	for(bp = q->first; bp; bp = bp->next){
		print("%c%d%c", c, bp->wptr-bp->rptr, (bp->flags&S_DELIM));
		print("%c%d%c", c, bp->wptr-bp->rptr, (bp->flags&S_DELIM)?'D':' ');
		for(cp = bp->rptr; cp<bp->wptr && cp<bp->rptr+10; cp++)
			print(" %uo", *cp);
		print("\n");

M gnot/sysfile.c => gnot/sysfile.c +42 -40
@@ 56,6 56,45 @@ openmode(ulong o)
}

long
syspipe(ulong *arg)
{
	int fd[2];
	Chan *c[2];
	Dev *d;

	validaddr(arg[0], 2*BY2WD, 1);
	evenaddr(arg[0]);
	d = &devtab[devno('|', 0)];
	c[0] = (*d->attach)(0);
	c[1] = 0;
	fd[0] = -1;
	fd[1] = -1;
	if(waserror()){
		close(c[0]);
		if(c[1])
			close(c[1]);
		if(fd[0] >= 0)
			u->fd[fd[0]]=0;
		if(fd[1] >= 0)
			u->fd[fd[1]]=0;
		nexterror();
	}
	c[1] = (*d->clone)(c[0], 0);
	(*d->walk)(c[0], "data");
	(*d->walk)(c[1], "data1");
	c[0] = (*d->open)(c[0], ORDWR);
	c[1] = (*d->open)(c[1], ORDWR);
	fd[0] = newfd();
	u->fd[fd[0]] = c[0];
	fd[1] = newfd();
	u->fd[fd[1]] = c[1];
	((long*)arg[0])[0] = fd[0];
	((long*)arg[0])[1] = fd[1];
	poperror();
	return 0;
}

long
sysdup(ulong *arg)
{
	int fd;


@@ 180,8 219,8 @@ sysread(ulong *arg)
	Chan *c;
	long n;

	c = fdtochan(arg[0], 0);
	validaddr(arg[1], arg[2], 0);
	c = fdtochan(arg[0], OREAD);
	validaddr(arg[1], arg[2], 1);
	qlock(c);
	if(waserror()){
		qunlock(c);


@@ 208,7 247,7 @@ syswrite(ulong *arg)
	Chan *c;
	long n;

	c = fdtochan(arg[0], 1);
	c = fdtochan(arg[0], OWRITE);
	validaddr(arg[1], arg[2], 0);
	qlock(c);
	if(waserror()){


@@ 383,43 422,6 @@ sysmount(ulong *arg)
}

long
syspipe(ulong *arg)
{
	int fd[2];
	Chan *c[2];
	Dev *d;

	validaddr(arg[0], 2*BY2WD, 1);
	evenaddr(arg[0]);
	d = &devtab[devno('|', 0)];
	c[0] = (*d->attach)(0);
	c[1] = 0;
	fd[0] = -1;
	fd[1] = -1;
	if(waserror()){
		close(c[0]);
		if(c[1])
			close(c[1]);
		if(fd[0] >= 0)
			u->fd[fd[0]]=0;
		if(fd[1] >= 0)
			u->fd[fd[1]]=0;
		nexterror();
	}
	c[1] = (*d->clone)(c[0], 0);
	c[0] = (*d->open)(c[0], ORDWR);
	c[1] = (*d->open)(c[1], ORDWR);
	fd[0] = newfd();
	u->fd[fd[0]] = c[0];
	fd[1] = newfd();
	u->fd[fd[1]] = c[1];
	((long*)arg[0])[0] = fd[0];
	((long*)arg[0])[1] = fd[1];
	poperror();
	return 0;
}

long
syscreate(ulong *arg)
{
	int fd;

M gnot/sysproc.c => gnot/sysproc.c +2 -0
@@ 500,6 500,8 @@ sysforkpgrp(ulong *arg)
	}
	if(arg[0] == 0)
		pgrpcpy(pg, u->p->pgrp);
	else
		memcpy(pg->user, u->p->pgrp->user, NAMELEN);
	closepgrp(u->p->pgrp);
	u->p->pgrp = pg;
	return pg->pgrpid;

M port/chan.c => port/chan.c +1 -1
@@ 479,7 479,7 @@ namec(char *name, int amode, int omode, ulong perm)
		name = skipslash(name);
	}else if(name[0] == '#'){
		mntok = 0;
		if(name[1]=='|' || name[1]=='M')
		if(name[1]=='M')
			error(0, Enonexist);
		t = devno(name[1], 1);
		if(t == -1)

M port/devpipe.c => port/devpipe.c +157 -47
@@ 8,80 8,111 @@
#include	"devtab.h"
#include	"fcall.h"

typedef struct Pipe	Pipe;

struct Pipe
{
	Ref;
	int	debug;
	Pipe	*next;
};

struct Pipealloc
{
	Lock;
	Pipe *pipe;
	Pipe *free;
} pipealloc;

static void pipeiput(Queue*, Block*);
static void pipeoput(Queue*, Block*);
static void pipestclose(Queue *);
Qinfo pipeinfo = { pipeiput, pipeoput, 0, pipestclose, "pipe" };

Dirtab pipedir[]={
	"data",		Sdataqid,	0,			0600,
	"ctl",		Sctlqid,	0,			0600,
	"data1",	Sdataqid,	0,			0600,
	"ctl1",		Sctlqid,	0,			0600,
};
#define NPIPEDIR 4

void
pipeinit(void)
{
}

/*
 *  allocate structures for conf.npipe pipes
 */
void
pipereset(void)
{
	Pipe *p, *ep;

	pipealloc.pipe = ialloc(conf.npipe * sizeof(Pipe), 0);
	ep = &pipealloc.pipe[conf.npipe-1];
	for(p = pipealloc.pipe; p < ep; p++)
		p->next = p+1;
	pipealloc.free = pipealloc.pipe;
}

/*
 *  allocate both streams
 *
 *  a subsequent clone will get them the second stream
 *  create a pipe, no streams are created until an open
 */
Chan*
pipeattach(char *spec)
{
	Pipe *p;
	Chan *c;
	int i;

	/*
	 *  make the first stream
	 */
	c = devattach('|', spec);
	c->qid = STREAMQID(0, Sdataqid);
	streamnew(c, &pipeinfo);

	lock(&pipealloc);
	if(pipealloc.free == 0){
		unlock(&pipealloc);
		error(0, Enopipe);
	}
	p = pipealloc.free;
	pipealloc.free = p->next;
	p->ref = 1;
	unlock(&pipealloc);

	c->qid = CHDIR|STREAMQID(2*(p - pipealloc.pipe), 0);
	return c;
}

Chan*
pipeclone(Chan *c, Chan *nc)
{
	/*
	 *  make the second stream 
	 */
	Pipe *p;

	p = &pipealloc.pipe[STREAMID(c->qid)/2];
	nc = devclone(c, nc);
	if(waserror()){
		close(nc);
		nexterror();
	}
	nc->qid = STREAMQID(1, Sdataqid);
	streamnew(nc, &pipeinfo);
	poperror();
	incref(p);
	return nc;
}

	/*
	 *  attach it to the first
	 */
	c->stream->devq->ptr = (Stream *)nc->stream;
	nc->stream->devq->ptr = (Stream *)c->stream;
	c->stream->devq->other->next = nc->stream->devq;
	nc->stream->devq->other->next = c->stream->devq;
int
pipegen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp)
{
	int id;

	/*
	 *  up the inuse count of each stream to reflect the
	 *  pointer from the other stream.
	 */
	if(streamenter(c->stream)<0)
		panic("pipeattach");
	if(streamenter(nc->stream)<0)
		panic("pipeattach");
	return nc;
	id = STREAMID(c->qid);
	if(i > 1)
		id++;
	if(tab==0 || i>=ntab)
		return -1;
	tab += i;
	devdir(c, STREAMQID(id, tab->qid), tab->name, tab->length, tab->perm, dp);
	return 1;
}


int
pipewalk(Chan *c, char *name)
{
	print("pipewalk\n");
	error(0, Egreg);
	return devwalk(c, name, pipedir, NPIPEDIR, pipegen);
}

void


@@ 90,10 121,60 @@ pipestat(Chan *c, char *db)
	streamstat(c, db, "pipe");
}

/*
 *  if the stream doesn't exist, create it
 */
Chan *
pipeopen(Chan *c, int omode)
{
	c->mode = omode;
	Pipe *p;
	Stream *local, *remote;

	if(CHDIR & c->qid){
		if(omode != OREAD)
			error(0, Ebadarg);
		c->mode = omode;
		c->flag |= COPEN;
		c->offset = 0;
		return c;
	}

	p = &pipealloc.pipe[STREAMID(c->qid)/2];
	remote = 0;
	if(waserror()){
		unlock(p);
		if(remote)
			streamclose1(remote);
		nexterror();
	}
	lock(p);
	streamopen(c, &pipeinfo);
	local = c->stream;
	if(local->devq->ptr == 0){
		/*
		 *  First stream opened, create the other end also
		 */
		remote = streamnew(c->type, c->dev, STREAMID(c->qid)^1, &pipeinfo, 1);

		/*
		 *  connect the device ends of both streams
		 */
		local->devq->ptr = remote;
		remote->devq->ptr = local;
		local->devq->other->next = remote->devq;
		remote->devq->other->next = local->devq;

		/*
		 *  increment the inuse count to reflect the
		 *  pointer from the other stream.
		 */
		if(streamenter(local)<0)
			panic("pipeattach");
	}
	unlock(p);
	poperror();

	c->mode = omode&~OTRUNC;
	c->flag |= COPEN;
	c->offset = 0;
	return c;


@@ 118,27 199,56 @@ pipewstat(Chan *c, char *db)
}

void
pipeexit(Pipe *p)
{
	decref(p);
	if(p->ref <= 0){
		lock(&pipealloc);
		p->next = pipealloc.free;
		pipealloc.free = p;
		unlock(&pipealloc);
	}
}

void
pipeclose(Chan *c)
{
	Stream *other;
	Stream *remote;
	Stream *local;
	Pipe *p;

	other = (Stream *)c->stream->devq->ptr;
	p = &pipealloc.pipe[STREAMID(c->qid)/2];

	if(waserror()){
		streamexit(other, 0);
		nexterror();
	/*
	 *  take care of assosiated streams
	 */
	if(local = c->stream){
		remote = (Stream *)c->stream->devq->ptr;
		if(waserror()){
			streamexit(remote, 0);
			pipeexit(p);
			nexterror();
		}
		streamclose(c);		/* close this stream */
		streamexit(remote, 0);	/* release stream for other half of pipe */
		poperror();
	}
	streamclose(c);		/* close this stream */
	streamexit(other, 0);	/* release stream for other half of pipe */
	poperror();
	pipeexit(p);
}

long
piperead(Chan *c, void *va, long n)
{
	return streamread(c, va, n);
	if(CHDIR&c->qid)
		return devdirread(c, va, n, pipedir, NPIPEDIR, pipegen);
	else
		return streamread(c, va, n);
}

/*
 *  a write to a closed pipe causes a note to be sent to
 *  the process.
 */
long
pipewrite(Chan *c, void *va, long n)
{

A port/stasync.c => port/stasync.c +453 -0
@@ 0,0 1,453 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"

#define DPRINT 	if(asyncdebug)kprint

/*
 *  configuration
 */
enum {
	MAXFRAME=	256,	/* also known to tsm8 code */
};

/* input states */
enum { Hunt=0, Framing, Framed, Data, Escape };

typedef struct Async {
	QLock;

	int	inuse;
	Queue	*wq;

	/* output state */

	QLock	xmit;		/* transmit lock */
	int	chan;		/* current urp channel */
	Block	*bp;		/* current output buffer */
	int	count;
	ushort	crc;

	/* input state */

	int	state;		/* input state */
	uchar	buf[MAXFRAME];	/* current input buffer */
	int	icount;
	ushort	icrc;

	/* statistics */

	ulong	chan0;
	ulong	toolong;
	ulong	tooshort;
	ulong	badcrc;
	ulong	badescape;
	ulong	in;		/* bytes in */
	ulong	out;		/* bytes out */
} Async;

Async *async;

/*
 *  async stream module definition
 */
static void asynciput(Queue*, Block*);
static void asyncoput(Queue*, Block*);
static void asyncopen(Queue*, Stream*);
static void asyncclose(Queue*);
static void asyncreset(void);
Qinfo asyncinfo = { asynciput, asyncoput, asyncopen, asyncclose, "async", asyncreset };

int asyncdebug = 0;
int asyncerror;

static ushort crc_table[256] = {
#include "crc_16.h"
};

#define	BOT	0050		/* begin trailer */
#define	BOTM	0051		/* begin trailer, more data follows */
#define	BOTS	0052		/* seq update alg. on this trailer */

#define	FRAME		0x7e
#define	STUF		0x9d

#define	CRCSTART	(crc_table[0xff])
#define	CRCFUNC(crc,x)	(crc_table[((crc)^(x))&0xff]^((crc)>>8))

/*
 *  create the async structures
 */
static void
asyncreset(void)
{
	async = (Async *)ialloc(conf.nasync*sizeof(Async), 0);
}

/*
 *  allocate an async structure
 */
static void
asyncopen(Queue *q, Stream *s)
{
	Async *ap;

	DPRINT("asyncopen %d\n", s->dev);

	for(ap = async; ap < &async[conf.nasync]; ap++){
		qlock(ap);
		if(ap->inuse == 0)
			break;
		qunlock(ap);
	}
	if(ap == &async[conf.nasync])
		error(0, Enoasync);
	q->ptr = q->other->ptr = ap;

	ap->inuse = 1;
	ap->bp = 0;
	ap->chan = -1;
	ap->count = 0;
	ap->toolong = 0;
	ap->tooshort = 0;
	ap->badcrc = 0;
	ap->badescape = 0;
	ap->chan0 = 0;
	ap->in = 0;
	ap->out = 0;
	ap->wq = WR(q);
	ap->state = Hunt;
	qunlock(ap);
}

static void
asyncclose(Queue * q)
{
	Async *ap = (Async *)q->ptr;

	DPRINT("asyncstclose %d\n", ap-async);
	qlock(ap);
	ap->inuse = 0;
	qunlock(ap);
}

/*
 *  free all blocks of a message in `q', `bp' is the first block
 *  of the message
 */
static void
freemsg(Queue *q, Block *bp)
{
	for(; bp; bp = getq(q)){
		if(bp->flags & S_DELIM){
			freeb(bp);
			return;
		}
		freeb(bp);
	}
}

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

void
aswrite(Async *ap)
{
	if(ap->bp->rptr == ap->bp->wptr)
		return;
	FLOWCTL(ap->wq);
	PUTNEXT(ap->wq, ap->bp);
	ap->bp = 0;
}

void
asputf(Async *ap, int frame)
{
	uchar *p;
	int c;

	p = ap->bp->wptr;
	if(ap->count > 0) {
		if(asyncerror)
			ap->crc^=1, asyncerror=0;
		*p++ = c = ap->crc&0xff;
		if(c == FRAME)
			*p++ = 0x00;
		*p++ = c = (ap->crc>>8)&0xff;
		if(c == FRAME)
			*p++ = 0x00;
		ap->count = 0;
	}
	if(frame) {
		*p++ = FRAME;
		*p++ = FRAME;
	}
	ap->bp->wptr = p;
	if(asyncdebug > 2)
		showframe("out", ap, ap->bp->rptr, BLEN(ap->bp));
	aswrite(ap);
}

void
asputc(Async *ap, int c)
{
	int d;
	uchar *p;

	if(ap->bp == 0)
		ap->bp = allocb(MAXFRAME+4);
	p = ap->bp->wptr;
	if(ap->count <= 0) {
		*p++ = FRAME;
		*p++ = FRAME;
		*p++ = d = 0x80|((ap->chan>>5)&0x7e);
		ap->crc = CRCFUNC(CRCSTART, d);
		*p++ = d = 0x80|((ap->chan<<1)&0x7e);
		ap->crc = CRCFUNC(ap->crc, d);
	}
	*p++ = c;
	if(c == FRAME)
		*p++ = 0x00;
	ap->crc = CRCFUNC(ap->crc, c);
	ap->bp->wptr = p;
	if(++ap->count >= MAXFRAME-4)
		asputf(ap, 0);
	else if(ap->bp->lim - p < 8)
		aswrite(ap);
}

/*
 *  output a block
 *
 *  the first 2 bytes of every message are the channel number,
 *  low order byte first.  the third is a possible trailing control
 *  character.
 */
void
asyncoput(Queue *q, Block *bp)
{
	Async *ap = (Async *)q->ptr;
	int c, chan, ctl;

	if(bp->type != M_DATA){
		freeb(bp);
		return;
	}

	/*
	 *  get a whole message before handing bytes to the device
	 */
	if(!putq(q, bp))
		return;

	/*
	 *  one transmitter at a time
	 */
	qlock(&ap->xmit);

	/*
	 *  parse message
	 */
	bp = getq(q);
	if(bp->wptr - bp->rptr < 3){
		freemsg(q, bp);
		qunlock(&ap->xmit);
		return;
	}
	chan = bp->rptr[0] | (bp->rptr[1]<<8);
	ctl = bp->rptr[2];
	bp->rptr += 3;

	/*
	 *  new frame if the channel number has changed
	 */
	if(chan != ap->chan && ap->count > 0)
		asputf(ap, 0);
	ap->chan = chan;

	/*
	 *  send the 8 bit data
	 */
	for(;;){
		/*
		 *  put in next packet
		 */
		while (bp->rptr < bp->wptr) {
			asputc(ap, c = *bp->rptr++);
			if(c == STUF)
				asputc(ap, 0);
		}

		/*
		 *  get next block 
		 */
		if(bp->flags & S_DELIM){
			freeb(bp);
			break;
		}
		freeb(bp);
		bp = getq(q);
		if(bp==0)
			break;
	}

	/*
	 *  send the control byte if there is one
	 */
	if(ctl){
		asputc(ap, STUF);
		asputc(ap, ctl);
		switch (ctl) {
		case BOT:
		case BOTM:
		case BOTS:
			break;
		default:
			asputf(ap, 1);
		}
	}

	qunlock(&ap->xmit);
	return;
}

/*
 *  Read bytes from the raw input.
 */

void
asdeliver(Queue *q, Async *ap)
{
	int chan, c;
	Block *bp = 0;
	uchar *p = ap->buf;
	int n = ap->icount;

	chan = *p++ & 0x7e;
	chan = (chan<<5)|((*p++ & 0x7e)>>1);
	if(chan==0) {
		DPRINT("a%d deliver chan 0\n", ap-async);
		ap->chan0++;
		return;
	}
	for (n-=4; n>0; n--) {
		if(!bp) {
			bp = allocb(n+2);
			bp->flags |= S_DELIM;
			bp->wptr[0] = chan;
			bp->wptr[1] = chan>>8;
			bp->wptr[2] = 0;
			bp->wptr += 3;
		}
		if((c = *p++) == STUF) {
			--n;
			if((c = *p++) != 0) {
				bp->rptr[2] = c;
				if(asyncdebug > 1)
					kprint("a%d<-(%d)%3.3uo %d\n",
						ap-async, chan, bp->rptr[2],
						bp->wptr - bp->rptr - 3);
				PUTNEXT(q, bp);
				bp = 0;
				continue;
			} else
				c = STUF;
		}
		*bp->wptr++ = c;
	}
	if(bp) {
		if(asyncdebug > 1)
			kprint("a%d<-(%d)%3.3uo %d\n",
				ap-async, chan, bp->rptr[2],
				bp->wptr - bp->rptr - 3);
		PUTNEXT(q, bp);
	}
}

static void
asynciput(Queue *q, Block *bp)
{
	int c;
	Async *ap = q->ptr;
	int state = ap->state;

	while(bp->wptr > bp->rptr){
		c = *bp->rptr++;
		switch(state) {
		case Hunt:	/* wait for framing byte */
			if(c == FRAME)
				state = Framing;
			break;
	
		case Framing:	/* saw 1 framing byte after Hunt */
			if(c == FRAME)
				state = Framed;
			else
				state = Hunt;
			break;
	
		case Framed:	/* saw 2 or more framing bytes */
			if(c == FRAME)
				break;
			state = Data;
			ap->icrc = CRCSTART;
			ap->icount = 0;
			goto Datachar;
	
		case Data:	/* mid-frame */
			if(c == FRAME) {
				state = Escape;
				break;
			}
		Datachar:
			if(ap->icount >= MAXFRAME) {
				DPRINT("a%d pkt too long\n", ap-async);
				ap->toolong++;
				state = Hunt;
				break;
			}
			ap->icrc = CRCFUNC(ap->icrc, c);
			ap->buf[ap->icount++] = c;
			break;
	
		case Escape:	/* saw framing byte in Data */
			switch (c) {
			case FRAME:
				if(asyncdebug > 2)
					showframe("in", ap, ap->buf, ap->icount);
				if(ap->icount < 5) {
					DPRINT("a%d pkt too short\n", ap-async);
					ap->tooshort++;
				} else if(ap->icrc != 0) {
					DPRINT("a%d bad crc\n", ap-async);
					ap->badcrc++;
				} else {
					asdeliver(q, ap);
				}
				state = Framed;
				break;
			case 0:
				c = FRAME;
				state = Data;
				goto Datachar;
			default:
				DPRINT("a%d bad escape\n", ap-async);
				ap->badescape++;
				state = Hunt;
				break;
			}
			break;
		}
	}
	ap->state = state;
	freeb(bp);
}

M port/stream.c => port/stream.c +27 -23
@@ 235,6 235,7 @@ allocq(Qinfo *qi)
	q->info = qi;
	q->put = qi->iput;
	q->len = q->nb = 0;
	q->ptr = 0;
	wq = q->other = q + 1;

	wq->flag = QINUSE;


@@ 242,6 243,7 @@ allocq(Qinfo *qi)
	wq->info = qi;
	wq->put = qi->oput;
	wq->other = q;
	wq->ptr = 0;
	wq->len = wq->nb = 0;

	unlock(q);


@@ 625,10 627,10 @@ streamgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp)
}

/*
 *  create a new stream
 *  create a new stream, if noopen is non-zero, don't increment the open count
 */
Stream *
streamnew(Chan *c, Qinfo *qi)
streamnew(ushort type, ushort dev, ushort id, Qinfo *qi, int noopen)
{
	Stream *s;
	Queue *q;


@@ 651,26 653,25 @@ streamnew(Chan *c, Qinfo *qi)
	}
	if(waserror()){
		unlock(s);
		streamclose(c);
		streamclose1(s);
		nexterror();
	}

	/*
	 *  marry a stream and a channel
	 *  identify the stream
	 */
	if(c){
		c->stream = s;
		s->type = c->type;
		s->dev = c->dev;
		s->id = STREAMID(c->qid);
	} else
		s->type = -1;
	s->type = type;
	s->dev = dev;
	s->id = id;

	/*
 	 *  hang a device and process q off the stream
	 */
	s->inuse = 1;
	s->opens = 1;
	if(noopen)
		s->opens = 0;
	else
		s->opens = 1;
	s->hread = 0;
	q = allocq(&procinfo);
	s->procq = WR(q);


@@ 699,7 700,7 @@ streamopen(Chan *c, Qinfo *qi)
	Queue *q;

	/*
	 *  if the stream already exists, just up the reference count.
	 *  if the stream already exists, just increment the reference counts.
	 */
	for(s = slist; s < &slist[conf.nstream]; s++) {
		if(s->inuse && s->type == c->type && s->dev == c->dev


@@ 721,7 722,7 @@ streamopen(Chan *c, Qinfo *qi)
	/*
	 *  create a new stream
	 */
	streamnew(c, qi);
	c->stream = streamnew(c->type, c->dev, STREAMID(c->qid), qi, 0);
}

/*


@@ 773,17 774,10 @@ streamexit(Stream *s, int locked)
 *  stream release its blocks and call its close routine.
 */
void
streamclose(Chan *c)
streamclose1(Stream *s)
{
	Queue *q, *nq;
	Block *bp;
	Stream *s = c->stream;

	/*
	 *  if not open, ignore it
	 */
	if(!c->stream)
		return;

	/*
	 *  decrement the reference count


@@ 817,6 811,16 @@ streamclose(Chan *c)
	streamexit(s, 1);
	unlock(s);
}
void
streamclose(Chan *c)
{
	/*
	 *  if no stream, ignore it
	 */
	if(!c->stream)
		return;
	streamclose1(c->stream);
}

/*
 *  put a block to be read into the queue.  wakeup any waiting reader


@@ 1174,7 1178,7 @@ dumpblocks(Queue *q, char c)

	lock(q);
	for(bp = q->first; bp; bp = bp->next){
		print("%c%d%c", c, bp->wptr-bp->rptr, (bp->flags&S_DELIM));
		print("%c%d%c", c, bp->wptr-bp->rptr, (bp->flags&S_DELIM)?'D':' ');
		for(cp = bp->rptr; cp<bp->wptr && cp<bp->rptr+10; cp++)
			print(" %uo", *cp);
		print("\n");

M port/sysfile.c => port/sysfile.c +44 -42
@@ 31,12 31,12 @@ fdtochan(int fd, int mode)

	if(fd<0 || NFD<=fd || (c=u->fd[fd])==0)
		error(0, Ebadfd);
	if(mode<0 || c->mode == 2)
	if(mode<0 || c->mode==ORDWR)
		return c;
	if((mode&16) && c->mode==0)
	if((mode&OTRUNC) && c->mode==OREAD)
    err:
		error(0, Ebadusefd);
	if((mode&~16) != c->mode)
	if((mode&~OTRUNC) != c->mode)
		goto err;
	return c;
}


@@ 56,6 56,45 @@ openmode(ulong o)
}

long
syspipe(ulong *arg)
{
	int fd[2];
	Chan *c[2];
	Dev *d;

	validaddr(arg[0], 2*BY2WD, 1);
	evenaddr(arg[0]);
	d = &devtab[devno('|', 0)];
	c[0] = (*d->attach)(0);
	c[1] = 0;
	fd[0] = -1;
	fd[1] = -1;
	if(waserror()){
		close(c[0]);
		if(c[1])
			close(c[1]);
		if(fd[0] >= 0)
			u->fd[fd[0]]=0;
		if(fd[1] >= 0)
			u->fd[fd[1]]=0;
		nexterror();
	}
	c[1] = (*d->clone)(c[0], 0);
	(*d->walk)(c[0], "data");
	(*d->walk)(c[1], "data1");
	c[0] = (*d->open)(c[0], ORDWR);
	c[1] = (*d->open)(c[1], ORDWR);
	fd[0] = newfd();
	u->fd[fd[0]] = c[0];
	fd[1] = newfd();
	u->fd[fd[1]] = c[1];
	((long*)arg[0])[0] = fd[0];
	((long*)arg[0])[1] = fd[1];
	poperror();
	return 0;
}

long
sysdup(ulong *arg)
{
	int fd;


@@ 180,7 219,7 @@ sysread(ulong *arg)
	Chan *c;
	long n;

	c = fdtochan(arg[0], 0);
	c = fdtochan(arg[0], OREAD);
	validaddr(arg[1], arg[2], 1);
	qlock(c);
	if(waserror()){


@@ 208,7 247,7 @@ syswrite(ulong *arg)
	Chan *c;
	long n;

	c = fdtochan(arg[0], 1);
	c = fdtochan(arg[0], OWRITE);
	validaddr(arg[1], arg[2], 0);
	qlock(c);
	if(waserror()){


@@ 383,43 422,6 @@ sysmount(ulong *arg)
}

long
syspipe(ulong *arg)
{
	int fd[2];
	Chan *c[2];
	Dev *d;

	validaddr(arg[0], 2*BY2WD, 1);
	evenaddr(arg[0]);
	d = &devtab[devno('|', 0)];
	c[0] = (*d->attach)(0);
	c[1] = 0;
	fd[0] = -1;
	fd[1] = -1;
	if(waserror()){
		close(c[0]);
		if(c[1])
			close(c[1]);
		if(fd[0] >= 0)
			u->fd[fd[0]]=0;
		if(fd[1] >= 0)
			u->fd[fd[1]]=0;
		nexterror();
	}
	c[1] = (*d->clone)(c[0], 0);
	c[0] = (*d->open)(c[0], ORDWR);
	c[1] = (*d->open)(c[1], ORDWR);
	fd[0] = newfd();
	u->fd[fd[0]] = c[0];
	fd[1] = newfd();
	u->fd[fd[1]] = c[1];
	((long*)arg[0])[0] = fd[0];
	((long*)arg[0])[1] = fd[1];
	poperror();
	return 0;
}

long
syscreate(ulong *arg)
{
	int fd;

M port/sysproc.c => port/sysproc.c +2 -0
@@ 496,6 496,8 @@ sysforkpgrp(ulong *arg)
	}
	if(arg[0] == 0)
		pgrpcpy(pg, u->p->pgrp);
	else
		memcpy(pg->user, u->p->pgrp->user, NAMELEN);
	closepgrp(u->p->pgrp);
	u->p->pgrp = pg;
	return pg->pgrpid;

M power/conf.h => power/conf.h +2 -0
@@ 24,6 24,8 @@ Conftab conftab[] = {
	{"nnoifc", &conf.nnoifc },
	{"nnoconv", &conf.nnoconv },
	{"nurp", &conf.nurp },
	{"nasync", &conf.nasync },
	{"npipe", &conf.npipe },
	{ 0, 0 },
};


M power/dat.h => power/dat.h +8 -7
@@ 148,6 148,8 @@ struct Conf
	ulong	nnoifc;		/* number of nonet interfaces */
	ulong	nnoconv;	/* number of nonet conversations/ifc */
	ulong	nurp;		/* max urp conversations */
	ulong	nasync;		/* number of async protocol modules */
	ulong	npipe;		/* number of pipes */
};

struct Dev


@@ 450,14 452,13 @@ struct Queue {
 */
struct Stream {
	Lock;			/* structure lock */
	int	inuse;		/* number of processes in stream */
	int	opens;		/* number of processes with stream open */
	int	hread;		/* number of reads after hangup */
	int	type;		/* correclation with Chan */
	int	dev;		/* ... */
	int	id;		/* ... */
	short	inuse;		/* number of processes in stream */
	short	opens;		/* number of processes with stream open */
	ushort	hread;		/* number of reads after hangup */
	ushort	type;		/* correlation with Chan */
	ushort	dev;		/* ... */
	ushort	id;		/* ... */
	QLock	rdlock;		/* read lock */
	QLock	wrlock;		/* write lock */
	Queue	*procq;		/* write queue at process end */
	Queue	*devq;		/* read queue at device end */
};

M power/errno.h => power/errno.h +2 -0
@@ 51,5 51,7 @@ enum{
	Ebadcnt,	/* read count greater than requested */
	Enoannounce,	/* listening on an unannounced network connection */
	Enovmem,	/* virtual memory allocation failed */
	Enoasync,	/* out of async stream modules */
	Enopipe,	/* out of pipes */
	Egreg,		/* ken hasn't implemented datakit */
};

M power/fns.h => power/fns.h +2 -1
@@ 169,12 169,13 @@ void	splx(int);
void	sunmap(int, uchar*);
Devgen	streamgen;
void	streamclose(Chan*);
void	streamclose1(Stream*);
int	streamenter(Stream*);
void	streamexit(Stream*, int);
void	streaminit(void);
long	streamread(Chan*, void*, long);
long	streamwrite(Chan*, void*, long, int);
Stream*	streamnew(Chan*, Qinfo*);
Stream*	streamnew(ushort, ushort, ushort, Qinfo*, int);
void	streamopen(Chan*, Qinfo*);
int	streamparse(char*, Block*);
void	streamstat(Chan*, char*, char*);

M power/main.c => power/main.c +1 -0
@@ 612,6 612,7 @@ confinit(void)
	conf.npte = 4 * conf.npage;
	conf.nqueue = 3 * conf.nstream;
	conf.nblock = 10 * conf.nstream;
	conf.npipe = conf.nstream/2;

	confread();