~kris/9p

9hist

bedca0009aa4bb013ea29410ab644c2dd5d865a0 — David du Colombier 24 years ago 03ab821
Plan 9 from Bell Labs 2001-10-16
5 files changed, 366 insertions(+), 177 deletions(-)

M pc/audio.h
M pc/devusb.c
M pc/ether82557.c
M pc/etherwavelan.c
M port/devaudio.c
M pc/audio.h => pc/audio.h +3 -3
@@ 1,10 1,10 @@
enum
{
	Bufsize		= 16*1024,	/* 92 ms each */
	Nbuf		= 16,		/* 1.5 seconds total */
	Bufsize		= 4*1024,	/* 23.22 ms each */
	Nbuf		= 32,		/* .74 seconds total */
	Dma		= 6,
	IrqAUDIO	= 7,
	SBswab		= 0,
	SBswab	= 0,
};

#define seteisadma(a, b)	dmainit(a, Bufsize);

M pc/devusb.c => pc/devusb.c +22 -26
@@ 206,7 206,7 @@ struct Endpt {
	int		data01;	/* 0=DATA0, 1=DATA1 */
	uchar	eof;
	ulong	csp;
	uchar	isopen;	/* ep operations forbidden on open endpoints */
/*	uchar	isopen;	 ep operations forbidden on open endpoints */
	uchar	mode;	/* OREAD, OWRITE, ORDWR */
	uchar	nbuf;	/* number of buffers allowed */
	uchar	iso;


@@ 645,6 645,7 @@ eptactivate(Endpt *e)
{
	ilock(&activends);
	if(e->active == 0){
		XPRINT("activate 0x%p\n", e);
		e->active = 1;
		e->activef = activends.f;
		activends.f = e;


@@ 661,6 662,7 @@ eptdeactivate(Endpt *e)
	ilock(&activends);
	if(e->active){
		e->active = 0;
		XPRINT("deactivate 0x%p\n", e);
		for(l = &activends.f; *l != e; l = &(*l)->activef)
			if(*l == nil){
				iunlock(&activends);


@@ 808,7 810,7 @@ schedendpt(Endpt *e)
		return 0;
	ub = &ubus;

	if (e->isopen){
	if (e->active){
		return -1;
	}
	e->off = 0;


@@ 1445,7 1447,7 @@ usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
	/*
	 * Top level directory contains the name of the device.
	 */
	if(c->qid.path == Qtopdir){
	if(QID(c->qid) == Qtopdir){
		if(s == DEVDOTDOT){
			mkqid(&q, Qtopdir, 0, QTDIR);
			devdir(c, q, "#U", 0, eve, 0555, dp);


@@ 1544,6 1546,7 @@ usbopen(Chan *c, int omode)
	f = 0;
	if(QID(c->qid) == Qnew){
		d = usbnewdevice();
		XPRINT("usbopen, new dev 0x%p\n", d);
		if(d == nil) {
			XPRINT("usbopen failed (usbnewdevice)\n");
			error(Enodev);


@@ 1553,8 1556,10 @@ usbopen(Chan *c, int omode)
		f = 1;
	}

	if(c->qid.path < Q3rd)
	if(QID(c->qid) < Q3rd){
		XPRINT("usbopen, devopen < Q3rd\n");
		return devopen(c, omode, nil, 0, usbgen);
	}

	qlock(&usbstate);
	if(waserror()){


@@ 1570,24 1575,26 @@ usbopen(Chan *c, int omode)
		d->busy = 1;
		if(!f)
			incref(d);
		XPRINT("usbopen, Qctl 0x%p\n", d);
		break;

	default:
		d = usbdevice(c);
		s = QID(c->qid) - Qep0;
		XPRINT("usbopen, default 0x%p, %d\n", d, s);
		if(s >= 0 && s < nelem(d->ep)){
			Endpt *e;
			if((e = d->ep[s]) == nil) {
				XPRINT("usbopen failed (endpoint)\n");
				error(Enodev);
			}
			XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
			if(schedendpt(e) < 0){
				if (e->isopen)
					error("can't schedule USB endpoint, isopen");
				if (e->active)
					error("can't schedule USB endpoint, active");
				else
					error("can't schedule USB endpoint");
			}
			e->isopen++;
			eptactivate(e);
		}
		incref(d);


@@ 1625,27 1632,17 @@ void
usbclose(Chan *c)
{
	Udev *d;
	int s;

	if(c->qid.type == QTDIR || c->qid.path < Q3rd)
	if(c->qid.type == QTDIR || QID(c->qid) < Q3rd)
		return;
	qlock(&usbstate);
	d = usbdevice(c);
	s = QID(c->qid) - Qep0;
	if(s >= 0 && s < nelem(d->ep)){
		Endpt *e;
		if(e = d->ep[s]) {
			if (e->isopen == 1){
				e->isopen = 0;
				eptdeactivate(e);
				unschedendpt(e);
			}
		}
	}
	if(QID(c->qid) == Qctl)
		d->busy = 0;
	if(c->flag & COPEN)
	if(c->flag & COPEN){
		XPRINT("usbclose: freedev 0x%p\n", d);
		freedev(d);
	}
	qunlock(&usbstate);
}



@@ 1858,13 1855,12 @@ epstatus(char *s, int n, Endpt *e, int i)
	int l;

	l = 0;
	l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks",
	l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n",
		i, e->csp, e->nbytes, e->nblocks);
	if (e->iso && e->toffset){
		l += snprint(s+l, n-l, " %6d buffered %10lud offset %19lld time",
			e->buffered, e->toffset, e->time);
		l += snprint(s+l, n-l, "bufsize %6d buffered %6d offset  %10lud time %19lld\n",
			e->maxpkt, e->buffered, e->toffset, e->time);
	}
	l += snprint(s+l, n-l, "\n");
	return l;
}



@@ 2103,7 2099,7 @@ usbwrite(Chan *c, void *a, long n, vlong offset)
				qunlock(&usbstate);
				nexterror();
			}
			if (e->isopen)
			if (e->active)
				error(Eperm);
			if(strcmp(fields[2], "bulk") == 0){
				Ctlr *ub;

M pc/ether82557.c => pc/ether82557.c +1 -0
@@ 904,6 904,7 @@ i82557pci(void)
			continue;
		case 0x1229:		/* Intel 8255[789] */
		case 0x1031:		/* Intel 82562EM */
		case 0x2449:		/* Intel ????? */
			break;
		}


M pc/etherwavelan.c => pc/etherwavelan.c +338 -146
@@ 27,67 27,10 @@
#include "../port/netif.h"
#include "etherif.h"

#define DEBUG	if(1)print
#define DEBUG	if(0){}else print

#define SEEKEYS 0

typedef struct Ctlr	Ctlr;
typedef struct Wltv	Wltv;
typedef struct WFrame	WFrame;
typedef struct Stats	Stats;
typedef struct WStats	WStats;
typedef struct WKey	WKey;

struct WStats
{
	ulong	ntxuframes;		// unicast frames
	ulong	ntxmframes;		// multicast frames
	ulong	ntxfrags;		// fragments
	ulong	ntxubytes;		// unicast bytes
	ulong	ntxmbytes;		// multicast bytes
	ulong	ntxdeferred;		// deferred transmits
	ulong	ntxsretries;		// single retries
	ulong	ntxmultiretries;	// multiple retries
	ulong	ntxretrylimit;
	ulong	ntxdiscards;
	ulong	nrxuframes;		// unicast frames
	ulong	nrxmframes;		// multicast frames
	ulong	nrxfrags;		// fragments
	ulong	nrxubytes;		// unicast bytes
	ulong	nrxmbytes;		// multicast bytes
	ulong	nrxfcserr;
	ulong	nrxdropnobuf;
	ulong	nrxdropnosa;
	ulong	nrxcantdecrypt;
	ulong	nrxmsgfrag;
	ulong	nrxmsgbadfrag;
	ulong	end;
};

struct WFrame
{
	ushort	sts;
	ushort	rsvd0;
	ushort	rsvd1;
	ushort	qinfo;
	ushort	rsvd2;
	ushort	rsvd3;
	ushort	txctl;
	ushort	framectl;
	ushort	id;
	uchar	addr1[Eaddrlen];
	uchar	addr2[Eaddrlen];
	uchar	addr3[Eaddrlen];
	ushort	seqctl;
	uchar	addr4[Eaddrlen];
	ushort	dlen;
	uchar	dstaddr[Eaddrlen];
	uchar	srcaddr[Eaddrlen];
	ushort	len;
	ushort	dat[3];
	ushort	type;
};

// Lucent's Length-Type-Value records to talk to the wavelan.
// most operational parameters are read/set using this.
enum


@@ 191,6 134,9 @@ enum
	WR_Data0	= 0x36,
	WR_Data1	= 0x38,

	WR_PciCor	= 0x26,
	WR_PciHcr	= 0x2E,

	// Frame stuff

	WF_Err		= 0x0003,


@@ 212,9 158,62 @@ enum

};

#define csr_outs(ctlr,r,arg)	outs((ctlr)->iob+(r),(arg))
#define csr_ins(ctlr,r)		ins((ctlr)->iob+(r))
#define csr_ack(ctlr,ev)	outs((ctlr)->iob+WR_EvAck,(ev))
typedef struct Ctlr	Ctlr;
typedef struct Wltv	Wltv;
typedef struct WFrame	WFrame;
typedef struct Stats	Stats;
typedef struct WStats	WStats;
typedef struct WKey	WKey;

struct WStats
{
	ulong	ntxuframes;		// unicast frames
	ulong	ntxmframes;		// multicast frames
	ulong	ntxfrags;		// fragments
	ulong	ntxubytes;		// unicast bytes
	ulong	ntxmbytes;		// multicast bytes
	ulong	ntxdeferred;		// deferred transmits
	ulong	ntxsretries;		// single retries
	ulong	ntxmultiretries;	// multiple retries
	ulong	ntxretrylimit;
	ulong	ntxdiscards;
	ulong	nrxuframes;		// unicast frames
	ulong	nrxmframes;		// multicast frames
	ulong	nrxfrags;		// fragments
	ulong	nrxubytes;		// unicast bytes
	ulong	nrxmbytes;		// multicast bytes
	ulong	nrxfcserr;
	ulong	nrxdropnobuf;
	ulong	nrxdropnosa;
	ulong	nrxcantdecrypt;
	ulong	nrxmsgfrag;
	ulong	nrxmsgbadfrag;
	ulong	end;
};

struct WFrame
{
	ushort	sts;
	ushort	rsvd0;
	ushort	rsvd1;
	ushort	qinfo;
	ushort	rsvd2;
	ushort	rsvd3;
	ushort	txctl;
	ushort	framectl;
	ushort	id;
	uchar	addr1[Eaddrlen];
	uchar	addr2[Eaddrlen];
	uchar	addr3[Eaddrlen];
	ushort	seqctl;
	uchar	addr4[Eaddrlen];
	ushort	dlen;
	uchar	dstaddr[Eaddrlen];
	uchar	srcaddr[Eaddrlen];
	ushort	len;
	ushort	dat[3];
	ushort	type;
};

struct WKey
{


@@ 252,6 251,7 @@ struct Wltv
struct Stats
{
	ulong	nints;
	ulong	ndoubleint;
	ulong	nrx;
	ulong	ntx;
	ulong	ntxrq;


@@ 303,10 303,75 @@ struct Ctlr

	int	ctlrno;

	/* for PCI-based devices */
	Ctlr	*next;
	ushort	*mmb;
	int	active;
	Pcidev	*pcidev;

	Stats;
	WStats;
};

/*
 * When we're using a PCI device and memory-mapped I/O, 
 * the registers are spaced out as though each takes 32 bits,
 * even though they are only 16-bit registers.  Thus, 
 * ctlr->mmb[reg] is the right way to access register reg,
 * even though a priori you'd expect to use ctlr->mmb[reg/2].
 */
static void
csr_outs(Ctlr *ctlr, int reg, ushort arg)
{
	if(ctlr->mmb)
		ctlr->mmb[reg] = arg;
	else
		outs(ctlr->iob+reg, arg);
}

static ushort
csr_ins(Ctlr *ctlr, int reg)
{
	if(ctlr->mmb)
		return ctlr->mmb[reg];
	else
		return ins(ctlr->iob+reg);
}

static void
csr_ack(Ctlr *ctlr, int ev)
{
	csr_outs(ctlr, WR_EvAck, ev);
}

static void
csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat)
{
	ushort *rp, *wp;

	if(ctlr->mmb){
		rp = &ctlr->mmb[reg];
		wp = dat;
		while(ndat-- > 0)
			*wp++ = *rp;
	}else
		inss(ctlr->iob+reg, dat, ndat);
}

static void
csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat)
{
	ushort *rp, *wp;

	if(ctlr->mmb){
		rp = dat;
		wp = &ctlr->mmb[reg];
		while(ndat-- > 0)
			*wp = *rp++;
	}else
		outss(ctlr->iob+reg, dat, ndat);
}

// w_... routines do not ilock the Ctlr and should
// be called locked.



@@ 343,16 408,36 @@ w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
		if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
			break;
	if(i==WTmOut){
		print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
		return -1;
		/*
		 * WCmdIni can take a really long time.
		 */
		enum { IniTmOut = 2000 };
		for(i=0; i<IniTmOut; i++){
			if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
				break;
			microdelay(100);
		}
		if(i < IniTmOut)
			if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i);
		if(i == IniTmOut){
			print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
			return -1;
		}
	}
	rc = csr_ins(ctlr, WR_Sts);
	csr_ack(ctlr, WCmdEv);

	if((rc&WCmdMsk) != (cmd&WCmdMsk) || (rc&WResSts)){
	if((rc&WCmdMsk) != (cmd&WCmdMsk)){
		print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
		return -1;
	}
	if(rc&WResSts){
		/*
		 * Don't print; this happens on every WCmdAccWr for some reason.
		 */
		if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
		return -1;
	}
	return 0;
}



@@ 398,7 483,7 @@ w_inltv(Ctlr* ctlr, Wltv* ltv)
		return -1;
	}
	if(ltv->len > 0)
		inss((ctlr)->iob+(WR_Data1), &ltv->val, ltv->len-1);
		csr_inss(ctlr, WR_Data1, &ltv->val, ltv->len-1);

	return 0;
}


@@ 408,7 493,7 @@ w_outltv(Ctlr* ctlr, Wltv* ltv)
{
	if(w_seek(ctlr,ltv->type, 0, 1))
		return;
	outss((ctlr)->iob+(WR_Data1), ltv, ltv->len+1);
	csr_outss(ctlr, WR_Data1, ltv, ltv->len+1);
	w_cmd(ctlr, WCmdAccWr, ltv->type);
}



@@ 486,7 571,7 @@ w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
		DEBUG("wavelan: w_read: seek failed");
		return 0;
	}
	inss((ctlr)->iob+(WR_Data1), buf, len/2);
	csr_inss(ctlr, WR_Data1, buf, len/2);

	return len;
}


@@ 502,7 587,7 @@ w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
			return 0;
		}

		outss((ctlr)->iob+(WR_Data0), buf, len/2);
		csr_outss(ctlr, WR_Data0, buf, len/2);

		csr_outs(ctlr, WR_Data0, 0xdead);
		csr_outs(ctlr, WR_Data0, 0xbeef);


