diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_uac1.c')
| -rw-r--r-- | drivers/usb/gadget/function/f_uac1.c | 896 | 
1 files changed, 358 insertions, 538 deletions
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index f2ac0cbc29a4..8656f84e17d9 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -1,24 +1,38 @@  /* - * f_audio.c -- USB Audio class function driver -  * - * Copyright (C) 2008 Bryan Wu <[email protected]> - * Copyright (C) 2008 Analog Devices, Inc + * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)   * - * Enter bugs at http://blackfin.uclinux.org/ + * Copyright (C) 2016 Ruslan Bilovol <[email protected]>   * - * Licensed under the GPL-2 or later. + * This driver doesn't expect any real Audio codec to be present + * on the device - the audio streams are simply sinked to and + * sourced from a virtual ALSA sound card created. + * + * This file is based on f_uac1.c which is + *   Copyright (C) 2008 Bryan Wu <[email protected]> + *   Copyright (C) 2008 Analog Devices, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version.   */ -#include <linux/slab.h> -#include <linux/kernel.h> +#include <linux/usb/audio.h>  #include <linux/module.h> -#include <linux/device.h> -#include <linux/atomic.h> +#include "u_audio.h"  #include "u_uac1.h" -static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); -static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); +struct f_uac1 { +	struct g_audio g_audio; +	u8 ac_intf, as_in_intf, as_out_intf; +	u8 ac_alt, as_in_alt, as_out_alt;	/* needed for get_alt() */ +}; + +static inline struct f_uac1 *func_to_uac1(struct usb_function *f) +{ +	return container_of(f, struct f_uac1, g_audio.func); +}  /*   * DESCRIPTORS ... most are static, but strings and full @@ -26,12 +40,17 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);   */  /* - * We have two interfaces- AudioControl and AudioStreaming - * TODO: only supcard playback currently + * We have three interfaces - one AudioControl and two AudioStreaming + * + * The driver implements a simple UAC_1 topology. + * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture + * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN   */ -#define F_AUDIO_AC_INTERFACE	0 -#define F_AUDIO_AS_INTERFACE	1 -#define F_AUDIO_NUM_INTERFACES	1 +#define F_AUDIO_AC_INTERFACE		0 +#define F_AUDIO_AS_OUT_INTERFACE	1 +#define F_AUDIO_AS_IN_INTERFACE		2 +/* Number of streaming interfaces */ +#define F_AUDIO_NUM_INTERFACES		2  /* B.3.1  Standard AC Interface Descriptor */  static struct usb_interface_descriptor ac_interface_desc = { @@ -46,89 +65,91 @@ static struct usb_interface_descriptor ac_interface_desc = {   * The number of AudioStreaming and MIDIStreaming interfaces   * in the Audio Interface Collection   */ -DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);  #define UAC_DT_AC_HEADER_LENGTH	UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) -/* 1 input terminal, 1 output terminal and 1 feature unit */ -#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \ -	+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0)) +/* 2 input terminals and 2 output terminals */ +#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ +	+ 2*UAC_DT_INPUT_TERMINAL_SIZE + 2*UAC_DT_OUTPUT_TERMINAL_SIZE)  /* B.3.2  Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_1 ac_header_desc = { +static struct uac1_ac_header_descriptor_2 ac_header_desc = {  	.bLength =		UAC_DT_AC_HEADER_LENGTH,  	.bDescriptorType =	USB_DT_CS_INTERFACE,  	.bDescriptorSubtype =	UAC_HEADER, -	.bcdADC =		__constant_cpu_to_le16(0x0100), -	.wTotalLength =		__constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), +	.bcdADC =		cpu_to_le16(0x0100), +	.wTotalLength =		cpu_to_le16(UAC_DT_TOTAL_LENGTH),  	.bInCollection =	F_AUDIO_NUM_INTERFACES,  	.baInterfaceNr = { -	/* Interface number of the first AudioStream interface */ +	/* Interface number of the AudioStream interfaces */  		[0] =		1, +		[1] =		2,  	}  }; -#define INPUT_TERMINAL_ID	1 -static struct uac_input_terminal_descriptor input_terminal_desc = { +#define USB_OUT_IT_ID	1 +static struct uac_input_terminal_descriptor usb_out_it_desc = {  	.bLength =		UAC_DT_INPUT_TERMINAL_SIZE,  	.bDescriptorType =	USB_DT_CS_INTERFACE,  	.bDescriptorSubtype =	UAC_INPUT_TERMINAL, -	.bTerminalID =		INPUT_TERMINAL_ID, +	.bTerminalID =		USB_OUT_IT_ID,  	.wTerminalType =	UAC_TERMINAL_STREAMING,  	.bAssocTerminal =	0,  	.wChannelConfig =	0x3,  }; -DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); - -#define FEATURE_UNIT_ID		2 -static struct uac_feature_unit_descriptor_0 feature_unit_desc = { -	.bLength		= UAC_DT_FEATURE_UNIT_SIZE(0), +#define IO_OUT_OT_ID	2 +static struct uac1_output_terminal_descriptor io_out_ot_desc = { +	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,  	.bDescriptorType	= USB_DT_CS_INTERFACE, -	.bDescriptorSubtype	= UAC_FEATURE_UNIT, -	.bUnitID		= FEATURE_UNIT_ID, -	.bSourceID		= INPUT_TERMINAL_ID, -	.bControlSize		= 2, -	.bmaControls[0]		= (UAC_FU_MUTE | UAC_FU_VOLUME), +	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL, +	.bTerminalID		= IO_OUT_OT_ID, +	.wTerminalType		= UAC_OUTPUT_TERMINAL_SPEAKER, +	.bAssocTerminal		= 0, +	.bSourceID		= USB_OUT_IT_ID,  }; -static struct usb_audio_control mute_control = { -	.list = LIST_HEAD_INIT(mute_control.list), -	.name = "Mute Control", -	.type = UAC_FU_MUTE, -	/* Todo: add real Mute control code */ -	.set = generic_set_cmd, -	.get = generic_get_cmd, +#define IO_IN_IT_ID	3 +static struct uac_input_terminal_descriptor io_in_it_desc = { +	.bLength		= UAC_DT_INPUT_TERMINAL_SIZE, +	.bDescriptorType	= USB_DT_CS_INTERFACE, +	.bDescriptorSubtype	= UAC_INPUT_TERMINAL, +	.bTerminalID		= IO_IN_IT_ID, +	.wTerminalType		= UAC_INPUT_TERMINAL_MICROPHONE, +	.bAssocTerminal		= 0, +	.wChannelConfig		= 0x3,  }; -static struct usb_audio_control volume_control = { -	.list = LIST_HEAD_INIT(volume_control.list), -	.name = "Volume Control", -	.type = UAC_FU_VOLUME, -	/* Todo: add real Volume control code */ -	.set = generic_set_cmd, -	.get = generic_get_cmd, +#define USB_IN_OT_ID	4 +static struct uac1_output_terminal_descriptor usb_in_ot_desc = { +	.bLength =		UAC_DT_OUTPUT_TERMINAL_SIZE, +	.bDescriptorType =	USB_DT_CS_INTERFACE, +	.bDescriptorSubtype =	UAC_OUTPUT_TERMINAL, +	.bTerminalID =		USB_IN_OT_ID, +	.wTerminalType =	UAC_TERMINAL_STREAMING, +	.bAssocTerminal =	0, +	.bSourceID =		IO_IN_IT_ID,  }; -static struct usb_audio_control_selector feature_unit = { -	.list = LIST_HEAD_INIT(feature_unit.list), -	.id = FEATURE_UNIT_ID, -	.name = "Mute & Volume Control", -	.type = UAC_FEATURE_UNIT, -	.desc = (struct usb_descriptor_header *)&feature_unit_desc, +/* B.4.1  Standard AS Interface Descriptor */ +static struct usb_interface_descriptor as_out_interface_alt_0_desc = { +	.bLength =		USB_DT_INTERFACE_SIZE, +	.bDescriptorType =	USB_DT_INTERFACE, +	.bAlternateSetting =	0, +	.bNumEndpoints =	0, +	.bInterfaceClass =	USB_CLASS_AUDIO, +	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,  }; -#define OUTPUT_TERMINAL_ID	3 -static struct uac1_output_terminal_descriptor output_terminal_desc = { -	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE, -	.bDescriptorType	= USB_DT_CS_INTERFACE, -	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL, -	.bTerminalID		= OUTPUT_TERMINAL_ID, -	.wTerminalType		= UAC_OUTPUT_TERMINAL_SPEAKER, -	.bAssocTerminal		= FEATURE_UNIT_ID, -	.bSourceID		= FEATURE_UNIT_ID, +static struct usb_interface_descriptor as_out_interface_alt_1_desc = { +	.bLength =		USB_DT_INTERFACE_SIZE, +	.bDescriptorType =	USB_DT_INTERFACE, +	.bAlternateSetting =	1, +	.bNumEndpoints =	1, +	.bInterfaceClass =	USB_CLASS_AUDIO, +	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,  }; -/* B.4.1  Standard AS Interface Descriptor */ -static struct usb_interface_descriptor as_interface_alt_0_desc = { +static struct usb_interface_descriptor as_in_interface_alt_0_desc = {  	.bLength =		USB_DT_INTERFACE_SIZE,  	.bDescriptorType =	USB_DT_INTERFACE,  	.bAlternateSetting =	0, @@ -137,7 +158,7 @@ static struct usb_interface_descriptor as_interface_alt_0_desc = {  	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,  }; -static struct usb_interface_descriptor as_interface_alt_1_desc = { +static struct usb_interface_descriptor as_in_interface_alt_1_desc = {  	.bLength =		USB_DT_INTERFACE_SIZE,  	.bDescriptorType =	USB_DT_INTERFACE,  	.bAlternateSetting =	1, @@ -147,18 +168,27 @@ static struct usb_interface_descriptor as_interface_alt_1_desc = {  };  /* B.4.2  Class-Specific AS Interface Descriptor */ -static struct uac1_as_header_descriptor as_header_desc = { +static struct uac1_as_header_descriptor as_out_header_desc = {  	.bLength =		UAC_DT_AS_HEADER_SIZE,  	.bDescriptorType =	USB_DT_CS_INTERFACE,  	.bDescriptorSubtype =	UAC_AS_GENERAL, -	.bTerminalLink =	INPUT_TERMINAL_ID, +	.bTerminalLink =	USB_OUT_IT_ID, +	.bDelay =		1, +	.wFormatTag =		UAC_FORMAT_TYPE_I_PCM, +}; + +static struct uac1_as_header_descriptor as_in_header_desc = { +	.bLength =		UAC_DT_AS_HEADER_SIZE, +	.bDescriptorType =	USB_DT_CS_INTERFACE, +	.bDescriptorSubtype =	UAC_AS_GENERAL, +	.bTerminalLink =	USB_IN_OT_ID,  	.bDelay =		1,  	.wFormatTag =		UAC_FORMAT_TYPE_I_PCM,  };  DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); -static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { +static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {  	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),  	.bDescriptorType =	USB_DT_CS_INTERFACE,  	.bDescriptorSubtype =	UAC_FORMAT_TYPE, @@ -184,48 +214,97 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {  	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,  	.bDescriptorType =	USB_DT_CS_ENDPOINT,  	.bDescriptorSubtype =	UAC_EP_GENERAL, -	.bmAttributes = 	1, +	.bmAttributes =		1,  	.bLockDelayUnits =	1, -	.wLockDelay =		__constant_cpu_to_le16(1), +	.wLockDelay =		cpu_to_le16(1), +}; + +static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = { +	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), +	.bDescriptorType =	USB_DT_CS_INTERFACE, +	.bDescriptorSubtype =	UAC_FORMAT_TYPE, +	.bFormatType =		UAC_FORMAT_TYPE_I, +	.bSubframeSize =	2, +	.bBitResolution =	16, +	.bSamFreqType =		1, +}; + +/* Standard ISO OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor as_in_ep_desc  = { +	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, +	.bEndpointAddress =	USB_DIR_IN, +	.bmAttributes =		USB_ENDPOINT_SYNC_ASYNC +				| USB_ENDPOINT_XFER_ISOC, +	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), +	.bInterval =		4, +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor as_iso_in_desc = { +	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE, +	.bDescriptorType =	USB_DT_CS_ENDPOINT, +	.bDescriptorSubtype =	UAC_EP_GENERAL, +	.bmAttributes =		1, +	.bLockDelayUnits =	0, +	.wLockDelay =		0,  };  static struct usb_descriptor_header *f_audio_desc[] = {  	(struct usb_descriptor_header *)&ac_interface_desc,  	(struct usb_descriptor_header *)&ac_header_desc, -	(struct usb_descriptor_header *)&input_terminal_desc, -	(struct usb_descriptor_header *)&output_terminal_desc, -	(struct usb_descriptor_header *)&feature_unit_desc, +	(struct usb_descriptor_header *)&usb_out_it_desc, +	(struct usb_descriptor_header *)&io_out_ot_desc, +	(struct usb_descriptor_header *)&io_in_it_desc, +	(struct usb_descriptor_header *)&usb_in_ot_desc, -	(struct usb_descriptor_header *)&as_interface_alt_0_desc, -	(struct usb_descriptor_header *)&as_interface_alt_1_desc, -	(struct usb_descriptor_header *)&as_header_desc, +	(struct usb_descriptor_header *)&as_out_interface_alt_0_desc, +	(struct usb_descriptor_header *)&as_out_interface_alt_1_desc, +	(struct usb_descriptor_header *)&as_out_header_desc, -	(struct usb_descriptor_header *)&as_type_i_desc, +	(struct usb_descriptor_header *)&as_out_type_i_desc,  	(struct usb_descriptor_header *)&as_out_ep_desc,  	(struct usb_descriptor_header *)&as_iso_out_desc, + +	(struct usb_descriptor_header *)&as_in_interface_alt_0_desc, +	(struct usb_descriptor_header *)&as_in_interface_alt_1_desc, +	(struct usb_descriptor_header *)&as_in_header_desc, + +	(struct usb_descriptor_header *)&as_in_type_i_desc, + +	(struct usb_descriptor_header *)&as_in_ep_desc, +	(struct usb_descriptor_header *)&as_iso_in_desc,  	NULL,  };  enum {  	STR_AC_IF, -	STR_INPUT_TERMINAL, -	STR_INPUT_TERMINAL_CH_NAMES, -	STR_FEAT_DESC_0, -	STR_OUTPUT_TERMINAL, -	STR_AS_IF_ALT0, -	STR_AS_IF_ALT1, +	STR_USB_OUT_IT, +	STR_USB_OUT_IT_CH_NAMES, +	STR_IO_OUT_OT, +	STR_IO_IN_IT, +	STR_IO_IN_IT_CH_NAMES, +	STR_USB_IN_OT, +	STR_AS_OUT_IF_ALT0, +	STR_AS_OUT_IF_ALT1, +	STR_AS_IN_IF_ALT0, +	STR_AS_IN_IF_ALT1,  };  static struct usb_string strings_uac1[] = {  	[STR_AC_IF].s = "AC Interface", -	[STR_INPUT_TERMINAL].s = "Input terminal", -	[STR_INPUT_TERMINAL_CH_NAMES].s = "Channels", -	[STR_FEAT_DESC_0].s = "Volume control & mute", -	[STR_OUTPUT_TERMINAL].s = "Output terminal", -	[STR_AS_IF_ALT0].s = "AS Interface", -	[STR_AS_IF_ALT1].s = "AS Interface", +	[STR_USB_OUT_IT].s = "Playback Input terminal", +	[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels", +	[STR_IO_OUT_OT].s = "Playback Output terminal", +	[STR_IO_IN_IT].s = "Capture Input terminal", +	[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels", +	[STR_USB_IN_OT].s = "Capture Output terminal", +	[STR_AS_OUT_IF_ALT0].s = "Playback Inactive", +	[STR_AS_OUT_IF_ALT1].s = "Playback Active", +	[STR_AS_IN_IF_ALT0].s = "Capture Inactive", +	[STR_AS_IN_IF_ALT1].s = "Capture Active",  	{ },  }; @@ -243,216 +322,6 @@ static struct usb_gadget_strings *uac1_strings[] = {   * This function is an ALSA sound card following USB Audio Class Spec 1.0.   */ -/*-------------------------------------------------------------------------*/ -struct f_audio_buf { -	u8 *buf; -	int actual; -	struct list_head list; -}; - -static struct f_audio_buf *f_audio_buffer_alloc(int buf_size) -{ -	struct f_audio_buf *copy_buf; - -	copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC); -	if (!copy_buf) -		return ERR_PTR(-ENOMEM); - -	copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC); -	if (!copy_buf->buf) { -		kfree(copy_buf); -		return ERR_PTR(-ENOMEM); -	} - -	return copy_buf; -} - -static void f_audio_buffer_free(struct f_audio_buf *audio_buf) -{ -	kfree(audio_buf->buf); -	kfree(audio_buf); -} -/*-------------------------------------------------------------------------*/ - -struct f_audio { -	struct gaudio			card; - -	/* endpoints handle full and/or high speeds */ -	struct usb_ep			*out_ep; - -	spinlock_t			lock; -	struct f_audio_buf *copy_buf; -	struct work_struct playback_work; -	struct list_head play_queue; - -	/* Control Set command */ -	struct list_head cs; -	u8 set_cmd; -	struct usb_audio_control *set_con; -}; - -static inline struct f_audio *func_to_audio(struct usb_function *f) -{ -	return container_of(f, struct f_audio, card.func); -} - -/*-------------------------------------------------------------------------*/ - -static void f_audio_playback_work(struct work_struct *data) -{ -	struct f_audio *audio = container_of(data, struct f_audio, -					playback_work); -	struct f_audio_buf *play_buf; - -	spin_lock_irq(&audio->lock); -	if (list_empty(&audio->play_queue)) { -		spin_unlock_irq(&audio->lock); -		return; -	} -	play_buf = list_first_entry(&audio->play_queue, -			struct f_audio_buf, list); -	list_del(&play_buf->list); -	spin_unlock_irq(&audio->lock); - -	u_audio_playback(&audio->card, play_buf->buf, play_buf->actual); -	f_audio_buffer_free(play_buf); -} - -static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) -{ -	struct f_audio *audio = req->context; -	struct usb_composite_dev *cdev = audio->card.func.config->cdev; -	struct f_audio_buf *copy_buf = audio->copy_buf; -	struct f_uac1_opts *opts; -	int audio_buf_size; -	int err; - -	opts = container_of(audio->card.func.fi, struct f_uac1_opts, -			    func_inst); -	audio_buf_size = opts->audio_buf_size; - -	if (!copy_buf) -		return -EINVAL; - -	/* Copy buffer is full, add it to the play_queue */ -	if (audio_buf_size - copy_buf->actual < req->actual) { -		list_add_tail(©_buf->list, &audio->play_queue); -		schedule_work(&audio->playback_work); -		copy_buf = f_audio_buffer_alloc(audio_buf_size); -		if (IS_ERR(copy_buf)) -			return -ENOMEM; -	} - -	memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual); -	copy_buf->actual += req->actual; -	audio->copy_buf = copy_buf; - -	err = usb_ep_queue(ep, req, GFP_ATOMIC); -	if (err) -		ERROR(cdev, "%s queue req: %d\n", ep->name, err); - -	return 0; - -} - -static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) -{ -	struct f_audio *audio = req->context; -	int status = req->status; -	u32 data = 0; -	struct usb_ep *out_ep = audio->out_ep; - -	switch (status) { - -	case 0:				/* normal completion? */ -		if (ep == out_ep) -			f_audio_out_ep_complete(ep, req); -		else if (audio->set_con) { -			memcpy(&data, req->buf, req->length); -			audio->set_con->set(audio->set_con, audio->set_cmd, -					le16_to_cpu(data)); -			audio->set_con = NULL; -		} -		break; -	default: -		break; -	} -} - -static int audio_set_intf_req(struct usb_function *f, -		const struct usb_ctrlrequest *ctrl) -{ -	struct f_audio		*audio = func_to_audio(f); -	struct usb_composite_dev *cdev = f->config->cdev; -	struct usb_request	*req = cdev->req; -	u8			id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); -	u16			len = le16_to_cpu(ctrl->wLength); -	u16			w_value = le16_to_cpu(ctrl->wValue); -	u8			con_sel = (w_value >> 8) & 0xFF; -	u8			cmd = (ctrl->bRequest & 0x0F); -	struct usb_audio_control_selector *cs; -	struct usb_audio_control *con; - -	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", -			ctrl->bRequest, w_value, len, id); - -	list_for_each_entry(cs, &audio->cs, list) { -		if (cs->id == id) { -			list_for_each_entry(con, &cs->control, list) { -				if (con->type == con_sel) { -					audio->set_con = con; -					break; -				} -			} -			break; -		} -	} - -	audio->set_cmd = cmd; -	req->context = audio; -	req->complete = f_audio_complete; - -	return len; -} - -static int audio_get_intf_req(struct usb_function *f, -		const struct usb_ctrlrequest *ctrl) -{ -	struct f_audio		*audio = func_to_audio(f); -	struct usb_composite_dev *cdev = f->config->cdev; -	struct usb_request	*req = cdev->req; -	int			value = -EOPNOTSUPP; -	u8			id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); -	u16			len = le16_to_cpu(ctrl->wLength); -	u16			w_value = le16_to_cpu(ctrl->wValue); -	u8			con_sel = (w_value >> 8) & 0xFF; -	u8			cmd = (ctrl->bRequest & 0x0F); -	struct usb_audio_control_selector *cs; -	struct usb_audio_control *con; - -	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", -			ctrl->bRequest, w_value, len, id); - -	list_for_each_entry(cs, &audio->cs, list) { -		if (cs->id == id) { -			list_for_each_entry(con, &cs->control, list) { -				if (con->type == con_sel && con->get) { -					value = con->get(con, cmd); -					break; -				} -			} -			break; -		} -	} - -	req->context = audio; -	req->complete = f_audio_complete; -	len = min_t(size_t, sizeof(value), len); -	memcpy(req->buf, &value, len); - -	return len; -} -  static int audio_set_endpoint_req(struct usb_function *f,  		const struct usb_ctrlrequest *ctrl)  { @@ -531,14 +400,6 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)  	 * activation uses set_alt().  	 */  	switch (ctrl->bRequestType) { -	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: -		value = audio_set_intf_req(f, ctrl); -		break; - -	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: -		value = audio_get_intf_req(f, ctrl); -		break; -  	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:  		value = audio_set_endpoint_req(f, ctrl);  		break; @@ -571,143 +432,158 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)  static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)  { -	struct f_audio		*audio = func_to_audio(f);  	struct usb_composite_dev *cdev = f->config->cdev; -	struct usb_ep *out_ep = audio->out_ep; -	struct usb_request *req; -	struct f_uac1_opts *opts; -	int req_buf_size, req_count, audio_buf_size; -	int i = 0, err = 0; - -	DBG(cdev, "intf %d, alt %d\n", intf, alt); +	struct usb_gadget *gadget = cdev->gadget; +	struct device *dev = &gadget->dev; +	struct f_uac1 *uac1 = func_to_uac1(f); +	int ret = 0; + +	/* No i/f has more than 2 alt settings */ +	if (alt > 1) { +		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); +		return -EINVAL; +	} -	opts = container_of(f->fi, struct f_uac1_opts, func_inst); -	req_buf_size = opts->req_buf_size; -	req_count = opts->req_count; -	audio_buf_size = opts->audio_buf_size; - -	if (intf == 1) { -		if (alt == 1) { -			err = config_ep_by_speed(cdev->gadget, f, out_ep); -			if (err) -				return err; - -			usb_ep_enable(out_ep); -			audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); -			if (IS_ERR(audio->copy_buf)) -				return -ENOMEM; - -			/* -			 * allocate a bunch of read buffers -			 * and queue them all at once. -			 */ -			for (i = 0; i < req_count && err == 0; i++) { -				req = usb_ep_alloc_request(out_ep, GFP_ATOMIC); -				if (req) { -					req->buf = kzalloc(req_buf_size, -							GFP_ATOMIC); -					if (req->buf) { -						req->length = req_buf_size; -						req->context = audio; -						req->complete = -							f_audio_complete; -						err = usb_ep_queue(out_ep, -							req, GFP_ATOMIC); -						if (err) -							ERROR(cdev, -							"%s queue req: %d\n", -							out_ep->name, err); -					} else -						err = -ENOMEM; -				} else -					err = -ENOMEM; -			} - -		} else { -			struct f_audio_buf *copy_buf = audio->copy_buf; -			if (copy_buf) { -				list_add_tail(©_buf->list, -						&audio->play_queue); -				schedule_work(&audio->playback_work); -			} +	if (intf == uac1->ac_intf) { +		/* Control I/f has only 1 AltSetting - 0 */ +		if (alt) { +			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); +			return -EINVAL;  		} +		return 0;  	} -	return err; +	if (intf == uac1->as_out_intf) { +		uac1->as_out_alt = alt; + +		if (alt) +			ret = u_audio_start_capture(&uac1->g_audio); +		else +			u_audio_stop_capture(&uac1->g_audio); +	} else if (intf == uac1->as_in_intf) { +		uac1->as_in_alt = alt; + +			if (alt) +				ret = u_audio_start_playback(&uac1->g_audio); +			else +				u_audio_stop_playback(&uac1->g_audio); +	} else { +		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); +		return -EINVAL; +	} + +	return ret;  } -static void f_audio_disable(struct usb_function *f) +static int f_audio_get_alt(struct usb_function *f, unsigned intf)  { -	return; +	struct usb_composite_dev *cdev = f->config->cdev; +	struct usb_gadget *gadget = cdev->gadget; +	struct device *dev = &gadget->dev; +	struct f_uac1 *uac1 = func_to_uac1(f); + +	if (intf == uac1->ac_intf) +		return uac1->ac_alt; +	else if (intf == uac1->as_out_intf) +		return uac1->as_out_alt; +	else if (intf == uac1->as_in_intf) +		return uac1->as_in_alt; +	else +		dev_err(dev, "%s:%d Invalid Interface %d!\n", +			__func__, __LINE__, intf); + +	return -EINVAL;  } -/*-------------------------------------------------------------------------*/ -static void f_audio_build_desc(struct f_audio *audio) +static void f_audio_disable(struct usb_function *f)  { -	struct gaudio *card = &audio->card; -	u8 *sam_freq; -	int rate; +	struct f_uac1 *uac1 = func_to_uac1(f); -	/* Set channel numbers */ -	input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card); -	as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card); +	uac1->as_out_alt = 0; +	uac1->as_in_alt = 0; -	/* Set sample rates */ -	rate = u_audio_get_playback_rate(card); -	sam_freq = as_type_i_desc.tSamFreq[0]; -	memcpy(sam_freq, &rate, 3); - -	/* Todo: Set Sample bits and other parameters */ - -	return; +	u_audio_stop_capture(&uac1->g_audio);  } +/*-------------------------------------------------------------------------*/ +  /* audio function driver setup/binding */ -static int -f_audio_bind(struct usb_configuration *c, struct usb_function *f) +static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)  { -	struct usb_composite_dev *cdev = c->cdev; -	struct f_audio		*audio = func_to_audio(f); -	struct usb_string	*us; -	int			status; -	struct usb_ep		*ep = NULL; -	struct f_uac1_opts	*audio_opts; +	struct usb_composite_dev	*cdev = c->cdev; +	struct usb_gadget		*gadget = cdev->gadget; +	struct f_uac1			*uac1 = func_to_uac1(f); +	struct g_audio			*audio = func_to_g_audio(f); +	struct f_uac1_opts		*audio_opts; +	struct usb_ep			*ep = NULL; +	struct usb_string		*us; +	u8				*sam_freq; +	int				rate; +	int				status;  	audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); -	audio->card.gadget = c->cdev->gadget; -	/* set up ASLA audio devices */ -	if (!audio_opts->bound) { -		status = gaudio_setup(&audio->card); -		if (status < 0) -			return status; -		audio_opts->bound = true; -	} +  	us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));  	if (IS_ERR(us))  		return PTR_ERR(us);  	ac_interface_desc.iInterface = us[STR_AC_IF].id; -	input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id; -	input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id; -	feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id; -	output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id; -	as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id; -	as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id; +	usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id; +	usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id; +	io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id; +	as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id; +	as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id; +	io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id; +	io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id; +	usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id; +	as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id; +	as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id; +	/* Set channel numbers */ +	usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask); +	usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask); +	as_out_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask); +	as_out_type_i_desc.bSubframeSize = audio_opts->c_ssize; +	as_out_type_i_desc.bBitResolution = audio_opts->c_ssize * 8; +	io_in_it_desc.bNrChannels = num_channels(audio_opts->p_chmask); +	io_in_it_desc.wChannelConfig = cpu_to_le16(audio_opts->p_chmask); +	as_in_type_i_desc.bNrChannels = num_channels(audio_opts->p_chmask); +	as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize; +	as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8; -	f_audio_build_desc(audio); +	/* Set sample rates */ +	rate = audio_opts->c_srate; +	sam_freq = as_out_type_i_desc.tSamFreq[0]; +	memcpy(sam_freq, &rate, 3); +	rate = audio_opts->p_srate; +	sam_freq = as_in_type_i_desc.tSamFreq[0]; +	memcpy(sam_freq, &rate, 3);  	/* allocate instance-specific interface IDs, and patch descriptors */  	status = usb_interface_id(c, f);  	if (status < 0)  		goto fail;  	ac_interface_desc.bInterfaceNumber = status; +	uac1->ac_intf = status; +	uac1->ac_alt = 0;  	status = usb_interface_id(c, f);  	if (status < 0)  		goto fail; -	as_interface_alt_0_desc.bInterfaceNumber = status; -	as_interface_alt_1_desc.bInterfaceNumber = status; +	as_out_interface_alt_0_desc.bInterfaceNumber = status; +	as_out_interface_alt_1_desc.bInterfaceNumber = status; +	uac1->as_out_intf = status; +	uac1->as_out_alt = 0; + +	status = usb_interface_id(c, f); +	if (status < 0) +		goto fail; +	as_in_interface_alt_0_desc.bInterfaceNumber = status; +	as_in_interface_alt_1_desc.bInterfaceNumber = status; +	uac1->as_in_intf = status; +	uac1->as_in_alt = 0; + +	audio->gadget = gadget;  	status = -ENODEV; @@ -718,52 +594,42 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)  	audio->out_ep = ep;  	audio->out_ep->desc = &as_out_ep_desc; -	status = -ENOMEM; +	ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); +	if (!ep) +		goto fail; +	audio->in_ep = ep; +	audio->in_ep->desc = &as_in_ep_desc;  	/* copy descriptors, and track endpoint copies */  	status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,  					NULL);  	if (status)  		goto fail; + +	audio->out_ep_maxpsize = as_out_ep_desc.wMaxPacketSize; +	audio->in_ep_maxpsize = as_in_ep_desc.wMaxPacketSize; +	audio->params.c_chmask = audio_opts->c_chmask; +	audio->params.c_srate = audio_opts->c_srate; +	audio->params.c_ssize = audio_opts->c_ssize; +	audio->params.p_chmask = audio_opts->p_chmask; +	audio->params.p_srate = audio_opts->p_srate; +	audio->params.p_ssize = audio_opts->p_ssize; +	audio->params.req_number = audio_opts->req_number; + +	status = g_audio_setup(audio, "UAC1_PCM", "UAC1_Gadget"); +	if (status) +		goto err_card_register; +  	return 0; +err_card_register: +	usb_free_all_descriptors(f);  fail: -	gaudio_cleanup(&audio->card);  	return status;  }  /*-------------------------------------------------------------------------*/ -static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) -{ -	con->data[cmd] = value; - -	return 0; -} - -static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) -{ -	return con->data[cmd]; -} - -/* Todo: add more control selecotor dynamically */ -static int control_selector_init(struct f_audio *audio) -{ -	INIT_LIST_HEAD(&audio->cs); -	list_add(&feature_unit.list, &audio->cs); - -	INIT_LIST_HEAD(&feature_unit.control); -	list_add(&mute_control.list, &feature_unit.control); -	list_add(&volume_control.list, &feature_unit.control); - -	volume_control.data[UAC__CUR] = 0xffc0; -	volume_control.data[UAC__MIN] = 0xe3a0; -	volume_control.data[UAC__MAX] = 0xfff0; -	volume_control.data[UAC__RES] = 0x0030; - -	return 0; -} -  static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)  {  	return container_of(to_config_group(item), struct f_uac1_opts, @@ -781,9 +647,10 @@ static struct configfs_item_operations f_uac1_item_ops = {  	.release	= f_uac1_attr_release,  }; -#define UAC1_INT_ATTRIBUTE(name)					\ -static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\ -					 char *page)			\ +#define UAC1_ATTRIBUTE(name)						\ +static ssize_t f_uac1_opts_##name##_show(				\ +					  struct config_item *item,	\ +					  char *page)			\  {									\  	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\  	int result;							\ @@ -795,7 +662,8 @@ static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\  	return result;							\  }									\  									\ -static ssize_t f_uac1_opts_##name##_store(struct config_item *item,		\ +static ssize_t f_uac1_opts_##name##_store(				\ +					  struct config_item *item,	\  					  const char *page, size_t len)	\  {									\  	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\ @@ -822,64 +690,22 @@ end:									\  									\  CONFIGFS_ATTR(f_uac1_opts_, name) -UAC1_INT_ATTRIBUTE(req_buf_size); -UAC1_INT_ATTRIBUTE(req_count); -UAC1_INT_ATTRIBUTE(audio_buf_size); - -#define UAC1_STR_ATTRIBUTE(name)					\ -static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\ -					 char *page)			\ -{									\ -	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\ -	int result;							\ -									\ -	mutex_lock(&opts->lock);					\ -	result = sprintf(page, "%s\n", opts->name);			\ -	mutex_unlock(&opts->lock);					\ -									\ -	return result;							\ -}									\ -									\ -static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\ -					  const char *page, size_t len)	\ -{									\ -	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\ -	int ret = -EBUSY;						\ -	char *tmp;							\ -									\ -	mutex_lock(&opts->lock);					\ -	if (opts->refcnt)						\ -		goto end;						\ -									\ -	tmp = kstrndup(page, len, GFP_KERNEL);				\ -	if (tmp) {							\ -		ret = -ENOMEM;						\ -		goto end;						\ -	}								\ -	if (opts->name##_alloc)						\ -		kfree(opts->name);					\ -	opts->name##_alloc = true;					\ -	opts->name = tmp;						\ -	ret = len;							\ -									\ -end:									\ -	mutex_unlock(&opts->lock);					\ -	return ret;							\ -}									\ -									\ -CONFIGFS_ATTR(f_uac1_opts_, name) - -UAC1_STR_ATTRIBUTE(fn_play); -UAC1_STR_ATTRIBUTE(fn_cap); -UAC1_STR_ATTRIBUTE(fn_cntl); +UAC1_ATTRIBUTE(c_chmask); +UAC1_ATTRIBUTE(c_srate); +UAC1_ATTRIBUTE(c_ssize); +UAC1_ATTRIBUTE(p_chmask); +UAC1_ATTRIBUTE(p_srate); +UAC1_ATTRIBUTE(p_ssize); +UAC1_ATTRIBUTE(req_number);  static struct configfs_attribute *f_uac1_attrs[] = { -	&f_uac1_opts_attr_req_buf_size, -	&f_uac1_opts_attr_req_count, -	&f_uac1_opts_attr_audio_buf_size, -	&f_uac1_opts_attr_fn_play, -	&f_uac1_opts_attr_fn_cap, -	&f_uac1_opts_attr_fn_cntl, +	&f_uac1_opts_attr_c_chmask, +	&f_uac1_opts_attr_c_srate, +	&f_uac1_opts_attr_c_ssize, +	&f_uac1_opts_attr_p_chmask, +	&f_uac1_opts_attr_p_srate, +	&f_uac1_opts_attr_p_ssize, +	&f_uac1_opts_attr_req_number,  	NULL,  }; @@ -894,12 +720,6 @@ static void f_audio_free_inst(struct usb_function_instance *f)  	struct f_uac1_opts *opts;  	opts = container_of(f, struct f_uac1_opts, func_inst); -	if (opts->fn_play_alloc) -		kfree(opts->fn_play); -	if (opts->fn_cap_alloc) -		kfree(opts->fn_cap); -	if (opts->fn_cntl_alloc) -		kfree(opts->fn_cntl);  	kfree(opts);  } @@ -917,21 +737,22 @@ static struct usb_function_instance *f_audio_alloc_inst(void)  	config_group_init_type_name(&opts->func_inst.group, "",  				    &f_uac1_func_type); -	opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; -	opts->req_count = UAC1_REQ_COUNT; -	opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE; -	opts->fn_play = FILE_PCM_PLAYBACK; -	opts->fn_cap = FILE_PCM_CAPTURE; -	opts->fn_cntl = FILE_CONTROL; +	opts->c_chmask = UAC1_DEF_CCHMASK; +	opts->c_srate = UAC1_DEF_CSRATE; +	opts->c_ssize = UAC1_DEF_CSSIZE; +	opts->p_chmask = UAC1_DEF_PCHMASK; +	opts->p_srate = UAC1_DEF_PSRATE; +	opts->p_ssize = UAC1_DEF_PSSIZE; +	opts->req_number = UAC1_DEF_REQ_NUM;  	return &opts->func_inst;  }  static void f_audio_free(struct usb_function *f)  { -	struct f_audio *audio = func_to_audio(f); +	struct g_audio *audio;  	struct f_uac1_opts *opts; -	gaudio_cleanup(&audio->card); +	audio = func_to_g_audio(f);  	opts = container_of(f->fi, struct f_uac1_opts, func_inst);  	kfree(audio);  	mutex_lock(&opts->lock); @@ -941,42 +762,41 @@ static void f_audio_free(struct usb_function *f)  static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)  { +	struct g_audio *audio = func_to_g_audio(f); + +	g_audio_cleanup(audio);  	usb_free_all_descriptors(f); + +	audio->gadget = NULL;  }  static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)  { -	struct f_audio *audio; +	struct f_uac1 *uac1;  	struct f_uac1_opts *opts;  	/* allocate and initialize one new instance */ -	audio = kzalloc(sizeof(*audio), GFP_KERNEL); -	if (!audio) +	uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL); +	if (!uac1)  		return ERR_PTR(-ENOMEM); -	audio->card.func.name = "g_audio"; -  	opts = container_of(fi, struct f_uac1_opts, func_inst);  	mutex_lock(&opts->lock);  	++opts->refcnt;  	mutex_unlock(&opts->lock); -	INIT_LIST_HEAD(&audio->play_queue); -	spin_lock_init(&audio->lock); - -	audio->card.func.bind = f_audio_bind; -	audio->card.func.unbind = f_audio_unbind; -	audio->card.func.set_alt = f_audio_set_alt; -	audio->card.func.setup = f_audio_setup; -	audio->card.func.disable = f_audio_disable; -	audio->card.func.free_func = f_audio_free; - -	control_selector_init(audio); -	INIT_WORK(&audio->playback_work, f_audio_playback_work); +	uac1->g_audio.func.name = "uac1_func"; +	uac1->g_audio.func.bind = f_audio_bind; +	uac1->g_audio.func.unbind = f_audio_unbind; +	uac1->g_audio.func.set_alt = f_audio_set_alt; +	uac1->g_audio.func.get_alt = f_audio_get_alt; +	uac1->g_audio.func.setup = f_audio_setup; +	uac1->g_audio.func.disable = f_audio_disable; +	uac1->g_audio.func.free_func = f_audio_free; -	return &audio->card.func; +	return &uac1->g_audio.func;  }  DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);  MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Bryan Wu"); +MODULE_AUTHOR("Ruslan Bilovol");  |