forsyth20121205-174063a80cc13 years agoHistory
947 lines · 15.2 KB · C
RawDownload
/* *	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" typedef struct	AQueue	AQueue;typedef struct	Buf	Buf; enum{	Qdir		= 0,	Qaudio,	Qvolume, 	Fmono		= 1,	Fin		= 2,	Fout		= 4, 	Aclosed		= 0,	Aread,	Awrite, 	Vaudio		= 0,	Vsynth,	Vcd,	Vline,	Vmic,	Vspeaker,	Vtreb,	Vbass,	Vspeed,	Nvol, 	Speed		= 44100,	Ncmd		= 50,		/* max volume command words */}; staticDirtab audiodir[] ={	".",		{Qdir, 0, QTDIR},	0,	0555,	"audio",	{Qaudio},		0,	0666,	"volume",	{Qvolume},		0,	0666,}; struct	Buf{	uchar*	virt;	ulong	phys;	Buf*	next;};struct	AQueue{	Lock;	Buf*	first;	Buf*	last;};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;		/* Aclosed/Aread/Awrite for /audio */	int	rivol[Nvol];		/* right/left input/output volumes */	int	livol[Nvol];	int	rovol[Nvol];	int	lovol[Nvol];	int	major;		/* SB16 major version number (sb 4) */	int	minor;		/* SB16 minor version number */ 	Buf	buf[Nbuf];	/* buffers and queues */	AQueue	empty;	AQueue	full;	Buf*	current;	Buf*	filling;} audio; 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, [Vtreb]		"treb",		Fout, 		50,	50,[Vbass]		"bass",		Fout, 		50,	50, [Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed,		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;	int	dma;} blaster; static	void	swab(uchar*); static	char	Emajor[]	= "soundblaster not responding/wrong version";static	char	Emode[]		= "illegal open mode";static	char	Evolume[]	= "illegal volume specifier"; static	intsbcmd(int val){	int i, s; 	for(i=1<<16; i!=0; i--) {		s = inb(blaster.wstatus);		if((s & 0x80) == 0) {			outb(blaster.write, val);			return 0;		}	}/*	print("#A: sbcmd (#%.2x) timeout\n", val);	/**/	return 1;} static	intsbread(void){	int i, s; 	for(i=1<<16; i!=0; i--) {		s = inb(blaster.rstatus);		if((s & 0x80) != 0) {			return inb(blaster.read);		}	}/*	print("#A: sbread did not respond\n");	/**/	return 0xbb;} static	intmxcmd(int addr, int val){ 	outb(blaster.mixaddr, addr);	outb(blaster.mixdata, val);	return 1;} static	intmxread(int addr){	int s; 	outb(blaster.mixaddr, addr);	s = inb(blaster.mixdata);	return s;} static	voidmxcmds(int s, int v){ 	if(v > 100)		v = 100;	if(v < 0)		v = 0;	mxcmd(s, (v*255)/100);} static	voidmxcmdt(int s, int v){ 	if(v > 100)		v = 100;	if(v <= 0)		mxcmd(s, 0);	else		mxcmd(s, 255-100+v);} static	voidmxcmdu(int s, int v){ 	if(v > 100)		v = 100;	if(v <= 0)		v = 0;	mxcmd(s, 128-50+v);} static	voidmxvolume(void){	int *left, *right;	int source; 	if(audio.amode == Aread){		left = audio.livol;		right = audio.rivol;	}else{		left = audio.lovol;		right = audio.rovol;	} 	ilock(&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 */ 	mxcmds(0x32, left[Vaudio]);	mxcmds(0x33, right[Vaudio]); 	mxcmds(0x34, left[Vsynth]);	mxcmds(0x35, right[Vsynth]); 	mxcmds(0x36, left[Vcd]);	mxcmds(0x37, right[Vcd]); 	mxcmds(0x38, left[Vline]);	mxcmds(0x39, right[Vline]); 	mxcmds(0x3a, left[Vmic]);	mxcmds(0x3b, left[Vspeaker]); 	mxcmdu(0x44, left[Vtreb]);	mxcmdu(0x45, right[Vtreb]); 	mxcmdu(0x46, left[Vbass]);	mxcmdu(0x47, right[Vbass]); 	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 */	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	voidputbuf(AQueue *q, Buf *b){ 	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	voidcontindma(void){	Buf *b; 	if(!audio.active)		goto shutdown; 	b = audio.current;	if(audio.amode == Aread) {		if(b)	/* shouldnt happen */			putbuf(&audio.full, b);		b = getbuf(&audio.empty);	} else {		if(b)	/* shouldnt happen */			putbuf(&audio.empty, b);		b = getbuf(&audio.full);	}	audio.current = b;	if(b == 0)		goto shutdown; 	dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread);	return; shutdown:	dmaend(blaster.dma);	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	voidstartdma(void){	ulong count;	int speed; 	ilock(&blaster);	dmaend(blaster.dma);	if(audio.amode == Aread) {		sbcmd(0x42);			/* input sampling rate */		speed = audio.livol[Vspeed];	} else {		sbcmd(0x41);			/* output sampling rate */		speed = audio.lovol[Vspeed];	}	sbcmd(speed>>8);	sbcmd(speed); 	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();	iunlock(&blaster);} /* * if audio is stopped, * start it up again. */static	voidpokeaudio(void){	if(!audio.active)		startdma();} static voidaudiosbintr(void){	int stat, dummy; 	stat = mxread(0x82) & 7;		/* get irq status */	if(stat) {		dummy = 0;		if(stat & 2) {			ilock(&blaster);			dummy = inb(blaster.clri16);			contindma();			iunlock(&blaster);			audio.intr = 1;			wakeup(&audio.vous);		}		if(stat & 1) {			dummy = inb(blaster.clri8);		}		if(stat & 4) {			dummy = inb(blaster.clri401);		}		USED(dummy);	}} static voidpcaudiosbintr(Ureg*, void*){/*	print("#A: audio interrupt\n");	/**/	audiosbintr();} static voidaudiodmaintr(void){/*	print("#A: dma interrupt\n");	/**/} static intanybuf(void*){	return audio.intr;} /* * wait for some output to get * empty buffers back. */static voidwaitaudio(void){ 	audio.intr = 0;	pokeaudio();	tsleep(&audio.vous, anybuf, 0, 10*1000);	if(audio.intr == 0) {/*		print("#A: audio timeout\n");	/**/		audio.active = 0;		pokeaudio();	}} static voidsbbufinit(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	voidsetempty(void){	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;	for(i=0; i<Nbuf; i++)		putbuf(&audio.empty, &audio.buf[i]);	iunlock(&blaster);} static	voidresetlevel(void){	int i; 	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;	}}

Lines 1–500 of 947.