~kris/9p

9hist

a6f1bb6e2ea229e3fa3289ad6ed94acf1ba6f4ad — David du Colombier 33 years ago bd91522
Plan 9 from Bell Labs 1993-08-06
12 files changed, 624 insertions(+), 2444 deletions(-)

M port/devcons.c
M port/devlance.c
M port/portdat.h
M power/dat.h
D power/debugger.c
M power/devduart.c
D power/devhs.c
M power/fns.h
D power/ipdat.h
M power/main.c
M power/mmu.c
M power/trap.c
M port/devcons.c => port/devcons.c +7 -4
@@ 47,9 47,9 @@ printinit(void)
void
putstrn(char *str, int n)
{
	char buf[PRINTSIZE+2];
	int m;
	char *t;
	char buf[PRINTSIZE+2];

	/*
	 *  if there's an attached bit mapped display,


@@ 66,9 66,10 @@ putstrn(char *str, int n)
	 */
	if(printq == 0)
		return;
	while(n > 0){

	while(n > 0) {
		t = memchr(str, '\n', n);
		if(t){
		if(t) {
			m = t - str;
			memmove(buf, str, m);
			buf[m] = '\r';


@@ 76,7 77,8 @@ putstrn(char *str, int n)
			qwrite(printq, buf, m+2, 1);
			str = t + 1;
			n -= m + 1;
		} else {
		}
		else {
			qwrite(printq, str, n, 1);
			break;
		}


@@ 97,6 99,7 @@ print(char *fmt, ...)

	n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
	putstrn(buf, n);

	return n;
}


M port/devlance.c => port/devlance.c +224 -619
@@ 5,11 5,11 @@
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"
#include	"devtab.h"
#include	"../port/netif.h"

enum {
	Ntypes=		9,		/* max number of ethernet packet types */
	Maxrb=		128,		/* max buffers in a ring */
	Ntypes=		9,	/* max number of ethernet packet types */
	Maxrb=		128,	/* max buffers in a ring */
};
#define RSUCC(x) (((x)+1)%l.nrrb)
#define TSUCC(x) (((x)+1)%l.ntrb)


@@ 23,7 23,7 @@ typedef struct {
	ushort	laddr;		/* low order piece of address */
	ushort	flags;		/* flags and high order piece of address */
	short	size;		/* size of buffer */
	ushort	cntflags;	/* (rcv)count of bytes in buffer; (xmt) more flags */
	ushort	cntflags;	/* (rcv)count of buffer; (xmt) more flags */
} Msg;

/*


@@ 69,386 69,89 @@ struct Lancemem
#define MPs(a) (*(short *)(l.lanceram + l.sep*((ushort*)&a - (ushort*)0)))
#define MPus(a) (*(ushort *)(l.lanceram + l.sep*((ushort*)&a - (ushort*)0)))

/*
 *  one per ethernet packet type
 */
typedef struct Ethertype	Ethertype;
struct Ethertype
enum
{
	QLock;
	Netprot;			/* stat info */
	int		type;		/* ethernet type */
	int		prom;		/* promiscuous mode */
	Queue		*q;
	int		inuse;
	Rendez		rc;		/* rendzvous for close */
	Ethertype	*closeline;	/* close list */
	PROM	= 0x8000,
	INTL	= 0x40,
	DRTY	= 0x20,
	COLL	= 0x10,
	DTCR	= 0x8,
	LOOP	= 0x4,
	DTX	= 0x2,
	DRX	= 0x1,
	ERR0	= 0x8000,
	BABL	= 0x4000,
	CERR	= 0x2000,
	MISS	= 0x1000,
	MERR	= 0x800,
	RINT	= 0x400,
	TINT	= 0x200,
	IDON	= 0x100,
	INTR	= 0x80,
	INEA	= 0x40,
	RXON	= 0x20,
	TXON	= 0x10,
	TDMD	= 0x8,
	STOP	= 0x4,
	STRT	= 0x2,
	INIT	= 0x1,
	/* flag bits from a buffer descriptor in the rcv/xmt rings */
	LANCEOWNER= 0x8000,	/* 1 means buffer can be used by the chip */
	ERR	= 0x4000,	/* error summary, the OR of all error bits */
	FRAM	= 0x2000,	/* CRC error */
	OFLO	= 0x1000,	/* (receive) lost some of the packet */
	MORE	= 0x1000,	/* (transmit) more than 1 retry to tx pkt */
	CRC	= 0x800,	/* (rcv) crc error reading packet */
	ONE	= 0x800,	/* (tx) one retry to transmit the packet */
	BUF	= 0x400,	/* (rcv) out of buffers */
	DEF	= 0x400,	/* (tx) deffered while transmitting packet */
	STP	= 0x200,	/* start of packet */
	ENP	= 0x100,	/* end of packet */
	/* cntflags bits from a buffer descriptor in the rcv/xmt rings */
	BUFF	= 0x8000,	/* buffer error (host screwed up?) */
	UFLO	= 0x4000,	/* underflow from memory */
	LCOL	= 0x1000,	/* late collision (ether too long?) */
	LCAR	= 0x800,	/* loss of carrier (ether broken?) */
	RTRY	= 0x400,	/* couldn't transmit (ether jammed?) */
	TTDR	= 0x3FF,	/* time domain reflectometer */
};

struct Softlance
{
	Lance;			/* host dependent lance params */

/*
 *  lance state
 */
typedef struct {
	QLock;
	uchar	ea[6];
	uchar	bcast[6];

	Lance;			/* host dependent lance params */
	int	prom;		/* number of promiscuous channels */
	int	all;		/* number of channels listening to all packets */
	int	wedged;		/* the lance is wedged */
	Network	net;

	int	inited;
	uchar	*lmp;		/* location of parity test */

	Rendez	rr;		/* rendezvous for an input buffer */
	QLock	rlock;		/* semaphore on tc */
	ushort	rl;		/* first rcv Message belonging to Lance */	

	Rendez	tr;		/* rendezvous for an output buffer */
	QLock	tlock;		/* semaphore on tc */
	ushort	tc;		/* next xmt Message CPU will try for */	

	Ethertype *closeline;	/* channels waiting to close */
	Lock	closepin;	/* lock for closeline */
	Ethertype e[Ntypes];
	int	debug;
	int	kstarted;
	uchar	bcast[6];

	Queue	self;	/* packets turned around at the interface */

	/* sadistics */

	int	misses;
	int	inpackets;
	int	outpackets;
	int	crcs;		/* input crc errors */
	int	oerrs;		/* output erros */
	int	frames;		/* framing errors */
	int	overflows;	/* packet overflows */
	int	buffs;		/* buffering errors */
} SoftLance;
static SoftLance l;

/*
 *  mode bits in the lance initialization block
 */
#define PROM	0x8000
#define INTL	0x40
#define DRTY	0x20
#define COLL	0x10
#define DTCR	0x8
#define LOOP	0x4
#define DTX	0x2
#define DRX	0x1

/*
 *  LANCE CSR0, this is the register we play with most often.  We leave
 *  this register pointed to by l.rap in normal operation.
 */
#define ERR0	0x8000
#define BABL	0x4000
#define CERR	0x2000
#define MISS	0x1000
#define MERR	0x800
#define RINT	0x400
#define TINT	0x200
#define IDON	0x100
#define INTR	0x80
#define INEA	0x40
#define RXON	0x20
#define TXON	0x10
#define TDMD	0x8
#define STOP	0x4
#define STRT	0x2
#define INIT	0x1

/*
 *  flag bits from a buffer descriptor in the rcv/xmt rings
 */
#define LANCEOWNER	0x8000	/* 1 means that the buffer can be used by the chip */
#define ERR	0x4000	/* error summary, the OR of all error bits */
#define FRAM	0x2000	/* CRC error and incoming packet not a multiple of 8 bits */
#define OFLO	0x1000	/* (receive) lost some of the packet */
#define MORE	0x1000	/* (transmit) more than 1 retry to send the packet */
#define CRC	0x800	/* (receive) crc error reading packet */
#define ONE	0x800	/* (transmit) one retry to transmit the packet */
#define BUF	0x400	/* (receive) out of buffers while reading a packet */
#define DEF	0x400	/* (transmit) deffered while transmitting packet */
#define STP	0x200	/* start of packet */
#define ENP	0x100	/* end of packet */

/*
 *  cntflags bits from a buffer descriptor in the rcv/xmt rings
 */
#define BUFF	0x8000	/* buffer error (host screwed up?) */
#define UFLO	0x4000	/* underflow from memory */
#define LCOL	0x1000	/* late collision (ether too long?) */
#define LCAR	0x800	/* loss of carrier (ether broken?) */
#define RTRY	0x400	/* couldn't transmit (bad station on ether?) */
#define TTDR	0x3FF	/* time domain reflectometer */

/*
 *  predeclared
 */
static void	lancekproc(void *);
static void	lancestart(int);
static void	lanceup(Etherpkt*, int);
static int	lanceclonecon(Chan*);
static void	lancestatsfill(Chan*, char*, int);
static void	lancetypefill(Chan*, char*, int);

/*
 *  lance stream module definition
 */
static void lanceoput(Queue*, Block*);
static void lancestopen(Queue*, Stream*);
static void lancestclose(Queue*);
static void stagerbuf(void);
Qinfo lanceinfo = { nullput, lanceoput, lancestopen, lancestclose, "lance" };

/*
 *  open a lance line discipline
 */
void
lancestopen(Queue *q, Stream *s)
{
	Ethertype *et;

	et = &l.e[s->id];
	RD(q)->ptr = WR(q)->ptr = et;
	et->type = 0;
	et->q = RD(q);
	et->inuse = 1;
}

/*
 *  close lance line discipline
 *
 *  the lock is to synchronize changing the ethertype with
 *  sending packets up the stream on interrupts.
 */
static int
isclosed(void *x)
{
	return ((Ethertype *)x)->q == 0;
}

static void
lancestclose(Queue *q)
{
	Ethertype *et;

	et = (Ethertype *)(q->ptr);
	if(et->prom){
		qlock(&l);
		l.prom--;
		if(l.prom == 0)
			lancestart(0);
		qunlock(&l);
	}
	if(et->type == -1){
		qlock(&l);
		l.all--;
		qunlock(&l);
	}

	/*
	 *  mark as closing and wait for kproc to close us
	 */
	lock(&l.closepin);
	et->closeline = l.closeline;
	l.closeline = et;
	unlock(&l.closepin);
	wakeup(&l.rr);
	sleep(&et->rc, isclosed, et);
	
	et->type = 0;
	et->q = 0;
	et->prom = 0;
	et->inuse = 0;
	netdisown(et);
}

/*
 *  the ``connect'' control message specifyies the type
 */
Proc *lanceout;
static int
isobuf(void *x)
{
	Msg *m;

	m = x;
	return l.wedged || (MPus(m->flags)&LANCEOWNER) == 0;
}

static void
lanceoput(Queue *q, Block *bp)
{
	int n, len;
	Etherpkt *p;
	Ethertype *e;
	Msg *m;
	Block *nbp;

	if(bp->type == M_CTL){
		e = q->ptr;
		qlock(&l);
		if(streamparse("connect", bp)){
			if(e->type == -1)
				l.all--;
			e->type = strtol((char *)bp->rptr, 0, 0);
			if(e->type == -1)
				l.all++;
		} else if(streamparse("promiscuous", bp)) {
			e->prom = 1;
			l.prom++;
			if(l.prom == 1)
				lancestart(PROM);/**/
		}
		qunlock(&l);
		freeb(bp);
		return;
	}

	/*
	 *  give packet a local address, return upstream if destined for
	 *  this machine.
	 */
	if(BLEN(bp) < ETHERHDRSIZE){
		bp = pullup(bp, ETHERHDRSIZE);
		if(bp == 0)
			return;
	}
	p = (Etherpkt *)bp->rptr;
	memmove(p->s, l.ea, sizeof(l.ea));
	if(*p->d == 0xff || l.prom || l.all){
		len = blen(bp);
		nbp = copyb(bp, len);
		nbp = expandb(nbp, len >= ETHERMINTU ? len : ETHERMINTU);
		if(nbp){
			nbp->wptr = nbp->rptr+len;
			putq(&l.self, nbp);
			wakeup(&l.rr);
		}
	} else if(*p->d == *l.ea && memcmp(l.ea, p->d, sizeof(l.ea)) == 0){
		len = blen(bp);
		bp = expandb(bp, len >= ETHERMINTU ? len : ETHERMINTU);
		if(bp){
			putq(&l.self, bp);
			wakeup(&l.rr);
		}
		return;
	}

	if(l.wedged) {
		freeb(bp);
		return;
	}
	QLock	tlock;		/* lock for grabbing transmitter queue */
	Rendez	tr;		/* wait here for free xmit buffer */

	/*
	 *  only one transmitter at a time
	 */
	qlock(&l.tlock);
	if(l.wedged) {
		qunlock(&l.tlock);
		freeb(bp);
		return;
	}
	Netif;
} l;

	if(waserror()){
		qunlock(&l.tlock);
		freeb(bp);
		nexterror();
	}
static void	promiscuous(void*, int);

	/*
	 *  Wait till we get an output buffer, complain if input
	 *  or output seems wedged.
	 */
	m = &(LANCEMEM->tmr[l.tc]);
	p = &l.tp[l.tc];
	while((MPus(m->flags)&LANCEOWNER) != 0) {
		tsleep(&l.tr, isobuf, m, 128);
		if(l.wedged || isobuf(m) == 0){
			qunlock(&l.tlock);
			freeb(bp);
			poperror();
			print("lance wedged, dumping block & restarting\n");
			lancestart(0);
			l.wedged = 0;
			return;
		}
	}

	/*
	 *  copy message into lance RAM
	 */
	len = 0;
	for(nbp = bp; nbp; nbp = nbp->next){
		if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){
			memmove(((uchar *)p)+len, nbp->rptr, n);
			len += n;
		} else
			print("no room damn it\n");
		if(bp->flags & S_DELIM)
			break;
	}

	/*
	 *  pad the packet (zero the pad)
	 */
	if(len < ETHERMINTU){
		memset(((char*)p)+len, 0, ETHERMINTU-len);
		len = ETHERMINTU;
	}

	/*
	 *  set up the ring descriptor and hand to lance
	 */
	l.outpackets++;
	MPs(m->size) = -len;
	MPus(m->cntflags) = 0;
	MPus(m->laddr) = LADDR(&l.ltp[l.tc]);
	MPus(m->flags) = LANCEOWNER|STP|ENP|HADDR(&l.ltp[l.tc]);
	l.tc = TSUCC(l.tc);
	*l.rdp = INEA|TDMD; /**/
	wbflush();
	qunlock(&l.tlock);
	freeb(bp);
	poperror();
}

/*
 *  stop the lance and allocate buffers
 */
void
lancereset(void)
{
	static int already;
	int i;

	if(already == 0){
		already = 1;
		lancesetup(&l);

		l.net.name = "ether";
		l.net.nconv = Ntypes;
		l.net.devp = &lanceinfo;
		l.net.protop = 0;
		l.net.listen = 0;
		l.net.clone = lanceclonecon;
		l.net.ninfo = 2;
		l.net.info[0].name = "stats";
		l.net.info[0].fill = lancestatsfill;
		l.net.info[1].name = "type";
		l.net.info[1].fill = lancetypefill;
		for(i = 0; i < Ntypes; i++)
			netadd(&l.net, &l.e[i], i);

		memset(l.bcast, 0xff, sizeof l.bcast);
		/* general network interface structure */
		netifinit(&l, "ether", Ntypes, 32*1024);
		l.alen = 6;
		memmove(l.addr, l.ea, 6);
		memmove(l.bcast, etherbcast, 6);
		l.promiscuous = promiscuous;
		l.arg = &l;
	}

	/*


@@ 459,8 162,9 @@ lancereset(void)
}

/*
 *  Initialize and start the lance.  This routine can be called only from a process.
 *  It may be used to restart a dead lance.
 * Initialize and start the lance. 
 * This routine can be called only from a process.
 * It may be used to restart a dead lance.
 */
static void
lancestart(int mode)


@@ 474,7 178,6 @@ lancestart(int mode)
	 *   quiescent
	 */
	qlock(&l.tlock);
	qlock(&l.rlock);

	lancereset();
	l.rl = 0;


@@ 507,7 210,7 @@ lancestart(int mode)
	 */
	m = lm->rmr;
	for(i = 0; i < l.nrrb; i++, m++){
		MPs(m->size) = -sizeof(Etherpkt);
		MPs(m->size) = -sizeof(Lancepkt);
		MPus(m->cntflags) = 0;
		MPus(m->laddr) = LADDR(&l.lrp[i]);
		MPus(m->flags) = HADDR(&l.lrp[i]);


@@ 561,147 264,13 @@ lancestart(int mode)
}

void
lanceinit(void)
{
}

Chan*
lanceattach(char *spec)
{
	if(l.kstarted == 0){
		kproc("lancekproc", lancekproc, 0);
		l.kstarted = 1;
		lancestart(0);
		print("lance ether: %.2x%.2x%.2x%.2x%.2x%.2x\n",
			l.ea[0], l.ea[1], l.ea[2], l.ea[3], l.ea[4], l.ea[5]);
	}
	return devattach('l', spec);
}

Chan*
lanceclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int	 
lancewalk(Chan *c, char *name)
{
	return netwalk(c, name, &l.net);
}

void	 
lancestat(Chan *c, char *dp)
{
	netstat(c, dp, &l.net);
}

/*
 *  Pass open's of anything except the directory to streamopen
 */
Chan*
lanceopen(Chan *c, int omode)
{
	return netopen(c, omode, &l.net);
}

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

void	 
lanceclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
}

long	 
lanceread(Chan *c, void *a, long n, ulong offset)
{
	return netread(c, a, n, offset,  &l.net);
}

long	 
lancewrite(Chan *c, void *a, long n, ulong offset)
{
	USED(offset);
	return streamwrite(c, a, n, 0);
}

void	 
lanceremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void	 
lancewstat(Chan *c, char *dp)
{
	netwstat(c, dp, &l.net);
}

/*
 *  user level network interface routines
 */
static void
lancestatsfill(Chan *c, char* p, int n)
{
	char buf[512];

	USED(c);
	sprint(buf, "in: %d\nout: %d\ncrc errs %d\noverflows: %d\nframe errs %d\nbuff errs: %d\noerrs %d\naddr: %.02x:%.02x:%.02x:%.02x:%.02x:%.02x\n",
		l.inpackets, l.outpackets, l.crcs,
		l.overflows, l.frames, l.buffs, l.oerrs,
		l.ea[0], l.ea[1], l.ea[2], l.ea[3], l.ea[4], l.ea[5]);
	strncpy(p, buf, n);
}

static void
lancetypefill(Chan *c, char* p, int n)
{
	char buf[16];
	Ethertype *e;

	e = &l.e[STREAMID(c->qid.path)];
	sprint(buf, "%d", e->type);
	strncpy(p, buf, n);
}

static int
lanceclonecon(Chan *c)
{
	Ethertype *e;

	USED(c);
	for(e = l.e; e < &l.e[Ntypes]; e++){
		qlock(e);
		if(e->inuse || e->q){
			qunlock(e);
			continue;
		}
		e->inuse = 1;
		netown(e, up->user, 0);
		qunlock(e);
		return e - l.e;
	}
	error(Enodev);
	return -1;		/* never reached */
}

/*
 *  We will:
 *	(1) Clear interrupt cause in the lance
 *	(2) service all current events
 */
void
lanceintr(void)
{
	ushort csr;
	Msg *m;
	Lancepkt *p;
	Netfile *f, **fp;
	ushort csr, t, x;
	Lancemem *lm = LANCEMEM;

	csr = *l.rdp;



@@ 721,7 290,6 @@ lanceintr(void)
			l.wedged = 1;
			l.misses = 0;
			lancereset();
			wakeup(&l.rr);
			wakeup(&l.tr);
			return;
		}


@@ 729,7 297,6 @@ lanceintr(void)

	if(csr & IDON){
		l.wedged = 0;
		qunlock(&l.rlock);
		qunlock(&l.tlock);
	}



@@ 740,91 307,9 @@ lanceintr(void)
		l.wedged = 1;

	/*
	 *  wakeup the input process
	 */
	if(csr & RINT)
		wakeup(&l.rr);

	/*
	 *  wake any process waiting for a transmit buffer
	 *  process incoming frames
	 */
	if(csr & TINT)
		wakeup(&l.tr);
}

/*
 *  send a packet upstream
 */
static void
lanceup(Etherpkt *p, int len)
{
	int t;
	Block *bp;
	Ethertype *e;

	if(len <= 0)
		return;

	t = (p->type[0]<<8) | p->type[1];
	for(e = &l.e[0]; e < &l.e[Ntypes]; e++){
		/*
		 *  check for open, the right type, and flow control
		 */
		if(e->q==0 || (t!=e->type && e->type!=-1) || e->q->next->len>Streamhi)
			continue;

		/*
		 *  only a trace channel gets packets destined for other machines
		 */
		if(e->type!=-1 && p->d[0]!=0xff
		&& (*p->d != *l.ea || memcmp(p->d, l.ea, sizeof(p->d))!=0))
			continue;

		if(!waserror()){
			bp = allocb(len);
			memmove(bp->rptr, (uchar *)p, len);
			bp->wptr += len;
			bp->flags |= S_DELIM;
			PUTNEXT(e->q, bp);
			poperror();
		}
	}
}

/*
 *  input process, awakened on each interrupt with rcv buffers filled
 */
static int
isinput(void *arg)
{
	Msg *m = arg;

	return ((MPus(m->flags) & LANCEOWNER)==0) || l.wedged || l.self.first
		|| l.closeline;
}

static void
lancekproc(void *arg)
{
	Etherpkt *p;
	Ethertype *e;
	int len;
	int t;
	Lancemem *lm = LANCEMEM;
	Msg *m;
	Block *bp;

	USED(arg);

	while(waserror())
		print("lancekproc err %s\n", up->error);

	for(;;){
		qlock(&l.rlock);
		while(bp = getq(&l.self)){
			lanceup((Etherpkt*)bp->rptr, BLEN(bp));
			freeb(bp);
		}
	if(csr & RINT) {
		m = &(lm->rmr[l.rl]);
		while((MPus(m->flags) & LANCEOWNER)==0){
			l.inpackets++;


@@ 839,18 324,23 @@ lancekproc(void *arg)
				if(t & BUFF)
					l.buffs++;
			} else {
				/*
				 *  stuff packet up each queue that wants it
				 */
				/* stuff packet up each queue that wants it */
				p = &l.rp[l.rl];
				len = MPus(m->cntflags) - 4;
				lanceup(p, len);
				x = MPus(m->cntflags) - 4;
				t = (p->type[0]<<8) | p->type[1];
				for(fp = l.f; fp < &l.f[Ntypes]; fp++){
					f = *fp;
					if(f == 0)
						continue;
					if(f->type == t || f->type < 0)
						qproduce(f->in, p->d, x);
				}
			}

			/*
			 *  stage the next input buffer
			 */
			MPs(m->size) = -sizeof(Etherpkt);
			MPs(m->size) = -sizeof(Lancepkt);
			MPus(m->cntflags) = 0;
			MPus(m->laddr) = LADDR(&l.lrp[l.rl]);
			MPus(m->flags) = LANCEOWNER|HADDR(&l.lrp[l.rl]);


@@ 858,29 348,144 @@ lancekproc(void *arg)
			l.rl = RSUCC(l.rl);
			m = &(lm->rmr[l.rl]);
		}
		qunlock(&l.rlock);
		sleep(&l.rr, isinput, m);

		/*
		 *  if the lance is wedged, restart it
		 */
		if(l.wedged){
			print("lance wedged, restarting\n");
			lancestart(0);
			l.wedged = 0;
		}
	}

		/*
		 *  close ethertypes requesting it
		 */
		if(l.closeline){
			lock(&l.closepin);
			for(e = l.closeline; e; e = e->closeline){
				e->q = 0;
				wakeup(&e->rc);
			}
			l.closeline = 0;
			unlock(&l.closepin);
	/*
	 *  wake any process waiting for a transmit buffer
	 */
	if(csr & TINT)
		wakeup(&l.tr);
}

/*
 *  turn promiscuous mode on/off
 */
static void
promiscuous(void *arg, int on)
{
	USED(arg);
	if(on)
		lancestart(PROM);
	else
		lancestart(0);;
}

void
lanceinit(void)
{
	lancestart(0);
	print("lance ether: %.2x%.2x%.2x%.2x%.2x%.2x\n",
		l.ea[0], l.ea[1], l.ea[2], l.ea[3], l.ea[4], l.ea[5]);
}

Chan*
lanceattach(char *spec)
{
	return devattach('l', spec);
}

Chan*
lanceclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int
lancewalk(Chan *c, char *name)
{
	return netifwalk(&l, c, name);
}

Chan*
lanceopen(Chan *c, int omode)
{
	return netifopen(&l, c, omode);
}

void
lancecreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
}

void
lanceclose(Chan *c)
{
	netifclose(&l, c);
}

long
lanceread(Chan *c, void *buf, long n, ulong offset)
{
	return netifread(&l, c, buf, n, offset);
}

static int
isoutbuf(void *x)
{
	Msg *m;

	m = x;
	return l.wedged || (MPus(m->flags)&LANCEOWNER) == 0;
}

long
lancewrite(Chan *c, void *buf, long n, ulong offset)
{
	Msg *m;
	Lancepkt *p;

	USED(offset);

	if(n > ETHERMAXTU)
		error(Ebadarg);

	/* etherif.c handles structure */
	if(NETTYPE(c->qid.path) != Ndataqid)
		return netifwrite(&l, c, buf, n);

	/* we handle data */
	qlock(&l.tlock);
	m = &(LANCEMEM->tmr[l.tc]);
	tsleep(&l.tr, isoutbuf, m, 10000);
	if(!isoutbuf(m) || l.wedged){
		print("lance transmitter jammed\n");
	}
	else {
		p = &l.tp[l.tc];
		memmove(p->d, buf, n);
		if(n < 60) {
			memset(p->d+n, 0, 60-n);
			n = 60;
		}
		memmove(p->s, l.ea, sizeof(l.ea));

		l.outpackets++;
		MPs(m->size) = -n;
		MPus(m->cntflags) = 0;
		MPus(m->laddr) = LADDR(&l.ltp[l.tc]);
		MPus(m->flags) = LANCEOWNER|STP|ENP|HADDR(&l.ltp[l.tc]);
		l.tc = TSUCC(l.tc);
		*l.rdp = INEA|TDMD; /**/
	}
	qunlock(&l.tlock);
	return n;
}

void
lanceremove(Chan *c)
{
	USED(c);
}

void
lancestat(Chan *c, char *dp)
{
	netifstat(&l, c, dp);
}

void
lancewstat(Chan *c, char *dp)
{
	netifwstat(&l, c, dp);
}

M port/portdat.h => port/portdat.h +4 -4
@@ 68,7 68,7 @@ struct RWlock
{
	Lock;				/* Lock modify lock */
	QLock	x;			/* Mutual exclusion lock */
	QLock	k;			/* Lock for waiting writers held for readers */
	QLock	k;			/* Lock for waiting writers */
	int	readers;		/* Count of readers in lock */
};



@@ 84,7 84,7 @@ struct Alarms
	Proc	*head;
};

