All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/1] USB Audio Device Class 3.0 Gadget support
@ 2017-11-07  1:52 Ruslan Bilovol
  2017-11-07  1:52 ` [PATCH 1/1] usb: gadget: add USB Audio Device Class 3.0 gadget support Ruslan Bilovol
  2017-11-07  2:04 ` [PATCH 0/1] USB Audio Device Class 3.0 Gadget support Ruslan Bilovol
  0 siblings, 2 replies; 9+ messages in thread
From: Ruslan Bilovol @ 2017-11-07  1:52 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Greg Kroah-Hartman, linux-usb, linux-doc, linux-kernel

Hi,

This patch adds USB Audio Device Class 3.0 [1] function
support to gadget subsystem.
I didn't add UAC3 support to legacy gadget as it will
make preprocessor configuration too complex (UAC3 device
must have two configurations for backward compatibility,
first is UAC1/2 and second is UAC3), yet also I'm too lazy
to do that and verify all possible configurations.

For modern ConfigFS interface I'll provide my configuration
for testing below; testing was done on a BeagleBone Black
board.

This patch depends on uac3 header files from include dir
which I'll post as part of ALSA host UAC3 patch and will
provide the link to it here.

uac_3
------------
mkdir cfg
mount none cfg -t configfs
mkdir cfg/usb_gadget/g1
cd cfg/usb_gadget/g1
mkdir configs/c.1
mkdir functions/uac3.0
mkdir strings/0x409
mkdir configs/c.1/strings/0x409
echo 0x0101 > idProduct
echo 0x1d6b > idVendor
echo my-serial-num > strings/0x409/serialnumber
echo my-manufacturer > strings/0x409/manufacturer
echo "Test gadget" > strings/0x409/product
echo "Conf 1" > configs/c.1/strings/0x409/configuration
echo 120 > configs/c.1/MaxPower
ln -s functions/uac3.0 configs/c.1
echo musb-hdrc.0 > UDC

uac_3 + uac2
------------
mkdir cfg
mount none cfg -t configfs
mkdir cfg/usb_gadget/g1
cd cfg/usb_gadget/g1
mkdir configs/c.1
mkdir functions/uac2.0
mkdir strings/0x409
mkdir configs/c.1/strings/0x409
echo "Test gadget" > strings/0x409/product
echo "Conf 1" > configs/c.1/strings/0x409/configuration
echo 120 > configs/c.1/MaxPower
ln -s functions/uac2.0 configs/c.1
mkdir configs/c.2
mkdir functions/uac3.0
mkdir strings/0x409
mkdir configs/c.2/strings/0x409
echo "Conf 2" > configs/c.2/strings/0x409/configuration
echo 120 > configs/c.2/MaxPower
ln -s functions/uac3.0 configs/c.2
echo 0x0101 > idProduct
echo 0x1d6b > idVendor
echo my-serial-num > strings/0x409/serialnumber
echo my-manufacturer > strings/0x409/manufacturer
echo musb-hdrc.0 > UDC


[1] http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip

Ruslan Bilovol (1):
  usb: gadget: add USB Audio Device Class 3.0 gadget support

 Documentation/ABI/testing/configfs-usb-gadget-uac3 |   14 +
 Documentation/usb/gadget-testing.txt               |   41 +
 drivers/usb/gadget/Kconfig                         |   22 +
 drivers/usb/gadget/function/Makefile               |    2 +
 drivers/usb/gadget/function/f_uac3.c               | 1497 ++++++++++++++++++++
 drivers/usb/gadget/function/u_uac3.h               |   38 +
 drivers/usb/gadget/legacy/Kconfig                  |    3 +-
 7 files changed, 1616 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-uac3
 create mode 100644 drivers/usb/gadget/function/f_uac3.c
 create mode 100644 drivers/usb/gadget/function/u_uac3.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/1] usb: gadget: add USB Audio Device Class 3.0 gadget support
  2017-11-07  1:52 [PATCH 0/1] USB Audio Device Class 3.0 Gadget support Ruslan Bilovol
@ 2017-11-07  1:52 ` Ruslan Bilovol
  2017-11-07 11:34   ` kbuild test robot
  2017-11-07 13:20   ` kbuild test robot
  2017-11-07  2:04 ` [PATCH 0/1] USB Audio Device Class 3.0 Gadget support Ruslan Bilovol
  1 sibling, 2 replies; 9+ messages in thread
From: Ruslan Bilovol @ 2017-11-07  1:52 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Greg Kroah-Hartman, linux-usb, linux-doc, linux-kernel

Recently released USB Audio Class 3.0 specification
introduces many significant changes comparing to
previous versions, like
 - new Power Domains, support for LPM/L1
 - new Cluster descriptor
 - changed layout of all class-specific descriptors
 - new High Capability descriptors
 - New class-specific String descriptors
 - new and removed units
 - additional sources for interrupts
 - removed Type II Audio Data Formats
 - ... and many other things (check spec)

It also provides backward compatibility through
multiple configurations, as well as requires
mandatory support for BADD (Basic Audio Device
Definition) on each ADC3.0 compliant device

This patch adds UAC3 gadget support basing on UAC3
specification, implementing Generic I/O Profile
(BAOF + BAIF) from BADD document.

There are still few areas for future improvements
because not all functionality is completely
implemented, for example volume, mute and
power management handling has dummy implementation
in some places

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
---
 Documentation/ABI/testing/configfs-usb-gadget-uac3 |   14 +
 Documentation/usb/gadget-testing.txt               |   41 +
 drivers/usb/gadget/Kconfig                         |   22 +
 drivers/usb/gadget/function/Makefile               |    2 +
 drivers/usb/gadget/function/f_uac3.c               | 1497 ++++++++++++++++++++
 drivers/usb/gadget/function/u_uac3.h               |   38 +
 drivers/usb/gadget/legacy/Kconfig                  |    3 +-
 7 files changed, 1616 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-uac3
 create mode 100644 drivers/usb/gadget/function/f_uac3.c
 create mode 100644 drivers/usb/gadget/function/u_uac3.h

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac3 b/Documentation/ABI/testing/configfs-usb-gadget-uac3
new file mode 100644
index 0000000..54bb00e
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac3
@@ -0,0 +1,14 @@
+What:		/config/usb-gadget/gadget/functions/uac3.name
+Date:		Nov 2017
+KernelVersion:	4.16
+Description:
+		The attributes:
+
+		c_chmask - capture channel mask
+		c_srate - capture sampling rate
+		c_ssize - capture sample size (bytes)
+		p_chmask - playback channel mask
+		p_srate - playback sampling rate
+		p_ssize - playback sample size (bytes)
+		req_number - the number of pre-allocated request
+			for both capture and playback
diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt
index fbc397d..36d5e2b 100644
--- a/Documentation/usb/gadget-testing.txt
+++ b/Documentation/usb/gadget-testing.txt
@@ -21,6 +21,7 @@ provided by gadgets.
 18. UVC function
 19. PRINTER function
 20. UAC1 function (new API)
+21. UAC3 function
 
 
 1. ACM function
@@ -817,3 +818,43 @@ e.g.:
 
 $ arecord -f dat -t wav -D hw:CARD=UAC1Gadget,DEV=0 | \
 aplay -D default:CARD=OdroidU3
+
+21. UAC3 function
+=================
+
+The function is provided by usb_f_uac3.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "uac3".
+The uac3 function provides these attributes in its function directory:
+
+	c_chmask - capture channel mask
+	c_srate - capture sampling rate
+	c_ssize - capture sample size (bytes)
+	p_chmask - playback channel mask
+	p_srate - playback sampling rate
+	p_ssize - playback sample size (bytes)
+	req_number - the number of pre-allocated request for both capture
+		     and playback
+
+The attributes have sane default values.
+
+Testing the UAC3 function
+-------------------------
+
+device: run the gadget
+host: aplay -l # should list our USB Audio Gadget
+
+This function does not require real hardware support, it just
+sends a stream of audio data to/from the host. In order to
+actually hear something at the device side, a command similar
+to this must be used at the device side:
+
+$ arecord -f dat -t wav -D hw:2,0 | aplay -D hw:0,0 &
+
+e.g.:
+
+$ arecord -f dat -t wav -D hw:CARD=UAC3Gadget,DEV=0 | \
+aplay -D default:CARD=OdroidU3
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 31cce78..d53ae7d 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -200,6 +200,9 @@ config USB_F_UAC1_LEGACY
 config USB_F_UAC2
 	tristate
 
+config USB_F_UAC3
+	tristate
+
 config USB_F_UVC
 	tristate
 
@@ -418,6 +421,25 @@ config USB_CONFIGFS_F_UAC2
 	  received from the USB Host and choose to provide whatever it
 	  wants as audio data to the USB Host.
 
+config USB_CONFIGFS_F_UAC3
+	bool "Audio Class 3.0"
+	depends on USB_CONFIGFS
+	depends on SND
+	select USB_LIBCOMPOSITE
+	select SND_PCM
+	select USB_U_AUDIO
+	select USB_F_UAC3
+	help
+	  This Audio function is compatible with USB Audio Class
+	  specification 3.0. It implements 1 AudioControl interface,
+	  1 AudioStreaming Interface each for USB-OUT and USB-IN.
+	  This driver doesn't expect any real Audio codec to be present
+	  on the device - the audio streams are simply sinked to and
+	  sourced from a virtual ALSA sound card created. The user-space
+	  application may choose to do whatever it wants with the data
+	  received from the USB Host and choose to provide whatever it
+	  wants as audio data to the USB Host.
+
 config USB_CONFIGFS_F_MIDI
 	bool "MIDI function"
 	depends on USB_CONFIGFS
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 86e8252..6a77eca 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -39,6 +39,8 @@ usb_f_uac1_legacy-y		:= f_uac1_legacy.o u_uac1_legacy.o
 obj-$(CONFIG_USB_F_UAC1_LEGACY)	+= usb_f_uac1_legacy.o
 usb_f_uac2-y			:= f_uac2.o
 obj-$(CONFIG_USB_F_UAC2)	+= usb_f_uac2.o
+usb_f_uac3-y			:= f_uac3.o
+obj-$(CONFIG_USB_F_UAC3)	+= usb_f_uac3.o
 usb_f_uvc-y			:= f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
 obj-$(CONFIG_USB_F_UVC)		+= usb_f_uvc.o
 usb_f_midi-y			:= f_midi.o
