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

1993/0804/port/devdk.c (diff list | history)

1993/0501/sys/src/9/port/devdk.c:1,16361993/0804/sys/src/9/port/devdk.c:0 (short | long | prev)
Deleted.
rsc Mon Mar 7 10:30:39 2005
1990/0312    
#include	"u.h" 
1992/0321    
#include	"../port/lib.h" 
1990/0312    
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
1992/0111    
#include	"../port/error.h" 
1990/0312    
                 
1990/11161    
#define	DPRINT	if(0) print 
1990/0312    
                 
1990/0911    
#define	NOW	(MACHP(0)->ticks) 
                 
1990/0312    
typedef struct Dkmsg	Dkmsg; 
typedef struct Line	Line; 
typedef struct Dk	Dk; 
                 
1991/1115    
enum { 
1992/0623    
	Maxdk = 4, 
1991/1115    
}; 
                 
1990/0312    
/* 
 *  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 */ 
1990/0617    
#define	T_CHG	3		/* change the status of a connection */ 
1990/0312    
#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)) 
                 
struct Line { 
	QLock; 
1992/0623    
	Netprot;		/* stat info */ 
1991/1107    
	int	lineno; 
1990/0312    
	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]; 
	Dk *dp;			/* interface contianing this line */ 
}; 
                 
/* 
 *  a dkmux dk.  one exists for every stream that a  
1990/0315    
 *  dkmux line discipline is pushed onto. 
1990/0312    
 */ 
1992/0625    
struct Dk 
{ 
1992/0623    
	QLock	netlock; 
1992/0625    
	Network	net;		/* stat info */ 
	Line	**linep;	/* array of line structures */ 
1992/0623    
                 
	QLock	csclock; 
1991/1116    
	Chan	*csc; 
                 
1990/1018    
	char	name[64];	/* dk name */ 
1990/0312    
	Queue	*wq;		/* dk output queue */ 
	int	ncsc;		/* csc line number */ 
1991/1116    
	int	lines;		/* number of lines */ 
1990/0707    
	int	restart; 
1990/0911    
	int	urpwindow; 
1990/1101    
	Rendez	timer; 
1990/1210    
	int	closeall;	/* set when we receive a closeall message */ 
	Rendez	closeallr;	/* wait here for a closeall */ 
1990/0312    
}; 
1992/0625    
Lock	dklock; 
Dk	*dk[Maxdk]; 
1990/0312    
                 
/* 
 *  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; 
1992/0717    
char *dkstate[] = 
{ 
	[Lclosed]	"Closed", 
	[Lopened]	"Opened", 
	[Lconnected]	"Established", 
	[Lrclose]	"Rclose", 
	[Llclose]	"Lclose", 
	[Ldialing]	"Dialing", 
	[Llistening]	"Listen", 
	[Lackwait]	"Ackwait", 
	[Laccepting]	"Accepting", 
}; 
1990/0312    
                 
/* 
1991/1206    
 *  map datakit error to errno  
1990/0312    
 */ 
