]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/audiosb16.c
merge
[plan9front.git] / sys / src / 9 / pc / audiosb16.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 "io.h"
10 #include "../port/error.h"
11 #include "../port/audioif.h"
12
13 typedef struct  Ring    Ring;
14 typedef struct  Blaster Blaster;
15 typedef struct  Ctlr    Ctlr;
16
17 enum
18 {
19         Vmaster,
20         Vaudio,
21         Vsynth,
22         Vcd,
23         Vline,
24         Vmic,
25         Vspeaker,
26         Vtreb,
27         Vbass,
28         Vigain,
29         Vogain,
30         Vspeed,
31         Nvol,
32
33         Blocksize       = 4096,
34         Blocks          = 65536/Blocksize,
35 };
36
37 struct Ring
38 {
39         uchar   *buf;
40         ulong   nbuf;
41
42         ulong   ri;
43         ulong   wi;
44 };
45
46 struct Blaster
47 {
48         Lock;
49         int     reset;          /* io ports to the sound blaster */
50         int     read;
51         int     write;
52         int     wstatus;
53         int     rstatus;
54         int     mixaddr;
55         int     mixdata;
56         int     clri8;
57         int     clri16;
58         int     clri401;
59
60         void    (*startdma)(Ctlr*);
61         void    (*intr)(Ctlr*);
62 };
63
64 struct Ctlr
65 {
66         Rendez  vous;
67         int     active;         /* boolean dma running */
68         int     major;          /* SB16 major version number (sb 4) */
69         int     minor;          /* SB16 minor version number */
70         ulong   totcount;       /* how many bytes processed since open */
71         vlong   tottime;        /* time at which totcount bytes were processed */
72         Ring    ring;           /* dma ring buffer */
73         Blaster blaster;
74
75         int     lvol[Nvol];
76         int     rvol[Nvol];
77
78         /* for probe */
79         Audio   *adev;
80         ISAConf conf;
81         Ctlr *next;
82 };
83
84 static Volume voltab[] = {
85         [Vmaster] "master", 0x30, 0xff, Stereo, 0,
86         [Vaudio] "audio", 0x32, 0xff, Stereo, 0,
87         [Vsynth] "synth", 0x34, 0xff, Stereo, 0,
88         [Vcd] "cd", 0x36, 0xff, Stereo, 0,
89         [Vline] "line", 0x38, 0xff, Stereo, 0,
90         [Vmic] "mic", 0x3a, 0xff, Mono, 0,
91         [Vspeaker] "speaker", 0x3b, 0xff, Mono, 0,
92         [Vtreb] "treb", 0x44, 0xff, Stereo, 0,
93         [Vbass] "bass", 0x46, 0xff, Stereo, 0,
94         [Vigain] "recgain", 0x3f, 0xff, Stereo, 0,
95         [Vogain] "outgain", 0x41, 0xff, Stereo, 0,
96         [Vspeed] "speed", 0, 0, Absolute, 0,
97         0,
98 };
99
100 static  char    Emajor[]        = "soundblaster not responding/wrong version";
101
102 static long
103 buffered(Ring *r)
104 {
105         ulong ri, wi;
106
107         ri = r->ri;
108         wi = r->wi;
109         if(wi >= ri)
110                 return wi - ri;
111         else
112                 return r->nbuf - (ri - wi);
113 }
114
115 static long
116 available(Ring *r)
117 {
118         long m;
119
120         m = (r->nbuf - 1) - buffered(r);
121         if(m < 0)
122                 m = 0;
123         return m;
124 }
125
126 static long
127 readring(Ring *r, uchar *p, long n)
128 {
129         long n0, m;
130
131         n0 = n;
132         while(n > 0){
133                 if((m = buffered(r)) <= 0)
134                         break;
135                 if(m > n)
136                         m = n;
137                 if(p){
138                         if(r->ri + m > r->nbuf)
139                                 m = r->nbuf - r->ri;
140                         memmove(p, r->buf + r->ri, m);
141                         p += m;
142                 }
143                 r->ri = (r->ri + m) % r->nbuf;
144                 n -= m;
145         }
146         return n0 - n;
147 }
148
149 static long
150 writering(Ring *r, uchar *p, long n)
151 {
152         long n0, m;
153
154         n0 = n;
155         while(n > 0){
156                 if((m = available(r)) <= 0)
157                         break;
158                 if(m > n)
159                         m = n;
160                 if(p){
161                         if(r->wi + m > r->nbuf)
162                                 m = r->nbuf - r->wi;
163                         memmove(r->buf + r->wi, p, m);
164                         p += m;
165                 }
166                 r->wi = (r->wi + m) % r->nbuf;
167                 n -= m;
168         }
169         return n0 - n;
170 }
171
172 static  int
173 sbcmd(Blaster *blaster, int val)
174 {
175         int i, s;
176
177         for(i=1<<16; i!=0; i--) {
178                 s = inb(blaster->wstatus);
179                 if((s & 0x80) == 0) {
180                         outb(blaster->write, val);
181                         return 0;
182                 }
183         }
184         return 1;
185 }
186
187 static  int
188 sbread(Blaster *blaster)
189 {
190         int i, s;
191
192         for(i=1<<16; i!=0; i--) {
193                 s = inb(blaster->rstatus);
194                 if((s & 0x80) != 0) {
195                         return inb(blaster->read);
196                 }
197         }
198         return -1;
199 }
200
201 static int
202 ess1688w(Blaster *blaster, int reg, int val)
203 {
204         if(sbcmd(blaster, reg) || sbcmd(blaster, val))
205                 return 1;
206         return 0;
207 }
208
209 static int
210 ess1688r(Blaster *blaster, int reg)
211 {
212         if(sbcmd(blaster, 0xC0) || sbcmd(blaster, reg))
213                 return -1;
214         return sbread(blaster);
215 }
216
217 static  int
218 mxcmd(Blaster *blaster, int addr, int val)
219 {
220         outb(blaster->mixaddr, addr);
221         outb(blaster->mixdata, val);
222         return 1;
223 }
224
225 static  int
226 mxread(Blaster *blaster, int addr)
227 {
228         int s;
229
230         outb(blaster->mixaddr, addr);
231         s = inb(blaster->mixdata);
232         return s;
233 }
234
235 static int
236 mxsetvol(Audio *adev, int x, int a[2])
237 {
238         Blaster *blaster;
239         Ctlr *ctlr = adev->ctlr;
240         Volume *vol;
241
242         if(x == Vspeed){
243                 ctlr->lvol[x] = ctlr->rvol[x] = a[0];
244                 return 0;
245         }
246
247         vol = voltab+x;
248         blaster = &ctlr->blaster;
249         ilock(blaster);
250         switch(vol->type){
251         case Stereo:
252                 ctlr->rvol[x] = a[1];
253                 mxcmd(blaster, vol->reg+1, a[1]);
254                 /* no break */
255         case Mono:
256                 ctlr->lvol[x] = a[0];
257                 mxcmd(blaster, vol->reg, a[0]);
258         }
259         iunlock(blaster);
260
261         return 0;
262 }
263
264 static int
265 mxgetvol(Audio *adev, int x, int a[2])
266 {
267         Ctlr *ctlr = adev->ctlr;
268
269         a[0] = ctlr->lvol[x];
270         a[1] = ctlr->rvol[x];
271
272         return 0;
273 }
274
275 static  void
276 contindma(Ctlr *ctlr)
277 {
278         Blaster *blaster;
279         Ring *ring;
280
281         blaster = &ctlr->blaster;
282         ring = &ctlr->ring;
283         if(buffered(ring) >= Blocksize){
284                 ring->ri = ring->nbuf - dmacount(ctlr->conf.dma);
285
286                 ctlr->totcount += Blocksize;
287                 ctlr->tottime = todget(nil);
288         }else{
289                 dmaend(ctlr->conf.dma);
290                 sbcmd(blaster, 0xd9);   /* exit at end of count */
291                 sbcmd(blaster, 0xd5);   /* pause */
292                 ctlr->active = 0;
293         }
294         wakeup(&ctlr->vous);
295 }
296
297 /*
298  * cause sb to get an interrupt per buffer.
299  * start first dma
300  */
301 static  void
302 sb16startdma(Ctlr *ctlr)
303 {
304         Blaster *blaster;
305         Ring *ring;
306         long count;
307         int speed;
308
309         blaster = &ctlr->blaster;
310         ring = &ctlr->ring;
311         ilock(blaster);
312         dmaend(ctlr->conf.dma);
313         if(0)
314                 sbcmd(blaster, 0x42);   /* input sampling rate */
315         else
316                 sbcmd(blaster, 0x41);   /* output sampling rate */
317         speed = ctlr->lvol[Vspeed];
318         sbcmd(blaster, speed>>8);
319         sbcmd(blaster, speed);
320
321         if(0)
322                 sbcmd(blaster, 0xbe);   /* A/D, autoinit */
323         else
324                 sbcmd(blaster, 0xb6);   /* D/A, autoinit */
325
326         sbcmd(blaster, 0x30);   /* stereo, signed 16 bit */
327
328         count = (Blocksize>>1) - 1;
329         sbcmd(blaster, count);
330         sbcmd(blaster, count>>8);
331
332         ctlr->active = 1;
333         if(dmasetup(ctlr->conf.dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){
334                 ctlr->active = 0;
335                 print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno);
336         }
337         iunlock(blaster);
338 }
339
340 static int
341 ess1688reset(Blaster *blaster, int ctlrno)
342 {
343         int i;
344
345         outb(blaster->reset, 3);
346         delay(1);       /* >3 υs */
347         outb(blaster->reset, 0);
348         delay(1);
349
350         i = sbread(blaster);
351         if(i != 0xAA) {
352                 print("#A%d: no response %#.2x\n", ctlrno, i);
353                 return 1;
354         }
355
356         if(sbcmd(blaster, 0xC6)){       /* extended mode */
357                 print("#A%d: barf 3\n", ctlrno);
358                 return 1;
359         }
360
361         return 0;
362 }
363
364 static  void
365 ess1688startdma(Ctlr *ctlr)
366 {
367         Blaster *blaster;
368         Ring *ring;
369         ulong count;
370         int speed, x;
371
372         blaster = &ctlr->blaster;
373         ring = &ctlr->ring;
374
375         ilock(blaster);
376         dmaend(ctlr->conf.dma);
377
378         ess1688reset(blaster, ctlr->adev->ctlrno);
379
380         /*
381          * Set the speed.
382          */
383         speed = ctlr->lvol[Vspeed];
384         if(speed < 4000)
385                 speed = 4000;
386         else if(speed > 48000)
387                 speed = 48000;
388         if(speed > 22000)
389                   x = 0x80|(256-(795500+speed/2)/speed);
390         else
391                   x = 128-(397700+speed/2)/speed;
392         ess1688w(blaster, 0xA1, x & 0xFF);
393
394         speed = (speed * 9) / 20;
395         x = 256 - 7160000 / (speed * 82);
396         ess1688w(blaster, 0xA2, x & 0xFF);
397
398         if(0)
399                 ess1688w(blaster, 0xB8, 0x0E);  /* A/D, autoinit */
400         else
401                 ess1688w(blaster, 0xB8, 0x04);  /* D/A, autoinit */
402         x = ess1688r(blaster, 0xA8) & ~0x03;
403         ess1688w(blaster, 0xA8, x|0x01);        /* 2 channels */
404         ess1688w(blaster, 0xB9, 2);     /* demand mode, 4 bytes per request */
405
406         if(1)
407                 ess1688w(blaster, 0xB6, 0);     /* for output */
408
409         ess1688w(blaster, 0xB7, 0x71);
410         ess1688w(blaster, 0xB7, 0xBC);
411
412         x = ess1688r(blaster, 0xB1) & 0x0F;
413         ess1688w(blaster, 0xB1, x|0x50);
414         x = ess1688r(blaster, 0xB2) & 0x0F;
415         ess1688w(blaster, 0xB2, x|0x50);
416
417         if(1)
418                 sbcmd(blaster, 0xD1);   /* speaker on */
419
420         count = -Blocksize;
421         ess1688w(blaster, 0xA4, count & 0xFF);
422         ess1688w(blaster, 0xA5, (count>>8) & 0xFF);
423         x = ess1688r(blaster, 0xB8);
424         ess1688w(blaster, 0xB8, x|0x05);
425
426         ctlr->active = 1;
427         if(dmasetup(ctlr->conf.dma, ring->buf, ring->nbuf, DMAWRITE|DMALOOP) < 0){
428                 ctlr->active = 0;
429                 print("#A%d: dmasetup fail\n", ctlr->adev->ctlrno);
430         }
431         iunlock(blaster);
432 }
433
434 static void
435 sb16intr(Ctlr *ctlr)
436 {
437         Blaster *blaster;
438         int stat;
439
440         blaster = &ctlr->blaster;
441         ilock(blaster);
442         stat = mxread(blaster, 0x82);           /* get irq status */
443         if(stat & 3){
444                 contindma(ctlr);
445                 if(stat & 2)
446                         inb(blaster->clri16);
447                 else if(stat & 1)
448                         inb(blaster->clri8);
449         } else if(stat & 4)
450                 inb(blaster->clri401);
451         iunlock(blaster);
452 }
453
454 static void
455 ess1688intr(Ctlr *ctlr)
456 {
457         Blaster *blaster;
458
459         blaster = &ctlr->blaster;
460         ilock(blaster);
461         contindma(ctlr);
462         inb(blaster->clri8);
463         iunlock(blaster);
464 }
465
466 static void
467 audiointr(Ureg *, void *arg)
468 {
469         Audio *adev;
470         Ctlr *ctlr;
471
472         adev = arg;
473         ctlr = adev->ctlr;
474         if(!ctlr->active){
475                 iprint("#A%d: unexpected %s interrupt\n",
476                         ctlr->adev->ctlrno, ctlr->adev->name);
477                 return;
478         }
479         ctlr->blaster.intr(ctlr);
480 }
481
482 static void
483 setempty(Ctlr *ctlr)
484 {
485         ilock(&ctlr->blaster);
486         ctlr->ring.ri = 0;
487         ctlr->ring.wi = 0;
488         ctlr->totcount = 0;
489         ctlr->tottime = 0LL;
490         iunlock(&ctlr->blaster);
491 }
492
493 static long
494 audiobuffered(Audio *adev)
495 {
496         return buffered(&((Ctlr*)adev->ctlr)->ring);
497 }
498
499 static long
500 audiostatus(Audio *adev, void *a, long n, vlong)
501 {
502         Ctlr *ctlr = adev->ctlr;
503
504         return snprint((char*)a, n, 
505                 "bufsize %6d buffered %6ld "
506                 "offset %10lud time %19lld\n",
507                 Blocksize, buffered(&ctlr->ring),
508                 ctlr->totcount, ctlr->tottime);
509 }
510
511 static int
512 inactive(void *arg)
513 {
514         Ctlr *ctlr = arg;
515
516         return !ctlr->active;
517 }
518
519 static int
520 anybuf(void *arg)
521 {
522         Ctlr *ctlr = arg;
523
524         return available(&ctlr->ring) || inactive(ctlr);
525 }
526
527 static long
528 audiowrite(Audio *adev, void *vp, long n, vlong)
529 {
530         uchar *p, *e;
531         Ctlr *ctlr;
532         Ring *ring;
533
534         p = vp;
535         e = p + n;
536         ctlr = adev->ctlr;
537         ring = &ctlr->ring;
538         while(p < e) {
539                 if((n = writering(ring, p, e - p)) <= 0){
540                         if(!ctlr->active && ring->ri == 0)
541                                 ctlr->blaster.startdma(ctlr);
542                         if(!ctlr->active)
543                                 setempty(ctlr);
544                         else
545                                 sleep(&ctlr->vous, anybuf, ctlr);
546                 }
547                 p += n;
548         }
549         return p - (uchar*)vp;
550 }
551
552 static void
553 audioclose(Audio *adev)
554 {
555         Ctlr *ctlr;
556
557         ctlr = adev->ctlr;
558         sleep(&ctlr->vous, inactive, ctlr);
559         setempty(ctlr);
560 }
561
562 static long
563 audiovolread(Audio *adev, void *a, long n, vlong)
564 {
565         return genaudiovolread(adev, a, n, 0, voltab, mxgetvol, 0);
566 }
567
568 static long
569 audiovolwrite(Audio *adev, void *a, long n, vlong)
570 {
571         Blaster *blaster;
572         Ctlr *ctlr;
573         int source;
574
575         ctlr = adev->ctlr;
576         blaster = &ctlr->blaster;
577
578         n = genaudiovolwrite(adev, a, n, 0, voltab, mxsetvol, 0);
579
580         source = 0;
581         if(ctlr->lvol[Vsynth])
582                 source |= 1<<6;
583         if(ctlr->rvol[Vsynth])
584                 source |= 1<<5;
585         if(ctlr->lvol[Vaudio])
586                 source |= 1<<4;
587         if(ctlr->rvol[Vaudio])
588                 source |= 1<<3;
589         if(ctlr->lvol[Vcd])
590                 source |= 1<<2;
591         if(ctlr->rvol[Vcd])
592                 source |= 1<<1;
593         if(ctlr->lvol[Vmic])
594                 source |= 1<<0;
595
596         ilock(blaster);
597         mxcmd(blaster, 0x3c, source);   /* output switch */
598         mxcmd(blaster, 0x3d, source);   /* input left switch */
599         mxcmd(blaster, 0x3e, source);   /* input right switch */
600         iunlock(blaster);
601
602         return n;
603 }
604
605 static int
606 ess1688(ISAConf* sbconf, Blaster *blaster, int ctlrno)
607 {
608         int i, major, minor;
609
610         /*
611          * Try for ESS1688.
612          */
613         sbcmd(blaster, 0xE7);                   /* get version */
614         major = sbread(blaster);
615         minor = sbread(blaster);
616         if(major != 0x68 || minor != 0x8B){
617                 print("#A%d: model %#.2x %#.2x; not ESS1688 compatible\n",
618                         ctlrno, major, minor);
619                 return -1;
620         }
621
622         ess1688reset(blaster, ctlrno);
623
624         switch(sbconf->irq){
625         case 2:
626         case 9:
627                 i = 0x50|(0<<2);
628                 break;
629         case 5:
630                 i = 0x50|(1<<2);
631                 break;
632         case 7:
633                 i = 0x50|(2<<2);
634                 break;
635         case 10:
636                 i = 0x50|(3<<2);
637                 break;
638         default:
639                 print("#A%d: bad ESS1688 irq %d\n", ctlrno, sbconf->irq);
640                 return 1;
641         }
642         ess1688w(blaster, 0xB1, i);
643
644         switch(sbconf->dma){
645         case 0:
646                 i = 0x50|(1<<2);
647                 break;
648         case 1:
649                 i = 0xF0|(2<<2);
650                 break;
651         case 3:
652                 i = 0x50|(3<<2);
653                 break;
654         default:
655                 print("#A%d: bad ESS1688 dma %lud\n", ctlrno, sbconf->dma);
656                 return 1;
657         }
658         ess1688w(blaster, 0xB2, i);
659
660         ess1688reset(blaster, ctlrno);
661
662         blaster->startdma = ess1688startdma;
663         blaster->intr = ess1688intr;
664
665         return 0;
666 }
667
668 static int
669 audioprobe(Audio *adev)
670 {
671         static int irq[] = {9,5,7,10};
672         static Ctlr *cards = nil;
673
674         Ctlr *ctlr;
675         Blaster *blaster;
676         int i, x;
677
678         /* make a list of audio isa cards if not already done */
679         if(cards == nil){
680                 for(i=0; i<nelem(irq); i++){
681                         ctlr = malloc(sizeof(Ctlr));
682                         memset(ctlr, 0, sizeof(Ctlr));
683                         ctlr->conf.port = 0x220 + i*0x10;
684                         ctlr->conf.irq = irq[i];
685                         ctlr->conf.dma = 0;
686                         if(isaconfig("audio", i, &ctlr->conf) == 0){
687                                 free(ctlr);
688                                 break;
689                         }
690                         ctlr->next = cards;
691                         cards = ctlr;
692                 }
693         }
694
695         /* pick a card */
696         for(ctlr = cards; ctlr; ctlr = ctlr->next){
697                 if(ctlr->conf.type && strcmp(adev->name, ctlr->conf.type) == 0){
698                         ctlr->conf.type = nil;
699                         goto Found;
700                 }
701         }
702         return -1;
703
704 Found:
705         switch(ctlr->conf.port){
706         case 0x220:
707         case 0x240:
708         case 0x260:
709         case 0x280:
710                 break;
711         default:
712                 print("#A%d: bad port %#lux\n", adev->ctlrno, ctlr->conf.port);
713                 return -1;
714         }
715
716         if(ioalloc(ctlr->conf.port, 0x10, 0, "audio") < 0){
717                 print("#A%d: cannot ioalloc range %lux+0x10\n",
718                         adev->ctlrno, ctlr->conf.port);
719                 return -1;
720         }
721         if(ioalloc(ctlr->conf.port+0x100, 1, 0, "audio.mpu401") < 0){
722                 iofree(ctlr->conf.port);
723                 print("#A%d: cannot ioalloc range %lux+0x01\n",
724                         adev->ctlrno, ctlr->conf.port+0x100);
725                 return -1;
726         }
727
728         ctlr->adev = adev;
729         adev->ctlr = ctlr;
730
731         blaster = &ctlr->blaster;
732         blaster->reset = ctlr->conf.port + 0x6;
733         blaster->read = ctlr->conf.port + 0xa;
734         blaster->write = ctlr->conf.port + 0xc;
735         blaster->wstatus = ctlr->conf.port + 0xc;
736         blaster->rstatus = ctlr->conf.port + 0xe;
737         blaster->mixaddr = ctlr->conf.port + 0x4;
738         blaster->mixdata = ctlr->conf.port + 0x5;
739         blaster->clri8 = ctlr->conf.port + 0xe;
740         blaster->clri16 = ctlr->conf.port + 0xf;
741         blaster->clri401 = ctlr->conf.port + 0x100;
742
743         blaster->startdma = sb16startdma;
744         blaster->intr = sb16intr;
745
746         outb(blaster->reset, 1);
747         delay(1);                       /* >3 υs */
748         outb(blaster->reset, 0);
749         delay(1);
750
751         i = sbread(blaster);
752         if(i != 0xaa) {
753                 print("#A%d: no response #%.2x\n", adev->ctlrno, i);
754 Errout:
755                 iofree(ctlr->conf.port);
756                 iofree(ctlr->conf.port+0x100);
757                 return -1;
758         }
759
760         sbcmd(blaster, 0xe1);                   /* get version */
761         ctlr->major = sbread(blaster);
762         ctlr->minor = sbread(blaster);
763
764         if(ctlr->major != 4) {
765                 if(ctlr->major != 3 || ctlr->minor != 1 ||
766                         ess1688(&ctlr->conf, blaster, adev->ctlrno)){
767                         print("#A%d: model %#.2x %#.2x; not SB 16 compatible\n",
768                                 adev->ctlrno, ctlr->major, ctlr->minor);
769                         goto Errout;
770                 }
771                 ctlr->major = 4;
772         }
773
774         /*
775          * initialize the mixer
776          */
777         mxcmd(blaster, 0x00, 0);                        /* Reset mixer */
778
779         for(i=0; i<Nvol; i++){
780                 int a[2];
781
782                 a[0] = 0;
783                 a[1] = 0;
784                 mxsetvol(adev, i, a);
785         }
786
787         /* set irq */
788         for(i=0; i<nelem(irq); i++){
789                 if(ctlr->conf.irq == irq[i]){
790                         mxcmd(blaster, 0x80, 1<<i);
791                         break;
792                 }
793         }
794         x = mxread(blaster, 0x80);
795         for(i=0; i<nelem(irq); i++){
796                 if(x & (1<<i)){
797                         ctlr->conf.irq = irq[i];
798                         break;
799                 }
800         }
801
802         for(;;){
803                 /* set 16bit dma */
804                 if(ctlr->conf.dma>=5 && ctlr->conf.dma<=7){
805                         x = mxread(blaster, 0x81);
806                         mxcmd(blaster, 0x81, (1<<ctlr->conf.dma) & 0xF0 | (x & 0x0F));
807                 }
808                 x = mxread(blaster, 0x81);
809                 for(i=5; i<=7; i++){
810                         if(x & (1<<i)){
811                                 ctlr->conf.dma = i;
812                                 break;
813                         }
814                 }
815                 if(ctlr->conf.dma>=5)
816                         break;
817                 ctlr->conf.dma = 7;
818         }
819
820         print("#A%d: %s port 0x%04lux irq %d dma %lud\n", adev->ctlrno, adev->name,
821                 ctlr->conf.port, ctlr->conf.irq, ctlr->conf.dma);
822
823         ctlr->ring.nbuf = Blocks*Blocksize;
824         if(dmainit(ctlr->conf.dma, ctlr->ring.nbuf))
825                 goto Errout;
826         ctlr->ring.buf = dmabva(ctlr->conf.dma);
827         print("#A%d: %s dma buffer %p-%p\n", adev->ctlrno, adev->name,
828                 ctlr->ring.buf, ctlr->ring.buf+ctlr->ring.nbuf);
829
830         setempty(ctlr);
831
832         adev->write = audiowrite;
833         adev->close = audioclose;
834         adev->volread = audiovolread;
835         adev->volwrite = audiovolwrite;
836         adev->status = audiostatus;
837         adev->buffered = audiobuffered;
838
839         intrenable(ctlr->conf.irq, audiointr, adev, BUSUNKNOWN, adev->name);
840
841         return 0;
842 }
843
844 void
845 audiosb16link(void)
846 {
847         addaudiocard("sb16", audioprobe);
848         addaudiocard("ess1688", audioprobe);
849 }