Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 *	SB 16 driver
3
 */
4
#include	"u.h"
5
#include	"../port/lib.h"
6
#include	"mem.h"
7
#include	"dat.h"
8
#include	"fns.h"
9
#include	"../port/error.h"
10
#include	"io.h"
11
#include	"audio.h"
12
 
13
typedef struct	AQueue	AQueue;
14
typedef struct	Buf	Buf;
15
 
16
enum
17
{
18
	Qdir		= 0,
19
	Qaudio,
20
	Qvolume,
21
	Qstatus,
22
 
23
	Fmono		= 1,
24
	Fin		= 2,
25
	Fout		= 4,
26
 
27
	Aclosed		= 0,
28
	Aread,
29
	Awrite,
30
 
31
	Vaudio		= 0,
32
	Vsynth,
33
	Vcd,
34
	Vline,
35
	Vmic,
36
	Vspeaker,
37
	Vtreb,
38
	Vbass,
39
	Vspeed,
40
	Nvol,
41
 
42
	Speed		= 44100,
43
	Ncmd		= 50,		/* max volume command words */
44
};
45
 
46
Dirtab
47
audiodir[] =
48
{
49
	".",	{Qdir, 0, QTDIR},		0,	DMDIR|0555,
50
	"audio",	{Qaudio},		0,	0666,
51
	"volume",	{Qvolume},		0,	0666,
52
	"audiostat",{Qstatus},		0,	0444,
53
};
54
 
55
struct	Buf
56
{
57
	uchar*	virt;
58
	ulong	phys;
59
	Buf*	next;
60
};
61
struct	AQueue
62
{
63
	Lock;
64
	Buf*	first;
65
	Buf*	last;
66
};
67
static	struct
68
{
69
	QLock;
70
	Rendez	vous;
71
	int	buffered;		/* number of bytes en route */
72
	int	bufinit;		/* boolean if buffers allocated */
73
	int	curcount;		/* how much data in current buffer */
74
	int	active;		/* boolean dma running */
75
	int	intr;			/* boolean an interrupt has happened */
76
	int	amode;		/* Aclosed/Aread/Awrite for /audio */
77
	int	rivol[Nvol];	/* right/left input/output volumes */
78
	int	livol[Nvol];
79
	int	rovol[Nvol];
80
	int	lovol[Nvol];
81
	int	major;		/* SB16 major version number (sb 4) */
82
	int	minor;		/* SB16 minor version number */
83
	ulong	totcount;	/* how many bytes processed since open */
84
	vlong	tottime;	/* time at which totcount bytes were processed */
85
 
86
	Buf	buf[Nbuf];		/* buffers and queues */
87
	AQueue	empty;
88
	AQueue	full;
89
	Buf*	current;
90
	Buf*	filling;
91
} audio;
92
 
93
static	struct
94
{
95
	char*	name;
96
	int	flag;
97
	int	ilval;		/* initial values */
98
	int	irval;
99
} volumes[] =
100
{
101
[Vaudio]		"audio",	Fout, 		50,	50,
102
[Vsynth]		"synth",	Fin|Fout,	0,	0,
103
[Vcd]		"cd",		Fin|Fout,	0,	0,
104
[Vline]		"line",	Fin|Fout,	0,	0,
105
[Vmic]		"mic",	Fin|Fout|Fmono,	0,	0,
106
[Vspeaker]	"speaker",	Fout|Fmono,	0,	0,
107
 
108
[Vtreb]		"treb",		Fout, 		50,	50,
109
[Vbass]		"bass",		Fout, 		50,	50,
110
 
111
[Vspeed]	"speed",	Fin|Fout|Fmono,	Speed,	Speed,
112
 
113
};
114
 
115
static struct
116
{
117
	Lock;
118
	int	reset;		/* io ports to the sound blaster */
119
	int	read;
120
	int	write;
121
	int	wstatus;
122
	int	rstatus;
123
	int	mixaddr;
124
	int	mixdata;
125
	int	clri8;
126
	int	clri16;
127
	int	clri401;
128
	int	dma;
129
 
130
	void	(*startdma)(void);
131
	void	(*intr)(void);
132
} blaster;
133
 
134
static	void	swab(uchar*);
135
 
136
static	char	Emajor[]	= "soundblaster not responding/wrong version";
137
static	char	Emode[]		= "illegal open mode";
138
static	char	Evolume[]	= "illegal volume specifier";
139
 
140
static	int
141
sbcmd(int val)
142
{
143
	int i, s;
144
 
145
	for(i=1<<16; i!=0; i--) {
146
		s = inb(blaster.wstatus);
147
		if((s & 0x80) == 0) {
148
			outb(blaster.write, val);
149
			return 0;
150
		}
151
	}
152
/*	print("#A: sbcmd (%#.2x) timeout\n", val);	/**/
153
	return 1;
154
}
155
 