enum { 
	DKok, 
	DKbusy, 
	DKnetotl, 
	DKdestotl, 
	DKbadnet, 
	DKnetbusy, 
	DKinuse, 
	DKreject, 
}; 
1991/1206    
char* dkerr[]={ 
1992/0622    
	[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",  
1990/0312    
}; 
1991/1206    
#define DKERRS sizeof(dkerr)/sizeof(char*) 
1990/0312    
                 
/* 
 *  imported 
 */ 
extern Qinfo urpinfo; 
                 
/* 
 *  predeclared 
 */ 
Chan*		dkattach(char*); 
1990/1101    
static void	dkmuxconfig(Queue*, Block*); 
static Chan*	dkopenline(Dk*, int); 
1991/1116    
static Chan*	dkopencsc(Dk*); 
1990/1101    
static int	dkmesg(Chan*, int, int, int, int); 
1990/0312    
static void	dkcsckproc(void*); 
static void	dkanswer(Chan*, int, int); 
static void	dkwindow(Chan*); 
static void	dkcall(int, Chan*, char*, char*, char*); 
static void	dktimer(void*); 
1990/1101    
static void	dkchgmesg(Chan*, Dk*, Dkmsg*, int); 
1990/0312    
static void	dkreplymesg(Dk*, Dkmsg*, int); 
Chan*		dkopen(Chan*, int); 
1990/1101    
static void	dkhangup(Line*); 
1990/0312    
                 
/* 
1991/1108    
 *  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); 
1992/0717    
static void	dkfillstatus(Chan*, char*, int); 
1991/1108    
                 
extern Qinfo dkinfo; 
                 
/* 
1990/0312    
 *  the datakit multiplexor stream module definition 
 */ 
1992/0625    
static Streamopen dkmuxopen; 
static Streamput dkmuxoput; 
static Streamput dkmuxiput; 
1990/1113    
Qinfo dkmuxinfo = 
{ 
	dkmuxiput, 
	dkmuxoput, 
	dkmuxopen, 
1992/0623    
	0, 
1990/1113    
	"dkmux" 
}; 
1990/0312    
                 
/* 
1992/0623    
 *  allocate a line if it doesn't exist 
 */ 
static Line* 
linealloc(Dk *dp, int lineno, int dolock) 
{ 
	Line *lp; 
                 
	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; 
} 
                 
/* 
1992/0223    
 *  a new dkmux.  hold the stream in place so it can never be closed down. 
1990/1101    
 */ 
static void 
dkmuxopen(Queue *q, Stream *s) 
{ 
1992/0625    
	RD(q)->ptr = 0; 
1990/1101    
	WR(q)->ptr = 0; 
1992/0219    
                 
1992/0625    
	naildownstream(s); 
1990/1101    
} 
                 
/* 
1990/0312    
 *  handle configuration 
 */ 
static void 
dkmuxoput(Queue *q, Block *bp) 
{ 
	if(bp->type != M_DATA){ 
		if(streamparse("config", bp)) 
1990/1101    
			dkmuxconfig(q, bp); 
1990/0312    
		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 
1990/1101    
 *	work. 
1990/0312    
 */ 
static void 
dkmuxiput(Queue *q, Block *bp) 
{ 
	Dk *dp; 
	Line *lp; 
	int line; 
                 
1990/1101    
	/* 
	 *  not configured yet 
	 */ 
	if(q->other->ptr == 0){ 
		freeb(bp); 
		return; 
	} 
                 
1990/0312    
	dp = (Dk *)q->ptr; 
	if(bp->type != M_DATA){ 
		PUTNEXT(q, bp); 
		return; 
	} 
                 
1990/0315    
	line = bp->rptr[0] | (bp->rptr[1]<<8); 
	bp->rptr += 2; 
1990/0312    
	if(line<0 || line>=dp->lines){ 
1990/0315    
		DPRINT("dkmuxiput bad line %d\n", line); 
1990/0312    
		freeb(bp); 
		return; 
	} 
                 
1992/0623    
	lp = linealloc(dp, line, 1); 
	if(lp && canqlock(lp)){ 
1990/0312    
		if(lp->rq) 
			PUTNEXT(lp->rq, bp); 
		else{ 
1990/0315    
			DPRINT("dkmuxiput unopened line %d\n", line); 
1990/0312    
			freeb(bp); 
		} 
		qunlock(lp); 
	} else { 
1990/0315    
		DPRINT("dkmuxiput unopened line %d\n", line); 
1990/0312    
		freeb(bp); 
	} 
} 
                 
/* 
 *  the datakit line stream module definition 
 */ 
1992/0625    
static Streamopen dkstopen; 
static Streamclose dkstclose; 
static Streamput dkoput, dkiput; 
1990/1113    
Qinfo dkinfo = 
{ 
	dkiput, 
	dkoput, 
	dkstopen, 
	dkstclose, 
	"dk" 
}; 
1990/0312    
                 
/* 
 *  open and save a pointer to the conversation 
 */ 
static void 
dkstopen(Queue *q, Stream *s) 
{ 
	Dk *dp; 
	Line *lp; 
                 
1992/0625    
	dp = dk[s->dev]; 
1991/1107    
	q->other->ptr = q->ptr = lp = dp->linep[s->id]; 
1990/0312    
	lp->dp = dp; 
1990/1104    
	lp->rq = q; 
1990/1101    
	if(lp->state==Lclosed) 
1990/0511    
		lp->state = Lopened; 
1990/0312    
} 
                 
/* 
 *  close down a datakit conversation 
 */ 
static void 
dkstclose(Queue *q) 
{ 
	Dk *dp; 
	Line *lp; 
1990/1101    
	Chan *c; 
1990/0312    
                 
	lp = (Line *)q->ptr; 
	dp = lp->dp; 
                 
	/* 
1990/1101    
	 *  if we never got going, we're done 
1990/0312    
	 */ 
1990/1101    
	if(lp->rq == 0){ 
		lp->state = Lclosed;  
		return; 
	} 
                 
	/* 
	 *  these states don't need the datakit 
	 */ 
1990/0312    
	switch(lp->state){ 
	case Lclosed: 
	case Llclose: 
1990/1101    
	case Lopened: 
		lp->state = Lclosed; 
		goto out; 
	} 
1990/0312    
                 
1990/1101    
	c = 0; 
	if(waserror()){ 
		lp->state = Lclosed; 
		if(c) 
			close(c); 
		goto out; 
	}	 
1991/1116    
	c = dkopencsc(dp); 
1990/1101    
                 
	/* 
	 *  shake hands with dk 
	 */ 
	switch(lp->state){ 
1990/0312    
	case Lrclose: 
1991/1107    
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0); 
1990/0312    
		lp->state = Lclosed; 
		break; 
                 
	case Lackwait: 
1991/1107    
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0); 
1990/0312    
		lp->state = Llclose; 
		break; 
                 
	case Llistening: 
1991/1107    
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0); 
1990/0312    
		lp->state = Llclose; 
		break; 
                 
	case Lconnected: 
1991/1107    
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0); 
1990/0312    
		lp->state = Llclose; 
		break; 
	} 
