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

1991/0419/gnot/devincon.c (diff list | history)

gnot/devincon.c on 1990/0312
1990/0312    
#include	"u.h" 
#include	"lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"errno.h" 
#include	"devtab.h" 
 
#include	"io.h" 
1990/0320    
#include	"ureg.h" 
1990/0312    
 
typedef struct Incon	Incon; 
typedef struct Device	Device; 
 
#define NOW (MACHP(0)->ticks*MS2HZ) 
 
1990/1214    
#define MICROSECOND USED(NOW) 
1990/0315    
 
1990/0312    
#define DPRINT if(0) 
 
enum { 
	Minstation=	2,	/* lowest station # to poll */ 
	Maxstation=	15,	/* highest station # to poll */ 
	Nincon=		1,	/* number of incons */ 
1990/0322    
	Nin=		32,	/* Blocks in the input ring */ 
1990/0320    
	Bsize=		128,	/* size of an input ring block */ 
1990/0321    
	Mfifo=		0xff	/* a mask, must be 2^n-1, must be > Nin */ 
1990/0312    
}; 
 
/* 
 *  incon datakit board 
 */ 
struct Device { 
	uchar	cdata; 
#define	cpolln	cdata 
	uchar	u0; 
	uchar	cstatus; 
	uchar	u1; 
	uchar	creset; 
	uchar	u2; 
	uchar	csend; 
	uchar	u3; 
	ushort	data_cntl;	/* data is high byte, cntl is low byte */ 
	uchar	status; 
#define cmd	status 
	uchar	u5; 
	uchar	reset; 
	uchar	u6; 
	uchar	send; 
	uchar	u7; 
}; 
#define	INCON	((Device *)0x40700000) 
 
struct Incon { 
	QLock; 
 
	QLock	xmit;		/* transmit lock */ 
	QLock	reslock;	/* reset lock */ 
	Device	*dev; 
	int	station;	/* station number */ 
	int	state;		/* chip state */ 
	Rendez	r;		/* output process */ 
	Rendez	kr;		/* input kernel process */ 
	ushort	chan;		/* current input channel */ 
	Queue	*rq;		/* read queue */ 
	int	kstarted;	/* true if kernel process started */ 
 
1990/0315    
	/*  input blocks */ 
1990/0312    
 
1990/0315    
	Block	*inb[Nin]; 
	ushort	wi;		 
	ushort	ri; 
 
1990/0312    
	/* statistics */ 
 
	ulong	overflow;	/* overflow errors */ 
	ulong	pack0;		/* channel 0 */ 
	ulong	crc;		/* crc errors */ 
	ulong	in;		/* bytes in */ 
	ulong	out;		/* bytes out */ 
}; 
 
Incon incon[Nincon]; 
 
/* 
 *  chip state 
 */ 
enum { 
	Selecting, 
	Selected, 
1990/0725    
	Notliving, 
1990/0312    
}; 
 
/* 
 *  internal chip registers 
 */ 
#define	sel_polln	0 
#define	sel_station	1 
#define	sel_poll0	2 
#define sel_rcv_cnt	3 
#define sel_rcv_tim	4 
#define sel_tx_cnt	5 
 
/* 
 *  CSR bits 
 */ 
#define INCON_RUN	0x80 
#define INCON_STOP	0x00 
#define ENABLE_IRQ	0x40 
#define ENABLE_TX_IRQ	0x20 
#define INCON_ALIVE	0x80 
#define TX_FULL		0x10 
#define TX_EMPTY	0x08 
#define RCV_EMPTY	0x04 
#define OVERFLOW	0x02 
#define CRC_ERROR	0x01 
 
/* 
 *  polling constants 
 */ 
#define HT_GNOT	0x30 
#define ST_UNIX 0x04 
#define NCHAN 16 
 
static void inconkproc(void*); 
 
/* 
 *  incon stream module definition 
 */ 
static void inconoput(Queue*, Block*); 
static void inconstopen(Queue*, Stream*); 
static void inconstclose(Queue*); 
1990/11151    
Qinfo inconinfo = 
{ 
	nullput, 
	inconoput, 
	inconstopen, 
	inconstclose, 
	"incon" 
}; 
1990/0312    
 
1990/0629    
int incondebug; 
 
1990/0312    
/* 
 *  set the incon parameters 
 */ 