156
static	int
157
sbread(void)
158
{
159
	int i, s;
160
 
161
	for(i=1<<16; i!=0; i--) {
162
		s = inb(blaster.rstatus);
163
		if((s & 0x80) != 0) {
164
			return inb(blaster.read);
165
		}
166
	}
167
/*	print("#A: sbread did not respond\n");	/**/
168
	return -1;
169
}
170
 
171
static int
172
ess1688w(int reg, int val)
173
{
174
	if(sbcmd(reg) || sbcmd(val))
175
		return 1;
176
 
177
	return 0;
178
}
179
 
180
static int
181
ess1688r(int reg)
182
{
183
	if(sbcmd(0xC0) || sbcmd(reg))
184
		return -1;
185
 
186
	return sbread();
187
}
188
 
189
static	int
190
mxcmd(int addr, int val)
191
{
192
 
193
	outb(blaster.mixaddr, addr);
194
	outb(blaster.mixdata, val);
195
	return 1;
196
}
197
 
198
static	int
199
mxread(int addr)
200
{
201
	int s;
202
 
203
	outb(blaster.mixaddr, addr);
204
	s = inb(blaster.mixdata);
205
	return s;
206
}
207
 
208
static	void
209
mxcmds(int s, int v)
210
{
211
 
212
	if(v > 100)
213
		v = 100;
214
	if(v < 0)
215
		v = 0;
216
	mxcmd(s, (v*255)/100);
217
}
218
 
219
static	void
220
mxcmdt(int s, int v)
221
{
222
 
223
	if(v > 100)
224
		v = 100;
225
	if(v <= 0)
226
		mxcmd(s, 0);
227
	else
228
		mxcmd(s, 255-100+v);
229
}
230
 
231
static	void
232
mxcmdu(int s, int v)
233
{
234
 
235
	if(v > 100)
236
		v = 100;
237
	if(v <= 0)
238
		v = 0;
239
	mxcmd(s, 128-50+v);
240
}
241
 
242
static	void
243
mxvolume(void)
244
{
245
	int *left, *right;
246
	int source;
247
 
248
	if(audio.amode == Aread){
249
		left = audio.livol;
250
		right = audio.rivol;
251
	}else{
252
		left = audio.lovol;
253
		right = audio.rovol;
254
	}
255
 
256
	ilock(&blaster);
257
 
258
	mxcmd(0x30, 255);		/* left master */
259
	mxcmd(0x31, 255);		/* right master */
260
	mxcmd(0x3f, 0);		/* left igain */
261
	mxcmd(0x40, 0);		/* right igain */
262
	mxcmd(0x41, 0);		/* left ogain */
263
	mxcmd(0x42, 0);		/* right ogain */
264
 
265
	mxcmds(0x32, left[Vaudio]);
266
	mxcmds(0x33, right[Vaudio]);
267
 
268
	mxcmds(0x34, left[Vsynth]);
269
	mxcmds(0x35, right[Vsynth]);
270
 
271
	mxcmds(0x36, left[Vcd]);
272
	mxcmds(0x37, right[Vcd]);
273
 
274
	mxcmds(0x38, left[Vline]);
275
	mxcmds(0x39, right[Vline]);
276
 
277
	mxcmds(0x3a, left[Vmic]);
278
	mxcmds(0x3b, left[Vspeaker]);
279
 
280
	mxcmdu(0x44, left[Vtreb]);
281
	mxcmdu(0x45, right[Vtreb]);
282
 
283
	mxcmdu(0x46, left[Vbass]);
284
	mxcmdu(0x47, right[Vbass]);
285
 
286
	source = 0;
287
	if(left[Vsynth])
288
		source |= 1<<6;
289
	if(right[Vsynth])
290
		source |= 1<<5;
291
	if(left[Vaudio])
292
		source |= 1<<4;
293
	if(right[Vaudio])
294
		source |= 1<<3;
295
	if(left[Vcd])
296
		source |= 1<<2;
297
	if(right[Vcd])
298
		source |= 1<<1;
299
	if(left[Vmic])
300
		source |= 1<<0;
301
	if(audio.amode == Aread)
302
		mxcmd(0x3c, 0);		/* output switch */
303
	else
304
		mxcmd(0x3c, source);
305
	mxcmd(0x3d, source);		/* input left switch */
306
	mxcmd(0x3e, source);		/* input right switch */
307
	iunlock(&blaster);
308
}
309
 