1990/1101    
	poperror(); 
	close(c); 
1990/0312    
                 
1990/1101    
out: 
1990/0312    
	qlock(lp); 
	lp->rq = 0; 
	qunlock(lp); 
1991/1116    
                 
1992/0623    
	netdisown(lp); 
1992/0409    
	lp->window = 0; 
1990/0312    
} 
                 
/* 
 *  this is only called by hangup 
 */ 
static void 
dkiput(Queue *q, Block *bp) 
{ 
	PUTNEXT(q, bp); 
} 
                 
/* 
 *  we assume that each put is a message. 
 * 
1990/0911    
 *  add a 2 byte channel number to the start of each message, 
1992/0623    
 *  low order byte first.  Make sure the first block contains 
 *  both the 2 channel bytes and the control byte. 
1990/0312    
 */ 
static void 
dkoput(Queue *q, Block *bp) 
{ 
	Line *lp; 
	Dk *dp; 
	int line; 
                 
	if(bp->type != M_DATA){ 
		freeb(bp); 
1990/11211    
		error(Ebadarg); 
1990/0312    
	} 
                 
	lp = (Line *)q->ptr; 
	dp = lp->dp; 
1991/1107    
	line = lp->lineno; 
1990/0312    
                 
1990/0911    
	bp = padb(bp, 2); 
1990/0312    
	bp->rptr[0] = line; 
	bp->rptr[1] = line>>8; 
                 
1992/0305    
	FLOWCTL(dp->wq, bp); 
1990/0312    
} 
                 
/* 
1990/0911    
 *  configure a datakit multiplexor.  this takes 5 arguments separated 
1990/0312    
 *  by spaces: 
 *	the line number of the common signalling channel (must be > 0) 
 *	the number of lines in the device (optional) 
1990/0707    
 *	the word `restart' or `norestart' (optional/default==restart) 
1990/0911    
 *	the name of the dk (default==dk) 
1992/1031    
 *	the urp window size (default==2048) 
1990/0312    
 * 
 *  we can configure only once 
 */ 
1990/1202    
static int 
1990/1210    
haveca(void *arg) 
1990/1202    
{ 
	Dk *dp; 
                 
	dp = arg; 
1990/1210    
	return dp->closeall; 
1990/1202    
} 
1990/0312    
static void 
1990/1101    
dkmuxconfig(Queue *q, Block *bp) 
1990/0312    
{ 
1990/1101    
	Dk *dp; 
1990/0911    
	char *fields[5]; 
1990/0312    
	int n; 
	char buf[64]; 
1990/1101    
	char name[NAMELEN]; 
	int lines; 
	int ncsc; 
	int restart; 
	int window; 
1990/0312    
                 
1990/1101    
	if(WR(q)->ptr){ 
1990/0312    
		freeb(bp); 
1990/11211    
		error(Egreg); 
1990/0312    
	} 
                 
	/* 
1990/1101    
	 *  defaults 
	 */ 
	ncsc = 1; 
	restart = 1; 
	lines = 16; 
1992/1031    
	window = 2048; 
1990/1101    
	strcpy(name, "dk"); 
                 
	/* 
1990/0312    
	 *  parse 
	 */ 
1990/0911    
	n = getfields((char *)bp->rptr, fields, 5, ' '); 
1990/0312    
	switch(n){ 
1990/0911    
	case 5: 
1990/1101    
		window = strtoul(fields[4], 0, 0); 
1992/1031    
		if(window < 16) 
			window = 1<<(window+4); 
1990/0707    
	case 4: 
1990/1101    
		strncpy(name, fields[3], sizeof(name)); 
1992/0625    
		name[sizeof(name)-1] = 0; 
1990/0312    
	case 3: 
1990/0707    
		if(strcmp(fields[2], "restart")!=0) 
1990/1101    
			restart = 0; 
1990/0312    
	case 2: 
1990/1101    
		lines = strtoul(fields[1], 0, 0); 
1990/0312    
	case 1: 
1990/1101    
		ncsc = strtoul(fields[0], 0, 0); 
1990/0312    
		break; 
	default: 
		freeb(bp); 
1990/11211    
		error(Ebadarg); 
1990/0312    
	} 
	freeb(bp); 
1990/1101    
	if(ncsc <= 0 || lines <= ncsc) 
1990/11211    
		error(Ebadarg); 
1990/0312    
                 
	/* 
1992/0625    
	 *  find a free dk slot.  it name is already configured 
	 *  or no slots are left, error. 
1990/0312    
	 */ 
1992/0625    
	lock(&dklock); 
	if(waserror()){ 
		unlock(&dklock); 
		nexterror(); 
1990/0312    
	} 
1992/0625    
	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; 
1992/0717    
	dp->net.info[0].name = "local"; 
1992/0625    
	dp->net.info[0].fill = dkfilladdr; 
1992/0717    
	dp->net.info[1].name = "remote"; 
1992/0625    
	dp->net.info[1].fill = dkfillraddr; 
	dp->net.info[2].name = "ruser"; 
	dp->net.info[2].fill = dkfillruser; 
1992/0717    
	dp->net.info[3].name = "urpstats"; 
1992/0625    
	dp->net.info[3].fill = urpfillstats; 
1992/0717    
	dp->net.info[4].name = "status"; 
	dp->net.info[4].fill = dkfillstatus; 
1990/1101    
	dp->restart = restart; 
	dp->urpwindow = window; 
	dp->wq = WR(q); 
1992/0625    
	q->ptr = q->other->ptr = dp; 
	dk[n] = dp; 
	unlock(&dklock); 
	poperror(); 
1990/0312    
                 
	/* 
1990/1101    
	 *  open csc here so that boot, dktimer, and dkcsckproc aren't 
	 *  all fighting for it at once. 
	 */ 
1991/1116    
	dkopencsc(dp); 
1990/1101    
                 
	/* 
1990/1202    
	 *  start a process to listen to csc messages 
	 */ 
	sprint(buf, "csc.%s.%d", dp->name, dp->ncsc); 
	kproc(buf, dkcsckproc, dp); 
                 
	/* 
1990/1004    
	 *  tell datakit we've rebooted. It should close all channels. 
1990/1101    
	 *  do this here to get it done before trying to open a channel. 
1990/1004    
	 */ 
	if(dp->restart) { 
1990/1101    
		DPRINT("dktimer: restart %s\n", dp->name); 
1990/1210    
		dp->closeall = 0; 
1990/1101    
		dkmesg(dp->csc, T_ALIVE, D_RESTART, 0, 0); 
1990/1004    
	} 
1992/0625    
	tsleep(&dp->closeallr, haveca, dp, 15000); 
1990/1004    
                 
	/* 
1990/1101    
	 *  start a keepalive process 
1990/0312    
	 */ 
1990/1101    
	sprint(buf, "timer.%s.%d", dp->name, dp->ncsc); 
	kproc(buf, dktimer, dp); 
1990/0312    
} 
                 