diff --git a/drivers/usb/gadget/function/f_uac3.c b/drivers/usb/gadget/function/f_uac3.c
new file mode 100644
index 0000000..4dee0a5
--- /dev/null
+++ b/drivers/usb/gadget/function/f_uac3.c
@@ -0,0 +1,1497 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_uac3.c -- USB Audio Class 3.0 Function
+ *
+ * Copyright (C) 2017 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ */
+
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
+#include <linux/module.h>
+
+#include "u_audio.h"
+#include "u_uac3.h"
+
+/*
+ * The driver implements Generic I/O Profile (BAOF + BAIF)
+ * from BasicAudioDevice3.0 spec:
+ *    USB-OUT -> IT_1 -> FU_2 -> OT_3 -> ALSA Capture
+ *    ALSA Playback -> IT_4 -> FU_5 -> OT_6 -> USB-IN
+ *
+ * Capture and Playback belong to independent Power Domains
+ * PD_10 and PD_11 respectively.
+ *
+ * Capture and Playback sampling rates are independently
+ * controlled by two clock sources:
+ *    CLK_9 := c_srate, and CLK_12 := p_srate
+ *
+ * Entity IDs are taken from BasicAudioDevice3.0 spec.
+ * The only difference is in additional playback clock
+ * source which is required for independent sampling rate
+ * of Capture and Playback channels.
+ */
+
+#define USB_OUT_IT_ID	1
+#define USB_OUT_FU_ID	2
+#define IO_OUT_OT_ID	3
+#define IO_IN_IT_ID	4
+#define USB_IN_FU_ID	5
+#define USB_IN_OT_ID	6
+
+#define USB_OUT_CLK_ID	9
+#define USB_IN_CLK_ID	12
+
+#define USB_OUT_PD_ID	10
+#define USB_IN_PD_ID	11
+
+#define CONTROL_ABSENT	0
+#define CONTROL_RDONLY	1
+#define CONTROL_RDWR	3
+
+#define CLK_FREQ_CTRL	0
+#define CLK_VLD_CTRL	2
+
+#define INSRT_CTRL	0
+#define OVRLD_CTRL	2
+#define UNFLW_CTRL	4
+#define OVFLW_CTRL	6
+
+struct uac3_hc_desc {
+	struct uac3_hc_descriptor_header	*hc_header;
+	struct list_head			list;
+};
+
+struct f_uac3 {
+	struct g_audio g_audio;
+
+	/* High Capacity descriptors */
+	struct list_head hc_desc_list;
+
+	u8 ac_intf, as_in_intf, as_out_intf;
+	u8 ac_alt, as_in_alt, as_out_alt;	/* needed for get_alt() */
+};
+
+static inline struct f_uac3 *func_to_uac3(struct usb_function *f)
+{
+	return container_of(f, struct f_uac3, g_audio.func);
+}
+
+static inline struct f_uac3_opts *g_audio_to_uac3_opts(struct g_audio *audio)
+{
+	return container_of(audio->func.fi, struct f_uac3_opts, func_inst);
+}
+
+/* --------- USB Function Interface ------------- */
+enum {
+	STR_ASSOC,
+	STR_IF_CTRL,
+	STR_AS_OUT_ALT0,
+	STR_AS_OUT_ALT1,
+	STR_AS_IN_ALT0,
+	STR_AS_IN_ALT1,
+};
+
+static struct usb_string strings_fn[] = {
+	[STR_ASSOC].s = "Source/Sink",
+	[STR_IF_CTRL].s = "Topology Control",
+	[STR_AS_OUT_ALT0].s = "Playback Inactive",
+	[STR_AS_OUT_ALT1].s = "Playback Active",
+	[STR_AS_IN_ALT0].s = "Capture Inactive",
+	[STR_AS_IN_ALT1].s = "Capture Active",
+	{ },
+};
+
+static struct usb_gadget_strings str_fn = {
+	.language = 0x0409,	/* en-us */
+	.strings = strings_fn,
+};
+
+static struct usb_gadget_strings *fn_strings[] = {
+	&str_fn,
+	NULL,
+};
+
+static struct usb_interface_assoc_descriptor iad_desc = {
+	.bLength = sizeof iad_desc,
+	.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+	.bFirstInterface = 0,
+	.bInterfaceCount = 3,
+	.bFunctionClass = USB_CLASS_AUDIO,
+	.bFunctionSubClass = UAC3_FUNCTION_SUBCLASS_GENERIC_IO,
+	.bFunctionProtocol = UAC_VERSION_3,
+};
+
+/* Audio Control Interface */
+static struct usb_interface_descriptor std_ac_if_desc = {
+	.bLength = sizeof std_ac_if_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.bInterfaceProtocol = UAC_VERSION_3,
+};
+
+/* Clock source for IN traffic */
+static struct uac3_clock_source_descriptor in_clk_src_desc = {
+	.bLength = sizeof in_clk_src_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC3_CLOCK_SOURCE,
+	.bClockID = USB_IN_CLK_ID,
+	.bmAttributes = UAC3_CLOCK_SOURCE_TYPE_INT,
+	.bmControls = cpu_to_le32(CONTROL_RDONLY << CLK_FREQ_CTRL),
+	.bReferenceTerminal = 0,
+	.wClockSourceStr = 0, /* Not used */
+};
+
+/* Clock source for OUT traffic */
+static struct uac3_clock_source_descriptor out_clk_src_desc = {
+	.bLength = sizeof out_clk_src_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC3_CLOCK_SOURCE,
+	.bClockID = USB_OUT_CLK_ID,
+	.bmAttributes = UAC3_CLOCK_SOURCE_TYPE_INT,
+	.bmControls = cpu_to_le32(CONTROL_RDONLY << CLK_FREQ_CTRL),
+	.bReferenceTerminal = 0,
+	.wClockSourceStr = 0, /* Not used */
+};
+
+/* Input Terminal for USB_OUT */
+static struct uac3_input_terminal_descriptor usb_out_it_desc = {
+	.bLength = sizeof usb_out_it_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_INPUT_TERMINAL,
+	.bTerminalID = USB_OUT_IT_ID,
+	.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
+	.bAssocTerminal = 0,
+	.bCSourceID = USB_OUT_CLK_ID,
+	.bmControls = 0,
+	.wClusterDescrID = 0, /* := dynamic */
+	.wExTerminalDescrID = 0,
+	.wConnectorsDescrID = 0,
+	.wTerminalDescrStr = 0, /* Not used */
+};
+
+/* Output Terminal for I/O-Out */
+static struct uac3_output_terminal_descriptor io_out_ot_desc = {
+	.bLength = sizeof io_out_ot_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+	.bTerminalID = IO_OUT_OT_ID,
+	.wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED),
+	.bAssocTerminal = 0,
+	.bSourceID = USB_OUT_FU_ID,
+	.bCSourceID = USB_OUT_CLK_ID,
+	.bmControls = 0,
+	.wExTerminalDescrID = 0,
+	.wConnectorsDescrID = 0,
+	.wTerminalDescrStr = 0, /* Not used */
+};
+
+/* Input Terminal for I/O-In */
+static struct uac3_input_terminal_descriptor io_in_it_desc = {
+	.bLength = sizeof io_in_it_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_INPUT_TERMINAL,
+	.bTerminalID = IO_IN_IT_ID,
+	.wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED),
+	.bAssocTerminal = 0,
+	.bCSourceID = USB_IN_CLK_ID,
+	.bmControls = 0,
+	.wClusterDescrID = 0, /* := dynamic */
+	.wExTerminalDescrID = 0,
+	.wConnectorsDescrID = 0,
+	.wTerminalDescrStr = 0, /* Not used */
+};
+
+/* Output Terminal for USB_IN */
+static struct uac3_output_terminal_descriptor usb_in_ot_desc = {
+	.bLength = sizeof usb_in_ot_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+	.bTerminalID = USB_IN_OT_ID,
+	.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
+	.bAssocTerminal = 0,
+	.bSourceID = USB_IN_FU_ID,
+	.bCSourceID = USB_IN_CLK_ID,
+	.bmControls = 0,
+	.wExTerminalDescrID = 0,
+	.wConnectorsDescrID = 0,
+	.wTerminalDescrStr = 0, /* Not used */
+};
+
+/* Feature Units - dynamically allocated */
+static struct uac3_feature_unit_descriptor *usb_out_fu_desc;
+static struct uac3_feature_unit_descriptor *usb_in_fu_desc;
+
+DECLARE_UAC3_POWER_DOMAIN_DESCRIPTOR(2);
+
+/* Time to recover from D1 to D0. 30 us, expressed in 50 us increments */
+#define BADP_RECOVERY_TIME_D1D0		0x0258
+/* Time to recover from D2 to D0. 300 ms, expressed in 50 us increments. */
+#define BADP_RECOVERY_TIME_D2D0		0x1770
+
+static struct uac3_power_domain_descriptor_2 usb_out_pd_desc = {
+	.bLength = sizeof usb_out_pd_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC3_POWER_DOMAIN,
+	.bPowerDomainID = USB_OUT_PD_ID,
+	.waRecoveryTime1 = cpu_to_le16(BADP_RECOVERY_TIME_D1D0),
+	.waRecoveryTime2 = cpu_to_le16(BADP_RECOVERY_TIME_D2D0),
+	.bNrEntities = 2,
+	.baEntityID[0] = USB_OUT_IT_ID,
+	.baEntityID[1] = IO_OUT_OT_ID,
+	.wPDomainDescrStr = 0,	/* Not used */
+};
+
+static struct uac3_power_domain_descriptor_2 usb_in_pd_desc = {
+	.bLength = sizeof usb_out_pd_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC3_POWER_DOMAIN,
+	.bPowerDomainID = USB_IN_PD_ID,
+	.waRecoveryTime1 = cpu_to_le16(BADP_RECOVERY_TIME_D1D0),
+	.waRecoveryTime2 = cpu_to_le16(BADP_RECOVERY_TIME_D2D0),
+	.bNrEntities = 2,
+	.baEntityID[0] = IO_IN_IT_ID,
+	.baEntityID[1] = USB_IN_OT_ID,
+	.wPDomainDescrStr = 0,	/* Not used */
+};
+
+static struct uac3_ac_header_descriptor ac_hdr_desc = {
+	.bLength = sizeof ac_hdr_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_MS_HEADER,
+	.bCategory = UAC3_FUNCTION_IO_BOX,
+	/* .wTotalLength := DYNAMIC */
+	.bmControls = 0,
+};
+
+/* Audio Streaming OUT Interface - Alt0 */
+static struct usb_interface_descriptor std_as_out_if0_desc = {
+	.bLength = sizeof std_as_out_if0_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+	.bInterfaceProtocol = UAC_VERSION_3,
+};
+
+/* Audio Streaming OUT Interface - Alt1 */
+static struct usb_interface_descriptor std_as_out_if1_desc = {
+	.bLength = sizeof std_as_out_if1_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 1,
+	.bNumEndpoints = 1,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+	.bInterfaceProtocol = UAC_VERSION_3,
+};
+
+/* Audio Stream OUT Intface Desc */
+static struct uac3_as_header_descriptor as_out_hdr_desc = {
+	.bLength = sizeof as_out_hdr_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_AS_GENERAL,
+	.bTerminalLink = USB_OUT_IT_ID,
+	.bmControls = 0,
+	.wClusterDescrID = 0,
+	.bmFormats = cpu_to_le64(UAC_FORMAT_TYPE_I_PCM),
+	/* .bSubslotSize = DYNAMIC */
+	/* .bBitResolution = DYNAMIC */
+	.bmAuxProtocols = 0,
+	.bControlSize = 0,
+};
+
+/* STD AS ISO OUT Endpoint */
+static struct usb_endpoint_descriptor fs_epout_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bEndpointAddress = USB_DIR_OUT,
+	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+	.wMaxPacketSize = cpu_to_le16(1023),
+	.bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor hs_epout_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+	.wMaxPacketSize = cpu_to_le16(1024),
+	.bInterval = 4,
+};
+
+/* CS AS ISO OUT Endpoint */
+static struct uac3_iso_endpoint_descriptor as_iso_out_desc = {
+	.bLength = sizeof as_iso_out_desc,
+	.bDescriptorType = USB_DT_CS_ENDPOINT,
+
+	.bDescriptorSubtype = UAC_EP_GENERAL,
+	.bmControls = 0,
+	.bLockDelayUnits = 0,
+	.wLockDelay = 0,
+};
+
+/* Audio Streaming IN Interface - Alt0 */
+static struct usb_interface_descriptor std_as_in_if0_desc = {
+	.bLength = sizeof std_as_in_if0_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+	.bInterfaceProtocol = UAC_VERSION_3,
+};
+
+/* Audio Streaming IN Interface - Alt1 */
+static struct usb_interface_descriptor std_as_in_if1_desc = {
+	.bLength = sizeof std_as_in_if1_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 1,
+	.bNumEndpoints = 1,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+	.bInterfaceProtocol = UAC_VERSION_3,
+};
+
+/* Audio Stream IN Intface Desc */
+static struct uac3_as_header_descriptor as_in_hdr_desc = {
+	.bLength = sizeof as_in_hdr_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_AS_GENERAL,
+	.bTerminalLink = USB_IN_OT_ID,
+	.bmControls = 0,
+	.wClusterDescrID = 0,
+	.bmFormats = cpu_to_le64(UAC_FORMAT_TYPE_I_PCM),
+	/* .bSubslotSize = DYNAMIC */
+	/* .bBitResolution = DYNAMIC */
+	.bmAuxProtocols = 0,
+	.bControlSize = 0,
+};
+
+/* STD AS ISO IN Endpoint */
+static struct usb_endpoint_descriptor fs_epin_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+	.wMaxPacketSize = cpu_to_le16(1023),
+	.bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor hs_epin_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+	.wMaxPacketSize = cpu_to_le16(1024),
+	.bInterval = 4,
+};
+
+/* CS AS ISO IN Endpoint */
+static struct uac3_iso_endpoint_descriptor as_iso_in_desc = {
+	.bLength = sizeof as_iso_in_desc,
+	.bDescriptorType = USB_DT_CS_ENDPOINT,
+
+	.bDescriptorSubtype = UAC_EP_GENERAL,
+	.bmControls = 0,
+	.bLockDelayUnits = 0,
+	.wLockDelay = 0,
+};
+
+static struct usb_descriptor_header *fs_ac_audio_desc[] = {
+	(struct usb_descriptor_header *)&iad_desc,
+	(struct usb_descriptor_header *)&std_ac_if_desc,
+
+	(struct usb_descriptor_header *)&ac_hdr_desc,
+	(struct usb_descriptor_header *)&in_clk_src_desc,
+	(struct usb_descriptor_header *)&out_clk_src_desc,
+	(struct usb_descriptor_header *)&usb_out_it_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
+	(struct usb_descriptor_header *)&io_out_ot_desc,
+	(struct usb_descriptor_header *)&usb_in_pd_desc,
+	(struct usb_descriptor_header *)&usb_out_pd_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *fs_as_audio_desc[] = {
+	(struct usb_descriptor_header *)&std_as_out_if0_desc,
+	(struct usb_descriptor_header *)&std_as_out_if1_desc,
+
+	(struct usb_descriptor_header *)&as_out_hdr_desc,
+	(struct usb_descriptor_header *)&fs_epout_desc,
+	(struct usb_descriptor_header *)&as_iso_out_desc,
+
+	(struct usb_descriptor_header *)&std_as_in_if0_desc,
+	(struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+	(struct usb_descriptor_header *)&as_in_hdr_desc,
+	(struct usb_descriptor_header *)&fs_epin_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *hs_as_audio_desc[] = {
+	(struct usb_descriptor_header *)&std_as_out_if0_desc,
+	(struct usb_descriptor_header *)&std_as_out_if1_desc,
+
+	(struct usb_descriptor_header *)&as_out_hdr_desc,
+	(struct usb_descriptor_header *)&hs_epout_desc,
+	(struct usb_descriptor_header *)&as_iso_out_desc,
+
+	(struct usb_descriptor_header *)&std_as_in_if0_desc,
+	(struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+	(struct usb_descriptor_header *)&as_in_hdr_desc,
+	(struct usb_descriptor_header *)&hs_epin_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+struct cntrl_cur_lay2 {
+	__le16	wCUR;
+};
+
+struct cntrl_range_lay2 {
+	__le16	wNumSubRanges;
+	__le16	wMIN;
+	__le16	wMAX;
+	__le16	wRES;
+} __packed;
+
+struct cntrl_cur_lay3 {
+	__le32	dCUR;
+};
+
+struct cntrl_range_lay3 {
+	__le16	wNumSubRanges;
+	__le32	dMIN;
+	__le32	dMAX;
+	__le32	dRES;
+} __packed;
+
+/*
+ * Cluster descriptor we build here:
+ *  +---------------------------------------+
+ *  | Header                                |
+ *  +---------------------------------------+
+ *  |                 | Information segment |
+ *  | Channel 1 block +---------------------+
+ *  |                 | End segment         |
+ *  +---------------------------------------+
+ *  |                 ...                   |
+ *  +---------------------------------------+
+ *  |                 | Information segment |
+ *  | Channel n block +---------------------+
+ *  |                 | End segment         |
+ *  +---------------------------------------+
+ *
+ *  FIXME: only mono and stereo channels supported at this time
+ */
+static void *build_cluster_descriptor(const struct f_uac3_opts *uac3_opts,
+		bool is_playback)
+{
+	struct uac3_cluster_header_descriptor *cluster_desc;
+	int i, chmask, nr_channels;
+	u16 desc_size;
+	void *p;
+
+	if (is_playback)
+		chmask = uac3_opts->p_chmask;
+	else
+		chmask = uac3_opts->c_chmask;
+
+	nr_channels = num_channels(chmask);
+
+	if (!nr_channels) {
+		pr_err("f_uac3: no channels\n");
+		return NULL;
+	}
+
+	if (chmask & ~(0x3)) {
+		pr_err("f_uac3: only mono/stereo channels supported\n");
+		return NULL;
+	}
+
+	desc_size = sizeof(struct uac3_cluster_header_descriptor) +
+		nr_channels *
+		(sizeof(struct uac3_cluster_information_segment_descriptor) +
+			sizeof(struct uac3_cluster_end_segment_descriptor));
+
+	cluster_desc = kzalloc(desc_size, GFP_KERNEL);
+	if (!cluster_desc)
+		return NULL;
+
+	cluster_desc->wLength = cpu_to_le16(desc_size);
+	cluster_desc->bDescriptorType = UAC3_CS_CLUSTER;
+	cluster_desc->bDescriptorSubtype = UAC3_SEGMENT_UNDEFINED;
+	cluster_desc->bNrChannels = nr_channels;
+
+	p = cluster_desc;
+	p += sizeof(struct uac3_cluster_header_descriptor);
+	for (i = 0; i < nr_channels; i++) {
+		struct uac3_cluster_information_segment_descriptor *is_desc;
+		struct uac3_cluster_end_segment_descriptor *es_desc;
+		u8 ch_relationship;
+
+		is_desc = p;
+		is_desc->wLength = cpu_to_le16(sizeof(*is_desc));
+		is_desc->bSegmentType = UAC3_CHANNEL_INFORMATION;
+		is_desc->bChPurpose = UAC3_PURPOSE_GENERIC_AUDIO;
+
+		switch (nr_channels) {
+		case 1:
+		default:
+			ch_relationship = UAC3_CH_MONO;
+			break;
+		case 2:
+			if (chmask & 1)
+				ch_relationship = UAC3_CH_LEFT;
+			else
+				ch_relationship = UAC3_CH_RIGHT;
+			break;
+		}
+
+		is_desc->bChRelationship = ch_relationship;
+		is_desc->bChGroupID = 0;
+
+		p += sizeof(struct uac3_cluster_information_segment_descriptor);
+		es_desc = p;
+		es_desc->wLength = cpu_to_le16(sizeof(*es_desc));
+		es_desc->bSegmentType = UAC3_END_SEGMENT;
+	}
+	return cluster_desc;
+}
+
+static struct uac3_feature_unit_descriptor *alloc_fu_desc(unsigned int ch,
+							  u8 unit_id,
+							  u8 source_id)
+{
+	struct uac3_feature_unit_descriptor *fu_desc;
+	__le32 *bma_control;
+
+	fu_desc = kzalloc(UAC3_DT_FEATURE_UNIT_SIZE(ch), GFP_KERNEL);
+	if (!fu_desc)
+		return NULL;
+
+	fu_desc->bLength = UAC3_DT_FEATURE_UNIT_SIZE(ch);
+	fu_desc->bDescriptorType = USB_DT_CS_INTERFACE;
+	fu_desc->bDescriptorSubtype = UAC3_FEATURE_UNIT;
+	fu_desc->bUnitID = unit_id;
+	fu_desc->bSourceID = source_id;
+
+	bma_control = (__le32 *)&fu_desc->bmaControls;
+
+	/* REVISIT: currently hardcoded as described in BADP spec */
+	/* Master Channel: Only a Mute Control shall be present */
+	*bma_control++ = cpu_to_le32(CONTROL_RDWR << ((UAC_FU_MUTE - 1) * 2));
+	/* Channel 1+ : Only a Volume Control shall be present  */
+	while (ch--)
+		*bma_control++ = cpu_to_le32(CONTROL_RDWR << ((UAC_FU_VOLUME - 1) * 2));
+
+	/* fu_desc->wFeatureDescrStr := Not used */
+
+	return fu_desc;
+}
+
+static void set_ep_max_packet_size(const struct f_uac3_opts *uac3_opts,
+				   struct usb_endpoint_descriptor *ep_desc,
+				   unsigned int factor, bool is_playback)
+{
+	int chmask, srate, ssize;
+	u16 max_packet_size;
+
+	if (is_playback) {
+		chmask = uac3_opts->p_chmask;
+		srate = uac3_opts->p_srate;
+		ssize = uac3_opts->p_ssize;
+	} else {
+		chmask = uac3_opts->c_chmask;
+		srate = uac3_opts->c_srate;
+		ssize = uac3_opts->c_ssize;
+	}
+
+	max_packet_size = num_channels(chmask) * ssize *
+		DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1)));
+	ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_packet_size,
+				le16_to_cpu(ep_desc->wMaxPacketSize)));
+}
+
+#define UAC3_COPY_DESCRIPTOR(mem, dst, desc) \
+	do { \
+		memcpy(mem, desc, (desc)->bLength); \
+		*(dst)++ = mem; \
+		mem += (desc)->bLength; \
+	} while (0)
+
+static struct usb_descriptor_header **
+uac3_copy_descriptors(enum usb_device_speed speed)
+{
+	struct usb_descriptor_header **uac3_control_desc = fs_ac_audio_desc;
+	struct usb_descriptor_header **uac3_streaming_desc;
+
+	struct usb_descriptor_header **tmp;
+	unsigned bytes;
+	unsigned n_desc;
+	void *mem;
+	struct usb_descriptor_header **ret;
+
+	switch (speed) {
+	case USB_SPEED_HIGH:
+		uac3_streaming_desc = hs_as_audio_desc;
+		break;
+
+	case USB_SPEED_FULL:
+	default:
+		uac3_streaming_desc = fs_as_audio_desc;
+		break;
+	}
+
+	/* Count descriptors and their sizes */
+	for (bytes = 0, n_desc = 0, tmp = uac3_control_desc; *tmp; tmp++, n_desc++)
+		bytes += (*tmp)->bLength;
+	if (usb_out_fu_desc) {
+		bytes += usb_out_fu_desc->bLength;
+		n_desc++;
+	}
+	if (usb_in_fu_desc) {
+		bytes += usb_in_fu_desc->bLength;
+		n_desc++;
+	}
+	for (tmp = uac3_streaming_desc; *tmp; tmp++, n_desc++)
+		bytes += (*tmp)->bLength;
+	bytes += (n_desc + 1) * sizeof(*tmp);
+
+	mem = kmalloc(bytes, GFP_KERNEL);
+	if (!mem)
+		return NULL;
+
+	/* fill in pointers starting at "tmp",
+	 * to descriptors copied starting at "mem";
+	 * and return "ret"
+	 */
+	tmp = mem;
+	ret = mem;
+	mem += (n_desc + 1) * sizeof(*tmp);
+	while (*uac3_control_desc) {
+		memcpy(mem, *uac3_control_desc, (*uac3_control_desc)->bLength);
+		*tmp = mem;
+		tmp++;
+		mem += (*uac3_control_desc)->bLength;
+		uac3_control_desc++;
+	}
+
+	if (usb_out_fu_desc)
+		UAC3_COPY_DESCRIPTOR(mem, tmp, usb_out_fu_desc);
+	if (usb_in_fu_desc)
+		UAC3_COPY_DESCRIPTOR(mem, tmp, usb_in_fu_desc);
+
+	while (*uac3_streaming_desc) {
+		memcpy(mem, *uac3_streaming_desc, (*uac3_streaming_desc)->bLength);
+		*tmp = mem;
+		tmp++;
+		mem += (*uac3_streaming_desc)->bLength;
+		uac3_streaming_desc++;
+	}
+	*tmp = NULL;
+
+	return ret;
+}
+
+static int f_audio_bind(struct usb_configuration *cfg, struct usb_function *fn)
+{
+	struct f_uac3 *uac3 = func_to_uac3(fn);
+	struct g_audio *audio = func_to_g_audio(fn);
+	struct usb_composite_dev *cdev = cfg->cdev;
+	struct usb_gadget *gadget = cdev->gadget;
+	struct device *dev = &gadget->dev;
+	struct f_uac3_opts *uac3_opts;
+	struct uac3_hc_descriptor_header *cluster_desc;
+	struct uac3_hc_desc *hc_desc;
+	struct usb_string *us;
+	u16 hc_desc_id = 1; /* HC id always starts from 1 */
+	int ret;
+
+	uac3_opts = container_of(fn->fi, struct f_uac3_opts, func_inst);
+
+	us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+
+	iad_desc.iFunction = us[STR_ASSOC].id;
+	std_ac_if_desc.iInterface = us[STR_IF_CTRL].id;
+	std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
+	std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id;
+	std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id;
+	std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id;
+
+	INIT_LIST_HEAD(&uac3->hc_desc_list);
+
+	/* Initialize the configurable parameters */
+	cluster_desc = build_cluster_descriptor(uac3_opts, 0); /* capture */
+	if (cluster_desc) {
+		hc_desc = kzalloc(sizeof *hc_desc, GFP_KERNEL);
+		hc_desc->hc_header = cluster_desc;
+		list_add(&hc_desc->list, &uac3->hc_desc_list);
+		cluster_desc->wDescriptorID = cpu_to_le16(hc_desc_id);
+		usb_out_it_desc.wClusterDescrID = cluster_desc->wDescriptorID;
+		as_out_hdr_desc.wClusterDescrID = cluster_desc->wDescriptorID;
+		hc_desc_id++;
+	}
+
+	cluster_desc = build_cluster_descriptor(uac3_opts, 1); /* playback */
+	if (cluster_desc) {
+		hc_desc = kzalloc(sizeof *hc_desc, GFP_KERNEL);
+		hc_desc->hc_header = cluster_desc;
+		list_add(&hc_desc->list, &uac3->hc_desc_list);
+		cluster_desc->wDescriptorID = cpu_to_le16(hc_desc_id);
+		io_in_it_desc.wClusterDescrID = cluster_desc->wDescriptorID;
+		as_in_hdr_desc.wClusterDescrID = cluster_desc->wDescriptorID;
+	}
+
+	as_out_hdr_desc.bSubslotSize = uac3_opts->c_ssize;
+	as_out_hdr_desc.bBitResolution = uac3_opts->c_ssize * 8;
+	as_in_hdr_desc.bSubslotSize = uac3_opts->p_ssize;
+	as_in_hdr_desc.bBitResolution = uac3_opts->p_ssize * 8;
+
+	/* alloc and configure Feature Unit descriptors */
+	usb_out_fu_desc = alloc_fu_desc(num_channels(uac3_opts->c_chmask),
+					USB_OUT_FU_ID,
+					USB_OUT_IT_ID);
+	if (!usb_out_fu_desc) {
+		dev_err(dev, "%s: can't allocate OUT FU descriptor on %s\n",
+				 fn->name, gadget->name);
+		ret = -ENOMEM;
+		goto err_free_hc_desc;
+	}
+
+	usb_in_fu_desc = alloc_fu_desc(num_channels(uac3_opts->p_chmask),
+					USB_IN_FU_ID,
+					IO_IN_IT_ID);
+	if (!usb_in_fu_desc) {
+		dev_err(dev, "%s: can't allocate IN FU descriptor on %s\n",
+				 fn->name, gadget->name);
+		ret = -ENOMEM;
+		goto err_free_out_fu_desc;
+	}
+
+	/* update AC desc size with allocated FUs */
+	ac_hdr_desc.wTotalLength = cpu_to_le16(
+			  sizeof in_clk_src_desc + sizeof out_clk_src_desc
+			+ sizeof usb_out_it_desc + sizeof io_in_it_desc
+			+ sizeof usb_in_ot_desc + sizeof io_out_ot_desc
+			+ sizeof usb_in_pd_desc + sizeof usb_out_pd_desc
+			+ usb_out_fu_desc->bLength + usb_in_fu_desc->bLength);
+
+	ret = usb_interface_id(cfg, fn);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't allocate AC interface id on %s\n",
+				 fn->name, gadget->name);
+		goto err_free_in_fu_desc;
+	}
+	std_ac_if_desc.bInterfaceNumber = ret;
+	uac3->ac_intf = ret;
+	uac3->ac_alt = 0;
+
+	ret = usb_interface_id(cfg, fn);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't allocate AS OUT interface id on %s\n",
+				 fn->name, gadget->name);
+		goto err_free_in_fu_desc;
+	}
+	std_as_out_if0_desc.bInterfaceNumber = ret;
+	std_as_out_if1_desc.bInterfaceNumber = ret;
+	uac3->as_out_intf = ret;
+	uac3->as_out_alt = 0;
+
+	ret = usb_interface_id(cfg, fn);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't allocate AS IN interface id on %s\n",
+				 fn->name, gadget->name);
+		goto err_free_in_fu_desc;
+	}
+	std_as_in_if0_desc.bInterfaceNumber = ret;
+	std_as_in_if1_desc.bInterfaceNumber = ret;
+	uac3->as_in_intf = ret;
+	uac3->as_in_alt = 0;
+
+	/* Calculate wMaxPacketSize according to audio bandwidth */
+	set_ep_max_packet_size(uac3_opts, &fs_epin_desc, 1000, true);
+	set_ep_max_packet_size(uac3_opts, &fs_epout_desc, 1000, false);
+	set_ep_max_packet_size(uac3_opts, &hs_epin_desc, 8000, true);
+	set_ep_max_packet_size(uac3_opts, &hs_epout_desc, 8000, false);
+
+	audio->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
+	if (!audio->out_ep) {
+		dev_err(dev, "%s: can't autoconfigure on %s\n",
+				 fn->name, gadget->name);
+		ret = -ENODEV;
+		goto err_free_in_fu_desc;
+	}
+
+	audio->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
+	if (!audio->in_ep) {
+		dev_err(dev, "%s: can't autoconfigure on %s\n",
+				 fn->name, gadget->name);
+		ret = -ENODEV;
+		goto err_free_in_fu_desc;
+	}
+
+	audio->in_ep_maxpsize = max_t(u16,
+				le16_to_cpu(fs_epin_desc.wMaxPacketSize),
+				le16_to_cpu(hs_epin_desc.wMaxPacketSize));
+	audio->out_ep_maxpsize = max_t(u16,
+				le16_to_cpu(fs_epout_desc.wMaxPacketSize),
+				le16_to_cpu(hs_epout_desc.wMaxPacketSize));
+
+	hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
+	hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
+
+	/* Copy descriptors */
+	fn->fs_descriptors = uac3_copy_descriptors(USB_SPEED_FULL);
+	if (!fn->fs_descriptors)
+		goto err_free_in_fu_desc;
+	if (gadget_is_dualspeed(gadget)) {
+		fn->hs_descriptors = uac3_copy_descriptors(USB_SPEED_HIGH);
+		if (!fn->hs_descriptors)
+			goto err_free_in_fu_desc;
+	}
+
+	audio->gadget = gadget;
+
+	audio->params.p_chmask = uac3_opts->p_chmask;
+	audio->params.p_srate = uac3_opts->p_srate;
+	audio->params.p_ssize = uac3_opts->p_ssize;
+	audio->params.c_chmask = uac3_opts->c_chmask;
+	audio->params.c_srate = uac3_opts->c_srate;
+	audio->params.c_ssize = uac3_opts->c_ssize;
+	audio->params.req_number = uac3_opts->req_number;
+	ret = g_audio_setup(audio, "UAC3 PCM", "UAC3_Gadget");
+	if (ret)
+		goto err_free_descs;
+	return 0;
+
+err_free_descs:
+	usb_free_all_descriptors(fn);
+	audio->gadget = NULL;
+err_free_in_fu_desc:
+	kfree(usb_in_fu_desc);
+	usb_in_fu_desc = NULL;
+err_free_out_fu_desc:
+	kfree(usb_out_fu_desc);
+	usb_out_fu_desc = NULL;
+err_free_hc_desc:
+	list_for_each_entry(hc_desc, &uac3->hc_desc_list, list)
+		kfree(hc_desc);
+
+	return ret;
+}
+
+static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct g_audio *audio = func_to_g_audio(f);
+	struct f_uac3 *uac3 = func_to_uac3(f);
+	struct uac3_hc_desc *hc_desc;
+
+	g_audio_cleanup(audio);
+	usb_free_all_descriptors(f);
+	audio->gadget = NULL;
+	kfree(usb_in_fu_desc);
+	usb_in_fu_desc = NULL;
+	kfree(usb_out_fu_desc);
+	usb_out_fu_desc = NULL;
+
+	list_for_each_entry(hc_desc, &uac3->hc_desc_list, list)
+		kfree(hc_desc);
+}
+
+static int f_audio_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
+{
+	struct usb_composite_dev *cdev = fn->config->cdev;
+	struct f_uac3 *uac3 = func_to_uac3(fn);
+	struct usb_gadget *gadget = cdev->gadget;
+	struct device *dev = &gadget->dev;
+	int ret = 0;
+
+	/* No i/f has more than 2 alt settings */
+	if (alt > 1) {
+		dev_err(dev, "%s: Invalid altsetting %d\n", fn->name, alt);
+		return -EINVAL;
+	}
+
+	if (intf == uac3->ac_intf) {
+		/* Control I/f has only 1 AltSetting - 0 */
+		if (alt) {
+			dev_err(dev,
+				"%s: Invalid Control I/f altsetting %d\n",
+				fn->name, alt);
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	if (intf == uac3->as_out_intf) {
+		uac3->as_out_alt = alt;
+
+		if (alt)
+			ret = u_audio_start_capture(&uac3->g_audio);
+		else
+			u_audio_stop_capture(&uac3->g_audio);
+	} else if (intf == uac3->as_in_intf) {
+		uac3->as_in_alt = alt;
+
+		if (alt)
+			ret = u_audio_start_playback(&uac3->g_audio);
+		else
+			u_audio_stop_playback(&uac3->g_audio);
+	} else {
+		dev_err(dev, "%s: Invalid interface %d\n", fn->name, intf);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int f_audio_get_alt(struct usb_function *fn, unsigned intf)
+{
+	struct f_uac3 *uac3 = func_to_uac3(fn);
+	struct g_audio *audio = func_to_g_audio(fn);
+
+	if (intf == uac3->ac_intf)
+		return uac3->ac_alt;
+	else if (intf == uac3->as_out_intf)
+		return uac3->as_out_alt;
+	else if (intf == uac3->as_in_intf)
+		return uac3->as_in_alt;
+	else
+		dev_err(&audio->gadget->dev, "%s: Invalid interface %d\n",
+							fn->name, intf);
+
+	return -EINVAL;
+}
+
+static void f_audio_disable(struct usb_function *fn)
+{
+	struct f_uac3 *uac3 = func_to_uac3(fn);
+
+	uac3->as_in_alt = 0;
+	uac3->as_out_alt = 0;
+	u_audio_stop_capture(&uac3->g_audio);
+	u_audio_stop_playback(&uac3->g_audio);
+}
+
+static int in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct usb_request *req = fn->config->cdev->req;
+	struct g_audio *audio = func_to_g_audio(fn);
+	struct f_uac3_opts *opts;
+	u16 w_length = le16_to_cpu(cr->wLength);
+	u16 w_index = le16_to_cpu(cr->wIndex);
+	u16 w_value = le16_to_cpu(cr->wValue);
+	u8 entity_id = (w_index >> 8) & 0xff;
+	u8 control_selector = w_value >> 8;
+	int value = -EOPNOTSUPP;
+	int p_srate, c_srate;
+
+	opts = g_audio_to_uac3_opts(audio);
+	p_srate = opts->p_srate;
+	c_srate = opts->c_srate;
+
+	switch (entity_id) {
+	case USB_IN_CLK_ID:
+	case USB_OUT_CLK_ID:
+		if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+			struct cntrl_cur_lay3 c;
+			memset(&c, 0, sizeof(struct cntrl_cur_lay3));
+
+			if (entity_id == USB_IN_CLK_ID)
+				c.dCUR = cpu_to_le32(p_srate);
+			else if (entity_id == USB_OUT_CLK_ID)
+				c.dCUR = cpu_to_le32(c_srate);
+
+			value = min_t(unsigned, w_length, sizeof c);
+			memcpy(req->buf, &c, value);
+		} else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) {
+			*(u8 *)req->buf = 1;
+			value = min_t(unsigned, w_length, 1);
+		} else {
+			dev_err(&audio->gadget->dev,
+				"%s:%d control_selector=%d TODO!\n",
+				__func__, __LINE__, control_selector);
+		}
+		break;
+
+	case USB_OUT_PD_ID:
+	case USB_IN_PD_ID:
+		if (control_selector == UAC3_AC_POWER_DOMAIN_CONTROL) {
+			/* FIXME: hardcoded to Power Domain State D0 */
+			*(u8 *)req->buf = 0;
+			value = min_t(unsigned, w_length, 1);
+		} else {
+			dev_err(&audio->gadget->dev,
+				"%s:%d control_selector=%d TODO!\n",
+				__func__, __LINE__, control_selector);
+		}
+		break;
+
+	case USB_IN_FU_ID:
+	case USB_OUT_FU_ID:
+		if (control_selector == UAC_FU_MUTE) {
+			/* FIXME: hardcoded to false (not muted) */
+			*(u8 *)req->buf = 0;
+			value = min_t(unsigned, w_length, 1);
+		} else if (control_selector == UAC_FU_VOLUME) {
+			struct cntrl_cur_lay2 r;
+
+			/* FIXME: hardcoded to 0dB */
+			r.wCUR = 0;
+
+			value = min_t(unsigned, w_length, sizeof r);
+			memcpy(req->buf, &r, value);
+		} else {
+			dev_err(&audio->gadget->dev,
+				"%s:%d control_selector=%d TODO!\n",
+				__func__, __LINE__, control_selector);
+		}
+		break;
+
+	default:
+		value = -EOPNOTSUPP;
+		break;
+	}
+
+	return value;
+}
+
+static int
+in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct usb_request *req = fn->config->cdev->req;
+	struct g_audio *audio = func_to_g_audio(fn);
+	struct f_uac3_opts *opts;
+	u16 w_length = le16_to_cpu(cr->wLength);
+	u16 w_index = le16_to_cpu(cr->wIndex);
+	u16 w_value = le16_to_cpu(cr->wValue);
+	u8 entity_id = (w_index >> 8) & 0xff;
+	u8 control_selector = w_value >> 8;
+	int value = -EOPNOTSUPP;
+	int p_srate, c_srate;
+
+	opts = g_audio_to_uac3_opts(audio);
+	p_srate = opts->p_srate;
+	c_srate = opts->c_srate;
+
+
+	switch (entity_id) {
+	case USB_IN_CLK_ID:
+	case USB_OUT_CLK_ID: {
+		if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+			struct cntrl_range_lay3 r;
+
+			if (entity_id == USB_IN_CLK_ID)
+				r.dMIN = cpu_to_le32(p_srate);
+			else if (entity_id == USB_OUT_CLK_ID)
+				r.dMIN = cpu_to_le32(c_srate);
+			else
+				return -EOPNOTSUPP;
+
+			r.dMAX = r.dMIN;
+			r.dRES = 0;
+			r.wNumSubRanges = cpu_to_le16(1);
+
+			value = min_t(unsigned, w_length, sizeof r);
+			memcpy(req->buf, &r, value);
+		} else {
+			dev_err(&audio->gadget->dev,
+				"%s:%d control_selector=%d TODO!\n",
+				__func__, __LINE__, control_selector);
+		}
+		break;
+	}
+	case USB_IN_FU_ID:
+	case USB_OUT_FU_ID: {
+		if (control_selector == UAC_FU_VOLUME) {
+			struct cntrl_range_lay2 r;
+
+			r.wMIN = cpu_to_le16(0x8001); /* -127.9961 dB */
+			r.wMAX = 0; /* 0 dB */
+			r.wRES = cpu_to_le16(0x0001); /* in steps of 1/256 dB */
+			r.wNumSubRanges = cpu_to_le16(1);
+
+			value = min_t(unsigned, w_length, sizeof r);
+			memcpy(req->buf, &r, value);
+		} else {
+			dev_err(&audio->gadget->dev,
+				"%s:%d control_selector=%d TODO!\n",
+				__func__, __LINE__, control_selector);
+		}
+		break;
+	}
+	default:
+		dev_err(&audio->gadget->dev,
+			"%s:%d control_selector=%d TODO!\n",
+			__func__, __LINE__, control_selector);
+
+	}
+
+
+	return value;
+}
+
+static int out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct g_audio *audio = func_to_g_audio(fn);
+	u16 w_length = le16_to_cpu(cr->wLength);
+	u16 w_value = le16_to_cpu(cr->wValue);
+	u16 w_index = le16_to_cpu(cr->wIndex);
+	u8 entity_id = (w_index >> 8) & 0xff;
+	u8 control_selector = w_value >> 8;
+	int value = -EOPNOTSUPP;
+
+	/* TODO: implement real clock/mute/volume handling */
+	switch (entity_id) {
+	case USB_IN_CLK_ID:
+	case USB_OUT_CLK_ID:
+		if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+			value = w_length;
+		break;
+
+	case USB_IN_FU_ID:
+	case USB_OUT_FU_ID:
+		if ((control_selector == UAC_FU_MUTE)
+				|| (control_selector == UAC_FU_VOLUME))
+			value = w_length;
+		break;
+
+	default:
+		dev_err(&audio->gadget->dev,
+			"%s:%d control_selector=%d TODO!\n",
+			__func__, __LINE__, control_selector);
+	}
+
+	return value;
+}
+
+static int
+in_rq_hc_desc(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct f_uac3 *uac3 = func_to_uac3(fn);
+	struct g_audio *audio = func_to_g_audio(fn);
+	struct usb_request *req = fn->config->cdev->req;
+	u16 w_length = le16_to_cpu(cr->wLength);
+	u16 w_value = le16_to_cpu(cr->wValue);
+	struct uac3_hc_desc *hc_desc;
+	u16 hc_desc_len;
+	int value;
+
+	list_for_each_entry(hc_desc, &uac3->hc_desc_list, list) {
+		u16 w_desc_id;
+
+		w_desc_id = le16_to_cpu(hc_desc->hc_header->wDescriptorID);
+		if (w_desc_id == w_value)
+			goto found;
+	}
+	dev_err(&audio->gadget->dev, "No High Capability descriptor %d\n",
+				     w_value);
+	return -EOPNOTSUPP;
+
+found:
+	hc_desc_len = le16_to_cpu(hc_desc->hc_header->wLength);
+	value = min_t(unsigned, w_length, hc_desc_len);
+	memcpy(req->buf, hc_desc->hc_header, value);
+
+	return value;
+}
+
+static int ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	if (cr->bRequest == UAC3_CS_REQ_CUR)
+		return in_rq_cur(fn, cr);
+	else if (cr->bRequest == UAC3_CS_REQ_RANGE)
+		return in_rq_range(fn, cr);
+	else if (cr->bRequest == UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR)
+		return in_rq_hc_desc(fn, cr);
+	else
+		return -EOPNOTSUPP;
+}
+
+static int
+setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct f_uac3 *uac3 = func_to_uac3(fn);
+	struct g_audio *audio = func_to_g_audio(fn);
+	u16 w_index = le16_to_cpu(cr->wIndex);
+	u8 intf = w_index & 0xff;
+
+	if (intf != uac3->ac_intf) {
+		dev_err(&audio->gadget->dev,
+			"%s:%d Error!\n", __func__, __LINE__);
+		return -EOPNOTSUPP;
+	}
+
+	if (cr->bRequestType & USB_DIR_IN)
+		return ac_rq_in(fn, cr);
+	else if (cr->bRequest == UAC3_CS_REQ_CUR)
+		return out_rq_cur(fn, cr);
+
+	return -EOPNOTSUPP;
+}
+
+static int
+f_audio_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct usb_composite_dev *cdev = fn->config->cdev;
+	struct g_audio *audio = func_to_g_audio(fn);
+	struct usb_request *req = cdev->req;
+	u16 w_length = le16_to_cpu(cr->wLength);
+	int value = -EOPNOTSUPP;
+
+	/* Only Class specific requests are supposed to reach here */
+	if ((cr->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
+		return -EOPNOTSUPP;
+
+	if ((cr->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE)
+		value = setup_rq_inf(fn, cr);
+	else
+		dev_err(&audio->gadget->dev, "%s:%d Error!\n",
+				__func__, __LINE__);
+
+	if (value >= 0) {
+		req->length = value;
+		req->zero = value < w_length;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			dev_err(&audio->gadget->dev,
+				"%s:%d Error!\n", __func__, __LINE__);
+			req->status = 0;
+		}
+	}
+
+	return value;
+}
+
+static inline struct f_uac3_opts *to_f_uac3_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_uac3_opts,
+			    func_inst.group);
+}
+
+static void f_uac3_attr_release(struct config_item *item)
+{
+	struct f_uac3_opts *opts = to_f_uac3_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_uac3_item_ops = {
+	.release	= f_uac3_attr_release,
+};
+
+#define UAC3_ATTRIBUTE_CHMASK(name)					\
+static ssize_t f_uac3_opts_##name##_show(struct config_item *item,	\
+					 char *page)			\
+{									\
+	struct f_uac3_opts *opts = to_f_uac3_opts(item);		\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%u\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_uac3_opts_##name##_store(struct config_item *item,	\
+					  const char *page, size_t len)	\
+{									\
+	struct f_uac3_opts *opts = to_f_uac3_opts(item);		\
+	int ret;							\
+	u32 num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou32(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+	/* FIXME: only mono/stereo supported at this time */		\
+	if (num & (~0x3)) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+									\
+	opts->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_uac3_opts_, name)
+
+#define UAC3_ATTRIBUTE(name)						\
+static ssize_t f_uac3_opts_##name##_show(struct config_item *item,	\
+					 char *page)			\
+{									\
+	struct f_uac3_opts *opts = to_f_uac3_opts(item);		\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%u\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_uac3_opts_##name##_store(struct config_item *item,	\
+					  const char *page, size_t len)	\
+{									\
+	struct f_uac3_opts *opts = to_f_uac3_opts(item);		\
+	int ret;							\
+	u32 num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou32(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	opts->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_uac3_opts_, name)
+
+UAC3_ATTRIBUTE_CHMASK(p_chmask);
+UAC3_ATTRIBUTE(p_srate);
+UAC3_ATTRIBUTE(p_ssize);
+UAC3_ATTRIBUTE_CHMASK(c_chmask);
+UAC3_ATTRIBUTE(c_srate);
+UAC3_ATTRIBUTE(c_ssize);
+UAC3_ATTRIBUTE(req_number);
+
+static struct configfs_attribute *f_uac3_attrs[] = {
+	&f_uac3_opts_attr_p_chmask,
+	&f_uac3_opts_attr_p_srate,
+	&f_uac3_opts_attr_p_ssize,
+	&f_uac3_opts_attr_c_chmask,
+	&f_uac3_opts_attr_c_srate,
+	&f_uac3_opts_attr_c_ssize,
+	&f_uac3_opts_attr_req_number,
+	NULL,
+};
+
+static struct config_item_type f_uac3_func_type = {
+	.ct_item_ops	= &f_uac3_item_ops,
+	.ct_attrs	= f_uac3_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void f_audio_free_inst(struct usb_function_instance *f)
+{
+	struct f_uac3_opts *opts;
+
+	opts = container_of(f, struct f_uac3_opts, func_inst);
+	kfree(opts);
+}
+
+static struct usb_function_instance *f_audio_alloc_inst(void)
+{
+	struct f_uac3_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = f_audio_free_inst;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &f_uac3_func_type);
+
+	opts->p_chmask = UAC3_DEF_PCHMASK;
+	opts->p_srate = UAC3_DEF_PSRATE;
+	opts->p_ssize = UAC3_DEF_PSSIZE;
+	opts->c_chmask = UAC3_DEF_CCHMASK;
+	opts->c_srate = UAC3_DEF_CSRATE;
+	opts->c_ssize = UAC3_DEF_CSSIZE;
+	opts->req_number = UAC3_DEF_REQ_NUM;
+	return &opts->func_inst;
+}
+
+static void f_audio_free(struct usb_function *f)
+{
+	struct g_audio *audio;
+	struct f_uac3_opts *opts;
+
+	audio = func_to_g_audio(f);
+	opts = container_of(f->fi, struct f_uac3_opts, func_inst);
+	kfree(audio);
+	mutex_lock(&opts->lock);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
+}
+
+static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
+{
+	struct f_uac3	*uac3;
+	struct f_uac3_opts *opts;
+
+	uac3 = kzalloc(sizeof(*uac3), GFP_KERNEL);
+	if (!uac3)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_uac3_opts, func_inst);
+	mutex_lock(&opts->lock);
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
+
+	uac3->g_audio.func.name = "uac3_func";
+	uac3->g_audio.func.bind = f_audio_bind;
+	uac3->g_audio.func.unbind = f_audio_unbind;
+	uac3->g_audio.func.set_alt = f_audio_set_alt;
+	uac3->g_audio.func.get_alt = f_audio_get_alt;
+	uac3->g_audio.func.disable = f_audio_disable;
+	uac3->g_audio.func.setup = f_audio_setup;
+	uac3->g_audio.func.free_func = f_audio_free;
+
+	return &uac3->g_audio.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(uac3, f_audio_alloc_inst, f_audio_alloc);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/u_uac3.h b/drivers/usb/gadget/function/u_uac3.h
new file mode 100644
index 0000000..0ad594f
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac3.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_uac3.h
+ *
+ * Utility definitions for UAC3 function
+ *
+ * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ */
+
+#ifndef __U_UAC3_H
+#define __U_UAC3_H
+
+#include <linux/usb/composite.h>
+
+#define UAC3_DEF_PCHMASK 0x3
+#define UAC3_DEF_PSRATE 48000
+#define UAC3_DEF_PSSIZE 2
+#define UAC3_DEF_CCHMASK 0x3
+#define UAC3_DEF_CSRATE 48000
+#define UAC3_DEF_CSSIZE 2
+#define UAC3_DEF_REQ_NUM 2
+
+struct f_uac3_opts {
+	struct usb_function_instance	func_inst;
+	int				p_chmask;
+	int				p_srate;
+	int				p_ssize;
+	int				c_chmask;
+	int				c_srate;
+	int				c_ssize;
+	int				req_number;
+	bool				bound;
+
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* __U_UAC3_H */
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index a12fb45..dc104974 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -57,7 +57,8 @@ config USB_AUDIO
 	select USB_F_UAC1 if (GADGET_UAC1 && !GADGET_UAC1_LEGACY)
 	select USB_F_UAC1_LEGACY if (GADGET_UAC1 && GADGET_UAC1_LEGACY)
 	select USB_F_UAC2 if !GADGET_UAC1
-	select USB_U_AUDIO if (USB_F_UAC2 || USB_F_UAC1)
+	select USB_F_UAC3 if !GADGET_UAC1_LEGACY
+	select USB_U_AUDIO if (USB_F_UAC3 || USB_F_UAC2 || USB_F_UAC1)
 	help
 	  This Gadget Audio driver is compatible with USB Audio Class
 	  specification 2.0. It implements 1 AudioControl interface,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/1] USB Audio Device Class 3.0 Gadget support
  2017-11-07  1:52 [PATCH 0/1] USB Audio Device Class 3.0 Gadget support Ruslan Bilovol
  2017-11-07  1:52 ` [PATCH 1/1] usb: gadget: add USB Audio Device Class 3.0 gadget support Ruslan Bilovol
@ 2017-11-07  2:04 ` Ruslan Bilovol
  2017-12-04 11:36   ` Felipe Balbi
  1 sibling, 1 reply; 9+ messages in thread
From: Ruslan Bilovol @ 2017-11-07  2:04 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Greg Kroah-Hartman, linux-usb, linux-doc, linux-kernel

On Tue, Nov 7, 2017 at 3:52 AM, Ruslan Bilovol <ruslan.bilovol@gmail.com> wrote:
> Hi,
>
> This patch adds USB Audio Device Class 3.0 [1] function
> support to gadget subsystem.
> I didn't add UAC3 support to legacy gadget as it will
> make preprocessor configuration too complex (UAC3 device
> must have two configurations for backward compatibility,
> first is UAC1/2 and second is UAC3), yet also I'm too lazy
> to do that and verify all possible configurations.
>
> For modern ConfigFS interface I'll provide my configuration
> for testing below; testing was done on a BeagleBone Black
> board.
>
> This patch depends on uac3 header files from include dir
> which I'll post as part of ALSA host UAC3 patch and will
> provide the link to it here.

http://www.spinics.net/lists/alsa-devel/msg69071.html

Thanks,
Ruslan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] usb: gadget: add USB Audio Device Class 3.0 gadget support
  2017-11-07  1:52 ` [PATCH 1/1] usb: gadget: add USB Audio Device Class 3.0 gadget support Ruslan Bilovol
@ 2017-11-07 11:34   ` kbuild test robot
  2017-11-07 13:20   ` kbuild test robot
  1 sibling, 0 replies; 9+ messages in thread
From: kbuild test robot @ 2017-11-07 11:34 UTC (permalink / raw)
  To: Ruslan Bilovol
  Cc: kbuild-all, Felipe Balbi, Greg Kroah-Hartman, linux-usb,
	linux-doc, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1192 bytes --]

