plan 9 kernel history: overview | file list | diff list

1991/1101/port/devnonet.c (diff list | history)

port/devnonet.c on 1990/1210
1990/1210    
#include	"u.h" 
#include	"lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
#include	"errno.h" 
1991/0328    
#include	"../port/nonet.h" 
1990/1210    
 
1990/1229    
#define DPRINT if(pnonet)print 
1990/1210    
#define NOW (MACHP(0)->ticks*MS2HZ) 
1991/0206    
#define MSUCC(x) (((x)+1)%Nnomsg) 
1990/1210    
 
static Noifc *noifc; 
1990/1229    
int pnonet; 
1990/1210    
 
enum { 
	/* 
	 *  tuning parameters 
	 */ 
1991/0108    
	MSrexmit = 250,		/* retranmission interval in ms */ 
1990/1210    
 
	/* 
	 *  relative or immutable 
	 */ 
	Nsubdir = 5,		/* entries in the nonet directory */ 
	Nmask = Nnomsg - 1,	/* mask for log(Nnomsg) bits */ 
}; 
 
/* predeclared */ 
1991/0119    
static void	nohangup(Noconv*); 
static void	noreset(Noconv*); 
static Block*	nohdr(Noconv*, int); 
static void	nolisten(Chan*, Noifc*); 
static void	noannounce(Chan*, char*); 
static void	noconnect(Chan*, char*); 
static void	norack(Noconv*, int); 
static void	nosendctl(Noconv*, int, int); 
static void	nosend(Noconv*, Nomsg*); 
static void	nostartconv(Noconv*, int, char*, int); 
static void	noqack(Noconv*, int); 
static void	nokproc(void*); 
static void	noiput(Queue*, Block*); 
static void	nooput(Queue*, Block*); 
static void	noclose(Queue*); 
static void	noopen(Queue*, Stream*); 
1990/1210    
 
extern Qinfo	noetherinfo; 
extern Qinfo	nonetinfo; 
 
/* 
 *  nonet directory and subdirectory 
 */ 
enum { 
	/* 
	 *  per conversation 
	 */ 
	Naddrqid, 
	Nlistenqid, 
	Nraddrqid, 
	Nruserqid, 
	Nstatsqid, 
	Nchanqid, 
 
	/* 
	 *  per noifc 
	 */ 
	Ncloneqid, 
}; 
 
Dirtab *nonetdir; 
Dirtab nosubdir[]={ 
	"addr",		{Naddrqid},	0,	0600, 
	"listen",	{Nlistenqid},	0,	0600, 
	"raddr",	{Nraddrqid},	0,	0600, 
	"ruser",	{Nruserqid},	0,	0600, 
	"stats",	{Nstatsqid},	0,	0600, 
}; 
 
/* 
 *  Nonet conversation states (for Noconv.state) 
 */ 
enum { 
1991/0413    
	Cclosed=	0, 
	Copen=		1, 
	Cannounced=	2, 
	Cconnected=	3, 
	Cconnecting=	4, 
	Chungup=	5, 
	Creset=		6, 
1990/1210    
}; 
 
/* 
1991/0108    
 *  nonet kproc 
 */ 
static int kstarted; 
static Rendez nonetkr; 
 
/* 
1990/1210    
 *  nonet file system.  most of the calls use dev.c to access the nonet 
 *  directory and stream.c to access the nonet devices. 
 *  create the nonet directory.  the files are `clone' and stream 
 *  directories '1' to '32' (or whatever conf.noconv is in decimal) 
 */ 
void 
nonetreset(void) 
{ 
	int i; 
 
	/* 
	 *  allocate the interfaces 
	 */ 
	noifc = (Noifc *)ialloc(sizeof(Noifc) * conf.nnoifc, 0); 
1990/1229    
	for(i = 0; i < conf.nnoifc; i++) 
1990/1210    
		noifc[i].conv = (Noconv *)ialloc(sizeof(Noconv) * conf.nnoconv, 0); 
 
	/* 
	 *  create the directory. 
	 */ 
	nonetdir = (Dirtab *)ialloc(sizeof(Dirtab) * (conf.nnoconv+1), 0); 
 
	/* 
	 *  the circuits 
	 */ 
	for(i = 0; i < conf.nnoconv; i++) { 
		sprint(nonetdir[i].name, "%d", i); 
		nonetdir[i].qid.path = CHDIR|STREAMQID(i, Nchanqid); 
		nonetdir[i].qid.vers = 0; 
		nonetdir[i].length = 0; 
		nonetdir[i].perm = 0600; 
	} 
 
	/* 
	 *  the clone device 
	 */ 
	strcpy(nonetdir[i].name, "clone"); 
	nonetdir[i].qid.path = Ncloneqid; 
	nonetdir[i].qid.vers = 0; 
	nonetdir[i].length = 0; 
	nonetdir[i].perm = 0600; 
} 
 
void 
nonetinit(void) 
{ 
} 
 
/* 
 *  find an noifc and increment its count 
 */ 
Chan* 
nonetattach(char *spec) 
{ 
	Noifc *ifc; 
	Chan *c; 
 
	/* 
	 *  find an noifc with the same name 
	 */ 
	if(*spec == 0) 
		spec = "nonet"; 
	for(ifc = noifc; ifc < &noifc[conf.nnoifc]; ifc++){ 
		lock(ifc); 
		if(strcmp(spec, ifc->name)==0 && ifc->wq) { 
			ifc->ref++; 
			unlock(ifc); 
			break; 
		} 
		unlock(ifc); 
	} 
	if(ifc == &noifc[conf.nnoifc]) 
		error(Enoifc); 
	c = devattach('n', spec); 
	c->dev = ifc - noifc; 
1991/0108    
 
1990/1210    
	return c; 
} 
 
/* 
 *  up the reference count to the noifc 
 */ 
Chan* 
nonetclone(Chan *c, Chan *nc) 
{ 
	Noifc *ifc; 
 
	c = devclone(c, nc); 
	ifc = &noifc[c->dev]; 
	lock(ifc); 
	ifc->ref++; 
	unlock(ifc); 
	return c; 
} 
 