void 
dkreset(void) 
{ 
1990/0321    
	newqinfo(&dkmuxinfo); 
1990/0312    
} 
                 
void 
dkinit(void) 
{ 
} 
                 
Chan* 
dkattach(char *spec) 
{ 
	Chan *c; 
	Dk *dp; 
1992/0625    
	int dev; 
1990/0312    
                 
	/* 
1990/0722    
	 *  find a multiplexor with the same name (default dk) 
1990/0312    
	 */ 
1990/0722    
	if(*spec == 0) 
		spec = "dk"; 
1992/0625    
	for(dev = 0; dev < Maxdk; dev++){ 
		dp = dk[dev]; 
		if(dp && strcmp(dp->name, spec) == 0) 
			break; 
	} 
	if(dev == Maxdk) 
		error(Enoifc); 
1990/1101    
                 
	/* 
	 *  return the new channel 
	 */ 
1990/0312    
	c = devattach('k', spec); 
1992/0625    
	c->dev = dev; 
1990/0312    
	return c; 
} 
                 
Chan* 
dkclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
                 
int	  
dkwalk(Chan *c, char *name) 
{ 
1992/0625    
	return netwalk(c, name, &dk[c->dev]->net); 
1990/0312    
} 
                 
void	  
dkstat(Chan *c, char *dp) 
{ 
1992/0625    
	netstat(c, dp, &dk[c->dev]->net); 
1990/0312    
} 
                 
Chan* 
dkopen(Chan *c, int omode) 
{ 
1992/0623    
	Dk *dp; 
                 
1992/0625    
	dp = dk[c->dev]; 
1992/0623    
	linealloc(dp, STREAMID(c->qid.path), 1); 
	return netopen(c, omode, &dp->net); 
1990/0312    
} 
                 
void	  
dkcreate(Chan *c, char *name, int omode, ulong perm) 
{ 
1991/1214    
	USED(c); 
1992/0711    
	USED(name); 
	USED(omode); 
	USED(perm); 
1990/11211    
	error(Eperm); 
1990/0312    
} 
                 
void	  
dkclose(Chan *c) 
{ 
1990/0319    
	if(c->stream) 
1990/0312    
		streamclose(c); 
} 
                 
long	  
1991/0411    
dkread(Chan *c, void *a, long n, ulong offset) 
1990/0312    
{ 
1992/0625    
	return netread(c, a, n, offset, &dk[c->dev]->net); 
1990/0312    
} 
                 