@@ 763,10 848,8 @@ w_intr(Ether *ether)
		return;
	}

	csr_outs(ctlr, WR_IntEna, 0);
	rc = csr_ins(ctlr, WR_EvSts);
	csr_ack(ctlr, ~WEvs);	// Not interested on them

	csr_ack(ctlr, ~WEvs);	// Not interested in them
	if (rc & WRXEv){
		w_rxdone(ether);
		csr_ack(ctlr, WRXEv);


@@ 797,8 880,6 @@ w_intr(Ether *ether)
		ctlr->nidrop++;
		csr_ack(ctlr, WIDropEv);
	}

	w_intena(ctlr);
	w_txstart(ether);
}



@@ 821,7 902,7 @@ w_timer(void* arg)
			continue;
		ctlr->ticks++;

		ilock(&ctlr->Lock);
		ilock(ctlr);

		// Seems that the card gets frames BUT does
		// not send the interrupt; this is a problem because


@@ 838,10 919,10 @@ w_timer(void* arg)
		// the card frames in the wrong way; due to the
		// lack of documentation I cannot know.

//		if (csr_ins(ctlr, WR_EvSts)&WEvs){
//			ctlr->tickintr++;
//			w_intr(ether);
//		}
		if (csr_ins(ctlr, WR_EvSts)&WEvs){
			ctlr->tickintr++;
			w_intr(ether);
		}

		if ((ctlr->ticks % 10) == 0) {
			if (ctlr->txtmout && --ctlr->txtmout == 0){


@@ 856,7 937,7 @@ w_timer(void* arg)
			if (ctlr->txbusy == 0)
				w_cmd(ctlr, WCmdAskStats, WTyp_Stats);
		}
		iunlock(&ctlr->Lock);
		iunlock(ctlr);
	}
	pexit("terminated",0);
}