Hi Ruslan,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on balbi-usb/next]
[also build test ERROR on v4.14-rc8 next-20171107]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Ruslan-Bilovol/usb-gadget-add-USB-Audio-Device-Class-3-0-gadget-support/20171107-175202
base:   https://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git next
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

>> drivers/usb/gadget/function/f_uac3.c:10:32: fatal error: linux/usb/audio-v3.h: No such file or directory
    #include <linux/usb/audio-v3.h>
                                   ^
   compilation terminated.

vim +10 drivers/usb/gadget/function/f_uac3.c

  > 10	#include <linux/usb/audio-v3.h>
    11	#include <linux/module.h>
    12	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 61817 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] usb: gadget: add USB Audio Device Class 3.0 gadget support
  2017-11-07  1:52 ` [PATCH 1/1] usb: gadget: add USB Audio Device Class 3.0 gadget support Ruslan Bilovol
  2017-11-07 11:34   ` kbuild test robot
@ 2017-11-07 13:20   ` kbuild test robot
  1 sibling, 0 replies; 9+ messages in thread
From: kbuild test robot @ 2017-11-07 13:20 UTC (permalink / raw)
  To: Ruslan Bilovol
  Cc: kbuild-all, Felipe Balbi, Greg Kroah-Hartman, linux-usb,
	linux-doc, linux-kernel

Hi Ruslan,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on balbi-usb/next]
[also build test WARNING on v4.14-rc8 next-20171107]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Ruslan-Bilovol/usb-gadget-add-USB-Audio-Device-Class-3-0-gadget-support/20171107-175202
base:   https://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git next


coccinelle warnings: (new ones prefixed by >>)

>> drivers/usb/gadget/function/f_uac3.c:766:2-9: alloc with no test, possible model on line 780

vim +766 drivers/usb/gadget/function/f_uac3.c

   722	
   723	static int f_audio_bind(struct usb_configuration *cfg, struct usb_function *fn)
   724	{
   725		struct f_uac3 *uac3 = func_to_uac3(fn);
   726		struct g_audio *audio = func_to_g_audio(fn);
   727		struct usb_composite_dev *cdev = cfg->cdev;
   728		struct usb_gadget *gadget = cdev->gadget;
   729		struct device *dev = &gadget->dev;
   730		struct f_uac3_opts *uac3_opts;
   731		struct uac3_hc_descriptor_header *cluster_desc;
   732		struct uac3_hc_desc *hc_desc;
   733		struct usb_string *us;
   734		u16 hc_desc_id = 1; /* HC id always starts from 1 */
   735		int ret;
   736	
   737		uac3_opts = container_of(fn->fi, struct f_uac3_opts, func_inst);
   738	
   739		us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
   740		if (IS_ERR(us))
   741			return PTR_ERR(us);
   742	
   743		iad_desc.iFunction = us[STR_ASSOC].id;
   744		std_ac_if_desc.iInterface = us[STR_IF_CTRL].id;
   745		std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
   746		std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id;
   747		std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id;
   748		std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id;
   749	
   750		INIT_LIST_HEAD(&uac3->hc_desc_list);
   751	
   752		/* Initialize the configurable parameters */
   753		cluster_desc = build_cluster_descriptor(uac3_opts, 0); /* capture */
   754		if (cluster_desc) {
   755			hc_desc = kzalloc(sizeof *hc_desc, GFP_KERNEL);
   756			hc_desc->hc_header = cluster_desc;
   757			list_add(&hc_desc->list, &uac3->hc_desc_list);
   758			cluster_desc->wDescriptorID = cpu_to_le16(hc_desc_id);
   759			usb_out_it_desc.wClusterDescrID = cluster_desc->wDescriptorID;
   760			as_out_hdr_desc.wClusterDescrID = cluster_desc->wDescriptorID;
   761			hc_desc_id++;
   762		}
   763	
   764		cluster_desc = build_cluster_descriptor(uac3_opts, 1); /* playback */
   765		if (cluster_desc) {
 > 766			hc_desc = kzalloc(sizeof *hc_desc, GFP_KERNEL);
   767			hc_desc->hc_header = cluster_desc;
   768			list_add(&hc_desc->list, &uac3->hc_desc_list);
   769			cluster_desc->wDescriptorID = cpu_to_le16(hc_desc_id);
   770			io_in_it_desc.wClusterDescrID = cluster_desc->wDescriptorID;
   771			as_in_hdr_desc.wClusterDescrID = cluster_desc->wDescriptorID;
   772		}
   773	
   774		as_out_hdr_desc.bSubslotSize = uac3_opts->c_ssize;
   775		as_out_hdr_desc.bBitResolution = uac3_opts->c_ssize * 8;
   776		as_in_hdr_desc.bSubslotSize = uac3_opts->p_ssize;
   777		as_in_hdr_desc.bBitResolution = uac3_opts->p_ssize * 8;
   778	
   779		/* alloc and configure Feature Unit descriptors */
 > 780		usb_out_fu_desc = alloc_fu_desc(num_channels(uac3_opts->c_chmask),
   781						USB_OUT_FU_ID,
   782						USB_OUT_IT_ID);
   783		if (!usb_out_fu_desc) {
   784			dev_err(dev, "%s: can't allocate OUT FU descriptor on %s\n",
   785					 fn->name, gadget->name);
   786			ret = -ENOMEM;
   787			goto err_free_hc_desc;
   788		}
   789	
   790		usb_in_fu_desc = alloc_fu_desc(num_channels(uac3_opts->p_chmask),
   791						USB_IN_FU_ID,
   792						IO_IN_IT_ID);
   793		if (!usb_in_fu_desc) {
   794			dev_err(dev, "%s: can't allocate IN FU descriptor on %s\n",
   795					 fn->name, gadget->name);
   796			ret = -ENOMEM;
   797			goto err_free_out_fu_desc;
   798		}
   799	
   800		/* update AC desc size with allocated FUs */
   801		ac_hdr_desc.wTotalLength = cpu_to_le16(
   802				  sizeof in_clk_src_desc + sizeof out_clk_src_desc
   803				+ sizeof usb_out_it_desc + sizeof io_in_it_desc
   804				+ sizeof usb_in_ot_desc + sizeof io_out_ot_desc
   805				+ sizeof usb_in_pd_desc + sizeof usb_out_pd_desc
   806				+ usb_out_fu_desc->bLength + usb_in_fu_desc->bLength);
   807	
   808		ret = usb_interface_id(cfg, fn);
   809		if (ret < 0) {
   810			dev_err(dev, "%s: can't allocate AC interface id on %s\n",
   811					 fn->name, gadget->name);
   812			goto err_free_in_fu_desc;
   813		}
   814		std_ac_if_desc.bInterfaceNumber = ret;
   815		uac3->ac_intf = ret;
   816		uac3->ac_alt = 0;
   817	
   818		ret = usb_interface_id(cfg, fn);
   819		if (ret < 0) {
   820			dev_err(dev, "%s: can't allocate AS OUT interface id on %s\n",
   821					 fn->name, gadget->name);
   822			goto err_free_in_fu_desc;
   823		}
   824		std_as_out_if0_desc.bInterfaceNumber = ret;
   825		std_as_out_if1_desc.bInterfaceNumber = ret;
   826		uac3->as_out_intf = ret;
   827		uac3->as_out_alt = 0;
   828	
   829		ret = usb_interface_id(cfg, fn);
   830		if (ret < 0) {
   831			dev_err(dev, "%s: can't allocate AS IN interface id on %s\n",
   832					 fn->name, gadget->name);
   833			goto err_free_in_fu_desc;
   834		}
   835		std_as_in_if0_desc.bInterfaceNumber = ret;
   836		std_as_in_if1_desc.bInterfaceNumber = ret;
   837		uac3->as_in_intf = ret;
   838		uac3->as_in_alt = 0;
   839	
   840		/* Calculate wMaxPacketSize according to audio bandwidth */
   841		set_ep_max_packet_size(uac3_opts, &fs_epin_desc, 1000, true);
   842		set_ep_max_packet_size(uac3_opts, &fs_epout_desc, 1000, false);
   843		set_ep_max_packet_size(uac3_opts, &hs_epin_desc, 8000, true);
   844		set_ep_max_packet_size(uac3_opts, &hs_epout_desc, 8000, false);
   845	
   846		audio->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
   847		if (!audio->out_ep) {
   848			dev_err(dev, "%s: can't autoconfigure on %s\n",
   849					 fn->name, gadget->name);
   850			ret = -ENODEV;
   851			goto err_free_in_fu_desc;
   852		}
   853	
   854		audio->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
   855		if (!audio->in_ep) {
   856			dev_err(dev, "%s: can't autoconfigure on %s\n",
   857					 fn->name, gadget->name);
   858			ret = -ENODEV;
   859			goto err_free_in_fu_desc;
   860		}
   861	
   862		audio->in_ep_maxpsize = max_t(u16,
   863					le16_to_cpu(fs_epin_desc.wMaxPacketSize),
   864					le16_to_cpu(hs_epin_desc.wMaxPacketSize));
   865		audio->out_ep_maxpsize = max_t(u16,
   866					le16_to_cpu(fs_epout_desc.wMaxPacketSize),
   867					le16_to_cpu(hs_epout_desc.wMaxPacketSize));
   868	
   869		hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
   870		hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
   871	
   872		/* Copy descriptors */
   873		fn->fs_descriptors = uac3_copy_descriptors(USB_SPEED_FULL);
   874		if (!fn->fs_descriptors)
   875			goto err_free_in_fu_desc;
   876		if (gadget_is_dualspeed(gadget)) {
   877			fn->hs_descriptors = uac3_copy_descriptors(USB_SPEED_HIGH);
   878			if (!fn->hs_descriptors)
   879				goto err_free_in_fu_desc;
   880		}
   881	
   882		audio->gadget = gadget;
   883	
   884		audio->params.p_chmask = uac3_opts->p_chmask;
   885		audio->params.p_srate = uac3_opts->p_srate;
   886		audio->params.p_ssize = uac3_opts->p_ssize;
   887		audio->params.c_chmask = uac3_opts->c_chmask;
   888		audio->params.c_srate = uac3_opts->c_srate;
   889		audio->params.c_ssize = uac3_opts->c_ssize;
   890		audio->params.req_number = uac3_opts->req_number;
   891		ret = g_audio_setup(audio, "UAC3 PCM", "UAC3_Gadget");
   892		if (ret)
   893			goto err_free_descs;
   894		return 0;
   895	
   896	err_free_descs:
   897		usb_free_all_descriptors(fn);
   898		audio->gadget = NULL;
   899	err_free_in_fu_desc:
   900		kfree(usb_in_fu_desc);
   901		usb_in_fu_desc = NULL;
   902	err_free_out_fu_desc:
   903		kfree(usb_out_fu_desc);
   904		usb_out_fu_desc = NULL;
   905	err_free_hc_desc:
   906		list_for_each_entry(hc_desc, &uac3->hc_desc_list, list)
   907			kfree(hc_desc);
   908	
   909		return ret;
   910	}
   911	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/1] USB Audio Device Class 3.0 Gadget support
  2017-11-07  2:04 ` [PATCH 0/1] USB Audio Device Class 3.0 Gadget support Ruslan Bilovol
