From 964af639ad699404cbe36d0c1fc85970700e6107 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Apr 2018 14:23:37 +0200 Subject: ALSA: usb-audio: Initialize Dell Dock playback volumes In the early commit adcdd0d5a1cb ("ALSA: usb-audio: Skip volume controls triggers hangup on Dell USB Dock"), we add the mixer quirks for Dell dock to skip two mixer FU's for playback. This supposed that the device has always the proper initial volume, but it doesn't seem always correct. This patch adds the explicit initialization of the volumes to the fixed 0dB at the device probe time. Also, such a fixup is needed after the resume, so a new function is hooked to the resume callback as well. Bugzilla: http://bugzilla.suse.com/show_bug.cgi?id=1089467 Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 344d7b069d59..76fabc4b72b5 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2948,6 +2948,8 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) } } + snd_usb_mixer_resume_quirk(mixer); + return snd_usb_mixer_activate(mixer); } #endif -- cgit v1.2.2 From 4120fbedbb6c999686d03a9c74acc4f650ece8a2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 2 May 2018 11:52:55 +0200 Subject: ALSA: usb-audio: Add "Keep Interface" control This patch adds "Keep Interface" control for each USB-audio device. The control element is with SND_CTL_IFACE_CARD, so that it won't appear on any sane mixer applications. For a device that is confirmed to work well with "keep-interface" mode, user can flip the control via amixer, e.g. % amixer -c1 cset iface=CARD,name='Keep Interface' on and save/restore the state via alsactl. The reason to provide this via control API is that the behavior must be pretty depending on the device (and the firmware in it), so it's not ideal to apply via module option. For a device that certainly works, we may set it statically via a quirk table entry. But a device like Dell WD15 dock behaves so differently depending on the firmware, and we can't set it statically. So leave this as a dynamic switch each user can adjust freely. Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 76fabc4b72b5..bb203b3684fc 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2801,6 +2801,48 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) return 0; } +static int keep_iface_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = mixer->chip->keep_iface; + return 0; +} + +static int keep_iface_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + bool keep_iface = !!ucontrol->value.integer.value[0]; + + if (mixer->chip->keep_iface == keep_iface) + return 0; + mixer->chip->keep_iface = keep_iface; + return 1; +} + +static const struct snd_kcontrol_new keep_iface_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "Keep Interface", + .info = snd_ctl_boolean_mono_info, + .get = keep_iface_ctl_get, + .put = keep_iface_ctl_put, +}; + +static int create_keep_iface_ctl(struct usb_mixer_interface *mixer) +{ + struct snd_kcontrol *kctl = snd_ctl_new1(&keep_iface_ctl, mixer); + + /* need only one control per card */ + if (snd_ctl_find_id(mixer->chip->card, &kctl->id)) { + snd_ctl_free_one(kctl); + return 0; + } + + return snd_ctl_add(mixer->chip->card, kctl); +} + int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error) { @@ -2842,6 +2884,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, if ((err = snd_usb_mixer_controls(mixer)) < 0 || (err = snd_usb_mixer_status_create(mixer)) < 0) goto _error; + err = create_keep_iface_ctl(mixer); + if (err < 0) + goto _error; snd_usb_mixer_apply_create_quirk(mixer); -- cgit v1.2.2 From 8c558076c740e8009a96c6fdc3d4245dde62be77 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 May 2018 12:33:32 +0200 Subject: ALSA: usb-audio: Clean up mixer element list traverse Introduce a new macro for iterating over mixer element list for avoiding the open codes in many places. Also the open-coded container_of() and the forced cast to struct usb_mixer_elem_info are replaced with another simple macro, too. No functional changes but just readability improvement. Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index bb203b3684fc..265258b0e74c 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2598,9 +2598,9 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) { struct usb_mixer_elem_list *list; - for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { + for_each_mixer_elem(list, mixer, unitid) { struct usb_mixer_elem_info *info = - (struct usb_mixer_elem_info *)list; + mixer_elem_list_to_info(list); /* invalidate cache, so the value is read from the device */ info->cached = 0; snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, @@ -2611,7 +2611,7 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, struct usb_mixer_elem_list *list) { - struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; + struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", "S8", "U8", "S16", "U16"}; snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " @@ -2637,8 +2637,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, mixer->ignore_ctl_error); snd_iprintf(buffer, "Card: %s\n", chip->card->longname); for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { - for (list = mixer->id_elems[unitid]; list; - list = list->next_id_elem) { + for_each_mixer_elem(list, mixer, unitid) { snd_iprintf(buffer, " Unit: %i\n", list->id); if (list->kctl) snd_iprintf(buffer, @@ -2668,19 +2667,19 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, return; } - for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) + for_each_mixer_elem(list, mixer, unitid) count++; if (count == 0) return; - for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { + for_each_mixer_elem(list, mixer, unitid) { struct usb_mixer_elem_info *info; if (!list->kctl) continue; - info = (struct usb_mixer_elem_info *)list; + info = mixer_elem_list_to_info(list); if (count > 1 && info->control != control) continue; @@ -2946,7 +2945,7 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) static int restore_mixer_value(struct usb_mixer_elem_list *list) { - struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; + struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); int c, err, idx; if (cval->cmask) { @@ -2982,8 +2981,7 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) if (reset_resume) { /* restore cached mixer values */ for (id = 0; id < MAX_ID_ELEMS; id++) { - for (list = mixer->id_elems[id]; list; - list = list->next_id_elem) { + for_each_mixer_elem(list, mixer, id) { if (list->resume) { err = list->resume(list); if (err < 0) -- cgit v1.2.2 From eccfc1b868a9902bbfa2315a7c5385dbbf822dc4 Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Fri, 4 May 2018 04:24:02 +0300 Subject: ALSA: usb: mixer: make string parsing independent of mixer_build state Functions like snd_usb_copy_string_desc() or get_term_name() don't actually need mixer_build state but can use snd_usb_audio structure instead to get usb device. This patch has no functional change but prepares to future UAC3 BADD profiles support which don't have class-specific descriptors so won't have mixer_build state. Signed-off-by: Ruslan Bilovol Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 265258b0e74c..76417943ff85 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -201,10 +201,10 @@ static void *find_audio_control_unit(struct mixer_build *state, /* * copy a string with the given id */ -static int snd_usb_copy_string_desc(struct mixer_build *state, +static int snd_usb_copy_string_desc(struct snd_usb_audio *chip, int index, char *buf, int maxlen) { - int len = usb_string(state->chip->dev, index, buf, maxlen - 1); + int len = usb_string(chip->dev, index, buf, maxlen - 1); if (len < 0) return 0; @@ -658,14 +658,14 @@ static struct iterm_name_combo { { 0 }, }; -static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm, +static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iterm, unsigned char *name, int maxlen, int term_only) { struct iterm_name_combo *names; int len; if (iterm->name) { - len = snd_usb_copy_string_desc(state, iterm->name, + len = snd_usb_copy_string_desc(chip, iterm->name, name, maxlen); if (len) return len; @@ -1407,7 +1407,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; if (!len && nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(state->chip, nameid, kctl->id.name, sizeof(kctl->id.name)); switch (control) { @@ -1422,10 +1422,10 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, * - otherwise, anonymous name. */ if (!len) { - len = get_term_name(state, iterm, kctl->id.name, + len = get_term_name(state->chip, iterm, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) - len = get_term_name(state, &state->oterm, + len = get_term_name(state->chip, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) @@ -1498,7 +1498,7 @@ static void get_connector_control_name(struct mixer_build *state, struct usb_audio_term *term, bool is_input, char *name, int name_size) { - int name_len = get_term_name(state, term, name, name_size, 0); + int name_len = get_term_name(state->chip, term, name, name_size, 0); if (name_len == 0) strlcpy(name, "Unknown", name_size); @@ -1597,7 +1597,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, } kctl->private_free = snd_usb_mixer_elem_free; - ret = snd_usb_copy_string_desc(state, hdr->iClockSource, + ret = snd_usb_copy_string_desc(state->chip, hdr->iClockSource, name, sizeof(name)); if (ret > 0) snprintf(kctl->id.name, sizeof(kctl->id.name), @@ -1840,7 +1840,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (!len) - len = get_term_name(state, iterm, kctl->id.name, + len = get_term_name(state->chip, iterm, kctl->id.name, sizeof(kctl->id.name), 0); if (!len) len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1); @@ -2154,7 +2154,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); len = 0; if (nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(state->chip, + nameid, kctl->id.name, sizeof(kctl->id.name)); if (!len) @@ -2350,7 +2351,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, len = check_mapped_selector_name(state, unitid, i, namelist[i], MAX_ITEM_NAME_LEN); if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0) - len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0); + len = get_term_name(state->chip, &iterm, namelist[i], + MAX_ITEM_NAME_LEN, 0); if (! len) sprintf(namelist[i], "Input %u", i); } @@ -2372,12 +2374,12 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, /* if iSelector is given, use it */ nameid = uac_selector_unit_iSelector(desc); if (nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(state->chip, nameid, kctl->id.name, sizeof(kctl->id.name)); /* ... or pick up the terminal name at next */ if (!len) - len = get_term_name(state, &state->oterm, + len = get_term_name(state->chip, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 0); /* ... or use the fixed string "USB" as the last resort */ if (!len) -- cgit v1.2.2 From 17156f23e93c0f59e06dd2aaffd06221341caaee Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Fri, 4 May 2018 04:24:04 +0300 Subject: ALSA: usb: add UAC3 BADD profiles support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently released USB Audio Class 3.0 specification contains BADD (Basic Audio Device Definition) document which describes pre-defined UAC3 configurations. BADD support is mandatory for UAC3 devices, it should be implemented as a separate USB device configuration. As per BADD document, class-specific descriptors shall not be included in the Device’s Configuration descriptor ("inferred"), but host can guess them from BADD profile number, number of endpoints and their max packed sizes. This patch adds support of all BADD profiles from the spec Signed-off-by: Ruslan Bilovol Tested-by: Jorge Sanjuan Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 296 insertions(+), 31 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 76417943ff85..4987982250d5 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -112,14 +112,12 @@ enum { #include "mixer_maps.c" static const struct usbmix_name_map * -find_map(struct mixer_build *state, int unitid, int control) +find_map(const struct usbmix_name_map *p, int unitid, int control) { - const struct usbmix_name_map *p = state->map; - if (!p) return NULL; - for (p = state->map; p->id; p++) { + for (; p->id; p++) { if (p->id == unitid && (!control || !p->control || control == p->control)) return p; @@ -1333,16 +1331,16 @@ static struct usb_feature_control_info *get_feature_control_info(int control) return NULL; } -static void build_feature_ctl(struct mixer_build *state, void *raw_desc, - unsigned int ctl_mask, int control, - struct usb_audio_term *iterm, int unitid, - int readonly_mask) +static void __build_feature_ctl(struct usb_mixer_interface *mixer, + const struct usbmix_name_map *imap, + unsigned int ctl_mask, int control, + struct usb_audio_term *iterm, + struct usb_audio_term *oterm, + int unitid, int nameid, int readonly_mask) { - struct uac_feature_unit_descriptor *desc = raw_desc; struct usb_feature_control_info *ctl_info; unsigned int len = 0; int mapped_name = 0; - int nameid = uac_feature_unit_iFeature(desc); struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; @@ -1353,14 +1351,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, return; } - map = find_map(state, unitid, control); + map = find_map(imap, unitid, control); if (check_ignored_ctl(map)) return; cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return; - snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); + snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); cval->control = control; cval->cmask = ctl_mask; @@ -1369,7 +1367,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kfree(cval); return; } - if (state->mixer->protocol == UAC_VERSION_1) + if (mixer->protocol == UAC_VERSION_1) cval->val_type = ctl_info->type; else /* UAC_VERSION_2 */ cval->val_type = ctl_info->type_uac2 >= 0 ? @@ -1398,7 +1396,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (!kctl) { - usb_audio_err(state->chip, "cannot malloc kcontrol\n"); + usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); kfree(cval); return; } @@ -1407,7 +1405,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; if (!len && nameid) - len = snd_usb_copy_string_desc(state->chip, nameid, + len = snd_usb_copy_string_desc(mixer->chip, nameid, kctl->id.name, sizeof(kctl->id.name)); switch (control) { @@ -1422,10 +1420,12 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, * - otherwise, anonymous name. */ if (!len) { - len = get_term_name(state->chip, iterm, kctl->id.name, - sizeof(kctl->id.name), 1); - if (!len) - len = get_term_name(state->chip, &state->oterm, + if (iterm) + len = get_term_name(mixer->chip, iterm, + kctl->id.name, + sizeof(kctl->id.name), 1); + if (!len && oterm) + len = get_term_name(mixer->chip, oterm, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) @@ -1434,15 +1434,15 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, } if (!mapped_name) - check_no_speaker_on_headset(kctl, state->mixer->chip->card); + check_no_speaker_on_headset(kctl, mixer->chip->card); /* * determine the stream direction: * if the connected output is USB stream, then it's likely a * capture stream. otherwise it should be playback (hopefully :) */ - if (!mapped_name && !(state->oterm.type >> 16)) { - if ((state->oterm.type & 0xff00) == 0x0100) + if (!mapped_name && oterm && !(oterm->type >> 16)) { + if ((oterm->type & 0xff00) == 0x0100) append_ctl_name(kctl, " Capture"); else append_ctl_name(kctl, " Playback"); @@ -1470,7 +1470,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, } } - snd_usb_mixer_fu_apply_quirk(state->mixer, cval, unitid, kctl); + snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl); range = (cval->max - cval->min) / cval->res; /* @@ -1479,21 +1479,41 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, * devices. It will definitively catch all buggy Logitech devices. */ if (range > 384) { - usb_audio_warn(state->chip, + usb_audio_warn(mixer->chip, "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.", range); - usb_audio_warn(state->chip, + usb_audio_warn(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d", cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); } - usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", + usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); snd_usb_mixer_add_control(&cval->head, kctl); } +static void build_feature_ctl(struct mixer_build *state, void *raw_desc, + unsigned int ctl_mask, int control, + struct usb_audio_term *iterm, int unitid, + int readonly_mask) +{ + struct uac_feature_unit_descriptor *desc = raw_desc; + int nameid = uac_feature_unit_iFeature(desc); + + __build_feature_ctl(state->mixer, state->map, ctl_mask, control, + iterm, &state->oterm, unitid, nameid, readonly_mask); +} + +static void build_feature_ctl_badd(struct usb_mixer_interface *mixer, + unsigned int ctl_mask, int control, int unitid, + const struct usbmix_name_map *badd_map) +{ + __build_feature_ctl(mixer, badd_map, ctl_mask, control, + NULL, NULL, unitid, 0, 0); +} + static void get_connector_control_name(struct mixer_build *state, struct usb_audio_term *term, bool is_input, char *name, int name_size) @@ -1807,7 +1827,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, struct snd_kcontrol *kctl; const struct usbmix_name_map *map; - map = find_map(state, unitid, 0); + map = find_map(state->map, unitid, 0); if (check_ignored_ctl(map)) return; @@ -2106,7 +2126,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, if (!(controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1)))) continue; - map = find_map(state, unitid, valinfo->control); + map = find_map(state->map, unitid, valinfo->control); if (check_ignored_ctl(map)) continue; cval = kzalloc(sizeof(*cval), GFP_KERNEL); @@ -2310,7 +2330,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, if (desc->bNrInPins == 1) /* only one ? nonsense! */ return 0; - map = find_map(state, unitid, 0); + map = find_map(state->map, unitid, 0); if (check_ignored_ctl(map)) return 0; @@ -2497,6 +2517,246 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) return 0; } +/* UAC3 predefined channels configuration */ +struct uac3_badd_profile { + int subclass; + const char *name; + int c_chmask; /* capture channels mask */ + int p_chmask; /* playback channels mask */ + int st_chmask; /* side tone mixing channel mask */ +}; + +static struct uac3_badd_profile uac3_badd_profiles[] = { + { + /* + * BAIF, BAOF or combination of both + * IN: Mono or Stereo cfg, Mono alt possible + * OUT: Mono or Stereo cfg, Mono alt possible + */ + .subclass = UAC3_FUNCTION_SUBCLASS_GENERIC_IO, + .name = "GENERIC IO", + .c_chmask = -1, /* dynamic channels */ + .p_chmask = -1, /* dynamic channels */ + }, + { + /* BAOF; Stereo only cfg, Mono alt possible */ + .subclass = UAC3_FUNCTION_SUBCLASS_HEADPHONE, + .name = "HEADPHONE", + .p_chmask = 3, + }, + { + /* BAOF; Mono or Stereo cfg, Mono alt possible */ + .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKER, + .name = "SPEAKER", + .p_chmask = -1, /* dynamic channels */ + }, + { + /* BAIF; Mono or Stereo cfg, Mono alt possible */ + .subclass = UAC3_FUNCTION_SUBCLASS_MICROPHONE, + .name = "MICROPHONE", + .c_chmask = -1, /* dynamic channels */ + }, + { + /* + * BAIOF topology + * IN: Mono only + * OUT: Mono or Stereo cfg, Mono alt possible + */ + .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET, + .name = "HEADSET", + .c_chmask = 1, + .p_chmask = -1, /* dynamic channels */ + .st_chmask = 1, + }, + { + /* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */ + .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER, + .name = "HEADSET ADAPTER", + .c_chmask = 1, + .p_chmask = 3, + .st_chmask = 1, + }, + { + /* BAIF + BAOF; IN: Mono only; OUT: Mono only */ + .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE, + .name = "SPEAKERPHONE", + .c_chmask = 1, + .p_chmask = 1, + }, + { 0 } /* terminator */ +}; + +static bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer, + struct uac3_badd_profile *f, + int c_chmask, int p_chmask) +{ + /* + * If both playback/capture channels are dynamic, make sure + * at least one channel is present + */ + if (f->c_chmask < 0 && f->p_chmask < 0) { + if (!c_chmask && !p_chmask) { + usb_audio_warn(mixer->chip, "BAAD %s: no channels?", + f->name); + return false; + } + return true; + } + + if ((f->c_chmask < 0 && !c_chmask) || + (f->c_chmask >= 0 && f->c_chmask != c_chmask)) { + usb_audio_warn(mixer->chip, "BAAD %s c_chmask mismatch", + f->name); + return false; + } + if ((f->p_chmask < 0 && !p_chmask) || + (f->p_chmask >= 0 && f->p_chmask != p_chmask)) { + usb_audio_warn(mixer->chip, "BAAD %s p_chmask mismatch", + f->name); + return false; + } + return true; +} + +/* + * create mixer controls for UAC3 BADD profiles + * + * UAC3 BADD device doesn't contain CS descriptors thus we will guess everything + * + * BADD device may contain Mixer Unit, which doesn't have any controls, skip it + */ +static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer, + int ctrlif) +{ + struct usb_device *dev = mixer->chip->dev; + struct usb_interface_assoc_descriptor *assoc; + int badd_profile = mixer->chip->badd_profile; + struct uac3_badd_profile *f; + const struct usbmix_ctl_map *map; + int p_chmask = 0, c_chmask = 0, st_chmask = 0; + int i; + + assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc; + + /* Detect BADD capture/playback channels from AS EP descriptors */ + for (i = 0; i < assoc->bInterfaceCount; i++) { + int intf = assoc->bFirstInterface + i; + + struct usb_interface *iface; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + unsigned int maxpacksize; + char dir_in; + int chmask, num; + + if (intf == ctrlif) + continue; + + iface = usb_ifnum_to_if(dev, intf); + num = iface->num_altsetting; + + if (num < 2) + return -EINVAL; + + /* + * The number of Channels in an AudioStreaming interface + * and the audio sample bit resolution (16 bits or 24 + * bits) can be derived from the wMaxPacketSize field in + * the Standard AS Audio Data Endpoint descriptor in + * Alternate Setting 1 + */ + alts = &iface->altsetting[1]; + altsd = get_iface_desc(alts); + + if (altsd->bNumEndpoints < 1) + return -EINVAL; + + /* check direction */ + dir_in = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN); + maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + + switch (maxpacksize) { + default: + usb_audio_err(mixer->chip, + "incorrect wMaxPacketSize 0x%x for BADD profile\n", + maxpacksize); + return -EINVAL; + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24: + chmask = 1; + break; + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: + chmask = 3; + break; + } + + if (dir_in) + c_chmask = chmask; + else + p_chmask = chmask; + } + + usb_audio_dbg(mixer->chip, + "UAC3 BADD profile 0x%x: detected c_chmask=%d p_chmask=%d\n", + badd_profile, c_chmask, p_chmask); + + /* check the mapping table */ + for (map = uac3_badd_usbmix_ctl_maps; map->id; map++) { + if (map->id == badd_profile) + break; + } + + if (!map->id) + return -EINVAL; + + for (f = uac3_badd_profiles; f->name; f++) { + if (badd_profile == f->subclass) + break; + } + if (!f->name) + return -EINVAL; + if (!uac3_badd_func_has_valid_channels(mixer, f, c_chmask, p_chmask)) + return -EINVAL; + st_chmask = f->st_chmask; + + /* Playback */ + if (p_chmask) { + /* Master channel, always writable */ + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, + UAC3_BADD_FU_ID2, map->map); + /* Mono/Stereo volume channels, always writable */ + build_feature_ctl_badd(mixer, p_chmask, UAC_FU_VOLUME, + UAC3_BADD_FU_ID2, map->map); + } + + /* Capture */ + if (c_chmask) { + /* Master channel, always writable */ + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, + UAC3_BADD_FU_ID5, map->map); + /* Mono/Stereo volume channels, always writable */ + build_feature_ctl_badd(mixer, c_chmask, UAC_FU_VOLUME, + UAC3_BADD_FU_ID5, map->map); + } + + /* Side tone-mixing */ + if (st_chmask) { + /* Master channel, always writable */ + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, + UAC3_BADD_FU_ID7, map->map); + /* Mono volume channel, always writable */ + build_feature_ctl_badd(mixer, 1, UAC_FU_VOLUME, + UAC3_BADD_FU_ID7, map->map); + } + + return 0; +} + /* * create mixer controls * @@ -2882,9 +3142,14 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, break; } - if ((err = snd_usb_mixer_controls(mixer)) < 0 || - (err = snd_usb_mixer_status_create(mixer)) < 0) + if (mixer->protocol == UAC_VERSION_3 && + chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { + if ((err = snd_usb_mixer_controls_badd(mixer, ctrlif)) < 0) + goto _error; + } else if ((err = snd_usb_mixer_controls(mixer)) < 0 || + (err = snd_usb_mixer_status_create(mixer)) < 0) { goto _error; + } err = create_keep_iface_ctl(mixer); if (err < 0) goto _error; -- cgit v1.2.2 From 6cfd839ae78ec3fac5ddbf7148155898727e90c3 Mon Sep 17 00:00:00 2001 From: Jorge Sanjuan Date: Fri, 11 May 2018 16:25:34 +0100 Subject: ALSA: usb-audio: UAC3. Add support for mixer unit. This adds support for the MIXER UNIT in UAC3. All the information is obtained from the (HIGH CAPABILITY) Cluster's header. We don't read the rest of the logical cluster to obtain the channel config as that wont make any difference in the current mixer behaviour. The name of the mixer unit is not yet requested as there is not support for the UAC3 Class Specific String requests. Tested in an UAC3 device working as a HEADSET with a basic mixer unit (same as the one in the BADD spec) with no controls. Signed-off-by: Jorge Sanjuan Reviewed-by: Ruslan Bilovol Tested-by: Ruslan Bilovol Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 7 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 173979e05e63..c4abb3bca2ad 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -716,6 +716,66 @@ static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iter return 0; } +/* + * Get logical cluster information for UAC3 devices. + */ +static int get_cluster_channels_v3(struct mixer_build *state, unsigned int cluster_id) +{ + struct uac3_cluster_header_descriptor c_header; + int err; + + err = snd_usb_ctl_msg(state->chip->dev, + usb_rcvctrlpipe(state->chip->dev, 0), + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + cluster_id, + snd_usb_ctrl_intf(state->chip), + &c_header, sizeof(c_header)); + if (err < 0) + goto error; + if (err != sizeof(c_header)) { + err = -EIO; + goto error; + } + + return c_header.bNrChannels; + +error: + usb_audio_err(state->chip, "cannot request logical cluster ID: %d (err: %d)\n", cluster_id, err); + return err; +} + +/* + * Get number of channels for a Mixer Unit. + */ +static int uac_mixer_unit_get_channels(struct mixer_build *state, + struct uac_mixer_unit_descriptor *desc) +{ + int mu_channels; + + if (desc->bLength < 11) + return -EINVAL; + if (!desc->bNrInPins) + return -EINVAL; + + switch (state->mixer->protocol) { + case UAC_VERSION_1: + case UAC_VERSION_2: + default: + mu_channels = uac_mixer_unit_bNrChannels(desc); + break; + case UAC_VERSION_3: + mu_channels = get_cluster_channels_v3(state, + uac3_mixer_unit_wClusterDescrID(desc)); + break; + } + + if (!mu_channels) + return -EINVAL; + + return mu_channels; +} + /* * parse the source unit recursively until it reaches to a terminal * or a branched unit. @@ -863,6 +923,18 @@ static int check_input_term(struct mixer_build *state, int id, term->name = le16_to_cpu(d->wClockSourceStr); return 0; } + case UAC3_MIXER_UNIT: { + struct uac_mixer_unit_descriptor *d = p1; + + err = uac_mixer_unit_get_channels(state, d); + if (err < 0) + return err; + + term->channels = err; + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + + return 0; + } default: return -ENODEV; } @@ -1826,11 +1898,10 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, */ static void build_mixer_unit_ctl(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc, - int in_pin, int in_ch, int unitid, - struct usb_audio_term *iterm) + int in_pin, int in_ch, int num_outs, + int unitid, struct usb_audio_term *iterm) { struct usb_mixer_elem_info *cval; - unsigned int num_outs = uac_mixer_unit_bNrChannels(desc); unsigned int i, len; struct snd_kcontrol *kctl; const struct usbmix_name_map *map; @@ -1907,14 +1978,17 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, int input_pins, num_ins, num_outs; int pin, ich, err; - if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || - !(num_outs = uac_mixer_unit_bNrChannels(desc))) { + err = uac_mixer_unit_get_channels(state, desc); + if (err < 0) { usb_audio_err(state->chip, "invalid MIXER UNIT descriptor %d\n", unitid); - return -EINVAL; + return err; } + num_outs = err; + input_pins = desc->bNrInPins; + num_ins = 0; ich = 0; for (pin = 0; pin < input_pins; pin++) { @@ -1941,7 +2015,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, } } if (ich_has_controls) - build_mixer_unit_ctl(state, desc, pin, ich, + build_mixer_unit_ctl(state, desc, pin, ich, num_outs, unitid, &iterm); } } -- cgit v1.2.2 From 1d38f5d828b45546e53095a172c77556642a8a04 Mon Sep 17 00:00:00 2001 From: Jorge Sanjuan Date: Fri, 11 May 2018 16:25:36 +0100 Subject: ALSA: usb-audio: UAC3 Add support for connector insertion. This adds support for the UAC3 insertion controls. The status is reported as a boolean value in the same way it used to do for UAC2. Hence, the presence of any connector in the response will make the control saying the jack is connected. The UAC2 support for this control has been moved to a dedicated control for connectors as both UAC2 and UAC3 follow a specific Control Request Parameter Block for this control. This parameter block for UAC3 could not be read in the same simplistic manner as in UAC2. This implementation is not requesting additional information from the HIGH CAPABILITY Connectors descriptor. Tested with an UAC3 device with UAC2 as legacy configuration. The connector status can be read with `amixer` and the interrupt is also caught with `alsactl monitor`. Signed-off-by: Jorge Sanjuan Reviewed-by: Ruslan Bilovol Tested-by: Ruslan Bilovol Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 14 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index c4abb3bca2ad..fb77847cbffc 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1328,6 +1328,51 @@ static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol, return 0; } +/* get the connectors status and report it as boolean type */ +static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + struct snd_usb_audio *chip = cval->head.mixer->chip; + int idx = 0, validx, ret, val; + + validx = cval->control << 8 | 0; + + ret = snd_usb_lock_shutdown(chip) ? -EIO : 0; + if (ret) + goto error; + + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); + if (cval->head.mixer->protocol == UAC_VERSION_2) { + struct uac2_connectors_ctl_blk uac2_conn; + + ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx, idx, &uac2_conn, sizeof(uac2_conn)); + val = !!uac2_conn.bNrChannels; + } else { /* UAC_VERSION_3 */ + struct uac3_insertion_ctl_blk uac3_conn; + + ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx, idx, &uac3_conn, sizeof(uac3_conn)); + val = !!uac3_conn.bmConInserted; + } + + snd_usb_unlock_shutdown(chip); + + if (ret < 0) { +error: + usb_audio_err(chip, + "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", + UAC_GET_CUR, validx, idx, cval->val_type); + return ret; + } + + ucontrol->value.integer.value[0] = val; + return 0; +} + static struct snd_kcontrol_new usb_feature_unit_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", /* will be filled later manually */ @@ -1358,6 +1403,15 @@ static struct snd_kcontrol_new usb_bool_master_control_ctl_ro = { .put = NULL, }; +static const struct snd_kcontrol_new usb_connector_ctl_ro = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", /* will be filled later manually */ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ctl_boolean_mono_info, + .get = mixer_ctl_connector_get, + .put = NULL, +}; + /* * This symbol is exported in order to allow the mixer quirks to * hook up to the standard feature unit control mechanism @@ -1626,17 +1680,25 @@ static void build_connector_control(struct mixer_build *state, return; snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id); /* - * The first byte from reading the UAC2_TE_CONNECTOR control returns the - * number of channels connected. This boolean ctl will simply report - * if any channels are connected or not. - * (Audio20_final.pdf Table 5-10: Connector Control CUR Parameter Block) + * UAC2: The first byte from reading the UAC2_TE_CONNECTOR control returns the + * number of channels connected. + * + * UAC3: The first byte specifies size of bitmap for the inserted controls. The + * following byte(s) specifies which connectors are inserted. + * + * This boolean ctl will simply report if any channels are connected + * or not. */ - cval->control = UAC2_TE_CONNECTOR; + if (state->mixer->protocol == UAC_VERSION_2) + cval->control = UAC2_TE_CONNECTOR; + else /* UAC_VERSION_3 */ + cval->control = UAC3_TE_INSERTION; + cval->val_type = USB_MIXER_BOOLEAN; cval->channels = 1; /* report true if any channel is connected */ cval->min = 0; cval->max = 1; - kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval); + kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval); if (!kctl) { usb_audio_err(state->chip, "cannot malloc kcontrol\n"); kfree(cval); @@ -1954,16 +2016,28 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid, void *raw_desc) { struct usb_audio_term iterm; - struct uac2_input_terminal_descriptor *d = raw_desc; + unsigned int control, bmctls, term_id; - check_input_term(state, d->bTerminalID, &iterm); if (state->mixer->protocol == UAC_VERSION_2) { - /* Check for jack detection. */ - if (uac_v2v3_control_is_readable(le16_to_cpu(d->bmControls), - UAC2_TE_CONNECTOR)) { - build_connector_control(state, &iterm, true); - } + struct uac2_input_terminal_descriptor *d_v2 = raw_desc; + control = UAC2_TE_CONNECTOR; + term_id = d_v2->bTerminalID; + bmctls = le16_to_cpu(d_v2->bmControls); + } else if (state->mixer->protocol == UAC_VERSION_3) { + struct uac3_input_terminal_descriptor *d_v3 = raw_desc; + control = UAC3_TE_INSERTION; + term_id = d_v3->bTerminalID; + bmctls = le32_to_cpu(d_v3->bmControls); + } else { + return 0; /* UAC1. No Insertion control */ } + + check_input_term(state, term_id, &iterm); + + /* Check for jack detection. */ + if (uac_v2v3_control_is_readable(bmctls, control)) + build_connector_control(state, &iterm, true); + return 0; } @@ -2554,7 +2628,7 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) } else { /* UAC_VERSION_3 */ switch (p1[2]) { case UAC_INPUT_TERMINAL: - return 0; /* NOP */ + return parse_audio_input_terminal(state, unitid, p1); case UAC3_MIXER_UNIT: return parse_audio_mixer_unit(state, unitid, p1); case UAC3_CLOCK_SOURCE: @@ -2932,6 +3006,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) err = parse_audio_unit(&state, desc->bCSourceID); if (err < 0 && err != -EINVAL) return err; + + if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls), + UAC3_TE_INSERTION)) { + build_connector_control(&state, &state.oterm, + false); + } } } -- cgit v1.2.2 From 710669455d9e6978336dcbcb220024fb64ec2d38 Mon Sep 17 00:00:00 2001 From: Jorge Sanjuan Date: Mon, 14 May 2018 12:03:42 +0100 Subject: ALSA: usb-audio: UAC3: Parse Input Terminal number of channels. Obtain the number of channels for the Input Terminal from the Logical Cluster Descriptor. This achieves a useful minimal parsing of this unit so it can be used in other units in the topology. Signed-off-by: Jorge Sanjuan Reviewed-by: Ruslan Bilovol Tested-by: Ruslan Bilovol Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index fb77847cbffc..bf74e7edc92b 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -902,8 +902,12 @@ static int check_input_term(struct mixer_build *state, int id, term->id = id; term->type = le16_to_cpu(d->wTerminalType); - /* REVISIT: UAC3 IT doesn't have channels/cfg */ - term->channels = 0; + err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID)); + if (err < 0) + return err; + term->channels = err; + + /* REVISIT: UAC3 IT doesn't have channels cfg */ term->chconfig = 0; term->name = le16_to_cpu(d->wTerminalDescrStr); -- cgit v1.2.2 From f25ecf8f987d51be388e53de7b9e0e5815acc10b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 May 2018 15:18:22 +0200 Subject: ALSA: usb-audio: Follow standard coding style Avoid if ((err = ...) style and expand to multiple lines instead. No change in the end result, but just the beautification. Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'sound/usb/mixer.c') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index bf74e7edc92b..898afd3001ea 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -598,7 +598,8 @@ int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, while (snd_ctl_find_id(mixer->chip->card, &kctl->id)) kctl->id.index++; - if ((err = snd_ctl_add(mixer->chip->card, kctl)) < 0) { + err = snd_ctl_add(mixer->chip->card, kctl); + if (err < 0) { usb_audio_dbg(mixer->chip, "cannot add control (err = %d)\n", err); return err; @@ -1850,7 +1851,8 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, } /* parse the source unit */ - if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0) + err = parse_audio_unit(state, hdr->bSourceID); + if (err < 0) return err; /* determine the input source type and name */ @@ -2270,7 +2272,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, } for (i = 0; i < num_ins; i++) { - if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0) + err = parse_audio_unit(state, desc->baSourceID[i]); + if (err < 0) return err; } @@ -2483,7 +2486,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, } for (i = 0; i < desc->bNrInPins; i++) { - if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0) + err = parse_audio_unit(state, desc->baSourceID[i]); + if (err < 0) return err; } @@ -3310,11 +3314,16 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, if (mixer->protocol == UAC_VERSION_3 && chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { - if ((err = snd_usb_mixer_controls_badd(mixer, ctrlif)) < 0) + err = snd_usb_mixer_controls_badd(mixer, ctrlif); + if (err < 0) + goto _error; + } else { + err = snd_usb_mixer_controls(mixer); + if (err < 0) + goto _error; + err = snd_usb_mixer_status_create(mixer); + if (err < 0) goto _error; - } else if ((err = snd_usb_mixer_controls(mixer)) < 0 || - (err = snd_usb_mixer_status_create(mixer)) < 0) { - goto _error; } err = create_keep_iface_ctl(mixer); if (err < 0) -- cgit v1.2.2