void 
1990/0315    
inconset(Incon *ip, int cnt, int del) 
1990/0312    
{ 
	Device *dev; 
 
1990/0315    
	if (cnt<1 || cnt>14 || del<1 || del>15) 
1990/11211    
		error(Ebadarg); 
1990/0312    
 
	dev = ip->dev; 
	dev->cmd = sel_rcv_cnt | INCON_RUN; 
1990/0315    
	MICROSECOND; 
1990/0312    
	*(uchar *)&dev->data_cntl = cnt; 
1990/0315    
	MICROSECOND; 
1990/0312    
	dev->cmd = sel_rcv_tim | INCON_RUN; 
1990/0315    
	MICROSECOND; 
	*(uchar *)&dev->data_cntl = del; 
	MICROSECOND; 
1990/0312    
	dev->cmd = INCON_RUN | ENABLE_IRQ; 
} 
 
1990/1101    
/* 
 *  parse a set request 
 */ 
void 
inconsetctl(Incon *ip, Block *bp) 
{ 
	char *field[3]; 
	int n; 
	int del; 
	int cnt; 
 
	del = 15; 
	n = getfields((char *)bp->rptr, field, 3, ' '); 
	switch(n){ 
	default: 
		freeb(bp); 
1990/11211    
		error(Ebadarg); 
1990/1101    
	case 2: 
		del = strtol(field[1], 0, 0); 
		if(del<0 || del>15){ 
			freeb(bp); 
1990/11211    
			error(Ebadarg); 
1990/1101    
		} 
		/* fall through */ 
	case 1: 
		cnt = strtol(field[0], 0, 0); 
		if(cnt<0 || cnt>15){ 
			freeb(bp); 
1990/11211    
			error(Ebadarg); 
1990/1101    
		} 
	} 
	inconset(ip, cnt, del); 
	freeb(bp); 
} 
 
1990/0312    
static void 
nop(void) 
{ 
} 
 
/* 
 *  poll for a station number 
 */ 
void  
inconpoll(Incon *ip, int station) 
{ 
	ulong timer; 
	Device *dev; 
 
	dev = ip->dev; 
 
	/* 
	 *  get us to a known state 
	 */ 
1990/0725    
	ip->state = Notliving; 
1990/0312    
	dev->cmd = INCON_STOP; 
 
	/* 
	 * try a station number 
	 */ 
	dev->cmd = sel_station; 
	*(uchar *)&dev->data_cntl = station; 
	dev->cmd = sel_poll0; 
	*(uchar *)&dev->data_cntl = HT_GNOT; 
	dev->cmd = sel_rcv_cnt; 
	*(uchar *)&dev->data_cntl = 3; 
	dev->cmd = sel_rcv_tim; 
	*(uchar *)&dev->data_cntl = 15; 
	dev->cmd = sel_tx_cnt; 
	*(uchar *)&dev->data_cntl = 1; 
	dev->cmd = sel_polln; 
	*(uchar *)&dev->data_cntl = 0x00; 
	*(uchar *)&dev->data_cntl = ST_UNIX; 
	*(uchar *)&dev->data_cntl = NCHAN; 
	*(uchar *)&dev->data_cntl = 'g'; 
	*(uchar *)&dev->data_cntl = 'n'; 
	*(uchar *)&dev->data_cntl = 'o'; 
	*(uchar *)&dev->data_cntl = 't'; 
	dev->cpolln = 0; 
 
	/* 
	 *  poll and wait for ready (or 1/4 second) 
	 */ 
	ip->state = Selecting; 
	dev->cmd = INCON_RUN | ENABLE_IRQ; 
	timer = NOW + 250; 
	while (NOW < timer) { 
		nop(); 
		if(dev->status & INCON_ALIVE){ 
			ip->station = station; 
			ip->state = Selected; 
			break; 
		} 
	} 
} 
 
/* 
 *  reset the chip and find a new staion number 
 */ 
void 
inconrestart(Incon *ip) 
{ 
	Device *dev; 
	int i; 
 
	if(!canqlock(&ip->reslock)) 
		return; 
 
1990/0623    
	print("inconrestart\n"); 
 
1990/0312    
	/* 
	 *  poll for incon station numbers 
	 */ 
	for(i = Minstation; i <= Maxstation; i++){ 
		inconpoll(ip, i); 
		if(ip->state == Selected) 
			break; 
	} 
	switch(ip->state) { 
	case Selecting: 
		print("incon[%d] not polled\n", ip-incon); 
		break; 
	case Selected: 
		print("incon[%d] station %d\n", ip-incon, ip->station); 
1990/0331    
		inconset(ip, 3, 15); 
1990/0312    
		break; 
	default: 
		print("incon[%d] bollixed\n", ip-incon); 
		break; 
	} 
	qunlock(&ip->reslock); 
} 
 
