~kris/9p

9hist

a45354107bf6d6b5174c23ec4a240b5689bbf06d — David du Colombier 31 years ago 5a2a419
Plan 9 from Bell Labs 1995-02-14
9 files changed, 321 insertions(+), 112 deletions(-)

M carrera/audio.h
M carrera/dat.h
A carrera/dma.c
M carrera/fns.h
M carrera/main.c
M pc/audio.h
M pc/devata.c
M pc/dma.c
M port/devaudio.c
M carrera/audio.h => carrera/audio.h +2 -0
@@ 5,6 5,8 @@ enum
	Dma		= 6,
	Irq		= 7,

	SBswab		= 1,

	DMA2_WRMASK	= 0xd4,
	DMA2_WRMODE	= 0xd6,
	DMA2_CLRBP	= 0xd8,

M carrera/dat.h => carrera/dat.h +7 -0
@@ 1,5 1,6 @@
typedef struct Conf	Conf;
typedef struct FPsave	FPsave;
typedef struct ISAConf	ISAConf;
typedef struct KMap	KMap;
typedef struct Lance	Lance;
typedef struct Lancemem	Lancemem;


@@ 150,6 151,12 @@ struct
	short	exiting;
}active;

struct ISAConf {
	char	type[NAMELEN];
	ulong	port;
	ulong	irq;
};

extern KMap kpte[];
extern register Mach	*m;
extern register Proc	*up;

A carrera/dma.c => carrera/dma.c +125 -0
@@ 0,0 1,125 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include 	"io.h"

/*
 *  headland chip set for the safari.
 */
typedef struct DMAport	DMAport;
typedef struct DMA	DMA;

enum
{
	/*
	 *  the byte registers for DMA0 are all one byte apart
	 */
	Dma0=		0x00,
	Dma0status=	Dma0+0x8,	/* status port */
	Dma0reset=	Dma0+0xD,	/* reset port */

	/*
	 *  the byte registers for DMA1 are all two bytes apart (why?)
	 */
	Dma1=		0xC0,
	Dma1status=	Dma1+2*0x8,	/* status port */
	Dma1reset=	Dma1+2*0xD,	/* reset port */
};

/*
 *  the dma controllers.  the first half of this structure specifies
 *  the I/O ports used by the DMA controllers.
 */
struct DMAport
{
	uchar	addr[4];	/* current address (4 channels) */
	uchar	count[4];	/* current count (4 channels) */
	uchar	page[4];	/* page registers (4 channels) */
	uchar	cmd;		/* command status register */
	uchar	req;		/* request registers */
	uchar	sbm;		/* single bit mask register */
	uchar	mode;		/* mode register */
	uchar	cbp;		/* clear byte pointer */
	uchar	mc;		/* master clear */
	uchar	cmask;		/* clear mask register */
	uchar	wam;		/* write all mask register bit */
};

struct DMA
{
	DMAport;
	int	shift;
	Lock;
};

DMA dma[2] = {
	{ 0x00, 0x02, 0x04, 0x06,
	  0x01, 0x03, 0x05, 0x07,
	  0x87, 0x83, 0x81, 0x82,
	  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
	 0 },

	{ 0xc0, 0xc4, 0xc8, 0xcc,
	  0xc2, 0xc6, 0xca, 0xce,
	  0x8f, 0x8b, 0x89, 0x8a,
	  0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
	 1 },
};

/*
 * setup a dma transfer.
 * needs to change if the address is over 16meg
 *
 *  we assume BIOS has set up the command register before we
 *  are booted.
 */
long
dmasetup(int chan, void *va, long len, int isread)
{
	DMA *dp;
	ulong pa;
	uchar mode;

	dp = &dma[(chan>>2)&1];
	chan = chan & 3;

	pa = PADDR(va);

	/*
	 * this setup must be atomic
	 */
	ilock(dp);
	mode = (isread ? 0x44 : 0x48) | chan;
	EISAOUTB(dp->mode, mode);		/* single mode dma (give CPU a chance at mem) */
	EISAOUTB(dp->page[chan], pa>>16);
	EISAOUTB(dp->cbp, 0);		/* set count & address to their first byte */
	EISAOUTB(dp->addr[chan], pa>>dp->shift);		/* set address */
	EISAOUTB(dp->addr[chan], pa>>(8+dp->shift));
	EISAOUTB(dp->count[chan], (len>>dp->shift)-1);		/* set count */
	EISAOUTB(dp->count[chan], ((len>>dp->shift)-1)>>8);
	EISAOUTB(dp->sbm, chan);		/* enable the channel */
	iunlock(dp);

	return len;
}