#define MAXSYSARG	5		/* for mount(fd, mpt, flag, arg, srv) */
#define MAXSYSARG	5	/* for mount(fd, mpt, flag, arg, srv) */
struct Sargs
{
	ulong	args[MAXSYSARG];


@@ 458,7 458,7 @@ struct Palloc
	Rendez	r;			/* Sleep for free mem */
	QLock	pwait;			/* Queue of procs waiting for memory */
	int	wanted;			/* Do the wakeup at free */
	ulong	cmembase;		/* Key memory protected from read by devproc */
	ulong	cmembase;		/* Key memory */
	ulong	cmemtop;
};



@@ 565,7 565,7 @@ struct Proc
	int	kp;		/* true if a kernel process */
	Proc	*palarm;	/* Next alarm time */
	ulong	alarm;		/* Time of call */
	int	newtlb;		/* Pager has changed my pte's so I must flush */
	int	newtlb;		/* Pager has changed my pte's, I must flush */

	ulong	rendtag;	/* Tag for rendezvous */ 
	ulong	rendval;	/* Value for rendezvous */

M power/dat.h => power/dat.h +14 -4
@@ 1,4 1,5 @@
typedef struct Conf	Conf;
typedef struct Lancepkt Lancepkt;
typedef struct FPsave	FPsave;
typedef struct Cycmsg	Cycmsg;
typedef struct Lance	Lance;