int	  
nonetwalk(Chan *c, char *name) 
{ 
	if(c->qid.path == CHDIR) 
		return devwalk(c, name, nonetdir, conf.nnoconv+1, devgen); 
	else 
		return devwalk(c, name, nosubdir, Nsubdir, streamgen); 
} 
 
void	  
nonetstat(Chan *c, char *dp) 
{ 
	if(c->qid.path == CHDIR) 
		devstat(c, dp, nonetdir, conf.nnoconv+1, devgen); 
	else if(c->qid.path == Ncloneqid) 
1991/0412    
		devstat(c, dp, &nonetdir[conf.nnoconv], 1, devgen); 
1990/1210    
	else 
		devstat(c, dp, nosubdir, Nsubdir, streamgen); 
} 
 
/* 
 *  opening a nonet device allocates a Noconv.  Opening the `clone' 
 *  device is a ``macro'' for finding a free Noconv and opening 
 *  it's ctl file. 
 */ 
Noconv * 
nonetopenclone(Chan *c, Noifc *ifc) 
{ 
	Noconv *cp; 
	Noconv *ep; 
 
	ep = &ifc->conv[conf.nnoconv]; 
	for(cp = &ifc->conv[0]; cp < ep; cp++){ 
		if(cp->state == Cclosed && canqlock(cp)){ 
			if(cp->state != Cclosed){ 
				qunlock(cp); 
				continue; 
			} 
			c->qid.path = CHDIR|STREAMQID(cp-ifc->conv, Nchanqid); 
			devwalk(c, "ctl", 0, 0, streamgen); 
			streamopen(c, &nonetinfo); 
			qunlock(cp); 
			break; 
		} 
	} 
	if(cp == ep) 
		error(Enodev);; 
	return cp; 
} 
 
Chan* 
nonetopen(Chan *c, int omode) 
{ 
	Stream *s; 
	Noifc *ifc; 
	int line; 
 
1991/0108    
	if(!kstarted){ 
1991/0119    
		kproc("nonetack", nokproc, 0); 
1991/0108    
		kstarted = 1; 
	} 
 
1990/1210    
	if(c->qid.path & CHDIR){ 
		/* 
		 *  directories are read only 
		 */ 
		if(omode != OREAD) 
			error(Ebadarg); 
	} else switch(STREAMTYPE(c->qid.path)){ 
	case Ncloneqid: 
		/* 
		 *  get an unused device and open it's control file 
		 */ 
		ifc = &noifc[c->dev]; 
1990/1214    
		nonetopenclone(c, ifc); 
1990/1210    
		break; 
	case Nlistenqid: 
		/* 
		 *  listen for a call and open the control file for the 
		 *  channel on which the call arrived. 
		 */ 
		line = STREAMID(c->qid.path); 
		ifc = &noifc[c->dev]; 
		if(ifc->conv[line].state != Cannounced) 
1991/0316    
			errors("channel not announced"); 
1991/0119    
		nolisten(c, ifc); 
1990/1210    
		break; 
	case Nraddrqid: 
		/* 
		 *  read only files 
		 */ 
		if(omode != OREAD) 
			error(Ebadarg); 
		break; 
	default: 
		/* 
		 *  open whatever c points to 
		 */ 
		streamopen(c, &nonetinfo); 
		break; 
	} 
 
	c->mode = openmode(omode); 
	c->flag |= COPEN; 
	c->offset = 0; 
	return c; 
} 
 
void	  
nonetcreate(Chan *c, char *name, int omode, ulong perm) 
{ 
	error(Eperm); 
} 
 
void	  
nonetclose(Chan *c) 
{ 
	Noifc *ifc; 
 
	/* 
1991/0119    
	 *  real closing happens in noclose 
1990/1210    
	 */ 
	if(c->qid.path != CHDIR) 
		streamclose(c); 
 
	/* 
	 *  let go of the multiplexor 
	 */ 
	nonetfreeifc(&noifc[c->dev]); 
} 
 
long	  
1991/0411    
nonetread(Chan *c, void *a, long n, ulong offset) 
1990/1210    
{ 
	int t; 
	Noconv *cp; 
	char stats[256]; 
 
	/* 
	 *  pass data/ctl reads onto the stream code 
	 */ 
	t = STREAMTYPE(c->qid.path); 
	if(t >= Slowqid) 
		return streamread(c, a, n); 
 
	if(c->qid.path == CHDIR) 
		return devdirread(c, a, n, nonetdir, conf.nnoconv+1, devgen); 
	if(c->qid.path & CHDIR) 
		return devdirread(c, a, n, nosubdir, Nsubdir, streamgen); 
 
	cp = &noifc[c->dev].conv[STREAMID(c->qid.path)]; 
	switch(t){ 
	case Nraddrqid: 
1991/0411    
		return stringread(c, a, n, cp->raddr, offset); 
1990/1210    
	case Naddrqid: 
1991/0411    
		return stringread(c, a, n, cp->addr, offset); 
1990/1210    
	case Nruserqid: 
1991/0411    
		return stringread(c, a, n, cp->ruser, offset); 
1990/1210    
	case Nstatsqid: 
		sprint(stats, "sent: %d\nrcved: %d\nrexmit: %d\nbad: %d\n", 
			cp->sent, cp->rcvd, cp->rexmit, cp->bad); 
1991/0411    
		return stringread(c, a, n, stats, offset); 
1990/1210    
	} 
	error(Eperm); 
} 
 