/*
 *  this must be called after a dma has been completed.
 */
void
dmaend(int chan)
{
	DMA *dp;

	dp = &dma[(chan>>2)&1];
	chan = chan & 3;

	/*
	 *  disable the channel
	 */
	ilock(dp);
	EISAOUTB(dp->sbm, 4|chan);
	iunlock(dp);
}

M carrera/fns.h => carrera/fns.h +3 -0
@@ 19,6 19,8 @@ void		consoff(void);
int		consputc(int);
void		dcflush(void*, ulong);
void		dcinvalidate(void*, ulong);
void		dmaend(int);
long		dmasetup(int, void*, long, int);
void		epcenable(ulong);
void		epcinit(int, int);
void		evenaddr(ulong);


@@ 42,6 44,7 @@ void		icflush(void *, ulong);
void		intr(Ureg*);
void		ioinit(int);
int		iprint(char*, ...);
int		isaconfig(char*, int, ISAConf*);
int		kbdinit(void);
void		kbdintr(void);
void		kfault(Ureg*);

M carrera/main.c => carrera/main.c +15 -0
@@ 480,6 480,21 @@ buzz(int f, int d)
}

/*
 * dummy routine for interoperability with pc's
 */
int
isaconfig(char *class, int ctlrno, ISAConf *isa)
{
	if(strcmp(class, "audio") == 0 && ctlrno == 0){
		strcpy(isa->type, "sb16");
		isa->port = 0x220;
		isa->irq = 7;
		return 1;
	}
	return 0;
}

/*
	register offsets of ARCS prom jmpbuf
		JB_PC		0
		JB_SP		1

M pc/audio.h => pc/audio.h +1 -25
@@ 3,31 3,7 @@ enum
	Bufsize		= 16*1024,	/* 92 ms each */
	Nbuf		= 16,		/* 1.5 seconds total */
	Dma		= 6,
	Irq		= 7,

	DMA2_WRMASK	= 0xd4,
	DMA2_WRMODE	= 0xd6,
	DMA2_CLRBP	= 0xd8,

	DMA6_ADDRESS	= 0xc8,
	DMA6_COUNT	= 0xca,
	DMA6_LPAGE	= 0x89,
	DMA6_HPAGE	= 0x489,

	Dspport		= 0x220,

	PORT_RESET	= Dspport + 0x6,
	PORT_READ	= Dspport + 0xA,
	PORT_WRITE	= Dspport + 0xC,
	PORT_WSTATUS	= Dspport + 0xC,
	PORT_RSTATUS	= Dspport + 0xE,

	PORT_MIXER_ADDR	= Dspport + 0x4,
	PORT_MIXER_DATA	= Dspport + 0x5,

	PORT_CLRI8	= Dspport + 0xE,
	PORT_CLRI16	= Dspport + 0xF,
	PORT_CLRI401	= Dspport + 0x100,
	SBswab		= 0,
};

#define seteisadma(a, b)	;

M pc/devata.c => pc/devata.c +13 -13
@@ 118,7 118,7 @@ struct Controller
{
	QLock;			/* exclusive access to the controller */

	Lock;			/* exclusive access to the registers */
	Lock	reglock;	/* exclusive access to the registers */

	int	confused;	/* needs to be recalibrated (or worse) */
	int	pbase;		/* base port */


@@ 566,7 566,7 @@ ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf)
	dp->usetime = m->ticks;
	cmdreadywait(dp);

	ilock(cp);
	ilock(&cp->reglock);
	cp->sofar = 0;
	cp->buf = buf;
	cp->nsecs = len;


@@ 589,7 589,7 @@ ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf)
		outss(cp->pbase+Pdata, cp->buf, dp->bytes/2);
	} else
		stat = 0;
	iunlock(cp);
	iunlock(&cp->reglock);

	if(stat & Serr)
		error(Eio);


