xref: /linux/drivers/usb/gadget/function/f_uac1.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
20591bc23SRuslan Bilovol /*
30591bc23SRuslan Bilovol  * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
40591bc23SRuslan Bilovol  *
50591bc23SRuslan Bilovol  * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
6695d39ffSJulian Scheel  * Copyright (C) 2021 Julian Scheel <julian@jusst.de>
70591bc23SRuslan Bilovol  *
80591bc23SRuslan Bilovol  * This driver doesn't expect any real Audio codec to be present
90591bc23SRuslan Bilovol  * on the device - the audio streams are simply sinked to and
100591bc23SRuslan Bilovol  * sourced from a virtual ALSA sound card created.
110591bc23SRuslan Bilovol  *
120591bc23SRuslan Bilovol  * This file is based on f_uac1.c which is
130591bc23SRuslan Bilovol  *   Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
140591bc23SRuslan Bilovol  *   Copyright (C) 2008 Analog Devices, Inc
150591bc23SRuslan Bilovol  */
160591bc23SRuslan Bilovol 
170591bc23SRuslan Bilovol #include <linux/usb/audio.h>
180591bc23SRuslan Bilovol #include <linux/module.h>
190591bc23SRuslan Bilovol 
200591bc23SRuslan Bilovol #include "u_audio.h"
210591bc23SRuslan Bilovol #include "u_uac1.h"
220591bc23SRuslan Bilovol 
23a59c68a6SRuslan Bilovol /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
24a59c68a6SRuslan Bilovol #define UAC1_CHANNEL_MASK 0x0FFF
25a59c68a6SRuslan Bilovol 
260356e628SRuslan Bilovol #define USB_OUT_FU_ID	(out_feature_unit_desc->bUnitID)
270356e628SRuslan Bilovol #define USB_IN_FU_ID	(in_feature_unit_desc->bUnitID)
280356e628SRuslan Bilovol 
29254cb1e0SRuslan Bilovol #define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
30254cb1e0SRuslan Bilovol #define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
310356e628SRuslan Bilovol #define FUIN_EN(_opts) ((_opts)->p_mute_present \
320356e628SRuslan Bilovol 			|| (_opts)->p_volume_present)
330356e628SRuslan Bilovol #define FUOUT_EN(_opts) ((_opts)->c_mute_present \
340356e628SRuslan Bilovol 			|| (_opts)->c_volume_present)
35254cb1e0SRuslan Bilovol 
360591bc23SRuslan Bilovol struct f_uac1 {
370591bc23SRuslan Bilovol 	struct g_audio g_audio;
380591bc23SRuslan Bilovol 	u8 ac_intf, as_in_intf, as_out_intf;
390591bc23SRuslan Bilovol 	u8 ac_alt, as_in_alt, as_out_alt;	/* needed for get_alt() */
400356e628SRuslan Bilovol 
410356e628SRuslan Bilovol 	struct usb_ctrlrequest setup_cr;	/* will be used in data stage */
420356e628SRuslan Bilovol 
430356e628SRuslan Bilovol 	/* Interrupt IN endpoint of AC interface */
440356e628SRuslan Bilovol 	struct usb_ep	*int_ep;
450356e628SRuslan Bilovol 	atomic_t	int_count;
46695d39ffSJulian Scheel 	int ctl_id;		/* EP id */
47695d39ffSJulian Scheel 	int c_srate;	/* current capture srate */
48695d39ffSJulian Scheel 	int p_srate;	/* current playback prate */
490591bc23SRuslan Bilovol };
500591bc23SRuslan Bilovol 
func_to_uac1(struct usb_function * f)510591bc23SRuslan Bilovol static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
520591bc23SRuslan Bilovol {
530591bc23SRuslan Bilovol 	return container_of(f, struct f_uac1, g_audio.func);
540591bc23SRuslan Bilovol }
550591bc23SRuslan Bilovol 
g_audio_to_uac1_opts(struct g_audio * audio)56a59c68a6SRuslan Bilovol static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio)
57a59c68a6SRuslan Bilovol {
58a59c68a6SRuslan Bilovol 	return container_of(audio->func.fi, struct f_uac1_opts, func_inst);
59a59c68a6SRuslan Bilovol }
60a59c68a6SRuslan Bilovol 
610591bc23SRuslan Bilovol /*
620591bc23SRuslan Bilovol  * DESCRIPTORS ... most are static, but strings and full
630591bc23SRuslan Bilovol  * configuration descriptors are built on demand.
640591bc23SRuslan Bilovol  */
650591bc23SRuslan Bilovol 
660591bc23SRuslan Bilovol /*
670591bc23SRuslan Bilovol  * We have three interfaces - one AudioControl and two AudioStreaming
680591bc23SRuslan Bilovol  *
690591bc23SRuslan Bilovol  * The driver implements a simple UAC_1 topology.
700591bc23SRuslan Bilovol  * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture
710591bc23SRuslan Bilovol  * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN
720591bc23SRuslan Bilovol  */
730591bc23SRuslan Bilovol 
740591bc23SRuslan Bilovol /* B.3.1  Standard AC Interface Descriptor */
750591bc23SRuslan Bilovol static struct usb_interface_descriptor ac_interface_desc = {
760591bc23SRuslan Bilovol 	.bLength =		USB_DT_INTERFACE_SIZE,
770591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_INTERFACE,
780356e628SRuslan Bilovol 	/* .bNumEndpoints =	DYNAMIC */
790591bc23SRuslan Bilovol 	.bInterfaceClass =	USB_CLASS_AUDIO,
800591bc23SRuslan Bilovol 	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
810591bc23SRuslan Bilovol };
820591bc23SRuslan Bilovol 
830591bc23SRuslan Bilovol /* B.3.2  Class-Specific AC Interface Descriptor */
84254cb1e0SRuslan Bilovol static struct uac1_ac_header_descriptor *ac_header_desc;
850591bc23SRuslan Bilovol 
860591bc23SRuslan Bilovol static struct uac_input_terminal_descriptor usb_out_it_desc = {
870591bc23SRuslan Bilovol 	.bLength =		UAC_DT_INPUT_TERMINAL_SIZE,
880591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_CS_INTERFACE,
890591bc23SRuslan Bilovol 	.bDescriptorSubtype =	UAC_INPUT_TERMINAL,
90254cb1e0SRuslan Bilovol 	/* .bTerminalID =	DYNAMIC */
9142370b82SRuslan Bilovol 	.wTerminalType =	cpu_to_le16(UAC_TERMINAL_STREAMING),
920591bc23SRuslan Bilovol 	.bAssocTerminal =	0,
9342370b82SRuslan Bilovol 	.wChannelConfig =	cpu_to_le16(0x3),
940591bc23SRuslan Bilovol };
950591bc23SRuslan Bilovol 
960591bc23SRuslan Bilovol static struct uac1_output_terminal_descriptor io_out_ot_desc = {
970591bc23SRuslan Bilovol 	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,
980591bc23SRuslan Bilovol 	.bDescriptorType	= USB_DT_CS_INTERFACE,
990591bc23SRuslan Bilovol 	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL,
100254cb1e0SRuslan Bilovol 	/* .bTerminalID =	DYNAMIC */
10142370b82SRuslan Bilovol 	.wTerminalType		= cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER),
1020591bc23SRuslan Bilovol 	.bAssocTerminal		= 0,
103254cb1e0SRuslan Bilovol 	/* .bSourceID =		DYNAMIC */
1040591bc23SRuslan Bilovol };
1050591bc23SRuslan Bilovol 
1060591bc23SRuslan Bilovol static struct uac_input_terminal_descriptor io_in_it_desc = {
1070591bc23SRuslan Bilovol 	.bLength		= UAC_DT_INPUT_TERMINAL_SIZE,
1080591bc23SRuslan Bilovol 	.bDescriptorType	= USB_DT_CS_INTERFACE,
1090591bc23SRuslan Bilovol 	.bDescriptorSubtype	= UAC_INPUT_TERMINAL,
110254cb1e0SRuslan Bilovol 	/* .bTerminalID		= DYNAMIC */
11142370b82SRuslan Bilovol 	.wTerminalType		= cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE),
1120591bc23SRuslan Bilovol 	.bAssocTerminal		= 0,
11342370b82SRuslan Bilovol 	.wChannelConfig		= cpu_to_le16(0x3),
1140591bc23SRuslan Bilovol };
1150591bc23SRuslan Bilovol 
1160591bc23SRuslan Bilovol static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
1170591bc23SRuslan Bilovol 	.bLength =		UAC_DT_OUTPUT_TERMINAL_SIZE,
1180591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_CS_INTERFACE,
1190591bc23SRuslan Bilovol 	.bDescriptorSubtype =	UAC_OUTPUT_TERMINAL,
120254cb1e0SRuslan Bilovol 	/* .bTerminalID =	DYNAMIC */
12142370b82SRuslan Bilovol 	.wTerminalType =	cpu_to_le16(UAC_TERMINAL_STREAMING),
1220591bc23SRuslan Bilovol 	.bAssocTerminal =	0,
123254cb1e0SRuslan Bilovol 	/* .bSourceID =		DYNAMIC */
1240591bc23SRuslan Bilovol };
1250591bc23SRuslan Bilovol 
1260356e628SRuslan Bilovol static struct uac_feature_unit_descriptor *in_feature_unit_desc;
1270356e628SRuslan Bilovol static struct uac_feature_unit_descriptor *out_feature_unit_desc;
1280356e628SRuslan Bilovol 
1290356e628SRuslan Bilovol /* AC IN Interrupt Endpoint */
1300356e628SRuslan Bilovol static struct usb_endpoint_descriptor ac_int_ep_desc = {
1310356e628SRuslan Bilovol 	.bLength = USB_DT_ENDPOINT_SIZE,
1320356e628SRuslan Bilovol 	.bDescriptorType = USB_DT_ENDPOINT,
1330356e628SRuslan Bilovol 	.bEndpointAddress = USB_DIR_IN,
1340356e628SRuslan Bilovol 	.bmAttributes = USB_ENDPOINT_XFER_INT,
1350356e628SRuslan Bilovol 	.wMaxPacketSize = cpu_to_le16(2),
1360356e628SRuslan Bilovol 	.bInterval = 4,
1370356e628SRuslan Bilovol };
1380356e628SRuslan Bilovol 
1390591bc23SRuslan Bilovol /* B.4.1  Standard AS Interface Descriptor */
1400591bc23SRuslan Bilovol static struct usb_interface_descriptor as_out_interface_alt_0_desc = {
1410591bc23SRuslan Bilovol 	.bLength =		USB_DT_INTERFACE_SIZE,
1420591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_INTERFACE,
1430591bc23SRuslan Bilovol 	.bAlternateSetting =	0,
1440591bc23SRuslan Bilovol 	.bNumEndpoints =	0,
1450591bc23SRuslan Bilovol 	.bInterfaceClass =	USB_CLASS_AUDIO,
1460591bc23SRuslan Bilovol 	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
1470591bc23SRuslan Bilovol };
1480591bc23SRuslan Bilovol 
1490591bc23SRuslan Bilovol static struct usb_interface_descriptor as_out_interface_alt_1_desc = {
1500591bc23SRuslan Bilovol 	.bLength =		USB_DT_INTERFACE_SIZE,
1510591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_INTERFACE,
1520591bc23SRuslan Bilovol 	.bAlternateSetting =	1,
1530591bc23SRuslan Bilovol 	.bNumEndpoints =	1,
1540591bc23SRuslan Bilovol 	.bInterfaceClass =	USB_CLASS_AUDIO,
1550591bc23SRuslan Bilovol 	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
1560591bc23SRuslan Bilovol };
1570591bc23SRuslan Bilovol 
1580591bc23SRuslan Bilovol static struct usb_interface_descriptor as_in_interface_alt_0_desc = {
1590591bc23SRuslan Bilovol 	.bLength =		USB_DT_INTERFACE_SIZE,
1600591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_INTERFACE,
1610591bc23SRuslan Bilovol 	.bAlternateSetting =	0,
1620591bc23SRuslan Bilovol 	.bNumEndpoints =	0,
1630591bc23SRuslan Bilovol 	.bInterfaceClass =	USB_CLASS_AUDIO,
1640591bc23SRuslan Bilovol 	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
1650591bc23SRuslan Bilovol };
1660591bc23SRuslan Bilovol 
1670591bc23SRuslan Bilovol static struct usb_interface_descriptor as_in_interface_alt_1_desc = {
1680591bc23SRuslan Bilovol 	.bLength =		USB_DT_INTERFACE_SIZE,
1690591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_INTERFACE,
1700591bc23SRuslan Bilovol 	.bAlternateSetting =	1,
1710591bc23SRuslan Bilovol 	.bNumEndpoints =	1,
1720591bc23SRuslan Bilovol 	.bInterfaceClass =	USB_CLASS_AUDIO,
1730591bc23SRuslan Bilovol 	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
1740591bc23SRuslan Bilovol };
1750591bc23SRuslan Bilovol 
1760591bc23SRuslan Bilovol /* B.4.2  Class-Specific AS Interface Descriptor */
1770591bc23SRuslan Bilovol static struct uac1_as_header_descriptor as_out_header_desc = {
1780591bc23SRuslan Bilovol 	.bLength =		UAC_DT_AS_HEADER_SIZE,
1790591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_CS_INTERFACE,
1800591bc23SRuslan Bilovol 	.bDescriptorSubtype =	UAC_AS_GENERAL,
181254cb1e0SRuslan Bilovol 	/* .bTerminalLink =	DYNAMIC */
1820591bc23SRuslan Bilovol 	.bDelay =		1,
18342370b82SRuslan Bilovol 	.wFormatTag =		cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
1840591bc23SRuslan Bilovol };
1850591bc23SRuslan Bilovol 
1860591bc23SRuslan Bilovol static struct uac1_as_header_descriptor as_in_header_desc = {
1870591bc23SRuslan Bilovol 	.bLength =		UAC_DT_AS_HEADER_SIZE,
1880591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_CS_INTERFACE,
1890591bc23SRuslan Bilovol 	.bDescriptorSubtype =	UAC_AS_GENERAL,
190254cb1e0SRuslan Bilovol 	/* .bTerminalLink =	DYNAMIC */
1910591bc23SRuslan Bilovol 	.bDelay =		1,
19242370b82SRuslan Bilovol 	.wFormatTag =		cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
1930591bc23SRuslan Bilovol };
1940591bc23SRuslan Bilovol 
195695d39ffSJulian Scheel DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
196695d39ffSJulian Scheel #define uac_format_type_i_discrete_descriptor			\
197695d39ffSJulian Scheel 	uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
1980591bc23SRuslan Bilovol 
199695d39ffSJulian Scheel static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
200695d39ffSJulian Scheel 	.bLength =		0, /* filled on rate setup */
2010591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_CS_INTERFACE,
2020591bc23SRuslan Bilovol 	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
2030591bc23SRuslan Bilovol 	.bFormatType =		UAC_FORMAT_TYPE_I,
2040591bc23SRuslan Bilovol 	.bSubframeSize =	2,
2050591bc23SRuslan Bilovol 	.bBitResolution =	16,
206695d39ffSJulian Scheel 	.bSamFreqType =		0, /* filled on rate setup */
2070591bc23SRuslan Bilovol };
2080591bc23SRuslan Bilovol 
2090591bc23SRuslan Bilovol /* Standard ISO OUT Endpoint Descriptor */
2100591bc23SRuslan Bilovol static struct usb_endpoint_descriptor as_out_ep_desc  = {
2110591bc23SRuslan Bilovol 	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
2120591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_ENDPOINT,
2130591bc23SRuslan Bilovol 	.bEndpointAddress =	USB_DIR_OUT,
2140591bc23SRuslan Bilovol 	.bmAttributes =		USB_ENDPOINT_SYNC_ADAPTIVE
2150591bc23SRuslan Bilovol 				| USB_ENDPOINT_XFER_ISOC,
2160591bc23SRuslan Bilovol 	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
2170591bc23SRuslan Bilovol 	.bInterval =		4,
2180591bc23SRuslan Bilovol };
2190591bc23SRuslan Bilovol 
2200591bc23SRuslan Bilovol /* Class-specific AS ISO OUT Endpoint Descriptor */
2210591bc23SRuslan Bilovol static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
2220591bc23SRuslan Bilovol 	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
2230591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_CS_ENDPOINT,
2240591bc23SRuslan Bilovol 	.bDescriptorSubtype =	UAC_EP_GENERAL,
2250591bc23SRuslan Bilovol 	.bmAttributes =		1,
2260591bc23SRuslan Bilovol 	.bLockDelayUnits =	1,
2270591bc23SRuslan Bilovol 	.wLockDelay =		cpu_to_le16(1),
2280591bc23SRuslan Bilovol };
2290591bc23SRuslan Bilovol 
230695d39ffSJulian Scheel static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
231695d39ffSJulian Scheel 	.bLength =		0, /* filled on rate setup */
2320591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_CS_INTERFACE,
2330591bc23SRuslan Bilovol 	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
2340591bc23SRuslan Bilovol 	.bFormatType =		UAC_FORMAT_TYPE_I,
2350591bc23SRuslan Bilovol 	.bSubframeSize =	2,
2360591bc23SRuslan Bilovol 	.bBitResolution =	16,
237695d39ffSJulian Scheel 	.bSamFreqType =		0, /* filled on rate setup */
2380591bc23SRuslan Bilovol };
2390591bc23SRuslan Bilovol 
2400591bc23SRuslan Bilovol /* Standard ISO OUT Endpoint Descriptor */
2410591bc23SRuslan Bilovol static struct usb_endpoint_descriptor as_in_ep_desc  = {
2420591bc23SRuslan Bilovol 	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
2430591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_ENDPOINT,
2440591bc23SRuslan Bilovol 	.bEndpointAddress =	USB_DIR_IN,
2450591bc23SRuslan Bilovol 	.bmAttributes =		USB_ENDPOINT_SYNC_ASYNC
2460591bc23SRuslan Bilovol 				| USB_ENDPOINT_XFER_ISOC,
2470591bc23SRuslan Bilovol 	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
2480591bc23SRuslan Bilovol 	.bInterval =		4,
2490591bc23SRuslan Bilovol };
2500591bc23SRuslan Bilovol 
2510591bc23SRuslan Bilovol /* Class-specific AS ISO OUT Endpoint Descriptor */
2520591bc23SRuslan Bilovol static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
2530591bc23SRuslan Bilovol 	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
2540591bc23SRuslan Bilovol 	.bDescriptorType =	USB_DT_CS_ENDPOINT,
2550591bc23SRuslan Bilovol 	.bDescriptorSubtype =	UAC_EP_GENERAL,
2560591bc23SRuslan Bilovol 	.bmAttributes =		1,
2570591bc23SRuslan Bilovol 	.bLockDelayUnits =	0,
2580591bc23SRuslan Bilovol 	.wLockDelay =		0,
2590591bc23SRuslan Bilovol };
2600591bc23SRuslan Bilovol 
2610591bc23SRuslan Bilovol static struct usb_descriptor_header *f_audio_desc[] = {
2620591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&ac_interface_desc,
2630591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&ac_header_desc,
2640591bc23SRuslan Bilovol 
2650591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&usb_out_it_desc,
2660591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&io_out_ot_desc,
2670356e628SRuslan Bilovol 	(struct usb_descriptor_header *)&out_feature_unit_desc,
2680356e628SRuslan Bilovol 
2690591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&io_in_it_desc,
2700591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&usb_in_ot_desc,
2710356e628SRuslan Bilovol 	(struct usb_descriptor_header *)&in_feature_unit_desc,
2720356e628SRuslan Bilovol 
2730356e628SRuslan Bilovol 	(struct usb_descriptor_header *)&ac_int_ep_desc,
2740591bc23SRuslan Bilovol 
2750591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
2760591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
2770591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_out_header_desc,
2780591bc23SRuslan Bilovol 
2790591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_out_type_i_desc,
2800591bc23SRuslan Bilovol 
2810591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_out_ep_desc,
2820591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_iso_out_desc,
2830591bc23SRuslan Bilovol 
2840591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
2850591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
2860591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_in_header_desc,
2870591bc23SRuslan Bilovol 
2880591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_in_type_i_desc,
2890591bc23SRuslan Bilovol 
2900591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_in_ep_desc,
2910591bc23SRuslan Bilovol 	(struct usb_descriptor_header *)&as_iso_in_desc,
2920591bc23SRuslan Bilovol 	NULL,
2930591bc23SRuslan Bilovol };
2940591bc23SRuslan Bilovol 
295b8fb6db6SPerr Zhang /* Standard ISO OUT Endpoint Descriptor */
296b8fb6db6SPerr Zhang static struct usb_endpoint_descriptor ss_as_out_ep_desc  = {
297b8fb6db6SPerr Zhang 	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
298b8fb6db6SPerr Zhang 	.bDescriptorType =	USB_DT_ENDPOINT,
299b8fb6db6SPerr Zhang 	.bEndpointAddress =	USB_DIR_OUT,
300b8fb6db6SPerr Zhang 	.bmAttributes =		USB_ENDPOINT_SYNC_ADAPTIVE
301b8fb6db6SPerr Zhang 				| USB_ENDPOINT_XFER_ISOC,
302b8fb6db6SPerr Zhang 	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
303b8fb6db6SPerr Zhang 	.bInterval =		4,
304b8fb6db6SPerr Zhang };
305b8fb6db6SPerr Zhang 
306b8fb6db6SPerr Zhang static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = {
307b8fb6db6SPerr Zhang 	.bLength		= sizeof(ss_as_out_ep_desc_comp),
308b8fb6db6SPerr Zhang 	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
309b8fb6db6SPerr Zhang 	.bMaxBurst		= 0,
310b8fb6db6SPerr Zhang 	.bmAttributes		= 0,
311b8fb6db6SPerr Zhang 	/* wBytesPerInterval = DYNAMIC */
312b8fb6db6SPerr Zhang };
313b8fb6db6SPerr Zhang 
314b8fb6db6SPerr Zhang /* Standard ISO OUT Endpoint Descriptor */
315b8fb6db6SPerr Zhang static struct usb_endpoint_descriptor ss_as_in_ep_desc  = {
316b8fb6db6SPerr Zhang 	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
317b8fb6db6SPerr Zhang 	.bDescriptorType =	USB_DT_ENDPOINT,
318b8fb6db6SPerr Zhang 	.bEndpointAddress =	USB_DIR_IN,
319b8fb6db6SPerr Zhang 	.bmAttributes =		USB_ENDPOINT_SYNC_ASYNC
320b8fb6db6SPerr Zhang 				| USB_ENDPOINT_XFER_ISOC,
321b8fb6db6SPerr Zhang 	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
322b8fb6db6SPerr Zhang 	.bInterval =		4,
323b8fb6db6SPerr Zhang };
324b8fb6db6SPerr Zhang 
325b8fb6db6SPerr Zhang static struct usb_ss_ep_comp_descriptor ss_as_in_ep_desc_comp = {
326b8fb6db6SPerr Zhang 	.bLength		= sizeof(ss_as_in_ep_desc_comp),
327b8fb6db6SPerr Zhang 	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
328b8fb6db6SPerr Zhang 	.bMaxBurst		= 0,
329b8fb6db6SPerr Zhang 	.bmAttributes		= 0,
330b8fb6db6SPerr Zhang 	/* wBytesPerInterval = DYNAMIC */
331b8fb6db6SPerr Zhang };
332b8fb6db6SPerr Zhang 
333b8fb6db6SPerr Zhang static struct usb_descriptor_header *f_audio_ss_desc[] = {
334b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&ac_interface_desc,
335b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&ac_header_desc,
336b8fb6db6SPerr Zhang 
337b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&usb_out_it_desc,
338b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&io_out_ot_desc,
339b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&io_in_it_desc,
340b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&usb_in_ot_desc,
341b8fb6db6SPerr Zhang 
342b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
343b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
344b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_out_header_desc,
345b8fb6db6SPerr Zhang 
346b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_out_type_i_desc,
347b8fb6db6SPerr Zhang 
348b8fb6db6SPerr Zhang 	//(struct usb_descriptor_header *)&as_out_ep_desc,
349b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&ss_as_out_ep_desc,
350b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&ss_as_out_ep_desc_comp,
351b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_iso_out_desc,
352b8fb6db6SPerr Zhang 
353b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
354b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
355b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_in_header_desc,
356b8fb6db6SPerr Zhang 
357b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_in_type_i_desc,
358b8fb6db6SPerr Zhang 
359b8fb6db6SPerr Zhang 	//(struct usb_descriptor_header *)&as_in_ep_desc,
360b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&ss_as_in_ep_desc,
361b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&ss_as_in_ep_desc_comp,
362b8fb6db6SPerr Zhang 	(struct usb_descriptor_header *)&as_iso_in_desc,
363b8fb6db6SPerr Zhang 	NULL,
364b8fb6db6SPerr Zhang };
365b8fb6db6SPerr Zhang 
3660591bc23SRuslan Bilovol enum {
3670591bc23SRuslan Bilovol 	STR_AC_IF,
3680591bc23SRuslan Bilovol 	STR_USB_OUT_IT,
3690591bc23SRuslan Bilovol 	STR_USB_OUT_IT_CH_NAMES,
3700591bc23SRuslan Bilovol 	STR_IO_OUT_OT,
3710591bc23SRuslan Bilovol 	STR_IO_IN_IT,
3720591bc23SRuslan Bilovol 	STR_IO_IN_IT_CH_NAMES,
3730591bc23SRuslan Bilovol 	STR_USB_IN_OT,
3740356e628SRuslan Bilovol 	STR_FU_IN,
3750356e628SRuslan Bilovol 	STR_FU_OUT,
3760591bc23SRuslan Bilovol 	STR_AS_OUT_IF_ALT0,
3770591bc23SRuslan Bilovol 	STR_AS_OUT_IF_ALT1,
3780591bc23SRuslan Bilovol 	STR_AS_IN_IF_ALT0,
3790591bc23SRuslan Bilovol 	STR_AS_IN_IF_ALT1,
3800591bc23SRuslan Bilovol };
3810591bc23SRuslan Bilovol 
3820591bc23SRuslan Bilovol static struct usb_string strings_uac1[] = {
383dfb05b5dSYunhao Tian 	/* [STR_AC_IF].s = DYNAMIC, */
3840591bc23SRuslan Bilovol 	[STR_USB_OUT_IT].s = "Playback Input terminal",
3850591bc23SRuslan Bilovol 	[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
3860591bc23SRuslan Bilovol 	[STR_IO_OUT_OT].s = "Playback Output terminal",
3870591bc23SRuslan Bilovol 	[STR_IO_IN_IT].s = "Capture Input terminal",
3880591bc23SRuslan Bilovol 	[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
3890591bc23SRuslan Bilovol 	[STR_USB_IN_OT].s = "Capture Output terminal",
3900356e628SRuslan Bilovol 	[STR_FU_IN].s = "Capture Volume",
3910356e628SRuslan Bilovol 	[STR_FU_OUT].s = "Playback Volume",
3920591bc23SRuslan Bilovol 	[STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
3930591bc23SRuslan Bilovol 	[STR_AS_OUT_IF_ALT1].s = "Playback Active",
3940591bc23SRuslan Bilovol 	[STR_AS_IN_IF_ALT0].s = "Capture Inactive",
3950591bc23SRuslan Bilovol 	[STR_AS_IN_IF_ALT1].s = "Capture Active",
3960591bc23SRuslan Bilovol 	{ },
3970591bc23SRuslan Bilovol };
3980591bc23SRuslan Bilovol 
3990591bc23SRuslan Bilovol static struct usb_gadget_strings str_uac1 = {
4000591bc23SRuslan Bilovol 	.language = 0x0409,	/* en-us */
4010591bc23SRuslan Bilovol 	.strings = strings_uac1,
4020591bc23SRuslan Bilovol };
4030591bc23SRuslan Bilovol 
4040591bc23SRuslan Bilovol static struct usb_gadget_strings *uac1_strings[] = {
4050591bc23SRuslan Bilovol 	&str_uac1,
4060591bc23SRuslan Bilovol 	NULL,
4070591bc23SRuslan Bilovol };
4080591bc23SRuslan Bilovol 
4090591bc23SRuslan Bilovol /*
4100591bc23SRuslan Bilovol  * This function is an ALSA sound card following USB Audio Class Spec 1.0.
4110591bc23SRuslan Bilovol  */
4120591bc23SRuslan Bilovol 
uac_cs_attr_sample_rate(struct usb_ep * ep,struct usb_request * req)413695d39ffSJulian Scheel static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
414695d39ffSJulian Scheel {
415695d39ffSJulian Scheel 	struct usb_function *fn = ep->driver_data;
416695d39ffSJulian Scheel 	struct usb_composite_dev *cdev = fn->config->cdev;
417695d39ffSJulian Scheel 	struct g_audio *agdev = func_to_g_audio(fn);
418695d39ffSJulian Scheel 	struct f_uac1 *uac1 = func_to_uac1(fn);
419695d39ffSJulian Scheel 	u8 *buf = (u8 *)req->buf;
420695d39ffSJulian Scheel 	u32 val = 0;
421695d39ffSJulian Scheel 
422695d39ffSJulian Scheel 	if (req->actual != 3) {
423695d39ffSJulian Scheel 		WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
424695d39ffSJulian Scheel 		return;
425695d39ffSJulian Scheel 	}
426695d39ffSJulian Scheel 
427695d39ffSJulian Scheel 	val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
428695d39ffSJulian Scheel 	if (uac1->ctl_id == (USB_DIR_IN | 2)) {
429695d39ffSJulian Scheel 		uac1->p_srate = val;
430695d39ffSJulian Scheel 		u_audio_set_playback_srate(agdev, uac1->p_srate);
431695d39ffSJulian Scheel 	} else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
432695d39ffSJulian Scheel 		uac1->c_srate = val;
433695d39ffSJulian Scheel 		u_audio_set_capture_srate(agdev, uac1->c_srate);
434695d39ffSJulian Scheel 	}
435695d39ffSJulian Scheel }
436695d39ffSJulian Scheel 
audio_notify_complete(struct usb_ep * _ep,struct usb_request * req)4370356e628SRuslan Bilovol static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
4380356e628SRuslan Bilovol {
4390356e628SRuslan Bilovol 	struct g_audio *audio = req->context;
4400356e628SRuslan Bilovol 	struct f_uac1 *uac1 = func_to_uac1(&audio->func);
4410356e628SRuslan Bilovol 
4420356e628SRuslan Bilovol 	atomic_dec(&uac1->int_count);
4430356e628SRuslan Bilovol 	kfree(req->buf);
4440356e628SRuslan Bilovol 	usb_ep_free_request(_ep, req);
4450356e628SRuslan Bilovol }
4460356e628SRuslan Bilovol 
audio_notify(struct g_audio * audio,int unit_id,int cs)4470356e628SRuslan Bilovol static int audio_notify(struct g_audio *audio, int unit_id, int cs)
4480356e628SRuslan Bilovol {
4490356e628SRuslan Bilovol 	struct f_uac1 *uac1 = func_to_uac1(&audio->func);
4500356e628SRuslan Bilovol 	struct usb_request *req;
4510356e628SRuslan Bilovol 	struct uac1_status_word *msg;
4520356e628SRuslan Bilovol 	int ret;
4530356e628SRuslan Bilovol 
4540356e628SRuslan Bilovol 	if (!uac1->int_ep->enabled)
4550356e628SRuslan Bilovol 		return 0;
4560356e628SRuslan Bilovol 
4570356e628SRuslan Bilovol 	if (atomic_inc_return(&uac1->int_count) > UAC1_DEF_INT_REQ_NUM) {
4580356e628SRuslan Bilovol 		atomic_dec(&uac1->int_count);
4590356e628SRuslan Bilovol 		return 0;
4600356e628SRuslan Bilovol 	}
4610356e628SRuslan Bilovol 
4620356e628SRuslan Bilovol 	req = usb_ep_alloc_request(uac1->int_ep, GFP_ATOMIC);
4630356e628SRuslan Bilovol 	if (req == NULL) {
4640356e628SRuslan Bilovol 		ret = -ENOMEM;
4650356e628SRuslan Bilovol 		goto err_dec_int_count;
4660356e628SRuslan Bilovol 	}
4670356e628SRuslan Bilovol 
4680356e628SRuslan Bilovol 	msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
4690356e628SRuslan Bilovol 	if (msg == NULL) {
4700356e628SRuslan Bilovol 		ret = -ENOMEM;
4710356e628SRuslan Bilovol 		goto err_free_request;
4720356e628SRuslan Bilovol 	}
4730356e628SRuslan Bilovol 
4740356e628SRuslan Bilovol 	msg->bStatusType = UAC1_STATUS_TYPE_IRQ_PENDING
4750356e628SRuslan Bilovol 				| UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF;
4760356e628SRuslan Bilovol 	msg->bOriginator = unit_id;
4770356e628SRuslan Bilovol 
4780356e628SRuslan Bilovol 	req->length = sizeof(*msg);
4790356e628SRuslan Bilovol 	req->buf = msg;
4800356e628SRuslan Bilovol 	req->context = audio;
4810356e628SRuslan Bilovol 	req->complete = audio_notify_complete;
4820356e628SRuslan Bilovol 
4830356e628SRuslan Bilovol 	ret = usb_ep_queue(uac1->int_ep, req, GFP_ATOMIC);
4840356e628SRuslan Bilovol 
4850356e628SRuslan Bilovol 	if (ret)
4860356e628SRuslan Bilovol 		goto err_free_msg;
4870356e628SRuslan Bilovol 
4880356e628SRuslan Bilovol 	return 0;
4890356e628SRuslan Bilovol 
4900356e628SRuslan Bilovol err_free_msg:
4910356e628SRuslan Bilovol 	kfree(msg);
4920356e628SRuslan Bilovol err_free_request:
4930356e628SRuslan Bilovol 	usb_ep_free_request(uac1->int_ep, req);
4940356e628SRuslan Bilovol err_dec_int_count:
4950356e628SRuslan Bilovol 	atomic_dec(&uac1->int_count);
4960356e628SRuslan Bilovol 
4970356e628SRuslan Bilovol 	return ret;
4980356e628SRuslan Bilovol }
4990356e628SRuslan Bilovol 
5000356e628SRuslan Bilovol static int
in_rq_cur(struct usb_function * fn,const struct usb_ctrlrequest * cr)5010356e628SRuslan Bilovol in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
5020356e628SRuslan Bilovol {
5030356e628SRuslan Bilovol 	struct usb_request *req = fn->config->cdev->req;
5040356e628SRuslan Bilovol 	struct g_audio *audio = func_to_g_audio(fn);
5050356e628SRuslan Bilovol 	struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
5060356e628SRuslan Bilovol 	u16 w_length = le16_to_cpu(cr->wLength);
5070356e628SRuslan Bilovol 	u16 w_index = le16_to_cpu(cr->wIndex);
5080356e628SRuslan Bilovol 	u16 w_value = le16_to_cpu(cr->wValue);
5090356e628SRuslan Bilovol 	u8 entity_id = (w_index >> 8) & 0xff;
5100356e628SRuslan Bilovol 	u8 control_selector = w_value >> 8;
5110356e628SRuslan Bilovol 	int value = -EOPNOTSUPP;
5120356e628SRuslan Bilovol 
5130356e628SRuslan Bilovol 	if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
5140356e628SRuslan Bilovol 			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
5150356e628SRuslan Bilovol 		unsigned int is_playback = 0;
5160356e628SRuslan Bilovol 
5170356e628SRuslan Bilovol 		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
5180356e628SRuslan Bilovol 			is_playback = 1;
5190356e628SRuslan Bilovol 
5200356e628SRuslan Bilovol 		if (control_selector == UAC_FU_MUTE) {
5210356e628SRuslan Bilovol 			unsigned int mute;
5220356e628SRuslan Bilovol 
5230356e628SRuslan Bilovol 			u_audio_get_mute(audio, is_playback, &mute);
5240356e628SRuslan Bilovol 
5250356e628SRuslan Bilovol 			*(u8 *)req->buf = mute;
5260356e628SRuslan Bilovol 			value = min_t(unsigned int, w_length, 1);
5270356e628SRuslan Bilovol 		} else if (control_selector == UAC_FU_VOLUME) {
5280356e628SRuslan Bilovol 			__le16 c;
5290356e628SRuslan Bilovol 			s16 volume;
5300356e628SRuslan Bilovol 
5310356e628SRuslan Bilovol 			u_audio_get_volume(audio, is_playback, &volume);
5320356e628SRuslan Bilovol 
5330356e628SRuslan Bilovol 			c = cpu_to_le16(volume);
5340356e628SRuslan Bilovol 
5350356e628SRuslan Bilovol 			value = min_t(unsigned int, w_length, sizeof(c));
5360356e628SRuslan Bilovol 			memcpy(req->buf, &c, value);
5370356e628SRuslan Bilovol 		} else {
5380356e628SRuslan Bilovol 			dev_err(&audio->gadget->dev,
5390356e628SRuslan Bilovol 				"%s:%d control_selector=%d TODO!\n",
5400356e628SRuslan Bilovol 				__func__, __LINE__, control_selector);
5410356e628SRuslan Bilovol 		}
5420356e628SRuslan Bilovol 	} else {
5430356e628SRuslan Bilovol 		dev_err(&audio->gadget->dev,
5440356e628SRuslan Bilovol 			"%s:%d entity_id=%d control_selector=%d TODO!\n",
5450356e628SRuslan Bilovol 			__func__, __LINE__, entity_id, control_selector);
5460356e628SRuslan Bilovol 	}
5470356e628SRuslan Bilovol 
5480356e628SRuslan Bilovol 	return value;
5490356e628SRuslan Bilovol }
5500356e628SRuslan Bilovol 
5510356e628SRuslan Bilovol static int
in_rq_min(struct usb_function * fn,const struct usb_ctrlrequest * cr)5520356e628SRuslan Bilovol in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr)
5530356e628SRuslan Bilovol {
5540356e628SRuslan Bilovol 	struct usb_request *req = fn->config->cdev->req;
5550356e628SRuslan Bilovol 	struct g_audio *audio = func_to_g_audio(fn);
5560356e628SRuslan Bilovol 	struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
5570356e628SRuslan Bilovol 	u16 w_length = le16_to_cpu(cr->wLength);
5580356e628SRuslan Bilovol 	u16 w_index = le16_to_cpu(cr->wIndex);
5590356e628SRuslan Bilovol 	u16 w_value = le16_to_cpu(cr->wValue);
5600356e628SRuslan Bilovol 	u8 entity_id = (w_index >> 8) & 0xff;
5610356e628SRuslan Bilovol 	u8 control_selector = w_value >> 8;
5620356e628SRuslan Bilovol 	int value = -EOPNOTSUPP;
5630356e628SRuslan Bilovol 
5640356e628SRuslan Bilovol 	if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
5650356e628SRuslan Bilovol 			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
5660356e628SRuslan Bilovol 		unsigned int is_playback = 0;
5670356e628SRuslan Bilovol 
5680356e628SRuslan Bilovol 		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
5690356e628SRuslan Bilovol 			is_playback = 1;
5700356e628SRuslan Bilovol 
5710356e628SRuslan Bilovol 		if (control_selector == UAC_FU_VOLUME) {
5720356e628SRuslan Bilovol 			__le16 r;
5730356e628SRuslan Bilovol 			s16 min_db;
5740356e628SRuslan Bilovol 
5750356e628SRuslan Bilovol 			if (is_playback)
5760356e628SRuslan Bilovol 				min_db = opts->p_volume_min;
5770356e628SRuslan Bilovol 			else
5780356e628SRuslan Bilovol 				min_db = opts->c_volume_min;
5790356e628SRuslan Bilovol 
5800356e628SRuslan Bilovol 			r = cpu_to_le16(min_db);
5810356e628SRuslan Bilovol 
5820356e628SRuslan Bilovol 			value = min_t(unsigned int, w_length, sizeof(r));
5830356e628SRuslan Bilovol 			memcpy(req->buf, &r, value);
5840356e628SRuslan Bilovol 		} else {
5850356e628SRuslan Bilovol 			dev_err(&audio->gadget->dev,
5860356e628SRuslan Bilovol 				"%s:%d control_selector=%d TODO!\n",
5870356e628SRuslan Bilovol 				__func__, __LINE__, control_selector);
5880356e628SRuslan Bilovol 		}
5890356e628SRuslan Bilovol 	} else {
5900356e628SRuslan Bilovol 		dev_err(&audio->gadget->dev,
5910356e628SRuslan Bilovol 			"%s:%d entity_id=%d control_selector=%d TODO!\n",
5920356e628SRuslan Bilovol 			__func__, __LINE__, entity_id, control_selector);
5930356e628SRuslan Bilovol 	}
5940356e628SRuslan Bilovol 
5950356e628SRuslan Bilovol 	return value;
5960356e628SRuslan Bilovol }
5970356e628SRuslan Bilovol 
5980356e628SRuslan Bilovol static int
in_rq_max(struct usb_function * fn,const struct usb_ctrlrequest * cr)5990356e628SRuslan Bilovol in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr)
6000356e628SRuslan Bilovol {
6010356e628SRuslan Bilovol 	struct usb_request *req = fn->config->cdev->req;
6020356e628SRuslan Bilovol 	struct g_audio *audio = func_to_g_audio(fn);
6030356e628SRuslan Bilovol 	struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
6040356e628SRuslan Bilovol 	u16 w_length = le16_to_cpu(cr->wLength);
6050356e628SRuslan Bilovol 	u16 w_index = le16_to_cpu(cr->wIndex);
6060356e628SRuslan Bilovol 	u16 w_value = le16_to_cpu(cr->wValue);
6070356e628SRuslan Bilovol 	u8 entity_id = (w_index >> 8) & 0xff;
6080356e628SRuslan Bilovol 	u8 control_selector = w_value >> 8;
6090356e628SRuslan Bilovol 	int value = -EOPNOTSUPP;
6100356e628SRuslan Bilovol 
6110356e628SRuslan Bilovol 	if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
6120356e628SRuslan Bilovol 			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
6130356e628SRuslan Bilovol 		unsigned int is_playback = 0;
6140356e628SRuslan Bilovol 
6150356e628SRuslan Bilovol 		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
6160356e628SRuslan Bilovol 			is_playback = 1;
6170356e628SRuslan Bilovol 
6180356e628SRuslan Bilovol 		if (control_selector == UAC_FU_VOLUME) {
6190356e628SRuslan Bilovol 			__le16 r;
6200356e628SRuslan Bilovol 			s16 max_db;
6210356e628SRuslan Bilovol 
6220356e628SRuslan Bilovol 			if (is_playback)
6230356e628SRuslan Bilovol 				max_db = opts->p_volume_max;
6240356e628SRuslan Bilovol 			else
6250356e628SRuslan Bilovol 				max_db = opts->c_volume_max;
6260356e628SRuslan Bilovol 
6270356e628SRuslan Bilovol 			r = cpu_to_le16(max_db);
6280356e628SRuslan Bilovol 
6290356e628SRuslan Bilovol 			value = min_t(unsigned int, w_length, sizeof(r));
6300356e628SRuslan Bilovol 			memcpy(req->buf, &r, value);
6310356e628SRuslan Bilovol 		} else {
6320356e628SRuslan Bilovol 			dev_err(&audio->gadget->dev,
6330356e628SRuslan Bilovol 				"%s:%d control_selector=%d TODO!\n",
6340356e628SRuslan Bilovol 				__func__, __LINE__, control_selector);
6350356e628SRuslan Bilovol 		}
6360356e628SRuslan Bilovol 	} else {
6370356e628SRuslan Bilovol 		dev_err(&audio->gadget->dev,
6380356e628SRuslan Bilovol 			"%s:%d entity_id=%d control_selector=%d TODO!\n",
6390356e628SRuslan Bilovol 			__func__, __LINE__, entity_id, control_selector);
6400356e628SRuslan Bilovol 	}
6410356e628SRuslan Bilovol 
6420356e628SRuslan Bilovol 	return value;
6430356e628SRuslan Bilovol }
6440356e628SRuslan Bilovol 
6450356e628SRuslan Bilovol static int
in_rq_res(struct usb_function * fn,const struct usb_ctrlrequest * cr)6460356e628SRuslan Bilovol in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr)
6470356e628SRuslan Bilovol {
6480356e628SRuslan Bilovol 	struct usb_request *req = fn->config->cdev->req;
6490356e628SRuslan Bilovol 	struct g_audio *audio = func_to_g_audio(fn);
6500356e628SRuslan Bilovol 	struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
6510356e628SRuslan Bilovol 	u16 w_length = le16_to_cpu(cr->wLength);
6520356e628SRuslan Bilovol 	u16 w_index = le16_to_cpu(cr->wIndex);
6530356e628SRuslan Bilovol 	u16 w_value = le16_to_cpu(cr->wValue);
6540356e628SRuslan Bilovol 	u8 entity_id = (w_index >> 8) & 0xff;
6550356e628SRuslan Bilovol 	u8 control_selector = w_value >> 8;
6560356e628SRuslan Bilovol 	int value = -EOPNOTSUPP;
6570356e628SRuslan Bilovol 
6580356e628SRuslan Bilovol 	if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
6590356e628SRuslan Bilovol 			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
6600356e628SRuslan Bilovol 		unsigned int is_playback = 0;
6610356e628SRuslan Bilovol 
6620356e628SRuslan Bilovol 		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
6630356e628SRuslan Bilovol 			is_playback = 1;
6640356e628SRuslan Bilovol 
6650356e628SRuslan Bilovol 		if (control_selector == UAC_FU_VOLUME) {
6660356e628SRuslan Bilovol 			__le16 r;
6670356e628SRuslan Bilovol 			s16 res_db;
6680356e628SRuslan Bilovol 
6690356e628SRuslan Bilovol 			if (is_playback)
6700356e628SRuslan Bilovol 				res_db = opts->p_volume_res;
6710356e628SRuslan Bilovol 			else
6720356e628SRuslan Bilovol 				res_db = opts->c_volume_res;
6730356e628SRuslan Bilovol 
6740356e628SRuslan Bilovol 			r = cpu_to_le16(res_db);
6750356e628SRuslan Bilovol 
6760356e628SRuslan Bilovol 			value = min_t(unsigned int, w_length, sizeof(r));
6770356e628SRuslan Bilovol 			memcpy(req->buf, &r, value);
6780356e628SRuslan Bilovol 		} else {
6790356e628SRuslan Bilovol 			dev_err(&audio->gadget->dev,
6800356e628SRuslan Bilovol 				"%s:%d control_selector=%d TODO!\n",
6810356e628SRuslan Bilovol 				__func__, __LINE__, control_selector);
6820356e628SRuslan Bilovol 		}
6830356e628SRuslan Bilovol 	} else {
6840356e628SRuslan Bilovol 		dev_err(&audio->gadget->dev,
6850356e628SRuslan Bilovol 			"%s:%d entity_id=%d control_selector=%d TODO!\n",
6860356e628SRuslan Bilovol 			__func__, __LINE__, entity_id, control_selector);
6870356e628SRuslan Bilovol 	}
6880356e628SRuslan Bilovol 
6890356e628SRuslan Bilovol 	return value;
6900356e628SRuslan Bilovol }
6910356e628SRuslan Bilovol 
6920356e628SRuslan Bilovol static void
out_rq_cur_complete(struct usb_ep * ep,struct usb_request * req)6930356e628SRuslan Bilovol out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
6940356e628SRuslan Bilovol {
6950356e628SRuslan Bilovol 	struct g_audio *audio = req->context;
6960356e628SRuslan Bilovol 	struct usb_composite_dev *cdev = audio->func.config->cdev;
6970356e628SRuslan Bilovol 	struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
6980356e628SRuslan Bilovol 	struct f_uac1 *uac1 = func_to_uac1(&audio->func);
6990356e628SRuslan Bilovol 	struct usb_ctrlrequest *cr = &uac1->setup_cr;
7000356e628SRuslan Bilovol 	u16 w_index = le16_to_cpu(cr->wIndex);
7010356e628SRuslan Bilovol 	u16 w_value = le16_to_cpu(cr->wValue);
7020356e628SRuslan Bilovol 	u8 entity_id = (w_index >> 8) & 0xff;
7030356e628SRuslan Bilovol 	u8 control_selector = w_value >> 8;
7040356e628SRuslan Bilovol 
7050356e628SRuslan Bilovol 	if (req->status != 0) {
7060356e628SRuslan Bilovol 		dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status);
7070356e628SRuslan Bilovol 		return;
7080356e628SRuslan Bilovol 	}
7090356e628SRuslan Bilovol 
7100356e628SRuslan Bilovol 	if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
7110356e628SRuslan Bilovol 			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
7120356e628SRuslan Bilovol 		unsigned int is_playback = 0;
7130356e628SRuslan Bilovol 
7140356e628SRuslan Bilovol 		if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
7150356e628SRuslan Bilovol 			is_playback = 1;
7160356e628SRuslan Bilovol 
7170356e628SRuslan Bilovol 		if (control_selector == UAC_FU_MUTE) {
7180356e628SRuslan Bilovol 			u8 mute = *(u8 *)req->buf;
7190356e628SRuslan Bilovol 
7200356e628SRuslan Bilovol 			u_audio_set_mute(audio, is_playback, mute);
7210356e628SRuslan Bilovol 
7220356e628SRuslan Bilovol 			return;
7230356e628SRuslan Bilovol 		} else if (control_selector == UAC_FU_VOLUME) {
7240356e628SRuslan Bilovol 			__le16 *c = req->buf;
7250356e628SRuslan Bilovol 			s16 volume;
7260356e628SRuslan Bilovol 
7270356e628SRuslan Bilovol 			volume = le16_to_cpu(*c);
7280356e628SRuslan Bilovol 			u_audio_set_volume(audio, is_playback, volume);
7290356e628SRuslan Bilovol 
7300356e628SRuslan Bilovol 			return;
7310356e628SRuslan Bilovol 		} else {
7320356e628SRuslan Bilovol 			dev_err(&audio->gadget->dev,
7330356e628SRuslan Bilovol 				"%s:%d control_selector=%d TODO!\n",
7340356e628SRuslan Bilovol 				__func__, __LINE__, control_selector);
7350356e628SRuslan Bilovol 			usb_ep_set_halt(ep);
7360356e628SRuslan Bilovol 		}
7370356e628SRuslan Bilovol 	} else {
7380356e628SRuslan Bilovol 		dev_err(&audio->gadget->dev,
7390356e628SRuslan Bilovol 			"%s:%d entity_id=%d control_selector=%d TODO!\n",
7400356e628SRuslan Bilovol 			__func__, __LINE__, entity_id, control_selector);
7410356e628SRuslan Bilovol 		usb_ep_set_halt(ep);
7420356e628SRuslan Bilovol 
7430356e628SRuslan Bilovol 	}
7440356e628SRuslan Bilovol }
7450356e628SRuslan Bilovol 
7460356e628SRuslan Bilovol static int
out_rq_cur(struct usb_function * fn,const struct usb_ctrlrequest * cr)7470356e628SRuslan Bilovol out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
7480356e628SRuslan Bilovol {
7490356e628SRuslan Bilovol 	struct usb_request *req = fn->config->cdev->req;
7500356e628SRuslan Bilovol 	struct g_audio *audio = func_to_g_audio(fn);
7510356e628SRuslan Bilovol 	struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
7520356e628SRuslan Bilovol 	struct f_uac1 *uac1 = func_to_uac1(&audio->func);
7530356e628SRuslan Bilovol 	u16 w_length = le16_to_cpu(cr->wLength);
7540356e628SRuslan Bilovol 	u16 w_index = le16_to_cpu(cr->wIndex);
7550356e628SRuslan Bilovol 	u16 w_value = le16_to_cpu(cr->wValue);
7560356e628SRuslan Bilovol 	u8 entity_id = (w_index >> 8) & 0xff;
7570356e628SRuslan Bilovol 	u8 control_selector = w_value >> 8;
7580356e628SRuslan Bilovol 
7590356e628SRuslan Bilovol 	if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
7600356e628SRuslan Bilovol 			(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
7610356e628SRuslan Bilovol 		memcpy(&uac1->setup_cr, cr, sizeof(*cr));
7620356e628SRuslan Bilovol 		req->context = audio;
7630356e628SRuslan Bilovol 		req->complete = out_rq_cur_complete;
7640356e628SRuslan Bilovol 
7650356e628SRuslan Bilovol 		return w_length;
7660356e628SRuslan Bilovol 	} else {
7670356e628SRuslan Bilovol 		dev_err(&audio->gadget->dev,
7680356e628SRuslan Bilovol 			"%s:%d entity_id=%d control_selector=%d TODO!\n",
7690356e628SRuslan Bilovol 			__func__, __LINE__, entity_id, control_selector);
7700356e628SRuslan Bilovol 	}
7710356e628SRuslan Bilovol 	return -EOPNOTSUPP;
7720356e628SRuslan Bilovol }
7730356e628SRuslan Bilovol 
ac_rq_in(struct usb_function * f,const struct usb_ctrlrequest * ctrl)7740356e628SRuslan Bilovol static int ac_rq_in(struct usb_function *f,
7750356e628SRuslan Bilovol 		const struct usb_ctrlrequest *ctrl)
7760356e628SRuslan Bilovol {
7770356e628SRuslan Bilovol 	struct usb_composite_dev *cdev = f->config->cdev;
7780356e628SRuslan Bilovol 	int value = -EOPNOTSUPP;
7790356e628SRuslan Bilovol 	u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
7800356e628SRuslan Bilovol 	u16 len = le16_to_cpu(ctrl->wLength);
7810356e628SRuslan Bilovol 	u16 w_value = le16_to_cpu(ctrl->wValue);
7820356e628SRuslan Bilovol 
7830356e628SRuslan Bilovol 	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
7840356e628SRuslan Bilovol 			ctrl->bRequest, w_value, len, ep);
7850356e628SRuslan Bilovol 
7860356e628SRuslan Bilovol 	switch (ctrl->bRequest) {
7870356e628SRuslan Bilovol 	case UAC_GET_CUR:
7880356e628SRuslan Bilovol 		return in_rq_cur(f, ctrl);
7890356e628SRuslan Bilovol 	case UAC_GET_MIN:
7900356e628SRuslan Bilovol 		return in_rq_min(f, ctrl);
7910356e628SRuslan Bilovol 	case UAC_GET_MAX:
7920356e628SRuslan Bilovol 		return in_rq_max(f, ctrl);
7930356e628SRuslan Bilovol 	case UAC_GET_RES:
7940356e628SRuslan Bilovol 		return in_rq_res(f, ctrl);
7950356e628SRuslan Bilovol 	case UAC_GET_MEM:
7960356e628SRuslan Bilovol 		break;
7970356e628SRuslan Bilovol 	case UAC_GET_STAT:
7980356e628SRuslan Bilovol 		value = len;
7990356e628SRuslan Bilovol 		break;
8000356e628SRuslan Bilovol 	default:
8010356e628SRuslan Bilovol 		break;
8020356e628SRuslan Bilovol 	}
8030356e628SRuslan Bilovol 
8040356e628SRuslan Bilovol 	return value;
8050356e628SRuslan Bilovol }
8060356e628SRuslan Bilovol 
audio_set_endpoint_req(struct usb_function * f,const struct usb_ctrlrequest * ctrl)8070591bc23SRuslan Bilovol static int audio_set_endpoint_req(struct usb_function *f,
8080591bc23SRuslan Bilovol 		const struct usb_ctrlrequest *ctrl)
8090591bc23SRuslan Bilovol {
8100591bc23SRuslan Bilovol 	struct usb_composite_dev *cdev = f->config->cdev;
811695d39ffSJulian Scheel 	struct usb_request	*req = f->config->cdev->req;
812695d39ffSJulian Scheel 	struct f_uac1		*uac1 = func_to_uac1(f);
8130591bc23SRuslan Bilovol 	int			value = -EOPNOTSUPP;
8140591bc23SRuslan Bilovol 	u16			ep = le16_to_cpu(ctrl->wIndex);
8150591bc23SRuslan Bilovol 	u16			len = le16_to_cpu(ctrl->wLength);
8160591bc23SRuslan Bilovol 	u16			w_value = le16_to_cpu(ctrl->wValue);
817695d39ffSJulian Scheel 	u8			cs = w_value >> 8;
8180591bc23SRuslan Bilovol 
8190591bc23SRuslan Bilovol 	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
8200591bc23SRuslan Bilovol 			ctrl->bRequest, w_value, len, ep);
8210591bc23SRuslan Bilovol 
8220591bc23SRuslan Bilovol 	switch (ctrl->bRequest) {
823695d39ffSJulian Scheel 	case UAC_SET_CUR: {
824695d39ffSJulian Scheel 		if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
825695d39ffSJulian Scheel 			cdev->gadget->ep0->driver_data = f;
826695d39ffSJulian Scheel 			uac1->ctl_id = ep;
827695d39ffSJulian Scheel 			req->complete = uac_cs_attr_sample_rate;
828695d39ffSJulian Scheel 		}
8290591bc23SRuslan Bilovol 		value = len;
8300591bc23SRuslan Bilovol 		break;
831695d39ffSJulian Scheel 	}
8320591bc23SRuslan Bilovol 
8330591bc23SRuslan Bilovol 	case UAC_SET_MIN:
8340591bc23SRuslan Bilovol 		break;
8350591bc23SRuslan Bilovol 
8360591bc23SRuslan Bilovol 	case UAC_SET_MAX:
8370591bc23SRuslan Bilovol 		break;
8380591bc23SRuslan Bilovol 
8390591bc23SRuslan Bilovol 	case UAC_SET_RES:
8400591bc23SRuslan Bilovol 		break;
8410591bc23SRuslan Bilovol 
8420591bc23SRuslan Bilovol 	case UAC_SET_MEM:
8430591bc23SRuslan Bilovol 		break;
8440591bc23SRuslan Bilovol 
8450591bc23SRuslan Bilovol 	default:
8460591bc23SRuslan Bilovol 		break;
8470591bc23SRuslan Bilovol 	}
8480591bc23SRuslan Bilovol 
8490591bc23SRuslan Bilovol 	return value;
8500591bc23SRuslan Bilovol }
8510591bc23SRuslan Bilovol 
audio_get_endpoint_req(struct usb_function * f,const struct usb_ctrlrequest * ctrl)8520591bc23SRuslan Bilovol static int audio_get_endpoint_req(struct usb_function *f,
8530591bc23SRuslan Bilovol 		const struct usb_ctrlrequest *ctrl)
8540591bc23SRuslan Bilovol {
8550591bc23SRuslan Bilovol 	struct usb_composite_dev *cdev = f->config->cdev;
856695d39ffSJulian Scheel 	struct usb_request *req = f->config->cdev->req;
857695d39ffSJulian Scheel 	struct f_uac1 *uac1 = func_to_uac1(f);
858695d39ffSJulian Scheel 	u8 *buf = (u8 *)req->buf;
8590591bc23SRuslan Bilovol 	int value = -EOPNOTSUPP;
860695d39ffSJulian Scheel 	u8 ep = le16_to_cpu(ctrl->wIndex);
8610591bc23SRuslan Bilovol 	u16 len = le16_to_cpu(ctrl->wLength);
8620591bc23SRuslan Bilovol 	u16 w_value = le16_to_cpu(ctrl->wValue);
863695d39ffSJulian Scheel 	u8 cs = w_value >> 8;
864695d39ffSJulian Scheel 	u32 val = 0;
8650591bc23SRuslan Bilovol 
8660591bc23SRuslan Bilovol 	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
8670591bc23SRuslan Bilovol 			ctrl->bRequest, w_value, len, ep);
8680591bc23SRuslan Bilovol 
8690591bc23SRuslan Bilovol 	switch (ctrl->bRequest) {
870695d39ffSJulian Scheel 	case UAC_GET_CUR: {
871695d39ffSJulian Scheel 		if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
872695d39ffSJulian Scheel 			if (ep == (USB_DIR_IN | 2))
873695d39ffSJulian Scheel 				val = uac1->p_srate;
874695d39ffSJulian Scheel 			else if (ep == (USB_DIR_OUT | 1))
875695d39ffSJulian Scheel 				val = uac1->c_srate;
876695d39ffSJulian Scheel 			buf[2] = (val >> 16) & 0xff;
877695d39ffSJulian Scheel 			buf[1] = (val >> 8) & 0xff;
878695d39ffSJulian Scheel 			buf[0] = val & 0xff;
879695d39ffSJulian Scheel 		}
880695d39ffSJulian Scheel 		value = len;
881695d39ffSJulian Scheel 		break;
882695d39ffSJulian Scheel 	}
8830591bc23SRuslan Bilovol 	case UAC_GET_MIN:
8840591bc23SRuslan Bilovol 	case UAC_GET_MAX:
8850591bc23SRuslan Bilovol 	case UAC_GET_RES:
8860591bc23SRuslan Bilovol 		value = len;
8870591bc23SRuslan Bilovol 		break;
8880591bc23SRuslan Bilovol 	case UAC_GET_MEM:
8890591bc23SRuslan Bilovol 		break;
8900591bc23SRuslan Bilovol 	default:
8910591bc23SRuslan Bilovol 		break;
8920591bc23SRuslan Bilovol 	}
8930591bc23SRuslan Bilovol 
8940591bc23SRuslan Bilovol 	return value;
8950591bc23SRuslan Bilovol }
8960591bc23SRuslan Bilovol 
8970591bc23SRuslan Bilovol static int
f_audio_setup(struct usb_function * f,const struct usb_ctrlrequest * ctrl)8980591bc23SRuslan Bilovol f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
8990591bc23SRuslan Bilovol {
9000591bc23SRuslan Bilovol 	struct usb_composite_dev *cdev = f->config->cdev;
9010591bc23SRuslan Bilovol 	struct usb_request	*req = cdev->req;
9020591bc23SRuslan Bilovol 	int			value = -EOPNOTSUPP;
9030591bc23SRuslan Bilovol 	u16			w_index = le16_to_cpu(ctrl->wIndex);
9040591bc23SRuslan Bilovol 	u16			w_value = le16_to_cpu(ctrl->wValue);
9050591bc23SRuslan Bilovol 	u16			w_length = le16_to_cpu(ctrl->wLength);
9060591bc23SRuslan Bilovol 
9070591bc23SRuslan Bilovol 	/* composite driver infrastructure handles everything; interface
9080591bc23SRuslan Bilovol 	 * activation uses set_alt().
9090591bc23SRuslan Bilovol 	 */
9100591bc23SRuslan Bilovol 	switch (ctrl->bRequestType) {
9110591bc23SRuslan Bilovol 	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
9120591bc23SRuslan Bilovol 		value = audio_set_endpoint_req(f, ctrl);
9130591bc23SRuslan Bilovol 		break;
9140591bc23SRuslan Bilovol 
9150591bc23SRuslan Bilovol 	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
9160591bc23SRuslan Bilovol 		value = audio_get_endpoint_req(f, ctrl);
9170591bc23SRuslan Bilovol 		break;
9180356e628SRuslan Bilovol 	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
9190356e628SRuslan Bilovol 		if (ctrl->bRequest == UAC_SET_CUR)
9200356e628SRuslan Bilovol 			value = out_rq_cur(f, ctrl);
9210356e628SRuslan Bilovol 		break;
9220356e628SRuslan Bilovol 	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
9230356e628SRuslan Bilovol 		value = ac_rq_in(f, ctrl);
9240356e628SRuslan Bilovol 		break;
9250591bc23SRuslan Bilovol 	default:
9260591bc23SRuslan Bilovol 		ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
9270591bc23SRuslan Bilovol 			ctrl->bRequestType, ctrl->bRequest,
9280591bc23SRuslan Bilovol 			w_value, w_index, w_length);
9290591bc23SRuslan Bilovol 	}
9300591bc23SRuslan Bilovol 
9310591bc23SRuslan Bilovol 	/* respond with data transfer or status phase? */
9320591bc23SRuslan Bilovol 	if (value >= 0) {
9330591bc23SRuslan Bilovol 		DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
9340591bc23SRuslan Bilovol 			ctrl->bRequestType, ctrl->bRequest,
9350591bc23SRuslan Bilovol 			w_value, w_index, w_length);
9360591bc23SRuslan Bilovol 		req->zero = 0;
9370591bc23SRuslan Bilovol 		req->length = value;
9380591bc23SRuslan Bilovol 		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
9390591bc23SRuslan Bilovol 		if (value < 0)
9400591bc23SRuslan Bilovol 			ERROR(cdev, "audio response on err %d\n", value);
9410591bc23SRuslan Bilovol 	}
9420591bc23SRuslan Bilovol 
9430591bc23SRuslan Bilovol 	/* device either stalls (value < 0) or reports success */
9440591bc23SRuslan Bilovol 	return value;
9450591bc23SRuslan Bilovol }
9460591bc23SRuslan Bilovol 
f_audio_set_alt(struct usb_function * f,unsigned intf,unsigned alt)9470591bc23SRuslan Bilovol static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
9480591bc23SRuslan Bilovol {
9490591bc23SRuslan Bilovol 	struct usb_composite_dev *cdev = f->config->cdev;
9500591bc23SRuslan Bilovol 	struct usb_gadget *gadget = cdev->gadget;
9510591bc23SRuslan Bilovol 	struct device *dev = &gadget->dev;
9520356e628SRuslan Bilovol 	struct g_audio *audio = func_to_g_audio(f);
9530591bc23SRuslan Bilovol 	struct f_uac1 *uac1 = func_to_uac1(f);
9540591bc23SRuslan Bilovol 	int ret = 0;
9550591bc23SRuslan Bilovol 
9560591bc23SRuslan Bilovol 	/* No i/f has more than 2 alt settings */
9570591bc23SRuslan Bilovol 	if (alt > 1) {
9580591bc23SRuslan Bilovol 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
9590591bc23SRuslan Bilovol 		return -EINVAL;
9600591bc23SRuslan Bilovol 	}
9610591bc23SRuslan Bilovol 
9620591bc23SRuslan Bilovol 	if (intf == uac1->ac_intf) {
9630591bc23SRuslan Bilovol 		/* Control I/f has only 1 AltSetting - 0 */
9640591bc23SRuslan Bilovol 		if (alt) {
9650591bc23SRuslan Bilovol 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
9660591bc23SRuslan Bilovol 			return -EINVAL;
9670591bc23SRuslan Bilovol 		}
9680356e628SRuslan Bilovol 
9690356e628SRuslan Bilovol 		/* restart interrupt endpoint */
9700356e628SRuslan Bilovol 		if (uac1->int_ep) {
9710356e628SRuslan Bilovol 			usb_ep_disable(uac1->int_ep);
9720356e628SRuslan Bilovol 			config_ep_by_speed(gadget, &audio->func, uac1->int_ep);
9730356e628SRuslan Bilovol 			usb_ep_enable(uac1->int_ep);
9740356e628SRuslan Bilovol 		}
9750356e628SRuslan Bilovol 
9760591bc23SRuslan Bilovol 		return 0;
9770591bc23SRuslan Bilovol 	}
9780591bc23SRuslan Bilovol 
9790591bc23SRuslan Bilovol 	if (intf == uac1->as_out_intf) {
9800591bc23SRuslan Bilovol 		uac1->as_out_alt = alt;
9810591bc23SRuslan Bilovol 
9820591bc23SRuslan Bilovol 		if (alt)
9830591bc23SRuslan Bilovol 			ret = u_audio_start_capture(&uac1->g_audio);
9840591bc23SRuslan Bilovol 		else
9850591bc23SRuslan Bilovol 			u_audio_stop_capture(&uac1->g_audio);
9860591bc23SRuslan Bilovol 	} else if (intf == uac1->as_in_intf) {
9870591bc23SRuslan Bilovol 		uac1->as_in_alt = alt;
9880591bc23SRuslan Bilovol 
9890591bc23SRuslan Bilovol 		if (alt)
9900591bc23SRuslan Bilovol 			ret = u_audio_start_playback(&uac1->g_audio);
9910591bc23SRuslan Bilovol 		else
9920591bc23SRuslan Bilovol 			u_audio_stop_playback(&uac1->g_audio);
9930591bc23SRuslan Bilovol 	} else {
9940591bc23SRuslan Bilovol 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
9950591bc23SRuslan Bilovol 		return -EINVAL;
9960591bc23SRuslan Bilovol 	}
9970591bc23SRuslan Bilovol 
9980591bc23SRuslan Bilovol 	return ret;
9990591bc23SRuslan Bilovol }
10000591bc23SRuslan Bilovol 
f_audio_get_alt(struct usb_function * f,unsigned intf)10010591bc23SRuslan Bilovol static int f_audio_get_alt(struct usb_function *f, unsigned intf)
10020591bc23SRuslan Bilovol {
10030591bc23SRuslan Bilovol 	struct usb_composite_dev *cdev = f->config->cdev;
10040591bc23SRuslan Bilovol 	struct usb_gadget *gadget = cdev->gadget;
10050591bc23SRuslan Bilovol 	struct device *dev = &gadget->dev;
10060591bc23SRuslan Bilovol 	struct f_uac1 *uac1 = func_to_uac1(f);
10070591bc23SRuslan Bilovol 
10080591bc23SRuslan Bilovol 	if (intf == uac1->ac_intf)
10090591bc23SRuslan Bilovol 		return uac1->ac_alt;
10100591bc23SRuslan Bilovol 	else if (intf == uac1->as_out_intf)
10110591bc23SRuslan Bilovol 		return uac1->as_out_alt;
10120591bc23SRuslan Bilovol 	else if (intf == uac1->as_in_intf)
10130591bc23SRuslan Bilovol 		return uac1->as_in_alt;
10140591bc23SRuslan Bilovol 	else
10150591bc23SRuslan Bilovol 		dev_err(dev, "%s:%d Invalid Interface %d!\n",
10160591bc23SRuslan Bilovol 			__func__, __LINE__, intf);
10170591bc23SRuslan Bilovol 
10180591bc23SRuslan Bilovol 	return -EINVAL;
10190591bc23SRuslan Bilovol }
10200591bc23SRuslan Bilovol 
10210591bc23SRuslan Bilovol 
f_audio_disable(struct usb_function * f)10220591bc23SRuslan Bilovol static void f_audio_disable(struct usb_function *f)
10230591bc23SRuslan Bilovol {
10240591bc23SRuslan Bilovol 	struct f_uac1 *uac1 = func_to_uac1(f);
10250591bc23SRuslan Bilovol 
10260591bc23SRuslan Bilovol 	uac1->as_out_alt = 0;
10270591bc23SRuslan Bilovol 	uac1->as_in_alt = 0;
10280591bc23SRuslan Bilovol 
1029cc2ac63dSRuslan Bilovol 	u_audio_stop_playback(&uac1->g_audio);
10300591bc23SRuslan Bilovol 	u_audio_stop_capture(&uac1->g_audio);
10310356e628SRuslan Bilovol 	if (uac1->int_ep)
10320356e628SRuslan Bilovol 		usb_ep_disable(uac1->int_ep);
10330591bc23SRuslan Bilovol }
10340591bc23SRuslan Bilovol 
1035d1d11dd1SPavel Hofman static void
f_audio_suspend(struct usb_function * f)1036d1d11dd1SPavel Hofman f_audio_suspend(struct usb_function *f)
1037d1d11dd1SPavel Hofman {
1038d1d11dd1SPavel Hofman 	struct f_uac1 *uac1 = func_to_uac1(f);
1039d1d11dd1SPavel Hofman 
1040d1d11dd1SPavel Hofman 	u_audio_suspend(&uac1->g_audio);
1041d1d11dd1SPavel Hofman }
1042d1d11dd1SPavel Hofman 
10430591bc23SRuslan Bilovol /*-------------------------------------------------------------------------*/
build_fu_desc(int chmask)10440356e628SRuslan Bilovol static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
10450356e628SRuslan Bilovol {
10460356e628SRuslan Bilovol 	struct uac_feature_unit_descriptor *fu_desc;
10470356e628SRuslan Bilovol 	int channels = num_channels(chmask);
10480356e628SRuslan Bilovol 	int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels);
10490591bc23SRuslan Bilovol 
10500356e628SRuslan Bilovol 	fu_desc = kzalloc(fu_desc_size, GFP_KERNEL);
10510356e628SRuslan Bilovol 	if (!fu_desc)
10520356e628SRuslan Bilovol 		return NULL;
10530356e628SRuslan Bilovol 
10540356e628SRuslan Bilovol 	fu_desc->bLength = fu_desc_size;
10550356e628SRuslan Bilovol 	fu_desc->bDescriptorType = USB_DT_CS_INTERFACE;
10560356e628SRuslan Bilovol 
10570356e628SRuslan Bilovol 	fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT;
10580356e628SRuslan Bilovol 	fu_desc->bControlSize  = 2;
10590356e628SRuslan Bilovol 
10600356e628SRuslan Bilovol 	/* bUnitID, bSourceID and bmaControls will be defined later */
10610356e628SRuslan Bilovol 
10620356e628SRuslan Bilovol 	return fu_desc;
10630356e628SRuslan Bilovol }
10640356e628SRuslan Bilovol 
10650356e628SRuslan Bilovol /* B.3.2  Class-Specific AC Interface Descriptor */
1066254cb1e0SRuslan Bilovol static struct
build_ac_header_desc(struct f_uac1_opts * opts)1067254cb1e0SRuslan Bilovol uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
1068254cb1e0SRuslan Bilovol {
1069254cb1e0SRuslan Bilovol 	struct uac1_ac_header_descriptor *ac_desc;
1070254cb1e0SRuslan Bilovol 	int ac_header_desc_size;
1071254cb1e0SRuslan Bilovol 	int num_ifaces = 0;
1072254cb1e0SRuslan Bilovol 
1073254cb1e0SRuslan Bilovol 	if (EPOUT_EN(opts))
1074254cb1e0SRuslan Bilovol 		num_ifaces++;
1075254cb1e0SRuslan Bilovol 	if (EPIN_EN(opts))
1076254cb1e0SRuslan Bilovol 		num_ifaces++;
1077254cb1e0SRuslan Bilovol 
1078254cb1e0SRuslan Bilovol 	ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(num_ifaces);
1079254cb1e0SRuslan Bilovol 
1080254cb1e0SRuslan Bilovol 	ac_desc = kzalloc(ac_header_desc_size, GFP_KERNEL);
1081254cb1e0SRuslan Bilovol 	if (!ac_desc)
1082254cb1e0SRuslan Bilovol 		return NULL;
1083254cb1e0SRuslan Bilovol 
1084254cb1e0SRuslan Bilovol 	ac_desc->bLength = ac_header_desc_size;
1085254cb1e0SRuslan Bilovol 	ac_desc->bDescriptorType = USB_DT_CS_INTERFACE;
1086254cb1e0SRuslan Bilovol 	ac_desc->bDescriptorSubtype = UAC_HEADER;
1087254cb1e0SRuslan Bilovol 	ac_desc->bcdADC = cpu_to_le16(0x0100);
1088254cb1e0SRuslan Bilovol 	ac_desc->bInCollection = num_ifaces;
1089254cb1e0SRuslan Bilovol 
1090254cb1e0SRuslan Bilovol 	/* wTotalLength and baInterfaceNr will be defined later */
1091254cb1e0SRuslan Bilovol 
1092254cb1e0SRuslan Bilovol 	return ac_desc;
1093254cb1e0SRuslan Bilovol }
1094254cb1e0SRuslan Bilovol 
1095254cb1e0SRuslan Bilovol /* Use macro to overcome line length limitation */
1096254cb1e0SRuslan Bilovol #define USBDHDR(p) (struct usb_descriptor_header *)(p)
1097254cb1e0SRuslan Bilovol 
setup_descriptor(struct f_uac1_opts * opts)1098254cb1e0SRuslan Bilovol static void setup_descriptor(struct f_uac1_opts *opts)
1099254cb1e0SRuslan Bilovol {
1100254cb1e0SRuslan Bilovol 	/* patch descriptors */
1101254cb1e0SRuslan Bilovol 	int i = 1; /* ID's start with 1 */
1102254cb1e0SRuslan Bilovol 
1103254cb1e0SRuslan Bilovol 	if (EPOUT_EN(opts))
1104254cb1e0SRuslan Bilovol 		usb_out_it_desc.bTerminalID = i++;
1105254cb1e0SRuslan Bilovol 	if (EPIN_EN(opts))
1106254cb1e0SRuslan Bilovol 		io_in_it_desc.bTerminalID = i++;
1107254cb1e0SRuslan Bilovol 	if (EPOUT_EN(opts))
1108254cb1e0SRuslan Bilovol 		io_out_ot_desc.bTerminalID = i++;
1109254cb1e0SRuslan Bilovol 	if (EPIN_EN(opts))
1110254cb1e0SRuslan Bilovol 		usb_in_ot_desc.bTerminalID = i++;
11110356e628SRuslan Bilovol 	if (FUOUT_EN(opts))
11120356e628SRuslan Bilovol 		out_feature_unit_desc->bUnitID = i++;
11130356e628SRuslan Bilovol 	if (FUIN_EN(opts))
11140356e628SRuslan Bilovol 		in_feature_unit_desc->bUnitID = i++;
1115254cb1e0SRuslan Bilovol 
11160356e628SRuslan Bilovol 	if (FUIN_EN(opts)) {
11170356e628SRuslan Bilovol 		usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID;
11180356e628SRuslan Bilovol 		in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID;
11190356e628SRuslan Bilovol 	} else {
1120254cb1e0SRuslan Bilovol 		usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
11210356e628SRuslan Bilovol 	}
11220356e628SRuslan Bilovol 	if (FUOUT_EN(opts)) {
11230356e628SRuslan Bilovol 		io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID;
11240356e628SRuslan Bilovol 		out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID;
11250356e628SRuslan Bilovol 	} else {
1126254cb1e0SRuslan Bilovol 		io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
11270356e628SRuslan Bilovol 	}
1128254cb1e0SRuslan Bilovol 
1129254cb1e0SRuslan Bilovol 	as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
1130254cb1e0SRuslan Bilovol 	as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
1131254cb1e0SRuslan Bilovol 
1132254cb1e0SRuslan Bilovol 	ac_header_desc->wTotalLength = cpu_to_le16(ac_header_desc->bLength);
1133254cb1e0SRuslan Bilovol 
1134254cb1e0SRuslan Bilovol 	if (EPIN_EN(opts)) {
1135254cb1e0SRuslan Bilovol 		u16 len = le16_to_cpu(ac_header_desc->wTotalLength);
1136254cb1e0SRuslan Bilovol 
1137254cb1e0SRuslan Bilovol 		len += sizeof(usb_in_ot_desc);
1138254cb1e0SRuslan Bilovol 		len += sizeof(io_in_it_desc);
11390356e628SRuslan Bilovol 		if (FUIN_EN(opts))
11400356e628SRuslan Bilovol 			len += in_feature_unit_desc->bLength;
1141254cb1e0SRuslan Bilovol 		ac_header_desc->wTotalLength = cpu_to_le16(len);
1142254cb1e0SRuslan Bilovol 	}
1143254cb1e0SRuslan Bilovol 	if (EPOUT_EN(opts)) {
1144254cb1e0SRuslan Bilovol 		u16 len = le16_to_cpu(ac_header_desc->wTotalLength);
1145254cb1e0SRuslan Bilovol 
1146254cb1e0SRuslan Bilovol 		len += sizeof(usb_out_it_desc);
1147254cb1e0SRuslan Bilovol 		len += sizeof(io_out_ot_desc);
11480356e628SRuslan Bilovol 		if (FUOUT_EN(opts))
11490356e628SRuslan Bilovol 			len += out_feature_unit_desc->bLength;
1150254cb1e0SRuslan Bilovol 		ac_header_desc->wTotalLength = cpu_to_le16(len);
1151254cb1e0SRuslan Bilovol 	}
1152254cb1e0SRuslan Bilovol 
1153254cb1e0SRuslan Bilovol 	i = 0;
1154254cb1e0SRuslan Bilovol 	f_audio_desc[i++] = USBDHDR(&ac_interface_desc);
1155254cb1e0SRuslan Bilovol 	f_audio_desc[i++] = USBDHDR(ac_header_desc);
1156254cb1e0SRuslan Bilovol 
1157254cb1e0SRuslan Bilovol 	if (EPOUT_EN(opts)) {
1158254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
1159254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
11600356e628SRuslan Bilovol 		if (FUOUT_EN(opts))
11610356e628SRuslan Bilovol 			f_audio_desc[i++] = USBDHDR(out_feature_unit_desc);
1162254cb1e0SRuslan Bilovol 	}
1163254cb1e0SRuslan Bilovol 
1164254cb1e0SRuslan Bilovol 	if (EPIN_EN(opts)) {
1165254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&io_in_it_desc);
1166254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
11670356e628SRuslan Bilovol 		if (FUIN_EN(opts))
11680356e628SRuslan Bilovol 			f_audio_desc[i++] = USBDHDR(in_feature_unit_desc);
1169254cb1e0SRuslan Bilovol 	}
1170254cb1e0SRuslan Bilovol 
11710356e628SRuslan Bilovol 	if (FUOUT_EN(opts) || FUIN_EN(opts))
11720356e628SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&ac_int_ep_desc);
11730356e628SRuslan Bilovol 
1174254cb1e0SRuslan Bilovol 	if (EPOUT_EN(opts)) {
1175254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_0_desc);
1176254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_1_desc);
1177254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_out_header_desc);
1178254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_out_type_i_desc);
1179254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_out_ep_desc);
1180254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_iso_out_desc);
1181254cb1e0SRuslan Bilovol 	}
1182254cb1e0SRuslan Bilovol 	if (EPIN_EN(opts)) {
1183254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_0_desc);
1184254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_1_desc);
1185254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_in_header_desc);
1186254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_in_type_i_desc);
1187254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_in_ep_desc);
1188254cb1e0SRuslan Bilovol 		f_audio_desc[i++] = USBDHDR(&as_iso_in_desc);
1189254cb1e0SRuslan Bilovol 	}
1190254cb1e0SRuslan Bilovol 	f_audio_desc[i] = NULL;
1191254cb1e0SRuslan Bilovol }
1192254cb1e0SRuslan Bilovol 
f_audio_validate_opts(struct g_audio * audio,struct device * dev)1193a59c68a6SRuslan Bilovol static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
1194a59c68a6SRuslan Bilovol {
1195a59c68a6SRuslan Bilovol 	struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
1196a59c68a6SRuslan Bilovol 
1197a59c68a6SRuslan Bilovol 	if (!opts->p_chmask && !opts->c_chmask) {
1198a59c68a6SRuslan Bilovol 		dev_err(dev, "Error: no playback and capture channels\n");
1199a59c68a6SRuslan Bilovol 		return -EINVAL;
1200a59c68a6SRuslan Bilovol 	} else if (opts->p_chmask & ~UAC1_CHANNEL_MASK) {
1201a59c68a6SRuslan Bilovol 		dev_err(dev, "Error: unsupported playback channels mask\n");
1202a59c68a6SRuslan Bilovol 		return -EINVAL;
1203a59c68a6SRuslan Bilovol 	} else if (opts->c_chmask & ~UAC1_CHANNEL_MASK) {
1204a59c68a6SRuslan Bilovol 		dev_err(dev, "Error: unsupported capture channels mask\n");
1205a59c68a6SRuslan Bilovol 		return -EINVAL;
1206a59c68a6SRuslan Bilovol 	} else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) {
1207a59c68a6SRuslan Bilovol 		dev_err(dev, "Error: incorrect playback sample size\n");
1208a59c68a6SRuslan Bilovol 		return -EINVAL;
1209a59c68a6SRuslan Bilovol 	} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
1210a59c68a6SRuslan Bilovol 		dev_err(dev, "Error: incorrect capture sample size\n");
1211a59c68a6SRuslan Bilovol 		return -EINVAL;
1212695d39ffSJulian Scheel 	} else if (!opts->p_srates[0]) {
1213a59c68a6SRuslan Bilovol 		dev_err(dev, "Error: incorrect playback sampling rate\n");
1214a59c68a6SRuslan Bilovol 		return -EINVAL;
1215695d39ffSJulian Scheel 	} else if (!opts->c_srates[0]) {
1216a59c68a6SRuslan Bilovol 		dev_err(dev, "Error: incorrect capture sampling rate\n");
1217a59c68a6SRuslan Bilovol 		return -EINVAL;
1218a59c68a6SRuslan Bilovol 	}
1219a59c68a6SRuslan Bilovol 
12200356e628SRuslan Bilovol 	if (opts->p_volume_max <= opts->p_volume_min) {
12210356e628SRuslan Bilovol 		dev_err(dev, "Error: incorrect playback volume max/min\n");
12220356e628SRuslan Bilovol 		return -EINVAL;
12230356e628SRuslan Bilovol 	} else if (opts->c_volume_max <= opts->c_volume_min) {
12240356e628SRuslan Bilovol 		dev_err(dev, "Error: incorrect capture volume max/min\n");
12250356e628SRuslan Bilovol 		return -EINVAL;
12260356e628SRuslan Bilovol 	} else if (opts->p_volume_res <= 0) {
12270356e628SRuslan Bilovol 		dev_err(dev, "Error: negative/zero playback volume resolution\n");
12280356e628SRuslan Bilovol 		return -EINVAL;
12290356e628SRuslan Bilovol 	} else if (opts->c_volume_res <= 0) {
12300356e628SRuslan Bilovol 		dev_err(dev, "Error: negative/zero capture volume resolution\n");
12310356e628SRuslan Bilovol 		return -EINVAL;
12320356e628SRuslan Bilovol 	}
12330356e628SRuslan Bilovol 
12340356e628SRuslan Bilovol 	if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) {
12350356e628SRuslan Bilovol 		dev_err(dev, "Error: incorrect playback volume resolution\n");
12360356e628SRuslan Bilovol 		return -EINVAL;
12370356e628SRuslan Bilovol 	} else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) {
12380356e628SRuslan Bilovol 		dev_err(dev, "Error: incorrect capture volume resolution\n");
12390356e628SRuslan Bilovol 		return -EINVAL;
12400356e628SRuslan Bilovol 	}
12410356e628SRuslan Bilovol 
1242a59c68a6SRuslan Bilovol 	return 0;
1243a59c68a6SRuslan Bilovol }
1244a59c68a6SRuslan Bilovol 
12450591bc23SRuslan Bilovol /* audio function driver setup/binding */
f_audio_bind(struct usb_configuration * c,struct usb_function * f)12460591bc23SRuslan Bilovol static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
12470591bc23SRuslan Bilovol {
12480591bc23SRuslan Bilovol 	struct usb_composite_dev	*cdev = c->cdev;
12490591bc23SRuslan Bilovol 	struct usb_gadget		*gadget = cdev->gadget;
1250a59c68a6SRuslan Bilovol 	struct device			*dev = &gadget->dev;
12510591bc23SRuslan Bilovol 	struct f_uac1			*uac1 = func_to_uac1(f);
12520591bc23SRuslan Bilovol 	struct g_audio			*audio = func_to_g_audio(f);
12530591bc23SRuslan Bilovol 	struct f_uac1_opts		*audio_opts;
12540591bc23SRuslan Bilovol 	struct usb_ep			*ep = NULL;
12550591bc23SRuslan Bilovol 	struct usb_string		*us;
1256254cb1e0SRuslan Bilovol 	int				ba_iface_id;
12570591bc23SRuslan Bilovol 	int				status;
1258695d39ffSJulian Scheel 	int				idx, i;
12590591bc23SRuslan Bilovol 
1260a59c68a6SRuslan Bilovol 	status = f_audio_validate_opts(audio, dev);
1261a59c68a6SRuslan Bilovol 	if (status)
1262a59c68a6SRuslan Bilovol 		return status;
1263a59c68a6SRuslan Bilovol 
12640591bc23SRuslan Bilovol 	audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
12650591bc23SRuslan Bilovol 
1266dfb05b5dSYunhao Tian 	strings_uac1[STR_AC_IF].s = audio_opts->function_name;
1267dfb05b5dSYunhao Tian 
12680591bc23SRuslan Bilovol 	us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
12690591bc23SRuslan Bilovol 	if (IS_ERR(us))
12700591bc23SRuslan Bilovol 		return PTR_ERR(us);
1271254cb1e0SRuslan Bilovol 
1272254cb1e0SRuslan Bilovol 	ac_header_desc = build_ac_header_desc(audio_opts);
1273254cb1e0SRuslan Bilovol 	if (!ac_header_desc)
1274254cb1e0SRuslan Bilovol 		return -ENOMEM;
1275254cb1e0SRuslan Bilovol 
12760356e628SRuslan Bilovol 	if (FUOUT_EN(audio_opts)) {
12770356e628SRuslan Bilovol 		out_feature_unit_desc = build_fu_desc(audio_opts->c_chmask);
12780356e628SRuslan Bilovol 		if (!out_feature_unit_desc) {
12790356e628SRuslan Bilovol 			status = -ENOMEM;
12800356e628SRuslan Bilovol 			goto fail;
12810356e628SRuslan Bilovol 		}
12820356e628SRuslan Bilovol 	}
12830356e628SRuslan Bilovol 	if (FUIN_EN(audio_opts)) {
12840356e628SRuslan Bilovol 		in_feature_unit_desc = build_fu_desc(audio_opts->p_chmask);
12850356e628SRuslan Bilovol 		if (!in_feature_unit_desc) {
12860356e628SRuslan Bilovol 			status = -ENOMEM;
12870356e628SRuslan Bilovol 			goto err_free_fu;
12880356e628SRuslan Bilovol 		}
12890356e628SRuslan Bilovol 	}
12900356e628SRuslan Bilovol 
12910591bc23SRuslan Bilovol 	ac_interface_desc.iInterface = us[STR_AC_IF].id;
12920591bc23SRuslan Bilovol 	usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id;
12930591bc23SRuslan Bilovol 	usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id;
12940591bc23SRuslan Bilovol 	io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id;
12950591bc23SRuslan Bilovol 	as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id;
12960591bc23SRuslan Bilovol 	as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id;
12970591bc23SRuslan Bilovol 	io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id;
12980591bc23SRuslan Bilovol 	io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id;
12990591bc23SRuslan Bilovol 	usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id;
13000591bc23SRuslan Bilovol 	as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id;
13010591bc23SRuslan Bilovol 	as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id;
13020591bc23SRuslan Bilovol 
13030356e628SRuslan Bilovol 	if (FUOUT_EN(audio_opts)) {
13040356e628SRuslan Bilovol 		u8 *i_feature;
13050356e628SRuslan Bilovol 
13060356e628SRuslan Bilovol 		i_feature = (u8 *)out_feature_unit_desc +
13070356e628SRuslan Bilovol 					out_feature_unit_desc->bLength - 1;
13080356e628SRuslan Bilovol 		*i_feature = us[STR_FU_OUT].id;
13090356e628SRuslan Bilovol 	}
13100356e628SRuslan Bilovol 	if (FUIN_EN(audio_opts)) {
13110356e628SRuslan Bilovol 		u8 *i_feature;
13120356e628SRuslan Bilovol 
13130356e628SRuslan Bilovol 		i_feature = (u8 *)in_feature_unit_desc +
13140356e628SRuslan Bilovol 					in_feature_unit_desc->bLength - 1;
13150356e628SRuslan Bilovol 		*i_feature = us[STR_FU_IN].id;
13160356e628SRuslan Bilovol 	}
13170356e628SRuslan Bilovol 
13180591bc23SRuslan Bilovol 	/* Set channel numbers */
13190591bc23SRuslan Bilovol 	usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask);
13200591bc23SRuslan Bilovol 	usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
13210591bc23SRuslan Bilovol 	as_out_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask);
13220591bc23SRuslan Bilovol 	as_out_type_i_desc.bSubframeSize = audio_opts->c_ssize;
13230591bc23SRuslan Bilovol 	as_out_type_i_desc.bBitResolution = audio_opts->c_ssize * 8;
13240591bc23SRuslan Bilovol 	io_in_it_desc.bNrChannels = num_channels(audio_opts->p_chmask);
13250591bc23SRuslan Bilovol 	io_in_it_desc.wChannelConfig = cpu_to_le16(audio_opts->p_chmask);
13260591bc23SRuslan Bilovol 	as_in_type_i_desc.bNrChannels = num_channels(audio_opts->p_chmask);
13270591bc23SRuslan Bilovol 	as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize;
13280591bc23SRuslan Bilovol 	as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8;
13290591bc23SRuslan Bilovol 
13300356e628SRuslan Bilovol 	if (FUOUT_EN(audio_opts)) {
13310356e628SRuslan Bilovol 		__le16 *bma = (__le16 *)&out_feature_unit_desc->bmaControls[0];
13320356e628SRuslan Bilovol 		u32 control = 0;
13330356e628SRuslan Bilovol 
13340356e628SRuslan Bilovol 		if (audio_opts->c_mute_present)
13350356e628SRuslan Bilovol 			control |= UAC_FU_MUTE;
13360356e628SRuslan Bilovol 		if (audio_opts->c_volume_present)
13370356e628SRuslan Bilovol 			control |= UAC_FU_VOLUME;
13380356e628SRuslan Bilovol 		*bma = cpu_to_le16(control);
13390356e628SRuslan Bilovol 	}
13400356e628SRuslan Bilovol 	if (FUIN_EN(audio_opts)) {
13410356e628SRuslan Bilovol 		__le16 *bma = (__le16 *)&in_feature_unit_desc->bmaControls[0];
13420356e628SRuslan Bilovol 		u32 control = 0;
13430356e628SRuslan Bilovol 
13440356e628SRuslan Bilovol 		if (audio_opts->p_mute_present)
13450356e628SRuslan Bilovol 			control |= UAC_FU_MUTE;
13460356e628SRuslan Bilovol 		if (audio_opts->p_volume_present)
13470356e628SRuslan Bilovol 			control |= UAC_FU_VOLUME;
13480356e628SRuslan Bilovol 		*bma = cpu_to_le16(control);
13490356e628SRuslan Bilovol 	}
13500356e628SRuslan Bilovol 
13510591bc23SRuslan Bilovol 	/* Set sample rates */
1352695d39ffSJulian Scheel 	for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
1353695d39ffSJulian Scheel 		if (audio_opts->c_srates[i] == 0)
1354695d39ffSJulian Scheel 			break;
1355695d39ffSJulian Scheel 		memcpy(as_out_type_i_desc.tSamFreq[idx++],
1356695d39ffSJulian Scheel 				&audio_opts->c_srates[i], 3);
1357695d39ffSJulian Scheel 	}
1358695d39ffSJulian Scheel 	as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
1359695d39ffSJulian Scheel 	as_out_type_i_desc.bSamFreqType = idx;
1360695d39ffSJulian Scheel 
1361695d39ffSJulian Scheel 	for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
1362695d39ffSJulian Scheel 		if (audio_opts->p_srates[i] == 0)
1363695d39ffSJulian Scheel 			break;
1364695d39ffSJulian Scheel 		memcpy(as_in_type_i_desc.tSamFreq[idx++],
1365695d39ffSJulian Scheel 				&audio_opts->p_srates[i], 3);
1366695d39ffSJulian Scheel 	}
1367695d39ffSJulian Scheel 	as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
1368695d39ffSJulian Scheel 	as_in_type_i_desc.bSamFreqType = idx;
1369695d39ffSJulian Scheel 	uac1->p_srate = audio_opts->p_srates[0];
1370695d39ffSJulian Scheel 	uac1->c_srate = audio_opts->c_srates[0];
13710591bc23SRuslan Bilovol 
13720591bc23SRuslan Bilovol 	/* allocate instance-specific interface IDs, and patch descriptors */
13730591bc23SRuslan Bilovol 	status = usb_interface_id(c, f);
13740591bc23SRuslan Bilovol 	if (status < 0)
13750356e628SRuslan Bilovol 		goto err_free_fu;
13760591bc23SRuslan Bilovol 	ac_interface_desc.bInterfaceNumber = status;
13770591bc23SRuslan Bilovol 	uac1->ac_intf = status;
13780591bc23SRuslan Bilovol 	uac1->ac_alt = 0;
13790591bc23SRuslan Bilovol 
1380254cb1e0SRuslan Bilovol 	ba_iface_id = 0;
1381254cb1e0SRuslan Bilovol 
1382254cb1e0SRuslan Bilovol 	if (EPOUT_EN(audio_opts)) {
13830591bc23SRuslan Bilovol 		status = usb_interface_id(c, f);
13840591bc23SRuslan Bilovol 		if (status < 0)
13850356e628SRuslan Bilovol 			goto err_free_fu;
13860591bc23SRuslan Bilovol 		as_out_interface_alt_0_desc.bInterfaceNumber = status;
13870591bc23SRuslan Bilovol 		as_out_interface_alt_1_desc.bInterfaceNumber = status;
1388254cb1e0SRuslan Bilovol 		ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
13890591bc23SRuslan Bilovol 		uac1->as_out_intf = status;
13900591bc23SRuslan Bilovol 		uac1->as_out_alt = 0;
1391254cb1e0SRuslan Bilovol 	}
13920591bc23SRuslan Bilovol 
1393254cb1e0SRuslan Bilovol 	if (EPIN_EN(audio_opts)) {
13940591bc23SRuslan Bilovol 		status = usb_interface_id(c, f);
13950591bc23SRuslan Bilovol 		if (status < 0)
13960356e628SRuslan Bilovol 			goto err_free_fu;
13970591bc23SRuslan Bilovol 		as_in_interface_alt_0_desc.bInterfaceNumber = status;
13980591bc23SRuslan Bilovol 		as_in_interface_alt_1_desc.bInterfaceNumber = status;
1399254cb1e0SRuslan Bilovol 		ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
14000591bc23SRuslan Bilovol 		uac1->as_in_intf = status;
14010591bc23SRuslan Bilovol 		uac1->as_in_alt = 0;
1402254cb1e0SRuslan Bilovol 	}
14030591bc23SRuslan Bilovol 
14040591bc23SRuslan Bilovol 	audio->gadget = gadget;
14050591bc23SRuslan Bilovol 
14060591bc23SRuslan Bilovol 	status = -ENODEV;
14070591bc23SRuslan Bilovol 
14080356e628SRuslan Bilovol 	ac_interface_desc.bNumEndpoints = 0;
14090356e628SRuslan Bilovol 
14100356e628SRuslan Bilovol 	/* allocate AC interrupt endpoint */
14110356e628SRuslan Bilovol 	if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) {
14120356e628SRuslan Bilovol 		ep = usb_ep_autoconfig(cdev->gadget, &ac_int_ep_desc);
14130356e628SRuslan Bilovol 		if (!ep)
14140356e628SRuslan Bilovol 			goto err_free_fu;
14150356e628SRuslan Bilovol 		uac1->int_ep = ep;
14160356e628SRuslan Bilovol 		uac1->int_ep->desc = &ac_int_ep_desc;
14170356e628SRuslan Bilovol 
14180356e628SRuslan Bilovol 		ac_interface_desc.bNumEndpoints = 1;
14190356e628SRuslan Bilovol 	}
14200356e628SRuslan Bilovol 
14210591bc23SRuslan Bilovol 	/* allocate instance-specific endpoints */
1422254cb1e0SRuslan Bilovol 	if (EPOUT_EN(audio_opts)) {
14230591bc23SRuslan Bilovol 		ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
14240591bc23SRuslan Bilovol 		if (!ep)
14250356e628SRuslan Bilovol 			goto err_free_fu;
1426b8fb6db6SPerr Zhang 		ss_as_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress;
14270591bc23SRuslan Bilovol 		audio->out_ep = ep;
14280591bc23SRuslan Bilovol 		audio->out_ep->desc = &as_out_ep_desc;
1429254cb1e0SRuslan Bilovol 	}
14300591bc23SRuslan Bilovol 
1431254cb1e0SRuslan Bilovol 	if (EPIN_EN(audio_opts)) {
14320591bc23SRuslan Bilovol 		ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
14330591bc23SRuslan Bilovol 		if (!ep)
14340356e628SRuslan Bilovol 			goto err_free_fu;
1435b8fb6db6SPerr Zhang 		ss_as_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress;
14360591bc23SRuslan Bilovol 		audio->in_ep = ep;
14370591bc23SRuslan Bilovol 		audio->in_ep->desc = &as_in_ep_desc;
1438254cb1e0SRuslan Bilovol 	}
1439254cb1e0SRuslan Bilovol 
1440254cb1e0SRuslan Bilovol 	setup_descriptor(audio_opts);
14410591bc23SRuslan Bilovol 
14420591bc23SRuslan Bilovol 	/* copy descriptors, and track endpoint copies */
1443b8fb6db6SPerr Zhang 	status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, f_audio_ss_desc,
1444b8fb6db6SPerr Zhang 					f_audio_ss_desc);
14450591bc23SRuslan Bilovol 	if (status)
14460356e628SRuslan Bilovol 		goto err_free_fu;
14470591bc23SRuslan Bilovol 
144842370b82SRuslan Bilovol 	audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
144942370b82SRuslan Bilovol 	audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
14500591bc23SRuslan Bilovol 	audio->params.c_chmask = audio_opts->c_chmask;
1451695d39ffSJulian Scheel 	memcpy(audio->params.c_srates, audio_opts->c_srates,
1452695d39ffSJulian Scheel 			sizeof(audio->params.c_srates));
14530591bc23SRuslan Bilovol 	audio->params.c_ssize = audio_opts->c_ssize;
14540356e628SRuslan Bilovol 	if (FUIN_EN(audio_opts)) {
14550356e628SRuslan Bilovol 		audio->params.p_fu.id = USB_IN_FU_ID;
14560356e628SRuslan Bilovol 		audio->params.p_fu.mute_present = audio_opts->p_mute_present;
14570356e628SRuslan Bilovol 		audio->params.p_fu.volume_present =
14580356e628SRuslan Bilovol 				audio_opts->p_volume_present;
14590356e628SRuslan Bilovol 		audio->params.p_fu.volume_min = audio_opts->p_volume_min;
14600356e628SRuslan Bilovol 		audio->params.p_fu.volume_max = audio_opts->p_volume_max;
14610356e628SRuslan Bilovol 		audio->params.p_fu.volume_res = audio_opts->p_volume_res;
14620356e628SRuslan Bilovol 	}
14630591bc23SRuslan Bilovol 	audio->params.p_chmask = audio_opts->p_chmask;
1464695d39ffSJulian Scheel 	memcpy(audio->params.p_srates, audio_opts->p_srates,
1465695d39ffSJulian Scheel 			sizeof(audio->params.p_srates));
14660591bc23SRuslan Bilovol 	audio->params.p_ssize = audio_opts->p_ssize;
14670356e628SRuslan Bilovol 	if (FUOUT_EN(audio_opts)) {
14680356e628SRuslan Bilovol 		audio->params.c_fu.id = USB_OUT_FU_ID;
14690356e628SRuslan Bilovol 		audio->params.c_fu.mute_present = audio_opts->c_mute_present;
14700356e628SRuslan Bilovol 		audio->params.c_fu.volume_present =
14710356e628SRuslan Bilovol 				audio_opts->c_volume_present;
14720356e628SRuslan Bilovol 		audio->params.c_fu.volume_min = audio_opts->c_volume_min;
14730356e628SRuslan Bilovol 		audio->params.c_fu.volume_max = audio_opts->c_volume_max;
14740356e628SRuslan Bilovol 		audio->params.c_fu.volume_res = audio_opts->c_volume_res;
14750356e628SRuslan Bilovol 	}
14760591bc23SRuslan Bilovol 	audio->params.req_number = audio_opts->req_number;
1477d9f27348SPavel Hofman 	audio->params.fb_max = FBACK_FAST_MAX;
14780356e628SRuslan Bilovol 	if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts))
14790356e628SRuslan Bilovol 		audio->notify = audio_notify;
14800591bc23SRuslan Bilovol 
14810591bc23SRuslan Bilovol 	status = g_audio_setup(audio, "UAC1_PCM", "UAC1_Gadget");
14820591bc23SRuslan Bilovol 	if (status)
14830591bc23SRuslan Bilovol 		goto err_card_register;
14840591bc23SRuslan Bilovol 
14850591bc23SRuslan Bilovol 	return 0;
14860591bc23SRuslan Bilovol 
14870591bc23SRuslan Bilovol err_card_register:
14880591bc23SRuslan Bilovol 	usb_free_all_descriptors(f);
14890356e628SRuslan Bilovol err_free_fu:
14900356e628SRuslan Bilovol 	kfree(out_feature_unit_desc);
14910356e628SRuslan Bilovol 	out_feature_unit_desc = NULL;
14920356e628SRuslan Bilovol 	kfree(in_feature_unit_desc);
14930356e628SRuslan Bilovol 	in_feature_unit_desc = NULL;
14940591bc23SRuslan Bilovol fail:
1495254cb1e0SRuslan Bilovol 	kfree(ac_header_desc);
1496254cb1e0SRuslan Bilovol 	ac_header_desc = NULL;
14970591bc23SRuslan Bilovol 	return status;
14980591bc23SRuslan Bilovol }
14990591bc23SRuslan Bilovol 
15000591bc23SRuslan Bilovol /*-------------------------------------------------------------------------*/
15010591bc23SRuslan Bilovol 
to_f_uac1_opts(struct config_item * item)15020591bc23SRuslan Bilovol static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)
15030591bc23SRuslan Bilovol {
15040591bc23SRuslan Bilovol 	return container_of(to_config_group(item), struct f_uac1_opts,
15050591bc23SRuslan Bilovol 			    func_inst.group);
15060591bc23SRuslan Bilovol }
15070591bc23SRuslan Bilovol 
f_uac1_attr_release(struct config_item * item)15080591bc23SRuslan Bilovol static void f_uac1_attr_release(struct config_item *item)
15090591bc23SRuslan Bilovol {
15100591bc23SRuslan Bilovol 	struct f_uac1_opts *opts = to_f_uac1_opts(item);
15110591bc23SRuslan Bilovol 
15120591bc23SRuslan Bilovol 	usb_put_function_instance(&opts->func_inst);
15130591bc23SRuslan Bilovol }
15140591bc23SRuslan Bilovol 
15150591bc23SRuslan Bilovol static struct configfs_item_operations f_uac1_item_ops = {
15160591bc23SRuslan Bilovol 	.release	= f_uac1_attr_release,
15170591bc23SRuslan Bilovol };
15180591bc23SRuslan Bilovol 
15190356e628SRuslan Bilovol #define uac1_kstrtou32			kstrtou32
15200356e628SRuslan Bilovol #define uac1_kstrtos16			kstrtos16
15210356e628SRuslan Bilovol #define uac1_kstrtobool(s, base, res)	kstrtobool((s), (res))
15220356e628SRuslan Bilovol 
15230356e628SRuslan Bilovol static const char *u32_fmt = "%u\n";
15240356e628SRuslan Bilovol static const char *s16_fmt = "%hd\n";
15250356e628SRuslan Bilovol static const char *bool_fmt = "%u\n";
15260356e628SRuslan Bilovol 
15270356e628SRuslan Bilovol #define UAC1_ATTRIBUTE(type, name)					\
15280591bc23SRuslan Bilovol static ssize_t f_uac1_opts_##name##_show(				\
15290591bc23SRuslan Bilovol 					  struct config_item *item,	\
15300591bc23SRuslan Bilovol 					  char *page)			\
15310591bc23SRuslan Bilovol {									\
15320591bc23SRuslan Bilovol 	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
15330591bc23SRuslan Bilovol 	int result;							\
15340591bc23SRuslan Bilovol 									\
15350591bc23SRuslan Bilovol 	mutex_lock(&opts->lock);					\
15360356e628SRuslan Bilovol 	result = sprintf(page, type##_fmt, opts->name);			\
15370591bc23SRuslan Bilovol 	mutex_unlock(&opts->lock);					\
15380591bc23SRuslan Bilovol 									\
15390591bc23SRuslan Bilovol 	return result;							\
15400591bc23SRuslan Bilovol }									\
15410591bc23SRuslan Bilovol 									\
15420591bc23SRuslan Bilovol static ssize_t f_uac1_opts_##name##_store(				\
15430591bc23SRuslan Bilovol 					  struct config_item *item,	\
15440591bc23SRuslan Bilovol 					  const char *page, size_t len)	\
15450591bc23SRuslan Bilovol {									\
15460591bc23SRuslan Bilovol 	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
15470591bc23SRuslan Bilovol 	int ret;							\
15480356e628SRuslan Bilovol 	type num;							\
15490591bc23SRuslan Bilovol 									\
15500591bc23SRuslan Bilovol 	mutex_lock(&opts->lock);					\
15510591bc23SRuslan Bilovol 	if (opts->refcnt) {						\
15520591bc23SRuslan Bilovol 		ret = -EBUSY;						\
15530591bc23SRuslan Bilovol 		goto end;						\
15540591bc23SRuslan Bilovol 	}								\
15550591bc23SRuslan Bilovol 									\
15560356e628SRuslan Bilovol 	ret = uac1_kstrto##type(page, 0, &num);				\
15570591bc23SRuslan Bilovol 	if (ret)							\
15580591bc23SRuslan Bilovol 		goto end;						\
15590591bc23SRuslan Bilovol 									\
15600591bc23SRuslan Bilovol 	opts->name = num;						\
15610591bc23SRuslan Bilovol 	ret = len;							\
15620591bc23SRuslan Bilovol 									\
15630591bc23SRuslan Bilovol end:									\
15640591bc23SRuslan Bilovol 	mutex_unlock(&opts->lock);					\
15650591bc23SRuslan Bilovol 	return ret;							\
15660591bc23SRuslan Bilovol }									\
15670591bc23SRuslan Bilovol 									\
15680591bc23SRuslan Bilovol CONFIGFS_ATTR(f_uac1_opts_, name)
15690591bc23SRuslan Bilovol 
1570695d39ffSJulian Scheel #define UAC1_RATE_ATTRIBUTE(name)					\
1571695d39ffSJulian Scheel static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\
1572695d39ffSJulian Scheel 					 char *page)			\
1573695d39ffSJulian Scheel {									\
1574695d39ffSJulian Scheel 	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
1575695d39ffSJulian Scheel 	int result = 0;							\
1576695d39ffSJulian Scheel 	int i;								\
1577695d39ffSJulian Scheel 									\
1578695d39ffSJulian Scheel 	mutex_lock(&opts->lock);					\
1579695d39ffSJulian Scheel 	page[0] = '\0';							\
1580695d39ffSJulian Scheel 	for (i = 0; i < UAC_MAX_RATES; i++) {				\
1581695d39ffSJulian Scheel 		if (opts->name##s[i] == 0)				\
1582695d39ffSJulian Scheel 			break;						\
1583695d39ffSJulian Scheel 		result += sprintf(page + strlen(page), "%u,",		\
1584695d39ffSJulian Scheel 				opts->name##s[i]);			\
1585695d39ffSJulian Scheel 	}								\
1586695d39ffSJulian Scheel 	if (strlen(page) > 0)						\
1587695d39ffSJulian Scheel 		page[strlen(page) - 1] = '\n';				\
1588695d39ffSJulian Scheel 	mutex_unlock(&opts->lock);					\
1589695d39ffSJulian Scheel 									\
1590695d39ffSJulian Scheel 	return result;							\
1591695d39ffSJulian Scheel }									\
1592695d39ffSJulian Scheel 									\
1593695d39ffSJulian Scheel static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\
1594695d39ffSJulian Scheel 					  const char *page, size_t len)	\
1595695d39ffSJulian Scheel {									\
1596695d39ffSJulian Scheel 	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
1597695d39ffSJulian Scheel 	char *split_page = NULL;					\
1598695d39ffSJulian Scheel 	int ret = -EINVAL;						\
1599695d39ffSJulian Scheel 	char *token;							\
1600695d39ffSJulian Scheel 	u32 num;							\
1601695d39ffSJulian Scheel 	int i;								\
1602695d39ffSJulian Scheel 									\
1603695d39ffSJulian Scheel 	mutex_lock(&opts->lock);					\
1604695d39ffSJulian Scheel 	if (opts->refcnt) {						\
1605695d39ffSJulian Scheel 		ret = -EBUSY;						\
1606695d39ffSJulian Scheel 		goto end;						\
1607695d39ffSJulian Scheel 	}								\
1608695d39ffSJulian Scheel 									\
1609695d39ffSJulian Scheel 	i = 0;								\
1610695d39ffSJulian Scheel 	memset(opts->name##s, 0x00, sizeof(opts->name##s));		\
1611695d39ffSJulian Scheel 	split_page = kstrdup(page, GFP_KERNEL);				\
1612695d39ffSJulian Scheel 	while ((token = strsep(&split_page, ",")) != NULL) {		\
1613695d39ffSJulian Scheel 		ret = kstrtou32(token, 0, &num);			\
1614695d39ffSJulian Scheel 		if (ret)						\
1615695d39ffSJulian Scheel 			goto end;					\
1616695d39ffSJulian Scheel 									\
1617695d39ffSJulian Scheel 		opts->name##s[i++] = num;				\
1618695d39ffSJulian Scheel 		ret = len;						\
1619695d39ffSJulian Scheel 	};								\
1620695d39ffSJulian Scheel 									\
1621695d39ffSJulian Scheel end:									\
1622695d39ffSJulian Scheel 	kfree(split_page);						\
1623695d39ffSJulian Scheel 	mutex_unlock(&opts->lock);					\
1624695d39ffSJulian Scheel 	return ret;							\
1625695d39ffSJulian Scheel }									\
1626695d39ffSJulian Scheel 									\
1627695d39ffSJulian Scheel CONFIGFS_ATTR(f_uac1_opts_, name)
1628695d39ffSJulian Scheel 
1629dfb05b5dSYunhao Tian #define UAC1_ATTRIBUTE_STRING(name)					\
1630dfb05b5dSYunhao Tian static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\
1631dfb05b5dSYunhao Tian 					 char *page)			\
1632dfb05b5dSYunhao Tian {									\
1633dfb05b5dSYunhao Tian 	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
1634dfb05b5dSYunhao Tian 	int result;							\
1635dfb05b5dSYunhao Tian 									\
1636dfb05b5dSYunhao Tian 	mutex_lock(&opts->lock);					\
1637c1a37186SLee Jones 	result = scnprintf(page, sizeof(opts->name), "%s", opts->name);	\
1638dfb05b5dSYunhao Tian 	mutex_unlock(&opts->lock);					\
1639dfb05b5dSYunhao Tian 									\
1640dfb05b5dSYunhao Tian 	return result;							\
1641dfb05b5dSYunhao Tian }									\
1642dfb05b5dSYunhao Tian 									\
1643dfb05b5dSYunhao Tian static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\
1644dfb05b5dSYunhao Tian 					  const char *page, size_t len)	\
1645dfb05b5dSYunhao Tian {									\
1646dfb05b5dSYunhao Tian 	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
1647dfb05b5dSYunhao Tian 	int ret = 0;							\
1648dfb05b5dSYunhao Tian 									\
1649dfb05b5dSYunhao Tian 	mutex_lock(&opts->lock);					\
1650dfb05b5dSYunhao Tian 	if (opts->refcnt) {						\
1651dfb05b5dSYunhao Tian 		ret = -EBUSY;						\
1652dfb05b5dSYunhao Tian 		goto end;						\
1653dfb05b5dSYunhao Tian 	}								\
1654dfb05b5dSYunhao Tian 									\
1655c1a37186SLee Jones 	ret = scnprintf(opts->name, min(sizeof(opts->name), len),	\
1656dfb05b5dSYunhao Tian 			"%s", page);					\
1657dfb05b5dSYunhao Tian 									\
1658dfb05b5dSYunhao Tian end:									\
1659dfb05b5dSYunhao Tian 	mutex_unlock(&opts->lock);					\
1660dfb05b5dSYunhao Tian 	return ret;							\
1661dfb05b5dSYunhao Tian }									\
1662dfb05b5dSYunhao Tian 									\
1663dfb05b5dSYunhao Tian CONFIGFS_ATTR(f_uac1_opts_, name)
1664dfb05b5dSYunhao Tian 
16650356e628SRuslan Bilovol UAC1_ATTRIBUTE(u32, c_chmask);
1666695d39ffSJulian Scheel UAC1_RATE_ATTRIBUTE(c_srate);
16670356e628SRuslan Bilovol UAC1_ATTRIBUTE(u32, c_ssize);
16680356e628SRuslan Bilovol UAC1_ATTRIBUTE(u32, p_chmask);
1669695d39ffSJulian Scheel UAC1_RATE_ATTRIBUTE(p_srate);
16700356e628SRuslan Bilovol UAC1_ATTRIBUTE(u32, p_ssize);
16710356e628SRuslan Bilovol UAC1_ATTRIBUTE(u32, req_number);
16720356e628SRuslan Bilovol 
16730356e628SRuslan Bilovol UAC1_ATTRIBUTE(bool, p_mute_present);
16740356e628SRuslan Bilovol UAC1_ATTRIBUTE(bool, p_volume_present);
16750356e628SRuslan Bilovol UAC1_ATTRIBUTE(s16, p_volume_min);
16760356e628SRuslan Bilovol UAC1_ATTRIBUTE(s16, p_volume_max);
16770356e628SRuslan Bilovol UAC1_ATTRIBUTE(s16, p_volume_res);
16780356e628SRuslan Bilovol 
16790356e628SRuslan Bilovol UAC1_ATTRIBUTE(bool, c_mute_present);
16800356e628SRuslan Bilovol UAC1_ATTRIBUTE(bool, c_volume_present);
16810356e628SRuslan Bilovol UAC1_ATTRIBUTE(s16, c_volume_min);
16820356e628SRuslan Bilovol UAC1_ATTRIBUTE(s16, c_volume_max);
16830356e628SRuslan Bilovol UAC1_ATTRIBUTE(s16, c_volume_res);
1684dfb05b5dSYunhao Tian UAC1_ATTRIBUTE_STRING(function_name);
16850591bc23SRuslan Bilovol 
16860591bc23SRuslan Bilovol static struct configfs_attribute *f_uac1_attrs[] = {
16870591bc23SRuslan Bilovol 	&f_uac1_opts_attr_c_chmask,
16880591bc23SRuslan Bilovol 	&f_uac1_opts_attr_c_srate,
16890591bc23SRuslan Bilovol 	&f_uac1_opts_attr_c_ssize,
16900591bc23SRuslan Bilovol 	&f_uac1_opts_attr_p_chmask,
16910591bc23SRuslan Bilovol 	&f_uac1_opts_attr_p_srate,
16920591bc23SRuslan Bilovol 	&f_uac1_opts_attr_p_ssize,
16930591bc23SRuslan Bilovol 	&f_uac1_opts_attr_req_number,
16940356e628SRuslan Bilovol 
16950356e628SRuslan Bilovol 	&f_uac1_opts_attr_p_mute_present,
16960356e628SRuslan Bilovol 	&f_uac1_opts_attr_p_volume_present,
16970356e628SRuslan Bilovol 	&f_uac1_opts_attr_p_volume_min,
16980356e628SRuslan Bilovol 	&f_uac1_opts_attr_p_volume_max,
16990356e628SRuslan Bilovol 	&f_uac1_opts_attr_p_volume_res,
17000356e628SRuslan Bilovol 
17010356e628SRuslan Bilovol 	&f_uac1_opts_attr_c_mute_present,
17020356e628SRuslan Bilovol 	&f_uac1_opts_attr_c_volume_present,
17030356e628SRuslan Bilovol 	&f_uac1_opts_attr_c_volume_min,
17040356e628SRuslan Bilovol 	&f_uac1_opts_attr_c_volume_max,
17050356e628SRuslan Bilovol 	&f_uac1_opts_attr_c_volume_res,
17060356e628SRuslan Bilovol 
1707dfb05b5dSYunhao Tian 	&f_uac1_opts_attr_function_name,
1708dfb05b5dSYunhao Tian 
17090591bc23SRuslan Bilovol 	NULL,
17100591bc23SRuslan Bilovol };
17110591bc23SRuslan Bilovol 
171297363902SBhumika Goyal static const struct config_item_type f_uac1_func_type = {
17130591bc23SRuslan Bilovol 	.ct_item_ops	= &f_uac1_item_ops,
17140591bc23SRuslan Bilovol 	.ct_attrs	= f_uac1_attrs,
17150591bc23SRuslan Bilovol 	.ct_owner	= THIS_MODULE,
17160591bc23SRuslan Bilovol };
17170591bc23SRuslan Bilovol 
f_audio_free_inst(struct usb_function_instance * f)17180591bc23SRuslan Bilovol static void f_audio_free_inst(struct usb_function_instance *f)
17190591bc23SRuslan Bilovol {
17200591bc23SRuslan Bilovol 	struct f_uac1_opts *opts;
17210591bc23SRuslan Bilovol 
17220591bc23SRuslan Bilovol 	opts = container_of(f, struct f_uac1_opts, func_inst);
17230591bc23SRuslan Bilovol 	kfree(opts);
17240591bc23SRuslan Bilovol }
17250591bc23SRuslan Bilovol 
f_audio_alloc_inst(void)17260591bc23SRuslan Bilovol static struct usb_function_instance *f_audio_alloc_inst(void)
17270591bc23SRuslan Bilovol {
17280591bc23SRuslan Bilovol 	struct f_uac1_opts *opts;
17290591bc23SRuslan Bilovol 
17300591bc23SRuslan Bilovol 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
17310591bc23SRuslan Bilovol 	if (!opts)
17320591bc23SRuslan Bilovol 		return ERR_PTR(-ENOMEM);
17330591bc23SRuslan Bilovol 
17340591bc23SRuslan Bilovol 	mutex_init(&opts->lock);
17350591bc23SRuslan Bilovol 	opts->func_inst.free_func_inst = f_audio_free_inst;
17360591bc23SRuslan Bilovol 
17370591bc23SRuslan Bilovol 	config_group_init_type_name(&opts->func_inst.group, "",
17380591bc23SRuslan Bilovol 				    &f_uac1_func_type);
17390591bc23SRuslan Bilovol 
17400591bc23SRuslan Bilovol 	opts->c_chmask = UAC1_DEF_CCHMASK;
1741695d39ffSJulian Scheel 	opts->c_srates[0] = UAC1_DEF_CSRATE;
17420591bc23SRuslan Bilovol 	opts->c_ssize = UAC1_DEF_CSSIZE;
17430591bc23SRuslan Bilovol 	opts->p_chmask = UAC1_DEF_PCHMASK;
1744695d39ffSJulian Scheel 	opts->p_srates[0] = UAC1_DEF_PSRATE;
17450591bc23SRuslan Bilovol 	opts->p_ssize = UAC1_DEF_PSSIZE;
17460356e628SRuslan Bilovol 
17470356e628SRuslan Bilovol 	opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
17480356e628SRuslan Bilovol 	opts->p_volume_present = UAC1_DEF_VOLUME_PRESENT;
17490356e628SRuslan Bilovol 	opts->p_volume_min = UAC1_DEF_MIN_DB;
17500356e628SRuslan Bilovol 	opts->p_volume_max = UAC1_DEF_MAX_DB;
17510356e628SRuslan Bilovol 	opts->p_volume_res = UAC1_DEF_RES_DB;
17520356e628SRuslan Bilovol 
17530356e628SRuslan Bilovol 	opts->c_mute_present = UAC1_DEF_MUTE_PRESENT;
17540356e628SRuslan Bilovol 	opts->c_volume_present = UAC1_DEF_VOLUME_PRESENT;
17550356e628SRuslan Bilovol 	opts->c_volume_min = UAC1_DEF_MIN_DB;
17560356e628SRuslan Bilovol 	opts->c_volume_max = UAC1_DEF_MAX_DB;
17570356e628SRuslan Bilovol 	opts->c_volume_res = UAC1_DEF_RES_DB;
17580356e628SRuslan Bilovol 
17590591bc23SRuslan Bilovol 	opts->req_number = UAC1_DEF_REQ_NUM;
1760dfb05b5dSYunhao Tian 
1761c1a37186SLee Jones 	scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
1762dfb05b5dSYunhao Tian 
17630591bc23SRuslan Bilovol 	return &opts->func_inst;
17640591bc23SRuslan Bilovol }
17650591bc23SRuslan Bilovol 
f_audio_free(struct usb_function * f)17660591bc23SRuslan Bilovol static void f_audio_free(struct usb_function *f)
17670591bc23SRuslan Bilovol {
17680591bc23SRuslan Bilovol 	struct g_audio *audio;
17690591bc23SRuslan Bilovol 	struct f_uac1_opts *opts;
17700591bc23SRuslan Bilovol 
17710591bc23SRuslan Bilovol 	audio = func_to_g_audio(f);
17720591bc23SRuslan Bilovol 	opts = container_of(f->fi, struct f_uac1_opts, func_inst);
17730591bc23SRuslan Bilovol 	kfree(audio);
17740591bc23SRuslan Bilovol 	mutex_lock(&opts->lock);
17750591bc23SRuslan Bilovol 	--opts->refcnt;
17760591bc23SRuslan Bilovol 	mutex_unlock(&opts->lock);
17770591bc23SRuslan Bilovol }
17780591bc23SRuslan Bilovol 
f_audio_unbind(struct usb_configuration * c,struct usb_function * f)17790591bc23SRuslan Bilovol static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
17800591bc23SRuslan Bilovol {
17810591bc23SRuslan Bilovol 	struct g_audio *audio = func_to_g_audio(f);
17820591bc23SRuslan Bilovol 
17830591bc23SRuslan Bilovol 	g_audio_cleanup(audio);
17840591bc23SRuslan Bilovol 	usb_free_all_descriptors(f);
17850591bc23SRuslan Bilovol 
17860356e628SRuslan Bilovol 	kfree(out_feature_unit_desc);
17870356e628SRuslan Bilovol 	out_feature_unit_desc = NULL;
17880356e628SRuslan Bilovol 	kfree(in_feature_unit_desc);
17890356e628SRuslan Bilovol 	in_feature_unit_desc = NULL;
17900356e628SRuslan Bilovol 
1791254cb1e0SRuslan Bilovol 	kfree(ac_header_desc);
1792254cb1e0SRuslan Bilovol 	ac_header_desc = NULL;
1793254cb1e0SRuslan Bilovol 
17940591bc23SRuslan Bilovol 	audio->gadget = NULL;
17950591bc23SRuslan Bilovol }
17960591bc23SRuslan Bilovol 
f_audio_alloc(struct usb_function_instance * fi)17970591bc23SRuslan Bilovol static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
17980591bc23SRuslan Bilovol {
17990591bc23SRuslan Bilovol 	struct f_uac1 *uac1;
18000591bc23SRuslan Bilovol 	struct f_uac1_opts *opts;
18010591bc23SRuslan Bilovol 
18020591bc23SRuslan Bilovol 	/* allocate and initialize one new instance */
18030591bc23SRuslan Bilovol 	uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL);
18040591bc23SRuslan Bilovol 	if (!uac1)
18050591bc23SRuslan Bilovol 		return ERR_PTR(-ENOMEM);
18060591bc23SRuslan Bilovol 
18070591bc23SRuslan Bilovol 	opts = container_of(fi, struct f_uac1_opts, func_inst);
18080591bc23SRuslan Bilovol 	mutex_lock(&opts->lock);
18090591bc23SRuslan Bilovol 	++opts->refcnt;
18100591bc23SRuslan Bilovol 	mutex_unlock(&opts->lock);
18110591bc23SRuslan Bilovol 
18120591bc23SRuslan Bilovol 	uac1->g_audio.func.name = "uac1_func";
18130591bc23SRuslan Bilovol 	uac1->g_audio.func.bind = f_audio_bind;
18140591bc23SRuslan Bilovol 	uac1->g_audio.func.unbind = f_audio_unbind;
18150591bc23SRuslan Bilovol 	uac1->g_audio.func.set_alt = f_audio_set_alt;
18160591bc23SRuslan Bilovol 	uac1->g_audio.func.get_alt = f_audio_get_alt;
18170591bc23SRuslan Bilovol 	uac1->g_audio.func.setup = f_audio_setup;
18180591bc23SRuslan Bilovol 	uac1->g_audio.func.disable = f_audio_disable;
1819d1d11dd1SPavel Hofman 	uac1->g_audio.func.suspend = f_audio_suspend;
18200591bc23SRuslan Bilovol 	uac1->g_audio.func.free_func = f_audio_free;
18210591bc23SRuslan Bilovol 
18220591bc23SRuslan Bilovol 	return &uac1->g_audio.func;
18230591bc23SRuslan Bilovol }
18240591bc23SRuslan Bilovol 
18250591bc23SRuslan Bilovol DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
1826*1cb9ba5eSJeff Johnson MODULE_DESCRIPTION("USB Audio Class 1.0 Function (using u_audio API)");
18270591bc23SRuslan Bilovol MODULE_LICENSE("GPL");
18280591bc23SRuslan Bilovol MODULE_AUTHOR("Ruslan Bilovol");
1829