@@ 148,6 149,15 @@ typedef void		KMap;
#define ACON	0x2
#define BCON	0x1

struct Lancepkt
{
	uchar	d[6];
	uchar	s[6];
	uchar	type[2];
	uchar	data[1500];
	uchar	crc[4];
};

/*
 *  system dependent lance stuff
 *  filled by lancesetup() 


@@ 166,10 176,10 @@ struct Lance
				    as seen by host */
	ushort	*lanceram;	/* start of lance ram as seen by host */
	Lancemem *lm;		/* start of lance ram as seen by lance */
	Etherpkt *rp;		/* receive buffers (host address) */
	Etherpkt *tp;		/* transmit buffers (host address) */
	Etherpkt *lrp;		/* receive buffers (lance address) */
	Etherpkt *ltp;		/* transmit buffers (lance address) */
	Lancepkt *rp;		/* receive buffers (host address) */
	Lancepkt *tp;		/* transmit buffers (host address) */
	Lancepkt *lrp;		/* receive buffers (lance address) */
	Lancepkt *ltp;		/* transmit buffers (lance address) */
};

struct

D power/debugger.c => power/debugger.c +0 -249
@@ 1,249 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"ureg.h"

IOQ dkbdq;
IOQ dprintq;

typedef struct Syslogbuf	Syslogbuf;
struct Syslogbuf
{
	char	*next;
	char	buf[4*1024];
};

Syslogbuf syslogbuf[4];

static int
dprint(char *fmt, ...)
{
	char buf[PRINTSIZE];
	int n;

	n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
	dprintq.puts(&dprintq, buf, n);
	return n;
}

static int
dgetline(char *p, int n)
{
	int c;
	char *end, *start;
	char buf[1];

	start = p;
	end = p + n - 1;
	for(;;){
		c = getc(&dkbdq);
		if(c <= 0)
			continue;

		/* echo */
		buf[0] = c;
		if(c == '\r' || c == '\n')
			dprintq.puts(&dprintq, "\r\n", 2);
		else
			dprintq.puts(&dprintq, buf, 1);

		if(c == '\b'){
			if(p != start)
				p--;
			continue;
		}
		if(c == '\r' || c == '\n')
			break;
		if(p >= end)
			continue;
		*p++ = c;
	}
	*p = 0;
	return p - start;
}

static void
printlog(char *cpu)
{
	int i, c;
	char *p, *end;
	Syslogbuf *s;

	if(cpu == 0)
		i = 0;
	else
		i = atoi(cpu);
	if(i < 0 || i > 3)
		return;
	s = &syslogbuf[i];

	if(s->next < s->buf || s->next >= &s->buf[sizeof(s->buf)])
		s->next = s->buf;
	end = s->next;
	p = end + 1;
	if(p >= &s->buf[sizeof(s->buf)] || p < s->buf)
		p = s->buf;
	while(p != end){
		c = *p & 0xff;
		if(c == '\r' || c == '\n')
			dprintq.puts(&dprintq, "\r\n", 2);
		else if(c >= ' ' && c <= '~')
			dprintq.puts(&dprintq, p, 1);
		p++;
		if(p >= &s->buf[sizeof(s->buf)])
			p = s->buf;
	}
}

static void
printhex(char *start, char *len)
{
	int i;
	ulong n;
	ulong *p;

	if(start == 0){
		dprint("!	h <location> <howmany> - hex display\r\n");
		return;
	}
	n = strtoul(start, 0, 16);
	if((n & KZERO) == 0)
		return;
	p = (ulong *)n;

	if(len)
		n = strtoul(len, 0, 0);
	else
		n = 1;

	while(n > 0){
		dprint("%lux/", p);
		for(i = 0; i < 8 && n > 0; i++, n--)
			dprint(" %8.8lux", p[i]);
		dprint("\r\n");
		p += 8;
	}
}

static void
printinfo(void)
{
	Mach *mp;
	Proc *p;
	Page *pg;
	Ureg *ur;
	ulong l;
	int i;

	for(i = 0; i < 4; i++){
		if((active.machs&(1<<i)) == 0)
			continue;
		mp = (void*)(MACHADDR+i*BY2PG);
		l = (ulong)(mp->proc);
		dprint("mach[%d] proc/%lux\r\n", i, l);
		dprint("mach[%d] splpc/%lux\r\n", i, mp->splpc);

		if(l & KZERO){
			p = (Proc*)l;
			l = (ulong)(p->upage);
			if(l & KZERO){
				pg = (Page*)l;
				l = pg->pa;
				dprint("mach[%d] proc->upage->pa/%lux\r\n", i, l);
			}
		}
		l = (ulong)(mp->ur);
		dprint("mach[%d] ur/%lux", i, l);
		ur = (Ureg*)l;
		dprint(" &status/%lux &cause/%lux &sp/%lux &pc/%lux",
				&ur->status, &ur->cause, &ur->sp, &ur->pc);
		dprint("\r\n");
	}
}

void
debugger(void *arg)
{
	int n, level;
	char buf[256];
	char *field[3];

	USED(arg);

	/*
	 *  sched() until we are on processor 1
	 */
	for(;;){
		level = splhi();
		if(m->machno == 1)
			break;
		splx(level);
		sched();
	}

	/*
 	 *  grab a port for a console
	 */
	initq(&dprintq);
	initq(&dkbdq);
	duartspecial(3, &dprintq, &dkbdq, 9600);

	/*
	 *  take processor and process out of active groups
	 */
	active.machs &= ~(1<<1);
	splx(level);

	for(;;){
		dprint("kdb> ");
		dgetline(buf, sizeof(buf));
		memset(field, 0, sizeof(field));
		n = getfields(buf, field, 3, ' ');
		if(n == 0)
			continue;
		switch(*field[0]){
		case 'l':
			printlog(field[1]);
			break;
		case 'h':
			printhex(field[1], field[2]);
			break;
		case 'i':
			printinfo();
			break;
		case 'q':
			firmware(cpuserver ? PROM_AUTOBOOT : PROM_REINIT);
			break;
		default:
			dprint("!commands are:\r\n");
			dprint("!	l <cpu#> - print cpu log\r\n");
			dprint("!	h <location> <howmany> - hex display\r\n");
			dprint("!	i - display some addresses\r\n");
			dprint("!	q - quit/rebooot\r\n");
			break;
		}
	}
}

void
syslog(char *str, int n)
{
	Syslogbuf *s;
	int level;

	level = splhi();
	s = &syslogbuf[m->machno];
	if(s->next < s->buf || s->next >= &s->buf[sizeof(s->buf)])
		s->next = s->buf;
	while(n-- > 0){
		*s->next = *str++;
		if(s->next >= &s->buf[sizeof(s->buf)-1])
			s->next = s->buf;
		else
			s->next++;
	}
	splx(level);
}

M power/devduart.c => power/devduart.c +356 -513
@@ 6,104 6,103 @@
#include	"io.h"
#include	"../port/error.h"

void	duartputs(IOQ*, char*, int);
void	iprint(char*, ...);

#define	PAD	15	/* registers are well-spaced */
#include	"devtab.h"
#include	"../port/netif.h"

/*
 *  Register set for half the duart.  There are really two sets in adjacent
 *  memory locations.
 * Register set for half the duart. 
 * There are really two sets in adjacent memory locations.
 */
