~kris/9p

9hist

eeee59b2bccf7e2ebdadc5cfc9280cbe408240eb — David du Colombier 27 years ago 29310bf
Plan 9 from Bell Labs 1999-06-08
M carrera/fns.h => carrera/fns.h +1 -1
@@ 72,7 72,7 @@ void		wrcompare(ulong);

void		serialinit(void);
void		ns16552special(int, int, Queue**, Queue**, int (*)(Queue*, int));
void		ns16552setup(ulong, ulong, char*);
void		ns16552setup(ulong, ulong, char*, int);
void		ns16552intr(int);
void		etherintr(void);
void		iomapinit(void);

M carrera/ns16552.h => carrera/ns16552.h +1 -1
@@ 13,7 13,7 @@ ns16552install(void)
		return;
	already = 1;

	ns16552setup(Uart1, UartFREQ, "eia0");
	ns16552setup(Uart1, UartFREQ, "eia0", Ns550);
}

#define RD(r)	(*(uchar*)((Uart1+r)^7))

M ip/esp.c => ip/esp.c +1 -0
@@ 484,6 484,7 @@ convlookup(Proto *esp, ulong spi)
		if(ecb->incoming && ecb->spi == spi)
			return c;
	}
	return nil;
}

static char *

A mpc/_inb.s => mpc/_inb.s +110 -0
@@ 0,0 1,110 @@
#include "mem.h"

#define	BDNZ	BC	16,0,
#define	BDNE	BC	0,2,

TEXT	inb(SB), $0
	OR	$ISAMEM, R3
	MOVBZ	(R3), R3
	RETURN

