LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
From: Jorge <jorge.sanjuan@codethink.co.uk>
To: Ruslan Bilovol <ruslan.bilovol@gmail.com>, Takashi Iwai <tiwai@suse.com>
Cc: Andrew Chant <achant@google.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 7/7] ALSA: usb: add UAC3 BADD profiles support
Date: Fri, 11 May 2018 16:36:36 +0100	[thread overview]
Message-ID: <61f13c53-fda2-65da-b405-41bdd17d04a5@codethink.co.uk> (raw)
In-Reply-To: <1525397044-15080-8-git-send-email-ruslan.bilovol@gmail.com>



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@gmail.com>

Tested-by: Jorge Sanjuan <jorge.sanjuan@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;
> 

  reply	other threads:[~2018-05-11 15:36 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-04  1:23 [PATCH v2 0/7] USB Audio Device Class 3.0 " Ruslan Bilovol
2018-05-04  1:23 ` [PATCH v2 1/7] ALSA: usb: stream: move audioformat alloc/init into separate function Ruslan Bilovol
2018-05-04  1:23 ` [PATCH v2 2/7] ALSA: usb: stream: refactor uac1/2 audio interface parsing Ruslan Bilovol
2018-05-04  1:24 ` [PATCH v2 3/7] ALSA: usb: stream: refactor uac3 " Ruslan Bilovol
2018-05-04  1:24 ` [PATCH v2 4/7] ALSA: usb: Only get AudioControl header for UAC1 class Ruslan Bilovol
2018-05-04  1:24 ` [PATCH v2 5/7] ALSA: usb: mixer: make string parsing independent of mixer_build state Ruslan Bilovol
2018-05-04  1:24 ` [PATCH v2 6/7] include: usb: audio-v3: add BADD-specific values Ruslan Bilovol
2018-05-04  1:24 ` [PATCH v2 7/7] ALSA: usb: add UAC3 BADD profiles support Ruslan Bilovol
2018-05-11 15:36   ` Jorge [this message]
2018-05-13  7:06     ` Takashi Iwai
2018-05-04  7:44 ` [PATCH v2 0/7] USB Audio Device Class 3.0 " Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=61f13c53-fda2-65da-b405-41bdd17d04a5@codethink.co.uk \
    --to=jorge.sanjuan@codethink.co.uk \
    --cc=achant@google.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ruslan.bilovol@gmail.com \
    --cc=tiwai@suse.com \
    --subject='Re: [PATCH v2 7/7] ALSA: usb: add UAC3 BADD profiles support' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).