@ 2017-12-04 11:36   ` Felipe Balbi
  2017-12-07 11:18     ` Ruslan Bilovol
  0 siblings, 1 reply; 9+ messages in thread
From: Felipe Balbi @ 2017-12-04 11:36 UTC (permalink / raw)
  To: Ruslan Bilovol; +Cc: Greg Kroah-Hartman, linux-usb, linux-doc, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1076 bytes --]


Hi,

Ruslan Bilovol <ruslan.bilovol@gmail.com> writes:
> On Tue, Nov 7, 2017 at 3:52 AM, Ruslan Bilovol <ruslan.bilovol@gmail.com> wrote:
>> Hi,
>>
>> This patch adds USB Audio Device Class 3.0 [1] function
>> support to gadget subsystem.
>> I didn't add UAC3 support to legacy gadget as it will
>> make preprocessor configuration too complex (UAC3 device
>> must have two configurations for backward compatibility,
>> first is UAC1/2 and second is UAC3), yet also I'm too lazy
>> to do that and verify all possible configurations.
>>
>> For modern ConfigFS interface I'll provide my configuration
>> for testing below; testing was done on a BeagleBone Black
>> board.
>>
>> This patch depends on uac3 header files from include dir
>> which I'll post as part of ALSA host UAC3 patch and will
>> provide the link to it here.
>
> http://www.spinics.net/lists/alsa-devel/msg69071.html