long	  
1991/0411    
dkwrite(Chan *c, void *a, long n, ulong offset) 
1990/0312    
{ 
	int t; 
	char buf[256]; 
	char *field[5]; 
	int m; 
                 
1992/0711    
	USED(offset); 
1990/11211    
	t = STREAMTYPE(c->qid.path); 
1990/0312    
                 
	/* 
	 *  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){ 
1992/0130    
		if(n > sizeof buf - 1) 
			n = sizeof buf - 1; 
		strncpy(buf, a, n); 
		buf[n] = '\0'; 
1990/0312    
		m = getfields(buf, field, 5, ' '); 
		if(strcmp(field[0], "connect")==0){ 
			if(m < 2) 
1990/11211    
				error(Ebadarg); 
1990/0312    
			dkcall(Dial, c, field[1], 0, 0); 
		} else if(strcmp(field[0], "announce")==0){ 
			if(m < 2) 
1990/11211    
				error(Ebadarg); 
1990/0312    
			dkcall(Announce, c, field[1], 0, 0); 
		} else if(strcmp(field[0], "redial")==0){ 
			if(m < 4) 
1990/11211    
				error(Ebadarg); 
1990/0312    
			dkcall(Redial, c, field[1], field[2], field[3]); 
		} else if(strcmp(field[0], "accept")==0){ 
			if(m < 2) 
1990/11211    
				error(Ebadarg); 
1990/0312    
			dkanswer(c, strtoul(field[1], 0, 0), 0); 
		} else if(strcmp(field[0], "reject")==0){ 
			if(m < 3) 
1990/11211    
				error(Ebadarg); 
1991/1206    
			for(m = 0; m < DKERRS-1; m++) 
				if(strcmp(field[2], dkerr[m]) == 0) 
					break; 
			dkanswer(c, strtoul(field[1], 0, 0), m); 
1990/0312    
		} else 
			return streamwrite(c, a, n, 0); 
		return n; 
	} 
                 
1990/11211    
	error(Eperm); 
1992/0520    
	return -1;		/* never reached */ 
1990/0312    
} 
                 
void	  
dkremove(Chan *c) 
{ 
1991/1214    
	USED(c); 
1990/11211    
	error(Eperm); 
1990/0312    
} 
                 
void	  
dkwstat(Chan *c, char *dp) 
{ 
1992/0625    
	netwstat(c, dp, &dk[c->dev]->net); 
1990/0312    
} 
                 
/* 
1991/1108    
 *  return the number of an unused line (reserve it) 
 */ 
static int 
dkcloneline(Chan *c) 
{ 
	Line *lp; 
	Dk *dp; 
	int line; 
                 
1992/0625    
	dp = dk[c->dev]; 
1991/1108    
	/* 
	 *  get an unused device and open its control file 
	 */ 
1992/0623    
	qlock(&dp->netlock); 
1991/1108    
	for(line = dp->ncsc+1; line < dp->lines; line++){ 
		lp = dp->linep[line]; 
1992/0623    
		if(lp == 0 || lp->state == Lclosed){ 
			lp = linealloc(dp, line, 0); 
1991/1108    
			lp->state = Lopened; 
1991/1115    
                 
			/* current user becomes owner */ 
1993/0501    
			netown(lp, up->user, 0); 
1991/1115    
                 
1992/0623    
			qunlock(&dp->netlock); 
1991/1108    
			return lp->lineno; 
		} 
	} 
1992/0623    
	qunlock(&dp->netlock); 
1991/1108    
	error(Enodev); 
1992/0520    
	return -1;		/* never reached */ 
1991/1108    
} 
                 
1990/1101    
static Chan* 
dkopenline(Dk *dp, int line) 
{ 
	Chan *c; 
                 
	c = 0; 
	if(waserror()){ 
		if(c) 
			close(c); 
		nexterror(); 
	} 
	c = dkattach(dp->name); 
1990/11211    
	c->qid.path = STREAMQID(line, Sdataqid); 
1990/1101    
	dkopen(c, ORDWR); 
	poperror(); 
                 
	return c; 
} 
                 
/* 
1992/0625    
 *  open the common signalling channel (dp->csc's reference count never goes below 1) 
1991/1116    
 */ 
static Chan* 
dkopencsc(Dk *dp) 
{ 
1992/0623    
	qlock(&dp->csclock); 
1991/1116    
	if(dp->csc == 0) 
		dp->csc = dkopenline(dp, dp->ncsc); 
1992/0625    
	incref(dp->csc); 
1992/0623    
	qunlock(&dp->csclock); 
1991/1116    
	return dp->csc; 
} 
                 
/* 
1991/1108    
 *  return the contents of the info files 
 */ 
void 
dkfilladdr(Chan *c, char *buf, int len) 
{ 
1992/0717    
	if(len < sizeof(dk[0]->linep[0]->addr)+2) 
		error(Ebadarg); 
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->addr); 
1991/1108    
} 
void 
dkfillraddr(Chan *c, char *buf, int len) 
{ 
1992/0717    
	if(len < sizeof(dk[0]->linep[0]->raddr)+2) 
		error(Ebadarg); 
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->raddr); 
1991/1108    
} 
void 
dkfillruser(Chan *c, char *buf, int len) 
{ 
1992/0717    
	if(len < sizeof(dk[0]->linep[0]->ruser)+2) 
		error(Ebadarg); 
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->ruser); 
1991/1108    
} 
1992/0409    
void 
1992/0717    
dkfillstatus(Chan *c, char *buf, int len) 
1992/0409    
{ 
1992/0717    
	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); 