@@ 646,12 646,12 @@ atasetbuf(Drive *dp, int on)

	cmdreadywait(dp);

	ilock(cp);
	ilock(&cp->reglock);
	cp->cmd = Csetbuf;
	outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);	/* read look ahead */
	outb(cp->pbase+Pdh, 0x20 | (dp->drive<<4));
	outb(cp->pbase+Pcmd, Csetbuf);
	iunlock(cp);
	iunlock(&cp->reglock);

	sleep(&cp->r, cmddone, cp);



@@ 727,7 727,7 @@ ataident(Drive *dp)

	cmdreadywait(dp);

	ilock(cp);
	ilock(&cp->reglock);
	cp->nsecs = 1;
	cp->sofar = 0;
	cp->cmd = Cident;


@@ 735,7 735,7 @@ ataident(Drive *dp)
	cp->buf = buf;
	outb(cp->pbase+Pdh, 0x20 | (dp->drive<<4));
	outb(cp->pbase+Pcmd, Cident);
	iunlock(cp);
	iunlock(&cp->reglock);

	sleep(&cp->r, cmddone, cp);
	if(cp->status & Serr){


@@ 812,7 812,7 @@ ataprobe(Drive *dp, int cyl, int sec, int head)

	cmdreadywait(dp);

	ilock(cp);
	ilock(&cp->reglock);
	cp->cmd = Cread;
	cp->dp = dp;
	cp->status = 0;


@@ 825,7 825,7 @@ ataprobe(Drive *dp, int cyl, int sec, int head)
	outb(cp->pbase+Pcyllsb, cyl);
	outb(cp->pbase+Pcylmsb, cyl>>8);
	outb(cp->pbase+Pcmd, Cread);
	iunlock(cp);
	iunlock(&cp->reglock);

	sleep(&cp->r, cmddone, cp);



@@ 1058,7 1058,7 @@ ataintr(Ureg *ur, void *arg)
	cp = &atac[0];
	dp = cp->dp;

	ilock(cp);
	ilock(&cp->reglock);

	loop = 0;
	while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){


@@ 1152,7 1152,7 @@ ataintr(Ureg *ur, void *arg)
		break;
	}

	iunlock(cp);
	iunlock(&cp->reglock);
}

void


@@ 1172,12 1172,12 @@ hardclock(void)

		diff = TK2SEC(m->ticks - dp->usetime);
		if((dp->state == Sspinning) && (diff >= spindowntime)){
			ilock(cp);
			ilock(&cp->reglock);
			cp->cmd = Cstandby;
			outb(cp->pbase+Pcount, 0);
			outb(cp->pbase+Pdh, 0x20 | (dp->drive<<4) | 0);
			outb(cp->pbase+Pcmd, cp->cmd);
			iunlock(cp);
			iunlock(&cp->reglock);
			dp->state = Sstandby;
		}
	}

M pc/dma.c => pc/dma.c +20 -16
@@ 56,13 56,12 @@ struct DMAport
	uchar	mc;		/* master clear */
	uchar	cmask;		/* clear mask register */
	uchar	wam;		/* write all mask register bit */

	Lock;
};

struct DMA
{
	DMAport;
	int	shift;
	Lock;
	DMAxfer	x[4];
};


@@ 71,11 70,14 @@ DMA dma[2] = {
	{ 0x00, 0x02, 0x04, 0x06,
	  0x01, 0x03, 0x05, 0x07,
	  0x87, 0x83, 0x81, 0x82,
	  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
	{ 0xc0, 0xc6, 0xca, 0xce,
	  0xc4, 0xc8, 0xcc, 0xcf,
	  0x80, 0x8b, 0x89, 0x8a,
	  0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde },
	  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
	 0 },

	{ 0xc0, 0xc4, 0xc8, 0xcc,
	  0xc2, 0xc6, 0xca, 0xce,
	  0x8f, 0x8b, 0x89, 0x8a,
	  0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
	 1 },
};

