[alsa-devel] [PATCH v2 7/7] ALSA: usb: add UAC3 BADD profiles support

Jorge jorge.sanjuan at codethink.co.uk
Fri May 11 17:36:36 CEST 2018



On 04/05/18 02:24, Ruslan Bilovol wrote:
> Recently released USB Audio Class 3.0 specification
> contains BADD (Basic Audio Device Definition) document
> which describes pre-defined UAC3 configurations.
> 
> BADD support is mandatory for UAC3 devices, it should be
> implemented as a separate USB device configuration.
> As per BADD document, class-specific descriptors
> shall not be included in the Device’s Configuration
> descriptor ("inferred"), but host can guess them
> from BADD profile number, number of endpoints and
> their max packed sizes.
> 
> This patch adds support of all BADD profiles from the spec
> 
> Signed-off-by: Ruslan Bilovol <ruslan.bilovol at gmail.com>

Tested-by: Jorge Sanjuan <jorge.sanjuan at codethink.co.uk>

> ---
>   sound/usb/card.c       |  14 +++
>   sound/usb/clock.c      |   9 +-
>   sound/usb/mixer.c      | 327 ++++++++++++++++++++++++++++++++++++++++++++-----
>   sound/usb/mixer_maps.c |  65 ++++++++++
>   sound/usb/stream.c     |  83 +++++++++++--
>   sound/usb/usbaudio.h   |   2 +
>   6 files changed, 459 insertions(+), 41 deletions(-)
> 
> diff --git a/sound/usb/card.c b/sound/usb/card.c
> index 0d7a5d7..f6c3c1c 100644
> --- a/sound/usb/card.c
> +++ b/sound/usb/card.c
> @@ -307,6 +307,20 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
>   			return -EINVAL;
>   		}
>   
> +		if (protocol == UAC_VERSION_3) {
> +			int badd = assoc->bFunctionSubClass;
> +
> +			if (badd != UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 &&
> +			    (badd < UAC3_FUNCTION_SUBCLASS_GENERIC_IO ||
> +			     badd > UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE)) {
> +				dev_err(&dev->dev,
> +					"Unsupported UAC3 BADD profile\n");
> +				return -EINVAL;
> +			}
> +
> +			chip->badd_profile = badd;
> +		}
> +
>   		for (i = 0; i < assoc->bInterfaceCount; i++) {
>   			int intf = assoc->bFirstInterface + i;
>   
> diff --git a/sound/usb/clock.c b/sound/usb/clock.c
> index 0b030d8..17673f3 100644
> --- a/sound/usb/clock.c
> +++ b/sound/usb/clock.c
> @@ -587,8 +587,15 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
>   	default:
>   		return set_sample_rate_v1(chip, iface, alts, fmt, rate);
>   
> -	case UAC_VERSION_2:
>   	case UAC_VERSION_3:
> +		if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
> +			if (rate != UAC3_BADD_SAMPLING_RATE)
> +				return -ENXIO;
> +			else
> +				return 0;
> +		}
> +	/* fall through */
> +	case UAC_VERSION_2:
>   		return set_sample_rate_v2v3(chip, iface, alts, fmt, rate);
>   	}
>   }
> diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
> index e280354..d98bc3f 100644
> --- a/sound/usb/mixer.c
> +++ b/sound/usb/mixer.c
> @@ -112,14 +112,12 @@ enum {
>   #include "mixer_maps.c"
>   
>   static const struct usbmix_name_map *
> -find_map(struct mixer_build *state, int unitid, int control)
> +find_map(const struct usbmix_name_map *p, int unitid, int control)
>   {
> -	const struct usbmix_name_map *p = state->map;
> -
>   	if (!p)
>   		return NULL;
>   
> -	for (p = state->map; p->id; p++) {
> +	for (; p->id; p++) {
>   		if (p->id == unitid &&
>   		    (!control || !p->control || control == p->control))
>   			return p;
> @@ -1333,16 +1331,16 @@ static struct usb_feature_control_info *get_feature_control_info(int control)
>   	return NULL;
>   }
>   
> -static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
> -			      unsigned int ctl_mask, int control,
> -			      struct usb_audio_term *iterm, int unitid,
> -			      int readonly_mask)
> +static void __build_feature_ctl(struct usb_mixer_interface *mixer,
> +				const struct usbmix_name_map *imap,
> +				unsigned int ctl_mask, int control,
> +				struct usb_audio_term *iterm,
> +				struct usb_audio_term *oterm,
> +				int unitid, int nameid, int readonly_mask)
>   {
> -	struct uac_feature_unit_descriptor *desc = raw_desc;
>   	struct usb_feature_control_info *ctl_info;
>   	unsigned int len = 0;
>   	int mapped_name = 0;
> -	int nameid = uac_feature_unit_iFeature(desc);
>   	struct snd_kcontrol *kctl;
>   	struct usb_mixer_elem_info *cval;
>   	const struct usbmix_name_map *map;
> @@ -1353,14 +1351,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
>   		return;
>   	}
>   
> -	map = find_map(state, unitid, control);
> +	map = find_map(imap, unitid, control);
>   	if (check_ignored_ctl(map))
>   		return;
>   
>   	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
>   	if (!cval)
>   		return;
> -	snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
> +	snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
>   	cval->control = control;
>   	cval->cmask = ctl_mask;
>   
> @@ -1369,7 +1367,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
>   		kfree(cval);
>   		return;
>   	}
> -	if (state->mixer->protocol == UAC_VERSION_1)
> +	if (mixer->protocol == UAC_VERSION_1)
>   		cval->val_type = ctl_info->type;
>   	else /* UAC_VERSION_2 */
>   		cval->val_type = ctl_info->type_uac2 >= 0 ?
> @@ -1398,7 +1396,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
>   		kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
>   
>   	if (!kctl) {
> -		usb_audio_err(state->chip, "cannot malloc kcontrol\n");
> +		usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
>   		kfree(cval);
>   		return;
>   	}
> @@ -1407,7 +1405,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
>   	len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
>   	mapped_name = len != 0;
>   	if (!len && nameid)
> -		len = snd_usb_copy_string_desc(state->chip, nameid,
> +		len = snd_usb_copy_string_desc(mixer->chip, nameid,
>   				kctl->id.name, sizeof(kctl->id.name));
>   
>   	switch (control) {
> @@ -1422,10 +1420,12 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
>   		 * - otherwise, anonymous name.
>   		 */
>   		if (!len) {
> -			len = get_term_name(state->chip, iterm, kctl->id.name,
> -					    sizeof(kctl->id.name), 1);
> -			if (!len)
> -				len = get_term_name(state->chip, &state->oterm,
> +			if (iterm)
> +				len = get_term_name(mixer->chip, iterm,
> +						    kctl->id.name,
> +						    sizeof(kctl->id.name), 1);
> +			if (!len && oterm)
> +				len = get_term_name(mixer->chip, oterm,
>   						    kctl->id.name,
>   						    sizeof(kctl->id.name), 1);
>   			if (!len)
> @@ -1434,15 +1434,15 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
>   		}
>   
>   		if (!mapped_name)
> -			check_no_speaker_on_headset(kctl, state->mixer->chip->card);
> +			check_no_speaker_on_headset(kctl, mixer->chip->card);
>   
>   		/*
>   		 * determine the stream direction:
>   		 * if the connected output is USB stream, then it's likely a
>   		 * capture stream.  otherwise it should be playback (hopefully :)
>   		 */
> -		if (!mapped_name && !(state->oterm.type >> 16)) {
> -			if ((state->oterm.type & 0xff00) == 0x0100)
> +		if (!mapped_name && oterm && !(oterm->type >> 16)) {
> +			if ((oterm->type & 0xff00) == 0x0100)
>   				append_ctl_name(kctl, " Capture");
>   			else
>   				append_ctl_name(kctl, " Playback");
> @@ -1470,7 +1470,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
>   		}
>   	}
>   
> -	snd_usb_mixer_fu_apply_quirk(state->mixer, cval, unitid, kctl);
> +	snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl);
>   
>   	range = (cval->max - cval->min) / cval->res;
>   	/*
> @@ -1479,21 +1479,41 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
>   	 * devices. It will definitively catch all buggy Logitech devices.
>   	 */
>   	if (range > 384) {
> -		usb_audio_warn(state->chip,
> +		usb_audio_warn(mixer->chip,
>   			       "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
>   			       range);
> -		usb_audio_warn(state->chip,
> +		usb_audio_warn(mixer->chip,
>   			       "[%d] FU [%s] ch = %d, val = %d/%d/%d",
>   			       cval->head.id, kctl->id.name, cval->channels,
>   			       cval->min, cval->max, cval->res);
>   	}
>   
> -	usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
> +	usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
>   		      cval->head.id, kctl->id.name, cval->channels,
>   		      cval->min, cval->max, cval->res);
>   	snd_usb_mixer_add_control(&cval->head, kctl);
>   }
>   
> +static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
> +			      unsigned int ctl_mask, int control,
> +			      struct usb_audio_term *iterm, int unitid,
> +			      int readonly_mask)
> +{
> +	struct uac_feature_unit_descriptor *desc = raw_desc;
> +	int nameid = uac_feature_unit_iFeature(desc);
> +
> +	__build_feature_ctl(state->mixer, state->map, ctl_mask, control,
> +			iterm, &state->oterm, unitid, nameid, readonly_mask);
> +}
> +
> +static void build_feature_ctl_badd(struct usb_mixer_interface *mixer,
> +			      unsigned int ctl_mask, int control, int unitid,
> +			      const struct usbmix_name_map *badd_map)
> +{
> +	__build_feature_ctl(mixer, badd_map, ctl_mask, control,
> +			NULL, NULL, unitid, 0, 0);
> +}
> +
>   static void get_connector_control_name(struct mixer_build *state,
>   				       struct usb_audio_term *term,
>   				       bool is_input, char *name, int name_size)
> @@ -1807,7 +1827,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
>   	struct snd_kcontrol *kctl;
>   	const struct usbmix_name_map *map;
>   
> -	map = find_map(state, unitid, 0);
> +	map = find_map(state->map, unitid, 0);
>   	if (check_ignored_ctl(map))
>   		return;
>   
> @@ -2106,7 +2126,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
>   
>   		if (!(controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
>   			continue;
> -		map = find_map(state, unitid, valinfo->control);
> +		map = find_map(state->map, unitid, valinfo->control);
>   		if (check_ignored_ctl(map))
>   			continue;
>   		cval = kzalloc(sizeof(*cval), GFP_KERNEL);
> @@ -2310,7 +2330,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
>   	if (desc->bNrInPins == 1) /* only one ? nonsense! */
>   		return 0;
>   
> -	map = find_map(state, unitid, 0);
> +	map = find_map(state->map, unitid, 0);
>   	if (check_ignored_ctl(map))
>   		return 0;
>   
> @@ -2497,6 +2517,246 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
>   	return 0;
>   }
>   
> +/* UAC3 predefined channels configuration */
> +struct uac3_badd_profile {
> +	int subclass;
> +	const char *name;
> +	int c_chmask;	/* capture channels mask */
> +	int p_chmask;	/* playback channels mask */
> +	int st_chmask;	/* side tone mixing channel mask */
> +};
> +
> +static struct uac3_badd_profile uac3_badd_profiles[] = {
> +	{
> +		/*
> +		 * BAIF, BAOF or combination of both
> +		 * IN: Mono or Stereo cfg, Mono alt possible
> +		 * OUT: Mono or Stereo cfg, Mono alt possible
> +		 */
> +		.subclass = UAC3_FUNCTION_SUBCLASS_GENERIC_IO,
> +		.name = "GENERIC IO",
> +		.c_chmask = -1,		/* dynamic channels */
> +		.p_chmask = -1,		/* dynamic channels */
> +	},
> +	{
> +		/* BAOF; Stereo only cfg, Mono alt possible */
> +		.subclass = UAC3_FUNCTION_SUBCLASS_HEADPHONE,
> +		.name = "HEADPHONE",
> +		.p_chmask = 3,
> +	},
> +	{
> +		/* BAOF; Mono or Stereo cfg, Mono alt possible */
> +		.subclass = UAC3_FUNCTION_SUBCLASS_SPEAKER,
> +		.name = "SPEAKER",
> +		.p_chmask = -1,		/* dynamic channels */
> +	},
> +	{
> +		/* BAIF; Mono or Stereo cfg, Mono alt possible */
> +		.subclass = UAC3_FUNCTION_SUBCLASS_MICROPHONE,
> +		.name = "MICROPHONE",
> +		.c_chmask = -1,		/* dynamic channels */
> +	},
> +	{
> +		/*
> +		 * BAIOF topology
> +		 * IN: Mono only
> +		 * OUT: Mono or Stereo cfg, Mono alt possible
> +		 */
> +		.subclass = UAC3_FUNCTION_SUBCLASS_HEADSET,
> +		.name = "HEADSET",
> +		.c_chmask = 1,
> +		.p_chmask = -1,		/* dynamic channels */
> +		.st_chmask = 1,
> +	},
> +	{
> +		/* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */
> +		.subclass = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER,
> +		.name = "HEADSET ADAPTER",
> +		.c_chmask = 1,
> +		.p_chmask = 3,
> +		.st_chmask = 1,
> +	},
> +	{
> +		/* BAIF + BAOF; IN: Mono only; OUT: Mono only */
> +		.subclass = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE,
> +		.name = "SPEAKERPHONE",
> +		.c_chmask = 1,
> +		.p_chmask = 1,
> +	},
> +	{ 0 } /* terminator */
> +};
> +
> +static bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer,
> +					      struct uac3_badd_profile *f,
> +					      int c_chmask, int p_chmask)
> +{
> +	/*
> +	 * If both playback/capture channels are dynamic, make sure
> +	 * at least one channel is present
> +	 */
> +	if (f->c_chmask < 0 && f->p_chmask < 0) {
> +		if (!c_chmask && !p_chmask) {
> +			usb_audio_warn(mixer->chip, "BAAD %s: no channels?",
> +				       f->name);
> +			return false;
> +		}
> +		return true;
> +	}
> +
> +	if ((f->c_chmask < 0 && !c_chmask) ||
> +	    (f->c_chmask >= 0 && f->c_chmask != c_chmask)) {
> +		usb_audio_warn(mixer->chip, "BAAD %s c_chmask mismatch",
> +			       f->name);
> +		return false;
> +	}
> +	if ((f->p_chmask < 0 && !p_chmask) ||
> +	    (f->p_chmask >= 0 && f->p_chmask != p_chmask)) {
> +		usb_audio_warn(mixer->chip, "BAAD %s p_chmask mismatch",
> +			       f->name);
> +		return false;
> +	}
> +	return true;
> +}
> +
> +/*
> + * create mixer controls for UAC3 BADD profiles
> + *
> + * UAC3 BADD device doesn't contain CS descriptors thus we will guess everything
> + *
> + * BADD device may contain Mixer Unit, which doesn't have any controls, skip it
> + */
> +static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
> +				       int ctrlif)
> +{
> +	struct usb_device *dev = mixer->chip->dev;
> +	struct usb_interface_assoc_descriptor *assoc;
> +	int badd_profile = mixer->chip->badd_profile;
> +	struct uac3_badd_profile *f;
> +	const struct usbmix_ctl_map *map;
> +	int p_chmask = 0, c_chmask = 0, st_chmask = 0;
> +	int i;
> +
> +	assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
> +
> +	/* Detect BADD capture/playback channels from AS EP descriptors */
> +	for (i = 0; i < assoc->bInterfaceCount; i++) {
> +		int intf = assoc->bFirstInterface + i;
> +
> +		struct usb_interface *iface;
> +		struct usb_host_interface *alts;
> +		struct usb_interface_descriptor *altsd;
> +		unsigned int maxpacksize;
> +		char dir_in;
> +		int chmask, num;
> +
> +		if (intf == ctrlif)
> +			continue;
> +
> +		iface = usb_ifnum_to_if(dev, intf);
> +		num = iface->num_altsetting;
> +
> +		if (num < 2)
> +			return -EINVAL;
> +
> +		/*
> +		 * The number of Channels in an AudioStreaming interface
> +		 * and the audio sample bit resolution (16 bits or 24
> +		 * bits) can be derived from the wMaxPacketSize field in
> +		 * the Standard AS Audio Data Endpoint descriptor in
> +		 * Alternate Setting 1
> +		 */
> +		alts = &iface->altsetting[1];
> +		altsd = get_iface_desc(alts);
> +
> +		if (altsd->bNumEndpoints < 1)
> +			return -EINVAL;
> +
> +		/* check direction */
> +		dir_in = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN);
> +		maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
> +
> +		switch (maxpacksize) {
> +		default:
> +			usb_audio_err(mixer->chip,
> +				"incorrect wMaxPacketSize 0x%x for BADD profile\n",
> +				maxpacksize);
> +			return -EINVAL;
> +		case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
> +		case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16:
> +		case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24:
> +		case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
> +			chmask = 1;
> +			break;
> +		case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
> +		case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16:
> +		case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24:
> +		case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24:
> +			chmask = 3;
> +			break;
> +		}
> +
> +		if (dir_in)
> +			c_chmask = chmask;
> +		else
> +			p_chmask = chmask;
> +	}
> +
> +	usb_audio_dbg(mixer->chip,
> +		"UAC3 BADD profile 0x%x: detected c_chmask=%d p_chmask=%d\n",
> +		badd_profile, c_chmask, p_chmask);
> +
> +	/* check the mapping table */
> +	for (map = uac3_badd_usbmix_ctl_maps; map->id; map++) {
> +		if (map->id == badd_profile)
> +			break;
> +	}
> +
> +	if (!map->id)
> +		return -EINVAL;
> +
> +	for (f = uac3_badd_profiles; f->name; f++) {
> +		if (badd_profile == f->subclass)
> +			break;
> +	}
> +	if (!f->name)
> +		return -EINVAL;
> +	if (!uac3_badd_func_has_valid_channels(mixer, f, c_chmask, p_chmask))
> +		return -EINVAL;
> +	st_chmask = f->st_chmask;
> +
> +	/* Playback */
> +	if (p_chmask) {
> +		/* Master channel, always writable */
> +		build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
> +				       UAC3_BADD_FU_ID2, map->map);
> +		/* Mono/Stereo volume channels, always writable */
> +		build_feature_ctl_badd(mixer, p_chmask, UAC_FU_VOLUME,
> +				       UAC3_BADD_FU_ID2, map->map);
> +	}
> +
> +	/* Capture */
> +	if (c_chmask) {
> +		/* Master channel, always writable */
> +		build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
> +				       UAC3_BADD_FU_ID5, map->map);
> +		/* Mono/Stereo volume channels, always writable */
> +		build_feature_ctl_badd(mixer, c_chmask, UAC_FU_VOLUME,
> +				       UAC3_BADD_FU_ID5, map->map);
> +	}
> +
> +	/* Side tone-mixing */
> +	if (st_chmask) {
> +		/* Master channel, always writable */
> +		build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE,
> +				       UAC3_BADD_FU_ID7, map->map);
> +		/* Mono volume channel, always writable */
> +		build_feature_ctl_badd(mixer, 1, UAC_FU_VOLUME,
> +				       UAC3_BADD_FU_ID7, map->map);
> +	}
> +
> +	return 0;
> +}
> +
>   /*
>    * create mixer controls
>    *
> @@ -2883,9 +3143,14 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
>   		break;
>   	}
>   
> -	if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
> -	    (err = snd_usb_mixer_status_create(mixer)) < 0)
> +	if (mixer->protocol == UAC_VERSION_3 &&
> +			chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
> +		if ((err = snd_usb_mixer_controls_badd(mixer, ctrlif)) < 0)
> +			goto _error;
> +	} else if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
> +			(err = snd_usb_mixer_status_create(mixer)) < 0) {
>   		goto _error;
> +	}
>   	err = create_keep_iface_ctl(mixer);
>   	if (err < 0)
>   		goto _error;
> diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
> index eaa03ac..71069e1 100644
> --- a/sound/usb/mixer_maps.c
> +++ b/sound/usb/mixer_maps.c
> @@ -485,3 +485,68 @@ struct usbmix_ctl_map {
>   	{ 0 } /* terminator */
>   };
>   
> +/*
> + * Control map entries for UAC3 BADD profiles
> + */
> +
> +static struct usbmix_name_map uac3_badd_generic_io_map[] = {
> +	{ UAC3_BADD_FU_ID2, "Generic Out Playback" },
> +	{ UAC3_BADD_FU_ID5, "Generic In Capture" },
> +	{ 0 }					/* terminator */
> +};
> +static struct usbmix_name_map uac3_badd_headphone_map[] = {
> +	{ UAC3_BADD_FU_ID2, "Headphone Playback" },
> +	{ 0 }					/* terminator */
> +};
> +static struct usbmix_name_map uac3_badd_speaker_map[] = {
> +	{ UAC3_BADD_FU_ID2, "Speaker Playback" },
> +	{ 0 }					/* terminator */
> +};
> +static struct usbmix_name_map uac3_badd_microphone_map[] = {
> +	{ UAC3_BADD_FU_ID5, "Mic Capture" },
> +	{ 0 }					/* terminator */
> +};
> +/* Covers also 'headset adapter' profile */
> +static struct usbmix_name_map uac3_badd_headset_map[] = {
> +	{ UAC3_BADD_FU_ID2, "Headset Playback" },
> +	{ UAC3_BADD_FU_ID5, "Headset Capture" },
> +	{ UAC3_BADD_FU_ID7, "Sidetone Mixing" },
> +	{ 0 }					/* terminator */
> +};
> +static struct usbmix_name_map uac3_badd_speakerphone_map[] = {
> +	{ UAC3_BADD_FU_ID2, "Speaker Playback" },
> +	{ UAC3_BADD_FU_ID5, "Mic Capture" },
> +	{ 0 }					/* terminator */
> +};
> +
> +static struct usbmix_ctl_map uac3_badd_usbmix_ctl_maps[] = {
> +	{
> +		.id = UAC3_FUNCTION_SUBCLASS_GENERIC_IO,
> +		.map = uac3_badd_generic_io_map,
> +	},
> +	{
> +		.id = UAC3_FUNCTION_SUBCLASS_HEADPHONE,
> +		.map = uac3_badd_headphone_map,
> +	},
> +	{
> +		.id = UAC3_FUNCTION_SUBCLASS_SPEAKER,
> +		.map = uac3_badd_speaker_map,
> +	},
> +	{
> +		.id = UAC3_FUNCTION_SUBCLASS_MICROPHONE,
> +		.map = uac3_badd_microphone_map,
> +	},
> +	{
> +		.id = UAC3_FUNCTION_SUBCLASS_HEADSET,
> +		.map = uac3_badd_headset_map,
> +	},
> +	{
> +		.id = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER,
> +		.map = uac3_badd_headset_map,
> +	},
> +	{
> +		.id = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE,
> +		.map = uac3_badd_speakerphone_map,
> +	},
> +	{ 0 } /* terminator */
> +};
> diff --git a/sound/usb/stream.c b/sound/usb/stream.c
> index 764be07..de8bbb3 100644
> --- a/sound/usb/stream.c
> +++ b/sound/usb/stream.c
> @@ -817,15 +817,67 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
>   	struct uac3_input_terminal_descriptor *input_term;
>   	struct uac3_output_terminal_descriptor *output_term;
>   	struct uac3_cluster_header_descriptor *cluster;
> -	struct uac3_as_header_descriptor *as;
> +	struct uac3_as_header_descriptor *as = NULL;
>   	struct uac3_hc_descriptor_header hc_header;
>   	struct snd_pcm_chmap_elem *chmap;
> +	unsigned char badd_profile;
> +	u64 badd_formats = 0;
>   	unsigned int num_channels;
>   	struct audioformat *fp;
>   	u16 cluster_id, wLength;
>   	int clock = 0;
>   	int err;
>   
> +	badd_profile = chip->badd_profile;
> +
> +	if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
> +		unsigned int maxpacksize =
> +			le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
> +
> +		switch (maxpacksize) {
> +		default:
> +			dev_err(&dev->dev,
> +				"%u:%d : incorrect wMaxPacketSize for BADD profile\n",
> +				iface_no, altno);
> +			return NULL;
> +		case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
> +		case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16:
> +			badd_formats = SNDRV_PCM_FMTBIT_S16_LE;
> +			num_channels = 1;
> +			break;
> +		case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24:
> +		case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
> +			badd_formats = SNDRV_PCM_FMTBIT_S24_3LE;
> +			num_channels = 1;
> +			break;
> +		case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
> +		case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16:
> +			badd_formats = SNDRV_PCM_FMTBIT_S16_LE;
> +			num_channels = 2;
> +			break;
> +		case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24:
> +		case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24:
> +			badd_formats = SNDRV_PCM_FMTBIT_S24_3LE;
> +			num_channels = 2;
> +			break;
> +		}
> +
> +		chmap = kzalloc(sizeof(*chmap), GFP_KERNEL);
> +		if (!chmap)
> +			return ERR_PTR(-ENOMEM);
> +
> +		if (num_channels == 1) {
> +			chmap->map[0] = SNDRV_CHMAP_MONO;
> +		} else {
> +			chmap->map[0] = SNDRV_CHMAP_FL;
> +			chmap->map[1] = SNDRV_CHMAP_FR;
> +		}
> +
> +		chmap->channels = num_channels;
> +		clock = UAC3_BADD_CS_ID9;
> +		goto found_clock;
> +	}
> +
>   	as = snd_usb_find_csint_desc(alts->extra, alts->extralen,
>   				     NULL, UAC_AS_GENERAL);
>   	if (!as) {
> @@ -931,16 +983,29 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
>   	if (!fp)
>   		return ERR_PTR(-ENOMEM);
>   
> -	fp->attributes = parse_uac_endpoint_attributes(chip, alts,
> -						       UAC_VERSION_3,
> -						       iface_no);
>   	fp->chmap = chmap;
>   
> -	/* ok, let's parse further... */
> -	if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
> -		kfree(fp->rate_table);
> -		kfree(fp);
> -		return NULL;
> +	if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
> +		fp->attributes = 0; /* No attributes */
> +
> +		fp->fmt_type = UAC_FORMAT_TYPE_I;
> +		fp->formats = badd_formats;
> +
> +		fp->nr_rates = 0;	/* SNDRV_PCM_RATE_CONTINUOUS */
> +		fp->rate_min = UAC3_BADD_SAMPLING_RATE;
> +		fp->rate_max = UAC3_BADD_SAMPLING_RATE;
> +		fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
> +
> +	} else {
> +		fp->attributes = parse_uac_endpoint_attributes(chip, alts,
> +							       UAC_VERSION_3,
> +							       iface_no);
> +		/* ok, let's parse further... */
> +		if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
> +			kfree(fp->rate_table);
> +			kfree(fp);
> +			return NULL;
> +		}
>   	}
>   
>   	return fp;
> diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
> index 1cb6b3e..7b28cbd 100644
> --- a/sound/usb/usbaudio.h
> +++ b/sound/usb/usbaudio.h
> @@ -49,6 +49,8 @@ struct snd_usb_audio {
>   	int num_suspended_intf;
>   	int sample_rate_read_error;
>   
> +	int badd_profile;		/* UAC3 BADD profile */
> +
>   	struct list_head pcm_list;	/* list of pcm streams */
>   	struct list_head ep_list;	/* list of audio-related endpoints */
>   	int pcm_devs;
> 


More information about the Alsa-devel mailing list