310
static	Buf*
311
getbuf(AQueue *q)
312
{
313
	Buf *b;
314
 
315
	ilock(q);
316
	b = q->first;
317
	if(b)
318
		q->first = b->next;
319
	iunlock(q);
320
 
321
	return b;
322
}
323
 
324
static	void
325
putbuf(AQueue *q, Buf *b)
326
{
327
 
328
	ilock(q);
329
	b->next = 0;
330
	if(q->first)
331
		q->last->next = b;
332
	else
333
		q->first = b;
334
	q->last = b;
335
	iunlock(q);
336
}
337
 
338
/*
339
 * move the dma to the next buffer
340
 */
341
static	void
342
contindma(void)
343
{
344
	Buf *b;
345
 
346
	if(!audio.active)
347
		goto shutdown;
348
 
349
	b = audio.current;
350
	if(b){
351
		audio.totcount += Bufsize;
352
		audio.tottime = todget(nil);
353
	}
354
	if(audio.amode == Aread) {
355
		if(b){
356
			putbuf(&audio.full, b);
357
			audio.buffered += Bufsize;
358
		}
359
		b = getbuf(&audio.empty);
360
	} else {
361
		if(b){
362
			putbuf(&audio.empty, b);
363
			audio.buffered -= Bufsize;
364
		}
365
		b = getbuf(&audio.full);
366
	}
367
	audio.current = b;
368
	if(b == 0)
369
		goto shutdown;
370
 
371
	if(dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread) >= 0)
372
		return;
373
	print("#A: dmasetup fail\n");
374
	putbuf(&audio.empty, b);
375
 
376
shutdown:
377
	dmaend(blaster.dma);
378
	sbcmd(0xd9);				/* exit at end of count */
379
	sbcmd(0xd5);				/* pause */
380
	audio.curcount = 0;
381
	audio.active = 0;
382
}
383
 
384
/*
385
 * cause sb to get an interrupt per buffer.
386
 * start first dma
387
 */
388
static	void
389
sb16startdma(void)
390
{
391
	ulong count;
392
	int speed;
393
 
394
	ilock(&blaster);
395
	dmaend(blaster.dma);
396
	if(audio.amode == Aread) {
397
		sbcmd(0x42);			/* input sampling rate */
398
		speed = audio.livol[Vspeed];
399
	} else {
400
		sbcmd(0x41);			/* output sampling rate */
401
		speed = audio.lovol[Vspeed];
402
	}
403
	sbcmd(speed>>8);
404
	sbcmd(speed);
405
 
406
	count = (Bufsize >> 1) - 1;
407
	if(audio.amode == Aread)
408
		sbcmd(0xbe);			/* A/D, autoinit */
409
	else
410
		sbcmd(0xb6);			/* D/A, autoinit */
411
	sbcmd(0x30);				/* stereo, 16 bit */
412
	sbcmd(count);
413
	sbcmd(count>>8);
414
 
415
	audio.active = 1;
416
	contindma();
417
	iunlock(&blaster);
418
}
419
 
420
static int
421
ess1688reset(void)
422
{
423
	int i;
424
 
425
	outb(blaster.reset, 3);
426
	delay(1);			/* >3 Ï…s */
427
	outb(blaster.reset, 0);
428
	delay(1);
429
 
430
	i = sbread();
431
	if(i != 0xAA) {
432
		print("#A: no response %#.2x\n", i);
433
		return 1;
434
	}
435
 
436
	if(sbcmd(0xC6)){		/* extended mode */
437
		print("#A: barf 3\n");
438
		return 1;
439
	}
440
 
441
	return 0;
442
}
443
 