/*


@@ 95,6 97,8 @@ dmainit(void)
			xp = &dp->x[chan];
			xp->pg.pa = (ulong)xspanalloc(BY2PG, BY2PG, 0);
			xp->pg.va = KZERO|xp->pg.pa;
			xp->len = 0;
			xp->isread = 0;
		}
	}
}


@@ 143,17 147,17 @@ dmasetup(int chan, void *va, long len, int isread)
	/*
	 * this setup must be atomic
	 */
	lock(dp);
	outb(dp->cbp, 0);		/* set count & address to their first byte */
	ilock(dp);
	mode = (isread ? 0x44 : 0x48) | chan;
	outb(dp->mode, mode);		/* single mode dma (give CPU a chance at mem) */
	outb(dp->addr[chan], pa);		/* set address */
	outb(dp->addr[chan], pa>>8);
	outb(dp->page[chan], pa>>16);
	outb(dp->count[chan], len-1);		/* set count */
	outb(dp->count[chan], (len-1)>>8);
	outb(dp->cbp, 0);		/* set count & address to their first byte */
	outb(dp->addr[chan], pa>>dp->shift);		/* set address */
	outb(dp->addr[chan], pa>>(8+dp->shift));
	outb(dp->count[chan], (len>>dp->shift)-1);		/* set count */
	outb(dp->count[chan], ((len>>dp->shift)-1)>>8);
	outb(dp->sbm, chan);		/* enable the channel */
	unlock(dp);
	iunlock(dp);

	return len;
}


@@ 177,9 181,9 @@ dmaend(int chan)
	/*
	 *  disable the channel
	 */
	lock(dp);
	ilock(dp);
	outb(dp->sbm, 4|chan);
	unlock(dp);
	iunlock(dp);

	xp = &dp->x[chan];
	if(xp->len == 0 || !xp->isread)

M port/devaudio.c => port/devaudio.c +135 -58
@@ 11,8 11,6 @@
#include	"io.h"
#include	"audio.h"


#define	nelem(x)	(sizeof (x)/sizeof(x[0]))
#define	NPORT		(sizeof audiodir/sizeof(Dirtab))

typedef struct	Buf	Buf;


@@ 26,7 24,8 @@ enum

	Fmono		= 1,

	Aread		= 0,
	Aclosed		= 0,
	Aread,
	Awrite,

	Speed		= 44100,


@@ 67,12 66,13 @@ struct	Queue
};
static	struct
{
	QLock;
	Rendez	vous;
	int	bufinit;	/* boolean if buffers allocated */
	int	curcount;	/* how much data in current buffer */
	int	active;		/* boolean dma running */
	int	intr;		/* boolean an interrupt has happened */
	int	amode;		/* Aread/Awrite for /audio */
	int	amode;		/* Aclosed/Aread/Awrite for /audio */
	Level	left;		/* all of left volumes */
	Level	right;		/* all of right volumes */
	int	mic;		/* mono level */


@@ 121,9 121,24 @@ static	struct
	0
};

static struct
{
	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;
} blaster;

static	void	swab(uchar*);

static	char	Emajor[]	= "SB16 version too old";
static	char	Emajor[]	= "SoundBlaster version too old";
static	char	Emode[]		= "illegal open mode";
static	char	Evolume[]	= "illegal volume specifier";



@@ 133,9 148,9 @@ sbcmd(int val)
	int i, s;

	for(i=1<<16; i!=0; i--) {
		s = inb(PORT_WSTATUS);
		s = inb(blaster.wstatus);
		if((s & 0x80) == 0) {
			outb(PORT_WRITE, val);
			outb(blaster.write, val);
			return 0;
		}
	}


@@ 149,9 164,9 @@ sbread(void)
	int i, s;

	for(i=1<<16; i!=0; i--) {
		s = inb(PORT_RSTATUS);
		s = inb(blaster.rstatus);
		if((s & 0x80) != 0) {
			return inb(PORT_READ);
			return inb(blaster.read);
		}
	}
/*	print("SB16 sbread did not respond\n");	/**/


@@ 162,8 177,8 @@ static	int
mxcmd(int addr, int val)
{

	outb(PORT_MIXER_ADDR, addr);
	outb(PORT_MIXER_DATA, val);
	outb(blaster.mixaddr, addr);
	outb(blaster.mixdata, val);
	return 1;
}



@@ 172,8 187,8 @@ mxread(int addr)
{
	int s;

	outb(PORT_MIXER_ADDR, addr);
	s = inb(PORT_MIXER_DATA);
	outb(blaster.mixaddr, addr);
	s = inb(blaster.mixdata);
	return s;
}



@@ 214,9 229,7 @@ mxcmdu(int s, int v)
static	void
mxvolume(void)
{
	ulong sp;

	sp = splhi();
	ilock(&blaster);
	mxcmds(0x30, audio.left.master);
	mxcmds(0x31, audio.right.master);



@@ 249,7 262,7 @@ mxvolume(void)
	mxcmd(0x3c, audio.oswitch);
	mxcmd(0x3d, audio.left.iswitch);
	mxcmd(0x3e, audio.right.iswitch);
	splx(sp);
	iunlock(&blaster);
}

