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

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


## diffname gnot/devdk.c 1990/0312
## diff -e /dev/null /n/bootesdump/1990/0312/sys/src/9/68020/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 discipline 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[0] | (bp->rptr[1]<<8);
	bp->rptr += 2;
	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;
print("attach\n");

	/*
	 *  find a multiplexor with the same name
	 */
	for(dp = dk; dp < &dk[Ndk]; dp++){
		qlock(dp);
print("name %s %lux\n", dp->name, dp->wq);
		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;
print("attach done\n");
	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 gnot/devdk.c 1990/0315
## diff -e /n/bootesdump/1990/0312/sys/src/9/68020/devdk.c /n/bootesdump/1990/0315/sys/src/9/68020/devdk.c
1124c
				DPRINT("DKbusy2 %ux\n", lp->state);
.
1118c
			DPRINT("DKbusy1\n");
.
596d
584d
577d
414a
	else
		panic("dkoput");
.
305c
		DPRINT("dkmuxiput unopened line %d\n", line);
.
300c
			DPRINT("dkmuxiput unopened line %d\n", line);
.
290c
		DPRINT("dkmuxiput bad line %d\n", line);
.
## diffname gnot/devdk.c 1990/0319
## diff -e /n/bootesdump/1990/0315/sys/src/9/68020/devdk.c /n/bootesdump/1990/0319/sys/src/9/68020/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 gnot/devdk.c 1990/0321
## diff -e /n/bootesdump/1990/0319/sys/src/9/68020/devdk.c /n/bootesdump/1990/0321/sys/src/9/68020/devdk.c
540a
	newqinfo(&dkmuxinfo);
	newqinfo(&urpinfo);
.
## diffname gnot/devdk.c 1990/0331
## diff -e /n/bootesdump/1990/0321/sys/src/9/68020/devdk.c /n/bootesdump/1990/0331/sys/src/9/68020/devdk.c
420c
	if(dp->wq->len >= Streamhi){
		print("dkoput free\n");
		freeb(bp);
	} else
		PUTNEXT(dp->wq, bp);
.
## diffname gnot/devdk.c 1990/0403
## diff -e /n/bootesdump/1990/0331/sys/src/9/68020/devdk.c /n/bootesdump/1990/0403/sys/src/9/68020/devdk.c
1134a
			if(lp->state != Lconnected)
				dkanswer(c, lineno, DKbusy);
.
420,424c
	PUTNEXT(dp->wq, bp);
.
## diffname gnot/devdk.c 1990/05313
## diff -e /n/bootesdump/1990/0403/sys/src/9/68020/devdk.c /n/bootesdump/1990/05313/sys/src/9/68020/devdk.c
1200a
	panic("dklisten terminates strangely\n");
.
898c
	if(lp->state != Lopened)
.
726c
	/* real closing happens in dkstclose */
.
676a
		qunlock(lp);
.
669d
420c
	if(QFULL(dp->wq->next)){
		print("dk wq full\n");
		freeb(bp);
	} else
		PUTNEXT(dp->wq, bp);
.
331a
	if(lp->state == Lclosed)
		lp->state = Lopened;
.
## diffname gnot/devdk.c 1990/0617
## diff -e /n/bootesdump/1990/05313/sys/src/9/68020/devdk.c /n/bootesdump/1990/0617/sys/src/9/68020/devdk.c
1291a

	/*
	 *  tell datakit we've rebooted. It should close all channels.
	 */
	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 gnot/devdk.c 1990/0707
## diff -e /n/bootesdump/1990/0617/sys/src/9/68020/devdk.c /n/bootesdump/1990/0707/sys/src/9/68020/devdk.c
1298a
	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 gnot/devdk.c 1990/0717
## diff -e /n/bootesdump/1990/0707/sys/src/9/68020/devdk.c /n/bootesdump/1990/0717/sys/src/9/68020/devdk.c
554c
	urpreset();
.
## diffname gnot/devdk.c 1990/0725
## diff -e /n/bootesdump/1990/0717/sys/src/9/68020/devdk.c /n/bootesdump/1990/0725/sys/src/9/68020/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 gnot/devdk.c 1990/0726
## diff -e /n/bootesdump/1990/0725/sys/src/9/68020/devdk.c /n/bootesdump/1990/0726/sys/src/9/68020/devdk.c
598,599d
596c
	 *  find a multiplexor with the same name
.
507c
		kproc("**dktimer**", dktimer, 0);
.
498c
	sprint(buf, "**csckproc%d**", dp->ncsc);
.
459d
## diffname gnot/devdk.c 1990/0728
## diff -e /n/bootesdump/1990/0726/sys/src/9/68020/devdk.c /n/bootesdump/1990/0728/sys/src/9/68020/devdk.c
1480c
	while(waserror())
		print("dktimer: error\n");
.
863a
	}