1992/0409    
} 
1991/1108    
                 
/* 
1990/0312    
 *  send a message to the datakit on the common signaling line 
 */ 
static int 
1990/1101    
dkmesg(Chan *c, int type, int srv, int p0, int p1) 
1990/0312    
{ 
	Dkmsg d; 
                 
1990/0911    
	if(waserror()){ 
		print("dkmesg: error\n"); 
1990/0312    
		return -1; 
1990/0911    
	} 
1990/0312    
	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; 
1990/1101    
	streamwrite(c, (char *)&d, sizeof(Dkmsg), 1); 
1990/0312    
	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]; 
1992/1030    
	int line, win; 
1990/0312    
	char dialtone; 
	int t_val, d_val; 
	Dk *dp; 
	Line *lp; 
	Chan *dc; 
1990/1101    
	Chan *csc; 
1990/0707    
	char *bang, *dot; 
1990/1101    
	                 
1990/11211    
	line = STREAMID(c->qid.path); 
1992/0625    
	dp = dk[c->dev]; 
1992/0623    
	lp = linealloc(dp, line, 1); 
1990/0312    
                 
	/* 
	 *  only dial on virgin lines 
	 */ 
1990/0511    
	if(lp->state != Lopened) 
1990/11211    
		error(Ebadarg); 
1990/0312    
                 
	DPRINT("dkcall(line=%d, type=%d, dest=%s)\n", line, type, addr); 
                 
	/* 
1990/0707    
	 *  build dial string 
	 *	- guard against new lines 
	 *	- change ! into . to delimit service 
1990/0312    
	 */ 
	if(strchr(addr, '\n')) 
1990/11211    
		error(Ebadarg); 
1993/0501    
	if(strlen(addr)+strlen(up->user)+2 >= sizeof(dialstr)) 
1990/11211    
		error(Ebadarg); 
1990/0312    
	strcpy(dialstr, addr); 
1990/0707    
	bang = strchr(dialstr, '!'); 
	if(bang){ 
		dot = strchr(dialstr, '.'); 
		if(dot==0 || dot > bang) 
			*bang = '.'; 
	} 
1990/0312    
	switch(type){ 
	case Dial: 
		t_val = T_SRV; 
		d_val = D_DIAL; 
		strcat(dialstr, "\n"); 
1993/0501    
		strcat(dialstr, up->user); 
1990/0312    
		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; 
1990/1214    
	default: 
		t_val = 0; 
		d_val = 0; 
		panic("bad dial type"); 
1990/0312    
	} 
                 
	/* 
1990/1214    
	 *  open the data file 
1990/0312    
	 */ 
1990/1214    
	dc = dkopenline(dp, line); 
1990/0312    
	if(waserror()){ 
1990/1214    
		close(dc); 
1990/0312    
		nexterror(); 
	} 
	lp->calltolive = 4; 
	lp->state = Ldialing; 
                 
	/* 
	 *  tell the controller we want to make a call 
	 */ 
	DPRINT("dialout\n"); 
1991/1116    
	csc = dkopencsc(dp); 
1990/1214    
	if(waserror()){ 
		close(csc); 
		nexterror(); 
	} 
1992/1030    
	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)); 
1990/1214    
	poperror(); 
1990/1101    
	close(csc); 
1990/0312    
                 
	/* 
	 *  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; 
1990/11211    
			error(Ebadarg); 
1990/0312    
		} 
	} 
                 
	/* 
	 *  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) 
1992/0111    
			error(dkerr[0]); 
1990/0312    
		else 
1992/0111    
			error(dkerr[lp->err]); 
1990/0312    
	} 
                 
	/* 
1990/0617    
	 *  change state if serving 
1990/0312    
	 */ 
	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; 
1991/1115    
	int from; 
1990/0312    
	Chan *dc; 
1992/0221    
	static int dts; 
1992/0717    
	char *cp; 
1990/0312    
                 
1992/0625    
	dp = dk[c->dev]; 
1991/1115    
	from = STREAMID(c->qid.path); 
1990/0312    
                 
	/* 
	 *  open the data file 
	 */ 
1990/11211    
	dc = dkopenline(dp, STREAMID(c->qid.path)); 
