diff mbox

[4/4] ALSA: usb: add UAC3 BADD profiles support

Message ID 1523658266-2259-5-git-send-email-ruslan.bilovol@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ruslan Bilovol April 13, 2018, 10:24 p.m. UTC
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 <ruslan.bilovol@gmail.com>
---
 sound/usb/card.c       |  14 +++
 sound/usb/clock.c      |   9 +-
 sound/usb/mixer.c      | 313 +++++++++++++++++++++++++++++++++++++++++++++++--
 sound/usb/mixer_maps.c |  65 ++++++++++
 sound/usb/stream.c     |  83 +++++++++++--
 sound/usb/usbaudio.h   |   2 +
 6 files changed, 466 insertions(+), 20 deletions(-)

Comments

Jorge Sanjuan April 14, 2018, 5:55 p.m. UTC | #1
On 2018-04-13 23: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.

Right. I would have thought that, since BADD is a subset of UAC3, it may 
be simpler to fill the Class Specific descriptors buffer and let the 
UAC3 path intact as it would result in the same behavior (for UAC3 and 
BADD configs) without the need to add that much code to the mixer, which 
is already quite big.

In the patch I proposed [1], the Class Specific buffer is filled once 
with the BADD descriptors, which are already UAC3 compliant, so the 
driver would handle the rest in the same way it would do with an UAC3 
configuration.

I will keep an eye on this as I'd need to do some work based on this 
instead.

[1] https://www.spinics.net/lists/alsa-devel/msg71617.html

Thanks,

Jorge

