Received: by 2002:ac0:a594:0:0:0:0:0 with SMTP id m20-v6csp939136imm; Fri, 11 May 2018 08:37:13 -0700 (PDT) X-Google-Smtp-Source: AB8JxZp2Wcw1+1XHf46hF3qMHi6cIldAwgL+87hts0eZksFoJ16mmI4LIw4YgVp9E6ZN5PwyEjiq X-Received: by 2002:a17:902:5ac6:: with SMTP id g6-v6mr6061246plm.262.1526053033203; Fri, 11 May 2018 08:37:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526053033; cv=none; d=google.com; s=arc-20160816; b=SWWUiBMUww23G9Qa1Hff73FHq0uRQPDYsfQHnTZkJ6iaC6qK0o+dgswNkBbzQAFjd8 7sI0MsXzVYcm/MejAeSKKYe4Y7QRKF9vAM8VhuClRlPfpykxWMomtDdLH/PcgKnWFoXn 5gPzRRdzzPLv9QBvRytyLMKmX+MIBSGUP458qQbJ8/bRAYCH99jFLsw1vzybRe05C6MI +8TNaGd81TFZLUDvh/qPamR9dH8BvFGbJCiJDIjVgbJETtgoF29s7Dt3Vc+IAvGzhkNq zcu8PB1Djc6B0t4x4ASiHVh8gYvQT28SfwEvWrdWHBlDCTB5JQpWLJGMdqt67rYwzZWw kqxQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :content-language:in-reply-to:mime-version:user-agent:date :message-id:from:references:cc:to:subject:arc-authentication-results; bh=MdvIyyFywnZRhYBcgaCVBD9lMW+8e94TvWR8cWNVWYM=; b=iqXNHNd/GnlHt3LFOZ/HJk1yPu/BwyUmKwkZspAZCMsb9vOQ60eFzcSyI61EUkQIHF X+QhnzGHc2pzrSwqCYdpPs6MZiHJMp2zW3+CfiZJ88iNrlwpFtzx3e8GsmRf+sBAvupN lGGbJRqupuGux8DkP/3GUtwtIjkwf0Pa58gUwtRIlXM38bKEHoP4YDjtYOLM93xxUBDb pfzn4j7G+2nBnowGZYv1/rUvWUF+ySS8vQ02FAWsAHnAD68UmQa9Z5ML06ND6Yjm00EG bUhPXXFEvlgNHZzwv8cjRPnA8OzSrycWr8jvObcQwqZ/Rwht+IK1QGz5JBXSxBbiO9Ko sFlA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=codethink.co.uk Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id bc2-v6si3304587plb.43.2018.05.11.08.36.58; Fri, 11 May 2018 08:37:13 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=codethink.co.uk Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751321AbeEKPgj (ORCPT + 99 others); Fri, 11 May 2018 11:36:39 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:52164 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750726AbeEKPgi (ORCPT ); Fri, 11 May 2018 11:36:38 -0400 Received: from 167-98-27-229.cust-167.exponential-e.net ([167.98.27.229] helo=[10.24.4.248]) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1fHA60-0002rb-IB; Fri, 11 May 2018 16:36:36 +0100 Subject: Re: [PATCH v2 7/7] ALSA: usb: add UAC3 BADD profiles support To: Ruslan Bilovol , Takashi Iwai Cc: Andrew Chant , Greg Kroah-Hartman , alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org References: <1525397044-15080-1-git-send-email-ruslan.bilovol@gmail.com> <1525397044-15080-8-git-send-email-ruslan.bilovol@gmail.com> From: Jorge Message-ID: <61f13c53-fda2-65da-b405-41bdd17d04a5@codethink.co.uk> Date: Fri, 11 May 2018 16:36:36 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.7.0 MIME-Version: 1.0 In-Reply-To: <1525397044-15080-8-git-send-email-ruslan.bilovol@gmail.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 04/05/18 02:24, Ruslan Bilovol wrote: > 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 > --- > sound/usb/card.c | 14 +++ > sound/usb/clock.c | 9 +- > sound/usb/mixer.c | 327 ++++++++++++++++++++++++++++++++++++++++++++----- > sound/usb/mixer_maps.c | 65 ++++++++++ > sound/usb/stream.c | 83 +++++++++++-- > sound/usb/usbaudio.h | 2 + > 6 files changed, 459 insertions(+), 41 deletions(-) > > diff --git a/sound/usb/card.c b/sound/usb/card.c > index 0d7a5d7..f6c3c1c 100644 > --- a/sound/usb/card.c > +++ b/sound/usb/card.c > @@ -307,6 +307,20 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) > return -EINVAL; > } > > + if (protocol == UAC_VERSION_3) { > + int badd = assoc->bFunctionSubClass; > + > + if (badd != UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 && > + (badd < UAC3_FUNCTION_SUBCLASS_GENERIC_IO || > + badd > UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE)) { > + dev_err(&dev->dev, > + "Unsupported UAC3 BADD profile\n"); > + return -EINVAL; > + } > + > + chip->badd_profile = badd; > + } > + > for (i = 0; i < assoc->bInterfaceCount; i++) { > int intf = assoc->bFirstInterface + i; > > diff --git a/sound/usb/clock.c b/sound/usb/clock.c > index 0b030d8..17673f3 100644 > --- a/sound/usb/clock.c > +++ b/sound/usb/clock.c > @@ -587,8 +587,15 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, > default: > return set_sample_rate_v1(chip, iface, alts, fmt, rate); > > - case UAC_VERSION_2: > case UAC_VERSION_3: > + if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { > + if (rate != UAC3_BADD_SAMPLING_RATE) > + return -ENXIO; > + else > + return 0; > + } > + /* fall through */ > + case UAC_VERSION_2: > return set_sample_rate_v2v3(chip, iface, alts, fmt, rate); > } > } > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index e280354..d98bc3f 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 > * > @@ -2883,9 +3143,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; > diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c > index eaa03ac..71069e1 100644 > --- a/sound/usb/mixer_maps.c > +++ b/sound/usb/mixer_maps.c > @@ -485,3 +485,68 @@ struct usbmix_ctl_map { > { 0 } /* terminator */ > }; > > +/* > + * Control map entries for UAC3 BADD profiles > + */ > + > +static struct usbmix_name_map uac3_badd_generic_io_map[] = { > + { UAC3_BADD_FU_ID2, "Generic Out Playback" }, > + { UAC3_BADD_FU_ID5, "Generic In Capture" }, > + { 0 } /* terminator */ > +}; > +static struct usbmix_name_map uac3_badd_headphone_map[] = { > + { UAC3_BADD_FU_ID2, "Headphone Playback" }, > + { 0 } /* terminator */ > +}; > +static struct usbmix_name_map uac3_badd_speaker_map[] = { > + { UAC3_BADD_FU_ID2, "Speaker Playback" }, > + { 0 } /* terminator */ > +}; > +static struct usbmix_name_map uac3_badd_microphone_map[] = { > + { UAC3_BADD_FU_ID5, "Mic Capture" }, > + { 0 } /* terminator */ > +}; > +/* Covers also 'headset adapter' profile */ > +static struct usbmix_name_map uac3_badd_headset_map[] = { > + { UAC3_BADD_FU_ID2, "Headset Playback" }, > + { UAC3_BADD_FU_ID5, "Headset Capture" }, > + { UAC3_BADD_FU_ID7, "Sidetone Mixing" }, > + { 0 } /* terminator */ > +}; > +static struct usbmix_name_map uac3_badd_speakerphone_map[] = { > + { UAC3_BADD_FU_ID2, "Speaker Playback" }, > + { UAC3_BADD_FU_ID5, "Mic Capture" }, > + { 0 } /* terminator */ > +}; > + > +static struct usbmix_ctl_map uac3_badd_usbmix_ctl_maps[] = { > + { > + .id = UAC3_FUNCTION_SUBCLASS_GENERIC_IO, > + .map = uac3_badd_generic_io_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_HEADPHONE, > + .map = uac3_badd_headphone_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_SPEAKER, > + .map = uac3_badd_speaker_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_MICROPHONE, > + .map = uac3_badd_microphone_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_HEADSET, > + .map = uac3_badd_headset_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER, > + .map = uac3_badd_headset_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE, > + .map = uac3_badd_speakerphone_map, > + }, > + { 0 } /* terminator */ > +}; > diff --git a/sound/usb/stream.c b/sound/usb/stream.c > index 764be07..de8bbb3 100644 > --- a/sound/usb/stream.c > +++ b/sound/usb/stream.c > @@ -817,15 +817,67 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, > struct uac3_input_terminal_descriptor *input_term; > struct uac3_output_terminal_descriptor *output_term; > struct uac3_cluster_header_descriptor *cluster; > - struct uac3_as_header_descriptor *as; > + struct uac3_as_header_descriptor *as = NULL; > struct uac3_hc_descriptor_header hc_header; > struct snd_pcm_chmap_elem *chmap; > + unsigned char badd_profile; > + u64 badd_formats = 0; > unsigned int num_channels; > struct audioformat *fp; > u16 cluster_id, wLength; > int clock = 0; > int err; > > + badd_profile = chip->badd_profile; > + > + if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { > + unsigned int maxpacksize = > + le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); > + > + switch (maxpacksize) { > + default: > + dev_err(&dev->dev, > + "%u:%d : incorrect wMaxPacketSize for BADD profile\n", > + iface_no, altno); > + return NULL; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: > + badd_formats = SNDRV_PCM_FMTBIT_S16_LE; > + num_channels = 1; > + break; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24: > + badd_formats = SNDRV_PCM_FMTBIT_S24_3LE; > + num_channels = 1; > + break; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: > + badd_formats = SNDRV_PCM_FMTBIT_S16_LE; > + num_channels = 2; > + break; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: > + badd_formats = SNDRV_PCM_FMTBIT_S24_3LE; > + num_channels = 2; > + break; > + } > + > + chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); > + if (!chmap) > + return ERR_PTR(-ENOMEM); > + > + if (num_channels == 1) { > + chmap->map[0] = SNDRV_CHMAP_MONO; > + } else { > + chmap->map[0] = SNDRV_CHMAP_FL; > + chmap->map[1] = SNDRV_CHMAP_FR; > + } > + > + chmap->channels = num_channels; > + clock = UAC3_BADD_CS_ID9; > + goto found_clock; > + } > + > as = snd_usb_find_csint_desc(alts->extra, alts->extralen, > NULL, UAC_AS_GENERAL); > if (!as) { > @@ -931,16 +983,29 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, > if (!fp) > return ERR_PTR(-ENOMEM); > > - fp->attributes = parse_uac_endpoint_attributes(chip, alts, > - UAC_VERSION_3, > - iface_no); > fp->chmap = chmap; > > - /* ok, let's parse further... */ > - if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { > - kfree(fp->rate_table); > - kfree(fp); > - return NULL; > + if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { > + fp->attributes = 0; /* No attributes */ > + > + fp->fmt_type = UAC_FORMAT_TYPE_I; > + fp->formats = badd_formats; > + > + fp->nr_rates = 0; /* SNDRV_PCM_RATE_CONTINUOUS */ > + fp->rate_min = UAC3_BADD_SAMPLING_RATE; > + fp->rate_max = UAC3_BADD_SAMPLING_RATE; > + fp->rates = SNDRV_PCM_RATE_CONTINUOUS; > + > + } else { > + fp->attributes = parse_uac_endpoint_attributes(chip, alts, > + UAC_VERSION_3, > + iface_no); > + /* ok, let's parse further... */ > + if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { > + kfree(fp->rate_table); > + kfree(fp); > + return NULL; > + } > } > > return fp; > diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h > index 1cb6b3e..7b28cbd 100644 > --- a/sound/usb/usbaudio.h > +++ b/sound/usb/usbaudio.h > @@ -49,6 +49,8 @@ struct snd_usb_audio { > int num_suspended_intf; > int sample_rate_read_error; > > + int badd_profile; /* UAC3 BADD profile */ > + > struct list_head pcm_list; /* list of pcm streams */ > struct list_head ep_list; /* list of audio-related endpoints */ > int pcm_devs; >