xref: /plan9-contrib/sys/src/9/port/devaudio.c (revision 567483c891f7c54442ce1d593e764767ee5fcaf7)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  *	SB 16 driver
37dd7cddfSDavid du Colombier  */
47dd7cddfSDavid du Colombier #include	"u.h"
57dd7cddfSDavid du Colombier #include	"../port/lib.h"
67dd7cddfSDavid du Colombier #include	"mem.h"
77dd7cddfSDavid du Colombier #include	"dat.h"
87dd7cddfSDavid du Colombier #include	"fns.h"
97dd7cddfSDavid du Colombier #include	"../port/error.h"
107dd7cddfSDavid du Colombier #include	"io.h"
117dd7cddfSDavid du Colombier #include	"audio.h"
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier typedef struct	AQueue	AQueue;
147dd7cddfSDavid du Colombier typedef struct	Buf	Buf;
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier enum
177dd7cddfSDavid du Colombier {
187dd7cddfSDavid du Colombier 	Qdir		= 0,
197dd7cddfSDavid du Colombier 	Qaudio,
207dd7cddfSDavid du Colombier 	Qvolume,
217dd7cddfSDavid du Colombier 	Qstatus,
227dd7cddfSDavid du Colombier 
237dd7cddfSDavid du Colombier 	Fmono		= 1,
247dd7cddfSDavid du Colombier 	Fin		= 2,
257dd7cddfSDavid du Colombier 	Fout		= 4,
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier 	Aclosed		= 0,
287dd7cddfSDavid du Colombier 	Aread,
297dd7cddfSDavid du Colombier 	Awrite,
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier 	Vaudio		= 0,
327dd7cddfSDavid du Colombier 	Vsynth,
337dd7cddfSDavid du Colombier 	Vcd,
347dd7cddfSDavid du Colombier 	Vline,
357dd7cddfSDavid du Colombier 	Vmic,
367dd7cddfSDavid du Colombier 	Vspeaker,
377dd7cddfSDavid du Colombier 	Vtreb,
387dd7cddfSDavid du Colombier 	Vbass,
397dd7cddfSDavid du Colombier 	Vspeed,
407dd7cddfSDavid du Colombier 	Nvol,
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier 	Speed		= 44100,
437dd7cddfSDavid du Colombier 	Ncmd		= 50,		/* max volume command words */
447dd7cddfSDavid du Colombier };
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier Dirtab
477dd7cddfSDavid du Colombier audiodir[] =
487dd7cddfSDavid du Colombier {
499a747e4fSDavid du Colombier 	".",	{Qdir, 0, QTDIR},		0,	DMDIR|0555,
507dd7cddfSDavid du Colombier 	"audio",	{Qaudio},		0,	0666,
517dd7cddfSDavid du Colombier 	"volume",	{Qvolume},		0,	0666,
527dd7cddfSDavid du Colombier 	"audiostat",{Qstatus},		0,	0444,
537dd7cddfSDavid du Colombier };
547dd7cddfSDavid du Colombier 
557dd7cddfSDavid du Colombier struct	Buf
567dd7cddfSDavid du Colombier {
577dd7cddfSDavid du Colombier 	uchar*	virt;
587dd7cddfSDavid du Colombier 	ulong	phys;
597dd7cddfSDavid du Colombier 	Buf*	next;
607dd7cddfSDavid du Colombier };
617dd7cddfSDavid du Colombier struct	AQueue
627dd7cddfSDavid du Colombier {
637dd7cddfSDavid du Colombier 	Lock;
647dd7cddfSDavid du Colombier 	Buf*	first;
657dd7cddfSDavid du Colombier 	Buf*	last;
667dd7cddfSDavid du Colombier };
677dd7cddfSDavid du Colombier static	struct
687dd7cddfSDavid du Colombier {
697dd7cddfSDavid du Colombier 	QLock;
707dd7cddfSDavid du Colombier 	Rendez	vous;
719a747e4fSDavid du Colombier 	int	buffered;		/* number of bytes en route */
727dd7cddfSDavid du Colombier 	int	bufinit;		/* boolean if buffers allocated */
737dd7cddfSDavid du Colombier 	int	curcount;		/* how much data in current buffer */
747dd7cddfSDavid du Colombier 	int	active;		/* boolean dma running */
757dd7cddfSDavid du Colombier 	int	intr;			/* boolean an interrupt has happened */
767dd7cddfSDavid du Colombier 	int	amode;		/* Aclosed/Aread/Awrite for /audio */
777dd7cddfSDavid du Colombier 	int	rivol[Nvol];	/* right/left input/output volumes */
787dd7cddfSDavid du Colombier 	int	livol[Nvol];
797dd7cddfSDavid du Colombier 	int	rovol[Nvol];
807dd7cddfSDavid du Colombier 	int	lovol[Nvol];
817dd7cddfSDavid du Colombier 	int	major;		/* SB16 major version number (sb 4) */
827dd7cddfSDavid du Colombier 	int	minor;		/* SB16 minor version number */
837dd7cddfSDavid du Colombier 	ulong	totcount;	/* how many bytes processed since open */
847dd7cddfSDavid du Colombier 	vlong	tottime;	/* time at which totcount bytes were processed */
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier 	Buf	buf[Nbuf];		/* buffers and queues */
877dd7cddfSDavid du Colombier 	AQueue	empty;
887dd7cddfSDavid du Colombier 	AQueue	full;
897dd7cddfSDavid du Colombier 	Buf*	current;
907dd7cddfSDavid du Colombier 	Buf*	filling;
917dd7cddfSDavid du Colombier } audio;
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier static	struct
947dd7cddfSDavid du Colombier {
957dd7cddfSDavid du Colombier 	char*	name;
967dd7cddfSDavid du Colombier 	int	flag;
977dd7cddfSDavid du Colombier 	int	ilval;		/* initial values */
987dd7cddfSDavid du Colombier 	int	irval;
997dd7cddfSDavid du Colombier } volumes[] =
1007dd7cddfSDavid du Colombier {
1017dd7cddfSDavid du Colombier [Vaudio]		"audio",	Fout, 		50,	50,
1027dd7cddfSDavid du Colombier [Vsynth]		"synth",	Fin|Fout,	0,	0,
1037dd7cddfSDavid du Colombier [Vcd]		"cd",		Fin|Fout,	0,	0,
1047dd7cddfSDavid du Colombier [Vline]		"line",	Fin|Fout,	0,	0,
1057dd7cddfSDavid du Colombier [Vmic]		"mic",	Fin|Fout|Fmono,	0,	0,
1067dd7cddfSDavid du Colombier [Vspeaker]	"speaker",	Fout|Fmono,	0,	0,
1077dd7cddfSDavid du Colombier 
1087dd7cddfSDavid du Colombier [Vtreb]		"treb",		Fout, 		50,	50,
1097dd7cddfSDavid du Colombier [Vbass]		"bass",		Fout, 		50,	50,
1107dd7cddfSDavid du Colombier 
1117dd7cddfSDavid du Colombier [Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
1127dd7cddfSDavid du Colombier 		0
1137dd7cddfSDavid du Colombier };
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier static struct
1167dd7cddfSDavid du Colombier {
1177dd7cddfSDavid du Colombier 	Lock;
1187dd7cddfSDavid du Colombier 	int	reset;		/* io ports to the sound blaster */
1197dd7cddfSDavid du Colombier 	int	read;
1207dd7cddfSDavid du Colombier 	int	write;
1217dd7cddfSDavid du Colombier 	int	wstatus;
1227dd7cddfSDavid du Colombier 	int	rstatus;
1237dd7cddfSDavid du Colombier 	int	mixaddr;
1247dd7cddfSDavid du Colombier 	int	mixdata;
1257dd7cddfSDavid du Colombier 	int	clri8;
1267dd7cddfSDavid du Colombier 	int	clri16;
1277dd7cddfSDavid du Colombier 	int	clri401;
1287dd7cddfSDavid du Colombier 	int	dma;
1297dd7cddfSDavid du Colombier 
1307dd7cddfSDavid du Colombier 	void	(*startdma)(void);
1317dd7cddfSDavid du Colombier 	void	(*intr)(void);
1327dd7cddfSDavid du Colombier } blaster;
1337dd7cddfSDavid du Colombier 
1347dd7cddfSDavid du Colombier static	void	swab(uchar*);
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier static	char	Emajor[]	= "soundblaster not responding/wrong version";
1377dd7cddfSDavid du Colombier static	char	Emode[]		= "illegal open mode";
1387dd7cddfSDavid du Colombier static	char	Evolume[]	= "illegal volume specifier";
1397dd7cddfSDavid du Colombier 
1407dd7cddfSDavid du Colombier static	int
sbcmd(int val)1417dd7cddfSDavid du Colombier sbcmd(int val)
1427dd7cddfSDavid du Colombier {
1437dd7cddfSDavid du Colombier 	int i, s;
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier 	for(i=1<<16; i!=0; i--) {
1467dd7cddfSDavid du Colombier 		s = inb(blaster.wstatus);
1477dd7cddfSDavid du Colombier 		if((s & 0x80) == 0) {
1487dd7cddfSDavid du Colombier 			outb(blaster.write, val);
1497dd7cddfSDavid du Colombier 			return 0;
1507dd7cddfSDavid du Colombier 		}
1517dd7cddfSDavid du Colombier 	}
152*567483c8SDavid du Colombier /*	print("#A: sbcmd (%#.2x) timeout\n", val);	/**/
1537dd7cddfSDavid du Colombier 	return 1;
1547dd7cddfSDavid du Colombier }
1557dd7cddfSDavid du Colombier 
1567dd7cddfSDavid du Colombier static	int
sbread(void)1577dd7cddfSDavid du Colombier sbread(void)
1587dd7cddfSDavid du Colombier {
1597dd7cddfSDavid du Colombier 	int i, s;
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	for(i=1<<16; i!=0; i--) {
1627dd7cddfSDavid du Colombier 		s = inb(blaster.rstatus);
1637dd7cddfSDavid du Colombier 		if((s & 0x80) != 0) {
1647dd7cddfSDavid du Colombier 			return inb(blaster.read);
1657dd7cddfSDavid du Colombier 		}
1667dd7cddfSDavid du Colombier 	}
1677dd7cddfSDavid du Colombier /*	print("#A: sbread did not respond\n");	/**/
1687dd7cddfSDavid du Colombier 	return -1;
1697dd7cddfSDavid du Colombier }
1707dd7cddfSDavid du Colombier 
1717dd7cddfSDavid du Colombier static int
ess1688w(int reg,int val)1727dd7cddfSDavid du Colombier ess1688w(int reg, int val)
1737dd7cddfSDavid du Colombier {
1747dd7cddfSDavid du Colombier 	if(sbcmd(reg) || sbcmd(val))
1757dd7cddfSDavid du Colombier 		return 1;
1767dd7cddfSDavid du Colombier 
1777dd7cddfSDavid du Colombier 	return 0;
1787dd7cddfSDavid du Colombier }
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier static int
ess1688r(int reg)1817dd7cddfSDavid du Colombier ess1688r(int reg)
1827dd7cddfSDavid du Colombier {
1837dd7cddfSDavid du Colombier 	if(sbcmd(0xC0) || sbcmd(reg))
1847dd7cddfSDavid du Colombier 		return -1;
1857dd7cddfSDavid du Colombier 
1867dd7cddfSDavid du Colombier 	return sbread();
1877dd7cddfSDavid du Colombier }
1887dd7cddfSDavid du Colombier 
1897dd7cddfSDavid du Colombier static	int
mxcmd(int addr,int val)1907dd7cddfSDavid du Colombier mxcmd(int addr, int val)
1917dd7cddfSDavid du Colombier {
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier 	outb(blaster.mixaddr, addr);
1947dd7cddfSDavid du Colombier 	outb(blaster.mixdata, val);
1957dd7cddfSDavid du Colombier 	return 1;
1967dd7cddfSDavid du Colombier }
1977dd7cddfSDavid du Colombier 
1987dd7cddfSDavid du Colombier static	int
mxread(int addr)1997dd7cddfSDavid du Colombier mxread(int addr)
2007dd7cddfSDavid du Colombier {
2017dd7cddfSDavid du Colombier 	int s;
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier 	outb(blaster.mixaddr, addr);
2047dd7cddfSDavid du Colombier 	s = inb(blaster.mixdata);
2057dd7cddfSDavid du Colombier 	return s;
2067dd7cddfSDavid du Colombier }
2077dd7cddfSDavid du Colombier 
2087dd7cddfSDavid du Colombier static	void
mxcmds(int s,int v)2097dd7cddfSDavid du Colombier mxcmds(int s, int v)
2107dd7cddfSDavid du Colombier {
2117dd7cddfSDavid du Colombier 
2127dd7cddfSDavid du Colombier 	if(v > 100)
2137dd7cddfSDavid du Colombier 		v = 100;
2147dd7cddfSDavid du Colombier 	if(v < 0)
2157dd7cddfSDavid du Colombier 		v = 0;
2167dd7cddfSDavid du Colombier 	mxcmd(s, (v*255)/100);
2177dd7cddfSDavid du Colombier }
2187dd7cddfSDavid du Colombier 
2197dd7cddfSDavid du Colombier static	void
mxcmdt(int s,int v)2207dd7cddfSDavid du Colombier mxcmdt(int s, int v)
2217dd7cddfSDavid du Colombier {
2227dd7cddfSDavid du Colombier 
2237dd7cddfSDavid du Colombier 	if(v > 100)
2247dd7cddfSDavid du Colombier 		v = 100;
2257dd7cddfSDavid du Colombier 	if(v <= 0)
2267dd7cddfSDavid du Colombier 		mxcmd(s, 0);
2277dd7cddfSDavid du Colombier 	else
2287dd7cddfSDavid du Colombier 		mxcmd(s, 255-100+v);
2297dd7cddfSDavid du Colombier }
2307dd7cddfSDavid du Colombier 
2317dd7cddfSDavid du Colombier static	void
mxcmdu(int s,int v)2327dd7cddfSDavid du Colombier mxcmdu(int s, int v)
2337dd7cddfSDavid du Colombier {
2347dd7cddfSDavid du Colombier 
2357dd7cddfSDavid du Colombier 	if(v > 100)
2367dd7cddfSDavid du Colombier 		v = 100;
2377dd7cddfSDavid du Colombier 	if(v <= 0)
2387dd7cddfSDavid du Colombier 		v = 0;
2397dd7cddfSDavid du Colombier 	mxcmd(s, 128-50+v);
2407dd7cddfSDavid du Colombier }
2417dd7cddfSDavid du Colombier 
2427dd7cddfSDavid du Colombier static	void
mxvolume(void)2437dd7cddfSDavid du Colombier mxvolume(void)
2447dd7cddfSDavid du Colombier {
2457dd7cddfSDavid du Colombier 	int *left, *right;
2467dd7cddfSDavid du Colombier 	int source;
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier 	if(audio.amode == Aread){
2497dd7cddfSDavid du Colombier 		left = audio.livol;
2507dd7cddfSDavid du Colombier 		right = audio.rivol;
2517dd7cddfSDavid du Colombier 	}else{
2527dd7cddfSDavid du Colombier 		left = audio.lovol;
2537dd7cddfSDavid du Colombier 		right = audio.rovol;
2547dd7cddfSDavid du Colombier 	}
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier 	ilock(&blaster);
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier 	mxcmd(0x30, 255);		/* left master */
2597dd7cddfSDavid du Colombier 	mxcmd(0x31, 255);		/* right master */
2607dd7cddfSDavid du Colombier 	mxcmd(0x3f, 0);		/* left igain */
2617dd7cddfSDavid du Colombier 	mxcmd(0x40, 0);		/* right igain */
2627dd7cddfSDavid du Colombier 	mxcmd(0x41, 0);		/* left ogain */
2637dd7cddfSDavid du Colombier 	mxcmd(0x42, 0);		/* right ogain */
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier 	mxcmds(0x32, left[Vaudio]);
2667dd7cddfSDavid du Colombier 	mxcmds(0x33, right[Vaudio]);
2677dd7cddfSDavid du Colombier 
2687dd7cddfSDavid du Colombier 	mxcmds(0x34, left[Vsynth]);
2697dd7cddfSDavid du Colombier 	mxcmds(0x35, right[Vsynth]);
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 	mxcmds(0x36, left[Vcd]);
2727dd7cddfSDavid du Colombier 	mxcmds(0x37, right[Vcd]);
2737dd7cddfSDavid du Colombier 
2747dd7cddfSDavid du Colombier 	mxcmds(0x38, left[Vline]);
2757dd7cddfSDavid du Colombier 	mxcmds(0x39, right[Vline]);
2767dd7cddfSDavid du Colombier 
2777dd7cddfSDavid du Colombier 	mxcmds(0x3a, left[Vmic]);
2787dd7cddfSDavid du Colombier 	mxcmds(0x3b, left[Vspeaker]);
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier 	mxcmdu(0x44, left[Vtreb]);
2817dd7cddfSDavid du Colombier 	mxcmdu(0x45, right[Vtreb]);
2827dd7cddfSDavid du Colombier 
2837dd7cddfSDavid du Colombier 	mxcmdu(0x46, left[Vbass]);
2847dd7cddfSDavid du Colombier 	mxcmdu(0x47, right[Vbass]);
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier 	source = 0;
2877dd7cddfSDavid du Colombier 	if(left[Vsynth])
2887dd7cddfSDavid du Colombier 		source |= 1<<6;
2897dd7cddfSDavid du Colombier 	if(right[Vsynth])
2907dd7cddfSDavid du Colombier 		source |= 1<<5;
2917dd7cddfSDavid du Colombier 	if(left[Vaudio])
2927dd7cddfSDavid du Colombier 		source |= 1<<4;
2937dd7cddfSDavid du Colombier 	if(right[Vaudio])
2947dd7cddfSDavid du Colombier 		source |= 1<<3;
2957dd7cddfSDavid du Colombier 	if(left[Vcd])
2967dd7cddfSDavid du Colombier 		source |= 1<<2;
2977dd7cddfSDavid du Colombier 	if(right[Vcd])
2987dd7cddfSDavid du Colombier 		source |= 1<<1;
2997dd7cddfSDavid du Colombier 	if(left[Vmic])
3007dd7cddfSDavid du Colombier 		source |= 1<<0;
3017dd7cddfSDavid du Colombier 	if(audio.amode == Aread)
3027dd7cddfSDavid du Colombier 		mxcmd(0x3c, 0);		/* output switch */
3037dd7cddfSDavid du Colombier 	else
3047dd7cddfSDavid du Colombier 		mxcmd(0x3c, source);
3057dd7cddfSDavid du Colombier 	mxcmd(0x3d, source);		/* input left switch */
3067dd7cddfSDavid du Colombier 	mxcmd(0x3e, source);		/* input right switch */
3077dd7cddfSDavid du Colombier 	iunlock(&blaster);
3087dd7cddfSDavid du Colombier }
3097dd7cddfSDavid du Colombier 
3107dd7cddfSDavid du Colombier static	Buf*
getbuf(AQueue * q)3117dd7cddfSDavid du Colombier getbuf(AQueue *q)
3127dd7cddfSDavid du Colombier {
3137dd7cddfSDavid du Colombier 	Buf *b;
3147dd7cddfSDavid du Colombier 
3157dd7cddfSDavid du Colombier 	ilock(q);
3167dd7cddfSDavid du Colombier 	b = q->first;
3177dd7cddfSDavid du Colombier 	if(b)
3187dd7cddfSDavid du Colombier 		q->first = b->next;
3197dd7cddfSDavid du Colombier 	iunlock(q);
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier 	return b;
3227dd7cddfSDavid du Colombier }
3237dd7cddfSDavid du Colombier 
3247dd7cddfSDavid du Colombier static	void
putbuf(AQueue * q,Buf * b)3257dd7cddfSDavid du Colombier putbuf(AQueue *q, Buf *b)
3267dd7cddfSDavid du Colombier {
3277dd7cddfSDavid du Colombier 
3287dd7cddfSDavid du Colombier 	ilock(q);
3297dd7cddfSDavid du Colombier 	b->next = 0;
3307dd7cddfSDavid du Colombier 	if(q->first)
3317dd7cddfSDavid du Colombier 		q->last->next = b;
3327dd7cddfSDavid du Colombier 	else
3337dd7cddfSDavid du Colombier 		q->first = b;
3347dd7cddfSDavid du Colombier 	q->last = b;
3357dd7cddfSDavid du Colombier 	iunlock(q);
3367dd7cddfSDavid du Colombier }
3377dd7cddfSDavid du Colombier 
3387dd7cddfSDavid du Colombier /*
3397dd7cddfSDavid du Colombier  * move the dma to the next buffer
3407dd7cddfSDavid du Colombier  */
3417dd7cddfSDavid du Colombier static	void
contindma(void)3427dd7cddfSDavid du Colombier contindma(void)
3437dd7cddfSDavid du Colombier {
3447dd7cddfSDavid du Colombier 	Buf *b;
3457dd7cddfSDavid du Colombier 
3467dd7cddfSDavid du Colombier 	if(!audio.active)
3477dd7cddfSDavid du Colombier 		goto shutdown;
3487dd7cddfSDavid du Colombier 
3497dd7cddfSDavid du Colombier 	b = audio.current;
3509a747e4fSDavid du Colombier 	if(b){
3519a747e4fSDavid du Colombier 		audio.totcount += Bufsize;
3529a747e4fSDavid du Colombier 		audio.tottime = todget(nil);
3539a747e4fSDavid du Colombier 	}
3547dd7cddfSDavid du Colombier 	if(audio.amode == Aread) {
3559a747e4fSDavid du Colombier 		if(b){
3567dd7cddfSDavid du Colombier 			putbuf(&audio.full, b);
3579a747e4fSDavid du Colombier 			audio.buffered += Bufsize;
3589a747e4fSDavid du Colombier 		}
3597dd7cddfSDavid du Colombier 		b = getbuf(&audio.empty);
3607dd7cddfSDavid du Colombier 	} else {
3619a747e4fSDavid du Colombier 		if(b){
3627dd7cddfSDavid du Colombier 			putbuf(&audio.empty, b);
3639a747e4fSDavid du Colombier 			audio.buffered -= Bufsize;
3649a747e4fSDavid du Colombier 		}
3657dd7cddfSDavid du Colombier 		b = getbuf(&audio.full);
3667dd7cddfSDavid du Colombier 	}
3677dd7cddfSDavid du Colombier 	audio.current = b;
3687dd7cddfSDavid du Colombier 	if(b == 0)
3697dd7cddfSDavid du Colombier 		goto shutdown;
3707dd7cddfSDavid du Colombier 
3717dd7cddfSDavid du Colombier 	if(dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread) >= 0)
3727dd7cddfSDavid du Colombier 		return;
3737dd7cddfSDavid du Colombier 	print("#A: dmasetup fail\n");
3747dd7cddfSDavid du Colombier 	putbuf(&audio.empty, b);
3757dd7cddfSDavid du Colombier 
3767dd7cddfSDavid du Colombier shutdown:
3777dd7cddfSDavid du Colombier 	dmaend(blaster.dma);
3787dd7cddfSDavid du Colombier 	sbcmd(0xd9);				/* exit at end of count */
3797dd7cddfSDavid du Colombier 	sbcmd(0xd5);				/* pause */
3807dd7cddfSDavid du Colombier 	audio.curcount = 0;
3817dd7cddfSDavid du Colombier 	audio.active = 0;
3827dd7cddfSDavid du Colombier }
3837dd7cddfSDavid du Colombier 
3847dd7cddfSDavid du Colombier /*
3857dd7cddfSDavid du Colombier  * cause sb to get an interrupt per buffer.
3867dd7cddfSDavid du Colombier  * start first dma
3877dd7cddfSDavid du Colombier  */
3887dd7cddfSDavid du Colombier static	void
sb16startdma(void)3897dd7cddfSDavid du Colombier sb16startdma(void)
3907dd7cddfSDavid du Colombier {
3917dd7cddfSDavid du Colombier 	ulong count;
3927dd7cddfSDavid du Colombier 	int speed;
3937dd7cddfSDavid du Colombier 
3947dd7cddfSDavid du Colombier 	ilock(&blaster);
3957dd7cddfSDavid du Colombier 	dmaend(blaster.dma);
3967dd7cddfSDavid du Colombier 	if(audio.amode == Aread) {
3977dd7cddfSDavid du Colombier 		sbcmd(0x42);			/* input sampling rate */
3987dd7cddfSDavid du Colombier 		speed = audio.livol[Vspeed];
3997dd7cddfSDavid du Colombier 	} else {
4007dd7cddfSDavid du Colombier 		sbcmd(0x41);			/* output sampling rate */
4017dd7cddfSDavid du Colombier 		speed = audio.lovol[Vspeed];
4027dd7cddfSDavid du Colombier 	}
4037dd7cddfSDavid du Colombier 	sbcmd(speed>>8);
4047dd7cddfSDavid du Colombier 	sbcmd(speed);
4057dd7cddfSDavid du Colombier 
4067dd7cddfSDavid du Colombier 	count = (Bufsize >> 1) - 1;
4077dd7cddfSDavid du Colombier 	if(audio.amode == Aread)
4087dd7cddfSDavid du Colombier 		sbcmd(0xbe);			/* A/D, autoinit */
4097dd7cddfSDavid du Colombier 	else
4107dd7cddfSDavid du Colombier 		sbcmd(0xb6);			/* D/A, autoinit */
4117dd7cddfSDavid du Colombier 	sbcmd(0x30);				/* stereo, 16 bit */
4127dd7cddfSDavid du Colombier 	sbcmd(count);
4137dd7cddfSDavid du Colombier 	sbcmd(count>>8);
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier 	audio.active = 1;
4167dd7cddfSDavid du Colombier 	contindma();
4177dd7cddfSDavid du Colombier 	iunlock(&blaster);
4187dd7cddfSDavid du Colombier }
4197dd7cddfSDavid du Colombier 
4207dd7cddfSDavid du Colombier static int
ess1688reset(void)4217dd7cddfSDavid du Colombier ess1688reset(void)
4227dd7cddfSDavid du Colombier {
4237dd7cddfSDavid du Colombier 	int i;
4247dd7cddfSDavid du Colombier 
4257dd7cddfSDavid du Colombier 	outb(blaster.reset, 3);
4267dd7cddfSDavid du Colombier 	delay(1);			/* >3 υs */
4277dd7cddfSDavid du Colombier 	outb(blaster.reset, 0);
4287dd7cddfSDavid du Colombier 	delay(1);
4297dd7cddfSDavid du Colombier 
4307dd7cddfSDavid du Colombier 	i = sbread();
4317dd7cddfSDavid du Colombier 	if(i != 0xAA) {
432*567483c8SDavid du Colombier 		print("#A: no response %#.2x\n", i);
4337dd7cddfSDavid du Colombier 		return 1;
4347dd7cddfSDavid du Colombier 	}
4357dd7cddfSDavid du Colombier 
4367dd7cddfSDavid du Colombier 	if(sbcmd(0xC6)){		/* extended mode */
4377dd7cddfSDavid du Colombier 		print("#A: barf 3\n");
4387dd7cddfSDavid du Colombier 		return 1;
4397dd7cddfSDavid du Colombier 	}
4407dd7cddfSDavid du Colombier 
4417dd7cddfSDavid du Colombier 	return 0;
4427dd7cddfSDavid du Colombier }
4437dd7cddfSDavid du Colombier 
4447dd7cddfSDavid du Colombier static	void
ess1688startdma(void)4457dd7cddfSDavid du Colombier ess1688startdma(void)
4467dd7cddfSDavid du Colombier {
4477dd7cddfSDavid du Colombier 	ulong count;
4487dd7cddfSDavid du Colombier 	int speed, x;
4497dd7cddfSDavid du Colombier 
4507dd7cddfSDavid du Colombier 	ilock(&blaster);
4517dd7cddfSDavid du Colombier 	dmaend(blaster.dma);
4527dd7cddfSDavid du Colombier 
4537dd7cddfSDavid du Colombier 	if(audio.amode == Awrite)
4547dd7cddfSDavid du Colombier 		ess1688reset();
4557dd7cddfSDavid du Colombier 	if(audio.amode == Aread)
4567dd7cddfSDavid du Colombier 		sbcmd(0xD3);			/* speaker off */
4577dd7cddfSDavid du Colombier 
4587dd7cddfSDavid du Colombier 	/*
4597dd7cddfSDavid du Colombier 	 * Set the speed.
4607dd7cddfSDavid du Colombier 	 */
4617dd7cddfSDavid du Colombier 	if(audio.amode == Aread)
4627dd7cddfSDavid du Colombier 		speed = audio.livol[Vspeed];
4637dd7cddfSDavid du Colombier 	else
4647dd7cddfSDavid du Colombier 		speed = audio.lovol[Vspeed];
4657dd7cddfSDavid du Colombier 	if(speed < 4000)
4667dd7cddfSDavid du Colombier 		speed = 4000;
4677dd7cddfSDavid du Colombier 	else if(speed > 48000)
4687dd7cddfSDavid du Colombier 		speed = 48000;
4697dd7cddfSDavid du Colombier 
4707dd7cddfSDavid du Colombier 	if(speed > 22000)
4717dd7cddfSDavid du Colombier 		  x = 0x80|(256-(795500+speed/2)/speed);
4727dd7cddfSDavid du Colombier 	else
4737dd7cddfSDavid du Colombier 		  x = 128-(397700+speed/2)/speed;
4747dd7cddfSDavid du Colombier 	ess1688w(0xA1, x & 0xFF);
4757dd7cddfSDavid du Colombier 
4767dd7cddfSDavid du Colombier 	speed = (speed * 9) / 20;
4777dd7cddfSDavid du Colombier 	x = 256 - 7160000 / (speed * 82);
4787dd7cddfSDavid du Colombier 	ess1688w(0xA2, x & 0xFF);
4797dd7cddfSDavid du Colombier 
4807dd7cddfSDavid du Colombier 	if(audio.amode == Aread)
4817dd7cddfSDavid du Colombier 		ess1688w(0xB8, 0x0E);		/* A/D, autoinit */
4827dd7cddfSDavid du Colombier 	else
4837dd7cddfSDavid du Colombier 		ess1688w(0xB8, 0x04);		/* D/A, autoinit */
4847dd7cddfSDavid du Colombier 	x = ess1688r(0xA8) & ~0x03;
4857dd7cddfSDavid du Colombier 	ess1688w(0xA8, x|0x01);			/* 2 channels */
4867dd7cddfSDavid du Colombier 	ess1688w(0xB9, 2);			/* demand mode, 4 bytes per request */
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier 	if(audio.amode == Awrite)
4897dd7cddfSDavid du Colombier 		ess1688w(0xB6, 0);
4907dd7cddfSDavid du Colombier 	ess1688w(0xB7, 0x71);
4917dd7cddfSDavid du Colombier 	ess1688w(0xB7, 0xBC);
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier 	x = ess1688r(0xB1) & 0x0F;
4947dd7cddfSDavid du Colombier 	ess1688w(0xB1, x|0x50);
4957dd7cddfSDavid du Colombier 	x = ess1688r(0xB2) & 0x0F;
4967dd7cddfSDavid du Colombier 	ess1688w(0xB2, x|0x50);
4977dd7cddfSDavid du Colombier 	if(audio.amode == Awrite)
4987dd7cddfSDavid du Colombier 		sbcmd(0xD1);			/* speaker on */
4997dd7cddfSDavid du Colombier 
5007dd7cddfSDavid du Colombier 	count = -Bufsize;
5017dd7cddfSDavid du Colombier 	ess1688w(0xA4, count & 0xFF);
5027dd7cddfSDavid du Colombier 	ess1688w(0xA5, (count>>8) & 0xFF);
5037dd7cddfSDavid du Colombier 	x = ess1688r(0xB8);
5047dd7cddfSDavid du Colombier 	ess1688w(0xB8, x|0x05);
5057dd7cddfSDavid du Colombier 
5067dd7cddfSDavid du Colombier 	audio.active = 1;
5077dd7cddfSDavid du Colombier 	contindma();
5087dd7cddfSDavid du Colombier 	iunlock(&blaster);
5097dd7cddfSDavid du Colombier }
5107dd7cddfSDavid du Colombier 
5117dd7cddfSDavid du Colombier /*
5127dd7cddfSDavid du Colombier  * if audio is stopped,
5137dd7cddfSDavid du Colombier  * start it up again.
5147dd7cddfSDavid du Colombier  */
5157dd7cddfSDavid du Colombier static	void
pokeaudio(void)5167dd7cddfSDavid du Colombier pokeaudio(void)
5177dd7cddfSDavid du Colombier {
5187dd7cddfSDavid du Colombier 	if(!audio.active)
5197dd7cddfSDavid du Colombier 		blaster.startdma();
5207dd7cddfSDavid du Colombier }
5217dd7cddfSDavid du Colombier 
5227dd7cddfSDavid du Colombier static void
sb16intr(void)5237dd7cddfSDavid du Colombier sb16intr(void)
5247dd7cddfSDavid du Colombier {
5257dd7cddfSDavid du Colombier 	int stat, dummy;
5267dd7cddfSDavid du Colombier 
5277dd7cddfSDavid du Colombier 	stat = mxread(0x82) & 7;		/* get irq status */
5287dd7cddfSDavid du Colombier 	if(stat) {
5297dd7cddfSDavid du Colombier 		dummy = 0;
5307dd7cddfSDavid du Colombier 		if(stat & 2) {
5317dd7cddfSDavid du Colombier 			ilock(&blaster);
5327dd7cddfSDavid du Colombier 			dummy = inb(blaster.clri16);
5337dd7cddfSDavid du Colombier 			contindma();
5347dd7cddfSDavid du Colombier 			iunlock(&blaster);
5357dd7cddfSDavid du Colombier 			audio.intr = 1;
5367dd7cddfSDavid du Colombier 			wakeup(&audio.vous);
5377dd7cddfSDavid du Colombier 		}
5387dd7cddfSDavid du Colombier 		if(stat & 1) {
5397dd7cddfSDavid du Colombier 			dummy = inb(blaster.clri8);
5407dd7cddfSDavid du Colombier 		}
5417dd7cddfSDavid du Colombier 		if(stat & 4) {
5427dd7cddfSDavid du Colombier 			dummy = inb(blaster.clri401);
5437dd7cddfSDavid du Colombier 		}
5447dd7cddfSDavid du Colombier 		USED(dummy);
5457dd7cddfSDavid du Colombier 	}
5467dd7cddfSDavid du Colombier }
5477dd7cddfSDavid du Colombier 
5487dd7cddfSDavid du Colombier static void
ess1688intr(void)5497dd7cddfSDavid du Colombier ess1688intr(void)
5507dd7cddfSDavid du Colombier {
5517dd7cddfSDavid du Colombier 	int dummy;
5527dd7cddfSDavid du Colombier 
5537dd7cddfSDavid du Colombier 	if(audio.active){
5547dd7cddfSDavid du Colombier 		ilock(&blaster);
5557dd7cddfSDavid du Colombier 		contindma();
5567dd7cddfSDavid du Colombier 		dummy = inb(blaster.clri8);
5577dd7cddfSDavid du Colombier 		iunlock(&blaster);
5587dd7cddfSDavid du Colombier 		audio.intr = 1;
5597dd7cddfSDavid du Colombier 		wakeup(&audio.vous);
5607dd7cddfSDavid du Colombier 		USED(dummy);
5617dd7cddfSDavid du Colombier 	}
5627dd7cddfSDavid du Colombier 	else
5637dd7cddfSDavid du Colombier 		print("#A: unexpected ess1688 interrupt\n");
5647dd7cddfSDavid du Colombier }
5657dd7cddfSDavid du Colombier 
5667dd7cddfSDavid du Colombier void
audiosbintr(void)5677dd7cddfSDavid du Colombier audiosbintr(void)
5687dd7cddfSDavid du Colombier {
5697dd7cddfSDavid du Colombier 	/*
5707dd7cddfSDavid du Colombier 	 * Carrera interrupt interface.
5717dd7cddfSDavid du Colombier 	 */
5727dd7cddfSDavid du Colombier 	blaster.intr();
5737dd7cddfSDavid du Colombier }
5747dd7cddfSDavid du Colombier 
5757dd7cddfSDavid du Colombier static void
pcaudiosbintr(Ureg *,void *)5767dd7cddfSDavid du Colombier pcaudiosbintr(Ureg*, void*)
5777dd7cddfSDavid du Colombier {
5787dd7cddfSDavid du Colombier 	/*
5797dd7cddfSDavid du Colombier 	 * x86 interrupt interface.
5807dd7cddfSDavid du Colombier 	 */
5817dd7cddfSDavid du Colombier 	blaster.intr();
5827dd7cddfSDavid du Colombier }
5837dd7cddfSDavid du Colombier 
5847dd7cddfSDavid du Colombier void
audiodmaintr(void)5857dd7cddfSDavid du Colombier audiodmaintr(void)
5867dd7cddfSDavid du Colombier {
5877dd7cddfSDavid du Colombier /*	print("#A: dma interrupt\n");	/**/
5887dd7cddfSDavid du Colombier }
5897dd7cddfSDavid du Colombier 
5907dd7cddfSDavid du Colombier static int
anybuf(void *)5917dd7cddfSDavid du Colombier anybuf(void*)
5927dd7cddfSDavid du Colombier {
5937dd7cddfSDavid du Colombier 	return audio.intr;
5947dd7cddfSDavid du Colombier }
5957dd7cddfSDavid du Colombier 
5967dd7cddfSDavid du Colombier /*
5977dd7cddfSDavid du Colombier  * wait for some output to get
5987dd7cddfSDavid du Colombier  * empty buffers back.
5997dd7cddfSDavid du Colombier  */
6007dd7cddfSDavid du Colombier static void
waitaudio(void)6017dd7cddfSDavid du Colombier waitaudio(void)
6027dd7cddfSDavid du Colombier {
6037dd7cddfSDavid du Colombier 
6047dd7cddfSDavid du Colombier 	audio.intr = 0;
6057dd7cddfSDavid du Colombier 	pokeaudio();
606e288d156SDavid du Colombier 	tsleep(&audio.vous, anybuf, 0, 10000);
6077dd7cddfSDavid du Colombier 	if(audio.intr == 0) {
6087dd7cddfSDavid du Colombier /*		print("#A: audio timeout\n");	/**/
6097dd7cddfSDavid du Colombier 		audio.active = 0;
6107dd7cddfSDavid du Colombier 		pokeaudio();
6117dd7cddfSDavid du Colombier 	}
6127dd7cddfSDavid du Colombier }
6137dd7cddfSDavid du Colombier 
6147dd7cddfSDavid du Colombier static void
sbbufinit(void)6157dd7cddfSDavid du Colombier sbbufinit(void)
6167dd7cddfSDavid du Colombier {
6177dd7cddfSDavid du Colombier 	int i;
6189a747e4fSDavid du Colombier 	uchar *p;
6197dd7cddfSDavid du Colombier 
6209c63691cSDavid du Colombier 	p = (uchar*)(((ulong)xalloc((Nbuf+1) * Bufsize) + Bufsize-1) &
6219c63691cSDavid du Colombier 		~(Bufsize-1));
6229c63691cSDavid du Colombier 	if (p == nil)
6239c63691cSDavid du Colombier 		panic("sbbufinit: no memory");
6247dd7cddfSDavid du Colombier 	for(i=0; i<Nbuf; i++) {
6257dd7cddfSDavid du Colombier 		dcflush(p, Bufsize);
6267dd7cddfSDavid du Colombier 		audio.buf[i].virt = UNCACHED(uchar, p);
6277dd7cddfSDavid du Colombier 		audio.buf[i].phys = (ulong)PADDR(p);
6289a747e4fSDavid du Colombier 		p += Bufsize;
6297dd7cddfSDavid du Colombier 	}
6307dd7cddfSDavid du Colombier }
6317dd7cddfSDavid du Colombier 
6327dd7cddfSDavid du Colombier static	void
setempty(void)6337dd7cddfSDavid du Colombier setempty(void)
6347dd7cddfSDavid du Colombier {
6357dd7cddfSDavid du Colombier 	int i;
6367dd7cddfSDavid du Colombier 
6377dd7cddfSDavid du Colombier 	ilock(&blaster);
6387dd7cddfSDavid du Colombier 	audio.empty.first = 0;
6397dd7cddfSDavid du Colombier 	audio.empty.last = 0;
6407dd7cddfSDavid du Colombier 	audio.full.first = 0;
6417dd7cddfSDavid du Colombier 	audio.full.last = 0;
6427dd7cddfSDavid du Colombier 	audio.current = 0;
6437dd7cddfSDavid du Colombier 	audio.filling = 0;
6449a747e4fSDavid du Colombier 	audio.buffered = 0;
6457dd7cddfSDavid du Colombier 	for(i=0; i<Nbuf; i++)
6467dd7cddfSDavid du Colombier 		putbuf(&audio.empty, &audio.buf[i]);
6477dd7cddfSDavid du Colombier 	audio.totcount = 0;
6487dd7cddfSDavid du Colombier 	audio.tottime = 0LL;
6497dd7cddfSDavid du Colombier 	iunlock(&blaster);
6507dd7cddfSDavid du Colombier }
6517dd7cddfSDavid du Colombier 
6527dd7cddfSDavid du Colombier static	void
resetlevel(void)6537dd7cddfSDavid du Colombier resetlevel(void)
6547dd7cddfSDavid du Colombier {
6557dd7cddfSDavid du Colombier 	int i;
6567dd7cddfSDavid du Colombier 
6577dd7cddfSDavid du Colombier 	for(i=0; volumes[i].name; i++) {
6587dd7cddfSDavid du Colombier 		audio.lovol[i] = volumes[i].ilval;
6597dd7cddfSDavid du Colombier 		audio.rovol[i] = volumes[i].irval;
6607dd7cddfSDavid du Colombier 		audio.livol[i] = volumes[i].ilval;
6617dd7cddfSDavid du Colombier 		audio.rivol[i] = volumes[i].irval;
6627dd7cddfSDavid du Colombier 	}
6637dd7cddfSDavid du Colombier }
6647dd7cddfSDavid du Colombier 
6657dd7cddfSDavid du Colombier static int
ess1688(ISAConf * sbconf)6667dd7cddfSDavid du Colombier ess1688(ISAConf* sbconf)
6677dd7cddfSDavid du Colombier {
6687dd7cddfSDavid du Colombier 	int i, major, minor;
6697dd7cddfSDavid du Colombier 
6707dd7cddfSDavid du Colombier 	/*
6717dd7cddfSDavid du Colombier 	 * Try for ESS1688.
6727dd7cddfSDavid du Colombier 	 */
6737dd7cddfSDavid du Colombier 	sbcmd(0xE7);			/* get version */
6747dd7cddfSDavid du Colombier 	major = sbread();
6757dd7cddfSDavid du Colombier 	minor = sbread();
6767dd7cddfSDavid du Colombier 	if(major != 0x68 || minor != 0x8B){
677*567483c8SDavid du Colombier 		print("#A: model %#.2x %#.2x; not ESS1688 compatible\n", major, minor);
6787dd7cddfSDavid du Colombier 		return 1;
6797dd7cddfSDavid du Colombier 	}
6807dd7cddfSDavid du Colombier 
6817dd7cddfSDavid du Colombier 	ess1688reset();
6827dd7cddfSDavid du Colombier 
6837dd7cddfSDavid du Colombier 	switch(sbconf->irq){
6847dd7cddfSDavid du Colombier 	case 2:
6857dd7cddfSDavid du Colombier 	case 9:
6867dd7cddfSDavid du Colombier 		i = 0x50|(0<<2);
6877dd7cddfSDavid du Colombier 		break;
6887dd7cddfSDavid du Colombier 	case 5:
6897dd7cddfSDavid du Colombier 		i = 0x50|(1<<2);
6907dd7cddfSDavid du Colombier 		break;
6917dd7cddfSDavid du Colombier 	case 7:
6927dd7cddfSDavid du Colombier 		i = 0x50|(2<<2);
6937dd7cddfSDavid du Colombier 		break;
6947dd7cddfSDavid du Colombier 	case 10:
6957dd7cddfSDavid du Colombier 		i = 0x50|(3<<2);
6967dd7cddfSDavid du Colombier 		break;
6977dd7cddfSDavid du Colombier 	default:
698fb7f0c93SDavid du Colombier 		print("#A: bad ESS1688 irq %d\n", sbconf->irq);
6997dd7cddfSDavid du Colombier 		return 1;
7007dd7cddfSDavid du Colombier 	}
7017dd7cddfSDavid du Colombier 	ess1688w(0xB1, i);
7027dd7cddfSDavid du Colombier 
7037dd7cddfSDavid du Colombier 	switch(sbconf->dma){
7047dd7cddfSDavid du Colombier 	case 0:
7057dd7cddfSDavid du Colombier 		i = 0x50|(1<<2);
7067dd7cddfSDavid du Colombier 		break;
7077dd7cddfSDavid du Colombier 	case 1:
7087dd7cddfSDavid du Colombier 		i = 0xF0|(2<<2);
7097dd7cddfSDavid du Colombier 		break;
7107dd7cddfSDavid du Colombier 	case 3:
7117dd7cddfSDavid du Colombier 		i = 0x50|(3<<2);
7127dd7cddfSDavid du Colombier 		break;
7137dd7cddfSDavid du Colombier 	default:
7147dd7cddfSDavid du Colombier 		print("#A: bad ESS1688 dma %lud\n", sbconf->dma);
7157dd7cddfSDavid du Colombier 		return 1;
7167dd7cddfSDavid du Colombier 	}
7177dd7cddfSDavid du Colombier 	ess1688w(0xB2, i);
7187dd7cddfSDavid du Colombier 
7197dd7cddfSDavid du Colombier 	ess1688reset();
7207dd7cddfSDavid du Colombier 
7217dd7cddfSDavid du Colombier 	blaster.startdma = ess1688startdma;
7227dd7cddfSDavid du Colombier 	blaster.intr = ess1688intr;
7237dd7cddfSDavid du Colombier 
7247dd7cddfSDavid du Colombier 	return 0;
7257dd7cddfSDavid du Colombier }
7267dd7cddfSDavid du Colombier 
7277dd7cddfSDavid du Colombier static void
audioinit(void)7287dd7cddfSDavid du Colombier audioinit(void)
7297dd7cddfSDavid du Colombier {
7307dd7cddfSDavid du Colombier 	ISAConf sbconf;
7317dd7cddfSDavid du Colombier 	int i, x;
7327dd7cddfSDavid du Colombier 	static int irq[] = {2,5,7,10};
7337dd7cddfSDavid du Colombier 
7347dd7cddfSDavid du Colombier 	sbconf.port = 0x220;
7357dd7cddfSDavid du Colombier 	sbconf.dma = Dma;
7367dd7cddfSDavid du Colombier 	sbconf.irq = IrqAUDIO;
7377dd7cddfSDavid du Colombier 	if(isaconfig("audio", 0, &sbconf) == 0)
7387dd7cddfSDavid du Colombier 		return;
7393ff48bf5SDavid du Colombier 	if(sbconf.type == nil ||
7403ff48bf5SDavid du Colombier 		(cistrcmp(sbconf.type, "sb16") != 0 &&
7413ff48bf5SDavid du Colombier 		 cistrcmp(sbconf.type, "ess1688") != 0))
7427dd7cddfSDavid du Colombier 		return;
7437dd7cddfSDavid du Colombier 	switch(sbconf.port){
7447dd7cddfSDavid du Colombier 	case 0x220:
7457dd7cddfSDavid du Colombier 	case 0x240:
7467dd7cddfSDavid du Colombier 	case 0x260:
7477dd7cddfSDavid du Colombier 	case 0x280:
7487dd7cddfSDavid du Colombier 		break;
7497dd7cddfSDavid du Colombier 	default:
750*567483c8SDavid du Colombier 		print("#A: bad port %#lux\n", sbconf.port);
7517dd7cddfSDavid du Colombier 		return;
7527dd7cddfSDavid du Colombier 	}
7539a747e4fSDavid du Colombier 
7549a747e4fSDavid du Colombier 	if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){
7559a747e4fSDavid du Colombier 		print("#A: cannot ioalloc range %lux+0x10\n", sbconf.port);
7569a747e4fSDavid du Colombier 		return;
7579a747e4fSDavid du Colombier 	}
7589a747e4fSDavid du Colombier 	if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){
7599a747e4fSDavid du Colombier 		iofree(sbconf.port);
7609a747e4fSDavid du Colombier 		print("#A: cannot ioalloc range %lux+0x01\n", sbconf.port+0x100);
7619a747e4fSDavid du Colombier 		return;
7629a747e4fSDavid du Colombier 	}
7639a747e4fSDavid du Colombier 
7647dd7cddfSDavid du Colombier 	switch(sbconf.irq){
7657dd7cddfSDavid du Colombier 	case 2:
7667dd7cddfSDavid du Colombier 	case 5:
7677dd7cddfSDavid du Colombier 	case 7:
7687dd7cddfSDavid du Colombier 	case 9:
7697dd7cddfSDavid du Colombier 	case 10:
7707dd7cddfSDavid du Colombier 		break;
7717dd7cddfSDavid du Colombier 	default:
772fb7f0c93SDavid du Colombier 		print("#A: bad irq %d\n", sbconf.irq);
7739a747e4fSDavid du Colombier 		iofree(sbconf.port);
7749a747e4fSDavid du Colombier 		iofree(sbconf.port+0x100);
7757dd7cddfSDavid du Colombier 		return;
7767dd7cddfSDavid du Colombier 	}
7777dd7cddfSDavid du Colombier 
7787dd7cddfSDavid du Colombier 	blaster.reset = sbconf.port + 0x6;
7797dd7cddfSDavid du Colombier 	blaster.read = sbconf.port + 0xa;
7807dd7cddfSDavid du Colombier 	blaster.write = sbconf.port + 0xc;
7817dd7cddfSDavid du Colombier 	blaster.wstatus = sbconf.port + 0xc;
7827dd7cddfSDavid du Colombier 	blaster.rstatus = sbconf.port + 0xe;
7837dd7cddfSDavid du Colombier 	blaster.mixaddr = sbconf.port + 0x4;
7847dd7cddfSDavid du Colombier 	blaster.mixdata = sbconf.port + 0x5;
7857dd7cddfSDavid du Colombier 	blaster.clri8 = sbconf.port + 0xe;
7867dd7cddfSDavid du Colombier 	blaster.clri16 = sbconf.port + 0xf;
7877dd7cddfSDavid du Colombier 	blaster.clri401 = sbconf.port + 0x100;
7887dd7cddfSDavid du Colombier 	blaster.dma = sbconf.dma;
7897dd7cddfSDavid du Colombier 
7907dd7cddfSDavid du Colombier 	blaster.startdma = sb16startdma;
7917dd7cddfSDavid du Colombier 	blaster.intr = sb16intr;
7927dd7cddfSDavid du Colombier 
7937dd7cddfSDavid du Colombier 	audio.amode = Aclosed;
7947dd7cddfSDavid du Colombier 	resetlevel();
7957dd7cddfSDavid du Colombier 
7967dd7cddfSDavid du Colombier 	outb(blaster.reset, 1);
7977dd7cddfSDavid du Colombier 	delay(1);			/* >3 υs */
7987dd7cddfSDavid du Colombier 	outb(blaster.reset, 0);
7997dd7cddfSDavid du Colombier 	delay(1);
8007dd7cddfSDavid du Colombier 
8017dd7cddfSDavid du Colombier 	i = sbread();
8027dd7cddfSDavid du Colombier 	if(i != 0xaa) {
8037dd7cddfSDavid du Colombier 		print("#A: no response #%.2x\n", i);
8049a747e4fSDavid du Colombier 		iofree(sbconf.port);
8059a747e4fSDavid du Colombier 		iofree(sbconf.port+0x100);
8067dd7cddfSDavid du Colombier 		return;
8077dd7cddfSDavid du Colombier 	}
8087dd7cddfSDavid du Colombier 
8097dd7cddfSDavid du Colombier 	sbcmd(0xe1);			/* get version */
8107dd7cddfSDavid du Colombier 	audio.major = sbread();
8117dd7cddfSDavid du Colombier 	audio.minor = sbread();
8127dd7cddfSDavid du Colombier 
8137dd7cddfSDavid du Colombier 	if(audio.major != 4) {
8147dd7cddfSDavid du Colombier 		if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){
815*567483c8SDavid du Colombier 			print("#A: model %#.2x %#.2x; not SB 16 compatible\n",
8167dd7cddfSDavid du Colombier 				audio.major, audio.minor);
8179a747e4fSDavid du Colombier 			iofree(sbconf.port);
8189a747e4fSDavid du Colombier 			iofree(sbconf.port+0x100);
8197dd7cddfSDavid du Colombier 			return;
8207dd7cddfSDavid du Colombier 		}
8217dd7cddfSDavid du Colombier 		audio.major = 4;
8227dd7cddfSDavid du Colombier 	}
8237dd7cddfSDavid du Colombier 
8247dd7cddfSDavid du Colombier 	/*
8257dd7cddfSDavid du Colombier 	 * initialize the mixer
8267dd7cddfSDavid du Colombier 	 */
8277dd7cddfSDavid du Colombier 	mxcmd(0x00, 0);			/* Reset mixer */
8287dd7cddfSDavid du Colombier 	mxvolume();
8297dd7cddfSDavid du Colombier 
8307dd7cddfSDavid du Colombier 	/*
8317dd7cddfSDavid du Colombier 	 * Attempt to set IRQ/DMA channels.
8327dd7cddfSDavid du Colombier 	 * On old ISA boards, these registers are writable.
8337dd7cddfSDavid du Colombier 	 * On Plug-n-Play boards, these are read-only.
8347dd7cddfSDavid du Colombier 	 *
8357dd7cddfSDavid du Colombier 	 * To accomodate both, we write to the registers,
8367dd7cddfSDavid du Colombier 	 * but then use the contents in case the write is
8377dd7cddfSDavid du Colombier 	 * disallowed.
8387dd7cddfSDavid du Colombier 	 */
8397dd7cddfSDavid du Colombier 	mxcmd(0x80,			/* irq */
8407dd7cddfSDavid du Colombier 		(sbconf.irq==2)? 1:
8417dd7cddfSDavid du Colombier 		(sbconf.irq==5)? 2:
8427dd7cddfSDavid du Colombier 		(sbconf.irq==7)? 4:
8437dd7cddfSDavid du Colombier 		(sbconf.irq==9)? 1:
8447dd7cddfSDavid du Colombier 		(sbconf.irq==10)? 8:
8457dd7cddfSDavid du Colombier 		0);
8469a747e4fSDavid du Colombier 
8477dd7cddfSDavid du Colombier 	mxcmd(0x81, 1<<blaster.dma);	/* dma */
8487dd7cddfSDavid du Colombier 
8497dd7cddfSDavid du Colombier 	x = mxread(0x81);
8507dd7cddfSDavid du Colombier 	for(i=5; i<=7; i++)
8517dd7cddfSDavid du Colombier 		if(x & (1<<i)){
8527dd7cddfSDavid du Colombier 			blaster.dma = i;
8537dd7cddfSDavid du Colombier 			break;
8547dd7cddfSDavid du Colombier 		}
8557dd7cddfSDavid du Colombier 
8567dd7cddfSDavid du Colombier 	x = mxread(0x80);
8577dd7cddfSDavid du Colombier 	for(i=0; i<=3; i++)
8587dd7cddfSDavid du Colombier 		if(x & (1<<i)){
8597dd7cddfSDavid du Colombier 			sbconf.irq = irq[i];
8607dd7cddfSDavid du Colombier 			break;
8617dd7cddfSDavid du Colombier 		}
8627dd7cddfSDavid du Colombier 
8637dd7cddfSDavid du Colombier 	seteisadma(blaster.dma, audiodmaintr);
8647dd7cddfSDavid du Colombier 	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);
8657dd7cddfSDavid du Colombier }
8667dd7cddfSDavid du Colombier 
8677dd7cddfSDavid du Colombier static Chan*
audioattach(char * param)8687dd7cddfSDavid du Colombier audioattach(char *param)
8697dd7cddfSDavid du Colombier {
8707dd7cddfSDavid du Colombier 	return devattach('A', param);
8717dd7cddfSDavid du Colombier }
8727dd7cddfSDavid du Colombier 
8739a747e4fSDavid du Colombier static Walkqid*
audiowalk(Chan * c,Chan * nc,char ** name,int nname)8749a747e4fSDavid du Colombier audiowalk(Chan *c, Chan *nc, char **name, int nname)
8757dd7cddfSDavid du Colombier {
8769a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
8777dd7cddfSDavid du Colombier }
8787dd7cddfSDavid du Colombier 
8799a747e4fSDavid du Colombier static int
audiostat(Chan * c,uchar * db,int n)8809a747e4fSDavid du Colombier audiostat(Chan *c, uchar *db, int n)
8817dd7cddfSDavid du Colombier {
8829a747e4fSDavid du Colombier 	audiodir[Qaudio].length = audio.buffered;
8839a747e4fSDavid du Colombier 	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
8847dd7cddfSDavid du Colombier }
8857dd7cddfSDavid du Colombier 
8867dd7cddfSDavid du Colombier static Chan*
audioopen(Chan * c,int omode)8877dd7cddfSDavid du Colombier audioopen(Chan *c, int omode)
8887dd7cddfSDavid du Colombier {
8897dd7cddfSDavid du Colombier 	int amode;
8907dd7cddfSDavid du Colombier 
8917dd7cddfSDavid du Colombier 	if(audio.major != 4)
8927dd7cddfSDavid du Colombier 		error(Emajor);
8937dd7cddfSDavid du Colombier 
8949a747e4fSDavid du Colombier 	switch((ulong)c->qid.path) {
8957dd7cddfSDavid du Colombier 	default:
8967dd7cddfSDavid du Colombier 		error(Eperm);
8977dd7cddfSDavid du Colombier 		break;
8987dd7cddfSDavid du Colombier 
8997dd7cddfSDavid du Colombier 	case Qstatus:
9007dd7cddfSDavid du Colombier 		if((omode&7) != OREAD)
9017dd7cddfSDavid du Colombier 			error(Eperm);
9027dd7cddfSDavid du Colombier 	case Qvolume:
9037dd7cddfSDavid du Colombier 	case Qdir:
9047dd7cddfSDavid du Colombier 		break;
9057dd7cddfSDavid du Colombier 
9067dd7cddfSDavid du Colombier 	case Qaudio:
9077dd7cddfSDavid du Colombier 		amode = Awrite;
9087dd7cddfSDavid du Colombier 		if((omode&7) == OREAD)
9097dd7cddfSDavid du Colombier 			amode = Aread;
9107dd7cddfSDavid du Colombier 		qlock(&audio);
9117dd7cddfSDavid du Colombier 		if(audio.amode != Aclosed){
9127dd7cddfSDavid du Colombier 			qunlock(&audio);
9137dd7cddfSDavid du Colombier 			error(Einuse);
9147dd7cddfSDavid du Colombier 		}
9157dd7cddfSDavid du Colombier 		if(audio.bufinit == 0) {
9167dd7cddfSDavid du Colombier 			audio.bufinit = 1;
9177dd7cddfSDavid du Colombier 			sbbufinit();
9187dd7cddfSDavid du Colombier 		}
9197dd7cddfSDavid du Colombier 		audio.amode = amode;
9207dd7cddfSDavid du Colombier 		setempty();
9217dd7cddfSDavid du Colombier 		audio.curcount = 0;
9227dd7cddfSDavid du Colombier 		qunlock(&audio);
9237dd7cddfSDavid du Colombier 		mxvolume();
9247dd7cddfSDavid du Colombier 		break;
9257dd7cddfSDavid du Colombier 	}
9267dd7cddfSDavid du Colombier 	c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
9277dd7cddfSDavid du Colombier 	c->mode = openmode(omode);
9287dd7cddfSDavid du Colombier 	c->flag |= COPEN;
9297dd7cddfSDavid du Colombier 	c->offset = 0;
9307dd7cddfSDavid du Colombier 
9317dd7cddfSDavid du Colombier 	return c;
9327dd7cddfSDavid du Colombier }
9337dd7cddfSDavid du Colombier 
9347dd7cddfSDavid du Colombier static void
audioclose(Chan * c)9357dd7cddfSDavid du Colombier audioclose(Chan *c)
9367dd7cddfSDavid du Colombier {
9377dd7cddfSDavid du Colombier 	Buf *b;
9387dd7cddfSDavid du Colombier 
9399a747e4fSDavid du Colombier 	switch((ulong)c->qid.path) {
9407dd7cddfSDavid du Colombier 	default:
9417dd7cddfSDavid du Colombier 		error(Eperm);
9427dd7cddfSDavid du Colombier 		break;
9437dd7cddfSDavid du Colombier 
9447dd7cddfSDavid du Colombier 	case Qdir:
9457dd7cddfSDavid du Colombier 	case Qvolume:
9467dd7cddfSDavid du Colombier 	case Qstatus:
9477dd7cddfSDavid du Colombier 		break;
9487dd7cddfSDavid du Colombier 
9497dd7cddfSDavid du Colombier 	case Qaudio:
9507dd7cddfSDavid du Colombier 		if(c->flag & COPEN) {
9517dd7cddfSDavid du Colombier 			qlock(&audio);
9527dd7cddfSDavid du Colombier 			if(audio.amode == Awrite) {
9537dd7cddfSDavid du Colombier 				/* flush out last partial buffer */
9547dd7cddfSDavid du Colombier 				b = audio.filling;
9557dd7cddfSDavid du Colombier 				if(b) {
9567dd7cddfSDavid du Colombier 					audio.filling = 0;
9577dd7cddfSDavid du Colombier 					memset(b->virt+audio.curcount, 0, Bufsize-audio.curcount);
9589a747e4fSDavid du Colombier 					audio.buffered += Bufsize-audio.curcount;
9597dd7cddfSDavid du Colombier 					swab(b->virt);
9607dd7cddfSDavid du Colombier 					putbuf(&audio.full, b);
9617dd7cddfSDavid du Colombier 				}
9627dd7cddfSDavid du Colombier 				if(!audio.active && audio.full.first)
9637dd7cddfSDavid du Colombier 					pokeaudio();
9647dd7cddfSDavid du Colombier 			}
9657dd7cddfSDavid du Colombier 			audio.amode = Aclosed;
9667dd7cddfSDavid du Colombier 			if(waserror()){
9677dd7cddfSDavid du Colombier 				qunlock(&audio);
9687dd7cddfSDavid du Colombier 				nexterror();
9697dd7cddfSDavid du Colombier 			}
9707dd7cddfSDavid du Colombier 			while(audio.active)
9717dd7cddfSDavid du Colombier 				waitaudio();
9727dd7cddfSDavid du Colombier 			setempty();
9737dd7cddfSDavid du Colombier 			poperror();
9747dd7cddfSDavid du Colombier 			qunlock(&audio);
9757dd7cddfSDavid du Colombier 		}
9767dd7cddfSDavid du Colombier 		break;
9777dd7cddfSDavid du Colombier 	}
9787dd7cddfSDavid du Colombier }
9797dd7cddfSDavid du Colombier 
9807dd7cddfSDavid du Colombier static long
audioread(Chan * c,void * v,long n,vlong off)9817dd7cddfSDavid du Colombier audioread(Chan *c, void *v, long n, vlong off)
9827dd7cddfSDavid du Colombier {
9837dd7cddfSDavid du Colombier 	int liv, riv, lov, rov;
9847dd7cddfSDavid du Colombier 	long m, n0;
9857dd7cddfSDavid du Colombier 	char buf[300];
9867dd7cddfSDavid du Colombier 	Buf *b;
9877dd7cddfSDavid du Colombier 	int j;
9887dd7cddfSDavid du Colombier 	ulong offset = off;
9897dd7cddfSDavid du Colombier 	char *a;
9907dd7cddfSDavid du Colombier 
9917dd7cddfSDavid du Colombier 	n0 = n;
9927dd7cddfSDavid du Colombier 	a = v;
9939a747e4fSDavid du Colombier 	switch((ulong)c->qid.path) {
9947dd7cddfSDavid du Colombier 	default:
9957dd7cddfSDavid du Colombier 		error(Eperm);
9967dd7cddfSDavid du Colombier 		break;
9977dd7cddfSDavid du Colombier 
9987dd7cddfSDavid du Colombier 	case Qdir:
9997dd7cddfSDavid du Colombier 		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
10007dd7cddfSDavid du Colombier 
10017dd7cddfSDavid du Colombier 	case Qaudio:
10027dd7cddfSDavid du Colombier 		if(audio.amode != Aread)
10037dd7cddfSDavid du Colombier 			error(Emode);
10047dd7cddfSDavid du Colombier 		qlock(&audio);
10057dd7cddfSDavid du Colombier 		if(waserror()){
10067dd7cddfSDavid du Colombier 			qunlock(&audio);
10077dd7cddfSDavid du Colombier 			nexterror();
10087dd7cddfSDavid du Colombier 		}
10097dd7cddfSDavid du Colombier 		while(n > 0) {
10107dd7cddfSDavid du Colombier 			b = audio.filling;
10117dd7cddfSDavid du Colombier 			if(b == 0) {
10127dd7cddfSDavid du Colombier 				b = getbuf(&audio.full);
10137dd7cddfSDavid du Colombier 				if(b == 0) {
10147dd7cddfSDavid du Colombier 					waitaudio();
10157dd7cddfSDavid du Colombier 					continue;
10167dd7cddfSDavid du Colombier 				}
10177dd7cddfSDavid du Colombier 				audio.filling = b;
10187dd7cddfSDavid du Colombier 				swab(b->virt);
10197dd7cddfSDavid du Colombier 				audio.curcount = 0;
10207dd7cddfSDavid du Colombier 			}
10217dd7cddfSDavid du Colombier 			m = Bufsize-audio.curcount;
10227dd7cddfSDavid du Colombier 			if(m > n)
10237dd7cddfSDavid du Colombier 				m = n;
10247dd7cddfSDavid du Colombier 			memmove(a, b->virt+audio.curcount, m);
10257dd7cddfSDavid du Colombier 
10267dd7cddfSDavid du Colombier 			audio.curcount += m;
10277dd7cddfSDavid du Colombier 			n -= m;
10287dd7cddfSDavid du Colombier 			a += m;
10299a747e4fSDavid du Colombier 			audio.buffered -= m;
10307dd7cddfSDavid du Colombier 			if(audio.curcount >= Bufsize) {
10317dd7cddfSDavid du Colombier 				audio.filling = 0;
10327dd7cddfSDavid du Colombier 				putbuf(&audio.empty, b);
10337dd7cddfSDavid du Colombier 			}
10347dd7cddfSDavid du Colombier 		}
10357dd7cddfSDavid du Colombier 		poperror();
10367dd7cddfSDavid du Colombier 		qunlock(&audio);
10377dd7cddfSDavid du Colombier 		break;
10387dd7cddfSDavid du Colombier 
10397dd7cddfSDavid du Colombier 	case Qstatus:
10407dd7cddfSDavid du Colombier 		buf[0] = 0;
10419a747e4fSDavid du Colombier 		snprint(buf, sizeof(buf), "bufsize %6d buffered %6d offset  %10lud time %19lld\n",
10429a747e4fSDavid du Colombier 			Bufsize, audio.buffered, audio.totcount, audio.tottime);
10437dd7cddfSDavid du Colombier 		return readstr(offset, a, n, buf);
10447dd7cddfSDavid du Colombier 
10457dd7cddfSDavid du Colombier 	case Qvolume:
10467dd7cddfSDavid du Colombier 		j = 0;
10477dd7cddfSDavid du Colombier 		buf[0] = 0;
10487dd7cddfSDavid du Colombier 		for(m=0; volumes[m].name; m++){
10497dd7cddfSDavid du Colombier 			liv = audio.livol[m];
10507dd7cddfSDavid du Colombier 			riv = audio.rivol[m];
10517dd7cddfSDavid du Colombier 			lov = audio.lovol[m];
10527dd7cddfSDavid du Colombier 			rov = audio.rovol[m];
10537dd7cddfSDavid du Colombier 			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
10547dd7cddfSDavid du Colombier 			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
10557dd7cddfSDavid du Colombier 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
10567dd7cddfSDavid du Colombier 					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
10577dd7cddfSDavid du Colombier 				else{
10587dd7cddfSDavid du Colombier 					if(volumes[m].flag & Fin)
10597dd7cddfSDavid du Colombier 						j += snprint(buf+j, sizeof(buf)-j,
10607dd7cddfSDavid du Colombier 							" in %d", liv);
10617dd7cddfSDavid du Colombier 					if(volumes[m].flag & Fout)
10627dd7cddfSDavid du Colombier 						j += snprint(buf+j, sizeof(buf)-j,
10637dd7cddfSDavid du Colombier 							" out %d", lov);
10647dd7cddfSDavid du Colombier 				}
10657dd7cddfSDavid du Colombier 			}else{
10667dd7cddfSDavid du Colombier 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
10677dd7cddfSDavid du Colombier 				    liv==lov && riv==rov)
10687dd7cddfSDavid du Colombier 					j += snprint(buf+j, sizeof(buf)-j,
10697dd7cddfSDavid du Colombier 						" left %d right %d",
10707dd7cddfSDavid du Colombier 						liv, riv);
10717dd7cddfSDavid du Colombier 				else{
10727dd7cddfSDavid du Colombier 					if(volumes[m].flag & Fin)
10737dd7cddfSDavid du Colombier 						j += snprint(buf+j, sizeof(buf)-j,
10747dd7cddfSDavid du Colombier 							" in left %d right %d",
10757dd7cddfSDavid du Colombier 							liv, riv);
10767dd7cddfSDavid du Colombier 					if(volumes[m].flag & Fout)
10777dd7cddfSDavid du Colombier 						j += snprint(buf+j, sizeof(buf)-j,
10787dd7cddfSDavid du Colombier 							" out left %d right %d",
10797dd7cddfSDavid du Colombier 							lov, rov);
10807dd7cddfSDavid du Colombier 				}
10817dd7cddfSDavid du Colombier 			}
10827dd7cddfSDavid du Colombier 			j += snprint(buf+j, sizeof(buf)-j, "\n");
10837dd7cddfSDavid du Colombier 		}
10847dd7cddfSDavid du Colombier 		return readstr(offset, a, n, buf);
10857dd7cddfSDavid du Colombier 	}
10867dd7cddfSDavid du Colombier 	return n0-n;
10877dd7cddfSDavid du Colombier }
10887dd7cddfSDavid du Colombier 
10897dd7cddfSDavid du Colombier static long
audiowrite(Chan * c,void * vp,long n,vlong)10907dd7cddfSDavid du Colombier audiowrite(Chan *c, void *vp, long n, vlong)
10917dd7cddfSDavid du Colombier {
10927dd7cddfSDavid du Colombier 	long m, n0;
10939a747e4fSDavid du Colombier 	int i, v, left, right, in, out;
10949a747e4fSDavid du Colombier 	Cmdbuf *cb;
10957dd7cddfSDavid du Colombier 	Buf *b;
10967dd7cddfSDavid du Colombier 	char *a;
10977dd7cddfSDavid du Colombier 
10987dd7cddfSDavid du Colombier 	a = vp;
10997dd7cddfSDavid du Colombier 	n0 = n;
11009a747e4fSDavid du Colombier 	switch((ulong)c->qid.path) {
11017dd7cddfSDavid du Colombier 	default:
11027dd7cddfSDavid du Colombier 		error(Eperm);
11037dd7cddfSDavid du Colombier 		break;
11047dd7cddfSDavid du Colombier 
11057dd7cddfSDavid du Colombier 	case Qvolume:
11067dd7cddfSDavid du Colombier 		v = Vaudio;
11077dd7cddfSDavid du Colombier 		left = 1;
11087dd7cddfSDavid du Colombier 		right = 1;
11097dd7cddfSDavid du Colombier 		in = 1;
11107dd7cddfSDavid du Colombier 		out = 1;
11119a747e4fSDavid du Colombier 		cb = parsecmd(vp, n);
11129a747e4fSDavid du Colombier 		if(waserror()){
11139a747e4fSDavid du Colombier 			free(cb);
11149a747e4fSDavid du Colombier 			nexterror();
11159a747e4fSDavid du Colombier 		}
11167dd7cddfSDavid du Colombier 
11179a747e4fSDavid du Colombier 		for(i = 0; i < cb->nf; i++){
11187dd7cddfSDavid du Colombier 			/*
11197dd7cddfSDavid du Colombier 			 * a number is volume
11207dd7cddfSDavid du Colombier 			 */
11219a747e4fSDavid du Colombier 			if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
11229a747e4fSDavid du Colombier 				m = strtoul(cb->f[i], 0, 10);
11237dd7cddfSDavid du Colombier 				if(left && out)
11247dd7cddfSDavid du Colombier 					audio.lovol[v] = m;
11257dd7cddfSDavid du Colombier 				if(left && in)
11267dd7cddfSDavid du Colombier 					audio.livol[v] = m;
11277dd7cddfSDavid du Colombier 				if(right && out)
11287dd7cddfSDavid du Colombier 					audio.rovol[v] = m;
11297dd7cddfSDavid du Colombier 				if(right && in)
11307dd7cddfSDavid du Colombier 					audio.rivol[v] = m;
11317dd7cddfSDavid du Colombier 				mxvolume();
11327dd7cddfSDavid du Colombier 				goto cont0;
11337dd7cddfSDavid du Colombier 			}
11347dd7cddfSDavid du Colombier 
11357dd7cddfSDavid du Colombier 			for(m=0; volumes[m].name; m++) {
11369a747e4fSDavid du Colombier 				if(strcmp(cb->f[i], volumes[m].name) == 0) {
11377dd7cddfSDavid du Colombier 					v = m;
11387dd7cddfSDavid du Colombier 					in = 1;
11397dd7cddfSDavid du Colombier 					out = 1;
11407dd7cddfSDavid du Colombier 					left = 1;
11417dd7cddfSDavid du Colombier 					right = 1;
11427dd7cddfSDavid du Colombier 					goto cont0;
11437dd7cddfSDavid du Colombier 				}
11447dd7cddfSDavid du Colombier 			}
11457dd7cddfSDavid du Colombier 
11469a747e4fSDavid du Colombier 			if(strcmp(cb->f[i], "reset") == 0) {
11477dd7cddfSDavid du Colombier 				resetlevel();
11487dd7cddfSDavid du Colombier 				mxvolume();
11497dd7cddfSDavid du Colombier 				goto cont0;
11507dd7cddfSDavid du Colombier 			}
11519a747e4fSDavid du Colombier 			if(strcmp(cb->f[i], "in") == 0) {
11527dd7cddfSDavid du Colombier 				in = 1;
11537dd7cddfSDavid du Colombier 				out = 0;
11547dd7cddfSDavid du Colombier 				goto cont0;
11557dd7cddfSDavid du Colombier 			}
11569a747e4fSDavid du Colombier 			if(strcmp(cb->f[i], "out") == 0) {
11577dd7cddfSDavid du Colombier 				in = 0;
11587dd7cddfSDavid du Colombier 				out = 1;
11597dd7cddfSDavid du Colombier 				goto cont0;
11607dd7cddfSDavid du Colombier 			}
11619a747e4fSDavid du Colombier 			if(strcmp(cb->f[i], "left") == 0) {
11627dd7cddfSDavid du Colombier 				left = 1;
11637dd7cddfSDavid du Colombier 				right = 0;
11647dd7cddfSDavid du Colombier 				goto cont0;
11657dd7cddfSDavid du Colombier 			}
11669a747e4fSDavid du Colombier 			if(strcmp(cb->f[i], "right") == 0) {
11677dd7cddfSDavid du Colombier 				left = 0;
11687dd7cddfSDavid du Colombier 				right = 1;
11697dd7cddfSDavid du Colombier 				goto cont0;
11707dd7cddfSDavid du Colombier 			}
11717dd7cddfSDavid du Colombier 			error(Evolume);
11727dd7cddfSDavid du Colombier 			break;
11737dd7cddfSDavid du Colombier 		cont0:;
11747dd7cddfSDavid du Colombier 		}
11759a747e4fSDavid du Colombier 		free(cb);
11769a747e4fSDavid du Colombier 		poperror();
11777dd7cddfSDavid du Colombier 		break;
11787dd7cddfSDavid du Colombier 
11797dd7cddfSDavid du Colombier 	case Qaudio:
11807dd7cddfSDavid du Colombier 		if(audio.amode != Awrite)
11817dd7cddfSDavid du Colombier 			error(Emode);
11827dd7cddfSDavid du Colombier 		qlock(&audio);
11837dd7cddfSDavid du Colombier 		if(waserror()){
11847dd7cddfSDavid du Colombier 			qunlock(&audio);
11857dd7cddfSDavid du Colombier 			nexterror();
11867dd7cddfSDavid du Colombier 		}
11877dd7cddfSDavid du Colombier 		while(n > 0) {
11887dd7cddfSDavid du Colombier 			b = audio.filling;
11897dd7cddfSDavid du Colombier 			if(b == 0) {
11907dd7cddfSDavid du Colombier 				b = getbuf(&audio.empty);
11917dd7cddfSDavid du Colombier 				if(b == 0) {
11927dd7cddfSDavid du Colombier 					waitaudio();
11937dd7cddfSDavid du Colombier 					continue;
11947dd7cddfSDavid du Colombier 				}
11957dd7cddfSDavid du Colombier 				audio.filling = b;
11967dd7cddfSDavid du Colombier 				audio.curcount = 0;
11977dd7cddfSDavid du Colombier 			}
11987dd7cddfSDavid du Colombier 
11997dd7cddfSDavid du Colombier 			m = Bufsize-audio.curcount;
12007dd7cddfSDavid du Colombier 			if(m > n)
12017dd7cddfSDavid du Colombier 				m = n;
12027dd7cddfSDavid du Colombier 			memmove(b->virt+audio.curcount, a, m);
12037dd7cddfSDavid du Colombier 
12047dd7cddfSDavid du Colombier 			audio.curcount += m;
12057dd7cddfSDavid du Colombier 			n -= m;
12067dd7cddfSDavid du Colombier 			a += m;
12079a747e4fSDavid du Colombier 			audio.buffered += m;
12087dd7cddfSDavid du Colombier 			if(audio.curcount >= Bufsize) {
12097dd7cddfSDavid du Colombier 				audio.filling = 0;
12107dd7cddfSDavid du Colombier 				swab(b->virt);
12117dd7cddfSDavid du Colombier 				putbuf(&audio.full, b);
121225910e17SDavid du Colombier 				pokeaudio();
12137dd7cddfSDavid du Colombier 			}
12147dd7cddfSDavid du Colombier 		}
12157dd7cddfSDavid du Colombier 		poperror();
12167dd7cddfSDavid du Colombier 		qunlock(&audio);
12177dd7cddfSDavid du Colombier 		break;
12187dd7cddfSDavid du Colombier 	}
12197dd7cddfSDavid du Colombier 	return n0 - n;
12207dd7cddfSDavid du Colombier }
12217dd7cddfSDavid du Colombier 
12227dd7cddfSDavid du Colombier static	void
swab(uchar * a)12237dd7cddfSDavid du Colombier swab(uchar *a)
12247dd7cddfSDavid du Colombier {
12257dd7cddfSDavid du Colombier 	ulong *p, *ep, b;
12267dd7cddfSDavid du Colombier 
12277dd7cddfSDavid du Colombier 	if(!SBswab){
12287dd7cddfSDavid du Colombier 		USED(a);
12297dd7cddfSDavid du Colombier 		return;
12307dd7cddfSDavid du Colombier 	}
12317dd7cddfSDavid du Colombier 	p = (ulong*)a;
12327dd7cddfSDavid du Colombier 	ep = p + (Bufsize>>2);
12337dd7cddfSDavid du Colombier 	while(p < ep) {
12347dd7cddfSDavid du Colombier 		b = *p;
12357dd7cddfSDavid du Colombier 		b = (b>>24) | (b<<24) |
12367dd7cddfSDavid du Colombier 			((b&0xff0000) >> 8) |
12377dd7cddfSDavid du Colombier 			((b&0x00ff00) << 8);
12387dd7cddfSDavid du Colombier 		*p++ = b;
12397dd7cddfSDavid du Colombier 	}
12407dd7cddfSDavid du Colombier }
12417dd7cddfSDavid du Colombier 
12427dd7cddfSDavid du Colombier Dev audiodevtab = {
12437dd7cddfSDavid du Colombier 	'A',
12447dd7cddfSDavid du Colombier 	"audio",
12457dd7cddfSDavid du Colombier 
12467dd7cddfSDavid du Colombier 	devreset,
12477dd7cddfSDavid du Colombier 	audioinit,
12489a747e4fSDavid du Colombier 	devshutdown,
12497dd7cddfSDavid du Colombier 	audioattach,
12507dd7cddfSDavid du Colombier 	audiowalk,
12517dd7cddfSDavid du Colombier 	audiostat,
12527dd7cddfSDavid du Colombier 	audioopen,
12537dd7cddfSDavid du Colombier 	devcreate,
12547dd7cddfSDavid du Colombier 	audioclose,
12557dd7cddfSDavid du Colombier 	audioread,
12567dd7cddfSDavid du Colombier 	devbread,
12577dd7cddfSDavid du Colombier 	audiowrite,
12587dd7cddfSDavid du Colombier 	devbwrite,
12597dd7cddfSDavid du Colombier 	devremove,
12607dd7cddfSDavid du Colombier 	devwstat,
12617dd7cddfSDavid du Colombier };
1262