@@ 880,9 961,9 @@ attach(Ether* ether)
	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
	ctlr = (Ctlr*) ether->ctlr;
	if (ctlr->attached == 0){
		ilock(&ctlr->Lock);
		ilock(ctlr);
		rc = w_enable(ether);
		iunlock(&ctlr->Lock);
		iunlock(ctlr);
		if(rc == 0){
			ctlr->attached = 1;
			kproc(name, w_timer, ether);


@@ 921,6 1002,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
	PRINTSTAT("Noise: %d\n", ctlr->noise-149);
	PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
	PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
	PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint);
	PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
	PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
	PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);


@@ 951,7 1033,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
	}

	// real card stats
	ilock(&ctlr->Lock);
	ilock(ctlr);
	PRINTSTR("\nCard stats: \n");
	PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
	PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));


@@ 990,7 1072,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
			PRINTSTAT("Transmit key id: %d\n", txid);
		}
	}
	iunlock(&ctlr->Lock);
	iunlock(ctlr);

	PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
	PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);


@@ 1142,16 1224,16 @@ ctl(Ether* ether, void* buf, long n)
	if(ctlr->attached == 0)
		error(Eshutdown);

	ilock(&ctlr->Lock);
	ilock(ctlr);
	if(option(ctlr, buf, n)){
		iunlock(&ctlr->Lock);
		iunlock(ctlr);
		error(Ebadctl);
	}
	if(ctlr->txbusy)
		w_txdone(ctlr, WTxErrEv);
	w_enable(ether);
	w_txstart(ether);
	iunlock(&ctlr->Lock);
	iunlock(ctlr);

	return n;
}