444
static	void
445
ess1688startdma(void)
446
{
447
	ulong count;
448
	int speed, x;
449
 
450
	ilock(&blaster);
451
	dmaend(blaster.dma);
452
 
453
	if(audio.amode == Awrite)
454
		ess1688reset();
455
	if(audio.amode == Aread)
456
		sbcmd(0xD3);			/* speaker off */
457
 
458
	/*
459
	 * Set the speed.
460
	 */
461
	if(audio.amode == Aread)
462
		speed = audio.livol[Vspeed];
463
	else
464
		speed = audio.lovol[Vspeed];
465
	if(speed < 4000)
466
		speed = 4000;
467
	else if(speed > 48000)
468
		speed = 48000;
469
 
470
	if(speed > 22000)
471
		  x = 0x80|(256-(795500+speed/2)/speed);
472
	else
473
		  x = 128-(397700+speed/2)/speed;
474
	ess1688w(0xA1, x & 0xFF);
475
 
476
	speed = (speed * 9) / 20;
477
	x = 256 - 7160000 / (speed * 82);
478
	ess1688w(0xA2, x & 0xFF);
479
 
480
	if(audio.amode == Aread)
481
		ess1688w(0xB8, 0x0E);		/* A/D, autoinit */
482
	else
483
		ess1688w(0xB8, 0x04);		/* D/A, autoinit */
484
	x = ess1688r(0xA8) & ~0x03;
485
	ess1688w(0xA8, x|0x01);			/* 2 channels */
486
	ess1688w(0xB9, 2);			/* demand mode, 4 bytes per request */
487
 
488
	if(audio.amode == Awrite)
489
		ess1688w(0xB6, 0);
490
	ess1688w(0xB7, 0x71);
491
	ess1688w(0xB7, 0xBC);
492
 
493
	x = ess1688r(0xB1) & 0x0F;
494
	ess1688w(0xB1, x|0x50);
495
	x = ess1688r(0xB2) & 0x0F;
496
	ess1688w(0xB2, x|0x50);
497
	if(audio.amode == Awrite)
498
		sbcmd(0xD1);			/* speaker on */
499
 
500
	count = -Bufsize;
501
	ess1688w(0xA4, count & 0xFF);
502
	ess1688w(0xA5, (count>>8) & 0xFF);
503
	x = ess1688r(0xB8);
504
	ess1688w(0xB8, x|0x05);
505
 
506
	audio.active = 1;
507
	contindma();
508
	iunlock(&blaster);
509
}
510
 
511
/*
512
 * if audio is stopped,
513
 * start it up again.
514
 */
515
static	void
516
pokeaudio(void)
517
{
518
	if(!audio.active)
519
		blaster.startdma();
520
}
521
 
522
static void
523
sb16intr(void)
524
{
525
	int stat, dummy;
526
 
527
	stat = mxread(0x82) & 7;		/* get irq status */
528
	if(stat) {
529
		dummy = 0;
530
		if(stat & 2) {
531
			ilock(&blaster);
532
			dummy = inb(blaster.clri16);
533
			contindma();
534
			iunlock(&blaster);
535
			audio.intr = 1;
536
			wakeup(&audio.vous);
537
		}
538
		if(stat & 1) {
539
			dummy = inb(blaster.clri8);
540
		}
541
		if(stat & 4) {
542
			dummy = inb(blaster.clri401);
543
		}
544
		USED(dummy);
545
	}
546
}
547
 
548
static void
549
ess1688intr(void)
550
{
551
	int dummy;
552
 
553
	if(audio.active){
554
		ilock(&blaster);
555
		contindma();
556
		dummy = inb(blaster.clri8);
557
		iunlock(&blaster);
558
		audio.intr = 1;
559
		wakeup(&audio.vous);
560
		USED(dummy);
561
	}
562
	else
563
		print("#A: unexpected ess1688 interrupt\n");
564
}
565
 
566
void
567
audiosbintr(void)
568
{
569
	/*
570
	 * Carrera interrupt interface.
571
	 */
572
	blaster.intr();
573
}
574
 
575
static void
576
pcaudiosbintr(Ureg*, void*)
577
{
578
	/*
579
	 * x86 interrupt interface.
580
	 */
581
	blaster.intr();
582
}
583
 
584
void
585
audiodmaintr(void)
586
{
587
/*	print("#A: dma interrupt\n");	/**/
588
}
589
 
590
static int
591
anybuf(void*)
592
{
593
	return audio.intr;
594
}
595
 
596
/*
597
 * wait for some output to get
598
 * empty buffers back.
599
 */
600
static void
601
waitaudio(void)
602
{
603
 
604
	audio.intr = 0;
605
	pokeaudio();
606
	tsleep(&audio.vous, anybuf, 0, 10000);
607
	if(audio.intr == 0) {
608
/*		print("#A: audio timeout\n");	/**/
609
		audio.active = 0;
610
		pokeaudio();
611
	}
612
}
613
 
614
static void
615
sbbufinit(void)
616
{
617
	int i;
618
	uchar *p;
619
 
620
	p = (uchar*)(((ulong)xalloc((Nbuf+1) * Bufsize) + Bufsize-1) &
621
		~(Bufsize-1));
622
	if (p == nil)
623
		panic("sbbufinit: no memory");
624
	for(i=0; i<Nbuf; i++) {
625
		dcflush(p, Bufsize);
626
		audio.buf[i].virt = UNCACHED(uchar, p);
627
		audio.buf[i].phys = (ulong)PADDR(p);
628
		p += Bufsize;
629
	}
630
}
631
 
