Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54b6a080 authored by Hemant Kumar's avatar Hemant Kumar
Browse files

usb: core: Add support to handle multi config audio device



A USB audio class 3.0 based device may express different audio interface
associations representing the same underlying hardware by using multiple
USB device configuration descriptors. Using BOS configuration summary
descriptor host software can choose which configuration to set. This is
done by going over list of configuration summary descriptors and selecting
the first available configuration supporting BADD subclass of UAC 3.0
protocol.

Change-Id: I548b362437deb525f952d4450cfae7420a524c65
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent 4ffaab25
Loading
Loading
Loading
Loading
+36 −2
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@

#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v3.h>
#include "usb.h"

static inline const char *plural(int n)
@@ -42,6 +44,36 @@ static int is_activesync(struct usb_interface_descriptor *desc)
		&& desc->bInterfaceProtocol == 1;
}

static int usb_audio_max_rev_config(struct usb_host_bos *bos)
{
	int desc_cnt, func_cnt, numfunc;
	int num_cfg_desc;
	struct usb_config_summary_descriptor *conf_summary;

	if (!bos || !bos->config_summary)
		goto done;

	conf_summary = bos->config_summary;
	num_cfg_desc = bos->num_config_summary_desc;

	for (desc_cnt = 0; desc_cnt < num_cfg_desc; desc_cnt++) {
		numfunc = conf_summary->bNumFunctions;
		for (func_cnt = 0; func_cnt < numfunc; func_cnt++) {
			/* look for BADD 3.0 */
			if (conf_summary->cs_info[func_cnt].bClass ==
				USB_CLASS_AUDIO &&
				conf_summary->cs_info[func_cnt].bProtocol ==
				UAC_VERSION_3 &&
				conf_summary->cs_info[func_cnt].bSubClass !=
				FULL_ADC_PROFILE)
				return conf_summary->bConfigurationValue;
		}
	}

done:
	return -EINVAL;
}

int usb_choose_configuration(struct usb_device *udev)
{
	int i;
@@ -132,7 +164,6 @@ int usb_choose_configuration(struct usb_device *udev)
			best = c;
			break;
		}

		/* If all the remaining configs are vendor-specific,
		 * choose the first one. */
		else if (!best)
@@ -145,6 +176,9 @@ int usb_choose_configuration(struct usb_device *udev)
			insufficient_power, plural(insufficient_power));

	if (best) {
		/* choose usb audio class preferred config if available */
		i = usb_audio_max_rev_config(udev->bos);
		if (i < 0)
			i = best->desc.bConfigurationValue;
		dev_dbg(&udev->dev,
			"configuration #%d chosen from %d choice%s\n",