Once that patch hits upstream, then we can queue this for merge window
otherwise we will just have issues and create unbisectable points in the
tree.

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/1] USB Audio Device Class 3.0 Gadget support
  2017-12-04 11:36   ` Felipe Balbi
@ 2017-12-07 11:18     ` Ruslan Bilovol
  2018-06-13  0:28         ` Ruslan Bilovol
  0 siblings, 1 reply; 9+ messages in thread
From: Ruslan Bilovol @ 2017-12-07 11:18 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Greg Kroah-Hartman, linux-usb, linux-doc, linux-kernel

Hi Felipe,

On Mon, Dec 4, 2017 at 1:36 PM, Felipe Balbi <balbi@kernel.org> wrote:
>
> Hi,
>
> Ruslan Bilovol <ruslan.bilovol@gmail.com> writes:
>> On Tue, Nov 7, 2017 at 3:52 AM, Ruslan Bilovol <ruslan.bilovol@gmail.com> wrote:
>>> Hi,
>>>
>>> This patch adds USB Audio Device Class 3.0 [1] function
>>> support to gadget subsystem.
>>> I didn't add UAC3 support to legacy gadget as it will
>>> make preprocessor configuration too complex (UAC3 device
>>> must have two configurations for backward compatibility,
>>> first is UAC1/2 and second is UAC3), yet also I'm too lazy
>>> to do that and verify all possible configurations.
>>>
>>> For modern ConfigFS interface I'll provide my configuration
>>> for testing below; testing was done on a BeagleBone Black
>>> board.
>>>
>>> This patch depends on uac3 header files from include dir
>>> which I'll post as part of ALSA host UAC3 patch and will
>>> provide the link to it here.
>>
>> http://www.spinics.net/lists/alsa-devel/msg69071.html
>
> Once that patch hits upstream, then we can queue this for merge window
> otherwise we will just have issues and create unbisectable points in the
> tree.

Takashi promised to create an immutable branch for that purpose.

However, I'm currently reworking configfs part of UAC3 for channels
configuration handling, which is now more clear after sharing missing
parts of UAC3 spec by Pierre-Louis Bossart during host side patches
review; so I will send v2 soon.

Thanks,
Ruslan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/1] USB Audio Device Class 3.0 Gadget support
  2017-12-07 11:18     ` Ruslan Bilovol