TEXT	insb(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$1, R4
insb1:
	MOVBZ	(R3), R7
	MOVBU	R7, 1(R4)
	BDNZ	insb1
	RETURN

TEXT	outb(SB), $0
	MOVW	v+4(FP), R4
	OR	$ISAMEM, R3
	MOVB	R4, (R3)
	RETURN

TEXT	outsb(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$1, R4
outsb1:
	MOVBZU	1(R4), R7
	MOVB	R7, (R3)
	BDNZ	outsb1
	RETURN

TEXT	ins(SB), $0
	OR	$ISAMEM, R3
	EIEIO
	MOVHBR	(R3), R3
	RETURN

TEXT	inss(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$2, R4
inss1:
	MOVHZ	(R3), R7
	MOVHU	R7, 2(R4)
	BDNZ	inss1
	RETURN

TEXT	outs(SB), $0
	MOVW	v+4(FP), R4
	OR	$ISAMEM, R3
	MOVHBR	R4, (R3)
	RETURN

TEXT	outss(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$2, R4
outss1:
	MOVHZU	2(R4), R7
	MOVH	R7, (R3)
	BDNZ	outss1
	RETURN

TEXT	inl(SB), $0
	OR	$ISAMEM, R3
	MOVWBR	(R3), R3
	RETURN

TEXT	insl(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$4, R4
insl1:
	MOVW	(R3), R7
	MOVWU	R7, 4(R4)
	BDNZ	insl1
	RETURN

TEXT	outl(SB), $0
	MOVW	v+4(FP), R4
	OR	$ISAMEM, R3
	MOVWBR	R4, (R3)
	RETURN

TEXT	outsl(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$4, R4
outsl1:
	MOVWU	4(R4), R7
	MOVW	R7, (R3)
	BDNZ	outsl1
	RETURN

M mpc/clock.c => mpc/clock.c +17 -10
@@ 112,26 112,33 @@ clockintr(Ureg *ur)
	m->iomem->swsr = 0xaa39;
	
	m->ticks++;
	if(m->proc)
		m->proc->pc = ur->pc;

	accounttime();
	if(kproftimer != nil)
		kproftimer(ur->pc);

	if(up)
		up->pc = ur->pc;

	checkalarms();
	if(m->machno == 0) {
		if(kproftick != nil)
			(*kproftick)(ur->pc);
		lock(&clock0lock);
		for(lp = clock0link; lp; lp = lp->link)
			lp->clock();
		unlock(&clock0lock);
	}

	if(up && up->state == Running){
//		if(up->type == Interp && tready())
//			ur->cr |= 1<<(31-31);
		if(nrdy > 0)
			sched();
	}
	if(up == 0 || up->state != Running)
		return;

// user profiling clock 
//	if(ur->status & KUSER) {
//		(*(ulong*)(USTKTOP-BY2WD)) += TK2MS(1);
//		segclock(ur->pc);
//	}

	if(anyready())
		sched();
}

void

M mpc/devether.c => mpc/devether.c +34 -39
@@ 12,8 12,6 @@

static Ether *etherxx[MaxEther];

void ethermediumlink(void);

Chan*
etherattach(char* spec)
{


@@ 37,19 35,6 @@ etherattach(char* spec)
	return chan;
}

static void
etherdetach(void)
{
	Ether **ether;

	ether = etherxx;
	while(*ether){
		if((*ether)->detach)
			(*ether)->detach(*ether);
		ether++;
	}
}

static int
etherwalk(Chan* chan, char* name)
{


@@ 76,18 61,14 @@ ethercreate(Chan*, char*, int, ulong)
static void
etherclose(Chan* chan)
{
	Ether *ether;

	ether = etherxx[chan->dev];
	netifclose(ether, chan);
	if(ether->closed)
		ether->closed(ether);
	netifclose(etherxx[chan->dev], chan);
}

static long
etherread(Chan* chan, void* buf, long n, vlong offset)
etherread(Chan* chan, void* buf, long n, vlong off)
{
	Ether *ether;
	ulong offset = off;

	ether = etherxx[chan->dev];
	if((chan->qid.path & CHDIR) == 0 && ether->ifstat){


@@ 153,7 134,7 @@ etheriq(Ether* ether, Block* bp, int freebp)
{
	Etherpkt *pkt;
	ushort type;
	int len;
	int len, multi, forme;
	Netfile **ep, *f, **fp, *fx;
	Block *xbp;



@@ 165,8 146,9 @@ etheriq(Ether* ether, Block* bp, int freebp)
	fx = 0;
	ep = &ether->f[Ntypes];

	multi = pkt->d[0] & 1;
	/* check for valid multcast addresses */
	if((pkt->d[0] & 1) && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){
		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
			if(freebp){
				freeb(bp);


@@ 176,6 158,9 @@ etheriq(Ether* ether, Block* bp, int freebp)
		}
	}

	// is it for me?
	forme = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;

	/*
	 * Multiplex the packet to all the connections which want it.
	 * If the packet is not to be used subsequently (freebp != 0),


@@ 183,7 168,7 @@ etheriq(Ether* ether, Block* bp, int freebp)
	 * saving a copy of the data (usual case hopefully).
	 */
	for(fp = ether->f; fp < ep; fp++){
		if((f = *fp) && (f->type == type || f->type < 0)){
		if((f = *fp) && (f->type == type || f->type < 0) && (forme || multi || f->prom)){
			if(f->type > -2){
				if(freebp && fx == 0)
					fx = f;


@@ 201,7 186,8 @@ etheriq(Ether* ether, Block* bp, int freebp)
	}

	if(fx){
		qpass(fx->in, bp);
		if(qpass(fx->in, bp) < 0)
			ether->soverflows++;
		return 0;
	}
	if(freebp){


@@ 215,7 201,7 @@ etheriq(Ether* ether, Block* bp, int freebp)
static int
etheroq(Ether* ether, Block* bp)
{
	int len, loopback, s;
	int len, loopback, s, mine;
	Etherpkt *pkt;

	ether->outpackets++;


@@ 226,10 212,14 @@ etheroq(Ether* ether, Block* bp)
	 * in promiscuous mode.
	 * If it's a loopback packet indicate to etheriq that the data isn't
	 * needed and return, etheriq will pass-on or free the block.
	 * To enable bridging to work, only packets that were originated
	 * by this interface are fed back.
	 */
	pkt = (Etherpkt*)bp->rp;
	len = BLEN(bp);
	loopback = (memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0);
	mine = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
	if(mine)
	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
		s = splhi();
		etheriq(ether, bp, loopback);


@@ 256,8 246,8 @@ etherwrite(Chan* chan, void* buf, long n, vlong)

	if(n > ETHERMAXTU)
		error(Etoobig);
//	if(n < ETHERMINTU)
//		error(Etoosmall);
	if(n < ETHERMINTU)
		error(Etoosmall);

	bp = allocb(n);
	if(waserror()){


@@ 279,7 269,6 @@ etherbwrite(Chan* chan, Block* bp, ulong)
	long n;

	n = BLEN(bp);

	ether = etherxx[chan->dev];
	if(NETTYPE(chan->qid.path) != Ndataqid){
		n = netifwrite(ether, chan, bp->rp, n);


@@ 291,6 280,10 @@ etherbwrite(Chan* chan, Block* bp, ulong)
		freeb(bp);
		error(Ebadarg);
	}
	if(n < ETHERMINTU){
		freeb(bp);
		error(Etoosmall);
	}

	return etheroq(ether, bp);
}


@@ 304,7 297,7 @@ void
addethercard(char* t, int (*r)(Ether*))
{
	static int ncard;
print("addethercard\n");

	if(ncard == MaxEther)
		panic("too many ether cards");
	cards[ncard].type = t;


@@ 342,8 335,6 @@ etherreset(void)
	int i, n, ctlrno;
	char name[NAMELEN], buf[128];

print("etherreset\n");
	//ethermediumlink();
	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
		if(ether == 0)
			ether = malloc(sizeof(Ether));


@@ 353,9 344,7 @@ print("etherreset\n");
		ether->mbps = 10;
		if(isaconfig("ether", ctlrno, ether) == 0)
			continue;
print("after isaconfig %d\n", n);
		for(n = 0; cards[n].type; n++){
print("card %d\n", n);
			if(cistrcmp(cards[n].type, ether->type))
				continue;
			for(i = 0; i < ether->nopt; i++){


@@ 367,9 356,16 @@ print("card %d\n", n);
			if(cards[n].reset(ether))
				break;

			/*
			 * IRQ2 doesn't really exist, it's used to gang the interrupt
			 * controllers together. A device set to IRQ2 will appear on
			 * the second interrupt controller as IRQ9.
			 */
			if(ether->irq == 2)
				ether->irq = 9;
			intrenable(ether->irq, ether->interrupt, ether, ether->tbdf);

			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d",
			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
			if(ether->mem)
				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));


@@ 388,9 384,9 @@ print("card %d\n", n);
					ether->oq = qopen(256*1024, 1, 0, 0);
			}
			else{
				netifinit(ether, name, Ntypes, 32*1024);
				netifinit(ether, name, Ntypes, 65*1024);
				if(ether->oq == 0)
					ether->oq = qopen(64*1024, 1, 0, 0);
					ether->oq = qopen(65*1024, 1, 0, 0);
			}
			if(ether->oq == 0)
				panic("etherreset %s", name);


@@ 434,7 430,6 @@ Dev etherdevtab = {
	etherreset,
	devinit,
	etherattach,
	/*etherdetach,*/
	devclone,
	etherwalk,
	etherstat,

A mpc/devpcmcia.c => mpc/devpcmcia.c +1118 -0
@@ 0,0 1,1118 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

/*
 *  MPC8xx PCMCIA driver 
 *
 */

typedef struct PCMmap	PCMmap;
typedef struct Slot	Slot;
typedef struct Conftab	Conftab;

enum
{
	Maxctlr=	1,

	/* pipr */
	Cvs1=	1<<15,		// voltage sense 1
	Cvs2=	1<<14,		// voltage sense 2
	Cwp=	1<<13,		// write protect
	Ccd2=	1<<12,		// card detect 2
	Ccd1=	1<<11,		// card detect 1
	Cbvd2=	1<<10,		// battery voltage 2
	Cbvd1=	1<<9,		// battery voltage 1
	Crdy=	1<<8,		// ready

	/* pscr */
	Cvs1_c=	1<<15,		// voltage sense 1 changed
	Cvs2_c=	1<<14,		// voltage sense 2 changed
	Cwp_c=	1<<13,		// write protect changed
	Ccd2_c=	1<<12,		// card detect 2 changed
	Ccd1_c=	1<<11,		// card detect 1 changed
	Cbvd2_c=	1<<10,	// battery voltage 2 changed
	Cbvd1_c=	1<<9,	// battery voltage 1 changed
	Crdy_l=	1<<7,		// ready is low
	Crdy_h=	1<<6,		// ready is hi
	Crdy_r=	1<<5,		// ready raising edge
	Crdy_f=	1<<4,		// ready falling edge

	/* pgcrx */
	Creset = IBIT(25),	// card reset
	Coe = IBIT(24),		// output enable

	/* porN */
	Rport8=		0<<6,
	Rport16=	1<<6,
	Rmem=	0<<3,	/* common memory space */
	Rattrib=	2<<3,	/* attribute space */
	Rio=		3<<3,
	Rdma=	4<<3,	/* normal DMA */
	Rdmalx=	5<<3,	/* DMA, last transaction */
	RA22_23= 6<<3,	/* ``drive A22 and A23 signals on CE2 and CE1'' */
	RslotB=	1<<2,	/* select slot B (always, on MPC823) */
	Rwp=	1<<1,	/* write protect */
	Rvalid=	1<<0,	/* region valid */

	/*
	 *  configuration registers - they start at an offset in attribute
	 *  memory found in the CIS.
	 */
	Rconfig=	0,
	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */

	Maxctab=	8,		/* maximum configuration table entries */
	Nmap= 4,		// map entries per slot
	Mshift=	12,
	Mgran=	(1<<Mshift),	/* granularity of maps */
	Mmask=	~(Mgran-1),	/* mask for address bits important to the chip */
};

/* configuration table entry */
struct Conftab
{
	int	index;
	ushort	irqs;		/* legal irqs */
	uchar	irqtype;
	uchar	bit16;		/* true for 16 bit access */
	struct {
		ulong	start;
		ulong	len;
	} io[16];
	int	nio;
	uchar	vpp1;
	uchar	vpp2;
	uchar	memwait;
	ulong	maxwait;
	ulong	readywait;
	ulong	otherwait;
};

/*
 * Map between ISA memory space and PCMCIA card memory space.
 */
struct PCMmap {
	ulong	ca;			/* card address */
	ulong	cea;			/* card end address */
	ulong	isa;			/* ISA address */
	int	len;			/* length of the ISA area */
	int	attr;			/* attribute memory */
	int	ref;
};

/* a card slot */
struct Slot
{
	Lock;
	int	ref;

	long	memlen;		/* memory length */
	uchar	base;		/* index register base */
	uchar	slotno;		/* slot number */

	/* status */
	uchar	special;	/* in use for a special device */
	uchar	already;	/* already inited */
	uchar	occupied;
	uchar	voltage;
	uchar	battery;
	uchar	wrprot;
	uchar	powered;
	uchar	configed;
	uchar	enabled;
	uchar	busy;

	/* cis info */
	char	verstr[512];	/* version string */
	uchar	cpresent;	/* config registers present */
	ulong	caddr;		/* relative address of config registers */
	int	nctab;		/* number of config table entries */
	Conftab	ctab[Maxctab];
	Conftab	*def;		/* default conftab */

	/* for walking through cis */
	int	cispos;		/* current position scanning cis */
	uchar	*cisbase;

	/* memory maps */
	Lock	mlock;		/* lock down the maps */
	int	time;
	PCMmap	mmap[Nmap];	/* maps, last is always for the kernel */
};

enum
{
	Qdir,
	Qmem,
	Qattr,
	Qctl,
};

#define SLOTNO(c)	((c->qid.path>>8)&0xff)
#define TYPE(c)		(c->qid.path&0xff)
#define QID(s,t)	(((s)<<8)|(t))

static void pcmciaintr(Ureg *, void *);
static void increfp(Slot *pp);
static void decrefp(Slot *pp);
static PCMmap*	pcmmap(Slot*, ulong, int, int);
static void	pcmunmap(Slot*, PCMmap*);

static Slot	*slot;
static nslot;

static int
pcmgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp)
{
	int slotno;
	Qid qid;
	long len;
	Slot *pp;
	char name[NAMELEN];

	USED(tab, ntab);
	if(i>=3*nslot)
		return -1;
	slotno = i/3;
	pp = slot + slotno;
	len = 0;
	switch(i%3){
	case 0:
		qid.path = QID(slotno, Qmem);
		sprint(name, "pcm%dmem", slotno);
		len = pp->memlen;
		break;
	case 1:
		qid.path = QID(slotno, Qattr);
		sprint(name, "pcm%dattr", slotno);
		len = pp->memlen;
		break;
	case 2:
		qid.path = QID(slotno, Qctl);
		sprint(name, "pcm%dctl", slotno);
		break;
	}
	qid.vers = 0;
	devdir(c, qid, name, len, eve, 0660, dp);
	return 1;
}

/*
 *  set up for slot cards
 */
static void
pcmciareset(void)
{
	static int already;
	int i;
	IMM *io;
	Slot *pp;

	if(already)
		return;
	already = 1;
	
	nslot = 2;
	slot = xalloc(nslot * sizeof(Slot));

	io = m->iomem;

	for(i=0; i<8; i++){
		io->pcmr[i].option = 0;
		io->pcmr[i].base = 0;
	}

	for(i=0; i<2; i++)
		io->pgcr[i] = (1<<(31-PCMCIAlevel)) | (1<<(23-PCMCIAlevel));

	for(i = 0; i < nslot; i++){
		pp = slot + i;
		pp->slotno = i;
		pp->memlen = 512*1024;
	}

	/* 512K - slow - attr */
	io->pcmr[0].base = ISAMEM + 512*1024;
	io->pcmr[0].option = (0x1a<<27) | (8<<16) | (6<<12) | (24<<7)
		| Rport16 | Rattrib | Rvalid;
	// 64K io 
	io->pcmr[1].base = ISAMEM+0x300;
	io->pcmr[1].option = (0x6<<27) | (2<<16) | (4<<12) | (8<<7)
		| Rport16 | Rio | Rvalid;

//	intrenable(VectorPIC+PCMCIAlevel, pcmciaintr, 0, BUSUNKNOWN);
	io->pscr = 0xFEF0FEF0;	/* reset status */
	io->per = 0x00800000;	/* enable interrupts */

	print("pcmcia reset\n");
}

static Chan*
pcmciaattach(char *spec)
{
	return devattach('y', spec);
}

static int
pcmciawalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, pcmgen);
}

static void
pcmciastat(Chan *c, char *db)
{
	devstat(c, db, 0, 0, pcmgen);
}

static Chan*
pcmciaopen(Chan *c, int omode)
{
	if(c->qid.path == CHDIR){
		if(omode != OREAD)
			error(Eperm);
	} else
		increfp(slot + SLOTNO(c));
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

static void
pcmciaclose(Chan *c)
{
	if(c->flag & COPEN)
		if(c->qid.path != CHDIR)
			decrefp(slot+SLOTNO(c));
}

/* a memmove using only bytes */
static void
memmoveb(uchar *to, uchar *from, int n)
{
	while(n-- > 0)
		*to++ = *from++;
}

static long
pcmread(int slotno, int attr, void *a, long n, vlong offset)
{
	int i, len;
	PCMmap *m;
	ulong ka;
	uchar *ac;
	Slot *pp;

	pp = slot + slotno;
	if(pp->memlen < offset)
		return 0;
	if(pp->memlen < offset + n)
		n = pp->memlen - offset;

	m = 0;
	if(waserror()){
		if(m)
			pcmunmap(pp, m);
		nexterror();
	}

	ac = a;
	for(len = n; len > 0; len -= i){
		m = pcmmap(pp, offset, 0, attr);
		if(m == 0)
			error("can't map PCMCIA card");
		if(offset + len > m->cea)
			i = m->cea - offset;
		else
			i = len;
		ka = KZERO|(m->isa + offset - m->ca);
		memmoveb(ac, (void*)ka, i);
		pcmunmap(pp, m);
		offset += i;
		ac += i;
	}
	poperror();
	return n;
}

static long
pcmciaread(Chan *c, void *a, long n, vlong offset)
{
	char *cp, *buf;
	ulong p;
	Slot *pp;

	p = TYPE(c);
	switch(p){
	case Qdir:
		return devdirread(c, a, n, 0, 0, pcmgen);
	case Qmem:
	case Qattr:
		return pcmread(SLOTNO(c), p==Qattr, a, n, offset);
	case Qctl:
		buf = malloc(2048);
		if(buf == nil)
			error(Enomem);
		if(waserror()){
			free(buf);
			nexterror();
		}
		cp = buf;
		pp = slot + SLOTNO(c);
		if(pp->occupied)
			cp += sprint(cp, "occupied\n");
		if(pp->enabled)
			cp += sprint(cp, "enabled\n");
		if(pp->powered)
			cp += sprint(cp, "powered\n");
		if(pp->configed)
			cp += sprint(cp, "configed\n");
		if(pp->wrprot)
			cp += sprint(cp, "write protected\n");
		if(pp->busy)
			cp += sprint(cp, "busy\n");
		cp += sprint(cp, "battery lvl %d\n", pp->battery);
		cp += sprint(cp, "voltage select %d\n", pp->voltage);
		cp += sprint(cp, "pipr %ux\n", m->iomem->pipr);
		cp += sprint(cp, "pcsr %ux\n", m->iomem->pscr);
		*cp = 0;
		n = readstr(offset, a, n, buf);
		poperror();
		free(buf);
		break;
	default:
		n=0;
		break;
	}
	return n;
}

static long
pcmwrite(int dev, int attr, void *a, long n, vlong offset)
{
	int i, len;
	PCMmap *m;
	ulong ka;
	uchar *ac;
	Slot *pp;

	pp = slot + dev;
	if(pp->memlen < offset)
		return 0;
	if(pp->memlen < offset + n)
		n = pp->memlen - offset;

#ifdef xxx
	m = 0;
	if(waserror()){
		if(m)
			pcmunmap(pp->slotno, m);
		nexterror();
	}

	ac = a;
	for(len = n; len > 0; len -= i){
		m = pcmmap(pp, offset, 0, attr);
		if(m == 0)
			error("can't map PCMCIA card");
		if(offset + len > m->cea)
			i = m->cea - offset;
		else
			i = len;
		ka = KZERO|(m->isa + offset - m->ca);
		memmoveb((void*)ka, ac, i);
		pcmunmap(pp->slotno, m);
		offset += i;
		ac += i;
	}

	poperror();
#endif

	return n;
}

static long
pcmciawrite(Chan *c, void *a, long n, vlong offset)
{
	ulong p;
	Slot *pp;
	char buf[32];

	p = TYPE(c);
	switch(p){
	case Qctl:
		if(n >= sizeof(buf))
			n = sizeof(buf) - 1;
		strncpy(buf, a, n);
		buf[n] = 0;
		pp = slot + SLOTNO(c);
		if(!pp->occupied)
			error(Eio);

		break;
	case Qmem:
	case Qattr:
		pp = slot + SLOTNO(c);
		if(pp->occupied == 0 || pp->enabled == 0)
			error(Eio);
		n = pcmwrite(pp->slotno, p == Qattr, a, n, offset);
		if(n < 0)
			error(Eio);
		break;
	default:
		error(Ebadusefd);
	}
	return n;
}

Dev pcmciadevtab = {
	'y',
	"pcmcia",

	pcmciareset,
	devinit,
	pcmciaattach,
	devclone,
	pcmciawalk,
	pcmciastat,
	pcmciaopen,
	devcreate,
	pcmciaclose,
	pcmciaread,
	devbread,
	pcmciawrite,
	devbwrite,
	devremove,
	devwstat,
};

static void
pcmciaintr(Ureg *, void *)
{
	print("pcmciaintr\n");
}

/*
 *  get a map for pc card region, return corrected len
 */
static PCMmap*
pcmmap(Slot *pp, ulong offset, int len, int attr)
{
	PCMmap *m;
	ulong e;

	if(attr == 0)
		panic("pcmmap");

	/* convert offset to granularity */
	if(len <= 0)
		len = 1;
	e = ROUND(offset+len, Mgran);
	offset &= Mmask;
	len = e - offset;

	m = pp->mmap;

	m->ca = 0;
	m->cea = 512*1024;
	m->isa = ISAMEM + 512*1024;
	m->len = 512*1024;
	m->attr = attr;
	m->ref = 1;

	return m;
}

static void
pcmunmap(Slot *pp, PCMmap *m)
{
}

static int
pcmio(int slotno, ISAConf *isa)
{
	uchar we, x, *p;
	Slot *pp;
	Conftab *ct, *et, *t;
	PCMmap *m;
	int i, index, irq;
	char *cp;

	isa->irq = VectorPIC+PCMCIAlevel;
	irq = isa->irq;

	if(slotno > nslot)
		return -1;
	pp = slot + slotno;

	if(!pp->occupied)
		return -1;

	et = &pp->ctab[pp->nctab];

	ct = 0;
	for(i = 0; i < isa->nopt; i++){
		if(strncmp(isa->opt[i], "index=", 6))
			continue;
		index = strtol(&isa->opt[i][6], &cp, 0);
		if(cp == &isa->opt[i][6] || index >= pp->nctab)
			return -1;
		ct = &pp->ctab[index];
	}

	if(ct == 0){
	
		/* assume default is right */
		if(pp->def)
			ct = pp->def;
		else
			ct = pp->ctab;
	
		/* try for best match */
		if(ct->nio == 0
		|| ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
			for(t = pp->ctab; t < et; t++)
				if(t->nio
				&& t->io[0].start == isa->port
				&& ((1<<irq) & t->irqs)){
					ct = t;
					break;
				}
		}
		if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
			for(t = pp->ctab; t < et; t++)
				if(t->nio && ((1<<irq) & t->irqs)){
					ct = t;
					break;
				}
		}
		if(ct->nio == 0){
			for(t = pp->ctab; t < et; t++)
				if(t->nio){
					ct = t;
					break;
				}
		}
	}

	if(ct == et || ct->nio == 0)
		return -1;

	/* enable io port map 0 */
	if(isa->port == 0)
		isa->port = ct->io[0].start;
	if(isa->port == 0 && ct->io[0].start == 0)
		return -1;

	if(pp->cpresent & (1<<Rconfig)){
		/*  Reset adapter */
		m = pcmmap(pp, pp->caddr + Rconfig, 1, 1);
		p = KADDR(m->isa + pp->caddr + Rconfig - m->ca);

		/* set configuration and interrupt type */
		x = 1;
		x |= ct->index<<1;
		if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7)) {
			x |= Clevel;
		}
		*p = x;
		delay(5);
		pcmunmap(pp, m);
	}
	return 0;
}

/*
 *  look for a card whose version contains 'idstr'
 */
int
pcmspecial(char *idstr, ISAConf *isa)
{
	Slot *pp;
	extern char *strstr(char*, char*);

	for(pp = slot; pp < slot+nslot; pp++){
		if(pp->special)
			continue;	/* already taken */
		increfp(pp);

		if(pp->occupied)
		if(strstr(pp->verstr, idstr))
		if(isa == 0 || pcmio(pp->slotno, isa) == 0){
			pp->special = 1;
			return pp->slotno;
		}

		decrefp(pp);
	}
	return -1;
}

void
pcmspecialclose(int slotno)
{
	Slot *pp;

	if(slotno >= nslot)
		panic("pcmspecialclose");
	pp = slot + slotno;
	pp->special = 0;
	decrefp(pp);
}

static void
slotinfo(Slot *pp)
{
	ulong pipr;

	pipr = m->iomem->pipr;
	if(pp->slotno == 0)
		pipr >>= 16;
	pp->occupied = (pipr&(Ccd1|Ccd2))==0;
	pp->battery = (pipr & (Cbvd1|Cbvd2)) != 0;
	pp->voltage = ((pipr & Cvs1) != 0) | (((pipr & Cvs2) != 0)<<1);
	pp->wrprot = (pipr&Cwp)==0;
	pp->busy = (pipr&Crdy)==0;
	pp->powered = 1;	// assume power is always on for the momment
}

/*
 *  read and crack the card information structure enough to set
 *  important parameters like power
 */
static void	tcfig(Slot*, int);
static void	tentry(Slot*, int);
static void	tvers1(Slot*, int);

static void (*parse[256])(Slot*, int) =
{
[0x15]	tvers1,
[0x1A]	tcfig,
[0x1B]	tentry,
};

static int
readc(Slot *pp, uchar *x)
{
	if(pp->cispos >= Mgran)
		return 0;
	*x = pp->cisbase[2*pp->cispos];
	pp->cispos++;
	return 1;
}

static void
cisread(Slot *pp)
{
	uchar link;
	uchar type;
	int this, i;
	PCMmap *m;

	memset(pp->ctab, 0, sizeof(pp->ctab));
	pp->caddr = 0;
	pp->cpresent = 0;
	pp->configed = 0;
	pp->nctab = 0;

	m = pcmmap(pp, 0, 0, 1);
	if(m == 0)
		return;
	pp->cisbase = KADDR(m->isa);
	pp->cispos = 0;

	/* loop through all the tuples */
	for(i = 0; i < 1000; i++){
		this = pp->cispos;
		if(readc(pp, &type) != 1)
			break;
		if(type == 0xFF)
			break;
		if(readc(pp, &link) != 1)
			break;
		if(parse[type])
			(*parse[type])(pp, type);
		if(link == 0xff)
			break;
		pp->cispos = this + (2+link);
	}
	pcmunmap(pp, m);
}

static ulong
getlong(Slot *pp, int size)
{
	uchar c;
	int i;
	ulong x;

	x = 0;
	for(i = 0; i < size; i++){
		if(readc(pp, &c) != 1)
			break;
		x |= c<<(i*8);
	}
	return x;
}

static void
tcfig(Slot *pp, int )
{
	uchar size, rasize, rmsize;
	uchar last;

	if(readc(pp, &size) != 1)
		return;
	rasize = (size&0x3) + 1;
	rmsize = ((size>>2)&0xf) + 1;
	if(readc(pp, &last) != 1)
		return;
	pp->caddr = getlong(pp, rasize);
	pp->cpresent = getlong(pp, rmsize);
}

/*
 *  enable the slot card
 */
static void
slotena(Slot *pp)
{
	IMM *io;
	int shift;

	if(pp->enabled)
		return;
	shift = (pp->slotno == 0)?16:0;

	io = m->iomem;
//	io->pgcr[pp->slotno] |= Coe;
	io->pgcr[pp->slotno] &= ~Coe;	// active low
	delay(300);
	io->pgcr[pp->slotno] |= Creset;
	delay(100);
	io->pgcr[pp->slotno] &= ~Creset;
	delay(500);
#ifdef XXX
	eieio();
	delay(300);
	io->pgcrb |= Cbreset;	/* TO DO: active high? active low? */
	eieio();
	delay(100);
	io->pgcrb &= ~Cbreset;
	eieio();
	delay(500);
#endif
	/* get configuration */
	slotinfo(pp);
	if(pp->occupied){
		cisread(pp);
		pp->enabled = 1;
	}
}

/*
 *  disable the slot card
 */
static void
slotdis(Slot *pp)
{
	pp->enabled = 0;
}

static void
increfp(Slot *pp)
{
	lock(pp);
	if(pp->ref++ == 0)
		slotena(pp);
	unlock(pp);
}

static void
decrefp(Slot *pp)
{
	lock(pp);
	if(pp->ref-- == 1)
		slotdis(pp);
	unlock(pp);
}

static ulong vexp[8] =
{
	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
};
static ulong vmant[16] =
{
	10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
};

static ulong
microvolt(Slot *pp)
{
	uchar c;
	ulong microvolts;
	ulong exp;

	if(readc(pp, &c) != 1)
		return 0;
	exp = vexp[c&0x7];
	microvolts = vmant[(c>>3)&0xf]*exp;
	while(c & 0x80){
		if(readc(pp, &c) != 1)
			return 0;
		switch(c){
		case 0x7d:
			break;		/* high impedence when sleeping */
		case 0x7e:
		case 0x7f:
			microvolts = 0;	/* no connection */
			break;
		default:
			exp /= 10;
			microvolts += exp*(c&0x7f);
		}
	}
	return microvolts;
}

static ulong
nanoamps(Slot *pp)
{
	uchar c;
	ulong nanoamps;

	if(readc(pp, &c) != 1)
		return 0;
	nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf];
	while(c & 0x80){
		if(readc(pp, &c) != 1)
			return 0;
		if(c == 0x7d || c == 0x7e || c == 0x7f)
			nanoamps = 0;
	}
	return nanoamps;
}

/*
 *  only nominal voltage is important for config
 */
static ulong
power(Slot *pp)
{
	uchar feature;
	ulong mv;

	mv = 0;
	if(readc(pp, &feature) != 1)
		return 0;
	if(feature & 1)
		mv = microvolt(pp);
	if(feature & 2)
		microvolt(pp);
	if(feature & 4)
		microvolt(pp);
	if(feature & 8)
		nanoamps(pp);
	if(feature & 0x10)
		nanoamps(pp);
	if(feature & 0x20)
		nanoamps(pp);
	if(feature & 0x40)
		nanoamps(pp);
	return mv/1000000;
}

static ulong mantissa[16] =
{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, };

static ulong exponent[8] =
{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, };

static ulong
ttiming(Slot *pp, int scale)
{
	uchar unscaled;
	ulong nanosecs;

	if(readc(pp, &unscaled) != 1)
		return 0;
	nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
	nanosecs = nanosecs * vexp[scale];
	return nanosecs;
}

static void
timing(Slot *pp, Conftab *ct)
{
	uchar c, i;

	if(readc(pp, &c) != 1)
		return;
	i = c&0x3;
	if(i != 3)
		ct->maxwait = ttiming(pp, i);		/* max wait */
	i = (c>>2)&0x7;
	if(i != 7)
		ct->readywait = ttiming(pp, i);		/* max ready/busy wait */
	i = (c>>5)&0x7;
	if(i != 7)
		ct->otherwait = ttiming(pp, i);		/* reserved wait */
}

static void
iospaces(Slot *pp, Conftab *ct)
{
	uchar c;
	int i, nio;

	ct->nio = 0;
	if(readc(pp, &c) != 1)
		return;

	ct->bit16 = ((c>>5)&3) >= 2;
	if(!(c & 0x80)){
		ct->io[0].start = 0;
		ct->io[0].len = 1<<(c&0x1f);
		ct->nio = 1;
		return;
	}

	if(readc(pp, &c) != 1)
		return;

	nio = (c&0xf)+1;
	for(i = 0; i < nio; i++){
		ct->io[i].start = getlong(pp, (c>>4)&0x3);
		ct->io[0].len = getlong(pp, (c>>6)&0x3);
	}
	ct->nio = nio;
}

static void
irq(Slot *pp, Conftab *ct)
{
	uchar c;

	if(readc(pp, &c) != 1)
		return;
	ct->irqtype = c & 0xe0;
	if(c & 0x10)
		ct->irqs = getlong(pp, 2);
	else
		ct->irqs = 1<<(c&0xf);
	ct->irqs &= 0xDEB8;		/* levels available to card */
}

static void
memspace(Slot *pp, int asize, int lsize, int host)
{
	ulong haddress, address, len;

	len = getlong(pp, lsize)*256;
	address = getlong(pp, asize)*256;
	USED(len, address);
	if(host){
		haddress = getlong(pp, asize)*256;
		USED(haddress);
	}
}

static void
tentry(Slot *pp, int )
{
	uchar c, i, feature;
	Conftab *ct;

	if(pp->nctab >= Maxctab)
		return;
	if(readc(pp, &c) != 1)
		return;
	ct = &pp->ctab[pp->nctab++];

	/* copy from last default config */
	if(pp->def)
		*ct = *pp->def;

	ct->index = c & 0x3f;

	/* is this the new default? */
	if(c & 0x40)
		pp->def = ct;

	/* memory wait specified? */
	if(c & 0x80){
		if(readc(pp, &i) != 1)
			return;
		if(i&0x80)
			ct->memwait = 1;
	}

	if(readc(pp, &feature) != 1)
		return;
	switch(feature&0x3){
	case 1:
		ct->vpp1 = ct->vpp2 = power(pp);
		break;
	case 2:
		power(pp);
		ct->vpp1 = ct->vpp2 = power(pp);
		break;
	case 3:
		power(pp);
		ct->vpp1 = power(pp);
		ct->vpp2 = power(pp);
		break;
	default:
		break;
	}
	if(feature&0x4)
		timing(pp, ct);
	if(feature&0x8)
		iospaces(pp, ct);
	if(feature&0x10)
		irq(pp, ct);
	switch((feature>>5)&0x3){
	case 1:
		memspace(pp, 0, 2, 0);
		break;
	case 2:
		memspace(pp, 2, 2, 0);
		break;
	case 3:
		if(readc(pp, &c) != 1)
			return;
		for(i = 0; i <= (c&0x7); i++)
			memspace(pp, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
		break;
	}
	pp->configed++;
}

static void
tvers1(Slot *pp, int )
{
	uchar c, major, minor;
	int  i;

	if(readc(pp, &major) != 1)
		return;
	if(readc(pp, &minor) != 1)
		return;
	for(i = 0; i < sizeof(pp->verstr)-1; i++){
		if(readc(pp, &c) != 1)
			return;
		if(c == 0)
			c = '\n';
		if(c == 0xff)
			break;
		pp->verstr[i] = c;
	}
	pp->verstr[i] = 0;
}

M mpc/devrtc.c => mpc/devrtc.c +30 -18
@@ 7,16 7,9 @@

#include	"io.h"

/*
 * MPC8xx real time clock
 * FADS board option switch
 * interrupt statistics
 */

enum{
	Qrtc = 1,
	Qswitch,
	Qintstat,
	Qnvram,

	/* sccr */
	RTDIV=	1<<24,


@@ 25,14 18,18 @@ enum{
	/* rtcsc */
	RTE=	1<<0,
	R38K=	1<<4,

	Nvoff=		4*1024,	/* where usable nvram lives */
	Nvsize=		4*1024,
};

static	QLock	rtclock;		/* mutex on clock operations */
static Lock nvrtlock;


static Dirtab rtcdir[]={
	"rtc",		{Qrtc, 0},	12,	0666,
	"switch",	{Qswitch, 0}, 0, 0444,
	"intstat",	{Qintstat, 0}, 0, 0444,
	"nvram",	{Qnvram, 0},	Nvsize,	0664,
};
#define	NRTC	(sizeof(rtcdir)/sizeof(rtcdir[0]))



@@ 79,9 76,8 @@ rtcopen(Chan *c, int omode)
		if(strcmp(up->user, eve)!=0 && omode!=OREAD)
			error(Eperm);
		break;
	case Qswitch:
	case Qintstat:
		if(omode!=OREAD)
	case Qnvram:
		if(strcmp(up->user, eve)!=0)
			error(Eperm);
		break;
	}


@@ 97,7 93,6 @@ static long
rtcread(Chan *c, void *buf, long n, vlong offset)
{
	ulong t;
//	char *b;

	if(c->qid.path & CHDIR)
		return devdirread(c, buf, n, rtcdir, NRTC, devgen);


@@ 107,8 102,16 @@ rtcread(Chan *c, void *buf, long n, vlong offset)
		t = m->iomem->rtc;
		n = readnum(offset, buf, n, t, 12);
		return n;
	case Qswitch:
		return readnum(offset, buf, n, 0xf/*(m->bcsr[2]>>19)&0xF*/, 12);
	case Qnvram:
		if(offset >= Nvsize)
			return 0;
		t = offset;
		if(t + n > Nvsize)
			n = Nvsize - t;
		ilock(&nvrtlock);
		memmove(buf, (uchar*)(NVRAMMEM + Nvoff + t), n);
		iunlock(&nvrtlock);
		return n;
	}
	error(Egreg);
	return 0;		/* not reached */


@@ 120,6 123,7 @@ rtcwrite(Chan *c, void *buf, long n, vlong offset)
	ulong secs;
	char *cp, *ep;
	IMM *io;
	ulong t;

	switch(c->qid.path){
	case Qrtc:


@@ 145,8 149,16 @@ rtcwrite(Chan *c, void *buf, long n, vlong offset)
		io->rtck = ~KEEP_ALIVE_KEY;
		iopunlock();
		return n;
	case Qswitch:
		return 0;
	case Qnvram:
		if(offset >= Nvsize)
			return 0;
		t = offset;
		if(t + n > Nvsize)
			n = Nvsize - t;
		ilock(&nvrtlock);
		memmove((uchar*)(NVRAMMEM + Nvoff + offset), buf, n);
		iunlock(&nvrtlock);
		return n;
	}
	error(Egreg);
	return 0;		/* not reached */

A mpc/devsac.c => mpc/devsac.c +647 -0
@@ 0,0 1,647 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

/*
 * Rather than reading /adm/users, which is a lot of work for
 * a toy program, we assume all groups have the form
 *	NNN:user:user:
 * meaning that each user is the leader of his own group.
 */

enum
{
	OPERM	= 0x3,		/* mask of all permission types in open mode */
	Nram	= 512,
};

typedef struct SacPath SacPath;
typedef struct Sac Sac;
typedef struct SacHeader SacHeader;
typedef struct SacDir SacDir;

enum {
	Magic = 0x5acf5,
};

struct SacDir
{
	char	name[NAMELEN];
	char	uid[NAMELEN];
	char	gid[NAMELEN];
	uchar	qid[4];
	uchar	mode[4];
	uchar	atime[4];
	uchar	mtime[4];
	uchar	length[8];
	uchar	blocks[8];
};

struct SacHeader
{
	uchar	magic[4];
	uchar	length[8];
	uchar	blocksize[4];
	uchar	md5[16];
};


struct Sac
{
	SacDir;
	SacPath *path;
};

struct SacPath
{
	Ref;
	SacPath *up;
	vlong blocks;
	int entry;
};

enum
{
	Pexec =		1,
	Pwrite = 	2,
	Pread = 	4,
	Pother = 	1,
	Pgroup = 	8,
	Powner =	64,
};

uchar *data = SACMEM;
int blocksize;
Sac root;

void	sacstat(SacDir*, char*);
void	io(void);
void	usage(void);
ulong	getl(void *p);
vlong	getv(void *p);
void	init(char*);
Sac	*saccpy(Sac *s);
Sac *saclookup(Sac *s, char *name);
int sacdirread(Sac *s, char *p, long off, long cnt);
void loadblock(void *buf, uchar *offset, int blocksize);
void sacfree(Sac*);

void
devinit(void)
{
	SacHeader *hdr;
	hdr = (SacHeader*)data;
	if(getl(hdr->magic) != Magic) {
print("devsac: bad magic");
		return;
	}
	blocksize = getl(hdr->blocksize);
	root.SacDir = *(SacDir*)(data + sizeof(SacHeader));
}

static Chan*
sacattach(char* spec)
{
	Chan *c;
	int dev;

	dev = atoi(spec);
	if(dev != 0)
		error("bad specification");

	// check if init found sac file system in memory
	if(blocksize == 0)
		error("devsac: bad magic");

	c = devattach('C', spec);
	c->qid = (Qid){getl(root.qid), 0};
	c->dev = dev;
	c->aux = saccpy(&root);
	return c;
}

Chan*
sacclone(Chan *c, Chan *nc)
{
	nc = devclone(c, nc);
	nc->aux = saccpy(c->aux);
	return nc;
}

int
devwalk(Chan *c, char *name, Dirtab *tab, int ntab, Devgen *gen)
{
	Sac *sac;

	isdir(c);
	sac = c->aux;
	if(strcmp(name, ".") == 0)
		return 1;
	sac = saclookup(sac, name);
	if(sac == nil) {
		strncpy(up->error, Enonexist, NAMELEN);
		return 0;
	}
	c->aux = sac;
	c->qid = (Qid){getl(sac->qid), 0};
	op = c->path;
	c->path = ptenter(&syspt, op, name);
	decref(op);
	return 1;
}

char *
ropen(Fid *f)
{
	int mode, trunc;

	if(f->open)
		return Eisopen;
	if(f->busy == 0)
		return Enotexist;
	mode = rhdr.mode;
	if(f->qid.path & CHDIR){
		if(mode != OREAD)
			return Eperm;
		thdr.qid = f->qid;
		return 0;
	}
	if(mode & ORCLOSE)
		return Erdonly;
	trunc = mode & OTRUNC;
	mode &= OPERM;
	if(mode==OWRITE || mode==ORDWR || trunc)
		return Erdonly;
	if(mode==OREAD)
		if(!perm(f, f->sac, Pread))
			return Eperm;
	if(mode==OEXEC)
		if(!perm(f, f->sac, Pexec))
			return Eperm;
	thdr.qid = f->qid;
	f->open = 1;
	return 0;
}

char *
rcreate(Fid *f)
{
	if(f->open)
		return Eisopen;
	if(f->busy == 0)
		return Enotexist;
	return Erdonly;
}

char*
rread(Fid *f)
{
	Sac *sac;
	char *buf, *buf2;
	long off;
	int n, cnt, i, j;
	uchar *blocks;
	vlong length;

	if(f->busy == 0)
		return Enotexist;
	sac = f->sac;
	thdr.count = 0;
	off = rhdr.offset;
	buf = thdr.data;
	cnt = rhdr.count;
	if(f->qid.path & CHDIR){
		cnt = (rhdr.count/DIRLEN)*DIRLEN;
		if(off%DIRLEN)
			return "i/o error";
		thdr.count = sacdirread(sac, buf, off, cnt);
		return 0;
	}
	length = getv(sac->length);
	if(off >= length) {
		rhdr.count = 0;
		return 0;
	}
	if(cnt > length-off)
		cnt = length-off;
	thdr.count = cnt;
	if(cnt == 0)
		return 0;
	blocks = data + getv(sac->blocks);
	buf2 = malloc(blocksize);
	while(cnt > 0) {
		i = off/blocksize;
		loadblock(buf2, blocks+i*8, blocksize);
		j = off-i*blocksize;
		n = blocksize-j;
		if(n > cnt)
			n = cnt;
		memmove(buf, buf2+j, n);
		cnt -= n;
		off += n;
	}
	free(buf2);
	return 0;
}

char*
sacwrite(Fid *f)
{
	if(f->busy == 0)
		return Enotexist;
	return Erdonly;
}

char *
rclunk(Fid *f)
{
	f->busy = 0;
	f->open = 0;
	free(f->user);
	sacfree(f->sac);
	return 0;
}

char *
rremove(Fid *f)
{
	f->busy = 0;
	f->open = 0;
	free(f->user);
	sacfree(f->sac);
	return Erdonly;
}

char *
rstat(Fid *f)
{
	if(f->busy == 0)
		return Enotexist;
	sacstat(f->sac, thdr.stat);
	return 0;
}

char *
rwstat(Fid *f)
{
	if(f->busy == 0)
		return Enotexist;
	return Erdonly;
}

Sac*
saccpy(Sac *s)
{
	Sac *ss;
	
	ss = malloc(sizeof(Sac));
	*ss = *s;
	if(ss->path)
		incref(ss->path);
	return ss;
}

SacPath *
sacpathalloc(SacPath *p, vlong blocks, int entry)
{
	SacPath *pp = malloc(sizeof(SacPath));
	pp->ref = 1;
	pp->blocks = blocks;
	pp->entry = entry;
	pp->up = p;
	return pp;
}

static void
sacpathfree(SacPath *p)
{
	if(p == nil)
		return;
	if(decref(p) > 0)
		return;
	sacpathfree(p->up);
	free(p);
}


void
sacfree(Sac *s)
{
	sacpathfree(s->path);
	free(s);
}

void
sacstat(SacDir *s, char *buf)
{
	Dir dir;

	memmove(dir.name, s->name, NAMELEN);
	dir.qid = (Qid){getl(s->qid), 0};
	dir.mode = getl(s->mode);
	dir.length = getv(s->length);
	if(dir.mode &CHDIR)
		dir.length *= DIRLEN;
	strcpy(dir.uid, s->uid);
	strcpy(dir.gid, s->gid);
	dir.atime = getl(s->atime);
	dir.mtime = getl(s->mtime);
	convD2M(&dir, buf);
}

void
loadblock(void *buf, uchar *offset, int blocksize)
{
	vlong block, n;

	block = getv(offset);
	if(block < 0) {
		block = -block;
		n = getv(offset+8);
		if(n < 0)
			n = -n;
		n -= block;
//fprint(2, "blocksize = %d, block = %lld n = %lld\n", blocksize, block, n);
		if(unsac(buf, data+block, blocksize, n)<0)
			panic("unsac failed!");
	} else {
		memmove(buf, data+block, blocksize);
	}
}

Sac*
sacparent(Sac *s)
{
	uchar *blocks;
	SacDir *buf;
	int per, i;
	SacPath *p;

	p = s->path;
	if(p == nil || p->up == nil) {
		pathfree(p);
		*s = root;
		return s;
	}
	p = p->up;

//fprint(2, "sacparent = %lld %d\n", p->blocks, p->entry);
	blocks = data + p->blocks;
	per = blocksize/sizeof(SacDir);
	i = p->entry/per;
	buf = malloc(per*sizeof(SacDir));
	loadblock(buf, blocks + i*8, per*sizeof(SacDir));
	s->SacDir = buf[p->entry-i*per];
//fprint(2, "sacparent = %s\n", s->name);
	free(buf);
	incref(p);
	pathfree(s->path);
	s->path = p;
	return s;
}

int
sacdirread(Sac *s, char *p, long off, long cnt)
{
	uchar *blocks;
	SacDir *buf;
	int iblock, per, i, j, ndir;

	blocks = data + getv(s->blocks);
	per = blocksize/sizeof(SacDir);
	ndir = getv(s->length);
	off /= DIRLEN;
	cnt /= DIRLEN;
	if(off >= ndir)
		return 0;
	if(cnt > ndir-off)
		cnt = ndir-off;
	iblock = -1;
	buf = malloc(per*sizeof(SacDir));
	for(i=off; i<off+cnt; i++) {
		j = i/per;
		if(j != iblock) {
			loadblock(buf, blocks + j*8, per*sizeof(SacDir));
			iblock = j;
		}
		j *= per;
		sacstat(buf+i-j, p);
		p += DIRLEN;
	}
	free(buf);
	return cnt*DIRLEN;
}

Sac*
saclookup(Sac *s, char *name)
{
	int ndir;
	int top, bot, i, j, k, per;
	uchar *blocks;
	SacDir *buf;
	int iblock;
	SacDir *sd;
	
	if(strcmp(name, "..") == 0)
		return sacparent(s);
	blocks = data + getv(s->blocks);
	per = blocksize/sizeof(SacDir);
	ndir = getv(s->length);
	buf = malloc(per*sizeof(SacDir));
	iblock = -1;

	if(1) {
		// linear search
		for(i=0; i<ndir; i++) {
			j = i/per;
			if(j != iblock) {
				loadblock(buf, blocks + j*8, per*sizeof(SacDir));
				iblock = j;
			}
			j *= per;
			sd = buf+i-j;
			k = strcmp(name, sd->name);
			if(k == 0) {
//print("walk %s %lld %d\n", name, getv(s->blocks), i);
				s->path = sacpathalloc(s->path, getv(s->blocks), i);
				s->SacDir = *sd;
				free(buf);
				return s;
			}
		}
		free(buf);
		return 0;
	}

	// binary search
	top = ndir;
	bot = 0;
	while(bot != top){
if(bot>top)
sysfatal("binary serach failed: %d %d\n", bot, top);
		i = (bot+top)>>1;
		j = i/per;
		if(j != iblock) {
			loadblock(buf, blocks + j*8, per*sizeof(SacDir));
			iblock = j;
		}
		j *= per;
		sd = buf+i-j;
		k = strcmp(name, sd->name);
		if(k == 0) {
			s->path = sacpathalloc(s->path, getv(s->blocks), i);
			s->SacDir = *sd;
			free(buf);
		}
		if(k < 0) {
			top = i;
			sd = buf;
			if(strcmp(name, sd->name) < 0)
				top = j;
		} else {
			bot = i+1;
			if(ndir-j < per)
				i = ndir-j;
			else
				i = per;
			sd = buf+i-1;
			if(strcmp(name, sd->name) > 0)
				bot = j+i;
		}
	}
	return 0;
}

Fid *
newfid(int fid)
{
	Fid *f, *ff;

	ff = 0;
	for(f = fids; f; f = f->next)
		if(f->fid == fid)
			return f;
		else if(!ff && !f->busy)
			ff = f;
	if(ff){
		ff->fid = fid;
		return ff;
	}
	f = malloc(sizeof *f);
//fprint(2, "newfid\n");
	memset(f, 0 , sizeof(Fid));
	f->fid = fid;
	f->next = fids;
	fids = f;
	return f;
}

void
io(void)
{
	char *err;
	int n;

	for(;;){
		/*
		 * reading from a pipe or a network device
		 * will give an error after a few eof reads
		 * however, we cannot tell the difference
		 * between a zero-length read and an interrupt
		 * on the processes writing to us,
		 * so we wait for the error
		 */
		n = read(mfd[0], mdata, sizeof mdata);
		if(n == 0)
			continue;
		if(n < 0)
			error("mount read");
		if(convM2S(mdata, &rhdr, n) == 0)
			continue;

		if(debug)
			fprint(2, "sacfs:<-%F\n", &rhdr);

		thdr.data = mdata + MAXMSG;
		if(!fcalls[rhdr.type])
			err = "bad fcall type";
		else
			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
		if(err){
			thdr.type = Rerror;
			strncpy(thdr.ename, err, ERRLEN);
		}else{
			thdr.type = rhdr.type + 1;
			thdr.fid = rhdr.fid;
		}
		thdr.tag = rhdr.tag;
		if(debug)
			fprint(2, "ramfs:->%F\n", &thdr);/**/
		n = convS2M(&thdr, mdata);
		if(write(mfd[1], mdata, n) != n)
			error("mount write");
	}
}

int
perm(Fid *f, Sac *s, int p)
{
	ulong perm = getl(s->mode);
	if((p*Pother) & perm)
		return 1;
	if(strcmp(f->user, s->gid)==0 && ((p*Pgroup) & perm))
		return 1;
	if(strcmp(f->user, s->uid)==0 && ((p*Powner) & perm))
		return 1;
	return 0;
}


ulong
getl(void *p)
{
	uchar *a = p;

	return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
}

vlong
getv(void *p)
{
	uchar *a = p;
	ulong l0, l1;
	vlong v;

	l0 = (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
	a += 4;
	l1 = (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
	
	v = l0;
	v <<= 32;
	v |= l1;
	return v;
}

Dev sacdevtab = {
	'C',
	"sac",

	devreset,
	sacinit,
	sacattach,
	sacclone,
	sacwalk,
	sacstat,
	sacopen,
	devcreate,
	sacclose,
	sacread,
	devbread,
	sacwrite,
	devbwrite,
	devremove,
	devwstat,
};

M mpc/devuart.c => mpc/devuart.c +27 -11
@@ 11,7 11,7 @@
enum {
	FADS823 = 1,

	Rbufsize=	512,
	Rbufsize=	32,	// read buffer size
	Nuart=	3,	/* max per machine (SMC1, SMC2, SCC2) */
	CTLS=	's'&037,
	CTLQ=	'q'&037,


@@ 188,8 188,6 @@ struct Uart
	Queue	*iq;
	Queue	*oq;

	/* staging areas to avoid some of the per character costs */
	uchar	istage[Rbufsize][2];	/* double buffered */
	int	rdrx;	/* last buffer read */

	Lock	plock;		/* for output variables */


@@ 230,12 228,22 @@ uartsetbuf(Uart *up)
{
	IOCparam *p;
	BD *bd;
	int n;
	uchar *buf;

	p = up->param;
	p->rfcr = 0x18;
	p->tfcr = 0x18;
	p->mrblr = Rbufsize;

	// allocate buffer for receive
	// the buffers need to be CACHELINESZ aligned and
	// a multiple of CACHELINESZ long in order for
	// the manul cache coherence to work
	n = 2*ROUND(Rbufsize, CACHELINESZ)+CACHELINESZ-1;
	buf = malloc(n);
	buf = (uchar*)ROUND((long)buf, CACHELINESZ);
	
	if((bd = up->rxb) == nil){
		bd = bdalloc(2);
		up->rxb = bd;


@@ 243,12 251,12 @@ uartsetbuf(Uart *up)
	p->rbase = (ushort)bd;
	bd->status = BDEmpty|BDInt;
	bd->length = 0;
	bd->addr = PADDR(up->istage[0]);
	bd->addr = PADDR(buf);
	bd++;
	bd->status = BDEmpty|BDInt|BDWrap;
	bd->length = 0;
	bd->addr = PADDR(up->istage[1]);
	dcflush(up->istage, sizeof(up->istage));
	bd->addr = PADDR(buf + ROUND(Rbufsize, CACHELINESZ));
	dcflush(buf, 2*ROUND(Rbufsize, CACHELINESZ));
	up->rdrx = 0;

	if((bd = up->txb) == nil){


@@ 288,7 296,7 @@ smcsetup(Uart *up)
	/* SMC protocol parameters */
	p = (Uartsmc*)up->param;
	up->brkcr = &p->brkcr;
	p->maxidl = 1;
	p->maxidl = 10;
	p->brkln = 0;
	p->brkec = 0;
	p->brkcr = 1;


@@ 892,6 900,8 @@ uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int
	p->opens++;
}

int lastc = 'x';

static int
uartinput(Uart *p, BD *bd)
{


@@ 922,8 932,10 @@ uartinput(Uart *p, BD *bd)
				}
				/* BUG? should discard on/off char? */
			}
			if(p->putc)
			if(p->putc) {
				(*p->putc)(p->iq, ch);
				lastc = ch;
			}
		}
	}
	if(p->putc == nil && l > 0)


@@ 1021,6 1033,7 @@ uartintr(Uart *p, int events)
			iunlock(&p->plock);
		}
	}
#ifdef XXX
	eieio();
	/* TO DO: modem status isn't available on 82xFADS */
	if(dokick && p->cts && !p->blocked){


@@ 1030,7 1043,10 @@ uartintr(Uart *p, int events)
			iunlock(&p->plock);
		}
		cpmop(RestartTx, p->cpmid, 0);
	} else if (events & TXE)
	} else 
#endif

	if (events & TXE)
		cpmop(RestartTx, p->cpmid, 0);
}



@@ 1215,8 1231,8 @@ uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
	IMM *io;
	char str[256];

	sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %lud perr %lud intr %lud", p->opens,
		p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts);
	sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %lud perr %lud intr %lud lastc = %c", p->opens,
		p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts, lastc);
	/* TO DO: cts, dsr, ring, dcd, dtr, rts aren't all available on 82xFADS */
	io = m->iomem;
	if(p->scc){

A mpc/ether589.c => mpc/ether589.c +156 -0
@@ 0,0 1,156 @@
/*
 * 3C589 and 3C562.
 * To do:
 *	check xcvr10Base2 still works (is GlobalReset necessary?).
 *	pull the station address out of the card space for the 3C562,
 *	it has no EEPROM.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

enum {						/* all windows */
	CommandR		= 0x000E,
	IntStatusR		= 0x000E,
};

enum {						/* Commands */
	GlobalReset		= 0x0000,
	SelectRegisterWindow	= 0x0001,
	RxReset			= 0x0005,
	TxReset			= 0x000B,
	AcknowledgeInterrupt	= 0x000D,
};

enum {						/* IntStatus bits */
	commandInProgress	= 0x1000,
};

#define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
#define STATUS(port)		ins((port)+IntStatusR)

enum {						/* Window 0 - setup */
	Wsetup			= 0x0000,
						/* registers */
	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
	EepromCommand		= 0x000A,
	EepromData		= 0x000C,
						/* AddressConfig Bits */
	autoSelect9		= 0x0080,
	xcvrMask9		= 0xC000,
						/* ConfigControl bits */
	Ena			= 0x0001,
	base10TAvailable9	= 0x0200,
	coaxAvailable9		= 0x1000,
	auiAvailable9		= 0x2000,
						/* EepromCommand bits */
	EepromReadRegister	= 0x0080,
	EepromBusy		= 0x8000,
};

enum {						/* Window 1 - operating set */
	Wop			= 0x0001,
};

enum {						/* Window 3 - FIFO management */
	Wfifo			= 0x0003,
						/* registers */
	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
						/* InternalConfig bits */
	xcvr10BaseT		= 0x00000000,
	xcvr10Base2		= 0x00300000,
};

enum {						/* Window 4 - diagnostic */
	Wdiagnostic		= 0x0004,
						/* registers */
	MediaStatus		= 0x000A,
						/* MediaStatus bits */
	linkBeatDetect		= 0x0800,
};

extern int etherelnk3reset(Ether*);

static int
configASIC(Ether* ether, int port, int xcvr)
{
	int x;

	/* set Window 0 configuration registers */
	COMMAND(port, SelectRegisterWindow, Wsetup);
	outs(port+ConfigControl, Ena);

	/* IRQ must be 3 on 3C589/3C562 */
	outs(port + ResourceConfig, 0x3F00);

	x = ins(port+AddressConfig) & ~xcvrMask9;
	x |= (xcvr>>20)<<14;
	outs(port+AddressConfig, x);

	COMMAND(port, TxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
	COMMAND(port, RxReset, 0);
	while(STATUS(port) & commandInProgress)
		;

	return etherelnk3reset(ether);
}

static int
reset(Ether* ether)
{
	int slot;
	int port;

	if(ether->irq == 0)
		ether->irq = 10;
	if(ether->port == 0)
		ether->port = 0x240;
	port = ether->port;

	if((slot = pcmspecial(ether->type, ether)) < 0)
		return -1;

	/* try configuring as a 10BaseT */
	if(configASIC(ether, port, xcvr10BaseT) < 0){
		pcmspecialclose(slot);
		return -1;
	}
	delay(100);
	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
	if(ins(port+MediaStatus) & linkBeatDetect){
		COMMAND(port, SelectRegisterWindow, Wop);
		print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, ether->type);
		return 0;
	}

	/* try configuring as a 10base2 */
	COMMAND(port, GlobalReset, 0);
	if(configASIC(ether, port, xcvr10Base2) < 0){
		pcmspecialclose(slot);
		return -1;
	}
	print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, ether->type);

	return 0;
}

void
ether589link(void)
{
	addethercard("3C589", reset);
	addethercard("3C562", reset);
	addethercard("589E", reset);
}

A mpc/etherelnk3.c => mpc/etherelnk3.c +1854 -0
@@ 0,0 1,1854 @@
/*
 * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
 * To do:
 *	check robustness in the face of errors (e.g. busmaster & rxUnderrun);
 *	RxEarly and busmaster;
 *	autoSelect;
 *	PCI latency timer and master enable;
 *	errata list;
 *	rewrite all initialisation;
 *	handle the cyclone adapter.
 *
 * Product ID:
 *	9150 ISA	3C509[B]
 *	9050 ISA	3C509[B]-TP
 *	9450 ISA	3C509[B]-COMBO
 *	9550 ISA	3C509[B]-TPO
 *
 *	9350 EISA	3C579
 *	9250 EISA	3C579-TP
 *
 *	5920 EISA	3C592-[TP|COMBO|TPO]
 *	5970 EISA	3C597-TX	Fast Etherlink 10BASE-T/100BASE-TX
 *	5971 EISA	3C597-T4	Fast Etherlink 10BASE-T/100BASE-T4
 *	5972 EISA	3C597-MII	Fast Etherlink 10BASE-T/MII
 *
 *	5900 PCI	3C590-[TP|COMBO|TPO]
 *	5950 PCI	3C595-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
 *	5951 PCI	3C595-T4	Fast Etherlink Shared 10BASE-T/100BASE-T4
 *	5952 PCI	3C595-MII	Fast Etherlink 10BASE-T/MII
 *
 *	9000 PCI	3C900-TPO	Etherlink III XL PCI 10BASE-T
 *	9001 PCI	3C900-COMBO	Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
 *	9005 PCI	3C900B-COMBO	Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
 *	9050 PCI	3C905-TX	Fast Etherlink XL Shared 10BASE-T/100BASE-TX
 *	9051 PCI	3C905-T4	Fast Etherlink Shared 10BASE-T/100BASE-T4
 *	9055 PCI	3C905B-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
 *
 *	9058 PCMCIA	3C589[B]-[TP|COMBO]
 *
 *	627C MCA	3C529
 *	627D MCA	3C529-TP
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

#define XCVRDEBUG		if(0)print

enum {
	IDport			= 0x0110,	/* anywhere between 0x0100 and 0x01F0 */
};

enum {						/* all windows */
	CommandR		= 0x000E,
	IntStatusR		= 0x000E,
};

enum {						/* Commands */
	GlobalReset		= 0x0000,
	SelectRegisterWindow	= 0x0001,
	EnableDcConverter	= 0x0002,
	RxDisable		= 0x0003,
	RxEnable		= 0x0004,
	RxReset			= 0x0005,
	Stall			= 0x0006,	/* 3C90x */
	TxDone			= 0x0007,
	RxDiscard		= 0x0008,
	TxEnable		= 0x0009,
	TxDisable		= 0x000A,
	TxReset			= 0x000B,
	RequestInterrupt	= 0x000C,
	AcknowledgeInterrupt	= 0x000D,
	SetInterruptEnable	= 0x000E,
	SetIndicationEnable	= 0x000F,	/* SetReadZeroMask */
	SetRxFilter		= 0x0010,
	SetRxEarlyThresh	= 0x0011,
	SetTxAvailableThresh	= 0x0012,
	SetTxStartThresh	= 0x0013,
	StartDma		= 0x0014,	/* initiate busmaster operation */
	StatisticsEnable	= 0x0015,
	StatisticsDisable	= 0x0016,
	DisableDcConverter	= 0x0017,
	SetTxReclaimThresh	= 0x0018,	/* PIO-only adapters */
	PowerUp			= 0x001B,	/* not all adapters */
	PowerDownFull		= 0x001C,	/* not all adapters */
	PowerAuto		= 0x001D,	/* not all adapters */
};

enum {						/* (Global|Rx|Tx)Reset command bits */
	tpAuiReset		= 0x0001,	/* 10BaseT and AUI transceivers */
	endecReset		= 0x0002,	/* internal Ethernet encoder/decoder */
	networkReset		= 0x0004,	/* network interface logic */
	fifoReset		= 0x0008,	/* FIFO control logic */
	aismReset		= 0x0010,	/* autoinitialise state-machine logic */
	hostReset		= 0x0020,	/* bus interface logic */
	dmaReset		= 0x0040,	/* bus master logic */
	vcoReset		= 0x0080,	/* on-board 10Mbps VCO */
	updnReset		= 0x0100,	/* upload/download (Rx/TX) logic */

	resetMask		= 0x01FF,
};

enum {						/* Stall command bits */
	upStall			= 0x0000,
	upUnStall		= 0x0001,
	dnStall			= 0x0002,
	dnUnStall		= 0x0003,
};

enum {						/* SetRxFilter command bits */
	receiveIndividual	= 0x0001,	/* match station address */
	receiveMulticast	= 0x0002,
	receiveBroadcast	= 0x0004,
	receiveAllFrames	= 0x0008,	/* promiscuous */
};

enum {						/* StartDma command bits */
	Upload			= 0x0000,	/* transfer data from adapter to memory */
	Download		= 0x0001,	/* transfer data from memory to adapter */
};

enum {						/* IntStatus bits */
	interruptLatch		= 0x0001,
	hostError		= 0x0002,	/* Adapter Failure */
	txComplete		= 0x0004,
	txAvailable		= 0x0008,
	rxComplete		= 0x0010,
	rxEarly			= 0x0020,
	intRequested		= 0x0040,
	updateStats		= 0x0080,
	transferInt		= 0x0100,	/* Bus Master Transfer Complete */
	dnComplete		= 0x0200,
	upComplete		= 0x0400,
	busMasterInProgress	= 0x0800,
	commandInProgress	= 0x1000,

	interruptMask		= 0x07FE,
};

#define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
#define STATUS(port)		ins((port)+IntStatusR)

enum {						/* Window 0 - setup */
	Wsetup			= 0x0000,
						/* registers */
	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
	EepromCommand		= 0x000A,
	EepromData		= 0x000C,
						/* AddressConfig Bits */
	autoSelect9		= 0x0080,
	xcvrMask9		= 0xC000,
						/* ConfigControl bits */
	Ena			= 0x0001,
	base10TAvailable9	= 0x0200,
	coaxAvailable9		= 0x1000,
	auiAvailable9		= 0x2000,
						/* EepromCommand bits */
	EepromReadRegister	= 0x0080,
	EepromBusy		= 0x8000,
};

#define EEPROMCMD(port, cmd, a)	outs((port)+EepromCommand, (cmd)|(a))
#define EEPROMBUSY(port)	(ins((port)+EepromCommand) & EepromBusy)
#define EEPROMDATA(port)	ins((port)+EepromData)

enum {						/* Window 1 - operating set */
	Wop			= 0x0001,
						/* registers */
	Fifo			= 0x0000,
	RxError			= 0x0004,	/* 3C59[0257] only */
	RxStatus		= 0x0008,
	Timer			= 0x000A,
	TxStatus		= 0x000B,
	TxFree			= 0x000C,
						/* RxError bits */
	rxOverrun		= 0x0001,
	runtFrame		= 0x0002,
	alignmentError		= 0x0004,	/* Framing */
	crcError		= 0x0008,
	oversizedFrame		= 0x0010,
	dribbleBits		= 0x0080,
						/* RxStatus bits */
	rxBytes			= 0x1FFF,	/* 3C59[0257] mask */
	rxBytes9		= 0x07FF,	/* 3C5[078]9 mask */
	rxError9		= 0x3800,	/* 3C5[078]9 error mask */
	rxOverrun9		= 0x0000,
	oversizedFrame9		= 0x0800,
	dribbleBits9		= 0x1000,
	runtFrame9		= 0x1800,
	alignmentError9		= 0x2000,	/* Framing */
	crcError9		= 0x2800,
	rxError			= 0x4000,
	rxIncomplete		= 0x8000,
						/* TxStatus Bits */
	txStatusOverflow	= 0x0004,
	maxCollisions		= 0x0008,
	txUnderrun		= 0x0010,
	txJabber		= 0x0020,
	interruptRequested	= 0x0040,
	txStatusComplete	= 0x0080,
};

enum {						/* Window 2 - station address */
	Wstation		= 0x0002,

	ResetOp905B		= 0x000C,
};

enum {						/* Window 3 - FIFO management */
	Wfifo			= 0x0003,
						/* registers */
	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
	OtherInt		= 0x0004,	/* 3C59[0257] */
	RomControl		= 0x0006,	/* 3C509B, 3C59[27] */
	MacControl		= 0x0006,	/* 3C59[0257] */
	ResetOptions		= 0x0008,	/* 3C59[0257] */
	MediaOptions		= 0x0008,	/* 3C905B */
	RxFree			= 0x000A,
						/* InternalConfig bits */
	disableBadSsdDetect	= 0x00000100,
	ramLocation		= 0x00000200,	/* 0 external, 1 internal */
	ramPartition5to3	= 0x00000000,
	ramPartition3to1	= 0x00010000,
	ramPartition1to1	= 0x00020000,
	ramPartition3to5	= 0x00030000,
	ramPartitionMask	= 0x00030000,
	xcvr10BaseT		= 0x00000000,
	xcvrAui			= 0x00100000,	/* 10BASE5 */
	xcvr10Base2		= 0x00300000,
	xcvr100BaseTX		= 0x00400000,
	xcvr100BaseFX		= 0x00500000,
	xcvrMii			= 0x00600000,
	xcvrMask		= 0x00700000,
	autoSelect		= 0x01000000,
						/* MacControl bits */
	deferExtendEnable	= 0x0001,
	deferTimerSelect	= 0x001E,	/* mask */
	fullDuplexEnable	= 0x0020,
	allowLargePackets	= 0x0040,
	extendAfterCollision	= 0x0080,	/* 3C90xB */
	flowControlEnable	= 0x0100,	/* 3C90xB */
	vltEnable		= 0x0200,	/* 3C90xB */
						/* ResetOptions bits */
	baseT4Available		= 0x0001,
	baseTXAvailable		= 0x0002,
	baseFXAvailable		= 0x0004,
	base10TAvailable	= 0x0008,
	coaxAvailable		= 0x0010,
	auiAvailable		= 0x0020,
	miiConnector		= 0x0040,
};

enum {						/* Window 4 - diagnostic */
	Wdiagnostic		= 0x0004,
						/* registers */
	VcoDiagnostic		= 0x0002,
	FifoDiagnostic		= 0x0004,
	NetworkDiagnostic	= 0x0006,
	PhysicalMgmt		= 0x0008,
	MediaStatus		= 0x000A,
	BadSSD			= 0x000C,
	UpperBytesOk		= 0x000D,
						/* FifoDiagnostic bits */
	txOverrun		= 0x0400,
	rxUnderrun		= 0x2000,
	receiving		= 0x8000,
						/* PhysicalMgmt bits */
	mgmtClk			= 0x0001,
	mgmtData		= 0x0002,
	mgmtDir			= 0x0004,
	cat5LinkTestDefeat	= 0x8000,
						/* MediaStatus bits */
	dataRate100		= 0x0002,
	crcStripDisable		= 0x0004,
	enableSqeStats		= 0x0008,
	collisionDetect		= 0x0010,
	carrierSense		= 0x0020,
	jabberGuardEnable	= 0x0040,
	linkBeatEnable		= 0x0080,
	jabberDetect		= 0x0200,
	polarityReversed	= 0x0400,
	linkBeatDetect		= 0x0800,
	txInProg		= 0x1000,
	dcConverterEnabled	= 0x4000,
	auiDisable		= 0x8000,	/* 10BaseT transceiver selected */
};

enum {						/* Window 5 - internal state */
	Wstate			= 0x0005,
						/* registers */
	TxStartThresh		= 0x0000,
	TxAvailableThresh	= 0x0002,
	RxEarlyThresh		= 0x0006,
	RxFilter		= 0x0008,
	InterruptEnable		= 0x000A,
	IndicationEnable	= 0x000C,
};

enum {						/* Window 6 - statistics */
	Wstatistics		= 0x0006,
						/* registers */
	CarrierLost		= 0x0000,
	SqeErrors		= 0x0001,
	MultipleColls		= 0x0002,
	SingleCollFrames	= 0x0003,
	LateCollisions		= 0x0004,
	RxOverruns		= 0x0005,
	FramesXmittedOk		= 0x0006,
	FramesRcvdOk		= 0x0007,
	FramesDeferred		= 0x0008,
	UpperFramesOk		= 0x0009,
	BytesRcvdOk		= 0x000A,
	BytesXmittedOk		= 0x000C,
};

enum {						/* Window 7 - bus master operations */
	Wmaster			= 0x0007,
						/* registers */
	MasterAddress		= 0x0000,
	MasterLen		= 0x0006,
	MasterStatus		= 0x000C,
						/* MasterStatus bits */
	masterAbort		= 0x0001,
	targetAbort		= 0x0002,
	targetRetry		= 0x0004,
	targetDisc		= 0x0008,
	masterDownload		= 0x1000,
	masterUpload		= 0x4000,
	masterInProgress	= 0x8000,

	masterMask		= 0xD00F,
};

enum {						/* 3C90x extended register set */
	PktStatus		= 0x0020,	/* 32-bits */
	DnListPtr		= 0x0024,	/* 32-bits, 8-byte aligned */
	FragAddr		= 0x0028,	/* 32-bits */
	FragLen			= 0x002C,	/* 16-bits */
	ListOffset		= 0x002E,	/* 8-bits */
	TxFreeThresh		= 0x002F,	/* 8-bits */
	UpPktStatus		= 0x0030,	/* 32-bits */
	FreeTimer		= 0x0034,	/* 16-bits */
	UpListPtr		= 0x0038,	/* 32-bits, 8-byte aligned */

						/* PktStatus bits */
	fragLast		= 0x00000001,
	dnCmplReq		= 0x00000002,
	dnStalled		= 0x00000004,
	upCompleteX		= 0x00000008,
	dnCompleteX		= 0x00000010,
	upRxEarlyEnable		= 0x00000020,
	armCountdown		= 0x00000040,
	dnInProg		= 0x00000080,
	counterSpeed		= 0x00000010,	/* 0 3.2uS, 1 320nS */
	countdownMode		= 0x00000020,
						/* UpPktStatus bits (dpd->control) */
	upPktLenMask		= 0x00001FFF,
	upStalled		= 0x00002000,
	upError			= 0x00004000,
	upPktComplete		= 0x00008000,
	upOverrun		= 0x00010000,	/* RxError<<16 */
	upRuntFrame		= 0x00020000,
	upAlignmentError	= 0x00040000,
	upCRCError		= 0x00080000,
	upOversizedFrame	= 0x00100000,
	upDribbleBits		= 0x00800000,
	upOverflow		= 0x01000000,

	dnIndicate		= 0x80000000,	/* FrameStartHeader (dpd->control) */

	updnLastFrag		= 0x80000000,	/* (dpd->len) */

	Nup			= 32,
	Ndn			= 64,
};

/*
 * Up/Dn Packet Descriptors.
 * The hardware info (np, control, addr, len) must be 8-byte aligned
 * and this structure size must be a multiple of 8.
 */
typedef struct Pd Pd;
typedef struct Pd {
	ulong	np;				/* next pointer */
	ulong	control;			/* FSH or UpPktStatus */
	ulong	addr;
	ulong	len;

	Pd*	next;
	Block*	bp;
} Pd;

typedef struct {
	Lock	wlock;				/* window access */

	int	attached;
	int	busmaster;
	Block*	rbp;				/* receive buffer */

	Block*	txbp;				/* FIFO -based transmission */
	int	txthreshold;
	int	txbusy;

	int	nup;				/* full-busmaster -based reception */
	void*	upbase;
	Pd*	upr;
	Pd*	uphead;

	int	ndn;				/* full-busmaster -based transmission */
	void*	dnbase;
	Pd*	dnr;
	Pd*	dnhead;
	Pd*	dntail;
	int	dnq;

	long	interrupts;			/* statistics */
	long	timer;
	long	stats[BytesRcvdOk+3];

	int	upqmax;
	int	upqmaxhw;
	ulong	upinterrupts;
	ulong	upqueued;
	ulong	upstalls;
	int	dnqmax;
	int	dnqmaxhw;
	ulong	dninterrupts;
	ulong	dnqueued;

	int	xcvr;				/* transceiver type */
	int	rxstatus9;			/* old-style RxStatus register */
	int	rxearly;			/* RxEarlyThreshold */
	int	ts;				/* threshold shift */
	int	upenabled;
	int	dnenabled;
} Ctlr;

static void
init905(Ctlr* ctlr)
{
	Block *bp;
	Pd *pd, *prev;

	/*
	 * Create rings for the receive and transmit sides.
	 * Take care with alignment:
	 *	make sure ring base is 8-byte aligned;
	 *	make sure each entry is 8-byte aligned.
	 */
	ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
	ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);

	prev = ctlr->upr;
	for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
		pd->np = PADDR(&prev->np);
		pd->control = 0;
		bp = allocb(sizeof(Etherpkt));
		pd->addr = PADDR(bp->rp);
		pd->len = updnLastFrag|sizeof(Etherpkt);

		pd->next = prev;
		prev = pd;
		pd->bp = bp;
	}
	ctlr->uphead = ctlr->upr;

	ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
	ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);

	prev = ctlr->dnr;
	for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
		pd->next = prev;
		prev = pd;
	}
	ctlr->dnhead = ctlr->dnr;
	ctlr->dntail = ctlr->dnr;
	ctlr->dnq = 0;
}