long	  
1991/0411    
nonetwrite(Chan *c, void *a, long n, ulong offset) 
1990/1210    
{ 
	int t; 
	int m; 
	char buf[256]; 
	char *field[5]; 
 
	t = STREAMTYPE(c->qid.path); 
 
	/* 
	 *  get data dispatched as quickly as possible 
	 */ 
	if(t == Sdataqid) 
		return streamwrite(c, a, n, 0); 
 
	/* 
1991/0119    
	 *  easier to do here than in nooput 
1990/1210    
	 */ 
	if(t == Sctlqid){ 
		strncpy(buf, a, sizeof buf); 
		m = getfields(buf, field, 5, ' '); 
		if(strcmp(field[0], "connect")==0){ 
			if(m < 2) 
				error(Ebadarg); 
1991/0119    
			noconnect(c, field[1]); 
1990/1210    
		} else if(strcmp(field[0], "announce")==0){ 
1991/0119    
			noannounce(c, field[1]); 
1990/1210    
		} else if(strcmp(field[0], "accept")==0){ 
			/* ignore */; 
		} else if(strcmp(field[0], "reject")==0){ 
			/* ignore */; 
		} else 
			return streamwrite(c, a, n, 0); 
		return n; 
	} 
	 
	error(Eperm); 
} 
 
void	  
nonetremove(Chan *c) 
{ 
	error(Eperm); 
} 
 
void	  
nonetwstat(Chan *c, char *dp) 
{ 
	error(Eperm); 
} 
 
/* 
 *  the device stream module definition 
 */ 
Qinfo nonetinfo = 
{ 
1991/0119    
	noiput, 
	nooput, 
	noopen, 
	noclose, 
1990/1210    
	"nonet" 
}; 
 
/* 
 *  store the device end of the stream so that the multiplexor can 
 *  send blocks upstream. 
1991/0119    
 * 
 *	State Transition:	Cclosed -> Copen 
1990/1210    
 */ 
static void 
1991/0119    
noopen(Queue *q, Stream *s) 
1990/1210    
{ 
	Noifc *ifc; 
	Noconv *cp; 
 
	ifc = &noifc[s->dev]; 
	cp = &ifc->conv[s->id]; 
	cp->s = s; 
	cp->ifc = ifc; 
	cp->rq = RD(q); 
	cp->state = Copen; 
	RD(q)->ptr = WR(q)->ptr = (void *)cp; 
} 
 
1991/0320    
 
1990/1210    
static int 
ishungup(void *a) 
{ 
	Noconv *cp; 
 
	cp = (Noconv *)a; 
1991/0119    
	switch(cp->state){ 
	case Chungup: 
	case Creset: 
	case Cclosed: 
		return 1; 
	} 
	return 0; 
1990/1210    
} 
1991/0320    
/* 
 *  wait until a hangup is received. 
 *  then send a hangup message (until one is received). 
 * 
 *	State Transitions:	* -> Cclosed 
 */ 
1990/1210    
static void 
1991/0119    
noclose(Queue *q) 
1990/1210    
{ 
	Noconv *cp; 
	Nomsg *mp; 
	int i; 
 
	cp = (Noconv *)q->ptr; 
 
	if(waserror()){ 
		cp->rcvcircuit = -1; 
		cp->state = Cclosed; 
		nexterror(); 
	} 
 
	/* 
	 *  send hangup messages to the other side 
	 *  until it hangs up or we get tired. 
	 */ 
1991/0119    
	switch(cp->state){ 
	case Cconnected: 
		/* 
		 *  send close till we get one back 
		 */ 
		nosendctl(cp, NO_HANGUP, 1); 
1990/1210    
		for(i=0; i<10 && !ishungup(cp); i++){ 
1991/0209    
			nosendctl(cp, NO_HANGUP, 0); 
1990/1210    
			tsleep(&cp->r, ishungup, cp, MSrexmit); 
		} 
1991/0119    
		break; 
	case Chungup: 
		/* 
		 *  ack any close 
		 */ 
		nosendctl(cp, NO_HANGUP, 1); 
		break; 
1990/1210    
	} 
 
1991/0108    
	qlock(cp); 
1991/0206    
	/* 
	 *  we give up, ack any unacked messages 
	 */ 
	for(i = cp->first; i != cp->next; i = MSUCC(i)) 
		norack(cp, cp->out[i].mid); 
1990/1210    
	cp->rcvcircuit = -1; 
	cp->state = Cclosed; 
1991/0328    
	if(cp->media){ 
		freeb(cp->media); 
		cp->media = 0; 
	} 
1991/0108    
	qunlock(cp); 
1991/0206    
 
1990/1210    
	poperror(); 
} 
 
/* 
 *  send all messages up stream.  this should only be control messages 
1991/0119    
 * 
 *	State Transition:	(on M_HANGUP) * -> Chungup 
1990/1210    
 */ 
static void 
1991/0119    
noiput(Queue *q, Block *bp) 
1990/1210    
{ 
	Noconv *cp; 
 
	if(bp->type == M_HANGUP){ 
		cp = (Noconv *)q->ptr; 
		cp->state = Chungup; 
	} 
	PUTNEXT(q, bp); 
} 
 
/* 
 *  queue a block 
 */ 
1991/0502    
enum { 
	Window=	1, 
}; 
 