> 
> This patch adds support of all BADD profiles from the spec
> 
> Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
> ---
>  sound/usb/card.c       |  14 +++
>  sound/usb/clock.c      |   9 +-
>  sound/usb/mixer.c      | 313 
> +++++++++++++++++++++++++++++++++++++++++++++++--
>  sound/usb/mixer_maps.c |  65 ++++++++++
>  sound/usb/stream.c     |  83 +++++++++++--
>  sound/usb/usbaudio.h   |   2 +
>  6 files changed, 466 insertions(+), 20 deletions(-)
> 
> diff --git a/sound/usb/card.c b/sound/usb/card.c
> index 4d866bd..47ebc50 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 301ad61..e5c3b0d 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,6 +1331,76 @@ static struct usb_feature_control_info
> *get_feature_control_info(int control)
>  	return NULL;
>  }
> 
> +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)
> +{
> +	struct usb_feature_control_info *ctl_info;
> +	unsigned int len = 0;
> +	struct snd_kcontrol *kctl;
> +	struct usb_mixer_elem_info *cval;
> +	const struct usbmix_name_map *map;
> +
> +	map = find_map(badd_map, unitid, control);
> +	if (!map)
> +		return;
> +
> +	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
> +	if (!cval)
> +		return;
> +	snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
> +	cval->control = control;
> +	cval->cmask = ctl_mask;
> +
> +	ctl_info = get_feature_control_info(control);
> +	if (!ctl_info) {
> +		kfree(cval);
> +		return;
> +	}
> +	cval->val_type = ctl_info->type;
> +
> +	if (ctl_mask == 0) {
> +		cval->channels = 1;	/* master channel */
> +	} else {
> +		int i, c = 0;
> +
> +		for (i = 0; i < 2; i++)
> +			if (ctl_mask & (1 << i))
> +				c++;
> +		cval->channels = c;
> +	}
> +
> +	kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
> +
> +	if (!kctl) {
> +		usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
> +		kfree(cval);
> +		return;
> +	}
> +	kctl->private_free = snd_usb_mixer_elem_free;
> +	len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
> +
> +	append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " 
> Volume");
> +
> +	/* get min/max values */
> +	get_min_max_with_quirks(cval, 0, kctl);
> +
> +	if (control == UAC_FU_VOLUME) {
> +		check_mapped_dB(map, cval);
> +		if (cval->dBmin < cval->dBmax || !cval->initialized) {
> +			kctl->tlv.c = snd_usb_mixer_vol_tlv;
> +			kctl->vd[0].access |=
> +				SNDRV_CTL_ELEM_ACCESS_TLV_READ |
> +				SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
> +		}
> +	}
> +
> +	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,
> @@ -1353,7 +1421,7 @@ static void build_feature_ctl(struct mixer_build
> *state, void *raw_desc,
>  		return;
>  	}
> 
> -	map = find_map(state, unitid, control);
> +	map = find_map(state->map, unitid, control);
>  	if (check_ignored_ctl(map))
>  		return;
> 
> @@ -1806,7 +1874,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;
> 
> @@ -2105,7 +2173,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);
> @@ -2308,7 +2376,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;
> 
> @@ -2495,6 +2563,226 @@ static int snd_usb_mixer_dev_free(struct
> snd_device *device)
>  }
> 
>  /*
> + * 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;
> +	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;
> +
> +		if (intf != ctrlif) {
> +			struct usb_interface *iface;
> +			struct usb_host_interface *alts;
> +			struct usb_interface_descriptor *altsd;
> +			unsigned int maxpacksize;
> +			char dir_in;
> +			int chmask, num;
> +
> +			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) {
> +			mixer->ignore_ctl_error = map->ignore_ctl_error;
> +			break;
> +		}
> +	}
> +
> +	if (!map->id)
> +		return -EINVAL;
> +
> +	switch (badd_profile) {
> +	default:
> +		return -EINVAL;
> +	case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
> +		/*
> +		 * BAIF, BAOF or combination of both
> +		 * IN: Mono or Stereo cfg, Mono alt possible
> +		 * OUT: Mono or Stereo cfg, Mono alt possible
> +		 */
> +		/* c_chmask := DYNAMIC */
> +		/* p_chmask := DYNAMIC */
> +		if (!c_chmask && !p_chmask) {
> +			usb_audio_err(mixer->chip,
> +				"BADD GENERIC_IO profile: no channels?\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case UAC3_FUNCTION_SUBCLASS_HEADPHONE:
> +		/* BAOF; Stereo only cfg, Mono alt possible */
> +		if (p_chmask != 3)
> +			usb_audio_warn(mixer->chip,
> +				"BADD HEADPHONE p_chmask mismatch: expected 3 actual %d\n",
> +				p_chmask);
> +		p_chmask = 3;
> +		break;
> +	case UAC3_FUNCTION_SUBCLASS_SPEAKER:
> +		/* BAOF; Mono or Stereo cfg, Mono alt possible */
> +		/* p_chmask := DYNAMIC */
> +		if (!p_chmask) {
> +			usb_audio_err(mixer->chip,
> +				"BADD SPEAKER profile: no playback channels?\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case UAC3_FUNCTION_SUBCLASS_MICROPHONE:
> +		/* BAIF; Mono or Stereo cfg, Mono alt possible */
> +		/* c_chmask := DYNAMIC */
> +		if (!c_chmask) {
> +			usb_audio_err(mixer->chip,
> +				"BADD MICROPHONE profile: no capture channels?\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case UAC3_FUNCTION_SUBCLASS_HEADSET:
> +		/*
> +		 * BAIOF
> +		 * IN: Mono only
> +		 * OUT: Mono or Stereo cfg, Mono alt possible
> +		 */
> +		if (c_chmask != 1)
> +			usb_audio_warn(mixer->chip,
> +				"BADD HEADSET c_chmask mismatch: expected 1 actual %d\n",
> +				c_chmask);
> +		c_chmask = 1;
> +		st_chmask = 1;
> +		/* p_chmask := DYNAMIC */
> +		if (!p_chmask) {
> +			usb_audio_err(mixer->chip,
> +				"BADD HEADSET profile: no playback channels?\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER:
> +		/* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */
> +		if (c_chmask != 1)
> +			usb_audio_warn(mixer->chip,
> +				"BADD HEADSET_ADAPTER c_chmask mismatch: expected 1 actual %d\n",
> +				c_chmask);
> +		if (p_chmask != 3)
> +			usb_audio_warn(mixer->chip,
> +				"BADD HEADSET_ADAPTER p_chmask mismatch: expected 3 actual %d\n",
> +				p_chmask);
> +		c_chmask = 1;
> +		st_chmask = 1;
> +		p_chmask = 3;
> +		break;
> +	case UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE:
> +		/* BAIF + BAOF; IN: Mono only; OUT: Mono only */
> +		if (c_chmask != 1)
> +			usb_audio_warn(mixer->chip,
> +				"BADD SPEAKERPHONE c_chmask mismatch: expected 1 actual %d\n",
> +				c_chmask);
> +		if (p_chmask != 1)
> +			usb_audio_warn(mixer->chip,
> +				"BADD SPEAKERPHONE p_chmask mismatch: expected 1 actual %d\n",
> +				p_chmask);
> +		c_chmask = 1;
> +		p_chmask = 1;
> +		break;
> +	}
> +
> +	/* 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
>   *
>   * walk through all UAC_OUTPUT_TERMINAL descriptors to search for 
> mixers
> @@ -2838,9 +3126,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;
> +	}
> 
>  	snd_usb_mixer_apply_create_quirk(mixer);
> 
> diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
> index 9038b2e..13f03c8 100644
> --- a/sound/usb/mixer_maps.c
> +++ b/sound/usb/mixer_maps.c
> @@ -482,3 +482,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, "Side Tone 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 586d664..ea5a13e0 100644
> --- a/sound/usb/stream.c
> +++ b/sound/usb/stream.c
> @@ -798,15 +798,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) {
> @@ -922,18 +974,31 @@ static int parse_uac_endpoint_attributes(struct
> snd_usb_audio *chip,
>  	if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
>  		fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
>  				* (fp->maxpacksize & 0x7ff);
> -	fp->attributes = parse_uac_endpoint_attributes(chip, alts,
> -						       UAC_VERSION_3,
> -						       iface_no);
>  	fp->clock = clock;
>  	fp->chmap = chmap;
>  	INIT_LIST_HEAD(&fp->list);
> 
> -	/* 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 4d5c89a..1bb5e2c 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;
kernel test robot April 16, 2018, 9:05 a.m. UTC | #2
Hi Ruslan,

Thank you for the patch! Perhaps something to improve:

url:    https://github.com/0day-ci/linux/commits/Ruslan-Bilovol/USB-Audio-Device-Class-3-0-BADD-profiles-support/20180414-182703
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next

smatch warnings:
sound/usb/stream.c:964 snd_usb_get_audioformat_uac3() warn: possible memory leak of 'chmap'

# https://github.com/0day-ci/linux/commit/7e255927f3e697fce36dcd48f205a7997771ed4d
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 7e255927f3e697fce36dcd48f205a7997771ed4d
vim +/chmap +964 sound/usb/stream.c

9a2fe9b801 Ruslan Bilovol 2018-03-21   791  
1145e3d13b Ruslan Bilovol 2018-04-14   792  static struct audioformat *
1145e3d13b Ruslan Bilovol 2018-04-14   793  snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
1145e3d13b Ruslan Bilovol 2018-04-14   794  			     struct usb_host_interface *alts,
1145e3d13b Ruslan Bilovol 2018-04-14   795  			     int iface_no, int altno, int stream)
1145e3d13b Ruslan Bilovol 2018-04-14   796  {
1145e3d13b Ruslan Bilovol 2018-04-14   797  	struct usb_device *dev = chip->dev;
9a2fe9b801 Ruslan Bilovol 2018-03-21   798  	struct uac3_input_terminal_descriptor *input_term;
9a2fe9b801 Ruslan Bilovol 2018-03-21   799  	struct uac3_output_terminal_descriptor *output_term;
9a2fe9b801 Ruslan Bilovol 2018-03-21   800  	struct uac3_cluster_header_descriptor *cluster;
7e255927f3 Ruslan Bilovol 2018-04-14   801  	struct uac3_as_header_descriptor *as = NULL;
9a2fe9b801 Ruslan Bilovol 2018-03-21   802  	struct uac3_hc_descriptor_header hc_header;
1145e3d13b Ruslan Bilovol 2018-04-14   803  	struct snd_pcm_chmap_elem *chmap;
7e255927f3 Ruslan Bilovol 2018-04-14   804  	unsigned char badd_profile;
7e255927f3 Ruslan Bilovol 2018-04-14   805  	u64 badd_formats  = 0;
1145e3d13b Ruslan Bilovol 2018-04-14   806  	unsigned int num_channels;
1145e3d13b Ruslan Bilovol 2018-04-14   807  	struct audioformat *fp;
9a2fe9b801 Ruslan Bilovol 2018-03-21   808  	u16 cluster_id, wLength;
1145e3d13b Ruslan Bilovol 2018-04-14   809  	int clock = 0;
1145e3d13b Ruslan Bilovol 2018-04-14   810  	int err;
9a2fe9b801 Ruslan Bilovol 2018-03-21   811  
7e255927f3 Ruslan Bilovol 2018-04-14   812  	badd_profile = chip->badd_profile;
7e255927f3 Ruslan Bilovol 2018-04-14   813  
7e255927f3 Ruslan Bilovol 2018-04-14   814  	if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
7e255927f3 Ruslan Bilovol 2018-04-14   815  		unsigned int maxpacksize =
7e255927f3 Ruslan Bilovol 2018-04-14   816  			le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
7e255927f3 Ruslan Bilovol 2018-04-14   817  
7e255927f3 Ruslan Bilovol 2018-04-14   818  		switch (maxpacksize) {
7e255927f3 Ruslan Bilovol 2018-04-14   819  		default:
7e255927f3 Ruslan Bilovol 2018-04-14   820  			dev_err(&dev->dev,
7e255927f3 Ruslan Bilovol 2018-04-14   821  				"%u:%d : incorrect wMaxPacketSize for BADD profile\n",
7e255927f3 Ruslan Bilovol 2018-04-14   822  				iface_no, altno);
7e255927f3 Ruslan Bilovol 2018-04-14   823  			return NULL;
7e255927f3 Ruslan Bilovol 2018-04-14   824  		case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
7e255927f3 Ruslan Bilovol 2018-04-14   825  		case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16:
7e255927f3 Ruslan Bilovol 2018-04-14   826  			badd_formats = SNDRV_PCM_FMTBIT_S16_LE;
7e255927f3 Ruslan Bilovol 2018-04-14   827  			num_channels = 1;
7e255927f3 Ruslan Bilovol 2018-04-14   828  			break;
7e255927f3 Ruslan Bilovol 2018-04-14   829  		case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24:
7e255927f3 Ruslan Bilovol 2018-04-14   830  		case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
7e255927f3 Ruslan Bilovol 2018-04-14   831  			badd_formats = SNDRV_PCM_FMTBIT_S24_3LE;
7e255927f3 Ruslan Bilovol 2018-04-14   832  			num_channels = 1;
7e255927f3 Ruslan Bilovol 2018-04-14   833  			break;
7e255927f3 Ruslan Bilovol 2018-04-14   834  		case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
7e255927f3 Ruslan Bilovol 2018-04-14   835  		case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16:
7e255927f3 Ruslan Bilovol 2018-04-14   836  			badd_formats = SNDRV_PCM_FMTBIT_S16_LE;
7e255927f3 Ruslan Bilovol 2018-04-14   837  			num_channels = 2;
7e255927f3 Ruslan Bilovol 2018-04-14   838  			break;
7e255927f3 Ruslan Bilovol 2018-04-14   839  		case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24:
7e255927f3 Ruslan Bilovol 2018-04-14   840  		case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24:
7e255927f3 Ruslan Bilovol 2018-04-14   841  			badd_formats = SNDRV_PCM_FMTBIT_S24_3LE;
7e255927f3 Ruslan Bilovol 2018-04-14   842  			num_channels = 2;
7e255927f3 Ruslan Bilovol 2018-04-14   843  			break;
7e255927f3 Ruslan Bilovol 2018-04-14   844  		}
7e255927f3 Ruslan Bilovol 2018-04-14   845  
7e255927f3 Ruslan Bilovol 2018-04-14   846  		chmap = kzalloc(sizeof(*chmap), GFP_KERNEL);
7e255927f3 Ruslan Bilovol 2018-04-14   847  		if (!chmap)
7e255927f3 Ruslan Bilovol 2018-04-14   848  			return ERR_PTR(-ENOMEM);
7e255927f3 Ruslan Bilovol 2018-04-14   849  
7e255927f3 Ruslan Bilovol 2018-04-14   850  		if (num_channels == 1) {
7e255927f3 Ruslan Bilovol 2018-04-14   851  			chmap->map[0] = SNDRV_CHMAP_MONO;
7e255927f3 Ruslan Bilovol 2018-04-14   852  		} else {
7e255927f3 Ruslan Bilovol 2018-04-14   853  			chmap->map[0] = SNDRV_CHMAP_FL;
7e255927f3 Ruslan Bilovol 2018-04-14   854  			chmap->map[1] = SNDRV_CHMAP_FR;
7e255927f3 Ruslan Bilovol 2018-04-14   855  		}
7e255927f3 Ruslan Bilovol 2018-04-14   856  
7e255927f3 Ruslan Bilovol 2018-04-14   857  		chmap->channels = num_channels;
7e255927f3 Ruslan Bilovol 2018-04-14   858  		clock = UAC3_BADD_CS_ID9;
7e255927f3 Ruslan Bilovol 2018-04-14   859  		goto found_clock;
7e255927f3 Ruslan Bilovol 2018-04-14   860  	}
7e255927f3 Ruslan Bilovol 2018-04-14   861  
1145e3d13b Ruslan Bilovol 2018-04-14   862  	as = snd_usb_find_csint_desc(alts->extra, alts->extralen,
9a2fe9b801 Ruslan Bilovol 2018-03-21   863  				     NULL, UAC_AS_GENERAL);
9a2fe9b801 Ruslan Bilovol 2018-03-21   864  	if (!as) {
9a2fe9b801 Ruslan Bilovol 2018-03-21   865  		dev_err(&dev->dev,
9a2fe9b801 Ruslan Bilovol 2018-03-21   866  			"%u:%d : UAC_AS_GENERAL descriptor not found\n",
9a2fe9b801 Ruslan Bilovol 2018-03-21   867  			iface_no, altno);
1145e3d13b Ruslan Bilovol 2018-04-14   868  		return NULL;
e8e8babf56 Daniel Mack    2011-09-12   869  	}
e8e8babf56 Daniel Mack    2011-09-12   870  
9a2fe9b801 Ruslan Bilovol 2018-03-21   871  	if (as->bLength < sizeof(*as)) {
9a2fe9b801 Ruslan Bilovol 2018-03-21   872  		dev_err(&dev->dev,
9a2fe9b801 Ruslan Bilovol 2018-03-21   873  			"%u:%d : invalid UAC_AS_GENERAL desc\n",
9a2fe9b801 Ruslan Bilovol 2018-03-21   874  			iface_no, altno);
1145e3d13b Ruslan Bilovol 2018-04-14   875  		return NULL;
9a2fe9b801 Ruslan Bilovol 2018-03-21   876  	}
9a2fe9b801 Ruslan Bilovol 2018-03-21   877  
9a2fe9b801 Ruslan Bilovol 2018-03-21   878  	cluster_id = le16_to_cpu(as->wClusterDescrID);
9a2fe9b801 Ruslan Bilovol 2018-03-21   879  	if (!cluster_id) {
9a2fe9b801 Ruslan Bilovol 2018-03-21   880  		dev_err(&dev->dev,
9a2fe9b801 Ruslan Bilovol 2018-03-21   881  			"%u:%d : no cluster descriptor\n",
9a2fe9b801 Ruslan Bilovol 2018-03-21   882  			iface_no, altno);
1145e3d13b Ruslan Bilovol 2018-04-14   883  		return NULL;
9a2fe9b801 Ruslan Bilovol 2018-03-21   884  	}
9a2fe9b801 Ruslan Bilovol 2018-03-21   885  
9a2fe9b801 Ruslan Bilovol 2018-03-21   886  	/*
9a2fe9b801 Ruslan Bilovol 2018-03-21   887  	 * Get number of channels and channel map through
9a2fe9b801 Ruslan Bilovol 2018-03-21   888  	 * High Capability Cluster Descriptor
9a2fe9b801 Ruslan Bilovol 2018-03-21   889  	 *
9a2fe9b801 Ruslan Bilovol 2018-03-21   890  	 * First step: get High Capability header and
9a2fe9b801 Ruslan Bilovol 2018-03-21   891  	 * read size of Cluster Descriptor
9a2fe9b801 Ruslan Bilovol 2018-03-21   892  	 */
9a2fe9b801 Ruslan Bilovol 2018-03-21   893  	err = snd_usb_ctl_msg(chip->dev,
9a2fe9b801 Ruslan Bilovol 2018-03-21   894  			usb_rcvctrlpipe(chip->dev, 0),
9a2fe9b801 Ruslan Bilovol 2018-03-21   895  			UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
9a2fe9b801 Ruslan Bilovol 2018-03-21   896  			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
9a2fe9b801 Ruslan Bilovol 2018-03-21   897  			cluster_id,
9a2fe9b801 Ruslan Bilovol 2018-03-21   898  			snd_usb_ctrl_intf(chip),
9a2fe9b801 Ruslan Bilovol 2018-03-21   899  			&hc_header, sizeof(hc_header));
9a2fe9b801 Ruslan Bilovol 2018-03-21   900  	if (err < 0)
1145e3d13b Ruslan Bilovol 2018-04-14   901  		return ERR_PTR(err);
9a2fe9b801 Ruslan Bilovol 2018-03-21   902  	else if (err != sizeof(hc_header)) {
9a2fe9b801 Ruslan Bilovol 2018-03-21   903  		dev_err(&dev->dev,
9a2fe9b801 Ruslan Bilovol 2018-03-21   904  			"%u:%d : can't get High Capability descriptor\n",
9a2fe9b801 Ruslan Bilovol 2018-03-21   905  			iface_no, altno);
1145e3d13b Ruslan Bilovol 2018-04-14   906  		return ERR_PTR(-EIO);
9a2fe9b801 Ruslan Bilovol 2018-03-21   907  	}
9a2fe9b801 Ruslan Bilovol 2018-03-21   908  
9a2fe9b801 Ruslan Bilovol 2018-03-21   909  	/*
9a2fe9b801 Ruslan Bilovol 2018-03-21   910  	 * Second step: allocate needed amount of memory
9a2fe9b801 Ruslan Bilovol 2018-03-21   911  	 * and request Cluster Descriptor
9a2fe9b801 Ruslan Bilovol 2018-03-21   912  	 */
9a2fe9b801 Ruslan Bilovol 2018-03-21   913  	wLength = le16_to_cpu(hc_header.wLength);
9a2fe9b801 Ruslan Bilovol 2018-03-21   914  	cluster = kzalloc(wLength, GFP_KERNEL);
9a2fe9b801 Ruslan Bilovol 2018-03-21   915  	if (!cluster)
1145e3d13b Ruslan Bilovol 2018-04-14   916  		return ERR_PTR(-ENOMEM);
9a2fe9b801 Ruslan Bilovol 2018-03-21   917  	err = snd_usb_ctl_msg(chip->dev,
9a2fe9b801 Ruslan Bilovol 2018-03-21   918  			usb_rcvctrlpipe(chip->dev, 0),
9a2fe9b801 Ruslan Bilovol 2018-03-21   919  			UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
9a2fe9b801 Ruslan Bilovol 2018-03-21   920  			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
9a2fe9b801 Ruslan Bilovol 2018-03-21   921  			cluster_id,
9a2fe9b801 Ruslan Bilovol 2018-03-21   922  			snd_usb_ctrl_intf(chip),
9a2fe9b801 Ruslan Bilovol 2018-03-21   923  			cluster, wLength);
9a2fe9b801 Ruslan Bilovol 2018-03-21   924  	if (err < 0) {
9a2fe9b801 Ruslan Bilovol 2018-03-21   925  		kfree(cluster);
1145e3d13b Ruslan Bilovol 2018-04-14   926  		return ERR_PTR(err);
9a2fe9b801 Ruslan Bilovol 2018-03-21   927  	} else if (err != wLength) {
9a2fe9b801 Ruslan Bilovol 2018-03-21   928  		dev_err(&dev->dev,
9a2fe9b801 Ruslan Bilovol 2018-03-21   929  			"%u:%d : can't get Cluster Descriptor\n",
9a2fe9b801 Ruslan Bilovol 2018-03-21   930  			iface_no, altno);
9a2fe9b801 Ruslan Bilovol 2018-03-21   931  		kfree(cluster);
1145e3d13b Ruslan Bilovol 2018-04-14   932  		return ERR_PTR(-EIO);
9a2fe9b801 Ruslan Bilovol 2018-03-21   933  	}
9a2fe9b801 Ruslan Bilovol 2018-03-21   934  
9a2fe9b801 Ruslan Bilovol 2018-03-21   935  	num_channels = cluster->bNrChannels;
1145e3d13b Ruslan Bilovol 2018-04-14   936  	chmap = convert_chmap_v3(cluster);
9a2fe9b801 Ruslan Bilovol 2018-03-21   937  	kfree(cluster);
9a2fe9b801 Ruslan Bilovol 2018-03-21   938  
1145e3d13b Ruslan Bilovol 2018-04-14   939  	/*
1145e3d13b Ruslan Bilovol 2018-04-14   940  	 * lookup the terminal associated to this interface
1145e3d13b Ruslan Bilovol 2018-04-14   941  	 * to extract the clock
1145e3d13b Ruslan Bilovol 2018-04-14   942  	 */
1145e3d13b Ruslan Bilovol 2018-04-14   943  	input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
9a2fe9b801 Ruslan Bilovol 2018-03-21   944  							    as->bTerminalLink);
9a2fe9b801 Ruslan Bilovol 2018-03-21   945  	if (input_term) {
9a2fe9b801 Ruslan Bilovol 2018-03-21   946  		clock = input_term->bCSourceID;
1145e3d13b Ruslan Bilovol 2018-04-14   947  		goto found_clock;
9a2fe9b801 Ruslan Bilovol 2018-03-21   948  	}
9a2fe9b801 Ruslan Bilovol 2018-03-21   949  
9a2fe9b801 Ruslan Bilovol 2018-03-21   950  	output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
9a2fe9b801 Ruslan Bilovol 2018-03-21   951  							     as->bTerminalLink);
9a2fe9b801 Ruslan Bilovol 2018-03-21   952  	if (output_term) {
9a2fe9b801 Ruslan Bilovol 2018-03-21   953  		clock = output_term->bCSourceID;
1145e3d13b Ruslan Bilovol 2018-04-14   954  		goto found_clock;
9a2fe9b801 Ruslan Bilovol 2018-03-21   955  	}
9a2fe9b801 Ruslan Bilovol 2018-03-21   956  
1145e3d13b Ruslan Bilovol 2018-04-14   957  	dev_err(&dev->dev, "%u:%d : bogus bTerminalLink %d\n",
9a2fe9b801 Ruslan Bilovol 2018-03-21   958  			iface_no, altno, as->bTerminalLink);
1145e3d13b Ruslan Bilovol 2018-04-14   959  	return NULL;
e8e8babf56 Daniel Mack    2011-09-12   960  
1145e3d13b Ruslan Bilovol 2018-04-14   961  found_clock:
e8e8babf56 Daniel Mack    2011-09-12   962  	fp = kzalloc(sizeof(*fp), GFP_KERNEL);
9ecb2406de Markus Elfring 2017-08-11   963  	if (!fp)
1145e3d13b Ruslan Bilovol 2018-04-14  @964  		return ERR_PTR(-ENOMEM);
e8e8babf56 Daniel Mack    2011-09-12   965  
e8e8babf56 Daniel Mack    2011-09-12   966  	fp->iface = iface_no;
e8e8babf56 Daniel Mack    2011-09-12   967  	fp->altsetting = altno;
e8e8babf56 Daniel Mack    2011-09-12   968  	fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
e8e8babf56 Daniel Mack    2011-09-12   969  	fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
e8e8babf56 Daniel Mack    2011-09-12   970  	fp->datainterval = snd_usb_parse_datainterval(chip, alts);
1145e3d13b Ruslan Bilovol 2018-04-14   971  	fp->protocol = UAC_VERSION_3;
e8e8babf56 Daniel Mack    2011-09-12   972  	fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
e8e8babf56 Daniel Mack    2011-09-12   973  	fp->channels = num_channels;
e8e8babf56 Daniel Mack    2011-09-12   974  	if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
e8e8babf56 Daniel Mack    2011-09-12   975  		fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
e8e8babf56 Daniel Mack    2011-09-12   976  				* (fp->maxpacksize & 0x7ff);
e8e8babf56 Daniel Mack    2011-09-12   977  	fp->clock = clock;
1145e3d13b Ruslan Bilovol 2018-04-14   978  	fp->chmap = chmap;
836b34a935 Vladis Dronov  2016-03-31   979  	INIT_LIST_HEAD(&fp->list);
e8e8babf56 Daniel Mack    2011-09-12   980  
7e255927f3 Ruslan Bilovol 2018-04-14   981  	if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
7e255927f3 Ruslan Bilovol 2018-04-14   982  		fp->attributes = 0; /* No attributes */
7e255927f3 Ruslan Bilovol 2018-04-14   983  
7e255927f3 Ruslan Bilovol 2018-04-14   984  		fp->fmt_type = UAC_FORMAT_TYPE_I;
7e255927f3 Ruslan Bilovol 2018-04-14   985  		fp->formats = badd_formats;
7e255927f3 Ruslan Bilovol 2018-04-14   986  
7e255927f3 Ruslan Bilovol 2018-04-14   987  		fp->nr_rates = 0;	/* SNDRV_PCM_RATE_CONTINUOUS */
7e255927f3 Ruslan Bilovol 2018-04-14   988  		fp->rate_min = UAC3_BADD_SAMPLING_RATE;
7e255927f3 Ruslan Bilovol 2018-04-14   989  		fp->rate_max = UAC3_BADD_SAMPLING_RATE;
7e255927f3 Ruslan Bilovol 2018-04-14   990  		fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
7e255927f3 Ruslan Bilovol 2018-04-14   991  
7e255927f3 Ruslan Bilovol 2018-04-14   992  	} else {
7e255927f3 Ruslan Bilovol 2018-04-14   993  		fp->attributes = parse_uac_endpoint_attributes(chip, alts,
7e255927f3 Ruslan Bilovol 2018-04-14   994  							       UAC_VERSION_3,
7e255927f3 Ruslan Bilovol 2018-04-14   995  							       iface_no);
e8e8babf56 Daniel Mack    2011-09-12   996  		/* ok, let's parse further... */
1145e3d13b Ruslan Bilovol 2018-04-14   997  		if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
e8e8babf56 Daniel Mack    2011-09-12   998  			kfree(fp->rate_table);
e8e8babf56 Daniel Mack    2011-09-12   999  			kfree(fp);
1145e3d13b Ruslan Bilovol 2018-04-14  1000  			return NULL;
e8e8babf56 Daniel Mack    2011-09-12  1001  		}
7e255927f3 Ruslan Bilovol 2018-04-14  1002  	}
9a2fe9b801 Ruslan Bilovol 2018-03-21  1003  
1145e3d13b Ruslan Bilovol 2018-04-14  1004  	return fp;
1145e3d13b Ruslan Bilovol 2018-04-14  1005  }
9a2fe9b801 Ruslan Bilovol 2018-03-21  1006  

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Ruslan Bilovol April 18, 2018, 12:41 a.m. UTC | #3
On Sat, Apr 14, 2018 at 8:55 PM, Jorge Sanjuan
<jorge.sanjuan@codethink.co.uk> wrote:
>
>
> On 2018-04-13 23: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.
>
>
> Right. I would have thought that, since BADD is a subset of UAC3, it may be
> simpler to fill the Class Specific descriptors buffer and let the UAC3 path
> intact as it would result in the same behavior (for UAC3 and BADD configs)
> without the need to add that much code to the mixer, which is already quite
> big.
>
> In the patch I proposed [1], the Class Specific buffer is filled once with
> the BADD descriptors, which are already UAC3 compliant, so the driver would
> handle the rest in the same way it would do with an UAC3 configuration.

That was looking as a good idea to me as well when I seen patch [1] first
time. However, after thinking a bit more, I realized that in mixer.c we
just need to initialize from one to three feature units for any BADD profile.
Mentioned Feature Units are simple and can have only Volume/Mute controls.
We also have nothing to do with Mixer Unit (which exists in BAIOF
topology) since it doesn't have any controls.
Most of the code there is to just detect all possible combinations of
channels, topologies (BAIF, BAOF, BAIOF, BAIF+BAOF) for BADD profiles,
and to add some meaningful names for feature unit controls, which
are missing in the descriptors.
The only change in the mixer.c I'm unhappy with is to have separate
build_feature_ctl_badd() function that is nothing else but simplified
version of build_feature_ctl() function; but I already have an idea how to
reuse original one for BADD case.

Changes to stream.c are very simple and straightforward, almost all
values are common/predefined for all BADD profiles except channel
numbers and sample size.

So as a boottom line direct changes to mixer/stream code seems more
easy and understandable in this particular case rather than generating
all needed class-specific desriptors. And we have all BADD profiles
support in this quite short patch.

Thanks,
Ruslan

>
> I will keep an eye on this as I'd need to do some work based on this
> instead.
>
> [1] https://www.spinics.net/lists/alsa-devel/msg71617.html
>
> Thanks,
>
> Jorge
>
>>
>> This patch adds support of all BADD profiles from the spec
>>
>> Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
>> ---
>>  sound/usb/card.c       |  14 +++
>>  sound/usb/clock.c      |   9 +-
>>  sound/usb/mixer.c      | 313
>> +++++++++++++++++++++++++++++++++++++++++++++++--
>>  sound/usb/mixer_maps.c |  65 ++++++++++
>>  sound/usb/stream.c     |  83 +++++++++++--
>>  sound/usb/usbaudio.h   |   2 +
>>  6 files changed, 466 insertions(+), 20 deletions(-)
>>
>> diff --git a/sound/usb/card.c b/sound/usb/card.c
>> index 4d866bd..47ebc50 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 301ad61..e5c3b0d 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,6 +1331,76 @@ static struct usb_feature_control_info
>> *get_feature_control_info(int control)
>>         return NULL;
>>  }
>>
>> +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)
>> +{
>> +       struct usb_feature_control_info *ctl_info;
>> +       unsigned int len = 0;
>> +       struct snd_kcontrol *kctl;
>> +       struct usb_mixer_elem_info *cval;
>> +       const struct usbmix_name_map *map;
>> +
>> +       map = find_map(badd_map, unitid, control);
>> +       if (!map)
>> +               return;
>> +
>> +       cval = kzalloc(sizeof(*cval), GFP_KERNEL);
>> +       if (!cval)
>> +               return;
>> +       snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
>> +       cval->control = control;
>> +       cval->cmask = ctl_mask;
>> +
>> +       ctl_info = get_feature_control_info(control);
>> +       if (!ctl_info) {
>> +               kfree(cval);
>> +               return;
>> +       }
>> +       cval->val_type = ctl_info->type;
>> +
>> +       if (ctl_mask == 0) {
>> +               cval->channels = 1;     /* master channel */
>> +       } else {
>> +               int i, c = 0;
>> +
>> +               for (i = 0; i < 2; i++)
>> +                       if (ctl_mask & (1 << i))
>> +                               c++;
>> +               cval->channels = c;
>> +       }
>> +
>> +       kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
>> +
>> +       if (!kctl) {
>> +               usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
>> +               kfree(cval);
>> +               return;
>> +       }
>> +       kctl->private_free = snd_usb_mixer_elem_free;
>> +       len = check_mapped_name(map, kctl->id.name,
>> sizeof(kctl->id.name));
>> +
>> +       append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : "
>> Volume");
>> +
>> +       /* get min/max values */
>> +       get_min_max_with_quirks(cval, 0, kctl);
>> +
>> +       if (control == UAC_FU_VOLUME) {
>> +               check_mapped_dB(map, cval);
>> +               if (cval->dBmin < cval->dBmax || !cval->initialized) {
>> +                       kctl->tlv.c = snd_usb_mixer_vol_tlv;
>> +                       kctl->vd[0].access |=
>> +                               SNDRV_CTL_ELEM_ACCESS_TLV_READ |
>> +                               SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
>> +               }
>> +       }
>> +
>> +       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,
>> @@ -1353,7 +1421,7 @@ static void build_feature_ctl(struct mixer_build
>> *state, void *raw_desc,
>>                 return;
>>         }
>>
>> -       map = find_map(state, unitid, control);
>> +       map = find_map(state->map, unitid, control);
>>         if (check_ignored_ctl(map))
>>                 return;
>>
>> @@ -1806,7 +1874,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;
>>
>> @@ -2105,7 +2173,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);
>> @@ -2308,7 +2376,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;
>>
>> @@ -2495,6 +2563,226 @@ static int snd_usb_mixer_dev_free(struct
>> snd_device *device)
>>  }
>>
>>  /*
>> + * 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;
>> +       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;
>> +
>> +               if (intf != ctrlif) {
>> +                       struct usb_interface *iface;
>> +                       struct usb_host_interface *alts;
>> +                       struct usb_interface_descriptor *altsd;
>> +                       unsigned int maxpacksize;
>> +                       char dir_in;
>> +                       int chmask, num;
>> +
>> +                       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) {
>> +                       mixer->ignore_ctl_error = map->ignore_ctl_error;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       if (!map->id)
>> +               return -EINVAL;
>> +
>> +       switch (badd_profile) {
>> +       default:
>> +               return -EINVAL;
>> +       case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
>> +               /*
>> +                * BAIF, BAOF or combination of both
>> +                * IN: Mono or Stereo cfg, Mono alt possible
>> +                * OUT: Mono or Stereo cfg, Mono alt possible
>> +                */
>> +               /* c_chmask := DYNAMIC */
>> +               /* p_chmask := DYNAMIC */
>> +               if (!c_chmask && !p_chmask) {
>> +                       usb_audio_err(mixer->chip,
>> +                               "BADD GENERIC_IO profile: no
>> channels?\n");
>> +                       return -EINVAL;
>> +               }
>> +               break;
>> +       case UAC3_FUNCTION_SUBCLASS_HEADPHONE:
>> +               /* BAOF; Stereo only cfg, Mono alt possible */
>> +               if (p_chmask != 3)
>> +                       usb_audio_warn(mixer->chip,
>> +                               "BADD HEADPHONE p_chmask mismatch:
>> expected 3 actual %d\n",
>> +                               p_chmask);
>> +               p_chmask = 3;
>> +               break;
>> +       case UAC3_FUNCTION_SUBCLASS_SPEAKER:
>> +               /* BAOF; Mono or Stereo cfg, Mono alt possible */
>> +               /* p_chmask := DYNAMIC */
>> +               if (!p_chmask) {
>> +                       usb_audio_err(mixer->chip,
>> +                               "BADD SPEAKER profile: no playback
>> channels?\n");
>> +                       return -EINVAL;
>> +               }
>> +               break;
>> +       case UAC3_FUNCTION_SUBCLASS_MICROPHONE:
>> +               /* BAIF; Mono or Stereo cfg, Mono alt possible */
>> +               /* c_chmask := DYNAMIC */
>> +               if (!c_chmask) {
>> +                       usb_audio_err(mixer->chip,
>> +                               "BADD MICROPHONE profile: no capture
>> channels?\n");
>> +                       return -EINVAL;
>> +               }
>> +               break;
>> +       case UAC3_FUNCTION_SUBCLASS_HEADSET:
>> +               /*
>> +                * BAIOF
>> +                * IN: Mono only
>> +                * OUT: Mono or Stereo cfg, Mono alt possible
>> +                */
>> +               if (c_chmask != 1)
>> +                       usb_audio_warn(mixer->chip,
>> +                               "BADD HEADSET c_chmask mismatch: expected
>> 1 actual %d\n",
>> +                               c_chmask);
>> +               c_chmask = 1;
>> +               st_chmask = 1;
>> +               /* p_chmask := DYNAMIC */
>> +               if (!p_chmask) {
>> +                       usb_audio_err(mixer->chip,
>> +                               "BADD HEADSET profile: no playback
>> channels?\n");
>> +                       return -EINVAL;
>> +               }
>> +               break;
>> +       case UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER:
>> +               /* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt
>> possible */
>> +               if (c_chmask != 1)
>> +                       usb_audio_warn(mixer->chip,
>> +                               "BADD HEADSET_ADAPTER c_chmask mismatch:
>> expected 1 actual %d\n",
>> +                               c_chmask);
>> +               if (p_chmask != 3)
>> +                       usb_audio_warn(mixer->chip,
>> +                               "BADD HEADSET_ADAPTER p_chmask mismatch:
>> expected 3 actual %d\n",
>> +                               p_chmask);
>> +               c_chmask = 1;
>> +               st_chmask = 1;
>> +               p_chmask = 3;
>> +               break;
>> +       case UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE:
>> +               /* BAIF + BAOF; IN: Mono only; OUT: Mono only */
>> +               if (c_chmask != 1)
>> +                       usb_audio_warn(mixer->chip,
>> +                               "BADD SPEAKERPHONE c_chmask mismatch:
>> expected 1 actual %d\n",
>> +                               c_chmask);
>> +               if (p_chmask != 1)
>> +                       usb_audio_warn(mixer->chip,
>> +                               "BADD SPEAKERPHONE p_chmask mismatch:
>> expected 1 actual %d\n",
>> +                               p_chmask);
>> +               c_chmask = 1;
>> +               p_chmask = 1;
>> +               break;
>> +       }
>> +
>> +       /* 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
>>   *
>>   * walk through all UAC_OUTPUT_TERMINAL descriptors to search for mixers
>> @@ -2838,9 +3126,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;
>> +       }
>>
>>         snd_usb_mixer_apply_create_quirk(mixer);
>>
>> diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
>> index 9038b2e..13f03c8 100644
>> --- a/sound/usb/mixer_maps.c
>> +++ b/sound/usb/mixer_maps.c
>> @@ -482,3 +482,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, "Side Tone 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 586d664..ea5a13e0 100644
>> --- a/sound/usb/stream.c
>> +++ b/sound/usb/stream.c
>> @@ -798,15 +798,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) {
>> @@ -922,18 +974,31 @@ static int parse_uac_endpoint_attributes(struct
>> snd_usb_audio *chip,
>>         if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
>>                 fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
>>                                 * (fp->maxpacksize & 0x7ff);
>> -       fp->attributes = parse_uac_endpoint_attributes(chip, alts,
>> -                                                      UAC_VERSION_3,
>> -                                                      iface_no);
>>         fp->clock = clock;
>>         fp->chmap = chmap;
>>         INIT_LIST_HEAD(&fp->list);
>>
>> -       /* 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 4d5c89a..1bb5e2c 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;
Andrew Chant April 19, 2018, 9:42 a.m. UTC | #4
On Sat, Apr 14, 2018 at 6:24 AM, Ruslan Bilovol
<ruslan.bilovol@gmail.com> 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 <ruslan.bilovol@gmail.com>
> ---
>  sound/usb/card.c       |  14 +++
>  sound/usb/clock.c      |   9 +-
>  sound/usb/mixer.c      | 313 +++++++++++++++++++++++++++++++++++++++++++++++--
>  sound/usb/mixer_maps.c |  65 ++++++++++
>  sound/usb/stream.c     |  83 +++++++++++--
>  sound/usb/usbaudio.h   |   2 +
>  6 files changed, 466 insertions(+), 20 deletions(-)

> --- a/sound/usb/mixer_maps.c
> +++ b/sound/usb/mixer_maps.c
> @@ -482,3 +482,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, "Side Tone Mixing" },
Can you please call this "Sidetone"?
This better matches other Sidetone control names in the sound tree and
makes it compatible with existing Android userspace usage.
Takashi Iwai April 19, 2018, 10:19 a.m. UTC | #5
On Sat, 14 Apr 2018 00:24:26 +0200,
Ruslan Bilovol wrote:
> 
> +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)
> +{
....
> +	kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
> +
> +	if (!kctl) {
> +		usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");

No need for error message after malloc failure.  The kernel is already
chatty about 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;
> +	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;
> +
> +		if (intf != ctrlif) {

In this case, it's better to skip like

		if (intf == ctrlif)
			continue;

so that we can save an indentation for the whole long block.

> +	switch (badd_profile) {
> +	default:
> +		return -EINVAL;
> +	case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
> +		/*
> +		 * BAIF, BAOF or combination of both
> +		 * IN: Mono or Stereo cfg, Mono alt possible
> +		 * OUT: Mono or Stereo cfg, Mono alt possible
> +		 */
> +		/* c_chmask := DYNAMIC */
> +		/* p_chmask := DYNAMIC */
> +		if (!c_chmask && !p_chmask) {
> +			usb_audio_err(mixer->chip,
> +				"BADD GENERIC_IO profile: no channels?\n");
> +			return -EINVAL;
> +		}
> +		break;

Maybe we can simplify the whole switch/case with a table lookup.
For example,

	for (f = uac3_func_tables; f->name; f++) {
		if (badd_profile == f->subclass)
			break;
	}
	if (!f->name)
		return -EINVAL;
	if (!uac3_func_has_valid_channels(mixer, f, c_chmask, p_chmask))
		return -EINVAL;
	st_chmask = f->st_chmask;

and in uac3_func_has_valid_channels(),

static bool uac3_func_has_valid_channels()
{
	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;
}


thanks,

Takashi
Ruslan Bilovol April 23, 2018, 7:47 p.m. UTC | #6
On Thu, Apr 19, 2018 at 12:42 PM, Andrew Chant <achant@google.com> wrote:
> On Sat, Apr 14, 2018 at 6:24 AM, Ruslan Bilovol
> <ruslan.bilovol@gmail.com> 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 <ruslan.bilovol@gmail.com>
>> ---
>>  sound/usb/card.c       |  14 +++
>>  sound/usb/clock.c      |   9 +-
>>  sound/usb/mixer.c      | 313 +++++++++++++++++++++++++++++++++++++++++++++++--
>>  sound/usb/mixer_maps.c |  65 ++++++++++
>>  sound/usb/stream.c     |  83 +++++++++++--
>>  sound/usb/usbaudio.h   |   2 +
>>  6 files changed, 466 insertions(+), 20 deletions(-)
>
>> --- a/sound/usb/mixer_maps.c
>> +++ b/sound/usb/mixer_maps.c
>> @@ -482,3 +482,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, "Side Tone Mixing" },
> Can you please call this "Sidetone"?
> This better matches other Sidetone control names in the sound tree and
> makes it compatible with existing Android userspace usage.

I have no any objections, "Side Tone" was in UAC3 documentation, but
"Sidetone" is OK too, so will change it

Thanks,
Ruslan
Ruslan Bilovol April 24, 2018, 7:51 a.m. UTC | #7
On Thu, Apr 19, 2018 at 1:19 PM, Takashi Iwai <tiwai@suse.de> wrote:
> On Sat, 14 Apr 2018 00:24:26 +0200,
> Ruslan Bilovol wrote:
>>
>> +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)
>> +{
> ....
>> +     kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
>> +
>> +     if (!kctl) {
>> +             usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
>
> No need for error message after malloc failure.  The kernel is already
> chatty about it.

Okay. BTW, I'm trying to avoid separate badd variant of
build_feature_ctl(), so this most probably will go away.

There are many existing places in usb-snd which can be cleaned
up though.

>
>
>> +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;
>> +     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;
>> +
>> +             if (intf != ctrlif) {
>
> In this case, it's better to skip like
>
>                 if (intf == ctrlif)
>                         continue;
>
> so that we can save an indentation for the whole long block.

Good point, will do it

>
>> +     switch (badd_profile) {
>> +     default:
>> +             return -EINVAL;
>> +     case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
>> +             /*
>> +              * BAIF, BAOF or combination of both
>> +              * IN: Mono or Stereo cfg, Mono alt possible
>> +              * OUT: Mono or Stereo cfg, Mono alt possible
>> +              */
>> +             /* c_chmask := DYNAMIC */
>> +             /* p_chmask := DYNAMIC */
>> +             if (!c_chmask && !p_chmask) {
>> +                     usb_audio_err(mixer->chip,
>> +                             "BADD GENERIC_IO profile: no channels?\n");
>> +                     return -EINVAL;
>> +             }
>> +             break;
>
> Maybe we can simplify the whole switch/case with a table lookup.

Yes, it should be possible, I'll implent it that way

Thanks,
Ruslan

> For example,
>
>         for (f = uac3_func_tables; f->name; f++) {
>                 if (badd_profile == f->subclass)
>                         break;
>         }
>         if (!f->name)
>                 return -EINVAL;
>         if (!uac3_func_has_valid_channels(mixer, f, c_chmask, p_chmask))
>                 return -EINVAL;
>         st_chmask = f->st_chmask;
>
> and in uac3_func_has_valid_channels(),
>
> static bool uac3_func_has_valid_channels()
> {
>         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;
> }
>
>
> thanks,
>
> Takashi
diff mbox

Patch

diff --git a/sound/usb/card.c b/sound/usb/card.c
index 4d866bd..47ebc50 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 301ad61..e5c3b0d 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,6 +1331,76 @@  static struct usb_feature_control_info *get_feature_control_info(int control)
 	return NULL;
 }
 
+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)
+{
+	struct usb_feature_control_info *ctl_info;
+	unsigned int len = 0;
+	struct snd_kcontrol *kctl;
+	struct usb_mixer_elem_info *cval;
+	const struct usbmix_name_map *map;
+
+	map = find_map(badd_map, unitid, control);
+	if (!map)
+		return;
+
+	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+	if (!cval)
+		return;
+	snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
+	cval->control = control;
+	cval->cmask = ctl_mask;
+
+	ctl_info = get_feature_control_info(control);
+	if (!ctl_info) {
+		kfree(cval);
+		return;
+	}
+	cval->val_type = ctl_info->type;
+
+	if (ctl_mask == 0) {
+		cval->channels = 1;	/* master channel */
+	} else {
+		int i, c = 0;
+
+		for (i = 0; i < 2; i++)
+			if (ctl_mask & (1 << i))
+				c++;
+		cval->channels = c;
+	}
+
+	kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+
+	if (!kctl) {
+		usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
+		kfree(cval);
+		return;
+	}
+	kctl->private_free = snd_usb_mixer_elem_free;
+	len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
+
+	append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume");
+
+	/* get min/max values */
+	get_min_max_with_quirks(cval, 0, kctl);
+
+	if (control == UAC_FU_VOLUME) {
+		check_mapped_dB(map, cval);
+		if (cval->dBmin < cval->dBmax || !cval->initialized) {
+			kctl->tlv.c = snd_usb_mixer_vol_tlv;
+			kctl->vd[0].access |=
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+				SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+		}
+	}
+
+	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,
@@ -1353,7 +1421,7 @@  static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 		return;
 	}
 
-	map = find_map(state, unitid, control);
+	map = find_map(state->map, unitid, control);
 	if (check_ignored_ctl(map))
 		return;
 
@@ -1806,7 +1874,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;
 
@@ -2105,7 +2173,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);
@@ -2308,7 +2376,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;
 
@@ -2495,6 +2563,226 @@  static int snd_usb_mixer_dev_free(struct snd_device *device)
 }
 
 /*
+ * 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;
+	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;
+
+		if (intf != ctrlif) {
+			struct usb_interface *iface;
+			struct usb_host_interface *alts;
+			struct usb_interface_descriptor *altsd;
+			unsigned int maxpacksize;
+			char dir_in;
+			int chmask, num;
+
+			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) {
+			mixer->ignore_ctl_error = map->ignore_ctl_error;
+			break;
+		}
+	}
+
+	if (!map->id)
+		return -EINVAL;
+
+	switch (badd_profile) {
+	default:
+		return -EINVAL;
+	case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
+		/*
+		 * BAIF, BAOF or combination of both
+		 * IN: Mono or Stereo cfg, Mono alt possible
+		 * OUT: Mono or Stereo cfg, Mono alt possible
+		 */
+		/* c_chmask := DYNAMIC */
+		/* p_chmask := DYNAMIC */
+		if (!c_chmask && !p_chmask) {
+			usb_audio_err(mixer->chip,
+				"BADD GENERIC_IO profile: no channels?\n");
+			return -EINVAL;
+		}
+		break;
+	case UAC3_FUNCTION_SUBCLASS_HEADPHONE:
+		/* BAOF; Stereo only cfg, Mono alt possible */
+		if (p_chmask != 3)
+			usb_audio_warn(mixer->chip,
+				"BADD HEADPHONE p_chmask mismatch: expected 3 actual %d\n",
+				p_chmask);
+		p_chmask = 3;
+		break;
+	case UAC3_FUNCTION_SUBCLASS_SPEAKER:
+		/* BAOF; Mono or Stereo cfg, Mono alt possible */
+		/* p_chmask := DYNAMIC */
+		if (!p_chmask) {
+			usb_audio_err(mixer->chip,
+				"BADD SPEAKER profile: no playback channels?\n");
+			return -EINVAL;
+		}
+		break;
+	case UAC3_FUNCTION_SUBCLASS_MICROPHONE:
+		/* BAIF; Mono or Stereo cfg, Mono alt possible */
+		/* c_chmask := DYNAMIC */
+		if (!c_chmask) {
+			usb_audio_err(mixer->chip,
+				"BADD MICROPHONE profile: no capture channels?\n");
+			return -EINVAL;
+		}
+		break;
+	case UAC3_FUNCTION_SUBCLASS_HEADSET:
+		/*
+		 * BAIOF
+		 * IN: Mono only
+		 * OUT: Mono or Stereo cfg, Mono alt possible
+		 */
+		if (c_chmask != 1)
+			usb_audio_warn(mixer->chip,
+				"BADD HEADSET c_chmask mismatch: expected 1 actual %d\n",
+				c_chmask);
+		c_chmask = 1;
+		st_chmask = 1;
+		/* p_chmask := DYNAMIC */
+		if (!p_chmask) {
+			usb_audio_err(mixer->chip,
+				"BADD HEADSET profile: no playback channels?\n");
+			return -EINVAL;
+		}
+		break;
+	case UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER:
+		/* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */
+		if (c_chmask != 1)
+			usb_audio_warn(mixer->chip,
+				"BADD HEADSET_ADAPTER c_chmask mismatch: expected 1 actual %d\n",
+				c_chmask);
+		if (p_chmask != 3)
+			usb_audio_warn(mixer->chip,
+				"BADD HEADSET_ADAPTER p_chmask mismatch: expected 3 actual %d\n",
+				p_chmask);
+		c_chmask = 1;
+		st_chmask = 1;
+		p_chmask = 3;
+		break;
+	case UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE:
+		/* BAIF + BAOF; IN: Mono only; OUT: Mono only */
+		if (c_chmask != 1)
+			usb_audio_warn(mixer->chip,
+				"BADD SPEAKERPHONE c_chmask mismatch: expected 1 actual %d\n",
+				c_chmask);
+		if (p_chmask != 1)
+			usb_audio_warn(mixer->chip,
+				"BADD SPEAKERPHONE p_chmask mismatch: expected 1 actual %d\n",
+				p_chmask);
+		c_chmask = 1;
+		p_chmask = 1;
+		break;
+	}
+
+	/* 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
  *
  * walk through all UAC_OUTPUT_TERMINAL descriptors to search for mixers
@@ -2838,9 +3126,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;
+	}
 
 	snd_usb_mixer_apply_create_quirk(mixer);
 
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 9038b2e..13f03c8 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -482,3 +482,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, "Side Tone 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 586d664..ea5a13e0 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -798,15 +798,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) {
@@ -922,18 +974,31 @@  static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
 	if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
 		fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
 				* (fp->maxpacksize & 0x7ff);
-	fp->attributes = parse_uac_endpoint_attributes(chip, alts,
-						       UAC_VERSION_3,
-						       iface_no);
 	fp->clock = clock;
 	fp->chmap = chmap;
 	INIT_LIST_HEAD(&fp->list);
 
-	/* 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 4d5c89a..1bb5e2c 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;