Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/port/devdk.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname port/devdk.c 1990/0312
## diff -e /dev/null /n/bootesdump/1990/0312/sys/src/9/mips/devdk.c
0a
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"errno.h"

#define NOW (MACHP(0)->ticks)
#define DPRINT if(0)

enum {
	/*
	 *  configuration parameters
	 */
	Ndk = 2,		/* max dks */

	/*
	 *  relative or immutable
	 */
	Nline = 256,		/* max lines per dk */
	Ndir = Nline + 1,	/* entries in the dk directory */
	Nsubdir = 5,		/* entries in the sub directory */
};

typedef struct Dkmsg	Dkmsg;
typedef struct Line	Line;
typedef struct Dk	Dk;

/*
 *  types of possible dkcalls
 */
enum {
	Dial,
	Announce,
	Redial
};

/*
 *  format of messages to/from the datakit controller on the common
 *  signalling line
 */
struct Dkmsg {
	uchar	type;
	uchar	srv;
	uchar	param0l;
	uchar	param0h;
	uchar	param1l;
	uchar	param1h;
	uchar	param2l;
	uchar	param2h;
	uchar	param3l;
	uchar	param3h;
	uchar	param4l;
	uchar	param4h;
};

/*
 *  message codes (T_xxx == dialin.type, D_xxx == dialin.srv)
 */
#define	T_SRV	1		/* service request */
#define   D_SERV	1		/* (host to dkmux) announce a service */
#define   D_DIAL	2		/* (host to dkmux) connect to a service */
#define   D_XINIT	7		/* (dkmux to host) line has been spliced */
#define	T_REPLY	2		/* reply to T_SRV/D_SERV or T_SRV/D_DIAL */
#define	  D_OK		1		/* not used */
#define	  D_OPEN	2		/* (dkmux to host) connection established */
#define	  D_FAIL	3		/* (dkmux to host) connection failed */
#define	T_CHG	3		/* linege the status of a connection */
#define	  D_CLOSE	1		/* close the connection */
#define	  D_ISCLOSED	2		/* (dkmux to host) confirm a close */
#define	  D_CLOSEALL	3		/* (dkmux to host) close all connections */
#define	  D_REDIAL	6		/* (host to dkmux) redial a call */
#define	T_ALIVE	4		/* (host to dkmux) keep alive message */
#define	  D_CONTINUE	0		/* host has not died since last msg */
#define	  D_RESTART	1		/* host has restarted */
#define   D_MAXCHAN	2		/* request maximum line number */
#define	T_RESTART 8		/* (dkmux to host) datakit restarted */

/*
 *  macros for cracking/forming the window negotiation parameter
 */
#define MIN(x,y)  (x < y ? x : y)
#define W_WINDOW(o,d,t)  ((o<<8) | (d<<4) | t | 0100000)
#define W_VALID(x)  ((x) & 0100000)
#define W_ORIG(x)  (((x)>>8) & 017)
#define W_DEST(x)  (((x)>>4) & 017)
#define W_TRAF(x)  ((x) & 017)
#define W_DESTMAX(x,y)  (W_WINDOW(W_ORIG(x),MIN(W_DEST(x),y),W_TRAF(x)))
#define W_LIMIT(x,y)  (W_WINDOW(MIN(W_ORIG(x),y),MIN(W_DEST(x),y),W_TRAF(x)))
#define	W_VALUE(x)	(1<<((x)+4))
#define WS_2K	7

/*
 *  one per datakit line
 */
struct Line {
	QLock;
	Rendez	r;		/* wait here for dial */
	int	state;		/* dial state */
	int	err;		/* dialing error (if non zero) */
	int	window;		/* negotiated window */
	int	timestamp;	/* timestamp of last call received on this line */
	int	calltolive;	/* multiple of 15 seconds for dialing state to last */
	Queue	*rq;
	char	addr[64];
	char	raddr[64];
	char	ruser[32];
	char	other[64];
	Dk *dp;			/* interface contianing this line */
};

/*
 *  a dkmux dk.  one exists for every stream that a 
 *  dkmux line disilpline is pushed onto.
 */
struct Dk {
	QLock;
	int	ref;
	char	name[64];	/* dk name */	
	Queue	*wq;		/* dk output queue */
	int	lines;		/* number of lines */
	int	ncsc;		/* csc line number */
	Chan	*csc;		/* common signalling line */
	Line	line[Nline];
};
static Dk dk[Ndk];

/*
 *  conversation states (for Line.state)
 */
typedef enum {
	Lclosed=0,
	Lopened,		/* opened but no call out */
	Lconnected,		/* opened and a call set up on htis line */
	Lrclose,		/* remote end has closed down */
	Llclose,		/* local end has closed down */
	Ldialing,		/* dialing a new call */
	Llistening,		/* this line listening for calls */
	Lackwait,		/* incoming call waiting for ack/nak */
	Laccepting,		/* waiting for user to accept or reject the call */
} Lstate;

/*
 *  datakit error to errno 
 */
enum {
	DKok,
	DKbusy,
	DKnetotl,
	DKdestotl,
	DKbadnet,
	DKnetbusy,
	DKinuse,
	DKreject,
};
int dkerr[]={
	[DKok]Egreg,
	[DKbusy]Einuse,		/* destination busy */
	[DKnetotl]Enetotl,	/* network not answering */
	[DKdestotl]Edestotl,	/* destination not answering */ 
	[DKbadnet]Ebadnet,	/* unassigned destination */
	[DKnetbusy]Enetbusy,	/* network overload */
	[DKinuse]Einuse,	/* server already exists */
	[DKreject]Erejected	/* call rejected by destination */
};
#define DKERRS sizeof(dkerr)/sizeof(int)

/*
 *  imported
 */
extern Qinfo urpinfo;

/*
 *  predeclared
 */
Chan*		dkattach(char*);
static void	dkmuxconfig(Dk*, Block*);
static int	dkmesg(Dk*, int, int, int, int);
static void	dkcsckproc(void*);
static int	dklisten(Chan*);
static void	dkanswer(Chan*, int, int);
static void	dkwindow(Chan*);
static void	dkcall(int, Chan*, char*, char*, char*);
static void	dktimer(void*);
static void	dkchgmesg(Dk*, Dkmsg*, int);
static void	dkreplymesg(Dk*, Dkmsg*, int);
Chan*		dkopen(Chan*, int);

/*
 *  the datakit multiplexor stream module definition
 */
static void dkmuxopen(Queue *, Stream *);
static void dkmuxclose(Queue *);
static void dkmuxoput(Queue *, Block *);
static void dkmuxiput(Queue *, Block *);
Qinfo dkmuxinfo = { dkmuxiput, dkmuxoput, dkmuxopen, dkmuxclose, "dkmux" };

/*
 *  a new dkmux.  find a free dk structure and assign it to this queue.
 */
static void
dkmuxopen(Queue *q, Stream *s)
{
	Dk *dp;
	int i;

	for(dp = dk; dp < &dk[Ndk]; dp++){
		if(dp->wq == 0){
			qlock(dp);
			if(dp->wq) {
				/* someone was faster than us */
				qunlock(dp);
				continue;
			}
			q->ptr = q->other->ptr = (void *)dp;
			dp->csc = 0;
			dp->ncsc = 4;
			dp->lines = 16;
			dp->name[0] = 0;
			dp->wq = WR(q);
			qunlock(dp);
			return;
		}
	}
	error(0, Enoifc);
}

/*
 *  close down a dkmux
 */
static void
dkmuxclose(Queue *q)
{
	Dk *dp;

	dp = (Dk *)q->ptr;
	qlock(dp);
	if(dp->csc)
		close(dp->csc);
	dp->wq = 0;
	qunlock(dp);
}

/*
 *  handle configuration
 */
static void
dkmuxoput(Queue *q, Block *bp)
{
	Dk *dp;

	dp = (Dk *)q->ptr;
	if(bp->type != M_DATA){
		if(streamparse("config", bp))
			dkmuxconfig(dp, bp);
		else
			PUTNEXT(q, bp);
		return;
	}
	PUTNEXT(q, bp);
}

/*
 *  gather a message and send it up the appropriate stream
 *
 *  The first two bytes of each message contains the channel
 *  number, low order byte first.
 *
 *  Simplifying assumption:  one put == one message && the channel number
 *	is in the first block.  If this isn't true, demultiplexing will not
  *	work.
 */
static void
dkmuxiput(Queue *q, Block *bp)
{
	Dk *dp;
	Line *lp;
	int line;

	dp = (Dk *)q->ptr;
	if(bp->type != M_DATA){
		PUTNEXT(q, bp);
		return;
	}

	line = *bp->rptr++ | (*bp->rptr++<<8);
	if(line<0 || line>=dp->lines){
		print("dkmuxiput bad line %d\n", line);
		freeb(bp);
		return;
	}

	lp = &dp->line[line];
	if(canqlock(lp)){
		if(lp->rq)
			PUTNEXT(lp->rq, bp);
		else{
			print("dkmuxiput unopened line %d\n", line);
			freeb(bp);
		}
		qunlock(lp);
	} else {
		print("dkmuxiput unopened line %d\n", line);
		freeb(bp);
	}
}

/*
 *  the datakit line stream module definition
 */
static void dkstopen(Queue *, Stream *);
static void dkstclose(Queue *);
static void dkoput(Queue *, Block *);
static void dkiput(Queue *, Block *);
Qinfo dkinfo = { dkiput, dkoput, dkstopen, dkstclose, "dk" };

/*
 *  open and save a pointer to the conversation
 */
static void
dkstopen(Queue *q, Stream *s)
{
	Dk *dp;
	Line *lp;

	dp = &dk[s->dev];
	q->other->ptr = q->ptr = lp = &dp->line[s->id];
	lp->dp = dp;
	lp->rq = q;
}

/*
 *  close down a datakit conversation
 */
static void
dkstclose(Queue *q)
{
	Dk *dp;
	Line *lp;

	lp = (Line *)q->ptr;
	dp = lp->dp;

	/*
	 *  shake hands with dk
	 */
	switch(lp->state){
	case Lclosed:
	case Llclose:
		break;

	case Lrclose:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Lclosed;
		break;

	case Lackwait:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Llistening:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Lconnected:
		dkmesg(dp, T_CHG, D_CLOSE, lp - dp->line, 0);
		lp->state = Llclose;
		break;

	case Lopened:
		lp->state = Lclosed;
	}

	qlock(lp);
	lp->rq = 0;
	qunlock(lp);
}

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

/*
 *  we assume that each put is a message.
 *
 *  add a 2 byte channel number to the start of each message
 */
static void
dkoput(Queue *q, Block *bp)
{
	Line *lp;
	Dk *dp;
	int line;

	if(bp->type != M_DATA){
		freeb(bp);
		error(0, Ebadarg);
	}

	lp = (Line *)q->ptr;
	dp = lp->dp;
	line = lp - dp->line;

	if(bp->base && bp->rptr - bp->base >= 2)
		bp->rptr -= 2;
	bp->rptr[0] = line;
	bp->rptr[1] = line>>8;

	PUTNEXT(dp->wq, bp);
}

/*
 *  configure a datakit multiplexor.  this takes 3 arguments separated
 *  by spaces:
 *	the line number of the common signalling channel (must be > 0)
 *	the number of lines in the device (optional)
 *	the name of the dk (optional)
 *
 *  we can configure only once
 */