static Block*
rbpalloc(Block* (*f)(int))
{
	Block *bp;
	ulong addr;

	/*
	 * The receive buffers must be on a 32-byte
	 * boundary for EISA busmastering.
	 */
	if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
		addr = (ulong)bp->base;
		addr = ROUNDUP(addr, 32);
		bp->rp = (uchar*)addr;
	}

	return bp;
}

static uchar*
startdma(Ether* ether, ulong address)
{
	int port, status, w;
	uchar *wp;

	port = ether->port;

	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wmaster);

	wp = KADDR(inl(port+MasterAddress));
	status = ins(port+MasterStatus);
	if(status & (masterInProgress|targetAbort|masterAbort))
		print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
	outs(port+MasterStatus, masterMask);
	outl(port+MasterAddress, address);
	outs(port+MasterLen, sizeof(Etherpkt));
	COMMAND(port, StartDma, Upload);

	COMMAND(port, SelectRegisterWindow, w);
	return wp;
}

static void
promiscuous(void* arg, int on)
{
	int filter, port;
	Ether *ether;

	ether = (Ether*)arg;
	port = ether->port;

	filter = receiveBroadcast|receiveIndividual;
	if(ether->nmaddr)
		filter |= receiveMulticast;
	if(on)
		filter |= receiveAllFrames;
	COMMAND(port, SetRxFilter, filter);
}