static	Buf*


@@ 287,7 300,6 @@ static	void
contindma(void)
{
	Buf *b;
	ulong count, addr;

	if(!audio.active)
		goto shutdown;


@@ 306,24 318,11 @@ contindma(void)
	if(b == 0)
		goto shutdown;

	addr = b->phys;
	count = ((Bufsize) >> 1) - 1;

/*	outb(DMA2_WRMASK, 4|(Dma&3));	/* disable dma */
	outb(DMA6_LPAGE, (addr>>16) & 0xfe);
	outb(DMA2_CLRBP, 0);		/* clear ff */
	outb(DMA6_ADDRESS, addr>>1);
	outb(DMA6_ADDRESS, addr>>9);

	outb(DMA2_CLRBP, 0);		/* clear ff */
	outb(DMA6_COUNT, count);
	outb(DMA6_COUNT, count>>8);
	outb(DMA2_WRMASK, (Dma&3));		/* enable dma */

	dmasetup(Dma, b->virt, Bufsize, audio.amode == Aread);
	return;

shutdown:
	outb(DMA2_WRMASK, 4|(Dma&3));	/* disable dma */
	dmaend(Dma);
	sbcmd(0xd9);				/* exit at end of count */
	sbcmd(0xd5);				/* pause */
	audio.curcount = 0;


@@ 339,14 338,12 @@ startdma(void)
{
	ulong count;

	outb(DMA2_WRMASK, 4|(Dma&3));	/* disable dma */
	if(audio.amode == Aread) {
		outb(DMA2_WRMODE, 0x44 | (Dma&3));	/* set mode */
	ilock(&blaster);
	dmaend(Dma);
	if(audio.amode == Aread)
		sbcmd(0x42);			/* input sampling rate */
	} else {
		outb(DMA2_WRMODE, 0x48 | (Dma&3));	/* set mode */
	else
		sbcmd(0x41);			/* output sampling rate */
	}
	sbcmd(audio.speed>>8);
	sbcmd(audio.speed);



@@ 361,6 358,7 @@ startdma(void)

	audio.active = 1;
	contindma();
	iunlock(&blaster);
}

/*


@@ 370,12 368,8 @@ startdma(void)
static	void
pokeaudio(void)
{
	ulong sp;

	sp = splhi();
	if(!audio.active)
		startdma();
	splx(sp);
}

void


@@ 387,22 381,32 @@ audiosbintr(void)
	if(stat) {
		dummy = 0;
		if(stat & 2) {
			dummy = inb(PORT_CLRI16);
			ilock(&blaster);
			dummy = inb(blaster.clri16);
			contindma();
			iunlock(&blaster);
			audio.intr = 1;
			wakeup(&audio.vous);
		}
		if(stat & 1) {
			dummy = inb(PORT_CLRI8);
			dummy = inb(blaster.clri8);
		}
		if(stat & 4) {
			dummy = inb(PORT_CLRI401);
			dummy = inb(blaster.clri401);
		}
		USED(dummy);
	}
}

void
pcaudiosbintr(Ureg *ureg, void *rock)
{
	USED(ureg, rock);
/*	print("sb16 audio interrupt\n");	/**/
	audiosbintr();
}

void
audiodmaintr(void)
{
/*	print("sb16 dma interrupt\n");	/**/


@@ 427,7 431,7 @@ waitaudio(void)
	pokeaudio();
	tsleep(&audio.vous, anybuf, 0, 10*1000);
	if(audio.intr == 0) {
/*		print("audio timeout\n");	/**/
		print("audio timeout\n");	/**/
		audio.active = 0;
		pokeaudio();
	}


@@ 452,6 456,7 @@ setempty(void)
{
	int i;

	ilock(&blaster);
	audio.empty.first = 0;
	audio.empty.last = 0;
	audio.full.first = 0;


@@ 460,6 465,7 @@ setempty(void)
	audio.filling = 0;
	for(i=0; i<Nbuf; i++)
		putbuf(&audio.empty, &audio.buf[i]);
	iunlock(&blaster);
}

void


@@ 481,15 487,56 @@ resetlevel(void)
void
audioinit(void)
{
	ISAConf sbconf;
	int i;

	sbconf.port = 0x220;
	sbconf.irq = 7;
	if(isaconfig("audio", 0, &sbconf) == 0)
		return;
	if(strcmp(sbconf.type, "sb16") != 0)
		return;
	switch(sbconf.port){
	case 0x220:
	case 0x240:
	case 0x260:
	case 0x280:
		break;
	default:
		print("bad sb16 port 0x%x\n", sbconf.port);
		return;
	}
	switch(sbconf.irq){
	case 2:
	case 5:
	case 7:
	case 10:
		break;
	default:
		print("bad sb16 irq %d\n", sbconf.irq);
		return;
	}

	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;

	seteisadma(Dma, audiodmaintr);
	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);

	audio.amode = Aclosed;
	resetlevel();

	outb(PORT_RESET, 1);
	outb(blaster.reset, 1);
	delay(1);			/* >3 υs */
	outb(PORT_RESET, 0);
	outb(blaster.reset, 0);
	delay(1);

	i = sbread();


@@ 516,10 563,10 @@ audioinit(void)
	 * set up irq/dma chans
	 */
	mxcmd(0x80,			/* irq */
		(Irq==2)? 1:
		(Irq==5)? 2:
		(Irq==7)? 4:
		(Irq==10)? 8:
		(sbconf.irq==2)? 1:
		(sbconf.irq==5)? 2:
		(sbconf.irq==7)? 4:
		(sbconf.irq==10)? 8:
		0);
	mxcmd(0x81, 1<<Dma);	/* dma */
}


@@ 551,6 598,7 @@ audiostat(Chan *c, char *db)
Chan*
audioopen(Chan *c, int omode)
{
	int amode;

	if(audio.major != 4)
		error(Emajor);


@@ 565,17 613,22 @@ audioopen(Chan *c, int omode)
		break;

	case Qaudio:
		amode = Awrite;
		if((omode&7) == OREAD)
			amode = Aread;
		qlock(&audio);
		if(audio.amode != Aclosed){
			qunlock(&audio);
			error(Einuse);
		}
		if(audio.bufinit == 0) {
			audio.bufinit = 1;
			sbbufinit();
		}

		audio.amode = Awrite;
		if((omode&7) == OREAD)
			audio.amode = Aread;

		audio.amode = amode;
		setempty();
		audio.curcount = 0;
		qunlock(&audio);
		break;
	}
	c = devopen(c, omode, audiodir, NPORT, devgen);


@@ 612,9 665,17 @@ audioclose(Chan *c)

	case Qaudio:
		if(c->flag & COPEN) {
			qlock(&audio);
			audio.amode = Aclosed;
			if(waserror()){
				qunlock(&audio);
				nexterror();
			}
			while(audio.active)
				waitaudio();
			setempty();
			poperror();
			qunlock(&audio);
		}
		break;
	}


@@ 640,6 701,11 @@ audioread(Chan *c, char *a, long n, ulong offset)
	case Qaudio:
		if(audio.amode != Aread)
			error(Emode);
		qlock(&audio);
		if(waserror()){
			qunlock(&audio);
			nexterror();
		}
		while(n > 0) {
			b = audio.filling;
			if(b == 0) {


@@ 665,6 731,8 @@ audioread(Chan *c, char *a, long n, ulong offset)
				putbuf(&audio.empty, b);
			}
		}
		poperror();
		qunlock(&audio);
		break;

	case Qvolume:


@@ 770,6 838,11 @@ audiowrite(Chan *c, char *a, long n, ulong offset)
	case Qaudio:
		if(audio.amode != Awrite)
			error(Emode);
		qlock(&audio);
		if(waserror()){
			qunlock(&audio);
			nexterror();
		}
		while(n > 0) {
			b = audio.filling;
			if(b == 0) {


@@ 796,6 869,8 @@ audiowrite(Chan *c, char *a, long n, ulong offset)
				putbuf(&audio.full, b);
			}
		}
		poperror();
		qunlock(&audio);
		break;
	}
	return n0 - n;


@@ 829,6 904,8 @@ swab(uchar *a)
{
	ulong *p, *ep, b;

	if(!SBswab)
		return;
	p = (ulong*)a;
	ep = p + (Bufsize>>2);
	while(p < ep) {