632
static	void
633
setempty(void)
634
{
635
	int i;
636
 
637
	ilock(&blaster);
638
	audio.empty.first = 0;
639
	audio.empty.last = 0;
640
	audio.full.first = 0;
641
	audio.full.last = 0;
642
	audio.current = 0;
643
	audio.filling = 0;
644
	audio.buffered = 0;
645
	for(i=0; i<Nbuf; i++)
646
		putbuf(&audio.empty, &audio.buf[i]);
647
	audio.totcount = 0;
648
	audio.tottime = 0LL;
649
	iunlock(&blaster);
650
}
651
 
652
static	void
653
resetlevel(void)
654
{
655
	int i;
656
 
657
	for(i=0; volumes[i].name; i++) {
658
		audio.lovol[i] = volumes[i].ilval;
659
		audio.rovol[i] = volumes[i].irval;
660
		audio.livol[i] = volumes[i].ilval;
661
		audio.rivol[i] = volumes[i].irval;
662
	}
663
}
664
 
665
static int
666
ess1688(ISAConf* sbconf)
667
{
668
	int i, major, minor;
669
 
670
	/*
671
	 * Try for ESS1688.
672
	 */
673
	sbcmd(0xE7);			/* get version */
674
	major = sbread();
675
	minor = sbread();
676
	if(major != 0x68 || minor != 0x8B){
677
		print("#A: model %#.2x %#.2x; not ESS1688 compatible\n", major, minor);
678
		return 1;
679
	}
680
 
681
	ess1688reset();
682
 
683
	switch(sbconf->irq){
684
	case 2:
685
	case 9:
686
		i = 0x50|(0<<2);
687
		break;
688
	case 5:
689
		i = 0x50|(1<<2);
690
		break;
691
	case 7:
692
		i = 0x50|(2<<2);
693
		break;
694
	case 10:
695
		i = 0x50|(3<<2);
696
		break;
697
	default:
698
		print("#A: bad ESS1688 irq %d\n", sbconf->irq);
699
		return 1;
700
	}
701
	ess1688w(0xB1, i);
702
 
703
	switch(sbconf->dma){
704
	case 0:
705
		i = 0x50|(1<<2);
706
		break;
707
	case 1:
708
		i = 0xF0|(2<<2);
709
		break;
710
	case 3:
711
		i = 0x50|(3<<2);
712
		break;
713
	default:
714
		print("#A: bad ESS1688 dma %lud\n", sbconf->dma);
715
		return 1;
716
	}
717
	ess1688w(0xB2, i);
718
 
719
	ess1688reset();
720
 
721
	blaster.startdma = ess1688startdma;
722
	blaster.intr = ess1688intr;
723
 
724
	return 0;
725
}
726
 
