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