.
862c
	if(waserror()){
		print("dkmesg: error\n");
.
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 gnot/devdk.c 1990/08101
## diff -e /n/bootesdump/1990/0728/sys/src/9/68020/devdk.c /n/bootesdump/1990/08101/sys/src/9/68020/devdk.c
1320a
	}
	DPRINT("dkcsckproc: closeall %s\n", dp->name);
.
1319c
	if(dp->restart) {
		DPRINT("dkcsckproc: restart %s\n", dp->name);
.
498c
	sprint(buf, "csc.%s.%d", dp->name, dp->ncsc);
.
479a
	DPRINT("dkmuxconfig: ncsc=%d, lines=%d, restart=%d, name=\"%s\"\n",
		dp->ncsc, dp->lines, dp->restart, dp->name);
.
11a
#define	NOW	(MACHP(0)->ticks)

.
9,10c
#define	DPRINT	if(0)	/*kprint*/
.
## diffname gnot/devdk.c 1990/08163
## diff -e /n/bootesdump/1990/08101/sys/src/9/68020/devdk.c /n/bootesdump/1990/08163/sys/src/9/68020/devdk.c
981c
	dkmesg(dp, t_val, d_val, line, W_WINDOW(dp->urpwindow,dp->urpwindow,2));
.
461a
	case 5:
		dp->urpwindow = strtoul(fields[4], 0, 0);
.
460a
	dp->urpwindow = WS_2K;
.
459c
	n = getfields((char *)bp->rptr, fields, 5, ' ');
.
445c
	char *fields[5];
.
437c
 *	the name of the dk (default==dk)
 *	the urp window size (default==WS_2K)
.
432c
 *  configure a datakit multiplexor.  this takes 5 arguments separated
.
127a
	int	urpwindow;
.
## diffname gnot/devdk.c 1990/0905
## diff -e /n/bootesdump/1990/08163/sys/src/9/68020/devdk.c /n/bootesdump/1990/0905/sys/src/9/68020/devdk.c
418,421c
	bp = padb(bp, 2);
.
400c
 *  add a 2 byte channel number to the start of each message,
 *  low order byte first.
.
## diffname gnot/devdk.c 1990/0911
## diff -e /n/bootesdump/1990/0905/sys/src/9/68020/devdk.c /n/bootesdump/1990/0911/sys/src/9/68020/devdk.c
561d
## diffname gnot/devdk.c 1990/1004
## diff -e /n/bootesdump/1990/0911/sys/src/9/68020/devdk.c /n/bootesdump/1990/1004/sys/src/9/68020/devdk.c
1320,1329d
1115d
1105d
1103d
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 gnot/devdk.c 1990/1018
## diff -e /n/bootesdump/1990/1004/sys/src/9/68020/devdk.c /n/bootesdump/1990/1018/sys/src/9/68020/devdk.c
423,427c
	FLOWCTL(dp->wq);
	PUTNEXT(dp->wq, bp);
.
121c
	char	name[64];	/* dk name */
.
## diffname gnot/devdk.c 1990/1020
## diff -e /n/bootesdump/1990/1018/sys/src/9/68020/devdk.c /n/bootesdump/1990/1020/sys/src/9/68020/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 gnot/devdk.c 1990/1022
## diff -e /n/bootesdump/1990/1020/sys/src/9/68020/devdk.c /n/bootesdump/1990/1022/sys/src/9/68020/devdk.c
226,228d
209d
## diffname gnot/devdk.c 1990/1024
## diff -e /n/bootesdump/1990/1022/sys/src/9/68020/devdk.c /n/bootesdump/1990/1024/sys/src/9/68020/devdk.c
1555a
			unlock(dp);
.
1531a
			if(dp->csc==0){
				unlock(dp);
				continue;
			}
.
1530c
			if(!canlock(dp))
.
1328a
			if(n == 0)
				error(0, Ehungup);
.
1322a
	if(waserror()){
		Chan *csc;

		csc = dp->csc;
		lock(dp);
		dp->csc = 0;
		unlock(dp);
		close(csc);
		return;
	}

.
756,758c
	if(streamexit(dp->s, 0) == 0)
		dp->name[0] = 0;
.
754a
	/*
	 *  Let go of the mulitplexed stream.  If we're the last out,
	 *  free dp.
	 */
.
685d
681a
		dp = &dk[c->dev];
.
632,634c
	if(streamenter(dp->s) < 0)
		error(0, Ehungup);
.
625a
/*
 *  clone as long as the multiplexed channel is not closing
 *  down
 */
.
622a
	unlock(dp);
	poperror();
.
620a

	/*
	 *  don't let the multiplexed stream disappear under us
	 */
	if(streamenter(dp->s) < 0){
		/*
		 *  it's closing down, forget it
		 */
		unlock(dp);
		error(0, Ehungup);
	}

	/*
	 *  return the new channel
	 */
	if(waserror()){
		if(streamexit(dp->s, 0) == 0)
			dp->name[0] = 0;
		unlock(dp);
		nexterror();
	}
.
616,617c
		unlock(dp);
.
611,614c
		lock(dp);
		if(strcmp(spec, dp->name)==0)
.
275c
 *	work.
.
241,245c

	/*
	 *  if we're the last user of the stream,
	 *  free the Dk structure
	 */
	if(dp->s->inuse == 1)
		dp->name[0] = 0;

	/*
	 *  hang up all datakit connections
	 */
	for(i=dp->ncsc; i < dp->lines; i++)
		dkhangup(&dp->line[i]);
.
238a
	int i;
.
225c
			dp->s = s;
			unlock(dp);
.
223c
			strcpy(dp->name, "/");
.
216c
				unlock(dp);
.
212,214c
		if(dp->name[0]==0){
			lock(dp);
			if(dp->name[0]){
.
203a
 *  when we get though here dp->s is meaningful and the name is set to "/".
.
191a
static void	dkhangup(Line*);
.
122a
	Stream	*s;
.
119,120c
	Lock;
.
## diffname gnot/devdk.c 1990/1026
## diff -e /n/bootesdump/1990/1024/sys/src/9/68020/devdk.c /n/bootesdump/1990/1026/sys/src/9/68020/devdk.c
1613c
		tsleep(&dp->timer, return0, 0, 7500);
.
1611d
1603,1609c
				dkreplymesg(dp, (Dkmsg *)0, i);
				break;
.
1593,1601c
			case Ldialing:
				if(lp->calltolive==0 || --lp->calltolive!=0)
.
1588,1591c
		/*
		 *  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;
.
1579,1586c
		dkmesg(c, T_ALIVE, D_CONTINUE, 0, 0);
.
1577c
		 * send keep alive
.
1575a
		if(dp->opened==0)
			error(0, Ehungup);

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

.
1572,1573c
	c = 0;
	if(waserror()){
		if(c)
			close(c);
		return;
	}
.
1570a
	Chan *c;
.
1559,1564d
1557c
 *  send a I'm alive message every 7.5 seconds and remind the dk of
 *  any closed channels it hasn't acknowledged.
.
1463c
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
.
1454c
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
.
1448c
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
.
1427c
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
.
1418c
dkchgmesg(Chan *c, Dk *dp, Dkmsg *dialp, int line)
.
1388c
			dkchgmesg(dp->csc, dp, &d, line);
.
1362,1368c
		close(dp->csc);
.
1359c
	dp = a;
.
1357d
1026c
	csc = dkopenline(dp, dp->ncsc);
	dkmesg(csc, t_val, d_val, line, W_WINDOW(dp->urpwindow,dp->urpwindow,2));
	close(csc);
	csc = 0;
.
1018a
	/*
	 *  open the data file
	 */
	dc = dkopenline(dp, line);
.
1016,1017d
1013c
		if(csc)
			close(csc);
		if(dc)
			close(dc);
.
1011c
	dc = 0;
	csc = 0;
.
1009c
	 *  close temporary channels on error
.
956c
	
.
954a
	Chan *csc;
.
929c
	streamwrite(c, (char *)&d, sizeof(Dkmsg), 1);
.
911,912d
909d
906c
dkmesg(Chan *c, int type, int srv, int p0, int p1)
.
902a
 *  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;
}

/*
.
789,796d
786d
665,669d
658,661d
653,654d
645,650d
632,642d
622,629c
	dp = dkalloc(spec);
.
528,531c
	sprint(buf, "timer.%s.%d", dp->name, dp->ncsc);
	kproc(buf, dktimer, dp);
.
526c
	 *  start a keepalive process
.
523d
519c
	 *  start a process to listen to csc messages
.
514,515c
		DPRINT("dktimer: restart %s\n", dp->name);
		dkmesg(dp->csc, T_ALIVE, D_RESTART, 0, 0);
.
511a
	 *  do this here to get it done before trying to open a channel.
.
510a
	 *  open csc here so that boot, dktimer, and dkcsckproc aren't
	 *  all fighting for it at once.
	 */
	dp->csc = dkopenline(dp, dp->ncsc);

	/*
.
505,508c
	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);
.
499,503c
	dp = dkalloc(name);
	lock(dp);
	if(dp->opened){
		unlock(dp);
		error(0, Ebadarg);
.
497c
	 *  set up
.
492,494d
489,490c
	if(ncsc <= 0 || lines <= ncsc)
.
482c
		ncsc = strtoul(fields[0], 0, 0);
.
480c
		lines = strtoul(fields[1], 0, 0);
.
478c
			restart = 0;
.
475c
		strncpy(name, fields[3], sizeof(name));
.
473c
		window = strtoul(fields[4], 0, 0);
.
469,470d
467d
464a
	 *  defaults
	 */
	ncsc = 1;
	restart = 1;
	lines = 16;
	window = WS_2K;
	strcpy(name, "dk");

	/*
.
461c
		error(0, Egreg);
.
459c
	if(WR(q)->ptr){
.
457c
	char name[NAMELEN];
	int lines;
	int ncsc;
	int restart;
	int window;
.
453c
	Dk *dp;
.
451c
dkmuxconfig(Queue *q, Block *bp)
.
394a
out:
.
393a
	poperror();
	close(c);
.
387c
		dkmesg(c, T_CHG, D_CLOSE, lp - dp->line, 0);
.
382c
		dkmesg(c, T_CHG, D_CLOSE, lp - dp->line, 0);
.
377c
		dkmesg(c, T_CHG, D_CLOSE, lp - dp->line, 0);
.
372c
		dkmesg(c, T_CHG, D_CLOSE, lp - dp->line, 0);
.
363a
	 *  decrement ref count on mux'd line
	 */
	streamexit(dp->s, 0);

	/*
	 *  don't tell controller about closing down the csc
	 */
	if(lp - dp->line == dp->ncsc)
		goto out;

	c = 0;
	if(waserror()){
		if(c)
			close(c);
		lp->state = Lclosed;
		goto out;
	}	
	c = dkopenline(dp, dp->ncsc);

	/*
.
358a
	Chan *c;
.
343a
	lock(dp);
	if(dp->opened==0 || streamenter(dp->s)<0){
		unlock(dp);
		error(0, Ehungup);
	}
	unlock(dp);
.
295a
	/*
	 *  not configured yet
	 */
	if(q->other->ptr == 0){
		freeb(bp);
		return;
	}

.
271c
			dkmuxconfig(q, bp);
.
266,268d
257a

	/*
	 *  wakeup the timer so it can die
	 */
	wakeup(&dp->timer);
.
250,251c
	lock(dp);
	dp->opened = 0;
	unlock(dp);
.
247,248c
	 *  disallow new dkstopens() on this line.
	 *  the lock syncs with dkstopen().
.
244c
	dp = WR(q)->ptr;
	if(dp == 0)
		return;
.
235a
 *  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;
}

/*
.
232c
	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;
.
230a
		if(dp->name[0] == 0)
			freep = dp;
.
214,229c
		if(strcmp(name, dp->name) == 0){
			unlock(&dklock);
			return dp;
.
212a
	lock(&dklock);
	freep = 0;
.
211c
	Dk *freep;
.
204,208c
 *  Look for a dk struct with a name.  If none exists,  create one.
 */ 
static Dk *
dkalloc(char *name)
.
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;
.
119a
	int	opened;
.
## diffname gnot/devdk.c 1990/1101
## diff -e /n/bootesdump/1990/1026/sys/src/9/68020/devdk.c /n/bootesdump/1990/1101/sys/src/9/68020/devdk.c
1620a
		/*
		 *  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;

			case Ldialing:
				dkreplymesg(dp, (Dkmsg *)0, i);
				break;
			}
		}
.
1548a
				dkhangup(lp);
.
1547d
1532a
		/*
		 *  datakit wants us to close all lines
		 */
.
1455d
1451,1453c
			if(line >=0 && line<dp->lines)
.
1191,1192d
1186c
	dc = dkopenline(dp, STREAMID(c->qid));
.
822,823d
774d
771a
		lp->state = Lopened;
		qunlock(lp);
.
445,447d
422,425d
413d
410a
		lp->state = Lclosed;
.
407a
	}