727
static void
728
audioinit(void)
729
{
730
	ISAConf sbconf;
731
	int i, x;
732
	static int irq[] = {2,5,7,10};
733
 
734
	sbconf.port = 0x220;
735
	sbconf.dma = Dma;
736
	sbconf.irq = IrqAUDIO;
737
	if(isaconfig("audio", 0, &sbconf) == 0)
738
		return;
739
	if(sbconf.type == nil ||
740
		(cistrcmp(sbconf.type, "sb16") != 0 && 
741
		 cistrcmp(sbconf.type, "ess1688") != 0))
742
		return;
743
	switch(sbconf.port){
744
	case 0x220:
745
	case 0x240:
746
	case 0x260:
747
	case 0x280:
748
		break;
749
	default:
750
		print("#A: bad port %#lux\n", sbconf.port);
751
		return;
752
	}
753
 
754
	if(ioalloc(sbconf.port, 0x10, 0, "audio") < 0){
755
		print("#A: cannot ioalloc range %lux+0x10\n", sbconf.port);
756
		return;
757
	}
758
	if(ioalloc(sbconf.port+0x100, 1, 0, "audio.mpu401") < 0){
759
		iofree(sbconf.port);
760
		print("#A: cannot ioalloc range %lux+0x01\n", sbconf.port+0x100);
761
		return;
762
	}
763
 
764
	switch(sbconf.irq){
765
	case 2:
766
	case 5:
767
	case 7:
768
	case 9:
769
	case 10:
770
		break;
771
	default:
772
		print("#A: bad irq %d\n", sbconf.irq);
773
		iofree(sbconf.port);
774
		iofree(sbconf.port+0x100);
775
		return;
776
	}
777
 
778
	blaster.reset = sbconf.port + 0x6;
779
	blaster.read = sbconf.port + 0xa;
780
	blaster.write = sbconf.port + 0xc;
781
	blaster.wstatus = sbconf.port + 0xc;
782
	blaster.rstatus = sbconf.port + 0xe;
783
	blaster.mixaddr = sbconf.port + 0x4;
784
	blaster.mixdata = sbconf.port + 0x5;
785
	blaster.clri8 = sbconf.port + 0xe;
786
	blaster.clri16 = sbconf.port + 0xf;
787
	blaster.clri401 = sbconf.port + 0x100;
788
	blaster.dma = sbconf.dma;
789
 
790
	blaster.startdma = sb16startdma;
791
	blaster.intr = sb16intr;
792
 
793
	audio.amode = Aclosed;
794
	resetlevel();
795
 
796
	outb(blaster.reset, 1);
797
	delay(1);			/* >3 Ï…s */
798
	outb(blaster.reset, 0);
799
	delay(1);
800
 
801
	i = sbread();
802
	if(i != 0xaa) {
803
		print("#A: no response #%.2x\n", i);
804
		iofree(sbconf.port);
805
		iofree(sbconf.port+0x100);
806
		return;
807
	}
808
 
809
	sbcmd(0xe1);			/* get version */
810
	audio.major = sbread();
811
	audio.minor = sbread();
812
 
813
	if(audio.major != 4) {
814
		if(audio.major != 3 || audio.minor != 1 || ess1688(&sbconf)){
815
			print("#A: model %#.2x %#.2x; not SB 16 compatible\n",
816
				audio.major, audio.minor);
817
			iofree(sbconf.port);
818
			iofree(sbconf.port+0x100);
819
			return;
820
		}
821
		audio.major = 4;
822
	}
823
 
824
	/*
825
	 * initialize the mixer
826
	 */
827
	mxcmd(0x00, 0);			/* Reset mixer */
828
	mxvolume();
829
 
830
	/*
831
	 * Attempt to set IRQ/DMA channels.
832
	 * On old ISA boards, these registers are writable.
833
	 * On Plug-n-Play boards, these are read-only.
834
	 *
835
	 * To accomodate both, we write to the registers,
836
	 * but then use the contents in case the write is
837
	 * disallowed.
838
	 */
839
	mxcmd(0x80,			/* irq */
840
		(sbconf.irq==2)? 1:
841
		(sbconf.irq==5)? 2:
842
		(sbconf.irq==7)? 4:
843
		(sbconf.irq==9)? 1:
844
		(sbconf.irq==10)? 8:
845
		0);
846
 
847
	mxcmd(0x81, 1<<blaster.dma);	/* dma */
848
 
849
	x = mxread(0x81);
850
	for(i=5; i<=7; i++)
851
		if(x & (1<<i)){
852
			blaster.dma = i;
853
			break;
854
		}
855
 
856
	x = mxread(0x80);
857
	for(i=0; i<=3; i++)
858
		if(x & (1<<i)){
859
			sbconf.irq = irq[i];
860
			break;
861
		}
862
 
863
	seteisadma(blaster.dma, audiodmaintr);
864
	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);
865
}
866
 
867
static Chan*
868
audioattach(char *param)
869
{
870
	return devattach('A', param);
871
}
872
 
873
static Walkqid*
874
audiowalk(Chan *c, Chan *nc, char **name, int nname)
875
{
876
	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
877
}
878
 
879
static int
880
audiostat(Chan *c, uchar *db, int n)
881
{
882
	audiodir[Qaudio].length = audio.buffered;
883
	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
884
}
885
 
886
static Chan*
887
audioopen(Chan *c, int omode)
888
{
889
	int amode;
890
 
891
	if(audio.major != 4)
892
		error(Emajor);
893
 
894
	switch((ulong)c->qid.path) {
895
	default:
896
		error(Eperm);
897
		break;
898
 
899
	case Qstatus:
900
		if((omode&7) != OREAD)
901
			error(Eperm);
902
	case Qvolume:
903
	case Qdir:
904
		break;
905
 
906
	case Qaudio:
907
		amode = Awrite;
908
		if((omode&7) == OREAD)
909
			amode = Aread;
910
		qlock(&audio);
911
		if(audio.amode != Aclosed){
912
			qunlock(&audio);
913
			error(Einuse);
914
		}
915
		if(audio.bufinit == 0) {
916
			audio.bufinit = 1;
917
			sbbufinit();
918
		}
919
		audio.amode = amode;
920
		setempty();
921
		audio.curcount = 0;
922
		qunlock(&audio);
923
		mxvolume();
924
		break;
925
	}
926
	c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
927
	c->mode = openmode(omode);
928
	c->flag |= COPEN;
929
	c->offset = 0;
930
 
931
	return c;
932
}
933
 