@@ 1164,10 1246,10 @@ transmit(Ether* ether)
	if (ctlr == 0)
		return;

	ilock(&ctlr->Lock);
	ilock(ctlr);
	ctlr->ntxrq++;
	w_txstart(ether);
	iunlock(&ctlr->Lock);
	iunlock(ctlr);
}

static void


@@ 1180,9 1262,9 @@ promiscuous(void* arg, int on)
		error("card not found");
	if (ctlr->attached == 0)
		error("card not attached");
	ilock(&ctlr->Lock);
	ilock(ctlr);
	ltv_outs(ctlr, WTyp_Prom, (on?1:0));
	iunlock(&ctlr->Lock);
	iunlock(ctlr);
}

static void


@@ 1193,10 1275,10 @@ interrupt(Ureg* ,void* arg)

	if (ctlr == 0)
		return;
	ilock(&ctlr->Lock);
	ilock(ctlr);
	ctlr->nints++;
	w_intr(ether);
	iunlock(&ctlr->Lock);
	iunlock(ctlr);
}

static char* wavenames[] = {


@@ 1207,57 1289,16 @@ static char* wavenames[] = {
};

static int
reset(Ether* ether)
genreset(Ether* ether, Ctlr *ctlr)
{
	int i;
	char *p;
	Wltv ltv;
	Ctlr* ctlr;
	char *name, *p;

	if((ctlr = malloc(sizeof(Ctlr))) == nil)
		return -1;

	ilock(&ctlr->Lock);
	ctlr->ctlrno = ether->ctlrno;

	if (ether->port==0)
		ether->port=WDfltIOB;
	ctlr->iob = ether->port;
	if (ether->irq==0)
		ether->irq=WDfltIRQ;

	if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
		print("#l%d: port 0x%lx in use\n",
				ether->ctlrno, ether->port);
		goto abort1;
	}

	for (i=0; wavenames[i]; i++)
		if ((ctlr->slot = pcmspecial(wavenames[i], ether))>=0)
			break;
	name = wavenames[i];

	if (name == nil){
		for (i=0; i<ether->nopt; i++){
			if (cistrncmp(ether->opt[i], "id=", 3) == 0){
				name = &ether->opt[i][3];
				if ((ctlr->slot = pcmspecial(name, ether)) < 0)
					name = nil;
			}
		}
		if (name == nil){
			DEBUG("no wavelan found\n");
			goto abort;
		}
	}

	// DEBUG("#l%d: port=0x%lx irq=%ld\n",
	//		ether->ctlrno, ether->port, ether->irq);

	w_intdis(ctlr);
	if (w_cmd(ctlr,WCmdIni,0)){
		print("#l%d: init failed\n", ether->ctlrno);
		goto abort;
		return -1;
	}
	w_intdis(ctlr);
	ltv_outs(ctlr, WTyp_Tick, 8);


@@ 1288,7 1329,7 @@ reset(Ether* ether)
	if (w_inltv(ctlr, &ltv)){
		print("#l%d: unable to read mac addr\n",
			ether->ctlrno);
		goto abort;
		return -1;
	}
	memmove(ether->ea, ltv.addr, Eaddrlen);



@@ 1321,20 1362,171 @@ reset(Ether* ether)
//		ether->ea[0], ether->ea[1], ether->ea[2],
//		ether->ea[3], ether->ea[4], ether->ea[5]);

	iunlock(&ctlr->Lock);
	return 0;
}

