usb: core: Add support to parse config summary capability descriptors

USB 3.2 Specification updated configuration summary descriptor
different from drafted version. This descriptor is needed per
function. Descriptor provides list of configuration indices
that include that function. Use bcdVersion to handle spec
compliant descriptor and select device preferred config supporting
UAC3 or lower revision.

Change-Id: I7cf28eaf61ca91496be84d90ad00704fe4acb149
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 7b5cb28..e625e59 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -1020,6 +1020,15 @@
 		case USB_PTM_CAP_TYPE:
 			dev->bos->ptm_cap =
 				(struct usb_ptm_cap_descriptor *)buffer;
+			break;
+		case USB_CAP_TYPE_CONFIG_SUMMARY:
+			/* one such desc per function */
+			if (!dev->bos->num_config_summary_desc)
+				dev->bos->config_summary =
+				(struct usb_config_summary_descriptor *)buffer;
+
+			dev->bos->num_config_summary_desc++;
+			break;
 		default:
 			break;
 		}
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index bc8242b..761073d 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -42,6 +42,36 @@
 		&& desc->bInterfaceProtocol == 1;
 }
 
+static int get_usb_audio_config(struct usb_host_bos *bos)
+{
+	unsigned int desc_cnt, num_cfg_desc, len = 0;
+	unsigned char *buffer;
+	struct usb_config_summary_descriptor *conf_summary;
+
+	if (!bos || !bos->config_summary)
+		goto done;
+
+	num_cfg_desc = bos->num_config_summary_desc;
+	conf_summary = bos->config_summary;
+	buffer = (unsigned char *)conf_summary;
+	for (desc_cnt = 0; desc_cnt < num_cfg_desc; desc_cnt++) {
+		conf_summary =
+			(struct usb_config_summary_descriptor *)(buffer + len);
+
+		len += conf_summary->bLength;
+
+		if (conf_summary->bcdVersion != USB_CONFIG_SUMMARY_DESC_REV ||
+				conf_summary->bClass != USB_CLASS_AUDIO)
+			continue;
+
+		/* return 1st config as per device preference */
+		return conf_summary->bConfigurationIndex[0];
+	}
+
+done:
+	return -EINVAL;
+}
+
 int usb_choose_configuration(struct usb_device *udev)
 {
 	int i;
@@ -145,7 +175,10 @@
 			insufficient_power, plural(insufficient_power));
 
 	if (best) {
-		i = best->desc.bConfigurationValue;
+		/* choose device preferred config */
+		i = get_usb_audio_config(udev->bos);
+		if (i < 0)
+			i = best->desc.bConfigurationValue;
 		dev_dbg(&udev->dev,
 			"configuration #%d chosen from %d choice%s\n",
 			i, num_configs, plural(num_configs));
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 9692e72..c3aec3b 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -404,6 +404,8 @@
 	struct usb_ssp_cap_descriptor	*ssp_cap;
 	struct usb_ss_container_id_descriptor	*ss_id;
 	struct usb_ptm_cap_descriptor	*ptm_cap;
+	struct usb_config_summary_descriptor	*config_summary;
+	unsigned int	num_config_summary_desc;
 };
 
 int __usb_get_extra_descriptor(char *buffer, unsigned size,
diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h
index d5a5cae..321c7c1 100644
--- a/include/uapi/linux/usb/ch9.h
+++ b/include/uapi/linux/usb/ch9.h
@@ -1081,6 +1081,26 @@
  */
 #define USB_DT_USB_SSP_CAP_SIZE(ssac)	(12 + (ssac + 1) * 4)
 
+/*
+ * Configuration Summary descriptors: Defines a list of device preferred
+ * configurations. This descriptor may be used by Host software to decide
+ * which Configuration to use to obtain the desired functionality.
+ */
+#define USB_CAP_TYPE_CONFIG_SUMMARY	0x10
+#define USB_CONFIG_SUMMARY_DESC_REV	0x100
+
+struct usb_config_summary_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDevCapabilityType;
+	__u16 bcdVersion;
+	__u8 bClass;
+	__u8 bSubClass;
+	__u8 bProtocol;
+	__u8 bConfigurationCount;
+	__u8 bConfigurationIndex[];
+} __attribute__((packed));
+
 /*-------------------------------------------------------------------------*/
 
 /* USB_DT_WIRELESS_ENDPOINT_COMP:  companion descriptor associated with