static void
dkmuxconfig(Dk *dp, Block *bp)
{
	Chan *c;
	char *fields[3];
	int n;
	char buf[64];
	static int dktimeron;

	if(dp->csc != 0){
		freeb(bp);
		error(0, Ebadarg);
	}

	/*
	 *  parse
	 */
	n = getfields((char *)bp->rptr, fields, 3, ' ');
	switch(n){
	case 3:
		strncpy(dp->name, fields[2], sizeof(dp->name));
	case 2:
		dp->lines = strtoul(fields[1], 0, 0);
	case 1:
		dp->ncsc = strtoul(fields[0], 0, 0);
		break;
	default:
		freeb(bp);
		error(0, Ebadarg);
	}
	freeb(bp);
	if(dp->ncsc <= 0 || dp->lines <= dp->ncsc){
		dp->lines = 16;
		error(0, Ebadarg);
	}

	/*
	 *  open a stream for the csc and push urp onto it
	 */
	c = 0;
	if(waserror()){
		if(c)
			close(c);
		nexterror();
	}
	c = dkattach(dp->name);
	c->qid = STREAMQID(dp->ncsc, Sdataqid);
	dkopen(c, ORDWR);
	dp->csc = c;

	/*
	 *  start a process to deal with it
	 */
	sprint(buf, "**csckproc%d**", dp->ncsc);
	kproc(buf, dkcsckproc, dp);
	poperror();

	/*
	 *  start a keepalive process if one doesn't exist
	 */
	if(dktimeron == 0){
		dktimeron = 1;
		kproc("**dktimer**", dktimer, 0);
	}
}

/*
 *  qid's
 */
enum {
	/*
	 *  per line
	 */
	Daddrqid,
	Dlistenqid,
	Draddrqid,
	Duserqid,
	Dotherqid,
	Dlineqid,

	/*
	 *  per device
	 */
	Dcloneqid,
};

/*
 *  the dk directory
 */
Dirtab dkdir[Ndir];

/*
 *  the per stream directory structure
 */
Dirtab dksubdir[]={
	"addr",		Daddrqid,	0,	0600,
	"listen",	Dlistenqid,	0,	0600,
	"other",	Dotherqid,	0,	0600,
	"raddr",	Draddrqid,	0, 	0600,
	"ruser",	Duserqid,	0, 	0600,
};

/*
 *  dk file system.  most of the calls use dev.c to access the dk
 *  directory and stream.c to access the dk devices.
 */
void
dkreset(void)
{
}

/*
 *  create the dk directory.  the files are `clone' and stream
 *  directories '1' to '32' (or whatever Nline is in decimal)
 */
void
dkinit(void)
{
	int i;

	/*
	 *  create the directory.
	 */
	/*
	 *  the circuits
	 */
	for(i = 1; i < Nline; i++) {
		sprint(dkdir[i].name, "%d", i);
		dkdir[i].qid = CHDIR|STREAMQID(i, Dlineqid);
		dkdir[i].length = 0;
		dkdir[i].perm = 0600;
	}

	/*
	 *  the clone device
	 */
	strcpy(dkdir[0].name, "clone");
	dkdir[0].qid = Dcloneqid;
	dkdir[0].length = 0;
	dkdir[0].perm = 0600;
}

Chan*
dkattach(char *spec)
{
	Chan *c;
	Dk *dp;

	/*
	 *  find a multiplexor with the same name
	 */
	for(dp = dk; dp < &dk[Ndk]; dp++){
		qlock(dp);
		if(dp->wq && strcmp(spec, dp->name)==0) {
			dp->ref++;
			qunlock(dp);
			break;
		}
		qunlock(dp);
	}
	if(dp == &dk[Ndk])
		error(0, Enoifc);
	c = devattach('k', spec);
	c->dev = dp - dk;

	return c;
}

Chan*
dkclone(Chan *c, Chan *nc)
{
	Dk *dp;

	dp = &dk[c->dev];
	qlock(dp);
	dp->ref++;
	qunlock(dp);
	return devclone(c, nc);
}

int	 
dkwalk(Chan *c, char *name)
{
	if(c->qid == CHDIR)
		return devwalk(c, name, dkdir, dk[c->dev].lines, devgen);
	else
		return devwalk(c, name, dksubdir, Nsubdir, streamgen);
}

void	 
dkstat(Chan *c, char *dp)
{
	if(c->qid == CHDIR)
		devstat(c, dp, dkdir, dk[c->dev].lines, devgen);
	else
		devstat(c, dp, dksubdir, Nsubdir, streamgen);
}

/*
 *  opening a dk device allocates a Line.  Opening the `clone'
 *  device is a ``macro'' for finding a free Line and opening
 *  it's ctl file.
 *
 *  opening the `listen' sub device is a macro for listening for
 *  a new call.  Lile `clone' the ctl file of the new channel is
 *  returned.
 */