static void
multicast(void* arg, uchar *addr, int on)
{
	int filter, port;
	Ether *ether;

	USED(addr, on);

	ether = (Ether*)arg;
	port = ether->port;

	filter = receiveBroadcast|receiveIndividual;
	if(ether->nmaddr)
		filter |= receiveMulticast;
	if(ether->prom)
		filter |= receiveAllFrames;
	COMMAND(port, SetRxFilter, filter);
}

static void
attach(Ether* ether)
{
	int port, x;
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(&ctlr->wlock);
	if(ctlr->attached){
		iunlock(&ctlr->wlock);
		return;
	}

	port = ether->port;

	/*
	 * Set the receiver packet filter for this and broadcast addresses,
	 * set the interrupt masks for all interrupts, enable the receiver
	 * and transmitter.
	 */
	promiscuous(ether, ether->prom);

	x = interruptMask;
	if(ctlr->busmaster == 1)
		x &= ~(rxEarly|rxComplete);
	else{
		if(ctlr->dnenabled)
			x &= ~transferInt;
		if(ctlr->upenabled)
			x &= ~(rxEarly|rxComplete);
	}
	COMMAND(port, SetIndicationEnable, x);
	COMMAND(port, SetInterruptEnable, x);

	COMMAND(port, RxEnable, 0);
	COMMAND(port, TxEnable, 0);

	/*
	 * Prime the busmaster channel for receiving directly into a
	 * receive packet buffer if necessary.
	 */
	if(ctlr->busmaster == 1)
		startdma(ether, PADDR(ctlr->rbp->rp));
	else{
		if(ctlr->upenabled)
			outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
	}

	ctlr->attached = 1;
	iunlock(&ctlr->wlock);
}