/* 
 *  reset all incon chips. 
 */ 
void 
inconreset(void) 
{ 
	int i; 
	Incon *ip; 
 
	incon[0].dev = INCON; 
	incon[0].state = Selected; 
1990/0315    
	incon[0].ri = incon[0].wi = 0; 
1990/0331    
/*	inconset(&incon[0], 3, 15); /**/ 
1990/0312    
	for(i=1; i<Nincon; i++){ 
		incon[i].dev = INCON+i; 
1990/0725    
		incon[i].state = Notliving; 
1990/0312    
		incon[i].dev->cmd = INCON_STOP; 
1990/0315    
		incon[i].ri = incon[i].wi = 0; 
1990/0312    
	} 
} 
 
void 
inconinit(void) 
{ 
} 
 
/* 
 *  enable the device for interrupts, spec is the device number 
 */ 
Chan* 
inconattach(char *spec) 
{ 
	Incon *ip; 
	int i; 
	Chan *c; 
 
	i = strtoul(spec, 0, 0); 
	if(i >= Nincon) 
1990/11211    
		error(Ebadarg); 
1990/0312    
	ip = &incon[i]; 
	if(ip->state != Selected) 
		inconrestart(ip); 
 
	c = devattach('i', spec); 
	c->dev = i; 
1990/11211    
	c->qid.path = CHDIR; 
	c->qid.vers = 0; 
1990/0312    
	return c; 
} 
 
Chan* 
inconclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
 
int	  
inconwalk(Chan *c, char *name) 
{ 
	return devwalk(c, name, 0, 0, streamgen); 
} 
 
1991/0419    
Chan* 
inconclwalk(Chan *c, char *name) 
{ 
	return devclwalk(c, name); 
} 
 
1990/0312    
void	  
inconstat(Chan *c, char *dp) 
{ 
	devstat(c, dp, 0, 0, streamgen); 
} 
 
Chan* 
inconopen(Chan *c, int omode) 
{ 
1990/11211    
	if(c->qid.path == CHDIR){ 
1990/0312    
		if(omode != OREAD) 
1990/11211    
			error(Eperm); 
1990/0312    
	}else 
		streamopen(c, &inconinfo); 
	c->mode = openmode(omode); 
	c->flag |= COPEN; 
	c->offset = 0; 
	return c; 
} 
 
void	  
inconcreate(Chan *c, char *name, int omode, ulong perm) 
{ 
1990/11211    
	error(Eperm); 
1990/0312    
} 
 
void	  
inconclose(Chan *c) 
{ 
1990/11211    
	if(c->qid.path != CHDIR) 
1990/0312    
		streamclose(c); 
} 
 
long	  
1991/0411    
inconread(Chan *c, void *buf, long n, ulong offset) 
1990/0312    
{ 
	return streamread(c, buf, n); 
} 
 
long	  
1991/0411    
inconwrite(Chan *c, void *buf, long n, ulong offset) 
1990/0312    
{ 
	return streamwrite(c, buf, n, 0); 
} 
 
void	  
inconremove(Chan *c) 
{ 
1990/11211    
	error(Eperm); 
1990/0312    
} 
 
void	  
inconwstat(Chan *c, char *dp) 
{ 
1990/11211    
	error(Eperm); 
1990/0312    
} 
 
/* 
 *	the stream routines 
 */ 
 
/* 
 *  create the kernel process for input 
 */ 
static void 
inconstopen(Queue *q, Stream *s) 
{ 
	Incon *ip; 
	char name[32]; 
 
	ip = &incon[s->dev]; 
1990/0725    
	sprint(name, "incon%d", s->dev); 
1990/0312    
	q->ptr = q->other->ptr = ip; 
	ip->rq = q; 
	kproc(name, inconkproc, ip); 
} 
 
/* 
 *  kill off the kernel process 
 */ 