@ 2018-06-13  0:28         ` Ruslan Bilovol
  0 siblings, 0 replies; 9+ messages in thread
From: Ruslan Bilovol @ 2018-06-13  0:28 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Greg Kroah-Hartman, linux-usb, linux-doc, linux-kernel

On Thu, Dec 7, 2017 at 1:18 PM, Ruslan Bilovol <ruslan.bilovol@gmail.com> wrote:
> Hi Felipe,
>
> On Mon, Dec 4, 2017 at 1:36 PM, Felipe Balbi <balbi@kernel.org> wrote:
>>
>> Hi,
>>
>> Ruslan Bilovol <ruslan.bilovol@gmail.com> writes:
>>> On Tue, Nov 7, 2017 at 3:52 AM, Ruslan Bilovol <ruslan.bilovol@gmail.com> wrote:
>>>> Hi,
>>>>
>>>> This patch adds USB Audio Device Class 3.0 [1] function
>>>> support to gadget subsystem.
>>>> I didn't add UAC3 support to legacy gadget as it will
>>>> make preprocessor configuration too complex (UAC3 device
>>>> must have two configurations for backward compatibility,
>>>> first is UAC1/2 and second is UAC3), yet also I'm too lazy
>>>> to do that and verify all possible configurations.
>>>>
>>>> For modern ConfigFS interface I'll provide my configuration
>>>> for testing below; testing was done on a BeagleBone Black
>>>> board.
>>>>
>>>> This patch depends on uac3 header files from include dir
>>>> which I'll post as part of ALSA host UAC3 patch and will
>>>> provide the link to it here.
>>>
>>> http://www.spinics.net/lists/alsa-devel/msg69071.html
>>
>> Once that patch hits upstream, then we can queue this for merge window
>> otherwise we will just have issues and create unbisectable points in the
>> tree.
>
> Takashi promised to create an immutable branch for that purpose.
>
> However, I'm currently reworking configfs part of UAC3 for channels
> configuration handling, which is now more clear after sharing missing
> parts of UAC3 spec by Pierre-Louis Bossart during host side patches
> review; so I will send v2 soon.

OK, so now we have both UAC3 initial support patches [1] and
also UAC3 BADD profiles support [2] in Torvalds tree, and I'm going
to refresh this patch series and send v2 soon (perhaps in next few weeks)

[1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9a2fe9b801f585baccf8352d82839dcd54b300cf
[2]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=17156f23e93c0f59e06dd2aaffd06221341caaee

Thanks,
Ruslan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/1] USB Audio Device Class 3.0 Gadget support
@ 2018-06-13  0:28         ` Ruslan Bilovol
  0 siblings, 0 replies; 9+ messages in thread
