diff --git a/sys/dev/usb/uaudio.c b/sys/dev/usb/uaudio.c index a3020c6ebace..efa4ae1097dd 100644 --- a/sys/dev/usb/uaudio.c +++ b/sys/dev/usb/uaudio.c @@ -1,4 +1,4 @@ -/* $NetBSD: uaudio.c,v 1.81 2004/10/20 13:48:32 kent Exp $ */ +/* $NetBSD: uaudio.c,v 1.82 2004/10/21 12:41:07 kent Exp $ */ /* * Copyright (c) 1999 The NetBSD Foundation, Inc. @@ -44,7 +44,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.81 2004/10/20 13:48:32 kent Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.82 2004/10/21 12:41:07 kent Exp $"); #include #include @@ -192,10 +192,40 @@ struct uaudio_softc { char sc_dying; }; -#define UAC_OUTPUT 0 -#define UAC_INPUT 1 -#define UAC_EQUAL 2 -#define UAC_NCLASSES 3 +struct terminal_list { + int size; + uint16_t terminals[1]; +}; +#define TERMINAL_LIST_SIZE(N) (offsetof(struct terminal_list, terminals) \ + + sizeof(uint16_t) * (N)) + +struct io_terminal { + union { + const usb_descriptor_t *desc; + const struct usb_audio_input_terminal *it; + const struct usb_audio_output_terminal *ot; + const struct usb_audio_mixer_unit *mu; + const struct usb_audio_selector_unit *su; + const struct usb_audio_feature_unit *fu; + const struct usb_audio_processing_unit *pu; + const struct usb_audio_extension_unit *eu; + } d; + int inputs_size; + struct terminal_list **inputs; /* list of source input terminals */ + struct terminal_list *output; /* list of destination output terminals */ + int direct; /* direcly connected to an output terminal */ +}; + +#define UAC_OUTPUT 0 +#define UAC_INPUT 1 +#define UAC_EQUAL 2 +#define UAC_RECORD 3 +#define UAC_NCLASSES 4 +#ifdef UAUDIO_DEBUG +Static const char *uac_names[] = { + AudioCoutputs, AudioCinputs, AudioCequalization, AudioCrecord, +}; +#endif Static usbd_status uaudio_identify_ac(struct uaudio_softc *, const usb_config_descriptor_t *); @@ -206,33 +236,42 @@ Static usbd_status uaudio_process_as(struct uaudio_softc *, const usb_interface_descriptor_t *); Static void uaudio_add_alt(struct uaudio_softc *, const struct as_info *); -Static void uaudio_mixer_alias_ctl(struct uaudio_softc *, - struct mixerctl *, const char *); Static const usb_interface_descriptor_t *uaudio_find_iface (const char *, int, int *, int); Static void uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *); Static char *uaudio_id_name(struct uaudio_softc *, - const usb_descriptor_t **, int); + const struct io_terminal *, int); Static struct usb_audio_cluster uaudio_get_cluster(int, - const usb_descriptor_t **); + const struct io_terminal *); Static void uaudio_add_input(struct uaudio_softc *, - const usb_descriptor_t *, const usb_descriptor_t **); + const struct io_terminal *, int); Static void uaudio_add_output(struct uaudio_softc *, - const usb_descriptor_t *, const usb_descriptor_t **); + const struct io_terminal *, int); Static void uaudio_add_mixer(struct uaudio_softc *, - const usb_descriptor_t *, const usb_descriptor_t **); + const struct io_terminal *, int); Static void uaudio_add_selector(struct uaudio_softc *, - const usb_descriptor_t *, const usb_descriptor_t **); + const struct io_terminal *, int); +#ifdef UAUDIO_DEBUG +Static const char *uaudio_get_terminal_name(int); +#endif +Static int uaudio_determine_class(const struct io_terminal *, + struct mixerctl *); +Static const char *uaudio_feature_name(const struct io_terminal *, + struct mixerctl *); Static void uaudio_add_feature(struct uaudio_softc *, - const usb_descriptor_t *, const usb_descriptor_t **); + const struct io_terminal *, int); Static void uaudio_add_processing_updown(struct uaudio_softc *, - const usb_descriptor_t *, const usb_descriptor_t **); + const struct io_terminal *, int); Static void uaudio_add_processing(struct uaudio_softc *, - const usb_descriptor_t *, const usb_descriptor_t **); + const struct io_terminal *, int); Static void uaudio_add_extension(struct uaudio_softc *, - const usb_descriptor_t *, const usb_descriptor_t **); + const struct io_terminal *, int); +Static struct terminal_list *uaudio_merge_terminal_list + (const struct io_terminal *); +Static struct terminal_list *uaudio_io_terminaltype(int, + struct io_terminal *, int); Static usbd_status uaudio_identify(struct uaudio_softc *, const usb_config_descriptor_t *); @@ -566,6 +605,12 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc) malloc(len, M_USBDEV, M_NOWAIT) : realloc(sc->sc_ctls, len, M_USBDEV, M_NOWAIT); + if (mc->class < UAC_NCLASSES) { + DPRINTF(("%s: adding %s.%s\n", + __func__, uac_names[mc->class], mc->ctlname)); + } else { + DPRINTF(("%s: adding %s\n", __func__, mc->ctlname)); + } if (nmc == NULL) { printf("uaudio_mixer_add_ctl: no memory\n"); return; @@ -614,17 +659,8 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc) #endif } -void -uaudio_mixer_alias_ctl(struct uaudio_softc *sc, struct mixerctl *mc, - const char *name) -{ - /* XXX mark as alias? */ - strlcpy(mc->ctlname, name, sizeof(mc->ctlname)); - uaudio_mixer_add_ctl(sc, mc); -} - char * -uaudio_id_name(struct uaudio_softc *sc, const usb_descriptor_t **dps, int id) +uaudio_id_name(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { static char buf[32]; snprintf(buf, sizeof(buf), "i%d", id); @@ -632,57 +668,43 @@ uaudio_id_name(struct uaudio_softc *sc, const usb_descriptor_t **dps, int id) } struct usb_audio_cluster -uaudio_get_cluster(int id, const usb_descriptor_t **dps) +uaudio_get_cluster(int id, const struct io_terminal *iot) { struct usb_audio_cluster r; const usb_descriptor_t *dp; int i; for (i = 0; i < 25; i++) { /* avoid infinite loops */ - dp = dps[id]; + dp = iot[id].d.desc; if (dp == 0) goto bad; switch (dp->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: -#define p ((const struct usb_audio_input_terminal *)dp) - r.bNrChannels = p->bNrChannels; - USETW(r.wChannelConfig, UGETW(p->wChannelConfig)); - r.iChannelNames = p->iChannelNames; -#undef p + r.bNrChannels = iot[id].d.it->bNrChannels; + USETW(r.wChannelConfig, UGETW(iot[id].d.it->wChannelConfig)); + r.iChannelNames = iot[id].d.it->iChannelNames; return (r); case UDESCSUB_AC_OUTPUT: -#define p ((const struct usb_audio_output_terminal *)dp) - id = p->bSourceId; -#undef p + id = iot[id].d.ot->bSourceId; break; case UDESCSUB_AC_MIXER: -#define p ((const struct usb_audio_mixer_unit *)dp) r = *(struct usb_audio_cluster *) - &p->baSourceId[p->bNrInPins]; -#undef p + &iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins]; return (r); case UDESCSUB_AC_SELECTOR: /* XXX This is not really right */ -#define p ((const struct usb_audio_selector_unit *)dp) - id = p->baSourceId[0]; -#undef p + id = iot[id].d.su->baSourceId[0]; break; case UDESCSUB_AC_FEATURE: -#define p ((const struct usb_audio_feature_unit *)dp) - id = p->bSourceId; -#undef p + id = iot[id].d.fu->bSourceId; break; case UDESCSUB_AC_PROCESSING: -#define p ((const struct usb_audio_processing_unit *)dp) r = *(struct usb_audio_cluster *) - &p->baSourceId[p->bNrInPins]; -#undef p + &iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins]; return (r); case UDESCSUB_AC_EXTENSION: -#define p ((const struct usb_audio_extension_unit *)dp) r = *(struct usb_audio_cluster *) - &p->baSourceId[p->bNrInPins]; -#undef p + &iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins]; return (r); default: goto bad; @@ -696,12 +718,10 @@ uaudio_get_cluster(int id, const usb_descriptor_t **dps) } void -uaudio_add_input(struct uaudio_softc *sc, const usb_descriptor_t *v, - const usb_descriptor_t **dps) +uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { #ifdef UAUDIO_DEBUG - const struct usb_audio_input_terminal *d = - (const struct usb_audio_input_terminal *)v; + const struct usb_audio_input_terminal *d = iot[id].d.it; DPRINTFN(2,("uaudio_add_input: bTerminalId=%d wTerminalType=0x%04x " "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " @@ -713,12 +733,10 @@ uaudio_add_input(struct uaudio_softc *sc, const usb_descriptor_t *v, } void -uaudio_add_output(struct uaudio_softc *sc, const usb_descriptor_t *v, - const usb_descriptor_t **dps) +uaudio_add_output(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { #ifdef UAUDIO_DEBUG - const struct usb_audio_output_terminal *d = - (const struct usb_audio_output_terminal *)v; + const struct usb_audio_output_terminal *d = iot[id].d.ot; DPRINTFN(2,("uaudio_add_output: bTerminalId=%d wTerminalType=0x%04x " "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", @@ -728,10 +746,9 @@ uaudio_add_output(struct uaudio_softc *sc, const usb_descriptor_t *v, } void -uaudio_add_mixer(struct uaudio_softc *sc, const usb_descriptor_t *v, - const usb_descriptor_t **dps) +uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { - const struct usb_audio_mixer_unit *d = (const struct usb_audio_mixer_unit *)v; + const struct usb_audio_mixer_unit *d = iot[id].d.mu; struct usb_audio_mixer_unit_1 *d1; int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k; uByte *bm; @@ -743,7 +760,7 @@ uaudio_add_mixer(struct uaudio_softc *sc, const usb_descriptor_t *v, /* Compute the number of input channels */ ichs = 0; for (i = 0; i < d->bNrInPins; i++) - ichs += uaudio_get_cluster(d->baSourceId[i], dps).bNrChannels; + ichs += uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels; /* and the number of output channels */ d1 = (struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; @@ -752,12 +769,12 @@ uaudio_add_mixer(struct uaudio_softc *sc, const usb_descriptor_t *v, bm = d1->bmControls; mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); - mix.class = -1; + uaudio_determine_class(&iot[id], &mix); mix.type = MIX_SIGNED_16; mix.ctlunit = AudioNvolume; #define BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1) for (p = i = 0; i < d->bNrInPins; i++) { - chs = uaudio_get_cluster(d->baSourceId[i], dps).bNrChannels; + chs = uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels; mc = 0; for (c = 0; c < chs; c++) { mo = 0; @@ -779,7 +796,7 @@ uaudio_add_mixer(struct uaudio_softc *sc, const usb_descriptor_t *v, MAKE(p+c+1, o+1); } snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-%s", - d->bUnitId, uaudio_id_name(sc, dps, + d->bUnitId, uaudio_id_name(sc, iot, d->baSourceId[i])); mix.nchan = chs; uaudio_mixer_add_ctl(sc, &mix); @@ -793,11 +810,9 @@ uaudio_add_mixer(struct uaudio_softc *sc, const usb_descriptor_t *v, } void -uaudio_add_selector(struct uaudio_softc *sc, const usb_descriptor_t *v, - const usb_descriptor_t **dps) +uaudio_add_selector(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { - const struct usb_audio_selector_unit *d = - (const struct usb_audio_selector_unit *)v; + const struct usb_audio_selector_unit *d = iot[id].d.su; struct mixerctl mix; int i, wp; @@ -805,7 +820,7 @@ uaudio_add_selector(struct uaudio_softc *sc, const usb_descriptor_t *v, d->bUnitId, d->bNrInPins)); mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.wValue[0] = MAKE(0, 0); - mix.class = UAC_OUTPUT; + uaudio_determine_class(&iot[id], &mix); mix.nchan = 1; mix.type = MIX_SELECTOR; mix.ctlunit = ""; @@ -822,19 +837,240 @@ uaudio_add_selector(struct uaudio_softc *sc, const usb_descriptor_t *v, uaudio_mixer_add_ctl(sc, &mix); } -void -uaudio_add_feature(struct uaudio_softc *sc, const usb_descriptor_t *v, - const usb_descriptor_t **dps) +#ifdef UAUDIO_DEBUG +const char * +uaudio_get_terminal_name(int terminal_type) { - const struct usb_audio_feature_unit *d - = (const struct usb_audio_feature_unit *)v; + static char buf[100]; + + switch (terminal_type) { + case UAT_STREAM: return "UAT_STREAM"; + case UATI_MICROPHONE: return "UATI_MICROPHONE"; + case UATI_DESKMICROPHONE: return "UATI_DESKMICROPHONE"; + case UATI_PERSONALMICROPHONE: return "UATI_PERSONALMICROPHONE"; + case UATI_OMNIMICROPHONE: return "UATI_OMNIMICROPHONE"; + case UATI_MICROPHONEARRAY: return "UATI_MICROPHONEARRAY"; + case UATI_PROCMICROPHONEARR: return "UATI_PROCMICROPHONEARR"; + case UATO_SPEAKER: return "UATO_SPEAKER"; + case UATO_DESKTOPSPEAKER: return "UATO_DESKTOPSPEAKER"; + case UATO_ROOMSPEAKER: return "UATO_ROOMSPEAKER"; + case UATO_COMMSPEAKER: return "UATO_COMMSPEAKER"; + case UATO_HEADPHONES: return "UATO_HEADPHONES"; + case UATE_ANALOGCONN: return "UATE_ANALOGCONN"; + case UATE_LINECONN: return "UATE_LINECONN"; + case UATE_LEGACYCONN: return "UATE_LEGACYCONN"; + case UATE_DIGITALAUIFC: return "UATE_DIGITALAUIFC"; + case UATE_SPDIF: return "UATE_SPDIF"; + case UATE_1394DA: return "UATE_1394DA"; + case UATE_1394DV: return "UATE_1394DV"; + case UATF_CDPLAYER: return "UATF_CDPLAYER"; + case UATF_SYNTHESIZER: return "UATF_SYNTHESIZER"; + case UATF_VIDEODISCAUDIO: return "UATF_VIDEODISCAUDIO"; + case UATF_DVDAUDIO: return "UATF_DVDAUDIO"; + case UATF_TVTUNERAUDIO: return "UATF_TVTUNERAUDIO"; + case UAT_UNDEFINED: return "UAT_UNDEFINED"; + case UAT_VENDOR: return "UAT_VENDOR"; + case UATI_UNDEFINED: return "UATI_UNDEFINED"; +/* output terminal types */ + case UATO_UNDEFINED: return "UATO_UNDEFINED"; + case UATO_DISPLAYAUDIO: return "UATO_DISPLAYAUDIO"; + case UATO_SUBWOOFER: return "UATO_SUBWOOFER"; +/* bidir terminal types */ + case UATB_UNDEFINED: return "UATB_UNDEFINED"; + case UATB_HANDSET: return "UATB_HANDSET"; + case UATB_HEADSET: return "UATB_HEADSET"; + case UATB_SPEAKERPHONE: return "UATB_SPEAKERPHONE"; + case UATB_SPEAKERPHONEESUP: return "UATB_SPEAKERPHONEESUP"; + case UATB_SPEAKERPHONEECANC: return "UATB_SPEAKERPHONEECANC"; +/* telephony terminal types */ + case UATT_UNDEFINED: return "UATT_UNDEFINED"; + case UATT_PHONELINE: return "UATT_PHONELINE"; + case UATT_TELEPHONE: return "UATT_TELEPHONE"; + case UATT_DOWNLINEPHONE: return "UATT_DOWNLINEPHONE"; +/* external terminal types */ + case UATE_UNDEFINED: return "UATE_UNDEFINED"; +/* embedded function terminal types */ + case UATF_UNDEFINED: return "UATF_UNDEFINED"; + case UATF_CALIBNOISE: return "UATF_CALIBNOISE"; + case UATF_EQUNOISE: return "UATF_EQUNOISE"; + case UATF_DAT: return "UATF_DAT"; + case UATF_DCC: return "UATF_DCC"; + case UATF_MINIDISK: return "UATF_MINIDISK"; + case UATF_ANALOGTAPE: return "UATF_ANALOGTAPE"; + case UATF_PHONOGRAPH: return "UATF_PHONOGRAPH"; + case UATF_VCRAUDIO: return "UATF_VCRAUDIO"; + case UATF_SATELLITE: return "UATF_SATELLITE"; + case UATF_CABLETUNER: return "UATF_CABLETUNER"; + case UATF_DSS: return "UATF_DSS"; + case UATF_RADIORECV: return "UATF_RADIORECV"; + case UATF_RADIOXMIT: return "UATF_RADIOXMIT"; + case UATF_MULTITRACK: return "UATF_MULTITRACK"; + default: + snprintf(buf, sizeof(buf), "unknown type (0x%.4x)", terminal_type); + return buf; + } +} +#endif + +int +uaudio_determine_class(const struct io_terminal *iot, struct mixerctl *mix) +{ + int terminal_type; + + if (iot == NULL || iot->output == NULL) { + mix->class = UAC_OUTPUT; + return 0; + } + terminal_type = 0; + if (iot->output->size == 1) + terminal_type = iot->output->terminals[0]; + /* + * If the only output terminal is USB, + * the class is UAC_RECORD. + */ + if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { + mix->class = UAC_RECORD; + if (iot->inputs_size == 1 + && iot->inputs[0] != NULL + && iot->inputs[0]->size == 1) + return iot->inputs[0]->terminals[0]; + else + return 0; + } + /* + * If the ulitimate destination of the unit is just one output + * terminal and the unit is connected to the output terminal + * directly, the class is UAC_OUTPUT. + */ + if (terminal_type != 0 && iot->direct) { + mix->class = UAC_OUTPUT; + return terminal_type; + } + /* + * If the unit is connected to just one input terminal, + * the class is UAC_INPUT. + */ + if (iot->inputs_size == 1 && iot->inputs[0] != NULL + && iot->inputs[0]->size == 1) { + mix->class = UAC_INPUT; + return iot->inputs[0]->terminals[0]; + } + /* + * Otherwise, the class is UAC_OUTPUT. + */ + mix->class = UAC_OUTPUT; + return terminal_type; +} + +const char * +uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix) +{ + int terminal_type; + + terminal_type = uaudio_determine_class(iot, mix); + if (mix->class == UAC_RECORD && terminal_type == 0) + return AudioNmixerout; + DPRINTF(("%s: terminal_type=%s\n", __func__, + uaudio_get_terminal_name(terminal_type))); + switch (terminal_type) { + case UAT_STREAM: + return AudioNdac; + + case UATI_MICROPHONE: + case UATI_DESKMICROPHONE: + case UATI_PERSONALMICROPHONE: + case UATI_OMNIMICROPHONE: + case UATI_MICROPHONEARRAY: + case UATI_PROCMICROPHONEARR: + return AudioNmicrophone; + + case UATO_SPEAKER: + case UATO_DESKTOPSPEAKER: + case UATO_ROOMSPEAKER: + case UATO_COMMSPEAKER: + return AudioNspeaker; + + case UATO_HEADPHONES: + return AudioNheadphone; + + case UATE_ANALOGCONN: + case UATE_LINECONN: + case UATE_LEGACYCONN: + return AudioNline; + + case UATE_DIGITALAUIFC: + case UATE_SPDIF: + case UATE_1394DA: + case UATE_1394DV: + return AudioNaux; + + case UATF_CDPLAYER: + return AudioNcd; + + case UATF_SYNTHESIZER: + return AudioNfmsynth; + + case UATF_VIDEODISCAUDIO: + case UATF_DVDAUDIO: + case UATF_TVTUNERAUDIO: + return AudioNvideo; + + case UAT_UNDEFINED: + case UAT_VENDOR: + case UATI_UNDEFINED: +/* output terminal types */ + case UATO_UNDEFINED: + case UATO_DISPLAYAUDIO: + case UATO_SUBWOOFER: +/* bidir terminal types */ + case UATB_UNDEFINED: + case UATB_HANDSET: + case UATB_HEADSET: + case UATB_SPEAKERPHONE: + case UATB_SPEAKERPHONEESUP: + case UATB_SPEAKERPHONEECANC: +/* telephony terminal types */ + case UATT_UNDEFINED: + case UATT_PHONELINE: + case UATT_TELEPHONE: + case UATT_DOWNLINEPHONE: +/* external terminal types */ + case UATE_UNDEFINED: +/* embedded function terminal types */ + case UATF_UNDEFINED: + case UATF_CALIBNOISE: + case UATF_EQUNOISE: + case UATF_DAT: + case UATF_DCC: + case UATF_MINIDISK: + case UATF_ANALOGTAPE: + case UATF_PHONOGRAPH: + case UATF_VCRAUDIO: + case UATF_SATELLITE: + case UATF_CABLETUNER: + case UATF_DSS: + case UATF_RADIORECV: + case UATF_RADIOXMIT: + case UATF_MULTITRACK: + case 0xffff: + default: + DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type)); + return AudioNmaster; + } + return AudioNmaster; +} + +void +uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id) +{ + const struct usb_audio_feature_unit *d = iot[id].d.fu; uByte *ctls = d->bmaControls; int ctlsize = d->bControlSize; int nchan = (d->bLength - 7) / ctlsize; - int srcId = d->bSourceId; u_int fumask, mmask, cmask; struct mixerctl mix; int chan, ctl, i, unit; + const char *mixername; #define GET(i) (ctls[(i)*ctlsize] | \ (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0)) @@ -847,9 +1083,9 @@ uaudio_add_feature(struct uaudio_softc *sc, const usb_descriptor_t *v, cmask |= GET(chan); } - DPRINTFN(1,("uaudio_add_feature: bUnitId=%d bSourceId=%d, " + DPRINTFN(1,("uaudio_add_feature: bUnitId=%d, " "%d channels, mmask=0x%04x, cmask=0x%04x\n", - d->bUnitId, srcId, nchan, mmask, cmask)); + d->bUnitId, nchan, mmask, cmask)); if (nchan > MIX_MAX_CHAN) nchan = MIX_MAX_CHAN; @@ -874,77 +1110,63 @@ uaudio_add_feature(struct uaudio_softc *sc, const usb_descriptor_t *v, continue; } #undef GET - mix.class = UAC_OUTPUT; /* XXX we don't really know this */ + mixername = uaudio_feature_name(&iot[id], &mix); switch (ctl) { case MUTE_CONTROL: mix.type = MIX_ON_OFF; mix.ctlunit = ""; - uaudio_mixer_alias_ctl(sc, &mix, AudioNmute); snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNmute); + "%s.%s", mixername, AudioNmute); break; case VOLUME_CONTROL: mix.type = MIX_SIGNED_16; mix.ctlunit = AudioNvolume; - uaudio_mixer_alias_ctl(sc, &mix, AudioNmaster); - snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNmaster); + strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname)); break; case BASS_CONTROL: mix.type = MIX_SIGNED_8; mix.ctlunit = AudioNbass; - uaudio_mixer_alias_ctl(sc, &mix, AudioNbass); snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNbass); + "%s.%s", mixername, AudioNbass); break; case MID_CONTROL: mix.type = MIX_SIGNED_8; mix.ctlunit = AudioNmid; snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNmid); + "%s.%s", mixername, AudioNmid); break; case TREBLE_CONTROL: mix.type = MIX_SIGNED_8; mix.ctlunit = AudioNtreble; - uaudio_mixer_alias_ctl(sc, &mix, AudioNtreble); snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNtreble); + "%s.%s", mixername, AudioNtreble); break; case GRAPHIC_EQUALIZER_CONTROL: continue; /* XXX don't add anything */ break; case AGC_CONTROL: mix.type = MIX_ON_OFF; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNagc); mix.ctlunit = ""; + snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", + mixername, AudioNagc); break; case DELAY_CONTROL: mix.type = MIX_UNSIGNED_16; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNdelay); mix.ctlunit = "4 ms"; + snprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNdelay); break; case BASS_BOOST_CONTROL: mix.type = MIX_ON_OFF; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNbassboost); mix.ctlunit = ""; + snprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNbassboost); break; case LOUDNESS_CONTROL: mix.type = MIX_ON_OFF; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "fea%d-%s-%s", unit, uaudio_id_name(sc, dps, srcId), - AudioNloudness); mix.ctlunit = ""; + snprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNloudness); break; } uaudio_mixer_add_ctl(sc, &mix); @@ -953,11 +1175,9 @@ uaudio_add_feature(struct uaudio_softc *sc, const usb_descriptor_t *v, void uaudio_add_processing_updown(struct uaudio_softc *sc, - const usb_descriptor_t *v, - const usb_descriptor_t **dps) + const struct io_terminal *iot, int id) { - const struct usb_audio_processing_unit *d = - (const struct usb_audio_processing_unit *)v; + const struct usb_audio_processing_unit *d = iot[id].d.pu; const struct usb_audio_processing_unit_1 *d1 = (const struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins]; const struct usb_audio_processing_unit_updown *ud = @@ -977,7 +1197,7 @@ uaudio_add_processing_updown(struct uaudio_softc *sc, mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0); - mix.class = -1; + uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; /* XXX */ mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d-mode", d->bUnitId); @@ -991,11 +1211,9 @@ uaudio_add_processing_updown(struct uaudio_softc *sc, } void -uaudio_add_processing(struct uaudio_softc *sc, const usb_descriptor_t *v, - const usb_descriptor_t **dps) +uaudio_add_processing(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { - const struct usb_audio_processing_unit *d = - (const struct usb_audio_processing_unit *)v; + const struct usb_audio_processing_unit *d = iot[id].d.pu; const struct usb_audio_processing_unit_1 *d1 = (const struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins]; int ptype = UGETW(d->wProcessType); @@ -1008,7 +1226,7 @@ uaudio_add_processing(struct uaudio_softc *sc, const usb_descriptor_t *v, mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(XX_ENABLE_CONTROL, 0); - mix.class = -1; + uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d.%d-enable", @@ -1018,7 +1236,7 @@ uaudio_add_processing(struct uaudio_softc *sc, const usb_descriptor_t *v, switch(ptype) { case UPDOWNMIX_PROCESS: - uaudio_add_processing_updown(sc, v, dps); + uaudio_add_processing_updown(sc, iot, id); break; case DOLBY_PROLOGIC_PROCESS: case P3D_STEREO_EXTENDER_PROCESS: @@ -1035,11 +1253,9 @@ uaudio_add_processing(struct uaudio_softc *sc, const usb_descriptor_t *v, } void -uaudio_add_extension(struct uaudio_softc *sc, const usb_descriptor_t *v, - const usb_descriptor_t **dps) +uaudio_add_extension(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { - const struct usb_audio_extension_unit *d = - (const struct usb_audio_extension_unit *)v; + const struct usb_audio_extension_unit *d = iot[id].d.eu; const struct usb_audio_extension_unit_1 *d1 = (const struct usb_audio_extension_unit_1 *)&d->baSourceId[d->bNrInPins]; struct mixerctl mix; @@ -1054,7 +1270,7 @@ uaudio_add_extension(struct uaudio_softc *sc, const usb_descriptor_t *v, mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(UA_EXT_ENABLE, 0); - mix.class = -1; + uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "ext%d-enable", @@ -1063,6 +1279,192 @@ uaudio_add_extension(struct uaudio_softc *sc, const usb_descriptor_t *v, } } +struct terminal_list* +uaudio_merge_terminal_list(const struct io_terminal *iot) +{ + struct terminal_list *tml; + uint16_t *ptm; + int i, len; + + len = 0; + if (iot->inputs == NULL) + return NULL; + for (i = 0; i < iot->inputs_size; i++) { + if (iot->inputs[i] != NULL) + len += iot->inputs[i]->size; + } + tml = malloc(TERMINAL_LIST_SIZE(len), M_TEMP, M_NOWAIT); + if (tml == NULL) { + printf("uaudio_merge_terminal_list: no memory\n"); + return NULL; + } + tml->size = 0; + ptm = tml->terminals; + for (i = 0; i < iot->inputs_size; i++) { + if (iot->inputs[i] == NULL) + continue; + if (iot->inputs[i]->size > len) + break; + memcpy(ptm, iot->inputs[i]->terminals, + iot->inputs[i]->size * sizeof(uint16_t)); + tml->size += iot->inputs[i]->size; + ptm += iot->inputs[i]->size; + len -= iot->inputs[i]->size; + } + return tml; +} + +struct terminal_list * +uaudio_io_terminaltype(int outtype, struct io_terminal *iot, int id) +{ + struct terminal_list *tml; + struct io_terminal *it; + int src_id, i; + + it = &iot[id]; + if (it->output != NULL) { + /* already has outtype? */ + for (i = 0; i < it->output->size; i++) + if (it->output->terminals[i] == outtype) + return uaudio_merge_terminal_list(it); + tml = malloc(TERMINAL_LIST_SIZE(it->output->size + 1), + M_TEMP, M_NOWAIT); + if (tml == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return uaudio_merge_terminal_list(it); + } + memcpy(tml, it->output, TERMINAL_LIST_SIZE(it->output->size)); + tml->terminals[it->output->size] = outtype; + tml->size++; + free(it->output, M_TEMP); + it->output = tml; + if (it->inputs != NULL) { + for (i = 0; i < it->inputs_size; i++) + if (it->inputs[i] != NULL) + free(it->inputs[i], M_TEMP); + free(it->inputs, M_TEMP); + } + it->inputs_size = 0; + it->inputs = NULL; + } else { /* end `iot[id] != NULL' */ + it->inputs_size = 0; + it->inputs = NULL; + it->output = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT); + if (it->output == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + it->output->terminals[0] = outtype; + it->output->size = 1; + it->direct = FALSE; + } + + switch (it->d.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + tml = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT); + if (tml == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + free(it->inputs, M_TEMP); + it->inputs = NULL; + return NULL; + } + it->inputs[0] = tml; + tml->terminals[0] = UGETW(it->d.it->wTerminalType); + tml->size = 1; + it->inputs_size = 1; + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_FEATURE: + src_id = it->d.fu->bSourceId; + it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return uaudio_io_terminaltype(outtype, iot, src_id); + } + it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id); + it->inputs_size = 1; + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_OUTPUT: + it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + src_id = it->d.ot->bSourceId; + it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id); + it->inputs_size = 1; + iot[src_id].direct = TRUE; + return NULL; + case UDESCSUB_AC_MIXER: + it->inputs_size = 0; + it->inputs = malloc(sizeof(struct terminal_list *) + * it->d.mu->bNrInPins, M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + for (i = 0; i < it->d.mu->bNrInPins; i++) { + src_id = it->d.mu->baSourceId[i]; + it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + src_id); + it->inputs_size++; + } + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_SELECTOR: + it->inputs_size = 0; + it->inputs = malloc(sizeof(struct terminal_list *) + * it->d.su->bNrInPins, M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + for (i = 0; i < it->d.su->bNrInPins; i++) { + src_id = it->d.su->baSourceId[i]; + it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + src_id); + it->inputs_size++; + } + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_PROCESSING: + it->inputs_size = 0; + it->inputs = malloc(sizeof(struct terminal_list *) + * it->d.pu->bNrInPins, M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + for (i = 0; i < it->d.pu->bNrInPins; i++) { + src_id = it->d.pu->baSourceId[i]; + it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + src_id); + it->inputs_size++; + } + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_EXTENSION: + it->inputs_size = 0; + it->inputs = malloc(sizeof(struct terminal_list *) + * it->d.eu->bNrInPins, M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + printf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + for (i = 0; i < it->d.eu->bNrInPins; i++) { + src_id = it->d.eu->baSourceId[i]; + it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + src_id); + it->inputs_size++; + } + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_HEADER: + default: + return NULL; + } +} + usbd_status uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc) { @@ -1363,11 +1765,14 @@ uaudio_identify_as(struct uaudio_softc *sc, usbd_status uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc) { + struct io_terminal* iot; const usb_interface_descriptor_t *id; const struct usb_audio_control_descriptor *acdp; - const usb_descriptor_t *dp, *dps[256]; + const usb_descriptor_t *dp; + const struct usb_audio_output_terminal *pot; + struct terminal_list *tml; const char *buf, *ibuf, *ibufend; - int size, offs, aclen, ndps, i; + int size, offs, aclen, ndps, i, j; size = UGETW(cdesc->wTotalLength); buf = (char *)cdesc; @@ -1380,7 +1785,7 @@ uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc if (offs + sizeof *acdp > size) return (USBD_INVAL); sc->sc_ac_iface = id->bInterfaceNumber; - DPRINTFN(2,("uaudio_identify: AC interface is %d\n", sc->sc_ac_iface)); + DPRINTFN(2,("uaudio_identify_ac: AC interface is %d\n", sc->sc_ac_iface)); /* A class-specific AC interface header should follow. */ ibuf = buf + offs; @@ -1397,72 +1802,174 @@ uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc return (USBD_INVAL); sc->sc_audio_rev = UGETW(acdp->bcdADC); - DPRINTFN(2,("uaudio_identify: found AC header, vers=%03x, len=%d\n", + DPRINTFN(2,("uaudio_identify_ac: found AC header, vers=%03x, len=%d\n", sc->sc_audio_rev, aclen)); sc->sc_nullalt = -1; /* Scan through all the AC specific descriptors */ ibufend = ibuf + aclen; - dp = (usb_descriptor_t *)ibuf; + dp = (const usb_descriptor_t *)ibuf; ndps = 0; - memset(dps, 0, sizeof dps); + iot = malloc(sizeof(struct io_terminal) * 256, M_TEMP, M_NOWAIT | M_ZERO); + if (iot == NULL) { + printf("%s: no memory\n", __func__); + return USBD_NOMEM; + } for (;;) { ibuf += dp->bLength; if (ibuf >= ibufend) break; - dp = (usb_descriptor_t *)ibuf; + dp = (const usb_descriptor_t *)ibuf; if (ibuf + dp->bLength > ibufend) return (USBD_INVAL); if (dp->bDescriptorType != UDESC_CS_INTERFACE) { - printf("uaudio_identify: skip desc type=0x%02x\n", + printf("uaudio_identify_ac: skip desc type=0x%02x\n", dp->bDescriptorType); continue; } - i = ((struct usb_audio_input_terminal *)dp)->bTerminalId; - dps[i] = dp; + i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId; + iot[i].d.desc = dp; if (i > ndps) ndps = i; } ndps++; + /* construct io_terminal */ for (i = 0; i < ndps; i++) { - dp = dps[i]; + dp = iot[i].d.desc; if (dp == NULL) continue; - DPRINTF(("uaudio_identify: subtype=%d\n", - dp->bDescriptorSubtype)); - switch (dp->bDescriptorSubtype) { - case UDESCSUB_AC_HEADER: - printf("uaudio_identify: unexpected AC header\n"); - break; + if (dp->bDescriptorSubtype != UDESCSUB_AC_OUTPUT) + continue; + pot = iot[i].d.ot; + tml = uaudio_io_terminaltype(UGETW(pot->wTerminalType), iot, i); + if (tml != NULL) + free(tml, M_TEMP); + } + +#ifdef UAUDIO_DEBUG + for (i = 0; i < 256; i++) { + if (iot[i].d.desc == NULL) + continue; + printf("id %d:\t", i); + switch (iot[i].d.desc->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: - uaudio_add_input(sc, dp, dps); + printf("AC_INPUT type=%s\n", uaudio_get_terminal_name + (UGETW(iot[i].d.it->wTerminalType))); break; case UDESCSUB_AC_OUTPUT: - uaudio_add_output(sc, dp, dps); + printf("AC_OUTPUT type=%s ", uaudio_get_terminal_name + (UGETW(iot[i].d.ot->wTerminalType))); + printf("src=%d\n", iot[i].d.ot->bSourceId); break; case UDESCSUB_AC_MIXER: - uaudio_add_mixer(sc, dp, dps); + printf("AC_MIXER src="); + for (j = 0; j < iot[i].d.mu->bNrInPins; j++) + printf("%d ", iot[i].d.mu->baSourceId[j]); + printf("\n"); break; case UDESCSUB_AC_SELECTOR: - uaudio_add_selector(sc, dp, dps); + printf("AC_SELECTOR src="); + for (j = 0; j < iot[i].d.su->bNrInPins; j++) + printf("%d ", iot[i].d.su->baSourceId[j]); + printf("\n"); break; case UDESCSUB_AC_FEATURE: - uaudio_add_feature(sc, dp, dps); + printf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId); break; case UDESCSUB_AC_PROCESSING: - uaudio_add_processing(sc, dp, dps); + printf("AC_PROCESSING src="); + for (j = 0; j < iot[i].d.pu->bNrInPins; j++) + printf("%d ", iot[i].d.pu->baSourceId[j]); + printf("\n"); break; case UDESCSUB_AC_EXTENSION: - uaudio_add_extension(sc, dp, dps); + printf("AC_EXTENSION src="); + for (j = 0; j < iot[i].d.eu->bNrInPins; j++) + printf("%d ", iot[i].d.eu->baSourceId[j]); + printf("\n"); break; default: - printf("uaudio_identify: bad AC desc subtype=0x%02x\n", + printf("unknown audio control (subtype=%d)\n", + iot[i].d.desc->bDescriptorSubtype); + } + for (j = 0; j < iot[i].inputs_size; j++) { + int k; + printf("\tinput%d: ", j); + tml = iot[i].inputs[j]; + if (tml == NULL) { + printf("NULL\n"); + continue; + } + for (k = 0; k < tml->size; k++) + printf("%s ", uaudio_get_terminal_name + (tml->terminals[k])); + printf("\n"); + } + printf("\toutput: "); + tml = iot[i].output; + for (j = 0; j < tml->size; j++) + printf("%s ", uaudio_get_terminal_name(tml->terminals[j])); + printf("\n"); + } +#endif + + for (i = 0; i < ndps; i++) { + dp = iot[i].d.desc; + if (dp == NULL) + continue; + DPRINTF(("uaudio_identify_ac: id=%d subtype=%d\n", + i, dp->bDescriptorSubtype)); + switch (dp->bDescriptorSubtype) { + case UDESCSUB_AC_HEADER: + printf("uaudio_identify_ac: unexpected AC header\n"); + break; + case UDESCSUB_AC_INPUT: + uaudio_add_input(sc, iot, i); + break; + case UDESCSUB_AC_OUTPUT: + uaudio_add_output(sc, iot, i); + break; + case UDESCSUB_AC_MIXER: + uaudio_add_mixer(sc, iot, i); + break; + case UDESCSUB_AC_SELECTOR: + uaudio_add_selector(sc, iot, i); + break; + case UDESCSUB_AC_FEATURE: + uaudio_add_feature(sc, iot, i); + break; + case UDESCSUB_AC_PROCESSING: + uaudio_add_processing(sc, iot, i); + break; + case UDESCSUB_AC_EXTENSION: + uaudio_add_extension(sc, iot, i); + break; + default: + printf("uaudio_identify_ac: bad AC desc subtype=0x%02x\n", dp->bDescriptorSubtype); break; } } + + /* delete io_terminal */ + for (i = 0; i < 256; i++) { + if (iot[i].d.desc == NULL) + continue; + if (iot[i].inputs != NULL) { + for (j = 0; j < iot[i].inputs_size; j++) { + if (iot[i].inputs[j] != NULL) + free(iot[i].inputs[j], M_TEMP); + } + free(iot[i].inputs, M_TEMP); + } + if (iot[i].output != NULL) + free(iot[i].output, M_TEMP); + iot[i].d.desc = NULL; + } + free(iot, M_TEMP); + return (USBD_NORMAL_COMPLETION); } @@ -1500,6 +2007,12 @@ uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi) strlcpy(mi->label.name, AudioCequalization, sizeof(mi->label.name)); return (0); + case UAC_RECORD: + mi->type = AUDIO_MIXER_CLASS; + mi->mixer_class = UAC_RECORD; + mi->next = mi->prev = AUDIO_MIXER_LAST; + strlcpy(mi->label.name, AudioCrecord, sizeof(mi->label.name)); + return 0; default: break; }