934
static void
935
audioclose(Chan *c)
936
{
937
	Buf *b;
938
 
939
	switch((ulong)c->qid.path) {
940
	default:
941
		error(Eperm);
942
		break;
943
 
944
	case Qdir:
945
	case Qvolume:
946
	case Qstatus:
947
		break;
948
 
949
	case Qaudio:
950
		if(c->flag & COPEN) {
951
			qlock(&audio);
952
			if(audio.amode == Awrite) {
953
				/* flush out last partial buffer */
954
				b = audio.filling;
955
				if(b) {
956
					audio.filling = 0;
957
					memset(b->virt+audio.curcount, 0, Bufsize-audio.curcount);
958
					audio.buffered += Bufsize-audio.curcount;
959
					swab(b->virt);
960
					putbuf(&audio.full, b);
961
				}
962
				if(!audio.active && audio.full.first)
963
					pokeaudio();
964
			}
965
			audio.amode = Aclosed;
966
			if(waserror()){
967
				qunlock(&audio);
968
				nexterror();
969
			}
970
			while(audio.active)
971
				waitaudio();
972
			setempty();
973
			poperror();
974
			qunlock(&audio);
975
		}
976
		break;
977
	}
978
}
979
 
980
static long
981
audioread(Chan *c, void *v, long n, vlong off)
982
{
983
	int liv, riv, lov, rov;
984
	long m, n0;
985
	char buf[300];
986
	Buf *b;
987
	int j;
988
	ulong offset = off;
989
	char *a;
990
 
991
	n0 = n;
992
	a = v;
993
	switch((ulong)c->qid.path) {
994
	default:
995
		error(Eperm);
996
		break;
997
 
998
	case Qdir:
999
		return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
1000
 
1001
	case Qaudio:
1002
		if(audio.amode != Aread)
1003
			error(Emode);
1004
		qlock(&audio);
1005
		if(waserror()){
1006
			qunlock(&audio);
1007
			nexterror();
1008
		}
1009
		while(n > 0) {
1010
			b = audio.filling;
1011
			if(b == 0) {
1012
				b = getbuf(&audio.full);
1013
				if(b == 0) {
1014
					waitaudio();
1015
					continue;
1016
				}
1017
				audio.filling = b;
1018
				swab(b->virt);
1019
				audio.curcount = 0;
1020
			}
1021
			m = Bufsize-audio.curcount;
1022
			if(m > n)
1023
				m = n;
1024
			memmove(a, b->virt+audio.curcount, m);
1025
 
1026
			audio.curcount += m;
1027
			n -= m;
1028
			a += m;
1029
			audio.buffered -= m;
1030
			if(audio.curcount >= Bufsize) {
1031
				audio.filling = 0;
1032
				putbuf(&audio.empty, b);
1033
			}
1034
		}
1035
		poperror();
1036
		qunlock(&audio);
1037
		break;
1038
 
1039
	case Qstatus:
1040
		buf[0] = 0;
1041
		snprint(buf, sizeof(buf), "bufsize %6d buffered %6d offset  %10lud time %19lld\n",
1042
			Bufsize, audio.buffered, audio.totcount, audio.tottime);
1043
		return readstr(offset, a, n, buf);
1044
 
1045
	case Qvolume:
1046
		j = 0;
1047
		buf[0] = 0;
1048
		for(m=0; volumes[m].name; m++){
1049
			liv = audio.livol[m];
1050
			riv = audio.rivol[m];
1051
			lov = audio.lovol[m];
1052
			rov = audio.rovol[m];
1053
			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
1054
			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
1055
				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
1056
					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
1057
				else{
1058
					if(volumes[m].flag & Fin)
1059
						j += snprint(buf+j, sizeof(buf)-j,
1060
							" in %d", liv);
1061
					if(volumes[m].flag & Fout)
1062
						j += snprint(buf+j, sizeof(buf)-j,
1063
							" out %d", lov);
1064
				}
1065
			}else{
1066
				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
1067
				    liv==lov && riv==rov)
1068
					j += snprint(buf+j, sizeof(buf)-j,
1069
						" left %d right %d",
1070
						liv, riv);
1071
				else{
1072
					if(volumes[m].flag & Fin)
1073
						j += snprint(buf+j, sizeof(buf)-j,
1074
							" in left %d right %d",
1075
							liv, riv);
1076
					if(volumes[m].flag & Fout)
1077
						j += snprint(buf+j, sizeof(buf)-j,
1078
							" out left %d right %d",
1079
							lov, rov);
1080
				}
1081
			}
1082
			j += snprint(buf+j, sizeof(buf)-j, "\n");
1083
		}
1084
		return readstr(offset, a, n, buf);
1085
	}
1086
	return n0-n;
1087
}
1088
 
