shithub: riscv

Download patch

ref: b74418c2ce5c64ba39473e2eb50ea1112be63109
parent: e7e04b5cbbedfb1411fcc38b7e7f67db98a251c2
author: cinap_lenrek <cinap_lenrek@localhost>
date: Wed May 18 15:57:31 EDT 2011

sb16: new approach, works in qemu

--- a/sys/src/9/pc/audiosb16.c
+++ b/sys/src/9/pc/audiosb16.c
@@ -9,8 +9,9 @@
 #include "io.h"
 #include "../port/error.h"
 
-typedef struct	AQueue	AQueue;
-typedef struct	Buf	Buf;
+typedef struct	Ring	Ring;
+typedef struct	Blaster	Blaster;
+typedef struct	Ctlr	Ctlr;
 
 enum
 {
@@ -31,40 +32,44 @@
 
 	Speed		= 44100,
 	Ncmd		= 50,		/* max volume command words */
-};
 
-enum
-{
-	Bufsize	= 1024,	/* 5.8 ms each, must be power of two */
-	Nbuf	= 128,	/* .74 seconds total */
-	SBswab	= 0,
+	Blocksize	= 4096,
+	Blocks		= 65536/Blocksize,
 };
 
-#define CACHELINESZ		8
-#define UNCACHED(type, v)	(type*)((ulong)(v))
-
-struct	Buf
+struct Ring
 {
-	uchar*	virt;
-	ulong	phys;
-	Buf*	next;
+	uchar	*buf;
+	ulong	nbuf;
+
+	ulong	ri;
+	ulong	wi;
 };
 
-struct	AQueue
+struct Blaster
 {
 	Lock;
-	Buf*	first;
-	Buf*	last;
+	int	reset;		/* io ports to the sound blaster */
+	int	read;
+	int	write;
+	int	wstatus;
+	int	rstatus;
+	int	mixaddr;
+	int	mixdata;
+	int	clri8;
+	int	clri16;
+	int	clri401;
+	int	dma;
+
+	void	(*startdma)(Ctlr*);
+	void	(*intr)(Ctlr*);
 };
 
-static	struct
+struct Ctlr
 {
 	QLock;
 	Rendez	vous;
-	int	buffered;		/* number of bytes en route */
-	int	curcount;		/* how much data in current buffer */
 	int	active;		/* boolean dma running */
-	int	intr;			/* boolean an interrupt has happened */
 	int	rivol[Nvol];	/* right/left input/output volumes */
 	int	livol[Nvol];
 	int	rovol[Nvol];
@@ -73,134 +78,172 @@
 	int	minor;		/* SB16 minor version number */
 	ulong	totcount;	/* how many bytes processed since open */
 	vlong	tottime;	/* time at which totcount bytes were processed */
+	Ring	ring;		/* dma ring buffer */
+	Blaster	blaster;
+	Audio	*adev;
+};
 