static void
statistics(Ether* ether)
{
	int port, i, u, w;
	Ctlr *ctlr;

	port = ether->port;
	ctlr = ether->ctlr;

	/*
	 * 3C59[27] require a read between a PIO write and
	 * reading a statistics register.
	 */
	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wstatistics);
	STATUS(port);

	for(i = 0; i < UpperFramesOk; i++)
		ctlr->stats[i] += inb(port+i) & 0xFF;
	u = inb(port+UpperFramesOk) & 0xFF;
	ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
	ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
	ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
	ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;

	switch(ctlr->xcvr){

	case xcvrMii:
	case xcvr100BaseTX:
	case xcvr100BaseFX:
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		STATUS(port);
		ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
		break;
	}

	COMMAND(port, SelectRegisterWindow, w);
}

static void
txstart(Ether* ether)
{
	int port, len;
	Ctlr *ctlr;
	Block *bp;

	port = ether->port;
	ctlr = ether->ctlr;

	/*
	 * Attempt to top-up the transmit FIFO. If there's room simply
	 * stuff in the packet length (unpadded to a dword boundary), the
	 * packet data (padded) and remove the packet from the queue.
	 * If there's no room post an interrupt for when there is.
	 * This routine is called both from the top level and from interrupt
	 * level and expects to be called with ctlr->wlock already locked
	 * and the correct register window (Wop) in place.
	 */
	for(;;){
		if(ctlr->txbp){
			bp = ctlr->txbp;
			ctlr->txbp = 0;
		}
		else{
			bp = qget(ether->oq);
			if(bp == nil)
				break;
		}

		len = ROUNDUP(BLEN(bp), 4);
		if(len+4 <= ins(port+TxFree)){
			outl(port+Fifo, BLEN(bp));
			outsl(port+Fifo, bp->rp, len/4);

			freeb(bp);

			ether->outpackets++;
		}
		else{
			ctlr->txbp = bp;
			if(ctlr->txbusy == 0){
				ctlr->txbusy = 1;
				COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
			}
			break;
		}
	}
}

static void
txstart905(Ether* ether)
{
	Ctlr *ctlr;
	int port, stalled, timeo;
	Block *bp;
	Pd *pd;

	ctlr = ether->ctlr;
	port = ether->port;
	/*
	 * Free any completed packets.
	 */
	pd = ctlr->dntail;
	while(ctlr->dnq){
		if(PADDR(&pd->np) == inl(port+DnListPtr))
			break;
		if(pd->bp){
			freeb(pd->bp);
			pd->bp = nil;
		}
		ctlr->dnq--;
		pd = pd->next;
	}
	ctlr->dntail = pd;

	stalled = 0;
	while(ctlr->dnq < (ctlr->ndn-1)){
		bp = qget(ether->oq);
		if(bp == nil)
			break;

		pd = ctlr->dnhead->next;
		pd->np = 0;
		pd->control = dnIndicate|BLEN(bp);
		pd->addr = PADDR(bp->rp);
		pd->len = updnLastFrag|BLEN(bp);
		pd->bp = bp;

		if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
			COMMAND(port, Stall, dnStall);
			for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
				;
			if(timeo == 0)
				print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
			stalled = 1;
		}

		coherence();
		ctlr->dnhead->np = PADDR(&pd->np);
		ctlr->dnhead->control &= ~dnIndicate;
		ctlr->dnhead = pd;
		if(ctlr->dnq == 0)
			ctlr->dntail = pd;
		ctlr->dnq++;

		ctlr->dnqueued++;
	}

	if(ctlr->dnq > ctlr->dnqmax)
		ctlr->dnqmax = ctlr->dnq;

	/*
	 * If the adapter is not currently processing anything
	 * and there is something on the queue, start it processing.
	 */
	if(inl(port+DnListPtr) == 0 && ctlr->dnq)
		outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
	if(stalled)
		COMMAND(port, Stall, dnUnStall);
}

static void
transmit(Ether* ether)
{
	Ctlr *ctlr;
	int port, w;

	port = ether->port;
	ctlr = ether->ctlr;

	ilock(&ctlr->wlock);
	if(ctlr->dnenabled)
		txstart905(ether);
	else{
		w = (STATUS(port)>>13) & 0x07;
		COMMAND(port, SelectRegisterWindow, Wop);
		txstart(ether);
		COMMAND(port, SelectRegisterWindow, w);
	}
	iunlock(&ctlr->wlock);
}

static void
receive905(Ether* ether)
{
	Ctlr *ctlr;
	int len, port, q;
	Pd *pd;
	Block *bp;

	ctlr = ether->ctlr;
	port = ether->port;

	if(inl(port+UpPktStatus) & upStalled)
		ctlr->upstalls++;
	q = 0;
	for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
		if(pd->control & upError){
			if(pd->control & upOverrun)
				ether->overflows++;
			if(pd->control & (upOversizedFrame|upRuntFrame))
				ether->buffs++;
			if(pd->control & upAlignmentError)
				ether->frames++;
			if(pd->control & upCRCError)
				ether->crcs++;
		}
		else if(bp = iallocb(sizeof(Etherpkt)+4)){
			len = pd->control & rxBytes;
			pd->bp->wp = pd->bp->rp+len;
			etheriq(ether, pd->bp, 1);
			pd->bp = bp;
			pd->addr = PADDR(bp->rp);
			coherence();
		}

		pd->control = 0;
		COMMAND(port, Stall, upUnStall);

		q++;
	}
	ctlr->uphead = pd;

	ctlr->upqueued += q;
	if(q > ctlr->upqmax)
		ctlr->upqmax = q;
}

static void
receive(Ether* ether)
{
	int len, port, rxerror, rxstatus;
	Ctlr *ctlr;
	Block *bp;

	port = ether->port;
	ctlr = ether->ctlr;

	while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
		if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
			break;

		/*
		 * If there was an error, log it and continue.
		 * Unfortunately the 3C5[078]9 has the error info in the status register
		 * and the 3C59[0257] implement a separate RxError register.
		 */
		if(rxstatus & rxError){
			if(ctlr->rxstatus9){
				switch(rxstatus & rxError9){

				case rxOverrun9:
					ether->overflows++;
					break;

				case oversizedFrame9:
				case runtFrame9:
					ether->buffs++;
					break;

				case alignmentError9:
					ether->frames++;
					break;

				case crcError9:
					ether->crcs++;
					break;

				}
			}
			else{
				rxerror = inb(port+RxError);
				if(rxerror & rxOverrun)
					ether->overflows++;
				if(rxerror & (oversizedFrame|runtFrame))
					ether->buffs++;
				if(rxerror & alignmentError)
					ether->frames++;
				if(rxerror & crcError)
					ether->crcs++;
			}
		}

		/*
		 * If there was an error or a new receive buffer can't be
		 * allocated, discard the packet and go on to the next.
		 */
		if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){
			COMMAND(port, RxDiscard, 0);
			while(STATUS(port) & commandInProgress)
				;

			if(ctlr->busmaster == 1)
				startdma(ether, PADDR(ctlr->rbp->rp));

			continue;
		}

		/*
		 * A valid receive packet awaits:
		 *	if using PIO, read it into the buffer;
		 *	discard the packet from the FIFO;
		 *	if using busmastering, start a new transfer for
		 *	  the next packet and as a side-effect get the
		 *	  end-pointer of the one just received;
		 *	pass the packet on to whoever wants it.
		 */
		if(ctlr->busmaster == 0 || ctlr->busmaster == 2){
			len = (rxstatus & rxBytes9);
			ctlr->rbp->wp = ctlr->rbp->rp + len;
			insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4));
		}

		COMMAND(port, RxDiscard, 0);
		while(STATUS(port) & commandInProgress)
			;

		if(ctlr->busmaster == 1)
			ctlr->rbp->wp = startdma(ether, PADDR(bp->rp));

		etheriq(ether, ctlr->rbp, 1);
		ctlr->rbp = bp;
	}
}