struct Duartreg
{
	uchar	mr1_2,		/* Mode Register Channels 1 & 2 */
		pad0[PAD];
	uchar	sr_csr,		/* Status Register/Clock Select Register */
		pad1[PAD];
	uchar	cmnd,		/* Command Register */
		pad2[PAD];
	uchar	data,		/* RX Holding / TX Holding Register */
		pad3[PAD];
	uchar	ipc_acr,	/* Input Port Change/Aux. Control Register */
		pad4[PAD];
	uchar	is_imr,		/* Interrupt Status/Interrupt Mask Register */
		pad5[PAD];
	uchar	ctur,		/* Counter/Timer Upper Register */
		pad6[PAD];
	uchar	ctlr,		/* Counter/Timer Lower Register */
		pad7[PAD];
	uchar	mr12;		/* Mode Register Channels 1 & 2 */
	uchar	pad0[15];
	uchar	srcsr;		/* Status Register/Clock Select Register */
	uchar	pad1[15];
	uchar	cmnd;		/* Command Register */
	uchar	pad2[15];
	uchar	data;		/* RX Holding / TX Holding Register */
	uchar	pad3[15];
	uchar	ipcacr;		/* Input Uart Change/Aux. Control Register */
	uchar	pad4[15];
	uchar	isimr;		/* Interrupt Status/Interrupt Mask Register */
	uchar	pad5[15];
	uchar	ctur;		/* Counter/Timer Upper Register */
	uchar	pad6[15];
	uchar	ctlr;		/* Counter/Timer Lower Register */
	uchar	pad7[15];
};
#define	ppcr	is_imr		/* in the second register set */

#define DBD75		0
#define DBD110		1
#define DBD38400	2
#define DBD150		3
#define DBD300		4
#define DBD600		5
#define DBD1200		6
#define DBD2000		7
#define DBD2400		8
#define DBD4800		9
#define DBD1800		10
#define DBD9600		11
#define DBD19200	12

enum{
	CHAR_ERR	=0x00,	/* MR1x - Mode Register 1 */
	EVEN_PAR	=0x00,
	ODD_PAR		=0x04,
	NO_PAR		=0x10,
	CBITS8		=0x03,
	CBITS7		=0x02,
	CBITS6		=0x01,
	CBITS5		=0x00,
	NORM_OP		=0x00,	/* MR2x - Mode Register 2 */
	TWOSTOPB	=0x0F,
	ONESTOPB	=0x07,
	ENB_RX		=0x01,	/* CRx - Command Register */
	DIS_RX		=0x02,
	ENB_TX		=0x04,
	DIS_TX		=0x08,
	RESET_MR 	=0x10,
	RESET_RCV  	=0x20,
	RESET_TRANS  	=0x30,
	RESET_ERR  	=0x40,
	RESET_BCH	=0x50,
	STRT_BRK	=0x60,
	STOP_BRK	=0x70,
	RCV_RDY		=0x01,	/* SRx - Channel Status Register */
	FIFOFULL	=0x02,
	XMT_RDY		=0x04,
	XMT_EMT		=0x08,
	OVR_ERR		=0x10,
	PAR_ERR		=0x20,
	FRM_ERR		=0x40,
	RCVD_BRK	=0x80,
	IM_IPC		=0x80,	/* IMRx/ISRx - Interrupt Mask/Interrupt Status */
	IM_DBB		=0x40,
	IM_RRDYB	=0x20,
	IM_XRDYB	=0x10,
	IM_CRDY		=0x08,
	IM_DBA		=0x04,
	IM_RRDYA	=0x02,
	IM_XRDYA	=0x01,
	BD38400		=0xCC|0x0000,
	BD19200		=0xCC|0x0100,
	BD9600		=0xBB|0x0000,
	BD4800		=0x99|0x0000,
	BD2400		=0x88|0x0000,
	BD1200		=0x66|0x0000,
	BD300		=0x44|0x0000,

	Maxduart	=8,
#define	ppcr	isimr		/* in the second register set */

enum
{
	DBD75		= 0,
	DBD110		= 1,
	DBD38400	= 2,
	DBD150		= 3,
	DBD300		= 4,
	DBD600		= 5,
	DBD1200		= 6,
	DBD2000		= 7,
	DBD2400		= 8,
	DBD4800		= 9,
	DBD1800		= 10,
	DBD9600		= 11,
	DBD19200	= 12,
	CHARERR		= 0x00,	/* MR1x - Mode Register 1 */
	EVENPAR		= 0x00,
	ODDPAR		= 0x04,
	NOPAR		= 0x10,
	CBITS8		= 0x03,
	CBITS7		= 0x02,
	CBITS6		= 0x01,
	CBITS5		= 0x00,
	NORMOP		= 0x00,	/* MR2x - Mode Register 2 */
	TWOSTOPB	= 0x0F,
	ONESTOPB	= 0x07,
	ENBRX		= 0x01,	/* CRx - Command Register */
	DISRX		= 0x02,
	ENBTX		= 0x04,
	DISTX		= 0x08,
	RESETMR 	= 0x10,
	RESETRCV  	= 0x20,
	RESETTRANS  	= 0x30,
	RESETERR  	= 0x40,
	RESETBCH	= 0x50,
	STRTBRK		= 0x60,
	STOPBRK		= 0x70,
	RCVRDY		= 0x01,	/* SRx - Channel Status Register */
	FIFOFULL	= 0x02,
	XMTRDY		= 0x04,
	XMTEMT		= 0x08,
	OVRERR		= 0x10,
	PARERR		= 0x20,
	FRMERR		= 0x40,
	RCVDBRK		= 0x80,
	IMIPC		= 0x80,	/* IMRx/ISRx - Int Mask/Interrupt Status */
	IMDBB		= 0x40,
	IMRRDYB		= 0x20,
	IMXRDYB		= 0x10,
	IMCRDY		= 0x08,
	IMDBA		= 0x04,
	IMRRDYA		= 0x02,
	IMXRDYA		= 0x01,
	BD38400		= 0xCC|0x0000,
	BD19200		= 0xCC|0x0100,
	BD9600		= 0xBB|0x0000,
	BD4800		= 0x99|0x0000,
	BD2400		= 0x88|0x0000,
	BD1200		= 0x66|0x0000,
	BD300		= 0x44|0x0000,

	Maxduart	= 8,
};

/*
 *  requests to perform on a duart
 */
enum {
enum
{
	Dnone=	0,
	Dbaud,
	Dbreak,


@@ 130,11 129,10 @@ Duart duart[Maxduart];
/*
 *  values specific to a single duart port
 */
typedef struct Port	Port;
struct Port
typedef struct Uart	Uart;
struct Uart
{
	QLock;
	int		printing;	/* true if printing */
	Duart		*d;		/* device */
	Duartreg	*reg;		/* duart registers (for this port) */
	int		c;		/* character to restart output */


@@ 142,24 140,25 @@ struct Port
	int		val;		/* value of operation */
	Rendez		opr;		/* waiot here for op to complete */

	/* console interface */
	int		nostream;	/* can't use the stream interface */
	IOQ		*iq;		/* input character queue */
	IOQ		*oq;		/* output character queue */
	int		printing;	/* need kick */
	int		opens;
	Rendez		r;

	/* stream interface */
	Queue		*wq;		/* write queue */
	Rendez		r;		/* kproc waiting for input */
 	int		kstarted;	/* kproc started */
	/* buffers */
	int	(*putc)(Queue*, int);
	Queue	*iq;
	Queue	*oq;
};
Port duartport[2*Maxduart];
Uart uart[2*Maxduart];

void	duartkick(Uart*);

/*
 *  configure a duart port, default is 9600 baud, 8 bits/char, 1 stop bit,
 *  no parity
 */
void
duartsetup(Port *p, Duart *d, int devno)
duartsetup(Uart *p, Duart *d, int devno)
{
	Duartreg *reg;



@@ 167,16 166,19 @@ duartsetup(Port *p, Duart *d, int devno)
	reg = &d->reg[devno];
	p->reg = reg;

	reg->cmnd = RESET_RCV|DIS_TX|DIS_RX;
	reg->cmnd = RESET_TRANS;
	reg->cmnd = RESET_ERR;
	reg->cmnd = STOP_BRK;
	reg->cmnd = RESETRCV|DISTX|DISRX;
	reg->cmnd = RESETTRANS;
	reg->cmnd = RESETERR;
	reg->cmnd = STOPBRK;

	reg->cmnd = RESET_MR;
	reg->mr1_2 = NO_PAR|CBITS8;
	reg->mr1_2 = ONESTOPB;
	reg->sr_csr = (DBD9600<<4)|DBD9600;
	reg->cmnd = ENB_TX|ENB_RX;
	reg->cmnd = RESETMR;
	reg->mr12 = NOPAR|CBITS8;
	reg->mr12 = ONESTOPB;
	reg->srcsr = (DBD9600<<4)|DBD9600;
	reg->cmnd = ENBTX|ENBRX;

	p->iq = qopen(4*1024, 0, 0, 0);
	p->oq = qopen(4*1024, 0, duartkick, p);
}

/*


@@ 185,19 187,21 @@ duartsetup(Port *p, Duart *d, int devno)
void
duartinit(void)
{
	Port *p;
	Uart *p;
	Duart *d;

	d = &duart[m->machno];
	if(d->inited)
		return;

	d->reg = DUARTREG;
	d->imr = IM_RRDYA|IM_XRDYA|IM_RRDYB|IM_XRDYB;
	d->reg->is_imr = d->imr;
	d->imr = IMRRDYA|IMXRDYA|IMRRDYB|IMXRDYB;
	d->reg->isimr = d->imr;
	d->acr = 0x80;			/* baud rate set 2 */
	d->reg->ipc_acr = d->acr;
	d->reg->ipcacr = d->acr;

	p = &uart[2*m->machno];