.
406c
	switch(lp->state){
	case Lclosed:
	case Llclose:
	case Lopened:
		lp->state = Lclosed;
.
404c
	 *  these states don't need the datakit
.
398a
	 *  if we never got going, we're done
	 */
	if(lp->rq == 0){
		lp->state = Lclosed; 
		return;
	}

	/*
.
382a
	lp->rq = q;
.
378,381c
	if(lp->state==Lclosed)
.
371a
	q->other->ptr = q->ptr = lp = &dp->line[s->id];
	lp->dp = dp;
.
## diffname gnot/devdk.c 1990/1104
## diff -e /n/bootesdump/1990/1101/sys/src/9/68020/devdk.c /n/bootesdump/1990/1104/sys/src/9/68020/devdk.c
382d
379a
	lp->rq = q;
.
## diffname gnot/devdk.c 1990/11151
## diff -e /n/bootesdump/1990/1104/sys/src/9/68020/devdk.c /n/bootesdump/1990/11151/sys/src/9/68020/devdk.c
360c
Qinfo dkinfo =
{
	dkiput,
	dkoput,
	dkstopen,
	dkstclose,
	"dk"
};
.
205c
Qinfo dkmuxinfo =
{
	dkmuxiput,
	dkmuxoput,
	dkmuxopen,
	dkmuxclose,
	"dkmux"
};
.
## diffname gnot/devdk.c 1990/11211
## diff -e /n/bootesdump/1990/11151/sys/src/9/68020/devdk.c /n/bootesdump/1990/11211/sys/src/9/68020/devdk.c
1670a
		DPRINT("keep alive\n");
.
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);
.
9c
#define	DPRINT	if(0) print
.
## diffname gnot/devdk.c 1990/1126
## diff -e /n/bootesdump/1990/11211/sys/src/9/68020/devdk.c /n/bootesdump/1990/1126/sys/src/9/68020/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 gnot/devdk.c 1990/1210 # deleted
## diff -e /n/bootesdump/1990/1126/sys/src/9/68020/devdk.c /n/bootesdump/1990/1210/sys/src/9/68020/devdk.c
1,1684d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].