static int 
1990/0725    
kNotliving(void *arg) 
1990/0312    
{ 
	Incon *ip; 
 
	ip = (Incon *)arg; 
	return ip->kstarted == 0; 
} 
static void 
inconstclose(Queue * q) 
{ 
	Incon *ip; 
 
	ip = (Incon *)q->ptr; 
	qlock(ip); 
	ip->rq = 0; 
	qunlock(ip); 
	wakeup(&ip->kr); 
1990/0725    
	sleep(&ip->r, kNotliving, ip); 
1990/0312    
} 
 
/* 
 *  free all blocks of a message in `q', `bp' is the first block 
 *  of the message 
 */ 
static void 
freemsg(Queue *q, Block *bp) 
{ 
	for(; bp; bp = getq(q)){ 
		if(bp->flags & S_DELIM){ 
			freeb(bp); 
			return; 
		} 
		freeb(bp); 
	} 
} 
 
/* 
 *  output a block 
 * 
 *  the first 2 bytes of every message are the channel number, 
 *  low order byte first.  the third is a possible trailing control 
 *  character. 
 */ 
void 
inconoput(Queue *q, Block *bp) 
{ 
	Device *dev; 
	Incon *ip; 
	ulong end; 
	int chan; 
	int ctl; 
	int n, size; 
 
1990/1101    
	ip = (Incon *)q->ptr; 
 
1990/0312    
	if(bp->type != M_DATA){ 
1990/1101    
		if(streamparse("inconset", bp)) 
			inconsetctl(ip, bp); 
		else 
			freeb(bp); 
1990/0312    
		return; 
	} 
 
	/* 
	 *  get a whole message before handing bytes to the device 
	 */ 
	if(!putq(q, bp)) 
		return; 
 
	/* 
	 *  one transmitter at a time 
	 */ 
	qlock(&ip->xmit); 
	dev = ip->dev; 
 
	/* 
	 *  parse message 
	 */ 
	bp = getq(q); 
	if(bp->wptr - bp->rptr < 3){ 
		freemsg(q, bp); 
		qunlock(&ip->xmit); 
		return; 
	} 
	chan = bp->rptr[0] | (bp->rptr[1]<<8); 
	ctl = bp->rptr[2]; 
	bp->rptr += 3; 
1990/0802    
	if(chan<=0) 
		print("bad channel %d\n", chan); 
1990/0312    
 
1990/0629    
	if(incondebug) 
		print("->(%d)%uo %d\n", chan, ctl, bp->wptr - bp->rptr); 
 
1990/0312    
	/* 
	 *  make sure there's an incon out there 
	 */ 
1990/0725    
	if(!(dev->status&INCON_ALIVE) || ip->state==Notliving){ 
1990/0312    
		inconrestart(ip); 
		freemsg(q, bp); 
		qunlock(&ip->xmit); 
		return; 
	} 
 
	/* 
	 *  send the 8 bit data 
	 */ 
	for(;;){ 
		/* 
		 *  spin till there is room 
		 */ 
		for(end = NOW+1000; dev->status & TX_FULL;){ 
			nop();	/* make sure we don't optimize too much */ 
			if(NOW > end){ 
				print("incon output stuck\n"); 
				freemsg(q, bp); 
				qunlock(&ip->xmit); 
				return; 
			} 
		} 
 
		/* 
		 *  put in next packet 
		 */ 
		n = bp->wptr - bp->rptr; 
		if(n > 16) 
			n = 16; 
		size = n; 
		dev->cdata = chan; 
		while(n--){ 
			*(uchar *)&dev->data_cntl = *bp->rptr++; 
		} 
 
		/* 
		 *  get next block  
		 */ 
		if(bp->rptr >= bp->wptr){ 
			if(bp->flags & S_DELIM){ 
				freeb(bp); 
				break; 
			} 
			freeb(bp); 
			bp = getq(q); 
			if(bp==0) 
				break; 
		} 
 
		/* 
		 *  end packet 
		 */ 
		dev->cdata = 0; 
	} 
 
	/* 
	 *  send the control byte if there is one 
	 */ 
	if(ctl){ 
		if(size >= 16){ 
			dev->cdata = 0; 
1990/0320    
			MICROSECOND; 
1990/1212    
			for(end = NOW+1000; dev->status & TX_FULL;){ 
				nop();	/* make sure we don't optimize too much */ 
				if(NOW > end){ 
					print("incon output stuck\n"); 
					freemsg(q, bp); 
					qunlock(&ip->xmit); 
					return; 
				} 
			} 
1990/0312    
			dev->cdata = chan; 
1990/0320    
			MICROSECOND; 
1990/0312    
		} 
1990/1212    
		if(dev->status & TX_FULL) 
			print("inconfull\n"); 
1990/0312    
		dev->cdata = ctl; 
	} 
1990/0320    
	MICROSECOND; 
1990/0312    
	dev->cdata = 0; 
 
	qunlock(&ip->xmit); 
	return; 
} 
 