static void
interrupt(Ureg*, void* arg)
{
	Ether *ether;
	int port, status, s, w, x;
	Ctlr *ctlr;

	ether = arg;
	port = ether->port;
	ctlr = ether->ctlr;

	ilock(&ctlr->wlock);
	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wop);

	ctlr->interrupts++;
	ctlr->timer += inb(port+Timer) & 0xFF;
	while((status = STATUS(port)) & (interruptMask|interruptLatch)){
		if(status & hostError){
			/*
			 * Adapter failure, try to find out why, reset if
			 * necessary. What happens if Tx is active and a reset
			 * occurs, need to retransmit? This probably isn't right.
			 */
			COMMAND(port, SelectRegisterWindow, Wdiagnostic);
			x = ins(port+FifoDiagnostic);
			COMMAND(port, SelectRegisterWindow, Wop);
			print("#l%d: status 0x%uX, diag 0x%uX\n",
			    ether->ctlrno, status, x);

			if(x & txOverrun){
				if(ctlr->busmaster == 0)
					COMMAND(port, TxReset, 0);
				else
					COMMAND(port, TxReset, (updnReset|dmaReset));
				COMMAND(port, TxEnable, 0);
			}

			if(x & rxUnderrun){
				/*
				 * This shouldn't happen...
				 * Reset the receiver and restore the filter and RxEarly
				 * threshold before re-enabling.
				 * Need to restart any busmastering?
				 */
				COMMAND(port, SelectRegisterWindow, Wstate);
				s = (port+RxFilter) & 0x000F;
				COMMAND(port, SelectRegisterWindow, Wop);
				COMMAND(port, RxReset, 0);
				while(STATUS(port) & commandInProgress)
					;
				COMMAND(port, SetRxFilter, s);
				COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
				COMMAND(port, RxEnable, 0);
			}

			status &= ~hostError;
		}

		if(status & (transferInt|rxComplete)){
			receive(ether);
			status &= ~(transferInt|rxComplete);
		}

		if(status & (upComplete)){
			COMMAND(port, AcknowledgeInterrupt, upComplete);
			receive905(ether);
			status &= ~upComplete;
			ctlr->upinterrupts++;
		}

		if(status & txComplete){
			/*
			 * Pop the TxStatus stack, accumulating errors.
			 * Adjust the TX start threshold if there was an underrun.
			 * If there was a Jabber or Underrun error, reset
			 * the transmitter, taking care not to reset the dma logic
			 * as a busmaster receive may be in progress.
			 * For all conditions enable the transmitter.
			 */
			s = 0;
			do{
				if(x = inb(port+TxStatus))
					outb(port+TxStatus, 0);
				s |= x;
			}while(STATUS(port) & txComplete);

			if(s & txUnderrun){
				if(ctlr->dnenabled){
					while(inl(port+PktStatus) & dnInProg)
						;
				}
				COMMAND(port, SelectRegisterWindow, Wdiagnostic);
				while(ins(port+MediaStatus) & txInProg)
					;
				COMMAND(port, SelectRegisterWindow, Wop);
				if(ctlr->txthreshold < ETHERMAXTU)
					ctlr->txthreshold += ETHERMINTU;
			}

			/*
			 * According to the manual, maxCollisions does not require
			 * a TxReset, merely a TxEnable. However, evidence points to
			 * it being necessary on the 3C905. The jury is still out.
			 * On busy or badly configured networks maxCollisions can
			 * happen frequently enough for messages to be annoying so
			 * keep quiet about them by popular request.
			 */
			if(s & (txJabber|txUnderrun|maxCollisions)){
				if(ctlr->busmaster == 0)
					COMMAND(port, TxReset, 0);
				else
					COMMAND(port, TxReset, (updnReset|dmaReset));
				while(STATUS(port) & commandInProgress)
					;
				COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
				if(ctlr->busmaster == 2)
					outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
				if(ctlr->dnenabled)
					status |= dnComplete;
			}

			if(s & ~(txStatusComplete|maxCollisions))
				print("#l%d: txstatus 0x%uX, threshold %d\n",
			    		ether->ctlrno, s, ctlr->txthreshold);
			COMMAND(port, TxEnable, 0);
			ether->oerrs++;
			status &= ~txComplete;
			status |= txAvailable;
		}

		if(status & txAvailable){
			COMMAND(port, AcknowledgeInterrupt, txAvailable);
			ctlr->txbusy = 0;
			txstart(ether);
			status &= ~txAvailable;
		}

		if(status & dnComplete){
			COMMAND(port, AcknowledgeInterrupt, dnComplete);
			txstart905(ether);
			status &= ~dnComplete;
			ctlr->dninterrupts++;
		}

		if(status & updateStats){
			statistics(ether);
			status &= ~updateStats;
		}

		/*
		 * Currently, this shouldn't happen.
		 */
		if(status & rxEarly){
			COMMAND(port, AcknowledgeInterrupt, rxEarly);
			status &= ~rxEarly;
		}

		/*
		 * Panic if there are any interrupts not dealt with.
		 */
		if(status & interruptMask)
			panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);

		COMMAND(port, AcknowledgeInterrupt, interruptLatch);
	}

	COMMAND(port, SelectRegisterWindow, w);
	iunlock(&ctlr->wlock);
}

static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
	char *p;
	int len;
	Ctlr *ctlr;

	if(n == 0)
		return 0;

	ctlr = ether->ctlr;

	ilock(&ctlr->wlock);
	statistics(ether);
	iunlock(&ctlr->wlock);

	p = malloc(READSTR);
	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
	len += snprint(p+len, READSTR-len, "timer: %lud\n", ctlr->timer);
	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->stats[CarrierLost]);
	len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n", ctlr->stats[SqeErrors]);
	len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n", ctlr->stats[MultipleColls]);
	len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n", ctlr->stats[SingleCollFrames]);
	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->stats[LateCollisions]);
	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->stats[RxOverruns]);
	len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n", ctlr->stats[FramesXmittedOk]);
	len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n", ctlr->stats[FramesRcvdOk]);
	len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->stats[FramesDeferred]);
	len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n", ctlr->stats[BytesRcvdOk]);
	len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", ctlr->stats[BytesRcvdOk+1]);

	if(ctlr->upenabled){
		if(ctlr->upqmax > ctlr->upqmaxhw)
			ctlr->upqmaxhw = ctlr->upqmax;
		len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n",
			ctlr->upqueued, ctlr->upinterrupts,
			ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls);
		ctlr->upqmax = 0;
	}
	if(ctlr->dnenabled){
		if(ctlr->dnqmax > ctlr->dnqmaxhw)
			ctlr->dnqmaxhw = ctlr->dnqmax;
		len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n",
			ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw);
		ctlr->dnqmax = 0;
	}

	snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]);

	n = readstr(offset, a, n, p);
	free(p);

	return n;
}

static void
txrxreset(int port)
{
	COMMAND(port, TxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
	COMMAND(port, RxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
}

typedef struct Adapter {
	int	port;
	int	irq;
	int	tbdf;
} Adapter;
static Block* adapter;

static void
tcmadapter(int port, int irq, int tbdf)
{
	Block *bp;
	Adapter *ap;

	bp = allocb(sizeof(Adapter));
	ap = (Adapter*)bp->rp;
	ap->port = port;
	ap->irq = irq;
	ap->tbdf = tbdf;

	bp->next = adapter;
	adapter = bp;
}

/*
 * Write two 0 bytes to identify the IDport and then reset the
 * ID sequence. Then send the ID sequence to the card to get
 * the card into command state.
 */
static void
idseq(void)
{
	int i;
	uchar al;
	static int reset, untag;

	/*
	 * One time only:
	 *	reset any adapters listening
	 */
	if(reset == 0){
		outb(IDport, 0);
		outb(IDport, 0);
		outb(IDport, 0xC0);
		delay(20);
		reset = 1;
	}

	outb(IDport, 0);
	outb(IDport, 0);
	for(al = 0xFF, i = 0; i < 255; i++){
		outb(IDport, al);
		if(al & 0x80){
			al <<= 1;
			al ^= 0xCF;
		}
		else
			al <<= 1;
	}

	/*
	 * One time only:
	 *	write ID sequence to get the attention of all adapters;
	 *	untag all adapters.
	 * If a global reset is done here on all adapters it will confuse
	 * any ISA cards configured for EISA mode.
	 */
	if(untag == 0){
		outb(IDport, 0xD0);
		untag = 1;
	}
}

static ulong
activate(void)
{
	int i;
	ushort x, acr;

	/*
	 * Do the little configuration dance:
	 *
	 * 2. write the ID sequence to get to command state.
	 */
	idseq();

	/*
	 * 3. Read the Manufacturer ID from the EEPROM.
	 *    This is done by writing the IDPort with 0x87 (0x80
	 *    is the 'read EEPROM' command, 0x07 is the offset of
	 *    the Manufacturer ID field in the EEPROM).
	 *    The data comes back 1 bit at a time.
	 *    A delay seems necessary between reading the bits.
	 *
	 * If the ID doesn't match, there are no more adapters.
	 */
	outb(IDport, 0x87);
	delay(20);
	for(x = 0, i = 0; i < 16; i++){
		delay(20);
		x <<= 1;
		x |= inb(IDport) & 0x01;
	}
	if(x != 0x6D50)
		return 0;

	/*
	 * 3. Read the Address Configuration from the EEPROM.
	 *    The Address Configuration field is at offset 0x08 in the EEPROM).
	 */
	outb(IDport, 0x88);
	for(acr = 0, i = 0; i < 16; i++){
		delay(20);
		acr <<= 1;
		acr |= inb(IDport) & 0x01;
	}

	return (acr & 0x1F)*0x10 + 0x200;
}

static char* tcmpcmcia[] = {
	"3C589",			/* 3COM 589[ABCD] */
	"3C562",			/* 3COM 562 */
	"589E",				/* 3COM Megahertz 589E */
	nil,
};

static int
tcm5XXpcmcia(Ether* ether)
{
	int i;

	for(i = 0; tcmpcmcia[i] != nil; i++){
		if(!cistrcmp(ether->type, tcmpcmcia[i]))
			return ether->port;
	}

	return 0;
}

static void
setxcvr(int port, int xcvr, int is9)
{
	int x;

	if(is9){
		COMMAND(port, SelectRegisterWindow, Wsetup);
		x = ins(port+AddressConfig) & ~xcvrMask9;
		x |= (xcvr>>20)<<14;
		outs(port+AddressConfig, x);
	}
	else{
		COMMAND(port, SelectRegisterWindow, Wfifo);
		x = inl(port+InternalConfig) & ~xcvrMask;
		x |= xcvr;
		outl(port+InternalConfig, x);
	}

	txrxreset(port);
}

static void
setfullduplex(int port)
{
	int x;

	COMMAND(port, SelectRegisterWindow, Wfifo);
	x = ins(port+MacControl);
	outs(port+MacControl, fullDuplexEnable|x);

	txrxreset(port);
}

static int
miimdi(int port, int n)
{
	int data, i;

	/*
	 * Read n bits from the MII Management Register.
	 */
	data = 0;
	for(i = n-1; i >= 0; i--){
		if(ins(port) & mgmtData)
			data |= (1<<i);
		microdelay(1);
		outs(port, mgmtClk);
		microdelay(1);
		outs(port, 0);
		microdelay(1);
	}

	return data;
}

static void
miimdo(int port, int bits, int n)
{
	int i, mdo;

	/*
	 * Write n bits to the MII Management Register.
	 */
	for(i = n-1; i >= 0; i--){
		if(bits & (1<<i))
			mdo = mgmtDir|mgmtData;
		else
			mdo = mgmtDir;
		outs(port, mdo);
		microdelay(1);
		outs(port, mdo|mgmtClk);
		microdelay(1);
		outs(port, mdo);
		microdelay(1);
	}
}

static int
miir(int port, int phyad, int regad)
{
	int data, w;

	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
	port += PhysicalMgmt;

	/*
	 * Preamble;
	 * ST+OP+PHYAD+REGAD;
	 * TA + 16 data bits.
	 */
	miimdo(port, 0xFFFFFFFF, 32);
	miimdo(port, 0x1800|(phyad<<5)|regad, 14);
	data = miimdi(port, 18);

	port -= PhysicalMgmt;
	COMMAND(port, SelectRegisterWindow, w);

	if(data & 0x10000)
		return -1;

	return data & 0xFFFF;
}

static void
scanphy(int port)
{
	int i, x;

	for(i = 0; i < 32; i++){
		if((x = miir(port, i, 2)) == -1 || x == 0)
			continue;
		x <<= 6;
		x |= miir(port, i, 3)>>10;
		XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
		USED(x);
	}
}

#ifdef notdef
static struct xxx {
	int	available;
	int	next;
} xxx[8] = {
	{ base10TAvailable,	1, },		/* xcvr10BaseT	-> xcvrAui */
	{ auiAvailable,		3, },		/* xcvrAui	-> xcvr10Base2 */
	{ 0, -1, },
	{ coaxAvailable,	-1, },		/* xcvr10Base2	-> nowhere */
	{ baseTXAvailable,	5, },		/* xcvr100BaseTX-> xcvr100BaseFX */
	{ baseFXAvailable,	-1, },		/* xcvr100BaseFX-> nowhere */
	{ miiConnector,		-1, },		/* xcvrMii	-> nowhere */
	{ 0, -1, },
};
#endif /* notdef */

static int
autoselect(int port, int xcvr, int is9)
{
	int media, x;

	USED(xcvr);

	/*
	 * Pathetic attempt at automatic media selection.
	 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
	 * cards operational.
	 * It's a bonus if it works for anything else.
	 */
	if(is9){
		COMMAND(port, SelectRegisterWindow, Wsetup);
		x = ins(port+ConfigControl);
		media = 0;
		if(x & base10TAvailable9)
			media |= base10TAvailable;
		if(x & coaxAvailable9)
			media |= coaxAvailable;
		if(x & auiAvailable9)
			media |= auiAvailable;
	}
	else{
		COMMAND(port, SelectRegisterWindow, Wfifo);
		media = ins(port+ResetOptions);
	}
	XCVRDEBUG("autoselect: media %uX\n", media);

	if(media & miiConnector)
		return xcvrMii;

	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
	XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));

	if(media & baseTXAvailable){
		/*
		 * Must have InternalConfig register.
		 */
		setxcvr(port, xcvr100BaseTX, is9);

		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
		outs(port+MediaStatus, linkBeatEnable|x);
		delay(10);

		if(ins(port+MediaStatus) & linkBeatDetect)
			return xcvr100BaseTX;
		outs(port+MediaStatus, x);
	}

	if(media & base10TAvailable){
		setxcvr(port, xcvr10BaseT, is9);

		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus) & ~dcConverterEnabled;
		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
		delay(100);

		XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
		if(ins(port+MediaStatus) & linkBeatDetect)
			return xcvr10BaseT;
		outs(port+MediaStatus, x);
	}

	/*
	 * Botch.
	 */
	return autoSelect;
}

static int
eepromdata(int port, int offset)
{
	COMMAND(port, SelectRegisterWindow, Wsetup);
	while(EEPROMBUSY(port))
		;
	EEPROMCMD(port, EepromReadRegister, offset);
	while(EEPROMBUSY(port))
		;
	return EEPROMDATA(port);
}

int
etherelnk3reset(Ether* ether)
{
	int anar, anlpar, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr;
	Block *bp, **bpp;
	Adapter *ap;
	uchar ea[Eaddrlen];
	Ctlr *ctlr;

	/*
	 * Any adapter matches if no ether->port is supplied,
	 * otherwise the ports must match.
	 */
	port = 0;
	bpp = &adapter;
	for(bp = *bpp; bp; bp = bp->next){
		ap = (Adapter*)bp->rp;
		if(ether->port == 0 || ether->port == ap->port){
			port = ap->port;
			ether->irq = ap->irq;
			ether->tbdf = ap->tbdf;
			*bpp = bp->next;
			freeb(bp);
			break;
		}
		bpp = &bp->next;
	}
	if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0)
		return -1;

	/*
	 * Read the DeviceID from the EEPROM, it's at offset 0x03,
	 * and do something depending on capabilities.
	 */
	switch(did = eepromdata(port, 0x03)){

	case 0x9000:
	case 0x9001:
	case 0x9005:
	case 0x9050:
	case 0x9051:
	case 0x9055:
		if(BUSTYPE(ether->tbdf) != BusPCI)
			goto buggery;
		busmaster = 2;
		goto vortex;

	case 0x5900:
	case 0x5920:
	case 0x5950:
	case 0x5951:
	case 0x5952:
	case 0x5970:
	case 0x5971:
	case 0x5972:
		busmaster = 1;
	vortex:
		COMMAND(port, SelectRegisterWindow, Wfifo);
		xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
		rxearly = 8188;
		rxstatus9 = 0;
		break;

	buggery:
	default:
		busmaster = 0;
		COMMAND(port, SelectRegisterWindow, Wsetup);
		x = ins(port+AddressConfig);
		xcvr = ((x & xcvrMask9)>>14)<<20;
		if(x & autoSelect9)
			xcvr |= autoSelect;
		rxearly = 2044;
		rxstatus9 = 1;
		break;
	}

	/*
	 * Check if the adapter's station address is to be overridden.
	 * If not, read it from the EEPROM and set in ether->ea prior to loading the
	 * station address in Wstation. The EEPROM returns 16-bits at a time.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
		for(i = 0; i < Eaddrlen/2; i++){
			x = eepromdata(port, i);
			ether->ea[2*i] = x>>8;
			ether->ea[2*i+1] = x;
		}
	}

	COMMAND(port, SelectRegisterWindow, Wstation);
	for(i = 0; i < Eaddrlen; i++)
		outb(port+i, ether->ea[i]);

	/*
	 * Enable the transceiver if necessary and determine whether
	 * busmastering can be used. Due to bugs in the first revision
	 * of the 3C59[05], don't use busmastering at 10Mbps.
	 */
	XCVRDEBUG("reset: xcvr %uX\n", xcvr);