	p = &duartport[2*m->machno];
	duartsetup(p, d, 0);
	p++;
	duartsetup(p, d, 1);


@@ 208,19 212,19 @@ duartinit(void)
 *  enable a duart port
 */
void
duartenable(Port *p)
duartenable(Uart *p)
{
	p->reg->cmnd = ENB_TX|ENB_RX;
	p->reg->cmnd = ENBTX|ENBRX;
}

void
duartenable0(void)
{
	DUARTREG->cmnd = ENB_TX|ENB_RX;
	DUARTREG->cmnd = ENBTX|ENBRX;
}

void
duartbaud(Port *p, int b)
duartbaud(Uart *p, int b)
{
	int x;



@@ 253,12 257,12 @@ duartbaud(Port *p, int b)
		p->d->acr |= 0x80;
	else
		p->d->acr &= ~0x80;
	p->d->reg->ipc_acr = p->d->acr;
	p->reg->sr_csr = x;
	p->d->reg->ipcacr = p->d->acr;
	p->reg->srcsr = x;
}

void
duartdtr(Port *p, int val)
duartdtr(Uart *p, int val)
{
	if (val)
		p->reg->ctlr = 0x01;


@@ 267,19 271,19 @@ duartdtr(Port *p, int val)
}

void
duartbreak(Port *p, int val)
duartbreak(Uart *p, int val)
{
	Duartreg *reg;

	reg = p->reg;
	if (val){
		p->d->imr &= ~IM_XRDYB;
		p->d->reg->is_imr = p->d->imr;
		reg->cmnd = STRT_BRK|ENB_TX;
		p->d->imr &= ~IMXRDYB;
		p->d->reg->isimr = p->d->imr;
		reg->cmnd = STRTBRK|ENBTX;
	} else {
		reg->cmnd = STOP_BRK|ENB_TX;
		p->d->imr |= IM_XRDYB;
		p->d->reg->is_imr = p->d->imr;
		reg->cmnd = STOPBRK|ENBTX;
		p->d->imr |= IMXRDYB;
		p->d->reg->isimr = p->d->imr;
	}
}



@@ 287,7 291,7 @@ duartbreak(Port *p, int val)
 *  do anything requested for this CPU's duarts
 */
void
duartslave0(Port *p)
duartslave0(Uart *p)
{
	switch(p->op){
	case Ddtr:


@@ 300,8 304,9 @@ duartslave0(Port *p)
		duartbreak(p, p->val);
		break;
	case Dprint:
		p->reg->cmnd = ENB_TX;
		p->reg->cmnd = ENBTX;
		p->reg->data = p->val;
iprint("slave %c\n", p->val);
		break;
	case Dena:
		duartenable(p);


@@ 316,62 321,75 @@ duartslave0(Port *p)
void
duartslave(void)
{
	IOQ *cq;
	Port *p;
	Uart *p;

	p = &duartport[2*m->machno];
	cq = p->iq;
	if(p->wq && cangetc(cq))
		wakeup(&cq->r);
	p = &uart[2*m->machno];
	if(p->op != Dnone)
		duartslave0(p);
	p++;
	cq = p->iq;
	if(p->wq && cangetc(cq))
		wakeup(&cq->r);
	if(p->op != Dnone)
		duartslave0(p);
}

void
duartrintr(Port *p)
duartrintr(Uart *p)
{
	Duartreg *reg;
	IOQ *cq;
	int status;
	char ch;
	int status;
	Duartreg *reg;

	reg = p->reg;
	status = reg->sr_csr;
	status = reg->srcsr;
	ch = reg->data;
	if(status & (FRM_ERR|OVR_ERR|PAR_ERR))
		reg->cmnd = RESET_ERR;
	if(status & (FRMERR|OVRERR|PARERR))
		reg->cmnd = RESETERR;

	cq = p->iq;
	if(cq->putc)
		(*cq->putc)(cq, ch);
	if(p->putc)
		(*p->putc)(p->iq, ch);
	else
		putc(cq, ch);
		qproduce(p->iq, &ch, 1);
}

/*
 *  (re)start output
 */
void
duartxintr(Port *p)
duartkick(Uart *p)
{
	char ch;
	int n, x;

	x = splhi();
	if(p->printing) {
		splx(x);
		return;
	}

	n = qconsume(p->oq, &ch, 1);
	if(n <= 0){
		splx(x);
		return;
	}

	p->printing = 1;
	p->val = ch;
	p->op = Dprint;
	splx(x);
}

void
duartxintr(Uart *p)
{
	Duartreg *reg;
	IOQ *cq;
	int ch;
	Duartreg *reg;

	cq = p->oq;
	lock(cq);
	ch = getc(cq);
	reg = p->reg;
	if(ch < 0){
	if(qconsume(p->oq, &ch, 1) <= 0) {
		p->printing = 0;
		wakeup(&cq->r);
		reg->cmnd = DIS_TX;
	} else
		reg->cmnd = DISTX;
	}
	else
		reg->data = ch;
	unlock(cq);
}

void


@@ 379,34 397,22 @@ duartintr(void)
{
	int cause;
	Duartreg *reg;
	Port *p;
	Uart *p;

	p = &duartport[2*m->machno];
	p = &uart[2*m->machno];
	reg = p->reg;
	cause = reg->is_imr;

	/*
	 * I can guess your interrupt.
	 */
	/*
	 * Is it 1?
	 */
	if(cause & IM_RRDYA)
	cause = reg->isimr;

	if(cause & IMRRDYA)
		duartrintr(p);
	/*
	 * Is it 2?
	 */
	if(cause & IM_XRDYA)

	if(cause & IMXRDYA)
		duartxintr(p);
	/*
	 * Is it 3?
	 */
	if(cause & IM_RRDYB)

	if(cause & IMRRDYB)
		duartrintr(p+1);
	/*
	 * Is it 4?
	 */
	if(cause & IM_XRDYB)

	if(cause & IMXRDYB)
		duartxintr(p+1);
}



@@ 416,310 422,85 @@ duartintr(void)
int
duartrawputc(int c)
{
	Duartreg *reg;
	int i;
	Duartreg *reg;

	reg = DUARTREG;
	if(c == '\n')
	if(c == '\n') {
		duartrawputc('\r');
	reg->cmnd = ENB_TX;
		delay(100);
	}
	reg->cmnd = ENBTX;
	i = 0;
	while((reg->sr_csr&XMT_RDY) == 0)
		if(++i >= 1000000){
			duartsetup(&duartport[0], &duart[0], 0);
			for(i=0; i<100000; i++)
				;
			break;
		}
	while((reg->srcsr&XMTRDY) == 0 && i++ < 100000)
		;
	reg->data = c;
	if(c == '\n')
		for(i=0; i<100000; i++)
			;
	return c;
}
void
duartrawputs(char *s)
{
	int i;
	while(*s){
		duartrawputc(*s++);
	}
	for(i=0; i < 1000000; i++)
		;
}

void
int
iprint(char *fmt, ...)
{
	int n, i;
	char buf[512];

	doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
	duartrawputs(buf);
	n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
	for(i = 0; i < n; i++)
		duartrawputc(buf[i]);
	return n;
}

/*
 *  Queue n characters for output; if queue is full, we lose characters.
 *  Get the output going if it isn't already.
 */
void
duartputs(IOQ *cq, char *s, int n)
duartspecial(int port, int s, Queue **in, Queue **out, int (*putc)(Queue*, int))
{
	int ch, x;
	Port *p;
	Uart *p;

	x = splhi();
	lock(cq);
	puts(cq, s, n);
	p = cq->ptr;
	if(p->printing == 0){
		ch = getc(cq);
		if(ch >= 0){
			p->printing = 1;
			p->val = ch;
			p->op = Dprint;
		}
	}
	unlock(cq);
	splx(x);
}

/*
 *  set up an duart port as something other than a stream
 */
void
duartspecial(int port, IOQ *oq, IOQ *iq, int baud)
{
	Port *p = &duartport[port];
	IOQ *zq;
	p = &uart[port];

	p->nostream = 1;
	if(oq){
		p->oq = oq;
		p->oq->puts = duartputs;
		p->oq->ptr = p;
	}
	if(iq){
		p->iq = iq;
		p->iq->ptr = p;

		/*
		 *  Stupid HACK to undo a stupid hack
		 */ 
		zq = &kbdq;
		if(iq == zq)
			kbdq.putc = kbdcr2nl;
	}
	duartenable(p);
	duartbaud(p, baud);
}
	if(s)
		duartbaud(p, s);

static int	duartputc(IOQ *, int);
static void	duartstopen(Queue*, Stream*);
static void	duartstclose(Queue*);
static void	duartoput(Queue*, Block*);
static void	duartkproc(void *);
Qinfo duartinfo =
{
	nullput,
	duartoput,
	duartstopen,
	duartstclose,
	"duart"
};
	p->putc = putc;
	if(in)
		*in = p->iq;
	if(out)
		*out = p->oq;

	p->opens++;
}

static int
opdone(void *x)
{
	Port *p = x;
	Uart *p = x;

	return p->op == Dnone;
}

static void
duartstopen(Queue *q, Stream *s)
{
	Port *p;
	char name[NAMELEN];

	p = &duartport[s->id];

	qlock(p);
	p->wq = WR(q);
	WR(q)->ptr = p;
	RD(q)->ptr = p;
	qunlock(p);

	if(p->kstarted == 0){
		p->kstarted = 1;
		sprint(name, "duart%d", s->id+1);
		kproc(name, duartkproc, p);
	}

	/* enable the port */
	qlock(p);
	p->op = Dena;
	sleep(&p->opr, opdone, p);
	qunlock(p);
}

static void
duartstclose(Queue *q)
{
	Port *p = q->ptr;

	qlock(p);
	p->wq = 0;
	p->iq->putc = 0;
	WR(q)->ptr = 0;
	RD(q)->ptr = 0;
	qunlock(p);
}

static void
duartoput(Queue *q, Block *bp)
{
	Port *p = q->ptr;
	IOQ *cq;
	int n, m;

	if(p == 0){
		freeb(bp);
		return;
	}
	cq = p->oq;
	if(waserror()){
		freeb(bp);
		nexterror();
	}
	if(bp->type == M_CTL){
		if(waserror()){
			qunlock(p);
			qunlock(p->d);
			nexterror();
		}
		qlock(p);
		qlock(p->d);
		while (cangetc(cq))	/* let output drain */
			sleep(&cq->r, cangetc, cq);
		n = strtoul((char *)(bp->rptr+1), 0, 0);
		switch(*bp->rptr){
		case 'B':
		case 'b':
			p->val = n;
			p->op = Dbaud;
			sleep(&p->opr, opdone, p);
			break;
		case 'D':
		case 'd':
			p->val = n;
			p->op = Ddtr;
			sleep(&p->opr, opdone, p);
			break;
		case 'K':
		case 'k':
			p->val = 1;
			p->op = Dbreak;
			if(!waserror()){
				sleep(&p->opr, opdone, p);
				tsleep(&p->opr, return0, 0, n);
				poperror();
			}
			p->val = 0;
			p->op = Dbreak;
			sleep(&p->opr, opdone, p);
			break;
		case 'R':
		case 'r':
			/* can't control? */
			break;
		}
		qunlock(p->d);
		qunlock(p);
		poperror();
	}else while((m = BLEN(bp)) > 0){
		while ((n = canputc(cq)) == 0){
			kprint(" duartoput: sleeping\n");
			sleep(&cq->r, canputc, cq);
		}
		if(n > m)
			n = m;
		(*cq->puts)(cq, bp->rptr, n);
		bp->rptr += n;
	}
	freeb(bp);
	poperror();
}

/*
 *  process to send bytes upstream for a port
 */
static void
duartkproc(void *a)
{
	Port *p = a;
	IOQ *cq = p->iq;
	Block *bp;
	int n;

loop:
	while ((n = cangetc(cq)) == 0)
		sleep(&cq->r, cangetc, cq);
	qlock(p);
	if(p->wq == 0){
		cq->out = cq->in;
	}else{
		bp = allocb(n);
		bp->flags |= S_DELIM;
		bp->wptr += gets(cq, bp->wptr, n);
		PUTNEXT(RD(p->wq), bp);
	}
	qunlock(p);
	goto loop;
}

Dirtab *duartdir;
int nduartport;
int nuart;

/*
 *  allocate the queues if no one else has
 */
void
duartreset(void)
{
	Port *p;
	int i;

	/*
 	 *  allocate the directory and fill it in
	 */
	nduartport = 2*conf.nmach;
	duartdir = xalloc(nduartport*2*sizeof(Dirtab));
	for(i = 0; i < nduartport; i++){
		sprint(duartdir[2*i].name, "eia%d", i+1);
		sprint(duartdir[2*i+1].name, "eia%dctl", i+1);
		duartdir[2*i].length = 0;
		duartdir[2*i+1].length = 0;
		duartdir[2*i].perm = 0666;
		duartdir[2*i+1].perm = 0666;
		duartdir[2*i].qid.path = STREAMQID(i, Sdataqid);
		duartdir[2*i+1].qid.path = STREAMQID(i, Sctlqid);
	}

	/*
	 *  allocate queues for any stream interfaces
	 */
	for(p = duartport; p < &duartport[nduartport]; p++){
		if(p->nostream)
			continue;

		p->iq = xalloc(sizeof(IOQ));
		initq(p->iq);
		p->iq->ptr = p;

		p->oq = xalloc(sizeof(IOQ));
		initq(p->oq);
		p->oq->ptr = p;
		p->oq->puts = duartputs;
	Dirtab *dp;

	nuart = 2*conf.nmach;
	duartdir = xalloc(2 * nuart * sizeof(Dirtab));
	dp = duartdir;
	for(i = 0; i < nuart; i++){
		/* 2 directory entries per port */
		print(dp->name, "eia%d", i);
		dp->qid.path = NETQID(i, Ndataqid);
		dp->perm = 0666;
		dp++;

		print(dp->name, "eia%dctl", i);
		dp->qid.path = NETQID(i, Nctlqid);
		dp->perm = 0666;
		dp++;
	}
}



@@ 738,19 519,25 @@ duartclone(Chan *c, Chan *nc)
int
duartwalk(Chan *c, char *name)
{
	return devwalk(c, name, duartdir, 2*nduartport, devgen);
	return devwalk(c, name, duartdir, 2*nuart, devgen);
}

void
duartstat(Chan *c, char *p)
duartstat(Chan *c, char *dp)
{
	switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
		streamstat(c, p, duartdir[2*STREAMID(c->qid.path)].name,
			duartdir[2*STREAMID(c->qid.path)].perm);
	int i;
	Uart *p;
	Dir dir;

	i = NETID(c->qid.path);
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		p = &uart[i];
		devdir(c, c->qid, duartdir[2*i].name, qlen(p->iq), eve, 0660, &dir);
		convD2M(&dir, dp);
		break;
	default:
		devstat(c, p, duartdir, 2*nduartport, devgen);
		devstat(c, dp, duartdir, 2*nuart, devgen);
		break;
	}
}


@@ 758,75 545,143 @@ duartstat(Chan *c, char *p)
Chan*
duartopen(Chan *c, int omode)
{
	Port *p;
	Uart *p;

	switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
	case Sctlqid:
		p = &duartport[STREAMID(c->qid.path)];
		break;
	default:
		p = 0;
		break;
	if(c->qid.path & CHDIR){
		if(omode != OREAD)
			error(Ebadarg);
	} 
	else {
		p = &uart[NETID(c->qid.path)];
		qlock(p);
		p->opens++;
		if(p->opens == 1) {
			/* enable the port */
			p->op = Dena;
			sleep(&p->opr, opdone, p);
		
			qreopen(p->iq);
			qreopen(p->oq);
		}
		qunlock(p);
	}

	if(p && p->nostream)
		error(Einuse);

	if((c->qid.path & CHDIR) == 0)
		streamopen(c, &duartinfo);
	return devopen(c, omode, duartdir, 2*nduartport, devgen);
	c->mode = omode&~OTRUNC;
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

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

void
duartclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
	Uart *p;

	if(c->qid.path & CHDIR)
		return;

	p = &uart[NETID(c->qid.path)];
	qlock(p);
	p->opens++;
	if(p->opens == 0){
		qclose(p->iq);
		qclose(p->oq);
	}
	qunlock(p);
}

long
duartread(Chan *c, void *buf, long n, ulong offset)
{
	Port *p;
	Uart *p;

	if(c->qid.path&CHDIR)
		return devdirread(c, buf, n, duartdir, 2*nduartport, devgen);
	if(c->qid.path & CHDIR)
		return devdirread(c, buf, n, duartdir, 2*nuart, devgen);

	switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
		return streamread(c, buf, n);
	case Sctlqid:
		if(offset)
			return 0;
		p = &duartport[STREAMID(c->qid.path)];
		qlock(p);
		p->op = Dstate;
		sleep(&p->opr, opdone, p);
		*(uchar *)buf = p->val;
		qunlock(p);
		return 1;
	p = &uart[NETID(c->qid.path)];
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qread(p->iq, buf, n);
	case Nctlqid:
		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
	}

	error(Egreg);
	return 0;	/* not reached */
	return 0;
}

static void
duartctl(Uart *p, char *cmd)
{
	int n, i;

	/* let output drain for a while */
	for(i = 0; i < 16 && qlen(p->oq); i++)
		tsleep(&p->r, qlen, p->oq, 125);

	n = atoi(cmd+1);
	switch(cmd[0]){
	case 'B':
	case 'b':
		p->val = n;
		p->op = Dbaud;
		sleep(&p->opr, opdone, p);
		break;
	case 'D':
	case 'd':
		p->val = n;
		p->op = Ddtr;
		sleep(&p->opr, opdone, p);
		break;
	case 'K':
	case 'k':
		p->val = 1;
		p->op = Dbreak;
		if(!waserror()){
			sleep(&p->opr, opdone, p);
			tsleep(&p->opr, return0, 0, n);
			poperror();
		}
		p->val = 0;
		p->op = Dbreak;
		sleep(&p->opr, opdone, p);
		break;
	case 'R':
	case 'r':
		/* can't control? */
		break;
	}
}

long
duartwrite(Chan *c, void *va, long n, ulong offset)
{
	Uart *p;
	char cmd[32];

	USED(offset);
	return streamwrite(c, va, n, 0);

	if(c->qid.path & CHDIR)
		error(Eperm);

	p = &uart[NETID(c->qid.path)];
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qwrite(p->oq, va, n, 0);
	case Nctlqid:
		if(n >= sizeof(cmd))
			n = sizeof(cmd)-1;
		memmove(cmd, va, n);
		cmd[n] = 0;
		duartctl(p, cmd);
		return n;
	}
}

void


@@ 839,18 694,6 @@ duartremove(Chan *c)
void
duartwstat(Chan *c, char *p)
{
	USED(c);
	USED(p);
	USED(c, p);
	error(Eperm);
}

int
duartactive(void)
{
	int i;

	for(i = 0; i < nduartport; i++)
		if(duartport[i].printing)
			return 1;
	return 0;
}

D power/devhs.c => power/devhs.c +0 -595
@@ 1,595 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"devtab.h"

#include	"io.h"

typedef struct Hs	Hs;

enum {
	Maxburst=	1023,		/* maximum transmit burst size */
	Intvec=		0xd0,		/* vme vector for interrupts */
	Intlevel=	5,		/* level to interrupt on */
	Nhs=		1,