/* 
 *  return true if the raw fifo is non-empty 
 */ 
static int 
notempty(void *arg) 
{ 
	Incon *ip; 
 
	ip = (Incon *)arg; 
1990/0315    
	return ip->ri!=ip->wi; 
1990/0312    
} 
 
/* 
 *  Read bytes from the raw input circular buffer. 
 */ 
static void 
inconkproc(void *arg) 
{ 
	Incon *ip; 
1990/0322    
	Block *bp, *nbp; 
1990/1214    
	int i; 
1990/1101    
	int locked; 
1990/0312    
 
	ip = (Incon *)arg; 
	ip->kstarted = 1; 
 
1990/0315    
	/* 
	 *  create a number of blocks for input 
	 */ 
	for(i = 0; i < Nin; i++){ 
1990/0320    
		bp = ip->inb[i] = allocb(Bsize); 
1990/0315    
		bp->wptr += 3; 
	} 
 
1990/1101    
	locked = 0; 
	if(waserror()){ 
		if(locked) 
			qunlock(ip); 
		ip->kstarted = 0; 
		wakeup(&ip->r); 
		return; 
	} 
1990/1024    
 
1990/0312    
	for(;;){ 
		/* 
1990/0315    
		 *  sleep if input fifo empty 
		 */ 
		sleep(&ip->kr, notempty, ip); 
 
		/* 
1990/0312    
		 *  die if the device is closed 
		 */ 
1990/1214    
		USED(locked); 
1990/0312    
		qlock(ip); 
1990/1214    
		locked = 1; 
1990/0312    
		if(ip->rq == 0){ 
			qunlock(ip); 
			ip->kstarted = 0; 
			wakeup(&ip->r); 
1990/1214    
			poperror(); 
1990/0312    
			return; 
		} 
 
		/* 
1990/0322    
		 *  send blocks upstream and stage new blocks.  if the block is small 
		 *  (< 64 bytes) copy into a smaller buffer. 
1990/0312    
		 */ 
1990/0315    
		while(ip->ri != ip->wi){ 
1990/0322    
			bp = ip->inb[ip->ri]; 
1990/0331    
			PUTNEXT(ip->rq, bp); 
1990/0320    
			bp = ip->inb[ip->ri] = allocb(Bsize); 
1990/0315    
			bp->wptr += 3; 
			ip->ri = (ip->ri+1)%Nin; 
		} 
1990/1214    
		USED(locked); 
1990/0315    
		qunlock(ip); 
1990/1101    
		locked = 0; 
1990/0315    
	} 
} 
1990/0312    
 
1990/0315    
/* 
1990/0321    
 *  drop a single packet 
1990/0315    
 */ 
static void 
droppacket(Device *dev) 
{ 
	int i; 
	int c; 
 
1990/0321    
	for(i = 0; i < 17; i++){ 
		c = dev->data_cntl; 
		if(c==0) 
			break; 
1990/0315    
	} 
} 
 
/* 
1990/0321    
 *  flush the input fifo 
 */ 
static void 
flushfifo(Device *dev) 
{ 
	while(!(dev->status & RCV_EMPTY)) 
		droppacket(dev); 
} 
 
/* 
1990/0315    
 *  advance the queue. if we've run out of staged input blocks, 
 *  drop the packet and return 0.  otherwise return the next input 
 *  block to fill. 
 */ 
static Block * 
nextin(Incon *ip, unsigned int c) 
{ 
1990/0321    
	Block *bp; 
1990/0320    
	int next; 
1990/0315    
 
1990/0321    
	bp = ip->inb[ip->wi]; 
1990/0315    
	bp->base[0] = ip->chan; 
	bp->base[1] = ip->chan>>8; 
	bp->base[2] = c; 
1990/0629    
	if(incondebug) 
1990/0731    
		print("<-(%d)%uo %d\n", ip->chan, c, bp->wptr-bp->rptr); 
1990/0315    
 
1990/0731    
	next = (ip->wi+1)%Nin; 
1990/0321    
	if(next == ip->ri){ 
		bp->wptr = bp->base+3; 
		return bp; 
	} 
1990/0731    
	ip->wi = next; 
1990/0321    
 
1990/0315    
	return ip->inb[ip->wi]; 
} 
 