1990/1210    
static int 
windowopen(void *a) 
{ 
	Noconv *cp; 
1991/0207    
	int i; 
1990/1210    
 
	cp = (Noconv *)a; 
1991/0207    
	i = cp->next - cp->first; 
1991/0502    
	if(i>=0 && i<Window)	 
1991/0207    
		return 1; 
1991/0502    
	if(i<0 && Nnomsg+i<Window) 
1991/0207    
		return 1; 
	return 0;	 
1990/1210    
} 
static void 
1991/0119    
nooput(Queue *q, Block *bp) 
1990/1210    
{ 
	Noconv *cp; 
	int next; 
	Nomsg *mp; 
1991/0926    
	Block *first, *last; 
	int len; 
1990/1210    
 
	cp = (Noconv *)(q->ptr); 
 
	/* 
	 *  do all control functions 
	 */ 
	if(bp->type != M_DATA){ 
		freeb(bp); 
		error(Ebadctl); 
	} 
 
	/* 
	 *  collect till we see a delim 
	 */ 
	qlock(&cp->mlock); 
	if(!putb(q, bp)){ 
		qunlock(&cp->mlock); 
		return; 
	} 
 
1991/0206    
	/* 
1991/0926    
	 *  take the collected message out of the queue 
	 */ 
	first = q->first; 
	last = q->last; 
	len = q->len; 
	q->len = q->nb = 0; 
	q->first = q->last = 0; 
	if(len == 0){ 
		freeb(first); 
		qunlock(&cp->mlock); 
		return; 
	} 
 
	/* 
1991/0206    
	 *  block till we get an output buffer 
	 */ 
1990/1210    
	if(waserror()){ 
1991/0926    
		freeb(first); 
1991/0109    
		qunlock(&cp->mlock); 
		nexterror(); 
1990/1210    
	} 
1991/0206    
	while(!windowopen(cp)) 
1990/1210    
		sleep(&cp->r, windowopen, cp); 
	mp = &cp->out[cp->next]; 
1991/0206    
	cp->next = MSUCC(cp->next); 
	qlock(cp); 
	qunlock(&cp->mlock); 
	poperror(); 
1990/1210    
 
	/* 
1991/0206    
	 *  point the output buffer to the message 
1990/1210    
	 */ 
	mp->time = NOW + MSrexmit; 
1991/0926    
	mp->first = first; 
	mp->last = last; 
	mp->len = len; 
1990/1210    
	mp->mid ^= Nnomsg; 
	mp->acked = 0; 
 
	cp->sent++; 
 
	/* 
1991/01151    
	 *  send the message, the kproc will retry 
1990/1210    
	 */ 
1991/0925    
	if(!waserror()){ 
		nosend(cp, mp); 
		poperror(); 
1991/0206    
	} 
	qunlock(cp); 
1990/1210    
} 
 
/* 
 *  start a new conversation.  start an ack/retransmit process if 
 *  none already exists for this circuit. 
 */ 
void 
1991/0119    
nostartconv(Noconv *cp, int circuit, char *raddr, int state) 
1990/1210    
{ 
	int i; 
	char name[32]; 
	Noifc *ifc; 
 
	ifc = cp->ifc; 
 
	/* 
	 *  allocate the prototype header 
	 */ 
	cp->media = allocb(ifc->hsize + NO_HDRSIZE); 
	cp->media->wptr += ifc->hsize + NO_HDRSIZE; 
	cp->hdr = (Nohdr *)(cp->media->rptr + ifc->hsize); 
 
	/* 
	 *  fill in the circuit number 
	 */ 
	cp->hdr->flag = NO_NEWCALL|NO_ACKME; 
	cp->hdr->circuit[2] = circuit>>16; 
	cp->hdr->circuit[1] = circuit>>8; 
	cp->hdr->circuit[0] = circuit; 
 
	/* 
	 *  set the state variables 
	 */ 
	cp->state = state; 
	for(i = 1; i < Nnomsg; i++){ 
		cp->in[i].mid = i; 
		cp->in[i].acked = 0; 
		cp->in[i].rem = 0; 
		cp->out[i].mid = i | Nnomsg; 
		cp->out[i].acked = 1; 
		cp->out[i].rem = 0; 
	} 
	cp->in[0].mid = Nnomsg; 
	cp->in[0].acked = 0; 
	cp->in[0].rem = 0; 
	cp->out[0].mid = 0; 
	cp->out[0].acked = 1; 
	cp->out[0].rem = 0; 
1991/1101    
	cp->ack = 0; 
	cp->sendack = 0; 
1990/1210    
	cp->first = cp->next = 1; 
1991/0119    
	cp->rexmit = cp->bad = cp->sent = cp->rcvd = 0; 
	cp->lastacked = Nnomsg|(Nnomsg-1); 
1990/1210    
 
	/* 
	 *  used for demultiplexing 
	 */ 
	cp->rcvcircuit = circuit ^ 1; 
 
	/* 
	 *  media dependent header 
	 */ 
	(*ifc->connect)(cp, raddr); 
 
	/* 
	 *  status files 
	 */ 
	strncpy(cp->raddr, raddr, sizeof(cp->raddr)); 
	strcpy(cp->ruser, "none"); 
} 
 
/* 
 *  announce willingness to take calls 
1991/0119    
 * 
 *	State Transition:	Copen -> Chungup 
1990/1210    
 */ 
static void 
1991/0119    
noannounce(Chan *c, char *addr) 
1990/1210    
{ 
	Noconv *cp; 
 
	cp = (Noconv *)(c->stream->devq->ptr); 
	if(cp->state != Copen) 
		error(Einuse); 
	cp->state = Cannounced; 
} 
 
/* 
 *  connect to the destination whose name is pointed to by bp->rptr. 
 * 
 *  a service is separated from the destination system by a '!' 
1991/0119    
 * 
 *	State Transition:	Copen -> Cconnecting 
1990/1210    
 */ 
static void 
1991/0119    
noconnect(Chan *c, char *addr) 
1990/1210    
{ 
	Noifc *ifc; 
	Noconv *cp; 
	char *service; 
	char buf[2*NAMELEN+2]; 
 
	cp = (Noconv *)(c->stream->devq->ptr); 
	if(cp->state != Copen) 
		error(Einuse); 
	ifc = cp->ifc; 
	service = strchr(addr, '!'); 
	if(service){ 
		*service++ = 0; 
		if(strchr(service, ' ')) 
			error(Ebadctl); 
		if(strlen(service) > NAMELEN) 
			error(Ebadctl); 
	} 
 
1991/0119    
	nostartconv(cp, 2*(cp - ifc->conv), addr, Cconnecting); 
1990/1210    
 
	if(service){ 
		/* 
		 *  send service name and user name 
		 */ 
		cp->hdr->flag |= NO_SERVICE; 
		sprint(buf, "%s %s", service, u->p->pgrp->user); 
		c->qid.path = STREAMQID(STREAMID(c->qid.path), Sdataqid); 
		streamwrite(c, buf, strlen(buf), 1); 
		c->qid.path = STREAMQID(STREAMID(c->qid.path), Sctlqid); 
	} 
} 
 
