* [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.