1089
static long
1090
audiowrite(Chan *c, void *vp, long n, vlong)
1091
{
1092
	long m, n0;
1093
	int i, v, left, right, in, out;
1094
	Cmdbuf *cb;
1095
	Buf *b;
1096
	char *a;
1097
 
1098
	a = vp;
1099
	n0 = n;
1100
	switch((ulong)c->qid.path) {
1101
	default:
1102
		error(Eperm);
1103
		break;
1104
 
1105
	case Qvolume:
1106
		v = Vaudio;
1107
		left = 1;
1108
		right = 1;
1109
		in = 1;
1110
		out = 1;
1111
		cb = parsecmd(vp, n);
1112
		if(waserror()){
1113
			free(cb);
1114
			nexterror();
1115
		}
1116
 
1117
		for(i = 0; i < cb->nf; i++){
1118
			/*
1119
			 * a number is volume
1120
			 */
1121
			if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
1122
				m = strtoul(cb->f[i], 0, 10);
1123
				if(left && out)
1124
					audio.lovol[v] = m;
1125
				if(left && in)
1126
					audio.livol[v] = m;
1127
				if(right && out)
1128
					audio.rovol[v] = m;
1129
				if(right && in)
1130
					audio.rivol[v] = m;
1131
				mxvolume();
1132
				goto cont0;
1133
			}
1134
 
1135
			for(m=0; volumes[m].name; m++) {
1136
				if(strcmp(cb->f[i], volumes[m].name) == 0) {
1137
					v = m;
1138
					in = 1;
1139
					out = 1;
1140
					left = 1;
1141
					right = 1;
1142
					goto cont0;
1143
				}
1144
			}
1145
 
1146
			if(strcmp(cb->f[i], "reset") == 0) {
1147
				resetlevel();
1148
				mxvolume();
1149
				goto cont0;
1150
			}
1151
			if(strcmp(cb->f[i], "in") == 0) {
1152
				in = 1;
1153
				out = 0;
1154
				goto cont0;
1155
			}
1156
			if(strcmp(cb->f[i], "out") == 0) {
1157
				in = 0;
1158
				out = 1;
1159
				goto cont0;
1160
			}
1161
			if(strcmp(cb->f[i], "left") == 0) {
1162
				left = 1;
1163
				right = 0;
1164
				goto cont0;
1165
			}
1166
			if(strcmp(cb->f[i], "right") == 0) {
1167
				left = 0;
1168
				right = 1;
1169
				goto cont0;
1170
			}
1171
			error(Evolume);
1172
			break;
1173
		cont0:;
1174
		}
1175
		free(cb);
1176
		poperror();
1177
		break;
1178
 
1179
	case Qaudio:
1180
		if(audio.amode != Awrite)
1181
			error(Emode);
1182
		qlock(&audio);
1183
		if(waserror()){
1184
			qunlock(&audio);
1185
			nexterror();
1186
		}
1187
		while(n > 0) {
1188
			b = audio.filling;
1189
			if(b == 0) {
1190
				b = getbuf(&audio.empty);
1191
				if(b == 0) {
1192
					waitaudio();
1193
					continue;
1194
				}
1195
				audio.filling = b;
1196
				audio.curcount = 0;
1197
			}
1198
 
1199
			m = Bufsize-audio.curcount;
1200
			if(m > n)
1201
				m = n;
1202
			memmove(b->virt+audio.curcount, a, m);
1203
 
1204
			audio.curcount += m;
1205
			n -= m;
1206
			a += m;
1207
			audio.buffered += m;
1208
			if(audio.curcount >= Bufsize) {
1209
				audio.filling = 0;
1210
				swab(b->virt);
1211
				putbuf(&audio.full, b);
1212
				pokeaudio();
1213
			}
1214
		}
1215
		poperror();
1216
		qunlock(&audio);
1217
		break;
1218
	}
1219
	return n0 - n;
1220
}
1221
 
1222
static	void
1223
swab(uchar *a)
1224
{
1225
	ulong *p, *ep, b;
1226
 
1227
	if(!SBswab){
1228
		USED(a);
1229
		return;
1230
	}
1231
	p = (ulong*)a;
1232
	ep = p + (Bufsize>>2);
1233
	while(p < ep) {
1234
		b = *p;
1235
		b = (b>>24) | (b<<24) |
1236
			((b&0xff0000) >> 8) |
1237
			((b&0x00ff00) << 8);
1238
		*p++ = b;
1239
	}
1240
}
1241
 
1242
Dev audiodevtab = {
1243
	'A',
1244
	"audio",
1245
 
1246
	devreset,
1247
	audioinit,
1248
	devshutdown,
1249
	audioattach,
1250
	audiowalk,
1251
	audiostat,
1252
	audioopen,
1253
	devcreate,
1254
	audioclose,
1255
	audioread,
1256
	devbread,
1257
	audiowrite,
1258
	devbwrite,
1259
	devremove,
1260
	devwstat,
1261
};