/* 
 *  listen for a call.  There can be many listeners, but only one can sleep 
 *  on the circular queue at a time.  ifc->listenl lets only one at a time into 
 *  the sleep. 
1991/0119    
 * 
 *	State Transition:	Cclosed -> Copen -> Cconnecting 
1990/1210    
 */ 
static int 
iscall(void *a) 
{ 
	Noifc *ifc; 
 
	ifc = (Noifc *)a; 
	return ifc->rptr != ifc->wptr; 
} 
static void 
1991/0119    
nolisten(Chan *c, Noifc *ifc) 
1990/1210    
{ 
	Noconv *cp, *ep; 
	Nocall call; 
	int f; 
	char buf[2*NAMELEN+4]; 
	char *user; 
	long n; 
 
	call.msg = 0; 
 
	if(waserror()){ 
		if(call.msg) 
			freeb(call.msg); 
		qunlock(&ifc->listenl); 
		nexterror(); 
	} 
 
	for(;;){ 
		/* 
		 *  one listener at a time 
		 */ 
		qlock(&ifc->listenl); 
 
		/* 
		 *  wait for a call 
		 */ 
		sleep(&ifc->listenr, iscall, ifc); 
		call = ifc->call[ifc->rptr]; 
		ifc->rptr = (ifc->rptr+1) % Nnocalls; 
		qunlock(&ifc->listenl); 
 
		/* 
		 *  make sure we aren't already using this circuit 
		 */ 
		ep = &ifc->conv[conf.nnoconv]; 
		for(cp = &ifc->conv[0]; cp < ep; cp++){ 
1991/0112    
			if(cp->state>Cannounced && (call.circuit^1)==cp->rcvcircuit 
1990/1210    
			&& strcmp(call.raddr, cp->raddr)==0) 
				break; 
		} 
		if(cp != ep){ 
			freeb(call.msg); 
			call.msg = 0; 
			continue; 
		} 
 
		/* 
		 *  get a free channel 
		 */ 
		cp = nonetopenclone(c, ifc); 
		c->qid.path = STREAMQID(cp - ifc->conv, Sctlqid); 
 
		/* 
		 *  start the protocol and 
		 *  stuff the connect message into it 
		 */ 
		f = ((Nohdr *)(call.msg->rptr))->flag; 
1991/0108    
		DPRINT("call from %d %s\n", call.circuit, call.raddr); 
1991/0119    
		nostartconv(cp, call.circuit, call.raddr, Cconnecting); 
1991/0108    
		DPRINT("rcving %d byte message\n", call.msg->wptr - call.msg->rptr); 
1990/1210    
		nonetrcvmsg(cp, call.msg); 
		call.msg = 0; 
 
		/* 
		 *  if a service and remote user were specified, 
		 *  grab them 
		 */ 
		if(f & NO_SERVICE){ 
1991/0108    
			DPRINT("reading service\n"); 
1990/1210    
			c->qid.path = STREAMQID(cp - ifc->conv, Sdataqid); 
			n = streamread(c, buf, sizeof(buf)); 
			c->qid.path = STREAMQID(cp - ifc->conv, Sctlqid); 
			if(n <= 0) 
				error(Ebadctl); 
			buf[n] = 0; 
			user = strchr(buf, ' '); 
			if(user){ 
				*user++ = 0; 
				strncpy(cp->ruser, user, NAMELEN); 
			} else 
				strcpy(cp->ruser, "none"); 
			strncpy(cp->addr, buf, NAMELEN); 
		} 
		break; 
	} 
	poperror(); 
} 
 
/* 
 *  send a hangup signal up the stream to get all line disciplines 
 *  to cease and desist 
1991/0119    
 * 
 *	State Transition:	{Cconnected, Cconnecting} -> Chungup 
1990/1210    
 */ 
static void 
1991/0119    
nohangup(Noconv *cp) 
1990/1210    
{ 
	Block *bp; 
	Queue *q; 
 
1991/0119    
	switch(cp->state){ 
	case Cconnected: 
	case Cconnecting: 
		cp->state = Chungup; 
		bp = allocb(0); 
		bp->type = M_HANGUP; 
		q = cp->rq; 
		PUTNEXT(q, bp); 
		break; 
	} 
1990/1210    
	wakeup(&cp->r); 
} 
 
/* 
1991/0328    
 *  Send a hangup signal up the stream to get all line disciplines 
 *  to cease and desist.  Disassociate this conversation from a circuit 
 *  number.  Any subsequent close of the conversation will 
 *  not send hangup messages. 
1991/0119    
 * 
1991/0328    
 *	State Transition:	{Cconnected, Cconnecting, Chungup} -> Creset 
1991/0119    
 */ 
static void 
noreset(Noconv *cp) 
{ 
	Block *bp; 
	Queue *q; 
 
	switch(cp->state){ 
	case Cconnected: 
	case Cconnecting: 
1991/0328    
	case Chungup: 
1991/0119    
		cp->state = Creset; 
1991/0328    
		cp->rcvcircuit = -1; 
 		bp = allocb(0); 
1991/0119    
		bp->type = M_HANGUP; 
		q = cp->rq; 
		PUTNEXT(q, bp); 
		break; 
	} 
	wakeup(&cp->r); 
} 
 
/* 
1990/1210    
 *  process a message acknowledgement.  if the message 
 *  has any xmit buffers queued, free them. 
 */ 
static void 
1991/0119    
norack(Noconv *cp, int mid) 
1990/1210    
{ 
	Nomsg *mp; 
	Block *bp; 
	int i; 
 
	mp = &cp->out[mid & Nmask]; 
 
	/* 
	 *  if already acked, ignore 
	 */ 
	if(mp->acked || mp->mid != mid) 
		return; 
 
	/* 
1991/01151    
 	 *  free it 
1990/1210    
	 */ 
1991/01151    
	cp->rexmit = 0; 
1990/1210    
	cp->lastacked = mid; 
1991/0206    
	mp->acked = 1; 
1991/0904    
	if(mp->first) 
		freeb(mp->first); 
1991/01151    
	mp->first = 0; 
 
	/* 
	 *  advance first if this is the first 
	 */ 
	if((mid&Nmask) == cp->first){ 
		while(cp->first != cp->next){ 
			if(cp->out[cp->first].acked == 0) 
				break; 
1991/0206    
			cp->first = MSUCC(cp->first); 
1991/01151    
		} 
1991/0206    
		wakeup(&cp->r); 
1991/01151    
	} 
1990/1210    
} 
 
