FreeBSD/Linux Kernel Cross Reference
sys/port/devaudio.c
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 0
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] >= '' && 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 };
Cache object: 0708c015c826b08aae89223bc210f9c7
|