162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_audio.c -- USB Audio class function driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> 662306a36Sopenharmony_ci * Copyright (C) 2008 Analog Devices, Inc 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/atomic.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "u_uac1_legacy.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); 1862306a36Sopenharmony_cistatic int generic_get_cmd(struct usb_audio_control *con, u8 cmd); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * DESCRIPTORS ... most are static, but strings and full 2262306a36Sopenharmony_ci * configuration descriptors are built on demand. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * We have two interfaces- AudioControl and AudioStreaming 2762306a36Sopenharmony_ci * TODO: only supcard playback currently 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define F_AUDIO_AC_INTERFACE 0 3062306a36Sopenharmony_ci#define F_AUDIO_AS_INTERFACE 1 3162306a36Sopenharmony_ci#define F_AUDIO_NUM_INTERFACES 1 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* B.3.1 Standard AC Interface Descriptor */ 3462306a36Sopenharmony_cistatic struct usb_interface_descriptor ac_interface_desc = { 3562306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 3662306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 3762306a36Sopenharmony_ci .bNumEndpoints = 0, 3862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_AUDIO, 3962306a36Sopenharmony_ci .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * The number of AudioStreaming and MIDIStreaming interfaces 4462306a36Sopenharmony_ci * in the Audio Interface Collection 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ciDECLARE_UAC_AC_HEADER_DESCRIPTOR(1); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) 4962306a36Sopenharmony_ci/* 1 input terminal, 1 output terminal and 1 feature unit */ 5062306a36Sopenharmony_ci#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \ 5162306a36Sopenharmony_ci + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0)) 5262306a36Sopenharmony_ci/* B.3.2 Class-Specific AC Interface Descriptor */ 5362306a36Sopenharmony_cistatic struct uac1_ac_header_descriptor_1 ac_header_desc = { 5462306a36Sopenharmony_ci .bLength = UAC_DT_AC_HEADER_LENGTH, 5562306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 5662306a36Sopenharmony_ci .bDescriptorSubtype = UAC_HEADER, 5762306a36Sopenharmony_ci .bcdADC = cpu_to_le16(0x0100), 5862306a36Sopenharmony_ci .wTotalLength = cpu_to_le16(UAC_DT_TOTAL_LENGTH), 5962306a36Sopenharmony_ci .bInCollection = F_AUDIO_NUM_INTERFACES, 6062306a36Sopenharmony_ci .baInterfaceNr = { 6162306a36Sopenharmony_ci /* Interface number of the first AudioStream interface */ 6262306a36Sopenharmony_ci [0] = 1, 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define INPUT_TERMINAL_ID 1 6762306a36Sopenharmony_cistatic struct uac_input_terminal_descriptor input_terminal_desc = { 6862306a36Sopenharmony_ci .bLength = UAC_DT_INPUT_TERMINAL_SIZE, 6962306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 7062306a36Sopenharmony_ci .bDescriptorSubtype = UAC_INPUT_TERMINAL, 7162306a36Sopenharmony_ci .bTerminalID = INPUT_TERMINAL_ID, 7262306a36Sopenharmony_ci .wTerminalType = UAC_TERMINAL_STREAMING, 7362306a36Sopenharmony_ci .bAssocTerminal = 0, 7462306a36Sopenharmony_ci .wChannelConfig = 0x3, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciDECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define FEATURE_UNIT_ID 2 8062306a36Sopenharmony_cistatic struct uac_feature_unit_descriptor_0 feature_unit_desc = { 8162306a36Sopenharmony_ci .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), 8262306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 8362306a36Sopenharmony_ci .bDescriptorSubtype = UAC_FEATURE_UNIT, 8462306a36Sopenharmony_ci .bUnitID = FEATURE_UNIT_ID, 8562306a36Sopenharmony_ci .bSourceID = INPUT_TERMINAL_ID, 8662306a36Sopenharmony_ci .bControlSize = 2, 8762306a36Sopenharmony_ci .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic struct usb_audio_control mute_control = { 9162306a36Sopenharmony_ci .list = LIST_HEAD_INIT(mute_control.list), 9262306a36Sopenharmony_ci .name = "Mute Control", 9362306a36Sopenharmony_ci .type = UAC_FU_MUTE, 9462306a36Sopenharmony_ci /* Todo: add real Mute control code */ 9562306a36Sopenharmony_ci .set = generic_set_cmd, 9662306a36Sopenharmony_ci .get = generic_get_cmd, 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic struct usb_audio_control volume_control = { 10062306a36Sopenharmony_ci .list = LIST_HEAD_INIT(volume_control.list), 10162306a36Sopenharmony_ci .name = "Volume Control", 10262306a36Sopenharmony_ci .type = UAC_FU_VOLUME, 10362306a36Sopenharmony_ci /* Todo: add real Volume control code */ 10462306a36Sopenharmony_ci .set = generic_set_cmd, 10562306a36Sopenharmony_ci .get = generic_get_cmd, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct usb_audio_control_selector feature_unit = { 10962306a36Sopenharmony_ci .list = LIST_HEAD_INIT(feature_unit.list), 11062306a36Sopenharmony_ci .id = FEATURE_UNIT_ID, 11162306a36Sopenharmony_ci .name = "Mute & Volume Control", 11262306a36Sopenharmony_ci .type = UAC_FEATURE_UNIT, 11362306a36Sopenharmony_ci .desc = (struct usb_descriptor_header *)&feature_unit_desc, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define OUTPUT_TERMINAL_ID 3 11762306a36Sopenharmony_cistatic struct uac1_output_terminal_descriptor output_terminal_desc = { 11862306a36Sopenharmony_ci .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, 11962306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 12062306a36Sopenharmony_ci .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, 12162306a36Sopenharmony_ci .bTerminalID = OUTPUT_TERMINAL_ID, 12262306a36Sopenharmony_ci .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, 12362306a36Sopenharmony_ci .bAssocTerminal = FEATURE_UNIT_ID, 12462306a36Sopenharmony_ci .bSourceID = FEATURE_UNIT_ID, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* B.4.1 Standard AS Interface Descriptor */ 12862306a36Sopenharmony_cistatic struct usb_interface_descriptor as_interface_alt_0_desc = { 12962306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 13062306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 13162306a36Sopenharmony_ci .bAlternateSetting = 0, 13262306a36Sopenharmony_ci .bNumEndpoints = 0, 13362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_AUDIO, 13462306a36Sopenharmony_ci .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic struct usb_interface_descriptor as_interface_alt_1_desc = { 13862306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 13962306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 14062306a36Sopenharmony_ci .bAlternateSetting = 1, 14162306a36Sopenharmony_ci .bNumEndpoints = 1, 14262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_AUDIO, 14362306a36Sopenharmony_ci .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* B.4.2 Class-Specific AS Interface Descriptor */ 14762306a36Sopenharmony_cistatic struct uac1_as_header_descriptor as_header_desc = { 14862306a36Sopenharmony_ci .bLength = UAC_DT_AS_HEADER_SIZE, 14962306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 15062306a36Sopenharmony_ci .bDescriptorSubtype = UAC_AS_GENERAL, 15162306a36Sopenharmony_ci .bTerminalLink = INPUT_TERMINAL_ID, 15262306a36Sopenharmony_ci .bDelay = 1, 15362306a36Sopenharmony_ci .wFormatTag = UAC_FORMAT_TYPE_I_PCM, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciDECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { 15962306a36Sopenharmony_ci .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), 16062306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 16162306a36Sopenharmony_ci .bDescriptorSubtype = UAC_FORMAT_TYPE, 16262306a36Sopenharmony_ci .bFormatType = UAC_FORMAT_TYPE_I, 16362306a36Sopenharmony_ci .bSubframeSize = 2, 16462306a36Sopenharmony_ci .bBitResolution = 16, 16562306a36Sopenharmony_ci .bSamFreqType = 1, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* Standard ISO OUT Endpoint Descriptor */ 16962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor as_out_ep_desc = { 17062306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, 17162306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 17262306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 17362306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE 17462306a36Sopenharmony_ci | USB_ENDPOINT_XFER_ISOC, 17562306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), 17662306a36Sopenharmony_ci .bInterval = 4, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* Class-specific AS ISO OUT Endpoint Descriptor */ 18062306a36Sopenharmony_cistatic struct uac_iso_endpoint_descriptor as_iso_out_desc = { 18162306a36Sopenharmony_ci .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, 18262306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_ENDPOINT, 18362306a36Sopenharmony_ci .bDescriptorSubtype = UAC_EP_GENERAL, 18462306a36Sopenharmony_ci .bmAttributes = 1, 18562306a36Sopenharmony_ci .bLockDelayUnits = 1, 18662306a36Sopenharmony_ci .wLockDelay = cpu_to_le16(1), 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic struct usb_descriptor_header *f_audio_desc[] = { 19062306a36Sopenharmony_ci (struct usb_descriptor_header *)&ac_interface_desc, 19162306a36Sopenharmony_ci (struct usb_descriptor_header *)&ac_header_desc, 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci (struct usb_descriptor_header *)&input_terminal_desc, 19462306a36Sopenharmony_ci (struct usb_descriptor_header *)&output_terminal_desc, 19562306a36Sopenharmony_ci (struct usb_descriptor_header *)&feature_unit_desc, 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci (struct usb_descriptor_header *)&as_interface_alt_0_desc, 19862306a36Sopenharmony_ci (struct usb_descriptor_header *)&as_interface_alt_1_desc, 19962306a36Sopenharmony_ci (struct usb_descriptor_header *)&as_header_desc, 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci (struct usb_descriptor_header *)&as_type_i_desc, 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci (struct usb_descriptor_header *)&as_out_ep_desc, 20462306a36Sopenharmony_ci (struct usb_descriptor_header *)&as_iso_out_desc, 20562306a36Sopenharmony_ci NULL, 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cienum { 20962306a36Sopenharmony_ci STR_AC_IF, 21062306a36Sopenharmony_ci STR_INPUT_TERMINAL, 21162306a36Sopenharmony_ci STR_INPUT_TERMINAL_CH_NAMES, 21262306a36Sopenharmony_ci STR_FEAT_DESC_0, 21362306a36Sopenharmony_ci STR_OUTPUT_TERMINAL, 21462306a36Sopenharmony_ci STR_AS_IF_ALT0, 21562306a36Sopenharmony_ci STR_AS_IF_ALT1, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic struct usb_string strings_uac1[] = { 21962306a36Sopenharmony_ci [STR_AC_IF].s = "AC Interface", 22062306a36Sopenharmony_ci [STR_INPUT_TERMINAL].s = "Input terminal", 22162306a36Sopenharmony_ci [STR_INPUT_TERMINAL_CH_NAMES].s = "Channels", 22262306a36Sopenharmony_ci [STR_FEAT_DESC_0].s = "Volume control & mute", 22362306a36Sopenharmony_ci [STR_OUTPUT_TERMINAL].s = "Output terminal", 22462306a36Sopenharmony_ci [STR_AS_IF_ALT0].s = "AS Interface", 22562306a36Sopenharmony_ci [STR_AS_IF_ALT1].s = "AS Interface", 22662306a36Sopenharmony_ci { }, 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct usb_gadget_strings str_uac1 = { 23062306a36Sopenharmony_ci .language = 0x0409, /* en-us */ 23162306a36Sopenharmony_ci .strings = strings_uac1, 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic struct usb_gadget_strings *uac1_strings[] = { 23562306a36Sopenharmony_ci &str_uac1, 23662306a36Sopenharmony_ci NULL, 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* 24062306a36Sopenharmony_ci * This function is an ALSA sound card following USB Audio Class Spec 1.0. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 24462306a36Sopenharmony_cistruct f_audio_buf { 24562306a36Sopenharmony_ci u8 *buf; 24662306a36Sopenharmony_ci int actual; 24762306a36Sopenharmony_ci struct list_head list; 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic struct f_audio_buf *f_audio_buffer_alloc(int buf_size) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct f_audio_buf *copy_buf; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC); 25562306a36Sopenharmony_ci if (!copy_buf) 25662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC); 25962306a36Sopenharmony_ci if (!copy_buf->buf) { 26062306a36Sopenharmony_ci kfree(copy_buf); 26162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return copy_buf; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void f_audio_buffer_free(struct f_audio_buf *audio_buf) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci kfree(audio_buf->buf); 27062306a36Sopenharmony_ci kfree(audio_buf); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistruct f_audio { 27562306a36Sopenharmony_ci struct gaudio card; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci u8 ac_intf, ac_alt; 27862306a36Sopenharmony_ci u8 as_intf, as_alt; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* endpoints handle full and/or high speeds */ 28162306a36Sopenharmony_ci struct usb_ep *out_ep; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci spinlock_t lock; 28462306a36Sopenharmony_ci struct f_audio_buf *copy_buf; 28562306a36Sopenharmony_ci struct work_struct playback_work; 28662306a36Sopenharmony_ci struct list_head play_queue; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Control Set command */ 28962306a36Sopenharmony_ci struct list_head cs; 29062306a36Sopenharmony_ci u8 set_cmd; 29162306a36Sopenharmony_ci struct usb_audio_control *set_con; 29262306a36Sopenharmony_ci}; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic inline struct f_audio *func_to_audio(struct usb_function *f) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci return container_of(f, struct f_audio, card.func); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic void f_audio_playback_work(struct work_struct *data) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct f_audio *audio = container_of(data, struct f_audio, 30462306a36Sopenharmony_ci playback_work); 30562306a36Sopenharmony_ci struct f_audio_buf *play_buf; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci spin_lock_irq(&audio->lock); 30862306a36Sopenharmony_ci if (list_empty(&audio->play_queue)) { 30962306a36Sopenharmony_ci spin_unlock_irq(&audio->lock); 31062306a36Sopenharmony_ci return; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci play_buf = list_first_entry(&audio->play_queue, 31362306a36Sopenharmony_ci struct f_audio_buf, list); 31462306a36Sopenharmony_ci list_del(&play_buf->list); 31562306a36Sopenharmony_ci spin_unlock_irq(&audio->lock); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci u_audio_playback(&audio->card, play_buf->buf, play_buf->actual); 31862306a36Sopenharmony_ci f_audio_buffer_free(play_buf); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct f_audio *audio = req->context; 32462306a36Sopenharmony_ci struct usb_composite_dev *cdev = audio->card.func.config->cdev; 32562306a36Sopenharmony_ci struct f_audio_buf *copy_buf = audio->copy_buf; 32662306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts; 32762306a36Sopenharmony_ci int audio_buf_size; 32862306a36Sopenharmony_ci int err; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci opts = container_of(audio->card.func.fi, struct f_uac1_legacy_opts, 33162306a36Sopenharmony_ci func_inst); 33262306a36Sopenharmony_ci audio_buf_size = opts->audio_buf_size; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!copy_buf) 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Copy buffer is full, add it to the play_queue */ 33862306a36Sopenharmony_ci if (audio_buf_size - copy_buf->actual < req->actual) { 33962306a36Sopenharmony_ci spin_lock_irq(&audio->lock); 34062306a36Sopenharmony_ci list_add_tail(©_buf->list, &audio->play_queue); 34162306a36Sopenharmony_ci spin_unlock_irq(&audio->lock); 34262306a36Sopenharmony_ci schedule_work(&audio->playback_work); 34362306a36Sopenharmony_ci copy_buf = f_audio_buffer_alloc(audio_buf_size); 34462306a36Sopenharmony_ci if (IS_ERR(copy_buf)) 34562306a36Sopenharmony_ci return -ENOMEM; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual); 34962306a36Sopenharmony_ci copy_buf->actual += req->actual; 35062306a36Sopenharmony_ci audio->copy_buf = copy_buf; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci err = usb_ep_queue(ep, req, GFP_ATOMIC); 35362306a36Sopenharmony_ci if (err) 35462306a36Sopenharmony_ci ERROR(cdev, "%s queue req: %d\n", ep->name, err); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void f_audio_complete(struct usb_ep *ep, struct usb_request *req) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct f_audio *audio = req->context; 36362306a36Sopenharmony_ci int status = req->status; 36462306a36Sopenharmony_ci u32 data = 0; 36562306a36Sopenharmony_ci struct usb_ep *out_ep = audio->out_ep; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci switch (status) { 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci case 0: /* normal completion? */ 37062306a36Sopenharmony_ci if (ep == out_ep) 37162306a36Sopenharmony_ci f_audio_out_ep_complete(ep, req); 37262306a36Sopenharmony_ci else if (audio->set_con) { 37362306a36Sopenharmony_ci memcpy(&data, req->buf, req->length); 37462306a36Sopenharmony_ci audio->set_con->set(audio->set_con, audio->set_cmd, 37562306a36Sopenharmony_ci le16_to_cpu(data)); 37662306a36Sopenharmony_ci audio->set_con = NULL; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci default: 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int audio_set_intf_req(struct usb_function *f, 38562306a36Sopenharmony_ci const struct usb_ctrlrequest *ctrl) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct f_audio *audio = func_to_audio(f); 38862306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 38962306a36Sopenharmony_ci struct usb_request *req = cdev->req; 39062306a36Sopenharmony_ci u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); 39162306a36Sopenharmony_ci u16 len = le16_to_cpu(ctrl->wLength); 39262306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 39362306a36Sopenharmony_ci u8 con_sel = (w_value >> 8) & 0xFF; 39462306a36Sopenharmony_ci u8 cmd = (ctrl->bRequest & 0x0F); 39562306a36Sopenharmony_ci struct usb_audio_control_selector *cs; 39662306a36Sopenharmony_ci struct usb_audio_control *con; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", 39962306a36Sopenharmony_ci ctrl->bRequest, w_value, len, id); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci list_for_each_entry(cs, &audio->cs, list) { 40262306a36Sopenharmony_ci if (cs->id == id) { 40362306a36Sopenharmony_ci list_for_each_entry(con, &cs->control, list) { 40462306a36Sopenharmony_ci if (con->type == con_sel) { 40562306a36Sopenharmony_ci audio->set_con = con; 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci audio->set_cmd = cmd; 41462306a36Sopenharmony_ci req->context = audio; 41562306a36Sopenharmony_ci req->complete = f_audio_complete; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return len; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int audio_get_intf_req(struct usb_function *f, 42162306a36Sopenharmony_ci const struct usb_ctrlrequest *ctrl) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct f_audio *audio = func_to_audio(f); 42462306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 42562306a36Sopenharmony_ci struct usb_request *req = cdev->req; 42662306a36Sopenharmony_ci int value = -EOPNOTSUPP; 42762306a36Sopenharmony_ci u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); 42862306a36Sopenharmony_ci u16 len = le16_to_cpu(ctrl->wLength); 42962306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 43062306a36Sopenharmony_ci u8 con_sel = (w_value >> 8) & 0xFF; 43162306a36Sopenharmony_ci u8 cmd = (ctrl->bRequest & 0x0F); 43262306a36Sopenharmony_ci struct usb_audio_control_selector *cs; 43362306a36Sopenharmony_ci struct usb_audio_control *con; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", 43662306a36Sopenharmony_ci ctrl->bRequest, w_value, len, id); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci list_for_each_entry(cs, &audio->cs, list) { 43962306a36Sopenharmony_ci if (cs->id == id) { 44062306a36Sopenharmony_ci list_for_each_entry(con, &cs->control, list) { 44162306a36Sopenharmony_ci if (con->type == con_sel && con->get) { 44262306a36Sopenharmony_ci value = con->get(con, cmd); 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci req->context = audio; 45162306a36Sopenharmony_ci req->complete = f_audio_complete; 45262306a36Sopenharmony_ci len = min_t(size_t, sizeof(value), len); 45362306a36Sopenharmony_ci memcpy(req->buf, &value, len); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return len; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int audio_set_endpoint_req(struct usb_function *f, 45962306a36Sopenharmony_ci const struct usb_ctrlrequest *ctrl) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 46262306a36Sopenharmony_ci int value = -EOPNOTSUPP; 46362306a36Sopenharmony_ci u16 ep = le16_to_cpu(ctrl->wIndex); 46462306a36Sopenharmony_ci u16 len = le16_to_cpu(ctrl->wLength); 46562306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", 46862306a36Sopenharmony_ci ctrl->bRequest, w_value, len, ep); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci switch (ctrl->bRequest) { 47162306a36Sopenharmony_ci case UAC_SET_CUR: 47262306a36Sopenharmony_ci value = len; 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci case UAC_SET_MIN: 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci case UAC_SET_MAX: 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci case UAC_SET_RES: 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci case UAC_SET_MEM: 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci default: 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return value; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int audio_get_endpoint_req(struct usb_function *f, 49562306a36Sopenharmony_ci const struct usb_ctrlrequest *ctrl) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 49862306a36Sopenharmony_ci int value = -EOPNOTSUPP; 49962306a36Sopenharmony_ci u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); 50062306a36Sopenharmony_ci u16 len = le16_to_cpu(ctrl->wLength); 50162306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", 50462306a36Sopenharmony_ci ctrl->bRequest, w_value, len, ep); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci switch (ctrl->bRequest) { 50762306a36Sopenharmony_ci case UAC_GET_CUR: 50862306a36Sopenharmony_ci case UAC_GET_MIN: 50962306a36Sopenharmony_ci case UAC_GET_MAX: 51062306a36Sopenharmony_ci case UAC_GET_RES: 51162306a36Sopenharmony_ci value = len; 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci case UAC_GET_MEM: 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci default: 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return value; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int 52362306a36Sopenharmony_cif_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 52662306a36Sopenharmony_ci struct usb_request *req = cdev->req; 52762306a36Sopenharmony_ci int value = -EOPNOTSUPP; 52862306a36Sopenharmony_ci u16 w_index = le16_to_cpu(ctrl->wIndex); 52962306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 53062306a36Sopenharmony_ci u16 w_length = le16_to_cpu(ctrl->wLength); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* composite driver infrastructure handles everything; interface 53362306a36Sopenharmony_ci * activation uses set_alt(). 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci switch (ctrl->bRequestType) { 53662306a36Sopenharmony_ci case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: 53762306a36Sopenharmony_ci value = audio_set_intf_req(f, ctrl); 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: 54162306a36Sopenharmony_ci value = audio_get_intf_req(f, ctrl); 54262306a36Sopenharmony_ci break; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: 54562306a36Sopenharmony_ci value = audio_set_endpoint_req(f, ctrl); 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: 54962306a36Sopenharmony_ci value = audio_get_endpoint_req(f, ctrl); 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci default: 55362306a36Sopenharmony_ci ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", 55462306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 55562306a36Sopenharmony_ci w_value, w_index, w_length); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* respond with data transfer or status phase? */ 55962306a36Sopenharmony_ci if (value >= 0) { 56062306a36Sopenharmony_ci DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n", 56162306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 56262306a36Sopenharmony_ci w_value, w_index, w_length); 56362306a36Sopenharmony_ci req->zero = 0; 56462306a36Sopenharmony_ci req->length = value; 56562306a36Sopenharmony_ci value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); 56662306a36Sopenharmony_ci if (value < 0) 56762306a36Sopenharmony_ci ERROR(cdev, "audio response on err %d\n", value); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* device either stalls (value < 0) or reports success */ 57162306a36Sopenharmony_ci return value; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct f_audio *audio = func_to_audio(f); 57762306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 57862306a36Sopenharmony_ci struct usb_ep *out_ep = audio->out_ep; 57962306a36Sopenharmony_ci struct usb_request *req; 58062306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts; 58162306a36Sopenharmony_ci int req_buf_size, req_count, audio_buf_size; 58262306a36Sopenharmony_ci int i = 0, err = 0; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci DBG(cdev, "intf %d, alt %d\n", intf, alt); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci opts = container_of(f->fi, struct f_uac1_legacy_opts, func_inst); 58762306a36Sopenharmony_ci req_buf_size = opts->req_buf_size; 58862306a36Sopenharmony_ci req_count = opts->req_count; 58962306a36Sopenharmony_ci audio_buf_size = opts->audio_buf_size; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* No i/f has more than 2 alt settings */ 59262306a36Sopenharmony_ci if (alt > 1) { 59362306a36Sopenharmony_ci ERROR(cdev, "%s:%d Error!\n", __func__, __LINE__); 59462306a36Sopenharmony_ci return -EINVAL; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (intf == audio->ac_intf) { 59862306a36Sopenharmony_ci /* Control I/f has only 1 AltSetting - 0 */ 59962306a36Sopenharmony_ci if (alt) { 60062306a36Sopenharmony_ci ERROR(cdev, "%s:%d Error!\n", __func__, __LINE__); 60162306a36Sopenharmony_ci return -EINVAL; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci } else if (intf == audio->as_intf) { 60562306a36Sopenharmony_ci if (alt == 1) { 60662306a36Sopenharmony_ci err = config_ep_by_speed(cdev->gadget, f, out_ep); 60762306a36Sopenharmony_ci if (err) 60862306a36Sopenharmony_ci return err; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci usb_ep_enable(out_ep); 61162306a36Sopenharmony_ci audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); 61262306a36Sopenharmony_ci if (IS_ERR(audio->copy_buf)) 61362306a36Sopenharmony_ci return -ENOMEM; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * allocate a bunch of read buffers 61762306a36Sopenharmony_ci * and queue them all at once. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci for (i = 0; i < req_count && err == 0; i++) { 62062306a36Sopenharmony_ci req = usb_ep_alloc_request(out_ep, GFP_ATOMIC); 62162306a36Sopenharmony_ci if (req) { 62262306a36Sopenharmony_ci req->buf = kzalloc(req_buf_size, 62362306a36Sopenharmony_ci GFP_ATOMIC); 62462306a36Sopenharmony_ci if (req->buf) { 62562306a36Sopenharmony_ci req->length = req_buf_size; 62662306a36Sopenharmony_ci req->context = audio; 62762306a36Sopenharmony_ci req->complete = 62862306a36Sopenharmony_ci f_audio_complete; 62962306a36Sopenharmony_ci err = usb_ep_queue(out_ep, 63062306a36Sopenharmony_ci req, GFP_ATOMIC); 63162306a36Sopenharmony_ci if (err) 63262306a36Sopenharmony_ci ERROR(cdev, 63362306a36Sopenharmony_ci "%s queue req: %d\n", 63462306a36Sopenharmony_ci out_ep->name, err); 63562306a36Sopenharmony_ci } else 63662306a36Sopenharmony_ci err = -ENOMEM; 63762306a36Sopenharmony_ci } else 63862306a36Sopenharmony_ci err = -ENOMEM; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci } else { 64262306a36Sopenharmony_ci struct f_audio_buf *copy_buf = audio->copy_buf; 64362306a36Sopenharmony_ci if (copy_buf) { 64462306a36Sopenharmony_ci list_add_tail(©_buf->list, 64562306a36Sopenharmony_ci &audio->play_queue); 64662306a36Sopenharmony_ci schedule_work(&audio->playback_work); 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci audio->as_alt = alt; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return err; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic int f_audio_get_alt(struct usb_function *f, unsigned intf) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct f_audio *audio = func_to_audio(f); 65862306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (intf == audio->ac_intf) 66162306a36Sopenharmony_ci return audio->ac_alt; 66262306a36Sopenharmony_ci else if (intf == audio->as_intf) 66362306a36Sopenharmony_ci return audio->as_alt; 66462306a36Sopenharmony_ci else 66562306a36Sopenharmony_ci ERROR(cdev, "%s:%d Invalid Interface %d!\n", 66662306a36Sopenharmony_ci __func__, __LINE__, intf); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci return -EINVAL; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic void f_audio_disable(struct usb_function *f) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci return; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic void f_audio_build_desc(struct f_audio *audio) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct gaudio *card = &audio->card; 68162306a36Sopenharmony_ci u8 *sam_freq; 68262306a36Sopenharmony_ci int rate; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* Set channel numbers */ 68562306a36Sopenharmony_ci input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card); 68662306a36Sopenharmony_ci as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* Set sample rates */ 68962306a36Sopenharmony_ci rate = u_audio_get_playback_rate(card); 69062306a36Sopenharmony_ci sam_freq = as_type_i_desc.tSamFreq[0]; 69162306a36Sopenharmony_ci memcpy(sam_freq, &rate, 3); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Todo: Set Sample bits and other parameters */ 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci/* audio function driver setup/binding */ 69962306a36Sopenharmony_cistatic int 70062306a36Sopenharmony_cif_audio_bind(struct usb_configuration *c, struct usb_function *f) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 70362306a36Sopenharmony_ci struct f_audio *audio = func_to_audio(f); 70462306a36Sopenharmony_ci struct usb_string *us; 70562306a36Sopenharmony_ci int status; 70662306a36Sopenharmony_ci struct usb_ep *ep = NULL; 70762306a36Sopenharmony_ci struct f_uac1_legacy_opts *audio_opts; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci audio_opts = container_of(f->fi, struct f_uac1_legacy_opts, func_inst); 71062306a36Sopenharmony_ci audio->card.gadget = c->cdev->gadget; 71162306a36Sopenharmony_ci /* set up ASLA audio devices */ 71262306a36Sopenharmony_ci if (!audio_opts->bound) { 71362306a36Sopenharmony_ci status = gaudio_setup(&audio->card); 71462306a36Sopenharmony_ci if (status < 0) 71562306a36Sopenharmony_ci return status; 71662306a36Sopenharmony_ci audio_opts->bound = true; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); 71962306a36Sopenharmony_ci if (IS_ERR(us)) 72062306a36Sopenharmony_ci return PTR_ERR(us); 72162306a36Sopenharmony_ci ac_interface_desc.iInterface = us[STR_AC_IF].id; 72262306a36Sopenharmony_ci input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id; 72362306a36Sopenharmony_ci input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id; 72462306a36Sopenharmony_ci feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id; 72562306a36Sopenharmony_ci output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id; 72662306a36Sopenharmony_ci as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id; 72762306a36Sopenharmony_ci as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci f_audio_build_desc(audio); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* allocate instance-specific interface IDs, and patch descriptors */ 73362306a36Sopenharmony_ci status = usb_interface_id(c, f); 73462306a36Sopenharmony_ci if (status < 0) 73562306a36Sopenharmony_ci goto fail; 73662306a36Sopenharmony_ci ac_interface_desc.bInterfaceNumber = status; 73762306a36Sopenharmony_ci audio->ac_intf = status; 73862306a36Sopenharmony_ci audio->ac_alt = 0; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci status = usb_interface_id(c, f); 74162306a36Sopenharmony_ci if (status < 0) 74262306a36Sopenharmony_ci goto fail; 74362306a36Sopenharmony_ci as_interface_alt_0_desc.bInterfaceNumber = status; 74462306a36Sopenharmony_ci as_interface_alt_1_desc.bInterfaceNumber = status; 74562306a36Sopenharmony_ci audio->as_intf = status; 74662306a36Sopenharmony_ci audio->as_alt = 0; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci status = -ENODEV; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* allocate instance-specific endpoints */ 75162306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); 75262306a36Sopenharmony_ci if (!ep) 75362306a36Sopenharmony_ci goto fail; 75462306a36Sopenharmony_ci audio->out_ep = ep; 75562306a36Sopenharmony_ci audio->out_ep->desc = &as_out_ep_desc; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* copy descriptors, and track endpoint copies */ 75862306a36Sopenharmony_ci status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL, 75962306a36Sopenharmony_ci NULL); 76062306a36Sopenharmony_ci if (status) 76162306a36Sopenharmony_ci goto fail; 76262306a36Sopenharmony_ci return 0; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cifail: 76562306a36Sopenharmony_ci gaudio_cleanup(&audio->card); 76662306a36Sopenharmony_ci return status; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci con->data[cmd] = value; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic int generic_get_cmd(struct usb_audio_control *con, u8 cmd) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci return con->data[cmd]; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci/* Todo: add more control selecotor dynamically */ 78462306a36Sopenharmony_cistatic int control_selector_init(struct f_audio *audio) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci INIT_LIST_HEAD(&audio->cs); 78762306a36Sopenharmony_ci list_add(&feature_unit.list, &audio->cs); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci INIT_LIST_HEAD(&feature_unit.control); 79062306a36Sopenharmony_ci list_add(&mute_control.list, &feature_unit.control); 79162306a36Sopenharmony_ci list_add(&volume_control.list, &feature_unit.control); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci volume_control.data[UAC__CUR] = 0xffc0; 79462306a36Sopenharmony_ci volume_control.data[UAC__MIN] = 0xe3a0; 79562306a36Sopenharmony_ci volume_control.data[UAC__MAX] = 0xfff0; 79662306a36Sopenharmony_ci volume_control.data[UAC__RES] = 0x0030; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic inline 80262306a36Sopenharmony_cistruct f_uac1_legacy_opts *to_f_uac1_opts(struct config_item *item) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_uac1_legacy_opts, 80562306a36Sopenharmony_ci func_inst.group); 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic void f_uac1_attr_release(struct config_item *item) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci usb_put_function_instance(&opts->func_inst); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic struct configfs_item_operations f_uac1_item_ops = { 81662306a36Sopenharmony_ci .release = f_uac1_attr_release, 81762306a36Sopenharmony_ci}; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci#define UAC1_INT_ATTRIBUTE(name) \ 82062306a36Sopenharmony_cistatic ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ 82162306a36Sopenharmony_ci char *page) \ 82262306a36Sopenharmony_ci{ \ 82362306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item); \ 82462306a36Sopenharmony_ci int result; \ 82562306a36Sopenharmony_ci \ 82662306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 82762306a36Sopenharmony_ci result = sprintf(page, "%u\n", opts->name); \ 82862306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 82962306a36Sopenharmony_ci \ 83062306a36Sopenharmony_ci return result; \ 83162306a36Sopenharmony_ci} \ 83262306a36Sopenharmony_ci \ 83362306a36Sopenharmony_cistatic ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ 83462306a36Sopenharmony_ci const char *page, size_t len) \ 83562306a36Sopenharmony_ci{ \ 83662306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item); \ 83762306a36Sopenharmony_ci int ret; \ 83862306a36Sopenharmony_ci u32 num; \ 83962306a36Sopenharmony_ci \ 84062306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 84162306a36Sopenharmony_ci if (opts->refcnt) { \ 84262306a36Sopenharmony_ci ret = -EBUSY; \ 84362306a36Sopenharmony_ci goto end; \ 84462306a36Sopenharmony_ci } \ 84562306a36Sopenharmony_ci \ 84662306a36Sopenharmony_ci ret = kstrtou32(page, 0, &num); \ 84762306a36Sopenharmony_ci if (ret) \ 84862306a36Sopenharmony_ci goto end; \ 84962306a36Sopenharmony_ci \ 85062306a36Sopenharmony_ci opts->name = num; \ 85162306a36Sopenharmony_ci ret = len; \ 85262306a36Sopenharmony_ci \ 85362306a36Sopenharmony_ciend: \ 85462306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 85562306a36Sopenharmony_ci return ret; \ 85662306a36Sopenharmony_ci} \ 85762306a36Sopenharmony_ci \ 85862306a36Sopenharmony_ciCONFIGFS_ATTR(f_uac1_opts_, name) 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ciUAC1_INT_ATTRIBUTE(req_buf_size); 86162306a36Sopenharmony_ciUAC1_INT_ATTRIBUTE(req_count); 86262306a36Sopenharmony_ciUAC1_INT_ATTRIBUTE(audio_buf_size); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci#define UAC1_STR_ATTRIBUTE(name) \ 86562306a36Sopenharmony_cistatic ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ 86662306a36Sopenharmony_ci char *page) \ 86762306a36Sopenharmony_ci{ \ 86862306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item); \ 86962306a36Sopenharmony_ci int result; \ 87062306a36Sopenharmony_ci \ 87162306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 87262306a36Sopenharmony_ci result = sprintf(page, "%s\n", opts->name); \ 87362306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 87462306a36Sopenharmony_ci \ 87562306a36Sopenharmony_ci return result; \ 87662306a36Sopenharmony_ci} \ 87762306a36Sopenharmony_ci \ 87862306a36Sopenharmony_cistatic ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ 87962306a36Sopenharmony_ci const char *page, size_t len) \ 88062306a36Sopenharmony_ci{ \ 88162306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item); \ 88262306a36Sopenharmony_ci int ret = -EBUSY; \ 88362306a36Sopenharmony_ci char *tmp; \ 88462306a36Sopenharmony_ci \ 88562306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 88662306a36Sopenharmony_ci if (opts->refcnt) \ 88762306a36Sopenharmony_ci goto end; \ 88862306a36Sopenharmony_ci \ 88962306a36Sopenharmony_ci tmp = kstrndup(page, len, GFP_KERNEL); \ 89062306a36Sopenharmony_ci if (tmp) { \ 89162306a36Sopenharmony_ci ret = -ENOMEM; \ 89262306a36Sopenharmony_ci goto end; \ 89362306a36Sopenharmony_ci } \ 89462306a36Sopenharmony_ci if (opts->name##_alloc) \ 89562306a36Sopenharmony_ci kfree(opts->name); \ 89662306a36Sopenharmony_ci opts->name##_alloc = true; \ 89762306a36Sopenharmony_ci opts->name = tmp; \ 89862306a36Sopenharmony_ci ret = len; \ 89962306a36Sopenharmony_ci \ 90062306a36Sopenharmony_ciend: \ 90162306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 90262306a36Sopenharmony_ci return ret; \ 90362306a36Sopenharmony_ci} \ 90462306a36Sopenharmony_ci \ 90562306a36Sopenharmony_ciCONFIGFS_ATTR(f_uac1_opts_, name) 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ciUAC1_STR_ATTRIBUTE(fn_play); 90862306a36Sopenharmony_ciUAC1_STR_ATTRIBUTE(fn_cap); 90962306a36Sopenharmony_ciUAC1_STR_ATTRIBUTE(fn_cntl); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic struct configfs_attribute *f_uac1_attrs[] = { 91262306a36Sopenharmony_ci &f_uac1_opts_attr_req_buf_size, 91362306a36Sopenharmony_ci &f_uac1_opts_attr_req_count, 91462306a36Sopenharmony_ci &f_uac1_opts_attr_audio_buf_size, 91562306a36Sopenharmony_ci &f_uac1_opts_attr_fn_play, 91662306a36Sopenharmony_ci &f_uac1_opts_attr_fn_cap, 91762306a36Sopenharmony_ci &f_uac1_opts_attr_fn_cntl, 91862306a36Sopenharmony_ci NULL, 91962306a36Sopenharmony_ci}; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic const struct config_item_type f_uac1_func_type = { 92262306a36Sopenharmony_ci .ct_item_ops = &f_uac1_item_ops, 92362306a36Sopenharmony_ci .ct_attrs = f_uac1_attrs, 92462306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 92562306a36Sopenharmony_ci}; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic void f_audio_free_inst(struct usb_function_instance *f) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci opts = container_of(f, struct f_uac1_legacy_opts, func_inst); 93262306a36Sopenharmony_ci if (opts->fn_play_alloc) 93362306a36Sopenharmony_ci kfree(opts->fn_play); 93462306a36Sopenharmony_ci if (opts->fn_cap_alloc) 93562306a36Sopenharmony_ci kfree(opts->fn_cap); 93662306a36Sopenharmony_ci if (opts->fn_cntl_alloc) 93762306a36Sopenharmony_ci kfree(opts->fn_cntl); 93862306a36Sopenharmony_ci kfree(opts); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic struct usb_function_instance *f_audio_alloc_inst(void) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 94662306a36Sopenharmony_ci if (!opts) 94762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci mutex_init(&opts->lock); 95062306a36Sopenharmony_ci opts->func_inst.free_func_inst = f_audio_free_inst; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", 95362306a36Sopenharmony_ci &f_uac1_func_type); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; 95662306a36Sopenharmony_ci opts->req_count = UAC1_REQ_COUNT; 95762306a36Sopenharmony_ci opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE; 95862306a36Sopenharmony_ci opts->fn_play = FILE_PCM_PLAYBACK; 95962306a36Sopenharmony_ci opts->fn_cap = FILE_PCM_CAPTURE; 96062306a36Sopenharmony_ci opts->fn_cntl = FILE_CONTROL; 96162306a36Sopenharmony_ci return &opts->func_inst; 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic void f_audio_free(struct usb_function *f) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct f_audio *audio = func_to_audio(f); 96762306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci gaudio_cleanup(&audio->card); 97062306a36Sopenharmony_ci opts = container_of(f->fi, struct f_uac1_legacy_opts, func_inst); 97162306a36Sopenharmony_ci kfree(audio); 97262306a36Sopenharmony_ci mutex_lock(&opts->lock); 97362306a36Sopenharmony_ci --opts->refcnt; 97462306a36Sopenharmony_ci mutex_unlock(&opts->lock); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci usb_free_all_descriptors(f); 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic struct usb_function *f_audio_alloc(struct usb_function_instance *fi) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct f_audio *audio; 98562306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* allocate and initialize one new instance */ 98862306a36Sopenharmony_ci audio = kzalloc(sizeof(*audio), GFP_KERNEL); 98962306a36Sopenharmony_ci if (!audio) 99062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci audio->card.func.name = "g_audio"; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci opts = container_of(fi, struct f_uac1_legacy_opts, func_inst); 99562306a36Sopenharmony_ci mutex_lock(&opts->lock); 99662306a36Sopenharmony_ci ++opts->refcnt; 99762306a36Sopenharmony_ci mutex_unlock(&opts->lock); 99862306a36Sopenharmony_ci INIT_LIST_HEAD(&audio->play_queue); 99962306a36Sopenharmony_ci spin_lock_init(&audio->lock); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci audio->card.func.bind = f_audio_bind; 100262306a36Sopenharmony_ci audio->card.func.unbind = f_audio_unbind; 100362306a36Sopenharmony_ci audio->card.func.set_alt = f_audio_set_alt; 100462306a36Sopenharmony_ci audio->card.func.get_alt = f_audio_get_alt; 100562306a36Sopenharmony_ci audio->card.func.setup = f_audio_setup; 100662306a36Sopenharmony_ci audio->card.func.disable = f_audio_disable; 100762306a36Sopenharmony_ci audio->card.func.free_func = f_audio_free; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci control_selector_init(audio); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci INIT_WORK(&audio->playback_work, f_audio_playback_work); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci return &audio->card.func; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(uac1_legacy, f_audio_alloc_inst, f_audio_alloc); 101762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 101862306a36Sopenharmony_ciMODULE_AUTHOR("Bryan Wu"); 1019