/* 
 *  queue an acknowledgement to be sent.  ignore it if we already have Nnomsg 
 *  acknowledgements queued. 
 */ 
static void 
1991/0119    
noqack(Noconv *cp, int mid) 
1990/1210    
{ 
1991/1101    
	cp->ack = mid; 
	cp->sendack = 1; 
1990/1210    
} 
 
/* 
 *  make a packet header 
 */ 
Block * 
1991/0119    
nohdr(Noconv *cp, int rem) 
1990/1210    
{ 
	Block *bp; 
	Nohdr *hp; 
 
	bp = allocb(cp->ifc->hsize + NO_HDRSIZE + cp->ifc->mintu); 
1991/0318    
	memmove(bp->wptr, cp->media->rptr, cp->ifc->hsize + NO_HDRSIZE); 
1990/1210    
	bp->wptr += cp->ifc->hsize + NO_HDRSIZE; 
	hp = (Nohdr *)(bp->rptr + cp->ifc->hsize); 
	hp->remain[1] = rem>>8; 
	hp->remain[0] = rem; 
	hp->sum[0] = hp->sum[1] = 0; 
1991/0318    
 
1990/1210    
	return bp; 
} 
 
/* 
 *  transmit a message.  this involves breaking a possibly multi-block message into 
 *  a train of packets on the media. 
 * 
1991/0119    
 *  called by nooput().  the qlock(mp) synchronizes these two 
1990/1210    
 *  processes. 
 */ 
static void 
1991/0119    
nosend(Noconv *cp, Nomsg *mp) 
1990/1210    
{ 
	Noifc *ifc; 
	Queue *wq; 
	int msgrem; 
	int pktrem; 
	int n; 
	Block *bp, *pkt, *last; 
	uchar *rptr; 
 
	ifc = cp->ifc; 
	if(ifc == 0) 
		return; 
	wq = ifc->wq->next; 
 
	/* 
	 *  get the next acknowledge to use if the next queue up 
	 *  is not full. 
	 */ 
1991/1101    
	cp->sendack = 0; 
	cp->hdr->ack = cp->ack; 
1990/1210    
	cp->hdr->mid = mp->mid; 
 
1991/0118    
	/* 
	 *  package n blocks into m packets.  make sure 
	 *  no packet is < mintu or > maxtu in length. 
	 */ 
1990/1210    
	if(ifc->mintu > mp->len) { 
		/* 
		 *  short message: 
		 *  copy the whole message into the header block 
		 */ 
1991/0119    
		last = pkt = nohdr(cp, mp->len); 
1990/1210    
		for(bp = mp->first; bp; bp = bp->next){ 
1991/0318    
			memmove(pkt->wptr, bp->rptr, n = BLEN(bp)); 
1990/1210    
			pkt->wptr += n; 
		} 
1991/0118    
		/* 
		 *  round up to mintu 
		 */ 
		memset(pkt->wptr, 0, n = ifc->mintu-mp->len); 
1990/1210    
		pkt->wptr += n; 
	} else { 
		/* 
		 *  long message: 
		 *  break up the message into noifc packets and send them. 
		 *  once around this loop for each non-header block generated. 
		 */ 
		msgrem = mp->len; 
		pktrem = msgrem > ifc->maxtu ? ifc->maxtu : msgrem; 
		bp = mp->first; 
1990/1214    
		SET(rptr); 
1990/1210    
		if(bp) 
			rptr = bp->rptr; 
1991/0119    
		last = pkt = nohdr(cp, msgrem); 
1991/0118    
		n = 0; 
1990/1210    
		while(bp){ 
			/* 
			 *  if pkt full, send and create new header block 
			 */ 
			if(pktrem == 0){ 
				nonetcksum(pkt, ifc->hsize); 
				last->flags |= S_DELIM; 
				(*wq->put)(wq, pkt); 
1991/0119    
				last = pkt = nohdr(cp, -msgrem); 
1990/1210    
				pktrem = msgrem > ifc->maxtu ? ifc->maxtu : msgrem; 
			} 
			n = bp->wptr - rptr; 
			if(n > pktrem) 
				n = pktrem; 
			last = last->next = allocb(0); 
			last->rptr = rptr; 
			last->wptr = rptr = rptr + n; 
			msgrem -= n; 
			pktrem -= n; 
			if(rptr >= bp->wptr){ 
				bp = bp->next; 
				if(bp) 
					rptr = bp->rptr; 
			} 
		} 
1991/0118    
		/* 
		 *  round up last packet to mintu 
		 */ 
		if(n < ifc->mintu){ 
			n = ifc->mintu - n; 
			last = last->next = allocb(n); 
			memset(last->wptr, 0, n); 
			last->wptr += n; 
		} 
1990/1210    
	} 
	nonetcksum(pkt, ifc->hsize); 
	last->flags |= S_DELIM; 
1991/0118    
	if(cp->rexmit > 10) 
		mp->time = NOW + 10*MSrexmit; 
	else 
		mp->time = NOW + (cp->rexmit+1)*MSrexmit; 
1991/0318    
 
1990/1210    
	(*wq->put)(wq, pkt); 
} 
 
/* 
 *  send a control message (hangup or acknowledgement). 
 */ 
static void 
1991/0119    
nosendctl(Noconv *cp, int flag, int new) 
1990/1210    
{ 
1991/0102    
	Nomsg ctl; 
 
	ctl.len = 0; 
	ctl.first = 0; 
	ctl.acked = 0; 
1990/1210    
	if(new) 
1991/0102    
		ctl.mid = Nnomsg^cp->out[cp->next].mid; 
1990/1210    
	else 
1991/0102    
		ctl.mid = cp->lastacked; 
1990/1210    
	cp->hdr->flag |= flag; 
1991/0119    
	nosend(cp, &ctl); 
1990/1210    
} 
 
