2313 lines · 64.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * f_uac2.c -- USB Audio Class 2.0 Function * * Copyright (C) 2011 *    Yadwinder Singh (yadi.brar01@gmail.com) *    Jaswinder Singh (jaswinder.singh@linaro.org) * * Copyright (C) 2020 *    Ruslan Bilovol (ruslan.bilovol@gmail.com) */ #include <linux/usb/audio.h>#include <linux/usb/audio-v2.h>#include <linux/module.h> #include "u_audio.h" #include "u_uac2.h" /* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */#define UAC2_CHANNEL_MASK 0x07FFFFFF /* * The driver implements a simple UAC_2 topology. * USB-OUT -> IT_1 -> FU -> OT_3 -> ALSA_Capture * ALSA_Playback -> IT_2 -> FU -> OT_4 -> USB-IN * Capture and Playback sampling rates are independently *  controlled by two clock sources : *    CLK_5 := c_srate, and CLK_6 := p_srate */#define USB_OUT_CLK_ID	(out_clk_src_desc.bClockID)#define USB_IN_CLK_ID	(in_clk_src_desc.bClockID)#define USB_OUT_FU_ID	(out_feature_unit_desc->bUnitID)#define USB_IN_FU_ID	(in_feature_unit_desc->bUnitID) #define CONTROL_ABSENT	0#define CONTROL_RDONLY	1#define CONTROL_RDWR	3 #define CLK_FREQ_CTRL	0#define CLK_VLD_CTRL	2#define FU_MUTE_CTRL	0#define FU_VOL_CTRL	2 #define COPY_CTRL	0#define CONN_CTRL	2#define OVRLD_CTRL	4#define CLSTR_CTRL	6#define UNFLW_CTRL	8#define OVFLW_CTRL	10 #define EPIN_EN(_opts) ((_opts)->p_chmask != 0)#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)#define FUIN_EN(_opts) (EPIN_EN(_opts) \				&& ((_opts)->p_mute_present \				|| (_opts)->p_volume_present))#define FUOUT_EN(_opts) (EPOUT_EN(_opts) \				&& ((_opts)->c_mute_present \				|| (_opts)->c_volume_present))#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC) struct f_uac2 {	struct g_audio g_audio;	u8 ac_intf, as_in_intf, as_out_intf;	u8 ac_alt, as_in_alt, as_out_alt;	/* needed for get_alt() */ 	struct usb_ctrlrequest setup_cr;	/* will be used in data stage */ 	/* Interrupt IN endpoint of AC interface */	struct usb_ep	*int_ep;	atomic_t	int_count;	/* transient state, only valid during handling of a single control request */	int clock_id;}; static inline struct f_uac2 *func_to_uac2(struct usb_function *f){	return container_of(f, struct f_uac2, g_audio.func);} static inlinestruct f_uac2_opts *g_audio_to_uac2_opts(struct g_audio *agdev){	return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);} static int afunc_notify(struct g_audio *agdev, int unit_id, int cs); /* --------- USB Function Interface ------------- */ enum {	STR_ASSOC,	STR_IF_CTRL,	STR_CLKSRC_IN,	STR_CLKSRC_OUT,	STR_USB_IT,	STR_USB_IT_CH,	STR_IO_IT,	STR_IO_IT_CH,	STR_USB_OT,	STR_IO_OT,	STR_FU_IN,	STR_FU_OUT,	STR_AS_OUT_ALT0,	STR_AS_OUT_ALT1,	STR_AS_IN_ALT0,	STR_AS_IN_ALT1,	NUM_STR_DESCRIPTORS,}; static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {}; static const char *const speed_names[] = {	[USB_SPEED_UNKNOWN] = "UNKNOWN",	[USB_SPEED_LOW] = "LS",	[USB_SPEED_FULL] = "FS",	[USB_SPEED_HIGH] = "HS",	[USB_SPEED_WIRELESS] = "W",	[USB_SPEED_SUPER] = "SS",	[USB_SPEED_SUPER_PLUS] = "SS+",}; 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 = UAC2_FUNCTION_SUBCLASS_UNDEFINED,	.bFunctionProtocol = UAC_VERSION_2,}; /* 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 = DYNAMIC */	.bInterfaceClass = USB_CLASS_AUDIO,	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,	.bInterfaceProtocol = UAC_VERSION_2,}; /* Clock source for IN traffic */static struct uac_clock_source_descriptor in_clk_src_desc = {	.bLength = sizeof in_clk_src_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC2_CLOCK_SOURCE,	/* .bClockID = DYNAMIC */	.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,	.bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),	.bAssocTerminal = 0,}; /* Clock source for OUT traffic */static struct uac_clock_source_descriptor out_clk_src_desc = {	.bLength = sizeof out_clk_src_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC2_CLOCK_SOURCE,	/* .bClockID = DYNAMIC */	.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,	.bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),	.bAssocTerminal = 0,}; /* Input Terminal for USB_OUT */static struct uac2_input_terminal_descriptor usb_out_it_desc = {	.bLength = sizeof usb_out_it_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC_INPUT_TERMINAL,	/* .bTerminalID = DYNAMIC */	.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),	.bAssocTerminal = 0,	/* .bCSourceID = DYNAMIC */	.iChannelNames = 0,	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),}; /* Input Terminal for I/O-In */static struct uac2_input_terminal_descriptor io_in_it_desc = {	.bLength = sizeof io_in_it_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC_INPUT_TERMINAL,	/* .bTerminalID = DYNAMIC */	/* .wTerminalType = DYNAMIC */	.bAssocTerminal = 0,	/* .bCSourceID = DYNAMIC */	.iChannelNames = 0,	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),}; /* Ouput Terminal for USB_IN */static struct uac2_output_terminal_descriptor usb_in_ot_desc = {	.bLength = sizeof usb_in_ot_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,	/* .bTerminalID = DYNAMIC */	.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),	.bAssocTerminal = 0,	/* .bSourceID = DYNAMIC */	/* .bCSourceID = DYNAMIC */	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),}; /* Ouput Terminal for I/O-Out */static struct uac2_output_terminal_descriptor io_out_ot_desc = {	.bLength = sizeof io_out_ot_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,	/* .bTerminalID = DYNAMIC */	/* .wTerminalType = DYNAMIC */	.bAssocTerminal = 0,	/* .bSourceID = DYNAMIC */	/* .bCSourceID = DYNAMIC */	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),}; static struct uac2_feature_unit_descriptor *in_feature_unit_desc;static struct uac2_feature_unit_descriptor *out_feature_unit_desc; static struct uac2_ac_header_descriptor ac_hdr_desc = {	.bLength = sizeof ac_hdr_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC_MS_HEADER,	.bcdADC = cpu_to_le16(0x200),	.bCategory = UAC2_FUNCTION_IO_BOX,	/* .wTotalLength = DYNAMIC */	.bmControls = 0,}; /* AC IN Interrupt Endpoint */static struct usb_endpoint_descriptor fs_ep_int_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT, 	.bEndpointAddress = USB_DIR_IN,	.bmAttributes = USB_ENDPOINT_XFER_INT,	.wMaxPacketSize = cpu_to_le16(6),	.bInterval = 1,}; static struct usb_endpoint_descriptor hs_ep_int_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT, 	.bmAttributes = USB_ENDPOINT_XFER_INT,	.wMaxPacketSize = cpu_to_le16(6),	.bInterval = 4,}; static struct usb_endpoint_descriptor ss_ep_int_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT, 	.bEndpointAddress = USB_DIR_IN,	.bmAttributes = USB_ENDPOINT_XFER_INT,	.wMaxPacketSize = cpu_to_le16(6),	.bInterval = 4,}; static struct usb_ss_ep_comp_descriptor ss_ep_int_desc_comp = {	.bLength = sizeof(ss_ep_int_desc_comp),	.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,	.wBytesPerInterval = cpu_to_le16(6),}; /* 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_2,}; /* 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_2,}; /* Audio Stream OUT Intface Desc */static struct uac2_as_header_descriptor as_out_hdr_desc = {	.bLength = sizeof as_out_hdr_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC_AS_GENERAL,	/* .bTerminalLink = DYNAMIC */	.bmControls = 0,	.bFormatType = UAC_FORMAT_TYPE_I,	.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),	.iChannelNames = 0,}; /* Audio USB_OUT Format */static struct uac2_format_type_i_descriptor as_out_fmt1_desc = {	.bLength = sizeof as_out_fmt1_desc,	.bDescriptorType = USB_DT_CS_INTERFACE,	.bDescriptorSubtype = UAC_FORMAT_TYPE,	.bFormatType = UAC_FORMAT_TYPE_I,}; /* 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 = DYNAMIC */	/* .wMaxPacketSize = DYNAMIC */	.bInterval = 1,}; static struct usb_endpoint_descriptor hs_epout_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT, 	/* .bmAttributes = DYNAMIC */	/* .wMaxPacketSize = DYNAMIC */	/* .bInterval = DYNAMIC */}; static struct usb_endpoint_descriptor ss_epout_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT, 	.bEndpointAddress = USB_DIR_OUT,	/* .bmAttributes = DYNAMIC */	/* .wMaxPacketSize = DYNAMIC */	/* .bInterval = DYNAMIC */}; static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = {	.bLength		= sizeof(ss_epout_desc_comp),	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,	.bMaxBurst		= 0,	.bmAttributes		= 0,	/* wBytesPerInterval = DYNAMIC */}; /* CS AS ISO OUT Endpoint */static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {	.bLength = sizeof as_iso_out_desc,	.bDescriptorType = USB_DT_CS_ENDPOINT, 	.bDescriptorSubtype = UAC_EP_GENERAL,	.bmAttributes = 0,	.bmControls = 0,	.bLockDelayUnits = 0,	.wLockDelay = 0,}; /* STD AS ISO IN Feedback Endpoint */static struct usb_endpoint_descriptor fs_epin_fback_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT, 	.bEndpointAddress = USB_DIR_IN,	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,	.wMaxPacketSize = cpu_to_le16(3),	.bInterval = 1,}; static struct usb_endpoint_descriptor hs_epin_fback_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT, 	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,	.wMaxPacketSize = cpu_to_le16(4),	.bInterval = 4,}; static struct usb_endpoint_descriptor ss_epin_fback_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT, 	.bEndpointAddress = USB_DIR_IN,	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK,	.wMaxPacketSize = cpu_to_le16(4),	.bInterval = 4,}; static struct usb_ss_ep_comp_descriptor ss_epin_fback_desc_comp = {	.bLength		= sizeof(ss_epin_fback_desc_comp),	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,	.bMaxBurst		= 0,	.bmAttributes		= 0,	.wBytesPerInterval	= cpu_to_le16(4),};  /* 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_2,}; /* 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_2,}; /* Audio Stream IN Intface Desc */static struct uac2_as_header_descriptor as_in_hdr_desc = {	.bLength = sizeof as_in_hdr_desc,	.bDescriptorType = USB_DT_CS_INTERFACE, 	.bDescriptorSubtype = UAC_AS_GENERAL,	/* .bTerminalLink = DYNAMIC */	.bmControls = 0,	.bFormatType = UAC_FORMAT_TYPE_I,	.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),	.iChannelNames = 0,}; /* Audio USB_IN Format */static struct uac2_format_type_i_descriptor as_in_fmt1_desc = {	.bLength = sizeof as_in_fmt1_desc,	.bDescriptorType = USB_DT_CS_INTERFACE,	.bDescriptorSubtype = UAC_FORMAT_TYPE,	.bFormatType = UAC_FORMAT_TYPE_I,}; /* 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 = DYNAMIC */	.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 = DYNAMIC */	/* .bInterval = DYNAMIC */}; static struct usb_endpoint_descriptor ss_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 = DYNAMIC */	/* .bInterval = DYNAMIC */}; static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = {	.bLength		= sizeof(ss_epin_desc_comp),	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,	.bMaxBurst		= 0,	.bmAttributes		= 0,

Lines 1–500 of 2313.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t