Chan*
dkopen(Chan *c, int omode)
{
	extern Qinfo dkinfo;
	Stream *s;
	Line *lp, *end;
	Dk *dp;
	int line;

	if(c->qid == Dcloneqid){
		/*
		 *  get an unused device and open it's control file
		 */
		dp = &dk[c->dev];
		end = &dp->line[dp->lines];
		for(lp = &dp->line[dp->ncsc+1]; lp < end; lp++){
			if(lp->state == Lclosed && canqlock(lp)){
				if(lp->state != Lclosed){
					qunlock(lp);
					continue;
				}
				c->qid = STREAMQID(lp-dp->line, Sctlqid);
				qunlock(lp);
				break;
			}
		}
		if(lp == end)
			error(0, Enodev);
		streamopen(c, &dkinfo);
		pushq(c->stream, &urpinfo);
	} else if(STREAMTYPE(c->qid) == Dlistenqid){
		/*
		 *  listen for a call and open the control file for the
		 *  channel on which the call arrived.
		 */
		line = dklisten(c);
		c->qid = STREAMQID(line, Sctlqid);
		streamopen(c, &dkinfo);
		pushq(c->stream, &urpinfo);
		dkwindow(c);
	} else if(c->qid != CHDIR){
		/*
		 *  open whatever c points to, make sure it has an urp
		 */
		streamopen(c, &dkinfo);
		if(strcmp(c->stream->procq->next->info->name, "urp")!=0)
			pushq(c->stream, &urpinfo);
	}

	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

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

void	 
dkclose(Chan *c)
{
	Dk *dp;

	/* real closing happens in lancestclose */
	if(c->qid != CHDIR)
		streamclose(c);

	dp = &dk[c->dev];
	qlock(dp);
	dp->ref--;
	qunlock(dp);
}

long	 
dkread(Chan *c, void *a, long n)
{
	int t;
	Line *lp;

	t = STREAMTYPE(c->qid);
	if(t>=Slowqid || t==Dlineqid)
		return streamread(c, a, n);
	if(c->qid == CHDIR)
		return devdirread(c, a, n, dkdir, dk[c->dev].lines, devgen);

	lp = &dk[c->dev].line[STREAMID(c->qid)];
	switch(t){
	case Daddrqid:
		return stringread(c, a, n, lp->addr);
	case Draddrqid:
		return stringread(c, a, n, lp->raddr);
	case Duserqid:
		return stringread(c, a, n, lp->ruser);
	}
	error(0, Eperm);
}

long	 
dkwrite(Chan *c, void *a, long n)
{
	int t;
	char buf[256];
	char *field[5];
	int m;

	t = STREAMTYPE(c->qid);

	/*
	 *  get data dispatched as quickly as possible
	 */
	if(t == Sdataqid)
		return streamwrite(c, a, n, 0);

	/*
	 *  easier to do here than in dkoput
	 */
	if(t == Sctlqid){
		strncpy(buf, a, sizeof buf);
		m = getfields(buf, field, 5, ' ');
		if(strcmp(field[0], "connect")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkcall(Dial, c, field[1], 0, 0);
		} else if(strcmp(field[0], "announce")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkcall(Announce, c, field[1], 0, 0);
		} else if(strcmp(field[0], "redial")==0){
			if(m < 4)
				error(0, Ebadarg);
			dkcall(Redial, c, field[1], field[2], field[3]);
		} else if(strcmp(field[0], "accept")==0){
			if(m < 2)
				error(0, Ebadarg);
			dkanswer(c, strtoul(field[1], 0, 0), 0);
		} else if(strcmp(field[0], "reject")==0){
			if(m < 3)
				error(0, Ebadarg);
			dkanswer(c, strtoul(field[1], 0, 0), strtoul(field[2], 0, 0));
		} else
			return streamwrite(c, a, n, 0);
		return n;
	}

	if(t >= Slowqid)
		return streamwrite(c, a, n, 0);

	error(0, Eperm);
}

void	 
dkremove(Chan *c)
{
	error(0, Eperm);
}

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

void	 
dkerrstr(Error *e, char *buf)
{
	rooterrstr(e, buf);
}

void	 
dkuserstr(Error *e, char *buf)
{
	extern consuserstr(Error *, char *);

	consuserstr(e, buf);
}

/*
 *  send a message to the datakit on the common signaling line
 */
static int
dkmesg(Dk *dp, int type, int srv, int p0, int p1)
{
	Dkmsg d;
	Block *bp;

	if(dp->csc == 0)
		return -1;
	if(waserror())
		return -1;
	d.type = type;
	d.srv = srv;
	d.param0l = p0;
	d.param0h = p0>>8;
	d.param1l = p1;
	d.param1h = p1>>8;
	d.param2l = 0;
	d.param2h = 0;
	d.param3l = 0;
	d.param3h = 0;
	d.param4l = 0;
	d.param4h = 0;
	streamwrite(dp->csc, (char *)&d, sizeof(Dkmsg), 1);
	poperror();
	return 0;
}

/*
 *  call out on a datakit
 */
static int
calldone(void *a)
{
	Line *lp;

	lp = (Line *)a;
	return lp->state != Ldialing;
}
static void
dkcall(int type, Chan *c, char *addr, char *nuser, char *machine)
{
 	char dialstr[66];
	int line;
	char dialtone;
	int t_val, d_val;
	Dk *dp;
	Line *lp;
	Chan *dc;

	line = STREAMID(c->qid);
	dp = &dk[c->dev];
	lp = &dp->line[line];

	/*
	 *  only dial on virgin lines
	 */
	if(lp->state != Lclosed)
		error(0, Ebadarg);

	DPRINT("dkcall(line=%d, type=%d, dest=%s)\n", line, type, addr);

	/*
	 *  build dial string (guard against new lines)
	 */
	if(strchr(addr, '\n'))
		error(0, Ebadarg);
	if(strlen(addr)+strlen(u->p->pgrp->user)+2 >= sizeof(dialstr))
		error(0, Ebadarg);
	strcpy(dialstr, addr);
	switch(type){
	case Dial:
		t_val = T_SRV;
		d_val = D_DIAL;
		strcat(dialstr, "\n");
		strcat(dialstr, u->p->pgrp->user);
		strcat(dialstr, "\n");
		break;
	case Announce:
		t_val = T_SRV;
		d_val = D_SERV;
		break;
	case Redial:
		t_val = T_CHG;
		d_val = D_REDIAL;
		strcat(dialstr, "\n");
		strcat(dialstr, nuser);
		strcat(dialstr, "\n");
		strcat(dialstr, machine);
		strcat(dialstr, "\n");
		break;
	}

	/*
	 *  open the data file
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(line, Sdataqid);
	dkopen(dc, ORDWR);

	lp->calltolive = 4;
	lp->state = Ldialing;

	/*
	 *  tell the controller we want to make a call
	 */
	DPRINT("dialout\n");
	dkmesg(dp, t_val, d_val, line, W_WINDOW(WS_2K,WS_2K,2));

	/*
	 *  if redial, wait for a dial tone (otherwise we might send
	 *  the dialstr to the previous other end and not the controller)
	 */
	if(type==Redial){
		if(streamread(dc, &dialtone, 1L) != 1L){
			lp->state = Lconnected;
			error(0, Ebadarg);
		}
	}

	/*
	 *  make the call
	 */
	DPRINT("dialstr %s\n", dialstr);
	streamwrite(dc, dialstr, (long)strlen(dialstr), 1);
	close(dc);
	poperror();

	/*
	 *  redial's never get a reply, assume it worked
	 */
	if(type == Redial) {
		lp->state = Lconnected;
		return;
	}

	/*
	 *  wait for a reply
	 */
	DPRINT("reply wait\n");
	sleep(&lp->r, calldone, lp);

	/*
	 *  if there was an error, translate it to a plan 9
	 *  errno and report it to the user.
	 */
	DPRINT("got reply %d\n", lp->state);
	if(lp->state != Lconnected) {
		if(lp->err >= DKERRS)
			error(0, dkerr[0]);
		else
			error(0, dkerr[lp->err]);
	}

	/*
	 *  linege state if serving
	 */
	if(type == D_SERV){
		lp->state = Llistening;
	}
	DPRINT("connected!\n");

	/*
	 *  decode the window size
	 */
	if (W_VALID(lp->window)){
		/*
		 *  a 1127 window negotiation
		 */
		lp->window = W_VALUE(W_DEST(lp->window));
	} else if(lp->window>2 && lp->window<31){
		/*
		 *  a generic window negotiation
		 */
		lp->window = 1<<lp->window;
	} else
		lp->window = 0;

	/*
	 *  tag the connection
	 */
	strncpy(lp->addr, addr, sizeof(lp->addr)-1);
	strncpy(lp->raddr, addr, sizeof(lp->raddr)-1);

	/*
	 *  reset the protocol
	 */
	dkwindow(c);
}

/*
 *  listen for a call, reflavor the 
 */
static int
dklisten(Chan *c)
{
	char dialstr[512];
	char *line[12];
	char *field[8];
	Line *lp;
	Dk *dp;
	int n, lineno, ts, window;
	Chan *dc;

	dp = &dk[c->dev];

	/*
	 *  open the data file
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(STREAMID(c->qid), Sdataqid);
	dkopen(dc, ORDWR);

	/*
	 *  wait for a call in
	 */
	for(;;){
		/*
		 *  read the dialstring and null terminate it
		 */
		n = streamread(dc, dialstr, sizeof(dialstr)-1);
		DPRINT("returns %d\n", n);
		if(n <= 0)
			error(0, Eio);
		dialstr[n] = 0;
		DPRINT("dialstr = %s\n", dialstr);

		/*
		 *  break the dial string into lines
		 */
		n = getfields(dialstr, line, 12, '\n');
		if (n < 2) {
			DPRINT("bad dialstr from dk (1 line)\n");
			error(0, Eio);
		}

		/*
		 * line 0 is `line.tstamp.traffic[.urpparms.window]'
		 */
		window = 0;
		switch(getfields(line[0], field, 5, '.')){
		case 5:
			/*
			 *  generic way of passing window
			 */
			window = strtoul(field[4], 0, 0);
			if(window > 0 && window <31)
				window = 1<<window;
			else
				window = 0;
			/*
			 *  intentional fall through
			 */
		case 3:
			/*
			 *  1127 way of passing window
			 */
			if(window == 0){
				window = strtoul(field[2], 0, 0);
				if(W_VALID(window))
					window = W_VALUE(W_ORIG(window));
				else
					window = 0;
			}
			break;
		default:
			print("bad message from dk(bad first line)\n");
			continue;
		}
		lineno = strtoul(field[0], 0, 0);
		if(lineno >= dp->lines){
			print("dklisten: illegal line %d\n", lineno);
			continue;
		}
		lp = &dp->line[lineno];
		ts = strtoul(field[1], 0, 0);

		/*
		 *  this could be a duplicate request
		 */
		if(ts == lp->timestamp){
			print("dklisten: repeat timestamp %d\n", lineno);
			continue;
		}
	
		/*
		 *  take care of glare (datakit picked an inuse channel
		 *  for the call to come in on).
		 */
		if(!canqlock(lp)){
			print("DKbusy1\n");
			dkanswer(c, lineno, DKbusy);
			continue;
		} else {
			if(lp->state != Lclosed){
				qunlock(lp);
				print("DKbusy2 %ux\n", lp->state);
				dkanswer(c, lineno, DKbusy);
				continue;
			}
		}
		lp->window = window;

		/*
		 *  Line 1 is `my-dk-name.service[.more-things]'.
		 *  Special characters are escaped by '\'s.
		 */
		strncpy(lp->addr, line[1], sizeof(lp->addr)-1);
	
		/*
		 *  the rest is variable length
		 */
		switch(n) {
		case 2:
			/* no more lines */
			lp->ruser[0] = 0;
			lp->raddr[0] = 0;
			break;
		case 3:
			/* line 2 is `source.user.param1.param2' */
			getfields(line[2], field, 3, '.');
			strncpy(lp->raddr, field[0], sizeof(lp->raddr)-1);
			strncpy(lp->ruser, field[1], sizeof(lp->ruser)-1);
			break;
		case 4:
			/* line 2 is `user.param1.param2' */
			getfields(line[2], field, 2, '.');
			strncpy(lp->ruser, field[0], sizeof(lp->ruser)-1);
	
			/* line 3 is `source.node.mod.line' */
			strncpy(lp->raddr, line[3], sizeof(lp->raddr)-1);
			break;
		default:
			print("bad message from dk(>4 line)\n");
			qunlock(lp);
			error(0, Ebadarg);
		}

		sprint(lp->other, "w(%d)", W_TRAF(lp->window));
		DPRINT("src(%s)user(%s)dest(%s)other(%s)\n", lp->raddr, lp->ruser,
			lp->addr, lp->other);

		lp->timestamp = ts;
		lp->state = Lconnected;
		qunlock(lp);
		close(dc);
		poperror();
		DPRINT("dklisten returns %d\n", lineno);
		return lineno;
	}
}

/*
 *  answer a call
 */
static void
dkanswer(Chan *c, int line, int code)
{
	char reply[64];
	Dk *dp;
	Chan *dc;
	Line *lp;

	dp = &dk[c->dev];
	lp = &dp->line[line];

	/*
	 *  open the data file (c is a control file)
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid = STREAMQID(STREAMID(c->qid), Sdataqid);
	dkopen(dc, ORDWR);

	/*
	 *  send the reply
	 */
	sprint(reply, "%ud.%ud.%ud", line, lp->timestamp, code);
	DPRINT("dkanswer %s\n", reply);
	streamwrite(dc, reply, strlen(reply), 1);
	close(dc);
	poperror();
}

/*
 *  set the window size and reset the protocol
 */
static void
dkwindow(Chan *c)
{
	char buf[64];
	long wins;
	Line *lp;

	lp = &dk[c->dev].line[STREAMID(c->qid)];
	if(lp->window == 0)
		lp->window = 64;
	sprint(buf, "init %d %d", lp->window, Streamhi);
	streamwrite(c, buf, strlen(buf), 1);
}

/*
 *  hangup a datakit connection
 */
static void
dkhangup(Line *lp)
{
	Block *bp;

	qlock(lp);
	if(lp->rq){
		bp = allocb(0);
		bp->type = M_HANGUP;
		PUTNEXT(lp->rq, bp);
	}
	qunlock(lp);
}

/*
 *  A process which listens to all input on a csc line
 */
static void
dkcsckproc(void *a)
{
	long n;
	Dk *dp;
	Dkmsg d;
	int line;
	int i;

	dp = (Dk *)a;

	/*
	 *  loop forever listening
	 */
	for(;;){
		n = streamread(dp->csc, (char *)&d, (long)sizeof(d));
		if(n != sizeof(d)){
			print("strange csc message %d\n", n);
			continue;
		}
		line = (d.param0h<<8) + d.param0l;
/*		print("t(%d)s(%d)l(%d)\n", d.type, d.srv, line); /**/
		switch (d.type) {

		case T_CHG:	/* controller wants to close a line */
			dkchgmesg(dp, &d, line);
			break;
		
		case T_REPLY:	/* reply to a dial request */
			dkreplymesg(dp, &d, line);
			break;
		
		case T_SRV:	/* ignore it, it's useless */
			print("dksrvmesg(%d)\n", line);
			break;
		
		case T_RESTART:	/* datakit reboot */
			print("dk restart\n");
			if(line >=0 && line<dp->lines){
				print("maxlines=%d\n", line+1);
				dp->lines=line+1;
			}
			break;
		
		default:
			DPRINT("unrecognized csc message %o(%o)\n", d.type, line);
			break;
		}
	}
}

/*
 *  datakit requests or confirms closing a line
 */
static void
dkchgmesg(Dk *dp, Dkmsg *dialp, int line)
{
	Line *lp;

	if (line <= 0 || line >= dp->lines) {
		/* tell controller this line is not in use */
		dkmesg(dp, T_CHG, D_CLOSE, line, 0);
		return;
	}
	lp = &dp->line[line];
	switch (dialp->srv) {

	case D_CLOSE:		/* remote shutdown */
		switch (lp->state) {

		case Ldialing:
			/* simulate a failed connection */
			dkreplymesg(dp, (Dkmsg *)0, line);
			lp->state = Lrclose;
			break;

		case Lrclose:
		case Lconnected:
		case Llistening:
		case Lackwait:
			dkhangup(lp);
			lp->state = Lrclose;
			break;

		case Lopened:
			dkmesg(dp, T_CHG, D_CLOSE, line, 0);
			break;

		case Llclose:
		case Lclosed:
			dkhangup(lp);
			dkmesg(dp, T_CHG, D_CLOSE, line, 0);
			lp->state = Lclosed;
			break;
		}
		break;
	
	case D_ISCLOSED:	/* acknowledging a local shutdown */
		switch (lp->state) {
		case Llclose:
		case Lclosed:
			lp->state = Lclosed;
			break;

		case Lrclose:
		case Lconnected:
		case Llistening:
		case Lackwait:
			break;
		}
		break;

	default:
		print("unrecognized T_CHG\n");
	}
}

/*
 *  datakit replies to a dialout.  capture reply code and traffic parameters
 */
static void
dkreplymesg(Dk *dp, Dkmsg *dialp, int line)
{
	Proc *p;
	Line *lp;

	DPRINT("dkreplymesg(%d)\n", line);

	if(line < 0 || line >= dp->lines)
		return;

	lp=&dp->line[line];
	if(lp->state != Ldialing)
		return;

	if(dialp){
		/*
		 *  a reply from the dk
		 */
		lp->state = (dialp->srv==D_OPEN) ? Lconnected : Lrclose;
		lp->err = (dialp->param1h<<8) + dialp->param1l;
		lp->window = lp->err;
		DPRINT("dkreplymesg: %d\n", lp->state);
	} else {
		/*
		 *  a local abort
		 */
		lp->state = Lrclose;
		lp->err = 0;
	}

	if(lp->state==Lrclose){
		dkhangup(lp);
	}
	wakeup(&lp->r);
}

/*
 *  15-second timer for all interfaces
 */
static Rendez dkt;
static int
fuckit(void *a)
{
	return 0;
}
static void
dktimer(void *a)
{
	int dki, i;
	Dk *dp;
	Line *lp;

	waserror();

	for(;;){
		/*
		 *  loop through the active dks
		 */
		for(dki=0; dki<Ndk; dki++){
			dp = &dk[dki];
			if(dp->csc==0)
				continue;

			/*
			 * send keep alive
			 */
			dkmesg(dp, T_ALIVE, D_CONTINUE, 0, 0);

			/*
			 *  remind controller of dead lines and
			 *  timeout calls that take to long
			 */
			for (i=0; i<dp->lines; i++){
				lp = &dp->line[i];
				switch(lp->state){
				case Llclose:
					dkmesg(dp, T_CHG, D_CLOSE, i, 0);
					break;

				case Ldialing:
					if(lp->calltolive==0 || --lp->calltolive!=0)
						break;
					dkreplymesg(dp, (Dkmsg *)0, i);
					break;
				}
			}
		}
		tsleep(&dkt, fuckit, 0, 7500);
	}
}
.
## diffname port/devdk.c 1990/0315
## diff -e /n/bootesdump/1990/0312/sys/src/9/mips/devdk.c /n/bootesdump/1990/0315/sys/src/9/mips/devdk.c
1121c
				DPRINT("DKbusy2 %ux\n", lp->state);
.
1115c
			DPRINT("DKbusy1\n");
.
593d
413a
	else
		panic("dkoput");
.
304c
		DPRINT("dkmuxiput unopened line %d\n", line);
.
299c
			DPRINT("dkmuxiput unopened line %d\n", line);
.
289c
		DPRINT("dkmuxiput bad line %d\n", line);
.
287c
	line = bp->rptr[0] | (bp->rptr[1]<<8);
	bp->rptr += 2;
.
115c
 *  dkmux line discipline is pushed onto.
.
## diffname port/devdk.c 1990/0319
## diff -e /n/bootesdump/1990/0315/sys/src/9/mips/devdk.c /n/bootesdump/1990/0319/sys/src/9/mips/devdk.c
784,786d
727c
	switch(STREAMTYPE(c->qid)){
.
725a
	if(c->qid & CHDIR){
		if(c->qid == CHDIR)
			return devdirread(c, a, n, dkdir, dk[c->dev].lines, devgen);
		else
			return devdirread(c, a, n, dksubdir, Nsubdir, streamgen);
	}

.
723,724d
720,721c
	if(c->stream)
.
717d
705c
	if(c->stream)
.
684a
		break;
.
679a
		 *  read only files
		 */
		if(omode != OREAD)
			error(0, Ebadarg);
		break;
	default:
		/*
.
678c
		break;
	case Daddrqid:
	case Draddrqid:
	case Duserqid:
	case Dotherqid:
.
668c
		break;
	case Dlistenqid:
.
648a
		 *  directories are read only
		 */
		if(omode != OREAD)
			error(0, Ebadarg);
	} else switch(STREAMTYPE(c->qid)){
	case Dcloneqid:
		/*
.
647c
	if(c->qid & CHDIR){
.
## diffname port/devdk.c 1990/0321
## diff -e /n/bootesdump/1990/0319/sys/src/9/mips/devdk.c /n/bootesdump/1990/0321/sys/src/9/mips/devdk.c
540a
	newqinfo(&dkmuxinfo);
	newqinfo(&urpinfo);
.
## diffname port/devdk.c 1990/0403
## diff -e /n/bootesdump/1990/0321/sys/src/9/mips/devdk.c /n/bootesdump/1990/0403/sys/src/9/mips/devdk.c
1130a
			if(lp->state != Lconnected)
				dkanswer(c, lineno, DKbusy);
.
## diffname port/devdk.c 1990/0509
## diff -e /n/bootesdump/1990/0403/sys/src/9/mips/devdk.c /n/bootesdump/1990/0509/sys/src/9/mips/devdk.c
420c
	if(QFULL(dp->wq->next)){
		print("dk wq full\n");
		freeb(bp);
	} else
		PUTNEXT(dp->wq, bp);
.
## diffname port/devdk.c 1990/0511
## diff -e /n/bootesdump/1990/0509/sys/src/9/mips/devdk.c /n/bootesdump/1990/0511/sys/src/9/mips/devdk.c
1204a
	panic("dklisten terminates strangely\n");
.
902c
	if(lp->state != Lopened)
.
730c
	/* real closing happens in dkstclose */
.
680a
		qunlock(lp);
.
673d
331a
	if(lp->state == Lclosed)
		lp->state = Lopened;
.
## diffname port/devdk.c 1990/0617
## diff -e /n/bootesdump/1990/0511/sys/src/9/mips/devdk.c /n/bootesdump/1990/0617/sys/src/9/mips/devdk.c
1291a

	/*
	 *  tell datakit we've rebooted. It should close all channels.
	 */
	dkmesg(dp, T_ALIVE, D_RESTART, 0, 0);	/* do this on mips but not 68020 */
	dkmesg(dp, T_CHG, D_CLOSEALL, 0, 0);
.
1087a
print("bad dialstr %d '%s'\n", n, dialstr);
.
1078a
}
.
1077a
{print("bad n\n");
.
1006c
	 *  change state if serving
.
69c
#define	T_CHG	3		/* change the status of a connection */
.
## diffname port/devdk.c 1990/0707
## diff -e /n/bootesdump/1990/0617/sys/src/9/mips/devdk.c /n/bootesdump/1990/0707/sys/src/9/mips/devdk.c
1299c
	if(dp->restart)
		dkmesg(dp, T_ALIVE, D_RESTART, 0, 0);
.
916a
	bang = strchr(dialstr, '!');
	if(bang){
		dot = strchr(dialstr, '.');
		if(dot==0 || dot > bang)
			*bang = '.';
	}
.
910c
	 *  build dial string
	 *	- guard against new lines
	 *	- change ! into . to delimit service
.
895a
	char *bang, *dot;
.
458c
		if(strcmp(fields[2], "restart")!=0)
			dp->restart = 0;
.
456a
	case 4:
		strncpy(dp->name, fields[3], sizeof(dp->name));
.
455c
	dp->restart = 1;
	n = getfields((char *)bp->rptr, fields, 4, ' ');
.
442c
	char *fields[4];
.
433a
 *	the word `restart' or `norestart' (optional/default==restart)
.
430c
 *  configure a datakit multiplexor.  this takes 4 arguments separated
.
125a
	int	restart;
.
## diffname port/devdk.c 1990/0717
## diff -e /n/bootesdump/1990/0707/sys/src/9/mips/devdk.c /n/bootesdump/1990/0717/sys/src/9/mips/devdk.c
554c
	urpreset();
.
## diffname port/devdk.c 1990/0722
## diff -e /n/bootesdump/1990/0717/sys/src/9/mips/devdk.c /n/bootesdump/1990/0722/sys/src/9/mips/devdk.c
596a
	if(*spec == 0)
		spec = "dk";
.
595c
	 *  find a multiplexor with the same name (default dk)
.
506c
		kproc("dktimer", dktimer, 0);
.
497c
	sprint(buf, "csckproc%d", dp->ncsc);
.
458a
	strcpy(dp->name, "dk");
.
## diffname port/devdk.c 1990/0830
## diff -e /n/bootesdump/1990/0722/sys/src/9/mips/devdk.c /n/bootesdump/1990/0830/sys/src/9/mips/devdk.c
418,419c
	else {
		print("dkoput l %d b %ux r %ux w %ux\n", line, bp->base, bp->rptr,
			bp->wptr);
		freeb(bp);
		return;
	}
.
## diffname port/devdk.c 1990/0911
## diff -e /n/bootesdump/1990/0830/sys/src/9/mips/devdk.c /n/bootesdump/1990/0911/sys/src/9/mips/devdk.c
1487c
	while(waserror())
		print("dktimer: error\n");
.
1322a
	}
	DPRINT("dkcsckproc: closeall %s\n", dp->name);
.
1321c
	if(dp->restart) {
		DPRINT("dkcsckproc: restart %s\n", dp->name);
.
980c
	dkmesg(dp, t_val, d_val, line, W_WINDOW(dp->urpwindow,dp->urpwindow,2));
.
870a
	}
.
869c
	if(waserror()){
		print("dkmesg: error\n");
.
559d
502c
	sprint(buf, "csc.%s.%d", dp->name, dp->ncsc);
.
483a
	DPRINT("dkmuxconfig: ncsc=%d, lines=%d, restart=%d, name=\"%s\"\n",
		dp->ncsc, dp->lines, dp->restart, dp->name);
.
464a
	case 5:
		dp->urpwindow = strtoul(fields[4], 0, 0);
.
463a
	dp->urpwindow = WS_2K;
.
462c
	n = getfields((char *)bp->rptr, fields, 5, ' ');
.
448c
	char *fields[5];
.
440c
 *	the name of the dk (default==dk)
 *	the urp window size (default==WS_2K)
.
435c
 *  configure a datakit multiplexor.  this takes 5 arguments separated
.
416,423c
	bp = padb(bp, 2);
.
398c
 *  add a 2 byte channel number to the start of each message,
 *  low order byte first.
.
126a
	int	urpwindow;
.
11a
#define	NOW	(MACHP(0)->ticks)

.
9,10c
#define	DPRINT	if(0)	/*kprint*/
.
## diffname port/devdk.c 1990/0925
## diff -e /n/bootesdump/1990/0911/sys/src/9/mips/devdk.c /n/bootesdump/1990/0925/sys/src/9/mips/devdk.c
1115d
1105d
1103d
## diffname port/devdk.c 1990/1004
## diff -e /n/bootesdump/1990/0925/sys/src/9/mips/devdk.c /n/bootesdump/1990/1004/sys/src/9/mips/devdk.c
1317,1326d
501a
	 *  tell datakit we've rebooted. It should close all channels.
	 */
	if(dp->restart) {
		DPRINT("dkmuxconfig: restart %s\n", dp->name);
		dkmesg(dp, T_ALIVE, D_RESTART, 0, 0);
	}

	/*
.
## diffname port/devdk.c 1990/1011
## diff -e /n/bootesdump/1990/1004/sys/src/9/mips/devdk.c /n/bootesdump/1990/1011/sys/src/9/mips/devdk.c
424c
/*		print("dk wq full\n");/**/
.
## diffname port/devdk.c 1990/1018
## diff -e /n/bootesdump/1990/1011/sys/src/9/mips/devdk.c /n/bootesdump/1990/1018/sys/src/9/mips/devdk.c
423,427c
	FLOWCTL(dp->wq);
	PUTNEXT(dp->wq, bp);
.
121c
	char	name[64];	/* dk name */
.
## diffname port/devdk.c 1990/1022
## diff -e /n/bootesdump/1990/1018/sys/src/9/mips/devdk.c /n/bootesdump/1990/1022/sys/src/9/mips/devdk.c
1506c
			for (i=dp->ncsc+1; i<dp->lines; i++){
.
1423a
	case D_CLOSEALL:
		for(line = dp->ncsc+1; line < dp->lines; line++){
			lp = &dp->line[line];
			switch (lp->state) {
	
			case Ldialing:
				/* simulate a failed connection */
				dkreplymesg(dp, (Dkmsg *)0, line);
				lp->state = Lrclose;
				break;
	
			case Lrclose:
			case Lconnected:
			case Llistening:
			case Lackwait:
				dkhangup(lp);
				lp->state = Lrclose;
				break;
	
			case Lopened:
				break;
	
			case Llclose:
			case Lclosed:
				lp->state = Lclosed;
				break;
			}
		}
		break;

.
1409a
		if (line <= 0 || line >= dp->lines) {
			/* tell controller this line is not in use */
			dkmesg(dp, T_CHG, D_CLOSE, line, 0);
			return;
		}
		lp = &dp->line[line];
.
1379a
		if (line <= 0 || line >= dp->lines) {
			/* tell controller this line is not in use */
			dkmesg(dp, T_CHG, D_CLOSE, line, 0);
			return;
		}
		lp = &dp->line[line];
.
1371,1376d
224a
			for(lp = dp->line; lp < &dp->line[Nline]; lp++)
				if(lp->state != 0)
					panic("dkmuxopen l %d s %lux", lp-dp->line, lp->state);
.
208a
	Line *lp;
.
## diffname port/devdk.c 1990/1024
## diff -e /n/bootesdump/1990/1022/sys/src/9/mips/devdk.c /n/bootesdump/1990/1024/sys/src/9/mips/devdk.c
226,228d
209d
## diffname port/devdk.c 1990/1101
## diff -e /n/bootesdump/1990/1024/sys/src/9/mips/devdk.c /n/bootesdump/1990/1101/sys/src/9/mips/devdk.c
1557c
		tsleep(&dp->timer, return0, 0, 7500);
.
1548,1554c
				dkreplymesg(dp, (Dkmsg *)0, i);
				break;
.
1538,1546c
			case Ldialing:
				if(lp->calltolive==0 || --lp->calltolive!=0)
.
1533,1536c
		/*
		 *  remind controller of dead lines and
		 *  timeout calls that take to long
		 */
		for (i=dp->ncsc+1; i<dp->lines; i++){
			lp = &dp->line[i];
			switch(lp->state){
			case Llclose:
				dkmesg(c, T_CHG, D_CLOSE, i, 0);
				break;
.
1528,1531c
		dkmesg(c, T_ALIVE, D_CONTINUE, 0, 0);
.
1526c
		 * send keep alive
.
1524a
		if(dp->opened==0)
			error(0, Ehungup);

.
1523a
			case Ldialing:
				dkreplymesg(dp, (Dkmsg *)0, i);
				break;
			}
		}
		if(c)
			close(c);
		return;
	}

	/*
	 *  open csc
	 */
	dp = (Dk *)a;
	c = dkopenline(dp, dp->ncsc);

.
1521,1522c
	c = 0;
	if(waserror()){
		/*
		 *  hang up any calls waiting for the dk
		 */
		for (i=dp->ncsc+1; i<dp->lines; i++){
			lp = &dp->line[i];
			switch(lp->state){
			case Llclose:
				lp->state = Lclosed;
				break;
.
1519a
	Chan *c;
.
1508,1513d
1506c
 *  send a I'm alive message every 7.5 seconds and remind the dk of
 *  any closed channels it hasn't acknowledged.
.
1446a
				dkhangup(lp);
.
1445d
1430a
		/*
		 *  datakit wants us to close all lines
		 */
.
1412c
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
.
1403c
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
.
1397c
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
.
1376c
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
.
1367c
dkchgmesg(Chan *c, Dk *dp, Dkmsg *dialp, int line)
.
1353d
1349,1351c
			if(line >=0 && line<dp->lines)
.
1337c
			dkchgmesg(dp->csc, dp, &d, line);
.
1328a
			if(n == 0)
				error(0, Ehungup);
.
1322a
	if(waserror()){
		close(dp->csc);
		return;
	}

.
1321c
	dp = a;
.
1319d
1095,1096d
1090c
	dc = dkopenline(dp, STREAMID(c->qid));
.
988c
	csc = dkopenline(dp, dp->ncsc);
	dkmesg(csc, t_val, d_val, line, W_WINDOW(dp->urpwindow,dp->urpwindow,2));
	close(csc);
	csc = 0;
.
980a
	/*
	 *  open the data file
	 */
	dc = dkopenline(dp, line);
.
978,979d
975c
		if(csc)
			close(csc);
		if(dc)
			close(dc);
.
973c
	dc = 0;
	csc = 0;
.
971c
	 *  close temporary channels on error
.
918c
	
.
916a
	Chan *csc;
.
891c
	streamwrite(c, (char *)&d, sizeof(Dkmsg), 1);
.
873,874d
871d
868c
dkmesg(Chan *c, int type, int srv, int p0, int p1)
.
864a
 *  open the common signalling channel
 */
static Chan*
dkopenline(Dk *dp, int line)
{
	Chan *c;

	c = 0;
	if(waserror()){
		if(c)
			close(c);
		nexterror();
	}
	c = dkattach(dp->name);
	c->qid = STREAMQID(line, Sdataqid);
	dkopen(c, ORDWR);
	poperror();

	return c;
}

/*
.
754,758d
749,751d
701d
698a
		lp->state = Lopened;
		qunlock(lp);
.
685d
681a
		dp = &dk[c->dev];
.
629,634d
610,620c
	dp = dkalloc(spec);

	/*
	 *  return the new channel
	 */
.
516,519c
	sprint(buf, "timer.%s.%d", dp->name, dp->ncsc);
	kproc(buf, dktimer, dp);
.
514c
	 *  start a keepalive process
.
511d
507c
	 *  start a process to listen to csc messages
.
502,503c
		DPRINT("dktimer: restart %s\n", dp->name);
		dkmesg(dp->csc, T_ALIVE, D_RESTART, 0, 0);
.
499a
	 *  do this here to get it done before trying to open a channel.
.
498a
	 *  open csc here so that boot, dktimer, and dkcsckproc aren't
	 *  all fighting for it at once.
	 */
	dp->csc = dkopenline(dp, dp->ncsc);

	/*
.
493,496c
	dp->ncsc = ncsc;
	dp->lines = lines;
	dp->restart = restart;
	dp->urpwindow = window;
	dp->s = RD(q)->ptr;
	q->ptr = q->other->ptr = dp;
	dp->opened = 1;
	dp->wq = WR(q);
	unlock(dp);
.
487,491c
	dp = dkalloc(name);
	lock(dp);
	if(dp->opened){
		unlock(dp);
		error(0, Ebadarg);
.
485c
	 *  set up
.
480,482d
477,478c
	if(ncsc <= 0 || lines <= ncsc)
.
470c
		ncsc = strtoul(fields[0], 0, 0);
.
468c
		lines = strtoul(fields[1], 0, 0);
.
466c
			restart = 0;
.
463c
		strncpy(name, fields[3], sizeof(name));
.
461c
		window = strtoul(fields[4], 0, 0);
.
457,458d
455d
452a
	 *  defaults
	 */
	ncsc = 1;
	restart = 1;
	lines = 16;
	window = WS_2K;
	strcpy(name, "dk");

	/*
.
449c
		error(0, Egreg);
.
447c
	if(WR(q)->ptr){
.
445c
	char name[NAMELEN];
	int lines;
	int ncsc;
	int restart;
	int window;
.
441c
	Dk *dp;
.
439c
dkmuxconfig(Queue *q, Block *bp)
.
382a
out:
.
381a
	poperror();
	close(c);
.
378,380d
375c
		dkmesg(c, T_CHG, D_CLOSE, lp - dp->line, 0);
.
370c
		dkmesg(c, T_CHG, D_CLOSE, lp - dp->line, 0);
.
365c
		dkmesg(c, T_CHG, D_CLOSE, lp - dp->line, 0);
.
360c
		dkmesg(c, T_CHG, D_CLOSE, lp - dp->line, 0);
.
358a
	c = 0;
	if(waserror()){
		lp->state = Lclosed;
		if(c)
			close(c);
		goto out;
	}	
	c = dkopenline(dp, dp->ncsc);

	/*
	 *  shake hands with dk
	 */
	switch(lp->state){
.
357c
	case Lopened:
		lp->state = Lclosed;
		goto out;
	}
.
353a
	if(lp->rq == 0){
		lp->state = Lclosed; 
		return;
	}

	/*
	 *  decrement ref count on mux'd line
	 */
	streamexit(dp->s, 0);

	/*
	 *  these states don't need the datakit
	 */
.
352c
	 *  if we never got going, we're done
.
346a
	Chan *c;
.
336a
	lp->rq = q;
.
334,335c
	lock(dp);
	if(dp->opened==0 || streamenter(dp->s)<0){
		unlock(dp);
		error(0, Ehungup);
	}
	unlock(dp);
	if(lp->state==Lclosed)
.
283a
	/*
	 *  not configured yet
	 */
	if(q->other->ptr == 0){
		freeb(bp);
		return;
	}

.
275c
 *	work.
.
259c
			dkmuxconfig(q, bp);
.
254,256d
240,245c
	dp = WR(q)->ptr;
	if(dp == 0)
		return;

	/*
	 *  disallow new dkstopens() on this line.
	 *  the lock syncs with dkstopen().
	 */
	lock(dp);
	dp->opened = 0;
	unlock(dp);

	/*
	 *  hang up all datakit connections
	 */
	for(i=dp->ncsc; i < dp->lines; i++)
		dkhangup(&dp->line[i]);

	/*
	 *  wakeup the timer so it can die
	 */
	wakeup(&dp->timer);
.
238a
	int i;
.
232a
 *  a new dkmux.  find a free dk structure and assign it to this queue.
 *  when we get though here dp->s is meaningful and the name is set to "/".
 */
static void
dkmuxopen(Queue *q, Stream *s)
{
	RD(q)->ptr = s;
	WR(q)->ptr = 0;
}

/*
.
229c
	if(freep == 0){
		unlock(&dklock);
		error(0, Enoifc);
	}
	dp = freep;
	dp->opened = 0;
	dp->s = 0;
	dp->ncsc = 1;
	strncpy(dp->name, name, sizeof(freep->name));
	unlock(&dklock);
	return dp;
.
227a
		if(dp->name[0] == 0)
			freep = dp;
.
212,226c
		if(strcmp(name, dp->name) == 0){
			unlock(&dklock);
			return dp;
.
210a
	lock(&dklock);
	freep = 0;
.
209c
	Dk *freep;
.
203,206c
 *  Look for a dk struct with a name.  If none exists,  create one.
 */ 
static Dk *
dkalloc(char *name)
.
191a
static void	dkhangup(Line*);
.
189c
static void	dkchgmesg(Chan*, Dk*, Dkmsg*, int);
.
181,182c
static void	dkmuxconfig(Queue*, Block*);
static Chan*	dkopenline(Dk*, int);
static int	dkmesg(Chan*, int, int, int, int);
.
130a
static Lock dklock;
.
128a
	Rendez	timer;
.
125c
	Chan	*csc;
.
122a
	Stream	*s;
.
119,120c
	Lock;
	int	opened;
.
## diffname port/devdk.c 1990/1104
## diff -e /n/bootesdump/1990/1101/sys/src/9/mips/devdk.c /n/bootesdump/1990/1104/sys/src/9/mips/devdk.c
382d
379a
	lp->rq = q;
.
## diffname port/devdk.c 1990/1113
## diff -e /n/bootesdump/1990/1104/sys/src/9/mips/devdk.c /n/bootesdump/1990/1113/sys/src/9/mips/devdk.c
360c
Qinfo dkinfo =
{
	dkiput,
	dkoput,
	dkstopen,
	dkstclose,
	"dk"
};
.
205c
Qinfo dkmuxinfo =
{
	dkmuxiput,
	dkmuxoput,
	dkmuxopen,
	dkmuxclose,
	"dkmux"
};
.
## diffname port/devdk.c 1990/11161
## diff -e /n/bootesdump/1990/1113/sys/src/9/mips/devdk.c /n/bootesdump/1990/11161/sys/src/9/mips/devdk.c
1670a
		DPRINT("keep alive\n");
.
9c
#define	DPRINT	if(0) print
.
## diffname port/devdk.c 1990/11211
## diff -e /n/bootesdump/1990/11161/sys/src/9/mips/devdk.c /n/bootesdump/1990/11211/sys/src/9/mips/devdk.c
1666c
			error(Ehungup);
.
1447c
				error(Ehungup);
.
1398c
	lp = &dk[c->dev].line[STREAMID(c->qid.path)];
.
1375c
	dc->qid.path = STREAMQID(STREAMID(c->qid.path), Sdataqid);
.
1335c
			error(Ebadarg);
.
1231c
			error(Eio);
.
1221c
			error(Eio);
.
1205c
	dc = dkopenline(dp, STREAMID(c->qid.path));
.
1147c
			error(dkerr[lp->err]);
.
1145c
			error(dkerr[0]);
.
1112c
			error(Ebadarg);
.
1045c
		error(Ebadarg);
.
1043c
		error(Ebadarg);
.
1033c
		error(Ebadarg);
.
1025c
	line = STREAMID(c->qid.path);
.
965c
	c->qid.path = STREAMQID(line, Sdataqid);
.
936,949d
933c
	error(Eperm);
.
927c
	error(Eperm);
.
921c
	error(Eperm);
.
914c
				error(Ebadarg);
.
910c
				error(Ebadarg);
.
906c
				error(Ebadarg);
.
902c
				error(Ebadarg);
.
898c
				error(Ebadarg);
.
882c
	t = STREAMTYPE(c->qid.path);
.
871c
	error(Eperm);
.
862,863c
	lp = &dk[c->dev].line[STREAMID(c->qid.path)];
	switch(STREAMTYPE(c->qid.path)){
.
855,856c
	if(c->qid.path & CHDIR){
		if(c->qid.path == CHDIR)
.
837c
	error(Eperm);
.
816c
			error(Ebadarg);
.
803c
		c->qid.path = STREAMQID(line, Sctlqid);
.
791c
			error(Enodev);
.
786c
				c->qid.path = STREAMQID(lp-dp->line, Sctlqid);
.
772,773c
			error(Ebadarg);
	} else switch(STREAMTYPE(c->qid.path)){
.
767c
	if(c->qid.path & CHDIR){
.
743c
	if(c->qid.path == CHDIR)
.
734c
	if(c->qid.path == CHDIR)
.
699c
	dkdir[0].qid.path = Dcloneqid;
	dkdir[0].qid.vers = 0;
.
690c
		dkdir[i].qid.path = CHDIR|STREAMQID(i, Dlineqid);
		dkdir[i].qid.vers = 0;
.
656,660c
	"addr",		{Daddrqid},	0,	0600,
	"listen",	{Dlistenqid},	0,	0600,
	"other",	{Dotherqid},	0,	0600,
	"raddr",	{Draddrqid},	0, 	0600,
	"ruser",	{Duserqid},	0, 	0600,
.
587c
		error(Ebadarg);
.
578c
		error(Ebadarg);
.
574c
		error(Ebadarg);
.
543c
		error(Egreg);
.
502c
		error(Ebadarg);
.
391c
		error(Ehungup);
.
235c
		error(Enoifc);
.
## diffname port/devdk.c 1990/1126
## diff -e /n/bootesdump/1990/11211/sys/src/9/mips/devdk.c /n/bootesdump/1990/1126/sys/src/9/mips/devdk.c
779c
		 *  get an unused device and open its control file
.
746a
	else if(c->qid.path == Dcloneqid)
		devstat(c, dp, dkdir, 1, devgen);
.
## diffname port/devdk.c 1990/1202
## diff -e /n/bootesdump/1990/1126/sys/src/9/mips/devdk.c /n/bootesdump/1990/1202/sys/src/9/mips/devdk.c
1566a
		dp->allclosedp = 1;
		wakeup(&dp->rallclosed);
.
624a

	/*
	 *  wait for closeall in response to D_RESTART
	 */
	if(dp->restart)
		tsleep(&dp->rallclosed, allclosed, dp, 10000);
.
615,620d
605a
	 *  start a process to listen to csc messages
	 */
	sprint(buf, "csc.%s.%d", dp->name, dp->ncsc);
	kproc(buf, dkcsckproc, dp);

	/*
	 *  wait for first possible closeall (may not come)
	 */
	tsleep(&dp->rallclosed, allclosed, dp, 2000);

	/*
.
527a
static int
allclosed(void *arg)
{
	Dk *dp;

	dp = arg;
	return dp->allclosedp;
}
.
130a
	Rendez	rallclosed;
	int	allclosedp;	/* true once a closeall has been seen */
.
## diffname port/devdk.c 1990/1210
## diff -e /n/bootesdump/1990/1210/sys/src/9/mips/devdk.c /n/bootesdump/1990/1210/sys/src/9/port/devdk.c
1588,1589c
		dp->closeall = 1;
		wakeup(&dp->closeallr);
.
640,645d
632a
		tsleep(&dp->closeallr, haveca, dp, 5000); /* wait for restart closeall */
.
631a
		dp->closeall = 0;
.
622,626d
619a
	tsleep(&dp->closeallr, haveca, dp, 5000);	/* wait for initial closeall */
.
612a
	dp->closeall = 0;
.
536c
	return dp->closeall;
.
531c
haveca(void *arg)
.
131,132c
	int	closeall;	/* set when we receive a closeall message */
	Rendez	closeallr;	/* wait here for a closeall */
.
## diffname port/devdk.c 1990/1214
## diff -e /n/bootesdump/1990/1210/sys/src/9/port/devdk.c /n/bootesdump/1990/1214/sys/src/9/port/devdk.c
1667d
1641a
	dp = (Dk *)a;
.
1107d
1105a
	poperror();
.
1104a
	if(waserror()){
		close(csc);
		nexterror();
	}
.
1092,1096d
1086,1089c
		close(dc);
.
1083,1084c
	dc = dkopenline(dp, line);
.
1081c
	 *  open the data file
.
1077a
	default:
		t_val = 0;
		d_val = 0;
		panic("bad dial type");
.
## diffname port/devdk.c 1991/0411
## diff -e /n/bootesdump/1990/1214/sys/src/9/port/devdk.c /n/bootesdump/1991/0411/sys/src/9/port/devdk.c
893c
dkwrite(Chan *c, void *a, long n, ulong offset)
.
887c
		return stringread(c, a, n, lp->ruser, offset);
.
885c
		return stringread(c, a, n, lp->raddr, offset);
.
883c
		return stringread(c, a, n, lp->addr, offset);
.
866c
dkread(Chan *c, void *a, long n, ulong offset)
.
## diffname port/devdk.c 1991/0419
## diff -e /n/bootesdump/1991/0411/sys/src/9/port/devdk.c /n/bootesdump/1991/0419/sys/src/9/port/devdk.c
755a
Chan*
dkclwalk(Chan *c, char *name)
{
	return devclwalk(c, name);
}

.
## diffname port/devdk.c 1991/0427
## diff -e /n/bootesdump/1991/0419/sys/src/9/port/devdk.c /n/bootesdump/1991/0427/sys/src/9/port/devdk.c
756,761d
## diffname port/devdk.c 1991/0706
## diff -e /n/bootesdump/1991/0427/sys/src/9/port/devdk.c /n/bootesdump/1991/0706/sys/src/9/port/devdk.c
1476c
			print("unrecognized csc message %o.%o(%o)\n",
				d.type, d.srv, line);
.
770c
 *  its ctl file.
.
## diffname port/devdk.c 1991/0723
## diff -e /n/bootesdump/1991/0706/sys/src/9/port/devdk.c /n/bootesdump/1991/0723/sys/src/9/port/devdk.c
1467c
/*			print("dksrvmesg(%d)\n", line);		/**/
.
## diffname port/devdk.c 1991/0828
## diff -e /n/bootesdump/1991/0723/sys/src/9/port/devdk.c /n/bootesdump/1991/0828/sys/src/9/port/devdk.c
1472c
				dp->lines = line+1;
.
## diffname port/devdk.c 1991/1105
## diff -e /n/bootesdump/1991/0828/sys/src/9/port/devdk.c /n/bootesdump/1991/1105/sys/src/9/port/devdk.c
1062c
		strcat(dialstr, u->p->user);
.
1048c
	if(strlen(addr)+strlen(u->p->user)+2 >= sizeof(dialstr))
.
## diffname port/devdk.c 1991/1107
## diff -e /n/bootesdump/1991/1105/sys/src/9/port/devdk.c /n/bootesdump/1991/1107/sys/src/9/port/devdk.c
1685c
			lp = dp->linep[i];
.
1661a
		freeb(dp->alloc);
.
1649c
			lp = dp->linep[i];
.
1604c
	lp = dp->linep[line];
.
1555c
			lp = dp->linep[line];
.
1535c
		lp = dp->linep[line];
.
1499c
		lp = dp->linep[line];
.
1401c
	lp = dk[c->dev].linep[STREAMID(c->qid.path)];
.
1368c
	lp = dp->linep[line];
.
1341,1343c
		DPRINT("src(%s)user(%s)dest(%s)w(%d)\n", lp->raddr, lp->ruser,
			lp->addr, W_TRAF(lp->window));
.
1275c
		lp = dp->linep[lineno];
.
1031c
	lp = dp->linep[line];
.
880c
	lp = dk[c->dev].linep[STREAMID(c->qid.path)];
.
829d
810,811d
808c
		if(line == dp->lines)
.
804c
				c->qid.path = STREAMQID(line, Sctlqid);
				lp->state = Lopened;
				qunlock(lp);
.
797,798c
		for(line = dp->ncsc+1; line < dp->lines; line++){
			lp = dp->linep[line];
.
781c
	Line *lp;
.
731c
	dp = dkalloc(spec, 0, 0);
.
683a
	dk = (Dk*)ialloc(conf.dkif*sizeof(Dk), 0);
.
672d
652d
599,600d
593c
	dp = dkalloc(name, ncsc, lines);
.
509c
	line = lp->lineno;
.
467c
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0);
.
462c
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0);
.
457c
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0);
.
452c
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0);
.
388c
	q->other->ptr = q->ptr = lp = dp->linep[s->id];
.
347c
	lp = dp->linep[line];
.
284c
		dkhangup(dp->linep[i]);
.
243a

	/*
	 *  allocate memory for line structures
	 */
	n = sizeof(Line*)*dp->lines;
	bp = allocb(n);
	if(bp->lim - bp->base < n){
		unlock(&dklock);
		errors("too many lines");
	}
	dp->linep = (Line **)bp->base;
	bp->wptr += n;
	dp->alloc = bp;
	for(i = 0; i < n; i++){
		if(bp->lim - bp->wptr < sizeof(Line)){
			bp = allocb(sizeof(Line)*n);
			bp->next = dp->alloc;
			dp->alloc = bp;
		}
		dp->linep[i] = (Line*)bp->wptr;
		dp->linep[i]->lineno = i;
		bp->wptr += sizeof(Line);
	}
		
.
242c
	dp->ncsc = ncsc;
	dp->lines = lines;
.
238a
	if(lines == 0)
		errors("unknown dk interface");

	/*
 	 *  init the structures
	 */
.
227c
	for(dp = dk; dp < &dk[conf.dkif]; dp++){
.
223a
	Block *bp;
	int i, n;
.
220c
dkalloc(char *name, int ncsc, int lines)
.
134c
static Dk *dk;
.
132a

	Block	*alloc;
.
127c
	Line	**linep;
.
110d
99a
	int	lineno;
.
15,19d
## diffname port/devdk.c 1991/1108
## diff -e /n/bootesdump/1991/1107/sys/src/9/port/devdk.c /n/bootesdump/1991/1108/sys/src/9/port/devdk.c
1480c
		DPRINT("t(%d)s(%d)l(%d)\n", d.type, d.srv, line);
.
1466a
	DPRINT("dkcsckproc: %d\n", dp->ncsc);
.
1002a
 *  return the contents of the info files
 */
void
dkfilladdr(Chan *c, char *buf, int len)
{
	strncpy(buf, dk[c->dev].linep[STREAMID(c->qid.path)]->addr, len);
}
void
dkfillraddr(Chan *c, char *buf, int len)
{
	strncpy(buf, dk[c->dev].linep[STREAMID(c->qid.path)]->raddr, len);
}
void
dkfillruser(Chan *c, char *buf, int len)
{
	strncpy(buf, dk[c->dev].linep[STREAMID(c->qid.path)]->ruser, len);
}

/*
.
980a
 *  return the number of an unused line (reserve it)
 */
static int
dkcloneline(Chan *c)
{
	Line *lp;
	Dk *dp;
	int line;

	dp = &dk[c->dev];
	/*
	 *  get an unused device and open its control file
	 */
	for(line = dp->ncsc+1; line < dp->lines; line++){
		lp = dp->linep[line];
		if(lp->state == Lclosed && canqlock(lp)){
			if(lp->state != Lclosed){
				qunlock(lp);
				continue;
			}
			lp->state = Lopened;
			qunlock(lp);
			return lp->lineno;
		}
	}
	error(Enodev);
}

/*
.
894,915c
	return netread(c, a, n, offset,  &dk[c->dev].net);
.
806,875c
	return netopen(c, omode, &dk[c->dev].net);
.
794,802d
786,791c
	netstat(c, dp, &dk[c->dev].net);
.
777,780c
	return netwalk(c, name, &dk[c->dev].net);
.
721,744d
714,717d
669,706d
273c

	/*
	 *  fill in the network structure
	 */
	dp->net.name = dp->name;
	dp->net.nconv = dp->lines+1;
	dp->net.devp = &dkinfo;
	dp->net.protop = &urpinfo;
	dp->net.listen = dklisten;
	dp->net.clone = dkcloneline;
	dp->net.ninfo = 3;
	dp->net.info[0].name = "addr";
	dp->net.info[0].fill = dkfilladdr;
	dp->net.info[1].name = "raddr";
	dp->net.info[1].fill = dkfillraddr;
	dp->net.info[2].name = "ruser";
	dp->net.info[2].fill = dkfillruser;

.
271a
		n -= sizeof(Line);
.
265a
			memset(bp->base, 0, bp->lim-bp->base);
.
263c
	n = sizeof(Line)*(dp->lines);
	for(i = 1; i <= dp->lines; i++){
.
259a
	memset(bp->base, 0, bp->lim-bp->base);
.
254c
	n = sizeof(Line*)*(dp->lines+1);
.
239a
	}
.
238c
	if(lines == 0){
		unlock(&dklock);
.
222a
	Line *lp;
.
197a
 *  for standard network interface (net.c)
 */
static int	dkcloneline(Chan*);
static int	dklisten(Chan*);
static void	dkfilladdr(Chan*, char*, int);
static void	dkfillraddr(Chan*, char*, int);
static void	dkfillruser(Chan*, char*, int);

extern Qinfo dkinfo;

/*
.
187d
127a
	Network	net;
.
13,21d
## diffname port/devdk.c 1991/1114
## diff -e /n/bootesdump/1991/1108/sys/src/9/port/devdk.c /n/bootesdump/1991/1114/sys/src/9/port/devdk.c
270c
	for(i = 0; i <= dp->lines; i++){
.
## diffname port/devdk.c 1991/1115
## diff -e /n/bootesdump/1991/1114/sys/src/9/port/devdk.c /n/bootesdump/1991/1115/sys/src/9/port/devdk.c
1272a

		/* listener becomes owner */
		netown(&dp->net, lp->lineno, dp->prot[from].owner, 0);

.
1130a
	from = STREAMID(c->qid.path);
.
1127a
	int from;
.
854a

			/* current user becomes owner */
			netown(&dp->net, lp->lineno, u->p->user, 0);

.
830c
	netwstat(c, dp, &dk[c->dev].net);
.
698a
	for(i = 0; i < conf.dkif; i++)
		dk[i].prot = (Netprot*)ialloc(conf.nurp*sizeof(Netprot), 0);
.
697a
	int i;
.
532a
	netdisown(&dp->net, lp->lineno);
.
291a
	dp->net.prot = dp->prot;
.
287c
	dp->net.nconv = dp->lines;
.
270c
	for(i = 0; i < dp->lines; i++){
.
259c
	n = sizeof(Line*)*(dp->lines);
.
119a
	Netprot *prot;
.
16a
enum {
	Maxlines = 256,
};

.
## diffname port/devdk.c 1991/1116
## diff -e /n/bootesdump/1991/1115/sys/src/9/port/devdk.c /n/bootesdump/1991/1116/sys/src/9/port/devdk.c
1613,1616c
	c = dkopencsc(dp);
.
1040c
	csc = dkopencsc(dp);
.
898a
 *  open the common signalling channel
 */
static Chan*
dkopencsc(Dk *dp)
{
	qlock(dp);
	if(dp->csc == 0)
		dp->csc = dkopenline(dp, dp->ncsc);
	else
		incref(dp->csc);
	qunlock(dp);
	return dp->csc;
}

/*
.
876,878d
675c
	dkopencsc(dp);
.
540a

	if(lp->lineno == dp->ncsc)
		dp->csc = 0;
	netdisown(&dp->net, lp->lineno);
.
539d
507c
	c = dkopencsc(dp);
.
181a
static Chan*	dkopencsc(Dk*);
.
126c
	Block	*alloc;		/* blocks containing Line structs */
.
116c
	int	lines;		/* number of lines */
.
114d
110a

.
108a
	QLock;
	Chan	*csc;

.
## diffname port/devdk.c 1991/1120
## diff -e /n/bootesdump/1991/1116/sys/src/9/port/devdk.c /n/bootesdump/1991/1120/sys/src/9/port/devdk.c
1628d
547a

	lock(dp);
	dp->ref--;
	if(dp->ref == 0)
		freeb(dp->alloc);
	unlock(dp);
.
456a
	dp->ref++;
.
336a
	dp->name[0] = 0;
.
243c
		if(dp->name[0] == 0 && dp->ref == 0)
.
112a
	int	ref;
.
## diffname port/devdk.c 1991/1122
## diff -e /n/bootesdump/1991/1120/sys/src/9/port/devdk.c /n/bootesdump/1991/1122/sys/src/9/port/devdk.c
309a
	dp->net.info[3].name = "stats";
	dp->net.info[3].fill = urpfillstats;
.
303c
	dp->net.ninfo = 4;
.
## diffname port/devdk.c 1991/1206
## diff -e /n/bootesdump/1991/1122/sys/src/9/port/devdk.c /n/bootesdump/1991/1206/sys/src/9/port/devdk.c
1121c
			errors(dkerr[lp->err]);
.
1119c
			errors(dkerr[0]);
.
840c
			for(m = 0; m < DKERRS-1; m++)
				if(strcmp(field[2], dkerr[m]) == 0)
					break;
			dkanswer(c, strtoul(field[1], 0, 0), m);
.
173c
#define DKERRS sizeof(dkerr)/sizeof(char*)
.
163,171c
char* dkerr[]={
	[DKok]"",
	[DKbusy]"host overloaded",
	[DKnetotl]"network not answering",
	[DKdestotl]"host not answering",
	[DKbadnet]"unknown address",
	[DKnetbusy]"network overloaded",
	[DKinuse]"server in use",
	[DKreject]"connection refused", 
.
151c
 *  map datakit error to errno 
.
85,87d
## diffname port/devdk.c 1991/1214
## diff -e /n/bootesdump/1991/1206/sys/src/9/port/devdk.c /n/bootesdump/1991/1214/sys/src/9/port/devdk.c
851a
	USED(c);
.
779a
	USED(c);
.
## diffname port/devdk.c 1991/1228
## diff -e /n/bootesdump/1991/1214/sys/src/9/port/devdk.c /n/bootesdump/1991/1228/sys/src/9/port/devdk.c
166,167c
	[DKnetbusy]"network busy",
	[DKinuse]"service in use",
.
164c
	[DKdestotl]"destination not answering",
.
162c
	[DKbusy]"destination busy",
.
## diffname port/devdk.c 1992/0111
## diff -e /n/bootesdump/1991/1228/sys/src/9/port/devdk.c /n/bootesdump/1992/0111/sys/src/9/port/devdk.c
1123c
			error(dkerr[lp->err]);
.
1121c
			error(dkerr[0]);
.
270c
		error(Ebadarg);
.
248,251d
244c
	if(freep == 0 || lines == 0){
.
7c
#include	"../port/error.h"
.
## diffname port/devdk.c 1992/0130
## diff -e /n/bootesdump/1992/0111/sys/src/9/port/devdk.c /n/bootesdump/1992/0130/sys/src/9/port/devdk.c
813c
		if(n > sizeof buf - 1)
			n = sizeof buf - 1;
		strncpy(buf, a, n);
		buf[n] = '\0';
.
## diffname port/devdk.c 1992/0219
## diff -e /n/bootesdump/1992/0130/sys/src/9/port/devdk.c /n/bootesdump/1992/0219/sys/src/9/port/devdk.c
318a

	s->opens++;		/* Hold this queue in place */
	s->inuse++;
.
## diffname port/devdk.c 1992/0221
## diff -e /n/bootesdump/1992/0219/sys/src/9/port/devdk.c /n/bootesdump/1992/0221/sys/src/9/port/devdk.c
1259c
			if((dts++ % 1000) == 0)
				print("dklisten: repeat timestamp %d\n", lineno);
.
1177a
	static int dts;
.
## diffname port/devdk.c 1992/0223
## diff -e /n/bootesdump/1992/0221/sys/src/9/port/devdk.c /n/bootesdump/1992/0223/sys/src/9/port/devdk.c
325c
 *  close down a dkmux, this shouldn't happen
.
311,312c
 *  a new dkmux.  hold the stream in place so it can never be closed down.
.
## diffname port/devdk.c 1992/0305
## diff -e /n/bootesdump/1992/0223/sys/src/9/port/devdk.c /n/bootesdump/1992/0305/sys/src/9/port/devdk.c
591,592c
	FLOWCTL(dp->wq, bp);
.
109d
106a
	Lock;
.
## diffname port/devdk.c 1992/0321
## diff -e /n/bootesdump/1992/0305/sys/src/9/port/devdk.c /n/bootesdump/1992/0321/sys/src/9/port/devdk.c
2c
#include	"../port/lib.h"
.
## diffname port/devdk.c 1992/0409
## diff -e /n/bootesdump/1992/0321/sys/src/9/port/devdk.c /n/bootesdump/1992/0409/sys/src/9/port/devdk.c
1368a

	/*
 	 *  set window size
	 */
	if(code == 0){
		if(waserror()){
			close(dc);
			nexterror();
		}
		sprint(reply, "init %d %d", lp->window, Streamhi);
		dc = dkopenline(dp, line);
		dc->qid.path = STREAMQID(line, Sctlqid);
		streamwrite(dc, reply, strlen(reply), 1);
		close(dc);
		poperror();
	}
.
1154a
	sprint(lp->other, "w=%d", lp->window);
.
947a
void
dkfillother(Chan *c, char *buf, int len)
{
	strncpy(buf, dk[c->dev].linep[STREAMID(c->qid.path)]->other, len);
}
.
547a
	lp->window = 0;
.
304a
	dp->net.info[4].name = "other";
	dp->net.info[4].fill = dkfillother;
.
296c
	dp->net.ninfo = 5;
.
202a
static void	dkfillother(Chan*, char*, int);
.
97a
	char	other[32];
.
## diffname port/devdk.c 1992/0520
## diff -e /n/bootesdump/1992/0409/sys/src/9/port/devdk.c /n/bootesdump/1992/0520/sys/src/9/port/devdk.c
1344a
	return -1;		/* never reached */
.
898a
	return -1;		/* never reached */
.
852a
	return -1;		/* never reached */
.
## diffname port/devdk.c 1992/0601
## diff -e /n/bootesdump/1992/0520/sys/src/9/port/devdk.c /n/bootesdump/1992/0601/sys/src/9/port/devdk.c
163,169c
	[DKbusy]"devdk: destination busy",
	[DKnetotl]"devdk: network not answering",
	[DKdestotl]"devdk: destination not answering",
	[DKbadnet]"devdk: unknown address",
	[DKnetbusy]"devdk: network busy",
	[DKinuse]"devdk: service in use",
	[DKreject]"devdk: connection refused", 
.
## diffname port/devdk.c 1992/0622
## diff -e /n/bootesdump/1992/0601/sys/src/9/port/devdk.c /n/bootesdump/1992/0622/sys/src/9/port/devdk.c
725c
		dk[i].prot = (Netprot*)xalloc(conf.nurp*sizeof(Netprot));
.
723c
	dk = (Dk*)xalloc(conf.dkif*sizeof(Dk));
.
270,286d
264,268c
	dp->linep = (Line **)xalloc(sizeof(Line*) * dp->lines);
	if(dp->linep == 0)
		error(Enomem);
	lp = xalloc(dp->lines*sizeof(Line));
	if(lp == 0)
		error(Enomem);
	for(i = 0; i < dp->lines; i++) {
		lp->lineno = i;
		dp->linep[i] = lp++;
.
232d
162,169c
	[DKok]		"",
	[DKbusy]	"devdk: destination busy",
	[DKnetotl]	"devdk: network not answering",
	[DKdestotl]	"devdk: destination not answering",
	[DKbadnet]	"devdk: unknown address",
	[DKnetbusy]	"devdk: network busy",
	[DKinuse]	"devdk: service in use",
	[DKreject]	"devdk: connection refused", 
.
## diffname port/devdk.c 1992/0623
## diff -e /n/bootesdump/1992/0622/sys/src/9/port/devdk.c /n/bootesdump/1992/0623/sys/src/9/port/devdk.c
1679a
			if(lp == 0)
				continue;
.
1667a
	for(;;){
.
1664,1666c
	while(waserror());
.
1640,1661d
1601d
1598c
	if(line < 0 || line >= dp->lines || (lp = dp->linep[line]) == 0)
.
1552a
			if(lp == 0)
				continue;
.
1532d
1527c
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0) {
.
1496d
1491c
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0) {
.
1397c
	lp = linealloc(&dk[c->dev], STREAMID(c->qid.path), 1);
.
1348c
	lp = linealloc(dp, line, 1);
.
1345a
	Dk *dp;
.
1343d
1324c
		netown(lp, dp->linep[from]->owner, 0);
.
1250c
		lp = linealloc(dp, lineno, 1);
.
1002c
	lp = linealloc(dp, line, 1);
.
919c
	qunlock(&dp->csclock);
.
914c
	Line *lp;

	qlock(&dp->csclock);
.
884a
	qunlock(&dp->netlock);
.
881c
			qunlock(&dp->netlock);
.
879c
			netown(lp, u->p->user, 0);
.
871,875c
		if(lp == 0 || lp->state == Lclosed){
			lp = linealloc(dp, line, 0);
.
868a
	qlock(&dp->netlock);
.
762c
	Dk *dp;

	dp = &dk[c->dev];
	linealloc(dp, STREAMID(c->qid.path), 1);
	return netopen(c, omode, &dp->net);
.
709,711c

	dk = (Dk*)xalloc(Maxdk*sizeof(Dk));
.
560c
 *  low order byte first.  Make sure the first block contains
 *  both the 2 channel bytes and the control byte.
.
539,544d
537c
	netdisown(lp);
.
479,483d
445,451d
402,403c
	lp = linealloc(dp, line, 1);
	if(lp && canqlock(lp)){
.
314,347d
300a
 *  allocate a line if it doesn't exist
 */
static Line*
linealloc(Dk *dp, int lineno, int dolock)
{
	Line *lp;

	if(dp->opened == 0)
		error(Enoifc);
	if(dolock)
		qlock(&dp->netlock);
	if(lineno > dp->lines)
		panic("linealloc");
	lp = dp->linep[lineno];
	if(lp == 0){
		lp = smalloc(sizeof(Line));
		lp->lineno = lineno;
		netadd(&dp->net, lp, lineno);
		dp->linep[lineno] = lp;
	}
	if(dolock)
		qunlock(&dp->netlock);
	return lp;
}

/*
.
283d
263,272c
	dp->linep = smalloc(sizeof(Line*) * dp->lines);
.
261c
	 *  allocate memory for array of pointers to lines.
	 *  line structures are allocated as needed.
.
250,252d
242c
		if(dp->name[0] == 0)
.
237c
	for(dp = dk; dp < &dk[Maxdk]; dp++){
.
227c
static Dk*
.
220c
	0,
.
212d
125,128d
111c
	Lock;
.
107,108c
	QLock	netlock;
	Network	net;

	QLock	csclock;
.
86a
	Netprot;		/* stat info */
.
18c
	Maxdk = 4,
.
## diffname port/devdk.c 1992/0625
## diff -e /n/bootesdump/1992/0623/sys/src/9/port/devdk.c /n/bootesdump/1992/0625/sys/src/9/port/devdk.c
1495d
1491,1493c
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0)
.
1362c
	lp = linealloc(dk[c->dev], STREAMID(c->qid.path), 1);
.
1312c
	dp = dk[c->dev];
.
1142c
	dp = dk[c->dev];
.
966c
	dp = dk[c->dev];
.
909c
	strncpy(buf, dk[c->dev]->linep[STREAMID(c->qid.path)]->other, len);
.
904c
	strncpy(buf, dk[c->dev]->linep[STREAMID(c->qid.path)]->ruser, len);
.
899c
	strncpy(buf, dk[c->dev]->linep[STREAMID(c->qid.path)]->raddr, len);
.
894c
	strncpy(buf, dk[c->dev]->linep[STREAMID(c->qid.path)]->addr, len);
.
882,883c
	incref(dp->csc);
.
872c
 *  open the common signalling channel (dp->csc's reference count never goes below 1)
.
829c
	dp = dk[c->dev];
.
816c
	netwstat(c, dp, &dk[c->dev]->net);
.
746c
	return netread(c, a, n, offset, &dk[c->dev]->net);
.
724c
	dp = dk[c->dev];
.
716c
	netstat(c, dp, &dk[c->dev]->net);
.
710c
	return netwalk(c, name, &dk[c->dev]->net);
.
697c
	c->dev = dev;
.
691c
	for(dev = 0; dev < Maxdk; dev++){
		dp = dk[dev];
		if(dp && strcmp(dp->name, spec) == 0)
			break;
	}
	if(dev == Maxdk)
		error(Enoifc);
.
684a
	int dev;
.
669,671d
657a
	tsleep(&dp->closeallr, haveca, dp, 15000);
.
656d
646d
638d
632c
	q->ptr = q->other->ptr = dp;
	dk[n] = dp;
	unlock(&dklock);
	poperror();
.
628,630d
625a
	for(n = 0; n < Maxdk; n++){
		dp = dk[n];
		if(dp == 0)
			break;
		if(strcmp(name, dp->name) == 0)
			error(Einuse);
	}
	if(n == Maxdk)
		error(Enoifc);

	/*
	 *  allocate both a dk structure and an array of pointers to line
	 *  structures
	 */
	dp = smalloc(sizeof(Dk));
	dp->ncsc = ncsc;
	dp->lines = lines;
	dp->linep = smalloc(sizeof(Line*) * dp->lines);
	strcpy(dp->name, name);
	dp->net.name = dp->name;
	dp->net.nconv = dp->lines;
	dp->net.devp = &dkinfo;
	dp->net.protop = &urpinfo;
	dp->net.listen = dklisten;
	dp->net.clone = dkcloneline;
	dp->net.ninfo = 5;
	dp->net.info[0].name = "addr";
	dp->net.info[0].fill = dkfilladdr;
	dp->net.info[1].name = "raddr";
	dp->net.info[1].fill = dkfillraddr;
	dp->net.info[2].name = "ruser";
	dp->net.info[2].fill = dkfillruser;
	dp->net.info[3].name = "stats";
	dp->net.info[3].fill = urpfillstats;
	dp->net.info[4].name = "other";
	dp->net.info[4].fill = dkfillother;
.
620,624c
	lock(&dklock);
	if(waserror()){
		unlock(&dklock);
		nexterror();
.
618c
	 *  find a free dk slot.  it name is already configured
	 *  or no slots are left, error.
.
600a
		name[sizeof(name)-1] = 0;
.
501,502d
420c
	dp = dk[s->dev];
.
398,401c
static Streamopen dkstopen;
static Streamclose dkstclose;
static Streamput dkoput, dkiput;
.
321,322c
	naildownstream(s);
.
318c
	RD(q)->ptr = 0;
.
294,295d
223,286d
210,212c
static Streamopen dkmuxopen;
static Streamput dkmuxoput;
static Streamput dkmuxiput;
.
129,130c
Lock	dklock;
Dk	*dk[Maxdk];
.
122d
119d
114,116d
109c
	Network	net;		/* stat info */
	Line	**linep;	/* array of line structures */
.
107c
struct Dk
{
.
## diffname port/devdk.c 1992/0711
## diff -e /n/bootesdump/1992/0625/sys/src/9/port/devdk.c /n/bootesdump/1992/0711/sys/src/9/port/devdk.c
1560c
	int i;
.
1520d
1324d
843,844d
722a
	USED(offset);
.
698a
	USED(name);
	USED(omode);
	USED(perm);
.
## diffname port/devdk.c 1992/0717
## diff -e /n/bootesdump/1992/0711/sys/src/9/port/devdk.c /n/bootesdump/1992/0717/sys/src/9/port/devdk.c
1218a
		if(cp = strchr(lp->addr, '.')){
			*cp = '!';
			if(cp = strchr(cp, '.'))
				*cp = 0;
		}
.
1216c
		 *  Special characters are escaped by '\'s.  Convert to
		 *  a plan 9 address, i.e. system!service.
.
1107a
	char *cp;
.
1085d
876c
	Dk *dp;
	Line *lp;
	int line;
	char lbuf[65];

	line = STREAMID(c->qid.path);
	dp = dk[c->dev];
	lp = linealloc(dp, line, 1);
	sprint(lbuf, "%s/%d %d %s window %d\n", dp->name, line,
		lp->state != Lclosed ? 1 : 0, dkstate[lp->state], lp->window);
	strncpy(buf, lbuf, len);
.
874c
dkfillstatus(Chan *c, char *buf, int len)
.
871c
	if(len < sizeof(dk[0]->linep[0]->ruser)+2)
		error(Ebadarg);
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->ruser);
.
866c
	if(len < sizeof(dk[0]->linep[0]->raddr)+2)
		error(Ebadarg);
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->raddr);
.
861c
	if(len < sizeof(dk[0]->linep[0]->addr)+2)
		error(Ebadarg);
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->addr);
.
588,589c
	dp->net.info[4].name = "status";
	dp->net.info[4].fill = dkfillstatus;
.
586c
	dp->net.info[3].name = "urpstats";
.
582c
	dp->net.info[1].name = "remote";
.
580c
	dp->net.info[0].name = "local";
.
200c
static void	dkfillstatus(Chan*, char*, int);
.
142a
char *dkstate[] =
{
	[Lclosed]	"Closed",
	[Lopened]	"Opened",
	[Lconnected]	"Established",
	[Lrclose]	"Rclose",
	[Llclose]	"Lclose",
	[Ldialing]	"Dialing",
	[Llistening]	"Listen",
	[Lackwait]	"Ackwait",
	[Laccepting]	"Accepting",
};
.
99d
## diffname port/devdk.c 1992/1030
## diff -e /n/bootesdump/1992/0717/sys/src/9/port/devdk.c /n/bootesdump/1992/1030/sys/src/9/port/devdk.c
1034c
	for(win = 0; ; win++)
		if(W_VALUE(win) >= dp->urpwindow || win == 15)
			break;
	dkmesg(csc, t_val, d_val, line, W_WINDOW(win, win, 2));
.
950c
	int line, win;
.
## diffname port/devdk.c 1992/1031
## diff -e /n/bootesdump/1992/1030/sys/src/9/port/devdk.c /n/bootesdump/1992/1031/sys/src/9/port/devdk.c
536a
		if(window < 16)
			window = 1<<(window+4);
.
527c
	window = 2048;
.
491c
 *	the urp window size (default==2048)
.
83d
## diffname port/devdk.c 1993/0501
## diff -e /n/bootesdump/1992/1031/sys/src/9/port/devdk.c /n/fornaxdump/1993/0501/sys/src/brazil/port/devdk.c
993c
		strcat(dialstr, up->user);
.
979c
	if(strlen(addr)+strlen(up->user)+2 >= sizeof(dialstr))
.
823c
			netown(lp, up->user, 0);
.
## diffname port/devdk.c 1993/0804 # deleted
## diff -e /n/fornaxdump/1993/0501/sys/src/brazil/port/devdk.c /n/fornaxdump/1993/0804/sys/src/brazil/port/devdk.c
1,1636d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.