/* 
 *  receive a message (called by the multiplexor; noetheriput, nofddiiput, ...) 
1991/0119    
 * 
 *	State Transition:	(no NO_NEWCALL in msg) Cconnecting -> Cconnected 
1990/1210    
 */ 
void 
nonetrcvmsg(Noconv *cp, Block *bp) 
{ 
	Block *nbp; 
	Nohdr *h; 
	short r; 
	int c; 
	Nomsg *mp; 
	int f; 
	Queue *q; 
 
	q = cp->rq; 
 
	/* 
	 *  grab the packet header, push the pointer past the nonet header 
	 */ 
	h = (Nohdr *)bp->rptr; 
	bp->rptr += NO_HDRSIZE; 
	mp = &cp->in[h->mid & Nmask]; 
	r = (h->remain[1]<<8) | h->remain[0]; 
	f = h->flag; 
 
	/* 
1991/0328    
	 *  Obey reset even if the message id is bogus 
1990/1210    
	 */ 
1991/0328    
	if(f & NO_RESET){ 
1991/0608    
		DPRINT("reset received\n"); 
1991/0119    
		noreset(cp); 
1991/0328    
		freeb(bp); 
1990/1210    
		return; 
	} 
 
	/* 
1991/0328    
	 *  A new call request (maybe), treat it as a reset if seen on a 
	 *  connected, hungup, or reset channel. 
	 * 
 	 *  On a connecting channel, treat as a reset if this is an 
	 *  invalid message ID. 
	 */ 
	if(f & NO_NEWCALL){ 
		switch(cp->state){ 
		case Cclosed: 
		case Copen: 
		case Cannounced: 
		case Creset: 
1991/0608    
			DPRINT("nonetrcvmsg %d %d\n", cp->rcvcircuit, cp - cp->ifc->conv); 
			freeb(bp); 
			return; 
1991/0328    
		case Chungup: 
		case Cconnected: 
1991/0608    
			DPRINT("Nonet call on connected/hanging-up circ %d conv %d\n", 
1991/0328    
				cp->rcvcircuit, cp - cp->ifc->conv);  
			freeb(bp); 
			noreset(cp); 
			return; 
		case Cconnecting: 
			if(h->mid != mp->mid){ 
1991/0608    
				DPRINT("Nonet call on connecting circ %d conv %d\n", 
1991/0328    
					cp->rcvcircuit, cp - cp->ifc->conv);  
				freeb(bp); 
				noreset(cp); 
				return; 
			} 
			break; 
		} 
	} 
 
	/* 
1990/1210    
	 *  ignore old messages and process the acknowledgement 
	 */ 
1991/0606    
	if(h->mid!=mp->mid || (f&NO_NULL)){ 
1991/0108    
		DPRINT("old msg %d instead of %d r==%d\n", h->mid, mp->mid, r); 
1990/1210    
		if(r == 0){ 
1991/0119    
			norack(cp, h->ack); 
1991/0328    
			if(f & NO_HANGUP) 
1991/0119    
				nohangup(cp); 
1990/1210    
		} else { 
1991/0102    
			if(r>0){ 
1991/0119    
				norack(cp, h->ack); 
				noqack(cp, h->mid); 
1991/0102    
			} 
1990/1210    
			cp->bad++; 
		} 
		freeb(bp); 
		return; 
	} 
 
	if(r>=0){ 
		/* 
		 *  start of message packet 
		 */ 
		if(mp->first){ 
1991/01151    
			DPRINT("mp->mid==%d mp->rem==%d r==%d\n", mp->mid, mp->rem, r); 
			freeb(mp->first); 
			mp->first = mp->last = 0; 
			mp->len = 0; 
1990/1210    
		} 
		mp->rem = r; 
	} else { 
		/* 
		 *  a continuation 
		 */ 
		if(-r != mp->rem) { 
1991/01151    
			DPRINT("mp->mid==%d mp->rem==%d r==%d\n", mp->mid, mp->rem, r); 
1990/1210    
			cp->bad++; 
			freeb(bp); 
			return; 
		} 
	} 
 
	/* 
	 *  take care of packets that were padded up 
	 */ 
	mp->rem -= BLEN(bp); 
	if(mp->rem < 0){ 
		if(-mp->rem <= BLEN(bp)){ 
			bp->wptr += mp->rem; 
			mp->rem = 0; 
		} else 
			panic("nonetrcvmsg: short packet"); 
	} 
	putb(mp, bp); 
 
	/* 
	 *  if the last chunk - pass it up the stream and wake any 
	 *  waiting process. 
	 * 
	 *  if not, strip off the delimiter. 
	 */ 
	if(mp->rem == 0){ 
1991/0119    
		cp->hdr->flag &= ~(NO_NEWCALL|NO_SERVICE); 
		norack(cp, h->ack); 
1990/1210    
		if(f & NO_ACKME) 
1991/0119    
			noqack(cp, h->mid); 
1990/1210    
		mp->last->flags |= S_DELIM; 
		PUTNEXT(q, mp->first); 
		mp->first = mp->last = 0; 
		mp->len = 0; 
		cp->rcvd++; 
 
		/* 
		 *  cycle bufffer to next expected mid 
		 */ 
		mp->mid ^= Nnomsg; 
 
		/* 
1991/0119    
		 *  any NO_NEWCALL after this is another call 
1990/1210    
		 */ 
1991/0119    
		if(cp->state==Cconnecting && !(f&NO_NEWCALL)) 
1990/1210    
			cp->state = Cconnected; 
1991/0119    
 
		/* 
		 *  hangup (after processing message) 
		 */ 
1991/0328    
		if(f & NO_HANGUP){ 
1991/0119    
			DPRINT("hangup with message\n"); 
			nohangup(cp); 
		} 
1990/1210    
	} else 
		mp->last->flags &= ~S_DELIM; 
 
} 
 