1990/0312    
	if(waserror()){ 
		close(dc); 
		nexterror(); 
	} 
                 
	/* 
	 *  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) 
1990/11211    
			error(Eio); 
1990/0312    
		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"); 
1990/11211    
			error(Eio); 
1990/0312    
		} 
                 
		/* 
		 * 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; 
		} 
1992/0623    
		lp = linealloc(dp, lineno, 1); 
1990/0312    
		ts = strtoul(field[1], 0, 0); 
                 
		/* 
		 *  this could be a duplicate request 
		 */ 
		if(ts == lp->timestamp){ 
1992/0221    
			if((dts++ % 1000) == 0) 
				print("dklisten: repeat timestamp %d\n", lineno); 
1990/0403    
			if(lp->state != Lconnected) 
				dkanswer(c, lineno, DKbusy); 
1990/0312    
			continue; 
		} 
	                 
		/* 
		 *  take care of glare (datakit picked an inuse channel 
		 *  for the call to come in on). 
		 */ 
		if(!canqlock(lp)){ 
1990/0315    
			DPRINT("DKbusy1\n"); 
1990/0312    
			dkanswer(c, lineno, DKbusy); 
			continue; 
		} else { 
			if(lp->state != Lclosed){ 
				qunlock(lp); 
1990/0315    
				DPRINT("DKbusy2 %ux\n", lp->state); 
1990/0312    
				dkanswer(c, lineno, DKbusy); 
				continue; 
			} 
		} 
		lp->window = window; 
                 
		/* 
		 *  Line 1 is `my-dk-name.service[.more-things]'. 
1992/0717    
		 *  Special characters are escaped by '\'s.  Convert to 
		 *  a plan 9 address, i.e. system!service. 
1990/0312    
		 */ 
		strncpy(lp->addr, line[1], sizeof(lp->addr)-1); 
1992/0717    
		if(cp = strchr(lp->addr, '.')){ 
			*cp = '!'; 
			if(cp = strchr(cp, '.')) 
				*cp = 0; 
		} 
1990/0312    
	                 
		/* 
		 *  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); 
1990/11211    
			error(Ebadarg); 
1990/0312    
		} 
                 
1991/1107    
		DPRINT("src(%s)user(%s)dest(%s)w(%d)\n", lp->raddr, lp->ruser, 
			lp->addr, W_TRAF(lp->window)); 
1990/0312    
                 
		lp->timestamp = ts; 
		lp->state = Lconnected; 
1991/1115    
                 
		/* listener becomes owner */ 
1992/0623    
		netown(lp, dp->linep[from]->owner, 0); 
1991/1115    
                 
1990/0312    
		qunlock(lp); 
		close(dc); 
		poperror(); 
		DPRINT("dklisten returns %d\n", lineno); 
		return lineno; 
	} 
1990/0511    
	panic("dklisten terminates strangely\n"); 
1992/0520    
	return -1;		/* never reached */ 
1990/0312    
} 
                 
/* 
 *  answer a call 
 */ 
static void 
dkanswer(Chan *c, int line, int code) 
{ 
	char reply[64]; 
	Chan *dc; 
	Line *lp; 
1992/0623    
	Dk *dp; 
1990/0312    
                 
1992/0625    
	dp = dk[c->dev]; 
1992/0623    
	lp = linealloc(dp, line, 1); 
1990/0312    
                 
	/* 
	 *  open the data file (c is a control file) 
	 */ 
	dc = dkattach(dp->name); 
	if(waserror()){ 
		close(dc); 
		nexterror(); 
	} 
1990/11211    
	dc->qid.path = STREAMQID(STREAMID(c->qid.path), Sdataqid); 
1990/0312    
	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(); 
1992/0409    
                 
	/* 
 	 *  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(); 
	} 
1990/0312    
} 
                 
/* 
 *  set the window size and reset the protocol 
 */ 
static void 
dkwindow(Chan *c) 
{ 
	char buf[64]; 
	Line *lp; 
                 
1992/0625    
	lp = linealloc(dk[c->dev], STREAMID(c->qid.path), 1); 
1990/0312    
	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; 
                 
1990/1101    
	dp = a; 
1990/0312    
                 
1990/1101    
	if(waserror()){ 
		close(dp->csc); 
		return; 
	} 
1991/1108    
	DPRINT("dkcsckproc: %d\n", dp->ncsc); 
1990/1101    
                 
1990/0312    
	/* 
	 *  loop forever listening 
	 */ 
	for(;;){ 
		n = streamread(dp->csc, (char *)&d, (long)sizeof(d)); 
		if(n != sizeof(d)){ 
1990/1101    
			if(n == 0) 
1990/11211    
				error(Ehungup); 
1990/0312    
			print("strange csc message %d\n", n); 
			continue; 
		} 
		line = (d.param0h<<8) + d.param0l; 
1991/1108    
		DPRINT("t(%d)s(%d)l(%d)\n", d.type, d.srv, line); 
1990/0312    
		switch (d.type) { 
                 
		case T_CHG:	/* controller wants to close a line */ 
1990/1101    
			dkchgmesg(dp->csc, dp, &d, line); 
1990/0312    
			break; 
		                 
		case T_REPLY:	/* reply to a dial request */ 
			dkreplymesg(dp, &d, line); 
			break; 
		                 
		case T_SRV:	/* ignore it, it's useless */ 
1991/0723    
/*			print("dksrvmesg(%d)\n", line);		/**/ 
1990/0312    
			break; 
		                 
		case T_RESTART:	/* datakit reboot */ 
1990/1101    
			if(line >=0 && line<dp->lines) 
1991/0828    
				dp->lines = line+1; 
1990/0312    
			break; 
		                 
		default: 
1991/0706    
			print("unrecognized csc message %o.%o(%o)\n", 
				d.type, d.srv, line); 
1990/0312    
			break; 
		} 
	} 
} 
                 
/* 
 *  datakit requests or confirms closing a line 
 */ 
static void 
1990/1101    
dkchgmesg(Chan *c, Dk *dp, Dkmsg *dialp, int line) 
1990/0312    
{ 
	Line *lp; 
                 
	switch (dialp->srv) { 
                 
	case D_CLOSE:		/* remote shutdown */ 
1992/0623    
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0) { 
1990/1022    
			/* tell controller this line is not in use */ 
1990/1101    
			dkmesg(c, T_CHG, D_CLOSE, line, 0); 
1990/1022    
			return; 
		} 
1990/0312    
		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: 
1990/1101    
			dkmesg(c, T_CHG, D_CLOSE, line, 0); 
1990/0312    
			break; 
                 
		case Llclose: 
		case Lclosed: 
			dkhangup(lp); 
1990/1101    
			dkmesg(c, T_CHG, D_CLOSE, line, 0); 
1990/0312    
			lp->state = Lclosed; 
			break; 
		} 
		break; 
	                 
	case D_ISCLOSED:	/* acknowledging a local shutdown */ 
1992/0625    
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0) 
1990/1022    
			return; 
