wss.c revision 1.6 1 /* $NetBSD: wss.c,v 1.6 1995/07/07 02:15:12 brezak Exp $ */
2
3 /*
4 * Copyright (c) 1994 John Brezak
5 * Copyright (c) 1991-1993 Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the Computer Systems
19 * Engineering Group at Lawrence Berkeley Laboratory.
20 * 4. Neither the name of the University nor of the Laboratory may be used
21 * to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/syslog.h>
43 #include <sys/device.h>
44 #include <sys/proc.h>
45 #include <sys/buf.h>
46
47 #include <machine/cpu.h>
48 #include <machine/pio.h>
49
50 #include <sys/audioio.h>
51 #include <dev/audio_if.h>
52
53 #include <dev/isa/isavar.h>
54 #include <dev/isa/isadmavar.h>
55
56 #include <dev/ic/ad1848reg.h>
57 #include <dev/isa/ad1848var.h>
58 #include <dev/isa/wssreg.h>
59
60 /*
61 * Mixer devices
62 */
63 #define WSS_MIC_IN_LVL 0
64 #define WSS_LINE_IN_LVL 1
65 #define WSS_DAC_LVL 2
66 #define WSS_REC_LVL 3
67 #define WSS_MON_LVL 4
68 #define WSS_MIC_IN_MUTE 5
69 #define WSS_LINE_IN_MUTE 6
70 #define WSS_DAC_MUTE 7
71
72 #define WSS_RECORD_SOURCE 8
73
74 /* Classes */
75 #define WSS_INPUT_CLASS 9
76 #define WSS_RECORD_CLASS 10
77 #define WSS_MONITOR_CLASS 11
78
79 #ifdef AUDIO_DEBUG
80 #define DPRINTF(x) if (wssdebug) printf x
81 int wssdebug = 0;
82 #else
83 #define DPRINTF(x)
84 #endif
85
86 struct wss_softc {
87 struct device sc_dev; /* base device */
88 struct isadev sc_id; /* ISA device */
89 void *sc_ih; /* interrupt vectoring */
90
91 struct ad1848_softc sc_ad1848;
92 #define wss_irq sc_ad1848.sc_irq
93 #define wss_drq sc_ad1848.sc_drq
94
95 int mic_mute, cd_mute, dac_mute;
96 };
97
98 struct audio_device wss_device = {
99 "wss,ad1848",
100 "",
101 "WSS"
102 };
103
104 int wssprobe();
105 void wssattach();
106 int wssopen __P((dev_t, int));
107
108 int wss_getdev __P((void *, struct audio_device *));
109 int wss_setfd __P((void *, int));
110
111 int wss_set_out_port __P((void *, int));
112 int wss_get_out_port __P((void *));
113 int wss_set_in_port __P((void *, int));
114 int wss_get_in_port __P((void *));
115 int wss_mixer_set_port __P((void *, mixer_ctrl_t *));
116 int wss_mixer_get_port __P((void *, mixer_ctrl_t *));
117 int wss_query_devinfo __P((void *, mixer_devinfo_t *));
118
119 /*
120 * Define our interface to the higher level audio driver.
121 */
122
123 struct audio_hw_if wss_hw_if = {
124 wssopen,
125 ad1848_close,
126 NULL,
127 ad1848_set_in_sr,
128 ad1848_get_in_sr,
129 ad1848_set_out_sr,
130 ad1848_get_out_sr,
131 ad1848_query_encoding,
132 ad1848_set_encoding,
133 ad1848_get_encoding,
134 ad1848_set_precision,
135 ad1848_get_precision,
136 ad1848_set_channels,
137 ad1848_get_channels,
138 ad1848_round_blocksize,
139 wss_set_out_port,
140 wss_get_out_port,
141 wss_set_in_port,
142 wss_get_in_port,
143 ad1848_commit_settings,
144 ad1848_get_silence,
145 NULL,
146 NULL,
147 ad1848_dma_output,
148 ad1848_dma_input,
149 ad1848_halt_out_dma,
150 ad1848_halt_in_dma,
151 ad1848_cont_out_dma,
152 ad1848_cont_in_dma,
153 NULL,
154 wss_getdev,
155 wss_setfd,
156 wss_mixer_set_port,
157 wss_mixer_get_port,
158 wss_query_devinfo,
159 0, /* not full-duplex */
160 0
161 };
162
163 #ifndef NEWCONFIG
164 #define at_dma(flags, ptr, cc, chan) isa_dmastart(flags, ptr, cc, chan)
165 #endif
166
167 struct cfdriver wsscd = {
168 NULL, "wss", wssprobe, wssattach, DV_DULL, sizeof(struct wss_softc)
169 };
170
171 /*
172 * Probe for the Microsoft Sound System hardware.
173 */
174 int
175 wssprobe(parent, self, aux)
176 struct device *parent, *self;
177 void *aux;
178 {
179 register struct wss_softc *sc = (void *)self;
180 register struct isa_attach_args *ia = aux;
181 register u_short iobase = ia->ia_iobase;
182 static u_char interrupt_bits[12] = {
183 -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
184 };
185 static u_char dma_bits[4] = {1, 2, 0, 3};
186
187 if (!WSS_BASE_VALID(ia->ia_iobase)) {
188 printf("wss: configured iobase %x invalid\n", ia->ia_iobase);
189 return 0;
190 }
191
192 sc->sc_ad1848.sc_iobase = iobase;
193
194 /* Is there an ad1848 chip at the WSS iobase ? */
195 if (ad1848_probe(&sc->sc_ad1848) == 0)
196 return 0;
197
198 ia->ia_iosize = WSS_NPORT;
199
200 /* Setup WSS interrupt and DMA */
201 if (!WSS_DRQ_VALID(ia->ia_drq)) {
202 printf("wss: configured dma chan %d invalid\n", ia->ia_drq);
203 return 0;
204 }
205 sc->wss_drq = ia->ia_drq;
206
207 #ifdef NEWCONFIG
208 /*
209 * If the IRQ wasn't compiled in, auto-detect it.
210 */
211 if (ia->ia_irq == IRQUNK) {
212 ia->ia_irq = isa_discoverintr(ad1848_forceintr, &sc->sc_ad1848);
213 if (!WSS_IRQ_VALID(ia->ia_irq)) {
214 printf("wss: couldn't auto-detect interrupt\n");
215 return 0;
216 }
217 }
218 else
219 #endif
220 if (!WSS_IRQ_VALID(ia->ia_irq)) {
221 printf("wss: configured interrupt %d invalid\n", ia->ia_irq);
222 return 0;
223 }
224
225 sc->wss_irq = ia->ia_irq;
226
227 outb(iobase+WSS_CONFIG,
228 (interrupt_bits[ia->ia_irq] | dma_bits[ia->ia_drq]));
229
230 return 1;
231 }
232
233 /*
234 * Attach hardware to driver, attach hardware driver to audio
235 * pseudo-device driver .
236 */
237 void
238 wssattach(parent, self, aux)
239 struct device *parent, *self;
240 void *aux;
241 {
242 register struct wss_softc *sc = (struct wss_softc *)self;
243 struct isa_attach_args *ia = (struct isa_attach_args *)aux;
244 register u_short iobase = ia->ia_iobase;
245 int err;
246
247 sc->sc_ad1848.sc_recdrq = ia->ia_drq;
248
249 #ifdef NEWCONFIG
250 isa_establish(&sc->sc_id, &sc->sc_dev);
251 #endif
252 sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_EDGE, ISA_IPL_AUDIO,
253 ad1848_intr, &sc->sc_ad1848);
254
255 ad1848_attach(&sc->sc_ad1848);
256
257 printf(" (vers %d)", inb(iobase+WSS_STATUS) & WSS_VERSMASK);
258 printf("\n");
259
260 sc->sc_ad1848.parent = sc;
261
262 if ((err = audio_hardware_attach(&wss_hw_if, &sc->sc_ad1848)) != 0)
263 printf("wss: could not attach to audio pseudo-device driver (%d)\n", err);
264 }
265
266 static int
267 wss_to_vol(cp, vol)
268 mixer_ctrl_t *cp;
269 struct ad1848_volume *vol;
270 {
271 if (cp->un.value.num_channels == 1) {
272 vol->left = vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
273 return(1);
274 }
275 else if (cp->un.value.num_channels == 2) {
276 vol->left = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
277 vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
278 return(1);
279 }
280 return(0);
281 }
282
283 static int
284 wss_from_vol(cp, vol)
285 mixer_ctrl_t *cp;
286 struct ad1848_volume *vol;
287 {
288 if (cp->un.value.num_channels == 1) {
289 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol->left;
290 return(1);
291 }
292 else if (cp->un.value.num_channels == 2) {
293 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = vol->left;
294 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = vol->right;
295 return(1);
296 }
297 return(0);
298 }
299
300 int
301 wssopen(dev, flags)
302 dev_t dev;
303 int flags;
304 {
305 struct wss_softc *sc;
306 int unit = AUDIOUNIT(dev);
307
308 if (unit >= wsscd.cd_ndevs)
309 return ENODEV;
310
311 sc = wsscd.cd_devs[unit];
312 if (!sc)
313 return ENXIO;
314
315 return ad1848_open(&sc->sc_ad1848, dev, flags);
316 }
317
318 int
319 wss_getdev(addr, retp)
320 void *addr;
321 struct audio_device *retp;
322 {
323 *retp = wss_device;
324 return 0;
325 }
326
327 int
328 wss_setfd(addr, flag)
329 void *addr;
330 int flag;
331 {
332 /* Can't do full-duplex */
333 return(ENOTTY);
334 }
335
336
337 int
338 wss_set_out_port(addr, port)
339 void *addr;
340 int port;
341 {
342 DPRINTF(("wss_set_out_port:\n"));
343 return(EINVAL);
344 }
345
346 int
347 wss_get_out_port(addr)
348 void *addr;
349 {
350 DPRINTF(("wss_get_out_port:\n"));
351 return(EINVAL);
352 }
353
354 int
355 wss_set_in_port(addr, port)
356 void *addr;
357 int port;
358 {
359 register struct ad1848_softc *ac = addr;
360 register struct wss_softc *sc = ac->parent;
361
362 DPRINTF(("wss_set_in_port: %d\n", port));
363
364 switch(port) {
365 case WSS_MIC_IN_LVL:
366 port = MIC_IN_PORT;
367 break;
368 case WSS_LINE_IN_LVL:
369 port = LINE_IN_PORT;
370 break;
371 case WSS_DAC_LVL:
372 port = DAC_IN_PORT;
373 break;
374 default:
375 return(EINVAL);
376 /*NOTREACHED*/
377 }
378
379 return(ad1848_set_rec_port(ac, port));
380 }
381
382 int
383 wss_get_in_port(addr)
384 void *addr;
385 {
386 register struct ad1848_softc *ac = addr;
387 register struct wss_softc *sc = ac->parent;
388 int port = WSS_MIC_IN_LVL;
389
390 switch(ad1848_get_rec_port(ac)) {
391 case MIC_IN_PORT:
392 port = WSS_MIC_IN_LVL;
393 break;
394 case LINE_IN_PORT:
395 port = WSS_LINE_IN_LVL;
396 break;
397 case DAC_IN_PORT:
398 port = WSS_DAC_LVL;
399 break;
400 }
401
402 DPRINTF(("wss_get_in_port: %d\n", port));
403
404 return(port);
405 }
406
407 int
408 wss_mixer_set_port(addr, cp)
409 void *addr;
410 mixer_ctrl_t *cp;
411 {
412 register struct ad1848_softc *ac = addr;
413 register struct wss_softc *sc = ac->parent;
414 struct ad1848_volume vol;
415 u_char eq;
416 int error = EINVAL;
417
418 DPRINTF(("wss_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
419
420 switch (cp->dev) {
421 case WSS_MIC_IN_LVL: /* Microphone */
422 if (cp->type == AUDIO_MIXER_VALUE) {
423 if (wss_to_vol(cp, &vol))
424 error = ad1848_set_aux2_gain(ac, &vol);
425 }
426 break;
427
428 case WSS_MIC_IN_MUTE: /* Microphone */
429 if (cp->type == AUDIO_MIXER_ENUM) {
430 sc->mic_mute = cp->un.ord;
431 DPRINTF(("mic mute %d\n", cp->un.ord));
432 error = 0;
433 }
434 break;
435
436 case WSS_LINE_IN_LVL: /* linein/CD */
437 if (cp->type == AUDIO_MIXER_VALUE) {
438 if (wss_to_vol(cp, &vol))
439 error = ad1848_set_aux1_gain(ac, &vol);
440 }
441 break;
442
443 case WSS_LINE_IN_MUTE: /* linein/CD */
444 if (cp->type == AUDIO_MIXER_ENUM) {
445 sc->cd_mute = cp->un.ord;
446 DPRINTF(("CD mute %d\n", cp->un.ord));
447 error = 0;
448 }
449 break;
450
451 case WSS_DAC_LVL: /* dac out */
452 if (cp->type == AUDIO_MIXER_VALUE) {
453 if (wss_to_vol(cp, &vol))
454 error = ad1848_set_out_gain(ac, &vol);
455 }
456 break;
457
458 case WSS_DAC_MUTE: /* dac out */
459 if (cp->type == AUDIO_MIXER_ENUM) {
460 sc->dac_mute = cp->un.ord;
461 DPRINTF(("DAC mute %d\n", cp->un.ord));
462 error = 0;
463 }
464 break;
465
466 case WSS_REC_LVL: /* record level */
467 if (cp->type == AUDIO_MIXER_VALUE) {
468 if (wss_to_vol(cp, &vol))
469 error = ad1848_set_rec_gain(ac, &vol);
470 }
471 break;
472
473 case WSS_RECORD_SOURCE:
474 if (cp->type == AUDIO_MIXER_ENUM) {
475 error = ad1848_set_rec_port(ac, cp->un.ord);
476 }
477 break;
478
479 case WSS_MON_LVL:
480 if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
481 vol.left = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
482 error = ad1848_set_mon_gain(ac, &vol);
483 }
484 break;
485
486 default:
487 return ENXIO;
488 /*NOTREACHED*/
489 }
490
491 return 0;
492 }
493
494 int
495 wss_mixer_get_port(addr, cp)
496 void *addr;
497 mixer_ctrl_t *cp;
498 {
499 register struct ad1848_softc *ac = addr;
500 register struct wss_softc *sc = ac->parent;
501 struct ad1848_volume vol;
502 u_char eq;
503 int error = EINVAL;
504
505 DPRINTF(("wss_mixer_get_port: port=%d\n", cp->dev));
506
507 switch (cp->dev) {
508 case WSS_MIC_IN_LVL: /* Microphone */
509 if (cp->type == AUDIO_MIXER_VALUE) {
510 error = ad1848_get_aux2_gain(ac, &vol);
511 if (!error)
512 wss_from_vol(cp, &vol);
513 }
514 break;
515
516 case WSS_MIC_IN_MUTE:
517 if (cp->type == AUDIO_MIXER_ENUM) {
518 cp->un.ord = sc->mic_mute;
519 error = 0;
520 }
521 break;
522
523 case WSS_LINE_IN_LVL: /* linein/CD */
524 if (cp->type == AUDIO_MIXER_VALUE) {
525 error = ad1848_get_aux1_gain(ac, &vol);
526 if (!error)
527 wss_from_vol(cp, &vol);
528 }
529 break;
530
531 case WSS_LINE_IN_MUTE:
532 if (cp->type == AUDIO_MIXER_ENUM) {
533 cp->un.ord = sc->cd_mute;
534 error = 0;
535 }
536 break;
537
538 case WSS_DAC_LVL: /* dac out */
539 if (cp->type == AUDIO_MIXER_VALUE) {
540 error = ad1848_get_out_gain(ac, &vol);
541 if (!error)
542 wss_from_vol(cp, &vol);
543 }
544 break;
545
546 case WSS_DAC_MUTE:
547 if (cp->type == AUDIO_MIXER_ENUM) {
548 cp->un.ord = sc->dac_mute;
549 error = 0;
550 }
551 break;
552
553 case WSS_REC_LVL: /* record level */
554 if (cp->type == AUDIO_MIXER_VALUE) {
555 error = ad1848_get_rec_gain(ac, &vol);
556 if (!error)
557 wss_from_vol(cp, &vol);
558 }
559 break;
560
561 case WSS_RECORD_SOURCE:
562 if (cp->type == AUDIO_MIXER_ENUM) {
563 cp->un.ord = ad1848_get_rec_port(ac);
564 error = 0;
565 }
566 break;
567
568 case WSS_MON_LVL: /* monitor level */
569 if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
570 error = ad1848_get_mon_gain(ac, &vol);
571 if (!error)
572 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol.left;
573 }
574 break;
575
576 default:
577 error = ENXIO;
578 break;
579 }
580
581 return(error);
582 }
583
584 int
585 wss_query_devinfo(addr, dip)
586 void *addr;
587 register mixer_devinfo_t *dip;
588 {
589 register struct ad1848_softc *ac = addr;
590 register struct wss_softc *sc = ac->parent;
591
592 DPRINTF(("wss_query_devinfo: index=%d\n", dip->index));
593
594 switch(dip->index) {
595 case WSS_MIC_IN_LVL: /* Microphone */
596 dip->type = AUDIO_MIXER_VALUE;
597 dip->mixer_class = WSS_INPUT_CLASS;
598 dip->prev = AUDIO_MIXER_LAST;
599 dip->next = WSS_MIC_IN_MUTE;
600 strcpy(dip->label.name, AudioNmicrophone);
601 dip->un.v.num_channels = 2;
602 strcpy(dip->un.v.units.name, AudioNvolume);
603 break;
604
605 case WSS_LINE_IN_LVL: /* line/CD */
606 dip->type = AUDIO_MIXER_VALUE;
607 dip->mixer_class = WSS_INPUT_CLASS;
608 dip->prev = AUDIO_MIXER_LAST;
609 dip->next = WSS_LINE_IN_MUTE;
610 strcpy(dip->label.name, AudioNcd);
611 dip->un.v.num_channels = 2;
612 strcpy(dip->un.v.units.name, AudioNvolume);
613 break;
614
615 case WSS_DAC_LVL: /* dacout */
616 dip->type = AUDIO_MIXER_VALUE;
617 dip->mixer_class = WSS_INPUT_CLASS;
618 dip->prev = AUDIO_MIXER_LAST;
619 dip->next = WSS_DAC_MUTE;
620 strcpy(dip->label.name, AudioNdac);
621 dip->un.v.num_channels = 2;
622 strcpy(dip->un.v.units.name, AudioNvolume);
623 break;
624
625 case WSS_REC_LVL: /* record level */
626 dip->type = AUDIO_MIXER_VALUE;
627 dip->mixer_class = WSS_RECORD_CLASS;
628 dip->prev = AUDIO_MIXER_LAST;
629 dip->next = WSS_RECORD_SOURCE;
630 strcpy(dip->label.name, AudioNrecord);
631 dip->un.v.num_channels = 2;
632 strcpy(dip->un.v.units.name, AudioNvolume);
633 break;
634
635 case WSS_MON_LVL: /* monitor level */
636 dip->type = AUDIO_MIXER_VALUE;
637 dip->mixer_class = WSS_MONITOR_CLASS;
638 dip->next = dip->prev = AUDIO_MIXER_LAST;
639 strcpy(dip->label.name, AudioNmonitor);
640 dip->un.v.num_channels = 1;
641 strcpy(dip->un.v.units.name, AudioNvolume);
642 break;
643
644 case WSS_INPUT_CLASS: /* input class descriptor */
645 dip->type = AUDIO_MIXER_CLASS;
646 dip->mixer_class = WSS_INPUT_CLASS;
647 dip->next = dip->prev = AUDIO_MIXER_LAST;
648 strcpy(dip->label.name, AudioCInputs);
649 break;
650
651 case WSS_MONITOR_CLASS: /* monitor class descriptor */
652 dip->type = AUDIO_MIXER_CLASS;
653 dip->mixer_class = WSS_MONITOR_CLASS;
654 dip->next = dip->prev = AUDIO_MIXER_LAST;
655 strcpy(dip->label.name, AudioNmonitor);
656 break;
657
658 case WSS_RECORD_CLASS: /* record source class */
659 dip->type = AUDIO_MIXER_CLASS;
660 dip->mixer_class = WSS_RECORD_CLASS;
661 dip->next = dip->prev = AUDIO_MIXER_LAST;
662 strcpy(dip->label.name, AudioNrecord);
663 break;
664
665 case WSS_MIC_IN_MUTE:
666 dip->mixer_class = WSS_INPUT_CLASS;
667 dip->type = AUDIO_MIXER_ENUM;
668 dip->prev = WSS_MIC_IN_LVL;
669 dip->next = AUDIO_MIXER_LAST;
670 goto mute;
671
672 case WSS_LINE_IN_MUTE:
673 dip->mixer_class = WSS_INPUT_CLASS;
674 dip->type = AUDIO_MIXER_ENUM;
675 dip->prev = WSS_LINE_IN_LVL;
676 dip->next = AUDIO_MIXER_LAST;
677 goto mute;
678
679 case WSS_DAC_MUTE:
680 dip->mixer_class = WSS_INPUT_CLASS;
681 dip->type = AUDIO_MIXER_ENUM;
682 dip->prev = WSS_DAC_LVL;
683 dip->next = AUDIO_MIXER_LAST;
684 mute:
685 strcpy(dip->label.name, AudioNmute);
686 dip->un.e.num_mem = 2;
687 strcpy(dip->un.e.member[0].label.name, AudioNoff);
688 dip->un.e.member[0].ord = 0;
689 strcpy(dip->un.e.member[1].label.name, AudioNon);
690 dip->un.e.member[1].ord = 1;
691 break;
692
693 case WSS_RECORD_SOURCE:
694 dip->mixer_class = WSS_RECORD_CLASS;
695 dip->type = AUDIO_MIXER_ENUM;
696 dip->prev = WSS_REC_LVL;
697 dip->next = AUDIO_MIXER_LAST;
698 strcpy(dip->label.name, AudioNsource);
699 dip->un.e.num_mem = 3;
700 strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
701 dip->un.e.member[0].ord = WSS_MIC_IN_LVL;
702 strcpy(dip->un.e.member[1].label.name, AudioNcd);
703 dip->un.e.member[1].ord = WSS_LINE_IN_LVL;
704 strcpy(dip->un.e.member[2].label.name, AudioNdac);
705 dip->un.e.member[2].ord = WSS_DAC_LVL;
706 break;
707
708 default:
709 return ENXIO;
710 /*NOTREACHED*/
711 }
712 DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
713
714 return 0;
715 }
716