	/*
	 * csr flags
	 */
	ALIVE		= 0x0001,
	IENABLE		= 0x0004,
	EXOFLOW		= 0x0008,
	IRQ		= 0x0010,
	EMUTE		= 0x0020,
	EPARITY		= 0x0040,
	EFRAME		= 0x0080,
	EROFLOW		= 0x0100,
	REF		= 0x0800,
	XFF		= 0x4000,
	XHF		= 0x8000,

	/*
	 * csr reset flags
	 */
	FORCEW		= 0x0008,
	NORESET		= 0xFF00,
	RESET		= 0x0000,

	/*
	 * data flags
	 */
	CTL		= 0x0100,
	CHNO		= 0x0200,
	TXEOD		= 0x0400,
	NND		= 0x8000,
};

#define NOW (MACHP(0)->ticks*MS2HZ)
#define IPL(x)		((x)<<5)

struct Hs {
	QLock;

	QLock	xmit;
	HSdev	*addr;
	int	vec;		/* interupt vector */
	Rendez	r;		/* output process */
	Rendez	kr;		/* input kernel process */
	int	chan;		/* current input channel */
	Queue	*rq;		/* read queue */
	uchar	buf[1024];	/* bytes being collected */
	uchar	*wptr;		/* pointer into buf */
	ulong	time;		/* time byte in buf[0] arrived */
	int	kstarted;	/* true if kernel process started */
	int	started;

	/* statistics */

	ulong	parity;		/* parity errors */
	ulong	rintr;		/* rcv interrupts */
	ulong	tintr;		/* transmit interrupts */
	ulong	in;		/* bytes in */
	ulong	out;		/* bytes out */
};

Hs hs[Nhs];

static void hsintr(int);
static void hskproc(void*);

/*
 *  hs stream module definition
 */
static void hsoput(Queue*, Block*);
static void hsstopen(Queue*, Stream*);
static void hsstclose(Queue*);
Qinfo hsinfo =
{
	nullput,
	hsoput,
	hsstopen,
	hsstclose,
	"hs"
};

void
hsrestart(Hs *hp)
{
	HSdev *addr;

	addr = hp->addr;

	addr->csr = RESET;
	wbflush();
	delay(20);

	/*
	 *  set interrupt vector
	 *  turn on device
	 *  set forcew to a known value
	 *  interrupt on level `Intlevel'
	 */
	addr->vector = hp->vec;
	addr->csr = NORESET|IPL(Intlevel)|IENABLE|ALIVE;
	wbflush();
	delay(1);
	addr->csr = NORESET|IPL(Intlevel)|FORCEW|IENABLE|ALIVE;
	wbflush();
	delay(1);
	hp->started = 1;
}

/*
 *  reset all vme boards
 */
void
hsreset(void)
{
	int i;
	Hs *hp;

	for(i=0; i<Nhs; i++){
		hp = &hs[i];
		hp->addr = HSDEV+i;
		hp->vec = Intvec+i;
		hp->addr->csr = RESET;
		setvmevec(hp->vec, hsintr);
	}	
	wbflush();
	delay(20);
}

void
hsinit(void)
{
}

/*
 *  enable the device for interrupts, spec is the device number
 */
Chan*
hsattach(char *spec)
{
	Hs *hp;
	int i;
	Chan *c;

	i = strtoul(spec, 0, 0);
	if(i >= Nhs)
		error(Ebadarg);
	hp = &hs[i];
	if(!hp->started)
		hsrestart(hp);

	c = devattach('h', spec);
	c->dev = i;
	c->qid.path = CHDIR;
	return c;
}

Chan*
hsclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int	 
hswalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, streamgen);
}

void	 
hsstat(Chan *c, char *dp)
{
	devstat(c, dp, 0, 0, streamgen);
}

Chan*
hsopen(Chan *c, int omode)
{
	if(c->qid.path == CHDIR){
		if(omode != OREAD)
			error(Eperm);
	}else
		streamopen(c, &hsinfo);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

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

void	 
hsclose(Chan *c)
{
	if(c->qid.path != CHDIR)
		streamclose(c);
}

long	 
hsread(Chan *c, void *buf, long n, ulong offset)
{
	USED(offset);
	return streamread(c, buf, n);
}

long	 
hswrite(Chan *c, void *buf, long n, ulong offset)
{
	USED(offset);
	return streamwrite(c, buf, n, 0);
}

void	 
hsremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void	 
hswstat(Chan *c, char *dp)
{
	USED(c);
	USED(dp);
	error(Eperm);
}

/*
 *	the stream routines
 */

/*
 *  create the kernel process for input
 */
static void
hsstopen(Queue *q, Stream *s)
{
	Hs *hp;
	char name[32];

	hp = &hs[s->dev];
	sprint(name, "hs%d", s->dev);
	q->ptr = q->other->ptr = hp;
	hp->rq = q;
	kproc(name, hskproc, hp);
}

/*
 *  ask kproc to die and wait till it happens
 */
static int
kdead(void *arg)
{
	Hs *hp;

	hp = (Hs *)arg;
	return hp->kstarted == 0;
}
static void
hsstclose(Queue * q)
{
	Hs *hp;

	hp = (Hs *)q->ptr;
	qlock(hp);
	hp->rq = 0;
	qunlock(hp);
	while(waserror())
		;
	while(hp->kstarted){
		wakeup(&hp->kr);
		sleep(&hp->r, kdead, hp);
	}
	poperror();
}

/*
 *  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);
	}
}

/*
 *  return true if the output fifo is at least half empty.
 *  the implication is that it can take at least another 1000 byte burst.
 */
static int
halfempty(void *arg)
{
	HSdev *addr;

	addr = (HSdev*)arg;
	return addr->csr & XHF;
}

/*
 *  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
hsoput(Queue *q, Block *bp)
{
	HSdev *addr;
	Hs *hp;
	int burst;
	int chan;
	int ctl;
	int n;

	if(bp->type != M_DATA){
		freeb(bp);
		return;
	}
	if(BLEN(bp) < 3){
		bp = pullup(bp, 3);
		if(bp == 0){
			print("hsoput pullup failed\n");
			return;
		}
	}

	/*
	 *  get a whole message before handing bytes to the device
	 */
	if(!putq(q, bp))
		return;

	/*
	 *  one transmitter at a time
	 */
	hp = (Hs *)q->ptr;
	qlock(&hp->xmit);
	if(waserror()){
		qunlock(&hp->xmit);
		nexterror();
	}
	addr = hp->addr;

	/*
	 *  parse message
	 */
	bp = getq(q);
	chan = CHNO | bp->rptr[0] | (bp->rptr[1]<<8);
	ctl = bp->rptr[2];
	bp->rptr += 3;

	/*
	 *  send the 8 bit data, burst are up to Maxburst (9-bit) bytes long
	 */
	if(!(addr->csr & XHF))
		sleep(&hp->r, halfempty, addr);
/*	print("->%.2uo\n", CHNO|chan);/**/
	addr->data = CHNO|chan;
	burst = Maxburst;
	while(bp){
		if(burst == 0){
			addr->data = TXEOD;
/*			print("->%.2uo\n", TXEOD); /**/
			if(!(addr->csr & XHF))
				sleep(&hp->r, halfempty, addr);
/*			print("->%.2uo\n", CHNO|chan); /**/
			addr->data = CHNO|chan;
			burst = Maxburst;
		}
		n = bp->wptr - bp->rptr;
		if(n > burst)
			n = burst;
		burst -= n;
		while(n--){
/*			print("->%.2uo\n", *bp->rptr); /**/
			addr->data = *bp->rptr++;
		}
		if(bp->rptr >= bp->wptr){
			if(bp->flags & S_DELIM){
				freeb(bp);
				break;
			}
			freeb(bp);
			bp = getq(q);
		}
	}

	/*
	 *  send the control byte if there is one
	 */
	if(ctl){
/*		print("->%.2uo\n", CTL|ctl); /**/
		addr->data = CTL|ctl;
	}

	/*
	 *  start the fifo emptying
	 */
/*	print("->%.2uo\n", TXEOD); /**/
	addr->data = TXEOD;

	qunlock(&hp->xmit);
	poperror();
}

