|
|
1.1 root 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 "devtab.h"
11: #include "io.h"
12: #include "audio.h"
13:
14: #define NPORT (sizeof audiodir/sizeof(Dirtab))
15:
16: typedef struct AQueue AQueue;
17: typedef struct Buf Buf;
18:
19: enum
20: {
21: Qdir = 0,
22: Qaudio,
23: Qvolume,
24:
25: Fmono = 1,
26: Fin = 2,
27: Fout = 4,
28:
29: Aclosed = 0,
30: Aread,
31: Awrite,
32:
33: Vaudio = 0,
34: Vsynth,
35: Vcd,
36: Vline,
37: Vmic,
38: Vspeaker,
39: Vtreb,
40: Vbass,
41: Vspeed,
42: Nvol,
43:
44: Speed = 44100,
45: Ncmd = 50, /* max volume command words */
46: };
47:
48: Dirtab
49: audiodir[] =
50: {
51: "audio", {Qaudio}, 0, 0666,
52: "volume", {Qvolume}, 0, 0666,
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 bufinit; /* boolean if buffers allocated */
72: int curcount; /* how much data in current buffer */
73: int active; /* boolean dma running */
74: int intr; /* boolean an interrupt has happened */
75: int amode; /* Aclosed/Aread/Awrite for /audio */
76: int rivol[Nvol]; /* right/left input/output volumes */
77: int livol[Nvol];
78: int rovol[Nvol];
79: int lovol[Nvol];
80: int major; /* SB16 major version number (sb 4) */
81: int minor; /* SB16 minor version number */
82:
83: Buf buf[Nbuf]; /* buffers and queues */
84: AQueue empty;
85: AQueue full;
86: Buf* current;
87: Buf* filling;
88: } audio;
89:
90: static struct
91: {
92: char* name;
93: int flag;
94: int ilval; /* initial values */
95: int irval;
96: } volumes[] =
97: {
98: [Vaudio] "audio", Fout, 50, 50,
99: [Vsynth] "synth", Fin|Fout, 0, 0,
100: [Vcd] "cd", Fin|Fout, 0, 0,
101: [Vline] "line", Fin|Fout, 0, 0,
102: [Vmic] "mic", Fin|Fout|Fmono, 0, 0,
103: [Vspeaker] "speaker", Fout|Fmono, 0, 0,
104:
105: [Vtreb] "treb", Fout, 50, 50,
106: [Vbass] "bass", Fout, 50, 50,
107:
108: [Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed,
109: 0
110: };
111:
112: static struct
113: {
114: Lock;
115: int reset; /* io ports to the sound blaster */
116: int read;
117: int write;
118: int wstatus;
119: int rstatus;
120: int mixaddr;
121: int mixdata;
122: int clri8;
123: int clri16;
124: int clri401;
125: int dma;
126: } blaster;
127:
128: static void swab(uchar*);
129:
130: static char Emajor[] = "soundblaster not responding/wrong version";
131: static char Emode[] = "illegal open mode";
132: static char Evolume[] = "illegal volume specifier";
133:
134: static int
135: sbcmd(int val)
136: {
137: int i, s;
138:
139: for(i=1<<16; i!=0; i--) {
140: s = inb(blaster.wstatus);
141: if((s & 0x80) == 0) {
142: outb(blaster.write, val);
143: return 0;
144: }
145: }
146: return 1;
147: }
148:
149: static int
150: sbread(void)
151: {
152: int i, s;
153:
154: for(i=1<<16; i!=0; i--) {
155: s = inb(blaster.rstatus);
156: if((s & 0x80) != 0) {
157: return inb(blaster.read);
158: }
159: }
160: return 0xbb;
161: }
162:
163: static int
164: mxcmd(int addr, int val)
165: {
166:
167: outb(blaster.mixaddr, addr);
168: outb(blaster.mixdata, val);
169: return 1;
170: }
171:
172: static int
173: mxread(int addr)
174: {
175: int s;
176:
177: outb(blaster.mixaddr, addr);
178: s = inb(blaster.mixdata);
179: return s;
180: }
181:
182: static void
183: mxcmds(int s, int v)
184: {
185:
186: if(v > 100)
187: v = 100;
188: if(v < 0)
189: v = 0;
190: mxcmd(s, (v*255)/100);
191: }
192:
193: static void
194: mxcmdt(int s, int v)
195: {
196:
197: if(v > 100)
198: v = 100;
199: if(v <= 0)
200: mxcmd(s, 0);
201: else
202: mxcmd(s, 255-100+v);
203: }
204:
205: static void
206: mxcmdu(int s, int v)
207: {
208:
209: if(v > 100)
210: v = 100;
211: if(v <= 0)
212: v = 0;
213: mxcmd(s, 128-50+v);
214: }
215:
216: static void
217: mxvolume(void)
218: {
219: int *left, *right;
220: int source;
221:
222: if(audio.amode == Aread){
223: left = audio.livol;
224: right = audio.rivol;
225: }else{
226: left = audio.lovol;
227: right = audio.rovol;
228: }
229:
230: ilock(&blaster);
231:
232: mxcmd(0x30, 255); /* left master */
233: mxcmd(0x31, 255); /* right master */
234: mxcmd(0x3f, 0); /* left igain */
235: mxcmd(0x40, 0); /* right igain */
236: mxcmd(0x41, 0); /* left ogain */
237: mxcmd(0x42, 0); /* right ogain */
238:
239: mxcmds(0x32, left[Vaudio]);
240: mxcmds(0x33, right[Vaudio]);
241:
242: mxcmds(0x34, left[Vsynth]);
243: mxcmds(0x35, right[Vsynth]);
244:
245: mxcmds(0x36, left[Vcd]);
246: mxcmds(0x37, right[Vcd]);
247:
248: mxcmds(0x38, left[Vline]);
249: mxcmds(0x39, right[Vline]);
250:
251: mxcmds(0x3a, left[Vmic]);
252: mxcmds(0x3b, left[Vspeaker]);
253:
254: mxcmdu(0x44, left[Vtreb]);
255: mxcmdu(0x45, right[Vtreb]);
256:
257: mxcmdu(0x46, left[Vbass]);
258: mxcmdu(0x47, right[Vbass]);
259:
260: source = 0;
261: if(left[Vsynth])
262: source |= 1<<6;
263: if(right[Vsynth])
264: source |= 1<<5;
265: if(left[Vaudio])
266: source |= 1<<4;
267: if(right[Vaudio])
268: source |= 1<<3;
269: if(left[Vcd])
270: source |= 1<<2;
271: if(right[Vcd])
272: source |= 1<<1;
273: if(left[Vmic])
274: source |= 1<<0;
275: if(audio.amode == Aread)
276: mxcmd(0x3c, 0); /* output switch */
277: else
278: mxcmd(0x3c, source);
279: mxcmd(0x3d, source); /* input left switch */
280: mxcmd(0x3e, source); /* input right switch */
281: iunlock(&blaster);
282: }
283:
284: static Buf*
285: getbuf(AQueue *q)
286: {
287: Buf *b;
288:
289: ilock(q);
290: b = q->first;
291: if(b)
292: q->first = b->next;
293: iunlock(q);
294:
295: return b;
296: }
297:
298: static void
299: putbuf(AQueue *q, Buf *b)
300: {
301:
302: ilock(q);
303: b->next = 0;
304: if(q->first)
305: q->last->next = b;
306: else
307: q->first = b;
308: q->last = b;
309: iunlock(q);
310: }
311:
312: /*
313: * move the dma to the next buffer
314: */
315: static void
316: contindma(void)
317: {
318: Buf *b;
319:
320: if(!audio.active)
321: goto shutdown;
322:
323: b = audio.current;
324: if(audio.amode == Aread) {
325: if(b) /* shouldnt happen */
326: putbuf(&audio.full, b);
327: b = getbuf(&audio.empty);
328: } else {
329: if(b) /* shouldnt happen */
330: putbuf(&audio.empty, b);
331: b = getbuf(&audio.full);
332: }
333: audio.current = b;
334: if(b == 0)
335: goto shutdown;
336:
337: dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread);
338: return;
339:
340: shutdown:
341: dmaend(blaster.dma);
342: sbcmd(0xd9); /* exit at end of count */
343: sbcmd(0xd5); /* pause */
344: audio.curcount = 0;
345: audio.active = 0;
346: }
347:
348: /*
349: * cause sb to get an interrupt per buffer.
350: * start first dma
351: */
352: static void
353: startdma(void)
354: {
355: ulong count;
356: int speed;
357:
358: ilock(&blaster);
359: dmaend(blaster.dma);
360: if(audio.amode == Aread) {
361: sbcmd(0x42); /* input sampling rate */
362: speed = audio.livol[Vspeed];
363: } else {
364: sbcmd(0x41); /* output sampling rate */
365: speed = audio.lovol[Vspeed];
366: }
367: sbcmd(speed>>8);
368: sbcmd(speed);
369:
370: count = (Bufsize >> 1) - 1;
371: if(audio.amode == Aread)
372: sbcmd(0xbe); /* A/D, autoinit */
373: else
374: sbcmd(0xb6); /* D/A, autoinit */
375: sbcmd(0x30); /* stereo, 16 bit */
376: sbcmd(count);
377: sbcmd(count>>8);
378:
379: audio.active = 1;
380: contindma();
381: iunlock(&blaster);
382: }
383:
384: /*
385: * if audio is stopped,
386: * start it up again.
387: */
388: static void
389: pokeaudio(void)
390: {
391: if(!audio.active)
392: startdma();
393: }
394:
395: void
396: audiosbintr(void)
397: {
398: int stat, dummy;
399:
400: stat = mxread(0x82) & 7; /* get irq status */
401: if(stat) {
402: dummy = 0;
403: if(stat & 2) {
404: ilock(&blaster);
405: dummy = inb(blaster.clri16);
406: contindma();
407: iunlock(&blaster);
408: audio.intr = 1;
409: wakeup(&audio.vous);
410: }
411: if(stat & 1) {
412: dummy = inb(blaster.clri8);
413: }
414: if(stat & 4) {
415: dummy = inb(blaster.clri401);
416: }
417: USED(dummy);
418: }
419: }
420:
421: void
422: pcaudiosbintr(Ureg *ureg, void *rock)
423: {
424: USED(ureg, rock);
425: audiosbintr();
426: }
427:
428: void
429: audiodmaintr(void)
430: {
431: }
432:
433: static int
434: anybuf(void *p)
435: {
436: USED(p);
437: return audio.intr;
438: }
439:
440: /*
441: * wait for some output to get
442: * empty buffers back.
443: */
444: static void
445: waitaudio(void)
446: {
447:
448: audio.intr = 0;
449: pokeaudio();
450: tsleep(&audio.vous, anybuf, 0, 10*1000);
451: if(audio.intr == 0) {
452: audio.active = 0;
453: pokeaudio();
454: }
455: }
456:
457: static void
458: sbbufinit(void)
459: {
460: int i;
461: void *p;
462:
463: for(i=0; i<Nbuf; i++) {
464: p = xspanalloc(Bufsize, CACHELINESZ, 64*1024);
465: audio.buf[i].virt = UNCACHED(uchar, p);
466: audio.buf[i].phys = (ulong)PADDR(p);
467: }
468: }
469:
470: static void
471: setempty(void)
472: {
473: int i;
474:
475: ilock(&blaster);
476: audio.empty.first = 0;
477: audio.empty.last = 0;
478: audio.full.first = 0;
479: audio.full.last = 0;
480: audio.current = 0;
481: audio.filling = 0;
482: for(i=0; i<Nbuf; i++)
483: putbuf(&audio.empty, &audio.buf[i]);
484: iunlock(&blaster);
485: }
486:
487: void
488: audioreset(void)
489: {
490: }
491:
492: static void
493: resetlevel(void)
494: {
495: int i;
496:
497: for(i=0; volumes[i].name; i++) {
498: audio.lovol[i] = volumes[i].ilval;
499: audio.rovol[i] = volumes[i].irval;
500: audio.livol[i] = volumes[i].ilval;
501: audio.rivol[i] = volumes[i].irval;
502: }
503: }
504:
505: void
506: audioinit(void)
507: {
508: ISAConf sbconf;
509: int i;
510:
511: sbconf.port = 0x220;
512: sbconf.dma = 5;
513: sbconf.irq = 7;
514: if(isaconfig("audio", 0, &sbconf) == 0)
515: return;
516: if(strcmp(sbconf.type, "sb16") != 0)
517: return;
518:
519: switch(sbconf.port){
520: case 0x220:
521: case 0x240:
522: case 0x260:
523: case 0x280:
524: break;
525: default:
526: print("devaudio: bad sb16 port 0x%x\n", sbconf.port);
527: return;
528: }
529: switch(sbconf.irq){
530: case 2:
531: case 5:
532: case 7:
533: case 10:
534: break;
535: default:
536: print("devaudio: bad sb16 irq %d\n", sbconf.irq);
537: return;
538: }
539:
540: blaster.dma = sbconf.dma;
541:
542: blaster.reset = sbconf.port + 0x6;
543: blaster.read = sbconf.port + 0xa;
544: blaster.write = sbconf.port + 0xc;
545: blaster.wstatus = sbconf.port + 0xc;
546: blaster.rstatus = sbconf.port + 0xe;
547: blaster.mixaddr = sbconf.port + 0x4;
548: blaster.mixdata = sbconf.port + 0x5;
549: blaster.clri8 = sbconf.port + 0xe;
550: blaster.clri16 = sbconf.port + 0xf;
551: blaster.clri401 = sbconf.port + 0x100;
552:
553: seteisadma(blaster.dma, audiodmaintr);
554: setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);
555:
556: audio.amode = Aclosed;
557: resetlevel();
558:
559: outb(blaster.reset, 1);
560: delay(1); /* >3 υs */
561: outb(blaster.reset, 0);
562: delay(1);
563:
564: i = sbread();
565: if(i != 0xaa) {
566: print("sound blaster didnt respond #%.2x\n", i);
567: return;
568: }
569:
570: sbcmd(0xe1); /* get version */
571: audio.major = sbread();
572: audio.minor = sbread();
573:
574: if(audio.major != 4) {
575: print("bad soundblaster model #%.2x #%.2x; not SB 16\n", audio.major, audio.minor);
576: return;
577: }
578: /*
579: * initialize the mixer
580: */
581: mxcmd(0x00, 0); /* Reset mixer */
582: mxvolume();
583:
584: /*
585: * set up irq/dma chans
586: */
587: mxcmd(0x80, /* irq */
588: (sbconf.irq==2)? 1:
589: (sbconf.irq==5)? 2:
590: (sbconf.irq==7)? 4:
591: (sbconf.irq==10)? 8:
592: 0);
593: mxcmd(0x81, 1<<blaster.dma); /* dma */
594: }
595:
596: Chan*
597: audioattach(char *param)
598: {
599: return devattach('A', param);
600: }
601:
602: Chan*
603: audioclone(Chan *c, Chan *nc)
604: {
605: return devclone(c, nc);
606: }
607:
608: int
609: audiowalk(Chan *c, char *name)
610: {
611: return devwalk(c, name, audiodir, NPORT, devgen);
612: }
613:
614: void
615: audiostat(Chan *c, char *db)
616: {
617: devstat(c, db, audiodir, NPORT, devgen);
618: }
619:
620: Chan*
621: audioopen(Chan *c, int omode)
622: {
623: int amode;
624:
625: if(audio.major != 4)
626: error(Emajor);
627:
628: switch(c->qid.path & ~CHDIR) {
629: default:
630: error(Eperm);
631: break;
632:
633: case Qvolume:
634: case Qdir:
635: break;
636:
637: case Qaudio:
638: amode = Awrite;
639: if((omode&7) == OREAD)
640: amode = Aread;
641: qlock(&audio);
642: if(audio.amode != Aclosed){
643: qunlock(&audio);
644: error(Einuse);
645: }
646: if(audio.bufinit == 0) {
647: audio.bufinit = 1;
648: sbbufinit();
649: }
650: audio.amode = amode;
651: setempty();
652: audio.curcount = 0;
653: qunlock(&audio);
654: mxvolume();
655: break;
656: }
657: c = devopen(c, omode, audiodir, NPORT, devgen);
658: c->mode = openmode(omode);
659: c->flag |= COPEN;
660: c->offset = 0;
661:
662: return c;
663: }
664:
665: void
666: audiocreate(Chan *c, char *name, int omode, ulong perm)
667: {
668: USED(c);
669: USED(name);
670: USED(omode);
671: USED(perm);
672:
673: error(Eperm);
674: }
675:
676: void
677: audioclose(Chan *c)
678: {
679:
680: switch(c->qid.path & ~CHDIR) {
681: default:
682: error(Eperm);
683: break;
684:
685: case Qdir:
686: case Qvolume:
687: break;
688:
689: case Qaudio:
690: if(c->flag & COPEN) {
691: qlock(&audio);
692: audio.amode = Aclosed;
693: if(waserror()){
694: qunlock(&audio);
695: nexterror();
696: }
697: while(audio.active)
698: waitaudio();
699: setempty();
700: poperror();
701: qunlock(&audio);
702: }
703: break;
704: }
705: }
706:
707: long
708: audioread(Chan *c, char *a, long n, ulong offset)
709: {
710: int liv, riv, lov, rov;
711: long m, n0;
712: char buf[300];
713: Buf *b;
714: int j;
715:
716: n0 = n;
717: switch(c->qid.path & ~CHDIR) {
718: default:
719: error(Eperm);
720: break;
721:
722: case Qdir:
723: return devdirread(c, a, n, audiodir, NPORT, devgen);
724:
725: case Qaudio:
726: if(audio.amode != Aread)
727: error(Emode);
728: qlock(&audio);
729: if(waserror()){
730: qunlock(&audio);
731: nexterror();
732: }
733: while(n > 0) {
734: b = audio.filling;
735: if(b == 0) {
736: b = getbuf(&audio.full);
737: if(b == 0) {
738: waitaudio();
739: continue;
740: }
741: audio.filling = b;
742: swab(b->virt);
743: audio.curcount = 0;
744: }
745: m = Bufsize-audio.curcount;
746: if(m > n)
747: m = n;
748: memmove(a, b->virt+audio.curcount, m);
749:
750: audio.curcount += m;
751: n -= m;
752: a += m;
753: if(audio.curcount >= Bufsize) {
754: audio.filling = 0;
755: putbuf(&audio.empty, b);
756: }
757: }
758: poperror();
759: qunlock(&audio);
760: break;
761:
762: case Qvolume:
763: j = 0;
764: buf[0] = 0;
765: for(m=0; volumes[m].name; m++){
766: liv = audio.livol[m];
767: riv = audio.rivol[m];
768: lov = audio.lovol[m];
769: rov = audio.rovol[m];
770: j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
771: if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
772: if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
773: j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
774: else{
775: if(volumes[m].flag & Fin)
776: j += snprint(buf+j, sizeof(buf)-j, " in %d", liv);
777: if(volumes[m].flag & Fout)
778: j += snprint(buf+j, sizeof(buf)-j, " out %d", lov);
779: }
780: }else{
781: if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov)
782: j += snprint(buf+j, sizeof(buf)-j, " left %d right %d",
783: liv, riv);
784: else{
785: if(volumes[m].flag & Fin)
786: j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d",
787: liv, riv);
788: if(volumes[m].flag & Fout)
789: j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d",
790: lov, rov);
791: }
792: }
793: j += snprint(buf+j, sizeof(buf)-j, "\n");
794: }
795:
796: return readstr(offset, a, n, buf);
797: }
798: return n0-n;
799: }
800:
801: long
802: audiowrite(Chan *c, char *a, long n, ulong offset)
803: {
804: long m, n0;
805: int i, nf, v, left, right, in, out;
806: char buf[255], *field[Ncmd];
807: Buf *b;
808:
809: USED(offset);
810:
811: n0 = n;
812: switch(c->qid.path & ~CHDIR) {
813: default:
814: error(Eperm);
815: break;
816:
817: case Qvolume:
818: v = Vaudio;
819: left = 1;
820: right = 1;
821: in = 1;
822: out = 1;
823: if(n > sizeof(buf)-1)
824: n = sizeof(buf)-1;
825: memmove(buf, a, n);
826: buf[n] = '\0';
827:
828: nf = getfields(buf, field, Ncmd, " \t\n");
829: for(i = 0; i < nf; i++){
830: /*
831: * a number is volume
832: */
833: if(field[i][0] >= '0' && field[i][0] <= '9') {
834: m = strtoul(field[i], 0, 10);
835: if(left && out)
836: audio.lovol[v] = m;
837: if(left && in)
838: audio.livol[v] = m;
839: if(right && out)
840: audio.rovol[v] = m;
841: if(right && in)
842: audio.rivol[v] = m;
843: mxvolume();
844: goto cont0;
845: }
846:
847: for(m=0; volumes[m].name; m++) {
848: if(strcmp(field[i], volumes[m].name) == 0) {
849: v = m;
850: in = 1;
851: out = 1;
852: left = 1;
853: right = 1;
854: goto cont0;
855: }
856: }
857:
858: if(strcmp(field[i], "reset") == 0) {
859: resetlevel();
860: mxvolume();
861: goto cont0;
862: }
863: if(strcmp(field[i], "in") == 0) {
864: in = 1;
865: out = 0;
866: goto cont0;
867: }
868: if(strcmp(field[i], "out") == 0) {
869: in = 0;
870: out = 1;
871: goto cont0;
872: }
873: if(strcmp(field[i], "left") == 0) {
874: left = 1;
875: right = 0;
876: goto cont0;
877: }
878: if(strcmp(field[i], "right") == 0) {
879: left = 0;
880: right = 1;
881: goto cont0;
882: }
883: error(Evolume);
884: break;
885: cont0:;
886: }
887: break;
888:
889: case Qaudio:
890: if(audio.amode != Awrite)
891: error(Emode);
892: qlock(&audio);
893: if(waserror()){
894: qunlock(&audio);
895: nexterror();
896: }
897: while(n > 0) {
898: b = audio.filling;
899: if(b == 0) {
900: b = getbuf(&audio.empty);
901: if(b == 0) {
902: waitaudio();
903: continue;
904: }
905: audio.filling = b;
906: audio.curcount = 0;
907: }
908:
909: m = Bufsize-audio.curcount;
910: if(m > n)
911: m = n;
912: memmove(b->virt+audio.curcount, a, m);
913:
914: audio.curcount += m;
915: n -= m;
916: a += m;
917: if(audio.curcount >= Bufsize) {
918: audio.filling = 0;
919: swab(b->virt);
920: putbuf(&audio.full, b);
921: }
922: }
923: poperror();
924: qunlock(&audio);
925: break;
926: }
927: return n0 - n;
928: }
929:
930: void
931: audioremove(Chan *c)
932: {
933: USED(c);
934:
935: error(Eperm);
936: }
937:
938: void
939: audiowstat(Chan *c, char *dp)
940: {
941: USED(c);
942: USED(dp);
943:
944: error(Eperm);
945: }
946:
947: static void
948: swab(uchar *a)
949: {
950: ulong *p, *ep, b;
951:
952: if(!SBswab)
953: return;
954: p = (ulong*)a;
955: ep = p + (Bufsize>>2);
956: while(p < ep) {
957: b = *p;
958: b = (b>>24) | (b<<24) |
959: ((b&0xff0000) >> 8) |
960: ((b&0x00ff00) << 8);
961: *p++ = b;
962: }
963: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.