/*
 * forgive me, but i am weak
 */
if(did == 0x9055){
   xcvr = xcvrMii;
   XCVRDEBUG("9055 reset ops 0x%uX\n",
	ins(port+ResetOp905B));
}
else
	if(xcvr & autoSelect)
		xcvr = autoselect(port, xcvr, rxstatus9);
	XCVRDEBUG("autoselect returns: xcvr %uX, did 0x%uX\n", xcvr, did);
	switch(xcvr){

	case xcvrMii:
		/*
		 * Quick hack.
		scanphy(port);
		 */
		phyaddr = 24;
for(i = 0; i < 7; i++)
    XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
XCVRDEBUG("\n");

{	int phystat, timeo;
	for(timeo = 0; timeo < 30; timeo++){
		phystat = miir(port, phyaddr, 0x01);
		if(phystat & 0x20)
			break;
		XCVRDEBUG(" %2.2uX", phystat);
		delay(100);
	}
	XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
	XCVRDEBUG("\n");
}

		anar = miir(port, phyaddr, 0x04);
		anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
		anar &= anlpar;
		miir(port, phyaddr, 0x00);
		XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
			anar, anlpar, miir(port, phyaddr, 0x00),
			miir(port, phyaddr, 0x01));
		for(i = 0; i < ether->nopt; i++){
			if(cistrcmp(ether->opt[i], "fullduplex") == 0)
				anar |= 0x0100;
			else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
				anar |= 0x0100;
			else if(cistrcmp(ether->opt[i], "force100") == 0)
				anar |= 0x0080;
		}
		XCVRDEBUG("mii anar: %uX\n", anar);
		if(anar & 0x0100){		/* 100BASE-TXFD */
			ether->mbps = 100;
			setfullduplex(port);
		}
		else if(anar & 0x0200)		/* 100BASE-T4 */
			;
		else if(anar & 0x0080)		/* 100BASE-TX */
			ether->mbps = 100;
		else if(anar & 0x0040)		/* 10BASE-TFD */
			setfullduplex(port);
		else				/* 10BASE-T */
			;
		break;

	case xcvr100BaseTX:
	case xcvr100BaseFX:
		COMMAND(port, SelectRegisterWindow, Wfifo);
		x = inl(port+InternalConfig) & ~ramPartitionMask;
		outl(port+InternalConfig, x|ramPartition1to1);

		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
		x |= linkBeatEnable;
		outs(port+MediaStatus, x);

		if(x & dataRate100)
			ether->mbps = 100;
		break;

	case xcvr10BaseT:
		/*
		 * Enable Link Beat and Jabber to start the
		 * transceiver.
		 */
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus) & ~dcConverterEnabled;
		x |= linkBeatEnable|jabberGuardEnable;
		outs(port+MediaStatus, x);

		if((did & 0xFF00) == 0x5900)
			busmaster = 0;
		break;

	case xcvr10Base2:
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
		outs(port+MediaStatus, x);

		/*
		 * Start the DC-DC converter.
		 * Wait > 800 microseconds.
		 */
		COMMAND(port, EnableDcConverter, 0);
		delay(1);
		break;
	}

	/*
	 * Wop is the normal operating register set.
	 * The 3C59[0257] adapters allow access to more than one register window
	 * at a time, but there are situations where switching still needs to be
	 * done, so just do it.
	 * Clear out any lingering Tx status.
	 */
	COMMAND(port, SelectRegisterWindow, Wop);
	while(inb(port+TxStatus))
		outb(port+TxStatus, 0);

	/*
	 * Allocate a controller structure, clear out the
	 * adapter statistics, clear the statistics logged into ctlr
	 * and enable statistics collection. Xcvr is needed in order
	 * to collect the BadSSD statistics.
	 */
	ether->ctlr = malloc(sizeof(Ctlr));
	ctlr = ether->ctlr;

	ilock(&ctlr->wlock);
	ctlr->xcvr = xcvr;
	statistics(ether);
	memset(ctlr->stats, 0, sizeof(ctlr->stats));

	ctlr->busmaster = busmaster;
	ctlr->xcvr = xcvr;
	ctlr->rxstatus9 = rxstatus9;
	ctlr->rxearly = rxearly;
	if(rxearly >= 2048)
		ctlr->ts = 2;

	COMMAND(port, StatisticsEnable, 0);

	/*
	 * Allocate any receive buffers.
	 */
	switch(ctlr->busmaster){

	case 2:
		ctlr->dnenabled = 1;

		/*
		 * 10MUpldBug.
		 * Disabling is too severe, can use receive busmastering at
		 * 100Mbps OK, but how to tell which rate is actually being used -
		 * the 3c905 always seems to have dataRate100 set?
		 * Believe the bug doesn't apply if upRxEarlyEnable is set
		 * and the threshold is set such that uploads won't start
		 * until the whole packet has been received.
		 */
		ctlr->upenabled = 1;
		x = eepromdata(port, 0x0F);
		if(!(x & 0x01))
			outl(port+PktStatus, upRxEarlyEnable);

		if(ctlr->upenabled || ctlr->dnenabled){
			ctlr->nup = Nup;
			ctlr->ndn = Ndn;
			init905(ctlr);
		}
		else
			ctlr->rbp = rbpalloc(allocb);
		outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
		break;

	default:
		ctlr->rbp = rbpalloc(allocb);
		break;
	}

	/*
	 * Set a base TxStartThresh which will be incremented
	 * if any txUnderrun errors occur and ensure no RxEarly
	 * interrupts happen.
	 */
	ctlr->txthreshold = ETHERMAXTU/2;
	COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
	COMMAND(port, SetRxEarlyThresh, rxearly>>ctlr->ts);

	iunlock(&ctlr->wlock);

	/*
	 * Linkage to the generic ethernet driver.
	 */
	ether->port = port;
	ether->attach = attach;
	ether->transmit = transmit;
	ether->interrupt = interrupt;
	ether->ifstat = ifstat;

	ether->promiscuous = promiscuous;
	ether->multicast = multicast;
	ether->arg = ether;

	return 0;
}

void
etherelnk3link(void)
{
	addethercard("elnk3",  etherelnk3reset);
	addethercard("3C509",  etherelnk3reset);
}

M mpc/etherscc.c => mpc/etherscc.c +0 -2
@@ 394,8 394,6 @@ sccsetup(Ctlr *ctlr, SCC *scc, uchar *ea)
	Etherparam *p;
	IMM *io;

print("sccsetup\n");

	io = ioplock();
	p = (Etherparam*)KADDR(SCC1P);


M mpc/fns.h => mpc/fns.h +18 -3
@@ 19,8 19,8 @@ void	delay(int);
ulong	draminit(ulong*);
void	dtlbmiss(void);
void	dumpregs(Ureg*);
//void	eieio(void);
#define	eieio()
void	eieio(void);
//#define	eieio()
void	evenaddr(ulong);
void	faultpower(Ureg*, ulong addr, int read);
void	firmware(int);


@@ 42,11 42,17 @@ void	gotopc(ulong);
void	icflush(void*, ulong);
void	idle(void);
#define	idlehands()			/* nothing to do in the runproc */
int	iprint(char*, ...);
int	inb(int);
void	insb(int, void*, int);
ushort	ins(int);
void	inss(int, void*, int);
ulong	inl(int);
void	insl(int, void*, int);
void	intr(Ureg*);
void	intrenable(int, void (*)(Ureg*, void*), void*, int);
int	intrstats(char*, int);
void	intrvec(void);
int	iprint(char*, ...);
void	itlbmiss(void);
int	isaconfig(char*, int, ISAConf*);
void	kbdinit(void);


@@ 58,6 64,14 @@ void	mapinit(RMap*, Map*, int);
void	mathinit(void);
void	mmuinit(void);
ulong*	mmuwalk(ulong*, ulong, int);
void	outb(int, int);
void	outsb(int, void*, int);
void	outs(int, ushort);
void	outss(int, void*, int);
void	outl(int, ulong);
void	outsl(int, void*, int);
int		pcmspecial(char*, ISAConf*);
void	pcmspecialclose(int);
#define	procrestore(p)
void	procsave(Proc*);
void	procsetup(Proc*);


@@ 78,6 92,7 @@ void	trapinit(void);
void	trapvec(void);
void	uartinstall(void);
void	uartwait(void);	/* debugging */
int unsac(uchar *dst, uchar *src, int n, int nsrc);
void	wbflush(void);

#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))

A mpc/inb.s => mpc/inb.s +119 -0
@@ 0,0 1,119 @@
#include "mem.h"

#define	BDNZ	BC	16,0,
#define	BDNE	BC	0,2,

TEXT	inb(SB), $0
	OR	$ISAMEM, R3
	MOVBZ	(R3), R3
	RETURN