/*
 *  return true if the input fifo is non-empty
 */
static int
notempty(void *arg)
{
	HSdev *addr;

	addr = (HSdev *)arg;
	return addr->csr & REF;
}

/*
 *  fill a block with what is currently buffered and send it upstream
 */
static void
upstream(Hs *hp, unsigned int ctl)
{
	int n;
	Block *bp;

	n = hp->wptr - hp->buf;
	bp = allocb(3 + n);
	bp->wptr[0] = hp->chan;
	bp->wptr[1] = hp->chan>>8;
	bp->wptr[2] = ctl;
	if(n)
		memmove(&bp->wptr[3], hp->buf, n);
	bp->wptr += 3 + n;
	bp->flags |= S_DELIM;
	PUTNEXT(hp->rq, bp);
	hp->wptr = hp->buf;
	hp->time = 0;
}

/*
 *  Read bytes from the input fifo.  Since we take an interrupt every
 *  time the fifo goes non-empty, we need to waste time to let the
 *  fifo fill up.
 */
static void
hskproc(void *arg)
{
	Hs *hp;
	HSdev *addr;
	unsigned int c;
	int locked;

	hp = (Hs *)arg;
	addr = hp->addr;
	hp->kstarted = 1;
	hp->wptr = hp->buf;

	locked = 0;
	if(waserror()){
		if(locked)
			qunlock(hp);
		hp->kstarted = 0;
		wakeup(&hp->r);
		return;
	}

	for(;;){
		/*
		 *  die if the device is closed
		 */
		USED(locked);		/* so locked = 0 and locked = 1 stay */
		qlock(hp);
		locked = 1;
		if(hp->rq == 0){
			qunlock(hp);
			hp->kstarted = 0;
			wakeup(&hp->r);
			poperror();
			return;
		}

		/*
		 *  0xFFFF means an empty fifo
		 */
		while((c = addr->data) != 0xFFFF){
			hp->in++;
			if(c & CHNO){
				c &= 0x1FF;
				if(hp->chan == c)
					continue;
				/*
				 *  new channel, put anything saved upstream
				 */
				if(hp->wptr - hp->buf != 0)
					upstream(hp, 0);
				hp->chan = c;
			} else if(c & NND){
				/*
				 *  ctl byte, this ends a message
				 */
				upstream(hp, c);
			} else {
				/*
				 *  data byte, put in local buffer
				 */
				if(hp->time == 0)
					hp->time = NOW + 100;
				*hp->wptr++ = c;
				if(hp->wptr == &hp->buf[sizeof hp->buf])
					upstream(hp, 0);
			}
		}
		if(hp->time && NOW >= hp->time)
			upstream(hp, 0);
		USED(locked);
		qunlock(hp);
		locked = 0;

		/*
		 *  Sleep if input fifo empty. Make sure we don't hold onto
		 *  any byte for more than 1/10 second.
		 */
		if(!(addr->csr & REF)){
			if(hp->wptr == hp->buf)
				tsleep(&hp->kr, notempty, addr, 2000);
			else
				tsleep(&hp->kr, notempty, addr, 100);
		}
	}
}

/*
 *  only one flavor interrupt.   we have to use the less than half full
 *  and not empty bits to figure out whom to wake.
 */
static void
hsintr(int vec)
{
	ushort csr;
	Hs *hp;

	hp = &hs[vec - Intvec];
	if(hp < hs || hp > &hs[Nhs]){
		print("bad hs vec\n");
		return;
	}
	csr = hp->addr->csr;

	if(csr & REF){
		hp->rintr++;
		wakeup(&hp->kr);
	}
	if(csr & XHF){
		hp->tintr++;
		wakeup(&hp->r);
	}
	if((csr^XFF) & (XFF|EROFLOW|EFRAME|EPARITY|EXOFLOW)){
		hp->parity++;
		hsrestart(hp);
		print("hs %d: reset, csr = 0x%ux\n",
			vec - Intvec, csr);
	}
}

M power/fns.h => power/fns.h +3 -3
@@ 9,14 9,13 @@ void	confprint(void);
void	confset(char*);
int	conschar(void);
void	dcflush(void*, ulong);
void	debugger(void*);
int	duartactive(void);
void	duartenable0(void);
void	duartinit(void);
void	duartintr(void);
void	duartreset(void);
void	duartslave(void);
void	duartspecial(int, IOQ*, IOQ*, int);
void	duartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int));
void	duartxmit(int);
void	evenaddr(ulong);
void	faultmips(Ureg*, int, int);


@@ 32,6 31,7 @@ ulong	getcallerpc(void*);
void	icflush(void *, ulong);
void	ioboardinit(void);
void	intr(Ureg*);
int	iprint(char*, ...);
void	kbdchar(int);
void	kproftimer(ulong);
void	lanceintr(void);


@@ 58,7 58,7 @@ void	puttlb(ulong, ulong);
void	puttlbx(int, ulong, ulong);
int	readlog(ulong, char*, ulong);
void	restfpregs(FPsave*, ulong);
#define	screenputs syslog
#define	screenputs(a, b)
void	scsiintr(int);
void	setvmevec(int, void (*)(int));
void	sinit(void);

D power/ipdat.h => power/ipdat.h +0 -440
@@ 1,440 0,0 @@
typedef struct Ipconv	Ipconv;
typedef struct Ipifc	Ipifc;
typedef struct Fragq	Fragq;
typedef struct Ipfrag	Ipfrag;
typedef ulong		Ipaddr;
typedef struct Arpcache	Arpcache;
typedef ushort		Port;
typedef struct Udphdr	Udphdr;
typedef struct Etherhdr	Etherhdr;
typedef struct Reseq	Reseq;
typedef struct Tcp	Tcp;
typedef struct Tcpctl	Tcpctl;
typedef struct Tcphdr	Tcphdr;
typedef struct Timer	Timer;

struct Etherhdr {
#define ETHER_HDR	14
	uchar	d[6];
	uchar	s[6];
	uchar	type[2];

	/* Now we have the ip fields */
#define ETHER_IPHDR	20
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */
	uchar	ttl;		/* Time to live */
	uchar	proto;		/* Protocol */
	uchar	cksum[2];	/* Header checksum */
	uchar	src[4];		/* Ip source */
	uchar	dst[4];		/* Ip destination */
};

/* Ethernet packet types */
#define ET_IP	0x0800

/* A userlevel data gram */
struct Udphdr {
#define UDP_EHSIZE	22
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */

	/* Udp pseudo ip really starts here */
#define UDP_PHDRSIZE	12
#define UDP_HDRSIZE	20
	uchar	Unused;	
	uchar	udpproto;	/* Protocol */
	uchar	udpplen[2];	/* Header plus data length */
	uchar	udpsrc[4];	/* Ip source */
	uchar	udpdst[4];	/* Ip destination */
	uchar	udpsport[2];	/* Source port */
	uchar	udpdport[2];	/* Destination port */
	uchar	udplen[2];	/* data length */
	uchar	udpcksum[2];	/* Checksum */
};

#define TCP_PKT	(TCP_EHSIZE+TCP_IPLEN+TCP_PHDRSIZE)

struct Tcphdr {
#define TCP_EHSIZE	14
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */
#define TCP_IPLEN	8
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */

#define TCP_PHDRSIZE	12	
	uchar	Unused;
	uchar	proto;
	uchar	tcplen[2];
	uchar	tcpsrc[4];
	uchar	tcpdst[4];

#define TCP_HDRSIZE	20
	uchar	tcpsport[2];
	uchar	tcpdport[2];
	uchar	tcpseq[4];
	uchar	tcpack[4];
	uchar	tcpflag[2];
	uchar	tcpwin[2];
	uchar	tcpcksum[2];
	uchar	tcpurg[2];

	/* Options segment */
	uchar	tcpopt[2];
	uchar	tcpmss[2];
	};



struct Timer {
	Timer	*next;
	Timer	*prev;
	int	state;
	int	start;
	int	count;
	void	(*func)(void*);
	void	*arg;
	};

struct Tcpctl {
	QLock;
	uchar	state;		/* Connection state */
	uchar	type;		/* Listening or active connection */
	uchar	code;		/* Icmp code */		
	struct {
		int una;	/* Unacked data pointer */
		int nxt;	/* Next sequence expected */
		int ptr;	/* Data pointer */
		ushort wnd;	/* Tcp send window */
		int up;		/* Urgent data pointer */
		int wl1;
		int wl2;
	} snd;
	int	iss;
	ushort	cwind;
	ushort	ssthresh;
	int	resent;
	struct {
		int nxt;
		ushort wnd;
		int up;
	} rcv;
	int	irs;
	ushort	mss;
	int	rerecv;
	ushort	window;
	int	max_snd;
	int	last_ack;
	char	backoff;
	char	flags;
	char	tos;

	Block	*rcvq;
	ushort	rcvcnt;

	Block	*rcvoobq;
	ushort	rcvoobcnt;

	Block	*sndq;			/* List of data going out */
	ushort	sndcnt;			/* Amount of data in send queue */

	Block	*sndoobq;		/* List of blocks going oob */
	ushort	sndoobcnt;		/* Size of out of band queue */
	ushort	oobmark;		/* Out of band sequence mark */
	char	oobflags;		/* Out of band data flags */

	Reseq	*reseq;			/* Resequencing queue */
	Timer	timer;			 
	Timer	acktimer;		/* Acknoledge timer */
	Timer	rtt_timer;		/* Round trip timer */
	int	rttseq;			/* Round trip sequence */
	int	srtt;			/* Shortened round trip */
	int	mdev;			/* Mean deviation of round trip */
};

struct	Tcp {
	Port	source;
	Port	dest;
	int	seq;
	int	ack;
	char	flags;
	ushort	wnd;
	ushort	up;
	ushort	mss;
	};

struct Reseq {
	Reseq 	*next;
	Tcp	seg;
	Block	*bp;
	ushort	length;
	char	tos;
	};

/* An ip interface used for UDP/TCP/ARP/ICMP */
struct Ipconv {
	QLock;				/* Ref count lock */
	int 	ref;
	Qinfo	*stproto;		/* Stream protocol for this device */
	Ipaddr	dst;			/* Destination from connect */

	Port	psrc;			/* Source port */
	Port	pdst;			/* Destination port */

	uchar	ptype;			/* Source port type */
	Ipifc	*ipinterface;		/* Ip protocol interface */
	Queue	*readq;			/* Pointer to upstream read q */

	QLock	listenq;		/* List of people waiting incoming cons */
	Rendez	listenr;		/* Some where to sleep while waiting */
	Ipconv	*listen;
		
	char	err;			/* Async protocol error */
	int	backlog;		/* Maximum number of waiting connections */
	int	curlog;			/* Number of waiting connections */
	int 	contype;
	Tcpctl	tcpctl;			/* Tcp control block */
};

#define	MAX_TIME	100000000	/* Forever */
#define TCP_ACK		200		/* Timed ack sequence every 200ms */

#define URG	0x20
#define ACK	0x10
#define PSH	0x08
#define RST	0x04
#define SYN	0x02
#define FIN	0x01

#define EOL_KIND	0
#define NOOP_KIND	1
#define MSS_KIND	2