1991/0119    
 
1990/1210    
/* 
 *  noifc 
 */ 
/* 
 *  Create an ifc. 
 */ 
Noifc * 
nonetnewifc(Queue *q, Stream *s, int maxtu, int mintu, int hsize, 
	void (*connect)(Noconv *, char *)) 
{ 
	Noifc *ifc; 
	int i; 
 
	for(ifc = noifc; ifc < &noifc[conf.nnoifc]; ifc++){ 
		if(ifc->ref == 0){ 
			lock(ifc); 
			if(ifc->ref) { 
				/* someone was faster than us */ 
				unlock(ifc); 
				continue; 
			} 
			RD(q)->ptr = WR(q)->ptr = (void *)ifc; 
			for(i = 0; i < conf.nnoconv; i++) 
				ifc->conv[i].rcvcircuit = -1; 
			ifc->maxtu = maxtu - hsize - NO_HDRSIZE; 
			ifc->mintu = mintu - hsize - NO_HDRSIZE; 
			ifc->hsize = hsize; 
			ifc->connect = connect; 
			ifc->name[0] = 0; 
			ifc->wq = WR(q); 
			ifc->ref = 1; 
			unlock(ifc); 
			return ifc; 
		} 
	} 
	error(Enoifc); 
} 
 
/* 
 *  Free an noifc. 
 */ 
void 
nonetfreeifc(Noifc *ifc) 
{ 
	lock(ifc); 
	ifc->ref--; 
	if(ifc->ref == 0) 
		ifc->wq = 0; 
	unlock(ifc); 
} 
 
/* 
 *  calculate the checksum of a list of blocks.  ignore the first `offset' bytes. 
 */ 
int 
nonetcksum(Block *bp, int offset) 
{ 
	uchar *ep, *p; 
	int n; 
	ulong s; 
	Nohdr *hp; 
1991/0502    
	Block *first; 
1990/1210    
 
	s = 0; 
	p = bp->rptr + offset; 
	n = bp->wptr - p; 
	hp = (Nohdr *)p; 
	hp->sum[0] = hp->sum[1] = 0; 
	for(;;){ 
		ep = p+(n&~0x7); 
		while(p < ep) { 
			s = s + s + p[0]; 
			s = s + s + p[1]; 
			s = s + s + p[2]; 
			s = s + s + p[3]; 
			s = s + s + p[4]; 
			s = s + s + p[5]; 
			s = s + s + p[6]; 
			s = s + s + p[7]; 
			s = (s&0xffff) + (s>>16); 
			p += 8; 
		} 
		ep = p+(n&0x7); 
		while(p < ep) { 
			s = s + s + *p; 
			p++; 
		} 
		s = (s&0xffff) + (s>>16); 
		bp = bp->next; 
		if(bp == 0) 
			break; 
		p = bp->rptr; 
		n = BLEN(bp); 
	} 
	s = (s&0xffff) + (s>>16); 
	hp->sum[1] = s>>8; 
	hp->sum[0] = s; 
1991/0502    
	s &= 0xffff; 
1991/0503    
if(0) 
1991/0502    
	switch(s){ 
	case 0xac9f: 
	case 0xc1a4: 
	case 0xc41c: 
	case 0xc46d: 
		{ int i; 
		print("%lux s,", s); 
		for(bp = first; bp; bp = bp->next) 
			for(i = 0; i < BLEN(bp); i++) 
				print(" %ux", bp->rptr[i]); 
		} 
	} 
	return s; 
1990/1229    
} 
 
1991/0108    
/* 
 *  send acknowledges that need to be sent.  this happens at 1/2 
 *  the retransmission interval. 
 */ 
static void 
1991/0119    
nokproc(void *arg) 
1991/0108    
{ 
	Noifc *ifc; 
	Noconv *cp, *ep; 
1991/0118    
	Nomsg *mp; 
1991/0108    
 
	cp = 0; 
	ifc = 0; 
	if(waserror()){ 
		if(ifc) 
			unlock(ifc); 
		if(cp) 
			qunlock(cp); 
	} 
 
1991/0926    
	for(;;){ 
1991/01151    
		/* 
1991/0926    
		 *  loop through all active interfaces 
1991/01151    
		 */ 
1991/0926    
		for(ifc = noifc; ifc < &noifc[conf.nnoifc]; ifc++){ 
			if(ifc->wq==0 || !canlock(ifc)) 
1991/0108    
				continue; 
1991/0926    
	 
1991/01151    
			/* 
1991/0926    
			 *  loop through all active conversations 
1991/01151    
			 */ 
1991/0926    
			ep = ifc->conv + conf.nnoconv; 
			for(cp = ifc->conv; cp < ep; cp++){ 
				if(cp->state<=Copen || !canqlock(cp)) 
					continue; 
				if(cp->state <= Copen){ 
					qunlock(cp); 
					continue; 
				} 
	 
				/* 
				 *  resend the first message 
				 */ 
				if(cp->first!=cp->next && NOW>=cp->out[cp->first].time){ 
					mp = &(cp->out[cp->first]); 
					if(cp->rexmit++ > 15){ 
						norack(cp, mp->mid); 
						noreset(cp); 
					} else 
						nosend(cp, mp); 
				} 
	 
				/* 
				 *  get the acknowledges out 
				 */ 
1991/1101    
				if(cp->sendack){ 
					DPRINT("sending ack %d\n", cp->ack); 
1991/0926    
					nosendctl(cp, /*NO_NULL*/0, 0); 
				} 
				qunlock(cp); 
1991/01151    
			} 
1991/0926    
			unlock(ifc); 
1991/0108    
		} 
1991/0926    
		tsleep(&nonetkr, return0, 0, MSrexmit/2); 
1991/0108    
	} 
} 
 
1991/01151    
void 
nonettoggle(void) 
1990/1229    
{ 
	pnonet ^= 1; 
1990/1210    
} 
1991/0108    
 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)