-	Buf	buf[Nbuf];		/* buffers and queues */
-	AQueue	empty;
-	AQueue	full;
-	Buf*	current;
-	Buf*	filling;
-
-	int	probed;
-	int	ctlrno;
-} audio;
-
-static	struct
+static struct
 {
 	char*	name;
 	int	flag;
 	int	ilval;		/* initial values */
 	int	irval;
-} volumes[] =
-{
-[Vaudio]		"audio",	Fout, 		50,	50,
-[Vsynth]		"synth",	Fin|Fout,	0,	0,
-[Vcd]		"cd",		Fin|Fout,	0,	0,
-[Vline]		"line",	Fin|Fout,	0,	0,
-[Vmic]		"mic",	Fin|Fout|Fmono,	0,	0,
-[Vspeaker]	"speaker",	Fout|Fmono,	0,	0,
+} volumes[] = {
+	[Vaudio]		"audio",	Fout, 		50,	50,
+	[Vsynth]		"synth",	Fin|Fout,	0,	0,
+	[Vcd]		"cd",		Fin|Fout,	0,	0,
+	[Vline]		"line",	Fin|Fout,	0,	0,
+	[Vmic]		"mic",	Fin|Fout|Fmono,	0,	0,
+	[Vspeaker]	"speaker",	Fout|Fmono,	0,	0,
 
-[Vtreb]		"treb",		Fout, 		50,	50,
-[Vbass]		"bass",		Fout, 		50,	50,
+	[Vtreb]		"treb",		Fout, 		50,	50,
+	[Vbass]		"bass",		Fout, 		50,	50,
 
-[Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
-		0
+	[Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
+	0
 };
 
-static struct
+static	char	Emajor[]	= "soundblaster not responding/wrong version";
+static	char	Emode[]		= "illegal open mode";
+static	char	Evolume[]	= "illegal volume specifier";
+
+
+static long
+buffered(Ring *r)
 {
-	Lock;
-	int	reset;		/* io ports to the sound blaster */
-	int	read;
-	int	write;
-	int	wstatus;
-	int	rstatus;
-	int	mixaddr;
-	int	mixdata;
-	int	clri8;
-	int	clri16;
-	int	clri401;
-	int	dma;
+	ulong ri, wi;
 
-	void	(*startdma)(void);
-	void	(*intr)(void);
-} blaster;
+	ri = r->ri;
+	wi = r->wi;
+	if(wi >= ri)
+		return wi - ri;
+	else
+		return r->nbuf - (ri - wi);
+}
 
-static	void	swab(uchar*);
+static long
+available(Ring *r)
+{
+	long m;
 
-static	char	Emajor[]	= "soundblaster not responding/wrong version";
-static	char	Emode[]		= "illegal open mode";
-static	char	Evolume[]	= "illegal volume specifier";
+	m = (r->nbuf - 1) - buffered(r);
+	if(m < 0)
+		m = 0;
+	return m;
+}
 
+static long
+readring(Ring *r, uchar *p, long n)
+{
+	long n0, m;
+
+	n0 = n;
+	while(n > 0){
+		if((m = buffered(r)) <= 0)
+			break;
+		if(m > n)
+			m = n;
+		if(p){
+			if(r->ri + m > r->nbuf)
+				m = r->nbuf - r->ri;
+			memmove(p, r->buf + r->ri, m);
+			p += m;
+		}
+		r->ri = (r->ri + m) % r->nbuf;
+		n -= m;
+	}
+	return n0 - n;
+}
+
+static long
+writering(Ring *r, uchar *p, long n)
+{
+	long n0, m;
+
+	n0 = n;
+	while(n > 0){
+		if((m = available(r)) <= 0)
+			break;
+		if(m > n)
+			m = n;
+		if(p){
+			if(r->wi + m > r->nbuf)
+				m = r->nbuf - r->wi;
+			memmove(r->buf + r->wi, p, m);
+			p += m;
+		}
+		r->wi = (r->wi + m) % r->nbuf;
+		n -= m;
+	}
+	return n0 - n;
+}
+
 static	int
-sbcmd(int val)
+sbcmd(Blaster *blaster, int val)
 {
 	int i, s;
 
 	for(i=1<<16; i!=0; i--) {
-		s = inb(blaster.wstatus);
+		s = inb(blaster->wstatus);
 		if((s & 0x80) == 0) {
-			outb(blaster.write, val);
+			outb(blaster->write, val);
 			return 0;
 		}
 	}
-	print("#A%d: sbcmd (%#.2x) timeout\n", audio.ctlrno, val);	/**/
 	return 1;
 }
 
 static	int
-sbread(void)
+sbread(Blaster *blaster)
 {
 	int i, s;
 
 	for(i=1<<16; i!=0; i--) {
-		s = inb(blaster.rstatus);
+		s = inb(blaster->rstatus);
 		if((s & 0x80) != 0) {
-			return inb(blaster.read);
+			return inb(blaster->read);
 		}
 	}
-	print("#A%d: sbread did not respond\n", audio.ctlrno);	/**/
 	return -1;
 }
 
 static int
-ess1688w(int reg, int val)
+ess1688w(Blaster *blaster, int reg, int val)
 {
-	if(sbcmd(reg) || sbcmd(val))
+	if(sbcmd(blaster, reg) || sbcmd(blaster, val))
 		return 1;
-
 	return 0;
 }
 
 static int
-ess1688r(int reg)
+ess1688r(Blaster *blaster, int reg)
 {
-	if(sbcmd(0xC0) || sbcmd(reg))
+	if(sbcmd(blaster, 0xC0) || sbcmd(blaster, reg))
 		return -1;
-
-	return sbread();
+	return sbread(blaster);
 }
 
 static	int
-mxcmd(int addr, int val)
+mxcmd(Blaster *blaster, int addr, int val)
 {
-
-	outb(blaster.mixaddr, addr);
-	outb(blaster.mixdata, val);
+	outb(blaster->mixaddr, addr);
+	outb(blaster->mixdata, val);
 	return 1;
 }
 
 static	int
-mxread(int addr)
+mxread(Blaster *blaster, int addr)
 {
 	int s;
 
-	outb(blaster.mixaddr, addr);
-	s = inb(blaster.mixdata);
+	outb(blaster->mixaddr, addr);
+	s = inb(blaster->mixdata);
 	return s;
 }
 
 static	void
-mxcmds(int s, int v)
+mxcmds(Blaster *blaster, int s, int v)
 {
 
 	if(v > 100)
@@ -207,23 +250,23 @@
 		v = 100;
 	if(v < 0)
 		v = 0;
-	mxcmd(s, (v*255)/100);
+	mxcmd(blaster, s, (v*255)/100);
 }
 
 static	void
-mxcmdt(int s, int v)
+mxcmdt(Blaster *blaster, int s, int v)
 {
 
 	if(v > 100)
 		v = 100;
 	if(v <= 0)
-		mxcmd(s, 0);
+		mxcmd(blaster, s, 0);
 	else
-		mxcmd(s, 255-100+v);
+		mxcmd(blaster, s, 255-100+v);
 }
 
 static	void
-mxcmdu(int s, int v)
+mxcmdu(Blaster *blaster, int s, int v)
 {
 
 	if(v > 100)
@@ -230,53 +273,56 @@
 		v = 100;
 	if(v <= 0)
 		v = 0;
-	mxcmd(s, 128-50+v);
+	mxcmd(blaster, s, 128-50+v);
 }
 
 static	void
-mxvolume(void)
+mxvolume(Ctlr *ctlr)
 {
+	Blaster *blaster;
 	int *left, *right;
 	int source;
 
 	if(0){
-		left = audio.livol;
-		right = audio.rivol;
+		left = ctlr->livol;
+		right = ctlr->rivol;
 	}else{
-		left = audio.lovol;
-		right = audio.rovol;
+		left = ctlr->lovol;
+		right = ctlr->rovol;
 	}
 
-	ilock(&blaster);
+	blaster = &ctlr->blaster;
 
-	mxcmd(0x30, 255);		/* left master */
-	mxcmd(0x31, 255);		/* right master */
-	mxcmd(0x3f, 0);		/* left igain */
-	mxcmd(0x40, 0);		/* right igain */
-	mxcmd(0x41, 0);		/* left ogain */
-	mxcmd(0x42, 0);		/* right ogain */
+	ilock(blaster);
 
-	mxcmds(0x32, left[Vaudio]);
-	mxcmds(0x33, right[Vaudio]);
+	mxcmd(blaster, 0x30, 255);		/* left master */
+	mxcmd(blaster, 0x31, 255);		/* right master */
+	mxcmd(blaster, 0x3f, 0);		/* left igain */
+	mxcmd(blaster, 0x40, 0);		/* right igain */
+	mxcmd(blaster, 0x41, 0);		/* left ogain */
+	mxcmd(blaster, 0x42, 0);		/* right ogain */
 
-	mxcmds(0x34, left[Vsynth]);
-	mxcmds(0x35, right[Vsynth]);
+	mxcmds(blaster, 0x32, left[Vaudio]);
+	mxcmds(blaster, 0x33, right[Vaudio]);
 
-	mxcmds(0x36, left[Vcd]);
-	mxcmds(0x37, right[Vcd]);
+	mxcmds(blaster, 0x34, left[Vsynth]);
+	mxcmds(blaster, 0x35, right[Vsynth]);
 
-	mxcmds(0x38, left[Vline]);
-	mxcmds(0x39, right[Vline]);
+	mxcmds(blaster, 0x36, left[Vcd]);
+	mxcmds(blaster, 0x37, right[Vcd]);
 
-	mxcmds(0x3a, left[Vmic]);
-	mxcmds(0x3b, left[Vspeaker]);
+	mxcmds(blaster, 0x38, left[Vline]);
+	mxcmds(blaster, 0x39, right[Vline]);
 
-	mxcmdu(0x44, left[Vtreb]);
-	mxcmdu(0x45, right[Vtreb]);
+	mxcmds(blaster, 0x3a, left[Vmic]);
+	mxcmds(blaster, 0x3b, left[Vspeaker]);
 
-	mxcmdu(0x46, left[Vbass]);
-	mxcmdu(0x47, right[Vbass]);
+	mxcmdu(blaster, 0x44, left[Vtreb]);
+	mxcmdu(blaster, 0x45, right[Vtreb]);
 
+	mxcmdu(blaster, 0x46, left[Vbass]);
+	mxcmdu(blaster, 0x47, right[Vbass]);
+
 	source = 0;
 	if(left[Vsynth])
 		source |= 1<<6;
@@ -293,85 +339,34 @@
 	if(left[Vmic])
 		source |= 1<<0;
 	if(0)
-		mxcmd(0x3c, 0);		/* output switch */
+		mxcmd(blaster, 0x3c, 0);		/* output switch */
 	else
-		mxcmd(0x3c, source);
-	mxcmd(0x3d, source);		/* input left switch */
-	mxcmd(0x3e, source);		/* input right switch */
-	iunlock(&blaster);
+		mxcmd(blaster, 0x3c, source);
+	mxcmd(blaster, 0x3d, source);		/* input left switch */
+	mxcmd(blaster, 0x3e, source);		/* input right switch */
+	iunlock(blaster);
 }
 
-static	Buf*
-getbuf(AQueue *q)
-{
-	Buf *b;
-
-	ilock(q);
-	b = q->first;
-	if(b)
-		q->first = b->next;
-	iunlock(q);
-
-	return b;
-}
-
 static	void
-putbuf(AQueue *q, Buf *b)
+contindma(Ctlr *ctlr)
 {
+	Blaster *blaster;
+	Ring *ring;
 
-	ilock(q);
-	b->next = 0;
-	if(q->first)
-		q->last->next = b;
-	else
-		q->first = b;
-	q->last = b;
-	iunlock(q);
-}
+	blaster = &ctlr->blaster;
+	ring = &ctlr->ring;
+	if(buffered(ring) >= Blocksize){
+		ring->ri = ring->nbuf - dmacount(blaster->dma);
 
-/*
- * move the dma to the next buffer
- */
-static	void
-contindma(void)
-{
-	Buf *b;
-
-	if(!audio.active)
-		goto shutdown;
-
-	b = audio.current;
-	if(b){
-		audio.totcount += Bufsize;
-		audio.tottime = todget(nil);
+		ctlr->totcount += Blocksize;
+		ctlr->tottime = todget(nil);
+	}else{
+		dmaend(blaster->dma);
+		sbcmd(blaster, 0xd9);	/* exit at end of count */
+		sbcmd(blaster, 0xd5);	/* pause */
+		ctlr->active = 0;
 	}
-	if(0) {
-		if(b){
-			putbuf(&audio.full, b);
-			audio.buffered += Bufsize;
-		}
-		b = getbuf(&audio.empty);
-	} else {
-		if(b){
-			putbuf(&audio.empty, b);
-			audio.buffered -= Bufsize;
-		}
-		b = getbuf(&audio.full);
-	}
-	audio.current = b;
-	if(b == 0)
-		goto shutdown;
-	if(dmasetup(blaster.dma, b->virt, Bufsize, 0) >= 0)
-		return;
-	print("#A%d: dmasetup fail\n", audio.ctlrno);
-	putbuf(&audio.empty, b);
-
-shutdown:
-	dmaend(blaster.dma);
-	sbcmd(0xd9);				/* exit at end of count */
-	sbcmd(0xd5);				/* pause */
-	audio.curcount = 0;
-	audio.active = 0;
+	wakeup(&ctlr->vous);
 }
 
 /*
@@ -379,60 +374,64 @@
  * start first dma
  */
 static	void
-sb16startdma(void)
+sb16startdma(Ctlr *ctlr)
 {
-	ulong count;
+	Blaster *blaster;
+	Ring *ring;
+	long count;
 	int speed;
 
-	ilock(&blaster);
-	dmaend(blaster.dma);
+	blaster = &ctlr->blaster;
+	ring = &ctlr->ring;
+	ilock(blaster);
+	dmaend(blaster->dma);
 	if(0) {
-		sbcmd(0x42);			/* input sampling rate */
-		speed = audio.livol[Vspeed];
+		sbcmd(blaster, 0x42);			/* input sampling rate */
+		speed = ctlr->livol[Vspeed];
 	} else {
-		sbcmd(0x41);			/* output sampling rate */
-		speed = audio.lovol[Vspeed];
+		sbcmd(blaster, 0x41);			/* output sampling rate */
+		speed = ctlr->lovol[Vspeed];
 	}
-	sbcmd(speed>>8);
-	sbcmd(speed);
+	sbcmd(blaster, speed>>8);
+	sbcmd(blaster, speed);
 
 	if(0)
-		sbcmd(0xbe);			/* A/D, autoinit */
+		sbcmd(blaster, 0xbe);			/* A/D, autoinit */
 	else
-		sbcmd(0xb6);			/* D/A, autoinit */
-	sbcmd(0x30);				/* stereo, signed 16 bit */
+		sbcmd(blaster, 0xb6);			/* D/A, autoinit */
 
-	/*
-	 * not really sure if this is correct, but
-	 * it works in bochs
-	 */
-	count = (Bufsize >> 2) - 1;
-	sbcmd(count);
-	sbcmd(count>>8);
+	sbcmd(blaster, 0x30);				/* stereo, signed 16 bit */
 
-	audio.active = 1;
-	contindma();
-	iunlock(&blaster);
+	count = (Blocksize>>1) - 1;
+	sbcmd(blaster, count);
+	sbcmd(blaster, count>>8);
+
+	ctlr->active = 1;
+	if(dmasetup(blaster->dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){
+		ctlr->active = 0;
+		print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno);
+	}
+	iunlock(blaster);
 }
 
 static int
-ess1688reset(void)
+ess1688reset(Blaster *blaster, int ctlrno)
 {
 	int i;
 
-	outb(blaster.reset, 3);
+	outb(blaster->reset, 3);
 	delay(1);			/* >3 υs */
-	outb(blaster.reset, 0);
+	outb(blaster->reset, 0);
 	delay(1);
 
-	i = sbread();
+	i = sbread(blaster);
 	if(i != 0xAA) {
-		print("#A%d: no response %#.2x\n", audio.ctlrno, i);
+		print("#A%d: no response %#.2x\n", ctlrno, i);
 		return 1;
 	}
 
-	if(sbcmd(0xC6)){		/* extended mode */
-		print("#A%d: barf 3\n", audio.ctlrno);
+	if(sbcmd(blaster, 0xC6)){		/* extended mode */
+		print("#A%d: barf 3\n", ctlrno);
 		return 1;
 	}
 
@@ -440,23 +439,28 @@
 }
 
 static	void
-ess1688startdma(void)
+ess1688startdma(Ctlr *ctlr)
 {
+	Blaster *blaster;
+	Ring *ring;
 	ulong count;
 	int speed, x;
 
-	ilock(&blaster);
-	dmaend(blaster.dma);
+	blaster = &ctlr->blaster;
+	ring = &ctlr->ring;
 
-	ess1688reset();
+	ilock(blaster);
+	dmaend(blaster->dma);
 
+	ess1688reset(blaster, ctlr->adev->ctlrno);
+
 	/*
 	 * Set the speed.
 	 */
 	if(0)
-		speed = audio.livol[Vspeed];
+		speed = ctlr->livol[Vspeed];
 	else
-		speed = audio.lovol[Vspeed];
+		speed = ctlr->lovol[Vspeed];
 	if(speed < 4000)
 		speed = 4000;
 	else if(speed > 48000)
@@ -466,308 +470,211 @@
 		  x = 0x80|(256-(795500+speed/2)/speed);
 	else
 		  x = 128-(397700+speed/2)/speed;
-	ess1688w(0xA1, x & 0xFF);
+	ess1688w(blaster, 0xA1, x & 0xFF);
 
 	speed = (speed * 9) / 20;
 	x = 256 - 7160000 / (speed * 82);
-	ess1688w(0xA2, x & 0xFF);
+	ess1688w(blaster, 0xA2, x & 0xFF);
 
 	if(0)
-		ess1688w(0xB8, 0x0E);		/* A/D, autoinit */
+		ess1688w(blaster, 0xB8, 0x0E);		/* A/D, autoinit */
 	else
-		ess1688w(0xB8, 0x04);		/* D/A, autoinit */
-	x = ess1688r(0xA8) & ~0x03;
-	ess1688w(0xA8, x|0x01);			/* 2 channels */
-	ess1688w(0xB9, 2);			/* demand mode, 4 bytes per request */
+		ess1688w(blaster, 0xB8, 0x04);		/* D/A, autoinit */
+	x = ess1688r(blaster, 0xA8) & ~0x03;
+	ess1688w(blaster, 0xA8, x|0x01);			/* 2 channels */
+	ess1688w(blaster, 0xB9, 2);			/* demand mode, 4 bytes per request */
 
 	if(1)
-		ess1688w(0xB6, 0);		/* for output */
+		ess1688w(blaster, 0xB6, 0);		/* for output */
 
-	ess1688w(0xB7, 0x71);
-	ess1688w(0xB7, 0xBC);
+	ess1688w(blaster, 0xB7, 0x71);
+	ess1688w(blaster, 0xB7, 0xBC);
 
-	x = ess1688r(0xB1) & 0x0F;
-	ess1688w(0xB1, x|0x50);
-	x = ess1688r(0xB2) & 0x0F;
-	ess1688w(0xB2, x|0x50);
+	x = ess1688r(blaster, 0xB1) & 0x0F;
+	ess1688w(blaster, 0xB1, x|0x50);
+	x = ess1688r(blaster, 0xB2) & 0x0F;
+	ess1688w(blaster, 0xB2, x|0x50);
 
 	if(1)
-		sbcmd(0xD1);			/* speaker on */
+		sbcmd(blaster, 0xD1);			/* speaker on */
 
-	count = -Bufsize;
-	ess1688w(0xA4, count & 0xFF);
-	ess1688w(0xA5, (count>>8) & 0xFF);
-	x = ess1688r(0xB8);
-	ess1688w(0xB8, x|0x05);
+	count = -Blocksize;
+	ess1688w(blaster, 0xA4, count & 0xFF);
+	ess1688w(blaster, 0xA5, (count>>8) & 0xFF);
+	x = ess1688r(blaster, 0xB8);
+	ess1688w(blaster, 0xB8, x|0x05);
 
-	audio.active = 1;
-	contindma();
-	iunlock(&blaster);
+	ctlr->active = 1;
+	if(dmasetup(blaster->dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){
+		ctlr->active = 0;
+		print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno);
+	}
+	iunlock(blaster);
 }
 
-/*
- * if audio is stopped,
- * start it up again.
- */
-static	void
-pokeaudio(void)
-{
-	if(!audio.active)
-		blaster.startdma();
-}
-
 static void
-sb16intr(void)
+sb16intr(Ctlr *ctlr)
 {
+	Blaster *blaster;
 	int stat;
 
-	stat = mxread(0x82);		/* get irq status */
-	if(stat & 7) {
-		ilock(&blaster);
+	blaster = &ctlr->blaster;
+	ilock(blaster);
+	stat = mxread(blaster, 0x82);		/* get irq status */
+	if(stat & 3){
+		contindma(ctlr);
 		if(stat & 2)
-			inb(blaster.clri16);
+			inb(blaster->clri16);
 		else if(stat & 1)
-			inb(blaster.clri8);
-		if(stat & 3){
-			contindma();
-			iunlock(&blaster);
-
-			audio.intr = 1;
-			wakeup(&audio.vous);
-		}
-		if(stat & 4)
-			inb(blaster.clri401);
-	}
+			inb(blaster->clri8);
+	} else if(stat & 4)
+		inb(blaster->clri401);
+	iunlock(blaster);
 }
 
 static void
-ess1688intr(void)
+ess1688intr(Ctlr *ctlr)
 {
-	if(audio.active){
-		ilock(&blaster);
-		contindma();
-		inb(blaster.clri8);
-		iunlock(&blaster);
-		audio.intr = 1;
-		wakeup(&audio.vous);
-		return;
-	}
-	print("#A%d: unexpected ess1688 interrupt\n", audio.ctlrno);
-}
+	Blaster *blaster;
 
-void
-audiosbintr(void)
-{
-	/*
-	 * Carrera interrupt interface.
-	 */
-	blaster.intr();
+	blaster = &ctlr->blaster;
+	ilock(blaster);
+	contindma(ctlr);
+	inb(blaster->clri8);
+	iunlock(blaster);
 }
 
 static void
-pcaudiosbintr(Ureg*, void*)
+audiointr(Ureg *, void *arg)
 {
-	/*
-	 * x86 interrupt interface.
-	 */
-	blaster.intr();
-}
+	Audio *adev;
+	Ctlr *ctlr;
 
-static int
-anybuf(void*)
-{
-	return audio.intr;
-}
-
-/*
- * wait for some output to get
- * empty buffers back.
- */
-static int
-waitaudio(void)
-{
-
-	audio.intr = 0;
-	pokeaudio();
-	tsleep(&audio.vous, anybuf, 0, 10000);
-	if(audio.intr == 0) {
-		audio.active = 0;
-		print("#A%d: audio timeout\n", audio.ctlrno);	/**/
-		return -1;
+	adev = arg;
+	ctlr = adev->ctlr;
+	if(!ctlr->active){
+		iprint("#A%d: unexpected %s interrupt\n",
+			ctlr->adev->ctlrno, ctlr->adev->name);
+		return;
 	}
-	return 0;
+	ctlr->blaster.intr(ctlr);
 }
 
-static	void
-swab(uchar *a)
+static void
+setempty(Ctlr *ctlr)
 {
-	ulong *p, *ep, b;
-
-	if(!SBswab){
-		USED(a);
-		return;
-	}
-	p = (ulong*)a;
-	ep = p + (Bufsize>>2);
-	while(p < ep) {
-		b = *p;
-		b = (b>>24) | (b<<24) |
-			((b&0xff0000) >> 8) |
-			((b&0x00ff00) << 8);
-		*p++ = b;
-	}
+	ilock(&ctlr->blaster);
+	ctlr->ring.ri = 0;
+	ctlr->ring.wi = 0;
+	ctlr->totcount = 0;
+	ctlr->tottime = 0LL;
+	iunlock(&ctlr->blaster);
 }
 
 static void
-sbbufinit(void)
+resetlevel(Ctlr *ctlr)
 {
 	int i;
-	uchar *p;
 
-	p = (uchar*)(((ulong)xalloc((Nbuf+1) * Bufsize) + Bufsize-1) &
-		~(Bufsize-1));
-	if (p == nil)
-		panic("sbbufinit: no memory");
-	for(i=0; i<Nbuf; i++) {
-		dcflush(p, Bufsize);
-		audio.buf[i].virt = UNCACHED(uchar, p);
-		audio.buf[i].phys = (ulong)PADDR(p);
-		p += Bufsize;
+	for(i=0; volumes[i].name; i++) {
+		ctlr->lovol[i] = volumes[i].ilval;
+		ctlr->rovol[i] = volumes[i].irval;
+		ctlr->livol[i] = volumes[i].ilval;
+		ctlr->rivol[i] = volumes[i].irval;
 	}
 }
 
-static	void
-setempty(void)
+static long
+audiobuffered(Audio *adev)
 {
-	int i;
-
-	ilock(&blaster);
-	audio.empty.first = 0;
-	audio.empty.last = 0;
-	audio.full.first = 0;
-	audio.full.last = 0;
-	audio.current = 0;
-	audio.filling = 0;
-	audio.buffered = 0;
-	for(i=0; i<Nbuf; i++)
-		putbuf(&audio.empty, &audio.buf[i]);
-	audio.totcount = 0;
-	audio.tottime = 0LL;
-	iunlock(&blaster);
+	return buffered(&((Ctlr*)adev->ctlr)->ring);
 }
 
-static	void
-resetlevel(void)
+static long
+audiostatus(Audio *adev, void *a, long n, vlong off)
 {
-	int i;
+	char buf[300];
+	Ctlr *ctlr;
 
-	for(i=0; volumes[i].name; i++) {
-		audio.lovol[i] = volumes[i].ilval;
-		audio.rovol[i] = volumes[i].irval;
-		audio.livol[i] = volumes[i].ilval;
-		audio.rivol[i] = volumes[i].irval;
-	}
+	ctlr = adev->ctlr;
+	snprint(buf, sizeof(buf), 
+		"buffered %.4lx/%.4lx  offset %10lud time %19lld\n",
+		buffered(&ctlr->ring), available(&ctlr->ring),
+		ctlr->totcount, ctlr->tottime);
+	return readstr(off, a, n, buf);
 }
 
-static long
-audiobuffered(Audio *)
+static int
+inactive(void *arg)
 {
-	return audio.buffered;
+	Ctlr *ctlr = arg;
+
+	return !ctlr->active;
 }
 
-static long
-audiostatus(Audio *, void *a, long n, vlong off)
+static int
+anybuf(void *arg)
 {
-	char buf[300];
+	Ctlr *ctlr = arg;
 
-	snprint(buf, sizeof(buf), "bufsize %6d buffered %6d offset  %10lud time %19lld\n",
-		Bufsize, audio.buffered, audio.totcount, audio.tottime);
-	return readstr(off, a, n, buf);
+	return available(&ctlr->ring) || inactive(ctlr);
 }
 
 static long
-audiowrite(Audio *, void *vp, long n, vlong)
+audiowrite(Audio *adev, void *vp, long n, vlong)
 {
-	long m, n0;
-	Buf *b;
-	char *a;
+	uchar *p, *e;
+	Ctlr *ctlr;
+	Ring *ring;
+	long m;
 
-	a = vp;
-	n0 = n;
-	qlock(&audio);
+	p = vp;
+	e = p + n;
+	ctlr = adev->ctlr;
+	qlock(ctlr);
 	if(waserror()){
-		qunlock(&audio);
+		qunlock(ctlr);
 		nexterror();
 	}
-
-	while(n > 0) {
-		b = audio.filling;
-		if(b == 0) {
-			b = getbuf(&audio.empty);
-			if(b == 0) {
-				if(waitaudio())
-					pokeaudio();
+	ring = &ctlr->ring;
+	while(p < e) {
+		if((m = writering(ring, p, e - p)) <= 0){
+			if(!ctlr->active && ring->ri == 0)
+				ctlr->blaster.startdma(ctlr);
+			if(!ctlr->active){
+				setempty(ctlr);
 				continue;
 			}
-			audio.filling = b;
-			audio.curcount = 0;
+			sleep(&ctlr->vous, anybuf, ctlr);
+			continue;
 		}
-
-		m = Bufsize-audio.curcount;
-		if(m > n)
-			m = n;
-		memmove(b->virt+audio.curcount, a, m);
-
-		audio.curcount += m;
-		n -= m;
-		a += m;
-		audio.buffered += m;
-		if(audio.curcount >= Bufsize) {
-			audio.filling = 0;
-			swab(b->virt);
-			putbuf(&audio.full, b);
-			pokeaudio();
-		}
+		p += m;
 	}
 	poperror();
-	qunlock(&audio);
+	qunlock(ctlr);
 
-	return n0 - n;
+	return p - (uchar*)vp;
 }
 
 static void
-audioclose(Audio *)
+audioclose(Audio *adev)
 {
-	qlock(&audio);
+	Ctlr *ctlr;
+
+	ctlr = adev->ctlr;
+	qlock(ctlr);
 	if(waserror()){
-		qunlock(&audio);
+		qunlock(ctlr);
 		nexterror();
 	}
-	if(1) {
-		Buf *b;
-
-		/* flush out last partial buffer */
-		b = audio.filling;
-		if(b) {
-			audio.filling = 0;
-			memset(b->virt+audio.curcount, 0, Bufsize-audio.curcount);
-			audio.buffered += Bufsize-audio.curcount;
-			swab(b->virt);
-			putbuf(&audio.full, b);
-		}
-		if(!audio.active && audio.full.first)
-			pokeaudio();
-	}
-	while(audio.active && waitaudio() == 0)
-		;
-	setempty();
-	audio.curcount = 0;
+	sleep(&ctlr->vous, inactive, ctlr);
+	setempty(ctlr);
 	poperror();
-	qunlock(&audio);
+	qunlock(ctlr);
 }
 
 static int
-ess1688(ISAConf* sbconf)
+ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
 {
 	int i, major, minor;
 
@@ -774,15 +681,15 @@
 	/*
 	 * Try for ESS1688.
 	 */
-	sbcmd(0xE7);			/* get version */
-	major = sbread();
-	minor = sbread();
+	sbcmd(blaster, 0xE7);			/* get version */
+	major = sbread(blaster);
+	minor = sbread(blaster);
 	if(major != 0x68 || minor != 0x8B){
-		print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n", audio.ctlrno, major, minor);
-		return 1;
+		print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n", ctlrno, major, minor);
+		return -1;
 	}
 
-	ess1688reset();
+	ess1688reset(blaster, ctlrno);
 
 	switch(sbconf->irq){
 	case 2:
@@ -799,10 +706,10 @@
 		i = 0x50|(3<<2);
 		break;
 	default:
-		print("#A%d: bad ESS1688 irq %d\n", audio.ctlrno, sbconf->irq);
+		print("#A%d: bad ESS1688 irq %d\n", ctlrno, sbconf->irq);
 		return 1;
 	}
-	ess1688w(0xB1, i);
+	ess1688w(blaster, 0xB1, i);
 
 	switch(sbconf->dma){
 	case 0:
@@ -815,15 +722,15 @@
 		i = 0x50|(3<<2);
 		break;
 	default:
-		print("#A%d: bad ESS1688 dma %lud\n", audio.ctlrno, sbconf->dma);
+		print("#A%d: bad ESS1688 dma %lud\n", ctlrno, sbconf->dma);
 		return 1;
 	}
-	ess1688w(0xB2, i);
+	ess1688w(blaster, 0xB2, i);
 
-	ess1688reset();
+	ess1688reset(blaster, ctlrno);
 
-	blaster.startdma = ess1688startdma;
-	blaster.intr = ess1688intr;
+	blaster->startdma = ess1688startdma;
+	blaster->intr = ess1688intr;
 
 	return 0;
 }
@@ -831,26 +738,19 @@
 static int
 audioprobe(Audio *adev)
 {
+	static int irq[] = {9,5,7,10};
+
+	Ctlr *ctlr;
+	Blaster *blaster;
 	ISAConf sbconf;
 	int i, x;
-	static int irq[] = {2,5,7,10};
-	static int dma16[] = {0,5,6,7};
 
-	if(audio.probed)
-		return -1;
-
 	sbconf.port = 0x220;
-	sbconf.dma = 1;
 	sbconf.irq = 5;
+	sbconf.dma = 0;
 	if(isaconfig("audio", adev->ctlrno, &sbconf) == 0)
 		return -1;
 
-	audio.probed = 1;
-	audio.ctlrno = adev->ctlrno;
-	if(sbconf.type == nil ||
-		(cistrcmp(sbconf.type, "sb16") != 0 && 
-		 cistrcmp(sbconf.type, "ess1688") != 0))
-		return -1;
 	switch(sbconf.port){
 	case 0x220:
 	case 0x240:
@@ -858,125 +758,127 @@
 	case 0x280:
 		break;
 	default:
-		print("#A%d: bad port %#lux\n", audio.ctlrno, sbconf.port);
+		print("#A%d: bad port %#lux\n", adev->ctlrno, sbconf.port);
 		return -1;
 	}
 
 	if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){
-		print("#A%d: cannot ioalloc range %lux+0x10\n", audio.ctlrno, sbconf.port);
+		print("#A%d: cannot ioalloc range %lux+0x10\n", adev->ctlrno, sbconf.port);
 		return -1;
 	}
 	if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){
 		iofree(sbconf.port);
-		print("#A%d: cannot ioalloc range %lux+0x01\n", audio.ctlrno, sbconf.port+0x100);
+		print("#A%d: cannot ioalloc range %lux+0x01\n", adev->ctlrno, sbconf.port+0x100);
 		return -1;
 	}
 
-	blaster.reset = sbconf.port + 0x6;
-	blaster.read = sbconf.port + 0xa;
-	blaster.write = sbconf.port + 0xc;
-	blaster.wstatus = sbconf.port + 0xc;
-	blaster.rstatus = sbconf.port + 0xe;
-	blaster.mixaddr = sbconf.port + 0x4;
-	blaster.mixdata = sbconf.port + 0x5;
-	blaster.clri8 = sbconf.port + 0xe;
-	blaster.clri16 = sbconf.port + 0xf;
-	blaster.clri401 = sbconf.port + 0x100;
-	blaster.dma = sbconf.dma;
+	ctlr = malloc(sizeof(Ctlr));
+	ctlr->adev = adev;
+	adev->ctlr = ctlr;
 
-	blaster.startdma = sb16startdma;
-	blaster.intr = sb16intr;
+	blaster = &ctlr->blaster;
+	blaster->reset = sbconf.port + 0x6;
+	blaster->read = sbconf.port + 0xa;
+	blaster->write = sbconf.port + 0xc;
+	blaster->wstatus = sbconf.port + 0xc;
+	blaster->rstatus = sbconf.port + 0xe;
+	blaster->mixaddr = sbconf.port + 0x4;
+	blaster->mixdata = sbconf.port + 0x5;
+	blaster->clri8 = sbconf.port + 0xe;
+	blaster->clri16 = sbconf.port + 0xf;
+	blaster->clri401 = sbconf.port + 0x100;
+	blaster->dma = sbconf.dma;
 
-	resetlevel();
+	blaster->startdma = sb16startdma;
+	blaster->intr = sb16intr;
 
-	outb(blaster.reset, 1);
-	delay(3);			/* >3 υs */
-	outb(blaster.reset, 0);
+	resetlevel(ctlr);
+
+	outb(blaster->reset, 1);
+	delay(1);			/* >3 υs */
+	outb(blaster->reset, 0);
 	delay(1);
 
-	i = sbread();
+	i = sbread(blaster);
 	if(i != 0xaa) {
-		print("#A%d: no response #%.2x\n", audio.ctlrno, i);
+		print("#A%d: no response #%.2x\n", adev->ctlrno, i);
 		iofree(sbconf.port);
 		iofree(sbconf.port+0x100);
+		free(ctlr);
 		return -1;
 	}
 
-	sbcmd(0xe1);			/* get version */
-	audio.major = sbread();
-	audio.minor = sbread();
+	sbcmd(blaster, 0xe1);			/* get version */
+	ctlr->major = sbread(blaster);
+	ctlr->minor = sbread(blaster);
 
-	if(audio.major != 4) {
-		if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){
+	if(ctlr->major != 4) {
+		if(ctlr->major != 3 || ctlr->minor != 1 || ess1688(&sbconf, blaster, adev->ctlrno)){
 			print("#A%d: model %#.2x %#.2x; not SB 16 compatible\n",
-				audio.ctlrno, audio.major, audio.minor);
+				adev->ctlrno, ctlr->major, ctlr->minor);
 			iofree(sbconf.port);
 			iofree(sbconf.port+0x100);
 			return -1;
 		}
-		audio.major = 4;
+		ctlr->major = 4;
 	}
 
 	/*
 	 * initialize the mixer
 	 */
-	mxcmd(0x00, 0);			/* Reset mixer */
-	mxvolume();
+	mxcmd(blaster, 0x00, 0);			/* Reset mixer */
+	mxvolume(ctlr);
 
-	/*
-	 * Attempt to set IRQ/DMA channels.
-	 * On old ISA boards, these registers are writable.
-	 * On Plug-n-Play boards, these are read-only.
-	 *
-	 * To accomodate both, we write to the registers,
-	 * but then use the contents in case the write is
-	 * disallowed.
-	 */
+	/* set irq */
 	for(i=0; i<nelem(irq); i++){
-		if(irq[i] == sbconf.irq){
-			mxcmd(0x80, 1<<i);
+		if(sbconf.irq == irq[i]){
+			mxcmd(blaster, 0x80, 1<<i);
 			break;
 		}
 	}
-	x = mxread(0x80);
-	for(i=0; i<nelem(irq); i++)
+	x = mxread(blaster, 0x80);
+	for(i=0; i<nelem(irq); i++){
 		if(x & (1<<i)){
 			sbconf.irq = irq[i];
 			break;
 		}
+	}
 
-	if(blaster.dma < 4)
-		mxcmd(0x81, 1<<blaster.dma);
-	else {
-		for(i=0; i<nelem(dma16); i++){
-			if(dma16[i] == blaster.dma){
-				x = mxread(0x81);
-				mxcmd(0x81, 0x10<<i | (x & 0xf));
+	for(;;){
+		/* set 16bit dma */
+		if(blaster->dma>=5 && blaster->dma<=7){
+			x = mxread(blaster, 0x81);
+			mxcmd(blaster, 0x81, (1<<blaster->dma) & 0xF0 | (x & 0x0F));
+		}
+		x = mxread(blaster, 0x81);
+		for(i=5; i<=7; i++){
+			if(x & (1<<i)){
+				blaster->dma = i;
 				break;
 			}
 		}
-	}
-	x = mxread(0x81);
-	for(i=0; i<4; i++)
-		if(x & (1<<i))
-			blaster.dma = i;
-	for(i=0; i<nelem(dma16); i++)
-		if(x & (0x10<<i)){
-			blaster.dma = dma16[i];
-			break;
+		if(blaster->dma<5){
+			blaster->dma = 7;
+			continue;
 		}
+		break;
+	}
 
-	print("#A%d: %s port 0x%04lux irq %d dma %d\n", audio.ctlrno, sbconf.type,
-		sbconf.port, sbconf.irq, blaster.dma);
+	print("#A%d: %s port 0x%04lux irq %d dma %d\n", adev->ctlrno, sbconf.type,
+		sbconf.port, sbconf.irq, blaster->dma);
 
-	if(dmainit(blaster.dma, Bufsize))
+	ctlr->ring.nbuf = Blocks*Blocksize;
+	if(dmainit(blaster->dma, ctlr->ring.nbuf)){
+		free(ctlr);
 		return -1;
-	intrenable(sbconf.irq, pcaudiosbintr, 0, BUSUNKNOWN, "sb16");
+	}
+	ctlr->ring.buf = dmabva(blaster->dma);
 
-	sbbufinit();
-	setempty();
-	mxvolume();
+	intrenable(sbconf.irq, audiointr, adev, BUSUNKNOWN, sbconf.type);
 
+	setempty(ctlr);
+	mxvolume(ctlr);
+
 	adev->write = audiowrite;
 	adev->close = audioclose;
 	adev->status = audiostatus;
@@ -989,4 +891,5 @@
 audiosb16link(void)
 {
 	addaudiocard("sb16", audioprobe);
+	addaudiocard("ess1688", audioprobe);
 }
--- a/sys/src/9/pc/devfloppy.c
+++ b/sys/src/9/pc/devfloppy.c
@@ -857,7 +857,7 @@
 		dmaend(DMAchan);
 		nexterror();
 	}
-	dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
+	dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread ? DMAREAD : DMAWRITE);
 	if(dp->len < 0)
 		error(Eio);
 
@@ -999,7 +999,7 @@
 			dmaend(DMAchan);
 			nexterror();
 		}
-		if(dmasetup(DMAchan, buf, bp-buf, 0) < 0)
+		if(dmasetup(DMAchan, buf, bp-buf, DMAWRITE) < 0)
 			error(Eio);
 
 		/*
--- a/sys/src/9/pc/dma.c
+++ b/sys/src/9/pc/dma.c
@@ -18,7 +18,7 @@
 	int	blen;		/* bounce buffer length */
 	void*	va;		/* virtual address destination/src */
 	long	len;		/* bytes to be transferred */
-	int	isread;
+	int	flags;
 };
 
 /*
@@ -81,7 +81,7 @@
 	if(i8237dma > 2)
 		i8237dma = 2;
 
-	bva = xspanalloc(64*1024*i8237dma, BY2PG, 64*1024);
+	bva = xspanalloc(64*1024*i8237dma, 0, 64*1024);
 	if(bva == nil || PADDR(bva)+64*1024*i8237dma > 16*MB){
 		/*
 		 * This will panic with the current
@@ -137,11 +137,38 @@
 	xp->bpa = PADDR(xp->bva);
 	xp->blen = maxtransfer;
 	xp->len = 0;
-	xp->isread = 0;
+	xp->flags = 0;
 
 	return 0;
 }
 
+void*
+dmabva(int chan)
+{
+	DMA *dp;
+	DMAxfer *xp;
+
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+	xp = &dp->x[chan];
+	return xp->bva;
+}
+
+int
+dmacount(int chan)
+{
+	int     retval;
+	DMA     *dp;
+ 
+	dp = &dma[(chan>>2)&1];
+	chan = chan & 3;
+	ilock(dp);
+	retval = inb(dp->count[chan]);
+	retval |= inb(dp->count[chan]) << 8;
+	iunlock(dp);
+	return ((retval<<dp->shift)+1) & 0xFFFF;
+}
+
 /*
  *  setup a dma transfer.  if the destination is not in kernel
  *  memory, allocate a page for the transfer.
@@ -153,7 +180,7 @@
  *  boundaries)
  */
 long
-dmasetup(int chan, void *va, long len, int isread)
+dmasetup(int chan, void *va, long len, int flags)
 {
 	DMA *dp;
 	ulong pa;
@@ -175,11 +202,11 @@
 			return -1;
 		if(len > xp->blen)
 			len = xp->blen;
-		if(!isread)
+		if(!(flags & DMAREAD))
 			memmove(xp->bva, va, len);
 		xp->va = va;
 		xp->len = len;
-		xp->isread = isread;
+		xp->flags = flags;
 		pa = xp->bpa;
 	}
 	else
@@ -189,7 +216,7 @@
 	 * this setup must be atomic
 	 */
 	ilock(dp);
-	mode = (isread ? 0x44 : 0x48) | chan;
+	mode = ((flags & DMAREAD) ? 0x44 : 0x48) | ((flags & DMALOOP) ? 0x10 : 0) | chan;
 	outb(dp->mode, mode);	/* single mode dma (give CPU a chance at mem) */
 	outb(dp->page[chan], pa>>16);
 	outb(dp->cbp, 0);		/* set count & address to their first byte */
@@ -238,7 +265,7 @@
 	iunlock(dp);
 
 	xp = &dp->x[chan];
-	if(xp->len == 0 || !xp->isread)
+	if(xp->len == 0 || !(xp->flags & DMAREAD))
 		return;
 
 	/*
@@ -248,17 +275,3 @@
 	xp->len = 0;
 }
 
-/*
-int
-dmacount(int chan)
-{
-	int     retval;
-	DMA     *dp;
- 
-	dp = &dma[(chan>>2)&1];
-	outb(dp->cbp, 0);
-	retval = inb(dp->count[chan]);
-	retval |= inb(dp->count[chan]) << 8;
-	return((retval<<dp->shift)+1);
-}
- */
--- a/sys/src/9/pc/fns.h
+++ b/sys/src/9/pc/fns.h
@@ -18,10 +18,14 @@
 void	cpuidprint(void);
 void	(*cycles)(uvlong*);
 void	delay(int);
+void*	dmabva(int);
 int	dmacount(int);
 int	dmadone(int);
 void	dmaend(int);
 int	dmainit(int, int);
+#define DMAWRITE 0
+#define DMAREAD 1
+#define DMALOOP 2
 long	dmasetup(int, void*, long, int);
 #define	evenaddr(x)				/* x86 doesn't care */
 void	fpclear(void);
@@ -181,5 +185,3 @@
 #define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
 #define	KADDR(a)	kaddr(a)
 #define PADDR(a)	paddr((void*)(a))
-
-#define	dcflush(a, b)
--- a/sys/src/9/port/devaudio.c
+++ b/sys/src/9/port/devaudio.c
@@ -18,6 +18,7 @@
 	Qaudioctl,
 	Qaudiostatus,
 	Qvolume,
+
 	Maxaudioprobes = 8,
 };
 
@@ -48,19 +49,26 @@
 	int i, ctlrno = 0;
 	Audio **pp;
 	Audioprobe *probe;
+
 	pp = &audiodevs;
 	*pp = malloc(sizeof(Audio));
-	(*pp)->ctlrno = ctlrno++;
-	for(i = 0; i < naudioprobes; i++){
+
+	for(i=0; i<naudioprobes; i++){
 		probe = &audioprobes[i];
-		(*pp)->name = probe->name;
-		while(!probe->probe(*pp)){
+
+		for(;;){
+			memset(*pp, 0, sizeof(Audio));
+			(*pp)->ctlrno = ctlrno;
+			(*pp)->name = probe->name;
+			if(probe->probe(*pp))
+				break;
+
+			ctlrno++;
 			pp = &(*pp)->next;
 			*pp = malloc(sizeof(Audio));
-			(*pp)->ctlrno = ctlrno++;
-			(*pp)->name = probe->name;
 		}
 	}
+
 	free(*pp);
 	*pp = nil;
 }