abort:
	iofree(ether->port);
abort1:
	iunlock(&ctlr->Lock);
	free(ctlr);
	ether->ctlr = nil;
	return -1;
static int
wavelanpcmciareset(Ether *ether)
{
	int i;
	Ctlr *ctlr;

	if((ctlr = malloc(sizeof(Ctlr))) == nil)
		return -1;

	ilock(ctlr);
	ctlr->ctlrno = ether->ctlrno;

	if (ether->port==0)
		ether->port=WDfltIOB;
	ctlr->iob = ether->port;
	if (ether->irq==0)
		ether->irq=WDfltIRQ;

	if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
		print("#l%d: port 0x%lx in use\n",
				ether->ctlrno, ether->port);
		goto abort1;
	}

	/*
	 * If id= is specified, card must match.  Otherwise try generic.
	 */
	ctlr->slot = -1;
	for(i=0; i<ether->nopt; i++){
		if(cistrncmp(ether->opt[i], "id=", 3) == 0){
			if((ctlr->slot = pcmspecial(&ether->opt[i][3], ether)) < 0)
				return -1;
			break;
		}
	}
	if(ctlr->slot == -1){
		for (i=0; wavenames[i]; i++)
			if((ctlr->slot = pcmspecial(wavenames[i], ether))>=0)
				break;
		if(!wavenames[i]){
			DEBUG("no wavelan found\n");
			goto abort;
		}
	}

	// DEBUG("#l%d: port=0x%lx irq=%ld\n",
	//		ether->ctlrno, ether->port, ether->irq);

	if(genreset(ether, ctlr) < 0){
	abort:
		iofree(ether->port);
	abort1:
		iunlock(ctlr);
		free(ctlr);
		ether->ctlr = nil;
		return -1;
	}
	iunlock(ctlr);
	return 0;
}