/* 
 *  read the packets from the device into the staged input blocks. 
 *  we have to do this at interrupt tevel to turn off the interrupts. 
 */ 
static void 
rdpackets(Incon *ip) 
{ 
	Block *bp; 
	unsigned int c; 
	Device *dev; 
1990/0320    
	uchar *p; 
	int first = ip->wi; 
1990/0315    
 
	dev = ip->dev; 
1990/0320    
	bp = ip->inb[ip->wi]; 
	if(bp==0){ 
1990/0321    
		flushfifo(ip->dev); 
		return; 
1990/0320    
	} 
	p = bp->wptr; 
1990/0315    
	while(!(dev->status & RCV_EMPTY)){ 
1990/0312    
		/* 
		 *  get channel number 
		 */ 
1990/0315    
		c = (dev->data_cntl)>>8; 
1990/0321    
		if(c == 0){ 
			droppacket(dev); 
			continue; 
		} 
1990/0312    
		if(ip->chan != c){ 
1990/0320    
			if(p - bp->rptr > 3){ 
				bp->wptr = p; 
1990/0315    
				bp = nextin(ip, 0); 
1990/0320    
				p = bp->wptr; 
1990/0315    
			} 
1990/0312    
			ip->chan = c; 
		} 
 
		/* 
		 *  null byte marks end of packet 
		 */ 
1990/0315    
		for(;;){ 
			if((c=dev->data_cntl)&1) { 
1990/0312    
				/* 
				 *  data byte, put in local buffer 
				 */ 
1990/0320    
				*p++ = c>>8; 
1990/0312    
			} else if (c>>=8) { 
				/* 
				 *  control byte ends block 
				 */ 
1990/0320    
				bp->wptr = p; 
1990/0315    
				bp = nextin(ip, c); 
1990/0320    
				p = bp->wptr; 
1990/0312    
			} else { 
				/* end of packet */ 
				break; 
			} 
		} 
1990/0320    
 
		/* 
		 *  pass a block on if it doesn't have room for one more 
		 *  packet.  this way we don't have to check per byte. 
		 */ 
		if(p + 16 > bp->lim){ 
			bp->wptr = p; 
			bp = nextin(ip, 0); 
			p = bp->wptr; 
		} 
	}	 
	bp->wptr = p; 
1990/05312    
	if(bp->wptr != bp->base+3) 
		nextin(ip, 0); 
1990/0320    
 
	if(first != ip->wi)/**/ 
		wakeup(&ip->kr); 
1990/0312    
} 
 
/* 
 *  Receive an incon interrupt.  One entry point 
 *  for all types of interrupt.  Until we figure out 
 *  how to use more than one incon, this routine only 
 *  is for incon[0]. 
 */ 
inconintr(Ureg *ur) 
{ 
	uchar status; 
	Incon *ip; 
 
	ip = &incon[0]; 
 
	status = ip->dev->status; 
	if(!(status & RCV_EMPTY)) 
		rdpackets(ip); 
 
	/* check for exceptional conditions */ 
	if(status&(OVERFLOW|CRC_ERROR)){ 
1990/0619    
		if(status&OVERFLOW) 
1990/0312    
			ip->overflow++; 
1990/0619    
		if(status&CRC_ERROR) 
1990/0312    
			ip->crc++; 
	} 
 
	/* see if it died underneath us */ 
	if(!(status&INCON_ALIVE)){ 
		switch(ip->state){ 
		case Selected: 
			ip->dev->cmd = INCON_STOP; 
			print("Incon died\n"); 
			break; 
		case Selecting: 
			print("rejected\n"); 
			break; 
		default: 
			ip->dev->cmd = INCON_STOP; 
			break; 
		} 
1990/0725    
		ip->state = Notliving; 
1990/0312    
	} 
1990/0629    
} 
 
1991/0115    
void 
incontoggle(void) 
1990/0629    
{ 
	incondebug ^= 1; 
1990/0312    
} 


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