TEXT	insb(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$1, R4
insb1:
	MOVBZ	(R3), R7
	MOVBU	R7, 1(R4)
	BDNZ	insb1
	RETURN

TEXT	outb(SB), $0
	MOVW	v+4(FP), R4
	OR	$ISAMEM, R3
	EIEIO
	MOVB	R4, (R3)
	RETURN

TEXT	outsb(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$1, R4
outsb1:
	EIEIO
	MOVBZU	1(R4), R7
	MOVB	R7, (R3)
	BDNZ	outsb1
	RETURN

TEXT	ins(SB), $0
	OR	$ISAMEM, R3
	EIEIO
	MOVHBR	(R3), R3
	RETURN

TEXT	inss(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$2, R4
inss1:
	EIEIO
	MOVHZ	(R3), R7
	MOVHU	R7, 2(R4)
	BDNZ	inss1
	RETURN

TEXT	outs(SB), $0
	MOVW	v+4(FP), R4
	OR	$ISAMEM, R3
	EIEIO
	MOVHBR	R4, (R3)
	RETURN

TEXT	outss(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$2, R4
outss1:
	EIEIO
	MOVHZU	2(R4), R7
	MOVH	R7, (R3)
	BDNZ	outss1
	RETURN

TEXT	inl(SB), $0
	OR	$ISAMEM, R3
	EIEIO
	MOVWBR	(R3), R3
	RETURN

TEXT	insl(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$4, R4
insl1:
	EIEIO
	MOVW	(R3), R7
	MOVWU	R7, 4(R4)
	BDNZ	insl1
	RETURN

TEXT	outl(SB), $0
	MOVW	v+4(FP), R4
	OR	$ISAMEM, R3
	EIEIO
	MOVWBR	R4, (R3)
	RETURN

TEXT	outsl(SB), $0
	MOVW	v+4(FP), R4
	MOVW	n+8(FP), R5
	MOVW	R5, CTR
	OR	$ISAMEM, R3
	SUB	$4, R4
outsl1:
	EIEIO
	MOVWU	4(R4), R7
	MOVW	R7, (R3)
	BDNZ	outsl1
	RETURN

A mpc/inbc.c => mpc/inbc.c +134 -0
@@ 0,0 1,134 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"

int
inb(int port)
{
	return *(uchar*)(port + ISAMEM);
}

ushort
ins(int port)
{
	int x = *(ushort*)(port + ISAMEM);
	return ((x&0xff) << 8) | (x>>8);
}

ulong
inl(int port)
{
	ulong x = *(ulong*)(port + ISAMEM);
//print("inl %d\n", port);
	return (x << 24) | ((x&0xff00)<<8) | ((x>>8)&0xff00) | (x>>24);
}

void
outb(int port, int val)
{
//print("outb %d %ux\n", port, val);
	*(uchar*)(port + ISAMEM) = val;
}

void
outs(int port, ushort val)
{
//print("outs %d %ux\n", port, val);
	int x = (val<<8) | (val>>8);
	*(ushort*)(port + ISAMEM) = x;
}

void
outl(int port, ulong val)
{
//print("outl %d %ulx\n", port, val);
	ulong x = (val << 24) | ((val&0xff00)<<8) | ((val>>8)&0xff00) | (val>>24);
	*(ulong*)(port + ISAMEM) = x;
}

void
insb(int port, void *buf, int len)
{
	int i;
	uchar *p, *q;

//print("insb %d %d\n", port, len);
	p = (uchar*)(port + ISAMEM);
	q = buf;
	for(i = 0; i < len; i++){
		*q++ = *p;
	}
}

void
inss(int port, void *buf, int len)
{
	int i;
	ushort *p, *q;

//print("inss %d %d\n", port, len);
	p = (ushort*)(port + ISAMEM);
	q = buf;
	for(i = 0; i < len; i++){
		*q++ = *p;
	}
}

void
insl(int port, void *buf, int len)
{
	int i;
	ulong *p, *q;

//print("insl %d %d\n", port, len);
	p = (ulong*)(port + ISAMEM);
	q = buf;
	for(i = 0; i < len; i++){
		*q++ = *p;
	}
}

void
outsb(int port, void *buf, int len)
{
	int i;
	uchar *p, *q;

//print("outsb %d %d\n", port, len);
	p = (uchar*)(port + ISAMEM);
	q = buf;
	for(i = 0; i < len; i++){
		*p = *q++;
	}
}

void
outss(int port, void *buf, int len)
{
	int i;
	ushort *p, *q;

//print("outss %d %d\n", port, len);
	p = (ushort*)(port + ISAMEM);
	q = buf;
	for(i = 0; i < len; i++){
		*p = *q++;
	}
}

void
outsl(int port, void *buf, int len)
{
	int i;
	ulong *p, *q;

//print("outsl %d %d\n", port, len);
	p = (ulong*)(port + ISAMEM);
	q = buf;
	for(i = 0; i < len; i++){
		*p = *q++;
	}
}

M mpc/io.h => mpc/io.h +24 -2
@@ 324,8 324,7 @@ struct IMM {
			ulong	option;
		} pcmr[8];
		uchar	rsv3[0xe0-0xc0];
		ulong	pgcra;
		ulong	pgcrb;
		ulong	pgcr[2];
		ulong	pscr;
		uchar	rsv4[0xf0-0xec];
		ulong	pipr;


@@ 554,3 553,26 @@ struct IMM {
	uchar	siram[512];
	ushort	lcdmap[256];
};


enum {
	BusCBUS		= 0,		/* Corollary CBUS */
	BusCBUSII,			/* Corollary CBUS II */
	BusEISA,			/* Extended ISA */
	BusFUTURE,			/* IEEE Futurebus */
	BusINTERN,			/* Internal bus */
	BusISA,				/* Industry Standard Architecture */
	BusMBI,				/* Multibus I */
	BusMBII,			/* Multibus II */
	BusMCA,				/* Micro Channel Architecture */
	BusMPI,				/* MPI */
	BusMPSA,			/* MPSA */
	BusNUBUS,			/* Apple Macintosh NuBus */
	BusPCI,				/* Peripheral Component Interconnect */
	BusPCMCIA,			/* PC Memory Card International Association */
	BusTC,				/* DEC TurboChannel */
	BusVL,				/* VESA Local bus */
	BusVME,				/* VMEbus */
	BusXPRESS,			/* Express System Bus */
};


M mpc/l.s => mpc/l.s +18 -8
@@ 163,16 163,16 @@ TEXT	kernelmmu(SB), $0
	 * set:
	 *  PowerPC mode
	 *  Page protection mode - no 1K pages
	 *  CI when MMU is disbaled - this will change
	 *  ~CI when MMU is disbaled
	 *  WT when DMMU is disbaled - this will change
	 *  disable protected TLB for the momment
	 *  ignore user/supervisor state when looking for TLB
	 *  set first tlb entry to 28 - first lockable entry
	 */
	MOVW	$(MMUCIDEF|(31<<8)), R4
	MOVW	$((31<<8)), R4
	MOVW	R4, SPR(MI_CTR)	/* i-mmu control */
	ISYNC
	MOVW	$(MMUCIDEF|MMUWTDEF|(31<<8)), R4
	MOVW	$((31<<8)), R4
	MOVW	R4, SPR(MD_CTR)	/* d-mmu control */
	ISYNC



@@ 197,14 197,24 @@ ltlb:
	BDNZ	ltlb

	/* lock kernel entries in tlb - also reset tlb index*/
	MOVW	$(MMUCIDEF|MMURSV4), R4
	MOVW	$(MMURSV4), R4
	MOVW	R4, SPR(MI_CTR)	/* i-mmu control */
	ISYNC
	MOVW	$(MMUCIDEF|MMUWTDEF|MMURSV4), R4
	MOVW	$(MMURSV4), R4
	MOVW	R4, SPR(MD_CTR)	/* d-mmu control */
	ISYNC

	/* enable MMU */
	/* enable i-cache */
	MOVW	$(1<<25), R4
	MOVW	R4, SPR(IC_CST)
	ISYNC

 	/* enable d-cache 	*/
	MOVW	$(1<<25), R4
	MOVW	R4, SPR(DC_CST)
	ISYNC

 	/* enable MMU */
	MOVW	MSR, R4
	OR	$(MSR_IR|MSR_DR), R4
	MOVW	R4, MSR


@@ 684,8 694,8 @@ GLOBL	isavetbl+0(SB), $4

TEXT	tlbtab(SB), $-4
	/* epn, rpn, twc */
	TLBE(DRAMMEM|MMUEV, MMUPS8M|MMUWT|MMUV, DRAMMEM|MMUPP|MMUSPS|MMUSH|MMUCI|MMUV)	/* DRAM, 8M */
	TLBE((DRAMMEM+8*(1<<20))|MMUEV, MMUPS8M|MMUWT|MMUV, (DRAMMEM+8*(1<<20))|MMUPP|MMUSPS|MMUSH|MMUCI|MMUV)	/* DRAM, second 8M */
	TLBE(DRAMMEM|MMUEV, MMUPS8M|MMUWT|MMUV, DRAMMEM|MMUPP|MMUSPS|MMUSH|MMUV)	/* DRAM, 8M */
	TLBE((DRAMMEM+8*(1<<20))|MMUEV, MMUPS8M|MMUWT|MMUV, (DRAMMEM+8*(1<<20))|MMUPP|MMUSPS|MMUSH|MMUV)	/* DRAM, second 8M */
	TLBE(INTMEM|MMUEV, MMUPS8M|MMUWT|MMUV, INTMEM|MMUPP|MMUSPS|MMUSH|MMUCI|MMUV)	/* IO space 8M */
TEXT	tlbtabe(SB), $-4
	RETURN

M mpc/main.c => mpc/main.c +19 -7
@@ 39,6 39,11 @@ if(0) {
		delay(1000);
	}
}
// turn on pcmcia
*(uchar*)(NVRAMMEM+0x100001) |= 0x60;
//print("sr1=%ux sr2=%ux\n", *(uchar*)(NVRAMMEM+0x100000), *(uchar*)(NVRAMMEM+0x100001));

//print("sccr=%ulx\n", m->iomem->sccr);
	pageinit();
	procinit0();
	initseg();


@@ 46,7 51,6 @@ if(0) {
	links();
	chandevreset();
	swapinit();
print("usrinit\n");
	userinit();
predawn = 0;
	schedinit();


@@ 70,6 74,8 @@ machinit(void)
	m->speed = osc*(mf+1);
	m->cpuhz = m->speed*MHz;	/* general system clock (cycles) */
	m->clockgen = osc*MHz;		/* clock generator frequency (cycles) */

	*(ushort*)&(io->memc[4].base) = 0x8060;
}

void


@@ 202,6 208,8 @@ exit(int ispanic)
	else
		delay(1000);

	for(;;)
		;
//	arch->reset();
}



@@ 248,7 256,7 @@ confinit(void)
	ulong pa;

	conf.nmach = 1;		/* processors */
	conf.nproc = 200;	/* processes */
	conf.nproc = 30;	/* processes */

	// hard wire for now
	pa = 0xffd00000;		// leave 1 Meg for kernel


@@ 257,8 265,11 @@ confinit(void)
	conf.npage0 = nbytes/BY2PG;
	conf.base0 = pa;
	
	conf.npage1 = 0;
	conf.base1 = 0;
	pa = 0xfff04000;
	nbytes = 1024*1024 - 0x4000;

	conf.npage1 = nbytes/BY2PG;
	conf.base1 = pa;

	conf.npage = conf.npage0 + conf.npage1;



@@ 266,9 277,9 @@ confinit(void)
	conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG;

	/* set up other configuration parameters */
	conf.nswap = conf.npage*3;
	conf.nswppo = 4096;
	conf.nimage = 200;
	conf.nswap = 0; // conf.npage*3;
	conf.nswppo = 0; // 4096;
	conf.nimage = 20;

	conf.copymode = 0;		/* copy on write */
}


@@ 296,6 307,7 @@ getcfields(char* lp, char** fields, int n, char* sep)

static char BOOTARGS[] = 
		"ether0=type=SCC port=1 ea=08003e27df94\r\n"
		"ether1=type=589E port=0x300\r\n"
		"vgasize=640x480x8\r\n"
		"kernelpercent=40\r\n"
		"console=0 lcd\r\nbaud=9600\r\n";

M mpc/mem.h => mpc/mem.h +3 -2
@@ 123,7 123,7 @@
/*
 *  portable MMU bits for fault.c - though still machine specific
 */
#define PTEVALID	(MMUPP|MMUSH|MMUCI|MMUV)
#define PTEVALID	(MMUPP|MMUSH|MMUV)
#define PTEWRITE	(2<<10)
#define	PTERONLY	(3<<10)
#define	PTEUNCACHED	(1<<4)


@@ 177,9 177,10 @@
 * atlas board registers
 */
#define	INTMEM		0x80000000
#define	ISAMEM	0x80100000
#define	FLASH0MEM	0x80200000
#define	FLASH1MEM	0x80400000
#define	SDRAMMEM	0x03000000
#define	NVRAMMEM	0x80600000
#define DRAMMEM		0xff000000		/* to 0xffffffff: 16 Meg */

#define	SIRAM	(INTMEM+0xC00)

M mpc/mmu.c => mpc/mmu.c +22 -1
@@ 34,11 34,32 @@ mmurelease(Proc* proc)


void
putmmu(ulong va, ulong pa, Page*)
putmmu(ulong va, ulong pa, Page *pg)
{
	int x, r;
	char *ctl;

//if((va&0x8000000) == 0)
//print("putmmu va=%ux pa=%ux\n", va, pa);
	x = splhi();
	r = _putmmu(va, pa);

	ctl = &pg->cachectl[m->machno];
	switch(*ctl) {
	default:
		panic("putmmu: %d\n", *ctl);
		break;
	case PG_NOFLUSH:
		break;
	case PG_TXTFLUSH:
		icflush((void*)pg->va, BY2PG);
		*ctl = PG_NOFLUSH;
		break;
	case PG_NEWCOL:
		dcflush((void*)pg->va, BY2PG);
		*ctl = PG_NOFLUSH;
		break;
	}

	splx(x);
}

A mpc/nocache.c => mpc/nocache.c +39 -0
@@ 0,0 1,39 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

// No file caching

void
cinit(void)
{
}

void
copen(Chan *c)
{
print("copen called!\n");
	USED(c);
}

int
cread(Chan *c, uchar *buf, int len, vlong off)
{
	USED(c, buf, len, off);
	return 0;
}

void
cupdate(Chan *c, uchar *buf, int len, vlong off)
{
	USED(c, buf, len, off);
}

void
cwrite(Chan* c, uchar *buf, int len, vlong off)
{
	USED(c, buf, len, off);
}

M mpc/trap.c => mpc/trap.c +5 -1
@@ 212,7 212,7 @@ faultpower(Ureg *ureg, ulong addr, int read)
	char buf[ERRLEN];

	user = (ureg->srr1 & MSR_PR) != 0;
//print("fault: pc = %ux, addr = %ux read = %d user = %d stack=%ux\n", ureg->pc, addr, read, user, &ureg);
//print("fault: pid=%d pc = %ux, addr = %ux read = %d user = %d stack=%ux\n", up->pid, ureg->pc, addr, read, user, &ureg);
	insyscall = up->insyscall;
	up->insyscall = 1;
	spllo();


@@ 222,6 222,7 @@ faultpower(Ureg *ureg, ulong addr, int read)
			dumpregs(ureg);
			panic("fault: 0x%lux\n", addr);
		}
print("sys: trap: fault %s addr=0x%lux\n", read? "read" : "write", addr);
		sprint(buf, "sys: trap: fault %s addr=0x%lux",
			read? "read" : "write", addr);
		postnote(up, 1, buf, NDebug);


@@ 674,6 675,8 @@ notify(Ureg* ur)
	ulong s, sp;
	Note *n;

print("***notify\n");

	if(up->procctl)
		procctl(up);
	if(up->nnote == 0)


@@ 749,6 752,7 @@ noted(Ureg* ureg, ulong arg0)
	Ureg *nureg;
	ulong oureg, sp;

print("***noted\n");
	qlock(&up->debug);
	if(arg0!=NRSTR && !up->notified) {
		qunlock(&up->debug);

A mpc/unsac.c => mpc/unsac.c +401 -0
@@ 0,0 1,401 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

typedef struct Huff	Huff;

typedef struct Decode	Decode;

enum
{
	ZBase		= 2,			/* base of code to encode 0 runs */
	LitBase		= ZBase-1,		/* base of literal values */
	MaxLit		= 256,

	MaxLeaf		= MaxLit+LitBase,
	MaxHuffBits	= 15,			/* max bits in a huffman code */
	MaxSelHuffBits	= 15,			/* max bits for a table selector huffman code */

	Nhuffblock	= 16*1024,		/* symbols encoded before output */
	Nhuffslop	= 64,			/* slop for stuffing zeros, etc. */
};

struct Huff
{
	int	minbits;
	int	maxbits;
	int	flatbits;
	ulong	flat[1<<8];
	ulong	maxcode[MaxHuffBits+1];
	ulong	last[MaxHuffBits+1];
	ulong	decode[MaxLeaf];
};

struct Decode{
	Huff	tab;
	int	ndec;
	int	nbits;
	ulong	bits;
	int	nzero;
	int	base;
	ulong	maxblocksym;

	jmp_buf	errjmp;

	uchar	*src;				/* input buffer */
	uchar	*smax;				/* limit */
};

static	void	fatal(Decode *dec, char*, ...);

static	int	hdec(Decode*);
static	void	hflush(Decode*);
static	void	hbflush(Decode*);
static	ulong	bitget(Decode*, int);
static	int	mtf(uchar*, int);

int
unsac(uchar *dst, uchar *src, int n, int nsrc)
{
	Decode dec;
	uchar *buf, front[256];
	ulong *suflink, sums[256];
	ulong sum;
	int i, m, I, j, c;

	buf = malloc(n+2);
	suflink = malloc((n+2) * sizeof *suflink);

	if(waserror()) {
		free(buf);
		free(suflink);
		nexterror();
	}

	dec.src = src;
	dec.smax = src + nsrc;

	dec.nbits = 0;
	dec.bits = 0;
	dec.nzero = 0;
	for(i = 0; i < 256; i++)
		front[i] = i;

	n++;
	I = bitget(&dec, 16);
	if(I >= n)
		fatal(&dec, "corrupted input file: n=%d I=%d", n, I);

	/*
	 * decode the character usage map
	 */
	for(i = 0; i < 256; i++)
		sums[i] = 0;
	c = bitget(&dec, 1);
	for(i = 0; i < 256; ){
		m = bitget(&dec, 8) + 1;
		while(m--){
			if(i >= 256)
				fatal(&dec, "bad format encoding char map %d", m);
			front[i++] = c;
		}
		c = c ^ 1;
	}

	/*
	 * initialize mtf state
	 */
	c = 0;
	for(i = 0; i < 256; i++)
		if(front[i])
			front[c++] = i;
	dec.maxblocksym = c + LitBase;

	/*
	 * huffman decoding, move to front decoding,
	 * along with character counting
	 */
	hbflush(&dec);
	for(i = 0; i < n; i++){
		if(i == I)
			continue;
		m = hdec(&dec);

		/*
		 * move to front
		 */
		c = front[m];
		for(; m > 0; m--)
			front[m] = front[m-1];
		front[0] = c;

		buf[i] = c;
		sums[c]++;
	}

	sum = 1;
	for(i = 0; i < 256; i++){
		c = sums[i];
		sums[i] = sum;
		sum += c;
	}

	/*
	 * calculate the row step for column step array
	 * by calculating it for backwards moves and inverting it
	 */
	suflink[0] = I;
	for(j = 0; j < I; j++)
		suflink[sums[buf[j]]++] = j;
	for(j++; j < n; j++)
		suflink[sums[buf[j]]++] = j;

	/*
	 * to recover the suffix array, aka suffix array for input
	 * j = 0;
	 * for(i = I; i != 0; i = suflink[i])
	 *	sarray[i] = j++;
	 * sarray[i] = j++;
	 * note that suflink[i] = sarrayinv[sarray[i] + 1]
	 */

	/*
	 * produce the decoded data forwards
	 */
	n--;
	i = I;
	for(j = 0; j < n; j++){
		i = suflink[i];
		dst[j] = buf[i];
	}

	poperror();
	free(buf);
	free(suflink);
	return n;
}

static ulong
bitget(Decode *dec, int nb)
{
	int c;

	while(dec->nbits < nb){
		if(dec->src >= dec->smax)
			fatal(dec, "premature eof 1");
		c = *dec->src++;
		dec->bits <<= 8;
		dec->bits |= c;
		dec->nbits += 8;
	}
	dec->nbits -= nb;
	return (dec->bits >> dec->nbits) & ((1 << nb) - 1);
}

static void
fillbits(Decode *dec)
{
	int c;

	while(dec->nbits < 24){
		if(dec->src >= dec->smax)
			fatal(dec, "premature eof 2: nbits %d", dec->nbits);
		c = *dec->src++;
		dec->bits <<= 8;
		dec->bits |= c;
		dec->nbits += 8;
	}
}

/*
 * decode one symbol
 */
static int
hdecsym(Decode *dec, Huff *h)
{
	long c;
	int b;

	dec->bits &= (1 << dec->nbits) - 1;
	for(b = h->flatbits; (c = dec->bits >> (dec->nbits - b)) > h->maxcode[b]; b++)
		;
	if(b > h->maxbits)
		fatal(dec, "too many bits consumed: b=%d minbits=%d maxbits=%d", b, h->minbits, h->maxbits);
	dec->nbits -= b;
	c = h->decode[h->last[b] - c];

	return c;
}

static int
hdec(Decode *dec)
{
	ulong c;

	dec->ndec++;
	if(dec->nzero){
		dec->nzero--;
		return 0;
	}

	if(dec->nbits < dec->tab.maxbits)
		fillbits(dec);
	dec->bits &= (1 << dec->nbits) - 1;
	c = dec->tab.flat[dec->bits >> (dec->nbits - dec->tab.flatbits)];
	if(c == ~0)
		c = hdecsym(dec, &dec->tab);
	else{
		dec->nbits -= c & 0xff;
		c >>= 8;
	}

	/*
	 * reverse funny run-length coding
	 */
	if(c < ZBase){
		dec->nzero = dec->base << c;
		dec->base <<= 1;
		dec->nzero--;
		return 0;
	}

	dec->base = 1;
	c -= LitBase;
	return c;
}

static void
hbflush(Decode *dec)
{
	dec->base = 1;
	dec->ndec = 0;
	hflush(dec);
}

static void
hufftab(Decode *dec, Huff *h, ulong *hb, ulong *bitcount, int maxleaf, int maxbits, int flatbits)
{
	ulong c, code, nc[MaxHuffBits+1];
	int i, b, ec;

	code = 0;
	c = 0;
	h->minbits = maxbits;
	for(b = 1; b <= maxbits; b++){
		h->last[b] = c;
		if(c == 0)
			h->minbits = b;
		c += bitcount[b];
		nc[b] = code << 1;
		code = (code << 1) + bitcount[b];
		if(code > (1 << b))
			fatal(dec, "corrupted huffman table");
		h->maxcode[b] = code - 1;
		h->last[b] += code - 1;
	}
	if(code != (1 << maxbits))
		fatal(dec, "huffman table not full %d %d", code, 1<<maxbits);
	h->maxbits = b;
	if(flatbits > b)
		flatbits = b;
	h->flatbits = flatbits;

	b = 1 << flatbits;
	for(i = 0; i < b; i++)
		h->flat[i] = ~0;

	for(i = 0; i < maxleaf; i++){
		b = hb[i];
		if(b == 0)
			continue;
		c = nc[b]++;
		if(b <= flatbits){
if(c > (1<<(b+1)))fatal(dec, "xx1");
			code = (i << 8) | b;
			ec = (c + 1) << (flatbits - b);
			if(ec > (1<<flatbits))
				fatal(dec, "too big: ec=%d c=%d %d b=%d nc=%d\n", ec, c, c & ((1<<b)-1), b, nc[b]);
			for(c <<= (flatbits - b); c < ec; c++)
				h->flat[c] = code;
		}else{
			c = h->last[b] - c;
			if(c >= maxleaf)
				fatal(dec, "corrupted huffman table: c=%d maxleaf=%d i=%d b=%d maxbits=%d last=%d, nc=%d",
					c, maxleaf, i, b, maxbits, h->last[b], nc[b]);
			h->decode[c] = i;
		}
	}
}

static void
hflush(Decode *dec)
{
	Huff codetab;
	ulong bitcount[MaxHuffBits+1], hb[MaxLeaf];
	uchar tmtf[MaxHuffBits+1];
	int i, b, m, maxbits;

	/*
	 * read the tables for the tables
	 */
	for(i = 0; i <= MaxHuffBits; i++)
		bitcount[i] = 0;
	maxbits = 0;
	for(i = 0; i <= MaxHuffBits; i++){
		b = bitget(dec, 4);
		hb[i] = b;
		bitcount[b]++;
		if(b > maxbits)
			maxbits = b;
	}
	hufftab(dec, &codetab, hb, bitcount, MaxHuffBits+1, maxbits, 8);
	for(i = 0; i <= MaxHuffBits; i++)
		tmtf[i] = i;
	for(i = 0; i <= MaxHuffBits; i++)
		bitcount[i] = 0;
	maxbits = 0;
	for(i = 0; i < dec->maxblocksym; i++){
		if(dec->nbits <= codetab.maxbits)
			fillbits(dec);
		dec->bits &= (1 << dec->nbits) - 1;
		m = codetab.flat[dec->bits >> (dec->nbits - codetab.flatbits)];
		if(m == ~0)
			m = hdecsym(dec, &codetab);
		else{
			dec->nbits -= m & 0xff;
			m >>= 8;
		}
		b = tmtf[m];
		for(; m > 0; m--)
			tmtf[m] = tmtf[m-1];
		tmtf[0] = b;

		if(b > MaxHuffBits)
			fatal(dec, "bit length %d too large, max %d i %d maxval %d", b, MaxHuffBits, i, dec->maxblocksym);
		hb[i] = b;
		bitcount[b]++;
		if(b > maxbits)
			maxbits = b;
	}
	for(; i < MaxLeaf; i++)
		hb[i] = 0;

	hufftab(dec, &dec->tab, hb, bitcount, MaxLeaf, maxbits, 8);
}

static void
fatal(Decode *dec, char *fmt, ...)
{
	char buf[128];
	va_list arg;

	va_start(arg, fmt);
	doprint(buf, buf+sizeof(buf), fmt, arg);
	va_end(arg);

	print("devsac: %s\n", buf);
	error(buf);
}