From: Ruslan Bilovol @ 2018-06-13  0:28 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Greg Kroah-Hartman, linux-usb, linux-doc, linux-kernel

On Thu, Dec 7, 2017 at 1:18 PM, Ruslan Bilovol <ruslan.bilovol@gmail.com> wrote:
> Hi Felipe,
>
> On Mon, Dec 4, 2017 at 1:36 PM, Felipe Balbi <balbi@kernel.org> wrote:
>>
>> Hi,
>>
>> Ruslan Bilovol <ruslan.bilovol@gmail.com> writes:
>>> On Tue, Nov 7, 2017 at 3:52 AM, Ruslan Bilovol <ruslan.bilovol@gmail.com> wrote:
>>>> Hi,
>>>>
>>>> This patch adds USB Audio Device Class 3.0 [1] function
>>>> support to gadget subsystem.
>>>> I didn't add UAC3 support to legacy gadget as it will
>>>> make preprocessor configuration too complex (UAC3 device
>>>> must have two configurations for backward compatibility,
>>>> first is UAC1/2 and second is UAC3), yet also I'm too lazy
>>>> to do that and verify all possible configurations.
>>>>
>>>> For modern ConfigFS interface I'll provide my configuration
>>>> for testing below; testing was done on a BeagleBone Black
>>>> board.
>>>>
>>>> This patch depends on uac3 header files from include dir
>>>> which I'll post as part of ALSA host UAC3 patch and will
>>>> provide the link to it here.
>>>
>>> http://www.spinics.net/lists/alsa-devel/msg69071.html
>>
>> Once that patch hits upstream, then we can queue this for merge window
>> otherwise we will just have issues and create unbisectable points in the
>> tree.
>
> Takashi promised to create an immutable branch for that purpose.
>
> However, I'm currently reworking configfs part of UAC3 for channels
> configuration handling, which is now more clear after sharing missing
> parts of UAC3 spec by Pierre-Louis Bossart during host side patches
> review; so I will send v2 soon.

OK, so now we have both UAC3 initial support patches [1] and
also UAC3 BADD profiles support [2] in Torvalds tree, and I'm going
to refresh this patch series and send v2 soon (perhaps in next few weeks)

[1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9a2fe9b801f585baccf8352d82839dcd54b300cf
[2]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=17156f23e93c0f59e06dd2aaffd06221341caaee

Thanks,
Ruslan
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2018-06-13  0:28 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-07  1:52 [PATCH 0/1] USB Audio Device Class 3.0 Gadget support Ruslan Bilovol
2017-11-07  1:52 ` [PATCH 1/1] usb: gadget: add USB Audio Device Class 3.0 gadget support Ruslan Bilovol
2017-11-07 11:34   ` kbuild test robot
2017-11-07 13:20   ` kbuild test robot
2017-11-07  2:04 ` [PATCH 0/1] USB Audio Device Class 3.0 Gadget support Ruslan Bilovol
2017-12-04 11:36   ` Felipe Balbi
2017-12-07 11:18     ` Ruslan Bilovol
2018-06-13  0:28       ` Ruslan Bilovol
2018-06-13  0:28         ` Ruslan Bilovol

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.