#define MSS_LENGTH	4
#define MSL2		10
#define MSPTICK		200
#define DEF_MSS		1024
#define DEF_RTT		1000
#define	TCPOOB_HADDATA	1
#define	TCPOOB_HAVEDATA 2

#define TCP_PASSIVE	0
#define TCP_ACTIVE	1

#define MAXBACKOFF	5
#define FORCE		1
#define	CLONE		2
#define RETRAN		4
#define ACTIVE		8
#define SYNACK		16
#define AGAIN		8
#define DGAIN		4

#define TIMER_STOP	0
#define TIMER_RUN	1
#define TIMER_EXPIRE	2

#define	set_timer(t,x)	(((t)->start) = (x)/MSPTICK)
#define	dur_timer(t)	((t)->start)
#define	read_timer(t)	((t)->count)
#define	run_timer(t)	((t)->state == TIMER_RUN)

enum {
	CLOSED = 0,
	LISTEN,
	SYN_SENT,
	SYN_RECEIVED,
	ESTABLISHED,
	FINWAIT1,
	FINWAIT2,
	CLOSE_WAIT,
	CLOSING,
	LAST_ACK,
	TIME_WAIT
	};

/*
 * Ip interface structure. We have one for each active protocol driver
 */
struct Ipifc {
	QLock;
	int 		ref;
	uchar		protocol;		/* Ip header protocol number */
	char		name[NAMELEN];		/* Protocol name */
	void (*iprcv)	(Ipconv *, Block *);	/* Receive demultiplexor */
	Ipconv		*connections;		/* Connection list */
	int		maxmtu;			/* Maximum transfer unit */
	int		minmtu;			/* Minumum tranfer unit */
	int		hsize;			/* Media header size */	
	Lock;	
};

struct Fragq {
	QLock;
	Block  *blist;
	Fragq  *next;
	Ipaddr src;
	Ipaddr dst;
	ushort id;
	};

struct Ipfrag {
	ushort	foff;
	ushort	flen;
	};

struct Arpcache {
	uchar	status;		/* Entry status */
	uchar	type;		/* Entry type */
	Ipaddr	ip;		/* Host byte order */
	uchar	eip[4];		/* Network byte order */
	uchar	et[6];		/* Ethernet address for this ip */
	int	age;		/* Entry timeout */
	Arpcache *hash;
	Arpcache **hashhd;
	Arpcache *frwd;
	Arpcache *prev;
};
#define ARP_FREE	0
#define ARP_OK		1
#define ARP_ASKED	2
#define ARP_TEMP	0
#define ARP_PERM	1
#define Arphashsize	32
#define ARPHASH(p)	arphash[((p[2]^p[3])%Arphashsize)]
#define ARP_WAITMS	2500		/* Wait for arp replys */

#define IP_VER	0x40			/* Using IP version 4 */
#define IP_HLEN 0x05			/* Header length in characters */
#define IP_DF	0x4000			/* Don't fragment */
#define IP_MF	0x2000			/* More fragments */

#define	ICMP_ECHOREPLY		0	/* Echo Reply */
#define	ICMP_UNREACH		3	/* Destination Unreachable */
#define	ICMP_SOURCEQUENCH	4	/* Source Quench */
#define	ICMP_REDIRECT		5	/* Redirect */
#define	ICMP_ECHO		8	/* Echo Request */
#define	ICMP_TIMXCEED		11	/* Time-to-live Exceeded */
#define	ICMP_PARAMPROB		12	/* Parameter Problem */
#define	ICMP_TSTAMP		13	/* Timestamp */
#define	ICMP_TSTAMPREPLY	14	/* Timestamp Reply */
#define	ICMP_IREQ		15	/* Information Request */
#define	ICMP_IREQREPLY		16	/* Information Reply */

/* Sizes */
#define IP_MAX		8192			/* Maximum Internet packet size */
#define UDP_MAX		(IP_MAX-ETHER_IPHDR)	/* Maximum UDP datagram size */
#define UDP_DATMAX	(UDP_MAX-UDP_HDRSIZE)	/* Maximum amount of udp data */

/* Protocol numbers */
#define IP_UDPPROTO	17
#define IP_TCPPROTO	6

/* Protocol port numbers */
#define PORTALLOC	5000		/* First automatic allocated port */
#define PRIVPORTALLOC	600		/* First priveleged port allocated */
#define PORTMAX		30000		/* Last port to allocte */

/* Stuff to go in funs.h someday */
Ipifc   *newipifc(uchar, void (*)(Ipconv *, Block*), Ipconv *, int, int, int, char*);
void	closeipifc(Ipifc*);
ushort	ip_csum(uchar*);
int	arp_lookup(uchar*, uchar*);
Ipaddr	ipparse(char*);
void	hnputs(uchar*, ushort);
void	hnputl(uchar*, ulong);
ulong	nhgetl(uchar*);
ushort	nhgets(uchar*);
ushort	ptcl_csum(Block*bp, int, int);
void	ppkt(Block*);
void	udprcvmsg(Ipconv *, Block*);
Block	*btrim(Block*, int, int);
Block	*ip_reassemble(int, Block*, Etherhdr*);
Ipconv	*portused(Ipconv *, Port);
Port	nextport(Ipconv *, Port);
void	arp_enter(Arpentry*, int);
void	arp_flush(void);
int	arp_delete(char*);
void	arplinkhead(Arpcache*);
Fragq   *ipfragallo(void);
void	ipfragfree(Fragq*);
void	iproute(uchar*, uchar*);
void	initfrag(int);
Block	*copyb(Block*, int);
int	ntohtcp(Tcp*, Block**);
void	reset(Ipaddr, Ipaddr, char, ushort, Tcp*);
void	proc_syn(Ipconv*, char, Tcp*);
void	send_syn(Tcpctl*);
void	tcp_output(Ipconv*);
int	seq_within(int, int, int);
void	update(Ipconv *, Tcp *);
int	trim(Tcpctl *, Tcp *, Block **, ushort *);
void	add_reseq(Tcpctl *, char, Tcp *, Block *, ushort);
void	close_self(Ipconv *, int);
int	seq_gt(int, int);
void	appendb(Block **, Block *);
Ipconv	*ip_conn(Ipconv *, Port, Port, Ipaddr dest, char proto);
void	ipmkdir(Qinfo *, Dirtab *, Ipconv *);
int	inb_window(Tcpctl *, int);
Block	*htontcp(Tcp *, Block *, Tcphdr *);
void	start_timer(Timer *);
void	stop_timer(Timer *);
int	copyupb(Block **, uchar *, int);
void	init_tcpctl(Ipconv *);
void	close_self(Ipconv *, int);
int	iss(void);
int	seq_within(int, int, int);
int	seq_lt(int, int);
int	seq_le(int, int);
int	seq_gt(int, int);
int	seq_ge(int, int);
void	setstate(Ipconv *, char);
void	tcpackproc(void*);
Block 	*htontcp(Tcp *, Block *, Tcphdr *);
int	ntohtcp(Tcp *, Block **);
void	extract_oob(Block **, Block **, Tcp *);
void	get_reseq(Tcpctl *, char *, Tcp *, Block **, ushort *);
void	state_upcall(Ipconv*, char oldstate, char newstate);
int	backoff(int);
int	dupb(Block **, Block *, int, int);
void	tcp_input(Ipconv *, Block *);
void 	tcprcvwin(Ipconv *);
void	open_tcp(Ipconv *, int, ushort, char);
void	tcpflow(void*);
void 	tcp_timeout(void *);
void	tcp_acktimer(void *);
Ipconv  *ipclonecon(Chan *);
void	iplisten(Chan *, Ipconv *, Ipconv *);

#define	fmtaddr(xx)	(xx>>24)&0xff,(xx>>16)&0xff,(xx>>8)&0xff,xx&0xff
#define	MIN(a, b)	((a) < (b) ? (a) : (b))
#define MAX(a, b)	((a) > (b) ? (a) : (b))
#define BLKIP(xp)	((Etherhdr *)((xp)->rptr))
#define BLKFRAG(xp)	((Ipfrag *)((xp)->rptr))
#define PREC(x)		((x)>>5 & 7)

#define WORKBUF		64

extern Ipaddr Myip;
extern Ipaddr Mymask;
extern Ipaddr classmask[4];
extern Ipconv *ipconv[];
extern char *tcpstate[];
extern Rendez tcpflowr;
extern Qinfo tcpinfo;
extern Qinfo ipinfo;
extern Qinfo udpinfo;

M power/main.c => power/main.c +12 -11
@@ 23,22 23,24 @@ main(void)
	machinit();
	active.exiting = 0;
	active.machs = 1;
iprint("Boot baby\n");
	arginit();
	confinit();
	lockinit();
	xinit();
	duartinit();
	printinit();
	duartspecial(0, &printq, &kbdq, 9600);
	print("\n\nBRAZIL\n");
	duartspecial(0, 9600, &kbdq, &printq, kbdcr2nl);
iprint("F:\n", printq);
	print("\n\nBrazil\n");
	pageinit();
	tlbinit();
	vecinit();
	procinit0();
	vecinit();
	initseg();
	clockinit();
	ioboardinit();
	chandevreset();
	streaminit();
	swapinit();
	userinit();
	launchinit();


@@ 55,7 57,6 @@ machinit(void)
	memset(m, 0, sizeof(Mach));
	m->machno = n;
	m->stb = &stlb[n][0];
	duartinit();

	m->ledval = 0xff;
}


@@ 307,12 308,12 @@ launch(int n)
void
online(void)
{

	machinit();
	lock(&active);
	active.machs |= 1<<m->machno;
	unlock(&active);
	tlbinit();
	duartinit();
	clockinit();
	schedinit();
}


@@ 636,9 637,9 @@ lanceIO2setup(Lance *lp)
	 *  Start at 4k to avoid the initialization block and
	 *  descriptor rings.
	 */
	lp->lrp = (Etherpkt*)(4*1024);
	lp->lrp = (Lancepkt*)(4*1024);
	lp->ltp = lp->lrp + lp->nrrb;
	lp->rp = (Etherpkt*)(((ulong)LANCERAM) + (ulong)lp->lrp);
	lp->rp = (Lancepkt*)(((ulong)LANCERAM) + (ulong)lp->lrp);
	lp->tp = lp->rp + lp->nrrb;
}



@@ 670,11 671,11 @@ lanceIO3setup(Lance *lp)
	 *  allocate some host memory for buffers and map it into lance
	 *  space
	 */
	len = (lp->nrrb + lp->ntrb)*sizeof(Etherpkt);
	lp->rp = (Etherpkt*)xspanalloc(len , BY2PG, 0);
	len = (lp->nrrb + lp->ntrb)*sizeof(Lancepkt);
	lp->rp = (Lancepkt*)xspanalloc(len , BY2PG, 0);
	lp->tp = lp->rp + lp->nrrb;
	x = (ulong)lp->rp;
	lp->lrp = (Etherpkt*)(x & 0xFFF);
	lp->lrp = (Lancepkt*)(x & 0xFFF);
	lp->ltp = lp->lrp + lp->nrrb;
	index = LANCEINDEX;
	for(y = x+len; x < y; x += 0x1000){

M power/mmu.c => power/mmu.c +2 -2
@@ 70,10 70,10 @@ putmmu(ulong tlbvirt, ulong tlbphys, Page *pg)
	splhi();

	ctl = &pg->cachectl[m->machno]; 
	if(*ctl == PG_TXTFLUSH) {
	if(*ctl & PG_TXTFLUSH) {
		dcflush((void*)pg->pa, BY2PG);
		icflush((void*)pg->pa, BY2PG);
		*ctl = PG_NOFLUSH;
		*ctl &= ~PG_TXTFLUSH;
	}

	tp = up->pidonmach[m->machno];

M power/trap.c => power/trap.c +2 -0
@@ 245,10 245,12 @@ intr(Ureg *ur)
					any = 1;
				if(v & (1<<2))
					lanceintr();
/*
				if(v & (1<<1))
					scsiintr(1);
				if(v & (1<<0))
					scsiintr(0);
*/
			}
		}
		/*