static struct {
	int vid;
	int did;
} wavelanpci[] = {
	0x1260, 0x3873,	/* Intersil Prism2.5 */
};

static Ctlr *ctlrhead, *ctlrtail;

static void
wavelanpciscan(void)
{
	int i;
	ulong pa;
	Pcidev *p;
	Ctlr *ctlr;

	p = nil;
	while(p = pcimatch(p, 0, 0)){
		for(i=0; i<nelem(wavelanpci); i++)
			if(p->vid == wavelanpci[i].vid && p->did == wavelanpci[i].did)
				break;
		if(i==nelem(wavelanpci))
			continue;

		/*
		 * On the Prism, bar[0] is the memory-mapped register address (4KB),
		 */
		if(p->mem[0].size != 4096){
			print("wavelanpci: %.4ux %.4ux: unlikely mmio size\n", p->vid, p->did);
			continue;
		}

		ctlr = malloc(sizeof(Ctlr));
		ctlr->pcidev = p;
		pa = upamalloc(p->mem[0].bar&~0xF, p->mem[0].size, 0);
		if(pa == 0){
			print("wavelanpci: %.4ux %.4ux: upamalloc 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size);
			free(ctlr);
			continue;
		}
		ctlr->mmb = (ushort*)KADDR(pa);
		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
		pcisetbme(p);
	}
}

static int
wavelanpcireset(Ether *ether)
{
	int i;
	Ctlr *ctlr;

	if(ctlrhead == nil)
		wavelanpciscan();

	/*
	 * Allow plan9.ini to set vid, did?
	 */
	for(ctlr=ctlrhead; ctlr!=nil; ctlr=ctlr->next)
		if(ctlr->active == 0)
			break;
	if(ctlr == nil)
		return -1;

	ilock(ctlr);
	ether->irq = ctlr->pcidev->intl;
	ether->tbdf = ctlr->pcidev->tbdf;

	/*
	 * Really hard reset.
	 */
	csr_outs(ctlr, WR_PciCor, 0x0080);
	delay(250);
	csr_outs(ctlr, WR_PciCor, 0x0000);
	delay(500);
	for(i=0; i<2*10; i++){
		if(!(csr_ins(ctlr, WR_Cmd)&WCmdBusy))
			break;
		delay(100);
	}
	if(i >= 2*10)
		print("wavelan pci %.4ux %.4ux: reset timeout %.4ux\n",
			ctlr->pcidev->vid, ctlr->pcidev->did, csr_ins(ctlr, WR_Cmd));

	if(genreset(ether, ctlr) < 0){
		iunlock(ctlr);
		ether->ctlr = nil;
		return -1;
	}
	iunlock(ctlr);
	return 0;
}
	
void
etherwavelanlink(void)
{
	addethercard("wavelan", reset);
	addethercard("wavelan", wavelanpcmciareset);
	addethercard("wavelanpci", wavelanpcireset);
}

M port/devaudio.c => port/devaudio.c +2 -2
@@ 1016,8 1016,8 @@ audioread(Chan *c, void *v, long n, vlong off)

	case Qstatus:
		buf[0] = 0;
		snprint(buf, sizeof(buf), "bytes %lud\ntime %lld\n",
			audio.totcount, audio.tottime);
		snprint(buf, sizeof(buf), "bufsize %6d buffered %6d offset  %10lud time %19lld\n",
			Bufsize, audio.buffered, audio.totcount, audio.tottime);
		return readstr(offset, a, n, buf);

	case Qvolume: