plan 9 kernel history: overview | file list | diff list

1998/0825/port/devaudio.c (diff list | history)

port/devaudio.c on 1995/0119
1995/0119    
/* 
 *	SB 16 driver 
 */ 
#include	"u.h" 
#include	"../port/lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"../port/error.h" 
#include	"io.h" 
#include	"audio.h" 
 
1995/0217    
typedef struct	AQueue	AQueue; 
1995/0119    
typedef struct	Buf	Buf; 
 
enum 
{ 
	Qdir		= 0, 
	Qaudio, 
	Qvolume, 
 
	Fmono		= 1, 
1995/0221    
	Fin		= 2, 
	Fout		= 4, 
1995/0119    
 
1995/0214    
	Aclosed		= 0, 
	Aread, 
1995/0119    
	Awrite, 
 
1995/0221    
	Vaudio		= 0, 
	Vsynth, 
	Vcd, 
	Vline, 
	Vmic, 
	Vspeaker, 
	Vtreb, 
	Vbass, 
	Vspeed, 
	Nvol, 
 
1995/0119    
	Speed		= 44100, 
	Ncmd		= 50,		/* max volume command words */ 
}; 
 
Dirtab 
audiodir[] = 
{ 
	"audio",	{Qaudio},		0,	0666, 
	"volume",	{Qvolume},		0,	0666, 
}; 
 
struct	Buf 
{ 
	uchar*	virt; 
	ulong	phys; 
	Buf*	next; 
}; 
1995/0217    
struct	AQueue 
1995/0119    
{ 
	Lock; 
	Buf*	first; 
	Buf*	last; 
}; 
static	struct 
{ 
1995/0214    
	QLock; 
1995/0119    
	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 */ 
1995/0214    
	int	amode;		/* Aclosed/Aread/Awrite for /audio */ 
1995/0221    
	int	rivol[Nvol];		/* right/left input/output volumes */ 
	int	livol[Nvol]; 
	int	rovol[Nvol]; 
	int	lovol[Nvol]; 
1995/0119    
	int	major;		/* SB16 major version number (sb 4) */ 
	int	minor;		/* SB16 minor version number */ 
 
	Buf	buf[Nbuf];	/* buffers and queues */ 
1995/0217    
	AQueue	empty; 
	AQueue	full; 
1995/0119    
	Buf*	current; 
	Buf*	filling; 
} audio; 
 
static	struct 
{ 
	char*	name; 
	int	flag; 
1995/0221    
	int	ilval;		/* initial values */ 
1995/0119    
	int	irval; 
} volumes[] = 
{ 
1997/0327    
[Vaudio]	"audio",	Fout, 		50,	50, 
[Vsynth]	"synth",	Fin|Fout,	0,	0, 
[Vcd]		"cd",		Fin|Fout,	0,	0, 
[Vline]		"line",		Fin|Fout,	0,	0, 
1995/0221    
[Vmic]		"mic",		Fin|Fout|Fmono,	0,	0, 
[Vspeaker]	"speaker",	Fout|Fmono,	0,	0, 
1995/0119    
 
1995/0221    
[Vtreb]		"treb",		Fout, 		50,	50, 
[Vbass]		"bass",		Fout, 		50,	50, 
1995/0119    
 
1997/0327    
[Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed, 
		0 
1995/0119    
}; 
 
1995/0214    
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; 
1996/1024    
	int	dma; 
1998/0317    
 
	void	(*startdma)(void); 
	void	(*intr)(void); 
1995/0214    
} blaster; 
 
1995/0119    
static	void	swab(uchar*); 
 
1996/1024    
static	char	Emajor[]	= "soundblaster not responding/wrong version"; 
1995/0119    
static	char	Emode[]		= "illegal open mode"; 
static	char	Evolume[]	= "illegal volume specifier"; 
 
static	int 
sbcmd(int val) 
{ 
	int i, s; 
 
	for(i=1<<16; i!=0; i--) { 
1995/0214    
		s = inb(blaster.wstatus); 
1995/0119    
		if((s & 0x80) == 0) { 
1995/0214    
			outb(blaster.write, val); 
1995/0119    
			return 0; 
		} 
	} 
1998/0317    
/*	print("#A: sbcmd (0x%.2x) timeout\n", val);	/**/ 
1995/0119    
	return 1; 
} 
 
static	int 
sbread(void) 
{ 
	int i, s; 
 
	for(i=1<<16; i!=0; i--) { 
1995/0214    
		s = inb(blaster.rstatus); 
1995/0119    
		if((s & 0x80) != 0) { 
1995/0214    
			return inb(blaster.read); 
1995/0119    
		} 
	} 
1997/0327    
/*	print("#A: sbread did not respond\n");	/**/ 
1998/0317    
	return -1; 
1995/0119    
} 
 
1998/0317    
static int 
ess1688w(int reg, int val) 
{ 
	if(sbcmd(reg) || sbcmd(val)) 
		return 1; 
 
	return 0; 
} 
 
static int 
ess1688r(int reg) 
{ 
	if(sbcmd(0xC0) || sbcmd(reg)) 
		return -1; 
 
	return sbread(); 
} 
 
1995/0119    
static	int 
mxcmd(int addr, int val) 
{ 
 
1995/0214    
	outb(blaster.mixaddr, addr); 
	outb(blaster.mixdata, val); 
1995/0119    
	return 1; 
} 
 
static	int 
mxread(int addr) 
{ 
	int s; 
 
1995/0214    
	outb(blaster.mixaddr, addr); 
	s = inb(blaster.mixdata); 
1995/0119    
	return s; 
} 
 
static	void 
mxcmds(int s, int v) 
{ 
 
	if(v > 100) 
		v = 100; 
	if(v < 0) 
		v = 0; 
	mxcmd(s, (v*255)/100); 
} 
 
static	void 
mxcmdt(int s, int v) 
{ 
 
	if(v > 100) 
		v = 100; 
	if(v <= 0) 
		mxcmd(s, 0); 
	else 
		mxcmd(s, 255-100+v); 
} 
 
static	void 
mxcmdu(int s, int v) 
{ 
 
	if(v > 100) 
		v = 100; 
	if(v <= 0) 
		v = 0; 
	mxcmd(s, 128-50+v); 
} 
 
static	void 
mxvolume(void) 
{ 
1995/0221    
	int *left, *right; 
	int source; 
 
	if(audio.amode == Aread){ 
		left = audio.livol; 
		right = audio.rivol; 
	}else{ 
		left = audio.lovol; 
		right = audio.rovol; 
	} 
 
1995/0214    
	ilock(&blaster); 
1995/0119    
 
1995/0221    
	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 */ 
1995/0119    
 
1995/0221    
	mxcmds(0x32, left[Vaudio]); 
	mxcmds(0x33, right[Vaudio]); 
1995/0119    
 
1995/0221    
	mxcmds(0x34, left[Vsynth]); 
	mxcmds(0x35, right[Vsynth]); 
1995/0119    
 
1995/0221    
	mxcmds(0x36, left[Vcd]); 
	mxcmds(0x37, right[Vcd]); 
1995/0119    
 
1995/0221    
	mxcmds(0x38, left[Vline]); 
	mxcmds(0x39, right[Vline]); 
1995/0119    
 
1995/0221    
	mxcmds(0x3a, left[Vmic]); 
	mxcmds(0x3b, left[Vspeaker]); 
1995/0119    
 
1995/0221    
	mxcmdu(0x44, left[Vtreb]); 
	mxcmdu(0x45, right[Vtreb]); 
1995/0119    
 
1995/0221    
	mxcmdu(0x46, left[Vbass]); 
	mxcmdu(0x47, right[Vbass]); 
1995/0119    
 
1995/0221    
	source = 0; 
	if(left[Vsynth]) 
		source |= 1<<6; 
	if(right[Vsynth]) 
		source |= 1<<5; 
	if(left[Vaudio]) 
		source |= 1<<4; 
	if(right[Vaudio]) 
		source |= 1<<3; 
	if(left[Vcd]) 
		source |= 1<<2; 
	if(right[Vcd]) 
		source |= 1<<1; 
	if(left[Vmic]) 
		source |= 1<<0; 
	if(audio.amode == Aread) 
		mxcmd(0x3c, 0);		/* output switch */ 
	else 
		mxcmd(0x3c, source); 
	mxcmd(0x3d, source);		/* input left switch */ 
	mxcmd(0x3e, source);		/* input right switch */ 
1995/0214    
	iunlock(&blaster); 
1995/0119    
} 
 
static	Buf* 
1995/0217    
getbuf(AQueue *q) 
1995/0119    
{ 
	Buf *b; 
 
	ilock(q); 
	b = q->first; 
	if(b) 
		q->first = b->next; 
	iunlock(q); 
 
	return b; 
} 
 
static	void 
1995/0217    
putbuf(AQueue *q, Buf *b) 
1995/0119    
{ 
 
	ilock(q); 
	b->next = 0; 
	if(q->first) 
		q->last->next = b; 
	else 
		q->first = b; 
	q->last = b; 
	iunlock(q); 
} 
 
/* 
 * move the dma to the next buffer 
 */ 
static	void 
contindma(void) 
{ 
	Buf *b; 
 
	if(!audio.active) 
		goto shutdown; 
 
	b = audio.current; 
	if(audio.amode == Aread) { 
1998/0317    
		if(b)	/* shouldn't happen */ 
1995/0119    
			putbuf(&audio.full, b); 
		b = getbuf(&audio.empty); 
	} else { 
1998/0317    
		if(b)	/* shouldn't happen */ 
1995/0119    
			putbuf(&audio.empty, b); 
		b = getbuf(&audio.full); 
	} 
	audio.current = b; 
	if(b == 0) 
		goto shutdown; 
 
1998/0317    
	if(dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread) >= 0) 
		return; 
	print("#A: dmasetup fail\n"); 
	putbuf(&audio.empty, b); 
1995/0119    
 
shutdown: 
1996/1024    
	dmaend(blaster.dma); 
1995/0119    
	sbcmd(0xd9);				/* exit at end of count */ 
	sbcmd(0xd5);				/* pause */ 
	audio.curcount = 0; 
	audio.active = 0; 
} 
 
/* 
 * cause sb to get an interrupt per buffer. 
 * start first dma 
 */ 
static	void 
1998/0317    
sb16startdma(void) 
1995/0119    
{ 
	ulong count; 
1995/0221    
	int speed; 
1995/0119    
 
1995/0214    
	ilock(&blaster); 
1996/1024    
	dmaend(blaster.dma); 
1995/0221    
	if(audio.amode == Aread) { 
1995/0119    
		sbcmd(0x42);			/* input sampling rate */ 
1995/0221    
		speed = audio.livol[Vspeed]; 
	} else { 
1995/0119    
		sbcmd(0x41);			/* output sampling rate */ 
1995/0221    
		speed = audio.lovol[Vspeed]; 
	} 
	sbcmd(speed>>8); 
	sbcmd(speed); 
1995/0119    
 
	count = (Bufsize >> 1) - 1; 
	if(audio.amode == Aread) 
		sbcmd(0xbe);			/* A/D, autoinit */ 
	else 
		sbcmd(0xb6);			/* D/A, autoinit */ 
	sbcmd(0x30);				/* stereo, 16 bit */ 
	sbcmd(count); 
	sbcmd(count>>8); 
 
	audio.active = 1; 
	contindma(); 
1995/0214    
	iunlock(&blaster); 
1995/0119    
} 
 
1998/0317    
static int 
ess1688reset(void) 
{ 
	int i; 
 
	outb(blaster.reset, 3); 
	delay(1);			/* >3 υs */ 
	outb(blaster.reset, 0); 
	delay(1); 
 
	i = sbread(); 
	if(i != 0xAA) { 
		print("#A: no response 0x%.2x\n", i); 
		return 1; 
	} 
 
	if(sbcmd(0xC6)){		/* extended mode */ 
		print("#A: barf 3\n"); 
		return 1; 
	} 
 
	return 0; 
} 
 
static	void 
ess1688startdma(void) 
{ 
	ulong count; 
	int speed, x; 
 
	ilock(&blaster); 
	dmaend(blaster.dma); 
 
	if(audio.amode == Awrite) 
		ess1688reset(); 
	if(audio.amode == Aread) 
		sbcmd(0xD3);			/* speaker off */ 
 
	/* 
	 * Set the speed. 
	 */ 
	if(audio.amode == Aread) 
		speed = audio.livol[Vspeed]; 
	else 
		speed = audio.lovol[Vspeed]; 
	if(speed < 4000) 
		speed = 4000; 
	else if(speed > 48000) 
		speed = 48000; 
 
	if(speed > 22000) 
		  x = 0x80|(256-(795500+speed/2)/speed); 
	else 
		  x = 128-(397700+speed/2)/speed; 
	ess1688w(0xA1, x & 0xFF); 
 
	speed = (speed * 9) / 20; 
	x = 256 - 7160000 / (speed * 82); 
	ess1688w(0xA2, x & 0xFF); 
 
	if(audio.amode == Aread) 
		ess1688w(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 */ 
 
	if(audio.amode == Awrite) 
		ess1688w(0xB6, 0); 
	ess1688w(0xB7, 0x71); 
	ess1688w(0xB7, 0xBC); 
 
	x = ess1688r(0xB1) & 0x0F; 
	ess1688w(0xB1, x|0x50); 
	x = ess1688r(0xB2) & 0x0F; 
	ess1688w(0xB2, x|0x50); 
	if(audio.amode == Awrite) 
		sbcmd(0xD1);			/* speaker on */ 
 
	count = -Bufsize; 
	ess1688w(0xA4, count & 0xFF); 
	ess1688w(0xA5, (count>>8) & 0xFF); 
	x = ess1688r(0xB8); 
	ess1688w(0xB8, x|0x05); 
 
	audio.active = 1; 
	contindma(); 
	iunlock(&blaster); 
} 
 
1995/0119    
/* 
 * if audio is stopped, 
 * start it up again. 
 */ 
static	void 
pokeaudio(void) 
{ 
	if(!audio.active) 
1998/0317    
		blaster.startdma(); 
1995/0119    
} 
 
1998/0317    
static void 
sb16intr(void) 
1995/0119    
{ 
	int stat, dummy; 
 
	stat = mxread(0x82) & 7;		/* get irq status */ 
	if(stat) { 
		dummy = 0; 
		if(stat & 2) { 
1995/0214    
			ilock(&blaster); 
			dummy = inb(blaster.clri16); 
1995/0119    
			contindma(); 
1995/0214    
			iunlock(&blaster); 
1995/0119    
			audio.intr = 1; 
			wakeup(&audio.vous); 
		} 
		if(stat & 1) { 
1995/0214    
			dummy = inb(blaster.clri8); 
1995/0119    
		} 
		if(stat & 4) { 
1995/0214    
			dummy = inb(blaster.clri401); 
1995/0119    
		} 
		USED(dummy); 
	} 
} 
 
1998/0317    
static void 
ess1688intr(void) 
{ 
	int dummy; 
 
	if(audio.active){ 
		ilock(&blaster); 
		contindma(); 
		dummy = inb(blaster.clri8); 
		iunlock(&blaster); 
		audio.intr = 1; 
		wakeup(&audio.vous); 
		USED(dummy); 
	} 
	else 
		print("#A: unexpected ess1688 interrupt\n"); 
} 
 
1995/0119    
void 
1998/0317    
audiosbintr(void) 
{ 
	/* 
	 * Carrera interrupt interface. 
	 */ 
	blaster.intr(); 
} 
 
static void 
1995/0804    
pcaudiosbintr(Ureg*, void*) 
1995/0214    
{ 
1998/0317    
	/* 
	 * x86 interrupt interface. 
	 */ 
	blaster.intr(); 
1995/0214    
} 
 
void 
1995/0119    
audiodmaintr(void) 
{ 
1997/0327    
/*	print("#A: dma interrupt\n");	/**/ 
1995/0119    
} 
 
static int 
1995/0804    
anybuf(void*) 
1995/0119    
{ 
	return audio.intr; 
} 
 
/* 
 * wait for some output to get 
 * empty buffers back. 
 */ 
static void 
waitaudio(void) 
{ 
 
	audio.intr = 0; 
	pokeaudio(); 
	tsleep(&audio.vous, anybuf, 0, 10*1000); 
	if(audio.intr == 0) { 
1997/0327    
/*		print("#A: audio timeout\n");	/**/ 
1995/0119    
		audio.active = 0; 
		pokeaudio(); 
	} 
} 
 
static void 
sbbufinit(void) 
{ 
	int i; 
	void *p; 
 
	for(i=0; i<Nbuf; i++) { 
		p = xspanalloc(Bufsize, CACHELINESZ, 64*1024); 
		dcflush(p, Bufsize); 
		audio.buf[i].virt = UNCACHED(uchar, p); 
		audio.buf[i].phys = (ulong)PADDR(p); 
	} 
} 
 
static	void 
setempty(void) 
{ 
	int i; 
 
1995/0214    
	ilock(&blaster); 
1995/0119    
	audio.empty.first = 0; 
	audio.empty.last = 0; 
	audio.full.first = 0; 
	audio.full.last = 0; 
	audio.current = 0; 
	audio.filling = 0; 
	for(i=0; i<Nbuf; i++) 
		putbuf(&audio.empty, &audio.buf[i]); 
1995/0214    
	iunlock(&blaster); 
1995/0119    
} 
 
static	void 
resetlevel(void) 
{ 
	int i; 
 
	for(i=0; volumes[i].name; i++) { 
1995/0221    
		audio.lovol[i] = volumes[i].ilval; 
		audio.rovol[i] = volumes[i].irval; 
		audio.livol[i] = volumes[i].ilval; 
		audio.rivol[i] = volumes[i].irval; 
1995/0119    
	} 
} 
 
1998/0317    
static int 
ess1688(ISAConf* sbconf) 
{ 
	int i, major, minor; 
 
	/* 
	 * Try for ESS1688. 
	 */ 
	sbcmd(0xE7);			/* get version */ 
	major = sbread(); 
	minor = sbread(); 
	if(major != 0x68 || minor != 0x8B){ 
		print("#A: model 0x%.2x 0x%.2x; not ESS1688 compatible\n", major, minor); 
		return 1; 
	} 
 
	ess1688reset(); 
 
	switch(sbconf->irq){ 
	case 2: 
	case 9: 
		i = 0x50|(0<<2); 
		break; 
	case 5: 
		i = 0x50|(1<<2); 
		break; 
	case 7: 
		i = 0x50|(2<<2); 
		break; 
	case 10: 
		i = 0x50|(3<<2); 
		break; 
	default: 
1998/0825    
		print("#A: bad ESS1688 irq %lud\n", sbconf->irq); 
1998/0317    
		return 1; 
	} 
	ess1688w(0xB1, i); 
 
	switch(sbconf->dma){ 
	case 0: 
		i = 0x50|(1<<2); 
		break; 
	case 1: 
		i = 0xF0|(2<<2); 
		break; 
	case 3: 
		i = 0x50|(3<<2); 
		break; 
	default: 
1998/0825    
		print("#A: bad ESS1688 dma %lud\n", sbconf->dma); 
1998/0317    
		return 1; 
	} 
	ess1688w(0xB2, i); 
 
	ess1688reset(); 
 
	blaster.startdma = ess1688startdma; 
	blaster.intr = ess1688intr; 
 
	return 0; 
} 
 
1997/0327    
static void 
1995/0119    
audioinit(void) 
{ 
1995/0214    
	ISAConf sbconf; 
1995/0119    
	int i; 
 
1995/0214    
	sbconf.port = 0x220; 
1996/1025    
	sbconf.dma = Dma; 
1995/0214    
	sbconf.irq = 7; 
	if(isaconfig("audio", 0, &sbconf) == 0) 
		return; 
1998/0317    
	if(cistrcmp(sbconf.type, "sb16") != 0 && cistrcmp(sbconf.type, "ess1688") != 0) 
1995/0214    
		return; 
	switch(sbconf.port){ 
	case 0x220: 
	case 0x240: 
	case 0x260: 
	case 0x280: 
		break; 
	default: 
1998/0825    
		print("#A: bad port 0x%lux\n", sbconf.port); 
1995/0214    
		return; 
	} 
	switch(sbconf.irq){ 
	case 2: 
	case 5: 
	case 7: 
	case 10: 
		break; 
	default: 
1998/0825    
		print("#A: bad irq %lud\n", sbconf.irq); 
1995/0214    
		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; 
1997/0327    
	blaster.dma = sbconf.dma; 
1995/0214    
 
1998/0317    
	blaster.startdma = sb16startdma; 
	blaster.intr = sb16intr; 
 
1996/1024    
	seteisadma(blaster.dma, audiodmaintr); 
1995/0214    
	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0); 
1995/0119    
 
1995/0214    
	audio.amode = Aclosed; 
1995/0119    
	resetlevel(); 
 
1995/0214    
	outb(blaster.reset, 1); 
1995/0119    
	delay(1);			/* >3 υs */ 
1995/0214    
	outb(blaster.reset, 0); 
1995/0119    
	delay(1); 
 
	i = sbread(); 
	if(i != 0xaa) { 
1997/0327    
		print("#A: no response #%.2x\n", i); 
1995/0119    
		return; 
	} 
 
	sbcmd(0xe1);			/* get version */ 
	audio.major = sbread(); 
	audio.minor = sbread(); 
 
	if(audio.major != 4) { 
1998/0317    
		if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){ 
			print("#A: model 0x%.2x 0x%.2x; not SB 16 compatible\n", 
				audio.major, audio.minor); 
			return; 
		} 
		audio.major = 4; 
1995/0119    
	} 
1998/0512    
 
1995/0119    
	/* 
	 * initialize the mixer 
	 */ 
	mxcmd(0x00, 0);			/* Reset mixer */ 
	mxvolume(); 
 
	/* 
	 * set up irq/dma chans 
	 */ 
	mxcmd(0x80,			/* irq */ 
1995/0214    
		(sbconf.irq==2)? 1: 
		(sbconf.irq==5)? 2: 
		(sbconf.irq==7)? 4: 
		(sbconf.irq==10)? 8: 
1995/0119    
		0); 
1996/1024    
	mxcmd(0x81, 1<<blaster.dma);	/* dma */ 
1995/0119    
} 
 
1997/0327    
static Chan* 
1995/0119    
audioattach(char *param) 
{ 
	return devattach('A', param); 
} 
 
1997/0327    
static int 
1995/0119    
audiowalk(Chan *c, char *name) 
{ 
1997/0327    
	return devwalk(c, name, audiodir, nelem(audiodir), devgen); 
1995/0119    
} 
 
1997/0327    
static void 
1995/0119    
audiostat(Chan *c, char *db) 
{ 
1997/0327    
	devstat(c, db, audiodir, nelem(audiodir), devgen); 
1995/0119    
} 
 
1997/0327    
static Chan* 
1995/0119    
audioopen(Chan *c, int omode) 
{ 
1995/0214    
	int amode; 
1995/0119    
 
	if(audio.major != 4) 
		error(Emajor); 
 
	switch(c->qid.path & ~CHDIR) { 
	default: 
		error(Eperm); 
		break; 
 
	case Qvolume: 
	case Qdir: 
		break; 
 
	case Qaudio: 
1995/0214    
		amode = Awrite; 
		if((omode&7) == OREAD) 
			amode = Aread; 
		qlock(&audio); 
		if(audio.amode != Aclosed){ 
			qunlock(&audio); 
			error(Einuse); 
		} 
1995/0119    
		if(audio.bufinit == 0) { 
			audio.bufinit = 1; 
			sbbufinit(); 
		} 
1995/0214    
		audio.amode = amode; 
1995/0119    
		setempty(); 
		audio.curcount = 0; 
1995/0214    
		qunlock(&audio); 
1995/0221    
		mxvolume(); 
1995/0119    
		break; 
	} 
1997/0327    
	c = devopen(c, omode, audiodir, nelem(audiodir), devgen); 
1995/0119    
	c->mode = openmode(omode); 
	c->flag |= COPEN; 
	c->offset = 0; 
 
	return c; 
} 
 
1997/0327    
static void 
1995/0119    
audioclose(Chan *c) 
{ 
1998/0624    
	Buf *b; 
1995/0119    
 
	switch(c->qid.path & ~CHDIR) { 
	default: 
		error(Eperm); 
		break; 
 
	case Qdir: 
	case Qvolume: 
		break; 
 
	case Qaudio: 
		if(c->flag & COPEN) { 
1995/0214    
			qlock(&audio); 
1998/0624    
			if(audio.amode == Awrite) { 
1998/0603    
				/* flush out last partial buffer */ 
1998/0624    
				b = audio.filling; 
				if(b) { 
1998/0603    
					audio.filling = 0; 
					memset(b->virt, 0, Bufsize-audio.curcount); 
					swab(b->virt); 
					putbuf(&audio.full, b); 
				} 
1998/0624    
				if(!audio.active && audio.full.first) 
1998/0603    
					pokeaudio(); 
			} 
1995/0214    
			audio.amode = Aclosed; 
			if(waserror()){ 
				qunlock(&audio); 
				nexterror(); 
			} 
1995/0119    
			while(audio.active) 
				waitaudio(); 
			setempty(); 
1995/0214    
			poperror(); 
			qunlock(&audio); 
1995/0119    
		} 
		break; 
	} 
} 
 
1997/0327    
static long 
1998/0319    
audioread(Chan *c, char *a, long n, vlong off) 
1995/0119    
{ 
1995/0221    
	int liv, riv, lov, rov; 
	long m, n0; 
	char buf[300]; 
1995/0119    
	Buf *b; 
	int j; 
1998/0319    
	ulong offset = off; 
1995/0119    
 
	n0 = n; 
	switch(c->qid.path & ~CHDIR) { 
	default: 
		error(Eperm); 
		break; 
 
	case Qdir: 
1997/0327    
		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen); 
1995/0119    
 
	case Qaudio: 
		if(audio.amode != Aread) 
			error(Emode); 
1995/0214    
		qlock(&audio); 
		if(waserror()){ 
			qunlock(&audio); 
			nexterror(); 
		} 
1995/0119    
		while(n > 0) { 
			b = audio.filling; 
			if(b == 0) { 
				b = getbuf(&audio.full); 
				if(b == 0) { 
					waitaudio(); 
					continue; 
				} 
				audio.filling = b; 
				swab(b->virt); 
				audio.curcount = 0; 
			} 
			m = Bufsize-audio.curcount; 
			if(m > n) 
				m = n; 
			memmove(a, b->virt+audio.curcount, m); 
 
			audio.curcount += m; 
			n -= m; 
			a += m; 
			if(audio.curcount >= Bufsize) { 
				audio.filling = 0; 
				putbuf(&audio.empty, b); 
			} 
		} 
1995/0214    
		poperror(); 
		qunlock(&audio); 
1995/0119    
		break; 
 
	case Qvolume: 
		j = 0; 
1995/0221    
		buf[0] = 0; 
		for(m=0; volumes[m].name; m++){ 
			liv = audio.livol[m]; 
			riv = audio.rivol[m]; 
			lov = audio.lovol[m]; 
			rov = audio.rovol[m]; 
			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); 
			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ 
				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) 
					j += snprint(buf+j, sizeof(buf)-j, " %d", liv); 
				else{ 
					if(volumes[m].flag & Fin) 
1998/0327    
						j += snprint(buf+j, sizeof(buf)-j, 
1998/0331    
							" in %d", liv); 
1995/0221    
					if(volumes[m].flag & Fout) 
1998/0327    
						j += snprint(buf+j, sizeof(buf)-j, 
1998/0331    
							" out %d", lov); 
1995/0221    
				} 
			}else{ 
1998/0327    
				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && 
				    liv==lov && riv==rov) 
					j += snprint(buf+j, sizeof(buf)-j, 
						" left %d right %d", 
1995/0221    
						liv, riv); 
				else{ 
					if(volumes[m].flag & Fin) 
1998/0327    
						j += snprint(buf+j, sizeof(buf)-j, 
							" in left %d right %d", 
1995/0221    
							liv, riv); 
					if(volumes[m].flag & Fout) 
1998/0327    
						j += snprint(buf+j, sizeof(buf)-j, 
							" out left %d right %d", 
1995/0221    
							lov, rov); 
				} 
1995/0119    
			} 
1995/0221    
			j += snprint(buf+j, sizeof(buf)-j, "\n"); 
1995/0119    
		} 
 
		return readstr(offset, a, n, buf); 
	} 
	return n0-n; 
} 
 
1997/0327    
static long 
1998/0319    
audiowrite(Chan *c, char *a, long n, vlong) 
1995/0119    
{ 
	long m, n0; 
1995/0221    
	int i, nf, v, left, right, in, out; 
1995/0119    
	char buf[255], *field[Ncmd]; 
	Buf *b; 
 
	n0 = n; 
	switch(c->qid.path & ~CHDIR) { 
	default: 
		error(Eperm); 
		break; 
 
	case Qvolume: 
1995/0221    
		v = Vaudio; 
1995/0119    
		left = 1; 
		right = 1; 
1995/0221    
		in = 1; 
		out = 1; 
1995/0119    
		if(n > sizeof(buf)-1) 
			n = sizeof(buf)-1; 
		memmove(buf, a, n); 
		buf[n] = '\0'; 
 
1996/0315    
		nf = parsefields(buf, field, Ncmd, " \t\n"); 
1995/0119    
		for(i = 0; i < nf; i++){ 
			/* 
			 * a number is volume 
			 */ 
			if(field[i][0] >= '0' && field[i][0] <= '9') { 
				m = strtoul(field[i], 0, 10); 
1995/0221    
				if(left && out) 
					audio.lovol[v] = m; 
				if(left && in) 
					audio.livol[v] = m; 
				if(right && out) 
					audio.rovol[v] = m; 
				if(right && in) 
					audio.rivol[v] = m; 
1995/0119    
				mxvolume(); 
				goto cont0; 
			} 
 
			for(m=0; volumes[m].name; m++) { 
				if(strcmp(field[i], volumes[m].name) == 0) { 
					v = m; 
1995/0221    
					in = 1; 
					out = 1; 
					left = 1; 
					right = 1; 
1995/0119    
					goto cont0; 
				} 
			} 
 
			if(strcmp(field[i], "reset") == 0) { 
				resetlevel(); 
				mxvolume(); 
				goto cont0; 
			} 
1995/0221    
			if(strcmp(field[i], "in") == 0) { 
				in = 1; 
				out = 0; 
				goto cont0; 
			} 
			if(strcmp(field[i], "out") == 0) { 
				in = 0; 
				out = 1; 
				goto cont0; 
			} 
1995/0119    
			if(strcmp(field[i], "left") == 0) { 
				left = 1; 
				right = 0; 
				goto cont0; 
			} 
			if(strcmp(field[i], "right") == 0) { 
				left = 0; 
				right = 1; 
				goto cont0; 
			} 
			error(Evolume); 
			break; 
		cont0:; 
		} 
		break; 
 
	case Qaudio: 
		if(audio.amode != Awrite) 
			error(Emode); 
1995/0214    
		qlock(&audio); 
		if(waserror()){ 
			qunlock(&audio); 
			nexterror(); 
		} 
1995/0119    
		while(n > 0) { 
			b = audio.filling; 
			if(b == 0) { 
				b = getbuf(&audio.empty); 
				if(b == 0) { 
					waitaudio(); 
					continue; 
				} 
				audio.filling = b; 
				audio.curcount = 0; 
			} 
 
			m = Bufsize-audio.curcount; 
			if(m > n) 
				m = n; 
			memmove(b->virt+audio.curcount, a, m); 
 
			audio.curcount += m; 
			n -= m; 
			a += m; 
			if(audio.curcount >= Bufsize) { 
				audio.filling = 0; 
				swab(b->virt); 
				putbuf(&audio.full, b); 
			} 
		} 
1995/0214    
		poperror(); 
		qunlock(&audio); 
1995/0119    
		break; 
	} 
	return n0 - n; 
} 
 
static	void 
swab(uchar *a) 
{ 
	ulong *p, *ep, b; 
 
1995/0214    
	if(!SBswab) 
		return; 
1995/0119    
	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; 
	} 
} 
1997/0327    
 
Dev audiodevtab = { 
1997/0408    
	'A', 
	"audio", 
 
1997/0327    
	devreset, 
	audioinit, 
	audioattach, 
	devclone, 
	audiowalk, 
	audiostat, 
	audioopen, 
	devcreate, 
	audioclose, 
	audioread, 
	devbread, 
	audiowrite, 
	devbwrite, 
	devremove, 
	devwstat, 
}; 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)