1990/0312    
		switch (lp->state) { 
		case Llclose: 
		case Lclosed: 
			lp->state = Lclosed; 
			break; 
                 
		case Lrclose: 
		case Lconnected: 
		case Llistening: 
		case Lackwait: 
			break; 
		} 
		break; 
                 
1990/1022    
	case D_CLOSEALL: 
1990/1101    
		/* 
		 *  datakit wants us to close all lines 
		 */ 
1990/1022    
		for(line = dp->ncsc+1; line < dp->lines; line++){ 
1991/1107    
			lp = dp->linep[line]; 
1992/0623    
			if(lp == 0) 
				continue; 
1990/1022    
			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: 
				lp->state = Lrclose; 
1990/1101    
				dkhangup(lp); 
1990/1022    
				break; 
	                 
			case Lopened: 
				break; 
	                 
			case Llclose: 
			case Lclosed: 
				lp->state = Lclosed; 
				break; 
			} 
		} 
1990/1210    
		dp->closeall = 1; 
		wakeup(&dp->closeallr); 
1990/1022    
		break; 
                 
1990/0312    
	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) 
{ 
	Line *lp; 
                 
	DPRINT("dkreplymesg(%d)\n", line); 
                 
1992/0623    
	if(line < 0 || line >= dp->lines || (lp = dp->linep[line]) == 0) 
1990/0312    
		return; 
                 
	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); 
} 
                 
/* 
1990/1101    
 *  send a I'm alive message every 7.5 seconds and remind the dk of 
 *  any closed channels it hasn't acknowledged. 
1990/0312    
 */ 
static void 
dktimer(void *a) 
{ 
1992/0711    
	int i; 
1990/0312    
	Dk *dp; 
	Line *lp; 
1990/1101    
	Chan *c; 
1990/0312    
                 
1990/1214    
	dp = (Dk *)a; 
1991/1116    
	c = dkopencsc(dp); 
1990/1101    
                 
1992/0623    
	while(waserror()); 
1990/1101    
                 
1992/0623    
	for(;;){ 
1990/0312    
		/* 
1990/1101    
		 * send keep alive 
1990/0312    
		 */ 
1990/11161    
		DPRINT("keep alive\n"); 
1990/1101    
		dkmesg(c, T_ALIVE, D_CONTINUE, 0, 0); 
1990/0312    
                 
1990/1101    
		/* 
		 *  remind controller of dead lines and 
		 *  timeout calls that take to long 
		 */ 
		for (i=dp->ncsc+1; i<dp->lines; i++){ 
1991/1107    
			lp = dp->linep[i]; 
1992/0623    
			if(lp == 0) 
				continue; 
1990/1101    
			switch(lp->state){ 
			case Llclose: 
				dkmesg(c, T_CHG, D_CLOSE, i, 0); 
				break; 
1990/0312    
                 
1990/1101    
			case Ldialing: 
				if(lp->calltolive==0 || --lp->calltolive!=0) 
1990/0312    
					break; 
1990/1101    
				dkreplymesg(dp, (Dkmsg *)0, i); 
				break; 
1990/0312    
			} 
		} 
1990/1101    
		tsleep(&dp->timer, return0, 0, 7500); 
1990/0312    
	} 
} 


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